diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c7a07c4ac1e56..425a3152820ef 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1199,6 +1199,9 @@ FILE: ../../../flutter/shell/platform/linux/fl_renderer.cc FILE: ../../../flutter/shell/platform/linux/fl_renderer.h FILE: ../../../flutter/shell/platform/linux/fl_renderer_x11.cc FILE: ../../../flutter/shell/platform/linux/fl_renderer_x11.h +FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec.cc +FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_private.h +FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_test.cc FILE: ../../../flutter/shell/platform/linux/fl_string_codec.cc FILE: ../../../flutter/shell/platform/linux/fl_string_codec_test.cc FILE: ../../../flutter/shell/platform/linux/fl_value.cc @@ -1209,6 +1212,7 @@ FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_binary_messe FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_engine.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_message_codec.h +FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_string_codec.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_value.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_view.h diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index d9472cb4f9b11..cae44d2c3566d 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -49,6 +49,7 @@ _public_headers = [ "public/flutter_linux/fl_dart_project.h", "public/flutter_linux/fl_engine.h", "public/flutter_linux/fl_message_codec.h", + "public/flutter_linux/fl_standard_message_codec.h", "public/flutter_linux/fl_string_codec.h", "public/flutter_linux/fl_value.h", "public/flutter_linux/fl_view.h", @@ -70,6 +71,7 @@ source_set("flutter_linux") { "fl_message_codec.cc", "fl_renderer.cc", "fl_renderer_x11.cc", + "fl_standard_message_codec.cc", "fl_string_codec.cc", "fl_value.cc", "fl_view.cc", @@ -100,6 +102,7 @@ executable("flutter_linux_unittests") { "fl_binary_codec_test.cc", "fl_dart_project_test.cc", "fl_message_codec_test.cc", + "fl_standard_message_codec_test.cc", "fl_string_codec_test.cc", "fl_value_test.cc", "testing/fl_test.cc", diff --git a/shell/platform/linux/fl_standard_message_codec.cc b/shell/platform/linux/fl_standard_message_codec.cc new file mode 100644 index 0000000000000..b8423f7a9978f --- /dev/null +++ b/shell/platform/linux/fl_standard_message_codec.cc @@ -0,0 +1,563 @@ +// 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/linux/public/flutter_linux/fl_standard_message_codec.h" +#include "flutter/shell/platform/linux/fl_standard_message_codec_private.h" + +#include + +// See lib/src/services/message_codecs.dart in Flutter source for description of +// encoding. + +// Type values. +static constexpr int kValueNull = 0; +static constexpr int kValueTrue = 1; +static constexpr int kValueFalse = 2; +static constexpr int kValueInt32 = 3; +static constexpr int kValueInt64 = 4; +static constexpr int kValueFloat64 = 6; +static constexpr int kValueString = 7; +static constexpr int kValueUint8List = 8; +static constexpr int kValueInt32List = 9; +static constexpr int kValueInt64List = 10; +static constexpr int kValueFloat64List = 11; +static constexpr int kValueList = 12; +static constexpr int kValueMap = 13; + +struct _FlStandardMessageCodec { + FlMessageCodec parent_instance; +}; + +G_DEFINE_TYPE(FlStandardMessageCodec, + fl_standard_message_codec, + fl_message_codec_get_type()) + +// Functions to write standard C number types. + +static void write_uint8(GByteArray* buffer, uint8_t value) { + g_byte_array_append(buffer, &value, sizeof(uint8_t)); +} + +static void write_uint16(GByteArray* buffer, uint16_t value) { + g_byte_array_append(buffer, reinterpret_cast(&value), + sizeof(uint16_t)); +} + +static void write_uint32(GByteArray* buffer, uint32_t value) { + g_byte_array_append(buffer, reinterpret_cast(&value), + sizeof(uint32_t)); +} + +static void write_int32(GByteArray* buffer, int32_t value) { + g_byte_array_append(buffer, reinterpret_cast(&value), + sizeof(int32_t)); +} + +static void write_int64(GByteArray* buffer, int64_t value) { + g_byte_array_append(buffer, reinterpret_cast(&value), + sizeof(int64_t)); +} + +static void write_float64(GByteArray* buffer, double value) { + g_byte_array_append(buffer, reinterpret_cast(&value), + sizeof(double)); +} + +// Checks there is enough data in @buffer to be read. +static gboolean check_size(GBytes* buffer, + size_t offset, + size_t required, + GError** error) { + if (offset + required > g_bytes_get_size(buffer)) { + g_set_error(error, FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA, "Unexpected end of data"); + return FALSE; + } + return TRUE; +} + +// Gets a pointer to the given offset in @buffer. +static const uint8_t* get_data(GBytes* buffer, size_t* offset) { + return static_cast(g_bytes_get_data(buffer, nullptr)) + + *offset; +} + +// Reads an unsigned 8 bit number from @buffer and writes it to @value. +// Returns TRUE if successful, otherwise sets an error. +static gboolean read_uint8(GBytes* buffer, + size_t* offset, + uint8_t* value, + GError** error) { + if (!check_size(buffer, *offset, sizeof(uint8_t), error)) + return FALSE; + + *value = get_data(buffer, offset)[0]; + (*offset)++; + return TRUE; +} + +// Reads an unsigned 16 bit integer from @buffer and writes it to @value. +// Returns TRUE if successful, otherwise sets an error. +static gboolean read_uint16(GBytes* buffer, + size_t* offset, + uint16_t* value, + GError** error) { + if (!check_size(buffer, *offset, sizeof(uint16_t), error)) + return FALSE; + + *value = reinterpret_cast(get_data(buffer, offset))[0]; + *offset += sizeof(uint16_t); + return TRUE; +} + +// Reads an unsigned 32 bit integer from @buffer and writes it to @value. +// Returns TRUE if successful, otherwise sets an error. +static gboolean read_uint32(GBytes* buffer, + size_t* offset, + uint32_t* value, + GError** error) { + if (!check_size(buffer, *offset, sizeof(uint32_t), error)) + return FALSE; + + *value = reinterpret_cast(get_data(buffer, offset))[0]; + *offset += sizeof(uint32_t); + return TRUE; +} + +// Reads a #FL_VALUE_TYPE_INT stored as a signed 32 bit integer from @buffer. +// Returns a new #FlValue of type #FL_VALUE_TYPE_INT if successful or %NULL on +// error. +static FlValue* read_int32_value(GBytes* buffer, + size_t* offset, + GError** error) { + if (!check_size(buffer, *offset, sizeof(int32_t), error)) + return nullptr; + + FlValue* value = fl_value_new_int( + reinterpret_cast(get_data(buffer, offset))[0]); + *offset += sizeof(int32_t); + return value; +} + +// Reads a #FL_VALUE_TYPE_INT stored as a signed 64 bit integer from @buffer. +// Returns a new #FlValue of type #FL_VALUE_TYPE_INT if successful or %NULL on +// error. +static FlValue* read_int64_value(GBytes* buffer, + size_t* offset, + GError** error) { + if (!check_size(buffer, *offset, sizeof(int64_t), error)) + return nullptr; + + FlValue* value = fl_value_new_int( + reinterpret_cast(get_data(buffer, offset))[0]); + *offset += sizeof(int64_t); + return value; +} + +// Reads a 64 bit floating point number from @buffer and writes it to @value. +// Returns a new #FlValue of type #FL_VALUE_TYPE_FLOAT if successful or %NULL on +// error. +static FlValue* read_float64_value(GBytes* buffer, + size_t* offset, + GError** error) { + if (!check_size(buffer, *offset, sizeof(double), error)) + return nullptr; + + FlValue* value = fl_value_new_float( + reinterpret_cast(get_data(buffer, offset))[0]); + *offset += sizeof(double); + return value; +} + +// Reads an UTF-8 text string from @buffer in standard codec format. +// Returns a new #FlValue of type #FL_VALUE_TYPE_STRING if successful or %NULL +// on error. +static FlValue* read_string_value(FlStandardMessageCodec* self, + GBytes* buffer, + size_t* offset, + GError** error) { + uint32_t length; + if (!fl_standard_message_codec_read_size(self, buffer, offset, &length, + error)) + return nullptr; + if (!check_size(buffer, *offset, length, error)) + return nullptr; + FlValue* value = fl_value_new_string_sized( + reinterpret_cast(get_data(buffer, offset)), length); + *offset += length; + return value; +} + +// Reads an unsigned 8 bit list from @buffer in standard codec format. +// Returns a new #FlValue of type #FL_VALUE_TYPE_UINT8_LIST if successful or +// %NULL on error. +static FlValue* read_uint8_list_value(FlStandardMessageCodec* self, + GBytes* buffer, + size_t* offset, + GError** error) { + uint32_t length; + if (!fl_standard_message_codec_read_size(self, buffer, offset, &length, + error)) + return nullptr; + if (!check_size(buffer, *offset, sizeof(uint8_t) * length, error)) + return nullptr; + FlValue* value = fl_value_new_uint8_list(get_data(buffer, offset), length); + *offset += length; + return value; +} + +// Reads a signed 32 bit list from @buffer in standard codec format. +// Returns a new #FlValue of type #FL_VALUE_TYPE_INT32_LIST if successful or +// %NULL on error. +static FlValue* read_int32_list_value(FlStandardMessageCodec* self, + GBytes* buffer, + size_t* offset, + GError** error) { + uint32_t length; + if (!fl_standard_message_codec_read_size(self, buffer, offset, &length, + error)) + return nullptr; + if (!check_size(buffer, *offset, sizeof(int32_t) * length, error)) + return nullptr; + FlValue* value = fl_value_new_int32_list( + reinterpret_cast(get_data(buffer, offset)), length); + *offset += sizeof(int32_t) * length; + return value; +} + +// Reads a signed 64 bit list from @buffer in standard codec format. +// Returns a new #FlValue of type #FL_VALUE_TYPE_INT64_LIST if successful or +// %NULL on error. +static FlValue* read_int64_list_value(FlStandardMessageCodec* self, + GBytes* buffer, + size_t* offset, + GError** error) { + uint32_t length; + if (!fl_standard_message_codec_read_size(self, buffer, offset, &length, + error)) + return nullptr; + if (!check_size(buffer, *offset, sizeof(int64_t) * length, error)) + return nullptr; + FlValue* value = fl_value_new_int64_list( + reinterpret_cast(get_data(buffer, offset)), length); + *offset += sizeof(int64_t) * length; + return value; +} + +// Reads a floating point number list from @buffer in standard codec format. +// Returns a new #FlValue of type #FL_VALUE_TYPE_FLOAT_LIST if successful or +// %NULL on error. +static FlValue* read_float64_list_value(FlStandardMessageCodec* self, + GBytes* buffer, + size_t* offset, + GError** error) { + uint32_t length; + if (!fl_standard_message_codec_read_size(self, buffer, offset, &length, + error)) + return nullptr; + if (!check_size(buffer, *offset, sizeof(double) * length, error)) + return nullptr; + FlValue* value = fl_value_new_float_list( + reinterpret_cast(get_data(buffer, offset)), length); + *offset += sizeof(double) * length; + return value; +} + +// Reads a list from @buffer in standard codec format. +// Returns a new #FlValue of type #FL_VALUE_TYPE_LIST if successful or %NULL on +// error. +static FlValue* read_list_value(FlStandardMessageCodec* self, + GBytes* buffer, + size_t* offset, + GError** error) { + uint32_t length; + if (!fl_standard_message_codec_read_size(self, buffer, offset, &length, + error)) + return nullptr; + + g_autoptr(FlValue) list = fl_value_new_list(); + for (size_t i = 0; i < length; i++) { + g_autoptr(FlValue) child = + fl_standard_message_codec_read_value(self, buffer, offset, error); + if (child == nullptr) + return nullptr; + fl_value_append(list, child); + } + + return fl_value_ref(list); +} + +// Reads a map from @buffer in standard codec format. +// Returns a new #FlValue of type #FL_VALUE_TYPE_MAP if successful or %NULL on +// error. +static FlValue* read_map_value(FlStandardMessageCodec* self, + GBytes* buffer, + size_t* offset, + GError** error) { + uint32_t length; + if (!fl_standard_message_codec_read_size(self, buffer, offset, &length, + error)) + return nullptr; + + g_autoptr(FlValue) map = fl_value_new_map(); + for (size_t i = 0; i < length; i++) { + g_autoptr(FlValue) key = + fl_standard_message_codec_read_value(self, buffer, offset, error); + if (key == nullptr) + return nullptr; + g_autoptr(FlValue) value = + fl_standard_message_codec_read_value(self, buffer, offset, error); + if (value == nullptr) + return nullptr; + fl_value_set(map, key, value); + } + + return fl_value_ref(map); +} + +// Implements FlMessageCodec::encode_message. +static GBytes* fl_standard_message_codec_encode_message(FlMessageCodec* codec, + FlValue* message, + GError** error) { + FlStandardMessageCodec* self = + reinterpret_cast(codec); + + g_autoptr(GByteArray) buffer = g_byte_array_new(); + if (!fl_standard_message_codec_write_value(self, buffer, message, error)) + return nullptr; + return g_byte_array_free_to_bytes( + static_cast(g_steal_pointer(&buffer))); +} + +// Implements FlMessageCodec::decode_message. +static FlValue* fl_standard_message_codec_decode_message(FlMessageCodec* codec, + GBytes* message, + GError** error) { + FlStandardMessageCodec* self = + reinterpret_cast(codec); + + size_t offset = 0; + g_autoptr(FlValue) value = + fl_standard_message_codec_read_value(self, message, &offset, error); + if (value == nullptr) + return nullptr; + + if (offset != g_bytes_get_size(message)) { + g_set_error(error, FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_ADDITIONAL_DATA, + "Unused %zi bytes after standard message", + g_bytes_get_size(message) - offset); + return nullptr; + } + + return fl_value_ref(value); +} + +static void fl_standard_message_codec_class_init( + FlStandardMessageCodecClass* klass) { + FL_MESSAGE_CODEC_CLASS(klass)->encode_message = + fl_standard_message_codec_encode_message; + FL_MESSAGE_CODEC_CLASS(klass)->decode_message = + fl_standard_message_codec_decode_message; +} + +static void fl_standard_message_codec_init(FlStandardMessageCodec* self) {} + +G_MODULE_EXPORT FlStandardMessageCodec* fl_standard_message_codec_new() { + return static_cast( + g_object_new(fl_standard_message_codec_get_type(), nullptr)); +} + +void fl_standard_message_codec_write_size(FlStandardMessageCodec* codec, + GByteArray* buffer, + uint32_t size) { + if (size < 254) + write_uint8(buffer, size); + else if (size <= 0xffff) { + write_uint8(buffer, 254); + write_uint16(buffer, size); + } else { + write_uint8(buffer, 255); + write_uint32(buffer, size); + } +} + +gboolean fl_standard_message_codec_read_size(FlStandardMessageCodec* codec, + GBytes* buffer, + size_t* offset, + uint32_t* value, + GError** error) { + uint8_t value8; + if (!read_uint8(buffer, offset, &value8, error)) + return FALSE; + + if (value8 == 255) { + if (!read_uint32(buffer, offset, value, error)) + return FALSE; + } else if (value8 == 254) { + uint16_t value16; + if (!read_uint16(buffer, offset, &value16, error)) + return FALSE; + *value = value16; + } else + *value = value8; + + return TRUE; +} + +gboolean fl_standard_message_codec_write_value(FlStandardMessageCodec* self, + GByteArray* buffer, + FlValue* value, + GError** error) { + if (value == nullptr) { + write_uint8(buffer, kValueNull); + return TRUE; + } + + switch (fl_value_get_type(value)) { + case FL_VALUE_TYPE_NULL: + write_uint8(buffer, kValueNull); + return TRUE; + case FL_VALUE_TYPE_BOOL: + if (fl_value_get_bool(value)) + write_uint8(buffer, kValueTrue); + else + write_uint8(buffer, kValueFalse); + return TRUE; + case FL_VALUE_TYPE_INT: { + int64_t v = fl_value_get_int(value); + if (v >= INT32_MIN && v <= INT32_MAX) { + write_uint8(buffer, kValueInt32); + write_int32(buffer, v); + } else { + write_uint8(buffer, kValueInt64); + write_int64(buffer, v); + } + return TRUE; + } + case FL_VALUE_TYPE_FLOAT: + write_uint8(buffer, kValueFloat64); + write_float64(buffer, fl_value_get_float(value)); + return TRUE; + case FL_VALUE_TYPE_STRING: { + write_uint8(buffer, kValueString); + const char* text = fl_value_get_string(value); + size_t length = strlen(text); + fl_standard_message_codec_write_size(self, buffer, length); + g_byte_array_append(buffer, reinterpret_cast(text), + length); + return TRUE; + } + case FL_VALUE_TYPE_UINT8_LIST: { + write_uint8(buffer, kValueUint8List); + size_t length = fl_value_get_length(value); + fl_standard_message_codec_write_size(self, buffer, length); + g_byte_array_append(buffer, fl_value_get_uint8_list(value), + sizeof(uint8_t) * length); + return TRUE; + } + case FL_VALUE_TYPE_INT32_LIST: { + write_uint8(buffer, kValueInt32List); + size_t length = fl_value_get_length(value); + fl_standard_message_codec_write_size(self, buffer, length); + g_byte_array_append( + buffer, + reinterpret_cast(fl_value_get_int32_list(value)), + sizeof(int32_t) * length); + return TRUE; + } + case FL_VALUE_TYPE_INT64_LIST: { + write_uint8(buffer, kValueInt64List); + size_t length = fl_value_get_length(value); + fl_standard_message_codec_write_size(self, buffer, length); + g_byte_array_append( + buffer, + reinterpret_cast(fl_value_get_int64_list(value)), + sizeof(int64_t) * length); + return TRUE; + } + case FL_VALUE_TYPE_FLOAT_LIST: { + write_uint8(buffer, kValueFloat64List); + size_t length = fl_value_get_length(value); + fl_standard_message_codec_write_size(self, buffer, length); + g_byte_array_append( + buffer, + reinterpret_cast(fl_value_get_float_list(value)), + sizeof(double) * length); + return TRUE; + } + case FL_VALUE_TYPE_LIST: + write_uint8(buffer, kValueList); + fl_standard_message_codec_write_size(self, buffer, + fl_value_get_length(value)); + for (size_t i = 0; i < fl_value_get_length(value); i++) { + if (!fl_standard_message_codec_write_value( + self, buffer, fl_value_get_list_value(value, i), error)) + return FALSE; + } + return TRUE; + case FL_VALUE_TYPE_MAP: + write_uint8(buffer, kValueMap); + fl_standard_message_codec_write_size(self, buffer, + fl_value_get_length(value)); + for (size_t i = 0; i < fl_value_get_length(value); i++) { + if (!fl_standard_message_codec_write_value( + self, buffer, fl_value_get_map_key(value, i), error) || + !fl_standard_message_codec_write_value( + self, buffer, fl_value_get_map_value(value, i), error)) + return FALSE; + } + return TRUE; + } + + g_set_error(error, FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_UNSUPPORTED_TYPE, + "Unexpected FlValue type %d", fl_value_get_type(value)); + return FALSE; +} + +FlValue* fl_standard_message_codec_read_value(FlStandardMessageCodec* self, + GBytes* buffer, + size_t* offset, + GError** error) { + uint8_t type; + if (!read_uint8(buffer, offset, &type, error)) + return nullptr; + + g_autoptr(FlValue) value = nullptr; + if (type == kValueNull) + return fl_value_new_null(); + else if (type == kValueTrue) + return fl_value_new_bool(TRUE); + else if (type == kValueFalse) + return fl_value_new_bool(FALSE); + else if (type == kValueInt32) + value = read_int32_value(buffer, offset, error); + else if (type == kValueInt64) + value = read_int64_value(buffer, offset, error); + else if (type == kValueFloat64) + value = read_float64_value(buffer, offset, error); + else if (type == kValueString) + value = read_string_value(self, buffer, offset, error); + else if (type == kValueUint8List) + value = read_uint8_list_value(self, buffer, offset, error); + else if (type == kValueInt32List) + value = read_int32_list_value(self, buffer, offset, error); + else if (type == kValueInt64List) + value = read_int64_list_value(self, buffer, offset, error); + else if (type == kValueFloat64List) + value = read_float64_list_value(self, buffer, offset, error); + else if (type == kValueList) + value = read_list_value(self, buffer, offset, error); + else if (type == kValueMap) + value = read_map_value(self, buffer, offset, error); + else { + g_set_error(error, FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_UNSUPPORTED_TYPE, + "Unexpected standard codec type %02x", type); + return nullptr; + } + + return value == nullptr ? nullptr : fl_value_ref(value); +} diff --git a/shell/platform/linux/fl_standard_message_codec_private.h b/shell/platform/linux/fl_standard_message_codec_private.h new file mode 100644 index 0000000000000..c14ec95829906 --- /dev/null +++ b/shell/platform/linux/fl_standard_message_codec_private.h @@ -0,0 +1,77 @@ +// 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_LINUX_FL_STANDARD_MESSAGE_CODEC_PRIVATE_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_STANDARD_MESSAGE_CODEC_PRIVATE_H_ + +#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h" + +G_BEGIN_DECLS + +/** + * fl_standard_message_codec_write_size: + * @codec: a #FlStandardMessageCodec + * @buffer: buffer to write into + * @size: size value to write + * + * Writes a size field in Flutter Standard encoding. + */ +void fl_standard_message_codec_write_size(FlStandardMessageCodec* codec, + GByteArray* buffer, + uint32_t size); + +/** + * fl_standard_message_codec_read_size: + * @codec: a #FlStandardMessageCodec + * @buffer: buffer to read from + * @offset: (inout): read position in @buffer + * @value: location to read size + * @error: (allow-none): #GError location to store the error occurring, or %NULL + * + * Reads a size field in Flutter Standard encoding. + * + * Returns: %TRUE on success. + */ +gboolean fl_standard_message_codec_read_size(FlStandardMessageCodec* codec, + GBytes* buffer, + size_t* offset, + uint32_t* value, + GError** error); + +/** + * fl_standard_message_codec_write_value: + * @codec: a #FlStandardMessageCodec + * @buffer: buffer to write into + * @value: (allow-none): value to write + * @error: (allow-none): #GError location to store the error occurring, or %NULL + * + * Writes a #FlValue in Flutter Standard encoding. + * + * Returns: %TRUE on success. + */ +gboolean fl_standard_message_codec_write_value(FlStandardMessageCodec* codec, + GByteArray* buffer, + FlValue* value, + GError** error); + +/** + * fl_standard_message_codec_read_value: + * @codec: a #FlStandardMessageCodec + * @buffer: buffer to read from + * @offset: (inout): read position in @buffer + * @value: location to read size + * @error: (allow-none): #GError location to store the error occurring, or %NULL + * + * Reads a #FlValue in Flutter Standard encoding. + * + * Returns: a new #FlValue or %NULL on error. + */ +FlValue* fl_standard_message_codec_read_value(FlStandardMessageCodec* codec, + GBytes* buffer, + size_t* offset, + GError** error); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_STANDARD_MESSAGE_CODEC_PRIVATE_H_ diff --git a/shell/platform/linux/fl_standard_message_codec_test.cc b/shell/platform/linux/fl_standard_message_codec_test.cc new file mode 100644 index 0000000000000..2d9e18c7e04ca --- /dev/null +++ b/shell/platform/linux/fl_standard_message_codec_test.cc @@ -0,0 +1,1032 @@ +// 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/linux/public/flutter_linux/fl_standard_message_codec.h" +#include "flutter/shell/platform/linux/testing/fl_test.h" +#include "gtest/gtest.h" + +// NOTE(robert-ancell) These test cases assumes a little-endian architecture. +// These tests will need to be updated if tested on a big endian architecture. + +// Encodes a message using a FlStandardMessageCodec. Return a hex string with +// the encoded binary output. +static gchar* encode_message(FlValue* value) { + g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); + g_autoptr(GError) error = nullptr; + g_autoptr(GBytes) message = + fl_message_codec_encode_message(FL_MESSAGE_CODEC(codec), value, &error); + EXPECT_NE(message, nullptr); + EXPECT_EQ(error, nullptr); + + return bytes_to_hex_string(message); +} + +// Decodes a message using a FlStandardMessageCodec. The binary data is given in +// the form of a hex string. +static FlValue* decode_message(const char* hex_string) { + g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); + g_autoptr(GBytes) data = hex_string_to_bytes(hex_string); + g_autoptr(GError) error = nullptr; + g_autoptr(FlValue) value = + fl_message_codec_decode_message(FL_MESSAGE_CODEC(codec), data, &error); + EXPECT_EQ(error, nullptr); + EXPECT_NE(value, nullptr); + return fl_value_ref(value); +} + +// Decodes a message using a FlStandardMessageCodec. The binary data is given in +// the form of a hex string. Expect the given error. +static void decode_error_value(const char* hex_string, + GQuark domain, + gint code) { + g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); + g_autoptr(GBytes) data = hex_string_to_bytes(hex_string); + g_autoptr(GError) error = nullptr; + g_autoptr(FlValue) value = + fl_message_codec_decode_message(FL_MESSAGE_CODEC(codec), data, &error); + EXPECT_TRUE(value == nullptr); + EXPECT_TRUE(g_error_matches(error, domain, code)); +} + +TEST(FlStandardMessageCodecTest, EncodeNullptr) { + g_autofree gchar* hex_string = encode_message(nullptr); + EXPECT_STREQ(hex_string, "00"); +} + +TEST(FlStandardMessageCodecTest, EncodeNull) { + g_autoptr(FlValue) value = fl_value_new_null(); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, "00"); +} + +static gchar* encode_bool(gboolean value) { + g_autoptr(FlValue) v = fl_value_new_bool(value); + return encode_message(v); +} + +TEST(FlStandardMessageCodecTest, EncodeBoolFalse) { + g_autofree gchar* hex_string = encode_bool(FALSE); + EXPECT_STREQ(hex_string, "02"); +} + +TEST(FlStandardMessageCodecTest, EncodeBoolTrue) { + g_autofree gchar* hex_string = encode_bool(TRUE); + EXPECT_STREQ(hex_string, "01"); +} + +static gchar* encode_int(int64_t value) { + g_autoptr(FlValue) v = fl_value_new_int(value); + return encode_message(v); +} + +TEST(FlStandardMessageCodecTest, EncodeIntZero) { + g_autofree gchar* hex_string = encode_int(0); + EXPECT_STREQ(hex_string, "0300000000"); +} + +TEST(FlStandardMessageCodecTest, EncodeIntOne) { + g_autofree gchar* hex_string = encode_int(1); + EXPECT_STREQ(hex_string, "0301000000"); +} + +TEST(FlStandardMessageCodecTest, EncodeInt32) { + g_autofree gchar* hex_string = encode_int(0x01234567); + EXPECT_STREQ(hex_string, "0367452301"); +} + +TEST(FlStandardMessageCodecTest, EncodeInt32Min) { + g_autofree gchar* hex_string = encode_int(G_MININT32); + EXPECT_STREQ(hex_string, "0300000080"); +} + +TEST(FlStandardMessageCodecTest, EncodeInt32Max) { + g_autofree gchar* hex_string = encode_int(G_MAXINT32); + EXPECT_STREQ(hex_string, "03ffffff7f"); +} + +TEST(FlStandardMessageCodecTest, EncodeInt64) { + g_autofree gchar* hex_string = encode_int(0x0123456789abcdef); + EXPECT_STREQ(hex_string, "04efcdab8967452301"); +} + +TEST(FlStandardMessageCodecTest, EncodeInt64Min) { + g_autofree gchar* hex_string = encode_int(G_MININT64); + EXPECT_STREQ(hex_string, "040000000000000080"); +} + +TEST(FlStandardMessageCodecTest, EncodeInt64Max) { + g_autofree gchar* hex_string = encode_int(G_MAXINT64); + EXPECT_STREQ(hex_string, "04ffffffffffffff7f"); +} + +TEST(FlStandardMessageCodecTest, DecodeIntZero) { + g_autoptr(FlValue) value = decode_message("0300000000"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_int(value), 0); +} + +TEST(FlStandardMessageCodecTest, DecodeIntOne) { + g_autoptr(FlValue) value = decode_message("0301000000"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_int(value), 1); +} + +TEST(FlStandardMessageCodecTest, DecodeInt32) { + g_autoptr(FlValue) value = decode_message("0367452301"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_int(value), 0x01234567); +} + +TEST(FlStandardMessageCodecTest, DecodeInt32Min) { + g_autoptr(FlValue) value = decode_message("0300000080"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_int(value), G_MININT32); +} + +TEST(FlStandardMessageCodecTest, DecodeInt32Max) { + g_autoptr(FlValue) value = decode_message("03ffffff7f"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_int(value), G_MAXINT32); +} + +TEST(FlStandardMessageCodecTest, DecodeInt64) { + g_autoptr(FlValue) value = decode_message("04efcdab8967452301"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_int(value), 0x0123456789abcdef); +} + +TEST(FlStandardMessageCodecTest, DecodeInt64Min) { + g_autoptr(FlValue) value = decode_message("040000000000000080"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_int(value), G_MININT64); +} + +TEST(FlStandardMessageCodecTest, DecodeInt64Max) { + g_autoptr(FlValue) value = decode_message("04ffffffffffffff7f"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_int(value), G_MAXINT64); +} + +TEST(FlStandardMessageCodecTest, DecodeInt32NoData) { + decode_error_value("03", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeIntShortData1) { + decode_error_value("0367", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeIntShortData2) { + decode_error_value("03674523", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeInt64NoData) { + decode_error_value("04", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeInt64ShortData1) { + decode_error_value("04ef", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeInt64ShortData2) { + decode_error_value("04efcdab89674523", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +static gchar* encode_float(double value) { + g_autoptr(FlValue) v = fl_value_new_float(value); + return encode_message(v); +} + +TEST(FlStandardMessageCodecTest, EncodeFloatZero) { + g_autofree gchar* hex_string = encode_float(0); + EXPECT_STREQ(hex_string, "060000000000000000"); +} + +TEST(FlStandardMessageCodecTest, EncodeFloatOne) { + g_autofree gchar* hex_string = encode_float(1); + EXPECT_STREQ(hex_string, "06000000000000f03f"); +} + +TEST(FlStandardMessageCodecTest, EncodeFloatMinusOne) { + g_autofree gchar* hex_string = encode_float(-1); + EXPECT_STREQ(hex_string, "06000000000000f0bf"); +} + +TEST(FlStandardMessageCodecTest, EncodeFloatHalf) { + g_autofree gchar* hex_string = encode_float(0.5); + EXPECT_STREQ(hex_string, "06000000000000e03f"); +} + +TEST(FlStandardMessageCodecTest, EncodeFloatFraction) { + g_autofree gchar* hex_string = encode_float(M_PI); + EXPECT_STREQ(hex_string, "06182d4454fb210940"); +} + +TEST(FlStandardMessageCodecTest, EncodeFloatMinusZero) { + g_autofree gchar* hex_string = encode_float(-0.0); + EXPECT_STREQ(hex_string, "060000000000000080"); +} + +TEST(FlStandardMessageCodecTest, EncodeFloatNaN) { + g_autofree gchar* hex_string = encode_float(NAN); + EXPECT_STREQ(hex_string, "06000000000000f87f"); +} + +TEST(FlStandardMessageCodecTest, EncodeFloatInfinity) { + g_autofree gchar* hex_string = encode_float(INFINITY); + EXPECT_STREQ(hex_string, "06000000000000f07f"); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatZero) { + g_autoptr(FlValue) value = decode_message("060000000000000000"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_FLOAT); + EXPECT_EQ(fl_value_get_float(value), 0.0); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatOne) { + g_autoptr(FlValue) value = decode_message("06000000000000f03f"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_FLOAT); + EXPECT_EQ(fl_value_get_float(value), 1.0); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatMinusOne) { + g_autoptr(FlValue) value = decode_message("06000000000000f0bf"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_FLOAT); + EXPECT_EQ(fl_value_get_float(value), -1.0); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatHalf) { + g_autoptr(FlValue) value = decode_message("06000000000000e03f"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_FLOAT); + EXPECT_EQ(fl_value_get_float(value), 0.5); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatPi) { + g_autoptr(FlValue) value = decode_message("06182d4454fb210940"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_FLOAT); + EXPECT_EQ(fl_value_get_float(value), M_PI); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatMinusZero) { + g_autoptr(FlValue) value = decode_message("060000000000000080"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_FLOAT); + EXPECT_EQ(fl_value_get_float(value), -0.0); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatNaN) { + g_autoptr(FlValue) value = decode_message("06000000000000f87f"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_FLOAT); + EXPECT_TRUE(isnan(fl_value_get_float(value))); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatInfinity) { + g_autoptr(FlValue) value = decode_message("06000000000000f07f"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_FLOAT); + EXPECT_TRUE(isinf(fl_value_get_float(value))); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatNoData) { + decode_error_value("0600", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatShortData1) { + decode_error_value("0600", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatShortData2) { + decode_error_value("0600000000000000", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +static gchar* encode_string(const gchar* value) { + g_autoptr(FlValue) v = fl_value_new_string(value); + return encode_message(v); +} + +TEST(FlStandardMessageCodecTest, EncodeStringEmpty) { + g_autofree gchar* hex_string = encode_string(""); + EXPECT_STREQ(hex_string, "0700"); +} + +TEST(FlStandardMessageCodecTest, EncodeStringHello) { + g_autofree gchar* hex_string = encode_string("hello"); + EXPECT_STREQ(hex_string, "070568656c6c6f"); +} + +TEST(FlStandardMessageCodecTest, EncodeStringEmptySized) { + g_autoptr(FlValue) value = fl_value_new_string_sized(nullptr, 0); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, "0700"); +} + +TEST(FlStandardMessageCodecTest, EncodeStringHelloSized) { + g_autoptr(FlValue) value = fl_value_new_string_sized("Hello World", 5); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, "070548656c6c6f"); +} + +TEST(FlStandardMessageCodecTest, DecodeStringEmpty) { + g_autoptr(FlValue) value = decode_message("0700"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(value), ""); +} + +TEST(FlStandardMessageCodecTest, DecodeStringHello) { + g_autoptr(FlValue) value = decode_message("070568656c6c6f"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(value), "hello"); +} + +TEST(FlStandardMessageCodecTest, DecodeStringNoData) { + decode_error_value("07", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeStringLengthNoData) { + decode_error_value("0705", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeStringShortData1) { + decode_error_value("070568", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeStringShortData2) { + decode_error_value("070568656c6c", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, EncodeUint8ListEmpty) { + g_autoptr(FlValue) value = fl_value_new_uint8_list(nullptr, 0); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, "0800"); +} + +TEST(FlStandardMessageCodecTest, EncodeUint8List) { + uint8_t data[] = {0, 1, 2, 3, 4}; + g_autoptr(FlValue) value = fl_value_new_uint8_list(data, 5); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, "08050001020304"); +} + +TEST(FlStandardMessageCodecTest, DecodeUint8ListEmpty) { + g_autoptr(FlValue) value = decode_message("0800"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_UINT8_LIST); + EXPECT_EQ(fl_value_get_length(value), static_cast(0)); +} + +TEST(FlStandardMessageCodecTest, DecodeUint8List) { + g_autoptr(FlValue) value = decode_message("08050001020304"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_UINT8_LIST); + EXPECT_EQ(fl_value_get_length(value), static_cast(5)); + const uint8_t* data = fl_value_get_uint8_list(value); + EXPECT_EQ(data[0], 0); + EXPECT_EQ(data[1], 1); + EXPECT_EQ(data[2], 2); + EXPECT_EQ(data[3], 3); + EXPECT_EQ(data[4], 4); +} + +TEST(FlStandardMessageCodecTest, DecodeUint8ListNoData) { + decode_error_value("08", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeUint8ListLengthNoData) { + decode_error_value("0805", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeUint8ListShortData1) { + decode_error_value("080500", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeUint8ListShortData2) { + decode_error_value("080500010203", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, EncodeInt32ListEmpty) { + g_autoptr(FlValue) value = fl_value_new_int32_list(nullptr, 0); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, "0900"); +} + +TEST(FlStandardMessageCodecTest, EncodeInt32List) { + int32_t data[] = {0, -1, 2, -3, 4}; + g_autoptr(FlValue) value = fl_value_new_int32_list(data, 5); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, "090500000000ffffffff02000000fdffffff04000000"); +} + +TEST(FlStandardMessageCodecTest, DecodeInt32ListEmpty) { + g_autoptr(FlValue) value = decode_message("0900"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_INT32_LIST); + EXPECT_EQ(fl_value_get_length(value), static_cast(0)); +} + +TEST(FlStandardMessageCodecTest, DecodeInt32List) { + g_autoptr(FlValue) value = + decode_message("090500000000ffffffff02000000fdffffff04000000"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_INT32_LIST); + const int32_t* data = fl_value_get_int32_list(value); + EXPECT_EQ(data[0], 0); + EXPECT_EQ(data[1], -1); + EXPECT_EQ(data[2], 2); + EXPECT_EQ(data[3], -3); + EXPECT_EQ(data[4], 4); +} + +TEST(FlStandardMessageCodecTest, DecodeInt32ListNoData) { + decode_error_value("09", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeInt32ListLengthNoData) { + decode_error_value("0905", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeInt32ListShortData1) { + decode_error_value("090500", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeInt32ListShortData2) { + decode_error_value("090500000000ffffffff02000000fdffffff040000", + FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, EncodeInt64ListEmpty) { + g_autoptr(FlValue) value = fl_value_new_int64_list(nullptr, 0); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, "0a00"); +} + +TEST(FlStandardMessageCodecTest, EncodeInt64List) { + int64_t data[] = {0, -1, 2, -3, 4}; + g_autoptr(FlValue) value = fl_value_new_int64_list(data, 5); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, + "0a050000000000000000ffffffffffffffff0200000000000000fdffffff" + "ffffffff0400000000000000"); +} + +TEST(FlStandardMessageCodecTest, DecodeInt64ListEmpty) { + g_autoptr(FlValue) value = decode_message("0a00"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_INT64_LIST); + EXPECT_EQ(fl_value_get_length(value), static_cast(0)); +} + +TEST(FlStandardMessageCodecTest, DecodeInt64List) { + g_autoptr(FlValue) value = decode_message( + "0a050000000000000000ffffffffffffffff0200000000000000fdffffff" + "ffffffff0400000000000000"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_INT64_LIST); + const int64_t* data = fl_value_get_int64_list(value); + EXPECT_EQ(data[0], 0); + EXPECT_EQ(data[1], -1); + EXPECT_EQ(data[2], 2); + EXPECT_EQ(data[3], -3); + EXPECT_EQ(data[4], 4); +} + +TEST(FlStandardMessageCodecTest, DecodeInt64ListNoData) { + decode_error_value("0a", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeInt64ListLengthNoData) { + decode_error_value("0a05", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeInt64ListShortData1) { + decode_error_value("0a0500", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeInt64ListShortData2) { + decode_error_value( + "0a050000000000000000ffffffffffffffff0200000000000000fdffffffffffffff0400" + "0000000000", + FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, EncodeFloatListEmpty) { + g_autoptr(FlValue) value = fl_value_new_float_list(nullptr, 0); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, "0b00"); +} + +TEST(FlStandardMessageCodecTest, EncodeFloatList) { + double data[] = {0, -0.5, 0.25, -0.125, 0.00625}; + g_autoptr(FlValue) value = fl_value_new_float_list(data, 5); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, + "0b050000000000000000000000000000e0bf000000000000d03f00000000" + "0000c0bf9a9999999999793f"); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatListEmpty) { + g_autoptr(FlValue) value = decode_message("0b00"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_FLOAT_LIST); + EXPECT_EQ(fl_value_get_length(value), static_cast(0)); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatList) { + g_autoptr(FlValue) value = decode_message( + "0b050000000000000000000000000000e0bf000000000000d03f00000000" + "0000c0bf9a9999999999793f"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_FLOAT_LIST); + const double* data = fl_value_get_float_list(value); + EXPECT_FLOAT_EQ(data[0], 0.0); + EXPECT_FLOAT_EQ(data[1], -0.5); + EXPECT_FLOAT_EQ(data[2], 0.25); + EXPECT_FLOAT_EQ(data[3], -0.125); + EXPECT_FLOAT_EQ(data[4], 0.00625); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatListNoData) { + decode_error_value("0b", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatListLengthNoData) { + decode_error_value("0b05", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatListShortData1) { + decode_error_value("0b0500", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeFloatListShortData2) { + decode_error_value( + "0b050000000000000000000000000000e0bf000000000000d03f000000000000c0bf9a99" + "9999999979", + FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, EncodeListEmpty) { + g_autoptr(FlValue) value = fl_value_new_list(); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, "0c00"); +} + +TEST(FlStandardMessageCodecTest, EncodeListTypes) { + g_autoptr(FlValue) value = fl_value_new_list(); + fl_value_append_take(value, fl_value_new_null()); + fl_value_append_take(value, fl_value_new_bool(TRUE)); + fl_value_append_take(value, fl_value_new_int(42)); + fl_value_append_take(value, fl_value_new_float(M_PI)); + fl_value_append_take(value, fl_value_new_string("hello")); + fl_value_append_take(value, fl_value_new_list()); + fl_value_append_take(value, fl_value_new_map()); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, + "0c070001032a00000006182d4454fb210940070568656c6c6f0c000d00"); +} + +TEST(FlStandardMessageCodecTest, EncodeListNested) { + g_autoptr(FlValue) even_numbers = fl_value_new_list(); + g_autoptr(FlValue) odd_numbers = fl_value_new_list(); + for (int i = 0; i < 10; i++) { + if (i % 2 == 0) + fl_value_append_take(even_numbers, fl_value_new_int(i)); + else + fl_value_append_take(odd_numbers, fl_value_new_int(i)); + } + g_autoptr(FlValue) value = fl_value_new_list(); + fl_value_append(value, even_numbers); + fl_value_append(value, odd_numbers); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, + "0c020c05030000000003020000000304000000030600000003080000000c" + "0503010000000303000000030500000003070000000309000000"); +} + +TEST(FlStandardMessageCodecTest, DecodeListEmpty) { + g_autoptr(FlValue) value = decode_message("0c00"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_LIST); + EXPECT_EQ(fl_value_get_length(value), static_cast(0)); +} + +TEST(FlStandardMessageCodecTest, DecodeListTypes) { + g_autoptr(FlValue) value = decode_message( + "0c070001032a00000006182d4454fb210940070568656c6c6f0c000d00"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_LIST); + ASSERT_EQ(fl_value_get_length(value), static_cast(7)); + ASSERT_EQ(fl_value_get_type(fl_value_get_list_value(value, 0)), + FL_VALUE_TYPE_NULL); + ASSERT_EQ(fl_value_get_type(fl_value_get_list_value(value, 1)), + FL_VALUE_TYPE_BOOL); + EXPECT_TRUE(fl_value_get_bool(fl_value_get_list_value(value, 1))); + ASSERT_EQ(fl_value_get_type(fl_value_get_list_value(value, 2)), + FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_int(fl_value_get_list_value(value, 2)), 42); + ASSERT_EQ(fl_value_get_type(fl_value_get_list_value(value, 3)), + FL_VALUE_TYPE_FLOAT); + EXPECT_FLOAT_EQ(fl_value_get_float(fl_value_get_list_value(value, 3)), M_PI); + ASSERT_EQ(fl_value_get_type(fl_value_get_list_value(value, 4)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_list_value(value, 4)), "hello"); + ASSERT_EQ(fl_value_get_type(fl_value_get_list_value(value, 5)), + FL_VALUE_TYPE_LIST); + ASSERT_EQ(fl_value_get_length(fl_value_get_list_value(value, 5)), + static_cast(0)); + ASSERT_EQ(fl_value_get_type(fl_value_get_list_value(value, 6)), + FL_VALUE_TYPE_MAP); + ASSERT_EQ(fl_value_get_length(fl_value_get_list_value(value, 6)), + static_cast(0)); +} + +TEST(FlStandardMessageCodecTest, DecodeListNested) { + g_autoptr(FlValue) value = decode_message( + "0c020c05030000000003020000000304000000030600000003080000000c" + "0503010000000303000000030500000003070000000309000000"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_LIST); + ASSERT_EQ(fl_value_get_length(value), static_cast(2)); + FlValue* even_list = fl_value_get_list_value(value, 0); + ASSERT_EQ(fl_value_get_type(even_list), FL_VALUE_TYPE_LIST); + ASSERT_EQ(fl_value_get_length(even_list), static_cast(5)); + FlValue* odd_list = fl_value_get_list_value(value, 1); + ASSERT_EQ(fl_value_get_type(odd_list), FL_VALUE_TYPE_LIST); + ASSERT_EQ(fl_value_get_length(odd_list), static_cast(5)); + for (int i = 0; i < 5; i++) { + FlValue* v = fl_value_get_list_value(even_list, i); + ASSERT_EQ(fl_value_get_type(v), FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_int(v), i * 2); + + v = fl_value_get_list_value(odd_list, i); + ASSERT_EQ(fl_value_get_type(v), FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_int(v), i * 2 + 1); + } +} + +TEST(FlStandardMessageCodecTest, DecodeListNoData) { + decode_error_value("0c", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeListLengthNoData) { + decode_error_value("0c07", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeListShortData1) { + decode_error_value("0c0700", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeListShortData2) { + decode_error_value("0c070001032a00000006000000000000f8bf070568656c6c6f0c000d", + FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, EncodeDecodeLargeList) { + g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); + + g_autoptr(FlValue) value = fl_value_new_list(); + for (int i = 0; i < 65535; i++) + fl_value_append_take(value, fl_value_new_int(i)); + + g_autoptr(GError) error = nullptr; + g_autoptr(GBytes) message = + fl_message_codec_encode_message(FL_MESSAGE_CODEC(codec), value, &error); + EXPECT_NE(message, nullptr); + EXPECT_EQ(error, nullptr); + + g_autoptr(FlValue) decoded_value = + fl_message_codec_decode_message(FL_MESSAGE_CODEC(codec), message, &error); + EXPECT_EQ(error, nullptr); + EXPECT_NE(value, nullptr); + + ASSERT_TRUE(fl_value_equal(value, decoded_value)); +} + +TEST(FlStandardMessageCodecTest, EncodeMapEmpty) { + g_autoptr(FlValue) value = fl_value_new_map(); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, "0d00"); +} + +TEST(FlStandardMessageCodecTest, EncodeMapKeyTypes) { + g_autoptr(FlValue) value = fl_value_new_map(); + fl_value_set_take(value, fl_value_new_null(), fl_value_new_string("null")); + fl_value_set_take(value, fl_value_new_bool(TRUE), + fl_value_new_string("bool")); + fl_value_set_take(value, fl_value_new_int(42), fl_value_new_string("int")); + fl_value_set_take(value, fl_value_new_float(M_PI), + fl_value_new_string("float")); + fl_value_set_take(value, fl_value_new_string("hello"), + fl_value_new_string("string")); + fl_value_set_take(value, fl_value_new_list(), fl_value_new_string("list")); + fl_value_set_take(value, fl_value_new_map(), fl_value_new_string("map")); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, + "0d070007046e756c6c010704626f6f6c032a0000000703696e7406182d4454f" + "b2109400705666c6f6174070568656c6c6f0706737472696e670c0007046c69" + "73740d0007036d6170"); +} + +TEST(FlStandardMessageCodecTest, EncodeMapValueTypes) { + g_autoptr(FlValue) value = fl_value_new_map(); + fl_value_set_take(value, fl_value_new_string("null"), fl_value_new_null()); + fl_value_set_take(value, fl_value_new_string("bool"), + fl_value_new_bool(TRUE)); + fl_value_set_take(value, fl_value_new_string("int"), fl_value_new_int(42)); + fl_value_set_take(value, fl_value_new_string("float"), + fl_value_new_float(M_PI)); + fl_value_set_take(value, fl_value_new_string("string"), + fl_value_new_string("hello")); + fl_value_set_take(value, fl_value_new_string("list"), fl_value_new_list()); + fl_value_set_take(value, fl_value_new_string("map"), fl_value_new_map()); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, + "0d0707046e756c6c000704626f6f6c010703696e74032a0000000705666c6f6" + "17406182d4454fb2109400706737472696e67070568656c6c6f07046c697374" + "0c0007036d61700d00"); +} + +TEST(FlStandardMessageCodecTest, EncodeMapNested) { + g_autoptr(FlValue) str_to_int = fl_value_new_map(); + g_autoptr(FlValue) int_to_str = fl_value_new_map(); + const char* numbers[] = {"zero", "one", "two", "three", nullptr}; + for (int i = 0; numbers[i] != nullptr; i++) { + fl_value_set_take(str_to_int, fl_value_new_string(numbers[i]), + fl_value_new_int(i)); + fl_value_set_take(int_to_str, fl_value_new_int(i), + fl_value_new_string(numbers[i])); + } + g_autoptr(FlValue) value = fl_value_new_map(); + fl_value_set_string(value, "str-to-int", str_to_int); + fl_value_set_string(value, "int-to-str", int_to_str); + g_autofree gchar* hex_string = encode_message(value); + EXPECT_STREQ(hex_string, + "0d02070a7374722d746f2d696e740d0407047a65726f030000000007036f" + "6e650301000000070374776f030200000007057468726565030300000007" + "0a696e742d746f2d7374720d04030000000007047a65726f030100000007" + "036f6e650302000000070374776f030300000007057468726565"); +} + +TEST(FlStandardMessageCodecTest, DecodeMapEmpty) { + g_autoptr(FlValue) value = decode_message("0d00"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP); + ASSERT_EQ(fl_value_get_length(value), static_cast(0)); +} + +TEST(FlStandardMessageCodecTest, DecodeMapKeyTypes) { + g_autoptr(FlValue) value = decode_message( + "0d070007046e756c6c010704626f6f6c032a0000000703696e7406182d4454fb21094007" + "05666c6f6174070568656c6c6f0706737472696e670c0007046c6973740d0007036d617" + "0"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP); + ASSERT_EQ(fl_value_get_length(value), static_cast(7)); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 0)), + FL_VALUE_TYPE_NULL); + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(value, 0)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_value(value, 0)), "null"); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 1)), + FL_VALUE_TYPE_BOOL); + EXPECT_TRUE(fl_value_get_bool(fl_value_get_map_key(value, 1))); + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(value, 1)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_value(value, 1)), "bool"); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 2)), + FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_int(fl_value_get_map_key(value, 2)), 42); + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(value, 2)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_value(value, 2)), "int"); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 3)), + FL_VALUE_TYPE_FLOAT); + EXPECT_FLOAT_EQ(fl_value_get_float(fl_value_get_map_key(value, 3)), M_PI); + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(value, 3)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_value(value, 3)), "float"); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 4)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_key(value, 4)), "hello"); + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(value, 4)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_value(value, 4)), "string"); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 5)), + FL_VALUE_TYPE_LIST); + ASSERT_EQ(fl_value_get_length(fl_value_get_map_key(value, 5)), + static_cast(0)); + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(value, 5)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_value(value, 5)), "list"); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 6)), + FL_VALUE_TYPE_MAP); + ASSERT_EQ(fl_value_get_length(fl_value_get_map_key(value, 6)), + static_cast(0)); + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(value, 6)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_value(value, 6)), "map"); +} + +TEST(FlStandardMessageCodecTest, DecodeMapValueTypes) { + g_autoptr(FlValue) value = decode_message( + "0d0707046e756c6c000704626f6f6c010703696e74032a0000000705666c6f617406182d" + "4454fb2109400706737472696e67070568656c6c6f07046c6973740c0007036d61700d0" + "0"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP); + ASSERT_EQ(fl_value_get_length(value), static_cast(7)); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 0)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_key(value, 0)), "null"); + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(value, 0)), + FL_VALUE_TYPE_NULL); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 1)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_key(value, 1)), "bool"); + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(value, 1)), + FL_VALUE_TYPE_BOOL); + EXPECT_TRUE(fl_value_get_bool(fl_value_get_map_value(value, 1))); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 2)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_key(value, 2)), "int"); + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(value, 2)), + FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_int(fl_value_get_map_value(value, 2)), 42); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 3)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_key(value, 3)), "float"); + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(value, 3)), + FL_VALUE_TYPE_FLOAT); + EXPECT_FLOAT_EQ(fl_value_get_float(fl_value_get_map_value(value, 3)), M_PI); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 4)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_key(value, 4)), "string"); + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(value, 4)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_value(value, 4)), "hello"); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 5)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_key(value, 5)), "list"); + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(value, 5)), + FL_VALUE_TYPE_LIST); + ASSERT_EQ(fl_value_get_length(fl_value_get_map_value(value, 5)), + static_cast(0)); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 6)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_key(value, 6)), "map"); + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(value, 6)), + FL_VALUE_TYPE_MAP); + ASSERT_EQ(fl_value_get_length(fl_value_get_map_value(value, 6)), + static_cast(0)); +} + +TEST(FlStandardMessageCodecTest, DecodeMapNested) { + g_autoptr(FlValue) value = decode_message( + "0d02070a7374722d746f2d696e740d0407047a65726f030000000007036f" + "6e650301000000070374776f030200000007057468726565030300000007" + "0a696e742d746f2d7374720d04030000000007047a65726f030100000007" + "036f6e650302000000070374776f030300000007057468726565"); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP); + ASSERT_EQ(fl_value_get_length(value), static_cast(2)); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 0)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_key(value, 0)), + "str-to-int"); + FlValue* str_to_int = fl_value_get_map_value(value, 0); + ASSERT_EQ(fl_value_get_type(str_to_int), FL_VALUE_TYPE_MAP); + ASSERT_EQ(fl_value_get_length(str_to_int), static_cast(4)); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(value, 1)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_key(value, 1)), + "int-to-str"); + FlValue* int_to_str = fl_value_get_map_value(value, 1); + ASSERT_EQ(fl_value_get_type(int_to_str), FL_VALUE_TYPE_MAP); + ASSERT_EQ(fl_value_get_length(int_to_str), static_cast(4)); + + const char* numbers[] = {"zero", "one", "two", "three", nullptr}; + for (int i = 0; numbers[i] != nullptr; i++) { + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(str_to_int, i)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_key(str_to_int, i)), + numbers[i]); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(str_to_int, i)), + FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_int(fl_value_get_map_value(str_to_int, i)), i); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_key(int_to_str, i)), + FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_int(fl_value_get_map_key(int_to_str, i)), i); + + ASSERT_EQ(fl_value_get_type(fl_value_get_map_value(int_to_str, i)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_value_get_map_value(int_to_str, i)), + numbers[i]); + } +} + +TEST(FlStandardMessageCodecTest, DecodeMapNoData) { + decode_error_value("0d", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeMapLengthNoData) { + decode_error_value("0d07", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeMapShortData1) { + decode_error_value("0d0707", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, DecodeMapShortData2) { + decode_error_value( + "0d0707046e756c6c000704626f6f6c010703696e74032a0000000705666c" + "6f617406000000000000f8bf0706737472696e67070568656c6c6f07046c" + "6973740c0007036d61700d", + FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA); +} + +TEST(FlStandardMessageCodecTest, EncodeDecodeLargeMap) { + g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); + + g_autoptr(FlValue) value = fl_value_new_map(); + for (int i = 0; i < 512; i++) { + g_autofree gchar* key = g_strdup_printf("key%d", i); + fl_value_set_string_take(value, key, fl_value_new_int(i)); + } + + g_autoptr(GError) error = nullptr; + g_autoptr(GBytes) message = + fl_message_codec_encode_message(FL_MESSAGE_CODEC(codec), value, &error); + EXPECT_NE(message, nullptr); + EXPECT_EQ(error, nullptr); + + g_autoptr(FlValue) decoded_value = + fl_message_codec_decode_message(FL_MESSAGE_CODEC(codec), message, &error); + EXPECT_EQ(error, nullptr); + EXPECT_NE(value, nullptr); + + ASSERT_TRUE(fl_value_equal(value, decoded_value)); +} + +TEST(FlStandardMessageCodecTest, DecodeUnknownType) { + decode_error_value("0e", FL_MESSAGE_CODEC_ERROR, + FL_MESSAGE_CODEC_ERROR_UNSUPPORTED_TYPE); +} + +TEST(FlStandardMessageCodecTest, EncodeDecode) { + g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); + + g_autoptr(FlValue) input = fl_value_new_list(); + fl_value_append_take(input, fl_value_new_null()); + fl_value_append_take(input, fl_value_new_bool(TRUE)); + fl_value_append_take(input, fl_value_new_int(42)); + fl_value_append_take(input, fl_value_new_float(M_PI)); + fl_value_append_take(input, fl_value_new_string("hello")); + fl_value_append_take(input, fl_value_new_list()); + fl_value_append_take(input, fl_value_new_map()); + + g_autoptr(GError) error = nullptr; + g_autoptr(GBytes) message = + fl_message_codec_encode_message(FL_MESSAGE_CODEC(codec), input, &error); + EXPECT_NE(message, nullptr); + EXPECT_EQ(error, nullptr); + + g_autoptr(FlValue) output = + fl_message_codec_decode_message(FL_MESSAGE_CODEC(codec), message, &error); + EXPECT_EQ(error, nullptr); + EXPECT_NE(output, nullptr); + + ASSERT_TRUE(fl_value_equal(input, output)); +} diff --git a/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h b/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h new file mode 100644 index 0000000000000..5ba56ce62b15d --- /dev/null +++ b/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h @@ -0,0 +1,47 @@ +// 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_LINUX_FL_STANDARD_MESSAGE_CODEC_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_STANDARD_MESSAGE_CODEC_H_ + +#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION) +#error "Only can be included directly." +#endif + +#include "fl_message_codec.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlStandardMessageCodec, + fl_standard_message_codec, + FL, + STANDARD_CODEC, + FlMessageCodec) + +/** + * FlStandardMessageCodec: + * + * #FlStandardMessageCodec is an #FlMessageCodec that implements the Flutter + * standard message encoding. This encodes and decodes #FlValue of type + * #FL_VALUE_TYPE_NULL, #FL_VALUE_TYPE_BOOL, #FL_VALUE_TYPE_INT, + * #FL_VALUE_TYPE_FLOAT, #FL_VALUE_TYPE_STRING, #FL_VALUE_TYPE_UINT8_LIST, + * #FL_VALUE_TYPE_INT32_LIST, #FL_VALUE_TYPE_INT64_LIST, + * #FL_VALUE_TYPE_FLOAT_LIST, #FL_VALUE_TYPE_LIST, and #FL_VALUE_TYPE_MAP + * + * #FlStandardMessageCodec matches the StandardCodec class in the Flutter + * services library. + */ + +/* + * fl_standard_message_codec_new: + * + * Creates a #FlStandardMessageCodec. + * + * Returns: a new #FlStandardMessageCodec + */ +FlStandardMessageCodec* fl_standard_message_codec_new(); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_STANDARD_MESSAGE_CODEC_H_ diff --git a/shell/platform/linux/public/flutter_linux/flutter_linux.h b/shell/platform/linux/public/flutter_linux/flutter_linux.h index c41200b58b3a8..62449e81abf6f 100644 --- a/shell/platform/linux/public/flutter_linux/flutter_linux.h +++ b/shell/platform/linux/public/flutter_linux/flutter_linux.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include