#pragma once
+#include <cassert>
#include <charconv>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
+#include <exception>
+#include <functional>
#include <initializer_list>
#include <ios>
+#include <iterator>
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <string_view>
#include <system_error>
+#include <type_traits>
#include <unordered_map>
#include <variant>
#include <vector>
-struct JsonValue;
+enum class ResolveResult : bool
+{
+ Finished = false,
+ MoreToResolve = true,
+};
+
+template <typename T> class LazyVec final
+{
+ private:
+ struct Internals
+ {
+ std::vector<T> resolved_values;
+ explicit Internals(std::vector<T> &&resolved_values)
+ : resolved_values(std::move(resolved_values))
+ {
+ }
+ explicit Internals(std::initializer_list<T> resolved_values = {})
+ : resolved_values(resolved_values)
+ {
+ }
+ virtual ~Internals() = default;
+ virtual ResolveResult resolve_more()
+ {
+ return ResolveResult::Finished;
+ }
+ };
+ template <
+ typename Iterator, typename Sentinel,
+ typename = std::enable_if_t<
+ std::is_base_of_v<
+ std::input_iterator_tag,
+ typename std::iterator_traits<Iterator>::iterator_category> &&
+ std::is_same_v<
+ const typename std::iterator_traits<Iterator>::value_type,
+ const T>>>
+ struct LazyInternalsIter final : public Internals
+ {
+ struct State final
+ {
+ Iterator cur;
+ Sentinel end;
+ State(Iterator &&cur, Sentinel &&end) noexcept
+ : cur(std::move(cur)), end(std::move(end))
+ {
+ }
+ };
+ std::optional<State> state;
+ LazyInternalsIter(Iterator &&cur, Sentinel &&end) noexcept
+ : state(std::in_place, std::move(cur), std::move(end))
+ {
+ if (state->cur == state->end)
+ {
+ state = std::nullopt;
+ }
+ }
+ LazyInternalsIter(const LazyInternalsIter &) = delete;
+ LazyInternalsIter &operator=(const LazyInternalsIter &) = delete;
+ virtual ResolveResult resolve_more() override
+ {
+ if (!state)
+ {
+ return ResolveResult::Finished;
+ }
+ this->resolved_values.emplace_back(*state->cur++);
+ if (state->cur == state->end)
+ {
+ state = std::nullopt;
+ return ResolveResult::Finished;
+ }
+ return ResolveResult::MoreToResolve;
+ }
+ };
+ template <
+ typename Container,
+ typename Iterator = decltype(std::begin(std::declval<Container>())),
+ typename Sentinel = decltype(std::end(std::declval<Container>())),
+ typename = decltype(LazyInternalsIter<Iterator, Sentinel>::cur)>
+ struct LazyInternalsContainer final : public Internals
+ {
+ struct State final
+ {
+ Container container;
+ Iterator cur; // must come after container
+ Sentinel end; // must come after container
+ explicit State(Container &&container) noexcept
+ : container(std::move(container)),
+ cur(std::begin(this->container)),
+ end(std::begin(this->container))
+ {
+ }
+ State(const State &) = delete;
+ State &operator=(const State &) = delete;
+ };
+ std::optional<State> state;
+ explicit LazyInternalsContainer(Container &&container) noexcept
+ : state(std::in_place, std::move(container))
+ {
+ if (state->cur == state->end)
+ {
+ state = std::nullopt;
+ }
+ }
+ virtual ResolveResult resolve_more() override
+ {
+ if (!state)
+ {
+ return ResolveResult::Finished;
+ }
+ this->resolved_values.emplace_back(*state->cur++);
+ if (state->cur == state->end)
+ {
+ state = std::nullopt;
+ return ResolveResult::Finished;
+ }
+ return ResolveResult::MoreToResolve;
+ }
+ };
+ template <typename Fn,
+ typename = std::enable_if_t<std::is_convertible_v<
+ decltype(std::declval<Fn &>()()), std::optional<T>>>>
+ struct LazyInternalsFn final : public Internals
+ {
+ std::optional<Fn> fn;
+ explicit LazyInternalsFn(Fn &&fn) noexcept
+ : fn(std::in_place, std::move(fn))
+ {
+ }
+ virtual ResolveResult resolve_more() override
+ {
+ if (!fn)
+ {
+ return ResolveResult::Finished;
+ }
+ if (std::optional<T> value = (*fn)())
+ {
+ this->resolved_values.emplace_back(std::move(*value));
+ return ResolveResult::MoreToResolve;
+ }
+ fn = std::nullopt;
+ return ResolveResult::Finished;
+ }
+ };
+ std::shared_ptr<Internals> internals;
+
+ public:
+ LazyVec() noexcept : internals(std::make_shared<Internals>())
+ {
+ }
+ LazyVec(std::vector<T> values) noexcept
+ : internals(std::make_shared<Internals>(std::move(values)))
+ {
+ }
+ LazyVec(std::initializer_list<T> values) noexcept
+ : internals(std::make_shared<Internals>(values))
+ {
+ }
+ template <typename Iterator, typename Sentinel,
+ typename = decltype(LazyInternalsIter<Iterator, Sentinel>::cur)>
+ LazyVec(Iterator begin, Sentinel end) noexcept
+ : internals(std::make_shared<LazyInternalsIter<Iterator, Sentinel>>(
+ std::move(begin), std::move(end)))
+ {
+ }
+ template <typename Container, typename = decltype(LazyInternalsContainer<
+ Container>::container)>
+ LazyVec(Container container) noexcept
+ : internals(std::make_shared<LazyInternalsContainer<Container>>(
+ std::move(container)))
+ {
+ }
+ template <typename Fn, typename = void,
+ typename = decltype(LazyInternalsFn<Fn>::fn)>
+ LazyVec(Fn fn) noexcept
+ : internals(std::make_shared<LazyInternalsFn<Fn>>(std::move(fn)))
+ {
+ }
+
+ private:
+ static bool resolve_at(const std::shared_ptr<Internals> &internals,
+ std::size_t index) noexcept
+ {
+ while (index >= internals->resolved_values.size())
+ {
+ switch (internals->resolve_more())
+ {
+ case ResolveResult::Finished:
+ goto end;
+ case ResolveResult::MoreToResolve:
+ continue;
+ }
+ }
+ end:
+ return index < internals->resolved_values.size();
+ }
+
+ public:
+ bool resolve_at(std::size_t index) noexcept
+ {
+ return resolve_at(internals, index);
+ }
+ const T &at(std::size_t index) noexcept
+ {
+ if (!resolve_at(index))
+ {
+ assert(!"index out of bounds");
+ std::terminate();
+ }
+ return internals->resolved_values[index];
+ }
+ const T &operator[](std::size_t index) noexcept
+ {
+ return at(index);
+ }
+ std::size_t size_lower_bound() noexcept
+ {
+ return internals->resolved_values.size();
+ }
+ ResolveResult resolve_more()
+ {
+ return internals->resolve_more();
+ }
+ const std::vector<T> &resolve()
+ {
+ while (true)
+ {
+ switch (resolve_more())
+ {
+ case ResolveResult::Finished:
+ return internals->resolved_values;
+ case ResolveResult::MoreToResolve:
+ continue;
+ }
+ }
+ }
+ class const_iterator final
+ {
+ friend class LazyVec;
+
+ private:
+ std::shared_ptr<Internals> internals;
+ std::size_t index = 0;
+ bool resolve() const noexcept
+ {
+ assert(internals);
+ if (!internals)
+ {
+ std::terminate();
+ }
+ return resolve_at(internals, index);
+ }
+ explicit const_iterator(
+ const std::shared_ptr<Internals> &internals) noexcept
+ : internals(internals)
+ {
+ }
+
+ public:
+ using difference_type = std::ptrdiff_t;
+ using value_type = T;
+ using pointer = const T *;
+ using reference = const T &;
+ using iterator_category = std::forward_iterator_tag;
+
+ const_iterator() noexcept = default;
+ const_iterator &operator++()
+ {
+ assert(internals);
+ ++index;
+ return *this;
+ }
+ const_iterator operator++(int)
+ {
+ auto retval = *this;
+ operator++();
+ return retval;
+ }
+ pointer operator->() const noexcept
+ {
+ if (at_end())
+ {
+ assert(!"tried to dereference an end() iterator");
+ std::terminate();
+ }
+ return std::addressof(internals->resolved_values[index]);
+ }
+ reference operator*() const noexcept
+ {
+ return *operator->();
+ }
+ bool at_end() const noexcept
+ {
+ return !resolve();
+ }
+ bool operator==(const const_iterator &rhs) const noexcept
+ {
+ if (rhs.internals)
+ {
+ if (internals)
+ {
+ assert(internals == rhs.internals);
+ return index == rhs.index;
+ }
+ return rhs.at_end();
+ }
+ if (internals)
+ {
+ return at_end();
+ }
+ return true;
+ }
+ bool operator!=(const const_iterator &rhs) const noexcept
+ {
+ return !operator==(rhs);
+ }
+ };
+ const_iterator begin() const noexcept
+ {
+ return const_iterator(internals);
+ }
+ const_iterator cbegin() const noexcept
+ {
+ return const_iterator(internals);
+ }
+ const_iterator end() const noexcept
+ {
+ return const_iterator();
+ }
+ const_iterator cend() const noexcept
+ {
+ return const_iterator();
+ }
+ template <typename R, typename Fn> LazyVec<R> filter_map(Fn &&fn) noexcept
+ {
+ struct FilterMapper final
+ {
+ const_iterator iter;
+ Fn fn;
+ std::optional<R> operator()() noexcept
+ {
+ while (iter != const_iterator())
+ {
+ if (std::optional<R> retval = fn(*iter++))
+ {
+ return retval;
+ }
+ }
+ return std::nullopt;
+ }
+ };
+ return LazyVec<R>(FilterMapper{.iter = begin(), .fn = std::move(fn)});
+ }
+ template <typename R, typename Fn> LazyVec<R> map(Fn fn) noexcept
+ {
+ return filter_map<R>(
+ [fn](const T &value) -> std::optional<R> { return fn(value); });
+ }
+ template <typename Fn> LazyVec filter(Fn fn) noexcept
+ {
+ return filter_map<T>([fn](const T &value) -> std::optional<T> {
+ if (fn(value))
+ return value;
+ return std::nullopt;
+ });
+ }
+};
-using JsonString = std::string;
-using JsonFloat = double;
-using JsonNull = std::nullptr_t;
-using JsonArray = std::vector<JsonValue>;
-using JsonMap = std::vector<std::pair<std::string, JsonValue>>;
+struct JsonValue;
struct JsonValue final
{
- std::variant<JsonString, JsonFloat, JsonNull, std::unique_ptr<JsonArray>,
- std::unique_ptr<JsonMap>>
- value;
+ using String = std::string;
+ using Number = double;
+ using Object = LazyVec<std::pair<String, JsonValue>>;
+ using Array = LazyVec<JsonValue>;
+ using Bool = bool;
+ using Null = std::nullptr_t;
+ std::variant<String, Number, Object, Array, Bool, Null> value;
constexpr JsonValue() noexcept : value(nullptr)
{
}
- constexpr JsonValue(JsonNull) noexcept : value(nullptr)
+ JsonValue(String value) noexcept : value(std::move(value))
+ {
+ }
+ JsonValue(const char *value) noexcept : value(std::string(value))
{
}
- constexpr JsonValue(JsonFloat value) noexcept : value(value)
+ JsonValue(std::string_view value) noexcept : value(std::string(value))
{
}
- JsonValue(JsonString value) noexcept : value(std::move(value))
+ constexpr JsonValue(Number value) noexcept : value(value)
{
}
- JsonValue(std::unique_ptr<JsonArray> value) noexcept
- : value(std::move(value))
+ JsonValue(Object value) noexcept : value(std::move(value))
{
}
- JsonValue(JsonArray value) noexcept
- : value(std::make_unique<JsonArray>(std::move(value)))
+ JsonValue(Array value) noexcept : value(std::move(value))
{
}
- JsonValue(std::unique_ptr<JsonMap> value) noexcept
- : value(std::move(value))
+ constexpr JsonValue(Bool value) noexcept : value(value)
{
}
- JsonValue(JsonMap value) noexcept
- : value(std::make_unique<JsonMap>(std::move(value)))
+ constexpr JsonValue(Null) noexcept : value(nullptr)
+ {
+ }
+ constexpr JsonValue(char) noexcept = delete;
+ constexpr JsonValue(char16_t) noexcept = delete;
+ constexpr JsonValue(char32_t) noexcept = delete;
+#define JSON_VALUE_NUM(T) \
+ constexpr JsonValue(T value) noexcept \
+ : JsonValue(static_cast<double>(value)) \
+ { \
+ }
+ JSON_VALUE_NUM(float)
+ JSON_VALUE_NUM(long double)
+ JSON_VALUE_NUM(unsigned char)
+ JSON_VALUE_NUM(signed char)
+ JSON_VALUE_NUM(unsigned short)
+ JSON_VALUE_NUM(short)
+ JSON_VALUE_NUM(unsigned int)
+ JSON_VALUE_NUM(int)
+ JSON_VALUE_NUM(unsigned long)
+ JSON_VALUE_NUM(long)
+ JSON_VALUE_NUM(unsigned long long)
+ JSON_VALUE_NUM(long long)
+#undef JSON_VALUE_NUM
+ template <typename T, typename = decltype(JsonValue(std::declval<T &&>()))>
+ constexpr JsonValue(std::optional<T> value) noexcept : value(nullptr)
+ {
+ if (value)
+ {
+ *this = JsonValue(std::move(*value));
+ }
+ }
+ template <typename T,
+ typename = decltype(JsonValue(std::declval<const T &>()))>
+ JsonValue(LazyVec<T> value) noexcept
+ : value(value.template map<JsonValue>(
+ [](const T &value) { return JsonValue(value); }))
+ {
+ }
+ template <typename T,
+ typename = decltype(JsonValue(std::declval<const T &>()))>
+ JsonValue(std::vector<T> value) noexcept
+ : JsonValue(LazyVec<T>(std::move(value)))
{
}
/// decode a JsonString from WTF-8 to the encoding logically used in JSON:
}
}
};
- template <typename WriteStringView>
- void write(WriteStringView &&write_fn) const
+
+ private:
+ template <typename WriteStringView> struct Visitor final
{
- struct Visitor final
+ WriteStringView &write_fn;
+ bool pretty;
+ std::size_t indent = 0;
+ void write(std::string_view str)
{
- WriteStringView &write_fn;
- void write(std::string_view str)
- {
- write_fn(str);
- }
- void write(char ch)
+ write_fn(str);
+ }
+ void write(char ch)
+ {
+ write_fn(std::string_view(&ch, 1));
+ }
+ void write_indent()
+ {
+ for (std::size_t i = 0; i < indent; i++)
{
- write_fn(std::string_view(&ch, 1));
+ write(" ");
}
- void operator()(const JsonString &value)
+ }
+ void operator()(const String &value)
+ {
+ write('\"');
+ JsonStringDecoder decoder(value);
+ while (auto value_opt = decoder.next())
{
- write('\"');
- JsonStringDecoder decoder(value);
- while (auto value_opt = decoder.next())
+ std::uint16_t value = *value_opt;
+ switch (value)
{
- std::uint16_t value = *value_opt;
- switch (value)
+ case '\"':
+ case '\\':
+ write('\\');
+ write(static_cast<char>(value));
+ break;
+ case '\b':
+ write("\\b");
+ break;
+ case '\f':
+ write("\\f");
+ break;
+ case '\n':
+ write("\\n");
+ break;
+ case '\r':
+ write("\\r");
+ break;
+ case '\t':
+ write("\\t");
+ break;
+ default:
+ if (value >= 0x20 && value <= 0x7E)
{
- case '\"':
- case '\\':
- write('\\');
write(static_cast<char>(value));
- break;
- case '\b':
- write("\\b");
- break;
- case '\f':
- write("\\f");
- break;
- case '\n':
- write("\\n");
- break;
- case '\r':
- write("\\r");
- break;
- case '\t':
- write("\\t");
- break;
- default:
- if (value >= 0x20 && value <= 0x7E)
- {
- write(static_cast<char>(value));
- }
- else
+ }
+ else
+ {
+ static constexpr char hex_digits[] =
+ "0123456789ABCDEF";
+ write("\\u");
+ for (int i = 0; i < 4; i++)
{
- static constexpr char hex_digits[] =
- "0123456789ABCDEF";
- write("\\u");
- for (int i = 0; i < 4; i++)
- {
- write(hex_digits[value >> 12]);
- value <<= 4;
- }
+ write(hex_digits[value >> 12]);
+ value <<= 4;
}
- break;
}
+ break;
}
- write('"');
}
- void operator()(JsonFloat value)
+ write('"');
+ }
+ void operator()(Number value)
+ {
+ if (std::isnan(value))
+ {
+ write("NaN");
+ return;
+ }
+ if (std::signbit(value))
{
- if (std::isnan(value))
+ write('-');
+ value = -value;
+ }
+ if (std::isinf(value))
+ {
+ write("Infinity");
+ return;
+ }
+ if (value == 0)
+ {
+ write("0");
+ return;
+ }
+ using Buf = std::array<char, 32>;
+ auto try_format = [&](Buf &buf, bool e_format, int prec) -> bool {
+ int result;
+ if (e_format)
{
- write("NaN");
- return;
+ result = std::snprintf(&buf[0], buf.size(), "%1.*e", prec,
+ value);
}
- if (std::signbit(value))
+ else
{
- write('-');
- value = -value;
+ result = std::snprintf(&buf[0], buf.size(), "%1.*f", prec,
+ value);
}
- if (std::isinf(value))
+ if (result <= 0)
+ return false;
+ double parsed_value = std::strtod(&buf[0], nullptr);
+ if (parsed_value != value)
{
- write("Infinity");
- return;
+ // not precise enough
+ return false;
}
- if (value == 0)
+ return true;
+ };
+ Buf final_buf = {};
+ std::optional<std::string_view> final;
+ std::size_t end_prec = final_buf.size();
+ for (std::size_t prec = 0;
+ prec < final_buf.size() && prec < end_prec; prec++)
+ {
+ Buf buf;
+ if (try_format(buf, true, prec))
{
- write("0");
- return;
- }
- using Buf = std::array<char, 32>;
- auto try_format = [&](Buf &buf, bool e_format,
- int prec) -> bool {
- int result;
- if (e_format)
+ std::string_view str(&buf[0]);
+ if (!final || str.size() < final->size())
{
- result = std::snprintf(&buf[0], buf.size(), "%1.*e",
- prec, value);
+ final_buf = buf;
+ final = std::string_view(&final_buf[0], str.size());
+ end_prec = prec + 3;
}
- else
- {
- result = std::snprintf(&buf[0], buf.size(), "%1.*f",
- prec, value);
- }
- if (result <= 0)
- return false;
- double parsed_value = std::strtod(&buf[0], nullptr);
- if (parsed_value != value)
- {
- // not precise enough
- return false;
- }
- return true;
- };
- Buf final_buf = {};
- std::optional<std::string_view> final;
- std::size_t end_prec = final_buf.size();
- for (std::size_t prec = 0;
- prec < final_buf.size() && prec < end_prec; prec++)
+ }
+ if (try_format(buf, false, prec))
{
- Buf buf;
- if (try_format(buf, true, prec))
+ std::string_view str(&buf[0]);
+ if (!final || str.size() < final->size())
{
- std::string_view str(&buf[0]);
- if (!final || str.size() < final->size())
- {
- final_buf = buf;
- final =
- std::string_view(&final_buf[0], str.size());
- end_prec = prec + 3;
- }
- }
- if (try_format(buf, false, prec))
- {
- std::string_view str(&buf[0]);
- if (!final || str.size() < final->size())
- {
- final_buf = buf;
- final =
- std::string_view(&final_buf[0], str.size());
- end_prec = prec + 3;
- }
+ final_buf = buf;
+ final = std::string_view(&final_buf[0], str.size());
+ end_prec = prec + 3;
}
}
- if (final_buf[0] == '.')
- {
- write('0');
- }
- write(*final);
}
- void operator()(JsonNull)
+ if (final_buf[0] == '.')
{
- write("null");
+ write('0');
}
- void operator()(const std::unique_ptr<JsonArray> &value)
+ write(*final);
+ }
+ void operator()(Bool value)
+ {
+ write(value ? "true" : "false");
+ }
+ void operator()(Null)
+ {
+ write("null");
+ }
+ template <typename T, typename Fn>
+ void write_container_inner(const LazyVec<T> &value, Fn fn)
+ {
+ indent++;
+ std::string_view sep{};
+ bool any = false;
+ for (auto &v : value)
{
- write('[');
- std::string_view sep{};
- for (auto &i : *value)
+ any = true;
+ write(sep);
+ if (pretty)
{
- write(sep);
- sep = ",";
- std::visit(*this, i.value);
+ write('\n');
+ write_indent();
}
- write(']');
+ sep = ",";
+ fn(v);
}
- void operator()(const std::unique_ptr<JsonMap> &value)
+ indent--;
+ if (pretty && any)
{
- write('{');
- std::string_view sep{};
- for (auto &[k, v] : *value)
- {
- write(sep);
- sep = ",";
- operator()(k);
- write(':');
- std::visit(*this, v.value);
- }
- write('}');
+ write('\n');
+ write_indent();
}
- };
- std::visit(Visitor{.write_fn = write_fn}, value);
+ }
+ void operator()(const Array &value)
+ {
+ write('[');
+ write_container_inner(value, [&](const JsonValue &value) {
+ std::visit(*this, value.value);
+ });
+ write(']');
+ }
+ void operator()(const Object &value)
+ {
+ write('{');
+ write_container_inner(
+ value, [&](const std::pair<String, JsonValue> &value) {
+ operator()(value.first);
+ write(':');
+ if (pretty)
+ {
+ write(' ');
+ }
+ std::visit(*this, value.second.value);
+ });
+ write('}');
+ }
+ };
+
+ public:
+ template <typename WriteStringView>
+ void write(WriteStringView &&write_fn, bool pretty = false) const
+ {
+ std::visit(
+ Visitor<WriteStringView>{
+ .write_fn = write_fn,
+ .pretty = pretty,
+ },
+ value);
}
+ struct PrettyJsonValue;
+ PrettyJsonValue pretty(bool pretty = true) const noexcept;
friend std::ostream &operator<<(std::ostream &os, const JsonValue &self)
{
self.write([&](std::string_view str) { os << str; });
return os;
}
};
+
+struct JsonValue::PrettyJsonValue final
+{
+ JsonValue value;
+ bool pretty;
+ friend std::ostream &operator<<(std::ostream &os,
+ const PrettyJsonValue &self)
+ {
+ self.value.write([&](std::string_view str) { os << str; },
+ self.pretty);
+ return os;
+ }
+};
+
+inline auto JsonValue::pretty(bool pretty) const noexcept -> PrettyJsonValue
+{
+ return {.value = *this, .pretty = pretty};
+}
\ No newline at end of file