/* * Copyright 2024 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkTFixedArray_DEFINED #define SkTFixedArray_DEFINED #include "include/private/base/SkAssert.h" #include #include #include #include // IWYU pragma: keep for std::is_trivial_v namespace skia_private { /** * Represents an array of `T` (must be a trivial type) that cannot grow past a fixed size `N`. * The fixed-size restriction allows for tighter codegen and a smaller memory footprint. * Missing methods from TArray (e.g. `fromBack`) can be added on demand. * * The trivial-type restriction is only to simplify implementation; if there is a need, we can * adopt proper move/copy semantics in this class as well. */ template class FixedArray { public: using value_type = T; FixedArray() = default; FixedArray(std::initializer_list values) { SkASSERT(values.size() <= N); for (T value : values) { fData[fSize++] = value; } } FixedArray(int reserveCount) { // This is here to satisfy the TArray interface. Setting a reserve count on a fixed array // isn't useful. SkASSERT(reserveCount >= 0); SkASSERT(reserveCount <= N); } FixedArray(const T* array, int count) { this->reset(array, count); } FixedArray(const FixedArray& that) { this->reset(that.data(), that.size()); } FixedArray& operator=(const FixedArray& that) { if (this != &that) { this->reset(that.data(), that.size()); } return *this; } T& operator[](size_t index) { SkASSERT(index < fSize); return fData[index]; } const T& operator[](size_t index) const { SkASSERT(index < fSize); return fData[index]; } bool operator==(const FixedArray& that) const { return fSize == that.fSize && (0 == memcmp(fData, that.fData, fSize * sizeof(T))); } bool operator!=(const FixedArray& that) const { return !this->operator==(that); } int size() const { return fSize; } bool empty() const { return fSize == 0; } void clear() { fSize = 0; } void reset(const T* array, int count) { SkASSERT(count >= 0); SkASSERT(count <= N); fSize = count; std::memcpy(fData, array, count * sizeof(T)); } void resize(int newSize) { SkASSERT(newSize >= 0); SkASSERT(newSize <= N); if (fSize > newSize) { fSize = newSize; } else { while (fSize < newSize) { fData[fSize++] = T(); } } } T& push_back() { SkASSERT(fSize < N); T& ref = fData[fSize++]; ref = T(); return ref; } void push_back(T x) { SkASSERT(fSize < N); fData[fSize++] = x; } T* push_back_n(int n) { SkASSERT(n >= 0); SkASSERT(fSize + n <= N); T* ptr = fData + fSize; for (int index = 0; index < n; ++index) { ptr[index] = T(); } fSize += n; return ptr; } T* push_back_n(int n, const T& t) { SkASSERT(n >= 0); SkASSERT(fSize + n <= N); T* ptr = fData + fSize; for (int index = 0; index < n; ++index) { ptr[index] = t; } fSize += n; return ptr; } void pop_back() { SkASSERT(fSize >= 1); --fSize; } void pop_back_n(int n) { SkASSERT(fSize >= n); fSize -= n; } void removeShuffle(int n) { SkASSERT(n < fSize); int last = fSize - 1; if (n != last) { fData[n] = fData[last]; } fSize = last; } T* data() { return fData; } const T* data() const { return fData; } T* begin() { return fData; } const T* begin() const { return fData; } T* end() { return fData + fSize; } const T* end() const { return fData + fSize; } T& front() { SkASSERT(fSize > 0); return fData[0]; } const T& front() const { SkASSERT(fSize > 0); return fData[0]; } T& back() { SkASSERT(fSize > 0); return fData[fSize - 1]; } const T& back() const { SkASSERT(fSize > 0); return fData[fSize - 1]; } void reserve(int size) { // This is here to satisfy the TArray interface. SkASSERT(size >= 0); SkASSERT(size <= N); } constexpr int capacity() const { return N; } private: static_assert(std::is_trivial_v); static_assert(N > 0); static_assert(N < 256); // limited by `uint8_t fSize` T fData[N]; uint8_t fSize = 0; }; } // namespace skia_private #endif