Enhance FixedSizeDataVector<>.

Bring in some improvements I've been contemplating prior to using
on the chromium side.

-- initialize size_ via std::exchange().
-- remove writable_span(), overload span() method instead.
-- introduce subspan(), first(), and last() methods.
-- adjust callers appropriately.

Change-Id: Icc451b0ddd558dab417e9c8c5636e7028ef506aa
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/115030
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/font/cpdf_cidfont.cpp b/core/fpdfapi/font/cpdf_cidfont.cpp
index 0961ccc..3acb32c 100644
--- a/core/fpdfapi/font/cpdf_cidfont.cpp
+++ b/core/fpdfapi/font/cpdf_cidfont.cpp
@@ -661,7 +661,7 @@
   }
 
   FixedUninitDataVector<uint8_t> sub_data(length);
-  if (!face->GetSfntTable(kGsubTag, sub_data.writable_span())) {
+  if (!face->GetSfntTable(kGsubTag, sub_data.span())) {
     return index;
   }
 
diff --git a/core/fpdfapi/font/cpdf_cmap.cpp b/core/fpdfapi/font/cpdf_cmap.cpp
index a38eab4..a830c8b 100644
--- a/core/fpdfapi/font/cpdf_cmap.cpp
+++ b/core/fpdfapi/font/cpdf_cmap.cpp
@@ -519,7 +519,7 @@
 void CPDF_CMap::SetDirectCharcodeToCIDTableRange(uint32_t start_code,
                                                  uint32_t end_code,
                                                  uint16_t start_cid) {
-  pdfium::span<uint16_t> span = m_DirectCharcodeToCIDTable.writable_span();
+  pdfium::span<uint16_t> span = m_DirectCharcodeToCIDTable.span();
   for (uint32_t code = start_code; code <= end_code; ++code) {
     span[code] = static_cast<uint16_t>(start_cid + code - start_code);
   }
diff --git a/core/fpdfapi/page/cpdf_contentparser.cpp b/core/fpdfapi/page/cpdf_contentparser.cpp
index 0cc6bc5..efd5e39 100644
--- a/core/fpdfapi/page/cpdf_contentparser.cpp
+++ b/core/fpdfapi/page/cpdf_contentparser.cpp
@@ -186,7 +186,7 @@
   }
 
   size_t pos = 0;
-  auto data_span = buffer.writable_span();
+  auto data_span = buffer.span();
   for (const auto& stream : m_StreamArray) {
     fxcrt::spancpy(data_span.subspan(pos), stream->GetSpan());
     pos += stream->GetSize();
diff --git a/core/fpdfapi/parser/cpdf_syntax_parser.cpp b/core/fpdfapi/parser/cpdf_syntax_parser.cpp
index 6ca1df6..e4be290 100644
--- a/core/fpdfapi/parser/cpdf_syntax_parser.cpp
+++ b/core/fpdfapi/parser/cpdf_syntax_parser.cpp
@@ -791,7 +791,7 @@
     // changing object lifetimes by handing `substream` to `pStream`, make a
     // copy of the data here.
     FixedUninitDataVector<uint8_t> data(substream->GetSize());
-    bool did_read = substream->ReadBlockAtOffset(data.writable_span(), 0);
+    bool did_read = substream->ReadBlockAtOffset(data.span(), 0);
     CHECK(did_read);
     auto data_as_stream =
         pdfium::MakeRetain<CFX_ReadOnlyVectorStream>(std::move(data));
diff --git a/core/fpdfapi/render/cpdf_docrenderdata.cpp b/core/fpdfapi/render/cpdf_docrenderdata.cpp
index db0af09..02f71fc 100644
--- a/core/fpdfapi/render/cpdf_docrenderdata.cpp
+++ b/core/fpdfapi/render/cpdf_docrenderdata.cpp
@@ -104,9 +104,8 @@
       CPDF_TransferFunc::kChannelSampleSize);
   FixedUninitDataVector<uint8_t> samples_b(
       CPDF_TransferFunc::kChannelSampleSize);
-  std::array<pdfium::span<uint8_t>, 3> samples = {samples_r.writable_span(),
-                                                  samples_g.writable_span(),
-                                                  samples_b.writable_span()};
+  std::array<pdfium::span<uint8_t>, 3> samples = {
+      samples_r.span(), samples_g.span(), samples_b.span()};
   if (pArray) {
     for (size_t v = 0; v < CPDF_TransferFunc::kChannelSampleSize; ++v) {
       float input = static_cast<float>(v) / 255.0f;
diff --git a/core/fxcodec/flate/flatemodule.cpp b/core/fxcodec/flate/flatemodule.cpp
index a023bb1..a37ca44 100644
--- a/core/fxcodec/flate/flatemodule.cpp
+++ b/core/fxcodec/flate/flatemodule.cpp
@@ -155,7 +155,7 @@
   if (current_code_ + early_change_ == 4094)
     return;
 
-  pdfium::span<uint32_t> codes_span = codes_.writable_span();
+  pdfium::span<uint32_t> codes_span = codes_.span();
   codes_span[current_code_++] = (prefix_code << 16) | append_char;
   if (current_code_ + early_change_ == 512 - 258)
     code_len_ = 10;
@@ -166,7 +166,7 @@
 }
 
 void CLZWDecoder::DecodeString(uint32_t code) {
-  pdfium::span<uint8_t> decode_span = decode_stack_.writable_span();
+  pdfium::span<uint8_t> decode_span = decode_stack_.span();
   pdfium::span<const uint32_t> codes_span = codes_.span();
   while (true) {
     int index = code - 258;
@@ -199,7 +199,7 @@
 }
 
 bool CLZWDecoder::Decode() {
-  pdfium::span<uint8_t> decode_span = decode_stack_.writable_span();
+  pdfium::span<uint8_t> decode_span = decode_stack_.span();
   uint32_t old_code = 0xFFFFFFFF;
   uint8_t last_char = 0;
 
diff --git a/core/fxcrt/fixed_size_data_vector.h b/core/fxcrt/fixed_size_data_vector.h
index bec5e10..0293e3a 100644
--- a/core/fxcrt/fixed_size_data_vector.h
+++ b/core/fxcrt/fixed_size_data_vector.h
@@ -34,34 +34,51 @@
       : data_(MaybeInit(size, OPTION)), size_(CalculateSize(size, OPTION)) {}
   FixedSizeDataVector(const FixedSizeDataVector&) = delete;
   FixedSizeDataVector& operator=(const FixedSizeDataVector&) = delete;
+
   template <DataVectorAllocOption OTHER_OPTION>
-  FixedSizeDataVector(FixedSizeDataVector<T, OTHER_OPTION>&& that) noexcept {
-    data_ = std::move(that.data_);
-    size_ = that.size_;
-    that.size_ = 0;
-  }
+  FixedSizeDataVector(FixedSizeDataVector<T, OTHER_OPTION>&& that) noexcept
+      : data_(std::move(that.data_)), size_(std::exchange(that.size_, 0)) {}
+
   template <DataVectorAllocOption OTHER_OPTION>
   FixedSizeDataVector& operator=(
       FixedSizeDataVector<T, OTHER_OPTION>&& that) noexcept {
     data_ = std::move(that.data_);
-    size_ = that.size_;
-    that.size_ = 0;
+    size_ = std::exchange(that.size_, 0);
     return *this;
   }
+
   ~FixedSizeDataVector() = default;
 
+  bool empty() const { return size_ == 0; }
+  size_t size() const { return size_; }
+
+  // Implicit access to data via span.
+  operator pdfium::span<T>() { return span(); }
   operator pdfium::span<const T>() const { return span(); }
 
-  pdfium::span<T> writable_span() {
-    return pdfium::make_span(data_.get(), size_);
-  }
-
+  // Explicit access to data via span.
+  pdfium::span<T> span() { return pdfium::span<T>(data_.get(), size_); }
   pdfium::span<const T> span() const {
-    return pdfium::make_span(data_.get(), size_);
+    return pdfium::span<const T>(data_.get(), size_);
   }
 
-  size_t size() const { return size_; }
-  bool empty() const { return size_ == 0; }
+  // Convenience methods to slice the vector into spans.
+  pdfium::span<T> subspan(size_t offset,
+                          size_t count = pdfium::dynamic_extent) {
+    return span().subspan(offset, count);
+  }
+  pdfium::span<const T> subspan(size_t offset,
+                                size_t count = pdfium::dynamic_extent) const {
+    return span().subspan(offset, count);
+  }
+
+  pdfium::span<T> first(size_t count) { return span().first(count); }
+  pdfium::span<const T> first(size_t count) const {
+    return span().first(count);
+  }
+
+  pdfium::span<T> last(size_t count) { return span().last(count); }
+  pdfium::span<const T> last(size_t count) const { return span().last(count); }
 
  private:
   friend class FixedSizeDataVector<T, DataVectorAllocOption::kInitialized>;
diff --git a/core/fxcrt/fixed_try_alloc_zeroed_data_vector_unittest.cpp b/core/fxcrt/fixed_try_alloc_zeroed_data_vector_unittest.cpp
index 613d838..01e7b20 100644
--- a/core/fxcrt/fixed_try_alloc_zeroed_data_vector_unittest.cpp
+++ b/core/fxcrt/fixed_try_alloc_zeroed_data_vector_unittest.cpp
@@ -19,7 +19,6 @@
   EXPECT_EQ(0u, vec.size());
   EXPECT_TRUE(vec.empty());
   EXPECT_TRUE(vec.span().empty());
-  EXPECT_TRUE(vec.writable_span().empty());
 }
 
 TEST(FixedTryAllocZeroedDataVector, WithData) {
@@ -27,11 +26,10 @@
   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));
+  fxcrt::spancpy(vec.span(), pdfium::make_span(kData));
   EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
 }
 
@@ -42,41 +40,36 @@
   EXPECT_TRUE(vec.empty());
   EXPECT_EQ(0u, vec.size());
   EXPECT_EQ(0u, vec.span().size());
-  EXPECT_EQ(0u, vec.writable_span().size());
 }
 
 TEST(FixedTryAllocZeroedDataVector, Move) {
   FixedTryAllocZeroedDataVector<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));
