hpgsql-0.1.0.0: src/Hpgsql/Builder.hs
module Hpgsql.Builder where
-- \| This module replicates parts of the API of Data.ByteString.Builder but its own
-- builder is length-aware, which makes other parts of the code a little bit nicer.
-- In COPY benchmarks, this module was introduced in a commit (together with other
-- changes, like replacing `Maybe` with `BinaryField` in `ToPgField`) that barely
-- changed memory usage and runtime.
-- The benefits are exclusively for code readability, then.
-- \|
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Builder as Builder
import qualified Data.ByteString.Builder.Extra as Builder
import qualified Data.ByteString.Lazy as LBS
import Data.Int (Int16, Int32, Int64)
import Data.Word (Word8)
data BinaryField = SqlNull | NotNull !ByteString
deriving stock (Eq)
instance Show BinaryField where
show SqlNull = "NULL"
show (NotNull bs) = show bs
data LengthAwareBuilder = LengthAwareBuilder !Int32 !Builder.Builder
type Builder = LengthAwareBuilder
instance Semigroup LengthAwareBuilder where
LengthAwareBuilder l1 b1 <> LengthAwareBuilder l2 b2 = LengthAwareBuilder (l1 + l2) (b1 <> b2)
instance Monoid LengthAwareBuilder where
mempty = LengthAwareBuilder 0 mempty
builderLength :: LengthAwareBuilder -> Int32
builderLength (LengthAwareBuilder n _) = n
binaryField :: BinaryField -> LengthAwareBuilder
binaryField = \case
SqlNull -> LengthAwareBuilder 4 (Builder.int32BE (-1))
NotNull val -> let valLen = fromIntegral (BS.length val) in LengthAwareBuilder (4 + valLen) (Builder.int32BE valLen <> Builder.byteString val)
toLazyByteString :: LengthAwareBuilder -> LBS.ByteString
toLazyByteString (LengthAwareBuilder (fromIntegral -> totalLen) builder) = Builder.toLazyByteStringWith (Builder.untrimmedStrategy totalLen 0) mempty builder
toStrictByteString :: LengthAwareBuilder -> ByteString
toStrictByteString = LBS.toStrict . toLazyByteString
char7 :: Char -> LengthAwareBuilder
char7 c = LengthAwareBuilder 1 (Builder.char7 c)
string7 :: String -> LengthAwareBuilder
string7 s = LengthAwareBuilder (fromIntegral $ length s) (Builder.string7 s)
word8 :: Word8 -> LengthAwareBuilder
word8 s = LengthAwareBuilder 1 (Builder.word8 s)
int16BE :: Int16 -> LengthAwareBuilder
int16BE n = LengthAwareBuilder 2 (Builder.int16BE n)
int32BE :: Int32 -> LengthAwareBuilder
int32BE n = LengthAwareBuilder 4 (Builder.int32BE n)
int64BE :: Int64 -> LengthAwareBuilder
int64BE n = LengthAwareBuilder 8 (Builder.int64BE n)
byteString :: ByteString -> LengthAwareBuilder
byteString bs = LengthAwareBuilder (fromIntegral $ BS.length bs) (Builder.byteString bs)
lazyByteString :: LBS.ByteString -> LengthAwareBuilder
lazyByteString bs = LengthAwareBuilder (fromIntegral $ LBS.length bs) (Builder.lazyByteString bs)