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 {- Beamed int representation: 1. The integer is chunked up into 7-bit groups. Each of these 7bit chunks are encoded as a single octet. 2. All the octets except the last one has its 8th bit set. 3. 7th bit of the first octet represents sign. 3. Octets with bits 1..7 containing only 1 or 0 can be ignored when it's not affecting the sign: 0 | 0 0000000 1 | 0 0000001 63 | 0 0111111 64 | 1 0000000 0 1000000 127 | 1 0000000 0 1111111 128 | 1 0000001 0 0000000 8191 | 1 0111111 0 1111111 8192 | 1 0000000 1 1000000 0 0000000 65535 | 1 0000011 1 1111111 0 1111111 -1 | 0 1111111 -64 | 0 1000000 -65 | 1 1111111 0 0111111 -127 | 1 1111111 0 0000001 -128 | 1 1111111 0 0000000 -129 | 1 1111110 0 0111111 -8191 | 1 1000000 0 0000001 -8192 | 1 1000000 0 0000000 -8193 | 1 1111111 1 0111111 0 1111111 -} -- This might not work well for 32bit platform 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' -- This might not work well for 32bit platform 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-- }}}