Compare commits

..

2 Commits

Author SHA1 Message Date
03cb852bad
more bug fixes 2022-12-17 02:50:37 +01:00
5cba374d2b
bugfixing, debug. still broken, but better 2022-12-17 01:18:52 +01:00
3 changed files with 239 additions and 15 deletions

View File

@ -147,8 +147,13 @@ struct TextDocument {
std::vector<Op> ops;
for (size_t i = first_idx; i < last_idx; i++) {
if (!state.list.at(i).value.has_value()) {
// allready deleted
continue;
}
ops.emplace_back(typename ListType::OpDel{
state.list[i].id
state.list.at(i).id
});
// TODO: do delets get a seq?????
@ -163,7 +168,7 @@ struct TextDocument {
// note: rn it only creates 1 diff patch
std::vector<Op> merge(std::string_view text) {
if (text.empty()) {
if (state.list.empty()) {
if (state.list.empty() || state.doc_size == 0) {
// no op
return {};
} else {
@ -185,6 +190,7 @@ struct TextDocument {
// find start and end of changes
// start
size_t list_start = 0;
size_t list_start_counted = 0;
size_t text_start = 0;
bool differ = false;
for (; list_start < state.list.size() && text_start < text.size();) {
@ -201,6 +207,7 @@ struct TextDocument {
list_start++;
text_start++;
list_start_counted++;
}
// doc and text dont differ
@ -214,7 +221,10 @@ struct TextDocument {
// +1 so i can have unsigned
size_t list_end = state.list.size();
size_t text_end = text.size();
for (; list_end > 0 && text_end > 0 && list_end >= list_start && text_end >= text_start;) {
//for (; list_end > 0 && text_end > 0 && list_end >= list_start && text_end >= text_start;) {
//while (list_end >= list_start && text_end >= text_start) {
size_t list_end_counted = 0;
while (list_start_counted - list_end_counted > state.doc_size && text_end >= text_start) {
// jump over tombstones
if (!state.list[list_end-1].value.has_value()) {
list_end--;
@ -227,6 +237,7 @@ struct TextDocument {
list_end--;
text_end--;
list_end_counted++;
}
//std::cout << "list_end: " << list_end << " text_end: " << text_end << "\n";
@ -237,7 +248,7 @@ struct TextDocument {
if (list_start <= list_end && list_start < state.list.size()) {
ops = delRange(
state.list[list_start].id,
list_end < state.list.size() ? std::make_optional(state.list[list_end].id) : std::nullopt
(list_start == list_end ? list_end+1 : list_end) < state.list.size() ? std::make_optional(state.list[list_end].id) : std::nullopt
);
//std::cout << "deleted: " << ops.size() << "\n";
}
@ -245,11 +256,11 @@ struct TextDocument {
//std::cout << "text between: " << getText() << "\n";
// 2. add range (add all text_start - text_end)
if (text_start < text_end) {
if (state.doc_size < text.size()) {
auto tmp_add_ops = addText(
list_start == 0 ? std::nullopt : std::make_optional(state.list[list_start-1].id),
list_start == state.list.size() ? std::nullopt :std::make_optional(state.list.at(list_start).id),
text.substr(text_start, text_end-text_start)
text.substr(text_start, text.size() - state.doc_size)
);
//std::cout << "added: " << tmp_add_ops.size() << "\n";
ops.insert(ops.end(), tmp_add_ops.begin(), tmp_add_ops.end());

View File

@ -5,6 +5,7 @@
#include <random>
#include <iostream>
#include <cassert>
#include <variant>
// single letter agent, for testing only
using Agent = char;
@ -398,8 +399,106 @@ void testChange1(size_t seed) {
assert(doc.getText() == otherdoc.getText());
}
void testBugSame(void) {
Doc doc;
doc.local_agent = 'A';
std::string_view new_text1{"a"};
doc.merge(new_text1);
assert(doc.getText() == new_text1);
std::string_view new_text2{"aa"};
doc.merge(new_text2);
assert(doc.getText() == new_text2);
}
void testBugDoubleDel(void) {
Doc doc;
doc.local_agent = 'A';
{
std::string_view new_text{"a"};
doc.merge(new_text);
assert(doc.getText() == new_text);
}
{
std::string_view new_text{""};
const auto ops = doc.merge(new_text);
assert(doc.getText() == new_text);
assert(ops.size() == 1);
assert(std::holds_alternative<ListType::OpDel>(ops.front()));
assert(std::get<ListType::OpDel>(ops.front()).id.seq == 0);
}
{
std::string_view new_text{""};
const auto ops = doc.merge(new_text);
assert(doc.getText() == new_text);
assert(ops.size() == 0);
}
}
void testBugSameDel(void) {
Doc doc;
doc.local_agent = 'A';
{
std::string_view new_text{"a"};
doc.merge(new_text);
assert(doc.getText() == new_text);
}
{
std::string_view new_text{"aa"};
const auto ops = doc.merge(new_text);
assert(doc.getText() == new_text);
}
{
std::string_view new_text{"a"};
const auto ops = doc.merge(new_text);
assert(doc.getText() == new_text);
}
}
void testBugSameDel2(void) {
Doc doc;
doc.local_agent = 'A';
{
std::string_view new_text{"a"};
doc.merge(new_text);
assert(doc.getText() == new_text);
}
{
std::string_view new_text{"aa"};
const auto ops = doc.merge(new_text);
assert(doc.getText() == new_text);
}
{
std::string_view new_text{"aaa"};
const auto ops = doc.merge(new_text);
assert(doc.getText() == new_text);
}
{
std::string_view new_text{"aa"};
const auto ops = doc.merge(new_text);
assert(doc.getText() == new_text);
}
{
std::string_view new_text{"a"};
const auto ops = doc.merge(new_text);
assert(doc.getText() == new_text);
}
}
int main(void) {
const size_t loops = 10'000;
const size_t loops = 1'000;
{
std::cout << "testEmptyDocAdds:\n";
for (size_t i = 0; i < loops; i++) {
@ -453,6 +552,34 @@ int main(void) {
}
}
std::cout << std::string(40, '=') << "\n";
{
std::cout << "testBugSame:\n";
testBugSame();
}
std::cout << std::string(40, '=') << "\n";
{
std::cout << "testBugDoubleDel:\n";
testBugDoubleDel();
}
std::cout << std::string(40, '=') << "\n";
{
std::cout << "testBugSameDel:\n";
testBugSameDel();
}
std::cout << std::string(40, '=') << "\n";
{
std::cout << "testBugSameDel2:\n";
testBugSameDel2();
}
return 0;
}

View File

@ -1,18 +1,83 @@
#include <crdt/text_document.hpp>
#include <nlohmann/json.hpp>
extern "C" {
#include <zed_net.h>
}
#include <memory>
#include <string_view>
#include <zed_net.h>
#include <variant>
#include <iostream>
#include <cassert>
// single letter agent, for testing only
using Agent = char;
//using Agent = char;
using Agent = uint16_t; // tmp local port
using Doc = GreenCRDT::TextDocument<Agent>;
using ListType = Doc::ListType;
std::ostream& operator<<(std::ostream& out, const std::optional<ListType::ListID>& id) {
if (id.has_value()) {
out << id.value().id << "-" << id.value().seq;
} else {
out << "null";
}
return out;
}
std::ostream& operator<<(std::ostream& out, const ListType::OpAdd& op) {
out
<< "Add{ id:" << op.id.id
<< "-" << op.id.seq
<< ", v:" << op.value
<< ", l:" << op.parent_left
<< ", r:" << op.parent_right
<< " }"
;
return out;
}
std::ostream& operator<<(std::ostream& out, const ListType::OpDel& op) {
out
<< "Del{ id:" << op.id.id
<< "-" << op.id.seq
<< " }"
;
return out;
}
std::ostream& operator<<(std::ostream& out, const Doc::Op& op) {
if (std::holds_alternative<ListType::OpAdd>(op)) {
out << std::get<ListType::OpAdd>(op);
} else if (std::holds_alternative<ListType::OpDel>(op)) {
out << std::get<ListType::OpDel>(op);
}
return out;
}
std::ostream& operator<<(std::ostream& out, const std::optional<char>& id) {
if (id.has_value()) {
out << id.value();
} else {
out << "null";
}
return out;
}
std::ostream& operator<<(std::ostream& out, const ListType::Entry& e) {
out
<< "{ id:" << e.id.id
<< "-" << e.id.seq
<< ", v:" << e.value
<< ", l:" << e.parent_left
<< ", r:" << e.parent_right
<< " }"
;
return out;
}
static bool send_command(zed_net_socket_t* remote_socket, const std::string_view mode, const std::string_view command) {
auto j = nlohmann::json::array();
@ -106,7 +171,7 @@ int main(void) {
std::cout << "initialized zed_net\n";
const unsigned int port {1337};
const uint16_t port {1337};
zed_net_socket_t listen_socket;
if (zed_net_tcp_socket_open(
&listen_socket,
@ -142,9 +207,11 @@ int main(void) {
// send doauto text changed for inital buffer
Doc doc;
doc.local_agent = remote_address.port; // tmp: use local port as id
while (true) {
// 10MiB
auto buffer = std::make_unique<std::array<uint8_t, 1024*1024*10>>();
// 100MiB
auto buffer = std::make_unique<std::array<uint8_t, 1024*1024*100>>();
int64_t bytes_received {0};
bytes_received = zed_net_tcp_socket_receive(&remote_socket, buffer->data(), buffer->size());
@ -227,15 +294,34 @@ int main(void) {
}
std::string new_text;
for (const auto& line : j_lines) {
new_text += line;
new_text += '\n';
for (size_t i = 0; i < j_lines.size(); i++) {
if (!j_lines.at(i).empty()) {
new_text += static_cast<std::string>(j_lines.at(i));
}
if (i+1 < j_lines.size()) {
new_text += "\n";
}
}
//std::cout << "new_text:\n" << new_text << "\n";
//std::cout << "old_text:\n" << doc.getText() << "\n";
std::cout << "doc state: ";
for (const auto& e : doc.state.list) {
std::cout << e << " ";
}
std::cout << "\n";
const auto ops = doc.merge(new_text);
if (!ops.empty()) {
std::cout << "ops.size: " << ops.size() << "\n";
std::cout << "ops: ";
for (const auto& op : ops) {
std::cout << op << " ";
}
std::cout << "\n";
}
assert(doc.getText() == new_text);
} else {
std::cout << "unknown command '" << command << "'\n";
}