From 456527b44a8a2f23c819a67b1df6efd520cf25ae Mon Sep 17 00:00:00 2001 From: "arseny.kapoulkine@gmail.com" Date: Fri, 7 Dec 2012 04:49:23 +0000 Subject: Implement xml_node::append_buffer as a faster alternative to assembling documents from fragments (compared to parse & clone) git-svn-id: http://pugixml.googlecode.com/svn/trunk@936 99668b35-9821-0410-8761-19e4c4f06640 --- src/pugixml.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/pugixml.hpp | 11 ++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 163ac48..b8d3275 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -503,13 +503,21 @@ namespace pugi } PUGI__NS_BEGIN + struct xml_extra_buffer + { + char_t* buffer; + xml_extra_buffer* next; + }; + struct xml_document_struct: public xml_node_struct, public xml_allocator { - xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0) + xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0) { } const char_t* buffer; + + xml_extra_buffer* extra_buffers; }; inline xml_allocator& get_allocator(const xml_node_struct* node) @@ -4529,6 +4537,39 @@ namespace pugi return true; } + PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + // append_buffer is only valid for elements/documents + if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root); + + // get document node + impl::xml_document_struct* doc = static_cast(root()._root); + assert(doc); + + // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later) + impl::xml_memory_page* page = 0; + impl::xml_extra_buffer* extra = static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer), page)); + (void)page; + + if (!extra) return impl::make_parse_result(status_out_of_memory); + + // save name; name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level + char_t* name = _root->name; + _root->name = 0; + + // parse + xml_parse_result res = load_buffer_impl(doc, _root, const_cast(contents), size, options, encoding, false, false, &extra->buffer); + + // restore name + _root->name = name; + + // add extra buffer to the list + extra->next = doc->extra_buffers; + doc->extra_buffers = extra; + + return res; + } + PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const { if (!_root) return xml_node(); @@ -5125,6 +5166,8 @@ namespace pugi case status_bad_end_element: return "Error parsing end element tag"; case status_end_element_mismatch: return "Start-end tags mismatch"; + case status_append_invalid_root: return "Unable to append nodes: root is not an element or document"; + default: return "Unknown error"; } } @@ -5183,6 +5226,12 @@ namespace pugi _buffer = 0; } + // destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator) + for (impl::xml_extra_buffer* extra = static_cast(_root)->extra_buffers; extra; extra = extra->next) + { + if (extra->buffer) impl::xml_memory::deallocate(extra->buffer); + } + // destroy dynamic storage, leave sentinel page (it's in static memory) if (_root) { diff --git a/src/pugixml.hpp b/src/pugixml.hpp index 4ca5197..a736f53 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -201,6 +201,8 @@ namespace pugi class xml_tree_walker; + struct xml_parse_result; + class xml_node; class xml_text; @@ -473,6 +475,11 @@ namespace pugi bool remove_child(const xml_node& n); bool remove_child(const char_t* name); + // Parses buffer as an XML document fragment and appends all nodes as children of the current node. + // Copies/converts the buffer, so it may be deleted or changed after the function returns. + // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. + xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + // Find attribute using predicate. Returns first attribute for which predicate returned true. template xml_attribute find_attribute(Predicate pred) const { @@ -821,7 +828,9 @@ namespace pugi status_bad_start_element, // Parsing error occurred while parsing start element tag status_bad_attribute, // Parsing error occurred while parsing element attribute status_bad_end_element, // Parsing error occurred while parsing end element tag - status_end_element_mismatch // There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag) + status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag) + + status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer) }; // Parsing result -- cgit v1.2.3