Introduce FinalRefCountedObject template class

To add ref counting to any class while avoiding
virtual functions for reference counting.
This template can both slightly reduce binary size
and slightly improve performance.

Bug: webrtc:11308
Change-Id: I90ac735f6c220ee2a1a991a71039acdb0ca86453
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/198845
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33058}
diff --git a/rtc_base/copy_on_write_buffer.cc b/rtc_base/copy_on_write_buffer.cc
index 73182a1..f3cc710 100644
--- a/rtc_base/copy_on_write_buffer.cc
+++ b/rtc_base/copy_on_write_buffer.cc
@@ -32,16 +32,15 @@
     : CopyOnWriteBuffer(s.data(), s.length()) {}
 
 CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size)
-    : buffer_(size > 0 ? new RefCountedObject<Buffer>(size) : nullptr),
+    : buffer_(size > 0 ? new RefCountedBuffer(size) : nullptr),
       offset_(0),
       size_(size) {
   RTC_DCHECK(IsConsistent());
 }
 
 CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size, size_t capacity)
-    : buffer_(size > 0 || capacity > 0
-                  ? new RefCountedObject<Buffer>(size, capacity)
-                  : nullptr),
+    : buffer_(size > 0 || capacity > 0 ? new RefCountedBuffer(size, capacity)
+                                       : nullptr),
       offset_(0),
       size_(size) {
   RTC_DCHECK(IsConsistent());
@@ -61,7 +60,7 @@
   RTC_DCHECK(IsConsistent());
   if (!buffer_) {
     if (size > 0) {
-      buffer_ = new RefCountedObject<Buffer>(size);
+      buffer_ = new RefCountedBuffer(size);
       offset_ = 0;
       size_ = size;
     }
@@ -84,7 +83,7 @@
   RTC_DCHECK(IsConsistent());
   if (!buffer_) {
     if (new_capacity > 0) {
-      buffer_ = new RefCountedObject<Buffer>(0, new_capacity);
+      buffer_ = new RefCountedBuffer(0, new_capacity);
       offset_ = 0;
       size_ = 0;
     }
@@ -105,7 +104,7 @@
   if (buffer_->HasOneRef()) {
     buffer_->Clear();
   } else {
-    buffer_ = new RefCountedObject<Buffer>(0, capacity());
+    buffer_ = new RefCountedBuffer(0, capacity());
   }
   offset_ = 0;
   size_ = 0;
@@ -117,8 +116,8 @@
     return;
   }
 
-  buffer_ = new RefCountedObject<Buffer>(buffer_->data() + offset_, size_,
-                                         new_capacity);
+  buffer_ =
+      new RefCountedBuffer(buffer_->data() + offset_, size_, new_capacity);
   offset_ = 0;
   RTC_DCHECK(IsConsistent());
 }
diff --git a/rtc_base/copy_on_write_buffer.h b/rtc_base/copy_on_write_buffer.h
index 9ec72ee..526cbe5 100644
--- a/rtc_base/copy_on_write_buffer.h
+++ b/rtc_base/copy_on_write_buffer.h
@@ -159,9 +159,9 @@
   void SetData(const T* data, size_t size) {
     RTC_DCHECK(IsConsistent());
     if (!buffer_) {
-      buffer_ = size > 0 ? new RefCountedObject<Buffer>(data, size) : nullptr;
+      buffer_ = size > 0 ? new RefCountedBuffer(data, size) : nullptr;
     } else if (!buffer_->HasOneRef()) {
-      buffer_ = new RefCountedObject<Buffer>(data, size, capacity());
+      buffer_ = new RefCountedBuffer(data, size, capacity());
     } else {
       buffer_->SetData(data, size);
     }
@@ -196,7 +196,7 @@
   void AppendData(const T* data, size_t size) {
     RTC_DCHECK(IsConsistent());
     if (!buffer_) {
-      buffer_ = new RefCountedObject<Buffer>(data, size);
+      buffer_ = new RefCountedBuffer(data, size);
       offset_ = 0;
       size_ = size;
       RTC_DCHECK(IsConsistent());
@@ -242,7 +242,7 @@
 
   // Swaps two buffers.
   friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) {
-    std::swap(a.buffer_, b.buffer_);
+    a.buffer_.swap(b.buffer_);
     std::swap(a.offset_, b.offset_);
     std::swap(a.size_, b.size_);
   }
@@ -257,6 +257,7 @@
   }
 
  private:
+  using RefCountedBuffer = FinalRefCountedObject<Buffer>;
   // Create a copy of the underlying data if it is referenced from other Buffer
   // objects or there is not enough capacity.
   void UnshareAndEnsureCapacity(size_t new_capacity);
@@ -272,7 +273,7 @@
   }
 
   // buffer_ is either null, or points to an rtc::Buffer with capacity > 0.
-  scoped_refptr<RefCountedObject<Buffer>> buffer_;
+  scoped_refptr<RefCountedBuffer> buffer_;
   // This buffer may represent a slice of a original data.
   size_t offset_;  // Offset of a current slice in the original data in buffer_.
                    // Should be 0 if the buffer_ is empty.
diff --git a/rtc_base/ref_counted_object.h b/rtc_base/ref_counted_object.h
index ce18379..e86a1fb 100644
--- a/rtc_base/ref_counted_object.h
+++ b/rtc_base/ref_counted_object.h
@@ -59,6 +59,37 @@
   RTC_DISALLOW_COPY_AND_ASSIGN(RefCountedObject);
 };
 
