{-# LANGUAGE FlexibleContexts #-} -- | -- Copyright: 2020 Input Output (Hong Kong) Ltd., 2021-2022 Input Output Global Inc. (IOG), 2023-2025 Intersect -- License: Apache-2.0 module Data.Word7 ( -- * Types Word7 -- * Conversions , toWord7 , toWord8 , toWord7s , toNatural -- * Encode / Decode , putVariableLengthNat , getVariableLengthNat ) where import Prelude import Data.Binary.Get ( Get, getWord8 ) import Data.Binary.Put ( Put, putWord8 ) import Data.Bits ( shiftL, shiftR, (.&.), (.|.) ) import Data.List ( foldl' ) import Data.Word ( Word8 ) import Numeric.Natural ( Natural ) -- | A 'Word7' algebraic data-type. -- -- @since 2.0.0 newtype Word7 = Word7 Word8 deriving (Eq, Show) -- -- Conversions -- -- > toWord7 1 -- > Word7 1 -- > toWord7 127 -- > Word7 127 -- > toWord7 128 -- > Word7 0 toWord7 :: Word8 -> Word7 toWord7 x = Word7 (x .&. 0x7F) toWord8 :: Word7 -> Word8 toWord8 (Word7 x) = x -- > toWord7s 1 -- > [Word7 1] -- > toWord7s 128 -- > [Word7 1,Word7 0] -- > toWord7s 19099 -- > [Word7 1,Word7 21,Word7 27] toWord7s :: Natural -> [Word7] toWord7s = reverse . go where go n | n <= 0x7F = [Word7 . fromIntegral $ n] | otherwise = (toWord7 . fromIntegral) n : go (shiftR n 7) word7sToNat :: [Word7] -> Natural word7sToNat = foldl' f 0 where f n (Word7 r) = shiftL n 7 .|. (fromIntegral r) toNatural :: [Word7] -> Natural toNatural = fst . foldr (\(Word7 x) (res, pow) -> (res + (fromIntegral x)*(limit pow + 1), pow + 7) ) (0,0) where limit :: Int -> Natural limit pow = 2 ^ pow - 1 -- -- Decoding -- putVariableLengthNat :: Natural -> Put putVariableLengthNat = putWord7s . toWord7s where putWord7s :: [Word7] -> Put putWord7s [] = pure () putWord7s [Word7 x] = putWord8 x putWord7s (Word7 x : xs) = putWord8 (x .|. 0x80) >> putWord7s xs getVariableLengthNat :: Get Natural getVariableLengthNat = word7sToNat <$> getWord7s where getWord7s :: Get [Word7] getWord7s = do next <- getWord8 case next .&. 0x80 of 0x80 -> (:) (toWord7 next) <$> getWord7s _ -> pure [Word7 next]