Skip to content

Commit

Permalink
json: Escape strings in the json writer.
Browse files Browse the repository at this point in the history
  • Loading branch information
unknownbrackets committed Jun 6, 2018
1 parent 07e178a commit dfef902
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 15 deletions.
95 changes: 81 additions & 14 deletions ext/native/json/json_writer.cpp
@@ -1,6 +1,9 @@
#include <iomanip>
#include "json/json_writer.h"

JsonWriter::JsonWriter() {
JsonWriter::JsonWriter(int flags) {
pretty_ = (flags & PRETTY) != 0;
str_.imbue(std::locale::classic());
}

JsonWriter::~JsonWriter() {
Expand All @@ -13,15 +16,24 @@ void JsonWriter::begin() {

void JsonWriter::end() {
pop();
str_ << "\n";
if (pretty_)
str_ << "\n";
}

const char *JsonWriter::indent(int n) const {
static const char * const whitespace = " ";
if (!pretty_)
return "";
static const char * const whitespace = " ";
if (n > 32) {
// Avoid crash.
return whitespace;
}
return whitespace + (32 - n);
}

const char *JsonWriter::indent() const {
if (!pretty_)
return "";
int amount = (int)stack_.size() + 1;
amount *= 2; // 2-space indent.
return indent(amount);
Expand All @@ -37,25 +49,29 @@ const char *JsonWriter::comma() const {
if (stack_.back().first) {
return "";
} else {
return ",";
return ",\n";
}
}

const char *JsonWriter::arrayComma() const {
if (stack_.back().first) {
return "\n";
} else {
return ", ";
return pretty_ ? ", " : ",";
}
}

void JsonWriter::pushDict(const char *name) {
str_ << comma() << "\n" << indent() << "\"" << name << "\": {";
str_ << comma() << indent() << "\"";
writeEscapedString(name);
str_ << "\": {";
stack_.push_back(StackEntry(DICT));
}

void JsonWriter::pushArray(const char *name) {
str_ << comma() << "\n" << indent() << "\"" << name << "\": [";
str_ << comma() << indent() << "\"";
writeEscapedString(name);
str_ << "\": [";
stack_.push_back(StackEntry(ARRAY));
}

Expand All @@ -65,7 +81,9 @@ void JsonWriter::writeBool(bool value) {
}

void JsonWriter::writeBool(const char *name, bool value) {
str_ << comma() << "\n" << indent() << "\"" << name << "\": " << (value ? "true" : "false");
str_ << comma() << indent() << "\"";
writeEscapedString(name);
str_ << "\": " << (value ? "true" : "false");
stack_.back().first = false;
}

Expand All @@ -75,7 +93,9 @@ void JsonWriter::writeInt(int value) {
}

void JsonWriter::writeInt(const char *name, int value) {
str_ << comma() << "\n" << indent() << "\"" << name << "\": " << value;
str_ << comma() << indent() << "\"";
writeEscapedString(name);
str_ << "\": " << value;
stack_.back().first = false;
}

Expand All @@ -85,31 +105,78 @@ void JsonWriter::writeFloat(double value) {
}

void JsonWriter::writeFloat(const char *name, double value) {
str_ << comma() << "\n" << indent() << "\"" << name << "\": " << value;
str_ << comma() << indent() << "\"";
writeEscapedString(name);
str_ << "\": " << value;
stack_.back().first = false;
}

void JsonWriter::writeString(const char *value) {
str_ << arrayComma() << arrayIndent() << "\"" << value << "\"";
str_ << arrayComma() << arrayIndent() << "\"";
writeEscapedString(value);
str_ << "\"";
stack_.back().first = false;
}

void JsonWriter::writeString(const char *name, const char *value) {
str_ << comma() << "\n" << indent() << "\"" << name << "\": \"" << value << "\"";
str_ << comma() << indent() << "\"";
writeEscapedString(name);
str_ << "\": \"";
writeEscapedString(value);
str_ << "\"";
stack_.back().first = false;
}

void JsonWriter::pop() {
BlockType type = stack_.back().type;
stack_.pop_back();
if (pretty_)
str_ << "\n" << indent();
switch (type) {
case ARRAY:
str_ << "\n" << indent() << "]";
str_ << "]";
break;
case DICT:
str_ << "\n" << indent() << "}";
str_ << "}";
break;
}
if (stack_.size() > 0)
stack_.back().first = false;
}

void JsonWriter::writeEscapedString(const char *str) {
size_t pos = 0;
size_t len = strlen(str);

auto update = [&](size_t current, size_t skip = 0) {
size_t end = current;
if (pos < end)
str_ << std::string(str + pos, end - pos);
pos = end + skip;
};

for (size_t i = 0; i < len; ++i) {
if (str[i] == '\\' || str[i] == '"' || str[i] == '/') {
update(i);
str_ << '\\';
} else if (str[i] == '\r') {
update(i, 1);
str_ << "\\r";
} else if (str[i] == '\n') {
update(i, 1);
str_ << "\\n";
} else if (str[i] == '\t') {
update(i, 1);
str_ << "\\t";
} else if (str[i] < 32) {
update(i, 1);
str_ << "\\u" << std::hex << std::setw(4) << std::setfill('0') << (int)str[i] << std::dec << std::setw(0);
}
}

if (pos != 0) {
update(len);
} else {
str_ << str;
}
}
19 changes: 18 additions & 1 deletion ext/native/json/json_writer.h
Expand Up @@ -16,7 +16,7 @@

class JsonWriter {
public:
JsonWriter();
JsonWriter(int flags = NORMAL);
~JsonWriter();
void begin();
void end();
Expand All @@ -31,17 +31,33 @@ class JsonWriter {
void writeFloat(const char *name, double value);
void writeString(const char *value);
void writeString(const char *name, const char *value);
void writeString(const std::string &value) {
writeString(value.c_str());
}
void writeString(const char *name, const std::string &value) {
writeString(name, value.c_str());
}
void writeString(const std::string &name, const std::string &value) {
writeString(name.c_str(), value.c_str());
}

std::string str() const {
return str_.str();
}

enum {
NORMAL = 0,
PRETTY = 1,
};

private:
const char *indent(int n) const;
const char *comma() const;
const char *arrayComma() const;
const char *indent() const;
const char *arrayIndent() const;
void writeEscapedString(const char *s);

enum BlockType {
ARRAY,
DICT,
Expand All @@ -53,4 +69,5 @@ class JsonWriter {
};
std::vector<StackEntry> stack_;
std::ostringstream str_;
bool pretty_;
};

0 comments on commit dfef902

Please sign in to comment.