Reland "Saves 20K: Move non-parametric code for SkTDArray to common code"

This is a reland of commit e529a4637f677a9463c26d87f78330ec5483e7e4

Add SK_SPI to get the symbols to show up.

Original change's description:
> Saves 20K: Move non-parametric code for SkTDArray to common code
>
> Share common code from each of the template instances.
> The big wins in sharing:
>    resizeStorageToAtLeast
>    append
>    redoing the constructors
>
> Savings:
> From bloaty:
>     FILE SIZE        VM SIZE
>  --------------  --------------
>   +100%    -128  +100%    -128    .rodata
>    +99% -1.44Ki   +99% -1.44Ki    .eh_frame_hdr
>    +65% -2.76Ki  [ = ]       0    [Unmapped]
>    +99% -6.34Ki   +99% -6.34Ki    .eh_frame
>   +100% -9.34Ki  +100% -9.34Ki    .text
>
> Total -20Ki
>
> Stripped binary difference:
> 6502952 -> 6482472 = -20480
>
> Bug: skia:13657
> Change-Id: I71c629d815411c75452b51de6f086e11de62a105
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/580577
> Reviewed-by: John Stiles <johnstiles@google.com>
> Commit-Queue: Herb Derby <herb@google.com>
> Reviewed-by: Brian Salomon <bsalomon@google.com>

Bug: skia:13657
Change-Id: Ieeedceaf20e603b387089b20af426a15b5309a24
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/583242
Reviewed-by: John Stiles <johnstiles@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 2271a19..1074036 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1562,6 +1562,7 @@
     "src/core/SkStroke.cpp",
     "src/core/SkStrokeRec.cpp",
     "src/core/SkStrokerPriv.cpp",
+    "src/core/SkTDArray.cpp",
     "src/core/SkThreadID.cpp",
     "src/core/SkUtils.cpp",
     "src/effects/SkDashPathEffect.cpp",
diff --git a/gn/core.gni b/gn/core.gni
index 017f882..b42ff4e 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -422,6 +422,7 @@
   "$_src/core/SkSurfacePriv.h",
   "$_src/core/SkSwizzle.cpp",
   "$_src/core/SkTBlockList.h",
+  "$_src/core/SkTDArray.cpp",
   "$_src/core/SkTDPQueue.h",
   "$_src/core/SkTDynamicHash.h",
   "$_src/core/SkTInternalLList.h",
diff --git a/include/private/SkTDArray.h b/include/private/SkTDArray.h
index 997a240..d92e324 100644
--- a/include/private/SkTDArray.h
+++ b/include/private/SkTDArray.h
@@ -18,6 +18,35 @@
 #include <tuple>
 #include <utility>
 
+class SK_SPI SkTDStorage {
+public:
+    SkTDStorage() = default;
+    SkTDStorage(const SkTDStorage& that) = delete;
+    SkTDStorage& operator= (const SkTDStorage& that) = delete;
+    SkTDStorage(SkTDStorage&& that);
+    SkTDStorage& operator= (SkTDStorage&& that);
+    ~SkTDStorage();
+
+    int assign(const void* src, int count, size_t sizeOfT);
+
+    int resizeStorageToAtLeast(int count, size_t sizeOfT);
+    int shrinkToFit(int count, 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); }
+
+    struct StateUpdate {int count, reserve;};
+    StateUpdate append(
+            const void* src, int count, size_t sizeOfT, int reserve, int oldCount);
+private:
+    void* fStorage{nullptr};
+};
+
+template <typename T> static inline void swap(SkTDStorage& a, SkTDStorage& b) { a.swap(b); }
+
 // SkTDArray<T> implements a std::vector-like array for raw data-only objects that do not require
 // construction or destruction. The constructor and destructor for T will not be called; T objects
 // will always be moved via raw memcpy. Newly created T objects will contain uninitialized memory.