+  ASSERT_EQ(4u, vec.span().size());
+  fxcrt::spancpy(vec.span(), pdfium::make_span(kData));
   const int* const original_data_ptr = vec.span().data();
 
   FixedTryAllocZeroedDataVector<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(vec2.span().data(), original_data_ptr);
 
   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(vec.span().data(), original_data_ptr);
 
   EXPECT_EQ(0u, vec2.size());
   EXPECT_TRUE(vec2.empty());
   EXPECT_TRUE(vec2.span().empty());
-  EXPECT_TRUE(vec2.writable_span().empty());
 }
 
 TEST(FixedTryAllocZeroedDataVector, AssignFromFixedZeroedDataVector) {
@@ -84,8 +77,8 @@
 
   FixedZeroedDataVector<int> vec2(4);
   constexpr int kData[] = {1, 2, 3, 4};
-  ASSERT_EQ(4u, vec2.writable_span().size());
-  fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData));
+  ASSERT_EQ(4u, vec2.span().size());
+  fxcrt::spancpy(vec2.span(), pdfium::make_span(kData));
 
   vec = std::move(vec2);
   EXPECT_TRUE(vec2.empty());
@@ -98,8 +91,8 @@
 
   FixedUninitDataVector<int> vec2(4);
   constexpr int kData[] = {1, 2, 3, 4};
