diff options
Diffstat (limited to 'gfx/skia/skia/src/base/SkTDArray.cpp')
-rw-r--r-- | gfx/skia/skia/src/base/SkTDArray.cpp | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/base/SkTDArray.cpp b/gfx/skia/skia/src/base/SkTDArray.cpp new file mode 100644 index 0000000000..2cf7780f95 --- /dev/null +++ b/gfx/skia/skia/src/base/SkTDArray.cpp @@ -0,0 +1,240 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "include/private/base/SkTDArray.h" + +#include "include/private/base/SkMalloc.h" +#include "include/private/base/SkTFitsIn.h" +#include "include/private/base/SkTo.h" + +#include <climits> +#include <cstddef> +#include <cstdint> +#include <cstring> +#include <new> +#include <utility> + +SkTDStorage::SkTDStorage(int sizeOfT) : fSizeOfT{sizeOfT} {} + +SkTDStorage::SkTDStorage(const void* src, int size, int sizeOfT) + : fSizeOfT{sizeOfT} + , fCapacity{size} + , fSize{size} { + if (size > 0) { + SkASSERT(src != nullptr); + size_t storageSize = this->bytes(size); + fStorage = static_cast<std::byte*>(sk_malloc_throw(storageSize)); + memcpy(fStorage, src, storageSize); + } +} + +SkTDStorage::SkTDStorage(const SkTDStorage& that) + : SkTDStorage{that.fStorage, that.fSize, that.fSizeOfT} {} + +SkTDStorage& SkTDStorage::operator=(const SkTDStorage& that) { + if (this != &that) { + if (that.fSize <= fCapacity) { + fSize = that.fSize; + if (fSize > 0) { + memcpy(fStorage, that.data(), that.size_bytes()); + } + } else { + *this = SkTDStorage{that.data(), that.size(), that.fSizeOfT}; + } + } + return *this; +} + +SkTDStorage::SkTDStorage(SkTDStorage&& that) + : fSizeOfT{that.fSizeOfT} + , fStorage(std::exchange(that.fStorage, nullptr)) + , fCapacity{that.fCapacity} + , fSize{that.fSize} {} + +SkTDStorage& SkTDStorage::operator=(SkTDStorage&& that) { + if (this != &that) { + this->~SkTDStorage(); + new (this) SkTDStorage{std::move(that)}; + } + return *this; +} + +SkTDStorage::~SkTDStorage() { + sk_free(fStorage); +} + +void SkTDStorage::reset() { + const int sizeOfT = fSizeOfT; + this->~SkTDStorage(); + new (this) SkTDStorage{sizeOfT}; +} + +void SkTDStorage::swap(SkTDStorage& that) { + SkASSERT(fSizeOfT == that.fSizeOfT); + using std::swap; + swap(fStorage, that.fStorage); + swap(fCapacity, that.fCapacity); + swap(fSize, that.fSize); +} + +void SkTDStorage::resize(int newSize) { + SkASSERT(newSize >= 0); + if (newSize > fCapacity) { + this->reserve(newSize); + } + fSize = newSize; +} + +void SkTDStorage::reserve(int newCapacity) { + SkASSERT(newCapacity >= 0); + if (newCapacity > fCapacity) { + // Establish the maximum number of elements that includes a valid count for end. In the + // largest case end() = &fArray[INT_MAX] which is 1 after the last indexable element. + static constexpr int kMaxCount = INT_MAX; + + // Assume that the array will max out. + int expandedReserve = kMaxCount; + if (kMaxCount - newCapacity > 4) { + // Add 1/4 more than we need. Add 4 to ensure this grows by at least 1. Pin to + // kMaxCount if no room for 1/4 growth. + int growth = 4 + ((newCapacity + 4) >> 2); + // Read this line as: if (count + growth < kMaxCount) { ... } + // It's rewritten to avoid signed integer overflow. + if (kMaxCount - newCapacity > growth) { + expandedReserve = newCapacity + growth; + } + } + + + // With a T size of 1, the above allocator produces the progression of 7, 15, ... Since, + // the sizeof max_align_t is often 16, there is no reason to allocate anything less than + // 16 bytes. This eliminates a realloc when pushing back bytes to an SkTDArray. + if (fSizeOfT == 1) { + // Round up to the multiple of 16. + expandedReserve = (expandedReserve + 15) & ~15; + } + + fCapacity = expandedReserve; + size_t newStorageSize = this->bytes(fCapacity); + fStorage = static_cast<std::byte*>(sk_realloc_throw(fStorage, newStorageSize)); + } +} + +void SkTDStorage::shrink_to_fit() { + if (fCapacity != fSize) { + fCapacity = fSize; + // Because calling realloc with size of 0 is implementation defined, force to a good state + // by freeing fStorage. + if (fCapacity > 0) { + fStorage = static_cast<std::byte*>(sk_realloc_throw(fStorage, this->bytes(fCapacity))); + } else { + sk_free(fStorage); + fStorage = nullptr; + } + } +} + +void SkTDStorage::erase(int index, int count) { + SkASSERT(count >= 0); + SkASSERT(fSize >= count); + SkASSERT(0 <= index && index <= fSize); + + if (count > 0) { + // Check that the resulting size fits in an int. This will abort if not. + const int newCount = this->calculateSizeOrDie(-count); + this->moveTail(index, index + count, fSize); + this->resize(newCount); + } +} + +void SkTDStorage::removeShuffle(int index) { + SkASSERT(fSize > 0); + SkASSERT(0 <= index && index < fSize); + // Check that the new count is valid. + const int newCount = this->calculateSizeOrDie(-1); + this->moveTail(index, fSize - 1, fSize); + this->resize(newCount); +} + +void* SkTDStorage::prepend() { + return this->insert(/*index=*/0); +} + +void SkTDStorage::append() { + if (fSize < fCapacity) { + fSize++; + } else { + this->insert(fSize); + } +} + +void SkTDStorage::append(int count) { + SkASSERT(count >= 0); + // Read as: if (fSize + count <= fCapacity) {...}. This is a UB safe way to avoid the add. + if (fCapacity - fSize >= count) { + fSize += count; + } else { + this->insert(fSize, count, nullptr); + } +} + +void* SkTDStorage::append(const void* src, int count) { + return this->insert(fSize, count, src); +} + +void* SkTDStorage::insert(int index) { + return this->insert(index, /*count=*/1, nullptr); +} + +void* SkTDStorage::insert(int index, int count, const void* src) { + SkASSERT(0 <= index && index <= fSize); + SkASSERT(count >= 0); + + if (count > 0) { + const int oldCount = fSize; + const int newCount = this->calculateSizeOrDie(count); + this->resize(newCount); + this->moveTail(index + count, index, oldCount); + + if (src != nullptr) { + this->copySrc(index, src, count); + } + } + + return this->address(index); +} + +bool operator==(const SkTDStorage& a, const SkTDStorage& b) { + return a.size() == b.size() && + (a.size() == 0 || !memcmp(a.data(), b.data(), a.bytes(a.size()))); +} + +int SkTDStorage::calculateSizeOrDie(int delta) { + // Check that count will not go negative. + SkASSERT_RELEASE(-fSize <= delta); + + // We take care to avoid overflow here. + // Because count and delta are both signed 32-bit ints, the sum of count and delta is at + // most 4294967294, which fits fine in uint32_t. Proof follows in assert. + static_assert(UINT32_MAX >= (uint32_t)INT_MAX + (uint32_t)INT_MAX); + uint32_t testCount = (uint32_t)fSize + (uint32_t)delta; + SkASSERT_RELEASE(SkTFitsIn<int>(testCount)); + return SkToInt(testCount); +} + +void SkTDStorage::moveTail(int to, int tailStart, int tailEnd) { + SkASSERT(0 <= to && to <= fSize); + SkASSERT(0 <= tailStart && tailStart <= tailEnd && tailEnd <= fSize); + if (to != tailStart && tailStart != tailEnd) { + this->copySrc(to, this->address(tailStart), tailEnd - tailStart); + } +} + +void SkTDStorage::copySrc(int dstIndex, const void* src, int count) { + SkASSERT(count > 0); + memmove(this->address(dstIndex), src, this->bytes(count)); +} |