os/buffered_xml.h: support for generating XML

This patch extends the 'Buffered_xml' utility with a new constructor
that fills the buffer with the output of an 'Xml_generator'. It thereby
presents an easy way to generate XML to be consumed locally.

The patch also add a deprecation mark to the original 'xml' accessor
because copying 'Xml_node' objects (here as return value) is dangerous.
The new 'with_xml_node' method should instead be used to access the XML
content stored in the buffer.

Fixes #3602
This commit is contained in:
Norman Feske 2020-01-10 09:28:23 +01:00 committed by Christian Helmuth
parent 640a001ab6
commit f82e7df0ba
1 changed files with 73 additions and 11 deletions

View File

@ -15,7 +15,9 @@
#define _OS__BUFFERED_XML_H_ #define _OS__BUFFERED_XML_H_
/* Genode includes */ /* Genode includes */
#include <util/retry.h>
#include <util/xml_node.h> #include <util/xml_node.h>
#include <util/xml_generator.h>
#include <base/allocator.h> #include <base/allocator.h>
namespace Genode { class Buffered_xml; } namespace Genode { class Buffered_xml; }
@ -25,23 +27,51 @@ class Genode::Buffered_xml
{ {
private: private:
Allocator &_alloc; Allocator &_alloc;
char const * const _ptr; /* pointer to dynamically allocated buffer */
Xml_node const _xml; /* referring to buffer of '_ptr' */ struct Allocation { char *ptr; size_t size; } const _allocation;
Xml_node const _xml { _allocation.ptr, _allocation.size };
/** /**
* \throw Allocator::Out_of_memory * \throw Allocator::Out_of_memory
*/ */
static char const *_init_ptr(Allocator &alloc, Xml_node node) Allocation _copy_xml_node(Xml_node node)
{ {
char *ptr = nullptr; Allocation allocation { };
node.with_raw_node([&] (char const *start, size_t length) { node.with_raw_node([&] (char const *start, size_t length) {
ptr = (char *)alloc.alloc(length); allocation = Allocation { (char *)_alloc.alloc(length), length };
Genode::memcpy(ptr, start, length); Genode::memcpy(allocation.ptr, start, length);
}); });
return ptr; return allocation;
}
/**
* Generate XML into allocated buffer
*
* \throw Allocation::Out_of_memory
*/
template <typename FN>
Allocation _generate(char const *node_name, FN const &fn, size_t size)
{
Allocation allocation { };
retry<Xml_generator::Buffer_exceeded>(
[&] () {
allocation = Allocation { (char *)_alloc.alloc(size), size };
Xml_generator xml(allocation.ptr, size, node_name,
[&] () { fn(xml); }); },
[&] () {
_alloc.free(allocation.ptr, allocation.size);
allocation = { };
size = size*2;
}
);
return allocation;
} }
/* /*
@ -53,18 +83,50 @@ class Genode::Buffered_xml
public: public:
/** /**
* Constructor * Constructor for buffering a copy of the specified XML node
* *
* \throw Allocator::Out_of_memory * \throw Allocator::Out_of_memory
*/ */
Buffered_xml(Allocator &alloc, Xml_node node) Buffered_xml(Allocator &alloc, Xml_node node)
: :
_alloc(alloc), _ptr(_init_ptr(alloc, node)), _xml(_ptr, node.size()) _alloc(alloc), _allocation(_copy_xml_node(node))
{ } { }
~Buffered_xml() { _alloc.free(const_cast<char *>(_ptr), _xml.size()); } struct Min_size { size_t value; };
/**
* Constructor for buffering generated XML
*
* \param name name of top-level XML node
* \param fn functor that takes an 'Xml_generator &' as argument
* \param size initial allocation size
*
* \throw Allocator::Out_of_memory
*/
template <typename FN>
Buffered_xml(Allocator &alloc, char const *name, FN const &fn, Min_size size)
:
_alloc(alloc), _allocation(_generate(name, fn, size.value))
{ }
template <typename FN>
Buffered_xml(Allocator &alloc, char const *name, FN const &fn)
:
Buffered_xml(alloc, name, fn, Min_size{4000})
{ }
~Buffered_xml() { _alloc.free(_allocation.ptr, _allocation.size); }
/*
* \deprecated Use 'with_xml_node' instead
*/
Xml_node xml() const { return _xml; } Xml_node xml() const { return _xml; }
/**
* Call functor 'fn' with 'Xml_node const &' as argument
*/
template <typename FN>
void with_xml_node(FN const &fn) const { fn(_xml); }
}; };
#endif /* _OS__BUFFERED_XML_H_ */ #endif /* _OS__BUFFERED_XML_H_ */