Skip to content

Commit

Permalink
emitter: Support std::string_view
Browse files Browse the repository at this point in the history
Accept Emitter::operator<<(std::string_view).

ABI remains C++11 compatible by exposing new method
Emitter::Write(const char*, size_t).

All affected calls optimized to pass std::string values as pointer + size
tuple into appropriate routines.
  • Loading branch information
Daniel Levin authored and jbeder committed Dec 22, 2024
1 parent 8a9a7b7 commit 7470c2d
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 60 deletions.
15 changes: 13 additions & 2 deletions include/yaml-cpp/emitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@

#include <cmath>
#include <cstddef>
#include <cstring>
#include <limits>
#include <memory>
#include <sstream>
#include <string>
#include <type_traits>

#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
#include <string_view>
#endif

#include "yaml-cpp/binary.h"
#include "yaml-cpp/dll.h"
#include "yaml-cpp/emitterdef.h"
Expand Down Expand Up @@ -68,6 +73,7 @@ class YAML_CPP_API Emitter {
Emitter& SetLocalPrecision(const _Precision& precision);

// overloads of write
Emitter& Write(const char* str, std::size_t size);
Emitter& Write(const std::string& str);
Emitter& Write(bool b);
Emitter& Write(char ch);
Expand Down Expand Up @@ -201,8 +207,13 @@ inline void Emitter::SetStreamablePrecision<double>(std::stringstream& stream) {
}

// overloads of insertion
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
inline Emitter& operator<<(Emitter& emitter, const std::string_view& v) {
return emitter.Write(v.data(), v.size());
}
#endif
inline Emitter& operator<<(Emitter& emitter, const std::string& v) {
return emitter.Write(v);
return emitter.Write(v.data(), v.size());
}
inline Emitter& operator<<(Emitter& emitter, bool v) {
return emitter.Write(v);
Expand Down Expand Up @@ -233,7 +244,7 @@ inline Emitter& operator<<(Emitter& emitter, const Binary& b) {
}

inline Emitter& operator<<(Emitter& emitter, const char* v) {
return emitter.Write(std::string(v));
return emitter.Write(v, std::strlen(v));
}

inline Emitter& operator<<(Emitter& emitter, int v) {
Expand Down
4 changes: 2 additions & 2 deletions include/yaml-cpp/null.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#endif

#include "yaml-cpp/dll.h"
#include <string>
#include <cstddef>

namespace YAML {
class Node;
Expand All @@ -18,7 +18,7 @@ inline bool operator==(const _Null&, const _Null&) { return true; }
inline bool operator!=(const _Null&, const _Null&) { return false; }

YAML_CPP_API bool IsNull(const Node& node); // old API only
YAML_CPP_API bool IsNullString(const std::string& str);
YAML_CPP_API bool IsNullString(const char* str, std::size_t size);

extern YAML_CPP_API _Null Null;
}
Expand Down
24 changes: 14 additions & 10 deletions src/emitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -716,33 +716,33 @@ StringEscaping::value GetStringEscapingStyle(const EMITTER_MANIP emitterManip) {
}
}

Emitter& Emitter::Write(const std::string& str) {
Emitter& Emitter::Write(const char* str, std::size_t size) {
if (!good())
return *this;

StringEscaping::value stringEscaping = GetStringEscapingStyle(m_pState->GetOutputCharset());

const StringFormat::value strFormat =
Utils::ComputeStringFormat(str, m_pState->GetStringFormat(),
Utils::ComputeStringFormat(str, size, m_pState->GetStringFormat(),
m_pState->CurGroupFlowType(), stringEscaping == StringEscaping::NonAscii);

if (strFormat == StringFormat::Literal || str.size() > 1024)
if (strFormat == StringFormat::Literal || size > 1024)
m_pState->SetMapKeyFormat(YAML::LongKey, FmtScope::Local);

PrepareNode(EmitterNodeType::Scalar);

switch (strFormat) {
case StringFormat::Plain:
m_stream << str;
m_stream.write(str, size);
break;
case StringFormat::SingleQuoted:
Utils::WriteSingleQuotedString(m_stream, str);
Utils::WriteSingleQuotedString(m_stream, str, size);
break;
case StringFormat::DoubleQuoted:
Utils::WriteDoubleQuotedString(m_stream, str, stringEscaping);
Utils::WriteDoubleQuotedString(m_stream, str, size, stringEscaping);
break;
case StringFormat::Literal:
Utils::WriteLiteralString(m_stream, str,
Utils::WriteLiteralString(m_stream, str, size,
m_pState->CurIndent() + m_pState->GetIndent());
break;
}
Expand All @@ -752,6 +752,10 @@ Emitter& Emitter::Write(const std::string& str) {
return *this;
}

Emitter& Emitter::Write(const std::string& str) {
return Write(str.data(), str.size());
}

std::size_t Emitter::GetFloatPrecision() const {
return m_pState->GetFloatPrecision();
}
Expand Down Expand Up @@ -865,7 +869,7 @@ Emitter& Emitter::Write(const _Alias& alias) {

PrepareNode(EmitterNodeType::Scalar);

if (!Utils::WriteAlias(m_stream, alias.content)) {
if (!Utils::WriteAlias(m_stream, alias.content.data(), alias.content.size())) {
m_pState->SetError(ErrorMsg::INVALID_ALIAS);
return *this;
}
Expand All @@ -888,7 +892,7 @@ Emitter& Emitter::Write(const _Anchor& anchor) {

PrepareNode(EmitterNodeType::Property);

if (!Utils::WriteAnchor(m_stream, anchor.content)) {
if (!Utils::WriteAnchor(m_stream, anchor.content.data(), anchor.content.size())) {
m_pState->SetError(ErrorMsg::INVALID_ANCHOR);
return *this;
}
Expand Down Expand Up @@ -937,7 +941,7 @@ Emitter& Emitter::Write(const _Comment& comment) {

if (m_stream.col() > 0)
m_stream << Indentation(m_pState->GetPreCommentIndent());
Utils::WriteComment(m_stream, comment.content,
Utils::WriteComment(m_stream, comment.content.data(), comment.content.size(),
m_pState->GetPostCommentIndent());

m_pState->SetNonContent();
Expand Down
71 changes: 36 additions & 35 deletions src/emitterutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ int Utf8BytesIndicated(char ch) {
bool IsTrailingByte(char ch) { return (ch & 0xC0) == 0x80; }

bool GetNextCodePointAndAdvance(int& codePoint,
std::string::const_iterator& first,
std::string::const_iterator last) {
const char*& first,
const char* last) {
if (first == last)
return false;

Expand Down Expand Up @@ -153,23 +153,23 @@ void WriteCodePoint(ostream_wrapper& out, int codePoint) {
}
}

bool IsValidPlainScalar(const std::string& str, FlowType::value flowType,
bool IsValidPlainScalar(const char* str, std::size_t size, FlowType::value flowType,
bool allowOnlyAscii) {
// check against null
if (IsNullString(str)) {
if (IsNullString(str, size)) {
return false;
}

// check the start
const RegEx& start = (flowType == FlowType::Flow ? Exp::PlainScalarInFlow()
: Exp::PlainScalar());
if (!start.Matches(str)) {
if (!start.Matches(StringCharSource(str, size))) {
return false;
}

// and check the end for plain whitespace (which can't be faithfully kept in a
// plain scalar)
if (!str.empty() && *str.rbegin() == ' ') {
if (size != 0 && str[size - 1] == ' ') {
return false;
}

Expand All @@ -185,7 +185,7 @@ bool IsValidPlainScalar(const std::string& str, FlowType::value flowType,
const RegEx& disallowed =
flowType == FlowType::Flow ? disallowed_flow : disallowed_block;

StringCharSource buffer(str.c_str(), str.size());
StringCharSource buffer(str, size);
while (buffer) {
if (disallowed.Matches(buffer)) {
return false;
Expand All @@ -199,22 +199,22 @@ bool IsValidPlainScalar(const std::string& str, FlowType::value flowType,
return true;
}

bool IsValidSingleQuotedScalar(const std::string& str, bool escapeNonAscii) {
bool IsValidSingleQuotedScalar(const char* str, std::size_t size, bool escapeNonAscii) {
// TODO: check for non-printable characters?
return std::none_of(str.begin(), str.end(), [=](char ch) {
return std::none_of(str, str + size, [=](char ch) {
return (escapeNonAscii && (0x80 <= static_cast<unsigned char>(ch))) ||
(ch == '\n');
});
}

bool IsValidLiteralScalar(const std::string& str, FlowType::value flowType,
bool IsValidLiteralScalar(const char* str, std::size_t size, FlowType::value flowType,
bool escapeNonAscii) {
if (flowType == FlowType::Flow) {
return false;
}

// TODO: check for non-printable characters?
return std::none_of(str.begin(), str.end(), [=](char ch) {
return std::none_of(str, str + size, [=](char ch) {
return (escapeNonAscii && (0x80 <= static_cast<unsigned char>(ch)));
});
}
Expand Down Expand Up @@ -254,10 +254,10 @@ void WriteDoubleQuoteEscapeSequence(ostream_wrapper& out, int codePoint, StringE
out << hexDigits[(codePoint >> (4 * (digits - 1))) & 0xF];
}

bool WriteAliasName(ostream_wrapper& out, const std::string& str) {
bool WriteAliasName(ostream_wrapper& out, const char* str, std::size_t size) {
int codePoint;
for (std::string::const_iterator i = str.begin();
GetNextCodePointAndAdvance(codePoint, i, str.end());) {
for (const char* i = str;
GetNextCodePointAndAdvance(codePoint, i, str + size);) {
if (!IsAnchorChar(codePoint)) {
return false;
}
Expand All @@ -268,25 +268,25 @@ bool WriteAliasName(ostream_wrapper& out, const std::string& str) {
}
} // namespace

StringFormat::value ComputeStringFormat(const std::string& str,
StringFormat::value ComputeStringFormat(const char* str, std::size_t size,
EMITTER_MANIP strFormat,
FlowType::value flowType,
bool escapeNonAscii) {
switch (strFormat) {
case Auto:
if (IsValidPlainScalar(str, flowType, escapeNonAscii)) {
if (IsValidPlainScalar(str, size, flowType, escapeNonAscii)) {
return StringFormat::Plain;
}
return StringFormat::DoubleQuoted;
case SingleQuoted:
if (IsValidSingleQuotedScalar(str, escapeNonAscii)) {
if (IsValidSingleQuotedScalar(str, size, escapeNonAscii)) {
return StringFormat::SingleQuoted;
}
return StringFormat::DoubleQuoted;
case DoubleQuoted:
return StringFormat::DoubleQuoted;
case Literal:
if (IsValidLiteralScalar(str, flowType, escapeNonAscii)) {
if (IsValidLiteralScalar(str, size, flowType, escapeNonAscii)) {
return StringFormat::Literal;
}
return StringFormat::DoubleQuoted;
Expand All @@ -297,11 +297,11 @@ StringFormat::value ComputeStringFormat(const std::string& str,
return StringFormat::DoubleQuoted;
}

bool WriteSingleQuotedString(ostream_wrapper& out, const std::string& str) {
bool WriteSingleQuotedString(ostream_wrapper& out, const char* str, std::size_t size) {
out << "'";
int codePoint;
for (std::string::const_iterator i = str.begin();
GetNextCodePointAndAdvance(codePoint, i, str.end());) {
for (const char* i = str;
GetNextCodePointAndAdvance(codePoint, i, str + size);) {
if (codePoint == '\n') {
return false; // We can't handle a new line and the attendant indentation
// yet
Expand All @@ -317,12 +317,12 @@ bool WriteSingleQuotedString(ostream_wrapper& out, const std::string& str) {
return true;
}

bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str,
bool WriteDoubleQuotedString(ostream_wrapper& out, const char* str, std::size_t size,
StringEscaping::value stringEscaping) {
out << "\"";
int codePoint;
for (std::string::const_iterator i = str.begin();
GetNextCodePointAndAdvance(codePoint, i, str.end());) {
for (const char* i = str;
GetNextCodePointAndAdvance(codePoint, i, str + size);) {
switch (codePoint) {
case '\"':
out << "\\\"";
Expand Down Expand Up @@ -364,12 +364,12 @@ bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str,
return true;
}

bool WriteLiteralString(ostream_wrapper& out, const std::string& str,
bool WriteLiteralString(ostream_wrapper& out, const char* str, std::size_t size,
std::size_t indent) {
out << "|\n";
int codePoint;
for (std::string::const_iterator i = str.begin();
GetNextCodePointAndAdvance(codePoint, i, str.end());) {
for (const char* i = str;
GetNextCodePointAndAdvance(codePoint, i, str + size);) {
if (codePoint == '\n') {
out << "\n";
} else {
Expand Down Expand Up @@ -407,14 +407,14 @@ bool WriteChar(ostream_wrapper& out, char ch, StringEscaping::value stringEscapi
return true;
}

bool WriteComment(ostream_wrapper& out, const std::string& str,
bool WriteComment(ostream_wrapper& out, const char* str, std::size_t size,
std::size_t postCommentIndent) {
const std::size_t curIndent = out.col();
out << "#" << Indentation(postCommentIndent);
out.set_comment();
int codePoint;
for (std::string::const_iterator i = str.begin();
GetNextCodePointAndAdvance(codePoint, i, str.end());) {
for (const char* i = str;
GetNextCodePointAndAdvance(codePoint, i, str + size);) {
if (codePoint == '\n') {
out << "\n"
<< IndentTo(curIndent) << "#" << Indentation(postCommentIndent);
Expand All @@ -426,14 +426,14 @@ bool WriteComment(ostream_wrapper& out, const std::string& str,
return true;
}

bool WriteAlias(ostream_wrapper& out, const std::string& str) {
bool WriteAlias(ostream_wrapper& out, const char* str, std::size_t size) {
out << "*";
return WriteAliasName(out, str);
return WriteAliasName(out, str, size);
}

bool WriteAnchor(ostream_wrapper& out, const std::string& str) {
bool WriteAnchor(ostream_wrapper& out, const char* str, std::size_t size) {
out << "&";
return WriteAliasName(out, str);
return WriteAliasName(out, str, size);
}

bool WriteTag(ostream_wrapper& out, const std::string& str, bool verbatim) {
Expand Down Expand Up @@ -490,7 +490,8 @@ bool WriteTagWithPrefix(ostream_wrapper& out, const std::string& prefix,
}

bool WriteBinary(ostream_wrapper& out, const Binary& binary) {
WriteDoubleQuotedString(out, EncodeBase64(binary.data(), binary.size()),
std::string encoded = EncodeBase64(binary.data(), binary.size());
WriteDoubleQuotedString(out, encoded.data(), encoded.size(),
StringEscaping::None);
return true;
}
Expand Down
14 changes: 7 additions & 7 deletions src/emitterutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,22 @@ struct StringEscaping {
};

namespace Utils {
StringFormat::value ComputeStringFormat(const std::string& str,
StringFormat::value ComputeStringFormat(const char* str, std::size_t size,
EMITTER_MANIP strFormat,
FlowType::value flowType,
bool escapeNonAscii);

bool WriteSingleQuotedString(ostream_wrapper& out, const std::string& str);
bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str,
bool WriteSingleQuotedString(ostream_wrapper& out, const char* str, std::size_t size);
bool WriteDoubleQuotedString(ostream_wrapper& out, const char* str, std::size_t size,
StringEscaping::value stringEscaping);
bool WriteLiteralString(ostream_wrapper& out, const std::string& str,
bool WriteLiteralString(ostream_wrapper& out, const char* str, std::size_t size,
std::size_t indent);
bool WriteChar(ostream_wrapper& out, char ch,
StringEscaping::value stringEscapingStyle);
bool WriteComment(ostream_wrapper& out, const std::string& str,
bool WriteComment(ostream_wrapper& out, const char* str, std::size_t size,
std::size_t postCommentIndent);
bool WriteAlias(ostream_wrapper& out, const std::string& str);
bool WriteAnchor(ostream_wrapper& out, const std::string& str);
bool WriteAlias(ostream_wrapper& out, const char* str, std::size_t size);
bool WriteAnchor(ostream_wrapper& out, const char* str, std::size_t size);
bool WriteTag(ostream_wrapper& out, const std::string& str, bool verbatim);
bool WriteTagWithPrefix(ostream_wrapper& out, const std::string& prefix,
const std::string& tag);
Expand Down
Loading

0 comments on commit 7470c2d

Please sign in to comment.