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

lehins / mempack / 33

20 Sep 2024 08:45PM UTC coverage: 86.997% (-0.08%) from 87.079%
33

push

github

lehins
Fix test case

649 of 746 relevant lines covered (87.0%)

1.66 hits per line

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

90.25
/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 TypeApplications #-}
15
{-# LANGUAGE UnboxedTuples #-}
16

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

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

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

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

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

51
  -- ** Helpers
52
  failUnpack,
53
  unpackByteArray,
54

55
  -- * Helper packers
56
  VarLen (..),
57
  Length (..),
58
  Tag (..),
59
  packTagM,
60
  unpackTagM,
61
  unknownTagM,
62
  packedTagByteCount,
63

64
  -- * Internal utilities
65
  replicateTailM,
66
  lift_#,
67
  st_,
68

69
  -- * Re-exports for @GeneralizedNewtypeDeriving@
70
  StateT (..),
71
  FailT (..),
72
) where
73

74
#include "MachDeps.h"
75

76
import Control.Applicative (Alternative (..))
77
import Control.Monad (join, unless, when)
78
import qualified Control.Monad.Fail as F
79
import Control.Monad.Reader (MonadReader (..), lift)
80
import Control.Monad.State.Strict (MonadState (..), StateT (..), execStateT)
81
import Control.Monad.Trans.Fail (Fail, FailT (..), errorFail, failT, runFailAgg)
82
import Data.Array.Byte (ByteArray (..), MutableByteArray (..))
83
import Data.Bifunctor (first)
84
import Data.Bits (Bits (..), FiniteBits (..))
85
import Data.ByteString (ByteString)
86
import Data.ByteString.Short (ShortByteString)
87
import Data.Char (ord)
88
import Data.Complex (Complex (..))
89
import Data.List (intercalate)
90
import Data.MemPack.Buffer
91
import Data.MemPack.Error
92
import Data.Ratio
93
import Data.Semigroup (Sum (..))
94
import Data.Typeable
95
import GHC.Exts
96
import GHC.Int
97
import GHC.ST (ST (..), runST)
98
import GHC.Stable (StablePtr (..))
99
import GHC.Stack (HasCallStack)
100
import GHC.Word
101
import Numeric (showHex)
102
import Prelude hiding (fail)
103
#if __GLASGOW_HASKELL__ >= 900
104
import GHC.Num.Integer (Integer (..), integerCheck)
105
import GHC.Num.Natural (Natural (..), naturalCheck)
106
#elif defined(MIN_VERSION_integer_gmp)
107
import GHC.Integer.GMP.Internals (Integer (..), BigNat(BN#), isValidInteger#)
108
import GHC.Natural (Natural (..), isValidNatural)
109
#else
110
#error "Only integer-gmp is supported for now for older compilers"
111
#endif
112
#if !(MIN_VERSION_base(4,13,0))
113
import Prelude (fail)
114
#endif
115

116
-- | Monad that is used for serializing data into a `MutableByteArray`. It is based on
117
-- `StateT` that tracks the current index into the `MutableByteArray` where next write is
118
-- expected to happen.
119
newtype Pack s a = Pack
120
  { runPack :: MutableByteArray s -> StateT Int (ST s) a
2✔
121
  }
122

123
instance Functor (Pack s) where
×
124
  fmap f (Pack p) = Pack $ \buf -> fmap f (p buf)
×
125
  {-# INLINE fmap #-}
126
instance Applicative (Pack s) where
×
127
  pure = Pack . const . pure
2✔
128
  {-# INLINE pure #-}
129
  Pack a1 <*> Pack a2 =
×
130
    Pack $ \buf -> a1 buf <*> a2 buf
×
131
  {-# INLINE (<*>) #-}
132
  Pack a1 *> Pack a2 =
×
133
    Pack $ \buf -> a1 buf *> a2 buf
×
134
  {-# INLINE (*>) #-}
135
instance Monad (Pack s) where
2✔
136
  Pack m1 >>= p =
2✔
137
    Pack $ \buf -> m1 buf >>= \res -> runPack (p res) buf
2✔
138
  {-# INLINE (>>=) #-}
139
instance MonadReader (MutableByteArray s) (Pack s) where
140
  ask = Pack pure
2✔
141
  {-# INLINE ask #-}
142
  local f (Pack p) = Pack (p . f)
×
143
  {-# INLINE local #-}
144
  reader f = Pack (pure . f)
×
145
  {-# INLINE reader #-}
146
instance MonadState Int (Pack s) where
147
  get = Pack $ const get
×
148
  {-# INLINE get #-}
149
  put = Pack . const . put
×
150
  {-# INLINE put #-}
151
  state = Pack . const . state
2✔
152
  {-# INLINE state #-}
153

154
-- | Monad that is used for deserializing data from a memory `Buffer`. It is based on
155
-- `StateT` that tracks the current index into the @`Buffer` a@, from where the next read
156
-- suppose to happen. Unpacking can `F.fail` with `F.MonadFail` instance or with
157
-- `failUnpack` that provides a more type safe way of failing using `Error` interface.
158
newtype Unpack b a = Unpack
159
  { runUnpack :: b -> StateT Int (Fail SomeError) a
2✔
160
  }
161

162
instance Functor (Unpack s) where
×
163
  fmap f (Unpack p) = Unpack $ \buf -> fmap f (p buf)
2✔
164
  {-# INLINE fmap #-}
165
instance Applicative (Unpack b) where
×
166
  pure = Unpack . const . pure
2✔
167
  {-# INLINE pure #-}
168
  Unpack a1 <*> Unpack a2 =
×
169
    Unpack $ \buf -> a1 buf <*> a2 buf
×
170
  {-# INLINE (<*>) #-}
171
  Unpack a1 *> Unpack a2 =
×
172
    Unpack $ \buf -> a1 buf *> a2 buf
×
173
  {-# INLINE (*>) #-}
174
instance Monad (Unpack b) where
×
175
  Unpack m1 >>= p =
2✔
176
    Unpack $ \buf -> m1 buf >>= \res -> runUnpack (p res) buf
2✔
177
  {-# INLINE (>>=) #-}
178
#if !(MIN_VERSION_base(4,13,0))
179
  fail = Unpack . const . F.fail
180
#endif
181
instance F.MonadFail (Unpack b) where
182
  fail = Unpack . const . F.fail
2✔
183
instance MonadReader b (Unpack b) where
184
  ask = Unpack pure
2✔
185
  {-# INLINE ask #-}
186
  local f (Unpack p) = Unpack (p . f)
×
187
  {-# INLINE local #-}
188
  reader f = Unpack (pure . f)
×
189
  {-# INLINE reader #-}
190
instance MonadState Int (Unpack b) where
191
  get = Unpack $ const get
×
192
  {-# INLINE get #-}
193
  put = Unpack . const . put
×
194
  {-# INLINE put #-}
195
  state = Unpack . const . state
2✔
196
  {-# INLINE state #-}
197

198
instance Alternative (Unpack b) where
×
199
  empty = Unpack $ \_ -> lift empty
×
200
  {-# INLINE empty #-}
201
  Unpack r1 <|> Unpack r2 =
2✔
202
    Unpack $ \buf ->
2✔
203
      case r1 buf of
2✔
204
        StateT m1 ->
205
          case r2 buf of
2✔
206
            StateT m2 -> StateT $ \s -> m1 s <|> m2 s
2✔
207
  {-# INLINE (<|>) #-}
208

209
-- | Failing unpacking with an `Error`.
210
failUnpack :: Error e => e -> Unpack b a
211
failUnpack e = Unpack $ \_ -> lift $ failT (toSomeError e)
2✔
212

213
-- | Efficient serialization interface that operates directly on memory buffers.
214
class MemPack a where
215
  -- | Name of the type that is being deserialized for error reporting. Default
216
  -- implementation relies on `Typeable`.
217
  typeName :: String
218
  default typeName :: Typeable a => String
219
  typeName = show (typeRep (Proxy @a))
1✔
220

221
  -- | Report the exact size in number of bytes that packed version of this type will
222
  -- occupy. It is very important to get this right, otherwise `packM` will result in a
223
  -- runtime exception. Another words this is the expected property that it should hold:
224
  --
225
  -- prop> packedByteCount a == bufferByteCount (pack a)
226
  packedByteCount :: a -> Int
227

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

232
  -- | Read binary representation of the type directly from the buffer, which can be
233
  -- accessed with `ask` when necessary. Direct reads from the buffer should be preceded
234
  -- with advancing the buffer offset with `MonadState` by the number of bytes that will
235
  -- be consumed from the buffer and making sure that no reads outside of the buffer can
236
  -- happen. Violation of these rules will lead to segfaults.
237
  unpackM :: Buffer b => Unpack b a
238

239
instance MemPack () where
×
240
  packedByteCount _ = 0
2✔
241
  {-# INLINE packedByteCount #-}
242
  packM _ = pure ()
1✔
243
  {-# INLINE packM #-}
244
  unpackM = pure ()
2✔
245
  {-# INLINE unpackM #-}
246

247
instance MemPack Bool where
2✔
248
  packedByteCount _ = packedTagByteCount
2✔
249
  {-# INLINE packedByteCount #-}
250
  packM x = packTagM $ if x then 1 else 0
2✔
251
  {-# INLINE packM #-}
252
  unpackM =
2✔
253
    unpackTagM >>= \case
2✔
254
      0 -> pure False
2✔
255
      1 -> pure True
2✔
256
      n -> F.fail $ "Invalid value detected for Bool: " ++ show n
×
257
  {-# INLINE unpackM #-}
258

259
instance MemPack a => MemPack (Maybe a) where
260
  typeName = "Maybe " ++ typeName @a
2✔
261
  packedByteCount = \case
2✔
262
    Nothing -> packedTagByteCount
2✔
263
    Just a -> packedTagByteCount + packedByteCount a
2✔
264
  {-# INLINE packedByteCount #-}
265
  packM = \case
2✔
266
    Nothing -> packTagM 0
2✔
267
    Just a -> packTagM 1 >> packM a
2✔
268
  {-# INLINE packM #-}
269
  unpackM =
2✔
270
    unpackTagM >>= \case
2✔
271
      0 -> pure Nothing
2✔
272
      1 -> Just <$> unpackM
2✔
273
      n -> unknownTagM @(Maybe a) n
×
274
  {-# INLINE unpackM #-}
275

276
instance (MemPack a, MemPack b) => MemPack (Either a b) where
277
  typeName = "Either " ++ typeName @a ++ " " ++ typeName @b
2✔
278
  packedByteCount = \case
2✔
279
    Left a -> packedTagByteCount + packedByteCount a
1✔
280
    Right b -> packedTagByteCount + packedByteCount b
1✔
281
  {-# INLINE packedByteCount #-}
282
  packM = \case
2✔
283
    Left a -> packTagM 0 >> packM a
2✔
284
    Right b -> packTagM 1 >> packM b
2✔
285
  {-# INLINE packM #-}
286
  unpackM =
2✔
287
    unpackTagM >>= \case
2✔
288
      0 -> Left <$> unpackM
2✔
289
      1 -> Right <$> unpackM
2✔
290
      n -> unknownTagM @(Either a b) n
×
291
  {-# INLINE unpackM #-}
292

293
instance MemPack Char where
2✔
294
  packedByteCount _ = SIZEOF_HSCHAR
2✔
295
  {-# INLINE packedByteCount #-}
296
  packM a@(C# a#) = do
2✔
297
    MutableByteArray mba# <- ask
2✔
298
    I# i# <- packIncrement a
1✔
299
    lift_# (writeWord8ArrayAsWideChar# mba# i# a#)
2✔
300
  {-# INLINE packM #-}
301
  unpackM = do
2✔
302
    I# i# <- guardAdvanceUnpack SIZEOF_HSCHAR
2✔
303
    buf <- ask
2✔
304
    let c =
2✔
305
          buffer
2✔
306
            buf
2✔
307
            (\ba# -> C# (indexWord8ArrayAsWideChar# ba# i#))
2✔
308
            (\addr# -> C# (indexWideCharOffAddr# (addr# `plusAddr#` i#) 0#))
2✔
309
    when (ord c > 0x10FFFF) $
2✔
310
      F.fail $
2✔
311
        "Out of bounds Char was detected: '\\x" ++ showHex (fromEnum c) "'"
×
312
    pure c
2✔
313
  {-# INLINE unpackM #-}
314

315
instance MemPack Float where
2✔
316
  packedByteCount _ = SIZEOF_FLOAT
2✔
317
  {-# INLINE packedByteCount #-}
318
  packM a@(F# a#) = do
2✔
319
    MutableByteArray mba# <- ask
2✔
320
    I# i# <- packIncrement a
1✔
321
    lift_# (writeWord8ArrayAsFloat# mba# i# a#)
2✔
322
  {-# INLINE packM #-}
323
  unpackM = do
2✔
324
    I# i# <- guardAdvanceUnpack SIZEOF_FLOAT
2✔
325
    buf <- ask
2✔
326
    pure $!
2✔
327
      buffer
2✔
328
        buf
2✔
329
        (\ba# -> F# (indexWord8ArrayAsFloat# ba# i#))
2✔
330
        (\addr# -> F# (indexFloatOffAddr# (addr# `plusAddr#` i#) 0#))
2✔
331
  {-# INLINE unpackM #-}
332

333
instance MemPack Double where
2✔
334
  packedByteCount _ = SIZEOF_DOUBLE
2✔
335
  {-# INLINE packedByteCount #-}
336
  packM a@(D# a#) = do
2✔
337
    MutableByteArray mba# <- ask
2✔
338
    I# i# <- packIncrement a
1✔
339
    lift_# (writeWord8ArrayAsDouble# mba# i# a#)
2✔
340
  {-# INLINE packM #-}
341
  unpackM = do
2✔
342
    I# i# <- guardAdvanceUnpack SIZEOF_DOUBLE
2✔
343
    buf <- ask
2✔
344
    pure $!
2✔
345
      buffer
2✔
346
        buf
2✔
347
        (\ba# -> D# (indexWord8ArrayAsDouble# ba# i#))
2✔
348
        (\addr# -> D# (indexDoubleOffAddr# (addr# `plusAddr#` i#) 0#))
2✔
349
  {-# INLINE unpackM #-}
350

351
instance MemPack (Ptr a) where
352
  typeName = "Ptr"
2✔
353
  packedByteCount _ = SIZEOF_HSPTR
2✔
354
  {-# INLINE packedByteCount #-}
355
  packM a@(Ptr a#) = do
2✔
356
    MutableByteArray mba# <- ask
2✔
357
    I# i# <- packIncrement a
1✔
358
    lift_# (writeWord8ArrayAsAddr# mba# i# a#)
2✔
359
  {-# INLINE packM #-}
360
  unpackM = do
2✔
361
    I# i# <- guardAdvanceUnpack SIZEOF_HSPTR
2✔
362
    buf <- ask
2✔
363
    pure $!
2✔
364
      buffer
2✔
365
        buf
2✔
366
        (\ba# -> Ptr (indexWord8ArrayAsAddr# ba# i#))
2✔
367
        (\addr# -> Ptr (indexAddrOffAddr# (addr# `plusAddr#` i#) 0#))
2✔
368
  {-# INLINE unpackM #-}
369

370
instance MemPack (StablePtr a) where
371
  typeName = "StablePtr"
2✔
372
  packedByteCount _ = SIZEOF_HSSTABLEPTR
2✔
373
  {-# INLINE packedByteCount #-}
374
  packM a@(StablePtr a#) = do
2✔
375
    MutableByteArray mba# <- ask
2✔
376
    I# i# <- packIncrement a
1✔
377
    lift_# (writeWord8ArrayAsStablePtr# mba# i# a#)
2✔
378
  {-# INLINE packM #-}
379
  unpackM = do
2✔
380
    I# i# <- guardAdvanceUnpack SIZEOF_HSSTABLEPTR
2✔
381
    buf <- ask
2✔
382
    pure $!
2✔
383
      buffer
2✔
384
        buf
2✔
385
        (\ba# -> StablePtr (indexWord8ArrayAsStablePtr# ba# i#))
2✔
386
        (\addr# -> StablePtr (indexStablePtrOffAddr# (addr# `plusAddr#` i#) 0#))
2✔
387
  {-# INLINE unpackM #-}
388

389
instance MemPack Int where
2✔
390
  packedByteCount _ = SIZEOF_HSINT
2✔
391
  {-# INLINE packedByteCount #-}
392
  packM a@(I# a#) = do
2✔
393
    MutableByteArray mba# <- ask
2✔
394
    I# i# <- packIncrement a
1✔
395
    lift_# (writeWord8ArrayAsInt# mba# i# a#)
2✔
396
  {-# INLINE packM #-}
397
  unpackM = do
2✔
398
    I# i# <- guardAdvanceUnpack SIZEOF_HSINT
2✔
399
    buf <- ask
2✔
400
    pure $!
2✔
401
      buffer
2✔
402
        buf
2✔
403
        (\ba# -> I# (indexWord8ArrayAsInt# ba# i#))
2✔
404
        (\addr# -> I# (indexIntOffAddr# (addr# `plusAddr#` i#) 0#))
2✔
405
  {-# INLINE unpackM #-}
406

407
instance MemPack Int8 where
2✔
408
  packedByteCount _ = SIZEOF_INT8
2✔
409
  {-# INLINE packedByteCount #-}
410
  packM a@(I8# a#) = do
2✔
411
    MutableByteArray mba# <- ask
2✔
412
    I# i# <- packIncrement a
1✔
413
    lift_# (writeInt8Array# mba# i# a#)
2✔
414
  {-# INLINE packM #-}
415
  unpackM = do
2✔
416
    I# i# <- guardAdvanceUnpack SIZEOF_INT8
2✔
417
    buf <- ask
2✔
418
    pure $!
2✔
419
      buffer
2✔
420
        buf
2✔
421
        (\ba# -> I8# (indexInt8Array# ba# i#))
2✔
422
        (\addr# -> I8# (indexInt8OffAddr# (addr# `plusAddr#` i#) 0#))
2✔
423
  {-# INLINE unpackM #-}
424

425
instance MemPack Int16 where
2✔
426
  packedByteCount _ = SIZEOF_INT16
2✔
427
  {-# INLINE packedByteCount #-}
428
  packM a@(I16# a#) = do
2✔
429
    MutableByteArray mba# <- ask
2✔
430
    I# i# <- packIncrement a
1✔
431
    lift_# (writeWord8ArrayAsInt16# mba# i# a#)
2✔
432
  {-# INLINE packM #-}
433
  unpackM = do
2✔
434
    buf <- ask
2✔
435
    I# i# <- guardAdvanceUnpack SIZEOF_INT16
2✔
436
    pure $!
2✔
437
      buffer
2✔
438
        buf
2✔
439
        (\ba# -> I16# (indexWord8ArrayAsInt16# ba# i#))
2✔
440
        (\addr# -> I16# (indexInt16OffAddr# (addr# `plusAddr#` i#) 0#))
2✔
441
  {-# INLINE unpackM #-}
442

443
instance MemPack Int32 where
2✔
444
  packedByteCount _ = SIZEOF_INT32
2✔
445
  {-# INLINE packedByteCount #-}
446
  packM a@(I32# a#) = do
2✔
447
    MutableByteArray mba# <- ask
2✔
448
    I# i# <- packIncrement a
1✔
449
    lift_# (writeWord8ArrayAsInt32# mba# i# a#)
2✔
450
  {-# INLINE packM #-}
451
  unpackM = do
2✔
452
    buf <- ask
2✔
453
    I# i# <- guardAdvanceUnpack SIZEOF_INT32
2✔
454
    pure $!
2✔
455
      buffer
2✔
456
        buf
2✔
457
        (\ba# -> I32# (indexWord8ArrayAsInt32# ba# i#))
2✔
458
        (\addr# -> I32# (indexInt32OffAddr# (addr# `plusAddr#` i#) 0#))
2✔
459
  {-# INLINE unpackM #-}
460

461
instance MemPack Int64 where
2✔
462
  packedByteCount _ = SIZEOF_INT64
2✔
463
  {-# INLINE packedByteCount #-}
464
  packM a@(I64# a#) = do
2✔
465
    MutableByteArray mba# <- ask
2✔
466
    I# i# <- packIncrement a
1✔
467
    lift_# (writeWord8ArrayAsInt64# mba# i# a#)
2✔
468
  {-# INLINE packM #-}
469
  unpackM = do
2✔
470
    buf <- ask
2✔
471
    I# i# <- guardAdvanceUnpack SIZEOF_INT64
2✔
472
    pure $!
2✔
473
      buffer
2✔
474
        buf
2✔
475
        (\ba# -> I64# (indexWord8ArrayAsInt64# ba# i#))
2✔
476
        (\addr# -> I64# (indexInt64OffAddr# (addr# `plusAddr#` i#) 0#))
2✔
477
  {-# INLINE unpackM #-}
478

479
instance MemPack Word where
2✔
480
  packedByteCount _ = SIZEOF_HSWORD
2✔
481
  {-# INLINE packedByteCount #-}
482
  packM a@(W# a#) = do
2✔
483
    MutableByteArray mba# <- ask
2✔
484
    I# i# <- packIncrement a
1✔
485
    lift_# (writeWord8ArrayAsWord# mba# i# a#)
2✔
486
  {-# INLINE packM #-}
487
  unpackM = do
2✔
488
    I# i# <- guardAdvanceUnpack SIZEOF_HSWORD
2✔
489
    buf <- ask
2✔
490
    pure $!
2✔
491
      buffer
2✔
492
        buf
2✔
493
        (\ba# -> W# (indexWord8ArrayAsWord# ba# i#))
2✔
494
        (\addr# -> W# (indexWordOffAddr# (addr# `plusAddr#` i#) 0#))
2✔
495
  {-# INLINE unpackM #-}
496

497
instance MemPack Word8 where
2✔
498
  packedByteCount _ = SIZEOF_WORD8
2✔
499
  {-# INLINE packedByteCount #-}
500
  packM a@(W8# a#) = do
2✔
501
    MutableByteArray mba# <- ask
2✔
502
    I# i# <- packIncrement a
1✔
503
    lift_# (writeWord8Array# mba# i# a#)
2✔
504
  {-# INLINE packM #-}
505
  unpackM = do
2✔
506
    I# i# <- guardAdvanceUnpack SIZEOF_WORD8
2✔
507
    buf <- ask
2✔
508
    pure $!
2✔
509
      buffer
2✔
510
        buf
2✔
511
        (\ba# -> W8# (indexWord8Array# ba# i#))
2✔
512
        (\addr# -> W8# (indexWord8OffAddr# addr# i#))
2✔
513
  {-# INLINE unpackM #-}
514

515
instance MemPack Word16 where
2✔
516
  packedByteCount _ = SIZEOF_WORD16
2✔
517
  {-# INLINE packedByteCount #-}
518
  packM a@(W16# a#) = do
2✔
519
    MutableByteArray mba# <- ask
2✔
520
    I# i# <- packIncrement a
1✔
521
    lift_# (writeWord8ArrayAsWord16# mba# i# a#)
2✔
522
  {-# INLINE packM #-}
523
  unpackM = do
2✔
524
    buf <- ask
2✔
525
    I# i# <- guardAdvanceUnpack SIZEOF_WORD16
2✔
526
    pure $!
2✔
527
      buffer
2✔
528
        buf
2✔
529
        (\ba# -> W16# (indexWord8ArrayAsWord16# ba# i#))
2✔
530
        (\addr# -> W16# (indexWord16OffAddr# (addr# `plusAddr#` i#) 0#))
2✔
531
  {-# INLINE unpackM #-}
532

533
instance MemPack Word32 where
2✔
534
  packedByteCount _ = SIZEOF_WORD32
2✔
535
  {-# INLINE packedByteCount #-}
536
  packM a@(W32# a#) = do
2✔
537
    MutableByteArray mba# <- ask
2✔
538
    I# i# <- packIncrement a
1✔
539
    lift_# (writeWord8ArrayAsWord32# mba# i# a#)
2✔
540
  {-# INLINE packM #-}
541
  unpackM = do
2✔
542
    I# i# <- guardAdvanceUnpack SIZEOF_WORD32
2✔
543
    buf <- ask
2✔
544
    pure $!
2✔
545
      buffer
2✔
546
        buf
2✔
547
        (\ba# -> W32# (indexWord8ArrayAsWord32# ba# i#))
2✔
548
        (\addr# -> W32# (indexWord32OffAddr# (addr# `plusAddr#` i#) 0#))
2✔
549
  {-# INLINE unpackM #-}
550

551
instance MemPack Word64 where
2✔
552
  packedByteCount _ = SIZEOF_WORD64
2✔
553
  {-# INLINE packedByteCount #-}
554
  packM a@(W64# a#) = do
2✔
555
    MutableByteArray mba# <- ask
2✔
556
    I# i# <- packIncrement a
1✔
557
    lift_# (writeWord8ArrayAsWord64# mba# i# a#)
2✔
558
  {-# INLINE packM #-}
559
  unpackM = do
2✔
560
    I# i# <- guardAdvanceUnpack SIZEOF_WORD64
2✔
561
    buf <- ask
2✔
562
    pure $!
2✔
563
      buffer
2✔
564
        buf
2✔
565
        (\ba# -> W64# (indexWord8ArrayAsWord64# ba# i#))
2✔
566
        (\addr# -> W64# (indexWord64OffAddr# (addr# `plusAddr#` i#) 0#))
2✔
567
  {-# INLINE unpackM #-}
568

569
#if __GLASGOW_HASKELL__ >= 900
570
instance MemPack Integer where
2✔
571
  packedByteCount =
2✔
572
    (+ packedTagByteCount) . \case
2✔
573
      IS i# -> packedByteCount (I# i#)
1✔
574
      IP ba# -> packedByteCount (ByteArray ba#)
2✔
575
      IN ba# -> packedByteCount (ByteArray ba#)
2✔
576
  {-# INLINE packedByteCount #-}
577
  packM = \case
2✔
578
    IS i# -> packTagM 0 >> packM (I# i#)
2✔
579
    IP ba# -> packTagM 1 >> packM (ByteArray ba#)
2✔
580
    IN ba# -> packTagM 2 >> packM (ByteArray ba#)
2✔
581
  {-# INLINE packM #-}
582
  unpackM = do
2✔
583
    i <-
584
      unpackTagM >>= \case
2✔
585
        0 -> do
2✔
586
          I# i# <- unpackM
2✔
587
          pure $ IS i#
2✔
588
        1 -> do
2✔
589
          ByteArray ba# <- unpackM
2✔
590
          pure $ IP ba#
2✔
591
        2 -> do
2✔
592
          ByteArray ba# <- unpackM
2✔
593
          pure $ IN ba#
2✔
594
        t -> unknownTagM @Integer t
×
595
    unless (integerCheck i) $ F.fail $ "Invalid Integer decoded " ++ showInteger i
1✔
596
    pure i
2✔
597
    where
598
      showInteger = \case
×
599
        IS i# -> "IS " ++ show (I# i#)
×
600
        IP ba# -> "IP " ++ show (ByteArray ba#)
×
601
        IN ba# -> "IN " ++ show (ByteArray ba#)
×
602
  {-# INLINE unpackM #-}
603

604
instance MemPack Natural where
2✔
605
  packedByteCount =
2✔
606
    (+ packedTagByteCount) . \case
2✔
607
      NS w# -> packedByteCount (W# w#)
1✔
608
      NB ba# -> packedByteCount (ByteArray ba#)
2✔
609
  {-# INLINE packedByteCount #-}
610
  packM = \case
2✔
611
    NS w# -> packTagM 0 >> packM (W# w#)
2✔
612
    NB ba# -> packTagM 1 >> packM (ByteArray ba#)
2✔
613
  {-# INLINE packM #-}
614
  unpackM = do
2✔
615
    n <-
616
      unpackTagM >>= \case
2✔
617
        0 -> do
2✔
618
          W# w# <- unpackM
2✔
619
          pure $ NS w#
2✔
620
        1 -> do
2✔
621
          ByteArray ba# <- unpackM
2✔
622
          pure $ NB ba#
2✔
623
        t -> unknownTagM @Natural t
×
624
    unless (naturalCheck n) $ F.fail $ "Invalid Natural decoded " ++ showNatural n
1✔
625
    pure n
2✔
626
    where
627
      showNatural = \case
×
628
        NS w# -> "NS " ++ show (W# w#)
×
629
        NB ba# -> "NB " ++ show (ByteArray ba#)
×
630
  {-# INLINE unpackM #-}
631

632
#elif defined(MIN_VERSION_integer_gmp)
633

634
instance MemPack Integer where
635
  packedByteCount =
636
    (+ packedTagByteCount) . \case
637
      S# i# -> packedByteCount (I# i#)
638
      Jp# (BN# ba#) -> packedByteCount (ByteArray ba#)
639
      Jn# (BN# ba#) -> packedByteCount (ByteArray ba#)
640
  {-# INLINE packedByteCount #-}
641
  packM = \case
642
    S# i# -> packTagM 0 >> packM (I# i#)
643
    Jp# (BN# ba#) -> packTagM 1 >> packM (ByteArray ba#)
644
    Jn# (BN# ba#) -> packTagM 2 >> packM (ByteArray ba#)
645
  {-# INLINE packM #-}
646
  unpackM = do
647
    i <-
648
      unpackTagM >>= \case
649
        0 -> do
650
          I# i# <- unpackM
651
          pure $ S# i#
652
        1 -> do
653
          ByteArray ba# <- unpackM
654
          pure $ Jp# (BN# ba#)
655
        2 -> do
656
          ByteArray ba# <- unpackM
657
          pure $ Jn# (BN# ba#)
658
        t -> unknownTagM @Integer t
659
    unless (isTrue# (isValidInteger# i)) $ F.fail $ "Invalid Integer decoded " ++ showInteger i
660
    pure i
661
    where
662
      showInteger = \case
663
        S# i# -> "S# " ++ show (I# i#)
664
        Jp# (BN# ba#) -> "Jp# " ++ show (ByteArray ba#)
665
        Jn# (BN# ba#) -> "Jn# " ++ show (ByteArray ba#)
666
  {-# INLINE unpackM #-}
667

668
instance MemPack Natural where
669
  packedByteCount =
670
    (+ packedTagByteCount) . \case
671
      NatS# w# -> packedByteCount (W# w#)
672
      NatJ# (BN# ba#) -> packedByteCount (ByteArray ba#)
673
  {-# INLINE packedByteCount #-}
674
  packM = \case
675
    NatS# w# -> packTagM 0 >> packM (W# w#)
676
    NatJ# (BN# ba#) -> packTagM 1 >> packM (ByteArray ba#)
677
  {-# INLINE packM #-}
678
  unpackM = do
679
    n <-
680
      unpackTagM >>= \case
681
        0 -> do
682
          W# w# <- unpackM
683
          pure $ NatS# w#
684
        1 -> do
685
          ByteArray ba# <- unpackM
686
          pure $ NatJ# (BN# ba#)
687
        t -> unknownTagM @Natural t
688
    unless (isValidNatural n) $ F.fail $ "Invalid Natural decoded " ++ showNatural n
689
    pure n
690
    where
691
      showNatural = \case
692
        NatS# w# -> "NatS# " ++ show (W# w#)
693
        NatJ# (BN#  ba#) -> "NatJ# " ++ show (ByteArray ba#)
694
  {-# INLINE unpackM #-}
695

696
#endif
697

698
instance MemPack a => MemPack (Complex a) where
699
  typeName = "Complex " ++ typeName @a
2✔
700
  packedByteCount (a :+ b) = packedByteCount a + packedByteCount b
1✔
701
  {-# INLINE packedByteCount #-}
702
  packM (a :+ b) = packM a >> packM b
2✔
703
  {-# INLINE packM #-}
704
  unpackM = do
2✔
705
    !a <- unpackM
2✔
706
    !b <- unpackM
2✔
707
    pure (a :+ b)
2✔
708
  {-# INLINE unpackM #-}
709

710
instance (MemPack a, Integral a) => MemPack (Ratio a) where
711
  typeName = "Ratio " ++ typeName @a
2✔
712
  packedByteCount r = packedByteCount (numerator r) + packedByteCount (denominator r)
1✔
713
  {-# INLINE packedByteCount #-}
714
  packM r = packM (numerator r) >> packM (denominator r)
2✔
715
  {-# INLINE packM #-}
716
  unpackM = do
2✔
717
    !a <- unpackM
2✔
718
    !b <- unpackM
2✔
719
    when (b == 0) $ F.fail $ "Zero denominator was detected when unpacking " ++ typeName @(Ratio a)
1✔
720
    pure (a % b)
2✔
721
  {-# INLINE unpackM #-}
722

723
instance (MemPack a, MemPack b) => MemPack (a, b) where
724
  typeName = "(" ++ typeName @a ++ "," ++ typeName @b ++ ")"
2✔
725
  packedByteCount (a, b) = packedByteCount a + packedByteCount b
1✔
726
  {-# INLINE packedByteCount #-}
727
  packM (a, b) = packM a >> packM b
2✔
728
  {-# INLINE packM #-}
729
  unpackM = do
2✔
730
    !a <- unpackM
2✔
731
    !b <- unpackM
2✔
732
    pure (a, b)
2✔
733
  {-# INLINE unpackM #-}
734

735
instance (MemPack a, MemPack b, MemPack c) => MemPack (a, b, c) where
736
  typeName = "(" ++ typeName @a ++ "," ++ typeName @b ++ "," ++ typeName @c ++ ")"
2✔
737
  packedByteCount (a, b, c) = packedByteCount a + packedByteCount b + packedByteCount c
1✔
738
  {-# INLINE packedByteCount #-}
739
  packM (a, b, c) = packM a >> packM b >> packM c
2✔
740
  {-# INLINE packM #-}
741
  unpackM = do
2✔
742
    !a <- unpackM
2✔
743
    !b <- unpackM
2✔
744
    !c <- unpackM
2✔
745
    pure (a, b, c)
2✔
746
  {-# INLINE unpackM #-}
747

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

763
instance (MemPack a, MemPack b, MemPack c, MemPack d, MemPack e) => MemPack (a, b, c, d, e) where
764
  typeName =
2✔
765
    "("
2✔
766
      ++ intercalate
2✔
767
        ","
2✔
768
        [ typeName @a
2✔
769
        , typeName @b
2✔
770
        , typeName @c
2✔
771
        , typeName @d
2✔
772
        , typeName @e
2✔
773
        ]
774
      ++ ")"
2✔
775
  packedByteCount (a, b, c, d, e) =
2✔
776
    packedByteCount a + packedByteCount b + packedByteCount c + packedByteCount d + packedByteCount e
1✔
777
  {-# INLINE packedByteCount #-}
778
  packM (a, b, c, d, e) =
2✔
779
    packM a >> packM b >> packM c >> packM d >> packM e
2✔
780
  {-# INLINE packM #-}
781
  unpackM = do
2✔
782
    !a <- unpackM
2✔
783
    !b <- unpackM
2✔
784
    !c <- unpackM
2✔
785
    !d <- unpackM
2✔
786
    !e <- unpackM
2✔
787
    pure (a, b, c, d, e)
2✔
788
  {-# INLINE unpackM #-}
789

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

824
instance
825
  (MemPack a, MemPack b, MemPack c, MemPack d, MemPack e, MemPack f, MemPack g) =>
826
  MemPack (a, b, c, d, e, f, g)
827
  where
828
  typeName =
2✔
829
    "("
2✔
830
      ++ intercalate
2✔
831
        ","
2✔
832
        [ typeName @a
2✔
833
        , typeName @b
2✔
834
        , typeName @c
2✔
835
        , typeName @d
2✔
836
        , typeName @e
2✔
837
        , typeName @f
2✔
838
        , typeName @g
2✔
839
        ]
840
      ++ ")"
2✔
841
  packedByteCount (a, b, c, d, e, f, g) =
2✔
842
    packedByteCount a
1✔
843
      + packedByteCount b
1✔
844
      + packedByteCount c
1✔
845
      + packedByteCount d
1✔
846
      + packedByteCount e
1✔
847
      + packedByteCount f
1✔
848
      + packedByteCount g
2✔
849
  {-# INLINE packedByteCount #-}
850
  packM (a, b, c, d, e, f, g) =
2✔
851
    packM a >> packM b >> packM c >> packM d >> packM e >> packM f >> packM g
2✔
852
  {-# INLINE packM #-}
853
  unpackM = do
2✔
854
    !a <- unpackM
2✔
855
    !b <- unpackM
2✔
856
    !c <- unpackM
2✔
857
    !d <- unpackM
2✔
858
    !e <- unpackM
2✔
859
    !f <- unpackM
2✔
860
    !g <- unpackM
2✔
861
    pure (a, b, c, d, e, f, g)
2✔
862
  {-# INLINE unpackM #-}
863

864
instance MemPack a => MemPack [a] where
865
  typeName = "[" ++ typeName @a ++ "]"
2✔
866
  packedByteCount es = packedByteCount (Length (length es)) + getSum (foldMap (Sum . packedByteCount) es)
2✔
867
  {-# INLINE packedByteCount #-}
868
  packM as = do
2✔
869
    packM (Length (length as))
2✔
870
    mapM_ packM as
2✔
871
  {-# INLINE packM #-}
872
  unpackM = do
2✔
873
    Length n <- unpackM
2✔
874
    replicateTailM n unpackM
2✔
875
  {-# INLINE unpackM #-}
876

877
-- | Tail recursive version of `replicateM`
878
replicateTailM :: Monad m => Int -> m a -> m [a]
879
replicateTailM n f = go n []
2✔
880
  where
881
    go i !acc
2✔
882
      | i <= 0 = pure $ reverse acc
2✔
883
      | otherwise = f >>= \x -> go (i - 1) (x : acc)
1✔
884
{-# INLINE replicateTailM #-}
885

886
instance MemPack ByteArray where
2✔
887
  packedByteCount ba =
2✔
888
    let len = bufferByteCount ba
2✔
889
     in packedByteCount (Length len) + len
2✔
890
  {-# INLINE packedByteCount #-}
891
  packM ba@(ByteArray ba#) = do
2✔
892
    let !len@(I# len#) = bufferByteCount ba
2✔
893
    packM (Length len)
2✔
894
    I# curPos# <- state $ \i -> (i, i + len)
2✔
895
    MutableByteArray mba# <- ask
2✔
896
    lift_# (copyByteArray# ba# 0# mba# curPos# len#)
2✔
897
  {-# INLINE packM #-}
898
  unpackM = unpackByteArray False
2✔
899
  {-# INLINE unpackM #-}
900

901
instance MemPack ShortByteString where
2✔
902
  packedByteCount ba =
2✔
903
    let len = bufferByteCount ba
2✔
904
     in packedByteCount (Length len) + len
2✔
905
  {-# INLINE packedByteCount #-}
906
  packM = packM . byteArrayFromShortByteString
2✔
907
  {-# INLINE packM #-}
908
  unpackM = byteArrayToShortByteString <$> unpackByteArray False
2✔
909
  {-# INLINE unpackM #-}
910

911
instance MemPack ByteString where
2✔
912
  packedByteCount ba =
2✔
913
    let len = bufferByteCount ba
2✔
914
     in packedByteCount (Length len) + len
2✔
915
  {-# INLINE packedByteCount #-}
916
  packM bs = do
2✔
917
    let !len@(I# len#) = bufferByteCount bs
2✔
918
    packM (Length len)
2✔
919
    I# curPos# <- state $ \i -> (i, i + len)
2✔
920
    Pack $ \(MutableByteArray mba#) -> lift $ withPtrByteStringST bs $ \(Ptr addr#) ->
2✔
921
      st_ (copyAddrToByteArray# addr# mba# curPos# len#)
2✔
922
  {-# INLINE packM #-}
923
  unpackM = pinnedByteArrayToByteString <$> unpackByteArray True
2✔
924
  {-# INLINE unpackM #-}
925

926
-- | This is the implementation of `unpackM` for `ByteArray` and `ByteString`
927
unpackByteArray :: Buffer b => Bool -> Unpack b ByteArray
928
unpackByteArray isPinned = do
2✔
929
  Length len@(I# len#) <- unpackM
2✔
930
  I# curPos# <- guardAdvanceUnpack len
2✔
931
  buf <- ask
2✔
932
  pure $! runST $ do
2✔
933
    mba@(MutableByteArray mba#) <- newMutableByteArray isPinned len
2✔
934
    buffer
2✔
935
      buf
2✔
936
      (\ba# -> st_ (copyByteArray# ba# curPos# mba# 0# len#))
2✔
937
      (\addr# -> st_ (copyAddrToByteArray# (addr# `plusAddr#` curPos#) mba# 0# len#))
2✔
938
    freezeMutableByteArray mba
2✔
939
{-# INLINE unpackByteArray #-}
940

941
-- | Increment the offset counter of `Pack` monad by then number of `packedByteCount` and
942
-- return the starting offset.
943
packIncrement :: MemPack a => a -> Pack s Int
944
packIncrement a =
2✔
945
  state $ \i ->
2✔
946
    let !n = i + packedByteCount a
1✔
947
     in (i, n)
2✔
948
{-# INLINE packIncrement #-}
949

950
-- | Increment the offset counter of `Unpack` monad by the supplied number of
951
-- bytes. Returns the original offset or fails with `RanOutOfBytesError` whenever there is
952
-- not enough bytes in the `Buffer`.
953
guardAdvanceUnpack :: Buffer b => Int -> Unpack b Int
954
guardAdvanceUnpack n@(I# n#) = do
2✔
955
  buf <- ask
2✔
956
  let len = bufferByteCount buf
2✔
957
      failOutOfBytes i =
2✔
958
        failUnpack $
2✔
959
          toSomeError $
2✔
960
            RanOutOfBytesError
2✔
961
              { ranOutOfBytesRead = i
2✔
962
              , ranOutOfBytesAvailable = len
2✔
963
              , ranOutOfBytesRequested = n
2✔
964
              }
965
  -- Check that we still have enough bytes, while guarding against integer overflow.
966
  join $ state $ \i@(I# i#) ->
2✔
967
    case addIntC# i# n# of
2✔
968
      (# adv#, 0# #) ->
969
        if len < I# adv#
2✔
970
          then (failOutOfBytes i, i)
1✔
971
          else (pure i, I# adv#)
2✔
972
      _ -> (failOutOfBytes i, i)
×
973
{-# INLINE guardAdvanceUnpack #-}
974

975
-- | Serialize a type into an unpinned `ByteArray`
976
--
977
-- ====__Examples__
978
--
979
-- >>> :set -XTypeApplications
980
-- >>> unpack @[Int] $ pack ([1,2,3,4,5] :: [Int])
981
-- Right [1,2,3,4,5]
982
pack :: forall a. (MemPack a, HasCallStack) => a -> ByteArray
983
pack = packByteArray False
2✔
984
{-# INLINE pack #-}
985

986
-- | Serialize a type into a pinned `ByteString`
987
packByteString :: forall a. (MemPack a, HasCallStack) => a -> ByteString
988
packByteString = pinnedByteArrayToByteString . packByteArray True
2✔
989
{-# INLINE packByteString #-}
990

991
-- | Serialize a type into an unpinned `ShortByteString`
992
packShortByteString :: forall a. (MemPack a, HasCallStack) => a -> ShortByteString
993
packShortByteString = byteArrayToShortByteString . pack
×
994
{-# INLINE packShortByteString #-}
995

996
-- | Same as `pack`, but allows controlling the pinnedness of allocated memory
997
packByteArray ::
998
  forall a.
999
  (MemPack a, HasCallStack) =>
1000
  -- | Should the array be allocated in pinned memory?
1001
  Bool ->
1002
  a ->
1003
  ByteArray
1004
packByteArray isPinned a =
2✔
1005
  packWithByteArray isPinned (typeName @a) (packedByteCount a) (packM a)
1✔
1006
{-# INLINE packByteArray #-}
1007

1008
-- | Allocate a `MutableByteArray` and run the supplied `Pack` action on it. Freezes the
1009
-- allocated `MutableByteArray` at the end yielding the immutable `ByteArray` with
1010
-- serialization packed into it.
1011
packWithByteArray ::
1012
  HasCallStack =>
1013
  -- | Should the array be allocated in pinned memory?
1014
  Bool ->
1015
  -- | Name of the type that is being serialized. Used for error reporting
1016
  String ->
1017
  -- | Size of the array to be allocated
1018
  Int ->
1019
  (forall s. Pack s ()) ->
1020
  ByteArray
1021
packWithByteArray isPinned name len packerM =
2✔
1022
  runST $ packWithMutableByteArray isPinned name len packerM >>= freezeMutableByteArray
1✔
1023
{-# INLINE packWithByteArray #-}
1024

1025
-- | Same as `packByteArray`, but produces a mutable array instead
1026
packMutableByteArray ::
1027
  forall a s.
1028
  (MemPack a, HasCallStack) =>
1029
  -- | Should the array be allocated in pinned memory?
1030
  Bool ->
1031
  a ->
1032
  ST s (MutableByteArray s)
1033
packMutableByteArray isPinned a =
×
1034
  packWithMutableByteArray isPinned (typeName @a) (packedByteCount a) (packM a)
×
1035
{-# INLINE packMutableByteArray #-}
1036

1037
-- | Allocate a `MutableByteArray` and run the supplied `Pack` action on it.
1038
packWithMutableByteArray ::
1039
  forall s.
1040
  HasCallStack =>
1041
  -- | Should the array be allocated in pinned memory?
1042
  Bool ->
1043
  -- | Name of the type that is being serialized. Used for error reporting
1044
  String ->
1045
  -- | Size of the mutable array to be allocated
1046
  Int ->
1047
  -- | Packing action to be executed on the mutable buffer
1048
  Pack s () ->
1049
  ST s (MutableByteArray s)
1050
packWithMutableByteArray isPinned name len packerM = do
2✔
1051
  mba <- newMutableByteArray isPinned len
2✔
1052
  filledBytes <- execStateT (runPack packerM mba) 0
2✔
1053
  when (filledBytes /= len) $
2✔
1054
    if (filledBytes < len)
×
1055
      then
1056
        error $
×
1057
          "Some bug in 'packM' was detected. Buffer of length " <> showBytes len
×
1058
            ++ " was not fully filled while packing " <> name
×
1059
            ++ ". Unfilled " <> showBytes (len - filledBytes) <> "."
×
1060
      else
1061
        -- This is a critical error, therefore we are not gracefully failing this unpacking
1062
        error $
×
1063
          "Potential buffer overflow. Some bug in 'packM' was detected while packing " <> name
×
1064
            ++ ". Filled " <> showBytes (filledBytes - len) <> " more than allowed into a buffer of length "
×
1065
            ++ show len
×
1066
  pure mba
2✔
1067
{-# INLINE packWithMutableByteArray #-}
1068

1069
-- | Unpack a memory `Buffer` into a type using its `MemPack` instance. Besides the
1070
-- unpacked type it also returns an index into a buffer where unpacked has stopped.
1071
unpackLeftOver :: forall a b. (MemPack a, Buffer b, HasCallStack) => b -> Fail SomeError (a, Int)
1072
unpackLeftOver b = do
2✔
1073
  let len = bufferByteCount b
2✔
1074
  res@(_, consumedBytes) <- runStateT (runUnpack unpackM b) 0
2✔
1075
  when (consumedBytes > len) $
2✔
1076
    -- This is a critical error, therefore we are not gracefully failing this unpacking
1077
    error $
×
1078
      "Potential buffer overflow. Some bug in 'unpackM' was detected while unpacking " <> typeName @a
×
1079
        ++ ". Consumed " <> showBytes (consumedBytes - len) <> " more than allowed from a buffer of length "
×
1080
        ++ show len
×
1081
  pure res
2✔
1082
{-# INLINEABLE unpackLeftOver #-}
1083

1084
-- | Unpack a memory `Buffer` into a type using its `MemPack` instance. Besides potential
1085
-- unpacking failures due to a malformed buffer it will also fail the supplied `Buffer`
1086
-- was not fully consumed. Use `unpackLeftOver`, whenever a partially consumed buffer is
1087
-- possible.
1088
unpack :: forall a b. (MemPack a, Buffer b, HasCallStack) => b -> Either SomeError a
1089
unpack = first fromMultipleErrors . runFailAgg . unpackFail
2✔
1090
{-# INLINE unpack #-}
1091

1092
-- | Same as `unpack` except fails in a `Fail` monad, instead of `Either`.
1093
unpackFail :: forall a b. (MemPack a, Buffer b, HasCallStack) => b -> Fail SomeError a
1094
unpackFail b = do
2✔
1095
  let len = bufferByteCount b
2✔
1096
  (a, consumedBytes) <- unpackLeftOver b
2✔
1097
  when (consumedBytes /= len) $
2✔
1098
    failT $
2✔
1099
      toSomeError $
2✔
1100
        NotFullyConsumedError
2✔
1101
          { notFullyConsumedRead = consumedBytes
2✔
1102
          , notFullyConsumedAvailable = len
2✔
1103
          , notFullyConsumedTypeName = typeName @a
2✔
1104
          }
1105
  pure a
2✔
1106
{-# INLINEABLE unpackFail #-}
1107

1108
-- | Same as `unpackFail` except fails in any `MonadFail`, instead of `Fail`.
1109
unpackMonadFail :: forall a b m. (MemPack a, Buffer b, F.MonadFail m) => b -> m a
1110
unpackMonadFail = either (F.fail . show) pure . unpack
×
1111
{-# INLINE unpackMonadFail #-}
1112

1113
-- | Same as `unpack` except throws a runtime exception upon a failure
1114
unpackError :: forall a b. (MemPack a, Buffer b, HasCallStack) => b -> a
1115
unpackError = errorFail . unpackFail
2✔
1116
{-# INLINE unpackError #-}
1117

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

1124
instance MemPack (VarLen Word16) where
2✔
1125
  packedByteCount = packedVarLenByteCount
2✔
1126
  {-# INLINE packedByteCount #-}
1127
  packM v@(VarLen x) = p7 (p7 (p7 (errorTooManyBits "Word16"))) (numBits - 7)
1✔
1128
    where
1129
      p7 = packIntoCont7 x
2✔
1130
      {-# INLINE p7 #-}
1131
      numBits = packedVarLenByteCount v * 7
2✔
1132
  {-# INLINE packM #-}
1133
  unpackM = do
2✔
1134
    let d7 = unpack7BitVarLen
2✔
1135
        {-# INLINE d7 #-}
1136
    VarLen <$> d7 (d7 (unpack7BitVarLenLast 0b_1111_1100)) 0 0
2✔
1137
  {-# INLINE unpackM #-}
1138

1139
instance MemPack (VarLen Word32) where
2✔
1140
  packedByteCount = packedVarLenByteCount
2✔
1141
  {-# INLINE packedByteCount #-}
1142
  packM v@(VarLen x) = p7 (p7 (p7 (p7 (p7 (errorTooManyBits "Word32"))))) (numBits - 7)
1✔
1143
    where
1144
      p7 = packIntoCont7 x
2✔
1145
      {-# INLINE p7 #-}
1146
      numBits = packedVarLenByteCount v * 7
2✔
1147
  {-# INLINE packM #-}
1148
  unpackM = do
2✔
1149
    let d7 = unpack7BitVarLen
2✔
1150
        {-# INLINE d7 #-}
1151
    VarLen <$> d7 (d7 (d7 (d7 (unpack7BitVarLenLast 0b_1111_0000)))) 0 0
2✔
1152
  {-# INLINE unpackM #-}
1153

1154
instance MemPack (VarLen Word64) where
2✔
1155
  packedByteCount = packedVarLenByteCount
2✔
1156
  {-# INLINE packedByteCount #-}
1157
  packM v@(VarLen x) =
2✔
1158
    p7 (p7 (p7 (p7 (p7 (p7 (p7 (p7 (p7 (p7 (errorTooManyBits "Word64")))))))))) (numBits - 7)
1✔
1159
    where
1160
      p7 = packIntoCont7 x
2✔
1161
      {-# INLINE p7 #-}
1162
      numBits = packedVarLenByteCount v * 7
2✔
1163
  {-# INLINE packM #-}
1164
  unpackM = do
2✔
1165
    let d7 = unpack7BitVarLen
2✔
1166
        {-# INLINE d7 #-}
1167
    VarLen <$> d7 (d7 (d7 (d7 (d7 (d7 (d7 (d7 (d7 (unpack7BitVarLenLast 0b_1111_1110))))))))) 0 0
2✔
1168
  {-# INLINE unpackM #-}
1169

1170
instance MemPack (VarLen Word) where
2✔
1171
  packedByteCount = packedVarLenByteCount
2✔
1172
  {-# INLINE packedByteCount #-}
1173
#if WORD_SIZE_IN_BITS == 32
1174
  packM mba v@(VarLen x) = p7 (p7 (p7 (p7 (p7 (errorTooManyBits "Word"))))) (numBits - 7)
1175
    where
1176
      p7 = packIntoCont7 mba x
1177
      {-# INLINE p7 #-}
1178
      numBits = packedVarLenByteCount v * 7
1179
  {-# INLINE packM #-}
1180
  unpackM buf = do
1181
    let d7 = unpack7BitVarLen buf
1182
        {-# INLINE d7 #-}
1183
    VarLen <$> d7 (d7 (d7 (d7 (unpack7BitVarLenLast buf 0b_1111_0000)))) 0 0
1184
  {-# INLINE unpackM #-}
1185
#elif WORD_SIZE_IN_BITS == 64
1186
  packM v@(VarLen x) =
2✔
1187
    p7 (p7 (p7 (p7 (p7 (p7 (p7 (p7 (p7 (p7 (errorTooManyBits "Word")))))))))) (numBits - 7)
1✔
1188
    where
1189
      p7 = packIntoCont7 x
2✔
1190
      {-# INLINE p7 #-}
1191
      numBits = packedVarLenByteCount v * 7
2✔
1192
  {-# INLINE packM #-}
1193
  unpackM = do
2✔
1194
    let d7 = unpack7BitVarLen
2✔
1195
        {-# INLINE d7 #-}
1196
    VarLen <$> d7 (d7 (d7 (d7 (d7 (d7 (d7 (d7 (d7 (unpack7BitVarLenLast 0b_1111_1110))))))))) 0 0
2✔
1197
  {-# INLINE unpackM #-}
1198
#else
1199
#error "Only 32bit and 64bit systems are supported"
1200
#endif
1201

1202
packedVarLenByteCount :: FiniteBits b => VarLen b -> Int
1203
packedVarLenByteCount (VarLen x) =
2✔
1204
  case (finiteBitSize x - countLeadingZeros x) `quotRem` 7 of
1✔
1205
    (0, 0) -> 1
2✔
1206
    (q, 0) -> q
2✔
1207
    (q, _) -> q + 1
2✔
1208
{-# INLINE packedVarLenByteCount #-}
1209

1210
errorTooManyBits :: [Char] -> a
1211
errorTooManyBits name =
×
1212
  error $ "Bug detected. Trying to pack more bits for " ++ name ++ " than it should be posssible"
×
1213

1214
packIntoCont7 ::
1215
  (Bits t, Integral t) => t -> (Int -> Pack s ()) -> Int -> Pack s ()
1216
packIntoCont7 x cont n
2✔
1217
  | n <= 0 = packM (fromIntegral @_ @Word8 x .&. complement topBit8)
2✔
1218
  | otherwise = do
1✔
1219
      packM (fromIntegral @_ @Word8 (x `shiftR` n) .|. topBit8)
2✔
1220
      cont (n - 7)
2✔
1221
  where
1222
    topBit8 :: Word8
1223
    topBit8 = 0b_1000_0000
2✔
1224
{-# INLINE packIntoCont7 #-}
1225

1226
-- | Decode a variable length integral value that is encoded with 7 bits of data
1227
-- and the most significant bit (MSB), the 8th bit is set whenever there are
1228
-- more bits following. Continuation style allows us to avoid
1229
-- recursion. Removing loops is good for performance.
1230
unpack7BitVarLen ::
1231
  (Num a, Bits a, Buffer b) =>
1232
  -- | Continuation that will be invoked if MSB is set
1233
  (Word8 -> a -> Unpack b a) ->
1234
  -- | Will be set either to 0 initially or to the very first unmodified byte, which is
1235
  -- guaranteed to have the first bit set.
1236
  Word8 ->
1237
  -- | Accumulator
1238
  a ->
1239
  Unpack b a
1240
unpack7BitVarLen cont firstByte !acc = do
2✔
1241
  b8 :: Word8 <- unpackM
2✔
1242
  if b8 `testBit` 7
2✔
1243
    then
1244
      cont (if firstByte == 0 then b8 else firstByte) (acc `shiftL` 7 .|. fromIntegral (b8 `clearBit` 7))
2✔
1245
    else pure (acc `shiftL` 7 .|. fromIntegral b8)
2✔
1246
{-# INLINE unpack7BitVarLen #-}
1247

1248
unpack7BitVarLenLast ::
1249
  forall t b.
1250
  (Num t, Bits t, MemPack t, Buffer b) =>
1251
  Word8 ->
1252
  Word8 ->
1253
  t ->
1254
  Unpack b t
1255
unpack7BitVarLenLast mask firstByte acc = do
2✔
1256
  res <- unpack7BitVarLen (\_ _ -> F.fail "Too many bytes.") firstByte acc
1✔
1257
  -- Only while decoding the last 7bits we check if there was too many
1258
  -- bits supplied at the beginning.
1259
  unless (firstByte .&. mask == 0b_1000_0000) $
2✔
1260
    F.fail $
×
1261
      "Unexpected bits for "
×
1262
        ++ typeName @t
×
1263
        ++ " were set in the first byte of 'VarLen': 0x" <> showHex firstByte ""
×
1264
  pure res
2✔
1265
{-# INLINE unpack7BitVarLenLast #-}
1266

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

1273
instance Bounded Length where
1274
  minBound = 0
2✔
1275
  maxBound = Length maxBound
2✔
1276

1277
instance Enum Length where
1✔
1278
  toEnum n
2✔
1279
    | n < 0 = error $ "toEnum: Length cannot be negative: " ++ show n
1✔
1280
    | otherwise = Length n
1✔
1281
  fromEnum = unLength
2✔
1282

1283
instance MemPack Length where
2✔
1284
  packedByteCount = packedByteCount . VarLen . fromIntegral @Int @Word . unLength
2✔
1285
  packM (Length n)
2✔
1286
    | n < 0 = error $ "Length cannot be negative. Supplied: " ++ show n
1✔
1287
    | otherwise = packM (VarLen (fromIntegral @Int @Word n))
1✔
1288
  {-# INLINE packM #-}
1289
  unpackM = do
2✔
1290
    VarLen (w :: Word) <- unpackM
2✔
1291
    when (testBit w (finiteBitSize w - 1)) $
1✔
1292
      F.fail $
2✔
1293
        "Attempt to unpack negative length was detected: " ++ show (fromIntegral @Word @Int w)
×
1294
    pure $ Length $ fromIntegral @Word @Int w
2✔
1295
  {-# INLINE unpackM #-}
1296

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

1301
-- Manually defined instance, since ghc-8.6 has issues with deriving MemPack
1302
instance MemPack Tag where
2✔
1303
  packedByteCount _ = packedTagByteCount
2✔
1304
  {-# INLINE packedByteCount #-}
1305
  unpackM = unpackTagM
2✔
1306
  {-# INLINE unpackM #-}
1307
  packM = packTagM
2✔
1308
  {-# INLINE packM #-}
1309

1310
packedTagByteCount :: Int
1311
packedTagByteCount = SIZEOF_WORD8
2✔
1312
{-# INLINE packedTagByteCount #-}
1313

1314
unpackTagM :: Buffer b => Unpack b Tag
1315
unpackTagM = Tag <$> unpackM
2✔
1316
{-# INLINE unpackTagM #-}
1317

1318
packTagM :: Tag -> Pack s ()
1319
packTagM = packM . unTag
2✔
1320
{-# INLINE packTagM #-}
1321

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

1325
lift_# :: (State# s -> State# s) -> Pack s ()
1326
lift_# f = Pack $ \_ -> lift $ st_ f
2✔
1327
{-# INLINE lift_# #-}
1328

1329
st_ :: (State# s -> State# s) -> ST s ()
1330
st_ f = ST $ \s# -> (# f s#, () #)
2✔
1331
{-# INLINE st_ #-}
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