|
1
|
|
-{-# LANGUAGE BangPatterns, MagicHash, UnboxedTuples, FlexibleContexts #-}
|
|
2
|
|
--- | Efficient serialisation for GHCi Instruction arrays
|
|
3
|
|
---
|
|
4
|
|
--- Author: Ben Gamari
|
|
5
|
|
---
|
|
6
|
|
-module GHCi.BinaryArray(putArray, getArray) where
|
|
7
|
|
-
|
|
8
|
|
-import Prelude
|
|
9
|
|
-import Foreign.Ptr
|
|
10
|
|
-import Data.Binary
|
|
11
|
|
-import Data.Binary.Put (putBuilder)
|
|
12
|
|
-import qualified Data.Binary.Get.Internal as Binary
|
|
13
|
|
-import qualified Data.ByteString.Builder as BB
|
|
14
|
|
-import qualified Data.ByteString.Builder.Internal as BB
|
|
15
|
|
-import qualified Data.Array.Base as A
|
|
16
|
|
-import qualified Data.Array.IO.Internals as A
|
|
17
|
|
-import qualified Data.Array.Unboxed as A
|
|
18
|
|
-import GHC.Exts
|
|
19
|
|
-import GHC.IO
|
|
20
|
|
-
|
|
21
|
|
--- | An efficient serialiser of 'A.UArray'.
|
|
22
|
|
-putArray :: Binary i => A.UArray i a -> Put
|
|
23
|
|
-putArray (A.UArray l u _ arr#) = do
|
|
24
|
|
- put l
|
|
25
|
|
- put u
|
|
26
|
|
- putBuilder $ byteArrayBuilder arr#
|
|
27
|
|
-
|
|
28
|
|
-byteArrayBuilder :: ByteArray# -> BB.Builder
|
|
29
|
|
-byteArrayBuilder arr# = BB.builder $ go 0 (I# (sizeofByteArray# arr#))
|
|
30
|
|
- where
|
|
31
|
|
- go :: Int -> Int -> BB.BuildStep a -> BB.BuildStep a
|
|
32
|
|
- go !inStart !inEnd k (BB.BufferRange outStart outEnd)
|
|
33
|
|
- -- There is enough room in this output buffer to write all remaining array
|
|
34
|
|
- -- contents
|
|
35
|
|
- | inRemaining <= outRemaining = do
|
|
36
|
|
- copyByteArrayToAddr arr# inStart outStart inRemaining
|
|
37
|
|
- k (BB.BufferRange (outStart `plusPtr` inRemaining) outEnd)
|
|
38
|
|
- -- There is only enough space for a fraction of the remaining contents
|
|
39
|
|
- | otherwise = do
|
|
40
|
|
- copyByteArrayToAddr arr# inStart outStart outRemaining
|
|
41
|
|
- let !inStart' = inStart + outRemaining
|
|
42
|
|
- return $! BB.bufferFull 1 outEnd (go inStart' inEnd k)
|
|
43
|
|
- where
|
|
44
|
|
- inRemaining = inEnd - inStart
|
|
45
|
|
- outRemaining = outEnd `minusPtr` outStart
|
|
46
|
|
-
|
|
47
|
|
- copyByteArrayToAddr :: ByteArray# -> Int -> Ptr a -> Int -> IO ()
|
|
48
|
|
- copyByteArrayToAddr src# (I# src_off#) (Ptr dst#) (I# len#) =
|
|
49
|
|
- IO $ \s -> case copyByteArrayToAddr# src# src_off# dst# len# s of
|
|
50
|
|
- s' -> (# s', () #)
|
|
51
|
|
-
|
|
52
|
|
--- | An efficient deserialiser of 'A.UArray'.
|
|
53
|
|
-getArray :: (Binary i, A.Ix i, A.MArray A.IOUArray a IO) => Get (A.UArray i a)
|
|
54
|
|
-getArray = do
|
|
55
|
|
- l <- get
|
|
56
|
|
- u <- get
|
|
57
|
|
- arr@(A.IOUArray (A.STUArray _ _ _ arr#)) <-
|
|
58
|
|
- return $ unsafeDupablePerformIO $ A.newArray_ (l,u)
|
|
59
|
|
- let go 0 _ = return ()
|
|
60
|
|
- go !remaining !off = do
|
|
61
|
|
- Binary.readNWith n $ \ptr ->
|
|
62
|
|
- copyAddrToByteArray ptr arr# off n
|
|
63
|
|
- go (remaining - n) (off + n)
|
|
64
|
|
- where n = min chunkSize remaining
|
|
65
|
|
- sz <- return $ unsafeDupablePerformIO $ IO $ \s -> case getSizeofMutableByteArray# arr# s of
|
|
66
|
|
- (# s2, n #) -> (# s2, I# n #)
|
|
67
|
|
- go sz 0
|
|
68
|
|
- return $! unsafeDupablePerformIO $ unsafeFreezeIOUArray arr
|
|
69
|
|
- where
|
|
70
|
|
- chunkSize = 10*1024
|
|
71
|
|
-
|
|
72
|
|
- copyAddrToByteArray :: Ptr a -> MutableByteArray# RealWorld
|
|
73
|
|
- -> Int -> Int -> IO ()
|
|
74
|
|
- copyAddrToByteArray (Ptr src#) dst# (I# dst_off#) (I# len#) =
|
|
75
|
|
- IO $ \s -> case copyAddrToByteArray# src# dst# dst_off# len# s of
|
|
76
|
|
- s' -> (# s', () #)
|
|
77
|
|
-
|
|
78
|
|
--- this is inexplicably not exported in currently released array versions
|
|
79
|
|
-unsafeFreezeIOUArray :: A.IOUArray ix e -> IO (A.UArray ix e)
|
|
80
|
|
-unsafeFreezeIOUArray (A.IOUArray marr) = stToIO (A.unsafeFreezeSTUArray marr) |