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)
|
checkWindowSizeStep(size, step)
|
||||||
if (this is RandomAccess && this is List) {
|
if (this is RandomAccess && this is List) {
|
||||||
val thisSize = this.size
|
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
|
var index = 0
|
||||||
while (index < thisSize) {
|
while (index < thisSize) {
|
||||||
val windowSize = size.coerceAtMost(thisSize - index)
|
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> {
|
public fun <R> CharSequence.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (CharSequence) -> R): List<R> {
|
||||||
checkWindowSizeStep(size, step)
|
checkWindowSizeStep(size, step)
|
||||||
val thisSize = this.length
|
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
|
var index = 0
|
||||||
while (index < thisSize) {
|
while (index < thisSize) {
|
||||||
val end = index + size
|
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>> {
|
internal fun <T> windowedIterator(iterator: Iterator<T>, size: Int, step: Int, partialWindows: Boolean, reuseBuffer: Boolean): Iterator<List<T>> {
|
||||||
if (!iterator.hasNext()) return EmptyIterator
|
if (!iterator.hasNext()) return EmptyIterator
|
||||||
return iterator<List<T>> {
|
return iterator<List<T>> {
|
||||||
|
val bufferInitialCapacity = size.coerceAtMost(1024)
|
||||||
val gap = step - size
|
val gap = step - size
|
||||||
if (gap >= 0) {
|
if (gap >= 0) {
|
||||||
var buffer = ArrayList<T>(size)
|
var buffer = ArrayList<T>(bufferInitialCapacity)
|
||||||
var skip = 0
|
var skip = 0
|
||||||
for (e in iterator) {
|
for (e in iterator) {
|
||||||
if (skip > 0) { skip -= 1; continue }
|
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)
|
if (partialWindows || buffer.size == size) yield(buffer)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val buffer = RingBuffer<T>(size)
|
var buffer = RingBuffer<T>(bufferInitialCapacity)
|
||||||
for (e in iterator) {
|
for (e in iterator) {
|
||||||
buffer.add(e)
|
buffer.add(e)
|
||||||
if (buffer.isFull()) {
|
if (buffer.isFull()) {
|
||||||
|
if (buffer.size < size) { buffer = buffer.expanded(maxCapacity = size); continue }
|
||||||
|
|
||||||
yield(if (reuseBuffer) buffer else ArrayList(buffer))
|
yield(if (reuseBuffer) buffer else ArrayList(buffer))
|
||||||
buffer.removeFirst(step)
|
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.
|
* 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 {
|
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
|
private var startIndex: Int = 0
|
||||||
|
|
||||||
override var size: Int = 0
|
override var size: Int = filledSize
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override fun get(index: Int): T {
|
override fun get(index: Int): T {
|
||||||
@@ -149,6 +155,16 @@ private class RingBuffer<T>(val capacity: Int) : AbstractList<T>(), RandomAccess
|
|||||||
return toArray(arrayOfNulls(size))
|
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
|
* 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 ->
|
data.toList().let { expectedSingleChunk ->
|
||||||
assertEquals(expectedSingleChunk, data.chunked(size).single())
|
assertEquals(expectedSingleChunk, data.chunked(size).single())
|
||||||
assertEquals(expectedSingleChunk, data.chunked(size + 3).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())
|
assertTrue(empty.chunked(3).isEmpty())
|
||||||
|
|||||||
@@ -230,6 +230,11 @@ public class SequenceTest {
|
|||||||
seq.toList().let { expectedSingleChunk ->
|
seq.toList().let { expectedSingleChunk ->
|
||||||
assertEquals(expectedSingleChunk, seq.chunked(size).single())
|
assertEquals(expectedSingleChunk, seq.chunked(size).single())
|
||||||
assertEquals(expectedSingleChunk, seq.chunked(size + 3).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())
|
assertTrue(emptySequence<String>().chunked(3).none())
|
||||||
@@ -251,6 +256,16 @@ public class SequenceTest {
|
|||||||
assertEquals((startElement until startElement + 5).toList(), window)
|
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 size = 7
|
||||||
val seq = infiniteSeq.take(7)
|
val seq = infiniteSeq.take(7)
|
||||||
|
|
||||||
|
|||||||
@@ -1068,6 +1068,11 @@ class StringTest {
|
|||||||
data.toString().let { expectedSingleChunk ->
|
data.toString().let { expectedSingleChunk ->
|
||||||
assertEquals(expectedSingleChunk, data.chunked(size).single())
|
assertEquals(expectedSingleChunk, data.chunked(size).single())
|
||||||
assertEquals(expectedSingleChunk, data.chunked(size + 3).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())
|
assertTrue(arg1("").chunked(3).isEmpty())
|
||||||
|
|||||||
Reference in New Issue
Block a user