diff options
Diffstat (limited to 'formats/json/jvmMain/src/kotlinx/serialization/json/internal/ArrayPools.kt')
-rw-r--r-- | formats/json/jvmMain/src/kotlinx/serialization/json/internal/ArrayPools.kt | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/formats/json/jvmMain/src/kotlinx/serialization/json/internal/ArrayPools.kt b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/ArrayPools.kt new file mode 100644 index 00000000..0d36c6c0 --- /dev/null +++ b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/ArrayPools.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +package kotlinx.serialization.json.internal + +/* + * Not really documented kill switch as a workaround for potential + * (unlikely) problems with memory consumptions. + */ +private val MAX_CHARS_IN_POOL = runCatching { + System.getProperty("kotlinx.serialization.json.pool.size").toIntOrNull() +}.getOrNull() ?: 2 * 1024 * 1024 + +internal open class CharArrayPoolBase { + private val arrays = ArrayDeque<CharArray>() + private var charsTotal = 0 + + protected fun take(size: Int): CharArray { + /* + * Initially the pool is empty, so an instance will be allocated + * and the pool will be populated in the 'release' + */ + val candidate = synchronized(this) { + arrays.removeLastOrNull()?.also { charsTotal -= it.size } + } + return candidate ?: CharArray(size) + } + + protected fun releaseImpl(array: CharArray): Unit = synchronized(this) { + if (charsTotal + array.size >= MAX_CHARS_IN_POOL) return@synchronized + charsTotal += array.size + arrays.addLast(array) + } +} + +internal object CharArrayPool : CharArrayPoolBase() { + fun take(): CharArray = super.take(128) + + // Can release array of an arbitrary size + fun release(array: CharArray) = releaseImpl(array) +} + +// Pools char arrays of size 16K +internal actual object CharArrayPoolBatchSize : CharArrayPoolBase() { + + actual fun take(): CharArray = super.take(BATCH_SIZE) + + actual fun release(array: CharArray) { + require(array.size == BATCH_SIZE) { "Inconsistent internal invariant: unexpected array size ${array.size}" } + releaseImpl(array) + } +} + +// Byte array pool + +internal open class ByteArrayPoolBase { + private val arrays = ArrayDeque<kotlin.ByteArray>() + private var bytesTotal = 0 + + protected fun take(size: Int): ByteArray { + /* + * Initially the pool is empty, so an instance will be allocated + * and the pool will be populated in the 'release' + */ + val candidate = synchronized(this) { + arrays.removeLastOrNull()?.also { bytesTotal -= it.size / 2 } + } + return candidate ?: ByteArray(size) + } + + protected fun releaseImpl(array: ByteArray): Unit = synchronized(this) { + if (bytesTotal + array.size >= MAX_CHARS_IN_POOL) return@synchronized + bytesTotal += array.size / 2 + arrays.addLast(array) + } +} + +internal object ByteArrayPool8k : ByteArrayPoolBase() { + fun take(): ByteArray = super.take(8196) + + fun release(array: ByteArray) = releaseImpl(array) +} + + +internal object ByteArrayPool : ByteArrayPoolBase() { + fun take(): ByteArray = super.take(512) + + fun release(array: ByteArray) = releaseImpl(array) +} |