Ver código fonte

Added streaming parser for structured data format.

Added JSON streaming parser. Note that currently JSON parser ignores
frac and exp parts of number construct.
Tatsuhiro Tsujikawa 13 anos atrás
pai
commit
57b46d5123

+ 86 - 0
src/GenericParser.h

@@ -0,0 +1,86 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2012 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef D_GENERIC_PARSER_H
+#define D_GENERIC_PARSER_H
+
+#include "common.h"
+#include "SharedHandle.h"
+
+namespace aria2 {
+
+template<typename Parser, typename ParserStateMachine>
+class GenericParser {
+public:
+  GenericParser()
+    : parser_(&psm_)
+  {}
+
+  ~GenericParser()
+  {}
+
+  // Parses |size| bytes of data |data| and returns the number of
+  // bytes processed. On error, one of the negative error codes is
+  // returned.
+  ssize_t parseUpdate(const char* data, size_t size)
+  {
+    return parser_.parseUpdate(data, size);
+  }
+
+  // Parses |size| bytes of data |data| and returns result. On error,
+  // null value is returned. On success, the |error| will be the
+  // number of bytes processed (>= 0). On error, it will be one of the
+  // negative error code. This function also resets underlying parser
+  // facility and make it ready to reuse.
+  typename ParserStateMachine::ResultType
+  parseFinal(const char* data, size_t size, ssize_t& error)
+  {
+    typename ParserStateMachine::ResultType res;
+    error = parser_.parseFinal(data, size);
+    if(error < 0) {
+      res = ParserStateMachine::noResult;
+    } else {
+      res = psm_.getResult();
+    }
+    parser_.reset();
+    return res;
+  }
+private:
+  ParserStateMachine psm_;
+  Parser parser_;
+};
+
+} // namespace aria2
+
+#endif // D_GENERIC_PARSER_H

+ 747 - 0
src/JsonParser.cc