+template <class T>
+class FinalRefCountedObject final : public T {
+ public:
+  using T::T;
+  // Until c++17 compilers are allowed not to inherit the default constructor,
+  // and msvc doesn't. Thus the default constructor is forwarded explicitly.
+  FinalRefCountedObject() = default;
+  FinalRefCountedObject(const FinalRefCountedObject&) = delete;
+  FinalRefCountedObject& operator=(const FinalRefCountedObject&) = delete;
+
+  void AddRef() const { ref_count_.IncRef(); }
+  void Release() const {
+    if (ref_count_.DecRef() == RefCountReleaseStatus::kDroppedLastRef) {
+      delete this;
+    }
+  }
+  bool HasOneRef() const { return ref_count_.HasOneRef(); }
+
+ private:
+  ~FinalRefCountedObject() = default;
+
+  // gcc v7.1 requires default contructors for members of
+  // `FinalRefCountedObject` to be able to use inherited constructors.
+  // TODO(danilchap): Replace with simpler braced initialization when
+  // bot support for that version of gcc is dropped.
+  class ZeroBasedRefCounter : public webrtc::webrtc_impl::RefCounter {
+   public:
+    ZeroBasedRefCounter() : RefCounter(0) {}
+  } mutable ref_count_;
+};
+
 }  // namespace rtc
 
 #endif  // RTC_BASE_REF_COUNTED_OBJECT_H_
diff --git a/rtc_base/ref_counted_object_unittest.cc b/rtc_base/ref_counted_object_unittest.cc
index eacf731..05380b7 100644
--- a/rtc_base/ref_counted_object_unittest.cc
+++ b/rtc_base/ref_counted_object_unittest.cc
@@ -12,6 +12,7 @@
 
 #include <memory>
 #include <string>
+#include <type_traits>
 #include <utility>
 
 #include "api/scoped_refptr.h"
@@ -95,4 +96,19 @@
   EXPECT_EQ(c, ref->c_);
 }
 
+TEST(FinalRefCountedObject, CanWrapIntoScopedRefptr) {
+  using WrappedTyped = FinalRefCountedObject<A>;
+  static_assert(!std::is_polymorphic<WrappedTyped>::value, "");
+  scoped_refptr<WrappedTyped> ref(new WrappedTyped());
+  EXPECT_TRUE(ref.get());
+  EXPECT_TRUE(ref->HasOneRef());
+  // Test reference counter is updated on some simple operations.
+  scoped_refptr<WrappedTyped> ref2 = ref;
+  EXPECT_FALSE(ref->HasOneRef());
+  EXPECT_FALSE(ref2->HasOneRef());
+
+  ref = nullptr;
+  EXPECT_TRUE(ref2->HasOneRef());
+}
+
 }  // namespace rtc