forked from bblanchon/ArduinoJson
Compare commits
12 Commits
v6.5.0-bet
...
v6.6.0-bet
Author | SHA1 | Date | |
---|---|---|---|
b8d0041851 | |||
0a97d4c825 | |||
5eee947ffe | |||
720e6548c7 | |||
f375459d53 | |||
e842838a23 | |||
00aa038818 | |||
eb78077a0c | |||
d8d939660b | |||
ae089dcff7 | |||
1a4515c0b9 | |||
4eee8e8bdf |
@ -125,4 +125,6 @@ matrix:
|
||||
cache:
|
||||
directories:
|
||||
- "~/.platformio"
|
||||
- "fuzzing/json_corpus"
|
||||
- "fuzzing/msgpack_corpus"
|
||||
script: scripts/travis/$SCRIPT.sh
|
||||
|
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,6 +1,17 @@
|
||||
ArduinoJson: change log
|
||||
=======================
|
||||
|
||||
v6.6.0-beta (2018-11-13)
|
||||
-----------
|
||||
|
||||
* Removed `JsonArray::is<T>(i)` and `JsonArray::set(i,v)`
|
||||
* Removed `JsonObject::is<T>(k)` and `JsonObject::set(k,v)`
|
||||
* Replaced `T JsonArray::get<T>(i)` with `JsonVariant JsonArray::get(i)`
|
||||
* Replaced `T JsonObject::get<T>(k)` with `JsonVariant JsonObject::get(k)`
|
||||
* Added `JSON_STRING_SIZE()`
|
||||
* Replacing or removing a value now releases the memory
|
||||
* Added `DeserializationError::code()` to be used in switch statements (issue #846)
|
||||
|
||||
v6.5.0-beta (2018-10-13)
|
||||
-----------
|
||||
|
||||
|
@ -7,10 +7,16 @@ project(ArduinoJson)
|
||||
|
||||
enable_testing()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
|
||||
add_definitions(-DARDUINOJSON_DEBUG)
|
||||
add_compile_options(-g -O0)
|
||||
endif()
|
||||
|
||||
if(${COVERAGE})
|
||||
set(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
|
||||
set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage")
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_LIST_DIR}/src)
|
||||
add_subdirectory(third-party/catch)
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(fuzzing)
|
||||
|
@ -19,9 +19,9 @@ void setup() {
|
||||
StaticJsonDocument<200> doc;
|
||||
|
||||
// StaticJsonDocument<N> allocates memory on the stack, it can be
|
||||
// replaced by DynamicJsonObject which allocates in the heap.
|
||||
// replaced by DynamicJsonDocument which allocates in the heap.
|
||||
//
|
||||
// DynamicJsonObject doc(200);
|
||||
// DynamicJsonDocument doc(200);
|
||||
|
||||
// JSON input string.
|
||||
//
|
||||
|
13
fuzzing/CMakeLists.txt
Normal file
13
fuzzing/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# ArduinoJson - arduinojson.org
|
||||
# Copyright Benoit Blanchon 2014-2018
|
||||
# MIT License
|
||||
|
||||
add_executable(msgpack_fuzzer
|
||||
msgpack_fuzzer.cpp
|
||||
fuzzer_main.cpp
|
||||
)
|
||||
|
||||
add_executable(json_fuzzer
|
||||
json_fuzzer.cpp
|
||||
fuzzer_main.cpp
|
||||
)
|
@ -1,6 +1,6 @@
|
||||
# CAUTION: this file is invoked by https://github.com/google/oss-fuzz
|
||||
|
||||
CXXFLAGS += -I../src
|
||||
CXXFLAGS += -I../src -DARDUINOJSON_DEBUG
|
||||
|
||||
all: \
|
||||
$(OUT)/json_fuzzer \
|
||||
|
50
fuzzing/fuzzer_main.cpp
Normal file
50
fuzzing/fuzzer_main.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
// This file is NOT use by Google's OSS fuzz
|
||||
// I only use it to reproduce the bugs found
|
||||
|
||||
#include <stdint.h> // size_t
|
||||
#include <stdio.h> // fopen et al.
|
||||
#include <stdlib.h> // exit
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
|
||||
|
||||
std::vector<uint8_t> read(const char* path) {
|
||||
FILE* f = fopen(path, "rb");
|
||||
if (!f) {
|
||||
std::cerr << "Failed to open " << path << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
size_t size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
std::vector<uint8_t> buffer(size);
|
||||
if (fread(buffer.data(), 1, size, f) != size) {
|
||||
fclose(f);
|
||||
std::cerr << "Failed to read " << path << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
if (argc < 2) {
|
||||
std::cerr << "Usage: msgpack_fuzzer files" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::cout << "Loading " << argv[i] << std::endl;
|
||||
std::vector<uint8_t> buffer = read(argv[i]);
|
||||
LLVMFuzzerTestOneInput(buffer.data(), buffer.size());
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/bblanchon/ArduinoJson.git"
|
||||
},
|
||||
"version": "6.5.0-beta",
|
||||
"version": "6.6.0-beta",
|
||||
"authors": {
|
||||
"name": "Benoit Blanchon",
|
||||
"url": "https://blog.benoitblanchon.fr"
|
||||
|
@ -1,5 +1,5 @@
|
||||
name=ArduinoJson
|
||||
version=6.5.0-beta
|
||||
version=6.6.0-beta
|
||||
author=Benoit Blanchon <blog.benoitblanchon.fr>
|
||||
maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
|
||||
sentence=An efficient and elegant JSON library for Arduino.
|
||||
|
@ -16,7 +16,7 @@ fuzz() {
|
||||
|
||||
export ASAN_OPTIONS="detect_leaks=0"
|
||||
export LLVM_PROFILE_FILE="${FUZZER}.profraw"
|
||||
./${FUZZER} "$CORPUS_DIR" "$SEED_CORPUS_DIR" -max_total_time=30
|
||||
./${FUZZER} "$CORPUS_DIR" "$SEED_CORPUS_DIR" -max_total_time=30 -timeout=1
|
||||
|
||||
llvm-profdata-${CLANG} merge -sparse ${LLVM_PROFILE_FILE} -o ${FUZZER}.profdata
|
||||
llvm-cov-${CLANG} report ./${FUZZER} -instr-profile=${FUZZER}.profdata
|
||||
|
@ -130,16 +130,6 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ARDUINOJSON_ENABLE_ALIGNMENT
|
||||
#ifdef ARDUINO_ARCH_AVR
|
||||
// alignment isn't needed for 8-bit AVR
|
||||
#define ARDUINOJSON_ENABLE_ALIGNMENT 0
|
||||
#else
|
||||
// but most processors need pointers to be align on word size
|
||||
#define ARDUINOJSON_ENABLE_ALIGNMENT 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Control the exponentiation threshold for big numbers
|
||||
// CAUTION: cannot be more that 1e9 !!!!
|
||||
#ifndef ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD
|
||||
|
@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "JsonVariantData.hpp"
|
||||
#include "Slot.hpp"
|
||||
#include "SlotFunctions.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
@ -13,10 +12,11 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
inline JsonVariantData* arrayAdd(JsonArrayData* arr, MemoryPool* pool) {
|
||||
if (!arr) return 0;
|
||||
|
||||
Slot* slot = new (pool) Slot();
|
||||
VariantSlot* slot = pool->allocVariant();
|
||||
if (!slot) return 0;
|
||||
|
||||
slot->next = 0;
|
||||
slot->value.type = JSON_NULL;
|
||||
|
||||
if (arr->tail) {
|
||||
slot->prev = arr->tail;
|
||||
@ -28,20 +28,22 @@ inline JsonVariantData* arrayAdd(JsonArrayData* arr, MemoryPool* pool) {
|
||||
arr->tail = slot;
|
||||
}
|
||||
|
||||
slot->value.keyIsOwned = false;
|
||||
return &slot->value;
|
||||
}
|
||||
|
||||
inline Slot* arrayGetSlot(const JsonArrayData* arr, size_t index) {
|
||||
inline VariantSlot* arrayGetSlot(const JsonArrayData* arr, size_t index) {
|
||||
if (!arr) return 0;
|
||||
return slotAdvance(arr->head, index);
|
||||
}
|
||||
|
||||
inline JsonVariantData* arrayGet(const JsonArrayData* arr, size_t index) {
|
||||
Slot* slot = arrayGetSlot(arr, index);
|
||||
VariantSlot* slot = arrayGetSlot(arr, index);
|
||||
return slot ? &slot->value : 0;
|
||||
}
|
||||
|
||||
inline void arrayRemove(JsonArrayData* arr, Slot* slot) {
|
||||
inline void arrayRemove(JsonArrayData* arr, VariantSlot* slot,
|
||||
MemoryPool* pool) {
|
||||
if (!arr || !slot) return;
|
||||
|
||||
if (slot->prev)
|
||||
@ -52,10 +54,12 @@ inline void arrayRemove(JsonArrayData* arr, Slot* slot) {
|
||||
slot->next->prev = slot->prev;
|
||||
else
|
||||
arr->tail = slot->prev;
|
||||
|
||||
slotFree(slot, pool);
|
||||
}
|
||||
|
||||
inline void arrayRemove(JsonArrayData* arr, size_t index) {
|
||||
arrayRemove(arr, arrayGetSlot(arr, index));
|
||||
inline void arrayRemove(JsonArrayData* arr, size_t index, MemoryPool* pool) {
|
||||
arrayRemove(arr, arrayGetSlot(arr, index), pool);
|
||||
}
|
||||
|
||||
inline void arrayClear(JsonArrayData* arr) {
|
||||
@ -70,7 +74,7 @@ inline bool arrayCopy(JsonArrayData* dst, const JsonArrayData* src,
|
||||
MemoryPool* pool) {
|
||||
if (!dst || !src) return false;
|
||||
arrayClear(dst);
|
||||
for (Slot* s = src->head; s; s = s->next) {
|
||||
for (VariantSlot* s = src->head; s; s = s->next) {
|
||||
if (!variantCopy(arrayAdd(dst, pool), &s->value, pool)) return false;
|
||||
}
|
||||
return true;
|
||||
@ -81,8 +85,8 @@ bool variantEquals(const JsonVariantData*, const JsonVariantData*);
|
||||
inline bool arrayEquals(const JsonArrayData* a1, const JsonArrayData* a2) {
|
||||
if (a1 == a2) return true;
|
||||
if (!a1 || !a2) return false;
|
||||
Slot* s1 = a1->head;
|
||||
Slot* s2 = a2->head;
|
||||
VariantSlot* s1 = a1->head;
|
||||
VariantSlot* s2 = a2->head;
|
||||
for (;;) {
|
||||
if (s1 == s2) return true;
|
||||
if (!s1 || !s2) return false;
|
||||
@ -96,4 +100,15 @@ inline size_t arraySize(const JsonArrayData* arr) {
|
||||
if (!arr) return 0;
|
||||
return slotSize(arr->head);
|
||||
}
|
||||
|
||||
inline void arrayFree(JsonArrayData* arr, MemoryPool* pool) {
|
||||
ARDUINOJSON_ASSERT(arr);
|
||||
|
||||
VariantSlot* cur = arr->head;
|
||||
while (cur) {
|
||||
VariantSlot* next = cur->next;
|
||||
slotFree(cur, pool);
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -26,13 +26,13 @@ enum JsonVariantType {
|
||||
};
|
||||
|
||||
struct JsonObjectData {
|
||||
struct Slot *head;
|
||||
struct Slot *tail;
|
||||
struct VariantSlot *head;
|
||||
struct VariantSlot *tail;
|
||||
};
|
||||
|
||||
struct JsonArrayData {
|
||||
struct Slot *head;
|
||||
struct Slot *tail;
|
||||
struct VariantSlot *head;
|
||||
struct VariantSlot *tail;
|
||||
};
|
||||
|
||||
struct RawData {
|
||||
@ -48,6 +48,8 @@ union JsonVariantContent {
|
||||
JsonArrayData asArray;
|
||||
JsonObjectData asObject;
|
||||
const char *asString;
|
||||
struct StringSlot *asOwnedString;
|
||||
struct StringSlot *asOwnedRaw;
|
||||
struct {
|
||||
const char *data;
|
||||
size_t size;
|
||||
@ -56,7 +58,7 @@ union JsonVariantContent {
|
||||
|
||||
// this struct must be a POD type to prevent error calling offsetof on clang
|
||||
struct JsonVariantData {
|
||||
bool keyIsStatic : 1;
|
||||
bool keyIsOwned : 1;
|
||||
JsonVariantType type : 7;
|
||||
JsonVariantContent content;
|
||||
};
|
||||
|
@ -4,17 +4,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Memory/MemoryPool.hpp"
|
||||
#include "JsonVariantData.hpp"
|
||||
#include "SlotFunctions.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TKey>
|
||||
inline Slot* objectFindSlot(const JsonObjectData* obj, TKey key) {
|
||||
inline VariantSlot* objectFindSlot(const JsonObjectData* obj, TKey key) {
|
||||
if (!obj) return 0;
|
||||
Slot* slot = obj->head;
|
||||
VariantSlot* slot = obj->head;
|
||||
while (slot) {
|
||||
if (key.equals(slot->key)) break;
|
||||
if (key.equals(slotGetKey(slot))) break;
|
||||
slot = slot->next;
|
||||
}
|
||||
return slot;
|
||||
@ -28,10 +29,11 @@ inline bool objectContainsKey(const JsonObjectData* obj, const TKey& key) {
|
||||
template <typename TKey>
|
||||
inline JsonVariantData* objectAdd(JsonObjectData* obj, TKey key,
|
||||
MemoryPool* pool) {
|
||||
Slot* slot = new (pool) Slot();
|
||||
VariantSlot* slot = pool->allocVariant();
|
||||
if (!slot) return 0;
|
||||
|
||||
slot->next = 0;
|
||||
slot->value.type = JSON_NULL;
|
||||
|
||||
if (obj->tail) {
|
||||
slot->prev = obj->tail;
|
||||
@ -56,7 +58,7 @@ inline JsonVariantData* objectSet(JsonObjectData* obj, TKey key,
|
||||
if (key.isNull()) return 0;
|
||||
|
||||
// search a matching key
|
||||
Slot* slot = objectFindSlot(obj, key);
|
||||
VariantSlot* slot = objectFindSlot(obj, key);
|
||||
if (slot) return &slot->value;
|
||||
|
||||
return objectAdd(obj, key, pool);
|
||||
@ -64,7 +66,7 @@ inline JsonVariantData* objectSet(JsonObjectData* obj, TKey key,
|
||||
|
||||
template <typename TKey>
|
||||
inline JsonVariantData* objectGet(const JsonObjectData* obj, TKey key) {
|
||||
Slot* slot = objectFindSlot(obj, key);
|
||||
VariantSlot* slot = objectFindSlot(obj, key);
|
||||
return slot ? &slot->value : 0;
|
||||
}
|
||||
|
||||
@ -74,7 +76,8 @@ inline void objectClear(JsonObjectData* obj) {
|
||||
obj->tail = 0;
|
||||
}
|
||||
|
||||
inline void objectRemove(JsonObjectData* obj, Slot* slot) {
|
||||
inline void objectRemove(JsonObjectData* obj, VariantSlot* slot,
|
||||
MemoryPool* pool) {
|
||||
if (!obj) return;
|
||||
if (!slot) return;
|
||||
if (slot->prev)
|
||||
@ -85,6 +88,8 @@ inline void objectRemove(JsonObjectData* obj, Slot* slot) {
|
||||
slot->next->prev = slot->prev;
|
||||
else
|
||||
obj->tail = slot->prev;
|
||||
|
||||
slotFree(slot, pool);
|
||||
}
|
||||
|
||||
inline size_t objectSize(const JsonObjectData* obj) {
|
||||
@ -98,12 +103,12 @@ inline bool objectCopy(JsonObjectData* dst, const JsonObjectData* src,
|
||||
MemoryPool* pool) {
|
||||
if (!dst || !src) return false;
|
||||
objectClear(dst);
|
||||
for (Slot* s = src->head; s; s = s->next) {
|
||||
for (VariantSlot* s = src->head; s; s = s->next) {
|
||||
JsonVariantData* var;
|
||||
if (s->value.keyIsStatic)
|
||||
var = objectAdd(dst, ZeroTerminatedRamStringConst(s->key), pool);
|
||||
if (s->value.keyIsOwned)
|
||||
var = objectAdd(dst, ZeroTerminatedRamString(s->ownedKey->value), pool);
|
||||
else
|
||||
var = objectAdd(dst, ZeroTerminatedRamString(s->key), pool);
|
||||
var = objectAdd(dst, ZeroTerminatedRamStringConst(s->linkedKey), pool);
|
||||
if (!variantCopy(var, &s->value, pool)) return false;
|
||||
}
|
||||
return true;
|
||||
@ -113,9 +118,9 @@ inline bool objectEquals(const JsonObjectData* o1, const JsonObjectData* o2) {
|
||||
if (o1 == o2) return true;
|
||||
if (!o1 || !o2) return false;
|
||||
|
||||
for (Slot* s = o1->head; s; s = s->next) {
|
||||
for (VariantSlot* s = o1->head; s; s = s->next) {
|
||||
JsonVariantData* v1 = &s->value;
|
||||
JsonVariantData* v2 = objectGet(o2, makeString(s->key));
|
||||
JsonVariantData* v2 = objectGet(o2, makeString(slotGetKey(s)));
|
||||
if (!variantEquals(v1, v2)) return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -4,56 +4,74 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Memory/MemoryPool.hpp"
|
||||
#include "../Polyfills/assert.hpp"
|
||||
#include "../Strings/StringTypes.hpp"
|
||||
#include "JsonVariantData.hpp"
|
||||
#include "Slot.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TKey>
|
||||
inline bool slotSetKey(Slot* slot, TKey key, MemoryPool* pool) {
|
||||
const char* dup = key.save(pool);
|
||||
if (!dup) return false;
|
||||
slot->key = dup;
|
||||
slot->value.keyIsStatic = false;
|
||||
inline bool slotSetKey(VariantSlot* var, TKey key, MemoryPool* pool) {
|
||||
StringSlot* slot = key.save(pool);
|
||||
if (!slot) return false;
|
||||
var->ownedKey = slot;
|
||||
var->value.keyIsOwned = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool slotSetKey(Slot* slot, ZeroTerminatedRamStringConst key,
|
||||
MemoryPool* pool) {
|
||||
slot->key = key.save(pool);
|
||||
slot->value.keyIsStatic = true;
|
||||
inline bool slotSetKey(VariantSlot* var, ZeroTerminatedRamStringConst key,
|
||||
MemoryPool*) {
|
||||
var->linkedKey = key.c_str();
|
||||
var->value.keyIsOwned = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool slotSetKey(Slot* slot, StringInMemoryPool key, MemoryPool* pool) {
|
||||
slot->key = key.save(pool);
|
||||
slot->value.keyIsStatic = false;
|
||||
inline bool slotSetKey(VariantSlot* var, StringInMemoryPool key, MemoryPool*) {
|
||||
var->ownedKey = key.slot();
|
||||
var->value.keyIsOwned = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline const Slot* slotAdvance(const Slot* slot, size_t distance) {
|
||||
while (distance && slot) {
|
||||
slot = slot->next;
|
||||
inline const char* slotGetKey(const VariantSlot* var) {
|
||||
return var->value.keyIsOwned ? var->ownedKey->value : var->linkedKey;
|
||||
}
|
||||
|
||||
inline const VariantSlot* slotAdvance(const VariantSlot* var, size_t distance) {
|
||||
while (distance && var) {
|
||||
var = var->next;
|
||||
distance--;
|
||||
}
|
||||
return slot;
|
||||
return var;
|
||||
}
|
||||
|
||||
inline Slot* slotAdvance(Slot* slot, size_t distance) {
|
||||
while (distance && slot) {
|
||||
slot = slot->next;
|
||||
inline VariantSlot* slotAdvance(VariantSlot* var, size_t distance) {
|
||||
while (distance && var) {
|
||||
var = var->next;
|
||||
distance--;
|
||||
}
|
||||
return slot;
|
||||
return var;
|
||||
}
|
||||
|
||||
inline size_t slotSize(const Slot* slot) {
|
||||
inline size_t slotSize(const VariantSlot* var) {
|
||||
size_t n = 0;
|
||||
while (slot) {
|
||||
while (var) {
|
||||
n++;
|
||||
slot = slot->next;
|
||||
var = var->next;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void variantFree(JsonVariantData* var, MemoryPool* pool);
|
||||
|
||||
inline void slotFree(VariantSlot* var, MemoryPool* pool) {
|
||||
ARDUINOJSON_ASSERT(var != 0);
|
||||
ARDUINOJSON_ASSERT(pool != 0);
|
||||
|
||||
variantFree(&var->value, pool);
|
||||
|
||||
if (var->value.keyIsOwned) pool->freeString(var->ownedKey);
|
||||
|
||||
pool->freeVariant(var);
|
||||
}
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -10,10 +10,27 @@
|
||||
#include "ArrayFunctions.hpp"
|
||||
#include "JsonVariantData.hpp"
|
||||
#include "ObjectFunctions.hpp"
|
||||
#include "Slot.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
inline void variantFree(JsonVariantData* var, MemoryPool* pool) {
|
||||
ARDUINOJSON_ASSERT(var != 0);
|
||||
ARDUINOJSON_ASSERT(pool != 0);
|
||||
|
||||
switch (var->type) {
|
||||
case JSON_ARRAY:
|
||||
case JSON_OBJECT:
|
||||
arrayFree(&var->content.asArray, pool);
|
||||
break;
|
||||
case JSON_OWNED_STRING:
|
||||
case JSON_OWNED_RAW:
|
||||
pool->freeString(var->content.asOwnedString);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T variantAsIntegral(const JsonVariantData* var) {
|
||||
if (!var) return 0;
|
||||
@ -24,8 +41,9 @@ inline T variantAsIntegral(const JsonVariantData* var) {
|
||||
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_OWNED_STRING:
|
||||
return parseInteger<T>(var->content.asOwnedString->value);
|
||||
case JSON_FLOAT:
|
||||
return T(var->content.asFloat);
|
||||
default:
|
||||
@ -48,8 +66,9 @@ inline T variantAsFloat(const JsonVariantData* var) {
|
||||
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_OWNED_STRING:
|
||||
return parseFloat<T>(var->content.asOwnedString->value);
|
||||
case JSON_FLOAT:
|
||||
return static_cast<T>(var->content.asFloat);
|
||||
default:
|
||||
@ -59,11 +78,14 @@ inline T variantAsFloat(const JsonVariantData* var) {
|
||||
|
||||
inline const char* variantAsString(const JsonVariantData* var) {
|
||||
if (!var) return 0;
|
||||
if (var &&
|
||||
(var->type == JSON_LINKED_STRING || var->type == JSON_OWNED_STRING))
|
||||
return var->content.asString;
|
||||
else
|
||||
return 0;
|
||||
switch (var->type) {
|
||||
case JSON_LINKED_STRING:
|
||||
return var->content.asString;
|
||||
case JSON_OWNED_STRING:
|
||||
return var->content.asOwnedString->value;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline JsonArrayData* variantAsArray(JsonVariantData* var) {
|
||||
@ -94,23 +116,30 @@ inline const JsonObjectData* variantAsObject(const JsonVariantData* var) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline bool variantSetBoolean(JsonVariantData* var, bool value) {
|
||||
inline bool variantSetBoolean(JsonVariantData* var, bool value,
|
||||
MemoryPool* pool) {
|
||||
if (!var) return false;
|
||||
variantFree(var, pool);
|
||||
var->type = JSON_BOOLEAN;
|
||||
var->content.asInteger = static_cast<JsonUInt>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool variantSetFloat(JsonVariantData* var, JsonFloat value) {
|
||||
inline bool variantSetFloat(JsonVariantData* var, JsonFloat value,
|
||||
MemoryPool* pool) {
|
||||
if (!var) return false;
|
||||
variantFree(var, pool);
|
||||
var->type = JSON_FLOAT;
|
||||
var->content.asFloat = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool variantSetSignedInteger(JsonVariantData* var, T value) {
|
||||
inline bool variantSetSignedInteger(JsonVariantData* var, T value,
|
||||
MemoryPool* pool) {
|
||||
if (!var) return false;
|
||||
variantFree(var, pool);
|
||||
|
||||
if (value >= 0) {
|
||||
var->type = JSON_POSITIVE_INTEGER;
|
||||
var->content.asInteger = static_cast<JsonUInt>(value);
|
||||
@ -121,16 +150,20 @@ inline bool variantSetSignedInteger(JsonVariantData* var, T value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool variantSetSignedInteger(JsonVariantData* var, JsonUInt value) {
|
||||
inline bool variantSetUnsignedInteger(JsonVariantData* var, JsonUInt value,
|
||||
MemoryPool* pool) {
|
||||
if (!var) return false;
|
||||
variantFree(var, pool);
|
||||
var->type = JSON_POSITIVE_INTEGER;
|
||||
var->content.asInteger = static_cast<JsonUInt>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool variantSetLinkedRaw(JsonVariantData* var,
|
||||
SerializedValue<const char*> value) {
|
||||
SerializedValue<const char*> value,
|
||||
MemoryPool* pool) {
|
||||
if (!var) return false;
|
||||
variantFree(var, pool);
|
||||
var->type = JSON_LINKED_RAW;
|
||||
var->content.asRaw.data = value.data();
|
||||
var->content.asRaw.size = value.size();
|
||||
@ -141,11 +174,11 @@ template <typename T>
|
||||
inline bool variantSetOwnedRaw(JsonVariantData* var, SerializedValue<T> value,
|
||||
MemoryPool* pool) {
|
||||
if (!var) return false;
|
||||
const char* dup = makeString(value.data(), value.size()).save(pool);
|
||||
if (dup) {
|
||||
variantFree(var, pool);
|
||||
StringSlot* slot = makeString(value.data(), value.size()).save(pool);
|
||||
if (slot) {
|
||||
var->type = JSON_OWNED_RAW;
|
||||
var->content.asRaw.data = dup;
|
||||
var->content.asRaw.size = value.size();
|
||||
var->content.asOwnedRaw = slot;
|
||||
return true;
|
||||
} else {
|
||||
var->type = JSON_NULL;
|
||||
@ -156,10 +189,11 @@ inline bool variantSetOwnedRaw(JsonVariantData* var, SerializedValue<T> value,
|
||||
template <typename T>
|
||||
inline bool variantSetString(JsonVariantData* var, T value, MemoryPool* pool) {
|
||||
if (!var) return false;
|
||||
const char* dup = value.save(pool);
|
||||
if (dup) {
|
||||
variantFree(var, pool);
|
||||
StringSlot* slot = value.save(pool);
|
||||
if (slot) {
|
||||
var->type = JSON_OWNED_STRING;
|
||||
var->content.asString = dup;
|
||||
var->content.asOwnedString = slot;
|
||||
return true;
|
||||
} else {
|
||||
var->type = JSON_NULL;
|
||||
@ -167,32 +201,42 @@ inline bool variantSetString(JsonVariantData* var, T value, MemoryPool* pool) {
|
||||
}
|
||||
}
|
||||
|
||||
inline bool variantSetString(JsonVariantData* var, const char* value) {
|
||||
inline bool variantSetOwnedString(JsonVariantData* var, StringSlot* slot,
|
||||
MemoryPool* pool) {
|
||||
if (!var) return false;
|
||||
var->type = JSON_LINKED_STRING;
|
||||
var->content.asString = value;
|
||||
variantFree(var, pool);
|
||||
var->type = JSON_OWNED_STRING;
|
||||
var->content.asOwnedString = slot;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool variantSetString(JsonVariantData* var, const char* value,
|
||||
MemoryPool* pool) {
|
||||
return variantSetString(var, makeString(const_cast<char*>(value)), pool);
|
||||
if (!var) return false;
|
||||
variantFree(var, pool);
|
||||
var->type = JSON_LINKED_STRING;
|
||||
var->content.asString = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void variantSetNull(JsonVariantData* var) {
|
||||
if (var) var->type = JSON_NULL;
|
||||
inline void variantSetNull(JsonVariantData* var, MemoryPool* pool) {
|
||||
if (!var) return;
|
||||
variantFree(var, pool);
|
||||
var->type = JSON_NULL;
|
||||
}
|
||||
|
||||
inline JsonArrayData* variantToArray(JsonVariantData* var) {
|
||||
inline JsonArrayData* variantToArray(JsonVariantData* var, MemoryPool* pool) {
|
||||
if (!var) return 0;
|
||||
variantFree(var, pool);
|
||||
var->type = JSON_ARRAY;
|
||||
var->content.asArray.head = 0;
|
||||
var->content.asArray.tail = 0;
|
||||
return &var->content.asArray;
|
||||
}
|
||||
|
||||
inline JsonObjectData* variantToObject(JsonVariantData* var) {
|
||||
inline JsonObjectData* variantToObject(JsonVariantData* var, MemoryPool* pool) {
|
||||
if (!var) return 0;
|
||||
variantFree(var, pool);
|
||||
var->type = JSON_OBJECT;
|
||||
var->content.asObject.head = 0;
|
||||
var->content.asObject.tail = 0;
|
||||
@ -203,24 +247,29 @@ inline bool variantCopy(JsonVariantData* dst, const JsonVariantData* src,
|
||||
MemoryPool* pool) {
|
||||
if (!dst) return false;
|
||||
if (!src) {
|
||||
variantFree(dst, pool);
|
||||
dst->type = JSON_NULL;
|
||||
return true;
|
||||
}
|
||||
switch (src->type) {
|
||||
case JSON_ARRAY:
|
||||
return arrayCopy(variantToArray(dst), &src->content.asArray, pool);
|
||||
return arrayCopy(variantToArray(dst, pool), &src->content.asArray, pool);
|
||||
case JSON_OBJECT:
|
||||
return objectCopy(variantToObject(dst), &src->content.asObject, pool);
|
||||
return objectCopy(variantToObject(dst, pool), &src->content.asObject,
|
||||
pool);
|
||||
case JSON_OWNED_STRING:
|
||||
return variantSetString(dst, src->content.asString, pool);
|
||||
return variantSetString(
|
||||
dst, makeString(src->content.asOwnedString->value), pool);
|
||||
case JSON_OWNED_RAW:
|
||||
return variantSetOwnedRaw(
|
||||
dst,
|
||||
serialized(const_cast<char*>(src->content.asRaw.data),
|
||||
src->content.asRaw.size),
|
||||
pool);
|
||||
return variantSetOwnedRaw(dst,
|
||||
serialized(src->content.asOwnedRaw->value,
|
||||
src->content.asOwnedRaw->size),
|
||||
pool);
|
||||
default:
|
||||
*dst = *src;
|
||||
variantFree(dst, pool);
|
||||
// caution: don't override keyIsOwned
|
||||
dst->type = src->type;
|
||||
dst->content = src->content;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -260,11 +309,16 @@ inline bool variantEquals(const JsonVariantData* a, const JsonVariantData* b) {
|
||||
|
||||
switch (a->type) {
|
||||
case JSON_LINKED_RAW:
|
||||
case JSON_OWNED_RAW:
|
||||
case JSON_LINKED_STRING:
|
||||
case JSON_OWNED_STRING:
|
||||
return !strcmp(a->content.asString, b->content.asString);
|
||||
|
||||
case JSON_OWNED_RAW:
|
||||
case JSON_OWNED_STRING:
|
||||
return a->content.asOwnedString->size == b->content.asOwnedString->size &&
|
||||
!memcmp(a->content.asOwnedString->value,
|
||||
b->content.asOwnedString->value,
|
||||
a->content.asOwnedString->size);
|
||||
|
||||
case JSON_BOOLEAN:
|
||||
case JSON_POSITIVE_INTEGER:
|
||||
case JSON_NEGATIVE_INTEGER:
|
||||
|
@ -11,6 +11,10 @@
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class DeserializationError {
|
||||
// safe bool idiom
|
||||
typedef void (DeserializationError::*bool_type)() const;
|
||||
void safeBoolHelper() const {}
|
||||
|
||||
public:
|
||||
enum Code {
|
||||
Ok,
|
||||
@ -22,26 +26,52 @@ class DeserializationError {
|
||||
};
|
||||
|
||||
DeserializationError() {}
|
||||
DeserializationError(Code code) : _code(code) {}
|
||||
DeserializationError(Code c) : _code(c) {}
|
||||
|
||||
friend bool operator==(const DeserializationError& err, Code code) {
|
||||
return err._code == code;
|
||||
// Compare with DeserializationError
|
||||
friend bool operator==(const DeserializationError& lhs,
|
||||
const DeserializationError& rhs) {
|
||||
return lhs._code == rhs._code;
|
||||
}
|
||||
friend bool operator!=(const DeserializationError& lhs,
|
||||
const DeserializationError& rhs) {
|
||||
return lhs._code != rhs._code;
|
||||
}
|
||||
|
||||
friend bool operator==(Code code, const DeserializationError& err) {
|
||||
return err._code == code;
|
||||
// Compare with Code
|
||||
friend bool operator==(const DeserializationError& lhs, Code rhs) {
|
||||
return lhs._code == rhs;
|
||||
}
|
||||
friend bool operator==(Code lhs, const DeserializationError& rhs) {
|
||||
return lhs == rhs._code;
|
||||
}
|
||||
friend bool operator!=(const DeserializationError& lhs, Code rhs) {
|
||||
return lhs._code != rhs;
|
||||
}
|
||||
friend bool operator!=(Code lhs, const DeserializationError& rhs) {
|
||||
return lhs != rhs._code;
|
||||
}
|
||||
|
||||
friend bool operator!=(const DeserializationError& err, Code code) {
|
||||
return err._code != code;
|
||||
// Behaves like a bool
|
||||
operator bool_type() const {
|
||||
return _code != Ok ? &DeserializationError::safeBoolHelper : 0;
|
||||
}
|
||||
friend bool operator==(bool value, const DeserializationError& err) {
|
||||
return static_cast<bool>(err) == value;
|
||||
}
|
||||
friend bool operator==(const DeserializationError& err, bool value) {
|
||||
return static_cast<bool>(err) == value;
|
||||
}
|
||||
friend bool operator!=(bool value, const DeserializationError& err) {
|
||||
return static_cast<bool>(err) != value;
|
||||
}
|
||||
friend bool operator!=(const DeserializationError& err, bool value) {
|
||||
return static_cast<bool>(err) != value;
|
||||
}
|
||||
|
||||
friend bool operator!=(Code code, const DeserializationError& err) {
|
||||
return err._code != code;
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return _code != Ok;
|
||||
// Returns internal enum, useful for switch statement
|
||||
Code code() const {
|
||||
return _code;
|
||||
}
|
||||
|
||||
const char* c_str() const {
|
||||
|
@ -18,6 +18,7 @@ template <typename TReader, typename TStringStorage>
|
||||
class JsonDeserializer {
|
||||
typedef typename remove_reference<TStringStorage>::type::StringBuilder
|
||||
StringBuilder;
|
||||
typedef typename StringBuilder::StringType StringType;
|
||||
|
||||
public:
|
||||
JsonDeserializer(MemoryPool &memoryPool, TReader reader,
|
||||
@ -124,7 +125,7 @@ class JsonDeserializer {
|
||||
// Read each key value pair
|
||||
for (;;) {
|
||||
// Parse key
|
||||
StringInMemoryPool key;
|
||||
StringType key;
|
||||
err = parseKey(key);
|
||||
if (err) return err;
|
||||
|
||||
@ -165,7 +166,7 @@ class JsonDeserializer {
|
||||
}
|
||||
}
|
||||
|
||||
DeserializationError parseKey(StringInMemoryPool &key) {
|
||||
DeserializationError parseKey(StringType &key) {
|
||||
if (isQuote(current())) {
|
||||
return parseQuotedString(key);
|
||||
} else {
|
||||
@ -174,15 +175,15 @@ class JsonDeserializer {
|
||||
}
|
||||
|
||||
DeserializationError parseStringValue(JsonVariant variant) {
|
||||
StringInMemoryPool value;
|
||||
StringType value;
|
||||
DeserializationError err = parseQuotedString(value);
|
||||
if (err) return err;
|
||||
variant.set(value);
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
|
||||
DeserializationError parseQuotedString(StringInMemoryPool &result) {
|
||||
StringBuilder str = _stringStorage.startString();
|
||||
DeserializationError parseQuotedString(StringType &result) {
|
||||
StringBuilder builder = _stringStorage.startString();
|
||||
const char stopChar = current();
|
||||
|
||||
move();
|
||||
@ -203,16 +204,16 @@ class JsonDeserializer {
|
||||
move();
|
||||
}
|
||||
|
||||
str.append(c);
|
||||
builder.append(c);
|
||||
}
|
||||
|
||||
result = str.complete();
|
||||
result = builder.complete();
|
||||
if (result.isNull()) return DeserializationError::NoMemory;
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
|
||||
DeserializationError parseNonQuotedString(StringInMemoryPool &result) {
|
||||
StringBuilder str = _stringStorage.startString();
|
||||
DeserializationError parseNonQuotedString(StringType &result) {
|
||||
StringBuilder builder = _stringStorage.startString();
|
||||
|
||||
char c = current();
|
||||
if (c == '\0') return DeserializationError::IncompleteInput;
|
||||
@ -220,14 +221,14 @@ class JsonDeserializer {
|
||||
if (canBeInNonQuotedString(c)) { // no quotes
|
||||
do {
|
||||
move();
|
||||
str.append(c);
|
||||
builder.append(c);
|
||||
c = current();
|
||||
} while (canBeInNonQuotedString(c));
|
||||
} else {
|
||||
return DeserializationError::InvalidInput;
|
||||
}
|
||||
|
||||
result = str.complete();
|
||||
result = builder.complete();
|
||||
if (result.isNull()) return DeserializationError::NoMemory;
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
// Returns the size (in bytes) of an array with n elements.
|
||||
// Can be very handy to determine the size of a StaticMemoryPool.
|
||||
#define JSON_ARRAY_SIZE(NUMBER_OF_ELEMENTS) \
|
||||
((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::Slot))
|
||||
((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::VariantSlot))
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
@ -191,44 +191,18 @@ class JsonArray : public JsonArrayProxy<JsonArrayData>, public Visitable {
|
||||
}
|
||||
|
||||
// Gets the value at the specified index.
|
||||
template <typename T>
|
||||
FORCE_INLINE typename JsonVariantAs<T>::type get(size_t index) const {
|
||||
return get_impl(index).as<T>();
|
||||
}
|
||||
|
||||
// Check the type of the value at specified index.
|
||||
template <typename T>
|
||||
FORCE_INLINE bool is(size_t index) const {
|
||||
return get_impl(index).is<T>();
|
||||
FORCE_INLINE JsonVariant get(size_t index) const {
|
||||
return JsonVariant(_memoryPool, arrayGet(_data, index));
|
||||
}
|
||||
|
||||
// Removes element at specified position.
|
||||
FORCE_INLINE void remove(iterator it) const {
|
||||
arrayRemove(_data, it.internal());
|
||||
arrayRemove(_data, it.internal(), _memoryPool);
|
||||
}
|
||||
|
||||
// Removes element at specified index.
|
||||
FORCE_INLINE void remove(size_t index) const {
|
||||
arrayRemove(_data, index);
|
||||
}
|
||||
|
||||
// Sets the value at specified index.
|
||||
//
|
||||
// bool add(size_t index, const TValue&);
|
||||
// TValue = bool, long, int, short, float, double, serialized, JsonVariant,
|
||||
// std::string, String, JsonArray, JsonObject
|
||||
template <typename T>
|
||||
FORCE_INLINE bool set(size_t index, const T& value) const {
|
||||
if (!_data) return false;
|
||||
return get_impl(index).set(value);
|
||||
}
|
||||
//
|
||||
// bool add(size_t index, TValue);
|
||||
// TValue = char*, const char*, const FlashStringHelper*
|
||||
template <typename T>
|
||||
FORCE_INLINE bool set(size_t index, T* value) const {
|
||||
if (!_data) return false;
|
||||
return get_impl(index).set(value);
|
||||
arrayRemove(_data, index, _memoryPool);
|
||||
}
|
||||
|
||||
template <typename Visitor>
|
||||
@ -242,10 +216,6 @@ class JsonArray : public JsonArrayProxy<JsonArrayData>, public Visitable {
|
||||
return add().set(value);
|
||||
}
|
||||
|
||||
FORCE_INLINE JsonVariant get_impl(size_t index) const {
|
||||
return JsonVariant(_memoryPool, arrayGet(_data, index));
|
||||
}
|
||||
|
||||
MemoryPool* _memoryPool;
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Data/Slot.hpp"
|
||||
#include "Data/SlotFunctions.hpp"
|
||||
#include "JsonVariant.hpp"
|
||||
|
||||
@ -30,7 +29,7 @@ class JsonVariantPtr {
|
||||
class JsonArrayIterator {
|
||||
public:
|
||||
JsonArrayIterator() : _slot(0) {}
|
||||
explicit JsonArrayIterator(MemoryPool *memoryPool, Slot *slot)
|
||||
explicit JsonArrayIterator(MemoryPool *memoryPool, VariantSlot *slot)
|
||||
: _memoryPool(memoryPool), _slot(slot) {}
|
||||
|
||||
JsonVariant operator*() const {
|
||||
@ -58,13 +57,13 @@ class JsonArrayIterator {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Slot *internal() {
|
||||
VariantSlot *internal() {
|
||||
return _slot;
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryPool *_memoryPool;
|
||||
Slot *_slot;
|
||||
VariantSlot *_slot;
|
||||
};
|
||||
|
||||
class JsonVariantConstPtr {
|
||||
@ -86,7 +85,7 @@ class JsonVariantConstPtr {
|
||||
class JsonArrayConstIterator {
|
||||
public:
|
||||
JsonArrayConstIterator() : _slot(0) {}
|
||||
explicit JsonArrayConstIterator(const Slot *slot) : _slot(slot) {}
|
||||
explicit JsonArrayConstIterator(const VariantSlot *slot) : _slot(slot) {}
|
||||
|
||||
JsonVariantConst operator*() const {
|
||||
return JsonVariantConst(&_slot->value);
|
||||
@ -113,11 +112,11 @@ class JsonArrayConstIterator {
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Slot *internal() {
|
||||
const VariantSlot *internal() {
|
||||
return _slot;
|
||||
}
|
||||
|
||||
private:
|
||||
const Slot *_slot;
|
||||
const VariantSlot *_slot;
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -44,22 +44,22 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript>,
|
||||
}
|
||||
|
||||
FORCE_INLINE bool isNull() const {
|
||||
return _index >= _array.size();
|
||||
return get_impl().isNull();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
FORCE_INLINE typename JsonVariantAs<T>::type as() const {
|
||||
return _array.get<T>(_index);
|
||||
return get_impl().as<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
FORCE_INLINE bool is() const {
|
||||
return _array.is<T>(_index);
|
||||
return get_impl().is<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
FORCE_INLINE typename JsonVariantTo<T>::type to() const {
|
||||
return _array.get<JsonVariant>(_index).to<T>();
|
||||
return get_impl().to<T>();
|
||||
}
|
||||
|
||||
// Replaces the value
|
||||
@ -89,8 +89,8 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript>,
|
||||
}
|
||||
|
||||
private:
|
||||
JsonVariant get_impl() const {
|
||||
return _array.get<JsonVariant>(_index);
|
||||
FORCE_INLINE JsonVariant get_impl() const {
|
||||
return _array.get(_index);
|
||||
}
|
||||
|
||||
JsonArray _array;
|
||||
|
@ -53,7 +53,7 @@ class JsonDocument : public Visitable {
|
||||
|
||||
template <typename T>
|
||||
typename JsonVariantTo<T>::type to() {
|
||||
_memoryPool.clear();
|
||||
clear();
|
||||
return getVariant().template to<T>();
|
||||
}
|
||||
|
||||
|
@ -8,22 +8,18 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class JsonKey {
|
||||
public:
|
||||
JsonKey(const Slot* slot) : _slot(slot) {}
|
||||
JsonKey(const VariantSlot* slot) : _slot(slot) {}
|
||||
|
||||
operator const char*() const {
|
||||
return c_str();
|
||||
}
|
||||
|
||||
const char* c_str() const {
|
||||
return _slot ? _slot->key : 0;
|
||||
return _slot ? slotGetKey(_slot) : 0;
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return _slot == 0 || _slot->key == 0;
|
||||
}
|
||||
|
||||
bool isStatic() const {
|
||||
return _slot ? _slot->value.keyIsStatic : true;
|
||||
return _slot == 0 || _slot->linkedKey == 0;
|
||||
}
|
||||
|
||||
friend bool operator==(JsonKey lhs, const char* rhs) {
|
||||
@ -32,6 +28,6 @@ class JsonKey {
|
||||
}
|
||||
|
||||
private:
|
||||
const Slot* _slot;
|
||||
const VariantSlot* _slot;
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -10,7 +10,7 @@
|
||||
// Returns the size (in bytes) of an object with n elements.
|
||||
// Can be very handy to determine the size of a StaticMemoryPool.
|
||||
#define JSON_OBJECT_SIZE(NUMBER_OF_ELEMENTS) \
|
||||
((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::Slot))
|
||||
((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::VariantSlot))
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
@ -80,18 +80,18 @@ class JsonObjectConst : public JsonObjectProxy<const JsonObjectData>,
|
||||
// TKey = const std::string&, const String&
|
||||
// TValue = bool, char, long, int, short, float, double,
|
||||
// std::string, String, JsonArrayConst, JsonObjectConst
|
||||
template <typename TValue, typename TKey>
|
||||
FORCE_INLINE typename JsonVariantAs<TValue>::type get(const TKey& key) const {
|
||||
return get_impl(makeString(key)).template as<TValue>();
|
||||
template <typename TKey>
|
||||
FORCE_INLINE JsonVariantConst get(const TKey& key) const {
|
||||
return get_impl(makeString(key));
|
||||
}
|
||||
//
|
||||
// TValue get<TValue>(TKey) const;
|
||||
// TKey = char*, const char*, const FlashStringHelper*
|
||||
// TValue = bool, char, long, int, short, float, double,
|
||||
// std::string, String, JsonArrayConst, JsonObjectConst
|
||||
template <typename TValue, typename TKey>
|
||||
FORCE_INLINE typename JsonVariantAs<TValue>::type get(TKey* key) const {
|
||||
return get_impl(makeString(key)).template as<TValue>();
|
||||
template <typename TKey>
|
||||
FORCE_INLINE JsonVariantConst get(TKey* key) const {
|
||||
return get_impl(makeString(key));
|
||||
}
|
||||
|
||||
//
|
||||
@ -191,39 +191,18 @@ class JsonObject : public JsonObjectProxy<JsonObjectData>, public Visitable {
|
||||
// TKey = const std::string&, const String&
|
||||
// TValue = bool, char, long, int, short, float, double,
|
||||
// std::string, String, JsonArray, JsonObject
|
||||
template <typename TValue, typename TKey>
|
||||
FORCE_INLINE typename JsonVariantAs<TValue>::type get(const TKey& key) const {
|
||||
return get_impl(makeString(key)).template as<TValue>();
|
||||
template <typename TKey>
|
||||
FORCE_INLINE JsonVariant get(const TKey& key) const {
|
||||
return get_impl(makeString(key));
|
||||
}
|
||||
//
|
||||
// TValue get<TValue>(TKey) const;
|
||||
// TKey = char*, const char*, const FlashStringHelper*
|
||||
// TValue = bool, char, long, int, short, float, double,
|
||||
// std::string, String, JsonArray, JsonObject
|
||||
template <typename TValue, typename TKey>
|
||||
FORCE_INLINE typename JsonVariantAs<TValue>::type get(TKey* key) const {
|
||||
return get_impl(makeString(key)).template as<TValue>();
|
||||
}
|
||||
|
||||
// Checks the type of the value associated with the specified key.
|
||||
//
|
||||
//
|
||||
// bool is<TValue>(TKey) const;
|
||||
// TKey = const std::string&, const String&
|
||||
// TValue = bool, char, long, int, short, float, double,
|
||||
// std::string, String, JsonArray, JsonObject
|
||||
template <typename TValue, typename TKey>
|
||||
FORCE_INLINE bool is(const TKey& key) const {
|
||||
return get_impl(makeString(key)).template is<TValue>();
|
||||
}
|
||||
//
|
||||
// bool is<TValue>(TKey) const;
|
||||
// TKey = char*, const char*, const FlashStringHelper*
|
||||
// TValue = bool, char, long, int, short, float, double,
|
||||
// std::string, String, JsonArray, JsonObject
|
||||
template <typename TValue, typename TKey>
|
||||
FORCE_INLINE bool is(TKey* key) const {
|
||||
return get_impl(makeString(key)).template is<TValue>();
|
||||
template <typename TKey>
|
||||
FORCE_INLINE JsonVariant get(TKey* key) const {
|
||||
return get_impl(makeString(key));
|
||||
}
|
||||
|
||||
// Gets or sets the value associated with the specified key.
|
||||
@ -248,7 +227,7 @@ class JsonObject : public JsonObjectProxy<JsonObjectData>, public Visitable {
|
||||
}
|
||||
|
||||
FORCE_INLINE void remove(iterator it) const {
|
||||
objectRemove(_data, it.internal());
|
||||
objectRemove(_data, it.internal(), _memoryPool);
|
||||
}
|
||||
|
||||
// Removes the specified key and the associated value.
|
||||
@ -267,42 +246,6 @@ class JsonObject : public JsonObjectProxy<JsonObjectData>, public Visitable {
|
||||
remove_impl(makeString(key));
|
||||
}
|
||||
|
||||
// Sets the specified key with the specified value.
|
||||
//
|
||||
// bool set(TKey, TValue);
|
||||
// TKey = const std::string&, const String&
|
||||
// TValue = bool, long, int, short, float, double, serialized, JsonVariant,
|
||||
// std::string, String, JsonArray, JsonObject
|
||||
template <typename TValue, typename TKey>
|
||||
FORCE_INLINE bool set(const TKey& key, const TValue& value) const {
|
||||
return set(key).set(value);
|
||||
}
|
||||
//
|
||||
// bool set(TKey, TValue);
|
||||
// TKey = const std::string&, const String&
|
||||
// TValue = char*, const char*, const FlashStringHelper*
|
||||
template <typename TValue, typename TKey>
|
||||
FORCE_INLINE bool set(const TKey& key, TValue* value) const {
|
||||
return set(key).set(value);
|
||||
}
|
||||
//
|
||||
// bool set(TKey, const TValue&);
|
||||
// TKey = char*, const char*, const FlashStringHelper*
|
||||
// TValue = bool, long, int, short, float, double, serialized, JsonVariant,
|
||||
// std::string, String, JsonArray, JsonObject
|
||||
template <typename TValue, typename TKey>
|
||||
FORCE_INLINE bool set(TKey* key, const TValue& value) const {
|
||||
return set(key).set(value);
|
||||
}
|
||||
//
|
||||
// bool set(TKey, TValue);
|
||||
// TKey = char*, const char*, const FlashStringHelper*
|
||||
// TValue = char*, const char*, const FlashStringHelper*
|
||||
template <typename TValue, typename TKey>
|
||||
FORCE_INLINE bool set(TKey* key, TValue* value) const {
|
||||
return set(key).set(value);
|
||||
}
|
||||
|
||||
template <typename TKey>
|
||||
FORCE_INLINE JsonVariant set(TKey* key) const {
|
||||
return set_impl(makeString(key));
|
||||
@ -313,7 +256,11 @@ class JsonObject : public JsonObjectProxy<JsonObjectData>, public Visitable {
|
||||
return set_impl(makeString(key));
|
||||
}
|
||||
|
||||
FORCE_INLINE JsonVariant set(const StringInMemoryPool& key) const {
|
||||
FORCE_INLINE JsonVariant set(StringInMemoryPool key) const {
|
||||
return set_impl(key);
|
||||
}
|
||||
|
||||
FORCE_INLINE JsonVariant set(ZeroTerminatedRamStringConst key) const {
|
||||
return set_impl(key);
|
||||
}
|
||||
|
||||
@ -335,7 +282,7 @@ class JsonObject : public JsonObjectProxy<JsonObjectData>, public Visitable {
|
||||
|
||||
template <typename TStringRef>
|
||||
FORCE_INLINE void remove_impl(TStringRef key) const {
|
||||
objectRemove(_data, objectFindSlot(_data, key));
|
||||
objectRemove(_data, objectFindSlot(_data, key), _memoryPool);
|
||||
}
|
||||
|
||||
MemoryPool* _memoryPool;
|
||||
|
@ -11,7 +11,8 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class JsonPairPtr {
|
||||
public:
|
||||
JsonPairPtr(MemoryPool *memoryPool, Slot *slot) : _pair(memoryPool, slot) {}
|
||||
JsonPairPtr(MemoryPool *memoryPool, VariantSlot *slot)
|
||||
: _pair(memoryPool, slot) {}
|
||||
|
||||
const JsonPair *operator->() const {
|
||||
return &_pair;
|
||||
@ -29,7 +30,7 @@ class JsonObjectIterator {
|
||||
public:
|
||||
JsonObjectIterator() : _slot(0) {}
|
||||
|
||||
explicit JsonObjectIterator(MemoryPool *memoryPool, Slot *slot)
|
||||
explicit JsonObjectIterator(MemoryPool *memoryPool, VariantSlot *slot)
|
||||
: _memoryPool(memoryPool), _slot(slot) {}
|
||||
|
||||
JsonPair operator*() const {
|
||||
@ -57,18 +58,18 @@ class JsonObjectIterator {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Slot *internal() {
|
||||
VariantSlot *internal() {
|
||||
return _slot;
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryPool *_memoryPool;
|
||||
Slot *_slot;
|
||||
VariantSlot *_slot;
|
||||
};
|
||||
|
||||
class JsonPairConstPtr {
|
||||
public:
|
||||
JsonPairConstPtr(const Slot *slot) : _pair(slot) {}
|
||||
JsonPairConstPtr(const VariantSlot *slot) : _pair(slot) {}
|
||||
|
||||
const JsonPairConst *operator->() const {
|
||||
return &_pair;
|
||||
@ -86,7 +87,7 @@ class JsonObjectConstIterator {
|
||||
public:
|
||||
JsonObjectConstIterator() : _slot(0) {}
|
||||
|
||||
explicit JsonObjectConstIterator(const Slot *slot) : _slot(slot) {}
|
||||
explicit JsonObjectConstIterator(const VariantSlot *slot) : _slot(slot) {}
|
||||
|
||||
JsonPairConst operator*() const {
|
||||
return JsonPairConst(_slot);
|
||||
@ -113,11 +114,11 @@ class JsonObjectConstIterator {
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Slot *internal() {
|
||||
const VariantSlot *internal() {
|
||||
return _slot;
|
||||
}
|
||||
|
||||
private:
|
||||
const Slot *_slot;
|
||||
const VariantSlot *_slot;
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -98,11 +98,11 @@ class JsonObjectSubscript
|
||||
}
|
||||
|
||||
private:
|
||||
JsonVariant get_impl() const {
|
||||
return _object.get<JsonVariant>(_key);
|
||||
FORCE_INLINE JsonVariant get_impl() const {
|
||||
return _object.get(_key);
|
||||
}
|
||||
|
||||
JsonVariant set_impl() const {
|
||||
FORCE_INLINE JsonVariant set_impl() const {
|
||||
return _object.set(_key);
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
// A key value pair for JsonObjectData.
|
||||
class JsonPair {
|
||||
public:
|
||||
JsonPair(MemoryPool* memoryPool, Slot* slot) : _key(slot) {
|
||||
JsonPair(MemoryPool* memoryPool, VariantSlot* slot) : _key(slot) {
|
||||
if (slot) {
|
||||
_value = JsonVariant(memoryPool, &slot->value);
|
||||
}
|
||||
@ -32,7 +32,7 @@ class JsonPair {
|
||||
|
||||
class JsonPairConst {
|
||||
public:
|
||||
JsonPairConst(const Slot* slot) : _key(slot) {
|
||||
JsonPairConst(const VariantSlot* slot) : _key(slot) {
|
||||
if (slot) {
|
||||
_value = JsonVariantConst(&slot->value);
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ class JsonVariant : public JsonVariantProxy<JsonVariantData>,
|
||||
|
||||
// set(bool value)
|
||||
FORCE_INLINE bool set(bool value) const {
|
||||
return variantSetBoolean(_data, value);
|
||||
return variantSetBoolean(_data, value, _memoryPool);
|
||||
}
|
||||
|
||||
// set(double value);
|
||||
@ -142,7 +142,7 @@ class JsonVariant : public JsonVariantProxy<JsonVariantData>,
|
||||
FORCE_INLINE bool set(
|
||||
T value,
|
||||
typename enable_if<is_floating_point<T>::value>::type * = 0) const {
|
||||
return variantSetFloat(_data, static_cast<JsonFloat>(value));
|
||||
return variantSetFloat(_data, static_cast<JsonFloat>(value), _memoryPool);
|
||||
}
|
||||
|
||||
// set(char)
|
||||
@ -155,7 +155,7 @@ class JsonVariant : public JsonVariantProxy<JsonVariantData>,
|
||||
T value,
|
||||
typename enable_if<is_integral<T>::value && is_signed<T>::value>::type * =
|
||||
0) const {
|
||||
return variantSetSignedInteger(_data, value);
|
||||
return variantSetSignedInteger(_data, value, _memoryPool);
|
||||
}
|
||||
|
||||
// set(unsigned short)
|
||||
@ -165,12 +165,13 @@ class JsonVariant : public JsonVariantProxy<JsonVariantData>,
|
||||
FORCE_INLINE bool set(
|
||||
T value, typename enable_if<is_integral<T>::value &&
|
||||
is_unsigned<T>::value>::type * = 0) const {
|
||||
return variantSetSignedInteger(_data, static_cast<JsonUInt>(value));
|
||||
return variantSetUnsignedInteger(_data, static_cast<JsonUInt>(value),
|
||||
_memoryPool);
|
||||
}
|
||||
|
||||
// set(SerializedValue<const char *>)
|
||||
FORCE_INLINE bool set(SerializedValue<const char *> value) const {
|
||||
return variantSetLinkedRaw(_data, value);
|
||||
return variantSetLinkedRaw(_data, value, _memoryPool);
|
||||
}
|
||||
|
||||
// set(SerializedValue<std::string>)
|
||||
@ -201,12 +202,15 @@ class JsonVariant : public JsonVariantProxy<JsonVariantData>,
|
||||
|
||||
// set(const char*);
|
||||
FORCE_INLINE bool set(const char *value) const {
|
||||
return variantSetString(_data, value);
|
||||
return variantSetString(_data, value, _memoryPool);
|
||||
}
|
||||
|
||||
// set(const char*);
|
||||
// for internal use only
|
||||
FORCE_INLINE bool set(StringInMemoryPool value) const {
|
||||
return variantSetString(_data, value, _memoryPool);
|
||||
return variantSetOwnedString(_data, value.slot(), _memoryPool);
|
||||
}
|
||||
FORCE_INLINE bool set(ZeroTerminatedRamStringConst value) const {
|
||||
return variantSetString(_data, value.c_str(), _memoryPool);
|
||||
}
|
||||
|
||||
bool set(JsonVariantConst value) const;
|
||||
@ -323,15 +327,4 @@ class JsonVariantConst : public JsonVariantProxy<const JsonVariantData>,
|
||||
return JsonVariantConst(objectGet(variantAsObject(_data), makeString(key)));
|
||||
}
|
||||
};
|
||||
|
||||
class JsonVariantLocal : public JsonVariant {
|
||||
public:
|
||||
explicit JsonVariantLocal(MemoryPool *memoryPool)
|
||||
: JsonVariant(memoryPool, &_localData) {
|
||||
_localData.type = JSON_NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
JsonVariantData _localData;
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -53,19 +53,19 @@ JsonVariant::as() const {
|
||||
template <typename T>
|
||||
inline typename enable_if<is_same<T, JsonArray>::value, JsonArray>::type
|
||||
JsonVariant::to() const {
|
||||
return JsonArray(_memoryPool, variantToArray(_data));
|
||||
return JsonArray(_memoryPool, variantToArray(_data, _memoryPool));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename enable_if<is_same<T, JsonObject>::value, JsonObject>::type
|
||||
JsonVariant::to() const {
|
||||
return JsonObject(_memoryPool, variantToObject(_data));
|
||||
return JsonObject(_memoryPool, variantToObject(_data, _memoryPool));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename enable_if<is_same<T, JsonVariant>::value, JsonVariant>::type
|
||||
JsonVariant::to() const {
|
||||
variantSetNull(_data);
|
||||
variantSetNull(_data, _memoryPool);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -89,11 +89,16 @@ inline void JsonVariantConst::accept(Visitor& visitor) const {
|
||||
return visitor.visitObject(JsonObjectConst(&_data->content.asObject));
|
||||
|
||||
case JSON_LINKED_STRING:
|
||||
case JSON_OWNED_STRING:
|
||||
return visitor.visitString(_data->content.asString);
|
||||
|
||||
case JSON_LINKED_RAW:
|
||||
case JSON_OWNED_STRING:
|
||||
return visitor.visitString(_data->content.asOwnedString->value);
|
||||
|
||||
case JSON_OWNED_RAW:
|
||||
return visitor.visitRawJson(_data->content.asOwnedRaw->value,
|
||||
_data->content.asOwnedRaw->size);
|
||||
|
||||
case JSON_LINKED_RAW:
|
||||
return visitor.visitRawJson(_data->content.asRaw.data,
|
||||
_data->content.asRaw.size);
|
||||
|
||||
|
28
src/ArduinoJson/Memory/Alignment.hpp
Normal file
28
src/ArduinoJson/Memory/Alignment.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h> // size_t
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
inline bool isAligned(void *ptr) {
|
||||
const size_t mask = sizeof(void *) - 1;
|
||||
size_t addr = reinterpret_cast<size_t>(ptr);
|
||||
return (addr & mask) == 0;
|
||||
}
|
||||
|
||||
inline size_t addPadding(size_t bytes) {
|
||||
const size_t mask = sizeof(void *) - 1;
|
||||
return (bytes + mask) & ~mask;
|
||||
}
|
||||
|
||||
template <size_t bytes>
|
||||
struct AddPadding {
|
||||
static const size_t mask = sizeof(void *) - 1;
|
||||
static const size_t value = (bytes + mask) & ~mask;
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -1,19 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MemoryPool.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class AllocableInMemoryPool {
|
||||
public:
|
||||
void *operator new(size_t n, MemoryPool *memoryPool) NOEXCEPT {
|
||||
return memoryPool->alloc(n);
|
||||
}
|
||||
|
||||
void operator delete(void *, MemoryPool *)NOEXCEPT {}
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -5,7 +5,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Strings/StringInMemoryPool.hpp"
|
||||
#include "Alignment.hpp"
|
||||
#include "MemoryPool.hpp"
|
||||
#include "StaticMemoryPool.hpp"
|
||||
|
||||
#include <stdlib.h> // malloc, free
|
||||
|
||||
@ -32,18 +34,15 @@ class DefaultAllocator {
|
||||
|
||||
template <typename TAllocator>
|
||||
class DynamicMemoryPoolBase : public MemoryPool {
|
||||
struct Block;
|
||||
struct EmptyBlock {
|
||||
class Block : public StaticMemoryPoolBase {
|
||||
public:
|
||||
Block(char* buf, size_t sz, Block* nxt)
|
||||
: StaticMemoryPoolBase(buf, sz), next(nxt) {}
|
||||
Block* next;
|
||||
size_t capacity;
|
||||
size_t size;
|
||||
};
|
||||
struct Block : EmptyBlock {
|
||||
uint8_t data[1];
|
||||
};
|
||||
|
||||
public:
|
||||
enum { EmptyBlockSize = sizeof(EmptyBlock) };
|
||||
enum { EmptyBlockSize = sizeof(Block) };
|
||||
|
||||
DynamicMemoryPoolBase(size_t initialSize = ARDUINOJSON_DEFAULT_POOL_SIZE)
|
||||
: _head(NULL), _nextBlockCapacity(initialSize) {}
|
||||
@ -56,17 +55,83 @@ class DynamicMemoryPoolBase : public MemoryPool {
|
||||
_nextBlockCapacity = capacity;
|
||||
}
|
||||
|
||||
// Gets the number of bytes occupied in the memoryPool
|
||||
size_t size() const {
|
||||
size_t total = 0;
|
||||
for (const Block* b = _head; b; b = b->next) total += b->size;
|
||||
return total;
|
||||
virtual size_t size() const {
|
||||
size_t sum = 0;
|
||||
for (Block* b = _head; b; b = b->next) {
|
||||
sum += b->size();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
// Allocates the specified amount of bytes in the memoryPool
|
||||
virtual void* alloc(size_t bytes) {
|
||||
alignNextAlloc();
|
||||
return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes);
|
||||
virtual VariantSlot* allocVariant() {
|
||||
for (Block* b = _head; b; b = b->next) {
|
||||
VariantSlot* s = b->allocVariant();
|
||||
if (s) return s;
|
||||
}
|
||||
|
||||
if (!addNewBlock(sizeof(VariantSlot))) return 0;
|
||||
|
||||
return _head->allocVariant();
|
||||
}
|
||||
|
||||
virtual void freeVariant(VariantSlot* slot) {
|
||||
for (Block* b = _head; b; b = b->next) {
|
||||
if (b->owns(slot)) {
|
||||
b->freeVariant(slot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void freeString(StringSlot* slot) {
|
||||
for (Block* b = _head; b; b = b->next) {
|
||||
if (b->owns(slot)) {
|
||||
b->freeString(slot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual StringSlot* allocFrozenString(size_t n) {
|
||||
for (Block* b = _head; b; b = b->next) {
|
||||
StringSlot* s = b->allocFrozenString(n);
|
||||
if (s) return s;
|
||||
}
|
||||
|
||||
if (!addNewBlock(sizeof(StringSlot) + n)) return 0;
|
||||
|
||||
return _head->allocFrozenString(n);
|
||||
}
|
||||
|
||||
virtual StringSlot* allocExpandableString() {
|
||||
for (Block* b = _head; b; b = b->next) {
|
||||
StringSlot* s = b->allocExpandableString();
|
||||
if (s) return s;
|
||||
}
|
||||
|
||||
if (!addNewBlock(sizeof(StringSlot))) return 0;
|
||||
|
||||
return _head->allocExpandableString();
|
||||
}
|
||||
|
||||
virtual StringSlot* expandString(StringSlot* oldSlot) {
|
||||
if (!addNewBlock(sizeof(StringSlot) + oldSlot->size)) return 0;
|
||||
|
||||
StringSlot* newSlot = _head->allocExpandableString();
|
||||
|
||||
ARDUINOJSON_ASSERT(newSlot->size > oldSlot->size);
|
||||
memcpy(newSlot->value, oldSlot->value, oldSlot->size);
|
||||
freeString(oldSlot);
|
||||
|
||||
return newSlot;
|
||||
}
|
||||
|
||||
virtual void freezeString(StringSlot* slot, size_t newSize) {
|
||||
for (Block* b = _head; b; b = b->next) {
|
||||
if (b->owns(slot)) {
|
||||
b->freezeString(slot, newSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resets the memoryPool.
|
||||
@ -74,7 +139,7 @@ class DynamicMemoryPoolBase : public MemoryPool {
|
||||
void clear() {
|
||||
Block* currentBlock = _head;
|
||||
while (currentBlock != NULL) {
|
||||
_nextBlockCapacity = currentBlock->capacity;
|
||||
_nextBlockCapacity = currentBlock->capacity();
|
||||
Block* nextBlock = currentBlock->next;
|
||||
_allocator.deallocate(currentBlock);
|
||||
currentBlock = nextBlock;
|
||||
@ -82,71 +147,22 @@ class DynamicMemoryPoolBase : public MemoryPool {
|
||||
_head = 0;
|
||||
}
|
||||
|
||||
class StringBuilder {
|
||||
public:
|
||||
explicit StringBuilder(DynamicMemoryPoolBase* parent)
|
||||
: _parent(parent), _start(NULL), _length(0) {}
|
||||
|
||||
void append(char c) {
|
||||
if (_parent->canAllocInHead(1)) {
|
||||
char* end = static_cast<char*>(_parent->allocInHead(1));
|
||||
*end = c;
|
||||
if (_length == 0) _start = end;
|
||||
} else {
|
||||
char* newStart =
|
||||
static_cast<char*>(_parent->allocInNewBlock(_length + 1));
|
||||
if (_start && newStart) memcpy(newStart, _start, _length);
|
||||
if (newStart) newStart[_length] = c;
|
||||
_start = newStart;
|
||||
}
|
||||
_length++;
|
||||
}
|
||||
|
||||
StringInMemoryPool complete() {
|
||||
append(0);
|
||||
return _start;
|
||||
}
|
||||
|
||||
private:
|
||||
DynamicMemoryPoolBase* _parent;
|
||||
char* _start;
|
||||
size_t _length;
|
||||
};
|
||||
|
||||
StringBuilder startString() {
|
||||
return StringBuilder(this);
|
||||
size_t blockCount() const {
|
||||
size_t sum = 0;
|
||||
for (Block* b = _head; b; b = b->next) sum++;
|
||||
return sum;
|
||||
}
|
||||
|
||||
private:
|
||||
void alignNextAlloc() {
|
||||
if (_head) _head->size = this->round_size_up(_head->size);
|
||||
}
|
||||
|
||||
bool canAllocInHead(size_t bytes) const {
|
||||
return _head != NULL && _head->size + bytes <= _head->capacity;
|
||||
}
|
||||
|
||||
void* allocInHead(size_t bytes) {
|
||||
void* p = _head->data + _head->size;
|
||||
_head->size += bytes;
|
||||
return p;
|
||||
}
|
||||
|
||||
void* allocInNewBlock(size_t bytes) {
|
||||
bool addNewBlock(size_t minCapacity) {
|
||||
size_t capacity = _nextBlockCapacity;
|
||||
if (bytes > capacity) capacity = bytes;
|
||||
if (!addNewBlock(capacity)) return NULL;
|
||||
_nextBlockCapacity *= 2;
|
||||
return allocInHead(bytes);
|
||||
}
|
||||
|
||||
bool addNewBlock(size_t capacity) {
|
||||
size_t bytes = EmptyBlockSize + capacity;
|
||||
Block* block = static_cast<Block*>(_allocator.allocate(bytes));
|
||||
if (block == NULL) return false;
|
||||
block->capacity = capacity;
|
||||
block->size = 0;
|
||||
block->next = _head;
|
||||
if (minCapacity > capacity) capacity = minCapacity;
|
||||
capacity = addPadding(capacity);
|
||||
size_t bytes = sizeof(Block) + capacity;
|
||||
char* p = reinterpret_cast<char*>(_allocator.allocate(bytes));
|
||||
if (!p) return false;
|
||||
Block* block = new (p) Block(p + sizeof(Block), capacity, _head);
|
||||
_nextBlockCapacity = capacity * 2;
|
||||
_head = block;
|
||||
return true;
|
||||
}
|
||||
|
@ -5,36 +5,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
#include <stdint.h> // for uint8_t
|
||||
#include <string.h>
|
||||
|
||||
#include "../Configuration.hpp"
|
||||
#include "../Polyfills/attributes.hpp"
|
||||
#include "StringSlot.hpp"
|
||||
#include "VariantSlot.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
// Handle the memory management (done in derived classes) and calls the parser.
|
||||
// This abstract class is implemented by StaticMemoryPool which implements a
|
||||
// fixed memory allocation.
|
||||
|
||||
class MemoryPool {
|
||||
public:
|
||||
// Allocates n bytes in the MemoryPool.
|
||||
// Return a pointer to the allocated memory or NULL if allocation fails.
|
||||
virtual void *alloc(size_t size) = 0;
|
||||
virtual StringSlot *allocExpandableString() = 0;
|
||||
virtual StringSlot *allocFrozenString(size_t) = 0;
|
||||
virtual StringSlot *expandString(StringSlot *) = 0;
|
||||
virtual void freezeString(StringSlot *, size_t) = 0;
|
||||
virtual void freeString(StringSlot *) = 0;
|
||||
|
||||
virtual VariantSlot *allocVariant() = 0;
|
||||
virtual void freeVariant(VariantSlot *) = 0;
|
||||
|
||||
virtual size_t size() const = 0;
|
||||
|
||||
protected:
|
||||
// CAUTION: NO VIRTUAL DESTRUCTOR!
|
||||
// If we add a virtual constructor the Arduino compiler will add malloc()
|
||||
// and free() to the binary, adding 706 useless bytes.
|
||||
~MemoryPool() {}
|
||||
|
||||
// Preserve aligment if necessary
|
||||
static FORCE_INLINE size_t round_size_up(size_t bytes) {
|
||||
#if ARDUINOJSON_ENABLE_ALIGNMENT
|
||||
const size_t x = sizeof(void *) - 1;
|
||||
return (bytes + x) & ~x;
|
||||
#else
|
||||
return bytes;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
67
src/ArduinoJson/Memory/SlotList.hpp
Normal file
67
src/ArduinoJson/Memory/SlotList.hpp
Normal file
@ -0,0 +1,67 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TSlot>
|
||||
class SlotList {
|
||||
public:
|
||||
SlotList() : _head(0) {}
|
||||
|
||||
TSlot *pop() {
|
||||
TSlot *slot = _head;
|
||||
if (slot) _head = slot->next;
|
||||
return slot;
|
||||
}
|
||||
|
||||
void push(TSlot *slot) {
|
||||
slot->next = _head;
|
||||
_head = slot;
|
||||
}
|
||||
|
||||
bool remove(const TSlot *slot) {
|
||||
if (_head == slot) {
|
||||
_head = slot->next;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (TSlot *s = _head; s; s = s->next) {
|
||||
if (s->next == slot) {
|
||||
s->next = slot->next;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool remove(const void *slot) {
|
||||
return remove(reinterpret_cast<const TSlot *>(slot));
|
||||
}
|
||||
|
||||
template <typename Functor>
|
||||
void forEach(const Functor &f) {
|
||||
for (TSlot *s = _head; s; s = s->next) {
|
||||
f(s);
|
||||
}
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
size_t sum = 0;
|
||||
for (TSlot *s = _head; s; s = s->next) sum += sizeof(TSlot);
|
||||
return sum;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_head = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
TSlot *_head;
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -4,94 +4,201 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Polyfills/assert.hpp"
|
||||
#include "../Polyfills/mpl/max.hpp"
|
||||
#include "../Strings/StringInMemoryPool.hpp"
|
||||
#include "Alignment.hpp"
|
||||
#include "MemoryPool.hpp"
|
||||
#include "SlotList.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
// _begin _end
|
||||
// v v
|
||||
// +-------------+--------------+-----------+
|
||||
// | strings... | (free) | ...slots |
|
||||
// +-------------+--------------+-----------+
|
||||
// ^ ^
|
||||
// _left _right
|
||||
|
||||
class StaticMemoryPoolBase : public MemoryPool {
|
||||
public:
|
||||
class StringBuilder {
|
||||
class UpdateStringSlotAddress {
|
||||
public:
|
||||
explicit StringBuilder(StaticMemoryPoolBase* parent) : _parent(parent) {
|
||||
_start = parent->_buffer + parent->_size;
|
||||
}
|
||||
UpdateStringSlotAddress(const char* address, size_t offset)
|
||||
: _address(address), _offset(offset) {}
|
||||
|
||||
void append(char c) {
|
||||
if (_parent->canAlloc(1)) {
|
||||
char* last = static_cast<char*>(_parent->doAlloc(1));
|
||||
*last = c;
|
||||
}
|
||||
}
|
||||
|
||||
StringInMemoryPool complete() const {
|
||||
if (_parent->canAlloc(1)) {
|
||||
char* last = static_cast<char*>(_parent->doAlloc(1));
|
||||
*last = '\0';
|
||||
return _start;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
void operator()(StringSlot* slot) const {
|
||||
ARDUINOJSON_ASSERT(slot != NULL);
|
||||
if (slot->value > _address) slot->value -= _offset;
|
||||
}
|
||||
|
||||
private:
|
||||
StaticMemoryPoolBase* _parent;
|
||||
char* _start;
|
||||
const char* _address;
|
||||
size_t _offset;
|
||||
};
|
||||
|
||||
public:
|
||||
// Gets the capacity of the memoryPool in bytes
|
||||
size_t capacity() const {
|
||||
return _capacity;
|
||||
return size_t(_end - _begin);
|
||||
}
|
||||
|
||||
// Gets the current usage of the memoryPool in bytes
|
||||
size_t size() const {
|
||||
return _size;
|
||||
virtual size_t size() const {
|
||||
return allocated_bytes() - _freeVariants.size() - _freeStrings.size();
|
||||
}
|
||||
|
||||
// Allocates the specified amount of bytes in the memoryPool
|
||||
virtual void* alloc(size_t bytes) {
|
||||
alignNextAlloc();
|
||||
if (!canAlloc(bytes)) return NULL;
|
||||
return doAlloc(bytes);
|
||||
virtual VariantSlot* allocVariant() {
|
||||
VariantSlot* s = _freeVariants.pop();
|
||||
if (s) return s;
|
||||
return s ? s : allocRight<VariantSlot>();
|
||||
}
|
||||
|
||||
virtual void freeVariant(VariantSlot* slot) {
|
||||
freeVariantSlot(slot);
|
||||
compactRightSide();
|
||||
}
|
||||
|
||||
virtual void freeString(StringSlot* slot) {
|
||||
freeStringSlot(slot);
|
||||
compactLeftSide(slot->value, slot->size);
|
||||
compactRightSide();
|
||||
}
|
||||
|
||||
virtual StringSlot* allocFrozenString(size_t n) {
|
||||
StringSlot* s = allocStringSlot();
|
||||
if (!s) return 0;
|
||||
if (!canAlloc(n)) return 0;
|
||||
|
||||
s->value = _left;
|
||||
s->size = n;
|
||||
_left += n;
|
||||
_usedString.push(s);
|
||||
checkInvariants();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual StringSlot* allocExpandableString() {
|
||||
StringSlot* s = allocStringSlot();
|
||||
if (!s) return 0;
|
||||
|
||||
s->value = _left;
|
||||
s->size = size_t(_right - _left);
|
||||
_usedString.push(s);
|
||||
_left = _right;
|
||||
|
||||
checkInvariants();
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual StringSlot* expandString(StringSlot*) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void freezeString(StringSlot* slot, size_t newSize) {
|
||||
_left -= (slot->size - newSize);
|
||||
slot->size = newSize;
|
||||
checkInvariants();
|
||||
}
|
||||
|
||||
// Resets the memoryPool.
|
||||
// USE WITH CAUTION: this invalidates all previously allocated data
|
||||
void clear() {
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
StringBuilder startString() {
|
||||
return StringBuilder(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
StaticMemoryPoolBase(char* memoryPool, size_t capa)
|
||||
: _buffer(memoryPool), _capacity(capa), _size(0) {}
|
||||
|
||||
~StaticMemoryPoolBase() {}
|
||||
|
||||
private:
|
||||
void alignNextAlloc() {
|
||||
_size = round_size_up(_size);
|
||||
_left = _begin;
|
||||
_right = _end;
|
||||
_freeVariants.clear();
|
||||
_freeStrings.clear();
|
||||
_usedString.clear();
|
||||
}
|
||||
|
||||
bool canAlloc(size_t bytes) const {
|
||||
return _size + bytes <= _capacity;
|
||||
return _left + bytes <= _right;
|
||||
}
|
||||
|
||||
void* doAlloc(size_t bytes) {
|
||||
void* p = &_buffer[_size];
|
||||
_size += bytes;
|
||||
bool owns(void* p) const {
|
||||
return _begin <= p && p < _end;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* allocRight() {
|
||||
return reinterpret_cast<T*>(allocRight(sizeof(T)));
|
||||
}
|
||||
|
||||
char* allocRight(size_t bytes) {
|
||||
if (!canAlloc(bytes)) return 0;
|
||||
_right -= bytes;
|
||||
return _right;
|
||||
}
|
||||
|
||||
// Workaround for missing placement new
|
||||
void* operator new(size_t, void* p) {
|
||||
return p;
|
||||
}
|
||||
|
||||
char* _buffer;
|
||||
size_t _capacity;
|
||||
size_t _size;
|
||||
};
|
||||
protected:
|
||||
StaticMemoryPoolBase(char* buffer, size_t capa)
|
||||
: _begin(buffer),
|
||||
_left(buffer),
|
||||
_right(buffer + capa),
|
||||
_end(buffer + capa) {}
|
||||
|
||||
~StaticMemoryPoolBase() {}
|
||||
|
||||
// Gets the current usage of the memoryPool in bytes
|
||||
size_t allocated_bytes() const {
|
||||
return size_t(_left - _begin + _end - _right);
|
||||
}
|
||||
|
||||
private:
|
||||
StringSlot* allocStringSlot() {
|
||||
StringSlot* s = _freeStrings.pop();
|
||||
if (s) return s;
|
||||
return allocRight<StringSlot>();
|
||||
}
|
||||
|
||||
void freeVariantSlot(VariantSlot* slot) {
|
||||
_freeVariants.push(slot);
|
||||
}
|
||||
|
||||
void freeStringSlot(StringSlot* slot) {
|
||||
_usedString.remove(slot);
|
||||
_freeStrings.push(slot);
|
||||
}
|
||||
|
||||
void compactLeftSide(char* holeAddress, size_t holeSize) {
|
||||
ARDUINOJSON_ASSERT(holeAddress >= _begin);
|
||||
ARDUINOJSON_ASSERT(holeAddress + holeSize <= _left);
|
||||
char* holeEnd = holeAddress + holeSize;
|
||||
memmove(holeAddress, // where the hole begun
|
||||
holeEnd, // where the hole ended
|
||||
size_t(_left - holeEnd)); // everything after the hole
|
||||
_left -= holeSize;
|
||||
_usedString.forEach(UpdateStringSlotAddress(holeAddress, holeSize));
|
||||
checkInvariants();
|
||||
}
|
||||
|
||||
void compactRightSide() {
|
||||
loop:
|
||||
if (_freeStrings.remove(_right)) {
|
||||
_right += sizeof(StringSlot);
|
||||
goto loop;
|
||||
}
|
||||
if (_freeVariants.remove(_right)) {
|
||||
_right += sizeof(VariantSlot);
|
||||
goto loop;
|
||||
}
|
||||
checkInvariants();
|
||||
}
|
||||
|
||||
void checkInvariants() {
|
||||
ARDUINOJSON_ASSERT(_begin <= _left);
|
||||
ARDUINOJSON_ASSERT(_left <= _right);
|
||||
ARDUINOJSON_ASSERT(_right <= _end);
|
||||
}
|
||||
|
||||
char *_begin, *_left, *_right, *_end;
|
||||
SlotList<VariantSlot> _freeVariants;
|
||||
SlotList<StringSlot> _freeStrings;
|
||||
SlotList<StringSlot> _usedString;
|
||||
}; // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
@ -108,7 +215,8 @@ class StaticMemoryPoolBase : public MemoryPool {
|
||||
// bytes.
|
||||
template <size_t CAPACITY>
|
||||
class StaticMemoryPool : public StaticMemoryPoolBase {
|
||||
static const size_t ACTUAL_CAPACITY = Max<1, CAPACITY>::value;
|
||||
static const size_t ACTUAL_CAPACITY =
|
||||
AddPadding<Max<1, CAPACITY>::value>::value;
|
||||
|
||||
public:
|
||||
explicit StaticMemoryPool()
|
||||
|
53
src/ArduinoJson/Memory/StringBuilder.hpp
Normal file
53
src/ArduinoJson/Memory/StringBuilder.hpp
Normal file
@ -0,0 +1,53 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
void append(const char* s) {
|
||||
while (*s) append(*s++);
|
||||
}
|
||||
|
||||
void append(const char* s, size_t n) {
|
||||
while (n-- > 0) append(*s++);
|
||||
}
|
||||
|
||||
void append(char c) {
|
||||
if (!_slot) return;
|
||||
|
||||
if (_size >= _slot->size) {
|
||||
_slot = _parent->expandString(_slot);
|
||||
if (!_slot) return;
|
||||
}
|
||||
|
||||
_slot->value[_size++] = c;
|
||||
}
|
||||
|
||||
StringType complete() {
|
||||
append('\0');
|
||||
if (_slot) {
|
||||
_parent->freezeString(_slot, _size);
|
||||
}
|
||||
return _slot;
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryPool* _parent;
|
||||
size_t _size;
|
||||
StringSlot* _slot;
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
20
src/ArduinoJson/Memory/StringSlot.hpp
Normal file
20
src/ArduinoJson/Memory/StringSlot.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h> // for size_t
|
||||
#include "../Configuration.hpp"
|
||||
|
||||
#define JSON_STRING_SIZE(SIZE) \
|
||||
(sizeof(ARDUINOJSON_NAMESPACE::StringSlot) + (SIZE))
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
struct StringSlot {
|
||||
char *value;
|
||||
size_t size;
|
||||
struct StringSlot *next;
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -4,16 +4,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Memory/AllocableInMemoryPool.hpp"
|
||||
#include "JsonVariantData.hpp"
|
||||
#include "../Data/JsonVariantData.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
struct Slot : AllocableInMemoryPool {
|
||||
struct VariantSlot {
|
||||
JsonVariantData value;
|
||||
struct Slot* next;
|
||||
struct Slot* prev;
|
||||
const char* key;
|
||||
struct VariantSlot* next;
|
||||
struct VariantSlot* prev;
|
||||
union {
|
||||
const char* linkedKey;
|
||||
StringSlot* ownedKey;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -17,6 +17,7 @@ template <typename TReader, typename TStringStorage>
|
||||
class MsgPackDeserializer {
|
||||
typedef typename remove_reference<TStringStorage>::type::StringBuilder
|
||||
StringBuilder;
|
||||
typedef typename StringBuilder::StringType StringType;
|
||||
|
||||
public:
|
||||
MsgPackDeserializer(MemoryPool &memoryPool, TReader reader,
|
||||
@ -220,16 +221,29 @@ class MsgPackDeserializer {
|
||||
return readString(variant, size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DeserializationError readString(StringType &str) {
|
||||
T size;
|
||||
if (!readInteger(size)) return DeserializationError::IncompleteInput;
|
||||
return readString(str, size);
|
||||
}
|
||||
|
||||
DeserializationError readString(JsonVariant variant, size_t n) {
|
||||
StringBuilder str = _stringStorage.startString();
|
||||
StringType s;
|
||||
DeserializationError err = readString(s, n);
|
||||
if (!err) variant.set(s);
|
||||
return err;
|
||||
}
|
||||
|
||||
DeserializationError readString(StringType &s, size_t n) {
|
||||
StringBuilder builder = _stringStorage.startString();
|
||||
for (; n; --n) {
|
||||
uint8_t c;
|
||||
if (!readBytes(c)) return DeserializationError::IncompleteInput;
|
||||
str.append(static_cast<char>(c));
|
||||
builder.append(static_cast<char>(c));
|
||||
}
|
||||
StringInMemoryPool s = str.complete();
|
||||
s = builder.complete();
|
||||
if (s.isNull()) return DeserializationError::NoMemory;
|
||||
variant.set(s);
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
|
||||
@ -278,12 +292,11 @@ class MsgPackDeserializer {
|
||||
if (_nestingLimit == 0) return DeserializationError::TooDeep;
|
||||
--_nestingLimit;
|
||||
for (; n; --n) {
|
||||
JsonVariantLocal key(_memoryPool);
|
||||
DeserializationError err = parse(key);
|
||||
StringType key;
|
||||
DeserializationError err = parseKey(key);
|
||||
if (err) return err;
|
||||
if (!key.is<char *>()) return DeserializationError::NotSupported;
|
||||
|
||||
JsonVariant value = object.set(StringInMemoryPool(key.as<char *>()));
|
||||
JsonVariant value = object.set(key);
|
||||
if (value.isInvalid()) return DeserializationError::NoMemory;
|
||||
|
||||
err = parse(value);
|
||||
@ -293,6 +306,27 @@ class MsgPackDeserializer {
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
|
||||
DeserializationError parseKey(StringType &key) {
|
||||
uint8_t code;
|
||||
if (!readByte(code)) return DeserializationError::IncompleteInput;
|
||||
|
||||
if ((code & 0xe0) == 0xa0) return readString(key, code & 0x1f);
|
||||
|
||||
switch (code) {
|
||||
case 0xd9:
|
||||
return readString<uint8_t>(key);
|
||||
|
||||
case 0xda:
|
||||
return readString<uint16_t>(key);
|
||||
|
||||
case 0xdb:
|
||||
return readString<uint32_t>(key);
|
||||
|
||||
default:
|
||||
return DeserializationError::NotSupported;
|
||||
}
|
||||
}
|
||||
|
||||
MemoryPool *_memoryPool;
|
||||
TReader _reader;
|
||||
TStringStorage _stringStorage;
|
||||
|
12
src/ArduinoJson/Polyfills/assert.hpp
Normal file
12
src/ArduinoJson/Polyfills/assert.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef ARDUINOJSON_DEBUG
|
||||
#include <assert.h>
|
||||
#define ARDUINOJSON_ASSERT(X) assert(X)
|
||||
#else
|
||||
#define ARDUINOJSON_ASSERT(X) ((void)0)
|
||||
#endif
|
@ -4,20 +4,22 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Memory/MemoryPool.hpp"
|
||||
#include "../Memory/StringBuilder.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TMemoryPool>
|
||||
class StringCopier {
|
||||
public:
|
||||
StringCopier(TMemoryPool& memoryPool) : _memoryPool(&memoryPool) {}
|
||||
typedef ARDUINOJSON_NAMESPACE::StringBuilder StringBuilder;
|
||||
|
||||
typedef typename TMemoryPool::StringBuilder StringBuilder;
|
||||
StringCopier(MemoryPool* memoryPool) : _memoryPool(memoryPool) {}
|
||||
|
||||
StringBuilder startString() {
|
||||
return _memoryPool->startString();
|
||||
return StringBuilder(_memoryPool);
|
||||
}
|
||||
|
||||
private:
|
||||
TMemoryPool* _memoryPool;
|
||||
MemoryPool* _memoryPool;
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -11,13 +11,15 @@ class StringMover {
|
||||
public:
|
||||
class StringBuilder {
|
||||
public:
|
||||
typedef ZeroTerminatedRamStringConst StringType;
|
||||
|
||||
StringBuilder(TChar** ptr) : _writePtr(ptr), _startPtr(*ptr) {}
|
||||
|
||||
void append(char c) {
|
||||
*(*_writePtr)++ = TChar(c);
|
||||
}
|
||||
|
||||
StringInMemoryPool complete() const {
|
||||
StringType complete() const {
|
||||
*(*_writePtr)++ = 0;
|
||||
return reinterpret_cast<const char*>(_startPtr);
|
||||
}
|
||||
|
@ -9,34 +9,34 @@
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TMemoryPool, typename TInput, typename Enable = void>
|
||||
template <typename TInput, typename Enable = void>
|
||||
struct StringStorage {
|
||||
typedef StringCopier<TMemoryPool> type;
|
||||
typedef StringCopier type;
|
||||
|
||||
static type create(TMemoryPool& jb, TInput&) {
|
||||
return type(jb);
|
||||
static type create(MemoryPool& pool, TInput&) {
|
||||
return type(&pool);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TMemoryPool, typename TChar>
|
||||
struct StringStorage<TMemoryPool, TChar*,
|
||||
template <typename TChar>
|
||||
struct StringStorage<TChar*,
|
||||
typename enable_if<!is_const<TChar>::value>::type> {
|
||||
typedef StringMover<TChar> type;
|
||||
|
||||
static type create(TMemoryPool&, TChar* input) {
|
||||
static type create(MemoryPool&, TChar* input) {
|
||||
return type(input);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TMemoryPool, typename TInput>
|
||||
typename StringStorage<TMemoryPool, TInput>::type makeStringStorage(
|
||||
TMemoryPool& jb, TInput& input) {
|
||||
return StringStorage<TMemoryPool, TInput>::create(jb, input);
|
||||
template <typename TInput>
|
||||
typename StringStorage<TInput>::type makeStringStorage(MemoryPool& pool,
|
||||
TInput& input) {
|
||||
return StringStorage<TInput>::create(pool, input);
|
||||
}
|
||||
|
||||
template <typename TMemoryPool, typename TChar>
|
||||
typename StringStorage<TMemoryPool, TChar*>::type makeStringStorage(
|
||||
TMemoryPool& jb, TChar* input) {
|
||||
return StringStorage<TMemoryPool, TChar*>::create(jb, input);
|
||||
template <typename TChar>
|
||||
typename StringStorage<TChar*>::type makeStringStorage(MemoryPool& pool,
|
||||
TChar* input) {
|
||||
return StringStorage<TChar*>::create(pool, input);
|
||||
}
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -13,12 +13,12 @@ class ArduinoString {
|
||||
ArduinoString(const ::String& str) : _str(&str) {}
|
||||
|
||||
template <typename TMemoryPool>
|
||||
const char* save(TMemoryPool* memoryPool) const {
|
||||
StringSlot* save(TMemoryPool* memoryPool) const {
|
||||
if (isNull()) return NULL;
|
||||
size_t n = _str->length() + 1;
|
||||
void* dup = memoryPool->alloc(n);
|
||||
if (dup != NULL) memcpy(dup, _str->c_str(), n);
|
||||
return static_cast<const char*>(dup);
|
||||
StringSlot* slot = memoryPool->allocFrozenString(n);
|
||||
if (slot) memcpy(slot->value, _str->c_str(), n);
|
||||
return slot;
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
|
@ -22,11 +22,11 @@ class FixedSizeFlashString {
|
||||
}
|
||||
|
||||
template <typename TMemoryPool>
|
||||
const char* save(TMemoryPool* memoryPool) const {
|
||||
StringSlot* save(TMemoryPool* memoryPool) const {
|
||||
if (!_str) return NULL;
|
||||
void* dup = memoryPool->alloc(_size);
|
||||
if (dup != NULL) memcpy_P(dup, (const char*)_str, _size);
|
||||
return static_cast<const char*>(dup);
|
||||
StringSlot* slot = memoryPool->allocFrozenString(_size);
|
||||
if (!slot) memcpy_P(slot->value, (const char*)_str, _size);
|
||||
return slot;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
|
@ -23,12 +23,11 @@ class FixedSizeRamString {
|
||||
}
|
||||
|
||||
template <typename TMemoryPool>
|
||||
const char* save(TMemoryPool* memoryPool) const {
|
||||
StringSlot* save(TMemoryPool* memoryPool) const {
|
||||
if (!_str) return NULL;
|
||||
void* dup = memoryPool->alloc(_size);
|
||||
if (!dup) return NULL;
|
||||
memcpy(dup, _str, _size);
|
||||
return static_cast<const char*>(dup);
|
||||
StringSlot* slot = memoryPool->allocFrozenString(_size);
|
||||
if (slot) memcpy(slot->value, _str, _size);
|
||||
return slot;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
|
@ -13,11 +13,11 @@ class StlString {
|
||||
StlString(const std::string& str) : _str(&str) {}
|
||||
|
||||
template <typename TMemoryPool>
|
||||
const char* save(TMemoryPool* memoryPool) const {
|
||||
StringSlot* save(TMemoryPool* memoryPool) const {
|
||||
size_t n = _str->length() + 1;
|
||||
void* dup = memoryPool->alloc(n);
|
||||
if (dup != NULL) memcpy(dup, _str->c_str(), n);
|
||||
return static_cast<const char*>(dup);
|
||||
StringSlot* slot = memoryPool->allocFrozenString(n);
|
||||
if (slot) memcpy(slot->value, _str->c_str(), n);
|
||||
return slot;
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
|
@ -4,13 +4,45 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ZeroTerminatedRamStringConst.hpp"
|
||||
#include <string.h>
|
||||
#include "../Memory/StringSlot.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class StringInMemoryPool : public ZeroTerminatedRamStringConst {
|
||||
class StringInMemoryPool {
|
||||
public:
|
||||
StringInMemoryPool(const char* str = 0) : ZeroTerminatedRamStringConst(str) {}
|
||||
StringInMemoryPool(StringSlot* s = 0) : _slot(s) {}
|
||||
|
||||
bool equals(const char* expected) const {
|
||||
if (!_slot) return expected == 0;
|
||||
const char* actual = _slot->value;
|
||||
if (actual == expected) return true;
|
||||
return strcmp(actual, expected) == 0;
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return !_slot;
|
||||
}
|
||||
|
||||
template <typename TMemoryPool>
|
||||
StringSlot* save(TMemoryPool*) const {
|
||||
return _slot;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return _slot->size;
|
||||
}
|
||||
|
||||
StringSlot* slot() const {
|
||||
return _slot;
|
||||
}
|
||||
|
||||
const char* c_str() const {
|
||||
return _slot->value;
|
||||
}
|
||||
|
||||
protected:
|
||||
StringSlot* _slot;
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -21,12 +21,12 @@ class ZeroTerminatedFlashString {
|
||||
}
|
||||
|
||||
template <typename TMemoryPool>
|
||||
const char* save(TMemoryPool* memoryPool) const {
|
||||
StringSlot* save(TMemoryPool* memoryPool) const {
|
||||
if (!_str) return NULL;
|
||||
size_t n = size() + 1; // copy the terminator
|
||||
void* dup = memoryPool->alloc(n);
|
||||
if (dup != NULL) memcpy_P(dup, (const char*)_str, n);
|
||||
return static_cast<const char*>(dup);
|
||||
StringSlot* slot = memoryPool->allocFrozenString(n);
|
||||
if (slot) memcpy_P(slot->value, reinterpret_cast<const char*>(_str), n);
|
||||
return slot;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
|
@ -14,13 +14,12 @@ class ZeroTerminatedRamString : public ZeroTerminatedRamStringConst {
|
||||
: ZeroTerminatedRamStringConst(str) {}
|
||||
|
||||
template <typename TMemoryPool>
|
||||
const char* save(TMemoryPool* memoryPool) const {
|
||||
StringSlot* save(TMemoryPool* memoryPool) const {
|
||||
if (!_str) return NULL;
|
||||
size_t n = size() + 1;
|
||||
void* dup = memoryPool->alloc(n);
|
||||
if (!dup) return NULL;
|
||||
memcpy(dup, _str, n);
|
||||
return static_cast<const char*>(dup);
|
||||
StringSlot* slot = memoryPool->allocFrozenString(n);
|
||||
if (slot) memcpy(slot->value, _str, n);
|
||||
return slot;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -11,7 +11,7 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class ZeroTerminatedRamStringConst {
|
||||
public:
|
||||
ZeroTerminatedRamStringConst(const char* str) : _str(str) {}
|
||||
ZeroTerminatedRamStringConst(const char* str = 0) : _str(str) {}
|
||||
|
||||
bool equals(const char* expected) const {
|
||||
const char* actual = _str;
|
||||
@ -23,15 +23,19 @@ class ZeroTerminatedRamStringConst {
|
||||
return !_str;
|
||||
}
|
||||
|
||||
template <typename TMemoryPool>
|
||||
const char* save(TMemoryPool*) const {
|
||||
return _str;
|
||||
}
|
||||
// template <typename TMemoryPool>
|
||||
// const char* save(TMemoryPool*) const {
|
||||
// return _str;
|
||||
// }
|
||||
|
||||
size_t size() const {
|
||||
return strlen(_str);
|
||||
}
|
||||
|
||||
const char* c_str() const {
|
||||
return _str;
|
||||
}
|
||||
|
||||
protected:
|
||||
const char* _str;
|
||||
};
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define ARDUINOJSON_VERSION "6.5.0-beta"
|
||||
#define ARDUINOJSON_VERSION "6.6.0-beta"
|
||||
#define ARDUINOJSON_VERSION_MAJOR 6
|
||||
#define ARDUINOJSON_VERSION_MINOR 5
|
||||
#define ARDUINOJSON_VERSION_MINOR 6
|
||||
#define ARDUINOJSON_VERSION_REVISION 0
|
||||
|
@ -2,11 +2,14 @@
|
||||
# Copyright Benoit Blanchon 2014-2018
|
||||
# MIT License
|
||||
|
||||
add_executable(DynamicMemoryPoolTests
|
||||
alloc.cpp
|
||||
add_executable(DynamicMemoryPoolTests
|
||||
allocString.cpp
|
||||
allocVariant.cpp
|
||||
blocks.cpp
|
||||
clear.cpp
|
||||
no_memory.cpp
|
||||
size.cpp
|
||||
startString.cpp
|
||||
StringBuilder.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(DynamicMemoryPoolTests catch)
|
||||
|
41
test/DynamicMemoryPool/StringBuilder.cpp
Normal file
41
test/DynamicMemoryPool/StringBuilder.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
|
||||
#include <ArduinoJson/Memory/StringBuilder.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("DynamicMemoryPool::startString()") {
|
||||
SECTION("WorksWhenBufferIsBigEnough") {
|
||||
DynamicMemoryPool memoryPool(JSON_STRING_SIZE(8));
|
||||
|
||||
StringBuilder str(&memoryPool);
|
||||
str.append("abcdefg");
|
||||
|
||||
REQUIRE(memoryPool.blockCount() == 1);
|
||||
REQUIRE(str.complete().equals("abcdefg"));
|
||||
}
|
||||
|
||||
SECTION("GrowsWhenBufferIsTooSmall") {
|
||||
DynamicMemoryPool memoryPool(JSON_STRING_SIZE(8));
|
||||
|
||||
StringBuilder str(&memoryPool);
|
||||
str.append("abcdefghABC");
|
||||
|
||||
REQUIRE(memoryPool.blockCount() == 2);
|
||||
REQUIRE(str.complete().equals("abcdefghABC"));
|
||||
}
|
||||
|
||||
SECTION("SizeIncreases") {
|
||||
DynamicMemoryPool memoryPool(JSON_STRING_SIZE(5));
|
||||
|
||||
StringBuilder str(&memoryPool);
|
||||
str.append('h');
|
||||
str.complete();
|
||||
|
||||
REQUIRE(JSON_STRING_SIZE(2) == memoryPool.size());
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
#include <sstream>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
static bool isAligned(void* ptr) {
|
||||
const size_t mask = sizeof(void*) - 1;
|
||||
size_t addr = reinterpret_cast<size_t>(ptr);
|
||||
return (addr & mask) == 0;
|
||||
}
|
||||
|
||||
std::stringstream allocatorLog;
|
||||
|
||||
struct SpyingAllocator : DefaultAllocator {
|
||||
void* allocate(size_t n) {
|
||||
allocatorLog << "A" << (n - DynamicMemoryPool::EmptyBlockSize);
|
||||
return DefaultAllocator::allocate(n);
|
||||
}
|
||||
void deallocate(void* p) {
|
||||
allocatorLog << "F";
|
||||
return DefaultAllocator::deallocate(p);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("DynamicMemoryPool::alloc()") {
|
||||
SECTION("Returns different pointers") {
|
||||
DynamicMemoryPool memoryPool;
|
||||
void* p1 = memoryPool.alloc(1);
|
||||
void* p2 = memoryPool.alloc(2);
|
||||
REQUIRE(p1 != p2);
|
||||
}
|
||||
|
||||
SECTION("Doubles allocation size when full") {
|
||||
allocatorLog.str("");
|
||||
{
|
||||
DynamicMemoryPoolBase<SpyingAllocator> memoryPool(1);
|
||||
memoryPool.alloc(1);
|
||||
memoryPool.alloc(1);
|
||||
}
|
||||
REQUIRE(allocatorLog.str() == "A1A2FF");
|
||||
}
|
||||
|
||||
SECTION("Resets allocation size after clear()") {
|
||||
allocatorLog.str("");
|
||||
{
|
||||
DynamicMemoryPoolBase<SpyingAllocator> memoryPool(1);
|
||||
memoryPool.alloc(1);
|
||||
memoryPool.alloc(1);
|
||||
memoryPool.clear();
|
||||
memoryPool.alloc(1);
|
||||
}
|
||||
REQUIRE(allocatorLog.str() == "A1A2FFA1F");
|
||||
}
|
||||
|
||||
SECTION("Makes a big allocation when needed") {
|
||||
allocatorLog.str("");
|
||||
{
|
||||
DynamicMemoryPoolBase<SpyingAllocator> memoryPool(1);
|
||||
memoryPool.alloc(42);
|
||||
}
|
||||
REQUIRE(allocatorLog.str() == "A42F");
|
||||
}
|
||||
|
||||
SECTION("Alignment") {
|
||||
// make room for two but not three
|
||||
DynamicMemoryPool tinyBuf(2 * sizeof(void*) + 1);
|
||||
|
||||
REQUIRE(isAligned(tinyBuf.alloc(1))); // this on is aligned by design
|
||||
REQUIRE(isAligned(tinyBuf.alloc(1))); // this one fits in the first block
|
||||
REQUIRE(isAligned(tinyBuf.alloc(1))); // this one requires a new block
|
||||
}
|
||||
}
|
28
test/DynamicMemoryPool/allocString.cpp
Normal file
28
test/DynamicMemoryPool/allocString.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
|
||||
#include <catch.hpp>
|
||||
#include <sstream>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("DynamicMemoryPool::allocFrozenString()") {
|
||||
DynamicMemoryPool pool;
|
||||
|
||||
SECTION("Returns different pointers") {
|
||||
StringSlot* a = pool.allocFrozenString(1);
|
||||
StringSlot* b = pool.allocFrozenString(2);
|
||||
REQUIRE(a != b);
|
||||
REQUIRE(a->value != b->value);
|
||||
}
|
||||
|
||||
SECTION("Returns same slot after freeString") {
|
||||
StringSlot* a = pool.allocFrozenString(1);
|
||||
pool.freeString(a);
|
||||
StringSlot* b = pool.allocFrozenString(2);
|
||||
REQUIRE(a == b);
|
||||
REQUIRE(a->value == b->value);
|
||||
}
|
||||
}
|
41
test/DynamicMemoryPool/allocVariant.cpp
Normal file
41
test/DynamicMemoryPool/allocVariant.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("DynamicMemoryPool::allocVariant()") {
|
||||
DynamicMemoryPool memoryPool;
|
||||
|
||||
SECTION("Returns different pointer") {
|
||||
VariantSlot* s1 = memoryPool.allocVariant();
|
||||
VariantSlot* s2 = memoryPool.allocVariant();
|
||||
|
||||
REQUIRE(s1 != s2);
|
||||
}
|
||||
|
||||
SECTION("Returns same pointer after freeSlot()") {
|
||||
VariantSlot* s1 = memoryPool.allocVariant();
|
||||
memoryPool.freeVariant(s1);
|
||||
VariantSlot* s2 = memoryPool.allocVariant();
|
||||
|
||||
REQUIRE(s1 == s2);
|
||||
}
|
||||
|
||||
SECTION("Returns aligned pointers") {
|
||||
// make room for two but not three
|
||||
// pass an uneven capacity
|
||||
DynamicMemoryPool pool(2 * sizeof(VariantSlot) + 1);
|
||||
|
||||
REQUIRE(isAligned(pool.allocVariant()));
|
||||
REQUIRE(isAligned(pool.allocVariant()));
|
||||
REQUIRE(pool.blockCount() == 1);
|
||||
|
||||
REQUIRE(isAligned(pool.allocVariant()));
|
||||
REQUIRE(isAligned(pool.allocVariant()));
|
||||
REQUIRE(pool.blockCount() == 2);
|
||||
}
|
||||
}
|
69
test/DynamicMemoryPool/blocks.cpp
Normal file
69
test/DynamicMemoryPool/blocks.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
|
||||
#include <catch.hpp>
|
||||
#include <sstream>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
std::stringstream allocatorLog;
|
||||
|
||||
struct SpyingAllocator : DefaultAllocator {
|
||||
void* allocate(size_t n) {
|
||||
allocatorLog << "A" << (n - DynamicMemoryPool::EmptyBlockSize);
|
||||
return DefaultAllocator::allocate(n);
|
||||
}
|
||||
void deallocate(void* p) {
|
||||
allocatorLog << "F";
|
||||
return DefaultAllocator::deallocate(p);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("DynamicMemoryPool blocks") {
|
||||
SECTION("Doubles allocation size when full") {
|
||||
allocatorLog.str("");
|
||||
{
|
||||
DynamicMemoryPoolBase<SpyingAllocator> memoryPool(sizeof(VariantSlot));
|
||||
memoryPool.allocVariant();
|
||||
memoryPool.allocVariant();
|
||||
}
|
||||
std::stringstream expected;
|
||||
expected << "A" << sizeof(VariantSlot) // block 1
|
||||
<< "A" << 2 * sizeof(VariantSlot) // block 2, twice bigger
|
||||
<< "FF";
|
||||
|
||||
REQUIRE(allocatorLog.str() == expected.str());
|
||||
}
|
||||
|
||||
SECTION("Resets allocation size after clear()") {
|
||||
allocatorLog.str("");
|
||||
{
|
||||
DynamicMemoryPoolBase<SpyingAllocator> memoryPool(sizeof(VariantSlot));
|
||||
memoryPool.allocVariant();
|
||||
memoryPool.allocVariant();
|
||||
memoryPool.clear();
|
||||
memoryPool.allocVariant();
|
||||
}
|
||||
std::stringstream expected;
|
||||
expected << "A" << sizeof(VariantSlot) // block 1
|
||||
<< "A" << 2 * sizeof(VariantSlot) // block 2, twice bigger
|
||||
<< "FF" // clear
|
||||
<< "A" << sizeof(VariantSlot) // block 1
|
||||
<< "F";
|
||||
REQUIRE(allocatorLog.str() == expected.str());
|
||||
}
|
||||
|
||||
/* SECTION("Alloc big block for large string") {
|
||||
allocatorLog.str("");
|
||||
{
|
||||
DynamicMemoryPoolBase<SpyingAllocator> memoryPool(1);
|
||||
memoryPool.allocString(42);
|
||||
}
|
||||
std::stringstream expected;
|
||||
expected << "A" << JSON_STRING_SIZE(42) // block 1
|
||||
<< "F";
|
||||
REQUIRE(allocatorLog.str() == expected.str());
|
||||
}*/
|
||||
}
|
47
test/DynamicMemoryPool/clear.cpp
Normal file
47
test/DynamicMemoryPool/clear.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("StaticMemoryPool::clear()") {
|
||||
DynamicMemoryPool memoryPool;
|
||||
|
||||
SECTION("Discards allocated variants") {
|
||||
memoryPool.allocVariant();
|
||||
REQUIRE(memoryPool.size() > 0);
|
||||
|
||||
memoryPool.clear();
|
||||
CHECK(memoryPool.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("Discards allocated strings") {
|
||||
memoryPool.allocFrozenString(10);
|
||||
REQUIRE(memoryPool.size() > 0);
|
||||
|
||||
memoryPool.clear();
|
||||
|
||||
CHECK(memoryPool.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("Purges variant cache") {
|
||||
memoryPool.freeVariant(memoryPool.allocVariant());
|
||||
REQUIRE(memoryPool.size() == 0);
|
||||
|
||||
memoryPool.clear();
|
||||
|
||||
CHECK(memoryPool.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("Purges string cache") {
|
||||
memoryPool.freeString(memoryPool.allocFrozenString(10));
|
||||
// REQUIRE(memoryPool.size() == 0);
|
||||
|
||||
memoryPool.clear();
|
||||
|
||||
CHECK(memoryPool.size() == 0);
|
||||
}
|
||||
}
|
@ -2,7 +2,8 @@
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
|
||||
#include <ArduinoJson/Memory/StringBuilder.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
@ -17,8 +18,6 @@ struct NoMemoryAllocator {
|
||||
TEST_CASE("DynamicMemoryPool no memory") {
|
||||
DynamicMemoryPoolBase<NoMemoryAllocator> _memoryPool;
|
||||
|
||||
typedef DynamicMemoryPoolBase<NoMemoryAllocator>::StringBuilder StringBuilder;
|
||||
|
||||
SECTION("FixCodeCoverage") {
|
||||
// call this function to fix code coverage
|
||||
NoMemoryAllocator().deallocate(NULL);
|
||||
@ -34,8 +33,8 @@ TEST_CASE("DynamicMemoryPool no memory") {
|
||||
// REQUIRE(err != DeserializationError::Ok);
|
||||
// }
|
||||
|
||||
SECTION("startString()") {
|
||||
StringBuilder str = _memoryPool.startString();
|
||||
SECTION("StringBuilder returns null") {
|
||||
StringBuilder str(&_memoryPool);
|
||||
str.append('!');
|
||||
REQUIRE(str.complete().isNull());
|
||||
}
|
||||
|
@ -14,16 +14,43 @@ TEST_CASE("DynamicMemoryPool::size()") {
|
||||
REQUIRE(0 == memoryPool.size());
|
||||
}
|
||||
|
||||
SECTION("Increases after alloc()") {
|
||||
memoryPool.alloc(1);
|
||||
REQUIRE(1U <= memoryPool.size());
|
||||
memoryPool.alloc(1);
|
||||
REQUIRE(2U <= memoryPool.size());
|
||||
SECTION("Increases after allocExpandableString()") {
|
||||
StringSlot* a = memoryPool.allocExpandableString();
|
||||
memoryPool.freezeString(a, 1);
|
||||
REQUIRE(memoryPool.size() == JSON_STRING_SIZE(1));
|
||||
|
||||
StringSlot* b = memoryPool.allocExpandableString();
|
||||
memoryPool.freezeString(b, 1);
|
||||
REQUIRE(memoryPool.size() == 2 * JSON_STRING_SIZE(1));
|
||||
}
|
||||
|
||||
SECTION("Goes back to 0 after clear()") {
|
||||
memoryPool.alloc(1);
|
||||
memoryPool.clear();
|
||||
SECTION("Increases after allocVariant()") {
|
||||
memoryPool.allocVariant();
|
||||
REQUIRE(sizeof(VariantSlot) == memoryPool.size());
|
||||
|
||||
memoryPool.allocVariant();
|
||||
REQUIRE(2 * sizeof(VariantSlot) == memoryPool.size());
|
||||
}
|
||||
|
||||
SECTION("Decreases after freeVariant()") {
|
||||
VariantSlot* a = memoryPool.allocVariant();
|
||||
VariantSlot* b = memoryPool.allocVariant();
|
||||
|
||||
memoryPool.freeVariant(b);
|
||||
REQUIRE(sizeof(VariantSlot) == memoryPool.size());
|
||||
|
||||
memoryPool.freeVariant(a);
|
||||
REQUIRE(0 == memoryPool.size());
|
||||
}
|
||||
|
||||
SECTION("Decreases after freeString()") {
|
||||
StringSlot* a = memoryPool.allocFrozenString(5);
|
||||
StringSlot* b = memoryPool.allocFrozenString(6);
|
||||
|
||||
memoryPool.freeString(b);
|
||||
REQUIRE(memoryPool.size() == JSON_STRING_SIZE(5));
|
||||
|
||||
memoryPool.freeString(a);
|
||||
REQUIRE(memoryPool.size() == 0);
|
||||
}
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
typedef DynamicMemoryPool::StringBuilder StringBuilder;
|
||||
|
||||
TEST_CASE("DynamicMemoryPool::startString()") {
|
||||
SECTION("WorksWhenBufferIsBigEnough") {
|
||||
DynamicMemoryPool memoryPool(6);
|
||||
|
||||
StringBuilder str = memoryPool.startString();
|
||||
str.append('h');
|
||||
str.append('e');
|
||||
str.append('l');
|
||||
str.append('l');
|
||||
str.append('o');
|
||||
|
||||
REQUIRE(str.complete().equals("hello"));
|
||||
}
|
||||
|
||||
SECTION("GrowsWhenBufferIsTooSmall") {
|
||||
DynamicMemoryPool memoryPool(5);
|
||||
|
||||
StringBuilder str = memoryPool.startString();
|
||||
str.append('h');
|
||||
str.append('e');
|
||||
str.append('l');
|
||||
str.append('l');
|
||||
str.append('o');
|
||||
|
||||
REQUIRE(str.complete().equals("hello"));
|
||||
}
|
||||
|
||||
SECTION("SizeIncreases") {
|
||||
DynamicMemoryPool memoryPool(5);
|
||||
|
||||
StringBuilder str = memoryPool.startString();
|
||||
REQUIRE(0 == memoryPool.size());
|
||||
|
||||
str.append('h');
|
||||
REQUIRE(1 == memoryPool.size());
|
||||
|
||||
str.complete();
|
||||
REQUIRE(2 == memoryPool.size());
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@ add_executable(JsonArrayTests
|
||||
isNull.cpp
|
||||
iterator.cpp
|
||||
remove.cpp
|
||||
set.cpp
|
||||
size.cpp
|
||||
std_string.cpp
|
||||
subscript.cpp
|
||||
|
@ -102,13 +102,13 @@ TEST_CASE("JsonArray::add()") {
|
||||
|
||||
SECTION("should duplicate char*") {
|
||||
array.add(const_cast<char*>("world"));
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string") {
|
||||
array.add(std::string("world"));
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
@ -120,19 +120,19 @@ TEST_CASE("JsonArray::add()") {
|
||||
|
||||
SECTION("should duplicate serialized(char*)") {
|
||||
array.add(serialized(const_cast<char*>("{}")));
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 2;
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(2);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate serialized(std::string)") {
|
||||
array.add(serialized(std::string("{}")));
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 2;
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(2);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate serialized(std::string)") {
|
||||
array.add(serialized(std::string("\0XX", 3)));
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 3;
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(3);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
}
|
||||
|
@ -1,115 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace Catch::Matchers;
|
||||
|
||||
TEST_CASE("JsonArray::set()") {
|
||||
DynamicJsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
array.add(0);
|
||||
|
||||
SECTION("int") {
|
||||
array.set(0, 123);
|
||||
REQUIRE(123 == array[0].as<int>());
|
||||
REQUIRE(array[0].is<int>());
|
||||
REQUIRE_FALSE(array[0].is<bool>());
|
||||
}
|
||||
|
||||
SECTION("double") {
|
||||
array.set(0, 123.45);
|
||||
REQUIRE(123.45 == array[0].as<double>());
|
||||
REQUIRE(array[0].is<double>());
|
||||
REQUIRE_FALSE(array[0].is<int>());
|
||||
}
|
||||
|
||||
SECTION("bool") {
|
||||
array.set(0, true);
|
||||
REQUIRE(true == array[0].as<bool>());
|
||||
REQUIRE(array[0].is<bool>());
|
||||
REQUIRE_FALSE(array[0].is<int>());
|
||||
}
|
||||
|
||||
SECTION("const char*") {
|
||||
array.set(0, "hello");
|
||||
REQUIRE_THAT(array[0].as<const char*>(), Equals("hello"));
|
||||
REQUIRE(array[0].is<const char*>());
|
||||
REQUIRE_FALSE(array[0].is<int>());
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("set()") {
|
||||
int i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "world");
|
||||
|
||||
array.add("hello");
|
||||
array.set(0, vla);
|
||||
|
||||
REQUIRE(std::string("world") == array[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("nested array") {
|
||||
DynamicJsonDocument doc2;
|
||||
JsonArray arr = doc2.to<JsonArray>();
|
||||
|
||||
array.set(0, arr);
|
||||
|
||||
REQUIRE(arr == array[0].as<JsonArray>());
|
||||
REQUIRE(array[0].is<JsonArray>());
|
||||
REQUIRE_FALSE(array[0].is<int>());
|
||||
}
|
||||
|
||||
SECTION("nested object") {
|
||||
DynamicJsonDocument doc2;
|
||||
JsonObject obj = doc2.to<JsonObject>();
|
||||
|
||||
array.set(0, obj);
|
||||
|
||||
REQUIRE(obj == array[0].as<JsonObject>());
|
||||
REQUIRE(array[0].is<JsonObject>());
|
||||
REQUIRE_FALSE(array[0].is<int>());
|
||||
}
|
||||
|
||||
SECTION("array subscript") {
|
||||
DynamicJsonDocument doc2;
|
||||
JsonArray arr = doc2.to<JsonArray>();
|
||||
arr.add("hello");
|
||||
|
||||
array.set(0, arr[0]);
|
||||
|
||||
REQUIRE_THAT(array[0].as<char*>(), Equals("hello"));
|
||||
}
|
||||
|
||||
SECTION("object subscript") {
|
||||
DynamicJsonDocument doc2;
|
||||
JsonObject obj = doc2.to<JsonObject>();
|
||||
obj["x"] = "hello";
|
||||
|
||||
array.set(0, obj["x"]);
|
||||
|
||||
REQUIRE_THAT(array[0].as<char*>(), Equals("hello"));
|
||||
}
|
||||
|
||||
SECTION("should not duplicate const char*") {
|
||||
array.set(0, "world");
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate char*") {
|
||||
array.set(0, const_cast<char*>("world"));
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string") {
|
||||
array.set(0, std::string("world"));
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
}
|
@ -21,15 +21,7 @@ TEST_CASE("JsonArray::size()") {
|
||||
REQUIRE(2U == array.size());
|
||||
}
|
||||
|
||||
SECTION("remains the same after set()") {
|
||||
array.add("hello");
|
||||
REQUIRE(1U == array.size());
|
||||
|
||||
array.set(0, "hello");
|
||||
REQUIRE(1U == array.size());
|
||||
}
|
||||
|
||||
SECTION("remains the same after assigment") {
|
||||
SECTION("remains the same after replacing an element") {
|
||||
array.add("hello");
|
||||
REQUIRE(1U == array.size());
|
||||
|
||||
|
@ -21,14 +21,6 @@ TEST_CASE("std::string") {
|
||||
REQUIRE(std::string("hello") == array[0]);
|
||||
}
|
||||
|
||||
SECTION("set()") {
|
||||
std::string value("world");
|
||||
array.add("hello");
|
||||
array.set(0, value);
|
||||
eraseString(value);
|
||||
REQUIRE(std::string("world") == array[0]);
|
||||
}
|
||||
|
||||
SECTION("operator[]") {
|
||||
std::string value("world");
|
||||
array.add("hello");
|
||||
|
@ -105,13 +105,13 @@ TEST_CASE("JsonArray::operator[]") {
|
||||
|
||||
SECTION("should duplicate char*") {
|
||||
array[0] = const_cast<char*>("world");
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string") {
|
||||
array[0] = std::string("world");
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,15 @@ void testStringification(DeserializationError error, std::string expected) {
|
||||
}
|
||||
|
||||
void testBoolification(DeserializationError error, bool expected) {
|
||||
// DeserializationError on left-hand side
|
||||
CHECK(error == expected);
|
||||
CHECK(error != !expected);
|
||||
CHECK(!error == !expected);
|
||||
|
||||
// DeserializationError on right-hand side
|
||||
CHECK(expected == error);
|
||||
CHECK(!expected != error);
|
||||
CHECK(!expected == !error);
|
||||
}
|
||||
|
||||
#define TEST_STRINGIFICATION(symbol) \
|
||||
@ -38,15 +46,92 @@ TEST_CASE("DeserializationError") {
|
||||
TEST_BOOLIFICATION(NotSupported, true);
|
||||
}
|
||||
|
||||
SECTION("ostream code") {
|
||||
SECTION("ostream DeserializationError") {
|
||||
std::stringstream s;
|
||||
s << DeserializationError(DeserializationError::InvalidInput);
|
||||
REQUIRE(s.str() == "InvalidInput");
|
||||
}
|
||||
|
||||
SECTION("ostream code") {
|
||||
SECTION("ostream DeserializationError::Code") {
|
||||
std::stringstream s;
|
||||
s << DeserializationError::InvalidInput;
|
||||
REQUIRE(s.str() == "InvalidInput");
|
||||
}
|
||||
|
||||
SECTION("out of range") {
|
||||
int code = 666;
|
||||
DeserializationError err(
|
||||
*reinterpret_cast<DeserializationError::Code*>(&code));
|
||||
REQUIRE(err.c_str() == std::string("???"));
|
||||
}
|
||||
|
||||
SECTION("switch") {
|
||||
DeserializationError err = DeserializationError::InvalidInput;
|
||||
switch (err.code()) {
|
||||
case DeserializationError::InvalidInput:
|
||||
SUCCEED();
|
||||
break;
|
||||
default:
|
||||
FAIL();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Comparisons") {
|
||||
DeserializationError invalidInput(DeserializationError::InvalidInput);
|
||||
DeserializationError ok(DeserializationError::Ok);
|
||||
|
||||
SECTION("DeserializationError == bool") {
|
||||
REQUIRE(invalidInput == true);
|
||||
REQUIRE(ok == false);
|
||||
}
|
||||
|
||||
SECTION("bool == DeserializationError") {
|
||||
REQUIRE(true == invalidInput);
|
||||
REQUIRE(false == ok);
|
||||
}
|
||||
|
||||
SECTION("DeserializationError != bool") {
|
||||
REQUIRE(invalidInput != false);
|
||||
REQUIRE(ok != true);
|
||||
}
|
||||
|
||||
SECTION("bool != DeserializationError") {
|
||||
REQUIRE(false != invalidInput);
|
||||
REQUIRE(true != ok);
|
||||
}
|
||||
|
||||
SECTION("Negations") {
|
||||
REQUIRE(!invalidInput == false);
|
||||
REQUIRE(!ok == true);
|
||||
}
|
||||
|
||||
SECTION("DeserializationError == Code") {
|
||||
REQUIRE(invalidInput == DeserializationError::InvalidInput);
|
||||
REQUIRE(ok == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("Code == DeserializationError") {
|
||||
REQUIRE(DeserializationError::InvalidInput == invalidInput);
|
||||
REQUIRE(DeserializationError::Ok == ok);
|
||||
}
|
||||
|
||||
SECTION("DeserializationError != Code") {
|
||||
REQUIRE(invalidInput != DeserializationError::Ok);
|
||||
REQUIRE(ok != DeserializationError::InvalidInput);
|
||||
}
|
||||
|
||||
SECTION("Code != DeserializationError") {
|
||||
REQUIRE(DeserializationError::Ok != invalidInput);
|
||||
REQUIRE(DeserializationError::InvalidInput != ok);
|
||||
}
|
||||
|
||||
SECTION("DeserializationError == DeserializationError") {
|
||||
REQUIRE_FALSE(invalidInput == ok);
|
||||
}
|
||||
|
||||
SECTION("DeserializationError != DeserializationError") {
|
||||
REQUIRE(invalidInput != ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") {
|
||||
}
|
||||
|
||||
SECTION("TooSmallBufferForArrayWithOneValue") {
|
||||
StaticJsonDocument<JSON_ARRAY_SIZE(1) - 1> doc;
|
||||
StaticJsonDocument<JSON_ARRAY_SIZE(0)> doc;
|
||||
char input[] = "[1]";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
@ -34,7 +34,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") {
|
||||
}
|
||||
|
||||
SECTION("TooSmallBufferForArrayWithNestedObject") {
|
||||
StaticJsonDocument<JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(0) - 1> doc;
|
||||
StaticJsonDocument<JSON_ARRAY_SIZE(0) + JSON_OBJECT_SIZE(0)> doc;
|
||||
char input[] = "[{}]";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
@ -56,7 +56,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") {
|
||||
|
||||
deserializeJson(doc, " [ \"1234567\" ] ");
|
||||
|
||||
REQUIRE(JSON_ARRAY_SIZE(1) + sizeof("1234567") == doc.memoryUsage());
|
||||
REQUIRE(JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(8) == doc.memoryUsage());
|
||||
// note: we use a string of 8 bytes to be sure that the StaticMemoryPool
|
||||
// will not insert bytes to enforce alignement
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") {
|
||||
}
|
||||
|
||||
SECTION("TooSmallBufferForObjectWithOneValue") {
|
||||
StaticJsonDocument<JSON_OBJECT_SIZE(1) - 1> doc;
|
||||
StaticJsonDocument<JSON_OBJECT_SIZE(0)> doc;
|
||||
char input[] = "{\"a\":1}";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
@ -34,7 +34,7 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") {
|
||||
}
|
||||
|
||||
SECTION("TooSmallBufferForObjectWithNestedObject") {
|
||||
StaticJsonDocument<JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(0) - 1> doc;
|
||||
StaticJsonDocument<JSON_OBJECT_SIZE(0) + JSON_ARRAY_SIZE(0)> doc;
|
||||
char input[] = "{\"a\":[]}";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
@ -10,7 +10,7 @@ using namespace Catch::Matchers;
|
||||
namespace my {
|
||||
using ARDUINOJSON_NAMESPACE::isinf;
|
||||
using ARDUINOJSON_NAMESPACE::isnan;
|
||||
}
|
||||
} // namespace my
|
||||
|
||||
TEST_CASE("deserializeJson(DynamicJsonDocument&)") {
|
||||
DynamicJsonDocument doc;
|
||||
@ -223,4 +223,10 @@ TEST_CASE("deserializeJson(DynamicJsonDocument&)") {
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Repeated object key") {
|
||||
DeserializationError err = deserializeJson(doc, "{a:{b:{c:1}},a:2}");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
}
|
||||
|
@ -92,4 +92,70 @@ TEST_CASE("DynamicJsonDocument") {
|
||||
REQUIRE(json == "{\"hello\":\"world\"}");
|
||||
REQUIRE(ddoc.nestingLimit == 42);
|
||||
}
|
||||
|
||||
SECTION("memoryUsage()") {
|
||||
SECTION("Increases after adding value to array") {
|
||||
JsonArray arr = doc.to<JsonArray>();
|
||||
|
||||
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0));
|
||||
arr.add(42);
|
||||
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1));
|
||||
arr.add(43);
|
||||
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(2));
|
||||
}
|
||||
|
||||
SECTION("Decreases after removing value from array") {
|
||||
JsonArray arr = doc.to<JsonArray>();
|
||||
arr.add(42);
|
||||
arr.add(43);
|
||||
|
||||
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(2));
|
||||
arr.remove(1);
|
||||
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1));
|
||||
arr.remove(0);
|
||||
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0));
|
||||
}
|
||||
|
||||
SECTION("Increases after adding value to object") {
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
|
||||
obj["a"] = 1;
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1));
|
||||
obj["b"] = 2;
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2));
|
||||
}
|
||||
|
||||
SECTION("Decreases after removing value from object") {
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj["a"] = 1;
|
||||
obj["b"] = 2;
|
||||
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2));
|
||||
obj.remove("a");
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1));
|
||||
obj.remove("b");
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
|
||||
}
|
||||
|
||||
SECTION("Decreases after removing nested object from array") {
|
||||
JsonArray arr = doc.to<JsonArray>();
|
||||
JsonObject obj = arr.createNestedObject();
|
||||
obj["hello"] = "world";
|
||||
|
||||
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1));
|
||||
arr.remove(0);
|
||||
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0));
|
||||
}
|
||||
|
||||
SECTION("Decreases after removing nested array from object") {
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
JsonArray arr = obj.createNestedArray("hello");
|
||||
arr.add("world");
|
||||
|
||||
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1));
|
||||
obj.remove("hello");
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,10 @@ add_executable(JsonObjectTests
|
||||
createNestedArray.cpp
|
||||
createNestedObject.cpp
|
||||
equals.cpp
|
||||
get.cpp
|
||||
invalid.cpp
|
||||
is.cpp
|
||||
isNull.cpp
|
||||
iterator.cpp
|
||||
remove.cpp
|
||||
set.cpp
|
||||
size.cpp
|
||||
std_string.cpp
|
||||
subscript.cpp
|
||||
|
@ -8,7 +8,7 @@
|
||||
TEST_CASE("JsonObject::containsKey()") {
|
||||
DynamicJsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj.set("hello", 42);
|
||||
obj["hello"] = 42;
|
||||
|
||||
SECTION("returns true only if key is present") {
|
||||
REQUIRE(false == obj.containsKey("world"));
|
||||
|
@ -22,4 +22,17 @@ TEST_CASE("JsonObject::createNestedObject()") {
|
||||
obj.createNestedObject(vla);
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("releases memory from nested object") {
|
||||
obj.createNestedObject(std::string("a"))
|
||||
.createNestedObject(std::string("b"))
|
||||
.set(std::string("c"))
|
||||
.set(1);
|
||||
// {"a":{"b":{"c":1}}}
|
||||
REQUIRE(doc.memoryUsage() ==
|
||||
3 * JSON_OBJECT_SIZE(1) + 3 * JSON_STRING_SIZE(2));
|
||||
|
||||
obj.createNestedObject(std::string("a"));
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2));
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace Catch::Matchers;
|
||||
|
||||
TEST_CASE("JsonObject::get()") {
|
||||
DynamicJsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
|
||||
SECTION("get<const char*>(const char*)") {
|
||||
obj.set("hello", "world");
|
||||
const char* value = obj.get<const char*>("hello");
|
||||
REQUIRE_THAT(value, Equals("world"));
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("get<const char*>(VLA)") {
|
||||
obj.set("hello", "world");
|
||||
int i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
REQUIRE(std::string("world") == obj.get<char*>(vla));
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("works on JsonObjectConst") {
|
||||
obj.set("hello", "world");
|
||||
const char* value =
|
||||
static_cast<JsonObjectConst>(obj).get<const char*>("hello");
|
||||
REQUIRE_THAT(value, Equals("world"));
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ TEST_CASE("JsonObject::invalid()") {
|
||||
}
|
||||
|
||||
SECTION("AddFails") {
|
||||
obj.set("hello", "world");
|
||||
obj["hello"] = "world";
|
||||
REQUIRE(0 == obj.size());
|
||||
}
|
||||
|
||||
|
@ -1,28 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonObject::is<T>()") {
|
||||
DynamicJsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj["int"] = 42;
|
||||
obj["str"] = "hello";
|
||||
|
||||
SECTION("is<int>(const char*)") {
|
||||
REQUIRE(true == obj.is<int>("int"));
|
||||
REQUIRE(false == obj.is<int>("str"));
|
||||
}
|
||||
|
||||
#if HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("is<T>(VLA)") {
|
||||
int i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "int");
|
||||
|
||||
REQUIRE(true == obj.is<int>(vla));
|
||||
}
|
||||
#endif
|
||||
}
|
@ -19,18 +19,47 @@ TEST_CASE("JsonObject::remove()") {
|
||||
obj.remove("a");
|
||||
serializeJson(obj, result);
|
||||
REQUIRE("{\"b\":1,\"c\":2}" == result);
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2));
|
||||
}
|
||||
|
||||
SECTION("Remove middle") {
|
||||
obj.remove("b");
|
||||
serializeJson(obj, result);
|
||||
REQUIRE("{\"a\":0,\"c\":2}" == result);
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2));
|
||||
}
|
||||
|
||||
SECTION("Remove last") {
|
||||
obj.remove("c");
|
||||
serializeJson(obj, result);
|
||||
REQUIRE("{\"a\":0,\"b\":1}" == result);
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2));
|
||||
}
|
||||
|
||||
SECTION("Release value string memory") {
|
||||
obj["c"] = std::string("Copy me!");
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(3) + JSON_STRING_SIZE(9));
|
||||
|
||||
obj.remove("c");
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2));
|
||||
}
|
||||
|
||||
SECTION("Release key string memory") {
|
||||
obj[std::string("Copy me!")] = 42;
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(4) + JSON_STRING_SIZE(9));
|
||||
|
||||
obj.remove("Copy me!");
|
||||
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(3));
|
||||
}
|
||||
|
||||
SECTION("Release raw string memory") {
|
||||
obj["c"] = serialized(std::string("Copy me!"));
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(3) + JSON_STRING_SIZE(8));
|
||||
|
||||
obj.remove("c");
|
||||
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,174 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
#include <string>
|
||||
|
||||
TEST_CASE("JsonObject::set()") {
|
||||
DynamicJsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
|
||||
SECTION("int") {
|
||||
obj.set("hello", 123);
|
||||
|
||||
REQUIRE(123 == obj["hello"].as<int>());
|
||||
REQUIRE(obj["hello"].is<int>());
|
||||
REQUIRE_FALSE(obj["hello"].is<bool>());
|
||||
}
|
||||
|
||||
SECTION("double") {
|
||||
obj.set("hello", 123.45);
|
||||
|
||||
REQUIRE(123.45 == obj["hello"].as<double>());
|
||||
REQUIRE(obj["hello"].is<double>());
|
||||
REQUIRE_FALSE(obj["hello"].is<bool>());
|
||||
}
|
||||
|
||||
SECTION("bool") {
|
||||
obj.set("hello", true);
|
||||
|
||||
REQUIRE(obj["hello"].as<bool>());
|
||||
REQUIRE(obj["hello"].is<bool>());
|
||||
REQUIRE_FALSE(obj["hello"].is<long>());
|
||||
}
|
||||
|
||||
SECTION("const char*") {
|
||||
obj.set("hello", "h3110");
|
||||
|
||||
REQUIRE(std::string("h3110") == obj["hello"].as<const char*>());
|
||||
REQUIRE(obj["hello"].is<const char*>());
|
||||
REQUIRE_FALSE(obj["hello"].is<long>());
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("key is a VLA") {
|
||||
int i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
obj.set(vla, "world");
|
||||
|
||||
REQUIRE(std::string("world") == obj["hello"]);
|
||||
}
|
||||
|
||||
SECTION("value is a VLA") {
|
||||
int i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "world");
|
||||
|
||||
obj.set("hello", vla);
|
||||
|
||||
REQUIRE(std::string("world") == obj["hello"]);
|
||||
}
|
||||
|
||||
SECTION("key and value are VLAs") {
|
||||
int i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "world");
|
||||
|
||||
obj.set(vla, vla);
|
||||
|
||||
REQUIRE(std::string("world") == obj["world"]);
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("nested array") {
|
||||
DynamicJsonDocument doc2;
|
||||
JsonArray arr = doc2.to<JsonArray>();
|
||||
|
||||
obj.set("hello", arr);
|
||||
|
||||
REQUIRE(arr == obj["hello"].as<JsonArray>());
|
||||
REQUIRE(obj["hello"].is<JsonArray>());
|
||||
REQUIRE_FALSE(obj["hello"].is<JsonObject>());
|
||||
}
|
||||
|
||||
SECTION("nested object") {
|
||||
DynamicJsonDocument doc2;
|
||||
JsonObject obj2 = doc2.to<JsonObject>();
|
||||
|
||||
obj.set("hello", obj2);
|
||||
|
||||
REQUIRE(obj2 == obj["hello"].as<JsonObject>());
|
||||
REQUIRE(obj["hello"].is<JsonObject>());
|
||||
REQUIRE_FALSE(obj["hello"].is<JsonArray>());
|
||||
}
|
||||
|
||||
SECTION("array subscript") {
|
||||
DynamicJsonDocument doc2;
|
||||
JsonArray arr = doc2.to<JsonArray>();
|
||||
arr.add(42);
|
||||
|
||||
obj.set("a", arr[0]);
|
||||
|
||||
REQUIRE(42 == obj["a"]);
|
||||
}
|
||||
|
||||
SECTION("object subscript") {
|
||||
DynamicJsonDocument doc2;
|
||||
JsonObject obj2 = doc2.to<JsonObject>();
|
||||
obj2.set("x", 42);
|
||||
|
||||
obj.set("a", obj2["x"]);
|
||||
|
||||
REQUIRE(42 == obj["a"]);
|
||||
}
|
||||
|
||||
SECTION("returns true when allocation succeeds") {
|
||||
StaticJsonDocument<JSON_OBJECT_SIZE(1) + 15> doc2;
|
||||
JsonObject obj2 = doc2.to<JsonObject>();
|
||||
|
||||
REQUIRE(true == obj2.set(std::string("hello"), std::string("world")));
|
||||
}
|
||||
|
||||
SECTION("returns false when allocation fails") {
|
||||
StaticJsonDocument<JSON_OBJECT_SIZE(1) + 10> doc2;
|
||||
JsonObject obj2 = doc2.to<JsonObject>();
|
||||
|
||||
REQUIRE(false == obj2.set(std::string("hello"), std::string("world")));
|
||||
}
|
||||
|
||||
SECTION("should not duplicate const char*") {
|
||||
obj.set("hello", "world");
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate char* value") {
|
||||
obj.set("hello", const_cast<char*>("world"));
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate char* key") {
|
||||
obj.set(const_cast<char*>("hello"), "world");
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate char* key&value") {
|
||||
obj.set(const_cast<char*>("hello"), const_cast<char*>("world"));
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12;
|
||||
REQUIRE(expectedSize <= doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string value") {
|
||||
obj.set("hello", std::string("world"));
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string key") {
|
||||
obj.set(std::string("hello"), "world");
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string key&value") {
|
||||
obj.set(std::string("hello"), std::string("world"));
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12;
|
||||
REQUIRE(expectedSize <= doc.memoryUsage());
|
||||
}
|
||||
}
|
@ -15,12 +15,12 @@ TEST_CASE("JsonObject::size()") {
|
||||
}
|
||||
|
||||
SECTION("increases when values are added") {
|
||||
obj.set("hello", 42);
|
||||
obj["hello"] = 42;
|
||||
REQUIRE(1 == obj.size());
|
||||
}
|
||||
|
||||
SECTION("decreases when values are removed") {
|
||||
obj.set("hello", 42);
|
||||
obj["hello"] = 42;
|
||||
obj.remove("hello");
|
||||
REQUIRE(0 == obj.size());
|
||||
}
|
||||
|
@ -31,70 +31,6 @@ TEST_CASE("std::string") {
|
||||
REQUIRE(std::string("value") == obj[std::string("key")]);
|
||||
}
|
||||
|
||||
SECTION("set(key)") {
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
std::string key("hello");
|
||||
obj.set(key, "world");
|
||||
eraseString(key);
|
||||
REQUIRE(std::string("world") == obj["hello"]);
|
||||
}
|
||||
|
||||
SECTION("set(value)") {
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
std::string value("world");
|
||||
obj.set("hello", value);
|
||||
eraseString(value);
|
||||
REQUIRE(std::string("world") == obj["hello"]);
|
||||
}
|
||||
|
||||
SECTION("set(key,value)") {
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
std::string key("hello");
|
||||
std::string value("world");
|
||||
obj.set(key, value);
|
||||
eraseString(key);
|
||||
eraseString(value);
|
||||
REQUIRE(std::string("world") == obj["hello"]);
|
||||
}
|
||||
|
||||
SECTION("set(JsonArraySubscript)") {
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
DynamicJsonDocument doc2;
|
||||
JsonArray arr = doc2.to<JsonArray>();
|
||||
arr.add("world");
|
||||
|
||||
obj.set(std::string("hello"), arr[0]);
|
||||
|
||||
REQUIRE(std::string("world") == obj["hello"]);
|
||||
}
|
||||
|
||||
SECTION("set(JsonObjectSubscript)") {
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
DynamicJsonDocument doc2;
|
||||
JsonObject obj2 = doc2.to<JsonObject>();
|
||||
obj2.set("x", "world");
|
||||
|
||||
obj.set(std::string("hello"), obj2["x"]);
|
||||
|
||||
REQUIRE(std::string("world") == obj["hello"]);
|
||||
}
|
||||
|
||||
SECTION("get<T>()") {
|
||||
char json[] = "{\"key\":\"value\"}";
|
||||
deserializeJson(doc, json);
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(std::string("value") == obj.get<const char *>(std::string("key")));
|
||||
}
|
||||
|
||||
SECTION("is<T>()") {
|
||||
char json[] = "{\"key\":\"value\"}";
|
||||
deserializeJson(doc, json);
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(true == obj.is<const char *>(std::string("key")));
|
||||
}
|
||||
|
||||
SECTION("createNestedObject()") {
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
std::string key = "key";
|
||||
|
@ -86,7 +86,7 @@ TEST_CASE("JsonObject::operator[]") {
|
||||
SECTION("object subscript") {
|
||||
DynamicJsonDocument doc2;
|
||||
JsonObject obj2 = doc2.to<JsonObject>();
|
||||
obj2.set("x", 42);
|
||||
obj2["x"] = 42;
|
||||
|
||||
obj["a"] = obj2["x"];
|
||||
|
||||
@ -107,40 +107,56 @@ TEST_CASE("JsonObject::operator[]") {
|
||||
|
||||
SECTION("should duplicate char* value") {
|
||||
obj["hello"] = const_cast<char*>("world");
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate char* key") {
|
||||
obj[const_cast<char*>("hello")] = "world";
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate char* key&value") {
|
||||
obj[const_cast<char*>("hello")] = const_cast<char*>("world");
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12;
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(6);
|
||||
REQUIRE(expectedSize <= doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string value") {
|
||||
obj["hello"] = std::string("world");
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string key") {
|
||||
obj[std::string("hello")] = "world";
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string key&value") {
|
||||
obj[std::string("hello")] = std::string("world");
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12;
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(6);
|
||||
REQUIRE(expectedSize <= doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should release string memory when overiding value") {
|
||||
obj["hello"] = std::string("world");
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6));
|
||||
|
||||
obj["hello"] = 42;
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1));
|
||||
}
|
||||
|
||||
SECTION("should release nested array memory when overiding value") {
|
||||
obj.createNestedArray("hello").add("world");
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(1));
|
||||
|
||||
obj["hello"] = 42;
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1));
|
||||
}
|
||||
|
||||
SECTION("should ignore null key") {
|
||||
// object must have a value to make a call to strcmp()
|
||||
obj["dummy"] = 42;
|
||||
|
@ -26,7 +26,7 @@ TEST_CASE("serializeJson(JsonObject)") {
|
||||
|
||||
SECTION("TwoStrings") {
|
||||
obj["key1"] = "value1";
|
||||
obj.set("key2", "value2");
|
||||
obj["key2"] = "value2";
|
||||
|
||||
check(obj, "{\"key1\":\"value1\",\"key2\":\"value2\"}");
|
||||
}
|
||||
@ -64,31 +64,31 @@ TEST_CASE("serializeJson(JsonObject)") {
|
||||
|
||||
SECTION("TwoIntegers") {
|
||||
obj["a"] = 1;
|
||||
obj.set("b", 2);
|
||||
obj["b"] = 2;
|
||||
check(obj, "{\"a\":1,\"b\":2}");
|
||||
}
|
||||
|
||||
SECTION("serialized(const char*)") {
|
||||
obj["a"] = serialized("[1,2]");
|
||||
obj.set("b", serialized("[4,5]"));
|
||||
obj["b"] = serialized("[4,5]");
|
||||
check(obj, "{\"a\":[1,2],\"b\":[4,5]}");
|
||||
}
|
||||
|
||||
SECTION("Two doubles") {
|
||||
obj["a"] = 12.34;
|
||||
obj.set("b", 56.78);
|
||||
obj["b"] = 56.78;
|
||||
check(obj, "{\"a\":12.34,\"b\":56.78}");
|
||||
}
|
||||
|
||||
SECTION("TwoNull") {
|
||||
obj["a"] = static_cast<char *>(0);
|
||||
obj.set("b", static_cast<char *>(0));
|
||||
obj["b"] = static_cast<char *>(0);
|
||||
check(obj, "{\"a\":null,\"b\":null}");
|
||||
}
|
||||
|
||||
SECTION("TwoBooleans") {
|
||||
obj["a"] = true;
|
||||
obj.set("b", false);
|
||||
obj["b"] = false;
|
||||
check(obj, "{\"a\":true,\"b\":false}");
|
||||
}
|
||||
|
||||
@ -98,7 +98,7 @@ TEST_CASE("serializeJson(JsonObject)") {
|
||||
|
||||
obj.createNestedArray("a");
|
||||
obj["b"] = b.to<JsonArray>();
|
||||
obj.set("c", c.to<JsonArray>());
|
||||
obj["c"] = c.to<JsonArray>();
|
||||
|
||||
check(obj, "{\"a\":[],\"b\":[],\"c\":[]}");
|
||||
}
|
||||
@ -109,7 +109,7 @@ TEST_CASE("serializeJson(JsonObject)") {
|
||||
|
||||
obj.createNestedObject("a");
|
||||
obj["b"] = b.to<JsonObject>();
|
||||
obj.set("c", c.to<JsonObject>());
|
||||
obj["c"] = c.to<JsonObject>();
|
||||
|
||||
check(obj, "{\"a\":{},\"b\":{},\"c\":{}}");
|
||||
}
|
||||
|
@ -6,12 +6,14 @@ add_executable(JsonVariantTests
|
||||
as.cpp
|
||||
compare.cpp
|
||||
copy.cpp
|
||||
get.cpp
|
||||
is.cpp
|
||||
isnull.cpp
|
||||
misc.cpp
|
||||
or.cpp
|
||||
set_get.cpp
|
||||
set.cpp
|
||||
subscript.cpp
|
||||
to.cpp
|
||||
undefined.cpp
|
||||
)
|
||||
|
||||
|
@ -108,6 +108,16 @@ TEST_CASE("JsonVariant::as()") {
|
||||
REQUIRE(variant.as<std::string>() == std::string("hello"));
|
||||
}
|
||||
|
||||
SECTION("set(std::string(\"4.2\"))") {
|
||||
variant.set(std::string("4.2"));
|
||||
|
||||
REQUIRE(variant.as<bool>() == true);
|
||||
REQUIRE(variant.as<long>() == 4L);
|
||||
REQUIRE(variant.as<double>() == 4.2);
|
||||
REQUIRE(variant.as<char*>() == std::string("4.2"));
|
||||
REQUIRE(variant.as<std::string>() == std::string("4.2"));
|
||||
}
|
||||
|
||||
SECTION("set(\"true\")") {
|
||||
variant.set("true");
|
||||
|
||||
|
@ -216,7 +216,7 @@ TEST_CASE("JsonVariant comparisons") {
|
||||
JsonVariant variant2 = doc2.to<JsonVariant>();
|
||||
JsonVariant variant3 = doc3.to<JsonVariant>();
|
||||
|
||||
SECTION("IntegerInVariant") {
|
||||
SECTION("Variants containing integers") {
|
||||
variant1.set(42);
|
||||
variant2.set(42);
|
||||
variant3.set(666);
|
||||
@ -228,7 +228,7 @@ TEST_CASE("JsonVariant comparisons") {
|
||||
REQUIRE_FALSE(variant1 == variant3);
|
||||
}
|
||||
|
||||
SECTION("StringInVariant") {
|
||||
SECTION("Variants containing linked strings") {
|
||||
variant1.set("0hello" + 1); // make sure they have
|
||||
variant2.set("1hello" + 1); // different addresses
|
||||
variant3.set("world");
|
||||
@ -240,7 +240,19 @@ TEST_CASE("JsonVariant comparisons") {
|
||||
REQUIRE_FALSE(variant1 == variant3);
|
||||
}
|
||||
|
||||
SECTION("DoubleInVariant") {
|
||||
SECTION("Variants containing owned strings") {
|
||||
variant1.set(std::string("hello"));
|
||||
variant2.set(std::string("hello"));
|
||||
variant3.set(std::string("world"));
|
||||
|
||||
REQUIRE(variant1 == variant2);
|
||||
REQUIRE_FALSE(variant1 != variant2);
|
||||
|
||||
REQUIRE(variant1 != variant3);
|
||||
REQUIRE_FALSE(variant1 == variant3);
|
||||
}
|
||||
|
||||
SECTION("Variants containing double") {
|
||||
variant1.set(42.0);
|
||||
variant2.set(42.0);
|
||||
variant3.set(666.0);
|
||||
|
@ -13,22 +13,24 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
|
||||
|
||||
SECTION("stores JsonArray by copy") {
|
||||
JsonArray arr = doc2.to<JsonArray>();
|
||||
arr.add(42);
|
||||
JsonObject obj = arr.createNestedObject();
|
||||
obj["hello"] = "world";
|
||||
|
||||
var1.set(arr);
|
||||
|
||||
arr[0] = 666;
|
||||
REQUIRE(var1.as<std::string>() == "[42]");
|
||||
REQUIRE(var1.as<std::string>() == "[{\"hello\":\"world\"}]");
|
||||
}
|
||||
|
||||
SECTION("stores JsonObject by copy") {
|
||||
JsonObject obj = doc2.to<JsonObject>();
|
||||
obj["value"] = 42;
|
||||
JsonArray arr = obj.createNestedArray("value");
|
||||
arr.add(42);
|
||||
|
||||
var1.set(obj);
|
||||
|
||||
obj["value"] = 666;
|
||||
REQUIRE(var1.as<std::string>() == "{\"value\":42}");
|
||||
REQUIRE(var1.as<std::string>() == "{\"value\":[42]}");
|
||||
}
|
||||
|
||||
SECTION("stores const char* by reference") {
|
||||
@ -45,20 +47,20 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
|
||||
var1.set(str);
|
||||
var2.set(var1);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == 8);
|
||||
REQUIRE(doc2.memoryUsage() == 8);
|
||||
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
}
|
||||
|
||||
SECTION("stores std::string by copy") {
|
||||
var1.set(std::string("hello!!"));
|
||||
var2.set(var1);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == 8);
|
||||
REQUIRE(doc2.memoryUsage() == 8);
|
||||
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
}
|
||||
|
||||
SECTION("stores Serialized<const char*> by reference") {
|
||||
var1.set(serialized("hello!!", 8));
|
||||
var1.set(serialized("hello!!", JSON_STRING_SIZE(8)));
|
||||
var2.set(var1);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == 0);
|
||||
@ -70,15 +72,34 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
|
||||
var1.set(serialized(str, 8));
|
||||
var2.set(var1);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == 8);
|
||||
REQUIRE(doc2.memoryUsage() == 8);
|
||||
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
}
|
||||
|
||||
SECTION("stores Serialized<std::string> by copy") {
|
||||
var1.set(serialized(std::string("hello!!!")));
|
||||
var2.set(var1);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == 8);
|
||||
REQUIRE(doc2.memoryUsage() == 8);
|
||||
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
}
|
||||
|
||||
SECTION("releases string memory when replacing with null") {
|
||||
var1.set(std::string("hello"));
|
||||
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(6));
|
||||
|
||||
var1.set(JsonVariant());
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
SECTION("releases string memory when replacing with iteger") {
|
||||
var1.set(std::string("hello"));
|
||||
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(6));
|
||||
|
||||
var2.set(42);
|
||||
var1.set(var2);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == 0);
|
||||
}
|
||||
}
|
||||
|
@ -138,87 +138,3 @@ TEST_CASE("JsonVariant set()/get()") {
|
||||
checkValue<JsonObject>(object);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant and strings") {
|
||||
DynamicJsonDocument doc;
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
|
||||
SECTION("stores const char* by reference") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(static_cast<const char *>(str));
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "world");
|
||||
}
|
||||
|
||||
SECTION("stores char* by copy") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(str);
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
}
|
||||
|
||||
SECTION("stores unsigned char* by copy") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(reinterpret_cast<unsigned char *>(str));
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
}
|
||||
|
||||
SECTION("stores signed char* by copy") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(reinterpret_cast<signed char *>(str));
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("stores VLA by copy") {
|
||||
int n = 16;
|
||||
char str[n];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(str);
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("stores std::string by copy") {
|
||||
std::string str;
|
||||
|
||||
str = "hello";
|
||||
variant.set(str);
|
||||
str.replace(0, 5, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant with not enough memory") {
|
||||
StaticJsonDocument<1> doc;
|
||||
|
||||
JsonVariant v = doc.to<JsonVariant>();
|
||||
|
||||
SECTION("std::string") {
|
||||
v.set(std::string("hello"));
|
||||
REQUIRE(v.isNull());
|
||||
}
|
||||
|
||||
SECTION("Serialized<std::string>") {
|
||||
v.set(serialized(std::string("hello")));
|
||||
REQUIRE(v.isNull());
|
||||
}
|
||||
}
|
146
test/JsonVariant/set.cpp
Normal file
146
test/JsonVariant/set.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonVariant and strings") {
|
||||
DynamicJsonDocument doc;
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
|
||||
SECTION("stores const char* by reference") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(static_cast<const char *>(str));
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "world");
|
||||
}
|
||||
|
||||
SECTION("stores char* by copy") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(str);
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
}
|
||||
|
||||
SECTION("stores unsigned char* by copy") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(reinterpret_cast<unsigned char *>(str));
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
}
|
||||
|
||||
SECTION("stores signed char* by copy") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(reinterpret_cast<signed char *>(str));
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("stores VLA by copy") {
|
||||
int n = 16;
|
||||
char str[n];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(str);
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("stores std::string by copy") {
|
||||
std::string str;
|
||||
|
||||
str = "hello";
|
||||
variant.set(str);
|
||||
str.replace(0, 5, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant::set() should release string memory") {
|
||||
DynamicJsonDocument doc;
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
|
||||
variant.set(std::string("hello"));
|
||||
REQUIRE(doc.memoryUsage() == JSON_STRING_SIZE(6));
|
||||
|
||||
SECTION("int") {
|
||||
variant.set(-42);
|
||||
REQUIRE(doc.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
SECTION("unsigned int") {
|
||||
variant.set(42U);
|
||||
REQUIRE(doc.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
SECTION("bool") {
|
||||
variant.set(true);
|
||||
REQUIRE(doc.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
SECTION("float") {
|
||||
variant.set(3.14);
|
||||
REQUIRE(doc.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
SECTION("const char*") {
|
||||
variant.set("hello");
|
||||
REQUIRE(doc.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
SECTION("std::string") {
|
||||
variant.set(std::string("X"));
|
||||
REQUIRE(doc.memoryUsage() == JSON_STRING_SIZE(2));
|
||||
}
|
||||
|
||||
SECTION("SerializedValue<const char*>") {
|
||||
variant.set(serialized("[42]"));
|
||||
REQUIRE(doc.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
SECTION("SerializedValue<std::string>") {
|
||||
variant.set(serialized(std::string("42")));
|
||||
REQUIRE(doc.memoryUsage() == JSON_STRING_SIZE(2));
|
||||
}
|
||||
|
||||
SECTION("StringInMemoryPool") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, std::string("{\"A\":\"hello\",\"A\":\"B\"}"));
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
// it stores the key twice, but should release "hello"
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1) + 3 * JSON_STRING_SIZE(2));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant with not enough memory") {
|
||||
StaticJsonDocument<1> doc;
|
||||
|
||||
JsonVariant v = doc.to<JsonVariant>();
|
||||
|
||||
SECTION("std::string") {
|
||||
v.set(std::string("hello"));
|
||||
REQUIRE(v.isNull());
|
||||
}
|
||||
|
||||
SECTION("Serialized<std::string>") {
|
||||
v.set(serialized(std::string("hello")));
|
||||
REQUIRE(v.isNull());
|
||||
}
|
||||
}
|
32
test/JsonVariant/to.cpp
Normal file
32
test/JsonVariant/to.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <stdint.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
static const char* null = 0;
|
||||
|
||||
TEST_CASE("JsonVariant::to<T>() releases string memory") {
|
||||
DynamicJsonDocument doc;
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
|
||||
variant.set(std::string("hello"));
|
||||
REQUIRE(doc.memoryUsage() == JSON_STRING_SIZE(6));
|
||||
|
||||
SECTION("JsonVariant") {
|
||||
variant.to<JsonVariant>();
|
||||
REQUIRE(doc.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
SECTION("JsonArray") {
|
||||
variant.to<JsonArray>();
|
||||
REQUIRE(doc.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
SECTION("JsonObject") {
|
||||
variant.to<JsonObject>();
|
||||
REQUIRE(doc.memoryUsage() == 0);
|
||||
}
|
||||
}
|
@ -110,45 +110,6 @@ TEST_CASE("unsigned char[]") {
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("get()") {
|
||||
unsigned char key[] = "hello";
|
||||
|
||||
DynamicJsonDocument doc;
|
||||
deserializeJson(doc, "{\"hello\":\"world\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
REQUIRE(std::string("world") == obj.get<char*>(key));
|
||||
}
|
||||
|
||||
SECTION("set() key") {
|
||||
unsigned char key[] = "hello";
|
||||
|
||||
DynamicJsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj.set(key, "world");
|
||||
|
||||
REQUIRE(std::string("world") == obj["hello"]);
|
||||
}
|
||||
|
||||
SECTION("set() value") {
|
||||
unsigned char value[] = "world";
|
||||
|
||||
DynamicJsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj.set("hello", value);
|
||||
|
||||
REQUIRE(std::string("world") == obj["hello"]);
|
||||
}
|
||||
|
||||
SECTION("set() key&value") {
|
||||
unsigned char key[] = "world";
|
||||
|
||||
DynamicJsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj.set(key, key);
|
||||
|
||||
REQUIRE(std::string("world") == obj["world"]);
|
||||
}
|
||||
|
||||
SECTION("containsKey()") {
|
||||
unsigned char key[] = "hello";
|
||||
|
||||
@ -169,16 +130,6 @@ TEST_CASE("unsigned char[]") {
|
||||
REQUIRE(0 == obj.size());
|
||||
}
|
||||
|
||||
SECTION("is()") {
|
||||
unsigned char key[] = "hello";
|
||||
|
||||
DynamicJsonDocument doc;
|
||||
deserializeJson(doc, "{\"hello\":42}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(true == obj.is<int>(key));
|
||||
}
|
||||
|
||||
SECTION("createNestedArray()") {
|
||||
unsigned char key[] = "hello";
|
||||
|
||||
@ -228,17 +179,6 @@ TEST_CASE("unsigned char[]") {
|
||||
|
||||
REQUIRE(std::string("world") == arr[0]);
|
||||
}
|
||||
|
||||
SECTION("set()") {
|
||||
unsigned char value[] = "world";
|
||||
|
||||
DynamicJsonDocument doc;
|
||||
JsonArray arr = doc.to<JsonArray>();
|
||||
arr.add("hello");
|
||||
arr.set(0, value);
|
||||
|
||||
REQUIRE(std::string("world") == arr[0]);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("JsonArraySubscript") {
|
||||
|
@ -32,6 +32,47 @@ TEST_CASE("deserialize MsgPack object") {
|
||||
REQUIRE(obj["one"] == 1);
|
||||
REQUIRE(obj["two"] == 2);
|
||||
}
|
||||
|
||||
SECTION("key is str 8") {
|
||||
const char* input = "\x82\xd9\x03one\x01\xd9\x03two\x02";
|
||||
|
||||
DeserializationError error = deserializeMsgPack(doc, input);
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(error == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["one"] == 1);
|
||||
REQUIRE(obj["two"] == 2);
|
||||
}
|
||||
|
||||
SECTION("key is str 16") {
|
||||
const char* input =
|
||||
"\x82\xdb\x00\x00\x00\x03one\x01\xdb\x00\x00\x00\x03two\x02";
|
||||
|
||||
DeserializationError error = deserializeMsgPack(doc, input);
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(error == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["one"] == 1);
|
||||
REQUIRE(obj["two"] == 2);
|
||||
}
|
||||
|
||||
SECTION("key is str 32") {
|
||||
const char* input =
|
||||
"\x82\xdb\x00\x00\x00\x03one\x01\xdb\x00\x00\x00\x03two\x02";
|
||||
|
||||
DeserializationError error = deserializeMsgPack(doc, input);
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(error == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["one"] == 1);
|
||||
REQUIRE(obj["two"] == 2);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("map 16") {
|
||||
|
@ -5,17 +5,21 @@
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
static const size_t epsilon = sizeof(void*);
|
||||
|
||||
template <size_t Capacity>
|
||||
static void check(const char* input, DeserializationError expected) {
|
||||
StaticJsonDocument<Capacity> variant;
|
||||
|
||||
DeserializationError error = deserializeMsgPack(variant, input);
|
||||
|
||||
CAPTURE(input);
|
||||
REQUIRE(error == expected);
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
static void checkString(const char* input, DeserializationError expected) {
|
||||
check<JSON_STRING_SIZE(Size)>(input, expected);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") {
|
||||
SECTION("single values always fit") {
|
||||
check<0>("\xc0", DeserializationError::Ok); // nil
|
||||
@ -27,31 +31,39 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") {
|
||||
}
|
||||
|
||||
SECTION("fixstr") {
|
||||
check<0>("\xA0", DeserializationError::Ok);
|
||||
check<0>("\xA1H", DeserializationError::NoMemory);
|
||||
check<4>("\xA1H", DeserializationError::Ok);
|
||||
check<4>("\xA5Hello", DeserializationError::NoMemory);
|
||||
checkString<8>("\xA0", DeserializationError::Ok);
|
||||
checkString<8>("\xA7ZZZZZZZ", DeserializationError::Ok);
|
||||
checkString<8>("\xA8ZZZZZZZZ", DeserializationError::NoMemory);
|
||||
checkString<16>("\xAFZZZZZZZZZZZZZZZ", DeserializationError::Ok);
|
||||
checkString<16>("\xB0ZZZZZZZZZZZZZZZZ", DeserializationError::NoMemory);
|
||||
}
|
||||
|
||||
SECTION("str 8") {
|
||||
check<0>("\xD9\x00", DeserializationError::Ok);
|
||||
check<0>("\xD9\x01H", DeserializationError::NoMemory);
|
||||
check<4>("\xD9\x01H", DeserializationError::Ok);
|
||||
check<4>("\xD9\x05Hello", DeserializationError::NoMemory);
|
||||
checkString<8>("\xD9\x00", DeserializationError::Ok);
|
||||
checkString<8>("\xD9\x07ZZZZZZZ", DeserializationError::Ok);
|
||||
checkString<8>("\xD9\x08ZZZZZZZZ", DeserializationError::NoMemory);
|
||||
checkString<16>("\xD9\x0FZZZZZZZZZZZZZZZ", DeserializationError::Ok);
|
||||
checkString<16>("\xD9\x10ZZZZZZZZZZZZZZZZ", DeserializationError::NoMemory);
|
||||
}
|
||||
|
||||
SECTION("str 16") {
|
||||
check<0>("\xDA\x00\x00", DeserializationError::Ok);
|
||||
check<0>("\xDA\x00\x01H", DeserializationError::NoMemory);
|
||||
check<4>("\xDA\x00\x01H", DeserializationError::Ok);
|
||||
check<4>("\xDA\x00\x05Hello", DeserializationError::NoMemory);
|
||||
checkString<8>("\xDA\x00\x00", DeserializationError::Ok);
|
||||
checkString<8>("\xDA\x00\x07ZZZZZZZ", DeserializationError::Ok);
|
||||
checkString<8>("\xDA\x00\x08ZZZZZZZZ", DeserializationError::NoMemory);
|
||||
checkString<16>("\xDA\x00\x0FZZZZZZZZZZZZZZZ", DeserializationError::Ok);
|
||||
checkString<16>("\xDA\x00\x10ZZZZZZZZZZZZZZZZ",
|
||||
DeserializationError::NoMemory);
|
||||
}
|
||||
|
||||
SECTION("str 32") {
|
||||
check<0>("\xDB\x00\x00\x00\x00", DeserializationError::Ok);
|
||||
check<0>("\xDB\x00\x00\x00\x01H", DeserializationError::NoMemory);
|
||||
check<4>("\xDB\x00\x00\x00\x01H", DeserializationError::Ok);
|
||||
check<4>("\xDB\x00\x00\x00\x05Hello", DeserializationError::NoMemory);
|
||||
checkString<8>("\xDB\x00\x00\x00\x00", DeserializationError::Ok);
|
||||
checkString<8>("\xDB\x00\x00\x00\x07ZZZZZZZ", DeserializationError::Ok);
|
||||
checkString<8>("\xDB\x00\x00\x00\x08ZZZZZZZZ",
|
||||
DeserializationError::NoMemory);
|
||||
checkString<16>("\xDB\x00\x00\x00\x0FZZZZZZZZZZZZZZZ",
|
||||
DeserializationError::Ok);
|
||||
checkString<16>("\xDB\x00\x00\x00\x10ZZZZZZZZZZZZZZZZ",
|
||||
DeserializationError::NoMemory);
|
||||
}
|
||||
|
||||
SECTION("fixarray") {
|
||||
@ -89,14 +101,14 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") {
|
||||
SECTION("{H:1}") {
|
||||
check<JSON_OBJECT_SIZE(0)>("\x81\xA1H\x01",
|
||||
DeserializationError::NoMemory);
|
||||
check<JSON_OBJECT_SIZE(1) + epsilon>("\x81\xA1H\x01",
|
||||
DeserializationError::Ok);
|
||||
check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>(
|
||||
"\x81\xA1H\x01", DeserializationError::Ok);
|
||||
}
|
||||
SECTION("{H:1,W:2}") {
|
||||
check<JSON_OBJECT_SIZE(1) + epsilon>("\x82\xA1H\x01\xA1W\x02",
|
||||
DeserializationError::NoMemory);
|
||||
check<JSON_OBJECT_SIZE(2) + 2 * epsilon>("\x82\xA1H\x01\xA1W\x02",
|
||||
DeserializationError::Ok);
|
||||
check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>(
|
||||
"\x82\xA1H\x01\xA1W\x02", DeserializationError::NoMemory);
|
||||
check<JSON_OBJECT_SIZE(2) + 2 * JSON_STRING_SIZE(2)>(
|
||||
"\x82\xA1H\x01\xA1W\x02", DeserializationError::Ok);
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,14 +119,14 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") {
|
||||
SECTION("{H:1}") {
|
||||
check<JSON_OBJECT_SIZE(0)>("\xDE\x00\x01\xA1H\x01",
|
||||
DeserializationError::NoMemory);
|
||||
check<JSON_OBJECT_SIZE(1) + epsilon>("\xDE\x00\x01\xA1H\x01",
|
||||
DeserializationError::Ok);
|
||||
check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>(
|
||||
"\xDE\x00\x01\xA1H\x01", DeserializationError::Ok);
|
||||
}
|
||||
SECTION("{H:1,W:2}") {
|
||||
check<JSON_OBJECT_SIZE(1) + epsilon>("\xDE\x00\x02\xA1H\x01\xA1W\x02",
|
||||
DeserializationError::NoMemory);
|
||||
check<JSON_OBJECT_SIZE(2) + 2 * epsilon>("\xDE\x00\x02\xA1H\x01\xA1W\x02",
|
||||
DeserializationError::Ok);
|
||||
check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>(
|
||||
"\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::NoMemory);
|
||||
check<JSON_OBJECT_SIZE(2) + 2 * JSON_OBJECT_SIZE(1)>(
|
||||
"\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok);
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,14 +138,14 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") {
|
||||
SECTION("{H:1}") {
|
||||
check<JSON_OBJECT_SIZE(0)>("\xDF\x00\x00\x00\x01\xA1H\x01",
|
||||
DeserializationError::NoMemory);
|
||||
check<JSON_OBJECT_SIZE(1) + epsilon>("\xDF\x00\x00\x00\x01\xA1H\x01",
|
||||
DeserializationError::Ok);
|
||||
check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>(
|
||||
"\xDF\x00\x00\x00\x01\xA1H\x01", DeserializationError::Ok);
|
||||
}
|
||||
SECTION("{H:1,W:2}") {
|
||||
check<JSON_OBJECT_SIZE(1) + epsilon>(
|
||||
check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>(
|
||||
"\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02",
|
||||
DeserializationError::NoMemory);
|
||||
check<JSON_OBJECT_SIZE(2) + 2 * epsilon>(
|
||||
check<JSON_OBJECT_SIZE(2) + 2 * JSON_OBJECT_SIZE(1)>(
|
||||
"\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok);
|
||||
}
|
||||
}
|
||||
|
@ -21,10 +21,9 @@ static void check(const JsonArray array, const char (&expected_data)[N]) {
|
||||
check(array, expected_data, expected_len);
|
||||
}
|
||||
|
||||
// TODO: this function is used by the commented test
|
||||
// static void check(const JsonArray array, const std::string& expected) {
|
||||
// check(array, expected.data(), expected.length());
|
||||
// }
|
||||
static void check(const JsonArray array, const std::string& expected) {
|
||||
check(array, expected.data(), expected.length());
|
||||
}
|
||||
|
||||
TEST_CASE("serialize MsgPack array") {
|
||||
DynamicJsonDocument doc;
|
||||
@ -49,12 +48,11 @@ TEST_CASE("serialize MsgPack array") {
|
||||
"\x0E\x0F");
|
||||
}
|
||||
|
||||
// TODO: this test is too slow
|
||||
// SECTION("array 32") {
|
||||
// const char* nil = 0;
|
||||
// for (int i = 0; i < 65536; i++) array.add(nil);
|
||||
//
|
||||
// check(array,
|
||||
// std::string("\xDD\x00\x01\x00\x00", 5) + std::string(65536, 0xC0));
|
||||
// }
|
||||
SECTION("array 32") {
|
||||
const char* nil = 0;
|
||||
for (int i = 0; i < 65536; i++) array.add(nil);
|
||||
|
||||
check(array,
|
||||
std::string("\xDD\x00\x01\x00\x00", 5) + std::string(65536, '\xc0'));
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,11 @@
|
||||
# MIT License
|
||||
|
||||
add_executable(StaticMemoryPoolTests
|
||||
alloc.cpp
|
||||
allocVariant.cpp
|
||||
allocString.cpp
|
||||
clear.cpp
|
||||
size.cpp
|
||||
startString.cpp
|
||||
StringBuilder.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(StaticMemoryPoolTests catch)
|
||||
|
39
test/StaticMemoryPool/StringBuilder.cpp
Normal file
39
test/StaticMemoryPool/StringBuilder.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/StaticMemoryPool.hpp>
|
||||
#include <ArduinoJson/Memory/StringBuilder.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("StringBuilder") {
|
||||
SECTION("WorksWhenBufferIsBigEnough") {
|
||||
StaticMemoryPool<JSON_STRING_SIZE(6)> memoryPool;
|
||||
|
||||
StringBuilder str(&memoryPool);
|
||||
str.append("hello");
|
||||
|
||||
REQUIRE(str.complete().equals("hello"));
|
||||
}
|
||||
|
||||
SECTION("ReturnsNullWhenTooSmall") {
|
||||
StaticMemoryPool<1> memoryPool;
|
||||
|
||||
StringBuilder str(&memoryPool);
|
||||
str.append("hello!!!");
|
||||
|
||||
REQUIRE(str.complete().isNull());
|
||||
}
|
||||
|
||||
SECTION("Increases size of memory pool") {
|
||||
StaticMemoryPool<JSON_STRING_SIZE(6)> memoryPool;
|
||||
|
||||
StringBuilder str(&memoryPool);
|
||||
str.append('h');
|
||||
str.complete();
|
||||
|
||||
REQUIRE(JSON_STRING_SIZE(2) == memoryPool.size());
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/StaticMemoryPool.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
static bool isAligned(void *ptr) {
|
||||
const size_t mask = sizeof(void *) - 1;
|
||||
size_t addr = reinterpret_cast<size_t>(ptr);
|
||||
return (addr & mask) == 0;
|
||||
}
|
||||
|
||||
TEST_CASE("StaticMemoryPool::alloc()") {
|
||||
StaticMemoryPool<64> memoryPool;
|
||||
|
||||
SECTION("Returns different addresses") {
|
||||
void *p1 = memoryPool.alloc(1);
|
||||
void *p2 = memoryPool.alloc(1);
|
||||
REQUIRE(p1 != p2);
|
||||
}
|
||||
|
||||
SECTION("Returns non-NULL when using full capacity") {
|
||||
void *p = memoryPool.alloc(64);
|
||||
REQUIRE(0 != p);
|
||||
}
|
||||
|
||||
SECTION("Returns NULL when full") {
|
||||
memoryPool.alloc(64);
|
||||
void *p = memoryPool.alloc(1);
|
||||
REQUIRE(0 == p);
|
||||
}
|
||||
|
||||
SECTION("Returns NULL when memoryPool is too small") {
|
||||
void *p = memoryPool.alloc(65);
|
||||
REQUIRE(0 == p);
|
||||
}
|
||||
|
||||
SECTION("Returns aligned pointers") {
|
||||
for (size_t size = 1; size <= sizeof(void *); size++) {
|
||||
void *p = memoryPool.alloc(1);
|
||||
REQUIRE(isAligned(p));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Returns same address after clear()") {
|
||||
void *p1 = memoryPool.alloc(1);
|
||||
memoryPool.clear();
|
||||
void *p2 = memoryPool.alloc(1);
|
||||
REQUIRE(p1 == p2);
|
||||
}
|
||||
}
|
131
test/StaticMemoryPool/allocString.cpp
Normal file
131
test/StaticMemoryPool/allocString.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/StaticMemoryPool.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("StaticMemoryPool::allocFrozenString()") {
|
||||
const size_t poolCapacity = 64;
|
||||
const size_t longestString = poolCapacity - sizeof(StringSlot);
|
||||
StaticMemoryPool<poolCapacity> pool;
|
||||
|
||||
SECTION("Returns different addresses") {
|
||||
StringSlot *a = pool.allocFrozenString(1);
|
||||
StringSlot *b = pool.allocFrozenString(1);
|
||||
REQUIRE(a != b);
|
||||
REQUIRE(a->value != b->value);
|
||||
}
|
||||
|
||||
SECTION("Returns a StringSlot of the right size") {
|
||||
StringSlot *s = pool.allocFrozenString(12);
|
||||
REQUIRE(s->size == 12);
|
||||
}
|
||||
|
||||
SECTION("Returns NULL when full") {
|
||||
pool.allocFrozenString(longestString);
|
||||
void *p = pool.allocFrozenString(1);
|
||||
REQUIRE(0 == p);
|
||||
}
|
||||
|
||||
SECTION("Returns NULL when pool is too small") {
|
||||
void *p = pool.allocFrozenString(longestString + 1);
|
||||
REQUIRE(0 == p);
|
||||
}
|
||||
|
||||
SECTION("Returns aligned pointers") {
|
||||
REQUIRE(isAligned(pool.allocFrozenString(1)));
|
||||
REQUIRE(isAligned(pool.allocFrozenString(1)));
|
||||
}
|
||||
|
||||
SECTION("Returns same address after clear()") {
|
||||
StringSlot *a = pool.allocFrozenString(1);
|
||||
pool.clear();
|
||||
StringSlot *b = pool.allocFrozenString(1);
|
||||
|
||||
REQUIRE(a == b);
|
||||
REQUIRE(a->value == b->value);
|
||||
}
|
||||
|
||||
SECTION("Returns same address after freeString()") {
|
||||
StringSlot *a = pool.allocFrozenString(1);
|
||||
pool.freeString(a);
|
||||
StringSlot *b = pool.allocFrozenString(1);
|
||||
|
||||
REQUIRE(a == b);
|
||||
REQUIRE(a->value == b->value);
|
||||
}
|
||||
|
||||
SECTION("Can use full capacity when fresh") {
|
||||
StringSlot *a = pool.allocFrozenString(longestString);
|
||||
|
||||
REQUIRE(a != 0);
|
||||
}
|
||||
|
||||
SECTION("Can use full capacity after clear") {
|
||||
pool.allocFrozenString(longestString);
|
||||
pool.clear();
|
||||
|
||||
StringSlot *a = pool.allocFrozenString(longestString);
|
||||
|
||||
REQUIRE(a != 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("StaticMemoryPool::freeString()") {
|
||||
const size_t poolCapacity = 512;
|
||||
const size_t longestString = poolCapacity - sizeof(StringSlot);
|
||||
StaticMemoryPool<poolCapacity> pool;
|
||||
|
||||
static const size_t testStringSize =
|
||||
(poolCapacity - sizeof(StringSlot) * 4 - sizeof(VariantSlot) * 4) / 4;
|
||||
|
||||
SECTION("Restores full capacity") {
|
||||
StringSlot *strings[4];
|
||||
VariantSlot *variants[4];
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
strings[i] = pool.allocFrozenString(testStringSize);
|
||||
REQUIRE(strings[i] != 0);
|
||||
variants[i] = pool.allocVariant();
|
||||
REQUIRE(variants[i] != 0);
|
||||
}
|
||||
|
||||
// In random order
|
||||
pool.freeString(strings[2]);
|
||||
pool.freeVariant(variants[3]);
|
||||
pool.freeVariant(variants[0]);
|
||||
pool.freeString(strings[0]);
|
||||
pool.freeVariant(variants[1]);
|
||||
pool.freeString(strings[1]);
|
||||
pool.freeVariant(variants[2]);
|
||||
pool.freeString(strings[3]);
|
||||
|
||||
StringSlot *b = pool.allocFrozenString(longestString);
|
||||
|
||||
REQUIRE(b != 0);
|
||||
REQUIRE(b->size == longestString);
|
||||
}
|
||||
|
||||
SECTION("Move strings") {
|
||||
StringSlot *a = pool.allocFrozenString(6);
|
||||
strcpy(a->value, "hello");
|
||||
|
||||
StringSlot *b = pool.allocFrozenString(7);
|
||||
strcpy(b->value, "world!");
|
||||
pool.freeString(a);
|
||||
|
||||
REQUIRE(b->size == 7);
|
||||
REQUIRE(b->value == std::string("world!"));
|
||||
REQUIRE(a->value == b->value);
|
||||
}
|
||||
|
||||
SECTION("Accepts non-frozen string") {
|
||||
StringSlot *a = pool.allocExpandableString();
|
||||
pool.freeString(a);
|
||||
|
||||
REQUIRE(pool.size() == 0);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user