Merged the two StringBuilder classes into one

This commit is contained in:
Benoit Blanchon
2018-10-18 17:54:33 +02:00
parent 1a4515c0b9
commit ae089dcff7
9 changed files with 91 additions and 102 deletions

View File

@ -6,6 +6,7 @@
#include "../Strings/StringInMemoryPool.hpp" #include "../Strings/StringInMemoryPool.hpp"
#include "MemoryPool.hpp" #include "MemoryPool.hpp"
#include "StringBuilder.hpp"
#include <stdlib.h> // malloc, free #include <stdlib.h> // malloc, free
@ -39,7 +40,7 @@ class DynamicMemoryPoolBase : public MemoryPool {
size_t size; size_t size;
}; };
struct Block : EmptyBlock { struct Block : EmptyBlock {
uint8_t data[1]; char data[1];
}; };
public: public:
@ -64,11 +65,23 @@ class DynamicMemoryPoolBase : public MemoryPool {
} }
// Allocates the specified amount of bytes in the memoryPool // Allocates the specified amount of bytes in the memoryPool
virtual void* alloc(size_t bytes) { virtual char* alloc(size_t bytes) {
alignNextAlloc(); alignNextAlloc();
return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes); return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes);
} }
virtual char* realloc(char* oldPtr, size_t oldSize, size_t newSize) {
size_t n = newSize - oldSize;
if (canAllocInHead(n)) {
allocInHead(n);
return oldPtr;
} else {
char* newPtr = allocInNewBlock(newSize);
if (oldPtr && newPtr) memcpy(newPtr, oldPtr, oldSize);
return newPtr;
}
}
// Resets the memoryPool. // Resets the memoryPool.
// USE WITH CAUTION: this invalidates all previously allocated data // USE WITH CAUTION: this invalidates all previously allocated data
void clear() { void clear() {
@ -82,37 +95,6 @@ class DynamicMemoryPoolBase : public MemoryPool {
_head = 0; _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() { StringBuilder startString() {
return StringBuilder(this); return StringBuilder(this);
} }
@ -126,13 +108,13 @@ class DynamicMemoryPoolBase : public MemoryPool {
return _head != NULL && _head->size + bytes <= _head->capacity; return _head != NULL && _head->size + bytes <= _head->capacity;
} }
void* allocInHead(size_t bytes) { char* allocInHead(size_t bytes) {
void* p = _head->data + _head->size; char* p = _head->data + _head->size;
_head->size += bytes; _head->size += bytes;
return p; return p;
} }
void* allocInNewBlock(size_t bytes) { char* allocInNewBlock(size_t bytes) {
size_t capacity = _nextBlockCapacity; size_t capacity = _nextBlockCapacity;
if (bytes > capacity) capacity = bytes; if (bytes > capacity) capacity = bytes;
if (!addNewBlock(capacity)) return NULL; if (!addNewBlock(capacity)) return NULL;

View File

@ -19,7 +19,9 @@ class MemoryPool {
public: public:
// Allocates n bytes in the MemoryPool. // Allocates n bytes in the MemoryPool.
// Return a pointer to the allocated memory or NULL if allocation fails. // Return a pointer to the allocated memory or NULL if allocation fails.
virtual void *alloc(size_t size) = 0; virtual char *alloc(size_t size) = 0;
virtual char *realloc(char *oldPtr, size_t oldSize, size_t newSize) = 0;
protected: protected:
// CAUTION: NO VIRTUAL DESTRUCTOR! // CAUTION: NO VIRTUAL DESTRUCTOR!

View File

@ -7,39 +7,12 @@
#include "../Polyfills/mpl/max.hpp" #include "../Polyfills/mpl/max.hpp"
#include "../Strings/StringInMemoryPool.hpp" #include "../Strings/StringInMemoryPool.hpp"
#include "MemoryPool.hpp" #include "MemoryPool.hpp"
#include "StringBuilder.hpp"
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
class StaticMemoryPoolBase : public MemoryPool { class StaticMemoryPoolBase : public MemoryPool {
public: public:
class StringBuilder {
public:
explicit StringBuilder(StaticMemoryPoolBase* parent) : _parent(parent) {
_start = parent->_buffer + parent->_size;
}
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;
}
}
private:
StaticMemoryPoolBase* _parent;
char* _start;
};
// Gets the capacity of the memoryPool in bytes // Gets the capacity of the memoryPool in bytes
size_t capacity() const { size_t capacity() const {
return _capacity; return _capacity;
@ -51,12 +24,19 @@ class StaticMemoryPoolBase : public MemoryPool {
} }
// Allocates the specified amount of bytes in the memoryPool // Allocates the specified amount of bytes in the memoryPool
virtual void* alloc(size_t bytes) { virtual char* alloc(size_t bytes) {
alignNextAlloc(); alignNextAlloc();
if (!canAlloc(bytes)) return NULL; if (!canAlloc(bytes)) return NULL;
return doAlloc(bytes); return doAlloc(bytes);
} }
virtual char* realloc(char* oldPtr, size_t oldSize, size_t newSize) {
size_t n = newSize - oldSize;
if (!canAlloc(n)) return NULL;
doAlloc(n);
return oldPtr;
}
// Resets the memoryPool. // Resets the memoryPool.
// USE WITH CAUTION: this invalidates all previously allocated data // USE WITH CAUTION: this invalidates all previously allocated data
void clear() { void clear() {
@ -82,8 +62,8 @@ class StaticMemoryPoolBase : public MemoryPool {
return _size + bytes <= _capacity; return _size + bytes <= _capacity;
} }
void* doAlloc(size_t bytes) { char* doAlloc(size_t bytes) {
void* p = &_buffer[_size]; char* p = &_buffer[_size];
_size += bytes; _size += bytes;
return p; return p;
} }

View File

@ -0,0 +1,35 @@
// 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:
explicit StringBuilder(MemoryPool* parent)
: _parent(parent), _start(0), _size(0) {
_start = _parent->alloc(1);
}
void append(char c) {
_start = _parent->realloc(_start, _size + 1, _size + 2);
if (_start) _start[_size++] = c;
}
StringInMemoryPool complete() {
if (_start) _start[_size] = 0;
return _start;
}
private:
MemoryPool* _parent;
char* _start;
size_t _size;
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -4,20 +4,22 @@
#pragma once #pragma once
#include "../Memory/MemoryPool.hpp"
#include "../Memory/StringBuilder.hpp"
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
template <typename TMemoryPool>
class StringCopier { class StringCopier {
public: public:
StringCopier(TMemoryPool& memoryPool) : _memoryPool(&memoryPool) {} typedef ARDUINOJSON_NAMESPACE::StringBuilder StringBuilder;
typedef typename TMemoryPool::StringBuilder StringBuilder; StringCopier(MemoryPool* memoryPool) : _memoryPool(memoryPool) {}
StringBuilder startString() { StringBuilder startString() {
return _memoryPool->startString(); return StringBuilder(_memoryPool);
} }
private: private:
TMemoryPool* _memoryPool; MemoryPool* _memoryPool;
}; };
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE

View File

@ -9,34 +9,34 @@
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
template <typename TMemoryPool, typename TInput, typename Enable = void> template <typename TInput, typename Enable = void>
struct StringStorage { struct StringStorage {
typedef StringCopier<TMemoryPool> type; typedef StringCopier type;
static type create(TMemoryPool& jb, TInput&) { static type create(MemoryPool& pool, TInput&) {
return type(jb); return type(&pool);
} }
}; };
template <typename TMemoryPool, typename TChar> template <typename TChar>
struct StringStorage<TMemoryPool, TChar*, struct StringStorage<TChar*,
typename enable_if<!is_const<TChar>::value>::type> { typename enable_if<!is_const<TChar>::value>::type> {
typedef StringMover<TChar> type; typedef StringMover<TChar> type;
static type create(TMemoryPool&, TChar* input) { static type create(MemoryPool&, TChar* input) {
return type(input); return type(input);
} }
}; };
template <typename TMemoryPool, typename TInput> template <typename TInput>
typename StringStorage<TMemoryPool, TInput>::type makeStringStorage( typename StringStorage<TInput>::type makeStringStorage(MemoryPool& pool,
TMemoryPool& jb, TInput& input) { TInput& input) {
return StringStorage<TMemoryPool, TInput>::create(jb, input); return StringStorage<TInput>::create(pool, input);
} }
template <typename TMemoryPool, typename TChar> template <typename TChar>
typename StringStorage<TMemoryPool, TChar*>::type makeStringStorage( typename StringStorage<TChar*>::type makeStringStorage(MemoryPool& pool,
TMemoryPool& jb, TChar* input) { TChar* input) {
return StringStorage<TMemoryPool, TChar*>::create(jb, input); return StringStorage<TChar*>::create(pool, input);
} }
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE

View File

@ -17,8 +17,6 @@ struct NoMemoryAllocator {
TEST_CASE("DynamicMemoryPool no memory") { TEST_CASE("DynamicMemoryPool no memory") {
DynamicMemoryPoolBase<NoMemoryAllocator> _memoryPool; DynamicMemoryPoolBase<NoMemoryAllocator> _memoryPool;
typedef DynamicMemoryPoolBase<NoMemoryAllocator>::StringBuilder StringBuilder;
SECTION("FixCodeCoverage") { SECTION("FixCodeCoverage") {
// call this function to fix code coverage // call this function to fix code coverage
NoMemoryAllocator().deallocate(NULL); NoMemoryAllocator().deallocate(NULL);

View File

@ -7,8 +7,6 @@
using namespace ARDUINOJSON_NAMESPACE; using namespace ARDUINOJSON_NAMESPACE;
typedef DynamicMemoryPool::StringBuilder StringBuilder;
TEST_CASE("DynamicMemoryPool::startString()") { TEST_CASE("DynamicMemoryPool::startString()") {
SECTION("WorksWhenBufferIsBigEnough") { SECTION("WorksWhenBufferIsBigEnough") {
DynamicMemoryPool memoryPool(6); DynamicMemoryPool memoryPool(6);
@ -40,12 +38,9 @@ TEST_CASE("DynamicMemoryPool::startString()") {
DynamicMemoryPool memoryPool(5); DynamicMemoryPool memoryPool(5);
StringBuilder str = memoryPool.startString(); StringBuilder str = memoryPool.startString();
REQUIRE(0 == memoryPool.size());
str.append('h'); str.append('h');
REQUIRE(1 == memoryPool.size());
str.complete(); str.complete();
REQUIRE(2 == memoryPool.size()); REQUIRE(2 == memoryPool.size());
} }
} }

View File

@ -8,8 +8,6 @@
using namespace ARDUINOJSON_NAMESPACE; using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("StaticMemoryPool::startString()") { TEST_CASE("StaticMemoryPool::startString()") {
typedef StaticMemoryPoolBase::StringBuilder StringBuilder;
SECTION("WorksWhenBufferIsBigEnough") { SECTION("WorksWhenBufferIsBigEnough") {
StaticMemoryPool<6> memoryPool; StaticMemoryPool<6> memoryPool;
@ -40,12 +38,9 @@ TEST_CASE("StaticMemoryPool::startString()") {
StaticMemoryPool<5> memoryPool; StaticMemoryPool<5> memoryPool;
StringBuilder str = memoryPool.startString(); StringBuilder str = memoryPool.startString();
REQUIRE(0 == memoryPool.size());
str.append('h'); str.append('h');
REQUIRE(1 == memoryPool.size());
str.complete(); str.complete();
REQUIRE(2 == memoryPool.size()); REQUIRE(2 == memoryPool.size());
} }
} }