Make sure buffer is large enough for the nul terminator
[TortoiseGit.git] / src / TortoiseProc / lanes.cpp
blob2e35a806a46b72d8e2d2e8bdd19d785e4e5dd724
1 /*
2 Description: history graph computation
4 Author: Marco Costalba (C) 2005-2007
5 Copyright (C) 2008-2015 - TortoiseGit
7 Copyright: See COPYING file that comes with this distribution
9 */
10 #include "stdafx.h"
11 #include "lanes.h"
13 #define IS_NODE(x) (x == NODE || x == NODE_R || x == NODE_L)
16 void Lanes::init(const CGitHash& expectedSha) {
18 clear();
19 activeLane = 0;
20 setBoundary(false);
21 bool wasEmptyCross = false;
22 add(BRANCH, expectedSha, activeLane, wasEmptyCross);
25 void Lanes::clear() {
27 typeVec.clear();
28 nextShaVec.clear();
31 void Lanes::setBoundary(bool b) {
32 // changes the state so must be called as first one
34 NODE = b ? BOUNDARY_C : MERGE_FORK;
35 NODE_R = b ? BOUNDARY_R : MERGE_FORK_R;
36 NODE_L = b ? BOUNDARY_L : MERGE_FORK_L;
37 boundary = b;
39 if (boundary)
40 typeVec[activeLane] = BOUNDARY;
43 bool Lanes::isFork(const CGitHash& sha, bool& isDiscontinuity) {
45 int pos = findNextSha(sha, 0);
46 isDiscontinuity = (activeLane != pos);
47 if (pos == -1) // new branch case
48 return false;
50 return (findNextSha(sha, pos + 1) != -1);
52 int cnt = 0;
53 while (pos != -1) {
54 cnt++;
55 pos = findNextSha(sha, pos + 1);
56 // if (isDiscontinuity)
57 // isDiscontinuity = (activeLane != pos);
59 return (cnt > 1);
63 void Lanes::setFork(const CGitHash& sha) {
65 int rangeStart, rangeEnd, idx;
66 rangeStart = rangeEnd = idx = findNextSha(sha, 0);
68 while (idx != -1) {
69 rangeEnd = idx;
70 typeVec[idx] = TAIL;
71 idx = findNextSha(sha, idx + 1);
73 typeVec[activeLane] = NODE;
75 int& startT = typeVec[rangeStart];
76 int& endT = typeVec[rangeEnd];
78 if (startT == NODE)
79 startT = NODE_L;
81 if (endT == NODE)
82 endT = NODE_R;
84 if (startT == TAIL)
85 startT = TAIL_L;
87 if (endT == TAIL)
88 endT = TAIL_R;
90 for (int i = rangeStart + 1; i < rangeEnd; ++i) {
92 int& t = typeVec[i];
94 if (t == NOT_ACTIVE)
95 t = CROSS;
97 else if (t == EMPTY)
98 t = CROSS_EMPTY;
102 void Lanes::setMerge(const CGitHashList& parents) {
103 // setFork() must be called before setMerge()
105 if (boundary)
106 return; // handle as a simple active line
108 int& t = typeVec[activeLane];
109 bool wasFork = (t == NODE);
110 bool wasFork_L = (t == NODE_L);
111 bool wasFork_R = (t == NODE_R);
112 bool startJoinWasACross = false, endJoinWasACross = false;
113 bool endWasEmptyCross = false;
115 t = NODE;
117 int rangeStart = activeLane, rangeEnd = activeLane;
118 CGitHashList::const_iterator it(parents.begin());
119 for (++it; it != parents.end(); ++it) { // skip first parent
121 int idx = findNextSha(*it, 0);
122 if (idx != -1) {
124 if (idx > rangeEnd) {
126 rangeEnd = idx;
127 endJoinWasACross = typeVec[idx] == CROSS;
130 if (idx < rangeStart) {
132 rangeStart = idx;
133 startJoinWasACross = typeVec[idx] == CROSS;
136 typeVec[idx] = JOIN;
138 else
139 rangeEnd = add(HEAD, *it, rangeEnd + 1, endWasEmptyCross);
141 int& startT = typeVec[rangeStart];
142 int& endT = typeVec[rangeEnd];
144 if (startT == NODE && !wasFork && !wasFork_R)
145 startT = NODE_L;
147 if (endT == NODE && !wasFork && !wasFork_L)
148 endT = NODE_R;
150 if (startT == JOIN && !startJoinWasACross)
151 startT = JOIN_L;
153 if (endT == JOIN && !endJoinWasACross)
154 endT = JOIN_R;
156 if (startT == HEAD)
157 startT = HEAD_L;
159 if (endT == HEAD && !endWasEmptyCross)
160 endT = HEAD_R;
162 for (int i = rangeStart + 1; i < rangeEnd; ++i) {
164 int& t2 = typeVec[i];
166 if (t2 == NOT_ACTIVE)
167 t2 = CROSS;
169 else if (t2 == EMPTY)
170 t2 = CROSS_EMPTY;
172 else if (t2 == TAIL_R || t2 == TAIL_L)
173 t2 = TAIL;
177 void Lanes::setInitial() {
179 int& t = typeVec[activeLane];
180 if (!IS_NODE(t) && t != APPLIED)
181 t = (boundary ? BOUNDARY : INITIAL);
184 void Lanes::setApplied() {
186 // applied patches are not merges, nor forks
187 typeVec[activeLane] = APPLIED; // TODO test with boundaries
190 void Lanes::changeActiveLane(const CGitHash& sha) {
192 int& t = typeVec[activeLane];
193 if (t == INITIAL || isBoundary(t))
194 t = EMPTY;
195 else
196 t = NOT_ACTIVE;
198 int idx = findNextSha(sha, 0); // find first sha
199 if (idx != -1)
200 typeVec[idx] = ACTIVE; // called before setBoundary()
201 else {
202 bool wasEmptyCross = false;
203 idx = add(BRANCH, sha, activeLane, wasEmptyCross); // new branch
206 activeLane = idx;
209 void Lanes::afterMerge() {
211 if (boundary)
212 return; // will be reset by changeActiveLane()
214 for (unsigned int i = 0; i < typeVec.size(); ++i) {
216 int& t = typeVec[i];
218 if (isHead(t) || isJoin(t) || t == CROSS)
219 t = NOT_ACTIVE;
221 else if (t == CROSS_EMPTY)
222 t = EMPTY;
224 else if (IS_NODE(t))
225 t = ACTIVE;
229 void Lanes::afterFork() {
231 for (unsigned int i = 0; i < typeVec.size(); ++i) {
233 int& t = typeVec[i];
235 if (t == CROSS)
236 t = NOT_ACTIVE;
238 else if (isTail(t) || t == CROSS_EMPTY)
239 t = EMPTY;
241 if (!boundary && IS_NODE(t))
242 t = ACTIVE; // boundary will be reset by changeActiveLane()
244 while (typeVec.back() == EMPTY) {
245 typeVec.pop_back();
246 nextShaVec.pop_back();
250 bool Lanes::isBranch() {
252 return (typeVec[activeLane] == BRANCH);
255 void Lanes::afterBranch() {
257 typeVec[activeLane] = ACTIVE; // TODO test with boundaries
260 void Lanes::afterApplied() {
262 typeVec[activeLane] = ACTIVE; // TODO test with boundaries
265 void Lanes::nextParent(const CGitHash& sha) {
267 if(boundary)
268 nextShaVec[activeLane].Empty();
269 else
270 nextShaVec[activeLane] = sha;
273 int Lanes::findNextSha(const CGitHash& next, int pos) {
275 for (unsigned int i = pos; i < nextShaVec.size(); ++i)
276 if (nextShaVec[i] == next)
277 return i;
278 return -1;
281 int Lanes::findType(int type, int pos) {
283 for (unsigned int i = pos; i < typeVec.size(); ++i)
284 if (typeVec[i] == type)
285 return i;
286 return -1;
289 int Lanes::add(int type, const CGitHash& next, int pos, bool& wasEmptyCross) {
291 wasEmptyCross = false;
292 // first check empty lanes starting from pos
293 if (pos < (int)typeVec.size()) {
294 int posEmpty = findType(EMPTY, pos);
295 int posCrossEmpty = findType(CROSS_EMPTY, pos);
296 // Use first "empty" position.
297 if (posEmpty != -1 && posCrossEmpty != -1)
298 pos = min(posEmpty, posCrossEmpty);
299 else if (posEmpty != -1)
300 pos = posEmpty;
301 else if (posCrossEmpty != -1)
302 pos = posCrossEmpty;
303 else
304 pos = -1;
306 if (pos != -1) {
307 wasEmptyCross = (pos == posCrossEmpty);
309 typeVec[pos] = type;
310 nextShaVec[pos] = next;
311 return pos;
314 // if all lanes are occupied add a new lane
315 typeVec.push_back(type);
316 nextShaVec.push_back(next);
317 return (int)typeVec.size() - 1;