Improved Encode type

This commit is contained in:
VyacheslavHashov 2017-03-03 03:04:31 +03:00
parent 856d839a7c
commit a0d12906c5
2 changed files with 79 additions and 57 deletions

View File

@ -5,6 +5,7 @@ module Database.PostgreSQL.Protocol.Encoders
import Data.Word (Word32)
import Data.Monoid ((<>))
import Data.Char (ord)
import qualified Data.Vector as V
import qualified Data.ByteString as B
@ -21,8 +22,11 @@ encodeStartMessage (StartupMessage (Username uname) (DatabaseName dbname))
where
len = fromIntegral $ getEncodeLen payload
payload = putWord32BE currentVersion <>
putPgString "user" <> putPgString uname <>
putPgString "database" <> putPgString dbname <> putWord8 0
putByteStringNull "user" <>
putByteStringNull uname <>
putByteStringNull "database" <>
putByteStringNull dbname <>
putWord8 0
encodeStartMessage SSLRequest
-- Value hardcoded by PostgreSQL docs.
= putWord32BE 8 <> putWord32BE 80877103
@ -31,8 +35,8 @@ encodeClientMessage :: ClientMessage -> Encode
encodeClientMessage (Bind (PortalName portalName) (StatementName stmtName)
paramFormat values resultFormat)
= prependHeader 'B' $
putPgString portalName <>
putPgString stmtName <>
putByteStringNull portalName <>
putByteStringNull stmtName <>
-- `1` means that the specified format code is applied to all parameters
putWord16BE 1 <>
encodeFormat paramFormat <>
@ -43,32 +47,32 @@ encodeClientMessage (Bind (PortalName portalName) (StatementName stmtName)
putWord16BE 1 <>
encodeFormat resultFormat
encodeClientMessage (CloseStatement (StatementName stmtName))
= prependHeader 'C' $ putChar8 'S' <> putPgString stmtName
= prependHeader 'C' $ putChar8 'S' <> putByteStringNull stmtName
encodeClientMessage (ClosePortal (PortalName portalName))
= prependHeader 'C' $ putChar8 'P' <> putPgString portalName
= prependHeader 'C' $ putChar8 'P' <> putByteStringNull portalName
encodeClientMessage (DescribeStatement (StatementName stmtName))
= prependHeader 'D' $ putChar8 'S' <> putPgString stmtName
= prependHeader 'D' $ putChar8 'S' <> putByteStringNull stmtName
encodeClientMessage (DescribePortal (PortalName portalName))
= prependHeader 'D' $ putChar8 'P' <> putPgString portalName
= prependHeader 'D' $ putChar8 'P' <> putByteStringNull portalName
encodeClientMessage (Execute (PortalName portalName) (RowsToReceive rows))
= prependHeader 'E' $
putPgString portalName <>
putByteStringNull portalName <>
putWord32BE rows
encodeClientMessage Flush
= prependHeader 'H' mempty
encodeClientMessage (Parse (StatementName stmtName) (StatementSQL stmt) oids)
= prependHeader 'P' $
putPgString stmtName <>
putPgString stmt <>
putByteStringNull stmtName <>
putByteStringNull stmt <>
putWord16BE (fromIntegral $ V.length oids) <>
foldMap (putWord32BE . unOid) oids
encodeClientMessage (PasswordMessage passtext)
= prependHeader 'p' $ putPgString $ getPassword passtext
= prependHeader 'p' $ putByteStringNull $ getPassword passtext
where
getPassword (PasswordPlain p) = p
getPassword (PasswordMD5 p) = p
encodeClientMessage (SimpleQuery (StatementSQL stmt))
= prependHeader 'Q' $ putPgString stmt
= prependHeader 'Q' $ putByteStringNull stmt
encodeClientMessage Sync
= prependHeader 'S' mempty
encodeClientMessage Terminate
@ -76,18 +80,24 @@ encodeClientMessage Terminate
-- | Encodes single data values. Length `-1` indicates a NULL parameter value.
-- No value bytes follow in the NULL case.
{-# INLINE encodeValue #-}
encodeValue :: Maybe B.ByteString -> Encode
encodeValue Nothing = putWord32BE (-1)
encodeValue (Just v) = putWord32BE (fromIntegral $ B.length v)
<> putByteString v
{-# INLINE encodeFormat #-}
encodeFormat :: Format -> Encode
encodeFormat Text = putWord16BE 0
encodeFormat Binary = putWord16BE 1
{-# INLINE prependHeader #-}
prependHeader :: Char -> Encode -> Encode
prependHeader c payload =
-- Length includes itself but not the first message-type byte
let len = 4 + fromIntegral (getEncodeLen payload)
in putChar8 c <> putWord32BE len <> payload
{-# INLINE putChar8 #-}
putChar8 :: Char -> Encode
putChar8 = putWord8 . fromIntegral . ord

View File

@ -3,10 +3,9 @@ module Database.PostgreSQL.Protocol.Store.Encode where
import Data.Monoid (Monoid(..), (<>))
import Foreign (poke, plusPtr, Ptr)
import Data.Int (Int16, Int32)
import Data.Word (Word8, Word16, Word32)
import Data.Char (ord)
import Data.Bits (shiftR)
import Data.Word
import Foreign
import Data.ByteString (ByteString)
import Data.ByteString.Internal as B(toForeignPtr)
import Data.Store.Core (Poke(..), unsafeEncodeWith, pokeStatePtr,
@ -15,65 +14,78 @@ import Data.Store.Core (Poke(..), unsafeEncodeWith, pokeStatePtr,
data Encode = Encode {-# UNPACK #-} !Int !(Poke ())
instance Monoid Encode where
mempty = Encode 0 . Poke $ \_ offset -> pure (offset, ())
{-# INLINE mempty #-}
mempty = Encode 0 . Poke $ \_ offset -> pure (offset, ())
(Encode len1 f1) `mappend` (Encode len2 f2) = Encode (len1 + len2) (f1 *> f2)
{-# INLINE mappend #-}
(Encode len1 f1) `mappend` (Encode len2 f2) = Encode (len1 + len2) (f1 *> f2)
{-# INLINE getEncodeLen #-}
getEncodeLen :: Encode -> Int
getEncodeLen (Encode len _) = len
{-# INLINE getEncodeLen #-}
{-# INLINE runEncode #-}
runEncode :: Encode -> ByteString
runEncode (Encode len f) = unsafeEncodeWith f len
{-# INLINE runEncode #-}
fixedPrim :: Int -> (Ptr Word8 -> IO ()) -> Encode
fixedPrim len f = Encode len . Poke $ \state offset -> do
{-# INLINE fixed #-}
fixed :: Int -> (Ptr Word8 -> IO ()) -> Encode
fixed len f = Encode len . Poke $ \state offset -> do
f $ pokeStatePtr state `plusPtr` offset
let !newOffset = offset + len
return (newOffset, ())
{-# INLINE fixedPrim #-}
putWord8 :: Word8 -> Encode
putWord8 w = fixedPrim 1 $ \p -> poke p w
{-# INLINE putWord8 #-}
putChar8 :: Char -> Encode
putChar8 = putWord8 . fromIntegral . ord
{-# INLINE putChar8 #-}
putWord16BE :: Word16 -> Encode
putWord16BE w = fixedPrim 2 $ \p -> do
poke p (fromIntegral (shiftR w 8) :: Word8)
poke (p `plusPtr` 1) (fromIntegral w :: Word8)
{-# INLINE putWord16BE #-}
putWord32BE :: Word32 -> Encode
putWord32BE w = fixedPrim 4 $ \p -> do
poke p (fromIntegral (shiftR w 24) :: Word8)
poke (p `plusPtr` 1) (fromIntegral (shiftR w 16) :: Word8)
poke (p `plusPtr` 2) (fromIntegral (shiftR w 8) :: Word8)
poke (p `plusPtr` 3) (fromIntegral w :: Word8)
{-# INLINE putWord32BE #-}
putInt32BE :: Int32 -> Encode
putInt32BE = putWord32BE . fromIntegral
{-# INLINE putInt32BE #-}
putInt16BE :: Int16 -> Encode
putInt16BE = putWord16BE . fromIntegral
{-# INLINE putInt16BE #-}
{-# INLINE putByteString #-}
putByteString :: ByteString -> Encode
putByteString bs =
let (ptr, offset, len) = toForeignPtr bs
in Encode len $ pokeFromForeignPtr ptr offset len
{-# INLINE putByteString #-}
-- | C-like string
putPgString :: ByteString -> Encode
putPgString bs = putByteString bs <> putWord8 0
{-# INLINE putPgString #-}
{-# INLINE putByteStringNull #-}
putByteStringNull :: ByteString -> Encode
putByteStringNull bs = putByteString bs <> putWord8 0
{-# INLINE putWord8 #-}
putWord8 :: Word8 -> Encode
putWord8 w = fixed 1 $ \p -> poke p w
{-# INLINE putWord16BE #-}
putWord16BE :: Word16 -> Encode
putWord16BE w = fixed 2 $ \p -> poke (castPtr p) (byteSwap16 w)
{-# INLINE putWord32BE #-}
putWord32BE :: Word32 -> Encode
putWord32BE w = fixed 4 $ \p -> poke (castPtr p) (byteSwap32 w)
{-# INLINE putWord64BE #-}
putWord64BE :: Word64 -> Encode
putWord64BE w = fixed 8 $ \p -> poke (castPtr p) (byteSwap64 w)
{-# INLINE putInt16BE #-}
putInt16BE :: Int16 -> Encode
putInt16BE = putWord16BE . fromIntegral
{-# INLINE putInt32BE #-}
putInt32BE :: Int32 -> Encode
putInt32BE = putWord32BE . fromIntegral
{-# INLINE putInt64BE #-}
putInt64BE :: Int64 -> Encode
putInt64BE = putWord64BE . fromIntegral
{-# INLINE putFloat32BE #-}
putFloat32BE :: Float -> Encode
putFloat32BE float = fixed 4 $ \ptr -> byteSwap32 <$> floatToWord float
>>= poke (castPtr ptr)
{-# INLINE putFloat64BE #-}
putFloat64BE :: Double -> Encode
putFloat64BE double = fixed 8 $ \ptr -> byteSwap64 <$> floatToWord double
>>= poke (castPtr ptr)
{-# INLINE floatToWord #-}
floatToWord :: (Storable word, Storable float) => float -> IO word
floatToWord float = alloca $ \buf -> do
poke (castPtr buf) float
peek buf