From d6410304369bea0644da9ab594f22af6201fa16e Mon Sep 17 00:00:00 2001
From: Quincey Koziol <koziol@hdfgroup.org>
Date: Sat, 18 Jun 2005 00:10:22 -0500
Subject: [svn-r10951] Purpose:     Bug fix

Description:
    Hyperslab selections that had a selection offset and were applied to a
chunked dataset could get into an infinite loop or core dump if the same
selection was used multiple times, with different selection offsets.

Solution:
    "Normalize" the selection with the selection offset, generate the
selections for the chunks overlapped and then "denormalize" the selection.

Platforms tested:
    FreeBSD 4.11 (sleipnir)
    Too minor to require h5committest
---
 release_docs/RELEASE.txt |   5 +++
 src/H5Dio.c              |  22 +++++++---
 src/H5Shyper.c           |  78 +++++++++++++++++++++++++++-------
 src/H5Sprivate.h         |   3 +-
 test/tselect.c           | 108 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 194 insertions(+), 22 deletions(-)

diff --git a/release_docs/RELEASE.txt b/release_docs/RELEASE.txt
index d99eadf..dd327f6 100644
--- a/release_docs/RELEASE.txt
+++ b/release_docs/RELEASE.txt
@@ -290,6 +290,11 @@ Bug Fixes since HDF5-1.6.0 release
 
     Library
     -------
+        - Fixed bug with hyperslab selections that use selection offsets and
+            operate on chunked datasets going into infinite loop or dumping
+            core.  QAK - 2005/06/17
+        - Corrected memory leak and possible corruption when opening a group.
+            QAK - 2005/06/17
         - Added check for opaque datatype tags being too long (check against
             H5T_OPAQUE_TAG_MAX, currently set to 256).  QAK - 2005/06/14
         - Fixed various errors in maintaining names for open objects in the
diff --git a/src/H5Dio.c b/src/H5Dio.c
index b5259a3..f1d12f8 100644
--- a/src/H5Dio.c
+++ b/src/H5Dio.c
@@ -2358,6 +2358,8 @@ H5D_create_chunk_map(const H5D_t *dataset, const H5T_t *mem_type, const H5S_t *f
     H5S_t *tmp_mspace=NULL;     /* Temporary memory dataspace */
     H5S_t *equiv_mspace=NULL;   /* Equivalent memory dataspace */
     hbool_t equiv_mspace_init=0;/* Equivalent memory dataspace was created */
+    hssize_t old_offset[H5O_LAYOUT_NDIMS];  /* Old selection offset */
+    hbool_t file_space_normalized = FALSE;  /* File dataspace was normalized */
     hid_t f_tid=(-1);           /* Temporary copy of file datatype for iteration */
     hbool_t iter_init=0;        /* Selection iteration info has been initialized */
     unsigned f_ndims;           /* The number of dimensions of the file's dataspace */
@@ -2404,6 +2406,16 @@ H5D_create_chunk_map(const H5D_t *dataset, const H5T_t *mem_type, const H5S_t *f
     if(H5S_get_simple_extent_dims(file_space, fm->f_dims, NULL)<0)
         HGOTO_ERROR (H5E_DATASPACE, H5E_CANTGET, FAIL, "unable to get dimensionality")
 
+    /* Normalize hyperslab selections by adjusting them by the offset */
+    /* (It might be worthwhile to normalize both the file and memory dataspaces
+     * before any (contiguous, chunked, etc) file I/O operation, in order to
+     * speed up hyperslab calculations by removing the extra checks and/or
+     * additions involving the offset and the hyperslab selection -QAK)
+     */
+    if(H5S_hyper_normalize_offset(file_space, old_offset)<0)
+        HGOTO_ERROR (H5E_DATASET, H5E_BADSELECT, FAIL, "unable to normalize dataspace by offset")
+    file_space_normalized = TRUE;
+
     /* Decide the number of chunks in each dimension*/
     for(u=0; u<f_ndims; u++) {
         /* Keep the size of the chunk dimensions as hsize_t for various routines */
@@ -2564,6 +2576,10 @@ done:
         if(H5I_dec_ref(f_tid)<0)
             HDONE_ERROR (H5E_DATASET, H5E_CANTFREE, FAIL, "Can't decrement temporary datatype ID")
     } /* end if */
+    if(file_space_normalized) {
+        if(H5S_hyper_denormalize_offset(file_space, old_offset)<0)
+            HGOTO_ERROR (H5E_DATASET, H5E_BADSELECT, FAIL, "unable to normalize dataspace by offset")
+    } /* end if */
    
     FUNC_LEAVE_NOAPI(ret_value)
 } /* end H5D_create_chunk_map() */
@@ -2722,12 +2738,6 @@ H5D_create_chunk_file_map_hyper(const fm_map *fm)
                 HGOTO_ERROR (H5E_DATASPACE, H5E_CANTINIT, FAIL, "unable to convert selection to span trees")
             } /* end if */
 
