Add a FixedZeroedDataVector container.

This is a simple container to hold data of a fixed size. Unlike
FixedUninitDataVector, it does initialize the underlying memory. Since
it always initializes the memory to all zeroes, it does so more
efficiently than std::vector in debug builds. Its public methods are
very limited and generally requires callers to get to the underlying
data via spans.

Change-Id: I4a5e4238ce4833563e157b203df7243379d9121d
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/98171
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn
index d926496..bf969c0 100644
--- a/core/fxcrt/BUILD.gn
+++ b/core/fxcrt/BUILD.gn
@@ -40,6 +40,7 @@
     "data_vector.h",
     "fileaccess_iface.h",
     "fixed_uninit_data_vector.h",
+    "fixed_zeroed_data_vector.h",
     "fx_bidi.cpp",
     "fx_bidi.h",
     "fx_codepage.cpp",
@@ -171,6 +172,7 @@
     "cfx_seekablestreamproxy_unittest.cpp",
     "cfx_timer_unittest.cpp",
     "fixed_uninit_data_vector_unittest.cpp",
+    "fixed_zeroed_data_vector_unittest.cpp",
     "fx_bidi_unittest.cpp",
     "fx_coordinates_unittest.cpp",
     "fx_extension_unittest.cpp",
diff --git a/core/fxcrt/fixed_zeroed_data_vector.h b/core/fxcrt/fixed_zeroed_data_vector.h
new file mode 100644
index 0000000..71170df
--- /dev/null
+++ b/core/fxcrt/fixed_zeroed_data_vector.h
@@ -0,0 +1,64 @@
+// Copyright 2022 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_FIXED_ZEROED_DATA_VECTOR_H_
+#define CORE_FXCRT_FIXED_ZEROED_DATA_VECTOR_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "core/fxcrt/fx_memory_wrappers.h"
+#include "third_party/base/span.h"
+
+namespace fxcrt {
+
+// A simple data container that has a fixed size.
+// Unlike std::vector, it cannot be implicitly copied.
+// Access its data using spans.
+template <typename T>
+class FixedZeroedDataVector {
+ public:
+  FixedZeroedDataVector() : FixedZeroedDataVector(0) {}
+  explicit FixedZeroedDataVector(size_t size)
+      : data_(size ? FX_Alloc(T, size) : nullptr), size_(size) {}
+  FixedZeroedDataVector(const FixedZeroedDataVector&) = delete;
+  FixedZeroedDataVector& operator=(const FixedZeroedDataVector&) = delete;
+  FixedZeroedDataVector(FixedZeroedDataVector&& that) noexcept {
+    data_ = std::move(that.data_);
+    size_ = that.size_;
+    that.size_ = 0;
+  }
+  FixedZeroedDataVector& operator=(FixedZeroedDataVector&& that) noexcept {
+    data_ = std::move(that.data_);
+    size_ = that.size_;
+    that.size_ = 0;
+    return *this;
+  }
+  ~FixedZeroedDataVector() = default;
+
+  operator pdfium::span<const T>() const { return span(); }
+
+  pdfium::span<T> writable_span() {
+    return pdfium::make_span(data_.get(), size_);
+  }
+
+  pdfium::span<const T> span() const {
+    return pdfium::make_span(data_.get(), size_);
+  }
+
+  size_t size() const { return size_; }
+  bool empty() const { return size_ == 0; }
+
+ private:
+  std::unique_ptr<T, FxFreeDeleter> data_;
+  size_t size_;
+};
+
+}  // namespace fxcrt
+
+using fxcrt::FixedZeroedDataVector;
+
+#endif  // CORE_FXCRT_FIXED_ZEROED_DATA_VECTOR_H_
diff --git a/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp b/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp
new file mode 100644
index 0000000..e66600b
--- /dev/null
+++ b/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp
@@ -0,0 +1,62 @@
+// Copyright 2022 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/fixed_zeroed_data_vector.h"
+
+#include "core/fxcrt/span_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/span.h"
+
+TEST(FixedZeroedDataVector, NoData) {
+  FixedZeroedDataVector<int> vec;
+  EXPECT_EQ(0u, vec.size());
+  EXPECT_TRUE(vec.empty());
+  EXPECT_TRUE(vec.span().empty());
+  EXPECT_TRUE(vec.writable_span().empty());
+}
+
+TEST(FixedZeroedDataVector, WithData) {
+  FixedZeroedDataVector<int> vec(4);
+  EXPECT_FALSE(vec.empty());
+  EXPECT_EQ(4u, vec.size());
+  EXPECT_EQ(4u, vec.span().size());
+  EXPECT_EQ(4u, vec.writable_span().size());
+  EXPECT_THAT(vec.span(), testing::ElementsAre(0, 0, 0, 0));
+
+  constexpr int kData[] = {1, 2, 3, 4};
+  fxcrt::spancpy(vec.writable_span(), pdfium::make_span(kData));
+  EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
+}
+
+TEST(FixedZeroedDataVector, Move) {
+  FixedZeroedDataVector<int> vec(4);
+  constexpr int kData[] = {1, 2, 3, 4};
+  ASSERT_EQ(4u, vec.writable_span().size());
+  fxcrt::spancpy(vec.writable_span(), pdfium::make_span(kData));
+
+  FixedZeroedDataVector<int> vec2(std::move(vec));
+  EXPECT_FALSE(vec2.empty());
+  EXPECT_EQ(4u, vec2.size());
+  EXPECT_EQ(4u, vec2.span().size());
+  EXPECT_EQ(4u, vec2.writable_span().size());
+  EXPECT_THAT(vec2.span(), testing::ElementsAre(1, 2, 3, 4));
+
+  EXPECT_EQ(0u, vec.size());
+  EXPECT_TRUE(vec.empty());
+  EXPECT_TRUE(vec.span().empty());
+  EXPECT_TRUE(vec.writable_span().empty());
+
+  vec = std::move(vec2);
+  EXPECT_FALSE(vec.empty());
+  EXPECT_EQ(4u, vec.size());
+  EXPECT_EQ(4u, vec.span().size());
+  EXPECT_EQ(4u, vec.writable_span().size());
+  EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
+
+  EXPECT_EQ(0u, vec2.size());
+  EXPECT_TRUE(vec2.empty());
+  EXPECT_TRUE(vec2.span().empty());
+  EXPECT_TRUE(vec2.writable_span().empty());
+}