Skip to content

Commit b561c8d

Browse files
committed
Clone scala/collection/[im]mutable/BitSet.scala
1 parent 3c1da56 commit b561c8d

File tree

2 files changed

+749
-0
lines changed

2 files changed

+749
-0
lines changed
Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
/*
2+
* Scala (https://www.scala-lang.org)
3+
*
4+
* Copyright EPFL and Lightbend, Inc.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (http://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package scala
14+
package collection
15+
package immutable
16+
17+
import BitSetOps.{LogWL, updateArray}
18+
import mutable.Builder
19+
import scala.annotation.{implicitNotFound, nowarn}
20+
21+
/** A class for immutable bitsets.
22+
* $bitsetinfo
23+
* @see [[https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#immutable-bitsets "Scala's Collection Library overview"]]
24+
* section on `Immutable BitSets` for more information.
25+
*
26+
* @define Coll `immutable.BitSet`
27+
* @define coll immutable bitset
28+
*/
29+
sealed abstract class BitSet
30+
extends AbstractSet[Int]
31+
with SortedSet[Int]
32+
with SortedSetOps[Int, SortedSet, BitSet]
33+
with StrictOptimizedSortedSetOps[Int, SortedSet, BitSet]
34+
with collection.BitSet
35+
with collection.BitSetOps[BitSet]
36+
with Serializable {
37+
38+
override def unsorted: Set[Int] = this
39+
40+
override protected def fromSpecific(coll: IterableOnce[Int]): BitSet = bitSetFactory.fromSpecific(coll)
41+
override protected def newSpecificBuilder: Builder[Int, BitSet] = bitSetFactory.newBuilder
42+
override def empty: BitSet = bitSetFactory.empty
43+
44+
def bitSetFactory = BitSet
45+
46+
protected[collection] def fromBitMaskNoCopy(elems: Array[Long]): BitSet = BitSet.fromBitMaskNoCopy(elems)
47+
48+
def incl(elem: Int): BitSet = {
49+
require(elem >= 0, "bitset element must be >= 0")
50+
if (contains(elem)) this
51+
else {
52+
val idx = elem >> LogWL
53+
updateWord(idx, word(idx) | (1L << elem))
54+
}
55+
}
56+
57+
def excl(elem: Int): BitSet = {
58+
require(elem >= 0, "bitset element must be >= 0")
59+
if (contains(elem)) {
60+
val idx = elem >> LogWL
61+
updateWord(idx, word(idx) & ~(1L << elem))
62+
} else this
63+
}
64+
65+
/** Update word at index `idx`; enlarge set if `idx` outside range of set.
66+
*/
67+
protected def updateWord(idx: Int, w: Long): BitSet
68+
69+
override def map(f: Int => Int): BitSet = strictOptimizedMap(newSpecificBuilder, f)
70+
override def map[B](f: Int => B)(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] =
71+
super[StrictOptimizedSortedSetOps].map(f)
72+
73+
override def flatMap(f: Int => IterableOnce[Int]): BitSet = strictOptimizedFlatMap(newSpecificBuilder, f)
74+
override def flatMap[B](f: Int => IterableOnce[B])(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] =
75+
super[StrictOptimizedSortedSetOps].flatMap(f)
76+
77+
override def collect(pf: PartialFunction[Int, Int]): BitSet = strictOptimizedCollect(newSpecificBuilder, pf)
78+
override def collect[B](pf: scala.PartialFunction[Int, B])(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] =
79+
super[StrictOptimizedSortedSetOps].collect(pf)
80+
81+
// necessary for disambiguation
82+
override def zip[B](that: scala.IterableOnce[B])(implicit @implicitNotFound(collection.BitSet.zipOrdMsg) ev: Ordering[(Int, B)]): SortedSet[(Int, B)] =
83+
super.zip(that)
84+
85+
protected[this] def writeReplace(): AnyRef = new BitSet.SerializationProxy(this)
86+
}
87+
88+
/**
89+
* $factoryInfo
90+
* @define Coll `immutable.BitSet`
91+
* @define coll immutable bitset
92+
*/
93+
@nowarn("cat=deprecation&msg=Implementation classes of BitSet should not be accessed directly")
94+
@SerialVersionUID(3L)
95+
object BitSet extends SpecificIterableFactory[Int, BitSet] {
96+
97+
def fromSpecific(it: scala.collection.IterableOnce[Int]): BitSet =
98+
it match {
99+
case bs: BitSet => bs
100+
case _ => (newBuilder ++= it).result()
101+
}
102+
103+
final val empty: BitSet = new BitSet1(0L)
104+
105+
def newBuilder: Builder[Int, BitSet] =
106+
mutable.BitSet.newBuilder.mapResult(bs => fromBitMaskNoCopy(bs.elems))
107+
108+
private def createSmall(a: Long, b: Long): BitSet = if (b == 0L) new BitSet1(a) else new BitSet2(a, b)
109+
110+
/** A bitset containing all the bits in an array */
111+
def fromBitMask(elems: Array[Long]): BitSet = {
112+
val len = elems.length
113+
if (len == 0) empty
114+
else if (len == 1) new BitSet1(elems(0))
115+
else if (len == 2) createSmall(elems(0), elems(1))
116+
else {
117+
val a = java.util.Arrays.copyOf(elems, len)
118+
new BitSetN(a)
119+
}
120+
}
121+
122+
/** A bitset containing all the bits in an array, wrapping the existing
123+
* array without copying.
124+
*/
125+
def fromBitMaskNoCopy(elems: Array[Long]): BitSet = {
126+
val len = elems.length
127+
if (len == 0) empty
128+
else if (len == 1) new BitSet1(elems(0))
129+
else if (len == 2) createSmall(elems(0), elems(1))
130+
else new BitSetN(elems)
131+
}
132+
133+
@deprecated("Implementation classes of BitSet should not be accessed directly", "2.13.0")
134+
class BitSet1(val elems: Long) extends BitSet {
135+
protected[collection] def nwords = 1
136+
protected[collection] def word(idx: Int) = if (idx == 0) elems else 0L
137+
protected[collection] def updateWord(idx: Int, w: Long): BitSet =
138+
if (idx == 0) new BitSet1(w)
139+
else if (idx == 1) createSmall(elems, w)
140+
else this.fromBitMaskNoCopy(updateArray(Array(elems), idx, w))
141+
142+
143+
override def diff(other: collection.Set[Int]): BitSet = other match {
144+
case bs: collection.BitSet => bs.nwords match {
145+
case 0 => this
146+
case _ =>
147+
val newElems = elems & ~bs.word(0)
148+
if (newElems == 0L) this.empty else new BitSet1(newElems)
149+
}
150+
case _ => super.diff(other)
151+
}
152+
153+
override def filterImpl(pred: Int => Boolean, isFlipped: Boolean): BitSet = {
154+
val _elems = BitSetOps.computeWordForFilter(pred, isFlipped, elems, 0)
155+
if (_elems == 0L) this.empty else new BitSet1(_elems)
156+
}
157+
}
158+
159+
@deprecated("Implementation classes of BitSet should not be accessed directly", "2.13.0")
160+
class BitSet2(val elems0: Long, val elems1: Long) extends BitSet {
161+
protected[collection] def nwords = 2
162+
protected[collection] def word(idx: Int) = if (idx == 0) elems0 else if (idx == 1) elems1 else 0L
163+
protected[collection] def updateWord(idx: Int, w: Long): BitSet =
164+
if (idx == 0) new BitSet2(w, elems1)
165+
else if (idx == 1) createSmall(elems0, w)
166+
else this.fromBitMaskNoCopy(updateArray(Array(elems0, elems1), idx, w))
167+
168+
169+
override def diff(other: collection.Set[Int]): BitSet = other match {
170+
case bs: collection.BitSet => bs.nwords match {
171+
case 0 => this
172+
case 1 =>
173+
new BitSet2(elems0 & ~bs.word(0), elems1)
174+
case _ =>
175+
val _elems0 = elems0 & ~bs.word(0)
176+
val _elems1 = elems1 & ~bs.word(1)
177+
178+
if (_elems1 == 0L) {
179+
if (_elems0 == 0L) {
180+
this.empty
181+
} else {
182+
new BitSet1(_elems0)
183+
}
184+
} else {
185+
new BitSet2(_elems0, _elems1)
186+
}
187+
}
188+
case _ => super.diff(other)
189+
}
190+
191+
override def filterImpl(pred: Int => Boolean, isFlipped: Boolean): BitSet = {
192+
val _elems0 = BitSetOps.computeWordForFilter(pred, isFlipped, elems0, 0)
193+
val _elems1 = BitSetOps.computeWordForFilter(pred, isFlipped, elems1, 1)
194+
195+
if (_elems1 == 0L) {
196+
if (_elems0 == 0L) {
197+
this.empty
198+
}
199+
else new BitSet1(_elems0)
200+
}
201+
else new BitSet2(_elems0, _elems1)
202+
}
203+
}
204+
205+
@deprecated("Implementation classes of BitSet should not be accessed directly", "2.13.0")
206+
class BitSetN(val elems: Array[Long]) extends BitSet {
207+
protected[collection] def nwords = elems.length
208+
209+
protected[collection] def word(idx: Int) = if (idx < nwords) elems(idx) else 0L
210+
211+
protected[collection] def updateWord(idx: Int, w: Long): BitSet = this.fromBitMaskNoCopy(updateArray(elems, idx, w))
212+
213+
override def diff(that: collection.Set[Int]): BitSet = that match {
214+
case bs: collection.BitSet =>
215+
/*
216+
* Algorithm:
217+
*
218+
* We iterate, word-by-word, backwards from the shortest of the two bitsets (this, or bs) i.e. the one with
219+
* the fewer words. Two extra concerns for optimization are described below.
220+
*
221+
* Array Shrinking:
222+
* If `this` is not longer than `bs`, then since we must iterate through the full array of words,
223+
* we can track the new highest index word which is non-zero, at little additional cost. At the end, the new
224+
* Array[Long] allocated for the returned BitSet will only be of size `maxNonZeroIndex + 1`
225+
*
226+
* Tracking Changes:
227+
* If the two sets are disjoint, then we can return `this`. Therefor, until at least one change is detected,
228+
* we check each word for if it has changed from its corresponding word in `this`. Once a single change is
229+
* detected, we stop checking because the cost of the new Array must be paid anyways.
230+
*/
231+
232+
val bsnwords = bs.nwords
233+
val thisnwords = nwords
234+
if (bsnwords >= thisnwords) {
235+
// here, we may have opportunity to shrink the size of the array
236+
// so, track the highest index which is non-zero. That ( + 1 ) will be our new array length
237+
var i = thisnwords - 1
238+
var currentWord = 0L
239+
// if there are never any changes, we can return `this` at the end
240+
var anyChanges = false
241+
while (i >= 0 && currentWord == 0L) {
242+
val oldWord = word(i)
243+
currentWord = oldWord & ~bs.word(i)
244+
anyChanges ||= currentWord != oldWord
245+
i -= 1
246+
}
247+
if (i < 0) {
248+
// all indices >= 0 have had result 0, so the bitset is empty
249+
this.empty
250+
} else {
251+
val minimumNonZeroIndex: Int = i + 1
252+
while (!anyChanges && i >= 0) {
253+
val oldWord = word(i)
254+
currentWord = oldWord & ~bs.word(i)
255+
anyChanges ||= currentWord != oldWord
256+
i -= 1
257+
}
258+
if (anyChanges) {
259+
if (minimumNonZeroIndex == -1) {
260+
this.empty
261+
} else if (minimumNonZeroIndex == 0) {
262+
new BitSet1(currentWord)
263+
} else if (minimumNonZeroIndex == 1) {
264+
new BitSet2(word(0) & ~bs.word(0), currentWord)
265+
} else {
266+
val newArray = elems.take(minimumNonZeroIndex + 1)
267+
newArray(i + 1) = currentWord
268+
while (i >= 0) {
269+
newArray(i) = word(i) & ~bs.word(i)
270+
i -= 1
271+
}
272+
this.fromBitMaskNoCopy(newArray)
273+
}
274+
} else {
275+
this
276+
}
277+
}
278+
} else {
279+
var i = bsnwords - 1
280+
var anyChanges = false
281+
var currentWord = 0L
282+
while (i >= 0 && !anyChanges) {
283+
val oldWord = word(i)
284+
currentWord = oldWord & ~bs.word(i)
285+
anyChanges ||= currentWord != oldWord
286+
i -= 1
287+
}
288+
if (anyChanges) {
289+
val newElems = elems.clone()
290+
newElems(i + 1) = currentWord
291+
while (i >= 0) {
292+
newElems(i) = word(i) & ~bs.word(i)
293+
i -= 1
294+
}
295+
this.fromBitMaskNoCopy(newElems)
296+
} else {
297+
this
298+
}
299+
}
300+
case _ => super.diff(that)
301+
}
302+
303+
304+
override def filterImpl(pred: Int => Boolean, isFlipped: Boolean): BitSet = {
305+
// here, we may have opportunity to shrink the size of the array
306+
// so, track the highest index which is non-zero. That ( + 1 ) will be our new array length
307+
var i = nwords - 1
308+
var currentWord = 0L
309+
// if there are never any changes, we can return `this` at the end
310+
var anyChanges = false
311+
while (i >= 0 && currentWord == 0L) {
312+
val oldWord = word(i)
313+
currentWord = BitSetOps.computeWordForFilter(pred, isFlipped, oldWord, i)
314+
anyChanges ||= currentWord != oldWord
315+
i -= 1
316+
}
317+
if (i < 0) {
318+
// all indices >= 0 have had result 0, so the bitset is empty
319+
if (currentWord == 0) this.empty else this.fromBitMaskNoCopy(Array(currentWord))
320+
} else {
321+
val minimumNonZeroIndex: Int = i + 1
322+
while (!anyChanges && i >= 0) {
323+
val oldWord = word(i)
324+
currentWord = BitSetOps.computeWordForFilter(pred, isFlipped, oldWord, i)
325+
anyChanges ||= currentWord != oldWord
326+
i -= 1
327+
}
328+
if (anyChanges) {
329+
if (minimumNonZeroIndex == -1) {
330+
this.empty
331+
} else if (minimumNonZeroIndex == 0) {
332+
new BitSet1(currentWord)
333+
} else if (minimumNonZeroIndex == 1) {
334+
new BitSet2(BitSetOps.computeWordForFilter(pred, isFlipped, word(0), 0), currentWord)
335+
} else {
336+
val newArray = elems.take(minimumNonZeroIndex + 1)
337+
newArray(i + 1) = currentWord
338+
while (i >= 0) {
339+
newArray(i) = BitSetOps.computeWordForFilter(pred, isFlipped, word(i), i)
340+
i -= 1
341+
}
342+
this.fromBitMaskNoCopy(newArray)
343+
}
344+
} else {
345+
this
346+
}
347+
}
348+
}
349+
350+
override def toBitMask: Array[Long] = elems.clone()
351+
}
352+
353+
@SerialVersionUID(3L)
354+
private final class SerializationProxy(coll: BitSet) extends scala.collection.BitSet.SerializationProxy(coll) {
355+
protected[this] def readResolve(): Any = BitSet.fromBitMaskNoCopy(elems)
356+
}
357+
}

0 commit comments

Comments
 (0)