RUMM-2510 Fix the mutation resolver alg based on the Heckels definition#1065
Conversation
839f77e to
ee7df40
Compare
Codecov Report❌ Patch coverage is Please upload reports for the commit e625eac to get more accurate results.
Additional details and impacted files@@ Coverage Diff @@
## feature/session-replay-vo #1065 +/- ##
=============================================================
- Coverage 82.40% 82.22% -0.18%
=============================================================
Files 346 346
Lines 11040 11109 +69
Branches 1814 1839 +25
=============================================================
+ Hits 9097 9134 +37
- Misses 1383 1397 +14
- Partials 560 578 +18
🚀 New features to boost your workflow:
|
ee7df40 to
8a85559
Compare
0xnm
left a comment
There was a problem hiding this comment.
Nice! I've added few questions regarding the algorithm.
| - "kotlin.collections.MutableList.add(java.net.InetAddress)" | ||
| - "kotlin.collections.MutableList.addAll(kotlin.collections.Collection)" | ||
| - "kotlin.collections.MutableList.clear()" | ||
| - "kotlin.collections.MutableMap.containsKey(kotlin.Long)" |
There was a problem hiding this comment.
should be below, in the MutableMap zone.
| /** Computes a diff between two arrays. | ||
| * This implementation is based on Paul Heckel's algorithm for finding differences |
There was a problem hiding this comment.
| /** Computes a diff between two arrays. | |
| * This implementation is based on Paul Heckel's algorithm for finding differences | |
| /** | |
| * Computes a diff between two arrays. | |
| * This implementation is based on Paul Heckel's algorithm for finding differences |
| * within each of two arrays. It means that all elements in | ||
| * `oldArray` are guaranteed to have different `id` (same for `newArray`). Elements with | ||
| * the same `id` can appear in both arrays, which | ||
| * indicates one of two things determined by `newElement.isDifferent(than: oldElement)`: |
There was a problem hiding this comment.
seems like a Swift syntax :) Also happens in some other comments of this file.
| ): | ||
| MobileSegment.MobileIncrementalData.MobileMutationData? { |
There was a problem hiding this comment.
| ): | |
| MobileSegment.MobileIncrementalData.MobileMutationData? { | |
| ): MobileSegment.MobileIncrementalData.MobileMutationData? { |
| // to it in both files are identical, | ||
| // > then these lines must be the same line. | ||
| // This information can be used to find blocks of unchanged lines. | ||
| if (na.size > 1) { |
There was a problem hiding this comment.
is this line needed? if na.size <= 1, the for loop will be no-op anyway.
There was a problem hiding this comment.
followed exactly the algorithm from iOS but I will try to see what will happen in case I remove that condition and have a for(i in 1 until 0) loop.
| resolveUpdateMutation(prevMatchedWireframe, currentWireframe)?.let { | ||
| updates.add(it) | ||
| val removes: MutableList<MobileSegment.Remove> = LinkedList() | ||
| var removalOffsets = Array(oldSnapshot.size) { 0 } |
There was a problem hiding this comment.
| var removalOffsets = Array(oldSnapshot.size) { 0 } | |
| var removalOffsets = IntArray(oldSnapshot.size) |
| } | ||
| val removes = prevSnapshotAsMap.map { MobileSegment.Remove(it.key) } | ||
|
|
||
| runningOffset = 0 |
There was a problem hiding this comment.
seems it is not used after, so this line can be removed?
| val oldElement = oldSnapshot[indexInOld] | ||
| if ((indexInOld - removalOffset + runningOffset) != index) { | ||
| // Old element was moved to another position: | ||
| val previousID = if (index > 0) newSnapshot[index - 1].id() else null |
There was a problem hiding this comment.
| val previousID = if (index > 0) newSnapshot[index - 1].id() else null | |
| val previousId = if (index > 0) newSnapshot[index - 1].id() else null |
| } | ||
| is Entry.Reference -> { | ||
| // New element was added: | ||
| val previousID = if (index > 0) newSnapshot[index - 1].id() else null |
There was a problem hiding this comment.
| val previousID = if (index > 0) newSnapshot[index - 1].id() else null | |
| val previousId = if (index > 0) newSnapshot[index - 1].id() else null |
| val oldElement = oldSnapshot[indexInOld] | ||
| if ((indexInOld - removalOffset + runningOffset) != index) { | ||
| // Old element was moved to another position: | ||
| val previousID = if (index > 0) newSnapshot[index - 1].id() else null |
There was a problem hiding this comment.
why do we compare index with 0 and take index-1 later?
There was a problem hiding this comment.
Hmmm...because if you do not do that check you will have an IOB exception.
There was a problem hiding this comment.
Sorry, I meant what happens with index=0 (we shouldn't use it?) and why we substract 1 to get an element from newSnapshot.
There was a problem hiding this comment.
we use it, basically we try to get the previousId here which in case index=0 it will be null (see the else branch)
8a85559 to
320509f
Compare
0xnm
left a comment
There was a problem hiding this comment.
I added few suggestions, but they are non-blocking for me. LGTM!
|
|
||
| // 5th pass | ||
| // Similar to 4th pass, except it processes entries in descending order. | ||
| if (na.size > 1) { |
There was a problem hiding this comment.
I guess we can align with the change in 4th pass and remove this line.
| resolveUpdateMutation(prevMatchedWireframe, currentWireframe)?.let { | ||
| updates.add(it) | ||
| val removes: MutableList<MobileSegment.Remove> = LinkedList() | ||
| val removalOffsets = Array(oldSnapshot.size) { 0 } |
There was a problem hiding this comment.
| val removalOffsets = Array(oldSnapshot.size) { 0 } | |
| val removalOffsets = IntArray(oldSnapshot.size) |
| if (prevMatchedWireframe != null) { | ||
| resolveUpdateMutation(prevMatchedWireframe, currentWireframe)?.let { | ||
| updates.add(it) | ||
| val removes: MutableList<MobileSegment.Remove> = LinkedList() |
There was a problem hiding this comment.
| val removes: MutableList<MobileSegment.Remove> = LinkedList() | |
| val removes = LinkedList<MobileSegment.Remove>() |
320509f to
68a4e49
Compare
68a4e49 to
e625eac
Compare
What does this PR do?
The algorithm itself is based on Paul Heckel's paper: "A technique for isolating differences between files" (1978). It is fast and performant (O(n) in time and memory) way of computing changes in text files. It's quite popular and there are many implementations out there (e.g. DifferenceKit, or hdiff in Rust). I managed to simplify it a bit for our problem - unlike lines in text files, IDs in our wireframes are guaranteed to be unique.
Motivation
What inspired you to submit this pull request?
Additional Notes
Anything else we should know when reviewing?
Review checklist (to be filled by reviewers)