diff options
-rw-r--r-- | BUILD.gn | 2 | ||||
-rw-r--r-- | core/fpdfapi/parser/cfdf_document.cpp | 4 | ||||
-rw-r--r-- | core/fpdfapi/parser/cpdf_data_avail.cpp | 17 | ||||
-rw-r--r-- | core/fpdfapi/parser/cpdf_parser.cpp | 10 | ||||
-rw-r--r-- | core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp | 29 | ||||
-rw-r--r-- | core/fxcrt/cfx_memorystream.cpp | 199 | ||||
-rw-r--r-- | core/fxcrt/cfx_memorystream.h | 56 | ||||
-rw-r--r-- | core/fxcrt/cfx_seekablestreamproxy.cpp | 5 | ||||
-rw-r--r-- | core/fxcrt/fx_stream.cpp | 232 | ||||
-rw-r--r-- | core/fxcrt/fx_stream.h | 16 | ||||
-rw-r--r-- | core/fxcrt/xml/cfx_saxreader_unittest.cpp | 10 | ||||
-rw-r--r-- | fpdfsdk/fpdfsave.cpp | 12 | ||||
-rw-r--r-- | testing/libfuzzer/pdf_cfx_saxreader_fuzzer.cc | 7 | ||||
-rw-r--r-- | xfa/fgas/font/cfgas_fontmgr.cpp | 4 | ||||
-rw-r--r-- | xfa/fxfa/cxfa_ffdoc.cpp | 6 | ||||
-rw-r--r-- | xfa/fxfa/cxfa_ffwidget.cpp | 9 | ||||
-rw-r--r-- | xfa/fxfa/parser/cxfa_dataexporter.cpp | 4 | ||||
-rw-r--r-- | xfa/fxfa/parser/cxfa_node.cpp | 4 |
18 files changed, 328 insertions, 298 deletions
@@ -753,6 +753,8 @@ static_library("fxcrt") { "core/fxcrt/cfx_bytestring.cpp", "core/fxcrt/cfx_bytestring.h", "core/fxcrt/cfx_maybe_owned.h", + "core/fxcrt/cfx_memorystream.cpp", + "core/fxcrt/cfx_memorystream.h", "core/fxcrt/cfx_observable.h", "core/fxcrt/cfx_retain_ptr.h", "core/fxcrt/cfx_shared_copy_on_write.h", diff --git a/core/fpdfapi/parser/cfdf_document.cpp b/core/fpdfapi/parser/cfdf_document.cpp index 03cc655a0a..85e2f6ef04 100644 --- a/core/fpdfapi/parser/cfdf_document.cpp +++ b/core/fpdfapi/parser/cfdf_document.cpp @@ -13,6 +13,7 @@ #include "core/fpdfapi/parser/cpdf_dictionary.h" #include "core/fpdfapi/parser/cpdf_syntax_parser.h" #include "core/fpdfapi/parser/fpdf_parser_utility.h" +#include "core/fxcrt/cfx_memorystream.h" #include "third_party/base/ptr_util.h" CFDF_Document::CFDF_Document() @@ -39,7 +40,8 @@ std::unique_ptr<CFDF_Document> CFDF_Document::ParseFile( std::unique_ptr<CFDF_Document> CFDF_Document::ParseMemory(uint8_t* pData, uint32_t size) { - return CFDF_Document::ParseFile(IFX_MemoryStream::Create(pData, size)); + return CFDF_Document::ParseFile( + pdfium::MakeRetain<CFX_MemoryStream>(pData, size, false)); } void CFDF_Document::ParseStream( diff --git a/core/fpdfapi/parser/cpdf_data_avail.cpp b/core/fpdfapi/parser/cpdf_data_avail.cpp index f99cb467e5..41a3f0dae2 100644 --- a/core/fpdfapi/parser/cpdf_data_avail.cpp +++ b/core/fpdfapi/parser/cpdf_data_avail.cpp @@ -21,6 +21,7 @@ #include "core/fpdfapi/parser/cpdf_reference.h" #include "core/fpdfapi/parser/cpdf_stream.h" #include "core/fpdfapi/parser/fpdf_parser_utility.h" +#include "core/fxcrt/cfx_memorystream.h" #include "core/fxcrt/fx_extension.h" #include "core/fxcrt/fx_safe_types.h" #include "third_party/base/numerics/safe_conversions.h" @@ -716,8 +717,8 @@ bool CPDF_DataAvail::IsLinearizedFile(uint8_t* pData, uint32_t dwLen) { if (m_pLinearized) return true; - CFX_RetainPtr<IFX_MemoryStream> file = - IFX_MemoryStream::Create(pData, (size_t)dwLen, false); + auto file = pdfium::MakeRetain<CFX_MemoryStream>( + pData, static_cast<size_t>(dwLen), false); int32_t offset = GetHeaderOffset(file); if (offset == -1) { m_docStatus = PDF_DATAAVAIL_ERROR; @@ -756,8 +757,8 @@ bool CPDF_DataAvail::CheckEnd(DownloadHints* pHints) { uint8_t buffer[1024]; m_pFileRead->ReadBlock(buffer, req_pos, dwSize); - CFX_RetainPtr<IFX_MemoryStream> file = - IFX_MemoryStream::Create(buffer, (size_t)dwSize, false); + auto file = pdfium::MakeRetain<CFX_MemoryStream>( + buffer, static_cast<size_t>(dwSize), false); m_syntaxParser.InitParser(file, 0); m_syntaxParser.RestorePos(dwSize - 1); @@ -804,8 +805,8 @@ int32_t CPDF_DataAvail::CheckCrossRefStream(DownloadHints* pHints, m_pFileRead->ReadBlock(pBuf, m_dwCurrentXRefSteam, iSize); - CFX_RetainPtr<IFX_MemoryStream> file = - IFX_MemoryStream::Create(pBuf, (size_t)iSize, false); + auto file = pdfium::MakeRetain<CFX_MemoryStream>( + pBuf, static_cast<size_t>(iSize), false); m_parser.m_pSyntax->InitParser(file, 0); bool bNumber; @@ -1049,8 +1050,8 @@ bool CPDF_DataAvail::CheckTrailer(DownloadHints* pHints) { if (!m_pFileRead->ReadBlock(pBuf, m_dwTrailerOffset, iSize)) return false; - CFX_RetainPtr<IFX_MemoryStream> file = - IFX_MemoryStream::Create(pBuf, (size_t)iSize, false); + auto file = pdfium::MakeRetain<CFX_MemoryStream>( + pBuf, static_cast<size_t>(iSize), false); m_syntaxParser.InitParser(file, 0); std::unique_ptr<CPDF_Object> pTrailer( diff --git a/core/fpdfapi/parser/cpdf_parser.cpp b/core/fpdfapi/parser/cpdf_parser.cpp index c18a183a33..86428ab9d7 100644 --- a/core/fpdfapi/parser/cpdf_parser.cpp +++ b/core/fpdfapi/parser/cpdf_parser.cpp @@ -22,6 +22,7 @@ #include "core/fpdfapi/parser/cpdf_stream_acc.h" #include "core/fpdfapi/parser/cpdf_syntax_parser.h" #include "core/fpdfapi/parser/fpdf_parser_utility.h" +#include "core/fxcrt/cfx_memorystream.h" #include "core/fxcrt/fx_extension.h" #include "core/fxcrt/fx_safe_types.h" #include "third_party/base/ptr_util.h" @@ -1114,8 +1115,9 @@ std::unique_ptr<CPDF_Object> CPDF_Parser::ParseIndirectObject( if (!pObjStream) return nullptr; - CFX_RetainPtr<IFX_MemoryStream> file = IFX_MemoryStream::Create( - (uint8_t*)pObjStream->GetData(), (size_t)pObjStream->GetSize(), false); + auto file = pdfium::MakeRetain<CFX_MemoryStream>( + const_cast<uint8_t*>(pObjStream->GetData()), + static_cast<size_t>(pObjStream->GetSize()), false); CPDF_SyntaxParser syntax; syntax.InitParser(file, 0); const int32_t offset = GetStreamFirst(pObjStream); @@ -1194,8 +1196,8 @@ void CPDF_Parser::GetIndirectBinary(uint32_t objnum, int32_t offset = GetStreamFirst(pObjStream); const uint8_t* pData = pObjStream->GetData(); uint32_t totalsize = pObjStream->GetSize(); - CFX_RetainPtr<IFX_MemoryStream> file = - IFX_MemoryStream::Create((uint8_t*)pData, (size_t)totalsize, false); + auto file = pdfium::MakeRetain<CFX_MemoryStream>( + const_cast<uint8_t*>(pData), static_cast<size_t>(totalsize), false); CPDF_SyntaxParser syntax; syntax.InitParser(file, 0); diff --git a/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp b/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp index 5c7aa6054b..249276f29a 100644 --- a/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp +++ b/core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp @@ -8,6 +8,7 @@ #include "core/fpdfapi/parser/cpdf_object.h" #include "core/fpdfapi/parser/cpdf_parser.h" #include "core/fpdfapi/parser/cpdf_syntax_parser.h" +#include "core/fxcrt/cfx_memorystream.h" #include "core/fxcrt/fx_extension.h" #include "core/fxcrt/fx_stream.h" #include "testing/gtest/include/gtest/gtest.h" @@ -18,7 +19,7 @@ TEST(cpdf_syntax_parser, ReadHexString) { // Empty string. uint8_t data[] = ""; CPDF_SyntaxParser parser; - parser.InitParser(IFX_MemoryStream::Create(data, 0, false), 0); + parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 0, false), 0); EXPECT_EQ("", parser.ReadHexString()); EXPECT_EQ(0, parser.SavePos()); } @@ -27,7 +28,7 @@ TEST(cpdf_syntax_parser, ReadHexString) { // Blank string. uint8_t data[] = " "; CPDF_SyntaxParser parser; - parser.InitParser(IFX_MemoryStream::Create(data, 2, false), 0); + parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 2, false), 0); EXPECT_EQ("", parser.ReadHexString()); EXPECT_EQ(2, parser.SavePos()); } @@ -36,7 +37,7 @@ TEST(cpdf_syntax_parser, ReadHexString) { // Skips unknown characters. uint8_t data[] = "z12b"; CPDF_SyntaxParser parser; - parser.InitParser(IFX_MemoryStream::Create(data, 4, false), 0); + parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 4, false), 0); EXPECT_EQ("\x12\xb0", parser.ReadHexString()); EXPECT_EQ(4, parser.SavePos()); } @@ -45,7 +46,7 @@ TEST(cpdf_syntax_parser, ReadHexString) { // Skips unknown characters. uint8_t data[] = "*<&*#$^&@1"; CPDF_SyntaxParser parser; - parser.InitParser(IFX_MemoryStream::Create(data, 10, false), 0); + parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 10, false), 0); EXPECT_EQ("\x10", parser.ReadHexString()); EXPECT_EQ(10, parser.SavePos()); } @@ -54,7 +55,7 @@ TEST(cpdf_syntax_parser, ReadHexString) { // Skips unknown characters. uint8_t data[] = "\x80zab"; CPDF_SyntaxParser parser; - parser.InitParser(IFX_MemoryStream::Create(data, 4, false), 0); + parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 4, false), 0); EXPECT_EQ("\xab", parser.ReadHexString()); EXPECT_EQ(4, parser.SavePos()); } @@ -63,7 +64,7 @@ TEST(cpdf_syntax_parser, ReadHexString) { // Skips unknown characters. uint8_t data[] = "\xffzab"; CPDF_SyntaxParser parser; - parser.InitParser(IFX_MemoryStream::Create(data, 4, false), 0); + parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 4, false), 0); EXPECT_EQ("\xab", parser.ReadHexString()); EXPECT_EQ(4, parser.SavePos()); } @@ -72,7 +73,7 @@ TEST(cpdf_syntax_parser, ReadHexString) { // Regular conversion. uint8_t data[] = "1A2b>abcd"; CPDF_SyntaxParser parser; - parser.InitParser(IFX_MemoryStream::Create(data, 9, false), 0); + parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 9, false), 0); EXPECT_EQ("\x1a\x2b", parser.ReadHexString()); EXPECT_EQ(5, parser.SavePos()); } @@ -81,7 +82,7 @@ TEST(cpdf_syntax_parser, ReadHexString) { // Position out of bounds. uint8_t data[] = "12ab>"; CPDF_SyntaxParser parser; - parser.InitParser(IFX_MemoryStream::Create(data, 5, false), 0); + parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 5, false), 0); parser.RestorePos(5); EXPECT_EQ("", parser.ReadHexString()); @@ -103,7 +104,7 @@ TEST(cpdf_syntax_parser, ReadHexString) { // Missing ending >. uint8_t data[] = "1A2b"; CPDF_SyntaxParser parser; - parser.InitParser(IFX_MemoryStream::Create(data, 4, false), 0); + parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 4, false), 0); EXPECT_EQ("\x1a\x2b", parser.ReadHexString()); EXPECT_EQ(4, parser.SavePos()); } @@ -112,7 +113,7 @@ TEST(cpdf_syntax_parser, ReadHexString) { // Missing ending >. uint8_t data[] = "12abz"; CPDF_SyntaxParser parser; - parser.InitParser(IFX_MemoryStream::Create(data, 5, false), 0); + parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 5, false), 0); EXPECT_EQ("\x12\xab", parser.ReadHexString()); EXPECT_EQ(5, parser.SavePos()); } @@ -121,7 +122,7 @@ TEST(cpdf_syntax_parser, ReadHexString) { // Uneven number of bytes. uint8_t data[] = "1A2>asdf"; CPDF_SyntaxParser parser; - parser.InitParser(IFX_MemoryStream::Create(data, 8, false), 0); + parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 8, false), 0); EXPECT_EQ("\x1a\x20", parser.ReadHexString()); EXPECT_EQ(4, parser.SavePos()); } @@ -130,7 +131,7 @@ TEST(cpdf_syntax_parser, ReadHexString) { // Uneven number of bytes. uint8_t data[] = "1A2zasdf"; CPDF_SyntaxParser parser; - parser.InitParser(IFX_MemoryStream::Create(data, 8, false), 0); + parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 8, false), 0); EXPECT_EQ("\x1a\x2a\xdf", parser.ReadHexString()); EXPECT_EQ(8, parser.SavePos()); } @@ -139,7 +140,7 @@ TEST(cpdf_syntax_parser, ReadHexString) { // Just ending character. uint8_t data[] = ">"; CPDF_SyntaxParser parser; - parser.InitParser(IFX_MemoryStream::Create(data, 1, false), 0); + parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 1, false), 0); EXPECT_EQ("", parser.ReadHexString()); EXPECT_EQ(1, parser.SavePos()); } @@ -149,7 +150,7 @@ TEST(cpdf_syntax_parser, GetInvalidReference) { CPDF_SyntaxParser parser; // Data with a reference with number CPDF_Object::kInvalidObjNum uint8_t data[] = "4294967295 0 R"; - parser.InitParser(IFX_MemoryStream::Create(data, 14, false), 0); + parser.InitParser(pdfium::MakeRetain<CFX_MemoryStream>(data, 14, false), 0); std::unique_ptr<CPDF_Object> ref = parser.GetObject(nullptr, CPDF_Object::kInvalidObjNum, 0, false); EXPECT_FALSE(ref); diff --git a/core/fxcrt/cfx_memorystream.cpp b/core/fxcrt/cfx_memorystream.cpp new file mode 100644 index 0000000000..345b381226 --- /dev/null +++ b/core/fxcrt/cfx_memorystream.cpp @@ -0,0 +1,199 @@ +// Copyright 2017 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. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "core/fxcrt/cfx_memorystream.h" + +#include <algorithm> + +#include "core/fxcrt/fx_safe_types.h" + +namespace { + +const int32_t kBlockSize = 64 * 1024; + +} // namespace + +CFX_MemoryStream::CFX_MemoryStream(bool bConsecutive) + : m_nTotalSize(0), + m_nCurSize(0), + m_nCurPos(0), + m_nGrowSize(kBlockSize), + m_dwFlags(Type::kTakeOver | (bConsecutive ? Type::kConsecutive : 0)) {} + +CFX_MemoryStream::CFX_MemoryStream(uint8_t* pBuffer, + size_t nSize, + bool bTakeOver) + : m_nTotalSize(nSize), + m_nCurSize(nSize), + m_nCurPos(0), + m_nGrowSize(kBlockSize), + m_dwFlags(Type::kConsecutive | (bTakeOver ? Type::kTakeOver : 0)) { + m_Blocks.push_back(pBuffer); +} + +CFX_MemoryStream::~CFX_MemoryStream() { + if (m_dwFlags & Type::kTakeOver) { + for (uint8_t* pBlock : m_Blocks) + FX_Free(pBlock); + } +} + +FX_FILESIZE CFX_MemoryStream::GetSize() { + return static_cast<FX_FILESIZE>(m_nCurSize); +} + +bool CFX_MemoryStream::IsEOF() { + return m_nCurPos >= static_cast<size_t>(GetSize()); +} + +FX_FILESIZE CFX_MemoryStream::GetPosition() { + return static_cast<FX_FILESIZE>(m_nCurPos); +} + +bool CFX_MemoryStream::Flush() { + return true; +} + +bool CFX_MemoryStream::ReadBlock(void* buffer, + FX_FILESIZE offset, + size_t size) { + if (!buffer || !size || offset < 0) + return false; + + FX_SAFE_SIZE_T newPos = size; + newPos += offset; + if (!newPos.IsValid() || newPos.ValueOrDefault(0) == 0 || + newPos.ValueOrDie() > m_nCurSize) { + return false; + } + + m_nCurPos = newPos.ValueOrDie(); + if (m_dwFlags & Type::kConsecutive) { + memcpy(buffer, m_Blocks[0] + static_cast<size_t>(offset), size); + return true; + } + + size_t nStartBlock = static_cast<size_t>(offset) / m_nGrowSize; + offset -= static_cast<FX_FILESIZE>(nStartBlock * m_nGrowSize); + while (size) { + size_t nRead = std::min(size, m_nGrowSize - static_cast<size_t>(offset)); + memcpy(buffer, m_Blocks[nStartBlock] + offset, nRead); + buffer = static_cast<uint8_t*>(buffer) + nRead; + size -= nRead; + ++nStartBlock; + offset = 0; + } + return true; +} + +size_t CFX_MemoryStream::ReadBlock(void* buffer, size_t size) { + if (m_nCurPos >= m_nCurSize) + return 0; + + size_t nRead = std::min(size, m_nCurSize - m_nCurPos); + if (!ReadBlock(buffer, static_cast<int32_t>(m_nCurPos), nRead)) + return 0; + + return nRead; +} + +bool CFX_MemoryStream::WriteBlock(const void* buffer, + FX_FILESIZE offset, + size_t size) { + if (!buffer || !size) + return false; + + if (m_dwFlags & Type::kConsecutive) { + FX_SAFE_SIZE_T newPos = size; + newPos += offset; + if (!newPos.IsValid()) + return false; + + m_nCurPos = newPos.ValueOrDie(); + if (m_nCurPos > m_nTotalSize) { + m_nTotalSize = (m_nCurPos + m_nGrowSize - 1) / m_nGrowSize * m_nGrowSize; + if (m_Blocks.empty()) + m_Blocks.push_back(FX_Alloc(uint8_t, m_nTotalSize)); + else + m_Blocks[0] = FX_Realloc(uint8_t, m_Blocks[0], m_nTotalSize); + } + + memcpy(m_Blocks[0] + offset, buffer, size); + m_nCurSize = std::max(m_nCurSize, m_nCurPos); + + return true; + } + + FX_SAFE_SIZE_T newPos = size; + newPos += offset; + if (!newPos.IsValid()) + return false; + if (!ExpandBlocks(newPos.ValueOrDie())) + return false; + + m_nCurPos = newPos.ValueOrDie(); + size_t nStartBlock = static_cast<size_t>(offset) / m_nGrowSize; + offset -= static_cast<FX_FILESIZE>(nStartBlock * m_nGrowSize); + while (size) { + size_t nWrite = std::min(size, m_nGrowSize - static_cast<size_t>(offset)); + memcpy(m_Blocks[nStartBlock] + offset, buffer, nWrite); + buffer = static_cast<const uint8_t*>(buffer) + nWrite; + size -= nWrite; + ++nStartBlock; + offset = 0; + } + return true; +} + +void CFX_MemoryStream::EstimateSize(size_t nInitSize, size_t nGrowSize) { + if (m_dwFlags & Type::kConsecutive) { + if (m_Blocks.empty()) { + m_Blocks.push_back( + FX_Alloc(uint8_t, std::max(nInitSize, static_cast<size_t>(4096)))); + } + m_nGrowSize = std::max(nGrowSize, static_cast<size_t>(4096)); + } else if (m_Blocks.empty()) { + m_nGrowSize = std::max(nGrowSize, static_cast<size_t>(4096)); + } +} + +void CFX_MemoryStream::AttachBuffer(uint8_t* pBuffer, size_t nSize) { + if (!(m_dwFlags & Type::kConsecutive)) + return; + + m_Blocks.clear(); + m_Blocks.push_back(pBuffer); + m_nTotalSize = nSize; + m_nCurSize = nSize; + m_nCurPos = 0; + m_dwFlags = Type::kConsecutive; +} + +void CFX_MemoryStream::DetachBuffer() { + if (!(m_dwFlags & Type::kConsecutive)) + return; + + m_Blocks.clear(); + m_nTotalSize = 0; + m_nCurSize = 0; + m_nCurPos = 0; + m_dwFlags = Type::kTakeOver; +} + +bool CFX_MemoryStream::ExpandBlocks(size_t size) { + m_nCurSize = std::max(m_nCurSize, size); + if (size <= m_nTotalSize) + return true; + + size = (size - m_nTotalSize + m_nGrowSize - 1) / m_nGrowSize; + size_t iCount = m_Blocks.size(); + m_Blocks.resize(iCount + size); + while (size--) { + m_Blocks[iCount++] = FX_Alloc(uint8_t, m_nGrowSize); + m_nTotalSize += m_nGrowSize; + } + return true; +} diff --git a/core/fxcrt/cfx_memorystream.h b/core/fxcrt/cfx_memorystream.h new file mode 100644 index 0000000000..2112280237 --- /dev/null +++ b/core/fxcrt/cfx_memorystream.h @@ -0,0 +1,56 @@ +// Copyright 2017 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. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#ifndef CORE_FXCRT_CFX_MEMORYSTREAM_H_ +#define CORE_FXCRT_CFX_MEMORYSTREAM_H_ + +#include <vector> + +#include "core/fxcrt/cfx_retain_ptr.h" +#include "core/fxcrt/fx_stream.h" + +class CFX_MemoryStream : public IFX_SeekableStream { + public: + enum Type { kConsecutive = 1 << 0, kTakeOver = 1 << 1 }; + + template <typename T, typename... Args> + friend CFX_RetainPtr<T> pdfium::MakeRetain(Args&&... args); + + // IFX_SeekableStream + FX_FILESIZE GetSize() override; + FX_FILESIZE GetPosition() override; + bool IsEOF() override; + bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override; + size_t ReadBlock(void* buffer, size_t size) override; + bool WriteBlock(const void* buffer, FX_FILESIZE offset, size_t size) override; + bool Flush() override; + + bool IsConsecutive() const { return !!(m_dwFlags & Type::kConsecutive); } + + uint8_t* GetBuffer() const { + return !m_Blocks.empty() ? m_Blocks.front() : nullptr; + } + + void EstimateSize(size_t nInitSize, size_t nGrowSize); + void AttachBuffer(uint8_t* pBuffer, size_t nSize); + void DetachBuffer(); + + private: + explicit CFX_MemoryStream(bool bConsecutive); + CFX_MemoryStream(uint8_t* pBuffer, size_t nSize, bool bTakeOver); + ~CFX_MemoryStream() override; + + bool ExpandBlocks(size_t size); + + std::vector<uint8_t*> m_Blocks; + size_t m_nTotalSize; + size_t m_nCurSize; + size_t m_nCurPos; + size_t m_nGrowSize; + uint32_t m_dwFlags; +}; + +#endif // CORE_FXCRT_CFX_MEMORYSTREAM_H_ diff --git a/core/fxcrt/cfx_seekablestreamproxy.cpp b/core/fxcrt/cfx_seekablestreamproxy.cpp index 8be2e0898f..622f56b3ec 100644 --- a/core/fxcrt/cfx_seekablestreamproxy.cpp +++ b/core/fxcrt/cfx_seekablestreamproxy.cpp @@ -17,6 +17,7 @@ #include <utility> #include <vector> +#include "core/fxcrt/cfx_memorystream.h" #include "core/fxcrt/fx_codepage.h" #include "core/fxcrt/fx_extension.h" #include "third_party/base/ptr_util.h" @@ -189,7 +190,9 @@ CFX_SeekableStreamProxy::CFX_SeekableStreamProxy( } CFX_SeekableStreamProxy::CFX_SeekableStreamProxy(uint8_t* data, FX_STRSIZE size) - : CFX_SeekableStreamProxy(IFX_MemoryStream::Create(data, size), false) {} + : CFX_SeekableStreamProxy( + pdfium::MakeRetain<CFX_MemoryStream>(data, size, false), + false) {} CFX_SeekableStreamProxy::~CFX_SeekableStreamProxy() {} diff --git a/core/fxcrt/fx_stream.cpp b/core/fxcrt/fx_stream.cpp index a0086f269e..2c451c67b8 100644 --- a/core/fxcrt/fx_stream.cpp +++ b/core/fxcrt/fx_stream.cpp @@ -14,10 +14,6 @@ #include "core/fxcrt/fx_safe_types.h" #include "core/fxcrt/ifxcrt_fileaccess.h" -#define FX_MEMSTREAM_BlockSize (64 * 1024) -#define FX_MEMSTREAM_Consecutive 0x01 -#define FX_MEMSTREAM_TakeOver 0x02 - namespace { class CFX_CRTFileStream final : public IFX_SeekableStream { @@ -50,222 +46,6 @@ class CFX_CRTFileStream final : public IFX_SeekableStream { std::unique_ptr<IFXCRT_FileAccess> m_pFile; }; -class CFX_MemoryStream final : public IFX_MemoryStream { - public: - template <typename T, typename... Args> - friend CFX_RetainPtr<T> pdfium::MakeRetain(Args&&... args); - - // IFX_MemoryStream - FX_FILESIZE GetSize() override { - return static_cast<FX_FILESIZE>(m_nCurSize); - } - bool IsEOF() override { return m_nCurPos >= static_cast<size_t>(GetSize()); } - FX_FILESIZE GetPosition() override { - return static_cast<FX_FILESIZE>(m_nCurPos); - } - bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override; - size_t ReadBlock(void* buffer, size_t size) override; - bool WriteBlock(const void* buffer, FX_FILESIZE offset, size_t size) override; - bool Flush() override { return true; } - bool IsConsecutive() const override { - return !!(m_dwFlags & FX_MEMSTREAM_Consecutive); - } - void EstimateSize(size_t nInitSize, size_t nGrowSize) override; - uint8_t* GetBuffer() const override { - return !m_Blocks.empty() ? m_Blocks.front() : nullptr; - } - void AttachBuffer(uint8_t* pBuffer, - size_t nSize, - bool bTakeOver = false) override; - void DetachBuffer() override; - - private: - explicit CFX_MemoryStream(bool bConsecutive); - CFX_MemoryStream(uint8_t* pBuffer, size_t nSize, bool bTakeOver); - ~CFX_MemoryStream() override; - - bool ExpandBlocks(size_t size); - - std::vector<uint8_t*> m_Blocks; - size_t m_nTotalSize; - size_t m_nCurSize; - size_t m_nCurPos; - size_t m_nGrowSize; - uint32_t m_dwFlags; -}; - -CFX_MemoryStream::CFX_MemoryStream(bool bConsecutive) - : m_nTotalSize(0), - m_nCurSize(0), - m_nCurPos(0), - m_nGrowSize(FX_MEMSTREAM_BlockSize) { - m_dwFlags = - FX_MEMSTREAM_TakeOver | (bConsecutive ? FX_MEMSTREAM_Consecutive : 0); -} - -CFX_MemoryStream::CFX_MemoryStream(uint8_t* pBuffer, - size_t nSize, - bool bTakeOver) - : m_nTotalSize(nSize), - m_nCurSize(nSize), - m_nCurPos(0), - m_nGrowSize(FX_MEMSTREAM_BlockSize) { - m_Blocks.push_back(pBuffer); - m_dwFlags = - FX_MEMSTREAM_Consecutive | (bTakeOver ? FX_MEMSTREAM_TakeOver : 0); -} - -CFX_MemoryStream::~CFX_MemoryStream() { - if (m_dwFlags & FX_MEMSTREAM_TakeOver) { - for (uint8_t* pBlock : m_Blocks) - FX_Free(pBlock); - } -} - -bool CFX_MemoryStream::ReadBlock(void* buffer, - FX_FILESIZE offset, - size_t size) { - if (!buffer || !size || offset < 0) - return false; - - FX_SAFE_SIZE_T newPos = size; - newPos += offset; - if (!newPos.IsValid() || newPos.ValueOrDefault(0) == 0 || - newPos.ValueOrDie() > m_nCurSize) { - return false; - } - - m_nCurPos = newPos.ValueOrDie(); - if (m_dwFlags & FX_MEMSTREAM_Consecutive) { - memcpy(buffer, m_Blocks[0] + static_cast<size_t>(offset), size); - return true; - } - - size_t nStartBlock = static_cast<size_t>(offset) / m_nGrowSize; - offset -= static_cast<FX_FILESIZE>(nStartBlock * m_nGrowSize); - while (size) { - size_t nRead = std::min(size, m_nGrowSize - static_cast<size_t>(offset)); - memcpy(buffer, m_Blocks[nStartBlock] + offset, nRead); - buffer = static_cast<uint8_t*>(buffer) + nRead; - size -= nRead; - ++nStartBlock; - offset = 0; - } - return true; -} - -size_t CFX_MemoryStream::ReadBlock(void* buffer, size_t size) { - if (m_nCurPos >= m_nCurSize) - return 0; - - size_t nRead = std::min(size, m_nCurSize - m_nCurPos); - if (!ReadBlock(buffer, static_cast<int32_t>(m_nCurPos), nRead)) - return 0; - - return nRead; -} - -bool CFX_MemoryStream::WriteBlock(const void* buffer, - FX_FILESIZE offset, - size_t size) { - if (!buffer || !size) - return false; - - if (m_dwFlags & FX_MEMSTREAM_Consecutive) { - FX_SAFE_SIZE_T newPos = size; - newPos += offset; - if (!newPos.IsValid()) - return false; - - m_nCurPos = newPos.ValueOrDie(); - if (m_nCurPos > m_nTotalSize) { - m_nTotalSize = (m_nCurPos + m_nGrowSize - 1) / m_nGrowSize * m_nGrowSize; - if (m_Blocks.empty()) - m_Blocks.push_back(FX_Alloc(uint8_t, m_nTotalSize)); - else - m_Blocks[0] = FX_Realloc(uint8_t, m_Blocks[0], m_nTotalSize); - } - - memcpy(m_Blocks[0] + offset, buffer, size); - m_nCurSize = std::max(m_nCurSize, m_nCurPos); - - return true; - } - - FX_SAFE_SIZE_T newPos = size; - newPos += offset; - if (!newPos.IsValid()) - return false; - if (!ExpandBlocks(newPos.ValueOrDie())) - return false; - - m_nCurPos = newPos.ValueOrDie(); - size_t nStartBlock = static_cast<size_t>(offset) / m_nGrowSize; - offset -= static_cast<FX_FILESIZE>(nStartBlock * m_nGrowSize); - while (size) { - size_t nWrite = std::min(size, m_nGrowSize - static_cast<size_t>(offset)); - memcpy(m_Blocks[nStartBlock] + offset, buffer, nWrite); - buffer = static_cast<const uint8_t*>(buffer) + nWrite; - size -= nWrite; - ++nStartBlock; - offset = 0; - } - return true; -} - -void CFX_MemoryStream::EstimateSize(size_t nInitSize, size_t nGrowSize) { - if (m_dwFlags & FX_MEMSTREAM_Consecutive) { - if (m_Blocks.empty()) { - m_Blocks.push_back( - FX_Alloc(uint8_t, std::max(nInitSize, static_cast<size_t>(4096)))); - } - m_nGrowSize = std::max(nGrowSize, static_cast<size_t>(4096)); - } else if (m_Blocks.empty()) { - m_nGrowSize = std::max(nGrowSize, static_cast<size_t>(4096)); - } -} - -void CFX_MemoryStream::AttachBuffer(uint8_t* pBuffer, - size_t nSize, - bool bTakeOver) { - if (!(m_dwFlags & FX_MEMSTREAM_Consecutive)) - return; - - m_Blocks.clear(); - m_Blocks.push_back(pBuffer); - m_nTotalSize = nSize; - m_nCurSize = nSize; - m_nCurPos = 0; - m_dwFlags = - FX_MEMSTREAM_Consecutive | (bTakeOver ? FX_MEMSTREAM_TakeOver : 0); -} - -void CFX_MemoryStream::DetachBuffer() { - if (!(m_dwFlags & FX_MEMSTREAM_Consecutive)) - return; - - m_Blocks.clear(); - m_nTotalSize = 0; - m_nCurSize = 0; - m_nCurPos = 0; - m_dwFlags = FX_MEMSTREAM_TakeOver; -} - -bool CFX_MemoryStream::ExpandBlocks(size_t size) { - m_nCurSize = std::max(m_nCurSize, size); - if (size <= m_nTotalSize) - return true; - - size = (size - m_nTotalSize + m_nGrowSize - 1) / m_nGrowSize; - size_t iCount = m_Blocks.size(); - m_Blocks.resize(iCount + size); - while (size--) { - m_Blocks[iCount++] = FX_Alloc(uint8_t, m_nGrowSize); - m_nTotalSize += m_nGrowSize; - } - return true; -} - } // namespace // static @@ -294,18 +74,6 @@ IFX_SeekableReadStream::CreateFromFilename(const char* filename) { return IFX_SeekableStream::CreateFromFilename(filename, FX_FILEMODE_ReadOnly); } -// static -CFX_RetainPtr<IFX_MemoryStream> IFX_MemoryStream::Create(uint8_t* pBuffer, - size_t dwSize, - bool bTakeOver) { - return pdfium::MakeRetain<CFX_MemoryStream>(pBuffer, dwSize, bTakeOver); -} - -// static -CFX_RetainPtr<IFX_MemoryStream> IFX_MemoryStream::Create(bool bConsecutive) { - return pdfium::MakeRetain<CFX_MemoryStream>(bConsecutive); -} - bool IFX_SeekableWriteStream::WriteBlock(const void* pData, size_t size) { return WriteBlock(pData, GetSize(), size); } diff --git a/core/fxcrt/fx_stream.h b/core/fxcrt/fx_stream.h index 1aa9feae58..5eb83e5681 100644 --- a/core/fxcrt/fx_stream.h +++ b/core/fxcrt/fx_stream.h @@ -111,22 +111,6 @@ class IFX_SeekableStream : public IFX_SeekableReadStream, bool Flush() override = 0; }; -class IFX_MemoryStream : public IFX_SeekableStream { - public: - static CFX_RetainPtr<IFX_MemoryStream> Create(uint8_t* pBuffer, - size_t nSize, - bool bTakeOver = false); - static CFX_RetainPtr<IFX_MemoryStream> Create(bool bConsecutive = false); - - virtual bool IsConsecutive() const = 0; - virtual void EstimateSize(size_t nInitSize, size_t nGrowSize) = 0; - virtual uint8_t* GetBuffer() const = 0; - virtual void AttachBuffer(uint8_t* pBuffer, - size_t nSize, - bool bTakeOver = false) = 0; - virtual void DetachBuffer() = 0; -}; - #if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_ class CFindFileData { public: diff --git a/core/fxcrt/xml/cfx_saxreader_unittest.cpp b/core/fxcrt/xml/cfx_saxreader_unittest.cpp index 7865d0b77c..0bb5e1a066 100644 --- a/core/fxcrt/xml/cfx_saxreader_unittest.cpp +++ b/core/fxcrt/xml/cfx_saxreader_unittest.cpp @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "core/fxcrt/xml/cfx_saxreader.h" +#include "core/fxcrt/cfx_memorystream.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/test_support.h" @@ -50,10 +51,11 @@ class CFX_SAXReaderTest : public pdfium::FPDF_Test { } bool StartParse(char* str) { - return reader_.StartParse(IFX_MemoryStream::Create( - reinterpret_cast<uint8_t*>(str), strlen(str)), - 0, static_cast<uint32_t>(-1), - CFX_SaxParseMode_NotSkipSpace) >= 0; + return reader_.StartParse( + pdfium::MakeRetain<CFX_MemoryStream>( + reinterpret_cast<uint8_t*>(str), strlen(str), false), + 0, static_cast<uint32_t>(-1), + CFX_SaxParseMode_NotSkipSpace) >= 0; } int32_t ContinueParse() { diff --git a/fpdfsdk/fpdfsave.cpp b/fpdfsdk/fpdfsave.cpp index a8b0d05c61..8e92579518 100644 --- a/fpdfsdk/fpdfsave.cpp +++ b/fpdfsdk/fpdfsave.cpp @@ -16,6 +16,7 @@ #include "core/fpdfapi/parser/cpdf_reference.h" #include "core/fpdfapi/parser/cpdf_stream_acc.h" #include "core/fpdfapi/parser/cpdf_string.h" +#include "core/fxcrt/cfx_memorystream.h" #include "core/fxcrt/fx_extension.h" #include "fpdfsdk/fsdk_define.h" #include "public/fpdf_edit.h" @@ -139,8 +140,9 @@ bool SaveXFADocumentData( CPDF_Stream* pTemplateStream = pArray->GetStreamAt(iTemplate); auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pTemplateStream); pAcc->LoadAllData(); - CFX_RetainPtr<IFX_SeekableStream> pTemplate = IFX_MemoryStream::Create( - const_cast<uint8_t*>(pAcc->GetData()), pAcc->GetSize()); + CFX_RetainPtr<IFX_SeekableStream> pTemplate = + pdfium::MakeRetain<CFX_MemoryStream>( + const_cast<uint8_t*>(pAcc->GetData()), pAcc->GetSize(), false); pChecksum->UpdateChecksum(pTemplate); } CPDF_Stream* pFormStream = nullptr; @@ -173,7 +175,8 @@ bool SaveXFADocumentData( } // L"datasets" { - CFX_RetainPtr<IFX_SeekableStream> pDsfileWrite = IFX_MemoryStream::Create(); + CFX_RetainPtr<IFX_SeekableStream> pDsfileWrite = + pdfium::MakeRetain<CFX_MemoryStream>(false); if (pXFADocView->GetDoc()->SavePackage(XFA_HASHCODE_Datasets, pDsfileWrite, nullptr) && pDsfileWrite->GetSize() > 0) { @@ -200,7 +203,8 @@ bool SaveXFADocumentData( } // L"form" { - CFX_RetainPtr<IFX_SeekableStream> pfileWrite = IFX_MemoryStream::Create(); + CFX_RetainPtr<IFX_SeekableStream> pfileWrite = + pdfium::MakeRetain<CFX_MemoryStream>(false); if (pXFADocView->GetDoc()->SavePackage(XFA_HASHCODE_Form, pfileWrite, pChecksum.get()) && pfileWrite->GetSize() > 0) { diff --git a/testing/libfuzzer/pdf_cfx_saxreader_fuzzer.cc b/testing/libfuzzer/pdf_cfx_saxreader_fuzzer.cc index 7d54cff6da..6769d8e844 100644 --- a/testing/libfuzzer/pdf_cfx_saxreader_fuzzer.cc +++ b/testing/libfuzzer/pdf_cfx_saxreader_fuzzer.cc @@ -4,15 +4,16 @@ #include <memory> +#include "core/fxcrt/cfx_memorystream.h" #include "core/fxcrt/cfx_retain_ptr.h" #include "core/fxcrt/cfx_seekablestreamproxy.h" #include "core/fxcrt/xml/cfx_saxreader.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { CFX_SAXReader reader; - if (reader.StartParse( - IFX_MemoryStream::Create(const_cast<uint8_t*>(data), size), 0, -1, - CFX_SaxParseMode_NotSkipSpace) < 0) { + if (reader.StartParse(pdfium::MakeRetain<CFX_MemoryStream>( + const_cast<uint8_t*>(data), size, false), + 0, -1, CFX_SaxParseMode_NotSkipSpace) < 0) { return 0; } diff --git a/xfa/fgas/font/cfgas_fontmgr.cpp b/xfa/fgas/font/cfgas_fontmgr.cpp index 74c284012f..8430d4f834 100644 --- a/xfa/fgas/font/cfgas_fontmgr.cpp +++ b/xfa/fgas/font/cfgas_fontmgr.cpp @@ -10,8 +10,8 @@ #include <memory> #include <utility> +#include "core/fxcrt/cfx_memorystream.h" #include "core/fxcrt/fx_codepage.h" -#include "core/fxcrt/fx_stream.h" #include "core/fxge/cfx_fontmapper.h" #include "core/fxge/cfx_fontmgr.h" #include "core/fxge/cfx_gemodule.h" @@ -982,7 +982,7 @@ CFX_RetainPtr<IFX_SeekableReadStream> CFGAS_FontMgr::CreateFontStream( uint8_t* pBuffer = FX_Alloc(uint8_t, dwFileSize + 1); dwFileSize = pSystemFontInfo->GetFontData(hFont, 0, pBuffer, dwFileSize); - return IFX_MemoryStream::Create(pBuffer, dwFileSize, true); + return pdfium::MakeRetain<CFX_MemoryStream>(pBuffer, dwFileSize, true); } CFX_RetainPtr<IFX_SeekableReadStream> CFGAS_FontMgr::CreateFontStream( diff --git a/xfa/fxfa/cxfa_ffdoc.cpp b/xfa/fxfa/cxfa_ffdoc.cpp index 5e36c08ff0..228c4948b6 100644 --- a/xfa/fxfa/cxfa_ffdoc.cpp +++ b/xfa/fxfa/cxfa_ffdoc.cpp @@ -15,6 +15,7 @@ #include "core/fpdfapi/parser/fpdf_parser_decode.h" #include "core/fpdfdoc/cpdf_nametree.h" #include "core/fxcrt/cfx_checksumcontext.h" +#include "core/fxcrt/cfx_memorystream.h" #include "core/fxcrt/fx_extension.h" #include "core/fxcrt/fx_memory.h" #include "core/fxcrt/xml/cfx_xmlelement.h" @@ -389,8 +390,9 @@ CFX_RetainPtr<CFX_DIBitmap> CXFA_FFDoc::GetPDFNamedImage( auto pAcc = pdfium::MakeRetain<CPDF_StreamAcc>(pStream); pAcc->LoadAllData(); - CFX_RetainPtr<IFX_SeekableStream> pImageFileRead = IFX_MemoryStream::Create( - const_cast<uint8_t*>(pAcc->GetData()), pAcc->GetSize()); + CFX_RetainPtr<IFX_SeekableStream> pImageFileRead = + pdfium::MakeRetain<CFX_MemoryStream>( + const_cast<uint8_t*>(pAcc->GetData()), pAcc->GetSize(), false); CFX_RetainPtr<CFX_DIBitmap> pDibSource = XFA_LoadImageFromBuffer( pImageFileRead, FXCODEC_IMAGE_UNKNOWN, iImageXDpi, iImageYDpi); diff --git a/xfa/fxfa/cxfa_ffwidget.cpp b/xfa/fxfa/cxfa_ffwidget.cpp index 18d6f94bc7..bdadba7c85 100644 --- a/xfa/fxfa/cxfa_ffwidget.cpp +++ b/xfa/fxfa/cxfa_ffwidget.cpp @@ -14,6 +14,7 @@ #include "core/fxcodec/codec/ccodec_progressivedecoder.h" #include "core/fxcodec/fx_codec.h" #include "core/fxcrt/cfx_maybe_owned.h" +#include "core/fxcrt/cfx_memorystream.h" #include "core/fxge/cfx_gemodule.h" #include "core/fxge/cfx_pathdata.h" #include "core/fxge/cfx_renderdevice.h" @@ -1062,12 +1063,14 @@ CFX_RetainPtr<CFX_DIBitmap> XFA_LoadImageData(CXFA_FFDoc* pDoc, pImageBuffer = FX_Alloc(uint8_t, iLength); int32_t iRead = XFA_Base64Decode(bsData.c_str(), pImageBuffer); if (iRead > 0) { - pImageFileRead = IFX_MemoryStream::Create(pImageBuffer, iRead); + pImageFileRead = + pdfium::MakeRetain<CFX_MemoryStream>(pImageBuffer, iRead, false); } } else { bsContent = CFX_ByteString::FromUnicode(wsImage); - pImageFileRead = IFX_MemoryStream::Create( - const_cast<uint8_t*>(bsContent.raw_str()), bsContent.GetLength()); + pImageFileRead = pdfium::MakeRetain<CFX_MemoryStream>( + const_cast<uint8_t*>(bsContent.raw_str()), bsContent.GetLength(), + false); } } else { CFX_WideString wsURL = wsHref; diff --git a/xfa/fxfa/parser/cxfa_dataexporter.cpp b/xfa/fxfa/parser/cxfa_dataexporter.cpp index 0c702fe53c..70748f2666 100644 --- a/xfa/fxfa/parser/cxfa_dataexporter.cpp +++ b/xfa/fxfa/parser/cxfa_dataexporter.cpp @@ -8,6 +8,7 @@ #include <vector> +#include "core/fxcrt/cfx_memorystream.h" #include "core/fxcrt/fx_basic.h" #include "core/fxcrt/fx_codepage.h" #include "core/fxcrt/xml/cfx_xmldoc.h" @@ -224,8 +225,7 @@ void RegenerateFormFile_Changed(CXFA_Node* pNode, if (!pRichTextXML) break; - CFX_RetainPtr<IFX_MemoryStream> pMemStream = - IFX_MemoryStream::Create(true); + auto pMemStream = pdfium::MakeRetain<CFX_MemoryStream>(true); auto pTempStream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(pMemStream, true); diff --git a/xfa/fxfa/parser/cxfa_node.cpp b/xfa/fxfa/parser/cxfa_node.cpp index a3c9ee42e0..d6d2ece428 100644 --- a/xfa/fxfa/parser/cxfa_node.cpp +++ b/xfa/fxfa/parser/cxfa_node.cpp @@ -13,6 +13,7 @@ #include <vector> #include "core/fxcrt/cfx_decimal.h" +#include "core/fxcrt/cfx_memorystream.h" #include "core/fxcrt/fx_codepage.h" #include "core/fxcrt/fx_extension.h" #include "core/fxcrt/xml/cfx_xmlelement.h" @@ -1406,8 +1407,7 @@ void CXFA_Node::Script_NodeClass_SaveXML(CFXJSE_Arguments* pArguments) { } XFA_DataExporter_DealWithDataGroupNode(this); } - CFX_RetainPtr<IFX_MemoryStream> pMemoryStream = - IFX_MemoryStream::Create(true); + auto pMemoryStream = pdfium::MakeRetain<CFX_MemoryStream>(true); auto pStream = pdfium::MakeRetain<CFX_SeekableStreamProxy>(pMemoryStream, true); pStream->SetCodePage(FX_CODEPAGE_UTF8); |