{- | Variable-length integers (varints), a method to store arbitrarily large
     integers in a space efficient manner.

Note that varints aren't particularly efficient due to their decoding being
slow. They are most interesting when you wish to provide support for large
integers, but know that many (most?) inputs will be small, and want to be space
efficient for them. Protocol Buffers uses them extensively, while Cap'n Proto
swears them off.

TODO

  * https://en.wikipedia.org/wiki/Variable-length_quantity
    * I've defined basic unsigned varints. Signed varints have lots of options.
      You can use twos comp, zigzag, a sign bit, whatever.
-}

{-# LANGUAGE AllowAmbiguousTypes #-}

module Binrep.Type.Varint where

import Binrep
import Binrep.Type.Common ( Endianness(..) )

import Data.Bits
import FlatParse.Basic qualified as FP

import Data.Word ( Word8 )

-- | A variable-length unsigned integer (natural).
--
-- The base algorithm is to split the natural into groups of 7 bits, and use the
-- MSB to indicate whether another octet follows. You must specify a handful of
-- type variables, which select precise varint behaviour beyond this. See their
-- documentation for details.
--
-- You may select the type to use varnats at, but error handling isn't provided:
-- negatives won't work correctly, and overflow cannot be detected. So most of
-- the time, you probably want 'Natural' and 'Integer'.
--
-- Some examples:
--
--   * @'Varnat' ''Redundant' ''OnContinues'  ''BE' matches VLQ.
--   * @'Varnat' ''Redundant' ''OnContinues'  ''LE' matches LEB128, protobuf.
--   * @'Varnat' ''Bijective' ''OnContinues'  ''LE' matches Git's varints.
--   * @'Varnat' ''Bijective' ''OffContinues' ''LE' matches BPS's varints.
newtype Varnat (enc :: Encoding) (cont :: ContinuationBitBehaviour) (e :: Endianness) i = Varnat { forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Varnat enc cont e i -> i
getVarnat :: i }
    deriving (Varnat enc cont e i -> Varnat enc cont e i -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Eq i =>
Varnat enc cont e i -> Varnat enc cont e i -> Bool
/= :: Varnat enc cont e i -> Varnat enc cont e i -> Bool
$c/= :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Eq i =>
Varnat enc cont e i -> Varnat enc cont e i -> Bool
== :: Varnat enc cont e i -> Varnat enc cont e i -> Bool
$c== :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Eq i =>
Varnat enc cont e i -> Varnat enc cont e i -> Bool
Eq, Varnat enc cont e i -> Varnat enc cont e i -> Bool
Varnat enc cont e i -> Varnat enc cont e i -> Ordering
Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall {enc :: Encoding} {cont :: ContinuationBitBehaviour}
       {e :: Endianness} {i}.
Ord i =>
Eq (Varnat enc cont e i)
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Ord i =>
Varnat enc cont e i -> Varnat enc cont e i -> Bool
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Ord i =>
Varnat enc cont e i -> Varnat enc cont e i -> Ordering
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Ord i =>
Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
min :: Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
$cmin :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Ord i =>
Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
max :: Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
$cmax :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Ord i =>
Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
>= :: Varnat enc cont e i -> Varnat enc cont e i -> Bool
$c>= :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Ord i =>
Varnat enc cont e i -> Varnat enc cont e i -> Bool
> :: Varnat enc cont e i -> Varnat enc cont e i -> Bool
$c> :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Ord i =>
Varnat enc cont e i -> Varnat enc cont e i -> Bool
<= :: Varnat enc cont e i -> Varnat enc cont e i -> Bool
$c<= :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Ord i =>
Varnat enc cont e i -> Varnat enc cont e i -> Bool
< :: Varnat enc cont e i -> Varnat enc cont e i -> Bool
$c< :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Ord i =>
Varnat enc cont e i -> Varnat enc cont e i -> Bool
compare :: Varnat enc cont e i -> Varnat enc cont e i -> Ordering
$ccompare :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Ord i =>
Varnat enc cont e i -> Varnat enc cont e i -> Ordering
Ord, Int -> Varnat enc cont e i
Varnat enc cont e i -> Int
Varnat enc cont e i -> [Varnat enc cont e i]
Varnat enc cont e i -> Varnat enc cont e i
Varnat enc cont e i -> Varnat enc cont e i -> [Varnat enc cont e i]
Varnat enc cont e i
-> Varnat enc cont e i
-> Varnat enc cont e i
-> [Varnat enc cont e i]
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Enum i =>
Int -> Varnat enc cont e i
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Enum i =>
Varnat enc cont e i -> Int
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Enum i =>
Varnat enc cont e i -> [Varnat enc cont e i]
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Enum i =>
Varnat enc cont e i -> Varnat enc cont e i
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Enum i =>
Varnat enc cont e i -> Varnat enc cont e i -> [Varnat enc cont e i]
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Enum i =>
Varnat enc cont e i
-> Varnat enc cont e i
-> Varnat enc cont e i
-> [Varnat enc cont e i]
enumFromThenTo :: Varnat enc cont e i
-> Varnat enc cont e i
-> Varnat enc cont e i
-> [Varnat enc cont e i]
$cenumFromThenTo :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Enum i =>
Varnat enc cont e i
-> Varnat enc cont e i
-> Varnat enc cont e i
-> [Varnat enc cont e i]
enumFromTo :: Varnat enc cont e i -> Varnat enc cont e i -> [Varnat enc cont e i]
$cenumFromTo :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Enum i =>
Varnat enc cont e i -> Varnat enc cont e i -> [Varnat enc cont e i]
enumFromThen :: Varnat enc cont e i -> Varnat enc cont e i -> [Varnat enc cont e i]
$cenumFromThen :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Enum i =>
Varnat enc cont e i -> Varnat enc cont e i -> [Varnat enc cont e i]
enumFrom :: Varnat enc cont e i -> [Varnat enc cont e i]
$cenumFrom :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Enum i =>
Varnat enc cont e i -> [Varnat enc cont e i]
fromEnum :: Varnat enc cont e i -> Int
$cfromEnum :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Enum i =>
Varnat enc cont e i -> Int
toEnum :: Int -> Varnat enc cont e i
$ctoEnum :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Enum i =>
Int -> Varnat enc cont e i
pred :: Varnat enc cont e i -> Varnat enc cont e i
$cpred :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Enum i =>
Varnat enc cont e i -> Varnat enc cont e i
succ :: Varnat enc cont e i -> Varnat enc cont e i
$csucc :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Enum i =>
Varnat enc cont e i -> Varnat enc cont e i
Enum, Integer -> Varnat enc cont e i
Varnat enc cont e i -> Varnat enc cont e i
Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
forall a.
(a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a)
-> (a -> a)
-> (a -> a)
-> (Integer -> a)
-> Num a
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Num i =>
Integer -> Varnat enc cont e i
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Num i =>
Varnat enc cont e i -> Varnat enc cont e i
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Num i =>
Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
fromInteger :: Integer -> Varnat enc cont e i
$cfromInteger :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Num i =>
Integer -> Varnat enc cont e i
signum :: Varnat enc cont e i -> Varnat enc cont e i
$csignum :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Num i =>
Varnat enc cont e i -> Varnat enc cont e i
abs :: Varnat enc cont e i -> Varnat enc cont e i
$cabs :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Num i =>
Varnat enc cont e i -> Varnat enc cont e i
negate :: Varnat enc cont e i -> Varnat enc cont e i
$cnegate :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Num i =>
Varnat enc cont e i -> Varnat enc cont e i
* :: Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
$c* :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Num i =>
Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
- :: Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
$c- :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Num i =>
Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
+ :: Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
$c+ :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Num i =>
Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
Num, Varnat enc cont e i -> Rational
forall a. Num a -> Ord a -> (a -> Rational) -> Real a
forall {enc :: Encoding} {cont :: ContinuationBitBehaviour}
       {e :: Endianness} {i}.
Real i =>
Num (Varnat enc cont e i)
forall {enc :: Encoding} {cont :: ContinuationBitBehaviour}
       {e :: Endianness} {i}.
Real i =>
Ord (Varnat enc cont e i)
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Real i =>
Varnat enc cont e i -> Rational
toRational :: Varnat enc cont e i -> Rational
$ctoRational :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Real i =>
Varnat enc cont e i -> Rational
Real, Varnat enc cont e i -> Integer
Varnat enc cont e i
-> Varnat enc cont e i
-> (Varnat enc cont e i, Varnat enc cont e i)
Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
forall a.
Real a
-> Enum a
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> (a, a))
-> (a -> a -> (a, a))
-> (a -> Integer)
-> Integral a
forall {enc :: Encoding} {cont :: ContinuationBitBehaviour}
       {e :: Endianness} {i}.
Integral i =>
Enum (Varnat enc cont e i)
forall {enc :: Encoding} {cont :: ContinuationBitBehaviour}
       {e :: Endianness} {i}.
Integral i =>
Real (Varnat enc cont e i)
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Integral i =>
Varnat enc cont e i -> Integer
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Integral i =>
Varnat enc cont e i
-> Varnat enc cont e i
-> (Varnat enc cont e i, Varnat enc cont e i)
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Integral i =>
Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
toInteger :: Varnat enc cont e i -> Integer
$ctoInteger :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Integral i =>
Varnat enc cont e i -> Integer
divMod :: Varnat enc cont e i
-> Varnat enc cont e i
-> (Varnat enc cont e i, Varnat enc cont e i)
$cdivMod :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Integral i =>
Varnat enc cont e i
-> Varnat enc cont e i
-> (Varnat enc cont e i, Varnat enc cont e i)
quotRem :: Varnat enc cont e i
-> Varnat enc cont e i
-> (Varnat enc cont e i, Varnat enc cont e i)
$cquotRem :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Integral i =>
Varnat enc cont e i
-> Varnat enc cont e i
-> (Varnat enc cont e i, Varnat enc cont e i)
mod :: Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
$cmod :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Integral i =>
Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
div :: Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
$cdiv :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Integral i =>
Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
rem :: Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
$crem :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Integral i =>
Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
quot :: Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
$cquot :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Integral i =>
Varnat enc cont e i -> Varnat enc cont e i -> Varnat enc cont e i
Integral) via i
    deriving stock Int -> Varnat enc cont e i -> ShowS
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Show i =>
Int -> Varnat enc cont e i -> ShowS
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Show i =>
[Varnat enc cont e i] -> ShowS
forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Show i =>
Varnat enc cont e i -> String
showList :: [Varnat enc cont e i] -> ShowS
$cshowList :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Show i =>
[Varnat enc cont e i] -> ShowS
show :: Varnat enc cont e i -> String
$cshow :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Show i =>
Varnat enc cont e i -> String
showsPrec :: Int -> Varnat enc cont e i -> ShowS
$cshowsPrec :: forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
Show i =>
Int -> Varnat enc cont e i -> ShowS
Show

