Add fxcrt::Fill()

This is replacement for C++20 std::ranges:fill(), which itself is a
replacement for memset() in many cases. These should mass convert
to std::ranges::fill() once C++20 becomes available.

Avoid future unsafe_buffers annotations.

-- Use a better pattern than 0u to test non-modification.

Change-Id: I37891a721d61364a7af523512ae45b076a78d26a
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/118910
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Thomas Sepez <tsepez@google.com>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fdrm/fx_crypt_sha.cpp b/core/fdrm/fx_crypt_sha.cpp
index dbfd015..9c2bc96 100644
--- a/core/fdrm/fx_crypt_sha.cpp
+++ b/core/fdrm/fx_crypt_sha.cpp
@@ -10,6 +10,7 @@
 #endif
 
 #include "core/fdrm/fx_crypt.h"
+#include "core/fxcrt/stl_util.h"
 
 #include <string.h>
 
@@ -445,7 +446,7 @@
   context->state[5] = 0x9B05688C;
   context->state[6] = 0x1F83D9AB;
   context->state[7] = 0x5BE0CD19;
-  memset(context->buffer, 0, sizeof(context->buffer));
+  fxcrt::Fill(context->buffer, 0);
 }
 
 void CRYPT_SHA256Update(CRYPT_sha2_context* context,
@@ -510,7 +511,7 @@
   context->state[5] = 0x8eb44a8768581511ULL;
   context->state[6] = 0xdb0c2e0d64f98fa7ULL;
   context->state[7] = 0x47b5481dbefa4fa4ULL;
-  memset(context->buffer, 0, sizeof(context->buffer));
+  fxcrt::Fill(context->buffer, 0);
 }
 
 void CRYPT_SHA384Update(CRYPT_sha2_context* context,
@@ -574,7 +575,7 @@
   context->state[5] = 0x9b05688c2b3e6c1fULL;
   context->state[6] = 0x1f83d9abfb41bd6bULL;
   context->state[7] = 0x5be0cd19137e2179ULL;
-  memset(context->buffer, 0, sizeof(context->buffer));
+  fxcrt::Fill(context->buffer, 0);
 }
 
 void CRYPT_SHA512Update(CRYPT_sha2_context* context,
diff --git a/core/fpdfapi/page/cpdf_colorspace_unittest.cpp b/core/fpdfapi/page/cpdf_colorspace_unittest.cpp
index 9cedbd1..4e60207 100644
--- a/core/fpdfapi/page/cpdf_colorspace_unittest.cpp
+++ b/core/fpdfapi/page/cpdf_colorspace_unittest.cpp
@@ -10,9 +10,9 @@
 #include "core/fpdfapi/page/cpdf_colorspace.h"
 
 #include <stdint.h>
-#include <string.h>
 
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/stl_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(CPDF_CalGray, TranslateImageLine) {
@@ -23,7 +23,7 @@
   ASSERT_TRUE(pCal);
 
   uint8_t dst[12];
-  memset(dst, 0xbd, sizeof(dst));
+  fxcrt::Fill(dst, 0xbd);
   // `bTransMask` only applies to CYMK colorspaces.
   pCal->TranslateImageLine(dst, kSrc, 4, 4, 1, /*bTransMask=*/false);
   for (size_t i = 0; i < 12; ++i)
@@ -39,7 +39,7 @@
   ASSERT_TRUE(pCal);
 
   uint8_t dst[12];
-  memset(dst, 0xbd, sizeof(dst));
+  fxcrt::Fill(dst, 0xbd);
   // `bTransMask` only applies to CYMK colorspaces.
   pCal->TranslateImageLine(dst, kSrc, 4, 4, 1, /*bTransMask=*/false);
   for (size_t i = 0; i < 12; ++i)
diff --git a/core/fpdfapi/parser/cpdf_seekablemultistream_unittest.cpp b/core/fpdfapi/parser/cpdf_seekablemultistream_unittest.cpp
index 3a62f13..fa36d39 100644
--- a/core/fpdfapi/parser/cpdf_seekablemultistream_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_seekablemultistream_unittest.cpp
@@ -9,6 +9,7 @@
 
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/stl_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(CPDFSeekableMultiStreamTest, NoStreams) {
@@ -17,7 +18,7 @@
       pdfium::MakeRetain<CPDF_SeekableMultiStream>(std::move(streams));
 
   uint8_t output_buffer[16];
-  memset(output_buffer, 0xbd, sizeof(output_buffer));
+  fxcrt::Fill(output_buffer, 0xbd);
   EXPECT_FALSE(fileread->ReadBlockAtOffset(
       pdfium::make_span(output_buffer).first(0u), 0));
   EXPECT_EQ(0xbd, output_buffer[0]);
@@ -32,7 +33,7 @@
       pdfium::MakeRetain<CPDF_SeekableMultiStream>(std::move(streams));
 
   uint8_t output_buffer[16];
-  memset(output_buffer, 0xbd, sizeof(output_buffer));
+  fxcrt::Fill(output_buffer, 0xbd);
   EXPECT_FALSE(fileread->ReadBlockAtOffset(
       pdfium::make_span(output_buffer).first(0u), 0));
   EXPECT_EQ(0xbd, output_buffer[0]);
@@ -59,33 +60,33 @@
       pdfium::MakeRetain<CPDF_SeekableMultiStream>(std::move(streams));
 
   uint8_t output_buffer[16];
-  memset(output_buffer, 0xbd, sizeof(output_buffer));
+  fxcrt::Fill(output_buffer, 0xbd);
   EXPECT_TRUE(fileread->ReadBlockAtOffset(
       pdfium::make_span(output_buffer).first(0u), 0));
   EXPECT_EQ(0xbd, output_buffer[0]);
 
-  memset(output_buffer, 0xbd, sizeof(output_buffer));
+  fxcrt::Fill(output_buffer, 0xbd);
   EXPECT_TRUE(fileread->ReadBlockAtOffset(
       pdfium::make_span(output_buffer).first(0u), 1));
   EXPECT_EQ(0xbd, output_buffer[0]);
 
-  memset(output_buffer, 0xbd, sizeof(output_buffer));
+  fxcrt::Fill(output_buffer, 0xbd);
   EXPECT_TRUE(fileread->ReadBlockAtOffset(
       pdfium::make_span(output_buffer).first(1u), 0));
   EXPECT_EQ(0, memcmp(output_buffer, "o", 1));
   EXPECT_EQ(0xbd, output_buffer[1]);
 
-  memset(output_buffer, 0xbd, sizeof(output_buffer));
+  fxcrt::Fill(output_buffer, 0xbd);
   EXPECT_TRUE(fileread->ReadBlockAtOffset(output_buffer, 0));
   EXPECT_EQ(0, memcmp(output_buffer, "one two three!!!", 16));
 
-  memset(output_buffer, 0xbd, sizeof(output_buffer));
+  fxcrt::Fill(output_buffer, 0xbd);
   EXPECT_TRUE(fileread->ReadBlockAtOffset(
       pdfium::make_span(output_buffer).first(10u), 2));
   EXPECT_EQ(0, memcmp(output_buffer, "e two thre", 10));
   EXPECT_EQ(0xbd, output_buffer[11]);
 
-  memset(output_buffer, 0xbd, sizeof(output_buffer));
+  fxcrt::Fill(output_buffer, 0xbd);
   EXPECT_FALSE(fileread->ReadBlockAtOffset(output_buffer, 1));
   EXPECT_EQ(0, memcmp(output_buffer, "ne two three!!!", 15));
   EXPECT_EQ(0xbd, output_buffer[15]);
diff --git a/core/fxcodec/gif/lzw_decompressor.cpp b/core/fxcodec/gif/lzw_decompressor.cpp
index 3b82c8c..db46759 100644
--- a/core/fxcodec/gif/lzw_decompressor.cpp
+++ b/core/fxcodec/gif/lzw_decompressor.cpp
@@ -15,10 +15,12 @@
 
 #include <algorithm>
 #include <memory>
+#include <type_traits>
 #include <utility>
 
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/ptr_util.h"
+#include "core/fxcrt/stl_util.h"
 
 namespace fxcodec {
 
@@ -144,9 +146,11 @@
   code_size_cur_ = code_size_ + 1;
   code_next_ = code_end_ + 1;
   code_old_ = static_cast<uint16_t>(-1);
-  memset(code_table_, 0, sizeof(code_table_));
-  for (uint16_t i = 0; i < code_clear_; i++)
+  fxcrt::Fill(code_table_, CodeEntry{});  // Aggregate initialization.
+  static_assert(std::is_aggregate_v<CodeEntry>);
+  for (uint16_t i = 0; i < code_clear_; i++) {
     code_table_[i].suffix = static_cast<uint8_t>(i);
+  }
   decompressed_.resize(code_next_ - code_clear_ + 1);
   decompressed_next_ = 0;
 }
diff --git a/core/fxcodec/gif/lzw_decompressor_unittest.cpp b/core/fxcodec/gif/lzw_decompressor_unittest.cpp
index 7d0cde9..6b6ef5f 100644
--- a/core/fxcodec/gif/lzw_decompressor_unittest.cpp
+++ b/core/fxcodec/gif/lzw_decompressor_unittest.cpp
@@ -15,6 +15,7 @@
 #include <iterator>
 
 #include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/stl_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -38,15 +39,15 @@
     *decompressed = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
     *(decompressor->DecompressedNextForTest()) = decompressed->size();
     uint8_t dest_buf[20];
-    memset(dest_buf, static_cast<uint8_t>(-1), sizeof(dest_buf));
-
+    fxcrt::Fill(dest_buf, 0xff);
     EXPECT_EQ(0u, decompressor->ExtractDataForTest(dest_buf, 0));
-    for (size_t i = 0; i < std::size(dest_buf); ++i)
+    for (size_t i = 0; i < std::size(dest_buf); ++i) {
       EXPECT_EQ(static_cast<uint8_t>(-1), dest_buf[i]);
-
+    }
     EXPECT_EQ(10u, *(decompressor->DecompressedNextForTest()));
-    for (size_t i = 0; i < *(decompressor->DecompressedNextForTest()); ++i)
+    for (size_t i = 0; i < *(decompressor->DecompressedNextForTest()); ++i) {
       EXPECT_EQ(i, (*decompressed)[i]);
+    }
   }
 
   // Check that less than decompressed size only gets the expected number
@@ -55,18 +56,19 @@
     *decompressed = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
     *(decompressor->DecompressedNextForTest()) = decompressed->size();
     uint8_t dest_buf[20];
-    memset(dest_buf, static_cast<uint8_t>(-1), sizeof(dest_buf));
-
+    fxcrt::Fill(dest_buf, 0xff);
     EXPECT_EQ(5u, decompressor->ExtractDataForTest(dest_buf, 5));
     size_t i = 0;
-    for (; i < 5; ++i)
+    for (; i < 5; ++i) {
       EXPECT_EQ(9 - i, dest_buf[i]);
-    for (; i < std::size(dest_buf); ++i)
+    }
+    for (; i < std::size(dest_buf); ++i) {
       EXPECT_EQ(static_cast<uint8_t>(-1), dest_buf[i]);
-
+    }
     EXPECT_EQ(5u, *(decompressor->DecompressedNextForTest()));
-    for (i = 0; i < *(decompressor->DecompressedNextForTest()); ++i)
+    for (i = 0; i < *(decompressor->DecompressedNextForTest()); ++i) {
       EXPECT_EQ(i, (*decompressed)[i]);
+    }
   }
 
   // Check that greater than decompressed size depletes the decompressor
@@ -75,16 +77,16 @@
     *decompressed = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
     *(decompressor->DecompressedNextForTest()) = decompressed->size();
     uint8_t dest_buf[20];
-    memset(dest_buf, static_cast<uint8_t>(-1), sizeof(dest_buf));
-
+    fxcrt::Fill(dest_buf, 0xff);
     EXPECT_EQ(10u,
               decompressor->ExtractDataForTest(dest_buf, std::size(dest_buf)));
     size_t i = 0;
-    for (; i < 10; ++i)
+    for (; i < 10; ++i) {
       EXPECT_EQ(9 - i, dest_buf[i]);
-    for (; i < std::size(dest_buf); ++i)
+    }
+    for (; i < std::size(dest_buf); ++i) {
       EXPECT_EQ(static_cast<uint8_t>(-1), dest_buf[i]);
-
+    }
     EXPECT_EQ(0u, *(decompressor->DecompressedNextForTest()));
   }
 }
