Improvements with regex system.
This commit is contained in:
@@ -5,3 +5,5 @@ compile_commands.json
|
||||
bin/
|
||||
build/
|
||||
.cache/
|
||||
|
||||
sample.txt
|
||||
|
||||
@@ -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<RegexGroup> groups{};
|
||||
RegexGroup groups[9]{};
|
||||
};
|
||||
|
||||
const std::vector<RegexMatch> regex_search(
|
||||
std::vector<RegexMatch> 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<RegexMatch> &matches);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+32
-11
@@ -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<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(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<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;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
+7
-41
@@ -1,7 +1,7 @@
|
||||
#include "vase/search.h"
|
||||
#include "vase/iterators/chunk.h"
|
||||
|
||||
const std::vector<RegexMatch> regex_search(
|
||||
std::vector<RegexMatch> 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<RegexMatch> 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<RegexMatch> 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<RegexMatch> regex_search(
|
||||
offset = 0;
|
||||
break;
|
||||
} else {
|
||||
printf("A matching error occurred: %d\n", rc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -237,7 +231,6 @@ const std::vector<RegexMatch> regex_search(
|
||||
break;
|
||||
} else {
|
||||
offset = length;
|
||||
printf("A matching error occurred: %d\n", rc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -251,30 +244,3 @@ const std::vector<RegexMatch> regex_search(
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
+68
-70
@@ -98,18 +98,20 @@ struct ReplacePart {
|
||||
Constant
|
||||
} 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::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<ReplacePart> 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<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())
|
||||
return;
|
||||
|
||||
std::vector<ReplacePart> replace_parts = parse_replace(replace);
|
||||
std::vector<ReplacePart> replace_parts = parse_replace(append, replace);
|
||||
|
||||
struct ConstantRef {
|
||||
uint32_t pos = 0;
|
||||
uint32_t lines = 0;
|
||||
};
|
||||
std::vector<ConstantRef> 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<std::string>(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<Shard *> 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<std::string>(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<Shard *>(part.value));
|
||||
pieces.push_back(std::get<Shard *>(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<uint8_t>(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<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