{-# LANGUAGE ScopedTypeVariables #-}
module HaskellWorks.Data.Bits.Writer.Storable where
import Control.Monad.ST
import Data.Word
import HaskellWorks.Data.Bits.BitWise
import qualified Data.STRef                   as ST
import qualified Data.Vector.Storable.Mutable as DVSM
{-# ANN module ("HLint: ignore Reduce duplication" :: String) #-}
data Writer s = Writer
  { vector   :: DVSM.MVector s Word64
  , position :: ST.STRef s Int
  }
full :: Writer s -> ST s Bool
full writer = do
  p <- ST.readSTRef $ position writer
  return $ p * 64 >= DVSM.length (vector writer)
{-# INLINE full #-}
newWriter :: Int -> ST s (Writer s)
newWriter size = do
  v <- DVSM.new size
  p <- ST.newSTRef 0
  return $ Writer v p
{-# INLINE newWriter #-}
unsafeWriteBit :: Writer s -> Word64 -> ST s ()
unsafeWriteBit writer w = do
  let v = vector   writer             
  p <- ST.readSTRef $ position writer 
  let i = p .>. 6                     
  let o = p .&. 0x3f                  
  e <- DVSM.unsafeRead v i
  DVSM.unsafeWrite v i (((w .&. 1) .<. fromIntegral o) .|. e)
  ST.writeSTRef (position writer) (p + 1)
{-# INLINE unsafeWriteBit #-}
unsafeWriteLoBits :: Writer s -> Int -> Word64 -> ST s ()
unsafeWriteLoBits writer c w = do
  let u = w .&. ((1 .<. fromIntegral c) - 1)
  let v = vector   writer             
  p <- ST.readSTRef $ position writer 
  let i = p .>. 6                     
  let o = p .&. 0x3f                  
  lo <- DVSM.unsafeRead v i
  DVSM.unsafeWrite v i $ lo .|. (u .<. fromIntegral o)
  ST.writeSTRef (position writer) (p + c)
{-# INLINE unsafeWriteLoBits #-}
unsafeWriteBits :: Writer s -> Int -> Word64 -> ST s ()
unsafeWriteBits writer c w = do
  let u = w .&. ((1 .<. fromIntegral c) - 1)  
  let v = vector writer                       
  p <- ST.readSTRef $ position writer         
  let i = p .>. 6                             
  let j = i + 1                               
  let o = p .&. 0x3f                          
  lo <- DVSM.unsafeRead v i
  DVSM.unsafeWrite v i $ lo .|. (u .<. fromIntegral o)
  hi <- DVSM.unsafeRead v j
  DVSM.unsafeWrite v j $ hi .|. (u .>. fromIntegral (64 - o))
  ST.writeSTRef (position writer) (p + c)
{-# INLINE unsafeWriteBits #-}
written :: Writer s -> ST s (DVSM.MVector s Word64)
written writer = do
  p <- ST.readSTRef $ position writer         
  return $ DVSM.take ((p + 63) `div` 64) (vector writer)
{-# INLINE written #-}