gvariant: fix UBSan related code
[platform/upstream/dbus.git] / dbus / dbus-marshal-gvariant.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* dbus-marshal-gvariant.c  Marshalling routines for GVariant protocol
3  *
4  * Copyright (C) 2015  Samsung Electronics
5  *
6  * Licensed under the Academic Free License version 2.1
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #include <config.h>
25 #include "dbus-internals.h"
26 #include "dbus-marshal-gvariant.h"
27 #include "dbus-protocol-gvariant.h"
28 #include "dbus-marshal-basic.h"
29 #include "dbus-message-private.h"
30 #include "dbus-signature.h"
31 #include "dbus-connection-internal.h"
32 #include <endian.h>
33
34 /** Static #DBusString containing the signature of a message header */
35 _DBUS_STRING_DEFINE_STATIC(_dbus_header_gvariant_signature_str, DBUS_HEADER_GVARIANT_SIGNATURE);
36
37 static int
38 _dbus_reader_get_signature_fixed_size (const DBusString *signature, int *pos, int *alignment, int depth);
39
40 #define FIELD_ID_SIZE sizeof(dbus_uint64_t)
41
42 const DBusString *
43 _dbus_get_gvariant_header_signature_str (void)
44 {
45   return &_dbus_header_gvariant_signature_str;
46 }
47
48 static dbus_bool_t
49 append_sized_value (DBusString *str,
50                     dbus_uint64_t value,
51                     size_t value_size)
52 {
53   /* always write as little endian */
54   size_t i;
55   for (i = 0; i < value_size; i++)
56   {
57     size_t move = 8 * i;
58     dbus_uint64_t mask = 0xFFull << move;
59     if (!_dbus_string_append_byte(str, (value & mask) >> move))
60       return FALSE;
61   }
62   return TRUE;
63 }
64
65 #define MAX_OFFSET_SIZE 8
66 #define MAX_VALUE_FOR_OFFSET_SIZE(o) ((1ULL<<(8*(o)))-1)
67
68 /* taken from systemd */
69 static size_t
70 bus_gvariant_determine_word_size(size_t sz, size_t extra)
71 {
72   if (sz + extra <= 0xFF)
73     return 1;
74   else if (sz + extra*2 <= 0xFFFF)
75     return 2;
76   else if (sz + extra*4 <= 0xFFFFFFFF)
77     return 4;
78   else
79     return 8;
80 }
81
82 /* taken from systemd */
83 static size_t
84 bus_gvariant_read_word_le (const void *p, size_t sz)
85 {
86   union {
87     uint16_t u16;
88     uint32_t u32;
89     uint64_t u64;
90   } x;
91
92   // FIXME
93 //  assert(p);
94
95   if (sz == 1)
96     return *(uint8_t*) p;
97
98   memcpy(&x, p, sz);
99
100   if (sz == 2)
101     return le16toh(x.u16);
102   else if (sz == 4)
103     return le32toh(x.u32);
104   else if (sz == 8)
105     return le64toh(x.u64);
106   return 0;
107 }
108
109 static const char *
110 get_header_const_array (DBusHeader *header)
111 {
112   return _dbus_string_get_const_data (&header->data) + FIRST_GVARIANT_FIELD_OFFSET;
113 }
114
115 static size_t
116 get_header_array_size (DBusHeader *header)
117 {
118   return _dbus_string_get_length (&header->data) - FIRST_GVARIANT_FIELD_OFFSET - header->padding;
119 }
120
121 static dbus_bool_t
122 append_offsets (DBusString *str,
123                 size_t *fields_offsets,
124                 size_t n_fields_offsets)
125 {
126   size_t i;
127   size_t array_size = _dbus_string_get_length (str) - FIRST_GVARIANT_FIELD_OFFSET;
128   size_t offset_size = bus_gvariant_determine_word_size (array_size, n_fields_offsets);
129
130   for (i = 0; i < n_fields_offsets; i++)
131   {
132     if (!append_sized_value (str, fields_offsets[i], offset_size))
133       return FALSE;
134   }
135   return TRUE;
136 }
137
138 static dbus_bool_t
139 append_field_string (DBusString *str,
140               dbus_uint64_t field,
141               const char *value,
142               char type,
143               size_t *fields_offsets,
144               size_t *n_fields_offsets)
145 {
146   dbus_bool_t res = TRUE;
147
148   if (*n_fields_offsets >= DBUS_HEADER_FIELD_LAST)
149     return FALSE;
150
151   if (value != NULL)
152   {
153     res = res && _dbus_string_align_length(str, 8);
154     res = res && append_sized_value(str, field, FIELD_ID_SIZE);
155     res = res && _dbus_string_append_len(str, value, strlen(value)+1);
156     res = res && _dbus_string_append_byte(str, 0); /* variant value-signature separator */
157     res = res && _dbus_string_append_byte(str, type);
158     fields_offsets[(*n_fields_offsets)++] = _dbus_string_get_length(str) - FIRST_GVARIANT_FIELD_OFFSET;
159   }
160   return res;
161 }
162
163 static dbus_bool_t
164 append_field_uint64 (DBusString *str,
165               dbus_uint64_t field,
166               dbus_uint64_t value,
167               size_t *fields_offsets,
168               size_t *n_fields_offsets)
169 {
170   dbus_bool_t res = TRUE;
171
172   if (*n_fields_offsets >= DBUS_HEADER_FIELD_LAST)
173     return FALSE;
174
175   res = res && _dbus_string_align_length(str, 8);
176   res = res && append_sized_value(str, field, FIELD_ID_SIZE);
177   res = res && append_sized_value(str, value, 8);
178   res = res && _dbus_string_append_byte(str, 0); /* variant value-signature separator */
179   res = res && _dbus_string_append_byte(str, DBUS_TYPE_UINT64);
180   fields_offsets[(*n_fields_offsets)++] = _dbus_string_get_length(str) - FIRST_GVARIANT_FIELD_OFFSET;
181   return res;
182 }
183
184 static dbus_bool_t
185 append_field_uint32 (DBusString *str,
186               dbus_uint64_t field,
187               dbus_uint32_t value,
188               size_t *fields_offsets,
189               size_t *n_fields_offsets)
190 {
191   dbus_bool_t res = TRUE;
192
193   if (*n_fields_offsets >= DBUS_HEADER_FIELD_LAST)
194     return FALSE;
195
196   res = res && _dbus_string_align_length(str, 8);
197   res = res && append_sized_value(str, field, FIELD_ID_SIZE);
198   res = res && append_sized_value(str, value, 4);
199   res = res && _dbus_string_append_byte(str, 0); /* variant value-signature separator */
200   res = res && _dbus_string_append_byte(str, DBUS_TYPE_UINT32);
201   fields_offsets[(*n_fields_offsets)++] = _dbus_string_get_length(str) - FIRST_GVARIANT_FIELD_OFFSET;
202   return res;
203 }
204
205 static void
206 _dbus_header_toggle_gvariant (DBusHeader *header, dbus_bool_t gvariant)
207 {
208   header->protocol_version = gvariant ? DBUS_PROTOCOL_VERSION_GVARIANT : DBUS_MAJOR_PROTOCOL_VERSION;
209 }
210
211 static uintptr_t
212 get_next_field_address (const char *array_buffer, size_t offset)
213 {
214   return (uintptr_t)array_buffer + _DBUS_ALIGN_VALUE(offset, 8);
215 }
216
217 static dbus_uint64_t
218 get_field_after (const char *array_buffer, size_t offset)
219 {
220   return *(dbus_uint64_t*)(get_next_field_address(array_buffer, offset));
221 }
222
223 static void
224 _dbus_header_fill_cache (DBusHeader *header,
225                          size_t     *fields_offsets,
226                          size_t      n_fields_offsets)
227 {
228   const char *array_buffer = get_header_const_array (header);
229   size_t i;
230
231   if (get_header_array_size (header) > 0)
232   {
233     header->fields[get_field_after (array_buffer, 0)].value_pos = FIELD_ID_SIZE + FIRST_GVARIANT_FIELD_OFFSET;
234     for (i=0; i < n_fields_offsets-1; i++)
235     {
236       dbus_uint64_t field = get_field_after (array_buffer, fields_offsets[i]);
237       header->fields[field].value_pos = _DBUS_ALIGN_VALUE(fields_offsets[i],8) +
238                                         FIELD_ID_SIZE + FIRST_GVARIANT_FIELD_OFFSET;
239     }
240   }
241 }
242
243 static dbus_bool_t
244 correct_header_padding (DBusHeader *header)
245 {
246   int unpadded_len = _dbus_string_get_length (&header->data);
247   if (!_dbus_string_align_length (&header->data, 8))
248           return FALSE;
249
250   header->padding = _dbus_string_get_length (&header->data) - unpadded_len;
251   return TRUE;
252 }
253
254 dbus_bool_t
255 _dbus_header_gvariant_create (DBusHeader        *header,
256                               int                byte_order,
257                               int                type,
258                               const char        *destination,
259                               const char        *path,
260                               const char        *interface,
261                               const char        *member,
262                               const char        *error_name)
263 {
264   size_t fields_offsets[DBUS_HEADER_FIELD_LAST];
265   size_t n_fields_offsets = 0;
266   dbus_bool_t res = TRUE;
267
268   _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN ||
269                 byte_order == DBUS_BIG_ENDIAN);
270   _dbus_assert (((interface || type != DBUS_MESSAGE_TYPE_SIGNAL) && member) ||
271                 (error_name) ||
272                 !(interface || member || error_name));
273   _dbus_assert (_dbus_string_get_length (&header->data) == 0);
274
275   _dbus_header_toggle_gvariant (header, TRUE);
276
277   res = res && _dbus_string_append_byte (&header->data, byte_order);
278   res = res && _dbus_string_append_byte (&header->data, type);
279   res = res && _dbus_string_append_byte (&header->data, 0);   /* flags */
280   res = res && _dbus_string_append_byte (&header->data, DBUS_PROTOCOL_VERSION_GVARIANT);
281   res = res && append_sized_value (&header->data, 0, sizeof(dbus_uint32_t));    /* reserved */
282   res = res && append_sized_value (&header->data, 0, sizeof(dbus_uint64_t));    /* cookie */
283   /* array of fields */
284   res = res && append_field_string (&header->data, DBUS_HEADER_FIELD_PATH, path, DBUS_TYPE_OBJECT_PATH,
285                       fields_offsets, &n_fields_offsets);
286   res = res && append_field_string (&header->data, DBUS_HEADER_FIELD_DESTINATION, destination, DBUS_TYPE_STRING,
287                       fields_offsets, &n_fields_offsets);
288   res = res && append_field_string (&header->data, DBUS_HEADER_FIELD_INTERFACE, interface, DBUS_TYPE_STRING,
289                       fields_offsets, &n_fields_offsets);
290   res = res && append_field_string (&header->data, DBUS_HEADER_FIELD_MEMBER, member, DBUS_TYPE_STRING,
291                       fields_offsets, &n_fields_offsets);
292   res = res && append_field_string (&header->data, DBUS_HEADER_FIELD_ERROR_NAME, error_name, DBUS_TYPE_STRING,
293                       fields_offsets, &n_fields_offsets);
294   res = res && append_offsets (&header->data, fields_offsets, n_fields_offsets);
295
296   _dbus_header_fill_cache (header, fields_offsets, n_fields_offsets);
297   res = res && correct_header_padding (header);
298
299   return res;
300 }
301
302 static dbus_bool_t
303 marshal_gvariant_string (DBusString    *str,
304                          int            insert_at,
305                          const char    *value,
306                          int           *pos_after,
307                          dbus_bool_t    with_nul)
308 {
309   DBusString value_str;
310   size_t value_len = strlen(value);
311
312   if (with_nul)
313     value_len++;
314
315   _dbus_string_init_const_len (&value_str, value, value_len);
316   if (!_dbus_string_copy_len (&value_str, 0, value_len, str, insert_at))
317   {
318     return FALSE;
319   }
320
321   if (pos_after)
322     *pos_after = insert_at + value_len;
323
324   return TRUE;
325 }
326
327 dbus_bool_t
328 _dbus_marshal_write_gvariant_basic (DBusString *str,
329                                     int         insert_at,
330                                     int         type,
331                                     const void *value,
332                                     int         byte_order,
333                                     int        *pos_after)
334 {
335   const DBusBasicValue *vp;
336   _dbus_assert (dbus_type_is_basic (type));
337
338   vp = value;
339
340   switch (type)
341   {
342     case DBUS_TYPE_STRING:
343     case DBUS_TYPE_OBJECT_PATH:
344     case DBUS_TYPE_SIGNATURE:
345       return marshal_gvariant_string (str, insert_at, vp->str, pos_after, TRUE);
346     case DBUS_TYPE_BOOLEAN:
347       if (pos_after)
348         (*pos_after)++;
349       return _dbus_string_insert_byte (str, insert_at, vp->u32 != FALSE);
350     default:
351       return _dbus_marshal_write_basic (str, insert_at, type, value, byte_order, pos_after);
352   }
353 }
354
355 void
356 _dbus_marshal_read_gvariant_basic (const DBusString *str,
357                                     int               pos,
358                                     int               type,
359                                     void             *value,
360                                     int               byte_order,
361                                     int              *new_pos)
362 {
363   const char *str_data;
364
365   _dbus_assert (dbus_type_is_basic (type));
366
367   str_data = _dbus_string_get_const_data (str);
368   switch (type)
369   {
370     case DBUS_TYPE_STRING:
371     case DBUS_TYPE_OBJECT_PATH:
372     case DBUS_TYPE_SIGNATURE:
373       {
374         volatile char **vp = value;
375         *vp = (char*) str_data + pos;
376         pos += strlen (str_data+pos)+1;
377       }
378       break;
379     case DBUS_TYPE_BOOLEAN:
380       {
381         volatile dbus_bool_t *vp = value;
382         *vp = (dbus_bool_t) _dbus_string_get_byte (str, pos);
383         (pos)++;
384       }
385       break;
386     default:
387       _dbus_marshal_read_basic (str, pos, type, value, byte_order, new_pos);
388       break;
389   }
390
391   if (new_pos)
392     *new_pos = pos;
393 }
394
395 static void
396 get_offsets (const char *buffer, size_t container_size,
397              size_t *fields_offsets, size_t *n_fields_offsets,
398              size_t *offset_size)
399 {
400   *offset_size = bus_gvariant_determine_word_size (container_size, 0);
401
402   if (0 < container_size && 0 < *offset_size)
403   {
404     size_t last_offset_position = container_size - (*offset_size);
405     size_t last_offset = bus_gvariant_read_word_le (buffer + last_offset_position,
406                                                     (*offset_size));
407     size_t i;
408
409     *n_fields_offsets = (container_size - last_offset) / (*offset_size);
410
411     if (*n_fields_offsets == 0 || *n_fields_offsets >= DBUS_HEADER_FIELD_LAST)
412       return;
413
414     fields_offsets[(*n_fields_offsets)-1] = last_offset;
415     for (i = 0; i < (*n_fields_offsets)-1; i++)
416     {
417       fields_offsets[i] = bus_gvariant_read_word_le (buffer + last_offset + i*(*offset_size),
418                                                      (*offset_size));
419     }
420   }
421 }
422
423 static int
424 find_field (int field, const char *array_buffer, size_t *fields_offsets, size_t n_fields_offsets,
425             size_t *field_offset)
426 {
427     /* last_offset points to the offsets array, beyond the last element of the array container */
428     size_t last_offset = fields_offsets[n_fields_offsets-1];
429     size_t i = 0;
430     size_t next_offset = 0;
431
432     while ( next_offset < last_offset &&
433             get_field_after (array_buffer, next_offset) != (dbus_uint64_t) field)
434     {
435       next_offset = fields_offsets[i];
436       i++;
437     }
438     if (next_offset < last_offset)
439     {
440       *field_offset = next_offset;
441       return i;
442     }
443     return -1;
444 }
445
446 dbus_bool_t
447 _dbus_header_gvariant_delete_field (DBusHeader *header,
448                                     int field)
449 {
450   size_t fields_offsets[DBUS_HEADER_FIELD_LAST];
451   size_t n_fields_offsets = 0;
452   size_t offset_size = 0;
453   const char *array_buffer;
454
455   _dbus_assert(field <= DBUS_HEADER_FIELD_LAST);
456
457   array_buffer = get_header_const_array (header);
458
459   get_offsets (array_buffer,
460                get_header_array_size (header),
461                fields_offsets, &n_fields_offsets, &offset_size );
462
463   if (0 < n_fields_offsets)
464   {
465     /* check if the field is already in the header */
466     size_t field_offset;
467     int field_index = find_field (field, array_buffer, fields_offsets, n_fields_offsets, &field_offset);
468
469     /* prepare for changing - remove array offsets and offsets */
470     _dbus_string_shorten (&header->data, n_fields_offsets*offset_size + header->padding);
471
472     if (field_index >= 0)
473     {
474       /* field exists */
475       size_t field_len = 0;
476       size_t field_start = 0;
477       /* let's remove aligned block of the field, along with padding */
478       if (field_index == 0)
479       {
480         field_len = _DBUS_ALIGN_VALUE (fields_offsets[0],8);
481       }
482       else
483       {
484         field_len = _DBUS_ALIGN_VALUE (fields_offsets[field_index],8) -
485                     _DBUS_ALIGN_VALUE (fields_offsets[field_index-1],8);
486       }
487
488       field_start = FIRST_GVARIANT_FIELD_OFFSET + _DBUS_ALIGN_VALUE (field_offset, 8);
489
490       /* if this is the last field, then there is no padding at the end */
491       if (field_start + field_len > (size_t)_dbus_string_get_length (&header->data))
492       {
493         field_len = _dbus_string_get_length (&header->data) - field_start;
494       }
495
496       /* remove the field */
497       _dbus_string_delete (&header->data, field_start, field_len);
498       header->fields[field].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT;
499       /* and update offsets */
500       for (; (size_t)field_index < n_fields_offsets-1; field_index++)
501       {
502         fields_offsets[field_index] = fields_offsets[field_index+1]-field_len;
503       }
504       n_fields_offsets--;
505
506       /* remove padding from now-last field, if there is still any field */
507       if (n_fields_offsets > 0)
508           _dbus_string_shorten (&header->data,
509                                 _dbus_string_get_length(&header->data) -
510                                    (FIRST_GVARIANT_FIELD_OFFSET + fields_offsets[n_fields_offsets-1]));
511
512       header->padding = 0;
513     }
514   }
515
516   /* It seems impossible for append_offsets() and correct_header_padding() to fail,
517      because space for offsets was already allocated */
518   if (!append_offsets(&header->data, fields_offsets, n_fields_offsets))
519     return FALSE;
520   _dbus_header_fill_cache (header, fields_offsets, n_fields_offsets);
521   if (!correct_header_padding (header))
522     return FALSE;
523
524   return TRUE;
525 }
526
527 dbus_bool_t
528 _dbus_header_set_field_basic_gvariant (DBusHeader       *header,
529                               int               field,
530                               int               type,
531                               const void       *value)
532 {
533   size_t fields_offsets[DBUS_HEADER_FIELD_LAST];
534   size_t n_fields_offsets = 0;
535   dbus_bool_t result = TRUE;
536   const DBusBasicValue *vp = value;
537   size_t offset_size = 0;
538   const char *array_buffer;
539
540   _dbus_assert(field != DBUS_HEADER_FIELD_INVALID);
541   _dbus_assert(field <= DBUS_HEADER_FIELD_LAST);
542
543   array_buffer = get_header_const_array (header);
544
545   result = result && _dbus_header_gvariant_delete_field (header, field);
546
547   /* now, we are sure that there is no such field (anymore) - so, simply append */
548
549   get_offsets (array_buffer,
550                get_header_array_size (header),
551                fields_offsets, &n_fields_offsets, &offset_size );
552
553   /* prepare for changing - remove array offsets and padding */
554   _dbus_string_shorten (&header->data, n_fields_offsets*offset_size + header->padding);
555
556   switch (type)
557   {
558     case DBUS_TYPE_STRING:
559     case DBUS_TYPE_OBJECT_PATH:
560     case DBUS_TYPE_SIGNATURE:
561       result = result && append_field_string (&header->data, field, vp->str, type,
562           fields_offsets, &n_fields_offsets);
563       break;
564     case DBUS_TYPE_UINT32:
565       result = result && append_field_uint32 (&header->data, field, vp->u32,
566           fields_offsets, &n_fields_offsets);
567       break;
568     case DBUS_TYPE_UINT64:
569       append_field_uint64 (&header->data, field, vp->u64,
570           fields_offsets, &n_fields_offsets);
571       result = TRUE;
572       break;
573     default:
574       _dbus_assert_not_reached("Not a basic type");
575       result = FALSE;
576       break;
577   }
578
579   result = result && append_offsets(&header->data, fields_offsets, n_fields_offsets);
580   _dbus_header_fill_cache (header, fields_offsets, n_fields_offsets);
581   result = result && correct_header_padding (header);
582
583   return result;
584 }
585
586 dbus_bool_t
587 _dbus_header_get_field_basic_gvariant (DBusHeader    *header,
588                                        int            field,
589                                        int            type,
590                                        void          *value)
591 {
592   size_t fields_offsets[DBUS_HEADER_FIELD_LAST];
593   size_t n_fields_offsets = 0;
594   dbus_bool_t result = FALSE;
595   DBusBasicValue *vp = value;
596   size_t offset_size = 0;
597   const char *array_buffer;
598
599   _dbus_assert(field != DBUS_HEADER_FIELD_INVALID);
600   _dbus_assert(field <= DBUS_HEADER_FIELD_LAST);
601
602   array_buffer = get_header_const_array (header);
603
604   get_offsets( array_buffer,
605                get_header_array_size (header),
606                fields_offsets, &n_fields_offsets, &offset_size );
607
608   if (0 < n_fields_offsets)
609   {
610     /* check if the field is already in the header */
611     size_t field_offset;
612     int field_index = find_field (field, array_buffer, fields_offsets, n_fields_offsets, &field_offset);
613     if (0 <= field_index)
614     {
615       /* field found, get value */
616       const void *field_begin = array_buffer + _DBUS_ALIGN_VALUE(field_offset,8) + FIELD_ID_SIZE;
617       dbus_uint32_t byte_order = _dbus_header_get_byte_order (header);
618
619       switch (type)
620       {
621         case DBUS_TYPE_STRING:
622         case DBUS_TYPE_OBJECT_PATH:
623         case DBUS_TYPE_SIGNATURE:
624           {
625             vp->str = (char *)field_begin;
626           }
627           break;
628         case DBUS_TYPE_UINT32:
629           {
630             vp->u32 = *(const dbus_uint32_t *)field_begin;
631             if (byte_order != DBUS_COMPILER_BYTE_ORDER)
632               vp->u32 = DBUS_UINT32_SWAP_LE_BE (vp->u32);
633           }
634           break;
635         case DBUS_TYPE_UINT64:
636           {
637             vp->u64 = *(const dbus_uint64_t *)field_begin;
638             if (byte_order != DBUS_COMPILER_BYTE_ORDER)
639               vp->u64 = DBUS_UINT64_SWAP_LE_BE (vp->u64);
640           }
641           break;
642         default:
643           _dbus_assert_not_reached("Not a basic type");
644           break;
645       }
646
647       result = TRUE;
648     }
649   }
650   return result;
651 }
652
653 void
654 _dbus_marshal_skip_gvariant_basic (const DBusString *str,
655                                    int               type,
656                                    int               byte_order,
657                                    int              *pos)
658 {
659   switch (type)
660   {
661     case DBUS_TYPE_STRING:
662     case DBUS_TYPE_OBJECT_PATH:
663     case DBUS_TYPE_SIGNATURE:
664       /* FIXME - this will require redesign... size should come from upper container */
665       *pos += strlen (_dbus_string_get_const_data (str) + *pos) + 1; /* length plus nul */
666       break;
667     case DBUS_TYPE_BOOLEAN:
668       (*pos)++;
669       break;
670     default:
671       _dbus_marshal_skip_basic (str, type, byte_order, pos);
672       break;
673   }
674 }
675
676 dbus_bool_t
677 _dbus_header_load_gvariant (DBusHeader     *header,
678                             DBusValidity   *validity)
679 {
680   size_t fields_offsets[DBUS_HEADER_FIELD_LAST];
681   size_t n_fields_offsets = 0;
682   size_t offset_size = 0;
683   const char *array_buffer = get_header_const_array (header);
684
685   get_offsets( array_buffer,
686                get_header_array_size (header),
687                fields_offsets, &n_fields_offsets, &offset_size );
688
689   _dbus_header_fill_cache (header, fields_offsets, n_fields_offsets);
690   return TRUE;
691 }
692
693 dbus_bool_t
694 _dbus_gvariant_raw_get_lengths (const DBusString *str,
695                                 dbus_uint32_t    *fields_array_len_unsigned,
696                                 dbus_uint32_t    *body_len_unsigned,
697                                 DBusValidity     *validity)
698 {
699   size_t message_len = _dbus_string_get_length (str);
700   size_t body_offsets_size = bus_gvariant_determine_word_size (message_len, 0);
701   const char *message_ptr = _dbus_string_get_const_data (str);
702   /* so, the offset of end of fields is written at offset str->len - body_offsets_size */
703   size_t end_of_fields = bus_gvariant_read_word_le (message_ptr + message_len - body_offsets_size,
704                                                     body_offsets_size);
705   *fields_array_len_unsigned = end_of_fields - FIRST_GVARIANT_FIELD_OFFSET;
706
707   *body_len_unsigned = message_len - _DBUS_ALIGN_VALUE (end_of_fields, 8);
708   return TRUE;
709 }
710
711 DBusValidity
712 _dbus_validate_gvariant_body_with_reason (const DBusString *expected_signature,
713                                           int               expected_signature_start,
714                                           int               byte_order,
715                                           int              *bytes_remaining,
716                                           const DBusString *value_str,
717                                           int               value_pos,
718                                           int               len)
719 {
720   /* FIXME stub */
721   if (bytes_remaining)
722     *bytes_remaining = 0;
723   return DBUS_VALID;
724 }
725
726 dbus_bool_t
727 _dbus_message_gvariant_get_signature (DBusMessage       *message,
728                                       const DBusString **type_str_p,
729                                       int               *type_pos_p,
730                                       int               *type_str_len)
731 {
732   size_t body_len = _dbus_string_get_length (&message->body);
733   size_t message_len = _dbus_string_get_length (&message->header.data) + body_len;
734   size_t body_offsets_size = bus_gvariant_determine_word_size (message_len, 0);
735   const char *body_ptr = _dbus_string_get_const_data (&message->body);
736   const char *sig_end_ptr = body_ptr + body_len - body_offsets_size;
737   const char *sig_ptr = sig_end_ptr - 1;
738
739   while (sig_ptr >= body_ptr && (*sig_ptr) != 0)
740   {
741     sig_ptr--;
742   }
743
744   if (sig_ptr < body_ptr)
745     return FALSE;
746
747   if (type_str_p != NULL)
748     *type_str_p = &message->body;
749   *type_pos_p = sig_ptr - body_ptr + 1;
750   *type_str_len = sig_end_ptr - sig_ptr - 1;
751
752   return TRUE;
753 }
754
755 dbus_bool_t
756 _dbus_message_append_body_offset (DBusMessage *message)
757 {
758   size_t body_len = _dbus_string_get_length (&message->body);
759   size_t end_of_fields_offset = _dbus_string_get_length (&message->header.data) - message->header.padding;
760   size_t message_len = _dbus_string_get_length (&message->header.data) + body_len;
761   size_t body_offsets_size = bus_gvariant_determine_word_size (message_len, 1);
762
763   return append_sized_value (&message->body, end_of_fields_offset, body_offsets_size);
764 }
765
766 dbus_bool_t
767 _dbus_message_gvariant_add_signature (DBusMessage       *message,
768                                       const DBusString  *type_str)
769 {
770   dbus_bool_t res = _dbus_string_append_byte (&message->body, 0);
771   res = res && _dbus_string_append_byte (&message->body, '(');
772   res = res && marshal_gvariant_string (&message->body, _dbus_string_get_length (&message->body),
773                            _dbus_string_get_const_data (type_str), NULL, FALSE);
774   res = res && _dbus_string_append_byte (&message->body, ')');
775   return res;
776 }
777
778 dbus_bool_t
779 _dbus_message_gvariant_remove_body_offset (DBusMessage *message)
780 {
781   size_t offset_size = bus_gvariant_determine_word_size (_dbus_string_get_length (&message->header.data) +
782                                                             _dbus_string_get_length (&message->body),
783                                                          0);
784   _dbus_string_shorten (&message->body, offset_size);
785   return TRUE;
786 }
787
788 dbus_bool_t
789 _dbus_message_finalize_gvariant (DBusMessage *message, dbus_bool_t remove_signature_from_header)
790 {
791   DBusString str;
792   const DBusString *type_str;
793   int type_pos;
794   dbus_bool_t fieldSignaturePresent;
795   dbus_bool_t res = TRUE;
796
797   _dbus_assert (!message->locked);
798
799   if (message->header.protocol_version != DBUS_PROTOCOL_VERSION_GVARIANT)
800     return TRUE;
801
802   fieldSignaturePresent = _dbus_header_get_field_raw (&message->header,
803                                                       DBUS_HEADER_FIELD_SIGNATURE,
804                                                       &type_str,
805                                                       &type_pos);
806   if (fieldSignaturePresent)
807   {
808     /* if there is signature field, then we need to move this signature to body,
809      * and delete the field
810      */
811     const char *sig_ptr = _dbus_string_get_const_data (type_str) + type_pos;
812     int alignment;
813     int fixed_size;
814
815     _dbus_string_init_const (&str, sig_ptr);
816
817     /* There is a special case for structs of fixed size. They need to have size
818      * which is a multiply of required alignment.
819      * The root struct must be finalized just before adding signature and locking the message.
820      * Luckily, fixed size means that there are no offsets in the message body.
821      */
822     fixed_size = _dbus_reader_get_signature_fixed_size (type_str, &type_pos, &alignment, 1);
823     if (fixed_size > 0)
824     {
825       int current_length = _dbus_string_get_length (&message->body);
826       int diff = _DBUS_ALIGN_VALUE (current_length, alignment) - current_length;
827       if (diff > 0)
828         _dbus_string_insert_bytes (&message->body, current_length, diff, 0);
829     }
830   }
831   else
832   {
833     /* If there is no signature field, then the body is empty.
834      * However, we need to add signature anyway, because body is a variant.
835      */
836     _dbus_string_init_const (&str, "");
837     type_str = &str;
838     type_pos = 0;
839     /* Let's set the body also */
840     res = res && _dbus_string_set_length (&message->body, 0);
841     res = res && _dbus_string_append_byte (&message->body, 0);
842   }
843
844   res = res && _dbus_message_gvariant_add_signature (message, &str);
845
846   if (res && fieldSignaturePresent && remove_signature_from_header)
847     res = res && _dbus_header_gvariant_delete_field (&message->header, DBUS_HEADER_FIELD_SIGNATURE);
848
849   res = res && _dbus_message_append_body_offset (message);
850
851   return res;
852 }
853
854 /* returns length of the body inside the outermost variant
855  * that is, without offset and signature from the end of messages
856  */
857 static size_t
858 _dbus_message_gvariant_get_body_length (DBusMessage *message)
859 {
860   size_t body_len = _dbus_string_get_length (&message->body);
861   size_t message_len = body_len + _dbus_string_get_length (&message->header.data);
862   size_t offset_size = bus_gvariant_determine_word_size (message_len, 0);
863
864   if (body_len <= offset_size)
865           return 0;
866
867   body_len -= offset_size;
868
869   /* searching for variant's NULL byte */
870   while (body_len > 0 && _dbus_string_get_byte (&message->body, body_len) != 0)
871     body_len--;
872
873   return body_len;
874 }
875
876 static inline int
877 get_max (int a, int b)
878 {
879   return (a>b) ? a : b;
880 }
881
882 static int
883 update_size (int current_size, int size_of_element, int *alignment, int new_alignment)
884 {
885   *alignment = get_max (*alignment, new_alignment);
886   current_size = _DBUS_ALIGN_VALUE (current_size, *alignment);
887   return current_size + size_of_element;
888 }
889
890 static int
891 _dbus_reader_get_signature_fixed_size (const DBusString *signature, int *pos, int *alignment,
892                                        int depth)
893 {
894   int res = 0;
895   int current_alignment = 1;
896   dbus_bool_t variable = FALSE;
897   int length = _dbus_string_get_length (signature);
898
899   char c = _dbus_string_get_byte (signature, *pos);
900   if (c == DBUS_STRUCT_BEGIN_CHAR || c == DBUS_DICT_ENTRY_BEGIN_CHAR)
901   {
902     depth++;
903     (*pos)++;
904   }
905
906   do {
907     switch (_dbus_string_get_byte (signature, *pos))
908     {
909       case DBUS_TYPE_BYTE:
910       case DBUS_TYPE_BOOLEAN:
911         res += 1;
912         break;
913       case DBUS_TYPE_INT16:
914       case DBUS_TYPE_UINT16:
915         res = update_size (res, 2, &current_alignment, 2);
916         break;
917       case DBUS_TYPE_INT32:
918       case DBUS_TYPE_UINT32:
919       case DBUS_TYPE_UNIX_FD:
920         res = update_size (res, 4, &current_alignment, 4);
921         break;
922       case DBUS_TYPE_INT64:
923       case DBUS_TYPE_UINT64:
924       case DBUS_TYPE_DOUBLE:
925         res = update_size (res, 8, &current_alignment, 8);
926         break;
927       case DBUS_STRUCT_END_CHAR:
928       case DBUS_DICT_ENTRY_END_CHAR:
929         depth--;
930                 /* For fixed-size structs we need to account padding.
931                  */
932                 if (!variable)
933                         res = _DBUS_ALIGN_VALUE (res, current_alignment);
934         break;
935       case DBUS_STRUCT_BEGIN_CHAR:
936       case DBUS_DICT_ENTRY_BEGIN_CHAR:
937         {
938           int alignment_recursive;
939           int res_recursive = _dbus_reader_get_signature_fixed_size (signature, pos, &alignment_recursive, 0);
940           if (res_recursive == 0)
941             variable = TRUE;   /* variable size detected */
942
943           /* we need to update at least alignment */
944           res = update_size (res, res_recursive, &current_alignment, alignment_recursive);
945         }
946         break;
947       case DBUS_TYPE_VARIANT:
948         current_alignment = 8;
949         variable = TRUE;
950         break;
951       case DBUS_TYPE_ARRAY:
952         {
953           int alignment_recursive;
954           int recursive_pos = *pos + 1;
955           int res_recursive = _dbus_reader_get_signature_fixed_size (signature, &recursive_pos, &alignment_recursive, 0);
956
957           variable = TRUE;       /* variable size detected */
958
959           /* we need to update alignment */
960           res = update_size (res, res_recursive, &current_alignment, alignment_recursive);
961
962           /* and update position */
963           *pos = recursive_pos;
964         }
965         break;
966           case DBUS_TYPE_INVALID:
967                 depth = 0;
968                 break;
969       default:
970         variable = TRUE;       /* variable size detected */
971     }
972     (*pos)++;
973   } while (depth > 0 && *pos < length);
974
975   /* we want to point it to the last character, to allow upper instance to skip it */
976   (*pos)--;
977
978   if (alignment != NULL)
979     *alignment = current_alignment;
980
981   return variable ? 0 : res;
982 }
983
984 int
985 _dbus_reader_get_type_fixed_size (DBusTypeReader *reader, int *alignment)
986 {
987   int pos = reader->type_pos;
988   return _dbus_reader_get_signature_fixed_size (reader->type_str, &pos, alignment, 0);
989 }
990
991 int
992 _dbus_type_gvariant_get_fixed_size (const DBusString *type_str, int type_pos, int *alignment)
993 {
994   return _dbus_reader_get_signature_fixed_size (type_str, &type_pos, alignment, 0);
995 }
996
997 static int
998 get_current_type_types_only (const DBusTypeReader *reader)
999 {
1000   int t;
1001   if (reader->finished)
1002     t = DBUS_TYPE_INVALID;
1003   else
1004     t = _dbus_first_type_in_signature (reader->type_str,
1005                                        reader->type_pos);
1006
1007   return t;
1008 }
1009
1010 /* This is for structs and dict entries.
1011  * Counts variable elements inside a container.
1012  * This is equal to number of offsets embedded into the container.
1013  */
1014 int
1015 _dbus_reader_count_offsets (const DBusTypeReader *reader)
1016 {
1017   DBusTypeReader r;
1018   int variables = 0;
1019   dbus_bool_t prev_is_variable = FALSE;
1020   int current_type;
1021   int ending_char = 0;
1022
1023   _dbus_type_reader_init_types_only (&r,
1024                                      reader->type_str,
1025                                      reader->type_pos);
1026   r.gvariant = TRUE;
1027   r.klass = reader->klass;
1028
1029   /* In case we are in root container, we have signature without external parentheses.
1030    *  We go until ending nul, starting with position 0.
1031    * Otherwise, we are in a container, so let's check what kind of container it is,
1032    *  and set proper terminating character.
1033    */
1034   if (r.type_pos > 0)
1035     {
1036       /* Check what container we're in */
1037       switch (_dbus_string_get_byte (r.type_str, r.type_pos-1))
1038         {
1039           case DBUS_STRUCT_BEGIN_CHAR:
1040             ending_char = DBUS_STRUCT_END_CHAR;
1041             break;
1042           case DBUS_DICT_ENTRY_BEGIN_CHAR:
1043             ending_char = DBUS_DICT_ENTRY_END_CHAR;
1044             break;
1045           default:
1046             _dbus_assert_not_reached ("function must be called inside structs or dict entries");
1047             break;
1048         }
1049     }
1050   r.finished = (_dbus_string_get_byte (r.type_str, r.type_pos) == ending_char);
1051
1052   while ((current_type = get_current_type_types_only (&r)) != DBUS_TYPE_INVALID)
1053   {
1054     int size = _dbus_reader_get_type_fixed_size (&r, NULL);
1055     if (prev_is_variable)
1056       variables++;
1057     prev_is_variable = (size == 0);
1058     _dbus_type_signature_next (_dbus_string_get_const_data(r.type_str), &r.type_pos);
1059     r.finished = (_dbus_string_get_byte (r.type_str, r.type_pos) == ending_char);
1060   }
1061   return variables;
1062 }
1063
1064 size_t
1065 _dbus_reader_get_offset_of_end_of_variable (DBusTypeReader *reader)
1066 {
1067   if (reader->is_variant)
1068   {
1069     /* variant has its end set to the separating 0 */
1070     return reader->value_end;
1071   }
1072   else
1073   {
1074     const char *buffer = _dbus_string_get_const_data (reader->value_str) + reader->value_start;
1075     size_t container_size = reader->value_end - reader->value_start;
1076     size_t offset_size = bus_gvariant_determine_word_size (container_size, 0);
1077     int index_from_back = reader->offsets_from_back ?
1078                           reader->variable_index :
1079                           reader->n_offsets - 1 - reader->variable_index;
1080
1081     if (0 < container_size && 0 <= index_from_back)
1082     {
1083       size_t required_offset_position = container_size - (index_from_back+1)*offset_size;
1084       if (index_from_back < reader->n_offsets)
1085         return reader->value_start +
1086                bus_gvariant_read_word_le (buffer + required_offset_position,
1087                                           offset_size);
1088       else if (reader->offsets_from_back)
1089         return reader->value_start +
1090                container_size - (reader->n_offsets * offset_size); /* this is end of internal container */
1091     }
1092   }
1093
1094   return reader->value_start;
1095 }
1096
1097 int
1098 _dbus_reader_count_array_elems (const DBusTypeReader *reader)
1099 {
1100 /* To count the offsets we need to have offsets size and
1101  * the start and end of the offsets. The start of the offsets
1102  * is computed from the value of the last offset.
1103  */
1104   const char *buffer = _dbus_string_get_const_data (reader->value_str) + reader->value_start;
1105   size_t container_size = reader->value_end - reader->value_start;
1106   size_t offset_size = bus_gvariant_determine_word_size (container_size, 0);
1107   size_t last_offset = container_size;  /* this will give 0 if container is smaller than a single offset */
1108   if (container_size > offset_size)
1109           last_offset = bus_gvariant_read_word_le (buffer + container_size - offset_size, offset_size);
1110   return (container_size - last_offset) / offset_size;
1111 }
1112
1113 static dbus_bool_t
1114 write_offset (DBusString *offsets,
1115               size_t offset,
1116               size_t offset_size,
1117               int insert_at)
1118 {
1119   DBusString str;
1120   dbus_bool_t res = _dbus_string_init_preallocated (&str, offset_size);
1121   res = res && append_sized_value (&str, offset, offset_size);
1122   res = res && _dbus_string_copy_len (&str, 0, offset_size, offsets, insert_at);
1123   _dbus_string_free (&str);
1124   return res;
1125 }
1126
1127 static dbus_bool_t
1128 prepend_offset (DBusString *offsets,
1129                size_t offset,
1130                size_t offset_size)
1131 {
1132   return write_offset (offsets, offset, offset_size, 0);
1133 }
1134
1135 static dbus_bool_t
1136 append_offset (DBusString *offsets,
1137                size_t offset,
1138                size_t offset_size)
1139 {
1140   return write_offset (offsets, offset, offset_size, _dbus_string_get_length(offsets));
1141 }
1142
1143 static dbus_bool_t
1144 convert_offsets (DBusString *offsets,
1145                  size_t old_offsets_size,
1146                  size_t new_offsets_size)
1147 {
1148   char *old_offsets = NULL;
1149   size_t n_offsets = _dbus_string_get_length (offsets) / old_offsets_size;
1150   dbus_bool_t result = _dbus_string_steal_data (offsets, &old_offsets);
1151   size_t i;
1152
1153   for (i = 0; i < n_offsets && result; i++)
1154   {
1155     size_t offset = bus_gvariant_read_word_le (old_offsets + i*old_offsets_size, old_offsets_size);
1156     result = result && append_sized_value (offsets, offset, new_offsets_size);
1157   }
1158
1159   dbus_free (old_offsets);
1160
1161   return result;
1162 }
1163
1164 static size_t
1165 get_offsets_count (DBusString *offsets, size_t offsets_size)
1166 {
1167   return _dbus_string_get_length (offsets) / offsets_size;
1168 }
1169
1170 static dbus_bool_t
1171 check_offsets_for_adding (DBusTypeWriter *writer)
1172 {
1173   size_t container_size = writer->value_pos - writer->value_start;
1174   size_t n_offsets = get_offsets_count (writer->offsets,
1175                                         writer->offsets_size);
1176   size_t offsets_size = bus_gvariant_determine_word_size (container_size, n_offsets + 1);
1177   if (offsets_size != writer->offsets_size)
1178   {
1179     if (!convert_offsets (writer->offsets, writer->offsets_size, offsets_size))
1180       return FALSE;
1181     writer->offsets_size = offsets_size;
1182   }
1183   return TRUE;
1184 }
1185
1186 static dbus_bool_t
1187 convert_offsets_in_body (DBusTypeWriter *writer,
1188                          size_t new_offsets_size)
1189 {
1190   DBusString offsets;
1191   size_t n_offsets;
1192   size_t i;
1193   dbus_bool_t result = _dbus_string_init (&offsets);
1194   char *old_offsets;
1195
1196   result = result && _dbus_string_move (writer->value_str, writer->value_pos, &offsets, 0);
1197   n_offsets = _dbus_string_get_length (&offsets) / writer->offsets_size;
1198   old_offsets = _dbus_string_get_data (&offsets);
1199
1200   for (i = 0; i < n_offsets && result; i++)
1201   {
1202     size_t offset = bus_gvariant_read_word_le (old_offsets + i*writer->offsets_size, writer->offsets_size);
1203     result = result && append_sized_value (writer->value_str, offset, new_offsets_size);
1204   }
1205
1206   _dbus_string_free (&offsets);
1207   return result;
1208 }
1209
1210 static dbus_bool_t
1211 check_offsets_in_body_for_adding (DBusTypeWriter *writer)
1212 {
1213   size_t container_size = writer->value_pos - writer->value_start;
1214   size_t n_offsets = (_dbus_string_get_length (writer->value_str) - writer->value_pos) / writer->offsets_size;
1215   size_t offsets_size = bus_gvariant_determine_word_size (container_size, n_offsets + 1);
1216   if (offsets_size != writer->offsets_size)
1217   {
1218     if (!convert_offsets_in_body (writer, offsets_size))
1219       return FALSE;
1220     writer->offsets_size = offsets_size;
1221   }
1222   return TRUE;
1223 }
1224
1225 static dbus_bool_t
1226 _dbus_writer_gvariant_add_offset_with_variability (DBusTypeWriter *writer,
1227                                                    dbus_bool_t fixed)
1228 {
1229   writer->is_fixed = writer->is_fixed && fixed;
1230
1231   if (writer->body_container)
1232   {
1233     check_offsets_in_body_for_adding (writer);
1234
1235     if (*writer->u.root.last_offset != GVARIANT_LAST_OFFSET_NOT_SET)
1236     {
1237       write_offset (writer->value_str,
1238                     *writer->u.root.last_offset,
1239                     writer->offsets_size,
1240                     writer->value_pos);
1241     }
1242     if (!fixed)
1243       *writer->u.root.last_offset = writer->value_pos - writer->value_start;
1244     else
1245       *writer->u.root.last_offset = GVARIANT_LAST_OFFSET_NOT_SET;
1246   }
1247   else if (DBUS_TYPE_STRUCT == writer->container_type ||
1248            DBUS_TYPE_DICT_ENTRY == writer->container_type)
1249   {
1250     check_offsets_for_adding (writer);
1251
1252     if (writer->u.struct_or_dict.last_offset != GVARIANT_LAST_OFFSET_NOT_SET)
1253     {
1254       prepend_offset (writer->offsets,
1255                       writer->u.struct_or_dict.last_offset,
1256                       writer->offsets_size);
1257     }
1258     if (!fixed)
1259       writer->u.struct_or_dict.last_offset = writer->value_pos - writer->value_start;
1260     else
1261       writer->u.struct_or_dict.last_offset = GVARIANT_LAST_OFFSET_NOT_SET;
1262   }
1263   else if (DBUS_TYPE_ARRAY == writer->container_type)
1264   {
1265     if (writer->offsets_size > 0)
1266     {
1267       check_offsets_for_adding (writer);
1268
1269       if (!append_offset (writer->offsets,
1270                      writer->value_pos - writer->value_start,
1271                      writer->offsets_size))
1272               return FALSE;
1273     }
1274   }
1275   return TRUE;
1276 }
1277
1278 static dbus_bool_t
1279 _dbus_writer_gvariant_add_offset (DBusTypeWriter *writer,
1280                                   int type)
1281 {
1282   return _dbus_writer_gvariant_add_offset_with_variability (writer, dbus_type_is_fixed (type));
1283 }
1284
1285 /* this function gets only known alignments - other are 1 */
1286 static int
1287 get_alignment (int type)
1288 {
1289   switch (type)
1290   {
1291       case DBUS_TYPE_INT16:
1292       case DBUS_TYPE_UINT16:
1293         return 2;
1294       case DBUS_TYPE_INT32:
1295       case DBUS_TYPE_UINT32:
1296       case DBUS_TYPE_UNIX_FD:
1297         return 4;
1298       case DBUS_TYPE_INT64:
1299       case DBUS_TYPE_UINT64:
1300       case DBUS_TYPE_DOUBLE:
1301       case DBUS_TYPE_VARIANT:
1302         return 8;
1303       default:
1304         break;
1305   }
1306   return 1;
1307 }
1308
1309 static dbus_bool_t
1310 fix_struct_alignment_value (DBusTypeWriter *writer, int alignment)
1311 {
1312   dbus_bool_t result = TRUE;
1313   int old_alignment = writer->alignment;
1314   if (old_alignment < alignment)
1315   {
1316     int diff = _DBUS_ALIGN_VALUE (writer->value_start, alignment) - writer->value_start;
1317     result = _dbus_string_insert_bytes (writer->value_str, writer->value_start, diff, 0);
1318     writer->value_start += diff;
1319     writer->value_pos += diff;
1320     writer->alignment = alignment;
1321   }
1322   return result;
1323 }
1324
1325 static dbus_bool_t
1326 fix_struct_alignment (DBusTypeWriter *writer, int type)
1327 {
1328   return fix_struct_alignment_value (writer, get_alignment (type));
1329 }
1330
1331 static void
1332 update_root_last_pos (DBusTypeWriter *writer)
1333 {
1334   if (writer->body_container)
1335     *writer->u.root.last_pos = writer->value_pos;
1336 }
1337
1338 dbus_bool_t
1339 _dbus_type_writer_gvariant_write_basic_no_typecode (DBusTypeWriter *writer,
1340                                                     int             type,
1341                                                     const void     *value)
1342 {
1343   dbus_bool_t result = TRUE;
1344
1345   if (writer->container_type == DBUS_TYPE_STRUCT || writer->container_type == DBUS_TYPE_DICT_ENTRY)
1346     result = fix_struct_alignment (writer, type);
1347
1348   result = result && _dbus_marshal_write_gvariant_basic (writer->value_str,
1349                                                          writer->value_pos,
1350                                                          type,
1351                                                          value,
1352                                                          writer->byte_order,
1353                                                          &writer->value_pos);
1354
1355   update_root_last_pos (writer);
1356
1357   result = result && _dbus_writer_gvariant_add_offset (writer, type);
1358   return result;
1359 }
1360
1361 static dbus_bool_t
1362 write_offsets (DBusString *dest, size_t insert_at, DBusString *offsets)
1363 {
1364   return _dbus_string_copy (offsets, 0, dest, insert_at);
1365 }
1366
1367 dbus_bool_t
1368 _dbus_writer_unrecurse_gvariant_write (DBusTypeWriter *writer,
1369                                        DBusTypeWriter *sub)
1370 {
1371   dbus_bool_t result = TRUE;
1372
1373   if (writer->alignment < sub->alignment)
1374     writer->alignment = sub->alignment;
1375
1376   switch (sub->container_type) {
1377     case DBUS_TYPE_STRUCT:
1378     case DBUS_TYPE_DICT_ENTRY:
1379     {
1380       int diff;
1381       int sub_len;
1382
1383       if (NULL != sub->offsets)
1384       {
1385         write_offsets (sub->value_str, sub->value_pos, sub->offsets);
1386
1387         _dbus_string_free (sub->offsets);
1388         dbus_free (sub->offsets);
1389       }
1390
1391       diff = _DBUS_ALIGN_VALUE (writer->value_pos, sub->alignment) - writer->value_pos;
1392
1393       result = _dbus_string_insert_bytes (writer->value_str, writer->value_pos, diff, 0);
1394       writer->value_pos += diff;
1395       sub_len = _dbus_string_get_length (sub->value_str);
1396       result = result && _dbus_string_copy_len (sub->value_str, 0,
1397                                                 sub_len,
1398                                                 writer->value_str,
1399                                                 writer->value_pos);
1400       writer->value_pos += sub_len;
1401
1402       /* Structs may have padding if they are fixed-size structs.
1403        * This is because of requirement that struct size must be a multiply
1404        * of required alignment.
1405        */
1406       if (sub->is_fixed)
1407       {
1408         diff = _DBUS_ALIGN_VALUE (sub_len, sub->alignment) - sub_len;
1409         result = result && _dbus_string_insert_bytes (writer->value_str, writer->value_pos, diff, 0);
1410         writer->value_pos += diff;
1411       }
1412
1413       _dbus_string_free (sub->value_str);
1414       dbus_free (sub->value_str);
1415
1416       break;
1417     }
1418     case DBUS_TYPE_VARIANT:
1419     {
1420       int sub_type_len;
1421
1422       /* write separating nul byte */
1423       result = _dbus_string_insert_byte (sub->value_str, sub->value_pos, 0);
1424       sub->value_pos += 1;
1425
1426       /* write signature */
1427       sub_type_len = _dbus_string_get_length (sub->type_str);
1428       result = result && _dbus_string_copy_len (sub->type_str, 0,
1429                                                 sub_type_len,
1430                                                 sub->value_str,
1431                                                 sub->value_pos);
1432       sub->value_pos += sub_type_len;
1433
1434       /* free type string allocated in writer_recurse_variant() */
1435       _dbus_string_free (sub->type_str);
1436       dbus_free (sub->type_str);
1437
1438       /* update parent's string pointer */
1439       writer->value_pos = sub->value_pos;
1440
1441       break;
1442     }
1443     case DBUS_TYPE_ARRAY:
1444       writer->value_pos = sub->value_pos;
1445       if (NULL != sub->offsets)
1446       {
1447         write_offsets (sub->value_str, sub->value_pos, sub->offsets);
1448
1449         writer->value_pos += _dbus_string_get_length (sub->offsets);
1450
1451         _dbus_string_free (sub->offsets);
1452         dbus_free (sub->offsets);
1453       }
1454
1455       break;
1456     default:
1457       _dbus_assert_not_reached("Invalid container type");
1458   }
1459
1460   update_root_last_pos (writer);
1461
1462   /* well, we don't know where in the type string beginning of current container is */
1463   result = result && _dbus_writer_gvariant_add_offset_with_variability (writer, sub->is_fixed);
1464
1465   return result;
1466 }
1467
1468 void
1469 _dbus_type_reader_gvariant_init (DBusTypeReader *reader,
1470                                  DBusMessage    *message)
1471 {
1472 #ifndef DBUS_DISABLE_CHECKS
1473   if (!message->locked)
1474     _dbus_warn ("Warning: do not use dbus_message_iter_init() on an unlocked message; lock it first with dbus_message_lock()\n");
1475 #endif
1476   reader->gvariant = TRUE;
1477   /* GVariant wraps contents into struct, but in this place type is already
1478    * stripped off the parentheses (see get_const_signature()).
1479    */
1480
1481   reader->value_end = _dbus_message_gvariant_get_body_length (message);
1482   reader->n_offsets = _dbus_reader_count_offsets (reader);
1483 }