@@ -214,15 +216,14 @@
 
   static constexpr uint8_t kExpectedScanline[] = {0x00, 0x00, 0x00, 0x00};
   uint8_t output_data[std::size(kExpectedScanline)];
-
-  memset(output_data, 0xFF, sizeof(output_data));
+  fxcrt::Fill(output_data, 0xff);
   uint32_t output_size = std::size(output_data);
   EXPECT_EQ(LZWDecompressor::Status::kInsufficientDestSize,
             decompressor->Decode(output_data, &output_size));
   EXPECT_EQ(std::size(kExpectedScanline), output_size);
   EXPECT_THAT(output_data, ElementsAreArray(kExpectedScanline));
 
-  memset(output_data, 0xFF, sizeof(output_data));
+  fxcrt::Fill(output_data, 0xff);
   output_size = std::size(output_data);
   EXPECT_EQ(LZWDecompressor::Status::kSuccess,
             decompressor->Decode(output_data, &output_size));
diff --git a/core/fxcodec/jpx/jpx_unittest.cpp b/core/fxcodec/jpx/jpx_unittest.cpp
index c4bfc10..87f6dc8 100644
--- a/core/fxcodec/jpx/jpx_unittest.cpp
+++ b/core/fxcodec/jpx/jpx_unittest.cpp
@@ -17,6 +17,7 @@
 #include "core/fxcodec/jpx/jpx_decode_utils.h"
 #include "core/fxcrt/fx_memcpy_wrappers.h"
 #include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/stl_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/libopenjpeg/opj_malloc.h"
 
