Improvements with regex system.

This commit is contained in:
2026-07-29 16:40:08 +01:00
parent b10f1291a0
commit 89b24a7488
6 changed files with 117 additions and 132 deletions
+2
View File
@@ -5,3 +5,5 @@ compile_commands.json
bin/ bin/
build/ build/
.cache/ .cache/
sample.txt
+4 -6
View File
@@ -4,20 +4,18 @@
#include "vase/shard.h" #include "vase/shard.h"
struct RegexGroup { struct RegexGroup {
uint32_t start; uint32_t start{UINT32_MAX};
uint32_t end; uint32_t end{UINT32_MAX};
bool matched;
}; };
struct RegexMatch { struct RegexMatch {
uint32_t start; uint32_t start;
uint32_t end; uint32_t end;
std::vector<RegexGroup> groups{}; RegexGroup groups[9]{};
}; };
const std::vector<RegexMatch> regex_search( std::vector<RegexMatch> regex_search(
Shard *root, std::string_view pattern_str, Shard *root, std::string_view pattern_str,
uint32_t start_offset, uint32_t end_offset, uint32_t start_offset, uint32_t end_offset,
std::string_view options std::string_view options
); );
void print_regex(const std::vector<RegexMatch> &matches);
+4 -4
View File
@@ -4,20 +4,20 @@
#include "pch.h" #include "pch.h"
struct Shard { struct Shard {
enum struct ShardKind { enum struct ShardKind : uint8_t {
Branch, Branch,
Petal Petal
} kind; } kind;
uint8_t height;
uint32_t length; uint32_t length;
uint32_t lines; uint32_t lines;
uint8_t height;
std::atomic_uint32_t refs; std::atomic_uint32_t refs;
Shard(ShardKind kind, uint32_t length, uint32_t lines, uint8_t height) Shard(ShardKind kind, uint32_t length, uint32_t lines, uint8_t height)
: kind(kind), length(length), lines(lines), height(height), refs(1) {}; : kind(kind), height(height), length(length), lines(lines), refs(1) {};
virtual ~Shard() = default; virtual ~Shard() = default;
+32 -11
View File
@@ -24,7 +24,15 @@ int main(int argc, char *argv[]) {
char *text; char *text;
read_file(path, &text, &len); read_file(path, &text, &len);
auto start = std::chrono::steady_clock::now();
Vase vase = Vase(text, len); Vase vase = Vase(text, len);
auto end = std::chrono::steady_clock::now();
std::cout << "Time to load: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
<< " ms\n";
std::cout << "Press Enter to continue...";
std::cin.get();
vase.insert(1, "bcgr\ntt", 5); vase.insert(1, "bcgr\ntt", 5);
vase.insert(2, "cgr\ntt", 5); vase.insert(2, "cgr\ntt", 5);
@@ -40,28 +48,41 @@ int main(int argc, char *argv[]) {
vase.erase(offset_of(vase.root, 7, 3), -3); vase.erase(offset_of(vase.root, 7, 3), -3);
// supports gisxUn and m as inverse (as multiline is default.) // supports gisxUn and m as inverse (as multiline is default.)
std::vector<RegexMatch> matches = regex_search(vase.root, "a(.)c", 0, vase.length(), "s"); // print_regex(matches); // debug
print_regex(matches); // debug start = std::chrono::steady_clock::now();
std::vector<RegexMatch> matches = regex_search(vase.root, "here", 0, vase.length(), "");
end = std::chrono::steady_clock::now();
std::cout << "Time search: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
<< " ms\n";
print_shard(vase.root); // debug // print_shard(vase.root); // debug
LineIterator it(vase.root, 0); /*LineIterator it(vase.root, 0);
std::string line; std::string line;
while (it.next(line)) while (it.next(line))
std::cout << line << "\n"; std::cout << line << "\n";*/
vase.regex_search_replace("a(.)c", 0, vase.length(), "|$1|", "g"); /*start = std::chrono::steady_clock::now();
vase.regex_search_replace("here", 0, vase.length(), "hello", "");
end = std::chrono::steady_clock::now();
std::cout << "Time replace: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
<< " ms\n";*/
std::cout << "\n\n"; std::cout << "\n\n"
<< (int)vase.root->height << " append:" << (int)vase.append.current_offset;
ChunkIterator it2(vase.root); std::cout << "Press Enter to continue...";
std::cin.get();
/*ChunkIterator it2(vase.root);
const char *data; const char *data;
uint32_t length; uint32_t length;
while (it2.next(&data, &length)) while (it2.next(&data, &length))
std::cout << std::string(data, length); std::cout << std::string(data, length);*/
std::cout << "\n\n" // std::cout << vase.to_string();
<< vase.to_string();
return 0; return 0;
} }
+7 -41
View File
@@ -1,7 +1,7 @@
#include "vase/search.h" #include "vase/search.h"
#include "vase/iterators/chunk.h" #include "vase/iterators/chunk.h"
const std::vector<RegexMatch> regex_search( std::vector<RegexMatch> regex_search(
Shard *root, std::string_view pattern_str, Shard *root, std::string_view pattern_str,
uint32_t start_offset, uint32_t end_offset, uint32_t start_offset, uint32_t end_offset,
std::string_view options std::string_view options
@@ -51,12 +51,8 @@ const std::vector<RegexMatch> regex_search(
NULL NULL
); );
if (re == NULL) { if (re == NULL)
PCRE2_UCHAR buffer[256];
pcre2_get_error_message(errornumber, buffer, sizeof(buffer));
printf("Compilation failed at offset %d: %s\n", (int)erroroffset, buffer);
return results; return results;
}
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);
@@ -77,14 +73,13 @@ const std::vector<RegexMatch> regex_search(
.start = global_offset + (uint32_t)ovector[0], .start = global_offset + (uint32_t)ovector[0],
.end = global_offset + (uint32_t)ovector[1] .end = global_offset + (uint32_t)ovector[1]
}; };
match.groups.reserve(rc - 1); for (int i = 1; i < rc && i <= 9; ++i) {
for (int i = 1; i < rc; ++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.push_back({0, 0, false}); match.groups[i].start = global_offset + (uint32_t)s;
else match.groups[i].end = global_offset + (uint32_t)e;
match.groups.push_back({global_offset + (uint32_t)s, global_offset + (uint32_t)e, true}); }
} }
results.push_back(std::move(match)); results.push_back(std::move(match));
}; };
@@ -186,7 +181,6 @@ const std::vector<RegexMatch> regex_search(
offset = 0; offset = 0;
break; break;
} else { } else {
printf("A matching error occurred: %d\n", rc);
break; break;
} }
} }
@@ -237,7 +231,6 @@ const std::vector<RegexMatch> regex_search(
break; break;
} else { } else {
offset = length; offset = length;
printf("A matching error occurred: %d\n", rc);
break; break;
} }
} }
@@ -251,30 +244,3 @@ const std::vector<RegexMatch> regex_search(
return results; return results;
} }
void print_regex(const std::vector<RegexMatch> &matches) {
std::cout << "RegexMatches (" << matches.size() << "):\n";
for (size_t i = 0; i < matches.size(); ++i) {
const auto &match = matches[i];
std::cout << " [" << i << "] {\n";
std::cout << " start: " << match.start << '\n';
std::cout << " end: " << match.end << '\n';
std::cout << " groups (" << match.groups.size() << "):\n";
for (size_t j = 0; j < match.groups.size(); ++j) {
const auto &group = match.groups[j];
std::cout << " [" << j << "] { "
<< "matched: " << (group.matched ? "true" : "false")
<< ", start: " << group.start
<< ", end: " << group.end
<< " }\n";
}
std::cout << " }\n";
}
std::cout << "\n\n";
}
+65 -67
View File
@@ -98,18 +98,20 @@ struct ReplacePart {
Constant Constant
} type; } type;
std::variant<uint8_t, std::string> value; std::variant<uint8_t, Shard *> value;
}; };
std::vector<ReplacePart> parse_replace(std::string_view s) { std::vector<ReplacePart> parse_replace(AppendBuffer &buf, std::string_view s) {
std::vector<ReplacePart> parts; std::vector<ReplacePart> parts;
std::string constant; std::string constant;
auto flush_constant = [&]() { auto flush_constant = [&]() {
if (!constant.empty()) { if (!constant.empty()) {
uint32_t lines = 0;
uint32_t pos = buf.append(constant.data(), (uint32_t)constant.size(), &lines);
parts.push_back( parts.push_back(
ReplacePart{ ReplacePart{
.type = ReplacePart::PartType::Constant, .type = ReplacePart::PartType::Constant,
.value = std::move(constant) .value = new Petal((uint32_t)constant.size(), lines, &buf, pos)
} }
); );
constant.clear(); constant.clear();
@@ -153,28 +155,16 @@ std::vector<ReplacePart> parse_replace(std::string_view s) {
return parts; return parts;
} }
static Shard *extract_range(Shard *tree, uint32_t start, uint32_t end) { Shard *build_balanced(Shard **pieces, size_t lo, size_t hi) {
if (end <= start) if (hi - lo == 1)
return nullptr; return pieces[lo];
auto [left, rest] = split_shard(tree, start); size_t mid = lo + (hi - lo) / 2;
auto [mid, right] = split_shard(rest, end - start); Shard *left = build_balanced(pieces, lo, mid);
Shard *right = build_balanced(pieces, mid, hi);
Shard *node = new Branch(left, right);
Shard::release(left); Shard::release(left);
Shard::release(rest);
Shard::release(right); Shard::release(right);
return mid; return node;
}
static void append_piece(Shard *&accum, Shard *piece) {
if (!piece)
return;
if (!accum) {
accum = piece;
return;
}
Shard *combined = concat_shard(accum, piece);
Shard::release(accum);
Shard::release(piece);
accum = combined;
} }
void Vase::regex_search_replace( void Vase::regex_search_replace(
@@ -182,73 +172,81 @@ void Vase::regex_search_replace(
uint32_t start_offset, uint32_t end_offset, uint32_t start_offset, uint32_t end_offset,
std::string_view replace, std::string_view options std::string_view replace, std::string_view options
) { ) {
const std::vector<RegexMatch> matches = regex_search(root, pattern, start_offset, end_offset, options); std::vector<RegexMatch> matches = regex_search(root, pattern, start_offset, end_offset, options);
if (matches.empty()) if (matches.empty())
return; return;
std::vector<ReplacePart> replace_parts = parse_replace(replace); std::vector<ReplacePart> replace_parts = parse_replace(append, replace);
struct ConstantRef { std::vector<Shard *> pieces;
uint32_t pos = 0; pieces.reserve(matches.size() * 2 + 1);
uint32_t lines = 0;
}; Shard *remaining = root;
std::vector<ConstantRef> constants(replace_parts.size()); Shard::retain(remaining);
for (size_t i = 0; i < replace_parts.size(); ++i) { uint32_t cursor = 0;
if (replace_parts[i].type != ReplacePart::PartType::Constant)
continue; for (const RegexMatch &match : matches) {
const std::string &text = std::get<std::string>(replace_parts[i].value); uint32_t gap = match.start - cursor;
uint32_t lines = 0; if (gap > 0) {
uint32_t pos = append.append(text.data(), (uint32_t)text.size(), &lines); auto [keep, rest] = split_shard(remaining, gap);
constants[i] = {pos, lines}; Shard::release(remaining);
pieces.push_back(keep);
remaining = rest;
} }
for (auto it = matches.rbegin(); it != matches.rend(); ++it) { auto [dropped, rest2] = split_shard(remaining, match.end - match.start);
const RegexMatch &match = *it; Shard::release(remaining);
remaining = rest2;
Shard *replacement = nullptr;
for (size_t i = 0; i < replace_parts.size(); ++i) { for (size_t i = 0; i < replace_parts.size(); ++i) {
const ReplacePart &part = replace_parts[i]; const ReplacePart &part = replace_parts[i];
switch (part.type) { switch (part.type) {
case ReplacePart::PartType::Constant: { case ReplacePart::PartType::Constant:
const std::string &text = std::get<std::string>(part.value); Shard::retain(std::get<Shard *>(part.value));
const ConstantRef &ref = constants[i]; pieces.push_back(std::get<Shard *>(part.value));
Shard *piece = new Petal((uint32_t)text.size(), ref.lines, &append, ref.pos);
append_piece(replacement, piece);
break; break;
}
case ReplacePart::PartType::FullMatch: case ReplacePart::PartType::FullMatch:
append_piece(replacement, extract_range(root, match.start, match.end)); Shard::retain(dropped);
pieces.push_back(dropped);
break; break;
case ReplacePart::PartType::CaptureGroup: { case ReplacePart::PartType::CaptureGroup: {
uint8_t idx = std::get<uint8_t>(part.value); uint8_t idx = std::get<uint8_t>(part.value);
if (idx <= match.groups.size()) { if (idx <= 9) {
const RegexGroup &group = match.groups[idx - 1]; const RegexGroup &group = match.groups[idx - 1];
if (group.matched) if (group.start != UINT32_MAX) {
append_piece(replacement, extract_range(root, group.start, group.end)); 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; break;
} }
} }
} }
auto [left, rest] = split_shard(root, match.start);
auto [dropped, right] = split_shard(rest, match.end - match.start);
Shard::release(dropped); Shard::release(dropped);
Shard::release(rest); cursor = match.end;
Shard *new_root;
if (replacement) {
Shard *left2 = concat_shard(left, replacement);
Shard::release(left);
Shard::release(replacement);
new_root = concat_shard(left2, right);
Shard::release(left2);
} else {
new_root = concat_shard(left, right);
Shard::release(left);
} }
Shard::release(right); 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); Shard::release(root);
root = new_root; root = new_root;
} }
}