leb128-0.1.0.0: src/Codec/LEB128/Internal/BS.hs
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleInstances #-}
{- |
Module : Codec.LEB128.Internal.BS
Description : Encode values via (S)LEB128 using bytestring.
Copyright : (c) Andreas Klebinger 2020
License : BSD3
Maintainer : Andreas Klebinger
Portability : GHC >= 7.10
This module specializes the generic algorithms defined
in "Codec.LEB128.Generic" to use ByteString as byte
sequence representation.
-}
module Codec.LEB128.Internal.BS
( fromULEB128
, fromSLEB128
, fromULEB128Unsafe
, fromSLEB128Unsafe
, toULEB128
, toSLEB128
)
where
import Data.Word (Word8)
import Data.Maybe
import GHC.Magic
import qualified Data.ByteString.Builder as B
import qualified Data.ByteString as BS
import Control.Monad.Trans.State.Strict
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Class
import Control.Monad
import Codec.LEB128.Constraints
import Codec.LEB128.Generic as G
-- | Encode a unsigned value as bytestring builder in LEB128 encoding.
{-# INLINEABLE toULEB128 #-}
toULEB128 :: LEB128 a => a -> B.Builder
toULEB128 = (inline G.encodeLEB128) (B.word8)
-- | Encode a signed value as bytestring builder in SLEB128 encoding.
{-# INLINEABLE toSLEB128 #-}
toSLEB128 :: SLEB128 a => a -> B.Builder
toSLEB128 = (inline G.encodeSLEB128) (B.word8)
type UnsafeByteProvider = State (BS.ByteString)
{-# INLINABLE getByteUnsafe #-}
getByteUnsafe :: UnsafeByteProvider Word8
getByteUnsafe = do
(bs) <- get
let (!byte,!bs') = fromMaybe (error "Not enough bytes") $ BS.uncons bs
put $! bs'
return byte
{-# INLINABLE fromULEB128Unsafe #-}
-- | Decode a value in unsigned LEB128 encoding and return remaining bytes.
fromULEB128Unsafe :: LEB128 a => BS.ByteString -> (a,BS.ByteString)
fromULEB128Unsafe bytes = runState
((inline G.decodeLEB128) getByteUnsafe)
bytes
{-# INLINABLE fromSLEB128Unsafe #-}
-- | Decode a value in (signed) SLEB128 encoding and return remaining bytes.
fromSLEB128Unsafe :: SLEB128 a => BS.ByteString -> (a,BS.ByteString)
fromSLEB128Unsafe bytes = runState
((inline G.decodeSLEB128) getByteUnsafe)
bytes
type ByteProvider a = MaybeT (State BS.ByteString) a
{-# INLINE runByteProvider #-}
runByteProvider :: ByteProvider a -> BS.ByteString -> (Maybe a, BS.ByteString)
runByteProvider action = runState (runMaybeT action)
{-# INLINE liftMaybe #-}
liftMaybe :: (MonadPlus m) => Maybe a -> m a
liftMaybe = maybe mzero return
{-# INLINABLE getByte #-}
getByte :: (ByteProvider Word8)
getByte = do
(bs) <- lift get
(!byte,!bs') <- liftMaybe (BS.uncons bs)
lift $! put $! bs'
return byte
{-# INLINABLE fromULEB128 #-}
-- | Decode a value in unsigned LEB128 encoding and return remaining bytes.
fromULEB128 :: forall a. LEB128 a => BS.ByteString -> (Maybe a,BS.ByteString)
fromULEB128 =
let decode = (inline G.decodeLEB128) getByte :: MaybeT (State BS.ByteString) a
in runByteProvider decode
{-# INLINABLE fromSLEB128 #-}
-- | Decode a value in (signed) SLEB128 encoding and return remaining bytes.
fromSLEB128 :: forall a. SLEB128 a => BS.ByteString -> (Maybe a,BS.ByteString)
fromSLEB128 =
let decode = (inline G.decodeSLEB128) getByte :: MaybeT (State BS.ByteString) a
in runByteProvider decode