packages feed

liszt-0.2: src/Database/Liszt/Internal/Decoder.hs

module Database.Liszt.Internal.Decoder (Decoder(..)
  , runDecoder
  , DecodeResult(..)
  , decodeWord8
  , decodeVarInt
  , decodeInt) where

import Control.Monad.Catch
import Foreign.Ptr
import Foreign.Storable
import Data.Bits
import Data.Word
import System.Endian

newtype Decoder a = Decoder { unDecoder :: Ptr Word8 -> IO (DecodeResult a) }

runDecoder :: Decoder a -> Ptr Word8 -> IO a
runDecoder m p = do
  DecodeResult _ a <- unDecoder m p
  return a

data DecodeResult a = DecodeResult {-# UNPACK #-} !(Ptr Word8) !a

instance MonadThrow Decoder where
  throwM e = Decoder $ \_ -> throwM e

instance Functor Decoder where
  fmap f m = Decoder $ \p -> do
    DecodeResult p' a <- unDecoder m p
    return (DecodeResult p' (f a))
  {-# INLINE fmap #-}

instance Applicative Decoder where
  pure a = Decoder $ \p -> pure $ DecodeResult p a
  {-# INLINE pure #-}
  m <*> n = Decoder $ \p -> do
    DecodeResult p' f <- unDecoder m p
    DecodeResult p'' a <- unDecoder n p'
    return $ DecodeResult p'' (f a)
  {-# INLINE (<*>) #-}

instance Monad Decoder where
  return = pure
  m >>= k = Decoder $ \p -> do
    DecodeResult p' a <- unDecoder m p
    unDecoder (k a) p'
  {-# INLINE (>>=) #-}
  (>>) = (*>)

decodeWord8 :: Decoder Word8
decodeWord8 = Decoder $ \ptr -> do
  a <- peek ptr
  return $ DecodeResult (ptr `plusPtr` 1) a
{-# INLINE decodeWord8 #-}

decodeVarInt :: Decoder Int
decodeVarInt = decodeWord8 >>= go
  where
    go n
      | testBit n 7 = do
        m <- decodeWord8 >>= go
        return $! unsafeShiftL m 7 .|. clearBit (fromIntegral n) 7
      | otherwise = return $ fromIntegral n
{-# INLINE decodeVarInt #-}

decodeInt :: Decoder Int
decodeInt = Decoder $ \ptr -> do
  a <- peek (castPtr ptr)
  return $ DecodeResult (ptr `plusPtr` 8) (fromIntegral $ fromBE64 a)
{-# INLINE decodeInt #-}