From 34a796f7e9216b892cba34220105e64b7737e3d2 Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 28 Sep 2022 09:43:50 +0100 Subject: [PATCH] Tidy up and improve Vector class memory usage (#2558) This PR follows on from #2556 to reduce memory usage and performance when Vector is used with simple (scalar) values. Additional tests added. --- Sming/Wiring/WHashMap.h | 12 +- Sming/Wiring/WVector.h | 397 +++++++----------- Sming/Wiring/WiringList.h | 139 ++++-- .../app/application.cpp | 38 +- tests/HostTests/modules/Wiring.cpp | 200 ++++++--- 5 files changed, 436 insertions(+), 350 deletions(-) diff --git a/Sming/Wiring/WHashMap.h b/Sming/Wiring/WHashMap.h index 37a1685001..1b1de76155 100644 --- a/Sming/Wiring/WHashMap.h +++ b/Sming/Wiring/WHashMap.h @@ -32,6 +32,7 @@ #include #include #include "WiringList.h" +#include "Print.h" /** * @brief HashMap class template @@ -89,6 +90,15 @@ template class HashMap return &v; } + size_t printTo(Print& p) const + { + size_t n{0}; + n += p.print(k); + n += p.print(" = "); + n += p.print(v); + return n; + } + private: const K& k; Value& v; @@ -285,7 +295,7 @@ template class HashMap bool allocate(unsigned int newSize) { - return keys.allocate(newSize, K{}) && values.allocate(newSize, nil); + return keys.allocate(newSize) && values.allocate(newSize); } /** diff --git a/Sming/Wiring/WVector.h b/Sming/Wiring/WVector.h index 9f6cb46a1e..6da2e68965 100644 --- a/Sming/Wiring/WVector.h +++ b/Sming/Wiring/WVector.h @@ -22,6 +22,7 @@ #include #include #include +#include "WiringList.h" /** * @brief Vector class template @@ -74,7 +75,7 @@ template class Vector : public Countable return !operator==(rhs); } - Element& operator*() + template typename std::enable_if::type operator*() { return vector[index]; } @@ -90,72 +91,162 @@ template class Vector : public Countable }; // constructors - Vector(unsigned int initialCapacity = 10, unsigned int capacityIncrement = 10); - Vector(const Vector& rhv); - ~Vector(); + Vector(unsigned int initialCapacity = 10, unsigned int capacityIncrement = 10) : _increment(capacityIncrement) + { + _data.allocate(initialCapacity); + } + + Vector(const Vector& rhv) + { + copyFrom(rhv); + } // methods - unsigned int capacity() const; - bool contains(const Element& elem) const; - const Element& firstElement() const; + unsigned int capacity() const + { + return _data.size; + } + + bool contains(const Element& elem) const + { + return indexOf(elem) >= 0; + } + + const Element& firstElement() const + { + if(_size == 0) { + abort(); + } + + return _data[0]; + } + int indexOf(const Element& elem) const; - bool isEmpty() const; - const Element& lastElement() const; + + bool isEmpty() const + { + return _size == 0; + } + + const Element& lastElement() const + { + if(_size == 0) { + abort(); + } + + return _data[_size - 1]; + } + int lastIndexOf(const Element& elem) const; + unsigned int count() const override { return size(); } - unsigned int size() const; + + unsigned int size() const + { + return _size; + } + void copyInto(Element* array) const; + bool add(const Element& obj) { return addElement(obj); } + bool addElement(const Element& obj); bool addElement(Element* objp); + void clear() { removeAllElements(); } + bool ensureCapacity(unsigned int minCapacity); - void removeAllElements(); - bool removeElement(const Element& obj); + + void removeAllElements() + { + _data.clear(); + _size = 0; + } + + bool removeElement(const Element& obj) + { + return removeElementAt(indexOf(obj)); + } + + /** + * @brief Reduce or increase number of items + * @retval true on success, false on memory reallocation failure + * + * If increasing number of items, new items will be set to current `nil` value. + * If reducing number of items, old items will be deleted. + */ bool setSize(unsigned int newSize); - void trimToSize(); - const Element& elementAt(unsigned int index) const; + + /** + * @brief Reduce capacity to match current size + */ + void trimToSize() + { + if(_size < _data.size) { + _data.trim(_size, true); + } + } + + const Element& elementAt(unsigned int index) const + { + if(index >= _size) { + abort(); + } + return _data[index]; + } + bool insertElementAt(const Element& obj, unsigned int index); - const void remove(unsigned int index); - void removeElementAt(unsigned int index); + + bool remove(unsigned int index) + { + return removeElementAt(index); + } + + bool removeElementAt(unsigned int index); bool setElementAt(const Element& obj, unsigned int index); + const Element& get(unsigned int index) const { return elementAt(index); } - const Element& operator[](unsigned int index) const override; - Element& operator[](unsigned int index) override; + const Element& operator[](unsigned int index) const override + { + return elementAt(index); + } + + Element& operator[](unsigned int index) override + { + if(index >= _size) { + abort(); + } + return _data[index]; + } const Vector& operator=(const Vector& rhv) { - if(this != &rhv) + if(this != &rhv) { copyFrom(rhv); + } return *this; } - const Vector& operator=(const Vector&& other) noexcept // move assignment + + const Vector& operator=(Vector&& other) noexcept // move assignment { - if(_data != nullptr) { - removeAllElements(); - delete[] _data; // delete this storage - } - _data = other._data; // move - _size = other._size; - _capacity = other._capacity; - _increment = other._increment; - other._data = nullptr; // leave moved-from in valid state - other._size = 0; - other._capacity = 0; - other._increment = 0; + clear(); + _increment = 0; + std::swap(_data, other._data); + std::swap(_size, other._size); + std::swap(_increment, other._increment); return *this; } @@ -171,12 +262,12 @@ template class Vector : public Countable return Iterator(*this, count()); } - Iterator begin() const + const Iterator begin() const { return Iterator(*this, 0); } - Iterator end() const + const Iterator end() const { return Iterator(*this, count()); } @@ -185,94 +276,44 @@ template class Vector : public Countable void copyFrom(const Vector& rhv); protected: - unsigned int _size = 0; - unsigned int _capacity = 0; - unsigned int _increment; - Element** _data = nullptr; -}; - -template Vector::Vector(unsigned int initialCapacity, unsigned int capacityIncrement) -{ - _size = 0; - _capacity = initialCapacity; - _data = new Element*[_capacity]; - _increment = capacityIncrement; - if(_data == nullptr) { - _capacity = _increment = 0; - } -} + using ElementList = wiring_private::List; -template Vector::Vector(const Vector& rhv) -{ - copyFrom(rhv); -} + unsigned int _size{0}; + unsigned int _increment{0}; + ElementList _data; +}; template void Vector::copyFrom(const Vector& rhv) { - if(_data != nullptr) { - removeAllElements(); - delete[] _data; + _data.clear(); + if(!_data.allocate(rhv._data.size)) { + _size = _increment = 0; + return; } + _size = rhv._size; - _capacity = rhv._capacity; - _data = new Element*[_capacity]; _increment = rhv._increment; - if(_data == nullptr) { - _size = _capacity = _increment = 0; - } for(unsigned int i = 0; i < _size; i++) { - _data[i] = new Element(*(rhv._data[i])); + _data[i] = rhv._data[i]; } } -template Vector::~Vector() -{ - removeAllElements(); - delete[] _data; -} - -template unsigned int Vector::capacity() const -{ - return _capacity; -} - -template bool Vector::contains(const Element& elem) const -{ - return indexOf(elem) >= 0; -} - template void Vector::copyInto(Element* array) const { - if(array != nullptr) { - for(unsigned int i = 0; i < _size; i++) { - array[i] = *_data[i]; - } - } -} - -template const Element& Vector::elementAt(unsigned int index) const -{ - if(index >= _size || !_data) { - abort(); + if(array == nullptr) { + return; } - // add check for valid index - return *_data[index]; -} -template const Element& Vector::firstElement() const -{ - if(_size == 0 || !_data) { - abort(); + for(unsigned int i = 0; i < _size; i++) { + array[i] = _data[i]; } - - return *_data[0]; } template int Vector::indexOf(const Element& elem) const { for(unsigned int i = 0; i < _size; i++) { - if(*_data[i] == elem) { + if(_data[i] == elem) { return i; } } @@ -280,20 +321,6 @@ template int Vector::indexOf(const Element& elem) const return -1; } -template bool Vector::isEmpty() const -{ - return _size == 0; -} - -template const Element& Vector::lastElement() const -{ - if(_size == 0 || !_data) { - abort(); - } - - return *_data[_size - 1]; -} - template int Vector::lastIndexOf(const Element& elem) const { // check for empty vector @@ -305,7 +332,7 @@ template int Vector::lastIndexOf(const Element& elem) c do { i--; - if(*_data[i] == elem) { + if(_data[i] == elem) { return i; } } while(i != 0); @@ -313,17 +340,12 @@ template int Vector::lastIndexOf(const Element& elem) c return -1; } -template unsigned int Vector::size() const -{ - return _size; -} - template bool Vector::addElement(const Element& obj) { if(!ensureCapacity(_size + 1)) { return false; } - _data[_size++] = new Element(obj); + _data[_size++] = obj; return true; } @@ -338,22 +360,12 @@ template bool Vector::addElement(Element* objp) template bool Vector::ensureCapacity(unsigned int minCapacity) { - if(_capacity >= minCapacity) { + if(_data.size >= minCapacity) { return true; } - auto newCapacity = std::max(minCapacity, _capacity + _increment); - Element** temp = new Element*[newCapacity]; - // copy all elements - if(temp == nullptr) { - return false; - } - - _capacity = newCapacity; - memcpy(temp, _data, sizeof(Element*) * _size); - delete[] _data; - _data = temp; - return true; + auto newCapacity = std::max(minCapacity, _data.size + _increment); + return _data.allocate(newCapacity); } template bool Vector::insertElementAt(const Element& obj, unsigned int index) @@ -362,7 +374,6 @@ template bool Vector::insertElementAt(const Element& ob return addElement(obj); } - // need to verify index, right now you must know what you're doing if(index > _size) { return false; } @@ -370,66 +381,24 @@ template bool Vector::insertElementAt(const Element& ob return false; } - Element* newItem = new Element(obj); // pointer to new item - if(newItem == nullptr) { + if(!_data.insert(index, obj)) { return false; } - for(unsigned int i = index; i <= _size; i++) { - Element* tmp = _data[i]; - _data[i] = newItem; - - if(i != _size) { - newItem = tmp; - } else { - break; - } - } _size++; return true; } -template const void Vector::remove(unsigned int index) -{ - removeElementAt(index); -} - -template void Vector::removeAllElements() -{ - // avoid memory leak - for(unsigned int i = 0; i < _size; i++) { - delete _data[i]; - } - - _size = 0; -} - -template bool Vector::removeElement(const Element& obj) -{ - for(unsigned int i = 0; i < _size; i++) { - if(*_data[i] == obj) { - removeElementAt(i); - return true; - } - } - return false; -} - -template void Vector::removeElementAt(unsigned int index) +template bool Vector::removeElementAt(unsigned int index) { // check for valid index if(index >= _size) { - return; - } - - delete _data[index]; - - unsigned int i; - for(i = index + 1; i < _size; i++) { - _data[i - 1] = _data[i]; + return false; } + _data.remove(index); _size--; + return true; } template bool Vector::setElementAt(const Element& obj, unsigned int index) @@ -438,7 +407,7 @@ template bool Vector::setElementAt(const Element& obj, if(index >= _size) { return false; } - *_data[index] = obj; + _data[index] = obj; return true; } @@ -448,63 +417,23 @@ template bool Vector::setSize(unsigned int newSize) return false; } - if(newSize < _size) { - for(unsigned int i = newSize; i < _size; i++) { - delete _data[i]; - } - - _size = newSize; - } - + _data.trim(newSize, false); + _size = std::min(_size, newSize); return true; } -template void Vector::trimToSize() -{ - if(_size != _capacity) { - Element** temp = new Element*[_size]; - if(temp == nullptr) { - return; - } - - for(unsigned int i = 0; i < _size; i++) { - temp[i] = _data[i]; - } - - delete[] _data; - - _data = temp; - _capacity = _size; - } -} - -template const Element& Vector::operator[](unsigned int index) const -{ - return elementAt(index); -} - -template Element& Vector::operator[](unsigned int index) -{ - // check for valid index - //static Element dummy_writable_element; - if(index >= _size || !_data) { - //dummy_writable_element = 0; - //return dummy_writable_element; - abort(); - } - return *_data[index]; -} - template void Vector::sort(Comparer compareFunction) { - for(unsigned j = 1; j < _size; j++) // Start with 1 (not 0) - { - Element* key = _data[j]; + // Start with 1 (not 0) + for(unsigned j = 1; j < _size; j++) { + auto key = _data.values[j]; + Element& keyRef = _data[j]; + // Smaller values move up int i; - for(i = j - 1; (i >= 0) && compareFunction(*_data[i], *key) > 0; i--) // Smaller values move up - { - _data[i + 1] = _data[i]; + for(i = j - 1; (i >= 0) && compareFunction(_data[i], keyRef) > 0; i--) { + _data.values[i + 1] = _data.values[i]; } - _data[i + 1] = key; //Put key into its proper location + // Put key into its proper location + _data.values[i + 1] = key; } } diff --git a/Sming/Wiring/WiringList.h b/Sming/Wiring/WiringList.h index 120520812e..d799215cc4 100644 --- a/Sming/Wiring/WiringList.h +++ b/Sming/Wiring/WiringList.h @@ -13,119 +13,168 @@ namespace wiring_private { /** - * @brief List of object pointers + * @brief List of scalar values */ -template struct ObjectList { - T** values{nullptr}; +template struct ScalarList { + T* values{nullptr}; size_t size{0}; - ~ObjectList() + ~ScalarList() { clear(); } - bool allocate(size_t newSize, ...); + bool allocate(size_t newSize); void clear() { - while(size != 0) { - delete values[--size]; - } free(values); values = nullptr; + size = 0; + } + + bool insert(unsigned index, T value) + { + memmove(&values[index + 1], &values[index], size - index - 1); + values[index] = value; + return true; } void remove(unsigned index) { - delete values[index]; - memmove(&values[index], &values[index + 1], (size - index - 1) * sizeof(T*)); - values[size - 1] = nullptr; + memmove(&values[index], &values[index + 1], (size - index - 1) * sizeof(T)); } - T& operator[](unsigned index) + void trim(size_t newSize, bool reallocate) { - auto& ptr = values[index]; - if(ptr == nullptr) { - ptr = new T{}; + if(!reallocate) { + return; + } + + auto newmem = realloc(values, sizeof(T) * newSize); + if(newmem == nullptr) { + return; } - return *ptr; + + values = static_cast(newmem); + size = newSize; + } + + T& operator[](unsigned index) + { + return values[index]; } const T& operator[](unsigned index) const { - return const_cast(*this)[index]; + return const_cast(*this)[index]; } }; /** - * @brief List of scalar values + * @brief List of object pointers */ -template struct ScalarList { - T* values{nullptr}; - size_t size{0}; +template struct ObjectList : public ScalarList { + struct Element { + T*& value; + + Element& operator=(T* v) + { + delete value; + value = v; + return *this; + } - ~ScalarList() + template + typename std::enable_if::value, Element&>::type operator=(const U& v) + { + delete value; + value = new U{v}; + return *this; + } + + operator T&() + { + return *value; + } + }; + + ~ObjectList() { clear(); } - bool allocate(size_t newSize, const T& nil); + bool allocate(size_t newSize); void clear() { - free(values); - values = nullptr; - size = 0; + while(this->size != 0) { + delete this->values[--this->size]; + } + ScalarList::clear(); + } + + bool insert(unsigned index, const T& value) + { + auto el = new T(value); + if(el == nullptr) { + return false; + } + return ScalarList::insert(index, el); } void remove(unsigned index) { - memmove(&values[index], &values[index + 1], (size - index - 1) * sizeof(T)); + delete this->values[index]; + ScalarList::remove(index); + this->values[this->size - 1] = nullptr; } - T& operator[](unsigned index) + void trim(size_t newSize, bool reallocate) { - return values[index]; + for(unsigned i = this->size; i > newSize; --i) { + delete this->values[i - 1]; + this->values[i - 1] = nullptr; + } + + ScalarList::trim(newSize, reallocate); + } + + Element operator[](unsigned index) + { + return Element{this->values[index]}; } const T& operator[](unsigned index) const { - return const_cast(*this)[index]; + return *this->values[index]; } }; -template bool ObjectList::allocate(size_t newSize, ...) +template bool ScalarList::allocate(size_t newSize) { if(newSize <= size) { return true; } - auto newmem = realloc(values, sizeof(T*) * newSize); + auto newmem = realloc(values, sizeof(T) * newSize); if(newmem == nullptr) { return false; } - values = static_cast(newmem); - std::fill_n(&values[size], newSize - size, nullptr); - + values = static_cast(newmem); size = newSize; return true; } -template bool ScalarList::allocate(size_t newSize, const T& nil) +template bool ObjectList::allocate(size_t newSize) { - if(newSize <= size) { - return true; - } - - auto newmem = realloc(values, sizeof(T) * newSize); - if(newmem == nullptr) { + auto curSize = this->size; + if(!ScalarList::allocate(newSize)) { return false; } - values = static_cast(newmem); - std::fill_n(&values[size], newSize - size, nil); - size = newSize; + std::fill_n(&this->values[curSize], newSize - curSize, nullptr); return true; } diff --git a/samples/HttpServer_ConfigNetwork/app/application.cpp b/samples/HttpServer_ConfigNetwork/app/application.cpp index 6d0b8a623f..2bc0a694b3 100644 --- a/samples/HttpServer_ConfigNetwork/app/application.cpp +++ b/samples/HttpServer_ConfigNetwork/app/application.cpp @@ -69,16 +69,16 @@ int onIpConfig(HttpServerConnection& connection, HttpRequest& request, HttpRespo void onFile(HttpRequest& request, HttpResponse& response) { - if(lastModified.length() > 0 && request.headers[HTTP_HEADER_IF_MODIFIED_SINCE].equals(lastModified)) { + if(lastModified.length() > 0 && request.headers[HTTP_HEADER_IF_MODIFIED_SINCE] == lastModified) { response.code = HTTP_STATUS_NOT_MODIFIED; return; } String file = request.uri.getRelativePath(); - if(file[0] == '.') + if(file[0] == '.') { response.code = HTTP_STATUS_FORBIDDEN; - else { + } else { if(lastModified.length() > 0) { response.headers[HTTP_HEADER_LAST_MODIFIED] = lastModified; } @@ -103,15 +103,16 @@ void onAjaxNetworkList(HttpRequest& request, HttpResponse& response) } JsonArray netlist = json.createNestedArray("available"); - for(unsigned i = 0; i < networks.count(); i++) { - if(networks[i].hidden) + for(auto& nw : networks) { + if(nw.hidden) { continue; + } JsonObject item = netlist.createNestedObject(); - item["id"] = (int)networks[i].getHashId(); + item["id"] = nw.getHashId(); // Copy full string to JSON buffer memory - item["title"] = networks[i].ssid; - item["signal"] = networks[i].rssi; - item["encryption"] = networks[i].getAuthorizationMethodName(); + item["title"] = nw.ssid; + item["signal"] = nw.rssi; + item["encryption"] = nw.getAuthorizationMethodName(); } response.setAllowCrossDomainOrigin("*"); @@ -205,12 +206,18 @@ void startServers() void networkScanCompleted(bool succeeded, BssList& list) { - if(succeeded) { - for(unsigned i = 0; i < list.count(); i++) - if(!list[i].hidden && list[i].ssid.length() > 0) - networks.add(list[i]); + if(!succeeded) { + return; + } + + networks.clear(); + for(auto& nw : list) { + if(!nw.hidden && nw.ssid.length() > 0) { + networks.add(nw); + } } - networks.sort([](const BssInfo& a, const BssInfo& b) { return b.rssi - a.rssi; }); + + networks.sort([](auto& a, auto& b) { return b.rssi - a.rssi; }); } void init() @@ -232,8 +239,9 @@ void init() if(AppSettings.exist()) { WifiStation.config(AppSettings.ssid, AppSettings.password); - if(!AppSettings.dhcp && !AppSettings.ip.isNull()) + if(!AppSettings.dhcp && !AppSettings.ip.isNull()) { WifiStation.setIP(AppSettings.ip, AppSettings.netmask, AppSettings.gateway); + } } WifiStation.startScan(networkScanCompleted); diff --git a/tests/HostTests/modules/Wiring.cpp b/tests/HostTests/modules/Wiring.cpp index 63595ea939..647eccc88a 100644 --- a/tests/HostTests/modules/Wiring.cpp +++ b/tests/HostTests/modules/Wiring.cpp @@ -8,58 +8,47 @@ #include #include -class WiringTest : public TestGroup +namespace { -public: - WiringTest() : TestGroup(_F("Wiring")) - { - } +template Print& operator<<(Print& p, const std::pair& e) +{ + p << e.first << " = " << e.second; + return p; +} - template void println(const E& e) const - { - Serial.print(e.key()); - Serial.print(" = "); - Serial.println(*e); +template void print(const T& list, const char* separator = "\r\n") +{ + for(auto e : list) { + Serial << e << separator; } +} - template void print(const Map& map) const - { - for(auto e : map) { - println(e); - } - } +template void fillMap(T& map) +{ + auto startMem = MallocCount::getCurrent(); + map[MIME_HTML] = os_random() % 0xffff; + map[MIME_TEXT] = os_random() % 0xffff; + map[MIME_JS] = os_random() % 0xffff; + map[MIME_CSS] = os_random() % 0xffff; + map[MIME_XML] = os_random() % 0xffff; + map[MIME_JSON] = os_random() % 0xffff; + map[MIME_JPEG] = os_random() % 0xffff; + map[MIME_GIF] = os_random() % 0xffff; + map[MIME_PNG] = os_random() % 0xffff; + map[MIME_SVG] = os_random() % 0xffff; + map[MIME_ICO] = os_random() % 0xffff; + map[MIME_GZIP] = os_random() % 0xffff; + map[MIME_ZIP] = os_random() % 0xffff; + Serial << "fillMap heap " << MallocCount::getCurrent() - startMem << endl; +} - template void println(const std::pair& e) const - { - Serial.print(e.first); - Serial.print(" = "); - Serial.println(e.second); - } +} // namespace - template void print(const std::map& map) const - { - for(auto e : map) { - println(e); - } - } - - template void fillMap(T& map) +class WiringTest : public TestGroup +{ +public: + WiringTest() : TestGroup(_F("Wiring")) { - auto startMem = MallocCount::getCurrent(); - map[MIME_HTML] = os_random() % 0xffff; - map[MIME_TEXT] = os_random() % 0xffff; - map[MIME_JS] = os_random() % 0xffff; - map[MIME_CSS] = os_random() % 0xffff; - map[MIME_XML] = os_random() % 0xffff; - map[MIME_JSON] = os_random() % 0xffff; - map[MIME_JPEG] = os_random() % 0xffff; - map[MIME_GIF] = os_random() % 0xffff; - map[MIME_PNG] = os_random() % 0xffff; - map[MIME_SVG] = os_random() % 0xffff; - map[MIME_ICO] = os_random() % 0xffff; - map[MIME_GZIP] = os_random() % 0xffff; - map[MIME_ZIP] = os_random() % 0xffff; - Serial << "fillMap heap " << MallocCount::getCurrent() - startMem << endl; } void execute() override @@ -79,13 +68,15 @@ class WiringTest : public TestGroup print(map); for(auto e : map) { - String s = *e; - e->length(); + REQUIRE(e->startsWith("value")); } for(auto e : map) { *e += ": gobbed"; } + for(auto e : map) { + REQUIRE(e->endsWith("gobbed")); + } REQUIRE_EQ(map["b"], "value(b): gobbed"); @@ -143,17 +134,49 @@ class WiringTest : public TestGroup Serial << "Heap " << MallocCount::getCurrent() - startMem << endl; - for(auto& e : vector) { - Serial.println(e); - } + print(vector); for(auto& e : vector) { e += ": gobbed"; } - for(auto& e : vector) { - Serial.println(e); + CHECK(e.length() == 16 && e.endsWith("gobbed")); + } + + vector.setElementAt("potato", 1); + REQUIRE(vector[1] == "potato"); + REQUIRE(vector.count() == 4); + + vector[1] = "cabbage"; + REQUIRE(vector[1] == "cabbage"); + REQUIRE(vector.count() == 4); + + REQUIRE(!vector.insertElementAt("radish", 5)); + REQUIRE(vector.insertElementAt("radish", 4)); + REQUIRE(vector[4] == "radish"); + + REQUIRE(vector.firstElement() == "value(a): gobbed"); + REQUIRE(vector.lastElement() == "radish"); + + REQUIRE(vector.remove(2)); + REQUIRE(vector[2] == "value(d): gobbed"); + + REQUIRE(vector.setSize(3)); + REQUIRE_EQ(vector.count(), 3); + REQUIRE_EQ(vector.capacity(), 14); + + vector.trimToSize(); + REQUIRE_EQ(vector.capacity(), 3); + + String arr[3]; + vector.copyInto(arr); + for(unsigned i = 0; i < vector.count(); ++i) { + REQUIRE_EQ(vector[i], arr[i]); } + + REQUIRE(vector.addElement(new String("banana"))); + REQUIRE_EQ(vector.count(), 4); + REQUIRE_EQ(vector.capacity(), 13); } TEST_CASE("std::vector") @@ -168,17 +191,84 @@ class WiringTest : public TestGroup Serial << "Heap " << MallocCount::getCurrent() - startMem << endl; + print(vector); + for(auto& e : vector) { - Serial.println(e); + e += ": gobbed"; + } + + print(vector); + } + + TEST_CASE("Vector") + { + auto startMem = MallocCount::getCurrent(); + + Vector vector(32); + for(unsigned i = 0; i < 32; ++i) { + vector.add(os_random()); } + Serial << "Heap " << MallocCount::getCurrent() - startMem << endl; + + print(vector, ","); + Serial.println(); + for(auto& e : vector) { - e += ": gobbed"; + e += 12; } + print(vector, ","); + Serial.println(); + + vector.setElementAt(0, 1); + REQUIRE(vector[1] == 0); + REQUIRE(vector.count() == 32); + REQUIRE(vector.capacity() == 32); + + REQUIRE(!vector.insertElementAt(99, 35)); + REQUIRE(vector.insertElementAt(99, 32)); + REQUIRE(vector[32] == 99); + REQUIRE_EQ(vector.capacity(), 42); + + REQUIRE(vector.setSize(3)); + REQUIRE_EQ(vector.count(), 3); + REQUIRE_EQ(vector.capacity(), 42); + + vector.trimToSize(); + REQUIRE_EQ(vector.capacity(), 3); + + uint8_t arr[3]; + vector.copyInto(arr); + for(unsigned i = 0; i < vector.count(); ++i) { + REQUIRE_EQ(vector[i], arr[i]); + } + } + + TEST_CASE("std::vector") + { + auto startMem = MallocCount::getCurrent(); + + std::vector vector; + for(unsigned i = 0; i < 32; ++i) { + vector.push_back(os_random()); + } + + Serial << "Heap " << MallocCount::getCurrent() - startMem << endl; + + for(auto& e : vector) { + Serial << e << ", "; + } + Serial.println(); + for(auto& e : vector) { - Serial.println(e); + e += 12; } + + for(auto& e : vector) { + Serial << e << ", "; + } + Serial.println(); } TEST_CASE("MacAddress")