-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathForeign.hs
More file actions
346 lines (294 loc) · 13 KB
/
Copy pathForeign.hs
File metadata and controls
346 lines (294 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
--------------------------------------------------------------------------------
{-# language BangPatterns #-}
{-# language ForeignFunctionInterface #-}
{-# language MagicHash #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language UnboxedTuples #-}
--------------------------------------------------------------------------------
-- | A collection of data types, classes, and functions for interfacing
-- with another programming language using the `Prim` interface instead
-- of the `Storable` interface.
module Data.Primitive.Foreign
(
-- * Prim-Storable methods
sizeOf, alignment
, peek, peekElemOff, peekByteOff
, poke, pokeElemOff, pokeByteOff
-- * Memory allocation
-- ** Local allocation
, alloca, F.allocaBytes, F.allocaBytesAligned
-- ** Dynamic allocation
, malloc, F.mallocBytes
, calloc, F.callocBytes
, realloc, F.reallocBytes
, F.free, F.finalizerFree
-- * Marshalling arrays
-- ** Allocation
, mallocArray, mallocArray0
, allocaArray, allocaArray0
, reallocArray, reallocArray0
, callocArray, callocArray0
-- ** Marshalling
, peekArray, peekArray0
, pokeArray, pokeArray0
-- ** Combined allocation and marshalling
, newArray, newArray0
, withArray, withArray0
, withArrayLen, withArrayLen0
-- ** Copying
, copyArray
, moveArray
-- ** Finding the length
, lengthArray0
-- ** Indexing
, advancePtr
-- * General marshalling utilities
-- ** Combined allocation and marshalling
, with
, new
-- ** Marshalling of Maybe values
, F.maybeNew
, F.maybeWith
, F.maybePeek
-- ** Haskellish interface to memcpy and memmove
, F.copyBytes
, F.moveBytes
-- ** Filling up memory areas with required values
, F.fillBytes
) where
--------------------------------------------------------------------------------
import Control.Monad.Primitive (primitive)
import Data.Coerce (coerce)
import Data.Primitive.PrimArray
import Data.Primitive.Types
import Data.Void (Void)
import GHC.Exts
import GHC.Ptr
import qualified Foreign as F
--------------------------------------------------------------------------------
-- [Section: Foreign.Storable]
-- | Read a value from a memory area regarded as an array of values
-- of the same kind. The first argument specifies the start address
-- of the array and the second the index into the array (the first
-- element of the array has index @0@). The following equality holds,
--
-- > peekElemOff addr idx = fixIO $ \result ->
-- > peek (addr `plusPtr` (idx * sizeOf result))
--
-- Note that this is only a specification, not
-- necessarily the concrete implementation of the
-- function.
peekElemOff :: forall a. Prim a => Ptr a -> Int -> IO a
peekElemOff !ptr = coerce (F.peekElemOff @(PrimStorable a) (coerce ptr))
-- | Write a value to a memory area regarded as an array of
-- values of the same kind. The following equality holds:
--
-- > pokeElemOff addr idx x =
-- > poke (addr `plusPtr` (idx * sizeOf x)) x
pokeElemOff :: forall a. Prim a => Ptr a -> Int -> a -> IO ()
pokeElemOff !ptr !idx a = F.pokeElemOff (coerce ptr) idx (PrimStorable a)
-- | Read a value from a memory location given by a base
-- address and offset. The following equality holds:
--
-- > peekByteOff addr off = peek (addr `plusPtr` off)
peekByteOff :: forall a. Prim a => Ptr Void -> Int -> IO a
peekByteOff !ptr = coerce (F.peekByteOff @(PrimStorable a) ptr)
-- | Write a value to a memory location given by a base
-- address and offset. The following equality holds:
--
-- > pokeByteOff addr off x = poke (addr `plusPtr` off) x
pokeByteOff :: forall a. Prim a => Ptr Void -> Int -> a -> IO ()
pokeByteOff !ptr !idx a = F.pokeByteOff ptr idx (PrimStorable a)
-- | Read a value from the given memory location.
--
-- Note that the peek and poke functions might require properly
-- aligned addresses to function correctly. This is architecture
-- dependent; thus, portable code should ensure that when peeking
-- or poking values of some type @a@, the alignment constraint for
-- @a@, as given by the function 'alignment' is fulfilled.
peek :: forall a. Prim a => Ptr a -> IO a
peek = coerce (F.peek . coerce @(Ptr a) @(Ptr (PrimStorable a)))
-- | Write the given value to the given memory location. Alignment
-- restrictions might apply; see 'peek'.
poke :: forall a. Prim a => Ptr a -> a -> IO ()
poke !ptr a = F.poke (coerce ptr) (PrimStorable a)
--------------------------------------------------------------------------------
-- [Section: Foreign.Marshal.Alloc]
-- |@'alloca' f@ executes the computation @f@, passing as argument
-- a pointer to a temporarily allocated block of memory sufficient to
-- hold values of type @a@.
--
-- The memory is freed when @f@ terminates (either normally or via an
-- exception), so the pointer passed to @f@ must /not/ be used after this.
alloca :: forall a b. Prim a => (Ptr a -> IO b) -> IO b
alloca f = F.alloca (coerce f :: Ptr (PrimStorable a) -> IO b)
-- | Allocate a block of memory that is sufficient to hold values of type
-- @a@. The size of the area allocated is determined by the 'sizeOf'
-- method from the instance of 'Storable' for the appropriate type.
--
-- The memory may be deallocated using 'free' or 'finalizerFree' when
-- no longer required.
--
malloc :: forall a. Prim a => IO (Ptr a)
malloc = F.mallocBytes (sizeOf @a undefined)
-- | Like 'malloc' but memory is filled with bytes of value zero.
--
calloc :: forall a. Prim a => IO (Ptr a)
calloc = F.callocBytes (sizeOf @a undefined)
-- | Resize a memory area that was allocated with 'malloc' or 'mallocBytes'
-- to the size needed to store values of type @b@. The returned pointer
-- may refer to an entirely different memory area, but will be suitably
-- aligned to hold values of type @b@. The contents of the referenced
-- memory area will be the same as of the original pointer up to the
-- minimum of the original size and the size of values of type @b@.
--
-- If the argument to 'realloc' is 'nullPtr', 'realloc' behaves like
-- 'malloc'.
--
realloc :: forall a b. Prim b => Ptr a -> IO (Ptr b)
realloc ptr = coerce (F.realloc ptr :: IO (Ptr (PrimStorable b)))
--------------------------------------------------------------------------------
-- [Section: Foreign.Marshal.Array]
-- | Allocate storage for the given number of elements of a storable type
-- (like 'malloc', but for multiple elements).
mallocArray :: forall a. Prim a => Int -> IO (Ptr a)
mallocArray !idx = coerce (F.mallocArray @(PrimStorable a) idx)
-- | Like 'mallocArray', but add an extra position to hold a special
-- termination element.
mallocArray0 :: forall a. Prim a => Int -> IO (Ptr a)
mallocArray0 !idx = coerce (F.mallocArray0 @(PrimStorable a) idx)
-- | Temporarily allocate space for the given number of elements
-- (like 'alloca', but for multiple elements).
allocaArray :: forall a b. Prim a => Int -> (Ptr a -> IO b) -> IO b
allocaArray !idx f = F.allocaArray idx (coerce f :: Ptr (PrimStorable a) -> IO b)
-- | Like 'allocaArray', but add an extra position to hold a special
-- termination element.
allocaArray0 :: forall a b. Prim a => Int -> (Ptr a -> IO b) -> IO b
allocaArray0 !idx f = F.allocaArray0 idx (coerce f :: Ptr (PrimStorable a) -> IO b)
-- | Adjust the size of an array.
reallocArray :: forall a. Prim a => Ptr a -> Int -> IO (Ptr a)
reallocArray !ptr !idx = coerce (F.reallocArray @(PrimStorable a) (coerce ptr) idx)
-- | Adjust the size of an array, including an extra position for the
-- terminating element.
reallocArray0 :: forall a. Prim a => Ptr a -> Int -> IO (Ptr a)
reallocArray0 !ptr !idx = coerce (F.reallocArray0 @(PrimStorable a) (coerce ptr) idx)
-- | Like 'mallocArray', but allocated memory is filled with bytes of value zero.
callocArray :: forall a. Prim a => Int -> IO (Ptr a)
callocArray !idx = coerce (F.callocArray @(PrimStorable a) idx)
-- | Like 'mallocArray0', but allocated memory is filled with bytes of value zero.
callocArray0 :: forall a. Prim a => Int -> IO (Ptr a)
callocArray0 !idx = coerce (F.callocArray0 @(PrimStorable a) idx)
-- | Convert an array of given length into a Haskell 'PrimArray'.
peekArray :: forall a. Prim a => Int -> Ptr a -> IO (PrimArray a)
peekArray !sz@(I# n#) !(Ptr addr#) = do
marr@(MutablePrimArray ba#) <- newPrimArray sz
primitive $ \s0# ->
case (copyAddrToByteArray# addr# ba# 0# (n# *# (sizeOf# @a undefined)) s0#) of
s1# -> (# s1#, () #)
unsafeFreezePrimArray marr
-- | Convert an array terminated by the given terminator into a Haskell
-- 'PrimArray'.
peekArray0 :: forall a. (Prim a, Eq a) => a -> Ptr a -> IO (PrimArray a)
peekArray0 term !ptr = lengthArray0 term ptr >>= \size ->
peekArray size ptr
-- | Write the 'PrimArray' into memory at the given location.
pokeArray :: forall a. Prim a => Ptr a -> PrimArray a -> IO ()
pokeArray !ptr !arr = flip itraversePrimArray_ arr $ \ix atIx ->
pokeElemOff ptr ix atIx
-- | Write the 'PrimArray' into memory and terminate the elements
-- with a given terminating element.
pokeArray0 :: forall a. Prim a => a -> Ptr a -> PrimArray a -> IO ()
pokeArray0 term !ptr !arr =
let !sz = sizeofPrimArray arr
in flip itraversePrimArray_ arr $ \ix atIx ->
if ix == sz
then pokeElemOff ptr ix term
else pokeElemOff ptr ix atIx
-- | Write a 'PrimArray' into a newly allocated, consecutive
-- sequence of primitive values.
newArray :: forall a. Prim a => PrimArray a -> IO (Ptr a)
newArray !arr = do
ptr <- mallocArray (sizeofPrimArray arr)
pokeArray ptr arr
pure ptr
-- | Write a 'PrimArray' into a newly allocated, consecutive
-- sequence of primitive values, where the end is fixed by
-- the given terminating element.
newArray0 :: forall a. Prim a => a -> PrimArray a -> IO (Ptr a)
newArray0 term !arr = do
ptr <- mallocArray0 (sizeofPrimArray arr)
pokeArray0 term ptr arr
pure ptr
-- | Temporarily store a 'PrimArray' in memory.
withArray :: forall a b. Prim a => PrimArray a -> (Ptr a -> IO b) -> IO b
withArray !arr = withArrayLen arr . const
-- | Like 'withArray', but the action is also passed the size
-- of the 'PrimArray'.
withArrayLen :: forall a b. Prim a => PrimArray a -> (Int -> Ptr a -> IO b) -> IO b
withArrayLen !arr f = allocaArray len $ \ptr -> do
pokeArray ptr arr
f len ptr
where
!len = sizeofPrimArray arr
-- | Like 'withArray', but a terminator indicates where the array ends.
withArray0 :: forall a b. Prim a => a -> PrimArray a -> (Ptr a -> IO b) -> IO b
withArray0 term !arr = withArrayLen0 term arr . const
-- | Like 'withArrayLen', but a terminator indicates where the array ends.
withArrayLen0 :: forall a b. Prim a => a -> PrimArray a -> (Int -> Ptr a -> IO b) -> IO b
withArrayLen0 term !arr f = allocaArray0 len $ \ptr -> do
pokeArray0 term ptr arr
f len ptr
where
!len = sizeofPrimArray arr
-- | Return the number of elements in an array, excluding
-- the terminator
lengthArray0 :: forall a. (Prim a, Eq a)
=> a -- ^ terminating element
-> Ptr a
-> IO Int
lengthArray0 term !ptr = go 0
where
go !ix = peekElemOff ptr ix >>= \val ->
if val == term then pure ix else go (ix + 1)
-- | Advance a pointer into an array by the given number of elements.
advancePtr :: forall a. Prim a => Ptr a -> Int -> Ptr a
advancePtr !ptr !ix = ptr `plusPtr` (ix * sizeOf @a undefined)
-- | Copy the given number of elements from the source array
-- into the destination array; the memory regions /may not/ overlap.
copyArray :: forall a. Prim a
=> Ptr a -- ^ destination array
-> Ptr a -- ^ source array
-> Int -- ^ number of elements to copy
-> IO ()
copyArray !dest !src !size = F.copyBytes dest src (size * sizeOf @a undefined)
-- | Copy the given number of elements from the source array
-- into the destination array; the memory regions /may/ overlap.
moveArray :: forall a. Prim a
=> Ptr a -- ^ destination array
-> Ptr a -- ^ source array
-> Int -- ^ number of elements to copy
-> IO ()
moveArray !dest !src !size = F.moveBytes dest src (size * sizeOf @a undefined)
--------------------------------------------------------------------------------
-- [Section: Foreign.Marshal.Utils]
-- | @'with' val f@ executes the computation @f@, passing as argument
-- a pointer to a temporarily allocated block of memory into which
-- @val@ has been marshalled (the combination of 'alloca' and 'poke').
--
-- The memory is freed when @f@ terminates (either normally or via an
-- exception), so the pointer passed to @f@ must /not/ be used after this.
--
with :: forall a b. Prim a => a -> (Ptr a -> IO b) -> IO b
with val f = F.with (PrimStorable val) (coerce f :: Ptr (PrimStorable a) -> IO b)
-- | Allocate a block of memory and marshal a value into it
-- (the combination of 'malloc' and 'poke').
-- The size of the area allocated is determined by the 'Data.Primitive.Types.sizeOf'
-- method from the instance of 'Storable' for the appropriate type.
--
-- The memory may be deallocated using 'free' or
-- 'finalizerFree' when no longer required.
--
new :: forall a. Prim a => a -> IO (Ptr a)
new val = coerce (F.new (PrimStorable val))
--------------------------------------------------------------------------------