5
5
"strings"
6
6
)
7
7
8
- type differ struct {
8
+ // A Differ is a JSON Patch generator.
9
+ // The zero value is an empty generator ready to use.
10
+ type Differ struct {
9
11
patch Patch
10
12
hasher hasher
11
13
hashmap map [uint64 ]jsonNode
@@ -20,14 +22,43 @@ type options struct {
20
22
equivalent bool
21
23
}
22
24
23
- func (d * differ ) diff (src , tgt interface {}) {
25
+ // Patch returns the list of JSON patch operations
26
+ // generated by the Differ. The patch is valid for usage
27
+ // until the next comparison or reset.
28
+ func (d * Differ ) Patch () Patch {
29
+ return d .patch
30
+ }
31
+
32
+ // WithOpts applies the given options to the Differ
33
+ // and returns it to allow chained calls.
34
+ func (d * Differ ) WithOpts (opts ... Option ) * Differ {
35
+ for _ , o := range opts {
36
+ o (d )
37
+ }
38
+ return d
39
+ }
40
+
41
+ // Reset resets the Differ to be empty, but it retains the
42
+ // underlying storage for use by future comparisons.
43
+ func (d * Differ ) Reset () {
44
+ d .patch = d .patch [:0 ]
45
+
46
+ // Optimized map clear.
47
+ for k := range d .hashmap {
48
+ delete (d .hashmap , k )
49
+ }
50
+ }
51
+
52
+ // Compare computes the differences between src and tgt as
53
+ // a series of JSON Patch operations.
54
+ func (d * Differ ) Compare (src , tgt interface {}) {
24
55
if d .opts .factorize {
25
56
d .prepare (emptyPtr , src , tgt )
26
57
}
27
- d .compare (emptyPtr , src , tgt )
58
+ d .diff (emptyPtr , src , tgt )
28
59
}
29
60
30
- func (d * differ ) compare (ptr pointer , src , tgt interface {}) {
61
+ func (d * Differ ) diff (ptr pointer , src , tgt interface {}) {
31
62
if src == nil && tgt == nil {
32
63
return
33
64
}
@@ -70,7 +101,7 @@ func (d *differ) compare(ptr pointer, src, tgt interface{}) {
70
101
}
71
102
}
72
103
73
- func (d * differ ) prepare (ptr pointer , src , tgt interface {}) {
104
+ func (d * Differ ) prepare (ptr pointer , src , tgt interface {}) {
74
105
if src == nil && tgt == nil {
75
106
return
76
107
}
@@ -110,7 +141,7 @@ func (d *differ) prepare(ptr pointer, src, tgt interface{}) {
110
141
}
111
142
}
112
143
113
- func (d * differ ) rationalizeLastOps (ptr pointer , src , tgt interface {}, lastOpIdx int ) {
144
+ func (d * Differ ) rationalizeLastOps (ptr pointer , src , tgt interface {}, lastOpIdx int ) {
114
145
newOps := make (Patch , 0 , 2 )
115
146
116
147
if d .opts .invertible {
@@ -140,23 +171,30 @@ func (d *differ) rationalizeLastOps(ptr pointer, src, tgt interface{}, lastOpIdx
140
171
141
172
// compareObjects generates the patch operations that
142
173
// represents the differences between two JSON objects.
143
- func (d * differ ) compareObjects (ptr pointer , src , tgt map [string ]interface {}) {
144
- cmpSet := make ( map [string ]uint8 )
174
+ func (d * Differ ) compareObjects (ptr pointer , src , tgt map [string ]interface {}) {
175
+ cmpSet := map [string ]uint8 {}
145
176
146
177
for k := range src {
147
178
cmpSet [k ] |= 1 << 0
148
179
}
149
180
for k := range tgt {
150
181
cmpSet [k ] |= 1 << 1
151
182
}
152
- for _ , k := range sortedObjectKeys (cmpSet ) {
183
+ keys := make ([]string , 0 , len (cmpSet ))
184
+
185
+ for k := range cmpSet {
186
+ keys = append (keys , k )
187
+ }
188
+ sortStrings (keys )
189
+
190
+ for _ , k := range keys {
153
191
v := cmpSet [k ]
154
192
inOld := v & (1 << 0 ) != 0
155
193
inNew := v & (1 << 1 ) != 0
156
194
157
195
switch {
158
196
case inOld && inNew :
159
- d .compare (ptr .appendKey (k ), src [k ], tgt [k ])
197
+ d .diff (ptr .appendKey (k ), src [k ], tgt [k ])
160
198
case inOld && ! inNew :
161
199
d .remove (ptr .appendKey (k ), src [k ])
162
200
case ! inOld && inNew :
@@ -167,7 +205,7 @@ func (d *differ) compareObjects(ptr pointer, src, tgt map[string]interface{}) {
167
205
168
206
// compareArrays generates the patch operations that
169
207
// represents the differences between two JSON arrays.
170
- func (d * differ ) compareArrays (ptr pointer , src , tgt []interface {}) {
208
+ func (d * Differ ) compareArrays (ptr pointer , src , tgt []interface {}) {
171
209
size := min (len (src ), len (tgt ))
172
210
173
211
// When the source array contains more elements
@@ -183,7 +221,7 @@ func (d *differ) compareArrays(ptr pointer, src, tgt []interface{}) {
183
221
// Compare the elements at each index present in
184
222
// both the source and destination arrays.
185
223
for i := 0 ; i < size ; i ++ {
186
- d .compare (ptr .appendIndex (i ), src [i ], tgt [i ])
224
+ d .diff (ptr .appendIndex (i ), src [i ], tgt [i ])
187
225
}
188
226
next:
189
227
// When the target array contains more elements
@@ -194,7 +232,7 @@ next:
194
232
}
195
233
}
196
234
197
- func (d * differ ) unorderedDeepEqualSlice (src , tgt []interface {}) bool {
235
+ func (d * Differ ) unorderedDeepEqualSlice (src , tgt []interface {}) bool {
198
236
if len (src ) != len (tgt ) {
199
237
return false
200
238
}
@@ -206,7 +244,7 @@ func (d *differ) unorderedDeepEqualSlice(src, tgt []interface{}) bool {
206
244
}
207
245
for _ , v := range tgt {
208
246
k := d .hasher .digest (v )
209
- // If the digest hash if not in the diff ,
247
+ // If the digest hash if not in the Compare ,
210
248
// return early.
211
249
if _ , ok := diff [k ]; ! ok {
212
250
return false
@@ -219,7 +257,7 @@ func (d *differ) unorderedDeepEqualSlice(src, tgt []interface{}) bool {
219
257
return len (diff ) == 0
220
258
}
221
259
222
- func (d * differ ) add (ptr pointer , v interface {}) {
260
+ func (d * Differ ) add (ptr pointer , v interface {}) {
223
261
if ! d .opts .factorize {
224
262
d .patch = d .patch .append (OperationAdd , emptyPtr , ptr , nil , v )
225
263
return
@@ -246,21 +284,21 @@ func (d *differ) add(ptr pointer, v interface{}) {
246
284
}
247
285
}
248
286
249
- func (d * differ ) replace (ptr pointer , src , tgt interface {}) {
287
+ func (d * Differ ) replace (ptr pointer , src , tgt interface {}) {
250
288
if d .opts .invertible {
251
289
d .patch = d .patch .append (OperationTest , emptyPtr , ptr , nil , src )
252
290
}
253
291
d .patch = d .patch .append (OperationReplace , emptyPtr , ptr , src , tgt )
254
292
}
255
293
256
- func (d * differ ) remove (ptr pointer , v interface {}) {
294
+ func (d * Differ ) remove (ptr pointer , v interface {}) {
257
295
if d .opts .invertible {
258
296
d .patch = d .patch .append (OperationTest , emptyPtr , ptr , nil , v )
259
297
}
260
298
d .patch = d .patch .append (OperationRemove , emptyPtr , ptr , v , nil )
261
299
}
262
300
263
- func (d * differ ) findUnchanged (v interface {}) pointer {
301
+ func (d * Differ ) findUnchanged (v interface {}) pointer {
264
302
if d .hashmap != nil {
265
303
k := d .hasher .digest (v )
266
304
node , ok := d .hashmap [k ]
@@ -271,7 +309,7 @@ func (d *differ) findUnchanged(v interface{}) pointer {
271
309
return emptyPtr
272
310
}
273
311
274
- func (d * differ ) findRemoved (v interface {}) int {
312
+ func (d * Differ ) findRemoved (v interface {}) int {
275
313
for i := 0 ; i < len (d .patch ); i ++ {
276
314
op := d .patch [i ]
277
315
if op .Type == OperationRemove && deepEqual (op .OldValue , v ) {
@@ -281,21 +319,34 @@ func (d *differ) findRemoved(v interface{}) int {
281
319
return - 1
282
320
}
283
321
284
- func (d * differ ) applyOpts (opts ... Option ) {
322
+ func (d * Differ ) applyOpts (opts ... Option ) {
285
323
for _ , opt := range opts {
286
324
if opt != nil {
287
325
opt (d )
288
326
}
289
327
}
290
328
}
291
329
292
- func sortedObjectKeys (m map [string ]uint8 ) []string {
293
- keys := make ([]string , 0 , len (m ))
294
- for k := range m {
295
- keys = append (keys , k )
330
+ func sortStrings (v []string ) {
331
+ if len (v ) < 20 {
332
+ insertionSort (v )
333
+ } else {
334
+ sort .Strings (v )
335
+ }
336
+ }
337
+
338
+ func insertionSort (v []string ) {
339
+ for j := 1 ; j < len (v ); j ++ {
340
+ // Invariant: v[:j] contains the same elements as
341
+ // the original slice v[:j], but in sorted order.
342
+ key := v [j ]
343
+ i := j - 1
344
+ for i >= 0 && v [i ] > key {
345
+ v [i + 1 ] = v [i ]
346
+ i --
347
+ }
348
+ v [i + 1 ] = key
296
349
}
297
- sort .Strings (keys )
298
- return keys
299
350
}
300
351
301
352
func min (i , j int ) int {
0 commit comments