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