Skip to content

Commit a0400eb

Browse files
committed
Avoid systematic potentially heavy equality check at first
1 parent 9ae2e0e commit a0400eb

File tree

2 files changed

+24
-26
lines changed

2 files changed

+24
-26
lines changed

.scalafmt.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version = "3.7.2"
1+
version = "3.7.1"
22
maxColumn = 120
33
danglingParentheses.preset = false
44
align.preset = some

core/src/main/scala/diffson/jsonpatch/JsonDiff.scala

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,30 +20,30 @@ package jsonpatch
2020
import lcs._
2121
import jsonpointer._
2222

23-
import cats.implicits._
23+
import cats.syntax.all._
24+
import cats.data.Chain
25+
import cats.Eval
2426

2527
import scala.annotation.tailrec
26-
import cats.data.Chain
2728

2829
class JsonDiff[Json](diffArray: Boolean, rememberOld: Boolean)(implicit J: Jsony[Json], Lcs: Lcs[Json])
2930
extends Diff[Json, JsonPatch[Json]] {
3031
def diff(json1: Json, json2: Json): JsonPatch[Json] =
31-
JsonPatch(diff(json1, json2, Pointer.Root).toList)
32+
JsonPatch(diff(json1, json2, Pointer.Root).value.toList)
3233

33-
private def diff(json1: Json, json2: Json, pointer: Pointer): Chain[Operation[Json]] =
34-
if (json1 === json2)
35-
// if they are equal, this one is easy...
36-
Chain.empty
37-
else
38-
(json1, json2) match {
39-
case (JsObject(fields1), JsObject(fields2)) => fieldsDiff(fields1.toList, fields2.toList, pointer)
40-
case (JsArray(arr1), JsArray(arr2)) if diffArray => arraysDiff(arr1.toList, arr2.toList, pointer)
41-
case (_, _) => Chain.one(Replace(pointer, json2, if (rememberOld) Some(json1) else None))
42-
}
34+
private def diff(json1: Json, json2: Json, pointer: Pointer): Eval[Chain[Operation[Json]]] =
35+
(json1, json2) match {
36+
case (JsObject(fields1), JsObject(fields2)) => fieldsDiff(fields1.toList, fields2.toList, pointer)
37+
case (JsArray(arr1), JsArray(arr2)) if diffArray => arraysDiff(arr1.toList, arr2.toList, pointer)
38+
case _ if json1 === json2 =>
39+
// if they are equal, this one is easy...
40+
Eval.now(Chain.empty)
41+
case _ => Eval.now(Chain.one(Replace(pointer, json2, if (rememberOld) Some(json1) else None)))
42+
}
4343

4444
private def fieldsDiff(fields1: List[(String, Json)],
4545
fields2: List[(String, Json)],
46-
path: Pointer): Chain[Operation[Json]] = {
46+
path: Pointer): Eval[Chain[Operation[Json]]] = {
4747
// sort fields by name in both objects
4848
val sorted1 = fields1.sortBy(_._1)
4949
val sorted2 = fields2.sortBy(_._1)
@@ -67,28 +67,27 @@ class JsonDiff[Json](diffArray: Boolean, rememberOld: Boolean)(implicit J: Jsony
6767
Chain.fromSeq(fields2.map(None -> Some(_))) ++ acc
6868
}
6969

70-
@tailrec
7170
def fields(fs: List[(Option[(String, Json)], Option[(String, Json)])],
72-
acc: Chain[Operation[Json]]): Chain[Operation[Json]] = fs match {
73-
case (Some(f1), Some(f2)) :: tl if f1 == f2 =>
71+
acc: Chain[Operation[Json]]): Eval[Chain[Operation[Json]]] = fs match {
72+
case (Some(f1), Some(f2)) :: tl if f1._2 === f2._2 =>
7473
// all right, nothing changed
7574
fields(tl, acc)
7675
case (Some(f1), Some(f2)) :: tl =>
7776
// same field name, different values
78-
fields(tl, diff(f1._2, f2._2, path / f1._1) ++ acc)
77+
diff(f1._2, f2._2, path / f1._1).flatMap(d => fields(tl, d ++ acc))
7978
case (Some(f1), None) :: tl =>
8079
// the field was deleted
8180
fields(tl, acc.prepend(Remove[Json](path / f1._1, if (rememberOld) Some(f1._2) else None)))
8281
case (None, Some(f2)) :: tl =>
8382
// the field was added
8483
fields(tl, acc.prepend(Add(path / f2._1, f2._2)))
8584
case _ =>
86-
acc
85+
Eval.now(acc)
8786
}
8887
fields(associate(sorted1, sorted2, Chain.empty).toList, Chain.empty)
8988
}
9089

91-
private def arraysDiff(arr1: List[Json], arr2: List[Json], path: Pointer): Chain[Operation[Json]] = {
90+
private def arraysDiff(arr1: List[Json], arr2: List[Json], path: Pointer): Eval[Chain[Operation[Json]]] = {
9291
// get the longest common subsequence in the array
9392
val lcs = Lcs.lcs(arr1, arr2)
9493

@@ -118,7 +117,6 @@ class JsonDiff[Json](diffArray: Boolean, rememberOld: Boolean)(implicit J: Jsony
118117
yield Remove[Json](path / idx, if (rememberOld) Some(arr(idx - shift)) else None))
119118

120119
// now iterate over the first array to computes what was added, what was removed and what was modified
121-
@tailrec
122120
def loop(
123121
arr1: List[Json], // the first array
124122
arr2: List[Json], // the second array
@@ -127,7 +125,7 @@ class JsonDiff[Json](diffArray: Boolean, rememberOld: Boolean)(implicit J: Jsony
127125
idx2: Int, // current index in the second array
128126
lcs: List[(Int, Int)], // the list of remaining matching indices
129127
acc: Chain[Operation[Json]] // the already accumulated result
130-
): Chain[Operation[Json]] = (arr1, arr2) match {
128+
): Eval[Chain[Operation[Json]]] = (arr1, arr2) match {
131129
case (_ :: tl1, _) if isCommon1(idx1, lcs) =>
132130
// all values in arr2 were added until the index of common value
133131
val until = lcs.head._2
@@ -150,13 +148,13 @@ class JsonDiff[Json](diffArray: Boolean, rememberOld: Boolean)(implicit J: Jsony
150148
acc ++ remove(idx1 + shift1, until - 1 + shift1, idx1 + shift1, arr1))
151149
case (v1 :: tl1, v2 :: tl2) =>
152150
// values are different, recursively compute the diff of these values
153-
loop(tl1, tl2, idx1 + 1, shift1, idx2 + 1, lcs, acc ++ diff(v1, v2, path / (idx1 + shift1)))
151+
diff(v1, v2, path / (idx1 + shift1)).flatMap(d => loop(tl1, tl2, idx1 + 1, shift1, idx2 + 1, lcs, acc ++ d))
154152
case (_, Nil) =>
155153
// all subsequent values in arr1 were removed
156-
acc ++ remove(idx1 + shift1, idx1 + arr1.size - 1 + shift1, idx1 + shift1, arr1)
154+
Eval.now(acc ++ remove(idx1 + shift1, idx1 + arr1.size - 1 + shift1, idx1 + shift1, arr1))
157155
case (Nil, _) =>
158156
// all subsequent value in arr2 were added
159-
acc ++ Chain.fromSeq(arr2.map(Add(path / "-", _)))
157+
Eval.now(acc ++ Chain.fromSeq(arr2.map(Add(path / "-", _))))
160158
}
161159

162160
loop(arr1, arr2, 0, 0, 0, lcs, Chain.empty)

0 commit comments

Comments
 (0)