From b6f59fb9becdea2160f6e201ff35402e32bccc76 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Thu, 9 Jan 2014 14:48:43 +0100 Subject: [PATCH] XML generator and test Fixes #1019 --- os/include/util/xml_generator.h | 261 ++++++++++++++++++++++++++++ os/run/xml_generator.run | 51 ++++++ os/src/test/xml_generator/main.cc | 75 ++++++++ os/src/test/xml_generator/target.mk | 3 + tool/autopilot.list | 1 + 5 files changed, 391 insertions(+) create mode 100644 os/include/util/xml_generator.h create mode 100644 os/run/xml_generator.run create mode 100644 os/src/test/xml_generator/main.cc create mode 100644 os/src/test/xml_generator/target.mk diff --git a/os/include/util/xml_generator.h b/os/include/util/xml_generator.h new file mode 100644 index 000000000..23845a130 --- /dev/null +++ b/os/include/util/xml_generator.h @@ -0,0 +1,261 @@ +/* + * \brief Utility for generating XML + * \author Norman Feske + * \date 2014-01-07 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__UTIL__XML_GENERATOR_H_ +#define _INCLUDE__UTIL__XML_GENERATOR_H_ + +#include +#include + +namespace Genode { class Xml_generator; } + + +class Genode::Xml_generator +{ + public: + + /** + * Exception type + */ + class Buffer_exceeded { }; + + private: + + /** + * Buffer descriptor where the XML output goes to + * + * All 'append' functions may throw a 'Buffer_exceeded' exception. + */ + class Out_buffer + { + private: + + char *_dst; + size_t _capacity; + size_t _used = 0; + + void _check_advance(size_t const len) const { + if (_used + len > _capacity) + throw Buffer_exceeded(); } + + public: + + Out_buffer(char *dst, size_t capacity) + : _dst(dst), _capacity(capacity) { } + + void advance(size_t const len) + { + _check_advance(len); + _used += len; + } + + /** + * Append character + */ + void append(char const c) + { + _check_advance(1); + _dst[_used] = c; + advance(1); + } + + /** + * Append character 'n' times + */ + void append(char const c, size_t n) { + for (; n--; append(c)); } + + /** + * Append character buffer + */ + void append(char const *src, size_t len) { + for (; len--; append(*src++)); } + + /** + * Append null-terminated string + */ + void append(char const *src) { append(src, strlen(src)); } + + /** + * Return unused part of the buffer + */ + Out_buffer remainder() const { + return Out_buffer(_dst + _used, _capacity - _used); } + + /** + * Insert gap into already populated part of the buffer + * + * \return empty buffer spanning the gap + */ + Out_buffer insert_gap(unsigned const at, size_t const len) + { + /* don't allow the insertion into non-populated part */ + if (at > _used) + return Out_buffer(_dst + at, 0); + + _check_advance(len); + memmove(_dst + at + len, _dst + at, _used - at); + advance(len); + + return Out_buffer(_dst + at, len); + } + + /** + * Return number of unused bytes of the buffer + */ + size_t used() const { return _used; } + }; + + + class Node + { + private: + + /** + * Indentation level of node + */ + unsigned const _indent_level; + + Node * const _parent_node = 0; + + Out_buffer _out_buffer; + + bool _has_sub_nodes = false; + bool _has_attributes = false; + + /** + * Cursor position of next attribute to insert + */ + unsigned _attr_offset = 0; + + /** + * Called by sub node + */ + Out_buffer _content_buffer() + { + if (!_has_sub_nodes) + _out_buffer.append(">"); + + _out_buffer.append("\n"); + + _has_sub_nodes = true; + + return _out_buffer.remainder(); + } + + /** + * Called by sub node + */ + void _commit_content(Out_buffer content_buffer) + { + _out_buffer.advance(content_buffer.used()); + } + + public: + + void insert_attribute(char const *name, char const *value) + { + /* ' ' + name + '=' + '"' + value + '"' */ + size_t const gap = 1 + strlen(name) + 1 + 1 + strlen(value) + 1; + + Out_buffer dst = _out_buffer.insert_gap(_attr_offset, gap); + dst.append(' '); + dst.append(name); + dst.append("=\""); + dst.append(value); + dst.append("\""); + + _attr_offset += gap; + } + + template + Node(Xml_generator &xml, char const *name, FUNC const &func) + : + _indent_level(xml._curr_indent), + _parent_node(xml._curr_node), + _out_buffer(_parent_node ? _parent_node->_content_buffer() + : xml._out_buffer) + { + _out_buffer.append('\t', _indent_level); + _out_buffer.append("<"); + _out_buffer.append(name); + _attr_offset = _out_buffer.used(); + + xml._curr_node = this; + xml._curr_indent++; + + /* + * Process attributes and sub nodes + */ + func(); + + xml._curr_node = _parent_node; + xml._curr_indent--; + + if (_has_sub_nodes) { + _out_buffer.append("\n"); + _out_buffer.append('\t', _indent_level); + _out_buffer.append(""); + } else + _out_buffer.append("/>"); + + if (_parent_node) + _parent_node->_commit_content(_out_buffer); + else + xml._out_buffer = _out_buffer; + + _out_buffer.append('\0'); + } + }; + + Out_buffer _out_buffer; + Node *_curr_node = 0; + unsigned _curr_indent = 0; + + public: + + template + Xml_generator(char *dst, size_t dst_len, + char const *name, FUNC const &func) + : + _out_buffer(dst, dst_len) + { + if (dst) node(name, func); + } + + template + void node(char const *name, FUNC const &func = [] () { } ) + { + Node(*this, name, func); + } + + void node(char const *name) { Node(*this, name, [] () { }); } + + void attribute(char const *name, char const *str) + { + _curr_node->insert_attribute(name, str); + } + + void attribute(char const *name, long value) + { + char buf[64]; + Genode::snprintf(buf, sizeof(buf), "%ld", value); + _curr_node->insert_attribute(name, buf); + } + + size_t used() const { return _out_buffer.used(); } +}; + +#endif /* _INCLUDE__UTIL__XML_GENERATOR_H_ */ diff --git a/os/run/xml_generator.run b/os/run/xml_generator.run new file mode 100644 index 000000000..44f94eeca --- /dev/null +++ b/os/run/xml_generator.run @@ -0,0 +1,51 @@ +build "core init test/xml_generator" + +create_boot_directory + +install_config { + + + + + + + + + + + + + + + + + + +} + +build_boot_image "core init timer test-xml_generator" + +append qemu_args "-nographic -m 64" + +run_genode_until "--- XML generator test finished ---.*\n" 30 + +grep_output {^\[init -> test-xml_generator} + +compare_output_to { + [init -> test-xml_generator] --- XML generator test started --- + [init -> test-xml_generator] result: + [init -> test-xml_generator] + [init -> test-xml_generator] + [init -> test-xml_generator] + [init -> test-xml_generator] + [init -> test-xml_generator] + [init -> test-xml_generator] + [init -> test-xml_generator] used 199 bytes + [init -> test-xml_generator] buffer exceeded (expected error) + [init -> test-xml_generator] --- XML generator test finished --- +} diff --git a/os/src/test/xml_generator/main.cc b/os/src/test/xml_generator/main.cc new file mode 100644 index 000000000..7ce8aaa9e --- /dev/null +++ b/os/src/test/xml_generator/main.cc @@ -0,0 +1,75 @@ +/* + * \brief Test for XML generator + * \author Norman Feske + * \date 2014-01-07 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +using Genode::size_t; + + +static size_t fill_buffer_with_xml(char *dst, size_t dst_len) +{ + Genode::Xml_generator xml(dst, dst_len, "config", [&] + { + xml.attribute("xpos", "27"); + xml.attribute("ypos", "34"); + + xml.node("box", [&]() + { + xml.attribute("width", "320"); + xml.attribute("height", "240"); + }); + xml.node("label", [&] () + { + xml.attribute("name", "a test"); + xml.node("sub_label"); + xml.node("another_sub_label", [&] () + { + xml.node("sub_sub_label"); + }); + }); + xml.attribute("verbose", "true"); + }); + + return xml.used(); +} + + +int main(int argc, char **argv) +{ + using Genode::printf; + + printf("--- XML generator test started ---\n"); + + static char dst[1000]; + + /* + * Good-case test (to be matched against a known-good pattern in the + * corresponding run script). + */ + size_t used = fill_buffer_with_xml(dst, sizeof(dst)); + printf("result:\n\n%s\n\nused %zd bytes\n", dst, used); + + /* + * Test buffer overflow + */ + try { + fill_buffer_with_xml(dst, 20); } + catch (Genode::Xml_generator::Buffer_exceeded) { + printf("buffer exceeded (expected error)\n"); } + + printf("--- XML generator test finished ---\n"); + + return 0; +} + diff --git a/os/src/test/xml_generator/target.mk b/os/src/test/xml_generator/target.mk new file mode 100644 index 000000000..325c43cb0 --- /dev/null +++ b/os/src/test/xml_generator/target.mk @@ -0,0 +1,3 @@ +TARGET = test-xml_generator +SRC_CC = main.cc +LIBS = base diff --git a/tool/autopilot.list b/tool/autopilot.list index e21b850fb..85674087b 100644 --- a/tool/autopilot.list +++ b/tool/autopilot.list @@ -36,3 +36,4 @@ resource_request resource_yield gdb_monitor part_blk +xml_generator