diff --git a/enumset/src/commonMain/kotlin/EnumSet32.kt b/enumset/src/commonMain/kotlin/EnumSet32.kt index 4c0f6ce..42be9ac 100644 --- a/enumset/src/commonMain/kotlin/EnumSet32.kt +++ b/enumset/src/commonMain/kotlin/EnumSet32.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, OpenSavvy and contributors. + * Copyright (c) 2025-2026, OpenSavvy and contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,6 +64,19 @@ internal class EnumSet32> private constructor( override fun toString(): String = this.joinToString(", ", "[", "]") + override fun equals(other: Any?): Boolean { + if (other === null) return false + if (other === this) return true + if (other is EnumSet32<*>) return entries == other.entries && set == other.set + if (other !is Set<*>) return false + + return size == other.size && + this.containsAll(other) + } + + override fun hashCode(): Int = + sumOf { it.hashCode() } + companion object { internal fun > of(elements: Iterable, entries: EnumEntries): EnumSet32 { diff --git a/enumset/src/commonTest/kotlin/datatypes/EnumSetSuite.kt b/enumset/src/commonTest/kotlin/datatypes/EnumSetSuite.kt index 96dee98..fac07cb 100644 --- a/enumset/src/commonTest/kotlin/datatypes/EnumSetSuite.kt +++ b/enumset/src/commonTest/kotlin/datatypes/EnumSetSuite.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, OpenSavvy and contributors. + * Copyright (c) 2025-2026, OpenSavvy and contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,8 @@ import opensavvy.prepared.suite.prepared import opensavvy.prepared.suite.random.random import opensavvy.prepared.suite.random.randomInt import kotlin.enums.EnumEntries +import kotlin.random.Random +import kotlin.random.nextInt fun > SuiteDsl.testEnumSetValidity( name: String, @@ -151,6 +153,59 @@ fun > SuiteDsl.testEnumSetValidity( } testFullEnumSetValidity(name, entries, set) } + + suite("Identity") { + val itemCounts = listOf( + 0, + 1, + 2, + Random.nextInt(0..entries.size), + entries.size / 2, + entries.size, + ) + + for (itemCount in itemCounts) suite("$itemCount items") { + /** + * A set of size `itemCount` with random items. + * + * This is a standard set, used as a control for this test. + */ + val hashSet by prepared { + val allItems = entries.toMutableList() + random.use { allItems.shuffle() } + + val items = HashSet() + repeat(itemCount) { + items.add(allItems.removeLast()) + } + + items + } + + /** + * The test implementation we want to test, with the same content as `hashSet`. + */ + val testedSet by prepared { + create(hashSet()) + } + + test("Equals") { + check(hashSet() == testedSet()) { "A HashSet should detect that it has the same elements as the tested set" } + check(testedSet() == hashSet()) { "The tested set should detect that it has the same elements as the HashSet" } + } + + test("Equals: fast-paths") { + check(testedSet() == testedSet()) { "The tested set itself is equal to itself" } + check(testedSet() == create(hashSet())) { "The tested set is equal to another set of the same type with the same elements" } + check(!testedSet().equals(null)) + check(testedSet() != Any()) + } + + test("HashCode") { + check(hashSet().hashCode() == testedSet().hashCode()) + } + } + } } fun > SuiteDsl.testEmptyEnumSetValidity( -- 2.51.2 From 71ec9d2ff9d761a62635fa1950005971b784799b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Sat, 7 Feb 2026 20:59:38 +0100 Subject: [PATCH 2/4] feat(enumset): Create MutableEnumSet32 Closes https://gitlab.com/opensavvy/groundwork/pedestal/-/work_items/170 --- enumset/src/commonMain/kotlin/EnumSet.kt | 91 ++++++- enumset/src/commonMain/kotlin/EnumSet32.kt | 95 +++++++ enumset/src/commonTest/kotlin/EnumSetTest.kt | 20 +- .../kotlin/datatypes/EnumSetSuite.kt | 246 ++++++++++++++++++ 4 files changed, 450 insertions(+), 2 deletions(-) diff --git a/enumset/src/commonMain/kotlin/EnumSet.kt b/enumset/src/commonMain/kotlin/EnumSet.kt index 23f98cb..b1bcfbe 100644 --- a/enumset/src/commonMain/kotlin/EnumSet.kt +++ b/enumset/src/commonMain/kotlin/EnumSet.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, OpenSavvy and contributors. + * Copyright (c) 2025-2026, OpenSavvy and contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ package opensavvy.enumset import kotlin.enums.EnumEntries import kotlin.enums.enumEntries +// region Set + @PublishedApi @ExperimentalEnumSetApi internal fun > enumSetOf( @@ -103,3 +105,90 @@ inline fun > enumSetOf( @ExperimentalEnumSetApi inline fun > Set.toEnumSet(): Set = enumSetOf(this, enumEntries()) + +// endregion +// region MutableSet + +@PublishedApi +@ExperimentalEnumSetApi +internal fun > mutableEnumSetOf( + elements: Iterable, + entries: EnumEntries, +): MutableSet { + return when (entries.size) { + 0 -> mutableSetOf() + + in 1..32 -> MutableEnumSet32.of(elements, entries) + + else -> elements.toMutableSet() + } +} + +/** + * Creates a [MutableSet] optimized for storing elements of enumerations. + * + * Depending on the enumeration size, different implementations may be returned. + * + * ### Example + * + * ```kotlin + * enum class Foo { + * A, + * B, + * C, + * } + * + * val foo = mutableEnumSetOf(Foo.A, Foo.B) + * ``` + */ +@ExperimentalEnumSetApi +inline fun > mutableEnumSetOf( + vararg elements: E, +): MutableSet = + mutableEnumSetOf(elements.asIterable()) + +/** + * Creates a [MutableSet] optimized for storing elements of enumerations. + * + * Depending on the enumeration size, different implementations may be returned. + * + * ### Example + * + * ```kotlin + * enum class Foo { + * A, + * B, + * C, + * } + * + * val foo = mutableEnumSetOf(Foo.A, Foo.B) + * ``` + */ +@ExperimentalEnumSetApi +inline fun > mutableEnumSetOf( + elements: Iterable, +): MutableSet = + mutableEnumSetOf(elements, enumEntries()) + +/** + * Copies a regular [Set] into a mutable set optimized for enumerations. + * + * Depending on the enumeration size, different implementations may be returned. + * + * ### Example + * + * ```kotlin + * enum class Foo { + * A, + * B, + * C, + * } + * + * val foo = setOf(Foo.A, Foo.B).toMutableEnumSet() + * ``` + */ +@ExperimentalEnumSetApi +inline fun > Set.toMutableEnumSet(): MutableSet = + mutableEnumSetOf(this, enumEntries()) + +// endregion diff --git a/enumset/src/commonMain/kotlin/EnumSet32.kt b/enumset/src/commonMain/kotlin/EnumSet32.kt index 42be9ac..12cb90a 100644 --- a/enumset/src/commonMain/kotlin/EnumSet32.kt +++ b/enumset/src/commonMain/kotlin/EnumSet32.kt @@ -89,3 +89,98 @@ internal class EnumSet32> private constructor( } } } + +@ExperimentalEnumSetApi +internal class MutableEnumSet32> private constructor( + private val set: MutableBitSet32, + private val entries: EnumEntries, +) : MutableSet { + + init { + require(entries.size < 32) { "A MutableEnumSet32 cannot be instantiated for this enum because it has more than 32 entries" } + } + + override val size: Int + get() = set.size + + override fun isEmpty(): Boolean = + set.isEmpty() + + override fun contains(element: E): Boolean = + set.contains(element.ordinal) + + override fun iterator(): MutableIterator = + MutableEnumSet32Iterator(set.iterator(), entries) + + override fun add(element: E): Boolean = + set.add(element.ordinal) + + override fun remove(element: E): Boolean = + set.remove(element.ordinal) + + override fun addAll(elements: Collection): Boolean = + set.addAll(elements.map { it.ordinal }) + + override fun removeAll(elements: Collection): Boolean = + set.removeAll(elements.map { it.ordinal }) + + override fun retainAll(elements: Collection): Boolean = + set.retainAll(elements.map { it.ordinal }) + + override fun clear() { + set.clear() + } + + private class MutableEnumSet32Iterator>( + private val iter: MutableIterator, + private val entries: EnumEntries, + ) : MutableIterator { + override fun hasNext(): Boolean = + iter.hasNext() + + override fun next(): E = + entries[iter.next()] + + override fun remove() { + iter.remove() + } + } + + override fun containsAll(elements: Collection): Boolean { + val elementsMask = MutableBitSet32() + for (element in elements) { + val ordinal = element.ordinal + elementsMask.add(ordinal) + } + + return set.containsAll(elementsMask) + } + + override fun toString(): String = + this.joinToString(", ", "[", "]") + + override fun equals(other: Any?): Boolean { + if (other === null) return false + if (other === this) return true + if (other is MutableEnumSet32<*>) return entries == other.entries && set == other.set + if (other !is Set<*>) return false + + return size == other.size && + this.containsAll(other) + } + + override fun hashCode(): Int = + sumOf { it.hashCode() } + + companion object { + + internal fun > of(elements: Iterable, entries: EnumEntries): MutableEnumSet32 { + val elementsSet = MutableBitSet32() + for (element in elements) { + elementsSet.add(element.ordinal) + } + + return MutableEnumSet32(elementsSet, entries) + } + } +} diff --git a/enumset/src/commonTest/kotlin/EnumSetTest.kt b/enumset/src/commonTest/kotlin/EnumSetTest.kt index 097403d..c08f4c7 100644 --- a/enumset/src/commonTest/kotlin/EnumSetTest.kt +++ b/enumset/src/commonTest/kotlin/EnumSetTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, OpenSavvy and contributors. + * Copyright (c) 2025-2026, OpenSavvy and contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ package opensavvy.enumset import opensavvy.enumset.datatypes.testEmptyEnumSetValidity import opensavvy.enumset.datatypes.testEnumSetValidity +import opensavvy.enumset.datatypes.testMutableEnumSetValidity import opensavvy.prepared.runner.testballoon.preparedSuite import opensavvy.prepared.suite.assertions.checkThrows @@ -57,3 +58,20 @@ val EnumSetTest by preparedSuite { checkThrows { EnumSet32.of(emptyList(), Enum36.entries) } } } + +val MutableEnumSetTest by preparedSuite { + testEmptyEnumSetValidity("of 0 elements", Enum0.entries) { mutableEnumSetOf() } + testMutableEnumSetValidity("of 2 elements", Enum2.entries) { mutableEnumSetOf(it) } + testMutableEnumSetValidity("of 7 elements", Enum7.entries) { mutableEnumSetOf(it) } + testMutableEnumSetValidity("of 26 elements", Enum26.entries) { mutableEnumSetOf(it) } + testMutableEnumSetValidity("of 36 elements", Enum36.entries) { mutableEnumSetOf(it) } + + testEmptyEnumSetValidity("MutableEnumSet32 of 0 elements", Enum0.entries) { MutableEnumSet32.of(emptyList(), Enum0.entries) } + testMutableEnumSetValidity("MutableEnumSet32 of 2 elements", Enum2.entries) { MutableEnumSet32.of(it, Enum2.entries) } + testMutableEnumSetValidity("MutableEnumSet32 of 7 elements", Enum7.entries) { MutableEnumSet32.of(it, Enum7.entries) } + testMutableEnumSetValidity("MutableEnumSet32 of 26 elements", Enum26.entries) { MutableEnumSet32.of(it, Enum26.entries) } + + test("Cannot instantiate a MutableEnumSet32 with an enum that has more than 32 elements") { + checkThrows { MutableEnumSet32.of(emptyList(), Enum36.entries) } + } +} diff --git a/enumset/src/commonTest/kotlin/datatypes/EnumSetSuite.kt b/enumset/src/commonTest/kotlin/datatypes/EnumSetSuite.kt index fac07cb..02c3613 100644 --- a/enumset/src/commonTest/kotlin/datatypes/EnumSetSuite.kt +++ b/enumset/src/commonTest/kotlin/datatypes/EnumSetSuite.kt @@ -18,6 +18,8 @@ package opensavvy.enumset.datatypes +import com.benwoodworth.parameterize.parameterOf +import com.benwoodworth.parameterize.parameterize import opensavvy.enumset.EnumEntriesSet import opensavvy.enumset.EnumSet32 import opensavvy.enumset.ExperimentalEnumSetApi @@ -212,6 +214,17 @@ fun > SuiteDsl.testEmptyEnumSetValidity( name: String, entries: EnumEntries, create: () -> Set, +) { + val emptySet by prepared { + create() + } + testEmptyEnumSetValidity(name, entries, emptySet) +} + +fun > SuiteDsl.testEmptyEnumSetValidity( + name: String, + entries: EnumEntries, + create: Prepared>, ) = suite("Empty $name") { val set by prepared { create() @@ -290,3 +303,236 @@ private suspend fun > TestDsl.buildSetOfSize( add(entries.random(random.accessUnsafe())) } } + +fun > SuiteDsl.testMutableEnumSetValidity( + name: String, + entries: EnumEntries, + create: (Iterable) -> MutableSet, +) = suite("MutableSet $name") { + /** + * Shuffled `entries` to easily test non-deterministic order. + * + * Each test will see the elements in a different order. + */ + val shuffledEntries by prepared { + val result = entries.toMutableList() + random.use { r -> result.shuffle(r) } + result + } + + testEnumSetValidity(name, entries, create) + + suite("clear") { + val clearedSet by prepared { + val set = create(listOf(shuffledEntries()[0], shuffledEntries()[1])) + set.clear() + set + } + + testEmptyEnumSetValidity(name, entries, clearedSet) + } + + suite("add") { + test("Add an element to the empty set") { + val set = create(emptyList()) + val element = entries[entries.size / 2] + check(set.add(element)) + check(element in set) + check(set.size == 1) + } + + if (entries.size >= 2) { + test("Add an element to a set of one element") { + val set = create(listOf(shuffledEntries()[0])) + val element = shuffledEntries()[1] + check(set.add(element)) + check(element in set) + check(shuffledEntries()[0] in set) + check(set.size == 2) + } + } + + if (entries.size >= 3) { + test("Add an element to set with two elements") { + val set = create(listOf(shuffledEntries()[0], shuffledEntries()[1])) + val element = shuffledEntries()[2] + check(set.add(element)) + check(element in set) + check(shuffledEntries()[0] in set) + check(shuffledEntries()[1] in set) + check(set.size == 3) + } + } + + test("Add an element that is already present") { + val alreadyInSet = shuffledEntries()[0] + val element = shuffledEntries()[1] + val set = create(listOf(alreadyInSet, element)) + check(!set.add(element)) + check(alreadyInSet in set) + check(element in set) + check(set.size == 2) + } + } + + suite("addAll") { + parameterize { + val currentSize by parameterOf(0, 1, 2, 12.coerceAtMost(entries.size - 1), entries.size - 1) + + val set by prepared { + create(buildSetOfSize(currentSize, entries)) + } + + val addingSize by parameterOf(0, 1, entries.size.coerceAtMost(4)) + + val addingValues by prepared { + buildSetOfSize(addingSize, entries) + } + + val addingSet by parameterOf( + prepared("set of any type") { + addingValues() + }, + prepared("set of same type") { + create(addingValues()) + } + ) + + test("Adding $addingSize elements to a set of size $currentSize (${addingSet.name})") { + val set = set() + + val willBeAdded = addingSet().filter { it !in set } + + check(set.addAll(addingSet()) == willBeAdded.isNotEmpty()) + check(set.size == currentSize + willBeAdded.size) + } + } + } + + suite("remove") { + test("Remove an element from the empty set") { + val set = create(emptyList()) + val element = shuffledEntries()[0] + check(!set.remove(element)) + check(set.isEmpty()) + } + + test("Remove the only element in a set") { + val element = shuffledEntries()[0] + val set = create(listOf(element)) + check(set.remove(element)) + check(set.isEmpty()) + } + + if (entries.size >= 3) { + test("Remove an element that is not contained in the set") { + val set = create(listOf(shuffledEntries()[0], shuffledEntries()[1])) + val element = shuffledEntries()[2] + check(!set.remove(element)) + check(!set.isEmpty()) + check(set.size == 2) + } + } + + if (entries.size >= 3) { + test("Remove an element in a set with multiple elements") { + val element = shuffledEntries()[2] + val set = create(listOf(shuffledEntries()[0], shuffledEntries()[1], element)) + check(set.remove(element)) + check(!set.isEmpty()) + check(set.size == 2) + } + } + } + + suite("removeAll") { + parameterize { + val removingSize by parameterOf(0, 1, entries.size.coerceAtMost(4)) + + val currentSize by parameterOf(0, 1, 2, 12.coerceAtMost(entries.size - removingSize), entries.size - removingSize) + + val set by prepared { + create(buildSetOfSize(currentSize, entries)) + } + + val removingValues by prepared { + buildSetOfSize(removingSize, entries) + } + + val removingSet by parameterOf( + prepared("set of any type") { + removingValues() + }, + prepared("set of same type") { + create(removingValues()) + } + ) + + test("Removing $removingSize elements from a set of size $currentSize (${removingSet.name})") { + val set = set() + + val willBeRemoved = removingSet().filter { it in set } + + check(set.removeAll(removingSet()) == willBeRemoved.isNotEmpty()) + check(set.size == currentSize - willBeRemoved.size) + } + } + } + + if (entries.size >= 3) { + suite("Iterator") { + test("Remove an item while iterating") { + val set = create(listOf(entries[0], entries[1], entries[2])) + println("Iterating through set $set") + val iter = set.iterator() + + check(iter.hasNext()) + check(iter.next() == entries[0]) + + check(iter.hasNext()) + iter.remove() + + println("After removal: $set") + + check(iter.hasNext()) + check(iter.next() == entries[2]) + } + } + } + + if (entries.size >= 3) { + suite("retainAll") { + test("Remove all elements") { + val set = create(listOf(shuffledEntries()[0], shuffledEntries()[1], shuffledEntries()[2])) + + check(set.retainAll(emptySet())) + check(set.isEmpty()) + } + + if (entries.size >= 4) { + test("Remove a single element") { + val element1 = shuffledEntries()[0] + val element2 = shuffledEntries()[1] + val element3 = shuffledEntries()[2] + val element4 = shuffledEntries()[3] + val set = create(listOf(element1, element2, element3)) + + check(set.retainAll(setOf(element4, element1, element3))) + check(!set.isEmpty()) + check(set.size == 2) + } + } + + test("Remove no elements") { + val element1 = shuffledEntries()[0] + val element2 = shuffledEntries()[1] + val element3 = shuffledEntries()[2] + val set = create(listOf(element1, element2, element3)) + + check(!set.retainAll(setOf(element1, element2, element3))) + check(!set.isEmpty()) + check(set.size == 3) + } + } + } +} -- 2.51.2 From 1de142487f27d472daf22c04a4eebd38fd7f8758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Sat, 7 Feb 2026 22:19:17 +0100 Subject: [PATCH 3/4] fix(enumset): Fix the iterator().remove() method According to the specification, it wasn't always removing the correct element. --- enumset/src/commonMain/kotlin/Bit32.kt | 9 +++-- .../kotlin/datatypes/EnumSetSuite.kt | 3 +- .../commonTest/kotlin/datatypes/SetSuite.kt | 33 ++++++++++++++++++- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/enumset/src/commonMain/kotlin/Bit32.kt b/enumset/src/commonMain/kotlin/Bit32.kt index 4430d83..52198ff 100644 --- a/enumset/src/commonMain/kotlin/Bit32.kt +++ b/enumset/src/commonMain/kotlin/Bit32.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, OpenSavvy and contributors. + * Copyright (c) 2025-2026, OpenSavvy and contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -209,6 +209,7 @@ class MutableBitSet32 internal constructor(private var storage: BitSet32) : Muta private val set: MutableBitSet32, ) : MutableIterator { private var index = 0 + private var lastIndexReturned = -1 override fun hasNext(): Boolean { while (index < 32) { @@ -227,11 +228,15 @@ class MutableBitSet32 internal constructor(private var storage: BitSet32) : Muta throw NoSuchElementException() } + lastIndexReturned = index return index++ } override fun remove() { - set.remove(index) + check(lastIndexReturned != -1) { "Cannot remove an item before next() is called at least once" } + check(lastIndexReturned != -2) { "Cannot remove an item multiple times. remove() removes the last item viewed by next(), but it has already been removed" } + set.remove(lastIndexReturned) + lastIndexReturned = -2 } } diff --git a/enumset/src/commonTest/kotlin/datatypes/EnumSetSuite.kt b/enumset/src/commonTest/kotlin/datatypes/EnumSetSuite.kt index 02c3613..ecd6f9f 100644 --- a/enumset/src/commonTest/kotlin/datatypes/EnumSetSuite.kt +++ b/enumset/src/commonTest/kotlin/datatypes/EnumSetSuite.kt @@ -488,9 +488,10 @@ fun > SuiteDsl.testMutableEnumSetValidity( check(iter.hasNext()) check(iter.next() == entries[0]) + iter.remove() check(iter.hasNext()) - iter.remove() + check(iter.next() == entries[1]) println("After removal: $set") diff --git a/enumset/src/commonTest/kotlin/datatypes/SetSuite.kt b/enumset/src/commonTest/kotlin/datatypes/SetSuite.kt index e27362a..8316e69 100644 --- a/enumset/src/commonTest/kotlin/datatypes/SetSuite.kt +++ b/enumset/src/commonTest/kotlin/datatypes/SetSuite.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, OpenSavvy and contributors. + * Copyright (c) 2025-2026, OpenSavvy and contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -405,9 +405,40 @@ fun SuiteDsl.testMutableSetValidity( check(iter.hasNext()) iter.remove() + // 'remove' deletes the last item returned by 'next', NOT the last item as seen by hasNext + check(5 !in set) + check(7 in set) + check(9 in set) + check(set.size == 2) + + check(iter.next() == 7) + check(iter.hasNext()) check(iter.next() == 9) } + + test("Cannot remove an item before reading the first element") { + val set = create(arrayOf(5, 7, 9)) + val iter = set.iterator() + check(iter.hasNext()) + checkThrows { + iter.remove() + } + } + + test("Cannot remove an item twice") { + val set = create(arrayOf(5, 7, 9)) + println("Iterating through set $set") + val iter = set.iterator() + + check(iter.hasNext()) + check(iter.next() == 5) + iter.remove() + + checkThrows { + iter.remove() + } + } } suite("retainAll") { -- 2.51.2 From e28931e2bdaef15e65072c99505866ef1151e892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Sat, 7 Feb 2026 22:37:39 +0100 Subject: [PATCH 4/4] perf(enumset): Compare bit sets to other bit sets when possible --- enumset/src/commonMain/kotlin/EnumSet32.kt | 41 +++++++++------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/enumset/src/commonMain/kotlin/EnumSet32.kt b/enumset/src/commonMain/kotlin/EnumSet32.kt index 12cb90a..6e00c1d 100644 --- a/enumset/src/commonMain/kotlin/EnumSet32.kt +++ b/enumset/src/commonMain/kotlin/EnumSet32.kt @@ -18,6 +18,15 @@ package opensavvy.enumset import kotlin.enums.EnumEntries +@ExperimentalEnumSetApi +private fun > Iterable.toBitSet(): MutableBitSet32 { + val elements = MutableBitSet32() + for (element in this) { + elements.add(element.ordinal) + } + return elements +} + @ExperimentalEnumSetApi internal class EnumSet32> private constructor( private val set: BitSet32, @@ -52,13 +61,7 @@ internal class EnumSet32> private constructor( } override fun containsAll(elements: Collection): Boolean { - val elementsMask = MutableBitSet32() - for (element in elements) { - val ordinal = element.ordinal - elementsMask.add(ordinal) - } - - return set.containsAll(elementsMask) + return set.containsAll(elements.toBitSet()) } override fun toString(): String = @@ -80,12 +83,7 @@ internal class EnumSet32> private constructor( companion object { internal fun > of(elements: Iterable, entries: EnumEntries): EnumSet32 { - val elementsSet = MutableBitSet32() - for (element in elements) { - elementsSet.add(element.ordinal) - } - - return EnumSet32(elementsSet.toSet(), entries) + return EnumSet32(elements.toBitSet().toSet(), entries) } } } @@ -119,13 +117,13 @@ internal class MutableEnumSet32> private constructor( set.remove(element.ordinal) override fun addAll(elements: Collection): Boolean = - set.addAll(elements.map { it.ordinal }) + set.addAll(elements.toBitSet()) override fun removeAll(elements: Collection): Boolean = - set.removeAll(elements.map { it.ordinal }) + set.removeAll(elements.toBitSet()) override fun retainAll(elements: Collection): Boolean = - set.retainAll(elements.map { it.ordinal }) + set.retainAll(elements.toBitSet()) override fun clear() { set.clear() @@ -146,15 +144,8 @@ internal class MutableEnumSet32> private constructor( } } - override fun containsAll(elements: Collection): Boolean { - val elementsMask = MutableBitSet32() - for (element in elements) { - val ordinal = element.ordinal - elementsMask.add(ordinal) - } - - return set.containsAll(elementsMask) - } + override fun containsAll(elements: Collection): Boolean = + set.containsAll(elements.toBitSet()) override fun toString(): String = this.joinToString(", ", "[", "]")