@@ -0,0 +1,747 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2012 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "JsonParser.h"
+#include "StructParserStateMachine.h"
+
+#include "util.h"
+
+namespace aria2 {
+
+namespace json {
+
+namespace {
+enum {
+  JSON_FINISH,
+  JSON_ERROR,
+  JSON_VALUE,
+  JSON_OBJECT_KEY,
+  JSON_OBJECT_VAL,
+  JSON_OBJECT_SEP,
+  JSON_ARRAY,
+  JSON_ARRAY_SEP,
+  JSON_STRING,
+  JSON_STRING_ESCAPE,
+  JSON_STRING_UNICODE,
+  JSON_STRING_LOW_SURROGATE_ESCAPE,
+  JSON_STRING_LOW_SURROGATE_U,
+  JSON_STRING_LOW_SURROGATE,
+  JSON_NUMBER,
+  JSON_NUMBER_FRAC,
+  JSON_NUMBER_EXP_SIGN,
+  JSON_NUMBER_EXP,
+  JSON_TRUE,
+  JSON_FALSE,
+  JSON_NULL
+};
+} // namespace
+
+namespace {
+const char JSON_TRUE_STR[] = "true";
+const char JSON_FALSE_STR[] = "false";
+const char JSON_NULL_STR[] = "null";
+} // namespace
+
+JsonParser::JsonParser(StructParserStateMachine* psm)
+  : psm_(psm),
+    currentState_(JSON_VALUE),
+    codepoint_(0),
+    codepoint2_(0),
+    numberSign_(1),
+    number_(0),
+    frac_(0),
+    expSign_(1),
+    exp_(0),
+    numConsumed_(0),
+    lastError_(0)
+{
+  stateStack_.push(JSON_FINISH);
+}
+
+JsonParser::~JsonParser()
+{}
+
+namespace {
+bool isSpace(char c)
+{
+  return util::isLws(c) || util::isCRLF(c);
+}
+} // namespace
+
+ssize_t JsonParser::parseUpdate(const char* data, size_t size)
+{
+  size_t i;
+  if(currentState_ == JSON_FINISH) {
+    return 0;
+  } else if(currentState_ == JSON_ERROR) {
+    return lastError_;
+  }
+  for(i = 0; i < size; ++i) {
+    char c = data[i];
+    switch(currentState_) {
+    case JSON_ARRAY:
+      if(c == ']') {
+        onArrayEnd();
+        break;
+      } else if(isSpace(c)) {
+        break;
+      } else {
+        pushState(currentState_);
+        currentState_ = JSON_VALUE;
+        runBeginCallback(STRUCT_ARRAY_DATA_T);
+      }
+      // Fall through
+    case JSON_VALUE:
+      switch(c) {
+      case '{':
+        currentState_ = JSON_OBJECT_KEY;
+        runBeginCallback(STRUCT_DICT_T);
+        break;
+      case'[':
+        currentState_ = JSON_ARRAY;
+        runBeginCallback(STRUCT_ARRAY_T);
+        break;
+      case '"':
+        currentState_ = JSON_STRING;
+        runBeginCallback(STRUCT_STRING_T);
+        break;
+      case '-':
+        number_ = 0;
+        numberSign_ = -1;
+        numConsumed_ = 0;
+        currentState_ = JSON_NUMBER;
+        runBeginCallback(STRUCT_NUMBER_T);
+        break;
+      case 't':
+        numConsumed_ = 1;
+        currentState_ = JSON_TRUE;
+        runBeginCallback(STRUCT_BOOL_T);
+        break;
+      case 'f':
+        numConsumed_ = 1;
+        currentState_ = JSON_FALSE;
+        runBeginCallback(STRUCT_BOOL_T);
+        break;
+      case 'n':
+        numConsumed_ = 1;
+        currentState_ = JSON_NULL;
+        runBeginCallback(STRUCT_NULL_T);
+        break;
+      default:
+        if(util::isDigit(c)) {
+          number_ = c - '0';
+          numberSign_ = 1;
+          numConsumed_ = 1;
+          currentState_ = JSON_NUMBER;
+          runBeginCallback(STRUCT_NUMBER_T);
+        } else if(!isSpace(c)) {
+          currentState_ = JSON_ERROR;
+          return lastError_ = ERR_UNEXPECTED_CHAR_BEFORE_VAL;
+        }
+      }
+      break;
+    case JSON_TRUE:
+      if(JSON_TRUE_STR[numConsumed_] == c) {
+        ++numConsumed_;
+        if(numConsumed_ == sizeof(JSON_TRUE_STR)-1) {
+          runBoolCallback(true);
+          onBoolEnd();
+        }
+      } else {
+        currentState_ = JSON_ERROR;
+        return lastError_ = ERR_UNEXPECTED_LITERAL;
+      }
+      break;
+    case JSON_FALSE:
+      if(JSON_FALSE_STR[numConsumed_] == c) {
+        ++numConsumed_;
+        if(numConsumed_ == sizeof(JSON_FALSE_STR)-1) {
+          runBoolCallback(false);
+          onBoolEnd();
+        }
+      } else {
+        currentState_ = JSON_ERROR;
+        return lastError_ = ERR_UNEXPECTED_LITERAL;
+      }
+      break;
+    case JSON_NULL:
+      if(JSON_NULL_STR[numConsumed_] == c) {
+        ++numConsumed_;
+        if(numConsumed_ == sizeof(JSON_NULL_STR)-1) {
+          onNullEnd();
+        }
+      } else {
+        currentState_ = JSON_ERROR;
+        return lastError_ = ERR_UNEXPECTED_LITERAL;
+      }
+      break;
+    case JSON_OBJECT_KEY:
+      switch(c) {
+      case '"':
+        pushState(currentState_);
+        currentState_ = JSON_STRING;
+        runBeginCallback(STRUCT_DICT_KEY_T);
+        break;
+      case '}':
+        onObjectEnd();
+        break;
+      default:
+        if(!isSpace(c)) {
+          currentState_ = JSON_ERROR;
+          return lastError_ = ERR_UNEXPECTED_CHAR_BEFORE_OBJ_KEY;
+        }
+      }
+      break;
+
+    case JSON_OBJECT_VAL:
+      switch(c) {
+      case ':':
+        pushState(currentState_);
+        currentState_ = JSON_VALUE;
+        runBeginCallback(STRUCT_DICT_DATA_T);
+        break;
+      default:
+        if(!isSpace(c)) {
+          currentState_ = JSON_ERROR;
+          return lastError_ = ERR_UNEXPECTED_CHAR_BEFORE_OBJ_VAL;
+        }
+      }
+      break;
+    case JSON_OBJECT_SEP:
+      switch(c) {
+      case ',':
+        currentState_ = JSON_OBJECT_KEY;
+        break;
+      case '}':
+        onObjectEnd();
+        break;
+      default:
+        if(!isSpace(c)) {
+          currentState_ = JSON_ERROR;
+          return lastError_ = ERR_UNEXPECTED_CHAR_BEFORE_OBJ_SEP;
+        }
+      }
+      break;
+    case JSON_ARRAY_SEP:
+      switch(c) {
+      case ',':
+        pushState(JSON_ARRAY);
+        currentState_ = JSON_VALUE;
+        runBeginCallback(STRUCT_ARRAY_DATA_T);
+        break;
+      case ']':
+        onArrayEnd();
+        break;
+      default:
+        if(!isSpace(c)) {
+          currentState_ = JSON_ERROR;
+          return lastError_ = ERR_UNEXPECTED_CHAR_BEFORE_ARRAY_SEP;
+        }
+      }
+      break;
+    case JSON_STRING:
+      switch(c) {
+      case '"':
+        onStringEnd();
+        break;
+      case '\\':
+        currentState_ = JSON_STRING_ESCAPE;
+        break;
+      default: {
+        size_t j;
+        for(j = i; j < size && data[j] != '\\' && data[j] != '"'; ++j);
+        if(j - i >= 1) {
+          runCharactersCallback(&data[i], j - i);
+        }
+        i = j - 1;
+        break;
+      }
+      }
+      break;
+    case JSON_STRING_ESCAPE:
+      switch(c) {
+      case 'u':
+        codepoint_ = 0;
+        numConsumed_ = 0;
+        currentState_ = JSON_STRING_UNICODE;
+        break;
+      default:
+        switch(c) {
+        case 'b':
+          runCharactersCallback("\b", 1);
+          break;
+        case 'f':
+          runCharactersCallback("\f", 1);
+          break;
+        case 'n':
+          runCharactersCallback("\n", 1);
+          break;
+        case 'r':
+          runCharactersCallback("\r", 1);
+          break;
+        case 't':
+          runCharactersCallback("\t", 1);
+          break;
+        default: {
+          char temp[1];
+          temp[0] = c;
+          runCharactersCallback(temp, 1);
+          break;
+        }
+        }
+        currentState_ = JSON_STRING;
+      }
+      break;
+    case JSON_STRING_UNICODE: {
+      size_t j;
+      for(j = i; j < size && currentState_ == JSON_STRING_UNICODE; ++j) {
+        if(util::isHexDigit(data[j])) {
+          consumeUnicode(data[j]);
+        } else {
+          currentState_ = JSON_ERROR;
+          return lastError_ = ERR_INVALID_UNICODE_POINT;
+        }
+      }
+      i = j - 1;
+      break;
+    }
+    case JSON_STRING_LOW_SURROGATE_ESCAPE:
+      switch(c) {
+      case '\\':
+        currentState_ = JSON_STRING_LOW_SURROGATE_U;
+        break;
+      default:
+        currentState_ = JSON_ERROR;
+        return lastError_ = ERR_INVALID_UNICODE_POINT;
+      }
+      break;
+    case JSON_STRING_LOW_SURROGATE_U:
+      switch(c) {
+      case 'u':
+        codepoint2_ = 0;
+        numConsumed_ = 0;
+        currentState_ = JSON_STRING_LOW_SURROGATE;
+        break;
+      default:
+        currentState_ = JSON_ERROR;
+        return lastError_ = ERR_INVALID_UNICODE_POINT;
+      }
+      break;
+    case JSON_STRING_LOW_SURROGATE: {
+      size_t j;
+      for(j = i; j < size && currentState_ == JSON_STRING_LOW_SURROGATE; ++j) {
+        if(util::isHexDigit(data[j])) {
+          int rv = consumeLowSurrogate(data[j]);
+          if(rv != 0) {
+            currentState_ = JSON_ERROR;
+            lastError_ = rv;
+            return rv;
+          }
+        } else {
+          currentState_ = JSON_ERROR;
+          return lastError_ = ERR_INVALID_UNICODE_POINT;
+        }
+      }
+      i = j - 1;
+      break;
+    }
+    case JSON_NUMBER: {
+      size_t j;
+      for(j = i; j < size && in(data[j], '0', '9'); ++j) {
+        if((INT64_MAX - (data[j] - '0'))/ 10 < number_) {
+          currentState_ = JSON_ERROR;
+          return lastError_ = ERR_NUMBER_OUT_OF_RANGE;
+        }
+        number_ *= 10;
+        number_ += data[j] - '0';
+      }
+      numConsumed_ += j - i;
+      if(j != size) {
+        if(numConsumed_ == 0) {
+          currentState_ = JSON_ERROR;
+          return lastError_ = ERR_INVALID_NUMBER;
+        }
+        switch(data[j]) {
+        case '.':
+          frac_ = 0;
+          numConsumed_ = 0;
+          currentState_ = JSON_NUMBER_FRAC;
+          i = j;
+          break;
+        case 'e':
+        case 'E':
+          expSign_ = 1;
+          exp_ = 0;
+          numConsumed_ = 0;
+          currentState_ = JSON_NUMBER_EXP_SIGN;
+          i = j;
+          break;
+        default:
+          onNumberEnd();
+          i = j - 1;
+        }
+      } else {
+        i = j - 1;
+      }
+      break;
+    }
+    case JSON_NUMBER_FRAC: {
+      size_t j;
+      for(j = i; j < size && in(data[j], '0', '9'); ++j) {
+        // Take into account at most 8 digits
+        if(frac_ < 100000000) {
+          frac_ *= 10;
+          frac_ += data[j] - '0';
+        }
+      }
+      numConsumed_ += j - i;
+      if(j != size) {
+        if(numConsumed_ == 0) {
+          currentState_ = JSON_ERROR;
+          return lastError_ = ERR_INVALID_NUMBER;
+        }
+        switch(data[j]) {
+        case 'e':
+        case 'E':
+          exp_ = 0;
+          numConsumed_ = 0;
+          currentState_ = JSON_NUMBER_EXP_SIGN;
+          i = j;
+          break;
+        default:
+          i = j - 1;
+          onNumberEnd();
+        }
+      } else {
+        i = j - 1;
+      }
+      break;
+    }
+    case JSON_NUMBER_EXP_SIGN:
+      switch(c) {
+      case '+':
+        currentState_ = JSON_NUMBER_EXP;
+        break;
+      case '-':
+        expSign_ = -1;
+        currentState_ = JSON_NUMBER_EXP;
+        break;
+      default:
+        break;
+      }
+      if(currentState_ == JSON_NUMBER_EXP) {
+        break;
+      } else {
+        currentState_ = JSON_NUMBER_EXP;
+        // Fall through
+      }
+    case JSON_NUMBER_EXP: {
+      size_t j;
+      for(j = i; j < size && in(data[j], '0', '9'); ++j) {
+        // Take into account at most 8 digits
+        exp_ *= 10;
+        exp_ += data[j] - '0';
+        if(exp_ > 18) {
+          currentState_ = JSON_ERROR;
+          return lastError_ = ERR_NUMBER_OUT_OF_RANGE;
+        }
+      }
+      numConsumed_ += j - i;
+      if(j != size) {
+        if(numConsumed_ == 0) {
+          currentState_ = JSON_ERROR;
+          return lastError_ = ERR_INVALID_NUMBER;
+        }
+        switch(data[j]) {
+        default:
+          onNumberEnd();
+        }
+      }
+      i = j - 1;
+      break;
+    }
+    }
+    if(currentState_ == JSON_FINISH) {
+      break;
+    }
+  }
+  return i;
+}
+
+ssize_t JsonParser::parseFinal(const char* data, size_t len)
+{
+  ssize_t rv;
+  rv = parseUpdate(data, len);
+  if(rv >= 0) {
+    if(currentState_ != JSON_FINISH) {
+      rv = ERR_PREMATURE_DATA;
+    }
+  }
+  return rv;
+}
+
+void JsonParser::reset()
+{
+  psm_->reset();
+  currentState_ = JSON_VALUE;
+  lastError_ = 0;
+  while(!stateStack_.empty()) {
+    stateStack_.pop();
+  }
+  stateStack_.push(JSON_FINISH);
+}
+
+void JsonParser::onStringEnd()
+{
+  runEndCallback(stateTop() == JSON_OBJECT_KEY ?
+                 STRUCT_DICT_KEY_T : STRUCT_STRING_T);
+  onValueEnd();
+}
+
+void JsonParser::onNumberEnd()
+{
+  runNumberCallback(numberSign_ * number_, frac_, expSign_ * exp_);
+  runEndCallback(STRUCT_NUMBER_T);
+  onValueEnd();
+}
+
+void JsonParser::onObjectEnd()
+{
+  runEndCallback(STRUCT_DICT_T);
+  onValueEnd();
+}
+
+void JsonParser::onArrayEnd()
+{
+  runEndCallback(STRUCT_ARRAY_T);
+  onValueEnd();
+}
+
+void JsonParser::onBoolEnd()
+{
+  runEndCallback(STRUCT_BOOL_T);
+  onValueEnd();
+}
+
+void JsonParser::onNullEnd()
+{
+  runEndCallback(STRUCT_NULL_T);
+  onValueEnd();
+}
+
+void JsonParser::onValueEnd()
+{
+  switch(stateTop()) {
+  case JSON_OBJECT_KEY:
+    popState();
+    currentState_ = JSON_OBJECT_VAL;
+    break;
+  case JSON_OBJECT_VAL:
+    runEndCallback(STRUCT_DICT_DATA_T);
+    popState();
+    currentState_ = JSON_OBJECT_SEP;
+    break;
+  case JSON_ARRAY:
+    runEndCallback(STRUCT_ARRAY_DATA_T);
+    popState();
+    currentState_ = JSON_ARRAY_SEP;
+    break;
+  default:
+    assert(stateTop() == JSON_FINISH);
+    currentState_ = stateTop();
+    break;
+  }
+}
+
+void JsonParser::pushState(int state)
+{
+  stateStack_.push(state);
+}
+
+int JsonParser::stateTop() const
+{
+  return stateStack_.top();
+}
+
+int JsonParser::popState()
+{
+  int state = stateStack_.top();
+  stateStack_.pop();
+  return state;
+}
+
+void JsonParser::consumeUnicode(char c)
+{
+  codepoint_ *= 16;
+  codepoint_ += util::hexCharToUInt(c);
+  ++numConsumed_;
+  if(numConsumed_ == 4) {
+    if(in(codepoint_, 0xD800u, 0xDBFFu)) {
+      // This is high-surrogate codepoint
+      currentState_ = JSON_STRING_LOW_SURROGATE_ESCAPE;
+    } else {
+      char temp[3];
+      size_t len;
+      if(codepoint_ <= 0x007fu) {
+        temp[0] = static_cast<char>(codepoint_);
+        len = 1;
+      } else if(codepoint_ <= 0x07ffu) {
+        temp[0] = 0xC0u | (codepoint_ >> 6);
+        temp[1] = 0x80u | (codepoint_ & 0x003fu);
+        len = 2;
+      } else {
+        temp[0] = 0xE0u | (codepoint_ >> 12);
+        temp[1] = 0x80u | ((codepoint_ >> 6) & 0x003Fu);
+        temp[2] = 0x80u | (codepoint_ & 0x003Fu);
+        len = 3;
+      }
+      runCharactersCallback(temp, len);
+      currentState_ = JSON_STRING;
+    }
+  }
+}
+
+int JsonParser::consumeLowSurrogate(char c)
+{
+  codepoint2_ *= 16;
+  codepoint2_ += util::hexCharToUInt(c);
+  ++numConsumed_;
+  if(numConsumed_ == 4) {
+    if(!in(codepoint2_, 0xDC00u, 0xDFFFu)) {
+      return ERR_INVALID_UNICODE_POINT;
+    }
+    uint32_t fullcodepoint = 0x010000u;
+    fullcodepoint += (codepoint_ & 0x03FFu) << 10;
+    fullcodepoint += (codepoint2_ & 0x03FFu);
+    char temp[4];
+    temp[0] = 0xf0u | (fullcodepoint >> 18);
+    temp[1] = 0x80u | ((fullcodepoint >> 12) & 0x003Fu);
+    temp[2] = 0x80u | ((fullcodepoint >> 6) & 0x003Fu);
+    temp[3] = 0x80u | (fullcodepoint & 0x003Fu);
+    runCharactersCallback(temp, sizeof(temp));
+    currentState_ = JSON_STRING;
+  }
+  return 0;
+}
+
+void JsonParser::runBeginCallback(int elementType)
+{
+  // switch(elementType) {
+  // case STRUCT_DICT_T:
+  //   std::cout << "object start" << std::endl;
+  //   break;
+  // case STRUCT_DICT_KEY_T:
+  //   std::cout << "object key start" << std::endl;
+  //   break;
+  // case STRUCT_DICT_DATA_T:
+  //   std::cout << "object data start" << std::endl;
+  //   break;
+  // case STRUCT_ARRAY_T:
+  //   std::cout << "array start" << std::endl;
+  //   break;
+  // case STRUCT_ARRAY_DATA_T:
+  //   std::cout << "array data start" << std::endl;
+  //   break;
+  // case STRUCT_STRING_T:
+  //   std::cout << "string start" << std::endl;
+  //   break;
+  // case STRUCT_NUMBER_T:
+  //   std::cout << "number start" << std::endl;
+  //   break;
+  // case STRUCT_BOOL_T:
+  //   std::cout << "bool start" << std::endl;
+  //   break;
+  // case STRUCT_NULL_T:
+  //   std::cout << "null start" << std::endl;
+  //   break;
+  // default:
+  //   break;
+  // };
+  psm_->beginElement(elementType);
+}
+
+void JsonParser::runEndCallback(int elementType)
+{
+  // switch(elementType) {
+  // case STRUCT_DICT_T:
+  //   std::cout << "object end" << std::endl;
+  //   break;
+  // case STRUCT_DICT_KEY_T:
+  //   std::cout << "object key end" << std::endl;
+  //   break;
+  // case STRUCT_DICT_DATA_T:
+  //   std::cout << "object data end" << std::endl;
+  //   break;
+  // case STRUCT_ARRAY_T:
+  //   std::cout << "array end" << std::endl;
+  //   break;
+  // case STRUCT_ARRAY_DATA_T:
+  //   std::cout << "array data end" << std::endl;
+  //   break;
+  // case STRUCT_STRING_T:
+  //   std::cout << "string end" << std::endl;
+  //   break;
+  // case STRUCT_NUMBER_T:
+  //   std::cout << "number end" << std::endl;
+  //   break;
+  // case STRUCT_BOOL_T:
+  //   std::cout << "bool end" << std::endl;
+  //   break;
+  // case STRUCT_NULL_T:
+  //   std::cout << "null end" << std::endl;
+  //   break;
+  // default:
+  //   break;
+  // };
+  psm_->endElement(elementType);
+}
+
+void JsonParser::runCharactersCallback(const char* data, size_t len)
+{
+  psm_->charactersCallback(data, len);
+}
+
+void JsonParser::runNumberCallback(int64_t number, int frac, int exp)
+{
+  psm_->numberCallback(number, frac, exp);
+}
+
+void JsonParser::runBoolCallback(bool bval)
+{
+  psm_->boolCallback(bval);
+}
+
+} // namespace json
+
+} // namespace aria2

