Added streaming parser for structured data format.

Added JSON streaming parser. Note that currently JSON parser ignores
frac and exp parts of number construct.
pull/25/merge
Tatsuhiro Tsujikawa 2012-07-10 01:42:42 +09:00
parent c7131c14fe
commit 57b46d5123
15 changed files with 2087 additions and 0 deletions

86
src/GenericParser.h Normal file
View File

@ -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
src/JsonParser.cc Normal file
View File

@ -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
src/JsonParser.h Normal file
View File

@ -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

View File

@ -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\

View File

@ -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
src/ValueBaseJsonParser.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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