data ContinuationBitBehaviour
  = OnContinues
  -- ^ on=continue, off=end

  | OffContinues
  -- ^ on=end, off=continue

data Encoding
  = Redundant
  -- ^ simple, some varints have the same value

  | Bijective
  -- ^ each integer has exactly 1 varint encoding

-- | VLQ (cont=on)
instance (VarintContinuation cont, Integral i, Bits i) => Get (Varnat 'Redundant cont 'BE i) where
    get :: Getter (Varnat 'Redundant cont 'BE i)
get = i -> Getter (Varnat 'Redundant cont 'BE i)
go (i
0 :: i)
      where
        go :: i -> Getter (Varnat 'Redundant cont 'BE i)
go i
i = do
            Word8
w8 <- forall e. Parser e Word8
FP.anyWord8
            let i' :: i
i' = forall a. Bits a => a -> Int -> a
unsafeShiftL i
i Int
7 forall a. Bits a => a -> a -> a
.|. forall a b. (Integral a, Num b) => a -> b
fromIntegral (forall a. Bits a => a -> Int -> a
clearBit Word8
w8 Int
7)
            if forall (cont :: ContinuationBitBehaviour) a.
(VarintContinuation cont, Bits a) =>
a -> Int -> Bool
testVarintCont @cont Word8
w8 Int
7 then i -> Getter (Varnat 'Redundant cont 'BE i)
go i
i' else forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
i -> Varnat enc cont e i
Varnat i
i')

