| // 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 <gmodule.h> |
| |
| #include <cstring> |
| |
| // 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; |
| static constexpr int kValueFloat32List = 14; |
| |
| 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<uint8_t*>(&value), |
| sizeof(uint16_t)); |
| } |
| |
| static void write_uint32(GByteArray* buffer, uint32_t value) { |
| g_byte_array_append(buffer, reinterpret_cast<uint8_t*>(&value), |
| sizeof(uint32_t)); |
| } |
| |
| static void write_int32(GByteArray* buffer, int32_t value) { |
| g_byte_array_append(buffer, reinterpret_cast<uint8_t*>(&value), |
| sizeof(int32_t)); |
| } |
| |
| static void write_int64(GByteArray* buffer, int64_t value) { |
| g_byte_array_append(buffer, reinterpret_cast<uint8_t*>(&value), |
| sizeof(int64_t)); |
| } |
| |
| static void write_float64(GByteArray* buffer, double value) { |
| g_byte_array_append(buffer, reinterpret_cast<uint8_t*>(&value), |
| sizeof(double)); |
| } |
| |
| // Write padding bytes to align to @align multiple of bytes. |
| static void write_align(GByteArray* buffer, guint align) { |
| while (buffer->len % align != 0) { |
| write_uint8(buffer, 0); |
| } |
| } |
| |
| // 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; |
| } |
| |
| // Skip bytes to align next read on @align multiple of bytes. |
| static gboolean read_align(GBytes* buffer, |
| size_t* offset, |
| size_t align, |
| GError** error) { |
| if ((*offset) % align == 0) { |
| return TRUE; |
| } |
| |
| size_t required = align - (*offset) % align; |
| if (!check_size(buffer, *offset, required, error)) { |
| return FALSE; |
| } |
| |
| (*offset) += required; |
| 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<const uint8_t*>(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<const uint16_t*>(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<const uint32_t*>(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<const int32_t*>(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<const int64_t*>(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 (!read_align(buffer, offset, 8, error)) { |
| return nullptr; |
| } |
| if (!check_size(buffer, *offset, sizeof(double), error)) { |
| return nullptr; |
| } |
| |
| FlValue* value = fl_value_new_float( |
| reinterpret_cast<const double*>(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<const gchar*>(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 (!read_align(buffer, offset, 4, error)) { |
| return nullptr; |
| } |
| if (!check_size(buffer, *offset, sizeof(int32_t) * length, error)) { |
| return nullptr; |
| } |
| FlValue* value = fl_value_new_int32_list( |
| reinterpret_cast<const int32_t*>(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 (!read_align(buffer, offset, 8, error)) { |
| return nullptr; |
| } |
| if (!check_size(buffer, *offset, sizeof(int64_t) * length, error)) { |
| return nullptr; |
| } |
| FlValue* value = fl_value_new_int64_list( |
| reinterpret_cast<const int64_t*>(get_data(buffer, offset)), length); |
| *offset += sizeof(int64_t) * length; |
| return value; |
| } |
| |
| // Reads a 32 bit floating point number list from @buffer in standard codec |
| // format. Returns a new #FlValue of type #FL_VALUE_TYPE_FLOAT32_LIST if |
| // successful or %NULL on error. |
| static FlValue* read_float32_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 (!read_align(buffer, offset, 4, error)) { |
| return nullptr; |
| } |
| if (!check_size(buffer, *offset, sizeof(float) * length, error)) { |
| return nullptr; |
| } |
| FlValue* value = fl_value_new_float32_list( |
| reinterpret_cast<const float*>(get_data(buffer, offset)), length); |
| *offset += sizeof(float) * 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 (!read_align(buffer, offset, 8, error)) { |
| return nullptr; |
| } |
| if (!check_size(buffer, *offset, sizeof(double) * length, error)) { |
| return nullptr; |
| } |
| FlValue* value = fl_value_new_float_list( |
| reinterpret_cast<const double*>(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<FlStandardMessageCodec*>(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<GByteArray*>(g_steal_pointer(&buffer))); |
| } |
| |
| // Implements FlMessageCodec::decode_message. |
| static FlValue* fl_standard_message_codec_decode_message(FlMessageCodec* codec, |
| GBytes* message, |
| GError** error) { |
| if (g_bytes_get_size(message) == 0) { |
| return fl_value_new_null(); |
| } |
| |
| FlStandardMessageCodec* self = |
| reinterpret_cast<FlStandardMessageCodec*>(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<FlStandardMessageCodec*>( |
| 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_align(buffer, 8); |
| 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<const uint8_t*>(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); |
| write_align(buffer, 4); |
| g_byte_array_append( |
| buffer, |
| reinterpret_cast<const uint8_t*>(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); |
| write_align(buffer, 8); |
| g_byte_array_append( |
| buffer, |
| reinterpret_cast<const uint8_t*>(fl_value_get_int64_list(value)), |
| sizeof(int64_t) * length); |
| return TRUE; |
| } |
| case FL_VALUE_TYPE_FLOAT32_LIST: { |
| write_uint8(buffer, kValueFloat32List); |
| size_t length = fl_value_get_length(value); |
| fl_standard_message_codec_write_size(self, buffer, length); |
| write_align(buffer, 4); |
| g_byte_array_append( |
| buffer, |
| reinterpret_cast<const uint8_t*>(fl_value_get_float32_list(value)), |
| sizeof(float) * 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); |
| write_align(buffer, 8); |
| g_byte_array_append( |
| buffer, |
| reinterpret_cast<const uint8_t*>(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 == kValueFloat32List) { |
| value = read_float32_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); |
| } |