@@ -45,12 +46,12 @@
   uint8_t buffer[16];
 
   // Reads of size 0 do nothing but return an error code.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbd);
   EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 0, &dd));
   EXPECT_EQ(0xbd, buffer[0]);
 
   // Reads of nonzero size do nothing but return an error code.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbd);
   EXPECT_EQ(kReadError, opj_read_from_memory(buffer, sizeof(buffer), &dd));
   EXPECT_EQ(0xbd, buffer[0]);
 
@@ -72,12 +73,12 @@
   uint8_t buffer[16];
 
   // Reads of size 0 do nothing but return an error code.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbd);
   EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 0, &dd));
   EXPECT_EQ(0xbd, buffer[0]);
 
   // Reads of nonzero size do nothing but return an error code.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbd);
   EXPECT_EQ(kReadError, opj_read_from_memory(buffer, sizeof(buffer), &dd));
   EXPECT_EQ(0xbd, buffer[0]);
 
@@ -100,7 +101,7 @@
     DecodeData dd(stream_data, sizeof(stream_data));
 
     // Exact sized read in a single call.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(8u, opj_read_from_memory(buffer, sizeof(buffer), &dd));
     EXPECT_EQ(0x00, buffer[0]);
     EXPECT_EQ(0x01, buffer[1]);