-- | TODO nothing to test against - unsure if correct
instance (VarintContinuation cont, Integral i, Bits i) => Get (Varnat 'Bijective cont 'BE i) where
    get :: Getter (Varnat 'Bijective cont 'BE i)
get = i -> Getter (Varnat 'Bijective cont 'BE i)
go (i
0 :: i)
      where
        go :: i -> Getter (Varnat 'Bijective cont 'BE i)
go i
i = do
            Word8
w8 <- forall e. Parser e Word8
FP.anyWord8
            let i' :: i
i' = forall a. Bits a => a -> Int -> a
unsafeShiftL i
i Int
7 forall a. Bits a => a -> a -> a
.|. (forall a b. (Integral a, Num b) => a -> b
fromIntegral (forall a. Bits a => a -> Int -> a
clearBit Word8
w8 Int
7) forall a. Num a => a -> a -> a
+ i
1)
            if forall (cont :: ContinuationBitBehaviour) a.
(VarintContinuation cont, Bits a) =>
a -> Int -> Bool
testVarintCont @cont Word8
w8 Int
7 then i -> Getter (Varnat 'Bijective cont 'BE i)
go i
i' else forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
i -> Varnat enc cont e i
Varnat (i
i'forall a. Num a => a -> a -> a
-i
1))

