module Data.Bits.Coding
( Coding(..)
, getAligned, getBit, getBits, getBitsFrom
, putAligned, putUnaligned, putBit, putBits, putBitsFrom
) where
import Control.Applicative
import Control.Monad
import Control.Monad.State.Class
import Control.Monad.Reader.Class
import Control.Monad.Trans
import Data.Bits
import Data.Bits.Extras
import Data.Bytes.Get
import Data.Bytes.Put
import Data.Word
newtype Coding m a = Coding
{ runCoding :: forall r. (a -> Int -> Word8 -> m r) -> Int -> Word8 -> m r
}
instance Functor (Coding m) where
fmap f (Coding m) = Coding $ \ k -> m (k . f)
instance Monad m => Applicative (Coding m) where
pure a = Coding $ \k -> k a
(<*>) = ap
instance Monad m => Monad (Coding m) where
return = pure
Coding m >>= f = Coding $ \ k -> m $ \a -> runCoding (f a) k
fail e = Coding $ \_ _ _ -> fail e
instance (Monad m, Alternative m) => Alternative (Coding m) where
empty = Coding $ \_ _ _ -> empty
Coding m <|> Coding n = Coding $ \k i b -> do
(a,i',b') <- m (\a i' b' -> pure (a,i',b')) i b <|> n (\a i' b' -> pure (a,i',b')) i b
k a i' b'
instance MonadPlus m => MonadPlus (Coding m) where
mzero = Coding $ \_ _ _ -> mzero
mplus (Coding m) (Coding n) = Coding $ \k i b -> do
(a,i',b') <- m (\a i' b' -> return (a,i',b')) i b `mplus` n (\a i' b' -> return (a,i',b')) i b
k a i' b'
instance MonadTrans Coding where
lift m = Coding $ \k i w -> do
a <- m
k a i w
instance MonadState s m => MonadState s (Coding m) where
get = lift get
put = lift . put
instance MonadReader e m => MonadReader e (Coding m) where
ask = lift ask
local f (Coding m) = Coding $ \k i b -> do
(a,i',b') <- local f $ m (\a i' b' -> return (a, i', b')) i b
k a i' b'
getAligned :: MonadGet m => m a -> Coding m a
getAligned m = Coding $ \k _ _ -> m >>= \ a -> k a 0 0
getBit :: MonadGet m => Coding m Bool
getBit = Coding $ \ k i b ->
if i == 0
then getWord8 >>= \b' -> ((k $! testBit b' 7) $! 7) $! unsafeShiftL b' 1
else ((k $! testBit b 7) $! i 1) $! unsafeShiftL b 1
getBits :: (MonadGet m, Bits b) => Int -> Int -> b -> Coding m b
getBits from to bits | from < to = return bits
| otherwise = do b <- getBit
getBits (pred from) to $ assignBit bits from b
getBitsFrom :: (MonadGet m, Bits b) => Int -> b -> Coding m b
getBitsFrom from bits = getBits from 0 bits
instance MonadGet m => MonadGet (Coding m) where
type Remaining (Coding m) = Remaining m
type Bytes (Coding m) = Bytes m
skip = getAligned . skip
lookAhead (Coding m) = Coding $ \k i b -> lookAhead (m k i b)
lookAheadM (Coding m) = Coding $ \k i b -> lookAheadE (m (distribute k) i b) >>= factor
where
distribute k Nothing i' b' = return $ Left $ k (Nothing) i' b'
distribute k (Just a) i' b' = return $ Right $ k (Just a) i' b'
factor = either id id
lookAheadE (Coding m) = Coding $ \k i b -> lookAheadE (m (distribute k) i b) >>= factor
where
distribute k (Left e) i' b' = return $ Left $ k (Left e) i' b'
distribute k (Right a) i' b' = return $ Right $ k (Right a) i' b'
factor = either id id
getBytes = getAligned . getBytes
remaining = lift remaining
isEmpty = lift isEmpty
getWord8 = getAligned getWord8
getByteString = getAligned . getByteString
getLazyByteString = getAligned . getLazyByteString
getWord16le = getAligned getWord16le
getWord32le = getAligned getWord32le
getWord64le = getAligned getWord64le
getWord16be = getAligned getWord16be
getWord32be = getAligned getWord32be
getWord64be = getAligned getWord64be
getWord16host = getAligned getWord16host
getWord32host = getAligned getWord32host
getWord64host = getAligned getWord64host
getWordhost = getAligned getWordhost
putAligned :: MonadPut m => m a -> Coding m a
putAligned m = Coding $ \ k i b ->
if i == 0
then do
a <- m
k a 0 0
else do
putWord8 b
a <- m
k a 0 0
putUnaligned :: (MonadPut m, FiniteBits b) => b -> Coding m ()
putUnaligned b = putBitsFrom (finiteBitSize b) b
putBit :: MonadPut m => Bool -> Coding m ()
putBit v = Coding $ \k i b ->
if i == 7
then do
putWord8 (pushBit b i v)
k () 0 0
else (k () $! i + 1) $! pushBit b i v
where
pushBit w i False = clearBit w $ 7 i
pushBit w i True = setBit w $ 7 i
putBits :: (MonadPut m, Bits b) => Int -> Int -> b -> Coding m ()
putBits from to b | from < to = return ()
| otherwise = putBit (b `testBit` from) >> putBits (pred from) to b
putBitsFrom :: (MonadPut m, Bits b) => Int -> b -> Coding m ()
putBitsFrom from b = putBits from 0 b
instance MonadPut m => MonadPut (Coding m) where
putWord8 = putAligned . putWord8
putByteString = putAligned . putByteString
putLazyByteString = putAligned . putLazyByteString
flush = putAligned flush
putWord16le = putAligned . putWord16le
putWord32le = putAligned . putWord32le
putWord64le = putAligned . putWord64le
putWord16be = putAligned . putWord16be
putWord32be = putAligned . putWord32be
putWord64be = putAligned . putWord64be
putWord16host = putAligned . putWord16host
putWord32host = putAligned . putWord32host
putWord64host = putAligned . putWord64host
putWordhost = putAligned . putWordhost