[fleet] implement OpsIterator

An iterator to iterate over operation's ops back-and-forth.

GitOrigin-RevId: 5ea0253dc3598f2e3396374575f921acf382c0c5
This commit is contained in:
Fedor Bocharov
2025-03-31 23:01:15 +02:00
committed by intellij-monorepo-bot
parent 75c4740409
commit 8832f4362f
2 changed files with 107 additions and 8 deletions

View File

@@ -38,6 +38,89 @@ sealed class Op {
}
}
class OpsIterator(
private val owner: Any?,
private var cursor: Rope.Cursor<Array<Op>>?,
private val size: Int,
private var index: Int = 0,
private var elementIndex: Int = 0
): Iterator<Op> {
sealed interface Pos {
object Begin: Pos
object End: Pos
data class LenBefore(val offset: Int): Pos
data class LenAfter(val offset: Int): Pos
}
override fun next(): Op {
require(hasNext())
if (elementIndex >= cursor!!.element.size) {
cursor = cursor!!.next(owner)
elementIndex = 0
}
val op = cursor!!.element[elementIndex]
++elementIndex
++index
return op
}
override fun hasNext(): Boolean {
return cursor != null && index < size
}
fun prev(): Op {
require(hasPrev())
if (elementIndex == 0) {
cursor = cursor!!.prev(owner)
elementIndex = cursor!!.element.size
}
--elementIndex
--index
return cursor!!.element[elementIndex]
}
fun hasPrev(): Boolean {
return index > 0
}
fun scanTo(pos: Pos) {
when (pos) {
Pos.Begin -> {
cursor = cursor!!.scan(owner, OperationMonoid.Count, 0)
index = 0
elementIndex = 0
}
Pos.End -> {
cursor = cursor!!.scan(owner, OperationMonoid.Count, size)
index = size
elementIndex = MAX_LEAF_SIZE
}
is Pos.LenBefore -> {
val c = cursor!!
cursor = c.scan(owner, OperationMonoid.LenBefore, pos.offset)
elementIndex = 0
index = c.size(OperationMonoid.Count)
while (elementIndex < c.element.size && c.element[elementIndex].lenBefore <= pos.offset) {
++elementIndex
++index
}
}
is Pos.LenAfter -> {
val c = cursor!!
cursor = c.scan(owner, OperationMonoid.LenAfter, pos.offset)
elementIndex = 0
index = c.size(OperationMonoid.Count)
while (elementIndex < c.element.size && c.element[elementIndex].lenAfter <= pos.offset) {
++elementIndex
++index
}
}
}
}
}
/**
* After introducing Replace operation there still exists two asymmetries:
* 1. Insert operations at the same place do not commute, so that {transform} function is not commutative
@@ -52,13 +135,20 @@ sealed class Op {
class Operation(internal val rope: OpsRope) {
constructor(ops: List<Op>): this(OperationMonoid.ropeOf(listOf(ops.toTypedArray())))
val ops: Sequence<Op> get() = sequence {
val ops: Sequence<Op> get() = sequence { yieldAll(begin()) }
fun begin(): OpsIterator {
val owner = Any()
var cursor: Rope.Cursor<Array<Op>>? = rope.cursor(owner)
while (cursor != null) {
yieldAll(cursor.element.iterator())
cursor = cursor.next(owner)
}
val iterator = OpsIterator(owner, rope.cursor(owner), size)
iterator.scanTo(OpsIterator.Pos.Begin)
return iterator
}
fun end(): OpsIterator {
val owner = Any()
val iterator = OpsIterator(owner, rope.cursor(owner), size)
iterator.scanTo(OpsIterator.Pos.End)
return iterator
}
val size: Int

View File

@@ -94,16 +94,25 @@ class Rope<T> internal constructor(
/**
* Returns a pointer to the next chunk.
* If [owner] is the same as before, the previous Cursur is recycled and it's internals are reused for creating a new one.
* If [owner] is the same as before, the previous Cursor is recycled and it's internals are reused for creating a new one.
* */
fun next(owner: Any?): Cursor<T>? =
zipper.nextLeaf(owner, monoid)?.let { leaf ->
cursor(leaf)
}
/**
* Returns a pointer to the prev chunk.
* If [owner] is the same as before, the previous Cursor is recycled and it's internals are reused for creating a new one.
* */
fun prev(owner: Any?): Cursor<T>? =
zipper.prevLeaf(owner, monoid)?.let { leaf ->
cursor(leaf)
}
/**
* Builds a new rope, accomodating all changes made to the cursor.
* If [owner] is the same as before, the previous Cursur is recycled and it's internals are reused.
* If [owner] is the same as before, the previous Cursor is recycled and it's internals are reused.
* */
fun rope(owner: Any?): Rope<T> =
zipper.rope(owner, monoid)