• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lehins / mempack / 2

06 Sep 2025 05:59PM UTC coverage: 86.131% (+0.1%) from 85.987%
2

push

github

web-flow
Merge eafc400c4 into 072e28fea

53 of 62 new or added lines in 2 files covered. (85.48%)

56 existing lines in 3 files now uncovered.

708 of 822 relevant lines covered (86.13%)

1.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.21
/src/Data/MemPack.hs
1
{-# LANGUAGE AllowAmbiguousTypes #-}
2
{-# LANGUAGE BangPatterns #-}
3
{-# LANGUAGE BinaryLiterals #-}
4
{-# LANGUAGE CPP #-}
5
{-# LANGUAGE DefaultSignatures #-}
6
{-# LANGUAGE FlexibleInstances #-}
7
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8
{-# LANGUAGE LambdaCase #-}
9
{-# LANGUAGE MagicHash #-}
10
{-# LANGUAGE MultiParamTypeClasses #-}
11
{-# LANGUAGE NumericUnderscores #-}
12
{-# LANGUAGE RankNTypes #-}
13
{-# LANGUAGE ScopedTypeVariables #-}
14
{-# LANGUAGE TupleSections #-}
15
{-# LANGUAGE TypeApplications #-}
16
{-# LANGUAGE UnboxedTuples #-}
17

18
-- |
19
-- Module      : Data.MemPack
20
-- Copyright   : (c) Alexey Kuleshevich 2024-2025
21
-- License     : BSD3
22
-- Maintainer  : Alexey Kuleshevich <alexey@kuleshevi.ch>
23
-- Stability   : experimental
24
-- Portability : non-portable
25
module Data.MemPack (
26
  Pack (..),
27
  Unpack (..),
28
  MemPack (..),
29

30
  -- * Packing
31
  pack,
32
  packByteString,
33
  packShortByteString,
34

35
  -- ** Generalized
36
  packByteArray,
37
  packWithByteArray,
38
  packMutableByteArray,
39
  packWithMutableByteArray,
40

41
  -- ** Helpers
42
  packIncrement,
43
  guardAdvanceUnpack,
44

45
  -- * Unpacking
46
  unpack,
47
  unpackFail,
48
  unpackMonadFail,
49
  unpackError,
50
  unpackLeftOver,
51

52
  -- ** Helpers
53
  failUnpack,
54
  unpackByteArray,
55
  unpackByteArrayLen,
56
  packByteStringM,
57
  unpackByteStringM,
58
  packLiftST,
59
  unpackLiftST,
60

61
  -- * Helper packers
62
  VarLen (..),
63
  Length (..),
64
  Tag (..),
65
  packTagM,
66
  unpackTagM,
67
  unknownTagM,
68
  packedTagByteCount,
69

70
  -- * Internal utilities
71
  replicateTailM,
72
  lift_#,
73
  st_,
74

75
  -- * Re-exports for @GeneralizedNewtypeDeriving@
76
  StateT (..),
77
  FailT (..),
78
) where
79

80
#include "MachDeps.h"
81

82
import Data.Foldable (foldMap')
83
import Control.Applicative (Alternative (..))
84
import Control.Monad (join, unless, when)
85
import qualified Control.Monad.Fail as F
86
import Control.Monad.Reader (MonadReader (..), lift)
87
import Control.Monad.State.Strict (MonadState (..), StateT (..), execStateT)
88
import Control.Monad.Trans.Fail (Fail, FailT (..), errorFail, failT, runFailAgg, runFailAggT)
89
import Data.Array.Byte (ByteArray (..), MutableByteArray (..))
90
import Data.Bifunctor (first)
91
import Data.Bits (Bits (..), FiniteBits (..))
92
import Data.ByteString (ByteString)
93
import qualified Data.ByteString.Lazy as BSL
94
import qualified Data.ByteString.Lazy.Internal as BSL
95
import Data.ByteString.Short (ShortByteString)
96
import Data.Char (ord)
97
import Data.Complex (Complex (..))
98
import Data.Foldable (foldMap')
99
import Data.List (intercalate)
100
import Data.MemPack.Buffer
101
import Data.MemPack.Error
102
import Data.Primitive.Array (Array (..), newArray, sizeofArray, unsafeFreezeArray, writeArray)
103
import Data.Primitive.PrimArray (PrimArray (..), sizeofPrimArray)
104
import Data.Primitive.Types (Prim (sizeOf#))
105
import Data.Ratio
106
import Data.Semigroup (Sum (..))
107
#if MIN_VERSION_text(2,0,0)
108
import qualified Data.Text.Array as T
109
#endif
110
import qualified Data.Text.Encoding as T
111
import Data.Text.Internal (Text (..))
112
import Data.Typeable
113
import Data.Void (Void, absurd)
114
import GHC.Exts
115
import GHC.Int
116
import GHC.ST (ST (..), runST)
117
import GHC.Stable (StablePtr (..))
118
import GHC.Stack (HasCallStack)
119
import GHC.Word
120
import Numeric (showHex)
121
import Prelude hiding (fail)
122
#if __GLASGOW_HASKELL__ >= 900
123
import GHC.Num.Integer (Integer (..), integerCheck)
124
import GHC.Num.Natural (Natural (..), naturalCheck)
125
#elif defined(MIN_VERSION_integer_gmp)
126
import GHC.Integer.GMP.Internals (Integer (..), BigNat(BN#), isValidInteger#)
127
import GHC.Natural (Natural (..), isValidNatural)
128
#else
129
#error "Only integer-gmp is supported for now for older compilers"
130
#endif
131
#if !(MIN_VERSION_base(4,13,0))
132
import Prelude (fail)
133
#endif
134
#if !MIN_VERSION_primitive(0,8,0)
135
import qualified Data.Primitive.ByteArray as Prim (ByteArray(..))
136
#endif
137

138
-- | Monad that is used for serializing data into a `MutableByteArray`. It is based on
139
-- `StateT` that tracks the current index into the `MutableByteArray` where next write is
140
-- expected to happen.
141
newtype Pack s a = Pack
142
  { runPack :: MutableByteArray s -> StateT Int (ST s) a
2✔
143
  }
144

UNCOV
145
instance Functor (Pack s) where
×
146
  fmap f (Pack p) = Pack $ \buf -> fmap f (p buf)
×
147
  {-# INLINE fmap #-}
UNCOV
148
instance Applicative (Pack s) where
×
149
  pure = Pack . const . pure
2✔
150
  {-# INLINE pure #-}
UNCOV
151
  Pack a1 <*> Pack a2 =
×
152
    Pack $ \buf -> a1 buf <*> a2 buf
×
153
  {-# INLINE (<*>) #-}
UNCOV
154
  Pack a1 *> Pack a2 =
×
155
    Pack $ \buf -> a1 buf *> a2 buf
×
156
  {-# INLINE (*>) #-}
157
instance Monad (Pack s) where
2✔
158
  Pack m1 >>= p =
2✔
159
    Pack $ \buf -> m1 buf >>= \res -> runPack (p res) buf
2✔
160
  {-# INLINE (>>=) #-}
161
instance MonadReader (MutableByteArray s) (Pack s) where
162
  ask = Pack pure
2✔
163
  {-# INLINE ask #-}
UNCOV
164
  local f (Pack p) = Pack (p . f)
×
165
  {-# INLINE local #-}
UNCOV
166
  reader f = Pack (pure . f)
×
167
  {-# INLINE reader #-}
168
instance MonadState Int (Pack s) where
UNCOV
169
  get = Pack $ const get
×
170
  {-# INLINE get #-}
UNCOV
171
  put = Pack . const . put
×
172
  {-# INLINE put #-}
173
  state = Pack . const . state
2✔
174
  {-# INLINE state #-}
175

176
-- | Monad that is used for deserializing data from a memory `Buffer`. It is based on
177
-- `StateT` that tracks the current index into the @`Buffer` a@, from where the next read
178
-- suppose to happen. Unpacking can `F.fail` with `F.MonadFail` instance or with
179
-- `failUnpack` that provides a more type safe way of failing using `Error` interface.
180
newtype Unpack s b a = Unpack
181
  { runUnpack :: b -> StateT Int (FailT SomeError (ST s)) a
2✔
182
  }
183

NEW
184
instance Functor (Unpack s b) where
×
185
  fmap f (Unpack p) = Unpack $ \buf -> fmap f (p buf)
2✔
186
  {-# INLINE fmap #-}
NEW
187
instance Applicative (Unpack s b) where
×
188
  pure = Unpack . const . pure
2✔
189
  {-# INLINE pure #-}
190
  Unpack a1 <*> Unpack a2 =
2✔
191
    Unpack $ \buf -> a1 buf <*> a2 buf
1✔
192
  {-# INLINE (<*>) #-}
UNCOV
193
  Unpack a1 *> Unpack a2 =
×
194
    Unpack $ \buf -> a1 buf *> a2 buf
×
195
  {-# INLINE (*>) #-}
NEW
196
instance Monad (Unpack s b) where
×
197
  Unpack m1 >>= p =
2✔
198
    Unpack $ \buf -> m1 buf >>= \res -> runUnpack (p res) buf
2✔
199
  {-# INLINE (>>=) #-}
200
#if !(MIN_VERSION_base(4,13,0))
201
  fail = Unpack . const . F.fail
202
#endif
203
instance F.MonadFail (Unpack s b) where
204
  fail = Unpack . const . F.fail
2✔
205
instance MonadReader b (Unpack s b) where
206
  ask = Unpack pure
2✔
207
  {-# INLINE ask #-}
UNCOV
208
  local f (Unpack p) = Unpack (p . f)
×
209
  {-# INLINE local #-}
UNCOV
210
  reader f = Unpack (pure . f)
×
211
  {-# INLINE reader #-}
212
instance MonadState Int (Unpack s b) where
UNCOV
213
  get = Unpack $ const get
×
214
  {-# INLINE get #-}
UNCOV
215
  put = Unpack . const . put
×
216
  {-# INLINE put #-}
217
  state = Unpack . const . state
2✔
218
  {-# INLINE state #-}
219

NEW
220
instance Alternative (Unpack s b) where
×
221
  empty = Unpack $ \_ -> lift empty
×
222
  {-# INLINE empty #-}
223
  Unpack r1 <|> Unpack r2 =
2✔
224
    Unpack $ \buf ->
2✔
225
      case r1 buf of
2✔
226
        StateT m1 ->
227
          case r2 buf of
2✔
228
            StateT m2 -> StateT $ \s -> m1 s <|> m2 s
2✔
229
  {-# INLINE (<|>) #-}
230

231
-- | Failing unpacking with an `Error`.
232
failUnpack :: Error e => e -> Unpack s b a
233
failUnpack e = Unpack $ \_ -> lift $ failT (toSomeError e)
2✔
234

235
-- | Efficient serialization interface that operates directly on memory buffers.
236
class MemPack a where
237
  -- | Name of the type that is being deserialized for error reporting. Default
238
  -- implementation relies on `Typeable`.
239
  typeName :: String
240
  default typeName :: Typeable a => String
241
  typeName = show (typeRep (Proxy @a))
1✔
242

243
  -- | Report the exact size in number of bytes that packed version of this type will
244
  -- occupy. It is very important to get this right, otherwise `packM` will result in a
245
  -- runtime exception. Another words this is the expected property that it should hold:
246
  --
247
  -- prop> packedByteCount a == bufferByteCount (pack a)
248
  packedByteCount :: a -> Int
249

250
  -- | Write binary representation of a type into the `MutableByteArray` which can be
251
  -- accessed with `ask`, whenever direct operations on it are necessary.
252
  packM :: a -> Pack s ()
253

254
  -- | Read binary representation of the type directly from the buffer, which can be
255
  -- accessed with `ask` when necessary. Direct reads from the buffer should be preceded
256
  -- with advancing the buffer offset with `MonadState` by the number of bytes that will
257
  -- be consumed from the buffer and making sure that no reads outside of the buffer can
258
  -- happen. Violation of these rules will lead to segfaults.
259
  unpackM :: Buffer b => Unpack s b a
260

UNCOV
261
instance MemPack () where
×
262
  packedByteCount _ = 0
2✔
263
  {-# INLINE packedByteCount #-}
264
  packM () = pure ()
1✔
265
  {-# INLINE packM #-}
266
  unpackM = pure ()
2✔
267
  {-# INLINE unpackM #-}
268

UNCOV
269
instance MemPack Void where
×
270
  packedByteCount _ = 0
×
271
  packM = absurd
×
272
  unpackM = F.fail "Void is unpackable"
×
273

274
instance MemPack Bool where
2✔
275
  packedByteCount _ = packedTagByteCount
2✔
276
  {-# INLINE packedByteCount #-}
277
  packM x = packTagM $ if x then 1 else 0
2✔
278
  {-# INLINE packM #-}
279
  unpackM =
2✔
280
    unpackTagM >>= \case
2✔
281
      0 -> pure False
2✔
282
      1 -> pure True
2✔
UNCOV
283
      n -> F.fail $ "Invalid value detected for Bool: " ++ show n
×
284
  {-# INLINE unpackM #-}
285

286
instance MemPack a => MemPack (Maybe a) where
287
  typeName = "Maybe " ++ typeName @a
2✔
288
  packedByteCount = \case
2✔
289
    Nothing -> packedTagByteCount
2✔
290
    Just a -> packedTagByteCount + packedByteCount a
2✔
291
  {-# INLINE packedByteCount #-}
292
  packM = \case
2✔
293
    Nothing -> packTagM 0
2✔
294
    Just a -> packTagM 1 >> packM a
2✔
295
  {-# INLINE packM #-}
296
  unpackM =
2✔
297
    unpackTagM >>= \case
2✔
298
      0 -> pure Nothing
2✔
299
      1 -> Just <$> unpackM
2✔
UNCOV
300
      n -> unknownTagM @(Maybe a) n
×
301
  {-# INLINE unpackM #-}
302

303
instance (MemPack a, MemPack b) => MemPack (Either a b) where
304
  typeName = "Either " ++ typeName @a ++ " " ++ typeName @b
2✔
305
  packedByteCount = \case
2✔
306
    Left a -> packedTagByteCount + packedByteCount a
1✔
307
    Right b -> packedTagByteCount + packedByteCount b
1✔
308
  {-# INLINE packedByteCount #-}
309
  packM = \case
2✔
310
    Left a -> packTagM 0 >> packM a
2✔
311
    Right b -> packTagM 1 >> packM b
2✔
312
  {-# INLINE packM #-}
313
  unpackM =
2✔
314
    unpackTagM >>= \case
2✔
315
      0 -> Left <$> unpackM
2✔
316
      1 -> Right <$> unpackM
2✔
UNCOV
317
      n -> unknownTagM @(Either a b) n
×
318
  {-# INLINE unpackM #-}
319

320
instance MemPack Char where
2✔
321
  packedByteCount _ = SIZEOF_HSCHAR
2✔
322
  {-# INLINE packedByteCount #-}
323
  packM a@(C# a#) = do
2✔
324
    MutableByteArray mba# <- ask
2✔
325
    I# i# <- packIncrement a
1✔
326
    lift_# (writeWord8ArrayAsWideChar# mba# i# a#)
2✔
327
  {-# INLINE packM #-}
328
  unpackM = do
2✔
329
    I# i# <- guardAdvanceUnpack SIZEOF_HSCHAR
2✔
330
    buf <- ask
2✔
331
    let c =
2✔
332
          buffer
2✔
333
            buf
2✔
334
            (\ba# off# -> C# (indexWord8ArrayAsWideChar# ba# (i# +# off#)))
2✔
335
            (\addr# -> C# (indexWideCharOffAddr# (addr# `plusAddr#` i#) 0#))
2✔
336
        ordc :: Word32
337
        ordc = fromIntegral (ord c)
2✔
338
    when (ordc > 0x10FFFF) $
2✔
339
      F.fail $
2✔
UNCOV
340
        "Out of bounds Char was detected: '\\x" ++ showHex ordc "'"
×
341
    pure c
2✔
342
  {-# INLINE unpackM #-}
343

344
instance MemPack Float where
2✔
345
  packedByteCount _ = SIZEOF_FLOAT
2✔
346
  {-# INLINE packedByteCount #-}
347
  packM a@(F# a#) = do
2✔
348
    MutableByteArray mba# <- ask
2✔
349
    I# i# <- packIncrement a
1✔
350
    lift_# (writeWord8ArrayAsFloat# mba# i# a#)
2✔
351
  {-# INLINE packM #-}
352
  unpackM = do
2✔
353
    I# i# <- guardAdvanceUnpack SIZEOF_FLOAT
2✔
354
    buf <- ask
2✔
355
    pure $!
2✔
356
      buffer
2✔
357
        buf
2✔
358
        (\ba# off# -> F# (indexWord8ArrayAsFloat# ba# (i# +# off#)))
2✔
359
        (\addr# -> F# (indexFloatOffAddr# (addr# `plusAddr#` i#) 0#))
2✔
360
  {-# INLINE unpackM #-}
361

362
instance MemPack Double where
2✔
363
  packedByteCount _ = SIZEOF_DOUBLE
2✔
364
  {-# INLINE packedByteCount #-}
365
  packM a@(D# a#) = do
2✔
366
    MutableByteArray mba# <- ask
2✔
367
    I# i# <- packIncrement a
1✔
368
    lift_# (writeWord8ArrayAsDouble# mba# i# a#)
2✔
369
  {-# INLINE packM #-}
370
  unpackM = do
2✔
371
    I# i# <- guardAdvanceUnpack SIZEOF_DOUBLE
2✔
372
    buf <- ask
2✔
373
    pure $!
2✔
374
      buffer
2✔
375
        buf
2✔
376
        (\ba# off# -> D# (indexWord8ArrayAsDouble# ba# (i# +# off#)))
2✔
377
        (\addr# -> D# (indexDoubleOffAddr# (addr# `plusAddr#` i#) 0#))
2✔
378
  {-# INLINE unpackM #-}
379

380
instance MemPack (Ptr a) where
381
  typeName = "Ptr"
2✔
382
  packedByteCount _ = SIZEOF_HSPTR
2✔
383
  {-# INLINE packedByteCount #-}
384
  packM a@(Ptr a#) = do
2✔
385
    MutableByteArray mba# <- ask
2✔
386
    I# i# <- packIncrement a
1✔
387
    lift_# (writeWord8ArrayAsAddr# mba# i# a#)
2✔
388
  {-# INLINE packM #-}
389
  unpackM = do
2✔
390
    I# i# <- guardAdvanceUnpack SIZEOF_HSPTR
2✔
391
    buf <- ask
2✔
392
    pure $!
2✔
393
      buffer
2✔
394
        buf
2✔
395
        (\ba# off# -> Ptr (indexWord8ArrayAsAddr# ba# (i# +# off#)))
2✔
396
        (\addr# -> Ptr (indexAddrOffAddr# (addr# `plusAddr#` i#) 0#))
2✔
397
  {-# INLINE unpackM #-}
398

399
instance MemPack (StablePtr a) where
400
  typeName = "StablePtr"
2✔
401
  packedByteCount _ = SIZEOF_HSSTABLEPTR
2✔
402
  {-# INLINE packedByteCount #-}
403
  packM a@(StablePtr a#) = do
2✔
404
    MutableByteArray mba# <- ask
2✔
405
    I# i# <- packIncrement a
1✔
406
    lift_# (writeWord8ArrayAsStablePtr# mba# i# a#)
2✔
407
  {-# INLINE packM #-}
408
  unpackM = do
2✔
409
    I# i# <- guardAdvanceUnpack SIZEOF_HSSTABLEPTR
2✔
410
    buf <- ask
2✔
411
    pure $!
2✔
412
      buffer
2✔
413
        buf
2✔
414
        (\ba# off# -> StablePtr (indexWord8ArrayAsStablePtr# ba# (i# +# off#)))
2✔
415
        (\addr# -> StablePtr (indexStablePtrOffAddr# (addr# `plusAddr#` i#) 0#))
2✔
416
  {-# INLINE unpackM #-}
417

418
instance MemPack Int where
2✔
419
  packedByteCount _ = SIZEOF_HSINT
2✔
420
  {-# INLINE packedByteCount #-}
421
  packM a@(I# a#) = do
2✔
422
    MutableByteArray mba# <- ask
2✔
423
    I# i# <- packIncrement a
1✔
424
    lift_# (writeWord8ArrayAsInt# mba# i# a#)
2✔
425
  {-# INLINE packM #-}
426
  unpackM = do
2✔
427
    I# i# <- guardAdvanceUnpack SIZEOF_HSINT
2✔
428
    buf <- ask
2✔
429
    pure $!
2✔
430
      buffer
2✔
431
        buf
2✔
432
        (\ba# off# -> I# (indexWord8ArrayAsInt# ba# (i# +# off#)))
2✔
433
        (\addr# -> I# (indexIntOffAddr# (addr# `plusAddr#` i#) 0#))
2✔
434
  {-# INLINE unpackM #-}
435

436
instance MemPack Int8 where
2✔
437
  packedByteCount _ = SIZEOF_INT8
2✔
438
  {-# INLINE packedByteCount #-}
439
  packM a@(I8# a#) = do
2✔
440
    MutableByteArray mba# <- ask
2✔
441
    I# i# <- packIncrement a
1✔
442
    lift_# (writeInt8Array# mba# i# a#)
2✔
443
  {-# INLINE packM #-}
444
  unpackM = do
2✔
445
    I# i# <- guardAdvanceUnpack SIZEOF_INT8
2✔
446
    buf <- ask
2✔
447
    pure $!
2✔
448
      buffer
2✔
449
        buf
2✔
450
        (\ba# off# -> I8# (indexInt8Array# ba# (i# +# off#)))
2✔
451
        (\addr# -> I8# (indexInt8OffAddr# (addr# `plusAddr#` i#) 0#))
2✔
452
  {-# INLINE unpackM #-}
453

454
instance MemPack Int16 where
2✔
455
  packedByteCount _ = SIZEOF_INT16
2✔
456
  {-# INLINE packedByteCount #-}
457
  packM a@(I16# a#) = do
2✔
458
    MutableByteArray mba# <- ask
2✔
459
    I# i# <- packIncrement a
1✔
460
    lift_# (writeWord8ArrayAsInt16# mba# i# a#)
2✔
461
  {-# INLINE packM #-}
462
  unpackM = do
2✔
463
    buf <- ask
2✔
464
    I# i# <- guardAdvanceUnpack SIZEOF_INT16
2✔
465
    pure $!
2✔
466
      buffer
2✔
467
        buf
2✔
468
        (\ba# off# -> I16# (indexWord8ArrayAsInt16# ba# (i# +# off#)))
2✔
469
        (\addr# -> I16# (indexInt16OffAddr# (addr# `plusAddr#` i#) 0#))
2✔
470
  {-# INLINE unpackM #-}
471

472
instance MemPack Int32 where
2✔
473
  packedByteCount _ = SIZEOF_INT32
2✔
474
  {-# INLINE packedByteCount #-}
475
  packM a@(I32# a#) = do
2✔
476
    MutableByteArray mba# <- ask
2✔
477
    I# i# <- packIncrement a
1✔
478
    lift_# (writeWord8ArrayAsInt32# mba# i# a#)
2✔
479
  {-# INLINE packM #-}
480
  unpackM = do
2✔
481
    buf <- ask
2✔
482
    I# i# <- guardAdvanceUnpack SIZEOF_INT32
2✔
483
    pure $!
2✔
484
      buffer
2✔
485
        buf
2✔
486
        (\ba# off# -> I32# (indexWord8ArrayAsInt32# ba# (i# +# off#)))
2✔
487
        (\addr# -> I32# (indexInt32OffAddr# (addr# `plusAddr#` i#) 0#))
2✔
488
  {-# INLINE unpackM #-}
489

490
instance MemPack Int64 where
2✔
491
  packedByteCount _ = SIZEOF_INT64
2✔
492
  {-# INLINE packedByteCount #-}
493
  packM a@(I64# a#) = do
2✔
494
    MutableByteArray mba# <- ask
2✔
495
    I# i# <- packIncrement a
1✔
496
    lift_# (writeWord8ArrayAsInt64# mba# i# a#)
2✔
497
  {-# INLINE packM #-}
498
  unpackM = do
2✔
499
    buf <- ask
2✔
500
    I# i# <- guardAdvanceUnpack SIZEOF_INT64
2✔
501
    pure $!
2✔
502
      buffer
2✔
503
        buf
2✔
504
        (\ba# off# -> I64# (indexWord8ArrayAsInt64# ba# (i# +# off#)))
2✔
505
        (\addr# -> I64# (indexInt64OffAddr# (addr# `plusAddr#` i#) 0#))
2✔
506
  {-# INLINE unpackM #-}
507

508
instance MemPack Word where
2✔
509
  packedByteCount _ = SIZEOF_HSWORD
2✔
510
  {-# INLINE packedByteCount #-}
511
  packM a@(W# a#) = do
2✔
512
    MutableByteArray mba# <- ask
2✔
513
    I# i# <- packIncrement a
1✔
514
    lift_# (writeWord8ArrayAsWord# mba# i# a#)
2✔
515
  {-# INLINE packM #-}
516
  unpackM = do
2✔
517
    I# i# <- guardAdvanceUnpack SIZEOF_HSWORD
2✔
518
    buf <- ask
2✔
519
    pure $!
2✔
520
      buffer
2✔
521
        buf
2✔
522
        (\ba# off# -> W# (indexWord8ArrayAsWord# ba# (i# +# off#)))
2✔
523
        (\addr# -> W# (indexWordOffAddr# (addr# `plusAddr#` i#) 0#))
2✔
524
  {-# INLINE unpackM #-}
525

526
instance MemPack Word8 where
2✔
527
  packedByteCount _ = SIZEOF_WORD8
2✔
528
  {-# INLINE packedByteCount #-}
529
  packM a@(W8# a#) = do
2✔
530
    MutableByteArray mba# <- ask
2✔
531
    I# i# <- packIncrement a
1✔
532
    lift_# (writeWord8Array# mba# i# a#)
2✔
533
  {-# INLINE packM #-}
534
  unpackM = do
2✔
535
    I# i# <- guardAdvanceUnpack SIZEOF_WORD8
2✔
536
    buf <- ask
2✔
537
    pure $!
2✔
538
      buffer
2✔
539
        buf
2✔
540
        (\ba# off# -> W8# (indexWord8Array# ba# (i# +# off#)))
2✔
541
        (\addr# -> W8# (indexWord8OffAddr# addr# i#))
2✔
542
  {-# INLINE unpackM #-}
543

544
instance MemPack Word16 where
2✔
545
  packedByteCount _ = SIZEOF_WORD16
2✔
546
  {-# INLINE packedByteCount #-}
547
  packM a@(W16# a#) = do
2✔
548
    MutableByteArray mba# <- ask
2✔
549
    I# i# <- packIncrement a
1✔
550
    lift_# (writeWord8ArrayAsWord16# mba# i# a#)
2✔
551
  {-# INLINE packM #-}
552
  unpackM = do
2✔
553
    buf <- ask
2✔
554
    I# i# <- guardAdvanceUnpack SIZEOF_WORD16
2✔
555
    pure $!
2✔
556
      buffer
2✔
557
        buf
2✔
558
        (\ba# off# -> W16# (indexWord8ArrayAsWord16# ba# (i# +# off#)))
2✔
559
        (\addr# -> W16# (indexWord16OffAddr# (addr# `plusAddr#` i#) 0#))
2✔
560
  {-# INLINE unpackM #-}
561

562
instance MemPack Word32 where
2✔
563
  packedByteCount _ = SIZEOF_WORD32
2✔
564
  {-# INLINE packedByteCount #-}
565
  packM a@(W32# a#) = do
2✔
566
    MutableByteArray mba# <- ask
2✔
567
    I# i# <- packIncrement a
1✔
568
    lift_# (writeWord8ArrayAsWord32# mba# i# a#)
2✔
569
  {-# INLINE packM #-}
570
  unpackM = do
2✔
571
    I# i# <- guardAdvanceUnpack SIZEOF_WORD32
2✔
572
    buf <- ask
2✔
573
    pure $!
2✔
574
      buffer
2✔
575
        buf
2✔
576
        (\ba# off# -> W32# (indexWord8ArrayAsWord32# ba# (i# +# off#)))
2✔
577
        (\addr# -> W32# (indexWord32OffAddr# (addr# `plusAddr#` i#) 0#))
2✔
578
  {-# INLINE unpackM #-}
579

580
instance MemPack Word64 where
2✔
581
  packedByteCount _ = SIZEOF_WORD64
2✔
582
  {-# INLINE packedByteCount #-}
583
  packM a@(W64# a#) = do
2✔
584
    MutableByteArray mba# <- ask
2✔
585
    I# i# <- packIncrement a
1✔
586
    lift_# (writeWord8ArrayAsWord64# mba# i# a#)
2✔
587
  {-# INLINE packM #-}
588
  unpackM = do
2✔
589
    I# i# <- guardAdvanceUnpack SIZEOF_WORD64
2✔
590
    buf <- ask
2✔
591
    pure $!
2✔
592
      buffer
2✔
593
        buf
2✔
594
        (\ba# off# -> W64# (indexWord8ArrayAsWord64# ba# (i# +# off#)))
2✔
595
        (\addr# -> W64# (indexWord64OffAddr# (addr# `plusAddr#` i#) 0#))
2✔
596
  {-# INLINE unpackM #-}
597

598
#if __GLASGOW_HASKELL__ >= 900
599
instance MemPack Integer where
2✔
600
  packedByteCount =
2✔
601
    (+ packedTagByteCount) . \case
2✔
602
      IS i# -> packedByteCount (I# i#)
1✔
603
      IP ba# -> packedByteCount (ByteArray ba#)
2✔
604
      IN ba# -> packedByteCount (ByteArray ba#)
2✔
605
  {-# INLINE packedByteCount #-}
606
  packM = \case
2✔
607
    IS i# -> packTagM 0 >> packM (I# i#)
2✔
608
    IP ba# -> packTagM 1 >> packM (ByteArray ba#)
2✔
609
    IN ba# -> packTagM 2 >> packM (ByteArray ba#)
2✔
610
  {-# INLINE packM #-}
611
  unpackM = do
2✔
612
    i <-
613
      unpackTagM >>= \case
2✔
614
        0 -> do
2✔
615
          I# i# <- unpackM
2✔
616
          pure $ IS i#
2✔
617
        1 -> do
2✔
618
          ByteArray ba# <- unpackM
2✔
619
          pure $ IP ba#
2✔
620
        2 -> do
2✔
621
          ByteArray ba# <- unpackM
2✔
622
          pure $ IN ba#
2✔
UNCOV
623
        t -> unknownTagM @Integer t
×
624
    unless (integerCheck i) $ F.fail $ "Invalid Integer decoded " ++ showInteger i
1✔
625
    pure i
2✔
626
    where
UNCOV
627
      showInteger = \case
×
628
        IS i# -> "IS " ++ show (I# i#)
×
629
        IP ba# -> "IP " ++ show (ByteArray ba#)
×
630
        IN ba# -> "IN " ++ show (ByteArray ba#)
×
631
  {-# INLINE unpackM #-}
632

633
instance MemPack Natural where
2✔
634
  packedByteCount =
2✔
635
    (+ packedTagByteCount) . \case
2✔
636
      NS w# -> packedByteCount (W# w#)
1✔
637
      NB ba# -> packedByteCount (ByteArray ba#)
2✔
638
  {-# INLINE packedByteCount #-}
639
  packM = \case
2✔
640
    NS w# -> packTagM 0 >> packM (W# w#)
2✔
641
    NB ba# -> packTagM 1 >> packM (ByteArray ba#)
2✔
642
  {-# INLINE packM #-}
643
  unpackM = do
2✔
644
    n <-
645
      unpackTagM >>= \case
2✔
646
        0 -> do
2✔
647
          W# w# <- unpackM
2✔
648
          pure $ NS w#
2✔
649
        1 -> do
2✔
650
          ByteArray ba# <- unpackM
2✔
651
          pure $ NB ba#
2✔
UNCOV
652
        t -> unknownTagM @Natural t
×
653
    unless (naturalCheck n) $ F.fail $ "Invalid Natural decoded " ++ showNatural n
1✔
654
    pure n
2✔
655
    where
UNCOV
656
      showNatural = \case
×
657
        NS w# -> "NS " ++ show (W# w#)
×
658
        NB ba# -> "NB " ++ show (ByteArray ba#)
×
659
  {-# INLINE unpackM #-}
660

661
#elif defined(MIN_VERSION_integer_gmp)
662

663
instance MemPack Integer where
664
  packedByteCount =
665
    (+ packedTagByteCount) . \case
666
      S# i# -> packedByteCount (I# i#)
667
      Jp# (BN# ba#) -> packedByteCount (ByteArray ba#)
668
      Jn# (BN# ba#) -> packedByteCount (ByteArray ba#)
669
  {-# INLINE packedByteCount #-}
670
  packM = \case
671
    S# i# -> packTagM 0 >> packM (I# i#)
672
    Jp# (BN# ba#) -> packTagM 1 >> packM (ByteArray ba#)
673
    Jn# (BN# ba#) -> packTagM 2 >> packM (ByteArray ba#)
674
  {-# INLINE packM #-}
675
  unpackM = do
676
    i <-
677
      unpackTagM >>= \case
678
        0 -> do
679
          I# i# <- unpackM
680
          pure $ S# i#
681
        1 -> do
682
          ByteArray ba# <- unpackM
683
          pure $ Jp# (BN# ba#)
684
        2 -> do
685
          ByteArray ba# <- unpackM
686
          pure $ Jn# (BN# ba#)
687
        t -> unknownTagM @Integer t
688
    unless (isTrue# (isValidInteger# i)) $ F.fail $ "Invalid Integer decoded " ++ showInteger i
689
    pure i
690
    where
691
      showInteger = \case
692
        S# i# -> "S# " ++ show (I# i#)
693
        Jp# (BN# ba#) -> "Jp# " ++ show (ByteArray ba#)
694
        Jn# (BN# ba#) -> "Jn# " ++ show (ByteArray ba#)
695
  {-# INLINE unpackM #-}
696

697
instance MemPack Natural where
698
  packedByteCount =
699
    (+ packedTagByteCount) . \case
700
      NatS# w# -> packedByteCount (W# w#)
701
      NatJ# (BN# ba#) -> packedByteCount (ByteArray ba#)
702
  {-# INLINE packedByteCount #-}
703
  packM = \case
704
    NatS# w# -> packTagM 0 >> packM (W# w#)
705
    NatJ# (BN# ba#) -> packTagM 1 >> packM (ByteArray ba#)
706
  {-# INLINE packM #-}
707
  unpackM = do
708
    n <-
709
      unpackTagM >>= \case
710
        0 -> do
711
          W# w# <- unpackM
712
          pure $ NatS# w#
713
        1 -> do
714
          ByteArray ba# <- unpackM
715
          pure $ NatJ# (BN# ba#)
716
        t -> unknownTagM @Natural t
717
    unless (isValidNatural n) $ F.fail $ "Invalid Natural decoded " ++ showNatural n
718
    pure n
719
    where
720
      showNatural = \case
721
        NatS# w# -> "NatS# " ++ show (W# w#)
722
        NatJ# (BN#  ba#) -> "NatJ# " ++ show (ByteArray ba#)
723
  {-# INLINE unpackM #-}
724

725
#endif
726

727
instance MemPack a => MemPack (Complex a) where
728
  typeName = "Complex " ++ typeName @a
2✔
729
  packedByteCount (a :+ b) = packedByteCount a + packedByteCount b
1✔
730
  {-# INLINE packedByteCount #-}
731
  packM (a :+ b) = packM a >> packM b
2✔
732
  {-# INLINE packM #-}
733
  unpackM = do
2✔
734
    !a <- unpackM
2✔
735
    !b <- unpackM
2✔
736
    pure (a :+ b)
2✔
737
  {-# INLINE unpackM #-}
738

739
instance (MemPack a, Integral a) => MemPack (Ratio a) where
740
  typeName = "Ratio " ++ typeName @a
2✔
741
  packedByteCount r = packedByteCount (numerator r) + packedByteCount (denominator r)
1✔
742
  {-# INLINE packedByteCount #-}
743
  packM r = packM (numerator r) >> packM (denominator r)
2✔
744
  {-# INLINE packM #-}
745
  unpackM = do
2✔
746
    !a <- unpackM
2✔
747
    !b <- unpackM
2✔
748
    when (b == 0) $ F.fail $ "Zero denominator was detected when unpacking " ++ typeName @(Ratio a)
1✔
749
    pure (a % b)
2✔
750
  {-# INLINE unpackM #-}
751

752
instance (MemPack a, MemPack b) => MemPack (a, b) where
753
  typeName = "(" ++ typeName @a ++ "," ++ typeName @b ++ ")"
2✔
754
  packedByteCount (a, b) = packedByteCount a + packedByteCount b
1✔
755
  {-# INLINE packedByteCount #-}
756
  packM (a, b) = packM a >> packM b
2✔
757
  {-# INLINEABLE packM #-}
758
  unpackM = do
2✔
759
    !a <- unpackM
2✔
760
    !b <- unpackM
2✔
761
    pure (a, b)
2✔
762
  {-# INLINEABLE unpackM #-}
763

764
instance (MemPack a, MemPack b, MemPack c) => MemPack (a, b, c) where
765
  typeName = "(" ++ typeName @a ++ "," ++ typeName @b ++ "," ++ typeName @c ++ ")"
2✔
766
  packedByteCount (a, b, c) = packedByteCount a + packedByteCount b + packedByteCount c
1✔
767
  {-# INLINE packedByteCount #-}
768
  packM (a, b, c) = packM a >> packM b >> packM c
2✔
769
  {-# INLINEABLE packM #-}
770
  unpackM = do
2✔
771
    !a <- unpackM
2✔
772
    !b <- unpackM
2✔
773
    !c <- unpackM
2✔
774
    pure (a, b, c)
2✔
775
  {-# INLINEABLE unpackM #-}
776

777
instance (MemPack a, MemPack b, MemPack c, MemPack d) => MemPack (a, b, c, d) where
778
  typeName = "(" ++ typeName @a ++ "," ++ typeName @b ++ "," ++ typeName @c ++ "," ++ typeName @d ++ ")"
2✔
779
  packedByteCount (a, b, c, d) = packedByteCount a + packedByteCount b + packedByteCount c + packedByteCount d
1✔
780
  {-# INLINE packedByteCount #-}
781
  packM (a, b, c, d) =
2✔
782
    packM a >> packM b >> packM c >> packM d
2✔
783
  {-# INLINEABLE packM #-}
784
  unpackM = do
2✔
785
    !a <- unpackM
2✔
786
    !b <- unpackM
2✔
787
    !c <- unpackM
2✔
788
    !d <- unpackM
2✔
789
    pure (a, b, c, d)
2✔
790
  {-# INLINEABLE unpackM #-}
791

792
instance (MemPack a, MemPack b, MemPack c, MemPack d, MemPack e) => MemPack (a, b, c, d, e) where
793
  typeName =
2✔
794
    "("
2✔
795
      ++ intercalate
2✔
796
        ","
2✔
797
        [ typeName @a
2✔
798
        , typeName @b
2✔
799
        , typeName @c
2✔
800
        , typeName @d
2✔
801
        , typeName @e
2✔
802
        ]
803
      ++ ")"
2✔
804
  packedByteCount (a, b, c, d, e) =
2✔
805
    packedByteCount a + packedByteCount b + packedByteCount c + packedByteCount d + packedByteCount e
1✔
806
  {-# INLINE packedByteCount #-}
807
  packM (a, b, c, d, e) =
2✔
808
    packM a >> packM b >> packM c >> packM d >> packM e
2✔
809
  {-# INLINEABLE packM #-}
810
  unpackM = do
2✔
811
    !a <- unpackM
2✔
812
    !b <- unpackM
2✔
813
    !c <- unpackM
2✔
814
    !d <- unpackM
2✔
815
    !e <- unpackM
2✔
816
    pure (a, b, c, d, e)
2✔
817
  {-# INLINEABLE unpackM #-}
818

819
instance (MemPack a, MemPack b, MemPack c, MemPack d, MemPack e, MemPack f) => MemPack (a, b, c, d, e, f) where
820
  typeName =
2✔
821
    "("
2✔
822
      ++ intercalate
2✔
823
        ","
2✔
824
        [ typeName @a
2✔
825
        , typeName @b
2✔
826
        , typeName @c
2✔
827
        , typeName @d
2✔
828
        , typeName @e
2✔
829
        , typeName @f
2✔
830
        ]
831
      ++ ")"
2✔
832
  packedByteCount (a, b, c, d, e, f) =
2✔
833
    packedByteCount a
1✔
834
      + packedByteCount b
1✔
835
      + packedByteCount c
2✔
836
      + packedByteCount d
2✔
837
      + packedByteCount e
1✔
838
      + packedByteCount f
2✔
839
  {-# INLINE packedByteCount #-}
840
  packM (a, b, c, d, e, f) =
2✔
841
    packM a >> packM b >> packM c >> packM d >> packM e >> packM f
2✔
842
  {-# INLINEABLE packM #-}
843
  unpackM = do
2✔
844
    !a <- unpackM
2✔
845
    !b <- unpackM
2✔
846
    !c <- unpackM
2✔
847
    !d <- unpackM
2✔
848
    !e <- unpackM
2✔
849
    !f <- unpackM
2✔
850
    pure (a, b, c, d, e, f)
2✔
851
  {-# INLINEABLE unpackM #-}
852

853
instance
854
  (MemPack a, MemPack b, MemPack c, MemPack d, MemPack e, MemPack f, MemPack g) =>
855
  MemPack (a, b, c, d, e, f, g)
856
  where
857
  typeName =
2✔
858
    "("
2✔
859
      ++ intercalate
2✔
860
        ","
2✔
861
        [ typeName @a
2✔
862
        , typeName @b
2✔
863
        , typeName @c
2✔
864
        , typeName @d
2✔
865
        , typeName @e
2✔
866
        , typeName @f
2✔
867
        , typeName @g
2✔
868
        ]
869
      ++ ")"
2✔
870
  packedByteCount (a, b, c, d, e, f, g) =
2✔
871
    packedByteCount a
1✔
872
      + packedByteCount b
1✔
873
      + packedByteCount c
1✔
874
      + packedByteCount d
1✔
875
      + packedByteCount e
1✔
876
      + packedByteCount f
1✔
877
      + packedByteCount g
2✔
878
  {-# INLINE packedByteCount #-}
879
  packM (a, b, c, d, e, f, g) =
2✔
880
    packM a >> packM b >> packM c >> packM d >> packM e >> packM f >> packM g
2✔
881
  {-# INLINEABLE packM #-}
882
  unpackM = do
2✔
883
    !a <- unpackM
2✔
884
    !b <- unpackM
2✔
885
    !c <- unpackM
2✔
886
    !d <- unpackM
2✔
887
    !e <- unpackM
2✔
888
    !f <- unpackM
2✔
889
    !g <- unpackM
2✔
890
    pure (a, b, c, d, e, f, g)
2✔
891
  {-# INLINEABLE unpackM #-}
892

893
data T2 a b = T2 !a !b
894

NEW
895
instance (Semigroup a, Semigroup b) => Semigroup (T2 a b) where
×
896
  T2 a1 b1 <> T2 a2 b2 = T2 (a1 <> a2) (b1 <> b2)
2✔
897

NEW
898
instance (Monoid a, Monoid b) => Monoid (T2 a b) where
×
899
  mempty = T2 mempty mempty
2✔
900

901
instance MemPack a => MemPack [a] where
902
  typeName = "[" ++ typeName @a ++ "]"
2✔
903
  packedByteCount es =
2✔
904
    let T2 (Sum listLen) (Sum elemsLen) = foldMap' (\e -> T2 (Sum 1) (Sum (packedByteCount e))) es
2✔
905
     in packedByteCount (Length listLen) + elemsLen
2✔
906
  {-# INLINE packedByteCount #-}
907
  packM as = do
2✔
908
    packM (Length (length as))
2✔
909
    mapM_ packM as
2✔
910
  {-# INLINE packM #-}
911
  unpackM = do
2✔
912
    Length n <- unpackM
2✔
913
    replicateTailM n unpackM
2✔
914
  {-# INLINE unpackM #-}
915

916
instance MemPack a => MemPack (Array a) where
917
  typeName = "(Array " ++ typeName @a ++ ")"
2✔
918
  packedByteCount arr =
2✔
919
    packedByteCount (Length (sizeofArray arr)) + getSum (foldMap' (Sum . packedByteCount) arr)
2✔
920
  {-# INLINE packedByteCount #-}
921
  packM as = do
2✔
922
    packM (Length (length as))
2✔
923
    mapM_ packM as
2✔
924
  {-# INLINE packM #-}
925
  unpackM = do
2✔
926
    Length n <- unpackM
2✔
927
    marr <- unpackLiftST (newArray n (error "Uninitialized"))
1✔
928
    let fill !i = when (i < n) $ do
2✔
929
          e <- unpackM
2✔
930
          unpackLiftST (writeArray marr i e)
2✔
931
          fill (i + 1)
2✔
932
    fill 0
2✔
933
    unpackLiftST (unsafeFreezeArray marr)
2✔
934
  {-# INLINE unpackM #-}
935

936
-- | Tail recursive version of `replicateM`
937
replicateTailM :: Monad m => Int -> m a -> m [a]
938
replicateTailM n f = go n []
2✔
939
  where
940
    go i !acc
2✔
941
      | i <= 0 = pure $ reverse acc
2✔
942
      | otherwise = f >>= \x -> go (i - 1) (x : acc)
1✔
943
{-# INLINE replicateTailM #-}
944

945
instance MemPack ByteArray where
2✔
946
  packedByteCount ba =
2✔
947
    let len = bufferByteCount ba
2✔
948
     in packedByteCount (Length len) + len
2✔
949
  {-# INLINE packedByteCount #-}
950
  packM ba@(ByteArray ba#) = do
2✔
951
    let !len@(I# len#) = bufferByteCount ba
2✔
952
    packM (Length len)
2✔
953
    I# curPos# <- state $ \i -> (i, i + len)
2✔
954
    MutableByteArray mba# <- ask
2✔
955
    lift_# (copyByteArray# ba# 0# mba# curPos# len#)
2✔
956
  {-# INLINE packM #-}
957
  unpackM = unpackByteArray False
2✔
958
  {-# INLINE unpackM #-}
959

960
instance (Typeable a, Prim a) => MemPack (PrimArray a) where
2✔
961
  packedByteCount pa =
2✔
962
    let len = I# (sizeOf# (undefined :: a)) * sizeofPrimArray pa
1✔
963
     in packedByteCount (Length len) + len
2✔
964
  {-# INLINE packedByteCount #-}
965
  packM pa@(PrimArray ba#) = do
2✔
966
    let !len@(I# len#) = I# (sizeOf# (undefined :: a)) * sizeofPrimArray pa
1✔
967
    packM (Length len)
2✔
968
    I# curPos# <- state $ \i -> (i, i + len)
2✔
969
    MutableByteArray mba# <- ask
2✔
970
    lift_# (copyByteArray# ba# 0# mba# curPos# len#)
2✔
971
  {-# INLINE packM #-}
972
  unpackM = (\(ByteArray ba#) -> PrimArray ba#) <$> unpackByteArray False
2✔
973
  {-# INLINE unpackM #-}
974

975
#if !MIN_VERSION_primitive(0,8,0)
976
instance MemPack Prim.ByteArray where
977
  packedByteCount ba =
978
    let len = bufferByteCount ba
979
     in packedByteCount (Length len) + len
980
  {-# INLINE packedByteCount #-}
981
  packM ba@(Prim.ByteArray ba#) = do
982
    let !len@(I# len#) = bufferByteCount ba
983
    packM (Length len)
984
    I# curPos# <- state $ \i -> (i, i + len)
985
    MutableByteArray mba# <- ask
986
    lift_# (copyByteArray# ba# 0# mba# curPos# len#)
987
  {-# INLINE packM #-}
988
  unpackM = (\(ByteArray ba#) -> Prim.ByteArray ba#) <$> unpackByteArray False
989
  {-# INLINE unpackM #-}
990
#endif
991

992
instance MemPack ShortByteString where
×
993
  packedByteCount ba =
×
994
    let len = bufferByteCount ba
×
995
     in packedByteCount (Length len) + len
×
996
  {-# INLINE packedByteCount #-}
UNCOV
997
  packM = packM . byteArrayFromShortByteString
×
998
  {-# INLINE packM #-}
UNCOV
999
  unpackM = byteArrayToShortByteString <$> unpackByteArray False
×
1000
  {-# INLINE unpackM #-}
1001

1002
instance MemPack ByteString where
2✔
1003
  packedByteCount ba =
2✔
1004
    let len = bufferByteCount ba
2✔
1005
     in packedByteCount (Length len) + len
2✔
1006
  {-# INLINE packedByteCount #-}
1007
  packM bs = packM (Length (bufferByteCount bs)) >> packByteStringM bs
2✔
1008
  {-# INLINE packM #-}
1009
  unpackM = pinnedByteArrayToByteString <$> unpackByteArray True
2✔
1010
  {-# INLINE unpackM #-}
1011

1012
{- FOURMOLU_DISABLE -}
1013
instance MemPack BSL.ByteString where
2✔
1014
#if WORD_SIZE_IN_BITS == 32
1015
  packedByteCount bsl =
1016
    let len64 = BSL.length bsl
1017
        len = fromIntegral len64
1018
     in if len64 <= fromIntegral (maxBound :: Int)
1019
        then packedByteCount (Length len) + len
1020
        else error $ mconcat [ "Cannot pack more that '2 ^ 31 - 1' bytes on a 32bit architecture, "
1021
                             , "but tried to pack a lazy ByteString with "
1022
                             , show len64
1023
                             , " bytes"
1024
                             ]
1025
#elif WORD_SIZE_IN_BITS == 64
1026
  packedByteCount bsl =
2✔
1027
    let len = fromIntegral (BSL.length bsl)
2✔
1028
     in packedByteCount (Length len) + len
2✔
1029
#else
1030
#error "Only 32bit and 64bit systems are supported"
1031
#endif
1032
  {-# INLINE packedByteCount #-}
1033
  packM bsl = do
2✔
1034
    let !len = fromIntegral (BSL.length bsl)
2✔
1035
        go BSL.Empty = pure ()
1✔
1036
        go (BSL.Chunk bs rest) = packByteStringM bs >> go rest
2✔
1037
    packM (Length len)
2✔
1038
    go bsl
2✔
1039
  {-# INLINE packM #-}
1040
  unpackM = do
2✔
1041
    Length len <- unpackM
2✔
1042
    let c = BSL.defaultChunkSize
2✔
1043
        go n
2✔
1044
          | n == 0 = pure BSL.Empty
2✔
1045
          | n <= c = BSL.Chunk <$> unpackByteStringM n <*> pure BSL.Empty
1✔
UNCOV
1046
          | otherwise = BSL.Chunk <$> unpackByteStringM c <*> go (n - c)
×
1047
    go len
2✔
1048
  {-# INLINE unpackM #-}
1049

1050
instance MemPack Text where
2✔
1051
#if MIN_VERSION_text(2,0,0)
1052
  packedByteCount (Text _ _ byteCount) = packedByteCount (Length byteCount) + byteCount
2✔
1053
  packM (Text (T.ByteArray ba#) (I# offset#) len@(I# len#)) = do
2✔
1054
    packM (Length len)
2✔
1055
    I# curPos# <- state $ \i -> (i, i + len)
2✔
1056
    MutableByteArray mba# <- ask
2✔
1057
    lift_# (copyByteArray# ba# offset# mba# curPos# len#)
2✔
1058
#else
1059
  -- FIXME: This is very inefficient and shall be fixed in the next major version
1060
  packedByteCount = packedByteCount . T.encodeUtf8
1061
  packM = packM . T.encodeUtf8
1062
#endif
1063
  {-# INLINE packedByteCount #-}
1064
  {-# INLINE packM #-}
1065
  unpackM = do
2✔
1066
    bs <- unpackM
2✔
1067
    case T.decodeUtf8' bs of
2✔
1068
      Right txt -> pure txt
2✔
UNCOV
1069
      Left exc -> F.fail $ show exc
×
1070
  {-# INLINE unpackM #-}
1071
{- FOURMOLU_ENABLE -}
1072

1073
-- | This is the implementation of `unpackM` for `ByteArray`, `ByteString` and `ShortByteString`
1074
unpackByteArray :: Buffer b => Bool -> Unpack s b ByteArray
1075
unpackByteArray isPinned = unpackByteArrayLen isPinned . unLength =<< unpackM
2✔
1076
{-# INLINE unpackByteArray #-}
1077

1078
-- | Unpack a `ByteArray` with supplied number of bytes.
1079
--
1080
-- Similar to `unpackByteArray`, except it does not unpack a length.
1081
--
1082
-- @since 0.1.1
1083
unpackByteArrayLen :: Buffer b => Bool -> Int -> Unpack s b ByteArray
1084
unpackByteArrayLen isPinned len@(I# len#) = do
2✔
1085
  I# curPos# <- guardAdvanceUnpack len
2✔
1086
  buf <- ask
2✔
1087
  pure $! runST $ do
2✔
1088
    mba@(MutableByteArray mba#) <- newMutableByteArray isPinned len
2✔
1089
    buffer
2✔
1090
      buf
2✔
1091
      (\ba# off# -> st_ (copyByteArray# ba# (curPos# +# off#) mba# 0# len#))
2✔
1092
      (\addr# -> st_ (copyAddrToByteArray# (addr# `plusAddr#` curPos#) mba# 0# len#))
2✔
1093
    freezeMutableByteArray mba
2✔
1094
{-# INLINE unpackByteArrayLen #-}
1095

1096
-- | Increment the offset counter of `Pack` monad by then number of `packedByteCount` and
1097
-- return the starting offset.
1098
packIncrement :: MemPack a => a -> Pack s Int
1099
packIncrement a =
2✔
1100
  state $ \i ->
2✔
1101
    let !n = i + packedByteCount a
1✔
1102
     in (i, n)
2✔
1103
{-# INLINE packIncrement #-}
1104

1105
-- | Increment the offset counter of `Unpack` monad by the supplied number of
1106
-- bytes. Returns the original offset or fails with `RanOutOfBytesError` whenever there is
1107
-- not enough bytes in the `Buffer`.
1108
guardAdvanceUnpack :: Buffer b => Int -> Unpack s b Int
1109
guardAdvanceUnpack n@(I# n#) = do
2✔
1110
  buf <- ask
2✔
1111
  let !len = bufferByteCount buf
2✔
1112
  -- Check that we still have enough bytes, while guarding against integer overflow.
1113
  join $ state $ \i@(I# i#) ->
2✔
1114
    case addIntC# i# n# of
2✔
1115
      (# adv#, 0# #)
1116
        | len >= I# adv# -> (pure i, I# adv#)
2✔
1117
      _ -> (failOutOfBytes i len n, i)
1✔
1118
{-# INLINE guardAdvanceUnpack #-}
1119

1120
failOutOfBytes :: Int -> Int -> Int -> Unpack s b a
1121
failOutOfBytes i len n =
2✔
1122
  failUnpack $
2✔
1123
    toSomeError $
2✔
1124
      RanOutOfBytesError
2✔
1125
        { ranOutOfBytesRead = i
2✔
1126
        , ranOutOfBytesAvailable = len
2✔
1127
        , ranOutOfBytesRequested = n
2✔
1128
        }
1129
{-# NOINLINE failOutOfBytes #-}
1130

1131
-- | Serialize a type into an unpinned `ByteArray`
1132
--
1133
-- ====__Examples__
1134
--
1135
-- >>> :set -XTypeApplications
1136
-- >>> unpack @[Int] $ pack ([1,2,3,4,5] :: [Int])
1137
-- Right [1,2,3,4,5]
1138
pack :: forall a. (MemPack a, HasCallStack) => a -> ByteArray
1139
pack = packByteArray False
2✔
1140
{-# INLINE pack #-}
1141

1142
-- | Serialize a type into a pinned `ByteString`
1143
packByteString :: forall a. (MemPack a, HasCallStack) => a -> ByteString
1144
packByteString = pinnedByteArrayToByteString . packByteArray True
2✔
1145
{-# INLINE packByteString #-}
1146

1147
-- | Serialize a type into an unpinned `ShortByteString`
1148
packShortByteString :: forall a. (MemPack a, HasCallStack) => a -> ShortByteString
UNCOV
1149
packShortByteString = byteArrayToShortByteString . pack
×
1150
{-# INLINE packShortByteString #-}
1151

1152
-- | Same as `pack`, but allows controlling the pinnedness of allocated memory
1153
packByteArray ::
1154
  forall a.
1155
  (MemPack a, HasCallStack) =>
1156
  -- | Should the array be allocated in pinned memory?
1157
  Bool ->
1158
  a ->
1159
  ByteArray
1160
packByteArray isPinned a =
2✔
1161
  packWithByteArray isPinned (typeName @a) (packedByteCount a) (packM a)
1✔
1162
{-# INLINE packByteArray #-}
1163

1164
-- | Allocate a `MutableByteArray` and run the supplied `Pack` action on it. Freezes the
1165
-- allocated `MutableByteArray` at the end yielding the immutable `ByteArray` with
1166
-- serialization packed into it.
1167
packWithByteArray ::
1168
  HasCallStack =>
1169
  -- | Should the array be allocated in pinned memory?
1170
  Bool ->
1171
  -- | Name of the type that is being serialized. Used for error reporting
1172
  String ->
1173
  -- | Size of the array to be allocated
1174
  Int ->
1175
  (forall s. Pack s ()) ->
1176
  ByteArray
1177
packWithByteArray isPinned name len packerM =
2✔
1178
  runST $ packWithMutableByteArray isPinned name len packerM >>= freezeMutableByteArray
1✔
1179
{-# INLINE packWithByteArray #-}
1180

1181
-- | Same as `packByteArray`, but produces a mutable array instead
1182
packMutableByteArray ::
1183
  forall a s.
1184
  (MemPack a, HasCallStack) =>
1185
  -- | Should the array be allocated in pinned memory?
1186
  Bool ->
1187
  a ->
1188
  ST s (MutableByteArray s)
UNCOV
1189
packMutableByteArray isPinned a =
×
1190
  packWithMutableByteArray isPinned (typeName @a) (packedByteCount a) (packM a)
×
1191
{-# INLINE packMutableByteArray #-}
1192

1193
-- | Allocate a `MutableByteArray` and run the supplied `Pack` action on it.
1194
packWithMutableByteArray ::
1195
  forall s.
1196
  HasCallStack =>
1197
  -- | Should the array be allocated in pinned memory?
1198
  Bool ->
1199
  -- | Name of the type that is being serialized. Used for error reporting
1200
  String ->
1201
  -- | Size of the mutable array to be allocated
1202
  Int ->
1203
  -- | Packing action to be executed on the mutable buffer
1204
  Pack s () ->
1205
  ST s (MutableByteArray s)
1206
packWithMutableByteArray isPinned name len packerM = do
2✔
1207
  mba <- newMutableByteArray isPinned len
2✔
1208
  filledBytes <- execStateT (runPack packerM mba) 0
2✔
1209
  when (filledBytes /= len) $ errorFilledBytes name filledBytes len
1✔
1210
  pure mba
2✔
1211
{-# INLINEABLE packWithMutableByteArray #-}
1212

1213
-- | This is a critical error, therefore we are not gracefully failing this unpacking
1214
errorFilledBytes :: HasCallStack => [Char] -> Int -> Int -> a
UNCOV
1215
errorFilledBytes name filledBytes len =
×
1216
  if filledBytes < len
×
1217
    then
UNCOV
1218
      error $
×
1219
        "Some bug in 'packM' was detected. Buffer of length " <> showBytes len
×
1220
          ++ " was not fully filled while packing " <> name
×
1221
          ++ ". Unfilled " <> showBytes (len - filledBytes) <> "."
×
1222
    else
UNCOV
1223
      error $
×
1224
        "Potential buffer overflow. Some bug in 'packM' was detected while packing " <> name
×
1225
          ++ ". Filled " <> showBytes (filledBytes - len) <> " more than allowed into a buffer of length "
×
1226
          ++ show len
×
1227
{-# NOINLINE errorFilledBytes #-}
1228

1229
-- | Helper function for packing a `ByteString` without its length being packed first.
1230
--
1231
-- @since 0.1.1
1232
packByteStringM :: ByteString -> Pack s ()
1233
packByteStringM bs = do
2✔
1234
  let !len@(I# len#) = bufferByteCount bs
2✔
1235
  I# curPos# <- state $ \i -> (i, i + len)
2✔
1236
  Pack $ \(MutableByteArray mba#) -> lift $ withPtrByteStringST bs $ \(Ptr addr#) ->
2✔
1237
    st_ (copyAddrToByteArray# addr# mba# curPos# len#)
2✔
1238
{-# INLINE packByteStringM #-}
1239

1240
-- | Unpack a `ByteString` of a specified size.
1241
--
1242
-- @since 0.1.1
1243
unpackByteStringM ::
1244
  Buffer b =>
1245
  -- | number of bytes to unpack
1246
  Int ->
1247
  Unpack s b ByteString
1248
unpackByteStringM len = pinnedByteArrayToByteString <$> unpackByteArrayLen True len
2✔
1249
{-# INLINE unpackByteStringM #-}
1250

1251
-- | Unpack a memory `Buffer` into a type using its `MemPack` instance. Besides the
1252
-- unpacked type it also returns an index into a buffer where unpacked has stopped.
1253
unpackLeftOver :: forall a b. (MemPack a, Buffer b, HasCallStack) => b -> Fail SomeError (a, Int)
1254
unpackLeftOver b = FailT $ pure $ runST $ runFailAggT $ unpackLeftOverST b
2✔
1255
{-# INLINE unpackLeftOver #-}
1256

1257
-- | Unpack a memory `Buffer` into a type using its `MemPack` instance. Besides the
1258
-- unpacked type it also returns an index into a buffer where unpacked has stopped.
1259
unpackLeftOverST ::
1260
  forall a b s. (MemPack a, Buffer b, HasCallStack) => b -> FailT SomeError (ST s) (a, Int)
1261
unpackLeftOverST b = do
2✔
1262
  let len = bufferByteCount b
2✔
1263
  res@(_, consumedBytes) <- runStateT (runUnpack unpackM b) 0
2✔
1264
  when (consumedBytes > len) $ errorLeftOver (typeName @a) consumedBytes len
1✔
1265
  pure res
2✔
1266
{-# INLINEABLE unpackLeftOverST #-}
1267

1268
-- | This is a critical error, therefore we are not gracefully failing this unpacking
1269
errorLeftOver :: HasCallStack => String -> Int -> Int -> a
UNCOV
1270
errorLeftOver name consumedBytes len =
×
1271
  error $
×
1272
    "Potential buffer overflow. Some bug in 'unpackM' was detected while unpacking " <> name
×
1273
      ++ ". Consumed " <> showBytes (consumedBytes - len) <> " more than allowed from a buffer of length "
×
1274
      ++ show len
×
1275
{-# NOINLINE errorLeftOver #-}
1276

1277
-- | Unpack a memory `Buffer` into a type using its `MemPack` instance. Besides potential
1278
-- unpacking failures due to a malformed buffer it will also fail the supplied `Buffer`
1279
-- was not fully consumed. Use `unpackLeftOver`, whenever a partially consumed buffer is
1280
-- possible.
1281
unpack :: forall a b. (MemPack a, Buffer b, HasCallStack) => b -> Either SomeError a
1282
unpack = first fromMultipleErrors . runFailAgg . unpackFail
2✔
1283
{-# INLINEABLE unpack #-}
1284

1285
-- | Same as `unpack` except fails in a `Fail` monad, instead of `Either`.
1286
unpackFail :: forall a b. (MemPack a, Buffer b, HasCallStack) => b -> Fail SomeError a
1287
unpackFail b = do
2✔
1288
  let len = bufferByteCount b
2✔
1289
  (a, consumedBytes) <- unpackLeftOver b
2✔
1290
  when (consumedBytes /= len) $ unpackFailNotFullyConsumed (typeName @a) consumedBytes len
2✔
1291
  pure a
2✔
1292
{-# INLINEABLE unpackFail #-}
1293

1294
unpackFailNotFullyConsumed :: Applicative m => String -> Int -> Int -> FailT SomeError m a
1295
unpackFailNotFullyConsumed name consumedBytes len =
2✔
1296
  failT $
2✔
1297
    toSomeError $
2✔
1298
      NotFullyConsumedError
2✔
1299
        { notFullyConsumedRead = consumedBytes
2✔
1300
        , notFullyConsumedAvailable = len
2✔
1301
        , notFullyConsumedTypeName = name
2✔
1302
        }
1303
{-# NOINLINE unpackFailNotFullyConsumed #-}
1304

1305
-- | Same as `unpackFail` except fails in any `MonadFail`, instead of `Fail`.
1306
unpackMonadFail :: forall a b m. (MemPack a, Buffer b, F.MonadFail m) => b -> m a
UNCOV
1307
unpackMonadFail = either (F.fail . show) pure . unpack
×
1308
{-# INLINEABLE unpackMonadFail #-}
1309

1310
-- | Same as `unpack` except throws a runtime exception upon a failure
1311
unpackError :: forall a b. (MemPack a, Buffer b, HasCallStack) => b -> a
1312
unpackError = errorFail . unpackFail
2✔
1313
{-# INLINEABLE unpackError #-}
1314

1315
-- | Variable length encoding for bounded types. This type of encoding will use less
1316
-- memory for small values, but for larger values it will consume more memory and will be
1317
-- slower during packing/unpacking.
UNCOV
1318
newtype VarLen a = VarLen {unVarLen :: a}
×
1319
  deriving (Eq, Ord, Show, Bounded, Enum, Num, Real, Integral, Bits, FiniteBits)
1✔
1320

1321
instance MemPack (VarLen Word16) where
2✔
1322
  packedByteCount = packedVarLenByteCount
2✔
1323
  {-# INLINE packedByteCount #-}
1324
  packM v@(VarLen x) = p7 (p7 (p7 (errorTooManyBits "Word16"))) (numBits - 7)
1✔
1325
    where
1326
      p7 = packIntoCont7 x
2✔
1327
      {-# INLINE p7 #-}
1328
      numBits = packedVarLenByteCount v * 7
2✔
1329
  {-# INLINE packM #-}
1330
  unpackM = do
2✔
1331
    let d7 = unpack7BitVarLen
2✔
1332
        {-# INLINE d7 #-}
1333
    VarLen <$> d7 (d7 (unpack7BitVarLenLast 0b_1111_1100)) 0 0
2✔
1334
  {-# INLINE unpackM #-}
1335

1336
instance MemPack (VarLen Word32) where
2✔
1337
  packedByteCount = packedVarLenByteCount
2✔
1338
  {-# INLINE packedByteCount #-}
1339
  packM v@(VarLen x) = p7 (p7 (p7 (p7 (p7 (errorTooManyBits "Word32"))))) (numBits - 7)
1✔
1340
    where
1341
      p7 = packIntoCont7 x
2✔
1342
      {-# INLINE p7 #-}
1343
      numBits = packedVarLenByteCount v * 7
2✔
1344
  {-# INLINE packM #-}
1345
  unpackM = do
2✔
1346
    let d7 = unpack7BitVarLen
2✔
1347
        {-# INLINE d7 #-}
1348
    VarLen <$> d7 (d7 (d7 (d7 (unpack7BitVarLenLast 0b_1111_0000)))) 0 0
2✔
1349
  {-# INLINE unpackM #-}
1350

1351
instance MemPack (VarLen Word64) where
2✔
1352
  packedByteCount = packedVarLenByteCount
2✔
1353
  {-# INLINE packedByteCount #-}
1354
  packM v@(VarLen x) =
2✔
1355
    p7 (p7 (p7 (p7 (p7 (p7 (p7 (p7 (p7 (p7 (errorTooManyBits "Word64")))))))))) (numBits - 7)
1✔
1356
    where
1357
      p7 = packIntoCont7 x
2✔
1358
      {-# INLINE p7 #-}
1359
      numBits = packedVarLenByteCount v * 7
2✔
1360
  {-# INLINE packM #-}
1361
  unpackM = do
2✔
1362
    let d7 = unpack7BitVarLen
2✔
1363
        {-# INLINE d7 #-}
1364
    VarLen <$> d7 (d7 (d7 (d7 (d7 (d7 (d7 (d7 (d7 (unpack7BitVarLenLast 0b_1111_1110))))))))) 0 0
2✔
1365
  {-# INLINE unpackM #-}
1366

1367
instance MemPack (VarLen Word) where
2✔
1368
  packedByteCount = packedVarLenByteCount
2✔
1369
  {-# INLINE packedByteCount #-}
1370
#if WORD_SIZE_IN_BITS == 32
1371
  packM v@(VarLen x) = p7 (p7 (p7 (p7 (p7 (errorTooManyBits "Word"))))) (numBits - 7)
1372
    where
1373
      p7 = packIntoCont7 x
1374
      {-# INLINE p7 #-}
1375
      numBits = packedVarLenByteCount v * 7
1376
  {-# INLINE packM #-}
1377
  unpackM = do
1378
    let d7 = unpack7BitVarLen
1379
        {-# INLINE d7 #-}
1380
    VarLen <$> d7 (d7 (d7 (d7 (unpack7BitVarLenLast 0b_1111_0000)))) 0 0
1381
  {-# INLINE unpackM #-}
1382
#elif WORD_SIZE_IN_BITS == 64
1383
  packM v@(VarLen x) =
2✔
1384
    p7 (p7 (p7 (p7 (p7 (p7 (p7 (p7 (p7 (p7 (errorTooManyBits "Word")))))))))) (numBits - 7)
1✔
1385
    where
1386
      p7 = packIntoCont7 x
2✔
1387
      {-# INLINE p7 #-}
1388
      numBits = packedVarLenByteCount v * 7
2✔
1389
  {-# INLINE packM #-}
1390
  unpackM = do
2✔
1391
    let d7 = unpack7BitVarLen
2✔
1392
        {-# INLINE d7 #-}
1393
    VarLen <$> d7 (d7 (d7 (d7 (d7 (d7 (d7 (d7 (d7 (unpack7BitVarLenLast 0b_1111_1110))))))))) 0 0
2✔
1394
  {-# INLINE unpackM #-}
1395
#else
1396
#error "Only 32bit and 64bit systems are supported"
1397
#endif
1398

1399
packedVarLenByteCount :: FiniteBits b => VarLen b -> Int
1400
packedVarLenByteCount (VarLen x) =
2✔
1401
  case (finiteBitSize x - countLeadingZeros x) `quotRem` 7 of
1✔
1402
    (0, 0) -> 1
2✔
1403
    (q, 0) -> q
2✔
1404
    (q, _) -> q + 1
2✔
1405
{-# INLINE packedVarLenByteCount #-}
1406

1407
errorTooManyBits :: HasCallStack => String -> a
UNCOV
1408
errorTooManyBits name =
×
1409
  error $ "Bug detected. Trying to pack more bits for " ++ name ++ " than it should be posssible"
×
1410
{-# NOINLINE errorTooManyBits #-}
1411

1412
packIntoCont7 ::
1413
  (Bits t, Integral t) => t -> (Int -> Pack s ()) -> Int -> Pack s ()
1414
packIntoCont7 x cont n
2✔
1415
  | n <= 0 = packM (fromIntegral @_ @Word8 x .&. complement topBit8)
2✔
1416
  | otherwise = do
1✔
1417
      packM (fromIntegral @_ @Word8 (x `shiftR` n) .|. topBit8)
2✔
1418
      cont (n - 7)
2✔
1419
  where
1420
    topBit8 :: Word8
1421
    !topBit8 = 0b_1000_0000
2✔
1422
{-# INLINE packIntoCont7 #-}
1423

1424
-- | Decode a variable length integral value that is encoded with 7 bits of data
1425
-- and the most significant bit (MSB), the 8th bit is set whenever there are
1426
-- more bits following. Continuation style allows us to avoid
1427
-- recursion. Removing loops is good for performance.
1428
unpack7BitVarLen ::
1429
  (Num a, Bits a, Buffer b) =>
1430
  -- | Continuation that will be invoked if MSB is set
1431
  (Word8 -> a -> Unpack s b a) ->
1432
  -- | Will be set either to 0 initially or to the very first unmodified byte, which is
1433
  -- guaranteed to have the first bit set.
1434
  Word8 ->
1435
  -- | Accumulator
1436
  a ->
1437
  Unpack s b a
1438
unpack7BitVarLen cont firstByte !acc = do
2✔
1439
  b8 :: Word8 <- unpackM
2✔
1440
  if b8 `testBit` 7
2✔
1441
    then
1442
      cont (if firstByte == 0 then b8 else firstByte) (acc `shiftL` 7 .|. fromIntegral (b8 `clearBit` 7))
2✔
1443
    else pure (acc `shiftL` 7 .|. fromIntegral b8)
2✔
1444
{-# INLINE unpack7BitVarLen #-}
1445

1446
unpack7BitVarLenLast ::
1447
  forall t b s.
1448
  (Num t, Bits t, MemPack t, Buffer b) =>
1449
  Word8 ->
1450
  Word8 ->
1451
  t ->
1452
  Unpack s b t
1453
unpack7BitVarLenLast mask firstByte acc = do
2✔
1454
  res <- unpack7BitVarLen (\_ _ -> F.fail "Too many bytes.") firstByte acc
1✔
1455
  -- Only while decoding the last 7bits we check if there was too many
1456
  -- bits supplied at the beginning.
1457
  unless (firstByte .&. mask == 0b_1000_0000) $ unpack7BitVarLenLastFail (typeName @t) firstByte
1✔
1458
  pure res
2✔
1459
{-# INLINE unpack7BitVarLenLast #-}
1460

1461
unpack7BitVarLenLastFail :: F.MonadFail m => String -> Word8 -> m a
UNCOV
1462
unpack7BitVarLenLastFail name firstByte =
×
1463
  F.fail $
×
1464
    "Unexpected bits for "
×
1465
      ++ name
×
1466
      ++ " were set in the first byte of 'VarLen': 0x" <> showHex firstByte ""
×
1467
{-# NOINLINE unpack7BitVarLenLastFail #-}
1468

1469
-- | This is a helper type useful for serializing number of elements in data
1470
-- structures. It uses `VarLen` underneath, since sizes of common data structures aren't
1471
-- too big. It also prevents negative values from being serialized and deserialized.
1472
newtype Length = Length {unLength :: Int}
2✔
UNCOV
1473
  deriving (Eq, Show, Num)
×
1474

1475
instance Bounded Length where
1476
  minBound = 0
2✔
1477
  maxBound = Length maxBound
2✔
1478

1479
instance Enum Length where
1✔
1480
  toEnum n
2✔
1481
    | n < 0 = error $ "toEnum: Length cannot be negative: " ++ show n
1✔
1482
    | otherwise = Length n
1✔
1483
  fromEnum = unLength
2✔
1484

1485
instance MemPack Length where
2✔
1486
  packedByteCount = packedByteCount . VarLen . fromIntegral @Int @Word . unLength
2✔
1487
  packM (Length n)
2✔
1488
    | n < 0 = packLengthError n
1✔
1489
    | otherwise = packM (VarLen (fromIntegral @Int @Word n))
1✔
1490
  {-# INLINE packM #-}
1491
  unpackM = do
2✔
1492
    VarLen (w :: Word) <- unpackM
2✔
1493
    when (testBit w (finiteBitSize w - 1)) $ upackLengthFail w
1✔
1494
    pure $ Length $ fromIntegral @Word @Int w
2✔
1495
  {-# INLINE unpackM #-}
1496

1497
packLengthError :: Int -> a
UNCOV
1498
packLengthError n = error $ "Length cannot be negative. Supplied: " ++ show n
×
1499
{-# NOINLINE packLengthError #-}
1500

1501
upackLengthFail :: F.MonadFail m => Word -> m a
1502
upackLengthFail w =
2✔
1503
  F.fail $ "Attempt to unpack negative length was detected: " ++ show (fromIntegral @Word @Int w)
1✔
1504
{-# NOINLINE upackLengthFail #-}
1505

1506
-- | This is a helper type that is useful for creating `MemPack` instances for sum types.
1507
newtype Tag = Tag {unTag :: Word8}
2✔
1508
  deriving (Eq, Ord, Show, Num, Enum, Bounded)
1✔
1509

1510
-- Manually defined instance, since ghc-8.6 has issues with deriving MemPack
1511
instance MemPack Tag where
2✔
1512
  packedByteCount _ = packedTagByteCount
2✔
1513
  {-# INLINE packedByteCount #-}
1514
  unpackM = unpackTagM
2✔
1515
  {-# INLINE unpackM #-}
1516
  packM = packTagM
2✔
1517
  {-# INLINE packM #-}
1518

1519
packedTagByteCount :: Int
1520
packedTagByteCount = SIZEOF_WORD8
2✔
1521
{-# INLINE packedTagByteCount #-}
1522

1523
unpackTagM :: Buffer b => Unpack s b Tag
1524
unpackTagM = Tag <$> unpackM
2✔
1525
{-# INLINE unpackTagM #-}
1526

1527
packTagM :: Tag -> Pack s ()
1528
packTagM = packM . unTag
2✔
1529
{-# INLINE packTagM #-}
1530

1531
unknownTagM :: forall a m b. (MemPack a, F.MonadFail m) => Tag -> m b
UNCOV
1532
unknownTagM (Tag t) = F.fail $ "Unrecognized Tag: " ++ show t ++ " while decoding " ++ typeName @a
×
1533

1534
lift_# :: (State# s -> State# s) -> Pack s ()
1535
lift_# f = Pack $ \_ -> lift $ st_ f
2✔
1536
{-# INLINE lift_# #-}
1537

1538
st_ :: (State# s -> State# s) -> ST s ()
1539
st_ f = ST $ \s# -> (# f s#, () #)
2✔
1540
{-# INLINE st_ #-}
1541

1542
-- | Lift an `ST` action into the `Pack` monad
1543
--
1544
-- @since 0.2.0
1545
packLiftST :: ST s a -> Pack s a
NEW
1546
packLiftST st = Pack (\_ -> StateT (\i -> (,i) <$> st))
×
1547
{-# INLINE packLiftST #-}
1548

1549
-- | Lift an `ST` action into the `Unpack` monad
1550
--
1551
-- @since 0.2.0
1552
unpackLiftST :: ST s a -> Unpack s b a
1553
unpackLiftST st = Unpack (\_ -> StateT (\i -> FailT (Right . (,i) <$> st)))
2✔
1554
{-# INLINE unpackLiftST #-}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc