@@ -14,75 +14,160 @@ 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 =
35
+ if (isWebAssembly) null
36
+ else innerInit.asInstanceOf [js.Array [E ]]
37
+
38
+ private var innerWasm =
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 [Any ](initialCapacity)
48
+ else new js.Array [E ],
49
+ },
50
+ 0
51
+ )
28
52
}
29
53
30
54
def this () =
31
- this (new js.Array [E ])
55
+ this (
56
+ if (isWebAssembly) new Array [AnyRef ](16 )
57
+ else new js.Array [E ],
58
+ 0
59
+ )
32
60
33
61
def this (c : Collection [_ <: E ]) = {
34
- this ()
62
+ this (
63
+ if (isWebAssembly) new Array [AnyRef ](c.size())
64
+ else new js.Array [E ],
65
+ 0
66
+ )
35
67
addAll(c)
36
68
}
37
69
38
70
def trimToSize (): Unit = {
39
- // We ignore this as js.Array doesn't support explicit pre-allocation
71
+ if (isWebAssembly)
72
+ resizeTo(size())
73
+ // We ignore this in JS as js.Array doesn't support explicit pre-allocation
40
74
}
41
75
42
76
def ensureCapacity (minCapacity : Int ): Unit = {
43
- // We ignore this as js.Array doesn't support explicit pre-allocation
77
+ if (isWebAssembly) {
78
+ if (innerWasm.length < minCapacity)
79
+ resizeTo(minCapacity)
80
+ }
81
+ // We ignore this in JS as js.Array doesn't support explicit pre-allocation
44
82
}
45
83
46
84
def size (): Int =
47
- inner.length
48
-
49
- override def clone (): AnyRef =
50
- new ArrayList (inner.jsSlice(0 ))
85
+ if (isWebAssembly) _size
86
+ else innerJS.length
87
+
88
+ override def clone (): AnyRef = {
89
+ if (isWebAssembly)
90
+ new ArrayList (innerWasm.clone(), size())
91
+ else
92
+ new ArrayList (innerJS.jsSlice(0 ), 0 )
93
+ }
51
94
52
95
def get (index : Int ): E = {
53
96
checkIndexInBounds(index)
54
- inner(index)
97
+ if (isWebAssembly)
98
+ innerWasm(index).asInstanceOf [E ]
99
+ else
100
+ innerJS(index)
55
101
}
56
102
57
103
override def set (index : Int , element : E ): E = {
58
104
val e = get(index)
59
- inner(index) = element
105
+ if (isWebAssembly)
106
+ innerWasm(index) = element.asInstanceOf [AnyRef ]
107
+ else
108
+ innerJS(index) = element
60
109
e
61
110
}
62
111
63
112
override def add (e : E ): Boolean = {
64
- inner.push(e)
113
+ if (isWebAssembly) {
114
+ if (size() >= innerWasm.length)
115
+ expand()
116
+ innerWasm(size()) = e.asInstanceOf [AnyRef ]
117
+ _size += 1
118
+ } else {
119
+ innerJS.push(e)
120
+ }
65
121
true
66
122
}
67
123
68
124
override def add (index : Int , element : E ): Unit = {
69
125
checkIndexOnBounds(index)
70
- inner.splice(index, 0 , element)
126
+ if (isWebAssembly) {
127
+ if (size() >= innerWasm.length)
128
+ expand()
129
+ System .arraycopy(innerWasm, index, innerWasm, index + 1 , size() - index)
130
+ innerWasm(index) = element.asInstanceOf [AnyRef ]
131
+ _size += 1
132
+ } else {
133
+ innerJS.splice(index, 0 , element)
134
+ }
71
135
}
72
136
73
137
override def remove (index : Int ): E = {
74
138
checkIndexInBounds(index)
75
- arrayRemoveAndGet(inner, index)
139
+ if (isWebAssembly) {
140
+ val removed = innerWasm(index).asInstanceOf [E ]
141
+ System .arraycopy(innerWasm, index + 1 , innerWasm, index, size() - index - 1 )
142
+ innerWasm(size - 1 ) = null
143
+ _size -= 1
144
+ removed
145
+ } else {
146
+ arrayRemoveAndGet(innerJS, index)
147
+ }
76
148
}
77
149
78
150
override def clear (): Unit =
79
- inner.length = 0
151
+ if (isWebAssembly) {
152
+ Arrays .fill(innerWasm, null ) // free references for GC
153
+ _size = 0
154
+ } else {
155
+ innerJS.length = 0
156
+ }
80
157
81
158
override def addAll (index : Int , c : Collection [_ <: E ]): Boolean = {
82
159
c match {
83
160
case other : ArrayList [_] =>
84
161
checkIndexOnBounds(index)
85
- inner.splice(index, 0 , other.inner.toSeq: _* )
162
+ if (isWebAssembly) {
163
+ if (size() + other.size() >= innerWasm.length)
164
+ resizeTo(size() + other.size())
165
+ System .arraycopy(innerWasm, index, innerWasm, index + other.size(), size() - index)
166
+ System .arraycopy(other.innerWasm, 0 , innerWasm, index, other.size())
167
+ _size += c.size()
168
+ } else {
169
+ innerJS.splice(index, 0 , other.innerJS.toSeq: _* )
170
+ }
86
171
other.size() > 0
87
172
case _ => super .addAll(index, c)
88
173
}
@@ -91,6 +176,25 @@ class ArrayList[E] private (private[ArrayList] val inner: js.Array[E])
91
176
override protected def removeRange (fromIndex : Int , toIndex : Int ): Unit = {
92
177
if (fromIndex < 0 || toIndex > size() || toIndex < fromIndex)
93
178
throw new IndexOutOfBoundsException ()
94
- inner.splice(fromIndex, toIndex - fromIndex)
179
+ if (isWebAssembly) {
180
+ if (fromIndex != toIndex) {
181
+ System .arraycopy(innerWasm, toIndex, innerWasm, fromIndex, size() - toIndex)
182
+ val newSize = size() - toIndex + fromIndex
183
+ Arrays .fill(innerWasm, newSize, size(), null ) // free references for GC
184
+ _size = newSize
185
+ }
186
+ } else {
187
+ innerJS.splice(fromIndex, toIndex - fromIndex)
188
+ }
189
+ }
190
+
191
+ // Wasm only
192
+ private def expand (): Unit = {
193
+ resizeTo(Math .max(innerWasm.length * 2 , 16 ))
194
+ }
195
+
196
+ // Wasm only
197
+ private def resizeTo (newCapacity : Int ): Unit = {
198
+ innerWasm = Arrays .copyOf(innerWasm, newCapacity)
95
199
}
96
200
}
0 commit comments