From 737c6c1d20f8efd56778aa788f2bc7bca97a11f3 Mon Sep 17 00:00:00 2001 From: Syed Daanish Date: Tue, 28 Jul 2026 00:31:58 +0100 Subject: [PATCH] Setup basic piece table like data structure. --- include/pch.h | 5 +-- include/vase/append_buffer.h | 30 ++++++++++++++ include/vase/original_buffer.h | 26 +++++++++++++ include/vase/shard.h | 69 +++++++++++++++++++++++++++++++++ include/vase/vase.h | 28 ++++++++++++++ src/main.cc | 4 +- src/vase/append_buffer.cc | 71 ++++++++++++++++++++++++++++++++++ 7 files changed, 228 insertions(+), 5 deletions(-) create mode 100644 include/vase/append_buffer.h create mode 100644 include/vase/original_buffer.h create mode 100644 include/vase/shard.h create mode 100644 include/vase/vase.h create mode 100644 src/vase/append_buffer.cc diff --git a/include/pch.h b/include/pch.h index 96e2214..1741e9a 100644 --- a/include/pch.h +++ b/include/pch.h @@ -1,5 +1,4 @@ -#ifndef PCH_H -#define PCH_H +#pragma once #define PCRE2_CODE_UNIT_WIDTH 8 #define PCRE_WORKSPACE_SIZE 512 @@ -54,5 +53,3 @@ extern "C" { #include using namespace std::chrono_literals; - -#endif diff --git a/include/vase/append_buffer.h b/include/vase/append_buffer.h new file mode 100644 index 0000000..125221d --- /dev/null +++ b/include/vase/append_buffer.h @@ -0,0 +1,30 @@ +#pragma once + +#include "pch.h" + +struct AppendBuffer { + static constexpr uint32_t CHUNK_SIZE = 1 << 16; + using TChunk = char[CHUNK_SIZE]; + std::vector buf; + TChunk *t_current; + uint32_t current_offset{0}; + uint32_t t_offset{0}; + + using LChunk = uint32_t[CHUNK_SIZE]; + std::vector newlines; + LChunk *l_current; + uint32_t l_offset{0}; + + AppendBuffer(); + + ~AppendBuffer(); + + inline void key(char c); + void append(const char *text, uint32_t length); + +private: + inline void new_text_chunk(); + inline void new_line_chunk(); + + inline void _append(const char *text, uint32_t length); +}; diff --git a/include/vase/original_buffer.h b/include/vase/original_buffer.h new file mode 100644 index 0000000..6e8adfd --- /dev/null +++ b/include/vase/original_buffer.h @@ -0,0 +1,26 @@ +#pragma once + +#include "pch.h" + +struct OriginalBuffer { + char *buf; + uint32_t length; + std::vector newlines; + + // TODO: replace with file io or even lazy loaded file io for large files/logs etc. later. + OriginalBuffer(char *buf, uint32_t length) : buf(buf), length(length) { + const char *p = buf; + const char *end = buf + length; + while (p < end) { + p = (const char *)memchr(p, '\n', end - p); + if (!p) + break; + newlines.push_back(uint32_t(p - buf)); + p++; + } + } + + ~OriginalBuffer() { + free(buf); + } +}; diff --git a/include/vase/shard.h b/include/vase/shard.h new file mode 100644 index 0000000..2f054e6 --- /dev/null +++ b/include/vase/shard.h @@ -0,0 +1,69 @@ +#pragma once + +#include "pch.h" + +struct Shard { + enum struct ShardKind { + Branch, + Petal + } kind; + + uint32_t length; + uint32_t lines; + + std::atomic_uint32_t refs; + + Shard(ShardKind kind, uint32_t length, uint32_t lines) + : kind(kind), length(length), lines(lines) {}; +}; + +struct ShardPtr { + Shard *ptr; + + ShardPtr(const ShardPtr &other) : ptr(other.ptr) { + if (ptr) + ptr->refs++; + } + + ShardPtr &operator=(const ShardPtr &other) { + if (ptr == other.ptr) + return *this; + if (ptr && --ptr->refs == 0) + delete ptr; + ptr = other.ptr; + if (ptr) + ptr->refs++; + return *this; + } + + ShardPtr(Shard *p = nullptr) : ptr(p) { + if (ptr) + ptr->refs++; + } + + ~ShardPtr() { + if (ptr && --ptr->refs == 0) + delete ptr; + } +}; + +struct Branch : Shard { + ShardPtr left; + ShardPtr right; + + Branch(Shard *l, Shard *r) + : Shard(ShardKind::Branch, l->length + r->length, l->lines + r->lines), + left(ShardPtr(l)), right(ShardPtr(r)) {}; +}; + +struct Petal : Shard { + enum struct Kind { + Original, + Append + } source; + + uint32_t pos; + + Petal(uint32_t length, uint32_t lines, Kind source, uint32_t pos) + : Shard(ShardKind::Petal, length, lines), source(source), pos(pos) {}; +}; diff --git a/include/vase/vase.h b/include/vase/vase.h new file mode 100644 index 0000000..ba2e90b --- /dev/null +++ b/include/vase/vase.h @@ -0,0 +1,28 @@ +#pragma once + +#include "append_buffer.h" +#include "original_buffer.h" +#include "pch.h" +#include "shard.h" + +struct Vase { + OriginalBuffer original; + AppendBuffer append; + + std::vector undo; + uint8_t top; // of the undo stack. + uint8_t max; // for redo when no edits have been done after some undo. + + ShardPtr root; + + Vase(char *data, uint32_t length) + : original(data, length), + append() { + root = new Petal( + length, + original.newlines.size(), + Petal::Kind::Original, + 0 + ); + } +}; diff --git a/src/main.cc b/src/main.cc index c22cc96..6af9fea 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,3 +1,5 @@ #include "pch.h" -int main() { return 0; } +int main() { + return 0; +} diff --git a/src/vase/append_buffer.cc b/src/vase/append_buffer.cc new file mode 100644 index 0000000..6fd9477 --- /dev/null +++ b/src/vase/append_buffer.cc @@ -0,0 +1,71 @@ +#include "vase/append_buffer.h" + +AppendBuffer::AppendBuffer() { + new_text_chunk(); + new_line_chunk(); +} + +AppendBuffer::~AppendBuffer() { + for (auto p : buf) + free(p); + for (auto p : newlines) + free(p); +} + +inline void AppendBuffer::key(char c) { + if (t_offset == CHUNK_SIZE) + new_text_chunk(); + (*t_current)[t_offset++] = c; + if (c == '\n') { + if (l_offset == CHUNK_SIZE) + new_line_chunk(); + (*l_current)[l_offset++] = current_offset; + } + ++current_offset; +} + +void AppendBuffer::append(const char *text, uint32_t length) { + while (length) { + uint32_t space = CHUNK_SIZE - t_offset; + if (space == 0) { + new_text_chunk(); + space = CHUNK_SIZE; + } + uint32_t copy = length < space ? length : space; + _append(text, copy); + text += copy; + length -= copy; + } +} + +inline void AppendBuffer::new_text_chunk() { + t_current = (TChunk *)malloc(sizeof(TChunk)); + buf.push_back(t_current); + t_offset = 0; +} + +inline void AppendBuffer::new_line_chunk() { + l_current = (LChunk *)malloc(sizeof(LChunk)); + newlines.push_back(l_current); + l_offset = 0; +} + +inline void AppendBuffer::_append(const char *text, uint32_t length) { + memcpy((*t_current) + t_offset, text, length); + + const char *p = text; + const char *end = text + length; + + while (p < end) { + p = (const char *)memchr(p, '\n', end - p); + if (!p) + break; + if (l_offset == CHUNK_SIZE) + new_line_chunk(); + (*l_current)[l_offset++] = current_offset + uint32_t(p - text); + p++; + } + + t_offset += length; + current_offset += length; +}