/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright by The HDF Group. * * All rights reserved. * * * * This file is part of HDF5. The full HDF5 copyright notice, including * * terms governing use, modification, and redistribution, is contained in * * the COPYING file, which can be found at the root of the source code * * distribution tree, or in https://www.hdfgroup.org/licenses. * * If you do not have access to either file, you may request a copy from * * help@hdfgroup.org. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "H5private.h" #include "H5Eprivate.h" #include "H5MMprivate.h" /* Memory management */ #include "H5Oprivate.h" #include "H5VMprivate.h" /* Local typedefs */ typedef struct H5VM_memcpy_ud_t { unsigned char *dst; /* Pointer to destination buffer */ const unsigned char *src; /* Pointer to source buffer */ } H5VM_memcpy_ud_t; /* Local macros */ #define H5VM_HYPER_NDIMS H5O_LAYOUT_NDIMS /* Local prototypes */ static void H5VM__stride_optimize1(unsigned *np /*in,out*/, hsize_t *elmt_size /*in,out*/, const hsize_t *size, hsize_t *stride1); static void H5VM__stride_optimize2(unsigned *np /*in,out*/, hsize_t *elmt_size /*in,out*/, const hsize_t *size, hsize_t *stride1, hsize_t *stride2); /*------------------------------------------------------------------------- * Function: H5VM__stride_optimize1 * * Purpose: Given a stride vector which references elements of the * specified size, optimize the dimensionality, the stride * vector, and the element size to minimize the dimensionality * and the number of memory accesses. * * All arguments are passed by reference and their values may be * modified by this function. * * Return: void * *------------------------------------------------------------------------- */ static void H5VM__stride_optimize1(unsigned *np /*in,out*/, hsize_t *elmt_size /*in,out*/, const hsize_t *size, hsize_t *stride1) { FUNC_ENTER_PACKAGE_NOERR /* This has to be true because if we optimize the dimensionality down to * zero we still must make one reference. */ assert(1 == H5VM_vector_reduce_product(0, NULL)); /* Combine adjacent memory accesses */ while (*np && stride1[*np - 1] > 0 && (hsize_t)(stride1[*np - 1]) == *elmt_size) { *elmt_size *= size[*np - 1]; if (--*np) stride1[*np - 1] += size[*np] * stride1[*np]; } FUNC_LEAVE_NOAPI_VOID } /*------------------------------------------------------------------------- * Function: H5VM__stride_optimize2 * * Purpose: Given two stride vectors which reference elements of the * specified size, optimize the dimensionality, the stride * vectors, and the element size to minimize the dimensionality * and the number of memory accesses. * * All arguments are passed by reference and their values may be * modified by this function. * * Return: Non-negative on success/Negative on failure * *------------------------------------------------------------------------- */ static void H5VM__stride_optimize2(unsigned *np /*in,out*/, hsize_t *elmt_size /*in,out*/, const hsize_t *size, hsize_t *stride1, hsize_t *stride2) { FUNC_ENTER_PACKAGE_NOERR /* This has to be true because if we optimize the dimensionality down to * zero we still must make one reference. */ assert(1 == H5VM_vector_reduce_product(0, NULL)); assert(*elmt_size > 0); /* Combine adjacent memory accesses */ /* Unroll loop for common cases */ switch (*np) { case 1: /* For 0-D datasets (dunno if this ever gets used...) */ if (stride1[0] == *elmt_size && stride2[0] == *elmt_size) { *elmt_size *= size[0]; --*np; /* *np decrements to a value of 0 now */ } /* end if */ break; case 2: /* For 1-D datasets */ if (stride1[1] == *elmt_size && stride2[1] == *elmt_size) { *elmt_size *= size[1]; --*np; /* *np decrements to a value of 1 now */ stride1[0] += size[1] * stride1[1]; stride2[0] += size[1] * stride2[1]; if (stride1[0] == *elmt_size && stride2[0] == *elmt_size) { *elmt_size *= size[0]; --*np; /* *np decrements to a value of 0 now */ } /* end if */ } /* end if */ break; case 3: /* For 2-D datasets */ if (stride1[2] == *elmt_size && stride2[2] == *elmt_size) { *elmt_size *= size[2]; --*np; /* *np decrements to a value of 2 now */ stride1[1] += size[2] * stride1[2]; stride2[1] += size[2] * stride2[2]; if (stride1[1] == *elmt_size && stride2[1] == *elmt_size) { *elmt_size *= size[1]; --*np; /* *np decrements to a value of 1 now */ stride1[0] += size[1] * stride1[1]; stride2[0] += size[1] * stride2[1]; if (stride1[0] == *elmt_size && stride2[0] == *elmt_size) { *elmt_size *= size[0]; --*np; /* *np decrements to a value of 0 now */ } /* end if */ } /* end if */ } /* end if */ break; case 4: /* For 3-D datasets */ if (stride1[3] == *elmt_size && stride2[3] == *elmt_size) { *elmt_size *= size[3]; --*np; /* *np decrements to a value of 3 now */ stride1[2] += size[3] * stride1[3]; stride2[2] += size[3] * stride2[3]; if (stride1[2] == *elmt_size && stride2[2] == *elmt_size) { *elmt_size *= size[2]; --*np; /* *np decrements to a value of 2 now */ stride1[1] += size[2] * stride1[2]; stride2[1] += size[2] * stride2[2]; if (stride1[1] == *elmt_size && stride2[1] == *elmt_size) { *elmt_size *= size[1]; --*np; /* *np decrements to a value of 1 now */ stride1[0] += size[1] * stride1[1]; stride2[0] += size[1] * stride2[1]; if (stride1[0] == *elmt_size && stride2[0] == *elmt_size) { *elmt_size *= size[0]; --*np; /* *np decrements to a value of 0 now */ } /* end if */ } /* end if */ } /* end if */ } /* end if */ break; default: while (*np && stride1[*np - 1] == *elmt_size && stride2[*np - 1] == *elmt_size) { *elmt_size *= size[*np - 1]; if (--*np) { stride1[*np - 1] += size[*np] * stride1[*np]; stride2[*np - 1] += size[*np] * stride2[*np]; } } break; } /* end switch */ FUNC_LEAVE_NOAPI_VOID } /*------------------------------------------------------------------------- * Function: H5VM_hyper_stride * * Purpose: Given a description of a hyperslab, this function returns * (through STRIDE[]) the byte strides appropriate for accessing * all bytes of the hyperslab and the byte offset where the * striding will begin. The SIZE can be passed to the various * stride functions. * * The dimensionality of the whole array, the hyperslab, and the * returned stride array is N. The whole array dimensions are * TOTAL_SIZE and the hyperslab is at offset OFFSET and has * dimensions SIZE. * * The stride and starting point returned will cause the * hyperslab elements to be referenced in C order. * * Return: Byte offset from beginning of array to start of striding. * *------------------------------------------------------------------------- */ hsize_t H5VM_hyper_stride(unsigned n, const hsize_t *size, const hsize_t *total_size, const hsize_t *offset, hsize_t *stride /*out*/) { hsize_t skip; /*starting point byte offset */ hsize_t acc; /*accumulator */ int i; /*counter */ hsize_t ret_value; /* Return value */ FUNC_ENTER_NOAPI_NOINIT_NOERR assert(n <= H5VM_HYPER_NDIMS); assert(size); assert(total_size); assert(stride); /* init */ assert(n > 0); stride[n - 1] = 1; skip = offset ? offset[n - 1] : 0; switch (n) { case 2: /* 1-D dataset */ assert(total_size[1] >= size[1]); stride[0] = total_size[1] - size[1]; /*overflow checked*/ acc = total_size[1]; skip += acc * (offset ? offset[0] : 0); break; case 3: /* 2-D dataset */ assert(total_size[2] >= size[2]); stride[1] = total_size[2] - size[2]; /*overflow checked*/ acc = total_size[2]; skip += acc * (offset ? (hsize_t)offset[1] : 0); assert(total_size[1] >= size[1]); stride[0] = acc * (total_size[1] - size[1]); /*overflow checked*/ acc *= total_size[1]; skip += acc * (offset ? (hsize_t)offset[0] : 0); break; case 4: /* 3-D dataset */ assert(total_size[3] >= size[3]); stride[2] = total_size[3] - size[3]; /*overflow checked*/ acc = total_size[3]; skip += acc * (offset ? (hsize_t)offset[2] : 0); assert(total_size[2] >= size[2]); stride[1] = acc * (total_size[2] - size[2]); /*overflow checked*/ acc *= total_size[2]; skip += acc * (offset ? (hsize_t)offset[1] : 0); assert(total_size[1] >= size[1]); stride[0] = acc * (total_size[1] - size[1]); /*overflow checked*/ acc *= total_size[1]; skip += acc * (offset ? (hsize_t)offset[0] : 0); break; default: /* others */ for (i = (int)(n - 2), acc = 1; i >= 0; --i) { assert(total_size[i + 1] >= size[i + 1]); stride[i] = acc * (total_size[i + 1] - size[i + 1]); /*overflow checked*/ acc *= total_size[i + 1]; skip += acc * (offset ? (hsize_t)offset[i] : 0); } break; } /* end switch */ /* Set return value */ ret_value = skip; FUNC_LEAVE_NOAPI(ret_value) } /*------------------------------------------------------------------------- * Function: H5VM_hyper_eq * * Purpose: Determines whether two hyperslabs are equal. This function * assumes that both hyperslabs are relative to the same array, * for if not, they could not possibly be equal. * * Return: true if the hyperslabs are equal (that is, * both refer to exactly the same elements of an * array) * * false otherwise * * Never returns FAIL * *------------------------------------------------------------------------- */ htri_t H5VM_hyper_eq(unsigned n, const hsize_t *offset1, const hsize_t *size1, const hsize_t *offset2, const hsize_t *size2) { hsize_t nelmts1 = 1, nelmts2 = 1; unsigned i; htri_t ret_value = true; /* Return value */ /* Use FUNC_ENTER_NOAPI_NOINIT_NOERR here to avoid performance issues */ FUNC_ENTER_NOAPI_NOINIT_NOERR if (n == 0) HGOTO_DONE(true); for (i = 0; i < n; i++) { if ((offset1 ? offset1[i] : 0) != (offset2 ? offset2[i] : 0)) HGOTO_DONE(false); if ((size1 ? size1[i] : 0) != (size2 ? size2[i] : 0)) HGOTO_DONE(false); if (0 == (nelmts1 *= (size1 ? size1[i] : 0))) HGOTO_DONE(false); if (0 == (nelmts2 *= (size2 ? size2[i] : 0))) HGOTO_DONE(false); } done: FUNC_LEAVE_NOAPI(ret_value) } /*------------------------------------------------------------------------- * Function: H5VM_hyper_fill * * Purpose: Similar to memset() except it operates on hyperslabs... * * Fills a hyperslab of array BUF with some value VAL. BUF * is treated like a C-order array with N dimensions where the * size of each dimension is TOTAL_SIZE[]. The hyperslab which * will be filled with VAL begins at byte offset OFFSET[] from * the minimum corner of BUF and continues for SIZE[] bytes in * each dimension. * * Return: Non-negative on success/Negative on failure * *------------------------------------------------------------------------- */ herr_t H5VM_hyper_fill(unsigned n, const hsize_t *_size, const hsize_t *total_size, const hsize_t *offset, void *_dst, unsigned fill_value) { uint8_t *dst = (uint8_t *)_dst; /*cast for ptr arithmetic */ hsize_t size[H5VM_HYPER_NDIMS]; /*a modifiable copy of _size */ hsize_t dst_stride[H5VM_HYPER_NDIMS]; /*destination stride info */ hsize_t dst_start; /*byte offset to start of stride*/ hsize_t elmt_size = 1; /*bytes per element */ herr_t ret_value; /*function return status */ #ifndef NDEBUG unsigned u; #endif FUNC_ENTER_NOAPI_NOINIT_NOERR /* check args */ assert(n > 0 && n <= H5VM_HYPER_NDIMS); assert(_size); assert(total_size); assert(dst); #ifndef NDEBUG for (u = 0; u < n; u++) { assert(_size[u] > 0); assert(total_size[u] > 0); } #endif /* Copy the size vector so we can modify it */ H5VM_vector_cpy(n, size, _size); /* Compute an optimal destination stride vector */ dst_start = H5VM_hyper_stride(n, size, total_size, offset, dst_stride); H5VM__stride_optimize1(&n, &elmt_size, size, dst_stride); /* Copy */ ret_value = H5VM_stride_fill(n, elmt_size, size, dst_stride, dst + dst_start, fill_value); FUNC_LEAVE_NOAPI(ret_value) } /*------------------------------------------------------------------------- * Function: H5VM_hyper_copy * * Purpose: Copies a hyperslab from the source to the destination. * * A hyperslab is a logically contiguous region of * multi-dimensional size SIZE of an array whose dimensionality * is N and whose total size is DST_TOTAL_SIZE or SRC_TOTAL_SIZE. * The minimum corner of the hyperslab begins at a * multi-dimensional offset from the minimum corner of the DST * (destination) or SRC (source) array. The sizes and offsets * are assumed to be in C order, that is, the first size/offset * varies the slowest while the last varies the fastest in the * mapping from N-dimensional space to linear space. This * function assumes that the array elements are single bytes (if * your array has multi-byte elements then add an additional * dimension whose size is that of your element). * * The SRC and DST array may be the same array, but the results * are undefined if the source hyperslab overlaps the * destination hyperslab. * * Return: Non-negative on success/Negative on failure * *------------------------------------------------------------------------- */ herr_t H5VM_hyper_copy(unsigned n, const hsize_t *_size, const hsize_t *dst_size, const hsize_t *dst_offset, void *_dst, const hsize_t *src_size, const hsize_t *src_offset, const void *_src) { const uint8_t *src = (const uint8_t *)_src; /*cast for ptr arithmtc */ uint8_t *dst = (uint8_t *)_dst; /*cast for ptr arithmtc */ hsize_t size[H5VM_HYPER_NDIMS]; /*a modifiable _size */ hsize_t src_stride[H5VM_HYPER_NDIMS]; /*source stride info */ hsize_t dst_stride[H5VM_HYPER_NDIMS]; /*dest stride info */ hsize_t dst_start, src_start; /*offset to start at */ hsize_t elmt_size = 1; /*element size in bytes */ herr_t ret_value; /*return status */ #ifndef NDEBUG unsigned u; #endif FUNC_ENTER_NOAPI_NOINIT_NOERR /* check args */ assert(n > 0 && n <= H5VM_HYPER_NDIMS); assert(_size); assert(dst_size); assert(src_size); assert(dst); assert(src); #ifndef NDEBUG for (u = 0; u < n; u++) { assert(_size[u] > 0); assert(dst_size[u] > 0); assert(src_size[u] > 0); } #endif /* Copy the size vector so we can modify it */ H5VM_vector_cpy(n, size, _size); /* Compute stride vectors for source and destination */ #ifdef NO_INLINED_CODE dst_start = H5VM_hyper_stride(n, size, dst_size, dst_offset, dst_stride); src_start = H5VM_hyper_stride(n, size, src_size, src_offset, src_stride); #else /* NO_INLINED_CODE */ /* in-line version of two calls to H5VM_hyper_stride() */ { hsize_t dst_acc; /*accumulator */ hsize_t src_acc; /*accumulator */ int ii; /*counter */ /* init */ assert(n > 0); dst_stride[n - 1] = 1; src_stride[n - 1] = 1; dst_start = dst_offset ? dst_offset[n - 1] : 0; src_start = src_offset ? src_offset[n - 1] : 0; /* Unroll loop for common cases */ switch (n) { case 2: assert(dst_size[1] >= size[1]); assert(src_size[1] >= size[1]); dst_stride[0] = dst_size[1] - size[1]; /*overflow checked*/ src_stride[0] = src_size[1] - size[1]; /*overflow checked*/ dst_acc = dst_size[1]; src_acc = src_size[1]; dst_start += dst_acc * (dst_offset ? dst_offset[0] : 0); src_start += src_acc * (src_offset ? src_offset[0] : 0); break; case 3: assert(dst_size[2] >= size[2]); assert(src_size[2] >= size[2]); dst_stride[1] = dst_size[2] - size[2]; /*overflow checked*/ src_stride[1] = src_size[2] - size[2]; /*overflow checked*/ dst_acc = dst_size[2]; src_acc = src_size[2]; dst_start += dst_acc * (dst_offset ? dst_offset[1] : 0); src_start += src_acc * (src_offset ? src_offset[1] : 0); assert(dst_size[1] >= size[1]); assert(src_size[1] >= size[1]); dst_stride[0] = dst_acc * (dst_size[1] - size[1]); /*overflow checked*/ src_stride[0] = src_acc * (src_size[1] - size[1]); /*overflow checked*/ dst_acc *= dst_size[1]; src_acc *= src_size[1]; dst_start += dst_acc * (dst_offset ? dst_offset[0] : 0); src_start += src_acc * (src_offset ? src_offset[0] : 0); break; case 4: assert(dst_size[3] >= size[3]); assert(src_size[3] >= size[3]); dst_stride[2] = dst_size[3] - size[3]; /*overflow checked*/ src_stride[2] = src_size[3] - size[3]; /*overflow checked*/ dst_acc = dst_size[3]; src_acc = src_size[3]; dst_start += dst_acc * (dst_offset ? dst_offset[2] : 0); src_start += src_acc * (src_offset ? src_offset[2] : 0); assert(dst_size[2] >= size[2]); assert(src_size[2] >= size[2]); dst_stride[1] = dst_acc * (dst_size[2] - size[2]); /*overflow checked*/ src_stride[1] = src_acc * (src_size[2] - size[2]); /*overflow checked*/ dst_acc *= dst_size[2]; src_acc *= src_size[2]; dst_start += dst_acc * (dst_offset ? dst_offset[1] : 0); src_start += src_acc * (src_offset ? src_offset[1] : 0); assert(dst_size[1] >= size[1]); assert(src_size[1] >= size[1]); dst_stride[0] = dst_acc * (dst_size[1] - size[1]); /*overflow checked*/ src_stride[0] = src_acc * (src_size[1] - size[1]); /*overflow checked*/ dst_acc *= dst_size[1]; src_acc *= src_size[1]; dst_start += dst_acc * (dst_offset ? dst_offset[0] : 0); src_start += src_acc * (src_offset ? src_offset[0] : 0); break; default: /* others */ for (ii = (int)(n - 2), dst_acc = 1, src_acc = 1; ii >= 0; --ii) { assert(dst_size[ii + 1] >= size[ii + 1]); assert(src_size[ii + 1] >= size[ii + 1]); dst_stride[ii] = dst_acc * (dst_size[ii + 1] - size[ii + 1]); /*overflow checked*/ src_stride[ii] = src_acc * (src_size[ii + 1] - size[ii + 1]); /*overflow checked*/ dst_acc *= dst_size[ii + 1]; src_acc *= src_size[ii + 1]; dst_start += dst_acc * (dst_offset ? dst_offset[ii] : 0); src_start += src_acc * (src_offset ? src_offset[ii] : 0); } break; } /* end switch */ } #endif /* NO_INLINED_CODE */ /* Optimize the strides as a pair */ H5VM__stride_optimize2(&n, &elmt_size, size, dst_stride, src_stride); /* Perform the copy in terms of stride */ ret_value = H5VM_stride_copy(n, elmt_size, size, dst_stride, dst + dst_start, src_stride, src + src_start); FUNC_LEAVE_NOAPI(ret_value) } /*------------------------------------------------------------------------- * Function: H5VM_stride_fill * * Purpose: Fills all bytes of a hyperslab with the same value using * memset(). * * Return: Non-negative on success/Negative on failure * *------------------------------------------------------------------------- */ herr_t H5VM_stride_fill(unsigned n, hsize_t elmt_size, const hsize_t *size, const hsize_t *stride, void *_dst, unsigned fill_value) { uint8_t *dst = (uint8_t *)_dst; /*cast for ptr arithmetic */ hsize_t idx[H5VM_HYPER_NDIMS]; /*1-origin indices */ hsize_t nelmts; /*number of elements to fill */ hsize_t i; /*counter */ int j; /*counter */ bool carry; /*subtraction carray value */ FUNC_ENTER_NOAPI_NOINIT_NOERR assert(elmt_size < SIZE_MAX); H5VM_vector_cpy(n, idx, size); nelmts = H5VM_vector_reduce_product(n, size); for (i = 0; i < nelmts; i++) { /* Copy an element */ H5_CHECK_OVERFLOW(elmt_size, hsize_t, size_t); memset(dst, (int)fill_value, (size_t)elmt_size); /* Decrement indices and advance pointer */ for (j = (int)(n - 1), carry = true; j >= 0 && carry; --j) { dst += stride[j]; if (--idx[j]) carry = false; else { assert(size); idx[j] = size[j]; } /* end else */ } } FUNC_LEAVE_NOAPI(SUCCEED) } /*------------------------------------------------------------------------- * Function: H5VM_stride_copy * * Purpose: Uses DST_STRIDE and SRC_STRIDE to advance through the arrays * DST and SRC while copying bytes from SRC to DST. This * function minimizes the number of calls to memcpy() by * combining various strides, but it will never touch memory * outside the hyperslab defined by the strides. * * Note: If the src_stride is all zero and elmt_size is one, then it's * probably more efficient to use H5VM_stride_fill() instead. * * Return: Non-negative on success/Negative on failure * *------------------------------------------------------------------------- */ herr_t H5VM_stride_copy(unsigned n, hsize_t elmt_size, const hsize_t *size, const hsize_t *dst_stride, void *_dst, const hsize_t *src_stride, const void *_src) { uint8_t *dst = (uint8_t *)_dst; /*cast for ptr arithmetic*/ const uint8_t *src = (const uint8_t *)_src; /*cast for ptr arithmetic*/ hsize_t idx[H5VM_HYPER_NDIMS]; /*1-origin indices */ hsize_t nelmts; /*num elements to copy */ hsize_t i; /*counter */ int j; /*counters */ bool carry; /*carray for subtraction*/ FUNC_ENTER_NOAPI_NOINIT_NOERR assert(elmt_size < SIZE_MAX); if (n) { H5VM_vector_cpy(n, idx, size); nelmts = H5VM_vector_reduce_product(n, size); for (i = 0; i < nelmts; i++) { /* Copy an element */ H5_CHECK_OVERFLOW(elmt_size, hsize_t, size_t); H5MM_memcpy(dst, src, (size_t)elmt_size); /* Decrement indices and advance pointers */ for (j = (int)(n - 1), carry = true; j >= 0 && carry; --j) { src += src_stride[j]; dst += dst_stride[j]; if (--idx[j]) carry = false; else { assert(size); idx[j] = size[j]; } } } } else { H5_CHECK_OVERFLOW(elmt_size, hsize_t, size_t); H5MM_memcpy(dst, src, (size_t)elmt_size); } FUNC_LEAVE_NOAPI(SUCCEED) } /*------------------------------------------------------------------------- * Function: H5VM_stride_copy_s * * Purpose: Uses DST_STRIDE and SRC_STRIDE to advance through the arrays * DST and SRC while copying bytes from SRC to DST. This * function minimizes the number of calls to memcpy() by * combining various strides, but it will never touch memory * outside the hyperslab defined by the strides. * * Note: If the src_stride is all zero and elmt_size is one, then it's * probably more efficient to use H5VM_stride_fill() instead. * * Return: Non-negative on success/Negative on failure * *------------------------------------------------------------------------- */ herr_t H5VM_stride_copy_s(unsigned n, hsize_t elmt_size, const hsize_t *size, const hssize_t *dst_stride, void *_dst, const hssize_t *src_stride, const void *_src) { uint8_t *dst = (uint8_t *)_dst; /*cast for ptr arithmetic*/ const uint8_t *src = (const uint8_t *)_src; /*cast for ptr arithmetic*/ hsize_t idx[H5VM_HYPER_NDIMS]; /*1-origin indices */ hsize_t nelmts; /*num elements to copy */ hsize_t i; /*counter */ int j; /*counters */ bool carry; /*carray for subtraction*/ FUNC_ENTER_NOAPI_NOINIT_NOERR assert(elmt_size < SIZE_MAX); if (n) { H5VM_vector_cpy(n, idx, size); nelmts = H5VM_vector_reduce_product(n, size); for (i = 0; i < nelmts; i++) { /* Copy an element */ H5_CHECK_OVERFLOW(elmt_size, hsize_t, size_t); H5MM_memcpy(dst, src, (size_t)elmt_size); /* Decrement indices and advance pointers */ for (j = (int)(n - 1), carry = true; j >= 0 && carry; --j) { src += src_stride[j]; dst += dst_stride[j]; if (--idx[j]) carry = false; else { assert(size); idx[j] = size[j]; } } } } else { H5_CHECK_OVERFLOW(elmt_size, hsize_t, size_t); H5MM_memcpy(dst, src, (size_t)elmt_size); } FUNC_LEAVE_NOAPI(SUCCEED) } /*------------------------------------------------------------------------- * Function: H5VM_array_fill * * Purpose: Fills all bytes of an array with the same value using * memset(). Increases amount copied by power of two until the * halfway point is crossed, then copies the rest in one swoop. * * Return: Non-negative on success/Negative on failure * *------------------------------------------------------------------------- */ herr_t H5VM_array_fill(void *_dst, const void *src, size_t size, size_t count) { size_t copy_size; /* size of the buffer to copy */ size_t copy_items; /* number of items currently copying*/ size_t items_left; /* number of items left to copy */ uint8_t *dst = (uint8_t *)_dst; /* alias for pointer arithmetic */ FUNC_ENTER_NOAPI_NOINIT_NOERR assert(dst); assert(src); assert(size < SIZE_MAX && size > 0); assert(count < SIZE_MAX && count > 0); H5MM_memcpy(dst, src, size); /* copy first item */ /* Initialize counters, etc. while compensating for first element copied */ copy_size = size; copy_items = 1; items_left = count - 1; dst += size; /* copy until we've copied at least half of the items */ while (items_left >= copy_items) { H5MM_memcpy(dst, _dst, copy_size); /* copy the current chunk */ dst += copy_size; /* move the offset for the next chunk */ items_left -= copy_items; /* decrement the number of items left */ copy_size *= 2; /* increase the size of the chunk to copy */ copy_items *= 2; /* increase the count of items we are copying */ } /* end while */ if (items_left > 0) /* if there are any items left to copy */ H5MM_memcpy(dst, _dst, items_left * size); FUNC_LEAVE_NOAPI(SUCCEED) } /* H5VM_array_fill() */ /*------------------------------------------------------------------------- * Function: H5VM_array_down * * Purpose: Given a set of dimension sizes, calculate the size of each * "down" slice. This is the size of the dimensions for all the * dimensions below the current one, which is used for indexing * offsets in this dimension. * * Return: void * *------------------------------------------------------------------------- */ void H5VM_array_down(unsigned n, const hsize_t *total_size, hsize_t *down) { hsize_t acc; /* Accumulator */ int i; /* Counter */ FUNC_ENTER_NOAPI_NOINIT_NOERR assert(n <= H5VM_HYPER_NDIMS); assert(total_size); assert(down); /* Build the sizes of each dimension in the array * (From fastest to slowest) */ for (i = (int)(n - 1), acc = 1; i >= 0; i--) { down[i] = acc; acc *= total_size[i]; } FUNC_LEAVE_NOAPI_VOID } /* end H5VM_array_down() */ /*------------------------------------------------------------------------- * Function: H5VM_array_offset_pre * * Purpose: Given a coordinate description of a location in an array, this * function returns the byte offset of the coordinate. * * The dimensionality of the whole array, and the offset is N. * The whole array dimensions are TOTAL_SIZE and the coordinate * is at offset OFFSET. * * Return: Byte offset from beginning of array to element offset * *------------------------------------------------------------------------- */ hsize_t H5VM_array_offset_pre(unsigned n, const hsize_t *acc, const hsize_t *offset) { unsigned u; /* Local index variable */ hsize_t ret_value; /* Return value */ FUNC_ENTER_NOAPI_NOINIT_NOERR assert(n <= H5VM_HYPER_NDIMS); assert(acc); assert(offset); /* Compute offset in array */ for (u = 0, ret_value = 0; u < n; u++) ret_value += acc[u] * offset[u]; FUNC_LEAVE_NOAPI(ret_value) } /* end H5VM_array_offset_pre() */ /*------------------------------------------------------------------------- * Function: H5VM_array_offset * * Purpose: Given a coordinate description of a location in an array, * this function returns the byte offset of the coordinate. * * The dimensionality of the whole array, and the offset is N. * The whole array dimensions are TOTAL_SIZE and the coordinate * is at offset OFFSET. * * Return: Byte offset from beginning of array to element offset * *------------------------------------------------------------------------- */ hsize_t H5VM_array_offset(unsigned n, const hsize_t *total_size, const hsize_t *offset) { hsize_t acc_arr[H5VM_HYPER_NDIMS]; /* Accumulated size of down dimensions */ hsize_t ret_value; /* Return value */ FUNC_ENTER_NOAPI_NOERR assert(n <= H5VM_HYPER_NDIMS); assert(total_size); assert(offset); /* Build the sizes of each dimension in the array */ H5VM_array_down(n, total_size, acc_arr); /* Set return value */ ret_value = H5VM_array_offset_pre(n, acc_arr, offset); FUNC_LEAVE_NOAPI(ret_value) } /* end H5VM_array_offset() */ /*------------------------------------------------------------------------- * Function: H5VM_array_calc_pre * * Purpose: Given a linear offset in an array, the dimensions of that * array and the pre-computed 'down' (accumulator) sizes, this * function computes the coordinates of that offset in the array. * * The dimensionality of the whole array, and the coordinates is N. * The array dimensions are TOTAL_SIZE and the coordinates * are returned in COORD. The linear offset is in OFFSET. * * Return: Non-negative on success/Negative on failure * *------------------------------------------------------------------------- */ herr_t H5VM_array_calc_pre(hsize_t offset, unsigned n, const hsize_t *down, hsize_t *coords) { unsigned u; /* Local index variable */ FUNC_ENTER_NOAPI_NOINIT_NOERR /* Sanity check */ assert(n <= H5VM_HYPER_NDIMS); assert(coords); /* Compute the coordinates from the offset */ for (u = 0; u < n; u++) { coords[u] = offset / down[u]; offset %= down[u]; } /* end for */ FUNC_LEAVE_NOAPI(SUCCEED) } /* end H5VM_array_calc_pre() */ /*------------------------------------------------------------------------- * Function: H5VM_array_calc * * Purpose: Given a linear offset in an array and the dimensions of that * array, this function computes the coordinates of that offset * in the array. * * The dimensionality of the whole array, and the coordinates is N. * The array dimensions are TOTAL_SIZE and the coordinates * are returned in COORD. The linear offset is in OFFSET. * * Return: Non-negative on success/Negative on failure * *------------------------------------------------------------------------- */ herr_t H5VM_array_calc(hsize_t offset, unsigned n, const hsize_t *total_size, hsize_t *coords) { hsize_t idx[H5VM_HYPER_NDIMS]; /* Size of each dimension in bytes */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(FAIL) /* Sanity check */ assert(n <= H5VM_HYPER_NDIMS); assert(total_size); assert(coords); /* Build the sizes of each dimension in the array */ H5VM_array_down(n, total_size, idx); /* Compute the coordinates from the offset */ if (H5VM_array_calc_pre(offset, n, idx, coords) < 0) HGOTO_ERROR(H5E_INTERNAL, H5E_BADVALUE, FAIL, "can't compute coordinates"); done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5VM_array_calc() */ /*------------------------------------------------------------------------- * Function: H5VM_chunk_index * * Purpose: Given a coordinate offset (COORD), the size of each chunk * (CHUNK), the number of chunks in each dimension (NCHUNKS) * and the number of dimensions of all of these (NDIMS), calculate * a "chunk index" for the chunk that the coordinate offset is * located in. * * The chunk index starts at 0 and increases according to the * fastest changing dimension, then the next fastest, etc. * * For example, with a 3x5 chunk size and 6 chunks in the fastest * changing dimension and 3 chunks in the slowest changing * dimension, the chunk indices are as follows: * * +-----+-----+-----+-----+-----+-----+ * | | | | | | | * | 0 | 1 | 2 | 3 | 4 | 5 | * | | | | | | | * +-----+-----+-----+-----+-----+-----+ * | | | | | | | * | 6 | 7 | 8 | 9 | 10 | 11 | * | | | | | | | * +-----+-----+-----+-----+-----+-----+ * | | | | | | | * | 12 | 13 | 14 | 15 | 16 | 17 | * | | | | | | | * +-----+-----+-----+-----+-----+-----+ * * The chunk index is placed in the CHUNK_IDX location for return * from this function * * Return: Chunk index on success (can't fail) * *------------------------------------------------------------------------- */ hsize_t H5VM_chunk_index(unsigned ndims, const hsize_t *coord, const uint32_t *chunk, const hsize_t *down_nchunks) { hsize_t scaled_coord[H5VM_HYPER_NDIMS]; /* Scaled, coordinates, in terms of chunks */ hsize_t chunk_idx; /* Chunk index computed */ FUNC_ENTER_NOAPI_NOINIT_NOERR /* Sanity check */ assert(ndims <= H5VM_HYPER_NDIMS); assert(coord); assert(chunk); assert(down_nchunks); /* Defer to H5VM_chunk_index_scaled */ chunk_idx = H5VM_chunk_index_scaled(ndims, coord, chunk, down_nchunks, scaled_coord); FUNC_LEAVE_NOAPI(chunk_idx) } /* end H5VM_chunk_index() */ /*------------------------------------------------------------------------- * Function: H5VM_chunk_scaled * * Purpose: Compute the scaled coordinates for a chunk offset * * Return: void * *------------------------------------------------------------------------- */ void H5VM_chunk_scaled(unsigned ndims, const hsize_t *coord, const uint32_t *chunk, hsize_t *scaled) { unsigned u; /* Local index variable */ FUNC_ENTER_NOAPI_NOINIT_NOERR /* Sanity check */ assert(ndims <= H5VM_HYPER_NDIMS); assert(coord); assert(chunk); assert(scaled); /* Compute the scaled coordinates for actual coordinates */ /* (Note that the 'scaled' array is an 'OUT' parameter) */ for (u = 0; u < ndims; u++) scaled[u] = coord[u] / chunk[u]; FUNC_LEAVE_NOAPI_VOID } /* end H5VM_chunk_scaled() */ /*------------------------------------------------------------------------- * Function: H5VM_chunk_index_scaled * * Purpose: Given a coordinate offset (COORD), the size of each chunk * (CHUNK), the number of chunks in each dimension (NCHUNKS) * and the number of dimensions of all of these (NDIMS), calculate * a "chunk index" for the chunk that the coordinate offset is * located in. * * The chunk index starts at 0 and increases according to the * fastest changing dimension, then the next fastest, etc. * * For example, with a 3x5 chunk size and 6 chunks in the fastest * changing dimension and 3 chunks in the slowest changing * dimension, the chunk indices are as follows: * * +-----+-----+-----+-----+-----+-----+ * | | | | | | | * | 0 | 1 | 2 | 3 | 4 | 5 | * | | | | | | | * +-----+-----+-----+-----+-----+-----+ * | | | | | | | * | 6 | 7 | 8 | 9 | 10 | 11 | * | | | | | | | * +-----+-----+-----+-----+-----+-----+ * | | | | | | | * | 12 | 13 | 14 | 15 | 16 | 17 | * | | | | | | | * +-----+-----+-----+-----+-----+-----+ * * The chunk index is placed in the CHUNK_IDX location for return * from this function * * Note: This routine is identical to H5VM_chunk_index(), except for * caching the scaled information. Make changes in both places. * * Return: Chunk index on success (can't fail) * *------------------------------------------------------------------------- */ hsize_t H5VM_chunk_index_scaled(unsigned ndims, const hsize_t *coord, const uint32_t *chunk, const hsize_t *down_nchunks, hsize_t *scaled) { hsize_t chunk_idx; /* Computed chunk index */ unsigned u; /* Local index variable */ FUNC_ENTER_NOAPI_NOINIT_NOERR /* Sanity check */ assert(ndims <= H5VM_HYPER_NDIMS); assert(coord); assert(chunk); assert(down_nchunks); assert(scaled); /* Compute the scaled coordinates for actual coordinates */ /* (Note that the 'scaled' array is an 'OUT' parameter) */ for (u = 0; u < ndims; u++) scaled[u] = coord[u] / chunk[u]; /* Compute the chunk index */ chunk_idx = H5VM_array_offset_pre(ndims, down_nchunks, scaled); FUNC_LEAVE_NOAPI(chunk_idx) } /* end H5VM_chunk_index_scaled() */ /*------------------------------------------------------------------------- * Function: H5VM_opvv * * Purpose: Perform an operation on a source & destination sequences * of offset/length pairs. Each set of sequences has an array * of lengths, an array of offsets, the maximum number of * sequences and the current sequence to start at in the sequence. * * There may be different numbers of bytes in the source and * destination sequences, the operation stops when either the * source or destination sequence runs out of information. * * Note: The algorithm in this routine is [basically] the same as for * H5VM_memcpyvv(). Changes should be made to both! * * Return: Non-negative # of bytes operated on, on success/Negative on failure * *------------------------------------------------------------------------- */ ssize_t H5VM_opvv(size_t dst_max_nseq, size_t *dst_curr_seq, size_t dst_len_arr[], hsize_t dst_off_arr[], size_t src_max_nseq, size_t *src_curr_seq, size_t src_len_arr[], hsize_t src_off_arr[], H5VM_opvv_func_t op, void *op_data) { hsize_t *max_dst_off_ptr, *max_src_off_ptr; /* Pointers to max. source and destination offset locations */ hsize_t *dst_off_ptr, *src_off_ptr; /* Pointers to source and destination offset arrays */ size_t *dst_len_ptr, *src_len_ptr; /* Pointers to source and destination length arrays */ hsize_t tmp_dst_off, tmp_src_off; /* Temporary source and destination offset values */ size_t tmp_dst_len, tmp_src_len; /* Temporary source and destination length values */ size_t acc_len; /* Accumulated length of sequences */ ssize_t ret_value = 0; /* Return value (Total size of sequence in bytes) */ FUNC_ENTER_NOAPI(FAIL) /* Sanity check */ assert(dst_curr_seq); assert(*dst_curr_seq < dst_max_nseq); assert(dst_len_arr); assert(dst_off_arr); assert(src_curr_seq); assert(*src_curr_seq < src_max_nseq); assert(src_len_arr); assert(src_off_arr); assert(op); /* Set initial offset & length pointers */ dst_len_ptr = dst_len_arr + *dst_curr_seq; dst_off_ptr = dst_off_arr + *dst_curr_seq; src_len_ptr = src_len_arr + *src_curr_seq; src_off_ptr = src_off_arr + *src_curr_seq; /* Get temporary source & destination sequence offsets & lengths */ tmp_dst_len = *dst_len_ptr; tmp_dst_off = *dst_off_ptr; tmp_src_len = *src_len_ptr; tmp_src_off = *src_off_ptr; /* Compute maximum offset pointer values */ max_dst_off_ptr = dst_off_arr + dst_max_nseq; max_src_off_ptr = src_off_arr + src_max_nseq; /* Work through the sequences */ /* (Choose smallest sequence available initially) */ /* Source sequence is less than destination sequence */ if (tmp_src_len < tmp_dst_len) { src_smaller: acc_len = 0; do { /* Make operator callback */ if ((*op)(tmp_dst_off, tmp_src_off, tmp_src_len, op_data) < 0) HGOTO_ERROR(H5E_INTERNAL, H5E_CANTOPERATE, FAIL, "can't perform operation"); /* Accumulate number of bytes copied */ acc_len += tmp_src_len; /* Update destination length */ tmp_dst_off += tmp_src_len; tmp_dst_len -= tmp_src_len; /* Advance source offset & check for being finished */ src_off_ptr++; if (src_off_ptr >= max_src_off_ptr) { /* Roll accumulated changes into appropriate counters */ *dst_off_ptr = tmp_dst_off; *dst_len_ptr = tmp_dst_len; /* Done with sequences */ goto finished; } /* end if */ tmp_src_off = *src_off_ptr; /* Update source information */ src_len_ptr++; tmp_src_len = *src_len_ptr; } while (tmp_src_len < tmp_dst_len); /* Roll accumulated sequence lengths into return value */ ret_value += (ssize_t)acc_len; /* Transition to next state */ if (tmp_dst_len < tmp_src_len) goto dst_smaller; else goto equal; } /* end if */ /* Destination sequence is less than source sequence */ else if (tmp_dst_len < tmp_src_len) { dst_smaller: acc_len = 0; do { /* Make operator callback */ if ((*op)(tmp_dst_off, tmp_src_off, tmp_dst_len, op_data) < 0) HGOTO_ERROR(H5E_INTERNAL, H5E_CANTOPERATE, FAIL, "can't perform operation"); /* Accumulate number of bytes copied */ acc_len += tmp_dst_len; /* Update source length */ tmp_src_off += tmp_dst_len; tmp_src_len -= tmp_dst_len; /* Advance destination offset & check for being finished */ dst_off_ptr++; if (dst_off_ptr >= max_dst_off_ptr) { /* Roll accumulated changes into appropriate counters */ *src_off_ptr = tmp_src_off; *src_len_ptr = tmp_src_len; /* Done with sequences */ goto finished; } /* end if */ tmp_dst_off = *dst_off_ptr; /* Update destination information */ dst_len_ptr++; tmp_dst_len = *dst_len_ptr; } while (tmp_dst_len < tmp_src_len); /* Roll accumulated sequence lengths into return value */ ret_value += (ssize_t)acc_len; /* Transition to next state */ if (tmp_src_len < tmp_dst_len) goto src_smaller; else goto equal; } /* end else-if */ /* Destination sequence and source sequence are same length */ else { equal: acc_len = 0; do { /* Make operator callback */ if ((*op)(tmp_dst_off, tmp_src_off, tmp_dst_len, op_data) < 0) HGOTO_ERROR(H5E_INTERNAL, H5E_CANTOPERATE, FAIL, "can't perform operation"); /* Accumulate number of bytes copied */ acc_len += tmp_dst_len; /* Advance source & destination offset & check for being finished */ src_off_ptr++; dst_off_ptr++; if (src_off_ptr >= max_src_off_ptr || dst_off_ptr >= max_dst_off_ptr) /* Done with sequences */ goto finished; tmp_src_off = *src_off_ptr; tmp_dst_off = *dst_off_ptr; /* Update source information */ src_len_ptr++; tmp_src_len = *src_len_ptr; /* Update destination information */ dst_len_ptr++; tmp_dst_len = *dst_len_ptr; } while (tmp_dst_len == tmp_src_len); /* Roll accumulated sequence lengths into return value */ ret_value += (ssize_t)acc_len; /* Transition to next state */ if (tmp_dst_len < tmp_src_len) goto dst_smaller; else goto src_smaller; } /* end else */ finished: /* Roll accumulated sequence lengths into return value */ ret_value += (ssize_t)acc_len; /* Update current sequence vectors */ *dst_curr_seq = (size_t)(dst_off_ptr - dst_off_arr); *src_curr_seq = (size_t)(src_off_ptr - src_off_arr); done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5VM_opvv() */ /*------------------------------------------------------------------------- * Function: H5VM_memcpyvv * * Purpose: Given source and destination buffers in memory (SRC & DST) * copy sequences of from the source buffer into the destination * buffer. Each set of sequences has an array of lengths, an * array of offsets, the maximum number of sequences and the * current sequence to start at in the sequence. * * There may be different numbers of bytes in the source and * destination sequences, data copying stops when either the * source or destination buffer runs out of sequence information. * * Note: The algorithm in this routine is [basically] the same as for * H5VM_opvv(). Changes should be made to both! * * Return: Non-negative # of bytes copied on success/Negative on failure * *------------------------------------------------------------------------- */ ssize_t H5VM_memcpyvv(void *_dst, size_t dst_max_nseq, size_t *dst_curr_seq, size_t dst_len_arr[], hsize_t dst_off_arr[], const void *_src, size_t src_max_nseq, size_t *src_curr_seq, size_t src_len_arr[], hsize_t src_off_arr[]) { unsigned char *dst; /* Destination buffer pointer */ const unsigned char *src; /* Source buffer pointer */ hsize_t *max_dst_off_ptr, *max_src_off_ptr; /* Pointers to max. source and destination offset locations */ hsize_t *dst_off_ptr, *src_off_ptr; /* Pointers to source and destination offset arrays */ size_t *dst_len_ptr, *src_len_ptr; /* Pointers to source and destination length arrays */ size_t tmp_dst_len; /* Temporary dest. length value */ size_t tmp_src_len; /* Temporary source length value */ size_t acc_len; /* Accumulated length of sequences */ ssize_t ret_value = 0; /* Return value (Total size of sequence in bytes) */ FUNC_ENTER_NOAPI_NOINIT_NOERR /* Sanity check */ assert(_dst); assert(dst_curr_seq); assert(*dst_curr_seq < dst_max_nseq); assert(dst_len_arr); assert(dst_off_arr); assert(_src); assert(src_curr_seq); assert(*src_curr_seq < src_max_nseq); assert(src_len_arr); assert(src_off_arr); /* Set initial offset & length pointers */ dst_len_ptr = dst_len_arr + *dst_curr_seq; dst_off_ptr = dst_off_arr + *dst_curr_seq; src_len_ptr = src_len_arr + *src_curr_seq; src_off_ptr = src_off_arr + *src_curr_seq; /* Get temporary source & destination sequence lengths */ tmp_dst_len = *dst_len_ptr; tmp_src_len = *src_len_ptr; /* Compute maximum offset pointer values */ max_dst_off_ptr = dst_off_arr + dst_max_nseq; max_src_off_ptr = src_off_arr + src_max_nseq; /* Compute buffer offsets */ dst = (unsigned char *)_dst + *dst_off_ptr; src = (const unsigned char *)_src + *src_off_ptr; /* Work through the sequences */ /* (Choose smallest sequence available initially) */ /* Source sequence is less than destination sequence */ if (tmp_src_len < tmp_dst_len) { src_smaller: acc_len = 0; do { /* Copy data */ H5MM_memcpy(dst, src, tmp_src_len); /* Accumulate number of bytes copied */ acc_len += tmp_src_len; /* Update destination length */ tmp_dst_len -= tmp_src_len; /* Advance source offset & check for being finished */ src_off_ptr++; if (src_off_ptr >= max_src_off_ptr) { /* Roll accumulated changes into appropriate counters */ *dst_off_ptr += acc_len; *dst_len_ptr = tmp_dst_len; /* Done with sequences */ goto finished; } /* end if */ /* Update destination pointer */ dst += tmp_src_len; /* Update source information */ src_len_ptr++; tmp_src_len = *src_len_ptr; src = (const unsigned char *)_src + *src_off_ptr; } while (tmp_src_len < tmp_dst_len); /* Roll accumulated sequence lengths into return value */ ret_value += (ssize_t)acc_len; /* Transition to next state */ if (tmp_dst_len < tmp_src_len) goto dst_smaller; else goto equal; } /* end if */ /* Destination sequence is less than source sequence */ else if (tmp_dst_len < tmp_src_len) { dst_smaller: acc_len = 0; do { /* Copy data */ H5MM_memcpy(dst, src, tmp_dst_len); /* Accumulate number of bytes copied */ acc_len += tmp_dst_len; /* Update source length */ tmp_src_len -= tmp_dst_len; /* Advance destination offset & check for being finished */ dst_off_ptr++; if (dst_off_ptr >= max_dst_off_ptr) { /* Roll accumulated changes into appropriate counters */ *src_off_ptr += acc_len; *src_len_ptr = tmp_src_len; /* Done with sequences */ goto finished; } /* end if */ /* Update source pointer */ src += tmp_dst_len; /* Update destination information */ dst_len_ptr++; tmp_dst_len = *dst_len_ptr; dst = (unsigned char *)_dst + *dst_off_ptr; } while (tmp_dst_len < tmp_src_len); /* Roll accumulated sequence lengths into return value */ ret_value += (ssize_t)acc_len; /* Transition to next state */ if (tmp_src_len < tmp_dst_len) goto src_smaller; else goto equal; } /* end else-if */ /* Destination sequence and source sequence are same length */ else { equal: acc_len = 0; do { /* Copy data */ H5MM_memcpy(dst, src, tmp_dst_len); /* Accumulate number of bytes copied */ acc_len += tmp_dst_len; /* Advance source & destination offset & check for being finished */ src_off_ptr++; dst_off_ptr++; if (src_off_ptr >= max_src_off_ptr || dst_off_ptr >= max_dst_off_ptr) /* Done with sequences */ goto finished; /* Update source information */ src_len_ptr++; tmp_src_len = *src_len_ptr; src = (const unsigned char *)_src + *src_off_ptr; /* Update destination information */ dst_len_ptr++; tmp_dst_len = *dst_len_ptr; dst = (unsigned char *)_dst + *dst_off_ptr; } while (tmp_dst_len == tmp_src_len); /* Roll accumulated sequence lengths into return value */ ret_value += (ssize_t)acc_len; /* Transition to next state */ if (tmp_dst_len < tmp_src_len) goto dst_smaller; else goto src_smaller; } /* end else */ finished: /* Roll accumulated sequence lengths into return value */ ret_value += (ssize_t)acc_len; /* Update current sequence vectors */ *dst_curr_seq = (size_t)(dst_off_ptr - dst_off_arr); *src_curr_seq = (size_t)(src_off_ptr - src_off_arr); FUNC_LEAVE_NOAPI(ret_value) } /* end H5VM_memcpyvv() */