2004-12-19 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-marshal-recursive.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-marshal-recursive.c  Marshalling routines for recursive types
3  *
4  * Copyright (C) 2004 Red Hat, Inc.
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "dbus-marshal-recursive.h"
25 #include "dbus-internals.h"
26
27 /**
28  * @addtogroup DBusMarshal
29  * @{
30  */
31
32 void
33 _dbus_type_reader_init (DBusTypeReader    *reader,
34                         int                byte_order,
35                         const DBusString  *type_str,
36                         int                type_pos,
37                         const DBusString  *value_str,
38                         int                value_pos)
39 {
40   reader->byte_order = byte_order;
41   reader->type_str = type_str;
42   reader->type_pos = type_pos;
43   reader->value_str = value_str;
44   reader->value_pos = value_pos;
45
46   _dbus_verbose ("type reader %p init type_pos = %d value_pos = %d remaining sig '%s'\n",
47                  reader, reader->type_pos, reader->value_pos,
48                  _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
49 }
50
51 int
52 _dbus_type_reader_get_current_type (DBusTypeReader *reader)
53 {
54   int t;
55
56   t = _dbus_string_get_byte (reader->type_str,
57                              reader->type_pos);
58
59   if (t == DBUS_STRUCT_BEGIN_CHAR)
60     t = DBUS_TYPE_STRUCT;
61
62   /* this should never be a stopping place */
63   _dbus_assert (t != DBUS_STRUCT_END_CHAR);
64
65 #if 0
66   _dbus_verbose ("type reader %p current type_pos = %d type = %s\n",
67                  reader, reader->type_pos,
68                  _dbus_type_to_string (t));
69 #endif
70   
71   return t;
72 }
73
74 int
75 _dbus_type_reader_get_array_type (DBusTypeReader *reader)
76 {
77   int t;
78
79   t = _dbus_type_reader_get_current_type (reader);
80
81   if (t != DBUS_TYPE_ARRAY)
82     return DBUS_TYPE_INVALID;
83
84   t = _dbus_string_get_byte (reader->type_str,
85                              reader->type_pos + 1);  
86   
87   return t;
88 }
89
90 void
91 _dbus_type_reader_read_basic (DBusTypeReader    *reader,
92                               void              *value)
93 {
94   int t;
95   int next;
96
97   t = _dbus_type_reader_get_current_type (reader);
98
99   next = reader->value_pos;
100   _dbus_demarshal_basic_type (reader->value_str,
101                               t, value,
102                               reader->byte_order,
103                               &next);
104
105   _dbus_verbose ("type reader %p read basic type_pos = %d value_pos = %d next = %d remaining sig '%s'\n",
106                  reader, reader->type_pos, reader->value_pos, next,
107                  _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
108
109   _dbus_verbose_bytes_of_string (reader->value_str,
110                                  reader->value_pos,
111                                  MIN (16,
112                                       _dbus_string_get_length (reader->value_str) - reader->value_pos));
113 }
114
115 dbus_bool_t
116 _dbus_type_reader_read_array_of_basic (DBusTypeReader    *reader,
117                                        int                type,
118                                        void             **array,
119                                        int               *array_len)
120 {
121   
122   
123 }
124
125 /**
126  * Initialize a new reader pointing to the first type and
127  * corresponding value that's a child of the current container. It's
128  * an error to call this if the current type is a non-container.
129  *
130  * @param reader the reader
131  * @param sub a reader to init pointing to the first child
132  */
133 void
134 _dbus_type_reader_recurse (DBusTypeReader *reader,
135                            DBusTypeReader *sub)
136 {
137   int t;
138
139   t = _dbus_string_get_byte (reader->type_str, reader->type_pos);
140   
141   /* point subreader at the same place as reader */
142   _dbus_type_reader_init (sub,
143                           reader->byte_order,
144                           reader->type_str,
145                           reader->type_pos,
146                           reader->value_str,
147                           reader->value_pos);
148
149   _dbus_assert (t == DBUS_STRUCT_BEGIN_CHAR); /* only this works right now */
150   
151   sub->type_pos += 1;
152
153   /* no value_pos increment since the struct itself doesn't take up value space */
154
155   _dbus_verbose ("type reader %p recursed type_pos = %d value_pos = %d remaining sig '%s'\n",
156                  sub, sub->type_pos, sub->value_pos,
157                  _dbus_string_get_const_data_len (sub->type_str, sub->type_pos, 0));
158 }
159
160 /**
161  * Skip to the next value on this "level". e.g. the next field in a
162  * struct, the next value in an array, the next key or value in a
163  * dict. Returns FALSE at the end of the current container.
164  *
165  * @param reader the reader
166  * @returns FALSE if nothing more to read at or below this level
167  */
168 dbus_bool_t
169 _dbus_type_reader_next (DBusTypeReader *reader)
170 {
171   int t;
172
173   /* FIXME handled calling next when there's no next */
174   
175   t = _dbus_string_get_byte (reader->type_str, reader->type_pos);
176   
177   _dbus_verbose ("type reader %p next() { type_pos = %d value_pos = %d remaining sig '%s'\n",
178                  reader, reader->type_pos, reader->value_pos,
179                  _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
180   
181   switch (t)
182     {
183     case DBUS_STRUCT_BEGIN_CHAR:
184       /* Scan forward over the entire container contents */
185       {
186         DBusTypeReader sub;
187
188         /* Recurse into the struct */
189         _dbus_type_reader_recurse (reader, &sub);
190
191         /* Skip everything in this subreader */
192         while (_dbus_type_reader_next (&sub))
193           {
194             /* nothing */;
195           }
196
197         /* Now we are at the end of this container */
198         reader->type_pos = sub.type_pos;
199         reader->value_pos = sub.value_pos;
200       }
201       break;
202
203     default:
204       /* FIXME for array etc. this is more complicated */
205       _dbus_marshal_skip_basic_type (reader->value_str,
206                                      t, reader->byte_order,
207                                      &reader->value_pos);
208       reader->type_pos += 1;
209       break;
210     }
211
212   _dbus_verbose ("type reader %p }  type_pos = %d value_pos = %d remaining sig '%s'\n",
213                  reader, reader->type_pos, reader->value_pos,
214                  _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
215
216   /* FIXME this is wrong; we need to return FALSE when we finish the
217    * container we've recursed into; even if the signature continues.
218    */
219   
220   t = _dbus_string_get_byte (reader->type_str, reader->type_pos);
221
222   if (t == DBUS_STRUCT_END_CHAR)
223     {
224       reader->type_pos += 1;
225       return FALSE;
226     }
227   if (t == DBUS_TYPE_INVALID)
228     return FALSE;
229   
230   return TRUE;
231 }
232
233 void
234 _dbus_type_writer_init (DBusTypeWriter *writer,
235                         int             byte_order,
236                         DBusString     *type_str,
237                         int             type_pos,
238                         DBusString     *value_str,
239                         int             value_pos)
240 {
241   writer->byte_order = byte_order;
242   writer->type_str = type_str;
243   writer->type_pos = type_pos;
244   writer->value_str = value_str;
245   writer->value_pos = value_pos;
246   writer->container_type = DBUS_TYPE_INVALID;
247 }
248
249 dbus_bool_t
250 _dbus_type_writer_write_basic (DBusTypeWriter *writer,
251                                int             type,
252                                const void     *value)
253 {
254   dbus_bool_t retval;
255   int old_value_len;
256
257   old_value_len = _dbus_string_get_length (writer->value_str);
258   
259   /* First ensure that our type realloc will succeed */
260   if (!_dbus_string_alloc_space (writer->type_str, 1))
261     return FALSE;
262
263   retval = FALSE;
264         
265   if (!_dbus_marshal_basic_type (writer->value_str,
266                                  writer->value_pos,
267                                  type,
268                                  value,
269                                  writer->byte_order))
270     goto out;
271
272   writer->value_pos += _dbus_string_get_length (writer->value_str) - old_value_len;
273   
274   /* Now insert the type */
275   if (!_dbus_string_insert_byte (writer->type_str,
276                                  writer->type_pos,
277                                  type))
278     _dbus_assert_not_reached ("failed to insert byte after prealloc");
279
280   writer->type_pos += 1;
281   
282   retval = TRUE;
283   
284  out:
285   return retval;
286 }
287
288 dbus_bool_t
289 _dbus_type_writer_write_array (DBusTypeWriter *writer,
290                                int             type,
291                                const void     *array,
292                                int             array_len)
293 {
294
295
296 }
297
298 dbus_bool_t
299 _dbus_type_writer_recurse (DBusTypeWriter *writer,
300                            int             container_type,
301                            DBusTypeWriter *sub)
302 {
303   _dbus_type_writer_init (sub,
304                           writer->byte_order,
305                           writer->type_str,
306                           writer->type_pos,
307                           writer->value_str,
308                           writer->value_pos);
309   sub->container_type = container_type;
310   
311   switch (container_type)
312     {
313     case DBUS_TYPE_STRUCT:
314       {
315         if (!_dbus_string_insert_byte (sub->type_str,
316                                        sub->type_pos,
317                                        DBUS_STRUCT_BEGIN_CHAR))
318           return FALSE;
319
320         sub->type_pos += 1;
321       }
322       break;
323     default:
324       _dbus_assert_not_reached ("container_type unhandled");
325       break;
326     }
327   
328   return TRUE;
329 }
330
331 dbus_bool_t
332 _dbus_type_writer_unrecurse (DBusTypeWriter *writer,
333                              DBusTypeWriter *sub)
334 {
335   _dbus_assert (sub->type_pos > 0); /* can't be recursed if this fails */
336
337   if (sub->container_type == DBUS_TYPE_STRUCT)
338     {
339       if (!_dbus_string_insert_byte (sub->type_str,
340                                      sub->type_pos, 
341                                      DBUS_STRUCT_END_CHAR))
342         return FALSE;
343       sub->type_pos += 1;
344     }
345
346   /* Jump the parent writer to the new location */
347   writer->type_pos = sub->type_pos;
348   writer->value_pos = sub->value_pos;
349   
350   return TRUE;
351 }
352
353 /** @} */ /* end of DBusMarshal group */
354
355 #ifdef DBUS_BUILD_TESTS
356 #include "dbus-test.h"
357 #include <stdio.h>
358 #include <stdlib.h>
359
360 typedef struct
361 {
362   DBusString signature;
363   DBusString body;
364 } DataBlock;
365
366 typedef struct
367 {
368   int saved_sig_len;
369   int saved_body_len;
370 } DataBlockState;
371
372 static dbus_bool_t
373 data_block_init (DataBlock *block)
374 {
375   if (!_dbus_string_init (&block->signature))
376     return FALSE;
377
378   if (!_dbus_string_init (&block->body))
379     {
380       _dbus_string_free (&block->signature);
381       return FALSE;
382     }
383   
384   return TRUE;
385 }
386
387 static void
388 data_block_free (DataBlock *block)
389 {
390   _dbus_string_free (&block->signature);
391   _dbus_string_free (&block->body);
392 }
393
394 static void
395 data_block_save (DataBlock      *block,
396                  DataBlockState *state)
397 {
398   state->saved_sig_len = _dbus_string_get_length (&block->signature);
399   state->saved_body_len = _dbus_string_get_length (&block->body);
400 }
401
402 static void
403 data_block_restore (DataBlock      *block,
404                     DataBlockState *state)
405 {
406   /* These set_length should be shortening things so should always work */
407   
408   if (!_dbus_string_set_length (&block->signature,
409                                 state->saved_sig_len))
410     _dbus_assert_not_reached ("could not restore signature length");
411   
412   if (!_dbus_string_set_length (&block->body,
413                                 state->saved_body_len))
414     _dbus_assert_not_reached ("could not restore body length");
415 }
416
417 static void
418 data_block_init_reader_writer (DataBlock      *block,
419                                int             byte_order,
420                                DBusTypeReader *reader,
421                                DBusTypeWriter *writer)
422 {
423   _dbus_type_reader_init (reader,
424                           byte_order,
425                           &block->signature,
426                           _dbus_string_get_length (&block->signature),
427                           &block->body,
428                           _dbus_string_get_length (&block->body));
429   
430   _dbus_type_writer_init (writer,
431                           byte_order,
432                           &block->signature,
433                           _dbus_string_get_length (&block->signature),
434                           &block->body,
435                           _dbus_string_get_length (&block->body));
436 }
437
438 #define SAMPLE_INT32           12345678
439 #define SAMPLE_INT32_ALTERNATE 53781429
440 static dbus_bool_t
441 write_int32 (DataBlock      *block,
442              DBusTypeWriter *writer)
443 {
444   dbus_int32_t v = SAMPLE_INT32;
445
446   return _dbus_type_writer_write_basic (writer,
447                                         DBUS_TYPE_INT32,
448                                         &v);
449 }
450
451 static void
452 check_expected_type (DBusTypeReader *reader,
453                      int             expected)
454 {
455   int t;
456
457   t = _dbus_type_reader_get_current_type (reader);
458   
459   if (t != expected)
460     {
461       _dbus_warn ("Read type %s while expecting %s\n",
462                   _dbus_type_to_string (t),
463                   _dbus_type_to_string (expected));
464
465       _dbus_verbose_bytes_of_string (reader->type_str, 0,
466                                      _dbus_string_get_length (reader->type_str));
467       _dbus_verbose_bytes_of_string (reader->value_str, 0,
468                                      _dbus_string_get_length (reader->value_str));
469       
470       exit (1);
471     }
472 }
473
474 static dbus_bool_t
475 read_int32 (DataBlock      *block,
476             DBusTypeReader *reader)
477 {
478   dbus_int32_t v;
479
480   check_expected_type (reader, DBUS_TYPE_INT32);
481   
482   _dbus_type_reader_read_basic (reader,
483                                 (dbus_int32_t*) &v);
484
485   _dbus_assert (v == SAMPLE_INT32);
486
487   return TRUE;
488 }
489
490 static dbus_bool_t
491 write_struct_with_int32s (DataBlock      *block,
492                           DBusTypeWriter *writer)
493 {
494   dbus_int32_t v;
495   DataBlockState saved;
496   DBusTypeWriter sub;
497
498   data_block_save (block, &saved);
499   
500   if (!_dbus_type_writer_recurse (writer,
501                                   DBUS_TYPE_STRUCT,
502                                   &sub))
503     return FALSE;
504
505   v = SAMPLE_INT32;
506   if (!_dbus_type_writer_write_basic (&sub,
507                                       DBUS_TYPE_INT32,
508                                       &v))
509     {
510       data_block_restore (block, &saved);
511       return FALSE;
512     }
513
514   v = SAMPLE_INT32_ALTERNATE;
515   if (!_dbus_type_writer_write_basic (&sub,
516                                       DBUS_TYPE_INT32,
517                                       &v))
518     {
519       data_block_restore (block, &saved);
520       return FALSE;
521     }
522
523   if (!_dbus_type_writer_unrecurse (writer, &sub))
524     {
525       data_block_restore (block, &saved);
526       return FALSE;
527     }
528   
529   return TRUE;
530 }
531
532 static dbus_bool_t
533 read_struct_with_int32s (DataBlock      *block,
534                          DBusTypeReader *reader)
535 {
536   dbus_int32_t v;
537   DBusTypeReader sub;
538
539   check_expected_type (reader, DBUS_TYPE_STRUCT);
540   
541   _dbus_type_reader_recurse (reader, &sub);
542
543   check_expected_type (&sub, DBUS_TYPE_INT32);
544   
545   _dbus_type_reader_read_basic (&sub,
546                                 (dbus_int32_t*) &v);
547
548   _dbus_assert (v == SAMPLE_INT32);
549
550   _dbus_type_reader_next (&sub);
551   check_expected_type (&sub, DBUS_TYPE_INT32);
552   
553   _dbus_type_reader_read_basic (&sub,
554                                 (dbus_int32_t*) &v);
555
556   _dbus_assert (v == SAMPLE_INT32_ALTERNATE);
557   
558   return TRUE;
559 }
560
561 static dbus_bool_t
562 write_struct_of_structs (DataBlock      *block,
563                          DBusTypeWriter *writer)
564 {
565   DataBlockState saved;
566   DBusTypeWriter sub;
567
568   data_block_save (block, &saved);
569   
570   if (!_dbus_type_writer_recurse (writer,
571                                   DBUS_TYPE_STRUCT,
572                                   &sub))
573     return FALSE;
574
575   if (!write_struct_with_int32s (block, &sub))
576     {
577       data_block_restore (block, &saved);
578       return FALSE;
579     }
580   if (!write_struct_with_int32s (block, &sub))
581     {
582       data_block_restore (block, &saved);
583       return FALSE;
584     }
585   if (!write_struct_with_int32s (block, &sub))
586     {
587       data_block_restore (block, &saved);
588       return FALSE;
589     }
590
591   if (!_dbus_type_writer_unrecurse (writer, &sub))
592     {
593       data_block_restore (block, &saved);
594       return FALSE;
595     }
596   
597   return TRUE;
598 }
599
600 static dbus_bool_t
601 read_struct_of_structs (DataBlock      *block,
602                         DBusTypeReader *reader)
603 {
604   DBusTypeReader sub;
605   
606   check_expected_type (reader, DBUS_TYPE_STRUCT);
607   
608   _dbus_type_reader_recurse (reader, &sub);
609
610   if (!read_struct_with_int32s (block, &sub))
611     return FALSE;
612   _dbus_type_reader_next (&sub);
613   if (!read_struct_with_int32s (block, &sub))
614     return FALSE;
615   _dbus_type_reader_next (&sub);
616   if (!read_struct_with_int32s (block, &sub))
617     return FALSE;
618   
619   return TRUE;
620 }
621
622 static dbus_bool_t
623 write_struct_of_structs_of_structs (DataBlock      *block,
624                                     DBusTypeWriter *writer)
625 {
626   DataBlockState saved;
627   DBusTypeWriter sub;
628
629   data_block_save (block, &saved);
630   
631   if (!_dbus_type_writer_recurse (writer,
632                                   DBUS_TYPE_STRUCT,
633                                   &sub))
634     return FALSE;
635
636   if (!write_struct_of_structs (block, &sub))
637     {
638       data_block_restore (block, &saved);
639       return FALSE;
640     }
641   if (!write_struct_of_structs (block, &sub))
642     {
643       data_block_restore (block, &saved);
644       return FALSE;
645     }
646
647   if (!_dbus_type_writer_unrecurse (writer, &sub))
648     {
649       data_block_restore (block, &saved);
650       return FALSE;
651     }
652   
653   return TRUE;
654 }
655
656 static dbus_bool_t
657 read_struct_of_structs_of_structs (DataBlock      *block,
658                                    DBusTypeReader *reader)
659 {
660   DBusTypeReader sub;
661   
662   check_expected_type (reader, DBUS_TYPE_STRUCT);
663   
664   _dbus_type_reader_recurse (reader, &sub);
665
666   if (!read_struct_of_structs (block, &sub))
667     return FALSE;
668   _dbus_type_reader_next (&sub);
669   if (!read_struct_of_structs (block, &sub))
670     return FALSE;
671   
672   return TRUE;
673 }
674
675 typedef enum {
676   ITEM_INVALID = -1,
677   ITEM_INT32 = 0,
678   ITEM_STRUCT_WITH_INT32S,
679   ITEM_STRUCT_OF_STRUCTS,
680   ITEM_STRUCT_OF_STRUCTS_OF_STRUCTS,
681   ITEM_LAST
682 } WhichItem;
683
684
685 typedef dbus_bool_t (* WriteItemFunc) (DataBlock      *block,
686                                        DBusTypeWriter *writer);
687 typedef dbus_bool_t (* ReadItemFunc)  (DataBlock      *block,
688                                        DBusTypeReader *reader);
689
690 typedef struct
691 {
692   const char *desc;
693   WhichItem which;
694   WriteItemFunc write_item_func;
695   ReadItemFunc read_item_func;
696 } CheckMarshalItem;
697
698 static CheckMarshalItem items[] = {
699   { "int32",
700     ITEM_INT32, write_int32, read_int32 },
701   { "struct with two int32",
702     ITEM_STRUCT_WITH_INT32S, write_struct_with_int32s, read_struct_with_int32s },
703   { "struct with three structs of two int32",
704     ITEM_STRUCT_OF_STRUCTS, write_struct_of_structs, read_struct_of_structs },
705   { "struct of two structs of three structs of two int32",
706     ITEM_STRUCT_OF_STRUCTS_OF_STRUCTS,
707     write_struct_of_structs_of_structs,
708     read_struct_of_structs_of_structs }
709 };
710
711 typedef struct
712 {
713   /* Array of items in the above items[]; -1 terminated */
714   int items[20];
715 } TestRun;
716
717 static TestRun runs[] = {
718   { { ITEM_INVALID } },
719
720   /* INT32 */
721   { { ITEM_INT32, ITEM_INVALID } },
722   { { ITEM_INT32, ITEM_INT32, ITEM_INVALID } },
723   { { ITEM_INT32, ITEM_INT32, ITEM_INT32, ITEM_INT32, ITEM_INT32, ITEM_INVALID } },
724
725   /* STRUCT_WITH_INT32S */
726   { { ITEM_STRUCT_WITH_INT32S, ITEM_INVALID } },
727   { { ITEM_STRUCT_WITH_INT32S, ITEM_STRUCT_WITH_INT32S, ITEM_INVALID } },
728   { { ITEM_STRUCT_WITH_INT32S, ITEM_INT32, ITEM_STRUCT_WITH_INT32S, ITEM_INVALID } },
729   { { ITEM_INT32, ITEM_STRUCT_WITH_INT32S, ITEM_INT32, ITEM_STRUCT_WITH_INT32S, ITEM_INVALID } },
730   { { ITEM_INT32, ITEM_STRUCT_WITH_INT32S, ITEM_INT32, ITEM_INT32, ITEM_INT32, ITEM_STRUCT_WITH_INT32S, ITEM_INVALID } },
731
732   /* STRUCT_OF_STRUCTS */
733   { { ITEM_STRUCT_OF_STRUCTS, ITEM_INVALID } },
734   { { ITEM_STRUCT_OF_STRUCTS, ITEM_STRUCT_OF_STRUCTS, ITEM_INVALID } },
735   { { ITEM_STRUCT_OF_STRUCTS, ITEM_INT32, ITEM_STRUCT_OF_STRUCTS, ITEM_INVALID } },
736   { { ITEM_STRUCT_WITH_INT32S, ITEM_STRUCT_OF_STRUCTS, ITEM_INT32, ITEM_STRUCT_OF_STRUCTS, ITEM_INVALID } },
737   { { ITEM_INT32, ITEM_STRUCT_OF_STRUCTS, ITEM_INT32, ITEM_STRUCT_OF_STRUCTS, ITEM_INVALID } },
738   { { ITEM_STRUCT_OF_STRUCTS, ITEM_STRUCT_OF_STRUCTS, ITEM_STRUCT_OF_STRUCTS, ITEM_INVALID } },
739
740   /* STRUCT_OF_STRUCTS_OF_STRUCTS */
741   { { ITEM_STRUCT_OF_STRUCTS_OF_STRUCTS, ITEM_INVALID } },
742   { { ITEM_STRUCT_OF_STRUCTS_OF_STRUCTS, ITEM_STRUCT_OF_STRUCTS_OF_STRUCTS, ITEM_INVALID } },
743   { { ITEM_STRUCT_OF_STRUCTS_OF_STRUCTS, ITEM_INT32, ITEM_STRUCT_OF_STRUCTS_OF_STRUCTS, ITEM_INVALID } },
744   { { ITEM_STRUCT_WITH_INT32S, ITEM_STRUCT_OF_STRUCTS_OF_STRUCTS, ITEM_INT32, ITEM_STRUCT_OF_STRUCTS_OF_STRUCTS, ITEM_INVALID } },
745   { { ITEM_INT32, ITEM_STRUCT_OF_STRUCTS_OF_STRUCTS, ITEM_INT32, ITEM_STRUCT_OF_STRUCTS_OF_STRUCTS, ITEM_INVALID } },
746   { { ITEM_STRUCT_OF_STRUCTS_OF_STRUCTS, ITEM_STRUCT_OF_STRUCTS_OF_STRUCTS, ITEM_STRUCT_OF_STRUCTS_OF_STRUCTS, ITEM_INVALID } }
747   
748 };
749
750 static dbus_bool_t
751 perform_one_run (DataBlock *block,
752                  int        byte_order,
753                  TestRun   *run)
754 {
755   DBusTypeReader reader;
756   DBusTypeWriter writer;
757   int i;
758   DataBlockState saved;
759   dbus_bool_t retval;
760
761   retval = FALSE;
762
763   data_block_save (block, &saved);
764   
765   data_block_init_reader_writer (block, 
766                                  byte_order,
767                                  &reader, &writer);
768
769   i = 0;
770   while (run->items[i] != ITEM_INVALID)
771     {
772       CheckMarshalItem *item = &items[run->items[i]];
773
774       _dbus_verbose ("writing %s\n", item->desc);
775       
776       if (!(* item->write_item_func) (block, &writer))
777         goto out;
778       ++i;
779     }
780
781   i = 0;
782   while (run->items[i] != ITEM_INVALID)
783     {
784       CheckMarshalItem *item = &items[run->items[i]];
785
786       _dbus_verbose ("reading %s\n", item->desc);
787       
788       if (!(* item->read_item_func) (block, &reader))
789         goto out;
790
791       _dbus_type_reader_next (&reader);
792       
793       ++i;
794     }
795   
796   retval = TRUE;
797   
798  out:
799   data_block_restore (block, &saved);
800   return retval;
801 }
802
803 static dbus_bool_t
804 perform_all_runs (int byte_order,
805                   int initial_offset)
806 {
807   int i;
808   DataBlock block;
809   dbus_bool_t retval;
810
811   retval = FALSE;
812   
813   if (!data_block_init (&block))
814     return FALSE;
815
816   if (!_dbus_string_lengthen (&block.signature, initial_offset))
817     goto out;
818   
819   if (!_dbus_string_lengthen (&block.body, initial_offset))
820     goto out;
821   
822   i = 0;
823   while (i < _DBUS_N_ELEMENTS (runs))
824     {
825       if (!perform_one_run (&block, byte_order, &runs[i]))
826         goto out;
827       
828       ++i;
829     }
830
831   retval = TRUE;
832   
833  out:
834   data_block_free (&block);
835   
836   return retval;
837 }
838
839 static dbus_bool_t
840 perform_all_items (int byte_order,
841                    int initial_offset)
842 {
843   int i;
844   DataBlock block;
845   dbus_bool_t retval;
846   TestRun run;
847
848   retval = FALSE;
849   
850   if (!data_block_init (&block))
851     return FALSE;
852
853
854   if (!_dbus_string_lengthen (&block.signature, initial_offset))
855     goto out;
856   
857   if (!_dbus_string_lengthen (&block.body, initial_offset))
858     goto out;
859
860   /* Create a run containing all the items */
861   i = 0;
862   while (i < _DBUS_N_ELEMENTS (items))
863     {
864       _dbus_assert (i == items[i].which);
865       
866       run.items[i] = items[i].which;
867       
868       ++i;
869     }
870   
871   run.items[i] = ITEM_INVALID;
872
873   if (!perform_one_run (&block, byte_order, &run))
874     goto out;  
875   
876   retval = TRUE;
877   
878  out:
879   data_block_free (&block);
880   
881   return retval;
882 }
883
884 static dbus_bool_t
885 recursive_marshal_test_iteration (void *data)
886 {
887   int i;
888
889   i = 0;
890   while (i < 18)
891     {
892       if (!perform_all_runs (DBUS_LITTLE_ENDIAN, i))
893         return FALSE;
894       if (!perform_all_runs (DBUS_BIG_ENDIAN, i))
895         return FALSE;
896       if (!perform_all_items (DBUS_LITTLE_ENDIAN, i))
897         return FALSE;
898       if (!perform_all_items (DBUS_BIG_ENDIAN, i))
899         return FALSE;
900       
901       ++i;
902     }
903
904   return TRUE;
905 }
906
907 dbus_bool_t _dbus_marshal_recursive_test (void);
908
909 dbus_bool_t
910 _dbus_marshal_recursive_test (void)
911 {
912   _dbus_test_oom_handling ("recursive marshaling",
913                            recursive_marshal_test_iteration,
914                            NULL);  
915   
916   return TRUE;
917 }
918
919 #if 1
920 int
921 main (int argc, char **argv)
922 {
923   _dbus_marshal_recursive_test ();
924
925   return 0;
926 }
927 #endif /* main() */
928
929 #endif /* DBUS_BUILD_TESTS */