Skip to content
Open
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
8 changes: 4 additions & 4 deletions src/emitterutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,14 +174,14 @@ bool IsValidPlainScalar(const char* str, std::size_t size, FlowType::value flowT
}

// then check until something is disallowed
static const RegEx disallowed_flow =
static const never_destroyed<RegEx> disallowed_flow{
Exp::EndScalarInFlow() | (Exp::BlankOrBreak() + Exp::Comment()) |
Exp::NotPrintable() | Exp::Utf8_ByteOrderMark() | Exp::Break() |
Exp::Tab() | Exp::Ampersand();
static const RegEx disallowed_block =
Exp::Tab() | Exp::Ampersand()};
static const never_destroyed<RegEx> disallowed_block{
Exp::EndScalar() | (Exp::BlankOrBreak() + Exp::Comment()) |
Exp::NotPrintable() | Exp::Utf8_ByteOrderMark() | Exp::Break() |
Exp::Tab() | Exp::Ampersand();
Exp::Tab() | Exp::Ampersand()};
const RegEx& disallowed =
flowType == FlowType::Flow ? disallowed_flow : disallowed_block;

Expand Down
107 changes: 61 additions & 46 deletions src/exp.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <ios>
#include <string>

#include "never_destroyed.h"
#include "regex_yaml.h"
#include "stream.h"

