module Database.Perdure.WriteBits (
STSrcDest(..),
WordDest(..),
BitDest(..),
BitSrc(..),
WordSrc(..),
aligned,
padIncompleteWord,
WordSeq,
mkWordSeq,
CWordSeq(..),
BitAcc(..),
bitAccWordDest,
BSer,
ABitSeq,
mkABitSeq,
module Cgm.Data.Array
) where
import Foreign.Ptr
import Control.Applicative
import Control.Monad
import Foreign.Storable
import Foreign.Marshal.Array
import Cgm.Data.Array
import Cgm.Data.Word
import Cgm.Data.Len
import Cgm.Data.WordN hiding (d0)
import Cgm.System.Mem.Alloc
import Cgm.Control.Combinators
class STSrcDest d where
type SrcDestState d
type SrcDestST d = ST (SrcDestState d)
class STSrcDest d => WordDest d where
addWord :: Word -> d -> SrcDestST d d
class WordDest d => BitDest d where
addBit :: Word -> d -> SrcDestST d d
addBits :: Len Bool Word -> Word -> d -> SrcDestST d d
class STSrcDest d => BitSrc d where
addedBits :: d -> d -> Len Bool Word
copyBits :: (BitDest d', SrcDestST d ~ SrcDestST d') => d -> d -> d' -> SrcDestST d d'
class BitSrc d => WordSrc d where
addedWords :: d -> d -> Len Word Word
copyWords :: (WordDest d', SrcDestST d ~ SrcDestST d') => d -> d -> d' -> SrcDestST d d'
copyWordsPartial :: (BitDest d', SrcDestST d ~ SrcDestST d') => d -> d -> Len Bool Word -> d' -> SrcDestST d d'
instance STSrcDest (Ptr Word) where type SrcDestState (Ptr Word) = RealWorld
instance WordDest (Ptr Word) where
addWord w ptr = ioToST $ advancePtr ptr 1 <$ poke ptr w
instance BitSrc (Ptr Word) where
addedBits = refineLen ./ addedWords
copyBits = copyWords
instance WordSrc (Ptr Word) where
addedWords = (apply unsigned <$>) ./ minusPtrLen
copyWords end start d =
if end == start
then return d
else ioToST (peek start) >>= \w -> addWord w d >>= copyWords end (start `advancePtrLen` (1 :: Len Word Integer))
copyWordsPartial end start dropLow =
let len = addedWords end start
in if len == 0
then undefined
else (\d -> ioToST (peek start) >>= \w ->
addBits (refineLen word dropLow) (w `partialShiftRL` getLen dropLow) d) >=>
copyWords end (start `advancePtrLen` (1 :: Len Word Integer))
data WordSeq s f = WordSeq !(Len Word Word, [STPrimArray s f Word]) !(CWordSeq s f)
chunkSize :: Len Word Word
chunkSize = unsafeLen 2048
pushAnyFullChunk :: STMkArray (STPrimArray s f Word) => WordSeq s f -> ST s (WordSeq s f)
pushAnyFullChunk s@(WordSeq (l, r) (CWordSeq a ix)) =
if ix < chunkSize then return s else WordSeq (l + chunkSize, a : r) . (`CWordSeq` 0) <$> mkArray chunkSize
mkWordSeq :: STMkArray (STPrimArray s f Word) => ST s (WordSeq s f)
mkWordSeq = WordSeq (0, []) . (`CWordSeq` 0) <$> mkArray chunkSize
instance STSrcDest (WordSeq s f) where type SrcDestState (WordSeq s f) = s
instance STMkArray (STPrimArray s f Word) => WordDest (WordSeq s f) where
addWord w (WordSeq r c) = (WordSeq r <$> addWord w c) >>= pushAnyFullChunk
instance BitSrc (WordSeq s f) where
addedBits = refineLen ./ addedWords
copyBits = copyWords
instance WordSrc (WordSeq s f) where
addedWords (WordSeq (s1, _) c1) (WordSeq (s0, _) c0) = addedWords c1 c0 + (s1 s0)
copyWords (WordSeq (s1, r1) c1) start@(WordSeq (s0, _) c0) =
if s1 == s0
then copyWords c1 c0
else copyWords (WordSeq (s1 chunkSize, tail r1) $ chunkEnd (head r1)) start >=> copyWords c1 (chunkStart c1)
copyWordsPartial (WordSeq (s1, r1) c1) start@(WordSeq (s0, _) c0) =
if s1 == s0
then copyWordsPartial c1 c0
else \drp -> copyWordsPartial (WordSeq (s1 chunkSize, tail r1) $ chunkEnd (head r1)) start drp >=> copyWords c1 (chunkStart c1)
chunkStart :: CWordSeq s f -> CWordSeq s f
chunkStart (CWordSeq a _) = CWordSeq a 0
chunkEnd :: STPrimArray s f Word -> CWordSeq s f
chunkEnd a = CWordSeq a chunkSize
data CWordSeq s f = CWordSeq !(STPrimArray s f Word) !(Len Word Word)
instance STSrcDest (CWordSeq s f) where type SrcDestState (CWordSeq s f) = s
instance WordDest (CWordSeq s f) where
addWord w (CWordSeq arr ix) = CWordSeq arr (ix + 1) <$ writeArray arr ix w
instance BitSrc (CWordSeq s f) where
addedBits = refineLen ./ addedWords
copyBits = copyWords
instance WordSrc (CWordSeq s f) where
addedWords (CWordSeq _ n1) (CWordSeq _ n0) = n1 n0
copyWords end@(CWordSeq a i1) (CWordSeq _ i0) d =
if i1 == i0 then return d else readArray a i0 >>= flip addWord d >>= copyWords end (CWordSeq a $ i0 + 1)
copyWordsPartial end@(CWordSeq a i1) (CWordSeq _ i0) dropLow d =
if i1 == i0 then undefined
else do
w <- readArray a i0
addBits (refineLen word dropLow) (w `partialShiftRL` getLen dropLow) d >>= copyWords end (CWordSeq a $ i0 + 1)
data BitAcc d = BitAcc !Word !Word d
type BSer d = BitAcc d -> SrcDestST (BitAcc d) (BitAcc d)
onAlignment :: (d -> z) -> (BitAcc d -> z) -> BitAcc d -> z
onAlignment a u bd@(BitAcc b _ d) = if b == 0 then a d else u bd
aligned :: d -> BitAcc d
aligned = BitAcc 0 0
bitAccWordDest :: BitAcc d -> d
bitAccWordDest (BitAcc _ _ d) = d
padIncompleteWord :: WordDest d => BitAcc d -> SrcDestST d d
padIncompleteWord = onAlignment return (\(BitAcc _ acc d) -> addWord acc d)
instance STSrcDest d => STSrcDest (BitAcc d) where type SrcDestState (BitAcc d) = SrcDestState d
instance WordDest d => WordDest (BitAcc d) where
addWord !w (BitAcc b acc d) = BitAcc b (if b == 0 then 0 else partialShiftRL w (wordBits b)) <$> addWord (acc + partialShiftL w b) d
instance WordDest d => BitDest (BitAcc d) where
addBits n !w (BitAcc b acc d) = let
!acc' = acc + partialShiftL w b
b' = b + getLen n
b'' = b' wordBits
in if (signed $* b'') < 0
then return $ BitAcc b' acc' d
else let
!fullShift = getLen n == wordBits && b == 0
in if fullShift then aligned <$> addWord acc' d else BitAcc b'' (partialShiftRL w (getLen n b'')) <$> addWord acc' d
addBit !w (BitAcc b acc d) = let
acc' = acc + partialShiftL w b
b' = b + 1
b'' = b' wordBits
in if (signed $* b'') < 0
then return $ BitAcc b' acc' d
else aligned <$> addWord acc' d
instance WordSrc d => BitSrc (BitAcc d) where
copyBits (BitAcc endB endAcc end) (BitAcc startB _ start) =
if addedWords end start == 0
then addBits (unsafeLen $ endB startB) (endAcc `partialShiftRL` startB)
else copyWordsPartial end start (unsafeLen startB) >=> addBits (unsafeLen endB) endAcc
addedBits (BitAcc b1 _ d1) (BitAcc b0 _ d0) = refineLen (addedWords d1 d0) + unsafeLen (b1 b0)
type ABitSeq s = BitAcc (WordSeq s Free)
mkABitSeq :: ST s (ABitSeq s)
mkABitSeq = aligned <$> mkWordSeq