+ 119 - 0
src/JsonParser.h

@@ -0,0 +1,119 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2012 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef D_JSON_PARSER_H
+#define D_JSON_PARSER_H
+
+#include "common.h"
+
+#include <stack>
+
+namespace aria2 {
+
+class StructParserStateMachine;
+
+namespace json {
+
+enum JsonError {
+  ERR_UNEXPECTED_CHAR_BEFORE_VAL = -1,
+  ERR_UNEXPECTED_CHAR_BEFORE_OBJ_KEY = -2,
+  ERR_UNEXPECTED_CHAR_BEFORE_OBJ_VAL = -3,
+  ERR_UNEXPECTED_CHAR_BEFORE_OBJ_SEP = -4,
+  ERR_INVALID_UNICODE_POINT = -5,
+  ERR_INVALID_NUMBER = -6,
+  ERR_NUMBER_OUT_OF_RANGE = -7,
+  ERR_UNEXPECTED_CHAR_BEFORE_ARRAY_SEP = -8,
+  ERR_UNEXPECTED_LITERAL = -9,
+  ERR_PREMATURE_DATA = -10
+};
+
+class JsonParser {
+public:
+  JsonParser(StructParserStateMachine* psm);
+  ~JsonParser();
+  // Parses |size| bytes of data |data| and returns the number of
+  // bytes processed. On error, one of the negative error codes is
+  // returned.
+  ssize_t parseUpdate(const char* data, size_t size);
+  // Parses |size| bytes of data |data| and returns the number of
+  // bytes processed. On error, one of the negative error codes is
+  // returned. Call this function to signal the parser that this is
+  // the last piece of data. This function does NOT reset the internal
+  // state.
+  ssize_t parseFinal(const char* data, size_t size);
+  // Resets the internal state of the parser and makes it ready for
+  // reuse.
+  void reset();
+private:
+  void pushState(int state);
+  int stateTop() const;
+  int popState();
+  void runBeginCallback(int elementType);
+  void runEndCallback(int elementType);
+  void runCharactersCallback(const char* data, size_t len);
+  void runNumberCallback(int64_t number, int frac, int exp);
+  void runBoolCallback(bool bval);
+
+  void onStringEnd();
+  void onNumberEnd();
+  void onObjectEnd();
+  void onArrayEnd();
+  void onValueEnd();
+  void onBoolEnd();
+  void onNullEnd();
+
+  void consumeUnicode(char c);
+  int consumeLowSurrogate(char c);
+
+  StructParserStateMachine* psm_;
+  std::stack<int> stateStack_;
+  int currentState_;
+  // Unicode codepoint
+  uint16_t codepoint_;
+  // For low-surrogate codepoint
+  uint16_t codepoint2_;
+  int numberSign_;
+  int64_t number_;
+  int frac_;
+  int expSign_;
+  int exp_;
+  size_t numConsumed_;
+  int lastError_;
+};
+
+} // namespace json
+
+} // namespace aria2
+
+#endif // D_JSON_PARSER_H

