Improvements with regex system.
This commit is contained in:
@@ -5,3 +5,5 @@ compile_commands.json
|
|||||||
bin/
|
bin/
|
||||||
build/
|
build/
|
||||||
.cache/
|
.cache/
|
||||||
|
|
||||||
|
sample.txt
|
||||||
|
|||||||
@@ -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,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
@@ -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
@@ -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
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user