editor/src/editor/buffer.d

315 lines
7.0 KiB
D

import aliases;
import math;
import alloc;
import util;
struct FlatBuffer
{
Arena arena;
u8[] data;
u64 length;
u64 line_count;
Range pos;
}
struct Range
{
u32 x;
u32 y;
}
struct LineBuffers
{
Arena arena;
u8[][] lines;
u32[] lengths;
}
FlatBuffer
CreateFlatBuffer(u8[] data)
{
u64 capacity = RoundUp(cast(u64)(data.length) * 2, KB(4));
u8[] buf = MAllocArray!(u8)(capacity);
buf[0 .. data.length] = data[0 .. data.length];
u64 line_count = 1;
for(u64 i = 0; i < cast(u64)data.length; i += 1)
{
if (data.ptr[i] == '\n')
{
line_count += 1;
}
}
return FlatBuffer(
arena: CreateArena(MB(1)),
data: buf,
length: cast(u64)data.length,
line_count: line_count,
);
}
LineBuffers
CreateLineBuffers(u64 arena_size)
{
return LineBuffers(
arena: CreateArena(arena_size),
);
}
void
Insert(FlatBuffer* buffer, u8[] insert, u64 length, u64 pos)
{
if (buffer.length + length > buffer.data.length)
{
FlatBuffer new_buf = CreateFlatBuffer(buffer.data);
MFreeArray(buffer.data);
*buffer = new_buf;
}
for(u64 i = 0; i < length; i += 1)
{
if (insert.ptr[i] == '\n')
{
buffer.line_count += 1;
}
}
u64 temp_len = buffer.length-pos;
u8[] temp = AllocArray!(u8)(&buffer.arena, temp_len);
temp[0 .. temp_len] = buffer.data[pos .. pos+temp_len];
buffer.data[pos .. pos+length] = insert[0 .. length];
pos += length;
buffer.data[pos .. pos+temp_len] = temp[0 .. temp_len];
buffer.length += length;
Reset(&buffer.arena);
}
void
Insert(FlatBuffer* buffer, u8[] insert, u64 length, Range pos)
{
assert(pos.y <= buffer.line_count, "Insert failure: y provided is greater than line_count");
Insert(buffer, insert, length, RangeToPos(buffer, pos));
}
// TODO: handle case for when lines are longer than line buffer
void
GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length)
{
assert(start_line < buffer.line_count, "GetLines failure: start is not less than line_count");
assert(linebufs != null, "GetLines failure: linebufs is null");
Reset(&linebufs.arena);
linebufs.lines = AllocArray!(u8[])(&linebufs.arena, length);
linebufs.lengths = AllocArray!(u32)(&linebufs.arena, length);
i64 start = -1;
u64 line = 0;
u64 current_line = 0;
for(u64 i = 0; i < buffer.length; i += 1)
{
if (current_line == length)
{
break;
}
bool new_line = (buffer.data.ptr[i] == '\n');
if (line < start_line && new_line)
{
line += 1;
continue;
}
if (start < 0 && new_line)
{
linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, 1);
linebufs.lines[current_line][0] = '\n';
linebufs.lengths[current_line] = 1;
current_line += 1;
continue;
}
if (start < 0)
{
start = cast(i64)i;
continue;
}
if (new_line)
{
linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, i-start);
linebufs.lines[current_line][0 .. $] = buffer.data[start .. i];
linebufs.lengths[current_line] = cast(u32)(i-start);
current_line += 1;
start = -1;
continue;
}
if (i == buffer.length-1)
{
linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, buffer.length-start);
linebufs.lines[current_line][0 .. $] = buffer.data[start .. buffer.length];
linebufs.lengths[current_line] = cast(u32)(buffer.length-start);
}
}
}
u64
RangeToPos(FlatBuffer* buffer, Range range)
{
u64 buffer_pos;
u32 line, col;
for(u64 i = 0; i < buffer.length; i += 1)
{
if (range.y == line)
{
buffer_pos = i;
break;
}
if (buffer.data.ptr[i] == '\n')
{
line += 1;
}
}
for(u64 i = buffer_pos; i < buffer.length; i += 1)
{
if (col == range.x)
{
buffer_pos = i;
break;
}
col += 1;
assert(buffer.data.ptr[i] != '\n', "Insert FlatBuffer Range failure: x position not in range of line");
}
return buffer_pos;
}
void
Delete(FlatBuffer* buffer, u64 length, u64 pos)
{
u64 end = pos+length;
assert(end <= buffer.length, "Delete failure: pos+length is not in range");
u8[] temp;
if (end != buffer.length)
{
temp = AllocArray!(u8)(&buffer.arena, buffer.length-end);
temp[0 .. temp.length] = buffer.data[end .. buffer.length];
buffer.data[pos .. pos+temp.length] = temp[0 .. temp.length];
}
buffer.length -= length;
}
void
Replace(FlatBuffer* buffer, u8[] insert, u64 length, u64 pos, u64 delete_length)
{
Delete(buffer, delete_length, pos);
Insert(buffer, insert, insert.length, pos);
}
unittest
{
import std.stdio;
u8[] StringToU8(string str)
{
return (cast(u8*)str.ptr)[0 .. str.length];
}
// FlatBuffer Insert/Delete/Replace
{
u8[] data = StringToU8("This is a test string for testing the FlatBuffer struct.");
FlatBuffer buf = CreateFlatBuffer(data);
u8[] insert = StringToU8("This is a string inserted into the beginning of the buffer. ");
Insert(&buf, insert, insert.length, 0);
string result1 = "This is a string inserted into the beginning of the buffer. This is a test string for testing the FlatBuffer struct.";
foreach(i, ch; result1)
{
assert(buf.data[i] == ch, "FlatBuffer Insert failure: buffers do not match");
}
Delete(&buf, 17, 34);
// 10, 21
string result2 = "This is a string inserted into the buffer. This is a test string for testing the FlatBuffer struct.";
foreach(i, ch; result2)
{
assert(buf.data[i] == ch, "FlatBuffer Delete failure: buffers do not match");
}
insert = StringToU8("replaced line inserted into the beginning of ");
Replace(&buf, insert, insert.length, 10, 21);
string result3 = "This is a replaced line inserted into the beginning of the buffer. This is a test string for testing the FlatBuffer struct.";
foreach(i, ch; result3)
{
assert(buf.data[i] == ch, "FlatBuffer Replace failure: buffers do not match");
}
}
// FlatBuffer lines
{
u8[] data = StringToU8("This is a line.\nThis is a second line,\nalong with a third line.");
FlatBuffer buf = CreateFlatBuffer(data);
assert(buf.line_count == 3, "FlatBuffer line count mismatch");
u8[] insert = StringToU8("Maybe, ");
Insert(&buf, insert, insert.length, Range(x: 0, y: 1));
string result = "This is a line.\nMaybe, This is a second line,\nalong with a third line.";
foreach(i, ch; result)
{
assert(buf.data[i] == ch, "FlatBuffer Insert pos failure: buffers do not match");
}
u8[][] lines = new u8[][](3, 1024);
GetLines(&buf, lines, 0, 3);
string[] result_arr = [
"This is a line.",
"Maybe, This is a second line,",
"along with a third line.",
];
foreach(i, res; result_arr)
{
foreach(j, ch; res)
{
assert(lines[i][j] == ch, "FlatBuffer GetLines failure: result strings do not match");
}
}
insert = StringToU8("replaced line.\nThis is the previous ");
Insert(&buf, insert, insert.length, Range(x: 10, y: 0));
lines = new u8[][](4, 1024);
GetLines(&buf, lines, 0, 4);
result_arr = [
"This is a replaced line.",
"This is the previous line.",
"Maybe, This is a second line,",
"along with a third line.",
];
foreach(i, res; result_arr)
{
foreach(j, ch; res)
{
assert(lines[i][j] == ch, "FlatBuffer GetLines 2 failure: result strings do not match");
}
}
}
}