    {-# LANGUAGE CPP #-}
    {-# LANGUAGE ScopedTypeVariables, ForeignFunctionInterface #-}
    #if __GLASGOW_HASKELL__ >= 701
    {-# LANGUAGE Trustworthy #-}
    #endif
    -- | Copyright   : (c) 2010 Jasper Van der Jeugt
    --                 (c) 2010 - 2011 Simon Meier
    -- License       : BSD3-style (see LICENSE)
    --
    -- Maintainer    : Simon Meier <iridcode@gmail.com>
    -- Portability   : GHC
    --
    -- Encodings using ASCII encoded Unicode characters.
    --
    module Data.ByteString.Builder.Prim.ASCII
        (

         -- *** ASCII
         char7

          -- **** Decimal numbers
          -- | Decimal encoding of numbers using ASCII encoded characters.
        , int8Dec
        , int16Dec
        , int32Dec
        , int64Dec
        , intDec

        , word8Dec
        , word16Dec
        , word32Dec
        , word64Dec
        , wordDec

        {-
        -- These are the functions currently provided by Bryan O'Sullivans
        -- double-conversion library.
        --
        -- , float
        -- , floatWith
        -- , double
        -- , doubleWith
        -}

          -- **** Hexadecimal numbers

          -- | Encoding positive integers as hexadecimal numbers using lower-case
          -- ASCII characters. The shortest possible representation is used. For
          -- example,
          --
          -- > toLazyByteString (primBounded word16Hex 0x0a10) = "a10"
          --
          -- Note that there is no support for using upper-case characters. Please
          -- contact the maintainer if your application cannot work without
          -- hexadecimal encodings that use upper-case characters.
          --
        , word8Hex
        , word16Hex
        , word32Hex
        , word64Hex
        , wordHex

          -- **** Fixed-width hexadecimal numbers
          --
          -- | Encoding the bytes of fixed-width types as hexadecimal
          -- numbers using lower-case ASCII characters. For example,
          --
          -- > toLazyByteString (primFixed word16HexFixed 0x0a10) = "0a10"
          --
        , int8HexFixed
        , int16HexFixed
        , int32HexFixed
        , int64HexFixed
        , word8HexFixed
        , word16HexFixed
        , word32HexFixed
        , word64HexFixed
        , floatHexFixed
        , doubleHexFixed

        ) where

    import Data.ByteString.Builder.Prim.Binary
    import Data.ByteString.Builder.Prim.Internal
    import Data.ByteString.Builder.Prim.Internal.Floating
    import Data.ByteString.Builder.Prim.Internal.Base16
    import Data.ByteString.Builder.Prim.Internal.UncheckedShifts

    import Data.Char (ord)

    import Foreign
    import Foreign.C.Types

    -- | Encode the least 7-bits of a 'Char' using the ASCII encoding.
    {-# INLINE char7 #-}
    char7 :: FixedPrim Char
    char7 = (\c -> fromIntegral $ ord c .&. 0x7f) >$< word8

    ------------------------------------------------------------------------------
    -- Decimal Encoding
    ------------------------------------------------------------------------------

    -- Signed integers
    ------------------

    foreign import ccall unsafe "static _hs_bytestring_int_dec" c_int_dec
        :: CInt -> Ptr Word8 -> IO (Ptr Word8)

    foreign import ccall unsafe "static _hs_bytestring_long_long_int_dec" c_long_long_int_dec
        :: CLLong -> Ptr Word8 -> IO (Ptr Word8)

    {-# INLINE encodeIntDecimal #-}
    encodeIntDecimal :: Integral a => Int -> BoundedPrim a
    encodeIntDecimal bound = boudedPrim bound $ c_int_dec . fromIntegral

    -- | Decimal encoding of an 'Int8'.
    {-# INLINE int8Dec #-}
    int8Dec :: BoundedPrim Int8
    int8Dec = encodeIntDecimal 4

    -- | Decimal encoding of an 'Int16'.
    {-# INLINE int16Dec #-}
    int16Dec :: BoundedPrim Int16
    int16Dec = encodeIntDecimal 6

    -- | Decimal encoding of an 'Int32'.
    {-# INLINE int32Dec #-}
    int32Dec :: BoundedPrim Int32
    int32Dec = encodeIntDecimal 11

    -- | Decimal encoding of an 'Int64'.
    {-# INLINE int64Dec #-}
    int64Dec :: BoundedPrim Int64
    int64Dec = boudedPrim 20 $ c_long_long_int_dec . fromIntegral

    -- | Decimal encoding of an 'Int'.
    {-# INLINE intDec #-}
    intDec :: BoundedPrim Int
    intDec = caseWordSize_32_64
        (fromIntegral >$< int32Dec)
        (fromIntegral >$< int64Dec)

    -- Unsigned integers
    --------------------

    foreign import ccall unsafe "static _hs_bytestring_uint_dec" c_uint_dec
        :: CUInt -> Ptr Word8 -> IO (Ptr Word8)

    foreign import ccall unsafe "static _hs_bytestring_long_long_uint_dec" c_long_long_uint_dec
        :: CULLong -> Ptr Word8 -> IO (Ptr Word8)

    {-# INLINE encodeWordDecimal #-}
    encodeWordDecimal :: Integral a => Int -> BoundedPrim a
    encodeWordDecimal bound = boudedPrim bound $ c_uint_dec . fromIntegral

    -- | Decimal encoding of a 'Word8'.
    {-# INLINE word8Dec #-}
    word8Dec :: BoundedPrim Word8
    word8Dec = encodeWordDecimal 3

    -- | Decimal encoding of a 'Word16'.
    {-# INLINE word16Dec #-}
    word16Dec :: BoundedPrim Word16
    word16Dec = encodeWordDecimal 5

    -- | Decimal encoding of a 'Word32'.
    {-# INLINE word32Dec #-}
    word32Dec :: BoundedPrim Word32
    word32Dec = encodeWordDecimal 10

    -- | Decimal encoding of a 'Word64'.
    {-# INLINE word64Dec #-}
    word64Dec :: BoundedPrim Word64
    word64Dec = boudedPrim 20 $ c_long_long_uint_dec . fromIntegral

    -- | Decimal encoding of a 'Word'.
    {-# INLINE wordDec #-}
    wordDec :: BoundedPrim Word
    wordDec = caseWordSize_32_64
        (fromIntegral >$< word32Dec)
        (fromIntegral >$< word64Dec)

    ------------------------------------------------------------------------------
    -- Hexadecimal Encoding
    ------------------------------------------------------------------------------

    -- without lead
    ---------------

    foreign import ccall unsafe "static _hs_bytestring_uint_hex" c_uint_hex
        :: CUInt -> Ptr Word8 -> IO (Ptr Word8)

    foreign import ccall unsafe "static _hs_bytestring_long_long_uint_hex" c_long_long_uint_hex
        :: CULLong -> Ptr Word8 -> IO (Ptr Word8)

    {-# INLINE encodeWordHex #-}
    encodeWordHex :: forall a. (Storable a, Integral a) => BoundedPrim a
    encodeWordHex =
        boudedPrim (2 * sizeOf (undefined :: a)) $ c_uint_hex  . fromIntegral

    -- | Hexadecimal encoding of a 'Word8'.
    {-# INLINE word8Hex #-}
    word8Hex :: BoundedPrim Word8
    word8Hex = encodeWordHex

    -- | Hexadecimal encoding of a 'Word16'.
    {-# INLINE word16Hex #-}
    word16Hex :: BoundedPrim Word16
    word16Hex = encodeWordHex

    -- | Hexadecimal encoding of a 'Word32'.
    {-# INLINE word32Hex #-}
    word32Hex :: BoundedPrim Word32
    word32Hex = encodeWordHex

    -- | Hexadecimal encoding of a 'Word64'.
    {-# INLINE word64Hex #-}
    word64Hex :: BoundedPrim Word64
    word64Hex = boudedPrim 16 $ c_long_long_uint_hex . fromIntegral

    -- | Hexadecimal encoding of a 'Word'.
    {-# INLINE wordHex #-}
    wordHex :: BoundedPrim Word
    wordHex = caseWordSize_32_64
        (fromIntegral >$< word32Hex)
        (fromIntegral >$< word64Hex)

    -- fixed width; leading zeroes
    ------------------------------

    -- | Encode a 'Word8' using 2 nibbles (hexadecimal digits).
    {-# INLINE word8HexFixed #-}
    word8HexFixed :: FixedPrim Word8
    word8HexFixed = fixedPrim 2 $
        \x op -> poke (castPtr op) =<< encode8_as_16h lowerTable x

    -- | Encode a 'Word16' using 4 nibbles.
    {-# INLINE word16HexFixed #-}
    word16HexFixed :: FixedPrim Word16
    word16HexFixed =
        (\x -> (fromIntegral $ x `shiftr_w16` 8, fromIntegral x))
          >$< pairF word8HexFixed word8HexFixed

    -- | Encode a 'Word32' using 8 nibbles.
    {-# INLINE word32HexFixed #-}
    word32HexFixed :: FixedPrim Word32
    word32HexFixed =
        (\x -> (fromIntegral $ x `shiftr_w32` 16, fromIntegral x))
          >$< pairF word16HexFixed word16HexFixed
    -- | Encode a 'Word64' using 16 nibbles.
    {-# INLINE word64HexFixed #-}
    word64HexFixed :: FixedPrim Word64
    word64HexFixed =
        (\x -> (fromIntegral $ x `shiftr_w64` 32, fromIntegral x))
          >$< pairF word32HexFixed word32HexFixed

    -- | Encode a 'Int8' using 2 nibbles (hexadecimal digits).
    {-# INLINE int8HexFixed #-}
    int8HexFixed :: FixedPrim Int8
    int8HexFixed = fromIntegral >$< word8HexFixed

    -- | Encode a 'Int16' using 4 nibbles.
    {-# INLINE int16HexFixed #-}
    int16HexFixed :: FixedPrim Int16
    int16HexFixed = fromIntegral >$< word16HexFixed

    -- | Encode a 'Int32' using 8 nibbles.
    {-# INLINE int32HexFixed #-}
    int32HexFixed :: FixedPrim Int32
    int32HexFixed = fromIntegral >$< word32HexFixed

    -- | Encode a 'Int64' using 16 nibbles.
    {-# INLINE int64HexFixed #-}
    int64HexFixed :: FixedPrim Int64
    int64HexFixed = fromIntegral >$< word64HexFixed

    -- | Encode an IEEE 'Float' using 8 nibbles.
    {-# INLINE floatHexFixed #-}
    floatHexFixed :: FixedPrim Float
    floatHexFixed = encodeFloatViaWord32F word32HexFixed

    -- | Encode an IEEE 'Double' using 16 nibbles.
    {-# INLINE doubleHexFixed #-}
    doubleHexFixed :: FixedPrim Double
    doubleHexFixed = encodeDoubleViaWord64F word64HexFixed