Optimize 'windowed' and 'chunked' for char sequences, iterables and sequences
This commit is contained in:
@@ -1790,7 +1790,7 @@ public fun <T : Any> List<T?>.requireNoNulls(): List<T> {
|
|||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <T> Iterable<T>.chunked(size: Int): List<List<T>> {
|
public fun <T> Iterable<T>.chunked(size: Int): List<List<T>> {
|
||||||
return chunked(size) { it.toList() }
|
return windowed(size, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
@@ -1988,15 +1988,44 @@ public inline fun <T> Collection<T>.plusElement(element: T): List<T> {
|
|||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <T> Iterable<T>.windowed(size: Int, step: Int): List<List<T>> {
|
public fun <T> Iterable<T>.windowed(size: Int, step: Int): List<List<T>> {
|
||||||
return windowed(size, step) { it.toList() }
|
checkWindowSizeStep(size, step)
|
||||||
|
if (this is RandomAccess && this is List) {
|
||||||
|
val thisSize = this.size
|
||||||
|
val result = ArrayList<List<T>>((thisSize + step - 1) / step)
|
||||||
|
var index = 0
|
||||||
|
while (index < thisSize) {
|
||||||
|
result.add(List(size.coerceAtMost(thisSize - index)) { this[it + index] })
|
||||||
|
index += step
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
val result = ArrayList<List<T>>()
|
||||||
|
windowedIterator(iterator(), size, step, dropTrailing = false, reuseBuffer = false).forEach {
|
||||||
|
result.add(it)
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <T, R> Iterable<T>.windowed(size: Int, step: Int, transform: (List<T>) -> R): List<R> {
|
public fun <T, R> Iterable<T>.windowed(size: Int, step: Int, transform: (List<T>) -> R): List<R> {
|
||||||
if (this is List) {
|
checkWindowSizeStep(size, step)
|
||||||
return windowIndices(this.size, size, step, dropTrailing = false).asIterable().map { transform(subList(it.start, it.endInclusive + 1)) }
|
if (this is RandomAccess && this is List) {
|
||||||
|
val thisSize = this.size
|
||||||
|
val result = ArrayList<R>((thisSize + step - 1) / step)
|
||||||
|
val window = MovingSubList(this)
|
||||||
|
var index = 0
|
||||||
|
while (index < thisSize) {
|
||||||
|
window.move(index, (index + size).coerceAtMost(thisSize))
|
||||||
|
result.add(transform(window))
|
||||||
|
index += step
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
return windowForwardOnlySequenceImpl(iterator(), size, step, dropTrailing = false).asIterable().map(transform)
|
val result = ArrayList<R>()
|
||||||
|
windowedIterator(iterator(), size, step, dropTrailing = false, reuseBuffer = true).forEach {
|
||||||
|
result.add(transform(it))
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1311,7 +1311,7 @@ public fun <T : Any> Sequence<T?>.requireNoNulls(): Sequence<T> {
|
|||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <T> Sequence<T>.chunked(size: Int): Sequence<List<T>> {
|
public fun <T> Sequence<T>.chunked(size: Int): Sequence<List<T>> {
|
||||||
return chunked(size) { it.toList() }
|
return windowed(size, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
@@ -1497,13 +1497,12 @@ public inline fun <T> Sequence<T>.plusElement(element: T): Sequence<T> {
|
|||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <T> Sequence<T>.windowed(size: Int, step: Int): Sequence<List<T>> {
|
public fun <T> Sequence<T>.windowed(size: Int, step: Int): Sequence<List<T>> {
|
||||||
return windowed(size, step) { it.toList() }
|
return windowedSequence(size, step, dropTrailing = false, reuseBuffer = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <T, R> Sequence<T>.windowed(size: Int, step: Int, transform: (List<T>) -> R): Sequence<R> {
|
public fun <T, R> Sequence<T>.windowed(size: Int, step: Int, transform: (List<T>) -> R): Sequence<R> {
|
||||||
require(size > 0 && step > 0) { "size $size and step $step both must be greater than zero" }
|
return windowedSequence(size, step, dropTrailing = false, reuseBuffer = true).map(transform)
|
||||||
return Sequence { windowForwardOnlySequenceImpl(iterator(), size, step, dropTrailing = false).iterator() }.map(transform)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1104,7 +1104,7 @@ public inline fun CharSequence.sumByDouble(selector: (Char) -> Double): Double {
|
|||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun CharSequence.chunked(size: Int): List<String> {
|
public fun CharSequence.chunked(size: Int): List<String> {
|
||||||
return chunked(size) { it.toString() }
|
return windowed(size, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
@@ -1181,7 +1181,15 @@ public fun CharSequence.windowed(size: Int, step: Int): List<String> {
|
|||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <R> CharSequence.windowed(size: Int, step: Int, transform: (CharSequence) -> R): List<R> {
|
public fun <R> CharSequence.windowed(size: Int, step: Int, transform: (CharSequence) -> R): List<R> {
|
||||||
return windowIndices(this.length, size, step, dropTrailing = false).asIterable().map { transform(subSequence(it)) }
|
checkWindowSizeStep(size, step)
|
||||||
|
val thisSize = this.length
|
||||||
|
val result = ArrayList<R>((thisSize + step - 1) / step)
|
||||||
|
var index = 0
|
||||||
|
while (index < thisSize) {
|
||||||
|
result.add(transform(subSequence(index, (index + size).coerceAtMost(thisSize))))
|
||||||
|
index += step
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
@@ -1191,7 +1199,8 @@ public fun CharSequence.windowedSequence(size: Int, step: Int): Sequence<String>
|
|||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <R> CharSequence.windowedSequence(size: Int, step: Int, transform: (CharSequence) -> R): Sequence<R> {
|
public fun <R> CharSequence.windowedSequence(size: Int, step: Int, transform: (CharSequence) -> R): Sequence<R> {
|
||||||
return windowIndices(this.length, size, step, dropTrailing = false).map { transform(subSequence(it)) }
|
checkWindowSizeStep(size, step)
|
||||||
|
return (indices step step).asSequence().map { index -> transform(subSequence(index, (index + size).coerceAtMost(length))) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1800,7 +1800,7 @@ public fun <T : Any> List<T?>.requireNoNulls(): List<T> {
|
|||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <T> Iterable<T>.chunked(size: Int): List<List<T>> {
|
public fun <T> Iterable<T>.chunked(size: Int): List<List<T>> {
|
||||||
return chunked(size) { it.toList() }
|
return windowed(size, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
@@ -1998,15 +1998,44 @@ public inline fun <T> Collection<T>.plusElement(element: T): List<T> {
|
|||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <T> Iterable<T>.windowed(size: Int, step: Int): List<List<T>> {
|
public fun <T> Iterable<T>.windowed(size: Int, step: Int): List<List<T>> {
|
||||||
return windowed(size, step) { it.toList() }
|
checkWindowSizeStep(size, step)
|
||||||
|
if (this is RandomAccess && this is List) {
|
||||||
|
val thisSize = this.size
|
||||||
|
val result = ArrayList<List<T>>((thisSize + step - 1) / step)
|
||||||
|
var index = 0
|
||||||
|
while (index < thisSize) {
|
||||||
|
result.add(List(size.coerceAtMost(thisSize - index)) { this[it + index] })
|
||||||
|
index += step
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
val result = ArrayList<List<T>>()
|
||||||
|
windowedIterator(iterator(), size, step, dropTrailing = false, reuseBuffer = false).forEach {
|
||||||
|
result.add(it)
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <T, R> Iterable<T>.windowed(size: Int, step: Int, transform: (List<T>) -> R): List<R> {
|
public fun <T, R> Iterable<T>.windowed(size: Int, step: Int, transform: (List<T>) -> R): List<R> {
|
||||||
if (this is List) {
|
checkWindowSizeStep(size, step)
|
||||||
return windowIndices(this.size, size, step, dropTrailing = false).asIterable().map { transform(subList(it.start, it.endInclusive + 1)) }
|
if (this is RandomAccess && this is List) {
|
||||||
|
val thisSize = this.size
|
||||||
|
val result = ArrayList<R>((thisSize + step - 1) / step)
|
||||||
|
val window = MovingSubList(this)
|
||||||
|
var index = 0
|
||||||
|
while (index < thisSize) {
|
||||||
|
window.move(index, (index + size).coerceAtMost(thisSize))
|
||||||
|
result.add(transform(window))
|
||||||
|
index += step
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
return windowForwardOnlySequenceImpl(iterator(), size, step, dropTrailing = false).asIterable().map(transform)
|
val result = ArrayList<R>()
|
||||||
|
windowedIterator(iterator(), size, step, dropTrailing = false, reuseBuffer = true).forEach {
|
||||||
|
result.add(transform(it))
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1333,7 +1333,7 @@ public fun <T : Any> Sequence<T?>.requireNoNulls(): Sequence<T> {
|
|||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <T> Sequence<T>.chunked(size: Int): Sequence<List<T>> {
|
public fun <T> Sequence<T>.chunked(size: Int): Sequence<List<T>> {
|
||||||
return chunked(size) { it.toList() }
|
return windowed(size, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
@@ -1519,13 +1519,12 @@ public inline fun <T> Sequence<T>.plusElement(element: T): Sequence<T> {
|
|||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <T> Sequence<T>.windowed(size: Int, step: Int): Sequence<List<T>> {
|
public fun <T> Sequence<T>.windowed(size: Int, step: Int): Sequence<List<T>> {
|
||||||
return windowed(size, step) { it.toList() }
|
return windowedSequence(size, step, dropTrailing = false, reuseBuffer = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <T, R> Sequence<T>.windowed(size: Int, step: Int, transform: (List<T>) -> R): Sequence<R> {
|
public fun <T, R> Sequence<T>.windowed(size: Int, step: Int, transform: (List<T>) -> R): Sequence<R> {
|
||||||
require(size > 0 && step > 0) { "size $size and step $step both must be greater than zero" }
|
return windowedSequence(size, step, dropTrailing = false, reuseBuffer = true).map(transform)
|
||||||
return Sequence { windowForwardOnlySequenceImpl(iterator(), size, step, dropTrailing = false).iterator() }.map(transform)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1112,7 +1112,7 @@ public inline fun CharSequence.sumByDouble(selector: (Char) -> Double): Double {
|
|||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun CharSequence.chunked(size: Int): List<String> {
|
public fun CharSequence.chunked(size: Int): List<String> {
|
||||||
return chunked(size) { it.toString() }
|
return windowed(size, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
@@ -1189,7 +1189,15 @@ public fun CharSequence.windowed(size: Int, step: Int): List<String> {
|
|||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <R> CharSequence.windowed(size: Int, step: Int, transform: (CharSequence) -> R): List<R> {
|
public fun <R> CharSequence.windowed(size: Int, step: Int, transform: (CharSequence) -> R): List<R> {
|
||||||
return windowIndices(this.length, size, step, dropTrailing = false).asIterable().map { transform(subSequence(it)) }
|
checkWindowSizeStep(size, step)
|
||||||
|
val thisSize = this.length
|
||||||
|
val result = ArrayList<R>((thisSize + step - 1) / step)
|
||||||
|
var index = 0
|
||||||
|
while (index < thisSize) {
|
||||||
|
result.add(transform(subSequence(index, (index + size).coerceAtMost(thisSize))))
|
||||||
|
index += step
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
@@ -1199,7 +1207,8 @@ public fun CharSequence.windowedSequence(size: Int, step: Int): Sequence<String>
|
|||||||
|
|
||||||
@SinceKotlin("1.2")
|
@SinceKotlin("1.2")
|
||||||
public fun <R> CharSequence.windowedSequence(size: Int, step: Int, transform: (CharSequence) -> R): Sequence<R> {
|
public fun <R> CharSequence.windowedSequence(size: Int, step: Int, transform: (CharSequence) -> R): Sequence<R> {
|
||||||
return windowIndices(this.length, size, step, dropTrailing = false).map { transform(subSequence(it)) }
|
checkWindowSizeStep(size, step)
|
||||||
|
return (indices step step).asSequence().map { index -> transform(subSequence(index, (index + size).coerceAtMost(length))) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,119 +16,90 @@
|
|||||||
|
|
||||||
package kotlin.collections
|
package kotlin.collections
|
||||||
|
|
||||||
|
import kotlin.coroutines.experimental.buildIterator
|
||||||
|
|
||||||
internal fun windowIndices(sourceSize: Int, size: Int, step: Int, dropTrailing: Boolean): Sequence<IntRange> {
|
internal fun checkWindowSizeStep(size: Int, step: Int) {
|
||||||
require(size > 0 && step > 0) { "size $size and step $step both must be greater than zero" }
|
require(size > 0 && step > 0) {
|
||||||
|
if (size != step)
|
||||||
if (sourceSize == 0 || (size > sourceSize && dropTrailing)) {
|
"Both size $size and step $step must be greater than zero."
|
||||||
return emptySequence()
|
else
|
||||||
}
|
"size $size must be greater than zero."
|
||||||
if (size == 0) {
|
|
||||||
return when {
|
|
||||||
step > 0 -> (0 .. sourceSize - 1 step step)
|
|
||||||
else -> (sourceSize - 1 downTo 0 step -step)
|
|
||||||
}.asSequence().map { it .. it - 1 } // empty ranges with valid start
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentIndex = when {
|
|
||||||
step > 0 -> 0
|
|
||||||
else -> sourceSize - size
|
|
||||||
}
|
|
||||||
|
|
||||||
return generateSequence {
|
|
||||||
val startIndex = currentIndex
|
|
||||||
val endExclusive = currentIndex + size
|
|
||||||
|
|
||||||
when {
|
|
||||||
startIndex >= sourceSize -> null
|
|
||||||
endExclusive > sourceSize && dropTrailing -> null
|
|
||||||
startIndex < 0 && dropTrailing -> null
|
|
||||||
step < 0 && endExclusive <= 0 -> null
|
|
||||||
else -> {
|
|
||||||
currentIndex += step
|
|
||||||
startIndex.coerceAtLeast(0) .. endExclusive.coerceAtMost(sourceSize) - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun <T> windowForwardOnlySequenceImpl(iterator: Iterator<T>, size: Int, step: Int, dropTrailing: Boolean): Sequence<List<T>> {
|
internal fun <T> Sequence<T>.windowedSequence(size: Int, step: Int, dropTrailing: Boolean, reuseBuffer: Boolean): Sequence<List<T>> {
|
||||||
require(size > 0 && step > 0) { "size $size and step $step both must be greater than zero" }
|
checkWindowSizeStep(size, step)
|
||||||
|
return Sequence { windowedIterator(iterator(), size, step, dropTrailing, reuseBuffer) }
|
||||||
return if (step >= size) {
|
|
||||||
windowForwardWithGap(iterator, size, step, dropTrailing)
|
|
||||||
} else {
|
|
||||||
windowForwardWithOverlap(iterator, size, step, dropTrailing)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> windowForwardWithGap(iterator: Iterator<T>, size: Int, step: Int, dropTrailing: Boolean): Sequence<List<T>> {
|
internal fun <T> windowedIterator(iterator: Iterator<T>, size: Int, step: Int, dropTrailing: Boolean, reuseBuffer: Boolean): Iterator<List<T>> {
|
||||||
require(step >= size)
|
if (!iterator.hasNext()) return EmptyIterator
|
||||||
var first = true
|
return buildIterator<List<T>> {
|
||||||
val gap = step - size
|
val gap = step - size
|
||||||
|
if (gap >= 0) {
|
||||||
fun skipGap() {
|
var buffer = ArrayList<T>(size)
|
||||||
for (skip in 1..gap) {
|
var skip = 0
|
||||||
if (!iterator.hasNext()) {
|
for (e in iterator) {
|
||||||
break
|
if (skip > 0) { skip -= 1; continue }
|
||||||
|
buffer.add(e)
|
||||||
|
if (buffer.size == size) {
|
||||||
|
yield(buffer)
|
||||||
|
if (reuseBuffer) buffer.clear() else buffer = ArrayList(size)
|
||||||
|
skip = gap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (buffer.isNotEmpty()) {
|
||||||
|
if (!dropTrailing || buffer.size == size) yield(buffer)
|
||||||
}
|
}
|
||||||
iterator.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return generateSequence {
|
|
||||||
if (first) {
|
|
||||||
first = false
|
|
||||||
} else {
|
} else {
|
||||||
skipGap()
|
val buffer = RingBuffer<T>(size)
|
||||||
}
|
for (e in iterator) {
|
||||||
|
buffer.add(e)
|
||||||
val buffer = ArrayList<T>(size)
|
if (buffer.isFull()) {
|
||||||
for (i in 1..size) {
|
yield(if (reuseBuffer) buffer else ArrayList(buffer))
|
||||||
if (!iterator.hasNext()) {
|
buffer.removeFirst(step)
|
||||||
break
|
}
|
||||||
|
}
|
||||||
|
if (dropTrailing) {
|
||||||
|
if (buffer.size == size) yield(buffer)
|
||||||
|
} else {
|
||||||
|
while (buffer.size > step) {
|
||||||
|
yield(if (reuseBuffer) buffer else ArrayList(buffer))
|
||||||
|
buffer.removeFirst(step)
|
||||||
|
}
|
||||||
|
if (buffer.isNotEmpty()) yield(buffer)
|
||||||
}
|
}
|
||||||
buffer.add(iterator.next())
|
|
||||||
}
|
|
||||||
|
|
||||||
when {
|
|
||||||
buffer.isEmpty() && !iterator.hasNext() -> null
|
|
||||||
buffer.size < size && dropTrailing -> null
|
|
||||||
else -> buffer
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> windowForwardWithOverlap(iterator: Iterator<T>, size: Int, step: Int, dropTrailing: Boolean): Sequence<List<T>> {
|
internal class MovingSubList<out E>(private val list: List<E>) : AbstractList<E>(), RandomAccess {
|
||||||
require(step < size)
|
private var fromIndex: Int = 0
|
||||||
|
private var _size: Int = 0
|
||||||
|
|
||||||
val buffer = RingBuffer<T>(size)
|
fun move(fromIndex: Int, toIndex: Int) {
|
||||||
|
checkRangeIndexes(fromIndex, toIndex, list.size)
|
||||||
return generateSequence {
|
this.fromIndex = fromIndex
|
||||||
if (!buffer.isEmpty()) {
|
this._size = toIndex - fromIndex
|
||||||
buffer.removeFirst(minOf(step, buffer.size))
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!buffer.isFull() && iterator.hasNext()) {
|
|
||||||
buffer.add(iterator.next())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
when {
|
|
||||||
buffer.isEmpty() && !iterator.hasNext() -> null
|
|
||||||
!buffer.isFull() && dropTrailing -> null
|
|
||||||
else -> buffer.toArray().asList() as List<T>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun get(index: Int): E {
|
||||||
|
checkElementIndex(index, _size)
|
||||||
|
|
||||||
|
return list[fromIndex + index]
|
||||||
|
}
|
||||||
|
|
||||||
|
override val size: Int get() = _size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides ring buffer implementation.
|
* Provides ring buffer implementation.
|
||||||
*
|
*
|
||||||
* Buffer overflow is not allowed so [add] doesn't overwrite tail but raises an exception while [offer] returns `false`
|
* Buffer overflow is not allowed so [add] doesn't overwrite tail but raises an exception while [offer] returns `false`
|
||||||
* If it is going to be public API perhaps this behaviour could be customizable
|
* If it is going to be public API perhaps this behaviour could be customizable
|
||||||
*/
|
*/
|
||||||
internal class RingBuffer<T>(val capacity: Int): Iterable<T> {
|
private class RingBuffer<T>(val capacity: Int): AbstractList<T>(), RandomAccess {
|
||||||
init {
|
init {
|
||||||
require(capacity >= 0) { "ring buffer capacity should not be negative but it is $capacity" }
|
require(capacity >= 0) { "ring buffer capacity should not be negative but it is $capacity" }
|
||||||
}
|
}
|
||||||
@@ -136,31 +107,32 @@ internal class RingBuffer<T>(val capacity: Int): Iterable<T> {
|
|||||||
private val buffer = arrayOfNulls<Any?>(capacity)
|
private val buffer = arrayOfNulls<Any?>(capacity)
|
||||||
private var writePosition = 0
|
private var writePosition = 0
|
||||||
|
|
||||||
var size: Int = 0
|
override var size: Int = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
fun isEmpty() = size == 0
|
override fun get(index: Int): T {
|
||||||
|
checkElementIndex(index, size)
|
||||||
|
return getAtUnsafe(writePosition.backward(size - index))
|
||||||
|
}
|
||||||
|
|
||||||
fun isFull() = size == capacity
|
fun isFull() = size == capacity
|
||||||
|
|
||||||
override fun iterator(): Iterator<T> = when {
|
override fun iterator(): Iterator<T> = object : AbstractIterator<T>() {
|
||||||
isEmpty() -> EmptyIterator
|
|
||||||
else -> object : AbstractIterator<T>() {
|
|
||||||
private var count = size
|
private var count = size
|
||||||
private var idx = writePosition.backward(count)
|
private var index = writePosition.backward(size)
|
||||||
|
|
||||||
override fun computeNext() {
|
override fun computeNext() {
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
done()
|
done()
|
||||||
} else {
|
} else {
|
||||||
setNext(getAtUnsafe(idx))
|
setNext(getAtUnsafe(index))
|
||||||
idx = idx.forward()
|
index = index.forward(1)
|
||||||
count--
|
count--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toArray(): Array<out Any?> {
|
override fun toArray(): Array<Any?> {
|
||||||
val size = this.size
|
val size = this.size
|
||||||
val result = arrayOfNulls<Any?>(size)
|
val result = arrayOfNulls<Any?>(size)
|
||||||
var widx = 0
|
var widx = 0
|
||||||
@@ -187,7 +159,7 @@ internal class RingBuffer<T>(val capacity: Int): Iterable<T> {
|
|||||||
*/
|
*/
|
||||||
fun add(element: T) {
|
fun add(element: T) {
|
||||||
if (!offer(element)) {
|
if (!offer(element)) {
|
||||||
throw IllegalStateException("ring buffer is full")
|
throw IllegalStateException("Ring buffer is full.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,29 +172,11 @@ internal class RingBuffer<T>(val capacity: Int): Iterable<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buffer[writePosition] = element
|
buffer[writePosition] = element
|
||||||
writePosition = writePosition.forward()
|
writePosition = writePosition.forward(1)
|
||||||
size++
|
size++
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes first element from the buffer or fails with [NoSuchElementException] if the buffer is empty
|
|
||||||
*/
|
|
||||||
fun get(): T {
|
|
||||||
if (isEmpty()) {
|
|
||||||
throw NoSuchElementException("ring buffer is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
val readPosition = writePosition.backward(size)
|
|
||||||
|
|
||||||
val result = getAtUnsafe(readPosition)
|
|
||||||
buffer[readPosition] = null
|
|
||||||
|
|
||||||
size--
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes [n] first elements from the buffer or fails with [IllegalArgumentException] if not enough elements in the buffer to remove
|
* Removes [n] first elements from the buffer or fails with [IllegalArgumentException] if not enough elements in the buffer to remove
|
||||||
*/
|
*/
|
||||||
@@ -234,9 +188,6 @@ internal class RingBuffer<T>(val capacity: Int): Iterable<T> {
|
|||||||
val start = writePosition.backward(size)
|
val start = writePosition.backward(size)
|
||||||
val end = start.forward(n - 1)
|
val end = start.forward(n - 1)
|
||||||
|
|
||||||
for (i in start .. end) {
|
|
||||||
buffer[i] = null
|
|
||||||
}
|
|
||||||
if (start > end) {
|
if (start > end) {
|
||||||
buffer.fill(null, start, capacity)
|
buffer.fill(null, start, capacity)
|
||||||
buffer.fill(null, 0, end + 1)
|
buffer.fill(null, 0, end + 1)
|
||||||
@@ -248,33 +199,15 @@ internal class RingBuffer<T>(val capacity: Int): Iterable<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all elements from the buffer
|
|
||||||
*/
|
|
||||||
fun clear() {
|
|
||||||
size = 0
|
|
||||||
buffer.fill(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
|
@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
|
||||||
private inline fun getAtUnsafe(idx: Int): T = buffer[idx] as T
|
private inline fun getAtUnsafe(idx: Int): T = buffer[idx] as T
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
private inline fun Int.forward(n: Int = 1): Int {
|
private inline fun Int.forward(n: Int): Int = (this + n) % capacity
|
||||||
require(n >= 0)
|
|
||||||
require(n <= capacity)
|
|
||||||
|
|
||||||
val result = this + n
|
|
||||||
return if (result >= capacity) result - capacity else result
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
private inline fun Int.backward(n: Int = 1): Int {
|
private inline fun Int.backward(n: Int): Int = ((this - n) % capacity + capacity) % capacity
|
||||||
require(n >= 0)
|
|
||||||
require(n <= capacity)
|
|
||||||
|
|
||||||
return if (this < n) (this - n + capacity) else this - n
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: replace with Array.fill from stdlib when available in common
|
// TODO: replace with Array.fill from stdlib when available in common
|
||||||
private fun <T> Array<T>.fill(element: T, fromIndex: Int = 0, toIndex: Int = size): Unit {
|
private fun <T> Array<T>.fill(element: T, fromIndex: Int = 0, toIndex: Int = size): Unit {
|
||||||
|
|||||||
@@ -568,25 +568,46 @@ fun generators(): List<GenericFunction> {
|
|||||||
|
|
||||||
body {
|
body {
|
||||||
"""
|
"""
|
||||||
if (this is List) {
|
checkWindowSizeStep(size, step)
|
||||||
return windowIndices(this.size, size, step, dropTrailing = false).asIterable().map { transform(subList(it.start, it.endInclusive + 1)) }
|
if (this is RandomAccess && this is List) {
|
||||||
|
val thisSize = this.size
|
||||||
|
val result = ArrayList<R>((thisSize + step - 1) / step)
|
||||||
|
val window = MovingSubList(this)
|
||||||
|
var index = 0
|
||||||
|
while (index < thisSize) {
|
||||||
|
window.move(index, (index + size).coerceAtMost(thisSize))
|
||||||
|
result.add(transform(window))
|
||||||
|
index += step
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
return windowForwardOnlySequenceImpl(iterator(), size, step, dropTrailing = false).asIterable().map(transform)
|
val result = ArrayList<R>()
|
||||||
|
windowedIterator(iterator(), size, step, dropTrailing = false, reuseBuffer = true).forEach {
|
||||||
|
result.add(transform(it))
|
||||||
|
}
|
||||||
|
return result
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
customSignature(CharSequences) { "windowed(size: Int, step: Int, transform: (CharSequence) -> R)" }
|
customSignature(CharSequences) { "windowed(size: Int, step: Int, transform: (CharSequence) -> R)" }
|
||||||
body(CharSequences) {
|
body(CharSequences) {
|
||||||
"""
|
"""
|
||||||
return windowIndices(this.length, size, step, dropTrailing = false).asIterable().map { transform(subSequence(it)) }
|
checkWindowSizeStep(size, step)
|
||||||
|
val thisSize = this.length
|
||||||
|
val result = ArrayList<R>((thisSize + step - 1) / step)
|
||||||
|
var index = 0
|
||||||
|
while (index < thisSize) {
|
||||||
|
result.add(transform(subSequence(index, (index + size).coerceAtMost(thisSize))))
|
||||||
|
index += step
|
||||||
|
}
|
||||||
|
return result
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
returns(Sequences) { "Sequence<R>" }
|
returns(Sequences) { "Sequence<R>" }
|
||||||
body(Sequences) {
|
body(Sequences) {
|
||||||
"""
|
"""
|
||||||
require(size > 0 && step > 0) { "size ${"$"}size and step ${"$"}step both must be greater than zero" }
|
return windowedSequence(size, step, dropTrailing = false, reuseBuffer = true).map(transform)
|
||||||
return Sequence { windowForwardOnlySequenceImpl(iterator(), size, step, dropTrailing = false).iterator() }.map(transform)
|
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -598,8 +619,33 @@ fun generators(): List<GenericFunction> {
|
|||||||
returns(Sequences) { "Sequence<List<T>>" }
|
returns(Sequences) { "Sequence<List<T>>" }
|
||||||
returns(CharSequences) { "List<String>" }
|
returns(CharSequences) { "List<String>" }
|
||||||
|
|
||||||
body { "return windowed(size, step) { it.toList() }" }
|
|
||||||
|
body {
|
||||||
|
"""
|
||||||
|
checkWindowSizeStep(size, step)
|
||||||
|
if (this is RandomAccess && this is List) {
|
||||||
|
val thisSize = this.size
|
||||||
|
val result = ArrayList<List<T>>((thisSize + step - 1) / step)
|
||||||
|
var index = 0
|
||||||
|
while (index < thisSize) {
|
||||||
|
result.add(List(size.coerceAtMost(thisSize - index)) { this[it + index] })
|
||||||
|
index += step
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
val result = ArrayList<List<T>>()
|
||||||
|
windowedIterator(iterator(), size, step, dropTrailing = false, reuseBuffer = false).forEach {
|
||||||
|
result.add(it)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
"""
|
||||||
|
}
|
||||||
body(CharSequences) { "return windowed(size, step) { it.toString() }" }
|
body(CharSequences) { "return windowed(size, step) { it.toString() }" }
|
||||||
|
body(Sequences) {
|
||||||
|
"""
|
||||||
|
return windowedSequence(size, step, dropTrailing = false, reuseBuffer = false)
|
||||||
|
"""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
templates add f("windowedSequence(size: Int, step: Int, transform: (CharSequence) -> R)") {
|
templates add f("windowedSequence(size: Int, step: Int, transform: (CharSequence) -> R)") {
|
||||||
@@ -608,9 +654,10 @@ fun generators(): List<GenericFunction> {
|
|||||||
typeParam("R")
|
typeParam("R")
|
||||||
returns { "Sequence<R> "}
|
returns { "Sequence<R> "}
|
||||||
|
|
||||||
body(CharSequences) {
|
body {
|
||||||
"""
|
"""
|
||||||
return windowIndices(this.length, size, step, dropTrailing = false).map { transform(subSequence(it)) }
|
checkWindowSizeStep(size, step)
|
||||||
|
return (indices step step).asSequence().map { index -> transform(subSequence(index, (index + size).coerceAtMost(length))) }
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -643,8 +690,7 @@ fun generators(): List<GenericFunction> {
|
|||||||
returns(Sequences) { "Sequence<List<T>>" }
|
returns(Sequences) { "Sequence<List<T>>" }
|
||||||
returns(CharSequences) { "List<String>" }
|
returns(CharSequences) { "List<String>" }
|
||||||
|
|
||||||
body { "return chunked(size) { it.toList() }" }
|
body { "return windowed(size, size)" }
|
||||||
body(CharSequences) { "return chunked(size) { it.toString() }" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templates add f("chunkedSequence(size: Int, transform: (CharSequence) -> R)") {
|
templates add f("chunkedSequence(size: Int, transform: (CharSequence) -> R)") {
|
||||||
@@ -653,7 +699,11 @@ fun generators(): List<GenericFunction> {
|
|||||||
typeParam("R")
|
typeParam("R")
|
||||||
returns { "Sequence<R> "}
|
returns { "Sequence<R> "}
|
||||||
|
|
||||||
body { "return windowedSequence(size, size, transform)" }
|
body {
|
||||||
|
"""
|
||||||
|
return windowedSequence(size, size, transform)
|
||||||
|
"""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
templates add f("chunkedSequence(size: Int)") {
|
templates add f("chunkedSequence(size: Int)") {
|
||||||
|
|||||||
Reference in New Issue
Block a user