summaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
authorpippijn <pippijn@e03df62e-2008-0410-955e-edbf42e46eb7>2007-07-17 12:53:49 +0000
committerpippijn <pippijn@e03df62e-2008-0410-955e-edbf42e46eb7>2007-07-17 12:53:49 +0000
commit5bf8b9e7e9a8bc670d1b745a890d328547365759 (patch)
tree72444e682060447d78242f6c926b9c3b89ac6f7e /src/modules
parent49a4bc63114fcbaf197845cbdebbdd8a90115409 (diff)
Added a JSON-RPC module that can be used by other modules to provide a remote
procedure call interface. This should work in theory, but does not for me. But then, neither does m_httpd_stats so it's probably me... git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@7460 e03df62e-2008-0410-955e-edbf42e46eb7
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/json.h535
-rw-r--r--src/modules/m_rpc_json.cpp2109
2 files changed, 2644 insertions, 0 deletions
diff --git a/src/modules/json.h b/src/modules/json.h
new file mode 100644
index 000000000..c9fc5c4ff
--- /dev/null
+++ b/src/modules/json.h
@@ -0,0 +1,535 @@
+#ifndef JSON_H
+#define JSON_H
+
+#include <string>
+#include <vector>
+#include <map>
+#include <deque>
+#include <stack>
+
+#if __GNUC__ >= 3
+# define maybe_expect(expr,value) __builtin_expect ((expr),(value))
+# define is_const(expr) __builtin_constant_p ((expr))
+#else
+# define maybe_expect(expr,value) (expr)
+# define is_const(expr) 0
+#endif
+
+#define expect_false(expr) maybe_expect ((expr) != 0, 0)
+#define expect_true(expr) maybe_expect ((expr) != 0, 1)
+
+static void unreachable_internal (char const *file, int line, char const *function);
+static void throw_unless_internal (char const *file, int line, char const *function, char const *condition);
+static void throw_msg_unless_internal (char const *file, int line, char const *function, char const *message);
+
+#define throw_unreachable unreachable_internal (__FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define throw_unless(condition) if (!expect_false (condition)) throw_unless_internal (__FILE__, __LINE__, __PRETTY_FUNCTION__, #condition)
+#define throw_msg_unless(condition, message) if (!expect_false (condition)) throw_msg_unless_internal (__FILE__, __LINE__, __PRETTY_FUNCTION__, message)
+
+namespace json
+{
+ class ValueIterator;
+ class ValueConstIterator;
+
+ enum ValueType
+ {
+ nullValue = 0,
+ intValue,
+ uintValue,
+ realValue,
+ stringValue,
+ booleanValue,
+ arrayValue,
+ objectValue
+ };
+
+ class StaticString
+ {
+ public:
+ explicit StaticString (char const *czstring)
+ : str_ (czstring)
+ {
+ }
+
+ operator char const *() const
+ {
+ return str_;
+ }
+
+ char const *c_str() const
+ {
+ return str_;
+ }
+
+ private:
+ char const *str_;
+ };
+
+ class Value
+ {
+ friend class ValueIteratorBase;
+ public:
+ typedef std::vector<std::string> Members;
+ typedef ValueIterator iterator;
+ typedef ValueConstIterator const_iterator;
+ typedef unsigned ArrayIndex;
+
+ static const Value null;
+ static const int minInt;
+ static const int maxInt;
+ static const unsigned maxUInt;
+
+ private:
+ class CZString
+ {
+ public:
+ enum DuplicationPolicy
+ {
+ noDuplication = 0,
+ duplicate,
+ duplicateOnCopy
+ };
+ CZString (int index);
+ CZString (char const *cstr, DuplicationPolicy allocate);
+ CZString (const CZString &other);
+ ~CZString ();
+ CZString &operator = (const CZString &other);
+ bool operator < (const CZString &other) const;
+ bool operator == (const CZString &other) const;
+ int index () const;
+ char const *c_str () const;
+ bool isStaticString () const;
+ private:
+ void swap (CZString &other);
+ char const *cstr_;
+ int index_;
+ };
+
+ public:
+ typedef std::map<CZString, Value> ObjectValues;
+
+ public:
+ Value (ValueType type = nullValue);
+ Value (int value);
+ Value (unsigned value);
+ Value (double value);
+ Value (char const *value);
+ Value (const StaticString &value);
+ Value (const std::string &value);
+ Value (bool value);
+ Value (const Value &other);
+ ~Value ();
+
+ Value &operator = (const Value &other);
+
+ void swap (Value &other);
+
+ ValueType type () const;
+
+ bool operator < (const Value &other) const;
+ bool operator <= (const Value &other) const;
+ bool operator >= (const Value &other) const;
+ bool operator > (const Value &other) const;
+
+ bool operator == (const Value &other) const;
+ bool operator != (const Value &other) const;
+
+ operator char const * () const;
+ operator std::string () const;
+ operator int () const;
+ operator unsigned () const;
+ operator double () const;
+ operator bool () const;
+
+ bool isNull () const;
+ bool isBool () const;
+ bool isInt () const;
+ bool isUInt () const;
+ bool isIntegral () const;
+ bool isDouble () const;
+ bool isNumeric () const;
+ bool isString () const;
+ bool isArray () const;
+ bool isObject () const;
+
+ bool isConvertibleTo (ValueType other) const;
+
+ unsigned size () const;
+
+ bool empty () const;
+
+ bool operator ! () const;
+
+ void clear ();
+
+ void resize (unsigned size);
+
+ Value &operator [] (int index);
+ Value &operator [] (unsigned index);
+ const Value &operator [] (int index) const;
+ const Value &operator [] (unsigned index) const;
+ Value get (int index, const Value &defaultValue) const;
+ Value get (unsigned index, const Value &defaultValue) const;
+ bool isValidIndex (int index) const;
+ bool isValidIndex (unsigned index) const;
+
+ Value &append (const Value &value);
+
+ Value &operator [] (char const *key);
+ const Value &operator [] (char const *key) const;
+ Value &operator [] (const std::string &key);
+ const Value &operator [] (const std::string &key) const;
+ Value &operator [] (const StaticString &key);
+ Value get (char const *key, const Value &defaultValue) const;
+ Value get (const std::string &key, const Value &defaultValue) const;
+ Value removeMember (char const* key);
+ Value removeMember (const std::string &key);
+
+ bool isMember (char const *key) const;
+ bool isMember (const std::string &key) const;
+
+ Members getMemberNames () const;
+
+ const_iterator begin () const;
+ const_iterator end () const;
+
+ iterator begin ();
+ iterator end ();
+
+ private:
+ Value &resolveReference (char const *key, bool isStatic);
+
+ struct MemberNamesTransform
+ {
+ typedef char const *result_type;
+ char const *operator () (const CZString &name) const
+ {
+ return name.c_str ();
+ }
+ };
+
+ union ValueHolder
+ {
+ int int_;
+ unsigned uint_;
+ double real_;
+ bool bool_;
+ char *string_;
+ ObjectValues *map_;
+ } value_;
+
+ ValueType type_ : 8;
+ int allocated_ : 1;
+ };
+
+
+ class ValueAllocator
+ {
+ public:
+ enum { unknown = (unsigned)-1 };
+
+ virtual ~ValueAllocator ();
+
+ virtual char *makeMemberName (char const *memberName) = 0;
+ virtual void releaseMemberName (char *memberName) = 0;
+ virtual char *duplicateStringValue (char const *value,
+ unsigned length = unknown) = 0;
+ virtual void releaseStringValue (char *value) = 0;
+ };
+
+ class ValueIteratorBase
+ {
+ public:
+ typedef unsigned size_t;
+ typedef int difference_type;
+ typedef ValueIteratorBase SelfType;
+
+ ValueIteratorBase ();
+ explicit ValueIteratorBase (const Value::ObjectValues::iterator &current);
+
+ bool operator == (const SelfType &other) const
+ {
+ return isEqual (other);
+ }
+
+ bool operator != (const SelfType &other) const
+ {
+ return !isEqual (other);
+ }
+
+ difference_type operator - (const SelfType &other) const
+ {
+ return computeDistance (other);
+ }
+
+ Value key () const;
+
+ unsigned index () const;
+
+ char const *memberName () const;
+
+ protected:
+ Value &deref () const;
+
+ void increment ();
+
+ void decrement ();
+
+ difference_type computeDistance (const SelfType &other) const;
+
+ bool isEqual (const SelfType &other) const;
+
+ void copy (const SelfType &other);
+
+ private:
+ Value::ObjectValues::iterator current_;
+ };
+
+ class ValueConstIterator : public ValueIteratorBase
+ {
+ friend class Value;
+ public:
+ typedef unsigned size_t;
+ typedef int difference_type;
+ typedef const Value &reference;
+ typedef const Value *pointer;
+ typedef ValueConstIterator SelfType;
+
+ ValueConstIterator ();
+ private:
+ explicit ValueConstIterator (const Value::ObjectValues::iterator &current);
+ public:
+ SelfType &operator = (const ValueIteratorBase &other);
+
+ SelfType operator ++ (int)
+ {
+ SelfType temp (*this);
+ ++*this;
+ return temp;
+ }
+
+ SelfType operator -- (int)
+ {
+ SelfType temp (*this);
+ --*this;
+ return temp;
+ }
+
+ SelfType &operator -- ()
+ {
+ decrement ();
+ return *this;
+ }
+
+ SelfType &operator ++ ()
+ {
+ increment ();
+ return *this;
+ }
+
+ reference operator * () const
+ {
+ return deref ();
+ }
+ };
+
+
+ class ValueIterator : public ValueIteratorBase
+ {
+ friend class Value;
+ public:
+ typedef unsigned size_t;
+ typedef int difference_type;
+ typedef Value &reference;
+ typedef Value *pointer;
+ typedef ValueIterator SelfType;
+
+ ValueIterator ();
+ ValueIterator (const ValueConstIterator &other);
+ ValueIterator (const ValueIterator &other);
+
+ private:
+ explicit ValueIterator (const Value::ObjectValues::iterator &current);
+
+ public:
+
+ SelfType &operator = (const SelfType &other);
+
+ SelfType operator ++ (int)
+ {
+ SelfType temp (*this);
+ ++*this;
+ return temp;
+ }
+
+ SelfType operator -- (int)
+ {
+ SelfType temp (*this);
+ --*this;
+ return temp;
+ }
+
+ SelfType &operator -- ()
+ {
+ decrement ();
+ return *this;
+ }
+
+ SelfType &operator ++ ()
+ {
+ increment ();
+ return *this;
+ }
+
+ reference operator * () const
+ {
+ return deref ();
+ }
+ };
+} // namespace json
+
+namespace json
+{
+ class Value;
+
+ class Reader
+ {
+ public:
+ typedef char const *Location;
+
+ Reader ();
+
+ bool parse (const std::string &document, Value &root);
+ bool parse (char const *beginDoc, char const *endDOc, Value &root);
+ bool parse (std::istream &, Value &root);
+
+ std::string error_msgs () const;
+
+ private:
+ enum TokenType
+ {
+ tokenEndOfStream = 0,
+ tokenObjectBegin,
+ tokenObjectEnd,
+ tokenArrayBegin,
+ tokenArrayEnd,
+ tokenString,
+ tokenNumber,
+ tokenTrue,
+ tokenFalse,
+ tokenNull,
+ tokenArraySeparator,
+ tokenMemberSeparator,
+ tokenComment,
+ tokenError
+ };
+
+ class Token
+ {
+ public:
+ TokenType type_;
+ Location start_;
+ Location end_;
+ };
+
+ class ErrorInfo
+ {
+ public:
+ Token token_;
+ std::string message_;
+ Location extra_;
+ };
+
+ typedef std::deque<ErrorInfo> Errors;
+
+ bool expectToken (TokenType type, Token &token, char const *message);
+ bool readToken (Token &token);
+ void skipSpaces ();
+ bool match (Location pattern,
+ int patternLength);
+ bool readString ();
+ void readNumber ();
+ bool readValue ();
+ bool readObject ();
+ bool readArray ();
+ bool decodeNumber (Token &token);
+ bool decodeString (Token &token);
+ bool decodeString (Token &token, std::string &decoded);
+ bool decodeDouble (Token &token);
+ bool decodeUnicodeEscapeSequence (Token &token,
+ Location &current,
+ Location end,
+ unsigned &unicode);
+ bool addError (const std::string &message,
+ Token &token,
+ Location extra = 0);
+ bool recoverFromError (TokenType skipUntilToken);
+ bool addErrorAndRecover (const std::string &message,
+ Token &token,
+ TokenType skipUntilToken);
+ void skipUntilSpace ();
+ Value &currentValue ();
+ char getNextChar ();
+ void getLocationLineAndColumn (Location location,
+ int &line,
+ int &column) const;
+ std::string getLocationLineAndColumn (Location location) const;
+
+ typedef std::stack<Value *> Nodes;
+ Nodes nodes_;
+ Errors errors_;
+ std::string document_;
+ Location begin_;
+ Location end_;
+ Location current_;
+ Location lastValueEnd_;
+ Value *lastValue_;
+ };
+
+ std::istream& operator >> (std::istream&, Value&);
+
+} // namespace json
+
+namespace json
+{
+ class Value;
+
+ class Writer
+ {
+ public:
+ Writer () { }
+ ~Writer () { }
+
+ public:
+ std::string write (const Value &root);
+
+ private:
+ void writeValue (const Value &value);
+
+ std::string document_;
+ };
+
+ std::string valueToString (int value);
+ std::string valueToString (unsigned value);
+ std::string valueToString (double value);
+ std::string valueToString (bool value);
+ std::string valueToQuotedString (char const *value);
+} // namespace json
+
+/*
+ * JSON-RPC implementation in C++
+ */
+
+namespace json
+{
+ namespace rpc
+ {
+ typedef void (*method) (HTTPRequest *http, Value &request, Value &response);
+ void init (void);
+ void add_method (char *name, method mth);
+ void service (HTTPRequest *http, Value &request, Value &response);
+ void process (HTTPRequest *http, std::string &response, char const *request);
+ }
+}
+
+#endif // JSON_H
diff --git a/src/modules/m_rpc_json.cpp b/src/modules/m_rpc_json.cpp
new file mode 100644
index 000000000..6d9b024f9
--- /dev/null
+++ b/src/modules/m_rpc_json.cpp
@@ -0,0 +1,2109 @@
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2007 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include <cstddef>
+#include <cstdio>
+#include <iostream>
+#include <stdexcept>
+#include <utility>
+
+#include "inspircd.h"
+#include "users.h"
+#include "channels.h"
+#include "configreader.h"
+#include "modules.h"
+#include "inspsocket.h"
+#include "httpd.h"
+#include "json.h"
+
+/* $ModDesc: Provides a JSON-RPC interface for modules using m_httpd.so */
+
+class ModuleRpcJson : public Module
+{
+ public:
+ ModuleRpcJson(InspIRCd* Me) : Module(Me)
+ {
+ json::rpc::init ();
+ }
+
+ void OnEvent(Event* event)
+ {
+ std::stringstream data("");
+
+ if (event->GetEventID() == "httpd_url")
+ {
+ HTTPRequest* http = (HTTPRequest*)event->GetData();
+
+ if (http->GetURI() == "/jsonrpc" && http->GetType() == "POST")
+ {
+ std::string response_text;
+ json::rpc::process (http, response_text, http->GetPostData().c_str());
+ data << response_text;
+
+ /* Send the document back to m_httpd */
+ HTTPDocument response(http->sock, &data, 200, "X-Powered-By: m_rpc_json.so\r\n"
+ "Content-Type: application/json; charset=iso-8859-1\r\n");
+ Request req((char*)&response, (Module*)this, event->GetSource());
+ req.Send();
+ }
+ }
+ }
+
+ void Implements(char* List)
+ {
+ List[I_OnEvent] = 1;
+ }
+
+ virtual ~ModuleRpcJson()
+ {
+ }
+
+ virtual Version GetVersion()
+ {
+ return Version(0, 1, 0, 0, VF_VENDOR, API_VERSION);
+ }
+};
+
+static void
+unreachable_internal (char const *file, int line, char const *function)
+{
+ char buf[1024];
+ snprintf (buf, 1024, "%s (%d) [%s] critical: Unreachable line reached.",
+ file, line, function);
+
+ throw std::runtime_error (buf);
+}
+
+static void
+throw_unless_internal (char const *file, int line, char const *function, char const *condition)
+{
+ char buf[1024];
+ snprintf (buf, 1024, "%s (%d) [%s] critical: Assertion `%s' failed.",
+ file, line, function, condition);
+
+ throw std::runtime_error (buf);
+}
+
+static void
+throw_msg_unless_internal (char const *file, int line, char const *function, char const *message)
+{
+ char buf[1024];
+ snprintf (buf, 1024, "%s (%d) [%s] critical: %s.",
+ file, line, function, message);
+
+ throw std::runtime_error (buf);
+}
+
+
+namespace json
+{
+ ValueIteratorBase::ValueIteratorBase ()
+ {
+ }
+
+
+ ValueIteratorBase::ValueIteratorBase (const Value::ObjectValues::iterator &current)
+ : current_ (current)
+ {
+ }
+
+ Value &
+ ValueIteratorBase::deref () const
+ {
+ return current_->second;
+ }
+
+
+ void
+ ValueIteratorBase::increment ()
+ {
+ ++current_;
+ }
+
+
+ void
+ ValueIteratorBase::decrement ()
+ {
+ --current_;
+ }
+
+
+ ValueIteratorBase::difference_type
+ ValueIteratorBase::computeDistance (const SelfType &other) const
+ {
+ return difference_type (std::distance (current_, other.current_));
+ }
+
+
+ bool
+ ValueIteratorBase::isEqual (const SelfType &other) const
+ {
+ return current_ == other.current_;
+ }
+
+
+ void
+ ValueIteratorBase::copy (const SelfType &other)
+ {
+ current_ = other.current_;
+ }
+
+
+ Value
+ ValueIteratorBase::key () const
+ {
+ const Value::CZString czstring = (*current_).first;
+ if (czstring.c_str ())
+ {
+ if (czstring.isStaticString ())
+ return Value (StaticString (czstring.c_str ()));
+ return Value (czstring.c_str ());
+ }
+ return Value (czstring.index ());
+ }
+
+
+ unsigned
+ ValueIteratorBase::index () const
+ {
+ const Value::CZString czstring = (*current_).first;
+ if (!czstring.c_str ())
+ return czstring.index ();
+ return unsigned (-1);
+ }
+
+
+ char const *
+ ValueIteratorBase::memberName () const
+ {
+ char const *name = (*current_).first.c_str ();
+ return name ? name : "";
+ }
+
+
+ ValueConstIterator::ValueConstIterator ()
+ {
+ }
+
+
+ ValueConstIterator::ValueConstIterator (const Value::ObjectValues::iterator &current)
+ : ValueIteratorBase (current)
+ {
+ }
+
+ ValueConstIterator &
+ ValueConstIterator::operator = (const ValueIteratorBase &other)
+ {
+ copy (other);
+ return *this;
+ }
+
+
+ ValueIterator::ValueIterator ()
+ {
+ }
+
+
+ ValueIterator::ValueIterator (const Value::ObjectValues::iterator &current)
+ : ValueIteratorBase (current)
+ {
+ }
+
+ ValueIterator::ValueIterator (const ValueConstIterator &other)
+ : ValueIteratorBase (other)
+ {
+ }
+
+ ValueIterator::ValueIterator (const ValueIterator &other)
+ : ValueIteratorBase (other)
+ {
+ }
+
+ ValueIterator &
+ ValueIterator::operator = (const SelfType &other)
+ {
+ copy (other);
+ return *this;
+ }
+}
+
+namespace json
+{
+ inline bool
+ in (char c, char c1, char c2, char c3, char c4)
+ {
+ return c == c1 || c == c2 || c == c3 || c == c4;
+ }
+
+ inline bool
+ in (char c, char c1, char c2, char c3, char c4, char c5)
+ {
+ return c == c1 || c == c2 || c == c3 || c == c4 || c == c5;
+ }
+
+
+ Reader::Reader ()
+ {
+ }
+
+ bool
+ Reader::parse (const std::string &document,
+ Value &root)
+ {
+ document_ = document;
+ char const *begin = document_.c_str ();
+ char const *end = begin + document_.length ();
+ return parse (begin, end, root);
+ }
+
+ bool
+ Reader::parse (std::istream& sin,
+ Value &root)
+ {
+ std::string doc;
+ std::getline (sin, doc, (char)EOF);
+ return parse (doc, root);
+ }
+
+ bool
+ Reader::parse (char const *beginDoc, char const *endDOc,
+ Value &root)
+ {
+ begin_ = beginDoc;
+ end_ = endDOc;
+ current_ = begin_;
+ lastValueEnd_ = 0;
+ lastValue_ = 0;
+ errors_.clear ();
+ while (!nodes_.empty ())
+ nodes_.pop ();
+ nodes_.push (&root);
+
+ bool successful = readValue ();
+ return successful;
+ }
+
+
+ bool
+ Reader::readValue ()
+ {
+ Token token;
+ do
+ readToken (token);
+ while (token.type_ == tokenComment);
+ bool successful = true;
+
+ switch (token.type_)
+ {
+ case tokenObjectBegin:
+ successful = readObject ();
+ break;
+ case tokenArrayBegin:
+ successful = readArray ();
+ break;
+ case tokenNumber:
+ successful = decodeNumber (token);
+ break;
+ case tokenString:
+ successful = decodeString (token);
+ break;
+ case tokenTrue:
+ currentValue () = true;
+ break;
+ case tokenFalse:
+ currentValue () = false;
+ break;
+ case tokenNull:
+ currentValue () = Value ();
+ break;
+ default:
+ return addError ("Syntax error: value, object or array expected.", token);
+ }
+
+ return successful;
+ }
+
+
+ bool
+ Reader::expectToken (TokenType type, Token &token, char const *message)
+ {
+ readToken (token);
+ if (token.type_ != type)
+ return addError (message, token);
+ return true;
+ }
+
+
+ bool
+ Reader::readToken (Token &token)
+ {
+ skipSpaces ();
+ token.start_ = current_;
+ char c = getNextChar ();
+ bool ok = true;
+ switch (c)
+ {
+ case '{':
+ token.type_ = tokenObjectBegin;
+ break;
+ case '}':
+ token.type_ = tokenObjectEnd;
+ break;
+ case '[':
+ token.type_ = tokenArrayBegin;
+ break;
+ case ']':
+ token.type_ = tokenArrayEnd;
+ break;
+ case '"':
+ token.type_ = tokenString;
+ ok = readString ();
+ break;
+#if 0
+#ifdef __GNUC__
+ case '0'...'9':
+#endif
+#else
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9':
+#endif
+ case '-':
+ token.type_ = tokenNumber;
+ readNumber ();
+ break;
+ case 't':
+ token.type_ = tokenTrue;
+ ok = match ("rue", 3);
+ break;
+ case 'f':
+ token.type_ = tokenFalse;
+ ok = match ("alse", 4);
+ break;
+ case 'n':
+ token.type_ = tokenNull;
+ ok = match ("ull", 3);
+ break;
+ case ',':
+ token.type_ = tokenArraySeparator;
+ break;
+ case ':':
+ token.type_ = tokenMemberSeparator;
+ break;
+ case 0:
+ token.type_ = tokenEndOfStream;
+ break;
+ default:
+ ok = false;
+ break;
+ }
+ if (!ok)
+ token.type_ = tokenError;
+ token.end_ = current_;
+ return true;
+ }
+
+
+ void
+ Reader::skipSpaces ()
+ {
+ while (current_ != end_)
+ {
+ char c = *current_;
+ if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
+ ++current_;
+ else
+ break;
+ }
+ }
+
+
+ bool
+ Reader::match (Location pattern, int patternLength)
+ {
+ if (end_ - current_ < patternLength)
+ return false;
+ int index = patternLength;
+ while (index--)
+ if (current_[index] != pattern[index])
+ return false;
+ current_ += patternLength;
+ return true;
+ }
+
+
+ void
+ Reader::readNumber ()
+ {
+ while (current_ != end_)
+ {
+ if (!(*current_ >= '0' && *current_ <= '9') &&
+ !in (*current_, '.', 'e', 'E', '+', '-'))
+ break;
+ ++current_;
+ }
+ }
+
+ bool
+ Reader::readString ()
+ {
+ char c = 0;
+ while (current_ != end_)
+ {
+ c = getNextChar ();
+ if (c == '\\')
+ getNextChar ();
+ else if (c == '"')
+ break;
+ }
+ return c == '"';
+ }
+
+
+ bool
+ Reader::readObject ()
+ {
+ Token tokenName;
+ std::string name;
+ currentValue () = Value (objectValue);
+ while (readToken (tokenName))
+ {
+ if (tokenName.type_ == tokenObjectEnd && name.empty ()) // empty object
+ return true;
+ if (tokenName.type_ != tokenString)
+ break;
+
+ name = "";
+ if (!decodeString (tokenName, name))
+ return recoverFromError (tokenObjectEnd);
+
+ Token colon;
+ if (!readToken (colon) || colon.type_ != tokenMemberSeparator)
+ {
+ return addErrorAndRecover ("Missing ':' after object member name",
+ colon,
+ tokenObjectEnd);
+ }
+ Value &value = currentValue ()[ name ];
+ nodes_.push (&value);
+ bool ok = readValue ();
+ nodes_.pop ();
+ if (!ok) // error already set
+ return recoverFromError (tokenObjectEnd);
+
+ Token comma;
+ if (!readToken (comma)
+ || (comma.type_ != tokenObjectEnd &&
+ comma.type_ != tokenArraySeparator))
+ {
+ return addErrorAndRecover ("Missing ',' or '}' in object declaration",
+ comma,
+ tokenObjectEnd);
+ }
+ if (comma.type_ == tokenObjectEnd)
+ return true;
+ }
+ return addErrorAndRecover ("Missing '}' or object member name",
+ tokenName,
+ tokenObjectEnd);
+ }
+
+
+ bool
+ Reader::readArray ()
+ {
+ currentValue () = Value (arrayValue);
+ skipSpaces ();
+ if (*current_ == ']') // empty array
+ {
+ Token endArray;
+ readToken (endArray);
+ return true;
+ }
+ int index = 0;
+ while (true)
+ {
+ Value &value = currentValue ()[ index++ ];
+ nodes_.push (&value);
+ bool ok = readValue ();
+ nodes_.pop ();
+ if (!ok) // error already set
+ return recoverFromError (tokenArrayEnd);
+
+ Token token;
+ if (!readToken (token)
+ || (token.type_ != tokenArraySeparator &&
+ token.type_ != tokenArrayEnd))
+ {
+ return addErrorAndRecover ("Missing ',' or ']' in array declaration",
+ token,
+ tokenArrayEnd);
+ }
+ if (token.type_ == tokenArrayEnd)
+ break;
+ }
+ return true;
+ }
+
+
+ bool
+ Reader::decodeNumber (Token &token)
+ {
+ bool isDouble = false;
+ for (Location inspect = token.start_; inspect != token.end_; ++inspect)
+ {
+ isDouble = isDouble
+ || in (*inspect, '.', 'e', 'E', '+')
+ || (*inspect == '-' && inspect != token.start_);
+ }
+ if (isDouble)
+ return decodeDouble (token);
+ Location current = token.start_;
+ bool isNegative = *current == '-';
+ if (isNegative)
+ ++current;
+ unsigned threshold = (isNegative ? unsigned (-Value::minInt)
+ : Value::maxUInt) / 10;
+ unsigned value = 0;
+ while (current < token.end_)
+ {
+ char c = *current++;
+ if (c < '0' || c > '9')
+ return addError ("'" + std::string (token.start_, token.end_) + "' is not a number.", token);
+ if (value >= threshold)
+ return decodeDouble (token);
+ value = value * 10 + unsigned (c - '0');
+ }
+ if (isNegative)
+ currentValue () = -int (value);
+ else if (value <= unsigned (Value::maxInt))
+ currentValue () = int (value);
+ else
+ currentValue () = value;
+ return true;
+ }
+
+
+ bool
+ Reader::decodeDouble (Token &token)
+ {
+ double value = 0;
+ const int bufferSize = 32;
+ int count;
+ int length = int (token.end_ - token.start_);
+ if (length <= bufferSize)
+ {
+ char buffer[bufferSize];
+ memcpy (buffer, token.start_, length);
+ buffer[length] = 0;
+ count = sscanf (buffer, "%lf", &value);
+ }
+ else
+ {
+ std::string buffer (token.start_, token.end_);
+ count = sscanf (buffer.c_str (), "%lf", &value);
+ }
+
+ if (count != 1)
+ return addError ("'" + std::string (token.start_, token.end_) + "' is not a number.", token);
+ currentValue () = value;
+ return true;
+ }
+
+
+ bool
+ Reader::decodeString (Token &token)
+ {
+ std::string decoded;
+ if (!decodeString (token, decoded))
+ return false;
+ currentValue () = decoded;
+ return true;
+ }
+
+
+ bool
+ Reader::decodeString (Token &token, std::string &decoded)
+ {
+ Location current = token.start_ + 1; // skip '"'
+ Location end = token.end_ - 1; // do not include '"'
+ decoded.reserve (long (end - current));
+
+ while (current != end)
+ {
+ char c = *current++;
+ if (expect_false (c == '"'))
+ break;
+ else if (expect_false (c == '\\'))
+ {
+ if (expect_false (current == end))
+ return addError ("Empty escape sequence in string", token, current);
+ char escape = *current++;
+ switch (escape)
+ {
+ case '"':
+ case '/':
+ case '\\': decoded += escape; break;
+
+ case 'b': decoded += '\010'; break;
+ case 't': decoded += '\011'; break;
+ case 'n': decoded += '\012'; break;
+ case 'f': decoded += '\014'; break;
+ case 'r': decoded += '\015'; break;
+ case 'u':
+ {
+ unsigned unicode;
+ if (!decodeUnicodeEscapeSequence (token, current, end, unicode))
+ return false;
+ // @todo encode unicode as utf8.
+ // @todo remember to alter the writer too.
+ }
+ break;
+ default:
+ return addError ("Bad escape sequence in string", token, current);
+ }
+ }
+ else
+ {
+ decoded += c;
+ }
+ }
+
+ return true;
+ }
+
+
+ bool
+ Reader::decodeUnicodeEscapeSequence (Token &token,
+ Location &current,
+ Location end,
+ unsigned &unicode)
+ {
+ if (end - current < 4)
+ return addError ("Bad unicode escape sequence in string: four digits expected.", token, current);
+ unicode = 0;
+ for (int index = 0; index < 4; ++index)
+ {
+ char c = *current++;
+ unicode *= 16;
+ if (c >= '0' && c <= '9')
+ unicode += c - '0';
+ else if (c >= 'a' && c <= 'f')
+ unicode += c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ unicode += c - 'A' + 10;
+ else
+ return addError ("Bad unicode escape sequence in string: hexadecimal digit expected.", token, current);
+ }
+ return true;
+ }
+
+
+ bool
+ Reader::addError (const std::string &message,
+ Token &token,
+ Location extra)
+ {
+ ErrorInfo info;
+ info.token_ = token;
+ info.message_ = message;
+ info.extra_ = extra;
+ errors_.push_back (info);
+ return false;
+ }
+
+
+ bool
+ Reader::recoverFromError (TokenType skipUntilToken)
+ {
+ int errorCount = int (errors_.size ());
+ Token skip;
+ while (true)
+ {
+ if (!readToken (skip))
+ errors_.resize (errorCount); // discard errors caused by recovery
+ if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
+ break;
+ }
+ errors_.resize (errorCount);
+ return false;
+ }
+
+
+ bool
+ Reader::addErrorAndRecover (const std::string &message,
+ Token &token,
+ TokenType skipUntilToken)
+ {
+ addError (message, token);
+ return recoverFromError (skipUntilToken);
+ }
+
+
+ Value &
+ Reader::currentValue ()
+ {
+ return *(nodes_.top ());
+ }
+
+
+ char
+ Reader::getNextChar ()
+ {
+ if (current_ == end_)
+ return 0;
+ return *current_++;
+ }
+
+
+ void
+ Reader::getLocationLineAndColumn (Location location,
+ int &line,
+ int &column) const
+ {
+ Location current = begin_;
+ Location lastLineStart = current;
+ line = 0;
+ while (current < location && current != end_)
+ {
+ char c = *current++;
+ if (c == '\r')
+ {
+ if (*current == '\n')
+ ++current;
+ lastLineStart = current;
+ ++line;
+ }
+ else if (c == '\n')
+ {
+ lastLineStart = current;
+ ++line;
+ }
+ }
+ // column & line start at 1
+ column = int (location - lastLineStart) + 1;
+ ++line;
+ }
+
+
+ std::string
+ Reader::getLocationLineAndColumn (Location location) const
+ {
+ int line, column;
+ getLocationLineAndColumn (location, line, column);
+ char buffer[18+16+16+1];
+ sprintf (buffer, "Line %d, Column %d", line, column);
+ return buffer;
+ }
+
+
+ std::string
+ Reader::error_msgs () const
+ {
+ std::string formattedMessage;
+ for (Errors::const_iterator itError = errors_.begin ();
+ itError != errors_.end ();
+ ++itError)
+ {
+ const ErrorInfo &error = *itError;
+ formattedMessage += "* " + getLocationLineAndColumn (error.token_.start_) + "\n";
+ formattedMessage += " " + error.message_ + "\n";
+ if (error.extra_)
+ formattedMessage += "See " + getLocationLineAndColumn (error.extra_) + " for detail.\n";
+ }
+ return formattedMessage;
+ }
+} // namespace json
+
+namespace json
+{
+ void
+ unreachable_internal (char const *file, int line, char const *function)
+ {
+ char buf[1024];
+ snprintf (buf, 1024, "%s (%d) [%s] critical: Unreachable line reached.",
+ file, line, function);
+
+ throw std::runtime_error (buf);
+ }
+
+ void
+ throw_unless_internal (char const *file, int line, char const *function, char const *condition)
+ {
+ char buf[1024];
+ snprintf (buf, 1024, "%s (%d) [%s] critical: Assertion `%s' failed.",
+ file, line, function, condition);
+
+ throw std::runtime_error (buf);
+ }
+
+ void
+ throw_msg_unless_internal (char const *file, int line, char const *function, char const *message)
+ {
+ char buf[1024];
+ snprintf (buf, 1024, "%s (%d) [%s] critical: %s.",
+ file, line, function, message);
+
+ throw std::runtime_error (buf);
+ }
+
+ const Value Value::null;
+ const int Value::minInt = int (~ (unsigned (-1)/2));
+ const int Value::maxInt = int (unsigned (-1)/2);
+ const unsigned Value::maxUInt = unsigned (-1);
+
+ ValueAllocator::~ValueAllocator ()
+ {
+ }
+
+ class DefaultValueAllocator : public ValueAllocator
+ {
+ public:
+ virtual ~DefaultValueAllocator ()
+ {
+ }
+
+ virtual char *makeMemberName (char const *memberName)
+ {
+ return duplicateStringValue (memberName);
+ }
+
+ virtual void releaseMemberName (char *memberName)
+ {
+ releaseStringValue (memberName);
+ }
+
+ virtual char *duplicateStringValue (char const *value, unsigned length = unknown)
+ {
+ //@todo invesgate this old optimization
+#if 0
+ if (!value || value[0] == 0)
+ return 0;
+#endif
+
+ if (length == unknown)
+ length = (unsigned)strlen (value);
+ char *newString = static_cast<char *> (malloc (length + 1));
+ memcpy (newString, value, length);
+ newString[length] = 0;
+ return newString;
+ }
+
+ virtual void releaseStringValue (char *value)
+ {
+ if (value)
+ free (value);
+ }
+ };
+
+ static ValueAllocator *&valueAllocator ()
+ {
+ static DefaultValueAllocator defaultAllocator;
+ static ValueAllocator *valueAllocator = &defaultAllocator;
+ return valueAllocator;
+ }
+
+ static struct DummyValueAllocatorInitializer {
+ DummyValueAllocatorInitializer ()
+ {
+ valueAllocator (); // ensure valueAllocator () statics are initialized before main ().
+ }
+ } dummyValueAllocatorInitializer;
+
+
+
+ Value::CZString::CZString (int index)
+ : cstr_ (0)
+ , index_ (index)
+ {
+ }
+
+ Value::CZString::CZString (char const *cstr, DuplicationPolicy allocate)
+ : cstr_ (allocate == duplicate ? valueAllocator()->makeMemberName (cstr)
+ : cstr)
+ , index_ (allocate)
+ {
+ }
+
+ Value::CZString::CZString (const CZString &other)
+ : cstr_ (other.index_ != noDuplication && other.cstr_ != 0
+ ? valueAllocator()->makeMemberName (other.cstr_)
+ : other.cstr_)
+ , index_ (other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate)
+ : other.index_)
+ {
+ }
+
+ Value::CZString::~CZString ()
+ {
+ if (cstr_ && index_ == duplicate)
+ valueAllocator()->releaseMemberName (const_cast<char *> (cstr_));
+ }
+
+ void
+ Value::CZString::swap (CZString &other)
+ {
+ std::swap (cstr_, other.cstr_);
+ std::swap (index_, other.index_);
+ }
+
+ Value::CZString &
+ Value::CZString::operator = (const CZString &other)
+ {
+ CZString temp (other);
+ swap (temp);
+ return *this;
+ }
+
+ bool
+ Value::CZString::operator < (const CZString &other) const
+ {
+ if (cstr_)
+ return strcmp (cstr_, other.cstr_) < 0;
+ return index_ < other.index_;
+ }
+
+ bool
+ Value::CZString::operator == (const CZString &other) const
+ {
+ if (cstr_)
+ return strcmp (cstr_, other.cstr_) == 0;
+ return index_ == other.index_;
+ }
+
+
+ int
+ Value::CZString::index () const
+ {
+ return index_;
+ }
+
+
+ char const *
+ Value::CZString::c_str () const
+ {
+ return cstr_;
+ }
+
+ bool
+ Value::CZString::isStaticString () const
+ {
+ return index_ == noDuplication;
+ }
+
+
+ // class Value::Value
+
+ Value::Value (ValueType type)
+ : type_ (type)
+ , allocated_ (0)
+ {
+ switch (type)
+ {
+ case nullValue:
+ break;
+ case intValue:
+ case uintValue:
+ value_.int_ = 0;
+ break;
+ case realValue:
+ value_.real_ = 0.0;
+ break;
+ case stringValue:
+ value_.string_ = 0;
+ break;
+ case arrayValue:
+ case objectValue:
+ value_.map_ = new ObjectValues ();
+ break;
+ case booleanValue:
+ value_.bool_ = false;
+ break;
+ default:
+ throw_unreachable;
+ }
+ }
+
+
+ Value::Value (int value)
+ : type_ (intValue)
+ {
+ value_.int_ = value;
+ }
+
+
+ Value::Value (unsigned value)
+ : type_ (uintValue)
+ {
+ value_.uint_ = value;
+ }
+
+ Value::Value (double value)
+ : type_ (realValue)
+ {
+ value_.real_ = value;
+ }
+
+ Value::Value (char const *value)
+ : type_ (stringValue)
+ , allocated_ (true)
+ {
+ value_.string_ = valueAllocator()->duplicateStringValue (value);
+ }
+
+ Value::Value (const std::string &value)
+ : type_ (stringValue)
+ , allocated_ (true)
+ {
+ value_.string_ = valueAllocator()->duplicateStringValue (value.c_str (), (unsigned)value.length ());
+ }
+
+ Value::Value (const StaticString &value)
+ : type_ (stringValue)
+ , allocated_ (false)
+ {
+ value_.string_ = const_cast<char *> (value.c_str ());
+ }
+
+
+ Value::Value (bool value)
+ : type_ (booleanValue)
+ {
+ value_.bool_ = value;
+ }
+
+
+ Value::Value (const Value &other)
+ : type_ (other.type_)
+ {
+ switch (type_)
+ {
+ case nullValue:
+ case intValue:
+ case uintValue:
+ case realValue:
+ case booleanValue:
+ value_ = other.value_;
+ break;
+ case stringValue:
+ if (other.value_.string_)
+ {
+ value_.string_ = valueAllocator()->duplicateStringValue (other.value_.string_);
+ allocated_ = true;
+ }
+ else
+ value_.string_ = 0;
+ break;
+ case arrayValue:
+ case objectValue:
+ value_.map_ = new ObjectValues (*other.value_.map_);
+ break;
+ default:
+ throw_unreachable;
+ }
+ }
+
+
+ Value::~Value ()
+ {
+ switch (type_)
+ {
+ case nullValue:
+ case intValue:
+ case uintValue:
+ case realValue:
+ case booleanValue:
+ break;
+ case stringValue:
+ if (allocated_)
+ valueAllocator()->releaseStringValue (value_.string_);
+ break;
+ case arrayValue:
+ case objectValue:
+ delete value_.map_;
+ break;
+ default:
+ throw_unreachable;
+ }
+ }
+
+ Value &
+ Value::operator = (const Value &other)
+ {
+ Value temp (other);
+ swap (temp);
+ return *this;
+ }
+
+ void
+ Value::swap (Value &other)
+ {
+ ValueType temp = type_;
+ type_ = other.type_;
+ other.type_ = temp;
+ std::swap (value_, other.value_);
+ int temp2 = allocated_;
+ allocated_ = other.allocated_;
+ other.allocated_ = temp2;
+ }
+
+ ValueType
+ Value::type () const
+ {
+ return type_;
+ }
+
+ bool
+ Value::operator < (const Value &other) const
+ {
+ int typeDelta = type_ - other.type_;
+ if (typeDelta)
+ return typeDelta < 0 ? true : false;
+ switch (type_)
+ {
+ case nullValue:
+ return false;
+ case intValue:
+ return value_.int_ < other.value_.int_;
+ case uintValue:
+ return value_.uint_ < other.value_.uint_;
+ case realValue:
+ return value_.real_ < other.value_.real_;
+ case booleanValue:
+ return value_.bool_ < other.value_.bool_;
+ case stringValue:
+ return (value_.string_ == 0 && other.value_.string_)
+ || (other.value_.string_
+ && value_.string_
+ && strcmp (value_.string_, other.value_.string_) < 0);
+ case arrayValue:
+ case objectValue:
+ {
+ int delta = int (value_.map_->size () - other.value_.map_->size ());
+ if (delta)
+ return delta < 0;
+ return (*value_.map_) < (*other.value_.map_);
+ }
+ default:
+ throw_unreachable;
+ }
+ return 0; // unreachable
+ }
+
+ bool
+ Value::operator <= (const Value &other) const
+ {
+ return !(other > *this);
+ }
+
+ bool
+ Value::operator >= (const Value &other) const
+ {
+ return !(*this < other);
+ }
+
+ bool
+ Value::operator > (const Value &other) const
+ {
+ return other < *this;
+ }
+
+ bool
+ Value::operator == (const Value &other) const
+ {
+ if (type_ != other.type_)
+ return false;
+
+ switch (type_)
+ {
+ case nullValue:
+ return true;
+ case intValue:
+ return value_.int_ == other.value_.int_;
+ case uintValue:
+ return value_.uint_ == other.value_.uint_;
+ case realValue:
+ return value_.real_ == other.value_.real_;
+ case booleanValue:
+ return value_.bool_ == other.value_.bool_;
+ case stringValue:
+ return (value_.string_ == other.value_.string_)
+ || (other.value_.string_
+ && value_.string_
+ && strcmp (value_.string_, other.value_.string_) == 0);
+ case arrayValue:
+ case objectValue:
+ return value_.map_->size () == other.value_.map_->size ()
+ && (*value_.map_) == (*other.value_.map_);
+ default:
+ throw_unreachable;
+ }
+ return 0; // unreachable
+ }
+
+ bool
+ Value::operator != (const Value &other) const
+ {
+ return !(*this == other);
+ }
+
+ Value::operator char const * () const
+ {
+ throw_unless (type_ == stringValue);
+ return value_.string_;
+ }
+
+
+ Value::operator std::string () const
+ {
+ switch (type_)
+ {
+ case nullValue:
+ return "";
+ case stringValue:
+ return value_.string_ ? value_.string_ : "";
+ case booleanValue:
+ return value_.bool_ ? "true" : "false";
+ case intValue:
+ case uintValue:
+ case realValue:
+ case arrayValue:
+ case objectValue:
+ throw_msg_unless (false, "Type is not convertible to string");
+ default:
+ throw_unreachable;
+ }
+ return ""; // unreachable
+ }
+
+ Value::operator int () const
+ {
+ switch (type_)
+ {
+ case nullValue:
+ return 0;
+ case intValue:
+ return value_.int_;
+ case uintValue:
+ throw_msg_unless (value_.uint_ < (unsigned)maxInt, "integer out of signed integer range");
+ return value_.uint_;
+ case realValue:
+ throw_msg_unless (value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range");
+ return int (value_.real_);
+ case booleanValue:
+ return value_.bool_ ? 1 : 0;
+ case stringValue:
+ case arrayValue:
+ case objectValue:
+ throw_msg_unless (false, "Type is not convertible to int");
+ default:
+ throw_unreachable;
+ }
+ return 0; // unreachable;
+ }
+
+ Value::operator unsigned () const
+ {
+ switch (type_)
+ {
+ case nullValue:
+ return 0;
+ case intValue:
+ throw_msg_unless (value_.int_ >= 0, "Negative integer can not be converted to unsigned integer");
+ return value_.int_;
+ case uintValue:
+ return value_.uint_;
+ case realValue:
+ throw_msg_unless (value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range");
+ return unsigned (value_.real_);
+ case booleanValue:
+ return value_.bool_ ? 1 : 0;
+ case stringValue:
+ case arrayValue:
+ case objectValue:
+ throw_msg_unless (false, "Type is not convertible to uint");
+ default:
+ throw_unreachable;
+ }
+ return 0; // unreachable;
+ }
+
+ Value::operator double () const
+ {
+ switch (type_)
+ {
+ case nullValue:
+ return 0.0;
+ case intValue:
+ return value_.int_;
+ case uintValue:
+ return value_.uint_;
+ case realValue:
+ return value_.real_;
+ case booleanValue:
+ return value_.bool_ ? 1.0 : 0.0;
+ case stringValue:
+ case arrayValue:
+ case objectValue:
+ throw_msg_unless (false, "Type is not convertible to double");
+ default:
+ throw_unreachable;
+ }
+ return 0; // unreachable;
+ }
+
+ Value::operator bool () const
+ {
+ switch (type_)
+ {
+ case nullValue:
+ return false;
+ case intValue:
+ case uintValue:
+ return value_.int_ != 0;
+ case realValue:
+ return value_.real_ != 0.0;
+ case booleanValue:
+ return value_.bool_;
+ case stringValue:
+ return value_.string_ && value_.string_[0] != 0;
+ case arrayValue:
+ case objectValue:
+ return value_.map_->size () != 0;
+ default:
+ throw_unreachable;
+ }
+ return false; // unreachable;
+ }
+
+
+ bool
+ Value::isConvertibleTo (ValueType other) const
+ {
+ switch (type_)
+ {
+ case nullValue:
+ return true;
+ case intValue:
+ return (other == nullValue && value_.int_ == 0)
+ || other == intValue
+ || (other == uintValue && value_.int_ >= 0)
+ || other == realValue
+ || other == stringValue
+ || other == booleanValue;
+ case uintValue:
+ return (other == nullValue && value_.uint_ == 0)
+ || (other == intValue && value_.uint_ <= (unsigned)maxInt)
+ || other == uintValue
+ || other == realValue
+ || other == stringValue
+ || other == booleanValue;
+ case realValue:
+ return (other == nullValue && value_.real_ == 0.0)
+ || (other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt)
+ || (other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt)
+ || other == realValue
+ || other == stringValue
+ || other == booleanValue;
+ case booleanValue:
+ return (other == nullValue && value_.bool_ == false)
+ || other == intValue
+ || other == uintValue
+ || other == realValue
+ || other == stringValue
+ || other == booleanValue;
+ case stringValue:
+ return other == stringValue
+ || (other == nullValue && (!value_.string_ || value_.string_[0] == 0));
+ case arrayValue:
+ return other == arrayValue
+ || (other == nullValue && value_.map_->size () == 0);
+ case objectValue:
+ return other == objectValue
+ || (other == nullValue && value_.map_->size () == 0);
+ default:
+ throw_unreachable;
+ }
+ return false; // unreachable;
+ }
+
+
+ /// Number of values in array or object
+ unsigned
+ Value::size () const
+ {
+ switch (type_)
+ {
+ case nullValue:
+ case intValue:
+ case uintValue:
+ case realValue:
+ case booleanValue:
+ case stringValue:
+ return 0;
+ case arrayValue: // size of the array is highest index + 1
+ if (!value_.map_->empty ())
+ {
+ ObjectValues::const_iterator itLast = value_.map_->end ();
+ --itLast;
+ return itLast->first.index ()+1;
+ }
+ return 0;
+ case objectValue:
+ return int (value_.map_->size ());
+ default:
+ throw_unreachable;
+ }
+ return 0; // unreachable;
+ }
+
+
+ bool
+ Value::empty () const
+ {
+ if (isNull () || isArray () || isObject ())
+ return size () == 0u;
+ else
+ return false;
+ }
+
+
+ bool
+ Value::operator ! () const
+ {
+ return isNull ();
+ }
+
+
+ void
+ Value::clear ()
+ {
+ throw_unless (type_ == nullValue || type_ == arrayValue || type_ == objectValue);
+
+ switch (type_)
+ {
+ case arrayValue:
+ case objectValue:
+ value_.map_->clear ();
+ break;
+ default:
+ break;
+ }
+ }
+
+ void
+ Value::resize (unsigned newSize)
+ {
+ throw_unless (type_ == nullValue || type_ == arrayValue);
+ if (type_ == nullValue)
+ *this = Value (arrayValue);
+ unsigned oldSize = size ();
+ if (newSize == 0)
+ clear ();
+ else if (newSize > oldSize)
+ (*this)[ newSize - 1 ];
+ else
+ {
+ for (unsigned index = newSize; index < oldSize; ++index)
+ value_.map_->erase (index);
+ throw_unless (size () == newSize);
+ }
+ }
+
+
+ Value &
+ Value::operator [] (int index)
+ {
+ return operator [] (static_cast<unsigned> (index));
+ }
+
+
+ Value &
+ Value::operator [] (unsigned index)
+ {
+ throw_unless (type_ == nullValue || type_ == arrayValue);
+ if (type_ == nullValue)
+ *this = Value (arrayValue);
+ CZString key (index);
+ ObjectValues::iterator it = value_.map_->lower_bound (key);
+ if (it != value_.map_->end () && it->first == key)
+ return it->second;
+
+ ObjectValues::value_type defaultValue (key, null);
+ it = value_.map_->insert (it, defaultValue);
+ return it->second;
+ }
+
+
+ const Value &
+ Value::operator [] (int index) const
+ {
+ return operator [] (static_cast<unsigned> (index));
+ }
+
+
+ const Value &
+ Value::operator [] (unsigned index) const
+ {
+ throw_unless (type_ == nullValue || type_ == arrayValue);
+ if (type_ == nullValue)
+ return null;
+ CZString key (index);
+ ObjectValues::const_iterator it = value_.map_->find (key);
+ if (it == value_.map_->end ())
+ return null;
+ return it->second;
+ }
+
+
+ Value &
+ Value::operator [] (char const *key)
+ {
+ return resolveReference (key, false);
+ }
+
+
+ Value &
+ Value::resolveReference (char const *key, bool isStatic)
+ {
+ throw_unless (type_ == nullValue || type_ == objectValue);
+ if (type_ == nullValue)
+ *this = Value (objectValue);
+ CZString actualKey (key, isStatic ? CZString::noDuplication
+ : CZString::duplicateOnCopy);
+ ObjectValues::iterator it = value_.map_->lower_bound (actualKey);
+ if (it != value_.map_->end () && it->first == actualKey)
+ return it->second;
+
+ ObjectValues::value_type defaultValue (actualKey, null);
+ it = value_.map_->insert (it, defaultValue);
+ Value &value = it->second;
+ return value;
+ }
+
+
+ Value
+ Value::get (int index, const Value &defaultValue) const
+ {
+ return get (static_cast<unsigned> (index), defaultValue);
+ }
+
+
+ Value
+ Value::get (unsigned index, const Value &defaultValue) const
+ {
+ const Value *value = &((*this)[index]);
+ return value == &null ? defaultValue : *value;
+ }
+
+
+ bool
+ Value::isValidIndex (int index) const
+ {
+ return isValidIndex (static_cast<unsigned> (index));
+ }
+
+
+ bool
+ Value::isValidIndex (unsigned index) const
+ {
+ return index < size ();
+ }
+
+
+
+ const Value &
+ Value::operator [] (char const *key) const
+ {
+ throw_unless (type_ == nullValue || type_ == objectValue);
+ if (type_ == nullValue)
+ return null;
+ CZString actualKey (key, CZString::noDuplication);
+ ObjectValues::const_iterator it = value_.map_->find (actualKey);
+ if (it == value_.map_->end ())
+ return null;
+ return it->second;
+ }
+
+
+ Value &
+ Value::operator [] (const std::string &key)
+ {
+ return (*this)[ key.c_str () ];
+ }
+
+
+ const Value &
+ Value::operator [] (const std::string &key) const
+ {
+ return (*this)[ key.c_str () ];
+ }
+
+ Value &
+ Value::operator [] (const StaticString &key)
+ {
+ return resolveReference (key, true);
+ }
+
+
+ Value &
+ Value::append (const Value &value)
+ {
+ return (*this)[size ()] = value;
+ }
+
+
+ Value
+ Value::get (char const *key, const Value &defaultValue) const
+ {
+ const Value *value = &((*this)[key]);
+ return value == &null ? defaultValue : *value;
+ }
+
+
+ Value
+ Value::get (const std::string &key, const Value &defaultValue) const
+ {
+ return get (key.c_str (), defaultValue);
+ }
+
+ Value
+ Value::removeMember (char const *key)
+ {
+ throw_unless (type_ == nullValue || type_ == objectValue);
+ if (type_ == nullValue)
+ return null;
+ CZString actualKey (key, CZString::noDuplication);
+ ObjectValues::iterator it = value_.map_->find (actualKey);
+ if (it == value_.map_->end ())
+ return null;
+ Value old (it->second);
+ value_.map_->erase (it);
+ return old;
+ }
+
+ Value
+ Value::removeMember (const std::string &key)
+ {
+ return removeMember (key.c_str ());
+ }
+
+ bool
+ Value::isMember (char const *key) const
+ {
+ const Value *value = &((*this)[key]);
+ return value != &null;
+ }
+
+
+ bool
+ Value::isMember (const std::string &key) const
+ {
+ return isMember (key.c_str ());
+ }
+
+
+ Value::Members
+ Value::getMemberNames () const
+ {
+ throw_unless (type_ == nullValue || type_ == objectValue);
+ if (type_ == nullValue)
+ return Value::Members ();
+ Members members;
+ members.reserve (value_.map_->size ());
+ ObjectValues::const_iterator it;
+ ObjectValues::const_iterator itEnd = value_.map_->end ();
+ for (it = value_.map_->begin (); it != itEnd; ++it)
+ members.push_back (std::string (it->first.c_str()));
+ return members;
+ }
+
+ bool
+ Value::isNull () const
+ {
+ return type_ == nullValue;
+ }
+
+
+ bool
+ Value::isBool () const
+ {
+ return type_ == booleanValue;
+ }
+
+
+ bool
+ Value::isInt () const
+ {
+ return type_ == intValue;
+ }
+
+
+ bool
+ Value::isUInt () const
+ {
+ return type_ == uintValue;
+ }
+
+
+ bool
+ Value::isIntegral () const
+ {
+ return type_ == intValue
+ || type_ == uintValue
+ || type_ == booleanValue;
+ }
+
+
+ bool
+ Value::isDouble () const
+ {
+ return type_ == realValue;
+ }
+
+
+ bool
+ Value::isNumeric () const
+ {
+ return isIntegral () || isDouble ();
+ }
+
+
+ bool
+ Value::isString () const
+ {
+ return type_ == stringValue;
+ }
+
+
+ bool
+ Value::isArray () const
+ {
+ return type_ == nullValue || type_ == arrayValue;
+ }
+
+
+ bool
+ Value::isObject () const
+ {
+ return type_ == nullValue || type_ == objectValue;
+ }
+
+
+ Value::const_iterator
+ Value::begin () const
+ {
+ switch (type_)
+ {
+ case arrayValue:
+ case objectValue:
+ if (value_.map_)
+ return const_iterator (value_.map_->begin ());
+ break;
+ default:
+ break;
+ }
+ return const_iterator ();
+ }
+
+ Value::const_iterator
+ Value::end () const
+ {
+ switch (type_)
+ {
+ case arrayValue:
+ case objectValue:
+ if (value_.map_)
+ return const_iterator (value_.map_->end ());
+ break;
+ default:
+ break;
+ }
+ return const_iterator ();
+ }
+
+
+ Value::iterator
+ Value::begin ()
+ {
+ switch (type_)
+ {
+ case arrayValue:
+ case objectValue:
+ if (value_.map_)
+ return iterator (value_.map_->begin ());
+ break;
+ default:
+ break;
+ }
+ return iterator ();
+ }
+
+ Value::iterator
+ Value::end ()
+ {
+ switch (type_)
+ {
+ case arrayValue:
+ case objectValue:
+ if (value_.map_)
+ return iterator (value_.map_->end ());
+ break;
+ default:
+ break;
+ }
+ return iterator ();
+ }
+} // namespace json
+
+namespace json
+{
+ static void uintToString (unsigned value,
+ char *&current)
+ {
+ *--current = 0;
+ do
+ {
+ *--current = (value % 10) + '0';
+ value /= 10;
+ }
+ while (value != 0);
+ }
+
+ std::string valueToString (int value)
+ {
+ char buffer[32];
+ char *current = buffer + sizeof (buffer);
+ bool isNegative = value < 0;
+ if (isNegative)
+ value = -value;
+ uintToString (unsigned (value), current);
+ if (isNegative)
+ *--current = '-';
+ throw_unless (current >= buffer);
+ return current;
+ }
+
+
+ std::string valueToString (unsigned value)
+ {
+ char buffer[32];
+ char *current = buffer + sizeof (buffer);
+ uintToString (value, current);
+ throw_unless (current >= buffer);
+ return current;
+ }
+
+ std::string valueToString (double value)
+ {
+ char buffer[32];
+ sprintf (buffer, "%.16g", value);
+ return buffer;
+ }
+
+
+ std::string valueToString (bool value)
+ {
+ return value ? "true" : "false";
+ }
+
+ std::string valueToQuotedString (char const *value)
+ {
+ // Not sure how to handle unicode...
+ if (std::strpbrk (value, "\"\\\b\f\n\r\t") == NULL)
+ return std::string ("\"") + value + "\"";
+ // We have to walk value and escape any special characters.
+ // Appending to std::string is not efficient, but this should be rare.
+ // (Note: forward slashes are *not* rare, but I am not escaping them.)
+ unsigned maxsize = strlen (value) * 2 + 3; // allescaped+quotes+NULL
+ std::string result;
+ result.reserve (maxsize); // to avoid lots of mallocs
+ result += "\"";
+ for (char const* c=value; *c != 0; ++c){
+ switch (*c){
+ case '\"':
+ result += "\\\"";
+ break;
+ case '\\':
+ result += "\\\\";
+ break;
+ case '\b':
+ result += "\\b";
+ break;
+ case '\f':
+ result += "\\f";
+ break;
+ case '\n':
+ result += "\\n";
+ break;
+ case '\r':
+ result += "\\r";
+ break;
+ case '\t':
+ result += "\\t";
+ break;
+ case '/':
+ // Even though \/ is considered a legal escape in JSON, a bare
+ // slash is also legal, so I see no reason to escape it.
+ // (I hope I am not misunderstanding something.)
+ default:
+ result += *c;
+ }
+ }
+ result += "\"";
+ return result;
+ }
+
+ // Class Writer
+ std::string
+ Writer::write (const Value &root)
+ {
+ document_ = "";
+ writeValue (root);
+ document_ += "\n";
+ return document_;
+ }
+
+
+ void
+ Writer::writeValue (const Value &value)
+ {
+ switch (value.type ())
+ {
+ case nullValue:
+ document_ += "null";
+ break;
+ case intValue:
+ document_ += valueToString (static_cast<int> (value));
+ break;
+ case uintValue:
+ document_ += valueToString (static_cast<unsigned> (value));
+ break;
+ case realValue:
+ document_ += valueToString (static_cast<double> (value));
+ break;
+ case stringValue:
+ document_ += valueToQuotedString (static_cast<char const *> (value));
+ break;
+ case booleanValue:
+ document_ += valueToString (static_cast<bool> (value));
+ break;
+ case arrayValue:
+ {
+ document_ += "[";
+ int size = value.size ();
+ for (int index = 0; index < size; ++index)
+ {
+ if (index > 0)
+ document_ += ",";
+ writeValue (value[index]);
+ }
+ document_ += "]";
+ }
+ break;
+ case objectValue:
+ {
+ Value::Members members (value.getMemberNames ());
+ document_ += "{";
+ for (Value::Members::iterator it = members.begin ();
+ it != members.end ();
+ ++it)
+ {
+ const std::string &name = *it;
+ if (it != members.begin ())
+ document_ += ",";
+ document_ += valueToQuotedString (name.c_str ());
+ document_ += ":";
+ writeValue (value[name]);
+ }
+ document_ += "}";
+ }
+ break;
+ }
+ }
+} // namespace json
+
+/**
+ * RPC
+ */
+
+namespace json
+{
+ namespace rpc
+ {
+ typedef std::map<std::string, void (*) (HTTPRequest *, Value &, Value &)> method_map;
+
+ method_map methods;
+
+ void
+ add_method (char *name, method mth)
+ {
+ methods[name] = mth;
+ }
+
+ void
+ system_list_methods (HTTPRequest *http, Value &request, Value &response)
+ {
+ unsigned i = 0;
+ Value method_list (arrayValue);
+
+ method_map::iterator it;
+ for (it = methods.begin(); it != methods.end(); ++it)
+ {
+ method_list[i] = Value (it->first);
+ i++;
+ }
+
+ response["result"] = method_list;
+ }
+
+ void
+ init ()
+ {
+ add_method ("system.listMethods", &system_list_methods);
+ }
+
+ void
+ service (HTTPRequest *http, Value &request, Value &response)
+ {
+ char const *methodName = static_cast<char const *> (request["method"]);
+
+ method_map::iterator mth = methods.find (methodName);
+ if (mth != methods.end ())
+ (*mth->second) (http, request, response);
+ }
+
+ void
+ process (HTTPRequest *http, std::string &response_text, char const *request_text)
+ {
+ std::string text;
+ bool parse_success;
+ Value request (objectValue);
+ Value response (objectValue);
+ Reader r;
+ Writer w;
+
+ parse_success = r.parse (request_text, request_text + strlen (request_text), request);
+
+ service (http, request, response);
+
+ text = w.write (response);
+
+ response_text = text.c_str ();
+
+ return;
+ }
+ } // namespace rpc
+} // namespace json
+
+MODULE_INIT(ModuleRpcJson)