diff --git a/JsonGeneratorTests/JsonArray.h b/JsonGeneratorTests/JsonArray.h index 96d7aaa6..f3bc4564 100644 --- a/JsonGeneratorTests/JsonArray.h +++ b/JsonGeneratorTests/JsonArray.h @@ -37,9 +37,10 @@ private: JsonValue items[N]; int itemCount; - virtual void writeTo(StringBuilder& sb) + virtual void writeTo(JsonSink& sb) { sb.append("["); + sb.reserveRoom(1); for (int i = 0; i < itemCount; i++) { @@ -47,6 +48,7 @@ private: items[i].writeTo(sb); } + sb.releaseRoom(1); sb.append("]"); } }; diff --git a/JsonGeneratorTests/JsonGeneratorTests.vcxproj b/JsonGeneratorTests/JsonGeneratorTests.vcxproj index 18e185bf..d50f2a04 100644 --- a/JsonGeneratorTests/JsonGeneratorTests.vcxproj +++ b/JsonGeneratorTests/JsonGeneratorTests.vcxproj @@ -86,13 +86,13 @@ - - + + diff --git a/JsonGeneratorTests/JsonGeneratorTests.vcxproj.filters b/JsonGeneratorTests/JsonGeneratorTests.vcxproj.filters index b6aff73e..4a87129b 100644 --- a/JsonGeneratorTests/JsonGeneratorTests.vcxproj.filters +++ b/JsonGeneratorTests/JsonGeneratorTests.vcxproj.filters @@ -21,23 +21,17 @@ Source Files - - Source Files - - - Source Files - Source Files Source Files + + Source Files + - - Header Files - Header Files @@ -50,5 +44,11 @@ Header Files + + Header Files + + + Header Files + \ No newline at end of file diff --git a/JsonGeneratorTests/JsonHashTable.h b/JsonGeneratorTests/JsonHashTable.h index 878915ed..f5a58393 100644 --- a/JsonGeneratorTests/JsonHashTable.h +++ b/JsonGeneratorTests/JsonHashTable.h @@ -45,19 +45,24 @@ private: KeyValuePair items[N]; int itemCount; - virtual void writeTo(StringBuilder& sb) + virtual void writeTo(JsonSink& sink) { - sb.append("{"); + sink.append("{"); + sink.reserveRoom(1); for (int i = 0; i < itemCount; i++) { - if (i>0) sb.append(","); - sb.appendEscaped(items[i].key); - sb.append(":"); - items[i].value.writeTo(sb); + if (i>0) sink.append(","); + + JsonValue key(items[i].key); + + key.writeTo(sink); + sink.append(":"); + items[i].value.writeTo(sink); } - sb.append("}"); + sink.releaseRoom(1); + sink.append("}"); } }; diff --git a/JsonGeneratorTests/JsonObjectBase.h b/JsonGeneratorTests/JsonObjectBase.h index c9939250..fe9ee4e0 100644 --- a/JsonGeneratorTests/JsonObjectBase.h +++ b/JsonGeneratorTests/JsonObjectBase.h @@ -6,6 +6,7 @@ #pragma once #include "JsonValue.h" +#include "JsonSink.h" class JsonObjectBase { @@ -17,6 +18,6 @@ public: writeTo(sb); } - virtual void writeTo(StringBuilder& sb) = 0; + virtual void writeTo(JsonSink& sb) = 0; }; diff --git a/JsonGeneratorTests/JsonSink.h b/JsonGeneratorTests/JsonSink.h new file mode 100644 index 00000000..cd8344ac --- /dev/null +++ b/JsonGeneratorTests/JsonSink.h @@ -0,0 +1,19 @@ +/* + * Arduino JSON library + * Benoit Blanchon 2014 - MIT License + */ + +#pragma once + +class JsonSink +{ +public: + + virtual void append(char c) = 0; + virtual void append(const char* s) = 0; + + virtual bool hasRoomFor(int n) = 0; + virtual void reserveRoom(int n) = 0; + virtual void releaseRoom(int n) = 0; +}; + diff --git a/JsonGeneratorTests/JsonValue.cpp b/JsonGeneratorTests/JsonValue.cpp index 032c28be..26bc42f0 100644 --- a/JsonGeneratorTests/JsonValue.cpp +++ b/JsonGeneratorTests/JsonValue.cpp @@ -5,26 +5,88 @@ #include "JsonValue.h" #include "JsonObjectBase.h" +#include +#include -void JsonValue::writeBooleanTo(StringBuilder& sb) +void JsonValue::writeBooleanTo(JsonSink& sb) { sb.append(content.boolean ? "true" : "false"); } -void JsonValue::writeNumberTo(StringBuilder& sb) +void JsonValue::writeNumberTo(JsonSink& sb) { - sb.append(content.number); + char tmp[16]; + + _snprintf(tmp, sizeof(tmp), "%lg", content.number); + + sb.append(tmp); } -void JsonValue::writeObjectTo(StringBuilder& sb) +void JsonValue::writeObjectTo(JsonSink& sink) { if (content.object) - ((JsonObjectBase*) content.object)->writeTo(sb); + ((JsonObjectBase*) content.object)->writeTo(sink); else - sb.append("null"); + sink.append("null"); } -void JsonValue::writeStringTo(StringBuilder& sb) +void JsonValue::writeStringTo(JsonSink& sink) { - sb.appendEscaped(content.string); + auto s = content.string; + + if (!s) + { + return sink.append("null"); + } + + if (!sink.hasRoomFor(2)) + { + return; + } + + sink.append('\"'); + sink.reserveRoom(1); + + while (*s) + { + switch (*s) + { + case '"': + sink.append("\\\""); + break; + + case '\\': + sink.append("\\\\"); + break; + + case '\b': + sink.append("\\b"); + break; + + case '\f': + sink.append("\\f"); + break; + + case '\n': + sink.append("\\n"); + break; + + case '\r': + sink.append("\\r"); + break; + + case '\t': + sink.append("\\t"); + break; + + default: + sink.append(*s); + break; + } + + s++; + } + + sink.releaseRoom(1); + sink.append('\"'); } \ No newline at end of file diff --git a/JsonGeneratorTests/JsonValue.h b/JsonGeneratorTests/JsonValue.h index 3f9d8f0b..1cf1ee6d 100644 --- a/JsonGeneratorTests/JsonValue.h +++ b/JsonGeneratorTests/JsonValue.h @@ -41,10 +41,10 @@ public: content.object = &value; } - void writeTo(StringBuilder& sb) + void writeTo(JsonSink& sink) { // handmade polymorphism - (this->*implementation)(sb); + (this->*implementation)(sink); } private: @@ -59,10 +59,10 @@ private: Content content; - void (JsonValue::*implementation)(StringBuilder& sb); + void (JsonValue::*implementation)(JsonSink& sb); - void writeBooleanTo(StringBuilder& sb); - void writeNumberTo(StringBuilder& sb); - void writeObjectTo(StringBuilder& sb); - void writeStringTo(StringBuilder& sb); + void writeBooleanTo(JsonSink& sb); + void writeNumberTo(JsonSink& sb); + void writeObjectTo(JsonSink& sb); + void writeStringTo(JsonSink& sb); }; \ No newline at end of file diff --git a/JsonGeneratorTests/StringBuilderAppendEscapedTests.cpp b/JsonGeneratorTests/JsonValueTests.cpp similarity index 78% rename from JsonGeneratorTests/StringBuilderAppendEscapedTests.cpp rename to JsonGeneratorTests/JsonValueTests.cpp index 96e07581..ecc0a2b3 100644 --- a/JsonGeneratorTests/StringBuilderAppendEscapedTests.cpp +++ b/JsonGeneratorTests/JsonValueTests.cpp @@ -1,11 +1,12 @@ #include "CppUnitTest.h" #include "StringBuilder.h" +#include "JsonValue.h" using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace JsonGeneratorTests { - TEST_CLASS(StringBuilderAppendEscapedTests) + TEST_CLASS(JsonValueTests) { char buffer[20]; StringBuilder* sb; @@ -16,24 +17,24 @@ namespace JsonGeneratorTests { sb = new StringBuilder(buffer, sizeof(buffer)); } - + TEST_METHOD(InitialState) { assertResultIs(""); } + TEST_METHOD(Null) + { + append((char*)0); + assertResultIs("null"); + } + TEST_METHOD(EmptyString) { append(""); assertResultIs("\"\""); } - TEST_METHOD(Null) - { - append(NULL); - assertResultIs("null"); - } - TEST_METHOD(OneString) { append("ABCD"); @@ -55,16 +56,23 @@ namespace JsonGeneratorTests append(""); assertResultIs("\"ABCDEFGHIJKLMNOPQ\""); } - + TEST_METHOD(SpecialChars) { append("\\\"\b\f\n\r\t"); assertResultIs("\"\\\\\\\"\\b\\f\\n\\r\\t\""); } - - void append(const char* s) + + TEST_METHOD(Number) { - sb->appendEscaped(s); + append(3.14); + assertResultIs("3.14"); + } + + template + void append(T value) + { + JsonValue(value).writeTo(*sb); } void assertResultIs(const char* expected) diff --git a/JsonGeneratorTests/StringBuilder.cpp b/JsonGeneratorTests/StringBuilder.cpp index 610c9fc7..f7d2058b 100644 --- a/JsonGeneratorTests/StringBuilder.cpp +++ b/JsonGeneratorTests/StringBuilder.cpp @@ -3,27 +3,10 @@ * Benoit Blanchon 2014 - MIT License */ -#include -#include - #include "StringBuilder.h" -void StringBuilder::append(double value) -{ - char* tail = buffer + length; - - _snprintf(tail, capacity - length, "%lg", value); - - length += strlen(tail); -} - void StringBuilder::append(const char* s) { - if (!s) - { - return append("null"); - } - char* tail = buffer + length; while (*s && length= capacity) return; - if (length > capacity - 2) - { - // not enough from for quotes - return; - } - - buffer[length++] = '"'; - - // keep one slot for the end quote - capacity--; - - while (*s && length < capacity) - { - switch (*s) - { - case '"': - append("\\\""); - break; - - case '\\': - append("\\\\"); - break; - - case '\b': - append("\\b"); - break; - - case '\f': - append("\\f"); - break; - - case '\n': - append("\\n"); - break; - - case '\r': - append("\\r"); - break; - - case '\t': - append("\\t"); - break; - - default: - buffer[length++] = *s; - break; - } - - s++; - } - - buffer[length++] = '"'; + buffer[length++] = c; buffer[length] = 0; - - // restore the original capacity - capacity++; } \ No newline at end of file diff --git a/JsonGeneratorTests/StringBuilder.h b/JsonGeneratorTests/StringBuilder.h index 83989c73..886ac063 100644 --- a/JsonGeneratorTests/StringBuilder.h +++ b/JsonGeneratorTests/StringBuilder.h @@ -5,22 +5,38 @@ #pragma once -class StringBuilder +#include "JsonSink.h" + +class StringBuilder : public JsonSink { public: - StringBuilder(char* buf, size_t size) + StringBuilder(char* buf, int size) : buffer(buf), capacity(size-1), length(0) { buffer[0] = 0; } - void append(double); - void append(const char* s); - void appendEscaped(const char* s); + virtual void append(char c); + virtual void append(const char* s); + + virtual bool hasRoomFor(int n) + { + return capacity - length >= n; + } + + virtual void reserveRoom(int n) + { + capacity -= n; + } + + virtual void releaseRoom(int n) + { + capacity += n; + } private: char* buffer; - size_t capacity; - size_t length; + int capacity; + int length; }; diff --git a/JsonGeneratorTests/StringBuilderAppendTests.cpp b/JsonGeneratorTests/StringBuilderAppendTests.cpp deleted file mode 100644 index 0730d3a6..00000000 --- a/JsonGeneratorTests/StringBuilderAppendTests.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "CppUnitTest.h" -#include "StringBuilder.h" - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - -namespace JsonGeneratorTests -{ - TEST_CLASS(StringBuilderAppendTests) - { - char buffer[16]; - StringBuilder* sb; - - public: - - TEST_METHOD_INITIALIZE(Initialize) - { - sb = new StringBuilder(buffer, sizeof(buffer)); - } - - TEST_METHOD(InitialState) - { - assertResultIs(""); - } - - TEST_METHOD(EmptyString) - { - append(""); - assertResultIs(""); - } - - TEST_METHOD(Null) - { - append((char*)0); - assertResultIs("null"); - } - - TEST_METHOD(Number) - { - append(3.14); - assertResultIs("3.14"); - } - - TEST_METHOD(OneString) - { - append("ABCD"); - assertResultIs("ABCD"); - } - - TEST_METHOD(TwoStrings) - { - append("ABCD"); - append("EFGH"); - assertResultIs("ABCDEFGH"); - } - - TEST_METHOD(OverCapacity) - { - append("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - assertResultIs("ABCDEFGHIJKLMNO"); - } - - TEST_METHOD(SpecialChars) - { - append("\\\"\b\f\n\r"); - assertResultIs("\\\"\b\f\n\r"); - } - - void append(double d) - { - sb->append(d); - } - - void append(const char* s) - { - sb->append(s); - } - - void assertResultIs(const char* expected) - { - Assert::AreEqual(expected, buffer); - } - }; -} \ No newline at end of file