forked from BenLubar/bindiff
/
diff.go
129 lines (111 loc) · 2.99 KB
/
diff.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Package bindiff provides a bidirectional binary patch for pairs of []byte.
package bindiff
import (
"encoding/binary"
"errors"
"github.com/mb0/diff"
)
// ErrCorrupt is the only possible error from functions in this package.
var ErrCorrupt = errors.New("bindiff: corrupt patch")
// Diff computes the difference between old and new. A granularity of 1 or more
// combines changes with no greater than that many bytes between them.
func Diff(old, new []byte, granularity int) (patch []byte) {
changes := diff.Bytes(old, new)
if granularity > 0 {
changes = diff.Granular(granularity, changes)
}
for i, c := range changes {
a, b := c.A, c.B
for _, prev := range changes[:i] {
if prev.A < c.A {
a -= prev.Del
a += prev.Ins
}
if prev.B < c.B {
b -= prev.Ins
b += prev.Del
}
}
patch = writeUvarint(patch, a)
patch = writeUvarint(patch, b)
patch = writeUvarint(patch, c.Del)
patch = append(patch, old[c.A:c.A+c.Del]...)
patch = writeUvarint(patch, c.Ins)
patch = append(patch, new[c.B:c.B+c.Ins]...)
}
return
}
// Forward retrieves the second argument to Diff given the first argument and its output.
func Forward(old, patch []byte) (new []byte, err error) {
return doPatch(old, patch, func(data []byte, a, b int, del, ins []byte) ([]byte, error) {
return splice(data, a, len(del), ins)
})
}
// Reverse retrieves the first argument to Diff given the second argument and its output.
func Reverse(new, patch []byte) (old []byte, err error) {
return doPatch(new, patch, func(data []byte, a, b int, del, ins []byte) ([]byte, error) {
return splice(data, b, len(ins), del)
})
}
func doPatch(x, patch []byte, f func(data []byte, a, b int, del, ins []byte) ([]byte, error)) (y []byte, err error) {
y = make([]byte, len(x))
copy(y, x)
for len(patch) > 0 {
var a, b, l int
var del, ins []byte
a, patch, err = readUvarint(patch)
if err != nil {
return
}
b, patch, err = readUvarint(patch)
if err != nil {
return
}
l, patch, err = readUvarint(patch)
if err != nil {
return
}
if len(patch) < l {
err = ErrCorrupt
return
}
del, patch = patch[:l], patch[l:]
l, patch, err = readUvarint(patch)
if err != nil {
return
}
if len(patch) < l {
err = ErrCorrupt
return
}
ins, patch = patch[:l], patch[l:]
y, err = f(y, a, b, del, ins)
if err != nil {
return
}
}
return
}
func splice(base []byte, anchor, del int, ins []byte) ([]byte, error) {
if anchor+del > len(base) {
return base, ErrCorrupt
}
l := len(base)
if del < len(ins) {
base = append(base, make([]byte, len(ins)-del)...)
}
copy(base[anchor+len(ins):], base[anchor+del:])
copy(base[anchor:], ins)
return base[:l+len(ins)-del], nil
}
func writeUvarint(buf []byte, i int) []byte {
var varint [binary.MaxVarintLen64]byte
return append(buf, varint[:binary.PutUvarint(varint[:], uint64(i))]...)
}
func readUvarint(buf []byte) (int, []byte, error) {
i, n := binary.Uvarint(buf)
if n == 0 {
return 0, buf, ErrCorrupt
}
return int(i), buf[n:], nil
}