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