packages feed

mempack 0.1.2.0 → 0.2.0.0

raw patch · 8 files changed

+450/−94 lines, 8 filesdep +primitivedep ~vectorPVP ok

version bump matches the API change (PVP)

Dependencies added: primitive

Dependency ranges changed: vector

API changes (from Hackage documentation)

- Data.MemPack: instance Control.Monad.Fail.MonadFail (Data.MemPack.Unpack b)
- Data.MemPack: instance Control.Monad.Reader.Class.MonadReader b (Data.MemPack.Unpack b)
- Data.MemPack: instance Control.Monad.State.Class.MonadState GHC.Types.Int (Data.MemPack.Unpack b)
- Data.MemPack: instance GHC.Base.Alternative (Data.MemPack.Unpack b)
- Data.MemPack: instance GHC.Base.Applicative (Data.MemPack.Unpack b)
- Data.MemPack: instance GHC.Base.Functor (Data.MemPack.Unpack s)
- Data.MemPack: instance GHC.Base.Monad (Data.MemPack.Unpack b)
+ Data.MemPack: instance (Data.Typeable.Internal.Typeable a, Data.Primitive.Types.Prim a) => Data.MemPack.MemPack (Data.Primitive.PrimArray.PrimArray a)
+ Data.MemPack: instance Control.Monad.Fail.MonadFail (Data.MemPack.Unpack s b)
+ Data.MemPack: instance Control.Monad.Reader.Class.MonadReader b (Data.MemPack.Unpack s b)
+ Data.MemPack: instance Control.Monad.State.Class.MonadState GHC.Types.Int (Data.MemPack.Unpack s b)
+ Data.MemPack: instance Data.MemPack.MemPack a => Data.MemPack.MemPack (Data.Primitive.Array.Array a)
+ Data.MemPack: instance GHC.Base.Alternative (Data.MemPack.Unpack s b)
+ Data.MemPack: instance GHC.Base.Applicative (Data.MemPack.Unpack s b)
+ Data.MemPack: instance GHC.Base.Functor (Data.MemPack.Unpack s b)
+ Data.MemPack: instance GHC.Base.Monad (Data.MemPack.Unpack s b)
+ Data.MemPack: packBuffer :: (MemPack a, Buffer b, HasCallStack) => a -> b
+ Data.MemPack: packLiftST :: ST s a -> Pack s a
+ Data.MemPack: unpackLiftST :: ST s a -> Unpack s b a
+ Data.MemPack.Buffer: bufferHasToBePinned :: Buffer b => Bool
+ Data.MemPack.Buffer: instance Data.MemPack.Buffer.Buffer (Data.Primitive.PrimArray.PrimArray GHC.Word.Word8)
+ Data.MemPack.Buffer: instance Data.MemPack.Buffer.Buffer (Data.Vector.Primitive.Vector GHC.Word.Word8)
+ Data.MemPack.Buffer: instance Data.MemPack.Buffer.Buffer (Data.Vector.Storable.Vector GHC.Word.Word8)
+ Data.MemPack.Buffer: mkBuffer :: Buffer b => ByteArray# -> b
+ Data.MemPack.Buffer: withAddrByteStringST :: ByteString -> (Addr# -> ST s b) -> ST s b
+ Data.MemPack.Buffer: withForeignPtrST :: ForeignPtr a -> (Addr# -> ST s b) -> ST s b
- Data.MemPack: Unpack :: (b -> StateT Int (Fail SomeError) a) -> Unpack b a
+ Data.MemPack: Unpack :: (b -> StateT Int (FailT SomeError (ST s)) a) -> Unpack s b a
- Data.MemPack: [runUnpack] :: Unpack b a -> b -> StateT Int (Fail SomeError) a
+ Data.MemPack: [runUnpack] :: Unpack s b a -> b -> StateT Int (FailT SomeError (ST s)) a
- Data.MemPack: failUnpack :: Error e => e -> Unpack b a
+ Data.MemPack: failUnpack :: Error e => e -> Unpack s b a
- Data.MemPack: guardAdvanceUnpack :: Buffer b => Int -> Unpack b Int
+ Data.MemPack: guardAdvanceUnpack :: Buffer b => Int -> Unpack s b Int
- Data.MemPack: newtype Unpack b a
+ Data.MemPack: newtype Unpack s b a
- Data.MemPack: unpackByteArray :: Buffer b => Bool -> Unpack b ByteArray
+ Data.MemPack: unpackByteArray :: Buffer b => Bool -> Unpack s b ByteArray
- Data.MemPack: unpackByteArrayLen :: Buffer b => Bool -> Int -> Unpack b ByteArray
+ Data.MemPack: unpackByteArrayLen :: Buffer b => Bool -> Int -> Unpack s b ByteArray
- Data.MemPack: unpackByteStringM :: Buffer b => Int -> Unpack b ByteString
+ Data.MemPack: unpackByteStringM :: Buffer b => Int -> Unpack s b ByteString
- Data.MemPack: unpackM :: (MemPack a, Buffer b) => Unpack b a
+ Data.MemPack: unpackM :: (MemPack a, Buffer b) => Unpack s b a
- Data.MemPack: unpackTagM :: Buffer b => Unpack b Tag
+ Data.MemPack: unpackTagM :: Buffer b => Unpack s b Tag
- Data.MemPack.Buffer: buffer :: Buffer b => b -> (ByteArray# -> a) -> (Addr# -> a) -> a
+ Data.MemPack.Buffer: buffer :: Buffer b => b -> (ByteArray# -> Int# -> a) -> (Addr# -> a) -> a

Files

CHANGELOG.md view
@@ -1,5 +1,17 @@ # Changelog for `mempack` +## 0.2.0.0++* Add `mkBuffer`, `bufferHasToBePinned` and `packBuffer`+* Add an offset to the `buffer` function in order to support types that don't use the full+  underlying buffer.+* Add `Buffer` and `MemPack` instances for `PrimArray` and older definition of `ByteArray` from `primitive`.+* Add `MemPack` instances for `Array` from `primitive`.+* Switch `Unpack` monad to be based on the `ST` monad.+* Add `packLiftST` and `unpackLiftST`+* Add `withForeignPtrST` and `withAddrByteStringST`+* Add `Buffer` instances for primitive and storable `Vector`s+ ## 0.1.2.0  * Fix 32-bit support
bench/Bench.hs view
@@ -9,30 +9,30 @@ import Data.ByteString.Lazy (fromStrict, toStrict) import Data.ByteString.Short (fromShort, toShort) import Data.MemPack+import Data.Primitive.Array (Array) import qualified Data.Serialize as Cereal import qualified Data.Store as Store import qualified Flat as Flat+import GHC.Exts (fromList)  main :: IO () main = do   defaultMain-    [ env (pure [1 :: Int .. 100000]) $ \xs ->+    [ bgroup+        "packedByteCount"+        [ env (pure [1 :: Integer .. 1000000]) $ bench "[Integer]" . whnf packedByteCount+        ]+    , bgroup "[Int]" [env (pure [1 :: Int .. 100000]) packBench]+    , env (pure $ fromList @(Array Int) [1 :: Int .. 100000]) $ \arr ->         bgroup-          "Pack"-          [ bgroup-              "ByteString"-              [ bench "MemPack" $ nf packByteString xs-              , bench "Store" $ nf Store.encode xs-              , bench "Flat" $ nf Flat.flat xs-              , bench "Cereal" $ nf Cereal.encode xs-              , bench "Binary" $ nf (toStrict . Binary.encode) xs-              , bench "Serialise" $ nf (toStrict . Serialise.serialise) xs-              , bench "Avro" $ nf (toStrict . Avro.encodeValue) xs-              ]+          "Array Int"+          [ bench "Pack" $ nf packByteString arr+          , env (pure (packByteString arr)) (bench "Unpack" . nf (unpackError @(Array Int)))+          , bench "RoundTrip" $ nf (unpackError @(Array Int) . packShortByteString) arr           ]     , env (pure [1 :: Int .. 100000]) $ \xs ->         bgroup-          "RoundTrip"+          "RoundTrip List Through"           [ bgroup               "ByteString"               [ bench "MemPack" $ nf (unpackError @[Int] . packByteString) xs@@ -75,4 +75,30 @@                     xs               ]           ]+    ]++packBench ::+  ( MemPack a+  , Store.Store a+  , Flat.Flat a+  , Cereal.Serialize a+  , Binary.Binary a+  , Serialise.Serialise a+  , Avro.HasAvroSchema a+  , Avro.ToAvro a+  ) =>+  a -> Benchmark+packBench xs =+  bgroup+    "Pack"+    [ bgroup+        "ByteString"+        [ bench "MemPack" $ nf packByteString xs+        , bench "Store" $ nf Store.encode xs+        , bench "Flat" $ nf Flat.flat xs+        , bench "Cereal" $ nf Cereal.encode xs+        , bench "Binary" $ nf (toStrict . Binary.encode) xs+        , bench "Serialise" $ nf (toStrict . Serialise.serialise) xs+        , bench "Avro" $ nf (toStrict . Avro.encodeValue) xs+        ]     ]
mempack.cabal view
@@ -1,5 +1,5 @@ name:                mempack-version:             0.1.2.0+version:             0.2.0.0 synopsis:            Short description description:         Please see the README on GitHub at <https://github.com/lehins/mempack#readme> homepage:            https://github.com/lehins/mempack@@ -7,7 +7,7 @@ license-file:        LICENSE author:              Alexey Kuleshevich maintainer:          alexey@kuleshevi.ch-copyright:           2024 Alexey Kuleshevich+copyright:           2024-2025 Alexey Kuleshevich category:            Algorithms build-type:          Simple extra-source-files:  README.md@@ -21,8 +21,8 @@                    , GHC == 9.4.8                    , GHC == 9.6.7                    , GHC == 9.8.4-                   , GHC == 9.10.1-                   , GHC == 9.12.1+                   , GHC == 9.10.3+                   , GHC == 9.12.2  library   hs-source-dirs:      src@@ -31,11 +31,13 @@                      , Data.MemPack.Error    other-modules:-  build-depends:       base >= 4.12 && < 5+  build-depends:       base >=4.12 && <5                      , bytestring                      , FailT+                     , primitive >=0.6.4                      , mtl                      , text+                     , vector >=0.10.12   if !impl(ghc >= 9.4)     build-depends:     data-array-byte   if !impl(ghc >= 9.0)@@ -59,9 +61,11 @@                     , hspec                     , mempack                     , mtl+                    , primitive                     , QuickCheck                     , random >=1.2.1                     , text+                    , vector   if !impl(ghc >= 9.4)     build-depends:     data-array-byte @@ -90,6 +94,7 @@                      , criterion                      , flat                      , mempack+                     , primitive                      , serialise                      , store                      , vector
src/Data/MemPack.hs view
@@ -11,12 +11,13 @@ {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE UnboxedTuples #-}  -- | -- Module      : Data.MemPack--- Copyright   : (c) Alexey Kuleshevich 2024+-- Copyright   : (c) Alexey Kuleshevich 2024-2025 -- License     : BSD3 -- Maintainer  : Alexey Kuleshevich <alexey@kuleshevi.ch> -- Stability   : experimental@@ -28,6 +29,7 @@    -- * Packing   pack,+  packBuffer,   packByteString,   packShortByteString, @@ -54,6 +56,8 @@   unpackByteArrayLen,   packByteStringM,   unpackByteStringM,+  packLiftST,+  unpackLiftST,    -- * Helper packers   VarLen (..),@@ -81,7 +85,7 @@ import qualified Control.Monad.Fail as F import Control.Monad.Reader (MonadReader (..), lift) import Control.Monad.State.Strict (MonadState (..), StateT (..), execStateT)-import Control.Monad.Trans.Fail (Fail, FailT (..), errorFail, failT, runFailAgg)+import Control.Monad.Trans.Fail (Fail, FailT (..), errorFail, failT, runFailAgg, runFailAggT) import Data.Array.Byte (ByteArray (..), MutableByteArray (..)) import Data.Bifunctor (first) import Data.Bits (Bits (..), FiniteBits (..))@@ -91,11 +95,14 @@ import Data.ByteString.Short (ShortByteString) import Data.Char (ord) import Data.Complex (Complex (..))+import qualified Data.Foldable as F (foldl') import Data.List (intercalate) import Data.MemPack.Buffer import Data.MemPack.Error+import Data.Primitive.Array (Array (..), newArray, sizeofArray, unsafeFreezeArray, writeArray)+import Data.Primitive.PrimArray (PrimArray (..), sizeofPrimArray)+import Data.Primitive.Types (Prim (sizeOf#)) import Data.Ratio-import Data.Semigroup (Sum (..)) #if MIN_VERSION_text(2,0,0) import qualified Data.Text.Array as T #endif@@ -120,9 +127,12 @@ #else #error "Only integer-gmp is supported for now for older compilers" #endif-#if !(MIN_VERSION_base(4,13,0))+#if !MIN_VERSION_base(4,13,0) import Prelude (fail) #endif+#if !MIN_VERSION_primitive(0,8,0)+import qualified Data.Primitive.ByteArray as Prim (ByteArray(..))+#endif  -- | Monad that is used for serializing data into a `MutableByteArray`. It is based on -- `StateT` that tracks the current index into the `MutableByteArray` where next write is@@ -166,14 +176,14 @@ -- `StateT` that tracks the current index into the @`Buffer` a@, from where the next read -- suppose to happen. Unpacking can `F.fail` with `F.MonadFail` instance or with -- `failUnpack` that provides a more type safe way of failing using `Error` interface.-newtype Unpack b a = Unpack-  { runUnpack :: b -> StateT Int (Fail SomeError) a+newtype Unpack s b a = Unpack+  { runUnpack :: b -> StateT Int (FailT SomeError (ST s)) a   } -instance Functor (Unpack s) where+instance Functor (Unpack s b) where   fmap f (Unpack p) = Unpack $ \buf -> fmap f (p buf)   {-# INLINE fmap #-}-instance Applicative (Unpack b) where+instance Applicative (Unpack s b) where   pure = Unpack . const . pure   {-# INLINE pure #-}   Unpack a1 <*> Unpack a2 =@@ -182,23 +192,23 @@   Unpack a1 *> Unpack a2 =     Unpack $ \buf -> a1 buf *> a2 buf   {-# INLINE (*>) #-}-instance Monad (Unpack b) where+instance Monad (Unpack s b) where   Unpack m1 >>= p =     Unpack $ \buf -> m1 buf >>= \res -> runUnpack (p res) buf   {-# INLINE (>>=) #-} #if !(MIN_VERSION_base(4,13,0))   fail = Unpack . const . F.fail #endif-instance F.MonadFail (Unpack b) where+instance F.MonadFail (Unpack s b) where   fail = Unpack . const . F.fail-instance MonadReader b (Unpack b) where+instance MonadReader b (Unpack s b) where   ask = Unpack pure   {-# INLINE ask #-}   local f (Unpack p) = Unpack (p . f)   {-# INLINE local #-}   reader f = Unpack (pure . f)   {-# INLINE reader #-}-instance MonadState Int (Unpack b) where+instance MonadState Int (Unpack s b) where   get = Unpack $ const get   {-# INLINE get #-}   put = Unpack . const . put@@ -206,7 +216,7 @@   state = Unpack . const . state   {-# INLINE state #-} -instance Alternative (Unpack b) where+instance Alternative (Unpack s b) where   empty = Unpack $ \_ -> lift empty   {-# INLINE empty #-}   Unpack r1 <|> Unpack r2 =@@ -218,7 +228,7 @@   {-# INLINE (<|>) #-}  -- | Failing unpacking with an `Error`.-failUnpack :: Error e => e -> Unpack b a+failUnpack :: Error e => e -> Unpack s b a failUnpack e = Unpack $ \_ -> lift $ failT (toSomeError e)  -- | Efficient serialization interface that operates directly on memory buffers.@@ -241,11 +251,13 @@   packM :: a -> Pack s ()    -- | Read binary representation of the type directly from the buffer, which can be-  -- accessed with `ask` when necessary. Direct reads from the buffer should be preceded-  -- with advancing the buffer offset with `MonadState` by the number of bytes that will-  -- be consumed from the buffer and making sure that no reads outside of the buffer can-  -- happen. Violation of these rules will lead to segfaults.-  unpackM :: Buffer b => Unpack b a+  -- accessed with `ask` when necessary.+  --+  -- /Warning/ - Direct reads from the buffer should be preceded with advancing the buffer offset+  -- within `MonadState` by the exact number of bytes that gets consumed from that buffer.+  --+  -- __⚠__ - Violation of the above rule will lead to segfaults.+  unpackM :: Buffer b => Unpack s b a  instance MemPack () where   packedByteCount _ = 0@@ -320,7 +332,7 @@     let c =           buffer             buf-            (\ba# -> C# (indexWord8ArrayAsWideChar# ba# i#))+            (\ba# off# -> C# (indexWord8ArrayAsWideChar# ba# (i# +# off#)))             (\addr# -> C# (indexWideCharOffAddr# (addr# `plusAddr#` i#) 0#))         ordc :: Word32         ordc = fromIntegral (ord c)@@ -344,7 +356,7 @@     pure $!       buffer         buf-        (\ba# -> F# (indexWord8ArrayAsFloat# ba# i#))+        (\ba# off# -> F# (indexWord8ArrayAsFloat# ba# (i# +# off#)))         (\addr# -> F# (indexFloatOffAddr# (addr# `plusAddr#` i#) 0#))   {-# INLINE unpackM #-} @@ -362,7 +374,7 @@     pure $!       buffer         buf-        (\ba# -> D# (indexWord8ArrayAsDouble# ba# i#))+        (\ba# off# -> D# (indexWord8ArrayAsDouble# ba# (i# +# off#)))         (\addr# -> D# (indexDoubleOffAddr# (addr# `plusAddr#` i#) 0#))   {-# INLINE unpackM #-} @@ -381,7 +393,7 @@     pure $!       buffer         buf-        (\ba# -> Ptr (indexWord8ArrayAsAddr# ba# i#))+        (\ba# off# -> Ptr (indexWord8ArrayAsAddr# ba# (i# +# off#)))         (\addr# -> Ptr (indexAddrOffAddr# (addr# `plusAddr#` i#) 0#))   {-# INLINE unpackM #-} @@ -400,7 +412,7 @@     pure $!       buffer         buf-        (\ba# -> StablePtr (indexWord8ArrayAsStablePtr# ba# i#))+        (\ba# off# -> StablePtr (indexWord8ArrayAsStablePtr# ba# (i# +# off#)))         (\addr# -> StablePtr (indexStablePtrOffAddr# (addr# `plusAddr#` i#) 0#))   {-# INLINE unpackM #-} @@ -418,7 +430,7 @@     pure $!       buffer         buf-        (\ba# -> I# (indexWord8ArrayAsInt# ba# i#))+        (\ba# off# -> I# (indexWord8ArrayAsInt# ba# (i# +# off#)))         (\addr# -> I# (indexIntOffAddr# (addr# `plusAddr#` i#) 0#))   {-# INLINE unpackM #-} @@ -436,7 +448,7 @@     pure $!       buffer         buf-        (\ba# -> I8# (indexInt8Array# ba# i#))+        (\ba# off# -> I8# (indexInt8Array# ba# (i# +# off#)))         (\addr# -> I8# (indexInt8OffAddr# (addr# `plusAddr#` i#) 0#))   {-# INLINE unpackM #-} @@ -454,7 +466,7 @@     pure $!       buffer         buf-        (\ba# -> I16# (indexWord8ArrayAsInt16# ba# i#))+        (\ba# off# -> I16# (indexWord8ArrayAsInt16# ba# (i# +# off#)))         (\addr# -> I16# (indexInt16OffAddr# (addr# `plusAddr#` i#) 0#))   {-# INLINE unpackM #-} @@ -472,7 +484,7 @@     pure $!       buffer         buf-        (\ba# -> I32# (indexWord8ArrayAsInt32# ba# i#))+        (\ba# off# -> I32# (indexWord8ArrayAsInt32# ba# (i# +# off#)))         (\addr# -> I32# (indexInt32OffAddr# (addr# `plusAddr#` i#) 0#))   {-# INLINE unpackM #-} @@ -490,7 +502,7 @@     pure $!       buffer         buf-        (\ba# -> I64# (indexWord8ArrayAsInt64# ba# i#))+        (\ba# off# -> I64# (indexWord8ArrayAsInt64# ba# (i# +# off#)))         (\addr# -> I64# (indexInt64OffAddr# (addr# `plusAddr#` i#) 0#))   {-# INLINE unpackM #-} @@ -508,7 +520,7 @@     pure $!       buffer         buf-        (\ba# -> W# (indexWord8ArrayAsWord# ba# i#))+        (\ba# off# -> W# (indexWord8ArrayAsWord# ba# (i# +# off#)))         (\addr# -> W# (indexWordOffAddr# (addr# `plusAddr#` i#) 0#))   {-# INLINE unpackM #-} @@ -526,7 +538,7 @@     pure $!       buffer         buf-        (\ba# -> W8# (indexWord8Array# ba# i#))+        (\ba# off# -> W8# (indexWord8Array# ba# (i# +# off#)))         (\addr# -> W8# (indexWord8OffAddr# addr# i#))   {-# INLINE unpackM #-} @@ -544,7 +556,7 @@     pure $!       buffer         buf-        (\ba# -> W16# (indexWord8ArrayAsWord16# ba# i#))+        (\ba# off# -> W16# (indexWord8ArrayAsWord16# ba# (i# +# off#)))         (\addr# -> W16# (indexWord16OffAddr# (addr# `plusAddr#` i#) 0#))   {-# INLINE unpackM #-} @@ -562,7 +574,7 @@     pure $!       buffer         buf-        (\ba# -> W32# (indexWord8ArrayAsWord32# ba# i#))+        (\ba# off# -> W32# (indexWord8ArrayAsWord32# ba# (i# +# off#)))         (\addr# -> W32# (indexWord32OffAddr# (addr# `plusAddr#` i#) 0#))   {-# INLINE unpackM #-} @@ -580,7 +592,7 @@     pure $!       buffer         buf-        (\ba# -> W64# (indexWord8ArrayAsWord64# ba# i#))+        (\ba# off# -> W64# (indexWord8ArrayAsWord64# ba# (i# +# off#)))         (\addr# -> W64# (indexWord64OffAddr# (addr# `plusAddr#` i#) 0#))   {-# INLINE unpackM #-} @@ -881,7 +893,12 @@  instance MemPack a => MemPack [a] where   typeName = "[" ++ typeName @a ++ "]"-  packedByteCount es = packedByteCount (Length (length es)) + getSum (foldMap (Sum . packedByteCount) es)+  packedByteCount es =+    let go [] (# listLen#, elemsLen# #) = packedByteCount (Length (I# listLen#)) + (I# elemsLen#)+        go (x : xs) (# listLen#, elemsLen# #) =+          let !(I# bc#) = packedByteCount x+           in go xs (# 1# +# listLen#, bc# +# elemsLen# #)+     in go es (# 0#, 0# #)   {-# INLINE packedByteCount #-}   packM as = do     packM (Length (length as))@@ -892,6 +909,27 @@     replicateTailM n unpackM   {-# INLINE unpackM #-} +instance MemPack a => MemPack (Array a) where+  typeName = "(Array " ++ typeName @a ++ ")"+  packedByteCount arr =+    packedByteCount (Length (sizeofArray arr))+      + F.foldl' (\acc e -> acc + packedByteCount e) 0 arr+  {-# INLINE packedByteCount #-}+  packM as = do+    packM (Length (length as))+    mapM_ packM as+  {-# INLINE packM #-}+  unpackM = do+    Length n <- unpackM+    marr <- unpackLiftST (newArray n (error "Uninitialized"))+    let fill !i = when (i < n) $ do+          e <- unpackM+          unpackLiftST (writeArray marr i e)+          fill (i + 1)+    fill 0+    unpackLiftST (unsafeFreezeArray marr)+  {-# INLINE unpackM #-}+ -- | Tail recursive version of `replicateM` replicateTailM :: Monad m => Int -> m a -> m [a] replicateTailM n f = go n []@@ -916,6 +954,38 @@   unpackM = unpackByteArray False   {-# INLINE unpackM #-} +instance (Typeable a, Prim a) => MemPack (PrimArray a) where+  packedByteCount pa =+    let len = I# (sizeOf# (undefined :: a)) * sizeofPrimArray pa+     in packedByteCount (Length len) + len+  {-# INLINE packedByteCount #-}+  packM pa@(PrimArray ba#) = do+    let !len@(I# len#) = I# (sizeOf# (undefined :: a)) * sizeofPrimArray pa+    packM (Length len)+    I# curPos# <- state $ \i -> (i, i + len)+    MutableByteArray mba# <- ask+    lift_# (copyByteArray# ba# 0# mba# curPos# len#)+  {-# INLINE packM #-}+  unpackM = (\(ByteArray ba#) -> PrimArray ba#) <$> unpackByteArray False+  {-# INLINE unpackM #-}++#if !MIN_VERSION_primitive(0,8,0)+instance MemPack Prim.ByteArray where+  packedByteCount ba =+    let len = bufferByteCount ba+     in packedByteCount (Length len) + len+  {-# INLINE packedByteCount #-}+  packM ba@(Prim.ByteArray ba#) = do+    let !len@(I# len#) = bufferByteCount ba+    packM (Length len)+    I# curPos# <- state $ \i -> (i, i + len)+    MutableByteArray mba# <- ask+    lift_# (copyByteArray# ba# 0# mba# curPos# len#)+  {-# INLINE packM #-}+  unpackM = (\(ByteArray ba#) -> Prim.ByteArray ba#) <$> unpackByteArray False+  {-# INLINE unpackM #-}+#endif+ instance MemPack ShortByteString where   packedByteCount ba =     let len = bufferByteCount ba@@ -983,7 +1053,9 @@     MutableByteArray mba# <- ask     lift_# (copyByteArray# ba# offset# mba# curPos# len#) #else-  -- FIXME: This is very inefficient and shall be fixed in the next major version+  -- FIXME: This is very inefficient and hopefully will be fixed at some point.  It requires some+  -- clever change to the MemPack interface in order to allow memoization between `packedByteCount`+  -- and `packM`   packedByteCount = packedByteCount . T.encodeUtf8   packM = packM . T.encodeUtf8 #endif@@ -998,7 +1070,7 @@ {- FOURMOLU_ENABLE -}  -- | This is the implementation of `unpackM` for `ByteArray`, `ByteString` and `ShortByteString`-unpackByteArray :: Buffer b => Bool -> Unpack b ByteArray+unpackByteArray :: Buffer b => Bool -> Unpack s b ByteArray unpackByteArray isPinned = unpackByteArrayLen isPinned . unLength =<< unpackM {-# INLINE unpackByteArray #-} @@ -1007,7 +1079,7 @@ -- Similar to `unpackByteArray`, except it does not unpack a length. -- -- @since 0.1.1-unpackByteArrayLen :: Buffer b => Bool -> Int -> Unpack b ByteArray+unpackByteArrayLen :: Buffer b => Bool -> Int -> Unpack s b ByteArray unpackByteArrayLen isPinned len@(I# len#) = do   I# curPos# <- guardAdvanceUnpack len   buf <- ask@@ -1015,7 +1087,7 @@     mba@(MutableByteArray mba#) <- newMutableByteArray isPinned len     buffer       buf-      (\ba# -> st_ (copyByteArray# ba# curPos# mba# 0# len#))+      (\ba# off# -> st_ (copyByteArray# ba# (curPos# +# off#) mba# 0# len#))       (\addr# -> st_ (copyAddrToByteArray# (addr# `plusAddr#` curPos#) mba# 0# len#))     freezeMutableByteArray mba {-# INLINE unpackByteArrayLen #-}@@ -1032,7 +1104,7 @@ -- | Increment the offset counter of `Unpack` monad by the supplied number of -- bytes. Returns the original offset or fails with `RanOutOfBytesError` whenever there is -- not enough bytes in the `Buffer`.-guardAdvanceUnpack :: Buffer b => Int -> Unpack b Int+guardAdvanceUnpack :: Buffer b => Int -> Unpack s b Int guardAdvanceUnpack n@(I# n#) = do   buf <- ask   let !len = bufferByteCount buf@@ -1044,7 +1116,7 @@       _ -> (failOutOfBytes i len n, i) {-# INLINE guardAdvanceUnpack #-} -failOutOfBytes :: Int -> Int -> Int -> Unpack b a+failOutOfBytes :: Int -> Int -> Int -> Unpack s b a failOutOfBytes i len n =   failUnpack $     toSomeError $@@ -1071,6 +1143,27 @@ packByteString = pinnedByteArrayToByteString . packByteArray True {-# INLINE packByteString #-} +-- | Serialize a type into any `Buffer`+--+-- prop> pack xs == packBuffer xs+--+-- prop> packByteString xs == packBuffer xs+--+-- ====__Examples__+--+-- >>> :set -XTypeApplications+-- >>> import qualified Data.Vector.Primitive as VP+-- >>> import Data.Word (Word8)+-- >>> unpack @[Int] $ packBuffer @[Int] @(VP.Vector Word8) [1,2,3,4,5]+-- Right [1,2,3,4,5]+--+-- @since 0.2.0+packBuffer :: forall a b. (MemPack a, Buffer b, HasCallStack) => a -> b+packBuffer a =+  case packByteArray (bufferHasToBePinned @b) a of+    ByteArray ba -> mkBuffer ba+{-# INLINE packBuffer #-}+ -- | Serialize a type into an unpinned `ShortByteString` packShortByteString :: forall a. (MemPack a, HasCallStack) => a -> ShortByteString packShortByteString = byteArrayToShortByteString . pack@@ -1160,7 +1253,7 @@ packByteStringM bs = do   let !len@(I# len#) = bufferByteCount bs   I# curPos# <- state $ \i -> (i, i + len)-  Pack $ \(MutableByteArray mba#) -> lift $ withPtrByteStringST bs $ \(Ptr addr#) ->+  Pack $ \(MutableByteArray mba#) -> lift $ withAddrByteStringST bs $ \addr# ->     st_ (copyAddrToByteArray# addr# mba# curPos# len#) {-# INLINE packByteStringM #-} @@ -1171,19 +1264,26 @@   Buffer b =>   -- | number of bytes to unpack   Int ->-  Unpack b ByteString+  Unpack s b ByteString unpackByteStringM len = pinnedByteArrayToByteString <$> unpackByteArrayLen True len {-# INLINE unpackByteStringM #-}  -- | Unpack a memory `Buffer` into a type using its `MemPack` instance. Besides the -- unpacked type it also returns an index into a buffer where unpacked has stopped. unpackLeftOver :: forall a b. (MemPack a, Buffer b, HasCallStack) => b -> Fail SomeError (a, Int)-unpackLeftOver b = do+unpackLeftOver b = FailT $ pure $ runST $ runFailAggT $ unpackLeftOverST b+{-# INLINE unpackLeftOver #-}++-- | Unpack a memory `Buffer` into a type using its `MemPack` instance. Besides the+-- unpacked type it also returns an index into a buffer where unpacked has stopped.+unpackLeftOverST ::+  forall a b s. (MemPack a, Buffer b, HasCallStack) => b -> FailT SomeError (ST s) (a, Int)+unpackLeftOverST b = do   let len = bufferByteCount b   res@(_, consumedBytes) <- runStateT (runUnpack unpackM b) 0   when (consumedBytes > len) $ errorLeftOver (typeName @a) consumedBytes len   pure res-{-# INLINEABLE unpackLeftOver #-}+{-# INLINEABLE unpackLeftOverST #-}  -- | This is a critical error, therefore we are not gracefully failing this unpacking errorLeftOver :: HasCallStack => String -> Int -> Int -> a@@ -1348,13 +1448,13 @@ unpack7BitVarLen ::   (Num a, Bits a, Buffer b) =>   -- | Continuation that will be invoked if MSB is set-  (Word8 -> a -> Unpack b a) ->+  (Word8 -> a -> Unpack s b a) ->   -- | Will be set either to 0 initially or to the very first unmodified byte, which is   -- guaranteed to have the first bit set.   Word8 ->   -- | Accumulator   a ->-  Unpack b a+  Unpack s b a unpack7BitVarLen cont firstByte !acc = do   b8 :: Word8 <- unpackM   if b8 `testBit` 7@@ -1364,12 +1464,12 @@ {-# INLINE unpack7BitVarLen #-}  unpack7BitVarLenLast ::-  forall t b.+  forall t b s.   (Num t, Bits t, MemPack t, Buffer b) =>   Word8 ->   Word8 ->   t ->-  Unpack b t+  Unpack s b t unpack7BitVarLenLast mask firstByte acc = do   res <- unpack7BitVarLen (\_ _ -> F.fail "Too many bytes.") firstByte acc   -- Only while decoding the last 7bits we check if there was too many@@ -1440,7 +1540,7 @@ packedTagByteCount = SIZEOF_WORD8 {-# INLINE packedTagByteCount #-} -unpackTagM :: Buffer b => Unpack b Tag+unpackTagM :: Buffer b => Unpack s b Tag unpackTagM = Tag <$> unpackM {-# INLINE unpackTagM #-} @@ -1458,3 +1558,17 @@ st_ :: (State# s -> State# s) -> ST s () st_ f = ST $ \s# -> (# f s#, () #) {-# INLINE st_ #-}++-- | Lift an `ST` action into the `Pack` monad+--+-- @since 0.2.0+packLiftST :: ST s a -> Pack s a+packLiftST st = Pack (\_ -> StateT (\i -> (,i) <$> st))+{-# INLINE packLiftST #-}++-- | Lift an `ST` action into the `Unpack` monad+--+-- @since 0.2.0+unpackLiftST :: ST s a -> Unpack s b a+unpackLiftST st = Unpack (\_ -> StateT (\i -> FailT (Right . (,i) <$> st)))+{-# INLINE unpackLiftST #-}
src/Data/MemPack/Buffer.hs view
@@ -1,95 +1,265 @@-{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MagicHash #-}+{-# LANGUAGE UnboxedTuples #-}  -- | -- Module      : Data.MemPack.Buffer--- Copyright   : (c) Alexey Kuleshevich 2024+-- Copyright   : (c) Alexey Kuleshevich 2024-2025 -- License     : BSD3 -- Maintainer  : Alexey Kuleshevich <alexey@kuleshevi.ch> -- Stability   : experimental -- Portability : non-portable-module Data.MemPack.Buffer where+module Data.MemPack.Buffer (+  Buffer (..),+  newMutableByteArray,+  freezeMutableByteArray,+  withPtrByteStringST,+  withAddrByteStringST,+  withForeignPtrST,+  pinnedByteArrayToByteString,+  pinnedByteArrayToForeignPtr,+  byteArrayToShortByteString,+  byteArrayFromShortByteString,+)+where  import Data.Array.Byte import qualified Data.ByteString as BS-import qualified Data.ByteString.Short.Internal as SBS import qualified Data.ByteString.Internal as BS+import qualified Data.ByteString.Short.Internal as SBS+import Data.Primitive.PrimArray (PrimArray (..))+import Data.Word (Word8) import GHC.Exts-import GHC.ST import GHC.ForeignPtr+import GHC.ST+#if !MIN_VERSION_primitive(0,8,0)+import qualified Data.Primitive.ByteArray as Prim (ByteArray(..))+#endif+import qualified Data.Vector.Primitive as VP (Vector (..))+import qualified Data.Vector.Storable as VS (+  Vector,+  length,+  unsafeFromForeignPtr0,+  unsafeToForeignPtr0,+ )  -- | Immutable memory buffer class Buffer b where+  -- | Number of accessible bytes in the buffer.   bufferByteCount :: b -> Int -  buffer :: b -> (ByteArray# -> a) -> (Addr# -> a) -> a+  -- | Use one of the two suppplied functions to access memory of the buffer:+  buffer ::+    -- | A type that contains the actual buffer that will be accessed+    b ->+    -- | In case when a buffer is backed by a `ByteArray#` it will be accessed as such with an+    -- offset from the beginning of the ByteArray+    (ByteArray# -> Int# -> a) ->+    -- | In case when a buffer is backed by a pointer or a pinned `ByteArray#` it can be accessed as+    -- an `Addr#`. No offset is necessary here, since same affect can be achieved with pointer+    -- arithmetic.+    (Addr# -> a) ->+    a +  mkBuffer :: ByteArray# -> b++  bufferHasToBePinned :: Bool+ instance Buffer ByteArray where   bufferByteCount (ByteArray ba#) = I# (sizeofByteArray# ba#)   {-# INLINE bufferByteCount #-}+  buffer (ByteArray ba#) f _ = f ba# 0#+  {-# INLINE buffer #-}+  mkBuffer ba# = ByteArray ba#+  {-# INLINE mkBuffer #-}+  bufferHasToBePinned = False -  buffer (ByteArray ba#) f _ = f ba#+#if !MIN_VERSION_primitive(0,8,0)+instance Buffer Prim.ByteArray where+  bufferByteCount (Prim.ByteArray ba#) = I# (sizeofByteArray# ba#)+  {-# INLINE bufferByteCount #-}+  buffer (Prim.ByteArray ba#) f _ = f ba# 0#   {-# INLINE buffer #-}+  mkBuffer ba# = Prim.ByteArray ba#+  {-# INLINE mkBuffer #-}+  bufferHasToBePinned = False+#endif  instance Buffer SBS.ShortByteString where   bufferByteCount = SBS.length   {-# INLINE bufferByteCount #-}--  buffer (SBS.SBS ba#) f _ = f ba#+  buffer (SBS.SBS ba#) f _ = f ba# 0#   {-# INLINE buffer #-}+  mkBuffer ba# = SBS.SBS ba#+  {-# INLINE mkBuffer #-}+  bufferHasToBePinned = False  instance Buffer BS.ByteString where   bufferByteCount = BS.length   {-# INLINE bufferByteCount #-}+  buffer bs _f g =+    runST $ withAddrByteStringST bs $ \addr# -> pure (g addr#)+  {-# INLINE buffer #-}+  mkBuffer ba# = pinnedByteArrayToByteString (ByteArray ba#)+  {-# INLINE mkBuffer #-}+  bufferHasToBePinned = True -  buffer bs _ f =-    runST $ withPtrByteStringST bs $ \(Ptr addr#) -> pure $! f addr#+instance Buffer (PrimArray Word8) where+  bufferByteCount (PrimArray ba#) = I# (sizeofByteArray# ba#)+  {-# INLINE bufferByteCount #-}+  buffer (PrimArray ba#) f _ = f ba# 0#   {-# INLINE buffer #-}+  mkBuffer ba# = PrimArray ba#+  {-# INLINE mkBuffer #-}+  bufferHasToBePinned = False +instance Buffer (VP.Vector Word8) where+  bufferByteCount (VP.Vector _ len _) = len+  {-# INLINE bufferByteCount #-}+  buffer (VP.Vector (I# off#) _ ba) f = buffer ba (\ba# _ -> f ba# off#)+  {-# INLINE buffer #-}+  mkBuffer ba# =+    let ba = mkBuffer ba#+     in VP.Vector 0 (bufferByteCount ba) ba+  {-# INLINE mkBuffer #-}+  bufferHasToBePinned = False -newMutableByteArray :: Bool -> Int -> ST s (MutableByteArray s)+instance Buffer (VS.Vector Word8) where+  bufferByteCount = VS.length+  {-# INLINE bufferByteCount #-}+  buffer v _f g =+    runST $ withForeignPtrST (fst $ VS.unsafeToForeignPtr0 v) $ \addr# -> pure (g addr#)+  {-# INLINE buffer #-}+  mkBuffer ba# =+    VS.unsafeFromForeignPtr0 (pinnedByteArrayToForeignPtr ba#) (I# (sizeofByteArray# ba#))+  {-# INLINE mkBuffer #-}+  bufferHasToBePinned = True++-- | Allocate a new uninitialized `MutableByteArray`.+--+-- * __Warning__ - Memory allocated might contain random garbage and must be fully overwritten.+--+-- __⚠__ - Violation of the above rule could lead non-determinism and breakage of referential+-- transparency.+--+-- @since 0.1.0+newMutableByteArray ::+  -- | Should the mutable array be allocated as pinned or not+  Bool ->+  -- | Size of the mutable array in number of bytes.+  Int ->+  ST s (MutableByteArray s) newMutableByteArray isPinned (I# len#) =   ST $ \s# -> case (if isPinned then newPinnedByteArray# else newByteArray#) len# s# of     (# s'#, mba# #) -> (# s'#, MutableByteArray mba# #) {-# INLINE newMutableByteArray #-} +-- | /O(1)/ - Cast a `MutableByteArray` to an immutable `ByteArray` without copy.+--+-- * __Warning__ - Source mutable array must not be mutated, after this action.+--+-- __⚠__ - Violation of the above rule could potentially lead to corrupt memory and segfaults.+--+-- @since 0.1.0 freezeMutableByteArray :: MutableByteArray d -> ST d ByteArray freezeMutableByteArray (MutableByteArray mba#) =   ST $ \s# -> case unsafeFreezeByteArray# mba# s# of     (# s'#, ba# #) -> (# s'#, ByteArray ba# #) --- | It is ok to use ByteString withing ST, as long as underlying pointer is never mutated--- or returned from the supplied action.+-- | Run ST action on the underlying `Ptr` that points to the beginning of the `ByteString`+-- buffer. It is ok to use ByteString withing ST, as long as underlying pointer is never mutated or+-- returned from the supplied action.+--+-- * __Warning__ - It is important for the supplied action to not produce bottom, i.e. runtime+-- exceptions or infinite loops are not allowed within its body.+--+-- __⚠__ - Violation of the above rule could potentially lead to corrupt memory and segfaults.+--+-- @since 0.1.0 withPtrByteStringST :: BS.ByteString -> (Ptr a -> ST s b) -> ST s b+withPtrByteStringST bs f = withAddrByteStringST bs $ \addr# -> f (Ptr addr#)+{-# INLINE withPtrByteStringST #-}++-- | Same as `withPtrByteStringST`, except the supplied action expects an `Addr#` instead of a+-- `Ptr`.+--+-- __⚠__ - Violation of the rule from `withPtrByteStringST` could potentially lead to corrupt memory+-- and segfaults.+--+-- @since 0.2.0+withAddrByteStringST :: BS.ByteString -> (Addr# -> ST s b) -> ST s b #if MIN_VERSION_bytestring(0,11,0)-withPtrByteStringST (BS.BS (ForeignPtr addr# ptrContents) _) f = do+withAddrByteStringST (BS.BS fp _) = withForeignPtrST fp #else-withPtrByteStringST (BS.PS (ForeignPtr addr0# ptrContents) (I# offset#) _) f = do-  let !addr# = addr0# `plusAddr#` offset#+withAddrByteStringST (BS.PS fp offset _) = withForeignPtrST (fp `plusForeignPtr` offset) #endif-  !r <- f (Ptr addr#)-  -- It is safe to use `touch#` within ST, so `unsafeCoerce#` is OK+{-# INLINE withAddrByteStringST #-}++-- | Run an `ST` action on the underlying `Ptr` that points to the beginning of the `ByteString`+-- buffer. It is ok to use ByteString withing ST, as long as underlying pointer is never mutated or+-- returned from the supplied action.+--+-- * __Warning__ - It is important for the memory that backs the underlying `ForeignPtr` to not be+-- mutated outside of the `ST` monad that this action operates in, which is only allowed if it was+-- allocated in this execution of the `ST` monad.+--+-- * __Warning__ - It is important for the memory that backs the underlying `ForeignPtr` to not be+-- mutated at all if `ForeignPtr` was not allocated within the `ST` monad that this action operates+-- in.+--+-- * __Warning__ - It is important for the supplied action to not produce bottom, i.e. runtime+-- exceptions or infinite loops are not allowed within its body.+--+-- __⚠__ - Violation of the above rules could potentially lead to corrupt memory and segfaults.+--+-- @since 0.2.0+withForeignPtrST :: ForeignPtr a -> (Addr# -> ST s b) -> ST s b+withForeignPtrST (ForeignPtr addr# ptrContents) f = do+  !r <- f addr#+  -- It is safe to use `touch#` within ST, so using `unsafeCoerce#` here is totally OK   ST $ \s# -> (# unsafeCoerce# (touch# ptrContents (unsafeCoerce# s#)), () #)   pure r-{-# INLINE withPtrByteStringST #-}+{-# INLINE withForeignPtrST #-} +-- | /O(1)/ - Convert a pinned `ByteArray` to `BS.ByteString`.+--+-- * __Warning__ - There is no check that source `ByteArray` was allocated as pinned, so user of this+-- function must guarantee this invariant.+--+-- __⚠__ - Violation of the above rules could potentially lead to corrupt memory and segfaults.+--+-- @since 0.1.0 pinnedByteArrayToByteString :: ByteArray -> BS.ByteString pinnedByteArrayToByteString (ByteArray ba#) =   BS.PS (pinnedByteArrayToForeignPtr ba#) 0 (I# (sizeofByteArray# ba#)) {-# INLINE pinnedByteArrayToByteString #-} +-- | /O(1)/ - Convert a pinned `ByteArray#` to `ForeignPtr`.+--+-- * __Warning__ - There is no check that source `ByteArray#` was allocated as pinned, so user of this+-- function must guarantee this invariant.+--+-- __⚠__ - Violation of the above rules could potentially lead to corrupt memory and segfaults.+--+-- @since 0.1.0 pinnedByteArrayToForeignPtr :: ByteArray# -> ForeignPtr a pinnedByteArrayToForeignPtr ba# =   ForeignPtr (byteArrayContents# ba#) (PlainPtr (unsafeCoerce# ba#)) {-# INLINE pinnedByteArrayToForeignPtr #-} -+-- | /O(1)/ - Convert `ByteArray` to `SBS.ShortByteString`+--+-- @since 0.1.0 byteArrayToShortByteString :: ByteArray -> SBS.ShortByteString byteArrayToShortByteString (ByteArray ba#) = SBS.SBS ba# {-# INLINE byteArrayToShortByteString #-} +-- | /O(1)/ - Inverse of `byteArrayToShortByteString`. Convert `SBS.ShortByteString` to  `ByteArray`+--+-- @since 0.1.0 byteArrayFromShortByteString :: SBS.ShortByteString -> ByteArray byteArrayFromShortByteString (SBS.SBS ba#) = ByteArray ba# {-# INLINE byteArrayFromShortByteString #-}
src/Data/MemPack/Error.hs view
@@ -6,7 +6,7 @@  -- | -- Module      : Data.MemPack.Error--- Copyright   : (c) Alexey Kuleshevich 2024+-- Copyright   : (c) Alexey Kuleshevich 2024-2025 -- License     : BSD3 -- Maintainer  : Alexey Kuleshevich <alexey@kuleshevi.ch> -- Stability   : experimental
tests/Test/Common.hs view
@@ -9,10 +9,17 @@  import Data.Array.Byte (ByteArray (..)) import Data.ByteString (ByteString)-import Data.ByteString.Short.Internal as SBS (ShortByteString (..)) import qualified Data.ByteString.Lazy as BSL+import Data.ByteString.Short.Internal as SBS (ShortByteString (..)) import Data.MemPack.Buffer (byteArrayFromShortByteString)+import Data.Primitive.Array (Array)+import Data.Primitive.PrimArray (PrimArray (..), primArrayFromList)+import Data.Primitive.Types (Prim) import qualified Data.Text as T+import qualified Data.Vector.Generic as VG (fromList)+import qualified Data.Vector.Primitive as VP (Vector (..), drop)+import qualified Data.Vector.Storable as VS (Storable, Vector, drop)+import GHC.Exts (fromList) import System.Random.Stateful import Test.Hspec as X import Test.Hspec.QuickCheck as X@@ -50,6 +57,22 @@  instance Arbitrary T.Text where   arbitrary = T.pack <$> arbitrary++instance (Prim a, Arbitrary a) => Arbitrary (PrimArray a) where+  arbitrary = primArrayFromList <$> arbitrary++instance Arbitrary a => Arbitrary (Array a) where+  arbitrary = fromList <$> arbitrary++instance (Prim a, Arbitrary a) => Arbitrary (VP.Vector a) where+  arbitrary = do+    v <- VG.fromList <$> arbitrary+    frequency [(5, pure v), (5, flip VP.drop v . getNonNegative <$> arbitrary)]++instance (VS.Storable a, Arbitrary a) => Arbitrary (VS.Vector a) where+  arbitrary = do+    v <- VG.fromList <$> arbitrary+    frequency [(5, pure v), (5, flip VS.drop v . getNonNegative <$> arbitrary)]  qcByteArray :: Int -> Gen ByteArray qcByteArray n = byteArrayFromShortByteString <$> qcShortByteString n
tests/Test/MemPackSpec.hs view
@@ -18,8 +18,8 @@ import Data.Array.Byte (ByteArray) import Data.Bits import Data.ByteString (ByteString)-import Data.ByteString.Short (ShortByteString) import qualified Data.ByteString.Lazy as BSL+import Data.ByteString.Short (ShortByteString) import Data.Complex import Data.Either (isLeft) import Data.Function (fix)@@ -27,8 +27,11 @@ import Data.MemPack import Data.MemPack.Buffer import Data.MemPack.Error+import Data.Primitive.Array (Array)+import Data.Primitive.PrimArray (PrimArray (..)) import Data.Ratio import Data.Text (Text)+import qualified Data.Vector.Primitive as VP (Vector (..)) import Data.Word import Foreign.Ptr (IntPtr (..), Ptr, intPtrToPtr) import Foreign.StablePtr (StablePtr, castPtrToStablePtr, castStablePtrToPtr)@@ -145,7 +148,7 @@   unpackM =     (IntCase <$> unpackCase 0) <|> (Word16Case <$> unpackCase 1)     where-      unpackCase :: (Buffer b, MemPack a) => Tag -> Unpack b a+      unpackCase :: (Buffer b, MemPack a) => Tag -> Unpack s b a       unpackCase t = do         t' <- unpackM         unless (t == t') $ F.fail "Tag mismatch"@@ -196,6 +199,8 @@       it "ByteArray" $ failOnEmpty (mempty :: ByteArray)       it "ByteString" $ failOnEmpty (mempty :: ByteString)       it "ShortByteString" $ failOnEmpty (mempty :: ShortByteString)+      it "PrimArray" $ failOnEmpty (mempty :: PrimArray Word8)+      it "VP.Vector" $ failOnEmpty (mempty :: VP.Vector Word8)     prop "Fail on too much" $ expectNotFullyConsumed @a  spec :: Spec@@ -240,7 +245,8 @@   memPackSpec @ByteArray   memPackSpec @ByteString   memPackSpec @BSL.ByteString-  memPackSpec @ByteArray+  memPackSpec @(PrimArray Char)+  memPackSpec @(Array Integer)   memPackSpec @Text   memPackSpec @Backtrack   prop "Out of bound char" $ forAll (choose (0x110000, maxBound :: Word32)) $ \w32 ->