{- Copyright 2020 Awake Networks Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -} -- | This module differs from the "Data.ByteString.Builder" module by -- writing the octets in reverse order, which lets us compute the length -- of a submessage by writing that submessage and measuring its length -- before we write a variadic integer prefix encoding that length. -- -- Example use: -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (withLengthOf (word64Base128LEVar . fromIntegral) (word32BE 42 <> charUtf8 'λ'))) -- [6,0,0,0,42,206,187] {-# LANGUAGE BangPatterns #-} module Proto3.Wire.Reverse ( -- * `BuildR` type BuildR -- * Create `BuildR`s , etaBuildR , ensure , withLengthOf , byteString , lazyByteString , shortByteString , word8 , int8 , word16BE , word16LE , int16BE , int16LE , word32BE , word32LE , int32BE , int32LE , word64BE , word64LE , int64BE , int64LE , floatBE , floatLE , doubleBE , doubleLE , char7 , string7 , char8 , string8 , charUtf8 , stringUtf8 , textUtf8 , lazyTextUtf8 , wordBase128LEVar , wordBase128LEVar_inline , word32Base128LEVar , word32Base128LEVar_inline , word64Base128LEVar , word64Base128LEVar_inline , vectorBuildR -- * Consume `BuildR`s , runBuildR , toLazyByteString -- * Helpful combinators , foldlRVector -- * Exported for testing purposes only. , testWithUnused ) where import Data.Bits ( (.&.) ) import qualified Data.ByteString as B import qualified Data.ByteString.Internal as BI import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Internal as BLI import qualified Data.ByteString.Short as BS import qualified Data.ByteString.Short.Internal as BSI import qualified Data.ByteString.Unsafe as BU import Data.Char ( ord ) import Data.Int ( Int8, Int16, Int32, Int64 ) import qualified Data.Text as T import qualified Data.Text.Internal as TI import qualified Data.Text.Internal.Fusion as TIF import qualified Data.Text.Lazy as TL import Data.Vector.Generic ( Vector ) import Data.Word ( Word8, Word16, Word32, Word64 ) import Foreign ( castPtr ) import Proto3.Wire.Reverse.Internal import qualified Proto3.Wire.Reverse.Prim as Prim -- $setup -- >>> :set -XOverloadedStrings -- >>> :module Proto3.Wire.Reverse -- | Create a lazy `BL.ByteString` from a `BuildR` -- -- > toLazyByteString (x <> y) = toLazyByteString x <> toLazyByteString y -- > -- > toLazyByteString mempty = mempty -- -- >>> toLazyByteString (stringUtf8 "ABC") -- "ABC" toLazyByteString :: BuildR -> BL.ByteString toLazyByteString = snd . runBuildR -- | Convert a strict `B.ByteString` to a `BuildR` -- -- > byteString (x <> y) = byteString x <> byteString y -- > -- > byteString mempty = mempty -- -- >>> byteString "ABC" -- Proto3.Wire.Reverse.lazyByteString "ABC" byteString :: B.ByteString -> BuildR byteString bs = withUnused $ \unused -> let len = B.length bs in if len <= unused then unsafeConsume len $ \dst -> BU.unsafeUseAsCString bs $ \src -> BI.memcpy dst (castPtr src) len else prependChunk bs -- | Convert a lazy `BL.ByteString` to a `BuildR` -- -- Warning: evaluating the length will force the lazy `BL.ByteString`'s chunks, -- and they will remain allocated until you finish using the builder. -- -- > lazyByteString (x <> y) = lazyByteString x <> lazyByteString y -- > -- > lazyByteString mempty = mempty -- -- > lazyByteString . toLazyByteString = id -- > -- > toLazyByteString . lazyByteString = id -- -- >>> lazyByteString "ABC" -- Proto3.Wire.Reverse.lazyByteString "ABC" lazyByteString :: BL.ByteString -> BuildR lazyByteString = etaBuildR $ scan (ReverseChunks BL.empty) where scan :: ReverseChunks -> BL.ByteString -> BuildR scan r BLI.Empty = prepend r scan (ReverseChunks r) (BLI.Chunk c cs) = scan (ReverseChunks (BLI.Chunk c r)) cs prepend :: ReverseChunks -> BuildR prepend (ReverseChunks BLI.Empty) = mempty prepend (ReverseChunks (BLI.Chunk c cs)) = withUnused $ \unused -> let len = B.length c in if len <= unused then (prepend (ReverseChunks cs) <>) $ unsafeConsume len $ \dst -> BU.unsafeUseAsCString c $ \src -> BI.memcpy dst (castPtr src) len else prependReverseChunks (ReverseChunks(BLI.Chunk c cs)) -- | Convert a `BS.ShortByteString` to a `BuildR` -- -- > shortByteString (x <> y) = shortByteString x <> shortByteString y -- > -- > shortByteString mempty = mempty -- -- >>> shortByteString "ABC" -- Proto3.Wire.Reverse.lazyByteString "ABC" shortByteString :: BS.ShortByteString -> BuildR shortByteString bs = withUnused $ \unused -> let len = BS.length bs in if len <= unused then writeChunk bs 0 len else let rest = len - unused in writeChunk bs unused rest <> reallocate rest <> writeChunk bs 0 unused where writeChunk src off len = unsafeConsume len $ \dst -> BSI.copyToPtr src off dst len -- | Convert a `Word8` to a `BuildR` -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word8 42)) -- [42] word8 :: Word8 -> BuildR word8 = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.word8 x)) {-# INLINE word8 #-} -- | Convert a `Int8` to a `BuildR` -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (int8 (-5))) -- [251] int8 :: Int8 -> BuildR int8 = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.int8 x)) {-# INLINE int8 #-} -- | Convert a `Word16` to a `BuildR` by storing the bytes in big-endian order -- -- In other words, the most significant byte is stored first and the least -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word16BE 42)) -- [0,42] word16BE :: Word16 -> BuildR word16BE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.word16BE x)) {-# INLINE word16BE #-} -- | Convert a `Word16` to a `BuildR` by storing the bytes in little-endian -- order -- -- In other words, the least significant byte is stored first and the most -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word16LE 42)) -- [42,0] word16LE :: Word16 -> BuildR word16LE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.word16LE x)) {-# INLINE word16LE #-} -- | Convert an `Int16` to a `BuildR` by storing the bytes in big-endian order -- -- In other words, the most significant byte is stored first and the least -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (int16BE (-5))) -- [255,251] int16BE :: Int16 -> BuildR int16BE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.int16BE x)) {-# INLINE int16BE #-} -- | Convert an `Int16` to a `BuildR` by storing the bytes in little-endian -- order -- -- In other words, the least significant byte is stored first and the most -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (int16LE (-5))) -- [251,255] int16LE :: Int16 -> BuildR int16LE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.int16LE x)) {-# INLINE int16LE #-} -- | Convert a `Word32` to a `BuildR` by storing the bytes in big-endian order -- -- In other words, the most significant byte is stored first and the least -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32BE 42)) -- [0,0,0,42] word32BE :: Word32 -> BuildR word32BE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.word32BE x)) {-# INLINE word32BE #-} -- | Convert a `Word32` to a `BuildR` by storing the bytes in little-endian -- order -- -- In other words, the least significant byte is stored first and the most -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32LE 42)) -- [42,0,0,0] word32LE :: Word32 -> BuildR word32LE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.word32LE x)) {-# INLINE word32LE #-} -- | Convert an `Int32` to a `BuildR` by storing the bytes in big-endian order -- -- In other words, the most significant byte is stored first and the least -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (int32BE (-5))) -- [255,255,255,251] int32BE :: Int32 -> BuildR int32BE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.int32BE x)) {-# INLINE int32BE #-} -- | Convert an `Int32` to a `BuildR` by storing the bytes in little-endian -- order -- -- In other words, the least significant byte is stored first and the most -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (int32LE (-5))) -- [251,255,255,255] int32LE :: Int32 -> BuildR int32LE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.int32LE x)) {-# INLINE int32LE #-} -- | Convert a `Word64` to a `BuildR` by storing the bytes in big-endian order -- -- In other words, the most significant byte is stored first and the least -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64BE 42)) -- [0,0,0,0,0,0,0,42] word64BE :: Word64 -> BuildR word64BE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.word64BE x)) {-# INLINE word64BE #-} -- | Convert a `Word64` to a `BuildR` by storing the bytes in little-endian -- order -- -- In other words, the least significant byte is stored first and the most -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64LE 42)) -- [42,0,0,0,0,0,0,0] word64LE :: Word64 -> BuildR word64LE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.word64LE x)) {-# INLINE word64LE #-} -- | Convert an `Int64` to a `BuildR` by storing the bytes in big-endian order -- -- In other words, the most significant byte is stored first and the least -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (int64BE (-5))) -- [255,255,255,255,255,255,255,251] int64BE :: Int64 -> BuildR int64BE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.int64BE x)) {-# INLINE int64BE #-} -- | Convert an `Int64` to a `BuildR` by storing the bytes in little-endian -- order -- -- In other words, the least significant byte is stored first and the most -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (int64LE (-5))) -- [251,255,255,255,255,255,255,255] int64LE :: Int64 -> BuildR int64LE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.int64LE x)) {-# INLINE int64LE #-} -- | Convert a `Float` to a `BuildR` by storing the bytes in IEEE-754 format in -- big-endian order -- -- In other words, the most significant byte is stored first and the least -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (floatBE 4.2)) -- [64,134,102,102] floatBE :: Float -> BuildR floatBE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.floatBE x)) {-# INLINE floatBE #-} -- | Convert a `Float` to a `BuildR` by storing the bytes in IEEE-754 format in -- little-endian order -- -- In other words, the least significant byte is stored first and the most -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (floatLE 4.2)) -- [102,102,134,64] floatLE :: Float -> BuildR floatLE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.floatLE x)) {-# INLINE floatLE #-} -- | Convert a `Double` to a `BuildR` by storing the bytes in IEEE-754 format -- in big-endian order -- -- In other words, the most significant byte is stored first and the least -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (doubleBE 4.2)) -- [64,16,204,204,204,204,204,205] doubleBE :: Double -> BuildR doubleBE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.doubleBE x)) {-# INLINE doubleBE #-} -- | Convert a `Double` to a `BuildR` by storing the bytes in IEEE-754 format -- in little-endian order -- -- In other words, the least significant byte is stored first and the most -- significant byte is stored last -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (doubleLE 4.2)) -- [205,204,204,204,204,204,16,64] doubleLE :: Double -> BuildR doubleLE = \x -> Prim.liftBoundedPrim (Prim.liftFixedPrim (Prim.doubleLE x)) {-# INLINE doubleLE #-} -- | Convert an @ASCII@ `Char` to a `BuildR` -- -- __Careful:__ If you provide a Unicode character that is not part of the -- @ASCII@ alphabet this will only encode the lowest 7 bits -- -- >>> char7 ';' -- Proto3.Wire.Reverse.lazyByteString ";" -- >>> char7 'λ' -- Example of truncation -- Proto3.Wire.Reverse.lazyByteString ";" char7 :: Char -> BuildR char7 = word8 . (0x7F .&.) . fromIntegral . ord {-# INLINE char7 #-} -- | Convert an @ASCII@ `String` to a `BuildR` -- -- __Careful:__ If you provide a Unicode `String` that has non-@ASCII@ -- characters then this will only encode the lowest 7 bits of each character -- -- > string7 (x <> y) = string7 x <> string7 y -- > -- > string7 mempty = mempty -- -- >>> string7 "ABC" -- Proto3.Wire.Reverse.lazyByteString "ABC" -- >>> string7 "←↑→↓" -- Example of truncation -- Proto3.Wire.Reverse.lazyByteString "\DLE\DC1\DC2\DC3" string7 :: String -> BuildR string7 = foldMap char7 -- TO DO: 'Data.ByteString.Builder' goes to considerably more effort. -- Could we do better here? -- | Convert an @ISO/IEC 8859-1@ `Char` to a `BuildR` -- -- __Careful:__ If you provide a Unicode character that is not part of the -- @ISO/IEC 8859-1@ alphabet then this will only encode the lowest 8 bits -- -- >>> char8 ';' -- Proto3.Wire.Reverse.lazyByteString ";" -- >>> char8 'λ' -- Example of truncation -- Proto3.Wire.Reverse.lazyByteString "\187" char8 :: Char -> BuildR char8 = word8 . fromIntegral . ord {-# INLINE char8 #-} -- | Convert an @ISO/IEC 8859-1@ `String` to a `BuildR` -- -- __Careful:__ If you provide a Unicode `String` that has non-@ISO/IEC 8859-1@ -- characters then this will only encode the lowest 8 bits of each character -- -- > string8 (x <> y) = string8 x <> string8 y -- > -- > string8 mempty = mempty -- -- >>> string8 "ABC" -- Proto3.Wire.Reverse.lazyByteString "ABC" -- >>> string8 "←↑→↓" -- Example of truncation -- Proto3.Wire.Reverse.lazyByteString "\144\145\146\147" string8 :: String -> BuildR string8 = foldMap char8 -- TO DO: 'Data.ByteString.Builder' goes to considerably more effort. -- Could we do better here? -- | Convert a Unicode `Char` to a `BuildR` using a @UTF-8@ encoding -- -- >>> charUtf8 'A' -- Proto3.Wire.Reverse.lazyByteString "A" -- >>> charUtf8 'λ' -- Proto3.Wire.Reverse.lazyByteString "\206\187" -- >>> charUtf8 (Data.Char.chr 0x7FF) -- Proto3.Wire.Reverse.lazyByteString "\223\191" -- >>> charUtf8 (Data.Char.chr 0x800) -- Proto3.Wire.Reverse.lazyByteString "\224\160\128" -- >>> charUtf8 (Data.Char.chr 0xFFFF) -- Proto3.Wire.Reverse.lazyByteString "\239\191\191" -- >>> charUtf8 (Data.Char.chr 0x10000) -- Proto3.Wire.Reverse.lazyByteString "\240\144\128\128" -- >>> charUtf8 (Data.Char.chr 0x10FFFF) -- Proto3.Wire.Reverse.lazyByteString "\244\143\191\191" charUtf8 :: Char -> BuildR charUtf8 = \x -> Prim.liftBoundedPrim (Prim.charUtf8 x) {-# INLINE charUtf8 #-} -- | Convert a Unicode `String` to a `BuildR` using a @UTF-8@ encoding -- -- > stringUtf8 (x <> y) = stringUtf8 x <> stringUtf8 y -- > -- > stringUtf8 mempty = mempty -- -- >>> stringUtf8 "ABC" -- Proto3.Wire.Reverse.lazyByteString "ABC" -- >>> stringUtf8 "←↑→↓" -- Proto3.Wire.Reverse.lazyByteString "\226\134\144\226\134\145\226\134\146\226\134\147" -- >>> Data.ByteString.Lazy.hPutStr System.IO.stdout (toLazyByteString (stringUtf8 "←↑→↓\n")) -- ←↑→↓ stringUtf8 :: String -> BuildR stringUtf8 = foldMap charUtf8 -- TO DO: 'Data.ByteString.Builder' goes to considerably more effort. -- Could we do better here? -- | Convert a Unicode strict `T.Text` to a `BuildR` using a @UTF-8@ encoding -- -- > textUtf8 (x <> y) = textUtf8 x <> textUtf8 y -- > -- > textUtf8 mempty = mempty -- -- >>> textUtf8 "ABC" -- Proto3.Wire.Reverse.lazyByteString "ABC" -- >>> textUtf8 "←↑→↓" -- Proto3.Wire.Reverse.lazyByteString "\226\134\144\226\134\145\226\134\146\226\134\147" textUtf8 :: T.Text -> BuildR textUtf8 = etaBuildR $ \txt@(TI.Text _ _ word16Count) -> case TIF.reverseStream txt of TIF.Stream next t0 _ -> ensure bound (go t0) where -- Any non-surrogate UTF-16 word encodes a 'Char' whose UTF-8 -- encoding involves at most 3 octets. Any surrogate pair is -- two UTF-16 words that give rise to 4 octets. Therefore we -- will see at most 3 UTF-8 bytes per UTF-16 word of input. -- -- This is a significant overallocation in the ASCII case, -- where we see only one UTF-8 byte per UTF-16 word of input. -- If such overallocation becomes a problem, we could implement -- a prescan that computes the exact size required. -- -- However, we anticipate that in most cases we will be -- building from many text chunks that individually much -- smaller than the overall size of the combined result, -- making overallocation relatively harmless. bound = 3 * word16Count go = etaBuildR $ \t1 -> case next t1 of TIF.Done -> mempty TIF.Skip t2 -> go t2 TIF.Yield !ch t2 -> go t2 <> Prim.unsafeBuildBoundedPrim (Prim.charUtf8 ch) -- | Convert a Unicode lazy `TL.Text` to a `BuildR` using a @UTF-8@ encoding -- -- > lazyTextUtf8 (x <> y) = lazyTextUtf8 x <> lazyTextUtf8 y -- > -- > lazyTextUtf8 mempty = mempty -- -- >>> lazyTextUtf8 "ABC" -- Proto3.Wire.Reverse.lazyByteString "ABC" -- >>> lazyTextUtf8 "←↑→↓" -- Proto3.Wire.Reverse.lazyByteString "\226\134\144\226\134\145\226\134\146\226\134\147" lazyTextUtf8 :: TL.Text -> BuildR lazyTextUtf8 = TL.foldrChunks ((<>) . textUtf8) mempty -- | Convert a `Word` to a `BuildR` using this variable-length encoding: -- -- 1. Convert the given value to a base 128 representation -- without unnecessary digits (that is, omit zero digits -- unless they are less significant than nonzero digits). -- -- 2. Present those base-128 digits in order of increasing -- significance (that is, in little-endian order). -- -- 3. Add 128 to every digit except the most significant digit, -- yielding a sequence of octets terminated by one that is <= 127. -- -- This encoding is used in the wire format of Protocol Buffers version 3. -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar 42)) -- [42] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar 5376)) -- [128,42] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar (Data.Bits.shiftL 1 7 - 1))) -- [127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar (Data.Bits.shiftL 1 7))) -- [128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar (Data.Bits.shiftL 1 14 - 1))) -- [255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar (Data.Bits.shiftL 1 14))) -- [128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar (Data.Bits.shiftL 1 21 - 1))) -- [255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar (Data.Bits.shiftL 1 21))) -- [128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar (Data.Bits.shiftL 1 28 - 1))) -- [255,255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar (Data.Bits.shiftL 1 28))) -- [128,128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar (Data.Bits.shiftL 1 32 - 1))) -- [255,255,255,255,15] wordBase128LEVar :: Word -> BuildR wordBase128LEVar = \x -> Prim.liftBoundedPrim (Prim.wordBase128LEVar x) {-# INLINE wordBase128LEVar #-} -- | Like 'wordBase128LEVar' but inlined, which may bloat your code. On -- the other hand, inlining an application to a constant may shrink your code. -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar_inline 42)) -- [42] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar_inline 5376)) -- [128,42] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar_inline (Data.Bits.shiftL 1 7 - 1))) -- [127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar_inline (Data.Bits.shiftL 1 7))) -- [128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar_inline (Data.Bits.shiftL 1 14 - 1))) -- [255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar_inline (Data.Bits.shiftL 1 14))) -- [128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar_inline (Data.Bits.shiftL 1 21 - 1))) -- [255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar_inline (Data.Bits.shiftL 1 21))) -- [128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar_inline (Data.Bits.shiftL 1 28 - 1))) -- [255,255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar_inline (Data.Bits.shiftL 1 28))) -- [128,128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (wordBase128LEVar_inline (Data.Bits.shiftL 1 32 - 1))) -- [255,255,255,255,15] wordBase128LEVar_inline :: Word -> BuildR wordBase128LEVar_inline = \x -> Prim.liftBoundedPrim (Prim.wordBase128LEVar_inline x) {-# INLINE wordBase128LEVar_inline #-} -- | Convert a `Word32` to a `BuildR` using this variable-length encoding: -- -- 1. Convert the given value to a base 128 representation -- without unnecessary digits (that is, omit zero digits -- unless they are less significant than nonzero digits). -- -- 2. Present those base-128 digits in order of increasing -- significance (that is, in little-endian order). -- -- 3. Add 128 to every digit except the most significant digit, -- yielding a sequence of octets terminated by one that is <= 127. -- -- This encoding is used in the wire format of Protocol Buffers version 3. -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar 42)) -- [42] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar 5376)) -- [128,42] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar (Data.Bits.shiftL 1 7 - 1))) -- [127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar (Data.Bits.shiftL 1 7))) -- [128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar (Data.Bits.shiftL 1 14 - 1))) -- [255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar (Data.Bits.shiftL 1 14))) -- [128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar (Data.Bits.shiftL 1 21 - 1))) -- [255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar (Data.Bits.shiftL 1 21))) -- [128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar (Data.Bits.shiftL 1 28 - 1))) -- [255,255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar (Data.Bits.shiftL 1 28))) -- [128,128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar (Data.Bits.shiftL 1 32 - 1))) -- [255,255,255,255,15] word32Base128LEVar :: Word32 -> BuildR word32Base128LEVar = \x -> Prim.liftBoundedPrim (Prim.word32Base128LEVar x) {-# INLINE word32Base128LEVar #-} -- | Like 'word32Base128LEVar' but inlined, which may bloat your code. On -- the other hand, inlining an application to a constant may shrink your code. -- -- Currently 'word32Base128LEVar' is fully inline, so this makes no difference, -- but in future we might make different default space/speed tradeoffs. -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar_inline 42)) -- [42] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar_inline 5376)) -- [128,42] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar_inline (Data.Bits.shiftL 1 7 - 1))) -- [127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar_inline (Data.Bits.shiftL 1 7))) -- [128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar_inline (Data.Bits.shiftL 1 14 - 1))) -- [255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar_inline (Data.Bits.shiftL 1 14))) -- [128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar_inline (Data.Bits.shiftL 1 21 - 1))) -- [255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar_inline (Data.Bits.shiftL 1 21))) -- [128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar_inline (Data.Bits.shiftL 1 28 - 1))) -- [255,255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar_inline (Data.Bits.shiftL 1 28))) -- [128,128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word32Base128LEVar_inline (Data.Bits.shiftL 1 32 - 1))) -- [255,255,255,255,15] word32Base128LEVar_inline :: Word32 -> BuildR word32Base128LEVar_inline = \x -> Prim.liftBoundedPrim (Prim.word32Base128LEVar_inline x) {-# INLINE word32Base128LEVar_inline #-} -- | Like 'word32Base128LEVar' but for 64-bit inputs. -- -- Inlines when the value fits within 32 bits, but see -- also 'word64Base128LEVar_inline', which always inlines. -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar 42)) -- [42] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar 5376)) -- [128,42] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 7 - 1))) -- [127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 7))) -- [128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 14 - 1))) -- [255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 14))) -- [128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 21 - 1))) -- [255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 21))) -- [128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 28 - 1))) -- [255,255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 28))) -- [128,128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 32 - 1))) -- [255,255,255,255,15] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 32))) -- [128,128,128,128,16] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 56 - 1))) -- [255,255,255,255,255,255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 56))) -- [128,128,128,128,128,128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 60 - 1))) -- [255,255,255,255,255,255,255,255,15] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 60))) -- [128,128,128,128,128,128,128,128,16] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 63 - 1))) -- [255,255,255,255,255,255,255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (Data.Bits.shiftL 1 63))) -- [128,128,128,128,128,128,128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar (- (1 :: Data.Word.Word64)))) -- [255,255,255,255,255,255,255,255,255,1] word64Base128LEVar :: Word64 -> BuildR word64Base128LEVar = \x -> Prim.liftBoundedPrim (Prim.word64Base128LEVar x) {-# INLINE word64Base128LEVar #-} -- | Like 'word64Base128LEVar' but inlined, which may bloat your code. On -- the other hand, inlining an application to a constant may shrink your code. -- -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline 42)) -- [42] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline 5376)) -- [128,42] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 7 - 1))) -- [127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 7))) -- [128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 14 - 1))) -- [255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 14))) -- [128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 21 - 1))) -- [255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 21))) -- [128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 28 - 1))) -- [255,255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 28))) -- [128,128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 32 - 1))) -- [255,255,255,255,15] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 32))) -- [128,128,128,128,16] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 56 - 1))) -- [255,255,255,255,255,255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 56))) -- [128,128,128,128,128,128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 60 - 1))) -- [255,255,255,255,255,255,255,255,15] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 60))) -- [128,128,128,128,128,128,128,128,16] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 63 - 1))) -- [255,255,255,255,255,255,255,255,127] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (Data.Bits.shiftL 1 63))) -- [128,128,128,128,128,128,128,128,128,1] -- >>> Data.ByteString.Lazy.unpack (toLazyByteString (word64Base128LEVar_inline (- (1 :: Data.Word.Word64)))) -- [255,255,255,255,255,255,255,255,255,1] word64Base128LEVar_inline :: Word64 -> BuildR word64Base128LEVar_inline = \x -> Prim.liftBoundedPrim (Prim.word64Base128LEVar_inline x) {-# INLINE word64Base128LEVar_inline #-} -- | Essentially 'foldMap', but iterates right to left for efficiency. vectorBuildR :: Vector v a => (a -> BuildR) -> v a -> BuildR vectorBuildR f = etaBuildR (foldlRVector (\acc x -> acc <> f x) mempty) {-# INLINE vectorBuildR #-} -- | Exported for testing purposes only. testWithUnused :: (Int -> BuildR) -> BuildR testWithUnused = withUnused {-# WARNING testWithUnused "Exported for testing purposes only." #-}