{-# LANGUAGE BangPatterns #-} module Data.Beamable.Int ( beamInt , unbeamInt , pokeWord8 ) where import Data.Beamable.Splits import Blaze.ByteString.Builder import qualified Blaze.ByteString.Builder.Internal.Write as Write import Data.Bits ((.|.), (.&.), shift, shiftR) import Data.Int (Int64) import Data.Monoid ((<>)) import Foreign.Storable import GHC.Word import qualified Data.ByteString as B {- Beamed int representation: 1. The integer is chunked up into 7-bit groups, little-endian. 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 last octet represents sign. 0 | 0 0000000 1 | 0 0000001 63 | 0 0111111 64 | 1 1000000 0 0000000 127 | 1 1111111 0 0000000 128 | 1 0000000 0 0000001 8191 | 1 1111111 0 0111111 8192 | 1 0000000 1 1000000 0 0000000 65535 | 1 1111111 1 1111111 0 0000011 -1 | 0 1111111 -64 | 0 1000000 -65 | 1 0111111 0 1111111 -127 | 1 0000001 0 1111111 -128 | 1 0000000 0 1111111 -129 | 1 1111111 0 1111110 -8191 | 1 0000001 0 1000000 -8192 | 1 0000000 0 1000000 -8193 | 1 1111111 1 0111111 0 1111111 -} beamInt :: Int64 -> Builder beamInt !n = fromWrite $ Write.boundedWrite 10 $ beamIntPoke n {-# INLINE beamInt #-} beamIntPoke :: Int64 -> Write.Poke beamIntPoke n | isZeroOrMinus1 (n `shiftR` 6) = pokeWord8 firstSeptet | otherwise = pokeWord8 (firstSeptet .|. 0x80) <> beamIntPoke next where firstSeptet :: Word8 firstSeptet = fromIntegral $ n .&. 0x7F next = n `shiftR` 7 isZeroOrMinus1 x = (x + 1) `shiftR` 1 == 0 pokeWord8 :: Word8 -> Write.Poke pokeWord8 w = Write.pokeN 1 $ flip poke w {-# INLINE unbeamInt #-} -- This might not work well for 32bit platform unbeamInt :: B.ByteString -> (Int64, B.ByteString) unbeamInt bs = (fixSign (B.foldr' f 0 this), rest) where f :: Word8 -> Int64 -> Int64 f w i = (i `shift` 7) .|. fromIntegral (w .&. 0x7F) fixSign :: Int64 -> Int64 fixSign x = x `shift` (64 - l * 7) `shift` (l * 7 - 64) !l = B.length this !(!this, !rest) = splitAtLastWord bs -- }}}