Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2017-2024 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file.
*/
/*
* Copyright (C) 2014 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlinx.io
import kotlin.jvm.JvmField
import kotlin.jvm.JvmSynthetic
/**
* Tracks shared segment copies.
*
* A new [SegmentCopyTracker] instance should be not shared by default (i.e. `shared == false`).
* Any further [addCopy] calls should move the tracker to a shared state (i.e. `shared == true`).
* Once a shared segment copy is recycled, [removeCopy] should be called.
* Depending on implementation, calling [removeCopy] the same number of times as [addCopy] may
* or may not transition the tracked back to unshared stated.
*
* The class is not intended for public use and currently designed to fit the only use case - within JVM SegmentPool
* implementation.
*/
internal abstract class SegmentCopyTracker {
/**
* `true` if a tracker shared by multiple segment copies.
*/
abstract val shared: Boolean
/**
* Track a new copy created by sharing an associated segment.
*/
abstract fun addCopy()
/**
* Records reclamation of a shared segment copy associated with this tracker.
* If a tracker was in unshared state, this call should not affect an internal state.
*
* @return `true` if the segment was not shared *before* this called.
*/
abstract fun removeCopy(): Boolean
}
/**
* Simple [SegmentCopyTracker] that always reports shared state.
*/
internal object AlwaysSharedCopyTracker : SegmentCopyTracker() {
override val shared: Boolean get() = true
override fun addCopy() = Unit
override fun removeCopy(): Boolean = true
}
/**
* A segment of a buffer.
*
* Each segment in a buffer is a doubly-linked list node referencing the following and
* preceding segments in the buffer.
*
* Each segment in the pool is a singly-linked list node referencing the rest of segments in the pool.
*
* The underlying byte arrays of segments may be shared between buffers and byte strings. When a
* segment's byte array is shared the segment may not be recycled, nor may its byte data be changed.
* The lone exception is that the owner segment is allowed to append to the segment, writing data at
* `limit` and beyond. There is a single owning segment for each byte array. Positions,
* limits, prev, and next references are not shared.
*/
public class Segment {
private val data: ByteArray
/** The next byte of application data byte to read in this segment. */
@PublishedApi
@get:JvmSynthetic
@set:JvmSynthetic
internal var pos: Int = 0
/**
* The first byte of available data ready to be written to.
*
* If the segment is free and linked in the segment pool, the field contains total
* byte count of this and next segments.
*/
@PublishedApi
@get:JvmSynthetic
@set:JvmSynthetic
internal var limit: Int = 0
/** True if other segments or byte strings use the same byte array. */
internal val shared: Boolean
get() = copyTracker?.shared ?: false
/**
* Tracks number shared copies
*
* Note that this reference is not `@Volatile` as segments are not thread-safe and it's an error
* to modify the same segment concurrently.
* At the same time, an object [copyTracker] refers to could be modified concurrently.
*/
internal var copyTracker: SegmentCopyTracker? = null
/** True if this segment owns the byte array and can append to it, extending `limit`. */
@JvmField
internal var owner: Boolean = false
/** Next segment in a list, or null. */
@PublishedApi
@get:JvmSynthetic
@set:JvmSynthetic
internal var next: Segment? = null
/** Previous segment in the list, or null. */
@PublishedApi
@get:JvmSynthetic
@set:JvmSynthetic
internal var prev: Segment? = null
private constructor() {
this.data = ByteArray(SIZE)
this.owner = true
this.copyTracker = null
}
private constructor(data: ByteArray, pos: Int, limit: Int, shareToken: SegmentCopyTracker?, owner: Boolean) {
this.data = data
this.pos = pos
this.limit = limit
this.copyTracker = shareToken
this.owner = owner
}
/**
* Returns a new segment that shares the underlying byte array with this. Adjusting pos and limit
* are safe but writes are forbidden. This also marks the current segment as shared, which
* prevents it from being pooled.
*/
internal fun sharedCopy(): Segment {
val t = copyTracker ?: SegmentPool.tracker().also {
copyTracker = it
}
return Segment(data, pos, limit, t.also { it.addCopy() }, false)
}
/**
* Removes this segment of a list and returns its successor.
* Returns null if the list is now empty.
*/
internal fun pop(): Segment? {
val result = this.next
if (this.prev != null) {
this.prev!!.next = this.next
}
if (this.next != null) {
this.next!!.prev = this.prev
}
this.next = null
this.prev = null
return result
}
/**
* Appends `segment` after this segment in the list. Returns the pushed segment.
*/
internal fun push(segment: Segment): Segment {
segment.prev = this
segment.next = this.next
if (this.next != null) {
this.next!!.prev = segment
}
this.next = segment
return segment
}
/**
* Splits this head of a list into two segments. The first segment contains the
* data in `[pos..pos+byteCount)`. The second segment contains the data in
* `[pos+byteCount..limit)`. This can be useful when moving partial segments from one buffer to
* another.
*
* Returns the new head of the list.
*/
internal fun split(byteCount: Int): Segment {
require(byteCount > 0 && byteCount <= limit - pos) { "byteCount out of range" }
val prefix: Segment
// We have two competing performance goals:
// - Avoid copying data. We accomplish this by sharing segments.
// - Avoid short shared segments. These are bad for performance because they are readonly and
// may lead to long chains of short segments.
// To balance these goals we only share segments when the copy will be large.
if (byteCount >= SHARE_MINIMUM) {
prefix = sharedCopy()
} else {
prefix = SegmentPool.take()
data.copyInto(prefix.data, startIndex = pos, endIndex = pos + byteCount)
}
prefix.limit = prefix.pos + byteCount
pos += byteCount
if (this.prev != null) {
this.prev!!.push(prefix)
} else {
prefix.next = this
this.prev = prefix
}
return prefix
}
/**
* Call this when the tail and its predecessor may both be less than half full. This will copy
* data so that segments can be recycled.
*/
internal fun compact(): Segment {
check(this.prev != null) { "cannot compact" }
if (!this.prev!!.owner) return this // Cannot compact: prev isn't writable.
val byteCount = limit - pos
val availableByteCount = SIZE - this.prev!!.limit + if (this.prev!!.shared) 0 else this.prev!!.pos
if (byteCount > availableByteCount) return this // Cannot compact: not enough writable space.
val predecessor = this.prev
writeTo(predecessor!!, byteCount)
val successor = pop()
check(successor == null)
SegmentPool.recycle(this)
return predecessor
}
internal fun writeByte(byte: Byte) {
data[limit++] = byte
}
internal fun writeShort(short: Short) {
val data = data
var limit = limit
data[limit++] = (short.toInt() ushr 8 and 0xff).toByte()
data[limit++] = (short.toInt() and 0xff).toByte()
this.limit = limit
}
internal fun writeInt(int: Int) {
val data = data
var limit = limit
data[limit++] = (int ushr 24 and 0xff).toByte()
data[limit++] = (int ushr 16 and 0xff).toByte()
data[limit++] = (int ushr 8 and 0xff).toByte()
data[limit++] = (int and 0xff).toByte()
this.limit = limit
}
internal fun writeLong(long: Long) {
val data = data
var limit = limit
data[limit++] = (long ushr 56 and 0xffL).toByte()
data[limit++] = (long ushr 48 and 0xffL).toByte()
data[limit++] = (long ushr 40 and 0xffL).toByte()
data[limit++] = (long ushr 32 and 0xffL).toByte()
data[limit++] = (long ushr 24 and 0xffL).toByte()
data[limit++] = (long ushr 16 and 0xffL).toByte()
data[limit++] = (long ushr 8 and 0xffL).toByte()
data[limit++] = (long and 0xffL).toByte()
this.limit = limit
}
internal fun readByte(): Byte {
return data[pos++]
}
internal fun readShort(): Short {
val data = data
var pos = pos
val s = (data[pos++] and 0xff shl 8 or (data[pos++] and 0xff)).toShort()
this.pos = pos
return s
}
internal fun readInt(): Int {
val data = data
var pos = pos
val i = (
data[pos++] and 0xff shl 24
or (data[pos++] and 0xff shl 16)
or (data[pos++] and 0xff shl 8)
or (data[pos++] and 0xff)
)
this.pos = pos
return i
}
internal fun readLong(): Long {
val data = data
var pos = pos
val v = (
data[pos++] and 0xffL shl 56
or (data[pos++] and 0xffL shl 48)
or (data[pos++] and 0xffL shl 40)
or (data[pos++] and 0xffL shl 32)
or (data[pos++] and 0xffL shl 24)
or (data[pos++] and 0xffL shl 16)
or (data[pos++] and 0xffL shl 8)
or (data[pos++] and 0xffL)
)
this.pos = pos
return v
}
/** Moves `byteCount` bytes from this segment to `sink`. */
internal fun writeTo(sink: Segment, byteCount: Int) {
check(sink.owner) { "only owner can write" }
if (sink.limit + byteCount > SIZE) {
// We can't fit byteCount bytes at the sink's current position. Shift sink first.
if (sink.shared) throw IllegalArgumentException()
if (sink.limit + byteCount - sink.pos > SIZE) throw IllegalArgumentException()
sink.data.copyInto(sink.data, startIndex = sink.pos, endIndex = sink.limit)
sink.limit -= sink.pos
sink.pos = 0
}
data.copyInto(
sink.data, destinationOffset = sink.limit, startIndex = pos,
endIndex = pos + byteCount
)
sink.limit += byteCount
pos += byteCount
}
internal fun readTo(dst: ByteArray, dstStartOffset: Int, dstEndOffset: Int) {
val len = dstEndOffset - dstStartOffset
data.copyInto(dst, dstStartOffset, pos, pos + len)
pos += len
}
internal fun write(src: ByteArray, srcStartOffset: Int, srcEndOffset: Int) {
src.copyInto(data, limit, srcStartOffset, srcEndOffset)
limit += srcEndOffset - srcStartOffset
}
@PublishedApi
@get:JvmSynthetic
internal val size: Int
get() = limit - pos
@PublishedApi
@get:JvmSynthetic
internal val remainingCapacity: Int
get() = data.size - limit
/**
* Return a byte-array view over internal data.
*
* Returned array contains data layed out so that a readable slice starts at
* [Segment.pos] and ends at [Segment.limit], writable slice starts at [Segment.limit]
* and spans over [Segment.remainingCapacity] bytes.
*
* This method exists only to preserve binary compatibility if a segment's internal
* container eventually changes from ByteArray to something else.
*/
@PublishedApi
@JvmSynthetic
@Suppress("UNUSED_PARAMETER")
internal fun dataAsByteArray(readOnly: Boolean): ByteArray = data
/**
* Write back all modifications that were made to a view returned from [dataAsByteArray].
*
* This method exists only to preserve binary compatibility if a segment's internal
* container eventually changes from ByteArray to something else.
*/
@PublishedApi
@JvmSynthetic
@Suppress("UNUSED_PARAMETER")
internal fun writeBackData(data: ByteArray, bytesToCommit: Int): Unit = Unit
internal fun getUnchecked(index: Int): Byte {
return data[pos + index]
}
internal fun setUnchecked(index: Int, value: Byte) {
data[limit + index] = value
}
internal fun setUnchecked(index: Int, b0: Byte, b1: Byte) {
val d = data
val l = limit
d[l + index] = b0
d[l + index + 1] = b1
}
internal fun setUnchecked(index: Int, b0: Byte, b1: Byte, b2: Byte) {
val d = data
val l = limit
d[l + index] = b0
d[l + index + 1] = b1
d[l + index + 2] = b2
}
internal fun setUnchecked(index: Int, b0: Byte, b1: Byte, b2: Byte, b3: Byte) {
val d = data
val l = limit
d[l + index] = b0
d[l + index + 1] = b1
d[l + index + 2] = b2
d[l + index + 3] = b3
}
internal companion object {
/** The size of all segments in bytes. */
internal const val SIZE = 8192
/** Segments will be shared when doing so avoids `arraycopy()` of this many bytes. */
internal const val SHARE_MINIMUM = 1024
@JvmSynthetic
internal fun new(): Segment = Segment()
@JvmSynthetic
internal fun new(
data: ByteArray,
pos: Int,
limit: Int,
copyTracker: SegmentCopyTracker?,
owner: Boolean
): Segment = Segment(data, pos, limit, copyTracker, owner)
}
}
internal fun Segment.indexOf(byte: Byte, startOffset: Int, endOffset: Int): Int {
require(startOffset in 0 until size) {
"$startOffset"
}
require(endOffset in startOffset..size) {
"$endOffset"
}
val p = pos
val data = dataAsByteArray(true)
for (idx in startOffset until endOffset) {
if (data[p + idx] == byte) {
return idx
}
}
return -1
}
/**
* Searches for a `bytes` pattern within this segment starting at the offset `startOffset`.
* `startOffset` is relative and should be within `[0, size)`.
*/
internal fun Segment.indexOfBytesInbound(bytes: ByteArray, startOffset: Int): Int {
var offset = startOffset
val limit = size - bytes.size + 1
val firstByte = bytes[0]
val data = dataAsByteArray(true)
while (offset < limit) {
val idx = indexOf(firstByte, offset, limit)
if (idx < 0) {
return -1
}
var found = true
for (innerIdx in 1 until bytes.size) {
if (data[pos + idx + innerIdx] != bytes[innerIdx]) {
found = false
break
}
}
if (found) {
return idx
} else {
offset++
}
}
return -1
}
/**
* Searches for a `bytes` pattern starting in between offset `startOffset` and `size` within this segment
* and continued in the following segments.
* `startOffset` is relative and should be within `[0, size)`.
*/
internal fun Segment.indexOfBytesOutbound(bytes: ByteArray, startOffset: Int): Int {
var offset = startOffset
val firstByte = bytes[0]
while (offset in 0 until size) {
val idx = indexOf(firstByte, offset, size)
if (idx < 0) {
return -1
}
// The pattern should start in this segment
var seg = this
var data = seg.dataAsByteArray(true)
var scanOffset = offset
var found = true
for (element in bytes) {
// We ran out of bytes in this segment,
// so let's take the next one and continue the scan there.
if (scanOffset == seg.size) {
val next = seg.next ?: return -1
seg = next
data = seg.dataAsByteArray(true)
scanOffset = 0 // we're scanning the next segment right from the beginning
}
if (element != data[seg.pos + scanOffset]) {
found = false
break
}
scanOffset++
}
if (found) {
return offset
}
offset++
}
return -1
}
@PublishedApi
internal fun Segment.isEmpty(): Boolean = size == 0