/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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: H5Ocache.c * Sep 28 2005 * Quincey Koziol * * Purpose: Object header metadata cache virtual functions. * *------------------------------------------------------------------------- */ #define H5O_PACKAGE /*suppress error about including H5Opkg */ #include "H5private.h" /* Generic Functions */ #include "H5Eprivate.h" /* Error handling */ #include "H5FLprivate.h" /* Free lists */ #include "H5Opkg.h" /* Object headers */ /* Private typedefs */ /* PRIVATE PROTOTYPES */ /* Metadata cache callbacks */ static H5O_t *H5O_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, const void *_udata1, void *_udata2); static herr_t H5O_flush(H5F_t *f, hid_t dxpl_id, hbool_t destroy, haddr_t addr, H5O_t *oh); static herr_t H5O_clear(H5F_t *f, H5O_t *oh, hbool_t destroy); static herr_t H5O_compute_size(const H5F_t *f, const H5O_t *oh, size_t *size_ptr); /* H5O inherits cache-like properties from H5AC */ const H5AC_class_t H5AC_OHDR[1] = {{ H5AC_OHDR_ID, (H5AC_load_func_t)H5O_load, (H5AC_flush_func_t)H5O_flush, (H5AC_dest_func_t)H5O_dest, (H5AC_clear_func_t)H5O_clear, (H5AC_size_func_t)H5O_compute_size, }}; /*------------------------------------------------------------------------- * Function: H5O_flush_msgs * * Purpose: Flushes messages for object header. * * Return: Non-negative on success/Negative on failure * * Programmer: Quincey Koziol * koziol@ncsa.uiuc.edu * Nov 21 2005 * *------------------------------------------------------------------------- */ herr_t H5O_flush_msgs(H5F_t *f, H5O_t *oh) { uint8_t *p; /* Temporary pointer to encode with */ int id; /* ID of message to encode */ H5O_mesg_t *curr_msg; /* Pointer to current message being operated on */ herr_t (*encode)(H5F_t*, uint8_t*, const void*) = NULL; unsigned u; /* Local index variable */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(H5O_flush_msgs, FAIL) /* check args */ HDassert(f); HDassert(oh); /* Encode any dirty messages */ for(u = 0, curr_msg = &oh->mesg[0]; u < oh->nmesgs; u++, curr_msg++) { if(curr_msg->dirty) { p = curr_msg->raw - H5O_SIZEOF_MSGHDR(f); id = curr_msg->type->id; UINT16ENCODE(p, id); HDassert(curr_msg->raw_size < H5O_MAX_SIZE); UINT16ENCODE(p, curr_msg->raw_size); *p++ = curr_msg->flags; *p++ = 0; /*reserved*/ *p++ = 0; /*reserved*/ *p++ = 0; /*reserved*/ if(curr_msg->native) { HDassert(curr_msg->type->encode); /* allocate file space for chunks that have none yet */ if(H5O_CONT_ID == id && !H5F_addr_defined(((H5O_cont_t *)(curr_msg->native))->addr)) /* We now allocate disk space on insertion, instead * of on flush from the cache, so this case is now an * error. -- JRM */ HGOTO_ERROR(H5E_OHDR, H5E_SYSTEM, FAIL, "File space for message not allocated!?!") /* * Encode the message. If the message is shared then we * encode a Shared Object message instead of the object * which is being shared. */ HDassert(curr_msg->raw >= oh->chunk[curr_msg->chunkno].image); HDassert(curr_msg->raw_size == H5O_ALIGN (curr_msg->raw_size)); HDassert(curr_msg->raw + curr_msg->raw_size <= oh->chunk[curr_msg->chunkno].image + oh->chunk[curr_msg->chunkno].size); if(curr_msg->flags & H5O_FLAG_SHARED) encode = H5O_MSG_SHARED->encode; else encode = curr_msg->type->encode; if((encode)(f, curr_msg->raw, curr_msg->native) < 0) HGOTO_ERROR(H5E_OHDR, H5E_CANTENCODE, FAIL, "unable to encode object header message") } /* end if */ curr_msg->dirty = FALSE; oh->chunk[curr_msg->chunkno].dirty = TRUE; } /* end if */ } /* end for */ /* Sanity check for the correct # of messages in object header */ if(oh->nmesgs != u) HGOTO_ERROR(H5E_OHDR, H5E_CANTFLUSH, FAIL, "corrupt object header - too few messages") done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5O_flush_msgs() */ /*------------------------------------------------------------------------- * Function: H5O_load * * Purpose: Loads an object header from disk. * * Return: Success: Pointer to the new object header. * * Failure: NULL * * Programmer: Robb Matzke * matzke@llnl.gov * Aug 5 1997 * *------------------------------------------------------------------------- */ static H5O_t * H5O_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, const void UNUSED * _udata1, void UNUSED * _udata2) { H5O_t *oh = NULL; H5O_t *ret_value; uint8_t buf[16], *p; size_t mesg_size; size_t hdr_size; unsigned id; int mesgno; unsigned curmesg = 0, nmesgs; unsigned chunkno; unsigned skipped_msgs = 0; /* Number of unknown messages skipped */ unsigned merged_null_msgs = 0; /* Number of null messages merged together */ haddr_t chunk_addr; size_t chunk_size; uint8_t flags; FUNC_ENTER_NOAPI(H5O_load, NULL) /* check args */ HDassert(f); HDassert(H5F_addr_defined(addr)); HDassert(!_udata1); HDassert(!_udata2); /* allocate ohdr and init chunk list */ if (NULL==(oh = H5FL_CALLOC(H5O_t))) HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed"); /* read fixed-lenth part of object header */ hdr_size = H5O_SIZEOF_HDR(f); assert(hdr_size<=sizeof(buf)); if (H5F_block_read(f, H5FD_MEM_OHDR, addr, hdr_size, dxpl_id, buf) < 0) HGOTO_ERROR(H5E_OHDR, H5E_READERROR, NULL, "unable to read object header"); p = buf; /* decode version */ oh->version = *p++; if (H5O_VERSION != oh->version) HGOTO_ERROR(H5E_OHDR, H5E_VERSION, NULL, "bad object header version number"); /* reserved */ p++; /* decode number of messages */ UINT16DECODE(p, nmesgs); /* decode link count */ UINT32DECODE(p, oh->nlink); /* decode first chunk info */ chunk_addr = addr + (hsize_t)hdr_size; UINT32DECODE(p, chunk_size); /* build the message array */ oh->alloc_nmesgs = MAX(H5O_NMESGS, nmesgs); if (NULL==(oh->mesg=H5FL_SEQ_CALLOC(H5O_mesg_t,(size_t)oh->alloc_nmesgs))) HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed"); /* read each chunk from disk */ while(H5F_addr_defined(chunk_addr)) { /* increase chunk array size */ if(oh->nchunks >= oh->alloc_nchunks) { unsigned na = oh->alloc_nchunks + H5O_NCHUNKS; H5O_chunk_t *x = H5FL_SEQ_REALLOC (H5O_chunk_t, oh->chunk, (size_t)na); if(!x) HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed"); oh->alloc_nchunks = na; oh->chunk = x; } /* end if */ /* read the chunk raw data */ chunkno = oh->nchunks++; oh->chunk[chunkno].dirty = FALSE; oh->chunk[chunkno].addr = chunk_addr; oh->chunk[chunkno].size = chunk_size; if(NULL==(oh->chunk[chunkno].image = H5FL_BLK_MALLOC(chunk_image, chunk_size))) HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed"); if(H5F_block_read(f, H5FD_MEM_OHDR, chunk_addr, chunk_size, dxpl_id, oh->chunk[chunkno].image) < 0) HGOTO_ERROR(H5E_OHDR, H5E_READERROR, NULL, "unable to read object header data"); /* load messages from this chunk */ for(p = oh->chunk[chunkno].image; p < oh->chunk[chunkno].image + chunk_size; p += mesg_size) { UINT16DECODE(p, id); UINT16DECODE(p, mesg_size); HDassert(mesg_size==H5O_ALIGN (mesg_size)); flags = *p++; p += 3; /*reserved*/ /* Try to detect invalidly formatted object header messages */ if(p + mesg_size > oh->chunk[chunkno].image + chunk_size) HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, NULL, "corrupt object header") /* Skip header messages we don't know about */ /* (Usually from future versions of the library */ if(id >= NELMTS(H5O_msg_class_g) || NULL == H5O_msg_class_g[id]) { skipped_msgs++; continue; } /* end if */ if((H5F_get_intent(f) & H5F_ACC_RDWR) && H5O_NULL_ID == id && oh->nmesgs > 0 && H5O_NULL_ID == oh->mesg[oh->nmesgs - 1].type->id && oh->mesg[oh->nmesgs - 1].chunkno == chunkno) { /* combine adjacent null messages */ mesgno = oh->nmesgs - 1; oh->mesg[mesgno].raw_size += H5O_SIZEOF_MSGHDR(f) + mesg_size; oh->mesg[mesgno].dirty = TRUE; merged_null_msgs++; } else { /* new message */ if (oh->nmesgs >= nmesgs) HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, "corrupt object header - too many messages"); mesgno = oh->nmesgs++; oh->mesg[mesgno].type = H5O_msg_class_g[id]; oh->mesg[mesgno].dirty = FALSE; oh->mesg[mesgno].flags = flags; oh->mesg[mesgno].native = NULL; oh->mesg[mesgno].raw = p; oh->mesg[mesgno].raw_size = mesg_size; oh->mesg[mesgno].chunkno = chunkno; } /* end else */ } /* end for */ HDassert(p == oh->chunk[chunkno].image + chunk_size); /* decode next object header continuation message */ for(chunk_addr = HADDR_UNDEF; !H5F_addr_defined(chunk_addr) && curmesg < oh->nmesgs; ++curmesg) { if(H5O_CONT_ID == oh->mesg[curmesg].type->id) { H5O_cont_t *cont; cont = (H5O_MSG_CONT->decode) (f, dxpl_id, oh->mesg[curmesg].raw); oh->mesg[curmesg].native = cont; chunk_addr = cont->addr; chunk_size = cont->size; cont->chunkno = oh->nchunks; /*the next chunk to allocate */ } /* end if */ } /* end for */ } /* end while */ /* Mark the object header dirty if we've merged a message */ if(merged_null_msgs) oh->cache_info.is_dirty = TRUE; /* Sanity check for the correct # of messages in object header */ if((oh->nmesgs + skipped_msgs + merged_null_msgs) != nmesgs) HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, "corrupt object header - too few messages") /* Set return value */ ret_value = oh; done: if(!ret_value && oh) { if(H5O_dest(f,oh) < 0) HDONE_ERROR(H5E_OHDR, H5E_CANTFREE, NULL, "unable to destroy object header data") } /* end if */ FUNC_LEAVE_NOAPI(ret_value) } /* end H5O_load() */ /*------------------------------------------------------------------------- * Function: H5O_flush * * Purpose: Flushes (and destroys) an object header. * * Return: Non-negative on success/Negative on failure * * Programmer: Robb Matzke * matzke@llnl.gov * Aug 5 1997 * *------------------------------------------------------------------------- */ static herr_t H5O_flush(H5F_t *f, hid_t dxpl_id, hbool_t destroy, haddr_t addr, H5O_t *oh) { uint8_t buf[16], *p; hbool_t combine = FALSE; /* Whether to combine the object header prefix & the first chunk */ unsigned u; /* Local index variable */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(H5O_flush, FAIL) /* check args */ HDassert(f); HDassert(H5F_addr_defined(addr)); HDassert(oh); /* flush */ if(oh->cache_info.is_dirty) { /* Encode any dirty messages */ if(H5O_flush_msgs(f, oh) < 0) HGOTO_ERROR(H5E_OHDR, H5E_CANTFLUSH, FAIL, "unable to flush object header messages") /* Encode header prefix */ p = buf; /* encode version */ *p++ = oh->version; /* reserved */ *p++ = 0; /* encode number of messages */ UINT16ENCODE(p, oh->nmesgs); /* encode link count */ UINT32ENCODE(p, oh->nlink); /* encode body size */ UINT32ENCODE(p, oh->chunk[0].size); /* zero to alignment */ HDmemset (p, 0, (size_t)(H5O_SIZEOF_HDR(f)-12)); /* write the object header prefix */ /* Check if we can combine the object header prefix & the first chunk into one I/O operation */ if(oh->chunk[0].dirty && (addr + H5O_SIZEOF_HDR(f)) == oh->chunk[0].addr) { combine = TRUE; } /* end if */ else { if(H5F_block_write(f, H5FD_MEM_OHDR, addr, (size_t)H5O_SIZEOF_HDR(f), dxpl_id, buf) < 0) HGOTO_ERROR(H5E_OHDR, H5E_WRITEERROR, FAIL, "unable to write object header hdr to disk") } /* end else */ /* write each chunk to disk */ for(u = 0; u < oh->nchunks; u++) { if(oh->chunk[u].dirty) { HDassert(H5F_addr_defined(oh->chunk[u].addr)); if(u == 0 && combine) { /* Allocate space for the combined prefix and first chunk */ if((p = H5FL_BLK_MALLOC(chunk_image,(H5O_SIZEOF_HDR(f)+oh->chunk[u].size))) == NULL) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed") /* Copy in the prefix */ HDmemcpy(p, buf, (size_t)H5O_SIZEOF_HDR(f)); /* Copy in the first chunk */ HDmemcpy(p + H5O_SIZEOF_HDR(f), oh->chunk[u].image, oh->chunk[u].size); /* Write the combined prefix/chunk out */ if(H5F_block_write(f, H5FD_MEM_OHDR, addr, (H5O_SIZEOF_HDR(f) + oh->chunk[u].size), dxpl_id, p) < 0) HGOTO_ERROR(H5E_OHDR, H5E_WRITEERROR, FAIL, "unable to write object header data to disk") /* Release the memory for the combined prefix/chunk */ p = H5FL_BLK_FREE(chunk_image,p); } /* end if */ else { if(H5F_block_write(f, H5FD_MEM_OHDR, oh->chunk[u].addr, (oh->chunk[u].size), dxpl_id, oh->chunk[u].image) < 0) HGOTO_ERROR(H5E_OHDR, H5E_WRITEERROR, FAIL, "unable to write object header data to disk") } /* end else */ oh->chunk[u].dirty = FALSE; } /* end if */ } /* end for */ oh->cache_info.is_dirty = FALSE; } /* end if */ if (destroy) { if(H5O_dest(f,oh) < 0) HGOTO_ERROR(H5E_OHDR, H5E_CANTFREE, FAIL, "unable to destroy object header data") } /* end if */ done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5O_flush() */ /*------------------------------------------------------------------------- * Function: H5O_dest * * Purpose: Destroys an object header. * * Return: Non-negative on success/Negative on failure * * Programmer: Quincey Koziol * koziol@ncsa.uiuc.edu * Jan 15 2003 * *------------------------------------------------------------------------- */ herr_t H5O_dest(H5F_t UNUSED *f, H5O_t *oh) { unsigned i; FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5O_dest); /* check args */ assert(oh); /* Verify that node is clean */ assert (oh->cache_info.is_dirty==FALSE); /* destroy chunks */ for (i = 0; i < oh->nchunks; i++) { /* Verify that chunk is clean */ assert (oh->chunk[i].dirty==0); oh->chunk[i].image = H5FL_BLK_FREE(chunk_image,oh->chunk[i].image); } if(oh->chunk) oh->chunk = H5FL_SEQ_FREE(H5O_chunk_t,oh->chunk); /* destroy messages */ for (i = 0; i < oh->nmesgs; i++) { /* Verify that message is clean */ assert (oh->mesg[i].dirty==0); H5O_free_mesg(&oh->mesg[i]); } if(oh->mesg) oh->mesg = H5FL_SEQ_FREE(H5O_mesg_t,oh->mesg); /* destroy object header */ H5FL_FREE(H5O_t,oh); FUNC_LEAVE_NOAPI(SUCCEED); } /* end H5O_dest() */ /*------------------------------------------------------------------------- * Function: H5O_clear * * Purpose: Mark a object header in memory as non-dirty. * * Return: Non-negative on success/Negative on failure * * Programmer: Quincey Koziol * koziol@ncsa.uiuc.edu * Mar 20 2003 * *------------------------------------------------------------------------- */ static herr_t H5O_clear(H5F_t *f, H5O_t *oh, hbool_t destroy) { unsigned u; /* Local index variable */ herr_t ret_value = SUCCEED; FUNC_ENTER_NOAPI_NOINIT(H5O_clear); /* check args */ assert(oh); /* Mark chunks as clean */ for (u = 0; u < oh->nchunks; u++) oh->chunk[u].dirty=FALSE; /* Mark messages as clean */ for (u = 0; u < oh->nmesgs; u++) oh->mesg[u].dirty=FALSE; /* Mark whole header as clean */ oh->cache_info.is_dirty=FALSE; if (destroy) if (H5O_dest(f, oh) < 0) HGOTO_ERROR(H5E_OHDR, H5E_CANTFREE, FAIL, "unable to destroy object header data"); done: FUNC_LEAVE_NOAPI(ret_value); } /* end H5O_clear() */ /*------------------------------------------------------------------------- * Function: H5O_compute_size * * Purpose: Compute the size in bytes of the specified instance of * H5O_t on disk, and return it in *len_ptr. On failure, * the value of *len_ptr is undefined. * * The value returned will probably be low unless the object * has just been flushed, as we simply total up the size of * the header with the sizes of the chunks. Thus any message * that has been added since the last flush will not be * reflected in the total. * * Return: Non-negative on success/Negative on failure * * Programmer: John Mainzer * 5/13/04 * *------------------------------------------------------------------------- */ static herr_t H5O_compute_size(const H5F_t *f, const H5O_t *oh, size_t *size_ptr) { unsigned u; size_t size; FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5O_compute_size); /* check args */ HDassert(f); HDassert(oh); HDassert(size_ptr); size = H5O_SIZEOF_HDR(f); for (u = 0; u < oh->nchunks; u++) size += oh->chunk[u].size; HDassert(size >= H5O_SIZEOF_HDR(f)); *size_ptr = size; FUNC_LEAVE_NOAPI(SUCCEED); } /* H5O_compute_size() */