-  ASSERT_EQ(4u, vec2.writable_span().size());
-  fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData));
+  ASSERT_EQ(4u, vec2.span().size());
+  fxcrt::spancpy(vec2.span(), pdfium::make_span(kData));
 
   vec = std::move(vec2);
   EXPECT_TRUE(vec2.empty());
diff --git a/core/fxcrt/fixed_uninit_data_vector_unittest.cpp b/core/fxcrt/fixed_uninit_data_vector_unittest.cpp
index 70eda6a..e1414aa 100644
--- a/core/fxcrt/fixed_uninit_data_vector_unittest.cpp
+++ b/core/fxcrt/fixed_uninit_data_vector_unittest.cpp
@@ -4,6 +4,7 @@
 
 #include "core/fxcrt/fixed_uninit_data_vector.h"
 
+#include <numeric>
 #include <utility>
 
 #include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h"
@@ -18,53 +19,47 @@
   EXPECT_EQ(0u, vec.size());
   EXPECT_TRUE(vec.empty());
   EXPECT_TRUE(vec.span().empty());
-  EXPECT_TRUE(vec.writable_span().empty());
 }
 
 TEST(FixedUninitDataVector, WithData) {
   FixedUninitDataVector<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());
+  ASSERT_EQ(4u, vec.size());
+  ASSERT_EQ(4u, vec.span().size());
 
   constexpr int kData[] = {1, 2, 3, 4};
