From d5d34def1ca98fdacc9b31d992a865f07104d379 Mon Sep 17 00:00:00 2001 From: cvs2svn Date: Fri, 1 Oct 2004 18:00:55 -0500 Subject: [svn-r9355] This commit was manufactured by cvs2svn to create branch 'hdf5_1_6'. --- src/H5C.c | 4183 +++++++++++++++++++++++++++++++++++++++++ src/H5Cprivate.h | 421 +++++ src/H5Defl.c | 371 ++++ src/H5Dselect.c | 691 +++++++ test/cache.c | 4160 ++++++++++++++++++++++++++++++++++++++++ tools/h5jam/Dependencies | 83 + tools/h5jam/Makefile.in | 96 + tools/h5jam/getub.c | 170 ++ tools/h5jam/h5jam.c | 538 ++++++ tools/h5jam/h5jamgentest.c | 639 +++++++ tools/h5jam/h5unjam.c | 317 ++++ tools/h5jam/tellub.c | 189 ++ tools/h5jam/testh5jam.sh.in | 527 ++++++ tools/testfiles/twithub.h5 | Bin 0 -> 10820 bytes tools/testfiles/twithub513.h5 | Bin 0 -> 12560 bytes tools/testfiles/u10.txt | 1 + tools/testfiles/u511.txt | 1 + tools/testfiles/u512.txt | 1 + tools/testfiles/u513.txt | 1 + 19 files changed, 12389 insertions(+) create mode 100644 src/H5C.c create mode 100644 src/H5Cprivate.h create mode 100644 src/H5Defl.c create mode 100644 src/H5Dselect.c create mode 100644 test/cache.c create mode 100644 tools/h5jam/Dependencies create mode 100644 tools/h5jam/Makefile.in create mode 100644 tools/h5jam/getub.c create mode 100644 tools/h5jam/h5jam.c create mode 100644 tools/h5jam/h5jamgentest.c create mode 100644 tools/h5jam/h5unjam.c create mode 100644 tools/h5jam/tellub.c create mode 100644 tools/h5jam/testh5jam.sh.in create mode 100644 tools/testfiles/twithub.h5 create mode 100644 tools/testfiles/twithub513.h5 create mode 100755 tools/testfiles/u10.txt create mode 100755 tools/testfiles/u511.txt create mode 100755 tools/testfiles/u512.txt create mode 100755 tools/testfiles/u513.txt diff --git a/src/H5C.c b/src/H5C.c new file mode 100644 index 0000000..8c9c74b --- /dev/null +++ b/src/H5C.c @@ -0,0 +1,4183 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by the Board of Trustees of the University of Illinois. * + * 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 files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*------------------------------------------------------------------------- + * + * Created: H5C.c + * June 1 2004 + * John Mainzer + * + * Purpose: Functions in this file implement a generic cache for + * things which exist on disk, and which may be + * unambiguously referenced by their disk addresses. + * + * The code in this module was initially written in + * support of a complete re-write of the metadata cache + * in H5AC.c However, other uses for the cache code + * suggested themselves, and thus this file was created + * in an attempt to support re-use. + * + * For a detailed overview of the cache, please see the + * header comment for H5C_t in this file. + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +/************************************************************************** + * + * To Do: + * + * Code Changes: + * + * - Remove extra functionality in H5C_flush_single_entry()? + * + * - Change protect/unprotect to lock/unlock. + * + * - Change the way the dirty flag is set. Probably pass it in + * as a parameter in unprotect & insert. + * + * - Size should also be passed in as a parameter in insert and + * unprotect -- or some other way should be found to advise the + * cache of changes in entry size. + * + * - Flush entries in increasing address order in + * H5C_make_space_in_cache(). + * + * - Also in H5C_make_space_in_cache(), use high and low water marks + * to reduce the number of I/O calls. + * + * - When flushing, attempt to combine contiguous entries to reduce + * I/O overhead. Can't do this just yet as some entries are not + * contiguous. Do this in parallel only or in serial as well? + * + * - Create MPI type for dirty objects when flushing in parallel. + * + * - Fix TBBT routines so deletions don't move nodes in memory and + * point directly to the TBBT node from the LRU list, eliminating + * binary tree lookups when evicting objects from the cache. + * + * Tests: + * + * - Trim execution time. + * + * - Add random tests. + * + **************************************************************************/ + +#define H5F_PACKAGE /*suppress error about including H5Fpkg */ + +/* Pablo information */ +/* (Put before include files to avoid problems with inline functions) */ +#define PABLO_MASK H5C_mask + +#include "H5private.h" /* Generic Functions */ +#include "H5Cprivate.h" /* Cache */ +#include "H5Dprivate.h" /* Dataset functions */ +#include "H5Eprivate.h" /* Error handling */ +#include "H5Fpkg.h" /* Files */ +#include "H5FDprivate.h" /* File drivers */ +#include "H5FLprivate.h" /* Free Lists */ +#include "H5Iprivate.h" /* IDs */ +#include "H5MMprivate.h" /* Memory management */ +#include "H5Pprivate.h" /* Property lists */ +#include "H5TBprivate.h" /* Threaded, Balanced, Binary Trees */ + + +/**************************************************************************** + * + * We maintain doubly linked lists of instances of H5C_cache_entry_t for a + * variety of reasons -- protected list, LRU list, and the clean and dirty + * LRU lists at present. The following macros support linking and unlinking + * of instances of H5C_cache_entry_t by both their regular and auxilary next + * and previous pointers. + * + * The size and length fields are also maintained. + * + * Note that the relevant pair of prev and next pointers are presumed to be + * NULL on entry in the insertion macros. + * + * Finally, observe that the sanity checking macros evaluate to the empty + * string when H5C_DO_SANITY_CHECKS is FALSE. They also contain calls + * to the HGOTO_ERROR macro, which may not be appropriate in all cases. + * If so, we will need versions of the insertion and deletion macros which + * do not reference the sanity checking macros. + * JRM - 5/5/04 + * + ****************************************************************************/ + +#if H5C_DO_SANITY_CHECKS + +#define H5C__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \ +if ( ( (head_ptr) == NULL ) || \ + ( (tail_ptr) == NULL ) || \ + ( (entry_ptr) == NULL ) || \ + ( (len) <= 0 ) || \ + ( (Size) < (entry_ptr)->size ) || \ + ( ( (Size) == (entry_ptr)->size ) && ( (len) != 1 ) ) || \ + ( ( (entry_ptr)->prev == NULL ) && ( (head_ptr) != (entry_ptr) ) ) || \ + ( ( (entry_ptr)->next == NULL ) && ( (tail_ptr) != (entry_ptr) ) ) || \ + ( ( (len) == 1 ) && \ + ( ! ( ( (head_ptr) == (entry_ptr) ) && \ + ( (tail_ptr) == (entry_ptr) ) && \ + ( (entry_ptr)->next == NULL ) && \ + ( (entry_ptr)->prev == NULL ) && \ + ( (Size) == (entry_ptr)->size ) \ + ) \ + ) \ + ) \ + ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "DLL pre remove SC failed") \ +} + +#define H5C__DLL_SC(head_ptr, tail_ptr, len, Size, fv) \ +if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ + ( (head_ptr) != (tail_ptr) ) \ + ) || \ + ( (len) < 0 ) || \ + ( (Size) < 0 ) || \ + ( ( (len) == 1 ) && \ + ( ( (head_ptr) != (tail_ptr) ) || ( (cache_ptr)->size <= 0 ) || \ + ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) ) \ + ) \ + ) || \ + ( ( (len) >= 1 ) && \ + ( ( (head_ptr) == NULL ) || ( (head_ptr)->prev != NULL ) || \ + ( (tail_ptr) == NULL ) || ( (tail_ptr)->next != NULL ) \ + ) \ + ) \ + ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "DLL sanity check failed") \ +} + +#define H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \ +if ( ( (entry_ptr) == NULL ) || \ + ( (entry_ptr)->next != NULL ) || \ + ( (entry_ptr)->prev != NULL ) || \ + ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ + ( (head_ptr) != (tail_ptr) ) \ + ) || \ + ( (len) < 0 ) || \ + ( ( (len) == 1 ) && \ + ( ( (head_ptr) != (tail_ptr) ) || ( (Size) <= 0 ) || \ + ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) ) \ + ) \ + ) || \ + ( ( (len) >= 1 ) && \ + ( ( (head_ptr) == NULL ) || ( (head_ptr)->prev != NULL ) || \ + ( (tail_ptr) == NULL ) || ( (tail_ptr)->next != NULL ) \ + ) \ + ) \ + ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "DLL pre insert SC failed") \ +} + +#else /* H5C_DO_SANITY_CHECKS */ + +#define H5C__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv) +#define H5C__DLL_SC(head_ptr, tail_ptr, len, Size, fv) +#define H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv) + +#endif /* H5C_DO_SANITY_CHECKS */ + + +#define H5C__DLL_APPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val) \ + H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \ + fail_val) \ + if ( (head_ptr) == NULL ) \ + { \ + (head_ptr) = (entry_ptr); \ + (tail_ptr) = (entry_ptr); \ + } \ + else \ + { \ + (tail_ptr)->next = (entry_ptr); \ + (entry_ptr)->prev = (tail_ptr); \ + (tail_ptr) = (entry_ptr); \ + } \ + (len)++; \ + (Size) += (entry_ptr)->size; + +#define H5C__DLL_PREPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val) \ + H5C__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \ + fail_val) \ + if ( (head_ptr) == NULL ) \ + { \ + (head_ptr) = (entry_ptr); \ + (tail_ptr) = (entry_ptr); \ + } \ + else \ + { \ + (head_ptr)->prev = (entry_ptr); \ + (entry_ptr)->next = (head_ptr); \ + (head_ptr) = (entry_ptr); \ + } \ + (len)++; \ + (Size) += entry_ptr->size; + +#define H5C__DLL_REMOVE(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val) \ + H5C__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \ + fail_val) \ + { \ + if ( (head_ptr) == (entry_ptr) ) \ + { \ + (head_ptr) = (entry_ptr)->next; \ + if ( (head_ptr) != NULL ) \ + { \ + (head_ptr)->prev = NULL; \ + } \ + } \ + else \ + { \ + (entry_ptr)->prev->next = (entry_ptr)->next; \ + } \ + if ( (tail_ptr) == (entry_ptr) ) \ + { \ + (tail_ptr) = (entry_ptr)->prev; \ + if ( (tail_ptr) != NULL ) \ + { \ + (tail_ptr)->next = NULL; \ + } \ + } \ + else \ + { \ + (entry_ptr)->next->prev = (entry_ptr)->prev; \ + } \ + entry_ptr->next = NULL; \ + entry_ptr->prev = NULL; \ + (len)--; \ + (Size) -= entry_ptr->size; \ + } + + +#if H5C_DO_SANITY_CHECKS + +#define H5C__AUX_DLL_PRE_REMOVE_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv) \ +if ( ( (hd_ptr) == NULL ) || \ + ( (tail_ptr) == NULL ) || \ + ( (entry_ptr) == NULL ) || \ + ( (len) <= 0 ) || \ + ( (Size) < (entry_ptr)->size ) || \ + ( ( (Size) == (entry_ptr)->size ) && ( ! ( (len) == 1 ) ) ) || \ + ( ( (entry_ptr)->aux_prev == NULL ) && ( (hd_ptr) != (entry_ptr) ) ) || \ + ( ( (entry_ptr)->aux_next == NULL ) && ( (tail_ptr) != (entry_ptr) ) ) || \ + ( ( (len) == 1 ) && \ + ( ! ( ( (hd_ptr) == (entry_ptr) ) && ( (tail_ptr) == (entry_ptr) ) && \ + ( (entry_ptr)->aux_next == NULL ) && \ + ( (entry_ptr)->aux_prev == NULL ) && \ + ( (Size) == (entry_ptr)->size ) \ + ) \ + ) \ + ) \ + ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "aux DLL pre remove SC failed") \ +} + +#define H5C__AUX_DLL_SC(head_ptr, tail_ptr, len, Size, fv) \ +if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ + ( (head_ptr) != (tail_ptr) ) \ + ) || \ + ( (len) < 0 ) || \ + ( (Size) < 0 ) || \ + ( ( (len) == 1 ) && \ + ( ( (head_ptr) != (tail_ptr) ) || ( (Size) <= 0 ) || \ + ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) ) \ + ) \ + ) || \ + ( ( (len) >= 1 ) && \ + ( ( (head_ptr) == NULL ) || ( (head_ptr)->aux_prev != NULL ) || \ + ( (tail_ptr) == NULL ) || ( (tail_ptr)->aux_next != NULL ) \ + ) \ + ) \ + ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "AUX DLL sanity check failed") \ +} + +#define H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv) \ +if ( ( (entry_ptr) == NULL ) || \ + ( (entry_ptr)->aux_next != NULL ) || \ + ( (entry_ptr)->aux_prev != NULL ) || \ + ( ( ( (hd_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ + ( (hd_ptr) != (tail_ptr) ) \ + ) || \ + ( (len) < 0 ) || \ + ( ( (len) == 1 ) && \ + ( ( (hd_ptr) != (tail_ptr) ) || ( (Size) <= 0 ) || \ + ( (hd_ptr) == NULL ) || ( (hd_ptr)->size != (Size) ) \ + ) \ + ) || \ + ( ( (len) >= 1 ) && \ + ( ( (hd_ptr) == NULL ) || ( (hd_ptr)->aux_prev != NULL ) || \ + ( (tail_ptr) == NULL ) || ( (tail_ptr)->aux_next != NULL ) \ + ) \ + ) \ + ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, (fv), "AUX DLL pre insert SC failed") \ +} + +#else /* H5C_DO_SANITY_CHECKS */ + +#define H5C__AUX_DLL_PRE_REMOVE_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv) +#define H5C__AUX_DLL_SC(head_ptr, tail_ptr, len, Size, fv) +#define H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv) + +#endif /* H5C_DO_SANITY_CHECKS */ + + +#define H5C__AUX_DLL_APPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val)\ + H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \ + fail_val) \ + if ( (head_ptr) == NULL ) \ + { \ + (head_ptr) = (entry_ptr); \ + (tail_ptr) = (entry_ptr); \ + } \ + else \ + { \ + (tail_ptr)->aux_next = (entry_ptr); \ + (entry_ptr)->aux_prev = (tail_ptr); \ + (tail_ptr) = (entry_ptr); \ + } \ + (len)++; \ + (Size) += entry_ptr->size; + +#define H5C__AUX_DLL_PREPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \ + H5C__AUX_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \ + fv) \ + if ( (head_ptr) == NULL ) \ + { \ + (head_ptr) = (entry_ptr); \ + (tail_ptr) = (entry_ptr); \ + } \ + else \ + { \ + (head_ptr)->aux_prev = (entry_ptr); \ + (entry_ptr)->aux_next = (head_ptr); \ + (head_ptr) = (entry_ptr); \ + } \ + (len)++; \ + (Size) += entry_ptr->size; + +#define H5C__AUX_DLL_REMOVE(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \ + H5C__AUX_DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \ + fv) \ + { \ + if ( (head_ptr) == (entry_ptr) ) \ + { \ + (head_ptr) = (entry_ptr)->aux_next; \ + if ( (head_ptr) != NULL ) \ + { \ + (head_ptr)->aux_prev = NULL; \ + } \ + } \ + else \ + { \ + (entry_ptr)->aux_prev->aux_next = (entry_ptr)->aux_next; \ + } \ + if ( (tail_ptr) == (entry_ptr) ) \ + { \ + (tail_ptr) = (entry_ptr)->aux_prev; \ + if ( (tail_ptr) != NULL ) \ + { \ + (tail_ptr)->aux_next = NULL; \ + } \ + } \ + else \ + { \ + (entry_ptr)->aux_next->aux_prev = (entry_ptr)->aux_prev; \ + } \ + entry_ptr->aux_next = NULL; \ + entry_ptr->aux_prev = NULL; \ + (len)--; \ + (Size) -= entry_ptr->size; \ + } + + +/*********************************************************************** + * + * Stats collection macros + * + * The following macros must handle stats collection when this collection + * is enabled, and evaluate to the empty string when it is not. + * + ***********************************************************************/ + +#if H5C_COLLECT_CACHE_STATS + +#define H5C__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr) \ + (((cache_ptr)->insertions)[(entry_ptr)->type->id])++; \ + if ( (cache_ptr)->index_len > (cache_ptr)->max_index_len ) \ + (cache_ptr)->max_index_len = (cache_ptr)->index_len; \ + if ( (cache_ptr)->index_size > (cache_ptr)->max_index_size ) \ + (cache_ptr)->max_index_size = (cache_ptr)->index_size; \ + if ( (cache_ptr)->tree_len > (cache_ptr)->max_tree_len ) \ + (cache_ptr)->max_tree_len = (cache_ptr)->tree_len; \ + if ( (cache_ptr)->tree_size > (cache_ptr)->max_tree_size ) \ + (cache_ptr)->max_tree_size = (cache_ptr)->tree_size; \ + if ( (entry_ptr)->size > \ + ((cache_ptr)->max_size)[(entry_ptr)->type->id] ) { \ + ((cache_ptr)->max_size)[(entry_ptr)->type->id] \ + = (entry_ptr)->size; \ + } + +#define H5C__UPDATE_STATS_FOR_UNPROTECT(cache_ptr) \ + if ( (cache_ptr)->tree_len > (cache_ptr)->max_tree_len ) \ + (cache_ptr)->max_tree_len = (cache_ptr)->tree_len; \ + if ( (cache_ptr)->tree_size > (cache_ptr)->max_tree_size ) \ + (cache_ptr)->max_tree_size = (cache_ptr)->tree_size; + +#define H5C__UPDATE_STATS_FOR_RENAME(cache_ptr, entry_ptr) \ + (((cache_ptr)->renames)[(entry_ptr)->type->id])++; + +#define H5C__UPDATE_STATS_FOR_HT_INSERTION(cache_ptr) \ + (cache_ptr)->total_ht_insertions++; + +#define H5C__UPDATE_STATS_FOR_HT_DELETION(cache_ptr) \ + (cache_ptr)->total_ht_deletions++; + +#define H5C__UPDATE_STATS_FOR_HT_SEARCH(cache_ptr, success, depth) \ + if ( success ) { \ + (cache_ptr)->successful_ht_searches++; \ + (cache_ptr)->total_successful_ht_search_depth += depth; \ + } else { \ + (cache_ptr)->failed_ht_searches++; \ + (cache_ptr)->total_failed_ht_search_depth += depth; \ + } + +#if H5C_COLLECT_CACHE_ENTRY_STATS + +#define H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) \ + (entry_ptr)->accesses = 0; \ + (entry_ptr)->clears = 0; \ + (entry_ptr)->flushes = 0; + +#define H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) \ + (((cache_ptr)->clears)[(entry_ptr)->type->id])++; \ + ((entry_ptr)->clears)++; + +#define H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) \ + (((cache_ptr)->flushes)[(entry_ptr)->type->id])++; \ + ((entry_ptr)->flushes)++; + +#define H5C__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr) \ + (((cache_ptr)->evictions)[(entry_ptr)->type->id])++; \ + if ( (entry_ptr)->accesses > \ + ((cache_ptr)->max_accesses)[(entry_ptr)->type->id] ) { \ + ((cache_ptr)->max_accesses)[(entry_ptr)->type->id] \ + = (entry_ptr)->accesses; \ + } \ + if ( (entry_ptr)->accesses < \ + ((cache_ptr)->min_accesses)[(entry_ptr)->type->id] ) { \ + ((cache_ptr)->min_accesses)[(entry_ptr)->type->id] \ + = (entry_ptr)->accesses; \ + } \ + if ( (entry_ptr)->clears > \ + ((cache_ptr)->max_clears)[(entry_ptr)->type->id] ) { \ + ((cache_ptr)->max_clears)[(entry_ptr)->type->id] \ + = (entry_ptr)->clears; \ + } \ + if ( (entry_ptr)->flushes > \ + ((cache_ptr)->max_flushes)[(entry_ptr)->type->id] ) { \ + ((cache_ptr)->max_flushes)[(entry_ptr)->type->id] \ + = (entry_ptr)->flushes; \ + } \ + if ( (entry_ptr)->size > \ + ((cache_ptr)->max_size)[(entry_ptr)->type->id] ) { \ + ((cache_ptr)->max_size)[(entry_ptr)->type->id] \ + = (entry_ptr)->size; \ + } \ + +#define H5C__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit) \ + if ( hit ) \ + ((cache_ptr)->hits)[(entry_ptr)->type->id]++; \ + else \ + ((cache_ptr)->misses)[(entry_ptr)->type->id]++; \ + if ( (cache_ptr)->index_len > (cache_ptr)->max_index_len ) \ + (cache_ptr)->max_index_len = (cache_ptr)->index_len; \ + if ( (cache_ptr)->index_size > (cache_ptr)->max_index_size ) \ + (cache_ptr)->max_index_size = (cache_ptr)->index_size; \ + if ( (cache_ptr)->pl_len > (cache_ptr)->max_pl_len ) \ + (cache_ptr)->max_pl_len = (cache_ptr)->pl_len; \ + if ( (cache_ptr)->pl_size > (cache_ptr)->max_pl_size ) \ + (cache_ptr)->max_pl_size = (cache_ptr)->pl_size; \ + if ( (entry_ptr)->size > \ + ((cache_ptr)->max_size)[(entry_ptr)->type->id] ) { \ + ((cache_ptr)->max_size)[(entry_ptr)->type->id] \ + = (entry_ptr)->size; \ + } \ + ((entry_ptr)->accesses)++; + +#else /* H5C_COLLECT_CACHE_ENTRY_STATS */ + +#define H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) + +#define H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) \ + (((cache_ptr)->clears)[(entry_ptr)->type->id])++; + +#define H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) \ + (((cache_ptr)->flushes)[(entry_ptr)->type->id])++; + +#define H5C__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr) \ + (((cache_ptr)->evictions)[(entry_ptr)->type->id])++; + +#define H5C__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit) \ + if ( hit ) \ + ((cache_ptr)->hits)[(entry_ptr)->type->id]++; \ + else \ + ((cache_ptr)->misses)[(entry_ptr)->type->id]++; \ + if ( (cache_ptr)->index_len > (cache_ptr)->max_index_len ) \ + (cache_ptr)->max_index_len = (cache_ptr)->index_len; \ + if ( (cache_ptr)->index_size > (cache_ptr)->max_index_size ) \ + (cache_ptr)->max_index_size = (cache_ptr)->index_size; \ + if ( (cache_ptr)->pl_len > (cache_ptr)->max_pl_len ) \ + (cache_ptr)->max_pl_len = (cache_ptr)->pl_len; \ + if ( (cache_ptr)->pl_size > (cache_ptr)->max_pl_size ) \ + (cache_ptr)->max_pl_size = (cache_ptr)->pl_size; + +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ + +#else /* H5C_COLLECT_CACHE_STATS */ + +#define H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) +#define H5C__UPDATE_STATS_FOR_UNPROTECT(cache_ptr) +#define H5C__UPDATE_STATS_FOR_RENAME(cache_ptr, entry_ptr) +#define H5C__UPDATE_STATS_FOR_HT_INSERTION(cache_ptr) +#define H5C__UPDATE_STATS_FOR_HT_DELETION(cache_ptr) +#define H5C__UPDATE_STATS_FOR_HT_SEARCH(cache_ptr, success, depth) +#define H5C__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr) +#define H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) +#define H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) +#define H5C__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr) +#define H5C__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit) + +#endif /* H5C_COLLECT_CACHE_STATS */ + + +/*********************************************************************** + * + * Hash table access and manipulation macros: + * + * The following macros handle searches, insertions, and deletion in + * the hash table. + * + * When modifying these macros, remember to modify the similar macros + * in tst/cache.c + * + ***********************************************************************/ + +#define H5C__HASH_TABLE_LEN (32 * 1024) /* must be a power of 2 */ + +#define H5C__HASH_MASK ((size_t)(H5C__HASH_TABLE_LEN - 1) << 3) + +#define H5C__HASH_FCN(x) (int)(((x) & H5C__HASH_MASK) >> 3) + +#if H5C_DO_SANITY_CHECKS + +#define H5C__PRE_HT_INSERT_SC(cache_ptr, entry_ptr, fail_val) \ +if ( ( (cache_ptr) == NULL ) || \ + ( (cache_ptr)->magic != H5C__H5C_T_MAGIC ) || \ + ( (entry_ptr) == NULL ) || \ + ( ! H5F_addr_defined((entry_ptr)->addr) ) || \ + ( (entry_ptr)->ht_next != NULL ) || \ + ( (entry_ptr)->ht_prev != NULL ) || \ + ( (entry_ptr)->size <= 0 ) || \ + ( (k = H5C__HASH_FCN((entry_ptr)->addr)) < 0 ) || \ + ( k >= H5C__HASH_TABLE_LEN ) ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, \ + "Pre HT insert SC failed") \ +} + +#define H5C__PRE_HT_REMOVE_SC(cache_ptr, entry_ptr) \ +if ( ( (cache_ptr) == NULL ) || \ + ( (cache_ptr)->magic != H5C__H5C_T_MAGIC ) || \ + ( (cache_ptr)->index_len < 1 ) || \ + ( (entry_ptr) == NULL ) || \ + ( (cache_ptr)->index_size < (entry_ptr)->size ) || \ + ( ! H5F_addr_defined((entry_ptr)->addr) ) || \ + ( (entry_ptr)->size <= 0 ) || \ + ( H5C__HASH_FCN((entry_ptr)->addr) < 0 ) || \ + ( H5C__HASH_FCN((entry_ptr)->addr) >= H5C__HASH_TABLE_LEN ) || \ + ( ((cache_ptr)->index)[(H5C__HASH_FCN((entry_ptr)->addr))] \ + == NULL ) || \ + ( ( ((cache_ptr)->index)[(H5C__HASH_FCN((entry_ptr)->addr))] \ + != (entry_ptr) ) && \ + ( (entry_ptr)->ht_prev == NULL ) ) || \ + ( ( ((cache_ptr)->index)[(H5C__HASH_FCN((entry_ptr)->addr))] == \ + (entry_ptr) ) && \ + ( (entry_ptr)->ht_prev != NULL ) ) ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Pre HT remove SC failed") \ +} + +#define H5C__PRE_HT_SEARCH_SC(cache_ptr, Addr, fail_val) \ +if ( ( (cache_ptr) == NULL ) || \ + ( (cache_ptr)->magic != H5C__H5C_T_MAGIC ) || \ + ( ! H5F_addr_defined(Addr) ) || \ + ( H5C__HASH_FCN(Addr) < 0 ) || \ + ( H5C__HASH_FCN(Addr) >= H5C__HASH_TABLE_LEN ) ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, "Pre HT search SC failed") \ +} + +#define H5C__POST_SUC_HT_SEARCH_SC(cache_ptr, entry_ptr, Addr, k, fail_val) \ +if ( ( (cache_ptr) == NULL ) || \ + ( (cache_ptr)->magic != H5C__H5C_T_MAGIC ) || \ + ( (cache_ptr)->index_len < 1 ) || \ + ( (entry_ptr) == NULL ) || \ + ( (cache_ptr)->index_size < (entry_ptr)->size ) || \ + ( H5F_addr_ne((entry_ptr)->addr, (Addr)) ) || \ + ( (entry_ptr)->size <= 0 ) || \ + ( ((cache_ptr)->index)[k] == NULL ) || \ + ( ( ((cache_ptr)->index)[k] != (entry_ptr) ) && \ + ( (entry_ptr)->ht_prev == NULL ) ) || \ + ( ( ((cache_ptr)->index)[k] == (entry_ptr) ) && \ + ( (entry_ptr)->ht_prev != NULL ) ) || \ + ( ( (entry_ptr)->ht_prev != NULL ) && \ + ( (entry_ptr)->ht_prev->ht_next != (entry_ptr) ) ) || \ + ( ( (entry_ptr)->ht_next != NULL ) && \ + ( (entry_ptr)->ht_next->ht_prev != (entry_ptr) ) ) ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, \ + "Post successful HT search SC failed") \ +} + +#define H5C__POST_HT_SHIFT_TO_FRONT(cache_ptr, entry_ptr, k, fail_val) \ +if ( ( (cache_ptr) == NULL ) || \ + ( ((cache_ptr)->index)[k] != (entry_ptr) ) || \ + ( (entry_ptr)->ht_prev != NULL ) ) { \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, \ + "Post HT shift to front SC failed") \ +} + + +#else /* H5C_DO_SANITY_CHECKS */ + +#define H5C__PRE_HT_INSERT_SC(cache_ptr, entry_ptr, fail_val) +#define H5C__PRE_HT_REMOVE_SC(cache_ptr, entry_ptr) +#define H5C__PRE_HT_SEARCH_SC(cache_ptr, Addr, fail_val) +#define H5C__POST_SUC_HT_SEARCH_SC(cache_ptr, entry_ptr, Addr, k, fail_val) +#define H5C__POST_HT_SHIFT_TO_FRONT(cache_ptr, entry_ptr, k, fail_val) + +#endif /* H5C_DO_SANITY_CHECKS */ + + +#define H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, fail_val) \ +{ \ + int k; \ + H5C__PRE_HT_INSERT_SC(cache_ptr, entry_ptr, fail_val) \ + k = H5C__HASH_FCN((entry_ptr)->addr); \ + if ( ((cache_ptr)->index)[k] == NULL ) \ + { \ + ((cache_ptr)->index)[k] = (entry_ptr); \ + } \ + else \ + { \ + (entry_ptr)->ht_next = ((cache_ptr)->index)[k]; \ + (entry_ptr)->ht_next->ht_prev = (entry_ptr); \ + ((cache_ptr)->index)[k] = (entry_ptr); \ + } \ + (cache_ptr)->index_len++; \ + (cache_ptr)->index_size += (entry_ptr)->size; \ + H5C__UPDATE_STATS_FOR_HT_INSERTION(cache_ptr) \ +} + +#define H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr) \ +{ \ + int k; \ + H5C__PRE_HT_REMOVE_SC(cache_ptr, entry_ptr) \ + k = H5C__HASH_FCN((entry_ptr)->addr); \ + if ( (entry_ptr)->ht_next ) \ + { \ + (entry_ptr)->ht_next->ht_prev = (entry_ptr)->ht_prev; \ + } \ + if ( (entry_ptr)->ht_prev ) \ + { \ + (entry_ptr)->ht_prev->ht_next = (entry_ptr)->ht_next; \ + } \ + if ( ((cache_ptr)->index)[k] == (entry_ptr) ) \ + { \ + ((cache_ptr)->index)[k] = (entry_ptr)->ht_next; \ + } \ + (entry_ptr)->ht_next = NULL; \ + (entry_ptr)->ht_prev = NULL; \ + (cache_ptr)->index_len--; \ + (cache_ptr)->index_size -= (entry_ptr)->size; \ + H5C__UPDATE_STATS_FOR_HT_DELETION(cache_ptr) \ +} + +#define H5C__SEARCH_INDEX(cache_ptr, Addr, entry_ptr, fail_val) \ +{ \ + int k; \ + int depth = 0; \ + H5C__PRE_HT_SEARCH_SC(cache_ptr, Addr, fail_val) \ + k = H5C__HASH_FCN(Addr); \ + entry_ptr = ((cache_ptr)->index)[k]; \ + while ( ( entry_ptr ) && ( H5F_addr_ne(Addr, (entry_ptr)->addr) ) ) \ + { \ + (entry_ptr) = (entry_ptr)->ht_next; \ + (depth)++; \ + } \ + if ( entry_ptr ) \ + { \ + H5C__POST_SUC_HT_SEARCH_SC(cache_ptr, entry_ptr, Addr, k, fail_val) \ + if ( entry_ptr != ((cache_ptr)->index)[k] ) \ + { \ + if ( (entry_ptr)->ht_next ) \ + { \ + (entry_ptr)->ht_next->ht_prev = (entry_ptr)->ht_prev; \ + } \ + HDassert( (entry_ptr)->ht_prev != NULL ); \ + (entry_ptr)->ht_prev->ht_next = (entry_ptr)->ht_next; \ + ((cache_ptr)->index)[k]->ht_prev = (entry_ptr); \ + (entry_ptr)->ht_next = ((cache_ptr)->index)[k]; \ + (entry_ptr)->ht_prev = NULL; \ + ((cache_ptr)->index)[k] = (entry_ptr); \ + H5C__POST_HT_SHIFT_TO_FRONT(cache_ptr, entry_ptr, k, fail_val) \ + } \ + } \ + H5C__UPDATE_STATS_FOR_HT_SEARCH(cache_ptr, (entry_ptr != NULL), depth) \ +} + + +/************************************************************************** + * + * Tree insertion and deletion macros: + * + * These used to be functions, but I converted them to macros to avoid some + * function call overhead. + * + **************************************************************************/ + +/*------------------------------------------------------------------------- + * + * Macro: H5C__INSERT_ENTRY_IN_TREE + * + * Purpose: Insert the specified instance of H5C_cache_entry_t into + * the tree in the specified instance of H5C_t. Update + * the associated length and size fields. + * + * Return: N/A + * + * Programmer: John Mainzer, 5/10/04 + * + * Modifications: + * + * JRM -- 7/21/04 + * Updated function to set the in_tree flag when inserting + * an entry into the tree. Also modified the function to + * update the tree size and len fields instead of the similar + * index fields. + * + * All of this is part of the modifications to support the + * hash table. + * + * JRM -- 7/27/04 + * Converted the function H5C_insert_entry_in_tree() into + * the macro H5C__INSERT_ENTRY_IN_TREE in the hopes of + * wringing a little more speed out of the cache. + * + * Note that we don't bother to check if the entry is already + * in the tree -- if it is, H5TB_dins() will fail. + * + *------------------------------------------------------------------------- + */ + +#define H5C__INSERT_ENTRY_IN_TREE(cache_ptr, entry_ptr) \ +{ \ + H5TB_NODE * loc_node_ptr = NULL; \ + \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + HDassert( H5F_addr_defined((entry_ptr)->addr) ); \ + HDassert( !((entry_ptr)->in_tree) ); \ + \ + loc_node_ptr = H5TB_dins((cache_ptr)->tree_ptr, (void *)(entry_ptr), NULL);\ + \ + if ( loc_node_ptr == NULL ) { \ + \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't insert entry in tree") \ + } \ + \ + (entry_ptr)->in_tree = TRUE; \ + (cache_ptr)->tree_len++; \ + (cache_ptr)->tree_size += (entry_ptr)->size; \ + \ + HDassert( (cache_ptr)->tree_len > 0 ); \ + HDassert( (cache_ptr)->tree_size > 0 ); \ + \ +} /* H5C__INSERT_ENTRY_IN_TREE */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C__REMOVE_ENTRY_FROM_TREE + * + * Purpose: Remove the specified instance of H5C_cache_entry_t from the + * index tree in the specified instance of H5C_t. Update + * the associated length and size fields. + * + * Return: N/A + * + * Programmer: John Mainzer, 5/10/04 + * + * Modifications: + * + * JRM -- 7/21/04 + * Updated function for the addition of the hash table. + * + * JRM - 7/27/04 + * Converted from the function H5C_remove_entry_from_tree() + * to the macro H5C__REMOVE_ENTRY_FROM_TREE in the hopes of + * wringing a little more performance out of the cache. + * + *------------------------------------------------------------------------- + */ + +#define H5C__REMOVE_ENTRY_FROM_TREE(cache_ptr, entry_ptr, node_ptr) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (node_ptr) ); \ + HDassert( (node_ptr)->data == (entry_ptr) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + HDassert( (entry_ptr)->in_tree ); \ + HDassert( (cache_ptr)->tree_ptr ); \ + HDassert( (cache_ptr)->tree_ptr->root ); \ + \ + if ( H5TB_rem(&((cache_ptr)->tree_ptr->root), (node_ptr), NULL) \ + != (entry_ptr) ) { \ + \ + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "Can't delete entry from tree.") \ + \ + } else { \ + \ + HDassert( (cache_ptr)->tree_len > 0 ); \ + (cache_ptr)->tree_len--; \ + HDassert( (cache_ptr)->tree_size >= (entry_ptr)->size ); \ + (cache_ptr)->tree_size -= (entry_ptr)->size; \ + (entry_ptr)->in_tree = FALSE; \ + } \ +} /* H5C__REMOVE_ENTRY_FROM_TREE */ + + +/************************************************************************** + * + * Replacement policy update macros: + * + * These used to be functions, but I converted them to macros to avoid some + * function call overhead. + * + **************************************************************************/ + +/*------------------------------------------------------------------------- + * + * Macro: H5C__UPDATE_RP_FOR_EVICTION + * + * Purpose: Update the replacement policy data structures for an + * eviction of the specified cache entry. + * + * At present, we only support the modified LRU policy, so + * this function deals with that case unconditionally. If + * we ever support other replacement policies, the function + * should switch on the current policy and act accordingly. + * + * Return: Non-negative on success/Negative on failure. + * + * Programmer: John Mainzer, 5/10/04 + * + * Modifications: + * + * JRM - 7/27/04 + * Converted the function H5C_update_rp_for_eviction() to the + * macro H5C__UPDATE_RP_FOR_EVICTION in an effort to squeeze + * a bit more performance out of the cache. + * + * At least for the first cut, I am leaving the comments and + * white space in the macro. If they cause dificulties with + * the pre-processor, I'll have to remove them. + * + * JRM - 7/28/04 + * Split macro into two version, one supporting the clean and + * dirty LRU lists, and the other not. Yet another attempt + * at optimization. + * + *------------------------------------------------------------------------- + */ + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + +#define H5C__UPDATE_RP_FOR_EVICTION(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* If the entry is clean when it is evicted, it should be on the \ + * clean LRU list, if it was dirty, it should be on the dirty LRU list. \ + * Remove it from the appropriate list according to the value of the \ + * dirty flag. \ + */ \ + \ + if ( (entry_ptr)->is_dirty ) { \ + \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + } else { \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ + \ +} /* H5C__UPDATE_RP_FOR_EVICTION */ + +#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + +#define H5C__UPDATE_RP_FOR_EVICTION(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ +} /* H5C__UPDATE_RP_FOR_EVICTION */ + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + +/*------------------------------------------------------------------------- + * + * Macro: H5C__UPDATE_RP_FOR_FLUSH + * + * Purpose: Update the replacement policy data structures for a flush + * of the specified cache entry. + * + * At present, we only support the modified LRU policy, so + * this function deals with that case unconditionally. If + * we ever support other replacement policies, the function + * should switch on the current policy and act accordingly. + * + * Return: N/A + * + * Programmer: John Mainzer, 5/6/04 + * + * Modifications: + * + * JRM - 7/27/04 + * Converted the function H5C_update_rp_for_flush() to the + * macro H5C__UPDATE_RP_FOR_FLUSH in an effort to squeeze + * a bit more performance out of the cache. + * + * At least for the first cut, I am leaving the comments and + * white space in the macro. If they cause dificulties with + * pre-processor, I'll have to remove them. + * + * JRM - 7/28/04 + * Split macro into two version, one supporting the clean and + * dirty LRU lists, and the other not. Yet another attempt + * at optimization. + * + *------------------------------------------------------------------------- + */ + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + +#define H5C__UPDATE_RP_FOR_FLUSH(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list, and re-insert it at the head. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* since the entry is being flushed or cleared, one would think that it \ + * must be dirty -- but that need not be the case. Use the dirty flag \ + * to infer whether the entry is on the clean or dirty LRU list, and \ + * remove it. Then insert it at the head of the clean LRU list. \ + * \ + * The function presumes that a dirty entry will be either cleared or \ + * flushed shortly, so it is OK if we put a dirty entry on the clean \ + * LRU list. \ + */ \ + \ + if ( (entry_ptr)->is_dirty ) { \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + } else { \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ + \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + \ + /* End modified LRU specific code. */ \ + \ +} /* H5C__UPDATE_RP_FOR_FLUSH */ + +#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + +#define H5C__UPDATE_RP_FOR_FLUSH(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list, and re-insert it at the head. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* End modified LRU specific code. */ \ + \ +} /* H5C__UPDATE_RP_FOR_FLUSH */ + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + +/*------------------------------------------------------------------------- + * + * Macro: H5C__UPDATE_RP_FOR_INSERTION + * + * Purpose: Update the replacement policy data structures for an + * insertion of the specified cache entry. + * + * At present, we only support the modified LRU policy, so + * this function deals with that case unconditionally. If + * we ever support other replacement policies, the function + * should switch on the current policy and act accordingly. + * + * Return: N/A + * + * Programmer: John Mainzer, 5/17/04 + * + * Modifications: + * + * JRM - 7/27/04 + * Converted the function H5C_update_rp_for_insertion() to the + * macro H5C__UPDATE_RP_FOR_INSERTION in an effort to squeeze + * a bit more performance out of the cache. + * + * At least for the first cut, I am leaving the comments and + * white space in the macro. If they cause dificulties with + * pre-processor, I'll have to remove them. + * + * JRM - 7/28/04 + * Split macro into two version, one supporting the clean and + * dirty LRU lists, and the other not. Yet another attempt + * at optimization. + * + *------------------------------------------------------------------------- + */ + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + +#define H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* insert the entry at the head of the LRU list. */ \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* insert the entry at the head of the clean or dirty LRU list as \ + * appropriate. \ + */ \ + \ + if ( entry_ptr->is_dirty ) { \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + } else { \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ + \ + /* End modified LRU specific code. */ \ + \ +} + +#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + +#define H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* insert the entry at the head of the LRU list. */ \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* End modified LRU specific code. */ \ + \ +} + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + +/*------------------------------------------------------------------------- + * + * Macro: H5C__UPDATE_RP_FOR_PROTECT + * + * Purpose: Update the replacement policy data structures for a + * protect of the specified cache entry. + * + * To do this, unlink the specified entry from any data + * structures used by the replacement policy, and add the + * entry to the protected list. + * + * At present, we only support the modified LRU policy, so + * this function deals with that case unconditionally. If + * we ever support other replacement policies, the function + * should switch on the current policy and act accordingly. + * + * Return: N/A + * + * Programmer: John Mainzer, 5/17/04 + * + * Modifications: + * + * JRM - 7/27/04 + * Converted the function H5C_update_rp_for_protect() to the + * macro H5C__UPDATE_RP_FOR_PROTECT in an effort to squeeze + * a bit more performance out of the cache. + * + * At least for the first cut, I am leaving the comments and + * white space in the macro. If they cause dificulties with + * pre-processor, I'll have to remove them. + * + * JRM - 7/28/04 + * Split macro into two version, one supporting the clean and + * dirty LRU lists, and the other not. Yet another attempt + * at optimization. + * + *------------------------------------------------------------------------- + */ + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + +#define H5C__UPDATE_RP_FOR_PROTECT(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* Similarly, remove the entry from the clean or dirty LRU list \ + * as appropriate. \ + */ \ + \ + if ( (entry_ptr)->is_dirty ) { \ + \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + \ + } else { \ + \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ + \ + /* End modified LRU specific code. */ \ + \ + /* Regardless of the replacement policy, now add the entry to the \ + * protected list. \ + */ \ + \ + H5C__DLL_APPEND((entry_ptr), (cache_ptr)->pl_head_ptr, \ + (cache_ptr)->pl_tail_ptr, \ + (cache_ptr)->pl_len, \ + (cache_ptr)->pl_size, (fail_val)) \ +} /* H5C__UPDATE_RP_FOR_PROTECT */ + +#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + +#define H5C__UPDATE_RP_FOR_PROTECT(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* End modified LRU specific code. */ \ + \ + /* Regardless of the replacement policy, now add the entry to the \ + * protected list. \ + */ \ + \ + H5C__DLL_APPEND((entry_ptr), (cache_ptr)->pl_head_ptr, \ + (cache_ptr)->pl_tail_ptr, \ + (cache_ptr)->pl_len, \ + (cache_ptr)->pl_size, (fail_val)) \ +} /* H5C__UPDATE_RP_FOR_PROTECT */ + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + +/*------------------------------------------------------------------------- + * + * Macro: H5C__UPDATE_RP_FOR_RENAME + * + * Purpose: Update the replacement policy data structures for a + * rename of the specified cache entry. + * + * At present, we only support the modified LRU policy, so + * this function deals with that case unconditionally. If + * we ever support other replacement policies, the function + * should switch on the current policy and act accordingly. + * + * Return: N/A + * + * Programmer: John Mainzer, 5/17/04 + * + * Modifications: + * + * JRM - 7/27/04 + * Converted the function H5C_update_rp_for_rename() to the + * macro H5C__UPDATE_RP_FOR_RENAME in an effort to squeeze + * a bit more performance out of the cache. + * + * At least for the first cut, I am leaving the comments and + * white space in the macro. If they cause dificulties with + * pre-processor, I'll have to remove them. + * + * JRM - 7/28/04 + * Split macro into two version, one supporting the clean and + * dirty LRU lists, and the other not. Yet another attempt + * at optimization. + * + *------------------------------------------------------------------------- + */ + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + +#define H5C__UPDATE_RP_FOR_RENAME(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list, and re-insert it at the head. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* move the entry to the head of either the clean or dirty LRU list \ + * as appropriate. \ + */ \ + \ + if ( (entry_ptr)->is_dirty ) { \ + \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + \ + } else { \ + \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ + \ + /* End modified LRU specific code. */ \ + \ +} /* H5C__UPDATE_RP_FOR_RENAME */ + +#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + +#define H5C__UPDATE_RP_FOR_RENAME(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* modified LRU specific code */ \ + \ + /* remove the entry from the LRU list, and re-insert it at the head. */ \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* End modified LRU specific code. */ \ + \ +} /* H5C__UPDATE_RP_FOR_RENAME */ + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + +/*------------------------------------------------------------------------- + * + * Macro: H5C__UPDATE_RP_FOR_UNPROTECT + * + * Purpose: Update the replacement policy data structures for an + * unprotect of the specified cache entry. + * + * To do this, unlink the specified entry from the protected + * list, and re-insert it in the data structures used by the + * current replacement policy. + * + * At present, we only support the modified LRU policy, so + * this function deals with that case unconditionally. If + * we ever support other replacement policies, the function + * should switch on the current policy and act accordingly. + * + * Return: N/A + * + * Programmer: John Mainzer, 5/19/04 + * + * Modifications: + * + * JRM - 7/27/04 + * Converted the function H5C_update_rp_for_unprotect() to + * the macro H5C__UPDATE_RP_FOR_UNPROTECT in an effort to + * squeeze a bit more performance out of the cache. + * + * At least for the first cut, I am leaving the comments and + * white space in the macro. If they cause dificulties with + * pre-processor, I'll have to remove them. + * + * JRM - 7/28/04 + * Split macro into two version, one supporting the clean and + * dirty LRU lists, and the other not. Yet another attempt + * at optimization. + * + *------------------------------------------------------------------------- + */ + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + +#define H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( (entry_ptr)->is_protected); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* Regardless of the replacement policy, remove the entry from the \ + * protected list. \ + */ \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->pl_head_ptr, \ + (cache_ptr)->pl_tail_ptr, (cache_ptr)->pl_len, \ + (cache_ptr)->pl_size, (fail_val)) \ + \ + /* modified LRU specific code */ \ + \ + /* insert the entry at the head of the LRU list. */ \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* Similarly, insert the entry at the head of either the clean or \ + * dirty LRU list as appropriate. \ + */ \ + \ + if ( (entry_ptr)->is_dirty ) { \ + \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + \ + } else { \ + \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ + \ + /* End modified LRU specific code. */ \ + \ +} /* H5C__UPDATE_RP_FOR_UNPROTECT */ + +#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + +#define H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( (entry_ptr)->is_protected); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* Regardless of the replacement policy, remove the entry from the \ + * protected list. \ + */ \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->pl_head_ptr, \ + (cache_ptr)->pl_tail_ptr, (cache_ptr)->pl_len, \ + (cache_ptr)->pl_size, (fail_val)) \ + \ + /* modified LRU specific code */ \ + \ + /* insert the entry at the head of the LRU list. */ \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* End modified LRU specific code. */ \ + \ +} /* H5C__UPDATE_RP_FOR_UNPROTECT */ + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + +/**************************************************************************** + * + * structure H5C_t + * + * Catchall structure for all variables specific to an instance of the cache. + * + * While the individual fields of the structure are discussed below, the + * following overview may be helpful. + * + * Entries in the cache are stored in an instance of H5TB_TREE, indexed on + * the entry's disk address. While the H5TB_TREE is less efficient than + * hash table, it keeps the entries in address sorted order. As flushes + * in parallel mode are more efficient if they are issued in increasing + * address order, this is a significant benefit. Also the H5TB_TREE code + * was readily available, which reduced development time. + * + * While the cache was designed with multiple replacement policies in mind, + * at present only a modified form of LRU is supported. + * + * JRM - 4/26/04 + * + * Profiling has indicated that searches in the instance of H5TB_TREE are + * too expensive. To deal with this issue, I have augmented the cache + * with a hash table in which all entries will be stored. Given the + * advantages of flushing entries in increasing address order, the TBBT + * is retained, but only dirty entries are stored in it. At least for + * now, we will leave entries in the TBBT after they are flushed. + * + * Note that index_size and index_len now refer to the total size of + * and number of entries in the hash table. + * + * JRM - 7/19/04 + * + * ********************************************* + * + * WARNING: A copy of H5C_t is in tst/cache.c (under the name "local_H5C_t" + * to allow the test code to access the internal fields of the + * cache. If you modify H5C_t, be sure to update local_H5C_t + * in cache.c as well. + * + * ********************************************* + * + * magic: Unsigned 32 bit integer always set to H5C__H5C_T_MAGIC. This + * field is used to validate pointers to instances of H5C_t. + * + * max_type_id: Integer field containing the maximum type id number assigned + * to a type of entry in the cache. All type ids from 0 to + * max_type_id inclusive must be defined. The names of the + * types are stored in the type_name_table discussed below, and + * indexed by the ids. + * + * type_name_table_ptr: Pointer to an array of pointer to char of length + * max_type_id + 1. The strings pointed to by the entries + * in the array are the names of the entry types associated + * with the indexing type IDs. + * + * max_cache_size: Nominal maximum number of bytes that may be stored in the + * cache. This value should be viewed as a soft limit, as the + * cache can exceed this value under the following circumstances: + * + * a) All entries in the cache are protected, and the cache is + * asked to insert a new entry. In this case the new entry + * will be created. If this causes the cache to exceed + * max_cache_size, it will do so. The cache will attempt + * to reduce its size as entries are unprotected. + * + * b) When running in parallel mode, the cache may not be + * permitted to flush a dirty entry in response to a read. + * If there are no clean entries available to evict, the + * cache will exceed its maximum size. Again the cache + * will attempt to reduce its size to the max_cache_size + * limit on the next cache write. + * + * min_clean_size: Nominal minimum number of clean bytes in the cache. + * The cache attempts to maintain this number of bytes of + * clean data so as to avoid case b) above. Again, this is + * a soft limit. + * + * + * In addition to the call back functions required for each entry, the + * cache requires the following call back functions for this instance of + * the cache as a whole: + * + * check_write_permitted: In certain applications, the cache may not + * be allowed to write to disk at certain time. If specified, + * the check_write_permitted function is used to determine if + * a write is permissible at any given point in time. + * + * If no such function is specified (i.e. this field is NULL), + * the cache will presume that writes are always permissable. + * + * + * The cache requires an index to facilitate searching for entries. The + * following fields support that index. + * + * index_len: Number of entries currently in the hash table used to index + * the cache. + * + * index_size: Number of bytes of cache entries currently stored in the + * hash table used to index the cache. + * + * This value should not be mistaken for footprint of the + * cache in memory. The average cache entry is small, and + * the cache has a considerable overhead. Multiplying the + * index_size by two should yield a conservative estimate + * of the cache's memory footprint. + * + * index: Array of pointer to H5C_cache_entry_t of size + * H5C__HASH_TABLE_LEN. At present, this value is a power + * of two, not the usual prime number. + * + * I hope that the variable size of cache elements, the large + * hash table size, and the way in which HDF5 allocates space + * will combine to avoid problems with periodicity. If so, we + * can use a trivial hash function (a bit-and and a 3 bit left + * shift) with some small savings. + * + * If not, it will become evident in the statistics. Changing + * to the usual prime number length hash table will require + * changing the H5C__HASH_FCN macro and the deletion of the + * H5C__HASH_MASK #define. No other changes should be required. + * + * + * When we flush the cache, we need to write entries out in increasing + * address order. An instance of TBBT is used to store dirty entries in + * sorted order. Whether it is cheaper to sort the dirty entries as needed, + * or to maintain the tree is an open question. At a guess, it depends + * on how frequently the cache is flushed. We will see how it goes. + * + * For now at least, I will not remove dirty entries from the tree as they + * are flushed. + * + * tree_len: Number of entries currently in the threaded binary B-tree + * used to maintain a sorted list of dirty entries in the + * cache. + * + * tree_size: Number of bytes of cache entries currently stored in the + * threaded binary B-tree used to maintain a sorted list of + * dirty entries in the cache. + * + * tree_ptr: pointer to the instance of H5TB_TREE used maintain a sorted + * list of dirty entries in the cache. This sorted list has + * two uses: + * + * a) It allows us to flush dirty entries in increasing address + * order, which results in significant savings. + * + * b) It facilitates checking for adjacent dirty entries when + * attempting to evict entries from the cache. While we + * don't use this at present, I hope that this will allow + * some optimizations when I get to it. + * + * + * When a cache entry is protected, it must be removed from the LRU + * list(s) as it cannot be either flushed or evicted until it is unprotected. + * The following fields are used to implement the protected list (pl). + * + * pl_len: Number of entries currently residing on the protected list. + * + * pl_size: Number of bytes of cache entries currently residing on the + * protected list. + * + * pl_head_ptr: Pointer to the head of the doubly linked list of protected + * entries. Note that cache entries on this list are linked + * by their next and prev fields. + * + * This field is NULL if the list is empty. + * + * pl_tail_ptr: Pointer to the tail of the doubly linked list of protected + * entries. Note that cache entries on this list are linked + * by their next and prev fields. + * + * This field is NULL if the list is empty. + * + * + * The cache must have a replacement policy, and the fields supporting this + * policy must be accessible from this structure. + * + * While there has been interest in several replacement policies for + * this cache, the initial development schedule is tight. Thus I have + * elected to support only a modified LRU policy for the first cut. + * + * To further simplify matters, I have simply included the fields needed + * by the modified LRU in this structure. When and if we add support for + * other policies, it will probably be easiest to just add the necessary + * fields to this structure as well -- we only create one instance of this + * structure per file, so the overhead is not excessive. + * + * + * Fields supporting the modified LRU policy: + * + * See most any OS text for a discussion of the LRU replacement policy. + * + * When operating in parallel mode, we must ensure that a read does not + * cause a write. If it does, the process will hang, as the write will + * be collective and the other processes will not know to participate. + * + * To deal with this issue, I have modified the usual LRU policy by adding + * clean and dirty LRU lists to the usual LRU list. + * + * The clean LRU list is simply the regular LRU list with all dirty cache + * entries removed. + * + * Similarly, the dirty LRU list is the regular LRU list with all the clean + * cache entries removed. + * + * When reading in parallel mode, we evict from the clean LRU list only. + * This implies that we must try to ensure that the clean LRU list is + * reasonably well stocked at all times. + * + * We attempt to do this by trying to flush enough entries on each write + * to keep the cLRU_list_size >= min_clean_size. + * + * Even if we start with a completely clean cache, a sequence of protects + * without unprotects can empty the clean LRU list. In this case, the + * cache must grow temporarily. At the next write, we will attempt to + * evict enough entries to reduce index_size to less than max_cache_size. + * While this will usually be possible, all bets are off if enough entries + * are protected. + * + * Discussions of the individual fields used by the modified LRU replacement + * policy follow: + * + * LRU_list_len: Number of cache entries currently on the LRU list. + * + * Observe that LRU_list_len + pl_len must always equal + * index_len. + * + * LRU_list_size: Number of bytes of cache entries currently residing on the + * LRU list. + * + * Observe that LRU_list_size + pl_size must always equal + * index_size. + * + * LRU_head_ptr: Pointer to the head of the doubly linked LRU list. Cache + * entries on this list are linked by their next and prev fields. + * + * This field is NULL if the list is empty. + * + * LRU_tail_ptr: Pointer to the tail of the doubly linked LRU list. Cache + * entries on this list are linked by their next and prev fields. + * + * This field is NULL if the list is empty. + * + * cLRU_list_len: Number of cache entries currently on the clean LRU list. + * + * Observe that cLRU_list_len + dLRU_list_len must always + * equal LRU_list_len. + * + * cLRU_list_size: Number of bytes of cache entries currently residing on + * the clean LRU list. + * + * Observe that cLRU_list_size + dLRU_list_size must always + * equal LRU_list_size. + * + * cLRU_head_ptr: Pointer to the head of the doubly linked clean LRU list. + * Cache entries on this list are linked by their aux_next and + * aux_prev fields. + * + * This field is NULL if the list is empty. + * + * cLRU_tail_ptr: Pointer to the tail of the doubly linked clean LRU list. + * Cache entries on this list are linked by their aux_next and + * aux_prev fields. + * + * This field is NULL if the list is empty. + * + * dLRU_list_len: Number of cache entries currently on the dirty LRU list. + * + * Observe that cLRU_list_len + dLRU_list_len must always + * equal LRU_list_len. + * + * dLRU_list_size: Number of cache entries currently on the dirty LRU list. + * + * Observe that cLRU_list_len + dLRU_list_len must always + * equal LRU_list_len. + * + * dLRU_head_ptr: Pointer to the head of the doubly linked dirty LRU list. + * Cache entries on this list are linked by their aux_next and + * aux_prev fields. + * + * This field is NULL if the list is empty. + * + * dLRU_tail_ptr: Pointer to the tail of the doubly linked dirty LRU list. + * Cache entries on this list are linked by their aux_next and + * aux_prev fields. + * + * This field is NULL if the list is empty. + * + * + * Statistics collection fields: + * + * When enabled, these fields are used to collect statistics as described + * below. The first set are collected only when H5C_COLLECT_CACHE_STATS + * is true. + * + * hits: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the number of times an entry with type id + * equal to the array index has been in cache when requested in + * the current epoch. + * + * misses: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the number of times an entry with type id + * equal to the array index has not been in cache when + * requested in the current epoch. + * + * insertions: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the number of times an entry with type + * id equal to the array index has been inserted into the + * cache in the current epoch. + * + * clears: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the number of times an entry with type + * id equal to the array index has been cleared in the current + * epoch. + * + * flushes: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the number of times an entry with type id + * equal to the array index has been written to disk in the + * current epoch. + * + * evictions: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the number of times an entry with type id + * equal to the array index has been evicted from the cache in + * the current epoch. + * + * renames: Array of int64 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the number of times an entry with type + * id equal to the array index has been renamed in the current + * epoch. + * + * total_ht_insertions: Number of times entries have been inserted into the + * hash table in the current epoch. + * + * total_ht_deletions: Number of times entries have been deleted from the + * hash table in the current epoch. + * + * successful_ht_searches: int64 containing the total number of successful + * searches of the hash table in the current epoch. + * + * total_successful_ht_search_depth: int64 containing the total number of + * entries other than the targets examined in successful + * searches of the hash table in the current epoch. + * + * failed_ht_searches: int64 containing the total number of unsuccessful + * searches of the hash table in the current epoch. + * + * total_failed_ht_search_depth: int64 containing the total number of + * entries examined in unsuccessful searches of the hash + * table in the current epoch. + * + * max_index_len: Largest value attained by the index_len field in the + * current epoch. + * + * max_index_size: Largest value attained by the index_size field in the + * current epoch. + * + * max_tree_len: Largest value attained by the tree_len field in the + * current epoch. + * + * max_tree_size: Largest value attained by the tree_size field in the + * current epoch. + * + * max_pl_len: Largest value attained by the pl_len field in the + * current epoch. + * + * max_pl_size: Largest value attained by the pl_size field in the + * current epoch. + * + * The remaining stats are collected only when both H5C_COLLECT_CACHE_STATS + * and H5C_COLLECT_CACHE_ENTRY_STATS are true. + * + * max_accesses: Array of int32 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the maximum number of times any single + * entry with type id equal to the array index has been + * accessed in the current epoch. + * + * min_accesses: Array of int32 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the minimum number of times any single + * entry with type id equal to the array index has been + * accessed in the current epoch. + * + * max_clears: Array of int32 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the maximum number of times any single + * entry with type id equal to the array index has been cleared + * in the current epoch. + * + * max_flushes: Array of int32 of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the maximum number of times any single + * entry with type id equal to the array index has been + * flushed in the current epoch. + * + * max_size: Array of size_t of length H5C__MAX_NUM_TYPE_IDS. The cells + * are used to record the maximum size of any single entry + * with type id equal to the array index that has resided in + * the cache in the current epoch. + * + * + * Fields supporting testing: + * + * For test purposes, it is useful to turn off some asserts and sanity + * checks. The following flags support this. + * + * skip_file_checks: Boolean flag used to skip sanity checks on file + * parameters passed to the cache. In the test bed, there + * is no reason to have a file open, as the cache proper + * just passes these parameters through without using them. + * + * When this flag is set, all sanity checks on the file + * parameters are skipped. The field defaults to FALSE. + * + * skip_dxpl_id_checks: Boolean flag used to skip sanity checks on the + * dxpl_id parameters passed to the cache. These are not + * used directly by the cache, so skipping the checks + * simplifies the test bed. + * + * When this flag is set, all sanity checks on the dxpl_id + * parameters are skipped. The field defaults to FALSE. + * + ****************************************************************************/ + +#define H5C__H5C_T_MAGIC 0x005CAC0E +#define H5C__MAX_NUM_TYPE_IDS 9 + +struct H5C_t +{ + uint32_t magic; + + int32_t max_type_id; + const char * (* type_name_table_ptr); + + size_t max_cache_size; + size_t min_clean_size; + + H5C_write_permitted_func_t check_write_permitted; + + int32_t index_len; + size_t index_size; + H5C_cache_entry_t * (index[H5C__HASH_TABLE_LEN]); + + + int32_t tree_len; + size_t tree_size; + H5TB_TREE * tree_ptr; + + int32_t pl_len; + size_t pl_size; + H5C_cache_entry_t * pl_head_ptr; + H5C_cache_entry_t * pl_tail_ptr; + + int32_t LRU_list_len; + size_t LRU_list_size; + H5C_cache_entry_t * LRU_head_ptr; + H5C_cache_entry_t * LRU_tail_ptr; + + int32_t cLRU_list_len; + size_t cLRU_list_size; + H5C_cache_entry_t * cLRU_head_ptr; + H5C_cache_entry_t * cLRU_tail_ptr; + + int32_t dLRU_list_len; + size_t dLRU_list_size; + H5C_cache_entry_t * dLRU_head_ptr; + H5C_cache_entry_t * dLRU_tail_ptr; + +#if H5C_COLLECT_CACHE_STATS + + /* stats fields */ + int64_t hits[H5C__MAX_NUM_TYPE_IDS]; + int64_t misses[H5C__MAX_NUM_TYPE_IDS]; + int64_t insertions[H5C__MAX_NUM_TYPE_IDS]; + int64_t clears[H5C__MAX_NUM_TYPE_IDS]; + int64_t flushes[H5C__MAX_NUM_TYPE_IDS]; + int64_t evictions[H5C__MAX_NUM_TYPE_IDS]; + int64_t renames[H5C__MAX_NUM_TYPE_IDS]; + + int64_t total_ht_insertions; + int64_t total_ht_deletions; + int64_t successful_ht_searches; + int64_t total_successful_ht_search_depth; + int64_t failed_ht_searches; + int64_t total_failed_ht_search_depth; + + int32_t max_index_len; + size_t max_index_size; + + int32_t max_tree_len; + size_t max_tree_size; + + int32_t max_pl_len; + size_t max_pl_size; + +#if H5C_COLLECT_CACHE_ENTRY_STATS + + int32_t max_accesses[H5C__MAX_NUM_TYPE_IDS]; + int32_t min_accesses[H5C__MAX_NUM_TYPE_IDS]; + int32_t max_clears[H5C__MAX_NUM_TYPE_IDS]; + int32_t max_flushes[H5C__MAX_NUM_TYPE_IDS]; + size_t max_size[H5C__MAX_NUM_TYPE_IDS]; + +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ + +#endif /* H5C_COLLECT_CACHE_STATS */ + + hbool_t skip_file_checks; + hbool_t skip_dxpl_id_checks; + +}; + + +/* + * Private file-scope variables. + */ + +/* Declare a free list to manage the H5C_t struct */ +H5FL_DEFINE_STATIC(H5C_t); + +/* + * Private file-scope function declarations: + */ + +static herr_t H5C_flush_single_entry(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type_ptr, + haddr_t addr, + unsigned flags, + H5TB_NODE * tgt_node_ptr, + hbool_t * first_flush_ptr, + hbool_t del_entry_from_tree_on_destroy); + +static void * H5C_load_entry(H5F_t * f, + hid_t dxpl_id, + const H5C_class_t * type, + haddr_t addr, + const void * udata1, + void * udata2, + hbool_t skip_file_checks); + +static herr_t H5C_make_space_in_cache(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + size_t space_needed, + hbool_t write_permitted); + + +/*------------------------------------------------------------------------- + * Function: H5C_create + * + * Purpose: Allocate, initialize, and return the address of a new + * instance of H5C_t. + * + * In general, the max_cache_size parameter must be positive, + * and the min_clean_size parameter must lie in the closed + * interval [0, max_cache_size]. + * + * The check_write_permitted parameter must either be NULL, + * or point to a function of type H5C_write_permitted_func_t. + * If it is NULL, the cache will presume that writes are + * always permitted. + * + * Return: Success: Pointer to the new instance. + * + * Failure: NULL + * + * Programmer: John Mainzer + * 6/2/04 + * + * Modifications: + * + * JRM -- 7/20/04 + * Updated for the addition of the hash table. + * + *------------------------------------------------------------------------- + */ + +H5C_t * +H5C_create(size_t max_cache_size, + size_t min_clean_size, + int max_type_id, + const char * (* type_name_table_ptr), + H5C_write_permitted_func_t check_write_permitted) +{ + int i; + H5C_t * cache_ptr = NULL; + H5C_t * ret_value = NULL; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_create, NULL) + + HDassert( max_cache_size > 0 ); + HDassert( min_clean_size <= max_cache_size ); + + HDassert( max_type_id >= 0 ); + HDassert( max_type_id < H5C__MAX_NUM_TYPE_IDS ); + HDassert( type_name_table_ptr ); + + for ( i = 0; i <= max_type_id; i++ ) { + + HDassert( (type_name_table_ptr)[i] ); + HDassert( HDstrlen(( type_name_table_ptr)[i]) > 0 ); + } + + + if ( NULL == (cache_ptr = H5FL_CALLOC(H5C_t)) ) { + + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, \ + "memory allocation failed") + } + + if ( (cache_ptr->tree_ptr = H5TB_fast_dmake(H5TB_FAST_HADDR_COMPARE)) + == NULL ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, NULL, "can't create TBBT.") + } + + /* If we get this far, we should succeed. Go ahead and initialize all + * the fields. + */ + + cache_ptr->magic = H5C__H5C_T_MAGIC; + + cache_ptr->max_type_id = max_type_id; + cache_ptr->type_name_table_ptr = type_name_table_ptr; + + cache_ptr->max_cache_size = max_cache_size; + cache_ptr->min_clean_size = min_clean_size; + + cache_ptr->check_write_permitted = check_write_permitted; + + cache_ptr->index_len = 0; + cache_ptr->index_size = (size_t)0; + + cache_ptr->tree_len = 0; + cache_ptr->tree_size = (size_t)0; + + for ( i = 0; i < H5C__HASH_TABLE_LEN; i++ ) + { + (cache_ptr->index)[i] = NULL; + } + + cache_ptr->pl_len = 0; + cache_ptr->pl_size = (size_t)0; + cache_ptr->pl_head_ptr = NULL; + cache_ptr->pl_tail_ptr = NULL; + + cache_ptr->LRU_list_len = 0; + cache_ptr->LRU_list_size = (size_t)0; + cache_ptr->LRU_head_ptr = NULL; + cache_ptr->LRU_tail_ptr = NULL; + + cache_ptr->cLRU_list_len = 0; + cache_ptr->cLRU_list_size = (size_t)0; + cache_ptr->cLRU_head_ptr = NULL; + cache_ptr->cLRU_tail_ptr = NULL; + + cache_ptr->dLRU_list_len = 0; + cache_ptr->dLRU_list_size = (size_t)0; + cache_ptr->dLRU_head_ptr = NULL; + cache_ptr->dLRU_tail_ptr = NULL; + + H5C_stats__reset(cache_ptr); + + cache_ptr->skip_file_checks = FALSE; + cache_ptr->skip_dxpl_id_checks = FALSE; + + /* Set return value */ + ret_value = cache_ptr; + +done: + + if ( ret_value == 0 ) { + + if ( cache_ptr != NULL ) { + + if ( cache_ptr->tree_ptr != NULL ) { + + /* the index tree should be empty, so we can pass in + * NULL for the fd & fk parameters. + */ + H5TB_dfree(cache_ptr->tree_ptr, NULL, NULL); + } + + cache_ptr->magic = 0; + H5FL_FREE(H5C_t, cache_ptr); + cache_ptr = NULL; + + } /* end if */ + + } /* end if */ + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_create() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_dest + * + * Purpose: Flush all data to disk and destroy the cache. + * + * This function fails if any object are protected since the + * resulting file might not be consistent. + * + * The primary_dxpl_id and secondary_dxpl_id parameters + * specify the dxpl_ids used on the first write occasioned + * by the destroy (primary_dxpl_id), and on all subsequent + * writes (secondary_dxpl_id). This is useful in the metadata + * cache, but may not be needed elsewhere. If so, just use the + * same dxpl_id for both parameters. + * + * Note that *cache_ptr has been freed upon successful return. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 6/2/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_dest(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr) +{ + herr_t ret_value=SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_dest, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + + if ( H5C_flush_cache(f, primary_dxpl_id, secondary_dxpl_id, + cache_ptr, H5F_FLUSH_INVALIDATE) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache") + } + + if ( cache_ptr->tree_ptr != NULL ) { + + /* the index tree should be empty, so we can pass in + * NULL for the fd & fk parameters. + */ + H5TB_dfree(cache_ptr->tree_ptr, NULL, NULL); + cache_ptr->tree_ptr = NULL; + } + + cache_ptr->magic = 0; + + H5FL_FREE(H5C_t, cache_ptr); + +done: + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_dest() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_dest_empty + * + * Purpose: Destroy an empty cache. + * + * This function fails if the cache is not empty on entry. + * + * Note that *cache_ptr has been freed upon successful return. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 6/2/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_dest_empty(H5C_t * cache_ptr) +{ + herr_t ret_value=SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_dest_empty, FAIL) + + /* This would normally be an assert, but we need to use an HGOTO_ERROR + * call to shut up the compiler. + */ + if ( ( ! cache_ptr ) || + ( cache_ptr->magic != H5C__H5C_T_MAGIC ) || + ( cache_ptr->index_len != 0 ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "Bad cache_ptr or non-empty cache on entry.") + } + + + if ( cache_ptr->tree_ptr != NULL ) { + + /* the tree should be empty, so we can pass in + * NULL for the fd & fk parameters. + */ + H5TB_dfree(cache_ptr->tree_ptr, NULL, NULL); + cache_ptr->tree_ptr = NULL; + } + + cache_ptr->magic = 0; + + H5FL_FREE(H5C_t, cache_ptr); + +done: + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_dest_empty() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_flush_cache + * + * Purpose: Flush (and possibly destroy) the entries contained in the + * specified cache. + * + * If the cache contains protected entries, the function will + * fail, as protected entries cannot be flushed. However + * all unprotected entries should be flushed before the + * function returns failure. + * + * The primary_dxpl_id and secondary_dxpl_id parameters + * specify the dxpl_ids used on the first write occasioned + * by the flush (primary_dxpl_id), and on all subsequent + * writes (secondary_dxpl_id). This is useful in the metadata + * cache, but may not be needed elsewhere. If so, just use the + * same dxpl_id for both parameters. + * + * Return: Non-negative on success/Negative on failure or if there was + * a request to flush all items and something was protected. + * + * Programmer: John Mainzer + * 6/2/04 + * + * Modifications: + * + * JRM -- 7/20/04 + * Modified the function for the addition of the hash table. + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_flush_cache(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + unsigned flags) +{ + herr_t status; + herr_t ret_value = SUCCEED; + hbool_t destroy = ( (flags & H5F_FLUSH_INVALIDATE) != 0 ); + hbool_t first_flush = TRUE; + int32_t protected_entries = 0; + int32_t i; + H5TB_NODE * node_ptr; + H5C_cache_entry_t * entry_ptr; +#if H5C_DO_SANITY_CHECKS + int32_t actual_tree_len = 0; + size_t actual_tree_size = 0; +#endif /* H5C_DO_SANITY_CHECKS */ + + FUNC_ENTER_NOAPI(H5C_flush_cache, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( cache_ptr->tree_ptr ); + + if ( cache_ptr->tree_ptr->root == NULL ) { + + node_ptr = NULL; + HDassert( cache_ptr->tree_len == 0 ); + HDassert( cache_ptr->tree_size == 0 ); + + } else { + + node_ptr = H5TB_first(cache_ptr->tree_ptr->root); + } + + while ( node_ptr != NULL ) + { + entry_ptr = (H5C_cache_entry_t *)(node_ptr->data); + + HDassert( entry_ptr != NULL ); + HDassert( entry_ptr->in_tree ); + +#if H5C_DO_SANITY_CHECKS + actual_tree_len++; + actual_tree_size += entry_ptr->size; +#endif /* H5C_DO_SANITY_CHECKS */ + + if ( entry_ptr->is_protected ) { + + /* we have major problems -- but lets flush everything + * we can before we flag an error. + */ + protected_entries++; + + } else { + + status = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + NULL, + entry_ptr->addr, + flags, + node_ptr, + &first_flush, + FALSE); + if ( status < 0 ) { + + /* This shouldn't happen -- if it does, we are toast so + * just scream and die. + */ + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "Can't flush entry.") + } + } + + node_ptr = H5TB_next(node_ptr); + + } /* while */ + +#if H5C_DO_SANITY_CHECKS + HDassert( actual_tree_len == cache_ptr->tree_len ); + HDassert( actual_tree_size == cache_ptr->tree_size ); +#endif /* H5C_DO_SANITY_CHECKS */ + + if ( destroy ) { + + /* don't pass in any key or data free functions, as all + * unprotected entries should have already been destroyed. + */ + H5TB_free(&(cache_ptr->tree_ptr->root), NULL, NULL); + cache_ptr->tree_len = 0; + cache_ptr->tree_size = 0; + + /* Since we are doing a destroy, we must make a pass through + * the hash table and flush all entries that remain. Note that + * all remaining entries entries must be clean, so this will + * not result in any writes to disk. + */ + for ( i = 0; i < H5C__HASH_TABLE_LEN; i++ ) + { + while ( cache_ptr->index[i] ) + { + entry_ptr = cache_ptr->index[i]; + + if ( entry_ptr->is_protected ) { + + /* we have major problems -- but lets flush and destroy + * everything we can before we flag an error. + */ + + H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr) + + if ( !entry_ptr->in_tree ) { + + protected_entries++; + HDassert( !(entry_ptr->is_dirty) ); + } + } else { + + HDassert( !(entry_ptr->is_dirty) ); + HDassert( !(entry_ptr->in_tree) ); + + status = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + NULL, + entry_ptr->addr, + flags, + NULL, + &first_flush, + FALSE); + if ( status < 0 ) { + + /* This shouldn't happen -- if it does, we are toast so + * just scream and die. + */ + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "Can't flush entry.") + } + } + } + } + + HDassert( protected_entries == cache_ptr->pl_len ); + + if ( protected_entries > 0 ) + { + /* the caller asked us to flush and destroy a cache that + * contains one or more protected entries. Since we can't + * flush protected entries, we haven't destroyed them either. + * Since they are all on the protected list, just re-insert + * them into the cache before we flag an error. + */ + entry_ptr = cache_ptr->pl_head_ptr; + + while ( entry_ptr != NULL ) + { + entry_ptr->in_tree = FALSE; + + H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL) + + if ( entry_ptr->is_dirty ) { + + H5C__INSERT_ENTRY_IN_TREE(cache_ptr, entry_ptr) + } + entry_ptr = entry_ptr->next; + } + } + } + + HDassert( protected_entries <= cache_ptr->pl_len ); + + if ( cache_ptr->pl_len > 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_PROTECT, FAIL, "cache has protected items") + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_flush_cache() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_insert_entry + * + * Purpose: Adds the specified thing to the cache. The thing need not + * exist on disk yet, but it must have an address and disk + * space reserved. + * + * The primary_dxpl_id and secondary_dxpl_id parameters + * specify the dxpl_ids used on the first write occasioned + * by the insertion (primary_dxpl_id), and on all subsequent + * writes (secondary_dxpl_id). This is useful in the + * metadata cache, but may not be needed elsewhere. If so, + * just use the same dxpl_id for both parameters. + * + * The primary_dxpl_id is the dxpl_id passed to the + * check_write_permitted function if such a function has been + * provided. + * + * Observe that this function cannot occasion a read. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 6/2/04 + * + * Modifications: + * + * JRM -- 7/21/04 + * Updated function for the addition of the hash table. + * + *------------------------------------------------------------------------- + */ + +herr_t +H5C_insert_entry(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t addr, + void * thing) +{ + herr_t result; + herr_t ret_value = SUCCEED; /* Return value */ + hbool_t write_permitted = TRUE; + H5C_cache_entry_t * entry_ptr; + H5C_cache_entry_t * test_entry_ptr; + + FUNC_ENTER_NOAPI(H5C_insert_entry, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( type ); + HDassert( type->flush ); + HDassert( type->size ); + HDassert( H5F_addr_defined(addr) ); + HDassert( thing ); + + entry_ptr = (H5C_cache_entry_t *)thing; + + entry_ptr->addr = addr; + entry_ptr->type = type; + + if ( (type->size)(f, thing, &(entry_ptr->size)) < 0 ) { + + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGETSIZE, FAIL, \ + "Can't get size of thing") + } + + HDassert( entry_ptr->size < H5C_MAX_ENTRY_SIZE ); + + entry_ptr->in_tree = FALSE; + + entry_ptr->ht_next = NULL; + entry_ptr->ht_prev = NULL; + + entry_ptr->next = NULL; + entry_ptr->prev = NULL; + + entry_ptr->aux_next = NULL; + entry_ptr->aux_prev = NULL; + + H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) + + if ((cache_ptr->index_size + entry_ptr->size) > cache_ptr->max_cache_size) { + + size_t space_needed; + + if ( cache_ptr->check_write_permitted != NULL ) { + + result = (cache_ptr->check_write_permitted)(f, + primary_dxpl_id, + &write_permitted); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ + "Can't get write_permitted") + } + } + + HDassert( entry_ptr->size <= H5C_MAX_ENTRY_SIZE ); + + space_needed = (cache_ptr->index_size + entry_ptr->size) - + cache_ptr->max_cache_size; + + /* It would be nice to be able to do a tight sanity check on + * space_needed here, but it is hard to assign an upper bound on + * its value other than then value assigned to it. + * + * This fact springs from several features of the cache: + * + * First, it is possible for the cache to grow without + * bound as long as entries are protected and not unprotected. + * + * Second, when writes are not permitted it is also possible + * for the cache to grow without bound. + * + * Finally, we don't check to see if the cache is oversized + * at the end of an unprotect. As a result, it is possible + * to have a vastly oversized cache with no protected entries + * as long as all the protects preceed the unprotects. + * + * Since items 1 and 2 are not changing any time soon, I see + * no point in worrying about the third. + * + * In any case, I hope this explains why there is no sanity + * check on space_needed here. + */ + + result = H5C_make_space_in_cache(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + space_needed, + write_permitted); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ + "H5C_make_space_in_cache failed.") + } + } + + /* verify that the new entry isn't already in the hash table -- scream + * and die if it is. + */ + + H5C__SEARCH_INDEX(cache_ptr, addr, test_entry_ptr, FAIL) + + if ( test_entry_ptr != NULL ) { + + if ( test_entry_ptr == entry_ptr ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ + "entry already in cache.") + + } else { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \ + "duplicate entry in cache.") + } + } + + /* we don't initialize the protected field until here as it is + * possible that the entry is already in the cache, and already + * protected. If it is, we don't want to make things worse by + * marking it unprotected. + */ + + entry_ptr->is_protected = FALSE; + + H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL) + + if ( entry_ptr->is_dirty ) { + + H5C__INSERT_ENTRY_IN_TREE(cache_ptr, entry_ptr) + } + + H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, FAIL) + + H5C__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr) + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_insert_entry() */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_rename_entry + * + * Purpose: Use this function to notify the cache that an entry's + * file address changed. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 6/2/04 + * + * Modifications: + * + * JRM -- 7/21/04 + * Updated function for the addition of the hash table. + * + *------------------------------------------------------------------------- + */ + +herr_t +H5C_rename_entry(H5F_t * f, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t old_addr, + haddr_t new_addr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + H5TB_NODE * old_node_ptr = NULL; + H5C_cache_entry_t * entry_ptr = NULL; + H5C_cache_entry_t * test_entry_ptr = NULL; + H5C_cache_entry_t search_target; + + FUNC_ENTER_NOAPI(H5C_rename_entry, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( type ); + HDassert( H5F_addr_defined(old_addr) ); + HDassert( H5F_addr_defined(new_addr) ); + HDassert( H5F_addr_ne(old_addr, new_addr) ); + + H5C__SEARCH_INDEX(cache_ptr, old_addr, entry_ptr, FAIL) + + if ( ( entry_ptr == NULL ) || ( entry_ptr->type != type ) ) { + + /* the old item doesn't exist in the cache, so we are done. */ + HGOTO_DONE(SUCCEED) + + } else { + + HDassert( entry_ptr->addr == old_addr ); + HDassert( entry_ptr->type == type ); + HDassert( !(entry_ptr->is_protected) ); + + if ( entry_ptr->in_tree ) { + + HDassert( cache_ptr->tree_ptr ); + search_target.addr = old_addr; + + old_node_ptr = H5TB_dfind(cache_ptr->tree_ptr, + (void *)(&search_target), + NULL); + + HDassert( old_node_ptr != NULL ); + HDassert( old_node_ptr->key == (void *)entry_ptr ); + } + } + + H5C__SEARCH_INDEX(cache_ptr, new_addr, test_entry_ptr, FAIL) + + if ( test_entry_ptr != NULL ) { /* we are hosed */ + + if ( test_entry_ptr->type == type ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \ + "Target already renamed & reinserted???.") + + } else { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \ + "New address already in use?.") + } + } + + /* If we get this far, we have work to do. Remove *entry_ptr from + * the hash table (and tree if necessary), change its address to the + * new address, and then re-insert. + * + * Update the replacement policy for a hit to avoid an eviction before + * the renamed entry is touched. Update stats for a rename. + * + * Note that we do not check the size of the cache, or evict anything. + * Since this is a simple re-name, cache size should be unaffected. + */ + H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr) + + if ( old_node_ptr ) { + + H5C__REMOVE_ENTRY_FROM_TREE(cache_ptr, entry_ptr, old_node_ptr) + } + + entry_ptr->addr = new_addr; + + H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL) + + if ( entry_ptr->is_dirty ) { + + H5C__INSERT_ENTRY_IN_TREE(cache_ptr, entry_ptr) + } + + H5C__UPDATE_RP_FOR_RENAME(cache_ptr, entry_ptr, FAIL) + + H5C__UPDATE_STATS_FOR_RENAME(cache_ptr, entry_ptr) + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_rename_entry() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_protect + * + * Purpose: If the target entry is not in the cache, load it. If + * necessary, attempt to evict one or more entries to keep + * the cache within its maximum size. + * + * Mark the target entry as protected, and return its address + * to the caller. The caller must call H5C_unprotect() when + * finished with the entry. + * + * While it is protected, the entry may not be either evicted + * or flushed -- nor may it be accessed by another call to + * H5C_protect. Any attempt to do so will result in a failure. + * + * The primary_dxpl_id and secondary_dxpl_id parameters + * specify the dxpl_ids used on the first write occasioned + * by the insertion (primary_dxpl_id), and on all subsequent + * writes (secondary_dxpl_id). This is useful in the + * metadata cache, but may not be needed elsewhere. If so, + * just use the same dxpl_id for both parameters. + * + * All reads are performed with the primary_dxpl_id. + * + * Similarly, the primary_dxpl_id is passed to the + * check_write_permitted function if it is called. + * + * Return: Success: Ptr to the desired entry + * + * Failure: NULL + * + * Programmer: John Mainzer - 6/2/04 + * + * Modifications: + * + * JRM - 7/21/04 + * Updated for the addition of the hash table. + * + *------------------------------------------------------------------------- + */ + +void * +H5C_protect(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t addr, + const void * udata1, + void * udata2) +{ + hbool_t hit = FALSE; + void * thing = NULL; + H5C_cache_entry_t * entry_ptr; + void * ret_value; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_protect, NULL) + + /* check args */ + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( type ); + HDassert( type->flush ); + HDassert( type->load ); + HDassert( H5F_addr_defined(addr) ); + + /* first check to see if the target is in cache */ + H5C__SEARCH_INDEX(cache_ptr, addr, entry_ptr, NULL) + + if ( entry_ptr != NULL ) { + + hit = TRUE; + thing = (void *)entry_ptr; + + } else { /* must try to load the entry from disk. */ + + hit = FALSE; + thing = H5C_load_entry(f, primary_dxpl_id, type, addr, udata1, udata2, + cache_ptr->skip_file_checks); + + if ( thing == NULL ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "can't load entry") + } + + entry_ptr = (H5C_cache_entry_t *)thing; + + /* try to free up some space if necessay */ + if ( (cache_ptr->index_size + entry_ptr->size) > + cache_ptr->max_cache_size ) { + + hbool_t write_permitted = TRUE; + herr_t result; + size_t space_needed; + + if ( cache_ptr->check_write_permitted != NULL ) { + + result = (cache_ptr->check_write_permitted)(f, + primary_dxpl_id, + &write_permitted); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ + "Can't get write_permitted") + } + } + + HDassert( entry_ptr->size <= H5C_MAX_ENTRY_SIZE ); + + space_needed = (cache_ptr->index_size + entry_ptr->size) - + cache_ptr->max_cache_size; + + /* It would be nice to be able to do a tight sanity check on + * space_needed here, but it is hard to assign an upper bound on + * its value other than then value assigned to it. + * + * This fact springs from several features of the cache: + * + * First, it is possible for the cache to grow without + * bound as long as entries are protected and not unprotected. + * + * Second, when writes are not permitted it is also possible + * for the cache to grow without bound. + * + * Finally, we don't check to see if the cache is oversized + * at the end of an unprotect. As a result, it is possible + * to have a vastly oversized cache with no protected entries + * as long as all the protects preceed the unprotects. + * + * Since items 1 and 2 are not changing any time soon, I see + * no point in worrying about the third. + * + * In any case, I hope this explains why there is no sanity + * check on space_needed here. + */ + + result = H5C_make_space_in_cache(f, primary_dxpl_id, + secondary_dxpl_id, cache_ptr, + space_needed, write_permitted); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ + "H5C_make_space_in_cache failed.") + } + } + + /* Insert the entry in the hash table. It can't be dirty yet, so + * we don't even check to see if it should go in the tree. + */ + H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, NULL) + + /* insert the entry in the data structures used by the replacement + * policy. We are just going to take it out again when we update + * the replacement policy for a protect, but this simplifies the + * code. If we do this often enough, we may want to optimize this. + */ + H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, NULL) + } + + HDassert( entry_ptr->addr == addr ); + HDassert( entry_ptr->type == type ); + + if ( entry_ptr->is_protected ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, \ + "Target already protected?!?.") + } + + H5C__UPDATE_RP_FOR_PROTECT(cache_ptr, entry_ptr, NULL) + + entry_ptr->is_protected = TRUE; + + ret_value = thing; + + H5C__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit) + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_protect() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_unprotect + * + * Purpose: Undo an H5C_protect() call -- specifically, mark the + * entry as unprotected, remove it from the protected list, + * and give it back to the replacement policy. + * + * The TYPE and ADDR arguments must be the same as those in + * the corresponding call to H5C_protect() and the THING + * argument must be the value returned by that call to + * H5C_protect(). + * + * The primary_dxpl_id and secondary_dxpl_id parameters + * specify the dxpl_ids used on the first write occasioned + * by the unprotect (primary_dxpl_id), and on all subsequent + * writes (secondary_dxpl_id). Since an uprotect cannot + * occasion a write at present, all this is moot for now. + * However, things change, and in any case, + * H5C_flush_single_entry() needs primary_dxpl_id and + * secondary_dxpl_id in its parameter list. + * + * The function can't cause a read either, so the dxpl_id + * parameters are moot in this case as well. + * + * Return: Non-negative on success/Negative on failure + * + * If the deleted flag is TRUE, simply remove the target entry + * from the cache, clear it, and free it without writing it to + * disk. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 6/2/04 + * + * Modifications: + * + * JRM - 7/21/04 + * Updated the function for the addition of the hash table. + * In particular, we now add dirty entries to the tree if + * they aren't in the tree already. + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_unprotect(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t addr, + void * thing, + hbool_t deleted) +{ + herr_t ret_value = SUCCEED; /* Return value */ + H5C_cache_entry_t * entry_ptr; + H5C_cache_entry_t * test_entry_ptr; + + FUNC_ENTER_NOAPI(H5C_unprotect, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( type ); + HDassert( type->clear ); + HDassert( type->flush ); + HDassert( H5F_addr_defined(addr) ); + HDassert( thing ); + + entry_ptr = (H5C_cache_entry_t *)thing; + + HDassert( entry_ptr->addr == addr ); + HDassert( entry_ptr->type == type ); + + if ( ! (entry_ptr->is_protected) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ + "Entry already unprotected??") + } + + H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, entry_ptr, FAIL) + + entry_ptr->is_protected = FALSE; + + /* add the entry to the tree if it is dirty, and it isn't already in + * the tree. + */ + + if ( ( entry_ptr->is_dirty ) && ( ! (entry_ptr->in_tree) ) ) { + + H5C__INSERT_ENTRY_IN_TREE(cache_ptr, entry_ptr) + } + + /* this implementation of the "deleted" option is a bit inefficient, as + * we re-insert the entry to be deleted into the replacement policy + * data structures, only to remove them again. Depending on how often + * we do this, we may want to optimize a bit. + * + * On the other hand, this implementation is reasonably clean, and + * makes good use of existing code. + * JRM - 5/19/04 + */ + if ( deleted ) { + + /* the following first flush flag will never be used as we are + * calling H5C_flush_single_entry with both the H5F_FLUSH_CLEAR_ONLY + * and H5F_FLUSH_INVALIDATE flags. However, it is needed for the + * function call. + */ + hbool_t dummy_first_flush = TRUE; + + /* verify that the target entry is in the cache. */ + + H5C__SEARCH_INDEX(cache_ptr, addr, test_entry_ptr, FAIL) + + if ( test_entry_ptr == NULL ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ + "entry not in hash table?!?.") + } + else if ( test_entry_ptr != entry_ptr ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ + "hash table contains multiple entries for addr?!?.") + } + + if ( H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + type, + addr, + (H5F_FLUSH_CLEAR_ONLY|H5F_FLUSH_INVALIDATE), + NULL, + &dummy_first_flush, + TRUE) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "Can't flush.") + } + } + + H5C__UPDATE_STATS_FOR_UNPROTECT(cache_ptr) + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_unprotect() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_stats + * + * Purpose: Prints statistics about the cache. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 6/2/04 + * + * Modifications: + * + * JRM -- 7/21/04 + * Updated function for the addition of the hash table. + * + *------------------------------------------------------------------------- + */ + +herr_t +H5C_stats(H5C_t * cache_ptr, + const char * cache_name, + hbool_t +#if !H5C_COLLECT_CACHE_STATS + UNUSED +#endif /* H5C_COLLECT_CACHE_STATS */ + display_detailed_stats) +{ + herr_t ret_value = SUCCEED; /* Return value */ + +#if H5C_COLLECT_CACHE_STATS + int i; + int64_t total_hits = 0; + int64_t total_misses = 0; + int64_t total_insertions = 0; + int64_t total_clears = 0; + int64_t total_flushes = 0; + int64_t total_evictions = 0; + int64_t total_renames = 0; + int32_t aggregate_max_accesses = 0; + int32_t aggregate_min_accesses = 1000000; + int32_t aggregate_max_clears = 0; + int32_t aggregate_max_flushes = 0; + size_t aggregate_max_size = 0; + double hit_rate; + double average_successful_search_depth = 0.0; + double average_failed_search_depth = 0.0; +#endif /* H5C_COLLECT_CACHE_STATS */ + + FUNC_ENTER_NOAPI(H5C_stats, FAIL) + + /* This would normally be an assert, but we need to use an HGOTO_ERROR + * call to shut up the compiler. + */ + if ( ( ! cache_ptr ) || + ( cache_ptr->magic != H5C__H5C_T_MAGIC ) || + ( !cache_name ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr or cache_name") + } + +#if H5C_COLLECT_CACHE_STATS + + for ( i = 0; i <= cache_ptr->max_type_id; i++ ) { + + total_hits += cache_ptr->hits[i]; + total_misses += cache_ptr->misses[i]; + total_insertions += cache_ptr->insertions[i]; + total_clears += cache_ptr->clears[i]; + total_flushes += cache_ptr->flushes[i]; + total_evictions += cache_ptr->evictions[i]; + total_renames += cache_ptr->renames[i]; +#if H5C_COLLECT_CACHE_ENTRY_STATS + if ( aggregate_max_accesses < cache_ptr->max_accesses[i] ) + aggregate_max_accesses = cache_ptr->max_accesses[i]; + if ( aggregate_min_accesses > aggregate_max_accesses ) + aggregate_min_accesses = aggregate_max_accesses; + if ( aggregate_min_accesses > cache_ptr->min_accesses[i] ) + aggregate_min_accesses = cache_ptr->min_accesses[i]; + if ( aggregate_max_clears < cache_ptr->max_clears[i] ) + aggregate_max_clears = cache_ptr->max_clears[i]; + if ( aggregate_max_flushes < cache_ptr->max_flushes[i] ) + aggregate_max_flushes = cache_ptr->max_flushes[i]; + if ( aggregate_max_size < cache_ptr->max_size[i] ) + aggregate_max_size = cache_ptr->max_size[i]; +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ + } + + if ( ( total_hits > 0 ) || ( total_misses > 0 ) ) { + + hit_rate = 100.0 * ((double)(total_hits)) / + ((double)(total_hits + total_misses)); + } else { + hit_rate = 0.0; + } + + if ( cache_ptr->successful_ht_searches > 0 ) { + + average_successful_search_depth = + ((double)(cache_ptr->total_successful_ht_search_depth)) / + ((double)(cache_ptr->successful_ht_searches)); + } + + if ( cache_ptr->failed_ht_searches > 0 ) { + + average_failed_search_depth = + ((double)(cache_ptr->total_failed_ht_search_depth)) / + ((double)(cache_ptr->failed_ht_searches)); + } + + + HDfprintf(stdout, "\nH5C: cache statistics for %s\n", + cache_name); + + HDfprintf(stdout, "\n"); + + HDfprintf(stdout, + " hash table insertion / deletions = %ld / %ld\n", + (long)(cache_ptr->total_ht_insertions), + (long)(cache_ptr->total_ht_deletions)); + + HDfprintf(stdout, + " HT successful / failed searches = %ld / %ld\n", + (long)(cache_ptr->successful_ht_searches), + (long)(cache_ptr->failed_ht_searches)); + + HDfprintf(stdout, + " Av. HT suc / failed search depth = %f / %f\n", + average_successful_search_depth, + average_failed_search_depth); + + HDfprintf(stdout, + " current (max) index size / length = %ld (%ld) / %ld (%ld)\n", + (long)(cache_ptr->index_size), + (long)(cache_ptr->max_index_size), + (long)(cache_ptr->index_len), + (long)(cache_ptr->max_index_len)); + + HDfprintf(stdout, + " current (max) tree size / length = %ld (%ld) / %ld (%ld)\n", + (long)(cache_ptr->tree_size), + (long)(cache_ptr->max_tree_size), + (long)(cache_ptr->tree_len), + (long)(cache_ptr->max_tree_len)); + + HDfprintf(stdout, + " current (max) PL size / length = %ld (%ld) / %ld (%ld)\n", + (long)(cache_ptr->pl_size), + (long)(cache_ptr->max_pl_size), + (long)(cache_ptr->pl_len), + (long)(cache_ptr->max_pl_len)); + + HDfprintf(stdout, + " current LRU list size / length = %ld / %ld\n", + (long)(cache_ptr->LRU_list_size), + (long)(cache_ptr->LRU_list_len)); + + HDfprintf(stdout, + " current clean LRU size / length = %ld / %ld\n", + (long)(cache_ptr->cLRU_list_size), + (long)(cache_ptr->cLRU_list_len)); + + HDfprintf(stdout, + " current dirty LRU size / length = %ld / %ld\n", + (long)(cache_ptr->dLRU_list_size), + (long)(cache_ptr->dLRU_list_len)); + + HDfprintf(stdout, + " Total hits / misses / hit_rate = %ld / %ld / %f\n", + (long)total_hits, + (long)total_misses, + hit_rate); + + HDfprintf(stdout, + " Total clears / flushes / evictions = %ld / %ld / %ld\n", + (long)total_clears, + (long)total_flushes, + (long)total_evictions); + + HDfprintf(stdout, " Total insertions / renames = %ld / %ld\n", + (long)total_insertions, + (long)total_renames); + +#if H5C_COLLECT_CACHE_ENTRY_STATS + + HDfprintf(stdout, " aggregate max / min accesses = %d / %d\n", + (int)aggregate_max_accesses, + (int)aggregate_min_accesses); + + HDfprintf(stdout, " aggregate max_clears / max_flushes = %d / %d\n", + (int)aggregate_max_clears, + (int)aggregate_max_flushes); + + HDfprintf(stdout, " aggregate max_size = %d\n", + (int)aggregate_max_size); + + +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ + + if ( display_detailed_stats ) + { + + for ( i = 0; i <= cache_ptr->max_type_id; i++ ) { + + HDfprintf(stdout, "\n"); + + HDfprintf(stdout, " Stats on %s:\n", + ((cache_ptr->type_name_table_ptr))[i]); + + if ( ( cache_ptr->hits[i] > 0 ) || ( cache_ptr->misses[i] > 0 ) ) { + + hit_rate = 100.0 * ((double)(cache_ptr->hits[i])) / + ((double)(cache_ptr->hits[i] + cache_ptr->misses[i])); + } else { + hit_rate = 0.0; + } + + HDfprintf(stdout, + " hits / misses / hit_rate = %ld / %ld / %f\n", + (long)(cache_ptr->hits[i]), + (long)(cache_ptr->misses[i]), + hit_rate); + + HDfprintf(stdout, + " clears / flushes / evictions = %ld / %ld / %ld\n", + (long)(cache_ptr->clears[i]), + (long)(cache_ptr->flushes[i]), + (long)(cache_ptr->evictions[i])); + + HDfprintf(stdout, + " insertions / renames = %ld / %ld\n", + (long)(cache_ptr->insertions[i]), + (long)(cache_ptr->renames[i])); + +#if H5C_COLLECT_CACHE_ENTRY_STATS + + HDfprintf(stdout, + " entry max / min accesses = %d / %d\n", + cache_ptr->max_accesses[i], + cache_ptr->min_accesses[i]); + + HDfprintf(stdout, + " entry max_clears / max_flushes = %d / %d\n", + cache_ptr->max_clears[i], + cache_ptr->max_flushes[i]); + + HDfprintf(stdout, + " entry max_size = %d\n", + (int)(cache_ptr->max_size[i])); + + +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ + + } + } + + HDfprintf(stdout, "\n"); + +#endif /* H5C_COLLECT_CACHE_STATS */ + +done: + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_stats() */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_stats__reset + * + * Purpose: Reset the stats fields to their initial values. + * + * Return: void + * + * Programmer: John Mainzer, 4/28/04 + * + * Modifications: + * + * JRM - 7/21/04 + * Updated for hash table related statistics. + * + *------------------------------------------------------------------------- + */ + +void +H5C_stats__reset(H5C_t * cache_ptr) +{ +#if H5C_COLLECT_CACHE_STATS + int i; +#endif /* H5C_COLLECT_CACHE_STATS */ + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + +#if H5C_COLLECT_CACHE_STATS + for ( i = 0; i <= cache_ptr->max_type_id; i++ ) + { + cache_ptr->hits[i] = 0; + cache_ptr->misses[i] = 0; + cache_ptr->insertions[i] = 0; + cache_ptr->clears[i] = 0; + cache_ptr->flushes[i] = 0; + cache_ptr->evictions[i] = 0; + cache_ptr->renames[i] = 0; + } + + cache_ptr->total_ht_insertions = 0; + cache_ptr->total_ht_deletions = 0; + cache_ptr->successful_ht_searches = 0; + cache_ptr->total_successful_ht_search_depth = 0; + cache_ptr->failed_ht_searches = 0; + cache_ptr->total_failed_ht_search_depth = 0; + + cache_ptr->max_index_len = 0; + cache_ptr->max_index_size = (size_t)0; + + cache_ptr->max_tree_len = 0; + cache_ptr->max_tree_size = (size_t)0; + + cache_ptr->max_pl_len = 0; + cache_ptr->max_pl_size = (size_t)0; + +#if H5C_COLLECT_CACHE_ENTRY_STATS + + for ( i = 0; i <= cache_ptr->max_type_id; i++ ) + { + cache_ptr->max_accesses[i] = 0; + cache_ptr->min_accesses[i] = 1000000; + cache_ptr->max_clears[i] = 0; + cache_ptr->max_flushes[i] = 0; + cache_ptr->max_size[i] = (size_t)0; + } + +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ +#endif /* H5C_COLLECT_CACHE_STATS */ + + return; + +} /* H5C_stats__reset() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_set_skip_flags + * + * Purpose: Set the values of the skip sanity check flags. + * + * This function and the skip sanity check flags were created + * for the convenience of the test bed. However it is + * possible that there may be other uses for the flags. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 6/11/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +herr_t +H5C_set_skip_flags(H5C_t * cache_ptr, + hbool_t skip_file_checks, + hbool_t skip_dxpl_id_checks) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5C_set_skip_flags, FAIL) + + /* This would normally be an assert, but we need to use an HGOTO_ERROR + * call to shut up the compiler. + */ + if ( ( ! cache_ptr ) || ( cache_ptr->magic != H5C__H5C_T_MAGIC ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr") + } + + cache_ptr->skip_file_checks = skip_file_checks; + cache_ptr->skip_dxpl_id_checks = skip_dxpl_id_checks; + +done: + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_set_skip_flags() */ + + +/*************************************************************************/ +/**************************** Private Functions: *************************/ +/*************************************************************************/ + +/*------------------------------------------------------------------------- + * + * Function: H5C_flush_single_entry + * + * Purpose: Flush or clear (and evict if requested) the cache entry + * with the specified address and type. If the type is NULL, + * any unprotected entry at the specified address will be + * flushed (and possibly evicted). + * + * Attempts to flush a protected entry will result in an + * error. + * + * *first_flush_ptr should be true if only one + * flush is contemplated before the next load, or if this + * is the first of a sequence of flushes that will be + * completed before the next load. *first_flush_ptr is set + * to false if a flush actually takes place, and should be + * left false until the end of the sequence. + * + * The primary_dxpl_id is used if *first_flush_ptr is TRUE + * on entry, and a flush actually takes place. The + * secondary_dxpl_id is used in any subsequent flush where + * *first_flush_ptr is FALSE on entry. + * + * If the H5F_FLUSH_CLEAR_ONLY flag is set, the entry will + * be cleared and not flushed -- in the case *first_flush_ptr, + * primary_dxpl_id, and secondary_dxpl_id are all irrelevent, + * and the call can't be part of a sequence of flushes. + * + * If the caller knows the address of the TBBT node at + * which the target entry resides, it can avoid a lookup + * by supplying that address in the tgt_node_ptr parameter. + * If this parameter is NULL, the function will do a TBBT + * search for the entry instead. + * + * The function does nothing silently if there is no entry + * at the supplied address, or if the entry found has the + * wrong type. + * + * Return: Non-negative on success/Negative on failure or if there was + * an attempt to flush a protected item. + * + * Programmer: John Mainzer, 5/5/04 + * + * Modifications: + * + * JRM -- 7/21/04 + * Updated function for the addition of the hash table. + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C_flush_single_entry(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type_ptr, + haddr_t addr, + unsigned flags, + H5TB_NODE * tgt_node_ptr, + hbool_t * first_flush_ptr, + hbool_t del_entry_from_tree_on_destroy) +{ + hbool_t destroy = ( (flags & H5F_FLUSH_INVALIDATE) != 0 ); + hbool_t clear_only = ( (flags & H5F_FLUSH_CLEAR_ONLY) != 0); + herr_t ret_value = SUCCEED; /* Return value */ + herr_t status; + H5TB_NODE * node_ptr; + H5C_cache_entry_t * entry_ptr = NULL; + H5C_cache_entry_t search_target; + + FUNC_ENTER_NOAPI_NOINIT(H5C_flush_single_entry) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( H5F_addr_defined(addr) ); + HDassert( first_flush_ptr ); + + /* attempt to find the target entry in the hash table */ + H5C__SEARCH_INDEX(cache_ptr, addr, entry_ptr, FAIL) + + /* If tgt_node_ptr is NULL, look up the target entry in the tree. */ + + if ( tgt_node_ptr == NULL ) { + + HDassert( cache_ptr->tree_ptr ); + search_target.addr = addr; + + node_ptr = H5TB_dfind(cache_ptr->tree_ptr, + (void *)&search_target, + NULL); + + } else { + + node_ptr = tgt_node_ptr; + } + +#if H5C_DO_SANITY_CHECKS + if ( node_ptr != NULL ) { + + if ( ( entry_ptr == NULL ) || + ( entry_ptr != (H5C_cache_entry_t *)(node_ptr->data) ) || + ( ! (entry_ptr->in_tree) ) || + ( entry_ptr->addr != addr ) || + ( node_ptr->data != node_ptr->key ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "Hash table and tree out of sync.") + } + } else if ( entry_ptr != NULL ) { + + if ( ( entry_ptr->in_tree ) || + ( entry_ptr->is_dirty ) || + ( entry_ptr->addr != addr ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "entry failed sanity checks.") + } + } +#endif /* H5C_DO_SANITY_CHECKS */ + + if ( ( entry_ptr != NULL ) && ( entry_ptr->is_protected ) ) + { + /* Attempt to flush a protected entry -- scream and die. */ + HGOTO_ERROR(H5E_CACHE, H5E_PROTECT, FAIL, \ + "Attempt to flush a protected entry.") + } + + if ( ( entry_ptr != NULL ) && + ( ( type_ptr == NULL ) || ( type_ptr->id == entry_ptr->type->id ) ) ) + { + /* we have work to do */ + +#ifdef H5_HAVE_PARALLEL +#ifndef NDEBUG + + /* If MPI based VFD is used, do special parallel I/O sanity checks. + * Note that we only do these sanity checks when the clear_only flag + * is not set, and the entry to be flushed is dirty. Don't bother + * otherwise as no file I/O can result. + * + * There are also cases (testing for instance) where it is convenient + * to pass in dummy dxpl_ids. Since we don't use the dxpl_ids directly, + * this isn't a problem -- but we do have to turn off sanity checks + * involving them. We use cache_ptr->skip_dxpl_id_checks to do this. + */ + if ( ( ! cache_ptr->skip_dxpl_id_checks ) && + ( ! clear_only ) && + ( entry_ptr->is_dirty ) && + ( IS_H5FD_MPI(f) ) ) { + + H5P_genplist_t *dxpl; /* Dataset transfer property list */ + H5FD_mpio_xfer_t xfer_mode; /* I/O xfer mode property value */ + + /* Get the dataset transfer property list */ + if ( NULL == (dxpl = H5I_object(primary_dxpl_id)) ) { + + HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, \ + "not a dataset creation property list") + } + + /* Get the transfer mode property */ + if( H5P_get(dxpl, H5D_XFER_IO_XFER_MODE_NAME, &xfer_mode) < 0 ) { + + HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, \ + "can't retrieve xfer mode") + } + + /* Sanity check transfer mode */ + HDassert( xfer_mode == H5FD_MPIO_COLLECTIVE || IS_H5FD_FPHDF5(f) ); + } + +#endif /* NDEBUG */ +#endif /* H5_HAVE_PARALLEL */ + + if ( clear_only ) { + H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) + } else { + H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) + } + + if ( destroy ) { + H5C__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr) + } + + /* Always remove the entry from the hash table on a destroy. On a + * flush with destroy, it is cheaper to discard the tree all at once + * rather than remove the entries one by one, so we only delete from + * the tree if requested. + * + * We must do deletions now as the callback routines will free the + * entry if destroy is true. + */ + if ( destroy ) { + + H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr) + + if ( ( node_ptr ) && ( del_entry_from_tree_on_destroy ) ) { + + H5C__REMOVE_ENTRY_FROM_TREE(cache_ptr, entry_ptr, node_ptr) + } + } + + /* Update the replacement policy for the flush or eviction. + * Again, do this now so we don't have to reference freed + * memory in the destroy case. + */ + if ( destroy ) { /* AKA eviction */ + + H5C__UPDATE_RP_FOR_EVICTION(cache_ptr, entry_ptr, FAIL) + + } else { + + H5C__UPDATE_RP_FOR_FLUSH(cache_ptr, entry_ptr, FAIL) + } + + /* Clear the dirty flag only, if requested */ + if ( clear_only ) { + /* Call the callback routine to clear all dirty flags for object */ + if ( (entry_ptr->type->clear)(f, entry_ptr, destroy) < 0 ) { + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "can't clear entry") + } + } else { + + /* Only block for all the processes on the first piece of metadata + */ + + if ( *first_flush_ptr && entry_ptr->is_dirty ) { + status = (entry_ptr->type->flush)(f, primary_dxpl_id, destroy, + entry_ptr->addr, entry_ptr); + *first_flush_ptr = FALSE; + } else { + status = (entry_ptr->type->flush)(f, secondary_dxpl_id, + destroy, entry_ptr->addr, + entry_ptr); + } + + if ( status < 0 ) { + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "unable to flush entry") + } + } + + if ( ! destroy ) { + + HDassert( !(entry_ptr->is_dirty) ); + } + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_flush_single_entry() */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_load_entry + * + * Purpose: Attempt to load the entry at the specified disk address + * and with the specified type into memory. If successful. + * return the in memory address of the entry. Return NULL + * on failure. + * + * Note that this function simply loads the entry into + * core. It does not insert it into the cache. + * + * Return: Non-NULL on success / NULL on failure. + * + * Programmer: John Mainzer, 5/18/04 + * + * Modifications: + * + * JRM - 7/21/04 + * Updated function for the addition of the hash table. + * + *------------------------------------------------------------------------- + */ + +static void * +H5C_load_entry(H5F_t * f, + hid_t dxpl_id, + const H5C_class_t * type, + haddr_t addr, + const void * udata1, + void * udata2, + hbool_t skip_file_checks) +{ + void * thing = NULL; + void * ret_value = NULL; + H5C_cache_entry_t * entry_ptr = NULL; + + FUNC_ENTER_NOAPI_NOINIT(H5C_load_entry) + + HDassert( skip_file_checks || f ); + HDassert( type ); + HDassert( type->load ); + HDassert( type->size ); + HDassert( H5F_addr_defined(addr) ); + + if ( NULL == (thing = (type->load)(f, dxpl_id, addr, udata1, udata2)) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "unable to load entry") + + } + + entry_ptr = (H5C_cache_entry_t *)thing; + + HDassert( entry_ptr->is_dirty == FALSE ); + + entry_ptr->addr = addr; + entry_ptr->type = type; + entry_ptr->is_protected = FALSE; + entry_ptr->in_tree = FALSE; + + if ( (type->size)(f, thing, &(entry_ptr->size)) < 0 ) { + + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGETSIZE, NULL, \ + "Can't get size of thing") + } + + HDassert( entry_ptr->size < H5C_MAX_ENTRY_SIZE ); + + entry_ptr->ht_next = NULL; + entry_ptr->ht_prev = NULL; + + entry_ptr->next = NULL; + entry_ptr->prev = NULL; + + entry_ptr->aux_next = NULL; + entry_ptr->aux_prev = NULL; + + H5C__RESET_CACHE_ENTRY_STATS(entry_ptr); + + ret_value = thing; + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_load_entry() */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_make_space_in_cache + * + * Purpose: Attempt to evict cache entries until the index_size + * is at least needed_space below max_cache_size. + * + * In passing, also attempt to bring cLRU_list_size to a + * value greater than min_clean_size. + * + * Depending on circumstances, both of these goals may + * be impossible, as in parallel mode, we must avoid generating + * a write as part of a read (to avoid deadlock in collective + * I/O), and in all cases, it is possible (though hopefully + * highly unlikely) that the protected list may exceed the + * maximum size of the cache. + * + * Thus the function simply does its best, returning success + * unless an error is encountered. + * + * The primary_dxpl_id and secondary_dxpl_id parameters + * specify the dxpl_ids used on the first write occasioned + * by the call (primary_dxpl_id), and on all subsequent + * writes (secondary_dxpl_id). This is useful in the metadata + * cache, but may not be needed elsewhere. If so, just use the + * same dxpl_id for both parameters. + * + * Observe that this function cannot occasion a read. + * + * Return: Non-negative on success/Negative on failure. + * + * Programmer: John Mainzer, 5/14/04 + * + * Modifications: + * + * JRM --7/21/04 + * Minor modifications in support of the addition of a hash + * table to facilitate lookups. + * + *------------------------------------------------------------------------- + */ + +static herr_t +H5C_make_space_in_cache(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + size_t space_needed, + hbool_t write_permitted) +{ + hbool_t first_flush = TRUE; + herr_t ret_value = SUCCEED; /* Return value */ + herr_t result; + int32_t entries_examined = 0; + int32_t initial_list_len; + H5C_cache_entry_t * entry_ptr; + H5C_cache_entry_t * prev_ptr; + + FUNC_ENTER_NOAPI_NOINIT(H5C_make_space_in_cache) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + + if ( write_permitted ) { + + initial_list_len = cache_ptr->LRU_list_len; + entry_ptr = cache_ptr->LRU_tail_ptr; + + while ( ( (cache_ptr->index_size + space_needed) + > + cache_ptr->max_cache_size + ) + && + ( entries_examined <= (2 * initial_list_len) ) + && + ( entry_ptr != NULL ) + ) + { + HDassert( ! (entry_ptr->is_protected) ); + + prev_ptr = entry_ptr->prev; + + if ( entry_ptr->is_dirty ) { + + result = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + entry_ptr->type, + entry_ptr->addr, + (unsigned)0, + NULL, + &first_flush, + FALSE); + } else { + + result = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + entry_ptr->type, + entry_ptr->addr, + H5F_FLUSH_INVALIDATE, + NULL, + &first_flush, + TRUE); + } + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "unable to flush entry") + } + + entry_ptr = prev_ptr; + } + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + + initial_list_len = cache_ptr->dLRU_list_len; + entry_ptr = cache_ptr->dLRU_tail_ptr; + + while ( ( cache_ptr->cLRU_list_size < cache_ptr->min_clean_size ) && + ( entries_examined <= initial_list_len ) && + ( entry_ptr != NULL ) + ) + { + HDassert( ! (entry_ptr->is_protected) ); + HDassert( entry_ptr->is_dirty ); + HDassert( entry_ptr->in_tree ); + + prev_ptr = entry_ptr->aux_prev; + + result = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + entry_ptr->type, + entry_ptr->addr, + (unsigned)0, + NULL, + &first_flush, + FALSE); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "unable to flush entry") + } + + entry_ptr = prev_ptr; + } + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + } else { + + HDassert( H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS ); + + initial_list_len = cache_ptr->cLRU_list_len; + entry_ptr = cache_ptr->cLRU_tail_ptr; + + while ( ( (cache_ptr->index_size + space_needed) + > + cache_ptr->max_cache_size + ) + && + ( entries_examined <= initial_list_len ) + && + ( entry_ptr != NULL ) + ) + { + HDassert( ! (entry_ptr->is_protected) ); + HDassert( ! (entry_ptr->is_dirty) ); + + prev_ptr = entry_ptr->aux_prev; + + result = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + entry_ptr->type, + entry_ptr->addr, + H5F_FLUSH_INVALIDATE, + NULL, + &first_flush, + TRUE); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "unable to flush entry") + } + + entry_ptr = prev_ptr; + } + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_make_space_in_cache() */ diff --git a/src/H5Cprivate.h b/src/H5Cprivate.h new file mode 100644 index 0000000..4c14c4c --- /dev/null +++ b/src/H5Cprivate.h @@ -0,0 +1,421 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by the Board of Trustees of the University of Illinois. * + * 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 files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*------------------------------------------------------------------------- + * + * Created: H5Cprivate.h + * 6/3/04 + * John Mainzer + * + * Purpose: Constants and typedefs available to the rest of the + * library. + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +#ifndef _H5Cprivate_H +#define _H5Cprivate_H + +#include "H5Cpublic.h" /*public prototypes */ + +/* Pivate headers needed by this header */ +#include "H5private.h" /* Generic Functions */ +#include "H5Fprivate.h" /* File access */ + +#define H5C_DO_SANITY_CHECKS 0 + +/* This sanity checking constant was picked out of the air. Increase + * or decrease it if appropriate. Its purposes is to detect corrupt + * object sizes, so it probably doesn't matter if it is a bit big. + * + * JRM - 5/17/04 + */ +#define H5C_MAX_ENTRY_SIZE ((size_t)(10 * 1024 * 1024)) + +/* H5C_COLLECT_CACHE_STATS controls overall collection of statistics + * on cache activity. In general, this #define should be set to 0. + */ +#define H5C_COLLECT_CACHE_STATS 0 + +/* H5C_COLLECT_CACHE_ENTRY_STATS controls collection of statistics + * in individual cache entries. + * + * H5C_COLLECT_CACHE_ENTRY_STATS should only be defined to true if + * H5C_COLLECT_CACHE_STATS is also defined to true. + */ +#if H5C_COLLECT_CACHE_STATS + +#define H5C_COLLECT_CACHE_ENTRY_STATS 1 + +#else + +#define H5C_COLLECT_CACHE_ENTRY_STATS 0 + +#endif /* H5C_COLLECT_CACHE_STATS */ + + +#ifdef H5_HAVE_PARALLEL + +/* we must maintain the clean and dirty LRU lists when we are compiled + * with parallel support. + */ +#define H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS 1 + +#else /* H5_HAVE_PARALLEL */ + +/* The clean and dirty LRU lists don't buy us anything here -- we may + * want them on for testing on occasion, but in general they should be + * off. + */ +#define H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS 0 + +#endif /* H5_HAVE_PARALLEL */ + + +/* + * Class methods pertaining to caching. Each type of cached object will + * have a constant variable with permanent life-span that describes how + * to cache the object. That variable will be of type H5C_class_t and + * have the following required fields... + * + * LOAD: Loads an object from disk to memory. The function + * should allocate some data structure and return it. + * + * FLUSH: Writes some data structure back to disk. It would be + * wise for the data structure to include dirty flags to + * indicate whether it really needs to be written. This + * function is also responsible for freeing memory allocated + * by the LOAD method if the DEST argument is non-zero (by + * calling the DEST method). + * + * DEST: Just frees memory allocated by the LOAD method. + * + * CLEAR: Just marks object as non-dirty. + * + * SIZE: Report the size (on disk) of the specified cache object. + * Note that the space allocated on disk may not be contiguous. + */ + +typedef void *(*H5C_load_func_t)(H5F_t *f, + hid_t dxpl_id, + haddr_t addr, + const void *udata1, + void *udata2); +typedef herr_t (*H5C_flush_func_t)(H5F_t *f, + hid_t dxpl_id, + hbool_t dest, + haddr_t addr, + void *thing); +typedef herr_t (*H5C_dest_func_t)(H5F_t *f, + void *thing); +typedef herr_t (*H5C_clear_func_t)(H5F_t *f, + void *thing, + hbool_t dest); +typedef herr_t (*H5C_size_func_t)(H5F_t *f, + void *thing, + size_t *size_ptr); + +typedef struct H5C_class_t { + int id; + H5C_load_func_t load; + H5C_flush_func_t flush; + H5C_dest_func_t dest; + H5C_clear_func_t clear; + H5C_size_func_t size; +} H5C_class_t; + + +/* Type defintions of call back functions used by the cache as a whole */ + +typedef herr_t (*H5C_write_permitted_func_t)(H5F_t *f, + hid_t dxpl_id, + hbool_t * write_permitted_ptr); + + +/* Default max cache size and min clean size are give here to make + * them generally accessable. + */ + +#define H5C__DEFAULT_MAX_CACHE_SIZE ((size_t)(4 * 1024 * 1024)) +#define H5C__DEFAULT_MIN_CLEAN_SIZE ((size_t)(2 * 1024 * 1024)) + + +/**************************************************************************** + * + * structure H5C_cache_entry_t + * + * Instances of the H5C_cache_entry_t structure are used to store cache + * entries in a hash table and sometimes in a threaded balanced binary tree. + * See H5TB.c for the particulars of the tree. + * + * In typical application, this structure is the first field in a + * structure to be cached. For historical reasons, the external module + * is responsible for managing the is_dirty field. All other fields are + * managed by the cache. + * + * Note that our current implementation of a threaded balanced binary tree + * will occasionaly change the node a particular datum is associated with. + * Thus this structure does not have a back pointer to its tree node. If we + * ever modify the threaded balanced binary tree code to fix this, a back + * pointer would save us a few tree traversals. + * + * The fields of this structure are discussed individually below: + * + * JRM - 4/26/04 + * + * addr: Base address of the cache entry on disk. + * + * size: Length of the cache entry on disk. Note that unlike normal + * caches, the entries in this cache are of variable length. + * The entries should never overlap, and when we do writebacks, + * we will want to writeback adjacent entries where possible. + * + * type: Pointer to the instance of H5C_class_t containing pointers + * to the methods for cache entries of the current type. This + * field should be NULL when the instance of H5C_cache_entry_t + * is not in use. + * + * The name is not particularly descriptive, but is retained + * to avoid changes in existing code. + * + * is_dirty: Boolean flag indicating whether the contents of the cache + * entry has been modified since the last time it was written + * to disk. + * + * NOTE: For historical reasons, this field is not maintained + * by the cache. Instead, the module using the cache + * sets this flag when it modifies the entry, and the + * flush and clear functions supplied by that module + * reset the dirty when appropriate. + * + * This is a bit quirky, so we may want to change this + * someday. However it will require a change in the + * cache interface. + * + * is_protected: Boolean flag indicating whether this entry is protected + * (or locked, to use more conventional terms). When it is + * protected, the entry cannot be flushed or accessed until + * it is unprotected (or unlocked -- again to use more + * conventional terms). + * + * Note that protected entries are removed from the LRU lists + * and inserted on the protected list. + * + * in_tree: Boolean flag indicating whether the entry is in the threaded + * balanced binary tree. As a general rule, entries are placed + * in the tree when they are marked dirty. However they may + * remain in the tree after being flushed. + * + * + * Fields supporting the hash table: + * + * Fields in the cache are indexed by a more or less conventional hash table. + * If there are multiple entries in any hash bin, they are stored in a doubly + * linked list. + * + * ht_next: Next pointer used by the hash table to store multiple + * entries in a single hash bin. This field points to the + * next entry in the doubly linked list of entries in the + * hash bin, or NULL if there is no next entry. + * + * ht_prev: Prev pointer used by the hash table to store multiple + * entries in a single hash bin. This field points to the + * previous entry in the doubly linked list of entries in + * the hash bin, or NULL if there is no previuos entry. + * + * + * Fields supporting replacement policies: + * + * The cache must have a replacement policy, and it will usually be + * necessary for this structure to contain fields supporting that policy. + * + * While there has been interest in several replacement policies for + * this cache, the initial development schedule is tight. Thus I have + * elected to support only a modified LRU policy for the first cut. + * + * When additional replacement policies are added, the fields in this + * section will be used in different ways or not at all. Thus the + * documentation of these fields is repeated for each replacement policy. + * + * Modified LRU: + * + * When operating in parallel mode, we must ensure that a read does not + * cause a write. If it does, the process will hang, as the write will + * be collective and the other processes will not know to participate. + * + * To deal with this issue, I have modified the usual LRU policy by adding + * clean and dirty LRU lists to the usual LRU list. When reading in + * parallel mode, we evict from the clean LRU list only. This implies + * that we must try to ensure that the clean LRU list is reasonably well + * stocked. See the comments on H5C_t in H5C.c for more details. + * + * Note that even if we start with a completely clean cache, a sequence + * of protects without unprotects can empty the clean LRU list. In this + * case, the cache must grow temporarily. At the next write, we will + * attempt to evict enough entries to get the cache down to its nominal + * maximum size. + * + * The use of the replacement policy fields under the Modified LRU policy + * is discussed below: + * + * next: Next pointer in either the LRU or the protected list, + * depending on the current value of protected. If there + * is no next entry on the list, this field should be set + * to NULL. + * + * prev: Prev pointer in either the LRU or the protected list, + * depending on the current value of protected. If there + * is no previous entry on the list, this field should be + * set to NULL. + * + * aux_next: Next pointer on either the clean or dirty LRU lists. + * This entry should be NULL when protected is true. When + * protected is false, and dirty is true, it should point + * to the next item on the dirty LRU list. When protected + * is false, and dirty is false, it should point to the + * next item on the clean LRU list. In either case, when + * there is no next item, it should be NULL. + * + * aux_prev: Previous pointer on either the clean or dirty LRU lists. + * This entry should be NULL when protected is true. When + * protected is false, and dirty is true, it should point + * to the previous item on the dirty LRU list. When protected + * is false, and dirty is false, it should point to the + * previous item on the clean LRU list. In either case, when + * there is no previous item, it should be NULL. + * + * + * Cache entry stats collection fields: + * + * These fields should only be compiled in when both H5C_COLLECT_CACHE_STATS + * and H5C_COLLECT_CACHE_ENTRY_STATS are true. When present, they allow + * collection of statistics on individual cache entries. + * + * accesses: int32_t containing the number of times this cache entry has + * been referenced in its lifetime. + * + * clears: int32_t containing the number of times this cache entry has + * been cleared in its life time. + * + * flushes: int32_t containing the number of times this cache entry has + * been flushed to file in its life time. + * + ****************************************************************************/ + +typedef struct H5C_cache_entry_t +{ + haddr_t addr; + size_t size; + const H5C_class_t * type; + hbool_t is_dirty; + hbool_t is_protected; + hbool_t in_tree; + + /* fields supporting the hash table: */ + + struct H5C_cache_entry_t * ht_next; + struct H5C_cache_entry_t * ht_prev; + + /* fields supporting replacement policies: */ + + struct H5C_cache_entry_t * next; + struct H5C_cache_entry_t * prev; + struct H5C_cache_entry_t * aux_next; + struct H5C_cache_entry_t * aux_prev; + +#if H5C_COLLECT_CACHE_ENTRY_STATS + + /* cache entry stats fields */ + + int32_t accesses; + int32_t clears; + int32_t flushes; + +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ + +} H5C_cache_entry_t; + + +/* Typedef for the main structure for the cache (defined in H5C.c) */ + +typedef struct H5C_t H5C_t; + +/* + * Library prototypes. + */ +H5_DLL H5C_t * H5C_create(size_t max_cache_size, + size_t min_clean_size, + int max_type_id, + const char * (* type_name_table_ptr), + H5C_write_permitted_func_t check_write_permitted); + +H5_DLL herr_t H5C_dest(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr); + +H5_DLL herr_t H5C_dest_empty(H5C_t * cache_ptr); + +H5_DLL herr_t H5C_flush_cache(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + unsigned flags); + +H5_DLL herr_t H5C_insert_entry(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t addr, + void * thing); + +H5_DLL herr_t H5C_rename_entry(H5F_t * f, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t old_addr, + haddr_t new_addr); + +H5_DLL void * H5C_protect(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t addr, + const void * udata1, + void * udata2); + +H5_DLL herr_t H5C_unprotect(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + const H5C_class_t * type, + haddr_t addr, + void * thing, + hbool_t deleted); + +H5_DLL herr_t H5C_stats(H5C_t * cache_ptr, + const char * cache_name, + hbool_t display_detailed_stats); + +H5_DLL void H5C_stats__reset(H5C_t * cache_ptr); + +H5_DLL herr_t H5C_set_skip_flags(H5C_t * cache_ptr, + hbool_t skip_file_checks, + hbool_t skip_dxpl_id_checks); + +#endif /* !_H5Cprivate_H */ + diff --git a/src/H5Defl.c b/src/H5Defl.c new file mode 100644 index 0000000..ab3e260 --- /dev/null +++ b/src/H5Defl.c @@ -0,0 +1,371 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by the Board of Trustees of the University of Illinois. * + * 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 files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Programmer: Quincey Koziol + * Thursday, September 30, 2004 + */ + +#define H5D_PACKAGE /*suppress error about including H5Dpkg */ + +/* Pablo information */ +/* (Put before include files to avoid problems with inline functions) */ +#define PABLO_MASK H5D_efl_mask + +#include "H5private.h" /* Generic Functions */ +#include "H5Dpkg.h" /* Datasets */ +#include "H5Eprivate.h" /* Error handling */ +#include "H5Fprivate.h" /* Files */ + +/* PRIVATE PROTOTYPES */ +static herr_t H5D_efl_read (const H5O_efl_t *efl, haddr_t addr, size_t size, + uint8_t *buf); +static herr_t H5D_efl_write(const H5O_efl_t *efl, haddr_t addr, size_t size, + const uint8_t *buf); + + +/*------------------------------------------------------------------------- + * Function: H5D_efl_read + * + * Purpose: Reads data from an external file list. It is an error to + * read past the logical end of file, but reading past the end + * of any particular member of the external file list results in + * zeros. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Robb Matzke + * Wednesday, March 4, 1998 + * + * Modifications: + * Robb Matzke, 1999-07-28 + * The ADDR argument is passed by value. + *------------------------------------------------------------------------- + */ +static herr_t +H5D_efl_read (const H5O_efl_t *efl, haddr_t addr, size_t size, uint8_t *buf) +{ + int fd=-1; + size_t to_read; +#ifndef NDEBUG + hsize_t tempto_read; +#endif /* NDEBUG */ + hsize_t skip=0; + haddr_t cur; + ssize_t n; + size_t u; /* Local index variable */ + herr_t ret_value=SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT(H5D_efl_read); + + /* Check args */ + assert (efl && efl->nused>0); + assert (H5F_addr_defined (addr)); + assert (size < SIZET_MAX); + assert (buf || 0==size); + + /* Find the first efl member from which to read */ + for (u=0, cur=0; unused; u++) { + if (H5O_EFL_UNLIMITED==efl->slot[u].size || addr < cur+efl->slot[u].size) { + skip = addr - cur; + break; + } + cur += efl->slot[u].size; + } + + /* Read the data */ + while (size) { + if (u>=efl->nused) + HGOTO_ERROR (H5E_EFL, H5E_OVERFLOW, FAIL, "read past logical end of file"); + if (H5F_OVERFLOW_HSIZET2OFFT (efl->slot[u].offset+skip)) + HGOTO_ERROR (H5E_EFL, H5E_OVERFLOW, FAIL, "external file address overflowed"); + if ((fd=HDopen (efl->slot[u].name, O_RDONLY, 0))<0) + HGOTO_ERROR (H5E_EFL, H5E_CANTOPENFILE, FAIL, "unable to open external raw data file"); + if (HDlseek (fd, (off_t)(efl->slot[u].offset+skip), SEEK_SET)<0) + HGOTO_ERROR (H5E_EFL, H5E_SEEKERROR, FAIL, "unable to seek in external raw data file"); +#ifndef NDEBUG + tempto_read = MIN(efl->slot[u].size-skip,(hsize_t)size); + H5_CHECK_OVERFLOW(tempto_read,hsize_t,size_t); + to_read = (size_t)tempto_read; +#else /* NDEBUG */ + to_read = MIN((size_t)(efl->slot[u].size-skip), size); +#endif /* NDEBUG */ + if ((n=HDread (fd, buf, to_read))<0) { + HGOTO_ERROR (H5E_EFL, H5E_READERROR, FAIL, "read error in external raw data file"); + } else if ((size_t)n=0) + HDclose (fd); + + FUNC_LEAVE_NOAPI(ret_value); +} + + +/*------------------------------------------------------------------------- + * Function: H5D_efl_write + * + * Purpose: Writes data to an external file list. It is an error to + * write past the logical end of file, but writing past the end + * of any particular member of the external file list just + * extends that file. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Robb Matzke + * Wednesday, March 4, 1998 + * + * Modifications: + * Robb Matzke, 1999-07-28 + * The ADDR argument is passed by value. + *------------------------------------------------------------------------- + */ +static herr_t +H5D_efl_write (const H5O_efl_t *efl, haddr_t addr, size_t size, const uint8_t *buf) +{ + int fd=-1; + size_t to_write; +#ifndef NDEBUG + hsize_t tempto_write; +#endif /* NDEBUG */ + haddr_t cur; + hsize_t skip=0; + size_t u; /* Local index variable */ + herr_t ret_value=SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT(H5D_efl_write); + + /* Check args */ + assert (efl && efl->nused>0); + assert (H5F_addr_defined (addr)); + assert (size < SIZET_MAX); + assert (buf || 0==size); + + /* Find the first efl member in which to write */ + for (u=0, cur=0; unused; u++) { + if (H5O_EFL_UNLIMITED==efl->slot[u].size || addr < cur+efl->slot[u].size) { + skip = addr - cur; + break; + } + cur += efl->slot[u].size; + } + + /* Write the data */ + while (size) { + if (u>=efl->nused) + HGOTO_ERROR (H5E_EFL, H5E_OVERFLOW, FAIL, "write past logical end of file"); + if (H5F_OVERFLOW_HSIZET2OFFT (efl->slot[u].offset+skip)) + HGOTO_ERROR (H5E_EFL, H5E_OVERFLOW, FAIL, "external file address overflowed"); + if ((fd=HDopen (efl->slot[u].name, O_CREAT|O_RDWR, 0666))<0) { + if (HDaccess (efl->slot[u].name, F_OK)<0) { + HGOTO_ERROR (H5E_EFL, H5E_CANTOPENFILE, FAIL, "external raw data file does not exist"); + } else { + HGOTO_ERROR (H5E_EFL, H5E_CANTOPENFILE, FAIL, "unable to open external raw data file"); + } + } + if (HDlseek (fd, (off_t)(efl->slot[u].offset+skip), SEEK_SET)<0) + HGOTO_ERROR (H5E_EFL, H5E_SEEKERROR, FAIL, "unable to seek in external raw data file"); +#ifndef NDEBUG + tempto_write = MIN(efl->slot[u].size-skip,(hsize_t)size); + H5_CHECK_OVERFLOW(tempto_write,hsize_t,size_t); + to_write = (size_t)tempto_write; +#else /* NDEBUG */ + to_write = MIN((size_t)(efl->slot[u].size-skip), size); +#endif /* NDEBUG */ + if ((size_t)HDwrite (fd, buf, to_write)!=to_write) + HGOTO_ERROR (H5E_EFL, H5E_READERROR, FAIL, "write error in external raw data file"); + HDclose (fd); + fd = -1; + size -= to_write; + buf += to_write; + skip = 0; + u++; + } + +done: + if (fd>=0) + HDclose (fd); + + FUNC_LEAVE_NOAPI(ret_value); +} + + +/*------------------------------------------------------------------------- + * Function: H5D_efl_readvv + * + * Purpose: Reads data from an external file list. It is an error to + * read past the logical end of file, but reading past the end + * of any particular member of the external file list results in + * zeros. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Quincey Koziol + * Wednesday, May 7, 2003 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +ssize_t +H5D_efl_readvv(H5D_io_info_t *io_info, + size_t dset_max_nseq, size_t *dset_curr_seq, size_t dset_len_arr[], hsize_t dset_offset_arr[], + size_t mem_max_nseq, size_t *mem_curr_seq, size_t mem_len_arr[], hsize_t mem_offset_arr[], + void *_buf) +{ + const H5O_efl_t *efl=&(io_info->store->efl); /* Pointer to efl info */ + unsigned char *buf; /* Pointer to buffer to write */ + haddr_t addr; /* Actual address to read */ + size_t size; /* Size of sequence in bytes */ + size_t u; /* Counting variable */ + size_t v; /* Counting variable */ + ssize_t ret_value=0; /* Return value */ + + FUNC_ENTER_NOAPI(H5D_efl_readvv, FAIL); + + /* Check args */ + assert (efl && efl->nused>0); + assert (_buf); + + /* Work through all the sequences */ + for(u=*dset_curr_seq, v=*mem_curr_seq; ustore->efl); /* Pointer to efl info */ + const unsigned char *buf; /* Pointer to buffer to write */ + haddr_t addr; /* Actual address to read */ + size_t size; /* Size of sequence in bytes */ + size_t u; /* Counting variable */ + size_t v; /* Counting variable */ + ssize_t ret_value=0; /* Return value */ + + FUNC_ENTER_NOAPI(H5D_efl_writevv, FAIL); + + /* Check args */ + assert (efl && efl->nused>0); + assert (_buf); + + /* Work through all the sequences */ + for(u=*dset_curr_seq, v=*mem_curr_seq; u + * Thursday, September 30, 2004 + * + * Purpose: Dataspace I/O functions. + */ + +#define H5D_PACKAGE /*suppress error about including H5Dpkg */ + +/* Pablo information */ +/* (Put before include files to avoid problems with inline functions) */ +#define PABLO_MASK H5D_select_mask + +#include "H5private.h" /* Generic Functions */ +#include "H5Dpkg.h" /* Datasets */ +#include "H5Eprivate.h" /* Error handling */ +#include "H5FLprivate.h" /* Free Lists */ + +/* Declare a free list to manage sequences of size_t */ +H5FL_SEQ_DEFINE_STATIC(size_t); + +/* Declare a free list to manage sequences of hsize_t */ +H5FL_SEQ_DEFINE_STATIC(hsize_t); + + +/*------------------------------------------------------------------------- + * Function: H5D_select_fscat + * + * Purpose: Scatters dataset elements from the type conversion buffer BUF + * to the file F where the data points are arranged according to + * the file dataspace FILE_SPACE and stored according to + * LAYOUT and EFL. Each element is ELMT_SIZE bytes. + * The caller is requesting that NELMTS elements are copied. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Quincey Koziol + * Thursday, June 20, 2002 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +herr_t +H5D_select_fscat (H5D_io_info_t *io_info, + const H5S_t *space, H5S_sel_iter_t *iter, size_t nelmts, + const void *_buf) +{ + const uint8_t *buf=_buf; /* Alias for pointer arithmetic */ + hsize_t _off[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence offsets */ + hsize_t *off=NULL; /* Pointer to sequence offsets */ + hsize_t mem_off; /* Offset in memory */ + size_t mem_curr_seq; /* "Current sequence" in memory */ + size_t dset_curr_seq; /* "Current sequence" in dataset */ + size_t _len[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence lengths */ + size_t *len=NULL; /* Array to store sequence lengths */ + size_t orig_mem_len, mem_len; /* Length of sequence in memory */ + size_t nseq; /* Number of sequences generated */ + size_t nelem; /* Number of elements used in sequences */ + herr_t ret_value=SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5D_select_fscat, FAIL); + + /* Check args */ + assert (io_info); + assert (space); + assert (iter); + assert (nelmts>0); + assert (_buf); + assert(TRUE==H5P_isa_class(io_info->dxpl_id,H5P_DATASET_XFER)); + + /* Allocate the vector I/O arrays */ + if(io_info->dxpl_cache->vec_size!=H5D_XFER_HYPER_VECTOR_SIZE_DEF) { + if((len = H5FL_SEQ_MALLOC(size_t,io_info->dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate I/O length vector array"); + if((off = H5FL_SEQ_MALLOC(hsize_t,io_info->dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate I/O offset vector array"); + } /* end if */ + else { + len=_len; + off=_off; + } /* end else */ + + /* Loop until all elements are written */ + while(nelmts>0) { + /* Get list of sequences for selection to write */ + if(H5S_SELECT_GET_SEQ_LIST(space,H5S_GET_SEQ_LIST_SORTED,iter,io_info->dxpl_cache->vec_size,nelmts,&nseq,&nelem,off,len)<0) + HGOTO_ERROR (H5E_INTERNAL, H5E_UNSUPPORTED, FAIL, "sequence length generation failed"); + + /* Reset the current sequence information */ + mem_curr_seq=dset_curr_seq=0; + orig_mem_len=mem_len=nelem*iter->elmt_size; + mem_off=0; + + /* Write sequence list out */ + if ((*io_info->ops.writevv)(io_info, nseq, &dset_curr_seq, len, off, 1, &mem_curr_seq, &mem_len, &mem_off, buf)<0) + HGOTO_ERROR(H5E_DATASPACE, H5E_WRITEERROR, FAIL, "write error"); + + /* Update buffer */ + buf += orig_mem_len; + + /* Decrement number of elements left to process */ + nelmts -= nelem; + } /* end while */ + +done: + if(io_info->dxpl_cache->vec_size!=H5D_XFER_HYPER_VECTOR_SIZE_DEF) { + if(len!=NULL) + H5FL_SEQ_FREE(size_t,len); + if(off!=NULL) + H5FL_SEQ_FREE(hsize_t,off); + } /* end if */ + FUNC_LEAVE_NOAPI(ret_value); +} /* H5D_select_fscat() */ + + +/*------------------------------------------------------------------------- + * Function: H5D_select_fgath + * + * Purpose: Gathers data points from file F and accumulates them in the + * type conversion buffer BUF. The LAYOUT argument describes + * how the data is stored on disk and EFL describes how the data + * is organized in external files. ELMT_SIZE is the size in + * bytes of a datum which this function treats as opaque. + * FILE_SPACE describes the dataspace of the dataset on disk + * and the elements that have been selected for reading (via + * hyperslab, etc). This function will copy at most NELMTS + * elements. + * + * Return: Success: Number of elements copied. + * Failure: 0 + * + * Programmer: Quincey Koziol + * Monday, June 24, 2002 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +size_t +H5D_select_fgath (H5D_io_info_t *io_info, + const H5S_t *space, H5S_sel_iter_t *iter, size_t nelmts, + void *_buf/*out*/) +{ + uint8_t *buf=_buf; /* Alias for pointer arithmetic */ + hsize_t _off[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence offsets */ + hsize_t *off=NULL; /* Pointer to sequence offsets */ + hsize_t mem_off; /* Offset in memory */ + size_t mem_curr_seq; /* "Current sequence" in memory */ + size_t dset_curr_seq; /* "Current sequence" in dataset */ + size_t _len[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence lengths */ + size_t *len=NULL; /* Pointer to sequence lengths */ + size_t orig_mem_len, mem_len; /* Length of sequence in memory */ + size_t nseq; /* Number of sequences generated */ + size_t nelem; /* Number of elements used in sequences */ + size_t ret_value=nelmts; /* Return value */ + + FUNC_ENTER_NOAPI(H5D_select_fgath, 0); + + /* Check args */ + assert (io_info); + assert (io_info->dset); + assert (io_info->store); + assert (space); + assert (iter); + assert (nelmts>0); + assert (_buf); + + /* Allocate the vector I/O arrays */ + if(io_info->dxpl_cache->vec_size!=H5D_XFER_HYPER_VECTOR_SIZE_DEF) { + if((len = H5FL_SEQ_MALLOC(size_t,io_info->dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, 0, "can't allocate I/O length vector array"); + if((off = H5FL_SEQ_MALLOC(hsize_t,io_info->dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, 0, "can't allocate I/O offset vector array"); + } /* end if */ + else { + len=_len; + off=_off; + } /* end else */ + + /* Loop until all elements are read */ + while(nelmts>0) { + /* Get list of sequences for selection to read */ + if(H5S_SELECT_GET_SEQ_LIST(space,H5S_GET_SEQ_LIST_SORTED,iter,io_info->dxpl_cache->vec_size,nelmts,&nseq,&nelem,off,len)<0) + HGOTO_ERROR (H5E_INTERNAL, H5E_UNSUPPORTED, 0, "sequence length generation failed"); + + /* Reset the current sequence information */ + mem_curr_seq=dset_curr_seq=0; + orig_mem_len=mem_len=nelem*iter->elmt_size; + mem_off=0; + + /* Read sequence list in */ + if ((*io_info->ops.readvv)(io_info, nseq, &dset_curr_seq, len, off, 1, &mem_curr_seq, &mem_len, &mem_off, buf)<0) + HGOTO_ERROR(H5E_DATASPACE, H5E_READERROR, 0, "read error"); + + /* Update buffer */ + buf += orig_mem_len; + + /* Decrement number of elements left to process */ + nelmts -= nelem; + } /* end while */ + +done: + if(io_info->dxpl_cache->vec_size!=H5D_XFER_HYPER_VECTOR_SIZE_DEF) { + if(len!=NULL) + H5FL_SEQ_FREE(size_t,len); + if(off!=NULL) + H5FL_SEQ_FREE(hsize_t,off); + } /* end if */ + FUNC_LEAVE_NOAPI(ret_value); +} /* H5D_select_fgath() */ + + +/*------------------------------------------------------------------------- + * Function: H5D_select_mscat + * + * Purpose: Scatters NELMTS data points from the scatter buffer + * TSCAT_BUF to the application buffer BUF. Each element is + * ELMT_SIZE bytes and they are organized in application memory + * according to SPACE. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Quincey Koziol + * Monday, July 8, 2002 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +herr_t +H5D_select_mscat (const void *_tscat_buf, const H5S_t *space, + H5S_sel_iter_t *iter, size_t nelmts, const H5D_dxpl_cache_t *dxpl_cache, + void *_buf/*out*/) +{ + uint8_t *buf=(uint8_t *)_buf; /* Get local copies for address arithmetic */ + const uint8_t *tscat_buf=(const uint8_t *)_tscat_buf; + hsize_t _off[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence offsets */ + hsize_t *off=NULL; /* Pointer to sequence offsets */ + size_t _len[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence lengths */ + size_t *len=NULL; /* Pointer to sequence lengths */ + size_t curr_len; /* Length of bytes left to process in sequence */ + size_t nseq; /* Number of sequences generated */ + size_t curr_seq; /* Current sequence being processed */ + size_t nelem; /* Number of elements used in sequences */ + herr_t ret_value=SUCCEED; /* Number of elements scattered */ + + FUNC_ENTER_NOAPI(H5D_select_mscat, FAIL); + + /* Check args */ + assert (tscat_buf); + assert (space); + assert (iter); + assert (nelmts>0); + assert (buf); + + /* Allocate the vector I/O arrays */ + if(dxpl_cache->vec_size!=H5D_XFER_HYPER_VECTOR_SIZE_DEF) { + if((len = H5FL_SEQ_MALLOC(size_t,dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate I/O length vector array"); + if((off = H5FL_SEQ_MALLOC(hsize_t,dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate I/O offset vector array"); + } /* end if */ + else { + len=_len; + off=_off; + } /* end else */ + + /* Loop until all elements are written */ + while(nelmts>0) { + /* Get list of sequences for selection to write */ + if(H5S_SELECT_GET_SEQ_LIST(space,0,iter,dxpl_cache->vec_size,nelmts,&nseq,&nelem,off,len)<0) + HGOTO_ERROR (H5E_INTERNAL, H5E_UNSUPPORTED, 0, "sequence length generation failed"); + + /* Loop, while sequences left to process */ + for(curr_seq=0; curr_seqvec_size!=H5D_XFER_HYPER_VECTOR_SIZE_DEF) { + if(len!=NULL) + H5FL_SEQ_FREE(size_t,len); + if(off!=NULL) + H5FL_SEQ_FREE(hsize_t,off); + } /* end if */ + FUNC_LEAVE_NOAPI(ret_value); +} /* H5D_select_mscat() */ + + +/*------------------------------------------------------------------------- + * Function: H5D_select_mgath + * + * Purpose: Gathers dataset elements from application memory BUF and + * copies them into the gather buffer TGATH_BUF. + * Each element is ELMT_SIZE bytes and arranged in application + * memory according to SPACE. + * The caller is requesting that at most NELMTS be gathered. + * + * Return: Success: Number of elements copied. + * Failure: 0 + * + * Programmer: Quincey Koziol + * Monday, June 24, 2002 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +size_t +H5D_select_mgath (const void *_buf, const H5S_t *space, + H5S_sel_iter_t *iter, size_t nelmts, const H5D_dxpl_cache_t *dxpl_cache, + void *_tgath_buf/*out*/) +{ + const uint8_t *buf=(const uint8_t *)_buf; /* Get local copies for address arithmetic */ + uint8_t *tgath_buf=(uint8_t *)_tgath_buf; + hsize_t _off[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence offsets */ + hsize_t *off=NULL; /* Pointer to sequence offsets */ + size_t _len[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence lengths */ + size_t *len=NULL; /* Pointer to sequence lengths */ + size_t curr_len; /* Length of bytes left to process in sequence */ + size_t nseq; /* Number of sequences generated */ + size_t curr_seq; /* Current sequence being processed */ + size_t nelem; /* Number of elements used in sequences */ + size_t ret_value=nelmts; /* Number of elements gathered */ + + FUNC_ENTER_NOAPI(H5D_select_mgath, 0); + + /* Check args */ + assert (buf); + assert (space); + assert (iter); + assert (nelmts>0); + assert (tgath_buf); + + /* Allocate the vector I/O arrays */ + if(dxpl_cache->vec_size!=H5D_XFER_HYPER_VECTOR_SIZE_DEF) { + if((len = H5FL_SEQ_MALLOC(size_t,dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, 0, "can't allocate I/O length vector array"); + if((off = H5FL_SEQ_MALLOC(hsize_t,dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, 0, "can't allocate I/O offset vector array"); + } /* end if */ + else { + len=_len; + off=_off; + } /* end else */ + + /* Loop until all elements are written */ + while(nelmts>0) { + /* Get list of sequences for selection to write */ + if(H5S_SELECT_GET_SEQ_LIST(space,0,iter,dxpl_cache->vec_size,nelmts,&nseq,&nelem,off,len)<0) + HGOTO_ERROR (H5E_INTERNAL, H5E_UNSUPPORTED, 0, "sequence length generation failed"); + + /* Loop, while sequences left to process */ + for(curr_seq=0; curr_seqvec_size!=H5D_XFER_HYPER_VECTOR_SIZE_DEF) { + if(len!=NULL) + H5FL_SEQ_FREE(size_t,len); + if(off!=NULL) + H5FL_SEQ_FREE(hsize_t,off); + } /* end if */ + FUNC_LEAVE_NOAPI(ret_value); +} /* H5D_select_mgath() */ + + +/*------------------------------------------------------------------------- + * Function: H5D_select_read + * + * Purpose: Reads directly from file into application memory. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Quincey Koziol + * Tuesday, July 23, 2002 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +herr_t +H5D_select_read(H5D_io_info_t *io_info, + size_t nelmts, size_t elmt_size, + const H5S_t *file_space, const H5S_t *mem_space, + void *buf/*out*/) +{ + H5S_sel_iter_t mem_iter; /* Memory selection iteration info */ + hbool_t mem_iter_init=0; /* Memory selection iteration info has been initialized */ + H5S_sel_iter_t file_iter; /* File selection iteration info */ + hbool_t file_iter_init=0; /* File selection iteration info has been initialized */ + hsize_t _mem_off[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence offsets in memory */ + hsize_t *mem_off=NULL; /* Pointer to sequence offsets in memory */ + hsize_t _file_off[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence offsets in the file */ + hsize_t *file_off=NULL; /* Pointer to sequence offsets in the file */ + size_t _mem_len[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence lengths in memory */ + size_t *mem_len=NULL; /* Pointer to sequence lengths in memory */ + size_t _file_len[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence lengths in the file */ + size_t *file_len=NULL; /* Pointer to sequence lengths in the file */ + size_t mem_nseq; /* Number of sequences generated in the file */ + size_t file_nseq; /* Number of sequences generated in memory */ + size_t mem_nelem; /* Number of elements used in memory sequences */ + size_t file_nelem; /* Number of elements used in file sequences */ + size_t curr_mem_seq; /* Current memory sequence to operate on */ + size_t curr_file_seq; /* Current file sequence to operate on */ + ssize_t tmp_file_len; /* Temporary number of bytes in file sequence */ + herr_t ret_value=SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5D_select_read, FAIL); + + /* Check args */ + assert(io_info); + assert(io_info->dset); + assert(io_info->dxpl_cache); + assert(io_info->store); + assert(buf); + assert(TRUE==H5P_isa_class(io_info->dxpl_id,H5P_DATASET_XFER)); + + /* Initialize file iterator */ + if (H5S_select_iter_init(&file_iter, file_space, elmt_size)<0) + HGOTO_ERROR (H5E_DATASPACE, H5E_CANTINIT, FAIL, "unable to initialize selection iterator"); + file_iter_init=1; /* File selection iteration info has been initialized */ + + /* Initialize memory iterator */ + if (H5S_select_iter_init(&mem_iter, mem_space, elmt_size)<0) + HGOTO_ERROR (H5E_DATASPACE, H5E_CANTINIT, FAIL, "unable to initialize selection iterator"); + mem_iter_init=1; /* Memory selection iteration info has been initialized */ + + /* Allocate the vector I/O arrays */ + if(io_info->dxpl_cache->vec_size!=H5D_XFER_HYPER_VECTOR_SIZE_DEF) { + if((mem_len = H5FL_SEQ_MALLOC(size_t,io_info->dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate I/O length vector array"); + if((mem_off = H5FL_SEQ_MALLOC(hsize_t,io_info->dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate I/O offset vector array"); + if((file_len = H5FL_SEQ_MALLOC(size_t,io_info->dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate I/O length vector array"); + if((file_off = H5FL_SEQ_MALLOC(hsize_t,io_info->dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate I/O offset vector array"); + } /* end if */ + else { + mem_len=_mem_len; + mem_off=_mem_off; + file_len=_file_len; + file_off=_file_off; + } /* end else */ + + /* Initialize sequence counts */ + curr_mem_seq=curr_file_seq=0; + mem_nseq=file_nseq=0; + + /* Loop, until all bytes are processed */ + while(nelmts>0) { + /* Check if more file sequences are needed */ + if(curr_file_seq>=file_nseq) { + /* Get sequences for file selection */ + if(H5S_SELECT_GET_SEQ_LIST(file_space,H5S_GET_SEQ_LIST_SORTED,&file_iter,io_info->dxpl_cache->vec_size,nelmts,&file_nseq,&file_nelem,file_off,file_len)<0) + HGOTO_ERROR (H5E_INTERNAL, H5E_UNSUPPORTED, FAIL, "sequence length generation failed"); + + /* Start at the beginning of the sequences again */ + curr_file_seq=0; + } /* end if */ + + /* Check if more memory sequences are needed */ + if(curr_mem_seq>=mem_nseq) { + /* Get sequences for memory selection */ + if(H5S_SELECT_GET_SEQ_LIST(mem_space,0,&mem_iter,io_info->dxpl_cache->vec_size,nelmts,&mem_nseq,&mem_nelem,mem_off,mem_len)<0) + HGOTO_ERROR (H5E_INTERNAL, H5E_UNSUPPORTED, FAIL, "sequence length generation failed"); + + /* Start at the beginning of the sequences again */ + curr_mem_seq=0; + } /* end if */ + + /* Read file sequences into current memory sequence */ + if ((tmp_file_len=(*io_info->ops.readvv)(io_info, + file_nseq, &curr_file_seq, file_len, file_off, + mem_nseq, &curr_mem_seq, mem_len, mem_off, + buf))<0) + HGOTO_ERROR(H5E_DATASPACE, H5E_READERROR, FAIL, "read error"); + + /* Decrement number of elements left to process */ + assert((tmp_file_len%elmt_size)==0); + nelmts-=(tmp_file_len/elmt_size); + } /* end while */ + +done: + /* Release file selection iterator */ + if(file_iter_init) { + if (H5S_SELECT_ITER_RELEASE(&file_iter)<0) + HDONE_ERROR (H5E_DATASPACE, H5E_CANTRELEASE, FAIL, "unable to release selection iterator"); + } /* end if */ + + /* Release memory selection iterator */ + if(mem_iter_init) { + if (H5S_SELECT_ITER_RELEASE(&mem_iter)<0) + HDONE_ERROR (H5E_DATASPACE, H5E_CANTRELEASE, FAIL, "unable to release selection iterator"); + } /* end if */ + + /* Free vector arrays */ + if(io_info->dxpl_cache->vec_size!=H5D_XFER_HYPER_VECTOR_SIZE_DEF) { + if(file_len!=NULL) + H5FL_SEQ_FREE(size_t,file_len); + if(file_off!=NULL) + H5FL_SEQ_FREE(hsize_t,file_off); + if(mem_len!=NULL) + H5FL_SEQ_FREE(size_t,mem_len); + if(mem_off!=NULL) + H5FL_SEQ_FREE(hsize_t,mem_off); + } /* end if */ + FUNC_LEAVE_NOAPI(ret_value); +} /* end H5D_select_read() */ + + +/*------------------------------------------------------------------------- + * Function: H5D_select_write + * + * Purpose: Writes directly from application memory into a file + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Quincey Koziol + * Tuesday, July 23, 2002 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +herr_t +H5D_select_write(H5D_io_info_t *io_info, + size_t nelmts, size_t elmt_size, + const H5S_t *file_space, const H5S_t *mem_space, + const void *buf/*out*/) +{ + H5S_sel_iter_t mem_iter; /* Memory selection iteration info */ + hbool_t mem_iter_init=0; /* Memory selection iteration info has been initialized */ + H5S_sel_iter_t file_iter; /* File selection iteration info */ + hbool_t file_iter_init=0; /* File selection iteration info has been initialized */ + hsize_t _mem_off[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence offsets in memory */ + hsize_t *mem_off=NULL; /* Pointer to sequence offsets in memory */ + hsize_t _file_off[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence offsets in the file */ + hsize_t *file_off=NULL; /* Pointer to sequence offsets in the file */ + size_t _mem_len[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence lengths in memory */ + size_t *mem_len=NULL; /* Pointer to sequence lengths in memory */ + size_t _file_len[H5D_XFER_HYPER_VECTOR_SIZE_DEF]; /* Array to store sequence lengths in the file */ + size_t *file_len=NULL; /* Pointer to sequence lengths in the file */ + size_t mem_nseq; /* Number of sequences generated in the file */ + size_t file_nseq; /* Number of sequences generated in memory */ + size_t mem_nelem; /* Number of elements used in memory sequences */ + size_t file_nelem; /* Number of elements used in file sequences */ + size_t curr_mem_seq; /* Current memory sequence to operate on */ + size_t curr_file_seq; /* Current file sequence to operate on */ + ssize_t tmp_file_len; /* Temporary number of bytes in file sequence */ + herr_t ret_value=SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5D_select_write, FAIL); + + /* Check args */ + assert(io_info); + assert(io_info->dset); + assert(io_info->store); + assert(TRUE==H5P_isa_class(io_info->dxpl_id,H5P_DATASET_XFER)); + assert(buf); + + /* Allocate the vector I/O arrays */ + if(io_info->dxpl_cache->vec_size!=H5D_XFER_HYPER_VECTOR_SIZE_DEF) { + if((mem_len = H5FL_SEQ_MALLOC(size_t,io_info->dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate I/O length vector array"); + if((mem_off = H5FL_SEQ_MALLOC(hsize_t,io_info->dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate I/O offset vector array"); + if((file_len = H5FL_SEQ_MALLOC(size_t,io_info->dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate I/O length vector array"); + if((file_off = H5FL_SEQ_MALLOC(hsize_t,io_info->dxpl_cache->vec_size))==NULL) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate I/O offset vector array"); + } /* end if */ + else { + mem_len=_mem_len; + mem_off=_mem_off; + file_len=_file_len; + file_off=_file_off; + } /* end else */ + + /* Initialize file iterator */ + if (H5S_select_iter_init(&file_iter, file_space, elmt_size)<0) + HGOTO_ERROR (H5E_DATASPACE, H5E_CANTINIT, FAIL, "unable to initialize selection iterator"); + file_iter_init=1; /* File selection iteration info has been initialized */ + + /* Initialize memory iterator */ + if (H5S_select_iter_init(&mem_iter, mem_space, elmt_size)<0) + HGOTO_ERROR (H5E_DATASPACE, H5E_CANTINIT, FAIL, "unable to initialize selection iterator"); + mem_iter_init=1; /* Memory selection iteration info has been initialized */ + + /* Initialize sequence counts */ + curr_mem_seq=curr_file_seq=0; + mem_nseq=file_nseq=0; + + /* Loop, until all bytes are processed */ + while(nelmts>0) { + /* Check if more file sequences are needed */ + if(curr_file_seq>=file_nseq) { + /* Get sequences for file selection */ + if(H5S_SELECT_GET_SEQ_LIST(file_space,H5S_GET_SEQ_LIST_SORTED,&file_iter,io_info->dxpl_cache->vec_size,nelmts,&file_nseq,&file_nelem,file_off,file_len)<0) + HGOTO_ERROR (H5E_INTERNAL, H5E_UNSUPPORTED, FAIL, "sequence length generation failed"); + + /* Start at the beginning of the sequences again */ + curr_file_seq=0; + } /* end if */ + + /* Check if more memory sequences are needed */ + if(curr_mem_seq>=mem_nseq) { + /* Get sequences for memory selection */ + if(H5S_SELECT_GET_SEQ_LIST(mem_space,0,&mem_iter,io_info->dxpl_cache->vec_size,nelmts,&mem_nseq,&mem_nelem,mem_off,mem_len)<0) + HGOTO_ERROR (H5E_INTERNAL, H5E_UNSUPPORTED, FAIL, "sequence length generation failed"); + + /* Start at the beginning of the sequences again */ + curr_mem_seq=0; + } /* end if */ + + /* Write memory sequences into file sequences */ + if ((tmp_file_len=(*io_info->ops.writevv)(io_info, + file_nseq, &curr_file_seq, file_len, file_off, + mem_nseq, &curr_mem_seq, mem_len, mem_off, + buf))<0) + HGOTO_ERROR(H5E_DATASPACE, H5E_WRITEERROR, FAIL, "write error"); + + /* Decrement number of elements left to process */ + assert((tmp_file_len%elmt_size)==0); + nelmts-=(tmp_file_len/elmt_size); + } /* end while */ + +done: + /* Release file selection iterator */ + if(file_iter_init) { + if (H5S_SELECT_ITER_RELEASE(&file_iter)<0) + HDONE_ERROR (H5E_DATASPACE, H5E_CANTRELEASE, FAIL, "unable to release selection iterator"); + } /* end if */ + + /* Release memory selection iterator */ + if(mem_iter_init) { + if (H5S_SELECT_ITER_RELEASE(&mem_iter)<0) + HDONE_ERROR (H5E_DATASPACE, H5E_CANTRELEASE, FAIL, "unable to release selection iterator"); + } /* end if */ + + /* Free vector arrays */ + if(io_info->dxpl_cache->vec_size!=H5D_XFER_HYPER_VECTOR_SIZE_DEF) { + if(file_len!=NULL) + H5FL_SEQ_FREE(size_t,file_len); + if(file_off!=NULL) + H5FL_SEQ_FREE(hsize_t,file_off); + if(mem_len!=NULL) + H5FL_SEQ_FREE(size_t,mem_len); + if(mem_off!=NULL) + H5FL_SEQ_FREE(hsize_t,mem_off); + } /* end if */ + + FUNC_LEAVE_NOAPI(ret_value); +} /* end H5D_select_write() */ + diff --git a/test/cache.c b/test/cache.c new file mode 100644 index 0000000..bdd27d1 --- /dev/null +++ b/test/cache.c @@ -0,0 +1,4160 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by the Board of Trustees of the University of Illinois. * + * 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 files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* Programmer: John Mainzer + * 6/9/04 + * + * This file contains tests for the cache implemented in + * H5C.c + */ +#include "h5test.h" +#include "H5Iprivate.h" + +const char *FILENAME[] = { + "cache", + NULL +}; + +#include "H5TBprivate.h" +#include "H5Cprivate.h" + +/* with apologies for the abuse of terminology... */ + +#define PICO_ENTRY_TYPE 0 +#define NANO_ENTRY_TYPE 1 +#define MICRO_ENTRY_TYPE 2 +#define TINY_ENTRY_TYPE 3 +#define SMALL_ENTRY_TYPE 4 +#define MEDIUM_ENTRY_TYPE 5 +#define LARGE_ENTRY_TYPE 6 +#define HUGE_ENTRY_TYPE 7 +#define MONSTER_ENTRY_TYPE 8 + +#define NUMBER_OF_ENTRY_TYPES 9 + +#define PICO_ENTRY_SIZE (size_t)1 +#define NANO_ENTRY_SIZE (size_t)4 +#define MICRO_ENTRY_SIZE (size_t)16 +#define TINY_ENTRY_SIZE (size_t)64 +#define SMALL_ENTRY_SIZE (size_t)256 +#define MEDIUM_ENTRY_SIZE (size_t)1024 +#define LARGE_ENTRY_SIZE (size_t)(4 * 1024) +#define HUGE_ENTRY_SIZE (size_t)(16 * 1024) +#define MONSTER_ENTRY_SIZE (size_t)(64 * 1024) + +#define NUM_PICO_ENTRIES (10 * 1024) +#define NUM_NANO_ENTRIES (10 * 1024) +#define NUM_MICRO_ENTRIES (10 * 1024) +#define NUM_TINY_ENTRIES (10 * 1024) +#define NUM_SMALL_ENTRIES (10 * 1024) +#define NUM_MEDIUM_ENTRIES (10 * 1024) +#define NUM_LARGE_ENTRIES (10 * 1024) +#define NUM_HUGE_ENTRIES (10 * 1024) +#define NUM_MONSTER_ENTRIES (10 * 1024) + +#define MAX_ENTRIES (10 * 1024) + +#define PICO_BASE_ADDR (haddr_t)0 +#define NANO_BASE_ADDR (haddr_t)(PICO_BASE_ADDR + \ + (PICO_ENTRY_SIZE * NUM_PICO_ENTRIES)) +#define MICRO_BASE_ADDR (haddr_t)(NANO_BASE_ADDR + \ + (NANO_ENTRY_SIZE * NUM_NANO_ENTRIES)) +#define TINY_BASE_ADDR (haddr_t)(MICRO_BASE_ADDR + \ + (MICRO_ENTRY_SIZE * NUM_MICRO_ENTRIES)) +#define SMALL_BASE_ADDR (haddr_t)(TINY_BASE_ADDR + \ + (TINY_ENTRY_SIZE * NUM_TINY_ENTRIES)) +#define MEDIUM_BASE_ADDR (haddr_t)(SMALL_BASE_ADDR + \ + (SMALL_ENTRY_SIZE * NUM_SMALL_ENTRIES)) +#define LARGE_BASE_ADDR (haddr_t)(MEDIUM_BASE_ADDR + \ + (MEDIUM_ENTRY_SIZE * NUM_MEDIUM_ENTRIES)) +#define HUGE_BASE_ADDR (haddr_t)(LARGE_BASE_ADDR + \ + (LARGE_ENTRY_SIZE * NUM_LARGE_ENTRIES)) +#define MONSTER_BASE_ADDR (haddr_t)(HUGE_BASE_ADDR + \ + (HUGE_ENTRY_SIZE * NUM_HUGE_ENTRIES)) + +#define PICO_ALT_BASE_ADDR (haddr_t)(MONSTER_BASE_ADDR + \ + (MONSTER_ENTRY_SIZE * NUM_MONSTER_ENTRIES)) +#define NANO_ALT_BASE_ADDR (haddr_t)(PICO_ALT_BASE_ADDR + \ + (PICO_ENTRY_SIZE * NUM_PICO_ENTRIES)) +#define MICRO_ALT_BASE_ADDR (haddr_t)(NANO_ALT_BASE_ADDR + \ + (NANO_ENTRY_SIZE * NUM_NANO_ENTRIES)) +#define TINY_ALT_BASE_ADDR (haddr_t)(MICRO_ALT_BASE_ADDR + \ + (MICRO_ENTRY_SIZE * NUM_MICRO_ENTRIES)) +#define SMALL_ALT_BASE_ADDR (haddr_t)(TINY_ALT_BASE_ADDR + \ + (TINY_ENTRY_SIZE * NUM_TINY_ENTRIES)) +#define MEDIUM_ALT_BASE_ADDR (haddr_t)(SMALL_ALT_BASE_ADDR + \ + (SMALL_ENTRY_SIZE * NUM_SMALL_ENTRIES)) +#define LARGE_ALT_BASE_ADDR (haddr_t)(MEDIUM_ALT_BASE_ADDR + \ + (MEDIUM_ENTRY_SIZE * NUM_MEDIUM_ENTRIES)) +#define HUGE_ALT_BASE_ADDR (haddr_t)(LARGE_ALT_BASE_ADDR + \ + (LARGE_ENTRY_SIZE * NUM_LARGE_ENTRIES)) +#define MONSTER_ALT_BASE_ADDR (haddr_t)(HUGE_ALT_BASE_ADDR + \ + (HUGE_ENTRY_SIZE * NUM_HUGE_ENTRIES)) + +typedef struct test_entry_t +{ + H5C_cache_entry_t header; /* entry data used by the cache + * -- must be first + */ + struct test_entry_t * self; /* pointer to this entry -- used for + * sanity checking. + */ + haddr_t addr; /* where the cache thinks this entry + * is located + */ + hbool_t at_main_addr; /* boolean flag indicating whether + * the entry is supposed to be at + * either its main or alternate + * address. + */ + haddr_t main_addr; /* initial location of the entry + */ + haddr_t alt_addr; /* location to which the entry + * can be relocated or "renamed" + */ + size_t size; /* how big the cache thinks this + * entry is + */ + int32_t type; /* indicates which entry array this + * entry is in + */ + int32_t index; /* index in its entry array + */ + int32_t reads; /* number of times this entry has + * been loaded. + */ + int32_t writes; /* number of times this entry has + * been written + */ + hbool_t is_dirty; /* entry has been modified since + * last write + */ + hbool_t is_protected; /* entry should currently be on + * the cache's protected list. + */ +} test_entry_t; + +/* The following is a cut down copy of the hash table manipulation + * macros from H5C.c, which have been further modified to avoid references + * to the error reporting macros. Needless to say, these macros must be + * updated as necessary. + */ + +#define H5C__HASH_TABLE_LEN (32 * 1024) /* must be a power of 2 */ +#define H5C__HASH_MASK ((size_t)(H5C__HASH_TABLE_LEN - 1) << 3) +#define H5C__HASH_FCN(x) (int)(((x) & H5C__HASH_MASK) >> 3) + +#define H5C__PRE_HT_SEARCH_SC(cache_ptr, Addr) \ +if ( ( (cache_ptr) == NULL ) || \ + ( (cache_ptr)->magic != H5C__H5C_T_MAGIC ) || \ + ( ! H5F_addr_defined(Addr) ) || \ + ( H5C__HASH_FCN(Addr) < 0 ) || \ + ( H5C__HASH_FCN(Addr) >= H5C__HASH_TABLE_LEN ) ) { \ + HDfprintf(stdout, "Pre HT search SC failed.\n"); \ +} + +#define H5C__POST_SUC_HT_SEARCH_SC(cache_ptr, entry_ptr, Addr, k) \ +if ( ( (cache_ptr) == NULL ) || \ + ( (cache_ptr)->magic != H5C__H5C_T_MAGIC ) || \ + ( (cache_ptr)->index_len < 1 ) || \ + ( (entry_ptr) == NULL ) || \ + ( (cache_ptr)->index_size < (entry_ptr)->size ) || \ + ( H5F_addr_ne((entry_ptr)->addr, (Addr)) ) || \ + ( (entry_ptr)->size <= 0 ) || \ + ( ((cache_ptr)->index)[k] == NULL ) || \ + ( ( ((cache_ptr)->index)[k] != (entry_ptr) ) && \ + ( (entry_ptr)->ht_prev == NULL ) ) || \ + ( ( ((cache_ptr)->index)[k] == (entry_ptr) ) && \ + ( (entry_ptr)->ht_prev != NULL ) ) || \ + ( ( (entry_ptr)->ht_prev != NULL ) && \ + ( (entry_ptr)->ht_prev->ht_next != (entry_ptr) ) ) || \ + ( ( (entry_ptr)->ht_next != NULL ) && \ + ( (entry_ptr)->ht_next->ht_prev != (entry_ptr) ) ) ) { \ + HDfprintf(stdout, "Post successful HT search SC failed.\n"); \ +} + + +#define H5C__SEARCH_INDEX(cache_ptr, Addr, entry_ptr) \ +{ \ + int k; \ + int depth = 0; \ + H5C__PRE_HT_SEARCH_SC(cache_ptr, Addr) \ + k = H5C__HASH_FCN(Addr); \ + entry_ptr = ((cache_ptr)->index)[k]; \ + while ( ( entry_ptr ) && ( H5F_addr_ne(Addr, (entry_ptr)->addr) ) ) \ + { \ + (entry_ptr) = (entry_ptr)->ht_next; \ + (depth)++; \ + } \ + if ( entry_ptr ) \ + { \ + H5C__POST_SUC_HT_SEARCH_SC(cache_ptr, entry_ptr, Addr, k) \ + if ( entry_ptr != ((cache_ptr)->index)[k] ) \ + { \ + if ( (entry_ptr)->ht_next ) \ + { \ + (entry_ptr)->ht_next->ht_prev = (entry_ptr)->ht_prev; \ + } \ + HDassert( (entry_ptr)->ht_prev != NULL ); \ + (entry_ptr)->ht_prev->ht_next = (entry_ptr)->ht_next; \ + ((cache_ptr)->index)[k]->ht_prev = (entry_ptr); \ + (entry_ptr)->ht_next = ((cache_ptr)->index)[k]; \ + (entry_ptr)->ht_prev = NULL; \ + ((cache_ptr)->index)[k] = (entry_ptr); \ + } \ + } \ +} + + +/* The following is a local copy of the H5C_t structure -- any changes in + * that structure must be reproduced here. The typedef is used to allow + * local access to the cache's private data. + */ + +#define H5C__H5C_T_MAGIC 0x005CAC0E +#define H5C__MAX_NUM_TYPE_IDS 9 + +typedef struct local_H5C_t +{ + uint32_t magic; + + int32_t max_type_id; + const char * (* type_name_table_ptr); + + size_t max_cache_size; + size_t min_clean_size; + + H5C_write_permitted_func_t check_write_permitted; + + int32_t index_len; + size_t index_size; + H5C_cache_entry_t * (index[H5C__HASH_TABLE_LEN]); + + + int32_t tree_len; + size_t tree_size; + H5TB_TREE * tree_ptr; + + int32_t pl_len; + size_t pl_size; + H5C_cache_entry_t * pl_head_ptr; + H5C_cache_entry_t * pl_tail_ptr; + + int32_t LRU_list_len; + size_t LRU_list_size; + H5C_cache_entry_t * LRU_head_ptr; + H5C_cache_entry_t * LRU_tail_ptr; + + int32_t cLRU_list_len; + size_t cLRU_list_size; + H5C_cache_entry_t * cLRU_head_ptr; + H5C_cache_entry_t * cLRU_tail_ptr; + + int32_t dLRU_list_len; + size_t dLRU_list_size; + H5C_cache_entry_t * dLRU_head_ptr; + H5C_cache_entry_t * dLRU_tail_ptr; + +#if H5C_COLLECT_CACHE_STATS + + /* stats fields */ + int64_t hits[H5C__MAX_NUM_TYPE_IDS]; + int64_t misses[H5C__MAX_NUM_TYPE_IDS]; + int64_t insertions[H5C__MAX_NUM_TYPE_IDS]; + int64_t clears[H5C__MAX_NUM_TYPE_IDS]; + int64_t flushes[H5C__MAX_NUM_TYPE_IDS]; + int64_t evictions[H5C__MAX_NUM_TYPE_IDS]; + int64_t renames[H5C__MAX_NUM_TYPE_IDS]; + + int64_t total_ht_insertions; + int64_t total_ht_deletions; + int64_t successful_ht_searches; + int64_t total_successful_ht_search_depth; + int64_t failed_ht_searches; + int64_t total_failed_ht_search_depth; + + int32_t max_index_len; + size_t max_index_size; + + int32_t max_tree_len; + size_t max_tree_size; + + int32_t max_pl_len; + size_t max_pl_size; + +#if H5C_COLLECT_CACHE_ENTRY_STATS + + int32_t max_accesses[H5C__MAX_NUM_TYPE_IDS]; + int32_t min_accesses[H5C__MAX_NUM_TYPE_IDS]; + int32_t max_clears[H5C__MAX_NUM_TYPE_IDS]; + int32_t max_flushes[H5C__MAX_NUM_TYPE_IDS]; + size_t max_size[H5C__MAX_NUM_TYPE_IDS]; + +#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ + +#endif /* H5C_COLLECT_CACHE_STATS */ + + hbool_t skip_file_checks; + hbool_t skip_dxpl_id_checks; + +} local_H5C_t; + + +/* global variable declarations: */ + +static hbool_t write_permitted = TRUE; +static hbool_t pass = TRUE; /* set to false on error */ +const char *failure_mssg = NULL; + +test_entry_t pico_entries[NUM_PICO_ENTRIES]; +test_entry_t nano_entries[NUM_NANO_ENTRIES]; +test_entry_t micro_entries[NUM_MICRO_ENTRIES]; +test_entry_t tiny_entries[NUM_TINY_ENTRIES]; +test_entry_t small_entries[NUM_SMALL_ENTRIES]; +test_entry_t medium_entries[NUM_MEDIUM_ENTRIES]; +test_entry_t large_entries[NUM_LARGE_ENTRIES]; +test_entry_t huge_entries[NUM_HUGE_ENTRIES]; +test_entry_t monster_entries[NUM_MONSTER_ENTRIES]; + +test_entry_t * entries[NUMBER_OF_ENTRY_TYPES] = +{ + pico_entries, + nano_entries, + micro_entries, + tiny_entries, + small_entries, + medium_entries, + large_entries, + huge_entries, + monster_entries +}; + +const int32_t max_indices[NUMBER_OF_ENTRY_TYPES] = +{ + NUM_PICO_ENTRIES - 1, + NUM_NANO_ENTRIES - 1, + NUM_MICRO_ENTRIES - 1, + NUM_TINY_ENTRIES - 1, + NUM_SMALL_ENTRIES - 1, + NUM_MEDIUM_ENTRIES - 1, + NUM_LARGE_ENTRIES - 1, + NUM_HUGE_ENTRIES - 1, + NUM_MONSTER_ENTRIES - 1 +}; + +const size_t entry_sizes[NUMBER_OF_ENTRY_TYPES] = +{ + PICO_ENTRY_SIZE, + NANO_ENTRY_SIZE, + MICRO_ENTRY_SIZE, + TINY_ENTRY_SIZE, + SMALL_ENTRY_SIZE, + MEDIUM_ENTRY_SIZE, + LARGE_ENTRY_SIZE, + HUGE_ENTRY_SIZE, + MONSTER_ENTRY_SIZE +}; + +const haddr_t base_addrs[NUMBER_OF_ENTRY_TYPES] = +{ + PICO_BASE_ADDR, + NANO_BASE_ADDR, + MICRO_BASE_ADDR, + TINY_BASE_ADDR, + SMALL_BASE_ADDR, + MEDIUM_BASE_ADDR, + LARGE_BASE_ADDR, + HUGE_BASE_ADDR, + MONSTER_BASE_ADDR +}; + +const haddr_t alt_base_addrs[NUMBER_OF_ENTRY_TYPES] = +{ + PICO_ALT_BASE_ADDR, + NANO_ALT_BASE_ADDR, + MICRO_ALT_BASE_ADDR, + TINY_ALT_BASE_ADDR, + SMALL_ALT_BASE_ADDR, + MEDIUM_ALT_BASE_ADDR, + LARGE_ALT_BASE_ADDR, + HUGE_ALT_BASE_ADDR, + MONSTER_ALT_BASE_ADDR +}; + +const char * entry_type_names[NUMBER_OF_ENTRY_TYPES] = +{ + "pico entries -- 1 B", + "nano entries -- 4 B", + "micro entries -- 16 B", + "tiny entries -- 64 B", + "small entries -- 256 B", + "medium entries -- 1 KB", + "large entries -- 4 KB", + "huge entries -- 16 KB", + "monster entries -- 64 KB" +}; + + +/* call back function declarations: */ + +static herr_t check_write_permitted(H5F_t UNUSED * f, + hid_t UNUSED dxpl_id, + hbool_t * write_permitted_ptr); + +static herr_t clear(H5F_t * f, void * thing, hbool_t dest); + +herr_t pico_clear(H5F_t * f, void * thing, hbool_t dest); +herr_t nano_clear(H5F_t * f, void * thing, hbool_t dest); +herr_t micro_clear(H5F_t * f, void * thing, hbool_t dest); +herr_t tiny_clear(H5F_t * f, void * thing, hbool_t dest); +herr_t small_clear(H5F_t * f, void * thing, hbool_t dest); +herr_t medium_clear(H5F_t * f, void * thing, hbool_t dest); +herr_t large_clear(H5F_t * f, void * thing, hbool_t dest); +herr_t huge_clear(H5F_t * f, void * thing, hbool_t dest); +herr_t monster_clear(H5F_t * f, void * thing, hbool_t dest); + + +static herr_t destroy(H5F_t UNUSED * f, void * thing); + +herr_t pico_dest(H5F_t * f, void * thing); +herr_t nano_dest(H5F_t * f, void * thing); +herr_t micro_dest(H5F_t * f, void * thing); +herr_t tiny_dest(H5F_t * f, void * thing); +herr_t small_dest(H5F_t * f, void * thing); +herr_t medium_dest(H5F_t * f, void * thing); +herr_t large_dest(H5F_t * f, void * thing); +herr_t huge_dest(H5F_t * f, void * thing); +herr_t monster_dest(H5F_t * f, void * thing); + + +static herr_t flush(H5F_t *f, hid_t UNUSED dxpl_id, hbool_t dest, + haddr_t addr, void *thing); + +herr_t pico_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, + haddr_t addr, void *thing); +herr_t nano_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, + haddr_t addr, void *thing); +herr_t micro_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, + haddr_t addr, void *thing); +herr_t tiny_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, + haddr_t addr, void *thing); +herr_t small_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, + haddr_t addr, void *thing); +herr_t medium_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, + haddr_t addr, void *thing); +herr_t large_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, + haddr_t addr, void *thing); +herr_t huge_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, + haddr_t addr, void *thing); +herr_t monster_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, + haddr_t addr, void *thing); + + +static void * load(H5F_t UNUSED *f, hid_t UNUSED dxpl_id, haddr_t addr, + const void UNUSED *udata1, void UNUSED *udata2); + +void * pico_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2); +void * nano_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2); +void * micro_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2); +void * tiny_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2); +void * small_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2); +void * medium_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2); +void * large_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2); +void * huge_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2); +void * monster_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2); + + +static herr_t size(H5F_t UNUSED * f, void * thing, size_t * size_ptr); + +herr_t pico_size(H5F_t * f, void * thing, size_t * size_ptr); +herr_t nano_size(H5F_t * f, void * thing, size_t * size_ptr); +herr_t micro_size(H5F_t * f, void * thing, size_t * size_ptr); +herr_t tiny_size(H5F_t * f, void * thing, size_t * size_ptr); +herr_t small_size(H5F_t * f, void * thing, size_t * size_ptr); +herr_t medium_size(H5F_t * f, void * thing, size_t * size_ptr); +herr_t large_size(H5F_t * f, void * thing, size_t * size_ptr); +herr_t huge_size(H5F_t * f, void * thing, size_t * size_ptr); +herr_t monster_size(H5F_t * f, void * thing, size_t * size_ptr); + + +/* callback table declaration */ + +static const H5C_class_t types[NUMBER_OF_ENTRY_TYPES] = +{ + { + PICO_ENTRY_TYPE, + (H5C_load_func_t)pico_load, + (H5C_flush_func_t)pico_flush, + (H5C_dest_func_t)pico_dest, + (H5C_clear_func_t)pico_clear, + (H5C_size_func_t)pico_size + }, + { + NANO_ENTRY_TYPE, + (H5C_load_func_t)nano_load, + (H5C_flush_func_t)nano_flush, + (H5C_dest_func_t)nano_dest, + (H5C_clear_func_t)nano_clear, + (H5C_size_func_t)nano_size + }, + { + MICRO_ENTRY_TYPE, + (H5C_load_func_t)micro_load, + (H5C_flush_func_t)micro_flush, + (H5C_dest_func_t)micro_dest, + (H5C_clear_func_t)micro_clear, + (H5C_size_func_t)micro_size + }, + { + TINY_ENTRY_TYPE, + (H5C_load_func_t)tiny_load, + (H5C_flush_func_t)tiny_flush, + (H5C_dest_func_t)tiny_dest, + (H5C_clear_func_t)tiny_clear, + (H5C_size_func_t)tiny_size + }, + { + SMALL_ENTRY_TYPE, + (H5C_load_func_t)small_load, + (H5C_flush_func_t)small_flush, + (H5C_dest_func_t)small_dest, + (H5C_clear_func_t)small_clear, + (H5C_size_func_t)small_size + }, + { + MEDIUM_ENTRY_TYPE, + (H5C_load_func_t)medium_load, + (H5C_flush_func_t)medium_flush, + (H5C_dest_func_t)medium_dest, + (H5C_clear_func_t)medium_clear, + (H5C_size_func_t)medium_size + }, + { + LARGE_ENTRY_TYPE, + (H5C_load_func_t)large_load, + (H5C_flush_func_t)large_flush, + (H5C_dest_func_t)large_dest, + (H5C_clear_func_t)large_clear, + (H5C_size_func_t)large_size + }, + { + HUGE_ENTRY_TYPE, + (H5C_load_func_t)huge_load, + (H5C_flush_func_t)huge_flush, + (H5C_dest_func_t)huge_dest, + (H5C_clear_func_t)huge_clear, + (H5C_size_func_t)huge_size + }, + { + MONSTER_ENTRY_TYPE, + (H5C_load_func_t)monster_load, + (H5C_flush_func_t)monster_flush, + (H5C_dest_func_t)monster_dest, + (H5C_clear_func_t)monster_clear, + (H5C_size_func_t)monster_size + } +}; + + +/* private function declarations: */ + +static void addr_to_type_and_index(haddr_t addr, + int32_t * type_ptr, + int32_t * index_ptr); + +#if 0 /* keep this for a while -- it may be useful */ +static haddr_t type_and_index_to_addr(int32_t type, + int32_t idx); +#endif + +static void insert_entry(H5C_t * cache_ptr, + int32_t type, + int32_t idx, + hbool_t dirty); + +static void rename_entry(H5C_t * cache_ptr, + int32_t type, + int32_t idx, + hbool_t main_addr); + +static void protect_entry(H5C_t * cache_ptr, + int32_t type, + int32_t idx); + +hbool_t entry_in_cache(H5C_t * cache_ptr, + int32_t type, + int32_t idx); + +static void reset_entries(void); + +static H5C_t * setup_cache(size_t max_cache_size, size_t min_clean_size); + +static void row_major_scan_forward(H5C_t * cache_ptr, + int32_t lag, + hbool_t verbose, + hbool_t reset_stats, + hbool_t display_stats, + hbool_t display_detailed_stats, + hbool_t do_inserts, + hbool_t dirty_inserts, + hbool_t do_renames, + hbool_t rename_to_main_addr, + hbool_t do_destroys, + int dirty_destroys, + int dirty_unprotects); + +static void row_major_scan_backward(H5C_t * cache_ptr, + int32_t lag, + hbool_t verbose, + hbool_t reset_stats, + hbool_t display_stats, + hbool_t display_detailed_stats, + hbool_t do_inserts, + hbool_t dirty_inserts, + hbool_t do_renames, + hbool_t rename_to_main_addr, + hbool_t do_destroys, + int dirty_destroys, + int dirty_unprotects); + +static void col_major_scan_forward(H5C_t * cache_ptr, + int32_t lag, + hbool_t verbose, + hbool_t reset_stats, + hbool_t display_stats, + hbool_t display_detailed_stats, + hbool_t do_inserts, + hbool_t dirty_inserts, + int dirty_unprotects); + +static void col_major_scan_backward(H5C_t * cache_ptr, + int32_t lag, + hbool_t verbose, + hbool_t reset_stats, + hbool_t display_stats, + hbool_t display_detailed_stats, + hbool_t do_inserts, + hbool_t dirty_inserts, + int dirty_unprotects); + +static void smoke_check_1(void); +static void smoke_check_2(void); +static void smoke_check_3(void); +static void smoke_check_4(void); +static void write_permitted_check(void); +static void check_flush_protected_err(void); +static void check_destroy_protected_err(void); +static void check_duplicate_insert_err(void); +static void check_rename_err(void); +static void check_double_protect_err(void); +static void check_double_unprotect_err(void); + +static void takedown_cache(H5C_t * cache_ptr, + hbool_t dump_stats, + hbool_t dump_detailed_stats); + +static void flush_cache(H5C_t * cache_ptr, + hbool_t destroy_entries, + hbool_t dump_stats, + hbool_t dump_detailed_stats); + +static void unprotect_entry(H5C_t * cache_ptr, + int32_t type, + int32_t idx, + int dirty, + hbool_t deleted); + +static void verify_clean(void); + +static void verify_unprotected(void); + + + +/* address translation funtions: */ + +/*------------------------------------------------------------------------- + * Function: addr_to_type_and_index + * + * Purpose: Given an address, compute the type and index of the + * associated entry. + * + * Return: void + * + * Programmer: John Mainzer + * 6/10/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +addr_to_type_and_index(haddr_t addr, + int32_t * type_ptr, + int32_t * index_ptr) +{ + int i; + int32_t type; + int32_t idx; + + HDassert( type_ptr ); + HDassert( index_ptr ); + + /* we only have a small number of entry types, so just do a + * linear search. If NUMBER_OF_ENTRY_TYPES grows, we may want + * to do a binary search instead. + */ + i = 1; + if ( addr >= PICO_ALT_BASE_ADDR ) { + + while ( ( i < NUMBER_OF_ENTRY_TYPES ) && + ( addr >= alt_base_addrs[i] ) ) + { + i++; + } + + } else { + + while ( ( i < NUMBER_OF_ENTRY_TYPES ) && + ( addr >= base_addrs[i] ) ) + { + i++; + } + } + + type = i - 1; + + HDassert( ( type >= 0 ) && ( type < NUMBER_OF_ENTRY_TYPES ) ); + + if ( addr >= PICO_ALT_BASE_ADDR ) { + + idx = (addr - alt_base_addrs[type]) / entry_sizes[type]; + HDassert( !((entries[type])[idx].at_main_addr) ); + HDassert( addr == (entries[type])[idx].alt_addr ); + + } else { + + idx = (addr - base_addrs[type]) / entry_sizes[type]; + HDassert( (entries[type])[idx].at_main_addr ); + HDassert( addr == (entries[type])[idx].main_addr ); + } + + HDassert( ( idx >= 0 ) && ( idx <= max_indices[type] ) ); + + HDassert( addr == (entries[type])[idx].addr ); + + *type_ptr = type; + *index_ptr = idx; + + return; + +} /* addr_to_type_and_index() */ + + +#if 0 /* This function has never been used, but we may want it + * some time. Lets keep it for now. + */ +/*------------------------------------------------------------------------- + * Function: type_and_index_to_addr + * + * Purpose: Given a type and index of an entry, compute the associated + * addr and return that value. + * + * Return: computed addr + * + * Programmer: John Mainzer + * 6/10/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static haddr_t +type_and_index_to_addr(int32_t type, + int32_t idx) +{ + haddr_t addr; + + HDassert( ( type >= 0 ) && ( type < NUMBER_OF_ENTRY_TYPES ) ); + HDassert( ( idx >= 0 ) && ( idx <= max_indices[type] ) ); + + addr = base_addrs[type] + (((haddr_t)idx) * entry_sizes[type]); + + HDassert( addr == (entries[type])[idx].addr ); + + if ( (entries[type])[idx].at_main_addr ) { + + HDassert( addr == (entries[type])[idx].main_addr ); + + } else { + + HDassert( addr == (entries[type])[idx].alt_addr ); + } + + return(addr); + +} /* type_and_index_to_addr() */ + +#endif + + +/* Call back functions: */ + +/*------------------------------------------------------------------------- + * + * Function: H5AC_check_if_write_permitted + * + * Purpose: Determine if a write is permitted under the current + * circumstances, and set *write_permitted_ptr accordingly. + * As a general rule it is, but when we are running in parallel + * mode with collective I/O, we must ensure that a read cannot + * cause a write. + * + * In the event of failure, the value of *write_permitted_ptr + * is undefined. + * + * Return: Non-negative on success/Negative on failure. + * + * Programmer: John Mainzer, 5/15/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static herr_t +check_write_permitted(H5F_t UNUSED * f, + hid_t UNUSED dxpl_id, + hbool_t * write_permitted_ptr) +{ + + HDassert( write_permitted_ptr ); + *write_permitted_ptr = write_permitted; + + return(SUCCEED); + +} /* check_write_permitted() */ + + +/*------------------------------------------------------------------------- + * Function: clear & friends + * + * Purpose: clear the entry. The helper functions verify that the + * correct version of clear is being called, and then call + * clear proper. + * + * Return: SUCCEED + * + * Programmer: John Mainzer + * 6/10/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static herr_t +clear(H5F_t * f, + void * thing, + hbool_t dest) +{ + test_entry_t * entry_ptr; + test_entry_t * base_addr; + + HDassert( thing ); + + entry_ptr = (test_entry_t *)thing; + base_addr = entries[entry_ptr->type]; + + HDassert( entry_ptr->index >= 0 ); + HDassert( entry_ptr->index <= max_indices[entry_ptr->type] ); + HDassert( entry_ptr == &(base_addr[entry_ptr->index]) ); + HDassert( entry_ptr == entry_ptr->self ); + HDassert( entry_ptr->header.addr == entry_ptr->addr ); + HDassert( entry_ptr->header.size == entry_ptr->size ); + HDassert( entry_ptr->size == entry_sizes[entry_ptr->type] ); + + entry_ptr->header.is_dirty = FALSE; + entry_ptr->is_dirty = FALSE; + + if ( dest ) { + + destroy(f, thing); + + } + + return(SUCCEED); + +} /* clear() */ + +herr_t +pico_clear(H5F_t * f, void * thing, hbool_t dest) +{ + HDassert ( ((test_entry_t *)thing)->type == PICO_ENTRY_TYPE ); + return(clear(f, thing, dest)); +} + +herr_t +nano_clear(H5F_t * f, void * thing, hbool_t dest) +{ + HDassert ( ((test_entry_t *)thing)->type == NANO_ENTRY_TYPE ); + return(clear(f, thing, dest)); +} + +herr_t +micro_clear(H5F_t * f, void * thing, hbool_t dest) +{ + HDassert ( ((test_entry_t *)thing)->type == MICRO_ENTRY_TYPE ); + return(clear(f, thing, dest)); +} + +herr_t +tiny_clear(H5F_t * f, void * thing, hbool_t dest) +{ + HDassert ( ((test_entry_t *)thing)->type == TINY_ENTRY_TYPE ); + return(clear(f, thing, dest)); +} + +herr_t +small_clear(H5F_t * f, void * thing, hbool_t dest) +{ + HDassert ( ((test_entry_t *)thing)->type == SMALL_ENTRY_TYPE ); + return(clear(f, thing, dest)); +} + +herr_t +medium_clear(H5F_t * f, void * thing, hbool_t dest) +{ + HDassert ( ((test_entry_t *)thing)->type == MEDIUM_ENTRY_TYPE ); + return(clear(f, thing, dest)); +} + +herr_t +large_clear(H5F_t * f, void * thing, hbool_t dest) +{ + HDassert ( ((test_entry_t *)thing)->type == LARGE_ENTRY_TYPE ); + return(clear(f, thing, dest)); +} + +herr_t +huge_clear(H5F_t * f, void * thing, hbool_t dest) +{ + HDassert ( ((test_entry_t *)thing)->type == HUGE_ENTRY_TYPE ); + return(clear(f, thing, dest)); +} + +herr_t +monster_clear(H5F_t * f, void * thing, hbool_t dest) +{ + HDassert ( ((test_entry_t *)thing)->type == MONSTER_ENTRY_TYPE ); + return(clear(f, thing, dest)); +} + + +/*------------------------------------------------------------------------- + * Function: dest & friends + * + * Purpose: Destroy the entry. The helper functions verify that the + * correct version of dest is being called, and then call + * dest proper. + * + * Return: SUCCEED + * + * Programmer: John Mainzer + * 6/10/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static herr_t +destroy(H5F_t UNUSED * f, + void * thing) +{ + test_entry_t * entry_ptr; + test_entry_t * base_addr; + + HDassert( thing ); + + entry_ptr = (test_entry_t *)thing; + base_addr = entries[entry_ptr->type]; + + HDassert ( entry_ptr->index >= 0 ); + HDassert ( entry_ptr->index <= max_indices[entry_ptr->type] ); + HDassert( entry_ptr == &(base_addr[entry_ptr->index]) ); + HDassert( entry_ptr == entry_ptr->self ); + HDassert( entry_ptr->header.addr == entry_ptr->addr ); + HDassert( entry_ptr->header.size == entry_ptr->size ); + HDassert( entry_ptr->size == entry_sizes[entry_ptr->type] ); + + HDassert( !(entry_ptr->is_dirty) ); + HDassert( !(entry_ptr->header.is_dirty) ); + + return(SUCCEED); + +} /* dest() */ + +herr_t +pico_dest(H5F_t * f, void * thing) +{ + HDassert ( ((test_entry_t *)thing)->type == PICO_ENTRY_TYPE ); + return(destroy(f, thing)); +} + +herr_t +nano_dest(H5F_t * f, void * thing) +{ + HDassert ( ((test_entry_t *)thing)->type == NANO_ENTRY_TYPE ); + return(destroy(f, thing)); +} + +herr_t +micro_dest(H5F_t * f, void * thing) +{ + HDassert ( ((test_entry_t *)thing)->type == MICRO_ENTRY_TYPE ); + return(destroy(f, thing)); +} + +herr_t +tiny_dest(H5F_t * f, void * thing) +{ + HDassert ( ((test_entry_t *)thing)->type == TINY_ENTRY_TYPE ); + return(destroy(f, thing)); +} + +herr_t +small_dest(H5F_t * f, void * thing) +{ + HDassert ( ((test_entry_t *)thing)->type == SMALL_ENTRY_TYPE ); + return(destroy(f, thing)); +} + +herr_t +medium_dest(H5F_t * f, void * thing) +{ + HDassert ( ((test_entry_t *)thing)->type == MEDIUM_ENTRY_TYPE ); + return(destroy(f, thing)); +} + +herr_t +large_dest(H5F_t * f, void * thing) +{ + HDassert ( ((test_entry_t *)thing)->type == LARGE_ENTRY_TYPE ); + return(destroy(f, thing)); +} + +herr_t +huge_dest(H5F_t * f, void * thing) +{ + HDassert ( ((test_entry_t *)thing)->type == HUGE_ENTRY_TYPE ); + return(destroy(f, thing)); +} + +herr_t +monster_dest(H5F_t * f, void * thing) +{ + HDassert ( ((test_entry_t *)thing)->type == MONSTER_ENTRY_TYPE ); + return(destroy(f, thing)); +} + + +/*------------------------------------------------------------------------- + * Function: flush & friends + * + * Purpose: flush the entry and mark it as clean. The helper functions + * verify that the correct version of flush is being called, + * and then call flush proper. + * + * Return: SUCCEED + * + * Programmer: John Mainzer + * 6/10/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static herr_t +flush(H5F_t *f, + hid_t UNUSED dxpl_id, + hbool_t dest, + haddr_t addr, + void *thing) +{ + test_entry_t * entry_ptr; + test_entry_t * base_addr; + + HDassert( thing ); + + entry_ptr = (test_entry_t *)thing; + base_addr = entries[entry_ptr->type]; + + HDassert( entry_ptr->index >= 0 ); + HDassert( entry_ptr->index <= max_indices[entry_ptr->type] ); + HDassert( entry_ptr == &(base_addr[entry_ptr->index]) ); + HDassert( entry_ptr == entry_ptr->self ); + HDassert( entry_ptr->header.addr == entry_ptr->addr ); + HDassert( entry_ptr->addr == addr ); + HDassert( entry_ptr->header.size == entry_ptr->size ); + HDassert( entry_ptr->size == entry_sizes[entry_ptr->type] ); + + if ( ( ! write_permitted ) && ( entry_ptr->is_dirty ) ) { + + pass = FALSE; + failure_mssg = "called flush when write_permitted is FALSE."; + } + + if ( entry_ptr->is_dirty ) { + + (entry_ptr->writes)++; + entry_ptr->is_dirty = FALSE; + entry_ptr->header.is_dirty = FALSE; + } + + if ( dest ) { + + destroy(f, thing); + + } + + return(SUCCEED); + +} /* flush() */ + +herr_t +pico_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, haddr_t addr, void *thing) +{ + HDassert ( ((test_entry_t *)thing)->type == PICO_ENTRY_TYPE ); + return(flush(f, dxpl_id, dest, addr, thing)); +} + +herr_t +nano_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, haddr_t addr, void *thing) +{ + HDassert ( ((test_entry_t *)thing)->type == NANO_ENTRY_TYPE ); + return(flush(f, dxpl_id, dest, addr, thing)); +} + +herr_t +micro_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, haddr_t addr, void *thing) +{ + HDassert ( ((test_entry_t *)thing)->type == MICRO_ENTRY_TYPE ); + return(flush(f, dxpl_id, dest, addr, thing)); +} + +herr_t +tiny_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, haddr_t addr, void *thing) +{ + HDassert ( ((test_entry_t *)thing)->type == TINY_ENTRY_TYPE ); + return(flush(f, dxpl_id, dest, addr, thing)); +} + +herr_t +small_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, haddr_t addr, void *thing) +{ + HDassert ( ((test_entry_t *)thing)->type == SMALL_ENTRY_TYPE ); + return(flush(f, dxpl_id, dest, addr, thing)); +} + +herr_t +medium_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, haddr_t addr, void *thing) +{ + HDassert ( ((test_entry_t *)thing)->type == MEDIUM_ENTRY_TYPE ); + return(flush(f, dxpl_id, dest, addr, thing)); +} + +herr_t +large_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, haddr_t addr, void *thing) +{ + HDassert ( ((test_entry_t *)thing)->type == LARGE_ENTRY_TYPE ); + return(flush(f, dxpl_id, dest, addr, thing)); +} + +herr_t +huge_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, haddr_t addr, void *thing) +{ + HDassert ( ((test_entry_t *)thing)->type == HUGE_ENTRY_TYPE ); + return(flush(f, dxpl_id, dest, addr, thing)); +} + +herr_t +monster_flush(H5F_t *f, hid_t dxpl_id, hbool_t dest, haddr_t addr, void *thing) +{ + HDassert ( ((test_entry_t *)thing)->type == MONSTER_ENTRY_TYPE ); + return(flush(f, dxpl_id, dest, addr, thing)); +} + + +/*------------------------------------------------------------------------- + * Function: load & friends + * + * Purpose: "load" the requested entry and mark it as clean. The + * helper functions verify that the correct version of load + * is being called, and then call load proper. + * + * Return: SUCCEED + * + * Programmer: John Mainzer + * 6/10/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void * +load(H5F_t UNUSED *f, + hid_t UNUSED dxpl_id, + haddr_t addr, + const void UNUSED *udata1, + void UNUSED *udata2) +{ + int32_t type; + int32_t idx; + test_entry_t * entry_ptr; + test_entry_t * base_addr; + + addr_to_type_and_index(addr, &type, &idx); + + base_addr = entries[type]; + entry_ptr = &(base_addr[idx]); + + HDassert( entry_ptr->type == type ); + HDassert( entry_ptr->type >= 0 ); + HDassert( entry_ptr->type < NUMBER_OF_ENTRY_TYPES ); + HDassert( entry_ptr->index == idx ); + HDassert( entry_ptr->index >= 0 ); + HDassert( entry_ptr->index <= max_indices[type] ); + HDassert( entry_ptr == entry_ptr->self ); + HDassert( entry_ptr->addr == addr ); + HDassert( entry_ptr->size == entry_sizes[type] ); + + entry_ptr->is_dirty = FALSE; + + (entry_ptr->reads)++; + + return(entry_ptr); + +} /* load() */ + +void * +pico_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2) +{ + return(load(f, dxpl_id, addr, udata1, udata2)); +} + +void * +nano_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2) +{ + return(load(f, dxpl_id, addr, udata1, udata2)); +} + +void * +micro_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2) +{ + return(load(f, dxpl_id, addr, udata1, udata2)); +} + +void * +tiny_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2) +{ + return(load(f, dxpl_id, addr, udata1, udata2)); +} + +void * +small_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2) +{ + return(load(f, dxpl_id, addr, udata1, udata2)); +} + +void * +medium_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2) +{ + return(load(f, dxpl_id, addr, udata1, udata2)); +} + +void * +large_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2) +{ + return(load(f, dxpl_id, addr, udata1, udata2)); +} + +void * +huge_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2) +{ + return(load(f, dxpl_id, addr, udata1, udata2)); +} + +void * +monster_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, + const void *udata1, void *udata2) +{ + return(load(f, dxpl_id, addr, udata1, udata2)); +} + + +/*------------------------------------------------------------------------- + * Function: size & friends + * + * Purpose: Get the size of the specified entry. The helper functions + * verify that the correct version of size is being called, + * and then call size proper. + * + * Return: SUCCEED + * + * Programmer: John Mainzer + * 6/10/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static herr_t +size(H5F_t UNUSED * f, + void * thing, + size_t * size_ptr) +{ + test_entry_t * entry_ptr; + test_entry_t * base_addr; + + HDassert( size_ptr ); + HDassert( thing ); + + entry_ptr = (test_entry_t *)thing; + base_addr = entries[entry_ptr->type]; + + HDassert( entry_ptr->index >= 0 ); + HDassert( entry_ptr->index <= max_indices[entry_ptr->type] ); + HDassert( entry_ptr == &(base_addr[entry_ptr->index]) ); + HDassert( entry_ptr == entry_ptr->self ); + HDassert( entry_ptr->header.addr == entry_ptr->addr ); + HDassert( entry_ptr->size == entry_sizes[entry_ptr->type] ); + + *size_ptr = entry_ptr->size; + + return(SUCCEED); + +} /* size() */ + +herr_t +pico_size(H5F_t * f, void * thing, size_t * size_ptr) +{ + HDassert ( ((test_entry_t *)thing)->type == PICO_ENTRY_TYPE ); + return(size(f, thing, size_ptr)); +} + +herr_t +nano_size(H5F_t * f, void * thing, size_t * size_ptr) +{ + HDassert ( ((test_entry_t *)thing)->type == NANO_ENTRY_TYPE ); + return(size(f, thing, size_ptr)); +} + +herr_t +micro_size(H5F_t * f, void * thing, size_t * size_ptr) +{ + HDassert ( ((test_entry_t *)thing)->type == MICRO_ENTRY_TYPE ); + return(size(f, thing, size_ptr)); +} + +herr_t +tiny_size(H5F_t * f, void * thing, size_t * size_ptr) +{ + HDassert ( ((test_entry_t *)thing)->type == TINY_ENTRY_TYPE ); + return(size(f, thing, size_ptr)); +} + +herr_t +small_size(H5F_t * f, void * thing, size_t * size_ptr) +{ + HDassert ( ((test_entry_t *)thing)->type == SMALL_ENTRY_TYPE ); + return(size(f, thing, size_ptr)); +} + +herr_t +medium_size(H5F_t * f, void * thing, size_t * size_ptr) +{ + HDassert ( ((test_entry_t *)thing)->type == MEDIUM_ENTRY_TYPE ); + return(size(f, thing, size_ptr)); +} + +herr_t +large_size(H5F_t * f, void * thing, size_t * size_ptr) +{ + HDassert ( ((test_entry_t *)thing)->type == LARGE_ENTRY_TYPE ); + return(size(f, thing, size_ptr)); +} + +herr_t +huge_size(H5F_t * f, void * thing, size_t * size_ptr) +{ + HDassert ( ((test_entry_t *)thing)->type == HUGE_ENTRY_TYPE ); + return(size(f, thing, size_ptr)); +} + +herr_t +monster_size(H5F_t * f, void * thing, size_t * size_ptr) +{ + HDassert ( ((test_entry_t *)thing)->type == MONSTER_ENTRY_TYPE ); + return(size(f, thing, size_ptr)); +} + + +/**************************************************************************/ +/**************************************************************************/ +/************************** test utility functions: ***********************/ +/**************************************************************************/ +/**************************************************************************/ + +/*------------------------------------------------------------------------- + * Function: entry_in_cache + * + * Purpose: Given a pointer to a cache, an entry type, and an index, + * determine if the entry is currently in the cache. + * + * Return: TRUE if the entry is in the cache, and FALSE otherwise. + * + * Programmer: John Mainzer + * 6/10/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +hbool_t +entry_in_cache(H5C_t * cache_ptr, + int32_t type, + int32_t idx) +{ + hbool_t in_cache = FALSE; /* will set to TRUE if necessary */ + test_entry_t * base_addr; + test_entry_t * entry_ptr; + H5C_cache_entry_t * test_ptr = NULL; + + HDassert( cache_ptr ); + HDassert( ( 0 <= type ) && ( type < NUMBER_OF_ENTRY_TYPES ) ); + HDassert( ( 0 <= idx ) && ( idx <= max_indices[type] ) ); + + base_addr = entries[type]; + entry_ptr = &(base_addr[idx]); + + HDassert( entry_ptr->index == idx ); + HDassert( entry_ptr->type == type ); + HDassert( entry_ptr == entry_ptr->self ); + + H5C__SEARCH_INDEX(((local_H5C_t *)cache_ptr), entry_ptr->addr, test_ptr) + + if ( test_ptr != NULL ) { + + in_cache = TRUE; + HDassert( test_ptr == (H5C_cache_entry_t *)entry_ptr ); + HDassert( entry_ptr->addr == entry_ptr->header.addr ); + } + + return(in_cache); + +} /* entry_in_cache() */ + + +/*------------------------------------------------------------------------- + * Function: reset_entries + * + * Purpose: reset the contents of the entries arrays to know values. + * + * Return: void + * + * Programmer: John Mainzer + * 6/10/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +reset_entries(void) + +{ + int i; + int j; + int32_t max_index; + haddr_t addr = 0; + haddr_t alt_addr = PICO_ALT_BASE_ADDR; + size_t entry_size; + test_entry_t * base_addr; + + for ( i = 0; i < NUMBER_OF_ENTRY_TYPES; i++ ) + { + entry_size = entry_sizes[i]; + max_index = max_indices[i]; + base_addr = entries[i]; + + HDassert( base_addr ); + + for ( j = 0; j <= max_index; j++ ) + { + /* one can argue that we should fill the header with garbage. + * If this is desired, we can simply comment out the header + * initialization - the headers will be full of garbage soon + * enough. + */ + + base_addr[j].header.addr = (haddr_t)0; + base_addr[j].header.size = (size_t)0; + base_addr[j].header.type = NULL; + base_addr[j].header.is_dirty = FALSE; + base_addr[j].header.is_protected = FALSE; + base_addr[j].header.next = NULL; + base_addr[j].header.prev = NULL; + base_addr[j].header.aux_next = NULL; + base_addr[j].header.aux_prev = NULL; + + base_addr[j].self = &(base_addr[j]); + base_addr[j].addr = addr; + base_addr[j].at_main_addr = TRUE; + base_addr[j].main_addr = addr; + base_addr[j].alt_addr = alt_addr; + base_addr[j].size = entry_size; + base_addr[j].type = i; + base_addr[j].index = j; + base_addr[j].reads = 0; + base_addr[j].writes = 0; + base_addr[j].is_dirty = FALSE; + base_addr[j].is_protected = FALSE; + + addr += (haddr_t)entry_size; + alt_addr += (haddr_t)entry_size; + } + } + + return; + +} /* reset_entries() */ + + +/*------------------------------------------------------------------------- + * Function: verify_clean + * + * Purpose: Verify that all cache entries are marked as clean. If any + * are not, set pass to FALSE. + * + * Do nothing if pass is FALSE on entry. + * + * Return: void + * + * Programmer: John Mainzer + * 6/10/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +verify_clean(void) + +{ + int i; + int j; + int dirty_count = 0; + int32_t max_index; + test_entry_t * base_addr; + + if ( pass ) { + + for ( i = 0; i < NUMBER_OF_ENTRY_TYPES; i++ ) + { + max_index = max_indices[i]; + base_addr = entries[i]; + + HDassert( base_addr ); + + for ( j = 0; j <= max_index; j++ ) + { + if ( ( base_addr[j].header.is_dirty ) || ( base_addr[j].is_dirty ) ) { + + dirty_count++; + } + } + } + + if ( dirty_count > 0 ) { + + pass = FALSE; + failure_mssg = "verify_clean() found dirty entry(s)."; + } + } + + return; + +} /* verify_clean() */ + + +/*------------------------------------------------------------------------- + * Function: verify_unprotected + * + * Purpose: Verify that no cache entries are marked as protected. If + * any are, set pass to FALSE. + * + * Do nothing if pass is FALSE on entry. + * + * Return: void + * + * Programmer: John Mainzer + * 6/10/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +verify_unprotected(void) + +{ + int i; + int j; + int protected_count = 0; + int32_t max_index; + test_entry_t * base_addr; + + if ( pass ) { + + for ( i = 0; i < NUMBER_OF_ENTRY_TYPES; i++ ) + { + max_index = max_indices[i]; + base_addr = entries[i]; + + HDassert( base_addr ); + + for ( j = 0; j <= max_index; j++ ) + { + HDassert( base_addr[j].header.is_protected == + base_addr[j].is_protected ); + + if ( ( base_addr[j].header.is_protected ) || + ( base_addr[j].is_protected ) ) { + + protected_count++; + } + } + } + + if ( protected_count > 0 ) { + + pass = FALSE; + failure_mssg = "verify_unprotected() found protected entry(s)."; + } + } + + return; + +} /* verify_unprotected() */ + + +/*------------------------------------------------------------------------- + * Function: setup_cache() + * + * Purpose: Allocate a cache of the desired size and configure it for + * use in the test bed. Return a pointer to the new cache + * structure. + * + * Return: Pointer to new cache, or NULL on failure. + * + * Programmer: John Mainzer + * 6/11/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static H5C_t * +setup_cache(size_t max_cache_size, + size_t min_clean_size) +{ + H5C_t * cache_ptr = NULL; + + cache_ptr = H5C_create(max_cache_size, + min_clean_size, + (NUMBER_OF_ENTRY_TYPES - 1), + (const char **)entry_type_names, + check_write_permitted); + + if ( cache_ptr == NULL ) { + + pass = FALSE; + failure_mssg = "H5C_create() returned NULL."; + + } else { + + H5C_set_skip_flags(cache_ptr, TRUE, TRUE); + } + + return(cache_ptr); + +} /* setup_cache() */ + + +/*------------------------------------------------------------------------- + * Function: takedown_cache() + * + * Purpose: Flush the specified cache and disable it. If requested, + * dump stats first. If pass is FALSE, do nothing. + * + * Return: void + * + * Programmer: John Mainzer + * 6/11/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +takedown_cache(H5C_t * cache_ptr, + hbool_t dump_stats, + hbool_t dump_detailed_stats) +{ + HDassert(cache_ptr); + + if ( pass ) { + + if ( dump_stats ) { + + H5C_stats(cache_ptr, "test cache", dump_detailed_stats); + } + + H5C_dest(NULL, -1, -1, cache_ptr); + } + + return; + +} /* takedown_cache() */ + + +/*------------------------------------------------------------------------- + * Function: flush_cache() + * + * Purpose: Flush the specified cache, destroying all entries if + requested. If requested, dump stats first. + * + * Return: void + * + * Programmer: John Mainzer + * 6/23/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +flush_cache(H5C_t * cache_ptr, + hbool_t destroy_entries, + hbool_t dump_stats, + hbool_t dump_detailed_stats) +{ + herr_t result = 0; + + HDassert(cache_ptr); + + verify_unprotected(); + + if ( pass ) { + + if ( destroy_entries ) { + + result = H5C_flush_cache(NULL, -1, -1, cache_ptr, + H5F_FLUSH_INVALIDATE); + + } else { + + result = H5C_flush_cache(NULL, -1, -1, cache_ptr, 0); + } + } + + if ( dump_stats ) { + + H5C_stats(cache_ptr, "test cache", dump_detailed_stats); + } + + if ( result < 0 ) { + + pass = FALSE; + failure_mssg = "error in H5C_flush_cache()."; + } + + return; + +} /* flush_cache() */ + + +/*------------------------------------------------------------------------- + * Function: insert_entry() + * + * Purpose: Insert the entry indicated by the type and index. Mark + * it clean or dirty as indicated. + * + * Note that I don't see much practical use for inserting + * a clean entry, but the interface permits it so we should + * test it. + * + * Do nothing if pass is false. + * + * Return: void + * + * Programmer: John Mainzer + * 6/16/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +insert_entry(H5C_t * cache_ptr, + int32_t type, + int32_t idx, + hbool_t dirty) +{ + herr_t result; + test_entry_t * base_addr; + test_entry_t * entry_ptr; + + if ( pass ) { + + HDassert( cache_ptr ); + HDassert( ( 0 <= type ) && ( type < NUMBER_OF_ENTRY_TYPES ) ); + HDassert( ( 0 <= idx ) && ( idx <= max_indices[type] ) ); + + base_addr = entries[type]; + entry_ptr = &(base_addr[idx]); + + HDassert( entry_ptr->index == idx ); + HDassert( entry_ptr->type == type ); + HDassert( entry_ptr == entry_ptr->self ); + HDassert( !(entry_ptr->is_protected) ); + + if ( dirty ) { + + (entry_ptr->header).is_dirty = dirty; + entry_ptr->is_dirty = dirty; + } + + result = H5C_insert_entry(NULL, -1, -1, cache_ptr, &(types[type]), + entry_ptr->addr, (void *)entry_ptr); + + if ( ( result < 0 ) || + ( entry_ptr->header.is_protected ) || + ( entry_ptr->header.type != &(types[type]) ) || + ( entry_ptr->size != entry_ptr->header.size ) || + ( entry_ptr->addr != entry_ptr->header.addr ) ) { + + pass = FALSE; + failure_mssg = "error in H5C_insert()."; + } + + HDassert( ((entry_ptr->header).type)->id == type ); + } + + return; + +} /* insert_entry() */ + + +/*------------------------------------------------------------------------- + * Function: rename_entry() + * + * Purpose: Rename the entry indicated by the type and index to its + * main or alternate address as indicated. If the entry is + * already at the desired entry, do nothing. + * + * Return: void + * + * Programmer: John Mainzer + * 6/21/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +rename_entry(H5C_t * cache_ptr, + int32_t type, + int32_t idx, + hbool_t main_addr) +{ + herr_t result; + hbool_t done = TRUE; /* will set to FALSE if we have work to do */ + haddr_t old_addr = HADDR_UNDEF; + haddr_t new_addr = HADDR_UNDEF; + test_entry_t * base_addr; + test_entry_t * entry_ptr; + + HDassert( cache_ptr ); + HDassert( ( 0 <= type ) && ( type < NUMBER_OF_ENTRY_TYPES ) ); + HDassert( ( 0 <= idx ) && ( idx <= max_indices[type] ) ); + + base_addr = entries[type]; + entry_ptr = &(base_addr[idx]); + + HDassert( entry_ptr->index == idx ); + HDassert( entry_ptr->type == type ); + HDassert( entry_ptr == entry_ptr->self ); + HDassert( !(entry_ptr->is_protected) ); + HDassert( !(entry_ptr->header.is_protected) ); + + if ( entry_ptr->at_main_addr && !main_addr ) { + + /* rename to alt addr */ + + HDassert( entry_ptr->addr == entry_ptr->main_addr ); + + done = FALSE; + old_addr = entry_ptr->addr; + new_addr = entry_ptr->alt_addr; + + } else if ( !(entry_ptr->at_main_addr) && main_addr ) { + + /* rename to main addr */ + + HDassert( entry_ptr->addr == entry_ptr->alt_addr ); + + done = FALSE; + old_addr = entry_ptr->addr; + new_addr = entry_ptr->main_addr; + } + + if ( ! done ) { + + result = H5C_rename_entry(NULL, cache_ptr, &(types[type]), + old_addr, new_addr); + } + + if ( ! done ) { + + if ( ( result < 0 ) || ( entry_ptr->header.addr != new_addr ) ) { + + pass = FALSE; + failure_mssg = "error in H5C_rename_entry()."; + + } else { + + entry_ptr->addr = new_addr; + entry_ptr->at_main_addr = main_addr; + } + } + + HDassert( ((entry_ptr->header).type)->id == type ); + + return; + +} /* insert_entry() */ + + +/*------------------------------------------------------------------------- + * Function: protect_entry() + * + * Purpose: Protect the entry indicated by the type and index. + * + * Do nothing if pass is FALSE on entry. + * + * Return: void + * + * Programmer: John Mainzer + * 6/11/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +protect_entry(H5C_t * cache_ptr, + int32_t type, + int32_t idx) +{ + /* const char * fcn_name = "protect_entry()"; */ + test_entry_t * base_addr; + test_entry_t * entry_ptr; + H5C_cache_entry_t * cache_entry_ptr; + + if ( pass ) { + + HDassert( cache_ptr ); + HDassert( ( 0 <= type ) && ( type < NUMBER_OF_ENTRY_TYPES ) ); + HDassert( ( 0 <= idx ) && ( idx <= max_indices[type] ) ); + + base_addr = entries[type]; + entry_ptr = &(base_addr[idx]); + + HDassert( entry_ptr->index == idx ); + HDassert( entry_ptr->type == type ); + HDassert( entry_ptr == entry_ptr->self ); + HDassert( !(entry_ptr->is_protected) ); + + cache_entry_ptr = H5C_protect(NULL, -1, -1, cache_ptr, &(types[type]), + entry_ptr->addr, NULL, NULL); + + if ( ( cache_entry_ptr != (void *)entry_ptr ) || + ( !(entry_ptr->header.is_protected) ) || + ( entry_ptr->header.type != &(types[type]) ) || + ( entry_ptr->size != entry_ptr->header.size ) || + ( entry_ptr->addr != entry_ptr->header.addr ) ) { + + pass = FALSE; + failure_mssg = "error in H5C_protect()."; + + } else { + + entry_ptr->is_protected = TRUE; + + } + + HDassert( ((entry_ptr->header).type)->id == type ); + } + + return; + +} /* protect_entry() */ + + +/*------------------------------------------------------------------------- + * Function: unprotect_entry() + * + * Purpose: Unprotect the entry indicated by the type and index. + * + * Do nothing if pass is FALSE on entry. + * + * Return: void + * + * Programmer: John Mainzer + * 6/12/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +#define NO_CHANGE -1 + +static void +unprotect_entry(H5C_t * cache_ptr, + int32_t type, + int32_t idx, + int dirty, + hbool_t deleted) +{ + /* const char * fcn_name = "unprotect_entry()"; */ + herr_t result; + test_entry_t * base_addr; + test_entry_t * entry_ptr; + + if ( pass ) { + + HDassert( cache_ptr ); + HDassert( ( 0 <= type ) && ( type < NUMBER_OF_ENTRY_TYPES ) ); + HDassert( ( 0 <= idx ) && ( idx <= max_indices[type] ) ); + + base_addr = entries[type]; + entry_ptr = &(base_addr[idx]); + + HDassert( entry_ptr->index == idx ); + HDassert( entry_ptr->type == type ); + HDassert( entry_ptr == entry_ptr->self ); + HDassert( entry_ptr->header.is_protected ); + HDassert( entry_ptr->is_protected ); + + if ( ( dirty == TRUE ) || ( dirty == FALSE ) ) { + + entry_ptr->header.is_dirty = dirty; + entry_ptr->is_dirty = dirty; + } + + result = H5C_unprotect(NULL, -1, -1, cache_ptr, &(types[type]), + entry_ptr->addr, (void *)entry_ptr, deleted); + + if ( ( result < 0 ) || + ( entry_ptr->header.is_protected ) || + ( entry_ptr->header.type != &(types[type]) ) || + ( entry_ptr->size != entry_ptr->header.size ) || + ( entry_ptr->addr != entry_ptr->header.addr ) ) { + + pass = FALSE; + failure_mssg = "error in H5C_unprotect()."; + + } + else + { + entry_ptr->is_protected = FALSE; + } + + HDassert( ((entry_ptr->header).type)->id == type ); + } + + return; + +} /* unprotect_entry() */ + + +/*------------------------------------------------------------------------- + * Function: row_major_scan_forward() + * + * Purpose: Do a sequence of inserts, protects, unprotects, renames, + * destroys while scanning through the set of entries. If + * pass is false on entry, do nothing. + * + * Return: void + * + * Programmer: John Mainzer + * 6/12/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +row_major_scan_forward(H5C_t * cache_ptr, + int32_t lag, + hbool_t verbose, + hbool_t reset_stats, + hbool_t display_stats, + hbool_t display_detailed_stats, + hbool_t do_inserts, + hbool_t dirty_inserts, + hbool_t do_renames, + hbool_t rename_to_main_addr, + hbool_t do_destroys, + int dirty_destroys, + int dirty_unprotects) +{ + const char * fcn_name = "row_major_scan_forward"; + int32_t type; + int32_t idx; + + if ( verbose ) + HDfprintf(stdout, "%s(): entering.\n", fcn_name); + + HDassert( lag > 5 ); + + type = 0; + + if ( ( pass ) && ( reset_stats ) ) { + + H5C_stats__reset(cache_ptr); + } + + while ( ( pass ) && ( type < NUMBER_OF_ENTRY_TYPES ) ) + { + idx = -lag; + + while ( ( pass ) && ( idx <= (max_indices[type] + lag) ) ) + { + if ( ( pass ) && ( do_inserts ) && ( (idx + lag) >= 0 ) && + ( (idx + lag) <= max_indices[type] ) && + ( ((idx + lag) % 2) == 0 ) && + ( ! entry_in_cache(cache_ptr, type, (idx + lag)) ) ) { + + if ( verbose ) + HDfprintf(stdout, "(i, %d, %d) ", type, (idx + lag)); + + insert_entry(cache_ptr, type, (idx + lag), dirty_inserts); + } + + + if ( ( pass ) && ( (idx + lag - 1) >= 0 ) && + ( (idx + lag - 1) <= max_indices[type] ) && + ( ( (idx + lag - 1) % 3 ) == 0 ) ) { + + if ( verbose ) + HDfprintf(stdout, "(p, %d, %d) ", type, (idx + lag - 1)); + + protect_entry(cache_ptr, type, (idx + lag - 1)); + } + + if ( ( pass ) && ( (idx + lag - 2) >= 0 ) && + ( (idx + lag - 2) <= max_indices[type] ) && + ( ( (idx + lag - 2) % 3 ) == 0 ) ) { + + if ( verbose ) + HDfprintf(stdout, "(u, %d, %d) ", type, (idx + lag - 2)); + + unprotect_entry(cache_ptr, type, idx+lag-2, NO_CHANGE, FALSE); + } + + + if ( ( pass ) && ( do_renames ) && ( (idx + lag - 2) >= 0 ) && + ( (idx + lag - 2) <= max_indices[type] ) && + ( ( (idx + lag - 2) % 3 ) == 0 ) ) { + + rename_entry(cache_ptr, type, (idx + lag - 2), + rename_to_main_addr); + } + + + if ( ( pass ) && ( (idx + lag - 3) >= 0 ) && + ( (idx + lag - 3) <= max_indices[type] ) && + ( ( (idx + lag - 3) % 5 ) == 0 ) ) { + + if ( verbose ) + HDfprintf(stdout, "(p, %d, %d) ", type, (idx + lag - 3)); + + protect_entry(cache_ptr, type, (idx + lag - 3)); + } + + if ( ( pass ) && ( (idx + lag - 5) >= 0 ) && + ( (idx + lag - 5) <= max_indices[type] ) && + ( ( (idx + lag - 5) % 5 ) == 0 ) ) { + + if ( verbose ) + HDfprintf(stdout, "(u, %d, %d) ", type, (idx + lag - 5)); + + unprotect_entry(cache_ptr, type, idx+lag-5, NO_CHANGE, FALSE); + } + + if ( ( pass ) && ( idx >= 0 ) && ( idx <= max_indices[type] ) ) { + + if ( verbose ) + HDfprintf(stdout, "(p, %d, %d) ", type, idx); + + protect_entry(cache_ptr, type, idx); + } + + + if ( ( pass ) && ( (idx - lag + 2) >= 0 ) && + ( (idx - lag + 2) <= max_indices[type] ) && + ( ( (idx - lag + 2) % 7 ) == 0 ) ) { + + if ( verbose ) + HDfprintf(stdout, "(u, %d, %d) ", type, (idx - lag + 2)); + + unprotect_entry(cache_ptr, type, idx-lag+2, NO_CHANGE, FALSE); + } + + if ( ( pass ) && ( (idx - lag + 1) >= 0 ) && + ( (idx - lag + 1) <= max_indices[type] ) && + ( ( (idx - lag + 1) % 7 ) == 0 ) ) { + + if ( verbose ) + HDfprintf(stdout, "(p, %d, %d) ", type, (idx - lag + 1)); + + protect_entry(cache_ptr, type, (idx - lag + 1)); + } + + + if ( do_destroys ) { + + if ( ( pass ) && ( (idx - lag) >= 0 ) && + ( ( idx - lag) <= max_indices[type] ) ) { + + switch ( (idx - lag) %4 ) { + + case 0: /* we just did an insert */ + unprotect_entry(cache_ptr, type, idx - lag, + NO_CHANGE, FALSE); + break; + + case 1: + if ( (entries[type])[idx-lag].is_dirty ) { + + unprotect_entry(cache_ptr, type, idx - lag, + NO_CHANGE, FALSE); + } else { + + unprotect_entry(cache_ptr, type, idx - lag, + dirty_unprotects, FALSE); + } + break; + + case 2: /* we just did an insrt */ + unprotect_entry(cache_ptr, type, idx - lag, + NO_CHANGE, TRUE); + break; + + case 3: + if ( (entries[type])[idx-lag].is_dirty ) { + + unprotect_entry(cache_ptr, type, idx - lag, + NO_CHANGE, TRUE); + } else { + + unprotect_entry(cache_ptr, type, idx - lag, + dirty_destroys, TRUE); + } + break; + + default: + HDassert(0); /* this can't happen... */ + break; + } + } + + } else { + + if ( ( pass ) && ( (idx - lag) >= 0 ) && + ( ( idx - lag) <= max_indices[type] ) ) { + + if ( verbose ) + HDfprintf(stdout, "(u, %d, %d) ", type, (idx - lag)); + + unprotect_entry(cache_ptr, type, idx - lag, + dirty_unprotects, FALSE); + } + } + + if ( verbose ) + HDfprintf(stdout, "\n"); + + idx++; + } + type++; + } + + if ( ( pass ) && ( display_stats ) ) { + + H5C_stats(cache_ptr, "test cache", display_detailed_stats); + } + + return; + +} /* row_major_scan_forward() */ + + +/*------------------------------------------------------------------------- + * Function: row_major_scan_backward() + * + * Purpose: Do a sequence of inserts, protects, unprotects, renames, + * destroys while scanning backwards through the set of + * entries. If pass is false on entry, do nothing. + * + * Return: void + * + * Programmer: John Mainzer + * 6/12/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +row_major_scan_backward(H5C_t * cache_ptr, + int32_t lag, + hbool_t verbose, + hbool_t reset_stats, + hbool_t display_stats, + hbool_t display_detailed_stats, + hbool_t do_inserts, + hbool_t dirty_inserts, + hbool_t do_renames, + hbool_t rename_to_main_addr, + hbool_t do_destroys, + int dirty_destroys, + int dirty_unprotects) +{ + const char * fcn_name = "row_major_scan_backward"; + int32_t type; + int32_t idx; + + if ( verbose ) + HDfprintf(stdout, "%s(): Entering.\n", fcn_name); + + HDassert( lag > 5 ); + + type = NUMBER_OF_ENTRY_TYPES - 1; + + if ( ( pass ) && ( reset_stats ) ) { + + H5C_stats__reset(cache_ptr); + } + + while ( ( pass ) && ( type >= 0 ) ) + { + idx = max_indices[type] + lag; + + while ( ( pass ) && ( idx >= -lag ) ) + { + if ( ( pass ) && ( do_inserts ) && ( (idx - lag) >= 0 ) && + ( (idx - lag) <= max_indices[type] ) && + ( ((idx - lag) % 2) == 1 ) && + ( ! entry_in_cache(cache_ptr, type, (idx - lag)) ) ) { + + if ( verbose ) + HDfprintf(stdout, "(i, %d, %d) ", type, (idx - lag)); + + insert_entry(cache_ptr, type, (idx - lag), dirty_inserts); + } + + + if ( ( pass ) && ( (idx - lag + 1) >= 0 ) && + ( (idx - lag + 1) <= max_indices[type] ) && + ( ( (idx - lag + 1) % 3 ) == 0 ) ) { + + if ( verbose ) + HDfprintf(stdout, "(p, %d, %d) ", type, (idx - lag + 1)); + + protect_entry(cache_ptr, type, (idx - lag + 1)); + } + + if ( ( pass ) && ( (idx - lag + 2) >= 0 ) && + ( (idx - lag + 2) <= max_indices[type] ) && + ( ( (idx - lag + 2) % 3 ) == 0 ) ) { + + if ( verbose ) + HDfprintf(stdout, "(u, %d, %d) ", type, (idx - lag + 2)); + + unprotect_entry(cache_ptr, type, idx-lag+2, NO_CHANGE, FALSE); + } + + + if ( ( pass ) && ( do_renames ) && ( (idx - lag + 2) >= 0 ) && + ( (idx - lag + 2) <= max_indices[type] ) && + ( ( (idx - lag + 2) % 3 ) == 0 ) ) { + + rename_entry(cache_ptr, type, (idx - lag + 2), + rename_to_main_addr); + } + + + if ( ( pass ) && ( (idx - lag + 3) >= 0 ) && + ( (idx - lag + 3) <= max_indices[type] ) && + ( ( (idx - lag + 3) % 5 ) == 0 ) ) { + + if ( verbose ) + HDfprintf(stdout, "(p, %d, %d) ", type, (idx - lag + 3)); + + protect_entry(cache_ptr, type, (idx - lag + 3)); + } + + if ( ( pass ) && ( (idx - lag + 5) >= 0 ) && + ( (idx - lag + 5) <= max_indices[type] ) && + ( ( (idx - lag + 5) % 5 ) == 0 ) ) { + + if ( verbose ) + HDfprintf(stdout, "(u, %d, %d) ", type, (idx - lag + 5)); + + unprotect_entry(cache_ptr, type, idx-lag+5, NO_CHANGE, FALSE); + } + + if ( ( pass ) && ( idx >= 0 ) && ( idx <= max_indices[type] ) ) { + + if ( verbose ) + HDfprintf(stdout, "(p, %d, %d) ", type, idx); + + protect_entry(cache_ptr, type, idx); + } + + + if ( ( pass ) && ( (idx + lag - 2) >= 0 ) && + ( (idx + lag - 2) <= max_indices[type] ) && + ( ( (idx + lag - 2) % 7 ) == 0 ) ) { + + if ( verbose ) + HDfprintf(stdout, "(u, %d, %d) ", type, (idx + lag - 2)); + + unprotect_entry(cache_ptr, type, idx+lag-2, NO_CHANGE, FALSE); + } + + if ( ( pass ) && ( (idx + lag - 1) >= 0 ) && + ( (idx + lag - 1) <= max_indices[type] ) && + ( ( (idx + lag - 1) % 7 ) == 0 ) ) { + + if ( verbose ) + HDfprintf(stdout, "(p, %d, %d) ", type, (idx + lag - 1)); + + protect_entry(cache_ptr, type, (idx + lag - 1)); + } + + + if ( do_destroys ) { + + if ( ( pass ) && ( (idx + lag) >= 0 ) && + ( ( idx + lag) <= max_indices[type] ) ) { + + switch ( (idx + lag) %4 ) { + + case 0: + if ( (entries[type])[idx+lag].is_dirty ) { + + unprotect_entry(cache_ptr, type, idx + lag, + NO_CHANGE, FALSE); + } else { + + unprotect_entry(cache_ptr, type, idx + lag, + dirty_unprotects, FALSE); + } + break; + + case 1: /* we just did an insert */ + unprotect_entry(cache_ptr, type, idx + lag, + NO_CHANGE, FALSE); + break; + + case 2: + if ( (entries[type])[idx + lag].is_dirty ) { + + unprotect_entry(cache_ptr, type, idx + lag, + NO_CHANGE, TRUE); + } else { + + unprotect_entry(cache_ptr, type, idx + lag, + dirty_destroys, TRUE); + } + break; + + case 3: /* we just did an insrt */ + unprotect_entry(cache_ptr, type, idx + lag, + NO_CHANGE, TRUE); + break; + + default: + HDassert(0); /* this can't happen... */ + break; + } + } + } else { + + if ( ( pass ) && ( (idx + lag) >= 0 ) && + ( ( idx + lag) <= max_indices[type] ) ) { + + if ( verbose ) + HDfprintf(stdout, "(u, %d, %d) ", type, (idx - lag)); + + unprotect_entry(cache_ptr, type, idx + lag, + dirty_unprotects, FALSE); + } + } + + if ( verbose ) + HDfprintf(stdout, "\n"); + + idx--; + } + type--; + } + + if ( ( pass ) && ( display_stats ) ) { + + H5C_stats(cache_ptr, "test cache", display_detailed_stats); + } + + return; + +} /* row_major_scan_backward() */ + + +/*------------------------------------------------------------------------- + * Function: col_major_scan_forward() + * + * Purpose: Do a sequence of inserts, protects, and unprotects + * while scanning through the set of entries. If + * pass is false on entry, do nothing. + * + * Return: void + * + * Programmer: John Mainzer + * 6/23/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +col_major_scan_forward(H5C_t * cache_ptr, + int32_t lag, + hbool_t verbose, + hbool_t reset_stats, + hbool_t display_stats, + hbool_t display_detailed_stats, + hbool_t do_inserts, + hbool_t dirty_inserts, + int dirty_unprotects) +{ + const char * fcn_name = "col_major_scan_forward()"; + int32_t type; + int32_t idx; + + if ( verbose ) + HDfprintf(stdout, "%s: entering.\n", fcn_name); + + HDassert( lag > 5 ); + + type = 0; + + if ( ( pass ) && ( reset_stats ) ) { + + H5C_stats__reset(cache_ptr); + } + + idx = -lag; + + while ( ( pass ) && ( (idx - lag) <= MAX_ENTRIES ) ) + { + type = 0; + + while ( ( pass ) && ( type < NUMBER_OF_ENTRY_TYPES ) ) + { + if ( ( pass ) && ( do_inserts ) && ( (idx + lag) >= 0 ) && + ( (idx + lag) <= max_indices[type] ) && + ( ((idx + lag) % 3) == 0 ) && + ( ! entry_in_cache(cache_ptr, type, (idx + lag)) ) ) { + + if ( verbose ) + HDfprintf(stdout, "(i, %d, %d) ", type, (idx + lag)); + + insert_entry(cache_ptr, type, (idx + lag), dirty_inserts); + } + + if ( ( pass ) && ( idx >= 0 ) && ( idx <= max_indices[type] ) ) { + + if ( verbose ) + HDfprintf(stdout, "(p, %d, %d) ", type, idx); + + protect_entry(cache_ptr, type, idx); + } + + if ( ( pass ) && ( (idx - lag) >= 0 ) && + ( (idx - lag) <= max_indices[type] ) ) { + + if ( verbose ) + HDfprintf(stdout, "(u, %d, %d) ", type, (idx - lag)); + + unprotect_entry(cache_ptr, type, idx - lag, + dirty_unprotects, FALSE); + } + + if ( verbose ) + HDfprintf(stdout, "\n"); + + type++; + } + + idx++; + } + + if ( ( pass ) && ( display_stats ) ) { + + H5C_stats(cache_ptr, "test cache", display_detailed_stats); + } + + return; + +} /* col_major_scan_forward() */ + + +/*------------------------------------------------------------------------- + * Function: col_major_scan_backward() + * + * Purpose: Do a sequence of inserts, protects, and unprotects + * while scanning backwards through the set of + * entries. If pass is false on entry, do nothing. + * + * Return: void + * + * Programmer: John Mainzer + * 6/23/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +col_major_scan_backward(H5C_t * cache_ptr, + int32_t lag, + hbool_t verbose, + hbool_t reset_stats, + hbool_t display_stats, + hbool_t display_detailed_stats, + hbool_t do_inserts, + hbool_t dirty_inserts, + int dirty_unprotects) +{ + const char * fcn_name = "col_major_scan_backward()"; + int mile_stone = 1; + int32_t type; + int32_t idx; + + if ( verbose ) + HDfprintf(stdout, "%s: entering.\n", fcn_name); + + HDassert( lag > 5 ); + + if ( ( pass ) && ( reset_stats ) ) { + + H5C_stats__reset(cache_ptr); + } + + idx = MAX_ENTRIES + lag; + + if ( verbose ) /* 1 */ + HDfprintf(stdout, "%s: point %d.\n", fcn_name, mile_stone++); + + + while ( ( pass ) && ( (idx + lag) >= 0 ) ) + { + type = NUMBER_OF_ENTRY_TYPES - 1; + + while ( ( pass ) && ( type >= 0 ) ) + { + if ( ( pass ) && ( do_inserts) && ( (idx - lag) >= 0 ) && + ( (idx - lag) <= max_indices[type] ) && + ( ((idx - lag) % 3) == 0 ) && + ( ! entry_in_cache(cache_ptr, type, (idx - lag)) ) ) { + + if ( verbose ) + HDfprintf(stdout, "(i, %d, %d) ", type, (idx - lag)); + + insert_entry(cache_ptr, type, (idx - lag), dirty_inserts); + } + + if ( ( pass ) && ( idx >= 0 ) && ( idx <= max_indices[type] ) ) { + + if ( verbose ) + HDfprintf(stdout, "(p, %d, %d) ", type, idx); + + protect_entry(cache_ptr, type, idx); + } + + if ( ( pass ) && ( (idx + lag) >= 0 ) && + ( (idx + lag) <= max_indices[type] ) ) { + + if ( verbose ) + HDfprintf(stdout, "(u, %d, %d) ", type, (idx + lag)); + + unprotect_entry(cache_ptr, type, idx + lag, + dirty_unprotects, FALSE); + } + + if ( verbose ) + HDfprintf(stdout, "\n"); + + type--; + } + + idx--; + } + + if ( verbose ) /* 2 */ + HDfprintf(stdout, "%s: point %d.\n", fcn_name, mile_stone++); + + if ( ( pass ) && ( display_stats ) ) { + + H5C_stats(cache_ptr, "test cache", display_detailed_stats); + } + + if ( verbose ) + HDfprintf(stdout, "%s: exiting.\n", fcn_name); + + return; + +} /* col_major_scan_backward() */ + + +/**************************************************************************/ +/**************************************************************************/ +/********************************* tests: *********************************/ +/**************************************************************************/ +/**************************************************************************/ + +/*------------------------------------------------------------------------- + * Function: smoke_check_1() + * + * Purpose: A basic functional test, inserts, destroys, and renames in + * the mix, along with repeated protects and unprotects. + * All entries are marked as clean. + * + * Return: void + * + * Programmer: John Mainzer + * 6/16/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +smoke_check_1(void) +{ + const char * fcn_name = "smoke_check_1"; + hbool_t show_progress = FALSE; + hbool_t dirty_inserts = FALSE; + int dirty_unprotects = FALSE; + int dirty_destroys = FALSE; + hbool_t display_stats = FALSE; + int32_t lag = 10; + int mile_stone = 1; + H5C_t * cache_ptr = NULL; + + TESTING("smoke check #1 -- all clean, ins, dest, ren, 4/2 MB cache"); + + pass = TRUE; + + if ( show_progress ) /* 1 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + reset_entries(); + + if ( show_progress ) /* 2 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + cache_ptr = setup_cache((size_t)(4 * 1024 * 1024), + (size_t)(2 * 1024 * 1024)); + + if ( show_progress ) /* 3 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + row_major_scan_forward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* do_renames */ TRUE, + /* rename_to_main_addr */ FALSE, + /* do_destroys */ TRUE, + /* dirty_destroys */ dirty_destroys, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 4 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + row_major_scan_backward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ FALSE, + /* dirty_inserts */ dirty_inserts, + /* do_renames */ TRUE, + /* rename_to_main_addr */ TRUE, + /* do_destroys */ FALSE, + /* dirty_destroys */ dirty_destroys, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 5 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + row_major_scan_forward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* do_renames */ TRUE, + /* rename_to_main_addr */ FALSE, + /* do_destroys */ FALSE, + /* dirty_destroys */ dirty_destroys, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 6 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + /* flush and destroy all entries in the cache: */ + + flush_cache(/* cache_ptr */ cache_ptr, + /* destroy_entries */ TRUE, + /* dump_stats */ FALSE, + /* dump_detailed_stats */ FALSE); + + if ( show_progress ) /* 7 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + col_major_scan_forward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 8 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + /* flush all entries in the cache: */ + + flush_cache(/* cache_ptr */ cache_ptr, + /* destroy_entries */ FALSE, + /* dump_stats */ FALSE, + /* dump_detailed_stats */ FALSE); + + if ( show_progress ) /* 9 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + col_major_scan_backward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 10 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + takedown_cache(cache_ptr, display_stats, TRUE); + + if ( show_progress ) /* 11 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + verify_clean(); + verify_unprotected(); + + if ( pass ) { PASSED(); } else { H5_FAILED(); } + + if ( ! pass ) + HDfprintf(stdout, "%s(): failure_mssg = \"%s\".\n", + fcn_name, failure_mssg); + +} /* smoke_check_1() */ + + +/*------------------------------------------------------------------------- + * Function: smoke_check_2() + * + * Purpose: A basic functional test, with inserts, destroys, and + * renames in the mix, along with some repeated protects + * and unprotects. About half the entries are marked as + * dirty. + * + * Return: void + * + * Programmer: John Mainzer + * 6/24/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +smoke_check_2(void) +{ + const char * fcn_name = "smoke_check_2"; + hbool_t show_progress = FALSE; + hbool_t dirty_inserts = TRUE; + int dirty_unprotects = TRUE; + int dirty_destroys = TRUE; + hbool_t display_stats = FALSE; + int32_t lag = 10; + int mile_stone = 1; + H5C_t * cache_ptr = NULL; + + TESTING("smoke check #2 -- ~1/2 dirty, ins, dest, ren, 4/2 MB cache"); + + pass = TRUE; + + if ( show_progress ) /* 1 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + reset_entries(); + + if ( show_progress ) /* 2 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + cache_ptr = setup_cache((size_t)(4 * 1024 * 1024), + (size_t)(2 * 1024 * 1024)); + + if ( show_progress ) /* 3 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + row_major_scan_forward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* do_renames */ TRUE, + /* rename_to_main_addr */ FALSE, + /* do_destroys */ TRUE, + /* dirty_destroys */ dirty_destroys, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 4 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + row_major_scan_backward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ FALSE, + /* dirty_inserts */ dirty_inserts, + /* do_renames */ TRUE, + /* rename_to_main_addr */ TRUE, + /* do_destroys */ FALSE, + /* dirty_destroys */ dirty_destroys, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 5 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + row_major_scan_forward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* do_renames */ TRUE, + /* rename_to_main_addr */ FALSE, + /* do_destroys */ FALSE, + /* dirty_destroys */ dirty_destroys, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 6 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + /* flush and destroy all entries in the cache: */ + + flush_cache(/* cache_ptr */ cache_ptr, + /* destroy_entries */ TRUE, + /* dump_stats */ FALSE, + /* dump_detailed_stats */ FALSE); + + if ( show_progress ) /* 7 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + col_major_scan_forward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 8 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + /* flush all entries in the cache: */ + + flush_cache(/* cache_ptr */ cache_ptr, + /* destroy_entries */ FALSE, + /* dump_stats */ FALSE, + /* dump_detailed_stats */ FALSE); + + if ( show_progress ) /* 9 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + col_major_scan_backward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 10 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + takedown_cache(cache_ptr, display_stats, TRUE); + + if ( show_progress ) /* 11 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + verify_clean(); + verify_unprotected(); + + if ( pass ) { PASSED(); } else { H5_FAILED(); } + + if ( ! pass ) + HDfprintf(stdout, "%s(): failure_mssg = \"%s\".\n", + fcn_name, failure_mssg); + +} /* smoke_check_2() */ + + +/*------------------------------------------------------------------------- + * Function: smoke_check_3() + * + * Purpose: A basic functional test on a tiny cache, with inserts, + * destroys, and renames in the mix, along with repeated + * protects and unprotects. All entries are marked as clean. + * + * Return: void + * + * Programmer: John Mainzer + * 6/16/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +smoke_check_3(void) +{ + const char * fcn_name = "smoke_check_3"; + hbool_t show_progress = FALSE; + hbool_t dirty_inserts = FALSE; + int dirty_unprotects = FALSE; + int dirty_destroys = FALSE; + hbool_t display_stats = FALSE; + int32_t lag = 10; + int mile_stone = 1; + H5C_t * cache_ptr = NULL; + + TESTING("smoke check #3 -- all clean, ins, dest, ren, 2/1 KB cache"); + + pass = TRUE; + + if ( show_progress ) /* 1 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + reset_entries(); + + if ( show_progress ) /* 2 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + cache_ptr = setup_cache((size_t)(2 * 1024), + (size_t)(1 * 1024)); + + if ( show_progress ) /* 3 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + row_major_scan_forward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* do_renames */ TRUE, + /* rename_to_main_addr */ FALSE, + /* do_destroys */ TRUE, + /* dirty_destroys */ dirty_destroys, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 4 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + row_major_scan_backward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ FALSE, + /* dirty_inserts */ dirty_inserts, + /* do_renames */ TRUE, + /* rename_to_main_addr */ TRUE, + /* do_destroys */ FALSE, + /* dirty_destroys */ dirty_destroys, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 5 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + row_major_scan_forward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* do_renames */ TRUE, + /* rename_to_main_addr */ FALSE, + /* do_destroys */ FALSE, + /* dirty_destroys */ dirty_destroys, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 6 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + /* flush and destroy all entries in the cache: */ + + flush_cache(/* cache_ptr */ cache_ptr, + /* destroy_entries */ TRUE, + /* dump_stats */ FALSE, + /* dump_detailed_stats */ FALSE); + + if ( show_progress ) /* 7 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + col_major_scan_forward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 8 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + /* flush all entries in the cache: */ + + flush_cache(/* cache_ptr */ cache_ptr, + /* destroy_entries */ FALSE, + /* dump_stats */ FALSE, + /* dump_detailed_stats */ FALSE); + + if ( show_progress ) /* 9 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + col_major_scan_backward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 10 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + takedown_cache(cache_ptr, display_stats, TRUE); + + if ( show_progress ) /* 11 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + verify_clean(); + verify_unprotected(); + + if ( pass ) { PASSED(); } else { H5_FAILED(); } + + if ( ! pass ) + HDfprintf(stdout, "%s(): failure_mssg = \"%s\".\n", + fcn_name, failure_mssg); + +} /* smoke_check_3() */ + + +/*------------------------------------------------------------------------- + * Function: smoke_check_4() + * + * Purpose: A basic functional test on a tiny cache, with inserts, + * destroys, and renames in the mix, along with repeated + * protects and unprotects. About half the entries are + * marked as dirty. + * + * Return: void + * + * Programmer: John Mainzer + * 6/24/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +smoke_check_4(void) +{ + const char * fcn_name = "smoke_check_4"; + hbool_t show_progress = FALSE; + hbool_t dirty_inserts = TRUE; + int dirty_unprotects = TRUE; + int dirty_destroys = TRUE; + hbool_t display_stats = FALSE; + int32_t lag = 10; + int mile_stone = 1; + H5C_t * cache_ptr = NULL; + + TESTING("smoke check #4 -- ~1/2 dirty, ins, dest, ren, 2/1 KB cache"); + + pass = TRUE; + + if ( show_progress ) /* 1 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + reset_entries(); + + if ( show_progress ) /* 2 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + cache_ptr = setup_cache((size_t)(2 * 1024), + (size_t)(1 * 1024)); + + if ( show_progress ) /* 3 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + row_major_scan_forward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* do_renames */ TRUE, + /* rename_to_main_addr */ FALSE, + /* do_destroys */ TRUE, + /* dirty_destroys */ dirty_destroys, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 4 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + row_major_scan_backward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ FALSE, + /* dirty_inserts */ dirty_inserts, + /* do_renames */ TRUE, + /* rename_to_main_addr */ TRUE, + /* do_destroys */ FALSE, + /* dirty_destroys */ dirty_destroys, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 5 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + row_major_scan_forward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* do_renames */ TRUE, + /* rename_to_main_addr */ FALSE, + /* do_destroys */ FALSE, + /* dirty_destroys */ dirty_destroys, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 6 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + /* flush and destroy all entries in the cache: */ + + flush_cache(/* cache_ptr */ cache_ptr, + /* destroy_entries */ TRUE, + /* dump_stats */ FALSE, + /* dump_detailed_stats */ FALSE); + + if ( show_progress ) /* 7 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + col_major_scan_forward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 8 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + /* flush all entries in the cache: */ + + flush_cache(/* cache_ptr */ cache_ptr, + /* destroy_entries */ FALSE, + /* dump_stats */ FALSE, + /* dump_detailed_stats */ FALSE); + + if ( show_progress ) /* 9 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + col_major_scan_backward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ dirty_inserts, + /* dirty_unprotects */ dirty_unprotects); + + if ( show_progress ) /* 10 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + takedown_cache(cache_ptr, display_stats, TRUE); + + if ( show_progress ) /* 11 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + verify_clean(); + verify_unprotected(); + + if ( pass ) { PASSED(); } else { H5_FAILED(); } + + if ( ! pass ) + HDfprintf(stdout, "%s(): failure_mssg = \"%s\".\n", + fcn_name, failure_mssg); + +} /* smoke_check_4() */ + + +/*------------------------------------------------------------------------- + * Function: write_permitted_check() + * + * Purpose: A basic test of the write permitted function. In essence, + * we load the cache up with dirty entryies, set + * write_permitted to FALSE, and then protect a bunch of + * entries. If there are any writes while write_permitted is + * FALSE, the test will fail. + * + * Return: void + * + * Programmer: John Mainzer + * 6/24/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +write_permitted_check(void) +{ + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + + const char * fcn_name = "write_permitted_check"; + hbool_t show_progress = FALSE; + hbool_t display_stats = FALSE; + int32_t lag = 10; + int mile_stone = 1; + H5C_t * cache_ptr = NULL; + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + TESTING("write permitted check -- 1/0 MB cache"); + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + + pass = TRUE; + + if ( show_progress ) /* 1 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + reset_entries(); + + if ( show_progress ) /* 2 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + cache_ptr = setup_cache((size_t)(1 * 1024 * 1024), + (size_t)(0)); + + if ( show_progress ) /* 3 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + row_major_scan_forward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ TRUE, + /* do_renames */ TRUE, + /* rename_to_main_addr */ FALSE, + /* do_destroys */ TRUE, + /* dirty_destroys */ TRUE, + /* dirty_unprotects */ TRUE); + + if ( show_progress ) /* 4 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + write_permitted = FALSE; + + row_major_scan_backward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ FALSE, + /* dirty_inserts */ FALSE, + /* do_renames */ TRUE, + /* rename_to_main_addr */ TRUE, + /* do_destroys */ FALSE, + /* dirty_destroys */ FALSE, + /* dirty_unprotects */ NO_CHANGE); + + if ( show_progress ) /* 5 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + write_permitted = TRUE; + + row_major_scan_forward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ TRUE, + /* do_renames */ TRUE, + /* rename_to_main_addr */ FALSE, + /* do_destroys */ FALSE, + /* dirty_destroys */ TRUE, + /* dirty_unprotects */ TRUE); + + if ( show_progress ) /* 6 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + /* flush and destroy all entries in the cache: */ + + flush_cache(/* cache_ptr */ cache_ptr, + /* destroy_entries */ TRUE, + /* dump_stats */ FALSE, + /* dump_detailed_stats */ FALSE); + + if ( show_progress ) /* 7 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + col_major_scan_forward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ TRUE, + /* dirty_inserts */ TRUE, + /* dirty_unprotects */ TRUE); + + if ( show_progress ) /* 8 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + write_permitted = FALSE; + + col_major_scan_backward(/* cache_ptr */ cache_ptr, + /* lag */ lag, + /* verbose */ FALSE, + /* reset_stats */ TRUE, + /* display_stats */ display_stats, + /* display_detailed_stats */ TRUE, + /* do_inserts */ FALSE, + /* dirty_inserts */ FALSE, + /* dirty_unprotects */ NO_CHANGE); + + write_permitted = TRUE; + + if ( show_progress ) /* 9 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + takedown_cache(cache_ptr, display_stats, TRUE); + + if ( show_progress ) /* 10 */ + HDfprintf(stdout, "%s() - %0d -- pass = %d\n", + fcn_name, mile_stone++, (int)pass); + + verify_clean(); + verify_unprotected(); + + if ( pass ) { PASSED(); } else { H5_FAILED(); } + + if ( ! pass ) + HDfprintf(stdout, "%s(): failure_mssg = \"%s\".\n", + fcn_name, failure_mssg); + +#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + SKIPPED(); + + HDfprintf(stdout, " Clean and dirty LRU lists disabled.\n"); + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + +} /* write_permitted_check() */ + + +/*------------------------------------------------------------------------- + * Function: check_flush_protected_err() + * + * Purpose: Verify that an attempt to flush the cache when it contains + * a protected entry will generate an error. + * + * Return: void + * + * Programmer: John Mainzer + * 6/24/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +check_flush_protected_err(void) +{ + const char * fcn_name = "check_flush_protected_err"; + H5C_t * cache_ptr = NULL; + + TESTING("flush cache with protected entry error"); + + pass = TRUE; + + /* allocate a cache, protect an entry, and try to flush. This + * should fail. Unprotect the entry and flush again -- should + * succeed. + */ + + if ( pass ) { + + reset_entries(); + + cache_ptr = setup_cache((size_t)(2 * 1024), + (size_t)(1 * 1024)); + + protect_entry(cache_ptr, 0, 0); + + if ( H5C_flush_cache(NULL, -1, -1, cache_ptr, 0) >= 0 ) { + + pass = FALSE; + failure_mssg = "flush succeeded on cache with protected entry.\n"; + + } else { + + unprotect_entry(cache_ptr, 0, 0, TRUE, FALSE); + + if ( H5C_flush_cache(NULL, -1, -1, cache_ptr, 0) < 0 ) { + + pass = FALSE; + failure_mssg = "flush failed after unprotect.\n"; + + } else { + + takedown_cache(cache_ptr, FALSE, FALSE); + } + } + } + + if ( pass ) { PASSED(); } else { H5_FAILED(); } + + if ( ! pass ) + HDfprintf(stdout, "%s(): failure_mssg = \"%s\".\n", + fcn_name, failure_mssg); + +} /* check_flush_protected_err() */ + + +/*------------------------------------------------------------------------- + * Function: check_destroy_protected_err() + * + * Purpose: Verify that an attempt to destroy the cache when it contains + * a protected entry will generate an error. + * + * Return: void + * + * Programmer: John Mainzer + * 6/24/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +check_destroy_protected_err(void) +{ + const char * fcn_name = "check_destroy_protected_err"; + H5C_t * cache_ptr = NULL; + + TESTING("destroy cache with protected entry error"); + + pass = TRUE; + + /* allocate a cache, protect an entry, and try to flush. This + * should fail. Unprotect the entry and flush again -- should + * succeed. + */ + + if ( pass ) { + + reset_entries(); + + cache_ptr = setup_cache((size_t)(2 * 1024), + (size_t)(1 * 1024)); + + protect_entry(cache_ptr, 0, 0); + + if ( H5C_dest(NULL, -1, -1, cache_ptr) >= 0 ) { + + pass = FALSE; + failure_mssg = "destroy succeeded on cache with protected entry.\n"; + + } else { + + unprotect_entry(cache_ptr, 0, 0, TRUE, FALSE); + + if ( H5C_dest(NULL, -1, -1, cache_ptr) < 0 ) { + + pass = FALSE; + failure_mssg = "destroy failed after unprotect.\n"; + + } + } + } + + if ( pass ) { PASSED(); } else { H5_FAILED(); } + + if ( ! pass ) + HDfprintf(stdout, "%s(): failure_mssg = \"%s\".\n", + fcn_name, failure_mssg); + +} /* check_destroy_protected_err() */ + + +/*------------------------------------------------------------------------- + * Function: check_duplicate_insert_err() + * + * Purpose: Verify that an attempt to insert and entry that is + * alread in the cache will generate an error. + * + * Return: void + * + * Programmer: John Mainzer + * 6/24/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +check_duplicate_insert_err(void) +{ + const char * fcn_name = "check_duplicate_insert_err"; + herr_t result; + H5C_t * cache_ptr = NULL; + test_entry_t * base_addr; + test_entry_t * entry_ptr; + + TESTING("duplicate entry insertion error"); + + pass = TRUE; + + /* allocate a cache, protect an entry, and then try to insert + * the entry again. This should fail. Unprotect the entry and + * destroy the cache -- should succeed. + */ + + if ( pass ) { + + reset_entries(); + + cache_ptr = setup_cache((size_t)(2 * 1024), + (size_t)(1 * 1024)); + + protect_entry(cache_ptr, 0, 0); + + if ( pass ) { + + base_addr = entries[0]; + entry_ptr = &(base_addr[0]); + + result = H5C_insert_entry(NULL, -1, -1, cache_ptr, + &(types[0]), entry_ptr->addr, + (void *)entry_ptr); + + if ( result >= 0 ) { + + pass = FALSE; + failure_mssg = "insert of duplicate entry succeeded.\n"; + + } else { + + unprotect_entry(cache_ptr, 0, 0, TRUE, FALSE); + + takedown_cache(cache_ptr, FALSE, FALSE); + } + } + } + + if ( pass ) { PASSED(); } else { H5_FAILED(); } + + if ( ! pass ) + HDfprintf(stdout, "%s(): failure_mssg = \"%s\".\n", + fcn_name, failure_mssg); + +} /* check_duplicate_insert_err() */ + + +/*------------------------------------------------------------------------- + * Function: check_rename_err() + * + * Purpose: Verify that an attempt to rename an entry to the address + * of an existing entry will generate an error. + * + * Return: void + * + * Programmer: John Mainzer + * 6/24/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +check_rename_err(void) +{ + const char * fcn_name = "check_rename_err()"; + herr_t result; + H5C_t * cache_ptr = NULL; + test_entry_t * entry_0_0_ptr; + test_entry_t * entry_0_1_ptr; + test_entry_t * entry_1_0_ptr; + + TESTING("rename to existing entry errors"); + + pass = TRUE; + + /* allocate a cache, and insert several entries. Try to rename + * entries to other entries resident in the cache. This should + * fail. Destroy the cache -- should succeed. + */ + + if ( pass ) { + + reset_entries(); + + cache_ptr = setup_cache((size_t)(2 * 1024), + (size_t)(1 * 1024)); + + insert_entry(cache_ptr, 0, 0, TRUE); + insert_entry(cache_ptr, 0, 1, TRUE); + insert_entry(cache_ptr, 1, 0, TRUE); + + entry_0_0_ptr = &((entries[0])[0]); + entry_0_1_ptr = &((entries[0])[1]); + entry_1_0_ptr = &((entries[1])[0]); + } + + if ( pass ) { + + result = H5C_rename_entry(NULL, cache_ptr, &(types[0]), + entry_0_0_ptr->addr, entry_0_1_ptr->addr); + + if ( result >= 0 ) { + + pass = FALSE; + failure_mssg = "rename to addr of same type succeeded.\n"; + } + } + + if ( pass ) { + + result = H5C_rename_entry(NULL, cache_ptr, &(types[0]), + entry_0_0_ptr->addr, entry_1_0_ptr->addr); + + if ( result >= 0 ) { + + pass = FALSE; + failure_mssg = "rename to addr of different type succeeded.\n"; + } + } + + if ( pass ) { + + takedown_cache(cache_ptr, FALSE, FALSE); + } + + if ( pass ) { PASSED(); } else { H5_FAILED(); } + + if ( ! pass ) + HDfprintf(stdout, "%s: failure_mssg = \"%s\".\n", + fcn_name, failure_mssg); + +} /* check_rename_err() */ + + +/*------------------------------------------------------------------------- + * Function: check_double_protect_err() + * + * Purpose: Verify that an attempt to protect an entry that is already + * protected will generate an error. + * + * Return: void + * + * Programmer: John Mainzer + * 6/24/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +check_double_protect_err(void) +{ + const char * fcn_name = "check_double_protect_err()"; + H5C_t * cache_ptr = NULL; + test_entry_t * entry_ptr; + H5C_cache_entry_t * cache_entry_ptr; + + TESTING("protect a protected entry error"); + + pass = TRUE; + + /* allocate a cache, protect an entry, and then try to protect + * the entry again. This should fail. Unprotect the entry and + * destroy the cache -- should succeed. + */ + + if ( pass ) { + + reset_entries(); + + cache_ptr = setup_cache((size_t)(2 * 1024), + (size_t)(1 * 1024)); + + protect_entry(cache_ptr, 0, 0); + + entry_ptr = &((entries[0])[0]); + } + + if ( pass ) { + + cache_entry_ptr = H5C_protect(NULL, -1, -1, cache_ptr, &(types[0]), + entry_ptr->addr, NULL, NULL); + + if ( cache_entry_ptr != NULL ) { + + pass = FALSE; + failure_mssg = "attempt to protect a protected entry succeeded.\n"; + } + } + + if ( pass ) { + + unprotect_entry(cache_ptr, 0, 0, FALSE, FALSE); + } + + if ( pass ) { + + takedown_cache(cache_ptr, FALSE, FALSE); + } + + if ( pass ) { PASSED(); } else { H5_FAILED(); } + + if ( ! pass ) + HDfprintf(stdout, "%s: failure_mssg = \"%s\".\n", + fcn_name, failure_mssg); + +} /* check_double_protect_err() */ + + +/*------------------------------------------------------------------------- + * Function: check_double_unprotect_err() + * + * Purpose: Verify that an attempt to unprotect an entry that is already + * unprotected will generate an error. + * + * Return: void + * + * Programmer: John Mainzer + * 6/24/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static void +check_double_unprotect_err(void) +{ + const char * fcn_name = "check_double_unprotect_err()"; + herr_t result; + H5C_t * cache_ptr = NULL; + test_entry_t * entry_ptr; + + TESTING("unprotect an unprotected entry error"); + + pass = TRUE; + + /* allocate a cache, protect an entry, unprotect it, and then try to + * unprotect the entry again. This should fail. Destroy the cache + * -- should succeed. + */ + + if ( pass ) { + + reset_entries(); + + cache_ptr = setup_cache((size_t)(2 * 1024), + (size_t)(1 * 1024)); + + protect_entry(cache_ptr, 0, 0); + + unprotect_entry(cache_ptr, 0, 0, FALSE, FALSE); + + entry_ptr = &((entries[0])[0]); + } + + if ( pass ) { + + result = H5C_unprotect(NULL, -1, -1, cache_ptr, &(types[0]), + entry_ptr->addr, (void *)entry_ptr, FALSE); + + if ( result > 0 ) { + + pass = FALSE; + failure_mssg = + "attempt to unprotect an unprotected entry succeeded.\n"; + } + } + + if ( pass ) { + + takedown_cache(cache_ptr, FALSE, FALSE); + } + + if ( pass ) { PASSED(); } else { H5_FAILED(); } + + if ( ! pass ) + HDfprintf(stdout, "%s: failure_mssg = \"%s\".\n", + fcn_name, failure_mssg); + +} /* check_double_unprotect_err() */ + + +/*------------------------------------------------------------------------- + * Function: main + * + * Purpose: Run tests on the cache code contained in H5C.c + * + * Return: Success: + * + * Failure: + * + * Programmer: John Mainzer + * 6/24/04 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +int +main(void) +{ + H5open(); + + smoke_check_1(); + smoke_check_2(); + smoke_check_3(); + smoke_check_4(); + write_permitted_check(); + check_flush_protected_err(); + check_destroy_protected_err(); + check_duplicate_insert_err(); + check_rename_err(); + check_double_protect_err(); + check_double_unprotect_err(); + + return(0); + +} /* main() */ diff --git a/tools/h5jam/Dependencies b/tools/h5jam/Dependencies new file mode 100644 index 0000000..be3a92b --- /dev/null +++ b/tools/h5jam/Dependencies @@ -0,0 +1,83 @@ +## This file is machine generated on GNU systems. +## Only temporary changes may be made here. + +h5jam.lo: \ + $(top_srcdir)/tools/h5jam/h5jam.c \ + $(top_srcdir)/src/hdf5.h \ + $(top_srcdir)/src/H5public.h \ + $(top_builddir)/src/H5pubconf.h \ + $(top_srcdir)/src/H5api_adpt.h \ + $(top_srcdir)/src/H5Apublic.h \ + $(top_srcdir)/src/H5Ipublic.h \ + $(top_srcdir)/src/H5ACpublic.h \ + $(top_srcdir)/src/H5Bpublic.h \ + $(top_srcdir)/src/H5Dpublic.h \ + $(top_srcdir)/src/H5Epublic.h \ + $(top_srcdir)/src/H5Epubgen.h \ + $(top_srcdir)/src/H5Fpublic.h \ + $(top_srcdir)/src/H5FDpublic.h \ + $(top_srcdir)/src/H5FPpublic.h \ + $(top_srcdir)/src/H5Gpublic.h \ + $(top_srcdir)/src/H5Opublic.h \ + $(top_srcdir)/src/H5HGpublic.h \ + $(top_srcdir)/src/H5HLpublic.h \ + $(top_srcdir)/src/H5MMpublic.h \ + $(top_srcdir)/src/H5Ppublic.h \ + $(top_srcdir)/src/H5Tpublic.h \ + $(top_srcdir)/src/H5Zpublic.h \ + $(top_srcdir)/src/H5Rpublic.h \ + $(top_srcdir)/src/H5Spublic.h \ + $(top_srcdir)/src/H5FDcore.h \ + $(top_srcdir)/src/H5FDfamily.h \ + $(top_srcdir)/src/H5FDgass.h \ + $(top_srcdir)/src/H5FDlog.h \ + $(top_srcdir)/src/H5FDmpi.h \ + $(top_srcdir)/src/H5FDfphdf5.h \ + $(top_srcdir)/src/H5FDmpio.h \ + $(top_srcdir)/src/H5FDmpiposix.h \ + $(top_srcdir)/src/H5FDmulti.h \ + $(top_srcdir)/src/H5FDsec2.h \ + $(top_srcdir)/src/H5FDsrb.h \ + $(top_srcdir)/src/H5FDstdio.h \ + $(top_srcdir)/src/H5FDstream.h \ + $(top_srcdir)/tools/lib/h5tools_utils.h +h5unjam.lo: \ + $(top_srcdir)/tools/h5jam/h5unjam.c \ + $(top_srcdir)/src/hdf5.h \ + $(top_srcdir)/src/H5public.h \ + $(top_builddir)/src/H5pubconf.h \ + $(top_srcdir)/src/H5api_adpt.h \ + $(top_srcdir)/src/H5Apublic.h \ + $(top_srcdir)/src/H5Ipublic.h \ + $(top_srcdir)/src/H5ACpublic.h \ + $(top_srcdir)/src/H5Bpublic.h \ + $(top_srcdir)/src/H5Dpublic.h \ + $(top_srcdir)/src/H5Epublic.h \ + $(top_srcdir)/src/H5Epubgen.h \ + $(top_srcdir)/src/H5Fpublic.h \ + $(top_srcdir)/src/H5FDpublic.h \ + $(top_srcdir)/src/H5FPpublic.h \ + $(top_srcdir)/src/H5Gpublic.h \ + $(top_srcdir)/src/H5Opublic.h \ + $(top_srcdir)/src/H5HGpublic.h \ + $(top_srcdir)/src/H5HLpublic.h \ + $(top_srcdir)/src/H5MMpublic.h \ + $(top_srcdir)/src/H5Ppublic.h \ + $(top_srcdir)/src/H5Tpublic.h \ + $(top_srcdir)/src/H5Zpublic.h \ + $(top_srcdir)/src/H5Rpublic.h \ + $(top_srcdir)/src/H5Spublic.h \ + $(top_srcdir)/src/H5FDcore.h \ + $(top_srcdir)/src/H5FDfamily.h \ + $(top_srcdir)/src/H5FDgass.h \ + $(top_srcdir)/src/H5FDlog.h \ + $(top_srcdir)/src/H5FDmpi.h \ + $(top_srcdir)/src/H5FDfphdf5.h \ + $(top_srcdir)/src/H5FDmpio.h \ + $(top_srcdir)/src/H5FDmpiposix.h \ + $(top_srcdir)/src/H5FDmulti.h \ + $(top_srcdir)/src/H5FDsec2.h \ + $(top_srcdir)/src/H5FDsrb.h \ + $(top_srcdir)/src/H5FDstdio.h \ + $(top_srcdir)/src/H5FDstream.h \ + $(top_srcdir)/tools/lib/h5tools_utils.h diff --git a/tools/h5jam/Makefile.in b/tools/h5jam/Makefile.in new file mode 100644 index 0000000..ccb2018 --- /dev/null +++ b/tools/h5jam/Makefile.in @@ -0,0 +1,96 @@ +## HDF5 Library Makefile(.in) +## +## Copyright by the Board of Trustees of the University of Illinois. +## 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 files COPYING and Copyright.html. COPYING can be found at the root +## of the source code distribution tree; Copyright.html can be found at the +## root level of an installed copy of the electronic HDF5 document set and +## is linked from the top-level documents page. It can also be found at +## http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have +## access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. +## +top_srcdir=@top_srcdir@ +top_builddir=../.. +srcdir=@srcdir@ +@COMMENCE@ + +## Add include directory to the C preprocessor flags, add -lh5tools and +## -lhdf5 to the list of libraries. +## +CPPFLAGS=-I. -I$(srcdir) -I$(top_builddir)/src -I$(top_srcdir)/src \ + -I$(top_srcdir)/tools/lib @CPPFLAGS@ + +## Test programs and scripts. +## +TEST_PROGS= +TEST_SCRIPTS=./testh5jam.sh + +OTHER_PROGS=tellub h5jamgentest getub +## These are our main targets: library and tools. +## +LIBTOOLS=../lib/libh5tools.la +LIBHDF5=$(top_builddir)/src/libhdf5.la + +PUB_PROGS=h5jam h5unjam + +PROGS=$(PUB_PROGS) $(TEST_PROGS) $(OTHER_PROGS) + +## Source and object files for the library; do not install +## +LIB_SRC= +LIB_OBJ=$(LIB_SRC:.c=.lo) +PUB_LIB= + +## Temporary files. *.h5 are generated by jamgentest. They should +## copied to the testfiles/ directory if update is required. +MOSTLYCLEAN=*.h5 *.txt +CLEAN= +DISTCLEAN=testh5jam.sh + +#### FIX AFTER HERE + +## Source and object files for programs... +## +PROG_SRC=h5jam.c h5unjam.c +PROG_OBJ=$(PROG_SRC:.c=.lo) + +PRIVATE_HDR= + +OTHER_SRC=tellub.c h5jamgentest.c getub.c +OTHER_OBJ=$(OTHER_SRC:.c=.lo) + +## Source and object files for the tests (to be cleaned up) +## +TEST_SRC= +TEST_OBJ=$(TEST_SRC:.c=.lo) $(OTHER_OBJ) + +## Programs have to be built before they can be tested! +## +check test _test: $(PROGS) + +## How to build the programs...They all depend on the hdf5 library and +## the tools library compiled in this directory. +## +$(PROGS): $(LIBTOOLS) $(LIBHDF5) + +h5jam: h5jam.lo + @$(LT_LINK_EXE) $(CFLAGS) -o $@ h5jam.lo $(LIBTOOLS) $(LIBHDF5) $(LDFLAGS) $(LIBS) + +h5unjam: h5unjam.lo + @$(LT_LINK_EXE) $(CFLAGS) -o $@ h5unjam.lo $(LIBTOOLS) $(LIBHDF5) $(LDFLAGS) $(LIBS) + +h5jamgentest: h5jamgentest.lo + @$(LT_LINK_EXE) $(CFLAGS) -o $@ h5jamgentest.lo $(LIBTOOLS) $(LIBHDF5) $(LDFLAGS) $(LIBS) + +tellub: tellub.lo + @$(LT_LINK_EXE) $(CFLAGS) -o $@ tellub.lo $(LIBTOOLS) $(LIBHDF5) $(LDFLAGS) $(LIBS) + +getub: getub.lo + @$(LT_LINK_EXE) $(CFLAGS) -o $@ getub.lo $(LIBTOOLS) $(LIBHDF5) $(LDFLAGS) $(LIBS) + +check-all: check + +@CONCLUDE@ diff --git a/tools/h5jam/getub.c b/tools/h5jam/getub.c new file mode 100644 index 0000000..58e9fe8 --- /dev/null +++ b/tools/h5jam/getub.c @@ -0,0 +1,170 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by the Board of Trustees of the University of Illinois. * + * 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 files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include + +#ifdef H5_HAVE_UNISTD_H +#include +#endif + +#include "H5private.h" +#include "h5tools_utils.h" + +void parse_command_line (int argc, const char *argv[]); + +#define TRUE 1 +#define FALSE 0 + +static char *progname="getub"; +char *nbytes = NULL; + +static const char *s_opts = "c:"; /* add more later ? */ +static struct long_options l_opts[] = { + {"c", require_arg, 'c'}, /* input file */ + {NULL, 0, '\0'} +}; + +/*------------------------------------------------------------------------- + * Function: usage + * + * Purpose: Print the usage message + * + * Return: void + * + * Programmer: + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +usage (const char *prog) +{ + fflush (stdout); + fprintf (stdout, "usage: %s -c nb file] \n", prog); + fprintf (stdout, " print first 'nb' byts of file to stdoug.\n"); +} + +/*------------------------------------------------------------------------- + * Function: parse_command_line + * + * Purpose: Parse the command line for the h5dumper. + * + * Return: Success: + * + * Failure: Exits program with EXIT_FAILURE value. + * + * Programmer: + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +void +parse_command_line (int argc, const char *argv[]) +{ + int opt = FALSE; + + /* parse command line options */ + while ((opt = get_option (argc, argv, s_opts, l_opts)) != EOF) + { + switch ((char) opt) + { + case 'c': + nbytes = strdup (opt_arg); + break; + case '?': + default: + usage (progname); + exit (EXIT_FAILURE); + } + } + + if (argc <= opt_ind) + { + error_msg (progname, "missing file name\n"); + usage (progname); + exit (EXIT_FAILURE); + } +} + +int +main (int argc, const char *argv[]) +{ + int fd; + unsigned int size; + char *filename; + long res; + char *buf; + + parse_command_line (argc, argv); + + if (nbytes == NULL) + { + /* missing arg */ + error_msg (progname, "missing size\n"); + usage (progname); + exit (EXIT_FAILURE); + } + if (argc <= (opt_ind)) + { + error_msg (progname, "missing file name\n"); + usage (progname); + exit (EXIT_FAILURE); + } + filename = strdup (argv[opt_ind]); + + size = 0; + res = sscanf (nbytes, "%u", &size); + if (res == EOF) + { + /* fail */ + error_msg (progname, "missing file name\n"); + usage (progname); + exit (EXIT_FAILURE); + } + + fd = HDopen (filename, O_RDONLY, 0); + if (fd < 0) + { + error_msg (progname, "can't open file %s\n", filename); + exit (EXIT_FAILURE); + } + + buf = malloc ((unsigned)(size + 1)); + if (buf == NULL) + { + close (fd); + exit (EXIT_FAILURE); + } + + res = HDread (fd, buf, (unsigned)size); + + if (res < size) + { + if (buf) + free (buf); + close (fd); + exit (EXIT_FAILURE); + } + + HDwrite (1, buf, (unsigned)size); + + if (buf) + free (buf); + close (fd); + return (EXIT_SUCCESS); +} diff --git a/tools/h5jam/h5jam.c b/tools/h5jam/h5jam.c new file mode 100644 index 0000000..d78e28c --- /dev/null +++ b/tools/h5jam/h5jam.c @@ -0,0 +1,538 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by the Board of Trustees of the University of Illinois. * + * 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 files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +#ifdef H5_HAVE_UNISTD_H +#include +#endif + +#include "hdf5.h" +#include "H5private.h" +#include "h5tools_utils.h" + +#define TRUE 1 +#define FALSE 0 + +hsize_t write_pad (int, hsize_t); +hsize_t compute_user_block_size (hsize_t); +hsize_t copy_some_to_file (int, int, hsize_t, hsize_t, ssize_t); +void parse_command_line (int, const char *[]); + +const char *progname = "jam"; +int d_status = EXIT_SUCCESS; +int do_clobber = FALSE; +char *output_file = NULL; +char *input_file = NULL; +char *ub_file = NULL; + +/* + * Command-line options: The user can specify short or long-named + * parameters. The long-named ones can be partially spelled. When + * adding more, make sure that they don't clash with each other. + */ +static const char *s_opts = "hi:u:o:c"; /* add more later ? */ +static struct long_options l_opts[] = { + {"help", no_arg, 'h'}, + {"hel", no_arg, 'h'}, + {"i", require_arg, 'i'}, /* input file */ + {"u", require_arg, 'u'}, /* user block file */ + {"o", require_arg, 'o'}, /* output file */ + {"clobber", no_arg, 'c'}, /* clobber existing UB */ + {"clobbe", no_arg, 'c'}, + {"clobb", no_arg, 'c'}, + {"clob", no_arg, 'c'}, + {"clo", no_arg, 'c'}, + {"cl", no_arg, 'c'}, + {NULL, 0, '\0'} +}; + +/*------------------------------------------------------------------------- + * Function: usage + * + * Purpose: Print the usage message + * + * Return: void + * + * Programmer: + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +usage (const char *prog) +{ + fflush (stdout); + fprintf (stdout, + "usage: %s -u user_block_file -i h5_file [-o ofile | --clobber] \n", + prog); + fprintf (stdout, " Add 'user_block_file' to front of \n"); + fprintf (stdout, + " 'h5_file', pad so 'h5_file' starts on proper\n"); + fprintf (stdout, " byte.\n"); + fprintf (stdout, "\n"); + fprintf (stdout, " %s -h \n", prog); + fprintf (stdout, " Print a usage message and exit\n"); +} + +/*------------------------------------------------------------------------- + * Function: parse_command_line + * + * Purpose: Parse the command line for the h5dumper. + * + * Return: Success: + * + * Failure: Exits program with EXIT_FAILURE value. + * + * Programmer: + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +void +parse_command_line (int argc, const char *argv[]) +{ + int opt = FALSE; + + /* parse command line options */ + while ((opt = get_option (argc, argv, s_opts, l_opts)) != EOF) + { + switch ((char) opt) + { + case 'o': + output_file = strdup (opt_arg); + break; + case 'i': + input_file = strdup (opt_arg); + break; + case 'u': + ub_file = strdup (opt_arg); + break; + case 'c': + do_clobber = TRUE; + break; + case 'h': + usage (progname); + exit (EXIT_SUCCESS); + case '?': + default: + usage (progname); + exit (EXIT_FAILURE); + } + } +} + +/*------------------------------------------------------------------------- + * Function: main + * + * Purpose: HDF5 user block jammer + * + * Return: Success: 0 + * Failure: 1 + * + * Programmer: + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +int +main (int argc, const char *argv[]) +{ + int ufid; + int h5fid; + int ofid; + void *edata; + H5E_auto_stack_t func; + hid_t ifile; + hid_t plist; + herr_t status; + htri_t testval; + hsize_t usize; + hsize_t h5fsize; + hsize_t startub; + hsize_t where; + hsize_t newubsize; + off_t fsize; + struct stat sbuf; + struct stat sbuf2; + int res; + + /* Disable error reporting */ + H5Eget_auto_stack (H5E_DEFAULT, &func, &edata); + H5Eset_auto_stack (H5E_DEFAULT, NULL, NULL); + + parse_command_line (argc, argv); + + if (ub_file == NULL) + { + /* no user block */ + error_msg (progname, "no user block file name\n"); + usage (progname); + exit (EXIT_FAILURE); + } + + if (input_file == NULL) + { + /* no user block */ + error_msg (progname, "no HDF5 file\n"); + usage (progname); + exit (EXIT_FAILURE); + } + + testval = H5Fis_hdf5 (input_file); + + if (testval <= 0) + { + error_msg (progname, "Input HDF5 file is not HDF \"%s\"\n", input_file); + exit (EXIT_FAILURE); + } + + ifile = H5Fopen (input_file, H5F_ACC_RDONLY, H5P_DEFAULT); + + if (ifile < 0) + { + error_msg (progname, "Can't open input HDF5 file \"%s\"\n", input_file); + exit (EXIT_FAILURE); + } + + plist = H5Fget_create_plist (ifile); + if (plist < 0) + { + error_msg (progname, "Can't get file creation plist for file \"%s\"\n", + input_file); + exit (EXIT_FAILURE); + } + + status = H5Pget_userblock (plist, &usize); + if (status < 0) + { + error_msg (progname, "Can't get user block for file \"%s\"\n", + input_file); + exit (EXIT_FAILURE); + } + + H5Pclose (plist); + H5Fclose (ifile); + + ufid = HDopen (ub_file, O_RDONLY, 0); + + if (ufid < 0) + { + error_msg (progname, "unable to open user block file \"%s\"\n", + ub_file); + exit (EXIT_FAILURE); + } + + res = stat (ub_file, &sbuf); + + if (res < 0) + { + error_msg (progname, "Can't stat file \"%s\"\n", ub_file); + exit (EXIT_FAILURE); + } + + fsize = sbuf.st_size; + + h5fid = HDopen (input_file, O_RDONLY, 0); + + if (h5fid < 0) + { + error_msg (progname, "unable to open HDF5 file for read \"%s\"\n", + input_file); + exit (EXIT_FAILURE); + } + + res = stat (input_file, &sbuf2); + + if (res < 0) + { + error_msg (progname, "Can't stat file \"%s\"\n", input_file); + exit (EXIT_FAILURE); + } + + h5fsize = sbuf2.st_size; + + if (output_file == NULL) + { + ofid = HDopen (input_file, O_WRONLY, 0); + + if (ofid < 0) + { + error_msg (progname, "unable to open output file \"%s\"\n", + output_file); + exit (EXIT_FAILURE); + } + } + else + { + ofid = HDopen (output_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); + + if (ofid < 0) + { + error_msg (progname, "unable to create output file \"%s\"\n", + output_file); + exit (EXIT_FAILURE); + } + } + + newubsize = compute_user_block_size ((hsize_t) fsize); + + startub = usize; + + if (usize > 0) + { + if (do_clobber == TRUE) + { + /* where is max of the current size or the new UB */ + if (usize > newubsize) + { + newubsize = usize; + } + startub = 0; /*blast the old */ + } + else + { + /* add new ub to current ublock, pad to new offset */ + newubsize += usize; + newubsize = compute_user_block_size ((hsize_t) newubsize); + } + } + + /* copy the HDF5 from starting at usize to starting at newubsize: + * makes room at 'from' for new ub */ + /* if no current ub, usize is 0 */ + copy_some_to_file (h5fid, ofid, usize, newubsize, + (ssize_t) (h5fsize - usize)); + + /* copy the old ub to the beginning of the new file */ + if (!do_clobber) + { + where = + copy_some_to_file (h5fid, ofid, (hsize_t) 0, (hsize_t) 0, + (ssize_t) usize); + } + + /* copy the new ub to the end of the ub */ + where = copy_some_to_file (ufid, ofid, (hsize_t) 0, startub, (ssize_t) - 1); + + /* pad the ub */ + where = write_pad (ofid, where); + + + close (ufid); + close (h5fid); + close (ofid); + + return d_status; +} + +/*------------------------------------------------------------------------- + * Function: copy_some_to_file + * + * Purpose: Copy part of the input file to output. + * infid: fd of file to read + * outfid: fd of file to write + * startin: offset of where to read from infid + * startout: offset of where to write to outfid + * limit: bytes to read/write + * + * If limit is < 0, the entire input file is copied. + * + * Note: this routine can be used to copy within + * the same file, i.e., infid and outfid can be the + * same file. + * + * Return: Success: last byte written in the output. + * Failure: Exits program with EXIT_FAILURE value. + * + * Programmer: + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +hsize_t +copy_some_to_file (int infid, int outfid, hsize_t startin, hsize_t startout, + ssize_t limit) +{ + char buf[1024]; + struct stat sbuf; + int res; + ssize_t tot = 0; + ssize_t howmuch = 0; + ssize_t nchars = -1; +/* used in assertion check + ssize_t ncw = -1; +*/ + ssize_t to; + ssize_t from; + ssize_t toend; + ssize_t fromend; + + if (startin > startout) + { + /* this case is prohibited */ + error_msg (progname, "copy_some_to_file: panic: startin > startout?\n"); + exit (EXIT_FAILURE); + } + + if (limit < 0) + { + res = fstat (infid, &sbuf); + + if (res < 0) + { + error_msg (progname, "Can't stat file \n"); + exit (EXIT_FAILURE); + } + + howmuch = sbuf.st_size; + } + else + { + howmuch = limit; + } + + if (howmuch == 0) + { + return 0; + } + + /* assert (howmuch > 0) */ + + toend = (ssize_t) startout + howmuch; + fromend = (ssize_t) startin + howmuch; + + if (howmuch > 512) + { + to = toend - 512; + from = fromend - 512; + } + else + { + to = toend - howmuch; + from = fromend - howmuch; + } + + while (howmuch > 0) + { + HDlseek (outfid, (off_t) to, SEEK_SET); + HDlseek (infid, (off_t) from, SEEK_SET); + + if (howmuch > 512) + { + nchars = HDread (infid, buf, (unsigned) 512); + } + else + { + nchars = HDread (infid, buf, howmuch); + } + + if (nchars <= 0) + { + printf ("huh? \n"); + exit (1); + } + /*ncw = */ HDwrite (outfid, buf, (unsigned) nchars); + + /* assert (ncw == nchars) */ + + tot += nchars; + howmuch -= nchars; + if (howmuch > 512) + { + to -= nchars; + from -= nchars; + } + else + { + to -= howmuch; + from -= howmuch; + } + } + + /* assert howmuch == 0 */ + /* assert tot == limit */ + + return ((hsize_t) tot + (hsize_t) startout); +} + + +/*------------------------------------------------------------------------- + * Function: compute_user_block_size + * + * Purpose: Find the offset of the HDF5 header after the user block: + * align at 0, 512, 1024, etc. + * ublock_size: the size of the user block (bytes). + * + * Return: Success: the location of the header == the size of the + * padded user block. + * Failure: none + * + * Return: Success: last byte written in the output. + * Failure: Exits program with EXIT_FAILURE value. + * + * Programmer: + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +hsize_t +compute_user_block_size (hsize_t ublock_size) +{ + ssize_t where = 512; + + if (ublock_size == 0) + return 0; + + while (where < ublock_size) + { + where *= 2; + } + + return (where); +} + +/* + * Write zeroes to fill the file from 'where' to 512, 1024, etc. bytes. + * + * Returns the size of the padded file. + */ +hsize_t +write_pad (int ofile, hsize_t where) +{ + unsigned int i; + char buf[1]; + hsize_t psize; + + buf[0] = '\0'; + + HDlseek (ofile, (off_t) where, SEEK_SET); + + psize = compute_user_block_size (where); + psize -= where; + + for (i = 0; i < psize; i++) + { + HDwrite (ofile, buf, 1); + } + return (where + psize); /* the new size of the file. */ +} diff --git a/tools/h5jam/h5jamgentest.c b/tools/h5jam/h5jamgentest.c new file mode 100644 index 0000000..7f6342f --- /dev/null +++ b/tools/h5jam/h5jamgentest.c @@ -0,0 +1,639 @@ + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by the Board of Trustees of the University of Illinois. * + * 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 files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Generate the binary hdf5 files and user block data for the jam/unjam tests. + * Usage: just execute the program without any arguments will + * generate all the files in the local directory. + * + * If you regenerate the test files (e.g., changing some code, + * trying it on a new platform, ...), you need to verify the correctness + * of the expected output and update the corresponding *.ddl files. + */ +#include + +#include "hdf5.h" +#include "H5private.h" + +#define UBTXT1 "u0.txt" +#define UBTXT2 "u10.txt" +#define UBTXT3 "u511.txt" +#define UBTXT4 "u512.txt" +#define UBTXT5 "u513.txt" +/* not used yet +#define UBTXT6 "u1023.txt" +#define UBTXT7 "u1024.txt" +#define UBTXT8 "u1025.txt" +#define UBTXT9 "u2047.txt" +#define UBTXT10 "u2048.txt" +#define UBTXT11 "u2049.txt" +#define UBBIN1 "u0.dat" +#define UBBIN2 "u10.dat" +#define UBBIN3 "u511.dat" +#define UBBIN4 "u512.dat" +#define UBBIN5 "u513.dat" +*/ + +/* not used yet +#define FILE1 "tnull.h5" +#define FILE2 "tnullwithub.h5" +*/ +/* tall is same as dumper test */ +#define FILE7 "tall.h5" +#define FILE8 "twithub.h5" +#define FILE9 "twithub513.h5" + +/* + * This pattern is used to fill text files + */ +char pattern[11] = "abcdefghij"; + +/*------------------------------------------------------------------------- + * prototypes + *------------------------------------------------------------------------- + */ + + +#define LENSTR 50 +#define LENSTR2 11 + +#define SPACE2_RANK 2 +#define SPACE2_DIM1 10 +#define SPACE2_DIM2 10 + +#define SPACE1_RANK 1 +#define SPACE1_DIM1 4 + +#define DIM1 20 +#define DIM2 10 +#define CDIM1 DIM1/2 +#define CDIM2 DIM2/2 +#define RANK 2 + +/* Element selection information */ +#define POINT1_NPOINTS 10 + +typedef enum{ + RED, + GREEN, + BLUE, + WHITE, + BLACK +} enumtype; + +/* Compound datatype */ +typedef struct s1_t { + unsigned int a; + unsigned int b; + float c; +} s1_t; + + +/* 1-D array datatype */ +#define ARRAY1_RANK 1 +#define ARRAY1_DIM1 4 + +/* 3-D array datatype */ +#define ARRAY2_RANK 3 +#define ARRAY2_DIM1 3 +#define ARRAY2_DIM2 4 +#define ARRAY2_DIM3 5 + +/* 2-D array datatype */ +#define ARRAY3_RANK 2 +#define ARRAY3_DIM1 6 +#define ARRAY3_DIM2 3 + +/* VL string datatype name */ +#define VLSTR_TYPE "vl_string_type" + + +/* + +/ : g1 g2 attr1 attr2 +g1 : g1.1 g1.2 +g1.1 : dset1.1.1(attr1, attr2) dset1.1.2 +g1.2 : g1.2.1 +g1.2.1 : slink +g2 : dset2.1 dset2.2 + +*/ + + +static void gent_all(void) { +hid_t fid, group, attr, dataset, space; +hsize_t dims[2]; +int data[2][2], dset1[10][10], dset2[20]; +char buf[60]; +int i, j; +float dset2_1[10], dset2_2[3][5]; + + fid = H5Fcreate(FILE7, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); + + /* create groups */ + group = H5Gcreate (fid, "/g1", 0); + H5Gclose(group); + + group = H5Gcreate (fid, "/g2", 0); + H5Gclose(group); + + group = H5Gcreate (fid, "/g1/g1.1", 0); + H5Gclose(group); + + group = H5Gcreate (fid, "/g1/g1.2", 0); + H5Gclose(group); + + group = H5Gcreate (fid, "/g1/g1.2/g1.2.1", 0); + H5Gclose(group); + + /* root attributes */ + group = H5Gopen (fid, "/"); + + dims[0] = 10; + space = H5Screate_simple(1, dims, NULL); + attr = H5Acreate (group, "attr1", H5T_STD_I8BE, space, H5P_DEFAULT); + sprintf(buf, "abcdefghi"); + H5Awrite(attr, H5T_NATIVE_SCHAR, buf); + H5Sclose(space); + H5Aclose(attr); + + dims[0] = 2; dims[1] = 2; + space = H5Screate_simple(2, dims, NULL); + attr = H5Acreate (group, "attr2", H5T_STD_I32BE, space, H5P_DEFAULT); + data[0][0] = 0; data[0][1] = 1; data[1][0] = 2; data[1][1] = 3; + H5Awrite(attr, H5T_NATIVE_INT, data); + H5Sclose(space); + H5Aclose(attr); + + H5Gclose(group); + + group = H5Gopen (fid, "/g1/g1.1"); + + /* dset1.1.1 */ + dims[0] = 10; dims[1] = 10; + space = H5Screate_simple(2, dims, NULL); + dataset = H5Dcreate(group, "dset1.1.1", H5T_STD_I32BE, space, H5P_DEFAULT); + for (i = 0; i < 10; i++) + for (j = 0; j < 10; j++) + dset1[i][j] = j*i; + H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset1); + H5Sclose(space); + + /* attributes of dset1.1.1 */ + dims[0] = 27; + space = H5Screate_simple(1, dims, NULL); + attr = H5Acreate (dataset, "attr1", H5T_STD_I8BE, space, H5P_DEFAULT); + sprintf(buf, "1st attribute of dset1.1.1"); + H5Awrite(attr, H5T_NATIVE_SCHAR, buf); + H5Sclose(space); + H5Aclose(attr); + + dims[0] = 27; + space = H5Screate_simple(1, dims, NULL); + attr = H5Acreate (dataset, "attr2", H5T_STD_I8BE, space, H5P_DEFAULT); + sprintf(buf, "2nd attribute of dset1.1.1"); + H5Awrite(attr, H5T_NATIVE_SCHAR, buf); + H5Sclose(space); + H5Aclose(attr); + + H5Dclose(dataset); + + /* dset1.1.2 */ + dims[0] = 20; + space = H5Screate_simple(1, dims, NULL); + dataset = H5Dcreate(group, "dset1.1.2", H5T_STD_I32BE, space, H5P_DEFAULT); + for (i = 0; i < 20; i++) + dset2[i] = i; + H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset2); + H5Sclose(space); + H5Dclose(dataset); + + H5Gclose(group); + + /* soft link */ + group = H5Gopen (fid, "/g1/g1.2/g1.2.1"); + H5Glink (group, H5G_LINK_SOFT, "somevalue", "slink"); + H5Gclose(group); + + group = H5Gopen (fid, "/g2"); + + /* dset2.1 */ + dims[0] = 10; + space = H5Screate_simple(1, dims, NULL); + dataset = H5Dcreate(group, "dset2.1", H5T_IEEE_F32BE, space, H5P_DEFAULT); + for (i = 0; i < 10; i++) + dset2_1[i] = (float)(i*0.1+1); + H5Dwrite(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset2_1); + H5Sclose(space); + H5Dclose(dataset); + + /* dset2.2 */ + dims[0] = 3; dims[1] = 5; + space = H5Screate_simple(2, dims, NULL); + dataset = H5Dcreate(group, "dset2.2", H5T_IEEE_F32BE, space, H5P_DEFAULT); + for (i = 0; i < 3; i++) + for (j = 0; j < 5; j++) + dset2_2[i][j] = (float)((i+1)*j*0.1); + H5Dwrite(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset2_2); + H5Sclose(space); + H5Dclose(dataset); + + H5Gclose(group); + + H5Fclose(fid); + +} + +static void gent_withub(void) { +hid_t fid, group, attr, dataset, space; +hid_t create_plist; +hsize_t dims[2]; +int data[2][2], dset1[10][10], dset2[20]; +char buf[512]; +int i, j; +float dset2_1[10], dset2_2[3][5]; +int fd; +char *bp; + + create_plist = H5Pcreate(H5P_FILE_CREATE); + H5Pset_userblock(create_plist,512); + fid = H5Fcreate(FILE8, H5F_ACC_TRUNC, create_plist, H5P_DEFAULT); + + /* create groups */ + group = H5Gcreate (fid, "/g1", 0); + H5Gclose(group); + + group = H5Gcreate (fid, "/g2", 0); + H5Gclose(group); + + group = H5Gcreate (fid, "/g1/g1.1", 0); + H5Gclose(group); + + group = H5Gcreate (fid, "/g1/g1.2", 0); + H5Gclose(group); + + group = H5Gcreate (fid, "/g1/g1.2/g1.2.1", 0); + H5Gclose(group); + + /* root attributes */ + group = H5Gopen (fid, "/"); + + dims[0] = 10; + space = H5Screate_simple(1, dims, NULL); + attr = H5Acreate (group, "attr1", H5T_STD_I8BE, space, H5P_DEFAULT); + sprintf(buf, "abcdefghi"); + H5Awrite(attr, H5T_NATIVE_SCHAR, buf); + H5Sclose(space); + H5Aclose(attr); + + dims[0] = 2; dims[1] = 2; + space = H5Screate_simple(2, dims, NULL); + attr = H5Acreate (group, "attr2", H5T_STD_I32BE, space, H5P_DEFAULT); + data[0][0] = 0; data[0][1] = 1; data[1][0] = 2; data[1][1] = 3; + H5Awrite(attr, H5T_NATIVE_INT, data); + H5Sclose(space); + H5Aclose(attr); + + H5Gclose(group); + + group = H5Gopen (fid, "/g1/g1.1"); + + /* dset1.1.1 */ + dims[0] = 10; dims[1] = 10; + space = H5Screate_simple(2, dims, NULL); + dataset = H5Dcreate(group, "dset1.1.1", H5T_STD_I32BE, space, H5P_DEFAULT); + for (i = 0; i < 10; i++) + for (j = 0; j < 10; j++) + dset1[i][j] = j*i; + H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset1); + H5Sclose(space); + + /* attributes of dset1.1.1 */ + dims[0] = 27; + space = H5Screate_simple(1, dims, NULL); + attr = H5Acreate (dataset, "attr1", H5T_STD_I8BE, space, H5P_DEFAULT); + sprintf(buf, "1st attribute of dset1.1.1"); + H5Awrite(attr, H5T_NATIVE_SCHAR, buf); + H5Sclose(space); + H5Aclose(attr); + + dims[0] = 27; + space = H5Screate_simple(1, dims, NULL); + attr = H5Acreate (dataset, "attr2", H5T_STD_I8BE, space, H5P_DEFAULT); + sprintf(buf, "2nd attribute of dset1.1.1"); + H5Awrite(attr, H5T_NATIVE_SCHAR, buf); + H5Sclose(space); + H5Aclose(attr); + + H5Dclose(dataset); + + /* dset1.1.2 */ + dims[0] = 20; + space = H5Screate_simple(1, dims, NULL); + dataset = H5Dcreate(group, "dset1.1.2", H5T_STD_I32BE, space, H5P_DEFAULT); + for (i = 0; i < 20; i++) + dset2[i] = i; + H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset2); + H5Sclose(space); + H5Dclose(dataset); + + H5Gclose(group); + + /* soft link */ + group = H5Gopen (fid, "/g1/g1.2/g1.2.1"); + H5Glink (group, H5G_LINK_SOFT, "somevalue", "slink"); + H5Gclose(group); + + group = H5Gopen (fid, "/g2"); + + /* dset2.1 */ + dims[0] = 10; + space = H5Screate_simple(1, dims, NULL); + dataset = H5Dcreate(group, "dset2.1", H5T_IEEE_F32BE, space, H5P_DEFAULT); + for (i = 0; i < 10; i++) + dset2_1[i] = (float)(i*0.1+1); + H5Dwrite(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset2_1); + H5Sclose(space); + H5Dclose(dataset); + + /* dset2.2 */ + dims[0] = 3; dims[1] = 5; + space = H5Screate_simple(2, dims, NULL); + dataset = H5Dcreate(group, "dset2.2", H5T_IEEE_F32BE, space, H5P_DEFAULT); + for (i = 0; i < 3; i++) + for (j = 0; j < 5; j++) + dset2_2[i][j] = (float)((i+1)*j*0.1); + H5Dwrite(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset2_2); + H5Sclose(space); + H5Dclose(dataset); + + H5Gclose(group); + + H5Fclose(fid); + + + fd = HDopen(FILE8,O_RDWR, 0); + if (fd < 0) { + /* panic */ + } + /* fill buf with pattern */ + memset(buf,'\0',512); + bp = buf; + for (i = 0; i < strlen(pattern); i++) { + *bp++ = pattern[i%10]; + } + + HDwrite(fd,buf,512); + + close(fd); +} + +static void gent_withub513(void) { +hid_t fid, group, attr, dataset, space; +hid_t create_plist; +hsize_t dims[2]; +int data[2][2], dset1[10][10], dset2[20]; +char buf[1023]; +int i, j; +float dset2_1[10], dset2_2[3][5]; +int fd; +char *bp; + + create_plist = H5Pcreate(H5P_FILE_CREATE); + H5Pset_userblock(create_plist,1024); + fid = H5Fcreate(FILE9, H5F_ACC_TRUNC, create_plist, H5P_DEFAULT); + + /* create groups */ + group = H5Gcreate (fid, "/g1", 0); + H5Gclose(group); + + group = H5Gcreate (fid, "/g2", 0); + H5Gclose(group); + + group = H5Gcreate (fid, "/g1/g1.1", 0); + H5Gclose(group); + + group = H5Gcreate (fid, "/g1/g1.2", 0); + H5Gclose(group); + + group = H5Gcreate (fid, "/g1/g1.2/g1.2.1", 0); + H5Gclose(group); + + /* root attributes */ + group = H5Gopen (fid, "/"); + + dims[0] = 10; + space = H5Screate_simple(1, dims, NULL); + attr = H5Acreate (group, "attr1", H5T_STD_I8BE, space, H5P_DEFAULT); + sprintf(buf, "abcdefghi"); + H5Awrite(attr, H5T_NATIVE_SCHAR, buf); + H5Sclose(space); + H5Aclose(attr); + + dims[0] = 2; dims[1] = 2; + space = H5Screate_simple(2, dims, NULL); + attr = H5Acreate (group, "attr2", H5T_STD_I32BE, space, H5P_DEFAULT); + data[0][0] = 0; data[0][1] = 1; data[1][0] = 2; data[1][1] = 3; + H5Awrite(attr, H5T_NATIVE_INT, data); + H5Sclose(space); + H5Aclose(attr); + + H5Gclose(group); + + group = H5Gopen (fid, "/g1/g1.1"); + + /* dset1.1.1 */ + dims[0] = 10; dims[1] = 10; + space = H5Screate_simple(2, dims, NULL); + dataset = H5Dcreate(group, "dset1.1.1", H5T_STD_I32BE, space, H5P_DEFAULT); + for (i = 0; i < 10; i++) + for (j = 0; j < 10; j++) + dset1[i][j] = j*i; + H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset1); + H5Sclose(space); + + /* attributes of dset1.1.1 */ + dims[0] = 27; + space = H5Screate_simple(1, dims, NULL); + attr = H5Acreate (dataset, "attr1", H5T_STD_I8BE, space, H5P_DEFAULT); + sprintf(buf, "1st attribute of dset1.1.1"); + H5Awrite(attr, H5T_NATIVE_SCHAR, buf); + H5Sclose(space); + H5Aclose(attr); + + dims[0] = 27; + space = H5Screate_simple(1, dims, NULL); + attr = H5Acreate (dataset, "attr2", H5T_STD_I8BE, space, H5P_DEFAULT); + sprintf(buf, "2nd attribute of dset1.1.1"); + H5Awrite(attr, H5T_NATIVE_SCHAR, buf); + H5Sclose(space); + H5Aclose(attr); + + H5Dclose(dataset); + + /* dset1.1.2 */ + dims[0] = 20; + space = H5Screate_simple(1, dims, NULL); + dataset = H5Dcreate(group, "dset1.1.2", H5T_STD_I32BE, space, H5P_DEFAULT); + for (i = 0; i < 20; i++) + dset2[i] = i; + H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset2); + H5Sclose(space); + H5Dclose(dataset); + + H5Gclose(group); + + /* soft link */ + group = H5Gopen (fid, "/g1/g1.2/g1.2.1"); + H5Glink (group, H5G_LINK_SOFT, "somevalue", "slink"); + H5Gclose(group); + + group = H5Gopen (fid, "/g2"); + + /* dset2.1 */ + dims[0] = 10; + space = H5Screate_simple(1, dims, NULL); + dataset = H5Dcreate(group, "dset2.1", H5T_IEEE_F32BE, space, H5P_DEFAULT); + for (i = 0; i < 10; i++) + dset2_1[i] = (float)(i*0.1+1); + H5Dwrite(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset2_1); + H5Sclose(space); + H5Dclose(dataset); + + /* dset2.2 */ + dims[0] = 3; dims[1] = 5; + space = H5Screate_simple(2, dims, NULL); + dataset = H5Dcreate(group, "dset2.2", H5T_IEEE_F32BE, space, H5P_DEFAULT); + for (i = 0; i < 3; i++) + for (j = 0; j < 5; j++) + dset2_2[i][j] = (float)((i+1)*j*0.1); + H5Dwrite(dataset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset2_2); + H5Sclose(space); + H5Dclose(dataset); + + H5Gclose(group); + + H5Fclose(fid); + + + fd = HDopen(FILE9,O_RDWR, 0); + if (fd < 0) { + /* panic */ + } + /* fill buf with pattern */ + memset(buf,'\0',1024); + bp = buf; + for (i = 0; i < 513; i++) { + *bp++ = pattern[i%10]; + } + + HDwrite(fd,buf,1024); + + close(fd); +} + +void +create_textfile(char *name, off_t size) { +char *buf; +int fd; +int i; +char *bp; + + fd = creat(name,0777); + if (fd < 0) { + /* panic */ + } + buf = calloc(size,1); + if (buf == NULL) { + /* panic */ + } + /* fill buf with pattern */ + bp = buf; + for (i = 0; i < size; i++) { + *bp++ = pattern[i%10]; + } + + + HDwrite(fd,buf,size); + + close(fd); +} + +#ifdef notdef +/* not used yet */ +void +create_binfile(char *name, off_t size) { +char *buf; +int fd; +int i; +char *bp; + + fd = creat(name,0777); + if (fd < 0) { + /* panic */ + } + buf = calloc(size,1); + if (buf == NULL) { + /* panic */ + } + /* fill buf with pattern */ + bp = buf; + for (i = 0; i < size; i++) { + *bp++ = (char) i & 0xff; + } + + HDwrite(fd,buf,size); + + close(fd); +} +#endif + +/*------------------------------------------------------------------------- + * Function: main + * + *------------------------------------------------------------------------- + */ + + +int main(void) +{ + +/* +create_textfile(UBTXT1,0); +*/ +create_textfile(UBTXT2,10); +create_textfile(UBTXT3,511); +create_textfile(UBTXT4,512); +create_textfile(UBTXT5,513); +/* +create_textfile(UBTXT6,1023); +create_textfile(UBTXT7,1024); +create_textfile(UBTXT8,1025); +create_textfile(UBTXT9,2047); +create_textfile(UBTXT10,2048); +create_textfile(UBTXT11,2049); + +create_binfile(UBBIN1,0); +create_binfile(UBBIN2,10); +create_binfile(UBBIN3,511); +create_binfile(UBBIN4,512); +create_binfile(UBBIN5,513); + +*/ + gent_all(); + gent_withub(); + gent_withub513(); + + return 0; +} diff --git a/tools/h5jam/h5unjam.c b/tools/h5jam/h5unjam.c new file mode 100644 index 0000000..20378e5 --- /dev/null +++ b/tools/h5jam/h5unjam.c @@ -0,0 +1,317 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by the Board of Trustees of the University of Illinois. * + * 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 files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include +#include + +#ifdef H5_HAVE_UNISTD_H +#include +#endif + +#include "hdf5.h" +#include "H5private.h" +#include "h5tools_utils.h" + +#define TRUE 1 +#define FALSE 0 + +hsize_t write_pad( int , hsize_t ); +hsize_t compute_pad( hsize_t ); +hsize_t copy_to_file( int , int , ssize_t, ssize_t ); + +const char *progname = "unjam"; +int d_status = EXIT_SUCCESS; +int do_delete = FALSE; +char *output_file = NULL; +char *input_file = NULL; +char *ub_file = NULL; + +/* + * Command-line options: The user can specify short or long-named + * parameters. The long-named ones can be partially spelled. When + * adding more, make sure that they don't clash with each other. + */ +static const char *s_opts = "hu:i:o:d"; +static struct long_options l_opts[] = { + { "help", no_arg, 'h' }, + { "hel", no_arg, 'h' }, + {"i", require_arg, 'i'}, /* input file */ + {"u", require_arg, 'u'}, /* user block file */ + {"o", require_arg, 'o'}, /* output file */ + {"delete", no_arg, 'd'}, /* delete ub */ + {"delet", no_arg, 'd'}, + {"dele", no_arg, 'd'}, + {"del", no_arg, 'd'}, + {"de", no_arg, 'd'}, + { NULL, 0, '\0' } +}; + +/*------------------------------------------------------------------------- + * Function: usage + * + * Purpose: Print the usage message + * + * Return: void + * + * Programmer: + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +usage(const char *prog) +{ + fflush(stdout); + fprintf(stdout, "usage: %s -i h5_file -o user_block_file_out -o h5_file_out [-d | --delete]\n", prog); + fprintf(stdout, " Extract user block from 'h5_file' into 'user_block_file'\n"); + fprintf(stdout, " and HDF5 file into 'h5_file_out'\n"); + + fprintf(stdout, " %s -h\n",prog); + fprintf(stdout, " Print a usage message and exit\n"); +} + +/*------------------------------------------------------------------------- + * Function: parse_command_line + * + * Purpose: Parse the command line for the h5dumper. + * + * Return: Success: + * + * Failure: Exits program with EXIT_FAILURE value. + * + * Programmer: + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +void +parse_command_line(int argc, const char *argv[]) +{ + int opt = FALSE; + + /* parse command line options */ + while ((opt = get_option(argc, argv, s_opts, l_opts)) != EOF) { + switch ((char)opt) { + case 'o': + output_file = strdup (opt_arg); + break; + case 'i': + input_file = strdup (opt_arg); + break; + case 'u': + ub_file = strdup (opt_arg); + break; + case 'd': + do_delete = TRUE; + break; + case 'h': + usage(progname); + exit(EXIT_SUCCESS); + case '?': + default: + usage(progname); + exit(EXIT_FAILURE); + } + } + + /* check for file name to be processed */ +/* + if (argc <= opt_ind+2) { + error_msg(progname, "missing file name\n"); + usage(progname); + exit(EXIT_FAILURE); + } +*/ +} + +/*------------------------------------------------------------------------- + * Function: main + * + * Purpose: HDF5 user block unjammer + * + * Return: Success: 0 + * Failure: 1 + * + * Programmer: + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +int +main(int argc, const char *argv[]) +{ + int ifid; + int ufid; + int h5fid; + void *edata; + H5E_auto_stack_t func; + hid_t ifile; + off_t fsize; + hsize_t usize; + htri_t testval; + herr_t status; + hid_t plist; + int res; + struct stat sbuf; + + /* Disable error reporting */ + H5Eget_auto_stack(H5E_DEFAULT, &func, &edata); + H5Eset_auto_stack(H5E_DEFAULT, NULL, NULL); + + parse_command_line(argc, argv); + + testval = H5Fis_hdf5(input_file); + + if (testval <= 0) { + error_msg(progname, "Input HDF5 file is not HDF \"%s\"\n", input_file); + exit(EXIT_FAILURE); + } + + ifile = H5Fopen(input_file, H5F_ACC_RDONLY , H5P_DEFAULT); + + if (ifile < 0) { + error_msg(progname, "Can't open input HDF5 file \"%s\"\n", input_file); + exit(EXIT_FAILURE); + } + + plist = H5Fget_create_plist(ifile); + if (plist < 0) { + error_msg(progname, "Can't get file creation plist for file \"%s\"\n", input_file); + exit(EXIT_FAILURE); + } + + status = H5Pget_userblock(plist, & usize ); + if (status < 0) { + error_msg(progname, "Can't get user block for file \"%s\"\n", input_file); + exit(EXIT_FAILURE); + } + + if (usize == 0) { + /* no user block to remove: message? */ + error_msg(progname, "\"%s\" has no user block: no change to file\n", input_file); + exit(EXIT_SUCCESS); + + } + + res = stat(input_file, &sbuf); + + if (res < 0) { + error_msg(progname, "Can't stat file \"%s\"\n", input_file); + exit(EXIT_FAILURE); + } + + fsize = sbuf.st_size; + + ifid = HDopen(input_file,O_RDONLY,0); + + if (ifid < 0) { + error_msg(progname, "unable to open input HDF5 file \"%s\"\n", input_file); + exit(EXIT_FAILURE); + } + + if (do_delete && (ub_file != NULL)) { + error_msg(progname, "??\"%s\"\n", ub_file); + exit(EXIT_FAILURE); + } + + if (ub_file == NULL) { + /* write to sdtout */ + ufid = dup(1); + } else { + ufid = HDopen(ub_file,O_WRONLY|O_CREAT|O_TRUNC, 0644 ); + + if (ufid < 0) { + error_msg(progname, "unable to open user block file for output\"%s\"\n", ub_file); + exit(EXIT_FAILURE); + } + } + + if (output_file == NULL) { + h5fid = HDopen(input_file,O_WRONLY, 0); + + if (h5fid < 0) { + error_msg(progname, "unable to open output HDF5 file \"%s\"\n", input_file); + exit(EXIT_FAILURE); + } + } else { + h5fid = open(output_file,O_WRONLY|O_CREAT|O_TRUNC, 0644 ); + + if (h5fid < 0) { + error_msg(progname, "unable to open output HDF5 file \"%s\"\n", output_file); + exit(EXIT_FAILURE); + } + } + + + /* copy from 0 to 'usize - 1' into ufid */ + if (!do_delete) { + copy_to_file( ifid, ufid, 0, (ssize_t) usize); + } + + /* copy from usize to end of file into h5fid, + * starting at end of user block if present + */ + copy_to_file( ifid, h5fid, (ssize_t) usize, (fsize - (ssize_t)usize) ); + + + close(ufid); + close(h5fid); + close(ifid); + + return d_status; +} + +/* + * Copy 'how_much' bytes from the input file to the output file, + * starting at byte 'where' in the input file. + * + * Returns the size of the output file. + */ +hsize_t +copy_to_file( int infid, int ofid, ssize_t where, ssize_t how_much ) { + char buf[1024]; + off_t to; + off_t from; + ssize_t nchars = -1; + + + if (how_much <= 0) { + /* nothing to copy */ + return(where); + } + from = where; + to = 0; + + while( how_much > 0) { + HDlseek(infid,from,SEEK_SET); + if (how_much > 512) { + nchars = HDread(infid,buf,(unsigned)512); + } else { + nchars = HDread(infid,buf,(unsigned)how_much); + } + HDlseek(ofid,to,SEEK_SET); + HDwrite(ofid,buf,(unsigned)nchars); + how_much -= nchars; + from += nchars; + to += nchars; + } + + return (where+how_much); +} diff --git a/tools/h5jam/tellub.c b/tools/h5jam/tellub.c new file mode 100644 index 0000000..ab88f4d --- /dev/null +++ b/tools/h5jam/tellub.c @@ -0,0 +1,189 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by the Board of Trustees of the University of Illinois. * + * 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 files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include + +#ifdef H5_HAVE_UNISTD_H +#include +#endif + +#include "hdf5.h" +#include "H5private.h" +#include "h5tools_utils.h" + +#define TRUE 1 +#define FALSE 0 + +const char *progname = "tellub"; + +/* + * Command-line options: The user can specify short or long-named + * parameters. The long-named ones can be partially spelled. When + * adding more, make sure that they don't clash with each other. + */ +static const char *s_opts = "h"; +static struct long_options l_opts[] = { + {"help", no_arg, 'h'}, + {"hel", no_arg, 'h'}, + {NULL, 0, '\0'} +}; + +/*------------------------------------------------------------------------- + * Function: usage + * + * Purpose: Print the usage message + * + * Return: void + * + * Programmer: + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +usage (const char *prog) +{ + fflush (stdout); + fprintf (stdout, "usage: %s h5_file\n", prog); + fprintf (stdout, + " Check that h5_fil is HDF5 file and print size of user block \n"); + fprintf (stdout, " %s -h\n", prog); + fprintf (stdout, " Print a usage message and exit\n"); +} + +/*------------------------------------------------------------------------- + * Function: parse_command_line + * + * Purpose: Parse the command line for the h5dumper. + * + * Return: Success: + * + * Failure: Exits program with EXIT_FAILURE value. + * + * Programmer: + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +void +parse_command_line (int argc, const char *argv[]) +{ + int opt = FALSE; + + /* parse command line options */ + while ((opt = get_option (argc, argv, s_opts, l_opts)) != EOF) + { + switch ((char) opt) + { + case 'h': + usage (progname); + exit (EXIT_SUCCESS); + case '?': + default: + usage (progname); + exit (EXIT_FAILURE); + } + } + + /* check for file name to be processed */ + if (argc <= opt_ind) + { + error_msg (progname, "missing file name\n"); + usage (progname); + exit (EXIT_FAILURE); + } +} + +/*------------------------------------------------------------------------- + * Function: main + * + * Purpose: HDF5 user block unjammer + * + * Return: Success: 0 + * Failure: 1 + * + * Programmer: + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +void +main (int argc, const char *argv[]) +{ + char *ifname; + void *edata; + H5E_auto_stack_t func; + hid_t ifile; + hsize_t usize; + htri_t testval; + herr_t status; + hid_t plist; + + /* Disable error reporting */ + H5Eget_auto_stack(H5E_DEFAULT, &func, &edata); + H5Eset_auto_stack(H5E_DEFAULT, NULL, NULL); + + parse_command_line (argc, argv); + + if (argc <= (opt_ind)) + { + error_msg (progname, "missing file name\n"); + usage (progname); + exit (EXIT_FAILURE); + } + + ifname = strdup (argv[opt_ind]); + + testval = H5Fis_hdf5 (ifname); + + if (testval <= 0) + { + error_msg (progname, "Input HDF5 file is not HDF \"%s\"\n", ifname); + exit (EXIT_FAILURE); + } + + ifile = H5Fopen (ifname, H5F_ACC_RDONLY, H5P_DEFAULT); + + if (ifile < 0) + { + error_msg (progname, "Can't open input HDF5 file \"%s\"\n", ifname); + exit (EXIT_FAILURE); + } + + plist = H5Fget_create_plist (ifile); + if (plist < 0) + { + error_msg (progname, "Can't get file creation plist for file \"%s\"\n", + ifname); + exit (EXIT_FAILURE); + } + + status = H5Pget_userblock (plist, &usize); + if (status < 0) + { + error_msg (progname, "Can't get user block for file \"%s\"\n", ifname); + exit (EXIT_FAILURE); + } + + printf ("%ld\n", (long) usize); + + H5Pclose (plist); + H5Fclose (ifile); + + exit (EXIT_SUCCESS); +} diff --git a/tools/h5jam/testh5jam.sh.in b/tools/h5jam/testh5jam.sh.in new file mode 100644 index 0000000..88e9a7c --- /dev/null +++ b/tools/h5jam/testh5jam.sh.in @@ -0,0 +1,527 @@ +#! /bin/sh +# +# Copyright by the Board of Trustees of the University of Illinois. +# 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 files COPYING and Copyright.html. COPYING can be found at the root +# of the source code distribution tree; Copyright.html can be found at the +# root level of an installed copy of the electronic HDF5 document set and +# is linked from the top-level documents page. It can also be found at +# http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have +# access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. +# +# Tests for the h5dump tool + +# Determine which filters are available +USE_FILTER_SZIP="@USE_FILTER_SZIP@" +USE_FILTER_DEFLATE="@USE_FILTER_DEFLATE@" +USE_FILTER_SHUFFLE="@USE_FILTER_SHUFFLE@" +USE_FILTER_FLETCHER32="@USE_FILTER_FLETCHER32@" + +DUMPER=h5dump # The dumper to use +DUMPER_BIN=`pwd`/../$DUMPER # The path of the dumper binary +JAM=h5jam # Tool to test +UNJAM=h5unjam # Tool to test +JAM_BIN=`pwd` # The path of the jam binary +UNJAM_BIN=`pwd` # The path of the jam binary + +CMP='cmp -s' +DIFF='diff -c' +AWK='awk' + +nerrors=0 +verbose=yes + +# The build (current) directory might be different than the source directory. +if test -z "$srcdir"; then + srcdir=. +fi +TESTFILES="$srcdir/../testfiles" + +#test -d ../testfiles || mkdir ../testfiles + +# Print a line-line message left justified in a field of 70 characters +# beginning with the word "Testing". +# +TESTING() { + SPACES=" " + echo "Testing $* $SPACES" | cut -c1-70 | tr -d '\012' +} + +# Print a line-line message left justified in a field of 70 characters +# beginning with the word "Compare". +# +COMPARE() { + SPACES=" " + echo "Compare $* $SPACES" | cut -c1-70 | tr -d '\012' +} + +# Print a "SKIP" message +SKIP() { + TESTING $JAM $@ + echo " -SKIP-" +} + +# +# COMPARE_FILES a.h5 b.h5 +# Compare two files, skipping the first line. This is used to +# compare the output of the dumper, skipping the file name which +# is different. +# The result is stored in 'compval'. +# +cmpval=0; +COMPARE_FILES() { + $AWK 'NR > 1' $1 > $1.cmp + $AWK 'NR > 1' $2 > $2.cmp + $CMP $1.cmp $2.cmp + cmpval=$? + rm -f $1.cmp $2.cmp +} + +# CLEANUP files +# Clean up named files. +CLEANUP() { + if test -z "$HDF5_NOCLEANUP"; then + for i in $* + do + rm -f $i + done + fi +} + +# SETUP file tocopy +# Clone a standard input file in the test directory +# +SETUP() { + cp $1 $2 +} + +# +# CHECKFILE orig.h5 compar.h5 +# Check that the test file is the same as an original. +# The two files are dumped with the dumper, and the output +# compared with COMPARE_FILES. +# If the files are the same, the test reports " PASSED", +# otherwise, it reports "*FAILED*" +CHECKFILE() { + expected="`dirname $2`/`basename $2 .h5`.out" + expected_err="`dirname $2`/`basename $2 .h5`.err" + actual="`basename $1 .h5`.out" + actual_err="`basename $1 .h5`.err" + + $RUNSERIAL $DUMPER_BIN/$DUMPER $1 >$expected 2>$expected_err + cat $expected_err >> $expected + + # dump the test file + COMPARE $2 to $1 + $RUNSERIAL $DUMPER_BIN/$DUMPER $2 >$actual 2>$actual_err + cat $actual_err >> $actual + + # compare the two files (ignore line 1) + COMPARE_FILES $actual $expected + if [ "$cmpval" = 0 ] ; then + echo " PASSED" + else + echo "*FAILED*" + echo " Expected result (*.ddl) differs from actual result (*.out)" + nerrors="`expr $nerrors + 1`" + test yes = "$verbose" && $DIFF $expected $actual |sed 's/^/ /' + fi + + # Clean up output files + if test -z "$HDF5_NOCLEANUP"; then + rm -f $actual $actual_err + rm -f $expected $expected_err + fi +} + +# +# CHECK_UB file.h5 user_block_file origfile.h5 +# +# Check the user block in 'file.h5' is the same as +# 'user_block' (allowing for padding). +# +# If the original file had a user block before the test +# then 'compare.h5' is passed. The user block must be extracted +# and the test file compared to: +# cat compare_ub user_block_file. +# +# This test uses './getub' to extract the user block from +# 'file.h5', which is compared to the file described above. +# +# The result is set in variable 'result1'. +# +result1=0; +CHECK_UB_1() { + hfile="$1" + ufile="$2" + + # check for third argument (the original file) + origfile=""; + if [ -n "$3" ]; + then + origfile="$3" + fi + + # find the length of the user block to check + s1=`cat $ufile | wc -c | sed -e 's/ //g'` + if [ "$s1" = "0" ]; + then + echo "File "$ufile" is empty" + result1=1; + fi + + # Get the size of the original user block, if any. + if [ -n "$origfile" ]; + then + # 'tellub' calls H5Fget_user_block to get the size + # of the user block + s2=`$JAM_BIN/tellub $origfile` + if [ "$s2" = "0" ]; + then + size=$s1; + cmpfile=$ufile + else + cmpfile="tt2" + size=`expr $s2 + $s1` + ./getub -c $s2 $origfile > $cmpfile + cat $ufile >> $cmpfile + fi + else + # assume no user block + s2="0" + size=$s1; + cmpfile=$ufile + fi + + # Extract 'size' bytes from the front of 'hfile' + # Compare to 'cmpfile', result is set in result1 + tfile="tt1" + ./getub -c $size $hfile > $tfile + res=`cmp $cmpfile $tfile` + if [ "$?" != "0" ]; + then + echo $res + result1=1; + else + result1=0; + fi + + # clean up + rm -f $tfile + if [ "$s2" != "0" ] ; + then + rm -f $cmpfile + fi +} + + +# CHECK_NOUB file.h5 +# +# Check that 'file.h5' has no user block. +# Setst result2 to 1 if there is a user block (fail), 0 if none (pass) + +result2=0; + +CHECK_NOUB() { + hfile="$1" + + # call 'ubsize' to get the size of the user block + ubsize=`$JAM_BIN/tellub $hfile` + + if [ "$?" != "0" ]; + then + # error + result2=1; + else + if [ "$ubsize" = "0" ]; + then + # pass + result2=0; + else + # fail + result2=1; + fi + fi +} + +# JAMTEST user_block file.h5 [--clobber] [ofile.h5] +# +# Test the 'jam' tool: +# 1. figure out the input and output, and the comparision +# that will be done. +# 2. call 'jam' with the appropriate arguments +# 3. check the user block is correct in the output (Check_UB) +# If the user block is correct, print "PASSED", else "*FAILED*" +JAMTEST() { + ufile="$1" + ifile="$2" + compare_test="" # the file to test + compare_orig="" # the comparison to test against + cleanup="" + + # sort out the arguments for the test and the check + do_clobber="no" + if [ "$3" = "--clobber" ]; + then + # clobber overwrites any existing user block + do_clobber="yes" + clobber="--clobber" + compare_orig="" + if [ -z "$4" ]; + then + # output goes to infile, compare ubfile to infile + ofile="" + compare_test="$ifile" + else + # output goes to $4, compare ofile to ubfile + ofile="$4" + compare_test="$ofile" + fi + else + clobber="" + # add user block to existing ub, if any + if [ -z "$3" ]; + then + # output goes to infile, compare ubfile to infile + ofile="" + compare_test="$ifile" + cp $ifile xxofile.h5 + compare_orig="xxofile.h5" + cleanup="$cleanup $compare_orig" + else + # output goes to $4, compare ofile to ubfile + ofile="$3" + compare_test="$ofile" + compare_orig="$ifile" + fi + fi + + # call 'jam' with the appropriate arguments + if [ -n "$ofile" ]; + then + TESTING h5jam -u `basename $ufile` -i `basename $ifile` -o `basename $ofile` $clobber + $JAM_BIN/$JAM -u $ufile -i $ifile -o $ofile $clobber + else + TESTING jam -u `basename $ufile` -i `basename $ifile` $clobber + $JAM_BIN/$JAM -u $ufile -i $ifile $clobber + fi + + #echo "CHECK_UB_1 $compare_test $ufile $compare_orig" + CHECK_UB_1 $compare_test $ufile $compare_orig + + if [ "$result1" = "0" ] ; + then + echo " PASSED" + else + echo " *FAILED*" + nerrors="`expr $nerrors + 1`" + fi + CLEANUP $cleanup +} + +# UNJAMTEST file.h5 [- | --delete] ofile +# +# Test the 'unjam' tool +# +###fix the working directory here and in jamtest +UNJAMTEST () { + infile="$1" + ofile="$3" + if [ "$2" = "-" ]; + then + uofile="uofile" + TESTING h5unjam -i `basename $infile` -o `basename $ofile` "> "`basename $uofile` + $JAM_BIN/$UNJAM -i $infile -o $ofile > $uofile + else + if [ "$2" = "--delete" ]; + then + uofile="none" + TESTING h5unjam -i `basename $infile` -o `basename $ofile` --delete + $JAM_BIN/$UNJAM -i $infile -o $ofile --delete + + else + uofile="$2" + TESTING h5unjam -i `basename $infile` -u `basename $uofile` -o `basename $ofile` + $JAM_BIN/$UNJAM -i $infile -u $uofile -o $ofile + fi + fi + + result1=0 + result2=0 + cleanup="" + if [ "$uofile" != "none" ]; + then + # sets result1 + CHECK_UB_1 $infile $uofile + CLEANUP $uofile + fi + + # sets result2 + CHECK_NOUB $ofile + + if [ "$result1" = "0" -a "$result2" = "0" ]; + then + echo " PASSED" + else + echo " *FAILED*" + nerrors="`expr $nerrors + 1`" + fi +} + + +############################################################################## +############################################################################## +### T H E T E S T S ### +############################################################################## +############################################################################## + +JAMTEST $TESTFILES/u10.txt $TESTFILES/tall.h5 ta2.h5 +CHECKFILE $TESTFILES/tall.h5 ta2.h5 +CLEANUP ta2.h5 +JAMTEST $TESTFILES/u511.txt $TESTFILES/tall.h5 ta3.h5 +CHECKFILE $TESTFILES/tall.h5 ta3.h5 +CLEANUP ta3.h5 +JAMTEST $TESTFILES/u512.txt $TESTFILES/tall.h5 ta4.h5 +CHECKFILE $TESTFILES/tall.h5 ta4.h5 +CLEANUP ta4.h5 +JAMTEST $TESTFILES/u513.txt $TESTFILES/tall.h5 ta5.h5 +CHECKFILE $TESTFILES/tall.h5 ta5.h5 +CLEANUP ta5.h5 + +SETUP $TESTFILES/tall.h5 ta.h5 +JAMTEST $TESTFILES/u10.txt ta.h5 +CHECKFILE $TESTFILES/tall.h5 ta.h5 +SETUP $TESTFILES/tall.h5 ta.h5 +JAMTEST $TESTFILES/u511.txt ta.h5 +CHECKFILE $TESTFILES/tall.h5 ta.h5 +SETUP $TESTFILES/tall.h5 ta.h5 +JAMTEST $TESTFILES/u512.txt ta.h5 +CHECKFILE $TESTFILES/tall.h5 ta.h5 +SETUP $TESTFILES/tall.h5 ta.h5 +JAMTEST $TESTFILES/u513.txt ta.h5 +CHECKFILE $TESTFILES/tall.h5 ta.h5 +CLEANUP ta.h5 + +JAMTEST $TESTFILES/u10.txt $TESTFILES/twithub.h5 tax2.h5 +CHECKFILE $TESTFILES/tall.h5 tax2.h5 +CLEANUP tax2.h5 +JAMTEST $TESTFILES/u511.txt $TESTFILES/twithub.h5 tax3.h5 +CHECKFILE $TESTFILES/tall.h5 tax3.h5 +CLEANUP tax3.h5 +JAMTEST $TESTFILES/u512.txt $TESTFILES/twithub.h5 tax4.h5 +CHECKFILE $TESTFILES/tall.h5 tax4.h5 +CLEANUP tax4.h5 +JAMTEST $TESTFILES/u513.txt $TESTFILES/twithub.h5 tax5.h5 +CHECKFILE $TESTFILES/tall.h5 tax5.h5 +CLEANUP tax5.h5 + +JAMTEST $TESTFILES/u10.txt $TESTFILES/twithub513.h5 tax6.h5 +CHECKFILE $TESTFILES/tall.h5 tax6.h5 +CLEANUP tax6.h5 +JAMTEST $TESTFILES/u511.txt $TESTFILES/twithub513.h5 tax7.h5 +CHECKFILE $TESTFILES/tall.h5 tax7.h5 +CLEANUP tax7.h5 +JAMTEST $TESTFILES/u512.txt $TESTFILES/twithub513.h5 tax8.h5 +CHECKFILE $TESTFILES/tall.h5 tax8.h5 +CLEANUP tax8.h5 +JAMTEST $TESTFILES/u513.txt $TESTFILES/twithub513.h5 tax9.h5 +CHECKFILE $TESTFILES/tall.h5 tax9.h5 +CLEANUP tax9.h5 + +JAMTEST $TESTFILES/u10.txt $TESTFILES/twithub.h5 --clobber taz2.h5 +CHECKFILE $TESTFILES/tall.h5 taz2.h5 +CLEANUP taz2.h5 +JAMTEST $TESTFILES/u511.txt $TESTFILES/twithub.h5 --clobber taz3.h5 +CHECKFILE $TESTFILES/tall.h5 taz3.h5 +CLEANUP taz3.h5 +JAMTEST $TESTFILES/u512.txt $TESTFILES/twithub.h5 --clobber taz4.h5 +CHECKFILE $TESTFILES/tall.h5 taz4.h5 +CLEANUP taz4.h5 +JAMTEST $TESTFILES/u513.txt $TESTFILES/twithub.h5 --clobber taz5.h5 +CHECKFILE $TESTFILES/tall.h5 taz5.h5 +CLEANUP taz5.h5 + +JAMTEST $TESTFILES/u10.txt $TESTFILES/twithub513.h5 --clobber taz6.h5 +CHECKFILE $TESTFILES/tall.h5 taz6.h5 +CLEANUP taz6.h5 +JAMTEST $TESTFILES/u511.txt $TESTFILES/twithub513.h5 --clobber taz7.h5 +CHECKFILE $TESTFILES/tall.h5 taz7.h5 +CLEANUP taz7.h5 +JAMTEST $TESTFILES/u512.txt $TESTFILES/twithub513.h5 --clobber taz8.h5 +CHECKFILE $TESTFILES/tall.h5 taz8.h5 +CLEANUP taz8.h5 +JAMTEST $TESTFILES/u513.txt $TESTFILES/twithub513.h5 --clobber taz9.h5 +CHECKFILE $TESTFILES/tall.h5 taz9.h5 +CLEANUP taz9.h5 + +SETUP $TESTFILES/twithub.h5 tay2.h5 +JAMTEST $TESTFILES/u10.txt tay2.h5 --clobber +CHECKFILE $TESTFILES/tall.h5 tay2.h5 +CLEANUP tay2.h5 +SETUP $TESTFILES/twithub.h5 tay3.h5 +JAMTEST $TESTFILES/u511.txt tay3.h5 --clobber +CHECKFILE $TESTFILES/tall.h5 tay3.h5 +CLEANUP tay3.h5 +SETUP $TESTFILES/twithub.h5 tay4.h5 +JAMTEST $TESTFILES/u512.txt tay4.h5 --clobber +CHECKFILE $TESTFILES/tall.h5 tay4.h5 +CLEANUP tay4.h5 +SETUP $TESTFILES/twithub.h5 tay5.h5 +JAMTEST $TESTFILES/u513.txt tay5.h5 --clobber +CHECKFILE $TESTFILES/tall.h5 tay5.h5 +CLEANUP tay5.h5 + +SETUP $TESTFILES/twithub513.h5 tay6.h5 +JAMTEST $TESTFILES/u10.txt tay6.h5 --clobber +CHECKFILE $TESTFILES/tall.h5 tay6.h5 +CLEANUP tay6.h5 +SETUP $TESTFILES/twithub513.h5 tay7.h5 +JAMTEST $TESTFILES/u511.txt tay7.h5 --clobber +CHECKFILE $TESTFILES/tall.h5 tay7.h5 +CLEANUP tay7.h5 +SETUP $TESTFILES/twithub513.h5 tay8.h5 +JAMTEST $TESTFILES/u512.txt tay8.h5 --clobber +CHECKFILE $TESTFILES/tall.h5 tay8.h5 +CLEANUP tay8.h5 +SETUP $TESTFILES/twithub513.h5 tay9.h5 +JAMTEST $TESTFILES/u513.txt tay9.h5 --clobber +CHECKFILE $TESTFILES/tall.h5 tay9.h5 +CLEANUP tay9.h5 + +SETUP $TESTFILES/twithub.h5 tai1.h5 +UNJAMTEST tai1.h5 o10.txt taa1.h5 +CHECKFILE $TESTFILES/tall.h5 taa1.h5 +CLEANUP taa1.h5 tai1.h5 o10.txt +SETUP $TESTFILES/twithub513.h5 tai2.h5 +UNJAMTEST tai2.h5 o512.txt taa2.h5 +CHECKFILE $TESTFILES/tall.h5 taa2.h5 +CLEANUP taa2.h5 tai2.h5 o512.txt + +SETUP $TESTFILES/twithub.h5 tai3.h5 +UNJAMTEST tai3.h5 - taa3.h5 +CHECKFILE $TESTFILES/tall.h5 taa3.h5 +CLEANUP taa3.h5 tai3.h5 +SETUP $TESTFILES/twithub513.h5 tai4.h5 +UNJAMTEST tai4.h5 - taa4.h5 +CHECKFILE $TESTFILES/tall.h5 taa4.h5 +CLEANUP taa4.h5 tai4.h5 + +SETUP $TESTFILES/twithub.h5 taj2.h5 +UNJAMTEST taj2.h5 --delete tac2.h5 +CHECKFILE $TESTFILES/tall.h5 tac2.h5 +CLEANUP tac2.h5 taj2.h5 +SETUP $TESTFILES/twithub513.h5 taj3.h5 +UNJAMTEST taj3.h5 --delete tac3.h5 +CHECKFILE $TESTFILES/tall.h5 tac3.h5 +CLEANUP tac3.h5 taj3.h5 + + + +if test $nerrors -eq 0 ; then + echo "All $JAM tests passed." +fi + +exit $nerrors diff --git a/tools/testfiles/twithub.h5 b/tools/testfiles/twithub.h5 new file mode 100644 index 0000000..105319f Binary files /dev/null and b/tools/testfiles/twithub.h5 differ diff --git a/tools/testfiles/twithub513.h5 b/tools/testfiles/twithub513.h5 new file mode 100644 index 0000000..8b6a2e2 Binary files /dev/null and b/tools/testfiles/twithub513.h5 differ diff --git a/tools/testfiles/u10.txt b/tools/testfiles/u10.txt new file mode 100755 index 0000000..c76a964 --- /dev/null +++ b/tools/testfiles/u10.txt @@ -0,0 +1 @@ +abcdefghij \ No newline at end of file diff --git a/tools/testfiles/u511.txt b/tools/testfiles/u511.txt new file mode 100755 index 0000000..bff1736 --- /dev/null +++ b/tools/testfiles/u511.txt @@ -0,0 +1 @@ +abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghija \ No newline at end of file diff --git a/tools/testfiles/u512.txt b/tools/testfiles/u512.txt new file mode 100755 index 0000000..33a36c9 --- /dev/null +++ b/tools/testfiles/u512.txt @@ -0,0 +1 @@ +abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijab \ No newline at end of file diff --git a/tools/testfiles/u513.txt b/tools/testfiles/u513.txt new file mode 100755 index 0000000..6b46ebf --- /dev/null +++ b/tools/testfiles/u513.txt @@ -0,0 +1 @@ +abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabc \ No newline at end of file -- cgit v0.12