blob: 5b0a4bf8bf66abd2887f13987ba29d99a0d9d5bb [file] [log] [blame]
// 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_value.h"
#include <gmodule.h>
struct _FlValue {
FlValueType type;
int ref_count;
};
typedef struct {
FlValue parent;
bool value;
} FlValueBool;
typedef struct {
FlValue parent;
int64_t value;
} FlValueInt;
typedef struct {
FlValue parent;
double value;
} FlValueDouble;
typedef struct {
FlValue parent;
gchar* value;
} FlValueString;
typedef struct {
FlValue parent;
uint8_t* values;
size_t values_length;
} FlValueUint8List;
typedef struct {
FlValue parent;
int32_t* values;
size_t values_length;
} FlValueInt32List;
typedef struct {
FlValue parent;
int64_t* values;
size_t values_length;
} FlValueInt64List;
typedef struct {
FlValue parent;
double* values;
size_t values_length;
} FlValueFloatList;
typedef struct {
FlValue parent;
GPtrArray* values;
} FlValueList;
typedef struct {
FlValue parent;
GPtrArray* keys;
GPtrArray* values;
} FlValueMap;
static FlValue* fl_value_new(FlValueType type, size_t size) {
FlValue* self = static_cast<FlValue*>(g_malloc0(size));
self->type = type;
self->ref_count = 1;
return self;
}
// Helper function to match GDestroyNotify type.
static void fl_value_destroy(gpointer value) {
fl_value_unref(static_cast<FlValue*>(value));
}
// Finds the index of a key in a FlValueMap.
// FIXME(robert-ancell) This is highly inefficient, and should be optimised if
// necessary.
static ssize_t fl_value_lookup_index(FlValue* self, FlValue* key) {
g_return_val_if_fail(self->type == FL_VALUE_TYPE_MAP, -1);
for (size_t i = 0; i < fl_value_get_length(self); i++) {
FlValue* k = fl_value_get_map_key(self, i);
if (fl_value_equal(k, key))
return i;
}
return -1;
}
// Converts an integer to a string and adds it to the buffer.
static void int_to_string(int64_t value, GString* buffer) {
g_string_append_printf(buffer, "%" G_GINT64_FORMAT, value);
}
// Converts a floating point number to a string and adds it to the buffer.
static void float_to_string(double value, GString* buffer) {
g_string_append_printf(buffer, "%.16f", value);
// Strip trailing zeros.
int zero_count = 0;
for (int i = buffer->len - 1; i >= 0; i--) {
// Leave one zero after a decimal point.
if (buffer->str[i] == '.') {
zero_count = zero_count == 0 ? 0 : zero_count - 1;
break;
}
if (buffer->str[i] != '0')
break;
zero_count++;
}
g_string_truncate(buffer, buffer->len - zero_count);
}
static void value_to_string(FlValue* value, GString* buffer) {
switch (value->type) {
case FL_VALUE_TYPE_NULL:
g_string_append(buffer, "null");
return;
case FL_VALUE_TYPE_BOOL:
if (fl_value_get_bool(value))
g_string_append(buffer, "true");
else
g_string_append(buffer, "false");
return;
case FL_VALUE_TYPE_INT:
int_to_string(fl_value_get_int(value), buffer);
return;
case FL_VALUE_TYPE_FLOAT:
float_to_string(fl_value_get_float(value), buffer);
return;
case FL_VALUE_TYPE_STRING: {
g_string_append(buffer, fl_value_get_string(value));
return;
}
case FL_VALUE_TYPE_UINT8_LIST: {
g_string_append(buffer, "[");
const uint8_t* values = fl_value_get_uint8_list(value);
for (size_t i = 0; i < fl_value_get_length(value); i++) {
if (i != 0)
g_string_append(buffer, ", ");
int_to_string(values[i], buffer);
}
g_string_append(buffer, "]");
return;
}
case FL_VALUE_TYPE_INT32_LIST: {
g_string_append(buffer, "[");
const int32_t* values = fl_value_get_int32_list(value);
for (size_t i = 0; i < fl_value_get_length(value); i++) {
if (i != 0)
g_string_append(buffer, ", ");
int_to_string(values[i], buffer);
}
g_string_append(buffer, "]");
return;
}
case FL_VALUE_TYPE_INT64_LIST: {
g_string_append(buffer, "[");
const int64_t* values = fl_value_get_int64_list(value);
for (size_t i = 0; i < fl_value_get_length(value); i++) {
if (i != 0)
g_string_append(buffer, ", ");
int_to_string(values[i], buffer);
}
g_string_append(buffer, "]");
return;
}
case FL_VALUE_TYPE_FLOAT_LIST: {
g_string_append(buffer, "[");
const double* values = fl_value_get_float_list(value);
for (size_t i = 0; i < fl_value_get_length(value); i++) {
if (i != 0)
g_string_append(buffer, ", ");
float_to_string(values[i], buffer);
}
g_string_append(buffer, "]");
return;
}
case FL_VALUE_TYPE_LIST: {
g_string_append(buffer, "[");
for (size_t i = 0; i < fl_value_get_length(value); i++) {
if (i != 0)
g_string_append(buffer, ", ");
value_to_string(fl_value_get_list_value(value, i), buffer);
}
g_string_append(buffer, "]");
return;
}
case FL_VALUE_TYPE_MAP: {
g_string_append(buffer, "{");
for (size_t i = 0; i < fl_value_get_length(value); i++) {
if (i != 0)
g_string_append(buffer, ", ");
value_to_string(fl_value_get_map_key(value, i), buffer);
g_string_append(buffer, ": ");
value_to_string(fl_value_get_map_value(value, i), buffer);
}
g_string_append(buffer, "}");
return;
}
default:
g_string_append_printf(buffer, "<unknown type %d>", value->type);
}
}
G_MODULE_EXPORT FlValue* fl_value_new_null() {
return fl_value_new(FL_VALUE_TYPE_NULL, sizeof(FlValue));
}
G_MODULE_EXPORT FlValue* fl_value_new_bool(bool value) {
FlValueBool* self = reinterpret_cast<FlValueBool*>(
fl_value_new(FL_VALUE_TYPE_BOOL, sizeof(FlValueBool)));
self->value = value ? true : false;
return reinterpret_cast<FlValue*>(self);
}
G_MODULE_EXPORT FlValue* fl_value_new_int(int64_t value) {
FlValueInt* self = reinterpret_cast<FlValueInt*>(
fl_value_new(FL_VALUE_TYPE_INT, sizeof(FlValueInt)));
self->value = value;
return reinterpret_cast<FlValue*>(self);
}
G_MODULE_EXPORT FlValue* fl_value_new_float(double value) {
FlValueDouble* self = reinterpret_cast<FlValueDouble*>(
fl_value_new(FL_VALUE_TYPE_FLOAT, sizeof(FlValueDouble)));
self->value = value;
return reinterpret_cast<FlValue*>(self);
}
G_MODULE_EXPORT FlValue* fl_value_new_string(const gchar* value) {
FlValueString* self = reinterpret_cast<FlValueString*>(
fl_value_new(FL_VALUE_TYPE_STRING, sizeof(FlValueString)));
self->value = g_strdup(value);
return reinterpret_cast<FlValue*>(self);
}
G_MODULE_EXPORT FlValue* fl_value_new_string_sized(const gchar* value,
size_t value_length) {
FlValueString* self = reinterpret_cast<FlValueString*>(
fl_value_new(FL_VALUE_TYPE_STRING, sizeof(FlValueString)));
self->value =
value_length == 0 ? g_strdup("") : g_strndup(value, value_length);
return reinterpret_cast<FlValue*>(self);
}
G_MODULE_EXPORT FlValue* fl_value_new_uint8_list(const uint8_t* data,
size_t data_length) {
FlValueUint8List* self = reinterpret_cast<FlValueUint8List*>(
fl_value_new(FL_VALUE_TYPE_UINT8_LIST, sizeof(FlValueUint8List)));
self->values_length = data_length;
self->values = static_cast<uint8_t*>(g_malloc(sizeof(uint8_t) * data_length));
memcpy(self->values, data, sizeof(uint8_t) * data_length);
return reinterpret_cast<FlValue*>(self);
}
G_MODULE_EXPORT FlValue* fl_value_new_uint8_list_from_bytes(GBytes* data) {
gsize length;
const uint8_t* d =
static_cast<const uint8_t*>(g_bytes_get_data(data, &length));
return fl_value_new_uint8_list(d, length);
}
G_MODULE_EXPORT FlValue* fl_value_new_int32_list(const int32_t* data,
size_t data_length) {
FlValueInt32List* self = reinterpret_cast<FlValueInt32List*>(
fl_value_new(FL_VALUE_TYPE_INT32_LIST, sizeof(FlValueInt32List)));
self->values_length = data_length;
self->values = static_cast<int32_t*>(g_malloc(sizeof(int32_t) * data_length));
memcpy(self->values, data, sizeof(int32_t) * data_length);
return reinterpret_cast<FlValue*>(self);
}
G_MODULE_EXPORT FlValue* fl_value_new_int64_list(const int64_t* data,
size_t data_length) {
FlValueInt64List* self = reinterpret_cast<FlValueInt64List*>(
fl_value_new(FL_VALUE_TYPE_INT64_LIST, sizeof(FlValueInt64List)));
self->values_length = data_length;
self->values = static_cast<int64_t*>(g_malloc(sizeof(int64_t) * data_length));
memcpy(self->values, data, sizeof(int64_t) * data_length);
return reinterpret_cast<FlValue*>(self);
}
G_MODULE_EXPORT FlValue* fl_value_new_float_list(const double* data,
size_t data_length) {
FlValueFloatList* self = reinterpret_cast<FlValueFloatList*>(
fl_value_new(FL_VALUE_TYPE_FLOAT_LIST, sizeof(FlValueFloatList)));
self->values_length = data_length;
self->values = static_cast<double*>(g_malloc(sizeof(double) * data_length));
memcpy(self->values, data, sizeof(double) * data_length);
return reinterpret_cast<FlValue*>(self);
}
G_MODULE_EXPORT FlValue* fl_value_new_list() {
FlValueList* self = reinterpret_cast<FlValueList*>(
fl_value_new(FL_VALUE_TYPE_LIST, sizeof(FlValueList)));
self->values = g_ptr_array_new_with_free_func(fl_value_destroy);
return reinterpret_cast<FlValue*>(self);
}
G_MODULE_EXPORT FlValue* fl_value_new_list_from_strv(
const gchar* const* str_array) {
g_return_val_if_fail(str_array != nullptr, nullptr);
g_autoptr(FlValue) value = fl_value_new_list();
for (int i = 0; str_array[i] != nullptr; i++)
fl_value_append_take(value, fl_value_new_string(str_array[i]));
return fl_value_ref(value);
}
G_MODULE_EXPORT FlValue* fl_value_new_map() {
FlValueMap* self = reinterpret_cast<FlValueMap*>(
fl_value_new(FL_VALUE_TYPE_MAP, sizeof(FlValueMap)));
self->keys = g_ptr_array_new_with_free_func(fl_value_destroy);
self->values = g_ptr_array_new_with_free_func(fl_value_destroy);
return reinterpret_cast<FlValue*>(self);
}
G_MODULE_EXPORT FlValue* fl_value_ref(FlValue* self) {
g_return_val_if_fail(self != nullptr, nullptr);
self->ref_count++;
return self;
}
G_MODULE_EXPORT void fl_value_unref(FlValue* self) {
g_return_if_fail(self != nullptr);
g_return_if_fail(self->ref_count > 0);
self->ref_count--;
if (self->ref_count != 0)
return;
switch (self->type) {
case FL_VALUE_TYPE_STRING: {
FlValueString* v = reinterpret_cast<FlValueString*>(self);
g_free(v->value);
break;
}
case FL_VALUE_TYPE_UINT8_LIST: {
FlValueUint8List* v = reinterpret_cast<FlValueUint8List*>(self);
g_free(v->values);
break;
}
case FL_VALUE_TYPE_INT32_LIST: {
FlValueInt32List* v = reinterpret_cast<FlValueInt32List*>(self);
g_free(v->values);
break;
}
case FL_VALUE_TYPE_INT64_LIST: {
FlValueInt64List* v = reinterpret_cast<FlValueInt64List*>(self);
g_free(v->values);
break;
}
case FL_VALUE_TYPE_FLOAT_LIST: {
FlValueFloatList* v = reinterpret_cast<FlValueFloatList*>(self);
g_free(v->values);
break;
}
case FL_VALUE_TYPE_LIST: {
FlValueList* v = reinterpret_cast<FlValueList*>(self);
g_ptr_array_unref(v->values);
break;
}
case FL_VALUE_TYPE_MAP: {
FlValueMap* v = reinterpret_cast<FlValueMap*>(self);
g_ptr_array_unref(v->keys);
g_ptr_array_unref(v->values);
break;
}
case FL_VALUE_TYPE_NULL:
case FL_VALUE_TYPE_BOOL:
case FL_VALUE_TYPE_INT:
case FL_VALUE_TYPE_FLOAT:
break;
}
g_free(self);
}
G_MODULE_EXPORT FlValueType fl_value_get_type(FlValue* self) {
g_return_val_if_fail(self != nullptr, FL_VALUE_TYPE_NULL);
return self->type;
}
G_MODULE_EXPORT bool fl_value_equal(FlValue* a, FlValue* b) {
g_return_val_if_fail(a != nullptr, false);
g_return_val_if_fail(b != nullptr, false);
if (a->type != b->type)
return false;
switch (a->type) {
case FL_VALUE_TYPE_NULL:
return true;
case FL_VALUE_TYPE_BOOL:
return fl_value_get_bool(a) == fl_value_get_bool(b);
case FL_VALUE_TYPE_INT:
return fl_value_get_int(a) == fl_value_get_int(b);
case FL_VALUE_TYPE_FLOAT:
return fl_value_get_float(a) == fl_value_get_float(b);
case FL_VALUE_TYPE_STRING: {
FlValueString* a_ = reinterpret_cast<FlValueString*>(a);
FlValueString* b_ = reinterpret_cast<FlValueString*>(b);
return g_strcmp0(a_->value, b_->value) == 0;
}
case FL_VALUE_TYPE_UINT8_LIST: {
if (fl_value_get_length(a) != fl_value_get_length(b))
return false;
const uint8_t* values_a = fl_value_get_uint8_list(a);
const uint8_t* values_b = fl_value_get_uint8_list(b);
for (size_t i = 0; i < fl_value_get_length(a); i++) {
if (values_a[i] != values_b[i])
return false;
}
return true;
}
case FL_VALUE_TYPE_INT32_LIST: {
if (fl_value_get_length(a) != fl_value_get_length(b))
return false;
const int32_t* values_a = fl_value_get_int32_list(a);
const int32_t* values_b = fl_value_get_int32_list(b);
for (size_t i = 0; i < fl_value_get_length(a); i++) {
if (values_a[i] != values_b[i])
return false;
}
return true;
}
case FL_VALUE_TYPE_INT64_LIST: {
if (fl_value_get_length(a) != fl_value_get_length(b))
return false;
const int64_t* values_a = fl_value_get_int64_list(a);
const int64_t* values_b = fl_value_get_int64_list(b);
for (size_t i = 0; i < fl_value_get_length(a); i++) {
if (values_a[i] != values_b[i])
return false;
}
return true;
}
case FL_VALUE_TYPE_FLOAT_LIST: {
if (fl_value_get_length(a) != fl_value_get_length(b))
return false;
const double* values_a = fl_value_get_float_list(a);
const double* values_b = fl_value_get_float_list(b);
for (size_t i = 0; i < fl_value_get_length(a); i++) {
if (values_a[i] != values_b[i])
return false;
}
return true;
}
case FL_VALUE_TYPE_LIST: {
if (fl_value_get_length(a) != fl_value_get_length(b))
return false;
for (size_t i = 0; i < fl_value_get_length(a); i++) {
if (!fl_value_equal(fl_value_get_list_value(a, i),
fl_value_get_list_value(b, i)))
return false;
}
return true;
}
case FL_VALUE_TYPE_MAP: {
if (fl_value_get_length(a) != fl_value_get_length(b))
return false;
for (size_t i = 0; i < fl_value_get_length(a); i++) {
FlValue* key = fl_value_get_map_key(a, i);
FlValue* value_b = fl_value_lookup(b, key);
if (value_b == nullptr)
return false;
FlValue* value_a = fl_value_get_map_value(a, i);
if (!fl_value_equal(value_a, value_b))
return false;
}
return true;
}
}
}
G_MODULE_EXPORT void fl_value_append(FlValue* self, FlValue* value) {
g_return_if_fail(self != nullptr);
g_return_if_fail(self->type == FL_VALUE_TYPE_LIST);
g_return_if_fail(value != nullptr);
fl_value_append_take(self, fl_value_ref(value));
}
G_MODULE_EXPORT void fl_value_append_take(FlValue* self, FlValue* value) {
g_return_if_fail(self != nullptr);
g_return_if_fail(self->type == FL_VALUE_TYPE_LIST);
g_return_if_fail(value != nullptr);
FlValueList* v = reinterpret_cast<FlValueList*>(self);
g_ptr_array_add(v->values, value);
}
G_MODULE_EXPORT void fl_value_set(FlValue* self, FlValue* key, FlValue* value) {
g_return_if_fail(self != nullptr);
g_return_if_fail(self->type == FL_VALUE_TYPE_MAP);
g_return_if_fail(key != nullptr);
g_return_if_fail(value != nullptr);
fl_value_set_take(self, fl_value_ref(key), fl_value_ref(value));
}
G_MODULE_EXPORT void fl_value_set_take(FlValue* self,
FlValue* key,
FlValue* value) {
g_return_if_fail(self != nullptr);
g_return_if_fail(self->type == FL_VALUE_TYPE_MAP);
g_return_if_fail(key != nullptr);
g_return_if_fail(value != nullptr);
FlValueMap* v = reinterpret_cast<FlValueMap*>(self);
ssize_t index = fl_value_lookup_index(self, key);
if (index < 0) {
g_ptr_array_add(v->keys, key);
g_ptr_array_add(v->values, value);
} else {
fl_value_destroy(v->keys->pdata[index]);
v->keys->pdata[index] = key;
fl_value_destroy(v->values->pdata[index]);
v->values->pdata[index] = value;
}
}
G_MODULE_EXPORT void fl_value_set_string(FlValue* self,
const gchar* key,
FlValue* value) {
g_return_if_fail(self != nullptr);
g_return_if_fail(self->type == FL_VALUE_TYPE_MAP);
g_return_if_fail(key != nullptr);
g_return_if_fail(value != nullptr);
fl_value_set_take(self, fl_value_new_string(key), fl_value_ref(value));
}
G_MODULE_EXPORT void fl_value_set_string_take(FlValue* self,
const gchar* key,
FlValue* value) {
g_return_if_fail(self != nullptr);
g_return_if_fail(self->type == FL_VALUE_TYPE_MAP);
g_return_if_fail(key != nullptr);
g_return_if_fail(value != nullptr);
fl_value_set_take(self, fl_value_new_string(key), value);
}
G_MODULE_EXPORT bool fl_value_get_bool(FlValue* self) {
g_return_val_if_fail(self != nullptr, FALSE);
g_return_val_if_fail(self->type == FL_VALUE_TYPE_BOOL, FALSE);
FlValueBool* v = reinterpret_cast<FlValueBool*>(self);
return v->value;
}
G_MODULE_EXPORT int64_t fl_value_get_int(FlValue* self) {
g_return_val_if_fail(self != nullptr, 0);
g_return_val_if_fail(self->type == FL_VALUE_TYPE_INT, 0);
FlValueInt* v = reinterpret_cast<FlValueInt*>(self);
return v->value;
}
G_MODULE_EXPORT double fl_value_get_float(FlValue* self) {
g_return_val_if_fail(self != nullptr, 0.0);
g_return_val_if_fail(self->type == FL_VALUE_TYPE_FLOAT, 0.0);
FlValueDouble* v = reinterpret_cast<FlValueDouble*>(self);
return v->value;
}
G_MODULE_EXPORT const gchar* fl_value_get_string(FlValue* self) {
g_return_val_if_fail(self != nullptr, nullptr);
g_return_val_if_fail(self->type == FL_VALUE_TYPE_STRING, nullptr);
FlValueString* v = reinterpret_cast<FlValueString*>(self);
return v->value;
}
G_MODULE_EXPORT const uint8_t* fl_value_get_uint8_list(FlValue* self) {
g_return_val_if_fail(self != nullptr, nullptr);
g_return_val_if_fail(self->type == FL_VALUE_TYPE_UINT8_LIST, nullptr);
FlValueUint8List* v = reinterpret_cast<FlValueUint8List*>(self);
return v->values;
}
G_MODULE_EXPORT const int32_t* fl_value_get_int32_list(FlValue* self) {
g_return_val_if_fail(self != nullptr, nullptr);
g_return_val_if_fail(self->type == FL_VALUE_TYPE_INT32_LIST, nullptr);
FlValueInt32List* v = reinterpret_cast<FlValueInt32List*>(self);
return v->values;
}
G_MODULE_EXPORT const int64_t* fl_value_get_int64_list(FlValue* self) {
g_return_val_if_fail(self != nullptr, nullptr);
g_return_val_if_fail(self->type == FL_VALUE_TYPE_INT64_LIST, nullptr);
FlValueInt64List* v = reinterpret_cast<FlValueInt64List*>(self);
return v->values;
}
G_MODULE_EXPORT const double* fl_value_get_float_list(FlValue* self) {
g_return_val_if_fail(self != nullptr, nullptr);
g_return_val_if_fail(self->type == FL_VALUE_TYPE_FLOAT_LIST, nullptr);
FlValueFloatList* v = reinterpret_cast<FlValueFloatList*>(self);
return v->values;
}
G_MODULE_EXPORT size_t fl_value_get_length(FlValue* self) {
g_return_val_if_fail(self != nullptr, 0);
g_return_val_if_fail(self->type == FL_VALUE_TYPE_UINT8_LIST ||
self->type == FL_VALUE_TYPE_INT32_LIST ||
self->type == FL_VALUE_TYPE_INT64_LIST ||
self->type == FL_VALUE_TYPE_FLOAT_LIST ||
self->type == FL_VALUE_TYPE_LIST ||
self->type == FL_VALUE_TYPE_MAP,
0);
switch (self->type) {
case FL_VALUE_TYPE_UINT8_LIST: {
FlValueUint8List* v = reinterpret_cast<FlValueUint8List*>(self);
return v->values_length;
}
case FL_VALUE_TYPE_INT32_LIST: {
FlValueInt32List* v = reinterpret_cast<FlValueInt32List*>(self);
return v->values_length;
}
case FL_VALUE_TYPE_INT64_LIST: {
FlValueInt64List* v = reinterpret_cast<FlValueInt64List*>(self);
return v->values_length;
}
case FL_VALUE_TYPE_FLOAT_LIST: {
FlValueFloatList* v = reinterpret_cast<FlValueFloatList*>(self);
return v->values_length;
}
case FL_VALUE_TYPE_LIST: {
FlValueList* v = reinterpret_cast<FlValueList*>(self);
return v->values->len;
}
case FL_VALUE_TYPE_MAP: {
FlValueMap* v = reinterpret_cast<FlValueMap*>(self);
return v->keys->len;
}
case FL_VALUE_TYPE_NULL:
case FL_VALUE_TYPE_BOOL:
case FL_VALUE_TYPE_INT:
case FL_VALUE_TYPE_FLOAT:
case FL_VALUE_TYPE_STRING:
return 0;
}
return 0;
}
G_MODULE_EXPORT FlValue* fl_value_get_list_value(FlValue* self, size_t index) {
g_return_val_if_fail(self != nullptr, nullptr);
g_return_val_if_fail(self->type == FL_VALUE_TYPE_LIST, nullptr);
FlValueList* v = reinterpret_cast<FlValueList*>(self);
return static_cast<FlValue*>(g_ptr_array_index(v->values, index));
}
G_MODULE_EXPORT FlValue* fl_value_get_map_key(FlValue* self, size_t index) {
g_return_val_if_fail(self != nullptr, nullptr);
g_return_val_if_fail(self->type == FL_VALUE_TYPE_MAP, nullptr);
FlValueMap* v = reinterpret_cast<FlValueMap*>(self);
return static_cast<FlValue*>(g_ptr_array_index(v->keys, index));
}
G_MODULE_EXPORT FlValue* fl_value_get_map_value(FlValue* self, size_t index) {
g_return_val_if_fail(self != nullptr, nullptr);
g_return_val_if_fail(self->type == FL_VALUE_TYPE_MAP, nullptr);
FlValueMap* v = reinterpret_cast<FlValueMap*>(self);
return static_cast<FlValue*>(g_ptr_array_index(v->values, index));
}
G_MODULE_EXPORT FlValue* fl_value_lookup(FlValue* self, FlValue* key) {
g_return_val_if_fail(self != nullptr, nullptr);
g_return_val_if_fail(self->type == FL_VALUE_TYPE_MAP, nullptr);
ssize_t index = fl_value_lookup_index(self, key);
if (index < 0)
return nullptr;
return fl_value_get_map_value(self, index);
}
G_MODULE_EXPORT FlValue* fl_value_lookup_string(FlValue* self,
const gchar* key) {
g_return_val_if_fail(self != nullptr, nullptr);
g_autoptr(FlValue) string_key = fl_value_new_string(key);
return fl_value_lookup(self, string_key);
}
G_MODULE_EXPORT gchar* fl_value_to_string(FlValue* value) {
GString* buffer = g_string_new("");
value_to_string(value, buffer);
return g_string_free(buffer, FALSE);
}