Minor refactoring in RingBuffer

This commit is contained in:
Ilya Gorbunov
2017-05-06 06:50:01 +03:00
parent 3f23742298
commit b94fd36d75
@@ -60,9 +60,7 @@ internal fun <T> windowedIterator(iterator: Iterator<T>, size: Int, step: Int, d
buffer.removeFirst(step) buffer.removeFirst(step)
} }
} }
if (dropTrailing) { if (!dropTrailing) {
if (buffer.size == size) yield(buffer)
} else {
while (buffer.size > step) { while (buffer.size > step) {
yield(if (reuseBuffer) buffer else ArrayList(buffer)) yield(if (reuseBuffer) buffer else ArrayList(buffer))
buffer.removeFirst(step) buffer.removeFirst(step)
@@ -96,8 +94,7 @@ internal class MovingSubList<out E>(private val list: List<E>) : AbstractList<E>
/** /**
* 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.
* If it is going to be public API perhaps this behaviour could be customizable
*/ */
private class RingBuffer<T>(val capacity: Int): AbstractList<T>(), RandomAccess { private class RingBuffer<T>(val capacity: Int): AbstractList<T>(), RandomAccess {
init { init {
@@ -105,76 +102,76 @@ private class RingBuffer<T>(val capacity: Int): AbstractList<T>(), RandomAccess
} }
private val buffer = arrayOfNulls<Any?>(capacity) private val buffer = arrayOfNulls<Any?>(capacity)
private var writePosition = 0 private var startIndex: Int = 0
override var size: Int = 0 override var size: Int = 0
private set private set
override fun get(index: Int): T { override fun get(index: Int): T {
checkElementIndex(index, size) checkElementIndex(index, size)
return getAtUnsafe(writePosition.backward(size - index)) @Suppress("UNCHECKED_CAST")
return buffer[startIndex.forward(index)] as T
} }
fun isFull() = size == capacity fun isFull() = size == capacity
override fun iterator(): Iterator<T> = object : AbstractIterator<T>() { override fun iterator(): Iterator<T> = object : AbstractIterator<T>() {
private var count = size private var count = size
private var index = writePosition.backward(size) private var index = startIndex
override fun computeNext() { override fun computeNext() {
if (count == 0) { if (count == 0) {
done() done()
} else { } else {
setNext(getAtUnsafe(index)) @Suppress("UNCHECKED_CAST")
setNext(buffer[index] as T)
index = index.forward(1) index = index.forward(1)
count-- count--
} }
} }
} }
override fun toArray(): Array<Any?> { @Suppress("UNCHECKED_CAST")
override fun <T> toArray(array: Array<T>): Array<T> {
val result: Array<T?> =
if (array.size < this.size) array.copyOf(this.size) else array as Array<T?>
val size = this.size val size = this.size
val result = arrayOfNulls<Any?>(size)
var widx = 0 var widx = 0
var idx = writePosition.backward(size) var idx = startIndex
while (widx < size && idx < capacity) { while (widx < size && idx < capacity) {
result[widx] = buffer[idx] result[widx] = buffer[idx] as T
widx++ widx++
idx++ idx++
} }
idx = 0 idx = 0
while (widx < size) { while (widx < size) {
result[widx] = buffer[idx] result[widx] = buffer[idx] as T
widx++ widx++
idx++ idx++
} }
if (result.size > this.size) result[this.size] = null
return result return result as Array<T>
}
override fun toArray(): Array<Any?> {
return toArray(arrayOfNulls(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
*/ */
fun add(element: T) { fun add(element: T) {
if (!offer(element)) {
throw IllegalStateException("Ring buffer is full.")
}
}
/**
* Offers [element] to the buffer and return `true` if it was added or `false` when there is no free space available in the buffer
*/
fun offer(element: T): Boolean {
if (isFull()) { if (isFull()) {
return false throw IllegalStateException("ring buffer is full")
} }
buffer[writePosition] = element buffer[startIndex.forward(size)] = element
writePosition = writePosition.forward(1)
size++ size++
return true
} }
/** /**
@@ -185,30 +182,25 @@ private class RingBuffer<T>(val capacity: Int): AbstractList<T>(), RandomAccess
require(n <= size) { "n shouldn't be greater than the buffer size: n = $n, size = $size" } require(n <= size) { "n shouldn't be greater than the buffer size: n = $n, size = $size" }
if (n > 0) { if (n > 0) {
val start = writePosition.backward(size) val start = startIndex
val end = start.forward(n - 1) val end = start.forward(n)
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)
} else { } else {
buffer.fill(null, start, end + 1) buffer.fill(null, start, end)
} }
startIndex = end
size -= n size -= n
} }
} }
@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
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): Int = (this + n) % capacity private inline fun Int.forward(n: Int): Int = (this + n) % capacity
@Suppress("NOTHING_TO_INLINE")
private inline fun Int.backward(n: Int): Int = ((this - n) % capacity + capacity) % capacity
// 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 {
for (idx in fromIndex .. toIndex-1) { for (idx in fromIndex .. toIndex-1) {