//
// aegis - project change supervisor
// Copyright (C) 2002-2008 Peter Miller
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see
// .
//
#include
#include
#include // for assert
#include
#include
#ifdef DEBUG
#define CHECK(llp) \
assert(llp->start1 <= llp->start2); \
assert(llp->start1 + llp->length1 <= llp->start2); \
assert((llp->length1 != 0) || (llp->start1 == 0)); \
assert(llp->start2 <= llp->maximum); \
assert(llp->start2 + llp->length2 <= llp->maximum); \
assert((llp->length2 != 0) || (llp->start2 == llp->maximum)); \
{ \
size_t mj, mk; \
for (mj = 0; mj < llp->length1; ++mj) \
assert(llp->item[llp->start1 + mj].cp); \
for (mk = 0; mk < llp->length2; ++mk) \
assert(llp->item[llp->start2 + mk].cp); \
}
#else
#define CHECK(llp)
#endif
void
line_list_constructor(line_list_t *llp)
{
trace(("line_list_constructor(llp = %08lX)\n{\n", (long)llp));
llp->maximum = 0;
llp->start1 = 0;
llp->length1 = 0;
llp->start2 = 0;
llp->length2 = 0;
llp->item = 0;
CHECK(llp);
trace(("}\n"));
}
void
line_list_destructor(line_list_t *llp)
{
trace(("line_list_destructor(llp = %08lX)\n{\n", (long)llp));
line_list_clear(llp);
delete [] llp->item;
llp->maximum = 0;
llp->start1 = 0;
llp->length1 = 0;
llp->start2 = 0;
llp->length2 = 0;
llp->item = 0;
trace(("}\n"));
}
void
line_list_clear(line_list_t *llp)
{
size_t j;
trace(("line_list_clear(llp = %08lX)\n{\n", (long)llp));
CHECK(llp);
for (j = 0; j < llp->length1; ++j)
line_destructor(llp->item + llp->start1 + j);
llp->start1 = 0;
llp->length1 = 0;
for (j = 0; j < llp->length2; ++j)
line_destructor(llp->item + llp->start2 + j);
llp->start2 = llp->maximum;
llp->length2 = 0;
CHECK(llp);
trace(("}\n"));
}
void
line_list_delete(line_list_t *llp, size_t first_line, size_t num_lines)
{
trace(("line_list_delete(llp = %08lX, first_line = %ld, "
"num_lines = %ld)\n{\n", (long)llp, (long)first_line, (long)num_lines));
CHECK(llp);
if (num_lines == 0)
first_line = 0;
assert(first_line < llp->length1 + llp->length2);
assert(first_line + num_lines <= llp->length1 + llp->length2);
while (num_lines)
{
size_t second_line;
trace(("llp->start1 = %ld\n", (long)llp->start1));
trace(("llp->length1 = %ld\n", (long)llp->length1));
if (first_line < llp->length1)
{
size_t partial_num_lines;
size_t remainder_num_lines;
size_t j;
//
// Destroy those lines which fall into the first range.
//
trace(("destroy mid range1\n"));
partial_num_lines = num_lines;
if (first_line + num_lines > llp->length1)
partial_num_lines = llp->length1 - first_line;
trace(("partial_num_lines = %ld\n", (long)partial_num_lines));
for (j = 0; j < partial_num_lines; ++j)
line_destructor(llp->item + llp->start1 + first_line + j);
//
// Move the end of the first range to the beginning of the
// second range.
//
remainder_num_lines = llp->length1 - first_line - partial_num_lines;
trace(("remainder_num_lines = %ld\n", (long)remainder_num_lines));
memmove
(
llp->item + llp->start2 - remainder_num_lines,
llp->item + llp->start1 + llp->length1 - remainder_num_lines,
remainder_num_lines * sizeof(llp->item[0])
);
llp->start2 -= remainder_num_lines;
llp->length2 += remainder_num_lines;
//
// Adjust the length of the first range.
//
if (first_line)
llp->length1 = first_line;
else
{
llp->start1 = 0;
llp->length1 = 0;
}
//
// Adjust the number of lines are are deleting.
//
// Note that "first_line" does not move, because we have
// deleted lines from the start of the range, but not moved
// the front of the delete range.
//
num_lines -= partial_num_lines;
trace(("num_lines = %ld\n", (long)num_lines));
continue;
}
trace(("llp->start2 = %ld\n", (long)llp->start2));
trace(("llp->length2 = %ld\n", (long)llp->length2));
second_line = first_line - llp->length1;
trace(("second_line = %ld\n", (long)second_line));
if (second_line < llp->length2)
{
size_t partial_num_lines;
size_t j;
//
// Move the beginning of the second range to the end of the
// first range.
//
memmove
(
llp->item + llp->start1 + llp->length1,
llp->item + llp->start2,
second_line * sizeof(llp->item[0])
);
llp->length1 += second_line;
llp->start2 += second_line;
llp->length2 -= second_line;
second_line = 0;
//
// Destroy those lines which fall into the second range.
//
trace(("destroy mid range2\n"));
partial_num_lines = num_lines;
if (partial_num_lines > llp->length2)
partial_num_lines = llp->length2;
for (j = 0; j < partial_num_lines; ++j)
line_destructor(llp->item + llp->start2 + j);
//
// Adjust the length of the second range.
//
llp->start2 += partial_num_lines;
llp->length2 -= partial_num_lines;
if (llp->length2 == 0)
llp->start2 = llp->maximum;
//
// Adjust the number of lines are are deleting.
//
num_lines -= partial_num_lines;
trace(("num_lines = %ld\n", (long)num_lines));
first_line += partial_num_lines;
trace(("first_line = %ld\n", (long)first_line));
continue;
}
//
// Oops. This isn't means to happen. It should also have been
// caught by the assert at the beginning of this function.
//
assert(0);
break;
}
trace(("Checking...\n"));
trace(("llp->start1 = %ld\n", (long)llp->start1));
trace(("llp->length1 = %ld\n", (long)llp->length1));
trace(("llp->start2 = %ld\n", (long)llp->start2));
trace(("llp->length2 = %ld\n", (long)llp->length2));
trace(("llp->maximum = %ld\n", (long)llp->maximum));
CHECK(llp);
trace(("}\n"));
}
void
line_list_insert(line_list_t *llp,
size_t first_line,
change::pointer cp,
string_ty *text)
{
trace(("line_list_insert(llp = %08lX, first_line = %ld, cp = %08lX, "
"text = %08lX)\n{\n", (long)llp, (long)first_line, (long)cp,
(long)text));
CHECK(llp);
assert(first_line <= llp->length1 + llp->length2);
assert(cp);
assert(text);
for (;;)
{
size_t second_line;
trace(("llp->start1 = %ld\n", (long)llp->start1));
trace(("llp->length1 = %ld\n", (long)llp->length1));
trace(("llp->start2 = %ld\n", (long)llp->start2));
trace(("llp->length2 = %ld\n", (long)llp->length2));
if (first_line <= llp->length1)
{
size_t remainder_num_lines;
//
// Move the first range down to the beginning of the buffer.
//
if (llp->start1)
{
memmove
(
llp->item,
llp->item + llp->start1,
llp->length1 * sizeof(llp->item[0])
);
llp->start1 = 0;
}
//
// We need to grow if we have run out of room in the first
// range (in which case the second range will be empty).
//
assert(llp->start1 == 0);
if (llp->length1 == llp->start2)
{
trace(("growing...\n"));
assert(llp->length2 == 0);
assert(llp->start2 == llp->maximum);
llp->maximum = llp->maximum * 2 + 4;
line_t *new_item = new line_t [llp->maximum];
for (size_t k = 0; k < llp->length1; ++k)
new_item[k] = llp->item[k];
delete [] llp->item;
llp->item = new_item;
llp->start2 = llp->maximum;
trace(("llp->start2 = %ld\n", (long)llp->start2));
}
//
// Move the end of the first range to the beginning of the
// second range.
//
remainder_num_lines = llp->length1 - first_line;
trace(("remainder_num_lines = %ld\n", (long)remainder_num_lines));
if (remainder_num_lines)
{
memmove
(
llp->item + llp->start2 - remainder_num_lines,
llp->item + llp->start1 + first_line,
remainder_num_lines * sizeof(llp->item[0])
);
llp->length1 = first_line;
llp->start2 -= remainder_num_lines;
llp->length2 += remainder_num_lines;
trace(("llp->start1 = %ld\n", (long)llp->start1));
trace(("llp->length1 = %ld\n", (long)llp->length1));
trace(("llp->start2 = %ld\n", (long)llp->start2));
trace(("llp->length2 = %ld\n", (long)llp->length2));
}
//
// Add the line to the end of the first range.
//
assert(llp->start1 == 0);
line_constructor
(
llp->item // + llp->start1
+ first_line,
cp,
text
);
//
// Extend the first range to cover it.
//
// If it grows into the second range, rearrange things so
// that the first range contains both, and the second range
// is empty.
//
llp->length1++;
assert(llp->start1 == 0);
if (// llp->start1 +
llp->length1 == llp->start2)
{
llp->length1 += llp->length2;
llp->start2 = llp->maximum;
llp->length2 = 0;
}
//
// All done.
//
trace(("llp->start1 = %ld\n", (long)llp->start1));
trace(("llp->length1 = %ld\n", (long)llp->length1));
trace(("llp->start2 = %ld\n", (long)llp->start2));
trace(("llp->length2 = %ld\n", (long)llp->length2));
break;
}
trace(("llp->start2 = %ld\n", (long)llp->start2));
trace(("llp->length2 = %ld\n", (long)llp->length2));
second_line = first_line - llp->length1;
trace(("second_line = %ld\n", (long)second_line));
if (second_line <= llp->length2)
{
//
// Move the beginning of the second range to the end of the
// first range.
//
memmove
(
llp->item + llp->start1 + llp->length1,
llp->item + llp->start2,
second_line * sizeof(llp->item[0])
);
llp->length1 += second_line;
llp->start2 += second_line;
llp->length2 -= second_line;
second_line = 0;
//
// If the second range is now empty, move it to the end.
//
if (llp->length2 == 0)
llp->start2 = llp->maximum;
//
// Go around again, now that we are the right shape.
//
continue;
}
//
// Oops. This isn't means to happen. It should also have been
// caught by the assert at the beginning of this function.
//
assert(0);
break;
}
CHECK(llp);
trace(("}\n"));
}