Add big-endian and little-endian parsers for Word128

This commit is contained in:
Andrew Martin 2019-10-17 11:26:14 -04:00
parent d94cf3d000
commit 00f437f8cc
5 changed files with 110 additions and 5 deletions

View file

@ -1,5 +1,10 @@
# Revision history for small-bytearray-builder # Revision history for small-bytearray-builder
## 0.3.1.0 -- 2019-??-??
* Add big-endian and little-endian parsers for `Word128`. This includes
both the single and multiple element variants.
## 0.3.0.0 -- 2019-10-17 ## 0.3.0.0 -- 2019-10-17
* Breaking change: Change the internal implementation of `Builder`. This * Breaking change: Change the internal implementation of `Builder`. This

View file

@ -44,11 +44,12 @@ library
build-depends: build-depends:
, base >=4.12.0.0 && <5 , base >=4.12.0.0 && <5
, byteslice >=0.1 && <0.2 , byteslice >=0.1 && <0.2
, bytestring >=0.10.8.2 && <0.11
, natural-arithmetic >=0.1 && <0.2
, primitive-offset >=0.2 && <0.3 , primitive-offset >=0.2 && <0.3
, run-st >=0.1 && <0.2 , run-st >=0.1 && <0.2
, bytestring >=0.10.8.2 && <0.11
, text-short >=0.1.3 && <0.2 , text-short >=0.1.3 && <0.2
, natural-arithmetic >=0.1 && <0.2 , wide-word >=0.1.0.9 && <0.2
if flag(checked) if flag(checked)
build-depends: primitive-checked >= 0.7 && <0.8 build-depends: primitive-checked >= 0.7 && <0.8
else else
@ -79,6 +80,7 @@ test-suite test
, tasty-quickcheck >=0.10.1 && <0.11 , tasty-quickcheck >=0.10.1 && <0.11
, text >=1.2 && <1.3 , text >=1.2 && <1.3
, vector , vector
, wide-word >=0.1.0.9 && <0.2
benchmark bench benchmark bench
type: exitcode-stdio-1.0 type: exitcode-stdio-1.0

View file

