int datMap(HDSLoc *locator, const char *type_str, const char *mode_str, int ndim, const hdsdim dims[], void **pntr, int *status) { int isprim = 0; char normtypestr[DAT__SZTYP+1]; size_t nbytes = 0; hid_t h5type = 0; int isreg = 0; void *regpntr = NULL; void *mapped = NULL; hdsmode_t accmode = HDSMODE_UNKNOWN; haddr_t offset; hdsbool_t try_mmap = HDS_FALSE; unsigned intent = 0; size_t actbytes = 0; if (*status != SAI__OK) return *status; /* First have to validate the access mode */ switch (mode_str[0]) { case 'R': case 'r': accmode = HDSMODE_READ; break; case 'U': case 'u': accmode = HDSMODE_UPDATE; break; case 'W': case 'w': accmode = HDSMODE_WRITE; break; default: *status = DAT__MODIN; emsRepf("datMap_6", "Unrecognized mode string '%s' for datMap", status, mode_str); goto CLEANUP; } /* Validate input locator. */ dat1ValidateLocator( "datMap", 1, locator, (accmode & HDSMODE_READ), status ); /* Get the HDF5 type code and confirm this is a primitive type */ isprim = dau1CheckType( 1, type_str, &h5type, normtypestr, sizeof(normtypestr), status ); if (!isprim) { if (*status == SAI__OK) { *status = DAT__TYPIN; emsRepf("datMap_1", "datGet: Data type must be a primitive type and not '%s'", status, normtypestr); } goto CLEANUP; } /* Not allowed to map undefined data in READ or UPDATE mode */ if (accmode == HDSMODE_UPDATE || accmode == HDSMODE_READ) { hdsbool_t defined; if (*status == SAI__OK) { datState( locator, &defined, status ); if (!defined) { *status = DAT__UNSET; emsRepf("datMap_6bb", "Can not map an undefined primitive in mode '%s'", status, mode_str); goto CLEANUP; } } } /* How did we open this file? */ CALLHDFQ( H5Fget_intent( locator->file_id, &intent )); if (accmode == HDSMODE_UPDATE || accmode == HDSMODE_WRITE) { /* Must check whether the file was opened for write */ if ( intent == H5F_ACC_RDONLY ) { *status = DAT__ACCON; emsRepf("datMap_6b", "datMap: Can not map readonly locator in mode '%s'", status, mode_str); goto CLEANUP; } } /* Verify that the specified dimensions match the locator dimensions */ if (*status == SAI__OK) { hdsdim locdims[DAT__MXDIM]; int locndims; int i; datShape(locator, DAT__MXDIM, locdims, &locndims, status ); /* Note that if we are mapping as a scalar the locator should refer to a single element */ if (ndim == 0) { size_t nelem = 1; for (i=0; i<locndims; i++) { nelem *= locdims[i]; } if (nelem != 1) { *status = DAT__DIMIN; emsRepf("datMap_6e", "datMap: Attempt to map as a scalar but locator" " refers to a primitive with %zu elements", status, nelem); goto CLEANUP; } } else { if (ndim != locndims) { *status = DAT__DIMIN; emsRepf("datMap_6c", "datMap: Dimensionality mismatch --" " requested number: %d locator number: %d", status, ndim, locndims ); goto CLEANUP; } for (i=0; i<ndim; i++) { if ( locdims[i] != dims[i] ) { *status = DAT__DIMIN; emsRepf("datMap_6d", "datMap: Dimension %d has size %zu but requested size %zu", status, i, (size_t)locdims[i], (size_t)dims[i]); goto CLEANUP; } } } } /* There is a super-special case for datMap when called with a map type of "_CHAR". In that case we need to work out the size ourselves and adjust the type size */ if (strcmp( "_CHAR", normtypestr ) == 0 ) { size_t clen = 0; char tmpbuff[DAT__SZTYP+1]; datClen( locator, &clen, status ); CALLHDFQ( H5Tset_size( h5type, clen ) ); one_snprintf( tmpbuff, sizeof(tmpbuff), "*%zu", status, clen ); one_strlcat( normtypestr, tmpbuff, DAT__SZTYP+1, status ); } /* Now we want the HDSTYPE of the requested type so that we can work out how much memory we will need to allocate. */ CALLHDFE( size_t, nbytes, H5Tget_size( h5type ), DAT__HDF5E, emsRep("datLen_size", "datMap: Error obtaining size of requested data type", status) ); { int i; if (ndim > 0) { for (i = 0; i < ndim; i++) { nbytes *= dims[i]; } } } /* Work out whether memory mapping is possible -- at the moment I'm pretty sure the only safe use of mmap is when we are reading the data and the file itself was opened readonly. I'm not sure what happens if other components are removed or added -- will the offset change? Maybe we just try */ offset = H5Dget_offset( locator->dataset_id ); if (offset != HADDR_UNDEF) { hid_t dataset_h5type = 0; /* In theory we can do a memory map so now compare the data types of the request and the low-level dataset. */ CALLHDFE( hid_t, dataset_h5type, H5Dget_type( locator->dataset_id ), DAT__HDF5E, emsRep("datMap_type", "datType: Error obtaining data type of dataset", status) ); if (H5Tequal( dataset_h5type, h5type )) { try_mmap = HDS_TRUE; } H5Tclose(dataset_h5type); } /* If this is a locator to a slice then for now we can't memory map. In theory if we knew the slice was contiguous (e.g a vectorized slice, or a single plane of a cube then we could mmap it anyhow. We do not want to have to emulate HDF5 dataspaces here */ if (locator->isslice) try_mmap = 0; /* There seem to be issues doing this on files opened for update/write. For now only allow mmap for files opened read only */ if (intent != H5F_ACC_RDONLY) try_mmap = 0; /* If mmap has been disabled by tuning the environment we just force it off here. */ if (!hds1GetUseMmap()) try_mmap = 0; #if DEBUG_HDS { char *name_str; char * file_str; const char * reason; name_str = dat1GetFullName( locator->dataset_id, 0, NULL, status ); file_str = dat1GetFullName( locator->dataset_id, 1, NULL, status ); if (offset != HADDR_UNDEF) { reason = "[HAD offset]"; } else { reason = "[no offset]"; } if (!try_mmap) { printf("Will NOT attempt %s to mmap %s:%s\n",reason,file_str,name_str); } else { printf("WILL TRY %s to mmap OFFSET=%zu %s:%s\n", reason, (size_t)offset, file_str, name_str); } MEM_FREE(name_str); } #endif if (try_mmap) { int fd = 0; int flags = 0; int prot = 0; hdsbool_t opened_fd = 0; if ( intent == H5F_ACC_RDONLY || accmode == HDSMODE_READ ) { flags |= O_RDONLY; prot = PROT_READ; } else { flags |= O_RDWR; prot = PROT_READ | PROT_WRITE; } if (*status == SAI__OK) { /* see what file driver we have */ hid_t fapl_id = -1; hid_t fdriv_id = -1; void * file_handle = NULL; herr_t herr = -1; fapl_id = H5Fget_access_plist(locator->file_id); fdriv_id = H5Pget_driver(fapl_id); if (fdriv_id == H5FD_SEC2 || fdriv_id == H5FD_STDIO) { /* If this is a POSIX or STDIO driver we get the handle */ herr = H5Fget_vfd_handle( locator->file_id, fapl_id, (void**)&file_handle); if (herr >= 0) { if (fdriv_id == H5FD_SEC2) { fd = *((int *)file_handle); } else if (fdriv_id == H5FD_STDIO) { FILE * fh = (FILE *)file_handle; fd = fileno(fh); } } } if (fapl_id > 0) H5Pclose( fapl_id ); if (fd == 0) { /* We have to open the file ourselves! */ char * fname = NULL; fname = dat1GetFullName( locator->dataset_id, 1, NULL, status ); fd = open(fname, flags); opened_fd = 1; if (fname) MEM_FREE(fname); } if (fd > 0) { /* Set up for memory mapping */ int mflags = 0; mflags = MAP_SHARED | MAP_FILE; if (*status == SAI__OK) { mapped = dat1Mmap( nbytes, prot, mflags, fd, offset, &isreg, ®pntr, &actbytes, status ); if (*status == SAI__OK) { /* Store the file descriptor in the locator to allow us to close */ if (mapped) { if (opened_fd) locator->fdmap = fd; locator->uses_true_mmap = 1; } } else { /* Not currently fatal -- we can try without the file */ if (opened_fd) close(fd); emsAnnul(status); } } } } } /* If we have not been able to map anything yet, just get some memory. It is zeroed (for WRITE) to match mmap behavior. We rely on the OS to decide when it is reasonable to do an anonymous mmap. */ if (!regpntr) { hdsbool_t mustget; mustget = (accmode == HDSMODE_READ || accmode == HDSMODE_UPDATE); if (mustget) { regpntr = cnfMalloc( nbytes ); } else { regpntr = cnfCalloc( 1, nbytes ); } if (!regpntr) { *status = DAT__NOMEM; emsRepf("datMap_cnf","datMap: Unable to allocate %zu bytes of memory", status, nbytes); goto CLEANUP; } /* Populate the memory - check with datState occurred earlier */ if (mustget) { datGet( locator, normtypestr, ndim, dims, regpntr, status ); } } CLEANUP: /* Cleanups that must happen always */ if (h5type) H5Tclose(h5type); /* cleanups that only happen if status is bad */ if (*status != SAI__OK) { if (mapped) { if (isreg == 1) cnfUregp( regpntr ); if ( munmap( mapped, actbytes ) != 0 ) { emsSyser( "MESSAGE", errno ); emsRep("datMap_4", "Error unmapping mapped memory: ^MESSAGE", status); } mapped = NULL; } else if (regpntr) { cnfFree( regpntr ); } regpntr = NULL; } /* Update the locator to reflect the mapped status */ if (*status == SAI__OK) { int i; locator->pntr = mapped; locator->regpntr = regpntr; locator->bytesmapped = actbytes; locator->accmode = accmode; /* In order to copy the data back into the underlying HDF5 dataset we need to store additional information about how this was mapped to allow us to either call datPut later on or at least a new dataspace. For now store the arguments so we can pass them straight to datPut */ locator->ndims = ndim; for (i=0; i<ndim; i++) { (locator->mapdims)[i] = dims[i]; } star_strlcpy( locator->maptype, normtypestr, sizeof(locator->maptype) ); } /* Note that the returned pointer is not necessarily the same as the mapped pointer because of pagesize corrections */ *pntr = regpntr; return *status; }
void *cnfRealloc( void * pntr, size_t size ) { /* *+ * Name: * cnfMalloc * Purpose: * Re-Allocate space that may be accessed from C and Fortran. * Invocation: * cpointer = cnfMalloc( size ); * Description: * This function allocates space in the same way as the standard C * remalloc() function, except that the pointer to the space * reallocated is automatically registered (using cnfRegp) for use * from both C and Fortran. This means that the returned pointer * may subsequently be converted into a Fortran pointer of type * F77_POINTER_TYPE (using cnfFptr), and back into a C pointer * (using cnfCptr). The contents of the space may therefore be * accessed from both languages. * Arguments: * void * pntr (Given) * Pointer to be re-allocated. Must have been malloced by cnfMalloc. * If new memory is allocated by this routine, this pointer will no * longer be valid. If the resize fails this pointer will still be valid. * This conforms to the standard ANSI C behaviour of realloc. * size_t size (Given) * The size of the required space. * Returned Value: * void *cnfRealloc * A registered pointer to the re-allocated space, or NULL if the * space could not be allocated. If NULL, "pntr" is not changed or freed. * Notes: * - The re-allocated space should be freed using cnfFree when no * longer required. *- */ int reg; /* Error status from pointer registration */ void * p; /* Temp pointer */ void * temp; /* Local copy of pointer from realloc */ /* Try to resize */ temp = starRealloc( pntr, size ); /* If a pointer to new memory was returned, then un-register the old */ /* pointer (if not NULL). */ if ( ( temp != pntr ) && ( pntr != NULL ) ) cnfUregp( pntr ); /* If a pointer to new memory was returned, attempt to register the new */ /* pointer (if not NULL). */ if ( ( temp != pntr ) && ( temp != NULL ) ) { reg = cnfRegp( temp ); /* If it could not be registered, then attempt to allocate some new memory */ /* with a registered pointer associated with it. */ if ( !reg ) { p = cnfMalloc( size ); /* If successful, transfer the data to the new (registered) memory and free */ /* the memory which could not be registered. */ if ( p ) { memcpy( p, temp, size ); starFree( temp ); temp = p; } else /* If no registered memory was available, free the unregistered memory and */ /* set the returned pointer to NULL. */ { starFree( temp ); temp = NULL; } } /* If an error occurred during pointer registration, free the unregistered */ /* memory and set the returned pointer to NULL. */ else if ( reg < 0 ) { starFree( temp ); temp = NULL; } } return temp; }