{-# OPTIONS_HADDOCK not-home #-}

-- | Copyright : (c) 2010 - 2011 Simon Meier
-- License     : BSD3-style (see LICENSE)
--
-- Maintainer  : Simon Meier <iridcode@gmail.com>
-- Portability : GHC
--
-- Constructing 'Builder's using ASCII-based encodings.
--
module Data.ByteString.Builder.ASCII
    (
      -- ** Formatting numbers as text
      -- | Formatting of numbers as ASCII text.
      --
      -- Note that you can also use these functions for the ISO/IEC 8859-1 and
      -- UTF-8 encodings, as the ASCII encoding is equivalent on the
      -- codepoints 0-127.

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

    , word8Dec
    , word16Dec
    , word32Dec
    , word64Dec
    , wordDec

    , floatDec
    , doubleDec

      -- *** Hexadecimal numbers

      -- | Encoding positive integers as hexadecimal numbers using lower-case
      -- ASCII characters. The shortest
      -- possible representation is used. For example,
      --
      -- >>> toLazyByteString (word16Hex 0x0a10)
      -- Chunk "a10" Empty
      --
      -- 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
      --
    , int8HexFixed
    , int16HexFixed
    , int32HexFixed
    , int64HexFixed
    , word8HexFixed
    , word16HexFixed
    , word32HexFixed
    , word64HexFixed

    , floatHexFixed
    , doubleHexFixed

    , byteStringHex
    , lazyByteStringHex

    ) where

import           Data.ByteString                                as S
import           Data.ByteString.Lazy                           as L
import           Data.ByteString.Builder.Internal (Builder)
import qualified Data.ByteString.Builder.Prim                   as P
import qualified Data.ByteString.Builder.Prim.Internal          as P
import           Data.ByteString.Builder.RealFloat (floatDec, doubleDec)
import           Data.ByteString.Internal.Type (c_int_dec_padded9, c_long_long_int_dec_padded18)

import           Foreign
import           Data.List.NonEmpty (NonEmpty(..))

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

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

-- | Decimal encoding of an 'Int8' using the ASCII digits.
--
-- e.g.
--
-- > toLazyByteString (int8Dec 42)   = "42"
-- > toLazyByteString (int8Dec (-1)) = "-1"
--
{-# INLINE int8Dec #-}
int8Dec :: Int8 -> Builder
int8Dec :: Int8 -> Builder
int8Dec = BoundedPrim Int8 -> Int8 -> Builder
forall a. BoundedPrim a -> a -> Builder
P.primBounded BoundedPrim Int8
P.int8Dec

-- | Decimal encoding of an 'Int16' using the ASCII digits.
{-# INLINE int16Dec #-}
int16Dec :: Int16 -> Builder
int16Dec :: Int16 -> Builder
int16Dec = BoundedPrim Int16 -> Int16 -> Builder
forall a. BoundedPrim a -> a -> Builder
P.primBounded BoundedPrim Int16
P.int16Dec

-- | Decimal encoding of an 'Int32' using the ASCII digits.
{-# INLINE int32Dec #-}
int32Dec :: Int32 -> Builder
int32Dec :: Int32 -> Builder
int32Dec = BoundedPrim Int32 -> Int32 -> Builder
forall a. BoundedPrim a -> a -> Builder
P.primBounded BoundedPrim Int32
P.int32Dec

-- | Decimal encoding of an 'Int64' using the ASCII digits.
{-# INLINE int64Dec #-}
int64Dec :: Int64 -> Builder
int64Dec :: Int64 -> Builder
int64Dec = BoundedPrim Int64 -> Int64 -> Builder
forall a. BoundedPrim a -> a -> Builder
P.primBounded BoundedPrim Int64
P.int64Dec

-- | Decimal encoding of an 'Int' using the ASCII digits.
{-# INLINE intDec #-}
intDec :: Int -> Builder
intDec :: Int -> Builder
intDec = BoundedPrim Int -> Int -> Builder
forall a. BoundedPrim a -> a -> Builder
P.primBounded BoundedPrim Int
P.intDec


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

-- | Decimal encoding of a 'Word8' using the ASCII digits.
{-# INLINE word8Dec #-}
word8Dec :: Word8 -> Builder
word8Dec :: Word8 -> Builder
word8Dec = BoundedPrim Word8 -> Word8 -> Builder
forall a. BoundedPrim a -> a -> Builder
P.primBounded BoundedPrim Word8
P.word8Dec

-- | Decimal encoding of a 'Word16' using the ASCII digits.
{-# INLINE word16Dec #-}
word16Dec :: Word16 -> Builder
word16Dec :: Word16 -> Builder
word16Dec = BoundedPrim Word16 -> Word16 -> Builder
forall a. BoundedPrim a -> a -> Builder
P.primBounded BoundedPrim Word16
P.word16Dec

-- | Decimal encoding of a 'Word32' using the ASCII digits.
{-# INLINE word32Dec #-}
word32Dec :: Word32 -> Builder
word32Dec :: Word32 -> Builder
word32Dec = BoundedPrim Word32 -> Word32 -> Builder
forall a. BoundedPrim a -> a -> Builder
P.primBounded BoundedPrim Word32
P.word32Dec

-- | Decimal encoding of a 'Word64' using the ASCII digits.
{-# INLINE word64Dec #-}
word64Dec :: Word64 -> Builder
word64Dec :: Word64 -> Builder
word64Dec = BoundedPrim Word64 -> Word64 -> Builder
forall a. BoundedPrim a -> a -> Builder
P.primBounded BoundedPrim Word64
P.word64Dec

-- | Decimal encoding of a 'Word' using the ASCII digits.
{-# INLINE wordDec #-}
wordDec :: Word -> Builder
wordDec :: Word -> Builder
wordDec = BoundedPrim Word -> Word -> Builder
forall a. BoundedPrim a -> a -> Builder
P.primBounded BoundedPrim Word
P.wordDec


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

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

-- | Shortest hexadecimal encoding of a 'Word8' using lower-case characters.
{-# INLINE word8Hex #-}
word8Hex :: Word8 -> Builder
word8Hex :: Word8 -> Builder
word8Hex = BoundedPrim Word8 -> Word8 -> Builder
forall a. BoundedPrim a -> a -> Builder
P.primBounded BoundedPrim Word8
P.word8Hex

-- | Shortest hexadecimal encoding of a 'Word16' using lower-case characters.
{-# INLINE word16Hex #-}
word16Hex :: Word16 -> Builder
word16Hex :: Word16 -> Builder
word16Hex = BoundedPrim Word16 -> Word16 -> Builder
forall a. BoundedPrim a -> a -> Builder
P.primBounded BoundedPrim Word16
P.word16Hex

-- | Shortest hexadecimal encoding of a 'Word32' using lower-case characters.
{-# INLINE word32Hex #-}
word32Hex :: Word32 -> Builder
word32Hex :: Word32 -> Builder
word32Hex = BoundedPrim Word32 -> Word32 -> Builder
forall a. BoundedPrim a -> a -> Builder
P.primBounded BoundedPrim Word32
P.word32Hex

-- | Shortest hexadecimal encoding of a 'Word64' using lower-case characters.
{-# INLINE word64Hex #-}
word64Hex :: Word64 -> Builder
word64Hex :: Word64 -> Builder
word64Hex = BoundedPrim Word64 -> Word64 -> Builder
forall a. BoundedPrim a -> a -> Builder
P.primBounded BoundedPrim Word64
P.word64Hex

-- | Shortest hexadecimal encoding of a 'Word' using lower-case characters.
{-# INLINE wordHex #-}
wordHex :: Word -> Builder
wordHex :: Word -> Builder
wordHex = BoundedPrim Word -> Word -> Builder
forall a. BoundedPrim a -> a -> Builder
P.primBounded BoundedPrim Word
P.wordHex


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

-- | Encode a 'Int8' using 2 nibbles (hexadecimal digits).
{-# INLINE int8HexFixed #-}
int8HexFixed :: Int8 -> Builder
int8HexFixed :: Int8 -> Builder
int8HexFixed = FixedPrim Int8 -> Int8 -> Builder
forall a. FixedPrim a -> a -> Builder
P.primFixed FixedPrim Int8
P.int8HexFixed

-- | Encode a 'Int16' using 4 nibbles.
{-# INLINE int16HexFixed #-}
int16HexFixed :: Int16 -> Builder
int16HexFixed :: Int16 -> Builder
int16HexFixed = FixedPrim Int16 -> Int16 -> Builder
forall a. FixedPrim a -> a -> Builder
P.primFixed FixedPrim Int16
P.int16HexFixed

-- | Encode a 'Int32' using 8 nibbles.
{-# INLINE int32HexFixed #-}
int32HexFixed :: Int32 -> Builder
int32HexFixed :: Int32 -> Builder
int32HexFixed = FixedPrim Int32 -> Int32 -> Builder
forall a. FixedPrim a -> a -> Builder
P.primFixed FixedPrim Int32
P.int32HexFixed

-- | Encode a 'Int64' using 16 nibbles.
{-# INLINE int64HexFixed #-}
int64HexFixed :: Int64 -> Builder
int64HexFixed :: Int64 -> Builder
int64HexFixed = FixedPrim Int64 -> Int64 -> Builder
forall a. FixedPrim a -> a -> Builder
P.primFixed FixedPrim Int64
P.int64HexFixed

-- | Encode a 'Word8' using 2 nibbles (hexadecimal digits).
{-# INLINE word8HexFixed #-}
word8HexFixed :: Word8 -> Builder
word8HexFixed :: Word8 -> Builder
word8HexFixed = FixedPrim Word8 -> Word8 -> Builder
forall a. FixedPrim a -> a -> Builder
P.primFixed FixedPrim Word8
P.word8HexFixed

-- | Encode a 'Word16' using 4 nibbles.
{-# INLINE word16HexFixed #-}
word16HexFixed :: Word16 -> Builder
word16HexFixed :: Word16 -> Builder
word16HexFixed = FixedPrim Word16 -> Word16 -> Builder
forall a. FixedPrim a -> a -> Builder
P.primFixed FixedPrim Word16
P.word16HexFixed

-- | Encode a 'Word32' using 8 nibbles.
{-# INLINE word32HexFixed #-}
word32HexFixed :: Word32 -> Builder
word32HexFixed :: Word32 -> Builder
word32HexFixed = FixedPrim Word32 -> Word32 -> Builder
forall a. FixedPrim a -> a -> Builder
P.primFixed FixedPrim Word32
P.word32HexFixed

-- | Encode a 'Word64' using 16 nibbles.
{-# INLINE word64HexFixed #-}
word64HexFixed :: Word64 -> Builder
word64HexFixed :: Word64 -> Builder
word64HexFixed = FixedPrim Word64 -> Word64 -> Builder
forall a. FixedPrim a -> a -> Builder
P.primFixed FixedPrim Word64
P.word64HexFixed

-- | Encode an IEEE 'Float' using 8 nibbles.
{-# INLINE floatHexFixed #-}
floatHexFixed :: Float -> Builder
floatHexFixed :: Float -> Builder
floatHexFixed = FixedPrim Float -> Float -> Builder
forall a. FixedPrim a -> a -> Builder
P.primFixed FixedPrim Float
P.floatHexFixed

-- | Encode an IEEE 'Double' using 16 nibbles.
{-# INLINE doubleHexFixed #-}
doubleHexFixed :: Double -> Builder
doubleHexFixed :: Double -> Builder
doubleHexFixed = FixedPrim Double -> Double -> Builder
forall a. FixedPrim a -> a -> Builder
P.primFixed FixedPrim Double
P.doubleHexFixed

-- | Encode each byte of a 'S.StrictByteString' using its fixed-width hex encoding.
{-# NOINLINE byteStringHex #-} -- share code
byteStringHex :: S.StrictByteString -> Builder
byteStringHex :: StrictByteString -> Builder
byteStringHex = FixedPrim Word8 -> StrictByteString -> Builder
P.primMapByteStringFixed FixedPrim Word8
P.word8HexFixed

-- | Encode each byte of a 'L.LazyByteString' using its fixed-width hex encoding.
{-# NOINLINE lazyByteStringHex #-} -- share code
lazyByteStringHex :: L.LazyByteString -> Builder
lazyByteStringHex :: LazyByteString -> Builder
lazyByteStringHex = FixedPrim Word8 -> LazyByteString -> Builder
P.primMapLazyByteStringFixed FixedPrim Word8
P.word8HexFixed


------------------------------------------------------------------------------
-- Fast decimal 'Integer' encoding.
------------------------------------------------------------------------------

-- An optimized version of the integer serialization code
-- in blaze-textual (c) 2011 MailRank, Inc. Bryan O'Sullivan
-- <bos@mailrank.com>. It is 2.5x faster on Int-sized integers and 4.5x faster
-- on larger integers.

-- | Maximal power of 10 fitting into an 'Int' without using the MSB.
--     10 ^ 9  for 32 bit ints  (31 * log 2 / log 10 =  9.33)
--     10 ^ 18 for 64 bit ints  (63 * log 2 / log 10 = 18.96)
--
-- FIXME: Think about also using the MSB. For 64 bit 'Int's this makes a
-- difference.
maxPow10 :: Integer
maxPow10 :: Integer
maxPow10 = Int -> Integer
forall a. Integral a => a -> Integer
toInteger (Int -> Integer) -> Int -> Integer
forall a b. (a -> b) -> a -> b
$ (Int
10 :: Int) Int -> Int -> Int
forall a b. (Num a, Integral b) => a -> b -> a
^ Int -> Int -> Int
forall a. a -> a -> a
P.caseWordSize_32_64 (Int
9 :: Int) Int
18

-- | Decimal encoding of an 'Integer' using the ASCII digits.
integerDec :: Integer -> Builder
integerDec :: Integer -> Builder
integerDec Integer
i
    | Int
i' <- Integer -> Int
forall a. Num a => Integer -> a
fromInteger Integer
i, Int -> Integer
forall a. Integral a => a -> Integer
toInteger Int
i' Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
i = Int -> Builder
intDec Int
i'
    | Integer
i Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< Integer
0     = FixedPrim Char -> Char -> Builder
forall a. FixedPrim a -> a -> Builder
P.primFixed FixedPrim Char
P.char8 Char
'-' Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` Integer -> Builder
go (-Integer
i)
    | Bool
otherwise =                                   Integer -> Builder
go Integer
i
  where
    go :: Integer -> Builder
    go :: Integer -> Builder
go Integer
n | Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< Integer
maxPow10 = Int -> Builder
intDec (Integer -> Int
forall a. Num a => Integer -> a
fromInteger Integer
n)
         | Bool
otherwise    =
             case NonEmpty Integer -> NonEmpty Int
putH (Integer -> Integer -> NonEmpty Integer
splitf (Integer
maxPow10 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
maxPow10) Integer
n) of
               Int
x:|[Int]
xs -> Int -> Builder
intDec Int
x Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` BoundedPrim Int -> [Int] -> Builder
forall a. BoundedPrim a -> [a] -> Builder
P.primMapListBounded BoundedPrim Int
intDecPadded [Int]
xs

    splitf :: Integer -> Integer -> NonEmpty Integer
    splitf :: Integer -> Integer -> NonEmpty Integer
splitf Integer
pow10 Integer
n0
      | Integer
pow10 Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
> Integer
n0  = Integer
n0 Integer -> [Integer] -> NonEmpty Integer
forall a. a -> [a] -> NonEmpty a
:| []
      | Bool
otherwise   = NonEmpty Integer -> NonEmpty Integer
splith (Integer -> Integer -> NonEmpty Integer
splitf (Integer
pow10 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
pow10) Integer
n0)
      where
        splith :: NonEmpty Integer -> NonEmpty Integer
splith (Integer
n:|[Integer]
ns) =
            case Integer
n Integer -> Integer -> (Integer, Integer)
forall a. Integral a => a -> a -> (a, a)
`quotRem` Integer
pow10 of
                (Integer
q,Integer
r) | Integer
q Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
> Integer
0     -> Integer
q Integer -> [Integer] -> NonEmpty Integer
forall a. a -> [a] -> NonEmpty a
:| Integer
r Integer -> [Integer] -> [Integer]
forall a. a -> [a] -> [a]
:  [Integer] -> [Integer]
splitb [Integer]
ns
                      | Bool
otherwise ->      Integer
r Integer -> [Integer] -> NonEmpty Integer
forall a. a -> [a] -> NonEmpty a
:| [Integer] -> [Integer]
splitb [Integer]
ns

        splitb :: [Integer] -> [Integer]
splitb []     = []
        splitb (Integer
n:[Integer]
ns) = case Integer
n Integer -> Integer -> (Integer, Integer)
forall a. Integral a => a -> a -> (a, a)
`quotRem` Integer
pow10 of
                            (Integer
q,Integer
r) -> Integer
q Integer -> [Integer] -> [Integer]
forall a. a -> [a] -> [a]
: Integer
r Integer -> [Integer] -> [Integer]
forall a. a -> [a] -> [a]
: [Integer] -> [Integer]
splitb [Integer]
ns

    putH :: NonEmpty Integer -> NonEmpty Int
    putH :: NonEmpty Integer -> NonEmpty Int
putH (Integer
n:|[Integer]
ns) = case Integer
n Integer -> Integer -> (Integer, Integer)
forall a. Integral a => a -> a -> (a, a)
`quotRem` Integer
maxPow10 of
                    (Integer
x,Integer
y)
                        | Int
q Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0     -> Int
q Int -> [Int] -> NonEmpty Int
forall a. a -> [a] -> NonEmpty a
:| Int
r Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
:  [Integer] -> [Int]
putB [Integer]
ns
                        | Bool
otherwise ->      Int
r Int -> [Int] -> NonEmpty Int
forall a. a -> [a] -> NonEmpty a
:| [Integer] -> [Int]
putB [Integer]
ns
                        where q :: Int
q = Integer -> Int
forall a. Num a => Integer -> a
fromInteger Integer
x
                              r :: Int
r = Integer -> Int
forall a. Num a => Integer -> a
fromInteger Integer
y

    putB :: [Integer] -> [Int]
    putB :: [Integer] -> [Int]
putB []     = []
    putB (Integer
n:[Integer]
ns) = case Integer
n Integer -> Integer -> (Integer, Integer)
forall a. Integral a => a -> a -> (a, a)
`quotRem` Integer
maxPow10 of
                    (Integer
q,Integer
r) -> Integer -> Int
forall a. Num a => Integer -> a
fromInteger Integer
q Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
: Integer -> Int
forall a. Num a => Integer -> a
fromInteger Integer
r Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
: [Integer] -> [Int]
putB [Integer]
ns


{-# INLINE intDecPadded #-}
intDecPadded :: P.BoundedPrim Int
intDecPadded :: BoundedPrim Int
intDecPadded = FixedPrim Int -> BoundedPrim Int
forall a. FixedPrim a -> BoundedPrim a
P.liftFixedToBounded (FixedPrim Int -> BoundedPrim Int)
-> FixedPrim Int -> BoundedPrim Int
forall a b. (a -> b) -> a -> b
$ FixedPrim Int -> FixedPrim Int -> FixedPrim Int
forall a. a -> a -> a
P.caseWordSize_32_64
    (Int -> (Int -> Ptr Word8 -> IO ()) -> FixedPrim Int
forall a. Int -> (a -> Ptr Word8 -> IO ()) -> FixedPrim a
P.fixedPrim  Int
9 ((Int -> Ptr Word8 -> IO ()) -> FixedPrim Int)
-> (Int -> Ptr Word8 -> IO ()) -> FixedPrim Int
forall a b. (a -> b) -> a -> b
$ CInt -> Ptr Word8 -> IO ()
c_int_dec_padded9            (CInt -> Ptr Word8 -> IO ())
-> (Int -> CInt) -> Int -> Ptr Word8 -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral)
    (Int -> (Int -> Ptr Word8 -> IO ()) -> FixedPrim Int
forall a. Int -> (a -> Ptr Word8 -> IO ()) -> FixedPrim a
P.fixedPrim Int
18 ((Int -> Ptr Word8 -> IO ()) -> FixedPrim Int)
-> (Int -> Ptr Word8 -> IO ()) -> FixedPrim Int
forall a b. (a -> b) -> a -> b
$ CLLong -> Ptr Word8 -> IO ()
c_long_long_int_dec_padded18 (CLLong -> Ptr Word8 -> IO ())
-> (Int -> CLLong) -> Int -> Ptr Word8 -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> CLLong
forall a b. (Integral a, Num b) => a -> b
fromIntegral)