diff --git a/include/toml++/toml.h b/include/toml++/toml.h index 52af453..147de5f 100644 --- a/include/toml++/toml.h +++ b/include/toml++/toml.h @@ -109,6 +109,7 @@ TOML_POP_WARNINGS // TOML_DISABLE_SPAM_WARNINGS #undef TOML_LANG_UNRELEASED #undef TOML_LAUNDER #undef TOML_LIKELY + #undef TOML_MAKE_BITOPS #undef TOML_MAKE_VERSION #undef TOML_MAY_THROW #undef TOML_MSVC diff --git a/include/toml++/toml_array.h b/include/toml++/toml_array.h index 3abd1cd..ecbb7dc 100644 --- a/include/toml++/toml_array.h +++ b/include/toml++/toml_array.h @@ -1029,6 +1029,7 @@ TOML_NAMESPACE_START /// \brief Prints the array out to a stream as formatted TOML. template friend std::basic_ostream& operator << (std::basic_ostream&, const array&); + // implemented in toml_default_formatter.h }; } TOML_NAMESPACE_END diff --git a/include/toml++/toml_common.h b/include/toml++/toml_common.h index 015a6f9..8919f6c 100644 --- a/include/toml++/toml_common.h +++ b/include/toml++/toml_common.h @@ -161,24 +161,6 @@ TOML_NAMESPACE_START // abi namespace template inline constexpr bool dependent_false = false; - #define TOML_P2S_DECL(Type) \ - template \ - inline void print_to_stream(Type, std::basic_ostream&) - TOML_P2S_DECL(int8_t); - TOML_P2S_DECL(int16_t); - TOML_P2S_DECL(int32_t); - TOML_P2S_DECL(int64_t); - TOML_P2S_DECL(uint8_t); - TOML_P2S_DECL(uint16_t); - TOML_P2S_DECL(uint32_t); - TOML_P2S_DECL(uint64_t); - TOML_P2S_DECL(float); - TOML_P2S_DECL(const date&); - TOML_P2S_DECL(const time&); - TOML_P2S_DECL(time_offset); - TOML_P2S_DECL(const date_time&); - #undef TOML_P2S_DECL - #if TOML_WINDOWS_COMPAT [[nodiscard]] TOML_API std::string narrow(std::wstring_view) noexcept; [[nodiscard]] TOML_API std::wstring widen(std::string_view) noexcept; @@ -314,7 +296,6 @@ TOML_IMPL_NAMESPACE_START template struct value_traits : value_traits {}; template struct value_traits : value_traits {}; - // integer value traits template struct integer_value_limits @@ -866,6 +847,43 @@ TOML_NAMESPACE_END TOML_NAMESPACE_START { + /// \brief Metadata associated with TOML values. + enum class value_flags : uint8_t + { + /// \brief None. + none, + + /// \brief Format integer values as binary. + format_as_binary = 1, + + /// \brief Format integer values as octal. + format_as_octal = 2, + + /// \brief Format integer values as hexadecimal. + format_as_hexadecimal = 3, + }; + TOML_MAKE_BITOPS(value_flags) + + /// \brief Format flags for modifying how TOML data is printed to streams. + enum class format_flags : uint8_t + { + /// \brief None. + none, + + /// \brief Dates and times will be emitted as quoted strings. + quote_dates_and_times = 1, + + /// \brief Strings will be emitted as single-quoted literal strings where possible. + allow_literal_strings = 2, + + /// \brief Strings containing newlines will be emitted as triple-quoted 'multi-line' strings where possible. + allow_multi_line_strings = 4, + + /// \brief Values with special format flags will be formatted accordingly. + allow_value_format_flags = 8, + }; + TOML_MAKE_BITOPS(format_flags) + /// \brief Pretty-prints the value of a node_type to a stream. /// /// \detail \cpp @@ -897,9 +915,39 @@ TOML_NAMESPACE_START } } - #if !defined(DOXYGEN) && !TOML_HEADER_ONLY + #ifndef DOXYGEN + + namespace impl + { + #define TOML_P2S_DECL(Type) \ + template \ + inline void print_to_stream(Type, std::basic_ostream&, value_flags = {}) + TOML_P2S_DECL(int8_t); + TOML_P2S_DECL(int16_t); + TOML_P2S_DECL(int32_t); + TOML_P2S_DECL(int64_t); + TOML_P2S_DECL(uint8_t); + TOML_P2S_DECL(uint16_t); + TOML_P2S_DECL(uint32_t); + TOML_P2S_DECL(uint64_t); + #undef TOML_P2S_DECL + + #define TOML_P2S_DECL(Type) \ + template \ + inline void print_to_stream(Type, std::basic_ostream&) + TOML_P2S_DECL(double); + TOML_P2S_DECL(const date&); + TOML_P2S_DECL(const time&); + TOML_P2S_DECL(time_offset); + TOML_P2S_DECL(const date_time&); + #undef TOML_P2S_DECL + } + + #if !TOML_HEADER_ONLY extern template TOML_API std::ostream& operator << (std::ostream&, node_type); - #endif + #endif // !TOML_HEADER_ONLY + + #endif // !DOXYGEN } TOML_NAMESPACE_END diff --git a/include/toml++/toml_default_formatter.h b/include/toml++/toml_default_formatter.h index fae8212..7027e12 100644 --- a/include/toml++/toml_default_formatter.h +++ b/include/toml++/toml_default_formatter.h @@ -319,7 +319,9 @@ TOML_NAMESPACE_START /// \brief The default flags for a default_formatter. static constexpr format_flags default_flags - = format_flags::allow_literal_strings | format_flags::allow_multi_line_strings; + = format_flags::allow_literal_strings + | format_flags::allow_multi_line_strings + | format_flags::allow_value_format_flags; /// \brief Constructs a default formatter and binds it to a TOML object. /// diff --git a/include/toml++/toml_formatter.h b/include/toml++/toml_formatter.h index 453b1d5..39e1e37 100644 --- a/include/toml++/toml_formatter.h +++ b/include/toml++/toml_formatter.h @@ -9,44 +9,6 @@ TOML_PUSH_WARNINGS TOML_DISABLE_SWITCH_WARNINGS -TOML_NAMESPACE_START -{ - /// \brief Format flags for modifying how TOML data is printed to streams. - enum class format_flags : uint8_t - { - /// \brief None. - none, - - /// \brief Dates and times will be emitted as quoted strings. - quote_dates_and_times = 1, - - /// \brief Strings will be emitted as single-quoted literal strings where possible. - allow_literal_strings = 2, - - /// \brief Strings containing newlines will be emitted as triple-quoted 'multi-line' strings where possible. - allow_multi_line_strings = 4, - }; - - [[nodiscard]] - TOML_ALWAYS_INLINE - TOML_ATTR(const) - TOML_ATTR(flatten) - constexpr format_flags operator & (format_flags lhs, format_flags rhs) noexcept - { - return static_cast(impl::unwrap_enum(lhs) & impl::unwrap_enum(rhs)); - } - - [[nodiscard]] - TOML_ALWAYS_INLINE - TOML_ATTR(const) - TOML_ATTR(flatten) - constexpr format_flags operator | (format_flags lhs, format_flags rhs) noexcept - { - return static_cast( impl::unwrap_enum(lhs) | impl::unwrap_enum(rhs) ); - } -} -TOML_NAMESPACE_END - TOML_IMPL_NAMESPACE_START { template @@ -56,7 +18,7 @@ TOML_IMPL_NAMESPACE_START const toml::node* source_; std::basic_ostream* stream_ = nullptr; format_flags flags_; - int8_t indent_; + int indent_; bool naked_newline_; protected: @@ -66,8 +28,8 @@ TOML_IMPL_NAMESPACE_START static constexpr size_t indent_columns = 4; static constexpr std::string_view indent_string = " "sv; - [[nodiscard]] int8_t indent() const noexcept { return indent_; } - void indent(int8_t level) noexcept { indent_ = level; } + [[nodiscard]] int indent() const noexcept { return indent_; } + void indent(int level) noexcept { indent_ = level; } void increase_indent() noexcept { indent_++; } void decrease_indent() noexcept { indent_--; } @@ -89,6 +51,12 @@ TOML_IMPL_NAMESPACE_START return (flags_ & format_flags::allow_multi_line_strings) != format_flags::none; } + [[nodiscard]] + bool value_format_flags_allowed() const noexcept + { + return (flags_ & format_flags::allow_value_format_flags) != format_flags::none; + } + [[nodiscard]] bool naked_newline() const noexcept { @@ -123,7 +91,7 @@ TOML_IMPL_NAMESPACE_START void print_indent() { - for (int8_t i = 0; i < indent_; i++) + for (int i = 0; i < indent_; i++) { print_to_stream(indent_string, *stream_); naked_newline_ = false; @@ -197,12 +165,7 @@ TOML_IMPL_NAMESPACE_START } else { - static constexpr auto is_dt = - std::is_same_v - || std::is_same_v - || std::is_same_v; - - if constexpr (is_dt) + if constexpr (is_one_of) { if (quote_dates_and_times()) { @@ -214,6 +177,27 @@ TOML_IMPL_NAMESPACE_START else print_to_stream(*val, *stream_); } + else if constexpr (is_one_of) + { + if (value_format_flags_allowed() && *val >= 0) + { + const auto fmt = val.flags() & value_flags::format_as_hexadecimal; + if (fmt != value_flags::none) + { + switch (fmt) + { + case value_flags::format_as_binary: print_to_stream("0b"sv, *stream_); break; + case value_flags::format_as_octal: print_to_stream("0o"sv, *stream_); break; + case value_flags::format_as_hexadecimal: print_to_stream("0x"sv, *stream_); break; + } + print_to_stream(*val, *stream_, fmt); + } + else + print_to_stream(*val, *stream_); + } + else + print_to_stream(*val, *stream_); + } else print_to_stream(*val, *stream_); diff --git a/include/toml++/toml_parser.hpp b/include/toml++/toml_parser.hpp index c298693..71a7cbb 100644 --- a/include/toml++/toml_parser.hpp +++ b/include/toml++/toml_parser.hpp @@ -2022,11 +2022,20 @@ TOML_IMPL_NAMESPACE_START if (has_any(has_p)) val = new value{ parse_hex_float() }; else if (has_any(has_x)) + { val = new value{ parse_integer<16>() }; + reinterpret_cast*>(val)->flags(value_flags::format_as_hexadecimal); + } else if (has_any(has_o)) + { val = new value{ parse_integer<8>() }; + reinterpret_cast*>(val)->flags(value_flags::format_as_octal); + } else if (has_any(has_b)) + { val = new value{ parse_integer<2>() }; + reinterpret_cast*>(val)->flags(value_flags::format_as_binary); + } else if (has_any(has_e) || (has_any(begins_zero | begins_digit) && chars[1] == U'.')) val = new value{ parse_float() }; else if (has_any(begins_sign)) @@ -2066,12 +2075,14 @@ TOML_IMPL_NAMESPACE_START // 0b10 case bzero_msk | has_b: val = new value{ parse_integer<2>() }; + reinterpret_cast*>(val)->flags(value_flags::format_as_binary); break; //=================== octal integers // 0o10 case bzero_msk | has_o: val = new value{ parse_integer<8>() }; + reinterpret_cast*>(val)->flags(value_flags::format_as_octal); break; //=================== decimal integers @@ -2090,6 +2101,7 @@ TOML_IMPL_NAMESPACE_START // 0x10 case bzero_msk | has_x: val = new value{ parse_integer<16>() }; + reinterpret_cast*>(val)->flags(value_flags::format_as_hexadecimal); break; //=================== decimal floats diff --git a/include/toml++/toml_preprocessor.h b/include/toml++/toml_preprocessor.h index d20e592..4519bac 100644 --- a/include/toml++/toml_preprocessor.h +++ b/include/toml++/toml_preprocessor.h @@ -467,6 +467,24 @@ is no longer necessary. #else #define TOML_ARM 0 #endif + +#define TOML_MAKE_BITOPS(type) \ + [[nodiscard]] \ + TOML_ALWAYS_INLINE \ + TOML_ATTR(const) \ + TOML_ATTR(flatten) \ + constexpr type operator & (type lhs, type rhs) noexcept \ + { \ + return static_cast(::toml::impl::unwrap_enum(lhs) & ::toml::impl::unwrap_enum(rhs)); \ + } \ + [[nodiscard]] \ + TOML_ALWAYS_INLINE \ + TOML_ATTR(const) \ + TOML_ATTR(flatten) \ + constexpr type operator | (type lhs, type rhs) noexcept \ + { \ + return static_cast(::toml::impl::unwrap_enum(lhs) | ::toml::impl::unwrap_enum(rhs)); \ + } //#==================================================================================================================== //# EXTENDED INT AND FLOAT TYPES diff --git a/include/toml++/toml_print_to_stream.h b/include/toml++/toml_print_to_stream.h index 715875d..131ba51 100644 --- a/include/toml++/toml_print_to_stream.h +++ b/include/toml++/toml_print_to_stream.h @@ -18,6 +18,9 @@ TOML_DISABLE_WARNINGS #endif TOML_ENABLE_WARNINGS +TOML_PUSH_WARNINGS +TOML_DISABLE_SWITCH_WARNINGS + TOML_IMPL_NAMESPACE_START { // Q: "why does print_to_stream() exist? why not just use ostream::write(), ostream::put() etc?" @@ -95,38 +98,85 @@ TOML_IMPL_NAMESPACE_START template <> inline constexpr size_t charconv_buffer_length = 3; // strlen("255") template - inline void print_integer_to_stream(T val, std::basic_ostream& stream) + inline void print_integer_to_stream(T val, std::basic_ostream& stream, value_flags format = {}) { static_assert( sizeof(Char) == 1, "The stream's underlying character type must be 1 byte in size." ); + if (!val) + { + print_to_stream('0', stream); + return; + } + + int base = 10; + if (format != value_flags::none && val >= T{}) + { + switch (format) + { + case value_flags::format_as_binary: base = 2; break; + case value_flags::format_as_octal: base = 8; break; + case value_flags::format_as_hexadecimal: base = 16; break; + default: break; + } + } + #if TOML_INT_CHARCONV - - char buf[charconv_buffer_length]; - const auto res = std::to_chars(buf, buf + sizeof(buf), val); + { + char buf[(sizeof(T) * CHAR_BIT)]; + const auto res = std::to_chars(buf, buf + sizeof(buf), val, base); const auto len = static_cast(res.ptr - buf); + if (base == 16) + { + for (size_t i = 0; i < len; i++) + if (buf[i] >= 'a') + buf[i] -= 32; + } print_to_stream(buf, len, stream); - + } #else + { + using unsigned_type = std::conditional_t<(sizeof(T) > sizeof(unsigned)), std::make_unsigned_t, unsigned>; + using cast_type = std::conditional_t, std::make_signed_t, unsigned_type>; - std::ostringstream ss; - ss.imbue(std::locale::classic()); - using cast_type = std::conditional_t, int64_t, uint64_t>; - ss << static_cast(val); - const auto str = std::move(ss).str(); - print_to_stream(str, stream); - + if TOML_UNLIKELY(format == value_flags::format_as_binary) + { + bool found_one = false; + const auto v = static_cast(val); + unsigned_type mask = unsigned_type{ 1 } << (sizeof(unsigned_type) * CHAR_BIT - 1u); + for (unsigned i = 0; i < sizeof(unsigned_type) * CHAR_BIT; i++) + { + if ((v & mask)) + { + print_to_stream('1', stream); + found_one = true; + } + else if (found_one) + print_to_stream('0', stream); + mask >>= 1; + } + } + else + { + std::ostringstream ss; + ss.imbue(std::locale::classic()); + ss << std::uppercase << std::setbase(base); + ss << static_cast(val); + const auto str = std::move(ss).str(); + print_to_stream(str, stream); + } + } #endif } - #define TOML_P2S_OVERLOAD(Type) \ - template \ - inline void print_to_stream(Type val, std::basic_ostream& stream) \ - { \ - static_assert(sizeof(Char) == 1); \ - print_integer_to_stream(val, stream); \ + #define TOML_P2S_OVERLOAD(Type) \ + template \ + inline void print_to_stream(Type val, std::basic_ostream& stream, value_flags format) \ + { \ + static_assert(sizeof(Char) == 1); \ + print_integer_to_stream(val, stream, format); \ } TOML_P2S_OVERLOAD(int8_t) @@ -432,3 +482,5 @@ TOML_NAMESPACE_START #endif } TOML_NAMESPACE_END + +TOML_POP_WARNINGS // TOML_DISABLE_SWITCH_WARNINGS diff --git a/include/toml++/toml_table.h b/include/toml++/toml_table.h index e294c6d..0bfff1a 100644 --- a/include/toml++/toml_table.h +++ b/include/toml++/toml_table.h @@ -1089,6 +1089,7 @@ TOML_NAMESPACE_START /// \brief Prints the table out to a stream as formatted TOML. template friend std::basic_ostream& operator << (std::basic_ostream&, const table&); + // implemented in toml_default_formatter.h }; #ifndef DOXYGEN diff --git a/include/toml++/toml_value.h b/include/toml++/toml_value.h index 83f604b..c6f58d7 100644 --- a/include/toml++/toml_value.h +++ b/include/toml++/toml_value.h @@ -200,6 +200,7 @@ TOML_NAMESPACE_START } ValueType val_; + value_flags flags_ = value_flags::none; public: @@ -234,14 +235,16 @@ TOML_NAMESPACE_START TOML_NODISCARD_CTOR value(const value& other) noexcept : node{ other }, - val_{ other.val_ } + val_{ other.val_ }, + flags_{ other.flags_ } {} /// \brief Move constructor. TOML_NODISCARD_CTOR value(value&& other) noexcept : node{ std::move(other) }, - val_{ std::move(other.val_) } + val_{ std::move(other.val_) }, + flags_{ other.flags_ } {} /// \brief Copy-assignment operator. @@ -249,6 +252,7 @@ TOML_NAMESPACE_START { node::operator=(rhs); val_ = rhs.val_; + flags_ = rhs.flags_; return *this; } @@ -259,6 +263,7 @@ TOML_NAMESPACE_START { node::operator=(std::move(rhs)); val_ = std::move(rhs.val_); + flags_ = rhs.flags_; } return *this; } @@ -369,9 +374,24 @@ TOML_NAMESPACE_START /// \brief Returns a reference to the underlying value (const overload). [[nodiscard]] explicit operator const value_type& () const& noexcept { return val_; } + /// \brief Returns the metadata flags associated with this value. + [[nodiscard]] value_flags flags() const noexcept + { + return flags_; + } + + /// \brief Sets the metadata flags associated with this value. + /// \returns A reference to the value object. + value& flags(value_flags new_flags) noexcept + { + flags_ = new_flags; + return *this; + } + /// \brief Prints the value out to a stream as formatted TOML. template friend std::basic_ostream& operator << (std::basic_ostream& lhs, const value& rhs); + // implemented in toml_default_formatter.h /// \brief Value-assignment operator. value& operator= (value_arg rhs) noexcept diff --git a/toml.hpp b/toml.hpp index f4cd98f..ac94b95 100644 --- a/toml.hpp +++ b/toml.hpp @@ -481,6 +481,24 @@ is no longer necessary. #define TOML_ARM 0 #endif +#define TOML_MAKE_BITOPS(type) \ + [[nodiscard]] \ + TOML_ALWAYS_INLINE \ + TOML_ATTR(const) \ + TOML_ATTR(flatten) \ + constexpr type operator & (type lhs, type rhs) noexcept \ + { \ + return static_cast(::toml::impl::unwrap_enum(lhs) & ::toml::impl::unwrap_enum(rhs)); \ + } \ + [[nodiscard]] \ + TOML_ALWAYS_INLINE \ + TOML_ATTR(const) \ + TOML_ATTR(flatten) \ + constexpr type operator | (type lhs, type rhs) noexcept \ + { \ + return static_cast(::toml::impl::unwrap_enum(lhs) | ::toml::impl::unwrap_enum(rhs)); \ + } + #ifdef __FLT16_MANT_DIG__ #if __FLT_RADIX__ == 2 \ && __FLT16_MANT_DIG__ == 11 \ @@ -743,24 +761,6 @@ TOML_NAMESPACE_START // abi namespace template inline constexpr bool dependent_false = false; - #define TOML_P2S_DECL(Type) \ - template \ - inline void print_to_stream(Type, std::basic_ostream&) - TOML_P2S_DECL(int8_t); - TOML_P2S_DECL(int16_t); - TOML_P2S_DECL(int32_t); - TOML_P2S_DECL(int64_t); - TOML_P2S_DECL(uint8_t); - TOML_P2S_DECL(uint16_t); - TOML_P2S_DECL(uint32_t); - TOML_P2S_DECL(uint64_t); - TOML_P2S_DECL(float); - TOML_P2S_DECL(const date&); - TOML_P2S_DECL(const time&); - TOML_P2S_DECL(time_offset); - TOML_P2S_DECL(const date_time&); - #undef TOML_P2S_DECL - #if TOML_WINDOWS_COMPAT [[nodiscard]] TOML_API std::string narrow(std::wstring_view) noexcept; [[nodiscard]] TOML_API std::wstring widen(std::string_view) noexcept; @@ -1293,6 +1293,32 @@ TOML_NAMESPACE_START TOML_ABI_NAMESPACE_END // TOML_LARGE_FILES + enum class value_flags : uint8_t + { + none, + + format_as_binary = 1, + + format_as_octal = 2, + + format_as_hexadecimal = 3, + }; + TOML_MAKE_BITOPS(value_flags) + + enum class format_flags : uint8_t + { + none, + + quote_dates_and_times = 1, + + allow_literal_strings = 2, + + allow_multi_line_strings = 4, + + allow_value_format_flags = 8, + }; + TOML_MAKE_BITOPS(format_flags) + template inline std::basic_ostream& operator << (std::basic_ostream& lhs, node_type rhs) { @@ -1309,9 +1335,39 @@ TOML_NAMESPACE_START } } - #if !defined(DOXYGEN) && !TOML_HEADER_ONLY + #ifndef DOXYGEN + + namespace impl + { + #define TOML_P2S_DECL(Type) \ + template \ + inline void print_to_stream(Type, std::basic_ostream&, value_flags = {}) + TOML_P2S_DECL(int8_t); + TOML_P2S_DECL(int16_t); + TOML_P2S_DECL(int32_t); + TOML_P2S_DECL(int64_t); + TOML_P2S_DECL(uint8_t); + TOML_P2S_DECL(uint16_t); + TOML_P2S_DECL(uint32_t); + TOML_P2S_DECL(uint64_t); + #undef TOML_P2S_DECL + + #define TOML_P2S_DECL(Type) \ + template \ + inline void print_to_stream(Type, std::basic_ostream&) + TOML_P2S_DECL(double); + TOML_P2S_DECL(const date&); + TOML_P2S_DECL(const time&); + TOML_P2S_DECL(time_offset); + TOML_P2S_DECL(const date_time&); + #undef TOML_P2S_DECL + } + + #if !TOML_HEADER_ONLY extern template TOML_API std::ostream& operator << (std::ostream&, node_type); - #endif + #endif // !TOML_HEADER_ONLY + + #endif // !DOXYGEN } TOML_NAMESPACE_END @@ -1637,6 +1693,9 @@ TOML_DISABLE_WARNINGS #endif TOML_ENABLE_WARNINGS +TOML_PUSH_WARNINGS +TOML_DISABLE_SWITCH_WARNINGS + TOML_IMPL_NAMESPACE_START { // Q: "why does print_to_stream() exist? why not just use ostream::write(), ostream::put() etc?" @@ -1714,38 +1773,85 @@ TOML_IMPL_NAMESPACE_START template <> inline constexpr size_t charconv_buffer_length = 3; // strlen("255") template - inline void print_integer_to_stream(T val, std::basic_ostream& stream) + inline void print_integer_to_stream(T val, std::basic_ostream& stream, value_flags format = {}) { static_assert( sizeof(Char) == 1, "The stream's underlying character type must be 1 byte in size." ); + if (!val) + { + print_to_stream('0', stream); + return; + } + + int base = 10; + if (format != value_flags::none && val >= T{}) + { + switch (format) + { + case value_flags::format_as_binary: base = 2; break; + case value_flags::format_as_octal: base = 8; break; + case value_flags::format_as_hexadecimal: base = 16; break; + default: break; + } + } + #if TOML_INT_CHARCONV - - char buf[charconv_buffer_length]; - const auto res = std::to_chars(buf, buf + sizeof(buf), val); + { + char buf[(sizeof(T) * CHAR_BIT)]; + const auto res = std::to_chars(buf, buf + sizeof(buf), val, base); const auto len = static_cast(res.ptr - buf); + if (base == 16) + { + for (size_t i = 0; i < len; i++) + if (buf[i] >= 'a') + buf[i] -= 32; + } print_to_stream(buf, len, stream); - + } #else + { + using unsigned_type = std::conditional_t<(sizeof(T) > sizeof(unsigned)), std::make_unsigned_t, unsigned>; + using cast_type = std::conditional_t, std::make_signed_t, unsigned_type>; - std::ostringstream ss; - ss.imbue(std::locale::classic()); - using cast_type = std::conditional_t, int64_t, uint64_t>; - ss << static_cast(val); - const auto str = std::move(ss).str(); - print_to_stream(str, stream); - + if TOML_UNLIKELY(format == value_flags::format_as_binary) + { + bool found_one = false; + const auto v = static_cast(val); + unsigned_type mask = unsigned_type{ 1 } << (sizeof(unsigned_type) * CHAR_BIT - 1u); + for (unsigned i = 0; i < sizeof(unsigned_type) * CHAR_BIT; i++) + { + if ((v & mask)) + { + print_to_stream('1', stream); + found_one = true; + } + else if (found_one) + print_to_stream('0', stream); + mask >>= 1; + } + } + else + { + std::ostringstream ss; + ss.imbue(std::locale::classic()); + ss << std::uppercase << std::setbase(base); + ss << static_cast(val); + const auto str = std::move(ss).str(); + print_to_stream(str, stream); + } + } #endif } - #define TOML_P2S_OVERLOAD(Type) \ - template \ - inline void print_to_stream(Type val, std::basic_ostream& stream) \ - { \ - static_assert(sizeof(Char) == 1); \ - print_integer_to_stream(val, stream); \ + #define TOML_P2S_OVERLOAD(Type) \ + template \ + inline void print_to_stream(Type val, std::basic_ostream& stream, value_flags format) \ + { \ + static_assert(sizeof(Char) == 1); \ + print_integer_to_stream(val, stream, format); \ } TOML_P2S_OVERLOAD(int8_t) @@ -2012,6 +2118,8 @@ TOML_NAMESPACE_START } TOML_NAMESPACE_END +TOML_POP_WARNINGS // TOML_DISABLE_SWITCH_WARNINGS + #endif //------------------------------------------------------------------------------- ↑ toml_print_to_stream.h ---- #if 1 //---------- ↓ toml_node.h ------------------------------------------------------------------------------------ @@ -2667,6 +2775,7 @@ TOML_NAMESPACE_START } ValueType val_; + value_flags flags_ = value_flags::none; public: @@ -2689,19 +2798,22 @@ TOML_NAMESPACE_START TOML_NODISCARD_CTOR value(const value& other) noexcept : node{ other }, - val_{ other.val_ } + val_{ other.val_ }, + flags_{ other.flags_ } {} TOML_NODISCARD_CTOR value(value&& other) noexcept : node{ std::move(other) }, - val_{ std::move(other.val_) } + val_{ std::move(other.val_) }, + flags_{ other.flags_ } {} value& operator= (const value& rhs) noexcept { node::operator=(rhs); val_ = rhs.val_; + flags_ = rhs.flags_; return *this; } @@ -2711,6 +2823,7 @@ TOML_NAMESPACE_START { node::operator=(std::move(rhs)); val_ = std::move(rhs.val_); + flags_ = rhs.flags_; } return *this; } @@ -2797,8 +2910,21 @@ TOML_NAMESPACE_START [[nodiscard]] explicit operator value_type && () && noexcept { return std::move(val_); } [[nodiscard]] explicit operator const value_type& () const& noexcept { return val_; } + [[nodiscard]] value_flags flags() const noexcept + { + return flags_; + } + + value& flags(value_flags new_flags) noexcept + { + flags_ = new_flags; + return *this; + } + template friend std::basic_ostream& operator << (std::basic_ostream& lhs, const value& rhs); + // implemented in toml_default_formatter.h + value& operator= (value_arg rhs) noexcept { if constexpr (std::is_same_v) @@ -3810,6 +3936,7 @@ TOML_NAMESPACE_START template friend std::basic_ostream& operator << (std::basic_ostream&, const array&); + // implemented in toml_default_formatter.h }; } TOML_NAMESPACE_END @@ -4323,6 +4450,7 @@ TOML_NAMESPACE_START template friend std::basic_ostream& operator << (std::basic_ostream&, const table&); + // implemented in toml_default_formatter.h }; #ifndef DOXYGEN @@ -5741,39 +5869,6 @@ TOML_IMPL_NAMESPACE_END TOML_PUSH_WARNINGS TOML_DISABLE_SWITCH_WARNINGS -TOML_NAMESPACE_START -{ - enum class format_flags : uint8_t - { - none, - - quote_dates_and_times = 1, - - allow_literal_strings = 2, - - allow_multi_line_strings = 4, - }; - - [[nodiscard]] - TOML_ALWAYS_INLINE - TOML_ATTR(const) - TOML_ATTR(flatten) - constexpr format_flags operator & (format_flags lhs, format_flags rhs) noexcept - { - return static_cast(impl::unwrap_enum(lhs) & impl::unwrap_enum(rhs)); - } - - [[nodiscard]] - TOML_ALWAYS_INLINE - TOML_ATTR(const) - TOML_ATTR(flatten) - constexpr format_flags operator | (format_flags lhs, format_flags rhs) noexcept - { - return static_cast( impl::unwrap_enum(lhs) | impl::unwrap_enum(rhs) ); - } -} -TOML_NAMESPACE_END - TOML_IMPL_NAMESPACE_START { template @@ -5783,7 +5878,7 @@ TOML_IMPL_NAMESPACE_START const toml::node* source_; std::basic_ostream* stream_ = nullptr; format_flags flags_; - int8_t indent_; + int indent_; bool naked_newline_; protected: @@ -5793,8 +5888,8 @@ TOML_IMPL_NAMESPACE_START static constexpr size_t indent_columns = 4; static constexpr std::string_view indent_string = " "sv; - [[nodiscard]] int8_t indent() const noexcept { return indent_; } - void indent(int8_t level) noexcept { indent_ = level; } + [[nodiscard]] int indent() const noexcept { return indent_; } + void indent(int level) noexcept { indent_ = level; } void increase_indent() noexcept { indent_++; } void decrease_indent() noexcept { indent_--; } [[nodiscard]] @@ -5815,6 +5910,12 @@ TOML_IMPL_NAMESPACE_START return (flags_ & format_flags::allow_multi_line_strings) != format_flags::none; } + [[nodiscard]] + bool value_format_flags_allowed() const noexcept + { + return (flags_ & format_flags::allow_value_format_flags) != format_flags::none; + } + [[nodiscard]] bool naked_newline() const noexcept { @@ -5849,7 +5950,7 @@ TOML_IMPL_NAMESPACE_START void print_indent() { - for (int8_t i = 0; i < indent_; i++) + for (int i = 0; i < indent_; i++) { print_to_stream(indent_string, *stream_); naked_newline_ = false; @@ -5923,12 +6024,7 @@ TOML_IMPL_NAMESPACE_START } else { - static constexpr auto is_dt = - std::is_same_v - || std::is_same_v - || std::is_same_v; - - if constexpr (is_dt) + if constexpr (is_one_of) { if (quote_dates_and_times()) { @@ -5940,6 +6036,27 @@ TOML_IMPL_NAMESPACE_START else print_to_stream(*val, *stream_); } + else if constexpr (is_one_of) + { + if (value_format_flags_allowed() && *val >= 0) + { + const auto fmt = val.flags() & value_flags::format_as_hexadecimal; + if (fmt != value_flags::none) + { + switch (fmt) + { + case value_flags::format_as_binary: print_to_stream("0b"sv, *stream_); break; + case value_flags::format_as_octal: print_to_stream("0o"sv, *stream_); break; + case value_flags::format_as_hexadecimal: print_to_stream("0x"sv, *stream_); break; + } + print_to_stream(*val, *stream_, fmt); + } + else + print_to_stream(*val, *stream_); + } + else + print_to_stream(*val, *stream_); + } else print_to_stream(*val, *stream_); @@ -6261,7 +6378,9 @@ TOML_NAMESPACE_START public: static constexpr format_flags default_flags - = format_flags::allow_literal_strings | format_flags::allow_multi_line_strings; + = format_flags::allow_literal_strings + | format_flags::allow_multi_line_strings + | format_flags::allow_value_format_flags; TOML_NODISCARD_CTOR explicit default_formatter(const toml::node& source, format_flags flags = default_flags) noexcept @@ -10500,11 +10619,20 @@ TOML_IMPL_NAMESPACE_START if (has_any(has_p)) val = new value{ parse_hex_float() }; else if (has_any(has_x)) + { val = new value{ parse_integer<16>() }; + reinterpret_cast*>(val)->flags(value_flags::format_as_hexadecimal); + } else if (has_any(has_o)) + { val = new value{ parse_integer<8>() }; + reinterpret_cast*>(val)->flags(value_flags::format_as_octal); + } else if (has_any(has_b)) + { val = new value{ parse_integer<2>() }; + reinterpret_cast*>(val)->flags(value_flags::format_as_binary); + } else if (has_any(has_e) || (has_any(begins_zero | begins_digit) && chars[1] == U'.')) val = new value{ parse_float() }; else if (has_any(begins_sign)) @@ -10544,12 +10672,14 @@ TOML_IMPL_NAMESPACE_START // 0b10 case bzero_msk | has_b: val = new value{ parse_integer<2>() }; + reinterpret_cast*>(val)->flags(value_flags::format_as_binary); break; //=================== octal integers // 0o10 case bzero_msk | has_o: val = new value{ parse_integer<8>() }; + reinterpret_cast*>(val)->flags(value_flags::format_as_octal); break; //=================== decimal integers @@ -10568,6 +10698,7 @@ TOML_IMPL_NAMESPACE_START // 0x10 case bzero_msk | has_x: val = new value{ parse_integer<16>() }; + reinterpret_cast*>(val)->flags(value_flags::format_as_hexadecimal); break; //=================== decimal floats @@ -11703,6 +11834,7 @@ TOML_POP_WARNINGS // TOML_DISABLE_SPAM_WARNINGS #undef TOML_LANG_UNRELEASED #undef TOML_LAUNDER #undef TOML_LIKELY + #undef TOML_MAKE_BITOPS #undef TOML_MAKE_VERSION #undef TOML_MAY_THROW #undef TOML_MSVC