Skip to content

Commit

Permalink
Introduce Myers algorithm in linear space (#112)
Browse files Browse the repository at this point in the history
  • Loading branch information
lppedd authored Jun 11, 2024
1 parent 875f4ce commit edfab24
Show file tree
Hide file tree
Showing 38 changed files with 1,841 additions and 925 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ build
/.idea
*.iml
out
.DS_Store
.DS_Store
.kotlin/
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ All credit for the implementation goes to original authors.

## Features

All features from version `4.10` of the original library are present, except for:
All features from version `4.12` of the original library are present, except for:

- fuzzy patches
- unified diff, which heavily uses file read/write and therefore needs a more complicated rewrite
- diff-utils-jgit, which uses JVM-only jgit library
- diff-utils-jgit, which uses JVM-only JGit

Please refer to the original guides for more information.

Expand All @@ -29,8 +30,10 @@ Currently, artifacts for the following platforms are supported:
- WebAssembly (JS and WASI)
- Native

The supported Native targets are (following the Kotlin/Native [target support guidelines](https://kotlinlang.org/docs/native-target-support.html)):
The supported Native targets are (following the Kotlin/Native [target support guidelines][1]):

| Tier 1 | Tier 2 | Tier 3 |
|:---------|:---------|:---------|
| macosX64 | linuxX64 | mingwX64 |

[1]: https://kotlinlang.org/docs/native-target-support.html
38 changes: 26 additions & 12 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest

plugins {
kotlin("multiplatform")
Expand All @@ -31,12 +32,17 @@ kotlin {

jvm {
compilations.configureEach {
compilerOptions.configure {
// Minimum bytecode level is 52
jvmTarget = JvmTarget.JVM_1_8

// Output interfaces with default methods
freeCompilerArgs.add("-Xjvm-default=all")
compileTaskProvider.configure {
compilerOptions {
// Minimum bytecode level is 52
jvmTarget = JvmTarget.JVM_1_8

// Output interfaces with default methods
freeCompilerArgs.addAll(
"-Xjvm-default=all", // Output interfaces with default methods
"-Xno-param-assertions", // Remove Intrinsics.checkNotNullParameter
)
}
}
}

Expand All @@ -48,23 +54,31 @@ kotlin {
}

js {
browser()
nodejs()
val testConfig: (KotlinJsTest).() -> Unit = {
useMocha {
// Override default 2s timeout
timeout = "120s"
}
}

browser {
testTask(testConfig)
}

nodejs {
testTask(testConfig)
}
}

@OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser()
nodejs()
applyBinaryen()
}

@OptIn(ExperimentalWasmDsl::class)
wasmWasi {
nodejs()

// Available since 2.0
// applyBinaryen()
}

linuxX64()
Expand Down
9 changes: 9 additions & 0 deletions detekt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,12 @@ formatting:
active: false
TrailingCommaOnDeclarationSite:
active: true
complexity:
ComplexCondition:
active: false
CyclomaticComplexMethod:
active: false
NestedBlockDepth:
active: false
LongParameterList:
functionThreshold: 7
897 changes: 502 additions & 395 deletions kotlin-js-store/yarn.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package io.github.petertrr.diffutils

import io.github.petertrr.diffutils.algorithm.DiffAlgorithm
import io.github.petertrr.diffutils.algorithm.DiffAlgorithmListener
import io.github.petertrr.diffutils.algorithm.DiffEqualizer
import io.github.petertrr.diffutils.algorithm.NoopAlgorithmListener
import io.github.petertrr.diffutils.algorithm.myers.MyersDiff
import io.github.petertrr.diffutils.patch.Patch
Expand Down Expand Up @@ -73,7 +74,7 @@ public fun diff(
public fun <T> diff(
source: List<T>,
target: List<T>,
equalizer: ((T, T) -> Boolean),
equalizer: DiffEqualizer<T>,
): Patch<T> =
diff(
source = source,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@
package io.github.petertrr.diffutils.algorithm

import io.github.petertrr.diffutils.patch.DeltaType
import kotlin.jvm.JvmField

public data class Change(
val deltaType: DeltaType,
val startOriginal: Int,
val endOriginal: Int,
val startRevised: Int,
val endRevised: Int,
@JvmField val deltaType: DeltaType,
@JvmField val startOriginal: Int,
@JvmField val endOriginal: Int,
@JvmField val startRevised: Int,
@JvmField val endRevised: Int,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2024 Peter Trifanov.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.github.petertrr.diffutils.algorithm

public fun interface DiffEqualizer<T> {
public fun test(one: T, two: T): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2024 Peter Trifanov.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.github.petertrr.diffutils.algorithm

public class EqualsDiffEqualizer<T> : DiffEqualizer<T> {
override fun test(one: T, two: T): Boolean =
one == two
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2024 Peter Trifanov.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.github.petertrr.diffutils.algorithm

public class IgnoreWsStringDiffEqualizer : DiffEqualizer<String> {
private companion object {
private val ws = Regex("\\s+")
}

override fun test(one: String, two: String): Boolean =
ws.replace(one.trim(), " ") == ws.replace(two.trim(), " ")
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ package io.github.petertrr.diffutils.algorithm.myers
import io.github.petertrr.diffutils.algorithm.Change
import io.github.petertrr.diffutils.algorithm.DiffAlgorithm
import io.github.petertrr.diffutils.algorithm.DiffAlgorithmListener
import io.github.petertrr.diffutils.algorithm.DiffEqualizer
import io.github.petertrr.diffutils.algorithm.EqualsDiffEqualizer
import io.github.petertrr.diffutils.patch.DeltaType

/**
* A clean-room implementation of Eugene Myers greedy differencing algorithm.
*/
public class MyersDiff<T>(private val equalizer: (T, T) -> Boolean = { t1, t2 -> t1 == t2 }) : DiffAlgorithm<T> {
public class MyersDiff<T>(private val equalizer: DiffEqualizer<T> = EqualsDiffEqualizer()) : DiffAlgorithm<T> {
/**
* Returns an empty diff if we get an error while procession the difference.
*/
Expand Down Expand Up @@ -82,7 +84,7 @@ public class MyersDiff<T>(private val equalizer: (T, T) -> Boolean = { t1, t2 ->
var j = i - k
var node = PathNode(i, j, snake = false, bootstrap = false, prev = prev)

while (i < origSize && j < revSize && equalizer.invoke(orig[i], rev[j])) {
while (i < origSize && j < revSize && equalizer.test(orig[i], rev[j])) {
i++
j++
}
Expand Down
Loading

0 comments on commit edfab24

Please sign in to comment.