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

lehins / mempack / 13

26 Sep 2025 08:57PM UTC coverage: 84.043% (-1.9%) from 85.987%
13

push

github

web-flow
Merge pull request #14 from lehins/add-buffer-instances-for-vector

Add instances for `primitive` and `vector`

61 of 91 new or added lines in 2 files covered. (67.03%)

56 existing lines in 3 files now uncovered.

711 of 846 relevant lines covered (84.04%)

1.6 hits per line

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

51.06
/src/Data/MemPack/Buffer.hs
1
{-# LANGUAGE AllowAmbiguousTypes #-}
2
{-# LANGUAGE BangPatterns #-}
3
{-# LANGUAGE CPP #-}
4
{-# LANGUAGE FlexibleInstances #-}
5
{-# LANGUAGE MagicHash #-}
6
{-# LANGUAGE UnboxedTuples #-}
7

8
-- |
9
-- Module      : Data.MemPack.Buffer
10
-- Copyright   : (c) Alexey Kuleshevich 2024-2025
11
-- License     : BSD3
12
-- Maintainer  : Alexey Kuleshevich <alexey@kuleshevi.ch>
13
-- Stability   : experimental
14
-- Portability : non-portable
15
module Data.MemPack.Buffer (
16
  Buffer (..),
17
  newMutableByteArray,
18
  freezeMutableByteArray,
19
  withPtrByteStringST,
20
  withAddrByteStringST,
21
  withForeignPtrST,
22
  pinnedByteArrayToByteString,
23
  pinnedByteArrayToForeignPtr,
24
  byteArrayToShortByteString,
25
  byteArrayFromShortByteString,
26
)
27
where
28

29
import Data.Array.Byte
30
import qualified Data.ByteString as BS
31
import qualified Data.ByteString.Internal as BS
32
import qualified Data.ByteString.Short.Internal as SBS
33
import Data.Primitive.PrimArray (PrimArray (..))
34
import Data.Word (Word8)
35
import GHC.Exts
36
import GHC.ForeignPtr
37
import GHC.ST
38
#if !MIN_VERSION_primitive(0,8,0)
39
import qualified Data.Primitive.ByteArray as Prim (ByteArray(..))
40
#endif
41
import qualified Data.Vector.Primitive as VP (Vector (..))
42
import qualified Data.Vector.Storable as VS (
43
  Vector,
44
  length,
45
  unsafeFromForeignPtr0,
46
  unsafeToForeignPtr0,
47
 )
48

49
-- | Immutable memory buffer
50
class Buffer b where
51
  -- | Number of accessible bytes in the buffer.
52
  bufferByteCount :: b -> Int
53

54
  -- | Use one of the two suppplied functions to access memory of the buffer:
55
  buffer ::
56
    -- | A type that contains the actual buffer that will be accessed
57
    b ->
58
    -- | In case when a buffer is backed by a `ByteArray#` it will be accessed as such with an
59
    -- offset from the beginning of the ByteArray
60
    (ByteArray# -> Int# -> a) ->
61
    -- | In case when a buffer is backed by a pointer or a pinned `ByteArray#` it can be accessed as
62
    -- an `Addr#`. No offset is necessary here, since same affect can be achieved with pointer
63
    -- arithmetic.
64
    (Addr# -> a) ->
65
    a
66

67
  mkBuffer :: ByteArray# -> b
68

69
  bufferHasToBePinned :: Bool
70

71
instance Buffer ByteArray where
72
  bufferByteCount (ByteArray ba#) = I# (sizeofByteArray# ba#)
2✔
73
  {-# INLINE bufferByteCount #-}
74
  buffer (ByteArray ba#) f _ = f ba# 0#
2✔
75
  {-# INLINE buffer #-}
NEW
76
  mkBuffer ba# = ByteArray ba#
×
77
  {-# INLINE mkBuffer #-}
NEW
78
  bufferHasToBePinned = False
×
79

80
#if !MIN_VERSION_primitive(0,8,0)
81
instance Buffer Prim.ByteArray where
82
  bufferByteCount (Prim.ByteArray ba#) = I# (sizeofByteArray# ba#)
83
  {-# INLINE bufferByteCount #-}
84
  buffer (Prim.ByteArray ba#) f _ = f ba# 0#
85
  {-# INLINE buffer #-}
86
  mkBuffer ba# = Prim.ByteArray ba#
87
  {-# INLINE mkBuffer #-}
88
  bufferHasToBePinned = False
89
#endif
90

91
instance Buffer SBS.ShortByteString where
92
  bufferByteCount = SBS.length
2✔
93
  {-# INLINE bufferByteCount #-}
NEW
94
  buffer (SBS.SBS ba#) f _ = f ba# 0#
×
95
  {-# INLINE buffer #-}
NEW
96
  mkBuffer ba# = SBS.SBS ba#
×
97
  {-# INLINE mkBuffer #-}
NEW
98
  bufferHasToBePinned = False
×
99

100
instance Buffer BS.ByteString where
101
  bufferByteCount = BS.length
2✔
102
  {-# INLINE bufferByteCount #-}
103
  buffer bs _f g =
2✔
104
    runST $ withAddrByteStringST bs $ \addr# -> pure (g addr#)
2✔
105
  {-# INLINE buffer #-}
NEW
106
  mkBuffer ba# = pinnedByteArrayToByteString (ByteArray ba#)
×
107
  {-# INLINE mkBuffer #-}
NEW
108
  bufferHasToBePinned = True
×
109

110
instance Buffer (PrimArray Word8) where
111
  bufferByteCount (PrimArray ba#) = I# (sizeofByteArray# ba#)
2✔
112
  {-# INLINE bufferByteCount #-}
NEW
113
  buffer (PrimArray ba#) f _ = f ba# 0#
×
114
  {-# INLINE buffer #-}
NEW
115
  mkBuffer ba# = PrimArray ba#
×
116
  {-# INLINE mkBuffer #-}
NEW
117
  bufferHasToBePinned = False
×
118

119
instance Buffer (VP.Vector Word8) where
120
  bufferByteCount (VP.Vector _ len _) = len
2✔
121
  {-# INLINE bufferByteCount #-}
NEW
122
  buffer (VP.Vector (I# off#) _ ba) f = buffer ba (\ba# _ -> f ba# off#)
×
123
  {-# INLINE buffer #-}
NEW
124
  mkBuffer ba# =
×
NEW
125
    let ba = mkBuffer ba#
×
NEW
126
     in VP.Vector 0 (bufferByteCount ba) ba
×
127
  {-# INLINE mkBuffer #-}
NEW
128
  bufferHasToBePinned = False
×
129

130
instance Buffer (VS.Vector Word8) where
NEW
131
  bufferByteCount = VS.length
×
132
  {-# INLINE bufferByteCount #-}
NEW
133
  buffer v _f g =
×
NEW
134
    runST $ withForeignPtrST (fst $ VS.unsafeToForeignPtr0 v) $ \addr# -> pure (g addr#)
×
135
  {-# INLINE buffer #-}
NEW
136
  mkBuffer ba# =
×
NEW
137
    VS.unsafeFromForeignPtr0 (pinnedByteArrayToForeignPtr ba#) (I# (sizeofByteArray# ba#))
×
138
  {-# INLINE mkBuffer #-}
NEW
139
  bufferHasToBePinned = True
×
140

141
-- | Allocate a new uninitialized `MutableByteArray`.
142
--
143
-- * __Warning__ - Memory allocated might contain random garbage and must be fully overwritten.
144
--
145
-- __⚠__ - Violation of the above rule could lead non-determinism and breakage of referential
146
-- transparency.
147
--
148
-- @since 0.1.0
149
newMutableByteArray ::
150
  -- | Should the mutable array be allocated as pinned or not
151
  Bool ->
152
  -- | Size of the mutable array in number of bytes.
153
  Int ->
154
  ST s (MutableByteArray s)
155
newMutableByteArray isPinned (I# len#) =
2✔
156
  ST $ \s# -> case (if isPinned then newPinnedByteArray# else newByteArray#) len# s# of
2✔
157
    (# s'#, mba# #) -> (# s'#, MutableByteArray mba# #)
2✔
158
{-# INLINE newMutableByteArray #-}
159

160
-- | /O(1)/ - Cast a `MutableByteArray` to an immutable `ByteArray` without copy.
161
--
162
-- * __Warning__ - Source mutable array must not be mutated, after this action.
163
--
164
-- __⚠__ - Violation of the above rule could potentially lead to corrupt memory and segfaults.
165
--
166
-- @since 0.1.0
167
freezeMutableByteArray :: MutableByteArray d -> ST d ByteArray
168
freezeMutableByteArray (MutableByteArray mba#) =
2✔
169
  ST $ \s# -> case unsafeFreezeByteArray# mba# s# of
2✔
170
    (# s'#, ba# #) -> (# s'#, ByteArray ba# #)
2✔
171

172
-- | Run ST action on the underlying `Ptr` that points to the beginning of the `ByteString`
173
-- buffer. It is ok to use ByteString withing ST, as long as underlying pointer is never mutated or
174
-- returned from the supplied action.
175
--
176
-- * __Warning__ - It is important for the supplied action to not produce bottom, i.e. runtime
177
-- exceptions or infinite loops are not allowed within its body.
178
--
179
-- __⚠__ - Violation of the above rule could potentially lead to corrupt memory and segfaults.
180
--
181
-- @since 0.1.0
182
withPtrByteStringST :: BS.ByteString -> (Ptr a -> ST s b) -> ST s b
NEW
183
withPtrByteStringST bs f = withAddrByteStringST bs $ \addr# -> f (Ptr addr#)
×
184
{-# INLINE withPtrByteStringST #-}
185

186
-- | Same as `withPtrByteStringST`, except the supplied action expects an `Addr#` instead of a
187
-- `Ptr`.
188
--
189
-- __⚠__ - Violation of the rule from `withPtrByteStringST` could potentially lead to corrupt memory
190
-- and segfaults.
191
--
192
-- @since 0.2.0
193
withAddrByteStringST :: BS.ByteString -> (Addr# -> ST s b) -> ST s b
194
#if MIN_VERSION_bytestring(0,11,0)
195
withAddrByteStringST (BS.BS fp _) = withForeignPtrST fp
2✔
196
#else
197
withAddrByteStringST (BS.PS fp offset _) = withForeignPtrST (fp `plusForeignPtr` offset)
198
#endif
199
{-# INLINE withAddrByteStringST #-}
200

201
-- | Run an `ST` action on the underlying `Ptr` that points to the beginning of the `ByteString`
202
-- buffer. It is ok to use ByteString withing ST, as long as underlying pointer is never mutated or
203
-- returned from the supplied action.
204
--
205
-- * __Warning__ - It is important for the memory that backs the underlying `ForeignPtr` to not be
206
-- mutated outside of the `ST` monad that this action operates in, which is only allowed if it was
207
-- allocated in this execution of the `ST` monad.
208
--
209
-- * __Warning__ - It is important for the memory that backs the underlying `ForeignPtr` to not be
210
-- mutated at all if `ForeignPtr` was not allocated within the `ST` monad that this action operates
211
-- in.
212
--
213
-- * __Warning__ - It is important for the supplied action to not produce bottom, i.e. runtime
214
-- exceptions or infinite loops are not allowed within its body.
215
--
216
-- __⚠__ - Violation of the above rules could potentially lead to corrupt memory and segfaults.
217
--
218
-- @since 0.2.0
219
withForeignPtrST :: ForeignPtr a -> (Addr# -> ST s b) -> ST s b
220
withForeignPtrST (ForeignPtr addr# ptrContents) f = do
2✔
221
  !r <- f addr#
2✔
222
  -- It is safe to use `touch#` within ST, so using `unsafeCoerce#` here is totally OK
223
  ST $ \s# -> (# unsafeCoerce# (touch# ptrContents (unsafeCoerce# s#)), () #)
1✔
224
  pure r
2✔
225
{-# INLINE withForeignPtrST #-}
226

227
-- | /O(1)/ - Convert a pinned `ByteArray` to `BS.ByteString`.
228
--
229
-- * __Warning__ - There is no check that source `ByteArray` was allocated as pinned, so user of this
230
-- function must guarantee this invariant.
231
--
232
-- __⚠__ - Violation of the above rules could potentially lead to corrupt memory and segfaults.
233
--
234
-- @since 0.1.0
235
pinnedByteArrayToByteString :: ByteArray -> BS.ByteString
236
pinnedByteArrayToByteString (ByteArray ba#) =
2✔
237
  BS.PS (pinnedByteArrayToForeignPtr ba#) 0 (I# (sizeofByteArray# ba#))
2✔
238
{-# INLINE pinnedByteArrayToByteString #-}
239

240
-- | /O(1)/ - Convert a pinned `ByteArray#` to `ForeignPtr`.
241
--
242
-- * __Warning__ - There is no check that source `ByteArray#` was allocated as pinned, so user of this
243
-- function must guarantee this invariant.
244
--
245
-- __⚠__ - Violation of the above rules could potentially lead to corrupt memory and segfaults.
246
--
247
-- @since 0.1.0
248
pinnedByteArrayToForeignPtr :: ByteArray# -> ForeignPtr a
249
pinnedByteArrayToForeignPtr ba# =
2✔
250
  ForeignPtr (byteArrayContents# ba#) (PlainPtr (unsafeCoerce# ba#))
1✔
251
{-# INLINE pinnedByteArrayToForeignPtr #-}
252

253
-- | /O(1)/ - Convert `ByteArray` to `SBS.ShortByteString`
254
--
255
-- @since 0.1.0
256
byteArrayToShortByteString :: ByteArray -> SBS.ShortByteString
UNCOV
257
byteArrayToShortByteString (ByteArray ba#) = SBS.SBS ba#
×
258
{-# INLINE byteArrayToShortByteString #-}
259

260
-- | /O(1)/ - Inverse of `byteArrayToShortByteString`. Convert `SBS.ShortByteString` to  `ByteArray`
261
--
262
-- @since 0.1.0
263
byteArrayFromShortByteString :: SBS.ShortByteString -> ByteArray
264
byteArrayFromShortByteString (SBS.SBS ba#) = ByteArray ba#
2✔
265
{-# INLINE byteArrayFromShortByteString #-}
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

© 2025 Coveralls, Inc