Skip to content

Commit

Permalink
add ropes4k
Browse files Browse the repository at this point in the history
  • Loading branch information
time4tea committed Aug 10, 2024
1 parent 5ac531b commit bea5453
Show file tree
Hide file tree
Showing 69 changed files with 17,276 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Forkhandles (4k) contains foundational libraries for Kotlin
- [Parser4k](parser4k) - Recursive descent parser combinator library
- [Partial4k](partial4k) - Adds partial application of functions to Kotlin
- [Result4k](result4k) - A usable Result type
- [Ropes4k](ropes4k) - A high-performance Rope data type.
- [State4k](state4k) - Simple event/command state machine modelling
- [Time4k](time4k) - Clock and deterministic scheduler
- [Tuples4k](tuples4k) - Tuple classes
Expand Down
3 changes: 3 additions & 0 deletions mock4k/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ description = "ForkHandles: The very cheapest mocking framework platform."

dependencies {
testImplementation(KotlinX.coroutines.core)

testImplementation(platform("org.junit:junit-bom:_"))
testImplementation("org.junit.jupiter:junit-jupiter")
}
1 change: 1 addition & 0 deletions projects.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mock4k
parser4k
partial4k
result4k
ropes4k
time4k
tuples4k
values4k
636 changes: 636 additions & 0 deletions ropes4k/LICENSE.md

Large diffs are not rendered by default.

153 changes: 153 additions & 0 deletions ropes4k/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@

# Ropes4k

An implementation of a Rope data structure in Kotlin.

Please note this library has a different license from the other forkhandles libraries due to
being derived from an upstream library.

[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)

## Why use a Rope?

Ropes are much faster than Strings, StringBuffers, or CharArrays for insert and delete operations.
When inserting or deleting, they don't copy the data meaning that the working data set size is much smaller.

## How much faster?

Really a lot, see the performance charts. These show comparitive performance
of Ropes, Strings and StringBuffers for a few operations. The numbers themselves
are not relevant, only the relative speeds.

Inserts, Prepends, and Deletes are effectively hundreds of times faster.

## What's the downside?

Because Ropes are stored as trees, getting characters by index is slower, so random-access into Ropes is not the best access pattern.
In-Order and Reversed-Order iteration of Ropes is relatively efficient in this implementation.

## How to use?

A Rope implements the following interfaces:

- CharSequence
- Iterable<Char>
- Comparable<CharSequence>
- Serializable

Your code should refer to CharSequence wherever possible. If it does, then using a Rope
is as simple as:

```kotlin
val r = Rope.of("123")
```

If you have a `CharArray`, you can use `ofCopy` - This is to highlight that `CharArray`s are copied right now.

```kotlin
var r = Rope.ofCopy("123".toCharArray())
```


## Caveats

The Rope doesn't copy your `CharSequence`, but Ropes are supposed to be immutable, so if you modify the underlying `CharSequence`
undefined behaviour will happen.

The current implementation *does* copy a `CharArray` on construction, and option is planned later to make a zero-copy version.

## Performance

### dev.forkhandles.ropes4k.test.bench.AppendBenchmark

Appending 500 subsequences of a 200kB charsequence to itself.

