diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 6449f00e4742b..c441a606189ba 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -835,12 +835,13 @@ FILE: ../../../flutter/shell/platform/android/surface/android_surface_mock.h FILE: ../../../flutter/shell/platform/android/vsync_waiter_android.cc FILE: ../../../flutter/shell/platform/android/vsync_waiter_android.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/basic_message_channel_unittests.cc -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/byte_stream_wrappers.h +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/byte_buffer_streams.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/encodable_value_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/engine_method_result.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/event_channel_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/byte_streams.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/encodable_value.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/engine_method_result.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/event_channel.h @@ -855,6 +856,7 @@ FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/ FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_result_functions.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registry.h +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_codec_serializer.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_message_codec.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/method_call_unittests.cc @@ -863,7 +865,6 @@ FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/method_result_fu FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_codec.cc -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_codec_serializer.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_message_codec_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_method_codec_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/incoming_message_dispatcher.cc diff --git a/shell/platform/common/cpp/client_wrapper/BUILD.gn b/shell/platform/common/cpp/client_wrapper/BUILD.gn index ade14b76f713e..331a0c97d3509 100644 --- a/shell/platform/common/cpp/client_wrapper/BUILD.gn +++ b/shell/platform/common/cpp/client_wrapper/BUILD.gn @@ -48,6 +48,8 @@ executable("client_wrapper_unittests") { "plugin_registrar_unittests.cc", "standard_message_codec_unittests.cc", "standard_method_codec_unittests.cc", + "testing/test_codec_extensions.cc", + "testing/test_codec_extensions.h", ] deps = [ diff --git a/shell/platform/common/cpp/client_wrapper/byte_stream_wrappers.h b/shell/platform/common/cpp/client_wrapper/byte_buffer_streams.h similarity index 58% rename from shell/platform/common/cpp/client_wrapper/byte_stream_wrappers.h rename to shell/platform/common/cpp/client_wrapper/byte_buffer_streams.h index 96ab0b0d06210..86cdfc4551209 100644 --- a/shell/platform/common/cpp/client_wrapper/byte_stream_wrappers.h +++ b/shell/platform/common/cpp/client_wrapper/byte_buffer_streams.h @@ -2,30 +2,29 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_BYTE_STREAM_WRAPPERS_H_ -#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_BYTE_STREAM_WRAPPERS_H_ - -// Utility classes for interacting with a buffer of bytes as a stream, for use -// in message channel codecs. +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_BYTE_BUFFER_STREAMS_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_BYTE_BUFFER_STREAMS_H_ +#include #include #include #include #include +#include "include/flutter/byte_streams.h" + namespace flutter { -// Wraps an array of bytes with utility methods for treating it as a readable -// stream. -class ByteBufferStreamReader { +// Implementation of ByteStreamReader base on a byte array. +class ByteBufferStreamReader : public ByteStreamReader { public: // Createa a reader reading from |bytes|, which must have a length of |size|. // |bytes| must remain valid for the lifetime of this object. explicit ByteBufferStreamReader(const uint8_t* bytes, size_t size) : bytes_(bytes), size_(size) {} - // Reads and returns the next byte from the stream. - uint8_t ReadByte() { + // |ByteStreamReader| + uint8_t ReadByte() override { if (location_ >= size_) { std::cerr << "Invalid read in StandardCodecByteStreamReader" << std::endl; return 0; @@ -33,9 +32,8 @@ class ByteBufferStreamReader { return bytes_[location_++]; } - // Reads the next |length| bytes from the stream into |buffer|. The caller - // is responsible for ensuring that |buffer| is large enough. - void ReadBytes(uint8_t* buffer, size_t length) { + // |ByteStreamReader| + void ReadBytes(uint8_t* buffer, size_t length) override { if (location_ + length > size_) { std::cerr << "Invalid read in StandardCodecByteStreamReader" << std::endl; return; @@ -44,9 +42,8 @@ class ByteBufferStreamReader { location_ += length; } - // Advances the read cursor to the next multiple of |alignment| relative to - // the start of the wrapped byte buffer, unless it is already aligned. - void ReadAlignment(uint8_t alignment) { + // |ByteStreamReader| + void ReadAlignment(uint8_t alignment) override { uint8_t mod = location_ % alignment; if (mod) { location_ += alignment - mod; @@ -62,30 +59,26 @@ class ByteBufferStreamReader { size_t location_ = 0; }; -// Wraps an array of bytes with utility methods for treating it as a writable -// stream. -class ByteBufferStreamWriter { +// Implementation of ByteStreamWriter based on a byte array. +class ByteBufferStreamWriter : public ByteStreamWriter { public: - // Createa a writter that writes into |buffer|. + // Creates a writer that writes into |buffer|. // |buffer| must remain valid for the lifetime of this object. explicit ByteBufferStreamWriter(std::vector* buffer) : bytes_(buffer) { assert(buffer); } - // Writes |byte| to the wrapped buffer. + // |ByteStreamWriter| void WriteByte(uint8_t byte) { bytes_->push_back(byte); } - // Writes the next |length| bytes from |bytes| into the wrapped buffer. - // The caller is responsible for ensuring that |buffer| is large enough. + // |ByteStreamWriter| void WriteBytes(const uint8_t* bytes, size_t length) { assert(length > 0); bytes_->insert(bytes_->end(), bytes, bytes + length); } - // Writes 0s until the next multiple of |alignment| relative to - // the start of the wrapped byte buffer, unless the write positition is - // already aligned. + // |ByteStreamWriter| void WriteAlignment(uint8_t alignment) { uint8_t mod = bytes_->size() % alignment; if (mod) { @@ -102,4 +95,4 @@ class ByteBufferStreamWriter { } // namespace flutter -#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_BYTE_STREAM_WRAPPERS_H_ +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_BYTE_BUFFER_STREAMS_H_ diff --git a/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni b/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni index 18b0897e2a987..f86d87e024313 100644 --- a/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni +++ b/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni @@ -6,6 +6,7 @@ core_cpp_client_wrapper_includes = get_path_info([ "include/flutter/basic_message_channel.h", "include/flutter/binary_messenger.h", + "include/flutter/byte_streams.h", "include/flutter/encodable_value.h", "include/flutter/engine_method_result.h", "include/flutter/event_channel.h", @@ -20,6 +21,7 @@ core_cpp_client_wrapper_includes = "include/flutter/method_result.h", "include/flutter/plugin_registrar.h", "include/flutter/plugin_registry.h", + "include/flutter/standard_codec_serializer.h", "include/flutter/standard_message_codec.h", "include/flutter/standard_method_codec.h", ], @@ -29,10 +31,9 @@ core_cpp_client_wrapper_includes = # reasonable (without forcing different kinds of clients to take unnecessary # code) to simplify use. core_cpp_client_wrapper_sources = get_path_info([ - "byte_stream_wrappers.h", + "byte_buffer_streams.h", "engine_method_result.cc", "plugin_registrar.cc", - "standard_codec_serializer.h", "standard_codec.cc", ], "abspath") diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/byte_streams.h b/shell/platform/common/cpp/client_wrapper/include/flutter/byte_streams.h new file mode 100644 index 0000000000000..11f9928cd027a --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/byte_streams.h @@ -0,0 +1,79 @@ +// Copyright 2013 The Flutter 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 FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BYTE_STREAMS_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BYTE_STREAMS_H_ + +// Interfaces for interacting with a stream of bytes, for use in codecs. + +namespace flutter { + +// An interface for a class that reads from a byte stream. +class ByteStreamReader { + public: + // Reads and returns the next byte from the stream. + virtual uint8_t ReadByte() = 0; + + // Reads the next |length| bytes from the stream into |buffer|. The caller + // is responsible for ensuring that |buffer| is large enough. + virtual void ReadBytes(uint8_t* buffer, size_t length) = 0; + + // Advances the read cursor to the next multiple of |alignment| relative to + // the start of the stream, unless it is already aligned. + virtual void ReadAlignment(uint8_t alignment) = 0; + + // Reads and returns the next 32-bit integer from the stream. + int32_t ReadInt32() { + int32_t value = 0; + ReadBytes(reinterpret_cast(&value), 4); + return value; + } + + // Reads and returns the next 64-bit integer from the stream. + int64_t ReadInt64() { + int64_t value = 0; + ReadBytes(reinterpret_cast(&value), 8); + return value; + } + + // Reads and returns the next 64-bit floating point number from the stream. + double ReadDouble() { + double value = 0; + ReadBytes(reinterpret_cast(&value), 8); + return value; + } +}; + +// An interface for a class that writes to a byte stream. +class ByteStreamWriter { + public: + // Writes |byte| to the stream. + virtual void WriteByte(uint8_t byte) = 0; + + // Writes the next |length| bytes from |bytes| to the stream + virtual void WriteBytes(const uint8_t* bytes, size_t length) = 0; + + // Writes 0s until the next multiple of |alignment| relative to the start + // of the stream, unless the write positition is already aligned. + virtual void WriteAlignment(uint8_t alignment) = 0; + + // Writes the given 32-bit int to the stream. + void WriteInt32(int32_t value) { + WriteBytes(reinterpret_cast(&value), 4); + } + + // Writes the given 64-bit int to the stream. + void WriteInt64(int64_t value) { + WriteBytes(reinterpret_cast(&value), 8); + } + + // Writes the given 36-bit double to the stream. + void WriteDouble(double value) { + WriteBytes(reinterpret_cast(&value), 8); + } +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BYTE_STREAMS_H_ diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/encodable_value.h b/shell/platform/common/cpp/client_wrapper/include/flutter/encodable_value.h index bb0265e0c49c9..91ac03d54781c 100644 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/encodable_value.h +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/encodable_value.h @@ -5,8 +5,8 @@ #ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENCODABLE_VALUE_H_ #define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENCODABLE_VALUE_H_ -#include - +#include +#include #include #include #include @@ -25,6 +25,57 @@ static_assert(sizeof(double) == 8, "EncodableValue requires a 64-bit double"); // version, or it will break when the legacy version is removed. #ifndef USE_LEGACY_ENCODABLE_VALUE +// A container for arbitrary types in EncodableValue. +// +// This is used in conjunction with StandardCodecExtension to allow using other +// types with a StandardMethodCodec/StandardMessageCodec. It is implicitly +// convertible to EncodableValue, so constructing an EncodableValue from a +// custom type can generally be written as: +// CustomEncodableValue(MyType(...)) +// rather than: +// EncodableValue(CustomEncodableValue(MyType(...))) +// +// For extracting recieved custom types, it is implicitly convertible to +// std::any. For example: +// const MyType& my_type_value = +// std::any_cast(std::get(value)); +// +// If RTTI is enabled, different extension types can be checked with type(): +// if (custom_value->type() == typeid(SomeData)) { ... } +// Clients that wish to disable RTTI would need to decide on another approach +// for distinguishing types (e.g., in StandardCodecExtension::WriteValueOfType) +// if multiple custom types are needed. For instance, wrapping all of the +// extension types in an EncodableValue-style variant, and only ever storing +// that variant in CustomEncodableValue. +class CustomEncodableValue { + public: + explicit CustomEncodableValue(const std::any& value) : value_(value) {} + ~CustomEncodableValue() = default; + + // Allow implict conversion to std::any to allow direct use of any_cast. + operator std::any &() { return value_; } + operator const std::any &() const { return value_; } + +#if __has_feature(cxx_rtti) + // Passthrough to std::any's type(). + const std::type_info& type() const noexcept { return value_.type(); } +#endif + + // This operator exists only to provide a stable ordering for use as a + // std::map key, to satisfy the compiler requirements for EncodableValue. + // It does not attempt to provide useful ordering semantics, and using a + // custom value as a map key is not recommended. + bool operator<(const CustomEncodableValue& other) const { + return this < &other; + } + bool operator==(const CustomEncodableValue& other) const { + return this == &other; + } + + private: + std::any value_; +}; + class EncodableValue; // Convenience type aliases. @@ -48,7 +99,8 @@ using EncodableValueVariant = std::variant, std::vector, EncodableList, - EncodableMap>; + EncodableMap, + CustomEncodableValue>; } // namespace internal // An object that can contain any value or collection type supported by @@ -76,7 +128,7 @@ using EncodableValueVariant = std::variant(value)) { +// if (std::holds_alternative(value)) { // std::string some_string = std::get(value); // } // @@ -99,6 +151,13 @@ class EncodableValue : public internal::EncodableValueVariant { return *this; } + // Allow implicit conversion from CustomEncodableValue; the only reason to + // make a CustomEncodableValue (which can only be constructed explicitly) is + // to use it with EncodableValue, so the risk of unintended conversions is + // minimal, and it avoids the need for the verbose: + // EncodableValue(CustomEncodableValue(...)). + EncodableValue(const CustomEncodableValue& v) : super(v) {} + // Override the conversion constructors from std::variant to make them // explicit, to avoid implicit conversion. // diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/standard_codec_serializer.h b/shell/platform/common/cpp/client_wrapper/include/flutter/standard_codec_serializer.h new file mode 100644 index 0000000000000..f571b3256e360 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/standard_codec_serializer.h @@ -0,0 +1,76 @@ +// Copyright 2013 The Flutter 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 FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_CODEC_SERIALIZER_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_CODEC_SERIALIZER_H_ + +#include "byte_streams.h" +#include "encodable_value.h" + +namespace flutter { + +// Encapsulates the logic for encoding/decoding EncodableValues to/from the +// standard codec binary representation. +// +// This can be subclassed to extend the standard codec with support for new +// types. +class StandardCodecSerializer { + public: + virtual ~StandardCodecSerializer(); + + // Returns the shared serializer instance. + static const StandardCodecSerializer& GetInstance(); + + // Prevent copying. + StandardCodecSerializer(StandardCodecSerializer const&) = delete; + StandardCodecSerializer& operator=(StandardCodecSerializer const&) = delete; + + // Reads and returns the next value from |stream|. + EncodableValue ReadValue(ByteStreamReader* stream) const; + + // Writes the encoding of |value| to |stream|, including the initial type + // discrimination byte. + // + // Can be overridden by a subclass to extend the codec. + virtual void WriteValue(const EncodableValue& value, + ByteStreamWriter* stream) const; + + protected: + // Codecs require long-lived serializers, so clients should always use + // GetInstance(). + StandardCodecSerializer(); + + // Reads and returns the next value from |stream|, whose discrimination byte + // was |type|. + // + // The discrimination byte will already have been read from the stream when + // this is called. + // + // Can be overridden by a subclass to extend the codec. + virtual EncodableValue ReadValueOfType(uint8_t type, + ByteStreamReader* stream) const; + + // Reads the variable-length size from the current position in |stream|. + size_t ReadSize(ByteStreamReader* stream) const; + + // Writes the variable-length size encoding to |stream|. + void WriteSize(size_t size, ByteStreamWriter* stream) const; + + private: + // Reads a fixed-type list whose values are of type T from the current + // position in |stream|, and returns it as the corresponding EncodableValue. + // |T| must correspond to one of the supported list value types of + // EncodableValue. + template + EncodableValue ReadVector(ByteStreamReader* stream) const; + + // Writes |vector| to |stream| as a fixed-type list. |T| must correspond to + // one of the supported list value types of EncodableValue. + template + void WriteVector(const std::vector vector, ByteStreamWriter* stream) const; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_CODEC_SERIALIZER_H_ diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/standard_message_codec.h b/shell/platform/common/cpp/client_wrapper/include/flutter/standard_message_codec.h index 75644c7a85f31..735dcda580c7e 100644 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/standard_message_codec.h +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/standard_message_codec.h @@ -5,8 +5,11 @@ #ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_MESSAGE_CODEC_H_ #define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_MESSAGE_CODEC_H_ +#include + #include "encodable_value.h" #include "message_codec.h" +#include "standard_codec_serializer.h" namespace flutter { @@ -14,8 +17,17 @@ namespace flutter { // Flutter engine via message channels. class StandardMessageCodec : public MessageCodec { public: - // Returns the shared instance of the codec. - static const StandardMessageCodec& GetInstance(); + // Returns an instance of the codec, optionally using a custom serializer to + // add support for more types. + // + // If provided, |serializer| must be long-lived. If no serializer is provided, + // the default will be used. + // + // The instance returned for a given |serializer| will be shared, and + // any instance returned from this will be long-lived, and can be safely + // passed to, e.g., channel constructors. + static const StandardMessageCodec& GetInstance( + const StandardCodecSerializer* serializer = nullptr); ~StandardMessageCodec(); @@ -24,9 +36,6 @@ class StandardMessageCodec : public MessageCodec { StandardMessageCodec& operator=(StandardMessageCodec const&) = delete; protected: - // Instances should be obtained via GetInstance. - StandardMessageCodec(); - // |flutter::MessageCodec| std::unique_ptr DecodeMessageInternal( const uint8_t* binary_message, @@ -35,6 +44,12 @@ class StandardMessageCodec : public MessageCodec { // |flutter::MessageCodec| std::unique_ptr> EncodeMessageInternal( const EncodableValue& message) const override; + + private: + // Instances should be obtained via GetInstance. + explicit StandardMessageCodec(const StandardCodecSerializer* serializer); + + const StandardCodecSerializer* serializer_; }; } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h b/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h index ef40897893183..729babc2b62b0 100644 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h @@ -5,28 +5,37 @@ #ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_METHOD_CODEC_H_ #define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_METHOD_CODEC_H_ +#include + #include "encodable_value.h" #include "method_call.h" #include "method_codec.h" +#include "standard_codec_serializer.h" namespace flutter { // An implementation of MethodCodec that uses a binary serialization. class StandardMethodCodec : public MethodCodec { public: - // Returns the shared instance of the codec. - static const StandardMethodCodec& GetInstance(); + // Returns an instance of the codec, optionally using a custom serializer to + // add support for more types. + // + // If provided, |serializer| must be long-lived. If no serializer is provided, + // the default will be used. + // + // The instance returned for a given |extension| will be shared, and + // any instance returned from this will be long-lived, and can be safely + // passed to, e.g., channel constructors. + static const StandardMethodCodec& GetInstance( + const StandardCodecSerializer* serializer = nullptr); - ~StandardMethodCodec() = default; + ~StandardMethodCodec(); // Prevent copying. StandardMethodCodec(StandardMethodCodec const&) = delete; StandardMethodCodec& operator=(StandardMethodCodec const&) = delete; protected: - // Instances should be obtained via GetInstance. - StandardMethodCodec() = default; - // |flutter::MethodCodec| std::unique_ptr> DecodeMethodCallInternal( const uint8_t* message, @@ -51,6 +60,12 @@ class StandardMethodCodec : public MethodCodec { const uint8_t* response, size_t response_size, MethodResult* result) const override; + + private: + // Instances should be obtained via GetInstance. + explicit StandardMethodCodec(const StandardCodecSerializer* serializer); + + const StandardCodecSerializer* serializer_; }; } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/standard_codec.cc b/shell/platform/common/cpp/client_wrapper/standard_codec.cc index 1f8e5375567ca..6ba13b48a67e1 100644 --- a/shell/platform/common/cpp/client_wrapper/standard_codec.cc +++ b/shell/platform/common/cpp/client_wrapper/standard_codec.cc @@ -8,17 +8,17 @@ // together to simplify use of the client wrapper, since the common case is // that any client that needs one of these files needs all three. -#include - +#include #include #include #include #include #include +#include "byte_buffer_streams.h" +#include "include/flutter/standard_codec_serializer.h" #include "include/flutter/standard_message_codec.h" #include "include/flutter/standard_method_codec.h" -#include "standard_codec_serializer.h" namespace flutter { @@ -111,75 +111,19 @@ StandardCodecSerializer::StandardCodecSerializer() = default; StandardCodecSerializer::~StandardCodecSerializer() = default; +const StandardCodecSerializer& StandardCodecSerializer::GetInstance() { + static StandardCodecSerializer sInstance; + return sInstance; +}; + EncodableValue StandardCodecSerializer::ReadValue( - ByteBufferStreamReader* stream) const { - EncodedType type = static_cast(stream->ReadByte()); - switch (type) { - case EncodedType::kNull: - return EncodableValue(); - case EncodedType::kTrue: - return EncodableValue(true); - case EncodedType::kFalse: - return EncodableValue(false); - case EncodedType::kInt32: { - int32_t int_value = 0; - stream->ReadBytes(reinterpret_cast(&int_value), 4); - return EncodableValue(int_value); - } - case EncodedType::kInt64: { - int64_t long_value = 0; - stream->ReadBytes(reinterpret_cast(&long_value), 8); - return EncodableValue(long_value); - } - case EncodedType::kFloat64: { - double double_value = 0; - stream->ReadAlignment(8); - stream->ReadBytes(reinterpret_cast(&double_value), 8); - return EncodableValue(double_value); - } - case EncodedType::kLargeInt: - case EncodedType::kString: { - size_t size = ReadSize(stream); - std::string string_value; - string_value.resize(size); - stream->ReadBytes(reinterpret_cast(&string_value[0]), size); - return EncodableValue(string_value); - } - case EncodedType::kUInt8List: - return ReadVector(stream); - case EncodedType::kInt32List: - return ReadVector(stream); - case EncodedType::kInt64List: - return ReadVector(stream); - case EncodedType::kFloat64List: - return ReadVector(stream); - case EncodedType::kList: { - size_t length = ReadSize(stream); - EncodableList list_value; - list_value.reserve(length); - for (size_t i = 0; i < length; ++i) { - list_value.push_back(ReadValue(stream)); - } - return EncodableValue(list_value); - } - case EncodedType::kMap: { - size_t length = ReadSize(stream); - EncodableMap map_value; - for (size_t i = 0; i < length; ++i) { - EncodableValue key = ReadValue(stream); - EncodableValue value = ReadValue(stream); - map_value.emplace(std::move(key), std::move(value)); - } - return EncodableValue(map_value); - } - } - std::cerr << "Unknown type in StandardCodecSerializer::ReadValue: " - << static_cast(type) << std::endl; - return EncodableValue(); + ByteStreamReader* stream) const { + uint8_t type = stream->ReadByte(); + return ReadValueOfType(type, stream); } void StandardCodecSerializer::WriteValue(const EncodableValue& value, - ByteBufferStreamWriter* stream) const { + ByteStreamWriter* stream) const { stream->WriteByte(static_cast(EncodedTypeForValue(value))); #ifdef USE_LEGACY_ENCODABLE_VALUE switch (value.type()) { @@ -187,22 +131,16 @@ void StandardCodecSerializer::WriteValue(const EncodableValue& value, case EncodableValue::Type::kBool: // Null and bool are encoded directly in the type. break; - case EncodableValue::Type::kInt: { - int32_t int_value = value.IntValue(); - stream->WriteBytes(reinterpret_cast(&int_value), 4); + case EncodableValue::Type::kInt: + stream->WriteInt32(std::get(value)); break; - } - case EncodableValue::Type::kLong: { - int64_t long_value = value.LongValue(); - stream->WriteBytes(reinterpret_cast(&long_value), 8); + case case EncodableValue::Type::kLong: + stream->WriteInt64(std::get(value)); break; - } - case EncodableValue::Type::kDouble: { + case EncodableValue::Type::kDouble: stream->WriteAlignment(8); - double double_value = value.DoubleValue(); - stream->WriteBytes(reinterpret_cast(&double_value), 8); + stream->WriteDouble(std::get(value)); break; - } case EncodableValue::Type::kString: { const auto& string_value = value.StringValue(); size_t size = string_value.size(); @@ -246,22 +184,16 @@ void StandardCodecSerializer::WriteValue(const EncodableValue& value, case 1: // Null and bool are encoded directly in the type. break; - case 2: { - int32_t int_value = std::get(value); - stream->WriteBytes(reinterpret_cast(&int_value), 4); + case 2: + stream->WriteInt32(std::get(value)); break; - } - case 3: { - int64_t long_value = std::get(value); - stream->WriteBytes(reinterpret_cast(&long_value), 8); + case 3: + stream->WriteInt64(std::get(value)); break; - } - case 4: { + case 4: stream->WriteAlignment(8); - double double_value = std::get(value); - stream->WriteBytes(reinterpret_cast(&double_value), 8); + stream->WriteDouble(std::get(value)); break; - } case 5: { const auto& string_value = std::get(value); size_t size = string_value.size(); @@ -301,11 +233,74 @@ void StandardCodecSerializer::WriteValue(const EncodableValue& value, } break; } + case 12: + std::cerr + << "Unhandled custom type in StandardCodecSerializer::WriteValue. " + << "Custom types require codec extensions." << std::endl; + break; } #endif } -size_t StandardCodecSerializer::ReadSize(ByteBufferStreamReader* stream) const { +EncodableValue StandardCodecSerializer::ReadValueOfType( + uint8_t type, + ByteStreamReader* stream) const { + switch (static_cast(type)) { + case EncodedType::kNull: + return EncodableValue(); + case EncodedType::kTrue: + return EncodableValue(true); + case EncodedType::kFalse: + return EncodableValue(false); + case EncodedType::kInt32: + return EncodableValue(stream->ReadInt32()); + case EncodedType::kInt64: + return EncodableValue(stream->ReadInt64()); + case EncodedType::kFloat64: + stream->ReadAlignment(8); + return EncodableValue(stream->ReadDouble()); + case EncodedType::kLargeInt: + case EncodedType::kString: { + size_t size = ReadSize(stream); + std::string string_value; + string_value.resize(size); + stream->ReadBytes(reinterpret_cast(&string_value[0]), size); + return EncodableValue(string_value); + } + case EncodedType::kUInt8List: + return ReadVector(stream); + case EncodedType::kInt32List: + return ReadVector(stream); + case EncodedType::kInt64List: + return ReadVector(stream); + case EncodedType::kFloat64List: + return ReadVector(stream); + case EncodedType::kList: { + size_t length = ReadSize(stream); + EncodableList list_value; + list_value.reserve(length); + for (size_t i = 0; i < length; ++i) { + list_value.push_back(ReadValue(stream)); + } + return EncodableValue(list_value); + } + case EncodedType::kMap: { + size_t length = ReadSize(stream); + EncodableMap map_value; + for (size_t i = 0; i < length; ++i) { + EncodableValue key = ReadValue(stream); + EncodableValue value = ReadValue(stream); + map_value.emplace(std::move(key), std::move(value)); + } + return EncodableValue(map_value); + } + } + std::cerr << "Unknown type in StandardCodecSerializer::ReadValueOfType: " + << static_cast(type) << std::endl; + return EncodableValue(); +} + +size_t StandardCodecSerializer::ReadSize(ByteStreamReader* stream) const { uint8_t byte = stream->ReadByte(); if (byte < 254) { return byte; @@ -321,7 +316,7 @@ size_t StandardCodecSerializer::ReadSize(ByteBufferStreamReader* stream) const { } void StandardCodecSerializer::WriteSize(size_t size, - ByteBufferStreamWriter* stream) const { + ByteStreamWriter* stream) const { if (size < 254) { stream->WriteByte(static_cast(size)); } else if (size <= 0xffff) { @@ -337,7 +332,7 @@ void StandardCodecSerializer::WriteSize(size_t size, template EncodableValue StandardCodecSerializer::ReadVector( - ByteBufferStreamReader* stream) const { + ByteStreamReader* stream) const { size_t count = ReadSize(stream); std::vector vector; vector.resize(count); @@ -351,9 +346,8 @@ EncodableValue StandardCodecSerializer::ReadVector( } template -void StandardCodecSerializer::WriteVector( - const std::vector vector, - ByteBufferStreamWriter* stream) const { +void StandardCodecSerializer::WriteVector(const std::vector vector, + ByteStreamWriter* stream) const { size_t count = vector.size(); WriteSize(count, stream); if (count == 0) { @@ -370,59 +364,92 @@ void StandardCodecSerializer::WriteVector( // ===== standard_message_codec.h ===== // static -const StandardMessageCodec& StandardMessageCodec::GetInstance() { - static StandardMessageCodec sInstance; - return sInstance; +const StandardMessageCodec& StandardMessageCodec::GetInstance( + const StandardCodecSerializer* serializer) { + if (!serializer) { + serializer = &StandardCodecSerializer::GetInstance(); + } + auto* sInstances = new std::map>; + auto it = sInstances->find(serializer); + if (it == sInstances->end()) { + // Uses new due to private constructor (to prevent API clients from + // accidentally passing temporary codec instances to channels). + auto emplace_result = sInstances->emplace( + serializer, std::unique_ptr( + new StandardMessageCodec(serializer))); + it = emplace_result.first; + } + return *(it->second); } -StandardMessageCodec::StandardMessageCodec() = default; +StandardMessageCodec::StandardMessageCodec( + const StandardCodecSerializer* serializer) + : serializer_(serializer) {} StandardMessageCodec::~StandardMessageCodec() = default; std::unique_ptr StandardMessageCodec::DecodeMessageInternal( const uint8_t* binary_message, size_t message_size) const { - StandardCodecSerializer serializer; ByteBufferStreamReader stream(binary_message, message_size); - return std::make_unique(serializer.ReadValue(&stream)); + return std::make_unique(serializer_->ReadValue(&stream)); } std::unique_ptr> StandardMessageCodec::EncodeMessageInternal( const EncodableValue& message) const { - StandardCodecSerializer serializer; auto encoded = std::make_unique>(); ByteBufferStreamWriter stream(encoded.get()); - serializer.WriteValue(message, &stream); + serializer_->WriteValue(message, &stream); return encoded; } // ===== standard_method_codec.h ===== // static -const StandardMethodCodec& StandardMethodCodec::GetInstance() { - static StandardMethodCodec sInstance; - return sInstance; +const StandardMethodCodec& StandardMethodCodec::GetInstance( + const StandardCodecSerializer* serializer) { + if (!serializer) { + serializer = &StandardCodecSerializer::GetInstance(); + } + auto* sInstances = new std::map>; + auto it = sInstances->find(serializer); + if (it == sInstances->end()) { + // Uses new due to private constructor (to prevent API clients from + // accidentally passing temporary codec instances to channels). + auto emplace_result = sInstances->emplace( + serializer, std::unique_ptr( + new StandardMethodCodec(serializer))); + it = emplace_result.first; + } + return *(it->second); } +StandardMethodCodec::StandardMethodCodec( + const StandardCodecSerializer* serializer) + : serializer_(serializer) {} + +StandardMethodCodec::~StandardMethodCodec() = default; + std::unique_ptr> StandardMethodCodec::DecodeMethodCallInternal(const uint8_t* message, size_t message_size) const { - StandardCodecSerializer serializer; ByteBufferStreamReader stream(message, message_size); #ifdef USE_LEGACY_ENCODABLE_VALUE - EncodableValue method_name = serializer.ReadValue(&stream); + EncodableValue method_name = serializer_->ReadValue(&stream); if (!method_name.IsString()) { std::cerr << "Invalid method call; method name is not a string." << std::endl; return nullptr; } auto arguments = - std::make_unique(serializer.ReadValue(&stream)); + std::make_unique(serializer_->ReadValue(&stream)); return std::make_unique>(method_name.StringValue(), std::move(arguments)); #else - EncodableValue method_name_value = serializer.ReadValue(&stream); + EncodableValue method_name_value = serializer_->ReadValue(&stream); const auto* method_name = std::get_if(&method_name_value); if (!method_name) { std::cerr << "Invalid method call; method name is not a string." @@ -430,7 +457,7 @@ StandardMethodCodec::DecodeMethodCallInternal(const uint8_t* message, return nullptr; } auto arguments = - std::make_unique(serializer.ReadValue(&stream)); + std::make_unique(serializer_->ReadValue(&stream)); return std::make_unique>(*method_name, std::move(arguments)); #endif @@ -439,14 +466,13 @@ StandardMethodCodec::DecodeMethodCallInternal(const uint8_t* message, std::unique_ptr> StandardMethodCodec::EncodeMethodCallInternal( const MethodCall& method_call) const { - StandardCodecSerializer serializer; auto encoded = std::make_unique>(); ByteBufferStreamWriter stream(encoded.get()); - serializer.WriteValue(EncodableValue(method_call.method_name()), &stream); + serializer_->WriteValue(EncodableValue(method_call.method_name()), &stream); if (method_call.arguments()) { - serializer.WriteValue(*method_call.arguments(), &stream); + serializer_->WriteValue(*method_call.arguments(), &stream); } else { - serializer.WriteValue(EncodableValue(), &stream); + serializer_->WriteValue(EncodableValue(), &stream); } return encoded; } @@ -454,14 +480,13 @@ StandardMethodCodec::EncodeMethodCallInternal( std::unique_ptr> StandardMethodCodec::EncodeSuccessEnvelopeInternal( const EncodableValue* result) const { - StandardCodecSerializer serializer; auto encoded = std::make_unique>(); ByteBufferStreamWriter stream(encoded.get()); stream.WriteByte(0); if (result) { - serializer.WriteValue(*result, &stream); + serializer_->WriteValue(*result, &stream); } else { - serializer.WriteValue(EncodableValue(), &stream); + serializer_->WriteValue(EncodableValue(), &stream); } return encoded; } @@ -471,20 +496,19 @@ StandardMethodCodec::EncodeErrorEnvelopeInternal( const std::string& error_code, const std::string& error_message, const EncodableValue* error_details) const { - StandardCodecSerializer serializer; auto encoded = std::make_unique>(); ByteBufferStreamWriter stream(encoded.get()); stream.WriteByte(1); - serializer.WriteValue(EncodableValue(error_code), &stream); + serializer_->WriteValue(EncodableValue(error_code), &stream); if (error_message.empty()) { - serializer.WriteValue(EncodableValue(), &stream); + serializer_->WriteValue(EncodableValue(), &stream); } else { - serializer.WriteValue(EncodableValue(error_message), &stream); + serializer_->WriteValue(EncodableValue(error_message), &stream); } if (error_details) { - serializer.WriteValue(*error_details, &stream); + serializer_->WriteValue(*error_details, &stream); } else { - serializer.WriteValue(EncodableValue(), &stream); + serializer_->WriteValue(EncodableValue(), &stream); } return encoded; } @@ -493,19 +517,18 @@ bool StandardMethodCodec::DecodeAndProcessResponseEnvelopeInternal( const uint8_t* response, size_t response_size, MethodResult* result) const { - StandardCodecSerializer serializer; ByteBufferStreamReader stream(response, response_size); uint8_t flag = stream.ReadByte(); switch (flag) { case 0: { - EncodableValue value = serializer.ReadValue(&stream); + EncodableValue value = serializer_->ReadValue(&stream); result->Success(value.IsNull() ? nullptr : &value); return true; } case 1: { - EncodableValue code = serializer.ReadValue(&stream); - EncodableValue message = serializer.ReadValue(&stream); - EncodableValue details = serializer.ReadValue(&stream); + EncodableValue code = serializer_->ReadValue(&stream); + EncodableValue message = serializer_->ReadValue(&stream); + EncodableValue details = serializer_->ReadValue(&stream); #ifdef USE_LEGACY_ENCODABLE_VALUE result->Error(code.StringValue(), message.IsNull() ? "" : message.StringValue(), diff --git a/shell/platform/common/cpp/client_wrapper/standard_codec_serializer.h b/shell/platform/common/cpp/client_wrapper/standard_codec_serializer.h deleted file mode 100644 index 89aab3b9988f8..0000000000000 --- a/shell/platform/common/cpp/client_wrapper/standard_codec_serializer.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2013 The Flutter 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 FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_ENCODABLE_VALUE_SERIALIZER_H_ -#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_ENCODABLE_VALUE_SERIALIZER_H_ - -#include "byte_stream_wrappers.h" -#include "include/flutter/encodable_value.h" - -namespace flutter { - -// Encapsulates the logic for encoding/decoding EncodableValues to/from the -// standard codec binary representation. -class StandardCodecSerializer { - public: - StandardCodecSerializer(); - ~StandardCodecSerializer(); - - // Prevent copying. - StandardCodecSerializer(StandardCodecSerializer const&) = delete; - StandardCodecSerializer& operator=(StandardCodecSerializer const&) = delete; - - // Reads and returns the next value from |stream|. - EncodableValue ReadValue(ByteBufferStreamReader* stream) const; - - // Writes the encoding of |value| to |stream|. - void WriteValue(const EncodableValue& value, - ByteBufferStreamWriter* stream) const; - - protected: - // Reads the variable-length size from the current position in |stream|. - size_t ReadSize(ByteBufferStreamReader* stream) const; - - // Writes the variable-length size encoding to |stream|. - void WriteSize(size_t size, ByteBufferStreamWriter* stream) const; - - // Reads a fixed-type list whose values are of type T from the current - // position in |stream|, and returns it as the corresponding EncodableValue. - // |T| must correspond to one of the support list value types of - // EncodableValue. - template - EncodableValue ReadVector(ByteBufferStreamReader* stream) const; - - // Writes |vector| to |stream| as a fixed-type list. |T| must correspond to - // one of the support list value types of EncodableValue. - template - void WriteVector(const std::vector vector, - ByteBufferStreamWriter* stream) const; -}; - -} // namespace flutter - -#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_ENCODABLE_VALUE_SERIALIZER_H_ diff --git a/shell/platform/common/cpp/client_wrapper/standard_message_codec_unittests.cc b/shell/platform/common/cpp/client_wrapper/standard_message_codec_unittests.cc index 16228057298cd..3d8c077133eab 100644 --- a/shell/platform/common/cpp/client_wrapper/standard_message_codec_unittests.cc +++ b/shell/platform/common/cpp/client_wrapper/standard_message_codec_unittests.cc @@ -6,21 +6,35 @@ #include #include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_message_codec.h" +#include "flutter/shell/platform/common/cpp/client_wrapper/testing/test_codec_extensions.h" #include "gtest/gtest.h" namespace flutter { // Validates round-trip encoding and decoding of |value|, and checks that the // encoded value matches |expected_encoding|. -static void CheckEncodeDecode(const EncodableValue& value, - const std::vector& expected_encoding) { - const StandardMessageCodec& codec = StandardMessageCodec::GetInstance(); +// +// If testing with CustomEncodableValues, |serializer| must be provided to +// handle the encoding/decoding, and |custom_comparator| must be provided to +// validate equality since CustomEncodableValue doesn't define a useful ==. +static void CheckEncodeDecode( + const EncodableValue& value, + const std::vector& expected_encoding, + const StandardCodecSerializer* serializer = nullptr, + std::function + custom_comparator = nullptr) { + const StandardMessageCodec& codec = + StandardMessageCodec::GetInstance(serializer); auto encoded = codec.EncodeMessage(value); ASSERT_TRUE(encoded); EXPECT_EQ(*encoded, expected_encoding); auto decoded = codec.DecodeMessage(*encoded); - EXPECT_EQ(value, *decoded); + if (custom_comparator) { + EXPECT_TRUE(custom_comparator(value, *decoded)); + } else { + EXPECT_EQ(value, *decoded); + } } // Validates round-trip encoding and decoding of |value|, and checks that the @@ -168,4 +182,39 @@ TEST(StandardMessageCodec, CanEncodeAndDecodeFloat64Array) { CheckEncodeDecode(value, bytes); } +TEST(StandardMessageCodec, CanEncodeAndDecodeSimpleCustomType) { + std::vector bytes = {0x80, 0x09, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00}; + auto point_comparator = [](const EncodableValue& a, const EncodableValue& b) { + const Point& a_point = + std::any_cast(std::get(a)); + const Point& b_point = + std::any_cast(std::get(b)); + return a_point == b_point; + }; + CheckEncodeDecode(CustomEncodableValue(Point(9, 16)), bytes, + &PointExtensionSerializer::GetInstance(), point_comparator); +} + +TEST(StandardMessageCodec, CanEncodeAndDecodeVariableLengthCustomType) { + std::vector bytes = { + 0x81, // custom type + 0x06, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, // data + 0x07, 0x04, // string type and length + 0x74, 0x65, 0x73, 0x74 // string characters + }; + auto some_data_comparator = [](const EncodableValue& a, + const EncodableValue& b) { + const SomeData& data_a = + std::any_cast(std::get(a)); + const SomeData& data_b = + std::any_cast(std::get(b)); + return data_a.data() == data_b.data() && data_a.label() == data_b.label(); + }; + CheckEncodeDecode(CustomEncodableValue( + SomeData("test", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05})), + bytes, &SomeDataExtensionSerializer::GetInstance(), + some_data_comparator); +} + } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/standard_method_codec_unittests.cc b/shell/platform/common/cpp/client_wrapper/standard_method_codec_unittests.cc index 6d0978772ca84..bb7a6f930f788 100644 --- a/shell/platform/common/cpp/client_wrapper/standard_method_codec_unittests.cc +++ b/shell/platform/common/cpp/client_wrapper/standard_method_codec_unittests.cc @@ -4,6 +4,7 @@ #include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_result_functions.h" #include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h" +#include "flutter/shell/platform/common/cpp/client_wrapper/testing/test_codec_extensions.h" #include "gtest/gtest.h" namespace flutter { @@ -158,4 +159,21 @@ TEST(StandardMethodCodec, HandlesErrorEnvelopesWithDetails) { EXPECT_TRUE(decoded_successfully); } +TEST(StandardMethodCodec, HandlesCustomTypeArguments) { + const StandardMethodCodec& codec = StandardMethodCodec::GetInstance( + &PointExtensionSerializer::GetInstance()); + Point point(7, 9); + MethodCall call( + "hello", std::make_unique(CustomEncodableValue(point))); + auto encoded = codec.EncodeMethodCall(call); + ASSERT_NE(encoded.get(), nullptr); + std::unique_ptr> decoded = + codec.DecodeMethodCall(*encoded); + ASSERT_NE(decoded.get(), nullptr); + + const Point& decoded_point = std::any_cast( + std::get(*decoded->arguments())); + EXPECT_EQ(point, decoded_point); +}; + } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/testing/test_codec_extensions.cc b/shell/platform/common/cpp/client_wrapper/testing/test_codec_extensions.cc new file mode 100644 index 0000000000000..53bdfe93a54e8 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/testing/test_codec_extensions.cc @@ -0,0 +1,80 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/common/cpp/client_wrapper/testing/test_codec_extensions.h" + +namespace flutter { + +PointExtensionSerializer::PointExtensionSerializer() = default; +PointExtensionSerializer::~PointExtensionSerializer() = default; + +// static +const PointExtensionSerializer& PointExtensionSerializer::GetInstance() { + static PointExtensionSerializer sInstance; + return sInstance; +} + +EncodableValue PointExtensionSerializer::ReadValueOfType( + uint8_t type, + ByteStreamReader* stream) const { + if (type == kPointType) { + int32_t x = stream->ReadInt32(); + int32_t y = stream->ReadInt32(); + return CustomEncodableValue(Point(x, y)); + } + return StandardCodecSerializer::ReadValueOfType(type, stream); +} + +void PointExtensionSerializer::WriteValue(const EncodableValue& value, + ByteStreamWriter* stream) const { + auto custom_value = std::get_if(&value); + if (!custom_value) { + StandardCodecSerializer::WriteValue(value, stream); + return; + } + stream->WriteByte(kPointType); + const Point& point = std::any_cast(*custom_value); + stream->WriteInt32(point.x()); + stream->WriteInt32(point.y()); +} + +SomeDataExtensionSerializer::SomeDataExtensionSerializer() = default; +SomeDataExtensionSerializer::~SomeDataExtensionSerializer() = default; + +// static +const SomeDataExtensionSerializer& SomeDataExtensionSerializer::GetInstance() { + static SomeDataExtensionSerializer sInstance; + return sInstance; +} + +EncodableValue SomeDataExtensionSerializer::ReadValueOfType( + uint8_t type, + ByteStreamReader* stream) const { + if (type == kSomeDataType) { + size_t size = ReadSize(stream); + std::vector data; + data.resize(size); + stream->ReadBytes(data.data(), size); + EncodableValue label = ReadValue(stream); + return CustomEncodableValue(SomeData(std::get(label), data)); + } + return StandardCodecSerializer::ReadValueOfType(type, stream); +} + +void SomeDataExtensionSerializer::WriteValue(const EncodableValue& value, + ByteStreamWriter* stream) const { + auto custom_value = std::get_if(&value); + if (!custom_value) { + StandardCodecSerializer::WriteValue(value, stream); + return; + } + stream->WriteByte(kSomeDataType); + const SomeData& some_data = std::any_cast(*custom_value); + size_t data_size = some_data.data().size(); + WriteSize(data_size, stream); + stream->WriteBytes(some_data.data().data(), data_size); + WriteValue(EncodableValue(some_data.label()), stream); +} + +} // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/testing/test_codec_extensions.h b/shell/platform/common/cpp/client_wrapper/testing/test_codec_extensions.h new file mode 100644 index 0000000000000..cbe01c8a886ff --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/testing/test_codec_extensions.h @@ -0,0 +1,89 @@ +// Copyright 2013 The Flutter 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 FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_TESTING_TEST_CODEC_EXTENSIONS_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_TESTING_TEST_CODEC_EXTENSIONS_H_ + +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/encodable_value.h" +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_codec_serializer.h" + +namespace flutter { + +// A representation of a point, for custom type testing of a simple type. +class Point { + public: + Point(int x, int y) : x_(x), y_(y) {} + ~Point() = default; + + int x() const { return x_; } + int y() const { return y_; } + + bool operator==(const Point& other) const { + return x_ == other.x_ && y_ == other.y_; + } + + private: + int x_; + int y_; +}; + +// A typed binary data object with extra fields, for custom type testing of a +// variable-length type that includes types handled by the core standard codec. +class SomeData { + public: + SomeData(const std::string label, const std::vector& data) + : label_(label), data_(data) {} + ~SomeData() = default; + + const std::string& label() const { return label_; } + const std::vector& data() const { return data_; } + + private: + std::string label_; + std::vector data_; +}; + +// Codec extension for Point. +class PointExtensionSerializer : public StandardCodecSerializer { + public: + PointExtensionSerializer(); + virtual ~PointExtensionSerializer(); + + static const PointExtensionSerializer& GetInstance(); + + // |TestCodecSerializer| + EncodableValue ReadValueOfType(uint8_t type, + ByteStreamReader* stream) const override; + + // |TestCodecSerializer| + void WriteValue(const EncodableValue& value, + ByteStreamWriter* stream) const override; + + private: + static constexpr uint8_t kPointType = 128; +}; + +// Codec extension for SomeData. +class SomeDataExtensionSerializer : public StandardCodecSerializer { + public: + SomeDataExtensionSerializer(); + virtual ~SomeDataExtensionSerializer(); + + static const SomeDataExtensionSerializer& GetInstance(); + + // |TestCodecSerializer| + EncodableValue ReadValueOfType(uint8_t type, + ByteStreamReader* stream) const override; + + // |TestCodecSerializer| + void WriteValue(const EncodableValue& value, + ByteStreamWriter* stream) const override; + + private: + static constexpr uint8_t kSomeDataType = 129; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_TESTING_TEST_CODEC_EXTENSIONS_H_