-
Notifications
You must be signed in to change notification settings - Fork 365
/
Copy pathcombined_diff_iterator.go
152 lines (139 loc) · 4.26 KB
/
combined_diff_iterator.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package graveler
import "bytes"
// CombinedDiffIterator calculates the diff between a commit and a branch, including the staging area of the branch.
// committedDiffIterator is the DiffIterator between the commit and the HEAD of the branch.
// stagingIterator is the ValueIterator on the staging area of the branch
// leftIterator is the ValueIterator on the commit
type CombinedDiffIterator struct {
started bool
committedDiffIterator DiffIterator
leftIterator ValueIterator
stagingIterator ValueIterator
committedDiff *Diff
stagingValue *ValueRecord
val *Diff
err error
}
func NewCombinedDiffIterator(committedDiffIterator DiffIterator, leftIterator ValueIterator, stagingIterator ValueIterator) *CombinedDiffIterator {
return &CombinedDiffIterator{committedDiffIterator: committedDiffIterator, leftIterator: leftIterator, stagingIterator: stagingIterator}
}
func (c *CombinedDiffIterator) loadNextStagingValue() {
c.stagingValue = nil
if c.stagingIterator.Next() {
c.stagingValue = c.stagingIterator.Value()
} else if c.stagingIterator.Err() != nil {
c.err = c.stagingIterator.Err()
}
}
func (c *CombinedDiffIterator) loadNextCommittedDiff() {
c.committedDiff = nil
if c.committedDiffIterator.Next() {
c.committedDiff = c.committedDiffIterator.Value()
} else if c.committedDiffIterator.Err() != nil {
c.err = c.committedDiffIterator.Err()
}
}
func (c *CombinedDiffIterator) Next() bool {
if !c.started {
c.started = true
c.loadNextStagingValue()
c.loadNextCommittedDiff()
}
for c.committedDiff != nil || c.stagingValue != nil {
if c.err != nil {
return false
}
if c.stagingValue == nil {
// nothing on staging - return the original diff
c.val = c.committedDiff
c.loadNextCommittedDiff()
return true
}
committedStagingCompareResult := 1 // for the case where committedDiff == nil
if c.committedDiff != nil {
committedStagingCompareResult = bytes.Compare(c.committedDiff.Key, c.stagingValue.Key)
}
if committedStagingCompareResult < 0 {
// nothing on staging - return the original diff
c.val = c.committedDiff
c.loadNextCommittedDiff()
return true
}
// something on staging
compareStagingResult := c.compareStagingWithLeft()
c.loadNextStagingValue()
if committedStagingCompareResult == 0 {
// both sides had values - need to advance committed iterator
c.loadNextCommittedDiff()
}
if compareStagingResult {
return true
}
}
return false
}
// compareStagingWithLeft checks if there is a diff between the left side to the staging area.
// For the last key fetched from the staging iterator, it checks the left-side iterator for the value of the same key.
// If there is a diff between them, it sets the diff and returns true. Otherwise, false is returned.
func (c *CombinedDiffIterator) compareStagingWithLeft() bool {
c.leftIterator.SeekGE(c.stagingValue.Key)
var leftVal *ValueRecord
if c.leftIterator.Next() {
leftVal = c.leftIterator.Value()
if !bytes.Equal(leftVal.Key, c.stagingValue.Key) {
// key wasn't on left side
leftVal = nil
}
} else if c.leftIterator.Err() != nil {
c.err = c.leftIterator.Err()
return false
}
var typ DiffType
var leftIdentity []byte
value := c.stagingValue.Value
switch {
case leftVal == nil:
if c.stagingValue.IsTombstone() {
// not on left, deleted on staging - no diff
return false
}
// not on left, but is on staging
typ = DiffTypeAdded
case c.stagingValue.IsTombstone():
// found on left, deleted on staging
leftIdentity = leftVal.Identity
value = leftVal.Value
typ = DiffTypeRemoved
default:
// found on both sides
leftIdentity = leftVal.Identity
if bytes.Equal(c.stagingValue.Identity, leftVal.Identity) {
// identity not changed - no diff
return false
}
typ = DiffTypeChanged
}
c.val = &Diff{
Type: typ,
Key: c.stagingValue.Key,
Value: value,
LeftIdentity: leftIdentity,
}
return true
}
func (c *CombinedDiffIterator) SeekGE(id Key) {
c.started = false
c.committedDiffIterator.SeekGE(id)
c.stagingIterator.SeekGE(id)
}
func (c *CombinedDiffIterator) Value() *Diff {
return c.val
}
func (c *CombinedDiffIterator) Err() error {
return c.err
}
func (c *CombinedDiffIterator) Close() {
c.committedDiffIterator.Close()
c.leftIterator.Close()
c.stagingIterator.Close()
}