Drop NFData constraint from compact.
[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 some internal functions and representation for dealing
17 -- with compact regions, which may be of interest if you need higher
18 -- performance.
19 --
20 -- /Since: 1.0.0/
21
22 module Data.Compact.Internal
23 ( Compact(..)
24 , mkCompact
25 , compactSized
26 ) where
27
28 import Control.Concurrent.MVar
29 import GHC.Prim
30 import GHC.Types
31
32 -- | A 'Compact' contains fully evaluated, pure, immutable data.
33 --
34 -- 'Compact' serves two purposes:
35 --
36 -- * Data stored in a 'Compact' has no garbage collection overhead.
37 -- The garbage collector considers the whole 'Compact' to be alive
38 -- if there is a reference to any object within it.
39 --
40 -- * A 'Compact' can be serialized, stored, and deserialized again.
41 -- The serialized data can only be deserialized by the exact binary
42 -- that created it, but it can be stored indefinitely before
43 -- deserialization.
44 --
45 -- Compacts are self-contained, so compacting data involves copying
46 -- it; if you have data that lives in two 'Compact's, each will have a
47 -- separate copy of the data.
48 --
49 -- The cost of compaction is similar to the cost of GC for the same
50 -- data, but it is perfomed only once. However, because
51 -- "Data.Compact.compact" does not stop-the-world, retaining internal
52 -- sharing during the compaction process is very costly. The user
53 -- can choose wether to 'compact' or 'compactWithSharing'.
54 --
55 -- When you have a @'Compact' a@, you can get a pointer to the actual object
56 -- in the region using "Data.Compact.getCompact". The 'Compact' type
57 -- serves as handle on the region itself; you can use this handle
58 -- to add data to a specific 'Compact' with 'compactAdd' or
59 -- 'compactAddWithSharing' (giving you a new handle which corresponds
60 -- to the same compact region, but points to the newly added object
61 -- in the region). At the moment, due to technical reasons,
62 -- it's not possible to get the @'Compact' a@ if you only have an @a@,
63 -- so make sure you hold on to the handle as necessary.
64 --
65 -- Data in a compact doesn't ever move, so compacting data is also a
66 -- way to pin arbitrary data structures in memory.
67 --
68 -- There are some limitations on what can be compacted:
69 --
70 -- * Functions. Compaction only applies to data.
71 --
72 -- * Pinned 'ByteArray#' objects cannot be compacted. This is for a
73 -- good reason: the memory is pinned so that it can be referenced by
74 -- address (the address might be stored in a C data structure, for
75 -- example), so we can't make a copy of it to store in the 'Compact'.
76 --
77 -- * Objects with mutable pointer fields also cannot be compacted,
78 -- because subsequent mutation would destroy the property that a compact is
79 -- self-contained.
80 --
81 -- If compaction encounters any of the above, a 'CompactionFailed'
82 -- exception will be thrown by the compaction operation.
83 --
84 data Compact a = Compact Compact# a (MVar ())
85 -- we can *read* from a Compact without taking a lock, but only
86 -- one thread can be writing to the compact at any given time.
87 -- The MVar here is to enforce mutual exclusion among writers.
88 -- Note: the MVar protects the Compact# only, not the pure value 'a'
89
90 -- | Make a new 'Compact' object, given a pointer to the true
91 -- underlying region. You must uphold the invariant that @a@ lives
92 -- in the compact region.
93 --
94 mkCompact
95 :: Compact# -> a -> State# RealWorld -> (# State# RealWorld, Compact a #)
96 mkCompact compact# a s =
97 case unIO (newMVar ()) s of { (# s1, lock #) ->
98 (# s1, Compact compact# a lock #) }
99 where
100 unIO (IO a) = a
101
102 -- | Transfer @a@ into a new compact region, with a preallocated size,
103 -- possibly preserving sharing or not. If you know how big the data
104 -- structure in question is, you can save time by picking an appropriate
105 -- block size for the compact region.
106 --
107 compactSized :: Int -> Bool -> a -> IO (Compact a)
108 compactSized (I# size) share a = IO $ \s0 ->
109 case compactNew# (int2Word# size) s0 of { (# s1, compact# #) ->
110 case compactAddPrim compact# a s1 of { (# s2, pk #) ->
111 mkCompact compact# pk s2 }}
112 where
113 compactAddPrim
114 | share = compactAddWithSharing#
115 | otherwise = compactAdd#