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
beamInt :: Int64 -> Builder
beamInt !n = fromWrite $ Write.boundedWrite 10 $ beamIntPoke n
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
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