@@ -116,19 +117,19 @@
     DecodeData dd(stream_data, sizeof(stream_data));
 
     // Simple read.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(2u, opj_read_from_memory(buffer, 2, &dd));
     EXPECT_EQ(0x00, buffer[0]);
     EXPECT_EQ(0x01, buffer[1]);
     EXPECT_EQ(0xbd, buffer[2]);
 
     // Read of size 0 doesn't affect things.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(0u, opj_read_from_memory(buffer, 0, &dd));
     EXPECT_EQ(0xbd, buffer[0]);
 
     // Read exactly up to end of data.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(6u, opj_read_from_memory(buffer, 6, &dd));
     EXPECT_EQ(0x02, buffer[0]);
     EXPECT_EQ(0x03, buffer[1]);
@@ -139,7 +140,7 @@
     EXPECT_EQ(0xbd, buffer[6]);
 
     // Read of size 0 at EOF is still an error.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 0, &dd));
     EXPECT_EQ(0xbd, buffer[0]);
   }
@@ -151,7 +152,7 @@
     DecodeData dd(stream_data, sizeof(stream_data));
 
     // Read beyond bounds in a single step.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(8u, opj_read_from_memory(buffer, sizeof(buffer) + 1, &dd));
     EXPECT_EQ(0x00, buffer[0]);
     EXPECT_EQ(0x01, buffer[1]);
@@ -167,7 +168,7 @@
     DecodeData dd(stream_data, sizeof(stream_data));
 
     // Read well beyond bounds in a single step.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(8u, opj_read_from_memory(
                       buffer, std::numeric_limits<OPJ_SIZE_T>::max(), &dd));
     EXPECT_EQ(0x00, buffer[0]);
@@ -185,7 +186,7 @@
 
     // Read of size 6 gets first 6 bytes.
     // rest of buffer intact.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(6u, opj_read_from_memory(buffer, 6, &dd));
     EXPECT_EQ(0x00, buffer[0]);
     EXPECT_EQ(0x01, buffer[1]);
@@ -196,14 +197,14 @@
     EXPECT_EQ(0xbd, buffer[6]);
 
     // Read of size 6 gets remaining two bytes.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(2u, opj_read_from_memory(buffer, 6, &dd));
     EXPECT_EQ(0x86, buffer[0]);
     EXPECT_EQ(0x87, buffer[1]);
     EXPECT_EQ(0xbd, buffer[2]);
 
     // Read of 6 more gets nothing and leaves rest of buffer intact.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 6, &dd));
     EXPECT_EQ(0xbd, buffer[0]);
   }
@@ -217,28 +218,28 @@
     DecodeData dd(stream_data, sizeof(stream_data));
 
     // Skiping within buffer is allowed.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(1u, opj_skip_from_memory(1, &dd));
     EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
     EXPECT_EQ(0x01, buffer[0]);
     EXPECT_EQ(0xbd, buffer[1]);
 
     // Skiping 0 bytes changes nothing.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(0, opj_skip_from_memory(0, &dd));
     EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
     EXPECT_EQ(0x02, buffer[0]);
     EXPECT_EQ(0xbd, buffer[1]);
 
     // Skiping to EOS-1 is possible.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
     EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
     EXPECT_EQ(0x87, buffer[0]);
     EXPECT_EQ(0xbd, buffer[1]);
 
     // Next read fails.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
     EXPECT_EQ(0xbd, buffer[0]);
   }
@@ -246,7 +247,7 @@
     DecodeData dd(stream_data, sizeof(stream_data));
 
     // Skiping directly to EOS is allowed.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(8u, opj_skip_from_memory(8, &dd));
 
     // Next read fails.
@@ -257,7 +258,7 @@
     DecodeData dd(stream_data, sizeof(stream_data));
 
     // Skipping beyond end of stream is allowed and returns full distance.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(9u, opj_skip_from_memory(9, &dd));
 
     // Next read fails.