+ 6 - 0
src/Makefile.am

@@ -199,6 +199,12 @@ SRCS =  Socket.h\
 	Triplet.h\
 	cookie_helper.cc cookie_helper.h\
 	json.cc json.h\
+	JsonParser.cc JsonParser.h\
+	StructParserStateMachine.h\
+	ValueBaseJsonParser.h\
+	ValueBaseStructParserState.h\
+	ValueBaseStructParserStateImpl.cc ValueBaseStructParserStateImpl.h\
+	ValueBaseStructParserStateMachine.cc ValueBaseStructParserStateMachine.h\
 	HttpServerBodyCommand.cc HttpServerBodyCommand.h\
 	RpcRequest.cc RpcRequest.h\
 	RpcMethod.cc RpcMethod.h\

+ 70 - 0
src/StructParserStateMachine.h

@@ -0,0 +1,70 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2012 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef D_STRUCT_PARSER_STATE_MACHINE_H
+#define D_STRUCT_PARSER_STATE_MACHINE_H
+
+#include "common.h"
+
+namespace aria2 {
+
+enum StructElementType {
+  STRUCT_DICT_T,
+  STRUCT_DICT_KEY_T,
+  STRUCT_DICT_DATA_T,
+  STRUCT_ARRAY_T,
+  STRUCT_ARRAY_DATA_T,
+  STRUCT_STRING_T,
+  STRUCT_NUMBER_T,
+  STRUCT_BOOL_T,
+  STRUCT_NULL_T
+};
+
+// Interface for streaming parser of structured data format (e.g.,
+// JSON, Bencode).
+class StructParserStateMachine {
+public:
+  virtual ~StructParserStateMachine() {}
+
+  virtual void beginElement(int elementType) = 0;
+  virtual void endElement(int elementType) = 0;
+  virtual void reset() = 0;
+  virtual void charactersCallback(const char* data, size_t len) = 0;
+  virtual void numberCallback(int64_t number, int frac, int exp) = 0;
+  virtual void boolCallback(bool bval) = 0;
+};
+
+} //  namespace aria2
+
+#endif // D_STRUCT_PARSER_STATE_MACHINE_H

