/** * Copyright 2014 by Benjamin Land (a.k.a. BenLand100) * * fastjson 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 3 of the License, or * (at your option) any later version. * * fastjson 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 fastjson. If not, see . */ #include "json.hh" #include #include #include #include #include #include namespace json { void Value::reset(Type type_) { decref(); this->type = type_; switch (type) { case TSTRING: data.string = new TString(); refcount = new TUInteger(0); return; case TOBJECT: data.object = new TObject(); refcount = new TUInteger(0); return; case TARRAY: data.array = new TArray(); refcount = new TUInteger(0); return; default: refcount = NULL; } } void Value::clean() { if (refcount) delete refcount; switch (type) { case TSTRING: delete data.string; break; case TOBJECT: delete data.object; break; case TARRAY: delete data.array; break; default: break; } type = TNULL; refcount = NULL; } std::vector Value::getMembers() const { checkType(TOBJECT); std::vector keys(data.object->size()); size_t i = 0; for (TObject::iterator pair = data.object->begin(); pair != data.object->end(); ++pair) { keys[i++] = pair->first; } return keys; } bool Value::isMember(std::string key) const { checkType(TOBJECT); return (data.object->find(key) != data.object->end()); } std::string Value::toJSONString() { std::stringstream stream; json::Writer writer(stream); writer.putValue(*this); return stream.str(); } std::string Value::prettyType(Type type) { switch (type) { case TOBJECT: return "TObject"; case TARRAY: return "TArray"; case TSTRING: return "TString"; case TBOOL: return "TBool"; case TREAL: return "TReal"; case TINTEGER: return "TInteger"; case TUINTEGER: return "TUInteger"; case TNULL: return "TNULL"; default: return "ERROR"; } } void Value::wrongType(Type actual, Type requested) { std::stringstream pretty; pretty << "JSON Value of type " << prettyType(actual) << " is not type " << prettyType(requested); throw std::runtime_error(pretty.str()); } parser_error::parser_error(const int line_, const int pos_, std::string desc_) : line(line_), pos(pos_), desc(desc_) { std::stringstream prettyss; prettyss << '[' << line << ':' << pos << "] " << desc; this->pretty = prettyss.str(); } parser_error::~parser_error() throw () { } const char* parser_error::what() const throw () { return pretty.c_str(); } Reader::Reader(std::istream &in) { std::string ret; char buffer[4096]; while (in.read(buffer, sizeof(buffer))) ret.append(buffer, sizeof(buffer)); ret.append(buffer, in.gcount()); data = new char[ret.length()+1]; cur = data; memcpy(data,ret.c_str(),ret.length()); data[ret.length()] = '\0'; line = 1; lastbr = cur; } Reader::Reader(const std::string &str) { data = new char[str.length()+1]; cur = data; memcpy(data,str.c_str(),str.length()); data[str.length()] = '\0'; line = 1; lastbr = cur; } Reader::~Reader() { delete [] data; } bool Reader::getValue(Value &result) { for (;;) { switch (*cur) { case '\n': line++; case '\r': lastbr = cur+1; case ' ': case '\t': cur++; break; case '-': case '+': case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': result = readNumber(); return true; case '{': result = readObject(); return true; case '[': result = readArray(); return true; case '"': result = readString(); return true; case 'n': //https://tools.ietf.org/rfc/rfc7159.txt if (cur[1] == 'u' && cur[2] == 'l' && cur[3] == 'l') { cur+=4; result = Value(); return true; } throw parser_error(line,cur-lastbr,"Unexpected character"); case 't': //https://tools.ietf.org/rfc/rfc7159.txt if (cur[1] == 'r' && cur[2] == 'u' && cur[3] == 'e') { cur+=4; result = Value(true); return true; } throw parser_error(line,cur-lastbr,"Unexpected character"); case 'f': //https://tools.ietf.org/rfc/rfc7159.txt if (cur[1] == 'a' && cur[2] == 'l' && cur[3] == 's' && cur[4] == 'e') { cur+=5; result = Value(false); return true; } throw parser_error(line,cur-lastbr,"Unexpected character"); case '/': //non-json comment skipComment(); break; case '\0': //EOF return false; default: throw parser_error(line,cur-lastbr,"Unexpected character"); } } throw parser_error(line,cur-lastbr,"Should never reach here. Probably hardware error."); } void Reader::skipComment() { if (cur[1] == '/') { cur++; while ((*cur) && *cur != '\n') { cur++; } line++; lastbr = cur+1; if (*cur++) return; } else if (cur[1] == '*') { cur++; for ( ; *cur; cur++) { switch (*cur) { case '*': if (cur[1] == '/') { cur += 2; return; } break; case '\n': line++; case '\r': lastbr = cur+1; } } if (*cur++) return; } throw parser_error(line,cur-lastbr,"Malformed comment"); } Value Reader::readNumber() { bool real = false; bool exp = false; char *start = cur; for (;;) { switch (*cur) { case 'x': //non-json hex if (cur-start == 1 && start[0] == '0') { cur++; start = cur; for (;;) { switch (*cur) { case 'A': case 'a': case 'B': case 'b': case 'C': case 'c': case 'D': case 'd': case 'E': case 'e': case 'F': case 'f': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': cur++; break; default: { char next = *cur; *cur = '\0'; errno = 0; char *end; TUInteger ui = strtoul(start,&end,16); if (end != cur) throw parser_error(line,cur-lastbr,"Malformed hex number"); if (ui == ULONG_MAX && errno == ERANGE) throw parser_error(line,cur-lastbr,"Unsigned integer out of bounds."); *cur = next; return Value(ui); } } } } else { throw parser_error(line,cur-lastbr,"Malformed hex number"); } case 'e': //exponential exp = true; cur++; break; case 'u': //non-json explicit unsigned *cur = '\0'; cur++; { char *end; Value v((TUInteger)strtoul(start,&end,10)); if (end != cur-1) throw parser_error(line,cur-lastbr,"Malformed integer"); return v; } case 'd': //non-json explicit real OR strange exponential switch (cur[1]) { case '+': case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (exp) throw parser_error(line,cur-lastbr,"Malformed exponential"); exp = true; } if (exp) { *cur = 'e'; //this is ugly but the syntax I'm trying to parse is also ugly cur++; break; } //intentional fallthrough case 'f': //non-json explicit real *cur = '\0'; cur++; { char *end; Value v((TReal)strtod(start,&end)); if (end != cur-1) throw parser_error(line,cur-lastbr,"Malformed real"); return v; } case '.': //real real = true; case '+': case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': cur++; break; default: { //any other character is end of number char next = *cur; *cur = '\0'; Value val; if (real || exp) { char *end; val = Value((TReal)strtod(start,&end)); if (end != cur) throw parser_error(line,cur-lastbr,"Malformed real"); } else { errno = 0; char *end; TInteger i = strtol(start,&end,10); if (end != cur) throw parser_error(line,cur-lastbr,"Malformed integer"); if (i == LONG_MIN && errno == ERANGE) throw parser_error(line,cur-lastbr,"Signed integer out of bounds."); if (i == LONG_MAX && errno == ERANGE) { errno = 0; TUInteger ui = strtoul(start,NULL,10); if (ui == ULONG_MAX && errno == ERANGE) throw parser_error(line,cur-lastbr,"Unsigned integer out of bounds."); val = Value(ui); } else { val = Value(i); } } *cur = next; return val; } } } throw parser_error(line,cur-lastbr,"Should never reach here. Probably hardware error."); } Value Reader::readString() { char *start = ++cur; for (;;) { switch (*(cur++)) { case '\\': cur++; //definitely an escape, so skip next character break; case '\"': cur[-1] = '\0'; return Value(unescapeString(std::string(start))); case '\0': throw parser_error(line,cur-lastbr,"Reached EOF while parsing string"); } } throw parser_error(line,cur-lastbr,"Should never reach here. Probably hardware error."); } Value Reader::readObject() { Value object = Value(); object.reset(TOBJECT); char *key = NULL; bool keyfound = false; Value val = Value(); cur++; for (;;) { switch (*cur) { case '/': //non-json comment skipComment(); break; case '\n': line++; case '\r': lastbr = cur+1; case ' ': case '\t': if (key && !keyfound) { *cur = '\0'; keyfound = true; } cur++; break; case '}': cur++; if (key) { throw parser_error(line,cur-lastbr,"} found where value expected"); } return object; case ',': cur++; if (key) { throw parser_error(line,cur-lastbr,", found where value expected"); } break; case ':': if (!key) { throw parser_error(line,cur-lastbr,": found where field expected"); } if (key && !keyfound) *cur = '\0'; cur++; if (!getValue(val)) { throw parser_error(line,cur-lastbr,"EOF reached while parsing object"); } object.setMember(std::string(key),val); key = NULL; keyfound = false; break; case '\"': cur++; key = cur; readString(); keyfound = true; break; case '\0': throw parser_error(line,cur-lastbr,"Reached EOF while parsing object"); default: if (keyfound) { throw parser_error(line,cur-lastbr,"Unexpected character where value expected"); } if (!key) key = cur; cur++; } } throw parser_error(line,cur-lastbr,"Should never reach here. Probably hardware error."); } Value Reader::readArray() { Value array = Value(); array.reset(TARRAY); Value next = Value(); cur++; for (;;) { switch (*cur) { case '/': //non-json comment skipComment(); break; case '\n': line++; case '\r': lastbr = cur+1; case ' ': case '\t': case ',': cur++; break; case ':': { //non-json value repetition cur++; Value reps; if (!getValue(reps) || reps.getType() != TINTEGER || reps.getInteger() < 0) { throw parser_error(line,cur-lastbr,"Array value repetition syntax error"); } const int nreps = reps.getInteger(); // The value to be repeated has already been pushed once if (nreps == 0) { array.data.array->pop_back(); } else { array.data.array->reserve(array.data.array->size() + nreps - 1); for (int i = 1; i < nreps; i++) { array.data.array->push_back(next); } } break; } case ']': cur++; return array; case '\0': throw parser_error(line,cur-lastbr,"Reached EOF while parsing array"); default: if (!getValue(next)) { throw parser_error(line,cur-lastbr,"EOF reached while parsing array"); } array.data.array->push_back(next); } } throw parser_error(line,cur-lastbr,"Should never reach here. Probably hardware error."); } Writer::Writer(std::ostream &stream) : out(stream) { } Writer::~Writer() { } void Writer::putValue(const Value &value) { writeValue(value,""); out << '\n'; } void Writer::writeValue(const Value &value, const std::string &depth) { switch (value.type) { case TINTEGER: out << value.data.integer; break; case TUINTEGER: out << value.data.uinteger; break; case TREAL: out.precision(std::numeric_limits::digits10); out << value.data.real; break; case TSTRING: out << '"' << escapeString(*(value.data.string)) << '"'; break; case TOBJECT: { const std::string nextdepth(depth+" "); TObject::iterator it = value.data.object->begin(); TObject::iterator end = value.data.object->end(); out << "{\n"; if (it != end) { out << nextdepth << '\"' << it->first << "\" : "; writeValue(it->second,nextdepth); it++; } for ( ; it != end; it++) { out << ",\n" << nextdepth << '\"' << it->first << "\" : "; writeValue(it->second,nextdepth); } out << '\n' << depth << '}'; } break; case TARRAY: { TArray::iterator it = value.data.array->begin(); TArray::iterator end = value.data.array->end(); out << '['; if (it != end) { writeValue(*it); it++; } for ( ; it != end; it++) { out << ", "; writeValue(*it); } out << ']'; } break; case TNULL: out << "null"; break; case TBOOL: out << (value.data.boolean ? "true" : "false"); } } //https://tools.ietf.org/rfc/rfc7159.txt std::string Writer::escapeString(std::string unescaped) { std::stringstream escaped; size_t last = 0, pos = 0, len = unescaped.length(); while (pos < len) { switch (unescaped[pos]) { case '"': case '\\': case '/': escaped << unescaped.substr(last,pos-last) << '\\' << unescaped[pos]; last = pos+1; break; case '\b': escaped << unescaped.substr(last,pos-last) << "\\b"; last = pos+1; break; case '\f': escaped << unescaped.substr(last,pos-last) << "\\f"; last = pos+1; break; case '\n': escaped << unescaped.substr(last,pos-last) << "\\n"; last = pos+1; break; case '\r': escaped << unescaped.substr(last,pos-last) << "\\r"; last = pos+1; break; case '\t': escaped << unescaped.substr(last,pos-last) << "\\t"; last = pos+1; break; default: if (unescaped[pos] < 0x20) throw parser_error(0,0,"Arbitrary unicode escapes not yet supported"); //FIXME } pos++; } escaped << unescaped.substr(last,pos-last); return escaped.str(); } //https://tools.ietf.org/rfc/rfc7159.txt std::string Reader::unescapeString(std::string escaped) { if (escaped.find("\\") != std::string::npos) { size_t last = 0, pos = 0; std::stringstream unescaped; while ((pos = escaped.find("\\",pos)) != std::string::npos) { unescaped << escaped.substr(last,pos-last); switch (escaped[pos+1]) { case '"': case '\\': case '/': unescaped << escaped[pos+1]; last = pos = pos+2; break; case 'b': unescaped << '\b'; last = pos = pos+2; break; case 'f': unescaped << '\f'; last = pos = pos+2; break; case 'n': unescaped << '\n'; last = pos = pos+2; break; case 'r': unescaped << '\r'; last = pos = pos+2; break; case 't': unescaped << '\t'; last = pos = pos+2; break; case 'u': throw parser_error(line,cur-lastbr,"Arbitrary unicode escapes not yet supported"); //FIXME default: throw parser_error(line,cur-lastbr,"Invalid escape sequence in string"); } } unescaped << escaped.substr(last); return unescaped.str(); } else { return escaped; } } }