@@ -269,7 +270,7 @@
 
     // Skipping way beyond EOS is allowd, doesn't wrap, and returns
     // full distance.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
     EXPECT_EQ(std::numeric_limits<OPJ_OFF_T>::max(),
               opj_skip_from_memory(std::numeric_limits<OPJ_OFF_T>::max(), &dd));
@@ -282,7 +283,7 @@
     DecodeData dd(stream_data, sizeof(stream_data));
 
     // Negative skip within buffer not is allowed, position unchanged.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
     EXPECT_EQ(kSkipError, opj_skip_from_memory(-2, &dd));
 
@@ -292,7 +293,7 @@
     EXPECT_EQ(0xbd, buffer[1]);
 
     // Negative skip before buffer is not allowed, position unchanged.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(kSkipError, opj_skip_from_memory(-4, &dd));
 
     // Next read succeeds as if nothing has happenned.
@@ -304,7 +305,7 @@
     DecodeData dd(stream_data, sizeof(stream_data));
 
     // Negative skip way before buffer is not allowed, doesn't wrap
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
     EXPECT_EQ(kSkipError,
               opj_skip_from_memory(std::numeric_limits<OPJ_OFF_T>::min(), &dd));
@@ -318,7 +319,7 @@
     DecodeData dd(stream_data, sizeof(stream_data));
 
     // Negative skip after EOS isn't alowed, still EOS.
-    memset(buffer, 0xbd, sizeof(buffer));
+    fxcrt::Fill(buffer, 0xbd);
     EXPECT_EQ(8u, opj_skip_from_memory(8, &dd));
     EXPECT_EQ(kSkipError, opj_skip_from_memory(-4, &dd));
 
@@ -333,21 +334,21 @@
   DecodeData dd(stream_data, sizeof(stream_data));
 
   // Seeking within buffer is allowed and read succeeds
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbd);
   EXPECT_TRUE(opj_seek_from_memory(1, &dd));
   EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
   EXPECT_EQ(0x01, buffer[0]);
   EXPECT_EQ(0xbd, buffer[1]);
 
   // Seeking before start returns error leaving position unchanged.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbd);
   EXPECT_FALSE(opj_seek_from_memory(-1, &dd));
   EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
   EXPECT_EQ(0x02, buffer[0]);
   EXPECT_EQ(0xbd, buffer[1]);
 
   // Seeking way before start returns error leaving position unchanged.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbd);
   EXPECT_FALSE(
       opj_seek_from_memory(std::numeric_limits<OPJ_OFF_T>::min(), &dd));
   EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
@@ -355,40 +356,40 @@
   EXPECT_EQ(0xbd, buffer[1]);
 
   // Seeking exactly to EOS is allowed but read fails.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbd);
   EXPECT_TRUE(opj_seek_from_memory(8, &dd));
   EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
   EXPECT_EQ(0xbd, buffer[0]);
 
   // Seeking back to zero offset is allowed and read succeeds.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbd);
   EXPECT_TRUE(opj_seek_from_memory(0, &dd));
   EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
   EXPECT_EQ(0x00, buffer[0]);
   EXPECT_EQ(0xbd, buffer[1]);
 
   // Seeking beyond end of stream is allowed but read fails.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbd);
   EXPECT_TRUE(opj_seek_from_memory(16, &dd));
   EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
   EXPECT_EQ(0xbd, buffer[0]);
 
   // Seeking within buffer after seek past EOF restores good state.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbd);
   EXPECT_TRUE(opj_seek_from_memory(4, &dd));
   EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
   EXPECT_EQ(0x84, buffer[0]);
   EXPECT_EQ(0xbd, buffer[1]);
 
   // Seeking way beyond EOS is allowed, doesn't wrap, and read fails.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbd);
   EXPECT_TRUE(opj_seek_from_memory(std::numeric_limits<OPJ_OFF_T>::max(), &dd));
   EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
   EXPECT_EQ(0xbd, buffer[0]);
 }
 
 TEST(fxcodec, YUV420ToRGB) {
-  opj_image_comp_t u = {};
+  opj_image_comp_t u = {};  // Aggregate initialization.
   static_assert(std::is_aggregate_v<decltype(u)>);
   u.dx = 1;
   u.dy = 1;
@@ -396,7 +397,7 @@
   u.h = 16;
   u.prec = 8;
   u.bpp = 8;
-  opj_image_comp_t v = {};
+  opj_image_comp_t v = {};  // Aggregate initialization.
   static_assert(std::is_aggregate_v<decltype(v)>);
   v.dx = 1;
   v.dy = 1;
@@ -404,13 +405,13 @@
   v.h = 16;
   v.prec = 8;
   v.bpp = 8;
-  opj_image_comp_t y = {};
+  opj_image_comp_t y = {};  // Aggregate initialization.
   static_assert(std::is_aggregate_v<decltype(y)>);
   y.dx = 1;
   y.dy = 1;
   y.prec = 8;
   y.bpp = 8;
-  opj_image_t img = {};
+  opj_image_t img = {};  // Aggregate initialization.
   static_assert(std::is_aggregate_v<decltype(img)>);
   img.numcomps = 3;
   img.color_space = OPJ_CLRSPC_SYCC;
diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn
index bd5d617..70ad8c9 100644
--- a/core/fxcrt/BUILD.gn
+++ b/core/fxcrt/BUILD.gn
@@ -236,6 +236,7 @@
     "scoped_set_insertion_unittest.cpp",
     "shared_copy_on_write_unittest.cpp",
     "span_util_unittest.cpp",
+    "stl_util_unittest.cpp",
     "string_pool_template_unittest.cpp",
     "tree_node_unittest.cpp",
     "unowned_ptr_unittest.cpp",
diff --git a/core/fxcrt/stl_util.h b/core/fxcrt/stl_util.h
index 504bbc4..8fe1639 100644
--- a/core/fxcrt/stl_util.h
+++ b/core/fxcrt/stl_util.h
@@ -5,6 +5,7 @@
 #ifndef CORE_FXCRT_STL_UTIL_H_
 #define CORE_FXCRT_STL_UTIL_H_
 
+#include <algorithm>
 #include <memory>
 
 #include "core/fxcrt/numerics/safe_conversions.h"
@@ -40,6 +41,12 @@
   return index >= 0 && index < CollectionSize<IndexType>(collection);
 }
 
