err_t qbuffer_copyin(qbuffer_t* buf, qbuffer_iter_t start, qbuffer_iter_t end, const void* ptr, size_t ret_len) { int64_t num_bytes = qbuffer_iter_num_bytes(start, end); ssize_t num_parts = qbuffer_iter_num_parts(start, end); struct iovec* iov = NULL; size_t iovcnt; size_t i,j; MAYBE_STACK_SPACE(struct iovec, iov_onstack); err_t err; if( num_bytes < 0 || num_parts < 0 || start.offset < buf->offset_start || end.offset > buf->offset_end ) return EINVAL; MAYBE_STACK_ALLOC(struct iovec, num_parts, iov, iov_onstack); if( ! iov ) return ENOMEM; err = qbuffer_to_iov(buf, start, end, num_parts, iov, NULL, &iovcnt); if( err ) goto error; j = 0; for( i = 0; i < iovcnt; i++ ) { if( j + iov[i].iov_len > ret_len ) goto error_nospace; memcpy(iov[i].iov_base, PTR_ADDBYTES(ptr, j), iov[i].iov_len); j += iov[i].iov_len; } MAYBE_STACK_FREE(iov, iov_onstack); return 0; error_nospace: err = EMSGSIZE; error: MAYBE_STACK_FREE(iov, iov_onstack); return err; }
qioerr qbuffer_flatten(qbuffer_t* buf, qbuffer_iter_t start, qbuffer_iter_t end, qbytes_t** bytes_out) { int64_t num_bytes = qbuffer_iter_num_bytes(start, end); ssize_t num_parts = qbuffer_iter_num_parts(start, end); struct iovec* iov = NULL; size_t iovcnt; size_t i,j; qbytes_t* ret; MAYBE_STACK_SPACE(struct iovec, iov_onstack); qioerr err; if( num_bytes < 0 || num_parts < 0 || start.offset < buf->offset_start || end.offset > buf->offset_end ) { *bytes_out = 0; QIO_RETURN_CONSTANT_ERROR(EINVAL, "range outside of buffer"); } err = qbytes_create_calloc(&ret, num_bytes); if( err ) { *bytes_out = 0; return err; } MAYBE_STACK_ALLOC(struct iovec, num_parts, iov, iov_onstack); if( ! iov ) { // The buffer was successfully allocated, so we have to release it here. qbytes_release(ret); *bytes_out = 0; return QIO_ENOMEM; } err = qbuffer_to_iov(buf, start, end, num_parts, iov, NULL, &iovcnt); if( err ) { MAYBE_STACK_FREE(iov, iov_onstack); // The buffer was successfully allocated, so we have to release it here. qbytes_release(ret); *bytes_out = 0; return err; } j = 0; for( i = 0; i < iovcnt; i++ ) { qio_memcpy(PTR_ADDBYTES(ret->data, j), iov[i].iov_base, iov[i].iov_len); j += iov[i].iov_len; } MAYBE_STACK_FREE(iov, iov_onstack); *bytes_out = ret; return 0; }
err_t bulk_put_buffer(int64_t dst_locale, void* dst_addr, int64_t dst_len, qbuffer_t* buf, qbuffer_iter_t start, qbuffer_iter_t end) { int64_t num_bytes = qbuffer_iter_num_bytes(start, end); ssize_t num_parts = qbuffer_iter_num_parts(start, end); struct iovec* iov = NULL; size_t iovcnt; size_t i,j; int iov_onstack; err_t err; if( num_bytes < 0 || num_parts < 0 || start.offset < buf->offset_start || end.offset > buf->offset_end ) return EINVAL; MAYBE_STACK_ALLOC(num_parts*sizeof(struct iovec), iov, iov_onstack); if( ! iov ) return ENOMEM; err = qbuffer_to_iov(buf, start, end, num_parts, iov, NULL, &iovcnt); if( err ) goto error; j = 0; for( i = 0; i < iovcnt; i++ ) { if( j + iov[i].iov_len > dst_len ) goto error_nospace; //memcpy(PTR_ADDBYTES(ptr, j), iov[i].iov_base, iov[i].iov_len); // TODO -- note -- technically, this should be gasnet_put_bulk, // since we don't want to require src/dst to have a particular alignment. chpl_gen_comm_put( iov[i].iov_base, dst_locale, PTR_ADDBYTES(dst_addr, j), sizeof(uint8_t), CHPL_TYPE_uint8_t, iov[i].iov_len, -1, "<internal>" ); j += iov[i].iov_len; } MAYBE_STACK_FREE(iov, iov_onstack); return 0; error_nospace: err = EMSGSIZE; error: MAYBE_STACK_FREE(iov, iov_onstack); return err; }
err_t qbuffer_flatten(qbuffer_t* buf, qbuffer_iter_t start, qbuffer_iter_t end, qbytes_t** bytes_out) { int64_t num_bytes = qbuffer_iter_num_bytes(start, end); ssize_t num_parts = qbuffer_iter_num_parts(start, end); struct iovec* iov = NULL; size_t iovcnt; size_t i,j; qbytes_t* ret; MAYBE_STACK_SPACE(struct iovec, iov_onstack); err_t err; if( num_bytes < 0 || num_parts < 0 || start.offset < buf->offset_start || end.offset > buf->offset_end ) return EINVAL; err = qbytes_create_calloc(&ret, num_bytes); if( err ) return err; MAYBE_STACK_ALLOC(struct iovec, num_parts, iov, iov_onstack); if( ! iov ) { qbytes_release(ret); return ENOMEM; } err = qbuffer_to_iov(buf, start, end, num_parts, iov, NULL, &iovcnt); if( err ) { MAYBE_STACK_FREE(iov, iov_onstack); qbytes_release(ret); return err; } j = 0; for( i = 0; i < iovcnt; i++ ) { memcpy(PTR_ADDBYTES(ret->data, j), iov[i].iov_base, iov[i].iov_len); j += iov[i].iov_len; } MAYBE_STACK_FREE(iov, iov_onstack); *bytes_out = ret; return 0; }
qioerr qbuffer_copyin(qbuffer_t* buf, qbuffer_iter_t start, qbuffer_iter_t end, const void* ptr, size_t ret_len) { int64_t num_bytes = qbuffer_iter_num_bytes(start, end); ssize_t num_parts = qbuffer_iter_num_parts(start, end); struct iovec* iov = NULL; size_t iovcnt; size_t i,j; MAYBE_STACK_SPACE(struct iovec, iov_onstack); qioerr err; if( num_bytes < 0 || num_parts < 0 || start.offset < buf->offset_start || end.offset > buf->offset_end ) { QIO_RETURN_CONSTANT_ERROR(EINVAL, "range outside of buffer"); } MAYBE_STACK_ALLOC(struct iovec, num_parts, iov, iov_onstack); if( ! iov ) return QIO_ENOMEM; err = qbuffer_to_iov(buf, start, end, num_parts, iov, NULL, &iovcnt); if( err ) goto error; j = 0; for( i = 0; i < iovcnt; i++ ) { if( j + iov[i].iov_len > ret_len ) goto error_nospace; qio_memcpy(iov[i].iov_base, PTR_ADDBYTES(ptr, j), iov[i].iov_len); j += iov[i].iov_len; } MAYBE_STACK_FREE(iov, iov_onstack); return 0; error_nospace: QIO_GET_CONSTANT_ERROR(err, EMSGSIZE, "no space in buffer"); error: MAYBE_STACK_FREE(iov, iov_onstack); return err; }
qioerr qbuffer_to_iov(qbuffer_t* buf, qbuffer_iter_t start, qbuffer_iter_t end, size_t max_iov, struct iovec *iov_out, qbytes_t** bytes_out /* can be NULL */, size_t *iovcnt_out) { deque_iterator_t d_end = deque_end(& buf->deque); deque_iterator_t iter; qbuffer_part_t* qbp; size_t i = 0; iter = start.iter; // invalid range! if( start.offset > end.offset ) { *iovcnt_out = 0; QIO_RETURN_CONSTANT_ERROR(EINVAL, "invalid range"); } if( deque_it_equals(iter, d_end) ) { // start is actually pointing to the end of the deque. no data. *iovcnt_out = 0; return 0; } if( deque_it_equals(iter, end.iter) ) { // we're only pointing to a single block. qbp = (qbuffer_part_t*) deque_it_get_cur_ptr(sizeof(qbuffer_part_t), iter); if( i >= max_iov ) goto error_nospace; iov_out[i].iov_base = PTR_ADDBYTES(qbp->bytes->data, qbp->skip_bytes + (start.offset - (qbp->end_offset - qbp->len_bytes))); iov_out[i].iov_len = end.offset - start.offset; if( bytes_out ) bytes_out[i] = qbp->bytes; if( iov_out[i].iov_len > 0 ) i++; } else { // otherwise, there's a possibly partial block in start. qbp = (qbuffer_part_t*) deque_it_get_cur_ptr(sizeof(qbuffer_part_t), iter); if( i >= max_iov ) goto error_nospace; iov_out[i].iov_base = PTR_ADDBYTES(qbp->bytes->data, qbp->skip_bytes + (start.offset - (qbp->end_offset - qbp->len_bytes))); iov_out[i].iov_len = qbp->end_offset - start.offset; if( bytes_out ) bytes_out[i] = qbp->bytes; // store it if we had any data there. if( iov_out[i].iov_len > 0 ) i++; // Now, on to the next. deque_it_forward_one(sizeof(qbuffer_part_t), &iter); // until we get to the same block as end, we need to store full blocks. while( ! deque_it_equals( iter, end.iter ) ) { if( deque_it_equals( iter, d_end ) ) { // error: end is not in deque. *iovcnt_out = 0; QIO_RETURN_CONSTANT_ERROR(EINVAL, "end is not in deque"); } qbp = (qbuffer_part_t*) deque_it_get_cur_ptr(sizeof(qbuffer_part_t), iter); if( i >= max_iov ) goto error_nospace; iov_out[i].iov_base = PTR_ADDBYTES(qbp->bytes->data, qbp->skip_bytes); iov_out[i].iov_len = qbp->len_bytes; if( bytes_out ) bytes_out[i] = qbp->bytes; // store it if we had any data there. if( iov_out[i].iov_len > 0 ) i++; // Now, on to the next. deque_it_forward_one(sizeof(qbuffer_part_t), &iter); } // at the end of the loop // is there any data in end? if( deque_it_equals(iter, d_end) ) { // we're currently pointing to the end; no need to add more. } else { qbp = (qbuffer_part_t*) deque_it_get_cur_ptr(sizeof(qbuffer_part_t), iter); // add a partial end block. We know it's different from // start since we handled that above. if( i >= max_iov ) goto error_nospace; iov_out[i].iov_base = PTR_ADDBYTES(qbp->bytes->data, qbp->skip_bytes); iov_out[i].iov_len = end.offset - (qbp->end_offset - qbp->len_bytes); if( bytes_out ) bytes_out[i] = qbp->bytes; if( iov_out[i].iov_len > 0 ) i++; } } *iovcnt_out = i; return 0; error_nospace: *iovcnt_out = 0; // EOVERFLOW or ENOBUFS would make sense too QIO_RETURN_CONSTANT_ERROR(EMSGSIZE, "no space in buffer"); }