+ 53 - 0
src/ValueBaseJsonParser.h

@@ -0,0 +1,53 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2012 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef D_VALUE_BASE_JSON_PARSER_H
+#define D_VALUE_BASE_JSON_PARSER_H
+
+#include "GenericParser.h"
+#include "JsonParser.h"
+#include "ValueBaseStructParserStateMachine.h"
+
+namespace aria2 {
+
+namespace json {
+
+typedef GenericParser<JsonParser, ValueBaseStructParserStateMachine>
+ValueBaseJsonParser;
+
+} // namespace json
+
+} // namespace aria2
+
+#endif // D_VALUE_BASE_JSON_PARSER_H

+ 57 - 0
src/ValueBaseStructParserState.h

@@ -0,0 +1,57 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2012 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef D_VALUE_BASE_STRUCT_PARSER_STATE_H
+#define D_VALUE_BASE_STRUCT_PARSER_STATE_H
+
+#include "common.h"
+
+namespace aria2 {
+
+class ValueBaseStructParserStateMachine;
+
+class ValueBaseStructParserState {
+public:
+  virtual ~ValueBaseStructParserState() {}
+
+  virtual void beginElement(ValueBaseStructParserStateMachine* psm,
+                            int elementType) = 0;
+
+  virtual void endElement(ValueBaseStructParserStateMachine* psm,
+                          int elementType) = 0;
+};
+
+} // namespace aria2
+
+#endif // D_VALUE_BASE_STRUCT_PARSER_STATE_H

+ 139 - 0
src/ValueBaseStructParserStateImpl.cc

@@ -0,0 +1,139 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2012 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "ValueBaseStructParserStateImpl.h"
+#include "ValueBaseStructParserStateMachine.h"
+#include "ValueBase.h"
+
+namespace aria2 {
+
+void ValueValueBaseStructParserState::beginElement
+(ValueBaseStructParserStateMachine* psm, int elementType)
+{
+  switch(elementType) {
+  case STRUCT_DICT_T:
+    psm->setCurrentFrameValue(Dict::g());
+    psm->pushDictState();
+    break;
+  case STRUCT_ARRAY_T:
+    psm->setCurrentFrameValue(List::g());
+    psm->pushArrayState();
+    break;
+  case STRUCT_STRING_T:
+    psm->pushStringState();
+    break;
+  case STRUCT_NUMBER_T:
+    psm->pushNumberState();
+    break;
+  case STRUCT_BOOL_T:
+    psm->pushBoolState();
+    break;
+  case STRUCT_NULL_T:
+    psm->pushNullState();
+    break;
+  default:
+    // Not reachable
+    assert(0);
+  }
+}
+
+void DictValueBaseStructParserState::beginElement
+(ValueBaseStructParserStateMachine* psm, int elementType)
+{
+  switch(elementType) {
+  case STRUCT_DICT_KEY_T:
+    psm->pushFrame();
+    psm->pushDictKeyState();
+    break;
+  case STRUCT_DICT_DATA_T:
+    psm->pushDictDataState();
+    break;
+  default:
+    // Not reachable
+    assert(0);
+  }
+}
+
+void DictKeyValueBaseStructParserState::endElement
+(ValueBaseStructParserStateMachine* psm, int elementType)
+{
+  psm->setCurrentFrameName(psm->getCharacters());
+}
+
+void DictDataValueBaseStructParserState::endElement
+(ValueBaseStructParserStateMachine* psm, int elementType)
+{
+  psm->popDictFrame();
+}
+
+void ArrayValueBaseStructParserState::beginElement
+(ValueBaseStructParserStateMachine* psm, int elementType)
+{
+  assert(elementType == STRUCT_ARRAY_DATA_T);
+  psm->pushFrame();
+  psm->pushArrayDataState();
+}
+
+void ArrayDataValueBaseStructParserState::endElement
+(ValueBaseStructParserStateMachine* psm, int elementType)
+{
+  psm->popArrayFrame();
+}
+
+void StringValueBaseStructParserState::endElement
+(ValueBaseStructParserStateMachine* psm, int elementType)
+{
+  psm->setCurrentFrameValue(String::g(psm->getCharacters()));
+}
+
+void NumberValueBaseStructParserState::endElement
+(ValueBaseStructParserStateMachine* psm, int elementType)
+{
+  // TODO Ignore frac and exp
+  psm->setCurrentFrameValue(Integer::g(psm->getNumber().number));
+}
+
+void BoolValueBaseStructParserState::endElement
+(ValueBaseStructParserStateMachine* psm, int elementType)
+{
+  psm->setCurrentFrameValue(psm->getBool() ? Bool::gTrue() : Bool::gFalse());
+}
+
+void NullValueBaseStructParserState::endElement
+(ValueBaseStructParserStateMachine* psm, int elementType)
+{
+  psm->setCurrentFrameValue(Null::g());
+}
+
+} // namespace aria2

+ 158 - 0
src/ValueBaseStructParserStateImpl.h

