Move fCount and fReserve to the common code
The common code is much more substantial now. The reduces the binary
size by another 8K.
herb@salt ~/s/skia (encapsulate-fArray-2)> ls -l *_skottie_tool.stripped
-rwxr-xr-x 1 herb primarygroup 6486584 new_skottie_tool.stripped*
-rwxr-xr-x 1 herb primarygroup 6494776 old_skottie_tool.stripped*
herb@salt ~/s/skia (encapsulate-fArray-2)> awk -e 'BEGIN {print 6494776 - 6486584}'
8192
Change-Id: I9ba30594f150d9ebd59ab2491b758fde3eb8449e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/583777
Reviewed-by: John Stiles <johnstiles@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/include/private/SkTDArray.h b/include/private/SkTDArray.h
index 417aada..096b992 100644
--- a/include/private/SkTDArray.h
+++ b/include/private/SkTDArray.h
@@ -13,6 +13,7 @@
#include "include/private/SkTo.h"
#include <algorithm>
+#include <cstddef>
#include <climits>
#include <initializer_list>
#include <tuple>
@@ -27,22 +28,63 @@
SkTDStorage& operator= (SkTDStorage&& that);
~SkTDStorage();
- int assign(const void* src, int count, size_t sizeOfT);
+ void reset();
- int resizeStorageToAtLeast(int count, size_t sizeOfT);
- int shrinkToFit(int count, size_t sizeOfT);
+ void assign(const void* src, int count, size_t sizeOfT);
+
+ bool empty() const { return fCount == 0; }
+ void clear() { fCount = 0; }
+ int size() const { return fCount; }
+
+ // Resizes the array to store exactly `newCount` elements.
+ //
+ // This never shrinks the allocation, and it may increase the allocation by
+ // more than is strictly required, based on a private growth heuristic.
+ void resize(int newCount, size_t sizeOfT);
+
+ int decreaseCount() {
+ SkASSERT(fCount > 0);
+ fCount -= 1;
+ return fCount;
+ }
+
+ void* push_back(size_t sizeOfT) {
+ if (fCount < fReserve) {
+ return fStorage + SkToSizeT(fCount++) * sizeOfT;
+ } else {
+ return this->append(sizeOfT);
+ }
+ }
+
+ size_t size_bytes(size_t sizeOfT) const;
+
+ int capacity() const { return fReserve; }
+ void reserve(size_t newReserve, size_t sizeOfT);
+
+ void shrinkToFit(size_t sizeOfT);
void swap(SkTDStorage& that) {
using std::swap;
swap(fStorage, that.fStorage);
}
- template <typename T>
- T* data() const { return static_cast<T*>(fStorage); }
+ template <typename T> T* data() const { return reinterpret_cast<T*>(fStorage); }
- struct StateUpdate {int count, reserve;};
- StateUpdate append(
- const void* src, int count, size_t sizeOfT, int reserve, int oldCount);
+ void* erase(int index, int count, size_t sizeOfT);
+ // Removes the entry at 'index' and replaces it with the last array element
+ void* removeShuffle(int index, size_t sizeOfT);
+
+ void* prepend(size_t sizeOfT);
+ void* append(size_t sizeOfT);
+ void* append(const void* src, int count, size_t sizeOfT);
+
+ void* insert(int index, size_t sizeOfT);
+ void* insert(int index, const void* src, int count, size_t sizeOfT);
+
private:
- void* fStorage{nullptr};
+ int calculateSizeDeltaOrDie(int delta) const;
+
+ std::byte* fStorage{nullptr};
+ int fReserve{0}; // size of the allocation in fArray (#elements)
+ int fCount{0}; // logical number of elements (fCount <= fReserve)
};
template <typename T> static inline void swap(SkTDStorage& a, SkTDStorage& b) { a.swap(b); }
@@ -58,102 +100,87 @@
SkTDArray() = default;
SkTDArray(const T src[], int count) {
SkASSERT(src || count == 0);
- fReserve = fStorage.assign(src, count, sizeof(T));
- fCount = count;
+ fStorage.assign(src, count, sizeof(T));
}
SkTDArray(const std::initializer_list<T>& list) : SkTDArray(list.begin(), list.size()) {}
SkTDArray(const SkTDArray<T>& src) {
- fReserve = fStorage.assign(src.data(), src.fCount, sizeof(T));
- fCount = src.fCount;
+ fStorage.assign(src.data(), src.count(), sizeof(T));
}
SkTDArray<T>& operator=(const SkTDArray<T>& src) {
if (this != &src) {
- if (src.fCount > fReserve) {
- fReserve = fStorage.assign(src.data(), src.fCount, sizeof(T));
- } else {
- sk_careful_memcpy(this->data(), src.data(), sizeof(T) * SkToSizeT(src.fCount));
- }
- fCount = src.fCount;
+ fStorage.assign(src.data(), src.count(), sizeof(T));
}
return *this;
}
SkTDArray(SkTDArray<T>&& src)
- : fStorage{std::move(src.fStorage)}
- , fReserve{src.fReserve}
- , fCount{src.fCount} {}
+ : fStorage{std::move(src.fStorage)} {}
SkTDArray<T>& operator=(SkTDArray<T>&& src) {
if (this != &src) {
fStorage = std::move(src.fStorage);
- fReserve = std::exchange(src.fReserve, 0);
- fCount = std::exchange(src.fCount, 0);
}
return *this;
}
friend bool operator==(const SkTDArray<T>& a, const SkTDArray<T>& b) {
- return a.fCount == b.fCount &&
- (a.fCount == 0 || !memcmp(a.data(), b.data(), SkToSizeT(a.fCount) * sizeof(T)));
+ return a.count() == b.count() &&
+ (a.count() == 0 || !memcmp(a.data(), b.data(), SkToSizeT(a.size()) * sizeof(T)));
}
friend bool operator!=(const SkTDArray<T>& a, const SkTDArray<T>& b) { return !(a == b); }
void swap(SkTDArray<T>& that) {
using std::swap;
swap(fStorage, that.fStorage);
- swap(fReserve, that.fReserve);
- swap(fCount, that.fCount);
}
- bool empty() const { return fCount == 0; }
+ bool empty() const { return fStorage.empty(); }
// Return the number of elements in the array
- int count() const { return fCount; }
- size_t size() const { return fCount; }
+ int count() const { return fStorage.size(); }
+ int size() const { return fStorage.size(); }
// Return the total number of elements allocated.
// reserved() - count() gives you the number of elements you can add
// without causing an allocation.
- int reserved() const { return fReserve; }
+ int reserved() const { return fStorage.capacity(); }
// return the number of bytes in the array: count * sizeof(T)
- size_t bytes() const { return fCount * sizeof(T); }
+ size_t bytes() const { return SkToSizeT(this->size()) * sizeof(T); }
T* data() { return fStorage.data<T>(); }
const T* data() const { return fStorage.data<T>(); }
T* begin() { return this->data(); }
const T* begin() const { return this->data(); }
- T* end() { return this->data() ? this->data() + fCount : nullptr; }
- const T* end() const { return this->data() ? this->data() + fCount : nullptr; }
+ T* end() { return this->data() ? this->data() + this->size() : nullptr; }
+ const T* end() const { return this->data() ? this->data() + this->size() : nullptr; }
T& operator[](int index) {
- SkASSERT(index < fCount);
+ SkASSERT(index < this->size());
return this->data()[index];
}
const T& operator[](int index) const {
- SkASSERT(index < fCount);
+ SkASSERT(index < this->size());
return this->data()[index];
}
T& getAt(int index) { return (*this)[index]; }
const T& back() const {
- SkASSERT(fCount > 0);
- return this->data()[fCount - 1];
+ SkASSERT(this->size() > 0);
+ return this->data()[this->size() - 1];
}
T& back() {
- SkASSERT(fCount > 0);
- return this->data()[fCount - 1];
+ SkASSERT(this->size() > 0);
+ return this->data()[this->size() - 1];
}
void reset() {
- this->~SkTDArray();
- new (this) SkTDArray{};
+ fStorage.reset();
}
void rewind() {
- // same as setCount(0)
- fCount = 0;
+ fStorage.clear();
}
// Sets the number of elements in the array.
@@ -161,155 +188,89 @@
// the storage allocated to some amount greater than that required.
// It will never shrink the storage.
void setCount(int count) {
- SkASSERT(count >= 0);
- if (count > fReserve) {
- this->resizeStorageToAtLeast(count);
- }
- fCount = count;
+ fStorage.resize(count, sizeof(T));
}
- void reserve(int newReserve) {
- SkASSERT(0 <= newReserve);
- if (fReserve < newReserve) {
- this->resizeStorageToAtLeast(newReserve);
- }
+ void reserve(size_t n) {
+ fStorage.reserve(n, sizeof(T));
}
- T* append() { return this->append(1, nullptr); }
+ T* append() {
+ return reinterpret_cast<T*>(fStorage.append(sizeof(T)));
+ }
T* append(int count, const T* src = nullptr) {
- int oldCount = fCount;
- auto [newCount, newReserve] = fStorage.append(src, count, sizeof(T), fReserve, fCount);
- fCount = newCount;
- fReserve = newReserve;
- return this->data() + oldCount;
+ return reinterpret_cast<T*>(fStorage.append(src, count, sizeof(T)));
}
- T* insert(int index) { return this->insert(index, 1, nullptr); }
+ T* insert(int index) {
+ return reinterpret_cast<T*>(fStorage.insert(index, sizeof(T)));
+ }
T* insert(int index, int count, const T* src = nullptr) {
- SkASSERT(count);
- SkASSERT(index <= fCount);
- size_t oldCount = fCount;
- this->adjustCount(count);
- T* dst = this->data() + index;
- memmove(dst + count, dst, sizeof(T) * (oldCount - index));
- if (src) {
- memcpy(dst, src, sizeof(T) * count);
- }
- return dst;
+ return reinterpret_cast<T*>(fStorage.insert(index, src, count, sizeof(T)));
}
void remove(int index, int count = 1) {
- SkASSERT(index + count <= fCount);
- fCount = fCount - count;
- memmove(this->data() + index, this->data() + index + count, sizeof(T) * (fCount - index));
+ fStorage.erase(index, count, sizeof(T));
}
void removeShuffle(int index) {
- SkASSERT(index < fCount);
- int newCount = fCount - 1;
- fCount = newCount;
- if (index != newCount) {
- memcpy(this->data() + index, this->data() + newCount, sizeof(T));
- }
+ fStorage.removeShuffle(index, sizeof(T));
}
int find(const T& elem) const {
- const T* iter = this->data();
- const T* stop = this->data() + fCount;
+ const T* iter = this->begin();
+ const T* stop = this->end();
for (; iter < stop; iter++) {
if (*iter == elem) {
- return SkToInt(iter - this->data());
+ return SkToInt(iter - this->begin());
}
}
return -1;
}
// routines to treat the array like a stack
- void push_back(const T& v) { *this->append() = v; }
+ void push_back(const T& v) { *reinterpret_cast<T*>(fStorage.push_back(sizeof(T))) = v; }
void pop(T* elem) {
- SkASSERT(fCount > 0);
- if (elem) *elem = (*this)[fCount - 1];
- --fCount;
+ SkASSERT(this->size() > 0);
+ if (elem) {
+ *elem = (*this)[this->size() - 1];
+ }
+ fStorage.decreaseCount();
}
void pop() {
- SkASSERT(fCount > 0);
- --fCount;
+ fStorage.decreaseCount();
}
void deleteAll() {
- T* iter = this->data();
- T* stop = this->data() + fCount;
- while (iter < stop) {
- delete *iter;
- iter += 1;
+ for (T p : *this) {
+ delete p;
}
this->reset();
}
void freeAll() {
- T* iter = this->data();
- T* stop = this->data() + fCount;
- while (iter < stop) {
- sk_free(*iter);
- iter += 1;
+ for (T p : *this) {
+ sk_free(p);
}
+
this->reset();
}
void unrefAll() {
- T* iter = this->data();
- T* stop = this->data() + fCount;
- while (iter < stop) {
- (*iter)->unref();
- iter += 1;
+ for (T p : *this) {
+ p->unref();
}
this->reset();
}
-#ifdef SK_DEBUG
- void validate() const {
- SkASSERT((fReserve == 0 && this->data() == nullptr) ||
- (fReserve > 0 && this->data() != nullptr));
- SkASSERT(fCount <= fReserve);
- }
-#endif
-
void shrinkToFit() {
- if (fReserve > fCount) {
- fReserve = fStorage.shrinkToFit(fCount, sizeof(T));
- }
+ fStorage.shrinkToFit(sizeof(T));
}
private:
- // Adjusts the number of elements in the array.
- // This is the same as calling setCount(count() + delta).
- void adjustCount(int delta) {
- SkASSERT(delta > 0);
-
- // We take care to avoid overflow here.
- // The sum of fCount and delta is at most 4294967294, which fits fine in uint32_t.
- uint32_t count = (uint32_t)fCount + (uint32_t)delta;
- SkASSERT_RELEASE(SkTFitsIn<int>(count));
-
- this->setCount(SkTo<int>(count));
- }
-
- // Increase the storage allocation such that it can hold (fCount + extra)
- // elements.
- // It never shrinks the allocation, and it may increase the allocation by
- // more than is strictly required, based on a private growth heuristic.
- //
- // note: this does NOT modify fCount
- void resizeStorageToAtLeast(int count) {
- SkASSERT(count > fReserve);
- fReserve = fStorage.resizeStorageToAtLeast(count, sizeof(T));
- }
-
SkTDStorage fStorage;
- int fReserve = 0; // size of the allocation in fArray (#elements)
- int fCount = 0; // logical number of elements (fCount <= fReserve)
};
template <typename T> static inline void swap(SkTDArray<T>& a, SkTDArray<T>& b) { a.swap(b); }
diff --git a/src/core/SkTDArray.cpp b/src/core/SkTDArray.cpp
index aeac67a..80bc700 100644
--- a/src/core/SkTDArray.cpp
+++ b/src/core/SkTDArray.cpp
@@ -5,11 +5,26 @@
* found in the LICENSE file.
*/
+#include "include/private/SkMalloc.h"
#include "include/private/SkTDArray.h"
#include "include/private/SkTo.h"
+#include <utility>
+
+namespace {
+size_t offset(int n, size_t sizeOfT) {
+ return SkToSizeT(n) * sizeOfT;
+}
+
+size_t mem_size(int n, size_t sizeOfT) {
+ return SkToSizeT(n) * sizeOfT;
+}
+} // namespace
+
SkTDStorage::SkTDStorage(SkTDStorage&& that)
- : fStorage{std::move(that.fStorage)} { that.fStorage = nullptr; }
+ : fStorage{std::move(std::exchange(that.fStorage, nullptr))}
+ , fReserve{std::exchange(that.fReserve, 0)}
+ , fCount{std::exchange(that.fCount, 0)} {}
SkTDStorage& SkTDStorage::operator=(SkTDStorage&& that) {
if (this != &that) {
@@ -23,60 +38,170 @@
sk_free(fStorage);
}
-int SkTDStorage::assign(const void* src, int count, size_t sizeOfT) {
- if (count > 0) {
- fStorage = sk_realloc_throw(fStorage, SkToSizeT(count) * sizeOfT);
- memcpy(fStorage, src, SkToSizeT(count) * sizeOfT);
- }
- return count;
+void SkTDStorage::reset() {
+ this->~SkTDStorage();
+ new (this) SkTDStorage{};
}
-int SkTDStorage::resizeStorageToAtLeast(int count, size_t sizeOfT) {
- SkASSERT(count > 0);
+void SkTDStorage::assign(const void* src, int count, size_t sizeOfT) {
+ SkASSERT(count >= 0);
+ if (count > 0) {
+ fCount = count;
+ size_t byteSize = this->size_bytes(sizeOfT);
+ if (fCount > fReserve) {
+ fStorage = static_cast<std::byte*>(sk_realloc_throw(fStorage, byteSize));
+ fReserve = fCount;
+ }
+ memcpy(fStorage, src, byteSize);
+ }
+}
+
+void SkTDStorage::resize(int newCount, size_t sizeOfT) {
+ SkASSERT(newCount >= 0);
+ if (fReserve < newCount) {
+ this->reserve(newCount, sizeOfT);
+ }
+ fCount = newCount;
+}
+
+size_t SkTDStorage::size_bytes(size_t sizeOfT) const {
+ return offset(fCount, sizeOfT);
+}
+
+void SkTDStorage::reserve(size_t reserveSize, size_t sizeOfT) {
+ // Note: this takes a size_t to centralize size checking.
+ SkASSERT_RELEASE(SkTFitsIn<int>(reserveSize));
+
// 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 newReserve = kMaxCount;
- if (kMaxCount - count > 4) {
+ int expandedReserve = kMaxCount;
+ int newReserve = SkToInt(reserveSize);
+ if (kMaxCount - newReserve > 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 + ((count + 4) >> 2);
+ int growth = 4 + ((newReserve + 4) >> 2);
// Read this line as: if (count + growth < kMaxCount) { ... }
// It's rewritten to avoid signed integer overflow.
- if (kMaxCount - count > growth) {
- newReserve = count + growth;
+ if (kMaxCount - newReserve > growth) {
+ expandedReserve = newReserve + growth;
}
}
- fStorage = sk_realloc_throw(fStorage, SkToSizeT(newReserve) * sizeOfT);
- return newReserve;
+ fReserve = expandedReserve;
+ fStorage = static_cast<std::byte*>(sk_realloc_throw(fStorage, mem_size(fReserve, sizeOfT)));
}
-int SkTDStorage::shrinkToFit(int count, size_t sizeOfT) {
- fStorage = sk_realloc_throw(fStorage, SkToSizeT(count) * sizeOfT);
- return count;
+void SkTDStorage::shrinkToFit(size_t sizeOfT) {
+ // Because calling realloc with size of 0 is implementation defined, force to a good state by
+ // freeing fStorage and setting reserve to 0.
+ if (fCount != 0) {
+ fReserve = fCount;
+ fStorage = static_cast<std::byte*>(sk_realloc_throw(fStorage, mem_size(fReserve, sizeOfT)));
+ } else {
+ fReserve = 0;
+ sk_free(fStorage);
+ fStorage = nullptr;
+ }
}
-SkTDStorage::StateUpdate SkTDStorage::append(
- const void* src, int count, size_t sizeOfT, int reserve, int oldCount) {
+void* SkTDStorage::erase(int index, int count, size_t sizeOfT) {
+ SkASSERT(index >= 0);
SkASSERT(count >= 0);
- int newCount = oldCount;
- int newReserve = reserve;
+ SkASSERT(index + count <= fCount);
+
+ // Check that the resulting size fits in an int. This will abort if not.
+ const int newCount = this->calculateSizeDeltaOrDie(-count);
+
+ const int tailBegin = index + count;
+ const size_t tailBeginOffset = offset(tailBegin, sizeOfT);
if (count > 0) {
- // We take care to avoid overflow here.
- // The sum of fCount and delta is at most 4294967294, which fits fine in uint32_t.
- uint32_t testCount = (uint32_t)oldCount + (uint32_t)count;
- SkASSERT_RELEASE(SkTFitsIn<int>(testCount));
- newCount = testCount;
- if (newCount > reserve) {
- newReserve = this->resizeStorageToAtLeast(newCount, sizeOfT);
+ // Move the tail down if there is one.
+ if (tailBegin < fCount) {
+ const size_t headEndOffset = offset(index, sizeOfT);
+ const size_t gapSize = this->size_bytes(sizeOfT) - tailBeginOffset;
+ memmove(fStorage + headEndOffset, fStorage + tailBeginOffset, gapSize);
}
+ this->resize(newCount, sizeOfT);
+ }
+
+ return fStorage + tailBeginOffset;
+}
+
+void* SkTDStorage::removeShuffle(int index, size_t sizeOfT) {
+ SkASSERT(0 <= index && index < fCount);
+ // Check that the new count is valid.
+ int newCount = this->calculateSizeDeltaOrDie(-1);
+
+ const size_t indexOffset = offset(index, sizeOfT);
+ const size_t lastElementOffset = offset(newCount, sizeOfT);
+
+ // Fill the index if not the last element.
+ if (index < newCount) {
+ memmove(fStorage + indexOffset, fStorage + lastElementOffset, sizeOfT);
+ }
+ this->resize(newCount, sizeOfT);
+ return fStorage + lastElementOffset;
+}
+
+void* SkTDStorage::prepend(size_t sizeOfT) {
+ return this->insert(/*index=*/0, sizeOfT);
+}
+
+void* SkTDStorage::append(size_t sizeOfT) {
+ return this->insert(fCount, sizeOfT);
+}
+
+void* SkTDStorage::append(const void* src, int count, size_t sizeOfT) {
+ return this->insert(fCount, src, count, sizeOfT);
+}
+
+void* SkTDStorage::insert(int index, size_t sizeOfT) {
+ return this->insert(index, nullptr, /*count=*/1, sizeOfT);
+}
+
+void* SkTDStorage::insert(int index, const void* src, int count, size_t sizeOfT) {
+ SkASSERT(0 <= index && index <= fCount);
+ SkASSERT(count >= 0);
+ const size_t indexOffset = offset(index, sizeOfT);
+
+ if (count > 0) {
+ const int oldCount = fCount;
+
+ size_t oldCountOffset = this->size_bytes(sizeOfT);
+
+ const int newCount = this->calculateSizeDeltaOrDie(count);
+ this->resize(newCount, sizeOfT);
+
+ // Shift memory to make space.
+ if (index < oldCount) {
+ // Safe because index <= oldCount and oldCount + count is safe.
+ size_t shiftOffset = offset(index + count, sizeOfT);
+
+ // Move the tail of data from index to oldCount.
+ memmove(fStorage + shiftOffset, fStorage + indexOffset, oldCountOffset - indexOffset);
+ }
+
if (src != nullptr) {
- memcpy(this->data<char>() + sizeOfT *SkToSizeT(oldCount), src,
- sizeOfT *SkToSizeT(count));
+ memcpy(fStorage + indexOffset, src, mem_size(count, sizeOfT));
}
}
- return {newCount, newReserve};
+
+ return fStorage + indexOffset;
}
+
+int SkTDStorage::calculateSizeDeltaOrDie(int delta) const {
+ // Check that count will not go negative.
+ SkASSERT_RELEASE(-fCount <= delta);
+
+ // We take care to avoid overflow here.
+ // Because fCount and delta are both signed 32-bit ints, the sum of fCount 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 count = (uint32_t)this->size() + (uint32_t)delta;
+ SkASSERT_RELEASE(SkTFitsIn<int>(count));
+ return SkToInt(count);
+}
+
diff --git a/src/utils/SkCharToGlyphCache.cpp b/src/utils/SkCharToGlyphCache.cpp
index 5536047..7387ed1 100644
--- a/src/utils/SkCharToGlyphCache.cpp
+++ b/src/utils/SkCharToGlyphCache.cpp
@@ -108,7 +108,7 @@
void SkCharToGlyphCache::insertCharAndGlyph(int index, SkUnichar unichar, SkGlyphID glyph) {
SkASSERT(fK32.size() == fV16.size());
- SkASSERT((unsigned)index < fK32.size());
+ SkASSERT(index < fK32.size());
SkASSERT(unichar < fK32[index]);
*fK32.insert(index) = unichar;