diff options
author | Idan Horowitz <idan.horowitz@gmail.com> | 2022-02-24 20:08:48 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-02-27 20:37:57 +0100 |
commit | feb00b7105bbb55ba28b3fe8b6c526695f77bc84 (patch) | |
tree | 4be5bcdf63171729618deff8194db8fbcdb5bc3f /AK/JsonArraySerializer.h | |
parent | 6682afb5d4d6b7455f85fa3a8f07dd14cae4211e (diff) | |
download | serenity-feb00b7105bbb55ba28b3fe8b6c526695f77bc84.zip |
Everywhere: Make JSON serialization fallible
This allows us to eliminate a major source of infallible allocation in
the Kernel, as well as lay down the groundwork for OOM fallibility in
userland.
Diffstat (limited to 'AK/JsonArraySerializer.h')
-rw-r--r-- | AK/JsonArraySerializer.h | 206 |
1 files changed, 150 insertions, 56 deletions
diff --git a/AK/JsonArraySerializer.h b/AK/JsonArraySerializer.h index fa0f3086af..3a757765a1 100644 --- a/AK/JsonArraySerializer.h +++ b/AK/JsonArraySerializer.h @@ -1,132 +1,216 @@ /* * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org> + * Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once -#include <AK/JsonValue.h> +#include <AK/Error.h> +#include <AK/Try.h> + +#ifndef KERNEL +# include <AK/JsonValue.h> +#endif namespace AK { template<typename Builder> +inline constexpr bool IsLegacyBuilder = requires(Builder builder) { builder.try_append('\0'); }; + +template<typename Builder = void> class JsonObjectSerializer; -template<typename Builder> +template<typename Builder = void> class JsonArraySerializer { public: - explicit JsonArraySerializer(Builder& builder) - : m_builder(builder) + static ErrorOr<JsonArraySerializer> try_create(Builder& builder) + { + if constexpr (IsLegacyBuilder<Builder>) + TRY(builder.try_append('[')); + else + TRY(builder.append('[')); + return JsonArraySerializer { builder }; + } + + JsonArraySerializer(JsonArraySerializer&& other) + : m_builder(other.m_builder) + , m_empty(other.m_empty) + , m_finished(exchange(other.m_finished, true)) { - (void)m_builder.append('['); } JsonArraySerializer(const JsonArraySerializer&) = delete; - JsonArraySerializer(JsonArraySerializer&&) = delete; ~JsonArraySerializer() { - if (!m_finished) - finish(); + VERIFY(m_finished); } #ifndef KERNEL - void add(const JsonValue& value) + ErrorOr<void> add(const JsonValue& value) { - begin_item(); + TRY(begin_item()); value.serialize(m_builder); + return {}; } #endif - void add(StringView value) + ErrorOr<void> add(StringView value) { - begin_item(); - (void)m_builder.append('"'); - (void)m_builder.append_escaped_for_json(value); - (void)m_builder.append('"'); + TRY(begin_item()); + if constexpr (IsLegacyBuilder<Builder>) { + TRY(m_builder.try_append('"')); + TRY(m_builder.try_append_escaped_for_json(value)); + TRY(m_builder.try_append('"')); + } else { + TRY(m_builder.append('"')); + TRY(m_builder.append_escaped_for_json(value)); + TRY(m_builder.append('"')); + } + return {}; } - void add(const String& value) - { - begin_item(); - (void)m_builder.append('"'); - (void)m_builder.append_escaped_for_json(value); - (void)m_builder.append('"'); +#ifndef KERNEL + ErrorOr<void> add(const String& value) + { + TRY(begin_item()); + if constexpr (IsLegacyBuilder<Builder>) { + TRY(m_builder.try_append('"')); + TRY(m_builder.try_append_escaped_for_json(value)); + TRY(m_builder.try_append('"')); + } else { + TRY(m_builder.append('"')); + TRY(m_builder.append_escaped_for_json(value)); + TRY(m_builder.append('"')); + } + return {}; } +#endif - void add(const char* value) + ErrorOr<void> add(const char* value) { - begin_item(); - (void)m_builder.append('"'); - (void)m_builder.append_escaped_for_json(value); - (void)m_builder.append('"'); + TRY(begin_item()); + if constexpr (IsLegacyBuilder<Builder>) { + TRY(m_builder.try_append('"')); + TRY(m_builder.try_append_escaped_for_json(value)); + TRY(m_builder.try_append('"')); + } else { + TRY(m_builder.append('"')); + TRY(m_builder.append_escaped_for_json(value)); + TRY(m_builder.append('"')); + } + return {}; } - void add(bool value) + ErrorOr<void> add(bool value) { - begin_item(); - (void)m_builder.append(value ? "true"sv : "false"sv); + TRY(begin_item()); + if constexpr (IsLegacyBuilder<Builder>) + TRY(m_builder.try_append(value ? "true"sv : "false"sv)); + else + TRY(m_builder.append(value ? "true"sv : "false"sv)); + return {}; } - void add(int value) + ErrorOr<void> add(int value) { - begin_item(); - (void)m_builder.appendff("{}", value); + TRY(begin_item()); + if constexpr (IsLegacyBuilder<Builder>) + TRY(m_builder.try_appendff("{}", value)); + else + TRY(m_builder.appendff("{}", value)); + return {}; } - void add(unsigned value) + ErrorOr<void> add(unsigned value) { - begin_item(); - (void)m_builder.appendff("{}", value); + TRY(begin_item()); + if constexpr (IsLegacyBuilder<Builder>) + TRY(m_builder.try_appendff("{}", value)); + else + TRY(m_builder.appendff("{}", value)); + return {}; } - void add(long value) + ErrorOr<void> add(long value) { - begin_item(); - (void)m_builder.appendff("{}", value); + TRY(begin_item()); + if constexpr (IsLegacyBuilder<Builder>) + TRY(m_builder.try_appendff("{}", value)); + else + TRY(m_builder.appendff("{}", value)); + return {}; } - void add(long unsigned value) + ErrorOr<void> add(long unsigned value) { - begin_item(); - (void)m_builder.appendff("{}", value); + TRY(begin_item()); + if constexpr (IsLegacyBuilder<Builder>) + TRY(m_builder.try_appendff("{}", value)); + else + TRY(m_builder.appendff("{}", value)); + return {}; } - void add(long long value) + ErrorOr<void> add(long long value) { - begin_item(); - (void)m_builder.appendff("{}", value); + TRY(begin_item()); + if constexpr (IsLegacyBuilder<Builder>) + TRY(m_builder.try_appendff("{}", value)); + else + TRY(m_builder.appendff("{}", value)); + return {}; } - void add(long long unsigned value) + ErrorOr<void> add(long long unsigned value) { - begin_item(); - (void)m_builder.appendff("{}", value); + TRY(begin_item()); + if constexpr (IsLegacyBuilder<Builder>) + TRY(m_builder.try_appendff("{}", value)); + else + TRY(m_builder.appendff("{}", value)); + return {}; } - JsonArraySerializer<Builder> add_array() + ErrorOr<JsonArraySerializer<Builder>> add_array() { - begin_item(); - return JsonArraySerializer(m_builder); + TRY(begin_item()); + return JsonArraySerializer::try_create(m_builder); } // Implemented in JsonObjectSerializer.h - JsonObjectSerializer<Builder> add_object(); + ErrorOr<JsonObjectSerializer<Builder>> add_object(); - void finish() + ErrorOr<void> finish() { VERIFY(!m_finished); m_finished = true; - (void)m_builder.append(']'); + if constexpr (IsLegacyBuilder<Builder>) + TRY(m_builder.try_append(']')); + else + TRY(m_builder.append(']')); + return {}; } private: - void begin_item() + explicit JsonArraySerializer(Builder& builder) + : m_builder(builder) + { + } + + ErrorOr<void> begin_item() { - if (!m_empty) - (void)m_builder.append(','); + VERIFY(!m_finished); + if (!m_empty) { + if constexpr (IsLegacyBuilder<Builder>) + TRY(m_builder.try_append(',')); + else + TRY(m_builder.append(',')); + } m_empty = false; + return {}; } Builder& m_builder; @@ -134,6 +218,16 @@ private: bool m_finished { false }; }; +// Template magic to allow for JsonArraySerializer<>::try_create(...) - Blame CxByte +template<> +struct JsonArraySerializer<void> { + template<typename Builder> + static ErrorOr<JsonArraySerializer<Builder>> try_create(Builder& builder) + { + return JsonArraySerializer<Builder>::try_create(builder); + } +}; + } using AK::JsonArraySerializer; |