+// Equivalent of C++20 std::ranges::fill().
+template <typename T, typename V>
+void Fill(T& container, const V& value) {
+  std::fill(std::begin(container), std::end(container), value);
+}
+
 }  // namespace fxcrt
 
 #endif  // CORE_FXCRT_STL_UTIL_H_
diff --git a/core/fxcrt/stl_util_unittest.cpp b/core/fxcrt/stl_util_unittest.cpp
new file mode 100644
index 0000000..5ee6993
--- /dev/null
+++ b/core/fxcrt/stl_util_unittest.cpp
@@ -0,0 +1,46 @@
+// Copyright 2024 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/stl_util.h"
+
+#include <stdint.h>
+
+#include <array>
+#include <vector>
+
+#include "core/fxcrt/span.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(fxcrt, FillCArray) {
+  uint32_t buf[4];
+  fxcrt::Fill(buf, 0x01020304u);
+  for (const auto b : buf) {
+    EXPECT_EQ(b, 0x01020304u);
+  }
+}
+
+TEST(fxcrt, FillStdArray) {
+  std::array<uint16_t, 10> buf;
+  fxcrt::Fill(buf, 0x0102u);
+  for (const auto b : buf) {
+    EXPECT_EQ(b, 0x0102u);
+  }
+}
+
+TEST(fxcrt, FillStdVector) {
+  std::vector<uint8_t> buf(15);
+  fxcrt::Fill(buf, 0x32u);
+  for (const auto b : buf) {
+    EXPECT_EQ(b, 0x32u);
+  }
+}
+
+TEST(fxcrt, FillSpan) {
+  float buf[12];
+  auto buf_span = pdfium::make_span(buf);
+  fxcrt::Fill(buf_span, 123.0f);
+  for (const auto b : buf) {
+    EXPECT_EQ(b, 123.0f);
+  }
+}
diff --git a/fpdfsdk/fpdf_structtree_embeddertest.cpp b/fpdfsdk/fpdf_structtree_embeddertest.cpp
index 0df7829..de53be0 100644
--- a/fpdfsdk/fpdf_structtree_embeddertest.cpp
+++ b/fpdfsdk/fpdf_structtree_embeddertest.cpp
@@ -10,6 +10,7 @@
 #include <iterator>
 #include <optional>
 
+#include "core/fxcrt/stl_util.h"
 #include "public/fpdf_structtree.h"
 #include "testing/embedder_test.h"
 #include "testing/fx_string_testhelpers.h"
@@ -462,10 +463,11 @@
 
     // Deliberately pass in a small buffer size to make sure |buffer| remains
     // untouched.
+    fxcrt::Fill(buffer, 0xbdfcu);
     ASSERT_EQ(18U, FPDF_StructElement_GetType(element, buffer, 1));
-    for (size_t i = 0; i < std::size(buffer); ++i)
-      EXPECT_EQ(0U, buffer[i]);
-
+    for (const auto b : buffer) {
+      EXPECT_EQ(0xbdfcu, b);
+    }
     ASSERT_EQ(18U, FPDF_StructElement_GetType(element, buffer, sizeof(buffer)));
     EXPECT_EQ(L"Document", GetPlatformWString(buffer));
   }
