CollectionsKt.windowed throws IllegalArgumentException
This commit is contained in:
@@ -2147,7 +2147,8 @@ public fun <T> Iterable<T>.windowed(size: Int, step: Int = 1, partialWindows: Bo
|
||||
checkWindowSizeStep(size, step)
|
||||
if (this is RandomAccess && this is List) {
|
||||
val thisSize = this.size
|
||||
val result = ArrayList<List<T>>((thisSize + step - 1) / step)
|
||||
val resultCapacity = thisSize / step + if (thisSize % step == 0) 0 else 1
|
||||
val result = ArrayList<List<T>>(resultCapacity)
|
||||
var index = 0
|
||||
while (index < thisSize) {
|
||||
val windowSize = size.coerceAtMost(thisSize - index)
|
||||
|
||||
@@ -1374,7 +1374,8 @@ public fun CharSequence.windowed(size: Int, step: Int = 1, partialWindows: Boole
|
||||
public fun <R> CharSequence.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (CharSequence) -> R): List<R> {
|
||||
checkWindowSizeStep(size, step)
|
||||
val thisSize = this.length
|
||||
val result = ArrayList<R>((thisSize + step - 1) / step)
|
||||
val resultCapacity = thisSize / step + if (thisSize % step == 0) 0 else 1
|
||||
val result = ArrayList<R>(resultCapacity)
|
||||
var index = 0
|
||||
while (index < thisSize) {
|
||||
val end = index + size
|
||||
|
||||
@@ -22,9 +22,10 @@ internal fun <T> Sequence<T>.windowedSequence(size: Int, step: Int, partialWindo
|
||||
internal fun <T> windowedIterator(iterator: Iterator<T>, size: Int, step: Int, partialWindows: Boolean, reuseBuffer: Boolean): Iterator<List<T>> {
|
||||
if (!iterator.hasNext()) return EmptyIterator
|
||||
return iterator<List<T>> {
|
||||
val bufferInitialCapacity = size.coerceAtMost(1024)
|
||||
val gap = step - size
|
||||
if (gap >= 0) {
|
||||
var buffer = ArrayList<T>(size)
|
||||
var buffer = ArrayList<T>(bufferInitialCapacity)
|
||||
var skip = 0
|
||||
for (e in iterator) {
|
||||
if (skip > 0) { skip -= 1; continue }
|
||||
@@ -39,10 +40,12 @@ internal fun <T> windowedIterator(iterator: Iterator<T>, size: Int, step: Int, p
|
||||
if (partialWindows || buffer.size == size) yield(buffer)
|
||||
}
|
||||
} else {
|
||||
val buffer = RingBuffer<T>(size)
|
||||
var buffer = RingBuffer<T>(bufferInitialCapacity)
|
||||
for (e in iterator) {
|
||||
buffer.add(e)
|
||||
if (buffer.isFull()) {
|
||||
if (buffer.size < size) { buffer = buffer.expanded(maxCapacity = size); continue }
|
||||
|
||||
yield(if (reuseBuffer) buffer else ArrayList(buffer))
|
||||
buffer.removeFirst(step)
|
||||
}
|
||||
@@ -83,15 +86,18 @@ internal class MovingSubList<out E>(private val list: List<E>) : AbstractList<E>
|
||||
*
|
||||
* Buffer overflow is not allowed so [add] doesn't overwrite tail but raises an exception.
|
||||
*/
|
||||
private class RingBuffer<T>(val capacity: Int) : AbstractList<T>(), RandomAccess {
|
||||
private class RingBuffer<T>(private val buffer: Array<Any?>, filledSize: Int) : AbstractList<T>(), RandomAccess {
|
||||
init {
|
||||
require(capacity >= 0) { "ring buffer capacity should not be negative but it is $capacity" }
|
||||
require(filledSize >= 0) { "ring buffer filled size should not be negative but it is $filledSize" }
|
||||
require(filledSize <= buffer.size) { "ring buffer filled size: $filledSize cannot be larger than the buffer size: ${buffer.size}" }
|
||||
}
|
||||
|
||||
private val buffer = arrayOfNulls<Any?>(capacity)
|
||||
constructor(capacity: Int) : this(arrayOfNulls<Any?>(capacity), 0)
|
||||
|
||||
private val capacity = buffer.size
|
||||
private var startIndex: Int = 0
|
||||
|
||||
override var size: Int = 0
|
||||
override var size: Int = filledSize
|
||||
private set
|
||||
|
||||
override fun get(index: Int): T {
|
||||
@@ -149,6 +155,16 @@ private class RingBuffer<T>(val capacity: Int) : AbstractList<T>(), RandomAccess
|
||||
return toArray(arrayOfNulls(size))
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ring buffer with the capacity equal to the minimum of [maxCapacity] and 1.5 * [capacity].
|
||||
* The returned ring buffer contains the same elements as this ring buffer.
|
||||
*/
|
||||
fun expanded(maxCapacity: Int): RingBuffer<T> {
|
||||
val newCapacity = (capacity + (capacity shr 1) + 1).coerceAtMost(maxCapacity)
|
||||
val newBuffer = if (startIndex == 0) buffer.copyOf(newCapacity) else toArray(arrayOfNulls(newCapacity))
|
||||
return RingBuffer(newBuffer, size)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add [element] to the buffer or fail with [IllegalStateException] if no free space available in the buffer
|
||||
*/
|
||||
|
||||
@@ -140,6 +140,11 @@ abstract class OrderedIterableTests<T : Iterable<String>>(createFrom: (Array<out
|
||||
data.toList().let { expectedSingleChunk ->
|
||||
assertEquals(expectedSingleChunk, data.chunked(size).single())
|
||||
assertEquals(expectedSingleChunk, data.chunked(size + 3).single())
|
||||
assertEquals(expectedSingleChunk, data.chunked(Int.MAX_VALUE).single())
|
||||
}
|
||||
|
||||
createFrom("a", "b").let { iterable ->
|
||||
assertEquals(iterable.toList(), iterable.chunked(Int.MAX_VALUE).single())
|
||||
}
|
||||
|
||||
assertTrue(empty.chunked(3).isEmpty())
|
||||
|
||||
@@ -230,6 +230,11 @@ public class SequenceTest {
|
||||
seq.toList().let { expectedSingleChunk ->
|
||||
assertEquals(expectedSingleChunk, seq.chunked(size).single())
|
||||
assertEquals(expectedSingleChunk, seq.chunked(size + 3).single())
|
||||
assertEquals(expectedSingleChunk, seq.chunked(Int.MAX_VALUE).single())
|
||||
}
|
||||
|
||||
infiniteSeq.take(2).let { seq2 ->
|
||||
assertEquals(seq2.toList(), seq2.chunked(Int.MAX_VALUE).single())
|
||||
}
|
||||
|
||||
assertTrue(emptySequence<String>().chunked(3).none())
|
||||
@@ -251,6 +256,16 @@ public class SequenceTest {
|
||||
assertEquals((startElement until startElement + 5).toList(), window)
|
||||
}
|
||||
|
||||
infiniteSeq.take(3500).windowed(2000, 1000, partialWindows = true).forEachIndexed { windowIndex, window ->
|
||||
val startElement = windowIndex * 1000
|
||||
val elementCount = when (windowIndex) {
|
||||
3 -> 500
|
||||
2 -> 1500
|
||||
else -> 2000
|
||||
}
|
||||
assertEquals((startElement until startElement + elementCount).toList(), window)
|
||||
}
|
||||
|
||||
val size = 7
|
||||
val seq = infiniteSeq.take(7)
|
||||
|
||||
|
||||
@@ -1068,6 +1068,11 @@ class StringTest {
|
||||
data.toString().let { expectedSingleChunk ->
|
||||
assertEquals(expectedSingleChunk, data.chunked(size).single())
|
||||
assertEquals(expectedSingleChunk, data.chunked(size + 3).single())
|
||||
assertEquals(expectedSingleChunk, data.chunked(Int.MAX_VALUE).single())
|
||||
}
|
||||
|
||||
arg1("ab").let {
|
||||
assertEquals(it.toString(), it.chunked(Int.MAX_VALUE).single())
|
||||
}
|
||||
|
||||
assertTrue(arg1("").chunked(3).isEmpty())
|
||||
|
||||
Reference in New Issue
Block a user