| // Copyright 2020 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_CRDTP_PROTOCOL_CORE_H_ |
| #define V8_CRDTP_PROTOCOL_CORE_H_ |
| |
| #include <sys/types.h> |
| |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "cbor.h" |
| #include "maybe.h" |
| #include "serializable.h" |
| #include "span.h" |
| #include "status.h" |
| |
| namespace v8_crdtp { |
| |
| class DeserializerState { |
| public: |
| using Storage = std::shared_ptr<const std::vector<uint8_t>>; |
| |
| // Creates a state from the raw bytes received from the peer. |
| explicit DeserializerState(std::vector<uint8_t> bytes); |
| // Creates the state from the part of another message. |
| DeserializerState(Storage storage, span<uint8_t> span); |
| DeserializerState(const DeserializerState& r) = delete; |
| DeserializerState(DeserializerState&& r) = default; |
| |
| // Registers |error|, unless the tokenizer's status is already an error. |
| void RegisterError(Error error); |
| // Registers |name| as a segment of the field path. |
| void RegisterFieldPath(span<char> name); |
| |
| // Produces an error message considering |tokenizer.Status()|, |
| // status_, and field_path_. |
| std::string ErrorMessage(span<char> message_name) const; |
| Status status() const; |
| const Storage& storage() const { return storage_; } |
| cbor::CBORTokenizer* tokenizer() { return &tokenizer_; } |
| |
| private: |
| const Storage storage_; |
| cbor::CBORTokenizer tokenizer_; |
| Status status_; |
| std::vector<span<char>> field_path_; |
| }; |
| |
| template <typename T, typename = void> |
| struct ProtocolTypeTraits {}; |
| |
| template <> |
| struct ProtocolTypeTraits<bool> { |
| static bool Deserialize(DeserializerState* state, bool* value); |
| static void Serialize(bool value, std::vector<uint8_t>* bytes); |
| }; |
| |
| template <> |
| struct ProtocolTypeTraits<int32_t> { |
| static bool Deserialize(DeserializerState* state, int* value); |
| static void Serialize(int value, std::vector<uint8_t>* bytes); |
| }; |
| |
| template <> |
| struct ProtocolTypeTraits<double> { |
| static bool Deserialize(DeserializerState* state, double* value); |
| static void Serialize(double value, std::vector<uint8_t>* bytes); |
| }; |
| |
| class ContainerSerializer { |
| public: |
| ContainerSerializer(std::vector<uint8_t>* bytes, uint8_t tag); |
| |
| template <typename T> |
| void AddField(span<char> field_name, const T& value) { |
| cbor::EncodeString8( |
| span<uint8_t>(reinterpret_cast<const uint8_t*>(field_name.data()), |
| field_name.size()), |
| bytes_); |
| ProtocolTypeTraits<T>::Serialize(value, bytes_); |
| } |
| template <typename T> |
| void AddField(span<char> field_name, const detail::ValueMaybe<T>& value) { |
| if (!value.isJust()) |
| return; |
| AddField(field_name, value.fromJust()); |
| } |
| template <typename T> |
| void AddField(span<char> field_name, const detail::PtrMaybe<T>& value) { |
| if (!value.isJust()) |
| return; |
| AddField(field_name, *value.fromJust()); |
| } |
| |
| void EncodeStop(); |
| |
| private: |
| std::vector<uint8_t>* const bytes_; |
| cbor::EnvelopeEncoder envelope_; |
| }; |
| |
| class ObjectSerializer { |
| public: |
| ObjectSerializer(); |
| ~ObjectSerializer(); |
| |
| template <typename T> |
| void AddField(span<char> name, const T& field) { |
| serializer_.AddField(name, field); |
| } |
| std::unique_ptr<Serializable> Finish(); |
| |
| private: |
| std::vector<uint8_t> owned_bytes_; |
| ContainerSerializer serializer_; |
| }; |
| |
| class DeserializerDescriptor { |
| public: |
| struct Field { |
| span<char> name; |
| bool is_optional; |
| bool (*deserializer)(DeserializerState* state, void* obj); |
| }; |
| |
| DeserializerDescriptor(const Field* fields, size_t field_count); |
| |
| bool Deserialize(DeserializerState* state, void* obj) const; |
| |
| private: |
| bool DeserializeField(DeserializerState* state, |
| span<char> name, |
| int* seen_mandatory_fields, |
| void* obj) const; |
| |
| const Field* const fields_; |
| const size_t field_count_; |
| const int mandatory_field_mask_; |
| }; |
| |
| template <typename T> |
| struct ProtocolTypeTraits<std::vector<T>> { |
| static bool Deserialize(DeserializerState* state, std::vector<T>* value) { |
| auto* tokenizer = state->tokenizer(); |
| if (tokenizer->TokenTag() == cbor::CBORTokenTag::ENVELOPE) |
| tokenizer->EnterEnvelope(); |
| if (tokenizer->TokenTag() != cbor::CBORTokenTag::ARRAY_START) { |
| state->RegisterError(Error::CBOR_ARRAY_START_EXPECTED); |
| return false; |
| } |
| assert(value->empty()); |
| tokenizer->Next(); |
| for (; tokenizer->TokenTag() != cbor::CBORTokenTag::STOP; |
| tokenizer->Next()) { |
| value->emplace_back(); |
| if (!ProtocolTypeTraits<T>::Deserialize(state, &value->back())) |
| return false; |
| } |
| return true; |
| } |
| |
| static void Serialize(const std::vector<T>& value, |
| std::vector<uint8_t>* bytes) { |
| ContainerSerializer container_serializer( |
| bytes, cbor::EncodeIndefiniteLengthArrayStart()); |
| for (const auto& item : value) |
| ProtocolTypeTraits<T>::Serialize(item, bytes); |
| container_serializer.EncodeStop(); |
| } |
| }; |
| |
| template <typename T> |
| struct ProtocolTypeTraits<std::unique_ptr<std::vector<T>>> { |
| static bool Deserialize(DeserializerState* state, |
| std::unique_ptr<std::vector<T>>* value) { |
| auto res = std::make_unique<std::vector<T>>(); |
| if (!ProtocolTypeTraits<std::vector<T>>::Deserialize(state, res.get())) |
| return false; |
| *value = std::move(res); |
| return true; |
| } |
| static void Serialize(const std::unique_ptr<std::vector<T>>& value, |
| std::vector<uint8_t>* bytes) { |
| ProtocolTypeTraits<std::vector<T>>::Serialize(*value, bytes); |
| } |
| }; |
| |
| class DeferredMessage : public Serializable { |
| public: |
| static std::unique_ptr<DeferredMessage> FromSerializable( |
| std::unique_ptr<Serializable> serializeable); |
| static std::unique_ptr<DeferredMessage> FromSpan(span<uint8_t> bytes); |
| |
| ~DeferredMessage() override = default; |
| virtual DeserializerState MakeDeserializer() const = 0; |
| |
| protected: |
| DeferredMessage() = default; |
| }; |
| |
| template <> |
| struct ProtocolTypeTraits<std::unique_ptr<DeferredMessage>> { |
| static bool Deserialize(DeserializerState* state, |
| std::unique_ptr<DeferredMessage>* value); |
| static void Serialize(const std::unique_ptr<DeferredMessage>& value, |
| std::vector<uint8_t>* bytes); |
| }; |
| |
| template <typename T> |
| struct ProtocolTypeTraits<detail::ValueMaybe<T>> { |
| static bool Deserialize(DeserializerState* state, |
| detail::ValueMaybe<T>* value) { |
| T res; |
| if (!ProtocolTypeTraits<T>::Deserialize(state, &res)) |
| return false; |
| *value = std::move(res); |
| return true; |
| } |
| |
| static void Serialize(const detail::ValueMaybe<T>& value, |
| std::vector<uint8_t>* bytes) { |
| ProtocolTypeTraits<T>::Serialize(value.fromJust(), bytes); |
| } |
| }; |
| |
| template <typename T> |
| struct ProtocolTypeTraits<detail::PtrMaybe<T>> { |
| static bool Deserialize(DeserializerState* state, |
| detail::PtrMaybe<T>* value) { |
| std::unique_ptr<T> res; |
| if (!ProtocolTypeTraits<std::unique_ptr<T>>::Deserialize(state, &res)) |
| return false; |
| *value = std::move(res); |
| return true; |
| } |
| |
| static void Serialize(const detail::PtrMaybe<T>& value, |
| std::vector<uint8_t>* bytes) { |
| ProtocolTypeTraits<T>::Serialize(*value.fromJust(), bytes); |
| } |
| }; |
| |
| template <typename T> |
| class DeserializableProtocolObject { |
| public: |
| static StatusOr<std::unique_ptr<T>> ReadFrom( |
| const DeferredMessage& deferred_message) { |
| auto state = deferred_message.MakeDeserializer(); |
| if (auto res = Deserialize(&state)) |
| return StatusOr<std::unique_ptr<T>>(std::move(res)); |
| return StatusOr<std::unique_ptr<T>>(state.status()); |
| } |
| |
| static StatusOr<std::unique_ptr<T>> ReadFrom(std::vector<uint8_t> bytes) { |
| auto state = DeserializerState(std::move(bytes)); |
| if (auto res = Deserialize(&state)) |
| return StatusOr<std::unique_ptr<T>>(std::move(res)); |
| return StatusOr<std::unique_ptr<T>>(state.status()); |
| } |
| |
| // Short-hand for legacy clients. This would swallow any errors, consider |
| // using ReadFrom. |
| static std::unique_ptr<T> FromBinary(const uint8_t* bytes, size_t size) { |
| std::unique_ptr<T> value(new T()); |
| auto deserializer = DeferredMessage::FromSpan(span<uint8_t>(bytes, size)) |
| ->MakeDeserializer(); |
| Deserialize(&deserializer, value.get()); |
| return value; |
| } |
| |
| static bool Deserialize(DeserializerState* state, T* value) { |
| return T::deserializer_descriptor().Deserialize(state, value); |
| } |
| |
| protected: |
| // This is for the sake of the macros used by derived classes thay may be in |
| // a different namespace; |
| using ProtocolType = T; |
| using DeserializerDescriptorType = DeserializerDescriptor; |
| template <typename U> |
| using DeserializableBase = DeserializableProtocolObject<U>; |
| |
| DeserializableProtocolObject() = default; |
| ~DeserializableProtocolObject() = default; |
| |
| private: |
| friend struct ProtocolTypeTraits<std::unique_ptr<T>>; |
| |
| static std::unique_ptr<T> Deserialize(DeserializerState* state) { |
| std::unique_ptr<T> value(new T()); |
| if (Deserialize(state, value.get())) |
| return value; |
| return nullptr; |
| } |
| }; |
| |
| template <typename T> |
| class ProtocolObject : public Serializable, |
| public DeserializableProtocolObject<T> { |
| public: |
| std::unique_ptr<T> Clone() const { |
| std::vector<uint8_t> serialized; |
| AppendSerialized(&serialized); |
| return T::ReadFrom(std::move(serialized)).value(); |
| } |
| // TODO(caseq): compatibility only, remove. |
| std::unique_ptr<T> clone() const { return Clone(); } |
| |
| protected: |
| using ProtocolType = T; |
| |
| ProtocolObject() = default; |
| }; |
| |
| template <typename T> |
| struct ProtocolTypeTraits< |
| T, |
| typename std::enable_if< |
| std::is_base_of<ProtocolObject<T>, T>::value>::type> { |
| static bool Deserialize(DeserializerState* state, T* value) { |
| return T::Deserialize(state, value); |
| } |
| |
| static void Serialize(const T& value, std::vector<uint8_t>* bytes) { |
| value.AppendSerialized(bytes); |
| } |
| }; |
| |
| template <typename T> |
| struct ProtocolTypeTraits< |
| std::unique_ptr<T>, |
| typename std::enable_if< |
| std::is_base_of<ProtocolObject<T>, T>::value>::type> { |
| static bool Deserialize(DeserializerState* state, std::unique_ptr<T>* value) { |
| std::unique_ptr<T> res = T::Deserialize(state); |
| if (!res) |
| return false; |
| *value = std::move(res); |
| return true; |
| } |
| |
| static void Serialize(const std::unique_ptr<T>& value, |
| std::vector<uint8_t>* bytes) { |
| ProtocolTypeTraits<T>::Serialize(*value, bytes); |
| } |
| }; |
| |
| #define DECLARE_DESERIALIZATION_SUPPORT() \ |
| friend DeserializableBase<ProtocolType>; \ |
| static const DeserializerDescriptorType& deserializer_descriptor() |
| |
| #define DECLARE_SERIALIZATION_SUPPORT() \ |
| public: \ |
| void AppendSerialized(std::vector<uint8_t>* bytes) const override; \ |
| \ |
| private: \ |
| friend DeserializableBase<ProtocolType>; \ |
| static const DeserializerDescriptorType& deserializer_descriptor() |
| |
| #define V8_CRDTP_DESERIALIZE_FILED_IMPL(name, field, is_optional) \ |
| { \ |
| MakeSpan(name), is_optional, \ |
| [](DeserializerState* __state, void* __obj) -> bool { \ |
| return ProtocolTypeTraits<decltype(field)>::Deserialize( \ |
| __state, &static_cast<ProtocolType*>(__obj)->field); \ |
| } \ |
| } |
| |
| // clang-format off |
| #define V8_CRDTP_BEGIN_DESERIALIZER(type) \ |
| const type::DeserializerDescriptorType& type::deserializer_descriptor() { \ |
| using namespace v8_crdtp; \ |
| static const DeserializerDescriptorType::Field fields[] = { |
| |
| #define V8_CRDTP_END_DESERIALIZER() \ |
| }; \ |
| static const DeserializerDescriptorType s_desc( \ |
| fields, sizeof fields / sizeof fields[0]); \ |
| return s_desc; \ |
| } |
| |
| #define V8_CRDTP_DESERIALIZE_FIELD(name, field) \ |
| V8_CRDTP_DESERIALIZE_FILED_IMPL(name, field, false) |
| #define V8_CRDTP_DESERIALIZE_FIELD_OPT(name, field) \ |
| V8_CRDTP_DESERIALIZE_FILED_IMPL(name, field, true) |
| |
| #define V8_CRDTP_BEGIN_SERIALIZER(type) \ |
| void type::AppendSerialized(std::vector<uint8_t>* bytes) const { \ |
| using namespace v8_crdtp; \ |
| ContainerSerializer __serializer(bytes, \ |
| cbor::EncodeIndefiniteLengthMapStart()); |
| |
| #define V8_CRDTP_SERIALIZE_FIELD(name, field) \ |
| __serializer.AddField(MakeSpan(name), field) |
| |
| #define V8_CRDTP_END_SERIALIZER() \ |
| __serializer.EncodeStop(); \ |
| } class __cddtp_dummy_name |
| // clang-format on |
| |
| } // namespace v8_crdtp |
| |
| #endif // V8_CRDTP_PROTOCOL_CORE_H_ |