@@ -506,14 +508,15 @@
 
     ASSERT_EQ(1, FPDF_StructElement_CountChildren(child));
     FPDF_STRUCTELEMENT gchild = FPDF_StructElement_GetChildAtIndex(child, 0);
-    memset(buffer, 0, sizeof(buffer));
+
+    fxcrt::Fill(buffer, 0xbdfcu);
     // Missing /Type in `gchild`
     ASSERT_EQ(0U,
               FPDF_StructElement_GetObjType(gchild, buffer, sizeof(buffer)));
     // Buffer is untouched.
-    for (size_t i = 0; i < std::size(buffer); ++i)
-      EXPECT_EQ(0U, buffer[i]);
-
+    for (const auto b : buffer) {
+      EXPECT_EQ(0xbdfcu, b);
+    }
     ASSERT_EQ(1, FPDF_StructElement_CountChildren(gchild));
     FPDF_STRUCTELEMENT ggchild = FPDF_StructElement_GetChildAtIndex(gchild, 0);
     ASSERT_EQ(28U,
@@ -577,16 +580,16 @@
     ASSERT_EQ(0U, FPDF_StructElement_GetTitle(nullptr, nullptr, 0));
     ASSERT_EQ(20U, FPDF_StructElement_GetTitle(element, nullptr, 0));
 
-    memset(buffer, 0, sizeof(buffer));
     // Deliberately pass in a small buffer size to make sure |buffer| remains
     // untouched.
+    fxcrt::Fill(buffer, 0xbdfcu);
     ASSERT_EQ(20U, FPDF_StructElement_GetTitle(element, buffer, 1));
-    for (size_t i = 0; i < std::size(buffer); ++i)
-      EXPECT_EQ(0U, buffer[i]);
+    for (const auto b : buffer) {
+      EXPECT_EQ(0xbdfcu, b);
+    }
 
     ASSERT_EQ(20U,
               FPDF_StructElement_GetTitle(element, buffer, sizeof(buffer)));
-
     EXPECT_EQ(L"TitleText", GetPlatformWString(buffer));
 
     ASSERT_EQ(1, FPDF_StructElement_CountChildren(element));
@@ -673,7 +676,7 @@
       EXPECT_EQ(12U, out_len);
       EXPECT_EQ(L"Table", GetPlatformWString(str_val));
 
-      memset(buffer, 0, sizeof(buffer));
+      fxcrt::Fill(buffer, 0u);
       ASSERT_TRUE(FPDF_StructElement_Attr_GetName(attr, 0, buffer,
                                                   sizeof(buffer), &out_len));
       EXPECT_EQ(8U, out_len);
diff --git a/fpdfsdk/fpdf_text_embeddertest.cpp b/fpdfsdk/fpdf_text_embeddertest.cpp
index afff1ed..954323d 100644
--- a/fpdfsdk/fpdf_text_embeddertest.cpp
+++ b/fpdfsdk/fpdf_text_embeddertest.cpp
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "build/build_config.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxge/fx_font.h"
 #include "public/cpp/fpdf_scopers.h"
 #include "public/fpdf_doc.h"
@@ -53,7 +54,7 @@
   ASSERT_TRUE(textpage);
 
   unsigned short buffer[128];
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbdbd);
 
   // Check that edge cases are handled gracefully
   EXPECT_EQ(0, FPDFText_GetText(textpage, 0, 128, nullptr));
@@ -63,7 +64,7 @@
   EXPECT_EQ(0, buffer[0]);
 
   // Keep going and check the next case.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbdbd);
   EXPECT_EQ(2, FPDFText_GetText(textpage, 0, 1, buffer));
   EXPECT_EQ(kHelloGoodbyeText[0], buffer[0]);
   EXPECT_EQ(0, buffer[1]);
@@ -87,7 +88,7 @@
   // the expected string, plus 2 more for the terminating character.
   static const char kSmallExpected[] = "Hello";
   unsigned short small_buffer[12];
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbdbd);
   EXPECT_EQ(6, FPDFText_GetText(textpage, 0, 5, small_buffer));
   EXPECT_TRUE(check_unsigned_shorts(kSmallExpected, small_buffer,
                                     sizeof(kSmallExpected)));
@@ -206,19 +207,19 @@
       9, FPDFText_GetBoundedText(textpage, 41.0, 56.0, 82.0, 48.0, nullptr, 0));
 
   // Extract starting at character 4 as above.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbdbd);
   EXPECT_EQ(
       1, FPDFText_GetBoundedText(textpage, 41.0, 56.0, 82.0, 48.0, buffer, 1));
   EXPECT_TRUE(check_unsigned_shorts(kHelloGoodbyeText + 4, buffer, 1));
   EXPECT_EQ(0xbdbd, buffer[1]);
 
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbdbd);
   EXPECT_EQ(
       9, FPDFText_GetBoundedText(textpage, 41.0, 56.0, 82.0, 48.0, buffer, 9));
   EXPECT_TRUE(check_unsigned_shorts(kHelloGoodbyeText + 4, buffer, 8));
   EXPECT_EQ(0xbdbd, buffer[9]);
 
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbdbd);
   EXPECT_EQ(10, FPDFText_GetBoundedText(textpage, 41.0, 56.0, 82.0, 48.0,
                                         buffer, 128));
   EXPECT_TRUE(check_unsigned_shorts(kHelloGoodbyeText + 4, buffer, 9));
