You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by am...@apache.org on 2010/11/20 16:17:56 UTC
svn commit: r1037235 [2/2] -
/trafficserver/traffic/branches/wccp/proxy/tsconfig/
Added: trafficserver/traffic/branches/wccp/proxy/tsconfig/TsConfigLexer.h
URL: http://svn.apache.org/viewvc/trafficserver/traffic/branches/wccp/proxy/tsconfig/TsConfigLexer.h?rev=1037235&view=auto
==============================================================================
--- trafficserver/traffic/branches/wccp/proxy/tsconfig/TsConfigLexer.h (added)
+++ trafficserver/traffic/branches/wccp/proxy/tsconfig/TsConfigLexer.h Sat Nov 20 15:17:55 2010
@@ -0,0 +1,16 @@
+# if ! defined(TS_CONFIG_LEXER_HEADER)
+# define TS_CONFIG_LEXER_HEADER
+
+// Need this for TsConfigHandlers
+# include "TsConfigParseEvents.h"
+
+# if defined(__cplusplus)
+ extern "C" {
+# endif
+# include "TsConfig.lex.h"
+ extern int tsconfigparse(yyscan_t lexer, struct TsConfigHandlers* handlers);
+# if defined(__cplusplus)
+}
+# endif
+
+# endif // TS_CONFIG_LEXER_HEADER
Added: trafficserver/traffic/branches/wccp/proxy/tsconfig/TsConfigParseEvents.h
URL: http://svn.apache.org/viewvc/trafficserver/traffic/branches/wccp/proxy/tsconfig/TsConfigParseEvents.h?rev=1037235&view=auto
==============================================================================
--- trafficserver/traffic/branches/wccp/proxy/tsconfig/TsConfigParseEvents.h (added)
+++ trafficserver/traffic/branches/wccp/proxy/tsconfig/TsConfigParseEvents.h Sat Nov 20 15:17:55 2010
@@ -0,0 +1,46 @@
+# if ! defined(TS_CONFIG_PARSE_EVENTS_HEADER)
+# define TS_CONFIG_PARSE_EVENTS_HEADER
+
+// Parsing events.
+
+# include "TsConfigTypes.h"
+
+typedef void (*TsConfigEventFunction)(void* data, YYSTYPE* token);
+struct TsConfigEventHandler {
+ TsConfigEventFunction _f; ///< Callback function.
+ void* _data; ///< Callback context data.
+};
+typedef int (*TsConfigErrorFunction)(void* data, char const* text);
+struct TsConfigErrorHandler {
+ TsConfigErrorFunction _f; ///< Callback function.
+ void* _data; ///< Callback context data.
+};
+
+enum TsConfigEventType {
+ TsConfigEventGroupOpen,
+ TsConfigEventGroupName,
+ TsConfigEventGroupClose,
+ TsConfigEventListOpen,
+ TsConfigEventListClose,
+ TsConfigEventPathOpen,
+ TsConfigEventPathTag,
+ TsConfigEventPathIndex,
+ TsConfigEventPathClose,
+ TsConfigEventLiteralValue,
+ TsConfigEventInvalidToken
+};
+
+# if defined(__cplusplus)
+static const size_t TS_CONFIG_N_EVENT_TYPES = TsConfigEventInvalidToken + 1;
+# else
+# define TS_CONFIG_N_EVENT_TYPES (TsConfigEventInvalidToken + 1)
+# endif
+
+struct TsConfigHandlers {
+ struct TsConfigErrorHandler error; ///< Syntax error.
+ /// Parsing event handlers.
+ /// Indexed by @c TsConfigEventType.
+ struct TsConfigEventHandler handler[TS_CONFIG_N_EVENT_TYPES];
+};
+
+# endif // TS_CONFIG_PARSE_EVENTS_HEADER
Added: trafficserver/traffic/branches/wccp/proxy/tsconfig/TsConfigTypes.h
URL: http://svn.apache.org/viewvc/trafficserver/traffic/branches/wccp/proxy/tsconfig/TsConfigTypes.h?rev=1037235&view=auto
==============================================================================
--- trafficserver/traffic/branches/wccp/proxy/tsconfig/TsConfigTypes.h (added)
+++ trafficserver/traffic/branches/wccp/proxy/tsconfig/TsConfigTypes.h Sat Nov 20 15:17:55 2010
@@ -0,0 +1,33 @@
+# if ! defined(TS_CONFIG_TYPES_HEADER)
+# define TS_CONFIG_TYPES_HEADER
+
+# if defined(_MSC_VER)
+# include <stddef.h>
+# else
+# include <unistd.h>
+# endif
+
+# if defined(__cplusplus)
+namespace ts { namespace config {
+# endif
+struct Location {
+ int _col; ///< Column.
+ int _line; ///< Line.
+};
+
+struct Token {
+ char* _s; ///< Text of token.
+ size_t _n; ///< Text length.
+ int _type; ///< Type of token.
+ struct Location _loc; ///< Location of token.
+};
+
+# if defined(__cplusplus)
+}} // namespace ts::config
+# define YYSTYPE ts::config::Token
+# else
+# define YYSTYPE struct Token
+#endif
+
+
+# endif // TS_CONFIG_TYPES_HEADER
\ No newline at end of file
Added: trafficserver/traffic/branches/wccp/proxy/tsconfig/TsErrataUtil.cc
URL: http://svn.apache.org/viewvc/trafficserver/traffic/branches/wccp/proxy/tsconfig/TsErrataUtil.cc?rev=1037235&view=auto
==============================================================================
--- trafficserver/traffic/branches/wccp/proxy/tsconfig/TsErrataUtil.cc (added)
+++ trafficserver/traffic/branches/wccp/proxy/tsconfig/TsErrataUtil.cc Sat Nov 20 15:17:55 2010
@@ -0,0 +1,151 @@
+/// @file
+/// Statics and utilities.
+
+# if !defined(_MSC_VER)
+# include <stdio.h>
+# include <string.h>
+# endif
+# include <stdarg.h>
+# include <errno.h>
+# include <TsErrataUtil.h>
+
+namespace ts { namespace msg {
+
+Errata::Code FATAL = 3; ///< Fatal, cannot continue.
+Errata::Code WARN = 2; ///< Significant, should be fixed.
+Errata::Code INFO = 1; /// Interesting, not necessarily a problem.
+Errata::Code DEBUG = 0; /// Debugging information.
+
+# if defined(_MSC_VER)
+char* strerror_r(int err, char* s, size_t n) {
+ strncpy(s, strerror(err), n-1);
+ s[n-1] = 0; // guarantee null termination.
+ return s;
+}
+
+# define snprintf _snprintf
+# endif
+
+Errata&
+log(Errata& err, Errata::Id id, Errata::Code code, char const* text) {
+ err.push(id, code, text);
+ return err;
+}
+
+Errata&
+log(Errata& err, Errata::Code code, char const* text) {
+ err.push(0, code, text);
+ return err;
+}
+
+Errata&
+log(RvBase& rv, Errata::Code code, char const* text) {
+ rv._errata.push(0, code, text);
+ return rv._errata;
+}
+
+Errata
+log(Errata::Code code, char const* text) {
+ Errata err;
+ err.push(0, code, text);
+ return err;
+}
+
+Errata&
+vlogf(
+ Errata& err,
+ Errata::Id id,
+ Errata::Code code,
+ char const* format,
+ va_list& rest
+) {
+ int n;
+ static size_t const SIZE = 8192;
+ char buffer[SIZE];
+
+ n = vsnprintf(buffer, SIZE, format, rest);
+ err.push(id, code, buffer);
+ return err;
+}
+
+Errata&
+logf(
+ Errata& err,
+ Errata::Id id,
+ Errata::Code code,
+ char const* format,
+ ...
+) {
+ va_list rest;
+ va_start(rest, format);
+ return vlogf(err, id, code, format, rest);
+}
+
+Errata
+logf(Errata::Code code, char const* format, ...) {
+ Errata err;
+ va_list rest;
+ va_start(rest, format);
+ return vlogf(err, Errata::Id(0), code, format, rest);
+}
+
+Errata&
+logf(Errata& err, Errata::Code code, char const* format, ...) {
+ va_list rest;
+ va_start(rest, format);
+ return vlogf(err, Errata::Id(0), code, format, rest);
+}
+
+Errata&
+logf(RvBase& base, Errata::Code code, char const* format, ...) {
+ va_list rest;
+ va_start(rest, format);
+ return vlogf(base._errata, Errata::Id(0), code, format, rest);
+}
+
+Errata
+log_errno(Errata::Code code, char const* text) {
+ static size_t const SIZE = 1024;
+ char buffer[SIZE];
+ return logf(code, "%s [%d] %s", text, errno, strerror_r(errno, buffer, SIZE));
+}
+
+Errata
+vlogf_errno(Errata& errata, Errata::Id id, Errata::Code code, char const* format, va_list& rest) {
+ int e = errno; // Preserve value before making system calls.
+ int n;
+ static int const E_SIZE = 256;
+ char e_buffer[E_SIZE];
+ static int const T_SIZE = 8192;
+ char t_buffer[T_SIZE];
+
+ n = vsnprintf(t_buffer, T_SIZE, format, rest);
+ if (0 <= n && n < T_SIZE) // still have room.
+ n += snprintf(t_buffer + n, T_SIZE - n, "[%d] %s", e, strerror_r(e, e_buffer, E_SIZE));
+ errata.push(id, code, t_buffer);
+ return errata;
+}
+
+Errata
+logf_errno(Errata::Code code, char const* format, ...) {
+ Errata zret;
+ va_list rest;
+ va_start(rest, format);
+ return vlogf_errno(zret, Errata::Id(), code, format, rest);
+}
+
+Errata
+logf_errno(Errata& errata, Errata::Code code, char const* format, ...) {
+ va_list rest;
+ va_start(rest, format);
+ return vlogf_errno(errata, Errata::Id(), code, format, rest);
+}
+
+Errata
+logf_errno(RvBase& rv, Errata::Code code, char const* format, ...) {
+ va_list rest;
+ va_start(rest, format);
+ return vlogf_errno(rv._errata, Errata::Id(), code, format, rest);
+}
+// ------------------------------------------------------
+}} // namespace ts::msg
Added: trafficserver/traffic/branches/wccp/proxy/tsconfig/TsErrataUtil.h
URL: http://svn.apache.org/viewvc/trafficserver/traffic/branches/wccp/proxy/tsconfig/TsErrataUtil.h?rev=1037235&view=auto
==============================================================================
--- trafficserver/traffic/branches/wccp/proxy/tsconfig/TsErrataUtil.h (added)
+++ trafficserver/traffic/branches/wccp/proxy/tsconfig/TsErrataUtil.h Sat Nov 20 15:17:55 2010
@@ -0,0 +1,151 @@
+/** @file
+ Utilities and logging.
+ Original implementation: Alan M. Carroll
+ */
+
+# if !defined(TS_ERRATA_UTIL_HEADER)
+# define TS_ERRATA_UTIL_HEADER
+
+// TBD: Need to do something better than this to get around using
+// 'DEBUG' as the name of a constant.
+# if defined DEBUG
+# undef DEBUG
+# define DEBUG DEBUG
+# endif
+
+# include <Errata.h>
+
+namespace ts { namespace msg {
+
+/// @name Message severity levels.
+//@{
+extern Errata::Code FATAL; ///< Fatal, cannot continue.
+extern Errata::Code WARN; ///< Significant, function degraded.
+extern Errata::Code INFO; ///< Interesting, not necessarily a problem.
+extern Errata::Code DEBUG; ///< Debugging information.
+//@}
+
+/** Logging / reporting support.
+ We build on top of @c Errata but we want to be able to prevent
+ message generation / allocation for messages with severity levels
+ less than a run time controllable value.
+
+ @internal Far from complete but serving as a prototype / experiment
+ to learn what's actually useful.
+*/
+//@{
+/// Report literal string to an Errata.
+/// @return @a err.
+Errata& log(
+ Errata& err,///< Target errata.
+ Errata::Id id, ///< Message ID.
+ Errata::Code code, ///< Severity level.
+ char const* text ///< Message text.
+);
+/// Report literal string to an Errata.
+/// Use message ID 0.
+/// @return @a err.
+Errata& log(
+ Errata& err,///< Target errata.
+ Errata::Code code, ///< Severity level.
+ char const* text ///< Message text.
+);
+/// Report literal string to a return value.
+/// Use message ID 0.
+/// @return The @c Errata in @a rv.
+Errata& log(
+ RvBase& rv,///< Return value.
+ Errata::Code code, ///< Severity level.
+ char const* text ///< Message text.
+);
+/// printf style log to Errata.
+/// @return @a err.
+Errata& logf(
+ Errata& err,///< Target errata.
+ Errata::Id id, ///< Message ID.
+ Errata::Code code, ///< Severity level.
+ char const* format, ///< Format string.
+ ... ///< Format string parameters.
+);
+/// printf style log to Errata.
+/// The message id is set to zero.
+/// @return @a err.
+Errata& logf(
+ Errata& err,///< Target errata.
+ Errata::Code code, ///< Severity level.
+ char const* format, ///< Format string.
+ ... ///< Format string parameters.
+);
+/// Return an Errata in a return value populated with a printf style formatted string.
+/// Use message ID 0.
+/// @return The @c Errata in @a rv.
+Errata& logf(
+ RvBase& rv, ///< Rv value.
+ Errata::Code code, ///< Severity level.
+ char const* format, ///< Message text.
+ ...
+);
+/// Return an Errata populated with a literal string.
+/// Use message ID 0.
+/// @return @a err.
+Errata log(
+ Errata::Code code, ///< Severity level.
+ char const* text ///< Message text.
+);
+/// Return an Errata populated with a printf style formatted string.
+/// Use message ID 0.
+/// @return @a err.
+Errata logf(
+ Errata::Code code, ///< Severity level.
+ char const* format, ///< Message text.
+ ...
+);
+/** Return an Errata based on @c errno.
+ The literal string is combined with the system text for the current
+ value of @c errno. This is modeled on @c perror. Message ID 0 is used.
+ @return The new @c Errata.
+ */
+Errata log_errno(
+ Errata::Code code, ///< Severity level.
+ char const* text ///< Message text.
+);
+/** Return an @c Errata based on @c errno.
+ @c errno and the corresponding system error string are appended to
+ the results from the @a format and following arguments.
+ @return The new @c Errata.
+ */
+Errata
+logf_errno(
+ Errata::Code code, ///< Severity code.
+ char const* format, ///< Format string.
+ ... ///< Arguments for @a format.
+);
+/** Add a message to an @a errata based on @c errno.
+ @c errno and the corresponding system error string are appended to
+ the results from the @a format and following arguments.
+ @return @a errata.
+ */
+Errata
+logf_errno(
+ Errata& errata, ///< Errata to use.
+ Errata::Code code, ///< Severity code.
+ char const* format, ///< Format string.
+ ... ///< Arguments for @a format.
+);
+/** Add a message to a return value based on @c errno.
+ @c errno and the corresponding system error string are appended to
+ the results from the @a format and following arguments.
+ @return The errata in @a rv.
+ */
+Errata
+logf_errno(
+ RvBase& rv, ///< Return value.
+ Errata::Code code, ///< Severity code.
+ char const* format, ///< Format string.
+ ... ///< Arguments for @a format.
+);
+//@}
+
+}} // namespace ts::msg
+
+# endif // define TS_ERRATA_UTIL_HEADER
Added: trafficserver/traffic/branches/wccp/proxy/tsconfig/TsValue.cc
URL: http://svn.apache.org/viewvc/trafficserver/traffic/branches/wccp/proxy/tsconfig/TsValue.cc?rev=1037235&view=auto
==============================================================================
--- trafficserver/traffic/branches/wccp/proxy/tsconfig/TsValue.cc (added)
+++ trafficserver/traffic/branches/wccp/proxy/tsconfig/TsValue.cc Sat Nov 20 15:17:55 2010
@@ -0,0 +1,338 @@
+# include "TsValue.h"
+# include "TsBuilder.h"
+
+# include <TsErrataUtil.h>
+# include <sys/stat.h>
+# include <malloc.h>
+
+# if !defined(_MSC_VER)
+# define _fileno fileno
+# endif
+
+// ---------------------------------------------------------------------------
+namespace ts { namespace config {
+// ---------------------------------------------------------------------------
+Buffer const detail::NULL_BUFFER(0,0);
+ConstBuffer const detail::NULL_CONST_BUFFER(0,0);
+detail::ValueItem detail::ValueTableImpl::NULL_ITEM(VoidValue);
+detail::PseudoBool::Type const detail::PseudoBool::FALSE = 0;
+detail::PseudoBool::Type const detail::PseudoBool::TRUE = &detail::PseudoBool::operator !;
+// This should not be called, it is used only as a pointer value.
+bool detail::PseudoBool::operator ! () const { return false; }
+// ---------------------------------------------------------------------------
+unsigned int const detail::Type_Property[N_VALUE_TYPES] = {
+ 0, // Void
+ detail::IS_VALID | detail::IS_CONTAINER, // List
+ detail::IS_VALID | detail::IS_CONTAINER, // Group
+ detail::IS_VALID | detail::IS_LITERAL, // String
+ detail::IS_VALID | detail::IS_LITERAL, // Integer
+};
+// ---------------------------------------------------------------------------
+detail::ValueTableImpl::ValueTableImpl() : _generation(0) { }
+detail::ValueTableImpl::~ValueTableImpl() {
+ for ( BufferGroup::iterator spot(_buffers.begin()), limit(_buffers.end()) ; spot != limit ; ++spot)
+ free(spot->_ptr);
+}
+// ---------------------------------------------------------------------------
+detail::ValueTable::ImplType*
+detail::ValueTable::instance() {
+ if (! _ptr) _ptr.reset(new ImplType);
+ return _ptr.get();
+}
+
+detail::ValueTable&
+detail::ValueTable::forceRootItem() {
+ ImplType* imp = this->instance();
+ if (0 == imp->_values.size())
+ imp->_values.push_back(ValueItem(GroupValue));
+ return *this;
+}
+
+Rv<detail::ValueIndex>
+detail::ValueTable::make(ValueIndex pidx, ValueType type, ConstBuffer const& name) {
+ Rv<ValueIndex> zret = NULL_VALUE_INDEX;
+ if (_ptr) {
+ size_t n = _ptr->_values.size();
+ // Check the parent
+ if (pidx < n) {
+ ValueItem* parent = &(_ptr->_values[pidx]);
+ if (IS_CONTAINER & Type_Property[parent->_type]) {
+ ValueItem* item;
+
+ _ptr->_values.push_back(ValueItem(type));
+ parent = &(_ptr->_values[pidx]); // possibly stale, refresh.
+ item = &(_ptr->_values[n]);
+ item->_parent = pidx;
+ parent->_children.push_back(n);
+ // Only use the name if the parent is a group.
+ if (GroupValue == parent->_type) item->_name = name;
+ zret = n; // mark for return to caller.
+ } else {
+ msg::log(zret.errata(), msg::WARN, "Add child failed because parent is not a container.");
+ }
+ } else {
+ msg::logf(zret.errata(), msg::WARN, "Add child failed because parent index (%ul) is out of range (%ul).", pidx.raw(), n);
+ }
+ } else {
+ msg::log(zret.errata(), msg::WARN, "Add child failed because the configuration is null.");
+ }
+ return zret;
+}
+
+detail::ValueItem&
+detail::ValueTable::operator [] ( ValueIndex idx ) {
+ assert(_ptr && idx < _ptr->_values.size());
+ return _ptr->_values[idx];
+}
+Buffer
+detail::ValueTable::alloc(size_t n) {
+ ImplType* imp = this->instance();
+ Buffer zret(static_cast<char*>(malloc(n)), n);
+ if (zret._ptr) imp->_buffers.push_back(zret);
+ return zret;
+}
+
+// ---------------------------------------------------------------------------
+Value
+Value::operator [] (size_t idx) {
+ Value zret;
+ detail::ValueItem* item = this->item();
+ if (item && idx < item->_children.size()) {
+ zret = Value(_config, item->_children[idx]);
+ if (PathValue == zret.getType()) zret = _config.getRoot().find(_config._table[zret._vidx]._path);
+ }
+ return zret;
+}
+
+Value
+Value::operator [] (ConstBuffer const& name) {
+ Value zret;
+ detail::ValueItem* item = this->item();
+ if (item) {
+ for ( detail::ValueItem::ChildGroup::iterator spot = item->_children.begin(), limit = item->_children.end(); spot != limit; ++spot ) {
+ if (_config._table[*spot]._name == name) {
+ zret = Value(_config, *spot);
+ if (PathValue == zret.getType()) zret = _config.getRoot().find(_config._table[zret._vidx]._path);
+ break;
+ }
+ }
+ }
+ return zret;
+}
+
+Value
+Value::find( ConstBuffer const& path ) {
+ Value zret = *this;
+ Path::Parser parser(path);
+ Rv<Path::Parser::Result> x;
+ ConstBuffer elt;
+ for ( x = parser.parse(&elt) ; zret && Path::Parser::EOP != x && Path::Parser::ERROR != x ; x = parser.parse(&elt) ) {
+ if (Path::Parser::TAG == x) zret = zret[elt];
+ else if (Path::Parser::INDEX == x) zret = zret[elt._size];
+ else zret.reset();
+ }
+ if (Path::Parser::EOP != x) zret.reset();
+ return zret;
+}
+
+Value
+Value::find(Path const& path ) {
+ Value zret = *this;
+ for ( size_t i = 0, n = path.count() ; i < n && zret ; ++i ) {
+ ConstBuffer const& elt = path[i];
+ if (elt._ptr) zret = zret[elt];
+ else zret = zret[elt._size];
+ }
+ return zret;
+}
+
+Rv<Value>
+Value::makeChild(ValueType type, ConstBuffer const& name) {
+ Rv<Value> zret;
+ Rv<detail::ValueIndex> vr = _config._table.make(this->_vidx, type, name);
+ if (vr.isOK()) zret = Value(_config, vr.result());
+ else zret.errata() = vr.errata();
+ return zret;
+}
+
+Rv<Value>
+Value::makeGroup(ConstBuffer const& name) {
+ return this->makeChild(GroupValue, name);
+}
+
+Rv<Value>
+Value::makeList(ConstBuffer const& name) {
+ return this->makeChild(ListValue, name);
+}
+
+Rv<Value>
+Value::makeString(ConstBuffer const& text, ConstBuffer const& name) {
+ Rv<Value> zret = this->makeChild(StringValue, name);
+ if (zret.isOK()) zret.result().setText(text);
+ return zret;
+}
+
+Rv<Value>
+Value::makeInteger(ConstBuffer const& text, ConstBuffer const& name) {
+ Rv<Value> zret = this->makeChild(IntegerValue, name);
+ if (zret.isOK()) zret.result().setText(text);
+ return zret;
+}
+
+Rv<Value>
+Value::makePath(Path const& path, ConstBuffer const& name) {
+ Rv<Value> zret = this->makeChild(PathValue, name);
+ if (zret.isOK()) _config._table[zret.result()._vidx]._path = path;
+ return zret;
+}
+// ---------------------------------------------------------------------------
+Path& Path::reset() {
+ if (_ptr) {
+ // If we're sharing the instance, make a new one for us.
+ if (_ptr.isShared()) _ptr = new ImplType;
+ else { // clear out the existing instance.
+ _ptr->_elements.clear();
+ }
+ }
+ return *this;
+}
+// ---------------------------------------------------------------------------
+Rv<Path::Parser::Result>
+Path::Parser::parse(ConstBuffer *cbuff) {
+ Rv<Result> zret = EOP;
+ enum State {
+ S_INIT, // initial state
+ S_INDEX, // reading index.
+ S_TAG, // reading tag.
+ S_DASH, // reading dashes in tag.
+ } state = S_INIT;
+
+ // Character bucket
+ enum Bucket {
+ C_INVALID, // Invalid input.
+ C_DIGIT, // digit.
+ C_IDENT, // Identifier character.
+ C_DASH, // A dash
+ C_DOT, // A dot (period).
+ };
+
+ if (cbuff) cbuff->reset();
+ char const* start = _c; // save starting character location.
+ size_t idx = 0; // accumulator for index value.
+
+ bool final = false;
+ while (! final && this->hasInput()) {
+ Bucket cb;
+ if (isdigit(*_c)) cb = C_DIGIT;
+ else if ('_' == *_c || isalpha(*_c)) cb = C_IDENT;
+ else if ('-' == *_c) cb = C_DASH;
+ else if ('.' == *_c) cb = C_DOT;
+ else cb = C_INVALID;
+
+ if (C_INVALID == cb) {
+ msg::logf(zret, msg::WARN, "Invalid character '%c' [%u] in path.", *_c, *_c);
+ } else switch (state) {
+ case S_INIT:
+ switch (cb) {
+ case C_DIGIT: state = S_INDEX; idx = *_c - '0'; break;
+ case C_IDENT: state = S_TAG; break;
+ case C_DASH: msg::logf(zret, msg::WARN, "Dash not allowed as leading character for tag."); final = true; break;
+ case C_DOT: msg::logf(zret, msg::WARN, "Separator without preceding element."); final = true; break;
+ default: msg::logf(zret, msg::WARN, "Internal error: unexpected character %u in INIT state.", *_c); final = true; break;
+ }
+ break;
+ case S_INDEX: // reading an index.
+ if (C_DIGIT == cb) idx = 10 * idx + *_c - '0';
+ else if (C_DOT == cb) { final = true; }
+ else {
+ msg::logf(zret, msg::WARN, "Invalid character '%c' [%u] in index element.", *_c, *_c);
+ final = true;
+ }
+ break;
+ case S_TAG: // reading a tag.
+ if (C_IDENT == cb || C_DIGIT == cb) ; // continue
+ else if (C_DASH == cb) state = S_DASH;
+ else if (C_DOT == cb) { final = true; }
+ else { // should never happen, but be safe.
+ msg::logf(zret, msg::WARN, "Invalid character '%c' [%u] in index element.", *_c, *_c);
+ final = true;
+ }
+ break;
+ case S_DASH: // dashes inside tag.
+ if (C_IDENT == cb || C_DIGIT == cb) state = S_TAG;
+ else if (C_DOT == cb) {
+ msg::log(zret, msg::WARN, "Trailing dash not allowed in tag element.");
+ final = true;
+ } else if (C_DASH != cb) { // should never happen, but be safe.
+ msg::logf(zret, msg::WARN, "Invalid character '%c' [%u] in index element.", *_c, *_c);
+ final = true;
+ }
+ break;
+ }
+ ++_c;
+ }
+ if (!zret.isOK()) {
+ zret = ERROR;
+ if (cbuff) cbuff->set(_c - 1, 1);
+ _c = 0;
+ _input.reset();
+ } else if (S_INIT == state) {
+ zret = EOP;
+ } else if (S_TAG == state) {
+ zret = TAG;
+ if (cbuff) {
+ cbuff->set(start, _c - start);
+ // if @a final is set, then we parsed a dot separator.
+ // don't include it in the returned tag.
+ if (final) cbuff->_size -= 1;
+ }
+ } else if (S_INDEX == state) {
+ zret = INDEX;
+ if (cbuff) cbuff->_size = idx;
+ } else if (S_DASH == state) {
+ zret = ERROR;
+ msg::log(zret, msg::WARN, "Trailing dash not allowed in tag element.");
+ if (cbuff) cbuff->set(start, _c - start);
+ }
+ return zret;
+}
+// ---------------------------------------------------------------------------
+Value
+Configuration::getRoot() {
+ this->_table.forceRootItem();
+ return Value(*this, 0);
+}
+
+Rv<Configuration>
+Configuration::loadFromPath(char const* path) {
+ Rv<Configuration> zret;
+ Buffer buffer(0,0);
+ FILE* in = fopen(path, "r");
+
+ if (in) {
+ struct stat info;
+ if (0 == fstat(_fileno(in), &info)) {
+ // Must reserve 2 bytes at the end for FLEX terminator.
+ buffer = zret.result().alloc(info.st_size + 2);
+ if (buffer._ptr) {
+ size_t n;
+ if (0 < (n = fread(buffer._ptr, sizeof(char), info.st_size, in))) {
+ buffer._size = n+2;
+ memset(buffer._ptr + n, 0, 2); // required by FLEX
+ zret = Builder(zret.result()).build(buffer);
+ } else {
+ msg::logf_errno(zret, msg::WARN, "failed to read %llu bytes from configuration file '%s'", info.st_size, path);
+ }
+ } else {
+ msg::logf_errno(zret, msg::WARN, "failed to allocate buffer for configuration file '%s' - needed %llu bytes.", path, info.st_size);
+ }
+ } else {
+ msg::logf_errno(zret, msg::WARN, "failed to determine file information on '%s'", path);
+ }
+ } else {
+ msg::logf_errno(zret, msg::WARN, "failed to open configuration file '%s'", path);
+ }
+ return zret;
+}
+
+}} // namespace ts::config
Added: trafficserver/traffic/branches/wccp/proxy/tsconfig/TsValue.h
URL: http://svn.apache.org/viewvc/trafficserver/traffic/branches/wccp/proxy/tsconfig/TsValue.h?rev=1037235&view=auto
==============================================================================
--- trafficserver/traffic/branches/wccp/proxy/tsconfig/TsValue.h (added)
+++ trafficserver/traffic/branches/wccp/proxy/tsconfig/TsValue.h Sat Nov 20 15:17:55 2010
@@ -0,0 +1,604 @@
+# if ! defined(TS_CONFIG_VALUE_HEADER)
+# define TS_CONFIG_VALUE_HEADER
+
+# include <TsBuffer.h>
+# include <NumericType.h>
+# include <IntrusivePtr.h>
+# include <Errata.h>
+# include <vector>
+
+namespace ts { namespace config {
+
+// Forward declares.
+class Value;
+class Path;
+
+namespace detail {
+ /** Class to provide a "pseudo bool" value.
+ This is used as the return type for the positive logical operator
+ (the converse of @c operator! ). This makes a class directly
+ usable in logical expressions. It is like a pointer but @b not
+ convertible to anything else, and so avoiding any undesirable
+ automatic conversions and the resulting ambiguities.
+ */
+ struct PseudoBool {
+ typedef bool (PseudoBool::*Type)() const; ///< The type itself.
+ bool operator ! () const; ///< A method to use for the @c true value.
+ static Type const TRUE; ///< The @c true equivalent.
+ static Type const FALSE; ///< The @c false equivalent.
+ };
+}
+
+/// Type of value.
+enum ValueType {
+ VoidValue, ///< No value, invalid.
+ ListValue, ///< List of values.
+ GroupValue, ///< Group of values.
+ StringValue, ///< Text string.
+ IntegerValue, ///< Integer.
+ PathValue, ///< Path.
+ // Update N_VALUE_TYPES if you change the last enum value !!
+};
+/// Number of value types.
+static size_t const N_VALUE_TYPES = PathValue + 1;
+
+/** A path to a value in a configuration.
+ */
+class Path {
+ friend class Value;
+protected:
+ class ImplType : public IntrusivePtrCounter {
+ friend class Path;
+ public:
+ ImplType(); ///< Constructor.
+ protected:
+ /** Container for path elements.
+ We are subtle with our elements, which can be either a string
+ or a numeric index. By convention, if the pointer in the buffer is
+ @c NULL, then the size is a numeric index. Otherwise it's a name.
+ */
+ typedef std::vector<ConstBuffer> Elements;
+ Elements _elements; ///< Path elements.
+ };
+public:
+ typedef Path self; ///< Self reference type.
+
+ Path(); ///< Default constructor.
+
+ /// Append a string tag to the path.
+ self& append(
+ ConstBuffer const& tag ///< Text of tag.
+ );
+ /// Append a numeric index to the path.
+ self& append(
+ size_t idx ///< Index.
+ );
+ /// Reset to default constructed state.
+ self& reset();
+
+ /// Get the number of elements in this path.
+ size_t count() const;
+
+ /// Access an element by @a index.
+ ConstBuffer const& operator [] (
+ size_t index ///< Element index.
+ ) const;
+
+ /** Parser for path text.
+ This is restartable so a path can be parsed in pieces.
+ @internal Sadly, FLEX is just too much overhead to be useful here.
+ */
+ class Parser {
+ public:
+ typedef Parser self; ///< Self reference type.
+
+ Parser(); ///< Default constructor.
+ /** Construct with input.
+
+ This default constructs the Parser then calls @c setInput with
+ @a text. It is provided as a convenience as that will be the
+ common use case.
+
+ @see setInput.
+ */
+ Parser(
+ ConstBuffer const& text ///< Input text.
+ );
+
+ /** Set the input @a text.
+ Parsing state is reset and the next parsing call will
+ start at the beginning of @a text.
+ */
+ self& setInput(
+ ConstBuffer const& text ///< Input buffer.
+ );
+
+ /// Parsing result.
+ enum Result {
+ ERROR, ///< Bad input.
+ TAG, ///< Path tag.
+ INDEX, ///< Path index.
+ EOP, ///< End Of Path.
+ };
+
+ /** Parse the next element in the path.
+
+ @a cbuff may be @c NULL in which case no data about elements
+ is available. In general this should be called until @c EOP
+ or @c ERROR is returned, each call returning the next element.
+
+ @return A parse @c Result.
+ - TAG: A tag was found. The start and length are stored in @a cbuff.
+ - INDEX: An index was found. The value is in @a cbuff._size.
+ - EOP: No more path elements were found. Do not continue parsing.
+ - ERROR: A syntax error was encountered. See the errata for detail. Do not continue parsing.
+ */
+ Rv<Result> parse(
+ ConstBuffer* cbuff = 0 ///< [out] Parsed path element.
+ );
+
+ /// Check if input is available.
+ bool hasInput() const;
+
+ protected:
+ ConstBuffer _input; ///< Current input buffer.
+ char const* _c; ///< Next input character.
+ };
+protected:
+ typedef IntrusivePtr<ImplType> ImplPtr; ///< Smart pointer to implementation.
+ ImplPtr _ptr; ///< Our instance.
+ /// Force an implementation instance and return a pointer to it.
+ ImplType* instance();
+};
+
+namespace detail {
+ /// Null buffer, handy in several places.
+ extern Buffer const NULL_BUFFER;
+ /// Null buffer, handy in several places.
+ extern ConstBuffer const NULL_CONST_BUFFER;
+ /// Index type for value items in the global table.
+ typedef NumericType<size_t, struct ValueIndexTag> ValueIndex;
+ /// Index value that presents NULL (invalid value).
+ static ValueIndex const NULL_VALUE_INDEX = static_cast<ValueIndex::raw_type>(-1);
+ /// Numeric type for configuration generation.
+ typedef NumericType<size_t, struct GenerationTag> Generation;
+
+ /** Value type properties.
+ These are used as bit masks on elements of an array.
+ */
+ static unsigned int const IS_VALID = 1;
+ static unsigned int const IS_LITERAL = 1<<1;
+ static unsigned int const IS_CONTAINER = 1<<2;
+
+ /// Value type property table.
+ extern unsigned int const Type_Property[N_VALUE_TYPES];
+
+ /** A value in the configuration.
+ This is used in a global table so it handles all types of Values.
+ Members that are not used for scalars are designed to be @c NULL
+ pointers in that case.
+ */
+ class ValueItem {
+ // Apparently the C++ standard, 7.3.1.2, states that unqualified
+ // friend classes only considers the current namespace, not any
+ // outer ones. So we have to fully qualify this. Blech.
+ friend class ts::config::Value;
+ friend class ValueTable;
+ public:
+ /// Default constructor.
+ ValueItem();
+ /// Construct empty item of a specific type.
+ ValueItem(ValueType type);
+ /// Get item type.
+ ValueType getType() const;
+ protected:
+ /// Type of value.
+ ValueType _type;
+ /// Index of parent value.
+ ValueIndex _parent;
+ /// Text of value (if scalar).
+ ConstBuffer _text;
+ /// Local name of value, if available.
+ ConstBuffer _name;
+ /// Container for children of this item.
+ typedef std::vector<ValueIndex> ChildGroup;
+ /// Child items of this item.
+ ChildGroup _children;
+ /// Path if present.
+ Path _path;
+
+ // This is for optimizing named access at some point in the future.
+ /// Hold a child item name in a table for fast lookup.
+ struct Name {
+ ConstBuffer _text; ///< Text of name.
+ ValueIndex _index; ///< Index of child.
+ };
+ /// Container for child names.
+ typedef std::vector<Name> NameGroup;
+ /** Child names, if appropriate.
+ This is faulted in when needed, if this value is an aggregate with
+ named children. The list must be sorted on name so that it can be binary
+ searched for performance.
+ */
+ NameGroup _names;
+ };
+
+ class ValueTable;
+
+ /** Table of configuration values.
+ This holds all the values for a specific configuration.
+ */
+ class ValueTableImpl : public IntrusivePtrCounter {
+ friend class ValueTable;
+ public:
+ typedef ValueTableImpl self; ///< Self reference type.
+
+ ValueTableImpl(); ///< Constructor.
+ ~ValueTableImpl(); ///< Destructor.
+ protected:
+ /// Container for value items.
+ typedef std::vector<ValueItem> ItemTable;
+ ItemTable _values; ///< All configuration values.
+ Generation _generation; ///< Generation number of configuration.
+ /// A group of buffers.
+ typedef std::vector<Buffer> BufferGroup;
+ /** Locally allocated buffers.
+ These are freed when this object is destroyed.
+ */
+ BufferGroup _buffers;
+
+ static ValueItem NULL_ITEM; ///< Null item for invalid access return.
+ };
+
+ /** Wrapper class for a table of configuration values.
+ @internal Really, this should be merged in to Configuration. The original
+ differences have evolved out of the implementation.
+ */
+ class ValueTable {
+ public:
+ typedef ValueTable self; ///< Self reference type.
+ typedef ValueTableImpl ImplType; ///< Implementation type.
+
+ /// Table size.
+ /// @return The number of value items in the table.
+ size_t size() const;
+ /// Generation.
+ /// @return The generation number.
+ Generation generation() const;
+
+ /// Const access by index.
+ /// @return The value item at index @a idx.
+ ValueItem const& operator [] (
+ ValueIndex idx ///< Index of item.
+ ) const;
+ /// Access by index.
+ /// @return The value item at index @a idx.
+ ValueItem& operator [] (
+ ValueIndex idx ///< Index of item.
+ );
+
+ /// Force the existence of the root item in the table.
+ /// @return @c this object.
+ self& forceRootItem();
+ /** Create a new item (value) with optional @a name
+ The table must contain @a parent. If @a name is omitted, the item
+ has an empty name.
+ @return Index of the new value item.
+ */
+ Rv<ValueIndex> make(
+ ValueIndex parent, ///< Index of parent for item.
+ ValueType type, ///< Type of item.
+ ConstBuffer const& name = NULL_BUFFER ///< Name (may be empty).
+ );
+
+ /// Test for not table existence.
+ /// @return @c false if the implementation instance exists, @c true if not.
+ bool operator ! () const;
+ /// Test for table existence.
+ /// @return @c true if the implementation instance exists, @c false if not.
+ operator PseudoBool::Type() const;
+ /// Reset to default constructed state.
+ /// @return @c this object.
+ self& reset();
+
+ /** Allocate a local buffer.
+ This buffer will persist until the implementation instance
+ is destoyed.
+ @return The allocated buffer.
+ */
+ Buffer alloc(size_t n);
+ protected:
+ typedef IntrusivePtr<ImplType> ImplPtr; ///< Smart pointer to implementation instance.
+ ImplPtr _ptr; ///< Implementation instance.
+
+ /// Force an implementation instance and return a pointer to it.
+ ImplType* instance();
+ };
+} // namespace detail
+
+/** Container for a configuration.
+ This is a wrapper class that holds a shared reference to a configuration.
+*/
+class Configuration {
+ friend class Value;
+public:
+ typedef Configuration self; ///< Self reference type.
+
+ /** Check if configuration is (not) valid.
+ @return @c true if this configuration is invalid, @c false otherwise.
+ */
+ bool operator ! () const;
+ /** Check if the configuration is valid.
+ @return The equivalent of @c true if this does @b not contain a value,
+ the equivalent of @c false if it does.
+ */
+ operator detail::PseudoBool::Type () const;
+ /** Get the root @c Value of the configuration.
+ The root is always a group and has no name.
+ @return The root value.
+ */
+ Value getRoot();
+
+ /** Find a value.
+ @return The value if found, an void valid if not.
+ */
+ Value find(
+ char const* path ///< configuration path to value.
+ );
+ /** Load a configuration from a file.
+
+ @note Check the returned errata for problems during configuration
+ load. It is probably not a good idea to use the configuration in
+ any error are reported.
+ @return A new @c Configuration and errata.
+ */
+ static Rv<self> loadFromPath(
+ char const* path ///< file system path.
+ );
+ /** Allocate a local buffer of size @a n.
+ This buffer will persist until the implementation instance
+ is destroyed.
+ @return The allocated buffer.
+ */
+ Buffer alloc(
+ size_t n ///< requested size of buffer.
+ );
+protected:
+ detail::ValueTable _table; ///< Table of values from the configuration.
+};
+
+/** This holds a value from the configuration.
+
+ @internal It is critical that none of the type specific subclasses define any data members
+ so that instances can be freely converted to and from this base class.
+*/
+class Value {
+ friend class Configuration;
+public:
+ typedef Value self; ///< Self reference type.
+ /// Default constructors.
+ /// Creates an @c NULL instance.
+ Value();
+ /// Destructor.
+ ~Value();
+
+ /// Get the type of value.
+ ValueType getType() const;
+ /// Test if this is a valid value.
+ /// @return @c true if this contains a value, @c false otherwise.
+ bool hasValue() const;
+ /** Operator form of @c hasValue.
+ @see hasValue
+ @return @c true if this does @b not contain a value, @c false if it does.
+ */
+ bool operator ! () const;
+ /** Logical form of @c hasValue for use in logical expressions.
+ @see hasValue
+ @return The equivalent of @c true if this does @b not contain a value,
+ the equivalent of @c false if it does.
+ */
+ operator detail::PseudoBool::Type () const;
+
+ /** Get the value text.
+ @return The text in the configuration file for this item if the item
+ is a scalar, an empty buffer otherwise.
+ */
+ ConstBuffer const& getText() const;
+ /// Set the @a text for this value.
+ self& setText(
+ ConstBuffer const& text
+ );
+
+ /// Test for a literal value.
+ /// @return @c true if the value is a literal, @c false if it is a container or invalid.
+ bool isLiteral() const;
+ /// Test for value container.
+ /// @return @c true if the value is a container (can have child values), @c false otherwise.
+ bool isContainer() const;
+ /// Get the parent value.
+ Value getParent() const;
+ /// Test if this is the root value for the configuration.
+ bool isRoot() const;
+
+ /// Get the number of child values.
+ size_t childCount() const;
+ /** Child access by @a index
+ @return The child or a @c Void value if there is no child with @a name.
+ */
+ Value operator [] (
+ size_t idx ///< Index of child value.
+ );
+ /** Child access by @a name.
+ @return The child or a @c Void value if there is no child with @a name.
+ */
+ Value operator [] (
+ ConstBuffer const& name
+ );
+ /** Child access by @a name.
+ @return The child or a @c Void value if there is no child with @a name.
+ */
+ Value operator [] (
+ char const* name ///< Null terminated string.
+ );
+
+ /** Creating child values.
+
+ These methods all take an optional @a name argument. This is required if @c this
+ is a @c Group and ignored if @c this is a @c List.
+
+
+ These methods will fail if
+ - @c this is not a container.
+ - @c this is a @c Group and no @a name is provided.
+
+ @note Currently for groups, duplicate names are not detected. The duplicates
+ will be inaccessible by name but can still be found by index. This is a problem
+ but I am still pondering the appropriate solution.
+
+ @see isContainer
+ @return The new value, or an invalid value plus errata on failure.
+
+ @internal I original had this as a single method, but changed to separate per type.
+ Overall less ugly because we can get the arguments more useful.
+ */
+ //@{
+ /// Create a @c String value.
+ Rv<Value> makeString(
+ ConstBuffer const& text, ///< String content.
+ ConstBuffer const& name = detail::NULL_BUFFER///< Optional name of value.
+ );
+ /// Create an @c Integer value.
+ Rv<Value> makeInteger(
+ ConstBuffer const& text, ///< Text of number.
+ ConstBuffer const& name = detail::NULL_BUFFER///< Optional name of value.
+ );
+ /// Create a @c Group value.
+ Rv<Value> makeGroup(
+ ConstBuffer const& name = detail::NULL_BUFFER///< Optional name of value.
+ );
+ /// Create a @c List value.
+ Rv<Value> makeList(
+ ConstBuffer const& name = detail::NULL_BUFFER///< Optional name of value.
+ );
+ /// Create a @c Path value.
+ Rv<Value> makePath(
+ Path const& path, ///< Path.
+ ConstBuffer const& name = detail::NULL_BUFFER///< Optional name of value.
+ );
+ /// Create a child by type.
+ /// Client must fill in any other required elements.
+ Rv<Value> makeChild(
+ ValueType type, ///< Type of child.
+ ConstBuffer const& name = detail::NULL_BUFFER///< Optional name of value.
+ );
+ //@}
+
+ /** Find a value.
+ @return The value if found, an void valid if not.
+ */
+ Value find(
+ ConstBuffer const& path ///< Path relative to this value.
+ );
+ /** Find a value.
+ @return The value if found, an void valid if not.
+ */
+ Value find(
+ char const* path ///< Path relative to this value.
+ );
+ /** Find a value using a precondensed path.
+ @return The value if found, an void valid if not.
+ */
+ Value find(
+ Path const& path ///< Path relative to this value.
+ );
+
+ /** Reset to default constructed state.
+ @note This wrapper is reset, the value in the configuration is unchanged.
+ @return @c this object.
+ */
+ self& reset();
+
+protected:
+ // Note: We store an index and not a pointer because a pointer will go stale
+ // if any items are added or removed from the underlying table.
+ // Also, by storing the configuration, we hold it in memory as long as a Value
+ // is in client hands.
+ Configuration _config; ///< The configuration for this value.
+ detail::ValueIndex _vidx; ///< Index of item.
+
+ static Buffer const NULL_BUFFER; ///< Empty buffer to return on method failures.
+
+ /// Construct from raw data.
+ Value(
+ Configuration cfg, ///< Source configuration.
+ detail::ValueIndex vidx ///< Index of value.
+ );
+
+ /** Get raw item pointer.
+ @note This pointer is unstable and must be recomputed on each method invocation.
+ @return The item pointer or @c NULL if this value is invalid.
+ */
+ detail::ValueItem* item();
+ /** Get constant raw item pointer.
+ @note This pointer is unstable and must be recomputed on each method invocation.
+ @return The item pointer or @c NULL if this value is invalid.
+ */
+ detail::ValueItem const* item() const;
+};
+
+// Inline methods.
+namespace detail {
+ inline bool ValueTable::operator ! () const { return ! _ptr; }
+ inline ValueTable::operator PseudoBool::Type () const { return _ptr ? PseudoBool::TRUE : PseudoBool::FALSE; }
+ inline size_t ValueTable::size() const { return _ptr ? _ptr->_values.size() : 0; }
+ inline Generation ValueTable::generation() const { return _ptr ? _ptr->_generation : Generation(0); }
+ inline ValueItem const& ValueTable::operator [] (ValueIndex idx) const { return const_cast<self*>(this)->operator [] (idx); }
+ inline ValueTable& ValueTable::reset() { _ptr = 0; return *this; }
+
+ inline ValueItem::ValueItem() : _type(VoidValue), _text(0,0), _name(0,0) {}
+ inline ValueItem::ValueItem(ValueType type) : _type(type), _text(0,0), _name(0,0) {}
+ inline ValueType ValueItem::getType() const { return _type; }
+}
+
+inline Value::~Value() { }
+inline Value::Value() : _vidx(detail::NULL_VALUE_INDEX) {}
+inline Value::Value(Configuration cfg, detail::ValueIndex vidx) : _config(cfg), _vidx(vidx) { }
+inline bool Value::hasValue() const { return _config && _vidx != detail::NULL_VALUE_INDEX; }
+inline Value::operator detail::PseudoBool::Type () const { return this->hasValue() ? detail::PseudoBool::TRUE : detail::PseudoBool::FALSE; }
+inline bool Value::operator ! () const { return ! this->hasValue(); }
+inline ValueType Value::getType() const { return this->hasValue() ? _config._table[_vidx]._type : VoidValue; }
+inline ConstBuffer const& Value::getText() const {
+ return this->hasValue() ? _config._table[_vidx]._name : detail::NULL_CONST_BUFFER;
+}
+inline Value& Value::setText(ConstBuffer const& text) { if (this->hasValue()) _config._table[_vidx]._text = text; return *this; }
+inline bool Value::isLiteral() const { return 0 != (detail::IS_LITERAL & detail::Type_Property[this->getType()]); }
+inline bool Value::isContainer() const { return 0 != (detail::IS_CONTAINER & detail::Type_Property[this->getType()]); }
+inline Value Value::getParent() const { return this->hasValue() ? Value(_config, _config._table[_vidx]._parent) : Value(); }
+inline bool Value::isRoot() const { return this->hasValue() && _vidx == 0; }
+inline Value& Value::reset() { _config = Configuration(); _vidx = detail::NULL_VALUE_INDEX; return *this; }
+inline detail::ValueItem* Value::item() { return this->hasValue() ? &(_config._table[_vidx]) : 0; }
+inline detail::ValueItem const* Value::item() const { return const_cast<self*>(this)->item(); }
+inline Value Value::operator [] (char const* name) { return (*this)[ConstBuffer(name, strlen(name))]; }
+inline Value Value::find(char const* path) { return this->find(ConstBuffer(path, strlen(path))); }
+
+inline Path::ImplType::ImplType() { }
+
+inline Path::Path() { }
+inline Path::ImplType* Path::instance() { if (!_ptr) _ptr = new ImplType; return _ptr.get(); }
+inline Path& Path::append(ConstBuffer const& tag) { this->instance()->_elements.push_back(tag); return *this; }
+inline Path& Path::append(size_t index) { this->instance()->_elements.push_back(ConstBuffer(0, index)); return *this; }
+inline size_t Path::count() const { return _ptr ? _ptr->_elements.size() : 0; }
+inline ConstBuffer const& Path::operator [] (size_t idx) const { return _ptr ? _ptr->_elements[idx] : detail::NULL_CONST_BUFFER; }
+
+inline Path::Parser::Parser() : _input(0,0), _c(0) { }
+inline Path::Parser::Parser( ConstBuffer const& text ) : _input(text), _c(text._ptr) { }
+inline bool Path::Parser::hasInput() const { return _input._ptr && _input._ptr + _input._size > _c; }
+
+inline bool Configuration::operator ! () const { return ! _table; }
+inline Configuration::operator detail::PseudoBool::Type() const { return _table.operator detail::PseudoBool::Type(); }
+inline Value Configuration::find( char const* path ) { return this->getRoot().find(path); }
+inline Buffer Configuration::alloc(size_t n) { return _table.alloc(n); }
+
+}} // namespace ts::config
+
+# endif