@@ -26,55 +55,52 @@
 // used with appropriate care. In new code, consider std::vector<T> instead.
 template <typename T> class SkTDArray {
 public:
-    SkTDArray() : fArray(nullptr), fReserve(0), fCount(0) {}
+    SkTDArray() = default;
     SkTDArray(const T src[], int count) {
         SkASSERT(src || count == 0);
-
-        fReserve = fCount = 0;
-        fArray = nullptr;
-        if (count) {
-            fArray = (T*)sk_malloc_throw(SkToSizeT(count) * sizeof(T));
-            memcpy(fArray, src, sizeof(T) * SkToSizeT(count));
-            fReserve = fCount = count;
-        }
+        fReserve = fStorage.assign(src, count, sizeof(T));
+        fCount = count;
     }
     SkTDArray(const std::initializer_list<T>& list) : SkTDArray(list.begin(), list.size()) {}
-    SkTDArray(const SkTDArray<T>& src) : fArray(nullptr), fReserve(0), fCount(0) {
-        SkTDArray<T> tmp(src.fArray, src.fCount);
-        this->swap(tmp);
+    SkTDArray(const SkTDArray<T>& src) {
+        fReserve = fStorage.assign(src.array(), src.fCount, sizeof(T));
+        fCount = src.fCount;
     }
-    SkTDArray(SkTDArray<T>&& src) : fArray(nullptr), fReserve(0), fCount(0) { this->swap(src); }
-    ~SkTDArray() { sk_free(fArray); }
-
     SkTDArray<T>& operator=(const SkTDArray<T>& src) {
         if (this != &src) {
             if (src.fCount > fReserve) {
-                SkTDArray<T> tmp(src.fArray, src.fCount);
-                this->swap(tmp);
+                fReserve = fStorage.assign(src.array(), src.fCount, sizeof(T));
             } else {
-                sk_careful_memcpy(fArray, src.fArray, sizeof(T) * SkToSizeT(src.fCount));
-                fCount = src.fCount;
+                sk_careful_memcpy(this->array(), src.array(), sizeof(T) * SkToSizeT(src.fCount));
             }
+            fCount = src.fCount;
         }
         return *this;
     }
+
+    SkTDArray(SkTDArray<T>&& src)
+        : fStorage{std::move(src.fStorage)}
+        , fReserve{src.fReserve}
+        , fCount{src.fCount} {}
+
     SkTDArray<T>& operator=(SkTDArray<T>&& src) {
         if (this != &src) {
-            this->swap(src);
-            src.reset();
+            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.fArray, b.fArray, SkToSizeT(a.fCount) * sizeof(T)));
+               (a.fCount == 0 || !memcmp(a.array(), b.array(), SkToSizeT(a.fCount) * 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(fArray, that.fArray);
+        swap(fStorage, that.fStorage);
         swap(fReserve, that.fReserve);
         swap(fCount, that.fCount);
     }
@@ -94,41 +120,36 @@
     // return the number of bytes in the array: count * sizeof(T)
     size_t bytes() const { return fCount * sizeof(T); }
 
-    T*       data() { return fArray; }
-    const T* data() const { return fArray; }
-    T*       begin() { return fArray; }
-    const T* begin() const { return fArray; }
-    T*       end() { return fArray ? fArray + fCount : nullptr; }
-    const T* end() const { return fArray ? fArray + fCount : nullptr; }
+    T*       data() { return this->array(); }
+    const T* data() const { return this->array(); }
+    T*       begin() { return this->array(); }
+    const T* begin() const { return this->array(); }
+    T*       end() { return this->array() ? this->array() + fCount : nullptr; }
+    const T* end() const { return this->array() ? this->array() + fCount : nullptr; }
 
     T& operator[](int index) {
         SkASSERT(index < fCount);
-        return fArray[index];
+        return this->array()[index];
     }
     const T& operator[](int index) const {
         SkASSERT(index < fCount);
-        return fArray[index];
+        return this->array()[index];
     }
 
     T& getAt(int index) { return (*this)[index]; }
 
     const T& back() const {
         SkASSERT(fCount > 0);
-        return fArray[fCount - 1];
+        return this->array()[fCount - 1];
     }
     T& back() {
         SkASSERT(fCount > 0);
-        return fArray[fCount - 1];
+        return this->array()[fCount - 1];
     }
 
     void reset() {
-        if (fArray) {
-            sk_free(fArray);
-            fArray = nullptr;
-            fReserve = fCount = 0;
-        } else {
-            SkASSERT(fReserve == 0 && fCount == 0);
-        }
+        this->~SkTDArray();
+        new (this) SkTDArray{};
     }
 
     void rewind() {
@@ -161,23 +182,17 @@
 
     T* prepend() {
         this->adjustCount(1);
-        memmove(fArray + 1, fArray, (fCount - 1) * sizeof(T));
-        return fArray;
+        memmove(this->array() + 1, this->array(), (fCount - 1) * sizeof(T));
+        return this->array();
     }
 
     T* append() { return this->append(1, nullptr); }
     T* append(int count, const T* src = nullptr) {
         int oldCount = fCount;
-        if (count) {
-            SkASSERT(src == nullptr || fArray == nullptr || src + count <= fArray ||
-                     fArray + oldCount <= src);
-
-            this->adjustCount(count);
-            if (src) {
-                memcpy(fArray + oldCount, src, sizeof(T) * count);
-            }
-        }
-        return fArray + oldCount;
+        auto [newCount, newReserve] = fStorage.append(src, count, sizeof(T), fReserve, fCount);
+        fCount = newCount;
+        fReserve = newReserve;
+        return this->array() + oldCount;
     }
 
     T* insert(int index) { return this->insert(index, 1, nullptr); }
@@ -186,7 +201,7 @@
         SkASSERT(index <= fCount);
         size_t oldCount = fCount;
         this->adjustCount(count);
-        T* dst = fArray + index;
+        T* dst = this->array() + index;
         memmove(dst + count, dst, sizeof(T) * (oldCount - index));
         if (src) {
             memcpy(dst, src, sizeof(T) * count);
@@ -197,7 +212,7 @@
     void remove(int index, int count = 1) {
         SkASSERT(index + count <= fCount);
         fCount = fCount - count;
-        memmove(fArray + index, fArray + index + count, sizeof(T) * (fCount - index));
+        memmove(this->array() + index, this->array() + index + count, sizeof(T) * (fCount - index));
     }
 
     void removeShuffle(int index) {
@@ -205,25 +220,25 @@
         int newCount = fCount - 1;
         fCount = newCount;
         if (index != newCount) {
-            memcpy(fArray + index, fArray + newCount, sizeof(T));
+            memcpy(this->array() + index, this->array() + newCount, sizeof(T));
         }
     }
 
     int find(const T& elem) const {
-        const T* iter = fArray;
-        const T* stop = fArray + fCount;
+        const T* iter = this->array();
+        const T* stop = this->array() + fCount;
 
         for (; iter < stop; iter++) {
             if (*iter == elem) {
-                return SkToInt(iter - fArray);
+                return SkToInt(iter - this->array());
             }
         }
         return -1;
     }
 
     int rfind(const T& elem) const {
-        const T* iter = fArray + fCount;
-        const T* stop = fArray;
+        const T* iter = this->array() + fCount;
+        const T* stop = this->array();
 
         while (iter > stop) {
             if (*--iter == elem) {
@@ -245,7 +260,7 @@
             return 0;
         }
         int count = std::min(max, fCount - index);
-        memcpy(dst, fArray + index, sizeof(T) * count);
+        memcpy(dst, this->array() + index, sizeof(T) * count);
         return count;
     }
 
@@ -267,8 +282,8 @@
     }
 
     void deleteAll() {
-        T* iter = fArray;
-        T* stop = fArray + fCount;
+        T* iter = this->array();
+        T* stop = this->array() + fCount;
         while (iter < stop) {
             delete *iter;
             iter += 1;
@@ -277,8 +292,8 @@
     }
 
     void freeAll() {
-        T* iter = fArray;
-        T* stop = fArray + fCount;
+        T* iter = this->array();
+        T* stop = this->array() + fCount;
         while (iter < stop) {
             sk_free(*iter);
             iter += 1;
@@ -287,8 +302,8 @@
     }
 
     void unrefAll() {
-        T* iter = fArray;
-        T* stop = fArray + fCount;
+        T* iter = this->array();
+        T* stop = this->array() + fCount;
         while (iter < stop) {
             (*iter)->unref();
             iter += 1;
@@ -297,8 +312,8 @@
     }
 
     void safeUnrefAll() {
-        T* iter = fArray;
-        T* stop = fArray + fCount;
+        T* iter = this->array();
+        T* stop = this->array() + fCount;
         while (iter < stop) {
             SkSafeUnref(*iter);
             iter += 1;
@@ -308,20 +323,22 @@
 
 #ifdef SK_DEBUG
     void validate() const {
-        SkASSERT((fReserve == 0 && fArray == nullptr) || (fReserve > 0 && fArray != nullptr));
+        SkASSERT((fReserve == 0 && this->array() == nullptr) ||
+                 (fReserve > 0 && this->array() != nullptr));
         SkASSERT(fCount <= fReserve);
     }
 #endif
 
     void shrinkToFit() {
-        if (fReserve != fCount) {
-            SkASSERT(fReserve > fCount);
-            fReserve = fCount;
-            fArray = (T*)sk_realloc_throw(fArray, fReserve * sizeof(T));
+        if (fReserve > fCount) {
+            fReserve = fStorage.shrinkToFit(fCount, sizeof(T));
         }
     }
 
 private:
+    T* array() { return fStorage.data<T>(); }
+    const T* array() const { return fStorage.data<T>(); }
+
     // Adjusts the number of elements in the array.
     // This is the same as calling setCount(count() + delta).
     void adjustCount(int delta) {
@@ -335,28 +352,6 @@
         this->setCount(SkTo<int>(count));
     }
 
-    static std::tuple<void*, int> ResizeStorageToAtLeast(void* array, int count, size_t TSize) {
-        // 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) {
-            // 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);
-            // Read this line as: if (count + growth < kMaxCount) { ... }
-            // It's rewritten to avoid signed integer overflow.
-            if (kMaxCount - count > growth) {
-                newReserve = count + growth;
-            }
-        }
-
-        void* newArray = sk_realloc_throw(array, SkToSizeT(newReserve) * TSize);
-        return {newArray, newReserve};
-    }
-
     // Increase the storage allocation such that it can hold (fCount + extra)
     // elements.
     // It never shrinks the allocation, and it may increase the allocation by
@@ -365,15 +360,12 @@
     //  note: this does NOT modify fCount
     void resizeStorageToAtLeast(int count) {
         SkASSERT(count > fReserve);
-
-        auto [array, reserve] = ResizeStorageToAtLeast(fArray, count, sizeof(T));
-        fArray = static_cast<T*>(array);
-        fReserve = reserve;
+        fReserve = fStorage.resizeStorageToAtLeast(count, sizeof(T));
     }
 
-    T*  fArray;
-    int fReserve;  // size of the allocation in fArray (#elements)
-    int fCount;    // logical number of elements (fCount <= fReserve)
+    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/public.bzl b/public.bzl
index 83803c2..ef586c4 100644
--- a/public.bzl
+++ b/public.bzl
@@ -639,6 +639,7 @@
     "src/core/SkSurfacePriv.h",
     "src/core/SkSwizzle.cpp",
     "src/core/SkTBlockList.h",
+    "src/core/SkTDArray.cpp",
     "src/core/SkTDPQueue.h",
     "src/core/SkTDynamicHash.h",
     "src/core/SkTInternalLList.h",
diff --git a/src/core/BUILD.bazel b/src/core/BUILD.bazel
index d22716b..97b8efb 100644
--- a/src/core/BUILD.bazel
+++ b/src/core/BUILD.bazel
@@ -329,6 +329,7 @@
     "SkSurfacePriv.h",
     "SkSwizzle.cpp",
     "SkTBlockList.h",
+    "SkTDArray.cpp",
     "SkTDPQueue.h",
     "SkTDynamicHash.h",
     "SkTInternalLList.h",
diff --git a/src/core/SkTDArray.cpp b/src/core/SkTDArray.cpp
new file mode 100644
index 0000000..aeac67a
--- /dev/null
+++ b/src/core/SkTDArray.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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/SkTDArray.h"
+#include "include/private/SkTo.h"
+
+SkTDStorage::SkTDStorage(SkTDStorage&& that)
+        : fStorage{std::move(that.fStorage)} { that.fStorage = nullptr; }
+
+SkTDStorage& SkTDStorage::operator=(SkTDStorage&& that) {
+    if (this != &that) {
+        this->~SkTDStorage();
+        new (this) SkTDStorage{std::move(that)};
+    }
+    return *this;
+}
+
+SkTDStorage::~SkTDStorage() {
+    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;
+}
+
+int SkTDStorage::resizeStorageToAtLeast(int count, size_t sizeOfT) {
+    SkASSERT(count > 0);
+    // 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) {
+        // 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);
+        // Read this line as: if (count + growth < kMaxCount) { ... }
+        // It's rewritten to avoid signed integer overflow.
+        if (kMaxCount - count > growth) {
+            newReserve = count + growth;
+        }
+    }
+
+    fStorage = sk_realloc_throw(fStorage, SkToSizeT(newReserve) * sizeOfT);
+    return newReserve;
+}
+
+int SkTDStorage::shrinkToFit(int count, size_t sizeOfT) {
+    fStorage = sk_realloc_throw(fStorage, SkToSizeT(count) * sizeOfT);
+    return count;
+}
+
+SkTDStorage::StateUpdate SkTDStorage::append(
+        const void* src, int count, size_t sizeOfT, int reserve, int oldCount) {
+    SkASSERT(count >= 0);
+    int newCount = oldCount;
+    int newReserve = reserve;
+    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);
+        }
+        if (src != nullptr) {
+            memcpy(this->data<char>() + sizeOfT *SkToSizeT(oldCount), src,
+                   sizeOfT *SkToSizeT(count));
+        }
+    }
+    return {newCount, newReserve};
+}