diff options
Diffstat (limited to 'nihil.ucl/emit.ccm')
| -rw-r--r-- | nihil.ucl/emit.ccm | 179 |
1 files changed, 85 insertions, 94 deletions
diff --git a/nihil.ucl/emit.ccm b/nihil.ucl/emit.ccm index b88f8e7..64b8f4f 100644 --- a/nihil.ucl/emit.ccm +++ b/nihil.ucl/emit.ccm @@ -1,110 +1,83 @@ -/* - * This source code is released into the public domain. - */ - +// This source code is released into the public domain. module; -#include <array> -#include <charconv> -#include <cstdlib> -#include <format> -#include <iterator> -#include <iosfwd> -#include <span> -#include <string> -#include <utility> - #include <ucl.h> export module nihil.ucl:emit; +import nihil.std; import :object; namespace nihil::ucl { -export enum struct emitter { +export enum struct emitter : std::uint8_t { configuration = UCL_EMIT_CONFIG, compact_json = UCL_EMIT_JSON_COMPACT, json = UCL_EMIT_JSON, yaml = UCL_EMIT_YAML, }; -/* - * Wrap ucl_emitter_functions for a particular output iterator type. - * - * We can't throw exceptions here since we're called from C code. The emit - * functions return an integer value, but it's not really clear what this is - * for and the C API seems to mostly ignore it. So, we just eat errors and - * keep going. - */ -template<std::output_iterator<char> Iterator> -struct emit_wrapper { - emit_wrapper(Iterator iterator_) - : iterator(std::move(iterator_)) - {} - - static auto append_character(unsigned char c, std::size_t nchars, - void *ud) - noexcept -> int - try { - auto *self = static_cast<emit_wrapper *>(ud); - - while (nchars--) - *self->iterator++ = static_cast<char>(c); +// Wrap ucl_emitter_functions for a particular output iterator type. +// +// We can't throw exceptions here since we're called from C code. The emit +// functions return an integer value, but it's not really clear what this is +// for and the C API seems to mostly ignore it. So, we just eat errors and +// keep going. +template <std::output_iterator<char> Iterator> +struct emit_wrapper +{ + explicit emit_wrapper(Iterator iterator) + : m_iterator(std::move(iterator)) + { + } + static auto + append_character(unsigned char const c, std::size_t nchars, void *const ud) noexcept -> int + try { + auto &self = check_magic(ud); + self.m_iterator = + std::ranges::fill_n(self.m_iterator, nchars, static_cast<char>(c)); return 0; } catch (...) { return 0; } - static auto append_len(unsigned char const *str, std::size_t len, - void *ud) - noexcept -> int + static auto append_len(unsigned char const *const str, std::size_t const len, + void *const ud) noexcept -> int try { - auto *self = static_cast<emit_wrapper *>(ud); - - for (auto c : std::span(str, len)) - *self->iterator++ = static_cast<char>(c); - + auto &self = check_magic(ud); + self.m_iterator = std::ranges::copy(std::span(str, len), self.m_iterator).out; return 0; } catch (...) { return 0; } - static auto append_int(std::int64_t value, void *ud) - noexcept -> int + static auto append_int(std::int64_t const value, void *const ud) noexcept -> int try { - auto constexpr bufsize = - std::numeric_limits<std::int64_t>::digits10; - auto buf = std::array<char, bufsize>(); + auto &self = check_magic(ud); - auto *self = static_cast<emit_wrapper *>(ud); - auto result = std::to_chars(buf.data(), buf.data() + buf.size(), - value, 10); - - if (result.ec == std::errc()) - for (auto c : std::span(buf.data(), result.ptr)) - *self->iterator++ = c; + auto buf = std::array<char, std::numeric_limits<std::int64_t>::digits10>(); + auto result = std::to_chars(begin(buf), end(buf), value, 10); + if (result.ec == std::errc()) { + auto chars = std::span(buf.data(), result.ptr); + self.m_iterator = std::ranges::copy(chars, self.m_iterator).out; + } return 0; } catch (...) { return 0; } - static auto append_double(double value, void *ud) - noexcept -> int + static auto append_double(double const value, void *const ud) noexcept -> int try { - auto constexpr bufsize = - std::numeric_limits<double>::digits10; - auto buf = std::array<char, bufsize>(); - - auto *self = static_cast<emit_wrapper *>(ud); - auto result = std::to_chars(buf.data(), buf.data() + buf.size(), - value); + auto &self = check_magic(ud); - if (result.ec == std::errc()) - for (auto c : std::span(buf.data(), result.ptr)) - *self->iterator++ = c; + auto buf = std::array<char, std::numeric_limits<double>::digits10>(); + auto result = std::to_chars(begin(buf), end(buf), value); + if (result.ec == std::errc()) { + auto chars = std::span(buf.data(), result.ptr); + self.m_iterator = std::ranges::copy(chars, self.m_iterator).out; + } return 0; } catch (...) { @@ -124,40 +97,61 @@ struct emit_wrapper { return ret; } + [[nodiscard]] auto iterator(this emit_wrapper &self) -> Iterator & + { + return self.m_iterator; + } + + [[nodiscard]] auto iterator(this emit_wrapper const &self) -> Iterator const & + { + return self.m_iterator; + } + private: - Iterator iterator{}; + Iterator m_iterator{}; + std::uint64_t m_magic = wrapper_magic; + + // Harden against memory errors. + static constexpr auto wrapper_magic = std::uint32_t{0x57524150}; + + static auto check_magic(void *p) -> emit_wrapper & + { + auto *ret = static_cast<emit_wrapper *>(p); + if (ret->m_magic != wrapper_magic) + throw std::runtime_error("Invalid emit_wrapper pointer"); + return *ret; + } }; -export auto emit(object const &object, emitter format, - std::output_iterator<char> auto &&it) - -> void +export auto +emit(object const &object, emitter const format, std::output_iterator<char> auto &&it) -> void { auto ucl_format = static_cast<ucl_emitter>(format); auto wrapper = emit_wrapper(it); auto functions = wrapper.get_functions(); - ::ucl_object_emit_full(object.get_ucl_object(), ucl_format, - &functions, nullptr); + ::ucl_object_emit_full(object.get_ucl_object(), ucl_format, &functions, nullptr); } -/* - * Basic ostream printer for UCL; default to JSON since it's probably what - * most people expect. - */ -export auto operator<<(std::ostream &, object const &) -> std::ostream &; +// Basic ostream printer for UCL; default to JSON since it's probably what +// most people expect. Note that most derived UCL types override this. +export auto operator<<(std::ostream &stream, object const &o) -> std::ostream & +{ + emit(o, emitter::json, std::ostream_iterator<char>(stream)); + return stream; +} } // namespace nihil::ucl -/* - * Specialisation of std::formatter<> for object. - */ -template<std::derived_from<nihil::ucl::object> T> +// Specialisation of std::formatter<> for object. Note that most derived +// UCL types override this. +template <std::derived_from<nihil::ucl::object> T> struct std::formatter<T, char> { nihil::ucl::emitter emitter = nihil::ucl::emitter::json; - template<class ParseContext> - constexpr ParseContext::iterator parse(ParseContext& ctx) + template <class ParseContext> + constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator { auto it = ctx.begin(); auto end = ctx.end(); @@ -179,8 +173,7 @@ struct std::formatter<T, char> case '}': return it; default: - throw std::format_error("Invalid format string " - "for UCL object"); + throw std::format_error("Invalid format string for UCL object"); } ++it; @@ -189,9 +182,8 @@ struct std::formatter<T, char> return it; } - template<class FmtContext> - FmtContext::iterator format(nihil::ucl::object const &o, - FmtContext& ctx) const + template <class FmtContext> + auto format(nihil::ucl::object const &o, FmtContext &ctx) const -> FmtContext::iterator { // We can't use emit() here since the context iterator is not // an std::output_iterator. @@ -202,8 +194,7 @@ struct std::formatter<T, char> auto wrapper = nihil::ucl::emit_wrapper(out); auto functions = wrapper.get_functions(); - ::ucl_object_emit_full(o.get_ucl_object(), ucl_format, - &functions, nullptr); - return out; + ::ucl_object_emit_full(o.get_ucl_object(), ucl_format, &functions, nullptr); + return wrapper.iterator(); } }; |
