packages feed

quic-0.1.0: Network/QUIC/Types/Integer.hs

{-# 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)