Fix Iterable, Sequence and String index overflow in windowed
This commit is contained in:
@@ -2160,7 +2160,7 @@ public fun <T> Iterable<T>.windowed(size: Int, step: Int = 1, partialWindows: Bo
|
|||||||
val resultCapacity = thisSize / step + if (thisSize % step == 0) 0 else 1
|
val resultCapacity = thisSize / step + if (thisSize % step == 0) 0 else 1
|
||||||
val result = ArrayList<List<T>>(resultCapacity)
|
val result = ArrayList<List<T>>(resultCapacity)
|
||||||
var index = 0
|
var index = 0
|
||||||
while (index < thisSize) {
|
while (index in 0 until thisSize) {
|
||||||
val windowSize = size.coerceAtMost(thisSize - index)
|
val windowSize = size.coerceAtMost(thisSize - index)
|
||||||
if (windowSize < size && !partialWindows) break
|
if (windowSize < size && !partialWindows) break
|
||||||
result.add(List(windowSize) { this[it + index] })
|
result.add(List(windowSize) { this[it + index] })
|
||||||
@@ -2197,12 +2197,14 @@ public fun <T, R> Iterable<T>.windowed(size: Int, step: Int = 1, partialWindows:
|
|||||||
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<R>((thisSize + step - 1) / step)
|
val resultCapacity = thisSize / step + if (thisSize % step == 0) 0 else 1
|
||||||
|
val result = ArrayList<R>(resultCapacity)
|
||||||
val window = MovingSubList(this)
|
val window = MovingSubList(this)
|
||||||
var index = 0
|
var index = 0
|
||||||
while (index < thisSize) {
|
while (index in 0 until thisSize) {
|
||||||
window.move(index, (index + size).coerceAtMost(thisSize))
|
val windowSize = size.coerceAtMost(thisSize - index)
|
||||||
if (!partialWindows && window.size < size) break
|
if (!partialWindows && windowSize < size) break
|
||||||
|
window.move(index, index + windowSize)
|
||||||
result.add(transform(window))
|
result.add(transform(window))
|
||||||
index += step
|
index += step
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1377,9 +1377,9 @@ public fun <R> CharSequence.windowed(size: Int, step: Int = 1, partialWindows: B
|
|||||||
val resultCapacity = thisSize / step + if (thisSize % step == 0) 0 else 1
|
val resultCapacity = thisSize / step + if (thisSize % step == 0) 0 else 1
|
||||||
val result = ArrayList<R>(resultCapacity)
|
val result = ArrayList<R>(resultCapacity)
|
||||||
var index = 0
|
var index = 0
|
||||||
while (index < thisSize) {
|
while (index in 0 until thisSize) {
|
||||||
val end = index + size
|
val end = index + size
|
||||||
val coercedEnd = if (end > thisSize) { if (partialWindows) thisSize else break } else end
|
val coercedEnd = if (end < 0 || end > thisSize) { if (partialWindows) thisSize else break } else end
|
||||||
result.add(transform(subSequence(index, coercedEnd)))
|
result.add(transform(subSequence(index, coercedEnd)))
|
||||||
index += step
|
index += step
|
||||||
}
|
}
|
||||||
@@ -1427,7 +1427,11 @@ public fun CharSequence.windowedSequence(size: Int, step: Int = 1, partialWindow
|
|||||||
public fun <R> CharSequence.windowedSequence(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (CharSequence) -> R): Sequence<R> {
|
public fun <R> CharSequence.windowedSequence(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (CharSequence) -> R): Sequence<R> {
|
||||||
checkWindowSizeStep(size, step)
|
checkWindowSizeStep(size, step)
|
||||||
val windows = (if (partialWindows) indices else 0 until length - size + 1) step step
|
val windows = (if (partialWindows) indices else 0 until length - size + 1) step step
|
||||||
return windows.asSequence().map { index -> transform(subSequence(index, (index + size).coerceAtMost(length))) }
|
return windows.asSequence().map { index ->
|
||||||
|
val end = index + size
|
||||||
|
val coercedEnd = if (end < 0 || end > length) length else end
|
||||||
|
transform(subSequence(index, coercedEnd))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -199,6 +199,24 @@ abstract class OrderedIterableTests<T : Iterable<String>>(createFrom: (Array<out
|
|||||||
assertFailsWith<IllegalArgumentException>("size $illegalValue") { data.windowed(illegalValue, 1) }
|
assertFailsWith<IllegalArgumentException>("size $illegalValue") { data.windowed(illegalValue, 1) }
|
||||||
assertFailsWith<IllegalArgumentException>("step $illegalValue") { data.windowed(1, illegalValue) }
|
assertFailsWith<IllegalArgumentException>("step $illegalValue") { data.windowed(1, illegalValue) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// index overflow tests
|
||||||
|
for (partialWindows in listOf(true, false)) {
|
||||||
|
|
||||||
|
val windowed1 = data.windowed(5, Int.MAX_VALUE, partialWindows)
|
||||||
|
assertEquals(data.take(5), windowed1.single())
|
||||||
|
val windowed2 = data.windowed(Int.MAX_VALUE, 5, partialWindows)
|
||||||
|
assertEquals(if (partialWindows) listOf(data.toList(), listOf("5", "6")) else listOf(), windowed2)
|
||||||
|
val windowed3 = data.windowed(Int.MAX_VALUE, Int.MAX_VALUE, partialWindows)
|
||||||
|
assertEquals(if (partialWindows) listOf(data.toList()) else listOf(), windowed3)
|
||||||
|
|
||||||
|
val windowedTransform1 = data.windowed(5, Int.MAX_VALUE, partialWindows) { it.joinToString("") }
|
||||||
|
assertEquals("01234", windowedTransform1.single())
|
||||||
|
val windowedTransform2 = data.windowed(Int.MAX_VALUE, 5, partialWindows) { it.joinToString("") }
|
||||||
|
assertEquals(if (partialWindows) listOf("0123456", "56") else listOf(), windowedTransform2)
|
||||||
|
val windowedTransform3 = data.windowed(Int.MAX_VALUE, Int.MAX_VALUE, partialWindows) { it.joinToString("") }
|
||||||
|
assertEquals(if (partialWindows) listOf("0123456") else listOf(), windowedTransform3)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -311,6 +311,24 @@ public class SequenceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ensureIsIntermediate(source = sequenceOf(1, 2, 3)) { it.windowed(2, 1) }
|
ensureIsIntermediate(source = sequenceOf(1, 2, 3)) { it.windowed(2, 1) }
|
||||||
|
|
||||||
|
// index overflow tests
|
||||||
|
for (partialWindows in listOf(true, false)) {
|
||||||
|
|
||||||
|
val windowed1 = seq.windowed(5, Int.MAX_VALUE, partialWindows)
|
||||||
|
assertEquals(seq.take(5).toList(), windowed1.single())
|
||||||
|
val windowed2 = seq.windowed(Int.MAX_VALUE, 5, partialWindows)
|
||||||
|
assertEquals(if (partialWindows) listOf(seq.toList(), listOf(5, 6)) else listOf(), windowed2.toList())
|
||||||
|
val windowed3 = seq.windowed(Int.MAX_VALUE, Int.MAX_VALUE, partialWindows)
|
||||||
|
assertEquals(if (partialWindows) listOf(seq.toList()) else listOf(), windowed3.toList())
|
||||||
|
|
||||||
|
val windowedTransform1 = seq.windowed(5, Int.MAX_VALUE, partialWindows) { it.joinToString("") }
|
||||||
|
assertEquals("01234", windowedTransform1.single())
|
||||||
|
val windowedTransform2 = seq.windowed(Int.MAX_VALUE, 5, partialWindows) { it.joinToString("") }
|
||||||
|
assertEquals(if (partialWindows) listOf("0123456", "56") else listOf(), windowedTransform2.toList())
|
||||||
|
val windowedTransform3 = seq.windowed(Int.MAX_VALUE, Int.MAX_VALUE, partialWindows) { it.joinToString("") }
|
||||||
|
assertEquals(if (partialWindows) listOf("0123456") else listOf(), windowedTransform3.toList())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun zip() {
|
@Test fun zip() {
|
||||||
|
|||||||
@@ -1130,6 +1130,38 @@ class StringTest {
|
|||||||
data.windowedSequence(window, step, partialWindows = true).iterator()) { iteratorBehavior() }
|
data.windowedSequence(window, step, partialWindows = true).iterator()) { iteratorBehavior() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// index overflow tests
|
||||||
|
for (partialWindows in listOf(true, false)) {
|
||||||
|
|
||||||
|
val windowed1 = data.windowed(5, Int.MAX_VALUE, partialWindows)
|
||||||
|
assertEquals("abcde", windowed1.single())
|
||||||
|
val windowed2 = data.windowed(Int.MAX_VALUE, 5, partialWindows)
|
||||||
|
assertEquals(if (partialWindows) listOf(data.toString(), data.substring(5, 7)) else listOf(), windowed2)
|
||||||
|
val windowed3 = data.windowed(Int.MAX_VALUE, Int.MAX_VALUE, partialWindows)
|
||||||
|
assertEquals(if (partialWindows) listOf(data.toString()) else listOf(), windowed3)
|
||||||
|
|
||||||
|
val windowedTransform1 = data.windowed(5, Int.MAX_VALUE, partialWindows) { it }
|
||||||
|
assertEquals("abcde", windowedTransform1.single())
|
||||||
|
val windowedTransform2 = data.windowed(Int.MAX_VALUE, 5, partialWindows) { it }
|
||||||
|
assertEquals(if (partialWindows) listOf(data.toString(), data.substring(5, 7)) else listOf(), windowedTransform2)
|
||||||
|
val windowedTransform3 = data.windowed(Int.MAX_VALUE, Int.MAX_VALUE, partialWindows) { it }
|
||||||
|
assertEquals(if (partialWindows) listOf(data.toString()) else listOf(), windowedTransform3)
|
||||||
|
|
||||||
|
val windowedSequence1 = data.windowedSequence(5, Int.MAX_VALUE, partialWindows)
|
||||||
|
assertEquals("abcde", windowedSequence1.single())
|
||||||
|
val windowedSequence2 = data.windowedSequence(Int.MAX_VALUE, 5, partialWindows)
|
||||||
|
assertEquals(if (partialWindows) listOf(data.toString(), data.substring(5, 7)) else listOf(), windowedSequence2.toList())
|
||||||
|
val windowedSequence3 = data.windowedSequence(Int.MAX_VALUE, Int.MAX_VALUE, partialWindows)
|
||||||
|
assertEquals(if (partialWindows) listOf(data.toString()) else listOf(), windowedSequence3.toList())
|
||||||
|
|
||||||
|
val windowedSequenceTransform1 = data.windowedSequence(5, Int.MAX_VALUE, partialWindows) { it }
|
||||||
|
assertEquals("abcde", windowedSequenceTransform1.single())
|
||||||
|
val windowedSequenceTransform2 = data.windowedSequence(Int.MAX_VALUE, 5, partialWindows) { it }
|
||||||
|
assertEquals(if (partialWindows) listOf(data.toString(), data.substring(5, 7)) else listOf(), windowedSequenceTransform2.toList())
|
||||||
|
val windowedSequenceTransform3 = data.windowedSequence(Int.MAX_VALUE, Int.MAX_VALUE, partialWindows) { it }
|
||||||
|
assertEquals(if (partialWindows) listOf(data.toString()) else listOf(), windowedSequenceTransform3.toList())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun map() = withOneCharSequenceArg { arg1 ->
|
@Test fun map() = withOneCharSequenceArg { arg1 ->
|
||||||
|
|||||||
@@ -655,12 +655,14 @@ object Generators : TemplateGroupBase() {
|
|||||||
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<R>((thisSize + step - 1) / step)
|
val resultCapacity = thisSize / step + if (thisSize % step == 0) 0 else 1
|
||||||
|
val result = ArrayList<R>(resultCapacity)
|
||||||
val window = MovingSubList(this)
|
val window = MovingSubList(this)
|
||||||
var index = 0
|
var index = 0
|
||||||
while (index < thisSize) {
|
while (index in 0 until thisSize) {
|
||||||
window.move(index, (index + size).coerceAtMost(thisSize))
|
val windowSize = size.coerceAtMost(thisSize - index)
|
||||||
if (!partialWindows && window.size < size) break
|
if (!partialWindows && windowSize < size) break
|
||||||
|
window.move(index, index + windowSize)
|
||||||
result.add(transform(window))
|
result.add(transform(window))
|
||||||
index += step
|
index += step
|
||||||
}
|
}
|
||||||
@@ -681,11 +683,12 @@ object Generators : TemplateGroupBase() {
|
|||||||
"""
|
"""
|
||||||
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 in 0 until thisSize) {
|
||||||
val end = index + size
|
val end = index + size
|
||||||
val coercedEnd = if (end > thisSize) { if (partialWindows) thisSize else break } else end
|
val coercedEnd = if (end < 0 || end > thisSize) { if (partialWindows) thisSize else break } else end
|
||||||
result.add(transform(subSequence(index, coercedEnd)))
|
result.add(transform(subSequence(index, coercedEnd)))
|
||||||
index += step
|
index += step
|
||||||
}
|
}
|
||||||
@@ -731,9 +734,10 @@ object Generators : TemplateGroupBase() {
|
|||||||
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 in 0 until thisSize) {
|
||||||
val windowSize = size.coerceAtMost(thisSize - index)
|
val windowSize = size.coerceAtMost(thisSize - index)
|
||||||
if (windowSize < size && !partialWindows) break
|
if (windowSize < size && !partialWindows) break
|
||||||
result.add(List(windowSize) { this[it + index] })
|
result.add(List(windowSize) { this[it + index] })
|
||||||
@@ -785,7 +789,11 @@ object Generators : TemplateGroupBase() {
|
|||||||
"""
|
"""
|
||||||
checkWindowSizeStep(size, step)
|
checkWindowSizeStep(size, step)
|
||||||
val windows = (if (partialWindows) indices else 0 until length - size + 1) step step
|
val windows = (if (partialWindows) indices else 0 until length - size + 1) step step
|
||||||
return windows.asSequence().map { index -> transform(subSequence(index, (index + size).coerceAtMost(length))) }
|
return windows.asSequence().map { index ->
|
||||||
|
val end = index + size
|
||||||
|
val coercedEnd = if (end < 0 || end > length) length else end
|
||||||
|
transform(subSequence(index, coercedEnd))
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user