{-# LANGUAGE BinaryLiterals #-} module Network.QUIC.Types.Integer ( encodeInt, encodeInt8, encodeInt', encodeInt'2, encodeInt'4, decodeInt, decodeInt', ) where import Data.ByteString.Internal (unsafeCreate) import Foreign.Ptr import Foreign.Storable import System.IO.Unsafe (unsafeDupablePerformIO) import Network.QUIC.Imports -- $setup -- >>> :set -XOverloadedStrings -- >>> import Network.QUIC.Utils ---------------------------------------------------------------- -- | -- >>> enc16 $ encodeInt 151288809941952652 -- "c2197c5eff14e88c" -- >>> enc16 $ encodeInt 494878333 -- "9d7f3e7d" -- >>> enc16 $ encodeInt 15293 -- "7bbd" -- >>> enc16 $ encodeInt 37 -- "25" encodeInt :: Int64 -> ByteString encodeInt i = unsafeCreate n $ go tag n i' where (tag, n, i') = tagLen i {-# NOINLINE encodeInt #-} encodeInt8 :: Int64 -> ByteString encodeInt8 i = unsafeCreate n $ go tag n i where n = 8 tag = 0b11000000 {-# NOINLINE encodeInt8 #-} encodeInt' :: WriteBuffer -> Int64 -> IO () encodeInt' wbuf i = go' tag n i' wbuf where (tag, n, i') = tagLen i encodeInt'2 :: WriteBuffer -> Int64 -> IO () encodeInt'2 wbuf i = go' tag n i' wbuf where tag = 0b01000000 n = 2 i' = i !<<. 48 encodeInt'4 :: WriteBuffer -> Int64 -> IO () encodeInt'4 wbuf i = go' tag n i' wbuf where tag = 0b10000000 n = 4 i' = i !<<. 32 tagLen :: Int64 -> (Word8, Int, Int64) tagLen i | i <= 63 = (0b00000000, 1, i !<<. 56) | i <= 16383 = (0b01000000, 2, i !<<. 48) | i <= 1073741823 = (0b10000000, 4, i !<<. 32) | otherwise = (0b11000000, 8, i) {-# INLINE tagLen #-} msb8 :: Int64 -> Word8 msb8 i = fromIntegral (i !>>. 56) {-# INLINE msb8 #-} go :: Word8 -> Int -> Int64 -> Ptr Word8 -> IO () go tag n0 i0 p0 = do poke p0 (tag .|. msb8 i0) let n' = n0 - 1 i' = i0 !<<. 8 p' = p0 `plusPtr` 1 loop n' i' p' where loop 0 _ _ = return () loop n i p = do poke p $ msb8 i let n' = n - 1 i' = i !<<. 8 p' = p `plusPtr` 1 loop n' i' p' {-# INLINE go #-} go' :: Word8 -> Int -> Int64 -> WriteBuffer -> IO () go' tag n0 i0 wbuf = do write8 wbuf (tag .|. msb8 i0) let n' = n0 - 1 i' = i0 !<<. 8 loop n' i' where loop 0 _ = return () loop n i = do write8 wbuf $ msb8 i let n' = n - 1 i' = i !<<. 8 loop n' i' {-# INLINE go' #-} ---------------------------------------------------------------- -- | -- >>> decodeInt (dec16 "c2197c5eff14e88c") -- 151288809941952652 -- >>> decodeInt (dec16 "9d7f3e7d") -- 494878333 -- >>> decodeInt (dec16 "7bbd") -- 15293 -- >>> decodeInt (dec16 "25") -- 37 decodeInt :: ByteString -> Int64 decodeInt bs = unsafeDupablePerformIO $ withReadBuffer bs decodeInt' {-# NOINLINE decodeInt #-} decodeInt' :: ReadBuffer -> IO Int64 decodeInt' rbuf = do b0 <- read8 rbuf let flag = b0 !>>. 6 b1 = fromIntegral (b0 .&. 0b00111111) case flag of 0 -> return b1 1 -> loop b1 1 2 -> loop b1 3 _ -> loop b1 7 where loop :: Int64 -> Int -> IO Int64 loop r 0 = return r loop r n = do b <- fromIntegral <$> read8 rbuf loop (r * 256 + b) (n - 1)