summaryrefslogtreecommitdiff
path: root/AK/JsonArraySerializer.h
diff options
context:
space:
mode:
authorIdan Horowitz <idan.horowitz@gmail.com>2022-02-24 20:08:48 +0200
committerAndreas Kling <kling@serenityos.org>2022-02-27 20:37:57 +0100
commitfeb00b7105bbb55ba28b3fe8b6c526695f77bc84 (patch)
tree4be5bcdf63171729618deff8194db8fbcdb5bc3f /AK/JsonArraySerializer.h
parent6682afb5d4d6b7455f85fa3a8f07dd14cae4211e (diff)
downloadserenity-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.h206
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;