summaryrefslogtreecommitdiff
path: root/src/ImmutGrowVector.hs
blob: d36209d31b6f141837854b6b757bb0ac668123bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
module ImmutGrowVector where

import Foreign.Storable
import Data.Vector.Storable qualified as VS


-- | Acts like an immutable storable vector, except that it's split in a long
-- prefix and a short suffix so that modifications at the end are cheap. If the
-- suffix gets longer (by appending elements), the suffix elements are promoted
-- to prefix elements once in a while, resulting in a big copy at those times.
data ImmutGrowVector a = ImmutGrowVector (VS.Vector a) (VS.Vector a)
  deriving (Show)

empty :: Storable a => ImmutGrowVector a
empty = ImmutGrowVector VS.empty VS.empty

fromListN :: Storable a => Int -> [a] -> ImmutGrowVector a
fromListN n l
  | n > 2 =
      let (l1, l2) = splitAt (n - 2) l
      in ImmutGrowVector (VS.fromListN (n - 2) l1) (VS.fromListN 2 l2)
  | otherwise =
      ImmutGrowVector VS.empty (VS.fromListN n l)

(!) :: Storable a => ImmutGrowVector a -> Int -> a
ImmutGrowVector prefix suffix ! i
  | i < VS.length prefix = prefix VS.! i
  | otherwise = suffix VS.! (i - VS.length prefix)

length :: Storable a => ImmutGrowVector a -> Int
length (ImmutGrowVector prefix suffix) = VS.length prefix + VS.length suffix

set :: Storable a => ImmutGrowVector a -> Int -> a -> ImmutGrowVector a
set (ImmutGrowVector prefix suffix) idx value
  | idx < VS.length prefix = error "ImmutGrowVector: mutation in slow part"
  | otherwise = ImmutGrowVector prefix (suffix VS.// [(idx - VS.length prefix, value)])

append :: Storable a => ImmutGrowVector a -> a -> ImmutGrowVector a
append (ImmutGrowVector prefix suffix) value
  | VS.length suffix < 8 = ImmutGrowVector prefix (suffix `VS.snoc` value)
  | otherwise =
      let n = VS.length suffix
      in ImmutGrowVector (prefix <> VS.take (n - 1) suffix) (VS.drop (n - 1) suffix `VS.snoc` value)