From fcb67f0e8606781eb01a53da01d539da4b50966c Mon Sep 17 00:00:00 2001 From: Quincey Koziol Date: Fri, 11 Mar 2005 09:05:32 -0500 Subject: [svn-r10195] Purpose: New feature Description: Add feature to modify an existing record in a B-tree Platforms tested: FreeBSD 4.11 (sleipnir) Solaris 2.9 (shanti) --- src/H5B2.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/H5B2private.h | 7 ++- src/H5Edefin.h | 1 + src/H5Einit.h | 5 ++ src/H5Epubgen.h | 2 + src/H5Eterm.h | 3 +- src/H5err.txt | 1 + test/btree2.c | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 8 files changed, 376 insertions(+), 4 deletions(-) diff --git a/src/H5B2.c b/src/H5B2.c index 828aebd..b82a75c 100644 --- a/src/H5B2.c +++ b/src/H5B2.c @@ -4051,6 +4051,187 @@ done: FUNC_LEAVE_NOAPI(ret_value) } /* H5B2_neighbor() */ + +/*------------------------------------------------------------------------- + * Function: H5B2_modify + * + * Purpose: Locate the specified information in a B-tree and modify it. + * The UDATA can point to additional data passed + * to the key comparison function. + * + * The 'OP' routine is called with the record found and the + * OP_DATA pointer, to allow caller to modify information about + * the record. + * + * Return: Non-negative on success, negative on failure. + * + * Programmer: Quincey Koziol + * koziol@ncsa.uiuc.edu + * Mar 10 2005 + * + *------------------------------------------------------------------------- + */ +herr_t +H5B2_modify(H5F_t *f, hid_t dxpl_id, const H5B2_class_t *type, haddr_t addr, + void *udata, H5B2_modify_t op, void *op_data) +{ + H5B2_t *bt2=NULL; /* Pointer to the B-tree header */ + H5RC_t *bt2_shared=NULL; /* Pointer to ref-counter for shared B-tree info */ + H5B2_shared_t *shared; /* Pointer to B-tree's shared information */ + hbool_t incr_rc=FALSE; /* Flag to indicate that we've incremented the B-tree's shared info reference count */ + H5B2_node_ptr_t curr_node_ptr; /* Node pointer info for current node */ + unsigned depth; /* Current depth of the tree */ + int cmp; /* Comparison value of records */ + unsigned idx; /* Location of record which matches key */ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI(H5B2_modify, FAIL) + + /* Check arguments. */ + HDassert(f); + HDassert(type); + HDassert(H5F_addr_defined(addr)); + HDassert(op); + + /* Look up the B-tree header */ + if (NULL == (bt2 = H5AC_protect(f, dxpl_id, H5AC_BT2_HDR, addr, type, NULL, H5AC_READ))) + HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to load B-tree header") + + /* Safely grab pointer to reference counted shared B-tree info, so we can release the B-tree header if necessary */ + bt2_shared=bt2->shared; + H5RC_INC(bt2_shared); + incr_rc=TRUE; + + /* Make copy of the root node pointer to start search with */ + curr_node_ptr = bt2->root; + + /* Current depth of the tree */ + depth=bt2->depth; + + /* Release header */ + if (H5AC_unprotect(f, dxpl_id, H5AC_BT2_HDR, addr, bt2, H5AC__NO_FLAGS_SET) < 0) + HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree header info") + bt2=NULL; + + /* Get the pointer to the shared B-tree info */ + shared=H5RC_GET_OBJ(bt2_shared); + HDassert(shared); + + /* Check for empty tree */ + if(curr_node_ptr.node_nrec==0) + HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "B-tree has no records") + + /* Walk down B-tree to find record or leaf node where record is located */ + cmp = -1; + while(depth>0 && cmp != 0) { + H5B2_internal_t *internal; /* Pointer to internal node in B-tree */ + H5B2_node_ptr_t next_node_ptr; /* Node pointer info for next node */ + + /* Lock B-tree current node */ + if (NULL == (internal = H5AC_protect(f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, &(curr_node_ptr.node_nrec), bt2_shared, H5AC_WRITE))) + HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to load B-tree internal node") + + /* Locate node pointer for child */ + cmp = H5B2_locate_record(shared->type, internal->nrec, shared->nat_off, internal->int_native, udata, &idx); + if(cmp > 0) + idx++; + + if(cmp != 0) { + /* Get node pointer for next node to search */ + next_node_ptr=internal->node_ptrs[idx]; + + /* Unlock current node */ + if (H5AC_unprotect(f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, internal, H5AC__NO_FLAGS_SET) < 0) + HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree node") + + /* Set pointer to next node to load */ + curr_node_ptr = next_node_ptr; + } /* end if */ + else { + hbool_t changed; /* Whether the 'modify' callback changed the record */ + + /* Make callback for current record */ + if ( (op)(H5B2_INT_NREC(internal,shared,idx), op_data, &changed) <0) { + /* Make certain that the callback didn't modify the value if it failed */ + HDassert(changed==FALSE); + + /* Unlock current node */ + if (H5AC_unprotect(f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, internal, H5AC__NO_FLAGS_SET) < 0) + HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree node") + + HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "'found' callback failed for B-tree find operation") + } /* end if */ + + /* Mark the node as dirty if it changed */ + internal->cache_info.is_dirty = changed; + + /* Unlock current node */ + if (H5AC_unprotect(f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, internal, H5AC__NO_FLAGS_SET) < 0) + HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree node") + + HGOTO_DONE(SUCCEED); + } /* end else */ + + /* Decrement depth we're at in B-tree */ + depth--; + } /* end while */ + + { + H5B2_leaf_t *leaf; /* Pointer to leaf node in B-tree */ + hbool_t changed; /* Whether the 'modify' callback changed the record */ + + /* Lock B-tree leaf node */ + if (NULL == (leaf = H5AC_protect(f, dxpl_id, H5AC_BT2_LEAF, curr_node_ptr.addr, &(curr_node_ptr.node_nrec), bt2_shared, H5AC_READ))) + HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to load B-tree internal node") + + /* Locate record */ + cmp = H5B2_locate_record(shared->type, leaf->nrec, shared->nat_off, leaf->leaf_native, udata, &idx); + + if(cmp != 0) { + /* Unlock leaf node */ + if (H5AC_unprotect(f, dxpl_id, H5AC_BT2_LEAF, curr_node_ptr.addr, leaf, H5AC__NO_FLAGS_SET) < 0) + HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree node") + + /* Note: don't push error on stack, leave that to next higher level, + * since many times the B-tree is searched in order to determine + * if an object exists in the B-tree or not. -QAK + */ +#ifdef OLD_WAY + HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "key not found in leaf node") +#else /* OLD_WAY */ + HGOTO_DONE(FAIL) +#endif /* OLD_WAY */ + } /* end if */ + else { + /* Make callback for current record */ + if ((op)(H5B2_LEAF_NREC(leaf,shared,idx), op_data, &changed) <0) { + /* Make certain that the callback didn't modify the value if it failed */ + HDassert(changed==FALSE); + + /* Unlock current node */ + if (H5AC_unprotect(f, dxpl_id, H5AC_BT2_LEAF, curr_node_ptr.addr, leaf, H5AC__NO_FLAGS_SET) < 0) + HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree node") + + HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "'found' callback failed for B-tree find operation") + } /* end if */ + } /* end else */ + + /* Mark the node as dirty if it changed */ + leaf->cache_info.is_dirty = changed; + + /* Unlock current node */ + if (H5AC_unprotect(f, dxpl_id, H5AC_BT2_LEAF, curr_node_ptr.addr, leaf, H5AC__NO_FLAGS_SET) < 0) + HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree node") + } + +done: + /* Check if we need to decrement the reference count for the B-tree's shared info */ + if(incr_rc) + H5RC_DEC(bt2_shared); + + FUNC_LEAVE_NOAPI(ret_value) +} /* H5B2_modify() */ + #ifdef H5B2_DEBUG /*------------------------------------------------------------------------- diff --git a/src/H5B2private.h b/src/H5B2private.h index c0abff9..da67213 100644 --- a/src/H5B2private.h +++ b/src/H5B2private.h @@ -59,9 +59,12 @@ typedef enum H5B2_subid_t { /* Define the operator callback function pointer for H5B2_iterate() */ typedef int (*H5B2_operator_t)(const void *record, void *op_data); -/* Define the 'found' callback function pointer for H5B2_find() */ +/* Define the 'found' callback function pointer for H5B2_find() & H5B2_neighbor() */ typedef herr_t (*H5B2_found_t)(const void *record, void *op_data); +/* Define the 'modify' callback function pointer for H5B2_modify() */ +typedef herr_t (*H5B2_modify_t)(void *record, void *op_data, hbool_t *changed); + /* Comparisons for H5B2_neighbor() call */ typedef enum H5B2_compare_t { H5B2_COMPARE_LESS, /* Records with keys less than query value */ @@ -111,6 +114,8 @@ H5_DLL herr_t H5B2_index(H5F_t *f, hid_t dxpl_id, const H5B2_class_t *type, H5_DLL herr_t H5B2_neighbor(H5F_t *f, hid_t dxpl_id, const H5B2_class_t *type, haddr_t addr, H5B2_compare_t comp, void *udata, H5B2_found_t op, void *op_data); +H5_DLL herr_t H5B2_modify(H5F_t *f, hid_t dxpl_id, const H5B2_class_t *type, + haddr_t addr, void *udata, H5B2_modify_t op, void *op_data); H5_DLL herr_t H5B2_remove(H5F_t *f, hid_t dxpl_id, const H5B2_class_t *type, haddr_t addr, void *udata); H5_DLL herr_t H5B2_get_nrec(H5F_t *f, hid_t dxpl_id, const H5B2_class_t *type, diff --git a/src/H5Edefin.h b/src/H5Edefin.h index e606cb9..164ec4c 100644 --- a/src/H5Edefin.h +++ b/src/H5Edefin.h @@ -166,6 +166,7 @@ hid_t H5E_CANTREDISTRIBUTE_g = FAIL; /* Unable to redistribute records */ hid_t H5E_CANTSWAP_g = FAIL; /* Unable to swap records */ hid_t H5E_CANTINSERT_g = FAIL; /* Unable to insert object */ hid_t H5E_CANTLIST_g = FAIL; /* Unable to list node */ +hid_t H5E_CANTMODIFY_g = FAIL; /* Unable to modify record */ /* Argument errors */ hid_t H5E_UNINITIALIZED_g = FAIL; /* Information is uinitialized */ diff --git a/src/H5Einit.h b/src/H5Einit.h index 7471494..5f68721 100644 --- a/src/H5Einit.h +++ b/src/H5Einit.h @@ -620,6 +620,11 @@ if((msg = H5E_create_msg(cls, H5E_MINOR, "Unable to list node"))==NULL) HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") if((H5E_CANTLIST_g = H5I_register(H5I_ERROR_MSG, msg))<0) HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") +assert(H5E_CANTMODIFY_g==(-1)); +if((msg = H5E_create_msg(cls, H5E_MINOR, "Unable to modify record"))==NULL) + HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") +if((H5E_CANTMODIFY_g = H5I_register(H5I_ERROR_MSG, msg))<0) + HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") /* Argument errors */ assert(H5E_UNINITIALIZED_g==(-1)); diff --git a/src/H5Epubgen.h b/src/H5Epubgen.h index ab39d3e..c88ed1f 100644 --- a/src/H5Epubgen.h +++ b/src/H5Epubgen.h @@ -274,6 +274,7 @@ H5_DLLVAR hid_t H5E_CANTCOMPARE_g; /* Can't compare objects */ #define H5E_CANTSWAP (H5OPEN H5E_CANTSWAP_g) #define H5E_CANTINSERT (H5OPEN H5E_CANTINSERT_g) #define H5E_CANTLIST (H5OPEN H5E_CANTLIST_g) +#define H5E_CANTMODIFY (H5OPEN H5E_CANTMODIFY_g) H5_DLLVAR hid_t H5E_NOTFOUND_g; /* Object not found */ H5_DLLVAR hid_t H5E_EXISTS_g; /* Object already exists */ H5_DLLVAR hid_t H5E_CANTENCODE_g; /* Unable to encode value */ @@ -283,6 +284,7 @@ H5_DLLVAR hid_t H5E_CANTREDISTRIBUTE_g; /* Unable to redistribute records */ H5_DLLVAR hid_t H5E_CANTSWAP_g; /* Unable to swap records */ H5_DLLVAR hid_t H5E_CANTINSERT_g; /* Unable to insert object */ H5_DLLVAR hid_t H5E_CANTLIST_g; /* Unable to list node */ +H5_DLLVAR hid_t H5E_CANTMODIFY_g; /* Unable to modify record */ /* Argument errors */ #define H5E_UNINITIALIZED (H5OPEN H5E_UNINITIALIZED_g) diff --git a/src/H5Eterm.h b/src/H5Eterm.h index dad89ab..b55dfcc 100644 --- a/src/H5Eterm.h +++ b/src/H5Eterm.h @@ -167,7 +167,8 @@ H5E_CANTSPLIT_g= H5E_CANTREDISTRIBUTE_g= H5E_CANTSWAP_g= H5E_CANTINSERT_g= -H5E_CANTLIST_g= +H5E_CANTLIST_g= +H5E_CANTMODIFY_g= /* Argument errors */ H5E_UNINITIALIZED_g= diff --git a/src/H5err.txt b/src/H5err.txt index 495bf19..ad15a11 100644 --- a/src/H5err.txt +++ b/src/H5err.txt @@ -166,6 +166,7 @@ MINOR, BTREE, H5E_CANTREDISTRIBUTE, Unable to redistribute records MINOR, BTREE, H5E_CANTSWAP, Unable to swap records MINOR, BTREE, H5E_CANTINSERT, Unable to insert object MINOR, BTREE, H5E_CANTLIST, Unable to list node +MINOR, BTREE, H5E_CANTMODIFY, Unable to modify record # Object header related errors MINOR, OHDR, H5E_LINKCOUNT, Bad object header link count diff --git a/test/btree2.c b/test/btree2.c index 81d563d..615251f 100644 --- a/test/btree2.c +++ b/test/btree2.c @@ -104,7 +104,7 @@ find_cb(const void *_record, void *_op_data) /*------------------------------------------------------------------------- * Function: neighbor_cb * - * Purpose: v2 B-tree find callback + * Purpose: v2 B-tree neighbor callback * * Return: Success: 0 * @@ -130,6 +130,35 @@ neighbor_cb(const void *_record, void *_op_data) /*------------------------------------------------------------------------- + * Function: modify_cb + * + * Purpose: v2 B-tree modify callback + * + * Return: Success: 0 + * + * Failure: 1 + * + * Programmer: Quincey Koziol + * Friday, March 10, 2005 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static int +modify_cb(void *_record, void *_op_data, hbool_t *changed) +{ + hsize_t *record = (hsize_t *)_record; + hsize_t *modify = (hsize_t *)_op_data; + + *record = *modify; + *changed = TRUE; + + return(0); +} /* end modify_cb() */ + + +/*------------------------------------------------------------------------- * Function: test_insert_basic * * Purpose: Basic tests for the B-tree v2 code @@ -5461,7 +5490,151 @@ error: H5Fclose(file); } H5E_END_TRY; return 1; -} /* test_find_neighbor() */ +} /* test_delete() */ + + +/*------------------------------------------------------------------------- + * Function: test_modify + * + * Purpose: Basic tests for the B-tree v2 code. This test exercises + * code to modify an existing record in the B-tree + * + * Return: Success: 0 + * + * Failure: 1 + * + * Programmer: Quincey Koziol + * Friday, March 10, 2005 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static int +test_modify(hid_t fapl) +{ + hid_t file=-1; + char filename[1024]; + H5F_t *f=NULL; + hsize_t record; /* Record to insert into tree */ + haddr_t bt2_addr; /* Address of B-tree created */ + hsize_t modify; /* Modified value */ + hsize_t found; /* Found value */ + unsigned u; /* Local index variable */ + herr_t ret; /* Generic error return value */ + + h5_fixname(FILENAME[0], fapl, filename, sizeof filename); + + /* Create the file to work on */ + if ((file=H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl))<0) TEST_ERROR; + + /* Get a pointer to the internal file object */ + if (NULL==(f=H5I_object(file))) { + H5Eprint_stack(H5E_DEFAULT, stdout); + goto error; + } /* end if */ + + /* + * Create v2 B-tree + */ + if (H5B2_create(f, H5P_DATASET_XFER_DEFAULT, H5B2_TEST, 512, 8, 100, 40, &bt2_addr/*out*/)<0) { + H5_FAILED(); + H5Eprint_stack(H5E_DEFAULT, stdout); + goto error; + } + + /* Insert records */ + for(u=0; u<100; u++) { + record=u*5; + if (H5B2_insert(f, H5P_DATASET_XFER_DEFAULT, H5B2_TEST, bt2_addr, &record)<0) { + H5_FAILED(); + H5Eprint_stack(H5E_DEFAULT, stdout); + goto error; + } /* end if */ + } /* end for */ + + /* + * Test modifying records + */ + TESTING("B-tree modify: attempt to modify non-existant record"); + + /* Attempt to modify a non-existant record */ + record = 3; + modify = 4; + H5E_BEGIN_TRY { + ret = H5B2_modify(f, H5P_DATASET_XFER_DEFAULT, H5B2_TEST, bt2_addr, &record, modify_cb, &modify); + } H5E_END_TRY; + /* Should fail */ + if(ret != FAIL) TEST_ERROR; + + PASSED(); + + TESTING("B-tree modify: modify record in leaf node"); + + /* Attempt to modify a record in a leaf node */ + record = 130; + modify = 131; + if (H5B2_modify(f, H5P_DATASET_XFER_DEFAULT, H5B2_TEST, bt2_addr, &record, modify_cb, &modify) < 0) { + H5_FAILED(); + H5Eprint_stack(H5E_DEFAULT, stdout); + goto error; + } /* end if */ + + /* Attempt to find modified record */ + record = 131; + found = 131; + if(H5B2_find(f, H5P_DATASET_XFER_DEFAULT, H5B2_TEST, bt2_addr, &record, find_cb, &found)<0) TEST_ERROR; + if(found != 131) TEST_ERROR; + + /* Attempt to find original record */ + record = 130; + found = 130; + H5E_BEGIN_TRY { + ret = H5B2_modify(f, H5P_DATASET_XFER_DEFAULT, H5B2_TEST, bt2_addr, &record, modify_cb, &modify); + } H5E_END_TRY; + /* Should fail */ + if(ret != FAIL) TEST_ERROR; + + PASSED(); + + TESTING("B-tree modify: modify record in internal node"); + + /* Attempt to modify a record in an internal node */ + record = 235; + modify = 237; + if (H5B2_modify(f, H5P_DATASET_XFER_DEFAULT, H5B2_TEST, bt2_addr, &record, modify_cb, &modify) < 0) { + H5_FAILED(); + H5Eprint_stack(H5E_DEFAULT, stdout); + goto error; + } /* end if */ + + /* Attempt to find modified record */ + record = 237; + found = 237; + if(H5B2_find(f, H5P_DATASET_XFER_DEFAULT, H5B2_TEST, bt2_addr, &record, find_cb, &found)<0) TEST_ERROR; + if(found != 237) TEST_ERROR; + + /* Attempt to find original record */ + record = 235; + found = 235; + H5E_BEGIN_TRY { + ret = H5B2_modify(f, H5P_DATASET_XFER_DEFAULT, H5B2_TEST, bt2_addr, &record, modify_cb, &modify); + } H5E_END_TRY; + /* Should fail */ + if(ret != FAIL) TEST_ERROR; + + PASSED(); + + if (H5Fclose(file)<0) TEST_ERROR; + + return 0; + +error: + H5E_BEGIN_TRY { + H5Fclose(file); + } H5E_END_TRY; + return 1; +} /* test_modify() */ /*------------------------------------------------------------------------- @@ -5537,6 +5710,9 @@ main(void) /* Test deleting B-trees */ nerrors += test_delete(fapl); + /* Test modifying B-tree records */ + nerrors += test_modify(fapl); + if (nerrors) goto error; puts("All v2 B-tree tests passed."); h5_cleanup(FILENAME, fapl); -- cgit v0.12