From 569b3aa5050cd1f46964385a7b2befb1de21e984 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Sun, 9 Jun 2019 20:19:40 +0200 Subject: [PATCH] extract PooledBlockAllocatorProvider GitOrigin-RevId: ac38d22d0707b30e67b2e3cf70f3408042e6cd98 --- .../src/IonObjectSerializer.kt | 89 ------------------ .../src/PooledBlockAllocatorProvider.kt | 92 +++++++++++++++++++ 2 files changed, 92 insertions(+), 89 deletions(-) create mode 100644 platform/object-serializer/src/PooledBlockAllocatorProvider.kt diff --git a/platform/object-serializer/src/IonObjectSerializer.kt b/platform/object-serializer/src/IonObjectSerializer.kt index 7b1b9d412226..559aebd913b1 100644 --- a/platform/object-serializer/src/IonObjectSerializer.kt +++ b/platform/object-serializer/src/IonObjectSerializer.kt @@ -4,23 +4,16 @@ package com.intellij.serialization import com.amazon.ion.IonException import com.amazon.ion.IonType import com.amazon.ion.IonWriter -import com.amazon.ion.impl.bin.Block -import com.amazon.ion.impl.bin.BlockAllocator -import com.amazon.ion.impl.bin.BlockAllocatorProvider import com.amazon.ion.impl.bin._Private_IonManagedBinaryWriterBuilder import com.amazon.ion.system.IonReaderBuilder import com.amazon.ion.system.IonTextWriterBuilder import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream import com.intellij.util.ParameterizedTypeImpl -import com.intellij.util.containers.ContainerUtil -import org.jetbrains.annotations.TestOnly import java.io.IOException import java.io.InputStream import java.io.OutputStream import java.lang.reflect.Type import java.nio.file.Path -import java.util.* -import java.util.concurrent.atomic.AtomicInteger import kotlin.experimental.or private const val FORMAT_VERSION = 2 @@ -207,86 +200,4 @@ private fun createIonWriterBuilder(binary: Boolean, out: OutputStream): IonWrite binary -> binaryWriterBuilder.newWriter(out) else -> textWriterBuilder.build(out) } -} - -internal class PooledBlockAllocatorProvider : BlockAllocatorProvider() { - companion object { - // 512 KB - internal const val POOL_THRESHOLD = 512 * 1024 - } - - @Suppress("RemoveExplicitTypeArguments") - private val allocators = ContainerUtil.createConcurrentIntObjectMap() - - private inner class PooledBlockAllocator(private val blockSize: Int) : BlockAllocator() { - private val freeBlocks = ArrayList() - - private val blockCounter = AtomicInteger() - - val byteSize: Int - get() = blockCounter.get() * blockSize - - override fun allocateBlock(): Block { - val lastIndex = freeBlocks.lastIndex - if (lastIndex != -1) { - return freeBlocks.removeAt(lastIndex) - } - - blockCounter.incrementAndGet() - return object : Block(ByteArray(blockSize)) { - override fun close() { - reset() - freeBlocks.add(this) - } - } - } - - override fun getBlockSize() = blockSize - - override fun close() { - if (allocators.putIfAbsent(blockSize, this) != null) { - // help GC - nullize - freeBlocks.clear() - blockCounter.set(0) - } - } - } - - @get:TestOnly - val byteSize: Int - get() { - var totalByteSize = 0 - for (allocator in allocators.elements()) { - totalByteSize += allocator.byteSize - } - return totalByteSize - } - - override fun vendAllocator(blockSize: Int): BlockAllocator { - if (blockSize <= 0) { - throw IllegalArgumentException("Invalid block size: $blockSize") - } - - // PooledBlockAllocator is not thread safe - do not put a new one to pool - val result = allocators.remove(blockSize) ?: PooledBlockAllocator(blockSize) - - var totalByteSize = 0 - val iterator = allocators.values().iterator() - var isExcess = false - while (iterator.hasNext()) { - val allocator = iterator.next() - if (isExcess) { - iterator.remove() - continue - } - - totalByteSize += allocator.byteSize - if (totalByteSize > POOL_THRESHOLD) { - iterator.remove() - isExcess = true - } - } - - return result - } } \ No newline at end of file diff --git a/platform/object-serializer/src/PooledBlockAllocatorProvider.kt b/platform/object-serializer/src/PooledBlockAllocatorProvider.kt new file mode 100644 index 000000000000..b4f79e63ff72 --- /dev/null +++ b/platform/object-serializer/src/PooledBlockAllocatorProvider.kt @@ -0,0 +1,92 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.serialization + +import com.amazon.ion.impl.bin.Block +import com.amazon.ion.impl.bin.BlockAllocator +import com.amazon.ion.impl.bin.BlockAllocatorProvider +import com.intellij.util.containers.ContainerUtil +import org.jetbrains.annotations.TestOnly +import java.util.* +import java.util.concurrent.atomic.AtomicInteger + +internal class PooledBlockAllocatorProvider : BlockAllocatorProvider() { + companion object { + // 512 KB + internal const val POOL_THRESHOLD = 512 * 1024 + } + + @Suppress("RemoveExplicitTypeArguments") + private val allocators = ContainerUtil.createConcurrentIntObjectMap() + + private inner class PooledBlockAllocator(private val blockSize: Int) : BlockAllocator() { + private val freeBlocks = ArrayList() + + private val blockCounter = AtomicInteger() + + val byteSize: Int + get() = blockCounter.get() * blockSize + + override fun allocateBlock(): Block { + val lastIndex = freeBlocks.lastIndex + if (lastIndex != -1) { + return freeBlocks.removeAt(lastIndex) + } + + blockCounter.incrementAndGet() + return object : Block(ByteArray(blockSize)) { + override fun close() { + reset() + freeBlocks.add(this) + } + } + } + + override fun getBlockSize() = blockSize + + override fun close() { + if (allocators.putIfAbsent(blockSize, this) != null) { + // help GC - nullize + freeBlocks.clear() + blockCounter.set(0) + } + } + } + + @get:TestOnly + val byteSize: Int + get() { + var totalByteSize = 0 + for (allocator in allocators.elements()) { + totalByteSize += allocator.byteSize + } + return totalByteSize + } + + override fun vendAllocator(blockSize: Int): BlockAllocator { + if (blockSize <= 0) { + throw IllegalArgumentException("Invalid block size: $blockSize") + } + + // PooledBlockAllocator is not thread safe - do not put a new one to pool + val result = allocators.remove(blockSize) ?: PooledBlockAllocator(blockSize) + + var totalByteSize = 0 + val iterator = allocators.values().iterator() + var isExcess = false + while (iterator.hasNext()) { + val allocator = iterator.next() + if (isExcess) { + iterator.remove() + continue + } + + totalByteSize += allocator.byteSize + if (totalByteSize > POOL_THRESHOLD) { + iterator.remove() + isExcess = true + } + } + + return result + } +} \ No newline at end of file