-- | protobuf (cont=on), LEB128 (cont=on)
--
-- not truly infinite length since shifters take 'Int', but practically infinite
instance (VarintContinuation cont, Integral i, Bits i) => Get (Varnat 'Redundant cont 'LE i) where
    get :: Getter (Varnat 'Redundant cont 'LE i)
get = i -> Int -> Getter (Varnat 'Redundant cont 'LE i)
go (i
0 :: i) (Int
0 :: Int)
      where
        go :: i -> Int -> Getter (Varnat 'Redundant cont 'LE i)
go i
i Int
n = do
            Word8
w8 <- forall e. Parser e Word8
FP.anyWord8
            let i' :: i
i' = i
i forall a. Bits a => a -> a -> a
.|. forall a. Bits a => a -> Int -> a
unsafeShiftL (forall a b. (Integral a, Num b) => a -> b
fromIntegral (forall a. Bits a => a -> Int -> a
clearBit Word8
w8 Int
7)) Int
n
            if forall (cont :: ContinuationBitBehaviour) a.
(VarintContinuation cont, Bits a) =>
a -> Int -> Bool
testVarintCont @cont Word8
w8 Int
7 then i -> Int -> Getter (Varnat 'Redundant cont 'LE i)
go i
i' (Int
nforall a. Num a => a -> a -> a
+Int
7) else forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
i -> Varnat enc cont e i
Varnat i
i')

-- | Git varint (cont=on), BPS (beat patches) (cont=off)
instance (VarintContinuation cont, Integral i, Bits i) => Get (Varnat 'Bijective cont 'LE i) where
    get :: Getter (Varnat 'Bijective cont 'LE i)
get = i -> Int -> Getter (Varnat 'Bijective cont 'LE i)
go (i
0 :: i) (Int
0 :: Int)
      where
        go :: i -> Int -> Getter (Varnat 'Bijective cont 'LE i)
go i
i Int
n = do
            Word8
w8 <- forall e. Parser e Word8
FP.anyWord8
            let i' :: i
i' = i
i forall a. Bits a => a -> a -> a
.|. forall a. Bits a => a -> Int -> a
unsafeShiftL (forall a b. (Integral a, Num b) => a -> b
fromIntegral (forall a. Bits a => a -> Int -> a
clearBit Word8
w8 Int
7) forall a. Num a => a -> a -> a
+ i
1) Int
n
            if forall (cont :: ContinuationBitBehaviour) a.
