{-# language BangPatterns #-}
{-# language DataKinds #-}
{-# language DuplicateRecordFields #-}
{-# language LambdaCase #-}
{-# language MagicHash #-}
{-# language NumericUnderscores #-}
{-# language RankNTypes #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language UnboxedTuples #-}
module Data.Bytes.Builder
(
Builder
, fromBounded
, run
, runOnto
, reversedOnto
, putMany
, putManyConsLength
, bytes
, copy
, copy2
, insert
, byteArray
, shortByteString
, shortTextUtf8
, shortTextJsonString
, cstring
, cstringLen
, stringUtf8
, word64Dec
, word32Dec
, word16Dec
, word8Dec
, wordDec
, naturalDec
, int64Dec
, int32Dec
, int16Dec
, int8Dec
, intDec
, integerDec
, word64PaddedUpperHex
, word32PaddedUpperHex
, word16PaddedUpperHex
, word16PaddedLowerHex
, word16LowerHex
, word16UpperHex
, word8PaddedUpperHex
, word8LowerHex
, ascii
, ascii2
, ascii3
, ascii4
, ascii5
, ascii6
, char
, word8
, word256BE
, word128BE
, word64BE
, word32BE
, word16BE
, int64BE
, int32BE
, int16BE
, word256LE
, word128LE
, word64LE
, word32LE
, word16LE
, int64LE
, int32LE
, int16LE
, intLEB128
, wordLEB128
, word64LEB128
, word8Array
, word16ArrayBE
, word32ArrayBE
, word64ArrayBE
, word128ArrayBE
, word256ArrayBE
, int64ArrayBE
, int32ArrayBE
, int16ArrayBE
, word16ArrayLE
, word32ArrayLE
, word64ArrayLE
, word128ArrayLE
, word256ArrayLE
, int64ArrayLE
, int32ArrayLE
, int16ArrayLE
, consLength
, consLength32LE
, consLength32BE
, consLength64BE
, doubleDec
, flush
) where
import Control.Exception (SomeException,toException)
import Control.Monad.ST (ST,runST)
import Control.Monad.IO.Class (MonadIO,liftIO)
import Data.Bits (unsafeShiftR,unsafeShiftL,xor,finiteBitSize)
import Data.Bytes.Builder.Unsafe (Builder(Builder),commitDistance1)
import Data.Bytes.Builder.Unsafe (BuilderState(BuilderState),pasteIO)
import Data.Bytes.Builder.Unsafe (Commits(Initial,Mutable,Immutable))
import Data.Bytes.Builder.Unsafe (reverseCommitsOntoChunks)
import Data.Bytes.Builder.Unsafe (commitsOntoChunks)
import Data.Bytes.Builder.Unsafe (stringUtf8,cstring,fromEffect)
import Data.Bytes.Builder.Unsafe (addCommitsLength,copyReverseCommits)
import Data.ByteString.Short.Internal (ShortByteString(SBS))
import Data.Bytes.Chunks (Chunks(ChunksNil))
import Data.Bytes.Types (Bytes(Bytes),MutableBytes(MutableBytes))
import Data.Char (ord)
import Data.Foldable (foldlM)
import Data.Int (Int64,Int32,Int16,Int8)
import Data.Primitive (ByteArray(..),MutableByteArray(..),PrimArray(..))
import Data.Text.Short (ShortText)
import Data.WideWord (Word128,Word256)
import Data.Word (Word64,Word32,Word16,Word8)
import Foreign.C.String (CStringLen)
import GHC.ByteOrder (ByteOrder(BigEndian,LittleEndian),targetByteOrder)
import GHC.Exts (Int(I#),Char(C#),Int#,State#,ByteArray#,(>=#))
import GHC.Exts (RealWorld,(+#),(-#),(<#))
import GHC.Exts ((*#))
import GHC.Integer.Logarithms.Compat (integerLog2#)
import GHC.IO (IO(IO),stToIO)
import GHC.Natural (naturalFromInteger,naturalToInteger)
import GHC.ST (ST(ST))
import GHC.Word (Word(W#),Word8(W8#))
import Numeric.Natural (Natural)
import qualified Arithmetic.Nat as Nat
import qualified Arithmetic.Types as Arithmetic
import qualified Data.Bytes.Builder.Bounded as Bounded
import qualified Data.Bytes.Builder.Bounded.Unsafe as UnsafeBounded
import qualified Data.Primitive as PM
import qualified Data.Text.Short as TS
import qualified GHC.Exts as Exts
run ::
Int
-> Builder
-> Chunks
run !hint bldr = runOnto hint bldr ChunksNil
runOnto ::
Int
-> Builder
-> Chunks
-> Chunks
runOnto hint@(I# hint# ) (Builder f) cs0 = runST $ do
MutableByteArray buf0 <- PM.newByteArray hint
cs <- ST $ \s0 -> case f buf0 0# hint# Initial s0 of
(# s1, bufX, offX, _, csX #) ->
(# s1, Mutable bufX offX csX #)
reverseCommitsOntoChunks cs0 cs
reversedOnto ::
Int
-> Builder
-> Chunks
-> Chunks
reversedOnto hint@(I# hint# ) (Builder f) cs0 = runST $ do
MutableByteArray buf0 <- PM.newByteArray hint
cs <- ST $ \s0 -> case f buf0 0# hint# Initial s0 of
(# s1, bufX, offX, _, csX #) ->
(# s1, Mutable bufX offX csX #)
commitsOntoChunks cs0 cs
putMany :: Foldable f
=> Int
-> (a -> Builder)
-> f a
-> (MutableBytes RealWorld -> IO b)
-> IO ()
{-# inline putMany #-}
putMany hint0 g xs cb = do
MutableByteArray buf0 <- PM.newByteArray hint
BuilderState bufZ offZ _ cmtsZ <- foldlM
(\st0 a -> do
st1@(BuilderState buf off _ cmts) <- pasteIO (g a) st0
case cmts of
Initial -> if I# off < threshold
then pure st1
else do
_ <- cb (MutableBytes (MutableByteArray buf) 0 (I# off))
pure (BuilderState buf0 0# hint# Initial)
_ -> do
let total = addCommitsLength (I# off) cmts
doff0 = total - I# off
large <- PM.newByteArray total
stToIO (PM.copyMutableByteArray large doff0 (MutableByteArray buf) 0 (I# off))
r <- stToIO (copyReverseCommits large doff0 cmts)
case r of
0 -> do
_ <- cb (MutableBytes large 0 total)
pure (BuilderState buf0 0# hint# Initial)
_ -> IO (\s0 -> Exts.raiseIO# putManyError s0)
) (BuilderState buf0 0# hint# Initial) xs
_ <- case cmtsZ of
Initial -> cb (MutableBytes (MutableByteArray bufZ) 0 (I# offZ))
_ -> IO (\s0 -> Exts.raiseIO# putManyError s0)
pure ()
where
!hint@(I# hint#) = max hint0 8
!threshold = div (hint * 3) 4
putManyError :: SomeException
{-# noinline putManyError #-}
putManyError = toException
(userError "bytebuild: putMany implementation error")
putManyConsLength :: (Foldable f, MonadIO m)
=> Arithmetic.Nat n
-> (Int -> Bounded.Builder n)
-> Int
-> (a -> Builder)
-> f a
-> (MutableBytes RealWorld -> m b)
-> m ()
{-# inline putManyConsLength #-}
putManyConsLength n buildSize hint g xs cb = do
let !(I# n# ) = Nat.demote n
let !(I# actual# ) = max hint (I# n# )
let !threshold = div (I# actual# * 3) 4
MutableByteArray buf0 <- liftIO (PM.newByteArray (I# actual# ))
BuilderState bufZ offZ _ cmtsZ <- foldlM
(\st0 a -> do
st1@(BuilderState buf off _ cmts) <- liftIO (pasteIO (g a) st0)
case cmts of
Initial -> if I# off < threshold
then pure st1
else do
let !dist = off -# n#
_ <- liftIO $ stToIO $ UnsafeBounded.pasteST
(buildSize (fromIntegral (I# dist)))
(MutableByteArray buf0) 0
_ <- cb (MutableBytes (MutableByteArray buf) 0 (I# off))
pure (BuilderState buf0 n# (actual# -# n# ) Initial)
_ -> do
let !dist = commitDistance1 buf0 n# buf off cmts
_ <- liftIO $ stToIO $ UnsafeBounded.pasteST
(buildSize (fromIntegral (I# dist)))
(MutableByteArray buf0) 0
let total = addCommitsLength (I# off) cmts
doff0 = total - I# off
large <- liftIO (PM.newByteArray total)
liftIO (stToIO (PM.copyMutableByteArray large doff0 (MutableByteArray buf) 0 (I# off)))
r <- liftIO (stToIO (copyReverseCommits large doff0 cmts))
case r of
0 -> do
_ <- cb (MutableBytes large 0 total)
pure (BuilderState buf0 n# (actual# -# n# ) Initial)
_ -> liftIO (IO (\s0 -> Exts.raiseIO# putManyError s0))
) (BuilderState buf0 n# (actual# -# n# ) Initial) xs
_ <- case cmtsZ of
Initial -> do
let !distZ = offZ -# n#
_ <- liftIO $ stToIO $ UnsafeBounded.pasteST
(buildSize (fromIntegral (I# distZ)))
(MutableByteArray buf0)
0
cb (MutableBytes (MutableByteArray bufZ) 0 (I# offZ))
_ -> liftIO (IO (\s0 -> Exts.raiseIO# putManyError s0))
pure ()
fromBounded ::
Arithmetic.Nat n
-> Bounded.Builder n
-> Builder
{-# inline fromBounded #-}
fromBounded n (UnsafeBounded.Builder f) = Builder $ \buf0 off0 len0 cs0 s0 ->
let !(I# req) = Nat.demote n
!(# s1, buf1, off1, len1, cs1 #) = case len0 >=# req of
1# -> (# s0, buf0, off0, len0, cs0 #)
_ -> let !(I# lenX) = max 4080 (I# req) in
case Exts.newByteArray# lenX s0 of
(# sX, bufX #) ->
(# sX, bufX, 0#, lenX, Mutable buf0 off0 cs0 #)
in case f buf1 off1 s1 of
(# s2, off2 #) -> (# s2, buf1, off2, len1 -# (off2 -# off1), cs1 #)
fromBoundedOne ::
Bounded.Builder 1
-> Builder
{-# inline fromBoundedOne #-}
fromBoundedOne (UnsafeBounded.Builder f) = Builder $ \buf0 off0 len0 cs0 s0 ->
let !(# s1, buf1, off1, len1, cs1 #) = case len0 of
0# -> case Exts.newByteArray# 4080# s0 of
(# sX, bufX #) ->
(# sX, bufX, 0#, 4080#, Mutable buf0 off0 cs0 #)
_ -> (# s0, buf0, off0, len0, cs0 #)
in case f buf1 off1 s1 of
(# s2, off2 #) -> (# s2, buf1, off2, len1 -# (off2 -# off1), cs1 #)
byteArray :: ByteArray -> Builder
byteArray a = bytes (Bytes a 0 (PM.sizeofByteArray a))
shortByteString :: ShortByteString -> Builder
shortByteString (SBS x) = bytes (Bytes a 0 (PM.sizeofByteArray a))
where a = ByteArray x
bytes :: Bytes -> Builder
bytes (Bytes (ByteArray src# ) (I# soff# ) (I# slen# )) = Builder
(\buf0 off0 len0 cs0 s0 -> case len0 <# slen# of
1# -> case slen# >=# 256# of
1# -> case Exts.newByteArray# 0# s0 of
(# s1, buf1 #) -> (# s1, buf1, 0#, 0#, Immutable src# soff# slen# (Mutable buf0 off0 cs0) #)
_ -> case Exts.newByteArray# 4080# s0 of
(# s1, buf1 #) -> case Exts.copyByteArray# src# soff# buf1 0# slen# s1 of
s2 -> (# s2, buf1, slen#, 4080# -# slen#, Mutable buf0 off0 cs0 #)
_ -> let !s1 = Exts.copyByteArray# src# soff# buf0 off0 slen# s0 in
(# s1, buf0, off0 +# slen#, len0 -# slen#, cs0 #)
)
copy :: Bytes -> Builder
copy (Bytes (ByteArray src# ) (I# soff# ) (I# slen# )) = Builder
(\buf0 off0 len0 cs0 s0 -> case len0 <# slen# of
1# -> case Exts.newByteArray# newSz s0 of
(# s1, buf1 #) -> case Exts.copyByteArray# src# soff# buf1 0# slen# s1 of
s2 -> (# s2, buf1, slen#, newSz -# slen#, Mutable buf0 off0 cs0 #)
_ -> let !s1 = Exts.copyByteArray# src# soff# buf0 off0 slen# s0 in
(# s1, buf0, off0 +# slen#, len0 -# slen#, cs0 #)
)
where
!(I# newSz) = max (I# slen#) 4080
cstringLen :: CStringLen -> Builder
cstringLen (Exts.Ptr src#, I# slen# ) = Builder
(\buf0 off0 len0 cs0 s0 -> case len0 <# slen# of
1# -> case Exts.newByteArray# newSz s0 of
(# s1, buf1 #) -> case Exts.copyAddrToByteArray# src# buf1 0# slen# s1 of
s2 -> (# s2, buf1, slen#, newSz -# slen#, Mutable buf0 off0 cs0 #)
_ -> let !s1 = Exts.copyAddrToByteArray# src# buf0 off0 slen# s0 in
(# s1, buf0, off0 +# slen#, len0 -# slen#, cs0 #)
)
where
!(I# newSz) = max (I# slen#) 4080
copy2 :: Bytes -> Bytes -> Builder
copy2 (Bytes (ByteArray srcA# ) (I# soffA# ) (I# slenA# ))
(Bytes (ByteArray srcB# ) (I# soffB# ) (I# slenB# )) = Builder
(\buf0 off0 len0 cs0 s0 -> case len0 <# slen# of
1# -> case Exts.newByteArray# newSz s0 of
(# s1, buf1 #) -> case Exts.copyByteArray# srcA# soffA# buf1 0# slenA# s1 of
s2 -> case Exts.copyByteArray# srcB# soffB# buf1 slenA# slenB# s2 of
s3 -> (# s3, buf1, slen#, newSz -# slen#, Mutable buf0 off0 cs0 #)
_ -> let !s1 = Exts.copyByteArray# srcA# soffA# buf0 off0 slenA# s0
!s2 = Exts.copyByteArray# srcB# soffB# buf0 (off0 +# slenA# ) slenB# s1 in
(# s2, buf0, off0 +# slen#, len0 -# slen#, cs0 #)
)
where
!slen# = slenA# +# slenB#
!(I# newSz) = max (I# slen#) 4080
insert :: Bytes -> Builder
insert (Bytes (ByteArray src# ) (I# soff# ) (I# slen# )) = Builder
(\buf0 off0 _ cs0 s0 -> case Exts.newByteArray# 0# s0 of
(# s1, buf1 #) ->
(# s1, buf1, 0#, 0#, Immutable src# soff# slen# (Mutable buf0 off0 cs0) #)
)
word8Array :: PrimArray Word8 -> Int -> Int -> Builder
word8Array (PrimArray arr) off len = bytes (Bytes (ByteArray arr) off len)
int64ArrayLE :: PrimArray Int64 -> Int -> Int -> Builder
int64ArrayLE (PrimArray x) = word64ArrayLE (PrimArray x)
int64ArrayBE :: PrimArray Int64 -> Int -> Int -> Builder
int64ArrayBE (PrimArray x) = word64ArrayBE (PrimArray x)
int32ArrayLE :: PrimArray Int32 -> Int -> Int -> Builder
int32ArrayLE (PrimArray x) = word32ArrayLE (PrimArray x)
int32ArrayBE :: PrimArray Int32 -> Int -> Int -> Builder
int32ArrayBE (PrimArray x) = word32ArrayBE (PrimArray x)
int16ArrayLE :: PrimArray Int16 -> Int -> Int -> Builder
int16ArrayLE (PrimArray x) = word16ArrayLE (PrimArray x)
int16ArrayBE :: PrimArray Int16 -> Int -> Int -> Builder
int16ArrayBE (PrimArray x) = word16ArrayBE (PrimArray x)
word128ArrayLE :: PrimArray Word128 -> Int -> Int -> Builder
word128ArrayLE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of
LittleEndian -> bytes (Bytes (ByteArray arr) (soff0 * 16) (slen0 * 16))
BigEndian -> word128ArraySwap src soff0 slen0
word128ArrayBE :: PrimArray Word128 -> Int -> Int -> Builder
word128ArrayBE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of
BigEndian -> bytes (Bytes (ByteArray arr) (soff0 * 16) (slen0 * 16))
LittleEndian -> word128ArraySwap src soff0 slen0
word256ArrayLE :: PrimArray Word256 -> Int -> Int -> Builder
word256ArrayLE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of
LittleEndian -> bytes (Bytes (ByteArray arr) (soff0 * 32) (slen0 * 32))
BigEndian -> word256ArraySwap src soff0 slen0
word256ArrayBE :: PrimArray Word256 -> Int -> Int -> Builder
word256ArrayBE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of
BigEndian -> bytes (Bytes (ByteArray arr) (soff0 * 32) (slen0 * 32))
LittleEndian -> word256ArraySwap src soff0 slen0
word64ArrayLE :: PrimArray Word64 -> Int -> Int -> Builder
word64ArrayLE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of
LittleEndian -> bytes (Bytes (ByteArray arr) (soff0 * 8) (slen0 * 8))
BigEndian -> word64ArraySwap src soff0 slen0
word64ArrayBE :: PrimArray Word64 -> Int -> Int -> Builder
word64ArrayBE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of
BigEndian -> bytes (Bytes (ByteArray arr) (soff0 * 8) (slen0 * 8))
LittleEndian -> word64ArraySwap src soff0 slen0
word32ArrayLE :: PrimArray Word32 -> Int -> Int -> Builder
word32ArrayLE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of
LittleEndian -> bytes (Bytes (ByteArray arr) (soff0 * 4) (slen0 * 4))
BigEndian -> word32ArraySwap src soff0 slen0
word32ArrayBE :: PrimArray Word32 -> Int -> Int -> Builder
word32ArrayBE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of
BigEndian -> bytes (Bytes (ByteArray arr) (soff0 * 4) (slen0 * 4))
LittleEndian -> word32ArraySwap src soff0 slen0
word16ArrayLE :: PrimArray Word16 -> Int -> Int -> Builder
word16ArrayLE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of
LittleEndian -> bytes (Bytes (ByteArray arr) (soff0 * 2) (slen0 * 2))
BigEndian -> word16ArraySwap src soff0 slen0
word16ArrayBE :: PrimArray Word16 -> Int -> Int -> Builder
word16ArrayBE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of
BigEndian -> bytes (Bytes (ByteArray arr) (soff0 * 2) (slen0 * 2))
LittleEndian -> word16ArraySwap src soff0 slen0
word16ArraySwap :: PrimArray Word16 -> Int -> Int -> Builder
word16ArraySwap src soff0 slen0 =
fromFunction (slen0 * 2) (go (soff0 * 2) ((soff0 + slen0) * 2))
where
go :: Int -> Int -> MutableByteArray s -> Int -> ST s Int
go !soff !send !dst !doff = if soff < send
then do
let v0 = PM.indexPrimArray (asWord8s src) soff
v1 = PM.indexPrimArray (asWord8s src) (soff + 1)
PM.writeByteArray dst doff v1
PM.writeByteArray dst (doff + 1) v0
go (soff + 2) send dst (doff + 2)
else pure doff
word32ArraySwap :: PrimArray Word32 -> Int -> Int -> Builder
word32ArraySwap src soff0 slen0 =
fromFunction (slen0 * 4) (go (soff0 * 4) ((soff0 + slen0) * 4))
where
go :: Int -> Int -> MutableByteArray s -> Int -> ST s Int
go !soff !send !dst !doff = if soff < send
then do
let v0 = PM.indexPrimArray (asWord8s src) soff
v1 = PM.indexPrimArray (asWord8s src) (soff + 1)
v2 = PM.indexPrimArray (asWord8s src) (soff + 2)
v3 = PM.indexPrimArray (asWord8s src) (soff + 3)
PM.writeByteArray dst doff v3
PM.writeByteArray dst (doff + 1) v2
PM.writeByteArray dst (doff + 2) v1
PM.writeByteArray dst (doff + 3) v0
go (soff + 4) send dst (doff + 4)
else pure doff
word64ArraySwap :: PrimArray Word64 -> Int -> Int -> Builder
word64ArraySwap src soff0 slen0 =
fromFunction (slen0 * 8) (go (soff0 * 8) ((soff0 + slen0) * 8))
where
go :: Int -> Int -> MutableByteArray s -> Int -> ST s Int
go !soff !send !dst !doff = if soff < send
then do
let v0 = PM.indexPrimArray (asWord8s src) soff
v1 = PM.indexPrimArray (asWord8s src) (soff + 1)
v2 = PM.indexPrimArray (asWord8s src) (soff + 2)
v3 = PM.indexPrimArray (asWord8s src) (soff + 3)
v4 = PM.indexPrimArray (asWord8s src) (soff + 4)
v5 = PM.indexPrimArray (asWord8s src) (soff + 5)
v6 = PM.indexPrimArray (asWord8s src) (soff + 6)
v7 = PM.indexPrimArray (asWord8s src) (soff + 7)
PM.writeByteArray dst doff v7
PM.writeByteArray dst (doff + 1) v6
PM.writeByteArray dst (doff + 2) v5
PM.writeByteArray dst (doff + 3) v4
PM.writeByteArray dst (doff + 4) v3
PM.writeByteArray dst (doff + 5) v2
PM.writeByteArray dst (doff + 6) v1
PM.writeByteArray dst (doff + 7) v0
go (soff + 8) send dst (doff + 8)
else pure doff
word128ArraySwap :: PrimArray Word128 -> Int -> Int -> Builder
word128ArraySwap src soff0 slen0 =
fromFunction (slen0 * 16) (go (soff0 * 16) ((soff0 + slen0) * 16))
where
go :: Int -> Int -> MutableByteArray s -> Int -> ST s Int
go !soff !send !dst !doff = if soff < send
then do
let v0 = PM.indexPrimArray (asWord8s src) soff
v1 = PM.indexPrimArray (asWord8s src) (soff + 1)
v2 = PM.indexPrimArray (asWord8s src) (soff + 2)
v3 = PM.indexPrimArray (asWord8s src) (soff + 3)
v4 = PM.indexPrimArray (asWord8s src) (soff + 4)
v5 = PM.indexPrimArray (asWord8s src) (soff + 5)
v6 = PM.indexPrimArray (asWord8s src) (soff + 6)
v7 = PM.indexPrimArray (asWord8s src) (soff + 7)
v8 = PM.indexPrimArray (asWord8s src) (soff + 8)
v9 = PM.indexPrimArray (asWord8s src) (soff + 9)
v10 = PM.indexPrimArray (asWord8s src) (soff + 10)
v11 = PM.indexPrimArray (asWord8s src) (soff + 11)
v12 = PM.indexPrimArray (asWord8s src) (soff + 12)
v13 = PM.indexPrimArray (asWord8s src) (soff + 13)
v14 = PM.indexPrimArray (asWord8s src) (soff + 14)
v15 = PM.indexPrimArray (asWord8s src) (soff + 15)
PM.writeByteArray dst doff v15
PM.writeByteArray dst (doff + 1) v14
PM.writeByteArray dst (doff + 2) v13
PM.writeByteArray dst (doff + 3) v12
PM.writeByteArray dst (doff + 4) v11
PM.writeByteArray dst (doff + 5) v10
PM.writeByteArray dst (doff + 6) v9
PM.writeByteArray dst (doff + 7) v8
PM.writeByteArray dst (doff + 8) v7
PM.writeByteArray dst (doff + 9) v6
PM.writeByteArray dst (doff + 10) v5
PM.writeByteArray dst (doff + 11) v4
PM.writeByteArray dst (doff + 12) v3
PM.writeByteArray dst (doff + 13) v2
PM.writeByteArray dst (doff + 14) v1
PM.writeByteArray dst (doff + 15) v0
go (soff + 16) send dst (doff + 16)
else pure doff
word256ArraySwap :: PrimArray Word256 -> Int -> Int -> Builder
word256ArraySwap src soff0 slen0 =
fromFunction (slen0 * 32) (go (soff0 * 32) ((soff0 + slen0) * 32))
where
go :: Int -> Int -> MutableByteArray s -> Int -> ST s Int
go !soff !send !dst !doff = if soff < send
then do
let loop !i
| i < 32 = do
let v = PM.indexPrimArray (asWord8s src) (soff + i)
PM.writeByteArray dst (doff + (31 - i)) v
loop (i + 1)
| otherwise = pure ()
loop 0
go (soff + 32) send dst (doff + 32)
else pure doff
asWord8s :: PrimArray a -> PrimArray Word8
asWord8s (PrimArray x) = PrimArray x
slicedUtf8TextJson :: ByteArray# -> Int# -> Int# -> Builder
{-# noinline slicedUtf8TextJson #-}
slicedUtf8TextJson !src# !soff0# !slen0# = fromFunction reqLen $ \dst doff0 -> do
PM.writeByteArray dst doff0 (c2w '"')
let go !soff !slen !doff = if slen > 0
then case indexChar8Array (ByteArray src#) soff of
'\\' -> write2 dst doff '\\' '\\' *> go (soff + 1) (slen - 1) (doff + 2)
'\"' -> write2 dst doff '\\' '\"' *> go (soff + 1) (slen - 1) (doff + 2)
'\n' -> write2 dst doff '\\' 'n' *> go (soff + 1) (slen - 1) (doff + 2)
'\r' -> write2 dst doff '\\' 'r' *> go (soff + 1) (slen - 1) (doff + 2)
'\t' -> write2 dst doff '\\' 't' *> go (soff + 1) (slen - 1) (doff + 2)
c -> if c >= '\x20'
then PM.writeByteArray dst doff (c2w c) *> go (soff + 1) (slen - 1) (doff + 1)
else do
write2 dst doff '\\' 'u'
doff' <- UnsafeBounded.pasteST
(Bounded.word16PaddedUpperHex (fromIntegral (c2w c)))
dst (doff + 2)
go (soff + 1) (slen - 1) doff'
else pure doff
doffRes <- go (I# soff0#) (I# slen0#) (doff0 + 1)
PM.writeByteArray dst doffRes (c2w '"')
pure (doffRes + 1)
where
slen0 = I# slen0#
reqLen = (2 * slen0) + 2
fromFunction :: Int -> (forall s. MutableByteArray s -> Int -> ST s Int) -> Builder
fromFunction (I# req) f = Builder $ \buf0 off0 len0 cs0 s0 ->
let !(# s1, buf1, off1, len1, cs1 #) = case len0 >=# req of
1# -> (# s0, buf0, off0, len0, cs0 #)
_ -> let !(I# lenX) = max 4080 (I# req) in
case Exts.newByteArray# lenX s0 of
(# sX, bufX #) ->
(# sX, bufX, 0#, lenX, Mutable buf0 off0 cs0 #)
in case unST (f (MutableByteArray buf1) (I# off1)) s1 of
(# s2, I# off2 #) -> (# s2, buf1, off2, len1 -# (off2 -# off1), cs1 #)
write2 :: MutableByteArray s -> Int -> Char -> Char -> ST s ()
write2 marr ix a b = do
PM.writeByteArray marr ix (c2w a)
PM.writeByteArray marr (ix + 1) (c2w b)
shortTextUtf8 :: ShortText -> Builder
shortTextUtf8 a =
let ba = shortTextToByteArray a
in bytes (Bytes ba 0 (PM.sizeofByteArray ba))
shortTextJsonString :: ShortText -> Builder
shortTextJsonString a =
let !(ByteArray ba) = shortTextToByteArray a
!(I# len) = PM.sizeofByteArray (ByteArray ba)
in slicedUtf8TextJson ba 0# len
word64Dec :: Word64 -> Builder
word64Dec w = fromBounded Nat.constant (Bounded.word64Dec w)
word32Dec :: Word32 -> Builder
word32Dec w = fromBounded Nat.constant (Bounded.word32Dec w)
word16Dec :: Word16 -> Builder
word16Dec w = fromBounded Nat.constant (Bounded.word16Dec w)
word8Dec :: Word8 -> Builder
word8Dec w = fromBounded Nat.constant (Bounded.word8Dec w)
wordDec :: Word -> Builder
wordDec w = fromBounded Nat.constant (Bounded.wordDec w)
doubleDec :: Double -> Builder
doubleDec w = fromBounded Nat.constant (Bounded.doubleDec w)
int64Dec :: Int64 -> Builder
int64Dec w = fromBounded Nat.constant (Bounded.int64Dec w)
int32Dec :: Int32 -> Builder
int32Dec w = fromBounded Nat.constant (Bounded.int32Dec w)
int16Dec :: Int16 -> Builder
int16Dec w = fromBounded Nat.constant (Bounded.int16Dec w)
int8Dec :: Int8 -> Builder
int8Dec w = fromBounded Nat.constant (Bounded.int8Dec w)
intDec :: Int -> Builder
intDec w = fromBounded Nat.constant (Bounded.intDec w)
word64PaddedUpperHex :: Word64 -> Builder
word64PaddedUpperHex w =
fromBounded Nat.constant (Bounded.word64PaddedUpperHex w)
word32PaddedUpperHex :: Word32 -> Builder
word32PaddedUpperHex w =
fromBounded Nat.constant (Bounded.word32PaddedUpperHex w)
word16PaddedUpperHex :: Word16 -> Builder
word16PaddedUpperHex w =
fromBounded Nat.constant (Bounded.word16PaddedUpperHex w)
word16PaddedLowerHex :: Word16 -> Builder
word16PaddedLowerHex w =
fromBounded Nat.constant (Bounded.word16PaddedLowerHex w)
word16LowerHex :: Word16 -> Builder
word16LowerHex w =
fromBounded Nat.constant (Bounded.word16LowerHex w)
word16UpperHex :: Word16 -> Builder
word16UpperHex w =
fromBounded Nat.constant (Bounded.word16UpperHex w)
word8LowerHex :: Word8 -> Builder
word8LowerHex w =
fromBounded Nat.constant (Bounded.word8LowerHex w)
word8PaddedUpperHex :: Word8 -> Builder
word8PaddedUpperHex w =
fromBounded Nat.constant (Bounded.word8PaddedUpperHex w)
ascii :: Char -> Builder
ascii c = fromBoundedOne (Bounded.ascii c)
ascii2 :: Char -> Char -> Builder
ascii2 a b = fromBounded Nat.constant (Bounded.ascii2 a b)
ascii3 :: Char -> Char -> Char -> Builder
ascii3 a b c = fromBounded Nat.constant (Bounded.ascii3 a b c)
ascii4 :: Char -> Char -> Char -> Char -> Builder
ascii4 a b c d = fromBounded Nat.constant (Bounded.ascii4 a b c d)
ascii5 :: Char -> Char -> Char -> Char -> Char -> Builder
ascii5 a b c d e = fromBounded Nat.constant (Bounded.ascii5 a b c d e)
ascii6 :: Char -> Char -> Char -> Char -> Char -> Char -> Builder
ascii6 a b c d e f = fromBounded Nat.constant (Bounded.ascii6 a b c d e f)
char :: Char -> Builder
char c = fromBounded Nat.constant (Bounded.char c)
unST :: ST s a -> State# s -> (# State# s, a #)
unST (ST f) = f
int64LE :: Int64 -> Builder
int64LE w = fromBounded Nat.constant (Bounded.int64LE w)
int32LE :: Int32 -> Builder
int32LE w = fromBounded Nat.constant (Bounded.int32LE w)
int16LE :: Int16 -> Builder
int16LE w = fromBounded Nat.constant (Bounded.int16LE w)
int64BE :: Int64 -> Builder
int64BE w = fromBounded Nat.constant (Bounded.int64BE w)
int32BE :: Int32 -> Builder
int32BE w = fromBounded Nat.constant (Bounded.int32BE w)
int16BE :: Int16 -> Builder
int16BE w = fromBounded Nat.constant (Bounded.int16BE w)
word256LE :: Word256 -> Builder
word256LE w = fromBounded Nat.constant (Bounded.word256LE w)
word128LE :: Word128 -> Builder
word128LE w = fromBounded Nat.constant (Bounded.word128LE w)
word64LE :: Word64 -> Builder
word64LE w = fromBounded Nat.constant (Bounded.word64LE w)
word32LE :: Word32 -> Builder
word32LE w = fromBounded Nat.constant (Bounded.word32LE w)
word16LE :: Word16 -> Builder
word16LE w = fromBounded Nat.constant (Bounded.word16LE w)
word256BE :: Word256 -> Builder
word256BE w = fromBounded Nat.constant (Bounded.word256BE w)
word128BE :: Word128 -> Builder
word128BE w = fromBounded Nat.constant (Bounded.word128BE w)
word64BE :: Word64 -> Builder
word64BE w = fromBounded Nat.constant (Bounded.word64BE w)
word32BE :: Word32 -> Builder
word32BE w = fromBounded Nat.constant (Bounded.word32BE w)
word16BE :: Word16 -> Builder
word16BE w = fromBounded Nat.constant (Bounded.word16BE w)
word8 :: Word8 -> Builder
word8 w = fromBoundedOne (Bounded.word8 w)
consLength ::
Arithmetic.Nat n
-> (Int -> Bounded.Builder n)
-> Builder
-> Builder
{-# inline consLength #-}
consLength !n buildSize (Builder f) = Builder $ \buf0 off0 len0 cs0 s0 ->
let !(I# lenSz) = Nat.demote n
!(# s1, buf1, off1, len1, cs1 #) = case len0 >=# lenSz of
1# -> (# s0, buf0, off0, len0, cs0 #)
_ -> case Exts.newByteArray# 4080# s0 of
(# sX, bufX #) ->
(# sX, bufX, 0#, 4080#, Mutable buf0 off0 cs0 #)
in case f buf1 (off1 +# lenSz) (len1 -# lenSz) cs1 s1 of
(# s2, buf2, off2, len2, cs2 #) ->
let !dist = commitDistance1 buf1 (off1 +# lenSz) buf2 off2 cs2
ST g = UnsafeBounded.pasteST
(buildSize (fromIntegral (I# dist)))
(MutableByteArray buf1)
(I# off1)
in case g s2 of
(# s3, _ #) -> (# s3, buf2, off2, len2, cs2 #)
consLength32LE :: Builder -> Builder
consLength32LE = consLength Nat.constant (\x -> Bounded.word32LE (fromIntegral x))
consLength32BE :: Builder -> Builder
consLength32BE = consLength Nat.constant (\x -> Bounded.word32BE (fromIntegral x))
consLength64BE :: Builder -> Builder
consLength64BE = consLength Nat.constant (\x -> Bounded.word64BE (fromIntegral x))
flush :: Int -> Builder
flush !reqSz = Builder $ \buf0 off0 _ cs0 s0 ->
case Exts.newByteArray# sz# s0 of
(# sX, bufX #) ->
(# sX, bufX, 0#, sz#, Mutable buf0 off0 cs0 #)
where
!(I# sz# ) = max reqSz 0
shortTextToByteArray :: ShortText -> ByteArray
shortTextToByteArray x = case TS.toShortByteString x of
SBS a -> ByteArray a
indexChar8Array :: ByteArray -> Int -> Char
indexChar8Array (ByteArray b) (I# i) = C# (Exts.indexCharArray# b i)
c2w :: Char -> Word8
c2w = fromIntegral . ord
zigZagNative :: Int -> Word
zigZagNative s = fromIntegral @Int @Word
((unsafeShiftL s 1) `xor` (unsafeShiftR s (finiteBitSize (undefined :: Word) - 1)))
intLEB128 :: Int -> Builder
intLEB128 = wordLEB128 . zigZagNative
wordLEB128 :: Word -> Builder
wordLEB128 w = fromBounded Nat.constant (Bounded.wordLEB128 w)
word64LEB128 :: Word64 -> Builder
word64LEB128 w = fromBounded Nat.constant (Bounded.word64LEB128 w)
integerDec :: Integer -> Builder
integerDec !i
| i < 0 = ascii '-' <> naturalDec (naturalFromInteger (negate i))
| otherwise = naturalDec (naturalFromInteger i)
naturalDec :: Natural -> Builder
naturalDec !n0 = fromEffect
(I# (11# +# (3# *# integerLog2# (naturalToInteger n0))))
(\marr off -> case n0 of
0 -> do
PM.writeByteArray marr off (0x30 :: Word8)
pure (off + 1)
_ -> go n0 marr off off
)
where
go :: forall s. Natural -> MutableByteArray s -> Int -> Int -> ST s Int
go !n !buf !off0 !off = case quotRem n 1_000_000_000 of
(q,r) -> case q of
0 -> do
off' <- backwardsWordLoop buf off (fromIntegral @Natural @Word r)
reverseBytes buf off0 (off' - 1)
pure off'
_ -> do
off' <- backwardsPasteWordPaddedDec9
(fromIntegral @Natural @Word r) buf off
go q buf off0 off'
reverseBytes :: MutableByteArray s -> Int -> Int -> ST s ()
{-# inline reverseBytes #-}
reverseBytes arr begin end = go begin end where
go ixA ixB = if ixA < ixB
then do
a :: Word8 <- PM.readByteArray arr ixA
b :: Word8 <- PM.readByteArray arr ixB
PM.writeByteArray arr ixA b
PM.writeByteArray arr ixB a
go (ixA + 1) (ixB - 1)
else pure ()
backwardsPasteWordPaddedDec9 ::
Word -> MutableByteArray s -> Int -> ST s Int
backwardsPasteWordPaddedDec9 !w !arr !off = do
backwardsPutRem10
(backwardsPutRem10 $ backwardsPutRem10 $ backwardsPutRem10 $
backwardsPutRem10 $ backwardsPutRem10 $ backwardsPutRem10 $
backwardsPutRem10 $ backwardsPutRem10
(\_ _ _ -> pure ())
) arr off w
pure (off + 9)
backwardsPutRem10 ::
(MutableByteArray s -> Int -> Word -> ST s a)
-> MutableByteArray s -> Int -> Word -> ST s a
{-# inline backwardsPutRem10 #-}
backwardsPutRem10 andThen arr off dividend = do
let quotient = approxDiv10 dividend
remainder = dividend - (10 * quotient)
PM.writeByteArray arr off (unsafeWordToWord8 (remainder + 48))
andThen arr (off + 1) quotient
backwardsWordLoop :: MutableByteArray s -> Int -> Word -> ST s Int
{-# inline backwardsWordLoop #-}
backwardsWordLoop arr off0 x0 = go off0 x0 where
go !off !(x :: Word) = if x > 0
then do
let (y,z) = quotRem x 10
PM.writeByteArray arr off (fromIntegral (z + 0x30) :: Word8)
go (off + 1) y
else pure off
approxDiv10 :: Word -> Word
approxDiv10 !n = unsafeShiftR (0x1999999A * n) 32
unsafeWordToWord8 :: Word -> Word8
unsafeWordToWord8 (W# w) = W8# w