ソースを参照

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 年 前
コミット
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