-- | Loading and storing doubles directly from/to memory buffers. -- -- * Calls out to foreign load and store functions written in C and C++, -- so performance should be alright. -- module Data.Repa.Scalar.Double ( -- * Loading loadDouble -- * Storing , storeDoubleShortest , storeDoubleFixed) where import Data.Word import GHC.Exts import qualified Data.ByteString.Internal as BS import qualified Data.Double.Conversion.ByteString as DC import qualified Foreign.Ptr as F import qualified Foreign.ForeignPtr as F import qualified Foreign.Storable as F import qualified Foreign.Marshal.Alloc as F import qualified Foreign.Marshal.Utils as F -- Double ----------------------------------------------------------------------------------------- -- | Load an ASCII `Double` from a foreign buffer -- returning the value and number of characters read. -- -- * Calls out do the stdlib `strtod` function. -- loadDouble :: Ptr Word8 -- ^ Buffer holding ASCII representation. -> Int -- ^ Length of buffer. -> IO (Double, Int) -- ^ Result, and number of characters read from buffer. loadDouble !pIn !len = F.allocaBytes (len + 1) $ \pBuf -> F.alloca $ \pRes -> do -- Copy the data to our new buffer. F.copyBytes pBuf pIn (fromIntegral len) -- Poke a 0 on the end to ensure it's null terminated. F.pokeByteOff pBuf len (0 :: Word8) -- Call the C strtod function let !d = strtod pBuf pRes -- Read back the end pointer. res <- F.peek pRes return (d, res `F.minusPtr` pBuf) {-# INLINE loadDouble #-} -- TODO: strtod will skip whitespace before the actual double, -- but we probably want to avoid this to be consistent. foreign import ccall unsafe strtod :: Ptr Word8 -> Ptr (Ptr Word8) -> Double -- | Store an ASCII `Double`, yielding a freshly allocated buffer -- and its length. -- -- * Calls out to the `double-conversion` library which is binding -- to a C++ implementation. -- -- * The value is printed as either (sign)digits.digits, -- or in exponential format, depending on which is shorter. -- -- * The result is buffer not null terminated. -- storeDoubleShortest :: Double -> IO (F.ForeignPtr Word8, Int) storeDoubleShortest d = case DC.toShortest d of BS.PS p _ n -> return (p, n) {-# INLINE storeDoubleShortest #-} -- | Like `showDoubleShortest`, but use a fixed number of digits after -- the decimal point. storeDoubleFixed :: Int -> Double -> IO (F.ForeignPtr Word8, Int) storeDoubleFixed !prec !d = case DC.toFixed prec d of BS.PS p _ n -> return (p, n) {-# INLINE storeDoubleFixed #-}