module ByteString.TreeBuilder
(
Builder,
byteString,
byte,
asciiIntegral,
asciiChar,
utf8Char,
utf8Ord,
utf8Text,
utf8LazyText,
intercalate,
length,
toByteString,
toLazyByteString,
)
where
import ByteString.TreeBuilder.Prelude hiding (foldl, foldr, length, intercalate)
import qualified ByteString.TreeBuilder.Tree as A
import qualified ByteString.TreeBuilder.Poker as D
import qualified ByteString.TreeBuilder.Prelude as F
import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as C
import qualified Data.ByteString.Lazy.Internal as E
import qualified Data.Text
import qualified Data.Text.Lazy
data Builder =
Builder !Int !A.Tree
instance Monoid Builder where
{-# INLINE mempty #-}
mempty =
Builder 0 A.Empty
{-# INLINABLE mappend #-}
mappend (Builder length1 tree1) (Builder length2 tree2) =
Builder (length1 + length2) (A.Branch tree1 tree2)
{-# INLINE mconcat #-}
mconcat =
foldl' mappend mempty
instance Semigroup Builder where
{-# INLINE sconcat #-}
sconcat =
foldl' mappend mempty
{-# INLINABLE (<>) #-}
(<>) = mappend
instance IsString Builder where
{-# INLINE fromString #-}
fromString string =
Builder (B.length bytes) (A.Leaf bytes)
where
bytes =
fromString string
{-# INLINE byteString #-}
byteString :: ByteString -> Builder
byteString bytes =
Builder (B.length bytes) (A.Leaf bytes)
{-# INLINE byte #-}
byte :: Word8 -> Builder
byte byte =
Builder 1 (A.Leaf (B.singleton byte))
{-# 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 utf8Char #-}
utf8Char :: Char -> Builder
utf8Char =
utf8Ord . ord
{-# INLINE utf8Ord #-}
utf8Ord :: Int -> Builder
utf8Ord x =
if x <= 0x7F
then
byte (fromIntegral x)
else
if x <= 0x07FF
then
byte (fromIntegral ((x `shiftR` 6) + 0xC0)) <>
byte (fromIntegral ((x .&. 0x3F) + 0x80))
else
if x <= 0xFFFF
then
byte (fromIntegral (x `shiftR` 12) + 0xE0) <>
byte (fromIntegral ((x `shiftR` 6) .&. 0x3F) + 0x80) <>
byte (fromIntegral (x .&. 0x3F) + 0x80)
else
byte (fromIntegral (x `shiftR` 18) + 0xF0) <>
byte (fromIntegral ((x `shiftR` 12) .&. 0x3F) + 0x80) <>
byte (fromIntegral ((x `shiftR` 6) .&. 0x3F) + 0x80) <>
byte (fromIntegral (x .&. 0x3F) + 0x80)
{-# INLINE utf8Text #-}
utf8Text :: Data.Text.Text -> Builder
utf8Text =
Data.Text.foldl' (\builder -> mappend builder . utf8Char) mempty
{-# INLINE utf8LazyText #-}
utf8LazyText :: Data.Text.Lazy.Text -> Builder
utf8LazyText =
Data.Text.Lazy.foldl' (\builder -> mappend builder . utf8Char) mempty
{-# INLINABLE intercalate #-}
intercalate :: (Foldable f, Monoid m) => m -> f m -> m
intercalate incut =
fst .
foldl' (\(acc, incutFn) x -> (incutFn (mappend x acc), mappend incut)) (mempty, id)
{-# INLINE foldl #-}
foldl :: (a -> ByteString -> a) -> a -> Builder -> a
foldl step init (Builder length tree) =
A.foldl step init tree
{-# INLINE foldr #-}
foldr :: (ByteString -> a -> a) -> a -> Builder -> a
foldr step init (Builder length tree) =
A.foldr step init tree
{-# INLINE length #-}
length :: Builder -> Int
length (Builder length tree) =
length
{-# INLINABLE toByteString #-}
toByteString :: Builder -> ByteString
toByteString (Builder length tree) =
C.unsafeCreate length $ \ptr ->
void $ D.pokeTree tree ptr
{-# INLINABLE toLazyByteString #-}
toLazyByteString :: Builder -> E.ByteString
toLazyByteString =
foldr E.Chunk E.Empty