Working on the buffer
This commit is contained in:
@@ -17,31 +17,95 @@ struct Range {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Buffer {
|
struct Buffer {
|
||||||
Allocator allocator;
|
U16 *data;
|
||||||
U16 *data;
|
Int len;
|
||||||
Int len;
|
Int cap;
|
||||||
Int cap;
|
Array<Int> line_starts;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Int GetSize(Range range) {
|
||||||
|
Assert(range.max >= range.min);
|
||||||
|
Int result = range.max - range.min;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
String16 GetString(Buffer &buffer, Range range = {0, INT64_MAX}) {
|
||||||
|
range.min = Clamp(range.min, (Int)0, buffer.len);
|
||||||
|
range.max = Clamp(range.max, (Int)0, buffer.len);
|
||||||
|
String16 result = {(wchar_t *)buffer.data + range.min, GetSize(range)};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Range GetEndAsRange(Buffer &buffer) {
|
||||||
|
Range result = {buffer.len, buffer.len};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Range GetBeginAsRange(Buffer &buffer) {
|
||||||
|
Range result = {0, 0};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Range GetRange(Buffer &buffer) {
|
||||||
|
Range result = {0, buffer.len};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Range GetLine(Buffer &buffer, Int line) {
|
||||||
|
Range result = {0, buffer.len};
|
||||||
|
result.min = buffer.line_starts[line];
|
||||||
|
if (buffer.line_starts.len < line + 1) {
|
||||||
|
result.max = buffer.line_starts[line + 1];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Int GetLineNumber(Buffer &buffer, Int pos) {
|
||||||
|
// @optimize: binary search
|
||||||
|
Add(&buffer.line_starts, buffer.len);
|
||||||
|
Int result = 0;
|
||||||
|
for (Int i = 0; i < buffer.line_starts.len - 1; i += 1) {
|
||||||
|
Range range = {buffer.line_starts[i], buffer.line_starts[i + 1]};
|
||||||
|
if (pos >= range.min && pos < range.max) {
|
||||||
|
result = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pop(&buffer.line_starts);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void InitBuffer(Allocator allocator, Buffer *buffer) {
|
void InitBuffer(Allocator allocator, Buffer *buffer) {
|
||||||
buffer->cap = 4096;
|
buffer->cap = 4096;
|
||||||
buffer->data = AllocArray(allocator, U16, buffer->cap);
|
buffer->data = AllocArray(allocator, U16, buffer->cap);
|
||||||
buffer->allocator = allocator;
|
buffer->line_starts.allocator = allocator;
|
||||||
|
Add(&buffer->line_starts, (Int)0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Grow(Buffer *buffer, Int change_size) {
|
void Grow(Buffer *buffer, Int change_size) {
|
||||||
Int new_size = buffer->len + change_size;
|
Int new_size = buffer->len + change_size;
|
||||||
if (new_size > buffer->cap) {
|
if (new_size > buffer->cap) {
|
||||||
Int outside = new_size - buffer->cap;
|
Allocator alo = buffer->line_starts.allocator;
|
||||||
Int new_cap = (buffer->cap + outside) * 2;
|
Int outside = new_size - buffer->cap;
|
||||||
U16 *new_array = AllocArray(buffer->allocator, U16, new_cap);
|
Int new_cap = (buffer->cap + outside) * 2;
|
||||||
|
U16 *new_array = AllocArray(alo, U16, new_cap);
|
||||||
MemoryCopy(new_array, buffer->data, buffer->len * sizeof(U16));
|
MemoryCopy(new_array, buffer->data, buffer->len * sizeof(U16));
|
||||||
Dealloc(buffer->allocator, &buffer->data);
|
Dealloc(alo, &buffer->data);
|
||||||
buffer->cap = new_cap;
|
buffer->cap = new_cap;
|
||||||
buffer->data = new_array;
|
buffer->data = new_array;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OffsetAllLinesPast(Buffer *buffer, Int line, Int *_offset) {
|
||||||
|
Int offset = *_offset;
|
||||||
|
*_offset = 0;
|
||||||
|
if (offset == 0) return;
|
||||||
|
|
||||||
|
for (Int i = line; i < buffer->line_starts.len; i += 1) {
|
||||||
|
buffer->line_starts[i] += offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ReplaceText(Buffer *buffer, Range range, String16 string) {
|
void ReplaceText(Buffer *buffer, Range range, String16 string) {
|
||||||
Assert(range.max >= range.min);
|
Assert(range.max >= range.min);
|
||||||
Assert(range.max >= 0 && range.max <= buffer->len);
|
Assert(range.max >= 0 && range.max <= buffer->len);
|
||||||
@@ -58,35 +122,113 @@ void ReplaceText(Buffer *buffer, Range range, String16 string) {
|
|||||||
U16 *end_remove = begin_remove + range_size;
|
U16 *end_remove = begin_remove + range_size;
|
||||||
Int remain_len = buffer->len - (range.min + range_size);
|
Int remain_len = buffer->len - (range.min + range_size);
|
||||||
|
|
||||||
|
Int new_buffer_len = buffer->len + change_size;
|
||||||
|
Array<Int> &ls = buffer->line_starts;
|
||||||
|
Int min_line_number = GetLineNumber(*buffer, range.min);
|
||||||
|
Assert(min_line_number < ls.len);
|
||||||
|
{
|
||||||
|
// Update line starts with removed string
|
||||||
|
// for (Int i = range.min; i < range.max; i += 1) {
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @todo fuck I need to update all lines after edition point
|
||||||
|
// Update line starts with added string
|
||||||
|
Int line_update_counter = 0;
|
||||||
|
Int nl = min_line_number + 1;
|
||||||
|
for (Int i = 0; i < string.len; i += 1) {
|
||||||
|
nl = min_line_number + 1;
|
||||||
|
bool next_line_valid = nl < ls.len;
|
||||||
|
|
||||||
|
if (string[i] == L'\n') {
|
||||||
|
Int new_line_pos = range.min + i + 1;
|
||||||
|
line_update_counter += 1;
|
||||||
|
|
||||||
|
Insert(&ls, new_line_pos, nl);
|
||||||
|
OffsetAllLinesPast(buffer, nl + 1, &line_update_counter);
|
||||||
|
min_line_number = nl;
|
||||||
|
} else if (next_line_valid) {
|
||||||
|
line_update_counter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OffsetAllLinesPast(buffer, nl, &line_update_counter);
|
||||||
|
}
|
||||||
|
|
||||||
U16 *begin_add = begin_remove;
|
U16 *begin_add = begin_remove;
|
||||||
U16 *end_add = begin_add + string.len;
|
U16 *end_add = begin_add + string.len;
|
||||||
MemoryMove(end_add, end_remove, remain_len * sizeof(U16));
|
MemoryMove(end_add, end_remove, remain_len * sizeof(U16));
|
||||||
MemoryCopy(begin_add, string.data, string.len * sizeof(U16));
|
MemoryCopy(begin_add, string.data, string.len * sizeof(U16));
|
||||||
buffer->len += change_size;
|
buffer->len = new_buffer_len;
|
||||||
}
|
|
||||||
|
|
||||||
Int GetSize(Range range) {
|
|
||||||
Assert(range.max >= range.min);
|
|
||||||
Int result = range.max - range.min;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
String16 GetString(Buffer &buffer, Range range = {0, INT64_MAX}) {
|
|
||||||
range.min = Clamp(range.min, (Int)0, buffer.len);
|
|
||||||
range.max = Clamp(range.max, (Int)0, buffer.len);
|
|
||||||
String16 result = {(wchar_t *)buffer.data + range.min, GetSize(range)};
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestBuffer() {
|
void TestBuffer() {
|
||||||
Scratch scratch;
|
Scratch scratch;
|
||||||
Buffer buffer = {};
|
{
|
||||||
|
Buffer buffer = {};
|
||||||
|
InitBuffer(scratch, &buffer);
|
||||||
|
Assert(buffer.line_starts.len == 1);
|
||||||
|
String16 test_string = L"Thing itself";
|
||||||
|
{
|
||||||
|
ReplaceText(&buffer, {{}, {}}, test_string);
|
||||||
|
Assert(buffer.cap == 4096);
|
||||||
|
Assert(buffer.len == 12);
|
||||||
|
String16 a = GetString(buffer);
|
||||||
|
Assert(a == test_string);
|
||||||
|
Assert(buffer.line_starts.len == 1);
|
||||||
|
Assert(buffer.line_starts[0] == 0);
|
||||||
|
Assert(GetLineNumber(buffer, 4) == 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ReplaceText(&buffer, {0, 5}, L"");
|
||||||
|
Assert(buffer.cap == 4096);
|
||||||
|
Assert(buffer.len == 12 - 5);
|
||||||
|
String16 a = GetString(buffer);
|
||||||
|
Assert(a == L" itself");
|
||||||
|
Assert(buffer.line_starts.len == 1);
|
||||||
|
Assert(buffer.line_starts[0] == 0);
|
||||||
|
Assert(GetLineNumber(buffer, 4) == 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ReplaceText(&buffer, GetEndAsRange(buffer), L" and");
|
||||||
|
Assert(buffer.cap == 4096);
|
||||||
|
Assert(buffer.len == 12 - 5 + 4);
|
||||||
|
String16 a = GetString(buffer);
|
||||||
|
Assert(a == L" itself and");
|
||||||
|
Assert(buffer.line_starts.len == 1);
|
||||||
|
Assert(buffer.line_starts[0] == 0);
|
||||||
|
Assert(GetLineNumber(buffer, 4) == 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ReplaceText(&buffer, GetRange(buffer), L"");
|
||||||
|
Assert(buffer.cap == 4096);
|
||||||
|
Assert(buffer.len == 0);
|
||||||
|
String16 a = GetString(buffer);
|
||||||
|
Assert(a == L"");
|
||||||
|
Assert(buffer.line_starts.len == 1);
|
||||||
|
Assert(buffer.line_starts[0] == 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ReplaceText(&buffer, GetEndAsRange(buffer), L"Memes and other\nthings");
|
||||||
|
Assert(buffer.line_starts.len == 2);
|
||||||
|
Assert(GetLineNumber(buffer, 17) == 1);
|
||||||
|
Assert(GetLineNumber(buffer, 16) == 1);
|
||||||
|
Assert(GetLineNumber(buffer, 15) == 0);
|
||||||
|
Assert(buffer.data[15] == L'\n');
|
||||||
|
Assert(buffer.data[16] == L't');
|
||||||
|
|
||||||
String16 test_string = L"Thing itself";
|
ReplaceText(&buffer, GetBeginAsRange(buffer), L"Things as is\nand stuff\n");
|
||||||
InitBuffer(scratch, &buffer);
|
Assert(buffer.line_starts.len == 4);
|
||||||
ReplaceText(&buffer, {{}, {}}, test_string);
|
Assert(GetLineNumber(buffer, 12) == 0);
|
||||||
Assert(buffer.cap == 4096);
|
Assert(buffer.data[12] == L'\n');
|
||||||
Assert(buffer.len == 12);
|
Assert(GetLineNumber(buffer, 13) == 1);
|
||||||
String16 a = GetString(buffer);
|
Assert(GetLineNumber(buffer, 21) == 1);
|
||||||
Assert(a == test_string);
|
Assert(GetLineNumber(buffer, 22) == 1);
|
||||||
|
Assert(buffer.data[22] == L'\n');
|
||||||
|
Assert(GetLineNumber(buffer, 23) == 2);
|
||||||
|
Assert(GetLineNumber(buffer, 37) == 2);
|
||||||
|
Assert(GetLineNumber(buffer, 38) == 2);
|
||||||
|
Assert(buffer.data[38] == L'\n');
|
||||||
|
Assert(GetLineNumber(buffer, 39) == 3);
|
||||||
|
Assert(buffer.data[39] == L't');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user