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