1
1
package jsondiff
2
2
3
3
import (
4
- "encoding/json"
5
- "reflect"
6
4
"sort"
7
5
"strings"
8
6
)
9
7
10
8
type differ struct {
11
9
patch Patch
12
10
hasher hasher
13
- hashmap map [uint64 ]* jsonNode
11
+ hashmap map [uint64 ]jsonNode
14
12
factorize bool
15
13
rationalize bool
16
14
invertible bool
15
+ targetBytes []byte
17
16
}
18
17
19
18
func (d * differ ) diff (src , tgt interface {}) {
@@ -24,7 +23,7 @@ func (d *differ) diff(src, tgt interface{}) {
24
23
}
25
24
26
25
func (d * differ ) compare (ptr pointer , src , tgt interface {}) {
27
- if reflect . DeepEqual ( src , tgt ) {
26
+ if src == nil && tgt == nil {
28
27
return
29
28
}
30
29
if ! areComparable (src , tgt ) {
@@ -40,6 +39,9 @@ func (d *differ) compare(ptr pointer, src, tgt interface{}) {
40
39
}
41
40
return
42
41
}
42
+ if deepValueEqual (src , tgt , typeSwitchKind (src )) {
43
+ return
44
+ }
43
45
size := len (d .patch )
44
46
45
47
// Values are comparable, but are not
@@ -52,8 +54,10 @@ func (d *differ) compare(ptr pointer, src, tgt interface{}) {
52
54
default :
53
55
// Generate a replace operation for
54
56
// scalar types.
55
- d .replace (ptr , src , tgt )
56
- return
57
+ if ! deepValueEqual (src , tgt , typeSwitchKind (src )) {
58
+ d .replace (ptr , src , tgt )
59
+ return
60
+ }
57
61
}
58
62
// Rationalize any new operations.
59
63
if d .rationalize && len (d .patch ) > size {
@@ -67,29 +71,28 @@ func (d *differ) prepare(ptr pointer, src, tgt interface{}) {
67
71
}
68
72
// When both values are deeply equals, save
69
73
// the location indexed by the value hash.
70
- if reflect .DeepEqual (src , tgt ) {
74
+ if ! areComparable (src , tgt ) {
75
+ return
76
+ } else if deepValueEqual (src , tgt , typeSwitchKind (src )) {
71
77
k := d .hasher .digest (tgt )
72
78
if d .hashmap == nil {
73
- d .hashmap = make (map [uint64 ]* jsonNode )
79
+ d .hashmap = make (map [uint64 ]jsonNode )
74
80
}
75
- d .hashmap [k ] = & jsonNode {ptr : ptr , val : tgt }
76
- return
77
- }
78
- if ! areComparable (src , tgt ) {
81
+ d .hashmap [k ] = jsonNode {ptr : ptr , val : tgt }
79
82
return
80
83
}
81
84
// At this point, the source and target values
82
85
// are non-nil and have comparable types.
83
- switch src .(type ) {
86
+ switch vsrc := src .(type ) {
84
87
case []interface {}:
85
- oarr := src .([] interface {})
88
+ oarr := vsrc
86
89
narr := tgt .([]interface {})
87
90
88
91
for i := 0 ; i < min (len (oarr ), len (narr )); i ++ {
89
92
d .prepare (ptr .appendIndex (i ), oarr [i ], narr [i ])
90
93
}
91
94
case map [string ]interface {}:
92
- oobj := src .( map [ string ] interface {})
95
+ oobj := vsrc
93
96
nobj := tgt .(map [string ]interface {})
94
97
95
98
for k , v1 := range oobj {
@@ -103,10 +106,10 @@ func (d *differ) prepare(ptr pointer, src, tgt interface{}) {
103
106
}
104
107
105
108
func (d * differ ) rationalizeLastOps (ptr pointer , src , tgt interface {}, lastOpIdx int ) {
106
- ops := make (Patch , 0 , 2 )
109
+ newOps := make (Patch , 0 , 2 )
107
110
108
111
if d .invertible {
109
- ops = ops .append (OperationTest , emptyPtr , ptr , nil , src )
112
+ newOps = newOps .append (OperationTest , emptyPtr , ptr , nil , src )
110
113
}
111
114
// replaceOp represents a single operation that
112
115
// replace the source document with the target.
@@ -115,17 +118,18 @@ func (d *differ) rationalizeLastOps(ptr pointer, src, tgt interface{}, lastOpIdx
115
118
Path : ptr ,
116
119
Value : tgt ,
117
120
}
118
- ops = append (ops , replaceOp )
121
+ newOps = append (newOps , replaceOp )
122
+ curOps := d .patch [lastOpIdx :]
119
123
120
- b2 , _ := json . Marshal ( replaceOp )
121
- b1 , _ := json . Marshal (d .patch [ lastOpIdx :] )
124
+ newLen := replaceOp . jsonLength ( d . targetBytes )
125
+ curLen := curOps . jsonLength (d .targetBytes )
122
126
123
- // If one operation is cheapest than many small
127
+ // If one operation is cheaper than many small
124
128
// operations that represents the changes between
125
129
// the two objects, replace the last operations.
126
- if len ( b1 ) > len ( b2 ) + 2 {
130
+ if curLen > newLen {
127
131
d .patch = d .patch [:lastOpIdx ]
128
- d .patch = append (d .patch , ops ... )
132
+ d .patch = append (d .patch , newOps ... )
129
133
}
130
134
}
131
135
@@ -135,10 +139,10 @@ func (d *differ) compareObjects(ptr pointer, src, tgt map[string]interface{}) {
135
139
cmpSet := make (map [string ]uint8 )
136
140
137
141
for k := range src {
138
- cmpSet [k ] |= ( 1 << 0 )
142
+ cmpSet [k ] |= 1 << 0
139
143
}
140
144
for k := range tgt {
141
- cmpSet [k ] |= ( 1 << 1 )
145
+ cmpSet [k ] |= 1 << 1
142
146
}
143
147
for _ , k := range sortedObjectKeys (cmpSet ) {
144
148
v := cmpSet [k ]
@@ -189,32 +193,30 @@ func (d *differ) add(ptr pointer, v interface{}) {
189
193
idx := d .findRemoved (v )
190
194
if idx != - 1 {
191
195
op := d .patch [idx ]
196
+
192
197
// https://tools.ietf.org/html/rfc6902#section-4.4
193
198
// The "from" location MUST NOT be a proper prefix
194
199
// of the "path" location; i.e., a location cannot
195
200
// be moved into one of its children.
196
- if ! strings .HasPrefix (ptr . String ( ), op .Path . String ( )) {
201
+ if ! strings .HasPrefix (string ( ptr ), string ( op .Path )) {
197
202
d .patch = d .patch .remove (idx )
198
203
d .patch = d .patch .append (OperationMove , op .Path , ptr , v , v )
199
204
}
200
205
return
201
206
}
202
207
uptr := d .findUnchanged (v )
203
- if uptr != emptyPtr && ! d .invertible {
208
+ if ! uptr . isRoot () && ! d .invertible {
204
209
d .patch = d .patch .append (OperationCopy , uptr , ptr , nil , v )
205
210
} else {
206
211
d .patch = d .patch .append (OperationAdd , emptyPtr , ptr , nil , v )
207
212
}
208
213
}
209
214
210
- // areComparable returns whether the interfaces values
215
+ // areComparable returns whether the interface values
211
216
// i1 and i2 can be compared. The values are comparable
212
217
// only if they are both non-nil and share the same kind.
213
218
func areComparable (i1 , i2 interface {}) bool {
214
- typ1 := reflect .TypeOf (i1 )
215
- typ2 := reflect .TypeOf (i2 )
216
-
217
- return typ1 != nil && typ2 != nil && typ1 .Kind () == typ2 .Kind ()
219
+ return typeSwitchKind (i1 ) == typeSwitchKind (i2 )
218
220
}
219
221
220
222
func (d * differ ) replace (ptr pointer , src , tgt interface {}) {
@@ -245,7 +247,7 @@ func (d *differ) findUnchanged(v interface{}) pointer {
245
247
func (d * differ ) findRemoved (v interface {}) int {
246
248
for i := 0 ; i < len (d .patch ); i ++ {
247
249
op := d .patch [i ]
248
- if op .Type == OperationRemove && reflect . DeepEqual (op .OldValue , v ) {
250
+ if op .Type == OperationRemove && deepEqual (op .OldValue , v ) {
249
251
return i
250
252
}
251
253
}
0 commit comments