Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion src/emitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -976,8 +976,32 @@ Emitter& Emitter::Write(const Binary& binary) {
if (!good())
return *this;

const StringFormat::value strFormat =
Utils::ComputeBinaryFormat(binary, m_pState->GetStringFormat(),
m_pState->CurGroupFlowType());

if (strFormat == StringFormat::Literal)
m_pState->SetMapKeyFormat(YAML::LongKey, FmtScope::Local);

PrepareNode(EmitterNodeType::Scalar);
Utils::WriteBinary(m_stream, binary);

switch (strFormat) {
case StringFormat::SingleQuoted:
Utils::WriteSingleQuotedBinary(m_stream, binary);
break;
// For a long period of time, this function outputed the DoubleQuoted form
// regardless of the options. In order not to change the default behavior,
// when strFormat is Plain, it is still treated as DoubleQuoted.
case StringFormat::Plain:
case StringFormat::DoubleQuoted:
Utils::WriteBinary(m_stream, binary);
break;
case StringFormat::Literal:
Utils::WriteLiteralBinary(m_stream, binary,
m_pState->CurIndent() + m_pState->GetIndent());
break;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add some comment for this block, what actually is happening?
Why do you format create a format string for "a"?
And why is there a case for case StringFormat::Plain that is commented out?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About "a":
Just like Emitter::Write for const char*, we should use ComputeStringFormat to generate StringFormat. And I also thought it necessary to offer functions like WriteSingleQuotedBinary and WriteLiteralBinary in Utils and call them, rather than directly generating base64 and calling Utils functions for strings. However, ComputeStringFormat indeed requires a string, but the base64 string is not generated in Emitter::Write! If we encode twice, it may cost double the time with long binary.
In fact ComputeStringFormat returns the same value for any non-empty base64 string and a, so I just passed a for all non-empty Binary.
Maybe adding a ComputeBinaryFormat will look better?

About Plain
Previously, without any options, the strFormat remains Plain by default but emits double-quoted style for Binary. Some long-existing tests are also based on this. Changing the default behavior may also influence some downstream projects. So I left it unchanged (still double-quoted).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best appreciation to you!

StartedScalar();

return *this;
Expand Down
40 changes: 40 additions & 0 deletions src/emitterutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,33 @@ StringFormat::value ComputeStringFormat(const char* str, std::size_t size,
return StringFormat::DoubleQuoted;
}

StringFormat::value ComputeBinaryFormat(const Binary &bin,
EMITTER_MANIP strFormat,
FlowType::value flowType) {
// Equivalent to calling ComputeStringFormat with the base64
// encoded form of 'bin'.
switch (strFormat) {
case Auto:
if (bin.size() > 0u) {
return StringFormat::Plain;
}
return StringFormat::DoubleQuoted;
case SingleQuoted:
return StringFormat::SingleQuoted;
case DoubleQuoted:
return StringFormat::DoubleQuoted;
case Literal:
if (flowType == FlowType::Flow) {
return StringFormat::DoubleQuoted;
}
return StringFormat::Literal;
default:
break;
}

return StringFormat::DoubleQuoted;
}

bool WriteSingleQuotedString(ostream_wrapper& out, const char* str, std::size_t size) {
out << "'";
int codePoint;
Expand Down Expand Up @@ -516,5 +543,18 @@ bool WriteBinary(ostream_wrapper& out, const Binary& binary) {
StringEscaping::None);
return true;
}

bool WriteLiteralBinary(ostream_wrapper& out, const Binary& binary, std::size_t indent) {
std::string encoded = EncodeBase64(binary.data(), binary.size());
WriteLiteralString(out, encoded.data(), encoded.size(), indent);
return true;
}

bool WriteSingleQuotedBinary(ostream_wrapper& out, const Binary& binary) {
std::string encoded = EncodeBase64(binary.data(), binary.size());
WriteSingleQuotedString(out, encoded.data(), encoded.size());
return true;
}

} // namespace Utils
} // namespace YAML
5 changes: 5 additions & 0 deletions src/emitterutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ StringFormat::value ComputeStringFormat(const char* str, std::size_t size,
EMITTER_MANIP strFormat,
FlowType::value flowType,
bool escapeNonAscii);
StringFormat::value ComputeBinaryFormat(const Binary &bin,
EMITTER_MANIP strFormat,
FlowType::value flowType);

