2019-06-17 13:24:24 -04:00
|
|
|
{-# language ScopedTypeVariables #-}
|
|
|
|
{-# language BangPatterns #-}
|
|
|
|
{-# language MagicHash #-}
|
|
|
|
{-# language UnboxedTuples #-}
|
|
|
|
{-# language RankNTypes #-}
|
|
|
|
{-# language LambdaCase #-}
|
|
|
|
|
|
|
|
module Data.ByteArray.Builder.Small
|
|
|
|
( -- * Unsafe Primitives
|
|
|
|
Builder(..)
|
|
|
|
, construct
|
|
|
|
-- * Evaluation
|
|
|
|
, run
|
|
|
|
, pasteST
|
2019-06-17 13:56:14 -04:00
|
|
|
, pasteIO
|
|
|
|
-- * Materialized Byte Sequences
|
|
|
|
, bytes
|
|
|
|
, bytearray
|
2019-06-17 13:24:24 -04:00
|
|
|
-- * Numbers
|
|
|
|
, word64Dec
|
|
|
|
) where
|
|
|
|
|
|
|
|
import Control.Monad.Primitive
|
2019-06-17 13:56:14 -04:00
|
|
|
import Control.Monad.ST
|
|
|
|
import Data.Bytes.Types
|
2019-06-17 13:24:24 -04:00
|
|
|
import Data.Char (ord)
|
|
|
|
import Data.Primitive
|
|
|
|
import GHC.Exts
|
|
|
|
import GHC.ST
|
2019-06-17 13:56:14 -04:00
|
|
|
import GHC.Word
|
2019-06-17 13:24:24 -04:00
|
|
|
|
|
|
|
-- | An unmaterialized sequence of bytes that may be pasted
|
|
|
|
-- into a mutable byte array.
|
|
|
|
newtype Builder = Builder
|
|
|
|
(forall s. MutableByteArray# s -> Int# -> Int# -> State# s -> (# State# s, Int# #))
|
|
|
|
|
|
|
|
instance Semigroup Builder where
|
|
|
|
{-# inline (<>) #-}
|
|
|
|
Builder f <> Builder g = Builder $ \arr off0 len0 s0 -> case f arr off0 len0 s0 of
|
|
|
|
(# s1, r #) -> case r /=# (-1#) of
|
|
|
|
1# -> g arr r (len0 +# (off0 -# r)) s1
|
|
|
|
_ -> (# s1, (-1#) #)
|
|
|
|
|
|
|
|
-- | Run a builder. An accurate size hint is important for good performance.
|
|
|
|
run ::
|
|
|
|
Int -- ^ Hint for upper bound on size
|
|
|
|
-> Builder -- ^ Builder
|
|
|
|
-> ByteArray
|
|
|
|
run hint b = runST $ do
|
|
|
|
let go !n = do
|
|
|
|
arr <- newByteArray n
|
|
|
|
pasteST b (MutableBytes arr 0 n) >>= \case
|
|
|
|
Nothing -> go (n + 64)
|
|
|
|
Just len -> do
|
|
|
|
shrinkMutableByteArray arr len
|
|
|
|
unsafeFreezeByteArray arr
|
|
|
|
go hint
|
|
|
|
|
|
|
|
pasteST :: Builder -> MutableBytes s -> ST s (Maybe Int)
|
|
|
|
{-# inline pasteST #-}
|
|
|
|
pasteST (Builder f) (MutableBytes (MutableByteArray arr) (I# off) (I# len)) =
|
|
|
|
ST $ \s0 -> case f arr off len s0 of
|
|
|
|
(# s1, r #) -> if isTrue# (r /=# (-1#))
|
|
|
|
then (# s1, Just (I# r) #)
|
|
|
|
else (# s1, Nothing #)
|
|
|
|
|
2019-06-17 13:56:14 -04:00
|
|
|
pasteIO :: Builder -> MutableBytes RealWorld -> IO (Maybe Int)
|
|
|
|
{-# inline pasteIO #-}
|
|
|
|
pasteIO b m = stToIO (pasteST b m)
|
|
|
|
|
2019-06-17 13:24:24 -04:00
|
|
|
construct :: (forall s. MutableBytes s -> ST s (Maybe Int)) -> Builder
|
|
|
|
construct f = Builder
|
|
|
|
$ \arr off len s0 ->
|
|
|
|
case unST (f (MutableBytes (MutableByteArray arr) (I# off) (I# len))) s0 of
|
|
|
|
(# s1, m #) -> case m of
|
|
|
|
Nothing -> (# s1, (-1#) #)
|
|
|
|
Just (I# n) -> (# s1, n #)
|
|
|
|
|
2019-06-17 13:56:14 -04:00
|
|
|
bytearray :: ByteArray -> Builder
|
|
|
|
bytearray a = bytes (Bytes a 0 (sizeofByteArray a))
|
|
|
|
|
|
|
|
bytes :: Bytes -> Builder
|
|
|
|
bytes (Bytes src soff slen) = construct $ \(MutableBytes arr off len) -> if len >= slen
|
|
|
|
then do
|
|
|
|
copyByteArray arr off src soff slen
|
|
|
|
pure (Just (len - slen))
|
|
|
|
else pure Nothing
|
|
|
|
|
2019-06-17 13:24:24 -04:00
|
|
|
word64Dec :: Word64 -> Builder
|
|
|
|
word64Dec (W64# w) = word64Dec# w
|
|
|
|
|
|
|
|
word64Dec# :: Word# -> Builder
|
|
|
|
{-# noinline word64Dec# #-}
|
|
|
|
word64Dec# w# = construct $ \(MutableBytes arr off0 len) -> if len >= 19
|
|
|
|
then if w /= 0
|
|
|
|
then do
|
|
|
|
let go off x = if x > 0
|
|
|
|
then do
|
|
|
|
let (y,z) = quotRem x 10
|
|
|
|
writeByteArray arr off (fromIntegral (z + 0x30) :: Word8)
|
|
|
|
go (off + 1) y
|
|
|
|
else do
|
|
|
|
reverseBytes arr off0 (off - 1)
|
|
|
|
pure (Just off)
|
|
|
|
go off0 w
|
|
|
|
else do
|
|
|
|
writeByteArray arr off0 (c2w '0')
|
|
|
|
pure (Just (off0 + 1))
|
|
|
|
else pure Nothing
|
|
|
|
where
|
|
|
|
w = W64# w#
|
|
|
|
|
|
|
|
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 <- readByteArray arr ixA
|
|
|
|
b :: Word8 <- readByteArray arr ixB
|
|
|
|
writeByteArray arr ixA b
|
|
|
|
writeByteArray arr ixB a
|
|
|
|
go (ixA + 1) (ixB - 1)
|
|
|
|
else pure ()
|
|
|
|
|
|
|
|
c2w :: Char -> Word8
|
|
|
|
c2w = fromIntegral . ord
|
|
|
|
|
|
|
|
unST :: ST s a -> State# s -> (# State# s, a #)
|
|
|
|
unST (ST f) = f
|
|
|
|
|
|
|
|
shrinkMutableByteArray :: MutableByteArray s -> Int -> ST s ()
|
|
|
|
shrinkMutableByteArray (MutableByteArray arr) (I# sz) =
|
|
|
|
primitive_ (shrinkMutableByteArray# arr sz)
|
|
|
|
|