module ByteString.StrictBuilder ( Builder, builderBytes, builderChunksBuilder, builderLength, builderPtrFiller, bytes, lazyBytes, asciiIntegral, asciiChar, utf8Char, storable, word8, word16BE, word32BE, word64BE, int8, int16BE, int32BE, int64BE, ) where import ByteString.StrictBuilder.Prelude import qualified ByteString.StrictBuilder.Population as A import qualified Data.ByteString as B import qualified Data.ByteString.Internal as C import qualified Data.ByteString.Lazy as F import qualified Data.ByteString.Builder.Internal as G import qualified ByteString.StrictBuilder.UTF8 as E data Builder = Builder !Int !A.Population instance Monoid Builder where {-# INLINE mempty #-} mempty = Builder 0 mempty {-# INLINE mappend #-} mappend (Builder leftSize leftPopulation) (Builder rightSize rightPopulation) = Builder (leftSize + rightSize) (leftPopulation <> rightPopulation) {-# INLINE mconcat #-} mconcat builders = Builder size population where size = foldl' (\acc (Builder x _) -> acc + x) 0 builders population = foldMap (\(Builder _ x) -> x) builders instance Semigroup Builder where (<>) = mappend instance IsString Builder where fromString = bytes . fromString instance Show Builder where show = show . builderBytes -- * ------------------------- {-| Efficiently constructs a strict bytestring. -} {-# INLINE builderBytes #-} builderBytes :: Builder -> ByteString builderBytes (Builder size population) = C.unsafeCreate size $ \ptr -> A.populationPtrUpdate population ptr $> () {-| Converts into the standard lazy bytestring builder. Does so efficiently using the internal APIs of \"bytestring\", without producing any intermediate representation. -} {-# INLINE builderChunksBuilder #-} builderChunksBuilder :: Builder -> G.Builder builderChunksBuilder (Builder size population) = G.ensureFree size <> A.populationChunksBuilder population {-| /O(1)/. Gets the size of the bytestring that is to be produced. -} {-# INLINE builderLength #-} builderLength :: Builder -> Int builderLength (Builder size population) = size {-| Use the builder to populate a buffer. It is your responsibility to ensure that the bounds are not exceeded. -} {-# INLINE builderPtrFiller #-} builderPtrFiller :: Builder -> (Int -> (Ptr Word8 -> IO ()) -> result) {-^ A continuation on the amount of bytes to be written and the action populating the pointer. -} -> result builderPtrFiller (Builder size (A.Population ptrUpdate)) cont = cont size (void . ptrUpdate) -- * Primitives ------------------------- {-# INLINE bytes #-} bytes :: ByteString -> Builder bytes bytes = Builder (B.length bytes) (A.bytes bytes) {-# INLINE lazyBytes #-} lazyBytes :: F.ByteString -> Builder lazyBytes = F.foldlChunks (\builder -> mappend builder . bytes) mempty {-# INLINE byte #-} byte :: Word8 -> Builder byte = word8 -- * Extras ------------------------- {-# INLINABLE asciiIntegral #-} asciiIntegral :: Integral a => a -> Builder asciiIntegral = \case 0 -> byte 48 x -> bool ((<>) (byte 45)) id (x >= 0) $ loop mempty $ abs x where loop builder remainder = case remainder of 0 -> builder _ -> case quotRem remainder 10 of (quot, rem) -> loop (byte (48 + fromIntegral rem) <> builder) quot {-# INLINE asciiChar #-} asciiChar :: Char -> Builder asciiChar = byte . fromIntegral . ord {-# INLINE CONLIKE storable #-} storable :: Storable a => a -> Builder storable value = Builder size (A.storable size value) where size = sizeOf value {-# INLINE word8 #-} word8 :: Word8 -> Builder word8 = Builder 1 . A.word8 {-# INLINE word16BE #-} word16BE :: Word16 -> Builder word16BE = Builder 2 . A.word16BE {-# INLINE word32BE #-} word32BE :: Word32 -> Builder word32BE = Builder 4 . A.word32BE {-# INLINE word64BE #-} word64BE :: Word64 -> Builder word64BE = Builder 8 . A.word64BE {-# INLINE int8 #-} int8 :: Int8 -> Builder int8 = Builder 1 . A.int8 {-# INLINE int16BE #-} int16BE :: Int16 -> Builder int16BE = Builder 2 . A.int16BE {-# INLINE int32BE #-} int32BE :: Int32 -> Builder int32BE = Builder 4 . A.int32BE {-# INLINE int64BE #-} int64BE :: Int64 -> Builder int64BE = Builder 8 . A.int64BE {-# INLINE utf8Char #-} utf8Char :: Char -> Builder utf8Char x = E.char x bytes_1 bytes_2 bytes_3 bytes_4 {-# INLINE bytes_1 #-} bytes_1 :: Word8 -> Builder bytes_1 b1 = Builder 1 (A.bytes_1 b1) {-# INLINE bytes_2 #-} bytes_2 :: Word8 -> Word8 -> Builder bytes_2 b1 b2 = Builder 2 (A.bytes_2 b1 b2) {-# INLINE bytes_3 #-} bytes_3 :: Word8 -> Word8 -> Word8 -> Builder bytes_3 b1 b2 b3 = Builder 3 (A.bytes_3 b1 b2 b3) {-# INLINE bytes_4 #-} bytes_4 :: Word8 -> Word8 -> Word8 -> Word8 -> Builder bytes_4 b1 b2 b3 b4 = Builder 4 (A.bytes_4 b1 b2 b3 b4)