| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| typedef struct _GVariantTypeInfo GVariantTypeInfo; |
| |
| |
| |
| |
| |
| |
| |
| (g_variant_type_info_get_type_string(info)[0]) |
| |
| struct _GVariantTypeInfo |
| { |
| gsize fixed_size; |
| guchar alignment; |
| guchar container_class; |
| }; |
| |
| typedef struct |
| { |
| GVariantTypeInfo *type_info; |
| |
| gsize i, a; |
| gint8 b, c; |
| |
| guint8 ending_type; |
| } GVariantMemberInfo; |
| |
| |
| |
| |
| |
| typedef struct |
| { |
| GVariantTypeInfo info; |
| |
| gchar *type_string; |
| gint ref_count; |
| } ContainerInfo; |
| |
| typedef struct |
| { |
| ContainerInfo container; |
| |
| GVariantTypeInfo *element; |
| } ArrayInfo; |
| |
| typedef struct |
| { |
| ContainerInfo container; |
| |
| GVariantMemberInfo *members; |
| gsize n_members; |
| } TupleInfo; |
| |
| |
| static const GVariantTypeInfo g_variant_type_info_basic_table[24] = { |
| |
| |
| |
| |
| { fixed_aligned(1) }, |
| { not_a_type }, |
| { fixed_aligned(8) }, |
| { not_a_type }, |
| { not_a_type }, |
| { unaligned }, |
| { fixed_aligned(4) }, |
| { fixed_aligned(4) }, |
| { not_a_type }, |
| { not_a_type }, |
| { not_a_type }, |
| { not_a_type }, |
| { fixed_aligned(2) }, |
| { unaligned }, |
| { not_a_type }, |
| { fixed_aligned(2) }, |
| { not_a_type }, |
| { unaligned }, |
| { fixed_aligned(8) }, |
| { fixed_aligned(4) }, |
| { aligned(8) }, |
| { not_a_type }, |
| { fixed_aligned(8) }, |
| { fixed_aligned(1) }, |
| |
| |
| |
| |
| }; |
| |
| static GRecMutex g_variant_type_info_lock; |
| static GHashTable *g_variant_type_info_table; |
| |
| static GVariantTypeInfo * g_variant_type_info_ref (GVariantTypeInfo *info); |
| static void g_variant_type_info_unref (GVariantTypeInfo *info); |
| static GVariantTypeInfo * g_variant_type_info_get (const GVariantType *type); |
| |
| |
| static ArrayInfo * |
| GV_ARRAY_INFO (GVariantTypeInfo *info) |
| { |
| return (ArrayInfo *) info; |
| } |
| |
| static void |
| array_info_free (GVariantTypeInfo *info) |
| { |
| ArrayInfo *array_info; |
| |
| g_assert (info->container_class == GV_ARRAY_INFO_CLASS); |
| array_info = (ArrayInfo *) info; |
| |
| g_variant_type_info_unref (array_info->element); |
| g_slice_free (ArrayInfo, array_info); |
| } |
| |
| static ContainerInfo * |
| array_info_new (const GVariantType *type) |
| { |
| ArrayInfo *info; |
| |
| info = g_slice_new (ArrayInfo); |
| info->container.info.container_class = GV_ARRAY_INFO_CLASS; |
| |
| info->element = g_variant_type_info_get (g_variant_type_element (type)); |
| info->container.info.alignment = info->element->alignment; |
| info->container.info.fixed_size = 0; |
| |
| return (ContainerInfo *) info; |
| } |
| |
| |
| |
| static TupleInfo * |
| GV_TUPLE_INFO (GVariantTypeInfo *info) |
| { |
| return (TupleInfo *) info; |
| } |
| |
| static void |
| tuple_info_free (GVariantTypeInfo *info) |
| { |
| TupleInfo *tuple_info; |
| gint i; |
| |
| g_assert (info->container_class == GV_TUPLE_INFO_CLASS); |
| tuple_info = (TupleInfo *) info; |
| |
| for (i = 0; i < tuple_info->n_members; i++) |
| g_variant_type_info_unref (tuple_info->members[i].type_info); |
| |
| g_slice_free1 (sizeof (GVariantMemberInfo) * tuple_info->n_members, |
| tuple_info->members); |
| g_slice_free (TupleInfo, tuple_info); |
| } |
| |
| static void |
| tuple_allocate_members (const GVariantType *type, |
| GVariantMemberInfo **members, |
| gsize *n_members) |
| { |
| const GVariantType *item_type; |
| gsize i = 0; |
| |
| *n_members = g_variant_type_n_items (type); |
| *members = g_slice_alloc (sizeof (GVariantMemberInfo) * *n_members); |
| |
| item_type = g_variant_type_first (type); |
| while (item_type) |
| { |
| GVariantMemberInfo *member = &(*members)[i++]; |
| |
| member->type_info = g_variant_type_info_get (item_type); |
| item_type = g_variant_type_next (item_type); |
| |
| if (member->type_info->fixed_size) |
| member->ending_type = G_VARIANT_MEMBER_ENDING_FIXED; |
| else if (item_type == NULL) |
| member->ending_type = G_VARIANT_MEMBER_ENDING_LAST; |
| else |
| member->ending_type = G_VARIANT_MEMBER_ENDING_OFFSET; |
| } |
| |
| g_assert (i == *n_members); |
| } |
| |
| |
| |
| |
| |
| static gboolean |
| tuple_get_item (TupleInfo *info, |
| GVariantMemberInfo *item, |
| gsize *d, |
| gsize *e) |
| { |
| if (&info->members[info->n_members] == item) |
| return FALSE; |
| |
| *d = item->type_info->alignment; |
| *e = item->type_info->fixed_size; |
| return TRUE; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void |
| tuple_table_append (GVariantMemberInfo **items, |
| gsize i, |
| gsize a, |
| gsize b, |
| gsize c) |
| { |
| GVariantMemberInfo *item = (*items)++; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| a += ~b & c; |
| c &= b; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| item->i = i; |
| item->a = a + b; |
| item->b = ~b; |
| item->c = c; |
| } |
| |
| static gsize |
| tuple_align (gsize offset, |
| guint alignment) |
| { |
| return offset + ((-offset) & alignment); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void |
| tuple_generate_table (TupleInfo *info) |
| { |
| GVariantMemberInfo *items = info->members; |
| gsize i = -1, a = 0, b = 0, c = 0, d, e; |
| |
| |
| |
| |
| |
| while (tuple_get_item (info, items, &d, &e)) |
| { |
| |
| if (d <= b) |
| c = tuple_align (c, d); |
| else |
| a += tuple_align (c, b), b = d, c = 0; |
| |
| |
| |
| |
| tuple_table_append (&items, i, a, b, c); |
| |
| |
| if (e == 0) |
| |
| |
| |
| |
| |
| |
| i++, a = b = c = 0; |
| else |
| |
| c += e; |
| } |
| } |
| |
| static void |
| tuple_set_base_info (TupleInfo *info) |
| { |
| GVariantTypeInfo *base = &info->container.info; |
| |
| if (info->n_members > 0) |
| { |
| GVariantMemberInfo *m; |
| |
| |
| |
| |
| base->alignment = 0; |
| for (m = info->members; m < &info->members[info->n_members]; m++) |
| |
| |
| |
| base->alignment |= m->type_info->alignment; |
| |
| m--; |
| |
| |
| |
| |
| |
| if (m->i == -1 && m->type_info->fixed_size) |
| |
| |
| |
| |
| |
| |
| |
| |
| base->fixed_size = |
| tuple_align (((m->a & m->b) | m->c) + m->type_info->fixed_size, |
| base->alignment); |
| else |
| |
| base->fixed_size = 0; |
| } |
| else |
| { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| base->alignment = 0; |
| base->fixed_size = 1; |
| } |
| } |
| |
| static ContainerInfo * |
| tuple_info_new (const GVariantType *type) |
| { |
| TupleInfo *info; |
| |
| info = g_slice_new (TupleInfo); |
| info->container.info.container_class = GV_TUPLE_INFO_CLASS; |
| |
| tuple_allocate_members (type, &info->members, &info->n_members); |
| tuple_generate_table (info); |
| tuple_set_base_info (info); |
| |
| return (ContainerInfo *) info; |
| } |
| |
| static const GVariantMemberInfo * |
| g_variant_type_info_member_info (GVariantTypeInfo *info, |
| gsize index) |
| { |
| TupleInfo *tuple_info = GV_TUPLE_INFO (info); |
| |
| if (index < tuple_info->n_members) |
| return &tuple_info->members[index]; |
| |
| return NULL; |
| } |
| |
| static GVariantTypeInfo * |
| g_variant_type_info_element (GVariantTypeInfo *info) |
| { |
| return GV_ARRAY_INFO (info)->element; |
| } |
| |
| static GVariantTypeInfo * |
| g_variant_type_info_ref (GVariantTypeInfo *info) |
| { |
| if (info->container_class) |
| { |
| ContainerInfo *container = (ContainerInfo *) info; |
| |
| g_assert_cmpint (container->ref_count, >, 0); |
| g_atomic_int_inc (&container->ref_count); |
| } |
| |
| return info; |
| } |
| |
| static void |
| g_variant_type_info_unref (GVariantTypeInfo *info) |
| { |
| if (info->container_class) |
| { |
| ContainerInfo *container = (ContainerInfo *) info; |
| |
| g_rec_mutex_lock (&g_variant_type_info_lock); |
| if (g_atomic_int_dec_and_test (&container->ref_count)) |
| { |
| g_hash_table_remove (g_variant_type_info_table, |
| container->type_string); |
| if (g_hash_table_size (g_variant_type_info_table) == 0) |
| { |
| g_hash_table_unref (g_variant_type_info_table); |
| g_variant_type_info_table = NULL; |
| } |
| g_rec_mutex_unlock (&g_variant_type_info_lock); |
| |
| g_free (container->type_string); |
| |
| if (info->container_class == GV_ARRAY_INFO_CLASS) |
| array_info_free (info); |
| |
| else if (info->container_class == GV_TUPLE_INFO_CLASS) |
| tuple_info_free (info); |
| |
| else |
| g_assert_not_reached (); |
| } |
| else |
| g_rec_mutex_unlock (&g_variant_type_info_lock); |
| } |
| } |
| |
| static GVariantTypeInfo * |
| g_variant_type_info_get (const GVariantType *type) |
| { |
| char type_char; |
| |
| type_char = g_variant_type_peek_string (type)[0]; |
| |
| if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE || |
| type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY || |
| type_char == G_VARIANT_TYPE_INFO_CHAR_TUPLE || |
| type_char == G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY) |
| { |
| GVariantTypeInfo *info; |
| gchar *type_string; |
| |
| type_string = g_variant_type_dup_string (type); |
| |
| g_rec_mutex_lock (&g_variant_type_info_lock); |
| |
| if (g_variant_type_info_table == NULL) |
| g_variant_type_info_table = g_hash_table_new (g_str_hash, |
| g_str_equal); |
| info = g_hash_table_lookup (g_variant_type_info_table, type_string); |
| |
| if (info == NULL) |
| { |
| ContainerInfo *container; |
| |
| if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE || |
| type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY) |
| { |
| container = array_info_new (type); |
| } |
| else |
| { |
| container = tuple_info_new (type); |
| } |
| |
| info = (GVariantTypeInfo *) container; |
| container->type_string = type_string; |
| container->ref_count = 1; |
| |
| g_hash_table_insert (g_variant_type_info_table, type_string, info); |
| type_string = NULL; |
| } |
| else |
| g_variant_type_info_ref (info); |
| |
| g_rec_mutex_unlock (&g_variant_type_info_lock); |
| g_free (type_string); |
| |
| return info; |
| } |
| else |
| { |
| const GVariantTypeInfo *info; |
| int index; |
| |
| index = type_char - 'b'; |
| g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table) == 24); |
| g_assert_cmpint (0, <=, index); |
| g_assert_cmpint (index, <, 24); |
| |
| info = g_variant_type_info_basic_table + index; |
| |
| return (GVariantTypeInfo *) info; |
| } |
| } |
| |
| static inline void |
| gvs_write_unaligned_le (guchar *bytes, |
| gsize value, |
| guint size) |
| { |
| union |
| { |
| guchar bytes[GLIB_SIZEOF_SIZE_T]; |
| gsize integer; |
| } tmpvalue; |
| |
| tmpvalue.integer = GSIZE_TO_LE (value); |
| memcpy (bytes, &tmpvalue.bytes, size); |
| } |
| |
| static guint |
| gvs_get_offset_size (gsize size) |
| { |
| if (size > G_MAXUINT32) |
| return 8; |
| |
| else if (size > G_MAXUINT16) |
| return 4; |
| |
| else if (size > G_MAXUINT8) |
| return 2; |
| |
| else if (size > 0) |
| return 1; |
| |
| return 0; |
| } |
| |
| static gsize |
| gvs_calculate_total_size (gsize body_size, |
| gsize offsets) |
| { |
| if (body_size + 1 * offsets <= G_MAXUINT8) |
| return body_size + 1 * offsets; |
| |
| if (body_size + 2 * offsets <= G_MAXUINT16) |
| return body_size + 2 * offsets; |
| |
| if (body_size + 4 * offsets <= G_MAXUINT32) |
| return body_size + 4 * offsets; |
| |
| return body_size + 8 * offsets; |
| } |
| |
| |
| |
| |
| |
| |
| typedef struct _OtVariantBuilderInfo OtVariantBuilderInfo; |
| |
| struct _OtVariantBuilderInfo { |
| OtVariantBuilderInfo *parent; |
| OtVariantBuilder *builder; |
| GVariantType *type; |
| GVariantTypeInfo *type_info; |
| guint64 offset; |
| int n_children; |
| GArray *child_ends; |
| |
| |
| |
| |
| const GVariantType *expected_type; |
| |
| |
| |
| const GVariantType *prev_item_type; |
| GVariantType *prev_item_type_base; |
| |
| |
| gsize min_items; |
| gsize max_items; |
| |
| |
| |
| |
| guint uniform_item_types : 1; |
| } ; |
| |
| struct _OtVariantBuilder { |
| gint ref_count; |
| int fd; |
| |
| |
| |
| |
| OtVariantBuilderInfo *head; |
| }; |
| |
| static OtVariantBuilderInfo * |
| ot_variant_builder_info_new (OtVariantBuilder *builder, const GVariantType *type) |
| { |
| OtVariantBuilderInfo *info; |
| |
| info = (OtVariantBuilderInfo *) g_slice_new0 (OtVariantBuilderInfo); |
| |
| g_return_val_if_fail (g_variant_type_is_container (type), NULL); |
| |
| info->builder = builder; |
| info->type = g_variant_type_copy (type); |
| info->type_info = g_variant_type_info_get (type); |
| info->offset = 0; |
| info->n_children = 0; |
| info->child_ends = g_array_new (FALSE, TRUE, sizeof (guint64)); |
| |
| switch (*(const gchar *) type) |
| { |
| case G_VARIANT_CLASS_VARIANT: |
| info->uniform_item_types = TRUE; |
| info->expected_type = NULL; |
| info->min_items = 1; |
| info->max_items = 1; |
| break; |
| |
| case G_VARIANT_CLASS_ARRAY: |
| info->uniform_item_types = TRUE; |
| info->expected_type = |
| g_variant_type_element (info->type); |
| info->min_items = 0; |
| info->max_items = -1; |
| break; |
| |
| case G_VARIANT_CLASS_MAYBE: |
| info->uniform_item_types = TRUE; |
| info->expected_type = |
| g_variant_type_element (info->type); |
| info->min_items = 0; |
| info->max_items = 1; |
| break; |
| |
| case G_VARIANT_CLASS_DICT_ENTRY: |
| info->uniform_item_types = FALSE; |
| info->expected_type = |
| g_variant_type_key (info->type); |
| info->min_items = 2; |
| info->max_items = 2; |
| break; |
| |
| case 'r': |
| info->uniform_item_types = FALSE; |
| info->expected_type = NULL; |
| info->min_items = 0; |
| info->max_items = -1; |
| break; |
| |
| case G_VARIANT_CLASS_TUPLE: |
| info->expected_type = |
| g_variant_type_first (info->type); |
| info->min_items = g_variant_type_n_items (type); |
| info->max_items = info->min_items; |
| info->uniform_item_types = FALSE; |
| break; |
| |
| default: |
| g_assert_not_reached (); |
| } |
| |
| return info; |
| } |
| |
| static void |
| ot_variant_builder_info_free (OtVariantBuilderInfo *info) |
| { |
| if (info->parent) |
| ot_variant_builder_info_free (info); |
| |
| g_variant_type_free (info->type); |
| g_array_unref (info->child_ends); |
| g_free (info->prev_item_type_base); |
| |
| g_slice_free (OtVariantBuilderInfo, info); |
| } |
| |
| OtVariantBuilder * |
| ot_variant_builder_new (const GVariantType *type, |
| int fd) |
| { |
| OtVariantBuilder *builder; |
| |
| builder = (OtVariantBuilder *) g_slice_new0 (OtVariantBuilder); |
| |
| g_return_val_if_fail (g_variant_type_is_container (type), NULL); |
| |
| builder->head = ot_variant_builder_info_new (builder, type); |
| builder->ref_count = 1; |
| builder->fd = fd; |
| |
| return builder; |
| } |
| |
| void |
| ot_variant_builder_unref (OtVariantBuilder *builder) |
| { |
| if (--builder->ref_count) |
| return; |
| |
| ot_variant_builder_info_free (builder->head); |
| |
| g_slice_free (OtVariantBuilder, builder); |
| } |
| |
| OtVariantBuilder * |
| ot_variant_builder_ref (OtVariantBuilder *builder) |
| { |
| builder->ref_count++; |
| return builder; |
| } |
| |
| |
| |
| static gboolean |
| ot_variant_builder_pre_add (OtVariantBuilderInfo *info, |
| const GVariantType *type, |
| GError **error) |
| { |
| guint alignment = 0; |
| |
| if (!info->uniform_item_types) |
| { |
| |
| if (info->expected_type) |
| info->expected_type = |
| g_variant_type_next (info->expected_type); |
| |
| if (info->prev_item_type) |
| info->prev_item_type = |
| g_variant_type_next (info->prev_item_type); |
| } |
| else |
| { |
| g_free (info->prev_item_type_base); |
| info->prev_item_type_base = (GVariantType *)g_strdup ((char *)type); |
| info->prev_item_type = info->prev_item_type_base; |
| } |
| |
| if (g_variant_type_is_tuple (info->type) || |
| g_variant_type_is_dict_entry (info->type)) |
| { |
| const GVariantMemberInfo *member_info; |
| |
| member_info = g_variant_type_info_member_info (info->type_info, info->n_children); |
| alignment = member_info->type_info->alignment; |
| } |
| else if (g_variant_type_is_array (info->type)) |
| { |
| GVariantTypeInfo *element_info = g_variant_type_info_element (info->type_info); |
| |
| alignment = element_info->alignment; |
| } |
| else if (g_variant_type_is_variant (info->type)) |
| { |
| alignment = info->type_info->alignment; |
| } |
| else |
| return glnx_throw (error, "adding to type %s not supported", (char *)info->type); |
| |
| while (info->offset & alignment) |
| { |
| if (glnx_loop_write (info->builder->fd, "\0", 1) < 0) |
| return glnx_throw_errno (error); |
| info->offset++; |
| } |
| |
| return TRUE; |
| } |
| |
| static void |
| ot_variant_builder_add_child_end (OtVariantBuilderInfo *info) |
| { |
| guint64 v = info->offset; |
| g_array_append_val (info->child_ends, v); |
| } |
| |
| |
| |
| |
| |
| static gboolean |
| ot_variant_builder_post_add (OtVariantBuilderInfo *info, |
| const GVariantType *type, |
| guint64 bytes_added, |
| GError **error) |
| { |
| info->offset += bytes_added; |
| |
| if (g_variant_type_is_tuple (info->type) || |
| g_variant_type_is_dict_entry (info->type)) |
| { |
| const GVariantMemberInfo *member_info; |
| |
| member_info = g_variant_type_info_member_info (info->type_info, info->n_children); |
| if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET) |
| ot_variant_builder_add_child_end (info); |
| } |
| else if (g_variant_type_is_array (info->type)) |
| { |
| GVariantTypeInfo *element_info = g_variant_type_info_element (info->type_info); |
| |
| if (!element_info->fixed_size) |
| ot_variant_builder_add_child_end (info); |
| } |
| else if (g_variant_type_is_variant (info->type)) |
| { |
| |
| if (glnx_loop_write (info->builder->fd, "\0", 1) < 0) |
| return glnx_throw_errno (error); |
| |
| if (glnx_loop_write (info->builder->fd, (char *)type, strlen ((char *)type)) < 0) |
| return glnx_throw_errno (error); |
| |
| info->offset += 1 + strlen ((char *)type); |
| } |
| else |
| return glnx_throw (error, "adding to type %s not supported", (char *)info->type); |
| |
| info->n_children++; |
| |
| return TRUE; |
| } |
| |
| gboolean |
| ot_variant_builder_add_from_fd (OtVariantBuilder *builder, |
| const GVariantType *type, |
| int fd, |
| guint64 size, |
| GError **error) |
| { |
| OtVariantBuilderInfo *info = builder->head; |
| |
| g_return_val_if_fail (info->n_children < info->max_items, |
| FALSE); |
| g_return_val_if_fail (!info->expected_type || |
| g_variant_type_is_subtype_of (type, |
| info->expected_type), |
| FALSE); |
| g_return_val_if_fail (!info->prev_item_type || |
| g_variant_type_is_subtype_of (info->prev_item_type, |
| type), |
| FALSE); |
| |
| if (!ot_variant_builder_pre_add (info, type, error)) |
| return FALSE; |
| |
| if (glnx_regfile_copy_bytes (fd, builder->fd, size) < 0) |
| return glnx_throw_errno (error); |
| |
| if (!ot_variant_builder_post_add (info, type, size, error)) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| gboolean |
| ot_variant_builder_add_value (OtVariantBuilder *builder, |
| GVariant *value, |
| GError **error) |
| { |
| OtVariantBuilderInfo *info = builder->head; |
| gconstpointer data; |
| gsize data_size; |
| |
| g_autoptr(GVariant) keep_around_until_return G_GNUC_UNUSED = g_variant_ref_sink (value); |
| |
| g_return_val_if_fail (info->n_children < info->max_items, |
| FALSE); |
| g_return_val_if_fail (!info->expected_type || |
| g_variant_is_of_type (value, |
| info->expected_type), |
| FALSE); |
| g_return_val_if_fail (!info->prev_item_type || |
| g_variant_is_of_type (value, |
| info->prev_item_type), |
| FALSE); |
| |
| if (!ot_variant_builder_pre_add (info, g_variant_get_type (value), error)) |
| return FALSE; |
| |
| data = g_variant_get_data (value); |
| data_size = g_variant_get_size (value); |
| |
| if (data) |
| { |
| if (glnx_loop_write (builder->fd, data, data_size) < 0) |
| return glnx_throw_errno (error); |
| } |
| |
| if (!ot_variant_builder_post_add (info, g_variant_get_type (value), data_size, error)) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| gboolean |
| ot_variant_builder_add (OtVariantBuilder *builder, |
| GError **error, |
| const gchar *format_string, |
| ...) |
| { |
| GVariant *variant; |
| va_list ap; |
| |
| va_start (ap, format_string); |
| variant = g_variant_new_va (format_string, NULL, &ap); |
| va_end (ap); |
| |
| return ot_variant_builder_add_value (builder, variant, error); |
| } |
| |
| |
| gboolean |
| ot_variant_builder_open (OtVariantBuilder *builder, |
| const GVariantType *type, |
| GError **error) |
| { |
| OtVariantBuilderInfo *info = builder->head; |
| OtVariantBuilderInfo *new_info; |
| |
| g_return_val_if_fail (info->n_children < info->max_items, |
| FALSE); |
| g_return_val_if_fail (!info->expected_type || |
| g_variant_type_is_subtype_of (type, |
| info->expected_type), |
| FALSE); |
| g_return_val_if_fail (!info->prev_item_type || |
| g_variant_type_is_subtype_of (info->prev_item_type, |
| type), |
| FALSE); |
| |
| if (!ot_variant_builder_pre_add (info, type, error)) |
| return FALSE; |
| |
| new_info = ot_variant_builder_info_new (builder, type); |
| new_info->parent = info; |
| |
| |
| if (info->prev_item_type) |
| { |
| if (!new_info->uniform_item_types) |
| |
| new_info->prev_item_type = |
| g_variant_type_first (info->prev_item_type); |
| |
| else if (!g_variant_type_is_variant (new_info->type)) |
| |
| new_info->prev_item_type = |
| g_variant_type_element (info->prev_item_type); |
| } |
| |
| builder->head = new_info; |
| return TRUE; |
| } |
| |
| gboolean |
| ot_variant_builder_close (OtVariantBuilder *builder, |
| GError **error) |
| { |
| OtVariantBuilderInfo *info = builder->head; |
| OtVariantBuilderInfo *parent; |
| |
| g_return_val_if_fail (info->parent != NULL, FALSE); |
| |
| if (!ot_variant_builder_end (builder, error)) |
| return FALSE; |
| |
| parent = info->parent; |
| |
| if (!ot_variant_builder_post_add (parent, info->type, info->offset, error)) |
| return FALSE; |
| |
| builder->head = parent; |
| |
| info->parent = NULL; |
| ot_variant_builder_info_free (info); |
| |
| return TRUE; |
| } |
| |
| gboolean |
| ot_variant_builder_end (OtVariantBuilder *builder, |
| GError **error) |
| { |
| OtVariantBuilderInfo *info = builder->head; |
| gboolean add_offset_table = FALSE; |
| gboolean reverse_offset_table = FALSE; |
| |
| g_return_val_if_fail (info->n_children >= info->min_items, |
| FALSE); |
| g_return_val_if_fail (!info->uniform_item_types || |
| info->prev_item_type != NULL || |
| g_variant_type_is_definite (info->type), |
| FALSE); |
| |
| if (g_variant_type_is_tuple (info->type) || |
| g_variant_type_is_dict_entry (info->type)) |
| { |
| add_offset_table = TRUE; |
| reverse_offset_table = TRUE; |
| } |
| else if (g_variant_type_is_array (info->type)) |
| { |
| GVariantTypeInfo *element_info = g_variant_type_info_element (info->type_info); |
| |
| if (!element_info->fixed_size) |
| add_offset_table = TRUE; |
| } |
| else if (g_variant_type_is_variant (info->type)) |
| { |
| |
| } |
| else |
| return glnx_throw (error, "closing type %s not supported", (char *)info->type); |
| |
| if (add_offset_table) |
| { |
| const gsize total_size = gvs_calculate_total_size (info->offset, info->child_ends->len); |
| const gsize offset_size = gvs_get_offset_size (total_size); |
| const gsize offset_table_size = total_size - info->offset; |
| g_autofree guchar *offset_table = g_malloc (offset_table_size); |
| guchar *p = offset_table; |
| if (reverse_offset_table) |
| { |
| for (int i = info->child_ends->len - 1; i >= 0; i--) |
| { |
| gvs_write_unaligned_le (p, g_array_index (info->child_ends, guint64, i), offset_size); |
| p += offset_size; |
| } |
| } |
| else |
| { |
| for (int i = 0; i < info->child_ends->len; i++) |
| { |
| gvs_write_unaligned_le (p, g_array_index (info->child_ends, guint64, i), offset_size); |
| p += offset_size; |
| } |
| } |
| |
| if (glnx_loop_write (builder->fd, offset_table, offset_table_size) < 0) |
| return glnx_throw_errno (error); |
| |
| info->offset += offset_table_size; |
| } |
| else |
| g_assert (info->child_ends->len == 0); |
| |
| return TRUE; |
| } |