Allow ostringsstreams to use same partition storage as ByteString.

Avoid overlapping easy-to-control streams with other kinds of
objects.

Change-Id: I977b837a6b18773289613ffb64a3e6b23b43d729
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/89171
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn
index a5c10fd..ffa1b60 100644
--- a/core/fxcrt/BUILD.gn
+++ b/core/fxcrt/BUILD.gn
@@ -175,6 +175,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/fx_memory_wrappers.h b/core/fxcrt/fx_memory_wrappers.h
index d7ccd89..d56b9de 100644
--- a/core/fxcrt/fx_memory_wrappers.h
+++ b/core/fxcrt/fx_memory_wrappers.h
@@ -73,4 +73,55 @@
   bool operator!=(const FxAllocAllocator& that) { return false; }
 };
 
+// Used to put backing store for std::string and std::ostringstream
+// into the string partition.
+// TODO(tsepez): de-duplicate with above if decide to keep this.
+template <class T>
+struct FxStringAllocator {
+ public:
+  using value_type = T;
+  using pointer = T*;
+  using const_pointer = const T*;
+  using reference = T&;
+  using const_reference = const T&;
+  using size_type = size_t;
+  using difference_type = ptrdiff_t;
+
+  template <class U>
+  struct rebind {
+    using other = FxStringAllocator<U>;
+  };
+
+  FxStringAllocator() noexcept = default;
+  FxStringAllocator(const FxStringAllocator& other) noexcept = default;
+  ~FxStringAllocator() = default;
+
+  template <typename U>
+  FxStringAllocator(const FxStringAllocator<U>& other) noexcept {}
+
+  pointer address(reference x) const noexcept { return &x; }
+  const_pointer address(const_reference x) const noexcept { return &x; }
+  pointer allocate(size_type n, const void* hint = 0) {
+    return FX_StringAlloc(value_type, n);
+  }
+  void deallocate(pointer p, size_type n) { FX_Free(p); }
+  size_type max_size() const noexcept {
+    return std::numeric_limits<size_type>::max() / sizeof(value_type);
+  }
+
+  template <class U, class... Args>
+  void construct(U* p, Args&&... args) {
+    new (reinterpret_cast<void*>(p)) U(std::forward<Args>(args)...);
+  }
+
+  template <class U>
+  void destroy(U* p) {
+    p->~U();
+  }
+
+  // There's no state, so they are all the same,
+  bool operator==(const FxStringAllocator& that) { return true; }
+  bool operator!=(const FxStringAllocator& that) { return false; }
+};
+
 #endif  // CORE_FXCRT_FX_MEMORY_WRAPPERS_H_
diff --git a/core/fxcrt/fx_memory_wrappers_unittest.cpp b/core/fxcrt/fx_memory_wrappers_unittest.cpp
index 0927683..f9bee32 100644
--- a/core/fxcrt/fx_memory_wrappers_unittest.cpp
+++ b/core/fxcrt/fx_memory_wrappers_unittest.cpp
@@ -5,6 +5,8 @@
 #include "core/fxcrt/fx_memory_wrappers.h"
 
 #include <memory>
+#include <sstream>
+#include <string>
 #include <vector>
 
 #include "build/build_config.h"
@@ -20,6 +22,7 @@
 }
 
 TEST(fxcrt, FxAllocAllocator) {
+  // Let ASAN sanity check some simple operations.
   std::vector<int, FxAllocAllocator<int>> vec;
   vec.push_back(42);
   vec.reserve(100);
@@ -31,3 +34,15 @@
   vec2.resize(0);
   vec2.push_back(42);
 }
+
+TEST(fxcrt, FxStringAllocator) {
+  // Let ASAN sanity check some simple operations.
+  std::basic_ostringstream<char, std::char_traits<char>,
+                           FxStringAllocator<char>>
+      str;
+  str << 'B';
+  str << "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+  str << "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+  str << "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+  str << 42.0f;
+}
diff --git a/core/fxcrt/stl_util.h b/core/fxcrt/stl_util.h
index 37156af..f4b0bdf 100644
--- a/core/fxcrt/stl_util.h
+++ b/core/fxcrt/stl_util.h
@@ -5,9 +5,12 @@
 #ifndef CORE_FXCRT_STL_UTIL_H_
 #define CORE_FXCRT_STL_UTIL_H_
 
+#include <iosfwd>
 #include <memory>
+#include <string>
 #include <vector>
 
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "third_party/base/numerics/safe_conversions.h"
 #include "third_party/base/numerics/safe_math.h"
 
@@ -44,6 +47,10 @@
   return std::vector<T, A>(safe_size.ValueOrDie());
 }
 
+// Stringstream that uses PartitionAlloc for backing store.
+using ostringstream = std::
+    basic_ostringstream<char, std::char_traits<char>, FxStringAllocator<char>>;
+
 }  // 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..33b80f2
--- /dev/null
+++ b/core/fxcrt/stl_util_unittest.cpp
@@ -0,0 +1,13 @@
+// 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/stl_util.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(STLUtil, OStringStream) {
+  fxcrt::ostringstream str;
+  str << 22 << "C is " << 71.6f << "F";
+  EXPECT_STREQ("22C is 71.6F", str.str().c_str());
+}