{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleContexts #-}
module Haskus.Binary.Bits.Get
( BitGetState(..)
, newBitGetState
, isEmpty
, skipBits
, skipBitsToAlignOnWord8
, getBits
, getBitsChecked
, getBitsBuffer
, BitGet
, BitGetT
, runBitGet
, runBitGetT
, runBitGetPartialT
, runBitGetPartial
, resumeBitGetPartialT
, resumeBitGetPartial
, isEmptyM
, skipBitsM
, skipBitsToAlignOnWord8M
, getBitsM
, getBitsCheckedM
, getBitBoolM
, getBitsBSM
, changeBitGetOrder
, withBitGetOrder
)
where
import System.IO.Unsafe (unsafePerformIO)
import Control.Monad.State
import Control.Monad.Identity
import Foreign.Marshal.Alloc (mallocBytes)
import Foreign.Ptr
import Haskus.Binary.Buffer
import Haskus.Binary.Bits.Order
import Haskus.Binary.Bits
import Haskus.Binary.Storable (poke)
data BitGetState = BitGetState
{ bitGetStateInput :: {-# UNPACK #-} !Buffer
, bitGetStateBitOffset :: {-# UNPACK #-} !Word
, bitGetStateBitOrder :: !BitOrder
} deriving (Show)
newBitGetState :: BitOrder -> Buffer -> BitGetState
newBitGetState bo bs = BitGetState bs 0 bo
isEmpty :: BitGetState -> Bool
isEmpty (BitGetState bs o _) = o == 0 && isBufferEmpty bs
skipBits :: Word -> BitGetState -> BitGetState
skipBits o (BitGetState bs n bo) = BitGetState (bufferUnsafeDrop d bs) n' bo
where
!o' = n+o
!d = fromIntegral $ byteOffset o'
!n' = bitOffset o'
skipBitsToAlignOnWord8 :: BitGetState -> BitGetState
skipBitsToAlignOnWord8 s = case bitGetStateBitOffset s of
0 -> s
n -> skipBits (8-n) s
getBits :: (Integral a, Bits a) => Word -> BitGetState -> a
getBits nbits (BitGetState bs off bo) = rec zeroBits 0 bs off nbits
where
rec w _ _ _ 0 = w
rec w n i o r = rec nw (n+nb) (bufferTail i) o' (r-nb)
where
c = bufferHead i
nb = min (8-o) r
tc = fromIntegral $ getBitRange bo o nb c
nw = case bo of
BB -> (w `shiftL` fromIntegral nb) .|. tc
LB -> (w `shiftL` fromIntegral nb) .|. tc
BL -> (tc `shiftL` fromIntegral n) .|. w
LL -> (tc `shiftL` fromIntegral n) .|. w
o' = bitOffset (o + nb)
getBitsChecked :: (Integral a, Bits a, ReversableBits a) => Word -> Word -> BitGetState -> a
{-# INLINABLE getBitsChecked #-}
getBitsChecked m n s
| n > m = error $ "Tried to read more than " ++ show m ++ " bits (" ++ show n ++")"
| otherwise = getBits n s
getBitsBuffer :: Word -> BitGetState -> Buffer
getBitsBuffer n (BitGetState bs o bo) =
if n == 0
then emptyBuffer
else
let
bs' = bufferUnsafeTake (n+1) bs
bs'' = bufferUnsafeTake n bs
rev = bufferMap reverseBits
in case (o,bo) of
(0,BB) -> bs''
(0,LL) -> bufferReverse bs''
(0,LB) -> rev bs''
(0,BL) -> rev $ bufferReverse bs''
(_,LL) -> getBitsBuffer n (BitGetState (bufferReverse bs') (8-o) BB)
(_,BL) -> rev . bufferReverse $ getBitsBuffer n (BitGetState bs' o BB)
(_,LB) -> rev . bufferReverse $ getBitsBuffer n (BitGetState bs' o LL)
(_,BB) -> unsafePerformIO $ do
let len = n+1
ptr <- mallocBytes (fromIntegral len)
let f r i = do
let
w = bufferUnsafeIndex bs (len-i)
w' = (w `shiftL` fromIntegral o) .|. r
r' = w `shiftR` (8-fromIntegral o)
poke (castPtr ptr `plusPtr` fromIntegral (len-i)) w'
return r'
foldM_ f 0 [1..len]
bufferUnsafeInit <$> bufferPackPtr len ptr
type BitGetT m a = StateT BitGetState m a
type BitGet a = BitGetT Identity a
runBitGetT :: Monad m => BitOrder -> BitGetT m a -> Buffer -> m a
runBitGetT bo m bs = evalStateT m (newBitGetState bo bs)
runBitGet :: BitOrder -> BitGet a -> Buffer -> a
runBitGet bo m bs = runIdentity (runBitGetT bo m bs)
runBitGetPartialT :: BitOrder -> BitGetT m a -> Buffer -> m (a, BitGetState)
runBitGetPartialT bo m bs = runStateT m (newBitGetState bo bs)
runBitGetPartial :: BitOrder -> BitGet a -> Buffer -> (a, BitGetState)
runBitGetPartial bo m bs = runIdentity (runBitGetPartialT bo m bs)
resumeBitGetPartialT :: BitGetT m a -> BitGetState -> m (a, BitGetState)
resumeBitGetPartialT = runStateT
resumeBitGetPartial :: BitGet a -> BitGetState -> (a,BitGetState)
resumeBitGetPartial m s = runIdentity (resumeBitGetPartialT m s)
isEmptyM :: Monad m => BitGetT m Bool
isEmptyM = gets isEmpty
skipBitsM :: Monad m => Word -> BitGetT m ()
skipBitsM = modify . skipBits
skipBitsToAlignOnWord8M :: Monad m => BitGetT m ()
skipBitsToAlignOnWord8M = modify skipBitsToAlignOnWord8
getBitsM :: (Integral a, Bits a, Monad m) => Word -> BitGetT m a
getBitsM n = do
v <- gets (getBits n)
skipBitsM n
return v
getBitsCheckedM :: (Integral a, Bits a, ReversableBits a, Monad m) => Word -> Word -> BitGetT m a
getBitsCheckedM m n = do
v <- gets (getBitsChecked m n)
skipBitsM n
return v
getBitBoolM :: (Monad m) => BitGetT m Bool
getBitBoolM = do
v <- getBitsM 1
return ((v :: Word) == 1)
getBitsBSM :: (Monad m) => Word -> BitGetT m Buffer
getBitsBSM n = do
bs <- gets (getBitsBuffer n)
skipBitsM (8*n)
return bs
changeBitGetOrder :: Monad m => BitOrder -> BitGetT m ()
changeBitGetOrder bo = modify (\s -> s { bitGetStateBitOrder = bo })
withBitGetOrder :: Monad m => BitOrder -> BitGetT m a -> BitGetT m a
withBitGetOrder bo m = do
bo' <- gets bitGetStateBitOrder
changeBitGetOrder bo
v <- m
changeBitGetOrder bo'
return v