* 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>
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) {
static constexpr int kMaxCount = INT_MAX;
int expandedReserve = kMaxCount;
if (kMaxCount - newCapacity > 4) {
int growth = 4 + ((newCapacity + 4) >> 2);
if (kMaxCount - newCapacity > growth) {
expandedReserve = newCapacity + growth;
}
}
if (fSizeOfT == 1) {
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;
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) {
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);
const int newCount = this->calculateSizeOrDie(-1);
this->moveTail(index, fSize - 1, fSize);
this->resize(newCount);
}
void* SkTDStorage::prepend() {
return this->insert(0);
}
void SkTDStorage::append() {
if (fSize < fCapacity) {
fSize++;
} else {
this->insert(fSize);
}
}
void SkTDStorage::append(int count) {
SkASSERT(count >= 0);
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, 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.empty() || !memcmp(a.data(), b.data(), a.bytes(a.size())));
}
int SkTDStorage::calculateSizeOrDie(int delta) {
SkASSERT_RELEASE(-fSize <= delta);
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));
}