1 /* -*- Mode: c; c-basic-offset: 2 -*-
2  *
3  * raptor_sequence.c - Raptor sequence support
4  *
5  * Copyright (C) 2003-2008, David Beckett http://www.dajobe.org/
6  * Copyright (C) 2003-2004, University of Bristol, UK http://www.bristol.ac.uk/
7  *
8  * This package is Free Software and part of Redland http://librdf.org/
9  *
10  * It is licensed under the following three licenses as alternatives:
11  *   1. GNU Lesser General Public License (LGPL) V2.1 or any newer version
12  *   2. GNU General Public License (GPL) V2 or any newer version
13  *   3. Apache License, V2.0 or any newer version
14  *
15  * You may not use this file except in compliance with at least one of
16  * the above three licenses.
17  *
18  * See LICENSE.html or LICENSE.txt at the top of this package for the
19  * complete terms and further detail along with the license texts for
20  * the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively.
21  *
22  *
23  *
24  */
25 
26 #ifdef HAVE_CONFIG_H
27 #include <raptor_config.h>
28 #endif
29 
30 #ifdef WIN32
31 #include <win32_raptor_config.h>
32 #endif
33 
34 #include <stdio.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <stdarg.h>
38 #ifdef HAVE_ERRNO_H
39 #include <errno.h>
40 #endif
41 #ifdef HAVE_STDLIB_H
42 #include <stdlib.h>
43 #endif
44 
45 
46 #include "raptor.h"
47 #include "raptor_internal.h"
48 
49 
50 #ifndef STANDALONE
51 
52 /*
53  * Sequence of maximum capacity C containing N data items
54  *
55  * array:
56  *    0            <-- N consecutive items -->         C - 1
57  * -----------------------------------------------------------
58  * |      |      | data1 |  .....     data N |  ...  |       |
59  * -----------------------------------------------------------
60  * ------ O -----> offset of first data item
61  *
62  * start    = O
63  * size     = N
64  * capacity = C
65  *
66  */
67 struct raptor_sequence_s {
68   /* how many items are in the sequence 0..capacity */
69   int size;
70 
71   /* length of the 'sequence' array below */
72   int capacity;
73 
74   /* offset of the first data item in the sequence: 0..capacity-1 */
75   int start;
76 
77   /* array of size 'capacity' pointing to the data */
78   void **sequence;
79 
80   /* handler to call to free a data item (or NULL) */
81   raptor_sequence_free_handler *free_handler;
82   raptor_sequence_free_handler_v2 *free_handler_v2;
83 
84   /* handler to call to print a data item (or NULL) */
85   raptor_sequence_print_handler *print_handler;
86   raptor_sequence_print_handler_v2 *print_handler_v2;
87 
88   /* free/print handler context data */
89   void *handler_context;
90 };
91 
92 
93 static int raptor_sequence_ensure(raptor_sequence *seq, int capacity, int grow_at_front);
94 
95 /**
96  * raptor_new_sequence:
97  * @free_handler: handler to free a sequence item
98  * @print_handler: handler to print a sequence item to a FILE*
99  *
100  * Constructor - create a new sequence with the given handlers.
101  *
102  * Return value: a new #raptor_sequence or NULL on failure
103  **/
104 raptor_sequence*
raptor_new_sequence(raptor_sequence_free_handler * free_handler,raptor_sequence_print_handler * print_handler)105 raptor_new_sequence(raptor_sequence_free_handler *free_handler,
106                     raptor_sequence_print_handler *print_handler)
107 {
108   raptor_sequence* seq=(raptor_sequence*)RAPTOR_CALLOC(raptor_sequence, 1, sizeof(raptor_sequence));
109   if(!seq)
110     return NULL;
111   seq->free_handler=free_handler;
112   seq->print_handler=print_handler;
113 
114   return seq;
115 }
116 
117 
118 /**
119  * raptor_new_sequence_v2:
120  * @free_handler: handler to free a sequence item
121  * @print_handler: handler to print a sequence item to a FILE*
122  * @handler_context: context information to pass to free/print handlers
123  *
124  * Constructor - create a new sequence with the given handlers and handler context.
125  *
126  * Return value: a new #raptor_sequence or NULL on failure
127  **/
128 raptor_sequence*
raptor_new_sequence_v2(raptor_sequence_free_handler_v2 * free_handler,raptor_sequence_print_handler_v2 * print_handler,void * handler_context)129 raptor_new_sequence_v2(raptor_sequence_free_handler_v2 *free_handler,
130                        raptor_sequence_print_handler_v2 *print_handler,
131                        void *handler_context)
132 {
133   raptor_sequence* seq=(raptor_sequence*)RAPTOR_CALLOC(raptor_sequence, 1, sizeof(raptor_sequence));
134   if(!seq)
135     return NULL;
136   seq->free_handler_v2=free_handler;
137   seq->print_handler_v2=print_handler;
138   seq->handler_context=handler_context;
139 
140   return seq;
141 }
142 
143 
144 /**
145  * raptor_free_sequence:
146  * @seq: sequence to destroy
147  *
148  * Destructor - free a #raptor_sequence
149  **/
150 void
raptor_free_sequence(raptor_sequence * seq)151 raptor_free_sequence(raptor_sequence* seq)
152 {
153   int i;
154   int j;
155 
156   RAPTOR_ASSERT_OBJECT_POINTER_RETURN(seq, raptor_sequence);
157 
158   if(seq->free_handler) {
159     for(i=seq->start, j=seq->start+seq->size; i<j; i++)
160       if(seq->sequence[i])
161         seq->free_handler(seq->sequence[i]);
162   } else if(seq->free_handler_v2) {
163     for(i=seq->start, j=seq->start+seq->size; i<j; i++)
164       if(seq->sequence[i])
165         seq->free_handler_v2(seq->handler_context, seq->sequence[i]);
166   }
167 
168   if(seq->sequence)
169     RAPTOR_FREE(ptrarray, seq->sequence);
170 
171   RAPTOR_FREE(raptor_sequence, seq);
172 }
173 
174 
175 static int
raptor_sequence_ensure(raptor_sequence * seq,int capacity,int grow_at_front)176 raptor_sequence_ensure(raptor_sequence *seq, int capacity, int grow_at_front)
177 {
178   void **new_sequence;
179   int offset;
180 
181   RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(seq, raptor_sequence, 1);
182 
183   if(capacity && seq->capacity >= capacity)
184     return 0;
185 
186   /* POLICY - minimum size */
187   if(capacity < 8)
188     capacity=8;
189 
190   new_sequence=(void**)RAPTOR_CALLOC(ptrarray, capacity, sizeof(void*));
191   if(!new_sequence)
192     return 1;
193 
194   offset=(grow_at_front ? (capacity-seq->capacity) : 0)+seq->start;
195   if(seq->size) {
196     memcpy(&new_sequence[offset], &seq->sequence[seq->start], sizeof(void*)*seq->size);
197     RAPTOR_FREE(ptrarray, seq->sequence);
198   }
199   seq->start=offset;
200 
201   seq->sequence=new_sequence;
202   seq->capacity=capacity;
203   return 0;
204 }
205 
206 
207 /**
208  * raptor_sequence_size:
209  * @seq: sequence object
210  *
211  * Get the number of items in a sequence.
212  *
213  * Return value: the sequence size (>=0)
214  **/
215 int
raptor_sequence_size(raptor_sequence * seq)216 raptor_sequence_size(raptor_sequence* seq)
217 {
218   RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(seq, raptor_sequence, -1);
219 
220   return seq->size;
221 }
222 
223 
224 /* Store methods */
225 
226 /**
227  * raptor_sequence_set_at:
228  * @seq: sequence object
229  * @idx: index into sequence to operate at
230  * @data: new data item.
231  *
232  * Replace/set an item in a sequence.
233  *
234  * The item at the offset @idx in the sequence is replaced with the
235  * new item @data (which may be NULL). Any existing item is freed
236  * with the sequence's free_handler.  If necessary the sequence
237  * is extended (with NULLs) to handle a larger offset.
238  *
239  * The sequence takes ownership of the new data item.  On failure, the
240  * item is freed immediately.
241  *
242  * Return value: non-0 on failure
243  **/
244 int
raptor_sequence_set_at(raptor_sequence * seq,int idx,void * data)245 raptor_sequence_set_at(raptor_sequence* seq, int idx, void *data)
246 {
247   int need_capacity;
248 
249   RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(seq, raptor_sequence, 1);
250 
251   /* Cannot provide a negative index */
252   if(idx < 0) {
253     if(data) {
254       if(seq->free_handler)
255         seq->free_handler(data);
256       else if(seq->free_handler_v2)
257         seq->free_handler_v2(seq->handler_context, data);
258     }
259     return 1;
260   }
261 
262   need_capacity=seq->start+idx+1;
263   if(need_capacity > seq->capacity) {
264     if(seq->capacity*2 > need_capacity)
265       need_capacity = seq->capacity*2;
266     if(raptor_sequence_ensure(seq, need_capacity, 0)) {
267       if(data) {
268         if(seq->free_handler)
269           seq->free_handler(data);
270         else if(seq->free_handler_v2)
271           seq->free_handler_v2(seq->handler_context, data);
272       }
273       return 1;
274     }
275   }
276 
277   if(idx < seq->size) {
278     /* if there is old data, delete it if there is a free handler */
279     if(seq->sequence[seq->start+idx]) {
280       if(seq->free_handler)
281         seq->free_handler(seq->sequence[seq->start+idx]);
282       else if(seq->free_handler_v2)
283         seq->free_handler_v2(seq->handler_context, seq->sequence[seq->start+idx]);
284     }
285     /*  size remains the same */
286   } else {
287     /* if there is no old data, size is increasing */
288     /* make sure there are seq->size items starting from seq->start */
289     seq->size=idx+1;
290   }
291 
292   seq->sequence[seq->start+idx]=data;
293   return 0;
294 }
295 
296 
297 
298 /**
299  * raptor_sequence_push:
300  * @seq: sequence to add to
301  * @data: item to add
302  *
303  * Add an item to the end of the sequence.
304  *
305  * The sequence takes ownership of the pushed item and frees it with the
306  * free_handler. On failure, the item is freed immediately.
307  *
308  * Return value: non-0 on failure
309  **/
310 int
raptor_sequence_push(raptor_sequence * seq,void * data)311 raptor_sequence_push(raptor_sequence* seq, void *data)
312 {
313   RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(seq, raptor_sequence, 1);
314 
315   if(seq->start+seq->size == seq->capacity) {
316     if(raptor_sequence_ensure(seq, seq->capacity*2, 0)) {
317       if(data) {
318         if(seq->free_handler)
319           seq->free_handler(data);
320         else if(seq->free_handler_v2)
321           seq->free_handler_v2(seq->handler_context, data);
322       }
323       return 1;
324     }
325   }
326 
327   seq->sequence[seq->start+seq->size]=data;
328   seq->size++;
329   return 0;
330 }
331 
332 
333 /**
334  * raptor_sequence_shift:
335  * @seq: sequence to add to
336  * @data: item to add
337  *
338  * Add an item to the start of the sequence.
339  *
340  * The sequence takes ownership of the shifted item and frees it with the
341  * free_handler. On failure, the item is freed immediately.
342  *
343  * Return value: non-0 on failure
344  **/
345 int
raptor_sequence_shift(raptor_sequence * seq,void * data)346 raptor_sequence_shift(raptor_sequence* seq, void *data)
347 {
348   RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(seq, raptor_sequence, 1);
349 
350   if(!seq->start) {
351     if(raptor_sequence_ensure(seq, seq->capacity*2, 1)) {
352       if(data) {
353         if(seq->free_handler)
354           seq->free_handler(data);
355         else if(seq->free_handler_v2)
356           seq->free_handler_v2(seq->handler_context, data);
357       }
358       return 1;
359     }
360   }
361 
362   seq->sequence[--seq->start]=data;
363   seq->size++;
364   return 0;
365 }
366 
367 
368 /**
369  * raptor_sequence_get_at:
370  * @seq: sequence to use
371  * @idx: index of item to get
372  *
373  * Retrieve an item at offset @index in the sequence.
374  *
375  * This is efficient to perform. #raptor_sequence is optimised
376  * to append/remove from the end of the sequence.
377  *
378  * After this call the item is still owned by the sequence.
379  *
380  * Return value: the object or NULL if @index is out of range (0... sequence size-1)
381  **/
382 void*
raptor_sequence_get_at(raptor_sequence * seq,int idx)383 raptor_sequence_get_at(raptor_sequence* seq, int idx)
384 {
385   RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(seq, raptor_sequence, NULL);
386 
387   if(idx < 0 || idx > seq->size-1)
388     return NULL;
389   return seq->sequence[seq->start+idx];
390 }
391 
392 
393 /**
394  * raptor_sequence_delete_at:
395  * @seq: sequence object
396  * @idx: index into sequence to operate at
397  *
398  * Remove an item from a position a sequence, returning it
399  *
400  * The item at the offset @idx in the sequence is replaced with a
401  * NULL pointer and any existing item is returned.  The caller
402  * owns the resulting item.
403  *
404  * Return value: NULL on failure
405  **/
406 void*
raptor_sequence_delete_at(raptor_sequence * seq,int idx)407 raptor_sequence_delete_at(raptor_sequence* seq, int idx)
408 {
409   void* data;
410 
411   RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(seq, raptor_sequence, NULL);
412 
413   if(idx < 0 || idx > seq->size-1)
414     return NULL;
415 
416   data=seq->sequence[seq->start+idx];
417   seq->sequence[seq->start+idx]=NULL;
418 
419   return data;
420 }
421 
422 
423 
424 /**
425  * raptor_sequence_pop:
426  * @seq: sequence to use
427  *
428  * Retrieve the item at the end of the sequence.
429  *
430  * Ownership of the item is transferred to the caller,
431  * i.e. caller is responsible of freeing the item.
432  *
433  * Return value: the object or NULL if the sequence is empty
434  **/
435 void*
raptor_sequence_pop(raptor_sequence * seq)436 raptor_sequence_pop(raptor_sequence* seq)
437 {
438   void *data;
439   int i;
440 
441   RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(seq, raptor_sequence, NULL);
442 
443   if(!seq->size)
444     return NULL;
445 
446   seq->size--;
447   i=seq->start+seq->size;
448   data=seq->sequence[i];
449   seq->sequence[i]=NULL;
450 
451   return data;
452 }
453 
454 
455 /**
456  * raptor_sequence_unshift:
457  * @seq: sequence to use
458  *
459  * Retrieve the item at the start of the sequence.
460  *
461  * Ownership of the item is transferred to the caller,
462  * i.e. caller is responsible of freeing the item.
463  *
464  * Return value: the object or NULL if the sequence is empty
465  **/
466 void*
raptor_sequence_unshift(raptor_sequence * seq)467 raptor_sequence_unshift(raptor_sequence* seq)
468 {
469   void *data;
470   int i;
471 
472   RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(seq, raptor_sequence, NULL);
473 
474   if(!seq->size)
475     return NULL;
476 
477   i=seq->start++;
478   data=seq->sequence[i];
479   seq->size--;
480   seq->sequence[i]=NULL;
481 
482   return data;
483 }
484 
485 
486 /**
487  * raptor_compare_strings:
488  * @a: pointer first string
489  * @b: pointer to second string
490  *
491  * Utility function for raptor_sequence_sort() to compare a sequence of strings.
492  *
493  * Return value: comparison of @a to @b as strings
494  **/
495 int
raptor_compare_strings(const void * a,const void * b)496 raptor_compare_strings(const void *a, const void *b)
497 {
498   return strcmp(*(char**)a, *(char**)b);
499 }
500 
501 
502 
503 /**
504  * raptor_sequence_sort:
505  * @seq: sequence to sort
506  * @compare: comparison function
507  *
508  * The comparison function is compatible with that used for qsort()
509  * and provides the addresses of pointers to the data that
510  * must be dereferenced to get to the stored sequence data.
511  *
512  **/
513 RAPTOR_EXTERN_C
514 void
raptor_sequence_sort(raptor_sequence * seq,int (* compare)(const void *,const void *))515 raptor_sequence_sort(raptor_sequence* seq,
516                      int(*compare)(const void *, const void *))
517 {
518   RAPTOR_ASSERT_OBJECT_POINTER_RETURN(seq, raptor_sequence);
519 
520   if(seq->size > 1)
521     qsort(&seq->sequence[seq->start], seq->size, sizeof(void*), compare);
522 }
523 
524 
525 
526 /**
527  * raptor_sequence_print_string:
528  * @data: data item (a char*)
529  * @fh: file handle to print to
530  *
531  * Helper function for printing a sequence of strings.
532  *
533  * Intended for use as a #raptor_sequence_print_handler passed into
534  * raptor_new_sequence().
535  */
536 void
raptor_sequence_print_string(char * data,FILE * fh)537 raptor_sequence_print_string(char *data, FILE *fh)
538 {
539   fputs(data, fh);
540 }
541 
542 
543 #if !defined(RAPTOR_DISABLE_DEPRECATED) && !defined(RAPTOR_DISABLE_V1)
544 /**
545  * raptor_sequence_print_uri:
546  * @data: data item (a #raptor_uri)
547  * @fh: file handle to print to
548  *
549  * Helper function for printing a sequence of URIs.
550  *
551  * Intended for use as a #raptor_sequence_print_handler passed into
552  * raptor_new_sequence().
553  *
554  * raptor_init() MUST have been called before calling this function.
555  *
556  * @deprecated: Use raptor_uri_print() instead.
557  */
558 void
raptor_sequence_print_uri(char * data,FILE * fh)559 raptor_sequence_print_uri(char *data, FILE *fh)
560 {
561   raptor_uri* uri=(raptor_uri*)data;
562   fputs((const char*)raptor_uri_as_string_v2(raptor_world_instance(), uri), fh);
563 }
564 #endif
565 
566 
567 /**
568  * raptor_sequence_set_print_handler:
569  * @seq: sequence
570  * @print_handler: print handler
571  *
572  * Set the print handler for the sequence.
573  *
574  * This is set in the raptor_new_sequence() constructor and can be
575  * overridden here.
576  */
577 void
raptor_sequence_set_print_handler(raptor_sequence * seq,raptor_sequence_print_handler * print_handler)578 raptor_sequence_set_print_handler(raptor_sequence *seq,
579                                   raptor_sequence_print_handler *print_handler) {
580   if(!seq)
581     return;
582   seq->print_handler=print_handler;
583 }
584 
585 
586 /**
587  * raptor_sequence_set_print_handler_v2:
588  * @seq: sequence
589  * @print_handler: print handler
590  *
591  * Set the print handler for the sequence.
592  *
593  * This is set in the raptor_new_sequence_v2() constructor and can be
594  * overridden here.
595  */
596 void
raptor_sequence_set_print_handler_v2(raptor_sequence * seq,raptor_sequence_print_handler_v2 * print_handler)597 raptor_sequence_set_print_handler_v2(raptor_sequence *seq,
598                                      raptor_sequence_print_handler_v2 *print_handler) {
599   if(!seq)
600     return;
601   seq->print_handler_v2=print_handler;
602 }
603 
604 
605 /**
606  * raptor_sequence_print:
607  * @seq: sequence to sort
608  * @fh: file handle
609  *
610  * Print the sequence contents using the print_handler to print the data items.
611  */
612 void
raptor_sequence_print(raptor_sequence * seq,FILE * fh)613 raptor_sequence_print(raptor_sequence* seq, FILE* fh)
614 {
615   int i;
616 
617   RAPTOR_ASSERT_OBJECT_POINTER_RETURN(seq, raptor_sequence);
618 
619   fputc('[', fh);
620   for(i=0; i<seq->size; i++) {
621     if(i)
622       fputs(", ", fh);
623     if(seq->sequence[seq->start+i]) {
624       if(seq->print_handler)
625         seq->print_handler(seq->sequence[seq->start+i], fh);
626       else if(seq->print_handler_v2)
627         seq->print_handler_v2(seq->handler_context, seq->sequence[seq->start+i], fh);
628     } else
629       fputs("(empty)", fh);
630   }
631   fputc(']', fh);
632 }
633 
634 
635 /**
636  * raptor_sequence_join:
637  * @dest: #raptor_sequence destination sequence
638  * @src: #raptor_sequence source sequence
639  *
640  * Join two sequences moving all items from one sequence to the end of another.
641  *
642  * After this operation, sequence src will be empty (zero size) but
643  * will have the same item capacity as before.
644  *
645  * Return value: non-0 on failure
646  */
647 int
raptor_sequence_join(raptor_sequence * dest,raptor_sequence * src)648 raptor_sequence_join(raptor_sequence* dest, raptor_sequence *src)
649 {
650   RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(dest, raptor_sequence, 1);
651   RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(src, raptor_sequence, 1);
652 
653   if(raptor_sequence_ensure(dest, dest->size + src->size, 0))
654     return 1;
655   memcpy(&dest->sequence[dest->start+dest->size], &src->sequence[src->start], sizeof(void*)*src->size);
656   dest->size += src->size;
657 
658   src->size=0;
659 
660   return 0;
661 }
662 
663 
664 #endif
665 
666 
667 
668 #ifdef STANDALONE
669 #include <stdio.h>
670 
671 int main(int argc, char *argv[]);
672 
673 
674 #define assert_match_string(function, expr, string) do { char *result=expr; if(strcmp(result, string)) { fprintf(stderr, "%s:" #function " failed - returned %s, expected %s\n", program, result, string); exit(1); } } while(0)
675 #define assert_match_int(function, expr, value) do { int result=expr; if(result != value) { fprintf(stderr, "%s:" #function " failed - returned %d, expected %d\n", program, result, value); exit(1); } } while(0)
676 
677 int
main(int argc,char * argv[])678 main(int argc, char *argv[])
679 {
680   const char *program=raptor_basename(argv[0]);
681   raptor_sequence* seq1=raptor_new_sequence(NULL, (raptor_sequence_print_handler*)raptor_sequence_print_string);
682   raptor_sequence* seq2=raptor_new_sequence(NULL, (raptor_sequence_print_handler*)raptor_sequence_print_string);
683   char *s;
684   int i;
685 
686   if(raptor_sequence_pop(seq1) || raptor_sequence_unshift(seq1)) {
687     fprintf(stderr, "%s: should not be able to pop/unshift from an empty sequence\n", program);
688     exit(1);
689   }
690 
691   raptor_sequence_set_at(seq1, 0, (void*)"first");
692 
693   raptor_sequence_push(seq1, (void*)"third");
694 
695   raptor_sequence_shift(seq1, (void*)"second");
696 
697   s=(char*)raptor_sequence_get_at(seq1, 0);
698   assert_match_string(raptor_sequence_get_at, s, "second");
699 
700   s=(char*)raptor_sequence_get_at(seq1, 1);
701   assert_match_string(raptor_sequence_get_at, s, "first");
702 
703   s=(char*)raptor_sequence_get_at(seq1, 2);
704   assert_match_string(raptor_sequence_get_at, s, "third");
705 
706   assert_match_int(raptor_sequence_size, raptor_sequence_size(seq1), 3);
707 
708   fprintf(stderr, "%s: sequence after additions: ", program);
709   raptor_sequence_print(seq1, stderr);
710   fputc('\n', stderr);
711 
712   /* now made alphabetical i.e. first, second, third */
713   raptor_sequence_sort(seq1, raptor_compare_strings);
714 
715   fprintf(stderr, "%s: sequence after sort: ", program);
716   raptor_sequence_print(seq1, stderr);
717   fputc('\n', stderr);
718 
719   s=(char*)raptor_sequence_pop(seq1);
720   assert_match_string(raptor_sequence_get_at, s, "third");
721 
722   assert_match_int(raptor_sequence_size, raptor_sequence_size(seq1), 2);
723 
724   fprintf(stderr, "%s: sequence after pop: ", program);
725   raptor_sequence_print(seq1, stderr);
726   fputc('\n', stderr);
727 
728   s=(char*)raptor_sequence_unshift(seq1);
729   assert_match_string(raptor_sequence_get_at, s, "first");
730 
731   assert_match_int(raptor_sequence_size, raptor_sequence_size(seq1), 1);
732 
733   fprintf(stderr, "%s: sequence after unshift: ", program);
734   raptor_sequence_print(seq1, stderr);
735   fputc('\n', stderr);
736 
737   s=(char*)raptor_sequence_get_at(seq1, 0);
738   assert_match_string(raptor_sequence_get_at, s, "second");
739 
740   raptor_sequence_push(seq2, (void*)"first.2");
741   if(raptor_sequence_join(seq2, seq1)) {
742     fprintf(stderr, "%s: raptor_sequence_join failed\n", program);
743     exit(1);
744   }
745 
746   assert_match_int(raptor_sequence_size, raptor_sequence_size(seq1), 0);
747   assert_match_int(raptor_sequence_size, raptor_sequence_size(seq2), 2);
748 
749   raptor_free_sequence(seq1);
750   raptor_free_sequence(seq2);
751 
752   /* test sequence growing */
753 
754   seq1=raptor_new_sequence(NULL, (raptor_sequence_print_handler*)raptor_sequence_print_string);
755   for(i=0; i<100; i++)
756     if(raptor_sequence_shift(seq1, (void*)"foo")) {
757       fprintf(stderr, "%s: raptor_sequence_shift failed\n", program);
758       exit(1);
759     }
760   assert_match_int(raptor_sequence_size, raptor_sequence_size(seq1), 100);
761   for(i=0; i<100; i++)
762     raptor_sequence_unshift(seq1);
763   assert_match_int(raptor_sequence_size, raptor_sequence_size(seq1), 0);
764   raptor_free_sequence(seq1);
765 
766   seq1=raptor_new_sequence(NULL, (raptor_sequence_print_handler*)raptor_sequence_print_string);
767   for(i=0; i<100; i++)
768     if(raptor_sequence_push(seq1, (void*)"foo")) {
769       fprintf(stderr, "%s: raptor_sequence_push failed\n", program);
770       exit(1);
771     }
772   assert_match_int(raptor_sequence_size, raptor_sequence_size(seq1), 100);
773   for(i=0; i<100; i++)
774     raptor_sequence_pop(seq1);
775   assert_match_int(raptor_sequence_size, raptor_sequence_size(seq1), 0);
776   raptor_free_sequence(seq1);
777 
778   return (0);
779 }
780 #endif
781