未验证 提交 b28b1dfe 编写于 作者: S stuartmorgan 提交者: GitHub

Allow extending the C++ standard codec (#20317)

Standard*Codec allows for extensions to support arbitrary types; this
had not previously been implemented for the C++ version.

Overview of changes:
- EncodableValue's std::variant type now allows for a new CustomEncodableValue, which is a thin wrapper around std::any, to store arbitrary extension types.
- ByteBufferStream* has been split into an interface class and the buffer-based implementation, with the former now part of the public API surface to be used in standard codec extensions.
  - They also gained utility methods for some common data types to simplify writing extensions.
- StandardCodecSerializer is now part of the public API surface, and is subclassable.
- StandardCodecSerializer's ReadValue has been split into ReadValue and ReadValueOfType to match the structure used when subclassing on the Dart side, for easier porting of custom extensions across languages.
- Standard*Codec now optionally accepts a non-default serializer in GetInstance, providing a shared instance using that serializer.

Fixes https://github.com/flutter/flutter/issues/31174
上级 8ed19649
......@@ -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
......
......@@ -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 = [
......
......@@ -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 <cassert>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <vector>
#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<uint8_t>* 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_
......@@ -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")
// 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<uint8_t*>(&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<uint8_t*>(&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<uint8_t*>(&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<const uint8_t*>(&value), 4);
}
// Writes the given 64-bit int to the stream.
void WriteInt64(int64_t value) {
WriteBytes(reinterpret_cast<const uint8_t*>(&value), 8);
}
// Writes the given 36-bit double to the stream.
void WriteDouble(double value) {
WriteBytes(reinterpret_cast<const uint8_t*>(&value), 8);
}
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BYTE_STREAMS_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 <assert.h>
#include <any>
#include <cassert>
#include <cstdint>
#include <map>
#include <string>
......@@ -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<MyType>(std::get<CustomEncodableValue>(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::monostate,
std::vector<int64_t>,
std::vector<double>,
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<std::monostate,
//
// The primary API surface for this object is std::variant. For instance,
// getting a string value from an EncodableValue, with type checking:
// if (std::get<std::string>(value)) {
// if (std::holds_alternative<std::string>(value)) {
// std::string some_string = std::get<std::string>(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.
//
......
......@@ -2,53 +2,75 @@
// 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_
#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_stream_wrappers.h"
#include "include/flutter/encodable_value.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:
StandardCodecSerializer();
~StandardCodecSerializer();
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(ByteBufferStreamReader* stream) const;
EncodableValue ReadValue(ByteStreamReader* stream) const;
// Writes the encoding of |value| to |stream|.
void WriteValue(const EncodableValue& value,
ByteBufferStreamWriter* 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(ByteBufferStreamReader* stream) const;
size_t ReadSize(ByteStreamReader* stream) const;
// Writes the variable-length size encoding to |stream|.
void WriteSize(size_t size, ByteBufferStreamWriter* stream) const;
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 support list value types of
// |T| must correspond to one of the supported list value types of
// EncodableValue.
template <typename T>
EncodableValue ReadVector(ByteBufferStreamReader* stream) const;
EncodableValue ReadVector(ByteStreamReader* stream) const;
// Writes |vector| to |stream| as a fixed-type list. |T| must correspond to
// one of the support list value types of EncodableValue.
// one of the supported list value types of EncodableValue.
template <typename T>
void WriteVector(const std::vector<T> vector,
ByteBufferStreamWriter* stream) const;
void WriteVector(const std::vector<T> vector, ByteStreamWriter* stream) const;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_ENCODABLE_VALUE_SERIALIZER_H_
#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_CODEC_SERIALIZER_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 <memory>
#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<EncodableValue> {
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<EncodableValue> {
StandardMessageCodec& operator=(StandardMessageCodec const&) = delete;
protected:
// Instances should be obtained via GetInstance.
StandardMessageCodec();
// |flutter::MessageCodec|
std::unique_ptr<EncodableValue> DecodeMessageInternal(
const uint8_t* binary_message,
......@@ -35,6 +44,12 @@ class StandardMessageCodec : public MessageCodec<EncodableValue> {
// |flutter::MessageCodec|
std::unique_ptr<std::vector<uint8_t>> EncodeMessageInternal(
const EncodableValue& message) const override;
private:
// Instances should be obtained via GetInstance.
explicit StandardMessageCodec(const StandardCodecSerializer* serializer);
const StandardCodecSerializer* serializer_;
};
} // namespace flutter
......
......@@ -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 <memory>
#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<EncodableValue> {
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<MethodCall<EncodableValue>> DecodeMethodCallInternal(
const uint8_t* message,
......@@ -51,6 +60,12 @@ class StandardMethodCodec : public MethodCodec<EncodableValue> {
const uint8_t* response,
size_t response_size,
MethodResult<EncodableValue>* result) const override;
private:
// Instances should be obtained via GetInstance.
explicit StandardMethodCodec(const StandardCodecSerializer* serializer);
const StandardCodecSerializer* serializer_;
};
} // namespace flutter
......
......@@ -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 <assert.h>
#include <cassert>
#include <cstring>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#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<EncodedType>(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<uint8_t*>(&int_value), 4);
return EncodableValue(int_value);
}
case EncodedType::kInt64: {
int64_t long_value = 0;
stream->ReadBytes(reinterpret_cast<uint8_t*>(&long_value), 8);
return EncodableValue(long_value);
}
case EncodedType::kFloat64: {
double double_value = 0;
stream->ReadAlignment(8);
stream->ReadBytes(reinterpret_cast<uint8_t*>(&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<uint8_t*>(&string_value[0]), size);
return EncodableValue(string_value);
}
case EncodedType::kUInt8List:
return ReadVector<uint8_t>(stream);
case EncodedType::kInt32List:
return ReadVector<int32_t>(stream);
case EncodedType::kInt64List:
return ReadVector<int64_t>(stream);
case EncodedType::kFloat64List:
return ReadVector<double>(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<int>(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<uint8_t>(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<const uint8_t*>(&int_value), 4);
case EncodableValue::Type::kInt:
stream->WriteInt32(std::get<int32_t>(value));
break;
}
case EncodableValue::Type::kLong: {
int64_t long_value = value.LongValue();
stream->WriteBytes(reinterpret_cast<const uint8_t*>(&long_value), 8);
case case EncodableValue::Type::kLong:
stream->WriteInt64(std::get<int64_t>(value));
break;
}
case EncodableValue::Type::kDouble: {
case EncodableValue::Type::kDouble:
stream->WriteAlignment(8);
double double_value = value.DoubleValue();
stream->WriteBytes(reinterpret_cast<const uint8_t*>(&double_value), 8);
stream->WriteDouble(std::get<double>(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<int32_t>(value);
stream->WriteBytes(reinterpret_cast<const uint8_t*>(&int_value), 4);
case 2:
stream->WriteInt32(std::get<int32_t>(value));
break;
}
case 3: {
int64_t long_value = std::get<int64_t>(value);
stream->WriteBytes(reinterpret_cast<const uint8_t*>(&long_value), 8);
case 3:
stream->WriteInt64(std::get<int64_t>(value));
break;
}
case 4: {
case 4:
stream->WriteAlignment(8);
double double_value = std::get<double>(value);
stream->WriteBytes(reinterpret_cast<const uint8_t*>(&double_value), 8);
stream->WriteDouble(std::get<double>(value));
break;
}
case 5: {
const auto& string_value = std::get<std::string>(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<EncodedType>(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<uint8_t*>(&string_value[0]), size);
return EncodableValue(string_value);
}
case EncodedType::kUInt8List:
return ReadVector<uint8_t>(stream);
case EncodedType::kInt32List:
return ReadVector<int32_t>(stream);
case EncodedType::kInt64List:
return ReadVector<int64_t>(stream);
case EncodedType::kFloat64List:
return ReadVector<double>(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<int>(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<uint8_t>(size));
} else if (size <= 0xffff) {
......@@ -337,7 +332,7 @@ void StandardCodecSerializer::WriteSize(size_t size,
template <typename T>
EncodableValue StandardCodecSerializer::ReadVector(
ByteBufferStreamReader* stream) const {
ByteStreamReader* stream) const {
size_t count = ReadSize(stream);
std::vector<T> vector;
vector.resize(count);
......@@ -351,9 +346,8 @@ EncodableValue StandardCodecSerializer::ReadVector(
}
template <typename T>
void StandardCodecSerializer::WriteVector(
const std::vector<T> vector,
ByteBufferStreamWriter* stream) const {
void StandardCodecSerializer::WriteVector(const std::vector<T> 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<const StandardCodecSerializer*,
std::unique_ptr<StandardMessageCodec>>;
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<StandardMessageCodec>(
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<EncodableValue> StandardMessageCodec::DecodeMessageInternal(
const uint8_t* binary_message,
size_t message_size) const {
StandardCodecSerializer serializer;
ByteBufferStreamReader stream(binary_message, message_size);
return std::make_unique<EncodableValue>(serializer.ReadValue(&stream));
return std::make_unique<EncodableValue>(serializer_->ReadValue(&stream));
}
std::unique_ptr<std::vector<uint8_t>>
StandardMessageCodec::EncodeMessageInternal(
const EncodableValue& message) const {
StandardCodecSerializer serializer;
auto encoded = std::make_unique<std::vector<uint8_t>>();
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<const StandardCodecSerializer*,
std::unique_ptr<StandardMethodCodec>>;
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<StandardMethodCodec>(
new StandardMethodCodec(serializer)));
it = emplace_result.first;
}
return *(it->second);
}
StandardMethodCodec::StandardMethodCodec(
const StandardCodecSerializer* serializer)
: serializer_(serializer) {}
StandardMethodCodec::~StandardMethodCodec() = default;
std::unique_ptr<MethodCall<EncodableValue>>
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<EncodableValue>(serializer.ReadValue(&stream));
std::make_unique<EncodableValue>(serializer_->ReadValue(&stream));
return std::make_unique<MethodCall<EncodableValue>>(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<std::string>(&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<EncodableValue>(serializer.ReadValue(&stream));
std::make_unique<EncodableValue>(serializer_->ReadValue(&stream));
return std::make_unique<MethodCall<EncodableValue>>(*method_name,
std::move(arguments));
#endif
......@@ -439,14 +466,13 @@ StandardMethodCodec::DecodeMethodCallInternal(const uint8_t* message,
std::unique_ptr<std::vector<uint8_t>>
StandardMethodCodec::EncodeMethodCallInternal(
const MethodCall<EncodableValue>& method_call) const {
StandardCodecSerializer serializer;
auto encoded = std::make_unique<std::vector<uint8_t>>();
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<std::vector<uint8_t>>
StandardMethodCodec::EncodeSuccessEnvelopeInternal(
const EncodableValue* result) const {
StandardCodecSerializer serializer;
auto encoded = std::make_unique<std::vector<uint8_t>>();
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<std::vector<uint8_t>>();
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<EncodableValue>* 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(),
......
......@@ -6,21 +6,35 @@
#include <vector>
#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<uint8_t>& 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<uint8_t>& expected_encoding,
const StandardCodecSerializer* serializer = nullptr,
std::function<bool(const EncodableValue& a, const EncodableValue& b)>
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<uint8_t> 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<Point>(std::get<CustomEncodableValue>(a));
const Point& b_point =
std::any_cast<Point>(std::get<CustomEncodableValue>(b));
return a_point == b_point;
};
CheckEncodeDecode(CustomEncodableValue(Point(9, 16)), bytes,
&PointExtensionSerializer::GetInstance(), point_comparator);
}
TEST(StandardMessageCodec, CanEncodeAndDecodeVariableLengthCustomType) {
std::vector<uint8_t> 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<SomeData>(std::get<CustomEncodableValue>(a));
const SomeData& data_b =
std::any_cast<SomeData>(std::get<CustomEncodableValue>(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
......@@ -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<EncodableValue> call(
"hello", std::make_unique<EncodableValue>(CustomEncodableValue(point)));
auto encoded = codec.EncodeMethodCall(call);
ASSERT_NE(encoded.get(), nullptr);
std::unique_ptr<MethodCall<EncodableValue>> decoded =
codec.DecodeMethodCall(*encoded);
ASSERT_NE(decoded.get(), nullptr);
const Point& decoded_point = std::any_cast<Point>(
std::get<CustomEncodableValue>(*decoded->arguments()));
EXPECT_EQ(point, decoded_point);
};
} // namespace flutter
// 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<CustomEncodableValue>(&value);
if (!custom_value) {
StandardCodecSerializer::WriteValue(value, stream);
return;
}
stream->WriteByte(kPointType);
const Point& point = std::any_cast<Point>(*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<uint8_t> data;
data.resize(size);
stream->ReadBytes(data.data(), size);
EncodableValue label = ReadValue(stream);
return CustomEncodableValue(SomeData(std::get<std::string>(label), data));
}
return StandardCodecSerializer::ReadValueOfType(type, stream);
}
void SomeDataExtensionSerializer::WriteValue(const EncodableValue& value,
ByteStreamWriter* stream) const {
auto custom_value = std::get_if<CustomEncodableValue>(&value);
if (!custom_value) {
StandardCodecSerializer::WriteValue(value, stream);
return;
}
stream->WriteByte(kSomeDataType);
const SomeData& some_data = std::any_cast<SomeData>(*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
// 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<uint8_t>& data)
: label_(label), data_(data) {}
~SomeData() = default;
const std::string& label() const { return label_; }
const std::vector<uint8_t>& data() const { return data_; }
private:
std::string label_;
std::vector<uint8_t> 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_
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册