Perfect
- Make original buffer use a disk backed mapping to save memory. - rearrange api function headers for search etc. - fix bugs with iterators. - support uint64_t for all byte and line offsets to allow for much larger files to be opened. - and more minor bug fixes.
This commit is contained in:
@@ -40,9 +40,12 @@ extern "C" {
|
|||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
#include <stdexcept>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
|||||||
@@ -8,16 +8,16 @@ struct AppendBuffer : Buffer {
|
|||||||
using TChunk = char[APPEND_CHUNK_SIZE];
|
using TChunk = char[APPEND_CHUNK_SIZE];
|
||||||
std::vector<TChunk *> buf;
|
std::vector<TChunk *> buf;
|
||||||
TChunk *t_current;
|
TChunk *t_current;
|
||||||
uint32_t current_offset{0};
|
uint64_t current_offset{0};
|
||||||
uint32_t t_offset{0};
|
uint64_t t_offset{0};
|
||||||
|
|
||||||
AppendBuffer();
|
AppendBuffer();
|
||||||
|
|
||||||
~AppendBuffer();
|
~AppendBuffer();
|
||||||
|
|
||||||
uint32_t key(char c);
|
uint64_t append(char c);
|
||||||
uint32_t append(const char *text, uint32_t length);
|
uint64_t append(const char *text, uint64_t length);
|
||||||
|
|
||||||
const char *read(uint32_t pos, uint32_t *out_len);
|
const char *read(uint64_t pos, uint64_t *out_len);
|
||||||
inline uint32_t length();
|
inline uint64_t length();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
struct Buffer {
|
struct Buffer {
|
||||||
virtual const char *read(uint32_t pos, uint32_t *out_len) = 0;
|
virtual const char *read(uint64_t pos, uint64_t *out_len) = 0;
|
||||||
virtual inline uint32_t length() = 0;
|
virtual inline uint64_t length() = 0;
|
||||||
virtual ~Buffer() = default;
|
virtual ~Buffer() = default;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,14 +4,14 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
struct OriginalBuffer : Buffer {
|
struct OriginalBuffer : Buffer {
|
||||||
char *buf;
|
const char *buf;
|
||||||
uint32_t len;
|
uint64_t len;
|
||||||
|
int fd = -1;
|
||||||
// Add 2 modes here, normal and lazy, lazy copies the file to a safe location and uses mmap.
|
|
||||||
OriginalBuffer(std::string path);
|
|
||||||
|
|
||||||
|
OriginalBuffer();
|
||||||
~OriginalBuffer();
|
~OriginalBuffer();
|
||||||
|
|
||||||
const char *read(uint32_t pos, uint32_t *out_len);
|
void initialize();
|
||||||
uint32_t length();
|
const char *read(uint64_t pos, uint64_t *out_len);
|
||||||
|
uint64_t length();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
constexpr uint32_t APPEND_CHUNK_SIZE = 64 * 1024;
|
constexpr uint64_t APPEND_CHUNK_SIZE = 64 * 1024;
|
||||||
constexpr uint32_t PETAL_SIZE_MAX = 32 * 1024;
|
constexpr uint64_t PETAL_SIZE_MAX = 32 * 1024;
|
||||||
|
|
||||||
static_assert(
|
static_assert(
|
||||||
APPEND_CHUNK_SIZE > PETAL_SIZE_MAX,
|
APPEND_CHUNK_SIZE > PETAL_SIZE_MAX,
|
||||||
|
|||||||
@@ -13,15 +13,18 @@ struct ChunkIterator {
|
|||||||
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;
|
uint64_t petal_offset = 0;
|
||||||
uint32_t global_offset = 0;
|
uint64_t global_offset = 0;
|
||||||
|
uint64_t global_line = 0;
|
||||||
|
bool at_end = false;
|
||||||
|
|
||||||
ChunkIterator(Shard *r, Direction dir);
|
ChunkIterator(Shard *r, Direction dir);
|
||||||
|
|
||||||
~ChunkIterator();
|
~ChunkIterator();
|
||||||
|
|
||||||
void seek_offset(uint32_t offset);
|
void seek_offset(uint64_t offset);
|
||||||
void seek_line(uint32_t offset);
|
void seek_line(uint64_t offset);
|
||||||
bool next(const char **data, uint32_t *out_len);
|
bool next(const char **data, uint64_t *out_len);
|
||||||
uint32_t byte_offset();
|
uint64_t byte_offset();
|
||||||
|
uint64_t line_offset();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,11 +9,14 @@ struct LineIterator {
|
|||||||
Direction dir;
|
Direction dir;
|
||||||
|
|
||||||
const char *chunk;
|
const char *chunk;
|
||||||
uint32_t len;
|
uint64_t len;
|
||||||
|
uint64_t offset = 0;
|
||||||
|
uint64_t chunk_offset = 0;
|
||||||
|
bool at_end = false;
|
||||||
|
|
||||||
LineIterator(Shard *r, uint32_t start_line, Direction dir);
|
LineIterator(Shard *r, uint64_t start_line, Direction dir);
|
||||||
~LineIterator() = default;
|
~LineIterator() = default;
|
||||||
|
|
||||||
bool next(std::string &line);
|
bool next(std::string &line);
|
||||||
uint32_t byte_offset();
|
uint64_t byte_offset();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "pch.h"
|
|
||||||
#include "vase/shard.h"
|
|
||||||
|
|
||||||
struct RegexGroup {
|
|
||||||
uint32_t start{UINT32_MAX};
|
|
||||||
uint32_t end{UINT32_MAX};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RegexMatch {
|
|
||||||
uint32_t start;
|
|
||||||
uint32_t end;
|
|
||||||
RegexGroup groups[9]{};
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<RegexMatch> regex_search(
|
|
||||||
Shard *root, std::string_view pattern_str,
|
|
||||||
uint32_t start_offset, uint32_t end_offset,
|
|
||||||
std::string_view options
|
|
||||||
);
|
|
||||||
+10
-9
@@ -14,12 +14,12 @@ struct Shard {
|
|||||||
|
|
||||||
uint8_t height;
|
uint8_t height;
|
||||||
|
|
||||||
uint32_t length;
|
uint64_t length;
|
||||||
uint32_t lines;
|
uint64_t lines;
|
||||||
|
|
||||||
std::atomic_uint32_t refs;
|
std::atomic_uint64_t refs;
|
||||||
|
|
||||||
Shard(ShardKind kind, uint32_t length, uint32_t lines, uint8_t height)
|
Shard(ShardKind kind, uint64_t length, uint64_t lines, uint8_t height)
|
||||||
: kind(kind), height(height), length(length), lines(lines), refs(1) {};
|
: kind(kind), height(height), length(length), lines(lines), refs(1) {};
|
||||||
|
|
||||||
static void retain(Shard *n);
|
static void retain(Shard *n);
|
||||||
@@ -46,18 +46,19 @@ struct Branch : Shard {
|
|||||||
struct Petal : Shard {
|
struct Petal : Shard {
|
||||||
Buffer *source;
|
Buffer *source;
|
||||||
|
|
||||||
uint32_t pos;
|
uint64_t pos;
|
||||||
|
|
||||||
Petal(uint32_t length, uint32_t lines, Buffer *source, uint32_t pos)
|
Petal(uint64_t length, uint64_t lines, Buffer *source, uint64_t pos)
|
||||||
: Shard(ShardKind::Petal, length, lines, 1),
|
: Shard(ShardKind::Petal, length, lines, 1),
|
||||||
source(source), pos(pos) {};
|
source(source), pos(pos) {};
|
||||||
};
|
};
|
||||||
|
|
||||||
Shard *create_shards(Buffer *o);
|
Shard *create_file_shards(std::string &path, OriginalBuffer *b);
|
||||||
|
Shard *create_swap_shards(std::string &path, OriginalBuffer *b);
|
||||||
|
|
||||||
std::pair<Shard *, Shard *> split_shard(Shard *n, uint32_t offset);
|
std::pair<Shard *, Shard *> split_shard(Shard *n, uint64_t offset);
|
||||||
Shard *concat_shard(Shard *left, Shard *right);
|
Shard *concat_shard(Shard *left, Shard *right);
|
||||||
Shard *merge(Shard *a, Shard *b);
|
Shard *merge(Shard *a, Shard *b);
|
||||||
Shard *merge_leaves(Shard *a, Shard *b);
|
Shard *merge_leaves(Shard *a, Shard *b);
|
||||||
Shard *append_leaf(Shard *root, Shard *leaf);
|
Shard *append_leaf(Shard *root, Shard *leaf);
|
||||||
Shard *build_balanced(Shard **pieces, uint32_t lo, uint32_t hi);
|
Shard *build_balanced(Shard **pieces, uint64_t lo, uint64_t hi);
|
||||||
|
|||||||
+52
-17
@@ -10,8 +10,8 @@
|
|||||||
#include "utils/utils.h"
|
#include "utils/utils.h"
|
||||||
|
|
||||||
struct Point {
|
struct Point {
|
||||||
uint32_t row;
|
uint64_t row;
|
||||||
uint32_t col;
|
uint64_t col;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Range {
|
struct Range {
|
||||||
@@ -19,30 +19,52 @@ struct Range {
|
|||||||
Point end;
|
Point end;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct RegexGroup {
|
||||||
|
uint64_t start{UINT32_MAX};
|
||||||
|
uint64_t end{UINT32_MAX};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RegexMatch {
|
||||||
|
uint64_t start;
|
||||||
|
uint64_t end;
|
||||||
|
RegexGroup groups[9]{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReplacePart {
|
||||||
|
enum struct PartType {
|
||||||
|
FullMatch,
|
||||||
|
CaptureGroup,
|
||||||
|
Constant
|
||||||
|
} type;
|
||||||
|
|
||||||
|
std::variant<uint8_t, Shard *> value;
|
||||||
|
};
|
||||||
|
|
||||||
struct Vase {
|
struct Vase {
|
||||||
OriginalBuffer original;
|
OriginalBuffer *original;
|
||||||
AppendBuffer append;
|
AppendBuffer *append;
|
||||||
Shard *root;
|
Shard *root;
|
||||||
|
|
||||||
Vase(std::string path);
|
Vase(std::string path);
|
||||||
|
|
||||||
~Vase();
|
~Vase();
|
||||||
|
|
||||||
uint32_t length();
|
uint64_t length();
|
||||||
std::string to_string();
|
std::string to_string();
|
||||||
|
std::string to_string(Range range);
|
||||||
|
|
||||||
Point resolve_column(uint32_t line, uint32_t column);
|
Point resolve_column(uint64_t line, uint64_t column);
|
||||||
|
|
||||||
LineIterator iterate(uint32_t line);
|
LineIterator iterate(uint64_t line);
|
||||||
|
|
||||||
void insert(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, uint64_t len);
|
||||||
void erase(Point *point, int64_t amount);
|
void erase(Point *point, uint64_t amount, Direction dir);
|
||||||
void erase(Range range);
|
void erase(Range range);
|
||||||
void replace(Range range, const char *data, uint32_t len);
|
void replace(Range range, const char *data, uint64_t len);
|
||||||
|
|
||||||
void move_clusters(Point *point, int64_t amount);
|
void move_clusters(Point *point, uint64_t amount, Direction dir);
|
||||||
void move_lines(Point *point, int64_t amount);
|
void move_lines(Point *point, uint64_t amount, Direction dir);
|
||||||
void clamp(Point *point);
|
void clamp(Point *point);
|
||||||
|
|
||||||
void regex_search_replace(
|
void regex_search_replace(
|
||||||
@@ -50,17 +72,30 @@ struct Vase {
|
|||||||
std::string_view replace, std::string_view options
|
std::string_view replace, std::string_view options
|
||||||
);
|
);
|
||||||
|
|
||||||
|
std::vector<Range> regex_search(
|
||||||
|
std::string_view pattern, Range range, std::string_view options
|
||||||
|
);
|
||||||
|
|
||||||
bool undo();
|
bool undo();
|
||||||
bool redo();
|
bool redo();
|
||||||
|
|
||||||
void snapshot();
|
void snapshot();
|
||||||
void prune_history(uint32_t n);
|
void prune_history(uint64_t n);
|
||||||
|
|
||||||
|
bool save(std::string path);
|
||||||
|
bool save_swap(std::string path);
|
||||||
|
|
||||||
|
uint64_t offset_of(Point point);
|
||||||
|
Point point_of(uint64_t offset);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Shard *> history;
|
std::vector<Shard *> history;
|
||||||
uint32_t history_top;
|
uint64_t history_top;
|
||||||
|
|
||||||
uint32_t offset_of(Point point);
|
void _insert(Point *point, const char *data, uint64_t len);
|
||||||
static void flatten(Shard *s, std::string &out);
|
|
||||||
void _insert(Point *point, const char *data, uint32_t len);
|
std::vector<ReplacePart> parse_replace(std::string_view s);
|
||||||
|
std::vector<RegexMatch> _regex_search(
|
||||||
|
std::string_view pattern, Range range, std::string_view options
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+5
-1
@@ -9,8 +9,12 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
Vase vase = Vase(std::string(argv[1]));
|
Vase vase = Vase(std::string(argv[1]));
|
||||||
|
|
||||||
|
auto matches = vase.regex_search("hello", {{0, 0}, {vase.root->lines, UINT64_MAX}}, "g");
|
||||||
|
|
||||||
std::cout << "\n"
|
std::cout << "\n"
|
||||||
<< vase.to_string()
|
<< (int)vase.length() << " bytes\n"
|
||||||
|
<< (int)vase.root->lines << " lines\n"
|
||||||
|
<< (int)matches.size() << " matches\n"
|
||||||
<< "\n";
|
<< "\n";
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ AppendBuffer::~AppendBuffer() {
|
|||||||
free(p);
|
free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AppendBuffer::key(char c) {
|
uint64_t AppendBuffer::append(char c) {
|
||||||
if (t_offset == APPEND_CHUNK_SIZE) {
|
if (t_offset == APPEND_CHUNK_SIZE) {
|
||||||
t_current = (TChunk *)malloc(sizeof(TChunk));
|
t_current = (TChunk *)malloc(sizeof(TChunk));
|
||||||
buf.push_back(t_current);
|
buf.push_back(t_current);
|
||||||
@@ -20,15 +20,15 @@ uint32_t AppendBuffer::key(char c) {
|
|||||||
return current_offset++;
|
return current_offset++;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AppendBuffer::append(const char *text, uint32_t length) {
|
uint64_t AppendBuffer::append(const char *text, uint64_t length) {
|
||||||
uint32_t start = current_offset;
|
uint64_t start = current_offset;
|
||||||
while (length > 0) {
|
while (length > 0) {
|
||||||
if (t_offset == APPEND_CHUNK_SIZE) {
|
if (t_offset == APPEND_CHUNK_SIZE) {
|
||||||
t_current = (TChunk *)malloc(sizeof(TChunk));
|
t_current = (TChunk *)malloc(sizeof(TChunk));
|
||||||
buf.push_back(t_current);
|
buf.push_back(t_current);
|
||||||
t_offset = 0;
|
t_offset = 0;
|
||||||
}
|
}
|
||||||
uint32_t copy = std::min(length, APPEND_CHUNK_SIZE - t_offset);
|
uint64_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;
|
||||||
@@ -38,18 +38,18 @@ uint32_t AppendBuffer::append(const char *text, uint32_t length) {
|
|||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *AppendBuffer::read(uint32_t pos, uint32_t *out_len) {
|
const char *AppendBuffer::read(uint64_t pos, uint64_t *out_len) {
|
||||||
if (pos >= current_offset)
|
if (pos >= current_offset)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
uint32_t local_offset = pos % APPEND_CHUNK_SIZE;
|
uint64_t local_offset = pos % APPEND_CHUNK_SIZE;
|
||||||
if (out_len) {
|
if (out_len) {
|
||||||
uint32_t remaining = current_offset - pos;
|
uint64_t remaining = current_offset - pos;
|
||||||
uint32_t until_chunk_end = APPEND_CHUNK_SIZE - local_offset;
|
uint64_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 / APPEND_CHUNK_SIZE])[local_offset];
|
return &(*buf[pos / APPEND_CHUNK_SIZE])[local_offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint32_t AppendBuffer::length() {
|
inline uint64_t AppendBuffer::length() {
|
||||||
return current_offset;
|
return current_offset;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,36 @@
|
|||||||
#include "vase/buffer/original.h"
|
#include "vase/buffer/original.h"
|
||||||
#include "io/file.h"
|
#include "io/file.h"
|
||||||
|
|
||||||
OriginalBuffer::OriginalBuffer(std::string path) {
|
OriginalBuffer::OriginalBuffer() {
|
||||||
read_file(path.c_str(), &buf, &len);
|
char tmp_path[] = "/tmp/tbuf.XXXXXX";
|
||||||
|
fd = mkstemp(tmp_path);
|
||||||
|
if (fd == -1)
|
||||||
|
exit(1);
|
||||||
|
unlink(tmp_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
OriginalBuffer::~OriginalBuffer() {
|
OriginalBuffer::~OriginalBuffer() {
|
||||||
free(buf);
|
if (buf)
|
||||||
|
munmap((char *)buf, len);
|
||||||
|
if (fd != -1)
|
||||||
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *OriginalBuffer::read(uint32_t pos, uint32_t *out_len) {
|
void OriginalBuffer::initialize() {
|
||||||
|
struct stat st;
|
||||||
|
if (fstat(fd, &st) == -1)
|
||||||
|
throw std::runtime_error("fstat failed");
|
||||||
|
len = (uint64_t)st.st_size;
|
||||||
|
buf = (const char *)mmap(nullptr, len, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||||
|
if (buf == MAP_FAILED) {
|
||||||
|
buf = nullptr;
|
||||||
|
throw std::runtime_error("mmap failed");
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *OriginalBuffer::read(uint64_t pos, uint64_t *out_len) {
|
||||||
if (pos >= len)
|
if (pos >= len)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (out_len)
|
if (out_len)
|
||||||
@@ -17,6 +38,6 @@ const char *OriginalBuffer::read(uint32_t pos, uint32_t *out_len) {
|
|||||||
return buf + pos;
|
return buf + pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t OriginalBuffer::length() {
|
uint64_t OriginalBuffer::length() {
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-16
@@ -11,15 +11,18 @@ ChunkIterator::~ChunkIterator() {
|
|||||||
Shard::release(root);
|
Shard::release(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChunkIterator::seek_offset(uint32_t offset) {
|
void ChunkIterator::seek_offset(uint64_t offset) {
|
||||||
stack.clear();
|
stack.clear();
|
||||||
petal = nullptr;
|
petal = nullptr;
|
||||||
petal_offset = 0;
|
petal_offset = 0;
|
||||||
|
global_offset = 0;
|
||||||
|
global_line = 0;
|
||||||
|
at_end = false;
|
||||||
if (!root)
|
if (!root)
|
||||||
return;
|
return;
|
||||||
if (offset >= root->length)
|
if (offset >= root->length)
|
||||||
offset = root->length - 1;
|
offset = root->length - 1;
|
||||||
uint32_t target = offset;
|
uint64_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) {
|
||||||
@@ -29,7 +32,7 @@ void ChunkIterator::seek_offset(uint32_t offset) {
|
|||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
auto *b = (Branch *)curr;
|
auto *b = (Branch *)curr;
|
||||||
uint32_t left_len = b->left->length;
|
uint64_t left_len = b->left->length;
|
||||||
if (target < left_len) {
|
if (target < left_len) {
|
||||||
if (dir == Direction::Forward)
|
if (dir == Direction::Forward)
|
||||||
stack.push_back(b->right);
|
stack.push_back(b->right);
|
||||||
@@ -39,6 +42,7 @@ void ChunkIterator::seek_offset(uint32_t offset) {
|
|||||||
if (dir == Direction::Backward)
|
if (dir == Direction::Backward)
|
||||||
stack.push_back(b->left);
|
stack.push_back(b->left);
|
||||||
global_offset += left_len;
|
global_offset += left_len;
|
||||||
|
global_line += b->left->lines;
|
||||||
curr = b->right;
|
curr = b->right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,31 +50,40 @@ void ChunkIterator::seek_offset(uint32_t offset) {
|
|||||||
std::unreachable();
|
std::unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChunkIterator::seek_line(uint32_t line) {
|
void ChunkIterator::seek_line(uint64_t line) {
|
||||||
stack.clear();
|
stack.clear();
|
||||||
petal = nullptr;
|
petal = nullptr;
|
||||||
petal_offset = 0;
|
petal_offset = 0;
|
||||||
|
global_offset = 0;
|
||||||
|
at_end = false;
|
||||||
bool last_line = false;
|
bool last_line = false;
|
||||||
if (!root)
|
if (!root)
|
||||||
return;
|
return;
|
||||||
|
if (dir == Direction::Backward) {
|
||||||
if (line > root->lines + 1)
|
if (line > root->lines + 1)
|
||||||
line = root->lines + 1;
|
line = root->lines + 1;
|
||||||
if (line == root->lines + 1)
|
if (line == root->lines + 1)
|
||||||
last_line = true;
|
last_line = true;
|
||||||
|
} else {
|
||||||
|
if (line > root->lines)
|
||||||
|
line = root->lines;
|
||||||
|
if (line == root->lines)
|
||||||
|
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) {
|
if (last_line && dir == Direction::Backward) {
|
||||||
petal_offset = petal->length;
|
petal_offset = petal->length;
|
||||||
global_offset += petal->length;
|
global_offset += petal->length;
|
||||||
} else {
|
} else {
|
||||||
const char *text = nullptr;
|
const char *text = nullptr;
|
||||||
uint32_t remaining = 0;
|
uint64_t remaining = 0;
|
||||||
uint32_t offset = 0;
|
uint64_t offset = 0;
|
||||||
while (line) {
|
while (line) {
|
||||||
if (!text) {
|
if (!text) {
|
||||||
uint32_t got = 0;
|
uint64_t got = 0;
|
||||||
text = petal->source->read(petal->pos + offset, &got);
|
text = petal->source->read(petal->pos + offset, &got);
|
||||||
remaining = std::min(got, petal->length - offset);
|
remaining = std::min(got, petal->length - offset);
|
||||||
}
|
}
|
||||||
@@ -87,13 +100,15 @@ void ChunkIterator::seek_line(uint32_t line) {
|
|||||||
}
|
}
|
||||||
if (dir == Direction::Backward)
|
if (dir == Direction::Backward)
|
||||||
--offset;
|
--offset;
|
||||||
|
if (last_line && dir == Direction::Forward)
|
||||||
|
at_end = true;
|
||||||
petal_offset = offset;
|
petal_offset = offset;
|
||||||
global_offset += offset;
|
global_offset += offset;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
auto *b = (Branch *)curr;
|
auto *b = (Branch *)curr;
|
||||||
uint32_t left_lines = b->left->lines;
|
uint64_t left_lines = b->left->lines;
|
||||||
if (line <= left_lines) {
|
if (line <= left_lines) {
|
||||||
if (dir == Direction::Forward)
|
if (dir == Direction::Forward)
|
||||||
stack.push_back(b->right);
|
stack.push_back(b->right);
|
||||||
@@ -110,30 +125,41 @@ void ChunkIterator::seek_line(uint32_t line) {
|
|||||||
std::unreachable();
|
std::unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ChunkIterator::byte_offset() {
|
uint64_t ChunkIterator::byte_offset() {
|
||||||
return global_offset;
|
return global_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChunkIterator::next(const char **data, uint32_t *out_len) {
|
uint64_t ChunkIterator::line_offset() {
|
||||||
|
return global_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChunkIterator::next(const char **data, uint64_t *out_len) {
|
||||||
if (dir == Direction::Forward) {
|
if (dir == Direction::Forward) {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (petal) {
|
if (petal) {
|
||||||
if (petal_offset == petal->length) {
|
if (petal_offset == petal->length) {
|
||||||
|
if (at_end) {
|
||||||
|
*data = nullptr;
|
||||||
|
*out_len = 0;
|
||||||
|
at_end = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
petal = nullptr;
|
petal = nullptr;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
uint32_t remaining = petal->length - petal_offset;
|
uint64_t remaining = petal->length - petal_offset;
|
||||||
uint32_t got = 0;
|
uint64_t got = 0;
|
||||||
const char *chunk = petal->source->read(petal->pos + petal_offset, &got);
|
const char *chunk = petal->source->read(petal->pos + petal_offset, &got);
|
||||||
if (got == 0) {
|
if (got == 0) {
|
||||||
petal = nullptr;
|
petal = nullptr;
|
||||||
petal_offset = 0;
|
petal_offset = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
uint32_t take = std::min(got, remaining);
|
uint64_t take = std::min(got, remaining);
|
||||||
*data = chunk;
|
*data = chunk;
|
||||||
*out_len = take;
|
*out_len = take;
|
||||||
petal_offset += take;
|
petal_offset += take;
|
||||||
|
global_offset += take;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (stack.empty())
|
if (stack.empty())
|
||||||
@@ -155,17 +181,19 @@ bool ChunkIterator::next(const char **data, uint32_t *out_len) {
|
|||||||
petal = nullptr;
|
petal = nullptr;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
uint32_t got = 0;
|
uint64_t got = 0;
|
||||||
*data = petal->source->read(petal->pos, &got);
|
*data = petal->source->read(petal->pos, &got);
|
||||||
if (petal_offset > got) {
|
if (petal_offset > got) {
|
||||||
uint32_t got2 = 0;
|
uint64_t got2 = 0;
|
||||||
*data = petal->source->read(petal->pos + got, &got2);
|
*data = petal->source->read(petal->pos + got, &got2);
|
||||||
*out_len = std::min(got2, petal_offset - got);
|
*out_len = std::min(got2, petal_offset - got);
|
||||||
petal_offset = got;
|
petal_offset = got;
|
||||||
|
global_offset -= *out_len;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
*out_len = std::min(got, petal_offset);
|
*out_len = std::min(got, petal_offset);
|
||||||
petal_offset = 0;
|
petal_offset = 0;
|
||||||
|
global_offset -= *out_len;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +1,86 @@
|
|||||||
#include "vase/iterators/line.h"
|
#include "vase/iterators/line.h"
|
||||||
|
|
||||||
LineIterator::LineIterator(Shard *r, uint32_t start_line, Direction dir)
|
LineIterator::LineIterator(Shard *r, uint64_t start_line, Direction dir)
|
||||||
: it(r, dir), dir(dir) {
|
: it(r, dir), dir(dir) {
|
||||||
it.seek_line(start_line + (dir == Direction::Backward));
|
int b = start_line == UINT64_MAX ? 0 : dir == Direction::Backward;
|
||||||
if (!it.next(&chunk, &len))
|
it.seek_line(start_line + b);
|
||||||
|
if (!it.next(&chunk, &len)) {
|
||||||
chunk = nullptr;
|
chunk = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (chunk == nullptr)
|
||||||
|
at_end = true;
|
||||||
|
if (dir == Direction::Forward)
|
||||||
|
chunk_offset = it.byte_offset() - len;
|
||||||
|
else
|
||||||
|
chunk_offset = it.byte_offset();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t LineIterator::byte_offset() {
|
uint64_t LineIterator::byte_offset() {
|
||||||
return 0;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LineIterator::next(std::string &line) {
|
bool LineIterator::next(std::string &line) {
|
||||||
line.clear();
|
line.clear();
|
||||||
|
if (at_end) {
|
||||||
|
offset = chunk_offset;
|
||||||
|
at_end = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (!chunk)
|
if (!chunk)
|
||||||
return false;
|
return false;
|
||||||
if (dir == Direction::Forward) {
|
if (dir == Direction::Forward) {
|
||||||
|
offset = chunk_offset;
|
||||||
while (true) {
|
while (true) {
|
||||||
const char *nl = (const char *)memchr(chunk, '\n', len);
|
const char *nl = (const char *)memchr(chunk, '\n', len);
|
||||||
if (!nl) {
|
if (!nl) {
|
||||||
if (len && chunk[len - 1] == '\r')
|
if (len && chunk[len - 1] == '\r')
|
||||||
--len;
|
--len;
|
||||||
line.append(chunk, len);
|
line.append(chunk, len);
|
||||||
|
chunk_offset += len;
|
||||||
if (!it.next(&chunk, &len)) {
|
if (!it.next(&chunk, &len)) {
|
||||||
chunk = nullptr;
|
chunk = nullptr;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (chunk == nullptr)
|
||||||
|
return true;
|
||||||
|
chunk_offset = it.byte_offset() - len;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const char *end = nl;
|
const char *end = nl;
|
||||||
if (end > chunk && *(end - 1) == '\r')
|
if (end > chunk && *(end - 1) == '\r')
|
||||||
--end;
|
--end;
|
||||||
line.append(chunk, end);
|
line.append(chunk, end);
|
||||||
uint32_t consumed = (nl - chunk) + 1;
|
uint64_t consumed = (nl - chunk) + 1;
|
||||||
chunk += consumed;
|
chunk += consumed;
|
||||||
len -= consumed;
|
len -= consumed;
|
||||||
|
chunk_offset += consumed;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
#if defined(__GLIBC__) || defined(__APPLE__)
|
||||||
const char *nl = (const char *)memrchr(chunk, '\n', len);
|
const char *nl = (const char *)memrchr(chunk, '\n', len);
|
||||||
|
#else
|
||||||
|
const char *nl = nullptr;
|
||||||
|
uint64_t i = len;
|
||||||
|
while (i--) {
|
||||||
|
if (chunk[i] == '\n') {
|
||||||
|
nl = chunk + i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (!nl) {
|
if (!nl) {
|
||||||
if (len && chunk[len - 1] == '\n')
|
|
||||||
--len;
|
|
||||||
if (len && chunk[len - 1] == '\r')
|
if (len && chunk[len - 1] == '\r')
|
||||||
--len;
|
--len;
|
||||||
line.insert(0, chunk, len);
|
line.insert(0, chunk, len);
|
||||||
|
chunk_offset -= len;
|
||||||
if (!it.next(&chunk, &len)) {
|
if (!it.next(&chunk, &len)) {
|
||||||
chunk = nullptr;
|
chunk = nullptr;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
chunk_offset = it.byte_offset();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const char *end = chunk + len;
|
const char *end = chunk + len;
|
||||||
@@ -59,6 +90,7 @@ bool LineIterator::next(std::string &line) {
|
|||||||
--line_end;
|
--line_end;
|
||||||
line.insert(0, start, line_end - start);
|
line.insert(0, start, line_end - start);
|
||||||
len = nl - chunk;
|
len = nl - chunk;
|
||||||
|
offset = chunk_offset + (start - chunk);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
#include "vase/vase.h"
|
||||||
|
|
||||||
|
std::vector<ReplacePart> Vase::parse_replace(std::string_view s) {
|
||||||
|
std::vector<ReplacePart> parts;
|
||||||
|
std::string constant;
|
||||||
|
auto flush_constant = [&]() {
|
||||||
|
if (!constant.empty()) {
|
||||||
|
uint64_t lines = 0;
|
||||||
|
uint64_t pos = append->append(constant.data(), (uint64_t)constant.size());
|
||||||
|
parts.push_back(
|
||||||
|
ReplacePart{
|
||||||
|
.type = ReplacePart::PartType::Constant,
|
||||||
|
.value = new Petal((uint64_t)constant.size(), lines, append, pos)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
constant.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (size_t i = 0; i < s.size(); ++i) {
|
||||||
|
char c = s[i];
|
||||||
|
if (c == '\\' && i + 1 < s.size() && s[i + 1] == '$') {
|
||||||
|
constant.push_back('$');
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c == '$' && i + 1 < s.size()) {
|
||||||
|
char next = s[i + 1];
|
||||||
|
if (next == '0') {
|
||||||
|
flush_constant();
|
||||||
|
parts.push_back(
|
||||||
|
ReplacePart{
|
||||||
|
.type = ReplacePart::PartType::FullMatch,
|
||||||
|
.value = (uint8_t)0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (next >= '1' && next <= '9') {
|
||||||
|
flush_constant();
|
||||||
|
parts.push_back(
|
||||||
|
ReplacePart{
|
||||||
|
.type = ReplacePart::PartType::CaptureGroup,
|
||||||
|
.value = (uint8_t)(next - '0')
|
||||||
|
}
|
||||||
|
);
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
constant.push_back(c);
|
||||||
|
}
|
||||||
|
flush_constant();
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vase::regex_search_replace(
|
||||||
|
std::string_view pattern, Range range,
|
||||||
|
std::string_view replace, std::string_view options
|
||||||
|
) {
|
||||||
|
std::vector<RegexMatch> matches = _regex_search(pattern, range, options);
|
||||||
|
if (matches.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::vector<ReplacePart> replace_parts = parse_replace(replace);
|
||||||
|
|
||||||
|
std::vector<Shard *> pieces;
|
||||||
|
pieces.reserve(matches.size() * 2 + 1);
|
||||||
|
|
||||||
|
Shard *remaining = root;
|
||||||
|
Shard::retain(remaining);
|
||||||
|
uint64_t cursor = 0;
|
||||||
|
|
||||||
|
for (const RegexMatch &match : matches) {
|
||||||
|
uint64_t gap = match.start - cursor;
|
||||||
|
if (gap > 0) {
|
||||||
|
auto [keep, rest] = split_shard(remaining, gap);
|
||||||
|
Shard::release(remaining);
|
||||||
|
pieces.push_back(keep);
|
||||||
|
remaining = rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto [dropped, rest2] = split_shard(remaining, match.end - match.start);
|
||||||
|
Shard::release(remaining);
|
||||||
|
remaining = rest2;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < replace_parts.size(); ++i) {
|
||||||
|
const ReplacePart &part = replace_parts[i];
|
||||||
|
switch (part.type) {
|
||||||
|
case ReplacePart::PartType::Constant:
|
||||||
|
Shard::retain(std::get<Shard *>(part.value));
|
||||||
|
pieces.push_back(std::get<Shard *>(part.value));
|
||||||
|
break;
|
||||||
|
case ReplacePart::PartType::FullMatch:
|
||||||
|
Shard::retain(dropped);
|
||||||
|
pieces.push_back(dropped);
|
||||||
|
break;
|
||||||
|
case ReplacePart::PartType::CaptureGroup: {
|
||||||
|
uint8_t idx = std::get<uint8_t>(part.value);
|
||||||
|
if (idx <= 9) {
|
||||||
|
const RegexGroup &group = match.groups[idx - 1];
|
||||||
|
if (group.start != UINT32_MAX) {
|
||||||
|
uint64_t ls = group.start - match.start;
|
||||||
|
uint64_t le = group.end - match.start;
|
||||||
|
auto [a, b] = split_shard(dropped, ls);
|
||||||
|
auto [g, c] = split_shard(b, le - ls);
|
||||||
|
Shard::release(a);
|
||||||
|
Shard::release(b);
|
||||||
|
Shard::release(c);
|
||||||
|
pieces.push_back(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Shard::release(dropped);
|
||||||
|
cursor = match.end;
|
||||||
|
}
|
||||||
|
pieces.push_back(remaining);
|
||||||
|
|
||||||
|
for (auto &part : replace_parts)
|
||||||
|
if (part.type == ReplacePart::PartType::Constant)
|
||||||
|
Shard::release(std::get<Shard *>(part.value));
|
||||||
|
|
||||||
|
std::vector<Shard *> compact;
|
||||||
|
compact.reserve(pieces.size());
|
||||||
|
for (Shard *p : pieces) {
|
||||||
|
if (p && p->length > 0)
|
||||||
|
compact.push_back(p);
|
||||||
|
else if (p)
|
||||||
|
Shard::release(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
Shard *new_root = compact.empty() ? nullptr : build_balanced(compact.data(), 0, compact.size());
|
||||||
|
Shard::release(root);
|
||||||
|
root = new_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Range> Vase::regex_search(
|
||||||
|
std::string_view pattern, Range range, std::string_view options
|
||||||
|
) {
|
||||||
|
std::vector<RegexMatch> matches = _regex_search(pattern, range, options);
|
||||||
|
if (matches.empty())
|
||||||
|
return {};
|
||||||
|
std::vector<Range> result;
|
||||||
|
result.reserve(matches.size());
|
||||||
|
for (auto match : matches)
|
||||||
|
result.push_back({point_of(match.start), point_of(match.end)});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
#include "vase/search.h"
|
#include "vase/vase.h"
|
||||||
#include "vase/iterators/chunk.h"
|
|
||||||
|
|
||||||
std::vector<RegexMatch> regex_search(
|
std::vector<RegexMatch> Vase::_regex_search(
|
||||||
Shard *root, std::string_view pattern_str,
|
std::string_view pattern, Range range, std::string_view options
|
||||||
uint32_t start_offset, uint32_t end_offset,
|
|
||||||
std::string_view options
|
|
||||||
) {
|
) {
|
||||||
bool global = false;
|
bool global = false;
|
||||||
uint32_t flags = PCRE2_MULTILINE;
|
uint64_t flags = PCRE2_MULTILINE;
|
||||||
|
|
||||||
const char *dot = "(?:(?!\\n)\\X)";
|
const char *dot = "(?:(?!\\n)\\X)";
|
||||||
|
|
||||||
@@ -43,12 +40,12 @@ std::vector<RegexMatch> regex_search(
|
|||||||
PCRE2_SIZE erroroffset;
|
PCRE2_SIZE erroroffset;
|
||||||
|
|
||||||
std::string out;
|
std::string out;
|
||||||
out.reserve(pattern_str.size() * 2);
|
out.reserve(pattern.size() * 2);
|
||||||
bool escaped = false;
|
bool escaped = false;
|
||||||
bool in_class = false;
|
bool in_class = false;
|
||||||
bool in_quote = false;
|
bool in_quote = false;
|
||||||
for (size_t i = 0; i < pattern_str.size(); i++) {
|
for (size_t i = 0; i < pattern.size(); i++) {
|
||||||
char c = pattern_str[i];
|
char c = pattern[i];
|
||||||
if (escaped) {
|
if (escaped) {
|
||||||
out += c;
|
out += c;
|
||||||
escaped = false;
|
escaped = false;
|
||||||
@@ -56,8 +53,8 @@ std::vector<RegexMatch> regex_search(
|
|||||||
}
|
}
|
||||||
if (c == '\\') {
|
if (c == '\\') {
|
||||||
out += c;
|
out += c;
|
||||||
if (i + 1 < pattern_str.size()) {
|
if (i + 1 < pattern.size()) {
|
||||||
char next = pattern_str[i + 1];
|
char next = pattern[i + 1];
|
||||||
if (next == 'Q')
|
if (next == 'Q')
|
||||||
in_quote = true;
|
in_quote = true;
|
||||||
else if (next == 'E')
|
else if (next == 'E')
|
||||||
@@ -104,40 +101,45 @@ 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);
|
||||||
|
|
||||||
|
uint64_t start_offset = offset_of(range.start);
|
||||||
|
uint64_t end_offset = offset_of(range.end);
|
||||||
|
|
||||||
|
std::cout << (int)start_offset << ":" << (int)end_offset;
|
||||||
|
|
||||||
ChunkIterator it(root, Direction::Forward);
|
ChunkIterator it(root, Direction::Forward);
|
||||||
it.seek_offset(start_offset);
|
it.seek_offset(start_offset);
|
||||||
|
|
||||||
const char *data;
|
const char *data;
|
||||||
uint32_t length;
|
uint64_t length;
|
||||||
|
|
||||||
char buf[2048];
|
char buf[2048];
|
||||||
|
|
||||||
uint32_t global_offset = start_offset;
|
uint64_t global_offset = start_offset;
|
||||||
int64_t offset = -1;
|
uint64_t offset = UINT64_MAX;
|
||||||
|
|
||||||
auto record_match = [&](int rc, PCRE2_SIZE *ovector) {
|
auto record_match = [&](int rc, PCRE2_SIZE *ovector) {
|
||||||
if (global_offset + (uint32_t)ovector[1] > end_offset || ovector[0] == ovector[1])
|
if (global_offset + (uint64_t)ovector[1] > end_offset || ovector[0] == ovector[1])
|
||||||
return;
|
return;
|
||||||
RegexMatch match{
|
RegexMatch match{
|
||||||
.start = global_offset + (uint32_t)ovector[0],
|
.start = global_offset + (uint64_t)ovector[0],
|
||||||
.end = global_offset + (uint32_t)ovector[1]
|
.end = global_offset + (uint64_t)ovector[1]
|
||||||
};
|
};
|
||||||
for (int i = 1; i < rc && i <= 9; ++i) {
|
for (int i = 1; i < rc && i <= 9; ++i) {
|
||||||
PCRE2_SIZE s = ovector[2 * i];
|
PCRE2_SIZE s = ovector[2 * i];
|
||||||
PCRE2_SIZE e = ovector[2 * i + 1];
|
PCRE2_SIZE e = ovector[2 * i + 1];
|
||||||
if (s != PCRE2_UNSET) {
|
if (s != PCRE2_UNSET) {
|
||||||
match.groups[i].start = global_offset + (uint32_t)s;
|
match.groups[i].start = global_offset + (uint64_t)s;
|
||||||
match.groups[i].end = global_offset + (uint32_t)e;
|
match.groups[i].end = global_offset + (uint64_t)e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
results.push_back(std::move(match));
|
results.push_back(std::move(match));
|
||||||
};
|
};
|
||||||
|
|
||||||
auto skip_to_next_line = [&](const char *&data, uint32_t &length, int64_t &offset, uint32_t &global_offset) {
|
auto skip_to_next_line = [&](const char *&data, uint64_t &length, uint64_t &offset, uint64_t &global_offset) {
|
||||||
while (true) {
|
while (true) {
|
||||||
const char *p = (const char *)memchr(data + offset, '\n', length - offset);
|
const char *p = (const char *)memchr(data + offset, '\n', length - offset);
|
||||||
if (p) {
|
if (p) {
|
||||||
uint32_t nl = p - data;
|
uint64_t nl = p - data;
|
||||||
offset = nl + 1;
|
offset = nl + 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -150,7 +152,7 @@ std::vector<RegexMatch> regex_search(
|
|||||||
};
|
};
|
||||||
|
|
||||||
while (global_offset < end_offset) {
|
while (global_offset < end_offset) {
|
||||||
if (offset == -1) {
|
if (offset == UINT64_MAX) {
|
||||||
bool more = it.next(&data, &length);
|
bool more = it.next(&data, &length);
|
||||||
if (!more)
|
if (!more)
|
||||||
break;
|
break;
|
||||||
@@ -158,7 +160,7 @@ std::vector<RegexMatch> regex_search(
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (offset < length) {
|
while (offset < length) {
|
||||||
if (offset == -1)
|
if (offset == UINT64_MAX)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
int rc = pcre2_match(
|
int rc = pcre2_match(
|
||||||
@@ -172,15 +174,15 @@ std::vector<RegexMatch> regex_search(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (rc == PCRE2_ERROR_PARTIAL) {
|
if (rc == PCRE2_ERROR_PARTIAL) {
|
||||||
uint32_t buflen = 0;
|
uint64_t buflen = 0;
|
||||||
|
|
||||||
PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
|
PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
|
||||||
uint32_t partial_start = ovector[0];
|
uint64_t partial_start = ovector[0];
|
||||||
uint32_t partial_length = length - partial_start;
|
uint64_t partial_length = length - partial_start;
|
||||||
|
|
||||||
if (partial_length >= 2048) {
|
if (partial_length >= 2048) {
|
||||||
global_offset += offset;
|
global_offset += offset;
|
||||||
offset = -1;
|
offset = UINT64_MAX;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +191,7 @@ std::vector<RegexMatch> regex_search(
|
|||||||
memcpy(buf, data + partial_start, partial_length);
|
memcpy(buf, data + partial_start, partial_length);
|
||||||
buflen += partial_length;
|
buflen += partial_length;
|
||||||
|
|
||||||
offset = -1;
|
offset = UINT64_MAX;
|
||||||
bool exhausted = false;
|
bool exhausted = false;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -201,7 +203,7 @@ std::vector<RegexMatch> regex_search(
|
|||||||
exhausted = true;
|
exhausted = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
uint32_t l = std::min(length, 2048 - buflen);
|
uint64_t l = std::min(length, 2048 - buflen);
|
||||||
memcpy(buf + buflen, data, l);
|
memcpy(buf + buflen, data, l);
|
||||||
buflen += l;
|
buflen += l;
|
||||||
|
|
||||||
@@ -235,7 +237,7 @@ std::vector<RegexMatch> regex_search(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (exhausted) {
|
if (exhausted) {
|
||||||
uint32_t search_from = 0;
|
uint64_t search_from = 0;
|
||||||
while (search_from <= buflen) {
|
while (search_from <= buflen) {
|
||||||
int rc = pcre2_match(
|
int rc = pcre2_match(
|
||||||
re,
|
re,
|
||||||
@@ -251,7 +253,7 @@ std::vector<RegexMatch> regex_search(
|
|||||||
PCRE2_SIZE *ov = pcre2_get_ovector_pointer(match_data);
|
PCRE2_SIZE *ov = pcre2_get_ovector_pointer(match_data);
|
||||||
record_match(rc, ov);
|
record_match(rc, ov);
|
||||||
if (global) {
|
if (global) {
|
||||||
search_from = (ov[1] == ov[0]) ? (uint32_t)ov[1] + 1 : (uint32_t)ov[1];
|
search_from = (ov[1] == ov[0]) ? (uint64_t)ov[1] + 1 : (uint64_t)ov[1];
|
||||||
} else {
|
} else {
|
||||||
const void *p = memchr(buf + ov[1], '\n', buflen - ov[1]);
|
const void *p = memchr(buf + ov[1], '\n', buflen - ov[1]);
|
||||||
if (!p)
|
if (!p)
|
||||||
@@ -259,7 +261,7 @@ std::vector<RegexMatch> regex_search(
|
|||||||
search_from = (const char *)p - buf + 1;
|
search_from = (const char *)p - buf + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
offset = -1;
|
offset = UINT64_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -267,7 +269,7 @@ std::vector<RegexMatch> regex_search(
|
|||||||
PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
|
PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
|
||||||
record_match(rc, ovector);
|
record_match(rc, ovector);
|
||||||
if (global) {
|
if (global) {
|
||||||
if ((int64_t)ovector[1] == offset)
|
if (ovector[1] == offset)
|
||||||
offset++;
|
offset++;
|
||||||
else
|
else
|
||||||
offset = ovector[1];
|
offset = ovector[1];
|
||||||
@@ -285,7 +287,7 @@ std::vector<RegexMatch> regex_search(
|
|||||||
}
|
}
|
||||||
|
|
||||||
global_offset += offset;
|
global_offset += offset;
|
||||||
offset = -1;
|
offset = UINT64_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
pcre2_match_data_free(match_data);
|
pcre2_match_data_free(match_data);
|
||||||
+41
-17
@@ -112,7 +112,7 @@ Shard *merge(Shard *a, Shard *b) {
|
|||||||
return balance(new Branch(a, b));
|
return balance(new Branch(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Shard *, Shard *> split_shard(Shard *n, uint32_t offset) {
|
std::pair<Shard *, Shard *> split_shard(Shard *n, uint64_t offset) {
|
||||||
if (!n)
|
if (!n)
|
||||||
return {nullptr, nullptr};
|
return {nullptr, nullptr};
|
||||||
if (offset == 0) {
|
if (offset == 0) {
|
||||||
@@ -139,10 +139,10 @@ std::pair<Shard *, Shard *> split_shard(Shard *n, uint32_t offset) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Petal *p = (Petal *)n;
|
Petal *p = (Petal *)n;
|
||||||
uint32_t count[2]{0};
|
uint64_t count[2]{0};
|
||||||
uint32_t read_offset = 0;
|
uint64_t read_offset = 0;
|
||||||
while (read_offset < p->length) {
|
while (read_offset < p->length) {
|
||||||
uint32_t got = 0;
|
uint64_t got = 0;
|
||||||
const char *c = p->source->read(p->pos + read_offset, &got);
|
const char *c = p->source->read(p->pos + read_offset, &got);
|
||||||
const char *end = c + std::min(got, p->length - read_offset);
|
const char *end = c + std::min(got, p->length - read_offset);
|
||||||
const char *cursor = c;
|
const char *cursor = c;
|
||||||
@@ -152,14 +152,14 @@ std::pair<Shard *, Shard *> split_shard(Shard *n, uint32_t offset) {
|
|||||||
cursor = end;
|
cursor = end;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
uint32_t nl_pos = read_offset + (uint32_t)(nl - c);
|
uint64_t nl_pos = read_offset + (uint64_t)(nl - c);
|
||||||
if (nl_pos < offset)
|
if (nl_pos < offset)
|
||||||
count[0]++;
|
count[0]++;
|
||||||
else
|
else
|
||||||
count[1]++;
|
count[1]++;
|
||||||
cursor = nl + 1;
|
cursor = nl + 1;
|
||||||
}
|
}
|
||||||
read_offset += (uint32_t)(cursor - c);
|
read_offset += (uint64_t)(cursor - c);
|
||||||
}
|
}
|
||||||
auto left = new Petal(
|
auto left = new Petal(
|
||||||
offset,
|
offset,
|
||||||
@@ -208,7 +208,7 @@ Shard *concat_shard(Shard *left, Shard *right) {
|
|||||||
return merge(left, right);
|
return merge(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
Shard *build_balanced(Shard **pieces, uint32_t lo, uint32_t hi) {
|
Shard *build_balanced(Shard **pieces, uint64_t lo, uint64_t hi) {
|
||||||
if (hi - lo == 1)
|
if (hi - lo == 1)
|
||||||
return pieces[lo];
|
return pieces[lo];
|
||||||
size_t mid = lo + (hi - lo) / 2;
|
size_t mid = lo + (hi - lo) / 2;
|
||||||
@@ -220,19 +220,34 @@ Shard *build_balanced(Shard **pieces, uint32_t lo, uint32_t hi) {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
Shard *create_shards(Buffer *o) {
|
Shard *create_file_shards(std::string &path, OriginalBuffer *o) {
|
||||||
|
int dest_fd = o->fd;
|
||||||
|
if (dest_fd == -1)
|
||||||
|
return nullptr;
|
||||||
|
int src_fd = open(path.c_str(), O_RDONLY);
|
||||||
|
if (src_fd == -1)
|
||||||
|
return nullptr;
|
||||||
|
struct stat st;
|
||||||
|
if (fstat(src_fd, &st) == -1)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
uint64_t total = (uint64_t)st.st_size;
|
||||||
std::vector<Shard *> pieces;
|
std::vector<Shard *> pieces;
|
||||||
uint32_t pos = 0;
|
uint64_t pos = 0;
|
||||||
const uint32_t total = o->length();
|
|
||||||
pieces.reserve((total + PETAL_SIZE_MAX - 1) / PETAL_SIZE_MAX);
|
pieces.reserve((total + PETAL_SIZE_MAX - 1) / PETAL_SIZE_MAX);
|
||||||
|
char buf[PETAL_SIZE_MAX];
|
||||||
|
|
||||||
while (pos < total) {
|
while (pos < total) {
|
||||||
uint32_t len = 0;
|
uint64_t want = std::min(PETAL_SIZE_MAX, total - pos);
|
||||||
const char *data = o->read(pos, &len);
|
ssize_t got = pread(src_fd, buf, want, pos);
|
||||||
uint32_t take = std::min(len, PETAL_SIZE_MAX);
|
if (got <= 0) {
|
||||||
uint32_t lines = 0;
|
close(src_fd);
|
||||||
const char *p = data;
|
return nullptr;
|
||||||
const char *end = data + take;
|
}
|
||||||
|
uint64_t take = (uint64_t)got;
|
||||||
|
uint64_t lines = 0;
|
||||||
|
const char *p = buf;
|
||||||
|
const char *end = p + take;
|
||||||
while (p < end) {
|
while (p < end) {
|
||||||
const void *nl = memchr(p, '\n', end - p);
|
const void *nl = memchr(p, '\n', end - p);
|
||||||
if (!nl)
|
if (!nl)
|
||||||
@@ -240,14 +255,23 @@ Shard *create_shards(Buffer *o) {
|
|||||||
lines++;
|
lines++;
|
||||||
p = (const char *)nl + 1;
|
p = (const char *)nl + 1;
|
||||||
}
|
}
|
||||||
|
ssize_t written = write(dest_fd, buf, take);
|
||||||
|
if (written != (ssize_t)take) {
|
||||||
|
close(src_fd);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
pieces.push_back(new Petal(take, lines, o, pos));
|
pieces.push_back(new Petal(take, lines, o, pos));
|
||||||
pos += take;
|
pos += take;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close(src_fd);
|
||||||
|
|
||||||
if (pieces.empty())
|
if (pieces.empty())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
o->initialize();
|
||||||
|
|
||||||
if (pieces.size() == 1)
|
if (pieces.size() == 1)
|
||||||
return pieces[0];
|
return pieces[0];
|
||||||
|
|
||||||
return build_balanced(pieces.data(), 0, pieces.size());
|
return build_balanced(pieces.data(), 0, pieces.size());
|
||||||
}
|
}
|
||||||
|
|||||||
+157
-227
@@ -1,11 +1,11 @@
|
|||||||
#include "vase/vase.h"
|
#include "vase/vase.h"
|
||||||
#include "utils/utils.h"
|
#include "utils/utils.h"
|
||||||
#include "vase/iterators/line.h"
|
#include "vase/iterators/line.h"
|
||||||
#include "vase/search.h"
|
|
||||||
|
|
||||||
Vase::Vase(std::string path)
|
Vase::Vase(std::string path) {
|
||||||
: original(path), append(),
|
append = new AppendBuffer();
|
||||||
root(create_shards(&original)) {
|
original = new OriginalBuffer();
|
||||||
|
root = create_file_shards(path, original);
|
||||||
history.push_back(root);
|
history.push_back(root);
|
||||||
Shard::retain(root);
|
Shard::retain(root);
|
||||||
history_top = 0;
|
history_top = 0;
|
||||||
@@ -15,19 +15,42 @@ Vase::~Vase() {
|
|||||||
Shard::release(root);
|
Shard::release(root);
|
||||||
for (auto s : history)
|
for (auto s : history)
|
||||||
Shard::release(s);
|
Shard::release(s);
|
||||||
|
delete original;
|
||||||
|
delete append;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Vase::length() {
|
uint64_t Vase::length() {
|
||||||
return root->length;
|
return root->length;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Vase::to_string() {
|
std::string Vase::to_string() {
|
||||||
std::string out;
|
std::string out;
|
||||||
flatten(root, out);
|
ChunkIterator it(root, Direction::Forward);
|
||||||
|
it.seek_offset(0);
|
||||||
|
const char *data;
|
||||||
|
uint64_t len;
|
||||||
|
while (it.next(&data, &len))
|
||||||
|
out.append(data, len);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
LineIterator Vase::iterate(uint32_t line) {
|
std::string Vase::to_string(Range range) {
|
||||||
|
std::string out;
|
||||||
|
ChunkIterator it(root, Direction::Forward);
|
||||||
|
uint64_t start = offset_of(range.start);
|
||||||
|
it.seek_offset(start);
|
||||||
|
const char *data;
|
||||||
|
uint64_t len;
|
||||||
|
uint64_t remaining = offset_of(range.end) - start;
|
||||||
|
while (remaining && it.next(&data, &len)) {
|
||||||
|
uint64_t n = std::min(len, remaining);
|
||||||
|
out.append(data, n);
|
||||||
|
remaining -= n;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
LineIterator Vase::iterate(uint64_t line) {
|
||||||
return LineIterator(root, line, Direction::Forward);
|
return LineIterator(root, line, Direction::Forward);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,21 +86,40 @@ void Vase::snapshot() {
|
|||||||
history_top++;
|
history_top++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vase::prune_history(uint32_t n) {
|
void Vase::prune_history(uint64_t n) {
|
||||||
n = std::min(n, history_top);
|
n = std::min(n, history_top);
|
||||||
for (uint32_t i = 0; i < n; ++i)
|
for (uint64_t i = 0; i < n; ++i)
|
||||||
Shard::release(history[i]);
|
Shard::release(history[i]);
|
||||||
history.erase(history.begin(), history.begin() + n);
|
history.erase(history.begin(), history.begin() + n);
|
||||||
history_top -= n;
|
history_top -= n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Vase::save(std::string path) {
|
||||||
|
std::ofstream file(path, std::ios::binary);
|
||||||
|
if (!file)
|
||||||
|
return false;
|
||||||
|
ChunkIterator it(root, Direction::Forward);
|
||||||
|
it.seek_offset(0);
|
||||||
|
const char *data;
|
||||||
|
uint64_t len;
|
||||||
|
while (it.next(&data, &len)) {
|
||||||
|
file.write(data, len);
|
||||||
|
if (!file)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Vase::save_swap(std::string path) {
|
||||||
|
}
|
||||||
|
|
||||||
void Vase::insert(Point *point, char key) {
|
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);
|
uint64_t pos = append->append(key);
|
||||||
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);
|
||||||
Shard *new_root = concat_shard(left2, right);
|
Shard *new_root = concat_shard(left2, right);
|
||||||
@@ -89,21 +131,21 @@ void Vase::insert(Point *point, char key) {
|
|||||||
root = new_root;
|
root = new_root;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vase::insert(Point *point, const char *data, uint32_t len) {
|
void Vase::insert(Point *point, const char *data, uint64_t len) {
|
||||||
while (len) {
|
while (len) {
|
||||||
uint32_t chunk_size = std::min<uint32_t>(len, PETAL_SIZE_MAX);
|
uint64_t chunk_size = std::min<uint64_t>(len, PETAL_SIZE_MAX);
|
||||||
_insert(point, data, chunk_size);
|
_insert(point, data, chunk_size);
|
||||||
len -= chunk_size;
|
len -= chunk_size;
|
||||||
data += chunk_size;
|
data += chunk_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vase::_insert(Point *point, const char *data, uint32_t len) {
|
void Vase::_insert(Point *point, const char *data, uint64_t len) {
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
return;
|
return;
|
||||||
uint32_t offset = offset_of(*point);
|
uint64_t offset = offset_of(*point);
|
||||||
uint32_t pos = append.append(data, len);
|
uint64_t pos = append->append(data, len);
|
||||||
uint32_t lines = 0;
|
uint64_t lines = 0;
|
||||||
const char *start = data;
|
const char *start = data;
|
||||||
const char *last_line = start;
|
const char *last_line = start;
|
||||||
const char *end = start + len;
|
const char *end = start + len;
|
||||||
@@ -111,10 +153,10 @@ void Vase::_insert(Point *point, const char *data, uint32_t len) {
|
|||||||
++lines;
|
++lines;
|
||||||
last_line = ++data;
|
last_line = ++data;
|
||||||
}
|
}
|
||||||
uint32_t col = 0;
|
uint64_t col = 0;
|
||||||
uint32_t remaining = end - last_line;
|
uint64_t remaining = end - last_line;
|
||||||
while (remaining) {
|
while (remaining) {
|
||||||
uint32_t n = grapheme_next_character_break_utf8(last_line, remaining);
|
uint64_t n = grapheme_next_character_break_utf8(last_line, remaining);
|
||||||
last_line += n;
|
last_line += n;
|
||||||
remaining -= n;
|
remaining -= n;
|
||||||
++col;
|
++col;
|
||||||
@@ -125,7 +167,7 @@ void Vase::_insert(Point *point, const char *data, uint32_t len) {
|
|||||||
} else {
|
} else {
|
||||||
point->col += col;
|
point->col += col;
|
||||||
}
|
}
|
||||||
Shard *inserted = new Petal(len, lines, &append, pos);
|
Shard *inserted = new Petal(len, lines, append, pos);
|
||||||
auto [left, right] = split_shard(root, offset);
|
auto [left, right] = split_shard(root, offset);
|
||||||
Shard *left2 = append_leaf(left, inserted);
|
Shard *left2 = append_leaf(left, inserted);
|
||||||
Shard *new_root = concat_shard(left2, right);
|
Shard *new_root = concat_shard(left2, right);
|
||||||
@@ -137,20 +179,20 @@ void Vase::_insert(Point *point, const char *data, uint32_t len) {
|
|||||||
root = new_root;
|
root = new_root;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vase::erase(Point *point, int64_t amount) {
|
void Vase::erase(Point *point, uint64_t amount, Direction dir) {
|
||||||
if (amount == 0)
|
if (amount == 0)
|
||||||
return;
|
return;
|
||||||
Point start = *point;
|
Point start = *point;
|
||||||
Point end = *point;
|
Point end = *point;
|
||||||
if (amount < 0)
|
if (dir == Direction::Forward)
|
||||||
move_clusters(&start, amount);
|
move_clusters(&end, amount, Direction::Forward);
|
||||||
else
|
else
|
||||||
move_clusters(&end, amount);
|
move_clusters(&start, amount, Direction::Backward);
|
||||||
uint32_t start_offset = offset_of(start);
|
uint64_t start_offset = offset_of(start);
|
||||||
uint32_t end_offset = offset_of(end);
|
uint64_t end_offset = offset_of(end);
|
||||||
if (start_offset > end_offset)
|
if (start_offset > end_offset)
|
||||||
std::swap(start_offset, end_offset);
|
std::swap(start_offset, end_offset);
|
||||||
uint32_t count = end_offset - start_offset;
|
uint64_t count = end_offset - start_offset;
|
||||||
auto [a, b] = split_shard(root, 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);
|
||||||
@@ -160,16 +202,16 @@ 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)
|
if (dir == Direction::Backward)
|
||||||
*point = start;
|
*point = start;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vase::erase(Range range) {
|
void Vase::erase(Range range) {
|
||||||
Point start = range.start;
|
Point start = range.start;
|
||||||
Point end = range.end;
|
Point end = range.end;
|
||||||
uint32_t start_offset = offset_of(start);
|
uint64_t start_offset = offset_of(start);
|
||||||
uint32_t end_offset = offset_of(end);
|
uint64_t end_offset = offset_of(end);
|
||||||
uint32_t count = end_offset - start_offset;
|
uint64_t count = end_offset - start_offset;
|
||||||
auto [a, b] = split_shard(root, 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);
|
||||||
@@ -181,40 +223,21 @@ void Vase::erase(Range range) {
|
|||||||
root = new_root;
|
root = new_root;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vase::replace(Range range, const char *data, uint32_t len) {
|
void Vase::replace(Range range, const char *data, uint64_t len) {
|
||||||
erase(range);
|
erase(range);
|
||||||
insert(&range.start, data, len);
|
insert(&range.start, data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vase::flatten(Shard *s, std::string &out) {
|
uint64_t Vase::offset_of(Point point) {
|
||||||
if (s->kind == Shard::ShardKind::Petal) {
|
|
||||||
auto *p = (Petal *)s;
|
|
||||||
uint32_t remaining = p->length;
|
|
||||||
uint32_t pos = p->pos;
|
|
||||||
while (remaining) {
|
|
||||||
uint32_t got;
|
|
||||||
const char *data = p->source->read(pos, &got);
|
|
||||||
uint32_t take = std::min(got, remaining);
|
|
||||||
out.append(data, take);
|
|
||||||
remaining -= take;
|
|
||||||
pos += take;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
auto *b = (Branch *)s;
|
|
||||||
flatten(b->left, out);
|
|
||||||
flatten(b->right, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Vase::offset_of(Point point) {
|
|
||||||
LineIterator it(root, point.row, Direction::Forward);
|
LineIterator it(root, point.row, Direction::Forward);
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
uint32_t offset = 0;
|
uint64_t offset = 0;
|
||||||
if (it.next(line)) {
|
if (it.next(line)) {
|
||||||
const char *ptr = line.data();
|
const char *ptr = line.data();
|
||||||
uint32_t remaining = line.length();
|
uint64_t remaining = line.length();
|
||||||
while (point.col && remaining) {
|
while (point.col && remaining) {
|
||||||
uint32_t next_len = grapheme_next_character_break_utf8(ptr, remaining);
|
uint64_t next_len = grapheme_next_character_break_utf8(ptr, remaining);
|
||||||
remaining -= next_len;
|
remaining -= next_len;
|
||||||
ptr += next_len;
|
ptr += next_len;
|
||||||
offset += next_len;
|
offset += next_len;
|
||||||
@@ -224,29 +247,78 @@ uint32_t Vase::offset_of(Point point) {
|
|||||||
return it.byte_offset() + offset;
|
return it.byte_offset() + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vase::move_clusters(Point *point, int64_t amount) {
|
Point Vase::point_of(uint64_t offset) {
|
||||||
|
ChunkIterator it(root, Direction::Backward);
|
||||||
|
it.seek_offset(offset);
|
||||||
|
Point p;
|
||||||
|
p.row = it.line_offset();
|
||||||
|
std::string line;
|
||||||
|
const char *chunk;
|
||||||
|
uint64_t len = 0;
|
||||||
|
if (!it.next(&chunk, &len))
|
||||||
|
return p;
|
||||||
|
while (true) {
|
||||||
|
#if defined(__GLIBC__) || defined(__APPLE__)
|
||||||
|
const char *nl = (const char *)memrchr(chunk, '\n', len);
|
||||||
|
#else
|
||||||
|
const char *nl = nullptr;
|
||||||
|
uint64_t i = len;
|
||||||
|
while (i--) {
|
||||||
|
if (chunk[i] == '\n') {
|
||||||
|
nl = chunk + i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!nl) {
|
||||||
|
if (len && chunk[len - 1] == '\r')
|
||||||
|
--len;
|
||||||
|
line.insert(0, chunk, len);
|
||||||
|
if (!it.next(&chunk, &len))
|
||||||
|
break;
|
||||||
|
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);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const char *ptr = line.data();
|
||||||
|
uint64_t remaining = line.length();
|
||||||
|
while (remaining) {
|
||||||
|
uint64_t next_len = grapheme_next_character_break_utf8(ptr, remaining);
|
||||||
|
remaining -= next_len;
|
||||||
|
ptr += next_len;
|
||||||
|
p.col++;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vase::move_clusters(Point *point, uint64_t amount, Direction dir) {
|
||||||
if (amount == 0)
|
if (amount == 0)
|
||||||
return;
|
return;
|
||||||
if (amount < 0) {
|
if (dir == Direction::Backward) {
|
||||||
amount = -amount;
|
|
||||||
LineIterator it(root, point->row, Direction::Backward);
|
LineIterator it(root, point->row, Direction::Backward);
|
||||||
while (amount > 0) {
|
while (amount) {
|
||||||
std::string line;
|
std::string line;
|
||||||
if (!it.next(line))
|
if (!it.next(line))
|
||||||
return;
|
return;
|
||||||
std::vector<uint32_t> clusters;
|
std::vector<uint64_t> clusters;
|
||||||
const char *ptr = line.data();
|
const char *ptr = line.data();
|
||||||
uint32_t remaining = line.size();
|
uint64_t remaining = line.size();
|
||||||
uint32_t byte = 0;
|
uint64_t byte = 0;
|
||||||
while (remaining) {
|
while (remaining) {
|
||||||
clusters.push_back(byte);
|
clusters.push_back(byte);
|
||||||
uint32_t len =
|
uint64_t len =
|
||||||
grapheme_next_character_break_utf8(ptr, remaining);
|
grapheme_next_character_break_utf8(ptr, remaining);
|
||||||
ptr += len;
|
ptr += len;
|
||||||
remaining -= len;
|
remaining -= len;
|
||||||
byte += len;
|
byte += len;
|
||||||
}
|
}
|
||||||
while (amount > 0 && point->col > 0) {
|
while (amount && point->col) {
|
||||||
point->col--;
|
point->col--;
|
||||||
amount--;
|
amount--;
|
||||||
}
|
}
|
||||||
@@ -260,29 +332,29 @@ void Vase::move_clusters(Point *point, int64_t amount) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LineIterator it(root, point->row, Direction::Forward);
|
LineIterator it(root, point->row, Direction::Forward);
|
||||||
while (amount > 0) {
|
while (amount) {
|
||||||
std::string line;
|
std::string line;
|
||||||
if (!it.next(line))
|
if (!it.next(line))
|
||||||
return;
|
return;
|
||||||
if (point->col > 0 || amount > 0) {
|
if (point->col || amount) {
|
||||||
const char *ptr = line.data();
|
const char *ptr = line.data();
|
||||||
uint32_t remaining = line.size();
|
uint64_t remaining = line.size();
|
||||||
uint32_t col = 0;
|
uint64_t col = 0;
|
||||||
while (col < point->col && remaining) {
|
while (col < point->col && remaining) {
|
||||||
uint32_t len = grapheme_next_character_break_utf8(ptr, remaining);
|
uint64_t len = grapheme_next_character_break_utf8(ptr, remaining);
|
||||||
ptr += len;
|
ptr += len;
|
||||||
remaining -= len;
|
remaining -= len;
|
||||||
col++;
|
col++;
|
||||||
}
|
}
|
||||||
while (amount > 0 && remaining) {
|
while (amount && remaining) {
|
||||||
uint32_t len = grapheme_next_character_break_utf8(ptr, remaining);
|
uint64_t len = grapheme_next_character_break_utf8(ptr, remaining);
|
||||||
ptr += len;
|
ptr += len;
|
||||||
remaining -= len;
|
remaining -= len;
|
||||||
point->col++;
|
point->col++;
|
||||||
amount--;
|
amount--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (amount > 0) {
|
if (amount) {
|
||||||
point->row++;
|
point->row++;
|
||||||
point->col = 0;
|
point->col = 0;
|
||||||
amount--;
|
amount--;
|
||||||
@@ -300,11 +372,11 @@ void Vase::clamp(Point *point) {
|
|||||||
point->col = 0;
|
point->col = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32_t clusters = 0;
|
uint64_t clusters = 0;
|
||||||
const char *ptr = line.data();
|
const char *ptr = line.data();
|
||||||
uint32_t remaining = line.size();
|
uint64_t remaining = line.size();
|
||||||
while (remaining) {
|
while (remaining) {
|
||||||
uint32_t len = grapheme_next_character_break_utf8(ptr, remaining);
|
uint64_t len = grapheme_next_character_break_utf8(ptr, remaining);
|
||||||
ptr += len;
|
ptr += len;
|
||||||
remaining -= len;
|
remaining -= len;
|
||||||
clusters++;
|
clusters++;
|
||||||
@@ -313,156 +385,14 @@ void Vase::clamp(Point *point) {
|
|||||||
point->col = clusters;
|
point->col = clusters;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vase::move_lines(Point *point, int64_t amount) {
|
void Vase::move_lines(Point *point, uint64_t amount, Direction dir) {
|
||||||
if (point->row + amount < 0)
|
if (dir == Direction::Forward) {
|
||||||
return;
|
|
||||||
point->row += amount;
|
point->row += amount;
|
||||||
|
} else {
|
||||||
|
if (amount > point->row)
|
||||||
|
point->row = 0;
|
||||||
|
else
|
||||||
|
point->row -= amount;
|
||||||
|
}
|
||||||
clamp(point);
|
clamp(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ReplacePart {
|
|
||||||
enum struct PartType {
|
|
||||||
FullMatch,
|
|
||||||
CaptureGroup,
|
|
||||||
Constant
|
|
||||||
} type;
|
|
||||||
|
|
||||||
std::variant<uint8_t, Shard *> value;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<ReplacePart> parse_replace(AppendBuffer &buf, std::string_view s) {
|
|
||||||
std::vector<ReplacePart> parts;
|
|
||||||
std::string constant;
|
|
||||||
auto flush_constant = [&]() {
|
|
||||||
if (!constant.empty()) {
|
|
||||||
uint32_t lines = 0;
|
|
||||||
uint32_t pos = buf.append(constant.data(), (uint32_t)constant.size());
|
|
||||||
parts.push_back(
|
|
||||||
ReplacePart{
|
|
||||||
.type = ReplacePart::PartType::Constant,
|
|
||||||
.value = new Petal((uint32_t)constant.size(), lines, &buf, pos)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
constant.clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
for (size_t i = 0; i < s.size(); ++i) {
|
|
||||||
char c = s[i];
|
|
||||||
if (c == '\\' && i + 1 < s.size() && s[i + 1] == '$') {
|
|
||||||
constant.push_back('$');
|
|
||||||
++i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == '$' && i + 1 < s.size()) {
|
|
||||||
char next = s[i + 1];
|
|
||||||
if (next == '0') {
|
|
||||||
flush_constant();
|
|
||||||
parts.push_back(
|
|
||||||
ReplacePart{
|
|
||||||
.type = ReplacePart::PartType::FullMatch,
|
|
||||||
.value = (uint8_t)0
|
|
||||||
}
|
|
||||||
);
|
|
||||||
++i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (next >= '1' && next <= '9') {
|
|
||||||
flush_constant();
|
|
||||||
parts.push_back(
|
|
||||||
ReplacePart{
|
|
||||||
.type = ReplacePart::PartType::CaptureGroup,
|
|
||||||
.value = (uint8_t)(next - '0')
|
|
||||||
}
|
|
||||||
);
|
|
||||||
++i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
constant.push_back(c);
|
|
||||||
}
|
|
||||||
flush_constant();
|
|
||||||
return parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Vase::regex_search_replace(
|
|
||||||
std::string_view pattern, Range range,
|
|
||||||
std::string_view replace, std::string_view options
|
|
||||||
) {
|
|
||||||
std::vector<RegexMatch> matches = regex_search(root, pattern, offset_of(range.start), offset_of(range.end), options);
|
|
||||||
if (matches.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::vector<ReplacePart> replace_parts = parse_replace(append, replace);
|
|
||||||
|
|
||||||
std::vector<Shard *> pieces;
|
|
||||||
pieces.reserve(matches.size() * 2 + 1);
|
|
||||||
|
|
||||||
Shard *remaining = root;
|
|
||||||
Shard::retain(remaining);
|
|
||||||
uint32_t cursor = 0;
|
|
||||||
|
|
||||||
for (const RegexMatch &match : matches) {
|
|
||||||
uint32_t gap = match.start - cursor;
|
|
||||||
if (gap > 0) {
|
|
||||||
auto [keep, rest] = split_shard(remaining, gap);
|
|
||||||
Shard::release(remaining);
|
|
||||||
pieces.push_back(keep);
|
|
||||||
remaining = rest;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto [dropped, rest2] = split_shard(remaining, match.end - match.start);
|
|
||||||
Shard::release(remaining);
|
|
||||||
remaining = rest2;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < replace_parts.size(); ++i) {
|
|
||||||
const ReplacePart &part = replace_parts[i];
|
|
||||||
switch (part.type) {
|
|
||||||
case ReplacePart::PartType::Constant:
|
|
||||||
Shard::retain(std::get<Shard *>(part.value));
|
|
||||||
pieces.push_back(std::get<Shard *>(part.value));
|
|
||||||
break;
|
|
||||||
case ReplacePart::PartType::FullMatch:
|
|
||||||
Shard::retain(dropped);
|
|
||||||
pieces.push_back(dropped);
|
|
||||||
break;
|
|
||||||
case ReplacePart::PartType::CaptureGroup: {
|
|
||||||
uint8_t idx = std::get<uint8_t>(part.value);
|
|
||||||
if (idx <= 9) {
|
|
||||||
const RegexGroup &group = match.groups[idx - 1];
|
|
||||||
if (group.start != UINT32_MAX) {
|
|
||||||
uint32_t ls = group.start - match.start;
|
|
||||||
uint32_t le = group.end - match.start;
|
|
||||||
auto [a, b] = split_shard(dropped, ls);
|
|
||||||
auto [g, c] = split_shard(b, le - ls);
|
|
||||||
Shard::release(a);
|
|
||||||
Shard::release(b);
|
|
||||||
Shard::release(c);
|
|
||||||
pieces.push_back(g);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Shard::release(dropped);
|
|
||||||
cursor = match.end;
|
|
||||||
}
|
|
||||||
pieces.push_back(remaining);
|
|
||||||
|
|
||||||
for (auto &part : replace_parts)
|
|
||||||
if (part.type == ReplacePart::PartType::Constant)
|
|
||||||
Shard::release(std::get<Shard *>(part.value));
|
|
||||||
|
|
||||||
std::vector<Shard *> compact;
|
|
||||||
compact.reserve(pieces.size());
|
|
||||||
for (Shard *p : pieces) {
|
|
||||||
if (p && p->length > 0)
|
|
||||||
compact.push_back(p);
|
|
||||||
else if (p)
|
|
||||||
Shard::release(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
Shard *new_root = compact.empty() ? nullptr : build_balanced(compact.data(), 0, compact.size());
|
|
||||||
Shard::release(root);
|
|
||||||
root = new_root;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user