module Data.Beamable.Int
( beamInt
, unbeamInt
) where
import Data.Bits ((.|.), (.&.), shift, testBit)
import qualified Data.ByteString as B
import Data.List (unfoldr)
import Data.Word (Word8)
import Data.Int (Int64)
import Blaze.ByteString.Builder
beamInt :: Int64 -> Builder
beamInt 0 = fromWord8 0
beamInt n = toBldr . bitmark . reverse . unfoldr f $ n
where
f :: Int64 -> Maybe (Word8, Int64)
f 0 = Nothing
f x = let w = fromIntegral x .&. 0x7F :: Word8
rest = x `shift` (negate 7)
in Just (w, if rest == (1) then 0 else rest)
bitmark :: [Word8] -> [Word8]
bitmark (w:[]) = [w]
bitmark (w:ws) = (w .|. 0x80) : bitmark ws
bitmark [] = []
toBldr :: [Word8] -> Builder
toBldr ws =
let ws' = if testBit (head ws) 6
then if n > 0 then 0x80:ws else ws
else if n > 0 then ws else 0xFF:ws
in fromWriteList writeWord8 ws'
unbeamInt :: B.ByteString -> (Int64, B.ByteString)
unbeamInt bs = (fixSign (B.foldl f 0 this), rest)
where
f :: Int64 -> Word8 -> Int64
f i w = (i `shift` 7) .|. fromIntegral (w .&. 0x7F)
fixSign :: Int64 -> Int64
fixSign x = x `shift` (64 l * 7) `shift` (l * 7 64)
Just lastWord = B.findIndex (not . flip testBit 7) bs
l = lastWord + 1
(this, rest) = B.splitAt l bs