BumpyCaps for type names.
Drop pp_ prefix all over.
Move util code to a subdir.
http://code.google.com/p/prettyprint/source/detail?r=533
Added:
/trunk/alias.h
/trunk/array.h
/trunk/binding.h
/trunk/context.h
/trunk/datatype.h
/trunk/datatype_types.h
/trunk/dirent.h
/trunk/driver.h
/trunk/field.h
/trunk/field_types.h
/trunk/path.cpp
/trunk/path.h
/trunk/regbits.h
/trunk/register.h
/trunk/register_types.h
/trunk/rwprocs.h
/trunk/scope.cpp
/trunk/scope.h
/trunk/tests/alias_test.cpp
/trunk/tests/array_test.cpp
/trunk/tests/binding_test.cpp
/trunk/tests/datatype_test.cpp
/trunk/tests/dirent_test.cpp
/trunk/tests/field_test.cpp
/trunk/tests/path_test.cpp
/trunk/tests/regbits_test.cpp
/trunk/tests/register_test.cpp
/trunk/tests/scope_test.cpp
/trunk/util
/trunk/util/bignum.h
/trunk/util/bignum_lambda.h
/trunk/util/bit_buffer.h
/trunk/util/filesystem.h
/trunk/util/keyed_vector.h
/trunk/util/printfxx.h
/trunk/util/shared_object.h
/trunk/util/simple_regex.h
/trunk/util/sockets.h
/trunk/util/syserror.h
/trunk/version.h
Deleted:
/trunk/bignum.h
/trunk/bit_buffer.h
/trunk/filesystem.h
/trunk/keyed_vector.h
/trunk/pp_alias.h
/trunk/pp_array.h
/trunk/pp_binding.h
/trunk/pp_context.h
/trunk/pp_datatype.h
/trunk/pp_datatypes.h
/trunk/pp_dirent.h
/trunk/pp_driver.h
/trunk/pp_field.h
/trunk/pp_fields.h
/trunk/pp_lambda.h
/trunk/pp_path.cpp
/trunk/pp_path.h
/trunk/pp_regbits.h
/trunk/pp_register.h
/trunk/pp_registers.h
/trunk/pp_rwprocs.h
/trunk/pp_scope.cpp
/trunk/pp_scope.h
/trunk/printfxx.h
/trunk/shared_object.h
/trunk/simple_regex.h
/trunk/sockets.h
/trunk/syserror.h
/trunk/tests/pp_alias_test.cpp
/trunk/tests/pp_array_test.cpp
/trunk/tests/pp_binding_test.cpp
/trunk/tests/pp_datatype_test.cpp
/trunk/tests/pp_dirent_test.cpp
/trunk/tests/pp_field_test.cpp
/trunk/tests/pp_path_test.cpp
/trunk/tests/pp_regbits_test.cpp
/trunk/tests/pp_register_test.cpp
/trunk/tests/pp_scope_test.cpp
Modified:
/trunk/Makefile
/trunk/TODO
/trunk/device_init.h
/trunk/devices/amd/k8.cpp
/trunk/devices/cpu/cpu.cpp
/trunk/devices/cpuid/cpuid.cpp
/trunk/devices/cpuid/generic_device.cpp
/trunk/devices/cpuid/generic_device.h
/trunk/devices/global.cpp
/trunk/devices/msr/generic_device.cpp
/trunk/devices/msr/generic_device.h
/trunk/devices/msr/msr.cpp
/trunk/devices/pci/generic_device.cpp
/trunk/devices/pci/generic_device.h
/trunk/devices/pci/pci.cpp
/trunk/drivers/Makefile
/trunk/drivers/all_drivers.cpp
/trunk/drivers/cpu/cpu_address.h
/trunk/drivers/cpu/cpu_driver.cpp
/trunk/drivers/cpu/cpu_driver.h
/trunk/drivers/cpu/tests/cpu_driver_test.cpp
/trunk/drivers/cpuid/cpuid_binding.cpp
/trunk/drivers/cpuid/cpuid_binding.h
/trunk/drivers/cpuid/cpuid_driver.cpp
/trunk/drivers/cpuid/cpuid_driver.h
/trunk/drivers/cpuid/tests/cpuid_io_test.cpp
/trunk/drivers/io/io_binding.cpp
/trunk/drivers/io/io_binding.h
/trunk/drivers/io/io_driver.cpp
/trunk/drivers/io/io_driver.h
/trunk/drivers/io/tests/io_io_test.cpp
/trunk/drivers/mem/mem_binding.cpp
/trunk/drivers/mem/mem_binding.h
/trunk/drivers/mem/mem_driver.cpp
/trunk/drivers/mem/mem_driver.h
/trunk/drivers/mem/tests/mem_io_test.cpp
/trunk/drivers/msr/msr_binding.cpp
/trunk/drivers/msr/msr_binding.h
/trunk/drivers/msr/msr_driver.cpp
/trunk/drivers/msr/msr_driver.h
/trunk/drivers/msr/tests/msr_io_test.cpp
/trunk/drivers/pci/pci_binding.cpp
/trunk/drivers/pci/pci_binding.h
/trunk/drivers/pci/pci_driver.cpp
/trunk/drivers/pci/pci_driver.h
/trunk/drivers/pci/tests/pci_io_test.cpp
/trunk/drivers.cpp
/trunk/drivers.h
/trunk/examples/pp_client.cpp
/trunk/examples/pp_discover.cpp
/trunk/examples/pp_fuse.cpp
/trunk/examples/pp_pci.cpp
/trunk/examples/pp_read.cpp
/trunk/examples/pp_server.cpp
/trunk/fake_language.cpp
/trunk/fake_language.h
/trunk/language.cpp
/trunk/language.h
/trunk/magic_regs.cpp
/trunk/pp.h
/trunk/rules.mk
/trunk/runtime.cpp
/trunk/runtime.h
/trunk/tests/Makefile
/trunk/tests/bignum_test.cpp
/trunk/tests/bit_buffer_test.cpp
/trunk/tests/fake_language_test.cpp
/trunk/tests/filesystem_test.cpp
/trunk/tests/keyed_vector_test.cpp
/trunk/tests/magic_regs_test.cpp
/trunk/tests/printfxx_test.cpp
/trunk/tests/regex_test.cpp
/trunk/tests/shared_object_test.cpp
/trunk/tests/sockets_test.cpp
/trunk/tests/syserror_test.cpp
/trunk/tests/test_binding.h
/trunk/tests/test_helpers.cpp
/trunk/tests/test_helpers.h
/trunk/version.cpp
=======================================
--- /dev/null
+++ /trunk/alias.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,56 @@
+/* Copyright (c) Tim Hockin, 2008 */
+#ifndef PP_ALIAS_H__
+#define PP_ALIAS_H__
+
+#include "pp/pp.h"
+#include "pp/dirent.h"
+#include "pp/path.h"
+
+namespace pp {
+
+/*
+ * Alias - a symbolic link to a dirent.
+ */
+class Alias: public Dirent
+{
+ public:
+ explicit Alias(const Path &path)
+ : Dirent(DIRENT_TYPE_ALIAS), m_path(path)
+ {
+ }
+
+ const Path &
+ link_path() const
+ {
+ return m_path;
+ }
+
+ private:
+ Path m_path;
+};
+typedef boost::shared_ptr<Alias> AliasPtr;
+typedef boost::shared_ptr<const Alias> ConstAliasPtr;
+
+#define new_pp_alias(...) ::pp::AliasPtr(new ::pp::Alias(__VA_ARGS__))
+
+// const
+inline ConstAliasPtr
+alias_from_dirent(const ConstDirentPtr &dirent)
+{
+ if (dirent->is_alias()) {
+ return static_pointer_cast<const Alias>(dirent);
+ }
+ throw Dirent::ConversionError("non-alias dirent used as alias");
+}
+// non-const
+inline AliasPtr
+alias_from_dirent(const DirentPtr &dirent)
+{
+ const ConstDirentPtr &const_dirent = dirent;
+ return const_pointer_cast<Alias>(
+ alias_from_dirent(const_dirent));
+}
+
+} // namespace pp
+
+#endif // PP_ALIAS_H__
=======================================
--- /dev/null
+++ /trunk/array.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,103 @@
+/* Copyright (c) Tim Hockin, 2007 */
+#ifndef PP_ARRAY_H__
+#define PP_ARRAY_H__
+
+#include "pp/pp.h"
+#include "pp/util/printfxx.h"
+#include <vector>
+#include <stdexcept>
+#include "pp/path.h"
+#include "pp/dirent.h"
+
+namespace pp {
+
+//
+// Array - an array of dirents.
+//
+class Array: public Dirent
+{
+ public:
+ explicit Array(DirentType type)
+ : Dirent(DIRENT_TYPE_ARRAY), m_type(type)
+ {
+ }
+ virtual ~Array()
+ {
+ }
+
+ //
+ // Add a dirent to this array.
+ // Throws:
+ // Dirent::ConversionError - wrong DirentType
+ //
+ void
+ append(const DirentPtr &dirent)
+ {
+ if (dirent->dirent_type() != m_type) {
+ throw Dirent::ConversionError(
+ sprintfxx("can't append %s to %s[]",
+ dirent->dirent_type(), m_type));
+ }
+ m_vector.push_back(dirent);
+ }
+
+ //
+ // Return the number of dirents in this array.
+ //
+ size_t
+ size() const
+ {
+ return m_vector.size();
+ }
+
+ //
+ // Provide access to the dirent at an index.
+ // Throws:
+ // std::out_of_range - index is out of range
+ //
+ const ConstDirentPtr &
+ at(size_t index) const
+ {
+ return m_vector.at(index);
+ }
+ const ConstDirentPtr &
+ operator[](size_t index) const
+ {
+ return at(index);
+ }
+
+ DirentType
+ array_type() const
+ {
+ return m_type;
+ }
+
+ private:
+ std::vector<ConstDirentPtr> m_vector;
+ DirentType m_type;
+};
+typedef boost::shared_ptr<Array> ArrayPtr;
+typedef boost::shared_ptr<const Array> ConstArrayPtr;
+
+#define new_pp_array(...) ::pp::ArrayPtr(new ::pp::Array(__VA_ARGS__))
+
+// const
+inline ConstArrayPtr
+array_from_dirent(const ConstDirentPtr &dirent)
+{
+ if (dirent->is_array()) {
+ return static_pointer_cast<const Array>(dirent);
+ }
+ throw Dirent::ConversionError("non-array dirent used as array");
+}
+// non-const
+inline ArrayPtr
+array_from_dirent(const DirentPtr &dirent)
+{
+ const ConstDirentPtr &const_dirent = dirent;
+ return const_pointer_cast<Array>(array_from_dirent(const_dirent));
+}
+
+} // namespace pp
+
+#endif // PP_ARRAY_H__
=======================================
--- /dev/null
+++ /trunk/binding.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,101 @@
+/* Copyright (c) Tim Hockin, 2007 */
+#ifndef PP_BINDING_H__
+#define PP_BINDING_H__
+
+#include "pp/pp.h"
+#include <stdexcept>
+#include <ostream>
+
+namespace pp {
+
+/*
+ * Binding - abstract base class for bound register spaces.
+ */
+class Binding
+{
+ public:
+ Binding() {}
+ virtual ~Binding() {}
+
+ /*
+ * Binding::read(address, width)
+ *
+ * Read from a register in this binding.
+ *
+ * Throws: Driver::IoError
+ */
+ virtual Value
+ read(const Value &address, const BitWidth width) const = 0;
+
+ /*
+ * Binding::write(address, width, value)
+ *
+ * Write to a register in this binding.
+ *
+ * Throws: Driver::IoError
+ */
+ virtual void
+ write(const Value &address, const BitWidth width,
+ const Value &value) const = 0;
+
+ /*
+ * Binding::to_string()
+ *
+ * Return a string representing this binding.
+ */
+ virtual string
+ to_string() const = 0;
+};
+typedef boost::shared_ptr<Binding> BindingPtr;
+typedef boost::shared_ptr<const Binding> ConstBindingPtr;
+
+inline std::ostream &
+operator<<(std::ostream& o, const Binding &binding)
+{
+ return o << binding.to_string();
+}
+
+/*
+ * SimpleBinding - a simple binding
+ */
+template<class Tio, class Taddress>
+class SimpleBinding: public Binding
+{
+ public:
+ explicit SimpleBinding(Taddress address)
+ : m_io(address)
+ {
+ }
+
+ virtual Value
+ read(const Value &address, const BitWidth width) const
+ {
+ return m_io.read(address, width);
+ }
+
+ virtual void
+ write(const Value &address, const BitWidth width,
+ const Value &value) const
+ {
+ return m_io.write(address, width, value);
+ }
+
+ virtual string
+ to_string() const
+ {
+ return ::to_string(m_io.address());
+ }
+
+ const Taddress &
+ address()
+ {
+ return m_io.address();
+ }
+
+ private:
+ Tio m_io;
+};
+
+} // namespace pp
+
+#endif // PP_BINDING_H__
=======================================
--- /dev/null
+++ /trunk/context.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,129 @@
+/* Copyright (c) Tim Hockin, 2008 */
+#ifndef PP_CONTEXT_H__
+#define PP_CONTEXT_H__
+
+#include "pp/pp.h"
+#include "pp/scope.h"
+#include "pp/path.h"
+#include "pp/dirent.h"
+#include "pp/register.h"
+#include "pp/field.h"
+#include "pp/datatype.h"
+
+namespace pp {
+
+/*
+ * This encapsulates a context of the PP tree.
+ */
+class Context;
+typedef boost::shared_ptr<Context> ContextPtr;
+#define
new_pp_context(...) ::pp::ContextPtr(new ::pp::Context(__VA_ARGS__))
+
+class Context
+{
+ private:
+ Path::Element m_name;
+ boost::weak_ptr<Scope> m_scope;
+ bool m_readonly;
+
+ public:
+ Context(const string &n, const ScopePtr &s)
+ : m_name(n), m_scope(s), m_readonly(false)
+ {
+ }
+ Context(const Path::Element &n, const ScopePtr &s)
+ : m_name(n), m_scope(s), m_readonly(false)
+ {
+ }
+
+ /*
+ * Take a read-only snapshot of the current context
+ */
+ ContextPtr
+ snapshot() const
+ {
+ ContextPtr new_ctxt = new_pp_context(*this);
+ new_ctxt->m_readonly = true;
+ return new_ctxt;
+ }
+
+ /*
+ * getter methods
+ */
+ string
+ name() const
+ {
+ return m_name.to_string();
+ }
+ ScopePtr
+ scope() const
+ {
+ return m_scope.lock();
+ }
+ bool
+ is_readonly() const
+ {
+ return m_readonly;
+ }
+
+ /*
+ * Encapsulate for ease of use.
+ */
+
+ const ConstBindingPtr &
+ binding() const
+ {
+ return scope()->binding();
+ }
+
+ void
+ add_datatype(const string &name, const DatatypePtr &datatype)
+ {
+ scope()->add_datatype(name, datatype);
+ }
+
+ ConstDatatypePtr
+ resolve_datatype(const string &name) const
+ {
+ return scope()->resolve_datatype(name);
+ }
+
+ Path
+ resolve_path(const string &path_str) const
+ {
+ return scope()->resolve_path(path_str);
+ }
+
+ void
+ add_dirent(const string &name, const DirentPtr &dirent)
+ {
+ scope()->add_dirent(name, dirent);
+ }
+ void
+ add_dirent(const Path::Element &name, const DirentPtr &dirent)
+ {
+ scope()->add_dirent(name, dirent);
+ }
+
+ bool
+ dirent_defined(const Path &path) const
+ {
+ return scope()->dirent_defined(path);
+ }
+
+ ConstDirentPtr
+ lookup_dirent(const Path &path, unsigned flags=0) const
+ {
+ return scope()->lookup_dirent(path, flags);
+ }
+
+ void
+ add_bookmark(const string &name)
+ {
+ return scope()->add_bookmark(name);
+ }
+};
+
+} // namespace pp
+
+#endif // PP_CONTEXT_H__
=======================================
--- /dev/null
+++ /trunk/datatype.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,104 @@
+/* Copyright (c) Tim Hockin, 2007 */
+#ifndef PP_DATATYPE_H__
+#define PP_DATATYPE_H__
+
+#include "pp/pp.h"
+#include <stdexcept>
+
+namespace pp {
+
+/*
+ * Datatype - abstract base class for all datatypes.
+ */
+class Datatype
+{
+ public:
+ // an invalid value error
+ struct InvalidError: public std::runtime_error
+ {
+ explicit InvalidError(const string &str)
+ : runtime_error(str)
+ {
+ }
+ };
+
+ virtual ~Datatype() {}
+
+ /*
+ * Datatype::evaluate(value)
+ *
+ * Evaluate a value against this datatype. This method returns a
+ * string containing the evaluated representation of the 'value'
+ * argument.
+ */
+ virtual string
+ evaluate(const Value &value) const = 0;
+
+ /*
+ * Datatype::lookup(str)
+ * Datatype::lookup(value)
+ *
+ * Lookup the value of a (potentially valid) evaluation for this
+ * datatype. Each specific subclass will override these.
+ *
+ * This can throw Datatype::InvalidError.
+ */
+ virtual Value
+ lookup(const string &str) const = 0;
+ virtual Value
+ lookup(const Value &value) const = 0;
+
+ /*
+ * Datatype::compare(value, str)
+ * Datatype::compare(value, value)
+ * Datatype::test(value, str)
+ * Datatype::test(value, value)
+ *
+ * Compare a Value against another argument, according to the
+ * rules of this datatype. Return an integer which indicates
+ * whether the lhs is less than, equal to, or greater than the rhs
+ * (negative, zero, positive return code, respectively).
+ *
+ * This can throw Datatype::InvalidError.
+ */
+ virtual int
+ compare(const Value &lhs, const string &rhs) const
+ {
+ /* default implementation */
+ Value result = lhs - lookup(rhs);
+ if (result < 0)
+ return -1;
+ if (result > 0)
+ return 1;
+ return 0;
+ }
+ virtual int
+ compare(const Value &lhs, const Value &rhs) const
+ {
+ /* default implementation */
+ Value result = lhs - lookup(rhs);
+ if (result < 0)
+ return -1;
+ if (result > 0)
+ return 1;
+ return 0;
+ }
+ virtual Value
+ test(const Value &lhs, const string &rhs) const
+ {
+ /* default implementation */
+ return (lhs & lookup(rhs));
+ }
+ virtual Value
+ test(const Value &lhs, const Value &rhs) const
+ {
+ /* default implementation */
+ return (lhs & lookup(rhs));
+ }
+};
+typedef boost::shared_ptr<Datatype> DatatypePtr;
+typedef boost::shared_ptr<const Datatype> ConstDatatypePtr;
+
+} // namespace pp
+
+#endif // PP_DATATYPE_H__
=======================================
--- /dev/null
+++ /trunk/datatype_types.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,741 @@
+/* Copyright (c) Tim Hockin, 2007 */
+#ifndef PP_DATATYPES_H__
+#define PP_DATATYPES_H__
+
+#include "pp/pp.h"
+#include "pp/util/printfxx.h"
+#include "pp/datatype.h"
+#include "pp/util/keyed_vector.h"
+#include "language.h"
+#include "pp/util/bit_buffer.h"
+
+namespace pp {
+
+/*
+ * MultiDatatype - datatype for combining multiple subtypes based
+ * on the value being read
+ *
+ * NOTE: range is between min and max inclusive
+ */
+class MultiDatatype: public Datatype
+{
+ private:
+ struct pp_multi_range
+ {
+ pp_multi_range(const ConstDatatypePtr &datatype,
+ Value min, Value max)
+ : type(datatype), low(min), high(max) {}
+ ConstDatatypePtr type;
+ Value low;
+ Value high;
+ };
+ std::vector<pp_multi_range> m_parts;
+
+ static bool
+ val_in_range(const Value &value, const pp_multi_range &range) {
+ return ((value >= range.low) && (value <= range.high));
+ }
+
+ public:
+ MultiDatatype()
+ {
+ }
+
+ virtual ~MultiDatatype()
+ {
+ }
+
+ /*
+ * MultiDatatype::evaluate(value)
+ *
+ * Evaluate a value against this datatype. This method returns a
+ * string containing the representation of the 'value' argument,
+ * evaluated with the datatype (if any) assigned to the given value's
+ * range.
+ *
+ */
+ virtual string
+ evaluate(const Value &value) const
+ {
+ for (size_t i = 0; i < m_parts.size(); i++) {
+ if (val_in_range(value, m_parts[i])) {
+ return m_parts[i].type->evaluate(value);
+ }
+ }
+
+ // Value out of range; return unknown indicator
+ return sprintfxx("<!%d!>", value);
+ }
+
+ /*
+ * MultiDatatype::lookup(str)
+ * MultiDatatype::lookup(value) -- simply validate it's in range
+ *
+ * Lookup the value of a (potentially valid) evaluation for this
+ * datatype. For an multi type, that means converting a string to
+ * it's corresponding value, or validating that a numeric value is
+ * a valid option.
+ *
+ * WARNING: in case the given string could map to multiple different
+ * Values, the lowest possible value is returned
+ *
+ * This will throw Datatype::InvalidError if the lookup is not a
+ * valid value for this enum.
+ *
+ */
+ virtual Value
+ lookup(const string &str) const
+ {
+ for (size_t i = 0; i < m_parts.size(); i++) {
+ try {
+ Value val = m_parts[i].type->lookup(str);
+ if (val_in_range(val, m_parts[i])) {
+ return val;
+ }
+ } catch (Datatype::InvalidError &e) {
+ // Keep trying on exception
+ }
+ }
+ throw Datatype::InvalidError("Could not match given string "
+ "to a range: " + str);
+ }
+
+ virtual Value
+ lookup(const Value &value) const
+ {
+ for (size_t i = 0; i < m_parts.size(); i++) {
+ if (val_in_range(value, m_parts[i])) {
+ return value;
+ }
+ }
+ throw Datatype::InvalidError("Given value out of range: " +
+ to_string(value));
+ }
+
+ /*
+ * MultiDatatype::add_range(pp_multi_range)
+ * MultiDatatype::add_range(datatype, min, max)
+ *
+ * Add a new range->datatype mapping to this multi datatype.
+ *
+ */
+ void
+ add_range(const pp_multi_range &range)
+ {
+ add_range(range.type, range.low, range.high);
+ }
+ void
+ add_range(const ConstDatatypePtr &datatype,
+ Value min, Value max)
+ {
+ if (min > max) {
+ throw Datatype::InvalidError(sprintfxx(
+ "Minimum value %d cannot be higher "
+ "than maximum value %d", min, max));
+ }
+
+ // If the vector is empty, we can add it right away
+ if (m_parts.size() == 0) {
+ m_parts.push_back(
+ pp_multi_range(datatype, min, max));
+ return;
+ }
+
+ // See if we can add it in the middle
+ std::vector<pp_multi_range>::iterator iter;
+ for (iter = m_parts.begin(); iter != m_parts.end(); iter++) {
+ if (min > iter->high) {
+ continue;
+ } else if (max < iter->low) {
+ m_parts.insert(iter, pp_multi_range(
+ datatype, min, max));
+ return;
+ } else {
+ throw Datatype::InvalidError(
+ "Cannot have overlapping ranges");
+ }
+ }
+
+ // Range is higher than everything else; add to end
+ m_parts.push_back(
+ pp_multi_range(datatype, min, max));
+ }
+};
+typedef boost::shared_ptr<MultiDatatype> MultiDatatypePtr;
+
+#define new_pp_multi_datatype(...) \
+ ::pp::MultiDatatypePtr(new ::pp::MultiDatatype(__VA_ARGS__))
+
+/*
+ * EnumDatatype - datatype for enumerated values.
+ *
+ * NOTE: Enum keys must be unique.
+ */
+class EnumDatatype: public Datatype
+{
+ private:
+ util::KeyedVector<string, Value> m_values;
+ string m_unknown;
+ bool m_custom_unknown;
+
+ public:
+ EnumDatatype()
+ : m_custom_unknown(false)
+ {
+ }
+ explicit EnumDatatype(const util::KeyedVector<string,
+ Value> &values)
+ : m_values(values), m_custom_unknown(false)
+ {
+ }
+ virtual ~EnumDatatype()
+ {
+ }
+
+ /*
+ * EnumDatatype::evaluate(value)
+ *
+ * Evaluate a value against this datatype. This method returns a
+ * string containing the evaluated representation of the 'value'
+ * argument.
+ */
+ virtual string
+ evaluate(const Value &value) const
+ {
+ for (size_t i=0; i < m_values.size(); i++) {
+ if (m_values[i] == value) {
+ return m_values.key_at(i);
+ }
+ }
+ if (m_custom_unknown) {
+ return m_unknown;
+ }
+ return sprintfxx("<!%d!>", value);
+ }
+
+ /*
+ * EnumDatatype::lookup(str)
+ * EnumDatatype::lookup(value)
+ *
+ * Lookup the value of a (potentially valid) evaluation for this
+ * datatype. For an enum type, that means converting a string to
+ * it's corresponding value, or validating that a numeric value is
+ * a valid option.
+ *
+ * This will throw Datatype::InvalidError if the lookup is not a
+ * valid value for this enum.
+ */
+ virtual Value
+ lookup(const string &str) const
+ {
+ for (size_t i=0; i < m_values.size(); i++) {
+ if (m_values.key_at(i) == str) {
+ return m_values[i];
+ }
+ }
+ throw Datatype::InvalidError(str);
+ }
+ virtual Value
+ lookup(const Value &value) const
+ {
+ for (size_t i=0; i < m_values.size(); i++) {
+ if (m_values[i] == value) {
+ return m_values[i];
+ }
+ }
+ throw Datatype::InvalidError(to_string(value));
+ }
+
+ /*
+ * EnumDatatype::add_value(name, value)
+ *
+ * Add a possible value to this enumeration.
+ */
+ void
+ add_value(const string &name, const Value &value)
+ {
+ DASSERT_MSG(m_values.find(name) == m_values.end(),
+ "adding duplicate enum key: "
+ + name + " = " + to_string(value));
+ m_values.insert(name, value);
+ }
+
+ /*
+ * EnumDatatype::set_unknown(name)
+ *
+ * Use a string for unknown enumerated values.
+ */
+ void
+ set_unknown(const string &name)
+ {
+ m_unknown = name;
+ m_custom_unknown = true;
+ }
+};
+typedef boost::shared_ptr<EnumDatatype> EnumDatatypePtr;
+
+#define new_pp_enum_datatype(...) \
+ ::pp::EnumDatatypePtr(new ::pp::EnumDatatype(__VA_ARGS__))
+
+/*
+ * BoolDatatype - datatype for boolean values.
+ */
+class BoolDatatype: public EnumDatatype
+{
+ public:
+ BoolDatatype(const string &true_str, const string &false_str)
+ {
+ add_value(true_str, 1);
+ add_value(false_str, 0);
+ set_unknown(true_str);
+ }
+
+ virtual Value
+ lookup(const Value &value) const
+ {
+ return value == 0 ? 0 : 1;
+ }
+ virtual Value
+ lookup(const string &str) const
+ {
+ return EnumDatatype::lookup(str);
+ }
+};
+typedef boost::shared_ptr<BoolDatatype> BoolDatatypePtr;
+
+#define new_pp_bool_datatype(...) \
+ ::pp::BoolDatatypePtr(new ::pp::BoolDatatype(__VA_ARGS__))
+
+/*
+ * BitmaskDatatype - datatype for bitmasks.
+ *
+ * NOTE: Bit names must be unique.
+ * //FIXME: this is operating on bit numbers, not masks - should it?
+ */
+class BitmaskDatatype: public Datatype
+{
+ private:
+ util::KeyedVector<string, Value> m_bits;
+
+ public:
+ BitmaskDatatype()
+ {
+ }
+ explicit BitmaskDatatype(const util::KeyedVector<string, Value> &bits)
+ : m_bits(bits)
+ {
+ }
+ virtual ~BitmaskDatatype()
+ {
+ }
+
+ /*
+ * BitmaskDatatype::evaluate(value)
+ *
+ * Evaluate a value against this datatype. This method returns a
+ * string containing the evaluated representation of the 'value'
+ * argument.
+ */
+ virtual string
+ evaluate(const Value &value) const
+ {
+ string ret("");
+ Value myval = value;
+
+ for (size_t i=0; myval != 0 && i < m_bits.size(); i++) {
+ Value b = m_bits[i];
+ Value mask = (1 << b);
+ if ((myval & mask) != 0) {
+ if (!ret.empty()) {
+ ret += " ";
+ }
+ ret += m_bits.key_at(i);
+ myval ^= (myval & mask);
+ }
+ }
+
+ int unknown = 0;
+ while (myval != 0) {
+ if ((myval & Value(1)) == 1) {
+ if (!ret.empty()) {
+ ret += " ";
+ }
+ ret += sprintfxx("<!%d!>", unknown);
+ }
+ myval >>= 1;
+ unknown++;
+ }
+
+ return ret;
+ }
+
+ /*
+ * BitmaskDatatype::lookup(str)
+ * BitmaskDatatype::lookup(value)
+ *
+ * Lookup the value of a (potentially valid) evaluation for this
+ * datatype. For a bitmask type, this means converting a string to
+ * it's corresponding bit value.
+ *
+ * This will throw Datatype::InvalidError if the string is not a
+ * valid value in this bitmask.
+ */
+ virtual Value
+ lookup(const string &str) const
+ {
+ //FIXME: need to support multiple bits
+ for (size_t i=0; i < m_bits.size(); i++) {
+ if (m_bits.key_at(i) == str) {
+ return m_bits[i];
+ }
+ }
+ throw Datatype::InvalidError(str);
+ }
+ virtual Value
+ lookup(const Value &value) const
+ {
+ //FIXME: need to support multiple bits
+ for (size_t i=0; i < m_bits.size(); i++) {
+ if (m_bits.at(i) == value) {
+ return m_bits[i];
+ }
+ }
+ throw Datatype::InvalidError(to_string(value));
+ }
+
+ /*
+ * BitmaskDatatype::add_bit(name, value)
+ *
+ * Add a named bit to this bitmask.
+ */
+ void
+ add_bit(const string &name, const Value &value)
+ {
+ DASSERT_MSG(m_bits.find(name) == m_bits.end(),
+ "adding duplicate bitmask key: "
+ + name + " = " + to_string(value));
+ m_bits.insert(name, value);
+ }
+};
+typedef boost::shared_ptr<BitmaskDatatype> BitmaskDatatypePtr;
+
+#define new_pp_bitmask_datatype(...) \
+ ::pp::BitmaskDatatypePtr(new ::pp::BitmaskDatatype(__VA_ARGS__))
+
+/*
+ * StringDatatype - datatype for string values
+ *
+ * This datatype interprets a given value as a string in little-endian
order,
+ * meaning that the least significant byte in the input is the first
+ * character in the string. For example, 0x6f6c6c6548 produces "Hello"
(where
+ * 0x48 == 'H').
+ *
+ * Internal non-printing characters are ignored for display purposes. For
+ * example, 0x6f6c6c00006548 also produces "Hello". However, they are
+ * preserved in the string so that the same Value is returned when
+ * using lookup() as that which was given to evaluate() in order to produce
+ * the string in the first place. That is, lookup() and evaluate() are
inverse.
+ */
+class StringDatatype: public Datatype
+{
+ public:
+ StringDatatype()
+ {
+ }
+ virtual ~StringDatatype()
+ {
+ }
+
+ /*
+ * StringDatatype::evaluate(value)
+ *
+ * Evaluate a value against this datatype. This method returns a
+ * string containing the evaluated representation of the 'value'
+ * argument.
+ */
+ virtual string
+ evaluate(const Value &value) const
+ {
+ util::BitBuffer bits = value.get_bitbuffer();
+ return string((char *)bits.get(), bits.size_bytes());
+ }
+
+ /*
+ * StringDatatype::lookup(value)
+ *
+ * Lookup the value of a (potentially valid) evaluation for this
+ * datatype. For a string type, this is a no-op.
+ */
+ virtual Value
+ lookup(const Value &value) const
+ {
+ return value;
+ }
+ virtual Value
+ lookup(const string &str) const
+ {
+ util::BitBuffer bits(BYTES_TO_BITS(str.size()),
+ (uint8_t *)str.data());
+ return Value(bits);
+ }
+
+};
+typedef boost::shared_ptr<StringDatatype> StringDatatypePtr;
+
+#define new_pp_string_datatype(...) \
+ ::pp::StringDatatypePtr(new ::pp::StringDatatype(__VA_ARGS__))
+
+/*
+ * IntDatatype - datatype for signed integer values.
+ *
+ * Notes:
+ * This class makes a private copy of the 'units' argument.
+ */
+class IntDatatype: public Datatype
+{
+ public:
+ explicit IntDatatype(const string &units = "")
+ : m_units(units)
+ {
+ }
+ virtual ~IntDatatype()
+ {
+ }
+
+ /*
+ * IntDatatype::evaluate(value)
+ *
+ * Evaluate a value against this datatype. This method returns a
+ * string containing the evaluated representation of the 'value'
+ * argument.
+ */
+ virtual string
+ evaluate(const Value &value) const
+ {
+ string ret = to_string(value);
+ if (!m_units.empty()) {
+ ret += " ";
+ ret += m_units;
+ }
+ return ret;
+ }
+
+ /*
+ * IntDatatype::lookup(value)
+ *
+ * Lookup the value of a (potentially valid) evaluation for this
+ * datatype. For an int type, this is a no-op.
+ */
+ virtual Value
+ lookup(const Value &value) const
+ {
+ return value;
+ }
+ virtual Value
+ lookup(const string &str) const
+ {
+ try {
+ return Value(str);
+ } catch (std::invalid_argument &e) {
+ throw Datatype::InvalidError(str);
+ }
+ }
+
+ protected:
+ string m_units;
+};
+typedef boost::shared_ptr<IntDatatype> IntDatatypePtr;
+
+#define new_pp_int_datatype(...) \
+ ::pp::IntDatatypePtr(new ::pp::IntDatatype(__VA_ARGS__))
+
+/*
+ * HexDatatype - datatype for hexadecimal values.
+ */
+class HexDatatype: public IntDatatype
+{
+ public:
+ explicit HexDatatype(const BitWidth width = BITS0,
+ const string &units = "")
+ : IntDatatype(units), m_width(width)
+ {
+ }
+ virtual ~HexDatatype()
+ {
+ }
+
+ /*
+ * HexDatatype::evaluate(value)
+ *
+ * Evaluate a value against this datatype. This method returns a
+ * string containing the evaluated representation of the 'value'
+ * argument.
+ */
+ virtual string
+ evaluate(const Value &value) const
+ {
+ string fmt = "0x%0" + to_string(m_width/4) + "x";
+ string ret = sprintfxx(fmt, value);
+ if (!m_units.empty()) {
+ ret += " ";
+ ret += m_units;
+ }
+ return ret;
+ }
+
+ private:
+ BitWidth m_width;
+};
+typedef boost::shared_ptr<HexDatatype> HexDatatypePtr;
+
+#define new_pp_hex_datatype(...) \
+ ::pp::HexDatatypePtr(new ::pp::HexDatatype(__VA_ARGS__))
+
+/*
+ * TransformDatatype - datatype wrapper to perform a transform on data
+ * before passing off to another datatype.
+ */
+template<typename Tdefunc, typename Tenfunc>
+class TransformDatatype: public Datatype
+{
+ private:
+ ConstDatatypePtr m_real_type;
+ Tdefunc m_decode_func;
+ Tenfunc m_encode_func;
+
+ public:
+ TransformDatatype(const ConstDatatypePtr &real_type,
+ const Tdefunc &decode_func, const Tenfunc &encode_func)
+ : m_real_type(real_type), m_decode_func(decode_func),
+ m_encode_func(encode_func)
+ {
+ }
+ virtual ~TransformDatatype()
+ {
+ }
+
+ /*
+ * TransformDatatype::evaluate(value)
+ *
+ * Evaluate a value against this datatype. This method returns a
+ * string containing the evaluated representation of the 'value'
+ * argument.
+ */
+ virtual string
+ evaluate(const Value &raw) const
+ {
+ Value cooked = m_decode_func(raw);
+ return m_real_type->evaluate(cooked);
+ }
+
+ /*
+ * TransformDatatype::lookup(value)
+ *
+ * Lookup the value of a (potentially valid) evaluation for this
+ * datatype.
+ */
+ virtual Value
+ lookup(const Value &cooked) const
+ {
+ Value tmp = m_real_type->lookup(cooked);
+ return m_encode_func(tmp);
+ }
+ virtual Value
+ lookup(const string &cooked) const
+ {
+ Value tmp = m_real_type->lookup(cooked);
+ return m_encode_func(tmp);
+ }
+};
+typedef boost::shared_ptr<Datatype> TransformDatatypePtr;
+
+// This has to be a template, because template argument inference only
+// happens on functions, not classes.
+template<typename Tdefunc, typename Tenfunc>
+TransformDatatypePtr
+new_pp_transform_datatype(const ConstDatatypePtr &real_type,
+ const Tdefunc &decode_func, const Tenfunc &encode_func)
+{
+ return TransformDatatypePtr(
+ new ::pp::TransformDatatype<Tdefunc, Tenfunc>(real_type,
+ decode_func, encode_func));
+}
+
+/*
+ * FixedDatatype - datatype for fixed-point values.
+ *
+ * Notes:
+ * This class makes a private copy of the 'units' argument.
+ */
+class FixedDatatype: public Datatype
+{
+ public:
+ explicit FixedDatatype(int nbits, const string &units = "")
+ : m_nbits(nbits), m_units(units)
+ {}
+ virtual ~FixedDatatype()
+ {}
+
+ /*
+ * FixedDatatype::evaluate(value)
+ *
+ * Evaluate a value against this datatype. This method returns a
+ * string containing the evaluated representation of the 'value'
+ * argument.
+ */
+ virtual string
+ evaluate(const Value &value) const
+ {
+ Value numer = value & ((Value(1) << m_nbits) - 1);
+ Value denom = Value::pow(2, m_nbits);
+ Value scalar = Value::pow(10, m_nbits);
+
+ Value decimal = (numer * scalar) / denom;
+ Value integral = value >> m_nbits;
+
+ // right align things like 0.500 to 0.5
+ while (decimal > 0 && (decimal % 10) == 0) {
+ decimal /= 10;
+ }
+ string ret = sprintfxx("%d.%d", integral, decimal);
+ if (!m_units.empty()) {
+ ret += " ";
+ ret += m_units;
+ }
+ return ret;
+ }
+
+ /*
+ * FixedDatatype::lookup(value)
+ *
+ * Lookup the value of a (potentially valid) evaluation for this
+ * datatype. For an int type, this is a no-op.
+ */
+ virtual Value
+ lookup(const Value &value) const
+ {
+ return value;
+ }
+ virtual Value
+ lookup(const string &str) const
+ {
+ try {
+ //FIXME:
+ return Value(str);
+ } catch (std::invalid_argument &e) {
+ throw Datatype::InvalidError(str);
+ }
+ }
+
+ protected:
+ int m_nbits;
+ string m_units;
+};
+typedef boost::shared_ptr<FixedDatatype> FixedDatatypePtr;
+
+#define new_pp_fixed_datatype(...) \
+ ::pp::FixedDatatypePtr(new ::pp::FixedDatatype(__VA_ARGS__))
+
+} // namespace pp
+
+#endif // PP_DATATYPES_H__
=======================================
--- /dev/null
+++ /trunk/dirent.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,117 @@
+/* Copyright (c) Tim Hockin, 2007 */
+#ifndef PP_DIRENT_H__
+#define PP_DIRENT_H__
+
+#include "pp/pp.h"
+#include <ostream>
+
+namespace pp {
+
+// The specific type of a dirent.
+typedef enum {
+ DIRENT_TYPE_REGISTER,
+ DIRENT_TYPE_FIELD,
+ DIRENT_TYPE_SCOPE,
+ DIRENT_TYPE_ARRAY,
+ DIRENT_TYPE_ALIAS,
+ DIRENT_TYPE_MAX,
+} DirentType;
+
+inline std::ostream &
+operator<<(std::ostream& o, const DirentType &type)
+{
+ if (type >= DIRENT_TYPE_MAX) {
+ throw std::out_of_range(
+ "invalid DirentType: " + to_string((int)type));
+ }
+
+ // Note: this is indexed by DirentType above.
+ static const char *dirent_type_strings[] = {
+ "Register",
+ "Field",
+ "Scope",
+ "Array",
+ "Alias",
+ };
+
+ return o << dirent_type_strings[type];
+}
+
+/*
+ * Dirent - a directory entry in a scope
+ */
+class Dirent
+{
+ public:
+ // a dirent conversion error
+ struct ConversionError: public std::runtime_error
+ {
+ explicit ConversionError(const string &str)
+ : runtime_error(str)
+ {
+ }
+ };
+
+ explicit Dirent(DirentType type): m_type(type)
+ {
+ DASSERT_MSG(type < DIRENT_TYPE_MAX, "invalid DirentType");
+ DTRACE(TRACE_DIRENTS && TRACE_LIFETIMES,
+ sprintfxx("new dirent @ %p", this));
+ }
+ virtual ~Dirent()
+ {
+ DTRACE(TRACE_DIRENTS && TRACE_LIFETIMES,
+ sprintfxx("del dirent @ %p", this));
+ }
+
+ /*
+ * Dirent::dirent_type()
+ *
+ * Get the DirentType of this object.
+ */
+ DirentType
+ dirent_type() const
+ {
+ return m_type;
+ }
+
+ /*
+ * Dirent::is_*()
+ *
+ * Check the type of this object.
+ */
+ bool
+ is_register() const
+ {
+ return (m_type == DIRENT_TYPE_REGISTER);
+ }
+ bool
+ is_field() const
+ {
+ return (m_type == DIRENT_TYPE_FIELD);
+ }
+ bool
+ is_scope() const
+ {
+ return (m_type == DIRENT_TYPE_SCOPE);
+ }
+ bool
+ is_array() const
+ {
+ return (m_type == DIRENT_TYPE_ARRAY);
+ }
+ bool
+ is_alias() const
+ {
+ return (m_type == DIRENT_TYPE_ALIAS);
+ }
+
+ private:
+ DirentType m_type;
+};
+typedef boost::shared_ptr<Dirent> DirentPtr;
+typedef boost::shared_ptr<const Dirent> ConstDirentPtr;
+
+} // namespace pp
+
+#endif // PP_DIRENT_H__
=======================================
--- /dev/null
+++ /trunk/driver.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,111 @@
+/* Copyright (c) Tim Hockin, 2007 */
+#ifndef PP_DRIVER_H__
+#define PP_DRIVER_H__
+
+#include "pp/pp.h"
+#include "pp/binding.h"
+#include "pp/scope.h"
+
+namespace pp {
+
+//
+// Driver - abstract base class for driver plugins.
+//
+class Driver
+{
+ public:
+ struct IoError: public std::runtime_error
+ {
+ explicit IoError(const string &str)
+ : runtime_error(str)
+ {
+ }
+ };
+ struct ArgsError: public std::runtime_error
+ {
+ explicit ArgsError(const string &str)
+ : runtime_error(str)
+ {
+ }
+ };
+ struct NotSupportedError: public std::runtime_error
+ {
+ explicit NotSupportedError(const string &str)
+ : runtime_error(str)
+ {
+ }
+ };
+
+ // ctor and dtor
+ Driver() {}
+ virtual ~Driver() {}
+
+ //
+ // Driver::name()
+ //
+ // Get the name of this driver.
+ //
+ virtual string
+ name() const = 0;
+
+ //
+ // Driver::new_binding(args)
+ //
+ // Create a new Binding.
+ //
+ // Throws: Driver::ArgsError
+ //
+ virtual BindingPtr
+ new_binding(const std::vector<Value> &args) const
+ {
+ (void)args;
+ throw NotSupportedError(this->name() + ": " +
+ "binding not supported for this driver");
+ }
+
+ //
+ // Driver::discover()
+ //
+ // Enumerate devices owned by this driver, and add them to the
+ // platform.
+ //
+ virtual void
+ discover() const
+ {
+ // do nothing by default
+ }
+
+ //
+ // Driver::DiscoveryCallback
+ //
+ // This is the functional signature of a driver discovery event.
+ // When a driver's discover() routine finds a device that matches a
+ // registered device (via register_discovery()), the
+ // DiscoveryCallback is called.
+ //
+ typedef void (*DiscoveryCallback)(const std::vector<Value> &args);
+
+ //
+ // Driver::register_discovery(args, function)
+ //
+ // Register a DiscoveryCallback which will be called when the
+ // driver's discover() routine finds a device that matches args.
+ // The contents af the args vector depends on the specific driver.
+ // If the callback_function is NULL, the driver should ignore any
+ // devices that match args.
+ //
+ virtual void
+ register_discovery(const std::vector<Value> &args,
+ DiscoveryCallback function)
+ {
+ (void)args;
+ (void)function;
+ throw NotSupportedError(this->name() + ": " +
+ "discovery not supported for this driver");
+ }
+};
+typedef boost::shared_ptr<Driver> DriverPtr;
+
+} // namespace pp
+
+#endif // PP_DRIVER_H__
=======================================
--- /dev/null
+++ /trunk/field.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,144 @@
+/* Copyright (c) Tim Hockin, 2007 */
+#ifndef PP_FIELD_H__
+#define PP_FIELD_H__
+
+#include "pp/pp.h"
+#include "pp/dirent.h"
+#include "pp/datatype.h"
+#include <stdexcept>
+
+namespace pp {
+
+/*
+ * Field - abstract base class for a data field.
+ */
+class Field: public Dirent
+{
+ public:
+ explicit Field(const ConstDatatypePtr &datatype)
+ : Dirent(DIRENT_TYPE_FIELD), m_datatype(datatype) {}
+ virtual ~Field() {}
+
+ /*
+ * Field::describe()
+ *
+ * Get a string which describes this field
+ */
+ virtual string
+ describe() const = 0;
+
+ /*
+ * Field::read()
+ *
+ * Read the current value of this field.
+ */
+ virtual Value
+ read() const = 0;
+
+ /*
+ * Field::write(value)
+ *
+ * Write a value to this field.
+ */
+ virtual void
+ write(const Value &value) const = 0;
+
+ /*
+ * Field::evaluate()
+ *
+ * Evaluate the value of this field against it's datatype. This
+ * method returns a string containing the evaluated representation
+ * of the field.
+ */
+ virtual string
+ evaluate() const
+ {
+ Value value = read();
+ return m_datatype->evaluate(value);
+ }
+
+ /*
+ * Field::lookup(str)
+ * Field::lookup(value)
+ *
+ * Lookup the numeric value of a (potentially valid) evaluation for
+ * this field.
+ *
+ * This can throw Datatype::InvalidError.
+ */
+ virtual Value
+ lookup(const string &str) const
+ {
+ return m_datatype->lookup(str);
+ }
+ virtual Value
+ lookup(const Value &value) const
+ {
+ return m_datatype->lookup(value);
+ }
+
+ /*
+ * Field::compare(str)
+ * Field::compare(value)
+ *
+ * Compare a field against another argument, according to the
+ * rules of this datatype. Return an integer which indicates
+ * whether the lhs is less than, equal to, or greater than the rhs
+ * (negative, zero, positive return code, respectively).
+ *
+ * This can throw Datatype::InvalidError.
+ */
+ virtual int
+ compare(const string &comparator) const
+ {
+ return m_datatype->compare(read(), comparator);
+ }
+ virtual int
+ compare(const Value &comparator) const
+ {
+ return m_datatype->compare(read(), comparator);
+ }
+
+ /*
+ * Field::test(str)
+ * Field::test(value)
+ *
+ * Perform a logical AND or a field and an argument.
+ */
+ virtual Value
+ test(const string &comparator) const
+ {
+ return m_datatype->test(read(), comparator);
+ }
+ virtual Value
+ test(const Value &comparator) const
+ {
+ return m_datatype->test(read(), comparator);
+ }
+
+ private:
+ ConstDatatypePtr m_datatype;
+};
+typedef boost::shared_ptr<Field> FieldPtr;
+typedef boost::shared_ptr<const Field> ConstFieldPtr;
+
+// const
+inline ConstFieldPtr
+field_from_dirent(const ConstDirentPtr &dirent)
+{
+ if (dirent->is_field()) {
+ return static_pointer_cast<const Field>(dirent);
+ }
+ throw Dirent::ConversionError("non-field dirent used as field");
+}
+// non-const
+inline FieldPtr
+field_from_dirent(const DirentPtr &dirent)
+{
+ const ConstDirentPtr &const_dirent = dirent;
+ return const_pointer_cast<Field>(field_from_dirent(const_dirent));
+}
+
+} // namespace pp
+
+#endif // PP_FIELD_H__
=======================================
--- /dev/null
+++ /trunk/field_types.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,199 @@
+/* Copyright (c) Tim Hockin, 2007 */
+#ifndef PP_FIELDS_H__
+#define PP_FIELDS_H__
+
+#include "pp/field.h"
+#include "pp/datatype.h"
+#include "pp/register.h"
+#include "pp/regbits.h"
+#include "pp/rwprocs.h"
+#include "pp/context.h"
+#include "runtime.h"
+#include <vector>
+
+namespace pp {
+
+/*
+ * DirectField - a field that maps directly to register bits.
+ */
+class DirectField: public Field
+{
+ public:
+ DirectField(const ConstDatatypePtr &datatype, const RegBits ®bits)
+ : Field(datatype), m_regbits(regbits)
+ {
+ }
+ virtual ~DirectField()
+ {
+ }
+
+ /*
+ * DirectField::describe()
+ */
+ virtual string
+ describe() const
+ {
+ return m_regbits.describe();
+ }
+
+ /*
+ * DirectField::read()
+ *
+ * Read the current value of this field.
+ *
+ * Throws: Driver::IoError
+ */
+ virtual Value
+ read() const
+ {
+ return m_regbits.read();
+ }
+
+ /*
+ * DirectField::write(value)
+ *
+ * Write a value to this field.
+ *
+ * Throws: Driver::IoError
+ */
+ virtual void
+ write(const Value &value) const
+ {
+ m_regbits.write(value);
+ }
+
+ private:
+ RegBits m_regbits;
+};
+typedef boost::shared_ptr<DirectField> DirectFieldPtr;
+
+#define new_pp_direct_field(...) \
+ ::pp::DirectFieldPtr(new ::pp::DirectField(__VA_ARGS__))
+
+/*
+ * ProcField - a field that is a procedure.
+ *
+ * Notes:
+ */
+class ProcField: public Field
+{
+ public:
+ ProcField(const ConstDatatypePtr &datatype,
+ const RwProcsPtr &access)
+ : Field(datatype), m_access(access),
+ m_context(pp::runtime::context_snapshot())
+ {
+ }
+ virtual ~ProcField()
+ {
+ }
+
+ /*
+ * ProcField::describe()
+ */
+ virtual string
+ describe() const
+ {
+ return "procedures";
+ }
+
+ /*
+ * ProcField::read()
+ *
+ * Read the current value of this field.
+ *
+ * Throws: Driver::IoError
+ */
+ virtual Value
+ read() const
+ {
+ pp::runtime::context_push(m_context);
+ Value ret = m_access->read();
+ pp::runtime::context_pop();
+ return ret;
+ }
+
+ /*
+ * ProcField::write(value)
+ *
+ * Write a value to this field.
+ *
+ * Throws: Driver::IoError
+ */
+ virtual void
+ write(const Value &value) const
+ {
+ pp::runtime::context_push(m_context);
+ m_access->write(value);
+ pp::runtime::context_pop();
+ }
+
+ private:
+ RwProcsPtr m_access;
+ ContextPtr m_context;
+};
+typedef boost::shared_ptr<ProcField> ProcFieldPtr;
+
+#define new_pp_proc_field(...) \
+ ::pp::ProcFieldPtr(new ::pp::ProcField(__VA_ARGS__))
+
+/*
+ * ConstantField - a field that returns a constant value.
+ */
+class ConstantField: public Field
+{
+ public:
+ ConstantField(const ConstDatatypePtr &datatype,
+ const Value &value)
+ : Field(datatype), m_value(value)
+ {
+ }
+ virtual ~ConstantField()
+ {
+ }
+
+ /*
+ * ConstantField::describe()
+ */
+ virtual string
+ describe() const
+ {
+ return "constant";
+ }
+
+ /*
+ * ConstantField::read()
+ *
+ * Read the current value of this field.
+ */
+ virtual Value
+ read() const
+ {
+ return m_value;
+ }
+
+ /*
+ * ConstantField::write(value)
+ *
+ * Write a value to this field.
+ *
+ * Throws: Driver::IoError
+ */
+ virtual void
+ write(const Value &value) const
+ {
+ (void)value;
+ // discard writes
+ }
+
+ private:
+ Value m_value;
+};
+typedef boost::shared_ptr<ConstantField> ConstantFieldPtr;
+
+#define new_pp_constant_field(...) \
+ ::pp::ConstantFieldPtr(new ::pp::ConstantField(__VA_ARGS__))
+
+} // namespace pp
+
+#endif // PP_FIELDS_H__
=======================================
--- /dev/null
+++ /trunk/path.cpp Sun Nov 8 23:26:22 2009
@@ -0,0 +1,324 @@
+// Copyright 2008 Google Inc. All Rights Reserved.
+
+#include "pp/pp.h"
+#include "pp/path.h"
+#include <stdexcept>
+#include <vector>
+#include <boost/algorithm/string.hpp>
+#include <ctype.h>
+
+namespace pp {
+
+//
+// Path::Element
+//
+
+string
+Path::Element::to_string() const
+{
+ string ret;
+ if (is_array()) {
+ ret = name() + "[";
+ if (m_array_mode == ARRAY_INDEX) {
+ ret += ::to_string(m_array_index);
+ }
+ ret += "]";
+ } else if (is_bookmark()) {
+ ret = "$" + name();
+ } else {
+ ret = name();
+ }
+ return ret;
+}
+
+bool
+Path::Element::equals(const Element &other) const
+{
+ return (to_string() == other.to_string());
+}
+
+const string &
+Path::Element::name() const
+{
+ return m_name;
+}
+
+bool
+Path::Element::is_array() const
+{
+ return (m_array_mode != ARRAY_NONE);
+}
+
+enum Path::Element::array_mode
+Path::Element::array_mode() const
+{
+ return m_array_mode;
+}
+
+int
+Path::Element::array_index() const
+{
+ return m_array_index;
+}
+
+bool
+Path::Element::is_bookmark() const
+{
+ return m_is_bookmark;
+}
+
+void
+Path::Element::parse(const string &input)
+{
+ enum {
+ ST_START,
+ ST_GOT_LEADING_SPECIAL,
+ ST_BODY,
+ ST_GOT_LEADING_DOT,
+ ST_GOT_ARRAY_OPEN,
+ ST_GOT_ARRAY_INDEX_NEGATIVE,
+ ST_DETECT_ARRAY_INDEX_BASE,
+ ST_ARRAY_INDEX,
+ ST_DONE,
+ } state = ST_START;
+ int idx_base = 10;
+ int idx_sign = 1;
+
+ for (size_t i = 0; i < input.size(); i++) {
+ char c = input[i];
+
+ switch (state) {
+ case ST_START:
+ if (c == '%') {
+ m_name += c;
+ state = ST_GOT_LEADING_SPECIAL;
+ } else if (c == '$') {
+ m_is_bookmark = true;
+ state = ST_GOT_LEADING_SPECIAL;
+ } else if (isalpha(c) || c == '_') {
+ m_name += c;
+ state = ST_BODY;
+ } else if (c == '.') {
+ state = ST_GOT_LEADING_DOT;
+ m_name += c;
+ } else {
+ parse_error(input);
+ return;
+ }
+ break;
+ case ST_GOT_LEADING_SPECIAL:
+ if (isalpha(c) || c == '_') {
+ m_name += c;
+ state = ST_BODY;
+ } else {
+ parse_error(input);
+ return;
+ }
+ break;
+ case ST_BODY:
+ if (isalnum(c) || c == '_' || c == '.') {
+ m_name += c;
+ state = ST_BODY;
+ } else if (c == '[') {
+ state = ST_GOT_ARRAY_OPEN;
+ } else {
+ parse_error(input);
+ return;
+ }
+ break;
+ case ST_GOT_LEADING_DOT:
+ if (c == '.') {
+ m_name += c;
+ state = ST_DONE;
+ } else {
+ parse_error(input);
+ return;
+ }
+ break;
+ case ST_GOT_ARRAY_OPEN:
+ if (c == ']') {
+ m_array_mode = ARRAY_APPEND;
+ state = ST_DONE;
+ } else if (c == '-') {
+ idx_sign = -1;
+ m_array_mode = ARRAY_INDEX;
+ state = ST_GOT_ARRAY_INDEX_NEGATIVE;
+ } else if (c == '0') {
+ m_array_mode = ARRAY_INDEX;
+ m_array_index = 0;
+ state = ST_DETECT_ARRAY_INDEX_BASE;
+ } else if (isdigit(c)) {
+ idx_base = 10;
+ m_array_mode = ARRAY_INDEX;
+ m_array_index = c - '0';
+ state = ST_ARRAY_INDEX;
+ } else {
+ parse_error(input);
+ return;
+ }
+ break;
+ case ST_GOT_ARRAY_INDEX_NEGATIVE:
+ if (c == '0') {
+ m_array_index = 0;
+ state = ST_DETECT_ARRAY_INDEX_BASE;
+ } else if (isdigit(c)) {
+ idx_base = 10;
+ m_array_index = c - '0';
+ state = ST_ARRAY_INDEX;
+ } else {
+ parse_error(input);
+ return;
+ }
+ break;
+ case ST_DETECT_ARRAY_INDEX_BASE:
+ if (tolower(c) == 'x') {
+ idx_base = 16;
+ state = ST_ARRAY_INDEX;
+ } else if (isdigit(c) && c <= '7') {
+ idx_base = 8;
+ m_array_index += c - '0';
+ state = ST_ARRAY_INDEX;
+ } else if (c == ']') {
+ idx_base = 10;
+ m_array_index *= idx_sign;
+ state = ST_DONE;
+ } else {
+ parse_error(input);
+ return;
+ }
+ break;
+ case ST_ARRAY_INDEX:
+ if (idx_base == 10 && isdigit(c)) {
+ m_array_index *= idx_base;
+ m_array_index += c - '0';
+ state = ST_ARRAY_INDEX;
+ } else if (idx_base == 8 && isdigit(c) && c <= '7') {
+ m_array_index *= idx_base;
+ m_array_index += c - '0';
+ state = ST_ARRAY_INDEX;
+ } else if (idx_base == 16 && isxdigit(c)) {
+ m_array_index *= idx_base;
+ if (c >= '0' && c <= '9') {
+ m_array_index += c - '0';
+ } else {
+ m_array_index += tolower(c) - 'a';
+ }
+ state = ST_ARRAY_INDEX;
+ } else if (c == ']') {
+ m_array_index *= idx_sign;
+ state = ST_DONE;
+ } else {
+ parse_error(input);
+ return;
+ }
+ break;
+ case ST_DONE:
+ parse_error(input);
+ return;
+ }
+ }
+
+ if (m_name == "") {
+ parse_error(input);
+ }
+}
+
+void
+Path::Element::parse_error(const string &name)
+{
+ throw Path::InvalidError("invalid path element '" + name + "'");
+}
+
+
+//
+// Path
+//
+
+string
+Path::to_string() const
+{
+ string result;
+
+ if (is_absolute()) {
+ result += "/";
+ }
+
+ Path::const_iterator it = begin();
+ while (it != end()) {
+ result += it->to_string();
+ it++;
+ if (it != end()) {
+ result += "/";
+ }
+ }
+
+ return result;
+}
+
+bool
+Path::equals(const Path &other) const
+{
+ // if they are different lengths, they cannot be equal
+ if (size() != other.size()) {
+ return false;
+ }
+ // if only one is absolute, they cannot be equal
+ if (is_absolute() != other.is_absolute()) {
+ return false;
+ }
+
+ // Iterate through both lists at the same rate comparing
+ // each element.
+ const_iterator my_iter = begin();
+ const_iterator your_iter = other.begin();
+
+ while (my_iter != end()) {
+ // if any elements are non-equal, return false
+ if (!my_iter->equals(*your_iter)) {
+ return false;
+ }
+ my_iter++;
+ your_iter++;
+ }
+
+ return true;
+}
+
+void
+Path::append(const string &str)
+{
+ // discard leading and trailing whitespace
+ string my_str = boost::algorithm::trim_copy(str);
+
+ // special case for ""
+ if (my_str.size() == 0) {
+ return;
+ }
+
+ std::vector<string> parts;
+
+ // Note: this function will self-correct excess delimiters.
+ // For example: given "/red/orange/yellow/", it does not
+ // create an empty part after the final '/'. Given
+ // the path "//red//orange", it will compact the duplicate
+ // delimiters.
+ boost::split(parts, my_str, boost::is_any_of("/"),
+ boost::token_compress_on);
+
+ // Determine if the path is relative or absolute by the first
+ // element of the vector. If it is an empty string, then the
+ // path is absolute.
+ if (parts.size() > 0 && parts[0].length() == 0) {
+ m_absolute = true;
+ }
+
+ // Add each non-empty part to the list (leading and trailing
+ // delimiters yield blank parts) as a Path::Element.
+ for (size_t i = 0; i < parts.size(); i++) {
+ if (parts[i].length() != 0) {
+ m_list.push_back(Element(parts[i]));
+ }
+ }
+}
+
+} // namespace pp
=======================================
--- /dev/null
+++ /trunk/path.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,594 @@
+// Copyright 2007 Google Inc. All Rights Reserved.
+
+#ifndef PP_PATH_HPP__
+#define PP_PATH_HPP__
+
+#include "pp/pp.h"
+#include <list>
+#include <stdexcept>
+#include <ostream>
+#include <boost/iterator_adaptors.hpp>
+
+namespace pp {
+
+//
+// This template class is a thin wrapper to make iterators work for
+// Path objects. This is largely based on the boost example
+// code for boost::iterator_facade.
+//
+template<typename Titer, typename Tval>
+class PathIterator
+ : public boost::iterator_facade<PathIterator<Titer, Tval>, Tval,
+ typename std::iterator_traits<Titer>::iterator_category>
+{
+ friend class boost::iterator_core_access;
+ template<class,class> friend class PathIterator;
+
+ public:
+ // default constructor
+ PathIterator() {}
+
+ // implicit conversion from the underlying iterator
+ PathIterator(Titer it): m_it(it) {}
+
+ // implicit conversion from non-const to const_iterator
+ template<class Tother>
+ PathIterator(const PathIterator<Titer, Tother> &other)
+ : m_it(other.m_it), m_trap(other.m_trap) {}
+
+ // get the underlying iterator
+ const Titer&
+ get() const
+ {
+ return m_it;
+ }
+
+ private:
+ // check for equality
+ template<typename Tthat>
+ bool
+ equal(const PathIterator<Titer, Tthat> &that) const
+ {
+ return(this->m_it == that.m_it);
+ }
+
+ // move the iterator forward by one
+ void
+ increment()
+ {
+ ++m_it;
+ }
+
+ // move the iterator backward by one
+ void
+ decrement()
+ {
+ --m_it;
+ }
+
+ // move the iterator forward or backward by n
+ void
+ advance(const std::ptrdiff_t n)
+ {
+ m_it += n;
+ }
+
+ // figure out the distance to another iterator
+ template<typename Tthere>
+ std::ptrdiff_t
+ distance_to(const PathIterator<Titer, Tthere> &there) const
+ {
+ return there.m_it - this->m_it;
+ }
+
+ // get at the referent
+ Tval &
+ dereference() const
+ {
+ return *m_it;
+ }
+
+ private:
+ // the actual underlying iterator
+ Titer m_it;
+
+ // a trap to catch const_iterator to iterator assignment
+ Tval *m_trap;
+};
+
+class Path
+{
+ public:
+ // a single path element
+ class Element
+ {
+ public:
+ enum array_mode {
+ ARRAY_NONE = 0,
+ ARRAY_INDEX,
+ ARRAY_APPEND,
+ };
+
+ private:
+ string m_name;
+ enum array_mode m_array_mode;
+ int m_array_index;
+ bool m_is_bookmark;
+
+ public:
+ // explicit ctor from string
+ // throws:
+ // Path::InvalidError
+ explicit
+ Element(const string &str)
+ : m_name(), m_array_mode(ARRAY_NONE), m_array_index(0),
+ m_is_bookmark(false)
+ {
+ parse(str);
+ }
+ explicit
+ Element(const char *str)
+ : m_name(), m_array_mode(ARRAY_NONE), m_array_index(0),
+ m_is_bookmark(false)
+ {
+ parse(str);
+ }
+
+ string
+ to_string() const;
+
+ bool
+ equals(const Element &other) const;
+
+ const string &
+ name() const;
+
+ bool
+ is_array() const;
+
+ enum array_mode
+ array_mode() const;
+
+ int
+ array_index() const;
+
+ bool
+ is_bookmark() const;
+
+ private:
+ void
+ parse(const string &input);
+
+ void
+ parse_error(const string &name);
+ };
+
+ // a path not found error
+ struct NotFoundError: public std::runtime_error
+ {
+ explicit NotFoundError(const std::string &str)
+ : runtime_error(str)
+ {
+ }
+ };
+
+ // an invalid path error
+ struct InvalidError: public std::runtime_error
+ {
+ explicit InvalidError(const std::string &str)
+ : runtime_error(str)
+ {
+ }
+ };
+
+ private:
+ typedef std::list<Element> Tlist;
+ typedef Tlist::iterator Titer;
+
+ // member variables
+ Tlist m_list;
+ bool m_absolute;
+
+ public:
+ typedef PathIterator<Titer, Element> iterator;
+ typedef PathIterator<Titer, const Element> const_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ // default constructor
+ Path()
+ : m_list(), m_absolute(false)
+ {
+ }
+ // implicit conversion from string
+ // throws:
+ // Path::InvalidError
+ Path(const string &path)
+ : m_list(), m_absolute(false)
+ {
+ append(path);
+ }
+ // implicit conversion from char* (for string-literal conversion)
+ // throws:
+ // Path::InvalidError
+ Path(const char *path)
+ : m_list(), m_absolute(false)
+ {
+ append(path);
+ }
+ // explicit conversion from Path::Element
+ explicit
+ Path(const Element &elem)
+ : m_list(), m_absolute(false)
+ {
+ m_list.push_back(elem);
+ }
+ // explicit conversion from 2 Path::Elements
+ Path(const Element &elem1, const Element &elem2)
+ : m_list(), m_absolute(false)
+ {
+ m_list.push_back(elem1);
+ m_list.push_back(elem2);
+ }
+ // copy constructor
+ Path(const Path &that)
+ : m_list(that.m_list), m_absolute(that.m_absolute)
+ {
+ }
+
+ // destructor
+ ~Path()
+ {
+ }
+
+ // iterator functionality
+ iterator
+ begin()
+ {
+ return m_list.begin();
+ }
+ iterator
+ end()
+ {
+ return m_list.end();
+ }
+ const_iterator
+ begin() const
+ {
+ Path *p = const_cast<Path *>(this);
+ return p->begin();
+ }
+ const_iterator
+ end() const
+ {
+ Path *p = const_cast<Path *>(this);
+ return p->end();
+ }
+
+ reverse_iterator
+ rbegin()
+ {
+ return m_list.rbegin();
+ }
+ reverse_iterator
+ rend()
+ {
+ return m_list.rend();
+ }
+ const_reverse_iterator
+ rbegin() const
+ {
+ Path *p = const_cast<Path *>(this);
+ return p->rbegin();
+ }
+ const_reverse_iterator
+ rend() const
+ {
+ Path *p = const_cast<Path *>(this);
+ return p->rend();
+ }
+
+ // get the number of elements
+ Tlist::size_type
+ size() const
+ {
+ return m_list.size();
+ }
+
+ // check if the path is initialized (i.e. holds a valid path)
+ bool
+ is_initialized() const
+ {
+ return (m_absolute || !m_list.empty());
+ }
+
+ // access the first/last values
+ // throws:
+ // std::out_of_range
+ Element &
+ front()
+ {
+ if (m_list.empty()) {
+ throw std::out_of_range("path is empty");
+ }
+ return m_list.front();
+ }
+ Element &
+ back()
+ {
+ if (m_list.empty()) {
+ throw std::out_of_range("path is empty");
+ }
+ return m_list.back();
+ }
+ const Element &
+ front() const
+ {
+ if (m_list.empty()) {
+ throw std::out_of_range("path is empty");
+ }
+ return m_list.front();
+ }
+ const Element &
+ back() const
+ {
+ if (m_list.empty()) {
+ throw std::out_of_range("path is empty");
+ }
+ return m_list.back();
+ }
+
+ //
+ // NOTE: all of the operations which modify the internal list are
+ // ignorant of whether a path is absolute or not. The "correct"
+ // behavior is not clear, so it is left up to the caller.
+ //
+
+ // add elements onto a path
+ void
+ push_back(const Path &path)
+ {
+ const_iterator it = path.begin();
+ while (it != path.end()) {
+ m_list.push_back(*it);
+ it++;
+ }
+ }
+ void
+ push_back(const Element &elem)
+ {
+ m_list.push_back(elem);
+ }
+
+ // remove items from the front or back
+ // throws:
+ // std::out_of_range
+ Element
+ pop_front()
+ {
+ Element old_front = front();
+ m_list.pop_front();
+ return old_front;
+ }
+ Element
+ pop_back()
+ {
+ Element old_back = back();
+ m_list.pop_back();
+ return old_back;
+ }
+
+ // insert one or more elements
+ void
+ insert(iterator pos, const Element &elem)
+ {
+ m_list.insert(pos.get(), elem);
+ }
+ void
+ insert(iterator pos, iterator first, iterator last)
+ {
+ m_list.insert(pos.get(), first.get(), last.get());
+ }
+
+ // erase one or more elements
+ iterator
+ erase(iterator pos)
+ {
+ return m_list.erase(pos.get());
+ }
+ iterator
+ erase(iterator first, iterator last)
+ {
+ return m_list.erase(first.get(), last.get());
+ }
+
+ // splice in a second path
+ void
+ splice(iterator pos, const Path &path)
+ {
+ Path tmp(path);
+ m_list.splice(pos.get(), tmp.m_list);
+ }
+
+ // reset everything
+ void
+ clear()
+ {
+ m_absolute = false;
+ m_list.clear();
+ }
+
+ // handle absolute vs. relative paths
+ bool
+ is_absolute() const
+ {
+ return m_absolute;
+ }
+ bool
+ is_relative() const
+ {
+ return !m_absolute;
+ }
+ void
+ set_absolute(bool val)
+ {
+ m_absolute = val;
+ }
+
+ string
+ to_string() const;
+
+ bool
+ equals(const Path &other) const;
+
+ private:
+ void
+ append(const string &str);
+};
+
+// Stream out a path element.
+inline std::ostream &
+operator<<(std::ostream& o, const Path::Element &element)
+{
+ return o << element.to_string();
+}
+
+// Allow comparison of path elements.
+inline bool
+operator==(const Path::Element &left, const Path::Element &right)
+{
+ return left.equals(right);
+}
+inline bool
+operator!=(const Path::Element &left, const Path::Element &right)
+{
+ return !(left == right);
+}
+// Allow comparison between path elements and strings, without converting
+// the strings to elements.
+inline bool
+operator==(const Path::Element &left, const string &right)
+{
+ return (left.to_string() == right);
+}
+inline bool
+operator==(const string &left, const Path::Element &right)
+{
+ return (right == left);
+}
+inline bool
+operator!=(const Path::Element &left, const string &right)
+{
+ return !(left == right);
+}
+inline bool
+operator!=(const string &left, const Path::Element &right)
+{
+ return !(left == right);
+}
+// These are needed to disambiguate comparisons to string-literals (which
+// could convert to string or Path::Element).
+inline bool
+operator==(const Path::Element &left, const char *right)
+{
+ return (left.to_string() == right);
+}
+inline bool
+operator==(const char *left, const Path::Element &right)
+{
+ return (right == left);
+}
+inline bool
+operator!=(const Path::Element &left, const char *right)
+{
+ return !(left == right);
+}
+inline bool
+operator!=(const char *left, const Path::Element &right)
+{
+ return !(left == right);
+}
+
+// Stream out a path.
+inline std::ostream &
+operator<<(std::ostream& o, const Path &path)
+{
+ return o << path.to_string();
+}
+
+// Allow comparison of paths.
+inline bool
+operator==(const Path &left, const Path &right)
+{
+ return left.equals(right);
+}
+inline bool
+operator!=(const Path &left, const Path &right)
+{
+ return !(left == right);
+}
+// Allow comparison between paths and strings, without converting the
+// strings to paths.
+inline bool
+operator==(const Path &left, const string &right)
+{
+ return to_string(left) == right;
+}
+inline bool
+operator==(const string &left, const Path &right)
+{
+ return (right == left);
+}
+inline bool
+operator!=(const Path &left, const string &right)
+{
+ return !(left == right);
+}
+inline bool
+operator!=(const string &left, const Path &right)
+{
+ return !(left == right);
+}
+// Allow comparison between paths and char* (string literals), without
+// converting the strings to paths. This is needed because there is an
+// implicit ctor for Path from char*, which makes some ambiguous calls.
+inline bool
+operator==(const Path &left, const char *right)
+{
+ return (left == string(right));
+}
+inline bool
+operator==(const char *left, const Path &right)
+{
+ return (right == left);
+}
+inline bool
+operator!=(const Path &left, const char *right)
+{
+ return !(left == right);
+}
+inline bool
+operator!=(const char *left, const Path &right)
+{
+ return !(left == right);
+}
+
+// Allow simple path appending and catenation.
+inline Path &
+operator+=(Path &left, const Path &right)
+{
+ // return the original Path, the arg here is a reference
+ left.push_back(right);
+ return left;
+}
+inline Path
+operator+(Path left, const Path &right)
+{
+ // return a new Path, the arg here is a copy
+ left.push_back(right);
+ return left;
+}
+
+} // namespace pp
+
+#endif // PP_PATH_HPP__
=======================================
--- /dev/null
+++ /trunk/regbits.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,244 @@
+/* Copyright (c) Tim Hockin, 2007 */
+#ifndef PP_REGBITS_H__
+#define PP_REGBITS_H__
+
+#include "pp/pp.h"
+#include "pp/util/printfxx.h"
+#include "pp/register.h"
+
+namespace pp {
+
+/*
+ * RegBits - a bit range from a register.
+ *
+ * Examples:
+ * RegBits A(reg1, 15, 0);
+ * cout << A.read() << endl; // 0x1234
+ *
+ * RegBits B(reg2, 15, 0);
+ * cout << B.read() << endl; // 0x5678
+ *
+ * RegBits C = A + B;
+ * cout << C.read() << endl; // 0x12345678
+ */
+//FIXME: rename to RegisterBits
+class RegBits
+{
+ public:
+ // a bitrange error
+ struct range_error: public std::runtime_error
+ {
+ explicit range_error(const string &str)
+ : runtime_error(str)
+ {
+ }
+ };
+
+ // create an empty regbits
+ RegBits(): m_register(), m_mask(0)
+ {}
+ // create a simple regbits
+ // NOTE: These ctors all take either a const_ptr or a plain _ptr, so
+ // that any register can be implicitly converted to regbits (otherwise
+ // a plain _ptr converts to _const_ptr, but not to regbits).
+ RegBits(const RegisterPtr ®)
+ {
+ // use the full register
+ init(reg, reg ? reg->width()-1 : 0, 0);
+ }
+ RegBits(const ConstRegisterPtr ®)
+ {
+ // use the full register
+ init(reg, reg ? reg->width()-1 : 0, 0);
+ }
+ RegBits(const RegisterPtr ®, unsigned bit)
+ {
+ // use a single bit
+ init(reg, bit, bit);
+ }
+ RegBits(const ConstRegisterPtr ®, unsigned bit)
+ {
+ // use a single bit
+ init(reg, bit, bit);
+ }
+ RegBits(const RegisterPtr ®,
+ unsigned hi_bit, unsigned lo_bit)
+ {
+ // use the specified bits
+ init(reg, hi_bit, lo_bit);
+ }
+ RegBits(const ConstRegisterPtr ®,
+ unsigned hi_bit, unsigned lo_bit)
+ {
+ // use the specified bits
+ init(reg, hi_bit, lo_bit);
+ }
+ /*
+ * Join two regbits. We always append new regbits at teh LSB (the
+ * right hand side in common notation). This has the effect of
+ * shifting the existing bits left in order to make room for the new
+ * bits. To be precise:
+ * A = B + C
+ * means:
+ * A = ((B << C.width()) | C)
+ */
+ RegBits
+ operator+(const RegBits &that) const
+ {
+ RegBits result;
+ if (m_register) {
+ // simple this becomes complex result
+ result.m_sub_bits.push_back(*this);
+ } else {
+ // complex this
+ result.m_sub_bits = this->m_sub_bits;
+ }
+ result.m_sub_bits.push_back(that);
+ return result;
+ }
+ RegBits &
+ operator+=(const RegBits &that)
+ {
+ if (m_register) {
+ // simple this becomes complex
+ m_sub_bits.push_back(*this);
+ m_register.reset();
+ m_mask = 0;
+ }
+ m_sub_bits.push_back(that);
+ return *this;
+ }
+
+ /*
+ * RegBits::describe()
+ */
+ string
+ describe() const
+ {
+ if (m_register) {
+ return to_string(boost::format("0x%s[%d:%d]")
+ %m_register->describe() %m_hi_bit %m_lo_bit);
+ }
+
+ string ret;
+ for (size_t i = 0; i < m_sub_bits.size(); i++) {
+ if (ret != "") {
+ ret += ",";
+ }
+ ret += m_sub_bits[i].describe();
+ }
+ return ret;
+ }
+
+ /*
+ * RegBits::read()
+ *
+ * Read the value of this regbits. The resulting value is
+ * right-justified.
+ */
+ Value
+ read() const
+ {
+ Value result = 0;
+ if (m_register) {
+ // simple
+ result = m_register->read();
+ result >>= m_lo_bit;
+ result &= m_mask;
+ } else {
+ // complex (MSB first)
+ for (size_t i = 0; i < m_sub_bits.size(); i++) {
+ result <<= m_sub_bits[i].width();
+ result |= m_sub_bits[i].read();
+ }
+ }
+ return result;
+ }
+
+ /*
+ * RegBits::write(value)
+ *
+ * Write a value to this regbits. The value is assumed to be
+ * right-justified.
+ */
+ void
+ write(const Value &value) const
+ {
+ if (m_register) {
+ // simple
+ Value myval = value;
+ myval &= m_mask;
+ myval <<= m_lo_bit;
+ Value tmp = m_register->read();
+ tmp ^= (tmp & (m_mask << m_lo_bit));
+ tmp |= myval;
+ m_register->write(tmp);
+ } else {
+ // complex (LSB first)
+ Value myval = value;
+ for (int i = m_sub_bits.size()-1; i >= 0; i--) {
+ m_sub_bits[i].write(myval);
+ myval >>= m_sub_bits[i].width();
+ }
+ }
+ }
+
+ /*
+ * RegBits::width()
+ *
+ * Return the width of this regbits, in bits.
+ */
+ BitWidth
+ width() const
+ {
+ unsigned long result = 0;
+ if (m_register) {
+ // simple
+ result = m_mask.popcount();
+ } else {
+ // complex
+ for (size_t i = 0; i < m_sub_bits.size(); i++) {
+ result += m_sub_bits[i].width();
+ }
+ }
+ return result;
+ }
+
+ private:
+ // a simple regbits populates these
+ ConstRegisterPtr m_register;
+ unsigned m_lo_bit;
+ unsigned m_hi_bit;
+ Value m_mask;
+ // a complex regbits populates this (most significant bits first)
+ std::vector<RegBits> m_sub_bits;
+
+ void
+ init(const ConstRegisterPtr ®, unsigned hi_bit, unsigned lo_bit)
+ {
+ // sanity checks first
+ if (!reg) {
+ throw range_error("NULL register");
+ }
+ if (hi_bit < lo_bit) {
+ throw range_error(sprintfxx("bad bitrange: [%d:%d]",
+ hi_bit, lo_bit));
+ }
+ if (hi_bit >= reg->width()) {
+ throw range_error(
+ sprintfxx("bad bitrange: [%d:%d]: "
+ "register is only %d bits",
+ hi_bit, lo_bit, reg->width()));
+ }
+
+ // do this only after sanity checks
+ m_register = reg;
+ m_lo_bit = lo_bit;
+ m_hi_bit = hi_bit;
+ m_mask = MASK((hi_bit - lo_bit) + 1);
+ }
+};
+
+} // namespace pp
+
+#endif // PP_REGBITS_H__
=======================================
--- /dev/null
+++ /trunk/register.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,86 @@
+/* Copyright (c) Tim Hockin, 2007 */
+#ifndef PP_REGISTER_H__
+#define PP_REGISTER_H__
+
+#include "pp/pp.h"
+#include "pp/dirent.h"
+
+namespace pp {
+
+/*
+ * Register - abstract base class for a register
+ */
+class Register: public Dirent
+{
+ protected:
+ BitWidth m_width;
+
+ public:
+ explicit Register(const BitWidth width)
+ : Dirent(DIRENT_TYPE_REGISTER), m_width(width)
+ {
+ }
+ virtual ~Register()
+ {
+ }
+
+ /*
+ * Register::width()
+ *
+ * Get the access width of this register.
+ */
+ BitWidth
+ width() const
+ {
+ return m_width;
+ }
+
+ /*
+ * Register::describe()
+ *
+ * Get a string which describes this register
+ */
+ virtual string
+ describe() const = 0;
+
+ /*
+ * Register::read()
+ *
+ * Read the value of this register.
+ */
+ virtual Value
+ read() const = 0;
+
+ /*
+ * Register::write(value)
+ *
+ * Write a value to this register.
+ */
+ virtual void
+ write(const Value &value) const = 0;
+};
+typedef boost::shared_ptr<Register> RegisterPtr;
+typedef boost::shared_ptr<const Register> ConstRegisterPtr;
+
+// const
+inline ConstRegisterPtr
+register_from_dirent(const ConstDirentPtr &dirent)
+{
+ if (dirent->is_register()) {
+ return static_pointer_cast<const Register>(dirent);
+ }
+ throw Dirent::ConversionError(
+ "non-register dirent used as register");
+}
+// non-const
+inline RegisterPtr
+register_from_dirent(const DirentPtr &dirent)
+{
+ const ConstDirentPtr &const_dirent = dirent;
+ return const_pointer_cast<Register>(
+ register_from_dirent(const_dirent));
+}
+
+} // namespace pp
+
+#endif // PP_REGISTER_H__
=======================================
--- /dev/null
+++ /trunk/register_types.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,128 @@
+/* Copyright (c) Tim Hockin, 2007 */
+#ifndef PP_REGISTER_TYPES_H__
+#define PP_REGISTER_TYPES_H__
+
+#include "pp/pp.h"
+#include "pp/register.h"
+#include "pp/binding.h"
+#include "pp/rwprocs.h"
+#include "pp/context.h"
+#include "runtime.h"
+
+namespace pp {
+
+/*
+ * BoundRegister - a register definition.
+ *
+ * Notes:
+ */
+class BoundRegister: public Register
+{
+ private:
+ ConstBindingPtr m_binding;
+ Value m_address;
+
+ public:
+ BoundRegister(const ConstBindingPtr &binding,
+ const Value &address, const BitWidth width)
+ : Register(width), m_binding(binding), m_address(address)
+ {
+ }
+
+ /*
+ * Describe this register.
+ */
+ virtual string
+ describe() const
+ {
+ return to_string(m_address);
+ }
+
+ /*
+ * Read the value of this register.
+ *
+ * Throws: Driver::IoError
+ */
+ virtual Value
+ read() const
+ {
+ return m_binding->read(m_address, m_width);
+ }
+
+ /*
+ * Write a value to this register.
+ *
+ * Throws: Driver::IoError
+ */
+ virtual void
+ write(const Value &value) const
+ {
+ m_binding->write(m_address, m_width, value);
+ }
+};
+
+#define new_pp_bound_register(...) \
+ ::pp::RegisterPtr(new ::pp::BoundRegister(__VA_ARGS__))
+
+/*
+ * ProcRegister - a procedure-register definition.
+ *
+ * Notes:
+ */
+class ProcRegister: public Register
+{
+ private:
+ RwProcsPtr m_access;
+ ContextPtr m_context;
+
+ public:
+ ProcRegister(const RwProcsPtr &access, const BitWidth width)
+ : Register(width), m_access(access),
+ m_context(pp::runtime::context_snapshot())
+ {
+ }
+
+ /*
+ * Describe this register.
+ */
+ virtual string
+ describe() const
+ {
+ return "procs";
+ }
+
+ /*
+ * Read the value of this register.
+ *
+ * FIXME: need better throws!
+ * Throws: Driver::IoError
+ */
+ virtual Value
+ read() const
+ {
+ pp::runtime::context_push(m_context);
+ Value ret = m_access->read() & MASK(width());
+ pp::runtime::context_pop();
+ return ret;
+ }
+
+ /*
+ * Write a value to this register.
+ *
+ * Throws: Driver::IoError
+ */
+ virtual void
+ write(const Value &value) const
+ {
+ pp::runtime::context_push(m_context);
+ m_access->write(value & MASK(width()));
+ pp::runtime::context_pop();
+ }
+};
+
+#define new_pp_proc_register(...) \
+ ::pp::RegisterPtr(new ::pp::ProcRegister(__VA_ARGS__))
+
+} // namespace pp
+
+#endif // PP_REGISTER_TYPES_H__
=======================================
--- /dev/null
+++ /trunk/rwprocs.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,20 @@
+/* Copyright (c) Tim Hockin, 2007 */
+#ifndef PP_RWPROCS_H__
+#define PP_RWPROCS_H__
+
+#include "pp/pp.h"
+
+namespace pp {
+
+// a helper for things which delegate read and write operations
+struct RwProcs
+{
+ virtual ~RwProcs() {}
+ virtual Value read() const = 0;
+ virtual void write(const Value &value) const = 0;
+};
+typedef boost::shared_ptr<const RwProcs> RwProcsPtr;
+
+} // namespace pp
+
+#endif // PP_RWPROCS_H__
=======================================
--- /dev/null
+++ /trunk/scope.cpp Sun Nov 8 23:26:22 2009
@@ -0,0 +1,455 @@
+// Copyright (c) Tim Hockin, 2007
+#include "pp/pp.h"
+#include "pp/util/printfxx.h"
+#include "pp/scope.h"
+#include <stdexcept>
+#include "pp/path.h"
+#include "pp/dirent.h"
+#include "pp/binding.h"
+#include "pp/util/keyed_vector.h"
+#include "pp/datatype.h"
+#include "pp/register.h"
+#include "pp/field.h"
+#include "pp/array.h"
+#include "pp/alias.h"
+#include "language.h"
+
+namespace pp {
+
+//
+// Get a pointer to the parent scope of this object. If this
+// scope is the top of the hierarchy, this method returns a
+// pointer to this object.
+//
+ConstScopePtr
+Scope::parent() const
+{
+ if (is_root()) {
+ return shared_from_this();
+ }
+ return m_parent.lock();
+}
+
+//
+// Set the parent scope of this object.
+//
+void
+Scope::set_parent(const ConstScopePtr &parent)
+{
+ m_parent = parent;
+}
+
+//
+// Return a boolean indicating whether this object is the top of the
+// containership hierarchy or not. This is the same as checking
+// for:
+// (object->parent() == object)
+//
+bool
+Scope::is_root() const
+{
+ return (m_parent.lock().get() == NULL);
+}
+
+//
+// Get the binding of this scope. If this scope is not bound,
+// climb the scope hierarchy until you find a binding. If no
+// scope in the hierarchy is bound, return NULL.
+//
+const ConstBindingPtr &
+Scope::binding() const
+{
+ const Scope *s = this;
+ while (!s->is_bound() && !s->is_root()) {
+ s = s->parent().get();
+ }
+ return s->m_binding;
+}
+bool
+Scope::is_bound() const
+{
+ return m_binding ? true : false;
+}
+
+//
+// Add a named datatype to this scope.
+//
+void
+Scope::add_datatype(const string &name, const DatatypePtr &datatype)
+{
+ m_datatypes.insert(name, datatype);
+}
+
+//
+// Return the number of named datatypes in this scope.
+//
+size_t
+Scope::n_datatypes() const
+{
+ return m_datatypes.size();
+}
+
+//
+// Provide access to the datatypes vector.
+//
+ConstDatatypePtr
+Scope::datatype(int index) const
+{
+ ConstDatatypePtr dt;
+ util::KeyedVector<string, ConstDatatypePtr>::const_iterator it;
+ it = m_datatypes.find(index);
+ if (it != m_datatypes.end()) {
+ dt = *it;
+ }
+ return dt;
+}
+ConstDatatypePtr
+Scope::datatype(string index) const
+{
+ ConstDatatypePtr dt;
+ util::KeyedVector<string, ConstDatatypePtr>::const_iterator it;
+ it = m_datatypes.find(index);
+ if (it != m_datatypes.end()) {
+ dt = *it;
+ }
+ return dt;
+}
+
+//
+// Return the name of the indexed datatype.
+//
+string
+Scope::datatype_name(int index) const
+{
+ return m_datatypes.key_at(index);
+}
+
+//
+// Look up a datatype by name.
+//
+ConstDatatypePtr
+Scope::resolve_datatype(const string &name) const
+{
+ DTRACE(TRACE_TYPES && TRACE_SCOPES,
+ "trying to resolve type \"" + name + "\"");
+
+ // look for it in this scope
+ ConstDatatypePtr dt = datatype(name);
+ if (dt) {
+ return dt;
+ }
+
+ // if not found in currrent scope fall through to parent scope
+ if (!is_root()) {
+ DTRACE(TRACE_TYPES && TRACE_SCOPES,
+ "type \"" + name
+ + "\" not found, climbing scope");
+ return parent()->resolve_datatype(name);
+ }
+
+ DTRACE(TRACE_TYPES && TRACE_SCOPES,
+ "type \"" + name + "\" not found");
+ return ConstDatatypePtr();
+}
+
+//
+// Canonicalize a path string, expanding and finalizing things
+// like bookmarks and array reverse-indices.
+//
+Path
+Scope::resolve_path(const Path &path) const
+{
+ Path ret;
+ if (walk_path(path, RESOLVE_ALIAS, NULL, &ret) < 0) {
+ return Path();
+ }
+ return ret;
+}
+
+//
+// Add a named dirent to this scope.
+//
+// Throws:
+// Path::NotFoundError - path not found
+// Path::InvalidError - invalid path element
+// Dirent::ConversionError - path element is not a scope
+//
+void
+Scope::add_dirent(const Path::Element &elem,
+ const DirentPtr &new_dirent)
+{
+ // is the element an array access?
+ if (elem.is_array()) {
+ // if so, we don't support direct indexed writes, just appends
+ if (elem.array_mode() != elem.ARRAY_APPEND) {
+ throw Path::InvalidError(
+ "array write is not an append: "
+ + elem.to_string());
+ }
+ // does the array already exist?
+ const DirentPtr &de = dirent(elem.name());
+ if (de != NULL) {
+ // if so, append the new dirent
+ const ArrayPtr &ar = array_from_dirent(de);
+ ar->append(new_dirent);
+ } else {
+ // if not, add the array and append the new dirent
+ ArrayPtr ar = new_pp_array(new_dirent->dirent_type());
+ m_dirents.insert(elem.name(), ar);
+ ar->append(new_dirent);
+ }
+ } else {
+ // if not, just add the new dirent
+ m_dirents.insert(elem.name(), new_dirent);
+ }
+}
+
+//
+// Return the number of dirents in this scope.
+//
+size_t
+Scope::n_dirents() const
+{
+ return m_dirents.size();
+}
+
+//
+// Provide access to the dirents vector.
+//
+DirentPtr
+Scope::dirent(int index)
+{
+ DirentPtr de;
+ util::KeyedVector<string, DirentPtr>::iterator it;
+ it = m_dirents.find(index);
+ if (it != m_dirents.end()) {
+ de = *it;
+ }
+ return de;
+}
+DirentPtr
+Scope::dirent(string index)
+{
+ DirentPtr de;
+ util::KeyedVector<string, DirentPtr>::iterator it;
+ it = m_dirents.find(index);
+ if (it != m_dirents.end()) {
+ de = *it;
+ }
+ return de;
+}
+ConstDirentPtr
+Scope::dirent(int index) const
+{
+ ConstDirentPtr de;
+ util::KeyedVector<string, DirentPtr>::const_iterator it
+ = m_dirents.find(index);
+ if (it != m_dirents.end()) {
+ de = *it;
+ }
+ return de;
+}
+ConstDirentPtr
+Scope::dirent(string index) const
+{
+ ConstDirentPtr de;
+ util::KeyedVector<string, DirentPtr>::const_iterator it
+ = m_dirents.find(index);
+ if (it != m_dirents.end()) {
+ de = *it;
+ }
+ return de;
+}
+
+//
+// Return the name of the indexed dirent.
+//
+const string &
+Scope::dirent_name(int index) const
+{
+ return m_dirents.key_at(index);
+}
+
+//
+// Return a pointer to the specified dirent.
+//
+// Returns:
+// NULL if path not found.
+// Throws:
+// Path::InvalidError - invalid path element
+// Dirent::ConversionError - path element is not a scope
+//
+ConstDirentPtr
+Scope::lookup_dirent(const Path &path, unsigned flags) const
+{
+ ConstDirentPtr de;
+ if (walk_path(path, flags, &de, NULL) < 0) {
+ de = ConstDirentPtr();
+ }
+ return de;
+}
+
+// This is a helper for lookup_dirent() and resolve_path(). It walks a
+// path through a scope, producing a final dirent and a canonicalized
+// path.
+int
+Scope::walk_path(const Path &path, unsigned flags,
+ ConstDirentPtr *out_de,
+ Path *out_path) const
+{
+ Path my_path(path);
+ const Scope *scope = this;
+
+ if (my_path.is_absolute()) {
+ while (!scope->is_root()) {
+ scope = scope->parent().get();
+ if (out_path) {
+ out_path->push_back(Path::Element(".."));
+ }
+ }
+ }
+
+ if (my_path.size() == 0) {
+ if (out_de) {
+ *out_de = scope->shared_from_this();
+ }
+ // out_path is correct already
+ return 0;
+ }
+
+ return scope->walk_path_internal(my_path, flags, out_de, out_path);
+}
+// NOTE: this takes path as a non-const reference
+int
+Scope::walk_path_internal(Path &path, unsigned flags,
+ ConstDirentPtr *out_de,
+ Path *out_path) const
+{
+ // get the next element of the path
+ Path::Element path_front = path.pop_front();
+
+ // look up the dirent of the next element
+ ConstDirentPtr de;
+ if (path_front == "..") {
+ // go up one level in the tree
+ de = parent();
+ if (out_path) {
+ if (out_path->size() > 0 && out_path->back() != "..") {
+ out_path->pop_back();
+ } else {
+ out_path->push_back(path_front);
+ }
+ }
+ } else if (path_front.is_bookmark()) {
+ // climb back up the stack, looking for the bookmark
+ const Scope *s = this;
+ while (!s->has_bookmark(path_front.name()) && !s->is_root()) {
+ s = s->parent().get();
+ if (out_path) {
+ out_path->push_back(Path::Element(".."));
+ }
+ }
+ // did we not find the bookmark?
+ if (!s->has_bookmark(path_front.name())) {
+ return -1;
+ }
+ de = s->shared_from_this();
+ } else if (path_front.is_array()) {
+ // it must be a dirent in the current scope
+ de = dirent(path_front.name());
+ if (!de) {
+ return -1;
+ }
+
+ // if path is array and dirent is not array: error
+ if (!de->is_array()) {
+ throw Dirent::ConversionError(
+ "path element is not an array: "
+ + path_front.to_string());
+ }
+ // if path is array_append, return not_found
+ if (path_front.array_mode() == path_front.ARRAY_APPEND) {
+ return -1;
+ }
+
+ ConstArrayPtr ar = array_from_dirent(de);
+ int index = path_front.array_index();
+
+ // handle a negative index
+ if (index < 0) {
+ index = ar->size() + index;
+ }
+
+ // sanity check the index
+ if (index < 0 || size_t(index) >= ar->size()) {
+ return -1;
+ }
+
+ // index into the array
+ de = ar->at(index);
+ if (out_path) {
+ string name = sprintfxx("%s[%d]",
+ path_front.name(), index);
+ out_path->push_back(Path::Element(name));
+ }
+ } else {
+ // it must be a dirent in the current scope
+ de = dirent(path_front.name());
+ if (!de) {
+ return -1;
+ }
+ if (out_path) {
+ out_path->push_back(path_front);
+ }
+ }
+
+ // if it is an alias, dereference it
+ if (de->is_alias() && (path.size() > 0 || flags & RESOLVE_ALIAS)) {
+ ConstAliasPtr a = alias_from_dirent(de);
+ de = lookup_dirent(a->link_path());
+ if (!de) {
+ return -1;
+ }
+ }
+
+ // did we find the dirent?
+ if (path.size() == 0) {
+ if (out_de) {
+ *out_de = de;
+ }
+ return 0;
+ }
+
+ // keep looking
+ if (de->is_scope()) {
+ ConstScopePtr s = scope_from_dirent(de);
+ return s->walk_path_internal(path, flags, out_de, out_path);
+ }
+
+ // error
+ throw Dirent::ConversionError(sprintfxx(
+ "path element is not a scope: %s (%s)", path, de->dirent_type()));
+}
+
+//
+// Test whether the path resolves to a defined dirent.
+//
+bool
+Scope::dirent_defined(const Path &path) const
+{
+ return lookup_dirent(path) ? true : false;
+}
+
+void
+Scope::add_bookmark(const string &name)
+{
+ m_bookmarks.insert(std::make_pair(name, 1));
+}
+
+bool
+Scope::has_bookmark(const string &name) const
+{
+ return (m_bookmarks.find(name) != m_bookmarks.end());
+}
+
+} // namespace pp
=======================================
--- /dev/null
+++ /trunk/scope.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,244 @@
+// Copyright (c) Tim Hockin, 2007
+#ifndef PP_SCOPE_H__
+#define PP_SCOPE_H__
+
+#include "pp/pp.h"
+#include <stdexcept>
+#include <map>
+#include "pp/path.h"
+#include "pp/dirent.h"
+#include "pp/binding.h"
+#include "pp/util/keyed_vector.h"
+#include "pp/datatype.h"
+#include "pp/register.h"
+#include "pp/field.h"
+#include "pp/array.h"
+#include <boost/enable_shared_from_this.hpp>
+
+namespace pp {
+
+//
+// Scope - a lexical scope.
+//
+class Scope;
+typedef boost::shared_ptr<Scope> ScopePtr;
+typedef boost::shared_ptr<const Scope> ConstScopePtr;
+typedef boost::weak_ptr<const Scope> WeakConstScopePtr;
+
+class Scope: public Dirent, public boost::enable_shared_from_this<Scope>
+{
+ private:
+ WeakConstScopePtr m_parent;
+ ConstBindingPtr m_binding;
+ util::KeyedVector<string, DirentPtr> m_dirents;
+ util::KeyedVector<string, ConstDatatypePtr> m_datatypes;
+ std::map<string, int> m_bookmarks;
+
+ public:
+ explicit Scope(const BindingPtr &binding = BindingPtr())
+ : Dirent(DIRENT_TYPE_SCOPE), m_parent(), m_binding(binding)
+ {
+ }
+ virtual ~Scope()
+ {
+ }
+
+ //
+ // Get a pointer to the parent scope of this object. If this
+ // scope is the top of the hierarchy, this method returns a
+ // pointer to this object.
+ //
+ ConstScopePtr
+ parent() const;
+
+ //
+ // Set the parent scope of this object.
+ //
+ void
+ set_parent(const ConstScopePtr &parent);
+
+ //
+ // Return a boolean indicating whether this object is the top of the
+ // containership hierarchy or not. This is the same as checking
+ // for:
+ // (object->parent() == object)
+ //
+ bool
+ is_root() const;
+
+ //
+ // Get the binding of this scope. If this scope is not bound,
+ // climb the scope hierarchy until you find a binding. If no
+ // scope in the hierarchy is bound, return NULL.
+ //
+ const ConstBindingPtr &
+ binding() const;
+ bool
+ is_bound() const;
+
+ //
+ // Add a named datatype to this scope.
+ //
+ // Throws:
+ // pp_parse_error - invalid name
+ //
+ //
+ void
+ add_datatype(const string &name, const DatatypePtr &datatype);
+
+ //
+ // Return the number of named datatypes in this scope.
+ //
+ size_t
+ n_datatypes() const;
+
+ //
+ // Provide access to the datatypes vector.
+ //
+ // Returns:
+ // NULL if not found, otherwise the desired Datatype.
+ //
+ ConstDatatypePtr
+ datatype(int index) const;
+ ConstDatatypePtr
+ datatype(string index) const;
+
+ //
+ // Return the name of the indexed datatype.
+ //
+ string
+ datatype_name(int index) const;
+
+ //
+ // Look up a datatype by name.
+ //
+ ConstDatatypePtr
+ resolve_datatype(const string &name) const;
+
+ //
+ // Canonicalize a path string, expanding and finalizing things
+ // like bookmarks and array reverse-indices.
+ //
+ // Returns:
+ // a valid Path on success.
+ // an uninitialized, empty Path on error.
+ // Throws:
+ // Path::InvalidError - invalid path element
+ // Dirent::ConversionError - path element is not a scope
+ //
+ Path
+ resolve_path(const Path &path) const;
+
+ //
+ // Add a named dirent to this scope.
+ //
+ // Throws:
+ // Path::NotFoundError - path not found
+ // Path::InvalidError - invalid path element
+ // Dirent::ConversionError - path element is not a scope
+ //
+ void
+ add_dirent(const string &name, const DirentPtr &dirent)
+ {
+ add_dirent(Path::Element(name), dirent);
+ }
+ void
+ add_dirent(const Path::Element &name, const DirentPtr &dirent);
+
+ //
+ // Return the number of dirents in this scope.
+ //
+ size_t
+ n_dirents() const;
+
+ //
+ // Provide access to the dirents vector.
+ //
+ // Returns:
+ // NULL if element not found, otherwise the desired Dirent.
+ //
+ DirentPtr
+ dirent(int index);
+ DirentPtr
+ dirent(string index);
+ ConstDirentPtr
+ dirent(int index) const;
+ ConstDirentPtr
+ dirent(string index) const;
+
+ //
+ // Return the name of the indexed dirent.
+ //
+ // Throws:
+ // std::out_of_range
+ //
+ const string &
+ dirent_name(int index) const;
+
+ // This flag indicates that lookup_dirent() should resolve aliases
+ // if the final dirent is an alias.
+ static const unsigned RESOLVE_ALIAS = 0x01;
+
+ //
+ // Return a pointer to the specified dirent.
+ //
+ // Returns:
+ // NULL if path not found.
+ // Throws:
+ // Path::InvalidError - invalid path element
+ // Dirent::ConversionError - path element is not a scope
+ //
+ //
+ ConstDirentPtr
+ lookup_dirent(const Path &path, unsigned flags=0) const;
+
+ //
+ // Tests whether the path resolves to a defined dirent.
+ //
+ bool
+ dirent_defined(const Path &path) const;
+
+ //
+ // Add a bookmark for this scope.
+ //
+ void
+ add_bookmark(const string &name);
+
+ //
+ // See if this scope has a particular bookmark.
+ //
+ bool
+ has_bookmark(const string &name) const;
+
+ private:
+ // Walk a path.
+ int
+ walk_path(const Path &path, unsigned flags,
+ ConstDirentPtr *out_de, Path *out_path) const;
+ int
+ walk_path_internal(Path &path, unsigned flags,
+ ConstDirentPtr *out_de, Path *out_path) const;
+};
+
+#define new_pp_scope(...) ::pp::ScopePtr(new ::pp::Scope(__VA_ARGS__))
+
+// const
+inline ConstScopePtr
+scope_from_dirent(const ConstDirentPtr &dirent)
+{
+ if (dirent->is_scope()) {
+ return static_pointer_cast<const Scope>(dirent);
+ }
+ throw Dirent::ConversionError("non-scope dirent used as scope");
+}
+// non-const
+inline ScopePtr
+scope_from_dirent(const DirentPtr &dirent)
+{
+ const ConstDirentPtr &const_dirent = dirent;
+ return const_pointer_cast<Scope>(scope_from_dirent(const_dirent));
+}
+
+} // namespace pp
+
+#endif // PP_SCOPE_H__
=======================================
--- /dev/null
+++ /trunk/tests/alias_test.cpp Sun Nov 8 23:26:22 2009
@@ -0,0 +1,28 @@
+#include "pp/pp.h"
+#include "pp/alias.h"
+#include "pp/dirent.h"
+#include "test_helpers.h"
+#include "pp_test.h"
+
+TEST(test_ctors)
+{
+ // test the basic constructor
+ {
+ pp::AliasPtr alias = new_pp_alias("foo");
+ if (alias->dirent_type() != pp::DIRENT_TYPE_ALIAS) {
+ TEST_ERROR("pp::Alias::Alias(string)");
+ }
+ if (alias->link_path() != "foo") {
+ TEST_ERROR("pp::Alias::Alias(string)");
+ }
+ }
+ {
+ pp::AliasPtr alias = new_pp_alias("foo/bar");
+ if (alias->dirent_type() != pp::DIRENT_TYPE_ALIAS) {
+ TEST_ERROR("pp::Alias::Alias(string)");
+ }
+ if (alias->link_path() != "foo/bar") {
+ TEST_ERROR("pp::Alias::Alias(string)");
+ }
+ }
+}
=======================================
--- /dev/null
+++ /trunk/tests/array_test.cpp Sun Nov 8 23:26:22 2009
@@ -0,0 +1,65 @@
+#include "pp/pp.h"
+#include "pp/array.h"
+#include "pp/dirent.h"
+#include "test_helpers.h"
+#include "pp_test.h"
+
+TEST(test_ctors)
+{
+ // test the basic constructor
+ {
+ pp::ArrayPtr array = new_pp_array(pp::DIRENT_TYPE_SCOPE);
+ if (array->array_type() != pp::DIRENT_TYPE_SCOPE) {
+ TEST_FAIL("pp::Array::Array()");
+ }
+ }
+ {
+ pp::ArrayPtr array = new_pp_array(pp::DIRENT_TYPE_REGISTER);
+ if (array->array_type() != pp::DIRENT_TYPE_REGISTER) {
+ TEST_FAIL("pp::Array::Array()");
+ }
+ }
+ {
+ pp::ArrayPtr array = new_pp_array(pp::DIRENT_TYPE_FIELD);
+ if (array->array_type() != pp::DIRENT_TYPE_FIELD) {
+ TEST_FAIL("pp::Array::Array()");
+ }
+ }
+ {
+ pp::ArrayPtr array = new_pp_array(pp::DIRENT_TYPE_ARRAY);
+ if (array->array_type() != pp::DIRENT_TYPE_ARRAY) {
+ TEST_FAIL("pp::Array::Array()");
+ }
+ }
+ {
+ pp::ArrayPtr array = new_pp_array(pp::DIRENT_TYPE_ALIAS);
+ if (array->array_type() != pp::DIRENT_TYPE_ALIAS) {
+ TEST_ERROR("pp::Array::Array()");
+ }
+ }
+}
+
+TEST(test_indexing)
+{
+ pp::ArrayPtr array = new_pp_array(pp::DIRENT_TYPE_SCOPE);
+ if (array->size() != 0) {
+ TEST_FAIL("pp::Array::size()");
+ }
+
+ pp::ScopePtr scope = new_pp_scope();
+ array->append(scope);
+ if (array->size() != 1) {
+ TEST_FAIL("pp::Array::append()");
+ }
+
+ if (array->at(0) != scope) {
+ TEST_FAIL("pp::Array::at()");
+ }
+
+ try {
+ pp::ArrayPtr array2 = new_pp_array(pp::DIRENT_TYPE_FIELD);
+ array2->append(new_pp_scope());
+ TEST_FAIL("pp::Array::append()");
+ } catch (std::exception &e) {
+ }
+}
=======================================
--- /dev/null
+++ /trunk/tests/binding_test.cpp Sun Nov 8 23:26:22 2009
@@ -0,0 +1,19 @@
+#include "pp/pp.h"
+#include "pp/binding.h"
+#include "test_binding.h"
+#include "pp_test.h"
+
+TEST(test_pp_binding)
+{
+ /* test the read() method */
+ pp::BindingPtr sp = new_test_binding();
+ if (sp->read(0, pp::BITS8) != 0xff) {
+ TEST_FAIL("pp::Binding::read()");
+ }
+
+ /* test the write() method */
+ sp->write(0, pp::BITS8, 0x11);
+ if (sp->read(0, pp::BITS16) != 0xff11) {
+ TEST_FAIL("pp::Binding::write()");
+ }
+}
=======================================
--- /dev/null
+++ /trunk/tests/datatype_test.cpp Sun Nov 8 23:26:22 2009
@@ -0,0 +1,611 @@
+#include "pp/pp.h"
+#include "pp/datatype_types.h"
+#include "pp/util/bignum_lambda.h"
+#include "pp_test.h"
+
+TEST(test_pp_enum_datatype)
+{
+ // test the basic constructor
+ pp::EnumDatatype e;
+
+ // test the evaluate() method
+ e.add_value("one", 1);
+ e.add_value("two", 2);
+ e.add_value("three", 3);
+ if (e.evaluate(2) != "two") {
+ TEST_FAIL("pp::EnumDatatype::evaluate()");
+ }
+ if (e.evaluate(0) != "<!0!>") {
+ TEST_FAIL("pp::EnumDatatype::evaluate()");
+ }
+
+ // test lookup()
+ TEST_ASSERT(e.lookup("one") == 1,
+ "pp::EnumDatatype::lookup(string)");
+ TEST_ASSERT(e.lookup("two") == 2,
+ "pp::EnumDatatype::lookup(string)");
+ TEST_ASSERT(e.lookup(1) == 1, "pp::EnumDatatype::lookup(int)");
+ TEST_ASSERT(e.lookup(2) == 2, "pp::EnumDatatype::lookup(int)");
+ try {
+ e.lookup("foo");
+ TEST_FAIL("pp::EnumDatatype::lookup(string)");
+ } catch (pp::Datatype::InvalidError &e) {
+ }
+ try {
+ e.lookup(4);
+ TEST_FAIL("pp::EnumDatatype::lookup(int)");
+ } catch (pp::Datatype::InvalidError &e) {
+ }
+}
+
+TEST(test_pp_multi_datatype)
+{
+ // test basic constructor
+ {
+ pp::MultiDatatype m;
+ }
+
+ // test the add_range() method
+ try {
+ // adding to beginning
+ pp::MultiDatatype m;
+ m.add_range(new_pp_hex_datatype(), 20, 30);
+ m.add_range(new_pp_int_datatype(), 15, 16);
+ m.add_range(new_pp_int_datatype(), 0, 10);
+ } catch (std::exception &e) {
+ TEST_FAIL("pp::MultiDatatype::add_range()");
+ }
+ try {
+ // adding in between
+ pp::MultiDatatype m;
+ m.add_range(new_pp_int_datatype(), 0, 10);
+ m.add_range(new_pp_hex_datatype(), 20, 30);
+ m.add_range(new_pp_int_datatype(), 15, 16);
+ } catch (std::exception &e) {
+ TEST_FAIL("pp::MultiDatatype::add_range()");
+ }
+ try {
+ // adding to end
+ pp::MultiDatatype m;
+ m.add_range(new_pp_int_datatype(), 0, 10);
+ m.add_range(new_pp_hex_datatype(), 11, 12);
+ m.add_range(new_pp_string_datatype(), 13, 20);
+ } catch (std::exception &e) {
+ TEST_FAIL("pp::MultiDatatype::add_range()");
+ }
+ try {
+ // overlap at the beginning
+ pp::MultiDatatype m;
+ m.add_range(new_pp_int_datatype(), 0, 10);
+ m.add_range(new_pp_hex_datatype(), 11, 12);
+ m.add_range(new_pp_int_datatype(), 0, 3);
+ TEST_FAIL("pp::MultiDatatype::add_range()");
+ } catch (std::exception &e) {
+ }
+ try {
+ // overlap in the middle
+ pp::MultiDatatype m;
+ m.add_range(new_pp_int_datatype(), 0, 10);
+ m.add_range(new_pp_hex_datatype(), 11, 13);
+ m.add_range(new_pp_int_datatype(), 8, 9);
+ TEST_FAIL("pp::MultiDatatype::add_range()");
+ } catch (std::exception &e) {
+ }
+ try {
+ // overlap in the middle across two ranges
+ pp::MultiDatatype m;
+ m.add_range(new_pp_int_datatype(), 0, 10);
+ m.add_range(new_pp_hex_datatype(), 11, 13);
+ m.add_range(new_pp_int_datatype(), 9, 12);
+ TEST_FAIL("pp::MultiDatatype::add_range()");
+ } catch (std::exception &e) {
+ }
+ try {
+ // overlap in the middle on a min
+ pp::MultiDatatype m;
+ m.add_range(new_pp_int_datatype(), 0, 10);
+ m.add_range(new_pp_hex_datatype(), 11, 13);
+ m.add_range(new_pp_int_datatype(), 11, 11);
+ TEST_FAIL("pp::MultiDatatype::add_range()");
+ } catch (std::exception &e) {
+ }
+ try {
+ // overlap in the middle on a max
+ pp::MultiDatatype m;
+ m.add_range(new_pp_int_datatype(), 0, 10);
+ m.add_range(new_pp_hex_datatype(), 11, 13);
+ m.add_range(new_pp_int_datatype(), 10, 10);
+ TEST_FAIL("pp::MultiDatatype::add_range()");
+ } catch (std::exception &e) {
+ }
+ try {
+ // overlap at the end
+ pp::MultiDatatype m;
+ m.add_range(new_pp_int_datatype(), 0, 10);
+ m.add_range(new_pp_hex_datatype(), 11, 13);
+ m.add_range(new_pp_int_datatype(), 12, 15);
+ TEST_FAIL("pp::MultiDatatype::add_range()");
+ } catch (std::exception &e) {
+ }
+ try {
+ // overlap touching the end
+ pp::MultiDatatype m;
+ m.add_range(new_pp_int_datatype(), 0, 10);
+ m.add_range(new_pp_hex_datatype(), 11, 13);
+ m.add_range(new_pp_int_datatype(), 13, 15);
+ TEST_FAIL("pp::MultiDatatype::add_range()");
+ } catch (std::exception &e) {
+ }
+ try {
+ // min > max
+ pp::MultiDatatype m;
+ m.add_range(new_pp_int_datatype(), 0, 10);
+ m.add_range(new_pp_hex_datatype(), 11, 13);
+ m.add_range(new_pp_int_datatype(), 25, 20);
+ TEST_FAIL("pp::MultiDatatype::add_range()");
+ } catch (std::exception &e) {
+ }
+
+ {
+ // test the evaluate() method
+ pp::MultiDatatype m;
+ m.add_range(new_pp_bool_datatype("true", "false"), 0, 1);
+ m.add_range(new_pp_int_datatype("units"), 5, 10);
+ m.add_range(new_pp_int_datatype(), 11, 12);
+ m.add_range(new_pp_hex_datatype(pp::BITS32), 15, 20);
+ util::KeyedVector<string, pp::Value> enumlist;
+ enumlist.insert("3", 25);
+ enumlist.insert("11", 26);
+ enumlist.insert("test_key", 27);
+ m.add_range(new_pp_enum_datatype(enumlist), 25, 27);
+ // ...bool
+ TEST_ASSERT(m.evaluate(pp::Value(0)) == "false",
+ "pp::MultiDatatype::evaluate()");
+ TEST_ASSERT(m.evaluate(pp::Value(1)) == "true",
+ "pp::MultiDatatype::evaluate()");
+ // ...int
+ TEST_ASSERT(m.evaluate(pp::Value(5)) == "5 units",
+ "pp::MultiDatatype::evaluate()");
+ TEST_ASSERT(m.evaluate(pp::Value(6)) == "6 units",
+ "pp::MultiDatatype::evaluate()");
+ TEST_ASSERT(m.evaluate(pp::Value(10)) == "10 units",
+ "pp::MultiDatatype::evaluate()");
+ // ...hex
+ TEST_ASSERT(m.evaluate(pp::Value(15)) == "0x0000000f",
+ "pp::MultiDatatype::evaluate()");
+ TEST_ASSERT(m.evaluate(pp::Value(16)) == "0x00000010",
+ "pp::MultiDatatype::evaluate()");
+ TEST_ASSERT(m.evaluate(pp::Value(20)) == "0x00000014",
+ "pp::MultiDatatype::evaluate()");
+ // ...enum
+ TEST_ASSERT(m.evaluate(pp::Value(25)) == "3",
+ "pp::MultiDatatype::evaluate()");
+ TEST_ASSERT(m.evaluate(pp::Value(26)) == "11",
+ "pp::MultiDatatype::evaluate()");
+ TEST_ASSERT(m.evaluate(pp::Value(27)) == "test_key",
+ "pp::MultiDatatype::evaluate()");
+ // ...out of range
+ TEST_ASSERT(m.evaluate(pp::Value(3)) == "<!3!>",
+ "pp::MultiDatatype::evaluate()");
+ TEST_ASSERT(m.evaluate(pp::Value(30)) == "<!30!>",
+ "pp::MultiDatatype::evaluate()");
+
+ // test the lookup() method
+ // ...lookup(pp::Value)
+ try {
+ TEST_ASSERT(m.lookup(pp::Value(0)) == pp::Value(0),
+ "pp::MultiDatatype::lookup()");
+ } catch (std::exception &e) {
+ TEST_FAIL("pp::MultiDatatype::lookup()");
+ }
+ try {
+ TEST_ASSERT(m.lookup(pp::Value(7)) == pp::Value(7),
+ "pp::MultiDatatype::lookup()");
+ } catch (std::exception &e) {
+ TEST_FAIL("pp::MultiDatatype::lookup()");
+ }
+ try {
+ TEST_ASSERT(m.lookup(pp::Value(20)) == pp::Value(20),
+ "pp::MultiDatatype::lookup()");
+ } catch (std::exception &e) {
+ TEST_FAIL("pp::MultiDatatype::lookup()");
+ }
+ try {
+ // out of range
+ m.lookup(pp::Value(13));
+ TEST_FAIL("pp::MultiDatatype::lookup()");
+ } catch (std::exception &e) {
+ }
+ // ...lookup(string)
+ try {
+ TEST_ASSERT(m.lookup("true") == pp::Value(1),
+ "pp::MultiDatatype::lookup()");
+ } catch (std::exception &e) {
+ TEST_FAIL("pp::MultiDatatype::lookup()");
+ }
+ try {
+ TEST_ASSERT(m.lookup("true") == pp::Value(1),
+ "pp::MultiDatatype::lookup()");
+ } catch (std::exception &e) {
+ TEST_FAIL("pp::MultiDatatype::lookup()");
+ }
+ try {
+ TEST_ASSERT(m.lookup("0x0000000f") == pp::Value(15),
+ "pp::MultiDatatype::lookup()");
+ } catch (std::exception &e) {
+ TEST_FAIL("pp::MultiDatatype::lookup()");
+ }
+ try {
+ TEST_ASSERT(m.lookup("test_key") == pp::Value(27),
+ "pp::MultiDatatype::lookup()");
+ } catch (std::exception &e) {
+ TEST_FAIL("pp::MultiDatatype::lookup()");
+ }
+ // FIXME: This test fails because of the ambiguity in the
+ // multi datatype; both pp::Value(11) and pp::Value(26)
+ // produce the string "11". For now, lookup(str) is defined to
+ // return the lowest pp::Value the given string could have
+ // produced.
+#if 0
+ try {
+ TEST_ASSERT(m.lookup("11") == pp::Value(26),
+ "pp::MultiDatatype::lookup()");
+ } catch (std::exception &e) {
+ TEST_FAIL("pp::MultiDatatype::lookup()");
+ }
+#endif
+ }
+}
+
+TEST(test_pp_string_datatype)
+{
+ // test the basic constructor
+ pp::StringDatatype s;
+
+ // test the evaluate() method
+ if (s.evaluate(0x41) != "A") {
+ TEST_FAIL("pp::StringDatatype::evaluate() returns " +
+ s.evaluate(pp::Value(0x41)));
+ }
+ // ...test null character
+ if (s.evaluate(pp::Value(0x00)) != "") {
+ TEST_FAIL("pp::StringDatatype::evaluate() returns " +
+ s.evaluate(pp::Value(0x00)));
+ }
+ // ...test multicharacter strings
+ if (s.evaluate(pp::Value("0x6f6c6c6548")) != "Hello") {
+ TEST_FAIL("pp::StringDatatype::evaluate() returns " +
+ s.evaluate(pp::Value("0x6f6c6c6548")));
+ }
+ // ...test multicharacter strings with nulls
+ if (s.evaluate(pp::Value("0x00006f6c6c6548")) != "Hello") {
+ TEST_FAIL("pp::StringDatatype::evaluate() returns " +
+ s.evaluate(pp::Value("0x00006f6c6c6548")));
+ }
+ if (s.evaluate(pp::Value("0x6f006c006c0065004800"))
+ == "Hello") {
+ // These should not be equal because the string returned
+ // by evaluate() contains embedded null characters.
+ TEST_FAIL("pp::StringDatatype::evaluate() returns " +
+ s.evaluate(pp::Value("0x6f006c006c0065004800")));
+ }
+
+ // test lookup()
+ TEST_ASSERT(s.lookup(pp::Value(0x00)) == pp::Value(0x00),
+ "pp::StringDatatype::lookup(pp::Value)");
+ TEST_ASSERT(s.lookup(pp::Value("0x1234567890"))
+ == pp::Value("0x1234567890"),
+ "pp::StringDatatype::lookup(pp::Value)");
+ TEST_ASSERT(s.lookup("A") == pp::Value(0x41),
+ "pp::StringDatatype::lookup(string)");
+ TEST_ASSERT(s.lookup("") == pp::Value(0x00),
+ "pp::StringDatatype::lookup(string)");
+ TEST_ASSERT(s.lookup("Hello")
+ == pp::Value("0x6f6c6c6548"),
+ "pp::StringDatatype::lookup(pp::Value)");
+
+ // test that evaluate() and lookup() are inverse
+ TEST_ASSERT(s.lookup(s.evaluate(0x333231)) == 0x333231,
+ "pp::StringDatatype::lookup(string) and "
+ "pp::StringDatatype::evaluate(pp::Value)");
+ TEST_ASSERT(s.lookup(s.evaluate(pp::Value("0x6d086c0065004800")))
+ == pp::Value("0x6d086c0065004800"),
+ "pp::StringDatatype::lookup(string) and "
+ "pp::StringDatatype::evaluate(pp::Value)");
+ TEST_ASSERT(s.evaluate(s.lookup("xyz")) == "xyz",
+ "pp::StringDatatype::lookup(string) and "
+ "pp::StringDatatype::evaluate(pp::Value)");
+}
+
+TEST(test_pp_bool_datatype)
+{
+ // test the basic constructor
+ pp::BoolDatatype b("TRUE", "FALSE");
+
+ // test the evaluate() method
+ if (b.evaluate(0) != "FALSE") {
+ TEST_FAIL("pp::BoolDatatype::evaluate()");
+ }
+ if (b.evaluate(1) != "TRUE") {
+ TEST_FAIL("pp::BoolDatatype::evaluate()");
+ }
+ if (b.evaluate(2) != "TRUE") {
+ TEST_FAIL("pp::BoolDatatype::evaluate()");
+ }
+
+ // test lookup()
+ TEST_ASSERT(b.lookup("FALSE") == 0,
+ "pp::BoolDatatype::lookup(string)");
+ TEST_ASSERT(b.lookup("TRUE") == 1,
+ "pp::BoolDatatype::lookup(string)");
+ TEST_ASSERT(b.lookup(pp::Value(0)) == 0,
+ "pp::BoolDatatype::lookup(int)");
+ TEST_ASSERT(b.lookup(1) == 1,
+ "pp::BoolDatatype::lookup(int)");
+ TEST_ASSERT(b.lookup(4) == 1,
+ "pp::BoolDatatype::lookup(int)");
+ try {
+ b.lookup("foo");
+ TEST_FAIL("pp::BoolDatatype::lookup(string)");
+ } catch (pp::Datatype::InvalidError &e) {
+ }
+}
+
+TEST(test_pp_bitmask_datatype)
+{
+ // test the basic constructor
+ pp::BitmaskDatatype b;
+
+ // test the evaluate() method
+ b.add_bit("bit_one", 1);
+ b.add_bit("bit_two", 2);
+ b.add_bit("bit_three", 3);
+ if (b.evaluate(2) != "bit_one") {
+ TEST_FAIL("pp::BitmaskDatatype::evaluate()");
+ }
+ if (b.evaluate(1) != "<!0!>") {
+ TEST_FAIL("pp::BitmaskDatatype::evaluate()");
+ }
+ if (b.evaluate(6) != "bit_one bit_two") {
+ TEST_FAIL("pp::BitmaskDatatype::evaluate()");
+ }
+ if (b.evaluate(0) != "") {
+ TEST_FAIL("pp::BitmaskDatatype::evaluate()");
+ }
+
+ // test lookup()
+ TEST_ASSERT(b.lookup("bit_one") == 1,
+ "pp::BitmaskDatatype::lookup(string)");
+ TEST_ASSERT(b.lookup("bit_two") == 2,
+ "pp::BitmaskDatatype::lookup(string)");
+ TEST_ASSERT(b.lookup(1) == 1,
+ "pp::BitmaskDatatype::lookup(int)");
+ TEST_ASSERT(b.lookup(2) == 2,
+ "pp::BitmaskDatatype::lookup(int)");
+ try {
+ b.lookup("foo");
+ TEST_FAIL("pp::BitmaskDatatype::lookup(string)");
+ } catch (pp::Datatype::InvalidError &e) {
+ }
+ try {
+ b.lookup(4);
+ TEST_FAIL("pp::BitmaskDatatype::lookup(int)");
+ } catch (pp::Datatype::InvalidError &e) {
+ }
+}
+
+TEST(test_pp_int_datatype)
+{
+ // test the basic constructor
+ pp::IntDatatype i1;
+
+ // test the evaluate() method
+ if (i1.evaluate(1) != "1") {
+ TEST_FAIL("pp::IntDatatype::evaluate()");
+ }
+ if (i1.evaluate(-1) != "-1") {
+ TEST_FAIL("pp::IntDatatype::evaluate()");
+ }
+
+ // test lookup()
+ TEST_ASSERT(i1.lookup("1") == 1,
+ "pp::IntDatatype::lookup(string)");
+ TEST_ASSERT(i1.lookup("23") == 23,
+ "pp::IntDatatype::lookup(string)");
+ TEST_ASSERT(i1.lookup(1) == 1, "pp::IntDatatype::lookup(int)");
+ TEST_ASSERT(i1.lookup(23) == 23, "pp::IntDatatype::lookup(int)");
+ try {
+ i1.lookup("foo");
+ TEST_FAIL("pp::IntDatatype::lookup(string)");
+ } catch (pp::Datatype::InvalidError &e) {
+ }
+
+ // test the units constructor
+ pp::IntDatatype i2("units");
+
+ // test the evaluate() method
+ if (i2.evaluate(1) != "1 units") {
+ TEST_FAIL("pp::IntDatatype::evaluate()");
+ }
+ if (i2.evaluate(-1) != "-1 units") {
+ TEST_FAIL("pp::IntDatatype::evaluate()");
+ }
+
+ // test lookup()
+ //FIXME: this test fails because bignum fails to parse "1 units"
+ // and returns 0, yet "1 " parses just fine.
+ #if 0
+ TEST_ASSERT(i2.lookup("1 units") == 1,
+ "pp::IntDatatype::lookup(string)");
+ TEST_ASSERT(i2.lookup("23 units") == 23,
+ "pp::IntDatatype::lookup(string)");
+ #endif
+ TEST_ASSERT(i2.lookup(1) == 1,
+ "pp::IntDatatype::lookup(int)");
+ TEST_ASSERT(i2.lookup(23) == 23,
+ "pp::IntDatatype::lookup(int)");
+ try {
+ i2.lookup("foo");
+ TEST_FAIL("pp::IntDatatype::lookup(string)");
+ } catch (pp::Datatype::InvalidError &e) {
+ }
+}
+
+TEST(test_pp_hex_datatype)
+{
+ // test the basic constructor
+ pp::HexDatatype h1;
+
+ // test the evaluate() method
+ if (h1.evaluate(1) != "0x1") {
+ TEST_FAIL("pp::HexDatatype::evaluate()");
+ }
+
+ // test lookup()
+ TEST_ASSERT(h1.lookup("0x1") == 1,
+ "pp::HexDatatype::lookup(string)");
+ TEST_ASSERT(h1.lookup("0x23") ==35,
+ "pp::HexDatatype::lookup(string)");
+ TEST_ASSERT(h1.lookup(1) == 1,
+ "pp::HexDatatype::lookup(int)");
+ TEST_ASSERT(h1.lookup(23) == 23,
+ "pp::HexDatatype::lookup(int)");
+ try {
+ h1.lookup("foo");
+ TEST_FAIL("pp::HexDatatype::lookup(string)");
+ } catch (pp::Datatype::InvalidError &e) {
+ }
+
+ // test the units constructor
+ pp::HexDatatype h2(pp::BITS0, "units");
+ if (h2.evaluate(1) != "0x1 units") {
+ TEST_FAIL("pp::HexDatatype::evaluate()");
+ }
+
+ // test lookup()
+ //FIXME: this test fails because bignum fails to parse "1 units"
+ // and returns 0, yet "1 " parses just fine.
+ #if 0
+ TEST_ASSERT(h1.lookup("0x1 units") == 1,
+ "pp::HexDatatype::lookup(string)");
+ TEST_ASSERT(h1.lookup("0x23 units") ==35,
+ "pp::HexDatatype::lookup(string)");
+ #endif
+ TEST_ASSERT(h1.lookup(1) == 1,
+ "pp::HexDatatype::lookup(int)");
+ TEST_ASSERT(h1.lookup(23) == 23,
+ "pp::HexDatatype::lookup(int)");
+ try {
+ h1.lookup("foo");
+ TEST_FAIL("pp::HexDatatype::lookup(string)");
+ } catch (pp::Datatype::InvalidError &e) {
+ }
+
+ // test the width constructor
+ pp::HexDatatype h3(pp::BITS8);
+
+ // test the evaluate() method
+ if (h3.evaluate(1) != "0x01") {
+ TEST_FAIL("pp::HexDatatype::evaluate()");
+ }
+ pp::HexDatatype h4(pp::BITS16);
+ if (h4.evaluate(0x0201) != "0x0201") {
+ TEST_FAIL("pp::HexDatatype::evaluate()");
+ }
+ pp::HexDatatype h5(pp::BITS32);
+ if (h5.evaluate(0x04030201) != "0x04030201") {
+ TEST_FAIL("pp::HexDatatype::evaluate()");
+ }
+ pp::HexDatatype h6(pp::BITS64);
+ if (h6.evaluate(pp::Value("0x0807060504030201")) !=
+ "0x0807060504030201") {
+ TEST_FAIL("pp::HexDatatype::evaluate()");
+ }
+
+ // test the units-and-width constructor
+ pp::HexDatatype h7(pp::BITS8, "units");
+
+ // test the evaluate() method
+ if (h7.evaluate(1) != "0x01 units") {
+ TEST_FAIL("pp::HexDatatype::evaluate()");
+ }
+ pp::HexDatatype h8(pp::BITS16, "units");
+ if (h8.evaluate(0x0201) != "0x0201 units") {
+ TEST_FAIL("pp::HexDatatype::evaluate()");
+ }
+ pp::HexDatatype h9(pp::BITS32, "units");
+ if (h9.evaluate(0x04030201) != "0x04030201 units") {
+ TEST_FAIL("pp::HexDatatype::evaluate()");
+ }
+ pp::HexDatatype h10(pp::BITS64, "units");
+ if (h10.evaluate(pp::Value("0x0807060504030201")) !=
+ "0x0807060504030201 units") {
+ TEST_FAIL("h10.evaluate()");
+ }
+}
+
+TEST(test_pp_transform_datatype)
+{
+ // test the basic constructor
+ pp::TransformDatatypePtr t1 = new_pp_transform_datatype(
+ new_pp_int_datatype(), _1+1, _1-1);
+
+ // test the evaluate() method
+ if (t1->evaluate(0) != "1") {
+ TEST_FAIL("pp::TransformDatatype::evaluate()");
+ }
+ if (t1->evaluate(1) != "2") {
+ TEST_FAIL("pp::TransformDatatype::evaluate()");
+ }
+
+ // test lookup()
+ TEST_ASSERT(t1->lookup("1") == 0,
+ "pp::TransformDatatype::lookup(string)");
+ TEST_ASSERT(t1->lookup("23") == 22,
+ "pp::TransformDatatype::lookup(string)");
+ TEST_ASSERT(t1->lookup(1) == 0,
+ "pp::TransformDatatype::lookup(int)");
+ TEST_ASSERT(t1->lookup(23) == 22,
+ "pp::TransformDatatype::lookup(int)");
+ try {
+ t1->lookup("foo");
+ TEST_FAIL("pp::TransformDatatype::lookup(string)");
+ } catch (pp::Datatype::InvalidError &e) {
+ }
+}
+
+TEST(test_pp_fixed_datatype)
+{
+ // test the basic constructor
+ pp::FixedDatatypePtr f1 = new_pp_fixed_datatype(2);
+
+ // test the evaluate() method
+ std::string str;
+ str = f1->evaluate(0);
+ TEST_ASSERT(str == "0.0")
+ << "pp::FixedDatatype::evaluate(): " << str;
+ str = f1->evaluate(1);
+ TEST_ASSERT(str == "0.25")
+ << "pp::FixedDatatype::evaluate(): " << str;
+ str = f1->evaluate(2);
+ TEST_ASSERT(str == "0.5")
+ << "pp::FixedDatatype::evaluate(): " << str;
+ str = f1->evaluate(3);
+ TEST_ASSERT(str == "0.75")
+ << "pp::FixedDatatype::evaluate(): " << str;
+ str = f1->evaluate(4);
+ TEST_ASSERT(str == "1.0")
+ << "pp::FixedDatatype::evaluate(): " << str;
+ str = f1->evaluate(5);
+ TEST_ASSERT(str == "1.25")
+ << "pp::FixedDatatype::evaluate(): " << str;
+ str = f1->evaluate(6);
+ TEST_ASSERT(str == "1.5")
+ << "pp::FixedDatatype::evaluate(): " << str;
+ str = f1->evaluate(7);
+ TEST_ASSERT(str == "1.75")
+ << "pp::FixedDatatype::evaluate(): " << str;
+
+ // test lookup
+ // FIXME:
+}
=======================================
--- /dev/null
+++ /trunk/tests/dirent_test.cpp Sun Nov 8 23:26:22 2009
@@ -0,0 +1,75 @@
+#include "pp/pp.h"
+#include "pp/dirent.h"
+#include "pp/field_types.h"
+#include "pp/register.h"
+#include "pp/scope.h"
+#include "pp/datatype_types.h"
+#include "pp_test.h"
+
+class TestDirent: public pp::Dirent
+{
+ public:
+ TestDirent(pp::DirentType type): pp::Dirent(type)
+ {
+ }
+ virtual pp::Value
+ read() const
+ {
+ return 0;
+ }
+ virtual void
+ write(const pp::Value &val) const
+ {
+ (void)val;
+ }
+};
+
+TEST(test_pp_dirent)
+{
+ /* test the field constructor */
+ pp::DirentPtr dirent(new TestDirent(pp::DIRENT_TYPE_FIELD));
+
+ /* test the dirent_type() method */
+ if (dirent->dirent_type() != pp::DIRENT_TYPE_FIELD) {
+ TEST_FAIL("pp::Dirent::dirent_type()");
+ }
+ if (!dirent->is_field()) {
+ TEST_FAIL("pp::Dirent::is_field()");
+ }
+ if (dirent->is_register()) {
+ TEST_FAIL("pp::Dirent::is_register()");
+ }
+
+ if (to_string(pp::DIRENT_TYPE_FIELD) != "Field") {
+ TEST_FAIL("pp::Dirent::operator<<(ostream)");
+ }
+ if (to_string(pp::DIRENT_TYPE_REGISTER) != "Register") {
+ TEST_FAIL("pp::Dirent::operator<<(ostream)");
+ }
+ if (to_string(pp::DIRENT_TYPE_SCOPE) != "Scope") {
+ TEST_FAIL("pp::Dirent::operator<<(ostream)");
+ }
+ if (to_string(pp::DIRENT_TYPE_ARRAY) != "Array") {
+ TEST_FAIL("pp::Dirent::operator<<(ostream)");
+ }
+ if (to_string(pp::DIRENT_TYPE_ALIAS) != "Alias") {
+ TEST_ERROR("pp::Dirent::operator<<(ostream)");
+ }
+
+ /* test up-casting */
+ try {
+ pp::field_from_dirent(dirent);
+ } catch (pp::Dirent::ConversionError &e) {
+ TEST_FAIL("pp::field_from_dirent()");
+ }
+ try {
+ pp::register_from_dirent(dirent);
+ TEST_FAIL("pp::register_from_dirent()");
+ } catch (pp::Dirent::ConversionError &e) {
+ }
+ try {
+ pp::scope_from_dirent(dirent);
+ TEST_FAIL("pp::scope_from_dirent()");
+ } catch (pp::Dirent::ConversionError &e) {
+ }
+}
=======================================
--- /dev/null
+++ /trunk/tests/field_test.cpp Sun Nov 8 23:26:22 2009
@@ -0,0 +1,216 @@
+#include "pp/pp.h"
+#include "pp/field_types.h"
+#include "pp/datatype_types.h"
+#include "pp/register_types.h"
+#include "pp/regbits.h"
+#include "test_binding.h"
+#include "pp_test.h"
+
+//FIXME: test lookup()
+//FIXME: test compare()
+
+TEST(test_int_field)
+{
+ /* two bindings with one reg each */
+ pp::BindingPtr bind1 = new_test_binding();
+ bind1->write(0, pp::BITS16, 0x1111);
+ pp::RegisterPtr r1 = new_pp_bound_register(bind1, 1, pp::BITS16);
+ pp::BindingPtr bind2 = new_test_binding();
+ bind2->write(0, pp::BITS16, 0x2222);
+ pp::RegisterPtr r2 = new_pp_bound_register(bind2, 1, pp::BITS16);
+
+ pp::DatatypePtr integer = new_pp_int_datatype();
+ pp::DirectField f1(integer,
+ pp::RegBits(r2, 7, 0) + pp::RegBits(r1, 7, 0));
+
+ /* test read() */
+ if (f1.read() != 8721) {
+ TEST_FAIL("pp::DirectField::read()");
+ }
+ if (f1.evaluate() != "8721") {
+ TEST_FAIL("pp::DirectField::evaluate()");
+ }
+
+ /* test write */
+ f1.write(0x0102);
+ if (f1.read() != 0x0102) {
+ TEST_FAIL("pp::DirectField::write()");
+ }
+ if (r1->read() != 0x1102) {
+ TEST_FAIL("pp::DirectField::write()");
+ }
+ if (r2->read() != 0x2201) {
+ TEST_FAIL("pp::DirectField::write()");
+ }
+}
+
+TEST(test_hex_field)
+{
+ /* two bindings with one reg each */
+ pp::BindingPtr bind1 = new_test_binding();
+ bind1->write(0, pp::BITS16, 0x1111);
+ pp::RegisterPtr r1 = new_pp_bound_register(bind1, 1, pp::BITS16);
+ pp::BindingPtr bind2 = new_test_binding();
+ bind2->write(0, pp::BITS16, 0x2222);
+ pp::RegisterPtr r2 = new_pp_bound_register(bind2, 1, pp::BITS16);
+
+ /* test a hex16 field */
+ pp::DatatypePtr hex16 = new_pp_hex_datatype(pp::BITS16);
+ pp::DirectField f1(hex16,
+ pp::RegBits(r2, 7, 0) + pp::RegBits(r1, 7, 0));
+
+ /* test read() */
+ if (f1.read() != 0x2211) {
+ TEST_FAIL("pp::DirectField::read()");
+ }
+ if (f1.evaluate() != "0x2211") {
+ TEST_FAIL("pp::DirectField::evaluate()");
+ }
+
+ /* test write */
+ f1.write(0x0102);
+ if (f1.read() != 0x0102) {
+ TEST_FAIL("pp::DirectField::write()");
+ }
+ if (r1->read() != 0x1102) {
+ TEST_FAIL("pp::DirectField::write()");
+ }
+ if (r2->read() != 0x2201) {
+ TEST_FAIL("pp::DirectField::write()");
+ }
+}
+
+TEST(test_enum_field)
+{
+ /* two bindings with one reg each */
+ pp::BindingPtr bind1 = new_test_binding();
+ bind1->write(0, pp::BITS16, 0x1111);
+ pp::RegisterPtr r1 = new_pp_bound_register(bind1, 1, pp::BITS16);
+ pp::BindingPtr bind2 = new_test_binding();
+ bind2->write(0, pp::BITS16, 0x2222);
+ pp::RegisterPtr r2 = new_pp_bound_register(bind2, 1, pp::BITS16);
+
+ /* test an enum field */
+ pp::EnumDatatypePtr e1 = new_pp_enum_datatype();
+ e1->add_value("one", 1);
+ e1->add_value("two", 2);
+ e1->add_value("three", 3);
+ e1->add_value("correct", 0x2211);
+ pp::DirectFieldPtr f1 = new_pp_direct_field(e1,
+ pp::RegBits(r2, 7, 0) + pp::RegBits(r1, 7, 0));
+
+ /* test read() */
+ if (f1->read() != 0x2211) {
+ TEST_FAIL("pp::DirectField::read()");
+ }
+ if (f1->evaluate() != "correct") {
+ TEST_FAIL("pp::DirectField::evaluate()");
+ }
+
+ /* test write */
+ //FIXME: write by string? or lookup by string?
+ f1->write(1);
+ if (f1->read() != 0x1) {
+ TEST_FAIL("pp::DirectField::write()");
+ }
+ if (r1->read() != 0x1101) {
+ TEST_FAIL("pp::DirectField::write()");
+ }
+ if (r2->read() != 0x2200) {
+ TEST_FAIL("pp::DirectField::write()");
+ }
+}
+
+TEST(test_bitmask_field)
+{
+ /* two bindings with one reg each */
+ pp::BindingPtr bind1 = new_test_binding();
+ bind1->write(0, pp::BITS16, 0x1111);
+ pp::RegisterPtr r1 = new_pp_bound_register(bind1, 1, pp::BITS16);
+ pp::BindingPtr bind2 = new_test_binding();
+ bind2->write(0, pp::BITS16, 0x2222);
+ pp::RegisterPtr r2 = new_pp_bound_register(bind2, 1, pp::BITS16);
+
+ /* test a bitmask field */
+ pp::BitmaskDatatypePtr b1 = new_pp_bitmask_datatype();
+ b1->add_bit("zero", 0);
+ b1->add_bit("one", 1);
+ b1->add_bit("two", 2);
+ b1->add_bit("three", 3);
+ b1->add_bit("four", 4);
+ b1->add_bit("nine", 9);
+ b1->add_bit("thirteen", 13);
+ pp::DirectFieldPtr f1 = new_pp_direct_field(b1,
+ pp::RegBits(r2, 7, 0) + pp::RegBits(r1, 7, 0));
+
+ /* test read() */
+ if (f1->read() != 0x2211) {
+ TEST_FAIL("pp::DirectField::read()");
+ }
+ if (f1->evaluate() != "zero four nine thirteen") {
+ TEST_FAIL("pp::DirectField::evaluate()");
+ }
+
+ /* test write */
+ //FIXME: write by string? or lookup by string?
+ f1->write(0x207);
+ if (f1->read() != 0x207) {
+ TEST_FAIL("pp::DirectField::write()");
+ }
+ if (r1->read() != 0x1107) {
+ TEST_FAIL("pp::DirectField::write()");
+ }
+ if (r2->read() != 0x2202) {
+ TEST_FAIL("pp::DirectField::write()");
+ }
+}
+
+class TestProcs: public pp::RwProcs
+{
+ private:
+ mutable pp::Value m_data;
+ public:
+ virtual pp::Value
+ read() const
+ {
+ return m_data;
+ }
+ virtual void
+ write(const pp::Value &value) const
+ {
+ m_data = value;
+ }
+};
+
+TEST(test_proc_field)
+{
+ // without this, there is no "current context"
+ pp::ScopePtr root = pp::initialize_device_tree();
+
+ pp::DatatypePtr hex = new_pp_hex_datatype();
+ pp::RwProcsPtr procs(new TestProcs);
+ pp::ProcField f(hex, procs);
+ f.write(0x12345678);
+ if (f.read() != 0x12345678) {
+ TEST_FAIL("pp::ProcField::write()");
+ }
+ if (f.evaluate() != "0x12345678") {
+ TEST_FAIL("pp::ProcField::write()");
+ }
+}
+
+TEST(test_constant_field)
+{
+ pp::DatatypePtr hex = new_pp_hex_datatype();
+ pp::ConstantField f(hex, 0x12345678);
+ if (f.read() != 0x12345678) {
+ TEST_FAIL("pp::ConstantField::read()");
+ }
+ if (f.evaluate() != "0x12345678") {
+ TEST_FAIL("pp::ConstantField::read()");
+ }
+ f.write(0);
+ if (f.read() != 0x12345678) {
+ TEST_FAIL("pp::ConstantField::write()");
+ }
+}
=======================================
--- /dev/null
+++ /trunk/tests/path_test.cpp Sun Nov 8 23:26:22 2009
@@ -0,0 +1,808 @@
+#include "pp/pp.h"
+#include "pp/path.h"
+#include <iostream>
+#include "pp_test.h"
+
+TEST(test_element)
+{
+ {
+ pp::Path::Element e("foo");
+ TEST_ASSERT(e.to_string() == "foo",
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.equals(pp::Path::Element("foo")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("bar")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_array() == false,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_bookmark() == false,
+ "pp::Path::Element::Element()");
+ }
+ {
+ pp::Path::Element e("%foo");
+ TEST_ASSERT(e.to_string() == "%foo",
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.equals(pp::Path::Element("%foo")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("%bar")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_array() == false,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_bookmark() == false,
+ "pp::Path::Element::Element()");
+ }
+ {
+ pp::Path::Element e("foo_123");
+ TEST_ASSERT(e.to_string() == "foo_123",
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.equals(pp::Path::Element("foo_123")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("foo_456")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_array() == false,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_bookmark() == false,
+ "pp::Path::Element::Element()");
+ }
+ {
+ pp::Path::Element e("foo[]");
+ TEST_ASSERT(e.to_string() == "foo[]",
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.equals(pp::Path::Element("foo[]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("foo")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("bar[]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_array() == true,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.array_mode() == e.ARRAY_APPEND,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_bookmark() == false,
+ "pp::Path::Element::Element()");
+ }
+ {
+ pp::Path::Element e("foo[0]");
+ TEST_ASSERT(e.to_string() == "foo[0]",
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.equals(pp::Path::Element("foo[0]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("foo")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("bar[0]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_array() == true,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.array_mode() == e.ARRAY_INDEX,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.array_index() == 0,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_bookmark() == false,
+ "pp::Path::Element::Element()");
+ }
+ {
+ pp::Path::Element e("foo[010]");
+ TEST_ASSERT(e.to_string() == "foo[8]",
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.equals(pp::Path::Element("foo[010]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("foo")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("bar[010]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.equals(pp::Path::Element("foo[8]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("bar[8]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_array() == true,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.array_mode() == e.ARRAY_INDEX,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.array_index() == 8,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_bookmark() == false,
+ "pp::Path::Element::Element()");
+ }
+ {
+ pp::Path::Element e("foo[0x10]");
+ TEST_ASSERT(e.to_string() == "foo[16]",
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.equals(pp::Path::Element("foo[0x10]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("foo")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("bar[0x10]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.equals(pp::Path::Element("foo[16]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("bar[16]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_array() == true,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.array_mode() == e.ARRAY_INDEX,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.array_index() == 16,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_bookmark() == false,
+ "pp::Path::Element::Element()");
+ }
+ {
+ pp::Path::Element e("foo[-1]");
+ TEST_ASSERT(e.to_string() == "foo[-1]",
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.equals(pp::Path::Element("foo[-1]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("foo")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("bar[-1]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_array() == true,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.array_mode() == e.ARRAY_INDEX,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.array_index() == -1,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_bookmark() == false,
+ "pp::Path::Element::Element()");
+ }
+ {
+ pp::Path::Element e("foo[-2]");
+ TEST_ASSERT(e.to_string() == "foo[-2]",
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.equals(pp::Path::Element("foo[-2]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("foo")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("bar[-2]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_array() == true,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.array_mode() == e.ARRAY_INDEX,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.array_index() == -2,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_bookmark() == false,
+ "pp::Path::Element::Element()");
+ }
+ {
+ pp::Path::Element e("foo[-010]");
+ TEST_ASSERT(e.to_string() == "foo[-8]",
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.equals(pp::Path::Element("foo[-8]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("foo")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("bar[-8]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_array() == true,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.array_mode() == e.ARRAY_INDEX,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.array_index() == -8,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_bookmark() == false,
+ "pp::Path::Element::Element()");
+ }
+ {
+ pp::Path::Element e("foo[-0x10]");
+ TEST_ASSERT(e.to_string() == "foo[-16]",
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.equals(pp::Path::Element("foo[-16]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("foo")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!e.equals(pp::Path::Element("bar[-16]")),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_array() == true,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.array_mode() == e.ARRAY_INDEX,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.array_index() == -16,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_bookmark() == false,
+ "pp::Path::Element::Element()");
+ }
+ {
+ pp::Path::Element e("_");
+ TEST_ASSERT(e.to_string() == "_",
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_array() == false,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_bookmark() == false,
+ "pp::Path::Element::Element()");
+ }
+ {
+ pp::Path::Element e("$foo");
+ TEST_ASSERT(e.to_string() == "$foo",
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_array() == false,
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(e.is_bookmark() == true,
+ "pp::Path::Element::Element()");
+ }
+ {
+ try {
+ pp::Path::Element e("foo$bar");
+ TEST_FAIL("pp::Path::pp::Path()");
+ } catch (pp::Path::InvalidError &e) {
+ }
+ }
+ {
+ try {
+ pp::Path::Element e("$123");
+ TEST_FAIL("pp::Path::pp::Path()");
+ } catch (pp::Path::InvalidError &e) {
+ }
+ }
+ {
+ try {
+ pp::Path::Element e("$");
+ TEST_FAIL("pp::Path::pp::Path()");
+ } catch (pp::Path::InvalidError &e) {
+ }
+ }
+ {
+ try {
+ pp::Path::Element e("$$");
+ TEST_FAIL("pp::Path::pp::Path()");
+ } catch (pp::Path::InvalidError &e) {
+ }
+ }
+ {
+ try {
+ pp::Path::Element e("123");
+ TEST_FAIL("pp::Path::pp::Path()");
+ } catch (pp::Path::InvalidError &e) {
+ }
+ }
+ {
+ try {
+ pp::Path::Element e("");
+ TEST_FAIL("pp::Path::pp::Path()");
+ } catch (pp::Path::InvalidError &e) {
+ }
+ }
+}
+
+TEST(test_ctors)
+{
+ // test the default ctor
+ {
+ pp::Path path;
+ TEST_ASSERT(path == "",
+ "pp::Path::pp::Path()");
+ TEST_ASSERT(!path.is_initialized(),
+ "pp::Path::pp::Path()");
+ TEST_ASSERT(!path.is_absolute(),
+ "pp::Path::pp::Path()");
+ }
+
+ // test construction from a string
+ {
+ pp::Path path("");
+ TEST_ASSERT(path == "",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(!path.is_initialized(),
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 0,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(!path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+ {
+ pp::Path path(string(""));
+ TEST_ASSERT(path == "",
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(!path.is_initialized(),
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(path.size() == 0,
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(!path.is_absolute(),
+ "pp::Path::pp::Path(string)");
+ }
+ {
+ pp::Path path("/");
+ TEST_ASSERT(path == "/",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.is_initialized(),
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 0,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+ {
+ pp::Path path(string("/"));
+ TEST_ASSERT(path == "/",
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(path.is_initialized(),
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(path.size() == 0,
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(path.is_absolute(),
+ "pp::Path::pp::Path(string)");
+ }
+
+ {
+ pp::Path path("a");
+ TEST_ASSERT(path == "a",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.is_initialized(),
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 1,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(!path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+ {
+ pp::Path path(string("a"));
+ TEST_ASSERT(path == "a",
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(path.is_initialized(),
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(path.size() == 1,
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(!path.is_absolute(),
+ "pp::Path::pp::Path(string)");
+ }
+ {
+ pp::Path path("/a");
+ TEST_ASSERT(path == "/a",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.is_initialized(),
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 1,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+ {
+ pp::Path path(string("/a"));
+ TEST_ASSERT(path == "/a",
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(path.is_initialized(),
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(path.size() == 1,
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(path.is_absolute(),
+ "pp::Path::pp::Path(string)");
+ }
+
+ {
+ pp::Path path("a/b");
+ TEST_ASSERT(path == "a/b",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 2,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(!path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+ {
+ pp::Path path(string("a/b"));
+ TEST_ASSERT(path == "a/b",
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(path.size() == 2,
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(!path.is_absolute(),
+ "pp::Path::pp::Path(string)");
+ }
+ {
+ pp::Path path("/a/b");
+ TEST_ASSERT(path == "/a/b",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 2,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+ {
+ pp::Path path(string("/a/b"));
+ TEST_ASSERT(path == "/a/b",
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(path.size() == 2,
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(path.is_absolute(),
+ "pp::Path::pp::Path(string)");
+ }
+
+ {
+ pp::Path path("a/b/c");
+ TEST_ASSERT(path == "a/b/c",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 3,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(!path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+ {
+ pp::Path path(string("a/b/c"));
+ TEST_ASSERT(path == "a/b/c",
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(path.size() == 3,
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(!path.is_absolute(),
+ "pp::Path::pp::Path(string)");
+ }
+ {
+ pp::Path path("/a/b/c");
+ TEST_ASSERT(path == "/a/b/c",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 3,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+ {
+ pp::Path path(string("/a/b/c"));
+ TEST_ASSERT(path == "/a/b/c",
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(path.size() == 3,
+ "pp::Path::pp::Path(string)");
+ TEST_ASSERT(path.is_absolute(),
+ "pp::Path::pp::Path(string)");
+ }
+
+ // test whitespace chomping
+ {
+ pp::Path path(" ");
+ TEST_ASSERT(path == "",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 0,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(!path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+ {
+ pp::Path path("/ ");
+ TEST_ASSERT(path == "/",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 0,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+ {
+ pp::Path path(" / ");
+ TEST_ASSERT(path == "/",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 0,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+ {
+ pp::Path path(" /");
+ TEST_ASSERT(path == "/",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 0,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+ {
+ pp::Path path(" a/b ");
+ TEST_ASSERT(path == "a/b",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 2,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(!path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+
+ // test delimiter collapsing
+ {
+ pp::Path path("////");
+ TEST_ASSERT(path == "/",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 0,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+ {
+ pp::Path path("a////b");
+ TEST_ASSERT(path == "a/b",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 2,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(!path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+ {
+ pp::Path path("////a////b");
+ TEST_ASSERT(path == "/a/b",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.size() == 2,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+ {
+ try {
+ pp::Path path("123");
+ TEST_FAIL("pp::Path::pp::Path()");
+ } catch (pp::Path::InvalidError &e) {
+ }
+ }
+
+ // test carats
+ {
+ pp::Path path("$foo");
+ TEST_ASSERT(path == "$foo",
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!path.is_absolute(),
+ "pp::Path::is_absolute()");
+ }
+ {
+ pp::Path path("$foo/foo");
+ TEST_ASSERT(path == "$foo/foo",
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(path != pp::Path("$foo/bar"),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(path != pp::Path("$bar/foo"),
+ "pp::Path::Element::Element()");
+ TEST_ASSERT(!path.is_absolute(),
+ "pp::Path::is_absolute()");
+ }
+ {
+ pp::Path path("/$foo/foo");
+ TEST_ASSERT(path == "/$foo/foo",
+ "pp::Path::pp::Path()");
+ TEST_ASSERT(path != pp::Path("/$foo/bar"),
+ "pp::Path::pp::Path()");
+ TEST_ASSERT(path != pp::Path("/$bar/foo"),
+ "pp::Path::pp::Path()");
+ TEST_ASSERT(path.is_absolute(),
+ "pp::Path::is_absolute()");
+ }
+
+ // test copy construction
+ {
+ pp::Path path("a/b");
+ pp::Path path2(path);
+ TEST_ASSERT(path2 == "a/b",
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(path2.size() == 2,
+ "pp::Path::pp::Path(char *)");
+ TEST_ASSERT(!path2.is_absolute(),
+ "pp::Path::pp::Path(char *)");
+ }
+}
+
+TEST(test_comparisons)
+{
+ {
+ pp::Path left("a/b/c");
+ pp::Path right("a/b/c");
+ TEST_ASSERT(left.equals(right), "pp::Path::equals()");
+ TEST_ASSERT(left == right, "pp::Path::operator==()");
+ }
+ {
+ pp::Path left("a/b/c");
+ pp::Path right("d/e/f");
+ TEST_ASSERT(!left.equals(right), "pp::Path::equals()");
+ TEST_ASSERT(left != right, "pp::Path::operator!=()");
+ }
+ {
+ pp::Path left("a/b/c");
+ string right("a/b/c");
+ TEST_ASSERT(left == right, "pp::Path::operator==()");
+ }
+ {
+ pp::Path left("a/b/c");
+ string right("d/e/f");
+ TEST_ASSERT(left != right, "pp::Path::operator!=()");
+ }
+ {
+ string left("a/b/c");
+ pp::Path right("a/b/c");
+ TEST_ASSERT(left == right, "pp::Path::operator==()");
+ }
+ {
+ string left("a/b/c");
+ pp::Path right("d/e/f");
+ TEST_ASSERT(left != right, "pp::Path::operator!=()");
+ }
+}
+
+TEST(test_iters)
+{
+ pp::Path path("a/b/c");
+
+ pp::Path::iterator it = path.begin();
+ TEST_ASSERT(it != path.end(), "pp::Path::end()");
+ TEST_ASSERT(*it == "a", "pp::Path::begin()");
+ it++;
+ TEST_ASSERT(it != path.end(), "pp::Path::end()");
+ TEST_ASSERT(*it == "b", "pp::Path::iterator::operator++()");
+ it++;
+ TEST_ASSERT(it != path.end(), "pp::Path::end()");
+ TEST_ASSERT(*it == "c", "pp::Path::iterator::operator++()");
+ it++;
+ TEST_ASSERT(it == path.end(), "pp::Path::end()");
+ it--;
+ TEST_ASSERT(it != path.end(), "pp::Path::end()");
+ TEST_ASSERT(*it == "c", "pp::Path::iterator::operator--()");
+ it--;
+ TEST_ASSERT(it != path.end(), "pp::Path::end()");
+ TEST_ASSERT(*it == "b", "pp::Path::iterator::operator--()");
+ it--;
+ TEST_ASSERT(it != path.end(), "pp::Path::end()");
+ TEST_ASSERT(it == path.begin(), "pp::Path::end()");
+ TEST_ASSERT(*it == "a", "pp::Path::iterator::operator--()");
+
+ pp::Path::reverse_iterator rit = path.rbegin();
+ TEST_ASSERT(rit != path.rend(), "pp::Path::rend()");
+ TEST_ASSERT(*rit == "c", "pp::Path::rbegin()");
+ rit++;
+ TEST_ASSERT(rit != path.rend(), "pp::Path::rend()");
+ TEST_ASSERT(*rit == "b", "pp::Path::iterator::operator++()");
+ rit++;
+ TEST_ASSERT(rit != path.rend(), "pp::Path::rend()");
+ TEST_ASSERT(*rit == "a", "pp::Path::iterator::operator++()");
+ rit++;
+ TEST_ASSERT(rit == path.rend(), "pp::Path::rend()");
+ rit--;
+ TEST_ASSERT(rit != path.rend(), "pp::Path::rend()");
+ TEST_ASSERT(*rit == "a", "pp::Path::iterator::operator--()");
+ rit--;
+ TEST_ASSERT(rit != path.rend(), "pp::Path::rend()");
+ TEST_ASSERT(*rit == "b", "pp::Path::iterator::operator--()");
+ rit--;
+ TEST_ASSERT(rit != path.rend(), "pp::Path::rend()");
+ TEST_ASSERT(rit == path.rbegin(), "pp::Path::rend()");
+ TEST_ASSERT(*rit == "c", "pp::Path::iterator::operator--()");
+}
+
+TEST(test_contents)
+{
+ pp::Path path;
+ pp::Path path2;
+ pp::Path::iterator it;
+
+ // create a path
+ path.push_back("a");
+ path.push_back("b");
+ path.push_back("c");
+ TEST_ASSERT(path.size() == 3, "pp::Path::size()");
+
+ it = path.begin();
+ TEST_ASSERT(*it == "a", "pp::Path::begin()");
+ it++;
+ TEST_ASSERT(*it == "b", "pp::Path::iterator::operator++()");
+ it++;
+ TEST_ASSERT(*it == "c", "pp::Path::iterator::operator++()");
+ it++;
+ TEST_ASSERT(it == path.end(), "pp::Path::end()");
+
+ // test front() and back()
+ TEST_ASSERT(path.front() == "a", "pp::Path::front()");
+ TEST_ASSERT(path.back() == "c", "pp::Path::back()");
+
+ // pop items
+ TEST_ASSERT(path.pop_front() == "a", "pp::Path::pop_front()");
+ TEST_ASSERT(path.pop_back() == "c", "pp::Path::pop_back()");
+ TEST_ASSERT(path.size() == 1, "pp::Path::size()");
+ TEST_ASSERT(path.front() == "b", "pp::Path::front()");
+ TEST_ASSERT(path.back() == "b", "pp::Path::back()");
+ path.pop_back();
+ TEST_ASSERT(path.size() == 0, "pp::Path::size()");
+
+ // cause exceptions
+ try {
+ path.pop_front();
+ TEST_FAIL("pp::Path::pop_front()");
+ } catch (std::out_of_range &e) {
+ }
+ try {
+ path.pop_back();
+ TEST_FAIL("pp::Path::pop_back()");
+ } catch (std::out_of_range &e) {
+ }
+ try {
+ path.front();
+ TEST_FAIL("pp::Path::front()");
+ } catch (std::out_of_range &e) {
+ }
+ try {
+ path.back();
+ TEST_FAIL("pp::Path::back()");
+ } catch (std::out_of_range &e) {
+ }
+
+ // test clear()
+ path = pp::Path("a/b/c");
+ TEST_ASSERT(path.size() == 3, "pp::Path::size()");
+ path.clear();
+ TEST_ASSERT(path.size() == 0, "pp::Path::size()");
+ TEST_ASSERT(path.is_initialized() == 0, "pp::Path::size()");
+
+ //test to_string()
+ path = pp::Path("a/b/c");
+ TEST_ASSERT(path.to_string() == "a/b/c",
+ "pp::Path::to_string()");
+ path = pp::Path("/a/b/c");
+ TEST_ASSERT(path.to_string() == "/a/b/c",
+ "pp::Path::to_string()");
+
+ // test catenation operators
+ // path += path
+ path = pp::Path("a/b/c");
+ path += pp::Path("d/e/f");
+ TEST_ASSERT(path == "a/b/c/d/e/f",
+ "pp::Path::operator+=(pp::Path, pp::Path)");
+ // path += string
+ path = pp::Path("a/b/c");
+ path += "d";
+ TEST_ASSERT(path == "a/b/c/d",
+ "pp::Path::operator+=(pp::Path, string)");
+ path = pp::Path("a/b/c");
+ path += "d/e/f";
+ TEST_ASSERT(path == "a/b/c/d/e/f",
+ "pp::Path::operator+=(pp::Path, string)");
+ // path + path
+ path = pp::Path("a/b/c") + pp::Path("d/e/f");
+ TEST_ASSERT(path == "a/b/c/d/e/f",
+ "pp::Path::operator+=(pp::Path, pp::Path)");
+ // path + string
+ path = pp::Path("a/b/c") + "d";
+ TEST_ASSERT(path == "a/b/c/d",
+ "pp::Path::operator+(pp::Path, string)");
+ path = pp::Path("a/b/c") + "d/e/f";
+ TEST_ASSERT(path == "a/b/c/d/e/f",
+ "pp::Path::operator+(pp::Path, string)");
+
+ // test insert
+ path = "a/b";
+ it = path.begin();
+ it++;
+ TEST_ASSERT(*it == "b", "pp::Path::iterator::operator++()");
+ path.insert(it, pp::Path::Element("c"));
+ TEST_ASSERT(path == "a/c/b", "pp::Path::insert(iterator, element)");
+ it = path.begin();
+ it++;
+ TEST_ASSERT(*it == "c", "pp::Path::iterator::operator++()");
+ path2 = "d/e/f";
+ path.insert(it, path2.begin(), path2.end());
+ TEST_ASSERT(path == "a/d/e/f/c/b",
+ "pp::Path::insert(iterator, iterator, iterator)");
+
+ // test erase()
+ path = pp::Path("a/b/c/d/e");
+ it = path.begin();
+ it++;
+ TEST_ASSERT(*it == "b", "pp::Path::iterator::operator++()");
+ path.erase(it);
+ TEST_ASSERT(path == "a/c/d/e", "pp::Path::erase(iterator)");
+ it = path.begin();
+ it++;
+ TEST_ASSERT(*it == "c", "pp::Path::iterator::operator++()");
+ path.erase(it, path.end());
+ TEST_ASSERT(path == "a", "pp::Path::erase(iterator, iterator)");
+
+ // test splice()
+ path = "a/b";
+ path2 = "c/d/e";
+ it = path.begin();
+ it++;
+ TEST_ASSERT(*it == "b", "pp::Path::iterator::operator++()");
+ path.splice(it, path2);
+ TEST_ASSERT(path == "a/c/d/e/b", "pp::Path::splice(iterator, pp::Path)");
+}
+
+TEST(test_const)
+{
+ pp::Path path("a/b/c");
+ const pp::Path &const_path = path;
+ TEST_ASSERT(const_path.size() == 3, "pp::Path::size()");
+
+ pp::Path::iterator it = path.begin();
+ pp::Path::const_iterator cit = const_path.begin();
+ TEST_ASSERT(*it == *cit, "pp::Path::begin()");
+ it++;
+ cit++;
+ TEST_ASSERT(*it == *cit, "pp::Path::iterator::operator++()");
+ it++;
+ cit++;
+ TEST_ASSERT(*it == *cit, "pp::Path::iterator::operator++()");
+}
=======================================
--- /dev/null
+++ /trunk/tests/regbits_test.cpp Sun Nov 8 23:26:22 2009
@@ -0,0 +1,155 @@
+#include "pp/pp.h"
+#include "pp/register_types.h"
+#include "pp/regbits.h"
+#include "test_binding.h"
+#include "pp_test.h"
+
+TEST(test_simple_regbits)
+{
+ // test ctors
+ {
+ pp::BindingPtr bind = new_test_binding();
+ pp::RegisterPtr reg =
+ new_pp_bound_register(bind, 0, pp::BITS16);
+ pp::RegBits rb;
+ TEST_ASSERT(rb.width() == 0,
+ "pp::RegBits::RegBits()");
+ }
+ {
+ pp::BindingPtr bind = new_test_binding();
+ pp::RegisterPtr reg =
+ new_pp_bound_register(bind, 0, pp::BITS16);
+ pp::RegBits rb(reg);
+ TEST_ASSERT(rb.read() == 0xffff,
+ "pp::RegBits::RegBits(Register)");
+ TEST_ASSERT(rb.width() == 16, "pp::RegBits::width()");
+ }
+ {
+ pp::BindingPtr bind = new_test_binding();
+ pp::RegisterPtr reg =
+ new_pp_bound_register(bind, 0, pp::BITS16);
+ pp::RegBits rb(reg, 7);
+ TEST_ASSERT(rb.read() == 0x1,
+ "pp::RegBits::RegBits(pp::Register, int)");
+ TEST_ASSERT(rb.width() == 1, "pp::RegBits::width()");
+ }
+ {
+ pp::BindingPtr bind = new_test_binding();
+ pp::RegisterPtr reg =
+ new_pp_bound_register(bind, 0, pp::BITS16);
+ pp::RegBits rb(reg, 3, 0);
+ TEST_ASSERT(rb.read() == 0xf,
+ "pp::RegBits::RegBits(pp::Register, int, int)");
+ TEST_ASSERT(rb.width() == 4, "pp::RegBits::width()");
+ }
+
+ // test an overly-large shift
+ try {
+ pp::BindingPtr bind = new_test_binding();
+ pp::RegisterPtr reg =
+ new_pp_bound_register(bind, 0, pp::BITS16);
+ pp::RegBits rb(reg, 32);
+ TEST_FAIL("pp::RegBits::RegBits()");
+ } catch (std::exception &e) {
+ }
+
+ // test write
+ {
+ pp::BindingPtr bind = new_test_binding();
+ pp::RegisterPtr reg =
+ new_pp_bound_register(bind, 0, pp::BITS16);
+ pp::RegBits rb(reg);
+ rb.write(0x98);
+ TEST_ASSERT(rb.read() == 0x98,
+ "pp::RegBits::write()");
+ TEST_ASSERT(reg->read() == 0x98,
+ "pp::RegBits::write()");
+ }
+ {
+ pp::BindingPtr bind = new_test_binding();
+ pp::RegisterPtr reg =
+ new_pp_bound_register(bind, 0, pp::BITS16);
+ pp::RegBits rb(reg, 7);
+ rb.write(0x00);
+ TEST_ASSERT(rb.read() == 0x0,
+ "pp::RegBits::write()");
+ TEST_ASSERT(reg->read() == 0xff7f,
+ "pp::RegBits::write()");
+ }
+ {
+ pp::BindingPtr bind = new_test_binding();
+ pp::RegisterPtr reg =
+ new_pp_bound_register(bind, 0, pp::BITS16);
+ pp::RegBits rb(reg, 3, 0);
+ rb.write(0x98);
+ TEST_ASSERT(rb.read() == 0x8,
+ "pp::RegBits::write()");
+ TEST_ASSERT(reg->read() == 0xfff8,
+ "pp::RegBits::write()");
+ }
+}
+
+TEST(test_complex_regbits)
+{
+ // test operator+
+ {
+ pp::BindingPtr bind = new_test_binding();
+ pp::RegisterPtr reg =
+ new_pp_bound_register(bind, 0, pp::BITS16);
+ reg->write(0x4321);
+ TEST_ASSERT(reg->read() == 0x4321,
+ "pp::BoundRegister::write()");
+
+ pp::RegBits rb = pp::RegBits(reg, 11, 8)
+ + pp::RegBits(reg, 3, 0);
+ TEST_ASSERT(rb.width() == 8, "pp::RegBits::width()");
+ TEST_ASSERT(rb.read() == 0x31, "pp::RegBits::write()");
+
+ rb.write(0x98);
+ TEST_ASSERT(rb.read() == 0x98, "pp::RegBits::write()");
+ TEST_ASSERT(reg->read() == 0x4928,
+ "pp::RegBits::write()");
+ }
+
+ // test operator+=
+ {
+ pp::BindingPtr bind = new_test_binding();
+ pp::RegisterPtr reg =
+ new_pp_bound_register(bind, 0, pp::BITS16);
+ reg->write(0x4321);
+ TEST_ASSERT(reg->read() == 0x4321,
+ "pp::BoundRegister::write()");
+
+ pp::RegBits rb(reg, 11, 8);
+ TEST_ASSERT(rb.width() == 4, "pp::RegBits::width()");
+ TEST_ASSERT(rb.read() == 0x3, "pp::RegBits::write()");
+ rb += pp::RegBits(reg, 3, 0);
+ TEST_ASSERT(rb.width() == 8, "pp::RegBits::width()");
+ TEST_ASSERT(rb.read() == 0x31, "pp::RegBits::write()");
+
+ rb.write(0x98);
+ TEST_ASSERT(rb.read() == 0x98, "pp::RegBits::write()");
+ TEST_ASSERT(reg->read() == 0x4928,
+ "pp::RegBits::write()");
+ }
+}
+
+TEST(test_exceptions)
+{
+ try {
+ pp::BindingPtr bind = new_test_binding();
+ pp::RegisterPtr reg =
+ new_pp_bound_register(bind, 0, pp::BITS16);
+ pp::RegBits rb(reg, 0, 15);
+ TEST_FAIL("pp::RegBits::RegBits(r, lo, hi)");
+ } catch (pp::RegBits::range_error &e) {
+ }
+ try {
+ pp::BindingPtr bind = new_test_binding();
+ pp::RegisterPtr reg =
+ new_pp_bound_register(bind, 0, pp::BITS16);
+ pp::RegBits rb(reg, 16);
+ TEST_FAIL("pp::RegBits::RegBits(r, too_hi)");
+ } catch (pp::RegBits::range_error &e) {
+ }
+}
=======================================
--- /dev/null
+++ /trunk/tests/register_test.cpp Sun Nov 8 23:26:22 2009
@@ -0,0 +1,171 @@
+#include "pp/pp.h"
+#include "pp/register_types.h"
+#include "pp/rwprocs.h"
+#include "test_binding.h"
+#include "pp_test.h"
+
+TEST(test_pp_bound_register)
+{
+ /* test the binding constructor */
+ pp::BindingPtr bind = new_test_binding();
+
+ /* set up test registers */
+ pp::BoundRegister r1(bind, 1, pp::BITS8);
+ pp::BoundRegister r2(bind, 2, pp::BITS16);
+ pp::BoundRegister r3(bind, 3, pp::BITS32);
+ pp::BoundRegister r4(bind, 4, pp::BITS64);
+ pp::BoundRegister r5(bind, 5, pp::BITS128);
+
+ /* test the read() method */
+ if (r1.read() != pp::MASK(pp::BITS8)) {
+ TEST_FAIL("pp::BoundRegister::read()");
+ }
+ if (r2.read() != pp::MASK(pp::BITS16)) {
+ TEST_FAIL("pp::BoundRegister::read()");
+ }
+ if (r3.read() != pp::MASK(pp::BITS32)) {
+ TEST_FAIL("pp::BoundRegister::read()");
+ }
+ if (r4.read() != pp::MASK(pp::BITS64)) {
+ TEST_FAIL("pp::BoundRegister::read()");
+ }
+ if (r5.read() != pp::MASK(pp::BITS128)) {
+ TEST_FAIL("pp::BoundRegister::read()");
+ }
+
+ /* test the write() method */
+ r1.write(0x11);
+ if (r1.read() != 0x11) {
+ TEST_FAIL("pp::BoundRegister::write()");
+ }
+ r2.write(0x2222);
+ if (r2.read() != 0x2222) {
+ TEST_FAIL("pp::BoundRegister::write()");
+ }
+ r3.write(0x33333333);
+ if (r3.read() != 0x33333333) {
+ TEST_FAIL("pp::BoundRegister::write()");
+ }
+ r4.write(pp::Value("0x4444444444444444"));
+ if (r4.read() != pp::Value("0x4444444444444444")) {
+ TEST_FAIL("pp::BoundRegister::write()");
+ }
+ r5.write(pp::Value("0x55555555555555555555555555555555"));
+ if (r5.read() != pp::Value("0x55555555555555555555555555555555")) {
+ TEST_FAIL("pp::BoundRegister::write()");
+ }
+
+ /* test write() by writing too many bits */
+ r1.write(0xff11);
+ if (r1.read() != 0x11) {
+ TEST_FAIL("pp::BoundRegister::write()");
+ }
+ r2.write(0xff2222);
+ if (r2.read() != 0x2222) {
+ TEST_FAIL("pp::BoundRegister::write()");
+ }
+ r3.write(0xff33333333ULL);
+ if (r3.read() != 0x33333333) {
+ TEST_FAIL("pp::BoundRegister::write()");
+ }
+ r4.write(pp::Value("0xff4444444444444444"));
+ if (r4.read() != pp::Value("0x4444444444444444")) {
+ TEST_FAIL("pp::BoundRegister::write()");
+ }
+ r5.write(pp::Value("0xff55555555555555555555555555555555"));
+ if (r5.read() != pp::Value("0x55555555555555555555555555555555")) {
+ TEST_FAIL("pp::BoundRegister::write()");
+ }
+}
+
+class TestProcs: public pp::RwProcs
+{
+ private:
+ mutable pp::Value m_data;
+ public:
+ TestProcs(): m_data(pp::MASK(pp::BITS128))
+ {
+ }
+ pp::Value
+ read() const
+ {
+ return m_data;
+ }
+ void
+ write(const pp::Value &value) const
+ {
+ m_data = value;
+ }
+};
+
+TEST(test_pp_proc_register)
+{
+ pp::RwProcsPtr procs(new TestProcs);
+
+ // without this, there is no "current context"
+ pp::ScopePtr root = pp::initialize_device_tree();
+
+ /* set up test registers */
+ pp::ProcRegister r1(procs, pp::BITS8);
+ pp::ProcRegister r2(procs, pp::BITS16);
+ pp::ProcRegister r3(procs, pp::BITS32);
+ pp::ProcRegister r4(procs, pp::BITS64);
+ pp::ProcRegister r5(procs, pp::BITS128);
+
+ /* test the read() method */
+ if (r1.read() != pp::MASK(pp::BITS8)) {
+ TEST_FAIL("pp::ProcRegister::read()");
+ }
+ if (r2.read() != pp::MASK(pp::BITS16)) {
+ TEST_FAIL("pp::ProcRegister::read()");
+ }
+ if (r3.read() != pp::MASK(pp::BITS32)) {
+ TEST_FAIL("pp::ProcRegister::read()");
+ }
+ if (r4.read() != pp::MASK(pp::BITS64)) {
+ TEST_FAIL("pp::ProcRegister::read()");
+ }
+ if (r5.read() != pp::MASK(pp::BITS128)) {
+ TEST_FAIL("pp::ProcRegister::read()");
+ }
+
+ /* test the write() method */
+ r1.write(0x11);
+ if (r1.read() != 0x11) {
+ TEST_FAIL("pp::ProcRegister::write()");
+ }
+ r2.write(0x2222);
+ if (r2.read() != 0x2222) {
+ TEST_FAIL("pp::ProcRegister::write()");
+ }
+ r3.write(0x33333333);
+ if (r3.read() != 0x33333333) {
+ TEST_FAIL("pp::ProcRegister::write()");
+ }
+ r4.write(pp::Value("0x4444444444444444"));
+ if (r4.read() != pp::Value("0x4444444444444444")) {
+ TEST_FAIL("pp::ProcRegister::write()");
+ }
+ r5.write(pp::Value("0x555555555555555555555555"));
+ if (r5.read() != pp::Value("0x555555555555555555555555")) {
+ TEST_FAIL("pp::ProcRegister::write()");
+ }
+
+ /* test write() by writing too many bits */
+ r1.write(0xff11);
+ if (r1.read() != 0x11) {
+ TEST_FAIL("pp::ProcRegister::write()");
+ }
+ r2.write(0xff2222);
+ if (r2.read() != 0x2222) {
+ TEST_FAIL("pp::ProcRegister::write()");
+ }
+ r3.write(0xff33333333ULL);
+ if (r3.read() != 0x33333333) {
+ TEST_FAIL("pp::ProcRegister::write()");
+ }
+ r5.write(pp::Value("0xff55555555555555555555555555555555"));
+ if (r5.read() != pp::Value("0x55555555555555555555555555555555")) {
+ TEST_FAIL("pp::ProcRegister::write()");
+ }
+}
=======================================
--- /dev/null
+++ /trunk/tests/scope_test.cpp Sun Nov 8 23:26:22 2009
@@ -0,0 +1,407 @@
+#include "pp/pp.h"
+#include "pp/scope.h"
+#include "test_binding.h"
+#include "test_helpers.h"
+#include "pp/datatype_types.h"
+#include "pp/register_types.h"
+#include "pp/field_types.h"
+#include "pp_test.h"
+
+TEST(test_ctors)
+{
+ // test the basic constructor
+ pp::ScopePtr scope = new_pp_scope();
+
+ if (!scope->is_root()) {
+ TEST_FAIL("pp::Scope::is_root()");
+ }
+ if (scope->parent() != scope) {
+ TEST_FAIL("pp::Scope::parent()");
+ }
+ if (scope->binding()) {
+ TEST_FAIL("pp::Scope::binding()");
+ }
+ if (scope->is_bound()) {
+ TEST_FAIL("pp::Scope::is_bound()");
+ }
+
+ // test the pp::Binding constructor
+ pp::BindingPtr bind = new_test_binding();
+ pp::ScopePtr scope2 = new_pp_scope(bind);
+
+ if (!scope2->is_root()) {
+ TEST_FAIL("pp::Scope::is_root()");
+ }
+ if (scope2->parent() != scope2) {
+ TEST_FAIL("pp::Scope::parent()");
+ }
+ if (scope2->binding() != bind) {
+ TEST_FAIL("pp::Scope::binding()");
+ }
+ if (!scope2->is_bound()) {
+ TEST_FAIL("pp::Scope::is_bound()");
+ }
+}
+
+TEST(test_parentage)
+{
+ pp::ScopePtr scope = new_pp_scope();
+ if (!scope->is_root()) {
+ TEST_FAIL("pp::Scope::is_root()");
+ }
+
+ pp::ScopePtr scope2 = new_pp_scope();
+ if (!scope2->is_root()) {
+ TEST_FAIL("pp::Scope::is_root()");
+ }
+
+ // test the set_parent() method
+ scope2->set_parent(scope);
+ if (scope2->parent() != scope) {
+ TEST_FAIL("pp::Scope::set_parent()");
+ }
+ if (scope2->is_root()) {
+ TEST_FAIL("pp::Scope::is_root()");
+ }
+}
+
+TEST(test_datatypes)
+{
+ pp::ScopePtr scope = new_pp_scope();
+
+ // nonexistent datatypes
+ if (scope->datatype("nonexistent_type") != NULL) {
+ TEST_FAIL("pp::Scope::datatype()");
+ }
+ if (scope->resolve_datatype("nonexistent_type") != NULL) {
+ TEST_FAIL("pp::Scope::resolve_datatype()");
+ }
+
+ // test add_datatype() and datatype()
+ pp::DatatypePtr type = new_pp_int_datatype();
+ scope->add_datatype("type", type);
+ if (scope->datatype(0) != type) {
+ TEST_FAIL("pp::Scope::add_datatype()");
+ }
+ if (scope->datatype("type") != type) {
+ TEST_FAIL("pp::Scope::add_datatype()");
+ }
+
+ // test n_datatypes()
+ if (scope->n_datatypes() != 1) {
+ TEST_FAIL("pp::Scope::n_datatypes()");
+ }
+
+ // test datatype_name()
+ if (scope->datatype_name(0) != "type") {
+ TEST_FAIL("pp::Scope::datatype_name()");
+ }
+
+ // test resolve_datatype()
+ pp::ConstDatatypePtr type2 = scope->resolve_datatype("type");
+ if (type2 != type) {
+ TEST_FAIL("pp::Scope::resolve_datatype()");
+ }
+
+ // test recursive resolve_datatype()
+ pp::ScopePtr psub = new_pp_scope();
+ psub->set_parent(scope);
+ type2 = psub->resolve_datatype("type");
+ if (type2 != type) {
+ TEST_FAIL("pp::Scope::resolve_datatype()");
+ }
+
+ // nonexistent type for chained scopes
+ type2 = psub->resolve_datatype("nonexistent-type");
+ if (type2 != NULL) {
+ TEST_FAIL("pp::Scope::resolve_datatype()");
+ }
+}
+
+TEST(test_exceptions)
+{
+ pp::ScopePtr scope = new_pp_scope();
+
+ // test out-of-bounds accesses
+ TEST_ASSERT(scope->n_dirents() == 0, "pp::Scope::n_dirents()");
+ if (scope->dirent(0) != NULL) {
+ TEST_FAIL("pp::Scope::dirent()");
+ }
+ if (scope->dirent("foo") != NULL) {
+ TEST_FAIL("pp::Scope::dirent()");
+ }
+ try {
+ scope->dirent_name(0);
+ TEST_FAIL("pp::Scope::dirent_name()");
+ } catch (std::out_of_range &e) {
+ }
+
+ // add a field
+ pp::DatatypePtr dt = new_pp_int_datatype();
+ pp::ConstantFieldPtr field = new_pp_constant_field(dt, 0);
+ scope->add_dirent("field", field);
+
+ // add an array
+ pp::ArrayPtr array = new_pp_array(pp::DIRENT_TYPE_FIELD);
+ scope->add_dirent("array", array);
+
+ // test invalid path elements
+ try {
+ scope->add_dirent("123", pp::FieldPtr());
+ TEST_FAIL("pp::Scope::add_dirent()");
+ } catch (pp::Path::InvalidError &e) {
+ }
+ try {
+ scope->lookup_dirent("foo/123");
+ TEST_FAIL("pp::Scope::lookup_dirent()");
+ } catch (pp::Path::InvalidError &e) {
+ }
+
+ // test array screwups
+ try {
+ scope->lookup_dirent("field[0]");
+ TEST_FAIL("pp::Scope::lookup_dirent()");
+ } catch (pp::Dirent::ConversionError &e) {
+ }
+ if (scope->lookup_dirent("array[0]") != NULL) {
+ TEST_FAIL("pp::Scope::lookup_dirent()");
+ }
+ try {
+ scope->add_dirent("array[0]", new_pp_scope());
+ TEST_FAIL("pp::Scope::add_dirent()");
+ } catch (pp::Path::InvalidError &e) {
+ }
+}
+
+TEST(test_dirents)
+{
+ // create a root scope
+ pp::ScopePtr root = new_pp_scope();
+ TEST_ASSERT(root->n_dirents() == 0,
+ "pp::Scope::n_dirents()");
+
+ // create a scope and add it to the root
+ pp::ScopePtr scope0 = new_pp_scope(new_test_binding());
+ scope0->set_parent(root);
+ root->add_dirent("scope0", scope0);
+ TEST_ASSERT(root->n_dirents() == 1,
+ "pp::Scope::n_dirents()");
+
+ // bookmark it
+ scope0->add_bookmark("bookmark");
+ TEST_ASSERT(scope0->has_bookmark("bookmark"),
+ "pp::Scope::add_bookmark()");
+
+ // create a field and add it to scope0
+ pp::DatatypePtr dt = new_pp_int_datatype();
+ pp::ConstantFieldPtr field1 = new_pp_constant_field(dt, 0);
+ scope0->add_dirent("field1", field1);
+ TEST_ASSERT(scope0->n_dirents() == 1,
+ "pp::Scope::n_dirents()");
+
+ // create a register and add it to scope0
+ pp::RegisterPtr reg1 = new_pp_bound_register(scope0->binding(),
+ 1, pp::BITS16);
+ scope0->add_dirent("reg1", reg1);
+ TEST_ASSERT(scope0->n_dirents() == 2,
+ "pp::Scope::n_dirents()");
+
+ // create a scope and add it to scope0
+ pp::ScopePtr scope1 = new_pp_scope();
+ scope1->set_parent(scope0);
+ scope0->add_dirent("scope1", scope1);
+ TEST_ASSERT(scope0->n_dirents() == 3,
+ "pp::Scope::n_dirents()");
+
+ // create an array
+ pp::ArrayPtr array1 = new_pp_array(pp::DIRENT_TYPE_FIELD);
+ // create a field and add it to array1
+ pp::ConstantFieldPtr field2 = new_pp_constant_field(dt, 0);
+ array1->append(field2);
+ // add array1 to scope0
+ scope0->add_dirent("array1", array1);
+ TEST_ASSERT(scope0->n_dirents() == 4,
+ "pp::Scope::n_dirents()");
+
+ // create an array of fields in scope0
+ pp::ConstantFieldPtr field3 = new_pp_constant_field(dt, 0);
+ scope0->add_dirent("array2[]", field3);
+ TEST_ASSERT(scope0->n_dirents() == 5, "pp::Scope::n_dirents()");
+ pp::ConstArrayPtr ar = pp::array_from_dirent(
+ scope0->lookup_dirent("array2"));
+ TEST_ASSERT(ar->size() == 1, "pp::Array::size()");
+ pp::ConstantFieldPtr field4 = new_pp_constant_field(dt, 0);
+ scope0->add_dirent("array2[]", field4);
+ TEST_ASSERT(scope0->n_dirents() == 5, "pp::Scope::n_dirents()");
+ TEST_ASSERT(ar->size() == 2, "pp::Array::size()");
+
+ // test dirent()
+ {
+ const pp::ConstFieldPtr &f =
+ pp::field_from_dirent(scope0->dirent(0));
+ TEST_ASSERT(f == field1,
+ "pp::Scope::dirent(int)");
+ }
+ {
+ const pp::ConstFieldPtr &f =
+ pp::field_from_dirent(scope0->dirent("field1"));
+ TEST_ASSERT(f == field1,
+ "pp::Scope::dirent(string)");
+ }
+ // test dirent_name()
+ {
+ TEST_ASSERT(scope0->dirent_name(0) == "field1",
+ "pp::Scope::dirent_name()");
+ }
+ // test lookup_dirent()
+ {
+ pp::ConstDirentPtr de;
+
+ // search for a field, exists
+ de = root->lookup_dirent("scope0/field1");
+ TEST_ASSERT(pp::field_from_dirent(de) == field1,
+ "pp::Scope::lookup_dirent()");
+
+ // search for a register, exists
+ de = root->lookup_dirent("scope0/reg1");
+ TEST_ASSERT(pp::register_from_dirent(de) == reg1,
+ "pp::Scope::lookup_dirent()");
+
+ // search for a scope, exists
+ de = root->lookup_dirent("scope0/scope1");
+ TEST_ASSERT(pp::scope_from_dirent(de) == scope1,
+ "pp::Scope::lookup_dirent()");
+
+ // search for a dirent, non-existing
+ if (root->lookup_dirent("scope0/foo") != NULL) {
+ TEST_FAIL("pp::Scope::lookup_dirent()");
+ }
+
+ // search for an item that is not a leaf node
+ try {
+ root->lookup_dirent("scope0/reg1/foo");
+ TEST_FAIL("pp::Scope::lookup_dirent()");
+ } catch (pp::Dirent::ConversionError &e) {
+ }
+
+ // search for an item through a path with ".."
+ de = root->lookup_dirent("scope0/scope1/../scope1");
+ TEST_ASSERT(pp::scope_from_dirent(de) == scope1,
+ "pp::Scope::lookup_dirent()");
+
+ // search for an array-held item
+ de = root->lookup_dirent("scope0/array1");
+ TEST_ASSERT(pp::array_from_dirent(de) == array1,
+ "pp::Scope::lookup_dirent()");
+ de = root->lookup_dirent("scope0/array1[0]");
+ TEST_ASSERT(pp::field_from_dirent(de) == field2,
+ "pp::Scope::lookup_dirent()");
+ }
+ // test dirent_defined()
+ {
+ bool found;
+
+ // search for a field, exists
+ found = root->dirent_defined("scope0/field1");
+ TEST_ASSERT(found, "pp::Scope::dirent_defined()");
+
+ // search for a register, exists
+ found = root->dirent_defined("scope0/reg1");
+ TEST_ASSERT(found, "pp::Scope::dirent_defined()");
+
+ // search for a scope, exists
+ found = root->dirent_defined("scope0/scope1");
+ TEST_ASSERT(found, "pp::Scope::dirent_defined()");
+
+ // search for a dirent, non-existing
+ found = root->dirent_defined("scope0/foo");
+ TEST_ASSERT(!found, "pp::Scope::dirent_defined()");
+
+ // search for an item that is not a leaf node
+ try {
+ found = root->dirent_defined("scope0/reg1/foo");
+ TEST_FAIL("pp::Scope::dirent_defined()");
+ } catch (pp::Dirent::ConversionError &e) {
+ }
+
+ // search for an item through a path with ".."
+ found = root->dirent_defined("scope0/scope1/../scope1");
+ TEST_ASSERT(found, "pp::Scope::dirent_defined()");
+ found = root->dirent_defined("scope0/scope1/../foo");
+ TEST_ASSERT(!found, "pp::Scope::dirent_defined()");
+
+ // search for an array-held item
+ found = root->dirent_defined("scope0/array1");
+ TEST_ASSERT(found, "pp::Scope::dirent_defined()");
+ found = root->dirent_defined("scope0/array1[]");
+ TEST_ASSERT(!found, "pp::Scope::dirent_defined()");
+ found = root->dirent_defined("scope0/array1[0]");
+ TEST_ASSERT(found, "pp::Scope::dirent_defined()");
+ found = root->dirent_defined("scope0/array1[1]");
+ TEST_ASSERT(!found, "pp::Scope::dirent_defined()");
+ }
+ // test resolve_path()
+ {
+ pp::Path final;
+
+ // resolve a path, exists
+ final = root->resolve_path("scope0/field1");
+ TEST_ASSERT(final == "scope0/field1")
+ << "pp::Scope::resolve_path(): got '" << final << "'";
+
+ // resolve a path, non-existing
+ final = root->resolve_path("scope0/foo");
+ TEST_ASSERT(!final.is_initialized())
+ << "pp::Scope::resolve_path(): got '" << final << "'";
+
+ // resolve an item through a path with leading ".."
+ final = scope1->resolve_path("../field1");
+ TEST_ASSERT(final == "../field1")
+ << "pp::Scope::resolve_path(): got '" << final << "'";
+
+ // resolve an item through a path with embedded ".."
+ final = root->resolve_path("scope0/scope1/../field1");
+ TEST_ASSERT(final == "scope0/field1")
+ << "pp::Scope::resolve_path(): got '" << final << "'";
+
+ // resolve an item through an absolute path
+ final = root->resolve_path("/scope0");
+ TEST_ASSERT(final == "scope0")
+ << "pp::Scope::resolve_path(): got '" << final << "'";
+
+ // resolve an item through an absolute path
+ final = scope1->resolve_path("/scope0");
+ TEST_ASSERT(final == "../../scope0")
+ << "pp::Scope::resolve_path(): got '" << final << "'";
+
+ // resolve an array-held item, positive index, in bounds
+ final = root->resolve_path("scope0/array1[0]");
+ TEST_ASSERT(final == "scope0/array1[0]")
+ << "pp::Scope::resolve_path(): got '" << final << "'";
+
+ // resolve an array-held item, positive index, out of bounds
+ final = root->resolve_path("scope0/array1[3]");
+ TEST_ASSERT(!final.is_initialized())
+ << "pp::Scope::resolve_path(): got '" << final << "'";
+
+ // resolve an array-held item, negative index, in bounds
+ final = root->resolve_path("scope0/array1[-1]");
+ TEST_ASSERT(final == "scope0/array1[0]")
+ << "pp::Scope::resolve_path(): got '" << final << "'";
+
+ // resolve an array-held item, negative index, out of bounds
+ final = root->resolve_path("scope0/array1[-3]");
+ TEST_ASSERT(!final.is_initialized())
+ << "pp::Scope::resolve_path(): got '" << final << "'";
+
+ // resolve an item through a bookmark
+ final = scope1->resolve_path("$bookmark/field1");
+ TEST_ASSERT(final == "../field1")
+ << "pp::Scope::resolve_path(): got '" << final << "'";
+
+ // resolve an item through a bookmark
+ final = scope1->resolve_path("$bad_bookmark/field1");
+ TEST_ASSERT(!final.is_initialized())
+ << "pp::Scope::resolve_path(): got '" << final << "'";
+ }
+}
=======================================
--- /dev/null
+++ /trunk/util/bignum.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,509 @@
+/* Copyright (c) Tim Hockin, 2007 */
+#ifndef PP_UTIL_BIGNUM_H__
+#define PP_UTIL_BIGNUM_H__
+
+#include <gmpxx.h>
+#include <ostream>
+#include <sstream>
+#include "pp/util/bit_buffer.h"
+
+#define BITS_PER_LONG (sizeof(long)*CHAR_BIT)
+
+namespace bignum {
+
+//
+// All BigInts are signed. GMP will allow you to create a BigInt from
+// either signed or unsigned raw integers, and it will do "the right
+// thing". Unsigned raw integers will always create a positive BigInt. It
+// is not clear exactly what happens if you read an out-of-bounds positive
+// BigInt as a signed value (e.g. ULONG_MAX read back as signed long). It
+// is also not clear what happens if you read a negative BigInt as an
+// unsigned value (e.g. -1 read back as unsigned int). Just don't do those
+// things, and we'll all be happy.
+//
+class BigInt: public mpz_class
+{
+ public:
+ // ctors are mostly pass-thru to GMP
+ BigInt() {}
+ BigInt(const BigInt &that): mpz_class(that) {}
+ template <class T, class U>
+ BigInt(const __gmp_expr<T, U> &expr): mpz_class(expr) {}
+ explicit BigInt(const char *str, int base=0): mpz_class(str, base) {}
+ explicit BigInt(const std::string &str, int base=0)
+ : mpz_class(str, base) {}
+ BigInt(signed char value): mpz_class(value) {}
+ BigInt(unsigned char value): mpz_class(value) {}
+ BigInt(signed short value): mpz_class(value) {}
+ BigInt(unsigned short value): mpz_class(value) {}
+ BigInt(signed int value): mpz_class(value) {}
+ BigInt(unsigned int value): mpz_class(value) {}
+ BigInt(signed long value): mpz_class(value) {}
+ BigInt(unsigned long value): mpz_class(value) {}
+ BigInt(float value): mpz_class(value) {}
+ BigInt(double value): mpz_class(value) {}
+ // GMP only supports up to 'long' args for it's ctors. We can
+ // support 'long long' at a small cost on 32 bit systems, and for
+ // free on 64 bit systems.
+ BigInt(signed long long value)
+ {
+ *this = value;
+ }
+ BigInt(unsigned long long value)
+ {
+ *this = value;
+ }
+ BigInt(const util::BitBuffer &bitbuf)
+ {
+ *this = bitbuf;
+ }
+
+ // assignment operators are mostly pass-thru to GMP
+ BigInt &
+ operator=(const BigInt &that)
+ { mpz_class::operator=(that); return *this; }
+ template <class T, class U>
+ BigInt &
+ operator=(const __gmp_expr<T, U> &that)
+ { mpz_class::operator=(that); return *this; }
+ BigInt &
+ operator=(const std::string &that)
+ { mpz_class::operator=(that); return *this; }
+ BigInt &
+ operator=(const char *that)
+ { mpz_class::operator=(that); return *this; }
+ BigInt &
+ operator=(signed char that)
+ { mpz_class::operator=(that); return *this; }
+ BigInt &
+ operator=(unsigned char that)
+ { mpz_class::operator=(that); return *this; }
+ BigInt &
+ operator=(signed short that)
+ { mpz_class::operator=(that); return *this; }
+ BigInt &
+ operator=(unsigned short that)
+ { mpz_class::operator=(that); return *this; }
+ BigInt &
+ operator=(signed int that)
+ { mpz_class::operator=(that); return *this; }
+ BigInt &
+ operator=(unsigned int that)
+ { mpz_class::operator=(that); return *this; }
+ BigInt &
+ operator=(signed long that)
+ { mpz_class::operator=(that); return *this; }
+ BigInt &
+ operator=(unsigned long that)
+ { mpz_class::operator=(that); return *this; }
+ BigInt &
+ operator=(float that)
+ { mpz_class::operator=(that); return *this; }
+ BigInt &
+ operator=(double that)
+ { mpz_class::operator=(that); return *this; }
+ BigInt &
+ operator=(signed long long that)
+ {
+ if ((sizeof(long) == sizeof(long long))
+ || (that >= LONG_MIN && that <= LONG_MAX)) {
+ // simple
+ *this = (signed long)that;
+ return *this;
+ }
+
+ // not so simple
+ int multiplier = 1;
+ if (that < 0) {
+ multiplier = -1;
+ that *= -1;
+ }
+
+ unsigned long vlo = that;
+ unsigned long vhi = (that >> (BITS_PER_LONG-1)) >> 1;
+ *this = vhi;
+ *this <<= BITS_PER_LONG;
+ *this += vlo;
+ *this *= multiplier;
+
+ return *this;
+ }
+ BigInt &
+ operator=(unsigned long long that)
+ {
+ if ((sizeof(long) == sizeof(long long))
+ || (that <= ULONG_MAX)) {
+ // simple
+ *this = (unsigned long)that;
+ return *this;
+ }
+
+ // not so simple
+ unsigned long vlo = that;
+ unsigned long vhi = (that >> (BITS_PER_LONG-1)) >> 1;
+ *this = vhi;
+ *this <<= BITS_PER_LONG;
+ *this += vlo;
+
+ return *this;
+ }
+ BigInt &
+ operator=(const util::BitBuffer &bitbuf)
+ {
+ // mpz_import() seems to not work.
+ *this = 0;
+ for (std::size_t i = bitbuf.size_bytes(); i > 0; i--) {
+ *this <<= CHAR_BIT;
+ *this += bitbuf.byte_at(i-1);
+ }
+ return *this;
+ }
+
+ // GMP only supports up to 'long' args for it's get methods. We
+ // can support 'long long' at a small cost on 32 bit systems, and
+ // for free on 64 bit systems.
+ long long
+ get_si() const
+ {
+ return get_int();
+ }
+ long long
+ get_int() const
+ {
+ if (sizeof(long) == sizeof(long long)
+ || (*this >= LONG_MIN && *this <= LONG_MAX)) {
+ // simple
+ return mpz_class::get_si();
+ }
+
+ // not so simple
+ BigInt myval(*this);
+ int multiplier = 1;
+ if (myval < 0) {
+ multiplier = -1;
+ myval *= -1;
+ }
+
+ unsigned long rlo = myval.mpz_class::get_ui();
+ myval >>= BITS_PER_LONG;
+ unsigned long long rhi = myval.mpz_class::get_ui();
+ unsigned long long result =
+ (((rhi << (BITS_PER_LONG-1))<<1) | rlo);
+ result *= multiplier;
+
+ return result;
+ }
+
+ unsigned long long
+ get_ui() const
+ {
+ return get_int();
+ }
+ unsigned long long
+ get_uint() const
+ {
+ if ((sizeof(long) == sizeof(long long))
+ || (*this >= 0 && *this <= ULONG_MAX)) {
+ // simple
+ return mpz_class::get_ui();
+ }
+
+ // not so simple
+ BigInt myval(*this);
+ unsigned long rlo = myval.mpz_class::get_ui();
+ myval >>= BITS_PER_LONG;
+ unsigned long long rhi = myval.mpz_class::get_ui();
+ unsigned long long result =
+ (((rhi << (BITS_PER_LONG-1))<<1) | rlo);
+
+ return result;
+ }
+
+ util::BitBuffer
+ get_bitbuffer(std::size_t bits=0) const
+ {
+ // mpz_export() seems to not work.
+ std::size_t bytes = 0;
+ BigInt tmp(*this);
+ while (tmp != 0) {
+ bytes++;
+ tmp >>= CHAR_BIT;
+ }
+ if (bits) {
+ bytes = (bits + (CHAR_BIT-1)) / CHAR_BIT;
+ }
+ util::BitBuffer bitbuf(bits ? bits : (bytes * CHAR_BIT));
+
+ BigInt myval(*this);
+ for (std::size_t i = 0; i < bytes; i++) {
+ bitbuf.byte_at(i) = myval.get_uint() & 0xff;
+ myval >>= CHAR_BIT;
+ }
+
+ return bitbuf;
+ }
+
+ // Stay consistent with the mpz_class API.
+ bool
+ fits_slonglong_p() const
+ {
+ return (*this >= LONG_MIN && *this <= LONG_MAX);
+ }
+ bool
+ fits_ulonglong_p() const
+ {
+ return (*this >= 0 && *this <= ULONG_MAX);
+ }
+
+ // Count the number of set bits.
+ unsigned long
+ popcount() const
+ {
+ return mpz_popcount(get_mpz_t());
+ }
+ static unsigned long
+ popcount(const BigInt &val)
+ {
+ return val.popcount();
+ }
+
+ // Exponentiate this BigInt and return the result.
+ BigInt
+ pow(unsigned long exponent) const
+ {
+ BigInt bn(*this);
+ mpz_pow_ui(bn.get_mpz_t(), get_mpz_t(), exponent);
+ return bn;
+ }
+ static BigInt
+ pow(const BigInt &val, unsigned long exponent)
+ {
+ return val.pow(exponent);
+ }
+
+ // Exponentiate this BigInt in place.
+ void
+ raise(unsigned long exponent)
+ {
+ mpz_pow_ui(get_mpz_t(), get_mpz_t(), exponent);
+ }
+
+ // convert to a decimal string
+ std::string
+ to_dec_string() const
+ {
+ std::ostringstream oss;
+ oss << *this;
+ return oss.str();
+ }
+
+ // convert to a hex string
+ std::string
+ to_hex_string() const
+ {
+ std::ostringstream oss;
+ oss << "0x" << std::hex << *this;
+ return oss.str();
+ }
+
+ // convert to an octal string
+ std::string
+ to_oct_string() const
+ {
+ std::ostringstream oss;
+ oss << "0" << std::oct << *this;
+ return oss.str();
+ }
+
+ // convert to a string
+ std::string
+ to_string() const
+ {
+ return to_dec_string();
+ }
+
+ // This strangeness allows BigInt to safely be treated as bools.
+ // It's commonly known as the "safe bool idiom".
+ private:
+ typedef void (BigInt::*BoolType)() const;
+ void convert_to_bool() const {}
+ public:
+ operator BoolType() const
+ {
+ if (*this == 0) {
+ return NULL;
+ }
+ return &BigInt::convert_to_bool;
+ }
+};
+
+//
+// Add some operators that the GMP implementation does not define.
+// Note: These are defined on mpz_class, not on BigInt.
+//
+
+inline mpz_class
+operator<<(const mpz_class &lhs, const mpz_class &rhs)
+{
+ return (lhs << rhs.get_ui());
+}
+inline mpz_class
+operator>>(const mpz_class &lhs, const mpz_class &rhs)
+{
+ return (lhs >> rhs.get_ui());
+}
+inline mpz_class &
+operator|=(mpz_class &lhs, const mpz_class &rhs)
+{
+ lhs = lhs | rhs;
+ return lhs;
+}
+inline mpz_class &
+operator&=(mpz_class &lhs, const mpz_class &rhs)
+{
+ lhs = lhs & rhs;
+ return lhs;
+}
+inline mpz_class &
+operator^=(mpz_class &lhs, const mpz_class &rhs)
+{
+ lhs = lhs ^ rhs;
+ return lhs;
+}
+
+//
+// Because BigInt supports 'long long' types, but GMP doesn't, we need
+// explicit operators for comparisons.
+// Note: These are defined on mpz_class, not on BigInt.
+//
+
+// ==
+inline bool
+operator==(const mpz_class &lhs, signed long long rhs)
+{
+ return (lhs == BigInt(rhs));
+}
+inline bool
+operator==(const mpz_class &lhs, unsigned long long rhs)
+{
+ return (lhs == BigInt(rhs));
+}
+inline bool
+operator==(signed long long lhs, const mpz_class &rhs)
+{
+ return (BigInt(lhs) == rhs);
+}
+inline bool
+operator==(unsigned long long lhs, const mpz_class &rhs)
+{
+ return (BigInt(lhs) == rhs);
+}
+// !=
+inline bool
+operator!=(const mpz_class &lhs, signed long long rhs)
+{
+ return (lhs != BigInt(rhs));
+}
+inline bool
+operator!=(const mpz_class &lhs, unsigned long long rhs)
+{
+ return (lhs != BigInt(rhs));
+}
+inline bool
+operator!=(signed long long lhs, const mpz_class &rhs)
+{
+ return (BigInt(lhs) != rhs);
+}
+inline bool
+operator!=(unsigned long long lhs, const mpz_class &rhs)
+{
+ return (BigInt(lhs) != rhs);
+}
+// <
+inline bool
+operator<(const mpz_class &lhs, signed long long rhs)
+{
+ return (lhs < BigInt(rhs));
+}
+inline bool
+operator<(const mpz_class &lhs, unsigned long long rhs)
+{
+ return (lhs < BigInt(rhs));
+}
+inline bool
+operator<(signed long long lhs, const mpz_class &rhs)
+{
+ return (BigInt(lhs) < rhs);
+}
+inline bool
+operator<(unsigned long long lhs, const mpz_class &rhs)
+{
+ return (BigInt(lhs) < rhs);
+}
+// >
+inline bool
+operator>(const mpz_class &lhs, signed long long rhs)
+{
+ return (lhs > BigInt(rhs));
+}
+inline bool
+operator>(const mpz_class &lhs, unsigned long long rhs)
+{
+ return (lhs > BigInt(rhs));
+}
+inline bool
+operator>(signed long long lhs, const mpz_class &rhs)
+{
+ return (BigInt(lhs) > rhs);
+}
+inline bool
+operator>(unsigned long long lhs, const mpz_class &rhs)
+{
+ return (BigInt(lhs) > rhs);
+}
+// <=
+inline bool
+operator<=(const mpz_class &lhs, signed long long rhs)
+{
+ return (lhs <= BigInt(rhs));
+}
+inline bool
+operator<=(const mpz_class &lhs, unsigned long long rhs)
+{
+ return (lhs <= BigInt(rhs));
+}
+inline bool
+operator<=(signed long long lhs, const mpz_class &rhs)
+{
+ return (BigInt(lhs) <= rhs);
+}
+inline bool
+operator<=(unsigned long long lhs, const mpz_class &rhs)
+{
+ return (BigInt(lhs) <= rhs);
+}
+// >=
+inline bool
+operator>=(const mpz_class &lhs, signed long long rhs)
+{
+ return (lhs >= BigInt(rhs));
+}
+inline bool
+operator>=(const mpz_class &lhs, unsigned long long rhs)
+{
+ return (lhs >= BigInt(rhs));
+}
+inline bool
+operator>=(signed long long lhs, const mpz_class &rhs)
+{
+ return (BigInt(lhs) >= rhs);
+}
+inline bool
+operator>=(unsigned long long lhs, const mpz_class &rhs)
+{
+ return (BigInt(lhs) >= rhs);
+}
+
+} // namespace bignum
+
+#endif // PP_UTIL_BIGNUM_H__
=======================================
--- /dev/null
+++ /trunk/util/bignum_lambda.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,36 @@
+/* Copyright (c) Tim Hockin, 2008 */
+#ifndef PP_UTIL_BIGNUM_LAMBDA_H__
+#define PP_UTIL_BIGNUM_LAMBDA_H__
+
+#include <boost/lambda/lambda.hpp>
+
+using boost::lambda::_1;
+using boost::lambda::_2;
+using boost::lambda::_3;
+
+//
+// Define some specializations for boost::lamba. It should 'Just Work'.
+//
+namespace boost {
+namespace lambda {
+
+template<class Taction>
+struct plain_return_type_2<arithmetic_action<Taction>,
+ bignum::BigInt, bignum::BigInt> {
+ typedef bignum::BigInt type;
+};
+template<typename Taction, typename Tother>
+struct plain_return_type_2<arithmetic_action<Taction>,
+ bignum::BigInt, Tother> {
+ typedef bignum::BigInt type;
+};
+template<typename Taction, typename Tother>
+struct plain_return_type_2<arithmetic_action<Taction>,
+ Tother, bignum::BigInt> {
+ typedef bignum::BigInt type;
+};
+
+} // namespace lambda
+} // namespace boost
+
+#endif // PP_UTIL_BIGNUM_LAMBDA_H__
=======================================
--- /dev/null
+++ /trunk/util/bit_buffer.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,148 @@
+// Copyright (c) Tim Hockin, 2008
+#ifndef PP_UTIL_BIT_BUFFER_H__
+#define PP_UTIL_BIT_BUFFER_H__
+
+#include <ostream>
+#include <sstream>
+#include <boost/format.hpp>
+#include <boost/shared_array.hpp>
+#include <stdint.h>
+#include <string.h>
+
+namespace util {
+
+//
+// This is a simple buffer of a fixed bit width. It is a suitable place
+// to store data for lower-level things like read() and write(). Once
+// initialized, it can not be resized except through a call to reset().
+//
+// Bitbuffers are always stored in little endian form as an array of
+// 'uint8_t'. Bits beyond the specified bit width are initialized to 0,
+// but could be changed by the caller.
+//
+class BitBuffer
+{
+ private:
+ typedef boost::shared_array<uint8_t> Uint8Array;
+ Uint8Array m_array;
+ std::size_t m_bits;
+ std::size_t m_bytes;
+
+ public:
+ BitBuffer(std::size_t bits = 0, uint8_t pattern = 0)
+ {
+ reset(bits, pattern);
+ }
+ BitBuffer(unsigned long bits, uint8_t *data)
+ {
+ reset(bits, data);
+ }
+
+ void
+ reset(std::size_t bits = 0, uint8_t pattern = 0)
+ {
+ m_bits = bits;
+ m_bytes = (m_bits+(CHAR_BIT-1))/CHAR_BIT;
+
+ Uint8Array tmp(NULL);
+ if (bits) {
+ tmp = Uint8Array(new uint8_t[m_bytes]);
+ memset(tmp.get(), pattern, m_bytes);
+ }
+ m_array = tmp;
+ }
+
+ void
+ reset(std::size_t bits, uint8_t *data)
+ {
+ m_bits = bits;
+ m_bytes = (m_bits+(CHAR_BIT-1))/CHAR_BIT;
+
+ Uint8Array tmp(NULL);
+ if (bits) {
+ tmp = Uint8Array(new uint8_t[m_bytes]);
+ memcpy(tmp.get(), data, m_bytes);
+ }
+ m_array = tmp;
+ }
+
+ void
+ fill(uint8_t pattern)
+ {
+ memset(m_array.get(), pattern, m_bytes);
+ }
+
+ uint8_t *
+ get()
+ {
+ return m_array.get();
+ }
+ const uint8_t *
+ get() const
+ {
+ return m_array.get();
+ }
+
+ std::size_t
+ size_bits() const
+ {
+ return m_bits;
+ }
+ std::size_t
+ size_bytes() const
+ {
+ return m_bytes;
+ }
+
+ uint8_t &
+ byte_at(int index)
+ {
+ return m_array[index];
+ }
+ const uint8_t &
+ byte_at(int index) const
+ {
+ return m_array[index];
+ }
+
+ std::string
+ to_string() const;
+};
+
+// stream output
+inline std::ostream &
+operator<<(std::ostream& o, const BitBuffer &bitbuf)
+{
+ o << "0x";
+ // this is signed on purpose
+ signed long i = bitbuf.size_bytes()-1;
+ if (i >= 0) {
+ // skip leading zeros
+ while (i > 0 && bitbuf.get()[i] == 0) {
+ i--;
+ }
+ // print the most-significant non-zero byte
+ o << boost::format("%x") %(int)bitbuf.get()[i];
+ i--;
+ // print the rest
+ for (i=i; i >= 0; i--) {
+ o << boost::format("%02x") %(int)bitbuf.get()[i];
+ }
+ } else {
+ o << "0";
+ }
+
+ return o;
+}
+
+inline std::string
+BitBuffer::to_string() const
+{
+ std::ostringstream oss;
+ oss << *this;
+ return oss.str();
+}
+
+} // namespace util
+
+#endif // PP_UTIL_BIT_BUFFER_H__
=======================================
--- /dev/null
+++ /trunk/util/filesystem.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,817 @@
+//
+// Thin wrappers around standard file/directory IO.
+// Tim Hockin <tho...@hockin.org>
+//
+#ifndef PP_UTIL_FILESYSTEM_HPP__
+#define PP_UTIL_FILESYSTEM_HPP__
+
+#undef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+
+#include <string>
+#include <stdexcept>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
+#include <unistd.h>
+#include <cstdlib>
+#include <fcntl.h>
+#include <cstring>
+#include <cerrno>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include "pp/pp.h"
+#include "pp/util/syserror.h"
+
+namespace filesystem {
+
+using std::size_t;
+using ::off_t;
+
+// a file
+class File;
+typedef boost::shared_ptr<File> FilePtr;
+typedef boost::shared_ptr<const File> ConstFilePtr;
+typedef boost::weak_ptr<File> WeakFilePtr;
+
+// a memory-mapped area of a file
+class FileMapping;
+typedef boost::shared_ptr<FileMapping> FileMappingPtr;
+
+// a directory
+class Directory;
+typedef boost::shared_ptr<Directory> DirectoryPtr;
+
+// a directory entry
+class Direntry;
+typedef boost::shared_ptr<Direntry> DirentryPtr;
+
+
+//
+// FileMapping
+//
+// This class tracks an individual mmap() of a file. When this class is
+// destructed, the mapping is munmap()ed.
+//
+// Users can not create instances of this class. To create a mapping call
+// filesystem::File::mmap(), which returns a smart pointer to one of these.
+//
+class FileMapping
+{
+ friend class File;
+
+ private:
+ // constructors - private to prevent abuse, defined later
+ FileMapping(const ConstFilePtr &file, off_t offset, size_t length,
+ int prot = PROT_READ, int flags = MAP_SHARED);
+
+ public:
+ // destructor
+ ~FileMapping()
+ {
+ unmap();
+ }
+
+ void
+ unmap()
+ {
+ if (!is_mapped()) {
+ return;
+ }
+
+ // If other references exist, this can be bad. Better to
+ // use the destructor whenever possible.
+ int r = munmap(m_real_address, m_real_length);
+ if (r < 0) {
+ syserr::throw_errno_error(errno,
+ "filesystem::FileMapping::unmap()");
+ }
+ m_address = m_real_address = NULL;
+ m_length = m_real_length = 0;
+ }
+
+ bool
+ is_mapped() const
+ {
+ return (m_address != NULL);
+ }
+
+ void *
+ address() const
+ {
+ return (void *)m_address;
+ }
+
+ off_t
+ offset() const
+ {
+ return m_offset;
+ }
+
+ size_t
+ length() const
+ {
+ return m_length;
+ }
+
+ int
+ prot() const
+ {
+ return m_prot;
+ }
+
+ int
+ flags() const
+ {
+ return m_flags;
+ }
+
+ private:
+ ConstFilePtr m_file;
+ // these are the file offset and map length that were requested
+ off_t m_offset;
+ size_t m_length;
+ uint8_t *m_address;
+ // these are the aligned file offset and map length
+ off_t m_real_offset;
+ size_t m_real_length;
+ uint8_t *m_real_address;
+ // flags for the mapping
+ int m_prot;
+ int m_flags;
+};
+
+//
+// file
+//
+// This class represents a single opened file. When this class is
+// destructed, the file descriptor is close()d.
+//
+// Users can not create instances of this class. To open a file call
+// filesystem::File::open(), which returns a smart pointer to one of these.
+//
+class File
+{
+ private:
+ // constructors - private to prevent abuse
+ File(): m_path(""), m_flags(-1), m_fd(-1)
+ {
+ }
+ File(const File &that)
+ : m_path(that.m_path), m_flags(that.m_flags), m_fd(that.m_fd)
+ {
+ }
+ File &
+ operator=(const File &that)
+ {
+ m_path = that.m_path;
+ m_flags = that.m_flags;
+ m_fd = that.m_fd;
+ return *this;
+ }
+
+ public:
+ // destructor
+ ~File()
+ {
+ if (m_fd >= 0) {
+ close();
+ }
+ }
+
+ static FilePtr
+ open(const std::string &path, int flags)
+ {
+ FilePtr f(new File());
+
+ f->m_this_ptr = f;
+ f->m_path = path;
+ f->m_flags = flags;
+ f->m_fd = ::open(path.c_str(), flags);
+ if (f->m_fd < 0) {
+ syserr::throw_errno_error(errno,
+ "filesystem::File::open(" + path + ")");
+ }
+
+ return f;
+ }
+
+ static FilePtr
+ fdopen(int fd, const std::string &path = "", int flags = -1)
+ {
+ FilePtr f(new File());
+
+ f->m_this_ptr = f;
+ f->m_path = path;
+ f->m_flags = flags;
+ f->m_fd = fd;
+
+ return f;
+ }
+
+ static FilePtr
+ tempfile(std::string path_template = "")
+ {
+ if (path_template == "") {
+ path_template = find_tmp_dir() + "/"
+ + to_string(getpid()) + ".XXXXXX";
+ }
+
+ int r;
+ char buf[path_template.size()+1]; // for mkstemp()
+
+ path_template.copy(buf, path_template.size());
+ buf[path_template.size()] = '\0';
+ r = ::mkstemp(buf);
+ if (r < 0) {
+ syserr::throw_errno_error(errno,
+ "filesystem::File::tempfile("+path_template+")");
+ }
+
+ return fdopen(r, buf, O_RDWR);
+ }
+
+ // warning: this is racy, but sometimes that is OK
+ static std::string
+ tempname(std::string path_template = "")
+ {
+ filesystem::FilePtr f
+ = filesystem::File::tempfile(path_template);
+ std::string filename = f->path();
+ f->close();
+ unlink(filename);
+ return filename;
+ }
+
+ void
+ reopen(int new_flags)
+ {
+ int new_fd;
+
+ new_fd = ::open(m_path.c_str(), new_flags);
+ if (new_fd < 0) {
+ syserr::throw_errno_error(errno,
+ "filesystem::File::open(" + m_path + ")");
+ }
+
+ int tmp = m_fd;
+ m_flags = new_flags;
+ m_fd = new_fd;
+ if (::close(tmp) < 0) {
+ syserr::throw_errno_error(errno,
+ "filesystem::File::close(" + m_path + ")");
+ }
+ }
+
+ void
+ close()
+ {
+ //
+ // If other references exist, this can be bad. Better to
+ // use the destructor whenever possible.
+ //
+ if (::close(m_fd) < 0) {
+ syserr::throw_errno_error(errno,
+ "filesystem::File::close(" + m_path + ")");
+ }
+ m_fd = -1;
+ }
+
+ static void
+ unlink(const std::string &path)
+ {
+ int r;
+
+ r = ::unlink(path.c_str());
+ if (r < 0) {
+ syserr::throw_errno_error(errno,
+ "filesystem::File::unlink(" + path + ")");
+ }
+ }
+
+ void
+ unlink()
+ {
+ unlink(m_path);
+ }
+
+ size_t
+ read(void *buf, size_t size) const
+ {
+ int r;
+
+ r = ::read(m_fd, buf, size);
+ if (r < 0) {
+ syserr::throw_errno_error(errno,
+ "filesystem::File::read(" + m_path + ")");
+ }
+
+ return r;
+ }
+
+ //FIXME: stat
+ //FIXME: create (static)
+ //FIXME: rename (static with 2 args, and 1 arg)
+ //FIXME: touch
+ //FIXME: add full_read() / full_write() methods?
+
+ std::string
+ read_line() const
+ {
+ std::string s;
+ char buf[2] = " ";
+
+ do {
+ read(buf, 1);
+ //FIXME: check for errors
+ s.append(buf);
+ } while (buf[0] != '\n');
+
+ return s;
+ }
+
+ size_t
+ write(void *buf, size_t size) const
+ {
+ int r;
+
+ r = ::write(m_fd, buf, size);
+ if (r < 0) {
+ syserr::throw_errno_error(errno,
+ "filesystem::File::write(" + m_path + ")");
+ }
+
+ return r;
+ }
+
+ FileMappingPtr
+ mmap(off_t offset, size_t length, int prot = -1,
+ int flags = MAP_SHARED) const
+ {
+ if (prot == -1) {
+ if (mode() == O_RDONLY) {
+ prot = PROT_READ;
+ } else if (mode() == O_WRONLY) {
+ prot = PROT_WRITE;
+ } else if (mode() == O_RDWR) {
+ prot = PROT_READ | PROT_WRITE;
+ }
+ }
+
+ return FileMappingPtr(new FileMapping(
+ ConstFilePtr(m_this_ptr),
+ offset, length, prot, flags));
+ }
+
+ size_t
+ seek(off_t offset, int whence) const
+ {
+ off_t r;
+
+ r = ::lseek(m_fd, offset, whence);
+ if (r == (off_t)-1) {
+ syserr::throw_errno_error(errno,
+ "filesystem::File::seek(" + m_path + ")");
+ }
+
+ // convert off_t (signed) to size_t (unsigned)
+ return r;
+ }
+
+ static size_t
+ size(const std::string &path)
+ {
+ int r;
+ struct stat st;
+
+ r = ::stat(path.c_str(), &st);
+ if (r < 0) {
+ syserr::throw_errno_error(errno,
+ "filesystem::File::size(" + path + ")");
+ }
+
+ // convert off_t (signed) to size_t (unsigned)
+ return st.st_size;
+ }
+
+ size_t
+ size() const
+ {
+ int r;
+ struct stat st;
+
+ r = ::fstat(m_fd, &st);
+ if (r < 0) {
+ syserr::throw_errno_error(errno,
+ "filesystem::File::size(" + m_path + ")");
+ }
+
+ // convert off_t (signed) to size_t (unsigned)
+ return st.st_size;
+ }
+
+ size_t
+ tell() const
+ {
+ return seek(0, SEEK_CUR);
+ }
+
+ bool
+ is_eof() const
+ {
+ return (tell() >= size());
+ }
+
+ bool
+ is_open() const
+ {
+ return (m_fd >= 0);
+ }
+
+ std::string
+ path() const
+ {
+ return m_path;
+ }
+
+ int
+ mode() const
+ {
+ return m_flags & O_ACCMODE;
+ }
+
+ int
+ flags() const
+ {
+ return m_flags;
+ }
+
+ int
+ fd() const
+ {
+ return m_fd;
+ }
+
+ private:
+ WeakFilePtr m_this_ptr;
+ std::string m_path;
+ int m_flags;
+ int m_fd;
+
+ static std::string
+ find_tmp_dir()
+ {
+ char *p;
+
+ p = getenv("TMPDIR");
+ if (p) {
+ return p;
+ }
+ p = getenv("TEMPDIR");
+ if (p) {
+ return p;
+ }
+ #ifdef P_tmpdir
+ return P_tmpdir;
+ #endif
+ return "/tmp";
+ }
+};
+
+inline
+FileMapping::FileMapping(const ConstFilePtr &file,
+ off_t offset, size_t length, int prot, int flags)
+ : m_file(file),
+ m_offset(offset), m_length(length), m_address(NULL),
+ m_real_offset(0), m_real_length(0), m_real_address(NULL),
+ m_prot(prot), m_flags(flags)
+{
+ size_t pgsize;
+ size_t pgmask;
+ off_t ptr_off;
+
+ // maps need to be page aligned
+ pgsize = getpagesize();
+ pgmask = pgsize - 1;
+ ptr_off = m_offset & pgmask;
+ m_real_offset = m_offset & ~((uint64_t)pgmask);
+ m_real_length = pgsize + ((m_length + pgmask) & ~pgmask);
+
+ m_real_address = (uint8_t *)mmap(NULL, m_real_length, prot,
+ flags, m_file->fd(), m_real_offset);
+ if (!m_real_address || m_real_address == MAP_FAILED) {
+ syserr::throw_errno_error(errno,
+ "filesystem::FileMapping::FileMapping()");
+ }
+
+ m_address = m_real_address + ptr_off;
+}
+
+//
+// Device
+//
+// This class represents a single device node.
+//
+// Users can not create instances of this class. To open a device call
+// filesystem::Device::open(), which returns a smart pointer to one of
these.
+//
+class Device: public File
+{
+ public:
+ static void
+ mkdev(const std::string &path, mode_t perms, int type,
+ int major, int minor)
+ {
+ int r;
+
+ r = ::mknod(path.c_str(), perms | type,
+ ::makedev(major, minor));
+ if (r < 0) {
+ syserr::throw_errno_error(errno,
+ "filesystem::Device::mkdev(" + path + ")");
+ }
+ }
+};
+
+//
+// Direntry
+//
+// This class represents a single directory entry.
+//
+// Users can not create instances of this class. To access a direntry, use
+// filesystem::Directory::read(), which returns a smart pointer to one of
+// these.
+//
+class Direntry
+{
+ friend class Directory;
+
+ private:
+ // constructor - implicit conversion from ::dirent
+ Direntry(struct ::dirent *de): m_dirent(de)
+ {
+ }
+
+ public:
+ // destructor
+ ~Direntry()
+ {
+ }
+
+ static bool
+ exists(const std::string &path)
+ {
+ struct ::stat st;
+ int r = ::stat(path.c_str(), &st);
+ return (r == 0);
+ }
+
+ std::string
+ name() const
+ {
+ return m_dirent->d_name;
+ }
+
+ bool
+ is_file() const
+ {
+ return m_dirent->d_type & DT_REG;
+ }
+
+ static bool
+ is_file(const std::string &path)
+ {
+ struct ::stat st;
+ return (::stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode));
+ }
+
+ bool
+ is_dir() const
+ {
+ return m_dirent->d_type & DT_DIR;
+ }
+
+ static bool
+ is_dir(const std::string &path)
+ {
+ struct ::stat st;
+ return (::stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode));
+ }
+
+ bool
+ is_link() const
+ {
+ return m_dirent->d_type & DT_LNK;
+ }
+
+ static bool
+ is_link(const std::string &path)
+ {
+ struct ::stat st;
+ return (::stat(path.c_str(), &st) == 0 && S_ISLNK(st.st_mode));
+ }
+
+ bool
+ is_fifo() const
+ {
+ return m_dirent->d_type & DT_FIFO;
+ }
+
+ static bool
+ is_fifo(const std::string &path)
+ {
+ struct ::stat st;
+ return (::stat(path.c_str(), &st) == 0 && S_ISFIFO(st.st_mode));
+ }
+
+ bool
+ is_socket() const
+ {
+ return m_dirent->d_type & DT_SOCK;
+ }
+
+ static bool
+ is_socket(const std::string &path)
+ {
+ struct ::stat st;
+ return (::stat(path.c_str(), &st) == 0 && S_ISSOCK(st.st_mode));
+ }
+
+ bool
+ is_chrdev() const
+ {
+ return m_dirent->d_type & DT_CHR;
+ }
+
+ static bool
+ is_chrdev(const std::string &path)
+ {
+ struct ::stat st;
+ return (::stat(path.c_str(), &st) == 0 && S_ISCHR(st.st_mode));
+ }
+
+ bool
+ is_blkdev() const
+ {
+ return m_dirent->d_type & DT_BLK;
+ }
+
+ static bool
+ is_blkdev(const std::string &path)
+ {
+ struct ::stat st;
+ return (::stat(path.c_str(), &st) == 0 && S_ISBLK(st.st_mode));
+ }
+
+ bool
+ is_dev() const
+ {
+ return (m_dirent->d_type & DT_CHR)
+ || (m_dirent->d_type & DT_BLK);
+ }
+
+ static bool
+ is_dev(const std::string &path)
+ {
+ struct ::stat st;
+ return (::stat(path.c_str(), &st) == 0
+ && (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)));
+ }
+
+ private:
+ struct ::dirent *m_dirent;
+};
+
+//
+// Directory
+//
+// This class represents a single directory. When this class is
destructed,
+// the directory is closedir()d.
+//
+// Users can not create instances of this class. To access a direntry, use
+// filesystem::Directory::open(), which returns a smart pointer to one of
+// these.
+//
+class Directory
+{
+ private:
+ // constructors - private to prevent abuse
+ Directory(): m_path(""), m_dir(NULL)
+ {
+ }
+ Directory(const Directory &that)
+ : m_path(that.m_path), m_dir(that.m_dir)
+ {
+ }
+ Directory &
+ operator=(const Directory &that)
+ {
+ m_path = that.m_path;
+ m_dir = that.m_dir;
+ return *this;
+ }
+
+ public:
+ // destructor
+ ~Directory()
+ {
+ if (m_dir) {
+ close();
+ }
+ }
+
+ static DirectoryPtr
+ open(const std::string &path)
+ {
+ DirectoryPtr d(new Directory());
+
+ d->m_dir = ::opendir(path.c_str());
+ if (!d->m_dir) {
+ syserr::throw_errno_error(errno,
+ "filesystem::Directory::open(" + path + ")");
+ }
+
+ return d;
+ }
+
+ void
+ close()
+ {
+ ::closedir(m_dir);
+ m_dir = NULL;
+ }
+
+ //FIXME: create
+ //FIXME: remove
+ //FIXME: mkdtemp => tempname() and tempdir()
+
+ DirentryPtr
+ read() const
+ {
+ struct ::dirent *de = ::readdir(m_dir);
+ if (de) {
+ return DirentryPtr(new Direntry(de));
+ }
+ return DirentryPtr();
+ }
+
+ void
+ rewind() const
+ {
+ ::rewinddir(m_dir);
+ }
+
+ bool
+ is_open() const
+ {
+ return (m_dir != NULL);
+ }
+
+ const std::string &
+ path() const
+ {
+ return m_path;
+ }
+
+ private:
+ std::string m_path;
+ ::DIR *m_dir;
+};
+
+inline void
+chdir(const string &path)
+{
+ int ret = ::chdir(path.c_str());
+ if (ret < 0) {
+ syserr::throw_errno_error(errno, path);
+ }
+}
+//FIXME: chdir(directory)
+
+inline const string
+getcwd()
+{
+ #ifdef _GNU_SOURCE
+ char *p = ::get_current_dir_name();
+ if (p == NULL) {
+ syserr::throw_errno_error(errno, "filesystem::getcwd()");
+ }
+ string cwd(p);
+ free(p);
+ return cwd;
+ #else
+ char buf[4096];
+ char *p = ::getcwd(buf, sizeof(buf));
+ if (p == NULL) {
+ syserr::throw_errno_error(errno, "filesystem::getcwd()");
+ }
+ return p;
+ #endif
+}
+
+} // namespace fs
+
+#endif // PP_UTIL_FILESYSTEM_HPP__
=======================================
--- /dev/null
+++ /trunk/util/keyed_vector.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,510 @@
+// keyed_vector.cpp
+//
+// Tim Hockin <tho...@hockin.org>
+// 2007
+//
+#ifndef PP_UTIL_KEYED_VECTOR_HPP__
+#define PP_UTIL_KEYED_VECTOR_HPP__
+
+#include <vector>
+#include <map>
+#include <stdexcept>
+#include <boost/iterator_adaptors.hpp>
+#include "pp/pp.h"
+#include "debug.h"
+
+namespace util {
+
+//
+// This template class is a thin wrapper to make iterators work for
+// KeyedVector objects. This is largely based on the boost example
+// code for boost::iterator_facade.
+//
+template<typename Titer, typename Tval>
+class KeyedVectorIterator
+ : public boost::iterator_facade<KeyedVectorIterator<Titer, Tval>, Tval,
+ typename std::iterator_traits<Titer>::iterator_category>
+{
+ friend class boost::iterator_core_access;
+ template<class,class> friend class KeyedVectorIterator;
+
+ public:
+ // default constructor
+ KeyedVectorIterator() {}
+
+ // implicit conversion from the underlying iterator
+ KeyedVectorIterator(Titer it): m_it(it) {}
+
+ // implicit conversion from iterator to const_iterator
+ template<class Tother>
+ KeyedVectorIterator(const KeyedVectorIterator<Titer, Tother> &other)
+ : m_it(other.m_it), m_trap(other.m_trap) {}
+
+ // get the underlying iterator
+ const Titer&
+ get() const
+ {
+ return m_it;
+ }
+
+ private:
+ // check for equality
+ template<typename Tthat>
+ bool
+ equal(const KeyedVectorIterator<Titer, Tthat> &that) const
+ {
+ return (this->m_it == that.m_it);
+ }
+
+ // move the iterator forward by one
+ void
+ increment()
+ {
+ ++m_it;
+ }
+
+ // move the iterator backward by one
+ void
+ decrement()
+ {
+ --m_it;
+ }
+
+ // move the iterator forward or backward by n
+ void
+ advance(const std::ptrdiff_t n)
+ {
+ m_it += n;
+ }
+
+ // figure out the distance to another iterator
+ template<typename Tthere>
+ std::ptrdiff_t
+ distance_to(const KeyedVectorIterator<Titer, Tthere> &there) const
+ {
+ return there.m_it - this->m_it;
+ }
+
+ // get at the referent
+ Tval &
+ dereference() const
+ {
+ return *m_it;
+ }
+
+ private:
+ // the actual underlying iterator
+ Titer m_it;
+
+ // a trap to catch const_iterator to iterator assignment
+ Tval *m_trap;
+};
+
+//
+// template class KeyedVector<Tkey, Tval>
+//
+// This template class implements a vector in which each stored object
+// (value) has a specific key. Indexing can be done by integer or by key.
+// When indexing by integer or iterating the data is returned in insert
+// order.
+//
+// This is a brief overview of how it works. First, there is an STL
+// vector of Tval which holds the data elements, in insert order. Then,
+// There is an STL map of Tkey->int. This holds the key elements and the
+// int index of the value in the value vector. Lastly, there is a vector
+// of map iterators, where the index in the vector is parallel to the
+// value vector. This allows you to access the data in order (via the
+// vectors), or to access it randomly (by the map).
+//
+// STL guarantees that map iterators do not get invalidated unless the
+// item pointed to is removed from the map. We rely on this when we store
+// iterators. When an item is removed from any position except the back
+// of the vectors, all subsequent entries must have their indices updated
+// in the map.
+//
+// Notes:
+// - The 'Tkey' type must have an == operator.
+// - Because of the dual modes of indexing, the 'Tkey' type can not be an
+// integral primitive.
+// - Keys must be unique. Inserting a duplicate key will over-write the
+// original value.
+//
+template<typename Tkey, typename Tval>
+class KeyedVector
+{
+ typedef std::vector<Tval> Tval_vector;
+ typedef typename Tval_vector::iterator Tval_iter;
+ typedef std::map<Tkey, int> Tkey_map;
+ typedef typename Tkey_map::iterator Tkey_iter;
+ typedef std::vector<Tkey_iter> Tkeyptr_vector;
+ typedef typename Tkeyptr_vector::iterator Tkeyptr_iter;
+
+ // the underlying data representation
+ Tkey_map m_keys;
+ Tval_vector m_values;
+ Tkeyptr_vector m_keyptrs;
+
+ public:
+ typedef Tval value_type;
+ typedef Tkey key_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef KeyedVectorIterator<Tval_iter, Tval> iterator;
+ typedef KeyedVectorIterator<Tval_iter, const Tval> const_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ // default ctor
+ KeyedVector()
+ {
+ }
+ // copy ctor
+ KeyedVector(const KeyedVector &other)
+ {
+ *this = other;
+ }
+ // assignment operator
+ KeyedVector &
+ operator=(const KeyedVector &other)
+ {
+ // We can't just copy each element, because m_keyptrs
+ // holds iterators into m_keys.
+ KeyedVector tmp;
+ for (size_type i = 0; i < other.size(); i++) {
+ tmp.insert(other.key_at(i), other.at(i));
+ }
+ swap(tmp);
+ return *this;
+ }
+ // swap data
+ void
+ swap(KeyedVector &other)
+ {
+ m_keys.swap(other.m_keys);
+ m_values.swap(other.m_values);
+ m_keyptrs.swap(other.m_keyptrs);
+ }
+ // clear data
+ void
+ clear()
+ {
+ m_keys.clear();
+ m_values.clear();
+ m_keyptrs.clear();
+ }
+
+ // get a forward iterator
+ iterator
+ begin()
+ {
+ return m_values.begin();
+ }
+ iterator
+ end()
+ {
+ return m_values.end();
+ }
+ const_iterator
+ begin() const
+ {
+ KeyedVector *p = const_cast<KeyedVector *>(this);
+ return p->begin();
+ }
+ const_iterator
+ end() const
+ {
+ KeyedVector *p = const_cast<KeyedVector *>(this);
+ return p->end();
+ }
+ // get a reverse iterator
+ reverse_iterator
+ rbegin()
+ {
+ return m_values.rbegin();
+ }
+ reverse_iterator
+ rend()
+ {
+ return m_values.rend();
+ }
+ const_reverse_iterator
+ rbegin() const
+ {
+ KeyedVector *p = const_cast<KeyedVector *>(this);
+ return p->rbegin();
+ }
+ const_reverse_iterator
+ rend() const
+ {
+ KeyedVector *p = const_cast<KeyedVector *>(this);
+ return p->rend();
+ }
+
+ // get meta-data
+ size_type
+ size() const
+ {
+ DASSERT(m_keys.size() == m_values.size());
+ return m_values.size();
+ }
+ size_type
+ max_size() const
+ {
+ return min(m_keys.max_size(), m_values.max_size());
+ }
+ size_type
+ capacity() const
+ {
+ return m_values.capacity();
+ }
+ bool
+ empty() const
+ {
+ return m_values.empty();
+ }
+
+ // get the first or last value - can throw std::out_of_range
+ reference
+ front()
+ {
+ bounds_check(0);
+ return m_values.front();
+ }
+ reference
+ back()
+ {
+ bounds_check(0);
+ return m_values.back();
+ }
+ const_reference
+ front() const
+ {
+ bounds_check(0);
+ return m_values.front();
+ }
+ const_reference
+ back() const
+ {
+ bounds_check(0);
+ return m_values.back();
+ }
+
+ // get the value at an int index - can throw std::out_of_range
+ reference
+ at(size_type index)
+ {
+ bounds_check(index);
+ return m_values[index];
+ }
+ reference
+ operator[](size_type index)
+ {
+ return at(index);
+ }
+ const_reference
+ at(size_type index) const
+ {
+ KeyedVector *p = const_cast<KeyedVector *>(this);
+ return p->at(index);
+ }
+ const_reference
+ operator[](size_type index) const
+ {
+ return at(index);
+ }
+
+ // get the value at a Tkey index - can throw std::out_of_range
+ reference
+ at(const Tkey &index)
+ {
+ iterator it = find(index);
+ if (it != end()) {
+ return *it;
+ }
+ throw std::out_of_range("key not found: " + to_string(index));
+ }
+ reference
+ operator[](const Tkey &index)
+ {
+ return at(index);
+ }
+ const_reference
+ at(const Tkey &index) const
+ {
+ KeyedVector *p = const_cast<KeyedVector *>(this);
+ return p->at(index);
+ }
+ const_reference
+ operator[](const Tkey &index) const
+ {
+ return at(index);
+ }
+
+ // get the const key at an int index - can throw std::out_of_range
+ const Tkey &
+ key_at(size_type index) const
+ {
+ bounds_check(index);
+ return m_keyptrs[index]->first;
+ }
+
+ // check for the existence of a key - does not throw
+ bool
+ has_key(const Tkey &key) const
+ {
+ return (find(key) != end());
+ }
+
+ // get the value for a key, or end() if not found - does not throw
+ iterator
+ find(size_type index)
+ {
+ if (index >= size()) {
+ return end();
+ }
+ return iter_at(index);
+ }
+ const_iterator
+ find(size_type index) const
+ {
+ KeyedVector *p = const_cast<KeyedVector *>(this);
+ return p->find(index);
+ }
+ iterator
+ find(const Tkey &key)
+ {
+ Tkey_iter found = m_keys.find(key);
+ if (found == m_keys.end()) {
+ return end();
+ }
+ return iter_at(found->second);
+ }
+ const_iterator
+ find(const Tkey &key) const
+ {
+ KeyedVector *p = const_cast<KeyedVector *>(this);
+ return p->find(key);
+ }
+
+ // add a unique key-value pair, or overwrite the value if the key
+ // already exists - same as insert()
+ void
+ push_back(const Tkey &key, const Tval &value)
+ {
+ insert(key, value);
+ }
+
+ // remove the first or last item - can throw std::out_of_range
+ void
+ pop_front()
+ {
+ bounds_check(0);
+ erase(m_keyptrs.front()->first);
+ }
+ void
+ pop_back()
+ {
+ bounds_check(0);
+ erase(m_keyptrs.back()->first);
+ }
+
+ // add a unique key-value pair, or overwrite the value if the key
+ // already exists
+ iterator
+ insert(const Tkey &key, const Tval &value)
+ {
+ std::pair<Tkey_iter, bool> ret;
+ typename Tkey_map::value_type key_val(key, -1);
+
+ // try to insert the key
+ ret = m_keys.insert(key_val);
+ if (ret.second) {
+ // the key was inserted into the map: add the
+ // value to the vectors, and to point to the key
+ m_values.push_back(value);
+ m_keyptrs.push_back(ret.first);
+ ret.first->second = size()-1;
+ return (m_values.end() - 1);
+ } else {
+ // the key already existed in the map: update the
+ // value, but leave the vectors alone
+ m_values[ret.first->second] = value;
+ return (iter_at(ret.first->second));
+ }
+ }
+
+ // remove a value by key
+ iterator
+ erase(const Tkey &key)
+ {
+ iterator found = find(key);
+ if (found != end()) {
+ return erase(found);
+ }
+ return end();
+ }
+ // remove value(s) by iterator(s)
+ iterator
+ erase(iterator pos)
+ {
+ size_type index = pos - begin();
+ iterator ret = pos+1;
+
+ m_keys.erase(m_keyptrs[index]);
+ m_keyptrs.erase(keyptr_iter_at(index));
+ m_values.erase(val_iter_at(index));
+
+ // shuffle the indices which are stored in the map
+ for (size_type i = index; i < size(); i++) {
+ m_keyptrs[i]->second--;
+ }
+
+ return ret;
+ }
+ iterator
+ erase(iterator first, iterator last)
+ {
+ while (first < last) {
+ erase(first);
+ first++;
+ }
+ return first;
+ }
+
+ private:
+ // check index bounds
+ void
+ bounds_check(size_type index) const
+ {
+ if (index >= size()) {
+ throw std::out_of_range("index out of range: "
+ + to_string(index));
+ }
+ }
+
+ // get various iterators by index - these are internal, so no
+ // bounds checking is needed
+ iterator
+ iter_at(size_type index)
+ {
+ return val_iter_at(index);
+ }
+ Tkeyptr_iter
+ keyptr_iter_at(size_type index)
+ {
+ return (m_keyptrs.begin() + index);
+ }
+ Tval_iter
+ val_iter_at(size_type index)
+ {
+ return (m_values.begin() + index);
+ }
+};
+
+} // namespace util
+
+#endif // PP_UTIL_KEYED_VECTOR_HPP__
=======================================
--- /dev/null
+++ /trunk/util/printfxx.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,111 @@
+#ifndef PP_UTIL_PRINTFXX_H__
+#define PP_UTIL_PRINTFXX_H__
+
+#include <string>
+#include <iostream>
+#include <sstream>
+#include <boost/format.hpp>
+#include <boost/preprocessor.hpp>
+
+// Note: this is in the top-level namespace.
+
+// convert any ostreamable object to a string
+template <typename T>
+inline std::string
+to_string(const T &val)
+{
+ std::ostringstream oss;
+ oss << val;
+ return oss.str();
+}
+
+// enable simple string manipulations of boost::format
+inline std::string &
+operator+=(std::string &str, const boost::format &fmt)
+{
+ // return the original lhs
+ str += to_string(fmt);
+ return str;
+}
+inline std::string
+operator+(const std::string &str, const boost::format &fmt)
+{
+ // return a new string
+ return std::string(str + to_string(fmt));
+}
+inline std::string
+operator+(const boost::format &fmt, const std::string &str)
+{
+ // return a new string
+ return std::string(to_string(fmt) + str);
+}
+inline bool
+operator==(const std::string &str, const boost::format &fmt)
+{
+ return (str == to_string(fmt));
+}
+inline bool
+operator==(const boost::format &fmt, const std::string &str)
+{
+ return (str == fmt);
+}
+inline bool
+operator!=(const std::string &str, const boost::format &fmt)
+{
+ return !(str == fmt);
+}
+inline bool
+operator!=(const boost::format &fmt, const std::string &str)
+{
+ return !(str == fmt);
+}
+
+// A lot of this is boost magic to generate a load of template
+// definitions. Not pretty, but useful.
+
+#define GEN_ARG(Z, N, _) % BOOST_PP_CAT(a, N)
+
+#define BOOST_PP_LOCAL_MACRO(N) \
+template <BOOST_PP_ENUM_PARAMS(N, class T)> \
+void \
+printfxx(const std::string& fmt, \
+ BOOST_PP_ENUM_BINARY_PARAMS(N, const T, &a)) { \
+ std::cout << boost::format(fmt) BOOST_PP_REPEAT(N, GEN_ARG, _); \
+} \
+template <BOOST_PP_ENUM_PARAMS(N, class T)> \
+void \
+fprintfxx(std::ostream &out, const std::string& fmt, \
+ BOOST_PP_ENUM_BINARY_PARAMS(N, const T, &a)) { \
+ out << boost::format(fmt) BOOST_PP_REPEAT(N, GEN_ARG, _); \
+} \
+template <BOOST_PP_ENUM_PARAMS(N, class T)> \
+std::string \
+sprintfxx(const std::string& fmt, \
+ BOOST_PP_ENUM_BINARY_PARAMS(N, const T, &a)) { \
+ return to_string(boost::format(fmt) BOOST_PP_REPEAT(N, GEN_ARG, _)); \
+}
+
+// allow up to 20 args
+#define BOOST_PP_LOCAL_LIMITS (1, 16)
+#include BOOST_PP_LOCAL_ITERATE()
+#undef GEN_ARG
+
+// no-args version is easier to write by hand instead of messing with
+// BOOST_PP_EXPR_IF
+inline void
+printfxx(const std::string &fmt)
+{
+ std::cout << boost::format(fmt);
+}
+inline void
+fprintfxx(std::ostream &out, const std::string &fmt)
+{
+ out << boost::format(fmt);
+}
+inline std::string
+sprintfxx(const std::string &fmt)
+{
+ return to_string(boost::format(fmt));
+}
+
+#endif // PP_UTIL_PRINTFXX_H__
=======================================
--- /dev/null
+++ /trunk/util/shared_object.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,137 @@
+/* Copyright (c) Tim Hockin, 2008 */
+#ifndef PP_UTIL_SHARED_OBJECT_H__
+#define PP_UTIL_SHARED_OBJECT_H__
+
+#include <stdexcept>
+#include <string>
+#include <dlfcn.h>
+#include <boost/shared_ptr.hpp>
+
+namespace util {
+
+//
+// This is a simple wrapper for loading and managing shared object files.
+// It does reference counting (via shared_ptr) on the "handle" and will
+// unload it only when the last reference is released.
+//
+class SharedObject
+{
+ public:
+ // something went wrong loading the object
+ struct load_error: public std::runtime_error
+ {
+ explicit load_error(const std::string &str)
+ : runtime_error(str)
+ {
+ }
+ };
+ // the handle was NULL
+ struct invalid_handle_error: public std::runtime_error
+ {
+ explicit invalid_handle_error(const std::string &str)
+ : runtime_error(str)
+ {
+ }
+ };
+ // a requested symbol lookup failed
+ struct symbol_not_found_error: public std::runtime_error
+ {
+ explicit symbol_not_found_error(const std::string &str)
+ : runtime_error(str)
+ {
+ }
+ };
+
+ private:
+ // member variables
+ boost::shared_ptr<void> m_handle;
+ std::string m_path;
+
+ public:
+ // these are the default flags with which to open a SharedObject
+ static const unsigned DEFAULT_FLAGS = (RTLD_NOW | RTLD_LOCAL);
+
+ // default ctor - does not open anything
+ SharedObject()
+ : m_handle(), m_path("")
+ {
+ }
+ // ctor - open a shared object file
+ explicit
+ SharedObject(const std::string &path, unsigned flags=DEFAULT_FLAGS)
+ : m_handle(), m_path(path)
+ {
+ open(path, flags);
+ }
+ // the default copy ctor is correct
+ // the default assignment operator is correct
+ // the dtor for m_handle does the dirty work
+
+ // Open a new shared object file. See the man-page for dlopen() for
+ // details about flags and path handling.
+ void
+ open(const std::string &path, unsigned flags = DEFAULT_FLAGS)
+ {
+ void *handle = dlopen(path.c_str(), flags);
+ if (handle == NULL) {
+ throw load_error(dlerror());
+ }
+ m_handle = make_handle_ptr(handle);
+ m_path = path;
+ }
+
+ // drop our reference to the current shared object
+ void
+ close()
+ {
+ m_handle = make_handle_ptr();
+ }
+
+ // get a pointer to a public symbol in the current shared object
+ void *
+ lookup_symbol(const std::string &symbol) const
+ {
+ if (handle() == NULL) {
+ throw invalid_handle_error(symbol);
+ }
+
+ // check for errors as per the man page
+ dlerror();
+ void *sym = dlsym(m_handle.get(), symbol.c_str());
+ const char *error = dlerror();
+ if (sym == NULL && error) {
+ throw symbol_not_found_error(error);
+ }
+
+ return sym;
+ }
+
+ // get the raw handle
+ const void *
+ handle() const
+ {
+ return m_handle.get();
+ }
+
+ // get the path that was used to open the shared object file
+ const std::string &
+ path() const
+ {
+ return m_path;
+ }
+
+ private:
+ // create a shared_ptr to a handle, with an appropriate deleter
+ boost::shared_ptr<void>
+ make_handle_ptr(void *ptr = NULL)
+ {
+ if (ptr == NULL) {
+ return boost::shared_ptr<void>();
+ }
+ return boost::shared_ptr<void>(ptr, dlclose);
+ }
+};
+
+} // namespace util
+
+#endif // PP_UTIL_SHARED_OBJECT_H__
=======================================
--- /dev/null
+++ /trunk/util/simple_regex.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,166 @@
+/* Copyright (c) Tim Hockin, 2008 */
+#ifndef PP_UTIL_SIMPLE_REGEX_H__
+#define PP_UTIL_SIMPLE_REGEX_H__
+
+#include <stdexcept>
+#include <sys/types.h>
+#include <regex.h>
+
+namespace util {
+
+// This is a simple regex class. Nothing fancy, and does not require a
+// library, as does boost.regex.
+// Example:
+// SimpleRegex re("^foo.*bar");
+// if (re.matches("foobar"))
+// std::cout << "yes!" << std::endl;
+class SimpleRegex
+{
+ private:
+ string m_re_string;
+ unsigned m_flags;
+ regex_t m_c_regex;
+
+ public:
+ // global defaults
+ static const unsigned DEFAULT_FLAGS = (REG_EXTENDED | REG_NOSUB);
+ static const unsigned DEFAULT_EXEC_FLAGS = 0;
+
+ // ctor from string
+ SimpleRegex(const string &re_str, unsigned flags = DEFAULT_FLAGS)
+ : m_re_string(re_str), m_flags(flags)
+ {
+ compile();
+ }
+ // copy ctor
+ SimpleRegex(const SimpleRegex &that)
+ {
+ *this = that;
+ }
+ // assignment operator
+ SimpleRegex &
+ operator=(const SimpleRegex &that)
+ {
+ if (this != &that) {
+ m_re_string = that.m_re_string;
+ m_flags = that.m_flags;
+ compile();
+ }
+ return *this;
+ }
+ // dtor
+ ~SimpleRegex()
+ {
+ regfree(&m_c_regex);
+ }
+
+ // see if a string matches against this SimpleRegex
+ bool
+ matches(const string &test_string,
+ unsigned exec_flags = DEFAULT_EXEC_FLAGS) const
+ {
+ int ret;
+ ret = regexec(&m_c_regex, test_string.c_str(),
+ 0, NULL, exec_flags);
+ return (ret == 0);
+ }
+
+ // setters
+ void
+ set_ignore_case(bool val)
+ {
+ unsigned flag = REG_ICASE;
+ if (val) {
+ set_flag(flag);
+ } else {
+ clear_flag(flag);
+ }
+ }
+ void
+ set_match_newline(bool val)
+ {
+ unsigned flag = REG_NEWLINE;
+ if (val) {
+ set_flag(flag);
+ } else {
+ clear_flag(flag);
+ }
+ }
+ void
+ set_extended(bool val)
+ {
+ unsigned flag = REG_EXTENDED;
+ if (val) {
+ set_flag(flag);
+ } else {
+ clear_flag(flag);
+ }
+ }
+
+ // getters
+ const string &
+ re_string() const
+ {
+ return m_re_string;
+ }
+ unsigned
+ flags() const
+ {
+ return m_flags;
+ }
+ bool
+ is_ignore_case() const
+ {
+ return (m_flags & REG_ICASE);
+ }
+ bool
+ is_match_newline() const
+ {
+ return (m_flags & REG_NEWLINE);
+ }
+ bool
+ is_extended() const
+ {
+ return (m_flags & REG_EXTENDED);
+ }
+ const regex_t *
+ c_regex() const
+ {
+ return &m_c_regex;
+ }
+
+ private:
+ void
+ compile()
+ {
+ int ret;
+ ret = regcomp(&m_c_regex, m_re_string.c_str(), m_flags);
+ if (ret != 0) {
+ char err_str[1024];
+ regerror(ret, &m_c_regex, err_str, sizeof(err_str));
+ throw std::invalid_argument(err_str);
+ }
+ }
+
+ void
+ set_flag(unsigned flag)
+ {
+ if (!(m_flags & flag)) {
+ m_flags |= flag;
+ compile();
+ }
+ }
+
+ void
+ clear_flag(unsigned flag)
+ {
+ if (m_flags & flag) {
+ m_flags &= ~flag;
+ compile();
+ }
+ }
+};
+
+} // namespace util
+
+#endif // PP_UTIL_SIMPLE_REGEX_H__
=======================================
--- /dev/null
+++ /trunk/util/sockets.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,235 @@
+/* Copyright (c) Andrew Schran, 2008 */
+#ifndef PP_UTIL_SOCKETS_H__
+#define PP_UTIL_SOCKETS_H__
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdlib.h>
+#include <string>
+#include "pp/util/syserror.h"
+
+namespace unix_socket {
+
+class Socket
+{
+ private:
+ int m_fd;
+ struct sockaddr_un m_remote;
+
+ public:
+ Socket(int fd)
+ {
+ m_fd = fd;
+ }
+
+ Socket(const std::string &path)
+ {
+ int ret;
+
+ // Make socket
+ m_fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (m_fd < 0) {
+ syserr::throw_errno_error(errno,
+ "unix_socket::Socket::Socket(" + path + ")");
+ }
+
+ m_remote.sun_family = AF_UNIX;
+ strncpy(m_remote.sun_path, path.c_str(),
+ sizeof(m_remote.sun_path));
+ ret = connect(m_fd, (struct sockaddr *)&m_remote,
+ path.size() + sizeof(m_remote.sun_family));
+ if (ret < 0) {
+ syserr::throw_errno_error(errno,
+ "unix_socket::Socket::Socket(" + path + ")");
+ }
+ }
+
+ virtual ~Socket()
+ {
+ close();
+ }
+
+ bool
+ is_connected() const
+ {
+ return (m_fd >= 0);
+ }
+
+ virtual void
+ close()
+ {
+ if (is_connected()) {
+ ::close(m_fd);
+ m_fd = -1;
+ }
+ }
+
+ void
+ send(std::string s) const
+ {
+ send(s.data(), s.length());
+ }
+
+ void
+ send(const void *msg, int len) const
+ {
+ if (!is_connected()) {
+ syserr::throw_errno_error(ENOTCONN,
+ "unix_socket::Socket::send");
+ }
+
+ const char *buf = (char *)msg;
+ int sent = 0;
+ int left = len;
+
+ while (sent < len) {
+ int n = ::send(m_fd, buf + sent, left, 0);
+ if (n == -1) {
+ // FIXME: check for EINTR
+ syserr::throw_errno_error(errno,
+ "unix_socket::Socket::send");
+ }
+ sent += n;
+ left -= n;
+ }
+ }
+
+ // Receive and return one line of data
+ // Discard the newline character
+ std::string
+ recv_line()
+ {
+ if (!is_connected()) {
+ syserr::throw_errno_error(ENOTCONN,
+ "unix_socket::Socket::recv_line");
+ }
+
+ char c;
+ ssize_t len;
+ std::string str;
+ while ((len = ::recv(m_fd, &c, sizeof(c), 0)) > 0) {
+ if (c == '\n')
+ break;
+ str.push_back(c);
+ }
+
+ if (len <= 0) {
+ close();
+ }
+
+ return str;
+ }
+
+ // Attempt to receive exactly len bytes
+ // Return number of bytes actually received
+ ssize_t
+ recv_all(void *buf, size_t len)
+ {
+ // Attempt to receive len bytes, if possible
+ size_t total = 0;
+ do {
+ ssize_t rcvd = recv((char *)buf + total,
+ len - total);
+ if (rcvd <= 0) {
+ // Connection closed, return whatever
+ // we have
+ return total;
+ }
+ total += rcvd;
+ } while (total < len);
+
+ return total;
+ }
+
+ // Receive up to len bytes
+ // Return number of bytes actually received
+ ssize_t
+ recv(void *buf, size_t len) {
+ if (!is_connected()) {
+ syserr::throw_errno_error(ENOTCONN,
+ "unix_socket::Socket::recv");
+ }
+
+ ssize_t ret = ::recv(m_fd, buf, len, 0);
+ if (ret <= 0 && len > 0) {
+ // FIXME: check for EINTR
+ close();
+ }
+
+ return ret;
+ }
+}; // class Socket
+
+class Server
+{
+ private:
+ int m_fd_listen;
+ std::string m_path;
+ struct sockaddr_un m_local, m_remote;
+ struct ucred m_cred;
+
+ public:
+ Server(const std::string &path)
+ : m_path(path)
+ {
+ int ret;
+
+ // Make socket
+ m_fd_listen = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (m_fd_listen < 0) {
+ syserr::throw_errno_error(errno,
+ "unix_socket::Server::Server(" + path + ")");
+ }
+
+ // Bind to path
+ m_local.sun_family = AF_UNIX;
+ strncpy(m_local.sun_path, path.c_str(), path.size() + 1);
+ // FIXME: should we unlink or not?
+ ::unlink(m_local.sun_path);
+ socklen_t len = path.length()
+ + sizeof(m_local.sun_family);
+ ret = bind(m_fd_listen, (struct sockaddr *)&m_local, len);
+ if (ret < 0) {
+ syserr::throw_errno_error(errno,
+ "unix_socket::Server::Server(" + path + ")");
+ }
+
+ // Listen for connections
+ ret = listen(m_fd_listen, 10); // allow 10 to queue
+ if (ret < 0) {
+ syserr::throw_errno_error(errno,
+ "unix_socket::Server::Server(" + path + ")");
+ }
+ } // Server
+
+ ~Server()
+ {
+ ::close(m_fd_listen);
+ ::unlink(m_path.c_str());
+ }
+
+ Socket
+ accept()
+ {
+ while (1) {
+ socklen_t len = sizeof(struct sockaddr_un);
+ int s = ::accept(m_fd_listen,
+ (struct sockaddr *)&m_remote, &len);
+ if (s < 0) {
+ s = 0;
+ if (errno == EINTR) {
+ continue; // signal
+ }
+ syserr::throw_errno_error(errno,
+ "unix_socket::Server::accept()");
+ }
+
+ return Socket(s);
+ }
+ } // accept
+
+}; // class Server
+
+} // namespace unix_socket
+
+#endif // PP_UTIL_SOCKETS_H__
=======================================
--- /dev/null
+++ /trunk/util/syserror.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,194 @@
+//
+// Tim Hockin <tho...@hockin.org>
+//
+#ifndef PP_UTIL_SYSERROR_HPP__
+#define PP_UTIL_SYSERROR_HPP__
+
+#include <stdexcept>
+#include <string>
+#include <cerrno>
+#include <cstring>
+
+namespace syserr {
+
+// a base class for errno-related errors
+class ErrnoError: public std::runtime_error
+{
+ private:
+ int m_error;
+
+ protected:
+ ErrnoError(int error, const std::string &msg)
+ : runtime_error(msg), m_error(error)
+ {
+ }
+
+ int
+ error()
+ {
+ return m_error;
+ }
+};
+
+#define DEFINE_ERROR(name) \
+ struct name: public ErrnoError \
+ { \
+ name(int error, const std::string &str) \
+ : ErrnoError(error, str) \
+ {} \
+ };
+
+// define a class for each errno
+DEFINE_ERROR(PermissionDenied);
+DEFINE_ERROR(OperationNotPermitted);
+DEFINE_ERROR(NotFound);
+DEFINE_ERROR(IoError);
+DEFINE_ERROR(Invalid);
+DEFINE_ERROR(NoDevice);
+DEFINE_ERROR(Busy);
+DEFINE_ERROR(Exists);
+DEFINE_ERROR(NotADirectory);
+DEFINE_ERROR(IsADirectory);
+DEFINE_ERROR(NoSpace);
+DEFINE_ERROR(IllegalSeek);
+DEFINE_ERROR(BrokenPipe);
+DEFINE_ERROR(TooManySymlinks);
+DEFINE_ERROR(NameTooLong);
+DEFINE_ERROR(OutOfMemory);
+DEFINE_ERROR(NotImplemented);
+DEFINE_ERROR(BadAddress);
+DEFINE_ERROR(BadFileDescriptor);
+DEFINE_ERROR(OutOfRange);
+DEFINE_ERROR(TryAgain);
+DEFINE_ERROR(NoChild);
+DEFINE_ERROR(TextBusy);
+DEFINE_ERROR(Interrupted);
+DEFINE_ERROR(OperationInProgress);
+DEFINE_ERROR(NotSupported);
+DEFINE_ERROR(OperationTimedOut);
+DEFINE_ERROR(ConnectionInProgress);
+DEFINE_ERROR(NoBufferSpace);
+DEFINE_ERROR(ProtocolNotSupported);
+DEFINE_ERROR(ProtocolNotAvailable);
+DEFINE_ERROR(ProtocolError);
+DEFINE_ERROR(AddressFamilyNotSupported);
+DEFINE_ERROR(AddressInUse);
+DEFINE_ERROR(ConnectionRefused);
+DEFINE_ERROR(ConnectionAborted);
+DEFINE_ERROR(SocketIsConnected);
+DEFINE_ERROR(SocketNotConnected);
+DEFINE_ERROR(NotASocket);
+DEFINE_ERROR(HostUnreachable);
+DEFINE_ERROR(NetUnreachable);
+DEFINE_ERROR(UnknownError);
+
+inline std::runtime_error
+throw_specific_errno_error(int error, const std::string &msg)
+{
+ switch (error) {
+ case EACCES:
+ throw PermissionDenied(error, msg);
+ case EPERM:
+ throw OperationNotPermitted(error, msg);
+ case ENOENT:
+ throw NotFound(error, msg);
+ case EIO:
+ throw IoError(error, msg);
+ case EINVAL:
+ throw Invalid(error, msg);
+ case ENXIO:
+ case ENODEV:
+ throw NoDevice(error, msg);
+ case EBUSY:
+ throw Busy(error, msg);
+ case EEXIST:
+ throw Exists(error, msg);
+ case ENOTDIR:
+ throw NotADirectory(error, msg);
+ case EISDIR:
+ throw IsADirectory(error, msg);
+ case ENOSPC:
+ throw NoSpace(error, msg);
+ case ESPIPE:
+ throw IllegalSeek(error, msg);
+ case EPIPE:
+ throw BrokenPipe(error, msg);
+ case ELOOP:
+ throw TooManySymlinks(error, msg);
+ case ENAMETOOLONG:
+ throw NameTooLong(error, msg);
+ case ENOMEM:
+ throw OutOfMemory(error, msg);
+ case ENOSYS:
+ throw NotImplemented(error, msg);
+ case EFAULT:
+ throw BadAddress(error, msg);
+ case EBADF:
+ throw BadFileDescriptor(error, msg);
+ case ERANGE:
+ throw OutOfRange(error, msg);
+ case EAGAIN: // same as EWOULDBLOCK
+ throw TryAgain(error, msg);
+ case ECHILD:
+ throw NoChild(error, msg);
+ case ETXTBSY:
+ throw TextBusy(error, msg);
+ case EINTR:
+ throw Interrupted(error, msg);
+ case EINPROGRESS:
+ throw OperationInProgress(error, msg);
+ case ENOTSUP: // same as EOPNOTSUPP
+ throw NotSupported(error, msg);
+ case ETIMEDOUT:
+ throw OperationTimedOut(error, msg);
+ case EALREADY:
+ throw ConnectionInProgress(error, msg);
+ case ENOBUFS:
+ throw NoBufferSpace(error, msg);
+ case EPROTONOSUPPORT:
+ throw ProtocolNotSupported(error, msg);
+ case ENOPROTOOPT:
+ throw ProtocolNotAvailable(error, msg);
+ case EPROTO:
+ throw ProtocolError(error, msg);
+ case EAFNOSUPPORT:
+ throw AddressFamilyNotSupported(error, msg);
+ case EADDRINUSE:
+ throw AddressInUse(error, msg);
+ case ECONNREFUSED:
+ throw ConnectionRefused(error, msg);
+ case ECONNABORTED:
+ throw ConnectionAborted(error, msg);
+ case EISCONN:
+ throw SocketIsConnected(error, msg);
+ case ENOTCONN:
+ throw SocketNotConnected(error, msg);
+ case ENOTSOCK:
+ throw NotASocket(error, msg);
+ case EHOSTUNREACH:
+ throw HostUnreachable(error, msg);
+ case ENETUNREACH:
+ throw NetUnreachable(error, msg);
+ }
+ // default
+ throw UnknownError(error, msg);
+}
+
+// callers can simply say "syserr::throw_errno_error(errno);"
+inline std::runtime_error
+throw_errno_error(int error)
+{
+ std::string msg(::strerror(error));
+ return throw_specific_errno_error(error, msg);
+}
+
+inline std::runtime_error
+throw_errno_error(int error, std::string msg)
+{
+ msg += std::string(": ") + ::strerror(error);
+ return throw_specific_errno_error(error, msg);
+}
+
+} // namespace syserr
+
+#endif // PP_UTIL_SYSERROR_HPP__
=======================================
--- /dev/null
+++ /trunk/version.h Sun Nov 8 23:26:22 2009
@@ -0,0 +1,26 @@
+/* Copyright (c) Tim Hockin, 2009 */
+#ifndef PP_VERSION_H__
+#define PP_VERSION_H__
+
+#include <stdint.h>
+
+namespace pp {
+
+//
+// PP version constants - these need to be extern so they are always
linked.
+//
+extern const uint8_t ver_major;
+extern const uint8_t ver_minor;
+extern const uint16_t ver_micro;
+extern const uint32_t version;
+extern const char *version_string;
+
+inline uint32_t
+MAKE_VERSION(uint8_t major, uint8_t minor, uint16_t micro)
+{
+ return ((major << 24) | (minor << 16) | (micro));
+}
+
+} // namespace pp
+
+#endif // PP_VERSION_H__
=======================================
--- /trunk/bignum.h Tue May 5 12:06:12 2009
+++ /dev/null
@@ -1,509 +0,0 @@
-/* Copyright (c) Tim Hockin, 2007 */
-#ifndef PP_BIGNUM_H__
-#define PP_BIGNUM_H__
-
-#include <gmpxx.h>
-#include <ostream>
-#include <sstream>
-#include "bit_buffer.h"
-
-#define BITS_PER_LONG (sizeof(long)*CHAR_BIT)
-
-namespace bignum {
-
-//
-// All BigInts are signed. GMP will allow you to create a BigInt from
-// either signed or unsigned raw integers, and it will do "the right
-// thing". Unsigned raw integers will always create a positive BigInt. It
-// is not clear exactly what happens if you read an out-of-bounds positive
-// BigInt as a signed value (e.g. ULONG_MAX read back as signed long). It
-// is also not clear what happens if you read a negative BigInt as an
-// unsigned value (e.g. -1 read back as unsigned int). Just don't do those
-// things, and we'll all be happy.
-//
-class BigInt: public mpz_class
-{
- public:
- // ctors are mostly pass-thru to GMP
- BigInt() {}
- BigInt(const BigInt &that): mpz_class(that) {}
- template <class T, class U>
- BigInt(const __gmp_expr<T, U> &expr): mpz_class(expr) {}
- explicit BigInt(const char *str, int base=0): mpz_class(str, base) {}
- explicit BigInt(const std::string &str, int base=0)
- : mpz_class(str, base) {}
- BigInt(signed char value): mpz_class(value) {}
- BigInt(unsigned char value): mpz_class(value) {}
- BigInt(signed short value): mpz_class(value) {}
- BigInt(unsigned short value): mpz_class(value) {}
- BigInt(signed int value): mpz_class(value) {}
- BigInt(unsigned int value): mpz_class(value) {}
- BigInt(signed long value): mpz_class(value) {}
- BigInt(unsigned long value): mpz_class(value) {}
- BigInt(float value): mpz_class(value) {}
- BigInt(double value): mpz_class(value) {}
- // GMP only supports up to 'long' args for it's ctors. We can
- // support 'long long' at a small cost on 32 bit systems, and for
- // free on 64 bit systems.
- BigInt(signed long long value)
- {
- *this = value;
- }
- BigInt(unsigned long long value)
- {
- *this = value;
- }
- BigInt(const util::BitBuffer &bitbuf)
- {
- *this = bitbuf;
- }
-
- // assignment operators are mostly pass-thru to GMP
- BigInt &
- operator=(const BigInt &that)
- { mpz_class::operator=(that); return *this; }
- template <class T, class U>
- BigInt &
- operator=(const __gmp_expr<T, U> &that)
- { mpz_class::operator=(that); return *this; }
- BigInt &
- operator=(const std::string &that)
- { mpz_class::operator=(that); return *this; }
- BigInt &
- operator=(const char *that)
- { mpz_class::operator=(that); return *this; }
- BigInt &
- operator=(signed char that)
- { mpz_class::operator=(that); return *this; }
- BigInt &
- operator=(unsigned char that)
- { mpz_class::operator=(that); return *this; }
- BigInt &
- operator=(signed short that)
- { mpz_class::operator=(that); return *this; }
- BigInt &
- operator=(unsigned short that)
- { mpz_class::operator=(that); return *this; }
- BigInt &
- operator=(signed int that)
- { mpz_class::operator=(that); return *this; }
- BigInt &
- operator=(unsigned int that)
- { mpz_class::operator=(that); return *this; }
- BigInt &
- operator=(signed long that)
- { mpz_class::operator=(that); return *this; }
- BigInt &
- operator=(unsigned long that)
- { mpz_class::operator=(that); return *this; }
- BigInt &
- operator=(float that)
- { mpz_class::operator=(that); return *this; }
- BigInt &
- operator=(double that)
- { mpz_class::operator=(that); return *this; }
- BigInt &
- operator=(signed long long that)
- {
- if ((sizeof(long) == sizeof(long long))
- || (that >= LONG_MIN && that <= LONG_MAX)) {
- // simple
- *this = (signed long)that;
- return *this;
- }
-
- // not so simple
- int multiplier = 1;
- if (that < 0) {
- multiplier = -1;
- that *= -1;
- }
-
- unsigned long vlo = that;
- unsigned long vhi = (that >> (BITS_PER_LONG-1)) >> 1;
- *this = vhi;
- *this <<= BITS_PER_LONG;
- *this += vlo;
- *this *= multiplier;
-
- return *this;
- }
- BigInt &
- operator=(unsigned long long that)
- {
- if ((sizeof(long) == sizeof(long long))
- || (that <= ULONG_MAX)) {
- // simple
- *this = (unsigned long)that;
- return *this;
- }
-
- // not so simple
- unsigned long vlo = that;
- unsigned long vhi = (that >> (BITS_PER_LONG-1)) >> 1;
- *this = vhi;
- *this <<= BITS_PER_LONG;
- *this += vlo;
-
- return *this;
- }
- BigInt &
- operator=(const util::BitBuffer &bitbuf)
- {
- // mpz_import() seems to not work.
- *this = 0;
- for (std::size_t i = bitbuf.size_bytes(); i > 0; i--) {
- *this <<= CHAR_BIT;
- *this += bitbuf.byte_at(i-1);
- }
- return *this;
- }
-
- // GMP only supports up to 'long' args for it's get methods. We
- // can support 'long long' at a small cost on 32 bit systems, and
- // for free on 64 bit systems.
- long long
- get_si() const
- {
- return get_int();
- }
- long long
- get_int() const
- {
- if (sizeof(long) == sizeof(long long)
- || (*this >= LONG_MIN && *this <= LONG_MAX)) {
- // simple
- return mpz_class::get_si();
- }
-
- // not so simple
- BigInt myval(*this);
- int multiplier = 1;
- if (myval < 0) {
- multiplier = -1;
- myval *= -1;
- }
-
- unsigned long rlo = myval.mpz_class::get_ui();
- myval >>= BITS_PER_LONG;
- unsigned long long rhi = myval.mpz_class::get_ui();
- unsigned long long result =
- (((rhi << (BITS_PER_LONG-1))<<1) | rlo);
- result *= multiplier;
-
- return result;
- }
-
- unsigned long long
- get_ui() const
- {
- return get_int();
- }
- unsigned long long
- get_uint() const
- {
- if ((sizeof(long) == sizeof(long long))
- || (*this >= 0 && *this <= ULONG_MAX)) {
- // simple
- return mpz_class::get_ui();
- }
-
- // not so simple
- BigInt myval(*this);
- unsigned long rlo = myval.mpz_class::get_ui();
- myval >>= BITS_PER_LONG;
- unsigned long long rhi = myval.mpz_class::get_ui();
- unsigned long long result =
- (((rhi << (BITS_PER_LONG-1))<<1) | rlo);
-
- return result;
- }
-
- util::BitBuffer
- get_bitbuffer(std::size_t bits=0) const
- {
- // mpz_export() seems to not work.
- std::size_t bytes = 0;
- BigInt tmp(*this);
- while (tmp != 0) {
- bytes++;
- tmp >>= CHAR_BIT;
- }
- if (bits) {
- bytes = (bits + (CHAR_BIT-1)) / CHAR_BIT;
- }
- util::BitBuffer bitbuf(bits ? bits : (bytes * CHAR_BIT));
-
- BigInt myval(*this);
- for (std::size_t i = 0; i < bytes; i++) {
- bitbuf.byte_at(i) = myval.get_uint() & 0xff;
- myval >>= CHAR_BIT;
- }
-
- return bitbuf;
- }
-
- // Stay consistent with the mpz_class API.
- bool
- fits_slonglong_p() const
- {
- return (*this >= LONG_MIN && *this <= LONG_MAX);
- }
- bool
- fits_ulonglong_p() const
- {
- return (*this >= 0 && *this <= ULONG_MAX);
- }
-
- // Count the number of set bits.
- unsigned long
- popcount() const
- {
- return mpz_popcount(get_mpz_t());
- }
- static unsigned long
- popcount(const BigInt &val)
- {
- return val.popcount();
- }
-
- // Exponentiate this BigInt and return the result.
- BigInt
- pow(unsigned long exponent) const
- {
- BigInt bn(*this);
- mpz_pow_ui(bn.get_mpz_t(), get_mpz_t(), exponent);
- return bn;
- }
- static BigInt
- pow(const BigInt &val, unsigned long exponent)
- {
- return val.pow(exponent);
- }
-
- // Exponentiate this BigInt in place.
- void
- raise(unsigned long exponent)
- {
- mpz_pow_ui(get_mpz_t(), get_mpz_t(), exponent);
- }
-
- // convert to a decimal string
- std::string
- to_dec_string() const
- {
- std::ostringstream oss;
- oss << *this;
- return oss.str();
- }
-
- // convert to a hex string
- std::string
- to_hex_string() const
- {
- std::ostringstream oss;
- oss << "0x" << std::hex << *this;
- return oss.str();
- }
-
- // convert to an octal string
- std::string
- to_oct_string() const
- {
- std::ostringstream oss;
- oss << "0" << std::oct << *this;
- return oss.str();
- }
-
- // convert to a string
- std::string
- to_string() const
- {
- return to_dec_string();
- }
-
- // This strangeness allows BigInt to safely be treated as bools.
- // It's commonly known as the "safe bool idiom".
- private:
- typedef void (BigInt::*bool_type)() const;
- void convert_to_bool() const {}
- public:
- operator bool_type() const
- {
- if (*this == 0) {
- return NULL;
- }
- return &BigInt::convert_to_bool;
- }
-};
-
-//
-// Add some operators that the GMP implementation does not define.
-// Note: These are defined on mpz_class, not on BigInt.
-//
-
-inline mpz_class
-operator<<(const mpz_class &lhs, const mpz_class &rhs)
-{
- return (lhs << rhs.get_ui());
-}
-inline mpz_class
-operator>>(const mpz_class &lhs, const mpz_class &rhs)
-{
- return (lhs >> rhs.get_ui());
-}
-inline mpz_class &
-operator|=(mpz_class &lhs, const mpz_class &rhs)
-{
- lhs = lhs | rhs;
- return lhs;
-}
-inline mpz_class &
-operator&=(mpz_class &lhs, const mpz_class &rhs)
-{
- lhs = lhs & rhs;
- return lhs;
-}
-inline mpz_class &
-operator^=(mpz_class &lhs, const mpz_class &rhs)
-{
- lhs = lhs ^ rhs;
- return lhs;
-}
-
-//
-// Because BigInt supports 'long long' types, but GMP doesn't, we need
-// explicit operators for comparisons.
-// Note: These are defined on mpz_class, not on BigInt.
-//
-
-// ==
-inline bool
-operator==(const mpz_class &lhs, signed long long rhs)
-{
- return (lhs == BigInt(rhs));
-}
-inline bool
-operator==(const mpz_class &lhs, unsigned long long rhs)
-{
- return (lhs == BigInt(rhs));
-}
-inline bool
-operator==(signed long long lhs, const mpz_class &rhs)
-{
- return (BigInt(lhs) == rhs);
-}
-inline bool
-operator==(unsigned long long lhs, const mpz_class &rhs)
-{
- return (BigInt(lhs) == rhs);
-}
-// !=
-inline bool
-operator!=(const mpz_class &lhs, signed long long rhs)
-{
- return (lhs != BigInt(rhs));
-}
-inline bool
-operator!=(const mpz_class &lhs, unsigned long long rhs)
-{
- return (lhs != BigInt(rhs));
-}
-inline bool
-operator!=(signed long long lhs, const mpz_class &rhs)
-{
- return (BigInt(lhs) != rhs);
-}
-inline bool
-operator!=(unsigned long long lhs, const mpz_class &rhs)
-{
- return (BigInt(lhs) != rhs);
-}
-// <
-inline bool
-operator<(const mpz_class &lhs, signed long long rhs)
-{
- return (lhs < BigInt(rhs));
-}
-inline bool
-operator<(const mpz_class &lhs, unsigned long long rhs)
-{
- return (lhs < BigInt(rhs));
-}
-inline bool
-operator<(signed long long lhs, const mpz_class &rhs)
-{
- return (BigInt(lhs) < rhs);
-}
-inline bool
-operator<(unsigned long long lhs, const mpz_class &rhs)
-{
- return (BigInt(lhs) < rhs);
-}
-// >
-inline bool
-operator>(const mpz_class &lhs, signed long long rhs)
-{
- return (lhs > BigInt(rhs));
-}
-inline bool
-operator>(const mpz_class &lhs, unsigned long long rhs)
-{
- return (lhs > BigInt(rhs));
-}
-inline bool
-operator>(signed long long lhs, const mpz_class &rhs)
-{
- return (BigInt(lhs) > rhs);
-}
-inline bool
-operator>(unsigned long long lhs, const mpz_class &rhs)
-{
- return (BigInt(lhs) > rhs);
-}
-// <=
-inline bool
-operator<=(const mpz_class &lhs, signed long long rhs)
-{
- return (lhs <= BigInt(rhs));
-}
-inline bool
-operator<=(const mpz_class &lhs, unsigned long long rhs)
-{
- return (lhs <= BigInt(rhs));
-}
-inline bool
-operator<=(signed long long lhs, const mpz_class &rhs)
-{
- return (BigInt(lhs) <= rhs);
-}
-inline bool
-operator<=(unsigned long long lhs, const mpz_class &rhs)
-{
- return (BigInt(lhs) <= rhs);
-}
-// >=
-inline bool
-operator>=(const mpz_class &lhs, signed long long rhs)
-{
- return (lhs >= BigInt(rhs));
-}
-inline bool
-operator>=(const mpz_class &lhs, unsigned long long rhs)
-{
- return (lhs >= BigInt(rhs));
-}
-inline bool
-operator>=(signed long long lhs, const mpz_class &rhs)
-{
- return (BigInt(lhs) >= rhs);
-}
-inline bool
-operator>=(unsigned long long lhs, const mpz_class &rhs)
-{
- return (BigInt(lhs) >= rhs);
-}
-
-} // namespace bignum
-
-#endif // PP_BIGNUM_H__
=======================================
--- /trunk/bit_buffer.h Tue May 5 12:06:12 2009
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright (c) Tim Hockin, 2008
-#ifndef PP_BIT_BUFFER_H__
-#define PP_BIT_BUFFER_H__
-
-#include <ostream>
-#include <sstream>
-#include <boost/format.hpp>
-#include <boost/shared_array.hpp>
-#include <stdint.h>
-#include <string.h>
-
-namespace util {
-
-//
-// This is a simple buffer of a fixed bit width. It is a suitable place
-// to store data for lower-level things like read() and write(). Once
-// initialized, it can not be resized except through a call to reset().
-//
-// Bitbuffers are always stored in little endian form as an array of
-// 'uint8_t'. Bits beyond the specified bit width are initialized to 0,
-// but could be changed by the caller.
-//
-class BitBuffer
-{
- private:
- typedef boost::shared_array<uint8_t> uint8_array;
- uint8_array m_array;
- std::size_t m_bits;
- std::size_t m_bytes;
-
- public:
- BitBuffer(std::size_t bits = 0, uint8_t pattern = 0)
- {
- reset(bits, pattern);
- }
- BitBuffer(unsigned long bits, uint8_t *data)
- {
- reset(bits, data);
- }
-
- void
- reset(std::size_t bits = 0, uint8_t pattern = 0)
- {
- m_bits = bits;
- m_bytes = (m_bits+(CHAR_BIT-1))/CHAR_BIT;
-
- uint8_array tmp(NULL);
- if (bits) {
- tmp = uint8_array(new uint8_t[m_bytes]);
- memset(tmp.get(), pattern, m_bytes);
- }
- m_array = tmp;
- }
-
- void
- reset(std::size_t bits, uint8_t *data)
- {
- m_bits = bits;
- m_bytes = (m_bits+(CHAR_BIT-1))/CHAR_BIT;
-
- uint8_array tmp(NULL);
- if (bits) {
- tmp = uint8_array(new uint8_t[m_bytes]);
- memcpy(tmp.get(), data, m_bytes);
- }
- m_array = tmp;
- }
-
- void
- fill(uint8_t pattern)
- {
- memset(m_array.get(), pattern, m_bytes);
- }
-
- uint8_t *
- get()
- {
- return m_array.get();
- }
- const uint8_t *
- get() const
- {
- return m_array.get();
- }
-
- std::size_t
- size_bits() const
- {
- return m_bits;
- }
- std::size_t
- size_bytes() const
- {
- return m_bytes;
- }
-
- uint8_t &
- byte_at(int index)
- {
- return m_array[index];
- }
- const uint8_t &
- byte_at(int index) const
- {
- return m_array[index];
- }
-
- std::string
- to_string() const;
-};
-
-// stream output
-inline std::ostream &
-operator<<(std::ostream& o, const BitBuffer &bitbuf)
-{
- o << "0x";
- // this is signed on purpose
- signed long i = bitbuf.size_bytes()-1;
- if (i >= 0) {
- // skip leading zeros
- while (i > 0 && bitbuf.get()[i] == 0) {
- i--;
- }
- // print the most-significant non-zero byte
- o << boost::format("%x") %(int)bitbuf.get()[i];
- i--;
- // print the rest
- for (i=i; i >= 0; i--) {
- o << boost::format("%02x") %(int)bitbuf.get()[i];
- }
- } else {
- o << "0";
- }
-
- return o;
-}
-
-inline std::string
-BitBuffer::to_string() const
-{
- std::ostringstream oss;
- oss << *this;
- return oss.str();
-}
-
-} // namespace util
-
-#endif // PP_BIT_BUFFER_H__
=======================================
--- /trunk/filesystem.h Wed May 6 16:10:05 2009
+++ /dev/null
@@ -1,817 +0,0 @@
-//
-// Thin wrappers around standard file/directory IO.
-// Tim Hockin <tho...@hockin.org>
-//
-#ifndef FILESYSTEM_HPP__
-#define FILESYSTEM_HPP__
-
-#undef _FILE_OFFSET_BITS
-#define _FILE_OFFSET_BITS 64
-
-#include <string>
-#include <stdexcept>
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-
-#include <unistd.h>
-#include <cstdlib>
-#include <fcntl.h>
-#include <cstring>
-#include <cerrno>
-#include <dirent.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-#include "pp.h"
-#include "syserror.h"
-
-namespace filesystem {
-
-using std::size_t;
-using ::off_t;
-
-// a file
-class File;
-typedef boost::shared_ptr<File> FilePtr;
-typedef boost::shared_ptr<const File> ConstFilePtr;
-typedef boost::weak_ptr<File> WeakFilePtr;
-
-// a memory-mapped area of a file
-class FileMapping;
-typedef boost::shared_ptr<FileMapping> FileMappingPtr;
-
-// a directory
-class Directory;
-typedef boost::shared_ptr<Directory> DirectoryPtr;
-
-// a directory entry
-class Direntry;
-typedef boost::shared_ptr<Direntry> DirentryPtr;
-
-
-//
-// FileMapping
-//
-// This class tracks an individual mmap() of a file. When this class is
-// destructed, the mapping is munmap()ed.
-//
-// Users can not create instances of this class. To create a mapping call
-// filesystem::File::mmap(), which returns a smart pointer to one of these.
-//
-class FileMapping
-{
- friend class File;
-
- private:
- // constructors - private to prevent abuse, defined later
- FileMapping(const ConstFilePtr &file, off_t offset, size_t length,
- int prot = PROT_READ, int flags = MAP_SHARED);
-
- public:
- // destructor
- ~FileMapping()
- {
- unmap();
- }
-
- void
- unmap()
- {
- if (!is_mapped()) {
- return;
- }
-
- // If other references exist, this can be bad. Better to
- // use the destructor whenever possible.
- int r = munmap(m_real_address, m_real_length);
- if (r < 0) {
- syserr::throw_errno_error(errno,
- "filesystem::FileMapping::unmap()");
- }
- m_address = m_real_address = NULL;
- m_length = m_real_length = 0;
- }
-
- bool
- is_mapped() const
- {
- return (m_address != NULL);
- }
-
- void *
- address() const
- {
- return (void *)m_address;
- }
-
- off_t
- offset() const
- {
- return m_offset;
- }
-
- size_t
- length() const
- {
- return m_length;
- }
-
- int
- prot() const
- {
- return m_prot;
- }
-
- int
- flags() const
- {
- return m_flags;
- }
-
- private:
- ConstFilePtr m_file;
- // these are the file offset and map length that were requested
- off_t m_offset;
- size_t m_length;
- uint8_t *m_address;
- // these are the aligned file offset and map length
- off_t m_real_offset;
- size_t m_real_length;
- uint8_t *m_real_address;
- // flags for the mapping
- int m_prot;
- int m_flags;
-};
-
-//
-// file
-//
-// This class represents a single opened file. When this class is
-// destructed, the file descriptor is close()d.
-//
-// Users can not create instances of this class. To open a file call
-// filesystem::File::open(), which returns a smart pointer to one of these.
-//
-class File
-{
- private:
- // constructors - private to prevent abuse
- File(): m_path(""), m_flags(-1), m_fd(-1)
- {
- }
- File(const File &that)
- : m_path(that.m_path), m_flags(that.m_flags), m_fd(that.m_fd)
- {
- }
- File &
- operator=(const File &that)
- {
- m_path = that.m_path;
- m_flags = that.m_flags;
- m_fd = that.m_fd;
- return *this;
- }
-
- public:
- // destructor
- ~File()
- {
- if (m_fd >= 0) {
- close();
- }
- }
-
- static FilePtr
- open(const std::string &path, int flags)
- {
- FilePtr f(new File());
-
- f->m_this_ptr = f;
- f->m_path = path;
- f->m_flags = flags;
- f->m_fd = ::open(path.c_str(), flags);
- if (f->m_fd < 0) {
- syserr::throw_errno_error(errno,
- "filesystem::File::open(" + path + ")");
- }
-
- return f;
- }
-
- static FilePtr
- fdopen(int fd, const std::string &path = "", int flags = -1)
- {
- FilePtr f(new File());
-
- f->m_this_ptr = f;
- f->m_path = path;
- f->m_flags = flags;
- f->m_fd = fd;
-
- return f;
- }
-
- static FilePtr
- tempfile(std::string path_template = "")
- {
- if (path_template == "") {
- path_template = find_tmp_dir() + "/"
- + to_string(getpid()) + ".XXXXXX";
- }
-
- int r;
- char buf[path_template.size()+1]; // for mkstemp()
-
- path_template.copy(buf, path_template.size());
- buf[path_template.size()] = '\0';
- r = ::mkstemp(buf);
- if (r < 0) {
- syserr::throw_errno_error(errno,
- "filesystem::File::tempfile("+path_template+")");
- }
-
- return fdopen(r, buf, O_RDWR);
- }
-
- // warning: this is racy, but sometimes that is OK
- static std::string
- tempname(std::string path_template = "")
- {
- filesystem::FilePtr f
- = filesystem::File::tempfile(path_template);
- std::string filename = f->path();
- f->close();
- unlink(filename);
- return filename;
- }
-
- void
- reopen(int new_flags)
- {
- int new_fd;
-
- new_fd = ::open(m_path.c_str(), new_flags);
- if (new_fd < 0) {
- syserr::throw_errno_error(errno,
- "filesystem::File::open(" + m_path + ")");
- }
-
- int tmp = m_fd;
- m_flags = new_flags;
- m_fd = new_fd;
- if (::close(tmp) < 0) {
- syserr::throw_errno_error(errno,
- "filesystem::File::close(" + m_path + ")");
- }
- }
-
- void
- close()
- {
- //
- // If other references exist, this can be bad. Better to
- // use the destructor whenever possible.
- //
- if (::close(m_fd) < 0) {
- syserr::throw_errno_error(errno,
- "filesystem::File::close(" + m_path + ")");
- }
- m_fd = -1;
- }
-
- static void
- unlink(const std::string &path)
- {
- int r;
-
- r = ::unlink(path.c_str());
- if (r < 0) {
- syserr::throw_errno_error(errno,
- "filesystem::File::unlink(" + path + ")");
- }
- }
-
- void
- unlink()
- {
- unlink(m_path);
- }
-
- size_t
- read(void *buf, size_t size) const
- {
- int r;
-
- r = ::read(m_fd, buf, size);
- if (r < 0) {
- syserr::throw_errno_error(errno,
- "filesystem::File::read(" + m_path + ")");
- }
-
- return r;
- }
-
- //FIXME: stat
- //FIXME: create (static)
- //FIXME: rename (static with 2 args, and 1 arg)
- //FIXME: touch
- //FIXME: add full_read() / full_write() methods?
-
- std::string
- read_line() const
- {
- std::string s;
- char buf[2] = " ";
-
- do {
- read(buf, 1);
- //FIXME: check for errors
- s.append(buf);
- } while (buf[0] != '\n');
-
- return s;
- }
-
- size_t
- write(void *buf, size_t size) const
- {
- int r;
-
- r = ::write(m_fd, buf, size);
- if (r < 0) {
- syserr::throw_errno_error(errno,
- "filesystem::File::write(" + m_path + ")");
- }
-
- return r;
- }
-
- FileMappingPtr
- mmap(off_t offset, size_t length, int prot = -1,
- int flags = MAP_SHARED) const
- {
- if (prot == -1) {
- if (mode() == O_RDONLY) {
- prot = PROT_READ;
- } else if (mode() == O_WRONLY) {
- prot = PROT_WRITE;
- } else if (mode() == O_RDWR) {
- prot = PROT_READ | PROT_WRITE;
- }
- }
-
- return FileMappingPtr(new FileMapping(
- ConstFilePtr(m_this_ptr),
- offset, length, prot, flags));
- }
-
- size_t
- seek(off_t offset, int whence) const
- {
- off_t r;
-
- r = ::lseek(m_fd, offset, whence);
- if (r == (off_t)-1) {
- syserr::throw_errno_error(errno,
- "filesystem::File::seek(" + m_path + ")");
- }
-
- // convert off_t (signed) to size_t (unsigned)
- return r;
- }
-
- static size_t
- size(const std::string &path)
- {
- int r;
- struct stat st;
-
- r = ::stat(path.c_str(), &st);
- if (r < 0) {
- syserr::throw_errno_error(errno,
- "filesystem::File::size(" + path + ")");
- }
-
- // convert off_t (signed) to size_t (unsigned)
- return st.st_size;
- }
-
- size_t
- size() const
- {
- int r;
- struct stat st;
-
- r = ::fstat(m_fd, &st);
- if (r < 0) {
- syserr::throw_errno_error(errno,
- "filesystem::File::size(" + m_path + ")");
- }
-
- // convert off_t (signed) to size_t (unsigned)
- return st.st_size;
- }
-
- size_t
- tell() const
- {
- return seek(0, SEEK_CUR);
- }
-
- bool
- is_eof() const
- {
- return (tell() >= size());
- }
-
- bool
- is_open() const
- {
- return (m_fd >= 0);
- }
-
- std::string
- path() const
- {
- return m_path;
- }
-
- int
- mode() const
- {
- return m_flags & O_ACCMODE;
- }
-
- int
- flags() const
- {
- return m_flags;
- }
-
- int
- fd() const
- {
- return m_fd;
- }
-
- private:
- WeakFilePtr m_this_ptr;
- std::string m_path;
- int m_flags;
- int m_fd;
-
- static std::string
- find_tmp_dir()
- {
- char *p;
-
- p = getenv("TMPDIR");
- if (p) {
- return p;
- }
- p = getenv("TEMPDIR");
- if (p) {
- return p;
- }
- #ifdef P_tmpdir
- return P_tmpdir;
- #endif
- return "/tmp";
- }
-};
-
-inline
-FileMapping::FileMapping(const ConstFilePtr &file,
- off_t offset, size_t length, int prot, int flags)
- : m_file(file),
- m_offset(offset), m_length(length), m_address(NULL),
- m_real_offset(0), m_real_length(0), m_real_address(NULL),
- m_prot(prot), m_flags(flags)
-{
- size_t pgsize;
- size_t pgmask;
- off_t ptr_off;
-
- // maps need to be page aligned
- pgsize = getpagesize();
- pgmask = pgsize - 1;
- ptr_off = m_offset & pgmask;
- m_real_offset = m_offset & ~((uint64_t)pgmask);
- m_real_length = pgsize + ((m_length + pgmask) & ~pgmask);
-
- m_real_address = (uint8_t *)mmap(NULL, m_real_length, prot,
- flags, m_file->fd(), m_real_offset);
- if (!m_real_address || m_real_address == MAP_FAILED) {
- syserr::throw_errno_error(errno,
- "filesystem::FileMapping::FileMapping()");
- }
-
- m_address = m_real_address + ptr_off;
-}
-
-//
-// Device
-//
-// This class represents a single device node.
-//
-// Users can not create instances of this class. To open a device call
-// filesystem::Device::open(), which returns a smart pointer to one of
these.
-//
-class Device: public File
-{
- public:
- static void
- mkdev(const std::string &path, mode_t perms, int type,
- int major, int minor)
- {
- int r;
-
- r = ::mknod(path.c_str(), perms | type,
- ::makedev(major, minor));
- if (r < 0) {
- syserr::throw_errno_error(errno,
- "filesystem::Device::mkdev(" + path + ")");
- }
- }
-};
-
-//
-// Direntry
-//
-// This class represents a single directory entry.
-//
-// Users can not create instances of this class. To access a direntry, use
-// filesystem::Directory::read(), which returns a smart pointer to one of
-// these.
-//
-class Direntry
-{
- friend class Directory;
-
- private:
- // constructor - implicit conversion from ::dirent
- Direntry(struct ::dirent *de): m_dirent(de)
- {
- }
-
- public:
- // destructor
- ~Direntry()
- {
- }
-
- static bool
- exists(const std::string &path)
- {
- struct ::stat st;
- int r = ::stat(path.c_str(), &st);
- return (r == 0);
- }
-
- std::string
- name() const
- {
- return m_dirent->d_name;
- }
-
- bool
- is_file() const
- {
- return m_dirent->d_type & DT_REG;
- }
-
- static bool
- is_file(const std::string &path)
- {
- struct ::stat st;
- return (::stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode));
- }
-
- bool
- is_dir() const
- {
- return m_dirent->d_type & DT_DIR;
- }
-
- static bool
- is_dir(const std::string &path)
- {
- struct ::stat st;
- return (::stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode));
- }
-
- bool
- is_link() const
- {
- return m_dirent->d_type & DT_LNK;
- }
-
- static bool
- is_link(const std::string &path)
- {
- struct ::stat st;
- return (::stat(path.c_str(), &st) == 0 && S_ISLNK(st.st_mode));
- }
-
- bool
- is_fifo() const
- {
- return m_dirent->d_type & DT_FIFO;
- }
-
- static bool
- is_fifo(const std::string &path)
- {
- struct ::stat st;
- return (::stat(path.c_str(), &st) == 0 && S_ISFIFO(st.st_mode));
- }
-
- bool
- is_socket() const
- {
- return m_dirent->d_type & DT_SOCK;
- }
-
- static bool
- is_socket(const std::string &path)
- {
- struct ::stat st;
- return (::stat(path.c_str(), &st) == 0 && S_ISSOCK(st.st_mode));
- }
-
- bool
- is_chrdev() const
- {
- return m_dirent->d_type & DT_CHR;
- }
-
- static bool
- is_chrdev(const std::string &path)
- {
- struct ::stat st;
- return (::stat(path.c_str(), &st) == 0 && S_ISCHR(st.st_mode));
- }
-
- bool
- is_blkdev() const
- {
- return m_dirent->d_type & DT_BLK;
- }
-
- static bool
- is_blkdev(const std::string &path)
- {
- struct ::stat st;
- return (::stat(path.c_str(), &st) == 0 && S_ISBLK(st.st_mode));
- }
-
- bool
- is_dev() const
- {
- return (m_dirent->d_type & DT_CHR)
- || (m_dirent->d_type & DT_BLK);
- }
-
- static bool
- is_dev(const std::string &path)
- {
- struct ::stat st;
- return (::stat(path.c_str(), &st) == 0
- && (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)));
- }
-
- private:
- struct ::dirent *m_dirent;
-};
-
-//
-// Directory
-//
-// This class represents a single directory. When this class is
destructed,
-// the directory is closedir()d.
-//
-// Users can not create instances of this class. To access a direntry, use
-// filesystem::Directory::open(), which returns a smart pointer to one of
-// these.
-//
-class Directory
-{
- private:
- // constructors - private to prevent abuse
- Directory(): m_path(""), m_dir(NULL)
- {
- }
- Directory(const Directory &that)
- : m_path(that.m_path), m_dir(that.m_dir)
- {
- }
- Directory &
- operator=(const Directory &that)
- {
- m_path = that.m_path;
- m_dir = that.m_dir;
- return *this;
- }
-
- public:
- // destructor
- ~Directory()
- {
- if (m_dir) {
- close();
- }
- }
-
- static DirectoryPtr
- open(const std::string &path)
- {
- DirectoryPtr d(new Directory());
-
- d->m_dir = ::opendir(path.c_str());
- if (!d->m_dir) {
- syserr::throw_errno_error(errno,
- "filesystem::Directory::open(" + path + ")");
- }
-
- return d;
- }
-
- void
- close()
- {
- ::closedir(m_dir);
- m_dir = NULL;
- }
-
- //FIXME: create
- //FIXME: remove
- //FIXME: mkdtemp => tempname() and tempdir()
-
- DirentryPtr
- read() const
- {
- struct ::dirent *de = ::readdir(m_dir);
- if (de) {
- return DirentryPtr(new Direntry(de));
- }
- return DirentryPtr();
- }
-
- void
- rewind() const
- {
- ::rewinddir(m_dir);
- }
-
- bool
- is_open() const
- {
- return (m_dir != NULL);
- }
-
- const std::string &
- path() const
- {
- return m_path;
- }
-
- private:
- std::string m_path;
- ::DIR *m_dir;
-};
-
-inline void
-chdir(const string &path)
-{
- int ret = ::chdir(path.c_str());
- if (ret < 0) {
- syserr::throw_errno_error(errno, path);
- }
-}
-//FIXME: chdir(directory)
-
-inline const string
-getcwd()
-{
- #ifdef _GNU_SOURCE
- char *p = ::get_current_dir_name();
- if (p == NULL) {
- syserr::throw_errno_error(errno, "filesystem::getcwd()");
- }
- string cwd(p);
- free(p);
- return cwd;
- #else
- char buf[4096];
- char *p = ::getcwd(buf, sizeof(buf));
- if (p == NULL) {
- syserr::throw_errno_error(errno, "filesystem::getcwd()");
- }
- return p;
- #endif
-}
-
-} // namespace fs
-
-#endif // FILESYSTEM_HPP__
=======================================
--- /trunk/keyed_vector.h Wed May 6 16:38:28 2009
+++ /dev/null
@@ -1,509 +0,0 @@
-// keyed_vector.cpp
-//
-// Tim Hockin <tho...@hockin.org>
-// 2007
-//
-#ifndef KEYED_VECTOR_HPP__
-#define KEYED_VECTOR_HPP__
-
-#include <vector>
-#include <map>
-#include <stdexcept>
-#include <boost/iterator_adaptors.hpp>
-#include "pp.h"
-#include "debug.h"
-
-namespace util {
-
-//
-// This template class is a thin wrapper to make iterators work for
-// KeyedVector objects. This is largely based on the boost example
-// code for boost::iterator_facade.
-//
-template<typename Titer, typename Tval>
-class KeyedVectorIterator
- : public boost::iterator_facade<KeyedVectorIterator<Titer, Tval>, Tval,
- typename std::iterator_traits<Titer>::iterator_category>
-{
- friend class boost::iterator_core_access;
- template<class,class> friend class KeyedVectorIterator;
-
- public:
- // default constructor
- KeyedVectorIterator() {}
-
- // implicit conversion from the underlying iterator
- KeyedVectorIterator(Titer it): m_it(it) {}
-
- // implicit conversion from iterator to const_iterator
- template<class Tother>
- KeyedVectorIterator(const KeyedVectorIterator<Titer, Tother> &other)
- : m_it(other.m_it), m_trap(other.m_trap) {}
-
- // get the underlying iterator
- const Titer&
- get() const
- {
- return m_it;
- }
-
- private:
- // check for equality
- template<typename Tthat>
- bool
- equal(const KeyedVectorIterator<Titer, Tthat> &that) const
- {
- return (this->m_it == that.m_it);
- }
-
- // move the iterator forward by one
- void
- increment()
- {
- ++m_it;
- }
-
- // move the iterator backward by one
- void
- decrement()
- {
- --m_it;
- }
-
- // move the iterator forward or backward by n
- void
- advance(const std::ptrdiff_t n)
- {
- m_it += n;
- }
-
- // figure out the distance to another iterator
- template<typename Tthere>
- std::ptrdiff_t
- distance_to(const KeyedVectorIterator<Titer, Tthere> &there) const
- {
- return there.m_it - this->m_it;
- }
-
- // get at the referent
- Tval &
- dereference() const
- {
- return *m_it;
- }
-
- private:
- // the actual underlying iterator
- Titer m_it;
-
- // a trap to catch const_iterator to iterator assignment
- Tval *m_trap;
-};
-
-//
-// template class KeyedVector<Tkey, Tval>
-//
-// This template class implements a vector in which each stored object
-// (value) has a specific key. Indexing can be done by integer or by key.
-// When indexing by integer or iterating the data is returned in insert
-// order.
-//
-// This is a brief overview of how it works. First, there is an STL
-// vector of Tval which holds the data elements, in insert order. Then,
-// There is an STL map of Tkey->int. This holds the key elements and the
-// int index of the value in the value vector. Lastly, there is a vector
-// of map iterators, where the index in the vector is parallel to the
-// value vector. This allows you to access the data in order (via the
-// vectors), or to access it randomly (by the map).
-//
-// STL guarantees that map iterators do not get invalidated unless the
-// item pointed to is removed from the map. We rely on this when we store
-// iterators. When an item is removed from any position except the back
-// of the vectors, all subsequent entries must have their indices updated
-// in the map.
-//
-// Notes:
-// - The 'Tkey' type must have an == operator.
-// - Because of the dual modes of indexing, the 'Tkey' type can not be an
-// integral primitive.
-// - Keys must be unique. Inserting a duplicate key will over-write the
-// original value.
-//
-template<typename Tkey, typename Tval>
-class KeyedVector
-{
- typedef std::vector<Tval> Tval_vector;
- typedef typename Tval_vector::iterator Tval_iter;
- typedef std::map<Tkey, int> Tkey_map;
- typedef typename Tkey_map::iterator Tkey_iter;
- typedef std::vector<Tkey_iter> Tkeyptr_vector;
- typedef typename Tkeyptr_vector::iterator Tkeyptr_iter;
-
- // the underlying data representation
- Tkey_map m_keys;
- Tval_vector m_values;
- Tkeyptr_vector m_keyptrs;
-
- public:
- typedef Tval value_type;
- typedef Tkey key_type;
- typedef value_type* pointer;
- typedef const value_type* const_pointer;
- typedef value_type& reference;
- typedef const value_type& const_reference;
- typedef std::size_t size_type;
- typedef std::ptrdiff_t difference_type;
- typedef KeyedVectorIterator<Tval_iter, Tval> iterator;
- typedef KeyedVectorIterator<Tval_iter, const Tval> const_iterator;
- typedef std::reverse_iterator<iterator> reverse_iterator;
- typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
-
- // default ctor
- KeyedVector()
- {
- }
- // copy ctor
- KeyedVector(const KeyedVector &other)
- {
- *this = other;
- }
- // assignment operator
- KeyedVector &
- operator=(const KeyedVector &other)
- {
- // We can't just copy each element, because m_keyptrs
- // holds iterators into m_keys.
- KeyedVector tmp;
- for (size_type i = 0; i < other.size(); i++) {
- tmp.insert(other.key_at(i), other.at(i));
- }
- swap(tmp);
- return *this;
- }
- // swap data
- void
- swap(KeyedVector &other)
- {
- m_keys.swap(other.m_keys);
- m_values.swap(other.m_values);
- m_keyptrs.swap(other.m_keyptrs);
- }
- // clear data
- void
- clear()
- {
- m_keys.clear();
- m_values.clear();
- m_keyptrs.clear();
- }
-
- // get a forward iterator
- iterator
- begin()
- {
- return m_values.begin();
- }
- iterator
- end()
- {
- return m_values.end();
- }
- const_iterator
- begin() const
- {
- KeyedVector *p = const_cast<KeyedVector *>(this);
- return p->begin();
- }
- const_iterator
- end() const
- {
- KeyedVector *p = const_cast<KeyedVector *>(this);
- return p->end();
- }
- // get a reverse iterator
- reverse_iterator
- rbegin()
- {
- return m_values.rbegin();
- }
- reverse_iterator
- rend()
- {
- return m_values.rend();
- }
- const_reverse_iterator
- rbegin() const
- {
- KeyedVector *p = const_cast<KeyedVector *>(this);
- return p->rbegin();
- }
- const_reverse_iterator
- rend() const
- {
- KeyedVector *p = const_cast<KeyedVector *>(this);
- return p->rend();
- }
-
- // get meta-data
- size_type
- size() const
- {
- DASSERT(m_keys.size() == m_values.size());
- return m_values.size();
- }
- size_type
- max_size() const
- {
- return min(m_keys.max_size(), m_values.max_size());
- }
- size_type
- capacity() const
- {
- return m_values.capacity();
- }
- bool
- empty() const
- {
- return m_values.empty();
- }
-
- // get the first or last value - can throw std::out_of_range
- reference
- front()
- {
- bounds_check(0);
- return m_values.front();
- }
- reference
- back()
- {
- bounds_check(0);
- return m_values.back();
- }
- const_reference
- front() const
- {
- bounds_check(0);
- return m_values.front();
- }
- const_reference
- back() const
- {
- bounds_check(0);
- return m_values.back();
- }
-
- // get the value at an int index - can throw std::out_of_range
- reference
- at(size_type index)
- {
- bounds_check(index);
- return m_values[index];
- }
- reference
- operator[](size_type index)
- {
- return at(index);
- }
- const_reference
- at(size_type index) const
- {
- KeyedVector *p = const_cast<KeyedVector *>(this);
- return p->at(index);
- }
- const_reference
- operator[](size_type index) const
- {
- return at(index);
- }
-
- // get the value at a Tkey index - can throw std::out_of_range
- reference
- at(const Tkey &index)
- {
- iterator it = find(index);
- if (it != end()) {
- return *it;
- }
- throw std::out_of_range("key not found: " + to_string(index));
- }
- reference
- operator[](const Tkey &index)
- {
- return at(index);
- }
- const_reference
- at(const Tkey &index) const
- {
- KeyedVector *p = const_cast<KeyedVector *>(this);
- return p->at(index);
- }
- const_reference
- operator[](const Tkey &index) const
- {
- return at(index);
- }
-
- // get the const key at an int index - can throw std::out_of_range
- const Tkey &
- key_at(size_type index) const
- {
- bounds_check(index);
- return m_keyptrs[index]->first;
- }
-
- // check for the existence of a key - does not throw
- bool
- has_key(const Tkey &key) const
- {
- return (find(key) != end());
- }
-
- // get the value for a key, or end() if not found - does not throw
- iterator
- find(size_type index)
- {
- if (index >= size()) {
- return end();
- }
- return iter_at(index);
- }
- const_iterator
- find(size_type index) const
- {
- KeyedVector *p = const_cast<KeyedVector *>(this);
- return p->find(index);
- }
- iterator
- find(const Tkey &key)
- {
- Tkey_iter found = m_keys.find(key);
- if (found == m_keys.end()) {
- return end();
- }
- return iter_at(found->second);
- }
- const_iterator
- find(const Tkey &key) const
- {
- KeyedVector *p = const_cast<KeyedVector *>(this);
- return p->find(key);
- }
-
- // add a unique key-value pair, or overwrite the value if the key
- // already exists - same as insert()
- void
- push_back(const Tkey &key, const Tval &value)
- {
- insert(key, value);
- }
-
- // remove the first or last item - can throw std::out_of_range
- void
- pop_front()
- {
- bounds_check(0);
- erase(m_keyptrs.front()->first);
- }
- void
- pop_back()
- {
- bounds_check(0);
- erase(m_keyptrs.back()->first);
- }
-
- // add a unique key-value pair, or overwrite the value if the key
- // already exists
- iterator
- insert(const Tkey &key, const Tval &value)
- {
- std::pair<Tkey_iter, bool> ret;
- typename Tkey_map::value_type key_val(key, -1);
-
- // try to insert the key
- ret = m_keys.insert(key_val);
- if (ret.second) {
- // the key was inserted into the map: add the
- // value to the vectors, and to point to the key
- m_values.push_back(value);
- m_keyptrs.push_back(ret.first);
- ret.first->second = size()-1;
- return (m_values.end() - 1);
- } else {
- // the key already existed in the map: update the
- // value, but leave the vectors alone
- m_values[ret.first->second] = value;
- return (iter_at(ret.first->second));
- }
- }
-
- // remove a value by key
- iterator
- erase(const Tkey &key)
- {
- iterator found = find(key);
- if (found != end()) {
- return erase(found);
- }
- return end();
- }
- // remove value(s) by iterator(s)
- iterator
- erase(iterator pos)
- {
- size_type index = pos - begin();
- iterator ret = pos+1;
-
- m_keys.erase(m_keyptrs[index]);
- m_keyptrs.erase(keyptr_iter_at(index));
- m_values.erase(val_iter_at(index));
-
- // shuffle the indices which are stored in the map
- for (size_type i = index; i < size(); i++) {
- m_keyptrs[i]->second--;
- }
-
- return ret;
- }
- iterator
- erase(iterator first, iterator last)
- {
- while (first < last) {
- erase(first);
- first++;
- }
- return first;
- }
-
- private:
- // check index bounds
- void
- bounds_check(size_type index) const
- {
- if (index >= size()) {
- throw std::out_of_range("index out of range: "
- + to_string(index));
- }
- }
-
- // get various iterators by index - these are internal, so no
- // bounds checking is needed
- iterator
- iter_at(size_type index)
- {
- return val_iter_at(index);
- }
- Tkeyptr_iter
- keyptr_iter_at(size_type index)
- {
- return (m_keyptrs.begin() + index);
- }
- Tval_iter
- val_iter_at(size_type index)
- {
- return (m_values.begin() + index);
- }
-};
-
-} // namespace util
-#endif // KEYED_VECTOR_HPP__
=======================================
--- /trunk/pp_alias.h Thu Oct 9 22:20:22 2008
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Copyright (c) Tim Hockin, 2008 */
-#ifndef PP_PP_ALIAS_H__
-#define PP_PP_ALIAS_H__
-
-#include "pp.h"
-#include "pp_dirent.h"
-#include "pp_path.h"
-
-/*
- * pp_alias - a symbolic link to a dirent.
- */
-class pp_alias: public pp_dirent
-{
- public:
- explicit pp_alias(const pp_path &path)
- : pp_dirent(PP_DIRENT_ALIAS), m_path(path)
- {
- }
-
- const pp_path &
- link_path() const
- {
- return m_path;
- }
-
- private:
- pp_path m_path;
-};
-typedef boost::shared_ptr<pp_alias> pp_alias_ptr;
-typedef boost::shared_ptr<const pp_alias> pp_alias_const_ptr;
-
-#define new_pp_alias(...) pp_alias_ptr(new pp_alias(__VA_ARGS__))
-
-// const
-inline pp_alias_const_ptr
-pp_alias_from_dirent(const pp_dirent_const_ptr &dirent)
-{
- if (dirent->is_alias()) {
- return static_pointer_cast<const pp_alias>(dirent);
- }
- throw pp_dirent::conversion_error("non-alias dirent used as alias");
-}
-// non-const
-inline pp_alias_ptr
-pp_alias_from_dirent(const pp_dirent_ptr &dirent)
-{
- const pp_dirent_const_ptr &const_dirent = dirent;
- return const_pointer_cast<pp_alias>(
- pp_alias_from_dirent(const_dirent));
-}
-
-#endif // PP_PP_ALIAS_H__
=======================================
--- /trunk/pp_array.h Thu Oct 16 15:26:38 2008
+++ /dev/null
@@ -1,100 +0,0 @@
-/* Copyright (c) Tim Hockin, 2007 */
-#ifndef PP_PP_ARRAY_H__
-#define PP_PP_ARRAY_H__
-
-#include "pp.h"
-#include "printfxx.h"
-#include <vector>
-#include <stdexcept>
-#include "pp_path.h"
-#include "pp_dirent.h"
-
-//
-// pp_array - an array of dirents.
-//
-class pp_array: public pp_dirent
-{
- public:
- explicit pp_array(pp_dirent_type type)
- : pp_dirent(PP_DIRENT_ARRAY), m_type(type)
- {
- }
- virtual ~pp_array()
- {
- }
-
- //
- // Add a dirent to this array.
- // Throws:
- // pp_dirent::conversion_error - wrong pp_dirent_type
- //
- void
- append(const pp_dirent_ptr &dirent)
- {
- if (dirent->dirent_type() != m_type) {
- throw pp_dirent::conversion_error(
- sprintfxx("can't append %s to %s[]",
- dirent->dirent_type(), m_type));
- }
- m_vector.push_back(dirent);
- }
-
- //
- // Return the number of dirents in this array.
- //
- size_t
- size() const
- {
- return m_vector.size();
- }
-
- //
- // Provide access to the dirent at an index.
- // Throws:
- // std::out_of_range - index is out of range
- //
- const pp_dirent_const_ptr &
- at(size_t index) const
- {
- return m_vector.at(index);
- }
- const pp_dirent_const_ptr &
- operator[](size_t index) const
- {
- return at(index);
- }
-
- pp_dirent_type
- array_type() const
- {
- return m_type;
- }
-
- private:
- std::vector<pp_dirent_const_ptr> m_vector;
- pp_dirent_type m_type;
-};
-typedef boost::shared_ptr<pp_array> pp_array_ptr;
-typedef boost::shared_ptr<const pp_array> pp_array_const_ptr;
-
-#define new_pp_array(...) pp_array_ptr(new pp_array(__VA_ARGS__))
-
-// const
-inline pp_array_const_ptr
-pp_array_from_dirent(const pp_dirent_const_ptr &dirent)
-{
- if (dirent->is_array()) {
- return static_pointer_cast<const pp_array>(dirent);
- }
- throw pp_dirent::conversion_error("non-array dirent used as array");
-}
-// non-const
-inline pp_array_ptr
-pp_array_from_dirent(const pp_dirent_ptr &dirent)
-{
- const pp_dirent_const_ptr &const_dirent = dirent;
- return const_pointer_cast<pp_array>(
- pp_array_from_dirent(const_dirent));
-}
-
-#endif // PP_PP_ARRAY_H__
=======================================
--- /trunk/pp_binding.h Tue Jul 29 23:41:26 2008
+++ /dev/null
@@ -1,97 +0,0 @@
-/* Copyright (c) Tim Hockin, 2007 */
-#ifndef PP_PP_BINDING_H__
-#define PP_PP_BINDING_H__
-
-#include "pp.h"
-#include <stdexcept>
-#include <ostream>
-
-/*
- * pp_binding - abstract base class for bound register spaces.
- */
-class pp_binding
-{
- public:
- pp_binding() {}
- virtual ~pp_binding() {}
-
- /*
- * pp_binding::read(address, width)
- *
- * Read from a register in this binding.
- *
- * Throws: pp_driver::io_error
- */
- virtual pp_value
- read(const pp_value &address, const pp_bitwidth width) const = 0;
-
- /*
- * pp_binding::write(address, width, value)
- *
- * Write to a register in this binding.
- *
- * Throws: pp_driver::io_error
- */
- virtual void
- write(const pp_value &address, const pp_bitwidth width,
- const pp_value &value) const = 0;
-
- /*
- * pp_binding::to_string()
- *
- * Return a string representing this binding.
- */
- virtual string
- to_string() const = 0;
-};
-typedef boost::shared_ptr<pp_binding> pp_binding_ptr;
-typedef boost::shared_ptr<const pp_binding> pp_binding_const_ptr;
-
-inline std::ostream &
-operator<<(std::ostream& o, const pp_binding &binding)
-{
- return o << binding.to_string();
-}
-
-/*
- * simple_binding - a simple binding
- */
-template<class Tio, class Taddress>
-class simple_binding: public pp_binding
-{
- public:
- explicit simple_binding(Taddress address)
- : m_io(address)
- {
- }
-
- virtual pp_value
- read(const pp_value &address, const pp_bitwidth width) const
- {
- return m_io.read(address, width);
- }
-
- virtual void
- write(const pp_value &address, const pp_bitwidth width,
- const pp_value &value) const
- {
- return m_io.write(address, width, value);
- }
-
- virtual string
- to_string() const
- {
- return ::to_string(m_io.address());
- }
-
- const Taddress &
- address()
- {
- return m_io.address();
- }
-
- private:
- Tio m_io;
-};
-
-#endif // PP_PP_BINDING_H__
=======================================
--- /trunk/pp_context.h Sat Oct 25 01:32:07 2008
+++ /dev/null
@@ -1,125 +0,0 @@
-/* Copyright (c) Tim Hockin, 2008 */
-#ifndef PP_PP_CONTEXT_H__
-#define PP_PP_CONTEXT_H__
-
-#include "pp.h"
-#include "pp_scope.h"
-#include "pp_path.h"
-#include "pp_dirent.h"
-#include "pp_register.h"
-#include "pp_field.h"
-#include "pp_datatype.h"
-
-/*
- * This encapsulates a context of the PP tree.
- */
-class pp_context;
-typedef boost::shared_ptr<pp_context> pp_context_ptr;
-#define new_pp_context(...) pp_context_ptr(new pp_context(__VA_ARGS__))
-
-class pp_context
-{
- private:
- pp_path::element m_name;
- boost::weak_ptr<pp_scope> m_scope;
- bool m_readonly;
-
- public:
- pp_context(const string &n, const pp_scope_ptr &s)
- : m_name(n), m_scope(s), m_readonly(false)
- {
- }
- pp_context(const pp_path::element &n, const pp_scope_ptr &s)
- : m_name(n), m_scope(s), m_readonly(false)
- {
- }
-
- /*
- * Take a read-only snapshot of the current context
- */
- pp_context_ptr
- snapshot() const
- {
- pp_context_ptr new_ctxt = new_pp_context(*this);
- new_ctxt->m_readonly = true;
- return new_ctxt;
- }
-
- /*
- * getter methods
- */
- string
- name() const
- {
- return m_name.to_string();
- }
- pp_scope_ptr
- scope() const
- {
- return m_scope.lock();
- }
- bool
- is_readonly() const
- {
- return m_readonly;
- }
-
- /*
- * Encapsulate for ease of use.
- */
-
- const pp_binding_const_ptr &
- binding() const
- {
- return scope()->binding();
- }
-
- void
- add_datatype(const string &name, const pp_datatype_ptr &datatype)
- {
- scope()->add_datatype(name, datatype);
- }
-
- pp_datatype_const_ptr
- resolve_datatype(const string &name) const
- {
- return scope()->resolve_datatype(name);
- }
-
- pp_path
- resolve_path(const string &path_str) const
- {
- return scope()->resolve_path(path_str);
- }
-
- void
- add_dirent(const string &name, const pp_dirent_ptr &dirent)
- {
- scope()->add_dirent(name, dirent);
- }
- void
- add_dirent(const pp_path::element &name, const pp_dirent_ptr &dirent)
- {
- scope()->add_dirent(name, dirent);
- }
-
- bool
- dirent_defined(const pp_path &path) const
- {
- return scope()->dirent_defined(path);
- }
-
- pp_dirent_const_ptr
- lookup_dirent(pp_path path, unsigned flags=0) const
- {
- return scope()->lookup_dirent(path, flags);
- }
-
- void
- add_bookmark(const string &name)
- {
- return scope()->add_bookmark(name);
- }
-};
-
-#endif // PP_PP_CONTEXT_H__
=======================================
--- /trunk/pp_datatype.h Tue Jul 29 23:41:26 2008
+++ /dev/null
@@ -1,100 +0,0 @@
-/* Copyright (c) Tim Hockin, 2007 */
-#ifndef PP_PP_DATATYPE_H__
-#define PP_PP_DATATYPE_H__
-
-#include "pp.h"
-#include <stdexcept>
-
-/*
- * pp_datatype - abstract base class for all datatypes.
- */
-class pp_datatype
-{
- public:
- // an invalid value error
- struct invalid_error: public std::runtime_error
- {
- explicit invalid_error(const string &str)
- : runtime_error(str)
- {
- }
- };
-
- virtual ~pp_datatype() {}
-
- /*
- * pp_datatype::evaluate(value)
- *
- * Evaluate a value against this datatype. This method returns a
- * string containing the evaluated representation of the 'value'
- * argument.
- */
- virtual string
- evaluate(const pp_value &value) const = 0;
-
- /*
- * pp_datatype::lookup(str)
- * pp_datatype::lookup(value)
- *
- * Lookup the value of a (potentially valid) evaluation for this
- * datatype. Each specific subclass will override these.
- *
- * This can throw pp_datatype::invalid_error.
- */
- virtual pp_value
- lookup(const string &str) const = 0;
- virtual pp_value
- lookup(const pp_value &value) const = 0;
-
- /*
- * pp_datatype::compare(value, str)
- * pp_datatype::compare(value, value)
- * pp_datatype::test(value, str)
- * pp_datatype::test(value, value)
- *
- * Compare a pp_value against another argument, according to the
- * rules of this datatype. Return an integer which indicates
- * whether the lhs is less than, equal to, or greater than the rhs
- * (negative, zero, positive return code, respectively).
- *
- * This can throw pp_datatype::invalid_error.
- */
- virtual int
- compare(const pp_value &lhs, const string &rhs) const
- {
- /* default implementation */
- pp_value result = lhs - lookup(rhs);
- if (result < 0)
- return -1;
- if (result > 0)
- return 1;
- return 0;
- }
- virtual int
- compare(const pp_value &lhs, const pp_value &rhs) const
- {
- /* default implementation */
- pp_value result = lhs - lookup(rhs);
- if (result < 0)
- return -1;
- if (result > 0)
- return 1;
- return 0;
- }
- virtual pp_value
- test(const pp_value &lhs, const string &rhs) const
- {
- /* default implementation */
- return (lhs & lookup(rhs));
- }
- virtual pp_value
- test(const pp_value &lhs, const pp_value &rhs) const
- {
- /* default implementation */
- return (lhs & lookup(rhs));
- }
-};
-typedef boost::shared_ptr<pp_datatype> pp_datatype_ptr;
-typedef boost::shared_ptr<const pp_datatype> pp_datatype_const_ptr;
-
-#endif // PP_PP_DATATYPE_H__
=======================================
--- /trunk/pp_datatypes.h Wed May 6 16:38:28 2009
+++ /dev/null
@@ -1,739 +0,0 @@
-/* Copyright (c) Tim Hockin, 2007 */
-#ifndef PP_PP_DATATYPES_H__
-#define PP_PP_DATATYPES_H__
-
-#include "pp.h"
-#include "printfxx.h"
-#include "pp_datatype.h"
-#include "keyed_vector.h"
-#include "language.h"
-#include "bit_buffer.h"
-
-/*
- * pp_multi_datatype - datatype for combining multiple subtypes based
- * on the value being read
- *
- * NOTE: range is between min and max inclusive
- */
-class pp_multi_datatype: public pp_datatype
-{
- private:
- struct pp_multi_range
- {
- pp_multi_range(const pp_datatype_const_ptr &datatype,
- pp_value min, pp_value max)
- : type(datatype), low(min), high(max) {}
- pp_datatype_const_ptr type;
- pp_value low;
- pp_value high;
- };
- std::vector<pp_multi_range> m_parts;
-
- static bool
- val_in_range(const pp_value &value, const pp_multi_range &range) {
- return ((value >= range.low) && (value <= range.high));
- }
-
- public:
- pp_multi_datatype()
- {
- }
-
- virtual ~pp_multi_datatype()
- {
- }
-
- /*
- * pp_multi_datatype::evaluate(value)
- *
- * Evaluate a value against this datatype. This method returns a
- * string containing the representation of the 'value' argument,
- * evaluated with the datatype (if any) assigned to the given value's
- * range.
- *
- */
- virtual string
- evaluate(const pp_value &value) const
- {
- for (size_t i = 0; i < m_parts.size(); i++) {
- if (val_in_range(value, m_parts[i])) {
- return m_parts[i].type->evaluate(value);
- }
- }
-
- // Value out of range; return unknown indicator
- return sprintfxx("<!%d!>", value);
- }
-
- /*
- * pp_multi_datatype::lookup(str)
- * pp_multi_datatype::lookup(value) -- simply validate it's in range
- *
- * Lookup the value of a (potentially valid) evaluation for this
- * datatype. For an multi type, that means converting a string to
- * it's corresponding value, or validating that a numeric value is
- * a valid option.
- *
- * WARNING: in case the given string could map to multiple different
- * pp_values, the lowest possible value is returned
- *
- * This will throw pp_datatype::invalid_error if the lookup is not a
- * valid value for this enum.
- *
- */
- virtual pp_value
- lookup(const string &str) const
- {
- for (size_t i = 0; i < m_parts.size(); i++) {
- try {
- pp_value val = m_parts[i].type->lookup(str);
- if (val_in_range(val, m_parts[i])) {
- return val;
- }
- } catch (pp_datatype::invalid_error &e) {
- // Keep trying on exception
- }
- }
- throw pp_datatype::invalid_error("Could not match given string "
- "to a range: " + str);
- }
-
- virtual pp_value
- lookup(const pp_value &value) const
- {
- for (size_t i = 0; i < m_parts.size(); i++) {
- if (val_in_range(value, m_parts[i])) {
- return value;
- }
- }
- throw pp_datatype::invalid_error("Given value out of range: " +
- to_string(value));
- }
-
- /*
- * pp_multi_datatype::add_range(pp_multi_range)
- * pp_multi_datatype::add_range(datatype, min, max)
- *
- * Add a new range->datatype mapping to this multi datatype.
- *
- */
- void
- add_range(const pp_multi_range &range)
- {
- add_range(range.type, range.low, range.high);
- }
- void
- add_range(const pp_datatype_const_ptr &datatype,
- pp_value min, pp_value max)
- {
- if (min > max) {
- throw pp_datatype::invalid_error(sprintfxx(
- "Minimum value %d cannot be higher "
- "than maximum value %d", min, max));
- }
-
- // If the vector is empty, we can add it right away
- if (m_parts.size() == 0) {
- m_parts.push_back(
- pp_multi_range(datatype, min, max));
- return;
- }
-
- // See if we can add it in the middle
- std::vector<pp_multi_range>::iterator iter;
- for (iter = m_parts.begin(); iter != m_parts.end(); iter++) {
- if (min > iter->high) {
- continue;
- } else if (max < iter->low) {
- m_parts.insert(iter, pp_multi_range(
- datatype, min, max));
- return;
- } else {
- throw pp_datatype::invalid_error(
- "Cannot have overlapping ranges");
- }
- }
-
- // Range is higher than everything else; add to end
- m_parts.push_back(
- pp_multi_range(datatype, min, max));
- }
-};
-typedef boost::shared_ptr<pp_multi_datatype> pp_multi_datatype_ptr;
-
-#define new_pp_multi_datatype(...) \
- pp_multi_datatype_ptr( \
- new pp_multi_datatype(__VA_ARGS__))
-
-/*
- * pp_enum_datatype - datatype for enumerated values.
- *
- * NOTE: Enum keys must be unique.
- */
-class pp_enum_datatype: public pp_datatype
-{
- private:
- util::KeyedVector<string, pp_value> m_values;
- string m_unknown;
- bool m_custom_unknown;
-
- public:
- pp_enum_datatype()
- : m_custom_unknown(false)
- {
- }
- explicit pp_enum_datatype(const util::KeyedVector<string,
- pp_value> &values)
- : m_values(values), m_custom_unknown(false)
- {
- }
- virtual ~pp_enum_datatype()
- {
- }
-
- /*
- * pp_enum_datatype::evaluate(value)
- *
- * Evaluate a value against this datatype. This method returns a
- * string containing the evaluated representation of the 'value'
- * argument.
- */
- virtual string
- evaluate(const pp_value &value) const
- {
- for (size_t i=0; i < m_values.size(); i++) {
- if (m_values[i] == value) {
- return m_values.key_at(i);
- }
- }
- if (m_custom_unknown) {
- return m_unknown;
- }
- return sprintfxx("<!%d!>", value);
- }
-
- /*
- * pp_enum_datatype::lookup(str)
- * pp_enum_datatype::lookup(value)
- *
- * Lookup the value of a (potentially valid) evaluation for this
- * datatype. For an enum type, that means converting a string to
- * it's corresponding value, or validating that a numeric value is
- * a valid option.
- *
- * This will throw pp_datatype::invalid_error if the lookup is not a
- * valid value for this enum.
- */
- virtual pp_value
- lookup(const string &str) const
- {
- for (size_t i=0; i < m_values.size(); i++) {
- if (m_values.key_at(i) == str) {
- return m_values[i];
- }
- }
- throw pp_datatype::invalid_error(str);
- }
- virtual pp_value
- lookup(const pp_value &value) const
- {
- for (size_t i=0; i < m_values.size(); i++) {
- if (m_values[i] == value) {
- return m_values[i];
- }
- }
- throw pp_datatype::invalid_error(to_string(value));
- }
-
- /*
- * pp_enum_datatype::add_value(name, value)
- *
- * Add a possible value to this enumeration.
- */
- void
- add_value(const string &name, const pp_value &value)
- {
- DASSERT_MSG(m_values.find(name) == m_values.end(),
- "adding duplicate enum key: "
- + name + " = " + to_string(value));
- m_values.insert(name, value);
- }
-
- /*
- * pp_enum_datatype::set_unknown(name)
- *
- * Use a string for unknown enumerated values.
- */
- void
- set_unknown(const string &name)
- {
- m_unknown = name;
- m_custom_unknown = true;
- }
-};
-typedef boost::shared_ptr<pp_enum_datatype> pp_enum_datatype_ptr;
-
-#define new_pp_enum_datatype(...) \
- pp_enum_datatype_ptr(new pp_enum_datatype(__VA_ARGS__))
-
-/*
- * pp_bool_datatype - datatype for boolean values.
- */
-class pp_bool_datatype: public pp_enum_datatype
-{
- public:
- pp_bool_datatype(const string &true_str, const string &false_str)
- {
- add_value(true_str, 1);
- add_value(false_str, 0);
- set_unknown(true_str);
- }
-
- virtual pp_value
- lookup(const pp_value &value) const
- {
- return value == 0 ? 0 : 1;
- }
- virtual pp_value
- lookup(const string &str) const
- {
- return pp_enum_datatype::lookup(str);
- }
-};
-typedef boost::shared_ptr<pp_bool_datatype> pp_bool_datatype_ptr;
-
-#define new_pp_bool_datatype(...) \
- pp_bool_datatype_ptr(new pp_bool_datatype(__VA_ARGS__))
-
-/*
- * pp_bitmask_datatype - datatype for bitmasks.
- *
- * NOTE: Bit names must be unique.
- * //FIXME: this is operating on bit numbers, not masks - should it?
- */
-class pp_bitmask_datatype: public pp_datatype
-{
- private:
- util::KeyedVector<string, pp_value> m_bits;
-
- public:
- pp_bitmask_datatype()
- {
- }
- explicit pp_bitmask_datatype(
- const util::KeyedVector<string, pp_value> &bits)
- : m_bits(bits)
- {
- }
- virtual ~pp_bitmask_datatype()
- {
- }
-
- /*
- * pp_bitmask_datatype::evaluate(value)
- *
- * Evaluate a value against this datatype. This method returns a
- * string containing the evaluated representation of the 'value'
- * argument.
- */
- virtual string
- evaluate(const pp_value &value) const
- {
- string ret("");
- pp_value myval = value;
-
- for (size_t i=0; myval != 0 && i < m_bits.size(); i++) {
- pp_value b = m_bits[i];
- pp_value mask = (1 << b);
- if ((myval & mask) != 0) {
- if (!ret.empty()) {
- ret += " ";
- }
- ret += m_bits.key_at(i);
- myval ^= (myval & mask);
- }
- }
-
- int unknown = 0;
- while (myval != 0) {
- if ((myval & pp_value(1)) == 1) {
- if (!ret.empty()) {
- ret += " ";
- }
- ret += sprintfxx("<!%d!>", unknown);
- }
- myval >>= 1;
- unknown++;
- }
-
- return ret;
- }
-
- /*
- * pp_bitmask_datatype::lookup(str)
- * pp_bitmask_datatype::lookup(value)
- *
- * Lookup the value of a (potentially valid) evaluation for this
- * datatype. For a bitmask type, this means converting a string to
- * it's corresponding bit value.
- *
- * This will throw pp_datatype::invalid_error if the string is not a
- * valid value in this bitmask.
- */
- virtual pp_value
- lookup(const string &str) const
- {
- //FIXME: need to support multiple bits
- for (size_t i=0; i < m_bits.size(); i++) {
- if (m_bits.key_at(i) == str) {
- return m_bits[i];
- }
- }
- throw pp_datatype::invalid_error(str);
- }
- virtual pp_value
- lookup(const pp_value &value) const
- {
- //FIXME: need to support multiple bits
- for (size_t i=0; i < m_bits.size(); i++) {
- if (m_bits.at(i) == value) {
- return m_bits[i];
- }
- }
- throw pp_datatype::invalid_error(to_string(value));
- }
-
- /*
- * pp_bitmask_datatype::add_bit(name, value)
- *
- * Add a named bit to this bitmask.
- */
- void
- add_bit(const string &name, const pp_value &value)
- {
- DASSERT_MSG(m_bits.find(name) == m_bits.end(),
- "adding duplicate bitmask key: "
- + name + " = " + to_string(value));
- m_bits.insert(name, value);
- }
-};
-typedef boost::shared_ptr<pp_bitmask_datatype> pp_bitmask_datatype_ptr;
-
-#define new_pp_bitmask_datatype(...) \
- pp_bitmask_datatype_ptr(new pp_bitmask_datatype(__VA_ARGS__))
-
-/*
- * pp_string_datatype - datatype for string values
- *
- * This datatype interprets a given value as a string in little-endian
order,
- * meaning that the least significant byte in the input is the first
- * character in the string. For example, 0x6f6c6c6548 produces "Hello"
(where
- * 0x48 == 'H').
- *
- * Internal non-printing characters are ignored for display purposes. For
- * example, 0x6f6c6c00006548 also produces "Hello". However, they are
- * preserved in the string so that the same pp_value is returned when
- * using lookup() as that which was given to evaluate() in order to produce
- * the string in the first place. That is, lookup() and evaluate() are
inverse.
- */
-class pp_string_datatype: public pp_datatype
-{
- public:
- pp_string_datatype()
- {
- }
- virtual ~pp_string_datatype()
- {
- }
-
- /*
- * pp_string_datatype::evaluate(value)
- *
- * Evaluate a value against this datatype. This method returns a
- * string containing the evaluated representation of the 'value'
- * argument.
- */
- virtual string
- evaluate(const pp_value &value) const
- {
- util::BitBuffer bits = value.get_bitbuffer();
- return string((char *)bits.get(), bits.size_bytes());
- }
-
- /*
- * pp_string_datatype::lookup(value)
- *
- * Lookup the value of a (potentially valid) evaluation for this
- * datatype. For a string type, this is a no-op.
- */
- virtual pp_value
- lookup(const pp_value &value) const
- {
- return value;
- }
- virtual pp_value
- lookup(const string &str) const
- {
- util::BitBuffer bits(BYTES_TO_BITS(str.size()),
- (uint8_t *)str.data());
- return pp_value(bits);
- }
-
-};
-typedef boost::shared_ptr<pp_string_datatype> pp_string_datatype_ptr;
-
-#define new_pp_string_datatype(...) \
- pp_string_datatype_ptr(new pp_string_datatype(__VA_ARGS__))
-
-/*
- * pp_int_datatype - datatype for signed integer values.
- *
- * Notes:
- * This class makes a private copy of the 'units' argument.
- */
-class pp_int_datatype: public pp_datatype
-{
- public:
- explicit pp_int_datatype(const string &units = "")
- : m_units(units)
- {
- }
- virtual ~pp_int_datatype()
- {
- }
-
- /*
- * pp_int_datatype::evaluate(value)
- *
- * Evaluate a value against this datatype. This method returns a
- * string containing the evaluated representation of the 'value'
- * argument.
- */
- virtual string
- evaluate(const pp_value &value) const
- {
- string ret = to_string(value);
- if (!m_units.empty()) {
- ret += " ";
- ret += m_units;
- }
- return ret;
- }
-
- /*
- * pp_int_datatype::lookup(value)
- *
- * Lookup the value of a (potentially valid) evaluation for this
- * datatype. For an int type, this is a no-op.
- */
- virtual pp_value
- lookup(const pp_value &value) const
- {
- return value;
- }
- virtual pp_value
- lookup(const string &str) const
- {
- try {
- return pp_value(str);
- } catch (std::invalid_argument &e) {
- throw pp_datatype::invalid_error(str);
- }
- }
-
- protected:
- string m_units;
-};
-typedef boost::shared_ptr<pp_int_datatype> pp_int_datatype_ptr;
-
-#define new_pp_int_datatype(...) \
- pp_int_datatype_ptr(new pp_int_datatype(__VA_ARGS__))
-
-/*
- * pp_hex_datatype - datatype for hexadecimal values.
- */
-class pp_hex_datatype: public pp_int_datatype
-{
- public:
- explicit pp_hex_datatype(const pp_bitwidth width = BITS0,
- const string &units = "")
- : pp_int_datatype(units), m_width(width)
- {
- }
- virtual ~pp_hex_datatype()
- {
- }
-
- /*
- * pp_hex_datatype::evaluate(value)
- *
- * Evaluate a value against this datatype. This method returns a
- * string containing the evaluated representation of the 'value'
- * argument.
- */
- virtual string
- evaluate(const pp_value &value) const
- {
- string fmt = "0x%0" + to_string(m_width/4) + "x";
- string ret = sprintfxx(fmt, value);
- if (!m_units.empty()) {
- ret += " ";
- ret += m_units;
- }
- return ret;
- }
-
- private:
- pp_bitwidth m_width;
-};
-typedef boost::shared_ptr<pp_hex_datatype> pp_hex_datatype_ptr;
-
-#define new_pp_hex_datatype(...) \
- pp_hex_datatype_ptr(new pp_hex_datatype(__VA_ARGS__))
-
-/*
- * pp_transform_datatype - datatype wrapper to perform a transform on data
- * before passing off to another datatype.
- */
-template<typename Tdefunc, typename Tenfunc>
-class pp_transform_datatype: public pp_datatype
-{
- private:
- pp_datatype_const_ptr m_real_type;
- Tdefunc m_decode_func;
- Tenfunc m_encode_func;
-
- public:
- pp_transform_datatype(const pp_datatype_const_ptr &real_type,
- const Tdefunc &decode_func, const Tenfunc &encode_func)
- : m_real_type(real_type), m_decode_func(decode_func),
- m_encode_func(encode_func)
- {
- }
- virtual ~pp_transform_datatype()
- {
- }
-
- /*
- * pp_transform_datatype::evaluate(value)
- *
- * Evaluate a value against this datatype. This method returns a
- * string containing the evaluated representation of the 'value'
- * argument.
- */
- virtual string
- evaluate(const pp_value &raw) const
- {
- pp_value cooked = m_decode_func(raw);
- return m_real_type->evaluate(cooked);
- }
-
- /*
- * pp_transform_datatype::lookup(value)
- *
- * Lookup the value of a (potentially valid) evaluation for this
- * datatype.
- */
- virtual pp_value
- lookup(const pp_value &cooked) const
- {
- pp_value tmp = m_real_type->lookup(cooked);
- return m_encode_func(tmp);
- }
- virtual pp_value
- lookup(const string &cooked) const
- {
- pp_value tmp = m_real_type->lookup(cooked);
- return m_encode_func(tmp);
- }
-};
-typedef boost::shared_ptr<pp_datatype> pp_transform_datatype_ptr;
-
-// This has to be a template, because template argument inference only
-// happens on functions, not classes.
-template<typename Tdefunc, typename Tenfunc>
-pp_transform_datatype_ptr
-new_pp_transform_datatype(const pp_datatype_const_ptr &real_type,
- const Tdefunc &decode_func, const Tenfunc &encode_func)
-{
- return pp_transform_datatype_ptr(
- new pp_transform_datatype<Tdefunc, Tenfunc>(real_type,
- decode_func, encode_func));
-}
-
-/*
- * pp_fixed_datatype - datatype for fixed-point values.
- *
- * Notes:
- * This class makes a private copy of the 'units' argument.
- */
-class pp_fixed_datatype: public pp_datatype
-{
- public:
- explicit pp_fixed_datatype(int nbits, const string &units = "")
- : m_nbits(nbits), m_units(units)
- {}
- virtual ~pp_fixed_datatype()
- {}
-
- /*
- * pp_fixed_datatype::evaluate(value)
- *
- * Evaluate a value against this datatype. This method returns a
- * string containing the evaluated representation of the 'value'
- * argument.
- */
- virtual string
- evaluate(const pp_value &value) const
- {
- pp_value numer = value & ((pp_value(1) << m_nbits) - 1);
- pp_value denom = pp_value::pow(2, m_nbits);
- pp_value scalar = pp_value::pow(10, m_nbits);
-
- pp_value decimal = (numer * scalar) / denom;
- pp_value integral = value >> m_nbits;
-
- // right align things like 0.500 to 0.5
- while (decimal > 0 && (decimal % 10) == 0) {
- decimal /= 10;
- }
- string ret = sprintfxx("%d.%d", integral, decimal);
- if (!m_units.empty()) {
- ret += " ";
- ret += m_units;
- }
- return ret;
- }
-
- /*
- * pp_fixed_datatype::lookup(value)
- *
- * Lookup the value of a (potentially valid) evaluation for this
- * datatype. For an int type, this is a no-op.
- */
- virtual pp_value
- lookup(const pp_value &value) const
- {
- return value;
- }
- virtual pp_value
- lookup(const string &str) const
- {
- try {
- //FIXME:
- return pp_value(str);
- } catch (std::invalid_argument &e) {
- throw pp_datatype::invalid_error(str);
- }
- }
-
- protected:
- int m_nbits;
- string m_units;
-};
-typedef boost::shared_ptr<pp_fixed_datatype> pp_fixed_datatype_ptr;
-
-#define new_pp_fixed_datatype(...) \
- pp_fixed_datatype_ptr(new pp_fixed_datatype(__VA_ARGS__))
-
-#endif // PP_PP_DATATYPES_H__
=======================================
***Additional files exist in this changeset.***