-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add BigTextTransformerImpl with transformInsert, length, substring an…
…d buildString functions tested
- Loading branch information
1 parent
63e23c1
commit 0255da3
Showing
8 changed files
with
589 additions
and
104 deletions.
There are no files selected for viewing
191 changes: 98 additions & 93 deletions
191
...mMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/bigtext/BigTextImpl.kt
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
...om/sunnychung/application/multiplatform/hellohttp/ux/bigtext/BigTextTransformNodeValue.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package com.sunnychung.application.multiplatform.hellohttp.ux.bigtext | ||
|
||
import com.williamfiset.algorithms.datastructures.balancedtree.RedBlackTree | ||
|
||
/** | ||
* | ||
* o r i g i n a l T R A N S F O R M # # # t e x t | ||
* | | | | | | | | | ||
* leftStringLength +*************+ | | +***+*+*****+ | ||
* leftTransformedLength | | +***************+ |---| | | | ||
* leftRenderLength +*************+*+***************+ | | +*****+ | ||
* leftOverallLength +*************+*+***************+*+***+*+*****+ | ||
* | ||
* # is a deleted character. | ||
*/ | ||
class BigTextTransformNodeValue : BigTextNodeValue() { | ||
var transformedBufferStart: Int = -1 | ||
var transformedBufferEndExclusive: Int = -1 | ||
|
||
override val renderBufferStart: Int | ||
get() = if (bufferOwnership == BufferOwnership.Delegated) { | ||
bufferOffsetStart | ||
} else { | ||
transformedBufferStart | ||
} | ||
|
||
override val renderBufferEndExclusive: Int | ||
get() = if (bufferOwnership == BufferOwnership.Delegated) { | ||
bufferOffsetEndExclusive | ||
} else { | ||
transformedBufferEndExclusive | ||
} | ||
|
||
var leftTransformedLength: Int = 0 | ||
val currentTransformedLength: Int | ||
get() = transformedBufferEndExclusive - transformedBufferStart - bufferLength | ||
|
||
override var leftRenderLength: Int = 0 | ||
override val currentRenderLength: Int | ||
get() = if (bufferOwnership == BufferOwnership.Delegated) { | ||
bufferLength | ||
} else { | ||
transformedBufferEndExclusive - transformedBufferStart | ||
} | ||
|
||
override var leftOverallLength: Int = 0 | ||
override val currentOverallLength: Int | ||
get() = bufferLength + currentRenderLength | ||
|
||
override fun debugLabel(node: RedBlackTree<BigTextNodeValue>.Node): String = buildString { | ||
node as RedBlackTree<BigTextTransformNodeValue>.Node | ||
|
||
append("$leftStringLength ${bufferOwnership.name.first()} [$bufferIndex: $bufferOffsetStart ..< $bufferOffsetEndExclusive] L ${node.renderLength()}") | ||
append(" Tr [$transformedBufferStart ..< $transformedBufferEndExclusive]") | ||
append(" Ren left=$leftRenderLength [$renderBufferStart ..< $renderBufferEndExclusive]") | ||
append(" row $leftNumOfRowBreaks/$rowBreakOffsets lw $lastRowWidth") | ||
} | ||
|
||
} |
183 changes: 183 additions & 0 deletions
183
...n/com/sunnychung/application/multiplatform/hellohttp/ux/bigtext/BigTextTransformerImpl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
package com.sunnychung.application.multiplatform.hellohttp.ux.bigtext | ||
|
||
import com.sunnychung.application.multiplatform.hellohttp.extension.length | ||
import com.williamfiset.algorithms.datastructures.balancedtree.RedBlackTree | ||
|
||
class BigTextTransformerImpl(private val delegate: BigTextImpl) : BigTextImpl(chunkSize = delegate.chunkSize) { | ||
|
||
override val tree: LengthTree<BigTextNodeValue> = LengthTree<BigTextTransformNodeValue>( | ||
object : RedBlackTreeComputations<BigTextTransformNodeValue> { | ||
override fun recomputeFromLeaf(it: RedBlackTree<BigTextTransformNodeValue>.Node) = recomputeAggregatedValues(it as RedBlackTree<BigTextNodeValue>.Node) | ||
override fun computeWhenLeftRotate(x: BigTextTransformNodeValue, y: BigTextTransformNodeValue) {} | ||
override fun computeWhenRightRotate(x: BigTextTransformNodeValue, y: BigTextTransformNodeValue) {} | ||
} | ||
) | ||
|
||
private fun BigTextNodeValue.toBigTextTransformNodeValue() : BigTextTransformNodeValue { | ||
return BigTextTransformNodeValue().also { | ||
it.leftNumOfLineBreaks = leftNumOfLineBreaks | ||
it.leftNumOfRowBreaks = leftNumOfRowBreaks | ||
it.leftStringLength = leftStringLength | ||
it.rowBreakOffsets = rowBreakOffsets.toList() | ||
it.lastRowWidth = lastRowWidth | ||
it.isEndWithForceRowBreak = isEndWithForceRowBreak | ||
it.bufferOffsetStart = bufferOffsetStart | ||
it.bufferOffsetEndExclusive = bufferOffsetEndExclusive | ||
it.bufferNumLineBreaksInRange = bufferNumLineBreaksInRange | ||
it.buffer = buffer // copy by ref | ||
it.bufferOwnership = BufferOwnership.Delegated | ||
} | ||
} | ||
|
||
fun RedBlackTree<BigTextNodeValue>.Node.toBigTextTransformNode(parentNode: RedBlackTree<BigTextTransformNodeValue>.Node) : RedBlackTree<BigTextTransformNodeValue>.Node { | ||
if (this === delegate.tree.NIL) { | ||
return (tree as LengthTree<BigTextTransformNodeValue>).NIL | ||
} | ||
|
||
return (tree as LengthTree<BigTextTransformNodeValue>).Node( | ||
value.toBigTextTransformNodeValue(), | ||
color, | ||
parentNode, | ||
tree.NIL, | ||
tree.NIL, | ||
).also { | ||
it.left = left.toBigTextTransformNode(it) | ||
it.right = right.toBigTextTransformNode(it) | ||
} | ||
} | ||
|
||
init { | ||
(tree as LengthTree<BigTextTransformNodeValue>).setRoot(delegate.tree.getRoot().toBigTextTransformNode(tree.NIL)) | ||
layouter = delegate.layouter | ||
contentWidth = delegate.contentWidth | ||
} | ||
|
||
override val length: Int | ||
get() = (tree as LengthTree<BigTextTransformNodeValue>).getRoot().renderLength() | ||
|
||
val originalLength: Int | ||
get() = tree.getRoot().length() | ||
|
||
override fun createNodeValue(): BigTextNodeValue { | ||
return BigTextTransformNodeValue() | ||
} | ||
|
||
fun insertOriginal(pos: Int, nodeValue: BigTextNodeValue) { // FIXME call me | ||
require(pos in 0 .. originalLength) { "Out of bound. pos = $pos, originalLength = $originalLength" } | ||
|
||
insertChunkAtPosition( | ||
position = pos, | ||
chunkedStringLength = nodeValue.bufferLength, | ||
ownership = BufferOwnership.Delegated, | ||
buffer = nodeValue.buffer, | ||
range = nodeValue.bufferOffsetStart until nodeValue.bufferOffsetEndExclusive | ||
) { | ||
bufferIndex = -1 | ||
bufferOffsetStart = nodeValue.bufferOffsetStart | ||
bufferOffsetEndExclusive = nodeValue.bufferOffsetEndExclusive | ||
this.buffer = nodeValue.buffer | ||
this.bufferOwnership = BufferOwnership.Delegated | ||
|
||
leftStringLength = 0 | ||
} | ||
} | ||
|
||
private fun transformInsertChunkAtPosition(position: Int, chunkedString: String) { | ||
log.d { "transformInsertChunkAtPosition($position, $chunkedString)" } | ||
require(chunkedString.length <= chunkSize) | ||
var buffer = if (buffers.isNotEmpty()) { | ||
buffers.last().takeIf { it.length + chunkedString.length <= chunkSize } | ||
} else null | ||
if (buffer == null) { | ||
buffer = TextBuffer(chunkSize) | ||
buffers += buffer | ||
} | ||
require(buffer.length + chunkedString.length <= chunkSize) | ||
val range = buffer.append(chunkedString) | ||
insertChunkAtPosition(position, chunkedString.length, BufferOwnership.Owned, buffer, range) { | ||
this as BigTextTransformNodeValue | ||
bufferIndex = -1 | ||
bufferOffsetStart = -1 | ||
bufferOffsetEndExclusive = -1 | ||
transformedBufferStart = range.start | ||
transformedBufferEndExclusive = range.endInclusive + 1 | ||
this.buffer = buffer | ||
this.bufferOwnership = BufferOwnership.Owned | ||
|
||
leftStringLength = 0 | ||
} | ||
} | ||
|
||
fun transformInsert(pos: Int, text: String): Int { | ||
require(pos in 0 .. originalLength) { "Out of bound. pos = $pos, originalLength = $originalLength" } | ||
|
||
/** | ||
* As insert position is searched by leftmost of original string position, | ||
* the insert is done by inserting to the same point in reverse order, | ||
* which is different from BigTextImpl#insertAt. | ||
*/ | ||
|
||
var start = text.length | ||
var last = buffers.lastOrNull()?.length | ||
while (start > 0) { | ||
if (last == null || last >= chunkSize) { | ||
// buffers += TextBuffer() | ||
last = 0 | ||
} | ||
val available = chunkSize - last | ||
val append = minOf(available, start) | ||
start -= append | ||
transformInsertChunkAtPosition(pos, text.substring(start until start + append)) | ||
last = buffers.last().length | ||
} | ||
layout(maxOf(0, pos - 1), minOf(length, pos + text.length + 1)) | ||
return text.length | ||
} | ||
|
||
fun deleteOriginal(originalRange: IntRange) { // FIXME call me | ||
require((originalRange.endInclusive + 1) in 0 .. originalLength) { "Out of bound. endExclusive = ${originalRange.endInclusive + 1}, originalLength = $originalLength" } | ||
super.delete(originalRange) | ||
} | ||
|
||
fun transformDelete(originalRange: IntRange): Int { | ||
require(originalRange.start <= originalRange.endInclusive + 1) { "start should be <= endExclusive" } | ||
require(0 <= originalRange.start) { "Invalid start" } | ||
require(originalRange.endInclusive + 1 <= originalLength) { "endExclusive is out of bound" } | ||
|
||
if (originalRange.start == originalRange.endInclusive + 1) { | ||
return 0 | ||
} | ||
|
||
val startNode = tree.findNodeByCharIndex(originalRange.start)!! | ||
val buffer = startNode.value.buffer // the buffer is not used. just to prevent NPE | ||
super.delete(originalRange) | ||
insertChunkAtPosition(originalRange.start, originalRange.length, BufferOwnership.Owned, buffer, -2 .. -2) { | ||
this as BigTextTransformNodeValue | ||
bufferIndex = -1 | ||
bufferOffsetStart = 0 | ||
bufferOffsetEndExclusive = originalRange.length | ||
transformedBufferStart = -2 | ||
transformedBufferEndExclusive = -2 | ||
this.buffer = buffer | ||
this.bufferOwnership = BufferOwnership.Owned | ||
|
||
leftStringLength = 0 | ||
} | ||
return - originalRange.length | ||
} | ||
|
||
override fun computeCurrentNodeProperties(nodeValue: BigTextNodeValue, left: RedBlackTree<BigTextNodeValue>.Node?) = with (nodeValue) { | ||
super.computeCurrentNodeProperties(nodeValue, left) | ||
|
||
this as BigTextTransformNodeValue | ||
left as RedBlackTree<BigTextTransformNodeValue>.Node? | ||
leftTransformedLength = left?.transformedOffset() ?: 0 | ||
leftRenderLength = left?.renderLength() ?: 0 | ||
leftOverallLength = left?.overallLength() ?: 0 | ||
} | ||
} | ||
|
||
fun RedBlackTree<BigTextTransformNodeValue>.Node.transformedOffset(): Int = | ||
(getValue()?.leftTransformedLength ?: 0) + | ||
(getValue()?.currentTransformedLength ?: 0) + | ||
(getRight().takeIf { it.isNotNil() }?.transformedOffset() ?: 0) |
13 changes: 13 additions & 0 deletions
13
...n/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/bigtext/LengthNodeValue.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.sunnychung.application.multiplatform.hellohttp.ux.bigtext | ||
|
||
interface LengthNodeValue { | ||
val leftStringLength: Int | ||
|
||
val bufferLength: Int | ||
|
||
val leftOverallLength: Int | ||
val currentOverallLength: Int | ||
|
||
val leftRenderLength: Int | ||
val currentRenderLength: Int | ||
} |
62 changes: 62 additions & 0 deletions
62
...vmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/bigtext/LengthTree.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package com.sunnychung.application.multiplatform.hellohttp.ux.bigtext | ||
|
||
import com.williamfiset.algorithms.datastructures.balancedtree.RedBlackTree | ||
|
||
open class LengthTree<out V>(computations: RedBlackTreeComputations<V>) : RedBlackTree2<@UnsafeVariance V>(computations) | ||
where V : LengthNodeValue, V : Comparable<@UnsafeVariance V>, V : DebuggableNode<in @UnsafeVariance V> { | ||
|
||
fun findNodeByCharIndex(index: Int): RedBlackTree<V>.Node? { | ||
var find = index | ||
return findNode { | ||
when (find) { | ||
in Int.MIN_VALUE until it.value.leftStringLength -> -1 | ||
it.value.leftStringLength, in it.value.leftStringLength until it.value.leftStringLength + it.value.bufferLength -> { | ||
if (it.left.isNotNil() && find == it.value.leftStringLength && it.left.value.bufferLength == 0) { | ||
-1 | ||
} else { | ||
0 | ||
} | ||
} | ||
in it.value.leftStringLength + it.value.bufferLength until Int.MAX_VALUE -> 1.also { compareResult -> | ||
val isTurnRight = compareResult > 0 | ||
if (isTurnRight) { | ||
find -= it.value.leftStringLength + it.value.bufferLength | ||
} | ||
} | ||
else -> throw IllegalStateException("what is find? $find") | ||
} | ||
} | ||
} | ||
|
||
fun findNodeByRenderCharIndex(index: Int): RedBlackTree<V>.Node? { | ||
var find = index | ||
return findNode { | ||
when (find) { | ||
in Int.MIN_VALUE until it.value.leftRenderLength -> -1 | ||
in it.value.leftRenderLength until it.value.leftRenderLength + it.value.currentRenderLength -> 0 | ||
in it.value.leftRenderLength + it.value.currentRenderLength until Int.MAX_VALUE -> 1.also { compareResult -> | ||
val isTurnRight = compareResult > 0 | ||
if (isTurnRight) { | ||
find -= it.value.leftRenderLength + it.value.currentRenderLength | ||
} | ||
} | ||
else -> throw IllegalStateException("what is find? $find") | ||
} | ||
} | ||
} | ||
} | ||
|
||
fun <V> RedBlackTree<V>.Node.length(): Int where V : LengthNodeValue, V : Comparable<V> = | ||
(getValue()?.leftStringLength ?: 0) + | ||
(getValue()?.bufferLength ?: 0) + | ||
(getRight().takeIf { it.isNotNil() }?.length() ?: 0) | ||
|
||
fun <V> RedBlackTree<V>.Node.renderLength(): Int where V : LengthNodeValue, V : Comparable<V> = | ||
(getValue()?.leftRenderLength ?: 0) + | ||
(getValue()?.currentRenderLength ?: 0) + | ||
(getRight().takeIf { it.isNotNil() }?.renderLength() ?: 0) | ||
|
||
fun <V> RedBlackTree<V>.Node.overallLength(): Int where V : LengthNodeValue, V : Comparable<V> = | ||
(getValue()?.leftOverallLength ?: 0) + | ||
(getValue()?.currentOverallLength ?: 0) + | ||
(getRight().takeIf { it.isNotNil() }?.overallLength() ?: 0) |
Oops, something went wrong.