@ -54,6 +54,7 @@ module Data.ByteArray.Builder
-- *** One -- *** One
, word8 , word8
-- **** Big Endian -- **** Big Endian
, word128BE
, word64BE , word64BE
, word32BE , word32BE
, word16BE , word16BE
@ -61,6 +62,7 @@ module Data.ByteArray.Builder
, int32BE , int32BE
, int16BE , int16BE
-- **** Little Endian -- **** Little Endian
, word128LE
, word64LE , word64LE
, word32LE , word32LE
, word16LE , word16LE
@ -73,6 +75,7 @@ module Data.ByteArray.Builder
, word16ArrayBE , word16ArrayBE
, word32ArrayBE , word32ArrayBE
, word64ArrayBE , word64ArrayBE
, word128ArrayBE
, int64ArrayBE , int64ArrayBE
, int32ArrayBE , int32ArrayBE
, int16ArrayBE , int16ArrayBE
@ -80,6 +83,7 @@ module Data.ByteArray.Builder
, word16ArrayLE , word16ArrayLE
, word32ArrayLE , word32ArrayLE
, word64ArrayLE , word64ArrayLE
, word128ArrayLE
, int64ArrayLE , int64ArrayLE
, int32ArrayLE , int32ArrayLE
, int16ArrayLE , int16ArrayLE
@ -99,17 +103,18 @@ import Data.ByteArray.Builder.Unsafe (Builder(Builder))
import Data.ByteArray.Builder.Unsafe (Commits(Initial,Mutable,Immutable)) import Data.ByteArray.Builder.Unsafe (Commits(Initial,Mutable,Immutable))
import Data.ByteArray.Builder.Unsafe (stringUtf8,cstring) import Data.ByteArray.Builder.Unsafe (stringUtf8,cstring)
import Data.ByteString.Short.Internal (ShortByteString(SBS)) import Data.ByteString.Short.Internal (ShortByteString(SBS))
import Data.Bytes.Chunks (Chunks(..))
import Data.Bytes.Types (Bytes(Bytes)) import Data.Bytes.Types (Bytes(Bytes))
import Data.Char (ord) import Data.Char (ord)
import Data.Int (Int64,Int32,Int16,Int8) import Data.Int (Int64,Int32,Int16,Int8)
import Data.Primitive (ByteArray(..),MutableByteArray(..),PrimArray(..)) import Data.Primitive (ByteArray(..),MutableByteArray(..),PrimArray(..))
import Data.Text.Short (ShortText) import Data.Text.Short (ShortText)
import Data.WideWord (Word128)
import Data.Word (Word64,Word32,Word16,Word8) import Data.Word (Word64,Word32,Word16,Word8)
import GHC.ByteOrder (ByteOrder(BigEndian,LittleEndian),targetByteOrder)
import GHC.Exts (Int(I#),Char(C#),Int#,State#,ByteArray#,(>=#)) import GHC.Exts (Int(I#),Char(C#),Int#,State#,ByteArray#,(>=#))
import GHC.Exts (MutableByteArray#,(+#),(-#),(<#)) import GHC.Exts (MutableByteArray#,(+#),(-#),(<#))
import GHC.ST (ST(ST)) import GHC.ST (ST(ST))
import Data.Bytes.Chunks (Chunks(..))
import GHC.ByteOrder (ByteOrder(BigEndian,LittleEndian),targetByteOrder)
import qualified Arithmetic.Nat as Nat import qualified Arithmetic.Nat as Nat
import qualified Arithmetic.Types as Arithmetic import qualified Arithmetic.Types as Arithmetic
@ -262,6 +267,16 @@ int16ArrayLE (PrimArray x) = word16ArrayLE (PrimArray x)
int16ArrayBE :: PrimArray Int16 -> Int -> Int -> Builder int16ArrayBE :: PrimArray Int16 -> Int -> Int -> Builder
int16ArrayBE (PrimArray x) = word16ArrayBE (PrimArray x) int16ArrayBE (PrimArray x) = word16ArrayBE (PrimArray x)
word128ArrayLE :: PrimArray Word128 -> Int -> Int -> Builder
word128ArrayLE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of
LittleEndian -> bytes (Bytes (ByteArray arr) (soff0 * 16) (slen0 * 16))
BigEndian -> word128ArraySwap src soff0 slen0
word128ArrayBE :: PrimArray Word128 -> Int -> Int -> Builder
word128ArrayBE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of
BigEndian -> bytes (Bytes (ByteArray arr) (soff0 * 16) (slen0 * 16))
LittleEndian -> word128ArraySwap src soff0 slen0
word64ArrayLE :: PrimArray Word64 -> Int -> Int -> Builder word64ArrayLE :: PrimArray Word64 -> Int -> Int -> Builder
word64ArrayLE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of word64ArrayLE src@(PrimArray arr) soff0 slen0 = case targetByteOrder of
LittleEndian -> bytes (Bytes (ByteArray arr) (soff0 * 8) (slen0 * 8)) LittleEndian -> bytes (Bytes (ByteArray arr) (soff0 * 8) (slen0 * 8))
@ -350,6 +365,53 @@ word64ArraySwap src soff0 slen0 =
go (soff + 8) send dst (doff + 8) go (soff + 8) send dst (doff + 8)
else pure doff else pure doff
word128ArraySwap :: PrimArray Word128 -> Int -> Int -> Builder
word128ArraySwap src soff0 slen0 =
fromFunction (slen0 * 16) (go (soff0 * 16) ((soff0 + slen0) * 16))
where
-- TODO: Perhaps we could put byteswapping functions to use
-- rather than indexing tons of Word8s. This could be done
-- both here and in the other swap functions. There are a
-- decent number of tests for these array-swapping functions,
-- which makes changing this less scary.
go :: Int -> Int -> MutableByteArray s -> Int -> ST s Int
go !soff !send !dst !doff = if soff < send
then do
let v0 = PM.indexPrimArray (asWord8s src) soff
v1 = PM.indexPrimArray (asWord8s src) (soff + 1)
v2 = PM.indexPrimArray (asWord8s src) (soff + 2)
v3 = PM.indexPrimArray (asWord8s src) (soff + 3)
v4 = PM.indexPrimArray (asWord8s src) (soff + 4)
v5 = PM.indexPrimArray (asWord8s src) (soff + 5)
v6 = PM.indexPrimArray (asWord8s src) (soff + 6)
v7 = PM.indexPrimArray (asWord8s src) (soff + 7)
v8 = PM.indexPrimArray (asWord8s src) (soff + 8)
v9 = PM.indexPrimArray (asWord8s src) (soff + 9)
v10 = PM.indexPrimArray (asWord8s src) (soff + 10)
v11 = PM.indexPrimArray (asWord8s src) (soff + 11)
v12 = PM.indexPrimArray (asWord8s src) (soff + 12)
v13 = PM.indexPrimArray (asWord8s src) (soff + 13)
v14 = PM.indexPrimArray (asWord8s src) (soff + 14)
v15 = PM.indexPrimArray (asWord8s src) (soff + 15)
PM.writeByteArray dst doff v15
PM.writeByteArray dst (doff + 1) v14
PM.writeByteArray dst (doff + 2) v13
PM.writeByteArray dst (doff + 3) v12
PM.writeByteArray dst (doff + 4) v11
PM.writeByteArray dst (doff + 5) v10
PM.writeByteArray dst (doff + 6) v9
PM.writeByteArray dst (doff + 7) v8
PM.writeByteArray dst (doff + 8) v7
PM.writeByteArray dst (doff + 9) v6
PM.writeByteArray dst (doff + 10) v5
PM.writeByteArray dst (doff + 11) v4
PM.writeByteArray dst (doff + 12) v3
PM.writeByteArray dst (doff + 13) v2
PM.writeByteArray dst (doff + 14) v1
PM.writeByteArray dst (doff + 15) v0
go (soff + 16) send dst (doff + 16)
else pure doff
asWord8s :: PrimArray a -> PrimArray Word8 asWord8s :: PrimArray a -> PrimArray Word8
asWord8s (PrimArray x) = PrimArray x asWord8s (PrimArray x) = PrimArray x
@ -595,6 +657,11 @@ int32BE w = fromBounded Nat.constant (Bounded.int32BE w)
int16BE :: Int16 -> Builder int16BE :: Int16 -> Builder
int16BE w = fromBounded Nat.constant (Bounded.int16BE w) int16BE w = fromBounded Nat.constant (Bounded.int16BE w)
-- | Requires exactly 16 bytes. Dump the octets of a 128-bit
-- word in a little-endian fashion.
word128LE :: Word128 -> Builder
word128LE w = fromBounded Nat.constant (Bounded.word128LE w)
-- | Requires exactly 8 bytes. Dump the octets of a 64-bit -- | Requires exactly 8 bytes. Dump the octets of a 64-bit
-- word in a little-endian fashion. -- word in a little-endian fashion.
word64LE :: Word64 -> Builder word64LE :: Word64 -> Builder
@ -615,6 +682,11 @@ word16LE w = fromBounded Nat.constant (Bounded.word16LE w)
word64BE :: Word64 -> Builder word64BE :: Word64 -> Builder
word64BE w = fromBounded Nat.constant (Bounded.word64BE w) word64BE w = fromBounded Nat.constant (Bounded.word64BE w)
-- | Requires exactly 16 bytes. Dump the octets of a 128-bit
-- word in a big-endian fashion.
word128BE :: Word128 -> Builder
word128BE w = fromBounded Nat.constant (Bounded.word128BE w)
-- | Requires exactly 4 bytes. Dump the octets of a 32-bit -- | Requires exactly 4 bytes. Dump the octets of a 32-bit
-- word in a big-endian fashion. -- word in a big-endian fashion.
word32BE :: Word32 -> Builder word32BE :: Word32 -> Builder

View file

@ -55,6 +55,7 @@ module Data.ByteArray.Builder.Bounded
-- *** One -- *** One
, word8 , word8
-- **** Big Endian -- **** Big Endian
, word128BE
, word64BE , word64BE
, word32BE , word32BE
, word16BE , word16BE
@ -62,6 +63,7 @@ module Data.ByteArray.Builder.Bounded
, int32BE , int32BE
, int16BE , int16BE
-- **** Little Endian -- **** Little Endian
, word128LE
, word64LE , word64LE
, word32LE , word32LE
, word16LE , word16LE
@ -81,6 +83,7 @@ import Data.ByteArray.Builder.Bounded.Unsafe (Builder(..))
import Data.Char (ord) import Data.Char (ord)
import Data.Primitive import Data.Primitive
import Data.Primitive.ByteArray.Offset (MutableByteArrayOffset(..)) import Data.Primitive.ByteArray.Offset (MutableByteArrayOffset(..))
import Data.WideWord (Word128(Word128))
import GHC.Exts import GHC.Exts
import GHC.Int (Int64(I64#),Int32(I32#),Int16(I16#),Int8(I8#)) import GHC.Int (Int64(I64#),Int32(I32#),Int16(I16#),Int8(I8#))
import GHC.ST (ST(ST)) import GHC.ST (ST(ST))
@ -609,6 +612,12 @@ int32LE (I32# i) = word32LE (W32# (int2Word# i))
int16LE :: Int16 -> Builder 2 int16LE :: Int16 -> Builder 2
int16LE (I16# i) = word16LE (W16# (int2Word# i)) int16LE (I16# i) = word16LE (W16# (int2Word# i))
word128LE :: Word128 -> Builder 16
word128LE (Word128 hi lo) = append (word64LE lo) (word64LE hi)
word128BE :: Word128 -> Builder 16
word128BE (Word128 hi lo) = append (word64BE hi) (word64BE lo)
-- | Requires exactly 8 bytes. Dump the octets of a 64-bit -- | Requires exactly 8 bytes. Dump the octets of a 64-bit
-- word in a little-endian fashion. -- word in a little-endian fashion.
word64LE :: Word64 -> Builder 8 word64LE :: Word64 -> Builder 8

View file

@ -3,14 +3,18 @@
{-# language TypeApplications #-} {-# language TypeApplications #-}
{-# language OverloadedStrings #-} {-# language OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
import Control.Applicative (liftA2)
import Control.Monad.ST (runST) import Control.Monad.ST (runST)
import Data.ByteArray.Builder import Data.ByteArray.Builder
import Data.Primitive (PrimArray) import Data.Primitive (PrimArray)
import Data.Word import Data.Word
import Data.Char (ord,chr) import Data.Char (ord,chr)
import Data.Primitive (ByteArray) import Data.Primitive (ByteArray)
import Data.WideWord (Word128(Word128))
import Test.Tasty (defaultMain,testGroup,TestTree) import Test.Tasty (defaultMain,testGroup,TestTree)
import Test.QuickCheck ((===)) import Test.QuickCheck ((===),Arbitrary)
import Text.Printf (printf) import Text.Printf (printf)
import Test.Tasty.HUnit ((@=?)) import Test.Tasty.HUnit ((@=?))
@ -164,6 +168,16 @@ tests = testGroup "Tests"
in runConcat 1 (foldMap word64BE xs) in runConcat 1 (foldMap word64BE xs)
=== ===
runConcat 1 (word64ArrayBE ys 0 (Prelude.length xs)) runConcat 1 (word64ArrayBE ys 0 (Prelude.length xs))
, TQC.testProperty "word128ArrayLE" $ \(xs :: [Word128]) ->
let ys = Exts.fromList xs :: PrimArray Word128
in runConcat 1 (foldMap word128LE xs)
===
runConcat 1 (word128ArrayLE ys 0 (Prelude.length xs))
, TQC.testProperty "word128ArrayBE" $ \(xs :: [Word128]) ->
let ys = Exts.fromList xs :: PrimArray Word128
in runConcat 1 (foldMap word128BE xs)
===
runConcat 1 (word128ArrayBE ys 0 (Prelude.length xs))
] ]
, testGroup "alternate" , testGroup "alternate"
[ TQC.testProperty "HexWord64" $ \x y -> [ TQC.testProperty "HexWord64" $ \x y ->
@ -193,3 +207,6 @@ showWord64PaddedUpperHex = printf "%016X"
runConcat :: Int -> Builder -> ByteArray runConcat :: Int -> Builder -> ByteArray
runConcat n = Chunks.concat . run n runConcat n = Chunks.concat . run n
instance Arbitrary Word128 where
arbitrary = liftA2 Word128 TQC.arbitrary TQC.arbitrary