12
12
13
13
package java .util
14
14
15
+ import scala .language .higherKinds
16
+
15
17
import scala .annotation .tailrec
16
18
17
- import scala .scalajs .js
19
+ import java .lang .Utils .roundUpToPowerOfTwo
20
+
21
+ import scala .scalajs .LinkingInfo
18
22
19
23
class PriorityQueue [E ] private (
20
- private val comp : Comparator [_ >: E ], internal : Boolean )
24
+ private val comp : Comparator [_ >: E ], internal : Boolean , initialCapacity : Int )
21
25
extends AbstractQueue [E ] with Serializable {
22
26
27
+ import PriorityQueue ._
28
+
23
29
def this () =
24
- this (NaturalComparator , internal = true )
30
+ this (NaturalComparator , internal = true , initialCapacity = 16 )
25
31
26
32
def this (initialCapacity : Int ) = {
27
- this ()
28
- if (initialCapacity < 1 )
29
- throw new IllegalArgumentException ()
33
+ this (
34
+ NaturalComparator ,
35
+ internal = true ,
36
+ {
37
+ if (initialCapacity < 1 )
38
+ throw new IllegalArgumentException
39
+ initialCapacity + 1 // index 0 is unused
40
+ }
41
+ )
30
42
}
31
43
32
44
def this (comparator : Comparator [_ >: E ]) = {
33
- this (NaturalComparator .select(comparator), internal = true )
45
+ this (NaturalComparator .select(comparator), internal = true , initialCapacity = 16 )
34
46
}
35
47
36
48
def this (initialCapacity : Int , comparator : Comparator [_ >: E ]) = {
37
- this (comparator)
38
- if (initialCapacity < 1 )
39
- throw new IllegalArgumentException ()
49
+ this (
50
+ NaturalComparator .select(comparator),
51
+ internal = true ,
52
+ {
53
+ if (initialCapacity < 1 )
54
+ throw new IllegalArgumentException ()
55
+ initialCapacity + 1 // index 0 is unused
56
+ }
57
+ )
40
58
}
41
59
42
60
def this (c : Collection [_ <: E ]) = {
@@ -47,47 +65,51 @@ class PriorityQueue[E] private (
47
65
NaturalComparator .select(c.comparator().asInstanceOf [Comparator [_ >: E ]])
48
66
case _ =>
49
67
NaturalComparator
50
- }, internal = true )
68
+ }, internal = true , roundUpToPowerOfTwo(c.size() + 1 )) // index 0 is unused
51
69
addAll(c)
52
70
}
53
71
54
72
def this (c : PriorityQueue [_ <: E ]) = {
55
- this (c.comp.asInstanceOf [Comparator [_ >: E ]], internal = true )
73
+ this (c.comp.asInstanceOf [Comparator [_ >: E ]], internal = true ,
74
+ roundUpToPowerOfTwo(c.size() + 1 )) // index 0 is unused
56
75
addAll(c)
57
76
}
58
77
59
78
def this (sortedSet : SortedSet [_ <: E ]) = {
60
79
this (NaturalComparator .select(
61
80
sortedSet.comparator().asInstanceOf [Comparator [_ >: E ]]),
62
- internal = true )
81
+ internal = true ,
82
+ roundUpToPowerOfTwo(sortedSet.size() + 1 )) // index 0 is unused
63
83
addAll(sortedSet)
64
84
}
65
85
66
86
// The index 0 is not used; the root is at index 1.
67
87
// This is standard practice in binary heaps, to simplify arithmetics.
68
- private [ this ] val inner = js. Array [E ]( null . asInstanceOf [E ])
88
+ private var inner : innerImpl. Repr [E ] = innerImpl.make [E ](initialCapacity )
69
89
70
90
override def add (e : E ): Boolean = {
71
91
if (e == null )
72
92
throw new NullPointerException ()
73
- inner.push(e)
74
- fixUp(inner.length - 1 )
93
+ val newInner = innerImpl.push(inner, e)
94
+ if (LinkingInfo .isWebAssembly) // opt: for JS we know it's always the same
95
+ inner = newInner
96
+ fixUp(innerImpl.length(inner) - 1 )
75
97
true
76
98
}
77
99
78
100
def offer (e : E ): Boolean = add(e)
79
101
80
102
def peek (): E =
81
- if (inner .length > 1 ) inner( 1 )
103
+ if (innerImpl .length(inner) > 1 ) innerImpl.get(inner, 1 )
82
104
else null .asInstanceOf [E ]
83
105
84
106
override def remove (o : Any ): Boolean = {
85
107
if (o == null ) {
86
108
false
87
109
} else {
88
- val len = inner .length
110
+ val len = innerImpl .length(inner)
89
111
var i = 1
90
- while (i != len && ! o.equals(inner( i))) {
112
+ while (i != len && ! o.equals(innerImpl.get(inner, i))) {
91
113
i += 1
92
114
}
93
115
@@ -101,9 +123,9 @@ class PriorityQueue[E] private (
101
123
}
102
124
103
125
private def removeExact (o : Any ): Unit = {
104
- val len = inner .length
126
+ val len = innerImpl .length(inner)
105
127
var i = 1
106
- while (i != len && (o.asInstanceOf [AnyRef ] ne inner( i).asInstanceOf [AnyRef ])) {
128
+ while (i != len && (o.asInstanceOf [AnyRef ] ne innerImpl.get(inner, i).asInstanceOf [AnyRef ])) {
107
129
i += 1
108
130
}
109
131
if (i == len)
@@ -112,12 +134,12 @@ class PriorityQueue[E] private (
112
134
}
113
135
114
136
private def removeAt (i : Int ): Unit = {
115
- val newLength = inner .length - 1
137
+ val newLength = innerImpl .length(inner) - 1
116
138
if (i == newLength) {
117
- inner.length = newLength
139
+ innerImpl.decLength(inner)
118
140
} else {
119
- inner(i) = inner( newLength)
120
- inner.length = newLength
141
+ innerImpl.set(inner, i, innerImpl.get(inner, newLength) )
142
+ innerImpl.decLength(inner)
121
143
fixUpOrDown(i)
122
144
}
123
145
}
@@ -126,9 +148,9 @@ class PriorityQueue[E] private (
126
148
if (o == null ) {
127
149
false
128
150
} else {
129
- val len = inner .length
151
+ val len = innerImpl .length(inner)
130
152
var i = 1
131
- while (i != len && ! o.equals(inner( i))) {
153
+ while (i != len && ! o.equals(innerImpl.get(inner, i))) {
132
154
i += 1
133
155
}
134
156
i != len
@@ -137,16 +159,16 @@ class PriorityQueue[E] private (
137
159
138
160
def iterator (): Iterator [E ] = {
139
161
new Iterator [E ] {
140
- private [this ] var inner : js. Array [E ] = PriorityQueue .this .inner
162
+ private [this ] var inner : innerImpl. Repr [E ] = PriorityQueue .this .inner
141
163
private [this ] var nextIdx : Int = 1
142
164
private [this ] var last : E = _ // null
143
165
144
- def hasNext (): Boolean = nextIdx < inner .length
166
+ def hasNext (): Boolean = nextIdx < innerImpl .length(inner)
145
167
146
168
def next (): E = {
147
169
if (! hasNext())
148
170
throw new NoSuchElementException (" empty iterator" )
149
- last = inner( nextIdx)
171
+ last = innerImpl.get(inner, nextIdx)
150
172
nextIdx += 1
151
173
last
152
174
}
@@ -173,27 +195,27 @@ class PriorityQueue[E] private (
173
195
if (last == null )
174
196
throw new IllegalStateException ()
175
197
if (inner eq PriorityQueue .this .inner) {
176
- inner = inner.jsSlice( nextIdx)
177
- nextIdx = 0
198
+ inner = innerImpl.copyFrom(inner, nextIdx)
199
+ nextIdx = 1
178
200
}
179
201
removeExact(last)
180
202
last = null .asInstanceOf [E ]
181
203
}
182
204
}
183
205
}
184
206
185
- def size (): Int = inner .length - 1
207
+ def size (): Int = innerImpl .length(inner) - 1
186
208
187
209
override def clear (): Unit =
188
- inner.length = 1
210
+ innerImpl.clear(inner)
189
211
190
212
def poll (): E = {
191
213
val inner = this .inner // local copy
192
- if (inner .length > 1 ) {
193
- val newSize = inner .length - 1
194
- val result = inner( 1 )
195
- inner( 1 ) = inner( newSize)
196
- inner.length = newSize
214
+ if (innerImpl .length(inner) > 1 ) {
215
+ val newSize = innerImpl .length(inner) - 1
216
+ val result = innerImpl.get(inner, 1 )
217
+ innerImpl.set(inner, 1 , innerImpl.get(inner, newSize) )
218
+ innerImpl.decLength(inner)
197
219
fixDown(1 )
198
220
result
199
221
} else {
@@ -212,7 +234,7 @@ class PriorityQueue[E] private (
212
234
*/
213
235
private [this ] def fixUpOrDown (m : Int ): Unit = {
214
236
val inner = this .inner // local copy
215
- if (m > 1 && comp.compare(inner( m >> 1 ), inner( m)) > 0 )
237
+ if (m > 1 && comp.compare(innerImpl.get(inner, m >> 1 ), innerImpl.get(inner, m)) > 0 )
216
238
fixUp(m)
217
239
else
218
240
fixDown(m)
@@ -227,16 +249,16 @@ class PriorityQueue[E] private (
227
249
/* At each step, even though `m` changes, the element moves with it, and
228
250
* hence inner(m) is always the same initial `innerAtM`.
229
251
*/
230
- val innerAtM = inner( m)
252
+ val innerAtM = innerImpl.get(inner, m)
231
253
232
254
@ inline @ tailrec
233
255
def loop (m : Int ): Unit = {
234
256
if (m > 1 ) {
235
257
val parent = m >> 1
236
- val innerAtParent = inner( parent)
258
+ val innerAtParent = innerImpl.get(inner, parent)
237
259
if (comp.compare(innerAtParent, innerAtM) > 0 ) {
238
- inner( parent) = innerAtM
239
- inner(m) = innerAtParent
260
+ innerImpl.set(inner, parent, innerAtM)
261
+ innerImpl.set(inner, m, innerAtParent)
240
262
loop(parent)
241
263
}
242
264
}
@@ -250,22 +272,22 @@ class PriorityQueue[E] private (
250
272
*/
251
273
private [this ] def fixDown (m : Int ): Unit = {
252
274
val inner = this .inner // local copy
253
- val size = inner .length - 1
275
+ val size = innerImpl .length(inner) - 1
254
276
255
277
/* At each step, even though `m` changes, the element moves with it, and
256
278
* hence inner(m) is always the same initial `innerAtM`.
257
279
*/
258
- val innerAtM = inner( m)
280
+ val innerAtM = innerImpl.get(inner, m)
259
281
260
282
@ inline @ tailrec
261
283
def loop (m : Int ): Unit = {
262
284
var j = 2 * m // left child of `m`
263
285
if (j <= size) {
264
- var innerAtJ = inner( j)
286
+ var innerAtJ = innerImpl.get(inner, j)
265
287
266
288
// if the left child is greater than the right child, switch to the right child
267
289
if (j < size) {
268
- val innerAtJPlus1 = inner( j + 1 )
290
+ val innerAtJPlus1 = innerImpl.get(inner, j + 1 )
269
291
if (comp.compare(innerAtJ, innerAtJPlus1) > 0 ) {
270
292
j += 1
271
293
innerAtJ = innerAtJPlus1
@@ -274,13 +296,116 @@ class PriorityQueue[E] private (
274
296
275
297
// if the node `m` is greater than the selected child, swap and recurse
276
298
if (comp.compare(innerAtM, innerAtJ) > 0 ) {
277
- inner(m) = innerAtJ
278
- inner(j) = innerAtM
299
+ innerImpl.set(inner, m, innerAtJ)
300
+ innerImpl.set(inner, j, innerAtM)
279
301
loop(j)
280
302
}
281
303
}
282
304
}
283
305
284
306
loop(m)
285
307
}
308
+
309
+ }
310
+
311
+ object PriorityQueue {
312
+
313
+ /* Get the best available implementation of inner array for the given platform.
314
+ *
315
+ * Use Array[AnyRef] in WebAssembly to avoid JS-interop. In JS, use js.Array.
316
+ * It is resizable by nature, so manual resizing is not needed.
317
+ *
318
+ * `linkTimeIf` is needed here to ensure the optimizer knows
319
+ * there is only one implementation of `InnerArrayImpl`, and de-virtualize/inline
320
+ * the function calls.
321
+ */
322
+
323
+ private val innerImpl : InnerArrayImpl = {
324
+ LinkingInfo .linkTimeIf[InnerArrayImpl ](LinkingInfo .isWebAssembly) {
325
+ InnerArrayImpl .JArrayImpl
326
+ } {
327
+ InnerArrayImpl .JSArrayImpl
328
+ }
329
+ }
330
+
331
+ private sealed abstract class InnerArrayImpl {
332
+ type Repr [E ] <: AnyRef
333
+
334
+ def make [E ](initialCapacity : Int ): Repr [E ]
335
+ def length (v : Repr [_]): Int
336
+ def decLength (v : Repr [_]): Unit
337
+ def get [E ](v : Repr [E ], index : Int ): E
338
+ def set [E ](v : Repr [E ], index : Int , e : E ): Unit
339
+ def push [E ](v : Repr [E ], e : E ): Repr [E ]
340
+ def copyFrom [E ](v : Repr [E ], from : Int ): Repr [E ]
341
+ def clear (v : Repr [_]): Unit
342
+ }
343
+
344
+ private object InnerArrayImpl {
345
+ object JSArrayImpl extends InnerArrayImpl {
346
+ import scala .scalajs .js
347
+
348
+ type Repr [E ] = js.Array [AnyRef ]
349
+
350
+ @ inline def make [E ](_initialCapacity : Int ): Repr [E ] = js.Array [AnyRef ](null )
351
+ @ inline def length (v : Repr [_]): Int = v.length
352
+ @ inline def decLength (v : Repr [_]): Unit =
353
+ v.length = v.length - 1
354
+ @ inline def get [E ](v : Repr [E ], index : Int ): E = v(index).asInstanceOf [E ]
355
+ @ inline def set [E ](v : Repr [E ], index : Int , e : E ): Unit =
356
+ v(index) = e.asInstanceOf [AnyRef ]
357
+ @ inline def push [E ](v : Repr [E ], e : E ): Repr [E ] = {
358
+ v.push(e.asInstanceOf [AnyRef ])
359
+ v
360
+ }
361
+ @ inline def copyFrom [E ](v : Repr [E ], from : Int ): Repr [E ] = v.jsSlice(from - 1 )
362
+ @ inline def clear (v : Repr [_]): Unit =
363
+ v.length = 1
364
+ }
365
+
366
+ /* We store the effective length in the index 0 of the array,
367
+ * which is unused both in JSArrayImpl and in this impl.
368
+ */
369
+ object JArrayImpl extends InnerArrayImpl {
370
+ type Repr [E ] = Array [AnyRef ]
371
+
372
+ @ inline def make [E ](initialCapacity : Int ): Repr [E ] = {
373
+ val v = new Array [AnyRef ](initialCapacity)
374
+ v(0 ) = 1 .asInstanceOf [AnyRef ]
375
+ v
376
+ }
377
+ @ inline def length (v : Repr [_]): Int = v(0 ).asInstanceOf [Int ]
378
+ @ inline def decLength (v : Repr [_]): Unit = {
379
+ val newLength = length(v) - 1
380
+ v(0 ) = newLength.asInstanceOf [AnyRef ]
381
+ v(newLength) = null // free reference for GC
382
+ }
383
+ @ inline def get [E ](v : Repr [E ], index : Int ): E = v(index).asInstanceOf [E ]
384
+ @ inline def set [E ](v : Repr [E ], index : Int , e : E ): Unit =
385
+ v(index) = e.asInstanceOf [AnyRef ]
386
+ @ inline def push [E ](v : Repr [E ], e : E ): Repr [E ] = {
387
+ val l = length(v)
388
+ val minCapacity = l + 1
389
+ val newArr =
390
+ if (v.length < minCapacity)
391
+ Arrays .copyOf(v, roundUpToPowerOfTwo(minCapacity))
392
+ else v
393
+ newArr(l) = e.asInstanceOf [AnyRef ]
394
+ newArr(0 ) = (l + 1 ).asInstanceOf [AnyRef ]
395
+ newArr
396
+ }
397
+ @ inline def copyFrom [E ](v : Repr [E ], from : Int ): Repr [E ] = {
398
+ val elemLength = length(v) - from
399
+ val newArr = new Array [AnyRef ](elemLength + 1 )
400
+ newArr(0 ) = (elemLength + 1 ).asInstanceOf [AnyRef ]
401
+ System .arraycopy(v, from, newArr, 1 , elemLength)
402
+ newArr
403
+ }
404
+ @ inline def clear (v : Repr [_]): Unit = {
405
+ Arrays .fill(v, null )
406
+ v(0 ) = 1 .asInstanceOf [AnyRef ]
407
+ }
408
+ }
409
+ }
410
+
286
411
}
0 commit comments