@@ -0,0 +1,158 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2012 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef D_VALUE_BASE_STRUCT_PARSER_STATE_IMPL_H
+#define D_VALUE_BASE_STRUCT_PARSER_STATE_IMPL_H
+
+#include "ValueBaseStructParserState.h"
+
+namespace aria2 {
+
+class ValueValueBaseStructParserState : public ValueBaseStructParserState {
+public:
+  virtual ~ValueValueBaseStructParserState() {}
+
+  virtual void beginElement(ValueBaseStructParserStateMachine* psm,
+                            int elementType);
+
+  virtual void endElement(ValueBaseStructParserStateMachine* psm,
+                          int elementType)
+  {}
+};
+
+class DictValueBaseStructParserState : public ValueBaseStructParserState {
+public:
+  virtual ~DictValueBaseStructParserState() {}
+
+  virtual void beginElement(ValueBaseStructParserStateMachine* psm,
+                            int elementType);
+
+  virtual void endElement(ValueBaseStructParserStateMachine* psm,
+                          int elementType)
+  {}
+};
+
+class DictKeyValueBaseStructParserState : public ValueBaseStructParserState {
+public:
+  virtual ~DictKeyValueBaseStructParserState() {}
+
+  virtual void beginElement(ValueBaseStructParserStateMachine* psm,
+                            int elementType)
+  {}
+
+  virtual void endElement(ValueBaseStructParserStateMachine* psm,
+                          int elementType);
+};
+
+class DictDataValueBaseStructParserState :
+    public ValueValueBaseStructParserState {
+public:
+  virtual ~DictDataValueBaseStructParserState() {}
+
+  virtual void endElement(ValueBaseStructParserStateMachine* psm,
+                          int elementType);
+};
+
+class ArrayValueBaseStructParserState : public ValueBaseStructParserState {
+public:
+  virtual ~ArrayValueBaseStructParserState() {}
+
+  virtual void beginElement(ValueBaseStructParserStateMachine* psm,
+                            int elementType);
+
+  virtual void endElement(ValueBaseStructParserStateMachine* psm,
+                          int elementType)
+  {}
+};
+
+class ArrayDataValueBaseStructParserState :
+    public ValueValueBaseStructParserState {
+public:
+  virtual ~ArrayDataValueBaseStructParserState() {}
+
+  virtual void endElement(ValueBaseStructParserStateMachine* psm,
+                          int elementType);
+};
+
+class StringValueBaseStructParserState : public ValueBaseStructParserState {
+public:
+  virtual ~StringValueBaseStructParserState() {}
+
+  virtual void beginElement(ValueBaseStructParserStateMachine* psm,
+                            int elementType)
+  {}
+
+  virtual void endElement(ValueBaseStructParserStateMachine* psm,
+                          int elementType);
+};
+
+class NumberValueBaseStructParserState : public ValueBaseStructParserState {
+public:
+  virtual ~NumberValueBaseStructParserState() {}
+
+  virtual void beginElement(ValueBaseStructParserStateMachine* psm,
+                            int elementType)
+  {}
+
+  virtual void endElement(ValueBaseStructParserStateMachine* psm,
+                          int elementType);
+};
+
+class BoolValueBaseStructParserState : public ValueBaseStructParserState {
+public:
+  virtual ~BoolValueBaseStructParserState() {}
+
+  virtual void beginElement(ValueBaseStructParserStateMachine* psm,
+                            int elementType)
+  {}
+
+  virtual void endElement(ValueBaseStructParserStateMachine* psm,
+                          int elementType);
+};
+
+class NullValueBaseStructParserState : public ValueBaseStructParserState {
+public:
+  virtual ~NullValueBaseStructParserState() {}
+
+  virtual void beginElement(ValueBaseStructParserStateMachine* psm,
+                            int elementType)
+  {}
+
+  virtual void endElement(ValueBaseStructParserStateMachine* psm,
+                          int elementType);
+};
+
+} // namespace aria2
+
+#endif // D_VALUE_BASE_STRUCT_PARSER_STATE_IMPL_H

+ 225 - 0
src/ValueBaseStructParserStateMachine.cc

@@ -0,0 +1,225 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2012 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "ValueBaseStructParserStateMachine.h"
+
+#include <cstring>
+
+#include "XmlRpcRequestParserController.h"
+#include "ValueBaseStructParserStateImpl.h"
+#include "ValueBase.h"
+
+namespace aria2 {
+
+namespace {
+ValueValueBaseStructParserState* valueState =
+  new ValueValueBaseStructParserState();
+DictValueBaseStructParserState* dictState =
+  new DictValueBaseStructParserState();
+DictKeyValueBaseStructParserState* dictKeyState =
+  new DictKeyValueBaseStructParserState();
+DictDataValueBaseStructParserState* dictDataState =
+  new DictDataValueBaseStructParserState();
+ArrayValueBaseStructParserState* arrayState =
+  new ArrayValueBaseStructParserState();
+ArrayDataValueBaseStructParserState* arrayDataState =
+  new ArrayDataValueBaseStructParserState();
+StringValueBaseStructParserState* stringState =
+  new StringValueBaseStructParserState();
+NumberValueBaseStructParserState* numberState =
+  new NumberValueBaseStructParserState();
+BoolValueBaseStructParserState* boolState =
+  new BoolValueBaseStructParserState();
+NullValueBaseStructParserState* nullState =
+  new NullValueBaseStructParserState();
+} // namespace
+
+const SharedHandle<ValueBase>
+ValueBaseStructParserStateMachine::noResult = ValueBase::none;
+
+ValueBaseStructParserStateMachine::ValueBaseStructParserStateMachine()
+  : ctrl_(new rpc::XmlRpcRequestParserController())
+{
+  stateStack_.push(valueState);
+}
+
+ValueBaseStructParserStateMachine::~ValueBaseStructParserStateMachine()
+{
+  delete ctrl_;
+}
+
+void ValueBaseStructParserStateMachine::reset()
+{
+  while(!stateStack_.empty()) {
+    stateStack_.pop();
+  }
+  stateStack_.push(valueState);
+  ctrl_->reset();
+}
+
+void ValueBaseStructParserStateMachine::beginElement(int elementType)
+{
+  stateStack_.top()->beginElement(this, elementType);
+}
+
+void ValueBaseStructParserStateMachine::endElement(int elementType)
+{
+  stateStack_.top()->endElement(this, elementType);
+  stateStack_.pop();
+}
+
+SharedHandle<ValueBase>
+ValueBaseStructParserStateMachine::getResult() const
+{
+  return getCurrentFrameValue();
+}
+
+void ValueBaseStructParserStateMachine::charactersCallback
+(const char* data, size_t len)
+{
+  sessionData_.str.append(data, len);
+}
+
+void ValueBaseStructParserStateMachine::numberCallback
+(int64_t number, int frac, int exp)
+{
+  sessionData_.number.number = number;
+  sessionData_.number.frac = frac;
+  sessionData_.number.exp = exp;
+}
+
+void ValueBaseStructParserStateMachine::boolCallback(bool bval)
+{
+  sessionData_.bval = bval;
+}
+
+const std::string& ValueBaseStructParserStateMachine::getCharacters() const
+{
+  return sessionData_.str;
+}
+
+const ValueBaseStructParserStateMachine::NumberData&
+ValueBaseStructParserStateMachine::getNumber() const
+{
+  return sessionData_.number;
+}
+
+bool ValueBaseStructParserStateMachine::getBool() const
+{
+  return sessionData_.bval;
+}
+
+void ValueBaseStructParserStateMachine::popArrayFrame()
+{
+  ctrl_->popArrayFrame();
+}
+
+void ValueBaseStructParserStateMachine::popDictFrame()
+{
+  ctrl_->popStructFrame();
+}
+
+void ValueBaseStructParserStateMachine::pushFrame()
+{
+  ctrl_->pushFrame();
+}
+
+void ValueBaseStructParserStateMachine::setCurrentFrameValue
+(const SharedHandle<ValueBase>& value)
+{
+  ctrl_->setCurrentFrameValue(value);
+}
+
+const SharedHandle<ValueBase>&
+ValueBaseStructParserStateMachine::getCurrentFrameValue() const
+{
+  return ctrl_->getCurrentFrameValue();
+}
+
+void ValueBaseStructParserStateMachine::setCurrentFrameName
+(const std::string& name)
+{
+  ctrl_->setCurrentFrameName(name);
+}
+
+void ValueBaseStructParserStateMachine::pushDictState()
+{
+  stateStack_.push(dictState);
+}
+
+void ValueBaseStructParserStateMachine::pushDictKeyState()
+{
+  sessionData_.str.clear();
+  stateStack_.push(dictKeyState);
+}
+
+void ValueBaseStructParserStateMachine::pushDictDataState()
+{
+  stateStack_.push(dictDataState);
+}
+
+void ValueBaseStructParserStateMachine::pushArrayState()
+{
+  stateStack_.push(arrayState);
+}
+
+void ValueBaseStructParserStateMachine::pushArrayDataState()
+{
+  stateStack_.push(arrayDataState);
+}
+
+void ValueBaseStructParserStateMachine::pushStringState()
+{
+  sessionData_.str.clear();
+  stateStack_.push(stringState);
+}
+
+void ValueBaseStructParserStateMachine::pushNumberState()
+{
+  memset(&sessionData_.number, 0, sizeof(sessionData_.number));
+  stateStack_.push(numberState);
+}
+
+void ValueBaseStructParserStateMachine::pushBoolState()
+{
+  sessionData_.bval = false;
+  stateStack_.push(boolState);
+}
+
+void ValueBaseStructParserStateMachine::pushNullState()
+{
+  stateStack_.push(nullState);
+}
+
+} // namespace aria2

