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; }
static PyObject* pydat_get(HDSObject *self) { // Recover C-pointer passed via Python HDSLoc* loc = HDS_retrieve_locator(self); // guard against structures int state, status = SAI__OK; errBegin(&status); datStruc(loc, &state, &status); if (raiseHDSException(&status)) return NULL; if(state) { PyErr_SetString(PyExc_IOError, "dat_get error: cannot use on structures"); return NULL; } // get type char typ_str[DAT__SZTYP+1]; datType(loc, typ_str, &status); // get shape const int NDIMX=7; int ndim; hdsdim tdim[NDIMX]; datShape(loc, NDIMX, tdim, &ndim, &status); if (raiseHDSException(&status)) return NULL; PyArrayObject* arr = NULL; // Either return values as a single scalar or a numpy array // Reverse order of dimensions npy_intp rdim[NDIMX]; int i; for(i=0; i<ndim; i++) rdim[i] = tdim[ndim-i-1]; if(strcmp(typ_str, "_INTEGER") == 0 || strcmp(typ_str, "_LOGICAL") == 0) { arr = (PyArrayObject*) PyArray_SimpleNew(ndim, rdim, NPY_INT); } else if(strcmp(typ_str, "_REAL") == 0) { arr = (PyArrayObject*) PyArray_SimpleNew(ndim, rdim, NPY_FLOAT); } else if(strcmp(typ_str, "_DOUBLE") == 0) { arr = (PyArrayObject*) PyArray_SimpleNew(ndim, rdim, NPY_DOUBLE); } else if(strncmp(typ_str, "_CHAR", 5) == 0) { // work out the number of bytes size_t nbytes; datLen(loc, &nbytes, &status); if (status != SAI__OK) goto fail; int ncdim = 1+ndim; int cdim[ncdim]; cdim[0] = nbytes+1; for(i=0; i<ndim; i++) cdim[i+1] = rdim[i]; PyArray_Descr *descr = PyArray_DescrNewFromType(NPY_STRING); descr->elsize = nbytes; arr = (PyArrayObject*) PyArray_NewFromDescr(&PyArray_Type, descr, ndim, rdim, NULL, NULL, 0, NULL); } else if(strcmp(typ_str, "_WORD") == 0) { arr = (PyArrayObject*) PyArray_SimpleNew(ndim, rdim, NPY_SHORT); } else if(strcmp(typ_str, "_UWORD") == 0) { arr = (PyArrayObject*) PyArray_SimpleNew(ndim, rdim, NPY_USHORT); } else if(strcmp(typ_str, "_BYTE") == 0) { arr = (PyArrayObject*) PyArray_SimpleNew(ndim, rdim, NPY_BYTE); } else if(strcmp(typ_str, "_UBYTE") == 0) { arr = (PyArrayObject*) PyArray_SimpleNew(ndim, rdim, NPY_UBYTE); } else { PyErr_SetString(PyExc_IOError, "dat_get: encountered an unimplemented type"); return NULL; } if(arr == NULL) goto fail; datGet(loc, typ_str, ndim, tdim, arr->data, &status); if(status != SAI__OK) goto fail; return PyArray_Return(arr); fail: raiseHDSException(&status); Py_XDECREF(arr); return NULL; };