Be memory efficient and clear hunk data after displaying it
[vng.git] / hunks / Hunk.cpp
blob2c586c2a530580f93f1a89466ad9e9c82af030f2
1 /*
2 * This file is part of the vng project
3 * Copyright (C) 2008 Thomas Zander <tzander@trolltech.com>
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "Hunk.h"
21 #include <QDebug>
22 #include <QRegExp>
24 class SubHunk {
25 public:
26 SubHunk() : start(-1), length(0), accepted(Vng::Undecided), added(0), removed(0) { }
27 int start, length;
28 Vng::Acceptance accepted;
29 int added, removed;
30 int lineNumber;
33 class Hunk::Private {
34 public:
35 Private() : lineNumber(-1), ref(1), linesBefore(0), linesAfter(0)
37 SubHunk *sh = new SubHunk();
38 subhunks.append(sh);
41 ~Private() {
42 qDeleteAll(subhunks);
44 QByteArray header;
45 int lineNumber;
46 QByteArray diff;
47 QAtomicInt ref;
48 int linesBefore, linesAfter;
50 QList<SubHunk *> subhunks;
52 QByteArray patchForAccepted(bool inverse);
55 Hunk::Hunk()
56 : d(new Private())
60 Hunk::Hunk(const Hunk &other)
61 : d(other.d)
63 d->ref.ref();
66 Hunk::~Hunk()
68 if (!d->ref.deref())
69 delete d;
72 void Hunk::addLine(const QByteArray &line)
74 if (line.size() == 0)
75 return;
76 if(d->header.isEmpty()) {
77 d->header = line;
78 QString header(line);
79 QRegExp regexp("^@@ -[\\d,]+ \\+(\\d+)\\b");
80 if (regexp.indexIn(header) != -1)
81 d->lineNumber = regexp.cap(1).toInt();
82 return;
84 if (line[0] == '\\') { // "\ No newline at end of file"
85 if (d->diff.size()) {
86 // check if we removed something from the last hunk and adjust its size
87 SubHunk *last = d->subhunks[d->subhunks.count()-1];
88 if (last->start + last->length == d->diff.size())
89 --last->length;
90 d->diff.chop(1); // TODO should that be 2 on Windows?
92 return;
94 else if (line[0] != ' ') {
95 SubHunk *last = d->subhunks[d->subhunks.count()-1];
96 if (last->start < 0) {
97 last->start = d->diff.size();
98 last->lineNumber = d->lineNumber + qMax(d->linesBefore, d->linesAfter);
100 else if (last->start + last->length < d->diff.size()) {
101 last = new SubHunk();
102 d->subhunks.append(last);
103 last->start = d->diff.size();
104 last->lineNumber = d->lineNumber + qMax(d->linesBefore, d->linesAfter);
106 last->length+=line.length();
107 if (line[0] == '-') {
108 last->removed++;
109 d->linesBefore++;
111 else {
112 last->added++;
113 d->linesAfter++;
116 else {
117 d->linesBefore++;
118 d->linesAfter++;
120 d->diff.append(line);
123 Hunk & Hunk::operator=(const Hunk &other)
125 other.d->ref.ref();
126 if (! d->ref.deref())
127 delete d;
128 d = other.d;
129 return *this;
132 int Hunk::lineNumber() const
134 return d->lineNumber;
137 int Hunk::lineNumber(int subHunk) const
139 Q_ASSERT(subHunk < d->subhunks.count());
140 SubHunk *sh = d->subhunks[subHunk];
141 return sh->lineNumber;
144 QByteArray Hunk::patch() const
146 return d->diff;
149 bool Hunk::isEmpty() const
151 return d->diff.isEmpty();
154 Vng::Acceptance Hunk::acceptance() const
156 Vng::Acceptance a = Vng::Undecided;
157 for (int i = 0; i < d->subhunks.count(); i++) {
158 SubHunk *sh = d->subhunks[i];
159 if (a != sh->accepted) {
160 if (a == Vng::Undecided)
161 a = sh->accepted;
162 else
163 return Vng::MixedAcceptance;
166 return a;
169 void Hunk::setAcceptance(Vng::Acceptance acceptance)
171 foreach(SubHunk *sh, d->subhunks)
172 sh->accepted = acceptance;
175 Vng::Acceptance Hunk::acceptance(int subHunk) const
177 Q_ASSERT(subHunk < d->subhunks.count());
178 SubHunk *sh = d->subhunks[subHunk];
179 return sh->accepted;
182 void Hunk::setAcceptance(int subhunk, Vng::Acceptance accepted)
184 Q_ASSERT(subhunk < d->subhunks.count());
185 SubHunk *sh = d->subhunks[subhunk];
186 sh->accepted = accepted;
189 QByteArray Hunk::header() const
191 return d->header;
194 QByteArray Hunk::subHunk(int index) const
196 if (d->subhunks.count() == 0)
197 return QByteArray();
198 Q_ASSERT(index >= 0);
199 Q_ASSERT(index < d->subhunks.count());
200 SubHunk *sh = d->subhunks[index];
201 return d->diff.mid(sh->start, sh->length);
204 int Hunk::subHunkCount() const
206 return d->subhunks.count();
209 QByteArray Hunk::rejectedPatch() const
211 return d->patchForAccepted(true);
214 QByteArray Hunk::acceptedPatch() const
216 return d->patchForAccepted(false);
219 QByteArray Hunk::Private::patchForAccepted(bool inverse)
221 QByteArray patch;
222 QDataStream out (&patch, QIODevice::WriteOnly);
223 const char *myDiff = diff.data();
224 const char *space = " ";
225 int offsetInDiff = 0; // position in diff
226 int before = linesBefore; // for the header
227 foreach(SubHunk *sh, subhunks) {
228 out.writeRawData(myDiff + offsetInDiff, sh->start - offsetInDiff); // write until start
229 if (!inverse && sh->accepted == Vng::Accepted || inverse && sh->accepted != Vng::Accepted) {
230 out.writeRawData(myDiff + sh->start, sh->length);
232 else {
233 const int end = sh->start + sh->length;
234 bool startOfLine = true;
235 bool skipLine;
236 int lastLineStart = 0;
237 for (int x = sh->start; x < end; x++) {
238 if (startOfLine) {
239 startOfLine = false;
240 skipLine = myDiff[x] == '-';
241 if (! skipLine) {
242 out.writeRawData(space, 1);
243 lastLineStart = x + 1;
245 continue;
247 if (myDiff[x] == '\n') {
248 startOfLine = true;
249 if (! skipLine)
250 out.writeRawData(myDiff + lastLineStart, x - lastLineStart + 1);
253 before += sh->added - sh->removed;
255 offsetInDiff = sh->start + sh->length;
257 Q_ASSERT(offsetInDiff <= diff.size());
258 out.writeRawData(myDiff + offsetInDiff, diff.size() - offsetInDiff);
259 if (patch[patch.size() -1] != '\n') {
260 const char *noNewLine = "\n\\ No newline at end of file\n";;
261 out.writeRawData(noNewLine, strlen(noNewLine));
264 // create header
265 QString header = "@@ -"+ QString::number(lineNumber) +","+ QString::number(before)
266 +" +"+ QString::number(lineNumber) +","+ QString::number(linesAfter) + " @@\n";
268 QByteArray answer;
269 answer.append(header);
270 answer.append(patch);
271 return answer;
274 int Hunk::linesAdded() const
276 int total = 0;
277 foreach(SubHunk *sh, d->subhunks)
278 total += sh->added;
279 return total;
282 int Hunk::linesRemoved() const
284 int total = 0;
285 foreach(SubHunk *sh, d->subhunks)
286 total += sh->removed;
287 return total;