/* * Copyright (c) 2018-2021, Andreas Kling * Copyright (c) 2023, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include namespace IPC { template ErrorOr encode(Encoder&, T const&) { static_assert(DependentFalse, "Base IPC::encode() was instantiated"); VERIFY_NOT_REACHED(); } class Encoder { public: explicit Encoder(MessageBuffer& buffer) : m_buffer(buffer) { } template ErrorOr encode(T const& value); ErrorOr extend_capacity(size_t capacity) { return m_buffer.data.try_ensure_capacity(m_buffer.data.size() + capacity); } void append(u8 value) { m_buffer.data.unchecked_append(value); } ErrorOr append(u8 const* values, size_t count) { TRY(extend_capacity(count)); m_buffer.data.unchecked_append(values, count); return {}; } ErrorOr append_file_descriptor(int fd) { auto auto_fd = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) AutoCloseFileDescriptor(fd))); return m_buffer.fds.try_append(move(auto_fd)); } ErrorOr encode_size(size_t size); private: MessageBuffer& m_buffer; }; template ErrorOr encode(Encoder& encoder, T const& value) { TRY(encoder.extend_capacity(sizeof(T))); if constexpr (sizeof(T) == 1) { encoder.append(static_cast(value)); } else if constexpr (sizeof(T) == 2) { encoder.append(static_cast(value)); encoder.append(static_cast(value >> 8)); } else if constexpr (sizeof(T) == 4) { encoder.append(static_cast(value)); encoder.append(static_cast(value >> 8)); encoder.append(static_cast(value >> 16)); encoder.append(static_cast(value >> 24)); } else if constexpr (sizeof(T) == 8) { encoder.append(static_cast(value)); encoder.append(static_cast(value >> 8)); encoder.append(static_cast(value >> 16)); encoder.append(static_cast(value >> 24)); encoder.append(static_cast(value >> 32)); encoder.append(static_cast(value >> 40)); encoder.append(static_cast(value >> 48)); encoder.append(static_cast(value >> 56)); } else { static_assert(DependentFalse); } return {}; } template ErrorOr encode(Encoder& encoder, T const& value) { return encoder.encode(to_underlying(value)); } template<> ErrorOr encode(Encoder&, float const&); template<> ErrorOr encode(Encoder&, double const&); template<> ErrorOr encode(Encoder&, String const&); template<> ErrorOr encode(Encoder&, StringView const&); template<> ErrorOr encode(Encoder&, DeprecatedString const&); template<> ErrorOr encode(Encoder&, ByteBuffer const&); template<> ErrorOr encode(Encoder&, JsonValue const&); template<> ErrorOr encode(Encoder&, Time const&); template<> ErrorOr encode(Encoder&, URL const&); template<> ErrorOr encode(Encoder&, Dictionary const&); template<> ErrorOr encode(Encoder&, File const&); template<> ErrorOr encode(Encoder&, Empty const&); template ErrorOr encode(Encoder& encoder, T const& vector) { // NOTE: Do not change this encoding without also updating LibC/netdb.cpp. TRY(encoder.encode_size(vector.size())); for (auto const& value : vector) TRY(encoder.encode(value)); return {}; } template ErrorOr encode(Encoder& encoder, T const& hashmap) { TRY(encoder.encode_size(hashmap.size())); for (auto it : hashmap) { TRY(encoder.encode(it.key)); TRY(encoder.encode(it.value)); } return {}; } template ErrorOr encode(Encoder& encoder, T const& queue) { return encoder.encode(IPC::File { queue.fd() }); } template ErrorOr encode(Encoder& encoder, T const& optional) { TRY(encoder.encode(optional.has_value())); if (optional.has_value()) TRY(encoder.encode(optional.value())); return {}; } template ErrorOr encode(Encoder& encoder, T const& variant) { TRY(encoder.encode(variant.index())); return variant.visit([&](auto const& value) { return encoder.encode(value); }); } // This must be last so that it knows about the above specializations. template ErrorOr Encoder::encode(T const& value) { return IPC::encode(*this, value); } }