Bug 1512155 - Introduce a movable type that combines mozilla::UniquePtr<T[]> and its length. r=froydnj
authorHenri Sivonen <hsivonen@hsivonen.fi>
Fri, 07 Dec 2018 08:28:08 +0000
changeset 508800 5344a9d4ec9ca4a27fce29328183d1dc06ebd441
parent 508799 d8e07613f15e55f43d654af8708ffa0cba1710d1
child 508801 ce34faf0d1ef5d62b9d1021c1bcf7e66cc3b8620
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1512155
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1512155 - Introduce a movable type that combines mozilla::UniquePtr<T[]> and its length. r=froydnj Differential Revision: https://phabricator.services.mozilla.com/D13795
mfbt/Buffer.h
mfbt/moz.build
mfbt/tests/gtest/TestBuffer.cpp
mfbt/tests/gtest/moz.build
parser/html/nsHtml5StreamParser.cpp
parser/html/nsHtml5StreamParser.h
new file mode 100644
--- /dev/null
+++ b/mfbt/Buffer.h
@@ -0,0 +1,167 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_Buffer_h
+#define mozilla_Buffer_h
+
+#include <algorithm>
+#include "mozilla/Maybe.h"
+#include "mozilla/Span.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
+
+namespace mozilla {
+
+/**
+ * A move-only type that wraps a mozilla::UniquePtr<T[]> and the length of
+ * the T[].
+ *
+ * Unlike mozilla::Array, the length is a run-time property.
+ * Unlike mozilla::Vector and nsTArray, does not have capacity and
+ * assocatiated growth functionality.
+ * Unlike mozilla::Span, mozilla::Buffer owns the allocation it points to.
+ */
+template <typename T>
+class Buffer final {
+ private:
+  mozilla::UniquePtr<T[]> mData;
+  size_t mLength;
+
+ public:
+  Buffer(const Buffer<T>& aOther) = delete;
+  Buffer<T>& operator=(const Buffer<T>& aOther) = delete;
+
+  /**
+   * Construct zero-lenth Buffer (without actually pointing to a heap
+   * allocation).
+   */
+  Buffer() : mData(nullptr), mLength(0){};
+
+  /**
+   * Construct from raw parts.
+   *
+   * aLength must not be greater than the actual length of the buffer pointed
+   * to by aData.
+   */
+  Buffer(mozilla::UniquePtr<T[]>&& aData, size_t aLength)
+      : mData(std::move(aData)), mLength(aLength) {}
+
+  /**
+   * Move constructor. Sets the moved-from Buffer to zero-length
+   * state.
+   */
+  Buffer(Buffer<T>&& aOther)
+      : mData(std::move(aOther.mData)), mLength(aOther.mLength) {
+    aOther.mLength = 0;
+  }
+
+  /**
+   * Construct by copying the elements of a Span.
+   *
+   * Allocates the internal buffer infallibly. Use CopyFrom for fallible
+   * allocation.
+   */
+  explicit Buffer(mozilla::Span<const T> aSpan)
+      : mData(mozilla::MakeUnique<T[]>(aSpan.Length())),
+        mLength(aSpan.Length()) {
+    std::copy(aSpan.cbegin(), aSpan.cend(), mData.get());
+  }
+
+  /**
+   * Create a new Buffer by copying the elements of a Span.
+   *
+   * Allocates the internal buffer fallibly.
+   */
+  static mozilla::Maybe<Buffer<T>> CopyFrom(mozilla::Span<const T> aSpan) {
+    auto data = mozilla::MakeUniqueFallible<T[]>(aSpan.Length());
+    if (!data) {
+      return mozilla::Nothing();
+    }
+    std::copy(aSpan.cbegin(), aSpan.cend(), data.get());
+    return mozilla::Some(Buffer(std::move(data), aSpan.Length()));
+  }
+
+  /**
+   * Construct a buffer of requested length.
+   *
+   * The contents will be initialized or uninitialized according
+   * to the behavior of mozilla::MakeUnique<T[]>(aLength) for T.
+   *
+   * Allocates the internal buffer infallibly. Use Alloc for fallible
+   * allocation.
+   */
+  explicit Buffer(size_t aLength)
+      : mData(mozilla::MakeUnique<T[]>(aLength)), mLength(aLength) {}
+
+  /**
+   * Create a new Buffer with an internal buffer of requested length.
+   *
+   * The contents will be initialized or uninitialized according to the
+   * behavior of mozilla::MakeUnique<T[]>(aLength) for T.
+   *
+   * Allocates the internal buffer fallibly.
+   */
+  static mozilla::Maybe<Buffer<T>> Alloc(size_t aLength) {
+    auto data = mozilla::MakeUniqueFallible<T[]>(aLength);
+    if (!data) {
+      return mozilla::Nothing();
+    }
+    return mozilla::Some(Buffer(std::move(data), aLength));
+  }
+
+  mozilla::Span<const T> AsSpan() const {
+    return mozilla::MakeSpan(mData.get(), mLength);
+  }
+  mozilla::Span<T> AsWritableSpan() {
+    return mozilla::MakeSpan(mData.get(), mLength);
+  }
+  operator mozilla::Span<const T>() const { return AsSpan(); }
+  operator mozilla::Span<T>() { return AsWritableSpan(); }
+
+  /**
+   * Guarantees a non-null and aligned pointer
+   * even for the zero-length case.
+   */
+  T* Elements() { return AsWritableSpan().Elements(); }
+  size_t Length() const { return mLength; }
+
+  T& operator[](size_t aIndex) {
+    MOZ_ASSERT(aIndex < mLength);
+    return mData.get()[aIndex];
+  }
+
+  const T& operator[](size_t aIndex) const {
+    MOZ_ASSERT(aIndex < mLength);
+    return mData.get()[aIndex];
+  }
+
+  typedef T* iterator;
+  typedef const T* const_iterator;
+  typedef ReverseIterator<T*> reverse_iterator;
+  typedef ReverseIterator<const T*> const_reverse_iterator;
+
+  // Methods for range-based for loops.
+  iterator begin() { return mData.get(); }
+  const_iterator begin() const { return mData.get(); }
+  const_iterator cbegin() const { return begin(); }
+  iterator end() { return mData.get() + mLength; }
+  const_iterator end() const { return mData.get() + mLength; }
+  const_iterator cend() const { return end(); }
+
+  // Methods for reverse iterating.
+  reverse_iterator rbegin() { return reverse_iterator(end()); }
+  const_reverse_iterator rbegin() const {
+    return const_reverse_iterator(end());
+  }
+  const_reverse_iterator crbegin() const { return rbegin(); }
+  reverse_iterator rend() { return reverse_iterator(begin()); }
+  const_reverse_iterator rend() const {
+    return const_reverse_iterator(begin());
+  }
+  const_reverse_iterator crend() const { return rend(); }
+};
+
+} /* namespace mozilla */
+
+#endif /* mozilla_Buffer_h */
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -18,16 +18,17 @@ EXPORTS.mozilla = [
     'AlreadyAddRefed.h',
     'Array.h',
     'ArrayUtils.h',
     'Assertions.h',
     'Atomics.h',
     'Attributes.h',
     'BinarySearch.h',
     'BloomFilter.h',
+    'Buffer.h',
     'BufferList.h',
     'Casting.h',
     'ChaosMode.h',
     'Char16.h',
     'CheckedInt.h',
     'Compiler.h',
     'Compression.h',
     'DebugOnly.h',
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/gtest/TestBuffer.cpp
@@ -0,0 +1,95 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "mozilla/Buffer.h"
+#include "mozilla/Array.h"
+
+using namespace mozilla;
+
+TEST(Buffer, TestBufferInfallible)
+{
+	const size_t LEN = 8;
+	Array<int32_t, LEN> arr = {1, 2, 3, 4, 5, 6, 7, 8};
+	Buffer<int32_t> buf(arr);
+
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(buf[i], arr[i]);
+	}
+
+	auto iter = buf.begin();
+	auto end = buf.end();
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(*iter, arr[i]);
+		iter++;
+	}
+	ASSERT_EQ(iter, end);
+
+	Span<int32_t> span = buf;
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(span[i], arr[i]);
+	}
+
+	auto spanIter = span.begin();
+	auto spanEnd = span.end();
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(*spanIter, arr[i]);
+		spanIter++;
+	}
+	ASSERT_EQ(spanIter, spanEnd);
+
+	span[3] = 42;
+	ASSERT_EQ(buf[3], 42);
+
+	Buffer<int32_t> another(std::move(buf));
+	ASSERT_EQ(another[3], 42);
+	ASSERT_EQ(buf.Length(), 0U);
+}
+
+TEST(Buffer, TestBufferFallible)
+{
+	const size_t LEN = 8;
+	Array<int32_t, LEN> arr = {1, 2, 3, 4, 5, 6, 7, 8};
+	auto maybe = Buffer<int32_t>::CopyFrom(arr);
+	ASSERT_TRUE(maybe.isSome());
+	Buffer<int32_t> buf(std::move(*maybe));
+
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(buf[i], arr[i]);
+	}
+
+	auto iter = buf.begin();
+	auto end = buf.end();
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(*iter, arr[i]);
+		iter++;
+	}
+	ASSERT_EQ(iter, end);
+
+	Span<int32_t> span = buf;
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(span[i], arr[i]);
+	}
+
+	auto spanIter = span.begin();
+	auto spanEnd = span.end();
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(*spanIter, arr[i]);
+		spanIter++;
+	}
+	ASSERT_EQ(spanIter, spanEnd);
+
+	span[3] = 42;
+	ASSERT_EQ(buf[3], 42);
+
+	Buffer<int32_t> another(std::move(buf));
+	ASSERT_EQ(another[3], 42);
+	ASSERT_EQ(buf.Length(), 0U);
+}
+
+TEST(Buffer, TestBufferElements)
+{
+	ASSERT_EQ(Buffer<int32_t>().Elements(), reinterpret_cast<int32_t*>(alignof(int32_t)));
+}
--- a/mfbt/tests/gtest/moz.build
+++ b/mfbt/tests/gtest/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
+    'TestBuffer.cpp',
     'TestLinkedList.cpp',
     'TestSpan.cpp',
 ]
 
 #LOCAL_INCLUDES += [
 #    '../../base',
 #]
 
