12
12
13
13
package java .util .concurrent
14
14
15
+ import scala .language .higherKinds
16
+
15
17
import java .lang .Cloneable
16
18
import java .lang .Utils ._
17
19
import java .lang .{reflect => jlr }
@@ -23,32 +25,46 @@ import scala.annotation.tailrec
23
25
import ScalaOps ._
24
26
25
27
import scala .scalajs ._
28
+ import scala .scalajs .LinkingInfo ._
26
29
27
- class CopyOnWriteArrayList [E <: AnyRef ] private (private var inner : js. Array [ E ] )
30
+ class CopyOnWriteArrayList [E <: AnyRef ] private (initialCapacity : Int )
28
31
extends List [E ] with RandomAccess with Cloneable with Serializable {
29
32
self =>
30
33
34
+ /* This class has two different implementations for the
35
+ * internal data storage, depending on whether we are on Wasm or JS.
36
+ * We use `js.Array` on JS, and `scala.Array` on Wasm.
37
+ * The initialCapacity parameter is effective only in Wasm,
38
+ * since js.Array doesn't support explicit pre-allocation.
39
+ *
40
+ * On Wasm, we store the length at the index 0 of the array.
41
+ */
42
+
43
+ import CopyOnWriteArrayList ._
44
+
45
+ private var inner : innerImpl.Repr [E ] = innerImpl.make(initialCapacity)
46
+
31
47
// requiresCopyOnWrite is false if and only if no other object
32
48
// (like the iterator) may have a reference to inner
33
49
private var requiresCopyOnWrite = false
34
50
35
51
def this () = {
36
- this (new js. Array [ E ] )
52
+ this (16 )
37
53
}
38
54
39
55
def this (c : Collection [_ <: E ]) = {
40
- this ()
56
+ this (c.size() )
41
57
addAll(c)
42
58
}
43
59
44
60
def this (toCopyIn : Array [E ]) = {
45
- this ()
61
+ this (toCopyIn.length )
46
62
for (i <- 0 until toCopyIn.length)
47
63
add(toCopyIn(i))
48
64
}
49
65
50
66
def size (): Int =
51
- inner .length
67
+ innerImpl .length(inner)
52
68
53
69
def isEmpty (): Boolean =
54
70
size() == 0
@@ -175,7 +191,7 @@ class CopyOnWriteArrayList[E <: AnyRef] private (private var inner: js.Array[E])
175
191
}
176
192
177
193
def clear (): Unit = {
178
- inner = new js. Array [ E ]
194
+ inner = innerImpl.make( 16 )
179
195
requiresCopyOnWrite = false
180
196
}
181
197
@@ -274,43 +290,49 @@ class CopyOnWriteArrayList[E <: AnyRef] private (private var inner: js.Array[E])
274
290
}
275
291
276
292
protected def innerGet (index : Int ): E =
277
- inner( index)
293
+ innerImpl.get(inner, index)
278
294
279
295
protected def innerSet (index : Int , elem : E ): Unit =
280
- inner( index) = elem
296
+ innerImpl.set(inner, index, elem)
281
297
282
- protected def innerPush (elem : E ): Unit =
283
- inner.push(elem)
298
+ protected def innerPush (elem : E ): Unit = {
299
+ val newInner = innerImpl.push(inner, elem)
300
+ if (LinkingInfo .isWebAssembly) // opt: for JS we know it's always the same
301
+ inner = newInner
302
+ }
284
303
285
- protected def innerInsert (index : Int , elem : E ): Unit =
286
- inner.splice(index, 0 , elem)
304
+ protected def innerInsert (index : Int , elem : E ): Unit = {
305
+ val newInner = innerImpl.add(inner, index, elem)
306
+ if (LinkingInfo .isWebAssembly) // opt: for JS we know it's always the same
307
+ inner = newInner
308
+ }
287
309
288
310
protected def innerInsertMany (index : Int , items : Collection [_ <: E ]): Unit = {
289
- val itemsArray = js. Array [ E ]( )
290
- items.scalaOps.foreach(itemsArray.push(_))
291
- inner.splice(index, 0 , itemsArray.toSeq : _* )
311
+ val newInner = innerImpl.add(inner, index, items )
312
+ if ( LinkingInfo .isWebAssembly) // opt: for JS we know it's always the same
313
+ inner = newInner
292
314
}
293
315
294
316
protected def innerRemove (index : Int ): E =
295
- arrayRemoveAndGet (inner, index)
317
+ innerImpl.remove (inner, index)
296
318
297
319
protected def innerRemoveMany (index : Int , count : Int ): Unit =
298
- inner.splice( index, count)
320
+ innerImpl.remove(inner, index, count)
299
321
300
322
protected def copyIfNeeded (): Unit = {
301
323
if (requiresCopyOnWrite) {
302
- inner = inner.jsSlice( )
324
+ inner = innerImpl.clone(inner )
303
325
requiresCopyOnWrite = false
304
326
}
305
327
}
306
328
307
- protected def innerSnapshot (): js. Array [E ] = {
329
+ protected def innerSnapshot (): innerImpl. Repr [E ] = {
308
330
requiresCopyOnWrite = true
309
331
inner
310
332
}
311
333
312
334
private class CopyOnWriteArrayListView (fromIndex : Int , private var toIndex : Int )
313
- extends CopyOnWriteArrayList [E ](null : js. Array [ E ] ) {
335
+ extends CopyOnWriteArrayList [E ](initialCapacity ) {
314
336
viewSelf =>
315
337
316
338
override def size (): Int =
@@ -381,7 +403,7 @@ class CopyOnWriteArrayList[E <: AnyRef] private (private var inner: js.Array[E])
381
403
override protected def copyIfNeeded (): Unit =
382
404
self.copyIfNeeded()
383
405
384
- override protected def innerSnapshot (): js. Array [E ] =
406
+ override protected def innerSnapshot (): innerImpl. Repr [E ] =
385
407
self.innerSnapshot()
386
408
387
409
protected def changeSize (delta : Int ): Unit =
@@ -399,7 +421,8 @@ class CopyOnWriteArrayList[E <: AnyRef] private (private var inner: js.Array[E])
399
421
}
400
422
}
401
423
402
- private class CopyOnWriteArrayListIterator [E ](arraySnapshot : js.Array [E ], i : Int , start : Int , end : Int )
424
+ private class CopyOnWriteArrayListIterator [E ](
425
+ arraySnapshot : CopyOnWriteArrayList .innerImpl.Repr [E ], i : Int , start : Int , end : Int )
403
426
extends AbstractRandomAccessListIterator [E ](i, start, end) {
404
427
override def remove (): Unit =
405
428
throw new UnsupportedOperationException
@@ -411,7 +434,7 @@ private class CopyOnWriteArrayListIterator[E](arraySnapshot: js.Array[E], i: Int
411
434
throw new UnsupportedOperationException
412
435
413
436
protected def get (index : Int ): E =
414
- arraySnapshot( index)
437
+ CopyOnWriteArrayList .innerImpl.get(arraySnapshot, index)
415
438
416
439
protected def remove (index : Int ): Unit =
417
440
throw new UnsupportedOperationException
@@ -422,3 +445,162 @@ private class CopyOnWriteArrayListIterator[E](arraySnapshot: js.Array[E], i: Int
422
445
protected def add (index : Int , e : E ): Unit =
423
446
throw new UnsupportedOperationException
424
447
}
448
+
449
+ object CopyOnWriteArrayList {
450
+
451
+ /* Get the best implementation of inner array for the given platform.
452
+ *
453
+ * Use Array[AnyRef] in WebAssembly to avoid JS-interop. In JS, use js.Array.
454
+ * It is resizable by nature, so manual resizing is not needed.
455
+ *
456
+ * `linkTimeIf` is needed here to ensure the optimizer knows
457
+ * there is only one implementation of `InnerArrayImpl`, and de-virtualize/inline
458
+ * the function calls.
459
+ */
460
+
461
+ // package private so that `protected def innerSnapshot` can access this field.
462
+ private [concurrent] val innerImpl : InnerArrayImpl = {
463
+ LinkingInfo .linkTimeIf[InnerArrayImpl ](LinkingInfo .isWebAssembly) {
464
+ InnerArrayImpl .JArrayImpl
465
+ } {
466
+ InnerArrayImpl .JSArrayImpl
467
+ }
468
+ }
469
+
470
+ private [concurrent] sealed abstract class InnerArrayImpl {
471
+ type Repr [E ] <: AnyRef
472
+
473
+ def make [E ](initialCapacity : Int ): Repr [E ]
474
+ def length (v : Repr [_]): Int
475
+ def get [E ](v : Repr [E ], index : Int ): E
476
+ def set [E ](v : Repr [E ], index : Int , e : E ): Unit
477
+ def push [E ](v : Repr [E ], e : E ): Repr [E ]
478
+ def add [E ](v : Repr [E ], index : Int , e : E ): Repr [E ]
479
+ def add [E ](v : Repr [E ], index : Int , items : Collection [_ <: E ]): Repr [E ]
480
+ def remove [E ](v : Repr [E ], index : Int ): E
481
+ def remove (v : Repr [_], index : Int , count : Int ): Unit
482
+ def clone [E ](v : Repr [E ]): Repr [E ]
483
+ }
484
+
485
+ private object InnerArrayImpl {
486
+ object JSArrayImpl extends InnerArrayImpl {
487
+ import scala .scalajs .js
488
+
489
+ type Repr [E ] = js.Array [AnyRef ]
490
+
491
+ @ inline def make [E ](_initialCapacity : Int ): Repr [E ] = js.Array [AnyRef ]()
492
+
493
+ @ inline def length (v : Repr [_]): Int = v.length
494
+
495
+ @ inline def get [E ](v : Repr [E ], index : Int ): E = v(index).asInstanceOf [E ]
496
+
497
+ @ inline def set [E ](v : Repr [E ], index : Int , e : E ): Unit =
498
+ v(index) = e.asInstanceOf [AnyRef ]
499
+
500
+ @ inline def push [E ](v : Repr [E ], e : E ): Repr [E ] = {
501
+ v.push(e.asInstanceOf [AnyRef ])
502
+ v
503
+ }
504
+
505
+ @ inline def add [E ](v : Repr [E ], index : Int , e : E ): Repr [E ] = {
506
+ v.splice(index, 0 , e.asInstanceOf [AnyRef ])
507
+ v
508
+ }
509
+
510
+ @ inline def add [E ](v : Repr [E ], index : Int , items : Collection [_ <: E ]): Repr [E ] = {
511
+ val itemsArray = js.Array [AnyRef ]()
512
+ items.scalaOps.foreach(e => itemsArray.push(e.asInstanceOf [AnyRef ]))
513
+ v.splice(index, 0 , itemsArray.toSeq: _* )
514
+ v
515
+ }
516
+
517
+ @ inline def remove [E ](v : Repr [E ], index : Int ): E =
518
+ arrayRemoveAndGet(v, index).asInstanceOf [E ]
519
+
520
+ @ inline def remove (v : Repr [_], index : Int , count : Int ): Unit =
521
+ v.splice(index, count)
522
+
523
+ @ inline def clone [E ](v : Repr [E ]): Repr [E ] =
524
+ v.jsSlice(0 )
525
+ }
526
+
527
+ object JArrayImpl extends InnerArrayImpl {
528
+ type Repr [E ] = Array [AnyRef ]
529
+
530
+ @ inline def make [E ](initialCapacity : Int ): Repr [E ] = {
531
+ val v = new Array [AnyRef ](roundUpToPowerOfTwo(initialCapacity + 1 ))
532
+ v(0 ) = 0 .asInstanceOf [AnyRef ]
533
+ v
534
+ }
535
+
536
+ @ inline def length (v : Repr [_]): Int = v(0 ).asInstanceOf [Int ]
537
+
538
+ @ inline def get [E ](v : Repr [E ], index : Int ): E = v(index + 1 ).asInstanceOf [E ] // Index 0 stores the length
539
+
540
+ @ inline def set [E ](v : Repr [E ], index : Int , e : E ): Unit =
541
+ v(index + 1 ) = e.asInstanceOf [AnyRef ]
542
+
543
+ @ inline def push [E ](v : Repr [E ], e : E ): Repr [E ] = {
544
+ val size = length(v)
545
+ val newArr = ensureCapacity(v, size + 1 )
546
+ newArr(size + 1 ) = e.asInstanceOf [AnyRef ]
547
+ newArr(0 ) = (size + 1 ).asInstanceOf [AnyRef ]
548
+ newArr
549
+ }
550
+
551
+ @ inline def add [E ](v : Repr [E ], index : Int , e : E ): Repr [E ] = {
552
+ val innerIdx = index + 1
553
+ val size = length(v)
554
+ val newArr = ensureCapacity(v, size + 1 )
555
+ System .arraycopy(newArr, innerIdx, newArr, innerIdx + 1 , size + 1 - innerIdx)
556
+ newArr(innerIdx) = e.asInstanceOf [AnyRef ]
557
+ newArr(0 ) = (size + 1 ).asInstanceOf [AnyRef ]
558
+ newArr
559
+ }
560
+
561
+ @ inline def add [E ](v : Repr [E ], index : Int , items : Collection [_ <: E ]): Repr [E ] = {
562
+ val innerIdx = index + 1
563
+ val size = length(v)
564
+ val itemsSize = items.size()
565
+ val newArr = ensureCapacity(v, size + itemsSize)
566
+ System .arraycopy(newArr, innerIdx, newArr, innerIdx + itemsSize, size + 1 - innerIdx)
567
+ System .arraycopy(items.toArray(), 0 , newArr, innerIdx, itemsSize)
568
+ newArr(0 ) = (size + itemsSize).asInstanceOf [AnyRef ]
569
+ newArr
570
+ }
571
+
572
+ @ inline def remove [E ](v : Repr [E ], index : Int ): E = {
573
+ val innerIdx = index + 1
574
+ val size = length(v)
575
+ val removed = v(innerIdx)
576
+ System .arraycopy(v, innerIdx + 1 , v, innerIdx, size - innerIdx)
577
+ v(size) = null // free reference for GC
578
+ v(0 ) = (size - 1 ).asInstanceOf [AnyRef ]
579
+ removed.asInstanceOf [E ]
580
+ }
581
+
582
+ @ inline def remove (v : Repr [_], index : Int , count : Int ): Unit = {
583
+ val innerIdx = index + 1
584
+ val size = length(v)
585
+ val toIndex = innerIdx + count
586
+ System .arraycopy(v, toIndex, v, innerIdx, size + 1 - toIndex)
587
+ val newSize = size - count
588
+ Arrays .fill(v, newSize + 1 , newSize + 1 + count, null ) // free references for GC
589
+ v(0 ) = newSize.asInstanceOf [AnyRef ]
590
+ }
591
+
592
+ @ inline def clone [E ](v : Repr [E ]): Repr [E ] =
593
+ v.clone()
594
+
595
+ @ inline private def ensureCapacity [E ](v : Repr [E ], minCapacity : Int ): Repr [E ] = {
596
+ val capacity = v.length - 1
597
+ if (capacity < minCapacity) {
598
+ val newCapacity = roundUpToPowerOfTwo(minCapacity + 1 ) // Index 0 stores the length
599
+ Arrays .copyOf(v, newCapacity)
600
+ } else {
601
+ v
602
+ }
603
+ }
604
+ }
605
+ }
606
+ }
0 commit comments