bytestring-0.10.4.1: Data/ByteString/Builder/ASCII.hs
{-# LANGUAGE ScopedTypeVariables, CPP, ForeignFunctionInterface,
MagicHash, UnboxedTuples #-}
{-# OPTIONS_HADDOCK hide #-}
#if __GLASGOW_HASKELL__ >= 701
{-# LANGUAGE Trustworthy #-}
#endif
-- | 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 Foreign
#if defined(__GLASGOW_HASKELL__) && defined(INTEGER_GMP)
import Data.Monoid (mappend)
import Foreign.C.Types
import qualified Data.ByteString.Builder.Prim.Internal as P
import Data.ByteString.Builder.Prim.Internal.UncheckedShifts
( caseWordSize_32_64 )
import GHC.Num (quotRemInteger)
import GHC.Types (Int(..))
# if __GLASGOW_HASKELL__ < 611
import GHC.Integer.Internals
# else
import GHC.Integer.GMP.Internals
# endif
#endif
------------------------------------------------------------------------------
-- Decimal Encoding
------------------------------------------------------------------------------
-- | Encode a 'String' using 'P.char7'.
{-# INLINE string7 #-}
string7 :: String -> Builder
string7 = P.primMapListFixed P.char7
------------------------------------------------------------------------------
-- 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 = P.primBounded P.int8Dec
-- | Decimal encoding of an 'Int16' using the ASCII digits.
{-# INLINE int16Dec #-}
int16Dec :: Int16 -> Builder
int16Dec = P.primBounded P.int16Dec
-- | Decimal encoding of an 'Int32' using the ASCII digits.
{-# INLINE int32Dec #-}
int32Dec :: Int32 -> Builder
int32Dec = P.primBounded P.int32Dec
-- | Decimal encoding of an 'Int64' using the ASCII digits.
{-# INLINE int64Dec #-}
int64Dec :: Int64 -> Builder
int64Dec = P.primBounded P.int64Dec
-- | Decimal encoding of an 'Int' using the ASCII digits.
{-# INLINE intDec #-}
intDec :: Int -> Builder
intDec = P.primBounded P.intDec
-- Unsigned integers
--------------------
-- | Decimal encoding of a 'Word8' using the ASCII digits.
{-# INLINE word8Dec #-}
word8Dec :: Word8 -> Builder
word8Dec = P.primBounded P.word8Dec
-- | Decimal encoding of a 'Word16' using the ASCII digits.
{-# INLINE word16Dec #-}
word16Dec :: Word16 -> Builder
word16Dec = P.primBounded P.word16Dec
-- | Decimal encoding of a 'Word32' using the ASCII digits.
{-# INLINE word32Dec #-}
word32Dec :: Word32 -> Builder
word32Dec = P.primBounded P.word32Dec
-- | Decimal encoding of a 'Word64' using the ASCII digits.
{-# INLINE word64Dec #-}
word64Dec :: Word64 -> Builder
word64Dec = P.primBounded P.word64Dec
-- | Decimal encoding of a 'Word' using the ASCII digits.
{-# INLINE wordDec #-}
wordDec :: Word -> Builder
wordDec = P.primBounded P.wordDec
-- Floating point numbers
-------------------------
-- TODO: Use Bryan O'Sullivan's double-conversion package to speed it up.
-- | /Currently slow./ Decimal encoding of an IEEE 'Float'.
{-# INLINE floatDec #-}
floatDec :: Float -> Builder
floatDec = string7 . show
-- | /Currently slow./ Decimal encoding of an IEEE 'Double'.
{-# INLINE doubleDec #-}
doubleDec :: Double -> Builder
doubleDec = string7 . show
------------------------------------------------------------------------------
-- Hexadecimal Encoding
------------------------------------------------------------------------------
-- without lead
---------------
-- | Shortest hexadecimal encoding of a 'Word8' using lower-case characters.
{-# INLINE word8Hex #-}
word8Hex :: Word8 -> Builder
word8Hex = P.primBounded P.word8Hex
-- | Shortest hexadecimal encoding of a 'Word16' using lower-case characters.
{-# INLINE word16Hex #-}
word16Hex :: Word16 -> Builder
word16Hex = P.primBounded P.word16Hex
-- | Shortest hexadecimal encoding of a 'Word32' using lower-case characters.
{-# INLINE word32Hex #-}
word32Hex :: Word32 -> Builder
word32Hex = P.primBounded P.word32Hex
-- | Shortest hexadecimal encoding of a 'Word64' using lower-case characters.
{-# INLINE word64Hex #-}
word64Hex :: Word64 -> Builder
word64Hex = P.primBounded P.word64Hex
-- | Shortest hexadecimal encoding of a 'Word' using lower-case characters.
{-# INLINE wordHex #-}
wordHex :: Word -> Builder
wordHex = P.primBounded P.wordHex
-- fixed width; leading zeroes
------------------------------
-- | Encode a 'Int8' using 2 nibbles (hexadecimal digits).
{-# INLINE int8HexFixed #-}
int8HexFixed :: Int8 -> Builder
int8HexFixed = P.primFixed P.int8HexFixed
-- | Encode a 'Int16' using 4 nibbles.
{-# INLINE int16HexFixed #-}
int16HexFixed :: Int16 -> Builder
int16HexFixed = P.primFixed P.int16HexFixed
-- | Encode a 'Int32' using 8 nibbles.
{-# INLINE int32HexFixed #-}
int32HexFixed :: Int32 -> Builder
int32HexFixed = P.primFixed P.int32HexFixed
-- | Encode a 'Int64' using 16 nibbles.
{-# INLINE int64HexFixed #-}
int64HexFixed :: Int64 -> Builder
int64HexFixed = P.primFixed P.int64HexFixed
-- | Encode a 'Word8' using 2 nibbles (hexadecimal digits).
{-# INLINE word8HexFixed #-}
word8HexFixed :: Word8 -> Builder
word8HexFixed = P.primFixed P.word8HexFixed
-- | Encode a 'Word16' using 4 nibbles.
{-# INLINE word16HexFixed #-}
word16HexFixed :: Word16 -> Builder
word16HexFixed = P.primFixed P.word16HexFixed
-- | Encode a 'Word32' using 8 nibbles.
{-# INLINE word32HexFixed #-}
word32HexFixed :: Word32 -> Builder
word32HexFixed = P.primFixed P.word32HexFixed
-- | Encode a 'Word64' using 16 nibbles.
{-# INLINE word64HexFixed #-}
word64HexFixed :: Word64 -> Builder
word64HexFixed = P.primFixed P.word64HexFixed
-- | Encode an IEEE 'Float' using 8 nibbles.
{-# INLINE floatHexFixed #-}
floatHexFixed :: Float -> Builder
floatHexFixed = P.primFixed P.floatHexFixed
-- | Encode an IEEE 'Double' using 16 nibbles.
{-# INLINE doubleHexFixed #-}
doubleHexFixed :: Double -> Builder
doubleHexFixed = P.primFixed P.doubleHexFixed
-- | Encode each byte of a 'S.ByteString' using its fixed-width hex encoding.
{-# NOINLINE byteStringHex #-} -- share code
byteStringHex :: S.ByteString -> Builder
byteStringHex = P.primMapByteStringFixed P.word8HexFixed
-- | Encode each byte of a lazy 'L.ByteString' using its fixed-width hex encoding.
{-# NOINLINE lazyByteStringHex #-} -- share code
lazyByteStringHex :: L.ByteString -> Builder
lazyByteStringHex = P.primMapLazyByteStringFixed P.word8HexFixed
------------------------------------------------------------------------------
-- Fast decimal 'Integer' encoding.
------------------------------------------------------------------------------
#if defined(__GLASGOW_HASKELL__) && defined(INTEGER_GMP)
-- 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.
# define PAIR(a,b) (# a,b #)
-- | 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 = toInteger $ (10 :: Int) ^ caseWordSize_32_64 (9 :: Int) 18
-- | Decimal encoding of an 'Integer' using the ASCII digits.
integerDec :: Integer -> Builder
integerDec (S# i#) = intDec (I# i#)
integerDec i
| i < 0 = P.primFixed P.char8 '-' `mappend` go (-i)
| otherwise = go ( i)
where
errImpossible fun =
error $ "integerDec: " ++ fun ++ ": the impossible happened."
go :: Integer -> Builder
go n | n < maxPow10 = intDec (fromInteger n)
| otherwise =
case putH (splitf (maxPow10 * maxPow10) n) of
(x:xs) -> intDec x `mappend` P.primMapListBounded intDecPadded xs
[] -> errImpossible "integerDec: go"
splitf :: Integer -> Integer -> [Integer]
splitf pow10 n0
| pow10 > n0 = [n0]
| otherwise = splith (splitf (pow10 * pow10) n0)
where
splith [] = errImpossible "splith"
splith (n:ns) =
case n `quotRemInteger` pow10 of
PAIR(q,r) | q > 0 -> q : r : splitb ns
| otherwise -> r : splitb ns
splitb [] = []
splitb (n:ns) = case n `quotRemInteger` pow10 of
PAIR(q,r) -> q : r : splitb ns
putH :: [Integer] -> [Int]
putH [] = errImpossible "putH"
putH (n:ns) = case n `quotRemInteger` maxPow10 of
PAIR(x,y)
| q > 0 -> q : r : putB ns
| otherwise -> r : putB ns
where q = fromInteger x
r = fromInteger y
putB :: [Integer] -> [Int]
putB [] = []
putB (n:ns) = case n `quotRemInteger` maxPow10 of
PAIR(q,r) -> fromInteger q : fromInteger r : putB ns
foreign import ccall unsafe "static _hs_bytestring_int_dec_padded9"
c_int_dec_padded9 :: CInt -> Ptr Word8 -> IO ()
foreign import ccall unsafe "static _hs_bytestring_long_long_int_dec_padded18"
c_long_long_int_dec_padded18 :: CLLong -> Ptr Word8 -> IO ()
{-# INLINE intDecPadded #-}
intDecPadded :: P.BoundedPrim Int
intDecPadded = P.liftFixedToBounded $ caseWordSize_32_64
(P.fixedPrim 9 $ c_int_dec_padded9 . fromIntegral)
(P.fixedPrim 18 $ c_long_long_int_dec_padded18 . fromIntegral)
#else
-- compilers other than GHC
-- | Decimal encoding of an 'Integer' using the ASCII digits. Implemented
-- using via the 'Show' instance of 'Integer's.
integerDec :: Integer -> Builder
integerDec = string7 . show
#endif