@@ -279,7 +280,7 @@
     ASSERT_EQ(kCharCount, FPDFText_CountChars(textpage.get()));
 
     unsigned short buffer[kCharCount + 1];
-    memset(buffer, 0x42, sizeof(buffer));
+    fxcrt::Fill(buffer, 0x4242);
     EXPECT_EQ(kCharCount + 1,
               FPDFText_GetText(textpage.get(), 0, kCharCount, buffer));
     EXPECT_EQ(0x05d1, buffer[0]);
@@ -694,20 +695,20 @@
   // Retrieve a link with too small a buffer.  Buffer will not be
   // NUL-terminated, but must not be modified past indicated length,
   // so pre-fill with a pattern to check write bounds.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbdbd);
   EXPECT_EQ(1, FPDFLink_GetURL(pagelink, 0, buffer, 1));
   EXPECT_TRUE(check_unsigned_shorts(expected_url, buffer, 1));
   EXPECT_EQ(0xbdbd, buffer[1]);
 
   // Check buffer that doesn't have space for a terminating NUL.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbdbd);
   EXPECT_EQ(static_cast<int>(expected_len - 1),
             FPDFLink_GetURL(pagelink, 0, buffer, expected_len - 1));
   EXPECT_TRUE(check_unsigned_shorts(expected_url, buffer, expected_len - 1));
   EXPECT_EQ(0xbdbd, buffer[expected_len - 1]);
 
   // Retreive link with exactly-sized buffer.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbdbd);
   EXPECT_EQ(static_cast<int>(expected_len),
             FPDFLink_GetURL(pagelink, 0, buffer, expected_len));
   EXPECT_TRUE(check_unsigned_shorts(expected_url, buffer, expected_len));
@@ -715,7 +716,7 @@
   EXPECT_EQ(0xbdbd, buffer[expected_len]);
 
   // Retreive link with ample-sized-buffer.
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbdbd);
   EXPECT_EQ(static_cast<int>(expected_len),
             FPDFLink_GetURL(pagelink, 0, buffer, 128));
   EXPECT_TRUE(check_unsigned_shorts(expected_url, buffer, expected_len));
@@ -1175,7 +1176,7 @@
     EXPECT_EQ(kData[i], FPDFText_GetUnicode(textpage, kStartIndex + i));
 
   unsigned short buffer[std::size(kData) + 1];
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbdbd);
   int count = FPDFText_GetText(textpage, kStartIndex, std::size(kData), buffer);
   ASSERT_GT(count, 0);
   ASSERT_EQ(std::size(kData) + 1, static_cast<size_t>(count));
@@ -1253,7 +1254,7 @@
 
   // Should not include the control characters in the output
   unsigned short buffer[128];
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbdbd);
   int num_chars = FPDFText_GetText(textpage, 0, 128, buffer);
   ASSERT_EQ(kHelloGoodbyeTextSize, num_chars);
   EXPECT_TRUE(
@@ -1264,7 +1265,7 @@
   // Offset is the length of 'Hello, world!\r\n' + 2 control characters in the
   // original stream
   static const int offset = 17;
-  memset(buffer, 0xbd, sizeof(buffer));
+  fxcrt::Fill(buffer, 0xbdbd);
   num_chars = FPDFText_GetText(textpage, offset, 128, buffer);
 
   ASSERT_GE(num_chars, 0);
@@ -1468,7 +1469,7 @@
       ASSERT_TRUE(textpage);
 
       unsigned short buffer[128];
-      memset(buffer, 0xbd, sizeof(buffer));
+      fxcrt::Fill(buffer, 0xbdbd);
       int num_chars = FPDFText_GetText(textpage.get(), 0, 128, buffer);
       ASSERT_EQ(kHelloGoodbyeTextSize, num_chars);
       EXPECT_TRUE(check_unsigned_shorts(kHelloGoodbyeText, buffer,
@@ -1479,7 +1480,7 @@
                 FPDFText_GetBoundedText(textpage.get(), box.left, box.top,
                                         box.right, box.bottom, nullptr, 0));
 
-      memset(buffer, 0xbd, sizeof(buffer));
+      fxcrt::Fill(buffer, 0xbdbd);
       ASSERT_EQ(expected_char_count + 1,
                 FPDFText_GetBoundedText(textpage.get(), box.left, box.top,
                                         box.right, box.bottom, buffer, 128));