Major fixes:
- ChunkIterator is now bidirectional - LineIterator is now also bidirectional - Undo/redo history operations - Replace operation - Range based api functions - And more minor bug fixes
This commit is contained in:
@@ -25,13 +25,13 @@ MRUBY_CFLAGS ?= -I./libs/mruby/build/host/include
|
|||||||
MRUBY_LIBS ?= -L./libs/mruby/build/host/lib -lmruby
|
MRUBY_LIBS ?= -L./libs/mruby/build/host/lib -lmruby
|
||||||
|
|
||||||
CFLAGS_DEBUG :=\
|
CFLAGS_DEBUG :=\
|
||||||
-std=c++20 -Wall -Wextra -fno-rtti \
|
-std=c++23 -Wall -Wextra -fno-rtti \
|
||||||
-Og -fno-inline -gsplit-dwarf \
|
-Og -fno-inline -gsplit-dwarf \
|
||||||
-g -fno-omit-frame-pointer -ffast-math \
|
-g -fno-omit-frame-pointer -ffast-math \
|
||||||
-I./include -I./libs/unicode_width
|
-I./include -I./libs/unicode_width
|
||||||
|
|
||||||
CFLAGS_RELEASE :=\
|
CFLAGS_RELEASE :=\
|
||||||
-std=c++20 -O3 -mtune=generic -fno-rtti \
|
-std=c++23 -O3 -mtune=generic -fno-rtti \
|
||||||
-fvisibility=hidden -static -ffast-math \
|
-fvisibility=hidden -static -ffast-math \
|
||||||
-fomit-frame-pointer -DNDEBUG -s \
|
-fomit-frame-pointer -DNDEBUG -s \
|
||||||
-I./include -I./libs/unicode_width
|
-I./include -I./libs/unicode_width
|
||||||
|
|||||||
+1
-3
@@ -29,12 +29,10 @@ inline int read_file(const char *path, char **text, uint32_t *len) {
|
|||||||
|
|
||||||
if (read != *len) {
|
if (read != *len) {
|
||||||
free(*text);
|
free(*text);
|
||||||
*text = NULL;
|
*text = nullptr;
|
||||||
*len = 0;
|
*len = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
(*text)[*len] = '\0'; // handy if treating it as a C string
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
struct Point {
|
|
||||||
uint32_t row;
|
|
||||||
uint32_t col;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "../constants.h"
|
||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
struct AppendBuffer : Buffer {
|
struct AppendBuffer : Buffer {
|
||||||
static constexpr uint32_t CHUNK_SIZE = 1 << 16;
|
using TChunk = char[APPEND_CHUNK_SIZE];
|
||||||
|
|
||||||
using TChunk = char[CHUNK_SIZE];
|
|
||||||
std::vector<TChunk *> buf;
|
std::vector<TChunk *> buf;
|
||||||
TChunk *t_current;
|
TChunk *t_current;
|
||||||
uint32_t current_offset{0};
|
uint32_t current_offset{0};
|
||||||
@@ -21,6 +20,4 @@ struct AppendBuffer : Buffer {
|
|||||||
|
|
||||||
const char *read(uint32_t pos, uint32_t *out_len);
|
const char *read(uint32_t pos, uint32_t *out_len);
|
||||||
inline uint32_t length();
|
inline uint32_t length();
|
||||||
|
|
||||||
inline void new_text_chunk();
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ struct OriginalBuffer : Buffer {
|
|||||||
char *buf;
|
char *buf;
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
|
|
||||||
// TODO: replace with file io or even lazy loaded file io for large files/logs etc. later.
|
// Add 2 modes here, normal and lazy, lazy copies the file to a safe location and uses mmap.
|
||||||
OriginalBuffer(char *buf, uint32_t len);
|
OriginalBuffer(std::string path);
|
||||||
|
|
||||||
~OriginalBuffer();
|
~OriginalBuffer();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
constexpr uint32_t APPEND_CHUNK_SIZE = 64 * 1024;
|
||||||
|
constexpr uint32_t PETAL_SIZE_MAX = 32 * 1024;
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
APPEND_CHUNK_SIZE > PETAL_SIZE_MAX,
|
||||||
|
"PETAL_SIZE_MAX must be smaller than APPEND_CHUNK_SIZE"
|
||||||
|
);
|
||||||
@@ -3,14 +3,20 @@
|
|||||||
#include "../shard.h"
|
#include "../shard.h"
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
|
enum struct Direction : uint8_t {
|
||||||
|
Forward,
|
||||||
|
Backward
|
||||||
|
};
|
||||||
|
|
||||||
struct ChunkIterator {
|
struct ChunkIterator {
|
||||||
|
Direction dir;
|
||||||
Shard *root = nullptr;
|
Shard *root = nullptr;
|
||||||
std::vector<Shard *> stack;
|
std::vector<Shard *> stack;
|
||||||
Petal *petal = nullptr;
|
Petal *petal = nullptr;
|
||||||
uint32_t petal_offset = 0;
|
uint32_t petal_offset = 0;
|
||||||
uint32_t global_offset;
|
uint32_t global_offset = 0;
|
||||||
|
|
||||||
ChunkIterator(Shard *r);
|
ChunkIterator(Shard *r, Direction dir);
|
||||||
|
|
||||||
~ChunkIterator();
|
~ChunkIterator();
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,12 @@
|
|||||||
|
|
||||||
struct LineIterator {
|
struct LineIterator {
|
||||||
ChunkIterator it;
|
ChunkIterator it;
|
||||||
|
Direction dir;
|
||||||
|
|
||||||
const char *cursor = nullptr;
|
const char *chunk;
|
||||||
bool eof = false;
|
uint32_t len;
|
||||||
uint32_t remaining = 0;
|
|
||||||
uint32_t line_offset = 0;
|
|
||||||
|
|
||||||
LineIterator(Shard *r, uint32_t start_line);
|
|
||||||
|
|
||||||
|
LineIterator(Shard *r, uint32_t start_line, Direction dir);
|
||||||
~LineIterator() = default;
|
~LineIterator() = default;
|
||||||
|
|
||||||
bool next(std::string &line);
|
bool next(std::string &line);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "buffer/buffer.h"
|
#include "buffer/buffer.h"
|
||||||
#include "buffer/original.h"
|
#include "buffer/original.h"
|
||||||
|
#include "constants.h"
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "utils/utils.h"
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
|||||||
+33
-11
@@ -2,14 +2,29 @@
|
|||||||
|
|
||||||
#include "buffer/append.h"
|
#include "buffer/append.h"
|
||||||
#include "buffer/original.h"
|
#include "buffer/original.h"
|
||||||
|
#include "constants.h"
|
||||||
#include "iterators/line.h"
|
#include "iterators/line.h"
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "search.h"
|
#include "search.h"
|
||||||
#include "shard.h"
|
#include "shard.h"
|
||||||
#include "utils/utils.h"
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
uint32_t row;
|
||||||
|
uint32_t col;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Range {
|
||||||
|
Point start;
|
||||||
|
Point end;
|
||||||
|
};
|
||||||
|
|
||||||
struct Vase {
|
struct Vase {
|
||||||
Vase(char *data, uint32_t length);
|
OriginalBuffer original;
|
||||||
|
AppendBuffer append;
|
||||||
|
Shard *root;
|
||||||
|
|
||||||
|
Vase(std::string path);
|
||||||
|
|
||||||
~Vase();
|
~Vase();
|
||||||
|
|
||||||
@@ -20,26 +35,33 @@ struct Vase {
|
|||||||
|
|
||||||
LineIterator iterate(uint32_t line);
|
LineIterator iterate(uint32_t line);
|
||||||
|
|
||||||
void input(Point *point, char key);
|
void insert(Point *point, char key);
|
||||||
void insert(Point *point, const char *data, uint32_t len);
|
void insert(Point *point, const char *data, uint32_t len);
|
||||||
void erase(Point *point, int64_t amount);
|
void erase(Point *point, int64_t amount);
|
||||||
bool jump(Point *point, int64_t amount);
|
void erase(Range range);
|
||||||
bool clamp(Point *point);
|
void replace(Range range, const char *data, uint32_t len);
|
||||||
|
|
||||||
|
void move_clusters(Point *point, int64_t amount);
|
||||||
|
void move_lines(Point *point, int64_t amount);
|
||||||
|
void clamp(Point *point);
|
||||||
|
|
||||||
void regex_search_replace(
|
void regex_search_replace(
|
||||||
std::string_view pattern,
|
std::string_view pattern,
|
||||||
Point start, Point end,
|
Point start, Point end,
|
||||||
std::string_view replace, std::string_view options
|
std::string_view replace, std::string_view options
|
||||||
);
|
);
|
||||||
|
|
||||||
private:
|
bool undo();
|
||||||
OriginalBuffer original;
|
bool redo();
|
||||||
AppendBuffer append;
|
|
||||||
Shard *root;
|
|
||||||
|
|
||||||
/*std::vector<Shard *> undo; // TODO: later
|
void snapshot();
|
||||||
uint16_t top; // of the undo stack.
|
void prune_history(uint32_t n);
|
||||||
uint16_t max; // for redo when no edits have been done after some undo.*/
|
|
||||||
|
private:
|
||||||
|
std::vector<Shard *> history;
|
||||||
|
uint32_t history_top;
|
||||||
|
|
||||||
uint32_t offset_of(Point point);
|
uint32_t offset_of(Point point);
|
||||||
static void flatten(Shard *s, std::string &out);
|
static void flatten(Shard *s, std::string &out);
|
||||||
|
void _insert(Point *point, const char *data, uint32_t len);
|
||||||
};
|
};
|
||||||
|
|||||||
+5
-27
@@ -1,39 +1,17 @@
|
|||||||
#include "io/file.h"
|
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "vase/iterators/chunk.h"
|
#include "vase/iterators/chunk.h"
|
||||||
#include "vase/iterators/line.h"
|
#include "vase/iterators/line.h"
|
||||||
#include "vase/shard.h"
|
|
||||||
#include "vase/vase.h"
|
#include "vase/vase.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
if (argc < 2)
|
||||||
#ifdef DEBUG
|
|
||||||
|
|
||||||
const char *text_o =
|
|
||||||
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz1\n"
|
|
||||||
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz2\n"
|
|
||||||
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz3\n"
|
|
||||||
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz4";
|
|
||||||
|
|
||||||
uint32_t len = strlen(text_o);
|
|
||||||
char *text = strdup(text_o);
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
const char *path;
|
|
||||||
|
|
||||||
if (argc >= 2)
|
|
||||||
path = argv[1];
|
|
||||||
else
|
|
||||||
throw std::runtime_error("Please give filename.");
|
throw std::runtime_error("Please give filename.");
|
||||||
|
|
||||||
uint32_t len;
|
Vase vase = Vase(std::string(argv[1]));
|
||||||
char *text;
|
|
||||||
read_file(path, &text, &len);
|
|
||||||
|
|
||||||
#endif
|
std::cout << "\n"
|
||||||
|
<< vase.to_string()
|
||||||
Vase vase = Vase(text, len);
|
<< "\n";
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-15
@@ -1,7 +1,8 @@
|
|||||||
#include "vase/buffer/append.h"
|
#include "vase/buffer/append.h"
|
||||||
|
|
||||||
AppendBuffer::AppendBuffer() {
|
AppendBuffer::AppendBuffer() {
|
||||||
new_text_chunk();
|
t_current = (TChunk *)malloc(sizeof(TChunk));
|
||||||
|
buf.push_back(t_current);
|
||||||
}
|
}
|
||||||
|
|
||||||
AppendBuffer::~AppendBuffer() {
|
AppendBuffer::~AppendBuffer() {
|
||||||
@@ -10,8 +11,11 @@ AppendBuffer::~AppendBuffer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AppendBuffer::key(char c) {
|
uint32_t AppendBuffer::key(char c) {
|
||||||
if (t_offset == CHUNK_SIZE)
|
if (t_offset == APPEND_CHUNK_SIZE) {
|
||||||
new_text_chunk();
|
t_current = (TChunk *)malloc(sizeof(TChunk));
|
||||||
|
buf.push_back(t_current);
|
||||||
|
t_offset = 0;
|
||||||
|
}
|
||||||
(*t_current)[t_offset++] = c;
|
(*t_current)[t_offset++] = c;
|
||||||
return current_offset++;
|
return current_offset++;
|
||||||
}
|
}
|
||||||
@@ -19,9 +23,12 @@ uint32_t AppendBuffer::key(char c) {
|
|||||||
uint32_t AppendBuffer::append(const char *text, uint32_t length) {
|
uint32_t AppendBuffer::append(const char *text, uint32_t length) {
|
||||||
uint32_t start = current_offset;
|
uint32_t start = current_offset;
|
||||||
while (length > 0) {
|
while (length > 0) {
|
||||||
if (t_offset == CHUNK_SIZE)
|
if (t_offset == APPEND_CHUNK_SIZE) {
|
||||||
new_text_chunk();
|
t_current = (TChunk *)malloc(sizeof(TChunk));
|
||||||
uint32_t copy = std::min(length, CHUNK_SIZE - t_offset);
|
buf.push_back(t_current);
|
||||||
|
t_offset = 0;
|
||||||
|
}
|
||||||
|
uint32_t copy = std::min(length, APPEND_CHUNK_SIZE - t_offset);
|
||||||
memcpy((*t_current) + t_offset, text, copy);
|
memcpy((*t_current) + t_offset, text, copy);
|
||||||
t_offset += copy;
|
t_offset += copy;
|
||||||
current_offset += copy;
|
current_offset += copy;
|
||||||
@@ -34,21 +41,15 @@ uint32_t AppendBuffer::append(const char *text, uint32_t length) {
|
|||||||
const char *AppendBuffer::read(uint32_t pos, uint32_t *out_len) {
|
const char *AppendBuffer::read(uint32_t pos, uint32_t *out_len) {
|
||||||
if (pos >= current_offset)
|
if (pos >= current_offset)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
uint32_t local_offset = pos % CHUNK_SIZE;
|
uint32_t local_offset = pos % APPEND_CHUNK_SIZE;
|
||||||
if (out_len) {
|
if (out_len) {
|
||||||
uint32_t remaining = current_offset - pos;
|
uint32_t remaining = current_offset - pos;
|
||||||
uint32_t until_chunk_end = CHUNK_SIZE - local_offset;
|
uint32_t until_chunk_end = APPEND_CHUNK_SIZE - local_offset;
|
||||||
*out_len = std::min(remaining, until_chunk_end);
|
*out_len = std::min(remaining, until_chunk_end);
|
||||||
}
|
}
|
||||||
return &(*buf[pos / CHUNK_SIZE])[local_offset];
|
return &(*buf[pos / APPEND_CHUNK_SIZE])[local_offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint32_t AppendBuffer::length() {
|
inline uint32_t AppendBuffer::length() {
|
||||||
return current_offset;
|
return current_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void AppendBuffer::new_text_chunk() {
|
|
||||||
t_current = (TChunk *)malloc(sizeof(TChunk));
|
|
||||||
buf.push_back(t_current);
|
|
||||||
t_offset = 0;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
#include "vase/buffer/original.h"
|
#include "vase/buffer/original.h"
|
||||||
|
#include "io/file.h"
|
||||||
|
|
||||||
OriginalBuffer::OriginalBuffer(char *buf, uint32_t len) : buf(buf), len(len) {}
|
OriginalBuffer::OriginalBuffer(std::string path) {
|
||||||
|
read_file(path.c_str(), &buf, &len);
|
||||||
|
}
|
||||||
|
|
||||||
OriginalBuffer::~OriginalBuffer() {
|
OriginalBuffer::~OriginalBuffer() {
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|||||||
+78
-25
@@ -1,6 +1,7 @@
|
|||||||
#include "vase/iterators/chunk.h"
|
#include "vase/iterators/chunk.h"
|
||||||
|
|
||||||
ChunkIterator::ChunkIterator(Shard *r) : root(r) {
|
ChunkIterator::ChunkIterator(Shard *r, Direction dir)
|
||||||
|
: dir(dir), root(r) {
|
||||||
if (!root)
|
if (!root)
|
||||||
return;
|
return;
|
||||||
Shard::retain(root);
|
Shard::retain(root);
|
||||||
@@ -14,42 +15,56 @@ void ChunkIterator::seek_offset(uint32_t offset) {
|
|||||||
stack.clear();
|
stack.clear();
|
||||||
petal = nullptr;
|
petal = nullptr;
|
||||||
petal_offset = 0;
|
petal_offset = 0;
|
||||||
global_offset = 0;
|
if (!root)
|
||||||
if (!root || offset >= root->length)
|
|
||||||
return;
|
return;
|
||||||
|
if (offset >= root->length)
|
||||||
|
offset = root->length - 1;
|
||||||
uint32_t target = offset;
|
uint32_t target = offset;
|
||||||
Shard *curr = root;
|
Shard *curr = root;
|
||||||
while (curr) {
|
while (curr) {
|
||||||
if (curr->kind == Shard::ShardKind::Petal) {
|
if (curr->kind == Shard::ShardKind::Petal) {
|
||||||
petal = (Petal *)curr;
|
petal = (Petal *)curr;
|
||||||
petal_offset = target;
|
petal_offset = target;
|
||||||
|
global_offset += target;
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
auto *b = (Branch *)curr;
|
auto *b = (Branch *)curr;
|
||||||
uint32_t left_len = b->left->length;
|
uint32_t left_len = b->left->length;
|
||||||
if (target < left_len) {
|
if (target < left_len) {
|
||||||
|
if (dir == Direction::Forward)
|
||||||
stack.push_back(b->right);
|
stack.push_back(b->right);
|
||||||
curr = b->left;
|
curr = b->left;
|
||||||
} else {
|
} else {
|
||||||
target -= left_len;
|
target -= left_len;
|
||||||
global_offset += b->left->length;
|
if (dir == Direction::Backward)
|
||||||
|
stack.push_back(b->left);
|
||||||
|
global_offset += left_len;
|
||||||
curr = b->right;
|
curr = b->right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
std::unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChunkIterator::seek_line(uint32_t line) {
|
void ChunkIterator::seek_line(uint32_t line) {
|
||||||
stack.clear();
|
stack.clear();
|
||||||
petal = nullptr;
|
petal = nullptr;
|
||||||
petal_offset = 0;
|
petal_offset = 0;
|
||||||
global_offset = 0;
|
bool last_line = false;
|
||||||
if (!root || line > root->lines)
|
if (!root)
|
||||||
return;
|
return;
|
||||||
|
if (line > root->lines + 1)
|
||||||
|
line = root->lines + 1;
|
||||||
|
if (line == root->lines + 1)
|
||||||
|
last_line = true;
|
||||||
Shard *curr = root;
|
Shard *curr = root;
|
||||||
while (curr) {
|
while (curr) {
|
||||||
if (curr->kind == Shard::ShardKind::Petal) {
|
if (curr->kind == Shard::ShardKind::Petal) {
|
||||||
petal = (Petal *)curr;
|
petal = (Petal *)curr;
|
||||||
|
if (last_line) {
|
||||||
|
petal_offset = petal->length;
|
||||||
|
global_offset += petal->length;
|
||||||
|
} else {
|
||||||
const char *text = nullptr;
|
const char *text = nullptr;
|
||||||
uint32_t remaining = 0;
|
uint32_t remaining = 0;
|
||||||
uint32_t offset = 0;
|
uint32_t offset = 0;
|
||||||
@@ -70,63 +85,101 @@ void ChunkIterator::seek_line(uint32_t line) {
|
|||||||
text = c + 1;
|
text = c + 1;
|
||||||
line--;
|
line--;
|
||||||
}
|
}
|
||||||
|
if (dir == Direction::Backward)
|
||||||
|
--offset;
|
||||||
petal_offset = offset;
|
petal_offset = offset;
|
||||||
|
global_offset += offset;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
auto *b = (Branch *)curr;
|
auto *b = (Branch *)curr;
|
||||||
uint32_t left_lines = b->left->lines;
|
uint32_t left_lines = b->left->lines;
|
||||||
if (line <= left_lines) {
|
if (line <= left_lines) {
|
||||||
|
if (dir == Direction::Forward)
|
||||||
stack.push_back(b->right);
|
stack.push_back(b->right);
|
||||||
curr = b->left;
|
curr = b->left;
|
||||||
} else {
|
} else {
|
||||||
line -= left_lines;
|
line -= left_lines;
|
||||||
|
if (dir == Direction::Backward)
|
||||||
|
stack.push_back(b->left);
|
||||||
global_offset += b->left->length;
|
global_offset += b->left->length;
|
||||||
curr = b->right;
|
curr = b->right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
std::unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ChunkIterator::byte_offset() {
|
uint32_t ChunkIterator::byte_offset() {
|
||||||
return global_offset + petal_offset;
|
return global_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChunkIterator::next(const char **data, uint32_t *out_len) {
|
bool ChunkIterator::next(const char **data, uint32_t *out_len) {
|
||||||
|
if (dir == Direction::Forward) {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (petal) {
|
if (petal) {
|
||||||
if (petal_offset < petal->length) {
|
if (petal_offset == petal->length) {
|
||||||
uint32_t remaining = petal->length - petal_offset;
|
|
||||||
uint32_t read_pos = petal->pos + petal_offset;
|
|
||||||
uint32_t got = 0;
|
|
||||||
const char *chunk = petal->source->read(read_pos, &got);
|
|
||||||
if (got == 0) {
|
|
||||||
petal_offset = petal->length;
|
|
||||||
petal = nullptr;
|
petal = nullptr;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
uint32_t remaining = petal->length - petal_offset;
|
||||||
|
uint32_t got = 0;
|
||||||
|
const char *chunk = petal->source->read(petal->pos + petal_offset, &got);
|
||||||
|
if (got == 0) {
|
||||||
|
petal = nullptr;
|
||||||
|
petal_offset = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
uint32_t take = std::min(got, remaining);
|
uint32_t take = std::min(got, remaining);
|
||||||
*data = chunk;
|
*data = chunk;
|
||||||
*out_len = take;
|
*out_len = take;
|
||||||
petal_offset += take;
|
petal_offset += take;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
global_offset += petal->length;
|
if (stack.empty())
|
||||||
petal = nullptr;
|
return false;
|
||||||
|
auto s = stack.back();
|
||||||
|
stack.pop_back();
|
||||||
|
while (s->kind == Shard::ShardKind::Branch) {
|
||||||
|
Branch *b = (Branch *)s;
|
||||||
|
stack.push_back(b->right);
|
||||||
|
s = b->left;
|
||||||
|
}
|
||||||
|
petal = (Petal *)s;
|
||||||
petal_offset = 0;
|
petal_offset = 0;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
while (true) {
|
||||||
|
if (petal) {
|
||||||
|
if (petal_offset == 0) {
|
||||||
|
petal = nullptr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint32_t got = 0;
|
||||||
|
*data = petal->source->read(petal->pos, &got);
|
||||||
|
if (petal_offset > got) {
|
||||||
|
uint32_t got2 = 0;
|
||||||
|
*data = petal->source->read(petal->pos + got, &got2);
|
||||||
|
*out_len = std::min(got2, petal_offset - got);
|
||||||
|
petal_offset = got;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
*out_len = std::min(got, petal_offset);
|
||||||
|
petal_offset = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (stack.empty())
|
if (stack.empty())
|
||||||
return false;
|
return false;
|
||||||
Shard *s = stack.back();
|
auto s = stack.back();
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
if (!s)
|
while (s->kind == Shard::ShardKind::Branch) {
|
||||||
continue;
|
Branch *b = (Branch *)s;
|
||||||
if (s->kind == Shard::ShardKind::Petal) {
|
|
||||||
petal = (Petal *)s;
|
|
||||||
petal_offset = 0;
|
|
||||||
} else {
|
|
||||||
auto *b = (Branch *)s;
|
|
||||||
stack.push_back(b->right);
|
|
||||||
stack.push_back(b->left);
|
stack.push_back(b->left);
|
||||||
|
s = b->right;
|
||||||
|
}
|
||||||
|
petal = (Petal *)s;
|
||||||
|
petal_offset = petal->length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+53
-28
@@ -1,40 +1,65 @@
|
|||||||
#include "vase/iterators/line.h"
|
#include "vase/iterators/line.h"
|
||||||
|
|
||||||
LineIterator::LineIterator(Shard *r, uint32_t start_line) : it(r) {
|
LineIterator::LineIterator(Shard *r, uint32_t start_line, Direction dir)
|
||||||
it.seek_line(start_line);
|
: it(r, dir), dir(dir) {
|
||||||
|
it.seek_line(start_line + (dir == Direction::Backward));
|
||||||
|
if (!it.next(&chunk, &len))
|
||||||
|
chunk = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t LineIterator::byte_offset() {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LineIterator::next(std::string &line) {
|
bool LineIterator::next(std::string &line) {
|
||||||
line.clear();
|
line.clear();
|
||||||
line_offset = it.byte_offset() - remaining;
|
if (!chunk)
|
||||||
|
return false;
|
||||||
|
if (dir == Direction::Forward) {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (remaining == 0) {
|
const char *nl = (const char *)memchr(chunk, '\n', len);
|
||||||
uint32_t got;
|
if (!nl) {
|
||||||
if (!it.next(&cursor, &got)) {
|
if (len && chunk[len - 1] == '\r')
|
||||||
eof = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
line_offset = it.byte_offset() - remaining;
|
|
||||||
remaining = got;
|
|
||||||
}
|
|
||||||
const char *nl = (const char *)memchr(cursor, '\n', remaining);
|
|
||||||
if (nl) {
|
|
||||||
const char *end = nl;
|
|
||||||
uint32_t len = nl - cursor;
|
|
||||||
if (len && cursor[len - 1] == '\r')
|
|
||||||
--len;
|
--len;
|
||||||
line.append(cursor, len);
|
line.append(chunk, len);
|
||||||
cursor = end + 1;
|
if (!it.next(&chunk, &len)) {
|
||||||
remaining -= (uint32_t)(len + 1);
|
chunk = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const char *end = nl;
|
||||||
|
if (end > chunk && *(end - 1) == '\r')
|
||||||
|
--end;
|
||||||
|
line.append(chunk, end);
|
||||||
|
uint32_t consumed = (nl - chunk) + 1;
|
||||||
|
chunk += consumed;
|
||||||
|
len -= consumed;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (true) {
|
||||||
|
const char *nl = (const char *)memrchr(chunk, '\n', len);
|
||||||
|
if (!nl) {
|
||||||
|
if (len && chunk[len - 1] == '\n')
|
||||||
|
--len;
|
||||||
|
if (len && chunk[len - 1] == '\r')
|
||||||
|
--len;
|
||||||
|
line.insert(0, chunk, len);
|
||||||
|
if (!it.next(&chunk, &len)) {
|
||||||
|
chunk = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const char *end = chunk + len;
|
||||||
|
const char *start = nl + 1;
|
||||||
|
const char *line_end = end;
|
||||||
|
if (line_end > start && *(line_end - 1) == '\r')
|
||||||
|
--line_end;
|
||||||
|
line.insert(0, start, line_end - start);
|
||||||
|
len = nl - chunk;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
line.append(cursor, remaining);
|
|
||||||
cursor += remaining;
|
|
||||||
remaining = 0;
|
|
||||||
}
|
}
|
||||||
return !line.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t LineIterator::byte_offset() {
|
|
||||||
return line_offset;
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -83,7 +83,7 @@ std::vector<RegexMatch> regex_search(
|
|||||||
|
|
||||||
pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(re, NULL);
|
pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(re, NULL);
|
||||||
|
|
||||||
ChunkIterator it(root);
|
ChunkIterator it(root, Direction::Forward);
|
||||||
it.seek_offset(start_offset);
|
it.seek_offset(start_offset);
|
||||||
|
|
||||||
const char *data;
|
const char *data;
|
||||||
|
|||||||
+3
-4
@@ -195,7 +195,7 @@ Shard *merge_leaves(Shard *a, Shard *b) {
|
|||||||
Shard *append_leaf(Shard *root, Shard *leaf) {
|
Shard *append_leaf(Shard *root, Shard *leaf) {
|
||||||
if (!root)
|
if (!root)
|
||||||
return leaf;
|
return leaf;
|
||||||
if (root->kind == Shard::ShardKind::Petal && root->length < 32 * 1024)
|
if (root->kind == Shard::ShardKind::Petal && root->length < PETAL_SIZE_MAX)
|
||||||
return merge_leaves(root, leaf);
|
return merge_leaves(root, leaf);
|
||||||
Branch *b = (Branch *)root;
|
Branch *b = (Branch *)root;
|
||||||
auto new_right = append_leaf(b->right, leaf);
|
auto new_right = append_leaf(b->right, leaf);
|
||||||
@@ -221,16 +221,15 @@ Shard *build_balanced(Shard **pieces, uint32_t lo, uint32_t hi) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Shard *create_shards(Buffer *o) {
|
Shard *create_shards(Buffer *o) {
|
||||||
constexpr uint32_t PETAL_SIZE = 32 * 1024;
|
|
||||||
std::vector<Shard *> pieces;
|
std::vector<Shard *> pieces;
|
||||||
uint32_t pos = 0;
|
uint32_t pos = 0;
|
||||||
const uint32_t total = o->length();
|
const uint32_t total = o->length();
|
||||||
pieces.reserve((total + PETAL_SIZE - 1) / PETAL_SIZE);
|
pieces.reserve((total + PETAL_SIZE_MAX - 1) / PETAL_SIZE_MAX);
|
||||||
|
|
||||||
while (pos < total) {
|
while (pos < total) {
|
||||||
uint32_t len = 0;
|
uint32_t len = 0;
|
||||||
const char *data = o->read(pos, &len);
|
const char *data = o->read(pos, &len);
|
||||||
uint32_t take = std::min(len, PETAL_SIZE);
|
uint32_t take = std::min(len, PETAL_SIZE_MAX);
|
||||||
uint32_t lines = 0;
|
uint32_t lines = 0;
|
||||||
const char *p = data;
|
const char *p = data;
|
||||||
const char *end = data + take;
|
const char *end = data + take;
|
||||||
|
|||||||
+192
-24
@@ -3,12 +3,18 @@
|
|||||||
#include "vase/iterators/line.h"
|
#include "vase/iterators/line.h"
|
||||||
#include "vase/search.h"
|
#include "vase/search.h"
|
||||||
|
|
||||||
Vase::Vase(char *data, uint32_t length)
|
Vase::Vase(std::string path)
|
||||||
: original(data, length), append(),
|
: original(path), append(),
|
||||||
root(create_shards(&original)) {}
|
root(create_shards(&original)) {
|
||||||
|
history.push_back(root);
|
||||||
|
Shard::retain(root);
|
||||||
|
history_top = 0;
|
||||||
|
}
|
||||||
|
|
||||||
Vase::~Vase() {
|
Vase::~Vase() {
|
||||||
Shard::release(root);
|
Shard::release(root);
|
||||||
|
for (auto s : history)
|
||||||
|
Shard::release(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Vase::length() {
|
uint32_t Vase::length() {
|
||||||
@@ -21,13 +27,56 @@ std::string Vase::to_string() {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vase::input(Point *point, char key) {
|
LineIterator Vase::iterate(uint32_t line) {
|
||||||
|
return LineIterator(root, line, Direction::Forward);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Vase::undo() {
|
||||||
|
if (history_top == 0)
|
||||||
|
return false;
|
||||||
|
Shard::release(root);
|
||||||
|
history_top--;
|
||||||
|
root = history[history_top];
|
||||||
|
Shard::retain(root);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Vase::redo() {
|
||||||
|
if (history_top + 1 >= history.size())
|
||||||
|
return false;
|
||||||
|
Shard::release(root);
|
||||||
|
history_top++;
|
||||||
|
root = history[history_top];
|
||||||
|
Shard::retain(root);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vase::snapshot() {
|
||||||
|
if (history[history_top] == root)
|
||||||
|
return;
|
||||||
|
while (history.size() > history_top + 1) {
|
||||||
|
Shard::release(history.back());
|
||||||
|
history.pop_back();
|
||||||
|
}
|
||||||
|
Shard::retain(root);
|
||||||
|
history.push_back(root);
|
||||||
|
history_top++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vase::prune_history(uint32_t n) {
|
||||||
|
n = std::min(n, history_top);
|
||||||
|
for (uint32_t i = 0; i < n; ++i)
|
||||||
|
Shard::release(history[i]);
|
||||||
|
history.erase(history.begin(), history.begin() + n);
|
||||||
|
history_top -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vase::insert(Point *point, char key) {
|
||||||
if (key == '\n')
|
if (key == '\n')
|
||||||
*point = {point->row + 1, 0};
|
*point = {point->row + 1, 0};
|
||||||
else
|
else
|
||||||
point->col++;
|
point->col++;
|
||||||
uint32_t pos = append.key(key);
|
uint32_t pos = append.key(key);
|
||||||
// limit petal size to 32KiB here later.
|
|
||||||
Shard *inserted = new Petal(1, key == '\n', &append, pos);
|
Shard *inserted = new Petal(1, key == '\n', &append, pos);
|
||||||
auto [left, right] = split_shard(root, offset_of(*point));
|
auto [left, right] = split_shard(root, offset_of(*point));
|
||||||
Shard *left2 = append_leaf(left, inserted);
|
Shard *left2 = append_leaf(left, inserted);
|
||||||
@@ -41,6 +90,15 @@ void Vase::input(Point *point, char key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Vase::insert(Point *point, const char *data, uint32_t len) {
|
void Vase::insert(Point *point, const char *data, uint32_t len) {
|
||||||
|
while (len) {
|
||||||
|
uint32_t chunk_size = std::min<uint32_t>(len, PETAL_SIZE_MAX);
|
||||||
|
_insert(point, data, chunk_size);
|
||||||
|
len -= chunk_size;
|
||||||
|
data += chunk_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vase::_insert(Point *point, const char *data, uint32_t len) {
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
return;
|
return;
|
||||||
uint32_t offset = offset_of(*point);
|
uint32_t offset = offset_of(*point);
|
||||||
@@ -80,20 +138,20 @@ void Vase::insert(Point *point, const char *data, uint32_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Vase::erase(Point *point, int64_t amount) {
|
void Vase::erase(Point *point, int64_t amount) {
|
||||||
// wrong: havta make amount be in clusters not bytes
|
|
||||||
uint32_t offset = offset_of(*point);
|
|
||||||
if (amount == 0)
|
if (amount == 0)
|
||||||
return;
|
return;
|
||||||
uint32_t start;
|
Point start = *point;
|
||||||
uint32_t count;
|
Point end = *point;
|
||||||
if (amount < 0) {
|
if (amount < 0)
|
||||||
count = std::min<uint32_t>(-amount, offset);
|
move_clusters(&start, amount);
|
||||||
start = offset - count;
|
else
|
||||||
} else {
|
move_clusters(&end, amount);
|
||||||
start = offset;
|
uint32_t start_offset = offset_of(start);
|
||||||
count = amount;
|
uint32_t end_offset = offset_of(end);
|
||||||
}
|
if (start_offset > end_offset)
|
||||||
auto [a, b] = split_shard(root, start);
|
std::swap(start_offset, end_offset);
|
||||||
|
uint32_t count = end_offset - start_offset;
|
||||||
|
auto [a, b] = split_shard(root, start_offset);
|
||||||
auto [d, c] = split_shard(b, count);
|
auto [d, c] = split_shard(b, count);
|
||||||
Shard *new_root = concat_shard(a, c);
|
Shard *new_root = concat_shard(a, c);
|
||||||
Shard::release(a);
|
Shard::release(a);
|
||||||
@@ -102,6 +160,30 @@ void Vase::erase(Point *point, int64_t amount) {
|
|||||||
Shard::release(d);
|
Shard::release(d);
|
||||||
Shard::release(root);
|
Shard::release(root);
|
||||||
root = new_root;
|
root = new_root;
|
||||||
|
if (amount < 0)
|
||||||
|
*point = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vase::erase(Range range) {
|
||||||
|
Point start = range.start;
|
||||||
|
Point end = range.end;
|
||||||
|
uint32_t start_offset = offset_of(start);
|
||||||
|
uint32_t end_offset = offset_of(end);
|
||||||
|
uint32_t count = end_offset - start_offset;
|
||||||
|
auto [a, b] = split_shard(root, start_offset);
|
||||||
|
auto [d, c] = split_shard(b, count);
|
||||||
|
Shard *new_root = concat_shard(a, c);
|
||||||
|
Shard::release(a);
|
||||||
|
Shard::release(b);
|
||||||
|
Shard::release(c);
|
||||||
|
Shard::release(d);
|
||||||
|
Shard::release(root);
|
||||||
|
root = new_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vase::replace(Range range, const char *data, uint32_t len) {
|
||||||
|
erase(range);
|
||||||
|
insert(&range.start, data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vase::flatten(Shard *s, std::string &out) {
|
void Vase::flatten(Shard *s, std::string &out) {
|
||||||
@@ -125,7 +207,7 @@ void Vase::flatten(Shard *s, std::string &out) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Vase::offset_of(Point point) {
|
uint32_t Vase::offset_of(Point point) {
|
||||||
LineIterator it(root, point.row);
|
LineIterator it(root, point.row, Direction::Forward);
|
||||||
std::string line;
|
std::string line;
|
||||||
uint32_t offset = 0;
|
uint32_t offset = 0;
|
||||||
if (it.next(line)) {
|
if (it.next(line)) {
|
||||||
@@ -142,14 +224,100 @@ uint32_t Vase::offset_of(Point point) {
|
|||||||
return it.byte_offset() + offset;
|
return it.byte_offset() + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Vase::jump(Point *, int64_t) {
|
void Vase::move_clusters(Point *point, int64_t amount) {
|
||||||
// todo.
|
if (amount == 0)
|
||||||
return false;
|
return;
|
||||||
|
if (amount < 0) {
|
||||||
|
amount = -amount;
|
||||||
|
LineIterator it(root, point->row, Direction::Backward);
|
||||||
|
while (amount > 0) {
|
||||||
|
std::string line;
|
||||||
|
if (!it.next(line))
|
||||||
|
return;
|
||||||
|
std::vector<uint32_t> clusters;
|
||||||
|
const char *ptr = line.data();
|
||||||
|
uint32_t remaining = line.size();
|
||||||
|
uint32_t byte = 0;
|
||||||
|
while (remaining) {
|
||||||
|
clusters.push_back(byte);
|
||||||
|
uint32_t len =
|
||||||
|
grapheme_next_character_break_utf8(ptr, remaining);
|
||||||
|
ptr += len;
|
||||||
|
remaining -= len;
|
||||||
|
byte += len;
|
||||||
|
}
|
||||||
|
while (amount > 0 && point->col > 0) {
|
||||||
|
point->col--;
|
||||||
|
amount--;
|
||||||
|
}
|
||||||
|
if (amount == 0)
|
||||||
|
return;
|
||||||
|
if (point->row == 0)
|
||||||
|
return;
|
||||||
|
point->row--;
|
||||||
|
point->col = clusters.size();
|
||||||
|
amount--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LineIterator it(root, point->row, Direction::Forward);
|
||||||
|
while (amount > 0) {
|
||||||
|
std::string line;
|
||||||
|
if (!it.next(line))
|
||||||
|
return;
|
||||||
|
if (point->col > 0 || amount > 0) {
|
||||||
|
const char *ptr = line.data();
|
||||||
|
uint32_t remaining = line.size();
|
||||||
|
uint32_t col = 0;
|
||||||
|
while (col < point->col && remaining) {
|
||||||
|
uint32_t len = grapheme_next_character_break_utf8(ptr, remaining);
|
||||||
|
ptr += len;
|
||||||
|
remaining -= len;
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
while (amount > 0 && remaining) {
|
||||||
|
uint32_t len = grapheme_next_character_break_utf8(ptr, remaining);
|
||||||
|
ptr += len;
|
||||||
|
remaining -= len;
|
||||||
|
point->col++;
|
||||||
|
amount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (amount > 0) {
|
||||||
|
point->row++;
|
||||||
|
point->col = 0;
|
||||||
|
amount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Vase::clamp(Point *) {
|
void Vase::clamp(Point *point) {
|
||||||
// todo.
|
if (point->row > root->lines)
|
||||||
return false;
|
point->row = root->lines;
|
||||||
|
LineIterator it(root, point->row, Direction::Forward);
|
||||||
|
std::string line;
|
||||||
|
if (!it.next(line)) {
|
||||||
|
point->col = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t clusters = 0;
|
||||||
|
const char *ptr = line.data();
|
||||||
|
uint32_t remaining = line.size();
|
||||||
|
while (remaining) {
|
||||||
|
uint32_t len = grapheme_next_character_break_utf8(ptr, remaining);
|
||||||
|
ptr += len;
|
||||||
|
remaining -= len;
|
||||||
|
clusters++;
|
||||||
|
}
|
||||||
|
if (point->col > clusters)
|
||||||
|
point->col = clusters;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vase::move_lines(Point *point, int64_t amount) {
|
||||||
|
if (point->row + amount < 0)
|
||||||
|
return;
|
||||||
|
point->row += amount;
|
||||||
|
clamp(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ReplacePart {
|
struct ReplacePart {
|
||||||
|
|||||||
Reference in New Issue
Block a user