{-# LANGUAGE FlexibleContexts #-}
module Haskus.Binary.Bits.Put
( BitPutState(..)
, newBitPutState
, putBits
, putBitsBuffer
, getBitPutBuffer
, getBitPutBufferList
, BitPut
, BitPutT
, runBitPut
, runBitPutT
, putBitsM
, putBitBoolM
, putBitsBufferM
, changeBitPutOrder
, withBitPutOrder
)
where
import Control.Monad.State
import Control.Monad.Identity
import Haskus.Binary.BufferBuilder as B
import Haskus.Binary.Buffer
import Haskus.Number.Word
import Haskus.Binary.BufferList (BufferList)
import Haskus.Binary.Bits.Order
import Haskus.Binary.Bits
data BitPutState = BitPutState
{ bitPutStateBuilder :: !BufferBuilder
, bitPutStateCurrent :: !Word8
, bitPutStateOffset :: !Word
, bitPutStateBitOrder :: !BitOrder
}
newBitPutState :: BitOrder -> BitPutState
newBitPutState = BitPutState mempty 0 0
putBits ::
( Integral a
, Bits a
, ReversableBits a
) => Word -> a -> BitPutState -> BitPutState
putBits n w s@(BitPutState builder b o bo) = s'
where
cn = min (8-o) n
s' = case n of
0 -> s
_ -> putBits (n-cn) w' (flush (BitPutState builder b' (o+cn) bo))
b' = shl (selectBits w) .|. b
w' = case bo of
BB -> w
BL -> w `shiftR` fromIntegral cn
LL -> w `shiftR` fromIntegral cn
LB -> w
selectBits :: (Bits a, ReversableBits a, Integral a) => a -> Word8
selectBits x = fromIntegral $ case bo of
BB -> maskDyn cn $ x `shiftR` fromIntegral (n-cn)
LB -> reverseLeastBits cn $ maskDyn cn $ x `shiftR` fromIntegral (n-cn)
LL -> maskDyn cn x
BL -> reverseLeastBits cn $ maskDyn cn x
shl :: Word8 -> Word8
shl x = case bo of
BB -> x `shiftL` (8 - fromIntegral o - fromIntegral cn)
BL -> x `shiftL` (8 - fromIntegral o - fromIntegral cn)
LL -> x `shiftL` fromIntegral o
LB -> x `shiftL` fromIntegral o
flush s2@(BitPutState b2 w2 o2 bo2)
| o2 == 8 = BitPutState (b2 `mappend` B.fromWord8 w2) 0 0 bo2
| otherwise = s2
putBitsBuffer :: Buffer -> BitPutState -> BitPutState
putBitsBuffer bs s
| isBufferEmpty bs = s
| otherwise = case s of
(BitPutState builder b 0 BB) -> BitPutState (builder `mappend` B.fromBuffer bs) b 0 BB
(BitPutState builder b 0 LL) -> BitPutState (builder `mappend` B.fromBuffer (bufferReverse bs)) b 0 LL
(BitPutState builder b 0 LB) -> BitPutState (builder `mappend` B.fromBuffer (rev bs)) b 0 LB
(BitPutState builder b 0 BL) -> BitPutState (builder `mappend` B.fromBuffer (rev (bufferReverse bs))) b 0 BL
(BitPutState _ _ _ BB) -> putBitsBuffer (bufferUnsafeTail bs) (putBits 8 (bufferUnsafeHead bs) s)
(BitPutState _ _ _ LL) -> putBitsBuffer (bufferUnsafeInit bs) (putBits 8 (bufferUnsafeLast bs) s)
(BitPutState _ _ _ BL) -> putBitsBuffer (bufferUnsafeInit bs) (putBits 8 (bufferUnsafeLast bs) s)
(BitPutState _ _ _ LB) -> putBitsBuffer (bufferUnsafeTail bs) (putBits 8 (bufferUnsafeHead bs) s)
where
rev = bufferMap reverseBits
flushIncomplete :: BitPutState -> BitPutState
flushIncomplete s@(BitPutState b w o bo)
| o == 0 = s
| otherwise = BitPutState (b `mappend` B.fromWord8 w) 0 0 bo
getBitPutBufferList :: BitPutState -> BufferList
getBitPutBufferList = toBufferList . bitPutStateBuilder . flushIncomplete
getBitPutBuffer :: BitPutState -> Buffer
getBitPutBuffer = toBuffer . bitPutStateBuilder . flushIncomplete
type BitPutT m a = StateT BitPutState m a
type BitPut a = BitPutT Identity a
runBitPutT :: Monad m => BitOrder -> BitPutT m a -> m Buffer
runBitPutT bo m = getBitPutBuffer <$> execStateT m (newBitPutState bo)
runBitPut :: BitOrder -> BitPut a -> Buffer
runBitPut bo m = runIdentity (runBitPutT bo m)
putBitsM :: (Monad m, Integral a, Bits a, ReversableBits a) => Word -> a -> BitPutT m ()
putBitsM n w = modify (putBits n w)
putBitBoolM :: (Monad m) => Bool -> BitPutT m ()
putBitBoolM b = putBitsM 1 (if b then 1 else 0 :: Word)
putBitsBufferM :: Monad m => Buffer -> BitPutT m ()
putBitsBufferM bs = modify (putBitsBuffer bs)
changeBitPutOrder :: Monad m => BitOrder -> BitPutT m ()
changeBitPutOrder bo = modify (\s -> s { bitPutStateBitOrder = bo })
withBitPutOrder :: Monad m => BitOrder -> BitPutT m a -> BitPutT m a
withBitPutOrder bo m = do
bo' <- gets bitPutStateBitOrder
changeBitPutOrder bo
v <- m
changeBitPutOrder bo'
return v