|
| 1 | +// Copyright 2013 The Flutter Authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style license that can be |
| 3 | +// found in the LICENSE file. |
| 4 | + |
| 5 | +#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h" |
| 6 | + |
| 7 | +#include "rapidjson/reader.h" |
| 8 | +#include "rapidjson/writer.h" |
| 9 | + |
| 10 | +#include <gmodule.h> |
| 11 | + |
| 12 | +G_DEFINE_QUARK(fl_json_message_codec_error_quark, fl_json_message_codec_error) |
| 13 | + |
| 14 | +struct _FlJsonMessageCodec { |
| 15 | + FlMessageCodec parent_instance; |
| 16 | +}; |
| 17 | + |
| 18 | +G_DEFINE_TYPE(FlJsonMessageCodec, |
| 19 | + fl_json_message_codec, |
| 20 | + fl_message_codec_get_type()) |
| 21 | + |
| 22 | +// Recursively writes #FlValue objects using rapidjson |
| 23 | +static gboolean write_value(rapidjson::Writer<rapidjson::StringBuffer>& writer, |
| 24 | + FlValue* value, |
| 25 | + GError** error) { |
| 26 | + if (value == nullptr) { |
| 27 | + writer.Null(); |
| 28 | + return TRUE; |
| 29 | + } |
| 30 | + |
| 31 | + switch (fl_value_get_type(value)) { |
| 32 | + case FL_VALUE_TYPE_NULL: |
| 33 | + writer.Null(); |
| 34 | + break; |
| 35 | + case FL_VALUE_TYPE_BOOL: |
| 36 | + writer.Bool(fl_value_get_bool(value)); |
| 37 | + break; |
| 38 | + case FL_VALUE_TYPE_INT: |
| 39 | + writer.Int64(fl_value_get_int(value)); |
| 40 | + break; |
| 41 | + case FL_VALUE_TYPE_FLOAT: |
| 42 | + writer.Double(fl_value_get_float(value)); |
| 43 | + break; |
| 44 | + case FL_VALUE_TYPE_STRING: |
| 45 | + writer.String(fl_value_get_string(value)); |
| 46 | + break; |
| 47 | + case FL_VALUE_TYPE_UINT8_LIST: { |
| 48 | + writer.StartArray(); |
| 49 | + const uint8_t* data = fl_value_get_uint8_list(value); |
| 50 | + for (size_t i = 0; i < fl_value_get_length(value); i++) |
| 51 | + writer.Int(data[i]); |
| 52 | + writer.EndArray(); |
| 53 | + break; |
| 54 | + } |
| 55 | + case FL_VALUE_TYPE_INT32_LIST: { |
| 56 | + writer.StartArray(); |
| 57 | + const int32_t* data = fl_value_get_int32_list(value); |
| 58 | + for (size_t i = 0; i < fl_value_get_length(value); i++) |
| 59 | + writer.Int(data[i]); |
| 60 | + writer.EndArray(); |
| 61 | + break; |
| 62 | + } |
| 63 | + case FL_VALUE_TYPE_INT64_LIST: { |
| 64 | + writer.StartArray(); |
| 65 | + const int64_t* data = fl_value_get_int64_list(value); |
| 66 | + for (size_t i = 0; i < fl_value_get_length(value); i++) |
| 67 | + writer.Int64(data[i]); |
| 68 | + writer.EndArray(); |
| 69 | + break; |
| 70 | + } |
| 71 | + case FL_VALUE_TYPE_FLOAT_LIST: { |
| 72 | + writer.StartArray(); |
| 73 | + const double* data = fl_value_get_float_list(value); |
| 74 | + for (size_t i = 0; i < fl_value_get_length(value); i++) |
| 75 | + writer.Double(data[i]); |
| 76 | + writer.EndArray(); |
| 77 | + break; |
| 78 | + } |
| 79 | + case FL_VALUE_TYPE_LIST: { |
| 80 | + writer.StartArray(); |
| 81 | + for (size_t i = 0; i < fl_value_get_length(value); i++) |
| 82 | + if (!write_value(writer, fl_value_get_list_value(value, i), error)) |
| 83 | + return FALSE; |
| 84 | + writer.EndArray(); |
| 85 | + break; |
| 86 | + } |
| 87 | + case FL_VALUE_TYPE_MAP: { |
| 88 | + writer.StartObject(); |
| 89 | + for (size_t i = 0; i < fl_value_get_length(value); i++) { |
| 90 | + FlValue* key = fl_value_get_map_key(value, i); |
| 91 | + if (fl_value_get_type(key) != FL_VALUE_TYPE_STRING) { |
| 92 | + g_set_error(error, FL_JSON_MESSAGE_CODEC_ERROR, |
| 93 | + FL_JSON_MESSAGE_CODEC_ERROR_INVALID_OBJECT_KEY_TYPE, |
| 94 | + "Invalid object key type"); |
| 95 | + return FALSE; |
| 96 | + } |
| 97 | + writer.Key(fl_value_get_string(key)); |
| 98 | + if (!write_value(writer, fl_value_get_map_value(value, i), error)) |
| 99 | + return FALSE; |
| 100 | + } |
| 101 | + writer.EndObject(); |
| 102 | + break; |
| 103 | + } |
| 104 | + default: |
| 105 | + g_set_error(error, FL_MESSAGE_CODEC_ERROR, |
| 106 | + FL_MESSAGE_CODEC_ERROR_UNSUPPORTED_TYPE, |
| 107 | + "Unexpected FlValue type %d", fl_value_get_type(value)); |
| 108 | + return FALSE; |
| 109 | + } |
| 110 | + |
| 111 | + return TRUE; |
| 112 | +} |
| 113 | + |
| 114 | +// Handler to parse JSON using rapidjson in SAX mode |
| 115 | +struct FlValueHandler { |
| 116 | + GPtrArray* stack; |
| 117 | + FlValue* key; |
| 118 | + GError* error; |
| 119 | + |
| 120 | + FlValueHandler() { |
| 121 | + stack = g_ptr_array_new_with_free_func( |
| 122 | + reinterpret_cast<GDestroyNotify>(fl_value_unref)); |
| 123 | + key = nullptr; |
| 124 | + error = nullptr; |
| 125 | + } |
| 126 | + |
| 127 | + ~FlValueHandler() { |
| 128 | + g_ptr_array_unref(stack); |
| 129 | + if (key != nullptr) |
| 130 | + fl_value_unref(key); |
| 131 | + if (error != nullptr) |
| 132 | + g_error_free(error); |
| 133 | + } |
| 134 | + |
| 135 | + // Gets the current head of the stack |
| 136 | + FlValue* get_head() { |
| 137 | + if (stack->len == 0) |
| 138 | + return nullptr; |
| 139 | + return static_cast<FlValue*>(g_ptr_array_index(stack, stack->len - 1)); |
| 140 | + } |
| 141 | + |
| 142 | + // Pushes a value onto the stack |
| 143 | + void push(FlValue* value) { g_ptr_array_add(stack, fl_value_ref(value)); } |
| 144 | + |
| 145 | + // Pops the stack |
| 146 | + void pop() { g_ptr_array_remove_index(stack, stack->len - 1); } |
| 147 | + |
| 148 | + // Adds a new value to the stack |
| 149 | + bool add(FlValue* value) { |
| 150 | + g_autoptr(FlValue) owned_value = value; |
| 151 | + FlValue* head = get_head(); |
| 152 | + if (head == nullptr) |
| 153 | + push(owned_value); |
| 154 | + else if (fl_value_get_type(head) == FL_VALUE_TYPE_LIST) |
| 155 | + fl_value_append(head, owned_value); |
| 156 | + else if (fl_value_get_type(head) == FL_VALUE_TYPE_MAP) { |
| 157 | + fl_value_set_take(head, key, fl_value_ref(owned_value)); |
| 158 | + key = nullptr; |
| 159 | + } else { |
| 160 | + g_set_error(&error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, |
| 161 | + "Can't add value to non container"); |
| 162 | + return false; |
| 163 | + } |
| 164 | + |
| 165 | + if (fl_value_get_type(owned_value) == FL_VALUE_TYPE_LIST || |
| 166 | + fl_value_get_type(owned_value) == FL_VALUE_TYPE_MAP) |
| 167 | + push(value); |
| 168 | + |
| 169 | + return true; |
| 170 | + } |
| 171 | + |
| 172 | + // The following implements the rapidjson SAX API |
| 173 | + |
| 174 | + bool Null() { return add(fl_value_new_null()); } |
| 175 | + |
| 176 | + bool Bool(bool b) { return add(fl_value_new_bool(b)); } |
| 177 | + |
| 178 | + bool Int(int i) { return add(fl_value_new_int(i)); } |
| 179 | + |
| 180 | + bool Uint(unsigned i) { return add(fl_value_new_int(i)); } |
| 181 | + |
| 182 | + bool Int64(int64_t i) { return add(fl_value_new_int(i)); } |
| 183 | + |
| 184 | + bool Uint64(uint64_t i) { |
| 185 | + // For some reason (bug in rapidjson?) this is not returned in Int64 |
| 186 | + if (i == G_MAXINT64) |
| 187 | + return add(fl_value_new_int(i)); |
| 188 | + else |
| 189 | + return add(fl_value_new_float(i)); |
| 190 | + } |
| 191 | + |
| 192 | + bool Double(double d) { return add(fl_value_new_float(d)); } |
| 193 | + |
| 194 | + bool RawNumber(const char* str, rapidjson::SizeType length, bool copy) { |
| 195 | + g_set_error(&error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, |
| 196 | + "RawNumber not supported"); |
| 197 | + return false; |
| 198 | + } |
| 199 | + |
| 200 | + bool String(const char* str, rapidjson::SizeType length, bool copy) { |
| 201 | + FlValue* v = fl_value_new_string_sized(str, length); |
| 202 | + return add(v); |
| 203 | + } |
| 204 | + |
| 205 | + bool StartObject() { return add(fl_value_new_map()); } |
| 206 | + |
| 207 | + bool Key(const char* str, rapidjson::SizeType length, bool copy) { |
| 208 | + if (key != nullptr) |
| 209 | + fl_value_unref(key); |
| 210 | + key = fl_value_new_string_sized(str, length); |
| 211 | + return true; |
| 212 | + } |
| 213 | + |
| 214 | + bool EndObject(rapidjson::SizeType memberCount) { |
| 215 | + pop(); |
| 216 | + return true; |
| 217 | + } |
| 218 | + |
| 219 | + bool StartArray() { return add(fl_value_new_list()); } |
| 220 | + |
| 221 | + bool EndArray(rapidjson::SizeType elementCount) { |
| 222 | + pop(); |
| 223 | + return true; |
| 224 | + } |
| 225 | +}; |
| 226 | + |
| 227 | +// Implements FlMessageCodec:encode_message |
| 228 | +static GBytes* fl_json_message_codec_encode_message(FlMessageCodec* codec, |
| 229 | + FlValue* message, |
| 230 | + GError** error) { |
| 231 | + rapidjson::StringBuffer buffer; |
| 232 | + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); |
| 233 | + |
| 234 | + if (!write_value(writer, message, error)) |
| 235 | + return nullptr; |
| 236 | + |
| 237 | + const gchar* text = buffer.GetString(); |
| 238 | + return g_bytes_new(text, strlen(text)); |
| 239 | +} |
| 240 | + |
| 241 | +// Implements FlMessageCodec:decode_message |
| 242 | +static FlValue* fl_json_message_codec_decode_message(FlMessageCodec* codec, |
| 243 | + GBytes* message, |
| 244 | + GError** error) { |
| 245 | + gsize data_length; |
| 246 | + const gchar* data = |
| 247 | + static_cast<const char*>(g_bytes_get_data(message, &data_length)); |
| 248 | + if (!g_utf8_validate(data, data_length, nullptr)) { |
| 249 | + g_set_error(error, FL_JSON_MESSAGE_CODEC_ERROR, |
| 250 | + FL_JSON_MESSAGE_CODEC_ERROR_INVALID_UTF8, |
| 251 | + "Message is not valid UTF8"); |
| 252 | + return nullptr; |
| 253 | + } |
| 254 | + |
| 255 | + FlValueHandler handler; |
| 256 | + rapidjson::Reader reader; |
| 257 | + rapidjson::MemoryStream ss(data, data_length); |
| 258 | + if (!reader.Parse(ss, handler)) { |
| 259 | + if (handler.error != nullptr) { |
| 260 | + g_propagate_error(error, handler.error); |
| 261 | + handler.error = nullptr; |
| 262 | + } else |
| 263 | + g_set_error(error, FL_JSON_MESSAGE_CODEC_ERROR, |
| 264 | + FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON, |
| 265 | + "Message is not valid JSON"); |
| 266 | + return nullptr; |
| 267 | + } |
| 268 | + |
| 269 | + FlValue* value = handler.get_head(); |
| 270 | + if (value == nullptr) { |
| 271 | + g_set_error(error, FL_JSON_MESSAGE_CODEC_ERROR, |
| 272 | + FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON, |
| 273 | + "Message is not valid JSON"); |
| 274 | + return nullptr; |
| 275 | + } |
| 276 | + |
| 277 | + return fl_value_ref(value); |
| 278 | +} |
| 279 | + |
| 280 | +static void fl_json_message_codec_class_init(FlJsonMessageCodecClass* klass) { |
| 281 | + FL_MESSAGE_CODEC_CLASS(klass)->encode_message = |
| 282 | + fl_json_message_codec_encode_message; |
| 283 | + FL_MESSAGE_CODEC_CLASS(klass)->decode_message = |
| 284 | + fl_json_message_codec_decode_message; |
| 285 | +} |
| 286 | + |
| 287 | +static void fl_json_message_codec_init(FlJsonMessageCodec* self) {} |
| 288 | + |
| 289 | +G_MODULE_EXPORT FlJsonMessageCodec* fl_json_message_codec_new() { |
| 290 | + return static_cast<FlJsonMessageCodec*>( |
| 291 | + g_object_new(fl_json_message_codec_get_type(), nullptr)); |
| 292 | +} |
| 293 | + |
| 294 | +G_MODULE_EXPORT gchar* fl_json_message_codec_encode(FlJsonMessageCodec* codec, |
| 295 | + FlValue* value, |
| 296 | + GError** error) { |
| 297 | + g_return_val_if_fail(FL_IS_JSON_CODEC(codec), nullptr); |
| 298 | + |
| 299 | + rapidjson::StringBuffer buffer; |
| 300 | + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); |
| 301 | + |
| 302 | + if (!write_value(writer, value, error)) |
| 303 | + return nullptr; |
| 304 | + |
| 305 | + return g_strdup(buffer.GetString()); |
| 306 | +} |
| 307 | + |
| 308 | +G_MODULE_EXPORT FlValue* fl_json_message_codec_decode(FlJsonMessageCodec* codec, |
| 309 | + const gchar* text, |
| 310 | + GError** error) { |
| 311 | + g_return_val_if_fail(FL_IS_JSON_CODEC(codec), nullptr); |
| 312 | + |
| 313 | + g_autoptr(GBytes) data = g_bytes_new_static(text, strlen(text)); |
| 314 | + g_autoptr(FlValue) value = fl_json_message_codec_decode_message( |
| 315 | + FL_MESSAGE_CODEC(codec), data, error); |
| 316 | + if (value == nullptr) |
| 317 | + return nullptr; |
| 318 | + |
| 319 | + return fl_value_ref(value); |
| 320 | +} |
0 commit comments