Expand All @@ -21,126 +22,137 @@ namespace YAML {
namespace Exp {
// misc
inline const RegEx& Empty() {
static const RegEx e;
static const never_destroyed<RegEx> e;
return e;
}
inline const RegEx& Space() {
static const RegEx e = RegEx(' ');
static const never_destroyed<RegEx> e{RegEx(' ')};
return e;
}
inline const RegEx& Tab() {
static const RegEx e = RegEx('\t');
static const never_destroyed<RegEx> e{RegEx('\t')};
return e;
}
inline const RegEx& Blank() {
static const RegEx e = Space() | Tab();
static const never_destroyed<RegEx> e{Space() | Tab()};
return e;
}
inline const RegEx& Break() {
static const RegEx e = RegEx('\n') | RegEx("\r\n") | RegEx('\r');
static const never_destroyed<RegEx> e{RegEx('\n') | RegEx("\r\n") |
RegEx('\r')};
return e;
}
inline const RegEx& BlankOrBreak() {
static const RegEx e = Blank() | Break();
static const never_destroyed<RegEx> e{Blank() | Break()};
return e;
}
inline const RegEx& Digit() {
static const RegEx e = RegEx('0', '9');
static const never_destroyed<RegEx> e{RegEx('0', '9')};
return e;
}
inline const RegEx& Alpha() {
static const RegEx e = RegEx('a', 'z') | RegEx('A', 'Z');
static const never_destroyed<RegEx> e{RegEx('a', 'z') | RegEx('A', 'Z')};
return e;
}
inline const RegEx& AlphaNumeric() {
static const RegEx e = Alpha() | Digit();
static const never_destroyed<RegEx> e{Alpha() | Digit()};
return e;
}
inline const RegEx& Word() {
static const RegEx e = AlphaNumeric() | RegEx('-');
static const never_destroyed<RegEx> e{AlphaNumeric() | RegEx('-')};
return e;
}
inline const RegEx& Hex() {
static const RegEx e = Digit() | RegEx('A', 'F') | RegEx('a', 'f');
static const never_destroyed<RegEx> e{Digit() | RegEx('A', 'F') |
RegEx('a', 'f')};
return e;
}
// Valid Unicode code points that are not part of c-printable (YAML 1.2, sec.
// 5.1)
inline const RegEx& NotPrintable() {
static const RegEx e =
static const never_destroyed<RegEx> e{
RegEx(0) |
RegEx("\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x7F", REGEX_OR) |
RegEx(0x0E, 0x1F) |
(RegEx('\xC2') + (RegEx('\x80', '\x84') | RegEx('\x86', '\x9F')));
(RegEx('\xC2') + (RegEx('\x80', '\x84') | RegEx('\x86', '\x9F')))};
return e;
}
inline const RegEx& Utf8_ByteOrderMark() {
static const RegEx e = RegEx("\xEF\xBB\xBF");
static const never_destroyed<RegEx> e{RegEx("\xEF\xBB\xBF")};
return e;
}

// actual tags

inline const RegEx& DocStart() {
static const RegEx e = RegEx("---") + (BlankOrBreak() | RegEx());
static const never_destroyed<RegEx> e{RegEx("---") +
(BlankOrBreak() | RegEx())};
return e;
}
inline const RegEx& DocEnd() {
static const RegEx e = RegEx("...") + (BlankOrBreak() | RegEx());
static const never_destroyed<RegEx> e{RegEx("...") +
(BlankOrBreak() | RegEx())};
return e;
}
inline const RegEx& DocIndicator() {
static const RegEx e = DocStart() | DocEnd();
static const never_destroyed<RegEx> e{DocStart() | DocEnd()};
return e;
}
inline const RegEx& BlockEntry() {
static const RegEx e = RegEx('-') + (BlankOrBreak() | RegEx());
static const never_destroyed<RegEx> e{RegEx('-') +
(BlankOrBreak() | RegEx())};
return e;
}
inline const RegEx& Key() {
static const RegEx e = RegEx('?') + BlankOrBreak();
static const never_destroyed<RegEx> e{RegEx('?') + BlankOrBreak()};
return e;
}
inline const RegEx& KeyInFlow() {
static const RegEx e = RegEx('?') + BlankOrBreak();
static const never_destroyed<RegEx> e{RegEx('?') + BlankOrBreak()};
return e;
}
inline const RegEx& Value() {
static const RegEx e = RegEx(':') + (BlankOrBreak() | RegEx());
static const never_destroyed<RegEx> e{RegEx(':') +
(BlankOrBreak() | RegEx())};
return e;
}
inline const RegEx& ValueInFlow() {
static const RegEx e = RegEx(':') + (BlankOrBreak() | RegEx(",]}", REGEX_OR));
static const never_destroyed<RegEx> e{
RegEx(':') + (BlankOrBreak() | RegEx(",]}", REGEX_OR))};
return e;
}
inline const RegEx& ValueInJSONFlow() {
static const RegEx e = RegEx(':');
static const never_destroyed<RegEx> e{RegEx(':')};
return e;
}
inline const RegEx& Ampersand() {
static const RegEx e = RegEx('&');
static const never_destroyed<RegEx> e{RegEx('&')};
return e;
}
inline const RegEx Comment() {
static const RegEx e = RegEx('#');
static const never_destroyed<RegEx> e{RegEx('#')};
return e;
}
inline const RegEx& Anchor() {
static const RegEx e = !(RegEx("[]{},", REGEX_OR) | BlankOrBreak());
static const never_destroyed<RegEx> e{
!(RegEx("[]{},", REGEX_OR) | BlankOrBreak())};
return e;
}
inline const RegEx& AnchorEnd() {
static const RegEx e = RegEx("?:,]}%@`", REGEX_OR) | BlankOrBreak();
static const never_destroyed<RegEx> e{RegEx("?:,]}%@`", REGEX_OR) |
BlankOrBreak()};
return e;
}
inline const RegEx& URI() {
static const RegEx e = Word() | RegEx("#;/?:@&=+$,_.!~*'()[]", REGEX_OR) |
(RegEx('%') + Hex() + Hex());
static const never_destroyed<RegEx> e{
Word() | RegEx("#;/?:@&=+$,_.!~*'()[]", REGEX_OR) |
(RegEx('%') + Hex() + Hex())};
return e;
}
inline const RegEx& Tag() {
static const RegEx e = Word() | RegEx("#;/?:@&=+$_.~*'()", REGEX_OR) |
(RegEx('%') + Hex() + Hex());
static const never_destroyed<RegEx> e{Word() |
RegEx("#;/?:@&=+$_.~*'()", REGEX_OR) |
(RegEx('%') + Hex() + Hex())};
return e;
}

Expand All @@ -151,54 +163,57 @@ inline const RegEx& Tag() {
// . In the flow context ? is illegal and : and - must not be followed with a
// space.
inline const RegEx& PlainScalar() {
static const RegEx e =
static const never_destroyed<RegEx> e{
!(BlankOrBreak() | RegEx(",[]{}#&*!|>\'\"%@`", REGEX_OR) |
(RegEx("-?:", REGEX_OR) + (BlankOrBreak() | RegEx())));
(RegEx("-?:", REGEX_OR) + (BlankOrBreak() | RegEx())))};
return e;
}
inline const RegEx& PlainScalarInFlow() {
static const RegEx e =
static const never_destroyed<RegEx> e{
!(BlankOrBreak() | RegEx("?,[]{}#&*!|>\'\"%@`", REGEX_OR) |
(RegEx("-:", REGEX_OR) + (Blank() | RegEx())));
(RegEx("-:", REGEX_OR) + (Blank() | RegEx())))};
return e;
}
inline const RegEx& EndScalar() {
static const RegEx e = RegEx(':') + (BlankOrBreak() | RegEx());
static const never_destroyed<RegEx> e{RegEx(':') +
(BlankOrBreak() | RegEx())};
return e;
}
inline const RegEx& EndScalarInFlow() {
static const RegEx e =
static const never_destroyed<RegEx> e{
(RegEx(':') + (BlankOrBreak() | RegEx() | RegEx(",]}", REGEX_OR))) |
RegEx(",?[]{}", REGEX_OR);
RegEx(",?[]{}", REGEX_OR)};
return e;
}

inline const RegEx& ScanScalarEndInFlow() {
static const RegEx e = (EndScalarInFlow() | (BlankOrBreak() + Comment()));
static const never_destroyed<RegEx> e{
(EndScalarInFlow() | (BlankOrBreak() + Comment()))};
return e;
}

inline const RegEx& ScanScalarEnd() {
static const RegEx e = EndScalar() | (BlankOrBreak() + Comment());
static const never_destroyed<RegEx> e{EndScalar() |
(BlankOrBreak() + Comment())};
return e;
}
inline const RegEx& EscSingleQuote() {
static const RegEx e = RegEx("\'\'");
static const never_destroyed<RegEx> e{RegEx("\'\'")};
return e;
}
inline const RegEx& EscBreak() {
static const RegEx e = RegEx('\\') + Break();
static const never_destroyed<RegEx> e{RegEx('\\') + Break()};
return e;
}

inline const RegEx& ChompIndicator() {
static const RegEx e = RegEx("+-", REGEX_OR);
static const never_destroyed<RegEx> e{RegEx("+-", REGEX_OR)};
return e;
}
inline const RegEx& Chomp() {
static const RegEx e = (ChompIndicator() + Digit()) |
(Digit() + ChompIndicator()) | ChompIndicator() |
Digit();
static const never_destroyed<RegEx> e{(ChompIndicator() + Digit()) |
(Digit() + ChompIndicator()) |
ChompIndicator() | Digit()};
return e;
}

Expand Down
43 changes: 43 additions & 0 deletions src/never_destroyed.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#ifndef NEVER_DESROYED_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define NEVER_DESROYED_H_62B23520_7C8E_11DE_8A39_0800200C9A66

#include <cstdint>
#include <utility>

namespace YAML {

// Wraps an underlying type T such that its storage is a direct member field of
// this object (i.e., without any indirection into the heap), but *unlike* most
// member fields T's destructor is never invoked. This is especially useful for
// function-local static variables that are not trivially destructable, because
// we shouldn't call their destructor at program exit because of the "static
// initialization order fiasco" (https://en.cppreference.com/cpp/language/siof)
// where the same logic applies to the indeterminate order of destruction.
template <typename T>
class never_destroyed {
public:
// Passes the constructor arguments along to T using perfect forwarding.
template <typename... Args>
explicit never_destroyed(Args&&... args) {
new (&storage_) T(std::forward<Args>(args)...);
}

// Non-copyable.
never_destroyed(const never_destroyed&) = delete;
void operator=(const never_destroyed&) = delete;
never_destroyed(never_destroyed&&) = delete;
void operator=(never_destroyed&&) = delete;

// No-op.
~never_destroyed() = default;

// Returns the underlying T reference.
operator const T&() const { return *reinterpret_cast<const T*>(&storage_); }

private:
alignas(T) std::uint8_t storage_[sizeof(T)];
};

} // namespace YAML

#endif // NEVER_DESROYED_H_62B23520_7C8E_11DE_8A39_0800200C9A66
3 changes: 2 additions & 1 deletion src/node_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <iterator>
#include <sstream>

#include "never_destroyed.h"
#include "yaml-cpp/exceptions.h"
#include "yaml-cpp/node/detail/memory.h"
#include "yaml-cpp/node/detail/node.h" // IWYU pragma: keep
Expand All @@ -16,7 +17,7 @@ namespace detail {
YAML_CPP_API std::atomic<size_t> node::m_amount{0};

const std::string& node_data::empty_scalar() {
static const std::string svalue;
static const never_destroyed<std::string> svalue;
return svalue;
}

Expand Down