![dev.forkhandles.ropes4k.test.bench.AppendBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.AppendBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.CreationBenchmark

Instantiating a 200kB instance. A Rope based on a CharSequence does no copy,
so it's way faster than the others, that all copy in some way.

![dev.forkhandles.ropes4k.test.bench.CreationBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.CreationBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.DeleteBenchmark

Deleting 500 subsequences from an original 200kB.

![dev.forkhandles.ropes4k.test.bench.DeleteBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.DeleteBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.IndexBenchmark

Indexing into a 200kB char string.

![dev.forkhandles.ropes4k.test.bench.IndexBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.IndexBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.InsertBenchmark

Inserting subsequences of a rope into itself

![dev.forkhandles.ropes4k.test.bench.InsertBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.InsertBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.Insert2Benchmark

Inserting subsequences of a rope into a different rope

![dev.forkhandles.ropes4k.test.bench.Insert2Benchmark](graphs/dev.forkhandles.ropes4k.test.bench.Insert2Benchmark.png)


### dev.forkhandles.ropes4k.test.bench.PrependBenchmark

Prepending 500 subsequences of a 200kB character string onto itself.

![dev.forkhandles.ropes4k.test.bench.PrependBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.PrependBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.RegexComplexBenchmark

![dev.forkhandles.ropes4k.test.bench.RegexComplexBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.RegexComplexBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.RegexSimpleBenchmark

Running simple regex against 200kB character string

![dev.forkhandles.ropes4k.test.bench.RegexSimpleBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.RegexSimpleBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.SearchSimpleBenchmark

Finding simple string in 200kB character string.

![dev.forkhandles.ropes4k.test.bench.SearchSimpleBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.SearchSimpleBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.SearchComplexBenchmark
![dev.forkhandles.ropes4k.test.bench.SearchComplexBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.SearchComplexBenchmark.png)



### dev.forkhandles.ropes4k.test.bench.TraversalBenchmark

Iterating over 200kB character string

![dev.forkhandles.ropes4k.test.bench.TraversalBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.TraversalBenchmark.png)



### dev.forkhandles.ropes4k.test.bench.WriteComplexBenchmark
![dev.forkhandles.ropes4k.test.bench.WriteComplexBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.WriteComplexBenchmark.png)



# Original Implementation

This version started off as [Ropes for Java](http://ahmadsoft.org/ropes/release.html) and the original code is GPL Licence

The original author of Ropes for Java was Amin Ahmad.

62 changes: 62 additions & 0 deletions ropes4k/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2024 James Richardson
* Licenced under GPL
*/

description = "ForkHandles ropes library"

plugins {
id("org.jetbrains.kotlinx.benchmark") version "0.4.10"
id("io.morethan.jmhreport") version "0.9.0"
}

kotlin {
explicitApi()
}

dependencies {
testImplementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:_")

testImplementation(platform("org.junit:junit-bom:_"))
testImplementation("org.junit.jupiter:junit-jupiter")

testImplementation("io.strikt:strikt-core:_")
}

tasks.test {
useJUnitPlatform {
excludeTags("Performance")
}
}

// build.gradle.kts
benchmark {
targets {
register("test")
}
benchmark {
configurations {
register("single") {
include(".*.\\.WriteComplexBenchmark")
}
}
}
}

// currently has to be run by hand after the benchmark,else it picks up previous run :-(
jmhReport {

fun findMostRecentJmhReportIn(d: File): String? {
return d.walkBottomUp()
.filter { it.name == "test.json" }
.sortedByDescending { it.lastModified() }
.firstOrNull()
?.absolutePath
?.also {
println("Selected JMH Report is $it")
}
}

jmhResultPath = findMostRecentJmhReportIn(project.file("build/reports/benchmarks"))
jmhReportOutput = project.file("build/reports/benchmarks").absolutePath
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 56 additions & 0 deletions ropes4k/graphs/links.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
### dev.forkhandles.ropes4k.test.bench.AppendBenchmark
![dev.forkhandles.ropes4k.test.bench.AppendBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.AppendBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.CreationBenchmark
![dev.forkhandles.ropes4k.test.bench.CreationBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.CreationBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.DeleteBenchmark
![dev.forkhandles.ropes4k.test.bench.DeleteBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.DeleteBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.IndexBenchmark
![dev.forkhandles.ropes4k.test.bench.IndexBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.IndexBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.Insert2Benchmark
![dev.forkhandles.ropes4k.test.bench.Insert2Benchmark](graphs/dev.forkhandles.ropes4k.test.bench.Insert2Benchmark.png)


### dev.forkhandles.ropes4k.test.bench.InsertBenchmark
![dev.forkhandles.ropes4k.test.bench.InsertBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.InsertBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.PrependBenchmark
![dev.forkhandles.ropes4k.test.bench.PrependBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.PrependBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.RegexComplexBenchmark
![dev.forkhandles.ropes4k.test.bench.RegexComplexBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.RegexComplexBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.RegexSimpleBenchmark
![dev.forkhandles.ropes4k.test.bench.RegexSimpleBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.RegexSimpleBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.SearchComplexBenchmark
![dev.forkhandles.ropes4k.test.bench.SearchComplexBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.SearchComplexBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.SearchSimpleBenchmark
![dev.forkhandles.ropes4k.test.bench.SearchSimpleBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.SearchSimpleBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.TraversalBenchmark
![dev.forkhandles.ropes4k.test.bench.TraversalBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.TraversalBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.TraversalComplexBenchmark
![dev.forkhandles.ropes4k.test.bench.TraversalComplexBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.TraversalComplexBenchmark.png)


### dev.forkhandles.ropes4k.test.bench.WriteComplexBenchmark
![dev.forkhandles.ropes4k.test.bench.WriteComplexBenchmark](graphs/dev.forkhandles.ropes4k.test.bench.WriteComplexBenchmark.png)


Loading

0 comments on commit bea5453

Please sign in to comment.