basics done, ready to refactor...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2018-07-13 21:40:42 +03:00
parent 5823b7b944
commit e4997c1d52

162
diff.js
View File

@ -34,20 +34,26 @@
// positions nor does it affect the the array lengths. // positions nor does it affect the the array lengths.
var EMPTY = {type: 'EMPTY'} var EMPTY = {type: 'EMPTY'}
var NONE = {type: 'NONE'}
//--------------------------------------------------------------------- //---------------------------------------------------------------------
// Helpers... // Helpers...
// XXX should we handle properties??? // XXX should we handle properties???
var _diff_items = function(diff, A, B, options, filter){ var _diff_items = function(diff, res, A, B, options, filter){
// JSON mode -> ignore attr order... // JSON mode -> ignore attr order...
var kA = Object.keys(A) var kA = Object.keys(A)
var kB = Object.keys(B) var kB = Object.keys(B)
if(filter){ if(filter){
kA = kA.filter(filter) kA = filter instanceof Array ?
kB = kB.filter(filter) filter.slice()
: kA.filter(filter)
kB = filter instanceof Array ?
filter.slice()
: kB.filter(filter)
} }
var B_index = kB.reduce(function(res, k){ var B_index = kB.reduce(function(res, k){
@ -60,7 +66,7 @@ var _diff_items = function(diff, A, B, options, filter){
// A keys... // A keys...
.map(function(ka){ .map(function(ka){
var res = [ka, var res = [ka,
_diff( diff(
A[ka], A[ka],
ka in B_index ? B[ka] : EMPTY, ka in B_index ? B[ka] : EMPTY,
options)] options)]
@ -72,7 +78,7 @@ var _diff_items = function(diff, A, B, options, filter){
.concat(Object.keys(B_index) .concat(Object.keys(B_index)
.map(function(kb){ .map(function(kb){
return [kb, return [kb,
_diff( diff(
EMPTY, EMPTY,
B[kb], B[kb],
options)]})) options)]}))
@ -80,24 +86,28 @@ var _diff_items = function(diff, A, B, options, filter){
.filter(function(e){ .filter(function(e){
return e[1] !== null }) return e[1] !== null })
items.length > 0 items.length > 0
&& (diff.items = (diff.items || []).concat(items)) && (res.items = (res.items || []).concat(items))
return diff return res
} }
var _diff_item_order = function(diff, A, B, options, filter){ var _diff_item_order = function(diff, res, A, B, options, filter){
var kA = Object.keys(A) var kA = Object.keys(A)
var kB = Object.keys(B) var kB = Object.keys(B)
if(filter){ if(filter){
kA = kA.filter(filter) kA = filter instanceof Array ?
kB = kB.filter(filter) filter.slice()
: kA.filter(filter)
kB = filter instanceof Array ?
filter.slice()
: kB.filter(filter)
} }
var item_order = _diff(kA, kB, {mode: 'JSON'}) var item_order = diff(kA, kB, {mode: 'JSON'})
item_order != null item_order != null
&& (diff.item_order = item_order) && (res.item_order = item_order)
return diff return res
} }
@ -170,6 +180,48 @@ var getCommonSections = function(A, B, cmp, min_chunk){
} }
// Format:
// [
// [
// [<gap-offset-A>,
// [ item, ... ]],
// [<gap-offset-B>,
// [ item, ... ]],
// ],
// ...
// ]
var getDiffSections = function(A, B, cmp, min_chunk){
// find the common sections...
var common_sections = getCommonSections(A, B, cmp, min_chunk)
common_sections.shift()
// collect gaps between common sections...
var a = 0
var b = 0
var gaps = []
common_sections
// make this consider the tail gap...
.concat({
A: A.length,
B: B.length,
length: 0,
})
.forEach(function(e){
// store the gap...
;(a != e.A || b != e.B)
&& gaps.push([
[a, A.slice(a, e.A)],
[b, B.slice(b, e.B)],
])
// go to next gap...
a = e.A + e.length
b = e.B + e.length
})
return gaps
}
//--------------------------------------------------------------------- //---------------------------------------------------------------------
// //
@ -197,11 +249,7 @@ var getCommonSections = function(A, B, cmp, min_chunk){
// [<key>, <diff>], // [<key>, <diff>],
// //
// // [S]plice section starting at key... // // [S]plice section starting at key...
// // The <diff> should contain two array sections. // [<key-a>, <key-b>, <diff>],
// // The section is treated as a seporate array, diffed
// // and spliced into the target array at <key>.
// // XXX is this too complicated???
// ['S', <key>, <diff>],
// //
// ... // ...
// ], // ],
@ -226,7 +274,7 @@ var getCommonSections = function(A, B, cmp, min_chunk){
// XXX revise format... // XXX revise format...
// XXX support Map(..) and other new-style types... // XXX support Map(..) and other new-style types...
var _diff = var _diff =
function(A, B, options){ function(A, B, options, cache){
options = options || {} options = options || {}
// same object... // same object...
@ -249,13 +297,18 @@ function(A, B, options){
// cache... // cache...
// XXX use this everywhere we use _diff... // XXX use this everywhere we use _diff...
var cache = new Map() cache = cache || new Map()
var cacheDiff = function(a, b){ var cacheDiff = cache.diff = cache.diff || function(a, b){
var l2 = cache.get(a) || new Map() var l2 = cache.get(a) || new Map()
var d = l2.get(b) || _diff(a, b) var d = l2.get(b) || _diff(a, b, options, cache)
cache.set(a, l2.set(b, d)) cache.set(a, l2.set(b, d))
return d return d
} }
var cmp = function(a, b){
return a === b
|| a == b
|| (cacheDiff(a, b) == null)
}
// Array... // Array...
// XXX check seen -- avoid recursion... // XXX check seen -- avoid recursion...
@ -265,52 +318,47 @@ function(A, B, options){
length: [A.length, B.length], length: [A.length, B.length],
} }
// find the common sections...
// XXX cache _diff(..) results...
var common_sections = getCommonSections(A, B,
function(a, b){
return a === b || a == b || cacheDiff(a, b) })
// collect gaps between common sections...
common_sections.shift()
var a = 0
var b = 0
var gaps = []
common_sections
// make this consider the tail gap...
.concat({
A: A.length,
B: B.length,
length: 0,
})
.forEach(function(e){
// store the gap...
a != e.A && b != e.B
&& gaps.push([
[a, A.slice(a, e.A)],
[b: B.slice(b, e.B)],
])
// go to next gap...
a = e.A + e.length
b = e.B + e.length
})
// XXX diff the gaps... // XXX diff the gaps...
// XXX res.items = getDiffSections(A, B, cmp)
.map(function(gap){
var a = gap[0][1]
var b = gap[1][1]
var i = gap[0][0]
var j = gap[1][0]
return a
.map(function(e, n){
return {
A: i+n,
B: j+n,
diff: cacheDiff(e, b.length > n ? b[n] : NONE)
} })
.concat(b.slice(a.length)
.map(function(e, n){
return {
A: a.length + i+n,
B: a.length + j+n,
diff: cacheDiff(a.length > n ? a[n] : NONE, e)
} }))
})
/* XXX
// indexed items... // indexed items...
_diff_items(res, A, B, options, _diff_items(cacheDiff, res, A, B, options,
function(e){ return e == 0 || !!(e*1) }) function(e){ return e == 0 || !!(e*1) })
// attributes... // attributes...
// XXX make this more configurable... (order needs to be optional in JSON) // XXX make this more configurable... (order needs to be optional in JSON)
options.mode != 'JSON' options.mode != 'JSON'
&& _diff_items(res, A, B, options, && _diff_items(cacheDiff, res, A, B, options,
function(e){ return !(e == 0 || !!(e*1)) }) function(e){ return !(e == 0 || !!(e*1)) })
// attributes order... // attributes order...
&& _diff_item_order(res, A, B, options, && _diff_item_order(cacheDiff, res, A, B, options,
function(e){ return !(e == 0 || !!(e*1)) }) function(e){ return !(e == 0 || !!(e*1)) })
//*/
return (res.items || []).length > 0 ? res : null return (res.items || []).length > 0 ? res : null
@ -323,11 +371,11 @@ function(A, B, options){
type: 'Object', type: 'Object',
} }
_diff_items(res, A, B, options) _diff_items(cacheDiff, res, A, B, options)
// XXX this should be applicable to JSON too... // XXX this should be applicable to JSON too...
options.mode != 'JSON' options.mode != 'JSON'
&& _diff_item_order(res, A, B, options) && _diff_item_order(cacheDiff, res, A, B, options)
// .constructor... // .constructor...
if(options.mode != 'JSON'){ if(options.mode != 'JSON'){