aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.ucl/emit.ccm
diff options
context:
space:
mode:
Diffstat (limited to 'nihil.ucl/emit.ccm')
-rw-r--r--nihil.ucl/emit.ccm179
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();
}
};