--- a/parser/html/nsHtml5StreamParser.cpp
+++ b/parser/html/nsHtml5StreamParser.cpp
@@ -1130,29 +1130,27 @@ void nsHtml5StreamParser::DoDataAvailabl
         nsITimer::TYPE_ONE_SHOT, "nsHtml5StreamParser::DoDataAvailable");
   }
   mFlushTimerArmed = true;
 }
 
 class nsHtml5DataAvailable : public Runnable {
  private:
   nsHtml5StreamParserPtr mStreamParser;
-  UniquePtr<uint8_t[]> mData;
-  uint32_t mLength;
+  Buffer<uint8_t> mData;
 
  public:
   nsHtml5DataAvailable(nsHtml5StreamParser* aStreamParser,
-                       UniquePtr<uint8_t[]> aData, uint32_t aLength)
+                       Buffer<uint8_t>&& aData)
       : Runnable("nsHtml5DataAvailable"),
         mStreamParser(aStreamParser),
-        mData(std::move(aData)),
-        mLength(aLength) {}
+        mData(std::move(aData)) {}
   NS_IMETHOD Run() override {
     mozilla::MutexAutoLock autoLock(mStreamParser->mTokenizerMutex);
-    mStreamParser->DoDataAvailable(MakeSpan(mData.get(), mLength));
+    mStreamParser->DoDataAvailable(mData);
     return NS_OK;
   }
 };
 
 nsresult nsHtml5StreamParser::OnDataAvailable(nsIRequest* aRequest,
                                               nsISupports* aContext,
                                               nsIInputStream* aInStream,
                                               uint64_t aSourceOffset,
@@ -1161,27 +1159,28 @@ nsresult nsHtml5StreamParser::OnDataAvai
   if (NS_FAILED(rv = mExecutor->IsBroken())) {
     return rv;
   }
 
   NS_ASSERTION(mRequest == aRequest, "Got data on wrong stream.");
   uint32_t totalRead;
   // Main thread to parser thread dispatch requires copying to buffer first.
   if (NS_IsMainThread()) {
-    auto data = MakeUniqueFallible<uint8_t[]>(aLength);
-    if (!data) {
+    Maybe<Buffer<uint8_t>> maybe = Buffer<uint8_t>::Alloc(aLength);
+    if (maybe.isNothing()) {
       return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
     }
-    rv = aInStream->Read(reinterpret_cast<char*>(data.get()), aLength,
-                         &totalRead);
+    Buffer<uint8_t> data(std::move(*maybe));
+    rv = aInStream->Read(reinterpret_cast<char*>(data.Elements()),
+                         data.Length(), &totalRead);
     NS_ENSURE_SUCCESS(rv, rv);
-    NS_ASSERTION(totalRead <= aLength, "Read more bytes than were available?");
+    MOZ_ASSERT(totalRead == aLength);
 
     nsCOMPtr<nsIRunnable> dataAvailable =
-        new nsHtml5DataAvailable(this, std::move(data), totalRead);
+        new nsHtml5DataAvailable(this, std::move(data));
     if (NS_FAILED(mEventTarget->Dispatch(dataAvailable,
                                          nsIThread::DISPATCH_NORMAL))) {
       NS_WARNING("Dispatching DataAvailable event failed.");
     }
     return rv;
   } else {
     NS_ASSERTION(IsParserThread(), "Wrong thread!");
     mozilla::MutexAutoLock autoLock(mTokenizerMutex);
--- a/parser/html/nsHtml5StreamParser.h
+++ b/parser/html/nsHtml5StreamParser.h
@@ -17,16 +17,17 @@
 #include "mozilla/Mutex.h"
 #include "mozilla/UniquePtr.h"
 #include "nsHtml5AtomTable.h"
 #include "nsHtml5Speculation.h"
 #include "nsISerialEventTarget.h"
 #include "nsITimer.h"
 #include "nsICharsetDetector.h"
 #include "mozilla/dom/DocGroup.h"
+#include "mozilla/Buffer.h"
 
 class nsHtml5Parser;
 
 #define NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE 1024
 #define NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE 1024
 
 enum eParserMode {
   /**