diff -r 273faff8b848 -r 82b7f6e41a65 src/XMLDocumentConstructor.h --- a/src/XMLDocumentConstructor.h Tue Jun 01 19:25:12 2021 +0200 +++ b/src/XMLDocumentConstructor.h Sat Jun 05 20:03:13 2021 +0200 @@ -40,14 +40,23 @@ enum class Mode { ROOT, - SEQUENCE, - MAPPING, - MAP_KEY + ARRAY, + MAP_KEY, + MAP_VALUE, + CHAR_STRING, + BYTE_STRING }; - std::string rootName = "cbor"; + + Glib::ustring rootName = "cbor"; + Glib::ustring itemName; xmlpp::Element* current; std::vector mode; + /** + * number of remainin items in the fixed-size container (map or array) at current tree-level + */ + std::vector remainingItems; +#define INDEFINITE -1 cbor_callbacks callbacks = cbor_empty_callbacks; @@ -66,11 +75,71 @@ return current->get_parent() == nullptr ? current : current->get_parent(); } + void checkRemainingItems() { + if (mode.back() == Mode::ARRAY || mode.back() == Mode::MAP_KEY) { + if (remainingItems.back() != INDEFINITE) { + remainingItems.back()--; + if (remainingItems.back() < 1) containerEnd(); + } + } + } + void appendScalarValue(Glib::ustring value, Glib::ustring cborType, bool isNull = false) { - // TODO: different behavior depending on mode - xmlpp::Element* element = current->add_child("scalar"); - element->add_child_text(value); - element->set_attribute("type", cborType); + // TODO: null + if (mode.back() == Mode::ARRAY) { + current->add_child(itemName)->add_child_text(value); + } else if (mode.back() == Mode::MAP_KEY) { + current = current->add_child(value); + mode.push_back(Mode::MAP_VALUE); + } else if (mode.back() == Mode::MAP_VALUE) { + current->add_child_text(value); + current = parentOrSelf(current); + mode.pop_back(); + } else if (mode.back() == Mode::ROOT) { + current->add_child_text(value); + } else { + // TODO: process YAML_SCALAR_EVENT + } + + checkRemainingItems(); + } + + void arrayStart(ssize_t size) { + xmlpp::Element* parent = current->get_parent(); + if (parent) { + itemName = current->get_name(); + parent->remove_child(current); + current = parent; + } else { + itemName = "item"; + } + + if (mode.back() == Mode::MAP_KEY) mode.pop_back(); + mode.push_back(Mode::ARRAY); + remainingItems.push_back(size); + } + + void mapStart(ssize_t size) { + if (mode.back() == Mode::ROOT) { + } else if (mode.back() == Mode::ARRAY) { + current = current->add_child(itemName); + } else if (mode.back() == Mode::MAP_VALUE) { + mode.pop_back(); + // TODO: remainingItems + } else { + // TODO: map might be a key of another map → wrap/nest + // …probably not + } + + mode.push_back(Mode::MAP_KEY); + remainingItems.push_back(size); + } + + void containerEnd() { + current->add_child_comment(Glib::ustring::compose("end of a container: mode = %1", (int) mode.back())); // FIXME: remove + remainingItems.pop_back(); + if (mode.back() == Mode::MAP_KEY || mode.back() == Mode::MAP_VALUE) current = parentOrSelf(current); + mode.pop_back(); // TODO: assert map/array } public: @@ -82,9 +151,7 @@ callbacks.array_start = [](void* context, size_t size) { CBOR_CALLBACK_START - // TODO: implement - xmlpp::Element* element = instance->current->add_child("array-start"); - element->set_attribute("size", std::to_string(size)); + instance->arrayStart(size); CBOR_CALLBACK_END }; @@ -105,6 +172,8 @@ callbacks.byte_string_start = [](void* context) { CBOR_CALLBACK_START + instance->mode.push_back(Mode::BYTE_STRING); + instance->remainingItems.push_back(INDEFINITE); // TODO: implement xmlpp::Element* element = instance->current->add_child("byte-string-start"); CBOR_CALLBACK_END @@ -130,30 +199,25 @@ callbacks.indef_array_start = [](void* context) { CBOR_CALLBACK_START - // TODO: implement - xmlpp::Element* element = instance->current->add_child("array-start"); + instance->arrayStart(INDEFINITE); CBOR_CALLBACK_END }; callbacks.indef_map_start = [](void* context) { CBOR_CALLBACK_START - // TODO: implement - xmlpp::Element* element = instance->current->add_child("map-start"); + instance->mapStart(INDEFINITE); CBOR_CALLBACK_END }; callbacks.indef_break = [](void* context) { CBOR_CALLBACK_START - // TODO: implement - xmlpp::Element* element = instance->current->add_child("indef-break"); + instance->containerEnd(); CBOR_CALLBACK_END }; callbacks.map_start = [](void* context, size_t size) { CBOR_CALLBACK_START - // TODO: implement - xmlpp::Element* element = instance->current->add_child("map-start"); - element->set_attribute("size", std::to_string(size)); + instance->mapStart(size); CBOR_CALLBACK_END }; @@ -195,6 +259,8 @@ callbacks.string_start = [](void* context) { CBOR_CALLBACK_START + instance->mode.push_back(Mode::CHAR_STRING); + instance->remainingItems.push_back(INDEFINITE); // TODO: implement xmlpp::Element* element = instance->current->add_child("string-start"); CBOR_CALLBACK_END @@ -250,7 +316,9 @@ void process() { current = parser->get_document()->create_root_node(rootName); + mode.push_back(Mode::ROOT); + // TODO: better streaming/buffering? (however, we still have to hold whole document in memory and „infinite“ stream processing is impossible) std::stringstream bufferStream; for (char ch = input->get(); input->good(); ch = input->get()) bufferStream.put(ch); std::string buffer = bufferStream.str(); @@ -262,6 +330,8 @@ if (result.status != cbor_decoder_status::CBOR_DECODER_FINISHED) throw relpipe::writer::RelpipeWriterException(L"CBOR parsing failed: status = " + std::to_wstring(result.status)); } + checkRemainingItems(); + parser->get_document()->get_root_node()->set_attribute("bytes-read", std::to_string(bytesRead)); } };