{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Data.Serialize.Varint
       ( Varint(..)
       , getVarint
       , putVarint
       ) where

import Data.Bits
import GHC.Word (Word8)

import Data.Serialize (Serialize(..), Get, Put, putWord8, getWord8)

newtype Varint a = Varint a
                 deriving (Eq, Num, Ord, Real, Integral, Enum, Show, Bits)

getVarint :: (Num a, Bits a) => Word8 -> Get (Varint a)
getVarint n
  | testBit n 7 = do
      Varint m <- getWord8 >>= getVarint
      pure (shiftL m 7 .|. clearBit (fromIntegral n) 7)
  | otherwise = pure (fromIntegral n)


putVarint :: (Integral a, Bits a) => a -> Put
putVarint n
  | n < 0x80 = putWord8 (fromIntegral n)
  | otherwise = do
      putWord8 (setBit (fromIntegral n) 7)
      putVarint (shiftR n 7)

instance (Bits a, Integral a) => Serialize (Varint a) where
  get = getWord8 >>= getVarint
  put = putVarint