-            /* Normalize hyperslab selections by adjusting them by the offset */
-            if(H5S_hyper_normalize_offset(tmp_fchunk)<0) {
-                (void)H5S_close(tmp_fchunk);
-                HGOTO_ERROR (H5E_DATASET, H5E_BADSELECT, FAIL, "unable to normalize dataspace by offset")
-            } /* end if */
-
             /* "AND" temporary chunk and current chunk */
             if(H5S_select_hyperslab(tmp_fchunk,H5S_SELECT_AND,coords,NULL,fm->chunk_dim,NULL)<0) {
                 (void)H5S_close(tmp_fchunk);
diff --git a/src/H5Shyper.c b/src/H5Shyper.c
index 6f8c1d1..b9f22fb 100644
--- a/src/H5Shyper.c
+++ b/src/H5Shyper.c
@@ -3671,7 +3671,7 @@ done:
  REVISION LOG
 --------------------------------------------------------------------------*/
 static htri_t
-H5S_hyper_intersect_block_helper (const H5S_hyper_span_info_t *spans, hssize_t *offset, hsize_t *start, hsize_t *end)
+H5S_hyper_intersect_block_helper (const H5S_hyper_span_info_t *spans, hsize_t *start, hsize_t *end)
 {
     H5S_hyper_span_t *curr;     /* Pointer to current span in 1st span tree */
     htri_t status;              /* Status from recursive call */
@@ -3681,7 +3681,6 @@ H5S_hyper_intersect_block_helper (const H5S_hyper_span_info_t *spans, hssize_t *
 
     /* Sanity check */
     assert(spans);
-    assert(offset);
     assert(start);
     assert(end);
 
@@ -3691,11 +3690,11 @@ H5S_hyper_intersect_block_helper (const H5S_hyper_span_info_t *spans, hssize_t *
     /* Iterate over the spans in the tree */
     while(curr!=NULL) {
         /* Check for span entirely before block */
-        if(((hssize_t)curr->high+*offset)<(hssize_t)*start)
+        if(curr->high < *start)
             /* Advance to next span in this dimension */
             curr=curr->next;
         /* If this span is past the end of the block, then we're done in this dimension */
-        else if(((hssize_t)curr->low+*offset)>(hssize_t)*end)
+        else if(curr->low > *end)
             HGOTO_DONE(FALSE)
         /* block & span overlap */
         else {
@@ -3703,7 +3702,7 @@ H5S_hyper_intersect_block_helper (const H5S_hyper_span_info_t *spans, hssize_t *
                 HGOTO_DONE(TRUE)
             else {
                 /* Recursively check spans in next dimension down */
-                if((status=H5S_hyper_intersect_block_helper(curr->down,offset+1,start+1,end+1))<0)
+                if((status=H5S_hyper_intersect_block_helper(curr->down,start+1,end+1))<0)
                     HGOTO_ERROR(H5E_DATASPACE, H5E_BADSELECT, FAIL, "can't perform hyperslab intersection check");
 
                 /* If there is a span intersection in the down dimensions, the span trees overlap */
@@ -3763,7 +3762,7 @@ H5S_hyper_intersect_block (H5S_t *space, hsize_t *start, hsize_t *end)
             HGOTO_ERROR(H5E_DATASPACE, H5E_UNINITIALIZED, FAIL, "dataspace does not have span tree");
 
     /* Perform the span-by-span intersection check */
-    if((ret_value=H5S_hyper_intersect_block_helper(space->select.sel_info.hslab->span_lst,space->select.offset,start,end))<0)
+    if((ret_value=H5S_hyper_intersect_block_helper(space->select.sel_info.hslab->span_lst,start,end))<0)
         HGOTO_ERROR(H5E_DATASPACE, H5E_BADSELECT, FAIL, "can't perform hyperslab intersection check");
 
 done:
@@ -4106,20 +4105,22 @@ done:
     "Normalize" a hyperslab selection by adjusting it's coordinates by the
     amount of the selection offset.
  USAGE
-    herr_t H5S_hyper_normalize_offset(space)
+    herr_t H5S_hyper_normalize_offset(space, old_offset)
         H5S_t *space;           IN/OUT: Pointer to dataspace to move
+        hssize_t *old_offset;   OUT: Pointer to space to store old offset
  RETURNS
     Non-negative on success, negative on failure
  DESCRIPTION
-    Moves the hyperslab selection by the selection offset and then resets
-    the selection offset to zeros.
+    Copies the current selection offset into the array provided, then
+    inverts the selection offset, subtracts the offset from the hyperslab
+    selection and resets the offset to zero.
  GLOBAL VARIABLES
  COMMENTS, BUGS, ASSUMPTIONS
  EXAMPLES
  REVISION LOG
 --------------------------------------------------------------------------*/
 herr_t
-H5S_hyper_normalize_offset(H5S_t *space)
+H5S_hyper_normalize_offset(H5S_t *space, hssize_t *old_offset)
 {
     unsigned u;                         /* Local index variable */
     herr_t      ret_value=SUCCEED;       /* Return value */
@@ -4131,17 +4132,18 @@ H5S_hyper_normalize_offset(H5S_t *space)
     /* Check for 'all' selection, instead of a hyperslab selection */
     /* (Technically, this check shouldn't be in the "hyperslab" routines...) */
     if(H5S_GET_SELECT_TYPE(space)!=H5S_SEL_ALL) {
-        /* Invert the selection offset */
-        for(u=0; u<space->extent.rank; u++)
-            space->select.offset[u] =- space->select.offset[u];
+        /* Copy & invert the selection offset */
+        for(u=0; u<space->extent.rank; u++) {
+            old_offset[u] = space->select.offset[u];
+            space->select.offset[u] = -space->select.offset[u];
+        } /* end for */
 
         /* Call the existing 'adjust' routine */
         if(H5S_hyper_adjust_s(space, space->select.offset)<0)
             HGOTO_ERROR(H5E_DATASPACE, H5E_BADSELECT, FAIL, "can't perform hyperslab normalization");
 
         /* Zero out the selection offset */
-        for(u=0; u<space->extent.rank; u++)
-            space->select.offset[u] = 0;
+        HDmemset(space->select.offset, 0, sizeof(hssize_t) * space->extent.rank);
     } /* end if */
 
 done:
@@ -4151,6 +4153,52 @@ done:
 
 /*--------------------------------------------------------------------------
  NAME
+    H5S_hyper_denormalize_offset
+ PURPOSE
+    "Denormalize" a hyperslab selection by reverse adjusting it's coordinates
+    by the amount of the former selection offset.
+ USAGE
+    herr_t H5S_hyper_normalize_offset(space, old_offset)
+        H5S_t *space;           IN/OUT: Pointer to dataspace to move
+        hssize_t *old_offset;   IN: Pointer to old offset array
+ RETURNS
+    Non-negative on success, negative on failure
+ DESCRIPTION
+    Subtracts the old offset from the current selection (canceling out the
+    effect of the "normalize" routine), then restores the old offset into
+    the dataspace.
+ GLOBAL VARIABLES
+ COMMENTS, BUGS, ASSUMPTIONS
+ EXAMPLES
+ REVISION LOG
+--------------------------------------------------------------------------*/
+herr_t
+H5S_hyper_denormalize_offset(H5S_t *space, const hssize_t *old_offset)
+{
+    herr_t      ret_value=SUCCEED;       /* Return value */
+
+    FUNC_ENTER_NOAPI_NOINIT(H5S_hyper_denormalize_offset);
+
+    assert(space);
+
+    /* Check for 'all' selection, instead of a hyperslab selection */
+    /* (Technically, this check shouldn't be in the "hyperslab" routines...) */
+    if(H5S_GET_SELECT_TYPE(space)!=H5S_SEL_ALL) {
+        /* Call the existing 'adjust' routine */
+        if(H5S_hyper_adjust_s(space, old_offset)<0)
+            HGOTO_ERROR(H5E_DATASPACE, H5E_BADSELECT, FAIL, "can't perform hyperslab normalization");
+
+        /* Copy the selection offset over */
+        HDmemcpy(space->select.offset, old_offset, sizeof(hssize_t) * space->extent.rank);
+    } /* end if */
+
+done:
+    FUNC_LEAVE_NOAPI(ret_value);
+}   /* H5S_hyper_denormalize_offset() */
+
+
+/*--------------------------------------------------------------------------
+ NAME
     H5S_hyper_append_span
  PURPOSE
     Create a new span and append to span list
diff --git a/src/H5Sprivate.h b/src/H5Sprivate.h
index 06c9f02..ac6a297 100644
--- a/src/H5Sprivate.h
+++ b/src/H5Sprivate.h
@@ -258,7 +258,8 @@ H5_DLL htri_t H5S_hyper_intersect_block (H5S_t *space, hsize_t *start, hsize_t *
 H5_DLL herr_t H5S_hyper_adjust_u(H5S_t *space, const hsize_t *offset);
 H5_DLL herr_t H5S_hyper_adjust_s(H5S_t *space, const hssize_t *offset);
 H5_DLL herr_t H5S_hyper_move(H5S_t *space, const hssize_t *offset);
-H5_DLL herr_t H5S_hyper_normalize_offset(H5S_t *space);
+H5_DLL herr_t H5S_hyper_normalize_offset(H5S_t *space, hssize_t *old_offset);
+H5_DLL herr_t H5S_hyper_denormalize_offset(H5S_t *space, const hssize_t *old_offset);
 
 /* Operations on selection iterators */
 H5_DLL herr_t H5S_select_iter_init(H5S_sel_iter_t *iter, const H5S_t *space, size_t elmt_size);
diff --git a/test/tselect.c b/test/tselect.c
index 1801e0c..92e765c 100644
--- a/test/tselect.c
+++ b/test/tselect.c
@@ -133,6 +133,11 @@
 #define SPACE11_DIM2    100
 #define SPACE11_NPOINTS 4
 
+/* Information for offsets w/chunks test #2 */
+#define SPACE12_RANK	        1
+#define SPACE12_DIM0            25
+#define SPACE12_CHUNK_DIM0      5
+
 /* Location comparison function */
 int compare_size_t(const void *s1, const void *s2);
 
@@ -6741,6 +6746,108 @@ test_select_hyper_chunk_offset(void)
 
 /****************************************************************
 **
+**  test_select_hyper_chunk_offset2(): Tests selections on dataspace,
+**      another test to verify that offsets for hyperslab selections are
+**      working in chunked datasets.
+** 
+****************************************************************/
+static void 
+test_select_hyper_chunk_offset2(void)
+{
+    hid_t       file, dataset;  /* handles */
+    hid_t       dataspace;   
+    hid_t       memspace; 
+    hid_t       dcpl;           /* Dataset creation property list */
+    herr_t      status;                             
+    unsigned    data_out[SPACE12_DIM0]; /* output buffer */
+    unsigned    data_in[SPACE12_CHUNK_DIM0]; /* input buffer */
+    hsize_t     dims[SPACE12_RANK]={SPACE12_DIM0};              /* Dimension size */
+    hsize_t     chunk_dims[SPACE12_RANK]={SPACE12_CHUNK_DIM0};  /* Chunk size */
+    hsize_t     start[SPACE12_RANK];    /* Start of hyperslab */
+    hsize_t     count[SPACE12_RANK];    /* Size of hyperslab */
+    hssize_t    offset[SPACE12_RANK];   /* hyperslab offset in the file */
+    unsigned    u, v;           /* Local index variables */
+
+    /* Output message about test being performed */
+    MESSAGE(6, ("Testing more hyperslab selections using offsets in chunked datasets\n"));
+
+    /* Initialize data to write out */
+    for (u = 0; u < SPACE12_DIM0; u++)
+        data_out[u] = u;
+
+    /* Create the file */
+    file = H5Fcreate(FILENAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
+    CHECK(file, FAIL, "H5Fcreate");
+
+    /* Create dataspace */
+    dataspace = H5Screate_simple(SPACE12_RANK, dims, NULL);
+    CHECK(dataspace, FAIL, "H5Screate_simple");
+
+    /* Create dataset creation property list */
+    dcpl = H5Pcreate(H5P_DATASET_CREATE);
+    CHECK(dcpl, FAIL, "H5Pcreate");
+
+    /* Set chunk sizes */
+    status = H5Pset_chunk(dcpl, SPACE12_RANK, chunk_dims);
+    CHECK(status, FAIL, "H5Pset_chunk");
+
+    /* Create dataset */
+    dataset = H5Dcreate(file, DATASETNAME, H5T_NATIVE_UINT, dataspace, dcpl);
+    CHECK(dataset, FAIL, "H5Dcreate");
+
+    /* Close DCPL */
+    status = H5Pclose(dcpl);
+    CHECK(status, FAIL, "H5Pclose");
+
+    /* Write out entire dataset */
+    status = H5Dwrite(dataset, H5T_NATIVE_UINT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data_out);
+    CHECK(status, FAIL, "H5Dclose");
+
+    /* Create memory dataspace (same size as a chunk) */
+    memspace = H5Screate_simple(SPACE12_RANK, chunk_dims, NULL);
+    CHECK(dataspace, FAIL, "H5Screate_simple");
+
+    /* 
+     * Define hyperslab in the file dataspace. 
+     */
+    start[0] = 0;
+    count[0] = SPACE12_CHUNK_DIM0;
+    status = H5Sselect_hyperslab(dataspace, H5S_SELECT_SET, start, NULL, count, NULL);
+    CHECK(status, FAIL, "H5Sselect_hyperslab");
+
+    /* Loop through retrieving data from file, checking it against data written */
+    for(u = 0; u < SPACE12_DIM0; u += SPACE12_CHUNK_DIM0) {
+        /* Set the offset of the file selection */
+        offset[0] = u;
+        status = H5Soffset_simple(dataspace, offset);
+        CHECK(status, FAIL, "H5Soffset_simple");
+        
+        /* Read in buffer of data */
+        status = H5Dread(dataset, H5T_NATIVE_UINT, memspace, dataspace,
+                H5P_DEFAULT, data_in);
+        CHECK(status, FAIL, "H5Dread");
+
+        /* Check data read in */
+        for(v = 0; v < SPACE12_CHUNK_DIM0; v++)
+            if(data_out[u + v] != data_in[v])
+                TestErrPrintf("Error! data_out[%u]=%u, data_in[%u]=%u\n",(unsigned)(u + v), data_out[u + v], v, data_in[v]);
+    } /* end for */
+
+    status = H5Dclose(dataset);
+    CHECK(status, FAIL, "H5Dclose");
+
+    status = H5Sclose(dataspace);
+    CHECK(status, FAIL, "H5Sclose");
+
+    status = H5Sclose(memspace);
+    CHECK(status, FAIL, "H5Sclose");
+
+    status = H5Fclose(file);
+    CHECK(status, FAIL, "H5Fclose");
+}   /* test_select_hyper_chunk_offset2() */
+
+/****************************************************************
+**
 **  test_select_bounds(): Tests selection bounds on dataspaces,
 **      both with and without offsets.
 ** 
@@ -7083,6 +7190,7 @@ test_select(void)
 
     /* Test using selection offset on hyperslab in chunked dataset */
     test_select_hyper_chunk_offset();
+    test_select_hyper_chunk_offset2();
 
     /* Test selection bounds with & without offsets */
     test_select_bounds();
-- 
cgit v0.12