From 3ad5261ff4da4bae388a400babca690f79115d04 Mon Sep 17 00:00:00 2001 From: Andrew Martin Date: Wed, 9 Oct 2019 09:48:28 -0400 Subject: [PATCH] Add little-endian encoding functions for Word{16,32,64}. Add machine encoding functions for 64-bit signed int. These just convert it to an unsigned int and call the corresponding encoding function. --- CHANGELOG.md | 2 ++ src/Data/ByteArray/Builder.hs | 45 +++++++++++++++++++++++-- src/Data/ByteArray/Builder/Bounded.hs | 48 ++++++++++++++++++++++++++- 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cfcc59..bc027df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ * Introduce `consLensBE32` for efficient serialization of wire protocols that require prefixing a payload with its length. +* Add `int64BE` as a convenience. +* Add little-endian encoding functions for `Word16`, `Word32`, and `Word64`. ## 0.2.1.0 -- 2019-09-05 diff --git a/src/Data/ByteArray/Builder.hs b/src/Data/ByteArray/Builder.hs index 053018b..56566b2 100644 --- a/src/Data/ByteArray/Builder.hs +++ b/src/Data/ByteArray/Builder.hs @@ -54,10 +54,20 @@ module Data.ByteArray.Builder , ascii , char -- ** Machine-Readable + -- *** One + , word8 + -- **** Big Endian , word64BE , word32BE , word16BE - , word8 + , int64BE + -- **** Little Endian + , word64LE + , word32LE + , word16LE + , int64LE + -- *** Many + , word8Array -- ** Prefixing with Length , consLength32BE , consLength64BE @@ -75,7 +85,7 @@ import Data.ByteString.Short.Internal (ShortByteString(SBS)) import Data.Bytes.Types (Bytes(Bytes),MutableBytes(MutableBytes)) import Data.Char (ord) import Data.Int (Int64,Int32,Int16,Int8) -import Data.Primitive (ByteArray(..),MutableByteArray(..)) +import Data.Primitive (ByteArray(..),MutableByteArray(..),PrimArray(..)) import Data.Primitive.ByteArray.Offset (MutableByteArrayOffset(..)) import Data.Text.Short (ShortText) import Data.Word (Word64,Word32,Word16,Word8) @@ -218,6 +228,12 @@ bytes (Bytes src soff slen) = construct $ \(MutableBytes arr off len) -> if len pure (Just (off + slen)) else pure Nothing +-- | Create a builder from a slice of an array of 'Word8'. There is the same +-- as 'bytes' but is provided as a convenience for users working with different +-- types. +word8Array :: PrimArray Word8 -> Int -> Int -> Builder +word8Array (PrimArray arr) off len = bytes (Bytes (ByteArray arr) off len) + -- Internal function. Precondition, the referenced slice of the -- byte sequence is UTF-8 encoded text. slicedUtf8TextJson :: ByteArray# -> Int# -> Int# -> Builder @@ -417,6 +433,31 @@ shrinkMutableByteArray :: MutableByteArray s -> Int -> ST s () shrinkMutableByteArray (MutableByteArray arr) (I# sz) = primitive_ (Exts.shrinkMutableByteArray# arr sz) +-- | Requires exactly 8 bytes. Dump the octets of a 64-bit +-- signed integer in a little-endian fashion. +int64LE :: Int64 -> Builder +int64LE w = fromBounded Nat.constant (Bounded.int64LE w) + +-- | Requires exactly 8 bytes. Dump the octets of a 64-bit +-- signed integer in a big-endian fashion. +int64BE :: Int64 -> Builder +int64BE w = fromBounded Nat.constant (Bounded.int64BE w) + +-- | Requires exactly 8 bytes. Dump the octets of a 64-bit +-- word in a little-endian fashion. +word64LE :: Word64 -> Builder +word64LE w = fromBounded Nat.constant (Bounded.word64LE w) + +-- | Requires exactly 4 bytes. Dump the octets of a 32-bit +-- word in a little-endian fashion. +word32LE :: Word32 -> Builder +word32LE w = fromBounded Nat.constant (Bounded.word32LE w) + +-- | Requires exactly 2 bytes. Dump the octets of a 16-bit +-- word in a little-endian fashion. +word16LE :: Word16 -> Builder +word16LE w = fromBounded Nat.constant (Bounded.word16LE w) + -- | Requires exactly 8 bytes. Dump the octets of a 64-bit -- word in a big-endian fashion. word64BE :: Word64 -> Builder diff --git a/src/Data/ByteArray/Builder/Bounded.hs b/src/Data/ByteArray/Builder/Bounded.hs index 5d41e47..ba9e535 100644 --- a/src/Data/ByteArray/Builder/Bounded.hs +++ b/src/Data/ByteArray/Builder/Bounded.hs @@ -52,10 +52,18 @@ module Data.ByteArray.Builder.Bounded , ascii , char -- ** Machine-Readable + -- *** One + , word8 + -- **** Big Endian , word64BE , word32BE , word16BE - , word8 + , int64BE + -- **** Little Endian + , word64LE + , word32LE + , word16LE + , int64LE -- * Encode Floating-Point Types , doubleDec ) where @@ -579,6 +587,26 @@ char c byteFourFour :: Word -> Word byteFourFour w = (0b00111111 .&. w) .|. 0b10000000 +int64BE :: Int64 -> Builder 8 +int64BE (I64# i) = word64BE (W64# (int2Word# i)) + +int64LE :: Int64 -> Builder 8 +int64LE (I64# i) = word64LE (W64# (int2Word# i)) + +-- | Requires exactly 8 bytes. Dump the octets of a 64-bit +-- word in a little-endian fashion. +word64LE :: Word64 -> Builder 8 +word64LE w = Unsafe.construct $ \arr off -> do + writeByteArray arr (off + 7) (fromIntegral @Word64 @Word8 (unsafeShiftR w 56)) + writeByteArray arr (off + 6) (fromIntegral @Word64 @Word8 (unsafeShiftR w 48)) + writeByteArray arr (off + 5) (fromIntegral @Word64 @Word8 (unsafeShiftR w 40)) + writeByteArray arr (off + 4) (fromIntegral @Word64 @Word8 (unsafeShiftR w 32)) + writeByteArray arr (off + 3) (fromIntegral @Word64 @Word8 (unsafeShiftR w 24)) + writeByteArray arr (off + 2) (fromIntegral @Word64 @Word8 (unsafeShiftR w 16)) + writeByteArray arr (off + 1) (fromIntegral @Word64 @Word8 (unsafeShiftR w 8)) + writeByteArray arr (off ) (fromIntegral @Word64 @Word8 w) + pure (off + 8) + -- | Requires exactly 8 bytes. Dump the octets of a 64-bit -- word in a big-endian fashion. word64BE :: Word64 -> Builder 8 @@ -593,6 +621,16 @@ word64BE w = Unsafe.construct $ \arr off -> do writeByteArray arr (off + 7) (fromIntegral @Word64 @Word8 w) pure (off + 8) +-- | Requires exactly 4 bytes. Dump the octets of a 32-bit +-- word in a little-endian fashion. +word32LE :: Word32 -> Builder 4 +word32LE w = Unsafe.construct $ \arr off -> do + writeByteArray arr (off + 3) (fromIntegral @Word32 @Word8 (unsafeShiftR w 24)) + writeByteArray arr (off + 2) (fromIntegral @Word32 @Word8 (unsafeShiftR w 16)) + writeByteArray arr (off + 1) (fromIntegral @Word32 @Word8 (unsafeShiftR w 8)) + writeByteArray arr (off ) (fromIntegral @Word32 @Word8 w) + pure (off + 4) + -- | Requires exactly 4 bytes. Dump the octets of a 32-bit -- word in a big-endian fashion. word32BE :: Word32 -> Builder 4 @@ -603,6 +641,14 @@ word32BE w = Unsafe.construct $ \arr off -> do writeByteArray arr (off + 3) (fromIntegral @Word32 @Word8 w) pure (off + 4) +-- | Requires exactly 2 bytes. Dump the octets of a 16-bit +-- word in a little-endian fashion. +word16LE :: Word16 -> Builder 2 +word16LE w = Unsafe.construct $ \arr off -> do + writeByteArray arr (off + 1) (fromIntegral @Word16 @Word8 (unsafeShiftR w 8)) + writeByteArray arr (off ) (fromIntegral @Word16 @Word8 w) + pure (off + 2) + -- | Requires exactly 2 bytes. Dump the octets of a 16-bit -- word in a big-endian fashion. word16BE :: Word16 -> Builder 2