// 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); }