diff --git a/src/emitterutils.cpp b/src/emitterutils.cpp index 74e58abdd..644897307 100644 --- a/src/emitterutils.cpp +++ b/src/emitterutils.cpp @@ -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 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 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; diff --git a/src/exp.h b/src/exp.h index c8837f0f8..98dba6b28 100644 --- a/src/exp.h +++ b/src/exp.h @@ -10,6 +10,7 @@ #include #include +#include "never_destroyed.h" #include "regex_yaml.h" #include "stream.h" @@ -21,126 +22,137 @@ namespace YAML { namespace Exp { // misc inline const RegEx& Empty() { - static const RegEx e; + static const never_destroyed e; return e; } inline const RegEx& Space() { - static const RegEx e = RegEx(' '); + static const never_destroyed e{RegEx(' ')}; return e; } inline const RegEx& Tab() { - static const RegEx e = RegEx('\t'); + static const never_destroyed e{RegEx('\t')}; return e; } inline const RegEx& Blank() { - static const RegEx e = Space() | Tab(); + static const never_destroyed e{Space() | Tab()}; return e; } inline const RegEx& Break() { - static const RegEx e = RegEx('\n') | RegEx("\r\n") | RegEx('\r'); + static const never_destroyed e{RegEx('\n') | RegEx("\r\n") | + RegEx('\r')}; return e; } inline const RegEx& BlankOrBreak() { - static const RegEx e = Blank() | Break(); + static const never_destroyed e{Blank() | Break()}; return e; } inline const RegEx& Digit() { - static const RegEx e = RegEx('0', '9'); + static const never_destroyed e{RegEx('0', '9')}; return e; } inline const RegEx& Alpha() { - static const RegEx e = RegEx('a', 'z') | RegEx('A', 'Z'); + static const never_destroyed e{RegEx('a', 'z') | RegEx('A', 'Z')}; return e; } inline const RegEx& AlphaNumeric() { - static const RegEx e = Alpha() | Digit(); + static const never_destroyed e{Alpha() | Digit()}; return e; } inline const RegEx& Word() { - static const RegEx e = AlphaNumeric() | RegEx('-'); + static const never_destroyed e{AlphaNumeric() | RegEx('-')}; return e; } inline const RegEx& Hex() { - static const RegEx e = Digit() | RegEx('A', 'F') | RegEx('a', 'f'); + static const never_destroyed 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 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 e{RegEx("\xEF\xBB\xBF")}; return e; } // actual tags inline const RegEx& DocStart() { - static const RegEx e = RegEx("---") + (BlankOrBreak() | RegEx()); + static const never_destroyed e{RegEx("---") + + (BlankOrBreak() | RegEx())}; return e; } inline const RegEx& DocEnd() { - static const RegEx e = RegEx("...") + (BlankOrBreak() | RegEx()); + static const never_destroyed e{RegEx("...") + + (BlankOrBreak() | RegEx())}; return e; } inline const RegEx& DocIndicator() { - static const RegEx e = DocStart() | DocEnd(); + static const never_destroyed e{DocStart() | DocEnd()}; return e; } inline const RegEx& BlockEntry() { - static const RegEx e = RegEx('-') + (BlankOrBreak() | RegEx()); + static const never_destroyed e{RegEx('-') + + (BlankOrBreak() | RegEx())}; return e; } inline const RegEx& Key() { - static const RegEx e = RegEx('?') + BlankOrBreak(); + static const never_destroyed e{RegEx('?') + BlankOrBreak()}; return e; } inline const RegEx& KeyInFlow() { - static const RegEx e = RegEx('?') + BlankOrBreak(); + static const never_destroyed e{RegEx('?') + BlankOrBreak()}; return e; } inline const RegEx& Value() { - static const RegEx e = RegEx(':') + (BlankOrBreak() | RegEx()); + static const never_destroyed e{RegEx(':') + + (BlankOrBreak() | RegEx())}; return e; } inline const RegEx& ValueInFlow() { - static const RegEx e = RegEx(':') + (BlankOrBreak() | RegEx(",]}", REGEX_OR)); + static const never_destroyed e{ + RegEx(':') + (BlankOrBreak() | RegEx(",]}", REGEX_OR))}; return e; } inline const RegEx& ValueInJSONFlow() { - static const RegEx e = RegEx(':'); + static const never_destroyed e{RegEx(':')}; return e; } inline const RegEx& Ampersand() { - static const RegEx e = RegEx('&'); + static const never_destroyed e{RegEx('&')}; return e; } inline const RegEx Comment() { - static const RegEx e = RegEx('#'); + static const never_destroyed e{RegEx('#')}; return e; } inline const RegEx& Anchor() { - static const RegEx e = !(RegEx("[]{},", REGEX_OR) | BlankOrBreak()); + static const never_destroyed e{ + !(RegEx("[]{},", REGEX_OR) | BlankOrBreak())}; return e; } inline const RegEx& AnchorEnd() { - static const RegEx e = RegEx("?:,]}%@`", REGEX_OR) | BlankOrBreak(); + static const never_destroyed 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 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 e{Word() | + RegEx("#;/?:@&=+$_.~*'()", REGEX_OR) | + (RegEx('%') + Hex() + Hex())}; return e; } @@ -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 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 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 e{RegEx(':') + + (BlankOrBreak() | RegEx())}; return e; } inline const RegEx& EndScalarInFlow() { - static const RegEx e = + static const never_destroyed 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 e{ + (EndScalarInFlow() | (BlankOrBreak() + Comment()))}; return e; } inline const RegEx& ScanScalarEnd() { - static const RegEx e = EndScalar() | (BlankOrBreak() + Comment()); + static const never_destroyed e{EndScalar() | + (BlankOrBreak() + Comment())}; return e; } inline const RegEx& EscSingleQuote() { - static const RegEx e = RegEx("\'\'"); + static const never_destroyed e{RegEx("\'\'")}; return e; } inline const RegEx& EscBreak() { - static const RegEx e = RegEx('\\') + Break(); + static const never_destroyed e{RegEx('\\') + Break()}; return e; } inline const RegEx& ChompIndicator() { - static const RegEx e = RegEx("+-", REGEX_OR); + static const never_destroyed e{RegEx("+-", REGEX_OR)}; return e; } inline const RegEx& Chomp() { - static const RegEx e = (ChompIndicator() + Digit()) | - (Digit() + ChompIndicator()) | ChompIndicator() | - Digit(); + static const never_destroyed e{(ChompIndicator() + Digit()) | + (Digit() + ChompIndicator()) | + ChompIndicator() | Digit()}; return e; } diff --git a/src/never_destroyed.h b/src/never_destroyed.h new file mode 100644 index 000000000..928d6355e --- /dev/null +++ b/src/never_destroyed.h @@ -0,0 +1,43 @@ +#ifndef NEVER_DESROYED_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NEVER_DESROYED_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#include +#include + +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 +class never_destroyed { + public: + // Passes the constructor arguments along to T using perfect forwarding. + template + explicit never_destroyed(Args&&... args) { + new (&storage_) T(std::forward(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(&storage_); } + + private: + alignas(T) std::uint8_t storage_[sizeof(T)]; +}; + +} // namespace YAML + +#endif // NEVER_DESROYED_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/node_data.cpp b/src/node_data.cpp index dbfb94191..fde6ce59c 100644 --- a/src/node_data.cpp +++ b/src/node_data.cpp @@ -3,6 +3,7 @@ #include #include +#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 @@ -16,7 +17,7 @@ namespace detail { YAML_CPP_API std::atomic node::m_amount{0}; const std::string& node_data::empty_scalar() { - static const std::string svalue; + static const never_destroyed svalue; return svalue; }