-
Notifications
You must be signed in to change notification settings - Fork 1
/
RandomAccessList.fs
402 lines (311 loc) · 15.4 KB
/
RandomAccessList.fs
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
namespace Yaaf.FSharp.Collections
module internal Literals2 =
[<Literal>]
let internal blockSizeShift = 5 // TODO: what can we do in 64Bit case?
[<Literal>]
let internal blockSize = 32
[<Literal>]
let internal blockIndexMask = 0x01f
open System.Threading
#if FX_NO_THREAD
#else
type NodeR(thread,array:obj[]) =
let thread = thread
new() = NodeR(ref null,Array.create Literals2.blockSize null)
static member InCurrentThread() = NodeR(ref Thread.CurrentThread,Array.create Literals2.blockSize null)
member this.Array = array
member this.Thread = thread
member this.SetThread t = thread := t
type internal TransientVect<'T> (count,shift:int,root:NodeR,tail:obj[]) =
let mutable count = count
let mutable root = root
let mutable tail = tail
let mutable shift = shift
new() = TransientVect<'T>(0,Literals2.blockSizeShift,NodeR.InCurrentThread(),Array.create Literals2.blockSize null)
member internal this.EnsureEditable(node:NodeR) =
if node.Thread = root.Thread then node else
NodeR(root.Thread,Array.copy node.Array)
member internal this.NewPath(level,node:NodeR) =
if level = 0 then node else
let ret = Array.create Literals2.blockSize null
ret.[0] <- this.NewPath(level - Literals2.blockSizeShift,node) :> obj
NodeR(node.Thread,ret)
member internal this.PushTail(level,parent:NodeR,tailnode) =
//if parent is leaf, insert node,
// else does it map to an existing child? -> nodeToInsert = pushNode one more level
// else alloc new path
//return nodeToInsert placed in copy of parent
let parent = this.EnsureEditable parent
let subidx = ((count - 1) >>> level) &&& Literals2.blockIndexMask
let ret = parent
let nodeToInsert =
if level = Literals2.blockSizeShift then tailnode else
let child = parent.Array.[subidx]
if child <> null then
this.PushTail(level-Literals2.blockSizeShift,child :?> NodeR,tailnode)
else
this.NewPath(level-Literals2.blockSizeShift,tailnode)
ret.Array.[subidx] <- nodeToInsert :> obj
ret
member internal this.ArrayFor i =
if i >= 0 && i < count then
if i >= this.TailOff() then tail else
let mutable node = root
let mutable level = shift
while level > 0 do
let pos = (i >>> level) &&& Literals2.blockIndexMask
node <- node.Array.[pos] :?> NodeR
level <- level - Literals2.blockSizeShift
node.Array
else raise (new System.IndexOutOfRangeException())
member this.conj<'T> (x:'T) =
this.EnsureEditable()
//room in tail?
if count - this.TailOff() < Literals2.blockSize then
tail.[count &&& Literals2.blockIndexMask] <- x :> obj
else
//full tail, push into tree
let tailNode = NodeR(root.Thread,tail)
let newShift = shift
let newTail = Array.create Literals2.blockSize null
newTail.[0] <- x :> obj
//overflow root?
let newRoot =
if (count >>> Literals2.blockSizeShift) > (1 <<< shift) then
let newRoot = NodeR(root.Thread,Array.create Literals2.blockSize null)
newRoot.Array.[0] <- root :> obj
newRoot.Array.[1] <- this.NewPath(shift,tailNode) :> obj
shift <- shift + Literals2.blockSizeShift
newRoot
else
this.PushTail(shift,root,tailNode)
tail <- newTail
root <- newRoot
count <- count + 1
this
member this.persistent() : RandomAccessList<'T> =
this.EnsureEditable()
root.SetThread null
let l = count - this.TailOff()
let trimmedTail = Array.init l (fun i -> tail.[i])
RandomAccessList(count, shift, root, trimmedTail)
member internal this.EnsureEditable() =
if !root.Thread = Thread.CurrentThread then () else
if !root.Thread <> null then
failwith "Transient used by non-owner thread"
failwith "Transient used after persistent! call"
member internal this.TailOff() =
if count < Literals2.blockSize then 0 else
((count - 1) >>> Literals2.blockSizeShift) <<< Literals2.blockSizeShift
and RandomAccessList<'T> (count,shift:int,root:NodeR,tail:obj[]) =
let hashCode = ref None
let tailOff =
if count < Literals2.blockSize then 0 else
((count - 1) >>> Literals2.blockSizeShift) <<< Literals2.blockSizeShift
static member Empty() : RandomAccessList<'T> = RandomAccessList<'T>(0,Literals2.blockSizeShift,NodeR(),[||])
static member ofSeq(items:'T seq) =
let mutable ret = TransientVect()
for item in (items |> List.ofSeq |> List.rev |> Seq.ofList) do
ret <- ret.conj item
ret.persistent()
override this.GetHashCode() =
match !hashCode with
| None ->
let mutable hash = 1
for x in this.rangedIterator(0,count) do
hash <- 31 * hash + Unchecked.hash x
hashCode := Some hash
hash
| Some hash -> hash
override this.Equals(other) =
match other with
| :? RandomAccessList<'T> as y ->
if this.Length <> y.Length then false else
if this.GetHashCode() <> y.GetHashCode() then false else
Seq.forall2 (Unchecked.equals) this y
| _ -> false
member internal this.SetHash hash = hashCode := hash; this
member internal this.NewPath(level,node:NodeR) =
if level = 0 then node else
let ret = NodeR(root.Thread,Array.create Literals2.blockSize null)
ret.Array.[0] <- this.NewPath(level - Literals2.blockSizeShift,node) :> obj
ret
member internal this.PushTail(level,parent:NodeR,tailnode) =
//if parent is leaf, insert node,
// else does it map to an existing child? -> nodeToInsert = pushNode one more level
// else alloc new path
//return nodeToInsert placed in copy of parent
let subidx = ((count - 1) >>> level) &&& Literals2.blockIndexMask
let ret = NodeR(parent.Thread,Array.copy parent.Array)
let nodeToInsert =
if level = Literals2.blockSizeShift then tailnode else
let child = parent.Array.[subidx]
if child <> null then
this.PushTail(level-Literals2.blockSizeShift,child :?> NodeR,tailnode)
else
this.NewPath(level-Literals2.blockSizeShift,tailnode)
ret.Array.[subidx] <- nodeToInsert :> obj
ret
member internal this.ArrayFor i =
if i >= 0 && i < count then
if i >= tailOff then tail else
let mutable node = root
let mutable level = shift
while level > 0 do
let pos = (i >>> level) &&& Literals2.blockIndexMask
node <- node.Array.[pos] :?> NodeR
level <- level - Literals2.blockSizeShift
node.Array
else raise (System.IndexOutOfRangeException())
member internal this.doAssoc(level,node:NodeR,i,x) =
let ret = NodeR(root.Thread,Array.copy node.Array)
if level = 0 then
ret.Array.[i &&& Literals2.blockIndexMask] <- x :> obj
else
let subidx = (i >>> level) &&& Literals2.blockIndexMask
ret.Array.[subidx] <- this.doAssoc(level - Literals2.blockSizeShift, node.Array.[subidx] :?> NodeR, i, x) :> obj
ret
member internal this.PopTail(level,node:NodeR) : NodeR =
let subidx = ((count-2) >>> level) &&& Literals2.blockIndexMask
if level > Literals2.blockSizeShift then
let newchild = this.PopTail(level - Literals2.blockSizeShift, node.Array.[subidx] :?> NodeR)
if newchild = Unchecked.defaultof<NodeR> && subidx = 0 then Unchecked.defaultof<NodeR> else
let ret = NodeR(root.Thread, Array.copy node.Array);
ret.Array.[subidx] <- newchild :> obj
ret
elif subidx = 0 then Unchecked.defaultof<NodeR> else
let ret = new NodeR(root.Thread, Array.copy node.Array)
ret.Array.[subidx] <- null
ret
member this.rangedIterator<'T>(startIndex,endIndex) : 'T seq =
if count = 0 then Seq.empty
else
let i = ref (endIndex - 1)
let array = if (endIndex - 1) < count then ref (this.ArrayFor !i) else ref null
seq {
while !i > (startIndex - 1) do
if (!i + 1) % Literals2.blockSize = 0 then
array := this.ArrayFor !i
yield (!array).[!i &&& Literals2.blockIndexMask] :?> 'T
i := !i - 1
}
member this.Cons (x : 'T) =
if count - tailOff < Literals2.blockSize then
let newTail = Array.append tail [|x:>obj|]
RandomAccessList<'T>(count + 1,shift,root,newTail)
else
//full tail, push into tree
let tailNode = NodeR(root.Thread,tail)
let newShift = shift
//overflow root?
if (count >>> Literals2.blockSizeShift) > (1 <<< shift) then
let newRoot = NodeR()
newRoot.Array.[0] <- root :> obj
newRoot.Array.[1] <- this.NewPath(shift,tailNode) :> obj
RandomAccessList<'T>(count + 1,shift + Literals2.blockSizeShift,newRoot,[| x |])
else
let newRoot = this.PushTail(shift,root,tailNode)
RandomAccessList<'T>(count + 1,shift,newRoot,[| x |])
member this.IsEmpty = (count = 0)
member this.Item
with get i =
let k = (count - 1) - i
let node = this.ArrayFor k
node.[k &&& Literals2.blockIndexMask] :?> 'T
member this.Head = if count > 0 then this.[0] else failwith "Can't peek empty randomAccessList"
member this.Rev() =
if count = 0 then RandomAccessList.Empty() :> RandomAccessList<'T>
else
let mutable ret = TransientVect()
for item in this.rangedIterator(0,count) do
ret <- ret.conj item
ret.persistent()
member this.TryHead = if count > 0 then Some (this.[0]) else None
member this.Length : int = count
member this.Tail =
if count = 0 then failwith "Can't tail empty randomAccessList" else
if count = 1 then RandomAccessList<'T>.Empty() else
if count - tailOff > 1 then
let mutable newroot = NodeR(ref Thread.CurrentThread, root.Array.Clone() :?> obj[])
let mutable ret = TransientVect(count - 1, shift, newroot, tail.[0..(tail.Length-1)])
ret.persistent()
else
let newtail = this.ArrayFor(count - 2)
let mutable newroot = this.PopTail(shift, root)
let mutable newshift = shift
if newroot = Unchecked.defaultof<NodeR> then
newroot <- NodeR()
if shift > Literals2.blockSizeShift && newroot.Array.[1] = null then
newroot <- newroot.Array.[0] :?> NodeR
newshift <- newshift - Literals2.blockSizeShift
RandomAccessList(count - 1, newshift, newroot, newtail)
member this.TryTail = if count = 0 then None else Some(this.Tail)
member this.Uncons = if count > 0 then this.[0], this.Tail else failwith "Can't peek empty randomAccessList"
member this.TryUncons = if count > 0 then Some(this.[0], this.Tail) else None
member this.Update(i, x : 'T) =
let k = (count - 1) - i
if k >= 0 && k < count then
if k >= tailOff then
let newTail = Array.copy tail
newTail.[k &&& Literals2.blockIndexMask] <- x :> obj
RandomAccessList(count, shift, root, newTail)
else
RandomAccessList(count, shift, this.doAssoc(shift, root, k, x),tail)
elif k = count then this.Cons x
else raise (new System.IndexOutOfRangeException())
member this.TryUpdate(i, x : 'T) =
if i >= 0 && i < count then Some(this.Update (i,x))
else None
interface System.Collections.Generic.IEnumerable<'T> with
member this.GetEnumerator () =
this.rangedIterator(0,count).GetEnumerator()
interface System.Collections.IEnumerable with
member this.GetEnumerator () =
(this.rangedIterator(0,count).GetEnumerator())
:> System.Collections.IEnumerator
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module RandomAccessList =
//pattern discriminators (active pattern)
let (|Cons|Nil|) (v : RandomAccessList<'T>) = match v.TryUncons with Some(a,b) -> Cons(a,b) | None -> Nil
let inline cons (x : 'T) (randomAccessList : 'T RandomAccessList) = randomAccessList.Cons x
let empty<'T> = RandomAccessList.Empty() :> RandomAccessList<'T>
let inline fold (f : ('State -> 'T -> 'State)) (state : 'State) (v : RandomAccessList<'T>) =
let rec loop state' (v' : RandomAccessList<'T>) count =
match count with
| _ when count = v'.Length -> state'
| _ -> loop (f state' v'.[count]) v' (count + 1)
loop state v 0
let inline foldBack (f : ('T -> 'State -> 'State)) (v : RandomAccessList<'T>) (state : 'State) =
let rec loop state' (v' : RandomAccessList<'T>) count =
match count with
| -1 -> state'
| _ -> loop (f v'.[count] state') v' (count - 1)
loop state v (v.Length - 1)
let init count (f: int -> 'T) : 'T RandomAccessList =
let mutable ret = TransientVect()
for i in 0..(count-1) do
ret <- ret.conj(f i)
ret.persistent().Rev()
let inline isEmpty (randomAccessList :'T RandomAccessList) = randomAccessList.IsEmpty
let inline head (randomAccessList :'T RandomAccessList) = randomAccessList.Head
let inline tryHead (randomAccessList :'T RandomAccessList) = randomAccessList.TryHead
let inline length (randomAccessList :'T RandomAccessList) : int = randomAccessList.Length
let map (f : 'T -> 'T1) (randomAccessList :'T RandomAccessList) : 'T1 RandomAccessList =
let mutable ret = TransientVect()
for item in randomAccessList do
ret <- ret.conj(f item)
ret.persistent().Rev()
let inline nth i (randomAccessList :'T RandomAccessList) = randomAccessList.[i]
let inline tryNth i (randomAccessList :'T RandomAccessList) =
if i >= 0 && i < randomAccessList.Length then Some(randomAccessList.[i])
else None
let ofSeq (items : 'T seq) = RandomAccessList.ofSeq items
let inline rev (randomAccessList :'T RandomAccessList) = randomAccessList.Rev()
let inline tail (randomAccessList :'T RandomAccessList) = randomAccessList.Tail
let inline tryTail (randomAccessList :'T RandomAccessList) = randomAccessList.TryTail
let inline toSeq (randomAccessList: 'T RandomAccessList) = randomAccessList :> seq<'T>
let inline uncons (randomAccessList :'T RandomAccessList) = randomAccessList.Uncons
let inline tryUncons (randomAccessList :'T RandomAccessList) = randomAccessList.TryUncons
let inline update i (x : 'T) (randomAccessList : 'T RandomAccessList) = randomAccessList.Update(i, x)
let inline tryUpdate i (x : 'T) (randomAccessList : 'T RandomAccessList) = randomAccessList.TryUpdate(i, x)
#endif