diff --git a/CHANGELOG.md b/CHANGELOG.md index bdcb93cc..e9a1240e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ HEAD * `JsonPair::key()` now returns a `JsonKey` * Increased the default capacity of `DynamicJsonDocument` * Fixed `JsonVariant::is()` (closes #763) +* Added `JsonArrayConst`, `JsonObjectConst`, and `JsonVariantConst` v6.4.0-beta (2018-09-11) ----------- diff --git a/src/ArduinoJson.hpp b/src/ArduinoJson.hpp index 5b7c016c..12d0e9da 100644 --- a/src/ArduinoJson.hpp +++ b/src/ArduinoJson.hpp @@ -9,7 +9,9 @@ #include "ArduinoJson/JsonArray.hpp" #include "ArduinoJson/JsonDocument.hpp" #include "ArduinoJson/JsonObject.hpp" +#include "ArduinoJson/JsonVariant.hpp" +#include "ArduinoJson/Data/VariantAsImpl.hpp" #include "ArduinoJson/JsonArrayImpl.hpp" #include "ArduinoJson/JsonArraySubscript.hpp" #include "ArduinoJson/JsonObjectImpl.hpp" @@ -26,13 +28,16 @@ namespace ArduinoJson { using ARDUINOJSON_NAMESPACE::DeserializationError; using ARDUINOJSON_NAMESPACE::DynamicJsonDocument; using ARDUINOJSON_NAMESPACE::JsonArray; +using ARDUINOJSON_NAMESPACE::JsonArrayConst; using ARDUINOJSON_NAMESPACE::JsonFloat; using ARDUINOJSON_NAMESPACE::JsonInteger; using ARDUINOJSON_NAMESPACE::JsonKey; using ARDUINOJSON_NAMESPACE::JsonObject; +using ARDUINOJSON_NAMESPACE::JsonObjectConst; using ARDUINOJSON_NAMESPACE::JsonPair; using ARDUINOJSON_NAMESPACE::JsonUInt; using ARDUINOJSON_NAMESPACE::JsonVariant; +using ARDUINOJSON_NAMESPACE::JsonVariantConst; using ARDUINOJSON_NAMESPACE::serialized; using ARDUINOJSON_NAMESPACE::StaticJsonDocument; } // namespace ArduinoJson diff --git a/src/ArduinoJson/Data/ArrayFunctions.hpp b/src/ArduinoJson/Data/ArrayFunctions.hpp new file mode 100644 index 00000000..3b680f49 --- /dev/null +++ b/src/ArduinoJson/Data/ArrayFunctions.hpp @@ -0,0 +1,99 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "JsonVariantData.hpp" +#include "Slot.hpp" +#include "SlotFunctions.hpp" + +namespace ARDUINOJSON_NAMESPACE { + +inline JsonVariantData* arrayAdd(JsonArrayData* arr, MemoryPool* pool) { + if (!arr) return 0; + + Slot* slot = new (pool) Slot(); + if (!slot) return 0; + + slot->next = 0; + + if (arr->tail) { + slot->prev = arr->tail; + arr->tail->next = slot; + arr->tail = slot; + } else { + slot->prev = 0; + arr->head = slot; + arr->tail = slot; + } + + return &slot->value; +} + +inline Slot* arrayGetSlot(const JsonArrayData* arr, size_t index) { + if (!arr) return 0; + return slotAdvance(arr->head, index); +} + +inline JsonVariantData* arrayGet(const JsonArrayData* arr, size_t index) { + Slot* slot = arrayGetSlot(arr, index); + return slot ? &slot->value : 0; +} + +inline void arrayRemove(JsonArrayData* arr, Slot* slot) { + if (!arr || !slot) return; + + if (slot->prev) + slot->prev->next = slot->next; + else + arr->head = slot->next; + if (slot->next) + slot->next->prev = slot->prev; + else + arr->tail = slot->prev; +} + +inline void arrayRemove(JsonArrayData* arr, size_t index) { + arrayRemove(arr, arrayGetSlot(arr, index)); +} + +inline void arrayClear(JsonArrayData* arr) { + if (!arr) return; + arr->head = 0; + arr->tail = 0; +} + +bool variantCopy(JsonVariantData*, const JsonVariantData*, MemoryPool*); + +inline bool arrayCopy(JsonArrayData* dst, const JsonArrayData* src, + MemoryPool* pool) { + if (!dst || !src) return false; + arrayClear(dst); + for (Slot* s = src->head; s; s = s->next) { + if (!variantCopy(arrayAdd(dst, pool), &s->value, pool)) return false; + } + return true; +} + +bool variantEquals(const JsonVariantData*, const JsonVariantData*); + +inline bool arrayEquals(const JsonArrayData* a1, const JsonArrayData* a2) { + if (a1 == a2) return true; + if (!a1 || !a2) return false; + Slot* s1 = a1->head; + Slot* s2 = a2->head; + for (;;) { + if (s1 == s2) return true; + if (!s1 || !s2) return false; + if (!variantEquals(&s1->value, &s2->value)) return false; + s1 = s1->next; + s2 = s2->next; + } +} + +inline size_t arraySize(const JsonArrayData* arr) { + if (!arr) return 0; + return slotSize(arr->head); +} +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Data/IsVariant.hpp b/src/ArduinoJson/Data/IsVariant.hpp deleted file mode 100644 index ae3659b2..00000000 --- a/src/ArduinoJson/Data/IsVariant.hpp +++ /dev/null @@ -1,15 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include "../Polyfills/type_traits.hpp" - -namespace ARDUINOJSON_NAMESPACE { - -class JsonVariantTag {}; - -template -struct IsVariant : is_base_of {}; -} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Data/JsonVariantAs.hpp b/src/ArduinoJson/Data/JsonVariantAs.hpp index 34b4a6b2..2d0738da 100644 --- a/src/ArduinoJson/Data/JsonVariantAs.hpp +++ b/src/ArduinoJson/Data/JsonVariantAs.hpp @@ -6,6 +6,13 @@ namespace ARDUINOJSON_NAMESPACE { +class JsonArray; +class JsonArrayConst; +class JsonObject; +class JsonObjectConst; +class JsonVariant; +class JsonVariantConst; + // A metafunction that returns the type of the value returned by // JsonVariant::as() template @@ -18,4 +25,25 @@ struct JsonVariantAs { typedef const char* type; }; +// A metafunction that returns the type of the value returned by +// JsonVariant::as() +template +struct JsonVariantConstAs { + typedef typename JsonVariantAs::type type; +}; + +template <> +struct JsonVariantConstAs { + typedef JsonVariantConst type; +}; + +template <> +struct JsonVariantConstAs { + typedef JsonObjectConst type; +}; + +template <> +struct JsonVariantConstAs { + typedef JsonArrayConst type; +}; } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Data/ObjectFunctions.hpp b/src/ArduinoJson/Data/ObjectFunctions.hpp new file mode 100644 index 00000000..cb5a3457 --- /dev/null +++ b/src/ArduinoJson/Data/ObjectFunctions.hpp @@ -0,0 +1,123 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "JsonVariantData.hpp" +#include "SlotFunctions.hpp" + +namespace ARDUINOJSON_NAMESPACE { + +template +inline Slot* objectFindSlot(const JsonObjectData* obj, TKey key) { + if (!obj) return 0; + Slot* slot = obj->head; + while (slot) { + if (key.equals(slot->key)) break; + slot = slot->next; + } + return slot; +} + +template +inline bool objectContainsKey(const JsonObjectData* obj, const TKey& key) { + return objectFindSlot(obj, key) != 0; +} + +template +inline JsonVariantData* objectAdd(JsonObjectData* obj, TKey key, + MemoryPool* pool) { + Slot* slot = new (pool) Slot(); + if (!slot) return 0; + + slot->next = 0; + + if (obj->tail) { + slot->prev = obj->tail; + obj->tail->next = slot; + obj->tail = slot; + } else { + slot->prev = 0; + obj->head = slot; + obj->tail = slot; + } + + if (!slotSetKey(slot, key, pool)) return 0; + return &slot->value; +} + +template +inline JsonVariantData* objectSet(JsonObjectData* obj, TKey key, + MemoryPool* pool) { + if (!obj) return 0; + + // ignore null key + if (key.isNull()) return 0; + + // search a matching key + Slot* slot = objectFindSlot(obj, key); + if (slot) return &slot->value; + + return objectAdd(obj, key, pool); +} + +template +inline JsonVariantData* objectGet(const JsonObjectData* obj, TKey key) { + Slot* slot = objectFindSlot(obj, key); + return slot ? &slot->value : 0; +} + +inline void objectClear(JsonObjectData* obj) { + if (!obj) return; + obj->head = 0; + obj->tail = 0; +} + +inline void objectRemove(JsonObjectData* obj, Slot* slot) { + if (!obj) return; + if (!slot) return; + if (slot->prev) + slot->prev->next = slot->next; + else + obj->head = slot->next; + if (slot->next) + slot->next->prev = slot->prev; + else + obj->tail = slot->prev; +} + +inline size_t objectSize(const JsonObjectData* obj) { + if (!obj) return 0; + return slotSize(obj->head); +} + +// bool variantCopy(JsonVariantData*, const JsonVariantData*, MemoryPool*); + +inline bool objectCopy(JsonObjectData* dst, const JsonObjectData* src, + MemoryPool* pool) { + if (!dst || !src) return false; + objectClear(dst); + for (Slot* s = src->head; s; s = s->next) { + JsonVariantData* var; + if (s->value.keyIsStatic) + var = objectAdd(dst, ZeroTerminatedRamStringConst(s->key), pool); + else + var = objectAdd(dst, ZeroTerminatedRamString(s->key), pool); + if (!variantCopy(var, &s->value, pool)) return false; + } + return true; +} + +inline bool objectEquals(const JsonObjectData* o1, const JsonObjectData* o2) { + if (o1 == o2) return true; + if (!o1 || !o2) return false; + + for (Slot* s = o1->head; s; s = s->next) { + JsonVariantData* v1 = &s->value; + JsonVariantData* v2 = objectGet(o2, makeString(s->key)); + if (!variantEquals(v1, v2)) return false; + } + return true; +} +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Data/SlotFunctions.hpp b/src/ArduinoJson/Data/SlotFunctions.hpp new file mode 100644 index 00000000..9c3dc4ad --- /dev/null +++ b/src/ArduinoJson/Data/SlotFunctions.hpp @@ -0,0 +1,59 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../Strings/StringTypes.hpp" +#include "JsonVariantData.hpp" +#include "Slot.hpp" + +namespace ARDUINOJSON_NAMESPACE { + +template +inline bool slotSetKey(Slot* slot, TKey key, MemoryPool* pool) { + const char* dup = key.save(pool); + if (!dup) return false; + slot->key = dup; + slot->value.keyIsStatic = false; + return true; +} + +inline bool slotSetKey(Slot* slot, ZeroTerminatedRamStringConst key, + MemoryPool* pool) { + slot->key = key.save(pool); + slot->value.keyIsStatic = true; + return true; +} + +inline bool slotSetKey(Slot* slot, StringInMemoryPool key, MemoryPool* pool) { + slot->key = key.save(pool); + slot->value.keyIsStatic = false; + return true; +} + +inline const Slot* slotAdvance(const Slot* slot, size_t distance) { + while (distance && slot) { + slot = slot->next; + distance--; + } + return slot; +} + +inline Slot* slotAdvance(Slot* slot, size_t distance) { + while (distance && slot) { + slot = slot->next; + distance--; + } + return slot; +} + +inline size_t slotSize(const Slot* slot) { + size_t n = 0; + while (slot) { + n++; + slot = slot->next; + } + return n; +} +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Data/VariantAs.hpp b/src/ArduinoJson/Data/VariantAs.hpp new file mode 100644 index 00000000..9f3de6ee --- /dev/null +++ b/src/ArduinoJson/Data/VariantAs.hpp @@ -0,0 +1,48 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../Serialization/DynamicStringWriter.hpp" +#include "VariantFunctions.hpp" + +namespace ARDUINOJSON_NAMESPACE { + +class JsonVariantConst; + +template +inline typename enable_if::value, T>::type variantAs( + const JsonVariantData* _data) { + return variantAsIntegral(_data); +} + +template +inline typename enable_if::value, T>::type variantAs( + const JsonVariantData* _data) { + return variantAsBoolean(_data); +} + +template +inline typename enable_if::value, T>::type variantAs( + const JsonVariantData* _data) { + return variantAsFloat(_data); +} + +template +inline typename enable_if::value || + is_same::value, + const char*>::type +variantAs(const JsonVariantData* _data) { + return variantAsString(_data); +} + +template +inline typename enable_if::value, T>::type +variantAs(const JsonVariantData* _data); + +template +inline typename enable_if::value, T>::type variantAs( + const JsonVariantData* _data); + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Data/VariantAsImpl.hpp b/src/ArduinoJson/Data/VariantAsImpl.hpp new file mode 100644 index 00000000..64471607 --- /dev/null +++ b/src/ArduinoJson/Data/VariantAsImpl.hpp @@ -0,0 +1,29 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../JsonVariant.hpp" +#include "../Serialization/DynamicStringWriter.hpp" +#include "VariantFunctions.hpp" + +namespace ARDUINOJSON_NAMESPACE { + +template +inline typename enable_if::value, T>::type +variantAs(const JsonVariantData* _data) { + return JsonVariantConst(_data); +} + +template +inline typename enable_if::value, T>::type variantAs( + const JsonVariantData* _data) { + const char* cstr = variantAsString(_data); + if (cstr) return T(cstr); + T s; + serializeJson(JsonVariantConst(_data), s); + return s; +} + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Data/VariantFunctions.hpp b/src/ArduinoJson/Data/VariantFunctions.hpp new file mode 100644 index 00000000..fba9abd9 --- /dev/null +++ b/src/ArduinoJson/Data/VariantFunctions.hpp @@ -0,0 +1,287 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../Numbers/parseFloat.hpp" +#include "../Numbers/parseInteger.hpp" +#include "../SerializedValue.hpp" +#include "ArrayFunctions.hpp" +#include "JsonVariantData.hpp" +#include "ObjectFunctions.hpp" +#include "Slot.hpp" + +namespace ARDUINOJSON_NAMESPACE { + +template +inline T variantAsIntegral(const JsonVariantData* var) { + if (!var) return 0; + switch (var->type) { + case JSON_POSITIVE_INTEGER: + case JSON_BOOLEAN: + return T(var->content.asInteger); + case JSON_NEGATIVE_INTEGER: + return T(~var->content.asInteger + 1); + case JSON_LINKED_STRING: + case JSON_OWNED_STRING: + return parseInteger(var->content.asString); + case JSON_FLOAT: + return T(var->content.asFloat); + default: + return 0; + } +} + +inline bool variantAsBoolean(const JsonVariantData* var) { + return variantAsIntegral(var) != 0; +} + +// T = float/double +template +inline T variantAsFloat(const JsonVariantData* var) { + if (!var) return 0; + switch (var->type) { + case JSON_POSITIVE_INTEGER: + case JSON_BOOLEAN: + return static_cast(var->content.asInteger); + case JSON_NEGATIVE_INTEGER: + return -static_cast(var->content.asInteger); + case JSON_LINKED_STRING: + case JSON_OWNED_STRING: + return parseFloat(var->content.asString); + case JSON_FLOAT: + return static_cast(var->content.asFloat); + default: + return 0; + } +} + +inline const char* variantAsString(const JsonVariantData* var) { + if (!var) return 0; + if (var && + (var->type == JSON_LINKED_STRING || var->type == JSON_OWNED_STRING)) + return var->content.asString; + else + return 0; +} + +inline JsonArrayData* variantAsArray(JsonVariantData* var) { + if (var && var->type == JSON_ARRAY) + return &var->content.asArray; + else + return 0; +} + +inline const JsonArrayData* variantAsArray(const JsonVariantData* var) { + if (var && var->type == JSON_ARRAY) + return &var->content.asArray; + else + return 0; +} + +inline JsonObjectData* variantAsObject(JsonVariantData* var) { + if (var && var->type == JSON_OBJECT) + return &var->content.asObject; + else + return 0; +} + +inline const JsonObjectData* variantAsObject(const JsonVariantData* var) { + if (var && var->type == JSON_OBJECT) + return &var->content.asObject; + else + return 0; +} + +inline bool variantSetBoolean(JsonVariantData* var, bool value) { + if (!var) return false; + var->type = JSON_BOOLEAN; + var->content.asInteger = static_cast(value); + return true; +} + +inline bool variantSetFloat(JsonVariantData* var, JsonFloat value) { + if (!var) return false; + var->type = JSON_FLOAT; + var->content.asFloat = value; + return true; +} + +template +inline bool variantSetSignedInteger(JsonVariantData* var, T value) { + if (!var) return false; + if (value >= 0) { + var->type = JSON_POSITIVE_INTEGER; + var->content.asInteger = static_cast(value); + } else { + var->type = JSON_NEGATIVE_INTEGER; + var->content.asInteger = ~static_cast(value) + 1; + } + return true; +} + +inline bool variantSetSignedInteger(JsonVariantData* var, JsonUInt value) { + if (!var) return false; + var->type = JSON_POSITIVE_INTEGER; + var->content.asInteger = static_cast(value); + return true; +} + +inline bool variantSetLinkedRaw(JsonVariantData* var, + SerializedValue value) { + if (!var) return false; + var->type = JSON_LINKED_RAW; + var->content.asRaw.data = value.data(); + var->content.asRaw.size = value.size(); + return true; +} + +template +inline bool variantSetOwnedRaw(JsonVariantData* var, SerializedValue value, + MemoryPool* pool) { + if (!var) return false; + const char* dup = makeString(value.data(), value.size()).save(pool); + if (dup) { + var->type = JSON_OWNED_RAW; + var->content.asRaw.data = dup; + var->content.asRaw.size = value.size(); + return true; + } else { + var->type = JSON_NULL; + return false; + } +} + +template +inline bool variantSetString(JsonVariantData* var, T value, MemoryPool* pool) { + if (!var) return false; + const char* dup = value.save(pool); + if (dup) { + var->type = JSON_OWNED_STRING; + var->content.asString = dup; + return true; + } else { + var->type = JSON_NULL; + return false; + } +} + +inline bool variantSetString(JsonVariantData* var, const char* value) { + if (!var) return false; + var->type = JSON_LINKED_STRING; + var->content.asString = value; + return true; +} + +inline bool variantSetString(JsonVariantData* var, const char* value, + MemoryPool* pool) { + return variantSetString(var, makeString(const_cast(value)), pool); +} + +inline void variantSetNull(JsonVariantData* var) { + if (var) var->type = JSON_NULL; +} + +inline JsonArrayData* variantToArray(JsonVariantData* var) { + if (!var) return 0; + var->type = JSON_ARRAY; + var->content.asArray.head = 0; + var->content.asArray.tail = 0; + return &var->content.asArray; +} + +inline JsonObjectData* variantToObject(JsonVariantData* var) { + if (!var) return 0; + var->type = JSON_OBJECT; + var->content.asObject.head = 0; + var->content.asObject.tail = 0; + return &var->content.asObject; +} + +inline bool variantCopy(JsonVariantData* dst, const JsonVariantData* src, + MemoryPool* pool) { + if (!dst) return false; + if (!src) { + dst->type = JSON_NULL; + return true; + } + switch (src->type) { + case JSON_ARRAY: + return arrayCopy(variantToArray(dst), &src->content.asArray, pool); + case JSON_OBJECT: + return objectCopy(variantToObject(dst), &src->content.asObject, pool); + case JSON_OWNED_STRING: + return variantSetString(dst, src->content.asString, pool); + case JSON_OWNED_RAW: + return variantSetOwnedRaw( + dst, + serialized(const_cast(src->content.asRaw.data), + src->content.asRaw.size), + pool); + default: + *dst = *src; + return true; + } +} + +inline bool variantIsInteger(const JsonVariantData* var) { + return var && (var->type == JSON_POSITIVE_INTEGER || + var->type == JSON_NEGATIVE_INTEGER); +} + +inline bool variantIsFloat(const JsonVariantData* var) { + return var && + (var->type == JSON_FLOAT || var->type == JSON_POSITIVE_INTEGER || + var->type == JSON_NEGATIVE_INTEGER); +} + +inline bool variantIsString(const JsonVariantData* var) { + return var && + (var->type == JSON_LINKED_STRING || var->type == JSON_OWNED_STRING); +} + +inline bool variantIsArray(const JsonVariantData* var) { + return var && var->type == JSON_ARRAY; +} + +inline bool variantIsObject(const JsonVariantData* var) { + return var && var->type == JSON_OBJECT; +} + +inline bool variantIsNull(const JsonVariantData* var) { + return var == 0 || var->type == JSON_NULL; +} + +inline bool variantEquals(const JsonVariantData* a, const JsonVariantData* b) { + if (a == b) return true; + if (!a || !b) return false; + if (a->type != b->type) return false; + + switch (a->type) { + case JSON_LINKED_RAW: + case JSON_OWNED_RAW: + case JSON_LINKED_STRING: + case JSON_OWNED_STRING: + return !strcmp(a->content.asString, b->content.asString); + + case JSON_BOOLEAN: + case JSON_POSITIVE_INTEGER: + case JSON_NEGATIVE_INTEGER: + return a->content.asInteger == b->content.asInteger; + + case JSON_ARRAY: + return arrayEquals(&a->content.asArray, &b->content.asArray); + + case JSON_OBJECT: + return objectEquals(&a->content.asObject, &b->content.asObject); + + case JSON_FLOAT: + return a->content.asFloat == b->content.asFloat; + + case JSON_NULL: + default: + return true; + } +} +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Json/JsonSerializer.hpp b/src/ArduinoJson/Json/JsonSerializer.hpp index 47cc9a82..c47f339a 100644 --- a/src/ArduinoJson/Json/JsonSerializer.hpp +++ b/src/ArduinoJson/Json/JsonSerializer.hpp @@ -19,10 +19,10 @@ class JsonSerializer { _writer.writeFloat(value); } - void visitArray(JsonArray array) { + void visitArray(JsonArrayConst array) { _writer.beginArray(); - JsonArray::iterator it = array.begin(); + JsonArrayConst::iterator it = array.begin(); while (it != array.end()) { it->accept(*this); @@ -35,10 +35,10 @@ class JsonSerializer { _writer.endArray(); } - void visitObject(JsonObject object) { + void visitObject(JsonObjectConst object) { _writer.beginObject(); - JsonObject::iterator it = object.begin(); + JsonObjectConst::iterator it = object.begin(); while (it != object.end()) { _writer.writeString(it->key()); _writer.writeColon(); @@ -115,6 +115,10 @@ inline std::ostream &operator<<(std::ostream &os, const JsonVariant &source) { serializeJson(source, os); return os; } +inline std::ostream &operator<<(std::ostream &os, JsonVariantConst source) { + os << serializeJson(source, os); + return os; +} inline std::ostream &operator<<(std::ostream &os, const JsonArraySubscript &source) { diff --git a/src/ArduinoJson/JsonArray.hpp b/src/ArduinoJson/JsonArray.hpp index 8d248fb2..f3a85e73 100644 --- a/src/ArduinoJson/JsonArray.hpp +++ b/src/ArduinoJson/JsonArray.hpp @@ -4,6 +4,7 @@ #pragma once +#include "Data/ArrayFunctions.hpp" #include "Data/JsonVariantData.hpp" #include "JsonArrayIterator.hpp" @@ -17,60 +18,91 @@ namespace ARDUINOJSON_NAMESPACE { class JsonObject; class JsonArraySubscript; -class JsonArray { - friend class JsonVariant; +template +class JsonArrayProxy { + public: + FORCE_INLINE bool isNull() const { + return _data == 0; + } + + FORCE_INLINE JsonVariantConst operator[](size_t index) const { + return JsonVariantConst(arrayGet(_data, index)); + } + + FORCE_INLINE size_t size() const { + return arraySize(_data); + } + + protected: + JsonArrayProxy(TData* data) : _data(data) {} + TData* _data; +}; + +class JsonArrayConst : public JsonArrayProxy { + friend class JsonArray; + typedef JsonArrayProxy proxy_type; + + public: + typedef JsonArrayConstIterator iterator; + + FORCE_INLINE iterator begin() const { + if (!_data) return iterator(); + return iterator(_data->head); + } + + FORCE_INLINE iterator end() const { + return iterator(); + } + + FORCE_INLINE JsonArrayConst() : proxy_type(0) {} + FORCE_INLINE JsonArrayConst(const JsonArrayData* data) : proxy_type(data) {} + + FORCE_INLINE bool operator==(JsonArrayConst rhs) const { + return arrayEquals(_data, rhs._data); + } +}; + +class JsonArray : public JsonArrayProxy { + typedef JsonArrayProxy proxy_type; public: typedef JsonArrayIterator iterator; - FORCE_INLINE JsonArray() : _memoryPool(0), _data(0) {} - FORCE_INLINE JsonArray(MemoryPool* pool, JsonArrayData* arr) - : _memoryPool(pool), _data(arr) {} + FORCE_INLINE JsonArray() : proxy_type(0), _memoryPool(0) {} + FORCE_INLINE JsonArray(MemoryPool* pool, JsonArrayData* data) + : proxy_type(data), _memoryPool(pool) {} operator JsonVariant() { return JsonVariant(_memoryPool, getVariantData(_data)); } + operator JsonArrayConst() const { + return JsonArrayConst(_data); + } + // Adds the specified value at the end of the array. // // bool add(TValue); // TValue = bool, long, int, short, float, double, serialized, JsonVariant, // std::string, String, JsonObject template - FORCE_INLINE bool add(const T& value) { + FORCE_INLINE bool add(const T& value) const { return add().set(value); } // Adds the specified value at the end of the array. - FORCE_INLINE bool add(JsonArray value) { + FORCE_INLINE bool add(JsonArray value) const { return add().set(value); } // // bool add(TValue); // TValue = char*, const char*, const FlashStringHelper* template - FORCE_INLINE bool add(T* value) { + FORCE_INLINE bool add(T* value) const { return add().set(value); } - JsonVariant add() { - if (!_data) return JsonVariant(); - - Slot* slot = new (_memoryPool) Slot(); - if (!slot) return JsonVariant(); - - slot->next = 0; - - if (_data->tail) { - slot->prev = _data->tail; - _data->tail->next = slot; - _data->tail = slot; - } else { - slot->prev = 0; - _data->head = slot; - _data->tail = slot; - } - - return JsonVariant(_memoryPool, &slot->value); + JsonVariant add() const { + return JsonVariant(_memoryPool, arrayAdd(_data, _memoryPool)); } FORCE_INLINE iterator begin() const { @@ -84,13 +116,13 @@ class JsonArray { // Imports a 1D array template - FORCE_INLINE bool copyFrom(T (&array)[N]) { + FORCE_INLINE bool copyFrom(T (&array)[N]) const { return copyFrom(array, N); } // Imports a 1D array template - bool copyFrom(T* array, size_t len) { + bool copyFrom(T* array, size_t len) const { bool ok = true; for (size_t i = 0; i < len; i++) { ok &= add(array[i]); @@ -100,7 +132,7 @@ class JsonArray { // Imports a 2D array template - bool copyFrom(T (&array)[N1][N2]) { + bool copyFrom(T (&array)[N1][N2]) const { bool ok = true; for (size_t i = 0; i < N1; i++) { JsonArray nestedArray = createNestedArray(); @@ -112,12 +144,8 @@ class JsonArray { } // Copy a JsonArray - bool copyFrom(JsonArray src) { - bool ok = _data != 0; - for (iterator it = src.begin(); it != src.end(); ++it) { - ok &= add(*it); - } - return ok; + FORCE_INLINE bool copyFrom(JsonArray src) const { + return arrayCopy(_data, src._data, _memoryPool); } // Exports a 1D array @@ -144,102 +172,54 @@ class JsonArray { } } - FORCE_INLINE JsonArray createNestedArray(); - FORCE_INLINE JsonObject createNestedObject(); + FORCE_INLINE JsonArray createNestedArray() const; + FORCE_INLINE JsonObject createNestedObject() const; - FORCE_INLINE JsonArraySubscript operator[](size_t index); - - FORCE_INLINE const JsonArraySubscript operator[](size_t index) const; + FORCE_INLINE JsonArraySubscript operator[](size_t index) const; FORCE_INLINE bool operator==(JsonArray rhs) const { - iterator it1 = begin(); - iterator it2 = rhs.begin(); - for (;;) { - if (it1 == end() && it2 == rhs.end()) return true; - if (it1 == end()) return false; - if (it2 == end()) return false; - if (*it1 != *it2) return false; - ++it1; - ++it2; - } + return arrayEquals(_data, rhs._data); } // Gets the value at the specified index. template FORCE_INLINE typename JsonVariantAs::type get(size_t index) const { - iterator it = begin() += index; - return it != end() ? it->as() : T(); + return get_impl(index).as(); } // Check the type of the value at specified index. template FORCE_INLINE bool is(size_t index) const { - iterator it = begin() += index; - return it != end() ? it->is() : false; + return get_impl(index).is(); } // Removes element at specified position. - FORCE_INLINE void remove(iterator it) { - if (!_data) return; - - Slot* slot = it.internal(); - if (!slot) return; - - if (slot->prev) - slot->prev->next = slot->next; - else - _data->head = slot->next; - if (slot->next) - slot->next->prev = slot->prev; - else - _data->tail = slot->prev; + FORCE_INLINE void remove(iterator it) const { + arrayRemove(_data, it.internal()); } // Removes element at specified index. - FORCE_INLINE void remove(size_t index) { - remove(begin() += index); + FORCE_INLINE void remove(size_t index) const { + arrayRemove(_data, index); } // Sets the value at specified index. // // bool add(size_t index, const TValue&); // TValue = bool, long, int, short, float, double, serialized, JsonVariant, - // std::string, String, JsonArrayData, JsonObject + // std::string, String, JsonArray, JsonObject template - FORCE_INLINE bool set(size_t index, const T& value) { + FORCE_INLINE bool set(size_t index, const T& value) const { if (!_data) return false; - return set_impl(index, value); + return get_impl(index).set(value); } // // bool add(size_t index, TValue); // TValue = char*, const char*, const FlashStringHelper* template - FORCE_INLINE bool set(size_t index, T* value) { + FORCE_INLINE bool set(size_t index, T* value) const { if (!_data) return false; - return set_impl(index, value); - } - // Sets the value at specified index. - // - // bool add(size_t index, JsonArray); - template - FORCE_INLINE bool set(size_t index, JsonArray value) { - if (!_data) return false; - return get(index).set(value); - } - - FORCE_INLINE size_t size() const { - if (!_data) return 0; - Slot* slot = _data->head; - size_t n = 0; - while (slot) { - slot = slot->next; - n++; - } - return n; - } - - FORCE_INLINE bool isNull() const { - return _data == 0; + return get_impl(index).set(value); } template @@ -252,18 +232,14 @@ class JsonArray { private: template - FORCE_INLINE bool set_impl(size_t index, TValueRef value) { - iterator it = begin() += index; - if (it == end()) return false; - return it->set(value); - } - - template - FORCE_INLINE bool add_impl(TValueRef value) { + FORCE_INLINE bool add_impl(TValueRef value) const { return add().set(value); } + FORCE_INLINE JsonVariant get_impl(size_t index) const { + return JsonVariant(_memoryPool, arrayGet(_data, index)); + } + MemoryPool* _memoryPool; - JsonArrayData* _data; }; } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/JsonArrayImpl.hpp b/src/ArduinoJson/JsonArrayImpl.hpp index d3da8409..02b2a60c 100644 --- a/src/ArduinoJson/JsonArrayImpl.hpp +++ b/src/ArduinoJson/JsonArrayImpl.hpp @@ -9,11 +9,11 @@ namespace ARDUINOJSON_NAMESPACE { -inline JsonArray JsonArray::createNestedArray() { +inline JsonArray JsonArray::createNestedArray() const { return add().to(); } -inline JsonObject JsonArray::createNestedObject() { +inline JsonObject JsonArray::createNestedObject() const { return add().to(); } } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/JsonArrayIterator.hpp b/src/ArduinoJson/JsonArrayIterator.hpp index 5cdfef23..73fa285a 100644 --- a/src/ArduinoJson/JsonArrayIterator.hpp +++ b/src/ArduinoJson/JsonArrayIterator.hpp @@ -5,6 +5,7 @@ #pragma once #include "Data/Slot.hpp" +#include "Data/SlotFunctions.hpp" #include "JsonVariant.hpp" namespace ARDUINOJSON_NAMESPACE { @@ -29,8 +30,8 @@ class JsonVariantPtr { class JsonArrayIterator { public: JsonArrayIterator() : _slot(0) {} - explicit JsonArrayIterator(MemoryPool *memoryPool, Slot *iterator) - : _memoryPool(memoryPool), _slot(iterator) {} + explicit JsonArrayIterator(MemoryPool *memoryPool, Slot *slot) + : _memoryPool(memoryPool), _slot(slot) {} JsonVariant operator*() const { return JsonVariant(_memoryPool, &_slot->value); @@ -53,10 +54,7 @@ class JsonArrayIterator { } JsonArrayIterator &operator+=(size_t distance) { - while (distance && _slot) { - _slot = _slot->next; - distance--; - } + _slot = slotAdvance(_slot, distance); return *this; } @@ -68,4 +66,58 @@ class JsonArrayIterator { MemoryPool *_memoryPool; Slot *_slot; }; + +class JsonVariantConstPtr { + public: + JsonVariantConstPtr(const JsonVariantData *data) : _variant(data) {} + + JsonVariantConst *operator->() { + return &_variant; + } + + JsonVariantConst &operator*() { + return _variant; + } + + private: + JsonVariantConst _variant; +}; + +class JsonArrayConstIterator { + public: + JsonArrayConstIterator() : _slot(0) {} + explicit JsonArrayConstIterator(const Slot *slot) : _slot(slot) {} + + JsonVariantConst operator*() const { + return JsonVariantConst(&_slot->value); + } + JsonVariantConstPtr operator->() { + return JsonVariantConstPtr(&_slot->value); + } + + bool operator==(const JsonArrayConstIterator &other) const { + return _slot == other._slot; + } + + bool operator!=(const JsonArrayConstIterator &other) const { + return _slot != other._slot; + } + + JsonArrayConstIterator &operator++() { + _slot = _slot->next; + return *this; + } + + JsonArrayConstIterator &operator+=(size_t distance) { + _slot = slotAdvance(_slot, distance); + return *this; + } + + const Slot *internal() { + return _slot; + } + + private: + const Slot *_slot; +}; } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/JsonArraySubscript.hpp b/src/ArduinoJson/JsonArraySubscript.hpp index 839e8de7..a44c0202 100644 --- a/src/ArduinoJson/JsonArraySubscript.hpp +++ b/src/ArduinoJson/JsonArraySubscript.hpp @@ -19,7 +19,7 @@ class JsonArraySubscript : public JsonVariantBase { : _array(array), _index(index) {} FORCE_INLINE JsonArraySubscript& operator=(const JsonArraySubscript& src) { - _array.set(_index, src.as()); + get_impl().set(src.as()); return *this; } @@ -30,7 +30,7 @@ class JsonArraySubscript : public JsonVariantBase { // std::string, String, JsonArray, JsonObject template FORCE_INLINE JsonArraySubscript& operator=(const T& src) { - _array.set(_index, src); + get_impl().set(src); return *this; } // @@ -38,7 +38,7 @@ class JsonArraySubscript : public JsonVariantBase { // TValue = char*, const char*, const FlashStringHelper* template FORCE_INLINE JsonArraySubscript& operator=(T* src) { - _array.set(_index, src); + get_impl().set(src); return *this; } @@ -57,7 +57,7 @@ class JsonArraySubscript : public JsonVariantBase { } template - FORCE_INLINE typename JsonVariantTo::type to() { + FORCE_INLINE typename JsonVariantTo::type to() const { return _array.get(_index).to(); } @@ -67,44 +67,42 @@ class JsonArraySubscript : public JsonVariantBase { // TValue = bool, long, int, short, float, double, serialized, JsonVariant, // std::string, String, JsonArray, JsonObject template - FORCE_INLINE bool set(const TValue& value) { - return _array.set(_index, value); + FORCE_INLINE bool set(const TValue& value) const { + return get_impl().set(value); } // // bool set(TValue) // TValue = char*, const char*, const FlashStringHelper* template - FORCE_INLINE bool set(TValue* value) { - return _array.set(_index, value); + FORCE_INLINE bool set(TValue* value) const { + return get_impl().set(value); } template void accept(Visitor& visitor) const { - return _array.get(_index).accept(visitor); + return get_impl().accept(visitor); + } + + FORCE_INLINE size_t size() const { + return get_impl().size(); } private: + JsonVariant get_impl() const { + return _array.get(_index); + } + JsonArray _array; const size_t _index; }; template inline JsonArraySubscript JsonVariantSubscripts::operator[]( - size_t index) { - return impl()->template as()[index]; -} - -template -inline const JsonArraySubscript JsonVariantSubscripts::operator[]( size_t index) const { return impl()->template as()[index]; } -inline JsonArraySubscript JsonArray::operator[](size_t index) { - return JsonArraySubscript(*this, index); -} - -inline const JsonArraySubscript JsonArray::operator[](size_t index) const { +inline JsonArraySubscript JsonArray::operator[](size_t index) const { return JsonArraySubscript(*this, index); } } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/JsonDocument.hpp b/src/ArduinoJson/JsonDocument.hpp index f7b1c110..b5478372 100644 --- a/src/ArduinoJson/JsonDocument.hpp +++ b/src/ArduinoJson/JsonDocument.hpp @@ -24,7 +24,12 @@ class JsonDocument { } template - typename JsonVariantAs::type as() const { + typename JsonVariantAs::type as() { + return getVariant().template as(); + } + + template + typename JsonVariantConstAs::type as() const { return getVariant().template as(); } @@ -53,12 +58,16 @@ class JsonDocument { } private: - JsonVariant getVariant() const { + JsonVariant getVariant() { return JsonVariant(&_memoryPool, &_rootData); } - mutable TMemoryPool _memoryPool; - mutable JsonVariantData _rootData; + JsonVariantConst getVariant() const { + return JsonVariantConst(&_rootData); + } + + TMemoryPool _memoryPool; + JsonVariantData _rootData; }; class DynamicJsonDocument : public JsonDocument { diff --git a/src/ArduinoJson/JsonKey.hpp b/src/ArduinoJson/JsonKey.hpp new file mode 100644 index 00000000..34c69dd6 --- /dev/null +++ b/src/ArduinoJson/JsonKey.hpp @@ -0,0 +1,37 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ARDUINOJSON_NAMESPACE { + +class JsonKey { + public: + JsonKey(const Slot* slot) : _slot(slot) {} + + operator const char*() const { + return c_str(); + } + + const char* c_str() const { + return _slot ? _slot->key : 0; + } + + bool isNull() const { + return _slot == 0 || _slot->key == 0; + } + + bool isStatic() const { + return _slot ? _slot->value.keyIsStatic : true; + } + + friend bool operator==(JsonKey lhs, const char* rhs) { + if (lhs.isNull()) return rhs == 0; + return rhs ? !strcmp(lhs, rhs) : false; + } + + private: + const Slot* _slot; +}; +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/JsonObject.hpp b/src/ArduinoJson/JsonObject.hpp index 39dfe6f3..964e02b2 100644 --- a/src/ArduinoJson/JsonObject.hpp +++ b/src/ArduinoJson/JsonObject.hpp @@ -4,7 +4,8 @@ #pragma once -#include "./JsonObjectIterator.hpp" +#include "Data/ObjectFunctions.hpp" +#include "JsonObjectIterator.hpp" // Returns the size (in bytes) of an object with n elements. // Can be very handy to determine the size of a StaticMemoryPool. @@ -13,88 +14,162 @@ namespace ARDUINOJSON_NAMESPACE { -class JsonObject { - friend class JsonVariant; +template +class JsonObjectProxy { + public: + // Tells weither the specified key is present and associated with a value. + // + // bool containsKey(TKey); + // TKey = const std::string&, const String& + template + FORCE_INLINE bool containsKey(const TKey& key) const { + return objectContainsKey(_data, makeString(key)); + } + // + // bool containsKey(TKey); + // TKey = char*, const char*, char[], const char[], const FlashStringHelper* + template + FORCE_INLINE bool containsKey(TKey* key) const { + return objectContainsKey(_data, makeString(key)); + } + + FORCE_INLINE size_t size() const { + return objectSize(_data); + } + + protected: + JsonObjectProxy(TData* data) : _data(data) {} + TData* _data; +}; + +class JsonObjectConst : public JsonObjectProxy { + friend class JsonObject; + typedef JsonObjectProxy proxy_type; + + public: + typedef JsonObjectConstIterator iterator; + + JsonObjectConst() : proxy_type(0) {} + JsonObjectConst(const JsonObjectData* data) : proxy_type(data) {} + + FORCE_INLINE iterator begin() const { + if (!_data) return iterator(); + return iterator(_data->head); + } + + FORCE_INLINE iterator end() const { + return iterator(); + } + + // Gets the value associated with the specified key. + // + // TValue get(TKey) const; + // TKey = const std::string&, const String& + // TValue = bool, char, long, int, short, float, double, + // std::string, String, JsonArrayConst, JsonObjectConst + template + FORCE_INLINE typename JsonVariantAs::type get(const TKey& key) const { + return get_impl(makeString(key)).template as(); + } + // + // TValue get(TKey) const; + // TKey = char*, const char*, const FlashStringHelper* + // TValue = bool, char, long, int, short, float, double, + // std::string, String, JsonArrayConst, JsonObjectConst + template + FORCE_INLINE typename JsonVariantAs::type get(TKey* key) const { + return get_impl(makeString(key)).template as(); + } + + // + // JsonVariantConst operator[](TKey) const; + // TKey = const std::string&, const String& + template + FORCE_INLINE typename enable_if::value, JsonVariantConst>::type + operator[](const TKey& key) const { + return get_impl(makeString(key)); + } + // + // JsonVariantConst operator[](TKey) const; + // TKey = const char*, const char[N], const FlashStringHelper* + template + FORCE_INLINE + typename enable_if::value, JsonVariantConst>::type + operator[](TKey* key) const { + return get_impl(makeString(key)); + } + + FORCE_INLINE bool operator==(JsonObjectConst rhs) const { + return objectEquals(_data, rhs._data); + } + + private: + template + FORCE_INLINE JsonVariantConst get_impl(TKey key) const { + return JsonVariantConst(objectGet(_data, key)); + } +}; + +class JsonObject : public JsonObjectProxy { + typedef JsonObjectProxy proxy_type; public: typedef JsonObjectIterator iterator; - FORCE_INLINE JsonObject() : _memoryPool(0), _data(0) {} - FORCE_INLINE JsonObject(MemoryPool* buf, JsonObjectData* object) - : _memoryPool(buf), _data(object) {} + FORCE_INLINE JsonObject() : proxy_type(0), _memoryPool(0) {} + FORCE_INLINE JsonObject(MemoryPool* buf, JsonObjectData* data) + : proxy_type(data), _memoryPool(buf) {} - operator JsonVariant() { + operator JsonVariant() const { return JsonVariant(_memoryPool, getVariantData(_data)); } + operator JsonObjectConst() const { + return JsonObjectConst(_data); + } + FORCE_INLINE iterator begin() const { if (!_data) return iterator(); return iterator(_memoryPool, _data->head); } - void clear() { - _data->head = 0; - _data->tail = 0; - } - - // Tells weither the specified key is present and associated with a value. - // - // bool containsKey(TKey); - // TKey = const std::string&, const String& - template - FORCE_INLINE bool containsKey(const TString& key) const { - return containsKey_impl(makeString(key)); - } - // - // bool containsKey(TKey); - // TKey = char*, const char*, char[], const char[], const FlashStringHelper* - template - FORCE_INLINE bool containsKey(TString* key) const { - return containsKey_impl(makeString(key)); - } - - bool copyFrom(JsonObject src) { - bool ok = _data != 0; - clear(); - for (iterator it = src.begin(); it != src.end(); ++it) { - if (it->key().isStatic()) - ok &= set(it->key().c_str(), it->value()); - else - ok &= set(const_cast(it->key().c_str()), it->value()); - } - return ok; - } - FORCE_INLINE iterator end() const { return iterator(); } + void clear() const { + objectClear(_data); + } + + FORCE_INLINE bool copyFrom(JsonObjectConst src) { + return objectCopy(_data, src._data, _memoryPool); + } + // Creates and adds a JsonArray. // // JsonArray createNestedArray(TKey); // TKey = const std::string&, const String& - template - FORCE_INLINE JsonArray createNestedArray(const TString& key); + template + FORCE_INLINE JsonArray createNestedArray(const TKey& key) const; // JsonArray createNestedArray(TKey); // TKey = char*, const char*, char[], const char[], const FlashStringHelper* - template - FORCE_INLINE JsonArray createNestedArray(TString* key); + template + FORCE_INLINE JsonArray createNestedArray(TKey* key) const; // Creates and adds a JsonObject. // // JsonObject createNestedObject(TKey); // TKey = const std::string&, const String& - template - FORCE_INLINE JsonObject createNestedObject(const TString& key) { - if (!_data) return JsonObject(); - return createNestedObject_impl(makeString(key)); + template + FORCE_INLINE JsonObject createNestedObject(const TKey& key) const { + return set(key).template to(); } // // JsonObject createNestedObject(TKey); // TKey = char*, const char*, char[], const char[], const FlashStringHelper* - template - FORCE_INLINE JsonObject createNestedObject(TString* key) { - return createNestedObject_impl(makeString(key)); + template + FORCE_INLINE JsonObject createNestedObject(TKey* key) const { + return set(key).template to(); } // Gets the value associated with the specified key. @@ -103,19 +178,18 @@ class JsonObject { // TKey = const std::string&, const String& // TValue = bool, char, long, int, short, float, double, // std::string, String, JsonArray, JsonObject - template - FORCE_INLINE typename JsonVariantAs::type get( - const TString& key) const { - return get_impl(makeString(key)); + template + FORCE_INLINE typename JsonVariantAs::type get(const TKey& key) const { + return get_impl(makeString(key)).template as(); } // // TValue get(TKey) const; // TKey = char*, const char*, const FlashStringHelper* // TValue = bool, char, long, int, short, float, double, // std::string, String, JsonArray, JsonObject - template - FORCE_INLINE typename JsonVariantAs::type get(TString* key) const { - return get_impl(makeString(key)); + template + FORCE_INLINE typename JsonVariantAs::type get(TKey* key) const { + return get_impl(makeString(key)).template as(); } // Checks the type of the value associated with the specified key. @@ -125,90 +199,58 @@ class JsonObject { // TKey = const std::string&, const String& // TValue = bool, char, long, int, short, float, double, // std::string, String, JsonArray, JsonObject - template - FORCE_INLINE bool is(const TString& key) const { - return is_impl(makeString(key)); + template + FORCE_INLINE bool is(const TKey& key) const { + return get_impl(makeString(key)).template is(); } // // bool is(TKey) const; // TKey = char*, const char*, const FlashStringHelper* // TValue = bool, char, long, int, short, float, double, // std::string, String, JsonArray, JsonObject - template - FORCE_INLINE bool is(TString* key) const { - return is_impl(makeString(key)); + template + FORCE_INLINE bool is(TKey* key) const { + return get_impl(makeString(key)).template is(); } // Gets or sets the value associated with the specified key. // // JsonObjectSubscript operator[](TKey) // TKey = const std::string&, const String& - template - FORCE_INLINE JsonObjectSubscript operator[]( - const TString& key) { - return JsonObjectSubscript(*this, key); + template + FORCE_INLINE JsonObjectSubscript operator[]( + const TKey& key) const { + return JsonObjectSubscript(*this, key); } // // JsonObjectSubscript operator[](TKey) // TKey = char*, const char*, char[], const char[N], const FlashStringHelper* - template - FORCE_INLINE JsonObjectSubscript operator[](TString* key) { - return JsonObjectSubscript(*this, key); - } - - // Gets the value associated with the specified key. - // - // const JsonObjectSubscript operator[](TKey) const; - // TKey = const std::string&, const String& - template - FORCE_INLINE const JsonObjectSubscript operator[]( - const TString& key) const { - return JsonObjectSubscript(*this, key); - } - // - // const JsonObjectSubscript operator[](TKey) const; - // TKey = const char*, const char[N], const FlashStringHelper* - template - FORCE_INLINE const JsonObjectSubscript operator[]( - TString* key) const { - return JsonObjectSubscript(*this, key); + template + FORCE_INLINE JsonObjectSubscript operator[](TKey* key) const { + return JsonObjectSubscript(*this, key); } FORCE_INLINE bool operator==(JsonObject rhs) const { - if (size() != rhs.size()) return false; - for (iterator it = begin(); it != end(); ++it) { - if (rhs.get(it->key()) != it->value()) return false; - } - return true; + return objectEquals(_data, rhs._data); } - FORCE_INLINE void remove(iterator it) { - if (!_data) return; - Slot* slot = it.internal(); - if (!slot) return; - if (slot->prev) - slot->prev->next = slot->next; - else - _data->head = slot->next; - if (slot->next) - slot->next->prev = slot->prev; - else - _data->tail = slot->prev; + FORCE_INLINE void remove(iterator it) const { + objectRemove(_data, it.internal()); } // Removes the specified key and the associated value. // // void remove(TKey); // TKey = const std::string&, const String& - template - FORCE_INLINE void remove(const TString& key) { + template + FORCE_INLINE void remove(const TKey& key) const { remove_impl(makeString(key)); } // // void remove(TKey); // TKey = char*, const char*, char[], const char[], const FlashStringHelper* - template - FORCE_INLINE void remove(TString* key) { + template + FORCE_INLINE void remove(TKey* key) const { remove_impl(makeString(key)); } @@ -218,16 +260,16 @@ class JsonObject { // TKey = const std::string&, const String& // TValue = bool, long, int, short, float, double, serialized, JsonVariant, // std::string, String, JsonArray, JsonObject - template - FORCE_INLINE bool set(const TString& key, const TValue& value) { + template + FORCE_INLINE bool set(const TKey& key, const TValue& value) const { return set(key).set(value); } // // bool set(TKey, TValue); // TKey = const std::string&, const String& // TValue = char*, const char*, const FlashStringHelper* - template - FORCE_INLINE bool set(const TString& key, TValue* value) { + template + FORCE_INLINE bool set(const TKey& key, TValue* value) const { return set(key).set(value); } // @@ -235,44 +277,33 @@ class JsonObject { // TKey = char*, const char*, const FlashStringHelper* // TValue = bool, long, int, short, float, double, serialized, JsonVariant, // std::string, String, JsonArray, JsonObject - template - FORCE_INLINE bool set(TString* key, const TValue& value) { + template + FORCE_INLINE bool set(TKey* key, const TValue& value) const { return set(key).set(value); } // // bool set(TKey, TValue); // TKey = char*, const char*, const FlashStringHelper* // TValue = char*, const char*, const FlashStringHelper* - template - FORCE_INLINE bool set(TString* key, TValue* value) { + template + FORCE_INLINE bool set(TKey* key, TValue* value) const { return set(key).set(value); } - template - FORCE_INLINE JsonVariant set(TString* key) { + template + FORCE_INLINE JsonVariant set(TKey* key) const { return set_impl(makeString(key)); } - template - FORCE_INLINE JsonVariant set(const TString& key) { + template + FORCE_INLINE JsonVariant set(const TKey& key) const { return set_impl(makeString(key)); } - FORCE_INLINE JsonVariant set(const StringInMemoryPool& key) { + FORCE_INLINE JsonVariant set(const StringInMemoryPool& key) const { return set_impl(key); } - FORCE_INLINE size_t size() const { - if (!_data) return 0; - size_t n = 0; - Slot* slot = _data->head; - while (slot) { - n++; - slot = slot->next; - } - return n; - } - FORCE_INLINE bool isNull() const { return _data == 0; } @@ -280,122 +311,27 @@ class JsonObject { template FORCE_INLINE void accept(Visitor& visitor) const { if (_data) - visitor.visitObject(*this); + visitor.visitObject(JsonObjectConst(_data)); else visitor.visitNull(); } private: template - FORCE_INLINE bool containsKey_impl(TStringRef key) const { - return findSlot(key) != 0; + FORCE_INLINE JsonVariant get_impl(TStringRef key) const { + return JsonVariant(_memoryPool, objectGet(_data, key)); + } + + template + FORCE_INLINE JsonVariant set_impl(TKey key) const { + return JsonVariant(_memoryPool, objectSet(_data, key, _memoryPool)); } template - FORCE_INLINE JsonArray createNestedArray_impl(TStringRef key); - - template - FORCE_INLINE JsonObject createNestedObject_impl(TStringRef key); - - // Returns the list node that matches the specified key. - template - Slot* findSlot(TStringRef key) { - if (!_data) return 0; - Slot* slot = _data->head; - while (slot) { - if (key.equals(slot->key)) break; - slot = slot->next; - } - return slot; - } - template - FORCE_INLINE Slot* findSlot(TStringRef key) const { - return const_cast(this)->findSlot(key); + FORCE_INLINE void remove_impl(TStringRef key) const { + objectRemove(_data, objectFindSlot(_data, key)); } - template - FORCE_INLINE typename JsonVariantAs::type get_impl( - TStringRef key) const { - Slot* slot = findSlot(key); - return slot ? JsonVariant(_memoryPool, &slot->value).as() - : TValue(); - } - - template - FORCE_INLINE bool is_impl(TStringRef key) const { - Slot* slot = findSlot(key); - return slot ? JsonVariant(_memoryPool, &slot->value).is() : false; - } - - template - FORCE_INLINE void remove_impl(TStringRef key) { - if (!_data) return; - Slot* slot = findSlot(key); - if (!slot) return; - if (slot->prev) - slot->prev->next = slot->next; - else - _data->head = slot->next; - if (slot->next) - slot->next->prev = slot->prev; - else - _data->tail = slot->prev; - } - - template - FORCE_INLINE JsonVariant set_impl(TStringRef key) { - if (!_data) return JsonVariant(); - - // ignore null key - if (key.isNull()) return JsonVariant(); - - // search a matching key - Slot* slot = findSlot(key); - if (!slot) { - // add the key - slot = new (_memoryPool) Slot(); - if (!slot) return JsonVariant(); - - slot->next = 0; - - if (_data->tail) { - slot->prev = _data->tail; - _data->tail->next = slot; - _data->tail = slot; - } else { - slot->prev = 0; - _data->head = slot; - _data->tail = slot; - } - - if (!set_key(slot, key)) return JsonVariant(); - } - - return JsonVariant(_memoryPool, &slot->value); - } - - template - FORCE_INLINE bool set_key(Slot* slot, TStringRef key) { - const char* dup = key.save(_memoryPool); - if (!dup) return false; - slot->key = dup; - slot->value.keyIsStatic = false; - return true; - } - - FORCE_INLINE bool set_key(Slot* slot, ZeroTerminatedRamStringConst key) { - slot->key = key.save(_memoryPool); - slot->value.keyIsStatic = true; - return true; - } - - FORCE_INLINE bool set_key(Slot* slot, StringInMemoryPool key) { - slot->key = key.save(_memoryPool); - slot->value.keyIsStatic = false; - return true; - } - - mutable MemoryPool* _memoryPool; - mutable JsonObjectData* _data; + MemoryPool* _memoryPool; }; } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/JsonObjectImpl.hpp b/src/ArduinoJson/JsonObjectImpl.hpp index 2b2a3c6a..73eb5a10 100644 --- a/src/ArduinoJson/JsonObjectImpl.hpp +++ b/src/ArduinoJson/JsonObjectImpl.hpp @@ -10,22 +10,12 @@ namespace ARDUINOJSON_NAMESPACE { template -inline JsonArray JsonObject::createNestedArray(const TString& key) { - return createNestedArray_impl(makeString(key)); +inline JsonArray JsonObject::createNestedArray(const TString& key) const { + return set(key).template to(); } template -inline JsonArray JsonObject::createNestedArray(TString* key) { - return createNestedArray_impl(makeString(key)); -} - -template -inline JsonArray JsonObject::createNestedArray_impl(TStringRef key) { - return set_impl(key).template to(); -} - -template -inline JsonObject JsonObject::createNestedObject_impl(TStringRef key) { - return set_impl(key).template to(); +inline JsonArray JsonObject::createNestedArray(TString* key) const { + return set(key).template to(); } } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/JsonObjectIterator.hpp b/src/ArduinoJson/JsonObjectIterator.hpp index 37e0a0da..1075d8c7 100644 --- a/src/ArduinoJson/JsonObjectIterator.hpp +++ b/src/ArduinoJson/JsonObjectIterator.hpp @@ -4,6 +4,7 @@ #pragma once +#include "Data/SlotFunctions.hpp" #include "JsonPair.hpp" namespace ARDUINOJSON_NAMESPACE { @@ -52,10 +53,7 @@ class JsonObjectIterator { } JsonObjectIterator &operator+=(size_t distance) { - while (_slot && distance > 0) { - _slot = _slot->next; - distance--; - } + _slot = slotAdvance(_slot, distance); return *this; } @@ -67,4 +65,59 @@ class JsonObjectIterator { MemoryPool *_memoryPool; Slot *_slot; }; + +class JsonPairConstPtr { + public: + JsonPairConstPtr(const Slot *slot) : _pair(slot) {} + + const JsonPairConst *operator->() const { + return &_pair; + } + + const JsonPairConst &operator*() const { + return _pair; + } + + private: + JsonPairConst _pair; +}; + +class JsonObjectConstIterator { + public: + JsonObjectConstIterator() : _slot(0) {} + + explicit JsonObjectConstIterator(const Slot *slot) : _slot(slot) {} + + JsonPairConst operator*() const { + return JsonPairConst(_slot); + } + JsonPairConstPtr operator->() { + return JsonPairConstPtr(_slot); + } + + bool operator==(const JsonObjectConstIterator &other) const { + return _slot == other._slot; + } + + bool operator!=(const JsonObjectConstIterator &other) const { + return _slot != other._slot; + } + + JsonObjectConstIterator &operator++() { + if (_slot) _slot = _slot->next; + return *this; + } + + JsonObjectConstIterator &operator+=(size_t distance) { + _slot = slotAdvance(_slot, distance); + return *this; + } + + const Slot *internal() { + return _slot; + } + + private: + const Slot *_slot; +}; } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/JsonObjectSubscript.hpp b/src/ArduinoJson/JsonObjectSubscript.hpp index b902cf93..f75df9eb 100644 --- a/src/ArduinoJson/JsonObjectSubscript.hpp +++ b/src/ArduinoJson/JsonObjectSubscript.hpp @@ -24,8 +24,12 @@ class JsonObjectSubscript FORCE_INLINE JsonObjectSubscript(JsonObject object, TStringRef key) : _object(object), _key(key) {} + operator JsonVariantConst() const { + return get_impl(); + } + FORCE_INLINE this_type &operator=(const this_type &src) { - _object.set(_key, src); + set_impl().set(src); return *this; } @@ -37,7 +41,7 @@ class JsonObjectSubscript template FORCE_INLINE typename enable_if::value, this_type &>::type operator=(const TValue &src) { - _object.set(_key, src); + set_impl().set(src); return *this; } // @@ -45,27 +49,27 @@ class JsonObjectSubscript // TValue = char*, const char*, const FlashStringHelper* template FORCE_INLINE this_type &operator=(TValue *src) { - _object.set(_key, src); + set_impl().set(src); return *this; } FORCE_INLINE bool isNull() const { - return !_object.containsKey(_key); + return get_impl().isNull(); } template FORCE_INLINE typename JsonVariantAs::type as() const { - return _object.get(_key); + return get_impl().template as(); } template FORCE_INLINE bool is() const { - return _object.is(_key); + return get_impl().template is(); } template FORCE_INLINE typename JsonVariantTo::type to() { - return _object.set(_key).template to(); + return set_impl().template to(); } // Sets the specified value. @@ -77,39 +81,39 @@ class JsonObjectSubscript template FORCE_INLINE typename enable_if::value, bool>::type set( const TValue &value) { - return _object.set(_key, value); + return set_impl().set(value); } // // bool set(TValue); // TValue = char*, const char, const FlashStringHelper* template FORCE_INLINE bool set(const TValue *value) { - return _object.set(_key, value); + return set_impl().set(value); } template void accept(Visitor &visitor) const { - return _object.get(_key).accept(visitor); + return get_impl().accept(visitor); } private: + JsonVariant get_impl() const { + return _object.get(_key); + } + + JsonVariant set_impl() const { + return _object.set(_key); + } + JsonObject _object; TStringRef _key; }; -template -template -inline typename enable_if::value, - const JsonObjectSubscript >::type - JsonVariantSubscripts::operator[](const TString &key) const { - return impl()->template as()[key]; -} - template template inline typename enable_if::value, JsonObjectSubscript >::type - JsonVariantSubscripts::operator[](const TString &key) { + JsonVariantSubscripts::operator[](const TString &key) const { return impl()->template as()[key]; } @@ -117,14 +121,6 @@ template template inline typename enable_if::value, JsonObjectSubscript >::type - JsonVariantSubscripts::operator[](TString *key) { - return impl()->template as()[key]; -} - -template -template -inline typename enable_if::value, - const JsonObjectSubscript >::type JsonVariantSubscripts::operator[](TString *key) const { return impl()->template as()[key]; } diff --git a/src/ArduinoJson/JsonPair.hpp b/src/ArduinoJson/JsonPair.hpp index f9b33f94..67cc8e63 100644 --- a/src/ArduinoJson/JsonPair.hpp +++ b/src/ArduinoJson/JsonPair.hpp @@ -4,39 +4,10 @@ #pragma once +#include "JsonKey.hpp" #include "JsonVariant.hpp" namespace ARDUINOJSON_NAMESPACE { - -class JsonKey { - public: - JsonKey(Slot* slot) : _slot(slot) {} - - operator const char*() const { - return c_str(); - } - - const char* c_str() const { - return _slot ? _slot->key : 0; - } - - bool isNull() const { - return _slot == 0 || _slot->key == 0; - } - - bool isStatic() const { - return _slot ? _slot->value.keyIsStatic : true; - } - - friend bool operator==(JsonKey lhs, const char* rhs) { - if (lhs.isNull()) return rhs == 0; - return rhs ? !strcmp(lhs, rhs) : false; - } - - private: - Slot* _slot; -}; - // A key value pair for JsonObjectData. class JsonPair { public: @@ -58,4 +29,25 @@ class JsonPair { JsonKey _key; JsonVariant _value; }; + +class JsonPairConst { + public: + JsonPairConst(const Slot* slot) : _key(slot) { + if (slot) { + _value = JsonVariantConst(&slot->value); + } + } + + JsonKey key() const { + return _key; + } + + JsonVariantConst value() const { + return _value; + } + + private: + JsonKey _key; + JsonVariantConst _value; +}; } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/JsonVariant.hpp b/src/ArduinoJson/JsonVariant.hpp index 1c4e2792..4562ffab 100644 --- a/src/ArduinoJson/JsonVariant.hpp +++ b/src/ArduinoJson/JsonVariant.hpp @@ -8,14 +8,14 @@ #include // for uint8_t #include "Data/JsonVariantData.hpp" +#include "Data/VariantAs.hpp" +#include "Data/VariantFunctions.hpp" #include "JsonVariant.hpp" #include "JsonVariantBase.hpp" #include "Memory/MemoryPool.hpp" #include "Numbers/parseFloat.hpp" #include "Numbers/parseInteger.hpp" #include "Polyfills/type_traits.hpp" -#include "Serialization/DynamicStringWriter.hpp" -#include "SerializedValue.hpp" namespace ARDUINOJSON_NAMESPACE { @@ -23,251 +23,11 @@ namespace ARDUINOJSON_NAMESPACE { class JsonArray; class JsonObject; -// A variant that can be a any value serializable to a JSON value. -// -// It can be set to: -// - a boolean -// - a char, short, int or a long (signed or unsigned) -// - a string (const char*) -// - a reference to a JsonArray or JsonObject -class JsonVariant : public JsonVariantBase { +// Contains the methods shared by JsonVariant and JsonVariantConst +template +class JsonVariantProxy { public: - // Intenal use only - FORCE_INLINE JsonVariant(MemoryPool *memoryPool, JsonVariantData *data) - : _memoryPool(memoryPool), _data(data) {} - - // Creates an uninitialized JsonVariant - FORCE_INLINE JsonVariant() : _memoryPool(0), _data(0) {} - - // set(bool value) - FORCE_INLINE bool set(bool value) { - if (!_data) return false; - _data->type = JSON_BOOLEAN; - _data->content.asInteger = static_cast(value); - return true; - } - - // set(double value); - // set(float value); - template - FORCE_INLINE bool set( - T value, typename enable_if::value>::type * = 0) { - if (!_data) return false; - _data->type = JSON_FLOAT; - _data->content.asFloat = static_cast(value); - return true; - } - - // set(char) - // set(signed short) - // set(signed int) - // set(signed long) - // set(signed char) - template - FORCE_INLINE bool set(T value, - typename enable_if::value && - is_signed::value>::type * = 0) { - if (!_data) return false; - if (value >= 0) { - _data->type = JSON_POSITIVE_INTEGER; - _data->content.asInteger = static_cast(value); - } else { - _data->type = JSON_NEGATIVE_INTEGER; - _data->content.asInteger = ~static_cast(value) + 1; - } - return true; - } - - // set(unsigned short) - // set(unsigned int) - // set(unsigned long) - template - FORCE_INLINE bool set(T value, - typename enable_if::value && - is_unsigned::value>::type * = 0) { - if (!_data) return false; - _data->type = JSON_POSITIVE_INTEGER; - _data->content.asInteger = static_cast(value); - return true; - } - - // set(SerializedValue) - FORCE_INLINE bool set(SerializedValue value) { - if (!_data) return false; - _data->type = JSON_LINKED_RAW; - _data->content.asRaw.data = value.data(); - _data->content.asRaw.size = value.size(); - return true; - } - - // set(SerializedValue) - // set(SerializedValue) - // set(SerializedValue) - template - FORCE_INLINE bool set( - SerializedValue value, - typename enable_if::value>::type * = 0) { - if (!_data) return false; - const char *dup = makeString(value.data(), value.size()).save(_memoryPool); - if (dup) { - _data->type = JSON_OWNED_RAW; - _data->content.asRaw.data = dup; - _data->content.asRaw.size = value.size(); - return true; - } else { - _data->type = JSON_NULL; - return false; - } - } - - // set(const std::string&) - // set(const String&) - template - FORCE_INLINE bool set(const T &value, - typename enable_if::value>::type * = 0) { - return setString(makeString(value)); - } - - // set(char*) - template - FORCE_INLINE bool set(T *value, - typename enable_if::value>::type * = 0) { - return setString(makeString(value)); - } - - // set(const char*); - FORCE_INLINE bool set(const char *value) { - if (!_data) return false; - _data->type = JSON_LINKED_STRING; - _data->content.asString = value; - return true; - } - - // set(const char*); - FORCE_INLINE bool set(StringInMemoryPool value) { - if (!_data) return false; - _data->type = JSON_OWNED_STRING; - _data->content.asString = value.save(_memoryPool); - return true; - } - - bool set(const JsonVariant &value); - - FORCE_INLINE bool set(JsonArray array); - FORCE_INLINE bool set(const JsonArraySubscript &); - FORCE_INLINE bool set(JsonObject object); - template - FORCE_INLINE bool set(const JsonObjectSubscript &); - - // Get the variant as the specified type. - // - // char as() const; - // signed char as() const; - // signed short as() const; - // signed int as() const; - // signed long as() const; - // unsigned char as() const; - // unsigned short as() const; - // unsigned int as() const; - // unsigned long as() const; - template - FORCE_INLINE const typename enable_if::value, T>::type as() - const { - if (!_data) return 0; - switch (_data->type) { - case JSON_POSITIVE_INTEGER: - case JSON_BOOLEAN: - return T(_data->content.asInteger); - case JSON_NEGATIVE_INTEGER: - return T(~_data->content.asInteger + 1); - case JSON_LINKED_STRING: - case JSON_OWNED_STRING: - return parseInteger(_data->content.asString); - case JSON_FLOAT: - return T(_data->content.asFloat); - default: - return 0; - } - } - // bool as() const - template - FORCE_INLINE const typename enable_if::value, T>::type as() - const { - return as() != 0; - } - // - // double as() const; - // float as() const; - template - FORCE_INLINE const typename enable_if::value, T>::type - as() const { - if (!_data) return 0; - switch (_data->type) { - case JSON_POSITIVE_INTEGER: - case JSON_BOOLEAN: - return static_cast(_data->content.asInteger); - case JSON_NEGATIVE_INTEGER: - return -static_cast(_data->content.asInteger); - case JSON_LINKED_STRING: - case JSON_OWNED_STRING: - return parseFloat(_data->content.asString); - case JSON_FLOAT: - return static_cast(_data->content.asFloat); - default: - return 0; - } - } - // - // const char* as() const; - // const char* as() const; - template - FORCE_INLINE typename enable_if::value || - is_same::value, - const char *>::type - as() const { - if (!_data) return 0; - if (_data && - (_data->type == JSON_LINKED_STRING || _data->type == JSON_OWNED_STRING)) - return _data->content.asString; - else - return 0; - } - // - // std::string as() const; - // String as() const; - template - FORCE_INLINE typename enable_if::value, T>::type as() - const { - const char *cstr = as(); - if (cstr) return T(cstr); - T s; - serializeJson(*this, s); - return s; - } - // - // JsonArray as() const; - // const JsonArray as() const; - template - FORCE_INLINE typename enable_if< - is_same::type, JsonArray>::value, - JsonArray>::type - as() const; - // - // JsonObject as() const; - // const JsonObject as() const; - template - FORCE_INLINE typename enable_if< - is_same::type, JsonObject>::value, T>::type - as() const; - // - // JsonVariant as const; - template - FORCE_INLINE typename enable_if::value, T>::type as() - const { - return *this; - } - - // Tells weither the variant has the specified type. + // Tells wether the variant has the specified type. // Returns true if the variant has type type T, false otherwise. // // bool is() const; @@ -282,8 +42,7 @@ class JsonVariant : public JsonVariantBase { template FORCE_INLINE typename enable_if::value, bool>::type is() const { - return _data && (_data->type == JSON_POSITIVE_INTEGER || - _data->type == JSON_NEGATIVE_INTEGER); + return variantIsInteger(_data); } // // bool is() const; @@ -291,9 +50,7 @@ class JsonVariant : public JsonVariantBase { template FORCE_INLINE typename enable_if::value, bool>::type is() const { - return _data && - (_data->type == JSON_FLOAT || _data->type == JSON_POSITIVE_INTEGER || - _data->type == JSON_NEGATIVE_INTEGER); + return variantIsFloat(_data); } // // bool is() const @@ -313,8 +70,7 @@ class JsonVariant : public JsonVariantBase { IsWriteableString::value, bool>::type is() const { - return _data && (_data->type == JSON_LINKED_STRING || - _data->type == JSON_OWNED_STRING); + return variantIsString(_data); } // // bool is const; @@ -323,7 +79,7 @@ class JsonVariant : public JsonVariantBase { FORCE_INLINE typename enable_if< is_same::type, JsonArray>::value, bool>::type is() const { - return _data && _data->type == JSON_ARRAY; + return variantIsArray(_data); } // // bool is const; @@ -332,51 +88,235 @@ class JsonVariant : public JsonVariantBase { FORCE_INLINE typename enable_if< is_same::type, JsonObject>::value, bool>::type is() const { - return _data && _data->type == JSON_OBJECT; + return variantIsObject(_data); } FORCE_INLINE bool isNull() const { - return _data == 0 || _data->type == JSON_NULL; + return variantIsNull(_data); } FORCE_INLINE bool isInvalid() const { return _data == 0; } + size_t size() const { + return objectSize(variantAsObject(_data)) + + arraySize(variantAsArray(_data)); + } + + protected: + JsonVariantProxy(TData *data) : _data(data) {} + TData *_data; +}; + +// A variant that can be a any value serializable to a JSON value. +// +// It can be set to: +// - a boolean +// - a char, short, int or a long (signed or unsigned) +// - a string (const char*) +// - a reference to a JsonArray or JsonObject +class JsonVariant : public JsonVariantProxy, + public JsonVariantBase { + typedef JsonVariantProxy proxy_type; + friend class JsonVariantConst; + + public: + // Intenal use only + FORCE_INLINE JsonVariant(MemoryPool *memoryPool, JsonVariantData *data) + : proxy_type(data), _memoryPool(memoryPool) {} + + // Creates an uninitialized JsonVariant + FORCE_INLINE JsonVariant() : proxy_type(0), _memoryPool(0) {} + + // set(bool value) + FORCE_INLINE bool set(bool value) const { + return variantSetBoolean(_data, value); + } + + // set(double value); + // set(float value); + template + FORCE_INLINE bool set( + T value, + typename enable_if::value>::type * = 0) const { + return variantSetFloat(_data, static_cast(value)); + } + + // set(char) + // set(signed short) + // set(signed int) + // set(signed long) + // set(signed char) + template + FORCE_INLINE bool set( + T value, + typename enable_if::value && is_signed::value>::type * = + 0) const { + return variantSetSignedInteger(_data, value); + } + + // set(unsigned short) + // set(unsigned int) + // set(unsigned long) + template + FORCE_INLINE bool set( + T value, typename enable_if::value && + is_unsigned::value>::type * = 0) const { + return variantSetSignedInteger(_data, static_cast(value)); + } + + // set(SerializedValue) + FORCE_INLINE bool set(SerializedValue value) const { + return variantSetLinkedRaw(_data, value); + } + + // set(SerializedValue) + // set(SerializedValue) + // set(SerializedValue) + template + FORCE_INLINE bool set( + SerializedValue value, + typename enable_if::value>::type * = 0) const { + return variantSetOwnedRaw(_data, value, _memoryPool); + } + + // set(const std::string&) + // set(const String&) + template + FORCE_INLINE bool set( + const T &value, + typename enable_if::value>::type * = 0) const { + return variantSetString(_data, makeString(value), _memoryPool); + } + + // set(char*) + template + FORCE_INLINE bool set( + T *value, typename enable_if::value>::type * = 0) const { + return variantSetString(_data, makeString(value), _memoryPool); + } + + // set(const char*); + FORCE_INLINE bool set(const char *value) const { + return variantSetString(_data, value); + } + + // set(const char*); + FORCE_INLINE bool set(StringInMemoryPool value) const { + return variantSetString(_data, value, _memoryPool); + } + + bool set(const JsonVariant &value) const; + + FORCE_INLINE bool set(JsonArray array) const; + FORCE_INLINE bool set(const JsonArraySubscript &) const; + FORCE_INLINE bool set(JsonObject object) const; + template + FORCE_INLINE bool set(const JsonObjectSubscript &) const; + + // Get the variant as the specified type. + // + // std::string as() const; + // String as() const; + template + FORCE_INLINE typename enable_if::value && + !is_same::value && + !is_same::value, + typename JsonVariantAs::type>::type + as() const { + return variantAs(_data); + } + // + // JsonArray as() const; + // const JsonArray as() const; + template + FORCE_INLINE typename enable_if::value, T>::type as() + const; + // + // JsonObject as() const; + // const JsonObject as() const; + template + FORCE_INLINE typename enable_if::value, T>::type as() + const; + // + // JsonVariant as const; + template + FORCE_INLINE typename enable_if::value, T>::type as() + const { + return *this; + } + template void accept(Visitor &visitor) const; + FORCE_INLINE bool operator==(JsonVariant lhs) const { + return variantEquals(_data, lhs._data); + } + + FORCE_INLINE bool operator!=(JsonVariant lhs) const { + return !variantEquals(_data, lhs._data); + } + // Change the type of the variant // // JsonArray to() template - typename enable_if::value, JsonArray>::type to(); + typename enable_if::value, JsonArray>::type to() const; // // JsonObject to() template - typename enable_if::value, JsonObject>::type to(); + typename enable_if::value, JsonObject>::type to() + const; // // JsonObject to() template - typename enable_if::value, JsonVariant>::type to(); + typename enable_if::value, JsonVariant>::type to() + const; private: - template - bool setString(TStringRef value) { - if (!_data) return false; - const char *dup = value.save(_memoryPool); - if (dup) { - _data->type = JSON_OWNED_STRING; - _data->content.asString = dup; - return true; - } else { - _data->type = JSON_NULL; - return false; - } + MemoryPool *_memoryPool; +}; + +class JsonVariantConst : public JsonVariantProxy, + public JsonVariantBase { + typedef JsonVariantProxy proxy_type; + + public: + JsonVariantConst() : proxy_type(0) {} + JsonVariantConst(const JsonVariantData *data) : proxy_type(data) {} + JsonVariantConst(JsonVariant var) : proxy_type(var._data) {} + + template + void accept(Visitor &visitor) const; + + // Get the variant as the specified type. + // + template + FORCE_INLINE typename JsonVariantConstAs::type as() const { + return variantAs::type>(_data); } - MemoryPool *_memoryPool; - JsonVariantData *_data; + FORCE_INLINE JsonVariantConst operator[](size_t index) const; + + // + // const JsonVariantConst operator[](TKey) const; + // TKey = const std::string&, const String& + template + FORCE_INLINE + typename enable_if::value, JsonVariantConst>::type + operator[](const TString &key) const { + return JsonVariantConst(objectGet(variantAsObject(_data), makeString(key))); + } + // + // JsonVariantConst operator[](TKey); + // TKey = const char*, const char[N], const FlashStringHelper* + template + FORCE_INLINE + typename enable_if::value, JsonVariantConst>::type + operator[](TString *key) const { + return JsonVariantConst(objectGet(variantAsObject(_data), makeString(key))); + } }; class JsonVariantLocal : public JsonVariant { diff --git a/src/ArduinoJson/JsonVariantBase.hpp b/src/ArduinoJson/JsonVariantBase.hpp index 478be4df..b18e9276 100644 --- a/src/ArduinoJson/JsonVariantBase.hpp +++ b/src/ArduinoJson/JsonVariantBase.hpp @@ -15,6 +15,5 @@ template class JsonVariantBase : public JsonVariantCasts, public JsonVariantComparisons, public JsonVariantOr, - public JsonVariantSubscripts, - public JsonVariantTag {}; + public JsonVariantSubscripts {}; } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/JsonVariantComparisons.hpp b/src/ArduinoJson/JsonVariantComparisons.hpp index d8b0cdaf..622da0eb 100644 --- a/src/ArduinoJson/JsonVariantComparisons.hpp +++ b/src/ArduinoJson/JsonVariantComparisons.hpp @@ -4,134 +4,158 @@ #pragma once -#include "Data/IsVariant.hpp" -#include "Data/JsonFloat.hpp" -#include "Data/JsonInteger.hpp" -#include "Polyfills/type_traits.hpp" -#include "Strings/StringTypes.hpp" +#include "JsonVariant.hpp" namespace ARDUINOJSON_NAMESPACE { -class JsonArray; -class JsonObject; +template +struct is_simple_value { + static const bool value = is_integral::value || + is_floating_point::value || + is_same::value; +}; -template +template class JsonVariantComparisons { public: - template - friend bool operator==(const JsonVariantComparisons &variant, - TComparand comparand) { - return variant.equals(comparand); - } - - template - friend typename enable_if::value, bool>::type - operator==(TComparand comparand, const JsonVariantComparisons &variant) { - return variant.equals(comparand); - } - - template - friend bool operator!=(const JsonVariantComparisons &variant, - TComparand comparand) { - return !variant.equals(comparand); - } - - template - friend typename enable_if::value, bool>::type - operator!=(TComparand comparand, const JsonVariantComparisons &variant) { - return !variant.equals(comparand); - } - - template - friend bool operator<=(const JsonVariantComparisons &left, TComparand right) { - return left.as() <= right; - } - - template - friend bool operator<=(TComparand comparand, - const JsonVariantComparisons &variant) { - return comparand <= variant.as(); - } - - template - friend bool operator>=(const JsonVariantComparisons &variant, - TComparand comparand) { - return variant.as() >= comparand; - } - - template - friend bool operator>=(TComparand comparand, - const JsonVariantComparisons &variant) { - return comparand >= variant.as(); - } - - template - friend bool operator<(const JsonVariantComparisons &varian, - TComparand comparand) { - return varian.as() < comparand; - } - - template - friend bool operator<(TComparand comparand, - const JsonVariantComparisons &variant) { - return comparand < variant.as(); - } - - template - friend bool operator>(const JsonVariantComparisons &variant, - TComparand comparand) { - return variant.as() > comparand; - } - - template - friend bool operator>(TComparand comparand, - const JsonVariantComparisons &variant) { - return comparand > variant.as(); - } - - private: - const TImpl *impl() const { - return static_cast(this); - } - + // const char* == TVariant template - const typename JsonVariantAs::type as() const { - return impl()->template as(); + friend typename enable_if::value, bool>::type operator==( + T *lhs, TVariant rhs) { + return makeString(lhs).equals(rhs.template as()); } + // std::string == TVariant template - bool is() const { - return impl()->template is(); + friend typename enable_if::value, bool>::type operator==( + const T &lhs, TVariant rhs) { + return makeString(lhs).equals(rhs.template as()); } - template - typename enable_if::value, bool>::type equals( - const TString &comparand) const { - return makeString(comparand).equals(as()); + // TVariant == const char* + template + friend typename enable_if::value, bool>::type operator==( + TVariant lhs, T *rhs) { + return makeString(rhs).equals(lhs.template as()); } - template - typename enable_if< - !IsVariant::value && !IsString::value, bool>::type - equals(const TComparand &comparand) const { - return as() == comparand; + // TVariant == std::string + template + friend typename enable_if::value, bool>::type operator==( + TVariant lhs, const T &rhs) { + return makeString(rhs).equals(lhs.template as()); } - template - bool equals(const JsonVariantComparisons &right) const { - if (is() && right.template is()) - return as() == right.template as(); - if (is() && right.template is()) - return as() == right.template as(); - if (is() && right.template is()) - return as() == right.template as(); - if (is() && right.template is()) - return as() == right.template as(); - if (is() && right.template is()) - return as() == right.template as(); - if (is() && right.template is()) - return makeString(as()).equals(right.template as()); + // bool/int/float == TVariant + template + friend typename enable_if::value, bool>::type operator==( + const T &lhs, TVariant rhs) { + return lhs == rhs.template as(); + } - return false; + // TVariant == bool/int/float + template + friend typename enable_if::value, bool>::type operator==( + TVariant lhs, const T &rhs) { + return lhs.template as() == rhs; + } + + // const char* != TVariant + template + friend typename enable_if::value, bool>::type operator!=( + T *lhs, TVariant rhs) { + return !makeString(lhs).equals(rhs.template as()); + } + + // std::string != TVariant + template + friend typename enable_if::value, bool>::type operator!=( + const T &lhs, TVariant rhs) { + return !makeString(lhs).equals(rhs.template as()); + } + + // TVariant != const char* + template + friend typename enable_if::value, bool>::type operator!=( + TVariant lhs, T *rhs) { + return !makeString(rhs).equals(lhs.template as()); + } + + // TVariant != std::string + template + friend typename enable_if::value, bool>::type operator!=( + TVariant lhs, const T &rhs) { + return !makeString(rhs).equals(lhs.template as()); + } + + // bool/int/float != TVariant + template + friend typename enable_if::value, bool>::type operator!=( + const T &lhs, TVariant rhs) { + return lhs != rhs.template as(); + } + + // TVariant != bool/int/float + template + friend typename enable_if::value, bool>::type operator!=( + TVariant lhs, const T &rhs) { + return lhs.template as() != rhs; + } + + // bool/int/float < TVariant + template + friend typename enable_if::value, bool>::type operator<( + const T &lhs, TVariant rhs) { + return lhs < rhs.template as(); + } + + // TVariant < bool/int/float + template + friend typename enable_if::value, bool>::type operator<( + TVariant lhs, const T &rhs) { + return lhs.template as() < rhs; + } + + // bool/int/float <= TVariant + template + friend typename enable_if::value, bool>::type operator<=( + const T &lhs, TVariant rhs) { + return lhs <= rhs.template as(); + } + + // TVariant <= bool/int/float + template + friend typename enable_if::value, bool>::type operator<=( + TVariant lhs, const T &rhs) { + return lhs.template as() <= rhs; + } + + // bool/int/float > TVariant + template + friend typename enable_if::value, bool>::type operator>( + const T &lhs, TVariant rhs) { + return lhs > rhs.template as(); + } + + // TVariant > bool/int/float + template + friend typename enable_if::value, bool>::type operator>( + TVariant lhs, const T &rhs) { + return lhs.template as() > rhs; + } + + // bool/int/float >= TVariant + template + friend typename enable_if::value, bool>::type operator>=( + const T &lhs, TVariant rhs) { + return lhs >= rhs.template as(); + } + + // TVariant >= bool/int/float + template + friend typename enable_if::value, bool>::type operator>=( + TVariant lhs, const T &rhs) { + return lhs.template as() >= rhs; } }; + } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/JsonVariantImpl.hpp b/src/ArduinoJson/JsonVariantImpl.hpp index d4d41904..67b1eac4 100644 --- a/src/ArduinoJson/JsonVariantImpl.hpp +++ b/src/ArduinoJson/JsonVariantImpl.hpp @@ -13,95 +13,65 @@ namespace ARDUINOJSON_NAMESPACE { -inline bool JsonVariant::set(JsonArray array) { +inline bool JsonVariant::set(JsonArray array) const { return to().copyFrom(array); } -inline bool JsonVariant::set(const JsonArraySubscript& value) { +inline bool JsonVariant::set(const JsonArraySubscript& value) const { return set(value.as()); } -inline bool JsonVariant::set(JsonObject object) { +inline bool JsonVariant::set(JsonObject object) const { return to().copyFrom(object); } template -inline bool JsonVariant::set(const JsonObjectSubscript& value) { +inline bool JsonVariant::set(const JsonObjectSubscript& value) const { return set(value.template as()); } -inline bool JsonVariant::set(const JsonVariant& value) { - if (!_data) return false; - if (!value._data) { - _data->type = JSON_NULL; - return true; - } - switch (value._data->type) { - case JSON_ARRAY: - return set(value.as()); - case JSON_OBJECT: - return set(value.as()); - case JSON_OWNED_STRING: - return set(const_cast(value._data->content.asString)); - case JSON_OWNED_RAW: - return set(serialized(const_cast(value._data->content.asRaw.data), - value._data->content.asRaw.size)); - default: - *_data = *value._data; - return true; - } +inline bool JsonVariant::set(const JsonVariant& value) const { + return variantCopy(_data, value._data, _memoryPool); } template -inline typename enable_if< - is_same::type, JsonArray>::value, JsonArray>::type +inline typename enable_if::value, T>::type JsonVariant::as() const { - if (_data && _data->type == JSON_ARRAY) - return JsonArray(_memoryPool, &_data->content.asArray); - else - return JsonArray(); + return JsonArray(_memoryPool, variantAsArray(_data)); } template -inline typename enable_if< - is_same::type, JsonObject>::value, T>::type +inline typename enable_if::value, T>::type JsonVariant::as() const { - if (_data && _data->type == JSON_OBJECT) - return JsonObject(_memoryPool, &_data->content.asObject); - else - return JsonObject(); + return JsonObject(_memoryPool, variantAsObject(_data)); } template inline typename enable_if::value, JsonArray>::type -JsonVariant::to() { - if (!_data) return JsonArray(); - _data->type = JSON_ARRAY; - _data->content.asArray.head = 0; - _data->content.asArray.tail = 0; - return JsonArray(_memoryPool, &_data->content.asArray); +JsonVariant::to() const { + return JsonArray(_memoryPool, variantToArray(_data)); } template typename enable_if::value, JsonObject>::type -JsonVariant::to() { - if (!_data) return JsonObject(); - _data->type = JSON_OBJECT; - _data->content.asObject.head = 0; - _data->content.asObject.tail = 0; - return JsonObject(_memoryPool, &_data->content.asObject); +JsonVariant::to() const { + return JsonObject(_memoryPool, variantToObject(_data)); } template typename enable_if::value, JsonVariant>::type -JsonVariant::to() { - if (!_data) return JsonVariant(); - _data->type = JSON_NULL; +JsonVariant::to() const { + variantSetNull(_data); return *this; } template inline void JsonVariant::accept(Visitor& visitor) const { + return JsonVariantConst(_data).accept(visitor); +} + +template +inline void JsonVariantConst::accept(Visitor& visitor) const { if (!_data) return visitor.visitNull(); switch (_data->type) { @@ -109,12 +79,10 @@ inline void JsonVariant::accept(Visitor& visitor) const { return visitor.visitFloat(_data->content.asFloat); case JSON_ARRAY: - return visitor.visitArray( - JsonArray(_memoryPool, &_data->content.asArray)); + return visitor.visitArray(JsonArrayConst(&_data->content.asArray)); case JSON_OBJECT: - return visitor.visitObject( - JsonObject(_memoryPool, &_data->content.asObject)); + return visitor.visitObject(JsonObjectConst(&_data->content.asObject)); case JSON_LINKED_STRING: case JSON_OWNED_STRING: @@ -138,4 +106,9 @@ inline void JsonVariant::accept(Visitor& visitor) const { return visitor.visitNull(); } } + +inline JsonVariantConst JsonVariantConst::operator[](size_t index) const { + return JsonArrayConst(variantAsArray(_data))[index]; +} + } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/JsonVariantSubscripts.hpp b/src/ArduinoJson/JsonVariantSubscripts.hpp index 831afac4..720404fa 100644 --- a/src/ArduinoJson/JsonVariantSubscripts.hpp +++ b/src/ArduinoJson/JsonVariantSubscripts.hpp @@ -21,50 +21,26 @@ class JsonObjectSubscript; template class JsonVariantSubscripts { public: - // Mimics an array or an object. - // Returns the size of the array or object if the variant has that type. - // Returns 0 if the variant is neither an array nor an object - size_t size() const { - return impl()->template as().size() + - impl()->template as().size(); - } - // Mimics an array. // Returns the element at specified index if the variant is an array. - FORCE_INLINE const JsonArraySubscript operator[](size_t index) const; - FORCE_INLINE JsonArraySubscript operator[](size_t index); + FORCE_INLINE JsonArraySubscript operator[](size_t index) const; // Mimics an object. // Returns the value associated with the specified key if the variant is // an object. // - // const JsonObjectSubscript operator[](TKey) const; - // TKey = const std::string&, const String& - template - FORCE_INLINE - typename enable_if::value, - const JsonObjectSubscript >::type - operator[](const TString &key) const; - // - // const JsonObjectSubscript operator[](TKey) const; + // JsonObjectSubscript operator[](TKey) const; // TKey = const std::string&, const String& template FORCE_INLINE typename enable_if::value, JsonObjectSubscript >::type - operator[](const TString &key); + operator[](const TString &key) const; // - // JsonObjectSubscript operator[](TKey); + // JsonObjectSubscript operator[](TKey) const; // TKey = const char*, const char[N], const FlashStringHelper* template FORCE_INLINE typename enable_if::value, JsonObjectSubscript >::type - operator[](TString *key); - // - // JsonObjectSubscript operator[](TKey); - // TKey = const char*, const char[N], const FlashStringHelper* - template - FORCE_INLINE typename enable_if::value, - const JsonObjectSubscript >::type operator[](TString *key) const; private: diff --git a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp index 875d67c4..7d9ba70b 100644 --- a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp @@ -35,7 +35,7 @@ class MsgPackSerializer { } } - void visitArray(JsonArray array) { + void visitArray(JsonArrayConst array) { size_t n = array.size(); if (n < 0x10) { writeByte(uint8_t(0x90 + array.size())); @@ -46,12 +46,12 @@ class MsgPackSerializer { writeByte(0xDD); writeInteger(uint32_t(n)); } - for (JsonArray::iterator it = array.begin(); it != array.end(); ++it) { + for (JsonArrayConst::iterator it = array.begin(); it != array.end(); ++it) { it->accept(*this); } } - void visitObject(JsonObject object) { + void visitObject(JsonObjectConst object) { size_t n = object.size(); if (n < 0x10) { writeByte(uint8_t(0x80 + n)); @@ -62,7 +62,8 @@ class MsgPackSerializer { writeByte(0xDF); writeInteger(uint32_t(n)); } - for (JsonObject::iterator it = object.begin(); it != object.end(); ++it) { + for (JsonObjectConst::iterator it = object.begin(); it != object.end(); + ++it) { visitString(it->key()); it->value().accept(*this); } diff --git a/src/ArduinoJson/Strings/ZeroTerminatedRamString.hpp b/src/ArduinoJson/Strings/ZeroTerminatedRamString.hpp index d36a7d64..8c07e2f6 100644 --- a/src/ArduinoJson/Strings/ZeroTerminatedRamString.hpp +++ b/src/ArduinoJson/Strings/ZeroTerminatedRamString.hpp @@ -38,4 +38,9 @@ struct IsString { static const bool value = sizeof(TChar) == 1; }; +template <> +struct IsString { + static const bool value = false; +}; + } // namespace ARDUINOJSON_NAMESPACE diff --git a/test/JsonArray/CMakeLists.txt b/test/JsonArray/CMakeLists.txt index 0f9e76a9..ae3342da 100644 --- a/test/JsonArray/CMakeLists.txt +++ b/test/JsonArray/CMakeLists.txt @@ -7,6 +7,7 @@ add_executable(JsonArrayTests copyFrom.cpp copyTo.cpp createNested.cpp + equals.cpp isNull.cpp iterator.cpp remove.cpp diff --git a/test/JsonArray/equals.cpp b/test/JsonArray/equals.cpp new file mode 100644 index 00000000..b7ca9e97 --- /dev/null +++ b/test/JsonArray/equals.cpp @@ -0,0 +1,32 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +TEST_CASE("JsonArray::operator==()") { + DynamicJsonDocument doc1; + JsonArray array1 = doc1.to(); + JsonArrayConst array1c = array1; + + DynamicJsonDocument doc2; + JsonArray array2 = doc2.to(); + JsonArrayConst array2c = array2; + + SECTION("should return false when arrays differ") { + array1.add("coucou"); + array2.add(1); + + REQUIRE_FALSE(array1 == array2); + REQUIRE_FALSE(array1c == array2c); + } + + SECTION("should return false when arrays differ") { + array1.add("coucou"); + array2.add("coucou"); + + REQUIRE(array1 == array2); + REQUIRE(array1c == array2c); + } +} diff --git a/test/JsonArray/isNull.cpp b/test/JsonArray/isNull.cpp index 1b80349c..b2bf2456 100644 --- a/test/JsonArray/isNull.cpp +++ b/test/JsonArray/isNull.cpp @@ -23,3 +23,22 @@ TEST_CASE("JsonArray::isNull()") { REQUIRE(array.isNull() == true); }*/ } + +TEST_CASE("JsonArrayConst::isNull()") { + SECTION("returns true for undefined JsonArray") { + JsonArrayConst array; + REQUIRE(array.isNull() == true); + } + + SECTION("returns false when allocation succeeds") { + StaticJsonDocument doc; + JsonArrayConst array = doc.to(); + REQUIRE(array.isNull() == false); + } + + /* SECTION("returns true when allocation fails") { + StaticJsonDocument<1> doc; + JsonArray array = doc.to(); + REQUIRE(array.isNull() == true); + }*/ +} diff --git a/test/JsonArray/iterator.cpp b/test/JsonArray/iterator.cpp index 95f9dd80..62bc72ca 100644 --- a/test/JsonArray/iterator.cpp +++ b/test/JsonArray/iterator.cpp @@ -5,15 +5,16 @@ #include #include -template +template static void run_iterator_test() { StaticJsonDocument doc; - JsonArray array = doc.to(); - array.add(12); - array.add(34); + JsonArray tmp = doc.to(); + tmp.add(12); + tmp.add(34); - TIterator it = array.begin(); - TIterator end = array.end(); + TArray array = tmp; + typename TArray::iterator it = array.begin(); + typename TArray::iterator end = array.end(); REQUIRE(end != it); REQUIRE(12 == it->template as()); @@ -27,7 +28,9 @@ static void run_iterator_test() { } TEST_CASE("JsonArray::begin()/end()") { - SECTION("Mutable") { - run_iterator_test(); - } + run_iterator_test(); +} + +TEST_CASE("JsonArrayConst::begin()/end()") { + run_iterator_test(); } diff --git a/test/JsonArray/subscript.cpp b/test/JsonArray/subscript.cpp index f70f4c93..a50fe958 100644 --- a/test/JsonArray/subscript.cpp +++ b/test/JsonArray/subscript.cpp @@ -58,9 +58,6 @@ TEST_CASE("JsonArray::operator[]") { array[0] = arr2; REQUIRE(arr2 == array[0].as()); - REQUIRE(arr2 == array[0].as()); // <- short hand - // REQUIRE(arr2 == array[0].as()); - // REQUIRE(arr2 == array[0].as()); // <- short hand REQUIRE(true == array[0].is()); REQUIRE(false == array[0].is()); } @@ -72,7 +69,6 @@ TEST_CASE("JsonArray::operator[]") { array[0] = obj; REQUIRE(obj == array[0].as()); - REQUIRE(obj == array[0].as()); // <- short hand REQUIRE(true == array[0].is()); REQUIRE(false == array[0].is()); } @@ -148,3 +144,18 @@ TEST_CASE("JsonArray::operator[]") { } #endif } + +TEST_CASE("JsonArrayConst::operator[]") { + DynamicJsonDocument doc; + JsonArray array = doc.to(); + array.add(0); + + SECTION("int") { + array[0] = 123; + JsonArrayConst carr = array; + + REQUIRE(123 == carr[0].as()); + REQUIRE(true == carr[0].is()); + REQUIRE(false == carr[0].is()); + } +} diff --git a/test/JsonObject/CMakeLists.txt b/test/JsonObject/CMakeLists.txt index dbed2e90..13e59189 100644 --- a/test/JsonObject/CMakeLists.txt +++ b/test/JsonObject/CMakeLists.txt @@ -7,6 +7,7 @@ add_executable(JsonObjectTests copy.cpp createNestedArray.cpp createNestedObject.cpp + equals.cpp get.cpp invalid.cpp is.cpp diff --git a/test/JsonObject/containsKey.cpp b/test/JsonObject/containsKey.cpp index 43776219..a423a31d 100644 --- a/test/JsonObject/containsKey.cpp +++ b/test/JsonObject/containsKey.cpp @@ -10,12 +10,15 @@ TEST_CASE("JsonObject::containsKey()") { JsonObject obj = doc.to(); obj.set("hello", 42); - SECTION("returns false if key not present") { + SECTION("returns true only if key is present") { REQUIRE(false == obj.containsKey("world")); + REQUIRE(true == obj.containsKey("hello")); } - SECTION("returns true if key present") { - REQUIRE(true == obj.containsKey("hello")); + SECTION("works with JsonObjectConst") { + JsonObjectConst cobj = obj; + REQUIRE(false == cobj.containsKey("world")); + REQUIRE(true == cobj.containsKey("hello")); } SECTION("returns false after remove()") { diff --git a/test/JsonObject/copy.cpp b/test/JsonObject/copy.cpp index 8691fe9b..b82c3b5d 100644 --- a/test/JsonObject/copy.cpp +++ b/test/JsonObject/copy.cpp @@ -56,4 +56,13 @@ TEST_CASE("JsonObject::copyFrom()") { REQUIRE(doc1.memoryUsage() == doc2.memoryUsage()); REQUIRE(obj2["hello"] == std::string("world")); } + + SECTION("should work with JsonObjectConst") { + obj1["hello"] = "world"; + + obj2.copyFrom(static_cast(obj1)); + + REQUIRE(doc1.memoryUsage() == doc2.memoryUsage()); + REQUIRE(obj2["hello"] == std::string("world")); + } } diff --git a/test/JsonObject/equals.cpp b/test/JsonObject/equals.cpp new file mode 100644 index 00000000..0d49a289 --- /dev/null +++ b/test/JsonObject/equals.cpp @@ -0,0 +1,35 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +TEST_CASE("JsonObject::operator==()") { + DynamicJsonDocument doc1; + JsonObject obj1 = doc1.to(); + JsonObjectConst obj1c = obj1; + + DynamicJsonDocument doc2; + JsonObject obj2 = doc2.to(); + JsonObjectConst obj2c = obj2; + + SECTION("should return false when objs differ") { + obj1["hello"] = "coucou"; + obj2["world"] = 1; + + REQUIRE_FALSE(obj1 == obj2); + REQUIRE_FALSE(obj1c == obj2c); + } + + SECTION("should return false when objs differ") { + obj1["hello"] = "world"; + obj1["anwser"] = 42; + // insert in different order + obj2["anwser"] = 42; + obj2["hello"] = "world"; + + REQUIRE(obj1 == obj2); + REQUIRE(obj1c == obj2c); + } +} diff --git a/test/JsonObject/get.cpp b/test/JsonObject/get.cpp index b3b8dc67..3f954274 100644 --- a/test/JsonObject/get.cpp +++ b/test/JsonObject/get.cpp @@ -27,4 +27,11 @@ TEST_CASE("JsonObject::get()") { REQUIRE(std::string("world") == obj.get(vla)); } #endif + + SECTION("works on JsonObjectConst") { + obj.set("hello", "world"); + const char* value = + static_cast(obj).get("hello"); + REQUIRE_THAT(value, Equals("world")); + } } diff --git a/test/JsonObject/iterator.cpp b/test/JsonObject/iterator.cpp index 5c1096f8..9b1208a6 100644 --- a/test/JsonObject/iterator.cpp +++ b/test/JsonObject/iterator.cpp @@ -26,23 +26,35 @@ TEST_CASE("JsonObject::begin()/end()") { REQUIRE(obj.end() == it); } - // SECTION("ConstIterator") { - // const JsonObject const_object = obj; - // JsonObject::iterator it = const_object.begin(); - - // REQUIRE(const_object.end() != it); - // REQUIRE_THAT(it->key(), Equals("ab")); - // REQUIRE(12 == it->value()); - // ++it; - // REQUIRE(const_object.end() != it); - // REQUIRE_THAT(it->key(), Equals("cd")); - // REQUIRE(34 == it->value()); - // ++it; - // REQUIRE(const_object.end() == it); - // } - SECTION("Dereferencing end() is safe") { REQUIRE(obj.end()->key().isNull()); REQUIRE(obj.end()->value().isNull()); } } + +TEST_CASE("JsonObjectConst::begin()/end()") { + StaticJsonDocument doc; + JsonObject obj = doc.to(); + obj["ab"] = 12; + obj["cd"] = 34; + + JsonObjectConst cobj = obj; + + SECTION("NonConstIterator") { + JsonObjectConst::iterator it = cobj.begin(); + REQUIRE(cobj.end() != it); + REQUIRE(it->key() == "ab"); + REQUIRE(12 == it->value()); + ++it; + REQUIRE(cobj.end() != it); + REQUIRE(it->key() == "cd"); + REQUIRE(34 == it->value()); + ++it; + REQUIRE(cobj.end() == it); + } + + SECTION("Dereferencing end() is safe") { + REQUIRE(cobj.end()->key().isNull()); + REQUIRE(cobj.end()->value().isNull()); + } +} diff --git a/test/JsonObject/subscript.cpp b/test/JsonObject/subscript.cpp index d04f0b91..47ec20d5 100644 --- a/test/JsonObject/subscript.cpp +++ b/test/JsonObject/subscript.cpp @@ -58,13 +58,7 @@ TEST_CASE("JsonObject::operator[]") { obj["hello"] = arr; REQUIRE(arr == obj["hello"].as()); - REQUIRE(arr == obj["hello"].as()); // <- short hand - // REQUIRE(arr == obj["hello"].as()); - // REQUIRE(arr == obj["hello"].as()); // <- short hand REQUIRE(true == obj["hello"].is()); - REQUIRE(true == obj["hello"].is()); - REQUIRE(true == obj["hello"].is()); - REQUIRE(true == obj["hello"].is()); REQUIRE(false == obj["hello"].is()); } @@ -75,9 +69,7 @@ TEST_CASE("JsonObject::operator[]") { obj["hello"] = obj2; REQUIRE(obj2 == obj["hello"].as()); - REQUIRE(obj2 == obj["hello"].as()); REQUIRE(true == obj["hello"].is()); - REQUIRE(true == obj["hello"].is()); REQUIRE(false == obj["hello"].is()); } @@ -219,4 +211,12 @@ TEST_CASE("JsonObject::operator[]") { REQUIRE(std::string("world") == obj[vla]); } #endif + + SECTION("chain") { + obj.createNestedObject("hello")["world"] = 123; + + REQUIRE(123 == obj["hello"]["world"].as()); + REQUIRE(true == obj["hello"]["world"].is()); + REQUIRE(false == obj["hello"]["world"].is()); + } } diff --git a/test/JsonVariant/as.cpp b/test/JsonVariant/as.cpp index e2e37552..7c0a450f 100644 --- a/test/JsonVariant/as.cpp +++ b/test/JsonVariant/as.cpp @@ -158,4 +158,16 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as() == 9223372036854775807); } #endif + + SECTION("should work on JsonVariantConst") { + variant.set("hello"); + + JsonVariantConst cvar = variant; + + REQUIRE(cvar.as() == false); + REQUIRE(cvar.as() == 0L); + REQUIRE(cvar.as() == std::string("hello")); + REQUIRE(cvar.as() == std::string("hello")); + // REQUIRE(cvar.as() == std::string("hello")); + } } diff --git a/test/JsonVariant/is.cpp b/test/JsonVariant/is.cpp index c9f0097b..b4a3b12b 100644 --- a/test/JsonVariant/is.cpp +++ b/test/JsonVariant/is.cpp @@ -5,116 +5,146 @@ #include #include -void checkIsArray(JsonArray value) { - DynamicJsonDocument doc; - JsonVariant var = doc.to(); - var.set(value); +template +void checkIsArray(TVariant var) { + REQUIRE(var.template is()); - REQUIRE(var.is()); - REQUIRE(var.is()); - REQUIRE(var.is()); - REQUIRE(var.is()); - - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); } -void checkIsBool(bool value) { +void testArray(JsonArray value) { DynamicJsonDocument doc; + JsonVariant var = doc.to(); var.set(value); - REQUIRE(var.is()); + checkIsArray(var); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); + JsonVariantConst cvar = var; + checkIsArray(cvar); } -void checkIsFloat(double value) { +template +void checkIsBool(TVariant var) { + REQUIRE(var.template is()); + + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); +} + +void testBool(bool value) { DynamicJsonDocument doc; JsonVariant var = doc.to(); var.set(value); - REQUIRE(var.is()); - REQUIRE(var.is()); + checkIsBool(var); + checkIsBool(JsonVariantConst(var)); +} - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); +template +void checkIsFloat(TVariant var) { + REQUIRE(var.template is()); + REQUIRE(var.template is()); + + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); +} + +void testFloat(double value) { + DynamicJsonDocument doc; + JsonVariant var = doc.to(); + var.set(value); + + checkIsFloat(var); + checkIsFloat(JsonVariantConst(var)); +} + +template +void checkIsInteger(TVariant var) { + REQUIRE(var.template is()); + REQUIRE(var.template is()); + REQUIRE(var.template is()); + REQUIRE(var.template is()); + + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); } template -void checkIsInteger(T value) { +void testInteger(T value) { DynamicJsonDocument doc; JsonVariant var = doc.to(); var.set(value); - REQUIRE(var.is()); - REQUIRE(var.is()); - REQUIRE(var.is()); - REQUIRE(var.is()); - - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); + checkIsInteger(var); + checkIsInteger(JsonVariantConst(var)); } -void checkIsString(const char *value) { +template +void checkIsString(TVariant var) { + REQUIRE(var.template is()); + REQUIRE(var.template is()); + + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); + REQUIRE_FALSE(var.template is()); +} + +void testString(const char *value) { DynamicJsonDocument doc; JsonVariant var = doc.to(); var.set(value); - REQUIRE(var.is()); - REQUIRE(var.is()); - - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); - REQUIRE_FALSE(var.is()); + checkIsString(var); + checkIsString(JsonVariantConst(var)); } TEST_CASE("JsonVariant::is()") { SECTION("JsonArray") { DynamicJsonDocument doc; JsonArray array = doc.to(); - checkIsArray(array); + testArray(array); } SECTION("bool") { - checkIsBool(true); - checkIsBool(false); + testBool(true); + testBool(false); } SECTION("double") { - checkIsFloat(4.2); + testFloat(4.2); } SECTION("int") { - checkIsInteger(42); + testInteger(42); } SECTION("long") { - checkIsInteger(42L); + testInteger(42L); } SECTION("string") { - checkIsString("42"); + testString("42"); } } diff --git a/test/JsonVariant/isnull.cpp b/test/JsonVariant/isnull.cpp index 63a100fa..98b3036c 100644 --- a/test/JsonVariant/isnull.cpp +++ b/test/JsonVariant/isnull.cpp @@ -44,4 +44,12 @@ TEST_CASE("JsonVariant::isNull()") { variant.set(JsonObject()); REQUIRE(variant.isNull() == true); }*/ + + SECTION("works with JsonVariantConst") { + variant.set(42); + + JsonVariantConst cvar = variant; + + REQUIRE(cvar.isNull() == false); + } } diff --git a/test/JsonVariant/subscript.cpp b/test/JsonVariant/subscript.cpp index c4e7b440..b3e7c6a2 100644 --- a/test/JsonVariant/subscript.cpp +++ b/test/JsonVariant/subscript.cpp @@ -120,3 +120,55 @@ TEST_CASE("JsonVariant::operator[]") { } #endif } + +TEST_CASE("JsonVariantConst::operator[]") { + DynamicJsonDocument doc; + JsonVariant var = doc.to(); + JsonVariantConst cvar = var; + + SECTION("The JsonVariant is undefined") { + REQUIRE(0 == cvar.size()); + REQUIRE(cvar["0"].isNull()); + REQUIRE(cvar[0].isNull()); + } + + SECTION("The JsonVariant is a string") { + var.set("hello world"); + REQUIRE(0 == cvar.size()); + REQUIRE(cvar["0"].isNull()); + REQUIRE(cvar[0].isNull()); + } + + SECTION("The JsonVariant is a JsonArray") { + JsonArray array = var.to(); + + SECTION("get value") { + array.add("element at index 0"); + array.add("element at index 1"); + + REQUIRE(2 == cvar.size()); + REQUIRE(std::string("element at index 0") == cvar[0]); + REQUIRE(std::string("element at index 1") == cvar[1]); + REQUIRE(std::string("element at index 0") == + var[static_cast(0)]); // issue #381 + REQUIRE(cvar[666].isNull()); + REQUIRE(cvar[3].isNull()); + REQUIRE(cvar["0"].isNull()); + } + } + + SECTION("The JsonVariant is a JsonObject") { + JsonObject object = var.to(); + + SECTION("get value") { + object["a"] = "element at key \"a\""; + object["b"] = "element at key \"b\""; + + REQUIRE(2 == cvar.size()); + REQUIRE(std::string("element at key \"a\"") == cvar["a"]); + REQUIRE(std::string("element at key \"b\"") == cvar["b"]); + REQUIRE(cvar["c"].isNull()); + REQUIRE(cvar[0].isNull()); + } + } +} diff --git a/test/JsonVariant/undefined.cpp b/test/JsonVariant/undefined.cpp index 8adcd5f5..1e8fbf87 100644 --- a/test/JsonVariant/undefined.cpp +++ b/test/JsonVariant/undefined.cpp @@ -32,15 +32,7 @@ TEST_CASE("JsonVariant undefined") { REQUIRE(variant.as().isNull()); } - SECTION("as()") { - REQUIRE(variant.as().isNull()); - } - SECTION("as()") { REQUIRE(variant.as().isNull()); } - - SECTION("as()") { - REQUIRE(variant.as().isNull()); - } } diff --git a/test/Misc/TypeTraits.cpp b/test/Misc/TypeTraits.cpp index 32a64b8b..884f2f9e 100644 --- a/test/Misc/TypeTraits.cpp +++ b/test/Misc/TypeTraits.cpp @@ -24,12 +24,6 @@ TEST_CASE("Polyfills/type_traits") { REQUIRE((is_array::value)); } - SECTION("IsVariant") { - REQUIRE( - static_cast(IsVariant >::value)); - REQUIRE(static_cast(IsVariant::value)); - } - SECTION("is_const") { CHECK(is_const::value == false); CHECK(is_const::value == true);