-  fxcrt::spancpy(vec.writable_span(), pdfium::make_span(kData));
+  fxcrt::spancpy(vec.span(), pdfium::make_span(kData));
   EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
 }
 
 TEST(FixedUninitDataVector, Move) {
   FixedUninitDataVector<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));
+  ASSERT_EQ(4u, vec.span().size());
+  fxcrt::spancpy(vec.span(), pdfium::make_span(kData));
   const int* const original_data_ptr = vec.span().data();
 
   FixedUninitDataVector<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());
+  ASSERT_EQ(4u, vec2.size());
+  ASSERT_EQ(4u, vec2.span().size());
   EXPECT_THAT(vec2.span(), testing::ElementsAre(1, 2, 3, 4));
   EXPECT_EQ(vec2.span().data(), original_data_ptr);
 
   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());
+  ASSERT_EQ(4u, vec.size());
+  ASSERT_EQ(4u, vec.span().size());
   EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
   EXPECT_EQ(vec.span().data(), original_data_ptr);
 
   EXPECT_EQ(0u, vec2.size());
   EXPECT_TRUE(vec2.empty());
   EXPECT_TRUE(vec2.span().empty());
-  EXPECT_TRUE(vec2.writable_span().empty());
 }
 
 TEST(FixedUninitDataVector, AssignFromFixedZeroedDataVector) {
@@ -72,12 +67,12 @@
 
   FixedZeroedDataVector<int> vec2(4);
   constexpr int kData[] = {1, 2, 3, 4};
-  ASSERT_EQ(4u, vec2.writable_span().size());
-  fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData));
+  ASSERT_EQ(4u, vec2.span().size());
+  fxcrt::spancpy(vec2.span(), pdfium::make_span(kData));
 
   vec = std::move(vec2);
   EXPECT_TRUE(vec2.empty());
-  EXPECT_EQ(4u, vec.span().size());
+  ASSERT_EQ(4u, vec.span().size());
   EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
 }
 
@@ -86,11 +81,58 @@
 
   FixedTryAllocZeroedDataVector<int> vec2(4);
   constexpr int kData[] = {1, 2, 3, 4};
-  ASSERT_EQ(4u, vec2.writable_span().size());
-  fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData));
+  ASSERT_EQ(4u, vec2.span().size());
+  fxcrt::spancpy(vec2.span(), pdfium::make_span(kData));
 
   vec = std::move(vec2);
   EXPECT_TRUE(vec2.empty());
-  EXPECT_EQ(4u, vec.span().size());
+  ASSERT_EQ(4u, vec.span().size());
   EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
 }
