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