+ 116 - 0
src/ValueBaseStructParserStateMachine.h

@@ -0,0 +1,116 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2012 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef D_VALUE_BASE_STRUCT_PARSER_STATE_MACHINE_H
+#define D_VALUE_BASE_STRUCT_PARSER_STATE_MACHINE_H
+
+#include "StructParserStateMachine.h"
+
+#include <string>
+#include <stack>
+
+#include "SharedHandle.h"
+
+namespace aria2 {
+
+class ValueBase;
+
+namespace rpc {
+class XmlRpcRequestParserController;
+} // namespace rpc;
+
+class ValueBaseStructParserState;
+
+// Implementation of StructParserStateMachine, using ValueBase as
+// value holder.
+class ValueBaseStructParserStateMachine : public StructParserStateMachine {
+public:
+  typedef SharedHandle<ValueBase> ResultType;
+  static const SharedHandle<ValueBase> noResult;
+
+  struct NumberData {
+    int64_t number;
+    int frac;
+    int exp;
+  };
+
+  struct SessionData {
+    std::string str;
+    NumberData number;
+    bool bval;
+  };
+
+  ValueBaseStructParserStateMachine();
+  virtual ~ValueBaseStructParserStateMachine();
+
+  virtual void beginElement(int elementType);
+  virtual void endElement(int elementType);
+
+  virtual void charactersCallback(const char* data, size_t len);
+  virtual void numberCallback(int64_t number, int frac, int exp);
+  virtual void boolCallback(bool bval);
+
+  SharedHandle<ValueBase> getResult() const;
+
+  virtual void reset();
+
+  const std::string& getCharacters() const;
+  const NumberData& getNumber() const;
+  bool getBool() const;
+
+  void popArrayFrame();
+  void popDictFrame();
+  void pushFrame();
+  void setCurrentFrameValue(const SharedHandle<ValueBase>& value);
+  const SharedHandle<ValueBase>& getCurrentFrameValue() const;
+  void setCurrentFrameName(const std::string& name);
+
+  void pushDictState();
+  void pushDictKeyState();
+  void pushDictDataState();
+  void pushArrayState();
+  void pushArrayDataState();
+  void pushStringState();
+  void pushNumberState();
+  void pushBoolState();
+  void pushNullState();
+private:
+  rpc::XmlRpcRequestParserController* ctrl_;
+  std::stack<ValueBaseStructParserState*> stateStack_;
+  SessionData sessionData_;
+};
+
+} // namespace aria2
+
+#endif // D_VALUE_BASE_STRUCT_PARSER_STATE_MACHINE_H

+ 9 - 0
src/XmlRpcRequestParserController.cc

@@ -92,6 +92,15 @@ XmlRpcRequestParserController::getCurrentFrameValue() const
   return currentFrame_.value_;
 }
 
+void XmlRpcRequestParserController::reset()
+{
+  while(!frameStack_.empty()) {
+    frameStack_.pop();
+  }
+  currentFrame_.reset();
+  methodName_.clear();
+}
+
 } // namespace rpc
   
 } // namespace aria2

+ 8 - 0
src/XmlRpcRequestParserController.h

@@ -57,6 +57,12 @@ private:
     {
       return value_ && !name_.empty();
     }
+
+    void reset()
+    {
+      value_.reset();
+      name_.clear();
+    }
   };
 
   std::stack<StateFrame> frameStack_;
@@ -87,6 +93,8 @@ public:
   }
 
   const std::string& getMethodName() const { return methodName_; }
+
+  void reset();
 };
 
 } // namespace rpc

+ 1 - 0
test/Makefile.am

@@ -74,6 +74,7 @@ aria2c_SOURCES = AllTest.cc\
 	TripletTest.cc\
 	CookieHelperTest.cc\
 	JsonTest.cc\
+	ValueBaseJsonParserTest.cc\
 	RpcResponseTest.cc\
 	RpcMethodTest.cc\
 	BufferedFileTest.cc\

+ 293 - 0
test/ValueBaseJsonParserTest.cc

