22{-# LANGUAGE GADTs #-}
33{-# LANGUAGE LinearTypes #-}
44{-# LANGUAGE NoImplicitPrelude #-}
5+ {-# LANGUAGE LambdaCase #-}
56{-# LANGUAGE MagicHash #-}
67{-# LANGUAGE ScopedTypeVariables #-}
78{-# LANGUAGE StrictData #-}
1112-- | Mutable vectors with a linear API.
1213--
1314-- Vectors are arrays that grow automatically, that you can append to with
14- -- 'snoc'.
15+ -- 'push'. They never shrink automatically to reduce unnecessary copying,
16+ -- use 'shrinkToFit' to get rid of the wasted space.
1517--
1618-- To use mutable vectors, create a linear computation of type
1719-- @Vector a #-> Ur b@ and feed it to 'constant' or 'fromList'.
@@ -50,11 +52,15 @@ module Data.Vector.Mutable.Linear
5052 -- * Mutators
5153 write ,
5254 unsafeWrite ,
53- snoc ,
55+ push ,
56+ pop ,
57+ slice ,
58+ shrinkToFit ,
5459 -- * Accessors
5560 read ,
5661 unsafeRead ,
5762 size ,
63+ capacity ,
5864 toList ,
5965 )
6066where
@@ -64,6 +70,15 @@ import Prelude.Linear hiding (read)
6470import Data.Array.Mutable.Linear (Array )
6571import qualified Data.Array.Mutable.Linear as Array
6672
73+ -- # Constants
74+ -------------------------------------------------------------------------------
75+
76+ -- | When growing the vector, capacity will be multiplied by this number.
77+ --
78+ -- This is usually chosen between 1.5 and 2; 2 being the most common.
79+ constGrowthFactor :: Int
80+ constGrowthFactor = 2
81+
6782-- # Core data types
6883-------------------------------------------------------------------------------
6984
@@ -81,6 +96,8 @@ data Vector a where
8196
8297-- | Create a 'Vector' from an 'Array'. Result will have the size and capacity
8398-- equal to the size of the given array.
99+ --
100+ -- This is a constant time operation.
84101fromArray :: HasCallStack => Array a #-> Vector a
85102fromArray arr =
86103 Array. size arr
@@ -104,24 +121,44 @@ constant size' x f
104121fromList :: HasCallStack => [a ] -> (Vector a #-> Ur b ) #-> Ur b
105122fromList xs f = Array. fromList xs (f . fromArray)
106123
107- -- | Number of elements inside the vector
124+ -- | Number of elements inside the vector.
125+ --
126+ -- This might be different than how much actual memory the vector is using.
127+ -- For that, see: 'capacity'.
108128size :: Vector a #-> (Vector a , Ur Int )
109129size (Vec size' arr) = (Vec size' arr, Ur size')
110130
111- -- | Insert at the end of the vector
112- snoc :: HasCallStack => Vector a #-> a -> Vector a
113- snoc (Vec size' arr) x =
114- Array. size arr & \ (arr', Ur cap) ->
115- if size' < cap
116- then write (Vec (size' + 1 ) arr') size' x
117- else write (unsafeResize ((max size' 1 ) * 2 ) (Vec (size' + 1 ) arr')) size' x
131+ -- | Capacity of a vector. In other words, the number of elements
132+ -- the vector can contain before it is copied to a bigger array.
133+ capacity :: Vector a #-> (Vector a , Ur Int )
134+ capacity (Vec s arr) =
135+ Array. size arr & \ (arr', cap) -> (Vec s arr', cap)
136+
137+ -- | Insert at the end of the vector. This will grow the vector if there
138+ -- is no empty space.
139+ push :: Vector a #-> a -> Vector a
140+ push vec x =
141+ growToFit 1 vec & \ (Vec s arr) ->
142+ write (Vec (s + 1 ) arr) s x
143+
144+ -- | Pop from the end of the vector. This will never shrink the vector, use
145+ -- 'shrinkToFit' to remove the wasted space.
146+ pop :: Vector a #-> (Vector a , Ur (Maybe a ))
147+ pop vec =
148+ size vec & \ case
149+ (vec', Ur 0 ) ->
150+ (vec', Ur Nothing )
151+ (vec', Ur s) ->
152+ read vec' (s- 1 ) & \ (Vec _ arr, Ur a) ->
153+ ( Vec (s- 1 ) arr
154+ , Ur (Just a)
155+ )
118156
119157-- | Write to an element . Note: this will not write to elements beyond the
120158-- current size of the vector and will error instead.
121159write :: HasCallStack => Vector a #-> Int -> a -> Vector a
122- write (Vec size' arr) ix val
123- | indexInRange size' ix = Vec size' (Array. unsafeWrite arr ix val)
124- | otherwise = arr `lseq` error " Write index not in range."
160+ write vec ix val =
161+ unsafeWrite (assertIndexInRange ix vec) ix val
125162
126163-- | Same as 'write', but does not do bounds-checking. The behaviour is undefined
127164-- when passed an invalid index.
@@ -132,11 +169,8 @@ unsafeWrite (Vec size' arr) ix val =
132169-- | Read from a vector, with an in-range index and error for an index that is
133170-- out of range (with the usual range @0..size-1@).
134171read :: HasCallStack => Vector a #-> Int -> (Vector a , Ur a )
135- read (Vec size' arr) ix
136- | indexInRange size' ix =
137- Array. unsafeRead arr ix
138- & \ (arr', val) -> (Vec size' arr', val)
139- | otherwise = arr `lseq` error " Read index not in range."
172+ read vec ix =
173+ unsafeRead (assertIndexInRange ix vec) ix
140174
141175-- | Same as 'read', but does not do bounds-checking. The behaviour is undefined
142176-- when passed an invalid index.
@@ -153,6 +187,32 @@ toList (Vec s arr) =
153187 Array. toList arr & \ (Ur xs) ->
154188 Ur (take s xs)
155189
190+ -- | Resize the vector to not have any wasted memory (size == capacity). This
191+ -- returns a semantically identical vector.
192+ shrinkToFit :: Vector a #-> Vector a
193+ shrinkToFit vec =
194+ capacity vec & \ (vec', Ur cap) ->
195+ size vec' & \ (vec'', Ur s') ->
196+ if cap > s'
197+ then unsafeResize s' vec''
198+ else vec''
199+
200+ -- | Return a slice of the vector with given size, starting from an offset.
201+ --
202+ -- Start offset + target size should be within the input vector, and both should
203+ -- be non-negative.
204+ --
205+ -- This is a constant time operation if the start offset is 0. Use 'shrinkToFit'
206+ -- to remove the possible wasted space if necessary.
207+ slice :: Int -> Int -> Vector a #-> Vector a
208+ slice from newSize (Vec oldSize arr) =
209+ if oldSize < from + newSize
210+ then arr `lseq` error " Slice index out of bounds"
211+ else if from == 0
212+ then Vec newSize arr
213+ else Array. slice from newSize arr & \ (oldArr, newArr) ->
214+ oldArr `lseq` fromArray newArr
215+
156216-- # Instances
157217-------------------------------------------------------------------------------
158218
@@ -162,6 +222,29 @@ instance Consumable (Vector a) where
162222-- # Internal library
163223-------------------------------------------------------------------------------
164224
225+ -- | Grows the vector to the closest power of growthFactor to
226+ -- fit at least n more elements.
227+ growToFit :: HasCallStack => Int -> Vector a #-> Vector a
228+ growToFit n vec =
229+ capacity vec & \ (vec', Ur cap) ->
230+ size vec' & \ (vec'', Ur s') ->
231+ if s' + n <= cap
232+ then vec''
233+ else
234+ let -- Calculate the closest power of growth factor
235+ -- larger than required size.
236+ newSize =
237+ constGrowthFactor -- This constant is defined above.
238+ ^ (ceiling :: Double -> Int )
239+ (logBase
240+ (fromIntegral constGrowthFactor)
241+ (fromIntegral (s' + n))) -- this is always
242+ -- > 0 because of
243+ -- the if condition
244+ in unsafeResize
245+ newSize
246+ vec''
247+
165248-- | Resize the vector to a non-negative size. In-range elements are preserved,
166249-- the possible new elements are bottoms.
167250unsafeResize :: HasCallStack => Int -> Vector a #-> Vector a
@@ -174,6 +257,10 @@ unsafeResize newSize (Vec size' ma) =
174257 ma
175258 )
176259
177- -- | Argument order: indexInRange size ix
178- indexInRange :: Int -> Int -> Bool
179- indexInRange size' ix = 0 <= ix && ix < size'
260+ -- | Check if given index is within the Vector, otherwise panic.
261+ assertIndexInRange :: HasCallStack => Int -> Vector a #-> Vector a
262+ assertIndexInRange i vec =
263+ size vec & \ (vec', Ur s) ->
264+ if 0 <= i && i < s
265+ then vec'
266+ else vec' `lseq` error " Vector: index out of bounds"
0 commit comments