From 89b24a74885db8bec74006ec090bb34e78feb014 Mon Sep 17 00:00:00 2001 From: Syed Daanish Date: Wed, 29 Jul 2026 16:40:08 +0100 Subject: [PATCH] Improvements with regex system. --- .gitignore | 2 + include/vase/search.h | 10 ++- include/vase/shard.h | 8 +-- src/main.cc | 43 +++++++++---- src/vase/search.cc | 48 +++------------ src/vase/vase.cc | 138 +++++++++++++++++++++--------------------- 6 files changed, 117 insertions(+), 132 deletions(-) diff --git a/.gitignore b/.gitignore index 21c25e4..782e984 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ compile_commands.json bin/ build/ .cache/ + +sample.txt diff --git a/include/vase/search.h b/include/vase/search.h index c60e35c..1d6ec50 100644 --- a/include/vase/search.h +++ b/include/vase/search.h @@ -4,20 +4,18 @@ #include "vase/shard.h" struct RegexGroup { - uint32_t start; - uint32_t end; - bool matched; + uint32_t start{UINT32_MAX}; + uint32_t end{UINT32_MAX}; }; struct RegexMatch { uint32_t start; uint32_t end; - std::vector groups{}; + RegexGroup groups[9]{}; }; -const std::vector regex_search( +std::vector regex_search( Shard *root, std::string_view pattern_str, uint32_t start_offset, uint32_t end_offset, std::string_view options ); -void print_regex(const std::vector &matches); diff --git a/include/vase/shard.h b/include/vase/shard.h index 655b2ca..7ec3af1 100644 --- a/include/vase/shard.h +++ b/include/vase/shard.h @@ -4,20 +4,20 @@ #include "pch.h" struct Shard { - enum struct ShardKind { + enum struct ShardKind : uint8_t { Branch, Petal } kind; + uint8_t height; + uint32_t length; uint32_t lines; - uint8_t height; - std::atomic_uint32_t refs; 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; diff --git a/src/main.cc b/src/main.cc index 59c2721..c21697f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -24,7 +24,15 @@ int main(int argc, char *argv[]) { char *text; read_file(path, &text, &len); + auto start = std::chrono::steady_clock::now(); Vase vase = Vase(text, len); + auto end = std::chrono::steady_clock::now(); + std::cout << "Time to load: " + << std::chrono::duration_cast(end - start).count() + << " ms\n"; + + std::cout << "Press Enter to continue..."; + std::cin.get(); vase.insert(1, "bcgr\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); // supports gisxUn and m as inverse (as multiline is default.) - std::vector 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 matches = regex_search(vase.root, "here", 0, vase.length(), ""); + end = std::chrono::steady_clock::now(); + std::cout << "Time search: " + << std::chrono::duration_cast(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; 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(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; uint32_t length; while (it2.next(&data, &length)) - std::cout << std::string(data, length); + std::cout << std::string(data, length);*/ - std::cout << "\n\n" - << vase.to_string(); + // std::cout << vase.to_string(); return 0; } diff --git a/src/vase/search.cc b/src/vase/search.cc index f1f8120..be407f7 100644 --- a/src/vase/search.cc +++ b/src/vase/search.cc @@ -1,7 +1,7 @@ #include "vase/search.h" #include "vase/iterators/chunk.h" -const std::vector regex_search( +std::vector regex_search( Shard *root, std::string_view pattern_str, uint32_t start_offset, uint32_t end_offset, std::string_view options @@ -51,12 +51,8 @@ const std::vector regex_search( 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); + if (re == NULL) return results; - } pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(re, NULL); @@ -77,14 +73,13 @@ const std::vector regex_search( .start = global_offset + (uint32_t)ovector[0], .end = global_offset + (uint32_t)ovector[1] }; - match.groups.reserve(rc - 1); - for (int i = 1; i < rc; ++i) { + for (int i = 1; i < rc && i <= 9; ++i) { PCRE2_SIZE s = ovector[2 * i]; PCRE2_SIZE e = ovector[2 * i + 1]; - if (s == PCRE2_UNSET) - match.groups.push_back({0, 0, false}); - else - match.groups.push_back({global_offset + (uint32_t)s, global_offset + (uint32_t)e, true}); + if (s != PCRE2_UNSET) { + match.groups[i].start = global_offset + (uint32_t)s; + match.groups[i].end = global_offset + (uint32_t)e; + } } results.push_back(std::move(match)); }; @@ -186,7 +181,6 @@ const std::vector regex_search( offset = 0; break; } else { - printf("A matching error occurred: %d\n", rc); break; } } @@ -237,7 +231,6 @@ const std::vector regex_search( break; } else { offset = length; - printf("A matching error occurred: %d\n", rc); break; } } @@ -251,30 +244,3 @@ const std::vector regex_search( return results; } - -void print_regex(const std::vector &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"; -} diff --git a/src/vase/vase.cc b/src/vase/vase.cc index d7a16d5..684f9d5 100644 --- a/src/vase/vase.cc +++ b/src/vase/vase.cc @@ -98,18 +98,20 @@ struct ReplacePart { Constant } type; - std::variant value; + std::variant value; }; -std::vector parse_replace(std::string_view s) { +std::vector parse_replace(AppendBuffer &buf, std::string_view s) { std::vector 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(), &lines); parts.push_back( ReplacePart{ .type = ReplacePart::PartType::Constant, - .value = std::move(constant) + .value = new Petal((uint32_t)constant.size(), lines, &buf, pos) } ); constant.clear(); @@ -153,28 +155,16 @@ std::vector parse_replace(std::string_view s) { return parts; } -static Shard *extract_range(Shard *tree, uint32_t start, uint32_t end) { - if (end <= start) - return nullptr; - auto [left, rest] = split_shard(tree, start); - auto [mid, right] = split_shard(rest, end - start); +Shard *build_balanced(Shard **pieces, size_t lo, size_t hi) { + if (hi - lo == 1) + return pieces[lo]; + size_t mid = lo + (hi - lo) / 2; + 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(rest); Shard::release(right); - return mid; -} - -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; + return node; } void Vase::regex_search_replace( @@ -182,73 +172,81 @@ void Vase::regex_search_replace( uint32_t start_offset, uint32_t end_offset, std::string_view replace, std::string_view options ) { - const std::vector matches = regex_search(root, pattern, start_offset, end_offset, options); + std::vector matches = regex_search(root, pattern, start_offset, end_offset, options); if (matches.empty()) return; - std::vector replace_parts = parse_replace(replace); + std::vector replace_parts = parse_replace(append, replace); - struct ConstantRef { - uint32_t pos = 0; - uint32_t lines = 0; - }; - std::vector constants(replace_parts.size()); - for (size_t i = 0; i < replace_parts.size(); ++i) { - if (replace_parts[i].type != ReplacePart::PartType::Constant) - continue; - const std::string &text = std::get(replace_parts[i].value); - uint32_t lines = 0; - uint32_t pos = append.append(text.data(), (uint32_t)text.size(), &lines); - constants[i] = {pos, lines}; - } + std::vector pieces; + pieces.reserve(matches.size() * 2 + 1); - for (auto it = matches.rbegin(); it != matches.rend(); ++it) { - const RegexMatch &match = *it; + 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; - Shard *replacement = nullptr; for (size_t i = 0; i < replace_parts.size(); ++i) { const ReplacePart &part = replace_parts[i]; switch (part.type) { - case ReplacePart::PartType::Constant: { - const std::string &text = std::get(part.value); - const ConstantRef &ref = constants[i]; - Shard *piece = new Petal((uint32_t)text.size(), ref.lines, &append, ref.pos); - append_piece(replacement, piece); + case ReplacePart::PartType::Constant: + Shard::retain(std::get(part.value)); + pieces.push_back(std::get(part.value)); break; - } case ReplacePart::PartType::FullMatch: - append_piece(replacement, extract_range(root, match.start, match.end)); + Shard::retain(dropped); + pieces.push_back(dropped); break; case ReplacePart::PartType::CaptureGroup: { uint8_t idx = std::get(part.value); - if (idx <= match.groups.size()) { + if (idx <= 9) { const RegexGroup &group = match.groups[idx - 1]; - if (group.matched) - append_piece(replacement, extract_range(root, group.start, group.end)); + 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; } } } - - auto [left, rest] = split_shard(root, match.start); - auto [dropped, right] = split_shard(rest, match.end - match.start); Shard::release(dropped); - Shard::release(rest); - - 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); - Shard::release(root); - root = new_root; + cursor = match.end; } + pieces.push_back(remaining); + + for (auto &part : replace_parts) + if (part.type == ReplacePart::PartType::Constant) + Shard::release(std::get(part.value)); + + std::vector 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; }