commonMain.ByteStringBuilder.kt Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/
package kotlinx.io.bytestring
import kotlin.math.max
/**
* A helper class facilitating [ByteString] construction.
*
* A builder is characterized by the [capacity] - the number of bytes that an instance of builder
* can receive before extending an underlying byte sequence, and [size] - the number of bytes being written
* to the builder.
*
* The builder avoids additional copies and allocations when `size == capacity` when [toByteString] called,
* thus it's recommended to specify expected [ByteString] size as `initialCapacity` when creating a builder.
*
* When a builder runs out of available capacity, a new byte sequence with extended capacity
* will be allocated and previously written data will be copied into it.
*
* @param initialCapacity the initial size of an underlying byte sequence.
*
* @sample kotlinx.io.bytestring.samples.ByteStringSamples.builderSample
* @sample kotlinx.io.bytestring.samples.ByteStringSamples.builderSampleWithoutAdditionalAllocs
*/
public class ByteStringBuilder(initialCapacity: Int = 0) {
private var buffer = ByteArray(initialCapacity)
private var offset: Int = 0
/**
* The number of bytes being written to this builder.
*/
public val size: Int
get() = offset
/**
* The number of bytes this builder can store without an extension of an internal buffer.
*/
public val capacity: Int
get() = buffer.size
/**
* Returns a new [ByteString] wrapping all bytes written to this builder.
*
* There will be no additional allocations or copying of data when `size == capacity`.
*/
public fun toByteString(): ByteString {
if (size == 0) {
return ByteString()
}
if (buffer.size == size) {
return ByteString.wrap(buffer)
}
return ByteString(buffer, 0, size)
}
/**
* Append a single byte to this builder.
*
* @param byte the byte to append.
*/
public fun append(byte: Byte) {
ensureCapacity(size + 1)
buffer[offset++] = byte
}
/**
* Appends a subarray of [array] starting at [startIndex] and ending at [endIndex] to this builder.
*
* @param array the array whose subarray should be appended.
* @param startIndex the first index (inclusive) to copy data from the [array].
* @param endIndex the last index (exclusive) to copy data from the [array]
*
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of [array] array indices.
* @throws IllegalArgumentException when `startIndex > endIndex`.
*/
public fun append(array: ByteArray, startIndex: Int = 0, endIndex: Int = array.size) {
require(startIndex <= endIndex) { "startIndex ($startIndex) > endIndex ($endIndex)" }
if (startIndex < 0 || endIndex > array.size) {
throw IndexOutOfBoundsException("startIndex ($startIndex) and endIndex ($endIndex) represents " +
"an interval out of array's bounds [0..${array.size}).")
}
ensureCapacity(offset + endIndex - startIndex)
array.copyInto(buffer, offset, startIndex, endIndex)
offset += endIndex - startIndex
}
private fun ensureCapacity(requiredCapacity: Int) {
if (buffer.size >= requiredCapacity) {
return
}
var desiredSize = if (buffer.isEmpty()) 16 else (buffer.size * 1.5).toInt()
desiredSize = max(desiredSize, requiredCapacity)
val newBuffer = ByteArray(desiredSize)
buffer.copyInto(newBuffer)
buffer = newBuffer
}
}
/**
* Appends unsigned byte to this builder.
*/
public fun ByteStringBuilder.append(byte: UByte): Unit = append(byte.toByte())
/**
* Appends a byte string to this builder.
*/
public fun ByteStringBuilder.append(byteString: ByteString) {
append(byteString.getBackingArrayReference())
}
/**
* Appends bytes to this builder.
*/
public fun ByteStringBuilder.append(vararg bytes: Byte): Unit = append(bytes)
/**
* Builds new byte string by populating newly created [ByteStringBuilder] initialized with the given [capacity]
* using provided [builderAction] and then converting it to [ByteString].
*/
public inline fun buildByteString(capacity: Int = 0, builderAction: ByteStringBuilder.() -> Unit): ByteString {
return ByteStringBuilder(capacity).apply(builderAction).toByteString()
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy