Replace 3-parameter CPDF_String ctor

Replace the 3-parameter CPDF_String ctor with a different 3-parameter
ctor that takes a span<const uint8_t> and a placeholder. Update callers
to use some kind of a uint8_t data container instead of stuffing the
data into a ByteString.

Change-Id: I168d1e4b42bbf804758f65ecfead23097a8526c5
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/119372
Reviewed-by: Thomas Sepez <tsepez@google.com>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/edit/cpdf_creator.cpp b/core/fpdfapi/edit/cpdf_creator.cpp
index 000df08..dfcfa7e 100644
--- a/core/fpdfapi/edit/cpdf_creator.cpp
+++ b/core/fpdfapi/edit/cpdf_creator.cpp
@@ -9,6 +9,7 @@
 #include <stdint.h>
 
 #include <algorithm>
+#include <array>
 #include <set>
 #include <utility>
 
@@ -95,17 +96,15 @@
   return true;
 }
 
-ByteString GenerateFileID(uint32_t dwSeed1, uint32_t dwSeed2) {
-  uint32_t buffer[4];
+std::array<uint32_t, 4> GenerateFileID(uint32_t dwSeed1, uint32_t dwSeed2) {
   void* pContext1 = FX_Random_MT_Start(dwSeed1);
   void* pContext2 = FX_Random_MT_Start(dwSeed2);
-  buffer[0] = FX_Random_MT_Generate(pContext1);
-  buffer[1] = FX_Random_MT_Generate(pContext1);
-  buffer[2] = FX_Random_MT_Generate(pContext2);
-  buffer[3] = FX_Random_MT_Generate(pContext2);
+  std::array<uint32_t, 4> buffer = {
+      FX_Random_MT_Generate(pContext1), FX_Random_MT_Generate(pContext1),
+      FX_Random_MT_Generate(pContext2), FX_Random_MT_Generate(pContext2)};
   FX_Random_MT_Close(pContext1);
   FX_Random_MT_Close(pContext2);
-  return ByteString(ByteStringView(pdfium::as_byte_span(buffer)));
+  return buffer;
 }
 
 bool OutputIndex(IFX_ArchiveStream* archive, FX_FILESIZE offset) {
@@ -570,9 +569,11 @@
   if (pID1) {
     m_pIDArray->Append(pID1->Clone());
   } else {
-    ByteString bsBuffer =
+    std::array<uint32_t, 4> file_id =
         GenerateFileID((uint32_t)(uintptr_t)this, m_dwLastObjNum);
-    m_pIDArray->AppendNew<CPDF_String>(bsBuffer, true);
+    m_pIDArray->AppendNew<CPDF_String>(
+        pdfium::as_bytes(pdfium::make_span(file_id)),
+        CPDF_String::DataType::kIsHex);
   }
 
   if (pOldIDArray) {
@@ -581,9 +582,11 @@
       m_pIDArray->Append(pID2->Clone());
       return;
     }
-    ByteString bsBuffer =
+    std::array<uint32_t, 4> file_id =
         GenerateFileID((uint32_t)(uintptr_t)this, m_dwLastObjNum);
-    m_pIDArray->AppendNew<CPDF_String>(bsBuffer, true);
+    m_pIDArray->AppendNew<CPDF_String>(
+        pdfium::as_bytes(pdfium::make_span(file_id)),
+        CPDF_String::DataType::kIsHex);
     return;
   }
 
diff --git a/core/fpdfapi/page/cpdf_image.cpp b/core/fpdfapi/page/cpdf_image.cpp
index a5e5552..35361a6 100644
--- a/core/fpdfapi/page/cpdf_image.cpp
+++ b/core/fpdfapi/page/cpdf_image.cpp
@@ -4,16 +4,12 @@
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
-#if defined(UNSAFE_BUFFERS_BUILD)
-// TODO(crbug.com/pdfium/2154): resolve buffer safety issues.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "core/fpdfapi/page/cpdf_image.h"
 
 #include <stdint.h>
 
 #include <algorithm>
+#include <array>
 #include <memory>
 #include <utility>
 
@@ -224,19 +220,11 @@
       pCS->AppendNew<CPDF_Name>("Indexed");
       pCS->AppendNew<CPDF_Name>("DeviceRGB");
       pCS->AppendNew<CPDF_Number>(1);
-      ByteString ct;
-      {
-        // Span's lifetime must end before ReleaseBuffer() below.
-        pdfium::span<char> pBuf = ct.GetBuffer(6);
-        pBuf[0] = static_cast<char>(reset_r);
-        pBuf[1] = static_cast<char>(reset_g);
-        pBuf[2] = static_cast<char>(reset_b);
-        pBuf[3] = static_cast<char>(set_r);
-        pBuf[4] = static_cast<char>(set_g);
-        pBuf[5] = static_cast<char>(set_b);
-      }
-      ct.ReleaseBuffer(6);
-      pCS->AppendNew<CPDF_String>(ct, true);
+      uint8_t ct[6] = {
+          static_cast<uint8_t>(reset_r), static_cast<uint8_t>(reset_g),
+          static_cast<uint8_t>(reset_b), static_cast<uint8_t>(set_r),
+          static_cast<uint8_t>(set_g),   static_cast<uint8_t>(set_b)};
+      pCS->AppendNew<CPDF_String>(ct, CPDF_String::DataType::kIsHex);
     }
     pDict->SetNewFor<CPDF_Number>("BitsPerComponent", 1);
     dest_pitch = (BitmapWidth + 7) / 8;
@@ -317,11 +305,13 @@
       uint8_t* dest_ptr = dest_span.data();
       const uint8_t* src_ptr = src_span.data();
       for (int32_t column = 0; column < BitmapWidth; column++) {
-        dest_ptr[0] = src_ptr[2];
-        dest_ptr[1] = src_ptr[1];
-        dest_ptr[2] = src_ptr[0];
-        dest_ptr += 3;
-        src_ptr += src_step;
+        UNSAFE_TODO({
+          dest_ptr[0] = src_ptr[2];
+          dest_ptr[1] = src_ptr[1];
+          dest_ptr[2] = src_ptr[0];
+          dest_ptr += 3;
+          src_ptr += src_step;
+        })
       }
       dest_span = dest_span.subspan(dest_pitch);
       src_span = src_span.subspan(src_pitch);
diff --git a/core/fpdfapi/page/cpdf_streamparser.cpp b/core/fpdfapi/page/cpdf_streamparser.cpp
index ec003a5..442ec80 100644
--- a/core/fpdfapi/page/cpdf_streamparser.cpp
+++ b/core/fpdfapi/page/cpdf_streamparser.cpp
@@ -327,13 +327,13 @@
   }
 
   if (first_char == '(') {
-    ByteString str = ReadString();
-    return pdfium::MakeRetain<CPDF_String>(m_pPool, str);
+    return pdfium::MakeRetain<CPDF_String>(m_pPool, ReadString());
   }
 
   if (first_char == '<') {
     if (m_WordSize == 1) {
-      return pdfium::MakeRetain<CPDF_String>(m_pPool, ReadHexString(), true);
+      return pdfium::MakeRetain<CPDF_String>(m_pPool, ReadHexString(),
+                                             CPDF_String::DataType::kIsHex);
     }
 
     auto pDict = pdfium::MakeRetain<CPDF_Dictionary>(m_pPool);
@@ -552,13 +552,15 @@
   }
 }
 
-ByteString CPDF_StreamParser::ReadHexString() {
-  if (!PositionIsInBounds())
-    return ByteString();
+DataVector<uint8_t> CPDF_StreamParser::ReadHexString() {
+  if (!PositionIsInBounds()) {
+    return DataVector<uint8_t>();
+  }
 
-  ByteString buf;
+  // TODO(thestig): Deduplicate CPDF_SyntaxParser::ReadHexString()?
+  DataVector<uint8_t> buf;
   bool bFirst = true;
-  int code = 0;
+  uint8_t code = 0;
   while (PositionIsInBounds()) {
     uint8_t ch = m_pBuf[m_Pos++];
     if (ch == '>')
@@ -572,14 +574,18 @@
       code = val * 16;
     } else {
       code += val;
-      buf += static_cast<uint8_t>(code);
+      buf.push_back(code);
     }
     bFirst = !bFirst;
   }
-  if (!bFirst)
-    buf += static_cast<char>(code);
+  if (!bFirst) {
+    buf.push_back(code);
+  }
 
-  return buf.First(std::min<size_t>(buf.GetLength(), kMaxStringLength));
+  if (buf.size() > kMaxStringLength) {
+    buf.resize(kMaxStringLength);
+  }
+  return buf;
 }
 
 bool CPDF_StreamParser::PositionIsInBounds() const {
diff --git a/core/fpdfapi/page/cpdf_streamparser.h b/core/fpdfapi/page/cpdf_streamparser.h
index 50f7d65..b6ad7d7 100644
--- a/core/fpdfapi/page/cpdf_streamparser.h
+++ b/core/fpdfapi/page/cpdf_streamparser.h
@@ -7,6 +7,9 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_STREAMPARSER_H_
 #define CORE_FPDFAPI_PAGE_CPDF_STREAMPARSER_H_
 
+#include <stdint.h>
+
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/raw_span.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/span.h"
@@ -47,7 +50,7 @@
 
   void GetNextWord(bool& bIsNumber);
   ByteString ReadString();
-  ByteString ReadHexString();
+  DataVector<uint8_t> ReadHexString();
   bool PositionIsInBounds() const;
 
   uint32_t m_Pos = 0;       // Current byte position within |m_pBuf|.
diff --git a/core/fpdfapi/page/cpdf_streamparser_unittest.cpp b/core/fpdfapi/page/cpdf_streamparser_unittest.cpp
index c9af44b..f38cd47 100644
--- a/core/fpdfapi/page/cpdf_streamparser_unittest.cpp
+++ b/core/fpdfapi/page/cpdf_streamparser_unittest.cpp
@@ -3,22 +3,26 @@
 // found in the LICENSE file.
 
 #include "core/fpdfapi/page/cpdf_streamparser.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::ElementsAre;
+using testing::IsEmpty;
+
 TEST(cpdf_streamparser, ReadHexString) {
   {
     // Position out of bounds.
     uint8_t data[] = "12ab>";
     CPDF_StreamParser parser(data);
     parser.SetPos(6);
-    EXPECT_EQ("", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), IsEmpty());
   }
 
   {
     // Regular conversion.
     uint8_t data[] = "1A2b>abcd";
     CPDF_StreamParser parser(data);
-    EXPECT_EQ("\x1a\x2b", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), ElementsAre(0x1a, 0x2b));
     EXPECT_EQ(5u, parser.GetPos());
   }
 
@@ -26,7 +30,7 @@
     // Missing ending >
     uint8_t data[] = "1A2b";
     CPDF_StreamParser parser(data);
-    EXPECT_EQ("\x1a\x2b", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), ElementsAre(0x1a, 0x2b));
     EXPECT_EQ(5u, parser.GetPos());
   }
 
@@ -34,14 +38,14 @@
     // Uneven number of bytes.
     uint8_t data[] = "1A2>asdf";
     CPDF_StreamParser parser(data);
-    EXPECT_EQ("\x1a\x20", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), ElementsAre(0x1a, 0x20));
     EXPECT_EQ(4u, parser.GetPos());
   }
 
   {
     uint8_t data[] = ">";
     CPDF_StreamParser parser(data);
-    EXPECT_EQ("", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), IsEmpty());
     EXPECT_EQ(1u, parser.GetPos());
   }
 }
diff --git a/core/fpdfapi/parser/cpdf_string.cpp b/core/fpdfapi/parser/cpdf_string.cpp
index ca0dd53..ab56541 100644
--- a/core/fpdfapi/parser/cpdf_string.cpp
+++ b/core/fpdfapi/parser/cpdf_string.cpp
@@ -17,21 +17,25 @@
 
 CPDF_String::CPDF_String() = default;
 
-CPDF_String::CPDF_String(WeakPtr<ByteStringPool> pPool, const ByteString& str)
-    : CPDF_String(pPool, str, false) {}
-
 CPDF_String::CPDF_String(WeakPtr<ByteStringPool> pPool,
-                         const ByteString& str,
-                         bool bHex)
-    : m_String(str), m_bHex(bHex) {
-  if (pPool)
+                         pdfium::span<const uint8_t> data,
+                         DataType is_hex)
+    : m_String(ByteStringView(data)), m_bHex(true) {
+  if (pPool) {
     m_String = pPool->Intern(m_String);
+  }
+}
+
+CPDF_String::CPDF_String(WeakPtr<ByteStringPool> pPool, const ByteString& str)
+    : m_String(str) {
+  if (pPool) {
+    m_String = pPool->Intern(m_String);
+  }
 }
 
 CPDF_String::CPDF_String(WeakPtr<ByteStringPool> pPool, WideStringView str)
-    : m_String(PDF_EncodeText(str)) {
-  if (pPool)
-    m_String = pPool->Intern(m_String);
+    : CPDF_String(pPool, PDF_EncodeText(str)) {
+  // Delegates to ctor above.
 }
 
 CPDF_String::~CPDF_String() = default;
diff --git a/core/fpdfapi/parser/cpdf_string.h b/core/fpdfapi/parser/cpdf_string.h
index 3b70d37..aa6af77 100644
--- a/core/fpdfapi/parser/cpdf_string.h
+++ b/core/fpdfapi/parser/cpdf_string.h
@@ -10,6 +10,7 @@
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
+#include "core/fxcrt/span.h"
 #include "core/fxcrt/string_pool_template.h"
 #include "core/fxcrt/weak_ptr.h"
 
@@ -17,6 +18,9 @@
  public:
   CONSTRUCT_VIA_MAKE_RETAIN;
 
+  // Used as a placeholder to differentiate constructors.
+  enum class DataType { kIsHex };
+
   // CPDF_Object:
   Type GetType() const override;
   RetainPtr<CPDF_Object> Clone() const override;
@@ -32,9 +36,10 @@
 
  private:
   CPDF_String();
-  // Same as the 3-param ctor, with `bHex` set to false.
+  CPDF_String(WeakPtr<ByteStringPool> pPool,
+              pdfium::span<const uint8_t> data,
+              DataType is_hex);
   CPDF_String(WeakPtr<ByteStringPool> pPool, const ByteString& str);
-  CPDF_String(WeakPtr<ByteStringPool> pPool, const ByteString& str, bool bHex);
   CPDF_String(WeakPtr<ByteStringPool> pPool, WideStringView str);
   ~CPDF_String() override;
 
diff --git a/core/fpdfapi/parser/cpdf_syntax_parser.cpp b/core/fpdfapi/parser/cpdf_syntax_parser.cpp
index bfddc14..4cdd669 100644
--- a/core/fpdfapi/parser/cpdf_syntax_parser.cpp
+++ b/core/fpdfapi/parser/cpdf_syntax_parser.cpp
@@ -28,6 +28,7 @@
 #include "core/fxcrt/check.h"
 #include "core/fxcrt/check_op.h"
 #include "core/fxcrt/compiler_specific.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fixed_size_data_vector.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_memcpy_wrappers.h"
@@ -321,12 +322,13 @@
   return buf;
 }
 