(VarintContinuation cont, Bits a) =>
a -> Int -> Bool
testVarintCont @cont Word8
w8 Int
7 then i -> Int -> Getter (Varnat 'Bijective cont 'LE i)
go i
i' (Int
nforall a. Num a => a -> a -> a
+Int
7) else forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
i -> Varnat enc cont e i
Varnat (i
i'forall a. Num a => a -> a -> a
-i
1))

-- TODO uses fromIntegral's overflow behaviour
instance (VarintContinuation cont, Integral i, Bits i) => Put (Varnat 'Redundant cont 'LE i) where
    put :: Varnat 'Redundant cont 'LE i -> Builder
put (Varnat i
i) = do
        if i
i forall a. Ord a => a -> a -> Bool
< i
0b10000000 then
            forall a. Put a => a -> Builder
put @Word8 forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fromIntegral i
i
        else
               forall a. Put a => a -> Builder
put @Word8 (forall (cont :: ContinuationBitBehaviour) a.
(VarintContinuation cont, Bits a) =>
a -> Int -> a
setVarintCont @cont (forall a b. (Integral a, Num b) => a -> b
fromIntegral i
i) Int
7)
            forall a. Semigroup a => a -> a -> a
<> forall a. Put a => a -> Builder
put @(Varnat 'Redundant cont 'LE i) (forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
i -> Varnat enc cont e i
Varnat (forall a. Bits a => a -> Int -> a
unsafeShiftR i
i Int
7))

-- TODO BE. Hard.
instance (VarintContinuation cont, Integral i, Bits i) => Put (Varnat 'Redundant cont 'BE i) where
    put :: Varnat 'Redundant cont 'BE i -> Builder
put (Varnat i
i) = do
        if i
i forall a. Ord a => a -> a -> Bool
< i
0b10000000 then
            forall a. Put a => a -> Builder
put @Word8 forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fromIntegral i
i
        else
               forall a. Put a => a -> Builder
put @(Varnat 'Redundant cont 'LE i) (forall (enc :: Encoding) (cont :: ContinuationBitBehaviour)
       (e :: Endianness) i.
i -> Varnat enc cont e i
Varnat (forall a. Bits a => a -> Int -> a
unsafeShiftR i
i Int
7))
            forall a. Semigroup a => a -> a -> a
<> forall a. Put a => a -> Builder
put @Word8 (forall (cont :: ContinuationBitBehaviour) a.
(VarintContinuation cont, Bits a) =>
a -> Int -> a
setVarintCont @cont (forall a b. (Integral a, Num b) => a -> b
fromIntegral (i
i forall a. Bits a => a -> a -> a
.&. i
0b11111111)) Int
7)

--------------------------------------------------------------------------------

class VarintContinuation (cont :: ContinuationBitBehaviour) where
    varintContinue :: Bool
instance VarintContinuation 'OnContinues  where varintContinue :: Bool
varintContinue = Bool
True
instance VarintContinuation 'OffContinues where varintContinue :: Bool
varintContinue = Bool
False

testVarintCont
    :: forall cont a. VarintContinuation cont => Bits a => a -> Int -> Bool
testVarintCont :: forall (cont :: ContinuationBitBehaviour) a.
(VarintContinuation cont, Bits a) =>
a -> Int -> Bool
testVarintCont a
a Int
n = case forall (cont :: ContinuationBitBehaviour).
VarintContinuation cont =>
Bool
varintContinue @cont of Bool
True  -> Bool
b
                                                  Bool
False -> Bool -> Bool
not Bool
b
  where b :: Bool
b = forall a. Bits a => a -> Int -> Bool
testBit a
a Int
n

setVarintCont
    :: forall cont a. VarintContinuation cont => Bits a => a -> Int -> a
setVarintCont :: forall (cont :: ContinuationBitBehaviour) a.
(VarintContinuation cont, Bits a) =>
a -> Int -> a
setVarintCont = case forall (cont :: ContinuationBitBehaviour).
VarintContinuation cont =>
Bool
varintContinue @cont of Bool
True  -> forall a. Bits a => a -> Int -> a
setBit
                                             Bool
False -> forall a. Bits a => a -> Int -> a
clearBit