Skip to content

Commit

Permalink
Add remaining std::string_view overloads (#636)
Browse files Browse the repository at this point in the history
Add remaining overloads and supporting unit tests. This concludes the initial phase of std::string_view support; for now the support is still opt-in via PUGIXML_STRING_VIEW define, but that will become unnecessary (enabled-by-default) in a future version.
  • Loading branch information
dantargz authored Oct 24, 2024
1 parent 7e70274 commit 13beda2
Show file tree
Hide file tree
Showing 5 changed files with 578 additions and 10 deletions.
257 changes: 257 additions & 0 deletions src/pugixml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,24 @@ PUGI_IMPL_NS_BEGIN
#endif
}

#ifdef PUGIXML_HAS_STRING_VIEW
// Check if the null-terminated dst string is equal to the entire contents of srcview
PUGI_IMPL_FN bool stringview_equal(string_view_t srcview, const char_t* dst)
{
// std::basic_string_view::compare(const char*) has the right behavior, but it performs an
// extra traversal of dst to compute its length.
assert(dst);
const char_t* src = srcview.data();
size_t srclen = srcview.size();

while (srclen && *dst && *src == *dst)
{
--srclen; ++dst; ++src;
}
return srclen == 0 && *dst == 0;
}
#endif

// Compare lhs with [rhs_begin, rhs_end)
PUGI_IMPL_FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count)
{
Expand Down Expand Up @@ -5413,6 +5431,14 @@ namespace pugi
return *this;
}

#ifdef PUGIXML_HAS_STRING_VIEW
PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(string_view_t rhs)
{
set_value(rhs);
return *this;
}
#endif

#ifdef PUGIXML_HAS_LONG_LONG
PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(long long rhs)
{
Expand Down Expand Up @@ -5736,6 +5762,64 @@ namespace pugi
return xml_node();
}

#ifdef PUGIXML_HAS_STRING_VIEW
PUGI_IMPL_FN xml_node xml_node::child(string_view_t name_) const
{
if (!_root) return xml_node();

for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
{
const char_t* iname = i->name;
if (iname && impl::stringview_equal(name_, iname))
return xml_node(i);
}

return xml_node();
}

PUGI_IMPL_FN xml_attribute xml_node::attribute(string_view_t name_) const
{
if (!_root) return xml_attribute();

for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute)
{
const char_t* iname = i->name;
if (iname && impl::stringview_equal(name_, iname))
return xml_attribute(i);
}

return xml_attribute();
}

PUGI_IMPL_FN xml_node xml_node::next_sibling(string_view_t name_) const
{
if (!_root) return xml_node();

for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling)
{
const char_t* iname = i->name;
if (iname && impl::stringview_equal(name_, iname))
return xml_node(i);
}

return xml_node();
}

PUGI_IMPL_FN xml_node xml_node::previous_sibling(string_view_t name_) const
{
if (!_root) return xml_node();

for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c)
{
const char_t* iname = i->name;
if (iname && impl::stringview_equal(name_, iname))
return xml_node(i);
}

return xml_node();
}
#endif

PUGI_IMPL_FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const
{
xml_attribute_struct* hint = hint_._attr;
Expand Down Expand Up @@ -5775,6 +5859,47 @@ namespace pugi
return xml_attribute();
}

#ifdef PUGIXML_HAS_STRING_VIEW
PUGI_IMPL_FN xml_attribute xml_node::attribute(string_view_t name_, xml_attribute& hint_) const
{
xml_attribute_struct* hint = hint_._attr;

// if hint is not an attribute of node, behavior is not defined
assert(!hint || (_root && impl::is_attribute_of(hint, _root)));

if (!_root) return xml_attribute();

// optimistically search from hint up until the end
for (xml_attribute_struct* i = hint; i; i = i->next_attribute)
{
const char_t* iname = i->name;
if (iname && impl::stringview_equal(name_, iname))
{
// update hint to maximize efficiency of searching for consecutive attributes
hint_._attr = i->next_attribute;

return xml_attribute(i);
}
}

// wrap around and search from the first attribute until the hint
// 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails
for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute)
{
const char_t* jname = j->name;
if (jname && impl::stringview_equal(name_, jname))
{
// update hint to maximize efficiency of searching for consecutive attributes
hint_._attr = j->next_attribute;

return xml_attribute(j);
}
}

return xml_attribute();
}
#endif

PUGI_IMPL_FN xml_node xml_node::previous_sibling() const
{
if (!_root) return xml_node();
Expand Down Expand Up @@ -5980,6 +6105,78 @@ namespace pugi
return a;
}