-ByteString CPDF_SyntaxParser::ReadHexString() {
+DataVector<uint8_t> CPDF_SyntaxParser::ReadHexString() {
   uint8_t ch;
-  if (!GetNextChar(ch))
-    return ByteString();
+  if (!GetNextChar(ch)) {
+    return DataVector<uint8_t>();
+  }
 
-  ByteString buf;
+  DataVector<uint8_t> buf;
   bool bFirst = true;
   uint8_t code = 0;
   while (true) {
@@ -339,16 +341,18 @@
         code = val * 16;
       } else {
         code += val;
-        buf += static_cast<char>(code);
+        buf.push_back(code);
       }
       bFirst = !bFirst;
     }
 
-    if (!GetNextChar(ch))
+    if (!GetNextChar(ch)) {
       break;
+    }
   }
-  if (!bFirst)
-    buf += static_cast<char>(code);
+  if (!bFirst) {
+    buf.push_back(code);
+  }
 
   return buf;
 }
@@ -532,12 +536,11 @@
     return pdfium::MakeRetain<CPDF_Null>();
 
   if (word == "(") {
-    ByteString str = ReadString();
-    return pdfium::MakeRetain<CPDF_String>(m_pPool, str);
+    return pdfium::MakeRetain<CPDF_String>(m_pPool, ReadString());
   }
   if (word == "<") {
-    ByteString str = ReadHexString();
-    return pdfium::MakeRetain<CPDF_String>(m_pPool, str, true);
+    return pdfium::MakeRetain<CPDF_String>(m_pPool, ReadHexString(),
+                                           CPDF_String::DataType::kIsHex);
   }
   if (word == "[") {
     auto pArray = pdfium::MakeRetain<CPDF_Array>();
diff --git a/core/fpdfapi/parser/cpdf_syntax_parser.h b/core/fpdfapi/parser/cpdf_syntax_parser.h
index a7fc3f2..42fbb14 100644
--- a/core/fpdfapi/parser/cpdf_syntax_parser.h
+++ b/core/fpdfapi/parser/cpdf_syntax_parser.h
@@ -82,7 +82,7 @@
   FX_FILESIZE GetDocumentSize() const;
 
   ByteString ReadString();
-  ByteString ReadHexString();
+  DataVector<uint8_t> ReadHexString();
 
   void SetTrailerEnds(std::vector<unsigned int>* trailer_ends) {
     m_TrailerEnds = trailer_ends;
diff --git a/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp b/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp
index 94e573c..beb3fb7 100644
--- a/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp
@@ -14,16 +14,20 @@
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 #include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "core/fxcrt/fx_extension.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/utils/path_service.h"
 
+using testing::ElementsAre;
+using testing::IsEmpty;
+
 TEST(SyntaxParserTest, ReadHexString) {
   {
     // Empty string.
     static const uint8_t data[] = "";
     CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
         pdfium::make_span(data, 0u)));
-    EXPECT_EQ("", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), IsEmpty());
     EXPECT_EQ(0, parser.GetPos());
   }
 
@@ -32,7 +36,7 @@
     static const uint8_t data[] = "  ";
     CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
         pdfium::make_span(data, 2u)));
-    EXPECT_EQ("", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), IsEmpty());
     EXPECT_EQ(2, parser.GetPos());
   }
 
@@ -41,7 +45,7 @@
     static const uint8_t data[] = "z12b";
     CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
         pdfium::make_span(data, 4u)));
-    EXPECT_EQ("\x12\xb0", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), ElementsAre(0x12, 0xb0));
     EXPECT_EQ(4, parser.GetPos());
   }
 
@@ -50,7 +54,7 @@
     static const uint8_t data[] = "*<&*#$^&@1";
     CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
         pdfium::make_span(data, 10u)));
-    EXPECT_EQ("\x10", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), ElementsAre(0x10));
     EXPECT_EQ(10, parser.GetPos());
   }
 
@@ -59,7 +63,7 @@
     static const uint8_t data[] = "\x80zab";
     CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
         pdfium::make_span(data, 4u)));
-    EXPECT_EQ("\xab", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), ElementsAre(0xab));
     EXPECT_EQ(4, parser.GetPos());
   }
 
@@ -68,7 +72,7 @@
     static const uint8_t data[] = "\xffzab";
     CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
         pdfium::make_span(data, 4u)));
