serenity/AK/JsonObjectSerializer.h
sin-ack c70f45ff44 Everywhere: Explicitly specify the size in StringView constructors
This commit moves the length calculations out to be directly on the
StringView users. This is an important step towards the goal of removing
StringView(char const*), as it moves the responsibility of calculating
the size of the string to the user of the StringView (which will prevent
naive uses causing OOB access).
2022-07-12 23:11:35 +02:00

271 lines
7.2 KiB
C++

/*
* 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/Error.h>
#include <AK/JsonArraySerializer.h>
#include <AK/Try.h>
#ifndef KERNEL
# include <AK/JsonValue.h>
#endif
namespace AK {
template<typename Builder>
class JsonObjectSerializer {
public:
static ErrorOr<JsonObjectSerializer> try_create(Builder& builder)
{
if constexpr (IsLegacyBuilder<Builder>)
TRY(builder.try_append('{'));
else
TRY(builder.append('{'));
return JsonObjectSerializer { builder };
}
JsonObjectSerializer(JsonObjectSerializer&& other)
: m_builder(other.m_builder)
, m_empty(other.m_empty)
, m_finished(exchange(other.m_finished, true))
{
}
JsonObjectSerializer(JsonObjectSerializer const&) = delete;
~JsonObjectSerializer()
{
VERIFY(m_finished);
}
#ifndef KERNEL
ErrorOr<void> add(StringView key, JsonValue const& value)
{
TRY(begin_item(key));
value.serialize(m_builder);
return {};
}
#endif
ErrorOr<void> add(StringView key, StringView value)
{
TRY(begin_item(key));
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 {};
}
#ifndef KERNEL
ErrorOr<void> add(StringView key, String const& value)
{
TRY(begin_item(key));
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
ErrorOr<void> add(StringView key, char const* value)
{
TRY(begin_item(key));
if constexpr (IsLegacyBuilder<Builder>) {
TRY(m_builder.try_append('"'));
TRY(m_builder.try_append_escaped_for_json({ value, strlen(value) }));
TRY(m_builder.try_append('"'));
} else {
TRY(m_builder.append('"'));
TRY(m_builder.append_escaped_for_json({ value, strlen(value) }));
TRY(m_builder.append('"'));
}
return {};
}
ErrorOr<void> add(StringView key, bool value)
{
TRY(begin_item(key));
if constexpr (IsLegacyBuilder<Builder>)
TRY(m_builder.try_append(value ? "true" : "false"));
else
TRY(m_builder.append(value ? "true" : "false"));
return {};
}
ErrorOr<void> add(StringView key, int value)
{
TRY(begin_item(key));
if constexpr (IsLegacyBuilder<Builder>)
TRY(m_builder.try_appendff("{}", value));
else
TRY(m_builder.appendff("{}", value));
return {};
}
ErrorOr<void> add(StringView key, unsigned value)
{
TRY(begin_item(key));
if constexpr (IsLegacyBuilder<Builder>)
TRY(m_builder.try_appendff("{}", value));
else
TRY(m_builder.appendff("{}", value));
return {};
}
ErrorOr<void> add(StringView key, long value)
{
TRY(begin_item(key));
if constexpr (IsLegacyBuilder<Builder>)
TRY(m_builder.try_appendff("{}", value));
else
TRY(m_builder.appendff("{}", value));
return {};
}
ErrorOr<void> add(StringView key, long unsigned value)
{
TRY(begin_item(key));
if constexpr (IsLegacyBuilder<Builder>)
TRY(m_builder.try_appendff("{}", value));
else
TRY(m_builder.appendff("{}", value));
return {};
}
ErrorOr<void> add(StringView key, long long value)
{
TRY(begin_item(key));
if constexpr (IsLegacyBuilder<Builder>)
TRY(m_builder.try_appendff("{}", value));
else
TRY(m_builder.appendff("{}", value));
return {};
}
ErrorOr<void> add(StringView key, long long unsigned value)
{
TRY(begin_item(key));
if constexpr (IsLegacyBuilder<Builder>)
TRY(m_builder.try_appendff("{}", value));
else
TRY(m_builder.appendff("{}", value));
return {};
}
#ifndef KERNEL
ErrorOr<void> add(StringView key, float value)
{
TRY(begin_item(key));
if constexpr (IsLegacyBuilder<Builder>)
TRY(m_builder.try_appendff("{}", value));
else
TRY(m_builder.appendff("{}", value));
return {};
}
ErrorOr<void> add(StringView key, double value)
{
TRY(begin_item(key));
if constexpr (IsLegacyBuilder<Builder>)
TRY(m_builder.try_appendff("{}", value));
else
TRY(m_builder.appendff("{}", value));
return {};
}
#endif
ErrorOr<JsonArraySerializer<Builder>> add_array(StringView key)
{
TRY(begin_item(key));
return JsonArraySerializer<Builder>::try_create(m_builder);
}
ErrorOr<JsonObjectSerializer<Builder>> add_object(StringView key)
{
TRY(begin_item(key));
return JsonObjectSerializer::try_create(m_builder);
}
ErrorOr<void> finish()
{
VERIFY(!m_finished);
m_finished = true;
if constexpr (IsLegacyBuilder<Builder>)
TRY(m_builder.try_append('}'));
else
TRY(m_builder.append('}'));
return {};
}
private:
explicit JsonObjectSerializer(Builder& builder)
: m_builder(builder)
{
}
ErrorOr<void> begin_item(StringView key)
{
VERIFY(!m_finished);
if (!m_empty) {
if constexpr (IsLegacyBuilder<Builder>)
TRY(m_builder.try_append(','));
else
TRY(m_builder.append(','));
}
m_empty = false;
if constexpr (IsLegacyBuilder<Builder>) {
TRY(m_builder.try_append('"'));
TRY(m_builder.try_append_escaped_for_json(key));
TRY(m_builder.try_append("\":"));
} else {
TRY(m_builder.append('"'));
TRY(m_builder.append_escaped_for_json(key));
TRY(m_builder.append("\":"));
}
return {};
}
Builder& m_builder;
bool m_empty { true };
bool m_finished { false };
};
// Template magic to allow for JsonObjectSerializer<>::try_create(...) - Blame CxByte
template<>
struct JsonObjectSerializer<void> {
template<typename Builder>
static ErrorOr<JsonObjectSerializer<Builder>> try_create(Builder& builder)
{
return JsonObjectSerializer<Builder>::try_create(builder);
}
};
template<typename Builder>
ErrorOr<JsonObjectSerializer<Builder>> JsonArraySerializer<Builder>::add_object()
{
TRY(begin_item());
return JsonObjectSerializer<Builder>::try_create(m_builder);
}
}
using AK::JsonObjectSerializer;