#ifdef PUGIXML_HAS_STRING_VIEW
PUGI_IMPL_FN xml_attribute xml_node::append_attribute(string_view_t name_)
{
if (!impl::allow_insert_attribute(type())) return xml_attribute();

impl::xml_allocator& alloc = impl::get_allocator(_root);
if (!alloc.reserve()) return xml_attribute();

xml_attribute a(impl::allocate_attribute(alloc));
if (!a) return xml_attribute();

impl::append_attribute(a._attr, _root);

a.set_name(name_);

return a;
}

PUGI_IMPL_FN xml_attribute xml_node::prepend_attribute(string_view_t name_)
{
if (!impl::allow_insert_attribute(type())) return xml_attribute();

impl::xml_allocator& alloc = impl::get_allocator(_root);
if (!alloc.reserve()) return xml_attribute();

xml_attribute a(impl::allocate_attribute(alloc));
if (!a) return xml_attribute();

impl::prepend_attribute(a._attr, _root);

a.set_name(name_);

return a;
}

PUGI_IMPL_FN xml_attribute xml_node::insert_attribute_after(string_view_t name_, const xml_attribute& attr)
{
if (!impl::allow_insert_attribute(type())) return xml_attribute();
if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();

impl::xml_allocator& alloc = impl::get_allocator(_root);
if (!alloc.reserve()) return xml_attribute();

xml_attribute a(impl::allocate_attribute(alloc));
if (!a) return xml_attribute();

impl::insert_attribute_after(a._attr, attr._attr, _root);

a.set_name(name_);

return a;
}

PUGI_IMPL_FN xml_attribute xml_node::insert_attribute_before(string_view_t name_, const xml_attribute& attr)
{
if (!impl::allow_insert_attribute(type())) return xml_attribute();
if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();

impl::xml_allocator& alloc = impl::get_allocator(_root);
if (!alloc.reserve()) return xml_attribute();

xml_attribute a(impl::allocate_attribute(alloc));
if (!a) return xml_attribute();

impl::insert_attribute_before(a._attr, attr._attr, _root);

a.set_name(name_);

return a;
}
#endif

PUGI_IMPL_FN xml_attribute xml_node::append_copy(const xml_attribute& proto)
{
if (!proto) return xml_attribute();
Expand Down Expand Up @@ -6156,6 +6353,44 @@ namespace pugi
return result;
}

#ifdef PUGIXML_HAS_STRING_VIEW
PUGI_IMPL_FN xml_node xml_node::append_child(string_view_t name_)
{
xml_node result = append_child(node_element);

result.set_name(name_);

return result;
}

PUGI_IMPL_FN xml_node xml_node::prepend_child(string_view_t name_)
{
xml_node result = prepend_child(node_element);

result.set_name(name_);

return result;
}

PUGI_IMPL_FN xml_node xml_node::insert_child_after(string_view_t name_, const xml_node& node)
{
xml_node result = insert_child_after(node_element, node);

result.set_name(name_);

return result;
}

PUGI_IMPL_FN xml_node xml_node::insert_child_before(string_view_t name_, const xml_node& node)
{
xml_node result = insert_child_before(node_element, node);

result.set_name(name_);

return result;
}
#endif

PUGI_IMPL_FN xml_node xml_node::append_copy(const xml_node& proto)
{
xml_node_type type_ = proto.type();
Expand Down Expand Up @@ -6299,6 +6534,13 @@ namespace pugi
return remove_attribute(attribute(name_));
}

#ifdef PUGIXML_HAS_STRING_VIEW
PUGI_IMPL_FN bool xml_node::remove_attribute(string_view_t name_)
{
return remove_attribute(attribute(name_));
}
#endif

PUGI_IMPL_FN bool xml_node::remove_attribute(const xml_attribute& a)
{
if (!_root || !a._attr) return false;
Expand Down Expand Up @@ -6339,6 +6581,13 @@ namespace pugi
return remove_child(child(name_));
}

#ifdef PUGIXML_HAS_STRING_VIEW
PUGI_IMPL_FN bool xml_node::remove_child(string_view_t name_)
{
return remove_child(child(name_));
}
#endif

PUGI_IMPL_FN bool xml_node::remove_child(const xml_node& n)
{
if (!_root || !n._root || n._root->parent != _root) return false;
Expand Down Expand Up @@ -6935,6 +7184,14 @@ namespace pugi
return *this;
}

#ifdef PUGIXML_HAS_STRING_VIEW
PUGI_IMPL_FN xml_text& xml_text::operator=(string_view_t rhs)
{
set(rhs);
return *this;
}
#endif

#ifdef PUGIXML_HAS_LONG_LONG
PUGI_IMPL_FN xml_text& xml_text::operator=(long long rhs)
{
Expand Down
Loading

0 comments on commit 13beda2

Please sign in to comment.