-    EXPECT_EQ("\xab", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), ElementsAre(0xab));
     EXPECT_EQ(4, parser.GetPos());
   }
 
@@ -77,7 +81,7 @@
     static const uint8_t data[] = "1A2b>abcd";
     CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
         pdfium::make_span(data, 9u)));
-    EXPECT_EQ("\x1a\x2b", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), ElementsAre(0x1a, 0x2b));
     EXPECT_EQ(5, parser.GetPos());
   }
 
@@ -87,17 +91,17 @@
     CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
         pdfium::make_span(data, 5u)));
     parser.SetPos(5);
-    EXPECT_EQ("", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), IsEmpty());
 
     parser.SetPos(6);
-    EXPECT_EQ("", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), IsEmpty());
 
     parser.SetPos(std::numeric_limits<FX_FILESIZE>::max());
-    EXPECT_EQ("", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), IsEmpty());
 
     // Check string still parses when set to 0.
     parser.SetPos(0);
-    EXPECT_EQ("\x12\xab", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), ElementsAre(0x12, 0xab));
   }
 
   {
@@ -105,7 +109,7 @@
     static const uint8_t data[] = "1A2b";
     CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
         pdfium::make_span(data, 4u)));
-    EXPECT_EQ("\x1a\x2b", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), ElementsAre(0x1a, 0x2b));
     EXPECT_EQ(4, parser.GetPos());
   }
 
@@ -114,7 +118,7 @@
     static const uint8_t data[] = "12abz";
     CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
         pdfium::make_span(data, 5u)));
-    EXPECT_EQ("\x12\xab", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), ElementsAre(0x12, 0xab));
     EXPECT_EQ(5, parser.GetPos());
   }
 
@@ -123,7 +127,7 @@
     static const uint8_t data[] = "1A2>asdf";
     CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
         pdfium::make_span(data, 8u)));
-    EXPECT_EQ("\x1a\x20", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), ElementsAre(0x1a, 0x20));
     EXPECT_EQ(4, parser.GetPos());
   }
 
@@ -132,7 +136,7 @@
     static const uint8_t data[] = "1A2zasdf";
     CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
         pdfium::make_span(data, 8u)));
-    EXPECT_EQ("\x1a\x2a\xdf", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), ElementsAre(0x1a, 0x2a, 0xdf));
     EXPECT_EQ(8, parser.GetPos());
   }
 
@@ -141,7 +145,7 @@
     const char gt = '>';
     CPDF_SyntaxParser parser(pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
         pdfium::byte_span_from_ref(gt)));
-    EXPECT_EQ("", parser.ReadHexString());
+    EXPECT_THAT(parser.ReadHexString(), IsEmpty());
     EXPECT_EQ(1, parser.GetPos());
   }
 }
diff --git a/core/fpdfdoc/cpdf_nametree_unittest.cpp b/core/fpdfdoc/cpdf_nametree_unittest.cpp
index 413c914..0861f10 100644
--- a/core/fpdfdoc/cpdf_nametree_unittest.cpp
+++ b/core/fpdfdoc/cpdf_nametree_unittest.cpp
@@ -107,8 +107,8 @@
   auto pNames = pRootDict->SetNewFor<CPDF_Array>("Names");
 
   // Add the key "1" (with BOM) and value 100 into the array.
-  constexpr char kData[] = "\xFE\xFF\x00\x31";
-  pNames->AppendNew<CPDF_String>(ByteString(kData, sizeof(kData) - 1), true);
+  constexpr uint8_t kData[] = {0xFE, 0xFF, 0x00, 0x31};
+  pNames->AppendNew<CPDF_String>(kData, CPDF_String::DataType::kIsHex);
   pNames->AppendNew<CPDF_Number>(100);
 
   // Check that the key is as expected.
