#include "JSONNode.h" #ifdef JSON_WRITE_PRIORITY #include "JSONWorker.h" #include "JSONGlobals.h" extern bool used_ascii_one; #ifdef JSON_INDENT inline json_string makeIndent(unsigned int amount) json_nothrow json_write_priority; inline json_string makeIndent(unsigned int amount) json_nothrow { if (amount == 0xFFFFFFFF) return json_global(EMPTY_JSON_STRING); json_string result; result.reserve(amount * json_global(INDENT).length()); for(unsigned int i = 0; i < amount; ++i){ result += json_global(INDENT); } JSON_ASSERT(result.capacity() == amount * json_global(INDENT).length(), JSON_TEXT("makeIndent made a string too big")); return result; } #else inline json_string makeIndent(unsigned int amount) json_nothrow { if (amount == 0xFFFFFFFF) return json_global(EMPTY_JSON_STRING); if (json_likely(amount < 8)){ static const json_string cache[] = { json_string(), json_string(JSON_TEXT("\t")), json_string(JSON_TEXT("\t\t")), json_string(JSON_TEXT("\t\t\t")), json_string(JSON_TEXT("\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t\t")) }; return cache[amount]; } #ifndef JSON_LESS_MEMORY if (json_likely(amount < 16)){ static const json_string cache[] = { json_string(JSON_TEXT("\t\t\t\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")) }; return cache[amount - 8]; } #if JSON_WRITE_PRIORITY == HIGH if (json_likely(amount < 24)){ static const json_string cache[] = { json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")), json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")) }; return cache[amount - 16]; } #endif #endif return json_string(amount, JSON_TEXT('\t')); } #endif void internalJSONNode::WriteName(bool formatted, bool arrayChild, json_string & output) const json_nothrow { if (!arrayChild){ output += JSON_TEXT("\""); JSONWorker::UnfixString(_name, _name_encoded, output); output += ((formatted) ? JSON_TEXT("\" : ") : JSON_TEXT("\":")); } } void internalJSONNode::WriteChildren(unsigned int indent, json_string & output) const json_nothrow { //Iterate through the children and write them if (json_likely(CHILDREN -> empty())) return; json_string indent_plus_one; //handle whether or not it's formatted JSON if (indent != 0xFFFFFFFF){ //it's formatted, make the indentation strings indent_plus_one = json_global(NEW_LINE) + makeIndent(++indent); } //else it's not formatted, leave the indentation strings empty const size_t size_minus_one = CHILDREN -> size() - 1; size_t i = 0; JSONNode ** it = CHILDREN -> begin(); for(JSONNode ** it_end = CHILDREN -> end(); it != it_end; ++it, ++i){ output += indent_plus_one; (*it) -> internal -> Write(indent, type() == JSON_ARRAY, output); if (json_likely(i < size_minus_one)) output += JSON_TEXT(','); //the last one does not get a comma, but all of the others do } if (indent != 0xFFFFFFFF){ output += json_global(NEW_LINE); output += makeIndent(indent - 1); } } #ifdef JSON_ARRAY_SIZE_ON_ONE_LINE void internalJSONNode::WriteChildrenOneLine(unsigned int indent, json_string & output) const json_nothrow { //Iterate through the children and write them if (json_likely(CHILDREN -> empty())) return; if ((*CHILDREN -> begin()) -> internal -> isContainer()) return WriteChildren(indent, output); json_string comma(JSON_TEXT(",")); if (indent != 0xFFFFFFFF){ comma += JSON_TEXT(' '); } //else it's not formatted, leave the indentation strings empty const size_t size_minus_one = CHILDREN -> size() - 1; size_t i = 0; JSONNode ** it = CHILDREN -> begin(); for(JSONNode ** it_end = CHILDREN -> end(); it != it_end; ++it, ++i){ (*it) -> internal -> Write(indent, type() == JSON_ARRAY, output); if (json_likely(i < size_minus_one)) output += comma; //the last one does not get a comma, but all of the others do } } #endif #ifdef JSON_COMMENTS void internalJSONNode::WriteComment(unsigned int indent, json_string & output) const json_nothrow { if (indent == 0xFFFFFFFF) return; if (json_likely(_comment.empty())) return; size_t pos = _comment.find(JSON_TEXT('\n')); const json_string current_indent(json_global(NEW_LINE) + makeIndent(indent)); if (json_likely(pos == json_string::npos)){ //Single line comment output += current_indent; output += json_global(SINGLELINE_COMMENT); output.append(_comment.begin(), _comment.end()); output += current_indent; return; } /* Multiline comments */ output += current_indent; #if !(defined(JSON_WRITE_BASH_COMMENTS) || defined(JSON_WRITE_SINGLE_LINE_COMMENTS)) const json_string current_indent_plus_one(json_global(NEW_LINE) + makeIndent(indent + 1)); output += JSON_TEXT("/*"); output += current_indent_plus_one; #endif size_t old = 0; while(pos != json_string::npos){ if (json_unlikely(pos && _comment[pos - 1] == JSON_TEXT('\r'))) --pos; #if defined(JSON_WRITE_BASH_COMMENTS) || defined(JSON_WRITE_SINGLE_LINE_COMMENTS) output += json_global(SINGLELINE_COMMENT); #endif output.append(_comment.begin() + old, _comment.begin() + pos); #if defined(JSON_WRITE_BASH_COMMENTS) || defined(JSON_WRITE_SINGLE_LINE_COMMENTS) output += current_indent; #else output += current_indent_plus_one; #endif old = (_comment[pos] == JSON_TEXT('\r')) ? pos + 2 : pos + 1; pos = _comment.find(JSON_TEXT('\n'), old); } #if defined(JSON_WRITE_BASH_COMMENTS) || defined(JSON_WRITE_SINGLE_LINE_COMMENTS) output += json_global(SINGLELINE_COMMENT); #endif output.append(_comment.begin() + old, _comment.end()); output += current_indent; #if !(defined(JSON_WRITE_BASH_COMMENTS) || defined(JSON_WRITE_SINGLE_LINE_COMMENTS)) output += JSON_TEXT("*/"); output += current_indent; #endif } #else inline void internalJSONNode::WriteComment(unsigned int, json_string &) const json_nothrow {} #endif void internalJSONNode::DumpRawString(json_string & output) const json_nothrow { //first remove the \1 characters if (used_ascii_one){ //if it hasn't been used yet, don't bother checking json_string result(_string.begin(), _string.end()); for(json_string::iterator beg = result.begin(), en = result.end(); beg != en; ++beg){ if (*beg == JSON_TEXT('\1')) *beg = JSON_TEXT('\"'); } output += result; return; } else { output.append(_string.begin(), _string.end()); } } void internalJSONNode::Write(unsigned int indent, bool arrayChild, json_string & output) const json_nothrow { const bool formatted = indent != 0xFFFFFFFF; WriteComment(indent, output); #if !defined(JSON_PREPARSE) && defined(JSON_READ_PRIORITY) if (!(formatted || fetched)){ //It's not formatted or fetched, just do a raw dump WriteName(false, arrayChild, output); //first remove the \1 characters DumpRawString(output); return; } #endif WriteName(formatted, arrayChild, output); //It's either formatted or fetched switch (_type){ case JSON_NODE: //got members, write the members Fetch(); output += JSON_TEXT("{"); WriteChildren(indent, output); output += JSON_TEXT("}"); return; case JSON_ARRAY: //write out the child nodes int he array Fetch(); output += JSON_TEXT("["); #ifdef JSON_ARRAY_SIZE_ON_ONE_LINE if (size() <= JSON_ARRAY_SIZE_ON_ONE_LINE){ WriteChildrenOneLine(indent, output); } else { #endif WriteChildren(indent, output); #ifdef JSON_ARRAY_SIZE_ON_ONE_LINE } #endif output += JSON_TEXT("]"); return; case JSON_NUMBER: //write out a literal, without quotes case JSON_NULL: case JSON_BOOL: output.append(_string.begin(), _string.end()); return; } JSON_ASSERT(_type == JSON_STRING, JSON_TEXT("Unknown json node type")); //If it go here, then it's a json_string #if !defined(JSON_PREPARSE) && defined(JSON_READ_PRIORITY) if (json_likely(fetched)){ #endif output += JSON_TEXT("\""); JSONWorker::UnfixString(_string, _string_encoded, output); //It's already been fetched, meaning that it's unescaped output += JSON_TEXT("\""); #if !defined(JSON_PREPARSE) && defined(JSON_READ_PRIORITY) } else { DumpRawString(output); //it hasn't yet been fetched, so it's already unescaped, just do a dump } #endif } #endif