mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-19 03:06:39 +00:00
util/xml_node.h: avoid internal use of exceptions
The XML parser used to rely in C++ exceptions while parsing, which is an artifact from the initial implementation. This patch reworks the code such that exceptions are avoided in the common cases. Fixes #3605
This commit is contained in:
parent
c5c5f8754c
commit
640a001ab6
@ -14,6 +14,7 @@
|
||||
#ifndef _INCLUDE__UTIL__XML_NODE_H_
|
||||
#define _INCLUDE__UTIL__XML_NODE_H_
|
||||
|
||||
#include <base/log.h>
|
||||
#include <util/token.h>
|
||||
#include <base/exception.h>
|
||||
|
||||
@ -45,8 +46,22 @@ class Genode::Xml_attribute
|
||||
*/
|
||||
typedef ::Genode::Token<Scanner_policy_xml_identifier> Token;
|
||||
|
||||
Token _name;
|
||||
Token _value;
|
||||
struct Tokens
|
||||
{
|
||||
Token name;
|
||||
Token value;
|
||||
|
||||
Tokens(Token t)
|
||||
: name(t.eat_whitespace()), value(name.next().next()) { };
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
bool const tag_present = (name.type() == Token::IDENT);
|
||||
bool const value_present = (name.next()[0] == '=' &&
|
||||
value.type() == Token::STRING);
|
||||
return tag_present && value_present;
|
||||
}
|
||||
} _tokens;
|
||||
|
||||
friend class Xml_node;
|
||||
|
||||
@ -58,6 +73,11 @@ class Genode::Xml_attribute
|
||||
*/
|
||||
friend class Tag;
|
||||
|
||||
/**
|
||||
* Return true if token refers to a valid attribute
|
||||
*/
|
||||
static bool _valid(Token t) { return Tokens(t).valid(); }
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -65,20 +85,19 @@ class Genode::Xml_attribute
|
||||
* construct an 'Xml_attribute' from a token sequence via an
|
||||
* assignment from the leading 'Token'.
|
||||
*/
|
||||
Xml_attribute(Token t) :
|
||||
_name(t.eat_whitespace()), _value(_name.next().next())
|
||||
explicit Xml_attribute(Token t) : _tokens(t)
|
||||
{
|
||||
if (_name.type() != Token::IDENT)
|
||||
if (_tokens.name.type() != Token::IDENT)
|
||||
throw Nonexistent_attribute();
|
||||
|
||||
if (_name.next()[0] != '=' || _value.type() != Token::STRING)
|
||||
if (!_tokens.valid())
|
||||
throw Invalid_syntax();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return token following the attribute declaration
|
||||
*/
|
||||
Token _next() const { return _name.next().next().next(); }
|
||||
Token _next_token() const { return _tokens.name.next().next().next(); }
|
||||
|
||||
public:
|
||||
|
||||
@ -102,20 +121,20 @@ class Genode::Xml_attribute
|
||||
* Limit number of characters by token length, take
|
||||
* null-termination into account.
|
||||
*/
|
||||
max_len = min(max_len, _name.len() + 1);
|
||||
strncpy(dst, _name.start(), max_len);
|
||||
max_len = min(max_len, _tokens.name.len() + 1);
|
||||
strncpy(dst, _tokens.name.start(), max_len);
|
||||
}
|
||||
|
||||
typedef String<64> Name;
|
||||
Name name() const {
|
||||
return Name(Cstring(_name.start(), _name.len())); }
|
||||
return Name(Cstring(_tokens.name.start(), _tokens.name.len())); }
|
||||
|
||||
/**
|
||||
* Return true if attribute has specified type
|
||||
*/
|
||||
bool has_type(char const *type) {
|
||||
return strlen(type) == _name.len() &&
|
||||
strcmp(type, _name.start(), _name.len()) == 0; }
|
||||
return strlen(type) == _tokens.name.len() &&
|
||||
strcmp(type, _tokens.name.start(), _tokens.name.len()) == 0; }
|
||||
|
||||
/**
|
||||
* Return size of value
|
||||
@ -123,7 +142,7 @@ class Genode::Xml_attribute
|
||||
* \deprecated use 'with_raw_node' instead
|
||||
* \noapi
|
||||
*/
|
||||
char const *value_base() const { return _value.start() + 1; }
|
||||
char const *value_base() const { return _tokens.value.start() + 1; }
|
||||
|
||||
/**
|
||||
* Return size of the value in bytes
|
||||
@ -137,15 +156,15 @@ class Genode::Xml_attribute
|
||||
* The invariant 'len >= 2' is enforced by the 'Xml_attribute'
|
||||
* constructor by checking the '_value' type for being a 'STRING'.
|
||||
*/
|
||||
return _value.len() - 2;
|
||||
return _tokens.value.len() - 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if attribute has the specified value
|
||||
*/
|
||||
bool has_value(char const *value) const {
|
||||
return strlen(value) == (_value.len() - 2)
|
||||
&& !strcmp(value, _value.start() + 1, _value.len() - 2); }
|
||||
return strlen(value) == (_tokens.value.len() - 2)
|
||||
&& !strcmp(value, _tokens.value.start() + 1, _tokens.value.len() - 2); }
|
||||
|
||||
/**
|
||||
* Call functor 'fn' with the data of the attribute value as argument
|
||||
@ -162,7 +181,7 @@ class Genode::Xml_attribute
|
||||
/*
|
||||
* Skip leading quote of the '_value' to access the actual value.
|
||||
*/
|
||||
fn(_value.start() + 1, value_size());
|
||||
fn(_tokens.value.start() + 1, value_size());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -239,7 +258,7 @@ class Genode::Xml_attribute
|
||||
/**
|
||||
* Return next attribute in attribute list
|
||||
*/
|
||||
Xml_attribute next() const { return Xml_attribute(_next()); }
|
||||
Xml_attribute next() const { return Xml_attribute(_next_token()); }
|
||||
};
|
||||
|
||||
|
||||
@ -325,10 +344,8 @@ class Genode::Xml_node
|
||||
/* skip attributes to find tag delimiter */
|
||||
Token delimiter = _name.next();
|
||||
if (supposed_type != END)
|
||||
try {
|
||||
for (Xml_attribute a = _name.next(); ; a = a._next())
|
||||
delimiter = a._next();
|
||||
} catch (Nonexistent_attribute) { }
|
||||
while (Xml_attribute::_valid(delimiter))
|
||||
delimiter = Xml_attribute(delimiter)._next_token();
|
||||
|
||||
delimiter = delimiter.eat_whitespace();
|
||||
|
||||
@ -395,6 +412,11 @@ class Genode::Xml_node
|
||||
return t.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if tag as at least one attribute
|
||||
*/
|
||||
bool has_attribute() const { return Xml_attribute::_valid(_name.next()); }
|
||||
|
||||
/**
|
||||
* Return first attribute of tag
|
||||
*/
|
||||
@ -505,12 +527,8 @@ class Genode::Xml_node
|
||||
}
|
||||
};
|
||||
|
||||
int _num_sub_nodes { 0 }; /* number of immediate sub nodes */
|
||||
|
||||
char const * _addr; /* first character of XML data */
|
||||
size_t _max_len; /* length of XML data in characters */
|
||||
Tag _start_tag;
|
||||
Tag _end_tag;
|
||||
|
||||
/**
|
||||
* Search matching end tag for given start tag and detemine number of
|
||||
@ -611,15 +629,49 @@ class Genode::Xml_node
|
||||
return t;
|
||||
}
|
||||
|
||||
struct Tags
|
||||
{
|
||||
int num_sub_nodes = 0;
|
||||
Tag start;
|
||||
Tag end;
|
||||
|
||||
Tags(char const *addr, size_t max_len)
|
||||
:
|
||||
start(skip_non_tag_characters(Token(addr, max_len))),
|
||||
end(_search_end_tag(start, num_sub_nodes))
|
||||
{ }
|
||||
} _tags;
|
||||
|
||||
/**
|
||||
* Return true if specified buffer contains a valid XML node
|
||||
*/
|
||||
static bool _valid(Tags const &tags)
|
||||
{
|
||||
if (tags.start.type() == Tag::EMPTY)
|
||||
return true;
|
||||
|
||||
if (tags.start.type() == Tag::START && tags.end.type() == Tag::END)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _valid_node_at(char const *at) const
|
||||
{
|
||||
bool const in_range = (at >= _addr && (size_t)(at - _addr) < _max_len);
|
||||
|
||||
return in_range && _valid(Tags(at, _max_len - (at - _addr)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create sub node from XML node
|
||||
*
|
||||
* \throw Nonexistent_sub_node
|
||||
* \throw Invalid_syntax
|
||||
*/
|
||||
Xml_node _sub_node(char const *at) const
|
||||
Xml_node _node_at(char const *at) const
|
||||
{
|
||||
if (at < _addr || (size_t)(at - _addr) >= _max_len)
|
||||
if (!_valid_node_at(at))
|
||||
throw Nonexistent_sub_node();
|
||||
|
||||
return Xml_node(at, _max_len - (at - _addr));
|
||||
@ -628,7 +680,7 @@ class Genode::Xml_node
|
||||
/**
|
||||
* Return pointer to start of content
|
||||
*/
|
||||
char const *_content_base() const { return _start_tag.next_token().start(); }
|
||||
char const *_content_base() const { return _tags.start.next_token().start(); }
|
||||
|
||||
public:
|
||||
|
||||
@ -642,15 +694,9 @@ class Genode::Xml_node
|
||||
*/
|
||||
Xml_node(char const *addr, size_t max_len = ~0UL)
|
||||
:
|
||||
_addr(addr),
|
||||
_max_len(max_len),
|
||||
_start_tag(skip_non_tag_characters(Token(addr, max_len))),
|
||||
_end_tag(_search_end_tag(_start_tag, _num_sub_nodes))
|
||||
_addr(addr), _max_len(max_len), _tags(addr, max_len)
|
||||
{
|
||||
/* check validity of XML node */
|
||||
if (_start_tag.type() == Tag::EMPTY) return;
|
||||
if (_start_tag.type() == Tag::START && _end_tag.type() == Tag::END) return;
|
||||
|
||||
if (!_valid(_tags))
|
||||
throw Invalid_syntax();
|
||||
}
|
||||
|
||||
@ -660,12 +706,12 @@ class Genode::Xml_node
|
||||
* \noapi
|
||||
*/
|
||||
void type_name(char *dst, size_t max_len) const {
|
||||
_start_tag.name().string(dst, max_len); }
|
||||
_tags.start.name().string(dst, max_len); }
|
||||
|
||||
/**
|
||||
* Return size of node including start and end tags in bytes
|
||||
*/
|
||||
size_t size() const { return _end_tag.next_token().start() - _addr; }
|
||||
size_t size() const { return _tags.end.next_token().start() - _addr; }
|
||||
|
||||
/**
|
||||
* Return pointer to start of node
|
||||
@ -680,10 +726,10 @@ class Genode::Xml_node
|
||||
*/
|
||||
size_t content_size() const
|
||||
{
|
||||
if (_start_tag.type() == Tag::EMPTY)
|
||||
if (_tags.start.type() == Tag::EMPTY)
|
||||
return 0;
|
||||
|
||||
return _end_tag.token().start() - _content_base();
|
||||
return _tags.end.token().start() - _content_base();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -692,7 +738,7 @@ class Genode::Xml_node
|
||||
typedef String<64> Type;
|
||||
Type type() const
|
||||
{
|
||||
Token name = _start_tag.name();
|
||||
Token name = _tags.start.name();
|
||||
return Type(Cstring(name.start(), name.len()));
|
||||
}
|
||||
|
||||
@ -700,9 +746,9 @@ class Genode::Xml_node
|
||||
* Return true if tag is of specified type
|
||||
*/
|
||||
bool has_type(char const *type) const {
|
||||
return (!strcmp(type, _start_tag.name().start(),
|
||||
_start_tag.name().len())
|
||||
&& strlen(type) == _start_tag.name().len()); }
|
||||
return (!strcmp(type, _tags.start.name().start(),
|
||||
_tags.start.name().len())
|
||||
&& strlen(type) == _tags.start.name().len()); }
|
||||
|
||||
/**
|
||||
* Call functor 'fn' with the node data '(char const *, size_t)'
|
||||
@ -710,7 +756,7 @@ class Genode::Xml_node
|
||||
template <typename FN>
|
||||
void with_raw_node(FN const &fn) const
|
||||
{
|
||||
fn(_addr, _end_tag.next_token().start() - _addr);
|
||||
fn(_addr, _tags.end.next_token().start() - _addr);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -724,7 +770,7 @@ class Genode::Xml_node
|
||||
template <typename FN>
|
||||
void with_raw_content(FN const &fn) const
|
||||
{
|
||||
if (_start_tag.type() == Tag::EMPTY)
|
||||
if (_tags.start.type() == Tag::EMPTY)
|
||||
return;
|
||||
|
||||
fn(_content_base(), content_size());
|
||||
@ -737,7 +783,7 @@ class Genode::Xml_node
|
||||
*
|
||||
* \noapi
|
||||
*/
|
||||
char *content_addr() const { return _start_tag.next_token().start(); }
|
||||
char *content_addr() const { return _tags.start.next_token().start(); }
|
||||
|
||||
/**
|
||||
* Return pointer to start of content
|
||||
@ -792,7 +838,7 @@ class Genode::Xml_node
|
||||
/**
|
||||
* Return the number of the XML node's immediate sub nodes
|
||||
*/
|
||||
size_t num_sub_nodes() const { return _num_sub_nodes; }
|
||||
size_t num_sub_nodes() const { return _tags.num_sub_nodes; }
|
||||
|
||||
/**
|
||||
* Return XML node following the current one
|
||||
@ -801,9 +847,11 @@ class Genode::Xml_node
|
||||
*/
|
||||
Xml_node next() const
|
||||
{
|
||||
Token after_node = _end_tag.next_token();
|
||||
Token after_node = _tags.end.next_token();
|
||||
after_node = skip_non_tag_characters(after_node);
|
||||
try { return _sub_node(after_node.start()); }
|
||||
try {
|
||||
return _node_at(after_node.start());
|
||||
}
|
||||
catch (Invalid_syntax) { throw Nonexistent_sub_node(); }
|
||||
}
|
||||
|
||||
@ -824,10 +872,22 @@ class Genode::Xml_node
|
||||
/**
|
||||
* Return true if node is the last of a node sequence
|
||||
*/
|
||||
bool last(char const *type = 0) const
|
||||
bool last(char const *type = nullptr) const
|
||||
{
|
||||
try { next(type); return false; }
|
||||
catch (Nonexistent_sub_node) { return true; }
|
||||
Token after = _tags.end.next_token();
|
||||
after = skip_non_tag_characters(after);
|
||||
|
||||
for (;;) {
|
||||
if (!_valid_node_at(after.start()))
|
||||
return true;
|
||||
|
||||
Xml_node node = _node_at(after.start());
|
||||
if (!type || node.has_type(type))
|
||||
return false;
|
||||
|
||||
after = node._tags.end.next_token();
|
||||
after = skip_non_tag_characters(after);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -839,17 +899,14 @@ class Genode::Xml_node
|
||||
*/
|
||||
Xml_node sub_node(unsigned idx = 0U) const
|
||||
{
|
||||
if (_num_sub_nodes > 0) {
|
||||
|
||||
/* look up node at specified index */
|
||||
if (_tags.num_sub_nodes > 0) {
|
||||
try {
|
||||
Xml_node curr_node = _sub_node(_content_base());
|
||||
Xml_node curr_node = _node_at(_content_base());
|
||||
for (; idx > 0; idx--)
|
||||
curr_node = curr_node.next();
|
||||
return curr_node;
|
||||
} catch (Invalid_syntax) { }
|
||||
}
|
||||
|
||||
throw Nonexistent_sub_node();
|
||||
}
|
||||
|
||||
@ -860,17 +917,16 @@ class Genode::Xml_node
|
||||
*/
|
||||
Xml_node sub_node(char const *type) const
|
||||
{
|
||||
if (_num_sub_nodes > 0) {
|
||||
if (_tags.num_sub_nodes > 0) {
|
||||
|
||||
/* search for sub node of specified type */
|
||||
try {
|
||||
Xml_node curr_node = _sub_node(_content_base());
|
||||
Xml_node curr_node = _node_at(_content_base());
|
||||
for ( ; true; curr_node = curr_node.next())
|
||||
if (curr_node.has_type(type))
|
||||
if (!type || curr_node.has_type(type))
|
||||
return curr_node;
|
||||
} catch (...) { }
|
||||
}
|
||||
|
||||
throw Nonexistent_sub_node();
|
||||
}
|
||||
|
||||
@ -893,20 +949,16 @@ class Genode::Xml_node
|
||||
template <typename FN>
|
||||
void for_each_sub_node(char const *type, FN const &fn) const
|
||||
{
|
||||
if (_num_sub_nodes == 0)
|
||||
if (!has_sub_node(type))
|
||||
return;
|
||||
|
||||
try {
|
||||
Xml_node node = sub_node();
|
||||
for (int i = 0; ; node = node.next()) {
|
||||
|
||||
for (Xml_node node = sub_node(type); ; node = node.next()) {
|
||||
if (!type || node.has_type(type))
|
||||
fn(node);
|
||||
|
||||
if (++i == _num_sub_nodes)
|
||||
if (node.last())
|
||||
break;
|
||||
}
|
||||
} catch (Nonexistent_sub_node) { }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -928,14 +980,11 @@ class Genode::Xml_node
|
||||
*/
|
||||
Xml_attribute attribute(unsigned idx) const
|
||||
{
|
||||
/* get first attribute of the node */
|
||||
Xml_attribute a = _start_tag.attribute();
|
||||
Xml_attribute attr = _tags.start.attribute();
|
||||
for (unsigned i = 0; i < idx; i++)
|
||||
attr = Xml_attribute(attr._next_token());
|
||||
|
||||
/* skip attributes until we reach the target index */
|
||||
for (; idx > 0; idx--)
|
||||
a = a._next();
|
||||
|
||||
return a;
|
||||
return attr;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -947,10 +996,13 @@ class Genode::Xml_node
|
||||
*/
|
||||
Xml_attribute attribute(char const *type) const
|
||||
{
|
||||
/* iterate, beginning with the first attribute of the node */
|
||||
for (Xml_attribute a = _start_tag.attribute(); ; a = a.next())
|
||||
if (a.has_type(type))
|
||||
return a;
|
||||
for (Xml_attribute attr = _tags.start.attribute(); ;) {
|
||||
if (attr.has_type(type))
|
||||
return attr;
|
||||
|
||||
attr = Xml_attribute(attr._next_token());
|
||||
}
|
||||
throw Nonexistent_attribute();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -968,7 +1020,27 @@ class Genode::Xml_node
|
||||
inline T attribute_value(char const *type, T const default_value) const
|
||||
{
|
||||
T result = default_value;
|
||||
try { attribute(type).value(result); } catch (...) { }
|
||||
|
||||
if (!_tags.start.has_attribute())
|
||||
return result;
|
||||
|
||||
for (Xml_attribute attr = _tags.start.attribute(); ; ) {
|
||||
|
||||
/* match */
|
||||
if (attr.has_type(type)) {
|
||||
attr.value(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* end of attribute */
|
||||
Token const next = attr._next_token();
|
||||
if (!Xml_attribute::_valid(next))
|
||||
break;
|
||||
|
||||
/* keep searching */
|
||||
attr = Xml_attribute(next);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -977,16 +1049,46 @@ class Genode::Xml_node
|
||||
*/
|
||||
inline bool has_attribute(char const *type) const
|
||||
{
|
||||
try { attribute(type); return true; } catch (...) { }
|
||||
if (!_tags.start.has_attribute())
|
||||
return false;
|
||||
|
||||
if (type == nullptr)
|
||||
return true;
|
||||
|
||||
for (Xml_attribute attr = _tags.start.attribute(); ; ) {
|
||||
if (attr.has_type(type))
|
||||
return true;
|
||||
|
||||
Token const next = attr._next_token();
|
||||
if (!Xml_attribute::_valid(next))
|
||||
return false;
|
||||
|
||||
attr = Xml_attribute(next);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if sub node of specified type exists
|
||||
*/
|
||||
inline bool has_sub_node(char const *type) const
|
||||
bool has_sub_node(char const *type) const
|
||||
{
|
||||
try { sub_node(type); return true; } catch (...) { }
|
||||
if (_tags.num_sub_nodes == 0)
|
||||
return false;
|
||||
|
||||
if (!_valid_node_at(_content_base()))
|
||||
return false;
|
||||
|
||||
if (type == nullptr)
|
||||
return true;
|
||||
|
||||
/* search for node of given type */
|
||||
for (Xml_node node = _node_at(_content_base()); ; node = node.next()) {
|
||||
if (node.has_type(type))
|
||||
return true;
|
||||
|
||||
if (node.last())
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user