diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2010-05-11 12:20:11 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2010-05-11 12:20:11 +0200 |
commit | 2e501c68a8641a2b3c430b55f13491a9c1c5d0f5 (patch) | |
tree | 49c2748443fe3c1f01108756b647440e0647a11b /examples | |
parent | 161beba6cdb0d91b15ad19fa8b3e51d986203915 (diff) |
Add support for custom allocators
New example: examples/cxx/hybrid/allocator.
Diffstat (limited to 'examples')
-rw-r--r-- | examples/cxx/hybrid/README | 11 | ||||
-rw-r--r-- | examples/cxx/hybrid/allocator/README | 62 | ||||
-rw-r--r-- | examples/cxx/hybrid/allocator/arena.cxx | 167 | ||||
-rw-r--r-- | examples/cxx/hybrid/allocator/arena.hxx | 52 | ||||
-rw-r--r-- | examples/cxx/hybrid/allocator/driver.cxx | 325 | ||||
-rw-r--r-- | examples/cxx/hybrid/allocator/makefile | 108 | ||||
-rw-r--r-- | examples/cxx/hybrid/allocator/people.xml | 28 | ||||
-rw-r--r-- | examples/cxx/hybrid/allocator/people.xsd | 37 | ||||
-rw-r--r-- | examples/cxx/hybrid/makefile | 22 |
9 files changed, 806 insertions, 6 deletions
diff --git a/examples/cxx/hybrid/README b/examples/cxx/hybrid/README index 7d9312d..dabc11f 100644 --- a/examples/cxx/hybrid/README +++ b/examples/cxx/hybrid/README @@ -1,7 +1,7 @@ -This directory contains a number of examples that show how to -use the Embedded C++/Hybrid mapping. The following list gives -an overview of each example. See the README files in example -directories for more information on each example. +This directory contains a number of examples that show how to use the +Embedded C++/Hybrid mapping. The following list gives an overview of +each example. See the README files in example directories for more +information on each example. hello A simple "Hello, world!" example that shows how to parse XML @@ -44,6 +44,9 @@ polyroot Shows how to handle XML vocabularies with polymorphic document root elements. +allocator + Shows how to use a custom memory allocator implementation. + custom/ A collection of examples that show how to customize the C++/Hybrid object model by using custom C++ classes instead of or in addition diff --git a/examples/cxx/hybrid/allocator/README b/examples/cxx/hybrid/allocator/README new file mode 100644 index 0000000..f2ea51c --- /dev/null +++ b/examples/cxx/hybrid/allocator/README @@ -0,0 +1,62 @@ +This example shows how to use a custom memory allocator implementation +with the Embedded C++/Hybrid mapping. It is based on the 'minimal' example +(so the support for STL, iostream, and C++ exceptions is disabled) and the +only changes made are to the memory management. + +The example consists of the following files: + +people.xsd + XML Schema which describes a collection of person records. + +people.xml + Sample XML instance document. + +people.hxx +people.cxx + +people-pskel.hxx +people-pskel.cxx +people-pimpl.hxx +people-pimpl.cxx + +people-pskel.hxx +people-pskel.cxx +people-pimpl.hxx +people-pimpl.cxx + Object model (the first pair of files), parser skeletons (the second + pair), parser implementations (the third pair), serializer skeletons + (the fourth pair), and serializer implementations (the fifth pair). + These files are generated by the XSD/e compiler from people.xsd. The + --generate-parser, --generate-serializer, --generate-aggregate, + --no-stl, --no-iostream, and --no-exceptions options were used to + request the generation of the parsing and serialization code as well + as to disable the use of STL, iostream, and C++ exceptions. Furthermore, + the --custom-allocator option was specific to make the generated code + use custom memory management functions instead of the standard new and + delete operators. + +arena.hxx +arena.cxx + An implementation of a simple pooled memory allocator. + +driver.cxx + Driver for the example. Besides the implementation of the main() + function, this file also provides the implementations of the custom + memory management function, xsde_alloc, xsde_realloc, and xsde_free, + that are used by the XSD/e runtime and the generated code. + + The driver first sets up the memory pool using a stack-allocated memory + region. It then calls the parser that constructs the object model from + the input XML file. After that the driver prints the content of the + object model to STDERR. Finally, the driver modifies the object model + and calls the serializer to serialize it back to XML. The implementation + also prints the memory usage statistics after parsing and after + serialization. + +To run the example on the sample XML instance document simply execute: + +$ ./driver people.xml + +The example reads from STDIN if input file is not specified: + +$ ./driver <people.xml diff --git a/examples/cxx/hybrid/allocator/arena.cxx b/examples/cxx/hybrid/allocator/arena.cxx new file mode 100644 index 0000000..facb852 --- /dev/null +++ b/examples/cxx/hybrid/allocator/arena.cxx @@ -0,0 +1,167 @@ +// file : examples/cxx/hybrid/allocator/arena.cxx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : not copyrighted - public domain + +#include <stdio.h> +#include <string.h> // memcpy + +#include "arena.hxx" + +arena:: +arena (void* memory, size_t size) + : alloc_count_ (0), + realloc_count_ (0), + free_count_ (0), + cur_allocated_ (0), + max_allocated_ (0) +{ + head_ = static_cast<block*> (memory); + head_->cap = size - sizeof (block); + head_->next = 0; + head_->used = false; +} + +void* arena:: +allocate (size_t size) +{ + block* b = head_; + + while (b != 0) + { + if (!b->used) + { + if (b->cap >= size) + { + // See if it makes sense to fragment this block. + // + if (b->cap - size >= sizeof (block) * 2) + { + block* b1 = reinterpret_cast<block*> ( + reinterpret_cast<char*> (b) + sizeof (block) + size); + + b1->cap = b->cap - size - sizeof (block); + b1->next = b->next; + b1->used = false; + + b->next = b1; + b->cap = size; + } + + b->used = true; + b->size = size; + + alloc_count_++; + cur_allocated_ += size; + + if (cur_allocated_ > max_allocated_) + max_allocated_ = cur_allocated_; + + return b + 1; + } + + // This block is not big enough. See if we can merge it + // with the next block. + // + if (b->next != 0 && !b->next->used) + { + block* b1 = b->next; + b->cap += b1->cap + sizeof (block); + b->next = b1->next; + + // Try the merged block again. + // + continue; + } + } + + // This block is either in use or not big enough. Continue + // searching. + // + b = b->next; + } + + return 0; +} + +void* arena:: +reallocate (void* p, size_t size) +{ + // If the passed pointer is NULL, reallocate is equivalent to + // allocate. + // + if (!p) + return allocate (size); + + block* b = static_cast<block*> (p) - 1; + + // If the passed size is NULL, reallocate is equivalent to free. + // + if (size == 0) + { + free (p); + return 0; + } + + // If this block is not large enough to satisfy the request, try to + // merge it with the next block(s). + // + while (b->cap < size && b->next != 0 && !b->next->used) + { + block* b1 = b->next; + b->cap += b1->cap + sizeof (block); + b->next = b1->next; + } + + // If this block is now large enough then we can reuse the same + // memory region. + // + if (b->cap >= size) + { + realloc_count_++; + cur_allocated_ += size - b->size; + + if (cur_allocated_ > max_allocated_) + max_allocated_ = cur_allocated_; + + b->size = size; + return p; + } + + // Otherwise allocate a new block and copy the data over. + // + void* r = allocate (size); + + if (r) + { + memcpy (r, p, b->size); + free (p); + } + + return r; +} + +void arena:: +free (void* p) +{ + if (p) + { + block* b = static_cast<block*> (p) - 1; + + cur_allocated_ -= b->size; + free_count_++; + + b->used = false; + } +} + +void arena:: +print_statistics () +{ + printf ("\n"); + printf ("allocations: %lu\n", alloc_count_); + printf ("reallocations: %lu\n", realloc_count_); + printf ("deallocations: %lu\n", free_count_); + printf ("currently in use: %lu bytes\n", cur_allocated_); + printf ("maximum in use: %lu bytes\n", max_allocated_); + printf ("\n"); +} diff --git a/examples/cxx/hybrid/allocator/arena.hxx b/examples/cxx/hybrid/allocator/arena.hxx new file mode 100644 index 0000000..0463a38 --- /dev/null +++ b/examples/cxx/hybrid/allocator/arena.hxx @@ -0,0 +1,52 @@ +// file : examples/cxx/hybrid/allocator/arena.hxx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : not copyrighted - public domain + +#ifndef ARENA_HXX +#define ARENA_HXX + +#include <stddef.h> // size_t + +// Sample pooled memory arena. The primary goal here is to provide a +// simple, if naive, implementation. As a result, it probably shouldn't +// be used in production. +// +class arena +{ +public: + arena (void* memory, size_t size); + + void* + allocate (size_t); + + void* + reallocate (void*, size_t); + + void + free (void*); + + void + print_statistics (); + +private: + struct block + { + size_t cap; // block capacity + size_t size; // allocated size + block* next; // next block + size_t used; // used flag + }; + + block* head_; // Linked list of blocks. + + // Statistics. + // + size_t alloc_count_; + size_t realloc_count_; + size_t free_count_; + + size_t cur_allocated_; + size_t max_allocated_; +}; + +#endif // ARENA_HXX diff --git a/examples/cxx/hybrid/allocator/driver.cxx b/examples/cxx/hybrid/allocator/driver.cxx new file mode 100644 index 0000000..933784e --- /dev/null +++ b/examples/cxx/hybrid/allocator/driver.cxx @@ -0,0 +1,325 @@ +// file : examples/cxx/hybrid/allocator/driver.cxx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : not copyrighted - public domain + +#include <stdio.h> + +#include "arena.hxx" + +#include "people.hxx" +#include "people-pimpl.hxx" +#include "people-simpl.hxx" + +// eVC++ 4.0 does not like using xml_schema::strdupx; Note also that +// xml_schema::strdupx will use the custom allocator to allocate the +// memory. +// +inline char* +strdupx (const char* s) +{ + return xml_schema::strdupx (s); +} + +// Implementation of the XSD/e custom allocator functions. +// +arena* xsde_arena; + +extern "C" void* +xsde_alloc (size_t n) +{ + return xsde_arena->allocate (n); +} + +extern "C" void* +xsde_realloc (void* p, size_t n) +{ + return xsde_arena->reallocate (p, n); +} + +extern "C" void +xsde_free (void* p) +{ + return xsde_arena->free (p); +} + +// +// +struct writer: xml_schema::writer +{ + virtual bool + write (const char* s, size_t n) + { + return fwrite (s, n, 1, stdout) == 1; + } + + virtual bool + flush () + { + return fflush (stdout) == 0; + } +}; + +int +main (int argc, char* argv[]) +{ + const char* input; + + if (argc < 2) + { + input = "STDIN"; + fprintf (stderr, "XML file not specified, reading from STDIN\n"); + } + else + input = argv[1]; + + // Set up the memory pool. + // + char pool[65536 * 2]; + arena ar (pool, sizeof (pool)); + xsde_arena = &ar; + + // Open the file or use STDIN. + // + FILE* f = argc > 1 ? fopen (argv[1], "rb") : stdin; + + if (f == 0) + { + fprintf (stderr, "%s: unable to open\n", input); + return 1; + } + + // Parse. + // + using xml_schema::parser_error; + + parser_error pe; + bool io_error = false; + people* ppl = 0; + + do + { + people_paggr people_p; + xml_schema::document_pimpl doc_p (people_p.root_parser (), + people_p.root_name ()); + + if (pe = doc_p._error ()) + break; + + people_p.pre (); + + if (pe = people_p._error ()) + break; + + char buf[4096]; + + do + { + size_t s = fread (buf, 1, sizeof (buf), f); + + if (s != sizeof (buf) && ferror (f)) + { + io_error = true; + break; + } + + doc_p.parse (buf, s, feof (f) != 0); + pe = doc_p._error (); + + } while (!pe && !feof (f)); + + if (io_error || pe) + break; + + ppl = people_p.post (); + + pe = people_p._error (); + + } while (false); + + if (argc > 1) + fclose (f); + + // Handle parsing errors. + // + if (io_error) + { + fprintf (stderr, "%s: read failure\n", input); + return 1; + } + + if (pe) + { + switch (pe.type ()) + { + case parser_error::sys: + { + fprintf (stderr, "%s: %s\n", input, pe.sys_text ()); + break; + } + case parser_error::xml: + { + fprintf (stderr, "%s:%lu:%lu: %s\n", + input, pe.line (), pe.column (), pe.xml_text ()); + break; + } +#ifdef XSDE_PARSER_VALIDATION + case parser_error::schema: + { + fprintf (stderr, "%s:%lu:%lu: %s\n", + input, pe.line (), pe.column (), pe.schema_text ()); + break; + } +#endif + case parser_error::app: + { + fprintf (stderr, "%s:%lu:%lu: application error %d\n", + input, pe.line (), pe.column (), pe.app_code ()); + break; + } + default: + break; + } + + return 1; + } + + // Print memory usage statistics. + // + xsde_arena->print_statistics (); + + // Print what we've got. + // + people::person_sequence& ps = ppl->person (); + + for (people::person_const_iterator i = ps.begin (); i != ps.end (); ++i) + { + printf ("first: %s\n" "last: %s\n" "gender: %s\n" "age: %hu\n\n", + i->first_name (), + i->last_name (), + i->gender ().string (), + i->age ()); + } + + // Remove people that are younger than 30. + // + for (people::person_iterator j = ps.begin (); j != ps.end ();) + { + if (j->age () < 30) + j = ps.erase (j); + else + ++j; + } + + // Insert a new person. Note that we need to allocate the + // object using the XSD/e allocator. + // + { + void* mem = xsde_alloc (sizeof (person)); + + if (!mem) + { + fprintf (stderr, "no memory\n"); + return 1; + } + + person* p = new (mem) person; + + p->first_name (strdupx ("Joe")); // No out of memory check. + p->last_name (strdupx ("Dirt")); // No out of memory check. + p->age (36); + p->gender (gender::male); + + ps.insert (ps.begin (), p); // No out of memory check. + } + + // Serialize. + // + using xml_schema::serializer_error; + + serializer_error se; + writer w; + + do + { + people_saggr people_s; + xml_schema::document_simpl doc_s (people_s.root_serializer (), + people_s.root_name ()); + + doc_s.add_no_namespace_schema ("people.xsd"); + + se = doc_s._error (); + if (se) + break; + + people_s.pre (*ppl); + + se = people_s._error (); + if (se) + break; + + doc_s.serialize (w, xml_schema::document_simpl::pretty_print); + + se = doc_s._error (); + if (se) + break; + + people_s.post (); + + se = people_s._error (); + + } while (false); + + + // Delete the people object model. Here we can explicitly call the + // destructor and then free the memory using the custom allocator + // function. Alternatively, if the arena is used to hold only one + // object model, then we can simply destroy the arena since all + // the dynamic memory allocated by the object model is inside + // this arena. + // + // + ppl->~people (); + xsde_free (ppl); + + // Print memory usage statistics. + // + xsde_arena->print_statistics (); + xsde_arena = 0; + + // Handle serializer errors. + // + if (se) + { + switch (se.type ()) + { + case serializer_error::sys: + { + fprintf (stderr, "error: %s\n", se.sys_text ()); + break; + } + case serializer_error::xml: + { + fprintf (stderr, "error: %s\n", se.xml_text ()); + break; + } +#ifdef XSDE_SERIALIZER_VALIDATION + case serializer_error::schema: + { + fprintf (stderr, "error: %s\n", se.schema_text ()); + break; + } +#endif + case serializer_error::app: + { + fprintf (stderr, "application error: %d\n", se.app_code ()); + break; + } + default: + break; + } + + return 1; + } + + return 0; +} diff --git a/examples/cxx/hybrid/allocator/makefile b/examples/cxx/hybrid/allocator/makefile new file mode 100644 index 0000000..d6a1cd6 --- /dev/null +++ b/examples/cxx/hybrid/allocator/makefile @@ -0,0 +1,108 @@ +# file : examples/cxx/hybrid/allocator/makefile +# author : Boris Kolpackov <boris@codesynthesis.com> +# copyright : Copyright (c) 2005-2010 Code Synthesis Tools CC +# license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +include $(dir $(lastword $(MAKEFILE_LIST)))../../../../build/bootstrap.make + +xsd := people.xsd +cxx := driver.cxx arena.cxx + +obj := $(addprefix $(out_base)/,\ +$(cxx:.cxx=.o) \ +$(xsd:.xsd=.o) \ +$(xsd:.xsd=-pskel.o) \ +$(xsd:.xsd=-pimpl.o) \ +$(xsd:.xsd=-sskel.o) \ +$(xsd:.xsd=-simpl.o)) + +dep := $(obj:.o=.o.d) + +xsde.l := $(out_root)/libxsde/xsde/xsde.l +xsde.l.cpp-options := $(out_root)/libxsde/xsde/xsde.l.cpp-options + +driver := $(out_base)/driver +dist := $(out_base)/.dist +dist-win := $(out_base)/.dist-win +clean := $(out_base)/.clean + +# Build. +# +$(driver): $(obj) $(xsde.l) + +$(obj) $(dep): $(xsde.l.cpp-options) + +genf := $(xsd:.xsd=.hxx) $(xsd:.xsd=.cxx) \ + $(xsd:.xsd=-pskel.hxx) $(xsd:.xsd=-pskel.cxx) \ + $(xsd:.xsd=-pimpl.hxx) $(xsd:.xsd=-pimpl.cxx) \ + $(xsd:.xsd=-sskel.hxx) $(xsd:.xsd=-sskel.cxx) \ + $(xsd:.xsd=-simpl.hxx) $(xsd:.xsd=-simpl.cxx) + +gen := $(addprefix $(out_base)/,$(genf)) + +$(gen): $(out_root)/xsde/xsde +$(gen): xsde := $(out_root)/xsde/xsde +$(gen): xsde_options += --generate-parser --generate-serializer \ +--generate-aggregate + +$(call include-dep,$(dep)) + +# Convenience alias for default target. +# +$(out_base)/: $(driver) + + +# Dist. +# +dist-common := $(out_base)/.dist-common +$(dist) $(dist-win) $(dist-common): path := $(subst $(src_root)/,,$(src_base)) + +$(dist-common): + $(call install-data,$(src_base)/driver.cxx,$(dist_prefix)/$(path)/driver.cxx) + $(call install-data,$(src_base)/arena.cxx,$(dist_prefix)/$(path)/arena.cxx) + $(call install-data,$(src_base)/arena.hxx,$(dist_prefix)/$(path)/arena.hxx) + $(call install-data,$(src_base)/people.xsd,$(dist_prefix)/$(path)/people.xsd) + $(call install-data,$(src_base)/people.xml,$(dist_prefix)/$(path)/people.xml) + +$(dist): $(dist-common) + $(call install-data,$(src_base)/README,$(dist_prefix)/$(path)/README) + +$(dist-win): $(dist-common) + $(call install-data,$(src_base)/README,$(dist_prefix)/$(path)/README.txt) + $(call message,,unix2dos $(dist_prefix)/$(path)/README.txt) + + +# Clean. +# +$(clean): $(driver).o.clean \ + $(addsuffix .cxx.clean,$(obj)) \ + $(addsuffix .cxx.clean,$(dep)) \ + $(addprefix $(out_base)/,$(xsd:.xsd=.cxx.xsd.clean)) + + +# Generated .gitignore. +# +ifeq ($(out_base),$(src_base)) +$(gen): | $(out_base)/.gitignore +$(driver): | $(out_base)/.gitignore + +$(out_base)/.gitignore: files := driver $(genf) +$(clean): $(out_base)/.gitignore.clean + +$(call include,$(bld_root)/git/gitignore.make) +endif + + +# How to. +# +$(call include,$(bld_root)/cxx/o-e.make) +$(call include,$(bld_root)/cxx/cxx-o.make) +$(call include,$(bld_root)/cxx/cxx-d.make) +$(call include,$(bld_root)/install.make) +$(call include,$(scf_root)/xsde/hybrid/xsd-cxx.make) + + +# Dependencies. +# +$(call import,$(src_root)/xsde/makefile) +$(call import,$(src_root)/libxsde/xsde/makefile) diff --git a/examples/cxx/hybrid/allocator/people.xml b/examples/cxx/hybrid/allocator/people.xml new file mode 100644 index 0000000..ad4135a --- /dev/null +++ b/examples/cxx/hybrid/allocator/people.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> + +<!-- + +file : examples/cxx/hybrid/allocator/people.xml +author : Boris Kolpackov <boris@codesynthesis.com> +copyright : not copyrighted - public domain + +--> + +<people xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="people.xsd"> + + <person> + <first-name>John</first-name> + <last-name>Doe</last-name> + <gender>male</gender> + <age>32</age> + </person> + + <person> + <first-name>Jane</first-name> + <last-name>Doe</last-name> + <gender>female</gender> + <age>28</age> + </person> + +</people> diff --git a/examples/cxx/hybrid/allocator/people.xsd b/examples/cxx/hybrid/allocator/people.xsd new file mode 100644 index 0000000..131be7b --- /dev/null +++ b/examples/cxx/hybrid/allocator/people.xsd @@ -0,0 +1,37 @@ +<?xml version="1.0"?> + +<!-- + +file : examples/cxx/hybrid/allocator/people.xsd +author : Boris Kolpackov <boris@codesynthesis.com> +copyright : not copyrighted - public domain + +--> + +<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> + + <xsd:simpleType name="gender"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="male"/> + <xsd:enumeration value="female"/> + </xsd:restriction> + </xsd:simpleType> + + <xsd:complexType name="person"> + <xsd:sequence> + <xsd:element name="first-name" type="xsd:string"/> + <xsd:element name="last-name" type="xsd:string"/> + <xsd:element name="gender" type="gender"/> + <xsd:element name="age" type="xsd:unsignedShort"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="people"> + <xsd:sequence> + <xsd:element name="person" type="person" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:element name="people" type="people"/> + +</xsd:schema> diff --git a/examples/cxx/hybrid/makefile b/examples/cxx/hybrid/makefile index c82a2ec..8bdb2de 100644 --- a/examples/cxx/hybrid/makefile +++ b/examples/cxx/hybrid/makefile @@ -5,8 +5,20 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../../../build/bootstrap.make -all_examples := binary compositors custom hello multiroot polymorphism \ -polyroot streaming library wildcard filter minimal +all_examples := \ +allocator \ +binary \ +compositors \ +custom \ +hello \ +multiroot \ +polymorphism \ +polyroot \ +streaming \ +library \ +wildcard \ +filter \ +minimal build_examples := binary compositors custom @@ -29,9 +41,15 @@ endif ifeq ($(xsde_stl),n) ifeq ($(xsde_exceptions),n) build_examples += minimal + +ifeq ($(xsde_custom_allocator),y) +build_examples += allocator +endif + endif endif + default := $(out_base)/ dist := $(out_base)/.dist dist-win := $(out_base)/.dist-win |