@@ -0,0 +1,293 @@
+#include "ValueBaseJsonParser.h"
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "RecoverableException.h"
+#include "array_fun.h"
+#include "ValueBase.h"
+
+namespace aria2 {
+
+class ValueBaseJsonParserTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(ValueBaseJsonParserTest);
+  CPPUNIT_TEST(testParseUpdate);
+  CPPUNIT_TEST(testParseUpdate_error);
+  CPPUNIT_TEST_SUITE_END();
+private:
+
+public:
+  void testParseUpdate();
+  void testParseUpdate_error();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION( ValueBaseJsonParserTest );
+
+void ValueBaseJsonParserTest::testParseUpdate()
+{
+  json::ValueBaseJsonParser parser;
+  ssize_t error;
+  {
+    // empty object
+    std::string src = "{}";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const Dict* dict = downcast<Dict>(r);
+    CPPUNIT_ASSERT(dict);
+  }
+  {
+    // empty object
+    std::string src = "{  }";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const Dict* dict = downcast<Dict>(r);
+    CPPUNIT_ASSERT(dict);
+  }
+  {
+    // empty array
+    std::string src = "[]";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const List* list = downcast<List>(r);
+    CPPUNIT_ASSERT(list);
+  }
+  {
+    // empty array
+    std::string src = "[ ]";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const List* list = downcast<List>(r);
+    CPPUNIT_ASSERT(list);
+  }
+  {
+    // empty string
+    std::string src = "[\"\"]";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const List* list = downcast<List>(r);
+    CPPUNIT_ASSERT(list);
+    const String* s = downcast<String>(list->get(0));
+    CPPUNIT_ASSERT_EQUAL(std::string(), s->s());
+  }
+  {
+    // string
+    std::string src = "[\"foobar\"]";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const List* list = downcast<List>(r);
+    CPPUNIT_ASSERT(list);
+    const String* s = downcast<String>(list->get(0));
+    CPPUNIT_ASSERT_EQUAL(std::string("foobar"), s->s());
+  }
+  {
+    // string with escape
+    std::string src = "[\"\\\\foo\\\"\\\"bar\"]";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const List* list = downcast<List>(r);
+    CPPUNIT_ASSERT(list);
+    const String* s = downcast<String>(list->get(0));
+    CPPUNIT_ASSERT_EQUAL(std::string("\\foo\"\"bar"), s->s());
+  }
+  {
+    // string with escape
+    std::string src = "[\"foo\\\"\"]";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const List* list = downcast<List>(r);
+    CPPUNIT_ASSERT(list);
+    const String* s = downcast<String>(list->get(0));
+    CPPUNIT_ASSERT_EQUAL(std::string("foo\""), s->s());
+  }
+  {
+    // string: utf-8 1 to 3 bytes.
+    std::string src = "[\"\\u0024\\u00A2\\u20AC\"]";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const List* list = downcast<List>(r);
+    CPPUNIT_ASSERT(list);
+    const String* s = downcast<String>(list->get(0));
+    CPPUNIT_ASSERT_EQUAL(std::string("$¢€"), s->s());
+  }
+  {
+    // string: utf-8 4 bytes
+    std::string src = "[\"\\uD852\\uDF62\"]";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const List* list = downcast<List>(r);
+    CPPUNIT_ASSERT(list);
+    const String* s = downcast<String>(list->get(0));
+    const unsigned char arr[] = { 0xF0u, 0xA4u, 0xADu, 0xA2u };
+    CPPUNIT_ASSERT_EQUAL(std::string(vbegin(arr), vend(arr)), s->s());
+  }
+  {
+    // null
+    std::string src = "[null]";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const List* list = downcast<List>(r);
+    CPPUNIT_ASSERT(list);
+    const Null* s = downcast<Null>(list->get(0));
+    CPPUNIT_ASSERT(s);
+  }
+  {
+    // true, false
+    std::string src = "[true, false]";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const List* list = downcast<List>(r);
+    CPPUNIT_ASSERT(list);
+    const Bool* trueValue = downcast<Bool>(list->get(0));
+    CPPUNIT_ASSERT(trueValue);
+    CPPUNIT_ASSERT(trueValue->val());
+    const Bool* falseValue = downcast<Bool>(list->get(1));
+    CPPUNIT_ASSERT(falseValue);
+    CPPUNIT_ASSERT(!falseValue->val());
+  }
+  {
+    // object: 1 member
+    std::string src = "{\"foo\":[\"bar\"]}";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const Dict* dict = downcast<Dict>(r);
+    CPPUNIT_ASSERT(dict);
+    const List* list = downcast<List>(dict->get("foo"));
+    CPPUNIT_ASSERT(list);
+    const String* s = downcast<String>(list->get(0));
+    CPPUNIT_ASSERT_EQUAL(std::string("bar"), s->s());
+  }
+  {
+    // object: 2 members
+    // TODO ValueBaseJsonParser does not allow empty dict key
+    std::string src = "{\"foo\":[\"bar\"], \"alpha\" : \"bravo\"}";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const Dict* dict = downcast<Dict>(r);
+    CPPUNIT_ASSERT(dict);
+    const List* list = downcast<List>(dict->get("foo"));
+    CPPUNIT_ASSERT(list);
+    const String* s = downcast<String>(list->get(0));
+    CPPUNIT_ASSERT_EQUAL(std::string("bar"), s->s());
+    const String* str = downcast<String>(dict->get("alpha"));
+    CPPUNIT_ASSERT_EQUAL(std::string("bravo"), str->s());
+  }
+  {
+    // array: 2 values
+    std::string src = "[\"foo\", {}]";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const List* list = downcast<List>(r);
+    CPPUNIT_ASSERT(list);
+    const String* s = downcast<String>(list->get(0));
+    CPPUNIT_ASSERT_EQUAL(std::string("foo"), s->s());
+    const Dict* dict = downcast<Dict>(list->get(1));
+    CPPUNIT_ASSERT(dict);
+  }
+  {
+    // Number: currently we ignore frac and exp
+    std::string src = "[0,-1,1.2,-1.2e-10,-1e10]";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const List* list = downcast<List>(r);
+    CPPUNIT_ASSERT(list);
+    const Integer* i = downcast<Integer>(list->get(0));
+    CPPUNIT_ASSERT_EQUAL((Integer::ValueType)0, i->i());
+    const Integer* i1 = downcast<Integer>(list->get(1));
+    CPPUNIT_ASSERT_EQUAL((Integer::ValueType)-1, i1->i());
+    const Integer* i2 = downcast<Integer>(list->get(2));
+    CPPUNIT_ASSERT_EQUAL((Integer::ValueType)1, i2->i());
+    const Integer* i3 = downcast<Integer>(list->get(3));
+    CPPUNIT_ASSERT_EQUAL((Integer::ValueType)-1, i3->i());
+    const Integer* i4 = downcast<Integer>(list->get(4));
+    CPPUNIT_ASSERT_EQUAL((Integer::ValueType)-1, i4->i());
+  }
+  {
+    // escape chars: ", \, /, \b, \f, \n, \r, \t
+    std::string src = "[\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"]";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const List* list = downcast<List>(r);
+    const String* s = downcast<String>(list->get(0));
+    CPPUNIT_ASSERT_EQUAL(std::string("\"\\/\b\f\n\r\t"), s->s());
+  }
+  {
+    // string: literal + escaped chars.
+    std::string src = "[\"foo\\u0024b\\u00A2\\u20ACbaz\"]";
+    SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                  error);
+    const List* list = downcast<List>(r);
+    CPPUNIT_ASSERT(list);
+    const String* s = downcast<String>(list->get(0));
+    CPPUNIT_ASSERT_EQUAL(std::string("foo$b¢€baz"), s->s());
+  }
+
+}
+
+namespace {
+void checkDecodeError(const std::string& src)
+{
+  json::ValueBaseJsonParser parser;
+  ssize_t error;
+  SharedHandle<ValueBase> r = parser.parseFinal(src.c_str(), src.size(),
+                                                error);
+  CPPUNIT_ASSERT(!r);
+  CPPUNIT_ASSERT(error < 0);
+}
+} // namespace
+
+void ValueBaseJsonParserTest::testParseUpdate_error()
+{
+  // object
+  checkDecodeError("{");
+  // object
+  checkDecodeError("}");
+  // object
+  checkDecodeError("{\"\":");
+  // object
+  checkDecodeError("{\"\":\"\",");
+  // array
+  checkDecodeError("[");
+  // array
+  checkDecodeError("]");
+  // array
+  checkDecodeError("[\"\"");
+  // array
+  checkDecodeError("[\"\",");
+  // string
+  checkDecodeError("[\"foo]");
+  // string
+  checkDecodeError("[\"\\u\"]");
+  // string
+  checkDecodeError("[\"\\u");
+  // string
+  checkDecodeError("[\"\\u000\"]");
+  // string
+  checkDecodeError("[\"\\u000");
+  // string
+  checkDecodeError("[\"\\uD852foo\"]");
+  // string
+  checkDecodeError("[\"\\uD852");
+  // string
+  checkDecodeError("[\"\\uD852\\u\"]");
+  // string
+  checkDecodeError("[\"\\uD852\\u");
+  // string
+  checkDecodeError("[\"\\uD852\\u0000\"]");
+  // string
+  checkDecodeError("[\"\\uD852\\uDF62");
+  // object
+  checkDecodeError("{:\"\"}");
+  // object
+  checkDecodeError("{\"foo\":}");
+  // number
+  // TODO ValueBaseJsonParser allows leading zeros
+  //checkDecodeError("[00]");
+  // number
+  checkDecodeError("[1.]");
+  // number
+  checkDecodeError("[1.1e]");
+  // bool
+  checkDecodeError("[t");
+}
+
+} // namespace aria2