bool WriteSingleQuotedString(ostream_wrapper& out, const char* str, std::size_t size);
bool WriteDoubleQuotedString(ostream_wrapper& out, const char* str, std::size_t size,
Expand All @@ -49,6 +52,8 @@ bool WriteTag(ostream_wrapper& out, const std::string& str, bool verbatim);
bool WriteTagWithPrefix(ostream_wrapper& out, const std::string& prefix,
const std::string& tag);
bool WriteBinary(ostream_wrapper& out, const Binary& binary);
bool WriteSingleQuotedBinary(ostream_wrapper& out, const Binary& binary);
bool WriteLiteralBinary(ostream_wrapper& out, const Binary& binary, std::size_t indent);
}
}

Expand Down
40 changes: 34 additions & 6 deletions test/integration/emitter_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1191,6 +1191,34 @@ TEST_F(EmitterTest, EmptyBinary) {
ExpectEmit("!!binary \"\"");
}

TEST_F(EmitterTest, BinaryStyles) {
Binary binary(reinterpret_cast<const unsigned char*>("Hello, World!"), 13);
out << BeginMap;
out << Key << "auto";
out << Value << Auto << binary;
out << Key << "single";
out << Value << SingleQuoted << binary;
out << Key << "double";
out << Value << DoubleQuoted << binary;
out << Key << "literal";
out << Value << Literal << binary;
out << Key << "literal_empty";
out << Value << Literal << Binary(reinterpret_cast<const unsigned char*>(""), 0);
out << Key << "literal_indented";
out << Value << Literal << Indent(8) << binary;
ExpectEmit(
"auto: !!binary \"SGVsbG8sIFdvcmxkIQ==\"\n"
"single: !!binary \'SGVsbG8sIFdvcmxkIQ==\'\n"
"double: !!binary \"SGVsbG8sIFdvcmxkIQ==\"\n"
"literal: !!binary |-\n"
" SGVsbG8sIFdvcmxkIQ==\n"
"literal_empty: !!binary |-\n"
"\n"
"literal_indented: !!binary |-\n"
" SGVsbG8sIFdvcmxkIQ=="
);
}

TEST_F(EmitterTest, ColonAtEndOfScalar) {
out << "a:";
ExpectEmit("\"a:\"");
Expand Down Expand Up @@ -1468,8 +1496,8 @@ TEST_F(EmitterTest, Infinity) {
out << YAML::EndMap;

ExpectEmit(
"foo: .inf\n"
"bar: .inf");
"foo: .inf\n"
"bar: .inf");
Comment on lines +1499 to +1500

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain why the indentation of these change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be caused by clang-format. Previously this case used tab for indentation and the formatter changed it to spaces.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be caused by clang-format. Previously this case used tab for indentation and the formatter changed it to spaces.

Ah yes! than just leave the fix in. I just noticed other PRs have the same change.

}

TEST_F(EmitterTest, NegInfinity) {
Expand All @@ -1481,8 +1509,8 @@ TEST_F(EmitterTest, NegInfinity) {
out << YAML::EndMap;

ExpectEmit(
"foo: -.inf\n"
"bar: -.inf");
"foo: -.inf\n"
"bar: -.inf");
}

TEST_F(EmitterTest, NaN) {
Expand All @@ -1494,8 +1522,8 @@ TEST_F(EmitterTest, NaN) {
out << YAML::EndMap;

ExpectEmit(
"foo: .nan\n"
"bar: .nan");
"foo: .nan\n"
"bar: .nan");
}

TEST_F(EmitterTest, ComplexFlowSeqEmbeddingAMapWithNewLine) {
Expand Down
Loading