mirror of
				https://github.com/eledio-devices/thirdparty-ArduinoJson.git
				synced 2025-10-31 16:14:11 +01:00 
			
		
		
		
	Extracted VariantData and CollectionData classes
This commit is contained in:
		| @@ -7,6 +7,7 @@ HEAD | ||||
| * Removed the automatic expansion of `DynamicJsonDocument`, it now has a fixed capacity. | ||||
| * Restored the monotonic allocator because the code was getting too big | ||||
| * Reduced the memory usage | ||||
| * Reduced the code size | ||||
| * Removed spurious files in the Particle library | ||||
|  | ||||
| v6.6.0-beta (2018-11-13) | ||||
|   | ||||
| @@ -15,6 +15,7 @@ | ||||
|  | ||||
| #include "ArduinoJson/Array/ArrayImpl.hpp" | ||||
| #include "ArduinoJson/Array/ArraySubscript.hpp" | ||||
| #include "ArduinoJson/Collection/CollectionImpl.hpp" | ||||
| #include "ArduinoJson/Object/ObjectImpl.hpp" | ||||
| #include "ArduinoJson/Object/ObjectSubscript.hpp" | ||||
| #include "ArduinoJson/Variant/VariantAsImpl.hpp" | ||||
|   | ||||
| @@ -4,92 +4,26 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "../Variant/SlotFunctions.hpp" | ||||
| #include "../Variant/VariantData.hpp" | ||||
| #include "../Collection/CollectionData.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| inline VariantData* arrayAdd(ArrayData* arr, MemoryPool* pool) { | ||||
|   if (!arr) return 0; | ||||
|  | ||||
|   VariantSlot* slot = pool->allocVariant(); | ||||
|   if (!slot) return 0; | ||||
|  | ||||
|   slot->init(); | ||||
|  | ||||
|   if (arr->tail) { | ||||
|     slot->attachTo(arr->tail); | ||||
|     arr->tail = slot; | ||||
|   } else { | ||||
|     arr->head = slot; | ||||
|     arr->tail = slot; | ||||
|   } | ||||
|  | ||||
|   return slot->getData(); | ||||
| inline VariantData *arrayAdd(CollectionData *arr, MemoryPool *pool) { | ||||
|   return arr ? arr->add(pool) : 0; | ||||
| } | ||||
|  | ||||
| inline VariantSlot* arrayGetSlot(const ArrayData* arr, size_t index) { | ||||
|   if (!arr) return 0; | ||||
|   return arr->head->getNext(index); | ||||
| } | ||||
|  | ||||
| inline VariantData* arrayGet(const ArrayData* arr, size_t index) { | ||||
|   VariantSlot* slot = arrayGetSlot(arr, index); | ||||
|   return slot ? slot->getData() : 0; | ||||
| } | ||||
|  | ||||
| inline void arrayRemove(ArrayData* arr, VariantSlot* slot) { | ||||
|   if (!arr || !slot) return; | ||||
|  | ||||
|   VariantSlot* prev = slot->getPrev(arr->head); | ||||
|   VariantSlot* next = slot->getNext(); | ||||
|  | ||||
|   if (prev) | ||||
|     prev->setNext(next); | ||||
| template <typename Visitor> | ||||
| inline void arrayAccept(const CollectionData *arr, Visitor &visitor) { | ||||
|   if (arr) | ||||
|     visitor.visitArray(*arr); | ||||
|   else | ||||
|     arr->head = next; | ||||
|   if (!next) arr->tail = prev; | ||||
|     visitor.visitNull(); | ||||
| } | ||||
|  | ||||
| inline void arrayRemove(ArrayData* arr, size_t index) { | ||||
|   arrayRemove(arr, arrayGetSlot(arr, index)); | ||||
| } | ||||
| inline bool arrayEquals(const CollectionData *lhs, const CollectionData *rhs) { | ||||
|   if (lhs == rhs) return true; | ||||
|   if (!lhs || !rhs) return false; | ||||
|  | ||||
| inline void arrayClear(ArrayData* arr) { | ||||
|   if (!arr) return; | ||||
|   arr->head = 0; | ||||
|   arr->tail = 0; | ||||
| } | ||||
|  | ||||
| bool variantCopy(VariantData*, const VariantData*, MemoryPool*); | ||||
|  | ||||
| inline bool arrayCopy(ArrayData* dst, const ArrayData* src, MemoryPool* pool) { | ||||
|   if (!dst || !src) return false; | ||||
|   arrayClear(dst); | ||||
|   for (VariantSlot* s = src->head; s; s = s->getNext()) { | ||||
|     if (!variantCopy(arrayAdd(dst, pool), s->getData(), pool)) return false; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool variantEquals(const VariantData*, const VariantData*); | ||||
|  | ||||
| inline bool arrayEquals(const ArrayData* a1, const ArrayData* a2) { | ||||
|   if (a1 == a2) return true; | ||||
|   if (!a1 || !a2) return false; | ||||
|   VariantSlot* s1 = a1->head; | ||||
|   VariantSlot* s2 = a2->head; | ||||
|   for (;;) { | ||||
|     if (s1 == s2) return true; | ||||
|     if (!s1 || !s2) return false; | ||||
|     if (!variantEquals(s1->getData(), s2->getData())) return false; | ||||
|     s1 = s1->getNext(); | ||||
|     s2 = s2->getNext(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| inline size_t arraySize(const ArrayData* arr) { | ||||
|   if (!arr) return 0; | ||||
|   return slotSize(arr->head); | ||||
|   return lhs->equalsArray(*rhs); | ||||
| } | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
| @@ -11,8 +11,7 @@ namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| class VariantPtr { | ||||
|  public: | ||||
|   VariantPtr(MemoryPool *memoryPool, VariantData *data) | ||||
|       : _variant(memoryPool, data) {} | ||||
|   VariantPtr(MemoryPool *pool, VariantData *data) : _variant(pool, data) {} | ||||
|  | ||||
|   VariantRef *operator->() { | ||||
|     return &_variant; | ||||
| @@ -29,14 +28,14 @@ class VariantPtr { | ||||
| class ArrayIterator { | ||||
|  public: | ||||
|   ArrayIterator() : _slot(0) {} | ||||
|   explicit ArrayIterator(MemoryPool *memoryPool, VariantSlot *slot) | ||||
|       : _memoryPool(memoryPool), _slot(slot) {} | ||||
|   explicit ArrayIterator(MemoryPool *pool, VariantSlot *slot) | ||||
|       : _pool(pool), _slot(slot) {} | ||||
|  | ||||
|   VariantRef operator*() const { | ||||
|     return VariantRef(_memoryPool, _slot->getData()); | ||||
|     return VariantRef(_pool, _slot->data()); | ||||
|   } | ||||
|   VariantPtr operator->() { | ||||
|     return VariantPtr(_memoryPool, _slot->getData()); | ||||
|     return VariantPtr(_pool, _slot->data()); | ||||
|   } | ||||
|  | ||||
|   bool operator==(const ArrayIterator &other) const { | ||||
| @@ -48,12 +47,12 @@ class ArrayIterator { | ||||
|   } | ||||
|  | ||||
|   ArrayIterator &operator++() { | ||||
|     _slot = _slot->getNext(); | ||||
|     _slot = _slot->next(); | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   ArrayIterator &operator+=(size_t distance) { | ||||
|     _slot = _slot->getNext(distance); | ||||
|     _slot = _slot->next(distance); | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
| @@ -62,7 +61,7 @@ class ArrayIterator { | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   MemoryPool *_memoryPool; | ||||
|   MemoryPool *_pool; | ||||
|   VariantSlot *_slot; | ||||
| }; | ||||
|  | ||||
| @@ -88,10 +87,10 @@ class ArrayConstRefIterator { | ||||
|   explicit ArrayConstRefIterator(const VariantSlot *slot) : _slot(slot) {} | ||||
|  | ||||
|   VariantConstRef operator*() const { | ||||
|     return VariantConstRef(_slot->getData()); | ||||
|     return VariantConstRef(_slot->data()); | ||||
|   } | ||||
|   VariantConstPtr operator->() { | ||||
|     return VariantConstPtr(_slot->getData()); | ||||
|     return VariantConstPtr(_slot->data()); | ||||
|   } | ||||
|  | ||||
|   bool operator==(const ArrayConstRefIterator &other) const { | ||||
| @@ -103,12 +102,12 @@ class ArrayConstRefIterator { | ||||
|   } | ||||
|  | ||||
|   ArrayConstRefIterator &operator++() { | ||||
|     _slot = _slot->getNext(); | ||||
|     _slot = _slot->next(); | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   ArrayConstRefIterator &operator+=(size_t distance) { | ||||
|     _slot = _slot->getNext(distance); | ||||
|     _slot = _slot->next(distance); | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -21,16 +21,21 @@ class ArraySubscript; | ||||
| template <typename TData> | ||||
| class ArrayRefBase { | ||||
|  public: | ||||
|   template <typename Visitor> | ||||
|   FORCE_INLINE void accept(Visitor& visitor) const { | ||||
|     arrayAccept(_data, visitor); | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE bool isNull() const { | ||||
|     return _data == 0; | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE VariantConstRef operator[](size_t index) const { | ||||
|     return VariantConstRef(arrayGet(_data, index)); | ||||
|     return VariantConstRef(_data ? _data->get(index) : 0); | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE size_t size() const { | ||||
|     return arraySize(_data); | ||||
|     return _data ? _data->size() : 0; | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
| @@ -38,24 +43,17 @@ class ArrayRefBase { | ||||
|   TData* _data; | ||||
| }; | ||||
|  | ||||
| class ArrayConstRef : public ArrayRefBase<const ArrayData>, public Visitable { | ||||
| class ArrayConstRef : public ArrayRefBase<const CollectionData>, | ||||
|                       public Visitable { | ||||
|   friend class ArrayRef; | ||||
|   typedef ArrayRefBase<const ArrayData> base_type; | ||||
|   typedef ArrayRefBase<const CollectionData> base_type; | ||||
|  | ||||
|  public: | ||||
|   typedef ArrayConstRefIterator iterator; | ||||
|  | ||||
|   template <typename Visitor> | ||||
|   FORCE_INLINE void accept(Visitor& visitor) const { | ||||
|     if (_data) | ||||
|       visitor.visitArray(*this); | ||||
|     else | ||||
|       visitor.visitNull(); | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE iterator begin() const { | ||||
|     if (!_data) return iterator(); | ||||
|     return iterator(_data->head); | ||||
|     return iterator(_data->head()); | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE iterator end() const { | ||||
| @@ -63,25 +61,25 @@ class ArrayConstRef : public ArrayRefBase<const ArrayData>, public Visitable { | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE ArrayConstRef() : base_type(0) {} | ||||
|   FORCE_INLINE ArrayConstRef(const ArrayData* data) : base_type(data) {} | ||||
|   FORCE_INLINE ArrayConstRef(const CollectionData* data) : base_type(data) {} | ||||
|  | ||||
|   FORCE_INLINE bool operator==(ArrayConstRef rhs) const { | ||||
|     return arrayEquals(_data, rhs._data); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| class ArrayRef : public ArrayRefBase<ArrayData>, public Visitable { | ||||
|   typedef ArrayRefBase<ArrayData> base_type; | ||||
| class ArrayRef : public ArrayRefBase<CollectionData>, public Visitable { | ||||
|   typedef ArrayRefBase<CollectionData> base_type; | ||||
|  | ||||
|  public: | ||||
|   typedef ArrayIterator iterator; | ||||
|  | ||||
|   FORCE_INLINE ArrayRef() : base_type(0), _memoryPool(0) {} | ||||
|   FORCE_INLINE ArrayRef(MemoryPool* pool, ArrayData* data) | ||||
|       : base_type(data), _memoryPool(pool) {} | ||||
|   FORCE_INLINE ArrayRef() : base_type(0), _pool(0) {} | ||||
|   FORCE_INLINE ArrayRef(MemoryPool* pool, CollectionData* data) | ||||
|       : base_type(data), _pool(pool) {} | ||||
|  | ||||
|   operator VariantRef() { | ||||
|     return VariantRef(_memoryPool, getVariantData(_data)); | ||||
|     return VariantRef(_pool, reinterpret_cast<VariantData*>(_data)); | ||||
|   } | ||||
|  | ||||
|   operator ArrayConstRef() const { | ||||
| @@ -110,12 +108,12 @@ class ArrayRef : public ArrayRefBase<ArrayData>, public Visitable { | ||||
|   } | ||||
|  | ||||
|   VariantRef add() const { | ||||
|     return VariantRef(_memoryPool, arrayAdd(_data, _memoryPool)); | ||||
|     return VariantRef(_pool, arrayAdd(_data, _pool)); | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE iterator begin() const { | ||||
|     if (!_data) return iterator(); | ||||
|     return iterator(_memoryPool, _data->head); | ||||
|     return iterator(_pool, _data->head()); | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE iterator end() const { | ||||
| @@ -153,7 +151,8 @@ class ArrayRef : public ArrayRefBase<ArrayData>, public Visitable { | ||||
|  | ||||
|   // Copy a ArrayRef | ||||
|   FORCE_INLINE bool copyFrom(ArrayRef src) const { | ||||
|     return arrayCopy(_data, src._data, _memoryPool); | ||||
|     if (!_data || !src._data) return false; | ||||
|     return _data->copyFrom(*src._data, _pool); | ||||
|   } | ||||
|  | ||||
|   // Exports a 1D array | ||||
| @@ -191,30 +190,22 @@ class ArrayRef : public ArrayRefBase<ArrayData>, public Visitable { | ||||
|  | ||||
|   // Gets the value at the specified index. | ||||
|   FORCE_INLINE VariantRef get(size_t index) const { | ||||
|     return VariantRef(_memoryPool, arrayGet(_data, index)); | ||||
|     return VariantRef(_pool, _data ? _data->get(index) : 0); | ||||
|   } | ||||
|  | ||||
|   // Removes element at specified position. | ||||
|   FORCE_INLINE void remove(iterator it) const { | ||||
|     arrayRemove(_data, it.internal()); | ||||
|     if (!_data) return; | ||||
|     _data->remove(it.internal()); | ||||
|   } | ||||
|  | ||||
|   // Removes element at specified index. | ||||
|   FORCE_INLINE void remove(size_t index) const { | ||||
|     arrayRemove(_data, index); | ||||
|   } | ||||
|  | ||||
|   template <typename Visitor> | ||||
|   FORCE_INLINE void accept(Visitor& visitor) const { | ||||
|     ArrayConstRef(_data).accept(visitor); | ||||
|     if (!_data) return; | ||||
|     _data->remove(index); | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   template <typename TValueRef> | ||||
|   FORCE_INLINE bool add_impl(TValueRef value) const { | ||||
|     return add().set(value); | ||||
|   } | ||||
|  | ||||
|   MemoryPool* _memoryPool; | ||||
|   MemoryPool* _pool; | ||||
| }; | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
							
								
								
									
										68
									
								
								src/ArduinoJson/Collection/CollectionData.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/ArduinoJson/Collection/CollectionData.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| // ArduinoJson - arduinojson.org | ||||
| // Copyright Benoit Blanchon 2014-2018 | ||||
| // MIT License | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| class MemoryPool; | ||||
| class VariantData; | ||||
| class VariantSlot; | ||||
|  | ||||
| class CollectionData { | ||||
|   VariantSlot *_head; | ||||
|   VariantSlot *_tail; | ||||
|  | ||||
|  public: | ||||
|   // Must be a POD! | ||||
|   // - no constructor | ||||
|   // - no destructor | ||||
|   // - no virtual | ||||
|   // - no inheritance | ||||
|   VariantSlot *addSlot(MemoryPool *); | ||||
|  | ||||
|   VariantData *add(MemoryPool *pool); | ||||
|  | ||||
|   template <typename TKey> | ||||
|   VariantData *add(TKey key, MemoryPool *pool); | ||||
|  | ||||
|   void clear(); | ||||
|  | ||||
|   template <typename TKey> | ||||
|   bool containsKey(const TKey &key) const; | ||||
|  | ||||
|   bool copyFrom(const CollectionData &src, MemoryPool *pool); | ||||
|  | ||||
|   bool equalsArray(const CollectionData &other) const; | ||||
|   bool equalsObject(const CollectionData &other) const; | ||||
|  | ||||
|   VariantData *get(size_t index) const; | ||||
|  | ||||
|   template <typename TKey> | ||||
|   VariantData *get(TKey key) const; | ||||
|  | ||||
|   VariantSlot *head() const { | ||||
|     return _head; | ||||
|   } | ||||
|  | ||||
|   void remove(size_t index); | ||||
|  | ||||
|   template <typename TKey> | ||||
|   void remove(TKey key) { | ||||
|     remove(getSlot(key)); | ||||
|   } | ||||
|  | ||||
|   void remove(VariantSlot *slot); | ||||
|  | ||||
|   size_t size() const; | ||||
|  | ||||
|  private: | ||||
|   VariantSlot *getSlot(size_t index) const; | ||||
|  | ||||
|   template <typename TKey> | ||||
|   VariantSlot *getSlot(TKey key) const; | ||||
|  | ||||
|   VariantSlot *getPreviousSlot(VariantSlot *) const; | ||||
| }; | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
							
								
								
									
										145
									
								
								src/ArduinoJson/Collection/CollectionImpl.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								src/ArduinoJson/Collection/CollectionImpl.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | ||||
| // ArduinoJson - arduinojson.org | ||||
| // Copyright Benoit Blanchon 2014-2018 | ||||
| // MIT License | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "../Variant/VariantData.hpp" | ||||
| #include "CollectionData.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| inline VariantSlot* CollectionData::addSlot(MemoryPool* pool) { | ||||
|   VariantSlot* slot = pool->allocVariant(); | ||||
|   if (!slot) return 0; | ||||
|  | ||||
|   if (_tail) { | ||||
|     _tail->setNextNotNull(slot); | ||||
|     _tail = slot; | ||||
|   } else { | ||||
|     _head = slot; | ||||
|     _tail = slot; | ||||
|   } | ||||
|  | ||||
|   slot->clear(); | ||||
|   return slot; | ||||
| } | ||||
|  | ||||
| inline VariantData* CollectionData::add(MemoryPool* pool) { | ||||
|   return addSlot(pool)->data(); | ||||
| } | ||||
|  | ||||
| template <typename TKey> | ||||
| inline VariantData* CollectionData::add(TKey key, MemoryPool* pool) { | ||||
|   VariantSlot* slot = addSlot(pool); | ||||
|   if (!slotSetKey(slot, key, pool)) return 0; | ||||
|   return slot->data(); | ||||
| } | ||||
|  | ||||
| inline void CollectionData::clear() { | ||||
|   _head = 0; | ||||
|   _tail = 0; | ||||
| } | ||||
|  | ||||
| template <typename TKey> | ||||
| inline bool CollectionData::containsKey(const TKey& key) const { | ||||
|   return getSlot(key) != 0; | ||||
| } | ||||
|  | ||||
| inline bool CollectionData::copyFrom(const CollectionData& src, | ||||
|                                      MemoryPool* pool) { | ||||
|   clear(); | ||||
|   for (VariantSlot* s = src._head; s; s = s->next()) { | ||||
|     VariantData* var; | ||||
|     if (s->key() != 0) { | ||||
|       if (s->ownsKey()) | ||||
|         var = add(ZeroTerminatedRamString(s->key()), pool); | ||||
|       else | ||||
|         var = add(ZeroTerminatedRamStringConst(s->key()), pool); | ||||
|     } else { | ||||
|       var = add(pool); | ||||
|     } | ||||
|     if (!var) return false; | ||||
|     if (!var->copyFrom(*s->data(), pool)) return false; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline bool CollectionData::equalsObject(const CollectionData& other) const { | ||||
|   size_t count = 0; | ||||
|   for (VariantSlot* slot = _head; slot; slot = slot->next()) { | ||||
|     VariantData* v1 = slot->data(); | ||||
|     VariantData* v2 = other.get(makeString(slot->key())); | ||||
|     if (!variantEquals(v1, v2)) return false; | ||||
|     count++; | ||||
|   } | ||||
|   return count == other.size(); | ||||
| } | ||||
|  | ||||
| inline bool CollectionData::equalsArray(const CollectionData& other) const { | ||||
|   VariantSlot* s1 = _head; | ||||
|   VariantSlot* s2 = other._head; | ||||
|   for (;;) { | ||||
|     if (s1 == s2) return true; | ||||
|     if (!s1 || !s2) return false; | ||||
|     if (!variantEquals(s1->data(), s2->data())) return false; | ||||
|     s1 = s1->next(); | ||||
|     s2 = s2->next(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| template <typename TKey> | ||||
| inline VariantSlot* CollectionData::getSlot(TKey key) const { | ||||
|   VariantSlot* slot = _head; | ||||
|   while (slot) { | ||||
|     if (key.equals(slot->key())) break; | ||||
|     slot = slot->next(); | ||||
|   } | ||||
|   return slot; | ||||
| } | ||||
|  | ||||
| inline VariantSlot* CollectionData::getSlot(size_t index) const { | ||||
|   return _head->next(index); | ||||
| } | ||||
|  | ||||
| inline VariantSlot* CollectionData::getPreviousSlot(VariantSlot* target) const { | ||||
|   VariantSlot* current = _head; | ||||
|   while (current) { | ||||
|     VariantSlot* next = current->next(); | ||||
|     if (next == target) return current; | ||||
|     current = next; | ||||
|   } | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| template <typename TKey> | ||||
| inline VariantData* CollectionData::get(TKey key) const { | ||||
|   VariantSlot* slot = getSlot(key); | ||||
|   return slot ? slot->data() : 0; | ||||
| } | ||||
|  | ||||
| inline VariantData* CollectionData::get(size_t index) const { | ||||
|   VariantSlot* slot = getSlot(index); | ||||
|   return slot ? slot->data() : 0; | ||||
| } | ||||
|  | ||||
| inline void CollectionData::remove(VariantSlot* slot) { | ||||
|   if (!slot) return; | ||||
|   VariantSlot* prev = getPreviousSlot(slot); | ||||
|   VariantSlot* next = slot->next(); | ||||
|   if (prev) | ||||
|     prev->setNext(next); | ||||
|   else | ||||
|     _head = next; | ||||
|   if (!next) _tail = prev; | ||||
| } | ||||
|  | ||||
| inline void CollectionData::remove(size_t index) { | ||||
|   remove(getSlot(index)); | ||||
| } | ||||
|  | ||||
| inline size_t CollectionData::size() const { | ||||
|   return slotSize(_head); | ||||
| } | ||||
|  | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
| @@ -14,63 +14,58 @@ | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| template <template <typename, typename> class TDeserializer, | ||||
|           typename TMemoryPool, typename TReader, typename TWriter> | ||||
| TDeserializer<TReader, TWriter> makeDeserializer(TMemoryPool &memoryPool, | ||||
| template <template <typename, typename> class TDeserializer, typename TReader, | ||||
|           typename TWriter> | ||||
| TDeserializer<TReader, TWriter> makeDeserializer(MemoryPool &pool, | ||||
|                                                  TReader reader, TWriter writer, | ||||
|                                                  uint8_t nestingLimit) { | ||||
|   return TDeserializer<TReader, TWriter>(memoryPool, reader, writer, | ||||
|                                          nestingLimit); | ||||
|   return TDeserializer<TReader, TWriter>(pool, reader, writer, nestingLimit); | ||||
| } | ||||
|  | ||||
| // DeserializationError deserialize(TDocument& doc, TString input); | ||||
| // TDocument = DynamicJsonDocument, StaticJsonDocument | ||||
| // DeserializationError deserialize(JsonDocument& doc, TString input); | ||||
| // TString = const std::string&, const String& | ||||
| template <template <typename, typename> class TDeserializer, typename TDocument, | ||||
|           typename TString> | ||||
| template <template <typename, typename> class TDeserializer, typename TString> | ||||
| typename enable_if<!is_array<TString>::value, DeserializationError>::type | ||||
| deserialize(TDocument &doc, const TString &input) { | ||||
| deserialize(JsonDocument &doc, const TString &input) { | ||||
|   doc.clear(); | ||||
|   return makeDeserializer<TDeserializer>( | ||||
|              doc.memoryPool(), makeReader(input), | ||||
|              makeStringStorage(doc.memoryPool(), input), doc.nestingLimit) | ||||
|       .parse(doc.template to<VariantRef>()); | ||||
|       .parse(doc.data()); | ||||
| } | ||||
| // | ||||
| // DeserializationError deserialize(TDocument& doc, TChar* input); | ||||
| // TDocument = DynamicJsonDocument, StaticJsonDocument | ||||
| // DeserializationError deserialize(JsonDocument& doc, TChar* input); | ||||
| // TChar* = char*, const char*, const FlashStringHelper* | ||||
| template <template <typename, typename> class TDeserializer, typename TDocument, | ||||
|           typename TChar> | ||||
| DeserializationError deserialize(TDocument &doc, TChar *input) { | ||||
| template <template <typename, typename> class TDeserializer, typename TChar> | ||||
| DeserializationError deserialize(JsonDocument &doc, TChar *input) { | ||||
|   doc.clear(); | ||||
|   return makeDeserializer<TDeserializer>( | ||||
|              doc.memoryPool(), makeReader(input), | ||||
|              makeStringStorage(doc.memoryPool(), input), doc.nestingLimit) | ||||
|       .parse(doc.template to<VariantRef>()); | ||||
|       .parse(doc.data()); | ||||
| } | ||||
| // | ||||
| // DeserializationError deserialize(TDocument& doc, TChar* input, size_t | ||||
| // DeserializationError deserialize(JsonDocument& doc, TChar* input, size_t | ||||
| // inputSize); | ||||
| // TDocument = DynamicJsonDocument, StaticJsonDocument | ||||
| // TChar* = char*, const char*, const FlashStringHelper* | ||||
| template <template <typename, typename> class TDeserializer, typename TDocument, | ||||
|           typename TChar> | ||||
| DeserializationError deserialize(TDocument &doc, TChar *input, | ||||
| template <template <typename, typename> class TDeserializer, typename TChar> | ||||
| DeserializationError deserialize(JsonDocument &doc, TChar *input, | ||||
|                                  size_t inputSize) { | ||||
|   doc.clear(); | ||||
|   return makeDeserializer<TDeserializer>( | ||||
|              doc.memoryPool(), makeReader(input, inputSize), | ||||
|              makeStringStorage(doc.memoryPool(), input), doc.nestingLimit) | ||||
|       .parse(doc.template to<VariantRef>()); | ||||
|       .parse(doc.data()); | ||||
| } | ||||
| // | ||||
| // DeserializationError deserialize(TDocument& doc, TStream input); | ||||
| // TDocument = DynamicJsonDocument, StaticJsonDocument | ||||
| // DeserializationError deserialize(JsonDocument& doc, TStream input); | ||||
| // TStream = std::istream&, Stream& | ||||
| template <template <typename, typename> class TDeserializer, typename TDocument, | ||||
|           typename TStream> | ||||
| DeserializationError deserialize(TDocument &doc, TStream &input) { | ||||
| template <template <typename, typename> class TDeserializer, typename TStream> | ||||
| DeserializationError deserialize(JsonDocument &doc, TStream &input) { | ||||
|   doc.clear(); | ||||
|   return makeDeserializer<TDeserializer>( | ||||
|              doc.memoryPool(), makeReader(input), | ||||
|              makeStringStorage(doc.memoryPool(), input), doc.nestingLimit) | ||||
|       .parse(doc.template to<VariantRef>()); | ||||
|       .parse(doc.data()); | ||||
| } | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
| @@ -30,8 +30,8 @@ class JsonDocument : public Visitable { | ||||
|   } | ||||
|  | ||||
|   void clear() { | ||||
|     _memoryPool.clear(); | ||||
|     _rootData.type = JSON_NULL; | ||||
|     _pool.clear(); | ||||
|     _data.setNull(); | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
| @@ -40,16 +40,11 @@ class JsonDocument : public Visitable { | ||||
|   } | ||||
|  | ||||
|   size_t memoryUsage() const { | ||||
|     return _memoryPool.size(); | ||||
|     return _pool.size(); | ||||
|   } | ||||
|  | ||||
|   size_t capacity() const { | ||||
|     return _memoryPool.capacity(); | ||||
|   } | ||||
|  | ||||
|   // for internal use only | ||||
|   MemoryPool& memoryPool() { | ||||
|     return _memoryPool; | ||||
|     return _pool.capacity(); | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
| @@ -58,10 +53,18 @@ class JsonDocument : public Visitable { | ||||
|     return getVariant().template to<T>(); | ||||
|   } | ||||
|  | ||||
|   // for internal use only | ||||
|   MemoryPool& memoryPool() { | ||||
|     return _pool; | ||||
|   } | ||||
|  | ||||
|   VariantData& data() { | ||||
|     return _data; | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   JsonDocument(char* buf, size_t capa) | ||||
|       : nestingLimit(ARDUINOJSON_DEFAULT_NESTING_LIMIT), | ||||
|         _memoryPool(buf, capa) {} | ||||
|       : nestingLimit(ARDUINOJSON_DEFAULT_NESTING_LIMIT), _pool(buf, capa) {} | ||||
|  | ||||
|   void copy(const JsonDocument& src) { | ||||
|     nestingLimit = src.nestingLimit; | ||||
| @@ -70,15 +73,15 @@ class JsonDocument : public Visitable { | ||||
|  | ||||
|  private: | ||||
|   VariantRef getVariant() { | ||||
|     return VariantRef(&_memoryPool, &_rootData); | ||||
|     return VariantRef(&_pool, &_data); | ||||
|   } | ||||
|  | ||||
|   VariantConstRef getVariant() const { | ||||
|     return VariantConstRef(&_rootData); | ||||
|     return VariantConstRef(&_data); | ||||
|   } | ||||
|  | ||||
|   MemoryPool _memoryPool; | ||||
|   VariantData _rootData; | ||||
|   MemoryPool _pool; | ||||
|   VariantData _data; | ||||
| }; | ||||
|  | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| #include "../Numbers/isFloat.hpp" | ||||
| #include "../Numbers/isInteger.hpp" | ||||
| #include "../Polyfills/type_traits.hpp" | ||||
| #include "../Variant/VariantRef.hpp" | ||||
| #include "../Variant/VariantData.hpp" | ||||
| #include "EscapeSequence.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
| @@ -18,26 +18,26 @@ template <typename TReader, typename TStringStorage> | ||||
| class JsonDeserializer { | ||||
|   typedef typename remove_reference<TStringStorage>::type::StringBuilder | ||||
|       StringBuilder; | ||||
|   typedef typename StringBuilder::StringType StringType; | ||||
|   typedef const char *StringType; | ||||
|  | ||||
|  public: | ||||
|   JsonDeserializer(MemoryPool &memoryPool, TReader reader, | ||||
|   JsonDeserializer(MemoryPool &pool, TReader reader, | ||||
|                    TStringStorage stringStorage, uint8_t nestingLimit) | ||||
|       : _memoryPool(&memoryPool), | ||||
|       : _pool(&pool), | ||||
|         _reader(reader), | ||||
|         _stringStorage(stringStorage), | ||||
|         _nestingLimit(nestingLimit), | ||||
|         _loaded(false) {} | ||||
|   DeserializationError parse(VariantRef variant) { | ||||
|   DeserializationError parse(VariantData &variant) { | ||||
|     DeserializationError err = skipSpacesAndComments(); | ||||
|     if (err) return err; | ||||
|  | ||||
|     switch (current()) { | ||||
|       case '[': | ||||
|         return parseArray(variant); | ||||
|         return parseArray(variant.toArray()); | ||||
|  | ||||
|       case '{': | ||||
|         return parseObject(variant); | ||||
|         return parseObject(variant.toObject()); | ||||
|  | ||||
|       default: | ||||
|         return parseValue(variant); | ||||
| @@ -68,12 +68,9 @@ class JsonDeserializer { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   DeserializationError parseArray(VariantRef variant) { | ||||
|   DeserializationError parseArray(CollectionData &array) { | ||||
|     if (_nestingLimit == 0) return DeserializationError::TooDeep; | ||||
|  | ||||
|     ArrayRef array = variant.to<ArrayRef>(); | ||||
|     if (array.isNull()) return DeserializationError::NoMemory; | ||||
|  | ||||
|     // Check opening braket | ||||
|     if (!eat('[')) return DeserializationError::InvalidInput; | ||||
|  | ||||
| @@ -87,12 +84,12 @@ class JsonDeserializer { | ||||
|     // Read each value | ||||
|     for (;;) { | ||||
|       // Allocate slot in array | ||||
|       VariantRef value = array.add(); | ||||
|       if (value.isInvalid()) return DeserializationError::NoMemory; | ||||
|       VariantData *value = array.add(_pool); | ||||
|       if (!value) return DeserializationError::NoMemory; | ||||
|  | ||||
|       // 1 - Parse value | ||||
|       _nestingLimit--; | ||||
|       err = parse(value); | ||||
|       err = parse(*value); | ||||
|       _nestingLimit++; | ||||
|       if (err) return err; | ||||
|  | ||||
| @@ -106,12 +103,9 @@ class JsonDeserializer { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   DeserializationError parseObject(VariantRef variant) { | ||||
|   DeserializationError parseObject(CollectionData &object) { | ||||
|     if (_nestingLimit == 0) return DeserializationError::TooDeep; | ||||
|  | ||||
|     ObjectRef object = variant.to<ObjectRef>(); | ||||
|     if (object.isNull()) return DeserializationError::NoMemory; | ||||
|  | ||||
|     // Check opening brace | ||||
|     if (!eat('{')) return DeserializationError::InvalidInput; | ||||
|  | ||||
| @@ -124,23 +118,24 @@ class JsonDeserializer { | ||||
|  | ||||
|     // Read each key value pair | ||||
|     for (;;) { | ||||
|       // Allocate slot in object | ||||
|       VariantSlot *slot = object.addSlot(_pool); | ||||
|       if (!slot) return DeserializationError::NoMemory; | ||||
|  | ||||
|       // Parse key | ||||
|       StringType key; | ||||
|       err = parseKey(key); | ||||
|       if (err) return err; | ||||
|       slot->setOwnedKey(key); | ||||
|  | ||||
|       // Skip spaces | ||||
|       err = skipSpacesAndComments(); | ||||
|       if (err) return err;  // Colon | ||||
|       if (!eat(':')) return DeserializationError::InvalidInput; | ||||
|  | ||||
|       // Allocate slot in object | ||||
|       VariantRef value = object.set(key); | ||||
|       if (value.isInvalid()) return DeserializationError::NoMemory; | ||||
|  | ||||
|       // Parse value | ||||
|       _nestingLimit--; | ||||
|       err = parse(value); | ||||
|       err = parse(*slot->data()); | ||||
|       _nestingLimit++; | ||||
|       if (err) return err; | ||||
|  | ||||
| @@ -158,7 +153,7 @@ class JsonDeserializer { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   DeserializationError parseValue(VariantRef variant) { | ||||
|   DeserializationError parseValue(VariantData &variant) { | ||||
|     if (isQuote(current())) { | ||||
|       return parseStringValue(variant); | ||||
|     } else { | ||||
| @@ -174,11 +169,11 @@ class JsonDeserializer { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   DeserializationError parseStringValue(VariantRef variant) { | ||||
|   DeserializationError parseStringValue(VariantData &variant) { | ||||
|     StringType value; | ||||
|     DeserializationError err = parseQuotedString(value); | ||||
|     if (err) return err; | ||||
|     variant.set(value); | ||||
|     variant.setOwnedString(value); | ||||
|     return DeserializationError::Ok; | ||||
|   } | ||||
|  | ||||
| @@ -208,7 +203,7 @@ class JsonDeserializer { | ||||
|     } | ||||
|  | ||||
|     result = builder.complete(); | ||||
|     if (result.isNull()) return DeserializationError::NoMemory; | ||||
|     if (!result) return DeserializationError::NoMemory; | ||||
|     return DeserializationError::Ok; | ||||
|   } | ||||
|  | ||||
| @@ -229,11 +224,11 @@ class JsonDeserializer { | ||||
|     } | ||||
|  | ||||
|     result = builder.complete(); | ||||
|     if (result.isNull()) return DeserializationError::NoMemory; | ||||
|     if (!result) return DeserializationError::NoMemory; | ||||
|     return DeserializationError::Ok; | ||||
|   } | ||||
|  | ||||
|   DeserializationError parseNumericValue(VariantRef result) { | ||||
|   DeserializationError parseNumericValue(VariantData &result) { | ||||
|     char buffer[64]; | ||||
|     uint8_t n = 0; | ||||
|  | ||||
| @@ -246,13 +241,13 @@ class JsonDeserializer { | ||||
|     buffer[n] = 0; | ||||
|  | ||||
|     if (isInteger(buffer)) { | ||||
|       result.set(parseInteger<Integer>(buffer)); | ||||
|       result.setInteger(parseInteger<Integer>(buffer)); | ||||
|     } else if (isFloat(buffer)) { | ||||
|       result.set(parseFloat<Float>(buffer)); | ||||
|       result.setFloat(parseFloat<Float>(buffer)); | ||||
|     } else if (!strcmp(buffer, "true")) { | ||||
|       result.set(true); | ||||
|       result.setBoolean(true); | ||||
|     } else if (!strcmp(buffer, "false")) { | ||||
|       result.set(false); | ||||
|       result.setBoolean(false); | ||||
|     } else if (!strcmp(buffer, "null")) { | ||||
|       // already null | ||||
|     } else { | ||||
| @@ -333,7 +328,7 @@ class JsonDeserializer { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   MemoryPool *_memoryPool; | ||||
|   MemoryPool *_pool; | ||||
|   TReader _reader; | ||||
|   TStringStorage _stringStorage; | ||||
|   uint8_t _nestingLimit; | ||||
|   | ||||
| @@ -20,15 +20,16 @@ class JsonSerializer { | ||||
|     _writer.writeFloat(value); | ||||
|   } | ||||
|  | ||||
|   void visitArray(ArrayConstRef array) { | ||||
|   void visitArray(const CollectionData &array) { | ||||
|     _writer.beginArray(); | ||||
|  | ||||
|     ArrayConstRef::iterator it = array.begin(); | ||||
|     while (it != array.end()) { | ||||
|       it->accept(*this); | ||||
|     VariantSlot *slot = array.head(); | ||||
|  | ||||
|       ++it; | ||||
|       if (it == array.end()) break; | ||||
|     while (slot != 0) { | ||||
|       slot->data()->accept(*this); | ||||
|  | ||||
|       slot = slot->next(); | ||||
|       if (slot == 0) break; | ||||
|  | ||||
|       _writer.writeComma(); | ||||
|     } | ||||
| @@ -36,17 +37,18 @@ class JsonSerializer { | ||||
|     _writer.endArray(); | ||||
|   } | ||||
|  | ||||
|   void visitObject(ObjectConstRef object) { | ||||
|   void visitObject(const CollectionData &object) { | ||||
|     _writer.beginObject(); | ||||
|  | ||||
|     ObjectConstRef::iterator it = object.begin(); | ||||
|     while (it != object.end()) { | ||||
|       _writer.writeString(it->key()); | ||||
|       _writer.writeColon(); | ||||
|       it->value().accept(*this); | ||||
|     VariantSlot *slot = object.head(); | ||||
|  | ||||
|       ++it; | ||||
|       if (it == object.end()) break; | ||||
|     while (slot != 0) { | ||||
|       _writer.writeString(slot->key()); | ||||
|       _writer.writeColon(); | ||||
|       slot->data()->accept(*this); | ||||
|  | ||||
|       slot = slot->next(); | ||||
|       if (slot == 0) break; | ||||
|  | ||||
|       _writer.writeComma(); | ||||
|     } | ||||
|   | ||||
| @@ -6,7 +6,6 @@ | ||||
|  | ||||
| #include "../Polyfills/assert.hpp" | ||||
| #include "../Polyfills/mpl/max.hpp" | ||||
| #include "../Strings/StringInMemoryPool.hpp" | ||||
| #include "../Variant/VariantSlot.hpp" | ||||
| #include "Alignment.hpp" | ||||
| #include "MemoryPool.hpp" | ||||
|   | ||||
| @@ -4,15 +4,12 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "../Strings/StringInMemoryPool.hpp" | ||||
| #include "MemoryPool.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| class StringBuilder { | ||||
|  public: | ||||
|   typedef StringInMemoryPool StringType; | ||||
|  | ||||
|   explicit StringBuilder(MemoryPool* parent) : _parent(parent), _size(0) { | ||||
|     _slot = _parent->allocExpandableString(); | ||||
|   } | ||||
| @@ -36,7 +33,7 @@ class StringBuilder { | ||||
|     _slot.value[_size++] = c; | ||||
|   } | ||||
|  | ||||
|   StringType complete() { | ||||
|   char* complete() { | ||||
|     append('\0'); | ||||
|     if (_slot.value) { | ||||
|       _parent->freezeString(_slot, _size); | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
| #include "../Deserialization/deserialize.hpp" | ||||
| #include "../Memory/MemoryPool.hpp" | ||||
| #include "../Polyfills/type_traits.hpp" | ||||
| #include "../Variant/VariantRef.hpp" | ||||
| #include "../Variant/VariantData.hpp" | ||||
| #include "endianess.hpp" | ||||
| #include "ieee754.hpp" | ||||
|  | ||||
| @@ -17,27 +17,28 @@ template <typename TReader, typename TStringStorage> | ||||
| class MsgPackDeserializer { | ||||
|   typedef typename remove_reference<TStringStorage>::type::StringBuilder | ||||
|       StringBuilder; | ||||
|   typedef typename StringBuilder::StringType StringType; | ||||
|   typedef const char *StringType; | ||||
|  | ||||
|  public: | ||||
|   MsgPackDeserializer(MemoryPool &memoryPool, TReader reader, | ||||
|   MsgPackDeserializer(MemoryPool &pool, TReader reader, | ||||
|                       TStringStorage stringStorage, uint8_t nestingLimit) | ||||
|       : _memoryPool(&memoryPool), | ||||
|       : _pool(&pool), | ||||
|         _reader(reader), | ||||
|         _stringStorage(stringStorage), | ||||
|         _nestingLimit(nestingLimit) {} | ||||
|  | ||||
|   DeserializationError parse(VariantRef variant) { | ||||
|   DeserializationError parse(VariantData &variant) { | ||||
|     uint8_t code; | ||||
|     if (!readByte(code)) return DeserializationError::IncompleteInput; | ||||
|  | ||||
|     if ((code & 0x80) == 0) { | ||||
|       variant.set(code); | ||||
|       variant.setUnsignedInteger(code); | ||||
|       return DeserializationError::Ok; | ||||
|     } | ||||
|  | ||||
|     if ((code & 0xe0) == 0xe0) { | ||||
|       variant.set(static_cast<int8_t>(code)); | ||||
|       // TODO: add setNegativeInteger() | ||||
|       variant.setSignedInteger(static_cast<int8_t>(code)); | ||||
|       return DeserializationError::Ok; | ||||
|     } | ||||
|  | ||||
| @@ -45,9 +46,13 @@ class MsgPackDeserializer { | ||||
|       return readString(variant, code & 0x1f); | ||||
|     } | ||||
|  | ||||
|     if ((code & 0xf0) == 0x90) return readArray(variant, code & 0x0F); | ||||
|     if ((code & 0xf0) == 0x90) { | ||||
|       return readArray(variant.toArray(), code & 0x0F); | ||||
|     } | ||||
|  | ||||
|     if ((code & 0xf0) == 0x80) return readObject(variant, code & 0x0F); | ||||
|     if ((code & 0xf0) == 0x80) { | ||||
|       return readObject(variant.toObject(), code & 0x0F); | ||||
|     } | ||||
|  | ||||
|     switch (code) { | ||||
|       case 0xc0: | ||||
| @@ -55,11 +60,11 @@ class MsgPackDeserializer { | ||||
|         return DeserializationError::Ok; | ||||
|  | ||||
|       case 0xc2: | ||||
|         variant.set(false); | ||||
|         variant.setBoolean(false); | ||||
|         return DeserializationError::Ok; | ||||
|  | ||||
|       case 0xc3: | ||||
|         variant.set(true); | ||||
|         variant.setBoolean(true); | ||||
|         return DeserializationError::Ok; | ||||
|  | ||||
|       case 0xcc: | ||||
| @@ -112,16 +117,16 @@ class MsgPackDeserializer { | ||||
|         return readString<uint32_t>(variant); | ||||
|  | ||||
|       case 0xdc: | ||||
|         return readArray<uint16_t>(variant); | ||||
|         return readArray<uint16_t>(variant.toArray()); | ||||
|  | ||||
|       case 0xdd: | ||||
|         return readArray<uint32_t>(variant); | ||||
|         return readArray<uint32_t>(variant.toArray()); | ||||
|  | ||||
|       case 0xde: | ||||
|         return readObject<uint16_t>(variant); | ||||
|         return readObject<uint16_t>(variant.toObject()); | ||||
|  | ||||
|       case 0xdf: | ||||
|         return readObject<uint32_t>(variant); | ||||
|         return readObject<uint32_t>(variant.toObject()); | ||||
|  | ||||
|       default: | ||||
|         return DeserializationError::NotSupported; | ||||
| @@ -174,48 +179,48 @@ class MsgPackDeserializer { | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   DeserializationError readInteger(VariantRef variant) { | ||||
|   DeserializationError readInteger(VariantData &variant) { | ||||
|     T value; | ||||
|     if (!readInteger(value)) return DeserializationError::IncompleteInput; | ||||
|     variant.set(value); | ||||
|     variant.setInteger(value); | ||||
|     return DeserializationError::Ok; | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   typename enable_if<sizeof(T) == 4, DeserializationError>::type readFloat( | ||||
|       VariantRef variant) { | ||||
|       VariantData &variant) { | ||||
|     T value; | ||||
|     if (!readBytes(value)) return DeserializationError::IncompleteInput; | ||||
|     fixEndianess(value); | ||||
|     variant.set(value); | ||||
|     variant.setFloat(value); | ||||
|     return DeserializationError::Ok; | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   typename enable_if<sizeof(T) == 8, DeserializationError>::type readDouble( | ||||
|       VariantRef variant) { | ||||
|       VariantData &variant) { | ||||
|     T value; | ||||
|     if (!readBytes(value)) return DeserializationError::IncompleteInput; | ||||
|     fixEndianess(value); | ||||
|     variant.set(value); | ||||
|     variant.setFloat(value); | ||||
|     return DeserializationError::Ok; | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   typename enable_if<sizeof(T) == 4, DeserializationError>::type readDouble( | ||||
|       VariantRef variant) { | ||||
|       VariantData &variant) { | ||||
|     uint8_t i[8];  // input is 8 bytes | ||||
|     T value;       // output is 4 bytes | ||||
|     uint8_t *o = reinterpret_cast<uint8_t *>(&value); | ||||
|     if (!readBytes(i, 8)) return DeserializationError::IncompleteInput; | ||||
|     doubleToFloat(i, o); | ||||
|     fixEndianess(value); | ||||
|     variant.set(value); | ||||
|     variant.setFloat(value); | ||||
|     return DeserializationError::Ok; | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   DeserializationError readString(VariantRef variant) { | ||||
|   DeserializationError readString(VariantData &variant) { | ||||
|     T size; | ||||
|     if (!readInteger(size)) return DeserializationError::IncompleteInput; | ||||
|     return readString(variant, size); | ||||
| @@ -228,46 +233,40 @@ class MsgPackDeserializer { | ||||
|     return readString(str, size); | ||||
|   } | ||||
|  | ||||
|   DeserializationError readString(VariantRef variant, size_t n) { | ||||
|   DeserializationError readString(VariantData &variant, size_t n) { | ||||
|     StringType s; | ||||
|     DeserializationError err = readString(s, n); | ||||
|     if (!err) variant.set(s); | ||||
|     if (!err) variant.setOwnedString(s); | ||||
|     return err; | ||||
|   } | ||||
|  | ||||
|   DeserializationError readString(StringType &s, size_t n) { | ||||
|   DeserializationError readString(StringType &result, size_t n) { | ||||
|     StringBuilder builder = _stringStorage.startString(); | ||||
|     for (; n; --n) { | ||||
|       uint8_t c; | ||||
|       if (!readBytes(c)) return DeserializationError::IncompleteInput; | ||||
|       builder.append(static_cast<char>(c)); | ||||
|     } | ||||
|     s = builder.complete(); | ||||
|     if (s.isNull()) return DeserializationError::NoMemory; | ||||
|     result = builder.complete(); | ||||
|     if (!result) return DeserializationError::NoMemory; | ||||
|     return DeserializationError::Ok; | ||||
|   } | ||||
|  | ||||
|   template <typename TSize> | ||||
|   DeserializationError readArray(VariantRef variant) { | ||||
|   DeserializationError readArray(CollectionData &array) { | ||||
|     TSize size; | ||||
|     if (!readInteger(size)) return DeserializationError::IncompleteInput; | ||||
|     return readArray(variant, size); | ||||
|     return readArray(array, size); | ||||
|   } | ||||
|  | ||||
|   DeserializationError readArray(VariantRef variant, size_t n) { | ||||
|     ArrayRef array = variant.to<ArrayRef>(); | ||||
|     if (array.isNull()) return DeserializationError::NoMemory; | ||||
|     return readArray(array, n); | ||||
|   } | ||||
|  | ||||
|   DeserializationError readArray(ArrayRef array, size_t n) { | ||||
|   DeserializationError readArray(CollectionData &array, size_t n) { | ||||
|     if (_nestingLimit == 0) return DeserializationError::TooDeep; | ||||
|     --_nestingLimit; | ||||
|     for (; n; --n) { | ||||
|       VariantRef value = array.add(); | ||||
|       if (value.isInvalid()) return DeserializationError::NoMemory; | ||||
|       VariantData *value = array.add(_pool); | ||||
|       if (!value) return DeserializationError::NoMemory; | ||||
|  | ||||
|       DeserializationError err = parse(value); | ||||
|       DeserializationError err = parse(*value); | ||||
|       if (err) return err; | ||||
|     } | ||||
|     ++_nestingLimit; | ||||
| @@ -275,31 +274,25 @@ class MsgPackDeserializer { | ||||
|   } | ||||
|  | ||||
|   template <typename TSize> | ||||
|   DeserializationError readObject(VariantRef variant) { | ||||
|   DeserializationError readObject(CollectionData &object) { | ||||
|     TSize size; | ||||
|     if (!readInteger(size)) return DeserializationError::IncompleteInput; | ||||
|     return readObject(variant, size); | ||||
|     return readObject(object, size); | ||||
|   } | ||||
|  | ||||
|   DeserializationError readObject(VariantRef variant, size_t n) { | ||||
|     ObjectRef object = variant.to<ObjectRef>(); | ||||
|     if (object.isNull()) return DeserializationError::NoMemory; | ||||
|  | ||||
|     return readObject(object, n); | ||||
|   } | ||||
|  | ||||
|   DeserializationError readObject(ObjectRef object, size_t n) { | ||||
|   DeserializationError readObject(CollectionData &object, size_t n) { | ||||
|     if (_nestingLimit == 0) return DeserializationError::TooDeep; | ||||
|     --_nestingLimit; | ||||
|     for (; n; --n) { | ||||
|       VariantSlot *slot = object.addSlot(_pool); | ||||
|       if (!slot) return DeserializationError::NoMemory; | ||||
|  | ||||
|       StringType key; | ||||
|       DeserializationError err = parseKey(key); | ||||
|       if (err) return err; | ||||
|       slot->setOwnedKey(key); | ||||
|  | ||||
|       VariantRef value = object.set(key); | ||||
|       if (value.isInvalid()) return DeserializationError::NoMemory; | ||||
|  | ||||
|       err = parse(value); | ||||
|       err = parse(*slot->data()); | ||||
|       if (err) return err; | ||||
|     } | ||||
|     ++_nestingLimit; | ||||
| @@ -327,7 +320,7 @@ class MsgPackDeserializer { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   MemoryPool *_memoryPool; | ||||
|   MemoryPool *_pool; | ||||
|   TReader _reader; | ||||
|   TStringStorage _stringStorage; | ||||
|   uint8_t _nestingLimit; | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
| #include "../Polyfills/type_traits.hpp" | ||||
| #include "../Serialization/measure.hpp" | ||||
| #include "../Serialization/serialize.hpp" | ||||
| #include "../Variant/VariantRef.hpp" | ||||
| #include "../Variant/VariantData.hpp" | ||||
| #include "endianess.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
| @@ -35,7 +35,7 @@ class MsgPackSerializer { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void visitArray(ArrayConstRef array) { | ||||
|   void visitArray(const CollectionData& 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 (ArrayConstRef::iterator it = array.begin(); it != array.end(); ++it) { | ||||
|       it->accept(*this); | ||||
|     for (VariantSlot* slot = array.head(); slot; slot = slot->next()) { | ||||
|       slot->data()->accept(*this); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void visitObject(ObjectConstRef object) { | ||||
|   void visitObject(const CollectionData& object) { | ||||
|     size_t n = object.size(); | ||||
|     if (n < 0x10) { | ||||
|       writeByte(uint8_t(0x80 + n)); | ||||
| @@ -62,10 +62,9 @@ class MsgPackSerializer { | ||||
|       writeByte(0xDF); | ||||
|       writeInteger(uint32_t(n)); | ||||
|     } | ||||
|     for (ObjectConstRef::iterator it = object.begin(); it != object.end(); | ||||
|          ++it) { | ||||
|       visitString(it->key()); | ||||
|       it->value().accept(*this); | ||||
|     for (VariantSlot* slot = object.head(); slot; slot = slot->next()) { | ||||
|       visitString(slot->key()); | ||||
|       slot->data()->accept(*this); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -15,7 +15,7 @@ class Key { | ||||
|   } | ||||
|  | ||||
|   const char* c_str() const { | ||||
|     return _slot ? slotGetKey(_slot) : 0; | ||||
|     return _slot ? _slot->key() : 0; | ||||
|   } | ||||
|  | ||||
|   bool isNull() const { | ||||
|   | ||||
| @@ -4,116 +4,52 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "../Memory/MemoryPool.hpp" | ||||
| #include "../Variant/SlotFunctions.hpp" | ||||
| #include "../Variant/VariantData.hpp" | ||||
| #include "../Collection/CollectionData.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| template <typename Visitor> | ||||
| void objectAccept(const CollectionData *obj, Visitor &visitor) { | ||||
|   if (obj) | ||||
|     visitor.visitObject(*obj); | ||||
|   else | ||||
|     visitor.visitNull(); | ||||
| } | ||||
|  | ||||
| template <typename TKey> | ||||
| inline VariantSlot* objectFindSlot(const ObjectData* obj, TKey key) { | ||||
| inline bool objectContainsKey(const CollectionData *obj, TKey key) { | ||||
|   return obj && obj->containsKey(key); | ||||
| } | ||||
|  | ||||
| inline bool objectEquals(const CollectionData *lhs, const CollectionData *rhs) { | ||||
|   if (lhs == rhs) return true; | ||||
|   if (!lhs || !rhs) return false; | ||||
|   return lhs->equalsObject(*rhs); | ||||
| } | ||||
|  | ||||
| template <typename TKey> | ||||
| inline VariantData *objectGet(const CollectionData *obj, TKey key) { | ||||
|   if (!obj) return 0; | ||||
|   VariantSlot* slot = obj->head; | ||||
|   while (slot) { | ||||
|     if (key.equals(slotGetKey(slot))) break; | ||||
|     slot = slot->getNext(); | ||||
|   } | ||||
|   return slot; | ||||
|   return obj->get(key); | ||||
| } | ||||
|  | ||||
| template <typename TKey> | ||||
| inline bool objectContainsKey(const ObjectData* obj, const TKey& key) { | ||||
|   return objectFindSlot(obj, key) != 0; | ||||
| void objectRemove(CollectionData *obj, TKey key) { | ||||
|   if (!obj) return; | ||||
|   obj->remove(key); | ||||
| } | ||||
|  | ||||
| template <typename TKey> | ||||
| inline VariantData* objectAdd(ObjectData* obj, TKey key, MemoryPool* pool) { | ||||
|   VariantSlot* slot = pool->allocVariant(); | ||||
|   if (!slot) return 0; | ||||
|  | ||||
|   slot->init(); | ||||
|  | ||||
|   if (obj->tail) { | ||||
|     slot->attachTo(obj->tail); | ||||
|     obj->tail = slot; | ||||
|   } else { | ||||
|     obj->head = slot; | ||||
|     obj->tail = slot; | ||||
|   } | ||||
|  | ||||
|   if (!slotSetKey(slot, key, pool)) return 0; | ||||
|   return slot->getData(); | ||||
| } | ||||
|  | ||||
| template <typename TKey> | ||||
| inline VariantData* objectSet(ObjectData* obj, TKey key, MemoryPool* pool) { | ||||
| inline VariantData *objectSet(CollectionData *obj, TKey key, MemoryPool *pool) { | ||||
|   if (!obj) return 0; | ||||
|  | ||||
|   // ignore null key | ||||
|   if (key.isNull()) return 0; | ||||
|  | ||||
|   // search a matching key | ||||
|   VariantSlot* slot = objectFindSlot(obj, key); | ||||
|   if (slot) return slot->getData(); | ||||
|   VariantData *var = obj->get(key); | ||||
|   if (var) return var; | ||||
|  | ||||
|   return objectAdd(obj, key, pool); | ||||
| } | ||||
|  | ||||
| template <typename TKey> | ||||
| inline VariantData* objectGet(const ObjectData* obj, TKey key) { | ||||
|   VariantSlot* slot = objectFindSlot(obj, key); | ||||
|   return slot ? slot->getData() : 0; | ||||
| } | ||||
|  | ||||
| inline void objectClear(ObjectData* obj) { | ||||
|   if (!obj) return; | ||||
|   obj->head = 0; | ||||
|   obj->tail = 0; | ||||
| } | ||||
|  | ||||
| inline void objectRemove(ObjectData* obj, VariantSlot* slot) { | ||||
|   if (!obj) return; | ||||
|   if (!slot) return; | ||||
|   VariantSlot* prev = slot->getPrev(obj->head); | ||||
|   VariantSlot* next = slot->getNext(); | ||||
|   if (prev) | ||||
|     prev->setNext(next); | ||||
|   else | ||||
|     obj->head = next; | ||||
|   if (!next) obj->tail = prev; | ||||
| } | ||||
|  | ||||
| inline size_t objectSize(const ObjectData* obj) { | ||||
|   if (!obj) return 0; | ||||
|   return slotSize(obj->head); | ||||
| } | ||||
|  | ||||
| // bool variantCopy(VariantData*, const VariantData*, MemoryPool*); | ||||
|  | ||||
| inline bool objectCopy(ObjectData* dst, const ObjectData* src, | ||||
|                        MemoryPool* pool) { | ||||
|   if (!dst || !src) return false; | ||||
|   objectClear(dst); | ||||
|   for (VariantSlot* s = src->head; s; s = s->getNext()) { | ||||
|     VariantData* var; | ||||
|     if (s->ownsKey()) | ||||
|       var = objectAdd(dst, ZeroTerminatedRamString(s->key()), pool); | ||||
|     else | ||||
|       var = objectAdd(dst, ZeroTerminatedRamStringConst(s->key()), pool); | ||||
|     if (!variantCopy(var, s->getData(), pool)) return false; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline bool objectEquals(const ObjectData* o1, const ObjectData* o2) { | ||||
|   if (o1 == o2) return true; | ||||
|   if (!o1 || !o2) return false; | ||||
|  | ||||
|   for (VariantSlot* s = o1->head; s; s = s->getNext()) { | ||||
|     VariantData* v1 = s->getData(); | ||||
|     VariantData* v2 = objectGet(o2, makeString(slotGetKey(s))); | ||||
|     if (!variantEquals(v1, v2)) return false; | ||||
|   } | ||||
|   return true; | ||||
|   return obj->add(key, pool); | ||||
| } | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
| @@ -11,8 +11,7 @@ namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| class PairPtr { | ||||
|  public: | ||||
|   PairPtr(MemoryPool *memoryPool, VariantSlot *slot) | ||||
|       : _pair(memoryPool, slot) {} | ||||
|   PairPtr(MemoryPool *pool, VariantSlot *slot) : _pair(pool, slot) {} | ||||
|  | ||||
|   const Pair *operator->() const { | ||||
|     return &_pair; | ||||
| @@ -30,14 +29,14 @@ class ObjectIterator { | ||||
|  public: | ||||
|   ObjectIterator() : _slot(0) {} | ||||
|  | ||||
|   explicit ObjectIterator(MemoryPool *memoryPool, VariantSlot *slot) | ||||
|       : _memoryPool(memoryPool), _slot(slot) {} | ||||
|   explicit ObjectIterator(MemoryPool *pool, VariantSlot *slot) | ||||
|       : _pool(pool), _slot(slot) {} | ||||
|  | ||||
|   Pair operator*() const { | ||||
|     return Pair(_memoryPool, _slot); | ||||
|     return Pair(_pool, _slot); | ||||
|   } | ||||
|   PairPtr operator->() { | ||||
|     return PairPtr(_memoryPool, _slot); | ||||
|     return PairPtr(_pool, _slot); | ||||
|   } | ||||
|  | ||||
|   bool operator==(const ObjectIterator &other) const { | ||||
| @@ -49,12 +48,12 @@ class ObjectIterator { | ||||
|   } | ||||
|  | ||||
|   ObjectIterator &operator++() { | ||||
|     _slot = _slot->getNext(); | ||||
|     _slot = _slot->next(); | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   ObjectIterator &operator+=(size_t distance) { | ||||
|     _slot = _slot->getNext(distance); | ||||
|     _slot = _slot->next(distance); | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
| @@ -63,7 +62,7 @@ class ObjectIterator { | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   MemoryPool *_memoryPool; | ||||
|   MemoryPool *_pool; | ||||
|   VariantSlot *_slot; | ||||
| }; | ||||
|  | ||||
| @@ -105,12 +104,12 @@ class ObjectConstIterator { | ||||
|   } | ||||
|  | ||||
|   ObjectConstIterator &operator++() { | ||||
|     _slot = _slot->getNext(); | ||||
|     _slot = _slot->next(); | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   ObjectConstIterator &operator+=(size_t distance) { | ||||
|     _slot = _slot->getNext(distance); | ||||
|     _slot = _slot->next(distance); | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -17,6 +17,11 @@ namespace ARDUINOJSON_NAMESPACE { | ||||
| template <typename TData> | ||||
| class ObjectRefBase { | ||||
|  public: | ||||
|   template <typename Visitor> | ||||
|   FORCE_INLINE void accept(Visitor& visitor) const { | ||||
|     objectAccept(_data, visitor); | ||||
|   } | ||||
|  | ||||
|   // Tells weither the specified key is present and associated with a value. | ||||
|   // | ||||
|   // bool containsKey(TKey); | ||||
| @@ -38,7 +43,7 @@ class ObjectRefBase { | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE size_t size() const { | ||||
|     return objectSize(_data); | ||||
|     return _data ? _data->size() : 0; | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
| @@ -46,28 +51,20 @@ class ObjectRefBase { | ||||
|   TData* _data; | ||||
| }; | ||||
|  | ||||
| class ObjectConstRef : public ObjectRefBase<const ObjectData>, | ||||
| class ObjectConstRef : public ObjectRefBase<const CollectionData>, | ||||
|                        public Visitable { | ||||
|   friend class ObjectRef; | ||||
|   typedef ObjectRefBase<const ObjectData> base_type; | ||||
|   typedef ObjectRefBase<const CollectionData> base_type; | ||||
|  | ||||
|  public: | ||||
|   typedef ObjectConstIterator iterator; | ||||
|  | ||||
|   ObjectConstRef() : base_type(0) {} | ||||
|   ObjectConstRef(const ObjectData* data) : base_type(data) {} | ||||
|  | ||||
|   template <typename Visitor> | ||||
|   FORCE_INLINE void accept(Visitor& visitor) const { | ||||
|     if (_data) | ||||
|       visitor.visitObject(*this); | ||||
|     else | ||||
|       visitor.visitNull(); | ||||
|   } | ||||
|   ObjectConstRef(const CollectionData* data) : base_type(data) {} | ||||
|  | ||||
|   FORCE_INLINE iterator begin() const { | ||||
|     if (!_data) return iterator(); | ||||
|     return iterator(_data->head); | ||||
|     return iterator(_data->head()); | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE iterator end() const { | ||||
| @@ -122,18 +119,18 @@ class ObjectConstRef : public ObjectRefBase<const ObjectData>, | ||||
|   } | ||||
| }; | ||||
|  | ||||
| class ObjectRef : public ObjectRefBase<ObjectData>, public Visitable { | ||||
|   typedef ObjectRefBase<ObjectData> base_type; | ||||
| class ObjectRef : public ObjectRefBase<CollectionData>, public Visitable { | ||||
|   typedef ObjectRefBase<CollectionData> base_type; | ||||
|  | ||||
|  public: | ||||
|   typedef ObjectIterator iterator; | ||||
|  | ||||
|   FORCE_INLINE ObjectRef() : base_type(0), _memoryPool(0) {} | ||||
|   FORCE_INLINE ObjectRef(MemoryPool* buf, ObjectData* data) | ||||
|       : base_type(data), _memoryPool(buf) {} | ||||
|   FORCE_INLINE ObjectRef() : base_type(0), _pool(0) {} | ||||
|   FORCE_INLINE ObjectRef(MemoryPool* buf, CollectionData* data) | ||||
|       : base_type(data), _pool(buf) {} | ||||
|  | ||||
|   operator VariantRef() const { | ||||
|     return VariantRef(_memoryPool, getVariantData(_data)); | ||||
|     return VariantRef(_pool, reinterpret_cast<VariantData*>(_data)); | ||||
|   } | ||||
|  | ||||
|   operator ObjectConstRef() const { | ||||
| @@ -142,7 +139,7 @@ class ObjectRef : public ObjectRefBase<ObjectData>, public Visitable { | ||||
|  | ||||
|   FORCE_INLINE iterator begin() const { | ||||
|     if (!_data) return iterator(); | ||||
|     return iterator(_memoryPool, _data->head); | ||||
|     return iterator(_pool, _data->head()); | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE iterator end() const { | ||||
| @@ -150,11 +147,13 @@ class ObjectRef : public ObjectRefBase<ObjectData>, public Visitable { | ||||
|   } | ||||
|  | ||||
|   void clear() const { | ||||
|     objectClear(_data); | ||||
|     if (!_data) return; | ||||
|     _data->clear(); | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE bool copyFrom(ObjectConstRef src) { | ||||
|     return objectCopy(_data, src._data, _memoryPool); | ||||
|     if (!_data || !src._data) return false; | ||||
|     return _data->copyFrom(*src._data, _pool); | ||||
|   } | ||||
|  | ||||
|   // Creates and adds a ArrayRef. | ||||
| @@ -225,7 +224,8 @@ class ObjectRef : public ObjectRefBase<ObjectData>, public Visitable { | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE void remove(iterator it) const { | ||||
|     objectRemove(_data, it.internal()); | ||||
|     if (!_data) return; | ||||
|     _data->remove(it.internal()); | ||||
|   } | ||||
|  | ||||
|   // Removes the specified key and the associated value. | ||||
| @@ -234,14 +234,14 @@ class ObjectRef : public ObjectRefBase<ObjectData>, public Visitable { | ||||
|   // TKey = const std::string&, const String& | ||||
|   template <typename TKey> | ||||
|   FORCE_INLINE void remove(const TKey& key) const { | ||||
|     remove_impl(makeString(key)); | ||||
|     objectRemove(_data, makeString(key)); | ||||
|   } | ||||
|   // | ||||
|   // void remove(TKey); | ||||
|   // TKey = char*, const char*, char[], const char[], const FlashStringHelper* | ||||
|   template <typename TKey> | ||||
|   FORCE_INLINE void remove(TKey* key) const { | ||||
|     remove_impl(makeString(key)); | ||||
|     objectRemove(_data, makeString(key)); | ||||
|   } | ||||
|  | ||||
|   template <typename TKey> | ||||
| @@ -254,35 +254,17 @@ class ObjectRef : public ObjectRefBase<ObjectData>, public Visitable { | ||||
|     return set_impl(makeString(key)); | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE VariantRef set(StringInMemoryPool key) const { | ||||
|     return set_impl(key); | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE VariantRef set(ZeroTerminatedRamStringConst key) const { | ||||
|     return set_impl(key); | ||||
|   } | ||||
|  | ||||
|   template <typename Visitor> | ||||
|   FORCE_INLINE void accept(Visitor& visitor) const { | ||||
|     ObjectConstRef(_data).accept(visitor); | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   template <typename TStringRef> | ||||
|   FORCE_INLINE VariantRef get_impl(TStringRef key) const { | ||||
|     return VariantRef(_memoryPool, objectGet(_data, key)); | ||||
|   template <typename TKey> | ||||
|   FORCE_INLINE VariantRef get_impl(TKey key) const { | ||||
|     return VariantRef(_pool, objectGet(_data, key)); | ||||
|   } | ||||
|  | ||||
|   template <typename TKey> | ||||
|   FORCE_INLINE VariantRef set_impl(TKey key) const { | ||||
|     return VariantRef(_memoryPool, objectSet(_data, key, _memoryPool)); | ||||
|     return VariantRef(_pool, objectSet(_data, key, _pool)); | ||||
|   } | ||||
|  | ||||
|   template <typename TStringRef> | ||||
|   FORCE_INLINE void remove_impl(TStringRef key) const { | ||||
|     objectRemove(_data, objectFindSlot(_data, key)); | ||||
|   } | ||||
|  | ||||
|   MemoryPool* _memoryPool; | ||||
|   MemoryPool* _pool; | ||||
| }; | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
| @@ -8,12 +8,12 @@ | ||||
| #include "Key.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
| // A key value pair for ObjectData. | ||||
| // A key value pair for CollectionData. | ||||
| class Pair { | ||||
|  public: | ||||
|   Pair(MemoryPool* memoryPool, VariantSlot* slot) : _key(slot) { | ||||
|   Pair(MemoryPool* pool, VariantSlot* slot) : _key(slot) { | ||||
|     if (slot) { | ||||
|       _value = VariantRef(memoryPool, slot->getData()); | ||||
|       _value = VariantRef(pool, slot->data()); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -34,7 +34,7 @@ class PairConst { | ||||
|  public: | ||||
|   PairConst(const VariantSlot* slot) : _key(slot) { | ||||
|     if (slot) { | ||||
|       _value = VariantConstRef(slot->getData()); | ||||
|       _value = VariantConstRef(slot->data()); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,8 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <stddef.h>  // size_t | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| template <typename T> | ||||
|   | ||||
| @@ -13,13 +13,13 @@ class StringCopier { | ||||
|  public: | ||||
|   typedef ARDUINOJSON_NAMESPACE::StringBuilder StringBuilder; | ||||
|  | ||||
|   StringCopier(MemoryPool* memoryPool) : _memoryPool(memoryPool) {} | ||||
|   StringCopier(MemoryPool* pool) : _pool(pool) {} | ||||
|  | ||||
|   StringBuilder startString() { | ||||
|     return StringBuilder(_memoryPool); | ||||
|     return StringBuilder(_pool); | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   MemoryPool* _memoryPool; | ||||
|   MemoryPool* _pool; | ||||
| }; | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
| @@ -10,17 +10,15 @@ class StringMover { | ||||
|  public: | ||||
|   class StringBuilder { | ||||
|    public: | ||||
|     typedef ZeroTerminatedRamStringConst StringType; | ||||
|  | ||||
|     StringBuilder(char** ptr) : _writePtr(ptr), _startPtr(*ptr) {} | ||||
|  | ||||
|     void append(char c) { | ||||
|       *(*_writePtr)++ = char(c); | ||||
|     } | ||||
|  | ||||
|     StringType complete() const { | ||||
|     char* complete() const { | ||||
|       *(*_writePtr)++ = 0; | ||||
|       return reinterpret_cast<const char*>(_startPtr); | ||||
|       return _startPtr; | ||||
|     } | ||||
|  | ||||
|    private: | ||||
|   | ||||
| @@ -12,10 +12,10 @@ class ArduinoString { | ||||
|  public: | ||||
|   ArduinoString(const ::String& str) : _str(&str) {} | ||||
|  | ||||
|   char* save(MemoryPool* memoryPool) const { | ||||
|   char* save(MemoryPool* pool) const { | ||||
|     if (isNull()) return NULL; | ||||
|     size_t n = _str->length() + 1; | ||||
|     char* dup = memoryPool->allocFrozenString(n); | ||||
|     char* dup = pool->allocFrozenString(n); | ||||
|     if (dup) memcpy(dup, _str->c_str(), n); | ||||
|     return dup; | ||||
|   } | ||||
|   | ||||
| @@ -21,9 +21,9 @@ class FixedSizeFlashString { | ||||
|     return !_str; | ||||
|   } | ||||
|  | ||||
|   char* save(MemoryPool* memoryPool) const { | ||||
|   char* save(MemoryPool* pool) const { | ||||
|     if (!_str) return NULL; | ||||
|     char* dup = memoryPool->allocFrozenString(_size); | ||||
|     char* dup = pool->allocFrozenString(_size); | ||||
|     if (!dup) memcpy_P(dup, (const char*)_str, _size); | ||||
|     return dup; | ||||
|   } | ||||
|   | ||||
| @@ -22,10 +22,9 @@ class FixedSizeRamString { | ||||
|     return !_str; | ||||
|   } | ||||
|  | ||||
|   template <typename TMemoryPool> | ||||
|   char* save(TMemoryPool* memoryPool) const { | ||||
|   char* save(MemoryPool* pool) const { | ||||
|     if (!_str) return NULL; | ||||
|     char* dup = memoryPool->allocFrozenString(_size); | ||||
|     char* dup = pool->allocFrozenString(_size); | ||||
|     if (dup) memcpy(dup, _str, _size); | ||||
|     return dup; | ||||
|   } | ||||
|   | ||||
| @@ -12,9 +12,9 @@ class StlString { | ||||
|  public: | ||||
|   StlString(const std::string& str) : _str(&str) {} | ||||
|  | ||||
|   char* save(MemoryPool* memoryPool) const { | ||||
|   char* save(MemoryPool* pool) const { | ||||
|     size_t n = _str->length() + 1; | ||||
|     char* dup = memoryPool->allocFrozenString(n); | ||||
|     char* dup = pool->allocFrozenString(n); | ||||
|     if (dup) memcpy(dup, _str->c_str(), n); | ||||
|     return dup; | ||||
|   } | ||||
|   | ||||
| @@ -1,39 +0,0 @@ | ||||
| // ArduinoJson - arduinojson.org | ||||
| // Copyright Benoit Blanchon 2014-2018 | ||||
| // MIT License | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string.h> | ||||
| #include "../Memory/MemoryPool.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| class StringInMemoryPool { | ||||
|  public: | ||||
|   StringInMemoryPool(char* s = 0) : _value(s) {} | ||||
|  | ||||
|   bool equals(const char* expected) const { | ||||
|     if (!_value) return expected == 0; | ||||
|     const char* actual = _value; | ||||
|     if (actual == expected) return true; | ||||
|     return strcmp(actual, expected) == 0; | ||||
|   } | ||||
|  | ||||
|   char* save(void*) { | ||||
|     return _value; | ||||
|   } | ||||
|  | ||||
|   bool isNull() const { | ||||
|     return !_value; | ||||
|   } | ||||
|  | ||||
|   const char* c_str() const { | ||||
|     return _value; | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   char* _value; | ||||
| }; | ||||
|  | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
| @@ -4,6 +4,7 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "../Memory/MemoryPool.hpp" | ||||
| #include "../Polyfills/type_traits.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
| @@ -18,7 +19,6 @@ struct IsString<T&> : IsString<T> {}; | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|  | ||||
| #include "FixedSizeRamString.hpp" | ||||
| #include "StringInMemoryPool.hpp" | ||||
| #include "ZeroTerminatedRamString.hpp" | ||||
| #include "ZeroTerminatedRamStringConst.hpp" | ||||
|  | ||||
|   | ||||
| @@ -20,10 +20,10 @@ class ZeroTerminatedFlashString { | ||||
|     return !_str; | ||||
|   } | ||||
|  | ||||
|   char* save(MemoryPool* memoryPool) const { | ||||
|   char* save(MemoryPool* pool) const { | ||||
|     if (!_str) return NULL; | ||||
|     size_t n = size() + 1;  // copy the terminator | ||||
|     char* dup = memoryPool->allocFrozenString(n); | ||||
|     char* dup = pool->allocFrozenString(n); | ||||
|     if (dup) memcpy_P(dup, reinterpret_cast<const char*>(_str), n); | ||||
|     return dup; | ||||
|   } | ||||
|   | ||||
| @@ -13,11 +13,10 @@ class ZeroTerminatedRamString : public ZeroTerminatedRamStringConst { | ||||
|   ZeroTerminatedRamString(const char* str) | ||||
|       : ZeroTerminatedRamStringConst(str) {} | ||||
|  | ||||
|   template <typename TMemoryPool> | ||||
|   char* save(TMemoryPool* memoryPool) const { | ||||
|   char* save(MemoryPool* pool) const { | ||||
|     if (!_str) return NULL; | ||||
|     size_t n = size() + 1; | ||||
|     char* dup = memoryPool->allocFrozenString(n); | ||||
|     char* dup = pool->allocFrozenString(n); | ||||
|     if (dup) memcpy(dup, _str, n); | ||||
|     return dup; | ||||
|   } | ||||
|   | ||||
| @@ -13,32 +13,25 @@ namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| template <typename TKey> | ||||
| inline bool slotSetKey(VariantSlot* var, TKey key, MemoryPool* pool) { | ||||
|   if (!var) return false; | ||||
|   char* dup = key.save(pool); | ||||
|   if (!dup) return false; | ||||
|   var->setKey(dup, true); | ||||
|   var->setOwnedKey(dup); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline bool slotSetKey(VariantSlot* var, ZeroTerminatedRamStringConst key, | ||||
|                        MemoryPool*) { | ||||
|   var->setKey(key.c_str(), false); | ||||
|   if (!var) return false; | ||||
|   var->setLinkedKey(key.c_str()); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline bool slotSetKey(VariantSlot* var, StringInMemoryPool key, MemoryPool*) { | ||||
|   var->setKey(key.c_str(), true); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline const char* slotGetKey(const VariantSlot* var) { | ||||
|   return var->key(); | ||||
| } | ||||
|  | ||||
| inline size_t slotSize(const VariantSlot* var) { | ||||
|   size_t n = 0; | ||||
|   while (var) { | ||||
|     n++; | ||||
|     var = var->getNext(); | ||||
|     var = var->next(); | ||||
|   } | ||||
|   return n; | ||||
| } | ||||
|   | ||||
| @@ -5,7 +5,6 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "../Serialization/DynamicStringWriter.hpp" | ||||
| #include "VariantFunctions.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| @@ -55,19 +54,19 @@ struct VariantConstAs<ArrayRef> { | ||||
| template <typename T> | ||||
| inline typename enable_if<is_integral<T>::value, T>::type variantAs( | ||||
|     const VariantData* _data) { | ||||
|   return variantAsIntegral<T>(_data); | ||||
|   return _data != 0 ? _data->asIntegral<T>() : T(0); | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| inline typename enable_if<is_same<T, bool>::value, T>::type variantAs( | ||||
|     const VariantData* _data) { | ||||
|   return variantAsBoolean(_data); | ||||
|   return _data != 0 ? _data->asBoolean() : false; | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| inline typename enable_if<is_floating_point<T>::value, T>::type variantAs( | ||||
|     const VariantData* _data) { | ||||
|   return variantAsFloat<T>(_data); | ||||
|   return _data != 0 ? _data->asFloat<T>() : T(0); | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| @@ -75,7 +74,7 @@ inline typename enable_if<is_same<T, const char*>::value || | ||||
|                               is_same<T, char*>::value, | ||||
|                           const char*>::type | ||||
| variantAs(const VariantData* _data) { | ||||
|   return variantAsString(_data); | ||||
|   return _data != 0 ? _data->asString() : 0; | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
|   | ||||
| @@ -5,7 +5,6 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "../Serialization/DynamicStringWriter.hpp" | ||||
| #include "VariantFunctions.hpp" | ||||
| #include "VariantRef.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
| @@ -19,7 +18,7 @@ variantAs(const VariantData* _data) { | ||||
| template <typename T> | ||||
| inline typename enable_if<IsWriteableString<T>::value, T>::type variantAs( | ||||
|     const VariantData* _data) { | ||||
|   const char* cstr = variantAsString(_data); | ||||
|   const char* cstr = _data != 0 ? _data->asString() : 0; | ||||
|   if (cstr) return T(cstr); | ||||
|   T s; | ||||
|   serializeJson(VariantConstRef(_data), s); | ||||
|   | ||||
							
								
								
									
										46
									
								
								src/ArduinoJson/Variant/VariantContent.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/ArduinoJson/Variant/VariantContent.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| // ArduinoJson - arduinojson.org | ||||
| // Copyright Benoit Blanchon 2014-2018 | ||||
| // MIT License | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <stddef.h>  // size_t | ||||
|  | ||||
| #include "../Collection/CollectionData.hpp" | ||||
| #include "../Numbers/Float.hpp" | ||||
| #include "../Numbers/Integer.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| enum { | ||||
|   VALUE_IS_NULL = 0, | ||||
|   VALUE_IS_LINKED_RAW, | ||||
|   VALUE_IS_OWNED_RAW, | ||||
|   VALUE_IS_LINKED_STRING, | ||||
|   VALUE_IS_OWNED_STRING, | ||||
|   VALUE_IS_BOOLEAN, | ||||
|   VALUE_IS_POSITIVE_INTEGER, | ||||
|   VALUE_IS_NEGATIVE_INTEGER, | ||||
|   VALUE_IS_ARRAY, | ||||
|   VALUE_IS_OBJECT, | ||||
|   VALUE_IS_FLOAT, | ||||
|   VALUE_MASK = 0x7F, | ||||
|   KEY_IS_OWNED = 0x80 | ||||
| }; | ||||
|  | ||||
| struct RawData { | ||||
|   const char *data; | ||||
|   size_t size; | ||||
| }; | ||||
|  | ||||
| union VariantContent { | ||||
|   Float asFloat; | ||||
|   UInt asInteger; | ||||
|   CollectionData asCollection; | ||||
|   const char *asString; | ||||
|   struct { | ||||
|     const char *data; | ||||
|     size_t size; | ||||
|   } asRaw; | ||||
| }; | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
| @@ -4,78 +4,285 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <stddef.h>  // ptrdiff_t, size_t | ||||
|  | ||||
| #include "../Numbers/Float.hpp" | ||||
| #include "../Numbers/Integer.hpp" | ||||
| #include "../Misc/SerializedValue.hpp" | ||||
| #include "VariantContent.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| class VariantSlot; | ||||
| class VariantData { | ||||
|   VariantContent _content;  // must be first to allow cast from array to variant | ||||
|   uint8_t _flags; | ||||
|  | ||||
| enum VariantType { | ||||
|   JSON_NULL, | ||||
|   JSON_LINKED_RAW, | ||||
|   JSON_OWNED_RAW, | ||||
|   JSON_LINKED_STRING, | ||||
|   JSON_OWNED_STRING, | ||||
|   JSON_BOOLEAN, | ||||
|   JSON_POSITIVE_INTEGER, | ||||
|   JSON_NEGATIVE_INTEGER, | ||||
|   JSON_ARRAY, | ||||
|   JSON_OBJECT, | ||||
|   JSON_FLOAT | ||||
|  public: | ||||
|   // Must be a POD! | ||||
|   // - no constructor | ||||
|   // - no destructor | ||||
|   // - no virtual | ||||
|   // - no inheritance | ||||
|  | ||||
|   template <typename Visitor> | ||||
|   void accept(Visitor &visitor) const { | ||||
|     switch (type()) { | ||||
|       case VALUE_IS_FLOAT: | ||||
|         return visitor.visitFloat(_content.asFloat); | ||||
|  | ||||
|       case VALUE_IS_ARRAY: | ||||
|         return visitor.visitArray(_content.asCollection); | ||||
|  | ||||
|       case VALUE_IS_OBJECT: | ||||
|         return visitor.visitObject(_content.asCollection); | ||||
|  | ||||
|       case VALUE_IS_LINKED_STRING: | ||||
|       case VALUE_IS_OWNED_STRING: | ||||
|         return visitor.visitString(_content.asString); | ||||
|  | ||||
|       case VALUE_IS_OWNED_RAW: | ||||
|       case VALUE_IS_LINKED_RAW: | ||||
|         return visitor.visitRawJson(_content.asRaw.data, _content.asRaw.size); | ||||
|  | ||||
|       case VALUE_IS_NEGATIVE_INTEGER: | ||||
|         return visitor.visitNegativeInteger(_content.asInteger); | ||||
|  | ||||
|       case VALUE_IS_POSITIVE_INTEGER: | ||||
|         return visitor.visitPositiveInteger(_content.asInteger); | ||||
|  | ||||
|       case VALUE_IS_BOOLEAN: | ||||
|         return visitor.visitBoolean(_content.asInteger != 0); | ||||
|  | ||||
|       default: | ||||
|         return visitor.visitNull(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   T asIntegral() const; | ||||
|  | ||||
|   template <typename T> | ||||
|   T asFloat() const; | ||||
|  | ||||
|   const char *asString() const; | ||||
|  | ||||
|   bool asBoolean() const { | ||||
|     return asIntegral<int>() != 0; | ||||
|   } | ||||
|  | ||||
|   CollectionData *asArray() { | ||||
|     if (type() == VALUE_IS_ARRAY) | ||||
|       return &_content.asCollection; | ||||
|     else | ||||
|       return 0; | ||||
|   } | ||||
|  | ||||
|   const CollectionData *asArray() const { | ||||
|     return const_cast<VariantData *>(this)->asArray(); | ||||
|   } | ||||
|  | ||||
|   CollectionData *asObject() { | ||||
|     if (type() == VALUE_IS_OBJECT) | ||||
|       return &_content.asCollection; | ||||
|     else | ||||
|       return 0; | ||||
|   } | ||||
|  | ||||
|   const CollectionData *asObject() const { | ||||
|     return const_cast<VariantData *>(this)->asObject(); | ||||
|   } | ||||
|  | ||||
|   bool copyFrom(const VariantData &src, MemoryPool *pool) { | ||||
|     switch (src.type()) { | ||||
|       case VALUE_IS_ARRAY: | ||||
|         return toArray().copyFrom(src._content.asCollection, pool); | ||||
|       case VALUE_IS_OBJECT: | ||||
|         return toObject().copyFrom(src._content.asCollection, pool); | ||||
|       case VALUE_IS_OWNED_STRING: | ||||
|         return setOwnedString(ZeroTerminatedRamString(src._content.asString), | ||||
|                               pool); | ||||
|       case VALUE_IS_OWNED_RAW: | ||||
|         return setOwnedRaw( | ||||
|             serialized(src._content.asRaw.data, src._content.asRaw.size), pool); | ||||
|       default: | ||||
|         setType(src.type()); | ||||
|         _content = src._content; | ||||
|         return true; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   bool equals(const VariantData &other) const { | ||||
|     if (type() != other.type()) return false; | ||||
|  | ||||
|     switch (type()) { | ||||
|       case VALUE_IS_LINKED_STRING: | ||||
|       case VALUE_IS_OWNED_STRING: | ||||
|         return !strcmp(_content.asString, other._content.asString); | ||||
|  | ||||
|       case VALUE_IS_LINKED_RAW: | ||||
|       case VALUE_IS_OWNED_RAW: | ||||
|         return _content.asRaw.size == other._content.asRaw.size && | ||||
|                !memcmp(_content.asRaw.data, other._content.asRaw.data, | ||||
|                        _content.asRaw.size); | ||||
|  | ||||
|       case VALUE_IS_BOOLEAN: | ||||
|       case VALUE_IS_POSITIVE_INTEGER: | ||||
|       case VALUE_IS_NEGATIVE_INTEGER: | ||||
|         return _content.asInteger == other._content.asInteger; | ||||
|  | ||||
|       case VALUE_IS_ARRAY: | ||||
|         return _content.asCollection.equalsArray(other._content.asCollection); | ||||
|  | ||||
|       case VALUE_IS_OBJECT: | ||||
|         return _content.asCollection.equalsObject(other._content.asCollection); | ||||
|  | ||||
|       case VALUE_IS_FLOAT: | ||||
|         return _content.asFloat == other._content.asFloat; | ||||
|  | ||||
|       case VALUE_IS_NULL: | ||||
|       default: | ||||
|         return true; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   bool isArray() const { | ||||
|     return type() == VALUE_IS_ARRAY; | ||||
|   } | ||||
|  | ||||
|   bool isBoolean() const { | ||||
|     return type() == VALUE_IS_BOOLEAN; | ||||
|   } | ||||
|  | ||||
|   bool isInteger() const { | ||||
|     return type() == VALUE_IS_POSITIVE_INTEGER || | ||||
|            type() == VALUE_IS_NEGATIVE_INTEGER; | ||||
|   } | ||||
|  | ||||
|   bool isFloat() const { | ||||
|     return type() == VALUE_IS_FLOAT || type() == VALUE_IS_POSITIVE_INTEGER || | ||||
|            type() == VALUE_IS_NEGATIVE_INTEGER; | ||||
|   } | ||||
|  | ||||
|   bool isString() const { | ||||
|     return (type() == VALUE_IS_LINKED_STRING || | ||||
|             type() == VALUE_IS_OWNED_STRING); | ||||
|   } | ||||
|  | ||||
|   bool isObject() const { | ||||
|     return type() == VALUE_IS_OBJECT; | ||||
|   } | ||||
|  | ||||
|   bool isNull() const { | ||||
|     return type() == VALUE_IS_NULL; | ||||
|   } | ||||
|  | ||||
|   void setBoolean(bool value) { | ||||
|     setType(VALUE_IS_BOOLEAN); | ||||
|     _content.asInteger = static_cast<UInt>(value); | ||||
|   } | ||||
|  | ||||
|   void setFloat(Float value) { | ||||
|     setType(VALUE_IS_FLOAT); | ||||
|     _content.asFloat = value; | ||||
|   } | ||||
|  | ||||
|   void setLinkedRaw(SerializedValue<const char *> value) { | ||||
|     setType(VALUE_IS_LINKED_RAW); | ||||
|     _content.asRaw.data = value.data(); | ||||
|     _content.asRaw.size = value.size(); | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   bool setOwnedRaw(SerializedValue<T> value, MemoryPool *pool) { | ||||
|     char *dup = makeString(value.data(), value.size()).save(pool); | ||||
|     if (dup) { | ||||
|       setType(VALUE_IS_OWNED_RAW); | ||||
|       _content.asRaw.data = dup; | ||||
|       _content.asRaw.size = value.size(); | ||||
|       return true; | ||||
|     } else { | ||||
|       setType(VALUE_IS_NULL); | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   typename enable_if<is_unsigned<T>::value>::type setInteger(T value) { | ||||
|     setUnsignedInteger(value); | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   typename enable_if<is_signed<T>::value>::type setInteger(T value) { | ||||
|     setSignedInteger(value); | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   void setSignedInteger(T value) { | ||||
|     if (value >= 0) { | ||||
|       setType(VALUE_IS_POSITIVE_INTEGER); | ||||
|       _content.asInteger = static_cast<UInt>(value); | ||||
|     } else { | ||||
|       setType(VALUE_IS_NEGATIVE_INTEGER); | ||||
|       _content.asInteger = ~static_cast<UInt>(value) + 1; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void setLinkedString(const char *value) { | ||||
|     setType(VALUE_IS_LINKED_STRING); | ||||
|     _content.asString = value; | ||||
|   } | ||||
|  | ||||
|   void setNull() { | ||||
|     setType(VALUE_IS_NULL); | ||||
|   } | ||||
|  | ||||
|   void setOwnedString(const char *s) { | ||||
|     setType(VALUE_IS_OWNED_STRING); | ||||
|     _content.asString = s; | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   bool setOwnedString(T value, MemoryPool *pool) { | ||||
|     char *dup = value.save(pool); | ||||
|     if (dup) { | ||||
|       setType(VALUE_IS_OWNED_STRING); | ||||
|       _content.asString = dup; | ||||
|       return true; | ||||
|     } else { | ||||
|       setType(VALUE_IS_NULL); | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void setUnsignedInteger(UInt value) { | ||||
|     setType(VALUE_IS_POSITIVE_INTEGER); | ||||
|     _content.asInteger = static_cast<UInt>(value); | ||||
|   } | ||||
|  | ||||
|   CollectionData &toArray() { | ||||
|     setType(VALUE_IS_ARRAY); | ||||
|     _content.asCollection.clear(); | ||||
|     return _content.asCollection; | ||||
|   } | ||||
|  | ||||
|   CollectionData &toObject() { | ||||
|     setType(VALUE_IS_OBJECT); | ||||
|     _content.asCollection.clear(); | ||||
|     return _content.asCollection; | ||||
|   } | ||||
|  | ||||
|   size_t size() const { | ||||
|     if (type() == VALUE_IS_OBJECT || type() == VALUE_IS_ARRAY) | ||||
|       return _content.asCollection.size(); | ||||
|     else | ||||
|       return 0; | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   uint8_t type() const { | ||||
|     return _flags & VALUE_MASK; | ||||
|   } | ||||
|  | ||||
|   void setType(uint8_t t) { | ||||
|     _flags &= KEY_IS_OWNED; | ||||
|     _flags |= t; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| struct ObjectData { | ||||
|   VariantSlot *head; | ||||
|   VariantSlot *tail; | ||||
| }; | ||||
|  | ||||
| struct ArrayData { | ||||
|   VariantSlot *head; | ||||
|   VariantSlot *tail; | ||||
| }; | ||||
|  | ||||
| struct RawData { | ||||
|   const char *data; | ||||
|   size_t size; | ||||
| }; | ||||
|  | ||||
| // A union that defines the actual content of a VariantData. | ||||
| // The enum VariantType determines which member is in use. | ||||
| union VariantContent { | ||||
|   Float asFloat; | ||||
|   UInt asInteger; | ||||
|   ArrayData asArray; | ||||
|   ObjectData asObject; | ||||
|   const char *asString; | ||||
|   struct { | ||||
|     const char *data; | ||||
|     size_t size; | ||||
|   } asRaw; | ||||
| }; | ||||
|  | ||||
| // this struct must be a POD type to prevent error calling offsetof on clang | ||||
| struct VariantData { | ||||
|   VariantContent content; | ||||
|   bool keyIsOwned : 1; | ||||
|   VariantType type : 7; | ||||
| }; | ||||
|  | ||||
| inline VariantData *getVariantData(ArrayData *arr) { | ||||
|   const ptrdiff_t offset = | ||||
|       offsetof(VariantData, content) - offsetof(VariantContent, asArray); | ||||
|   if (!arr) return 0; | ||||
|   return reinterpret_cast<VariantData *>(reinterpret_cast<char *>(arr) - | ||||
|                                          offset); | ||||
| } | ||||
|  | ||||
| inline VariantData *getVariantData(ObjectData *obj) { | ||||
|   const ptrdiff_t offset = | ||||
|       offsetof(VariantData, content) - offsetof(VariantContent, asObject); | ||||
|   if (!obj) return 0; | ||||
|   return reinterpret_cast<VariantData *>(reinterpret_cast<char *>(obj) - | ||||
|                                          offset); | ||||
| } | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
| @@ -4,292 +4,142 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "../Array/ArrayFunctions.hpp" | ||||
| #include "../Misc/SerializedValue.hpp" | ||||
| #include "../Numbers/parseFloat.hpp" | ||||
| #include "../Numbers/parseInteger.hpp" | ||||
| #include "../Object/ObjectFunctions.hpp" | ||||
| #include "VariantData.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
| template <typename T> | ||||
| inline T variantAsIntegral(const VariantData* 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<T>(var->content.asString); | ||||
|     case JSON_FLOAT: | ||||
|       return T(var->content.asFloat); | ||||
|     default: | ||||
|       return 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| inline bool variantAsBoolean(const VariantData* var) { | ||||
|   return variantAsIntegral<int>(var) != 0; | ||||
| } | ||||
|  | ||||
| // T = float/double | ||||
| template <typename T> | ||||
| inline T variantAsFloat(const VariantData* var) { | ||||
|   if (!var) return 0; | ||||
|   switch (var->type) { | ||||
|     case JSON_POSITIVE_INTEGER: | ||||
|     case JSON_BOOLEAN: | ||||
|       return static_cast<T>(var->content.asInteger); | ||||
|     case JSON_NEGATIVE_INTEGER: | ||||
|       return -static_cast<T>(var->content.asInteger); | ||||
|     case JSON_LINKED_STRING: | ||||
|     case JSON_OWNED_STRING: | ||||
|       return parseFloat<T>(var->content.asString); | ||||
|     case JSON_FLOAT: | ||||
|       return static_cast<T>(var->content.asFloat); | ||||
|     default: | ||||
|       return 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| inline const char* variantAsString(const VariantData* var) { | ||||
|   if (!var) return 0; | ||||
|   switch (var->type) { | ||||
|     case JSON_LINKED_STRING: | ||||
|     case JSON_OWNED_STRING: | ||||
|       return var->content.asString; | ||||
|     default: | ||||
|       return 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| inline ArrayData* variantAsArray(VariantData* var) { | ||||
|   if (var && var->type == JSON_ARRAY) | ||||
|     return &var->content.asArray; | ||||
| template <typename Visitor> | ||||
| inline void variantAccept(const VariantData *var, Visitor &visitor) { | ||||
|   if (var != 0) | ||||
|     var->accept(visitor); | ||||
|   else | ||||
|     return 0; | ||||
|     visitor.visitNull(); | ||||
| } | ||||
|  | ||||
| inline const ArrayData* variantAsArray(const VariantData* var) { | ||||
|   if (var && var->type == JSON_ARRAY) | ||||
|     return &var->content.asArray; | ||||
|   else | ||||
|     return 0; | ||||
| inline const CollectionData *variantAsObject(const VariantData *var) { | ||||
|   return var != 0 ? var->asObject() : 0; | ||||
| } | ||||
|  | ||||
| inline ObjectData* variantAsObject(VariantData* var) { | ||||
|   if (var && var->type == JSON_OBJECT) | ||||
|     return &var->content.asObject; | ||||
|   else | ||||
|     return 0; | ||||
| inline CollectionData *variantAsObject(VariantData *var) { | ||||
|   return var != 0 ? var->asObject() : 0; | ||||
| } | ||||
|  | ||||
| inline const ObjectData* variantAsObject(const VariantData* var) { | ||||
|   if (var && var->type == JSON_OBJECT) | ||||
|     return &var->content.asObject; | ||||
|   else | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| inline bool variantSetBoolean(VariantData* var, bool value) { | ||||
|   if (!var) return false; | ||||
|   var->type = JSON_BOOLEAN; | ||||
|   var->content.asInteger = static_cast<UInt>(value); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline bool variantSetFloat(VariantData* var, Float value) { | ||||
|   if (!var) return false; | ||||
|   var->type = JSON_FLOAT; | ||||
|   var->content.asFloat = value; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| inline bool variantSetSignedInteger(VariantData* var, T value) { | ||||
|   if (!var) return false; | ||||
|   if (value >= 0) { | ||||
|     var->type = JSON_POSITIVE_INTEGER; | ||||
|     var->content.asInteger = static_cast<UInt>(value); | ||||
|   } else { | ||||
|     var->type = JSON_NEGATIVE_INTEGER; | ||||
|     var->content.asInteger = ~static_cast<UInt>(value) + 1; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline bool variantSetUnsignedInteger(VariantData* var, UInt value) { | ||||
|   if (!var) return false; | ||||
|   var->type = JSON_POSITIVE_INTEGER; | ||||
|   var->content.asInteger = static_cast<UInt>(value); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline bool variantSetLinkedRaw(VariantData* var, | ||||
|                                 SerializedValue<const char*> 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 <typename T> | ||||
| inline bool variantSetOwnedRaw(VariantData* var, SerializedValue<T> value, | ||||
|                                MemoryPool* pool) { | ||||
|   if (!var) return false; | ||||
|   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 <typename T> | ||||
| inline bool variantSetString(VariantData* var, T value, MemoryPool* pool) { | ||||
|   if (!var) return false; | ||||
|   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 variantSetOwnedString(VariantData* var, char* s) { | ||||
|   if (!var) return false; | ||||
|   var->type = JSON_OWNED_STRING; | ||||
|   var->content.asString = s; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline bool variantSetString(VariantData* var, const char* value) { | ||||
|   if (!var) return false; | ||||
|   var->type = JSON_LINKED_STRING; | ||||
|   var->content.asString = value; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline void variantSetNull(VariantData* var) { | ||||
|   if (!var) return; | ||||
|   var->type = JSON_NULL; | ||||
| } | ||||
|  | ||||
| inline ArrayData* variantToArray(VariantData* var) { | ||||
|   if (!var) return 0; | ||||
|   var->type = JSON_ARRAY; | ||||
|   var->content.asArray.head = 0; | ||||
|   var->content.asArray.tail = 0; | ||||
|   return &var->content.asArray; | ||||
| } | ||||
|  | ||||
| inline ObjectData* variantToObject(VariantData* 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(VariantData* dst, const VariantData* src, | ||||
|                         MemoryPool* pool) { | ||||
| inline bool variantCopyFrom(VariantData *dst, const VariantData *src, | ||||
|                             MemoryPool *pool) { | ||||
|   if (!dst) return false; | ||||
|   if (!src) { | ||||
|     dst->type = JSON_NULL; | ||||
|     dst->setNull(); | ||||
|     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, ZeroTerminatedRamString(src->content.asString), pool); | ||||
|     case JSON_OWNED_RAW: | ||||
|       return variantSetOwnedRaw( | ||||
|           dst, serialized(src->content.asRaw.data, src->content.asRaw.size), | ||||
|           pool); | ||||
|     default: | ||||
|       // caution: don't override keyIsOwned | ||||
|       dst->type = src->type; | ||||
|       dst->content = src->content; | ||||
|       return true; | ||||
|   } | ||||
|   return dst->copyFrom(*src, pool); | ||||
| } | ||||
|  | ||||
| inline bool variantIsInteger(const VariantData* var) { | ||||
|   return var && (var->type == JSON_POSITIVE_INTEGER || | ||||
|                  var->type == JSON_NEGATIVE_INTEGER); | ||||
| } | ||||
|  | ||||
| inline bool variantIsFloat(const VariantData* var) { | ||||
|   return var && | ||||
|          (var->type == JSON_FLOAT || var->type == JSON_POSITIVE_INTEGER || | ||||
|           var->type == JSON_NEGATIVE_INTEGER); | ||||
| } | ||||
|  | ||||
| inline bool variantIsString(const VariantData* var) { | ||||
|   return var && | ||||
|          (var->type == JSON_LINKED_STRING || var->type == JSON_OWNED_STRING); | ||||
| } | ||||
|  | ||||
| inline bool variantIsArray(const VariantData* var) { | ||||
|   return var && var->type == JSON_ARRAY; | ||||
| } | ||||
|  | ||||
| inline bool variantIsObject(const VariantData* var) { | ||||
|   return var && var->type == JSON_OBJECT; | ||||
| } | ||||
|  | ||||
| inline bool variantIsNull(const VariantData* var) { | ||||
|   return var == 0 || var->type == JSON_NULL; | ||||
| } | ||||
|  | ||||
| inline bool variantEquals(const VariantData* a, const VariantData* b) { | ||||
| inline bool variantEquals(const VariantData *a, const VariantData *b) { | ||||
|   if (a == b) return true; | ||||
|   if (!a || !b) return false; | ||||
|   if (a->type != b->type) return false; | ||||
|  | ||||
|   switch (a->type) { | ||||
|     case JSON_LINKED_STRING: | ||||
|     case JSON_OWNED_STRING: | ||||
|       return !strcmp(a->content.asString, b->content.asString); | ||||
|  | ||||
|     case JSON_LINKED_RAW: | ||||
|     case JSON_OWNED_RAW: | ||||
|       return a->content.asRaw.size == b->content.asRaw.size && | ||||
|              !memcmp(a->content.asRaw.data, b->content.asRaw.data, | ||||
|                      a->content.asRaw.size); | ||||
|  | ||||
|     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; | ||||
|   } | ||||
|   return a->equals(*b); | ||||
| } | ||||
|  | ||||
| inline bool variantIsArray(const VariantData *var) { | ||||
|   return var && var->isArray(); | ||||
| } | ||||
|  | ||||
| inline bool variantIsBoolean(const VariantData *var) { | ||||
|   return var && var->isBoolean(); | ||||
| } | ||||
|  | ||||
| inline bool variantIsInteger(const VariantData *var) { | ||||
|   return var && var->isInteger(); | ||||
| } | ||||
|  | ||||
| inline bool variantIsFloat(const VariantData *var) { | ||||
|   return var && var->isFloat(); | ||||
| } | ||||
|  | ||||
| inline bool variantIsString(const VariantData *var) { | ||||
|   return var && var->isString(); | ||||
| } | ||||
|  | ||||
| inline bool variantIsObject(const VariantData *var) { | ||||
|   return var && var->isObject(); | ||||
| } | ||||
|  | ||||
| inline bool variantIsNull(const VariantData *var) { | ||||
|   return var == 0 || var->isNull(); | ||||
| } | ||||
|  | ||||
| inline bool variantSetBoolean(VariantData *var, bool value) { | ||||
|   if (!var) return false; | ||||
|   var->setBoolean(value); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline bool variantSetFloat(VariantData *var, Float value) { | ||||
|   if (!var) return false; | ||||
|   var->setFloat(value); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline bool variantSetLinkedRaw(VariantData *var, | ||||
|                                 SerializedValue<const char *> value) { | ||||
|   if (!var) return false; | ||||
|   var->setLinkedRaw(value); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| inline bool variantSetOwnedRaw(VariantData *var, SerializedValue<T> value, | ||||
|                                MemoryPool *pool) { | ||||
|   return var != 0 && var->setOwnedRaw(value, pool); | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| inline bool variantSetSignedInteger(VariantData *var, T value) { | ||||
|   if (!var) return false; | ||||
|   var->setSignedInteger(value); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline bool variantSetLinkedString(VariantData *var, const char *value) { | ||||
|   if (!var) return false; | ||||
|   var->setLinkedString(value); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline void variantSetNull(VariantData *var) { | ||||
|   if (!var) return; | ||||
|   var->setNull(); | ||||
| } | ||||
|  | ||||
| inline bool variantSetOwnedString(VariantData *var, char *value) { | ||||
|   if (!var) return false; | ||||
|   var->setOwnedString(value); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| inline bool variantSetOwnedString(VariantData *var, T value, MemoryPool *pool) { | ||||
|   return var != 0 && var->setOwnedString(value, pool); | ||||
| } | ||||
|  | ||||
| inline bool variantSetUnsignedInteger(VariantData *var, UInt value) { | ||||
|   if (!var) return false; | ||||
|   var->setUnsignedInteger(value); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline size_t variantSize(const VariantData *var) { | ||||
|   return var != 0 ? var->size() : 0; | ||||
| } | ||||
|  | ||||
| inline CollectionData *variantToArray(VariantData *var) { | ||||
|   if (!var) return 0; | ||||
|   return &var->toArray(); | ||||
| } | ||||
|  | ||||
| inline CollectionData *variantToObject(VariantData *var) { | ||||
|   if (!var) return 0; | ||||
|   return &var->toObject(); | ||||
| } | ||||
|  | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
| @@ -13,6 +13,53 @@ | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| template <typename T> | ||||
| inline T VariantData::asIntegral() const { | ||||
|   switch (type()) { | ||||
|     case VALUE_IS_POSITIVE_INTEGER: | ||||
|     case VALUE_IS_BOOLEAN: | ||||
|       return T(_content.asInteger); | ||||
|     case VALUE_IS_NEGATIVE_INTEGER: | ||||
|       return T(~_content.asInteger + 1); | ||||
|     case VALUE_IS_LINKED_STRING: | ||||
|     case VALUE_IS_OWNED_STRING: | ||||
|       return parseInteger<T>(_content.asString); | ||||
|     case VALUE_IS_FLOAT: | ||||
|       return T(_content.asFloat); | ||||
|     default: | ||||
|       return 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| // T = float/double | ||||
| template <typename T> | ||||
| inline T VariantData::asFloat() const { | ||||
|   switch (type()) { | ||||
|     case VALUE_IS_POSITIVE_INTEGER: | ||||
|     case VALUE_IS_BOOLEAN: | ||||
|       return static_cast<T>(_content.asInteger); | ||||
|     case VALUE_IS_NEGATIVE_INTEGER: | ||||
|       return -static_cast<T>(_content.asInteger); | ||||
|     case VALUE_IS_LINKED_STRING: | ||||
|     case VALUE_IS_OWNED_STRING: | ||||
|       return parseFloat<T>(_content.asString); | ||||
|     case VALUE_IS_FLOAT: | ||||
|       return static_cast<T>(_content.asFloat); | ||||
|     default: | ||||
|       return 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| inline const char* VariantData::asString() const { | ||||
|   switch (type()) { | ||||
|     case VALUE_IS_LINKED_STRING: | ||||
|     case VALUE_IS_OWNED_STRING: | ||||
|       return _content.asString; | ||||
|     default: | ||||
|       return 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| inline bool VariantRef::set(ArrayRef array) const { | ||||
|   return to<ArrayRef>().copyFrom(array); | ||||
| } | ||||
| @@ -31,35 +78,35 @@ inline bool VariantRef::set(const ObjectSubscript<TString>& value) const { | ||||
| } | ||||
|  | ||||
| inline bool VariantRef::set(VariantConstRef value) const { | ||||
|   return variantCopy(_data, value._data, _memoryPool); | ||||
|   return variantCopyFrom(_data, value._data, _pool); | ||||
| } | ||||
|  | ||||
| inline bool VariantRef::set(VariantRef value) const { | ||||
|   return variantCopy(_data, value._data, _memoryPool); | ||||
|   return variantCopyFrom(_data, value._data, _pool); | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| inline typename enable_if<is_same<T, ArrayRef>::value, T>::type VariantRef::as() | ||||
|     const { | ||||
|   return ArrayRef(_memoryPool, variantAsArray(_data)); | ||||
|   return ArrayRef(_pool, _data != 0 ? _data->asArray() : 0); | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| inline typename enable_if<is_same<T, ObjectRef>::value, T>::type | ||||
| VariantRef::as() const { | ||||
|   return ObjectRef(_memoryPool, variantAsObject(_data)); | ||||
|   return ObjectRef(_pool, variantAsObject(_data)); | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| inline typename enable_if<is_same<T, ArrayRef>::value, ArrayRef>::type | ||||
| VariantRef::to() const { | ||||
|   return ArrayRef(_memoryPool, variantToArray(_data)); | ||||
|   return ArrayRef(_pool, variantToArray(_data)); | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| typename enable_if<is_same<T, ObjectRef>::value, ObjectRef>::type | ||||
| VariantRef::to() const { | ||||
|   return ObjectRef(_memoryPool, variantToObject(_data)); | ||||
|   return ObjectRef(_pool, variantToObject(_data)); | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| @@ -69,50 +116,8 @@ VariantRef::to() const { | ||||
|   return *this; | ||||
| } | ||||
|  | ||||
| template <typename Visitor> | ||||
| inline void VariantRef::accept(Visitor& visitor) const { | ||||
|   return VariantConstRef(_data).accept(visitor); | ||||
| } | ||||
|  | ||||
| template <typename Visitor> | ||||
| inline void VariantConstRef::accept(Visitor& visitor) const { | ||||
|   if (!_data) return visitor.visitNull(); | ||||
|  | ||||
|   switch (_data->type) { | ||||
|     case JSON_FLOAT: | ||||
|       return visitor.visitFloat(_data->content.asFloat); | ||||
|  | ||||
|     case JSON_ARRAY: | ||||
|       return visitor.visitArray(ArrayConstRef(&_data->content.asArray)); | ||||
|  | ||||
|     case JSON_OBJECT: | ||||
|       return visitor.visitObject(ObjectConstRef(&_data->content.asObject)); | ||||
|  | ||||
|     case JSON_LINKED_STRING: | ||||
|     case JSON_OWNED_STRING: | ||||
|       return visitor.visitString(_data->content.asString); | ||||
|  | ||||
|     case JSON_OWNED_RAW: | ||||
|     case JSON_LINKED_RAW: | ||||
|       return visitor.visitRawJson(_data->content.asRaw.data, | ||||
|                                   _data->content.asRaw.size); | ||||
|  | ||||
|     case JSON_NEGATIVE_INTEGER: | ||||
|       return visitor.visitNegativeInteger(_data->content.asInteger); | ||||
|  | ||||
|     case JSON_POSITIVE_INTEGER: | ||||
|       return visitor.visitPositiveInteger(_data->content.asInteger); | ||||
|  | ||||
|     case JSON_BOOLEAN: | ||||
|       return visitor.visitBoolean(_data->content.asInteger != 0); | ||||
|  | ||||
|     default: | ||||
|       return visitor.visitNull(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| inline VariantConstRef VariantConstRef::operator[](size_t index) const { | ||||
|   return ArrayConstRef(variantAsArray(_data))[index]; | ||||
|   return ArrayConstRef(_data != 0 ? _data->asArray() : 0)[index]; | ||||
| } | ||||
|  | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
| @@ -14,7 +14,6 @@ | ||||
| #include "../Operators/VariantOperators.hpp" | ||||
| #include "../Polyfills/type_traits.hpp" | ||||
| #include "VariantAs.hpp" | ||||
| #include "VariantData.hpp" | ||||
| #include "VariantFunctions.hpp" | ||||
| #include "VariantRef.hpp" | ||||
|  | ||||
| @@ -58,7 +57,7 @@ class VariantRefBase { | ||||
|   template <typename T> | ||||
|   FORCE_INLINE typename enable_if<is_same<T, bool>::value, bool>::type is() | ||||
|       const { | ||||
|     return _data && _data->type == JSON_BOOLEAN; | ||||
|     return variantIsBoolean(_data); | ||||
|   } | ||||
|   // | ||||
|   // bool is<const char*>() const; | ||||
| @@ -96,13 +95,8 @@ class VariantRefBase { | ||||
|     return variantIsNull(_data); | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE bool isInvalid() const { | ||||
|     return _data == 0; | ||||
|   } | ||||
|  | ||||
|   size_t size() const { | ||||
|     return objectSize(variantAsObject(_data)) + | ||||
|            arraySize(variantAsArray(_data)); | ||||
|     return variantSize(_data); | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
| @@ -125,11 +119,11 @@ class VariantRef : public VariantRefBase<VariantData>, | ||||
|  | ||||
|  public: | ||||
|   // Intenal use only | ||||
|   FORCE_INLINE VariantRef(MemoryPool *memoryPool, VariantData *data) | ||||
|       : base_type(data), _memoryPool(memoryPool) {} | ||||
|   FORCE_INLINE VariantRef(MemoryPool *pool, VariantData *data) | ||||
|       : base_type(data), _pool(pool) {} | ||||
|  | ||||
|   // Creates an uninitialized VariantRef | ||||
|   FORCE_INLINE VariantRef() : base_type(0), _memoryPool(0) {} | ||||
|   FORCE_INLINE VariantRef() : base_type(0), _pool(0) {} | ||||
|  | ||||
|   // set(bool value) | ||||
|   FORCE_INLINE bool set(bool value) const { | ||||
| @@ -180,7 +174,7 @@ class VariantRef : public VariantRefBase<VariantData>, | ||||
|   FORCE_INLINE bool set( | ||||
|       SerializedValue<T> value, | ||||
|       typename enable_if<!is_same<const char *, T>::value>::type * = 0) const { | ||||
|     return variantSetOwnedRaw(_data, value, _memoryPool); | ||||
|     return variantSetOwnedRaw(_data, value, _pool); | ||||
|   } | ||||
|  | ||||
|   // set(const std::string&) | ||||
| @@ -189,28 +183,20 @@ class VariantRef : public VariantRefBase<VariantData>, | ||||
|   FORCE_INLINE bool set( | ||||
|       const T &value, | ||||
|       typename enable_if<IsString<T>::value>::type * = 0) const { | ||||
|     return variantSetString(_data, makeString(value), _memoryPool); | ||||
|     return variantSetOwnedString(_data, makeString(value), _pool); | ||||
|   } | ||||
|  | ||||
|   // set(char*) | ||||
|   // set(const __FlashStringHelper*) | ||||
|   template <typename T> | ||||
|   FORCE_INLINE bool set( | ||||
|       T *value, typename enable_if<IsString<T *>::value>::type * = 0) const { | ||||
|     return variantSetString(_data, makeString(value), _memoryPool); | ||||
|     return variantSetOwnedString(_data, makeString(value), _pool); | ||||
|   } | ||||
|  | ||||
|   // set(const char*); | ||||
|   FORCE_INLINE bool set(const char *value) const { | ||||
|     return variantSetString(_data, value); | ||||
|   } | ||||
|  | ||||
|   // for internal use only | ||||
|   FORCE_INLINE bool set(StringInMemoryPool value) const { | ||||
|     return variantSetOwnedString(_data, | ||||
|                                  value.save(_memoryPool));  // TODO: remove? | ||||
|   } | ||||
|   FORCE_INLINE bool set(ZeroTerminatedRamStringConst value) const { | ||||
|     return variantSetString(_data, value.c_str()); | ||||
|     return variantSetLinkedString(_data, value); | ||||
|   } | ||||
|  | ||||
|   bool set(VariantConstRef value) const; | ||||
| @@ -255,7 +241,9 @@ class VariantRef : public VariantRefBase<VariantData>, | ||||
|   } | ||||
|  | ||||
|   template <typename Visitor> | ||||
|   void accept(Visitor &visitor) const; | ||||
|   void accept(Visitor &visitor) const { | ||||
|     variantAccept(_data, visitor); | ||||
|   } | ||||
|  | ||||
|   FORCE_INLINE bool operator==(VariantRef lhs) const { | ||||
|     return variantEquals(_data, lhs._data); | ||||
| @@ -281,7 +269,7 @@ class VariantRef : public VariantRefBase<VariantData>, | ||||
|       const; | ||||
|  | ||||
|  private: | ||||
|   MemoryPool *_memoryPool; | ||||
|   MemoryPool *_pool; | ||||
| }; | ||||
|  | ||||
| class VariantConstRef : public VariantRefBase<const VariantData>, | ||||
| @@ -296,7 +284,9 @@ class VariantConstRef : public VariantRefBase<const VariantData>, | ||||
|   VariantConstRef(VariantRef var) : base_type(var._data) {} | ||||
|  | ||||
|   template <typename Visitor> | ||||
|   void accept(Visitor &visitor) const; | ||||
|   void accept(Visitor &visitor) const { | ||||
|     variantAccept(_data, visitor); | ||||
|   } | ||||
|  | ||||
|   // Get the variant as the specified type. | ||||
|   // | ||||
| @@ -323,7 +313,8 @@ class VariantConstRef : public VariantRefBase<const VariantData>, | ||||
|   FORCE_INLINE | ||||
|       typename enable_if<IsString<TString *>::value, VariantConstRef>::type | ||||
|       operator[](TString *key) const { | ||||
|     return VariantConstRef(objectGet(variantAsObject(_data), makeString(key))); | ||||
|     const CollectionData *obj = variantAsObject(_data); | ||||
|     return VariantConstRef(obj ? obj->get(makeString(key)) : 0); | ||||
|   } | ||||
| }; | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
| @@ -5,7 +5,9 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "../Polyfills/type_traits.hpp" | ||||
| #include "../Variant/VariantData.hpp" | ||||
| #include "../Variant/VariantContent.hpp" | ||||
|  | ||||
| #include <stdint.h>  // int8_t, int16_t | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| @@ -16,8 +18,7 @@ class VariantSlot { | ||||
|   // we cannot use composition because it adds padding | ||||
|   // (+20% on ESP8266 for example) | ||||
|   VariantContent _content; | ||||
|   bool _keyIsOwned : 1; | ||||
|   VariantType _type : 7; | ||||
|   uint8_t _flags; | ||||
|   VariantSlotDiff _next; | ||||
|   const char* _key; | ||||
|  | ||||
| @@ -28,23 +29,23 @@ class VariantSlot { | ||||
|   // - no virtual | ||||
|   // - no inheritance | ||||
|  | ||||
|   VariantData* getData() { | ||||
|   VariantData* data() { | ||||
|     return reinterpret_cast<VariantData*>(&_content); | ||||
|   } | ||||
|  | ||||
|   const VariantData* getData() const { | ||||
|   const VariantData* data() const { | ||||
|     return reinterpret_cast<const VariantData*>(&_content); | ||||
|   } | ||||
|  | ||||
|   VariantSlot* getNext() { | ||||
|   VariantSlot* next() { | ||||
|     return _next ? this + _next : 0; | ||||
|   } | ||||
|  | ||||
|   const VariantSlot* getNext() const { | ||||
|     return const_cast<VariantSlot*>(this)->getNext(); | ||||
|   const VariantSlot* next() const { | ||||
|     return const_cast<VariantSlot*>(this)->next(); | ||||
|   } | ||||
|  | ||||
|   VariantSlot* getNext(size_t distance) { | ||||
|   VariantSlot* next(size_t distance) { | ||||
|     VariantSlot* slot = this; | ||||
|     while (distance--) { | ||||
|       if (!slot->_next) return 0; | ||||
| @@ -53,29 +54,26 @@ class VariantSlot { | ||||
|     return slot; | ||||
|   } | ||||
|  | ||||
|   VariantSlot* getPrev(VariantSlot* head) { | ||||
|     while (head) { | ||||
|       VariantSlot* nxt = head->getNext(); | ||||
|       if (nxt == this) return head; | ||||
|       head = nxt; | ||||
|     } | ||||
|     return head; | ||||
|   } | ||||
|  | ||||
|   const VariantSlot* getNext(size_t distance) const { | ||||
|     return const_cast<VariantSlot*>(this)->getNext(distance); | ||||
|   const VariantSlot* next(size_t distance) const { | ||||
|     return const_cast<VariantSlot*>(this)->next(distance); | ||||
|   } | ||||
|  | ||||
|   void setNext(VariantSlot* slot) { | ||||
|     _next = VariantSlotDiff(slot ? slot - this : 0); | ||||
|   } | ||||
|  | ||||
|   void attachTo(VariantSlot* tail) { | ||||
|     tail->_next = VariantSlotDiff(this - tail); | ||||
|   void setNextNotNull(VariantSlot* slot) { | ||||
|     ARDUINOJSON_ASSERT(slot != 0); | ||||
|     _next = VariantSlotDiff(slot - this); | ||||
|   } | ||||
|  | ||||
|   void setKey(const char* k, bool owned) { | ||||
|     _keyIsOwned = owned; | ||||
|   void setOwnedKey(const char* k) { | ||||
|     _flags |= KEY_IS_OWNED; | ||||
|     _key = k; | ||||
|   } | ||||
|  | ||||
|   void setLinkedKey(const char* k) { | ||||
|     _flags &= VALUE_MASK; | ||||
|     _key = k; | ||||
|   } | ||||
|  | ||||
| @@ -84,13 +82,13 @@ class VariantSlot { | ||||
|   } | ||||
|  | ||||
|   bool ownsKey() const { | ||||
|     return _keyIsOwned; | ||||
|     return (_flags & KEY_IS_OWNED) != 0; | ||||
|   } | ||||
|  | ||||
|   void init() { | ||||
|   void clear() { | ||||
|     _next = 0; | ||||
|     _type = JSON_NULL; | ||||
|     _keyIsOwned = false; | ||||
|     _flags = 0; | ||||
|     _key = 0; | ||||
|   } | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -22,7 +22,25 @@ TEST_CASE("JsonArray::operator==()") { | ||||
|     REQUIRE_FALSE(array1c == array2c); | ||||
|   } | ||||
|  | ||||
|   SECTION("should return false when arrays differ") { | ||||
|   SECTION("should return false when LHS has more elements") { | ||||
|     array1.add(1); | ||||
|     array1.add(2); | ||||
|     array2.add(1); | ||||
|  | ||||
|     REQUIRE_FALSE(array1 == array2); | ||||
|     REQUIRE_FALSE(array1c == array2c); | ||||
|   } | ||||
|  | ||||
|   SECTION("should return false when RKS has more elements") { | ||||
|     array1.add(1); | ||||
|     array2.add(1); | ||||
|     array2.add(2); | ||||
|  | ||||
|     REQUIRE_FALSE(array1 == array2); | ||||
|     REQUIRE_FALSE(array1c == array2c); | ||||
|   } | ||||
|  | ||||
|   SECTION("should return true when arrays equal") { | ||||
|     array1.add("coucou"); | ||||
|     array2.add("coucou"); | ||||
|  | ||||
|   | ||||
| @@ -22,7 +22,25 @@ TEST_CASE("JsonObject::operator==()") { | ||||
|     REQUIRE_FALSE(obj1c == obj2c); | ||||
|   } | ||||
|  | ||||
|   SECTION("should return false when objs differ") { | ||||
|   SECTION("should return false when LHS has more elements") { | ||||
|     obj1["hello"] = "coucou"; | ||||
|     obj1["world"] = 666; | ||||
|     obj2["hello"] = "coucou"; | ||||
|  | ||||
|     REQUIRE_FALSE(obj1 == obj2); | ||||
|     REQUIRE_FALSE(obj1c == obj2c); | ||||
|   } | ||||
|  | ||||
|   SECTION("should return false when RKS has more elements") { | ||||
|     obj1["hello"] = "coucou"; | ||||
|     obj2["hello"] = "coucou"; | ||||
|     obj2["world"] = 666; | ||||
|  | ||||
|     REQUIRE_FALSE(obj1 == obj2); | ||||
|     REQUIRE_FALSE(obj1c == obj2c); | ||||
|   } | ||||
|  | ||||
|   SECTION("should return true when objs equal") { | ||||
|     obj1["hello"] = "world"; | ||||
|     obj1["anwser"] = 42; | ||||
|     // insert in different order | ||||
|   | ||||
| @@ -12,30 +12,30 @@ static char buffer[4096]; | ||||
|  | ||||
| TEST_CASE("StringBuilder") { | ||||
|   SECTION("Works when buffer is big enough") { | ||||
|     MemoryPool memoryPool(buffer, addPadding(JSON_STRING_SIZE(6))); | ||||
|     MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6))); | ||||
|  | ||||
|     StringBuilder str(&memoryPool); | ||||
|     StringBuilder str(&pool); | ||||
|     str.append("hello"); | ||||
|  | ||||
|     REQUIRE(str.complete().equals("hello")); | ||||
|     REQUIRE(str.complete() == std::string("hello")); | ||||
|   } | ||||
|  | ||||
|   SECTION("Returns null when too small") { | ||||
|     MemoryPool memoryPool(buffer, sizeof(void*)); | ||||
|     MemoryPool pool(buffer, sizeof(void*)); | ||||
|  | ||||
|     StringBuilder str(&memoryPool); | ||||
|     StringBuilder str(&pool); | ||||
|     str.append("hello world!"); | ||||
|  | ||||
|     REQUIRE(str.complete().isNull()); | ||||
|     REQUIRE(str.complete() == 0); | ||||
|   } | ||||
|  | ||||
|   SECTION("Increases size of memory pool") { | ||||
|     MemoryPool memoryPool(buffer, addPadding(JSON_STRING_SIZE(6))); | ||||
|     MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6))); | ||||
|  | ||||
|     StringBuilder str(&memoryPool); | ||||
|     StringBuilder str(&pool); | ||||
|     str.append('h'); | ||||
|     str.complete(); | ||||
|  | ||||
|     REQUIRE(JSON_STRING_SIZE(2) == memoryPool.size()); | ||||
|     REQUIRE(JSON_STRING_SIZE(2) == pool.size()); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -11,21 +11,21 @@ static const size_t poolCapacity = 512; | ||||
|  | ||||
| TEST_CASE("MemoryPool::clear()") { | ||||
|   char buffer[poolCapacity]; | ||||
|   MemoryPool memoryPool(buffer, sizeof(buffer)); | ||||
|   MemoryPool pool(buffer, sizeof(buffer)); | ||||
|  | ||||
|   SECTION("Discards allocated variants") { | ||||
|     memoryPool.allocVariant(); | ||||
|     pool.allocVariant(); | ||||
|  | ||||
|     memoryPool.clear(); | ||||
|     REQUIRE(memoryPool.size() == 0); | ||||
|     pool.clear(); | ||||
|     REQUIRE(pool.size() == 0); | ||||
|   } | ||||
|  | ||||
|   SECTION("Discards allocated strings") { | ||||
|     memoryPool.allocFrozenString(10); | ||||
|     REQUIRE(memoryPool.size() > 0); | ||||
|     pool.allocFrozenString(10); | ||||
|     REQUIRE(pool.size() > 0); | ||||
|  | ||||
|     memoryPool.clear(); | ||||
|     pool.clear(); | ||||
|  | ||||
|     REQUIRE(memoryPool.size() == 0); | ||||
|     REQUIRE(pool.size() == 0); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -11,48 +11,48 @@ char buffer[4096]; | ||||
|  | ||||
| TEST_CASE("MemoryPool::capacity()") { | ||||
|   const size_t capacity = 64; | ||||
|   MemoryPool memoryPool(buffer, capacity); | ||||
|   REQUIRE(capacity == memoryPool.capacity()); | ||||
|   MemoryPool pool(buffer, capacity); | ||||
|   REQUIRE(capacity == pool.capacity()); | ||||
| } | ||||
|  | ||||
| TEST_CASE("MemoryPool::size()") { | ||||
|   MemoryPool memoryPool(buffer, sizeof(buffer)); | ||||
|   MemoryPool pool(buffer, sizeof(buffer)); | ||||
|  | ||||
|   SECTION("Initial size is 0") { | ||||
|     REQUIRE(0 == memoryPool.size()); | ||||
|     REQUIRE(0 == pool.size()); | ||||
|   } | ||||
|  | ||||
|   SECTION("size() == capacity() after allocExpandableString()") { | ||||
|     memoryPool.allocExpandableString(); | ||||
|     REQUIRE(memoryPool.size() == memoryPool.capacity()); | ||||
|     pool.allocExpandableString(); | ||||
|     REQUIRE(pool.size() == pool.capacity()); | ||||
|   } | ||||
|  | ||||
|   SECTION("Decreases after freezeString()") { | ||||
|     StringSlot a = memoryPool.allocExpandableString(); | ||||
|     memoryPool.freezeString(a, 1); | ||||
|     REQUIRE(memoryPool.size() == JSON_STRING_SIZE(1)); | ||||
|     StringSlot a = pool.allocExpandableString(); | ||||
|     pool.freezeString(a, 1); | ||||
|     REQUIRE(pool.size() == JSON_STRING_SIZE(1)); | ||||
|  | ||||
|     StringSlot b = memoryPool.allocExpandableString(); | ||||
|     memoryPool.freezeString(b, 1); | ||||
|     REQUIRE(memoryPool.size() == 2 * JSON_STRING_SIZE(1)); | ||||
|     StringSlot b = pool.allocExpandableString(); | ||||
|     pool.freezeString(b, 1); | ||||
|     REQUIRE(pool.size() == 2 * JSON_STRING_SIZE(1)); | ||||
|   } | ||||
|  | ||||
|   SECTION("Increases after allocFrozenString()") { | ||||
|     memoryPool.allocFrozenString(0); | ||||
|     REQUIRE(memoryPool.size() == JSON_STRING_SIZE(0)); | ||||
|     pool.allocFrozenString(0); | ||||
|     REQUIRE(pool.size() == JSON_STRING_SIZE(0)); | ||||
|  | ||||
|     memoryPool.allocFrozenString(0); | ||||
|     REQUIRE(memoryPool.size() == 2 * JSON_STRING_SIZE(0)); | ||||
|     pool.allocFrozenString(0); | ||||
|     REQUIRE(pool.size() == 2 * JSON_STRING_SIZE(0)); | ||||
|   } | ||||
|  | ||||
|   SECTION("Doesn't grow when memory pool is full") { | ||||
|     const size_t variantCount = sizeof(buffer) / sizeof(VariantSlot); | ||||
|  | ||||
|     for (size_t i = 0; i < variantCount; i++) memoryPool.allocVariant(); | ||||
|     size_t size = memoryPool.size(); | ||||
|     for (size_t i = 0; i < variantCount; i++) pool.allocVariant(); | ||||
|     size_t size = pool.size(); | ||||
|  | ||||
|     memoryPool.allocVariant(); | ||||
|     pool.allocVariant(); | ||||
|  | ||||
|     REQUIRE(size == memoryPool.size()); | ||||
|     REQUIRE(size == pool.size()); | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user