@@ -14,80 +14,181 @@ package java.util
14
14
15
15
import java .lang .Cloneable
16
16
import java .lang .Utils ._
17
+ import java .util .ScalaOps ._
17
18
18
19
import scala .scalajs ._
20
+ import scala .scalajs .LinkingInfo .isWebAssembly
19
21
20
- class ArrayList [E ] private (private [ ArrayList ] val inner : js. Array [ E ] )
22
+ class ArrayList [E ] private (innerInit : AnyRef , private var _size : Int )
21
23
extends AbstractList [E ] with RandomAccess with Cloneable with Serializable {
22
24
self =>
23
25
26
+ /* This class has two different implementations for handling the
27
+ * internal data storage, depending on whether we are on Wasm or JS.
28
+ * On JS, we utilize `js.Array`. On Wasm, for performance reasons,
29
+ * we avoid JS interop and use a scala.Array.
30
+ * The `_size` field (unused in JS) keeps track of the effective size
31
+ * of the underlying Array for the Wasm implementation.
32
+ */
33
+
34
+ private val innerJS : js.Array [E ] =
35
+ if (isWebAssembly) null
36
+ else innerInit.asInstanceOf [js.Array [E ]]
37
+
38
+ private var innerWasm : Array [AnyRef ] =
39
+ if (! isWebAssembly) null
40
+ else innerInit.asInstanceOf [Array [AnyRef ]]
41
+
24
42
def this (initialCapacity : Int ) = {
25
- this (new js.Array [E ])
26
- if (initialCapacity < 0 )
27
- throw new IllegalArgumentException
43
+ this (
44
+ {
45
+ if (initialCapacity < 0 )
46
+ throw new IllegalArgumentException
47
+ if (isWebAssembly) new Array [AnyRef ](initialCapacity)
48
+ else new js.Array [E ]
49
+ },
50
+ 0
51
+ )
28
52
}
29
53
30
- def this () =
31
- this (new js.Array [E ])
54
+ def this () = this (16 )
32
55
33
56
def this (c : Collection [_ <: E ]) = {
34
- this ()
57
+ this (c.size() )
35
58
addAll(c)
36
59
}
37
60
38
61
def trimToSize (): Unit = {
39
- // We ignore this as js.Array doesn't support explicit pre-allocation
62
+ if (isWebAssembly)
63
+ resizeTo(size())
64
+ // We ignore this in JS as js.Array doesn't support explicit pre-allocation
40
65
}
41
66
42
67
def ensureCapacity (minCapacity : Int ): Unit = {
43
- // We ignore this as js.Array doesn't support explicit pre-allocation
68
+ if (isWebAssembly) {
69
+ if (innerWasm.length < minCapacity) {
70
+ if (minCapacity > (1 << 30 ))
71
+ resizeTo(minCapacity)
72
+ else
73
+ resizeTo(((1 << 31 ) >>> (Integer .numberOfLeadingZeros(minCapacity - 1 )) - 1 ))
74
+ }
75
+ }
76
+ // We ignore this in JS as js.Array doesn't support explicit pre-allocation
44
77
}
45
78
46
79
def size (): Int =
47
- inner.length
48
-
49
- override def clone (): AnyRef =
50
- new ArrayList (inner.jsSlice(0 ))
80
+ if (isWebAssembly) _size
81
+ else innerJS.length
82
+
83
+ override def clone (): AnyRef = {
84
+ if (isWebAssembly)
85
+ new ArrayList (innerWasm.clone(), size())
86
+ else
87
+ new ArrayList (innerJS.jsSlice(0 ), 0 )
88
+ }
51
89
52
90
def get (index : Int ): E = {
53
91
checkIndexInBounds(index)
54
- inner(index)
92
+ if (isWebAssembly)
93
+ innerWasm(index).asInstanceOf [E ]
94
+ else
95
+ innerJS(index)
55
96
}
56
97
57
98
override def set (index : Int , element : E ): E = {
58
99
val e = get(index)
59
- inner(index) = element
100
+ if (isWebAssembly)
101
+ innerWasm(index) = element.asInstanceOf [AnyRef ]
102
+ else
103
+ innerJS(index) = element
60
104
e
61
105
}
62
106
63
107
override def add (e : E ): Boolean = {
64
- inner.push(e)
108
+ if (isWebAssembly) {
109
+ if (size() >= innerWasm.length)
110
+ expand()
111
+ innerWasm(size()) = e.asInstanceOf [AnyRef ]
112
+ _size += 1
113
+ } else {
114
+ innerJS.push(e)
115
+ }
65
116
true
66
117
}
67
118
68
119
override def add (index : Int , element : E ): Unit = {
69
120
checkIndexOnBounds(index)
70
- inner.splice(index, 0 , element)
121
+ if (isWebAssembly) {
122
+ if (size() >= innerWasm.length)
123
+ expand()
124
+ System .arraycopy(innerWasm, index, innerWasm, index + 1 , size() - index)
125
+ innerWasm(index) = element.asInstanceOf [AnyRef ]
126
+ _size += 1
127
+ } else {
128
+ innerJS.splice(index, 0 , element)
129
+ }
71
130
}
72
131
73
132
override def remove (index : Int ): E = {
74
133
checkIndexInBounds(index)
75
- arrayRemoveAndGet(inner, index)
134
+ if (isWebAssembly) {
135
+ val removed = innerWasm(index).asInstanceOf [E ]
136
+ System .arraycopy(innerWasm, index + 1 , innerWasm, index, size() - index - 1 )
137
+ innerWasm(size - 1 ) = null // free reference for GC
138
+ _size -= 1
139
+ removed
140
+ } else {
141
+ arrayRemoveAndGet(innerJS, index)
142
+ }
76
143
}
77
144
78
145
override def clear (): Unit =
79
- inner.length = 0
146
+ if (isWebAssembly) {
147
+ Arrays .fill(innerWasm, null ) // free references for GC
148
+ _size = 0
149
+ } else {
150
+ innerJS.length = 0
151
+ }
80
152
81
153
override def addAll (index : Int , c : Collection [_ <: E ]): Boolean = {
82
154
c match {
83
155
case other : ArrayList [_] =>
84
- inner.splice(index, 0 , other.inner.toSeq: _* )
156
+ checkIndexOnBounds(index)
157
+ if (isWebAssembly) {
158
+ ensureCapacity(size() + other.size())
159
+ System .arraycopy(innerWasm, index, innerWasm, index + other.size(), size() - index)
160
+ System .arraycopy(other.innerWasm, 0 , innerWasm, index, other.size())
161
+ _size += c.size()
162
+ } else {
163
+ innerJS.splice(index, 0 , other.innerJS.toSeq: _* )
164
+ }
85
165
other.size() > 0
86
166
case _ => super .addAll(index, c)
87
167
}
88
168
}
89
169
90
- override protected def removeRange (fromIndex : Int , toIndex : Int ): Unit =
91
- inner.splice(fromIndex, toIndex - fromIndex)
170
+ override protected def removeRange (fromIndex : Int , toIndex : Int ): Unit = {
171
+ if (fromIndex < 0 || toIndex > size() || toIndex < fromIndex)
172
+ throw new IndexOutOfBoundsException ()
173
+ if (isWebAssembly) {
174
+ if (fromIndex != toIndex) {
175
+ System .arraycopy(innerWasm, toIndex, innerWasm, fromIndex, size() - toIndex)
176
+ val newSize = size() - toIndex + fromIndex
177
+ Arrays .fill(innerWasm, newSize, size(), null ) // free references for GC
178
+ _size = newSize
179
+ }
180
+ } else {
181
+ innerJS.splice(fromIndex, toIndex - fromIndex)
182
+ }
183
+ }
92
184
185
+ // Wasm only
186
+ private def expand (): Unit = {
187
+ resizeTo(Math .max(innerWasm.length * 2 , 16 ))
188
+ }
189
+
190
+ // Wasm only
191
+ private def resizeTo (newCapacity : Int ): Unit = {
192
+ innerWasm = Arrays .copyOf(innerWasm, newCapacity)
193
+ }
93
194
}
0 commit comments