    {-# LANGUAGE TypeFamilies, TypeOperators #-}

    -- |
    -- Module      : Data.Double.Conversion.ByteStringBuilder
    -- Copyright   : (c) 2011 MailRank, Inc.
    --
    -- License     : BSD-style
    -- Maintainer  : bos@serpentine.com
    -- Stability   : experimental
    -- Portability : GHC
    --
    -- Fast, efficient support for converting between double precision
    -- floating point values and bytestring builder.

    module Data.Double.Conversion.Internal.ByteStringBuilder
        (convert
        ) where

    import Control.Monad (when)

    import Data.ByteString.Builder.Prim.Internal (BoundedPrim, boudedPrim)

    import Data.Double.Conversion.Internal.FFI (ForeignFloating)
    import Data.Word (Word8)
    import Foreign.C.Types (CDouble, CFloat, CInt)
    import Foreign.Ptr (Ptr, plusPtr)

    convert :: (RealFloat a, RealFloat b , b ~ ForeignFloating a) => String -> CInt -> (b -> Ptr Word8 -> IO CInt) -> BoundedPrim a
    {-# SPECIALIZE convert :: String -> CInt -> (CDouble -> Ptr Word8 -> IO CInt) -> BoundedPrim Double #-}
    {-# SPECIALIZE convert :: String -> CInt -> (CFloat -> Ptr Word8 -> IO CInt) -> BoundedPrim Float #-}
    {-# INLINABLE convert #-}
    convert :: forall a b.
    (RealFloat a, RealFloat b, b ~ ForeignFloating a) =>
    String -> CInt -> (b -> Ptr Word8 -> IO CInt) -> BoundedPrim a
    convert String
    func CInt
    len b -> Ptr Word8 -> IO CInt
    act = Int -> (a -> Ptr Word8 -> IO (Ptr Word8)) -> BoundedPrim a
    forall a.
    Int -> (a -> Ptr Word8 -> IO (Ptr Word8)) -> BoundedPrim a
    boudedPrim (CInt -> Int
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral CInt
    len) ((a -> Ptr Word8 -> IO (Ptr Word8)) -> BoundedPrim a)
    -> (a -> Ptr Word8 -> IO (Ptr Word8)) -> BoundedPrim a
    forall a b. (a -> b) -> a -> b
    $ \a
    val Ptr Word8
    ptr -> do
      size <- b -> Ptr Word8 -> IO CInt
    act (a -> b
    forall a b. (Real a, Fractional b) => a -> b
    realToFrac a
    val) Ptr Word8
    ptr
      when (size == -1) .
        fail $ "Data.Double.Conversion.ByteString." ++ func ++
               ": conversion failed (invalid precision requested)"
      return (ptr `plusPtr` (fromIntegral size))