2780d19b1a1592040d649738ed743828752967b0
[ghc.git] / libraries / compact / Data / Compact / Internal.hs
1 {-# OPTIONS_GHC -fno-warn-name-shadowing #-}
2 {-# LANGUAGE MagicHash #-}
3 {-# LANGUAGE UnboxedTuples #-}
4
5 -----------------------------------------------------------------------------
6 -- |
7 -- Module : Data.Compact.Internal
8 -- Copyright : (c) The University of Glasgow 2001-2009
9 -- (c) Giovanni Campagna <gcampagn@cs.stanford.edu> 2015
10 -- License : BSD-style (see the file LICENSE)
11 --
12 -- Maintainer : libraries@haskell.org
13 -- Stability : unstable
14 -- Portability : non-portable (GHC Extensions)
15 --
16 -- This module provides a data structure, called a Compact, for
17 -- holding fully evaluated data in a consecutive block of memory.
18 --
19 -- This is a private implementation detail of the package and should
20 -- not be imported directly.
21 --
22 -- /Since: 1.0.0/
23
24 module Data.Compact.Internal
25 ( Compact(..)
26 , mkCompact
27 , compactSized
28 ) where
29
30 import Control.Concurrent.MVar
31 import Control.DeepSeq
32 import GHC.Prim
33 import GHC.Types
34
35 -- | A 'Compact' contains fully evaluated, pure, immutable data.
36 --
37 -- 'Compact' serves two purposes:
38 --
39 -- * Data stored in a 'Compact' has no garbage collection overhead.
40 -- The garbage collector considers the whole 'Compact' to be alive
41 -- if there is a reference to any object within it.
42 --
43 -- * A 'Compact' can be serialized, stored, and deserialized again.
44 -- The serialized data can only be deserialized by the exact binary
45 -- that created it, but it can be stored indefinitely before
46 -- deserialization.
47 --
48 -- Compacts are self-contained, so compacting data involves copying
49 -- it; if you have data that lives in two 'Compact's, each will have a
50 -- separate copy of the data.
51 --
52 -- The cost of compaction is similar to the cost of GC for the same
53 -- data, but it is perfomed only once. However, retainining internal
54 -- sharing during the compaction process is very costly, so it is
55 -- optional; there are two ways to create a 'Compact': 'compact' and
56 -- 'compactWithSharing'.
57 --
58 -- Data can be added to an existing 'Compact' with 'compactAdd' or
59 -- 'compactAddWithSharing'.
60 --
61 -- Data in a compact doesn't ever move, so compacting data is also a
62 -- way to pin arbitrary data structures in memory.
63 --
64 -- There are some limitations on what can be compacted:
65 --
66 -- * Functions. Compaction only applies to data.
67 --
68 -- * Pinned 'ByteArray#' objects cannot be compacted. This is for a
69 -- good reason: the memory is pinned so that it can be referenced by
70 -- address (the address might be stored in a C data structure, for
71 -- example), so we can't make a copy of it to store in the 'Compact'.
72 --
73 -- * Mutable objects also cannot be compacted, because subsequent
74 -- mutation would destroy the property that a compact is
75 -- self-contained.
76 --
77 -- If compaction encounters any of the above, a 'CompactionFailed'
78 -- exception will be thrown by the compaction operation.
79 --
80 data Compact a = Compact Compact# a (MVar ())
81 -- we can *read* from a Compact without taking a lock, but only
82 -- one thread can be writing to the compact at any given time.
83 -- The MVar here is to enforce mutual exclusion among writers.
84 -- Note: the MVar protects the Compact# only, not the pure value 'a'
85
86 mkCompact
87 :: Compact# -> a -> State# RealWorld -> (# State# RealWorld, Compact a #)
88 mkCompact compact# a s =
89 case unIO (newMVar ()) s of { (# s1, lock #) ->
90 (# s1, Compact compact# a lock #) }
91 where
92 unIO (IO a) = a
93
94 compactSized :: NFData a => Int -> Bool -> a -> IO (Compact a)
95 compactSized (I# size) share a = IO $ \s0 ->
96 case compactNew# (int2Word# size) s0 of { (# s1, compact# #) ->
97 case compactAddPrim compact# a s1 of { (# s2, pk #) ->
98 mkCompact compact# pk s2 }}
99 where
100 compactAddPrim
101 | share = compactAddWithSharing#
102 | otherwise = compactAdd#