diff --git a/fpdfsdk/fpdf_attachment.cpp b/fpdfsdk/fpdf_attachment.cpp
index 89b71f9..ccc2154 100644
--- a/fpdfsdk/fpdf_attachment.cpp
+++ b/fpdfsdk/fpdf_attachment.cpp
@@ -6,6 +6,7 @@
 
 #include <limits.h>
 
+#include <array>
 #include <memory>
 #include <utility>
 
@@ -33,11 +34,6 @@
 
 constexpr char kChecksumKey[] = "CheckSum";
 
-ByteString CFXByteStringHexDecode(const ByteString& bsHex) {
-  DataAndBytesConsumed result = HexDecode(bsHex.unsigned_span());
-  return ByteString(ByteStringView(result.data));
-}
-
 }  // namespace
 
 FPDF_EXPORT int FPDF_CALLCONV
@@ -162,11 +158,13 @@
   // SAFETY: required from caller.
   ByteString bsValue = UNSAFE_BUFFERS(ByteStringFromFPDFWideString(value));
   ByteString bsKey = key;
-  bool bEncodedAsHex = bsKey == kChecksumKey;
-  if (bEncodedAsHex) {
-    bsValue = CFXByteStringHexDecode(bsValue);
+  if (bsKey == kChecksumKey) {
+    pParamsDict->SetNewFor<CPDF_String>(bsKey,
+                                        HexDecode(bsValue.unsigned_span()).data,
+                                        CPDF_String::DataType::kIsHex);
+  } else {
+    pParamsDict->SetNewFor<CPDF_String>(bsKey, bsValue);
   }
-  pParamsDict->SetNewFor<CPDF_String>(bsKey, bsValue, bEncodedAsHex);
   return true;
 }
 
@@ -239,15 +237,12 @@
   pdfium::span<const uint8_t> contents_span = UNSAFE_BUFFERS(
       pdfium::make_span(static_cast<const uint8_t*>(contents), len));
 
-  ByteString digest;
-  {
-    auto digest_span = pdfium::as_writable_bytes(digest.GetBuffer(16));
-    CRYPT_MD5Generate(contents_span, digest_span.data());
-    digest.ReleaseBuffer(16);
-  }
+  std::array<uint8_t, 16> digest;
+  CRYPT_MD5Generate(contents_span, digest.data());
 
   // Set the checksum of the new attachment in the dictionary.
-  pParamsDict->SetNewFor<CPDF_String>(kChecksumKey, digest, /*bHex=*/true);
+  pParamsDict->SetNewFor<CPDF_String>(kChecksumKey, digest,
+                                      CPDF_String::DataType::kIsHex);
 
   // Create the file stream and have the filespec dictionary link to it.
   auto pFileStream = pDoc->NewIndirect<CPDF_Stream>(
diff --git a/fpdfsdk/fpdf_editpage.cpp b/fpdfsdk/fpdf_editpage.cpp
index 49e623a..9ddd89f 100644
--- a/fpdfsdk/fpdf_editpage.cpp
+++ b/fpdfsdk/fpdf_editpage.cpp
@@ -571,20 +571,27 @@
                              FPDF_BYTESTRING key,
                              void* value,
                              unsigned long value_len) {
-  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
-  if (!pPageObj || !PageObjectContainsMark(pPageObj, mark))
+  if (!value && value_len > 0) {
     return false;
+  }
+
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  if (!pPageObj || !PageObjectContainsMark(pPageObj, mark)) {
+    return false;
+  }
 
   RetainPtr<CPDF_Dictionary> pParams =
       GetOrCreateMarkParamsDict(document, mark);
-  if (!pParams)
+  if (!pParams) {
     return false;
+  }
 
-  if (!value && value_len > 0)
-    return false;
-
+  // SAFETY: required from caller.
   pParams->SetNewFor<CPDF_String>(
-      key, ByteString(static_cast<const char*>(value), value_len), true);
+      key,
+      UNSAFE_BUFFERS(
+          pdfium::make_span(static_cast<const uint8_t*>(value), value_len)),
+      CPDF_String::DataType::kIsHex);
   pPageObj->SetDirty(true);
   return true;
 }