Skip to content

Commit b635d5c

Browse files
authored
Replaces persistent collections with immutable collections (#101)
* Replaces kotlinx.collections.immutable dependency with custom immutable collections * Clean up unused code * Use Immutable collection adapters from kotlinx.collections.immutables * Add build.gradle.kts changes forgotten from last commit
1 parent eb6636f commit b635d5c

24 files changed

+483
-201
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ val isReleaseVersion: Boolean = !version.toString().endsWith("SNAPSHOT")
3030
dependencies {
3131
api("com.amazon.ion:ion-java:[1.4.0,)")
3232
compileOnly("com.amazon.ion:ion-java:1.4.0")
33-
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
3433
implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.4")
34+
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
3535
testImplementation("org.jetbrains.kotlin:kotlin-test")
3636
testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.2")
3737
testImplementation("org.junit.jupiter:junit-jupiter-params:5.6.2")

src/main/kotlin/com/amazon/ionelement/api/Ion.kt

Lines changed: 39 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package com.amazon.ionelement.api
1818

1919
import com.amazon.ion.Decimal
2020
import com.amazon.ion.Timestamp
21-
import com.amazon.ionelement.impl.*
2221
import com.amazon.ionelement.impl.BigIntIntElementImpl
2322
import com.amazon.ionelement.impl.BlobElementImpl
2423
import com.amazon.ionelement.impl.BoolElementImpl
@@ -34,12 +33,9 @@ import com.amazon.ionelement.impl.StructElementImpl
3433
import com.amazon.ionelement.impl.StructFieldImpl
3534
import com.amazon.ionelement.impl.SymbolElementImpl
3635
import com.amazon.ionelement.impl.TimestampElementImpl
36+
import com.amazon.ionelement.impl.collections.*
3737
import java.math.BigInteger
3838
import java.util.function.Consumer
39-
import kotlinx.collections.immutable.PersistentList
40-
import kotlinx.collections.immutable.persistentListOf
41-
import kotlinx.collections.immutable.toPersistentList
42-
import kotlinx.collections.immutable.toPersistentMap
4339

4440
internal typealias Annotations = List<String>
4541

@@ -127,8 +123,8 @@ public fun ionString(
127123
metas: MetaContainer = emptyMetaContainer()
128124
): StringElement = StringElementImpl(
129125
value = s,
130-
annotations = annotations.toEmptyOrPersistentList(),
131-
metas = metas.toPersistentMap()
126+
annotations = annotations.toImmutableList(),
127+
metas = metas.toImmutableMap()
132128
)
133129
/** Creates a [StringElement] that represents an Ion `symbol`. */
134130
public fun ionString(
@@ -144,8 +140,8 @@ public fun ionSymbol(
144140
metas: MetaContainer = emptyMetaContainer()
145141
): SymbolElement = SymbolElementImpl(
146142
value = s,
147-
annotations = annotations.toEmptyOrPersistentList(),
148-
metas = metas.toPersistentMap()
143+
annotations = annotations.toImmutableList(),
144+
metas = metas.toImmutableMap()
149145
)
150146

151147
/** Creates a [SymbolElement] that represents an Ion `symbol`. */
@@ -162,8 +158,8 @@ public fun ionTimestamp(
162158
metas: MetaContainer = emptyMetaContainer()
163159
): TimestampElement = TimestampElementImpl(
164160
timestampValue = Timestamp.valueOf(s),
165-
annotations = annotations.toEmptyOrPersistentList(),
166-
metas = metas.toPersistentMap()
161+
annotations = annotations.toImmutableList(),
162+
metas = metas.toImmutableMap()
167163
)
168164

169165
/** Creates a [TimestampElement] that represents an Ion `timestamp`. */
@@ -180,8 +176,8 @@ public fun ionTimestamp(
180176
metas: MetaContainer = emptyMetaContainer()
181177
): TimestampElement = TimestampElementImpl(
182178
timestampValue = timestamp,
183-
annotations = annotations.toEmptyOrPersistentList(),
184-
metas = metas.toPersistentMap()
179+
annotations = annotations.toImmutableList(),
180+
metas = metas.toImmutableMap()
185181
)
186182

187183
/** Creates a [TimestampElement] that represents an Ion `timestamp`. */
@@ -199,8 +195,8 @@ public fun ionInt(
199195
): IntElement =
200196
LongIntElementImpl(
201197
longValue = l,
202-
annotations = annotations.toEmptyOrPersistentList(),
203-
metas = metas.toPersistentMap()
198+
annotations = annotations.toImmutableList(),
199+
metas = metas.toImmutableMap()
204200
)
205201

206202
/** Creates an [IntElement] that represents an Ion `int`. */
@@ -217,8 +213,8 @@ public fun ionInt(
217213
metas: MetaContainer = emptyMetaContainer()
218214
): IntElement = BigIntIntElementImpl(
219215
bigIntegerValue = bigInt,
220-
annotations = annotations.toEmptyOrPersistentList(),
221-
metas = metas.toPersistentMap()
216+
annotations = annotations.toImmutableList(),
217+
metas = metas.toImmutableMap()
222218
)
223219

224220
/** Creates an [IntElement] that represents an Ion `BitInteger`. */
@@ -235,8 +231,8 @@ public fun ionBool(
235231
metas: MetaContainer = emptyMetaContainer()
236232
): BoolElement = BoolElementImpl(
237233
booleanValue = b,
238-
annotations = annotations.toEmptyOrPersistentList(),
239-
metas = metas.toPersistentMap()
234+
annotations = annotations.toImmutableList(),
235+
metas = metas.toImmutableMap()
240236
)
241237

242238
/** Creates a [BoolElement] that represents an Ion `bool`. */
@@ -253,8 +249,8 @@ public fun ionFloat(
253249
metas: MetaContainer = emptyMetaContainer()
254250
): FloatElement = FloatElementImpl(
255251
doubleValue = d,
256-
annotations = annotations.toEmptyOrPersistentList(),
257-
metas = metas.toPersistentMap()
252+
annotations = annotations.toImmutableList(),
253+
metas = metas.toImmutableMap()
258254
)
259255

260256
/** Creates a [FloatElement] that represents an Ion `float`. */
@@ -271,8 +267,8 @@ public fun ionDecimal(
271267
metas: MetaContainer = emptyMetaContainer()
272268
): DecimalElement = DecimalElementImpl(
273269
decimalValue = bigDecimal,
274-
annotations = annotations.toEmptyOrPersistentList(),
275-
metas = metas.toPersistentMap()
270+
annotations = annotations.toImmutableList(),
271+
metas = metas.toImmutableMap()
276272
)
277273

278274
/** Creates a [DecimalElement] that represents an Ion `decimall`. */
@@ -293,8 +289,8 @@ public fun ionBlob(
293289
metas: MetaContainer = emptyMetaContainer()
294290
): BlobElement = BlobElementImpl(
295291
bytes = bytes.clone(),
296-
annotations = annotations.toEmptyOrPersistentList(),
297-
metas = metas.toPersistentMap()
292+
annotations = annotations.toImmutableList(),
293+
metas = metas.toImmutableMap()
298294
)
299295

300296
/**
@@ -322,8 +318,8 @@ public fun ionClob(
322318
metas: MetaContainer = emptyMetaContainer()
323319
): ClobElement = ClobElementImpl(
324320
bytes = bytes.clone(),
325-
annotations = annotations.toEmptyOrPersistentList(),
326-
metas = metas.toPersistentMap()
321+
annotations = annotations.toImmutableList(),
322+
metas = metas.toImmutableMap()
327323
)
328324
/**
329325
* Creates a [ClobElement] that represents an Ion `clob`.
@@ -346,9 +342,9 @@ public fun ionListOf(
346342
metas: MetaContainer = emptyMetaContainer()
347343
): ListElement =
348344
ListElementImpl(
349-
values = iterable.mapToEmptyOrPersistentList { it.asAnyElement() },
350-
annotations = annotations.toEmptyOrPersistentList(),
351-
metas = metas.toPersistentMap()
345+
values = iterable.map { it.asAnyElement() }.toImmutableListUnsafe(),
346+
annotations = annotations.toImmutableList(),
347+
metas = metas.toImmutableMap()
352348
)
353349

354350
/** Creates a [ListElement] that represents an Ion `list`. */
@@ -395,9 +391,9 @@ public fun ionSexpOf(
395391
metas: MetaContainer = emptyMetaContainer()
396392
): SexpElement =
397393
SexpElementImpl(
398-
values = iterable.mapToEmptyOrPersistentList { it.asAnyElement() },
399-
annotations = annotations.toEmptyOrPersistentList(),
400-
metas = metas.toPersistentMap()
394+
values = iterable.map { it.asAnyElement() }.toImmutableListUnsafe(),
395+
annotations = annotations.toImmutableList(),
396+
metas = metas.toImmutableMap()
401397
)
402398

403399
/** Creates an [SexpElement] that represents an Ion `sexp`. */
@@ -437,9 +433,9 @@ public fun ionStructOf(
437433
metas: MetaContainer = emptyMetaContainer()
438434
): StructElement =
439435
StructElementImpl(
440-
allFields = fields.toEmptyOrPersistentList(),
441-
annotations = annotations.toEmptyOrPersistentList(),
442-
metas = metas.toPersistentMap()
436+
allFields = fields.toImmutableList(),
437+
annotations = annotations.toImmutableList(),
438+
metas = metas.toImmutableMap()
443439
)
444440

445441
/** Creates a [StructElement] that represents an Ion `struct` with the specified fields. */
@@ -457,7 +453,7 @@ public fun ionStructOf(
457453
): StructElement =
458454
ionStructOf(
459455
fields = fields.asIterable(),
460-
annotations = annotations.toEmptyOrPersistentList(),
456+
annotations = annotations.toImmutableList(),
461457
metas = metas
462458
)
463459

@@ -523,55 +519,14 @@ public fun buildStruct(
523519
return ionStructOf(fields, annotations, metas)
524520
}
525521

526-
/**
527-
* Converts an `Iterable` to a `PersistentList`.
528-
*
529-
* ### Why is this needed?
530-
* `kotlinx.collections.immutable` <= 0.3.7 has a bug that causes it to unnecessarily allocate empty `PersistentList`s
531-
* instead of using a singleton instance. The fix is in
532-
* [kotlinx.collections.immutable#176](https://github.com/Kotlin/kotlinx.collections.immutable/pull/176),
533-
* but we cannot use it because the version of `kotlinx.collections.immutable` that will have (or has) the fix
534-
* requires Kotlin stdlib >=1.9.2, and `ion-element-kotlin` supports consumers using Kotlin >= 1.3.0.
535-
*/
536-
internal fun <E> Iterable<E>.toEmptyOrPersistentList(): PersistentList<E> {
537-
val isEmpty = if (this is Collection<*>) {
538-
this.isEmpty()
539-
} else {
540-
!this.iterator().hasNext()
541-
}
542-
return if (isEmpty) EMPTY_PERSISTENT_LIST else toPersistentList()
543-
}
544-
545-
/**
546-
* Converts an `Iterable` to a `PersistentList`, transforming each element using [block].
547-
*
548-
* ### Why is this needed?
549-
* `kotlinx.collections.immutable` <= 0.3.7 has a bug that causes it to unnecessarily allocate empty `PersistentList`s
550-
* instead of using a singleton instance. The fix is in
551-
* [kotlinx.collections.immutable#176](https://github.com/Kotlin/kotlinx.collections.immutable/pull/176),
552-
* but we cannot use it because the version of `kotlinx.collections.immutable` that will have (or has) the fix
553-
* requires Kotlin stdlib >=1.9.2, and `ion-element-kotlin` supports consumers using Kotlin >= 1.3.0.
554-
*/
555-
internal inline fun <T, R> Iterable<T>.mapToEmptyOrPersistentList(block: (T) -> R): PersistentList<R> {
556-
val isEmpty = if (this is Collection<*>) {
557-
this.isEmpty()
558-
} else {
559-
!this.iterator().hasNext()
560-
}
561-
return if (isEmpty) EMPTY_PERSISTENT_LIST else mapTo(persistentListOf<R>().builder(), block).build()
562-
}
563-
564-
// Memoized empty PersistentList for the memoized container types and null values
565-
internal val EMPTY_PERSISTENT_LIST: PersistentList<Nothing> = persistentListOf()
566-
567522
// Memoized empty instances of our container types.
568-
private val EMPTY_LIST = ListElementImpl(EMPTY_PERSISTENT_LIST, EMPTY_PERSISTENT_LIST, EMPTY_METAS)
569-
private val EMPTY_SEXP = SexpElementImpl(EMPTY_PERSISTENT_LIST, EMPTY_PERSISTENT_LIST, EMPTY_METAS)
570-
private val EMPTY_STRUCT = StructElementImpl(EMPTY_PERSISTENT_LIST, EMPTY_PERSISTENT_LIST, EMPTY_METAS)
571-
private val EMPTY_BLOB = BlobElementImpl(ByteArray(0), EMPTY_PERSISTENT_LIST, EMPTY_METAS)
572-
private val EMPTY_CLOB = ClobElementImpl(ByteArray(0), EMPTY_PERSISTENT_LIST, EMPTY_METAS)
523+
private val EMPTY_LIST = ListElementImpl(EMPTY_IMMUTABLE_LIST, EMPTY_IMMUTABLE_LIST, EMPTY_METAS)
524+
private val EMPTY_SEXP = SexpElementImpl(EMPTY_IMMUTABLE_LIST, EMPTY_IMMUTABLE_LIST, EMPTY_METAS)
525+
private val EMPTY_STRUCT = StructElementImpl(EMPTY_IMMUTABLE_LIST, EMPTY_IMMUTABLE_LIST, EMPTY_METAS)
526+
private val EMPTY_BLOB = BlobElementImpl(ByteArray(0), EMPTY_IMMUTABLE_LIST, EMPTY_METAS)
527+
private val EMPTY_CLOB = ClobElementImpl(ByteArray(0), EMPTY_IMMUTABLE_LIST, EMPTY_METAS)
573528

574529
// Memoized instances of all of our null values.
575530
private val ALL_NULLS = ElementType.values().map {
576-
it to NullElementImpl(it, EMPTY_PERSISTENT_LIST, EMPTY_METAS) as IonElement
531+
it to NullElementImpl(it, EMPTY_IMMUTABLE_LIST, EMPTY_METAS) as IonElement
577532
}.toMap()

src/main/kotlin/com/amazon/ionelement/api/IonMeta.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@
1616
@file: JvmName("IonMeta")
1717
package com.amazon.ionelement.api
1818

19-
import kotlinx.collections.immutable.PersistentMap
20-
import kotlinx.collections.immutable.toPersistentHashMap
19+
import com.amazon.ionelement.impl.collections.*
2120

2221
public typealias MetaContainer = Map<String, Any>
23-
internal typealias PersistentMetaContainer = PersistentMap<String, Any>
22+
internal typealias ImmutableMetaContainer = ImmutableMap<String, Any>
2423

25-
internal val EMPTY_METAS = HashMap<String, Any>().toPersistentHashMap()
24+
internal val EMPTY_METAS: ImmutableMetaContainer = emptyMap<String, Any>().toImmutableMap()
2625

2726
public fun emptyMetaContainer(): MetaContainer = EMPTY_METAS
2827

src/main/kotlin/com/amazon/ionelement/impl/BigIntIntElementImpl.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,15 @@ package com.amazon.ionelement.impl
1717

1818
import com.amazon.ion.IonWriter
1919
import com.amazon.ionelement.api.*
20-
import com.amazon.ionelement.api.PersistentMetaContainer
20+
import com.amazon.ionelement.api.ImmutableMetaContainer
2121
import com.amazon.ionelement.api.constraintError
22+
import com.amazon.ionelement.impl.collections.*
2223
import java.math.BigInteger
23-
import kotlinx.collections.immutable.PersistentList
24-
import kotlinx.collections.immutable.toPersistentMap
2524

2625
internal class BigIntIntElementImpl(
2726
override val bigIntegerValue: BigInteger,
28-
override val annotations: PersistentList<String>,
29-
override val metas: PersistentMetaContainer
27+
override val annotations: ImmutableList<String>,
28+
override val metas: ImmutableMetaContainer
3029
) : AnyElementBase(), IntElement {
3130

3231
override val type: ElementType get() = ElementType.INT
@@ -42,7 +41,7 @@ internal class BigIntIntElementImpl(
4241
}
4342

4443
override fun copy(annotations: List<String>, metas: MetaContainer): BigIntIntElementImpl =
45-
BigIntIntElementImpl(bigIntegerValue, annotations.toEmptyOrPersistentList(), metas.toPersistentMap())
44+
BigIntIntElementImpl(bigIntegerValue, annotations.toImmutableList(), metas.toImmutableMap())
4645

4746
override fun withAnnotations(vararg additionalAnnotations: String): BigIntIntElementImpl = _withAnnotations(*additionalAnnotations)
4847
override fun withAnnotations(additionalAnnotations: Iterable<String>): BigIntIntElementImpl = _withAnnotations(additionalAnnotations)

src/main/kotlin/com/amazon/ionelement/impl/BlobElementImpl.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,13 @@ package com.amazon.ionelement.impl
1717

1818
import com.amazon.ion.IonWriter
1919
import com.amazon.ionelement.api.*
20-
import com.amazon.ionelement.api.PersistentMetaContainer
21-
import kotlinx.collections.immutable.PersistentList
22-
import kotlinx.collections.immutable.toPersistentMap
20+
import com.amazon.ionelement.api.ImmutableMetaContainer
21+
import com.amazon.ionelement.impl.collections.*
2322

2423
internal class BlobElementImpl(
2524
bytes: ByteArray,
26-
override val annotations: PersistentList<String>,
27-
override val metas: PersistentMetaContainer
25+
override val annotations: ImmutableList<String>,
26+
override val metas: ImmutableMetaContainer
2827
) : LobElementBase(bytes), BlobElement {
2928

3029
override val type: ElementType get() = ElementType.BLOB
@@ -34,7 +33,7 @@ internal class BlobElementImpl(
3433
override fun writeContentTo(writer: IonWriter) = writer.writeBlob(bytes)
3534

3635
override fun copy(annotations: List<String>, metas: MetaContainer): BlobElementImpl =
37-
BlobElementImpl(bytes, annotations.toEmptyOrPersistentList(), metas.toPersistentMap())
36+
BlobElementImpl(bytes, annotations.toImmutableList(), metas.toImmutableMap())
3837

3938
override fun withAnnotations(vararg additionalAnnotations: String): BlobElementImpl = _withAnnotations(*additionalAnnotations)
4039
override fun withAnnotations(additionalAnnotations: Iterable<String>): BlobElementImpl = _withAnnotations(additionalAnnotations)

src/main/kotlin/com/amazon/ionelement/impl/BoolElementImpl.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,18 @@ package com.amazon.ionelement.impl
1717

1818
import com.amazon.ion.IonWriter
1919
import com.amazon.ionelement.api.*
20-
import com.amazon.ionelement.api.PersistentMetaContainer
21-
import kotlinx.collections.immutable.PersistentList
22-
import kotlinx.collections.immutable.toPersistentMap
20+
import com.amazon.ionelement.api.ImmutableMetaContainer
21+
import com.amazon.ionelement.impl.collections.*
2322

2423
internal class BoolElementImpl(
2524
override val booleanValue: Boolean,
26-
override val annotations: PersistentList<String>,
27-
override val metas: PersistentMetaContainer
25+
override val annotations: ImmutableList<String>,
26+
override val metas: ImmutableMetaContainer
2827
) : AnyElementBase(), BoolElement {
2928
override val type: ElementType get() = ElementType.BOOL
3029

3130
override fun copy(annotations: List<String>, metas: MetaContainer): BoolElementImpl =
32-
BoolElementImpl(booleanValue, annotations.toEmptyOrPersistentList(), metas.toPersistentMap())
31+
BoolElementImpl(booleanValue, annotations.toImmutableList(), metas.toImmutableMap())
3332

3433
override fun withAnnotations(vararg additionalAnnotations: String): BoolElementImpl = _withAnnotations(*additionalAnnotations)
3534
override fun withAnnotations(additionalAnnotations: Iterable<String>): BoolElementImpl = _withAnnotations(additionalAnnotations)

src/main/kotlin/com/amazon/ionelement/impl/ClobElementImpl.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,13 @@ package com.amazon.ionelement.impl
1717

1818
import com.amazon.ion.IonWriter
1919
import com.amazon.ionelement.api.*
20-
import com.amazon.ionelement.api.PersistentMetaContainer
21-
import kotlinx.collections.immutable.PersistentList
22-
import kotlinx.collections.immutable.toPersistentMap
20+
import com.amazon.ionelement.api.ImmutableMetaContainer
21+
import com.amazon.ionelement.impl.collections.*
2322

2423
internal class ClobElementImpl(
2524
bytes: ByteArray,
26-
override val annotations: PersistentList<String>,
27-
override val metas: PersistentMetaContainer
25+
override val annotations: ImmutableList<String>,
26+
override val metas: ImmutableMetaContainer
2827
) : LobElementBase(bytes), ClobElement {
2928

3029
override val type: ElementType get() = ElementType.CLOB
@@ -33,7 +32,7 @@ internal class ClobElementImpl(
3332

3433
override fun writeContentTo(writer: IonWriter) = writer.writeClob(bytes)
3534
override fun copy(annotations: List<String>, metas: MetaContainer): ClobElementImpl =
36-
ClobElementImpl(bytes, annotations.toEmptyOrPersistentList(), metas.toPersistentMap())
35+
ClobElementImpl(bytes, annotations.toImmutableList(), metas.toImmutableMap())
3736

3837
override fun withAnnotations(vararg additionalAnnotations: String): ClobElementImpl = _withAnnotations(*additionalAnnotations)
3938
override fun withAnnotations(additionalAnnotations: Iterable<String>): ClobElementImpl = _withAnnotations(additionalAnnotations)

0 commit comments

Comments
 (0)