{- |
Internal implementation of variable-length decoding. This API is not guaranteed
to be stable.
-}
module Data.Attoparsec.VarWord.Internal
  ( varWordBe', varWordLe'
  ) where

import Data.Attoparsec.ByteString (Parser, anyWord8)
import Data.Bits (Bits, shiftL, testBit, (.&.), (.|.))
import Data.Word


msb :: Word8 -> Bool
msb = flip testBit 7

lsbs :: Word8 -> Word8
lsbs = (.&. 0b01111111)


-- | Internal implementation of big-endian variable-length decoding, exposing
--   the accumulator and how much to increment each continuation chunk.
varWordBe' :: (Bits a, Integral a) => a -> a -> Parser a
varWordBe' contInc acc = do
  byte <- anyWord8
  let acc' = shiftL acc 7 .|. fromIntegral (lsbs byte)
  if msb byte
    then varWordBe' contInc (acc' + contInc)
    else return acc'


-- | Internal implementation of little-endian variable-length decoding, exposing
--   the accumulator and the shift for each chunk.
varWordLe' :: (Bits a, Integral a) => Int -> a -> Parser a
varWordLe' shiftBy acc = do
  byte <- anyWord8
  let acc' = acc .|. shiftL (fromIntegral $ lsbs byte) shiftBy
  if msb byte
    then varWordLe' (shiftBy + 7) acc'
    else return acc'