+
+TEST(FixedUninitDataVector, Subspan) {
+  FixedUninitDataVector<uint32_t> vec(4);
+  std::iota(vec.span().begin(), vec.span().end(), 0u);
+
+  pdfium::span<uint32_t> empty = vec.subspan(2, 0);
+  EXPECT_TRUE(empty.empty());
+
+  pdfium::span<uint32_t> first = vec.subspan(0, 1);
+  ASSERT_EQ(first.size(), 1u);
+  EXPECT_EQ(first[0], 0u);
+
+  pdfium::span<uint32_t> mids = vec.subspan(1, 2);
+  ASSERT_EQ(mids.size(), 2u);
+  EXPECT_EQ(mids[0], 1u);
+  EXPECT_EQ(mids[1], 2u);
+
+  pdfium::span<uint32_t> rest = vec.subspan(3);
+  ASSERT_EQ(rest.size(), 1u);
+  EXPECT_EQ(rest[0], 3u);
+}
+
+TEST(FixedUninitDataVector, First) {
+  FixedUninitDataVector<uint32_t> vec(4);
+  std::iota(vec.span().begin(), vec.span().end(), 0u);
+
+  pdfium::span<uint32_t> empty = vec.first(0);
+  EXPECT_TRUE(empty.empty());
+
+  pdfium::span<uint32_t> some = vec.first(2);
+  ASSERT_EQ(some.size(), 2u);
+  EXPECT_EQ(some[0], 0u);
+  EXPECT_EQ(some[1], 1u);
+}
+
+TEST(FixedUninitDataVector, Last) {
+  FixedUninitDataVector<uint32_t> vec(4);
+  std::iota(vec.span().begin(), vec.span().end(), 0u);
+
+  pdfium::span<uint32_t> empty = vec.first(0);
+  EXPECT_TRUE(empty.empty());
+
+  pdfium::span<uint32_t> some = vec.first(2);
+  ASSERT_EQ(some.size(), 2u);
+  EXPECT_EQ(some[0], 0u);
+  EXPECT_EQ(some[1], 1u);
+}
diff --git a/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp b/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp
index 297818f..d251275 100644
--- a/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp
+++ b/core/fxcrt/fixed_zeroed_data_vector_unittest.cpp
@@ -18,54 +18,48 @@
   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());
+  ASSERT_EQ(4u, vec.size());
+  ASSERT_EQ(4u, vec.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));
+  fxcrt::spancpy(vec.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));
+  ASSERT_EQ(4u, vec.span().size());
+  fxcrt::spancpy(vec.span(), pdfium::make_span(kData));
   const int* const original_data_ptr = vec.span().data();
 
   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());
+  ASSERT_EQ(4u, vec2.size());
+  ASSERT_EQ(4u, vec2.span().size());
   EXPECT_THAT(vec2.span(), testing::ElementsAre(1, 2, 3, 4));
   EXPECT_EQ(vec2.span().data(), original_data_ptr);
 
   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());
+  ASSERT_EQ(4u, vec.size());
+  ASSERT_EQ(4u, vec.span().size());
   EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
   EXPECT_EQ(vec.span().data(), original_data_ptr);
 
   EXPECT_EQ(0u, vec2.size());
   EXPECT_TRUE(vec2.empty());
   EXPECT_TRUE(vec2.span().empty());
-  EXPECT_TRUE(vec2.writable_span().empty());
 }
 
 TEST(FixedZeroedDataVector, AssignFromFixedUninitDataVector) {
@@ -73,12 +67,12 @@
 
   FixedUninitDataVector<int> vec2(4);
   constexpr int kData[] = {1, 2, 3, 4};
-  ASSERT_EQ(4u, vec2.writable_span().size());
-  fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData));
+  ASSERT_EQ(4u, vec2.span().size());
+  fxcrt::spancpy(vec2.span(), pdfium::make_span(kData));
 
   vec = std::move(vec2);
   EXPECT_TRUE(vec2.empty());
-  EXPECT_EQ(4u, vec.span().size());
+  ASSERT_EQ(4u, vec.span().size());
   EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
 }
 
@@ -87,11 +81,11 @@
 
   FixedTryAllocZeroedDataVector<int> vec2(4);
   constexpr int kData[] = {1, 2, 3, 4};
-  ASSERT_EQ(4u, vec2.writable_span().size());
-  fxcrt::spancpy(vec2.writable_span(), pdfium::make_span(kData));
+  ASSERT_EQ(4u, vec2.span().size());
+  fxcrt::spancpy(vec2.span(), pdfium::make_span(kData));
 
   vec = std::move(vec2);
   EXPECT_TRUE(vec2.empty());
-  EXPECT_EQ(4u, vec.span().size());
+  ASSERT_EQ(4u, vec.span().size());
   EXPECT_THAT(vec.span(), testing::ElementsAre(1, 2, 3, 4));
 }
