{-# LANGUAGE CPP #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} module Bindings.HDF5.Dataset ( Dataset , createDataset , createAnonymousDataset , openDataset , closeDataset , getDatasetSpace , SpaceStatus(..) , getDatasetSpaceStatus , getDatasetType , getDatasetCreatePList , getDatasetAccessPList , getDatasetStorageSize , getDatasetOffset , readDataset , readDatasetInto , writeDataset , setDatasetExtent , fillSelection ) where #if __GLASGOW_HASKELL__ < 710 import Control.Applicative ((<$>)) #endif import Bindings.HDF5.Core import Bindings.HDF5.Dataspace import Bindings.HDF5.Datatype.Internal import Bindings.HDF5.Error import Bindings.HDF5.Object import Bindings.HDF5.PropertyList.DAPL import Bindings.HDF5.PropertyList.DCPL import Bindings.HDF5.PropertyList.DXPL import Bindings.HDF5.PropertyList.LCPL import Bindings.HDF5.Raw.H5D import Bindings.HDF5.Raw.H5I import Bindings.HDF5.Raw.H5P import Bindings.HDF5.Raw.H5S import Control.Monad.ST (RealWorld) import qualified Data.ByteString as BS import qualified Data.Vector.Storable as SV import qualified Data.Vector.Storable.Mutable as SV.M import Foreign.Ptr.Conventions newtype Dataset = Dataset HId_t deriving (Eq, HId, FromHId, HDFResultType) instance Object Dataset where staticObjectType = Tagged (Just DatasetObj) createDataset :: Location loc => loc -> BS.ByteString -> Datatype -> Dataspace -> Maybe LCPL -> Maybe DCPL -> Maybe DAPL -> IO Dataset createDataset loc_id name type_id space_id lcpl_id dcpl_id dapl_id = fmap Dataset $ withErrorCheck $ BS.useAsCString name $ \cname -> h5d_create2 (hid loc_id) cname (hid type_id) (hid space_id) (maybe h5p_DEFAULT hid lcpl_id) (maybe h5p_DEFAULT hid dcpl_id) (maybe h5p_DEFAULT hid dapl_id) createAnonymousDataset :: Location loc => loc -> Datatype -> Dataspace -> Maybe DCPL -> Maybe DAPL -> IO Dataset createAnonymousDataset loc_id type_id space_id dcpl_id dapl_id = fmap Dataset $ withErrorCheck $ h5d_create_anon (hid loc_id) (hid type_id) (hid space_id) (maybe h5p_DEFAULT hid dcpl_id) (maybe h5p_DEFAULT hid dapl_id) openDataset :: Location loc => loc -> BS.ByteString -> Maybe DAPL -> IO Dataset openDataset loc_id name dapl_id = fmap Dataset $ withErrorCheck $ BS.useAsCString name $ \cname -> h5d_open2 (hid loc_id) cname (maybe h5p_DEFAULT hid dapl_id) closeDataset :: Dataset -> IO () closeDataset (Dataset dset_id) = withErrorCheck_ (h5d_close dset_id) getDatasetSpace :: Dataset -> IO Dataspace getDatasetSpace (Dataset dset_id) = uncheckedFromHId <$> withErrorCheck (h5d_get_space dset_id) data SpaceStatus = NotAllocated | Allocated | PartAllocated deriving (Eq, Ord, Read, Show, Enum, Bounded) spaceStatusFromCode :: H5D_space_status_t -> SpaceStatus spaceStatusFromCode c | c == h5d_SPACE_STATUS_NOT_ALLOCATED = NotAllocated | c == h5d_SPACE_STATUS_ALLOCATED = Allocated | c == h5d_SPACE_STATUS_PART_ALLOCATED = PartAllocated | otherwise = error ("Unknown H5D_space_status_t " ++ show c) getDatasetSpaceStatus :: Dataset -> IO SpaceStatus getDatasetSpaceStatus (Dataset dset_id) = fmap spaceStatusFromCode $ withOut_ $ \status -> withErrorCheck (h5d_get_space_status dset_id status) getDatasetType :: Dataset -> IO Datatype getDatasetType (Dataset dset_id) = fmap Datatype $ withErrorCheck $ h5d_get_type dset_id getDatasetCreatePList :: Dataset -> IO DCPL getDatasetCreatePList (Dataset dset_id) = fmap uncheckedFromHId $ withErrorCheck $ h5d_get_create_plist dset_id getDatasetAccessPList :: Dataset -> IO DAPL getDatasetAccessPList (Dataset dset_id) = fmap uncheckedFromHId $ withErrorCheck $ h5d_get_access_plist dset_id getDatasetStorageSize :: Dataset -> IO HSize getDatasetStorageSize (Dataset dset_id) = HSize <$> h5d_get_storage_size dset_id getDatasetOffset :: Dataset -> IO HAddr getDatasetOffset (Dataset dset_id) = fmap HAddr $ withErrorCheck $ h5d_get_offset dset_id readDataset :: NativeType t => Dataset -> Maybe Dataspace -> Maybe DXPL -> IO (SV.Vector t) readDataset dset@(Dataset dset_id) file_space_id plist_id = do effectiveSelection <- maybe (getDatasetSpace dset) return file_space_id n <- getSimpleDataspaceExtentNPoints effectiveSelection withOutVector_ (fromIntegral n) $ \buf -> withErrorCheck_ $ h5d_read dset_id (hdfTypeOf1 buf) h5s_ALL (maybe h5s_ALL hid file_space_id) (maybe h5p_DEFAULT hid plist_id) buf readDatasetInto :: NativeType t => Dataset -> Maybe Dataspace -> Maybe Dataspace -> Maybe DXPL -> SV.MVector RealWorld t -> IO () readDatasetInto dset@(Dataset dset_id) mem_space_id file_space_id plist_id vec = do effectiveSelection <- maybe (getDatasetSpace dset) return mem_space_id n <- getSimpleDataspaceExtentNPoints effectiveSelection withOutMVector vec $ \vecSz buf -> if fromIntegral n > vecSz then fail "readDatasetInto: output vector is too small to contain selection" else withErrorCheck_ $ h5d_read dset_id (hdfTypeOf1 buf) (maybe h5s_ALL hid mem_space_id) (maybe h5s_ALL hid file_space_id) (maybe h5p_DEFAULT hid plist_id) buf writeDataset :: NativeType t => Dataset -> Maybe Dataspace -> Maybe Dataspace -> Maybe DXPL -> SV.Vector t -> IO () writeDataset (Dataset dset_id) mem_space_id file_space_id plist_id buf = withErrorCheck_ $ withInVector buf $ \ibuf -> h5d_write dset_id (hdfTypeOf1 buf) (maybe h5s_ALL hid mem_space_id) (maybe h5s_ALL hid file_space_id) (maybe h5p_DEFAULT hid plist_id) ibuf -- foreign import ccall "wrapper" wrap_iterate_op -- :: (InOut a -> HId_t -> CUInt -> InArray HSize_t -> InOut b -> IO HErr_t) -- -> IO (H5D_operator_t a b) -- -- TODO: figure out a good way to properly encapsulate the buffer so that -- out-of-bounds accesses are impossible. Probably use a storable vector -- and check the bounds against the dataspace. -- Also check whether h5s_ALL is a valid dataspace; Actually I'm not sure how -- it could be, given that there is no information here about the size -- of the array apart from whatever is carried in the dataspace. -- iterateDatasetSelection :: NativeType t => InOutArray t -> Dataspace -> (InOut t -> [HSize] -> IO HErr_t) -> IO () -- iterateDatasetSelection buf space_id op = do -- -- In order to marshall Haskell exceptions through the iterate operations, -- -- we use 'maxBound' as a "something might have happened" return value -- -- and pass a description of what that was via these IORefs. -- -- -- -- The assertion mentioned here is that the 'type_id' argument to h5d_iterate -- -- is passed unchanged to the operator. If that doesn't hold, then this -- -- function will need a more complicated type. -- assertionFailed <- newIORef False -- exception <- newIORef Nothing :: IO (IORef (Maybe SomeException)) -- -- let bufType = hdfTypeOf1 buf -- -- op <- wrap_iterate_op $ \elem type_id ndim (InArray point) _operator_data -> do -- point <- peekArray (fromIntegral ndim) point -- if type_id == bufType -- then do -- result <- try (op elem (fmap HSize point)) -- case result of -- Left exc -> do -- writeIORef exception (Just exc) -- return maxBound -- Right x -> return x -- else do -- writeIORef assertionFailed True -- return maxBound -- -- result <- withErrorCheck_ -- (h5d_iterate buf bufType (hid space_id) op (InOut nullPtr) -- `finally` freeHaskellFunPtr op) -- -- when (result == maxBound) $ do -- assertionFailed <- readIORef assertionFailed -- when assertionFailed (throwIO (AssertionFailed "iterateDatasetSelection: operator called with different type_id than h5d_iterate was called with!")) -- exception <- readIORef exception -- maybe (return result) throwIO exception -- -- return result setDatasetExtent :: Dataset -> [HSize] -> IO () setDatasetExtent (Dataset dset_id) sizes = withErrorCheck_ $ withInList [sz | HSize sz <- sizes] $ \isizes -> h5d_set_extent dset_id isizes -- Fill part of a vector with a value, using the geometry and selection of the given data space fillSelection :: (NativeType a, NativeType b) => a -> SV.MVector RealWorld b -> Dataspace -> IO () fillSelection fill buf space = withErrorCheck_ $ withIn fill $ \ifill -> SV.M.unsafeWith buf $ \pbuf -> h5d_fill ifill (hdfTypeOf1 ifill) (InOutArray pbuf) (hdfTypeOf1 buf) (hid space)