isolate sk_sp from larger cl

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1752683002

Review URL: https://codereview.chromium.org/1752683002
diff --git a/include/core/SkRefCnt.h b/include/core/SkRefCnt.h
index 9d1e5f1..43251d0 100644
--- a/include/core/SkRefCnt.h
+++ b/include/core/SkRefCnt.h
@@ -227,4 +227,103 @@
     mutable int32_t fRefCnt;
 };
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  Shared pointer class to wrap classes that support a ref()/unref() interface.
+ *
+ *  This can be used for classes inheriting from SkRefCnt, but it also works for other
+ *  classes that match the interface, but have different internal choices: e.g. the hosted class
+ *  may have its ref/unref be thread-safe, but that is not assumed/imposed by sk_sp.
+ */
+template <typename T> class sk_sp {
+public:
+    sk_sp() : fPtr(nullptr) {}
+    sk_sp(std::nullptr_t) : fPtr(nullptr) {}
+
+    /**
+     *  Shares the underlying object by calling ref(), so that both the argument and the newly
+     *  created sk_sp both have a reference to it.
+     */
+    sk_sp(const sk_sp<T>& that) : fPtr(SkSafeRef(that.get())) {}
+
+    /**
+     *  Move the underlying object from the argument to the newly created sk_sp. Afterwards only
+     *  the new sk_sp will have a reference to the object, and the argument will point to null.
+     *  No call to ref() or unref() will be made.
+     */
+    sk_sp(sk_sp<T>&& that) : fPtr(that.release()) {}
+
+    /**
+     *  Adopt the bare pointer into the newly created sk_sp.
+     *  No call to ref() or unref() will be made.
+     */
+    explicit sk_sp(T* obj) : fPtr(obj) {}
+
+    /**
+     *  Calls unref() on the underlying object pointer.
+     */
+    ~sk_sp() {
+        SkSafeUnref(fPtr);
+    }
+
+    sk_sp<T>& operator=(std::nullptr_t) { this->reset(); }
+
+    /**
+     *  Shares the underlying object referenced by the argument by calling ref() on it. If this
+     *  sk_sp previously had a reference to an object (i.e. not null) it will call unref() on that
+     *  object.
+     */
+    sk_sp<T>& operator=(const sk_sp<T>& that) {
+        this->reset(SkSafeRef(that.get()));
+        return *this;
+    }
+
+    /**
+     *  Move the underlying object from the argument to the sk_sp. If the sk_sp previously held
+     *  a reference to another object, unref() will be called on that object. No call to ref()
+     *  will be made.
+     */
+    sk_sp<T>& operator=(sk_sp<T>&& that) {
+        this->reset(that.release());
+        return *this;
+    }
+
+    bool operator==(std::nullptr_t) const { return this->get() == nullptr; }
+    bool operator!=(std::nullptr_t) const { return this->get() != nullptr; }
+
+    bool operator==(const sk_sp<T>& that) const { return this->get() == that.get(); }
+    bool operator!=(const sk_sp<T>& that) const { return this->get() != that.get(); }
+
+    explicit operator bool() const { return this->get() != nullptr; }
+
+    T* get() const { return fPtr; }
+    T* operator->() const { return fPtr; }
+
+    /**
+     *  Adopt the new bare pointer, and call unref() on any previously held object (if not null).
+     *  No call to ref() will be made.
+     */
+    void reset(T* ptr = nullptr) {
+        if (fPtr != ptr) {
+            SkSafeUnref(fPtr);
+            fPtr = ptr;
+        }
+    }
+
+    /**
+     *  Return the bare pointer, and set the internal object pointer to nullptr.
+     *  The caller must assume ownership of the object, and manage its reference count directly.
+     *  No call to unref() will be made.
+     */
+    T* SK_WARN_UNUSED_RESULT release() {
+        T* ptr = fPtr;
+        fPtr = nullptr;
+        return ptr;
+    }
+
+private:
+    T*  fPtr;
+};
+
 #endif
diff --git a/tests/RefCntTest.cpp b/tests/RefCntTest.cpp
index 6a1e0dc..e3f41bd 100644
--- a/tests/RefCntTest.cpp
+++ b/tests/RefCntTest.cpp
@@ -79,3 +79,93 @@
     test_refCnt(reporter);
     test_weakRefCnt(reporter);
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static int gRefCounter;
+static int gUnrefCounter;
+static int gNewCounter;
+static int gDeleteCounter;
+
+#define check(reporter, ref, unref, make, kill)             \
+    REPORTER_ASSERT(reporter, gRefCounter == ref);          \
+    REPORTER_ASSERT(reporter, gUnrefCounter == unref);      \
+    REPORTER_ASSERT(reporter, gNewCounter == make);         \
+    REPORTER_ASSERT(reporter, gDeleteCounter == kill);
+
+
+class Effect {
+public:
+    Effect() : fRefCnt(1) {
+        gNewCounter += 1;
+    }
+
+    int fRefCnt;
+
+    void ref() {
+        gRefCounter += 1;
+        fRefCnt += 1;
+    }
+    void unref() {
+        gUnrefCounter += 1;
+
+        SkASSERT(fRefCnt > 0);
+        if (0 == --fRefCnt) {
+            gDeleteCounter += 1;
+            delete this;
+        }
+    }
+
+    int* method() const { return new int; }
+};
+
+static sk_sp<Effect> Create() {
+    return sk_sp<Effect>(new Effect);
+}
+
+class Paint {
+public:
+    sk_sp<Effect> fEffect;
+
+    const sk_sp<Effect>& get() const { return fEffect; }
+
+    void set(sk_sp<Effect> value) {
+        fEffect = std::move(value);
+    }
+};
+
+DEF_TEST(sk_sp, reporter) {
+    gRefCounter = 0;
+    gUnrefCounter = 0;
+    gNewCounter = 0;
+    gDeleteCounter = 0;
+
+    Paint paint;
+    REPORTER_ASSERT(reporter, paint.fEffect.get() == nullptr);
+    REPORTER_ASSERT(reporter, !paint.get());
+    check(reporter, 0, 0, 0, 0);
+
+    paint.set(Create());
+    check(reporter, 0, 0, 1, 0);
+    REPORTER_ASSERT(reporter, paint.fEffect.get()->fRefCnt == 1);
+
+    paint.set(nullptr);
+    check(reporter, 0, 1, 1, 1);
+
+    auto e = Create();
+    REPORTER_ASSERT(reporter, sizeof(e) == sizeof(void*));
+
+    check(reporter, 0, 1, 2, 1);
+    paint.set(e);
+    check(reporter, 1, 1, 2, 1);
+    REPORTER_ASSERT(reporter, paint.fEffect.get()->fRefCnt == 2);
+
+    Paint paint2;
+    paint2.set(paint.get());
+    check(reporter, 2, 1, 2, 1);
+    REPORTER_ASSERT(reporter, paint.fEffect.get()->fRefCnt == 3);
+
+    delete paint.get()->method();
+    check(reporter, 2, 1, 2, 1);
+}
+