diff --git a/core/fxge/cfx_fontmapper.cpp b/core/fxge/cfx_fontmapper.cpp
index f9607c4..34b025f 100644
--- a/core/fxge/cfx_fontmapper.cpp
+++ b/core/fxge/cfx_fontmapper.cpp
@@ -807,8 +807,7 @@
     return FixedUninitDataVector<uint8_t>();
 
   FixedUninitDataVector<uint8_t> result(required_size);
-  size_t actual_size =
-      m_pFontInfo->GetFontData(font_handle, 0, result.writable_span());
+  size_t actual_size = m_pFontInfo->GetFontData(font_handle, 0, result.span());
   if (actual_size != required_size)
     return FixedUninitDataVector<uint8_t>();
 
@@ -825,8 +824,8 @@
       m_pFontMgr->GetCachedTTCFontDesc(ttc_size, checksum);
   if (!pFontDesc) {
     FixedUninitDataVector<uint8_t> font_data(ttc_size);
-    size_t size = m_pFontInfo->GetFontData(font_handle, kTableTTCF,
-                                           font_data.writable_span());
+    size_t size =
+        m_pFontInfo->GetFontData(font_handle, kTableTTCF, font_data.span());
     if (size != ttc_size)
       return nullptr;
 
@@ -858,8 +857,7 @@
       m_pFontMgr->GetCachedFontDesc(subst_name, weight, is_italic);
   if (!pFontDesc) {
     FixedUninitDataVector<uint8_t> font_data(data_size);
-    size_t size =
-        m_pFontInfo->GetFontData(font_handle, 0, font_data.writable_span());
+    size_t size = m_pFontInfo->GetFontData(font_handle, 0, font_data.span());
     if (size != data_size)
       return nullptr;
 
diff --git a/core/fxge/dib/cstretchengine.cpp b/core/fxge/dib/cstretchengine.cpp
index 920645a..ae986c6 100644
--- a/core/fxge/dib/cstretchengine.cpp
+++ b/core/fxge/dib/cstretchengine.cpp
@@ -313,7 +313,7 @@
     }
 
     const uint8_t* src_scan = m_pSource->GetScanline(m_CurRow).data();
-    pdfium::span<uint8_t> dest_span = m_InterBuf.writable_span().subspan(
+    pdfium::span<uint8_t> dest_span = m_InterBuf.subspan(
         (m_CurRow - m_SrcClip.top) * m_InterPitch, m_InterPitch);
     size_t dest_span_index = 0;
     // TODO(npm): reduce duplicated code here
@@ -441,7 +441,7 @@
       case TransformMethod::k8BppTo8Bpp: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           pdfium::span<const uint8_t> src_span =
-              m_InterBuf.span().subspan((col - m_DestClip.left) * DestBpp);
+              m_InterBuf.subspan((col - m_DestClip.left) * DestBpp);
           uint32_t dest_a = 0;
           for (int j = pWeights->m_SrcStart; j <= pWeights->m_SrcEnd; ++j) {
             uint32_t pixel_weight = pWeights->GetWeightForPosition(j);
@@ -457,7 +457,7 @@
       case TransformMethod::kManyBpptoManyBpp: {
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           pdfium::span<const uint8_t> src_span =
-              m_InterBuf.span().subspan((col - m_DestClip.left) * DestBpp);
+              m_InterBuf.subspan((col - m_DestClip.left) * DestBpp);
           uint32_t dest_r = 0;
           uint32_t dest_g = 0;
           uint32_t dest_b = 0;
@@ -480,7 +480,7 @@
         DCHECK(m_bHasAlpha);
         for (int col = m_DestClip.left; col < m_DestClip.right; ++col) {
           pdfium::span<const uint8_t> src_span =
-              m_InterBuf.span().subspan((col - m_DestClip.left) * DestBpp);
+              m_InterBuf.subspan((col - m_DestClip.left) * DestBpp);
           uint32_t dest_a = 0;
           uint32_t dest_r = 0;
           uint32_t dest_g = 0;
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp
index f39c3c1..a339885 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_context.cpp
@@ -366,7 +366,7 @@
   constexpr int kMaxWideChars = 1024;
   constexpr int kMaxBytes = kMaxWideChars * sizeof(uint16_t);
   FixedZeroedDataVector<uint8_t> buffer(kMaxBytes);
-  pdfium::span<uint8_t> buffer_span = buffer.writable_span();
+  pdfium::span<uint8_t> buffer_span = buffer.span();
   int byte_length = m_pFormFillEnv->JS_appResponse(
       wsQuestion, wsTitle, wsDefaultAnswer, WideString(), bMark, buffer_span);
   if (byte_length <= 0)
diff --git a/fxbarcode/common/BC_CommonBitMatrix.cpp b/fxbarcode/common/BC_CommonBitMatrix.cpp
index 92dd917..2248640 100644
--- a/fxbarcode/common/BC_CommonBitMatrix.cpp
+++ b/fxbarcode/common/BC_CommonBitMatrix.cpp
@@ -41,5 +41,5 @@
 
 void CBC_CommonBitMatrix::Set(size_t x, size_t y) {
   size_t offset = y * m_rowSize + (x >> 5);
-  m_bits.writable_span()[offset] |= 1u << (x & 0x1f);
+  m_bits.span()[offset] |= 1u << (x & 0x1f);
 }
diff --git a/fxbarcode/datamatrix/BC_ErrorCorrection.cpp b/fxbarcode/datamatrix/BC_ErrorCorrection.cpp
index 135c39e..6433e8d 100644
--- a/fxbarcode/datamatrix/BC_ErrorCorrection.cpp
+++ b/fxbarcode/datamatrix/BC_ErrorCorrection.cpp
@@ -157,7 +157,7 @@
     return WideString();
 
   FixedZeroedDataVector<uint16_t> ecc(numECWords);
-  pdfium::span<uint16_t> ecc_span = ecc.writable_span();
+  pdfium::span<uint16_t> ecc_span = ecc.span();
   for (size_t i = 0; i < len; ++i) {
     uint16_t m = ecc_span[numECWords - 1] ^ codewords[i];
     for (int32_t j = numECWords - 1; j > 0; --j) {
diff --git a/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp b/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp
index 48e4128..996fccc 100644
--- a/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp
+++ b/fxbarcode/pdf417/BC_PDF417BarcodeRow.cpp
@@ -30,7 +30,7 @@
 CBC_BarcodeRow::~CBC_BarcodeRow() = default;
 
 void CBC_BarcodeRow::AddBar(bool black, size_t width) {
-  pdfium::span<uint8_t> available = row_.writable_span().subspan(offset_);
+  pdfium::span<uint8_t> available = row_.subspan(offset_);
   CHECK_LE(width, available.size());
   fxcrt::spanset(available.first(width), black ? 1 : 0);
   offset_ += width;
diff --git a/fxjs/cjs_app.cpp b/fxjs/cjs_app.cpp
index d3dd81d..edfce5c 100644
--- a/fxjs/cjs_app.cpp
+++ b/fxjs/cjs_app.cpp
@@ -546,15 +546,14 @@
   constexpr int kMaxWideChars = 1024;
   constexpr int kMaxBytes = kMaxWideChars * sizeof(uint16_t);
   FixedZeroedDataVector<uint8_t> buffer(kMaxBytes);
-  pdfium::span<uint8_t> buffer_span = buffer.writable_span();
   int byte_length = pRuntime->GetFormFillEnv()->JS_appResponse(
-      swQuestion, swTitle, swDefault, swLabel, bPassword, buffer_span);
+      swQuestion, swTitle, swDefault, swLabel, bPassword, buffer.span());
+
   if (byte_length < 0 || byte_length > kMaxBytes)
     return CJS_Result::Failure(JSMessage::kParamTooLongError);
 
-  buffer_span = buffer_span.first(std::min<size_t>(kMaxBytes, byte_length));
-  return CJS_Result::Success(
-      pRuntime->NewString(WideString::FromUTF16LE(buffer_span).AsStringView()));
+  auto wstr = WideString::FromUTF16LE(buffer.first(byte_length));
+  return CJS_Result::Success(pRuntime->NewString(wstr.AsStringView()));
 }
 
 CJS_Result CJS_App::get_media(CJS_Runtime* pRuntime) {