finish adding json output
[benchmarks.git] / src / json.h
index 63abd3a0a881073fdc3512c0e0e15c30e264e8b4..5aee708c739588c58fd6982f62639149092d8d5f 100644 (file)
 #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:
@@ -198,188 +608,249 @@ struct JsonValue final
             }
         }
     };
-    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