aboutsummaryrefslogtreecommitdiff
path: root/source/DataFormatters/StringPrinter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/DataFormatters/StringPrinter.cpp')
-rw-r--r--source/DataFormatters/StringPrinter.cpp307
1 files changed, 156 insertions, 151 deletions
diff --git a/source/DataFormatters/StringPrinter.cpp b/source/DataFormatters/StringPrinter.cpp
index a011cd553d0c..b114add50640 100644
--- a/source/DataFormatters/StringPrinter.cpp
+++ b/source/DataFormatters/StringPrinter.cpp
@@ -9,109 +9,26 @@
#include "lldb/DataFormatters/StringPrinter.h"
-#include "lldb/Core/DataExtractor.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/ValueObject.h"
+#include "lldb/Target/Language.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
#include "llvm/Support/ConvertUTF.h"
#include <ctype.h>
-#include <functional>
#include <locale>
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::formatters;
-// I can't use a std::unique_ptr for this because the Deleter is a template argument there
-// and I want the same type to represent both pointers I want to free and pointers I don't need
-// to free - which is what this class essentially is
-// It's very specialized to the needs of this file, and not suggested for general use
-template <typename T = uint8_t, typename U = char, typename S = size_t>
-struct StringPrinterBufferPointer
-{
-public:
-
- typedef std::function<void(const T*)> Deleter;
-
- StringPrinterBufferPointer (std::nullptr_t ptr) :
- m_data(nullptr),
- m_size(0),
- m_deleter()
- {}
-
- StringPrinterBufferPointer(const T* bytes, S size, Deleter deleter = nullptr) :
- m_data(bytes),
- m_size(size),
- m_deleter(deleter)
- {}
-
- StringPrinterBufferPointer(const U* bytes, S size, Deleter deleter = nullptr) :
- m_data((T*)bytes),
- m_size(size),
- m_deleter(deleter)
- {}
-
- StringPrinterBufferPointer(StringPrinterBufferPointer&& rhs) :
- m_data(rhs.m_data),
- m_size(rhs.m_size),
- m_deleter(rhs.m_deleter)
- {
- rhs.m_data = nullptr;
- }
-
- StringPrinterBufferPointer(const StringPrinterBufferPointer& rhs) :
- m_data(rhs.m_data),
- m_size(rhs.m_size),
- m_deleter(rhs.m_deleter)
- {
- rhs.m_data = nullptr; // this is why m_data has to be mutable
- }
-
- const T*
- GetBytes () const
- {
- return m_data;
- }
-
- const S
- GetSize () const
- {
- return m_size;
- }
-
- ~StringPrinterBufferPointer ()
- {
- if (m_data && m_deleter)
- m_deleter(m_data);
- m_data = nullptr;
- }
-
- StringPrinterBufferPointer&
- operator = (const StringPrinterBufferPointer& rhs)
- {
- if (m_data && m_deleter)
- m_deleter(m_data);
- m_data = rhs.m_data;
- m_size = rhs.m_size;
- m_deleter = rhs.m_deleter;
- rhs.m_data = nullptr;
- return *this;
- }
-
-private:
- mutable const T* m_data;
- size_t m_size;
- Deleter m_deleter;
-};
-
// we define this for all values of type but only implement it for those we care about
// that's good because we get linker errors for any unsupported type
-template <StringElementType type>
-static StringPrinterBufferPointer<>
+template <lldb_private::formatters::StringPrinter::StringElementType type>
+static StringPrinter::StringPrinterBufferPointer<>
GetPrintableImpl(uint8_t* buffer, uint8_t* buffer_end, uint8_t*& next);
// mimic isprint() for Unicode codepoints
@@ -142,10 +59,10 @@ isprint(char32_t codepoint)
}
template <>
-StringPrinterBufferPointer<>
-GetPrintableImpl<StringElementType::ASCII> (uint8_t* buffer, uint8_t* buffer_end, uint8_t*& next)
+StringPrinter::StringPrinterBufferPointer<>
+GetPrintableImpl<StringPrinter::StringElementType::ASCII> (uint8_t* buffer, uint8_t* buffer_end, uint8_t*& next)
{
- StringPrinterBufferPointer<> retval = {nullptr};
+ StringPrinter::StringPrinterBufferPointer<> retval = {nullptr};
switch (*buffer)
{
@@ -212,10 +129,10 @@ ConvertUTF8ToCodePoint (unsigned char c0, unsigned char c1, unsigned char c2, un
}
template <>
-StringPrinterBufferPointer<>
-GetPrintableImpl<StringElementType::UTF8> (uint8_t* buffer, uint8_t* buffer_end, uint8_t*& next)
+StringPrinter::StringPrinterBufferPointer<>
+GetPrintableImpl<StringPrinter::StringElementType::UTF8> (uint8_t* buffer, uint8_t* buffer_end, uint8_t*& next)
{
- StringPrinterBufferPointer<> retval {nullptr};
+ StringPrinter::StringPrinterBufferPointer<> retval {nullptr};
unsigned utf8_encoded_len = getNumBytesForUTF8(*buffer);
@@ -232,7 +149,7 @@ GetPrintableImpl<StringElementType::UTF8> (uint8_t* buffer, uint8_t* buffer_end,
{
case 1:
// this is just an ASCII byte - ask ASCII
- return GetPrintableImpl<StringElementType::ASCII>(buffer, buffer_end, next);
+ return GetPrintableImpl<StringPrinter::StringElementType::ASCII>(buffer, buffer_end, next);
case 2:
codepoint = ConvertUTF8ToCodePoint((unsigned char)*buffer, (unsigned char)*(buffer+1));
break;
@@ -309,23 +226,40 @@ GetPrintableImpl<StringElementType::UTF8> (uint8_t* buffer, uint8_t* buffer_end,
// Given a sequence of bytes, this function returns:
// a sequence of bytes to actually print out + a length
// the following unscanned position of the buffer is in next
-static StringPrinterBufferPointer<>
-GetPrintable(StringElementType type, uint8_t* buffer, uint8_t* buffer_end, uint8_t*& next)
+static StringPrinter::StringPrinterBufferPointer<>
+GetPrintable(StringPrinter::StringElementType type, uint8_t* buffer, uint8_t* buffer_end, uint8_t*& next)
{
if (!buffer)
return {nullptr};
switch (type)
{
- case StringElementType::ASCII:
- return GetPrintableImpl<StringElementType::ASCII>(buffer, buffer_end, next);
- case StringElementType::UTF8:
- return GetPrintableImpl<StringElementType::UTF8>(buffer, buffer_end, next);
+ case StringPrinter::StringElementType::ASCII:
+ return GetPrintableImpl<StringPrinter::StringElementType::ASCII>(buffer, buffer_end, next);
+ case StringPrinter::StringElementType::UTF8:
+ return GetPrintableImpl<StringPrinter::StringElementType::UTF8>(buffer, buffer_end, next);
default:
return {nullptr};
}
}
+StringPrinter::EscapingHelper
+StringPrinter::GetDefaultEscapingHelper (GetPrintableElementType elem_type)
+{
+ switch (elem_type)
+ {
+ case GetPrintableElementType::UTF8:
+ return [] (uint8_t* buffer, uint8_t* buffer_end, uint8_t*& next) -> StringPrinter::StringPrinterBufferPointer<> {
+ return GetPrintable(StringPrinter::StringElementType::UTF8, buffer, buffer_end, next);
+ };
+ case GetPrintableElementType::ASCII:
+ return [] (uint8_t* buffer, uint8_t* buffer_end, uint8_t*& next) -> StringPrinter::StringPrinterBufferPointer<> {
+ return GetPrintable(StringPrinter::StringElementType::ASCII, buffer, buffer_end, next);
+ };
+ }
+ llvm_unreachable("bad element type");
+}
+
// use this call if you already have an LLDB-side buffer for the data
template<typename SourceDataType>
static bool
@@ -334,41 +268,44 @@ DumpUTFBufferToStream (ConversionResult (*ConvertFunction) (const SourceDataType
UTF8**,
UTF8*,
ConversionFlags),
- const DataExtractor& data,
- Stream& stream,
- char prefix_token,
- char quote,
- uint32_t sourceSize,
- bool escapeNonPrintables)
+ const StringPrinter::ReadBufferAndDumpToStreamOptions& dump_options)
{
- if (prefix_token != 0)
- stream.Printf("%c",prefix_token);
- if (quote != 0)
- stream.Printf("%c",quote);
+ Stream &stream(*dump_options.GetStream());
+ if (dump_options.GetPrefixToken() != 0)
+ stream.Printf("%s",dump_options.GetPrefixToken());
+ if (dump_options.GetQuote() != 0)
+ stream.Printf("%c",dump_options.GetQuote());
+ auto data(dump_options.GetData());
+ auto source_size(dump_options.GetSourceSize());
if (data.GetByteSize() && data.GetDataStart() && data.GetDataEnd())
{
const int bufferSPSize = data.GetByteSize();
- if (sourceSize == 0)
+ if (dump_options.GetSourceSize() == 0)
{
const int origin_encoding = 8*sizeof(SourceDataType);
- sourceSize = bufferSPSize/(origin_encoding / 4);
+ source_size = bufferSPSize/(origin_encoding / 4);
}
const SourceDataType *data_ptr = (const SourceDataType*)data.GetDataStart();
- const SourceDataType *data_end_ptr = data_ptr + sourceSize;
+ const SourceDataType *data_end_ptr = data_ptr + source_size;
- while (data_ptr < data_end_ptr)
+ const bool zero_is_terminator = dump_options.GetBinaryZeroIsTerminator();
+
+ if (zero_is_terminator)
{
- if (!*data_ptr)
+ while (data_ptr < data_end_ptr)
{
- data_end_ptr = data_ptr;
- break;
+ if (!*data_ptr)
+ {
+ data_end_ptr = data_ptr;
+ break;
+ }
+ data_ptr++;
}
- data_ptr++;
+
+ data_ptr = (const SourceDataType*)data.GetDataStart();
}
- data_ptr = (const SourceDataType*)data.GetDataStart();
-
lldb::DataBufferSP utf8_data_buffer_sp;
UTF8* utf8_data_ptr = nullptr;
UTF8* utf8_data_end_ptr = nullptr;
@@ -379,14 +316,26 @@ DumpUTFBufferToStream (ConversionResult (*ConvertFunction) (const SourceDataType
utf8_data_ptr = (UTF8*)utf8_data_buffer_sp->GetBytes();
utf8_data_end_ptr = utf8_data_ptr + utf8_data_buffer_sp->GetByteSize();
ConvertFunction ( &data_ptr, data_end_ptr, &utf8_data_ptr, utf8_data_end_ptr, lenientConversion );
+ if (false == zero_is_terminator)
+ utf8_data_end_ptr = utf8_data_ptr;
utf8_data_ptr = (UTF8*)utf8_data_buffer_sp->GetBytes(); // needed because the ConvertFunction will change the value of the data_ptr
}
else
{
// just copy the pointers - the cast is necessary to make the compiler happy
// but this should only happen if we are reading UTF8 data
- utf8_data_ptr = (UTF8*)data_ptr;
- utf8_data_end_ptr = (UTF8*)data_end_ptr;
+ utf8_data_ptr = const_cast<UTF8 *>(reinterpret_cast<const UTF8*>(data_ptr));
+ utf8_data_end_ptr = const_cast<UTF8 *>(reinterpret_cast<const UTF8*>(data_end_ptr));
+ }
+
+ const bool escape_non_printables = dump_options.GetEscapeNonPrintables();
+ lldb_private::formatters::StringPrinter::EscapingHelper escaping_callback;
+ if (escape_non_printables)
+ {
+ if (Language *language = Language::FindPlugin(dump_options.GetLanguage()))
+ escaping_callback = language->GetStringPrinterEscapingHelper(lldb_private::formatters::StringPrinter::GetPrintableElementType::UTF8);
+ else
+ escaping_callback = lldb_private::formatters::StringPrinter::GetDefaultEscapingHelper(lldb_private::formatters::StringPrinter::GetPrintableElementType::UTF8);
}
// since we tend to accept partial data (and even partially malformed data)
@@ -394,13 +343,13 @@ DumpUTFBufferToStream (ConversionResult (*ConvertFunction) (const SourceDataType
// hence we need to take a slower route and ensure we stay within boundaries
for (;utf8_data_ptr < utf8_data_end_ptr;)
{
- if (!*utf8_data_ptr)
+ if (zero_is_terminator && !*utf8_data_ptr)
break;
- if (escapeNonPrintables)
+ if (escape_non_printables)
{
uint8_t* next_data = nullptr;
- auto printable = GetPrintable(StringElementType::UTF8, utf8_data_ptr, utf8_data_end_ptr, next_data);
+ auto printable = escaping_callback(utf8_data_ptr, utf8_data_end_ptr, next_data);
auto printable_bytes = printable.GetBytes();
auto printable_size = printable.GetSize();
if (!printable_bytes || !next_data)
@@ -421,23 +370,39 @@ DumpUTFBufferToStream (ConversionResult (*ConvertFunction) (const SourceDataType
}
}
}
- if (quote != 0)
- stream.Printf("%c",quote);
+ if (dump_options.GetQuote() != 0)
+ stream.Printf("%c",dump_options.GetQuote());
+ if (dump_options.GetSuffixToken() != 0)
+ stream.Printf("%s",dump_options.GetSuffixToken());
+ if (dump_options.GetIsTruncated())
+ stream.Printf("...");
return true;
}
-lldb_private::formatters::ReadStringAndDumpToStreamOptions::ReadStringAndDumpToStreamOptions (ValueObject& valobj) :
+lldb_private::formatters::StringPrinter::ReadStringAndDumpToStreamOptions::ReadStringAndDumpToStreamOptions (ValueObject& valobj) :
ReadStringAndDumpToStreamOptions()
{
SetEscapeNonPrintables(valobj.GetTargetSP()->GetDebugger().GetEscapeNonPrintables());
}
-lldb_private::formatters::ReadBufferAndDumpToStreamOptions::ReadBufferAndDumpToStreamOptions (ValueObject& valobj) :
+lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStreamOptions::ReadBufferAndDumpToStreamOptions (ValueObject& valobj) :
ReadBufferAndDumpToStreamOptions()
{
SetEscapeNonPrintables(valobj.GetTargetSP()->GetDebugger().GetEscapeNonPrintables());
}
+lldb_private::formatters::StringPrinter::ReadBufferAndDumpToStreamOptions::ReadBufferAndDumpToStreamOptions (const ReadStringAndDumpToStreamOptions& options) :
+ ReadBufferAndDumpToStreamOptions()
+{
+ SetStream(options.GetStream());
+ SetPrefixToken(options.GetPrefixToken());
+ SetSuffixToken(options.GetSuffixToken());
+ SetQuote(options.GetQuote());
+ SetEscapeNonPrintables(options.GetEscapeNonPrintables());
+ SetBinaryZeroIsTerminator(options.GetBinaryZeroIsTerminator());
+ SetLanguage(options.GetLanguage());
+}
+
namespace lldb_private
{
@@ -447,7 +412,7 @@ namespace formatters
template <>
bool
-ReadStringAndDumpToStream<StringElementType::ASCII> (ReadStringAndDumpToStreamOptions options)
+StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::ASCII> (const ReadStringAndDumpToStreamOptions& options)
{
assert(options.GetStream() && "need a Stream to print the string to");
Error my_error;
@@ -458,11 +423,20 @@ ReadStringAndDumpToStream<StringElementType::ASCII> (ReadStringAndDumpToStreamOp
return false;
size_t size;
+ const auto max_size = process_sp->GetTarget().GetMaximumSizeOfStringSummary();
+ bool is_truncated = false;
if (options.GetSourceSize() == 0)
- size = process_sp->GetTarget().GetMaximumSizeOfStringSummary();
+ size = max_size;
else if (!options.GetIgnoreMaxLength())
- size = std::min(options.GetSourceSize(),process_sp->GetTarget().GetMaximumSizeOfStringSummary());
+ {
+ size = options.GetSourceSize();
+ if (size > max_size)
+ {
+ size = max_size;
+ is_truncated = true;
+ }
+ }
else
size = options.GetSourceSize();
@@ -473,25 +447,35 @@ ReadStringAndDumpToStream<StringElementType::ASCII> (ReadStringAndDumpToStreamOp
if (my_error.Fail())
return false;
- char prefix_token = options.GetPrefixToken();
+ const char* prefix_token = options.GetPrefixToken();
char quote = options.GetQuote();
if (prefix_token != 0)
- options.GetStream()->Printf("%c%c",prefix_token,quote);
+ options.GetStream()->Printf("%s%c",prefix_token,quote);
else if (quote != 0)
options.GetStream()->Printf("%c",quote);
uint8_t* data_end = buffer_sp->GetBytes()+buffer_sp->GetByteSize();
+ const bool escape_non_printables = options.GetEscapeNonPrintables();
+ lldb_private::formatters::StringPrinter::EscapingHelper escaping_callback;
+ if (escape_non_printables)
+ {
+ if (Language *language = Language::FindPlugin(options.GetLanguage()))
+ escaping_callback = language->GetStringPrinterEscapingHelper(lldb_private::formatters::StringPrinter::GetPrintableElementType::ASCII);
+ else
+ escaping_callback = lldb_private::formatters::StringPrinter::GetDefaultEscapingHelper(lldb_private::formatters::StringPrinter::GetPrintableElementType::ASCII);
+ }
+
// since we tend to accept partial data (and even partially malformed data)
// we might end up with no NULL terminator before the end_ptr
// hence we need to take a slower route and ensure we stay within boundaries
for (uint8_t* data = buffer_sp->GetBytes(); *data && (data < data_end);)
{
- if (options.GetEscapeNonPrintables())
+ if (escape_non_printables)
{
uint8_t* next_data = nullptr;
- auto printable = GetPrintable(StringElementType::ASCII, data, data_end, next_data);
+ auto printable = escaping_callback(data, data_end, next_data);
auto printable_bytes = printable.GetBytes();
auto printable_size = printable.GetSize();
if (!printable_bytes || !next_data)
@@ -511,16 +495,23 @@ ReadStringAndDumpToStream<StringElementType::ASCII> (ReadStringAndDumpToStreamOp
data++;
}
}
-
- if (quote != 0)
+
+ const char* suffix_token = options.GetSuffixToken();
+
+ if (suffix_token != 0)
+ options.GetStream()->Printf("%c%s",quote, suffix_token);
+ else if (quote != 0)
options.GetStream()->Printf("%c",quote);
+ if (is_truncated)
+ options.GetStream()->Printf("...");
+
return true;
}
template<typename SourceDataType>
static bool
-ReadUTFBufferAndDumpToStream (const ReadStringAndDumpToStreamOptions& options,
+ReadUTFBufferAndDumpToStream (const StringPrinter::ReadStringAndDumpToStreamOptions& options,
ConversionResult (*ConvertFunction) (const SourceDataType**,
const SourceDataType*,
UTF8**,
@@ -550,14 +541,23 @@ ReadUTFBufferAndDumpToStream (const ReadStringAndDumpToStreamOptions& options,
uint32_t sourceSize = options.GetSourceSize();
bool needs_zero_terminator = options.GetNeedsZeroTermination();
+
+ bool is_truncated = false;
+ const auto max_size = process_sp->GetTarget().GetMaximumSizeOfStringSummary();
if (!sourceSize)
{
- sourceSize = process_sp->GetTarget().GetMaximumSizeOfStringSummary();
+ sourceSize = max_size;
needs_zero_terminator = true;
}
else if (!options.GetIgnoreMaxLength())
- sourceSize = std::min(sourceSize,process_sp->GetTarget().GetMaximumSizeOfStringSummary());
+ {
+ if (sourceSize > max_size)
+ {
+ sourceSize = max_size;
+ is_truncated = true;
+ }
+ }
const int bufferSPSize = sourceSize * type_width;
@@ -581,13 +581,18 @@ ReadUTFBufferAndDumpToStream (const ReadStringAndDumpToStreamOptions& options,
}
DataExtractor data(buffer_sp, process_sp->GetByteOrder(), process_sp->GetAddressByteSize());
+
+ StringPrinter::ReadBufferAndDumpToStreamOptions dump_options(options);
+ dump_options.SetData(data);
+ dump_options.SetSourceSize(sourceSize);
+ dump_options.SetIsTruncated(is_truncated);
- return DumpUTFBufferToStream(ConvertFunction, data, *options.GetStream(), options.GetPrefixToken(), options.GetQuote(), sourceSize, options.GetEscapeNonPrintables());
+ return DumpUTFBufferToStream(ConvertFunction, dump_options);
}
template <>
bool
-ReadStringAndDumpToStream<StringElementType::UTF8> (ReadStringAndDumpToStreamOptions options)
+StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::UTF8> (const ReadStringAndDumpToStreamOptions& options)
{
return ReadUTFBufferAndDumpToStream<UTF8>(options,
nullptr);
@@ -595,7 +600,7 @@ ReadStringAndDumpToStream<StringElementType::UTF8> (ReadStringAndDumpToStreamOpt
template <>
bool
-ReadStringAndDumpToStream<StringElementType::UTF16> (ReadStringAndDumpToStreamOptions options)
+StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::UTF16> (const ReadStringAndDumpToStreamOptions& options)
{
return ReadUTFBufferAndDumpToStream<UTF16>(options,
ConvertUTF16toUTF8);
@@ -603,7 +608,7 @@ ReadStringAndDumpToStream<StringElementType::UTF16> (ReadStringAndDumpToStreamOp
template <>
bool
-ReadStringAndDumpToStream<StringElementType::UTF32> (ReadStringAndDumpToStreamOptions options)
+StringPrinter::ReadStringAndDumpToStream<StringPrinter::StringElementType::UTF32> (const ReadStringAndDumpToStreamOptions& options)
{
return ReadUTFBufferAndDumpToStream<UTF32>(options,
ConvertUTF32toUTF8);
@@ -611,16 +616,16 @@ ReadStringAndDumpToStream<StringElementType::UTF32> (ReadStringAndDumpToStreamOp
template <>
bool
-ReadBufferAndDumpToStream<StringElementType::UTF8> (ReadBufferAndDumpToStreamOptions options)
+StringPrinter::ReadBufferAndDumpToStream<StringPrinter::StringElementType::UTF8> (const ReadBufferAndDumpToStreamOptions& options)
{
assert(options.GetStream() && "need a Stream to print the string to");
- return DumpUTFBufferToStream<UTF8>(nullptr, options.GetData(), *options.GetStream(), options.GetPrefixToken(), options.GetQuote(), options.GetSourceSize(), options.GetEscapeNonPrintables());
+ return DumpUTFBufferToStream<UTF8>(nullptr, options);
}
template <>
bool
-ReadBufferAndDumpToStream<StringElementType::ASCII> (ReadBufferAndDumpToStreamOptions options)
+StringPrinter::ReadBufferAndDumpToStream<StringPrinter::StringElementType::ASCII> (const ReadBufferAndDumpToStreamOptions& options)
{
// treat ASCII the same as UTF8
// FIXME: can we optimize ASCII some more?
@@ -629,20 +634,20 @@ ReadBufferAndDumpToStream<StringElementType::ASCII> (ReadBufferAndDumpToStreamOp
template <>
bool
-ReadBufferAndDumpToStream<StringElementType::UTF16> (ReadBufferAndDumpToStreamOptions options)
+StringPrinter::ReadBufferAndDumpToStream<StringPrinter::StringElementType::UTF16> (const ReadBufferAndDumpToStreamOptions& options)
{
assert(options.GetStream() && "need a Stream to print the string to");
- return DumpUTFBufferToStream(ConvertUTF16toUTF8, options.GetData(), *options.GetStream(), options.GetPrefixToken(), options.GetQuote(), options.GetSourceSize(), options.GetEscapeNonPrintables());
+ return DumpUTFBufferToStream(ConvertUTF16toUTF8, options);
}
template <>
bool
-ReadBufferAndDumpToStream<StringElementType::UTF32> (ReadBufferAndDumpToStreamOptions options)
+StringPrinter::ReadBufferAndDumpToStream<StringPrinter::StringElementType::UTF32> (const ReadBufferAndDumpToStreamOptions& options)
{
assert(options.GetStream() && "need a Stream to print the string to");
- return DumpUTFBufferToStream(ConvertUTF32toUTF8, options.GetData(), *options.GetStream(), options.GetPrefixToken(), options.GetQuote(), options.GetSourceSize(), options.GetEscapeNonPrintables());
+ return DumpUTFBufferToStream(ConvertUTF32toUTF8, options);
}
} // namespace formatters