| /* |
| * Copyright 2022 Rive |
| */ |
| |
| #ifndef _RIVE_SIMPLE_ARRAY_HPP_ |
| #define _RIVE_SIMPLE_ARRAY_HPP_ |
| |
| #include "rive/rive_types.hpp" |
| |
| #include <initializer_list> |
| #include <type_traits> |
| #include <cstring> |
| #include <iterator> |
| |
| namespace rive |
| { |
| |
| template <typename T> class SimpleArrayBuilder; |
| |
| #ifdef TESTING |
| namespace SimpleArrayTesting |
| { |
| extern int mallocCount; |
| extern int reallocCount; |
| extern int freeCount; |
| void resetCounters(); |
| } // namespace SimpleArrayTesting |
| #endif |
| |
| /// Lightweight heap array meant to be used when knowing the exact memory layout |
| /// of the simple array is necessary, like marshaling the data to Dart/C#/Wasm. |
| /// Note that it intentionally doesn't have push/add/resize functionality as |
| /// that's reserved for a special case builder that should be to build up the |
| /// array. This saves the structure from needing to store extra ptrs and keeps |
| /// it optimally sized for marshaling. See SimpleArrayBuilder<T> below for push |
| /// functionality. |
| |
| template <typename T> class SimpleArray |
| { |
| public: |
| SimpleArray() : m_ptr(nullptr), m_size(0) {} |
| SimpleArray(size_t size) : m_ptr(static_cast<T*>(malloc(size * sizeof(T)))), m_size(size) |
| { |
| if constexpr (!std::is_pod<T>()) |
| { |
| for (T *element = m_ptr, *end = m_ptr + m_size; element < end; element++) |
| { |
| new (element) T(); |
| } |
| } |
| |
| #ifdef TESTING |
| SimpleArrayTesting::mallocCount++; |
| #endif |
| } |
| SimpleArray(const T* ptr, size_t size) : SimpleArray(size) |
| { |
| assert(ptr <= ptr + size); |
| if constexpr (std::is_pod<T>()) |
| { |
| memcpy(m_ptr, ptr, size * sizeof(T)); |
| } |
| else |
| { |
| for (T *element = m_ptr, *end = m_ptr + m_size; element < end; element++) |
| { |
| new (element) T(ptr++); |
| } |
| } |
| } |
| |
| constexpr SimpleArray(const SimpleArray<T>& other) : SimpleArray(other.m_ptr, other.m_size) {} |
| |
| constexpr SimpleArray(SimpleArray<T>&& other) : m_ptr(other.m_ptr), m_size(other.m_size) |
| { |
| other.m_ptr = nullptr; |
| other.m_size = 0; |
| } |
| |
| constexpr SimpleArray(SimpleArrayBuilder<T>&& other); |
| |
| SimpleArray<T>& operator=(const SimpleArray<T>& other) = delete; |
| |
| SimpleArray<T>& operator=(SimpleArray<T>&& other) |
| { |
| this->m_ptr = other.m_ptr; |
| this->m_size = other.m_size; |
| other.m_ptr = nullptr; |
| other.m_size = 0; |
| return *this; |
| } |
| |
| SimpleArray<T>& operator=(SimpleArrayBuilder<T>&& other); |
| |
| template <typename Container> |
| constexpr SimpleArray(Container& c) : SimpleArray(std::data(c), std::size(c)) |
| {} |
| constexpr SimpleArray(std::initializer_list<T> il) : SimpleArray(std::data(il), std::size(il)) |
| {} |
| ~SimpleArray() |
| { |
| if constexpr (!std::is_pod<T>()) |
| { |
| for (T *element = m_ptr, *end = m_ptr + m_size; element < end; element++) |
| { |
| element->~T(); |
| } |
| } |
| free(m_ptr); |
| #ifdef TESTING |
| SimpleArrayTesting::freeCount++; |
| #endif |
| } |
| |
| constexpr T& operator[](size_t index) const |
| { |
| assert(index < m_size); |
| return m_ptr[index]; |
| } |
| |
| constexpr T* data() const { return m_ptr; } |
| constexpr size_t size() const { return m_size; } |
| constexpr bool empty() const { return m_size == 0; } |
| |
| constexpr T* begin() const { return m_ptr; } |
| constexpr T* end() const { return m_ptr + m_size; } |
| |
| constexpr T& front() const { return (*this)[0]; } |
| constexpr T& back() const { return (*this)[m_size - 1]; } |
| |
| // returns byte-size of the entire simple array |
| constexpr size_t size_bytes() const { return m_size * sizeof(T); } |
| |
| // Makes rive::SimpleArray std::Container compatible |
| // https://en.cppreference.com/w/cpp/named_req/Container |
| typedef typename std::remove_cv<T>::type value_type; |
| typedef T& reference; |
| typedef T const& const_reference; |
| typedef T* iterator; |
| typedef T const* const_iterator; |
| typedef std::ptrdiff_t difference_type; |
| typedef size_t size_type; |
| |
| protected: |
| T* m_ptr; |
| size_t m_size; |
| }; |
| |
| /// Extension of SimpleArray which can progressively expand as contents are |
| /// pushed/added/written to it. Can be released as a simple SimpleArray. |
| template <typename T> class SimpleArrayBuilder : public SimpleArray<T> |
| { |
| friend class SimpleArray<T>; |
| |
| public: |
| SimpleArrayBuilder(size_t reserve) : SimpleArray<T>(reserve) |
| { |
| assert(this->m_ptr <= this->m_ptr + this->m_size); |
| m_write = this->m_ptr; |
| } |
| |
| SimpleArrayBuilder() : SimpleArrayBuilder(0) {} |
| |
| void add(const T& value) |
| { |
| growToFit(); |
| *m_write++ = value; |
| } |
| |
| void add(T&& value) |
| { |
| growToFit(); |
| *m_write++ = std::move(value); |
| } |
| |
| // Allows iterating just the written content. |
| constexpr size_t capacity() const { return this->m_size; } |
| constexpr size_t size() const { return m_write - this->m_ptr; } |
| constexpr bool empty() const { return size() == 0; } |
| constexpr T* begin() const { return this->m_ptr; } |
| constexpr T* end() const { return m_write; } |
| |
| constexpr T& front() const { return (*this)[0]; } |
| constexpr T& back() const { return *(m_write - 1); } |
| |
| private: |
| void growToFit() |
| { |
| if (m_write == this->m_ptr + this->m_size) |
| { |
| auto writeOffset = m_write - this->m_ptr; |
| this->resize(std::max((size_t)1, this->m_size * 2)); |
| m_write = this->m_ptr + writeOffset; |
| } |
| } |
| |
| void resize(size_t size) |
| { |
| if (size == this->m_size) |
| { |
| return; |
| } |
| #ifdef TESTING |
| SimpleArrayTesting::reallocCount++; |
| #endif |
| if constexpr (!std::is_pod<T>()) |
| { |
| // Call destructor for elements when sizing down. |
| for (T *element = this->m_ptr + size, *end = this->m_ptr + this->m_size; element < end; |
| element++) |
| { |
| element->~T(); |
| } |
| } |
| this->m_ptr = static_cast<T*>(realloc(this->m_ptr, size * sizeof(T))); |
| if constexpr (!std::is_pod<T>()) |
| { |
| // Call constructor for elements when sizing up. |
| for (T *element = this->m_ptr + this->m_size, *end = this->m_ptr + size; element < end; |
| element++) |
| { |
| new (element) T(); |
| } |
| } |
| this->m_size = size; |
| } |
| |
| T* m_write; |
| }; |
| |
| template <typename T> |
| constexpr SimpleArray<T>::SimpleArray(SimpleArrayBuilder<T>&& other) : m_size(other.size()) |
| { |
| // Bring the capacity down to the actual size (this should keep the same |
| // ptr, but that's not guaranteed, so we copy the ptr after the realloc). |
| other.resize(other.size()); |
| m_ptr = other.m_ptr; |
| other.m_ptr = nullptr; |
| other.m_size = 0; |
| } |
| |
| template <typename T> SimpleArray<T>& SimpleArray<T>::operator=(SimpleArrayBuilder<T>&& other) |
| { |
| other.resize(other.size()); |
| this->m_ptr = other.m_ptr; |
| this->m_size = other.m_size; |
| other.m_ptr = nullptr; |
| other.m_size = 0; |
| return *this; |
| } |
| |
| } // namespace rive |
| |
| #endif |