Merge branch 'master' of git://code.opencv.org/opencv
[profile/ivi/opencv.git] / modules / core / src / persistence.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                           License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15 // Third party copyrights are property of their respective owners.
16 //
17 // Redistribution and use in source and binary forms, with or without modification,
18 // are permitted provided that the following conditions are met:
19 //
20 //   * Redistribution's of source code must retain the above copyright notice,
21 //     this list of conditions and the following disclaimer.
22 //
23 //   * Redistribution's in binary form must reproduce the above copyright notice,
24 //     this list of conditions and the following disclaimer in the documentation
25 //     and/or other materials provided with the distribution.
26 //
27 //   * The name of the copyright holders may not be used to endorse or promote products
28 //     derived from this software without specific prior written permission.
29 //
30 // This software is provided by the copyright holders and contributors "as is" and
31 // any express or implied warranties, including, but not limited to, the implied
32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
33 // In no event shall the Intel Corporation or contributors be liable for any direct,
34 // indirect, incidental, special, exemplary, or consequential damages
35 // (including, but not limited to, procurement of substitute goods or services;
36 // loss of use, data, or profits; or business interruption) however caused
37 // and on any theory of liability, whether in contract, strict liability,
38 // or tort (including negligence or otherwise) arising in any way out of
39 // the use of this software, even if advised of the possibility of such damage.
40 //
41 //M*/
42
43 #include "precomp.hpp"
44
45 #include <ctype.h>
46 #include <deque>
47 #include <iterator>
48 #include <wchar.h>
49
50 #define USE_ZLIB 1
51
52 #ifdef __APPLE__
53 #  include "TargetConditionals.h"
54 #  if (defined TARGET_OS_IPHONE && TARGET_OS_IPHONE) || (defined TARGET_IPHONE_SIMULATOR && TARGET_IPHONE_SIMULATOR)
55 #    undef USE_ZLIB
56 #    define USE_ZLIB 0
57      typedef void* gzFile;
58 #  endif
59 #endif
60
61 #if USE_ZLIB
62 #  undef HAVE_UNISTD_H //to avoid redefinition
63 #  ifndef _LFS64_LARGEFILE
64 #    define _LFS64_LARGEFILE 0
65 #  endif
66 #  ifndef _FILE_OFFSET_BITS
67 #    define _FILE_OFFSET_BITS 0
68 #  endif
69 #  include <zlib.h>
70 #endif
71
72 /****************************************************************************************\
73 *                            Common macros and type definitions                          *
74 \****************************************************************************************/
75
76 #define cv_isprint(c)     ((uchar)(c) >= (uchar)' ')
77 #define cv_isprint_or_tab(c)  ((uchar)(c) >= (uchar)' ' || (c) == '\t')
78
79 static inline bool cv_isalnum(char c)
80 {
81     return ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
82 }
83
84 static inline bool cv_isalpha(char c)
85 {
86     return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
87 }
88
89 static inline bool cv_isdigit(char c)
90 {
91     return '0' <= c && c <= '9';
92 }
93
94 static inline bool cv_isspace(char c)
95 {
96     return (9 <= c && c <= 13) || c == ' ';
97 }
98
99 static char* icv_itoa( int _val, char* buffer, int /*radix*/ )
100 {
101     const int radix = 10;
102     char* ptr=buffer + 23 /* enough even for 64-bit integers */;
103     unsigned val = abs(_val);
104
105     *ptr = '\0';
106     do
107     {
108         unsigned r = val / radix;
109         *--ptr = (char)(val - (r*radix) + '0');
110         val = r;
111     }
112     while( val != 0 );
113
114     if( _val < 0 )
115         *--ptr = '-';
116
117     return ptr;
118 }
119
120 cv::string cv::FileStorage::getDefaultObjectName(const string& _filename)
121 {
122     static const char* stubname = "unnamed";
123     const char* filename = _filename.c_str();
124     const char* ptr2 = filename + _filename.size();
125     const char* ptr = ptr2 - 1;
126     cv::AutoBuffer<char> name_buf(_filename.size()+1);
127
128     while( ptr >= filename && *ptr != '\\' && *ptr != '/' && *ptr != ':' )
129     {
130         if( *ptr == '.' && (!*ptr2 || strncmp(ptr2, ".gz", 3) == 0) )
131             ptr2 = ptr;
132         ptr--;
133     }
134     ptr++;
135     if( ptr == ptr2 )
136         CV_Error( CV_StsBadArg, "Invalid filename" );
137
138     char* name = name_buf;
139
140     // name must start with letter or '_'
141     if( !cv_isalpha(*ptr) && *ptr!= '_' ){
142         *name++ = '_';
143     }
144
145     while( ptr < ptr2 )
146     {
147         char c = *ptr++;
148         if( !cv_isalnum(c) && c != '-' && c != '_' )
149             c = '_';
150         *name++ = c;
151     }
152     *name = '\0';
153     name = name_buf;
154     if( strcmp( name, "_" ) == 0 )
155         strcpy( name, stubname );
156     return cv::string(name);
157 }
158
159 namespace cv
160 {
161 #if !defined(ANDROID) || (defined(_GLIBCXX_USE_WCHAR_T) && _GLIBCXX_USE_WCHAR_T)
162 string fromUtf16(const WString& str)
163 {
164     cv::AutoBuffer<char> _buf(str.size()*4 + 1);
165     char* buf = _buf;
166
167     size_t sz = wcstombs(buf, str.c_str(), str.size());
168     if( sz == (size_t)-1 )
169         return string();
170     buf[sz] = '\0';
171     return string(buf);
172 }
173
174 WString toUtf16(const string& str)
175 {
176     cv::AutoBuffer<wchar_t> _buf(str.size() + 1);
177     wchar_t* buf = _buf;
178
179     size_t sz = mbstowcs(buf, str.c_str(), str.size());
180     if( sz == (size_t)-1 )
181         return WString();
182     buf[sz] = '\0';
183     return WString(buf);
184 }
185 #endif
186 }
187
188 typedef struct CvGenericHash
189 {
190     CV_SET_FIELDS()
191     int tab_size;
192     void** table;
193 }
194 CvGenericHash;
195
196 typedef CvGenericHash CvStringHash;
197
198 typedef struct CvFileMapNode
199 {
200     CvFileNode value;
201     const CvStringHashNode* key;
202     struct CvFileMapNode* next;
203 }
204 CvFileMapNode;
205
206 typedef struct CvXMLStackRecord
207 {
208     CvMemStoragePos pos;
209     CvString struct_tag;
210     int struct_indent;
211     int struct_flags;
212 }
213 CvXMLStackRecord;
214
215 #define CV_XML_OPENING_TAG 1
216 #define CV_XML_CLOSING_TAG 2
217 #define CV_XML_EMPTY_TAG 3
218 #define CV_XML_HEADER_TAG 4
219 #define CV_XML_DIRECTIVE_TAG 5
220
221 //typedef void (*CvParse)( struct CvFileStorage* fs );
222 typedef void (*CvStartWriteStruct)( struct CvFileStorage* fs, const char* key,
223                                     int struct_flags, const char* type_name );
224 typedef void (*CvEndWriteStruct)( struct CvFileStorage* fs );
225 typedef void (*CvWriteInt)( struct CvFileStorage* fs, const char* key, int value );
226 typedef void (*CvWriteReal)( struct CvFileStorage* fs, const char* key, double value );
227 typedef void (*CvWriteString)( struct CvFileStorage* fs, const char* key,
228                                const char* value, int quote );
229 typedef void (*CvWriteComment)( struct CvFileStorage* fs, const char* comment, int eol_comment );
230 typedef void (*CvStartNextStream)( struct CvFileStorage* fs );
231
232 typedef struct CvFileStorage
233 {
234     int flags;
235     int fmt;
236     int write_mode;
237     int is_first;
238     CvMemStorage* memstorage;
239     CvMemStorage* dststorage;
240     CvMemStorage* strstorage;
241     CvStringHash* str_hash;
242     CvSeq* roots;
243     CvSeq* write_stack;
244     int struct_indent;
245     int struct_flags;
246     CvString struct_tag;
247     int space;
248     char* filename;
249     FILE* file;
250     gzFile gzfile;
251     char* buffer;
252     char* buffer_start;
253     char* buffer_end;
254     int wrap_margin;
255     int lineno;
256     int dummy_eof;
257     const char* errmsg;
258     char errmsgbuf[128];
259
260     CvStartWriteStruct start_write_struct;
261     CvEndWriteStruct end_write_struct;
262     CvWriteInt write_int;
263     CvWriteReal write_real;
264     CvWriteString write_string;
265     CvWriteComment write_comment;
266     CvStartNextStream start_next_stream;
267
268     const char* strbuf;
269     size_t strbufsize, strbufpos;
270     std::deque<char>* outbuf;
271
272     bool is_opened;
273 }
274 CvFileStorage;
275
276 static void icvPuts( CvFileStorage* fs, const char* str )
277 {
278     if( fs->outbuf )
279         std::copy(str, str + strlen(str), std::back_inserter(*fs->outbuf));
280     else if( fs->file )
281         fputs( str, fs->file );
282 #if USE_ZLIB
283     else if( fs->gzfile )
284         gzputs( fs->gzfile, str );
285 #endif
286     else
287         CV_Error( CV_StsError, "The storage is not opened" );
288 }
289
290 static char* icvGets( CvFileStorage* fs, char* str, int maxCount )
291 {
292     if( fs->strbuf )
293     {
294         size_t i = fs->strbufpos, len = fs->strbufsize;
295         int j = 0;
296         const char* instr = fs->strbuf;
297         while( i < len && j < maxCount-1 )
298         {
299             char c = instr[i++];
300             if( c == '\0' )
301                 break;
302             str[j++] = c;
303             if( c == '\n' )
304                 break;
305         }
306         str[j++] = '\0';
307         fs->strbufpos = i;
308         return j > 1 ? str : 0;
309     }
310     if( fs->file )
311         return fgets( str, maxCount, fs->file );
312 #if USE_ZLIB
313     if( fs->gzfile )
314         return gzgets( fs->gzfile, str, maxCount );
315 #endif
316     CV_Error( CV_StsError, "The storage is not opened" );
317     return 0;
318 }
319
320 static int icvEof( CvFileStorage* fs )
321 {
322     if( fs->strbuf )
323         return fs->strbufpos >= fs->strbufsize;
324     if( fs->file )
325         return feof(fs->file);
326 #if USE_ZLIB
327     if( fs->gzfile )
328         return gzeof(fs->gzfile);
329 #endif
330     return false;
331 }
332
333 static void icvCloseFile( CvFileStorage* fs )
334 {
335     if( fs->file )
336         fclose( fs->file );
337 #if USE_ZLIB
338     else if( fs->gzfile )
339         gzclose( fs->gzfile );
340 #endif
341     fs->file = 0;
342     fs->gzfile = 0;
343     fs->strbuf = 0;
344     fs->strbufpos = 0;
345     fs->is_opened = false;
346 }
347
348 static void icvRewind( CvFileStorage* fs )
349 {
350     if( fs->file )
351         rewind(fs->file);
352 #if USE_ZLIB
353     else if( fs->gzfile )
354         gzrewind(fs->gzfile);
355 #endif
356     fs->strbufpos = 0;
357 }
358
359 #define CV_YML_INDENT  3
360 #define CV_XML_INDENT  2
361 #define CV_YML_INDENT_FLOW  1
362 #define CV_FS_MAX_LEN 4096
363
364 #define CV_FILE_STORAGE ('Y' + ('A' << 8) + ('M' << 16) + ('L' << 24))
365 #define CV_IS_FILE_STORAGE(fs) ((fs) != 0 && (fs)->flags == CV_FILE_STORAGE)
366
367 #define CV_CHECK_FILE_STORAGE(fs)                       \
368 {                                                       \
369     if( !CV_IS_FILE_STORAGE(fs) )                       \
370         CV_Error( (fs) ? CV_StsBadArg : CV_StsNullPtr,  \
371                   "Invalid pointer to file storage" );  \
372 }
373
374 #define CV_CHECK_OUTPUT_FILE_STORAGE(fs)                \
375 {                                                       \
376     CV_CHECK_FILE_STORAGE(fs);                          \
377     if( !fs->write_mode )                               \
378         CV_Error( CV_StsError, "The file storage is opened for reading" ); \
379 }
380
381 CV_IMPL const char*
382 cvAttrValue( const CvAttrList* attr, const char* attr_name )
383 {
384     while( attr && attr->attr )
385     {
386         int i;
387         for( i = 0; attr->attr[i*2] != 0; i++ )
388         {
389             if( strcmp( attr_name, attr->attr[i*2] ) == 0 )
390                 return attr->attr[i*2+1];
391         }
392         attr = attr->next;
393     }
394
395     return 0;
396 }
397
398
399 static CvGenericHash*
400 cvCreateMap( int flags, int header_size, int elem_size,
401              CvMemStorage* storage, int start_tab_size )
402 {
403     if( header_size < (int)sizeof(CvGenericHash) )
404         CV_Error( CV_StsBadSize, "Too small map header_size" );
405
406     if( start_tab_size <= 0 )
407         start_tab_size = 16;
408
409     CvGenericHash* map = (CvGenericHash*)cvCreateSet( flags, header_size, elem_size, storage );
410
411     map->tab_size = start_tab_size;
412     start_tab_size *= sizeof(map->table[0]);
413     map->table = (void**)cvMemStorageAlloc( storage, start_tab_size );
414     memset( map->table, 0, start_tab_size );
415
416     return map;
417 }
418
419 #ifdef __GNUC__
420 #define CV_PARSE_ERROR( errmsg )                                    \
421     icvParseError( fs, __func__, (errmsg), __FILE__, __LINE__ )
422 #else
423 #define CV_PARSE_ERROR( errmsg )                                    \
424     icvParseError( fs, "", (errmsg), __FILE__, __LINE__ )
425 #endif
426
427 static void
428 icvParseError( CvFileStorage* fs, const char* func_name,
429                const char* err_msg, const char* source_file, int source_line )
430 {
431     char buf[1<<10];
432     sprintf( buf, "%s(%d): %s", fs->filename, fs->lineno, err_msg );
433     cvError( CV_StsParseError, func_name, buf, source_file, source_line );
434 }
435
436
437 static void
438 icvFSCreateCollection( CvFileStorage* fs, int tag, CvFileNode* collection )
439 {
440     if( CV_NODE_IS_MAP(tag) )
441     {
442         if( collection->tag != CV_NODE_NONE )
443         {
444             assert( fs->fmt == CV_STORAGE_FORMAT_XML );
445             CV_PARSE_ERROR( "Sequence element should not have name (use <_></_>)" );
446         }
447
448         collection->data.map = cvCreateMap( 0, sizeof(CvFileNodeHash),
449                             sizeof(CvFileMapNode), fs->memstorage, 16 );
450     }
451     else
452     {
453         CvSeq* seq;
454         seq = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvFileNode), fs->memstorage );
455
456         // if <collection> contains some scalar element, add it to the newly created collection
457         if( CV_NODE_TYPE(collection->tag) != CV_NODE_NONE )
458             cvSeqPush( seq, collection );
459
460         collection->data.seq = seq;
461     }
462
463     collection->tag = tag;
464     cvSetSeqBlockSize( collection->data.seq, 8 );
465 }
466
467
468 /*static void
469 icvFSReleaseCollection( CvSeq* seq )
470 {
471     if( seq )
472     {
473         int is_map = CV_IS_SET(seq);
474         CvSeqReader reader;
475         int i, total = seq->total;
476         cvStartReadSeq( seq, &reader, 0 );
477
478         for( i = 0; i < total; i++ )
479         {
480             CvFileNode* node = (CvFileNode*)reader.ptr;
481
482             if( (!is_map || CV_IS_SET_ELEM( node )) && CV_NODE_IS_COLLECTION(node->tag) )
483             {
484                 if( CV_NODE_IS_USER(node->tag) && node->info && node->data.obj.decoded )
485                     cvRelease( (void**)&node->data.obj.decoded );
486                 if( !CV_NODE_SEQ_IS_SIMPLE( node->data.seq ))
487                     icvFSReleaseCollection( node->data.seq );
488             }
489             CV_NEXT_SEQ_ELEM( seq->elem_size, reader );
490         }
491     }
492 }*/
493
494
495 static char*
496 icvFSDoResize( CvFileStorage* fs, char* ptr, int len )
497 {
498     char* new_ptr = 0;
499     int written_len = (int)(ptr - fs->buffer_start);
500     int new_size = (int)((fs->buffer_end - fs->buffer_start)*3/2);
501     new_size = MAX( written_len + len, new_size );
502     new_ptr = (char*)cvAlloc( new_size + 256 );
503     fs->buffer = new_ptr + (fs->buffer - fs->buffer_start);
504     if( written_len > 0 )
505         memcpy( new_ptr, fs->buffer_start, written_len );
506     fs->buffer_start = new_ptr;
507     fs->buffer_end = fs->buffer_start + new_size;
508     new_ptr += written_len;
509     return new_ptr;
510 }
511
512
513 inline char* icvFSResizeWriteBuffer( CvFileStorage* fs, char* ptr, int len )
514 {
515     return ptr + len < fs->buffer_end ? ptr : icvFSDoResize( fs, ptr, len );
516 }
517
518
519 static char*
520 icvFSFlush( CvFileStorage* fs )
521 {
522     char* ptr = fs->buffer;
523     int indent;
524
525     if( ptr > fs->buffer_start + fs->space )
526     {
527         ptr[0] = '\n';
528         ptr[1] = '\0';
529         icvPuts( fs, fs->buffer_start );
530         fs->buffer = fs->buffer_start;
531     }
532
533     indent = fs->struct_indent;
534
535     if( fs->space != indent )
536     {
537         if( fs->space < indent )
538             memset( fs->buffer_start + fs->space, ' ', indent - fs->space );
539         fs->space = indent;
540     }
541
542     ptr = fs->buffer = fs->buffer_start + fs->space;
543
544     return ptr;
545 }
546
547
548 static void
549 icvClose( CvFileStorage* fs, std::string* out )
550 {
551     if( out )
552         out->clear();
553
554     if( !fs )
555         CV_Error( CV_StsNullPtr, "NULL double pointer to file storage" );
556
557     if( fs->is_opened )
558     {
559         if( fs->write_mode && (fs->file || fs->gzfile || fs->outbuf) )
560         {
561             if( fs->write_stack )
562             {
563                 while( fs->write_stack->total > 0 )
564                     cvEndWriteStruct(fs);
565             }
566             icvFSFlush(fs);
567             if( fs->fmt == CV_STORAGE_FORMAT_XML )
568                 icvPuts( fs, "</opencv_storage>\n" );
569         }
570
571         icvCloseFile(fs);
572     }
573
574     if( fs->outbuf && out )
575     {
576         out->resize(fs->outbuf->size());
577         std::copy(fs->outbuf->begin(), fs->outbuf->end(), out->begin());
578     }
579 }
580
581
582 /* closes file storage and deallocates buffers */
583 CV_IMPL  void
584 cvReleaseFileStorage( CvFileStorage** p_fs )
585 {
586     if( !p_fs )
587         CV_Error( CV_StsNullPtr, "NULL double pointer to file storage" );
588
589     if( *p_fs )
590     {
591         CvFileStorage* fs = *p_fs;
592         *p_fs = 0;
593
594         icvClose(fs, 0);
595
596         cvReleaseMemStorage( &fs->strstorage );
597         cvFree( &fs->buffer_start );
598         cvReleaseMemStorage( &fs->memstorage );
599
600         if( fs->outbuf )
601             delete fs->outbuf;
602
603         memset( fs, 0, sizeof(*fs) );
604         cvFree( &fs );
605     }
606 }
607
608
609 #define CV_HASHVAL_SCALE 33
610
611 CV_IMPL CvStringHashNode*
612 cvGetHashedKey( CvFileStorage* fs, const char* str, int len, int create_missing )
613 {
614     CvStringHashNode* node = 0;
615     unsigned hashval = 0;
616     int i, tab_size;
617     CvStringHash* map = fs->str_hash;
618
619     if( !fs )
620         return 0;
621
622     if( len < 0 )
623     {
624         for( i = 0; str[i] != '\0'; i++ )
625             hashval = hashval*CV_HASHVAL_SCALE + (unsigned char)str[i];
626         len = i;
627     }
628     else for( i = 0; i < len; i++ )
629         hashval = hashval*CV_HASHVAL_SCALE + (unsigned char)str[i];
630
631     hashval &= INT_MAX;
632     tab_size = map->tab_size;
633     if( (tab_size & (tab_size - 1)) == 0 )
634         i = (int)(hashval & (tab_size - 1));
635     else
636         i = (int)(hashval % tab_size);
637
638     for( node = (CvStringHashNode*)(map->table[i]); node != 0; node = node->next )
639     {
640         if( node->hashval == hashval &&
641             node->str.len == len &&
642             memcmp( node->str.ptr, str, len ) == 0 )
643             break;
644     }
645
646     if( !node && create_missing )
647     {
648         node = (CvStringHashNode*)cvSetNew( (CvSet*)map );
649         node->hashval = hashval;
650         node->str = cvMemStorageAllocString( map->storage, str, len );
651         node->next = (CvStringHashNode*)(map->table[i]);
652         map->table[i] = node;
653     }
654
655     return node;
656 }
657
658
659 CV_IMPL CvFileNode*
660 cvGetFileNode( CvFileStorage* fs, CvFileNode* _map_node,
661                const CvStringHashNode* key,
662                int create_missing )
663 {
664     CvFileNode* value = 0;
665     int k = 0, attempts = 1;
666
667     if( !fs )
668         return 0;
669
670     CV_CHECK_FILE_STORAGE(fs);
671
672     if( !key )
673         CV_Error( CV_StsNullPtr, "Null key element" );
674
675     if( _map_node )
676     {
677         if( !fs->roots )
678             return 0;
679         attempts = fs->roots->total;
680     }
681
682     for( k = 0; k < attempts; k++ )
683     {
684         int i, tab_size;
685         CvFileNode* map_node = _map_node;
686         CvFileMapNode* another;
687         CvFileNodeHash* map;
688
689         if( !map_node )
690             map_node = (CvFileNode*)cvGetSeqElem( fs->roots, k );
691
692         if( !CV_NODE_IS_MAP(map_node->tag) )
693         {
694             if( (!CV_NODE_IS_SEQ(map_node->tag) || map_node->data.seq->total != 0) &&
695                 CV_NODE_TYPE(map_node->tag) != CV_NODE_NONE )
696                 CV_Error( CV_StsError, "The node is neither a map nor an empty collection" );
697             return 0;
698         }
699
700         map = map_node->data.map;
701         tab_size = map->tab_size;
702
703         if( (tab_size & (tab_size - 1)) == 0 )
704             i = (int)(key->hashval & (tab_size - 1));
705         else
706             i = (int)(key->hashval % tab_size);
707
708         for( another = (CvFileMapNode*)(map->table[i]); another != 0; another = another->next )
709             if( another->key == key )
710             {
711                 if( !create_missing )
712                 {
713                     value = &another->value;
714                     return value;
715                 }
716                 CV_PARSE_ERROR( "Duplicated key" );
717             }
718
719         if( k == attempts - 1 && create_missing )
720         {
721             CvFileMapNode* node = (CvFileMapNode*)cvSetNew( (CvSet*)map );
722             node->key = key;
723
724             node->next = (CvFileMapNode*)(map->table[i]);
725             map->table[i] = node;
726             value = (CvFileNode*)node;
727         }
728     }
729
730     return value;
731 }
732
733
734 CV_IMPL CvFileNode*
735 cvGetFileNodeByName( const CvFileStorage* fs, const CvFileNode* _map_node, const char* str )
736 {
737     CvFileNode* value = 0;
738     int i, len, tab_size;
739     unsigned hashval = 0;
740     int k = 0, attempts = 1;
741
742     if( !fs )
743         return 0;
744
745     CV_CHECK_FILE_STORAGE(fs);
746
747     if( !str )
748         CV_Error( CV_StsNullPtr, "Null element name" );
749
750     for( i = 0; str[i] != '\0'; i++ )
751         hashval = hashval*CV_HASHVAL_SCALE + (unsigned char)str[i];
752     hashval &= INT_MAX;
753     len = i;
754
755     if( !_map_node )
756     {
757         if( !fs->roots )
758             return 0;
759         attempts = fs->roots->total;
760     }
761
762     for( k = 0; k < attempts; k++ )
763     {
764         CvFileNodeHash* map;
765         const CvFileNode* map_node = _map_node;
766         CvFileMapNode* another;
767
768         if( !map_node )
769             map_node = (CvFileNode*)cvGetSeqElem( fs->roots, k );
770
771         if( !CV_NODE_IS_MAP(map_node->tag) )
772         {
773             if( (!CV_NODE_IS_SEQ(map_node->tag) || map_node->data.seq->total != 0) &&
774                 CV_NODE_TYPE(map_node->tag) != CV_NODE_NONE )
775                 CV_Error( CV_StsError, "The node is neither a map nor an empty collection" );
776             return 0;
777         }
778
779         map = map_node->data.map;
780         tab_size = map->tab_size;
781
782         if( (tab_size & (tab_size - 1)) == 0 )
783             i = (int)(hashval & (tab_size - 1));
784         else
785             i = (int)(hashval % tab_size);
786
787         for( another = (CvFileMapNode*)(map->table[i]); another != 0; another = another->next )
788         {
789             const CvStringHashNode* key = another->key;
790
791             if( key->hashval == hashval &&
792                 key->str.len == len &&
793                 memcmp( key->str.ptr, str, len ) == 0 )
794             {
795                 value = &another->value;
796                 return value;
797             }
798         }
799     }
800
801     return value;
802 }
803
804
805 CV_IMPL CvFileNode*
806 cvGetRootFileNode( const CvFileStorage* fs, int stream_index )
807 {
808     CV_CHECK_FILE_STORAGE(fs);
809
810     if( !fs->roots || (unsigned)stream_index >= (unsigned)fs->roots->total )
811         return 0;
812
813     return (CvFileNode*)cvGetSeqElem( fs->roots, stream_index );
814 }
815
816
817 /* returns the sequence element by its index */
818 /*CV_IMPL CvFileNode*
819 cvGetFileNodeFromSeq( CvFileStorage* fs,
820                       CvFileNode* seq_node, int index )
821 {
822     CvFileNode* value = 0;
823     CvSeq* seq;
824
825     if( !seq_node )
826         seq = fs->roots;
827     else if( !CV_NODE_IS_SEQ(seq_node->tag) )
828     {
829         if( CV_NODE_IS_MAP(seq_node->tag) )
830             CV_Error( CV_StsError, "The node is map. Use cvGetFileNodeFromMap()." );
831         if( CV_NODE_TYPE(seq_node->tag) == CV_NODE_NONE )
832             CV_Error( CV_StsError, "The node is an empty object (None)." );
833         if( index != 0 && index != -1 )
834             CV_Error( CV_StsOutOfRange, "" );
835         value = seq_node;
836         EXIT;
837     }
838     else
839         seq = seq_node->data.seq;
840
841     if( !seq )
842         CV_Error( CV_StsNullPtr, "The file storage is empty" );
843
844     value = (CvFileNode*)cvGetSeqElem( seq, index, 0 );
845
846
847
848     return value;
849 }*/
850
851
852 static char*
853 icvDoubleToString( char* buf, double value )
854 {
855     Cv64suf val;
856     unsigned ieee754_hi;
857
858     val.f = value;
859     ieee754_hi = (unsigned)(val.u >> 32);
860
861     if( (ieee754_hi & 0x7ff00000) != 0x7ff00000 )
862     {
863         int ivalue = cvRound(value);
864         if( ivalue == value )
865             sprintf( buf, "%d.", ivalue );
866         else
867         {
868             static const char* fmt = "%.16e";
869             char* ptr = buf;
870             sprintf( buf, fmt, value );
871             if( *ptr == '+' || *ptr == '-' )
872                 ptr++;
873             for( ; cv_isdigit(*ptr); ptr++ )
874                 ;
875             if( *ptr == ',' )
876                 *ptr = '.';
877         }
878     }
879     else
880     {
881         unsigned ieee754_lo = (unsigned)val.u;
882         if( (ieee754_hi & 0x7fffffff) + (ieee754_lo != 0) > 0x7ff00000 )
883             strcpy( buf, ".Nan" );
884         else
885             strcpy( buf, (int)ieee754_hi < 0 ? "-.Inf" : ".Inf" );
886     }
887
888     return buf;
889 }
890
891
892 static char*
893 icvFloatToString( char* buf, float value )
894 {
895     Cv32suf val;
896     unsigned ieee754;
897     val.f = value;
898     ieee754 = val.u;
899
900     if( (ieee754 & 0x7f800000) != 0x7f800000 )
901     {
902         int ivalue = cvRound(value);
903         if( ivalue == value )
904             sprintf( buf, "%d.", ivalue );
905         else
906         {
907             static const char* fmt = "%.8e";
908             char* ptr = buf;
909             sprintf( buf, fmt, value );
910             if( *ptr == '+' || *ptr == '-' )
911                 ptr++;
912             for( ; cv_isdigit(*ptr); ptr++ )
913                 ;
914             if( *ptr == ',' )
915                 *ptr = '.';
916         }
917     }
918     else
919     {
920         if( (ieee754 & 0x7fffffff) != 0x7f800000 )
921             strcpy( buf, ".Nan" );
922         else
923             strcpy( buf, (int)ieee754 < 0 ? "-.Inf" : ".Inf" );
924     }
925
926     return buf;
927 }
928
929
930 static void
931 icvProcessSpecialDouble( CvFileStorage* fs, char* buf, double* value, char** endptr )
932 {
933     char c = buf[0];
934     int inf_hi = 0x7ff00000;
935
936     if( c == '-' || c == '+' )
937     {
938         inf_hi = c == '-' ? 0xfff00000 : 0x7ff00000;
939         c = *++buf;
940     }
941
942     if( c != '.' )
943         CV_PARSE_ERROR( "Bad format of floating-point constant" );
944
945     union{double d; uint64 i;} v;
946     v.d = 0.;
947     if( toupper(buf[1]) == 'I' && toupper(buf[2]) == 'N' && toupper(buf[3]) == 'F' )
948         v.i = (uint64)inf_hi << 32;
949     else if( toupper(buf[1]) == 'N' && toupper(buf[2]) == 'A' && toupper(buf[3]) == 'N' )
950         v.i = (uint64)-1;
951     else
952         CV_PARSE_ERROR( "Bad format of floating-point constant" );
953     *value = v.d;
954
955     *endptr = buf + 4;
956 }
957
958
959 static double icv_strtod( CvFileStorage* fs, char* ptr, char** endptr )
960 {
961     double fval = strtod( ptr, endptr );
962     if( **endptr == '.' )
963     {
964         char* dot_pos = *endptr;
965         *dot_pos = ',';
966         double fval2 = strtod( ptr, endptr );
967         *dot_pos = '.';
968         if( *endptr > dot_pos )
969             fval = fval2;
970         else
971             *endptr = dot_pos;
972     }
973
974     if( *endptr == ptr || cv_isalpha(**endptr) )
975         icvProcessSpecialDouble( fs, ptr, &fval, endptr );
976
977     return fval;
978 }
979
980
981 /****************************************************************************************\
982 *                                       YAML Parser                                      *
983 \****************************************************************************************/
984
985 static char*
986 icvYMLSkipSpaces( CvFileStorage* fs, char* ptr, int min_indent, int max_comment_indent )
987 {
988     for(;;)
989     {
990         while( *ptr == ' ' )
991             ptr++;
992         if( *ptr == '#' )
993         {
994             if( ptr - fs->buffer_start > max_comment_indent )
995                 return ptr;
996             *ptr = '\0';
997         }
998         else if( cv_isprint(*ptr) )
999         {
1000             if( ptr - fs->buffer_start < min_indent )
1001                 CV_PARSE_ERROR( "Incorrect indentation" );
1002             break;
1003         }
1004         else if( *ptr == '\0' || *ptr == '\n' || *ptr == '\r' )
1005         {
1006             int max_size = (int)(fs->buffer_end - fs->buffer_start);
1007             ptr = icvGets( fs, fs->buffer_start, max_size );
1008             if( !ptr )
1009             {
1010                 // emulate end of stream
1011                 ptr = fs->buffer_start;
1012                 ptr[0] = ptr[1] = ptr[2] = '.';
1013                 ptr[3] = '\0';
1014                 fs->dummy_eof = 1;
1015                 break;
1016             }
1017             else
1018             {
1019                 int l = (int)strlen(ptr);
1020                 if( ptr[l-1] != '\n' && ptr[l-1] != '\r' && !icvEof(fs) )
1021                     CV_PARSE_ERROR( "Too long string or a last string w/o newline" );
1022             }
1023
1024             fs->lineno++;
1025         }
1026         else
1027             CV_PARSE_ERROR( *ptr == '\t' ? "Tabs are prohibited in YAML!" : "Invalid character" );
1028     }
1029
1030     return ptr;
1031 }
1032
1033
1034 static char*
1035 icvYMLParseKey( CvFileStorage* fs, char* ptr,
1036                 CvFileNode* map_node, CvFileNode** value_placeholder )
1037 {
1038     char c;
1039     char *endptr = ptr - 1, *saveptr;
1040     CvStringHashNode* str_hash_node;
1041
1042     if( *ptr == '-' )
1043         CV_PARSE_ERROR( "Key may not start with \'-\'" );
1044
1045     do c = *++endptr;
1046     while( cv_isprint(c) && c != ':' );
1047
1048     if( c != ':' )
1049         CV_PARSE_ERROR( "Missing \':\'" );
1050
1051     saveptr = endptr + 1;
1052     do c = *--endptr;
1053     while( c == ' ' );
1054
1055     ++endptr;
1056     if( endptr == ptr )
1057         CV_PARSE_ERROR( "An empty key" );
1058
1059     str_hash_node = cvGetHashedKey( fs, ptr, (int)(endptr - ptr), 1 );
1060     *value_placeholder = cvGetFileNode( fs, map_node, str_hash_node, 1 );
1061     ptr = saveptr;
1062
1063     return ptr;
1064 }
1065
1066
1067 static char*
1068 icvYMLParseValue( CvFileStorage* fs, char* ptr, CvFileNode* node,
1069                   int parent_flags, int min_indent )
1070 {
1071     char buf[CV_FS_MAX_LEN + 1024];
1072     char* endptr = 0;
1073     char c = ptr[0], d = ptr[1];
1074     int is_parent_flow = CV_NODE_IS_FLOW(parent_flags);
1075     int value_type = CV_NODE_NONE;
1076     int len;
1077
1078     memset( node, 0, sizeof(*node) );
1079
1080     if( c == '!' ) // handle explicit type specification
1081     {
1082         if( d == '!' || d == '^' )
1083         {
1084             ptr++;
1085             value_type |= CV_NODE_USER;
1086         }
1087
1088         endptr = ptr++;
1089         do d = *++endptr;
1090         while( cv_isprint(d) && d != ' ' );
1091         len = (int)(endptr - ptr);
1092         if( len == 0 )
1093             CV_PARSE_ERROR( "Empty type name" );
1094         d = *endptr;
1095         *endptr = '\0';
1096
1097         if( len == 3 && !CV_NODE_IS_USER(value_type) )
1098         {
1099             if( memcmp( ptr, "str", 3 ) == 0 )
1100                 value_type = CV_NODE_STRING;
1101             else if( memcmp( ptr, "int", 3 ) == 0 )
1102                 value_type = CV_NODE_INT;
1103             else if( memcmp( ptr, "seq", 3 ) == 0 )
1104                 value_type = CV_NODE_SEQ;
1105             else if( memcmp( ptr, "map", 3 ) == 0 )
1106                 value_type = CV_NODE_MAP;
1107         }
1108         else if( len == 5 && !CV_NODE_IS_USER(value_type) )
1109         {
1110             if( memcmp( ptr, "float", 5 ) == 0 )
1111                 value_type = CV_NODE_REAL;
1112         }
1113         else if( CV_NODE_IS_USER(value_type) )
1114         {
1115             node->info = cvFindType( ptr );
1116             if( !node->info )
1117                 node->tag &= ~CV_NODE_USER;
1118         }
1119
1120         *endptr = d;
1121         ptr = icvYMLSkipSpaces( fs, endptr, min_indent, INT_MAX );
1122
1123         c = *ptr;
1124
1125         if( !CV_NODE_IS_USER(value_type) )
1126         {
1127             if( value_type == CV_NODE_STRING && c != '\'' && c != '\"' )
1128                 goto force_string;
1129             if( value_type == CV_NODE_INT )
1130                 goto force_int;
1131             if( value_type == CV_NODE_REAL )
1132                 goto force_real;
1133         }
1134     }
1135
1136     if( cv_isdigit(c) ||
1137         ((c == '-' || c == '+') && (cv_isdigit(d) || d == '.')) ||
1138         (c == '.' && cv_isalnum(d))) // a number
1139     {
1140         double fval;
1141         int ival;
1142         endptr = ptr + (c == '-' || c == '+');
1143         while( cv_isdigit(*endptr) )
1144             endptr++;
1145         if( *endptr == '.' || *endptr == 'e' )
1146         {
1147 force_real:
1148             fval = icv_strtod( fs, ptr, &endptr );
1149             /*if( endptr == ptr || cv_isalpha(*endptr) )
1150                 icvProcessSpecialDouble( fs, endptr, &fval, &endptr ));*/
1151
1152             node->tag = CV_NODE_REAL;
1153             node->data.f = fval;
1154         }
1155         else
1156         {
1157 force_int:
1158             ival = (int)strtol( ptr, &endptr, 0 );
1159             node->tag = CV_NODE_INT;
1160             node->data.i = ival;
1161         }
1162
1163         if( !endptr || endptr == ptr )
1164             CV_PARSE_ERROR( "Invalid numeric value (inconsistent explicit type specification?)" );
1165
1166         ptr = endptr;
1167     }
1168     else if( c == '\'' || c == '\"' ) // an explicit string
1169     {
1170         node->tag = CV_NODE_STRING;
1171         if( c == '\'' )
1172             for( len = 0; len < CV_FS_MAX_LEN; )
1173             {
1174                 c = *++ptr;
1175                 if( cv_isalnum(c) || (c != '\'' && cv_isprint(c)))
1176                     buf[len++] = c;
1177                 else if( c == '\'' )
1178                 {
1179                     c = *++ptr;
1180                     if( c != '\'' )
1181                         break;
1182                     buf[len++] = c;
1183                 }
1184                 else
1185                     CV_PARSE_ERROR( "Invalid character" );
1186             }
1187         else
1188             for( len = 0; len < CV_FS_MAX_LEN; )
1189             {
1190                 c = *++ptr;
1191                 if( cv_isalnum(c) || (c != '\\' && c != '\"' && cv_isprint(c)))
1192                     buf[len++] = c;
1193                 else if( c == '\"' )
1194                 {
1195                     ++ptr;
1196                     break;
1197                 }
1198                 else if( c == '\\' )
1199                 {
1200                     d = *++ptr;
1201                     if( d == '\'' )
1202                         buf[len++] = d;
1203                     else if( d == '\"' || d == '\\' || d == '\'' )
1204                         buf[len++] = d;
1205                     else if( d == 'n' )
1206                         buf[len++] = '\n';
1207                     else if( d == 'r' )
1208                         buf[len++] = '\r';
1209                     else if( d == 't' )
1210                         buf[len++] = '\t';
1211                     else if( d == 'x' || (cv_isdigit(d) && d < '8') )
1212                     {
1213                         int val, is_hex = d == 'x';
1214                         c = ptr[3];
1215                         ptr[3] = '\0';
1216                         val = strtol( ptr + is_hex, &endptr, is_hex ? 8 : 16 );
1217                         ptr[3] = c;
1218                         if( endptr == ptr + is_hex )
1219                             buf[len++] = 'x';
1220                         else
1221                         {
1222                             buf[len++] = (char)val;
1223                             ptr = endptr;
1224                         }
1225                     }
1226                 }
1227                 else
1228                     CV_PARSE_ERROR( "Invalid character" );
1229             }
1230
1231         if( len >= CV_FS_MAX_LEN )
1232             CV_PARSE_ERROR( "Too long string literal" );
1233
1234         node->data.str = cvMemStorageAllocString( fs->memstorage, buf, len );
1235     }
1236     else if( c == '[' || c == '{' ) // collection as a flow
1237     {
1238         int new_min_indent = min_indent + !is_parent_flow;
1239         int struct_flags = CV_NODE_FLOW + (c == '{' ? CV_NODE_MAP : CV_NODE_SEQ);
1240         int is_simple = 1;
1241
1242         icvFSCreateCollection( fs, CV_NODE_TYPE(struct_flags) +
1243                                         (node->info ? CV_NODE_USER : 0), node );
1244
1245         d = c == '[' ? ']' : '}';
1246
1247         for( ++ptr ;;)
1248         {
1249             CvFileNode* elem = 0;
1250
1251             ptr = icvYMLSkipSpaces( fs, ptr, new_min_indent, INT_MAX );
1252             if( *ptr == '}' || *ptr == ']' )
1253             {
1254                 if( *ptr != d )
1255                     CV_PARSE_ERROR( "The wrong closing bracket" );
1256                 ptr++;
1257                 break;
1258             }
1259
1260             if( node->data.seq->total != 0 )
1261             {
1262                 if( *ptr != ',' )
1263                     CV_PARSE_ERROR( "Missing , between the elements" );
1264                 ptr = icvYMLSkipSpaces( fs, ptr + 1, new_min_indent, INT_MAX );
1265             }
1266
1267             if( CV_NODE_IS_MAP(struct_flags) )
1268             {
1269                 ptr = icvYMLParseKey( fs, ptr, node, &elem );
1270                 ptr = icvYMLSkipSpaces( fs, ptr, new_min_indent, INT_MAX );
1271             }
1272             else
1273             {
1274                 if( *ptr == ']' )
1275                     break;
1276                 elem = (CvFileNode*)cvSeqPush( node->data.seq, 0 );
1277             }
1278             ptr = icvYMLParseValue( fs, ptr, elem, struct_flags, new_min_indent );
1279             if( CV_NODE_IS_MAP(struct_flags) )
1280                 elem->tag |= CV_NODE_NAMED;
1281             is_simple &= !CV_NODE_IS_COLLECTION(elem->tag);
1282         }
1283         node->data.seq->flags |= is_simple ? CV_NODE_SEQ_SIMPLE : 0;
1284     }
1285     else
1286     {
1287         int indent, struct_flags, is_simple;
1288
1289         if( is_parent_flow || c != '-' )
1290         {
1291             // implicit (one-line) string or nested block-style collection
1292             if( !is_parent_flow )
1293             {
1294                 if( c == '?' )
1295                     CV_PARSE_ERROR( "Complex keys are not supported" );
1296                 if( c == '|' || c == '>' )
1297                     CV_PARSE_ERROR( "Multi-line text literals are not supported" );
1298             }
1299
1300 force_string:
1301             endptr = ptr - 1;
1302
1303             do c = *++endptr;
1304             while( cv_isprint(c) &&
1305                    (!is_parent_flow || (c != ',' && c != '}' && c != ']')) &&
1306                    (is_parent_flow || c != ':' || value_type == CV_NODE_STRING));
1307
1308             if( endptr == ptr )
1309                 CV_PARSE_ERROR( "Invalid character" );
1310
1311             if( is_parent_flow || c != ':' )
1312             {
1313                 char* str_end = endptr;
1314                 node->tag = CV_NODE_STRING;
1315                 // strip spaces in the end of string
1316                 do c = *--str_end;
1317                 while( str_end > ptr && c == ' ' );
1318                 str_end++;
1319                 node->data.str = cvMemStorageAllocString( fs->memstorage, ptr, (int)(str_end - ptr) );
1320                 ptr = endptr;
1321                 return ptr;
1322             }
1323             struct_flags = CV_NODE_MAP;
1324         }
1325         else
1326             struct_flags = CV_NODE_SEQ;
1327
1328         icvFSCreateCollection( fs, struct_flags +
1329                     (node->info ? CV_NODE_USER : 0), node );
1330
1331         indent = (int)(ptr - fs->buffer_start);
1332         is_simple = 1;
1333
1334         for(;;)
1335         {
1336             CvFileNode* elem = 0;
1337
1338             if( CV_NODE_IS_MAP(struct_flags) )
1339             {
1340                 ptr = icvYMLParseKey( fs, ptr, node, &elem );
1341             }
1342             else
1343             {
1344                 c = *ptr++;
1345                 if( c != '-' )
1346                     CV_PARSE_ERROR( "Block sequence elements must be preceded with \'-\'" );
1347
1348                 elem = (CvFileNode*)cvSeqPush( node->data.seq, 0 );
1349             }
1350
1351             ptr = icvYMLSkipSpaces( fs, ptr, indent + 1, INT_MAX );
1352             ptr = icvYMLParseValue( fs, ptr, elem, struct_flags, indent + 1 );
1353             if( CV_NODE_IS_MAP(struct_flags) )
1354                 elem->tag |= CV_NODE_NAMED;
1355             is_simple &= !CV_NODE_IS_COLLECTION(elem->tag);
1356
1357             ptr = icvYMLSkipSpaces( fs, ptr, 0, INT_MAX );
1358             if( ptr - fs->buffer_start != indent )
1359             {
1360                 if( ptr - fs->buffer_start < indent )
1361                     break;
1362                 else
1363                     CV_PARSE_ERROR( "Incorrect indentation" );
1364             }
1365             if( memcmp( ptr, "...", 3 ) == 0 )
1366                 break;
1367         }
1368
1369         node->data.seq->flags |= is_simple ? CV_NODE_SEQ_SIMPLE : 0;
1370     }
1371
1372     return ptr;
1373 }
1374
1375
1376 static void
1377 icvYMLParse( CvFileStorage* fs )
1378 {
1379     char* ptr = fs->buffer_start;
1380     int is_first = 1;
1381
1382     for(;;)
1383     {
1384         // 0. skip leading comments and directives  and ...
1385         // 1. reach the first item
1386         for(;;)
1387         {
1388             ptr = icvYMLSkipSpaces( fs, ptr, 0, INT_MAX );
1389             if( !ptr )
1390                 return;
1391
1392             if( *ptr == '%' )
1393             {
1394                 if( memcmp( ptr, "%YAML:", 6 ) == 0 &&
1395                     memcmp( ptr, "%YAML:1.", 8 ) != 0 )
1396                     CV_PARSE_ERROR( "Unsupported YAML version (it must be 1.x)" );
1397                 *ptr = '\0';
1398             }
1399             else if( *ptr == '-' )
1400             {
1401                 if( memcmp(ptr, "---", 3) == 0 )
1402                 {
1403                     ptr += 3;
1404                     break;
1405                 }
1406                 else if( is_first )
1407                     break;
1408             }
1409             else if( cv_isalnum(*ptr) || *ptr=='_')
1410             {
1411                 if( !is_first )
1412                     CV_PARSE_ERROR( "The YAML streams must start with '---', except the first one" );
1413                 break;
1414             }
1415             else if( fs->dummy_eof )
1416                 break;
1417             else
1418                 CV_PARSE_ERROR( "Invalid or unsupported syntax" );
1419         }
1420
1421         ptr = icvYMLSkipSpaces( fs, ptr, 0, INT_MAX );
1422         if( memcmp( ptr, "...", 3 ) != 0 )
1423         {
1424             // 2. parse the collection
1425             CvFileNode* root_node = (CvFileNode*)cvSeqPush( fs->roots, 0 );
1426
1427             ptr = icvYMLParseValue( fs, ptr, root_node, CV_NODE_NONE, 0 );
1428             if( !CV_NODE_IS_COLLECTION(root_node->tag) )
1429                 CV_PARSE_ERROR( "Only collections as YAML streams are supported by this parser" );
1430
1431             // 3. parse until the end of file or next collection
1432             ptr = icvYMLSkipSpaces( fs, ptr, 0, INT_MAX );
1433             if( !ptr )
1434                 return;
1435         }
1436
1437         if( fs->dummy_eof )
1438             break;
1439         ptr += 3;
1440         is_first = 0;
1441     }
1442 }
1443
1444
1445 /****************************************************************************************\
1446 *                                       YAML Emitter                                     *
1447 \****************************************************************************************/
1448
1449 static void
1450 icvYMLWrite( CvFileStorage* fs, const char* key, const char* data )
1451 {
1452     int i, keylen = 0;
1453     int datalen = 0;
1454     int struct_flags;
1455     char* ptr;
1456
1457     struct_flags = fs->struct_flags;
1458
1459     if( key && key[0] == '\0' )
1460         key = 0;
1461
1462     if( CV_NODE_IS_COLLECTION(struct_flags) )
1463     {
1464         if( (CV_NODE_IS_MAP(struct_flags) ^ (key != 0)) )
1465             CV_Error( CV_StsBadArg, "An attempt to add element without a key to a map, "
1466                                     "or add element with key to sequence" );
1467     }
1468     else
1469     {
1470         fs->is_first = 0;
1471         struct_flags = CV_NODE_EMPTY | (key ? CV_NODE_MAP : CV_NODE_SEQ);
1472     }
1473
1474     if( key )
1475     {
1476         keylen = (int)strlen(key);
1477         if( keylen == 0 )
1478             CV_Error( CV_StsBadArg, "The key is an empty" );
1479
1480         if( keylen > CV_FS_MAX_LEN )
1481             CV_Error( CV_StsBadArg, "The key is too long" );
1482     }
1483
1484     if( data )
1485         datalen = (int)strlen(data);
1486
1487     if( CV_NODE_IS_FLOW(struct_flags) )
1488     {
1489         int new_offset;
1490         ptr = fs->buffer;
1491         if( !CV_NODE_IS_EMPTY(struct_flags) )
1492             *ptr++ = ',';
1493         new_offset = (int)(ptr - fs->buffer_start) + keylen + datalen;
1494         if( new_offset > fs->wrap_margin && new_offset - fs->struct_indent > 10 )
1495         {
1496             fs->buffer = ptr;
1497             ptr = icvFSFlush(fs);
1498         }
1499         else
1500             *ptr++ = ' ';
1501     }
1502     else
1503     {
1504         ptr = icvFSFlush(fs);
1505         if( !CV_NODE_IS_MAP(struct_flags) )
1506         {
1507             *ptr++ = '-';
1508             if( data )
1509                 *ptr++ = ' ';
1510         }
1511     }
1512
1513     if( key )
1514     {
1515         if( !cv_isalpha(key[0]) && key[0] != '_' )
1516             CV_Error( CV_StsBadArg, "Key must start with a letter or _" );
1517
1518         ptr = icvFSResizeWriteBuffer( fs, ptr, keylen );
1519
1520         for( i = 0; i < keylen; i++ )
1521         {
1522             char c = key[i];
1523
1524             ptr[i] = c;
1525             if( !cv_isalnum(c) && c != '-' && c != '_' && c != ' ' )
1526                 CV_Error( CV_StsBadArg, "Key names may only contain alphanumeric characters [a-zA-Z0-9], '-', '_' and ' '" );
1527         }
1528
1529         ptr += keylen;
1530         *ptr++ = ':';
1531         if( !CV_NODE_IS_FLOW(struct_flags) && data )
1532             *ptr++ = ' ';
1533     }
1534
1535     if( data )
1536     {
1537         ptr = icvFSResizeWriteBuffer( fs, ptr, datalen );
1538         memcpy( ptr, data, datalen );
1539         ptr += datalen;
1540     }
1541
1542     fs->buffer = ptr;
1543     fs->struct_flags = struct_flags & ~CV_NODE_EMPTY;
1544 }
1545
1546
1547 static void
1548 icvYMLStartWriteStruct( CvFileStorage* fs, const char* key, int struct_flags,
1549                         const char* type_name CV_DEFAULT(0))
1550 {
1551     int parent_flags;
1552     char buf[CV_FS_MAX_LEN + 1024];
1553     const char* data = 0;
1554
1555     struct_flags = (struct_flags & (CV_NODE_TYPE_MASK|CV_NODE_FLOW)) | CV_NODE_EMPTY;
1556     if( !CV_NODE_IS_COLLECTION(struct_flags))
1557         CV_Error( CV_StsBadArg,
1558         "Some collection type - CV_NODE_SEQ or CV_NODE_MAP, must be specified" );
1559
1560     if( CV_NODE_IS_FLOW(struct_flags) )
1561     {
1562         char c = CV_NODE_IS_MAP(struct_flags) ? '{' : '[';
1563         struct_flags |= CV_NODE_FLOW;
1564
1565         if( type_name )
1566             sprintf( buf, "!!%s %c", type_name, c );
1567         else
1568         {
1569             buf[0] = c;
1570             buf[1] = '\0';
1571         }
1572         data = buf;
1573     }
1574     else if( type_name )
1575     {
1576         sprintf( buf, "!!%s", type_name );
1577         data = buf;
1578     }
1579
1580     icvYMLWrite( fs, key, data );
1581
1582     parent_flags = fs->struct_flags;
1583     cvSeqPush( fs->write_stack, &parent_flags );
1584     fs->struct_flags = struct_flags;
1585
1586     if( !CV_NODE_IS_FLOW(parent_flags) )
1587         fs->struct_indent += CV_YML_INDENT + CV_NODE_IS_FLOW(struct_flags);
1588 }
1589
1590
1591 static void
1592 icvYMLEndWriteStruct( CvFileStorage* fs )
1593 {
1594     int parent_flags = 0, struct_flags;
1595     char* ptr;
1596
1597     struct_flags = fs->struct_flags;
1598     if( fs->write_stack->total == 0 )
1599         CV_Error( CV_StsError, "EndWriteStruct w/o matching StartWriteStruct" );
1600
1601     cvSeqPop( fs->write_stack, &parent_flags );
1602
1603     if( CV_NODE_IS_FLOW(struct_flags) )
1604     {
1605         ptr = fs->buffer;
1606         if( ptr > fs->buffer_start + fs->struct_indent && !CV_NODE_IS_EMPTY(struct_flags) )
1607             *ptr++ = ' ';
1608         *ptr++ = CV_NODE_IS_MAP(struct_flags) ? '}' : ']';
1609         fs->buffer = ptr;
1610     }
1611     else if( CV_NODE_IS_EMPTY(struct_flags) )
1612     {
1613         ptr = icvFSFlush(fs);
1614         memcpy( ptr, CV_NODE_IS_MAP(struct_flags) ? "{}" : "[]", 2 );
1615         fs->buffer = ptr + 2;
1616     }
1617
1618     if( !CV_NODE_IS_FLOW(parent_flags) )
1619         fs->struct_indent -= CV_YML_INDENT + CV_NODE_IS_FLOW(struct_flags);
1620     assert( fs->struct_indent >= 0 );
1621
1622     fs->struct_flags = parent_flags;
1623 }
1624
1625
1626 static void
1627 icvYMLStartNextStream( CvFileStorage* fs )
1628 {
1629     if( !fs->is_first )
1630     {
1631         while( fs->write_stack->total > 0 )
1632             icvYMLEndWriteStruct(fs);
1633
1634         fs->struct_indent = 0;
1635         icvFSFlush(fs);
1636         icvPuts( fs, "...\n" );
1637         icvPuts( fs, "---\n" );
1638         fs->buffer = fs->buffer_start;
1639     }
1640 }
1641
1642
1643 static void
1644 icvYMLWriteInt( CvFileStorage* fs, const char* key, int value )
1645 {
1646     char buf[128];
1647     icvYMLWrite( fs, key, icv_itoa( value, buf, 10 ));
1648 }
1649
1650
1651 static void
1652 icvYMLWriteReal( CvFileStorage* fs, const char* key, double value )
1653 {
1654     char buf[128];
1655     icvYMLWrite( fs, key, icvDoubleToString( buf, value ));
1656 }
1657
1658
1659 static void
1660 icvYMLWriteString( CvFileStorage* fs, const char* key,
1661                    const char* str, int quote CV_DEFAULT(0))
1662 {
1663     char buf[CV_FS_MAX_LEN*4+16];
1664     char* data = (char*)str;
1665     int i, len;
1666
1667     if( !str )
1668         CV_Error( CV_StsNullPtr, "Null string pointer" );
1669
1670     len = (int)strlen(str);
1671     if( len > CV_FS_MAX_LEN )
1672         CV_Error( CV_StsBadArg, "The written string is too long" );
1673
1674     if( quote || len == 0 || str[0] != str[len-1] || (str[0] != '\"' && str[0] != '\'') )
1675     {
1676         int need_quote = quote || len == 0;
1677         data = buf;
1678         *data++ = '\"';
1679         for( i = 0; i < len; i++ )
1680         {
1681             char c = str[i];
1682
1683             if( !need_quote && !cv_isalnum(c) && c != '_' && c != ' ' && c != '-' &&
1684                 c != '(' && c != ')' && c != '/' && c != '+' && c != ';' )
1685                 need_quote = 1;
1686
1687             if( !cv_isalnum(c) && (!cv_isprint(c) || c == '\\' || c == '\'' || c == '\"') )
1688             {
1689                 *data++ = '\\';
1690                 if( cv_isprint(c) )
1691                     *data++ = c;
1692                 else if( c == '\n' )
1693                     *data++ = 'n';
1694                 else if( c == '\r' )
1695                     *data++ = 'r';
1696                 else if( c == '\t' )
1697                     *data++ = 't';
1698                 else
1699                 {
1700                     sprintf( data, "x%02x", c );
1701                     data += 3;
1702                 }
1703             }
1704             else
1705                 *data++ = c;
1706         }
1707         if( !need_quote && (cv_isdigit(str[0]) ||
1708             str[0] == '+' || str[0] == '-' || str[0] == '.' ))
1709             need_quote = 1;
1710
1711         if( need_quote )
1712             *data++ = '\"';
1713         *data++ = '\0';
1714         data = buf + !need_quote;
1715     }
1716
1717     icvYMLWrite( fs, key, data );
1718 }
1719
1720
1721 static void
1722 icvYMLWriteComment( CvFileStorage* fs, const char* comment, int eol_comment )
1723 {
1724     int len; //, indent;
1725     int multiline;
1726     const char* eol;
1727     char* ptr;
1728
1729     if( !comment )
1730         CV_Error( CV_StsNullPtr, "Null comment" );
1731
1732     len = (int)strlen(comment);
1733     eol = strchr(comment, '\n');
1734     multiline = eol != 0;
1735     ptr = fs->buffer;
1736
1737     if( !eol_comment || multiline ||
1738         fs->buffer_end - ptr < len || ptr == fs->buffer_start )
1739         ptr = icvFSFlush( fs );
1740     else
1741         *ptr++ = ' ';
1742
1743     while( comment )
1744     {
1745         *ptr++ = '#';
1746         *ptr++ = ' ';
1747         if( eol )
1748         {
1749             ptr = icvFSResizeWriteBuffer( fs, ptr, (int)(eol - comment) + 1 );
1750             memcpy( ptr, comment, eol - comment + 1 );
1751             fs->buffer = ptr + (eol - comment);
1752             comment = eol + 1;
1753             eol = strchr( comment, '\n' );
1754         }
1755         else
1756         {
1757             len = (int)strlen(comment);
1758             ptr = icvFSResizeWriteBuffer( fs, ptr, len );
1759             memcpy( ptr, comment, len );
1760             fs->buffer = ptr + len;
1761             comment = 0;
1762         }
1763         ptr = icvFSFlush( fs );
1764     }
1765 }
1766
1767
1768 /****************************************************************************************\
1769 *                                       XML Parser                                       *
1770 \****************************************************************************************/
1771
1772 #define CV_XML_INSIDE_COMMENT 1
1773 #define CV_XML_INSIDE_TAG 2
1774 #define CV_XML_INSIDE_DIRECTIVE 3
1775
1776 static char*
1777 icvXMLSkipSpaces( CvFileStorage* fs, char* ptr, int mode )
1778 {
1779     int level = 0;
1780
1781     for(;;)
1782     {
1783         char c;
1784         ptr--;
1785
1786         if( mode == CV_XML_INSIDE_COMMENT )
1787         {
1788             do c = *++ptr;
1789             while( cv_isprint_or_tab(c) && (c != '-' || ptr[1] != '-' || ptr[2] != '>') );
1790
1791             if( c == '-' )
1792             {
1793                 assert( ptr[1] == '-' && ptr[2] == '>' );
1794                 mode = 0;
1795                 ptr += 3;
1796             }
1797         }
1798         else if( mode == CV_XML_INSIDE_DIRECTIVE )
1799         {
1800             // !!!NOTE!!! This is not quite correct, but should work in most cases
1801             do
1802             {
1803                 c = *++ptr;
1804                 level += c == '<';
1805                 level -= c == '>';
1806                 if( level < 0 )
1807                     return ptr;
1808             } while( cv_isprint_or_tab(c) );
1809         }
1810         else
1811         {
1812             do c = *++ptr;
1813             while( c == ' ' || c == '\t' );
1814
1815             if( c == '<' && ptr[1] == '!' && ptr[2] == '-' && ptr[3] == '-' )
1816             {
1817                 if( mode != 0 )
1818                     CV_PARSE_ERROR( "Comments are not allowed here" );
1819                 mode = CV_XML_INSIDE_COMMENT;
1820                 ptr += 4;
1821             }
1822             else if( cv_isprint(c) )
1823                 break;
1824         }
1825
1826         if( !cv_isprint(*ptr) )
1827         {
1828             int max_size = (int)(fs->buffer_end - fs->buffer_start);
1829             if( *ptr != '\0' && *ptr != '\n' && *ptr != '\r' )
1830                 CV_PARSE_ERROR( "Invalid character in the stream" );
1831             ptr = icvGets( fs, fs->buffer_start, max_size );
1832             if( !ptr )
1833             {
1834                 ptr = fs->buffer_start;
1835                 *ptr = '\0';
1836                 fs->dummy_eof = 1;
1837                 break;
1838             }
1839             else
1840             {
1841                 int l = (int)strlen(ptr);
1842                 if( ptr[l-1] != '\n' && ptr[l-1] != '\r' && !icvEof(fs) )
1843                     CV_PARSE_ERROR( "Too long string or a last string w/o newline" );
1844             }
1845             fs->lineno++;
1846         }
1847     }
1848     return ptr;
1849 }
1850
1851
1852 static char*
1853 icvXMLParseTag( CvFileStorage* fs, char* ptr, CvStringHashNode** _tag,
1854                 CvAttrList** _list, int* _tag_type );
1855
1856 static char*
1857 icvXMLParseValue( CvFileStorage* fs, char* ptr, CvFileNode* node,
1858                   int value_type CV_DEFAULT(CV_NODE_NONE))
1859 {
1860     CvFileNode *elem = node;
1861     int have_space = 1, is_simple = 1;
1862     int is_user_type = CV_NODE_IS_USER(value_type);
1863     memset( node, 0, sizeof(*node) );
1864
1865     value_type = CV_NODE_TYPE(value_type);
1866
1867     for(;;)
1868     {
1869         char c = *ptr, d;
1870         char* endptr;
1871
1872         if( cv_isspace(c) || c == '\0' || (c == '<' && ptr[1] == '!' && ptr[2] == '-') )
1873         {
1874             ptr = icvXMLSkipSpaces( fs, ptr, 0 );
1875             have_space = 1;
1876             c = *ptr;
1877         }
1878
1879         d = ptr[1];
1880
1881         if( c =='<' || c == '\0' )
1882         {
1883             CvStringHashNode *key = 0, *key2 = 0;
1884             CvAttrList* list = 0;
1885             CvTypeInfo* info = 0;
1886             int tag_type = 0;
1887             int is_noname = 0;
1888             const char* type_name = 0;
1889             int elem_type = CV_NODE_NONE;
1890
1891             if( d == '/' || c == '\0' )
1892                 break;
1893
1894             ptr = icvXMLParseTag( fs, ptr, &key, &list, &tag_type );
1895
1896             if( tag_type == CV_XML_DIRECTIVE_TAG )
1897                 CV_PARSE_ERROR( "Directive tags are not allowed here" );
1898             if( tag_type == CV_XML_EMPTY_TAG )
1899                 CV_PARSE_ERROR( "Empty tags are not supported" );
1900
1901             assert( tag_type == CV_XML_OPENING_TAG );
1902
1903             type_name = list ? cvAttrValue( list, "type_id" ) : 0;
1904             if( type_name )
1905             {
1906                 if( strcmp( type_name, "str" ) == 0 )
1907                     elem_type = CV_NODE_STRING;
1908                 else if( strcmp( type_name, "map" ) == 0 )
1909                     elem_type = CV_NODE_MAP;
1910                 else if( strcmp( type_name, "seq" ) == 0 )
1911                     elem_type = CV_NODE_SEQ;
1912                 else
1913                 {
1914                     info = cvFindType( type_name );
1915                     if( info )
1916                         elem_type = CV_NODE_USER;
1917                 }
1918             }
1919
1920             is_noname = key->str.len == 1 && key->str.ptr[0] == '_';
1921             if( !CV_NODE_IS_COLLECTION(node->tag) )
1922             {
1923                 icvFSCreateCollection( fs, is_noname ? CV_NODE_SEQ : CV_NODE_MAP, node );
1924             }
1925             else if( is_noname ^ CV_NODE_IS_SEQ(node->tag) )
1926                 CV_PARSE_ERROR( is_noname ? "Map element should have a name" :
1927                               "Sequence element should not have name (use <_></_>)" );
1928
1929             if( is_noname )
1930                 elem = (CvFileNode*)cvSeqPush( node->data.seq, 0 );
1931             else
1932                 elem = cvGetFileNode( fs, node, key, 1 );
1933
1934             ptr = icvXMLParseValue( fs, ptr, elem, elem_type);
1935             if( !is_noname )
1936                 elem->tag |= CV_NODE_NAMED;
1937             is_simple &= !CV_NODE_IS_COLLECTION(elem->tag);
1938             elem->info = info;
1939             ptr = icvXMLParseTag( fs, ptr, &key2, &list, &tag_type );
1940             if( tag_type != CV_XML_CLOSING_TAG || key2 != key )
1941                 CV_PARSE_ERROR( "Mismatched closing tag" );
1942             have_space = 1;
1943         }
1944         else
1945         {
1946             if( !have_space )
1947                 CV_PARSE_ERROR( "There should be space between literals" );
1948
1949             elem = node;
1950             if( node->tag != CV_NODE_NONE )
1951             {
1952                 if( !CV_NODE_IS_COLLECTION(node->tag) )
1953                     icvFSCreateCollection( fs, CV_NODE_SEQ, node );
1954
1955                 elem = (CvFileNode*)cvSeqPush( node->data.seq, 0 );
1956                 elem->info = 0;
1957             }
1958
1959             if( value_type != CV_NODE_STRING &&
1960                 (cv_isdigit(c) || ((c == '-' || c == '+') &&
1961                 (cv_isdigit(d) || d == '.')) || (c == '.' && cv_isalnum(d))) ) // a number
1962             {
1963                 double fval;
1964                 int ival;
1965                 endptr = ptr + (c == '-' || c == '+');
1966                 while( cv_isdigit(*endptr) )
1967                     endptr++;
1968                 if( *endptr == '.' || *endptr == 'e' )
1969                 {
1970                     fval = icv_strtod( fs, ptr, &endptr );
1971                     /*if( endptr == ptr || cv_isalpha(*endptr) )
1972                         icvProcessSpecialDouble( fs, ptr, &fval, &endptr ));*/
1973                     elem->tag = CV_NODE_REAL;
1974                     elem->data.f = fval;
1975                 }
1976                 else
1977                 {
1978                     ival = (int)strtol( ptr, &endptr, 0 );
1979                     elem->tag = CV_NODE_INT;
1980                     elem->data.i = ival;
1981                 }
1982
1983                 if( endptr == ptr )
1984                     CV_PARSE_ERROR( "Invalid numeric value (inconsistent explicit type specification?)" );
1985
1986                 ptr = endptr;
1987             }
1988             else
1989             {
1990                 // string
1991                 char buf[CV_FS_MAX_LEN+16];
1992                 int i = 0, len, is_quoted = 0;
1993                 elem->tag = CV_NODE_STRING;
1994                 if( c == '\"' )
1995                     is_quoted = 1;
1996                 else
1997                     --ptr;
1998
1999                 for( ;; )
2000                 {
2001                     c = *++ptr;
2002                     if( !cv_isalnum(c) )
2003                     {
2004                         if( c == '\"' )
2005                         {
2006                             if( !is_quoted )
2007                                 CV_PARSE_ERROR( "Literal \" is not allowed within a string. Use &quot;" );
2008                             ++ptr;
2009                             break;
2010                         }
2011                         else if( !cv_isprint(c) || c == '<' || (!is_quoted && cv_isspace(c)))
2012                         {
2013                             if( is_quoted )
2014                                 CV_PARSE_ERROR( "Closing \" is expected" );
2015                             break;
2016                         }
2017                         else if( c == '\'' || c == '>' )
2018                         {
2019                             CV_PARSE_ERROR( "Literal \' or > are not allowed. Use &apos; or &gt;" );
2020                         }
2021                         else if( c == '&' )
2022                         {
2023                             if( *++ptr == '#' )
2024                             {
2025                                 int val, base = 10;
2026                                 ptr++;
2027                                 if( *ptr == 'x' )
2028                                 {
2029                                     base = 16;
2030                                     ptr++;
2031                                 }
2032                                 val = (int)strtol( ptr, &endptr, base );
2033                                 if( (unsigned)val > (unsigned)255 ||
2034                                     !endptr || *endptr != ';' )
2035                                     CV_PARSE_ERROR( "Invalid numeric value in the string" );
2036                                 c = (char)val;
2037                             }
2038                             else
2039                             {
2040                                 endptr = ptr;
2041                                 do c = *++endptr;
2042                                 while( cv_isalnum(c) );
2043                                 if( c != ';' )
2044                                     CV_PARSE_ERROR( "Invalid character in the symbol entity name" );
2045                                 len = (int)(endptr - ptr);
2046                                 if( len == 2 && memcmp( ptr, "lt", len ) == 0 )
2047                                     c = '<';
2048                                 else if( len == 2 && memcmp( ptr, "gt", len ) == 0 )
2049                                     c = '>';
2050                                 else if( len == 3 && memcmp( ptr, "amp", len ) == 0 )
2051                                     c = '&';
2052                                 else if( len == 4 && memcmp( ptr, "apos", len ) == 0 )
2053                                     c = '\'';
2054                                 else if( len == 4 && memcmp( ptr, "quot", len ) == 0 )
2055                                     c = '\"';
2056                                 else
2057                                 {
2058                                     memcpy( buf + i, ptr-1, len + 2 );
2059                                     i += len + 2;
2060                                 }
2061                             }
2062                             ptr = endptr;
2063                         }
2064                     }
2065                     buf[i++] = c;
2066                     if( i >= CV_FS_MAX_LEN )
2067                         CV_PARSE_ERROR( "Too long string literal" );
2068                 }
2069                 elem->data.str = cvMemStorageAllocString( fs->memstorage, buf, i );
2070             }
2071
2072             if( !CV_NODE_IS_COLLECTION(value_type) && value_type != CV_NODE_NONE )
2073                 break;
2074             have_space = 0;
2075         }
2076     }
2077
2078     if( (CV_NODE_TYPE(node->tag) == CV_NODE_NONE ||
2079         (CV_NODE_TYPE(node->tag) != value_type &&
2080         !CV_NODE_IS_COLLECTION(node->tag))) &&
2081         CV_NODE_IS_COLLECTION(value_type) )
2082     {
2083         icvFSCreateCollection( fs, CV_NODE_IS_MAP(value_type) ?
2084                                         CV_NODE_MAP : CV_NODE_SEQ, node );
2085     }
2086
2087     if( value_type != CV_NODE_NONE &&
2088         value_type != CV_NODE_TYPE(node->tag) )
2089         CV_PARSE_ERROR( "The actual type is different from the specified type" );
2090
2091     if( CV_NODE_IS_COLLECTION(node->tag) && is_simple )
2092             node->data.seq->flags |= CV_NODE_SEQ_SIMPLE;
2093
2094     node->tag |= is_user_type ? CV_NODE_USER : 0;
2095     return ptr;
2096 }
2097
2098
2099 static char*
2100 icvXMLParseTag( CvFileStorage* fs, char* ptr, CvStringHashNode** _tag,
2101                 CvAttrList** _list, int* _tag_type )
2102 {
2103     int tag_type = 0;
2104     CvStringHashNode* tagname = 0;
2105     CvAttrList *first = 0, *last = 0;
2106     int count = 0, max_count = 4;
2107     int attr_buf_size = (max_count*2 + 1)*sizeof(char*) + sizeof(CvAttrList);
2108     char* endptr;
2109     char c;
2110     int have_space;
2111
2112     if( *ptr == '\0' )
2113         CV_PARSE_ERROR( "Preliminary end of the stream" );
2114
2115     if( *ptr != '<' )
2116         CV_PARSE_ERROR( "Tag should start with \'<\'" );
2117
2118     ptr++;
2119     if( cv_isalnum(*ptr) || *ptr == '_' )
2120         tag_type = CV_XML_OPENING_TAG;
2121     else if( *ptr == '/' )
2122     {
2123         tag_type = CV_XML_CLOSING_TAG;
2124         ptr++;
2125     }
2126     else if( *ptr == '?' )
2127     {
2128         tag_type = CV_XML_HEADER_TAG;
2129         ptr++;
2130     }
2131     else if( *ptr == '!' )
2132     {
2133         tag_type = CV_XML_DIRECTIVE_TAG;
2134         assert( ptr[1] != '-' || ptr[2] != '-' );
2135         ptr++;
2136     }
2137     else
2138         CV_PARSE_ERROR( "Unknown tag type" );
2139
2140     for(;;)
2141     {
2142         CvStringHashNode* attrname;
2143
2144         if( !cv_isalpha(*ptr) && *ptr != '_' )
2145             CV_PARSE_ERROR( "Name should start with a letter or underscore" );
2146
2147         endptr = ptr - 1;
2148         do c = *++endptr;
2149         while( cv_isalnum(c) || c == '_' || c == '-' );
2150
2151         attrname = cvGetHashedKey( fs, ptr, (int)(endptr - ptr), 1 );
2152         ptr = endptr;
2153
2154         if( !tagname )
2155             tagname = attrname;
2156         else
2157         {
2158             if( tag_type == CV_XML_CLOSING_TAG )
2159                 CV_PARSE_ERROR( "Closing tag should not contain any attributes" );
2160
2161             if( !last || count >= max_count )
2162             {
2163                 CvAttrList* chunk;
2164
2165                 chunk = (CvAttrList*)cvMemStorageAlloc( fs->memstorage, attr_buf_size );
2166                 memset( chunk, 0, attr_buf_size );
2167                 chunk->attr = (const char**)(chunk + 1);
2168                 count = 0;
2169                 if( !last )
2170                     first = last = chunk;
2171                 else
2172                     last = last->next = chunk;
2173             }
2174             last->attr[count*2] = attrname->str.ptr;
2175         }
2176
2177         if( last )
2178         {
2179             CvFileNode stub;
2180
2181             if( *ptr != '=' )
2182             {
2183                 ptr = icvXMLSkipSpaces( fs, ptr, CV_XML_INSIDE_TAG );
2184                 if( *ptr != '=' )
2185                     CV_PARSE_ERROR( "Attribute name should be followed by \'=\'" );
2186             }
2187
2188             c = *++ptr;
2189             if( c != '\"' && c != '\'' )
2190             {
2191                 ptr = icvXMLSkipSpaces( fs, ptr, CV_XML_INSIDE_TAG );
2192                 if( *ptr != '\"' && *ptr != '\'' )
2193                     CV_PARSE_ERROR( "Attribute value should be put into single or double quotes" );
2194             }
2195
2196             ptr = icvXMLParseValue( fs, ptr, &stub, CV_NODE_STRING );
2197             assert( stub.tag == CV_NODE_STRING );
2198             last->attr[count*2+1] = stub.data.str.ptr;
2199             count++;
2200         }
2201
2202         c = *ptr;
2203         have_space = cv_isspace(c) || c == '\0';
2204
2205         if( c != '>' )
2206         {
2207             ptr = icvXMLSkipSpaces( fs, ptr, CV_XML_INSIDE_TAG );
2208             c = *ptr;
2209         }
2210
2211         if( c == '>' )
2212         {
2213             if( tag_type == CV_XML_HEADER_TAG )
2214                 CV_PARSE_ERROR( "Invalid closing tag for <?xml ..." );
2215             ptr++;
2216             break;
2217         }
2218         else if( c == '?' && tag_type == CV_XML_HEADER_TAG )
2219         {
2220             if( ptr[1] != '>'  )
2221                 CV_PARSE_ERROR( "Invalid closing tag for <?xml ..." );
2222             ptr += 2;
2223             break;
2224         }
2225         else if( c == '/' && ptr[1] == '>' && tag_type == CV_XML_OPENING_TAG )
2226         {
2227             tag_type = CV_XML_EMPTY_TAG;
2228             ptr += 2;
2229             break;
2230         }
2231
2232         if( !have_space )
2233             CV_PARSE_ERROR( "There should be space between attributes" );
2234     }
2235
2236     *_tag = tagname;
2237     *_tag_type = tag_type;
2238     *_list = first;
2239
2240     return ptr;
2241 }
2242
2243
2244 static void
2245 icvXMLParse( CvFileStorage* fs )
2246 {
2247     char* ptr = fs->buffer_start;
2248     CvStringHashNode *key = 0, *key2 = 0;
2249     CvAttrList* list = 0;
2250     int tag_type = 0;
2251
2252     // CV_XML_INSIDE_TAG is used to prohibit leading comments
2253     ptr = icvXMLSkipSpaces( fs, ptr, CV_XML_INSIDE_TAG );
2254
2255     if( memcmp( ptr, "<?xml", 5 ) != 0 )
2256
2257         CV_PARSE_ERROR( "Valid XML should start with \'<?xml ...?>\'" );
2258
2259     ptr = icvXMLParseTag( fs, ptr, &key, &list, &tag_type );
2260
2261     /*{
2262         const char* version = cvAttrValue( list, "version" );
2263         if( version && strncmp( version, "1.", 2 ) != 0 )
2264             CV_Error( CV_StsParseError, "Unsupported version of XML" );
2265     }*/
2266     // we support any 8-bit encoding, so we do not need to check the actual encoding.
2267     // we do not support utf-16, but in the case of utf-16 we will not get here anyway.
2268     /*{
2269         const char* encoding = cvAttrValue( list, "encoding" );
2270         if( encoding && strcmp( encoding, "ASCII" ) != 0 &&
2271             strcmp( encoding, "UTF-8" ) != 0 &&
2272             strcmp( encoding, "utf-8" ) != 0 )
2273             CV_PARSE_ERROR( "Unsupported encoding" );
2274     }*/
2275
2276     while( *ptr != '\0' )
2277     {
2278         ptr = icvXMLSkipSpaces( fs, ptr, 0 );
2279
2280         if( *ptr != '\0' )
2281         {
2282             CvFileNode* root_node;
2283             ptr = icvXMLParseTag( fs, ptr, &key, &list, &tag_type );
2284             if( tag_type != CV_XML_OPENING_TAG ||
2285                 strcmp(key->str.ptr,"opencv_storage") != 0 )
2286                 CV_PARSE_ERROR( "<opencv_storage> tag is missing" );
2287
2288             root_node = (CvFileNode*)cvSeqPush( fs->roots, 0 );
2289             ptr = icvXMLParseValue( fs, ptr, root_node, CV_NODE_NONE );
2290             ptr = icvXMLParseTag( fs, ptr, &key2, &list, &tag_type );
2291             if( tag_type != CV_XML_CLOSING_TAG || key != key2 )
2292                 CV_PARSE_ERROR( "</opencv_storage> tag is missing" );
2293             ptr = icvXMLSkipSpaces( fs, ptr, 0 );
2294         }
2295     }
2296
2297     assert( fs->dummy_eof != 0 );
2298 }
2299
2300
2301 /****************************************************************************************\
2302 *                                       XML Emitter                                      *
2303 \****************************************************************************************/
2304
2305 #define icvXMLFlush icvFSFlush
2306
2307 static void
2308 icvXMLWriteTag( CvFileStorage* fs, const char* key, int tag_type, CvAttrList list )
2309 {
2310     char* ptr = fs->buffer;
2311     int i, len = 0;
2312     int struct_flags = fs->struct_flags;
2313
2314     if( key && key[0] == '\0' )
2315         key = 0;
2316
2317     if( tag_type == CV_XML_OPENING_TAG || tag_type == CV_XML_EMPTY_TAG )
2318     {
2319         if( CV_NODE_IS_COLLECTION(struct_flags) )
2320         {
2321             if( CV_NODE_IS_MAP(struct_flags) ^ (key != 0) )
2322                 CV_Error( CV_StsBadArg, "An attempt to add element without a key to a map, "
2323                                         "or add element with key to sequence" );
2324         }
2325         else
2326         {
2327             struct_flags = CV_NODE_EMPTY + (key ? CV_NODE_MAP : CV_NODE_SEQ);
2328             fs->is_first = 0;
2329         }
2330
2331         if( !CV_NODE_IS_EMPTY(struct_flags) )
2332             ptr = icvXMLFlush(fs);
2333     }
2334
2335     if( !key )
2336         key = "_";
2337     else if( key[0] == '_' && key[1] == '\0' )
2338         CV_Error( CV_StsBadArg, "A single _ is a reserved tag name" );
2339
2340     len = (int)strlen( key );
2341     *ptr++ = '<';
2342     if( tag_type == CV_XML_CLOSING_TAG )
2343     {
2344         if( list.attr )
2345             CV_Error( CV_StsBadArg, "Closing tag should not include any attributes" );
2346         *ptr++ = '/';
2347     }
2348
2349     if( !cv_isalpha(key[0]) && key[0] != '_' )
2350         CV_Error( CV_StsBadArg, "Key should start with a letter or _" );
2351
2352     ptr = icvFSResizeWriteBuffer( fs, ptr, len );
2353     for( i = 0; i < len; i++ )
2354     {
2355         char c = key[i];
2356         if( !cv_isalnum(c) && c != '_' && c != '-' )
2357             CV_Error( CV_StsBadArg, "Key name may only contain alphanumeric characters [a-zA-Z0-9], '-' and '_'" );
2358         ptr[i] = c;
2359     }
2360     ptr += len;
2361
2362     for(;;)
2363     {
2364         const char** attr = list.attr;
2365
2366         for( ; attr && attr[0] != 0; attr += 2 )
2367         {
2368             int len0 = (int)strlen(attr[0]);
2369             int len1 = (int)strlen(attr[1]);
2370
2371             ptr = icvFSResizeWriteBuffer( fs, ptr, len0 + len1 + 4 );
2372             *ptr++ = ' ';
2373             memcpy( ptr, attr[0], len0 );
2374             ptr += len0;
2375             *ptr++ = '=';
2376             *ptr++ = '\"';
2377             memcpy( ptr, attr[1], len1 );
2378             ptr += len1;
2379             *ptr++ = '\"';
2380         }
2381         if( !list.next )
2382             break;
2383         list = *list.next;
2384     }
2385
2386     if( tag_type == CV_XML_EMPTY_TAG )
2387         *ptr++ = '/';
2388     *ptr++ = '>';
2389     fs->buffer = ptr;
2390     fs->struct_flags = struct_flags & ~CV_NODE_EMPTY;
2391 }
2392
2393
2394 static void
2395 icvXMLStartWriteStruct( CvFileStorage* fs, const char* key, int struct_flags,
2396                         const char* type_name CV_DEFAULT(0))
2397 {
2398     CvXMLStackRecord parent;
2399     const char* attr[10];
2400     int idx = 0;
2401
2402     struct_flags = (struct_flags & (CV_NODE_TYPE_MASK|CV_NODE_FLOW)) | CV_NODE_EMPTY;
2403     if( !CV_NODE_IS_COLLECTION(struct_flags))
2404         CV_Error( CV_StsBadArg,
2405         "Some collection type: CV_NODE_SEQ or CV_NODE_MAP must be specified" );
2406
2407     if( type_name )
2408     {
2409         attr[idx++] = "type_id";
2410         attr[idx++] = type_name;
2411     }
2412     attr[idx++] = 0;
2413
2414     icvXMLWriteTag( fs, key, CV_XML_OPENING_TAG, cvAttrList(attr,0) );
2415
2416     parent.struct_flags = fs->struct_flags & ~CV_NODE_EMPTY;
2417     parent.struct_indent = fs->struct_indent;
2418     parent.struct_tag = fs->struct_tag;
2419     cvSaveMemStoragePos( fs->strstorage, &parent.pos );
2420     cvSeqPush( fs->write_stack, &parent );
2421
2422     fs->struct_indent += CV_XML_INDENT;
2423     if( !CV_NODE_IS_FLOW(struct_flags) )
2424         icvXMLFlush( fs );
2425
2426     fs->struct_flags = struct_flags;
2427     if( key )
2428     {
2429         fs->struct_tag = cvMemStorageAllocString( fs->strstorage, (char*)key, -1 );
2430     }
2431     else
2432     {
2433         fs->struct_tag.ptr = 0;
2434         fs->struct_tag.len = 0;
2435     }
2436 }
2437
2438
2439 static void
2440 icvXMLEndWriteStruct( CvFileStorage* fs )
2441 {
2442     CvXMLStackRecord parent;
2443
2444     if( fs->write_stack->total == 0 )
2445         CV_Error( CV_StsError, "An extra closing tag" );
2446
2447     icvXMLWriteTag( fs, fs->struct_tag.ptr, CV_XML_CLOSING_TAG, cvAttrList(0,0) );
2448     cvSeqPop( fs->write_stack, &parent );
2449
2450     fs->struct_indent = parent.struct_indent;
2451     fs->struct_flags = parent.struct_flags;
2452     fs->struct_tag = parent.struct_tag;
2453     cvRestoreMemStoragePos( fs->strstorage, &parent.pos );
2454 }
2455
2456
2457 static void
2458 icvXMLStartNextStream( CvFileStorage* fs )
2459 {
2460     if( !fs->is_first )
2461     {
2462         while( fs->write_stack->total > 0 )
2463             icvXMLEndWriteStruct(fs);
2464
2465         fs->struct_indent = 0;
2466         icvXMLFlush(fs);
2467         /* XML does not allow multiple top-level elements,
2468            so we just put a comment and continue
2469            the current (and the only) "stream" */
2470         icvPuts( fs, "\n<!-- next stream -->\n" );
2471         /*fputs( "</opencv_storage>\n", fs->file );
2472         fputs( "<opencv_storage>\n", fs->file );*/
2473         fs->buffer = fs->buffer_start;
2474     }
2475 }
2476
2477
2478 static void
2479 icvXMLWriteScalar( CvFileStorage* fs, const char* key, const char* data, int len )
2480 {
2481     if( CV_NODE_IS_MAP(fs->struct_flags) ||
2482         (!CV_NODE_IS_COLLECTION(fs->struct_flags) && key) )
2483     {
2484         icvXMLWriteTag( fs, key, CV_XML_OPENING_TAG, cvAttrList(0,0) );
2485         char* ptr = icvFSResizeWriteBuffer( fs, fs->buffer, len );
2486         memcpy( ptr, data, len );
2487         fs->buffer = ptr + len;
2488         icvXMLWriteTag( fs, key, CV_XML_CLOSING_TAG, cvAttrList(0,0) );
2489     }
2490     else
2491     {
2492         char* ptr = fs->buffer;
2493         int new_offset = (int)(ptr - fs->buffer_start) + len;
2494
2495         if( key )
2496             CV_Error( CV_StsBadArg, "elements with keys can not be written to sequence" );
2497
2498         fs->struct_flags = CV_NODE_SEQ;
2499
2500         if( (new_offset > fs->wrap_margin && new_offset - fs->struct_indent > 10) ||
2501             (ptr > fs->buffer_start && ptr[-1] == '>' && !CV_NODE_IS_EMPTY(fs->struct_flags)) )
2502         {
2503             ptr = icvXMLFlush(fs);
2504         }
2505         else if( ptr > fs->buffer_start + fs->struct_indent && ptr[-1] != '>' )
2506             *ptr++ = ' ';
2507
2508         memcpy( ptr, data, len );
2509         fs->buffer = ptr + len;
2510     }
2511 }
2512
2513
2514 static void
2515 icvXMLWriteInt( CvFileStorage* fs, const char* key, int value )
2516 {
2517     char buf[128], *ptr = icv_itoa( value, buf, 10 );
2518     int len = (int)strlen(ptr);
2519     icvXMLWriteScalar( fs, key, ptr, len );
2520 }
2521
2522
2523 static void
2524 icvXMLWriteReal( CvFileStorage* fs, const char* key, double value )
2525 {
2526     char buf[128];
2527     int len = (int)strlen( icvDoubleToString( buf, value ));
2528     icvXMLWriteScalar( fs, key, buf, len );
2529 }
2530
2531
2532 static void
2533 icvXMLWriteString( CvFileStorage* fs, const char* key, const char* str, int quote )
2534 {
2535     char buf[CV_FS_MAX_LEN*6+16];
2536     char* data = (char*)str;
2537     int i, len;
2538
2539     if( !str )
2540         CV_Error( CV_StsNullPtr, "Null string pointer" );
2541
2542     len = (int)strlen(str);
2543     if( len > CV_FS_MAX_LEN )
2544         CV_Error( CV_StsBadArg, "The written string is too long" );
2545
2546     if( quote || len == 0 || str[0] != '\"' || str[0] != str[len-1] )
2547     {
2548         int need_quote = quote || len == 0;
2549         data = buf;
2550         *data++ = '\"';
2551         for( i = 0; i < len; i++ )
2552         {
2553             char c = str[i];
2554
2555             if( (uchar)c >= 128 || c == ' ' )
2556             {
2557                 *data++ = c;
2558                 need_quote = 1;
2559             }
2560             else if( !cv_isprint(c) || c == '<' || c == '>' || c == '&' || c == '\'' || c == '\"' )
2561             {
2562                 *data++ = '&';
2563                 if( c == '<' )
2564                 {
2565                     memcpy(data, "lt", 2);
2566                     data += 2;
2567                 }
2568                 else if( c == '>' )
2569                 {
2570                     memcpy(data, "gt", 2);
2571                     data += 2;
2572                 }
2573                 else if( c == '&' )
2574                 {
2575                     memcpy(data, "amp", 3);
2576                     data += 3;
2577                 }
2578                 else if( c == '\'' )
2579                 {
2580                     memcpy(data, "apos", 4);
2581                     data += 4;
2582                 }
2583                 else if( c == '\"' )
2584                 {
2585                     memcpy( data, "quot", 4);
2586                     data += 4;
2587                 }
2588                 else
2589                 {
2590                     sprintf( data, "#x%02x", (uchar)c );
2591                     data += 4;
2592                 }
2593                 *data++ = ';';
2594                 need_quote = 1;
2595             }
2596             else
2597                 *data++ = c;
2598         }
2599         if( !need_quote && (cv_isdigit(str[0]) ||
2600             str[0] == '+' || str[0] == '-' || str[0] == '.' ))
2601             need_quote = 1;
2602
2603         if( need_quote )
2604             *data++ = '\"';
2605         len = (int)(data - buf) - !need_quote;
2606         *data++ = '\0';
2607         data = buf + !need_quote;
2608     }
2609
2610     icvXMLWriteScalar( fs, key, data, len );
2611 }
2612
2613
2614 static void
2615 icvXMLWriteComment( CvFileStorage* fs, const char* comment, int eol_comment )
2616 {
2617     int len;
2618     int multiline;
2619     const char* eol;
2620     char* ptr;
2621
2622     if( !comment )
2623         CV_Error( CV_StsNullPtr, "Null comment" );
2624
2625     if( strstr(comment, "--") != 0 )
2626         CV_Error( CV_StsBadArg, "Double hyphen \'--\' is not allowed in the comments" );
2627
2628     len = (int)strlen(comment);
2629     eol = strchr(comment, '\n');
2630     multiline = eol != 0;
2631     ptr = fs->buffer;
2632
2633     if( multiline || !eol_comment || fs->buffer_end - ptr < len + 5 )
2634         ptr = icvXMLFlush( fs );
2635     else if( ptr > fs->buffer_start + fs->struct_indent )
2636         *ptr++ = ' ';
2637
2638     if( !multiline )
2639     {
2640         ptr = icvFSResizeWriteBuffer( fs, ptr, len + 9 );
2641         sprintf( ptr, "<!-- %s -->", comment );
2642         len = (int)strlen(ptr);
2643     }
2644     else
2645     {
2646         strcpy( ptr, "<!--" );
2647         len = 4;
2648     }
2649
2650     fs->buffer = ptr + len;
2651     ptr = icvXMLFlush(fs);
2652
2653     if( multiline )
2654     {
2655         while( comment )
2656         {
2657             if( eol )
2658             {
2659                 ptr = icvFSResizeWriteBuffer( fs, ptr, (int)(eol - comment) + 1 );
2660                 memcpy( ptr, comment, eol - comment + 1 );
2661                 ptr += eol - comment;
2662                 comment = eol + 1;
2663                 eol = strchr( comment, '\n' );
2664             }
2665             else
2666             {
2667                 len = (int)strlen(comment);
2668                 ptr = icvFSResizeWriteBuffer( fs, ptr, len );
2669                 memcpy( ptr, comment, len );
2670                 ptr += len;
2671                 comment = 0;
2672             }
2673             fs->buffer = ptr;
2674             ptr = icvXMLFlush( fs );
2675         }
2676         sprintf( ptr, "-->" );
2677         fs->buffer = ptr + 3;
2678         icvXMLFlush( fs );
2679     }
2680 }
2681
2682
2683 /****************************************************************************************\
2684 *                              Common High-Level Functions                               *
2685 \****************************************************************************************/
2686
2687 CV_IMPL CvFileStorage*
2688 cvOpenFileStorage( const char* filename, CvMemStorage* dststorage, int flags, const char* encoding )
2689 {
2690     CvFileStorage* fs = 0;
2691     char* xml_buf = 0;
2692     int default_block_size = 1 << 18;
2693     bool append = (flags & 3) == CV_STORAGE_APPEND;
2694     bool mem = (flags & CV_STORAGE_MEMORY) != 0;
2695     bool write_mode = (flags & 3) != 0;
2696     bool isGZ = false;
2697     size_t fnamelen = 0;
2698
2699     if( !filename || filename[0] == '\0' )
2700     {
2701         if( !write_mode )
2702             CV_Error( CV_StsNullPtr, mem ? "NULL or empty filename" : "NULL or empty buffer" );
2703         mem = true;
2704     }
2705     else
2706         fnamelen = strlen(filename);
2707
2708     if( mem && append )
2709         CV_Error( CV_StsBadFlag, "CV_STORAGE_APPEND and CV_STORAGE_MEMORY are not currently compatible" );
2710
2711     fs = (CvFileStorage*)cvAlloc( sizeof(*fs) );
2712     memset( fs, 0, sizeof(*fs));
2713
2714     fs->memstorage = cvCreateMemStorage( default_block_size );
2715     fs->dststorage = dststorage ? dststorage : fs->memstorage;
2716
2717     fs->flags = CV_FILE_STORAGE;
2718     fs->write_mode = write_mode;
2719
2720     if( !mem )
2721     {
2722         fs->filename = (char*)cvMemStorageAlloc( fs->memstorage, fnamelen+1 );
2723         strcpy( fs->filename, filename );
2724
2725         char* dot_pos = strrchr(fs->filename, '.');
2726         char compression = '\0';
2727
2728         if( dot_pos && dot_pos[1] == 'g' && dot_pos[2] == 'z' &&
2729             (dot_pos[3] == '\0' || (cv_isdigit(dot_pos[3]) && dot_pos[4] == '\0')) )
2730         {
2731             if( append )
2732                 CV_Error(CV_StsNotImplemented, "Appending data to compressed file is not implemented" );
2733             isGZ = true;
2734             compression = dot_pos[3];
2735             if( compression )
2736                 dot_pos[3] = '\0', fnamelen--;
2737         }
2738
2739         if( !isGZ )
2740         {
2741             fs->file = fopen(fs->filename, !fs->write_mode ? "rt" : !append ? "wt" : "a+t" );
2742             if( !fs->file )
2743                 goto _exit_;
2744         }
2745         else
2746         {
2747             #if USE_ZLIB
2748             char mode[] = { fs->write_mode ? 'w' : 'r', 'b', compression ? compression : '3', '\0' };
2749             fs->gzfile = gzopen(fs->filename, mode);
2750             if( !fs->gzfile )
2751                 goto _exit_;
2752             #else
2753             CV_Error(CV_StsNotImplemented, "There is no compressed file storage support in this configuration");
2754             #endif
2755         }
2756     }
2757
2758     fs->roots = 0;
2759     fs->struct_indent = 0;
2760     fs->struct_flags = 0;
2761     fs->wrap_margin = 71;
2762
2763     if( fs->write_mode )
2764     {
2765         int fmt = flags & CV_STORAGE_FORMAT_MASK;
2766
2767         if( mem )
2768             fs->outbuf = new std::deque<char>;
2769
2770         if( fmt == CV_STORAGE_FORMAT_AUTO && filename )
2771         {
2772             const char* dot_pos = filename + fnamelen - (isGZ ? 7 : 4);
2773             fs->fmt = (dot_pos >= filename && (memcmp( dot_pos, ".xml", 4) == 0 ||
2774                     memcmp(dot_pos, ".XML", 4) == 0 || memcmp(dot_pos, ".Xml", 4) == 0)) ?
2775                 CV_STORAGE_FORMAT_XML : CV_STORAGE_FORMAT_YAML;
2776         }
2777         else
2778             fs->fmt = fmt != CV_STORAGE_FORMAT_AUTO ? fmt : CV_STORAGE_FORMAT_XML;
2779
2780         // we use factor=6 for XML (the longest characters (' and ") are encoded with 6 bytes (&apos; and &quot;)
2781         // and factor=4 for YAML ( as we use 4 bytes for non ASCII characters (e.g. \xAB))
2782         int buf_size = CV_FS_MAX_LEN*(fs->fmt == CV_STORAGE_FORMAT_XML ? 6 : 4) + 1024;
2783
2784         if( append )
2785             fseek( fs->file, 0, SEEK_END );
2786
2787         fs->write_stack = cvCreateSeq( 0, sizeof(CvSeq), fs->fmt == CV_STORAGE_FORMAT_XML ?
2788                 sizeof(CvXMLStackRecord) : sizeof(int), fs->memstorage );
2789         fs->is_first = 1;
2790         fs->struct_indent = 0;
2791         fs->struct_flags = CV_NODE_EMPTY;
2792         fs->buffer_start = fs->buffer = (char*)cvAlloc( buf_size + 1024 );
2793         fs->buffer_end = fs->buffer_start + buf_size;
2794         if( fs->fmt == CV_STORAGE_FORMAT_XML )
2795         {
2796             size_t file_size = fs->file ? (size_t)ftell( fs->file ) : (size_t)0;
2797             fs->strstorage = cvCreateChildMemStorage( fs->memstorage );
2798             if( !append || file_size == 0 )
2799             {
2800                 if( encoding )
2801                 {
2802                     if( strcmp( encoding, "UTF-16" ) == 0 ||
2803                         strcmp( encoding, "utf-16" ) == 0 ||
2804                         strcmp( encoding, "Utf-16" ) == 0 )
2805                         CV_Error( CV_StsBadArg, "UTF-16 XML encoding is not supported! Use 8-bit encoding\n");
2806
2807                     CV_Assert( strlen(encoding) < 1000 );
2808                     char buf[1100];
2809                     sprintf(buf, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", encoding);
2810                     icvPuts( fs, buf );
2811                 }
2812                 else
2813                     icvPuts( fs, "<?xml version=\"1.0\"?>\n" );
2814                 icvPuts( fs, "<opencv_storage>\n" );
2815             }
2816             else
2817             {
2818                 int xml_buf_size = 1 << 10;
2819                 char substr[] = "</opencv_storage>";
2820                 int last_occurence = -1;
2821                 xml_buf_size = MIN(xml_buf_size, int(file_size));
2822                 fseek( fs->file, -xml_buf_size, SEEK_END );
2823                 xml_buf = (char*)cvAlloc( xml_buf_size+2 );
2824                 // find the last occurence of </opencv_storage>
2825                 for(;;)
2826                 {
2827                     int line_offset = ftell( fs->file );
2828                     char* ptr0 = icvGets( fs, xml_buf, xml_buf_size ), *ptr;
2829                     if( !ptr0 )
2830                         break;
2831                     ptr = ptr0;
2832                     for(;;)
2833                     {
2834                         ptr = strstr( ptr, substr );
2835                         if( !ptr )
2836                             break;
2837                         last_occurence = line_offset + (int)(ptr - ptr0);
2838                         ptr += strlen(substr);
2839                     }
2840                 }
2841                 if( last_occurence < 0 )
2842                     CV_Error( CV_StsError, "Could not find </opencv_storage> in the end of file.\n" );
2843                 icvCloseFile( fs );
2844                 fs->file = fopen( fs->filename, "r+t" );
2845                 fseek( fs->file, last_occurence, SEEK_SET );
2846                 // replace the last "</opencv_storage>" with " <!-- resumed -->", which has the same length
2847                 icvPuts( fs, " <!-- resumed -->" );
2848                 fseek( fs->file, 0, SEEK_END );
2849                 icvPuts( fs, "\n" );
2850             }
2851             fs->start_write_struct = icvXMLStartWriteStruct;
2852             fs->end_write_struct = icvXMLEndWriteStruct;
2853             fs->write_int = icvXMLWriteInt;
2854             fs->write_real = icvXMLWriteReal;
2855             fs->write_string = icvXMLWriteString;
2856             fs->write_comment = icvXMLWriteComment;
2857             fs->start_next_stream = icvXMLStartNextStream;
2858         }
2859         else
2860         {
2861             if( !append )
2862                 icvPuts( fs, "%YAML:1.0\n" );
2863             else
2864                 icvPuts( fs, "...\n---\n" );
2865             fs->start_write_struct = icvYMLStartWriteStruct;
2866             fs->end_write_struct = icvYMLEndWriteStruct;
2867             fs->write_int = icvYMLWriteInt;
2868             fs->write_real = icvYMLWriteReal;
2869             fs->write_string = icvYMLWriteString;
2870             fs->write_comment = icvYMLWriteComment;
2871             fs->start_next_stream = icvYMLStartNextStream;
2872         }
2873     }
2874     else
2875     {
2876         if( mem )
2877         {
2878             fs->strbuf = filename;
2879             fs->strbufsize = fnamelen;
2880         }
2881
2882         size_t buf_size = 1 << 20;
2883         const char* yaml_signature = "%YAML:";
2884         char buf[16];
2885         icvGets( fs, buf, sizeof(buf)-2 );
2886         fs->fmt = strncmp( buf, yaml_signature, strlen(yaml_signature) ) == 0 ?
2887             CV_STORAGE_FORMAT_YAML : CV_STORAGE_FORMAT_XML;
2888
2889         if( !isGZ )
2890         {
2891             if( !mem )
2892             {
2893                 fseek( fs->file, 0, SEEK_END );
2894                 buf_size = ftell( fs->file );
2895             }
2896             else
2897                 buf_size = fs->strbufsize;
2898             buf_size = MIN( buf_size, (size_t)(1 << 20) );
2899             buf_size = MAX( buf_size, (size_t)(CV_FS_MAX_LEN*2 + 1024) );
2900         }
2901         icvRewind(fs);
2902
2903         fs->str_hash = cvCreateMap( 0, sizeof(CvStringHash),
2904                         sizeof(CvStringHashNode), fs->memstorage, 256 );
2905
2906         fs->roots = cvCreateSeq( 0, sizeof(CvSeq),
2907                         sizeof(CvFileNode), fs->memstorage );
2908
2909         fs->buffer = fs->buffer_start = (char*)cvAlloc( buf_size + 256 );
2910         fs->buffer_end = fs->buffer_start + buf_size;
2911         fs->buffer[0] = '\n';
2912         fs->buffer[1] = '\0';
2913
2914         //mode = cvGetErrMode();
2915         //cvSetErrMode( CV_ErrModeSilent );
2916         if( fs->fmt == CV_STORAGE_FORMAT_XML )
2917             icvXMLParse( fs );
2918         else
2919             icvYMLParse( fs );
2920         //cvSetErrMode( mode );
2921
2922         // release resources that we do not need anymore
2923         cvFree( &fs->buffer_start );
2924         fs->buffer = fs->buffer_end = 0;
2925     }
2926     fs->is_opened = true;
2927
2928 _exit_:
2929     if( fs )
2930     {
2931         if( cvGetErrStatus() < 0 || (!fs->file && !fs->gzfile && !fs->outbuf && !fs->strbuf) )
2932         {
2933             cvReleaseFileStorage( &fs );
2934         }
2935         else if( !fs->write_mode )
2936         {
2937             icvCloseFile(fs);
2938             // we close the file since it's not needed anymore. But icvCloseFile() resets is_opened,
2939             // which may be misleading. Since we restore the value of is_opened.
2940             fs->is_opened = true;
2941         }
2942     }
2943
2944     cvFree( &xml_buf );
2945     return  fs;
2946 }
2947
2948
2949 CV_IMPL void
2950 cvStartWriteStruct( CvFileStorage* fs, const char* key, int struct_flags,
2951                     const char* type_name, CvAttrList /*attributes*/ )
2952 {
2953     CV_CHECK_OUTPUT_FILE_STORAGE(fs);
2954     fs->start_write_struct( fs, key, struct_flags, type_name );
2955 }
2956
2957
2958 CV_IMPL void
2959 cvEndWriteStruct( CvFileStorage* fs )
2960 {
2961     CV_CHECK_OUTPUT_FILE_STORAGE(fs);
2962     fs->end_write_struct( fs );
2963 }
2964
2965
2966 CV_IMPL void
2967 cvWriteInt( CvFileStorage* fs, const char* key, int value )
2968 {
2969     CV_CHECK_OUTPUT_FILE_STORAGE(fs);
2970     fs->write_int( fs, key, value );
2971 }
2972
2973
2974 CV_IMPL void
2975 cvWriteReal( CvFileStorage* fs, const char* key, double value )
2976 {
2977     CV_CHECK_OUTPUT_FILE_STORAGE(fs);
2978     fs->write_real( fs, key, value );
2979 }
2980
2981
2982 CV_IMPL void
2983 cvWriteString( CvFileStorage* fs, const char* key, const char* value, int quote )
2984 {
2985     CV_CHECK_OUTPUT_FILE_STORAGE(fs);
2986     fs->write_string( fs, key, value, quote );
2987 }
2988
2989
2990 CV_IMPL void
2991 cvWriteComment( CvFileStorage* fs, const char* comment, int eol_comment )
2992 {
2993     CV_CHECK_OUTPUT_FILE_STORAGE(fs);
2994     fs->write_comment( fs, comment, eol_comment );
2995 }
2996
2997
2998 CV_IMPL void
2999 cvStartNextStream( CvFileStorage* fs )
3000 {
3001     CV_CHECK_OUTPUT_FILE_STORAGE(fs);
3002     fs->start_next_stream( fs );
3003 }
3004
3005
3006 static const char icvTypeSymbol[] = "ucwsifdr";
3007 #define CV_FS_MAX_FMT_PAIRS  128
3008
3009 static char*
3010 icvEncodeFormat( int elem_type, char* dt )
3011 {
3012     sprintf( dt, "%d%c", CV_MAT_CN(elem_type), icvTypeSymbol[CV_MAT_DEPTH(elem_type)] );
3013     return dt + ( dt[2] == '\0' && dt[0] == '1' );
3014 }
3015
3016 static int
3017 icvDecodeFormat( const char* dt, int* fmt_pairs, int max_len )
3018 {
3019     int fmt_pair_count = 0;
3020     int i = 0, k = 0, len = dt ? (int)strlen(dt) : 0;
3021
3022     if( !dt || !len )
3023         return 0;
3024
3025     assert( fmt_pairs != 0 && max_len > 0 );
3026     fmt_pairs[0] = 0;
3027     max_len *= 2;
3028
3029     for( ; k < len; k++ )
3030     {
3031         char c = dt[k];
3032
3033         if( cv_isdigit(c) )
3034         {
3035             int count = c - '0';
3036             if( cv_isdigit(dt[k+1]) )
3037             {
3038                 char* endptr = 0;
3039                 count = (int)strtol( dt+k, &endptr, 10 );
3040                 k = (int)(endptr - dt) - 1;
3041             }
3042
3043             if( count <= 0 )
3044                 CV_Error( CV_StsBadArg, "Invalid data type specification" );
3045
3046             fmt_pairs[i] = count;
3047         }
3048         else
3049         {
3050             const char* pos = strchr( icvTypeSymbol, c );
3051             if( !pos )
3052                 CV_Error( CV_StsBadArg, "Invalid data type specification" );
3053             if( fmt_pairs[i] == 0 )
3054                 fmt_pairs[i] = 1;
3055             fmt_pairs[i+1] = (int)(pos - icvTypeSymbol);
3056             if( i > 0 && fmt_pairs[i+1] == fmt_pairs[i-1] )
3057                 fmt_pairs[i-2] += fmt_pairs[i];
3058             else
3059             {
3060                 i += 2;
3061                 if( i >= max_len )
3062                     CV_Error( CV_StsBadArg, "Too long data type specification" );
3063             }
3064             fmt_pairs[i] = 0;
3065         }
3066     }
3067
3068     fmt_pair_count = i/2;
3069     return fmt_pair_count;
3070 }
3071
3072
3073 static int
3074 icvCalcElemSize( const char* dt, int initial_size )
3075 {
3076     int size = 0;
3077     int fmt_pairs[CV_FS_MAX_FMT_PAIRS], i, fmt_pair_count;
3078     int comp_size;
3079
3080     fmt_pair_count = icvDecodeFormat( dt, fmt_pairs, CV_FS_MAX_FMT_PAIRS );
3081     fmt_pair_count *= 2;
3082     for( i = 0, size = initial_size; i < fmt_pair_count; i += 2 )
3083     {
3084         comp_size = CV_ELEM_SIZE(fmt_pairs[i+1]);
3085         size = cvAlign( size, comp_size );
3086         size += comp_size * fmt_pairs[i];
3087     }
3088     if( initial_size == 0 )
3089     {
3090         comp_size = CV_ELEM_SIZE(fmt_pairs[1]);
3091         size = cvAlign( size, comp_size );
3092     }
3093     return size;
3094 }
3095
3096
3097 static int
3098 icvDecodeSimpleFormat( const char* dt )
3099 {
3100     int elem_type = -1;
3101     int fmt_pairs[CV_FS_MAX_FMT_PAIRS], fmt_pair_count;
3102
3103     fmt_pair_count = icvDecodeFormat( dt, fmt_pairs, CV_FS_MAX_FMT_PAIRS );
3104     if( fmt_pair_count != 1 || fmt_pairs[0] > 4 )
3105         CV_Error( CV_StsError, "Too complex format for the matrix" );
3106
3107     elem_type = CV_MAKETYPE( fmt_pairs[1], fmt_pairs[0] );
3108
3109     return elem_type;
3110 }
3111
3112
3113 CV_IMPL void
3114 cvWriteRawData( CvFileStorage* fs, const void* _data, int len, const char* dt )
3115 {
3116     const char* data0 = (const char*)_data;
3117     int offset = 0;
3118     int fmt_pairs[CV_FS_MAX_FMT_PAIRS*2], k, fmt_pair_count;
3119     char buf[256] = "";
3120
3121     CV_CHECK_OUTPUT_FILE_STORAGE( fs );
3122
3123     if( len < 0 )
3124         CV_Error( CV_StsOutOfRange, "Negative number of elements" );
3125
3126     fmt_pair_count = icvDecodeFormat( dt, fmt_pairs, CV_FS_MAX_FMT_PAIRS );
3127
3128     if( !len )
3129         return;
3130
3131     if( !data0 )
3132         CV_Error( CV_StsNullPtr, "Null data pointer" );
3133
3134     if( fmt_pair_count == 1 )
3135     {
3136         fmt_pairs[0] *= len;
3137         len = 1;
3138     }
3139
3140     for(;len--;)
3141     {
3142         for( k = 0; k < fmt_pair_count; k++ )
3143         {
3144             int i, count = fmt_pairs[k*2];
3145             int elem_type = fmt_pairs[k*2+1];
3146             int elem_size = CV_ELEM_SIZE(elem_type);
3147             const char* data, *ptr;
3148
3149             offset = cvAlign( offset, elem_size );
3150             data = data0 + offset;
3151
3152             for( i = 0; i < count; i++ )
3153             {
3154                 switch( elem_type )
3155                 {
3156                 case CV_8U:
3157                     ptr = icv_itoa( *(uchar*)data, buf, 10 );
3158                     data++;
3159                     break;
3160                 case CV_8S:
3161                     ptr = icv_itoa( *(char*)data, buf, 10 );
3162                     data++;
3163                     break;
3164                 case CV_16U:
3165                     ptr = icv_itoa( *(ushort*)data, buf, 10 );
3166                     data += sizeof(ushort);
3167                     break;
3168                 case CV_16S:
3169                     ptr = icv_itoa( *(short*)data, buf, 10 );
3170                     data += sizeof(short);
3171                     break;
3172                 case CV_32S:
3173                     ptr = icv_itoa( *(int*)data, buf, 10 );
3174                     data += sizeof(int);
3175                     break;
3176                 case CV_32F:
3177                     ptr = icvFloatToString( buf, *(float*)data );
3178                     data += sizeof(float);
3179                     break;
3180                 case CV_64F:
3181                     ptr = icvDoubleToString( buf, *(double*)data );
3182                     data += sizeof(double);
3183                     break;
3184                 case CV_USRTYPE1: /* reference */
3185                     ptr = icv_itoa( (int)*(size_t*)data, buf, 10 );
3186                     data += sizeof(size_t);
3187                     break;
3188                 default:
3189                     assert(0);
3190                     return;
3191                 }
3192
3193                 if( fs->fmt == CV_STORAGE_FORMAT_XML )
3194                 {
3195                     int buf_len = (int)strlen(ptr);
3196                     icvXMLWriteScalar( fs, 0, ptr, buf_len );
3197                 }
3198                 else
3199                     icvYMLWrite( fs, 0, ptr );
3200             }
3201
3202             offset = (int)(data - data0);
3203         }
3204     }
3205 }
3206
3207
3208 CV_IMPL void
3209 cvStartReadRawData( const CvFileStorage* fs, const CvFileNode* src, CvSeqReader* reader )
3210 {
3211     int node_type;
3212     CV_CHECK_FILE_STORAGE( fs );
3213
3214     if( !src || !reader )
3215         CV_Error( CV_StsNullPtr, "Null pointer to source file node or reader" );
3216
3217     node_type = CV_NODE_TYPE(src->tag);
3218     if( node_type == CV_NODE_INT || node_type == CV_NODE_REAL )
3219     {
3220         // emulate reading from 1-element sequence
3221         reader->ptr = (schar*)src;
3222         reader->block_max = reader->ptr + sizeof(*src)*2;
3223         reader->block_min = reader->ptr;
3224         reader->seq = 0;
3225     }
3226     else if( node_type == CV_NODE_SEQ )
3227     {
3228         cvStartReadSeq( src->data.seq, reader, 0 );
3229     }
3230     else if( node_type == CV_NODE_NONE )
3231     {
3232         memset( reader, 0, sizeof(*reader) );
3233     }
3234     else
3235         CV_Error( CV_StsBadArg, "The file node should be a numerical scalar or a sequence" );
3236 }
3237
3238
3239 CV_IMPL void
3240 cvReadRawDataSlice( const CvFileStorage* fs, CvSeqReader* reader,
3241                     int len, void* _data, const char* dt )
3242 {
3243     char* data0 = (char*)_data;
3244     int fmt_pairs[CV_FS_MAX_FMT_PAIRS*2], k = 0, fmt_pair_count;
3245     int i = 0, offset = 0, count = 0;
3246
3247     CV_CHECK_FILE_STORAGE( fs );
3248
3249     if( !reader || !data0 )
3250         CV_Error( CV_StsNullPtr, "Null pointer to reader or destination array" );
3251
3252     if( !reader->seq && len != 1 )
3253         CV_Error( CV_StsBadSize, "The readed sequence is a scalar, thus len must be 1" );
3254
3255     fmt_pair_count = icvDecodeFormat( dt, fmt_pairs, CV_FS_MAX_FMT_PAIRS );
3256
3257     for(;;)
3258     {
3259         for( k = 0; k < fmt_pair_count; k++ )
3260         {
3261             int elem_type = fmt_pairs[k*2+1];
3262             int elem_size = CV_ELEM_SIZE(elem_type);
3263             char* data;
3264
3265             count = fmt_pairs[k*2];
3266             offset = cvAlign( offset, elem_size );
3267             data = data0 + offset;
3268
3269             for( i = 0; i < count; i++ )
3270             {
3271                 CvFileNode* node = (CvFileNode*)reader->ptr;
3272                 if( CV_NODE_IS_INT(node->tag) )
3273                 {
3274                     int ival = node->data.i;
3275
3276                     switch( elem_type )
3277                     {
3278                     case CV_8U:
3279                         *(uchar*)data = CV_CAST_8U(ival);
3280                         data++;
3281                         break;
3282                     case CV_8S:
3283                         *(char*)data = CV_CAST_8S(ival);
3284                         data++;
3285                         break;
3286                     case CV_16U:
3287                         *(ushort*)data = CV_CAST_16U(ival);
3288                         data += sizeof(ushort);
3289                         break;
3290                     case CV_16S:
3291                         *(short*)data = CV_CAST_16S(ival);
3292                         data += sizeof(short);
3293                         break;
3294                     case CV_32S:
3295                         *(int*)data = ival;
3296                         data += sizeof(int);
3297                         break;
3298                     case CV_32F:
3299                         *(float*)data = (float)ival;
3300                         data += sizeof(float);
3301                         break;
3302                     case CV_64F:
3303                         *(double*)data = (double)ival;
3304                         data += sizeof(double);
3305                         break;
3306                     case CV_USRTYPE1: /* reference */
3307                         *(size_t*)data = ival;
3308                         data += sizeof(size_t);
3309                         break;
3310                     default:
3311                         assert(0);
3312                         return;
3313                     }
3314                 }
3315                 else if( CV_NODE_IS_REAL(node->tag) )
3316                 {
3317                     double fval = node->data.f;
3318                     int ival;
3319
3320                     switch( elem_type )
3321                     {
3322                     case CV_8U:
3323                         ival = cvRound(fval);
3324                         *(uchar*)data = CV_CAST_8U(ival);
3325                         data++;
3326                         break;
3327                     case CV_8S:
3328                         ival = cvRound(fval);
3329                         *(char*)data = CV_CAST_8S(ival);
3330                         data++;
3331                         break;
3332                     case CV_16U:
3333                         ival = cvRound(fval);
3334                         *(ushort*)data = CV_CAST_16U(ival);
3335                         data += sizeof(ushort);
3336                         break;
3337                     case CV_16S:
3338                         ival = cvRound(fval);
3339                         *(short*)data = CV_CAST_16S(ival);
3340                         data += sizeof(short);
3341                         break;
3342                     case CV_32S:
3343                         ival = cvRound(fval);
3344                         *(int*)data = ival;
3345                         data += sizeof(int);
3346                         break;
3347                     case CV_32F:
3348                         *(float*)data = (float)fval;
3349                         data += sizeof(float);
3350                         break;
3351                     case CV_64F:
3352                         *(double*)data = fval;
3353                         data += sizeof(double);
3354                         break;
3355                     case CV_USRTYPE1: /* reference */
3356                         ival = cvRound(fval);
3357                         *(size_t*)data = ival;
3358                         data += sizeof(size_t);
3359                         break;
3360                     default:
3361                         assert(0);
3362                         return;
3363                     }
3364                 }
3365                 else
3366                     CV_Error( CV_StsError,
3367                     "The sequence element is not a numerical scalar" );
3368
3369                 CV_NEXT_SEQ_ELEM( sizeof(CvFileNode), *reader );
3370                 if( !--len )
3371                     goto end_loop;
3372             }
3373
3374             offset = (int)(data - data0);
3375         }
3376     }
3377
3378 end_loop:
3379     if( i != count - 1 || k != fmt_pair_count - 1 )
3380         CV_Error( CV_StsBadSize,
3381         "The sequence slice does not fit an integer number of records" );
3382
3383     if( !reader->seq )
3384         reader->ptr -= sizeof(CvFileNode);
3385 }
3386
3387
3388 CV_IMPL void
3389 cvReadRawData( const CvFileStorage* fs, const CvFileNode* src,
3390                void* data, const char* dt )
3391 {
3392     CvSeqReader reader;
3393
3394     if( !src || !data )
3395         CV_Error( CV_StsNullPtr, "Null pointers to source file node or destination array" );
3396
3397     cvStartReadRawData( fs, src, &reader );
3398     cvReadRawDataSlice( fs, &reader, CV_NODE_IS_SEQ(src->tag) ?
3399                         src->data.seq->total : 1, data, dt );
3400 }
3401
3402
3403 static void
3404 icvWriteFileNode( CvFileStorage* fs, const char* name, const CvFileNode* node );
3405
3406 static void
3407 icvWriteCollection( CvFileStorage* fs, const CvFileNode* node )
3408 {
3409     int i, total = node->data.seq->total;
3410     int elem_size = node->data.seq->elem_size;
3411     int is_map = CV_NODE_IS_MAP(node->tag);
3412     CvSeqReader reader;
3413
3414     cvStartReadSeq( node->data.seq, &reader, 0 );
3415
3416     for( i = 0; i < total; i++ )
3417     {
3418         CvFileMapNode* elem = (CvFileMapNode*)reader.ptr;
3419         if( !is_map || CV_IS_SET_ELEM(elem) )
3420         {
3421             const char* name = is_map ? elem->key->str.ptr : 0;
3422             icvWriteFileNode( fs, name, &elem->value );
3423         }
3424         CV_NEXT_SEQ_ELEM( elem_size, reader );
3425     }
3426 }
3427
3428 static void
3429 icvWriteFileNode( CvFileStorage* fs, const char* name, const CvFileNode* node )
3430 {
3431     switch( CV_NODE_TYPE(node->tag) )
3432     {
3433     case CV_NODE_INT:
3434         fs->write_int( fs, name, node->data.i );
3435         break;
3436     case CV_NODE_REAL:
3437         fs->write_real( fs, name, node->data.f );
3438         break;
3439     case CV_NODE_STR:
3440         fs->write_string( fs, name, node->data.str.ptr, 0 );
3441         break;
3442     case CV_NODE_SEQ:
3443     case CV_NODE_MAP:
3444         fs->start_write_struct( fs, name, CV_NODE_TYPE(node->tag) +
3445                 (CV_NODE_SEQ_IS_SIMPLE(node->data.seq) ? CV_NODE_FLOW : 0),
3446                 node->info ? node->info->type_name : 0 );
3447         icvWriteCollection( fs, node );
3448         fs->end_write_struct( fs );
3449         break;
3450     case CV_NODE_NONE:
3451         fs->start_write_struct( fs, name, CV_NODE_SEQ, 0 );
3452         fs->end_write_struct( fs );
3453         break;
3454     default:
3455         CV_Error( CV_StsBadFlag, "Unknown type of file node" );
3456     }
3457 }
3458
3459
3460 CV_IMPL void
3461 cvWriteFileNode( CvFileStorage* fs, const char* new_node_name,
3462                  const CvFileNode* node, int embed )
3463 {
3464     CvFileStorage* dst = 0;
3465     CV_CHECK_OUTPUT_FILE_STORAGE(fs);
3466
3467     if( !node )
3468         return;
3469
3470     if( CV_NODE_IS_COLLECTION(node->tag) && embed )
3471     {
3472         icvWriteCollection( fs, node );
3473     }
3474     else
3475     {
3476         icvWriteFileNode( fs, new_node_name, node );
3477     }
3478     /*
3479     int i, stream_count;
3480     stream_count = fs->roots->total;
3481     for( i = 0; i < stream_count; i++ )
3482     {
3483         CvFileNode* node = (CvFileNode*)cvGetSeqElem( fs->roots, i, 0 );
3484         icvDumpCollection( dst, node );
3485         if( i < stream_count - 1 )
3486             dst->start_next_stream( dst );
3487     }*/
3488     cvReleaseFileStorage( &dst );
3489 }
3490
3491
3492 CV_IMPL const char*
3493 cvGetFileNodeName( const CvFileNode* file_node )
3494 {
3495     return file_node && CV_NODE_HAS_NAME(file_node->tag) ?
3496         ((CvFileMapNode*)file_node)->key->str.ptr : 0;
3497 }
3498
3499 /****************************************************************************************\
3500 *                          Reading/Writing etc. for standard types                       *
3501 \****************************************************************************************/
3502
3503 /*#define CV_TYPE_NAME_MAT "opencv-matrix"
3504 #define CV_TYPE_NAME_MATND "opencv-nd-matrix"
3505 #define CV_TYPE_NAME_SPARSE_MAT "opencv-sparse-matrix"
3506 #define CV_TYPE_NAME_IMAGE "opencv-image"
3507 #define CV_TYPE_NAME_SEQ "opencv-sequence"
3508 #define CV_TYPE_NAME_SEQ_TREE "opencv-sequence-tree"
3509 #define CV_TYPE_NAME_GRAPH "opencv-graph"*/
3510
3511 /******************************* CvMat ******************************/
3512
3513 static int
3514 icvIsMat( const void* ptr )
3515 {
3516     return CV_IS_MAT_HDR_Z(ptr);
3517 }
3518
3519 static void
3520 icvWriteMat( CvFileStorage* fs, const char* name,
3521              const void* struct_ptr, CvAttrList /*attr*/ )
3522 {
3523     const CvMat* mat = (const CvMat*)struct_ptr;
3524     char dt[16];
3525     CvSize size;
3526     int y;
3527
3528     assert( CV_IS_MAT_HDR_Z(mat) );
3529
3530     cvStartWriteStruct( fs, name, CV_NODE_MAP, CV_TYPE_NAME_MAT );
3531     cvWriteInt( fs, "rows", mat->rows );
3532     cvWriteInt( fs, "cols", mat->cols );
3533     cvWriteString( fs, "dt", icvEncodeFormat( CV_MAT_TYPE(mat->type), dt ), 0 );
3534     cvStartWriteStruct( fs, "data", CV_NODE_SEQ + CV_NODE_FLOW );
3535
3536     size = cvGetSize(mat);
3537     if( size.height > 0 && size.width > 0 && mat->data.ptr )
3538     {
3539         if( CV_IS_MAT_CONT(mat->type) )
3540         {
3541             size.width *= size.height;
3542             size.height = 1;
3543         }
3544
3545         for( y = 0; y < size.height; y++ )
3546             cvWriteRawData( fs, mat->data.ptr + (size_t)y*mat->step, size.width, dt );
3547     }
3548     cvEndWriteStruct( fs );
3549     cvEndWriteStruct( fs );
3550 }
3551
3552
3553 static int
3554 icvFileNodeSeqLen( CvFileNode* node )
3555 {
3556     return CV_NODE_IS_COLLECTION(node->tag) ? node->data.seq->total :
3557            CV_NODE_TYPE(node->tag) != CV_NODE_NONE;
3558 }
3559
3560
3561 static void*
3562 icvReadMat( CvFileStorage* fs, CvFileNode* node )
3563 {
3564     void* ptr = 0;
3565     CvMat* mat;
3566     const char* dt;
3567     CvFileNode* data;
3568     int rows, cols, elem_type;
3569
3570     rows = cvReadIntByName( fs, node, "rows", -1 );
3571     cols = cvReadIntByName( fs, node, "cols", -1 );
3572     dt = cvReadStringByName( fs, node, "dt", 0 );
3573
3574     if( rows < 0 || cols < 0 || !dt )
3575         CV_Error( CV_StsError, "Some of essential matrix attributes are absent" );
3576
3577     elem_type = icvDecodeSimpleFormat( dt );
3578
3579     data = cvGetFileNodeByName( fs, node, "data" );
3580     if( !data )
3581         CV_Error( CV_StsError, "The matrix data is not found in file storage" );
3582
3583     int nelems = icvFileNodeSeqLen( data );
3584     if( nelems > 0 && nelems != rows*cols*CV_MAT_CN(elem_type) )
3585         CV_Error( CV_StsUnmatchedSizes,
3586                  "The matrix size does not match to the number of stored elements" );
3587
3588     if( nelems > 0 )
3589     {
3590         mat = cvCreateMat( rows, cols, elem_type );
3591         cvReadRawData( fs, data, mat->data.ptr, dt );
3592     }
3593     else if( rows == 0 && cols == 0 )
3594         mat = cvCreateMatHeader( 0, 1, elem_type );
3595     else
3596         mat = cvCreateMatHeader( rows, cols, elem_type );
3597
3598     ptr = mat;
3599     return ptr;
3600 }
3601
3602
3603 /******************************* CvMatND ******************************/
3604
3605 static int
3606 icvIsMatND( const void* ptr )
3607 {
3608     return CV_IS_MATND_HDR(ptr);
3609 }
3610
3611
3612 static void
3613 icvWriteMatND( CvFileStorage* fs, const char* name,
3614                const void* struct_ptr, CvAttrList /*attr*/ )
3615 {
3616     CvMatND* mat = (CvMatND*)struct_ptr;
3617     CvMatND stub;
3618     CvNArrayIterator iterator;
3619     int dims, sizes[CV_MAX_DIM];
3620     char dt[16];
3621
3622     assert( CV_IS_MATND_HDR(mat) );
3623
3624     cvStartWriteStruct( fs, name, CV_NODE_MAP, CV_TYPE_NAME_MATND );
3625     dims = cvGetDims( mat, sizes );
3626     cvStartWriteStruct( fs, "sizes", CV_NODE_SEQ + CV_NODE_FLOW );
3627     cvWriteRawData( fs, sizes, dims, "i" );
3628     cvEndWriteStruct( fs );
3629     cvWriteString( fs, "dt", icvEncodeFormat( cvGetElemType(mat), dt ), 0 );
3630     cvStartWriteStruct( fs, "data", CV_NODE_SEQ + CV_NODE_FLOW );
3631
3632     if( mat->dim[0].size > 0 && mat->data.ptr )
3633     {
3634         cvInitNArrayIterator( 1, (CvArr**)&mat, 0, &stub, &iterator );
3635
3636         do
3637             cvWriteRawData( fs, iterator.ptr[0], iterator.size.width, dt );
3638         while( cvNextNArraySlice( &iterator ));
3639     }
3640     cvEndWriteStruct( fs );
3641     cvEndWriteStruct( fs );
3642 }
3643
3644
3645 static void*
3646 icvReadMatND( CvFileStorage* fs, CvFileNode* node )
3647 {
3648     void* ptr = 0;
3649     CvMatND* mat;
3650     const char* dt;
3651     CvFileNode* data;
3652     CvFileNode* sizes_node;
3653     int sizes[CV_MAX_DIM], dims, elem_type;
3654     int i, total_size;
3655
3656     sizes_node = cvGetFileNodeByName( fs, node, "sizes" );
3657     dt = cvReadStringByName( fs, node, "dt", 0 );
3658
3659     if( !sizes_node || !dt )
3660         CV_Error( CV_StsError, "Some of essential matrix attributes are absent" );
3661
3662     dims = CV_NODE_IS_SEQ(sizes_node->tag) ? sizes_node->data.seq->total :
3663            CV_NODE_IS_INT(sizes_node->tag) ? 1 : -1;
3664
3665     if( dims <= 0 || dims > CV_MAX_DIM )
3666         CV_Error( CV_StsParseError, "Could not determine the matrix dimensionality" );
3667
3668     cvReadRawData( fs, sizes_node, sizes, "i" );
3669     elem_type = icvDecodeSimpleFormat( dt );
3670
3671     data = cvGetFileNodeByName( fs, node, "data" );
3672     if( !data )
3673         CV_Error( CV_StsError, "The matrix data is not found in file storage" );
3674
3675
3676
3677     for( total_size = CV_MAT_CN(elem_type), i = 0; i < dims; i++ )
3678         total_size *= sizes[i];
3679
3680     int nelems = icvFileNodeSeqLen( data );
3681
3682     if( nelems > 0 && nelems != total_size )
3683         CV_Error( CV_StsUnmatchedSizes,
3684                  "The matrix size does not match to the number of stored elements" );
3685
3686     if( nelems > 0 )
3687     {
3688         mat = cvCreateMatND( dims, sizes, elem_type );
3689         cvReadRawData( fs, data, mat->data.ptr, dt );
3690     }
3691     else
3692         mat = cvCreateMatNDHeader( dims, sizes, elem_type );
3693
3694     ptr = mat;
3695     return ptr;
3696 }
3697
3698
3699 /******************************* CvSparseMat ******************************/
3700
3701 static int
3702 icvIsSparseMat( const void* ptr )
3703 {
3704     return CV_IS_SPARSE_MAT(ptr);
3705 }
3706
3707
3708 static int
3709 icvSortIdxCmpFunc( const void* _a, const void* _b, void* userdata )
3710 {
3711     int i, dims = *(int*)userdata;
3712     const int* a = *(const int**)_a;
3713     const int* b = *(const int**)_b;
3714
3715     for( i = 0; i < dims; i++ )
3716     {
3717         int delta = a[i] - b[i];
3718         if( delta )
3719             return delta;
3720     }
3721
3722     return 0;
3723 }
3724
3725
3726 static void
3727 icvWriteSparseMat( CvFileStorage* fs, const char* name,
3728                    const void* struct_ptr, CvAttrList /*attr*/ )
3729 {
3730     CvMemStorage* memstorage = 0;
3731     const CvSparseMat* mat = (const CvSparseMat*)struct_ptr;
3732     CvSparseMatIterator iterator;
3733     CvSparseNode* node;
3734     CvSeq* elements;
3735     CvSeqReader reader;
3736     int i, dims;
3737     int *prev_idx = 0;
3738     char dt[16];
3739
3740     assert( CV_IS_SPARSE_MAT(mat) );
3741
3742     memstorage = cvCreateMemStorage();
3743
3744     cvStartWriteStruct( fs, name, CV_NODE_MAP, CV_TYPE_NAME_SPARSE_MAT );
3745     dims = cvGetDims( mat, 0 );
3746
3747     cvStartWriteStruct( fs, "sizes", CV_NODE_SEQ + CV_NODE_FLOW );
3748     cvWriteRawData( fs, mat->size, dims, "i" );
3749     cvEndWriteStruct( fs );
3750     cvWriteString( fs, "dt", icvEncodeFormat( CV_MAT_TYPE(mat->type), dt ), 0 );
3751     cvStartWriteStruct( fs, "data", CV_NODE_SEQ + CV_NODE_FLOW );
3752
3753     elements = cvCreateSeq( CV_SEQ_ELTYPE_PTR, sizeof(CvSeq), sizeof(int*), memstorage );
3754
3755     node = cvInitSparseMatIterator( mat, &iterator );
3756     while( node )
3757     {
3758         int* idx = CV_NODE_IDX( mat, node );
3759         cvSeqPush( elements, &idx );
3760         node = cvGetNextSparseNode( &iterator );
3761     }
3762
3763     cvSeqSort( elements, icvSortIdxCmpFunc, &dims );
3764     cvStartReadSeq( elements, &reader, 0 );
3765
3766     for( i = 0; i < elements->total; i++ )
3767     {
3768         int* idx;
3769         void* val;
3770         int k = 0;
3771
3772         CV_READ_SEQ_ELEM( idx, reader );
3773         if( i > 0 )
3774         {
3775             for( ; idx[k] == prev_idx[k]; k++ )
3776                 assert( k < dims );
3777             if( k < dims - 1 )
3778                 fs->write_int( fs, 0, k - dims + 1 );
3779         }
3780         for( ; k < dims; k++ )
3781             fs->write_int( fs, 0, idx[k] );
3782         prev_idx = idx;
3783
3784         node = (CvSparseNode*)((uchar*)idx - mat->idxoffset );
3785         val = CV_NODE_VAL( mat, node );
3786
3787         cvWriteRawData( fs, val, 1, dt );
3788     }
3789
3790     cvEndWriteStruct( fs );
3791     cvEndWriteStruct( fs );
3792     cvReleaseMemStorage( &memstorage );
3793 }
3794
3795
3796 static void*
3797 icvReadSparseMat( CvFileStorage* fs, CvFileNode* node )
3798 {
3799     void* ptr = 0;
3800     CvSparseMat* mat;
3801     const char* dt;
3802     CvFileNode* data;
3803     CvFileNode* sizes_node;
3804     CvSeqReader reader;
3805     CvSeq* elements;
3806     int sizes[CV_MAX_DIM_HEAP], dims, elem_type, cn;
3807     int i;
3808
3809     sizes_node = cvGetFileNodeByName( fs, node, "sizes" );
3810     dt = cvReadStringByName( fs, node, "dt", 0 );
3811
3812     if( !sizes_node || !dt )
3813         CV_Error( CV_StsError, "Some of essential matrix attributes are absent" );
3814
3815     dims = CV_NODE_IS_SEQ(sizes_node->tag) ? sizes_node->data.seq->total :
3816            CV_NODE_IS_INT(sizes_node->tag) ? 1 : -1;
3817
3818     if( dims <= 0 || dims > CV_MAX_DIM_HEAP )
3819         CV_Error( CV_StsParseError, "Could not determine sparse matrix dimensionality" );
3820
3821     cvReadRawData( fs, sizes_node, sizes, "i" );
3822     elem_type = icvDecodeSimpleFormat( dt );
3823
3824     data = cvGetFileNodeByName( fs, node, "data" );
3825     if( !data || !CV_NODE_IS_SEQ(data->tag) )
3826         CV_Error( CV_StsError, "The matrix data is not found in file storage" );
3827
3828     mat = cvCreateSparseMat( dims, sizes, elem_type );
3829
3830     cn = CV_MAT_CN(elem_type);
3831     int idx[CV_MAX_DIM_HEAP];
3832     elements = data->data.seq;
3833     cvStartReadRawData( fs, data, &reader );
3834
3835     for( i = 0; i < elements->total; )
3836     {
3837         CvFileNode* elem = (CvFileNode*)reader.ptr;
3838         uchar* val;
3839         int k;
3840         if( !CV_NODE_IS_INT(elem->tag ))
3841             CV_Error( CV_StsParseError, "Sparse matrix data is corrupted" );
3842         k = elem->data.i;
3843         if( i > 0 && k >= 0 )
3844             idx[dims-1] = k;
3845         else
3846         {
3847             if( i > 0 )
3848                 k = dims + k - 1;
3849             else
3850                 idx[0] = k, k = 1;
3851             for( ; k < dims; k++ )
3852             {
3853                 CV_NEXT_SEQ_ELEM( elements->elem_size, reader );
3854                 i++;
3855                 elem = (CvFileNode*)reader.ptr;
3856                 if( !CV_NODE_IS_INT(elem->tag ) || elem->data.i < 0 )
3857                     CV_Error( CV_StsParseError, "Sparse matrix data is corrupted" );
3858                 idx[k] = elem->data.i;
3859             }
3860         }
3861         CV_NEXT_SEQ_ELEM( elements->elem_size, reader );
3862         i++;
3863         val = cvPtrND( mat, idx, 0, 1, 0 );
3864         cvReadRawDataSlice( fs, &reader, cn, val, dt );
3865         i += cn;
3866     }
3867
3868     ptr = mat;
3869     return ptr;
3870 }
3871
3872
3873 /******************************* IplImage ******************************/
3874
3875 static int
3876 icvIsImage( const void* ptr )
3877 {
3878     return CV_IS_IMAGE_HDR(ptr);
3879 }
3880
3881 static void
3882 icvWriteImage( CvFileStorage* fs, const char* name,
3883                const void* struct_ptr, CvAttrList /*attr*/ )
3884 {
3885     const IplImage* image = (const IplImage*)struct_ptr;
3886     char dt_buf[16], *dt;
3887     CvSize size;
3888     int y, depth;
3889
3890     assert( CV_IS_IMAGE(image) );
3891
3892     if( image->dataOrder == IPL_DATA_ORDER_PLANE )
3893         CV_Error( CV_StsUnsupportedFormat,
3894         "Images with planar data layout are not supported" );
3895
3896     cvStartWriteStruct( fs, name, CV_NODE_MAP, CV_TYPE_NAME_IMAGE );
3897     cvWriteInt( fs, "width", image->width );
3898     cvWriteInt( fs, "height", image->height );
3899     cvWriteString( fs, "origin", image->origin == IPL_ORIGIN_TL
3900                    ? "top-left" : "bottom-left", 0 );
3901     cvWriteString( fs, "layout", image->dataOrder == IPL_DATA_ORDER_PLANE
3902                    ? "planar" : "interleaved", 0 );
3903     if( image->roi )
3904     {
3905         cvStartWriteStruct( fs, "roi", CV_NODE_MAP + CV_NODE_FLOW );
3906         cvWriteInt( fs, "x", image->roi->xOffset );
3907         cvWriteInt( fs, "y", image->roi->yOffset );
3908         cvWriteInt( fs, "width", image->roi->width );
3909         cvWriteInt( fs, "height", image->roi->height );
3910         cvWriteInt( fs, "coi", image->roi->coi );
3911         cvEndWriteStruct( fs );
3912     }
3913
3914     depth = IPL2CV_DEPTH(image->depth);
3915     sprintf( dt_buf, "%d%c", image->nChannels, icvTypeSymbol[depth] );
3916     dt = dt_buf + (dt_buf[2] == '\0' && dt_buf[0] == '1');
3917     cvWriteString( fs, "dt", dt, 0 );
3918
3919     size = cvSize(image->width, image->height);
3920     if( size.width*image->nChannels*CV_ELEM_SIZE(depth) == image->widthStep )
3921     {
3922         size.width *= size.height;
3923         size.height = 1;
3924     }
3925
3926     cvStartWriteStruct( fs, "data", CV_NODE_SEQ + CV_NODE_FLOW );
3927     for( y = 0; y < size.height; y++ )
3928         cvWriteRawData( fs, image->imageData + y*image->widthStep, size.width, dt );
3929     cvEndWriteStruct( fs );
3930     cvEndWriteStruct( fs );
3931 }
3932
3933
3934 static void*
3935 icvReadImage( CvFileStorage* fs, CvFileNode* node )
3936 {
3937     void* ptr = 0;
3938     IplImage* image;
3939     const char* dt;
3940     CvFileNode* data;
3941     CvFileNode* roi_node;
3942     CvSeqReader reader;
3943     CvRect roi;
3944     int y, width, height, elem_type, coi, depth;
3945     const char* origin, *data_order;
3946
3947     width = cvReadIntByName( fs, node, "width", 0 );
3948     height = cvReadIntByName( fs, node, "height", 0 );
3949     dt = cvReadStringByName( fs, node, "dt", 0 );
3950     origin = cvReadStringByName( fs, node, "origin", 0 );
3951
3952     if( width == 0 || height == 0 || dt == 0 || origin == 0 )
3953         CV_Error( CV_StsError, "Some of essential image attributes are absent" );
3954
3955     elem_type = icvDecodeSimpleFormat( dt );
3956     data_order = cvReadStringByName( fs, node, "layout", "interleaved" );
3957     if( strcmp( data_order, "interleaved" ) != 0 )
3958         CV_Error( CV_StsError, "Only interleaved images can be read" );
3959
3960     data = cvGetFileNodeByName( fs, node, "data" );
3961     if( !data )
3962         CV_Error( CV_StsError, "The image data is not found in file storage" );
3963
3964     if( icvFileNodeSeqLen( data ) != width*height*CV_MAT_CN(elem_type) )
3965         CV_Error( CV_StsUnmatchedSizes,
3966         "The matrix size does not match to the number of stored elements" );
3967
3968     depth = cvIplDepth(elem_type);
3969     image = cvCreateImage( cvSize(width,height), depth, CV_MAT_CN(elem_type) );
3970
3971     roi_node = cvGetFileNodeByName( fs, node, "roi" );
3972     if( roi_node )
3973     {
3974         roi.x = cvReadIntByName( fs, roi_node, "x", 0 );
3975         roi.y = cvReadIntByName( fs, roi_node, "y", 0 );
3976         roi.width = cvReadIntByName( fs, roi_node, "width", 0 );
3977         roi.height = cvReadIntByName( fs, roi_node, "height", 0 );
3978         coi = cvReadIntByName( fs, roi_node, "coi", 0 );
3979
3980         cvSetImageROI( image, roi );
3981         cvSetImageCOI( image, coi );
3982     }
3983
3984     if( width*CV_ELEM_SIZE(elem_type) == image->widthStep )
3985     {
3986         width *= height;
3987         height = 1;
3988     }
3989
3990     width *= CV_MAT_CN(elem_type);
3991     cvStartReadRawData( fs, data, &reader );
3992     for( y = 0; y < height; y++ )
3993     {
3994         cvReadRawDataSlice( fs, &reader, width,
3995             image->imageData + y*image->widthStep, dt );
3996     }
3997
3998     ptr = image;
3999     return ptr;
4000 }
4001
4002
4003 /******************************* CvSeq ******************************/
4004
4005 static int
4006 icvIsSeq( const void* ptr )
4007 {
4008     return CV_IS_SEQ(ptr);
4009 }
4010
4011
4012 static void
4013 icvReleaseSeq( void** ptr )
4014 {
4015     if( !ptr )
4016         CV_Error( CV_StsNullPtr, "NULL double pointer" );
4017     *ptr = 0; // it's impossible now to release seq, so just clear the pointer
4018 }
4019
4020
4021 static void*
4022 icvCloneSeq( const void* ptr )
4023 {
4024     return cvSeqSlice( (CvSeq*)ptr, CV_WHOLE_SEQ,
4025                        0 /* use the same storage as for the original sequence */, 1 );
4026 }
4027
4028
4029 static void
4030 icvWriteHeaderData( CvFileStorage* fs, const CvSeq* seq,
4031                     CvAttrList* attr, int initial_header_size )
4032 {
4033     char header_dt_buf[128];
4034     const char* header_dt = cvAttrValue( attr, "header_dt" );
4035
4036     if( header_dt )
4037     {
4038         int dt_header_size;
4039         dt_header_size = icvCalcElemSize( header_dt, initial_header_size );
4040         if( dt_header_size > seq->header_size )
4041             CV_Error( CV_StsUnmatchedSizes,
4042             "The size of header calculated from \"header_dt\" is greater than header_size" );
4043     }
4044     else if( seq->header_size > initial_header_size )
4045     {
4046         if( CV_IS_SEQ(seq) && CV_IS_SEQ_POINT_SET(seq) &&
4047             seq->header_size == sizeof(CvPoint2DSeq) &&
4048             seq->elem_size == sizeof(int)*2 )
4049         {
4050             CvPoint2DSeq* point_seq = (CvPoint2DSeq*)seq;
4051
4052             cvStartWriteStruct( fs, "rect", CV_NODE_MAP + CV_NODE_FLOW );
4053             cvWriteInt( fs, "x", point_seq->rect.x );
4054             cvWriteInt( fs, "y", point_seq->rect.y );
4055             cvWriteInt( fs, "width", point_seq->rect.width );
4056             cvWriteInt( fs, "height", point_seq->rect.height );
4057             cvEndWriteStruct( fs );
4058             cvWriteInt( fs, "color", point_seq->color );
4059         }
4060         else if( CV_IS_SEQ(seq) && CV_IS_SEQ_CHAIN(seq) &&
4061                  CV_MAT_TYPE(seq->flags) == CV_8UC1 )
4062         {
4063             CvChain* chain = (CvChain*)seq;
4064
4065             cvStartWriteStruct( fs, "origin", CV_NODE_MAP + CV_NODE_FLOW );
4066             cvWriteInt( fs, "x", chain->origin.x );
4067             cvWriteInt( fs, "y", chain->origin.y );
4068             cvEndWriteStruct( fs );
4069         }
4070         else
4071         {
4072             unsigned extra_size = seq->header_size - initial_header_size;
4073             // a heuristic to provide nice defaults for sequences of int's & float's
4074             if( extra_size % sizeof(int) == 0 )
4075                 sprintf( header_dt_buf, "%ui", (unsigned)(extra_size/sizeof(int)) );
4076             else
4077                 sprintf( header_dt_buf, "%uu", extra_size );
4078             header_dt = header_dt_buf;
4079         }
4080     }
4081
4082     if( header_dt )
4083     {
4084         cvWriteString( fs, "header_dt", header_dt, 0 );
4085         cvStartWriteStruct( fs, "header_user_data", CV_NODE_SEQ + CV_NODE_FLOW );
4086         cvWriteRawData( fs, (uchar*)seq + sizeof(CvSeq), 1, header_dt );
4087         cvEndWriteStruct( fs );
4088     }
4089 }
4090
4091
4092 static char*
4093 icvGetFormat( const CvSeq* seq, const char* dt_key, CvAttrList* attr,
4094               int initial_elem_size, char* dt_buf )
4095 {
4096     char* dt = 0;
4097     dt = (char*)cvAttrValue( attr, dt_key );
4098
4099     if( dt )
4100     {
4101         int dt_elem_size;
4102         dt_elem_size = icvCalcElemSize( dt, initial_elem_size );
4103         if( dt_elem_size != seq->elem_size )
4104             CV_Error( CV_StsUnmatchedSizes,
4105             "The size of element calculated from \"dt\" and "
4106             "the elem_size do not match" );
4107     }
4108     else if( CV_MAT_TYPE(seq->flags) != 0 || seq->elem_size == 1 )
4109     {
4110         if( CV_ELEM_SIZE(seq->flags) != seq->elem_size )
4111             CV_Error( CV_StsUnmatchedSizes,
4112             "Size of sequence element (elem_size) is inconsistent with seq->flags" );
4113         dt = icvEncodeFormat( CV_MAT_TYPE(seq->flags), dt_buf );
4114     }
4115     else if( seq->elem_size > initial_elem_size )
4116     {
4117         unsigned extra_elem_size = seq->elem_size - initial_elem_size;
4118         // a heuristic to provide nice defaults for sequences of int's & float's
4119         if( extra_elem_size % sizeof(int) == 0 )
4120             sprintf( dt_buf, "%ui", (unsigned)(extra_elem_size/sizeof(int)) );
4121         else
4122             sprintf( dt_buf, "%uu", extra_elem_size );
4123         dt = dt_buf;
4124     }
4125
4126     return dt;
4127 }
4128
4129
4130 static void
4131 icvWriteSeq( CvFileStorage* fs, const char* name,
4132              const void* struct_ptr,
4133              CvAttrList attr, int level )
4134 {
4135     const CvSeq* seq = (CvSeq*)struct_ptr;
4136     CvSeqBlock* block;
4137     char buf[128];
4138     char dt_buf[128], *dt;
4139
4140     assert( CV_IS_SEQ( seq ));
4141     cvStartWriteStruct( fs, name, CV_NODE_MAP, CV_TYPE_NAME_SEQ );
4142
4143     if( level >= 0 )
4144         cvWriteInt( fs, "level", level );
4145
4146     dt = icvGetFormat( seq, "dt", &attr, 0, dt_buf );
4147
4148     strcpy(buf, "");
4149     if( CV_IS_SEQ_CLOSED(seq) )
4150         strcat(buf, " closed");
4151     if( CV_IS_SEQ_HOLE(seq) )
4152         strcat(buf, " hole");
4153     if( CV_IS_SEQ_CURVE(seq) )
4154         strcat(buf, " curve");
4155     if( CV_SEQ_ELTYPE(seq) == 0 && seq->elem_size != 1 )
4156         strcat(buf, " untyped");
4157
4158     cvWriteString( fs, "flags", buf + (buf[0] ? 1 : 0), 1 );
4159
4160     cvWriteInt( fs, "count", seq->total );
4161
4162     cvWriteString( fs, "dt", dt, 0 );
4163
4164     icvWriteHeaderData( fs, seq, &attr, sizeof(CvSeq) );
4165     cvStartWriteStruct( fs, "data", CV_NODE_SEQ + CV_NODE_FLOW );
4166
4167     for( block = seq->first; block; block = block->next )
4168     {
4169         cvWriteRawData( fs, block->data, block->count, dt );
4170         if( block == seq->first->prev )
4171             break;
4172     }
4173     cvEndWriteStruct( fs );
4174     cvEndWriteStruct( fs );
4175 }
4176
4177
4178 static void
4179 icvWriteSeqTree( CvFileStorage* fs, const char* name,
4180                  const void* struct_ptr, CvAttrList attr )
4181 {
4182     const CvSeq* seq = (CvSeq*)struct_ptr;
4183     const char* recursive_value = cvAttrValue( &attr, "recursive" );
4184     int is_recursive = recursive_value &&
4185                        strcmp(recursive_value,"0") != 0 &&
4186                        strcmp(recursive_value,"false") != 0 &&
4187                        strcmp(recursive_value,"False") != 0 &&
4188                        strcmp(recursive_value,"FALSE") != 0;
4189
4190     assert( CV_IS_SEQ( seq ));
4191
4192     if( !is_recursive )
4193     {
4194         icvWriteSeq( fs, name, seq, attr, -1 );
4195     }
4196     else
4197     {
4198         CvTreeNodeIterator tree_iterator;
4199
4200         cvStartWriteStruct( fs, name, CV_NODE_MAP, CV_TYPE_NAME_SEQ_TREE );
4201         cvStartWriteStruct( fs, "sequences", CV_NODE_SEQ );
4202         cvInitTreeNodeIterator( &tree_iterator, seq, INT_MAX );
4203
4204         for(;;)
4205         {
4206             if( !tree_iterator.node )
4207                 break;
4208             icvWriteSeq( fs, 0, tree_iterator.node, attr, tree_iterator.level );
4209             cvNextTreeNode( &tree_iterator );
4210         }
4211
4212         cvEndWriteStruct( fs );
4213         cvEndWriteStruct( fs );
4214     }
4215 }
4216
4217
4218 static void*
4219 icvReadSeq( CvFileStorage* fs, CvFileNode* node )
4220 {
4221     void* ptr = 0;
4222     CvSeq* seq;
4223     CvSeqBlock* block;
4224     CvFileNode *data, *header_node, *rect_node, *origin_node;
4225     CvSeqReader reader;
4226     int total, flags;
4227     int elem_size, header_size = sizeof(CvSeq);
4228     int fmt_pairs[CV_FS_MAX_FMT_PAIRS], i, fmt_pair_count;
4229     int items_per_elem = 0;
4230     const char* flags_str;
4231     const char* header_dt;
4232     const char* dt;
4233     char* endptr = 0;
4234
4235     flags_str = cvReadStringByName( fs, node, "flags", 0 );
4236     total = cvReadIntByName( fs, node, "count", -1 );
4237     dt = cvReadStringByName( fs, node, "dt", 0 );
4238
4239     if( !flags_str || total == -1 || !dt )
4240         CV_Error( CV_StsError, "Some of essential sequence attributes are absent" );
4241
4242     flags = CV_SEQ_MAGIC_VAL;
4243
4244     if( cv_isdigit(flags_str[0]) )
4245     {
4246         const int OLD_SEQ_ELTYPE_BITS = 9;
4247         const int OLD_SEQ_ELTYPE_MASK = (1 << OLD_SEQ_ELTYPE_BITS) - 1;
4248         const int OLD_SEQ_KIND_BITS = 3;
4249         const int OLD_SEQ_KIND_MASK = ((1 << OLD_SEQ_KIND_BITS) - 1) << OLD_SEQ_ELTYPE_BITS;
4250         const int OLD_SEQ_KIND_CURVE = 1 << OLD_SEQ_ELTYPE_BITS;
4251         const int OLD_SEQ_FLAG_SHIFT = OLD_SEQ_KIND_BITS + OLD_SEQ_ELTYPE_BITS;
4252         const int OLD_SEQ_FLAG_CLOSED = 1 << OLD_SEQ_FLAG_SHIFT;
4253         const int OLD_SEQ_FLAG_HOLE = 8 << OLD_SEQ_FLAG_SHIFT;
4254
4255         int flags0 = (int)strtol( flags_str, &endptr, 16 );
4256         if( endptr == flags_str || (flags0 & CV_MAGIC_MASK) != CV_SEQ_MAGIC_VAL )
4257             CV_Error( CV_StsError, "The sequence flags are invalid" );
4258         if( (flags0 & OLD_SEQ_KIND_MASK) == OLD_SEQ_KIND_CURVE )
4259             flags |= CV_SEQ_KIND_CURVE;
4260         if( flags0 & OLD_SEQ_FLAG_CLOSED )
4261             flags |= CV_SEQ_FLAG_CLOSED;
4262         if( flags0 & OLD_SEQ_FLAG_HOLE )
4263             flags |= CV_SEQ_FLAG_HOLE;
4264         flags |= flags0 & OLD_SEQ_ELTYPE_MASK;
4265     }
4266     else
4267     {
4268         if( strstr(flags_str, "curve") )
4269             flags |= CV_SEQ_KIND_CURVE;
4270         if( strstr(flags_str, "closed") )
4271             flags |= CV_SEQ_FLAG_CLOSED;
4272         if( strstr(flags_str, "hole") )
4273             flags |= CV_SEQ_FLAG_HOLE;
4274         if( !strstr(flags_str, "untyped") )
4275         {
4276             try
4277             {
4278                 flags |= icvDecodeSimpleFormat(dt);
4279             }
4280             catch(...)
4281             {
4282             }
4283         }
4284     }
4285
4286     header_dt = cvReadStringByName( fs, node, "header_dt", 0 );
4287     header_node = cvGetFileNodeByName( fs, node, "header_user_data" );
4288
4289     if( (header_dt != 0) ^ (header_node != 0) )
4290         CV_Error( CV_StsError,
4291         "One of \"header_dt\" and \"header_user_data\" is there, while the other is not" );
4292
4293     rect_node = cvGetFileNodeByName( fs, node, "rect" );
4294     origin_node = cvGetFileNodeByName( fs, node, "origin" );
4295
4296     if( (header_node != 0) + (rect_node != 0) + (origin_node != 0) > 1 )
4297         CV_Error( CV_StsError, "Only one of \"header_user_data\", \"rect\" and \"origin\" tags may occur" );
4298
4299     if( header_dt )
4300     {
4301         header_size = icvCalcElemSize( header_dt, header_size );
4302     }
4303     else if( rect_node )
4304         header_size = sizeof(CvPoint2DSeq);
4305     else if( origin_node )
4306         header_size = sizeof(CvChain);
4307
4308     elem_size = icvCalcElemSize( dt, 0 );
4309     seq = cvCreateSeq( flags, header_size, elem_size, fs->dststorage );
4310
4311     if( header_node )
4312     {
4313         cvReadRawData( fs, header_node, (char*)seq + sizeof(CvSeq), header_dt );
4314     }
4315     else if( rect_node )
4316     {
4317         CvPoint2DSeq* point_seq = (CvPoint2DSeq*)seq;
4318         point_seq->rect.x = cvReadIntByName( fs, rect_node, "x", 0 );
4319         point_seq->rect.y = cvReadIntByName( fs, rect_node, "y", 0 );
4320         point_seq->rect.width = cvReadIntByName( fs, rect_node, "width", 0 );
4321         point_seq->rect.height = cvReadIntByName( fs, rect_node, "height", 0 );
4322         point_seq->color = cvReadIntByName( fs, node, "color", 0 );
4323     }
4324     else if( origin_node )
4325     {
4326         CvChain* chain = (CvChain*)seq;
4327         chain->origin.x = cvReadIntByName( fs, origin_node, "x", 0 );
4328         chain->origin.y = cvReadIntByName( fs, origin_node, "y", 0 );
4329     }
4330
4331     cvSeqPushMulti( seq, 0, total, 0 );
4332     fmt_pair_count = icvDecodeFormat( dt, fmt_pairs, CV_FS_MAX_FMT_PAIRS );
4333     fmt_pair_count *= 2;
4334     for( i = 0; i < fmt_pair_count; i += 2 )
4335         items_per_elem += fmt_pairs[i];
4336
4337     data = cvGetFileNodeByName( fs, node, "data" );
4338     if( !data )
4339         CV_Error( CV_StsError, "The image data is not found in file storage" );
4340
4341     if( icvFileNodeSeqLen( data ) != total*items_per_elem )
4342         CV_Error( CV_StsError, "The number of stored elements does not match to \"count\"" );
4343
4344     cvStartReadRawData( fs, data, &reader );
4345     for( block = seq->first; block; block = block->next )
4346     {
4347         int delta = block->count*items_per_elem;
4348         cvReadRawDataSlice( fs, &reader, delta, block->data, dt );
4349         if( block == seq->first->prev )
4350             break;
4351     }
4352
4353     ptr = seq;
4354     return ptr;
4355 }
4356
4357
4358 static void*
4359 icvReadSeqTree( CvFileStorage* fs, CvFileNode* node )
4360 {
4361     void* ptr = 0;
4362     CvFileNode *sequences_node = cvGetFileNodeByName( fs, node, "sequences" );
4363     CvSeq* sequences;
4364     CvSeq* root = 0;
4365     CvSeq* parent = 0;
4366     CvSeq* prev_seq = 0;
4367     CvSeqReader reader;
4368     int i, total;
4369     int prev_level = 0;
4370
4371     if( !sequences_node || !CV_NODE_IS_SEQ(sequences_node->tag) )
4372         CV_Error( CV_StsParseError,
4373         "opencv-sequence-tree instance should contain a field \"sequences\" that should be a sequence" );
4374
4375     sequences = sequences_node->data.seq;
4376     total = sequences->total;
4377
4378     cvStartReadSeq( sequences, &reader, 0 );
4379     for( i = 0; i < total; i++ )
4380     {
4381         CvFileNode* elem = (CvFileNode*)reader.ptr;
4382         CvSeq* seq;
4383         int level;
4384         seq = (CvSeq*)cvRead( fs, elem );
4385         level = cvReadIntByName( fs, elem, "level", -1 );
4386         if( level < 0 )
4387             CV_Error( CV_StsParseError, "All the sequence tree nodes should contain \"level\" field" );
4388         if( !root )
4389             root = seq;
4390         if( level > prev_level )
4391         {
4392             assert( level == prev_level + 1 );
4393             parent = prev_seq;
4394             prev_seq = 0;
4395             if( parent )
4396                 parent->v_next = seq;
4397         }
4398         else if( level < prev_level )
4399         {
4400             for( ; prev_level > level; prev_level-- )
4401                 prev_seq = prev_seq->v_prev;
4402             parent = prev_seq->v_prev;
4403         }
4404         seq->h_prev = prev_seq;
4405         if( prev_seq )
4406             prev_seq->h_next = seq;
4407         seq->v_prev = parent;
4408         prev_seq = seq;
4409         prev_level = level;
4410         CV_NEXT_SEQ_ELEM( sequences->elem_size, reader );
4411     }
4412
4413     ptr = root;
4414     return ptr;
4415 }
4416
4417 /******************************* CvGraph ******************************/
4418
4419 static int
4420 icvIsGraph( const void* ptr )
4421 {
4422     return CV_IS_GRAPH(ptr);
4423 }
4424
4425
4426 static void
4427 icvReleaseGraph( void** ptr )
4428 {
4429     if( !ptr )
4430         CV_Error( CV_StsNullPtr, "NULL double pointer" );
4431
4432     *ptr = 0; // it's impossible now to release graph, so just clear the pointer
4433 }
4434
4435
4436 static void*
4437 icvCloneGraph( const void* ptr )
4438 {
4439     return cvCloneGraph( (const CvGraph*)ptr, 0 );
4440 }
4441
4442
4443 static void
4444 icvWriteGraph( CvFileStorage* fs, const char* name,
4445                const void* struct_ptr, CvAttrList attr )
4446 {
4447     int* flag_buf = 0;
4448     char* write_buf = 0;
4449     const CvGraph* graph = (const CvGraph*)struct_ptr;
4450     CvSeqReader reader;
4451     char buf[128];
4452     int i, k, vtx_count, edge_count;
4453     char vtx_dt_buf[128], *vtx_dt;
4454     char edge_dt_buf[128], *edge_dt;
4455     int write_buf_size;
4456
4457     assert( CV_IS_GRAPH(graph) );
4458     vtx_count = cvGraphGetVtxCount( graph );
4459     edge_count = cvGraphGetEdgeCount( graph );
4460     flag_buf = (int*)cvAlloc( vtx_count*sizeof(flag_buf[0]));
4461
4462     // count vertices
4463     cvStartReadSeq( (CvSeq*)graph, &reader );
4464     for( i = 0, k = 0; i < graph->total; i++ )
4465     {
4466         if( CV_IS_SET_ELEM( reader.ptr ))
4467         {
4468             CvGraphVtx* vtx = (CvGraphVtx*)reader.ptr;
4469             flag_buf[k] = vtx->flags;
4470             vtx->flags = k++;
4471         }
4472         CV_NEXT_SEQ_ELEM( graph->elem_size, reader );
4473     }
4474
4475     // write header
4476     cvStartWriteStruct( fs, name, CV_NODE_MAP, CV_TYPE_NAME_GRAPH );
4477
4478     cvWriteString(fs, "flags", CV_IS_GRAPH_ORIENTED(graph) ? "oriented" : "", 1);
4479
4480     cvWriteInt( fs, "vertex_count", vtx_count );
4481     vtx_dt = icvGetFormat( (CvSeq*)graph, "vertex_dt",
4482                     &attr, sizeof(CvGraphVtx), vtx_dt_buf );
4483     if( vtx_dt )
4484         cvWriteString( fs, "vertex_dt", vtx_dt, 0 );
4485
4486     cvWriteInt( fs, "edge_count", edge_count );
4487     edge_dt = icvGetFormat( (CvSeq*)graph->edges, "edge_dt",
4488                                 &attr, sizeof(CvGraphEdge), buf );
4489     sprintf( edge_dt_buf, "2if%s", edge_dt ? edge_dt : "" );
4490     edge_dt = edge_dt_buf;
4491     cvWriteString( fs, "edge_dt", edge_dt, 0 );
4492
4493     icvWriteHeaderData( fs, (CvSeq*)graph, &attr, sizeof(CvGraph) );
4494
4495     write_buf_size = MAX( 3*graph->elem_size, 1 << 16 );
4496     write_buf_size = MAX( 3*graph->edges->elem_size, write_buf_size );
4497     write_buf = (char*)cvAlloc( write_buf_size );
4498
4499     // as vertices and edges are written in similar way,
4500     // do it as a parametrized 2-iteration loop
4501     for( k = 0; k < 2; k++ )
4502     {
4503         const char* dt = k == 0 ? vtx_dt : edge_dt;
4504         if( dt )
4505         {
4506             CvSet* data = k == 0 ? (CvSet*)graph : graph->edges;
4507             int elem_size = data->elem_size;
4508             int write_elem_size = icvCalcElemSize( dt, 0 );
4509             char* src_ptr = write_buf;
4510             int write_max = write_buf_size / write_elem_size, write_count = 0;
4511
4512             // alignment of user part of the edge data following 2if
4513             int edge_user_align = sizeof(float);
4514
4515             if( k == 1 )
4516             {
4517                 int fmt_pairs[CV_FS_MAX_FMT_PAIRS], fmt_pair_count;
4518                 fmt_pair_count = icvDecodeFormat( dt, fmt_pairs, CV_FS_MAX_FMT_PAIRS );
4519                 if( fmt_pair_count > 2 && CV_ELEM_SIZE(fmt_pairs[2*2+1]) >= (int)sizeof(double))
4520                     edge_user_align = sizeof(double);
4521             }
4522
4523             cvStartWriteStruct( fs, k == 0 ? "vertices" : "edges",
4524                                 CV_NODE_SEQ + CV_NODE_FLOW );
4525             cvStartReadSeq( (CvSeq*)data, &reader );
4526             for( i = 0; i < data->total; i++ )
4527             {
4528                 if( CV_IS_SET_ELEM( reader.ptr ))
4529                 {
4530                     if( k == 0 ) // vertices
4531                         memcpy( src_ptr, reader.ptr + sizeof(CvGraphVtx), write_elem_size );
4532                     else
4533                     {
4534                         CvGraphEdge* edge = (CvGraphEdge*)reader.ptr;
4535                         src_ptr = (char*)cvAlignPtr( src_ptr, sizeof(int) );
4536                         ((int*)src_ptr)[0] = edge->vtx[0]->flags;
4537                         ((int*)src_ptr)[1] = edge->vtx[1]->flags;
4538                         *(float*)(src_ptr + sizeof(int)*2) = edge->weight;
4539                         if( elem_size > (int)sizeof(CvGraphEdge) )
4540                         {
4541                             char* src_ptr2 = (char*)cvAlignPtr( src_ptr + 2*sizeof(int)
4542                                                 + sizeof(float), edge_user_align );
4543                             memcpy( src_ptr2, edge + 1, elem_size - sizeof(CvGraphEdge) );
4544                         }
4545                     }
4546                     src_ptr += write_elem_size;
4547                     if( ++write_count >= write_max )
4548                     {
4549                         cvWriteRawData( fs, write_buf, write_count, dt );
4550                         write_count = 0;
4551                         src_ptr = write_buf;
4552                     }
4553                 }
4554                 CV_NEXT_SEQ_ELEM( data->elem_size, reader );
4555             }
4556
4557             if( write_count > 0 )
4558                 cvWriteRawData( fs, write_buf, write_count, dt );
4559             cvEndWriteStruct( fs );
4560         }
4561     }
4562
4563     cvEndWriteStruct( fs );
4564
4565     // final stage. restore the graph flags
4566     cvStartReadSeq( (CvSeq*)graph, &reader );
4567     vtx_count = 0;
4568     for( i = 0; i < graph->total; i++ )
4569     {
4570         if( CV_IS_SET_ELEM( reader.ptr ))
4571             ((CvGraphVtx*)reader.ptr)->flags = flag_buf[vtx_count++];
4572         CV_NEXT_SEQ_ELEM( graph->elem_size, reader );
4573     }
4574
4575     cvFree( &write_buf );
4576     cvFree( &flag_buf );
4577 }
4578
4579
4580 static void*
4581 icvReadGraph( CvFileStorage* fs, CvFileNode* node )
4582 {
4583     void* ptr = 0;
4584     char* read_buf = 0;
4585     CvGraphVtx** vtx_buf = 0;
4586     CvGraph* graph;
4587     CvFileNode *header_node, *vtx_node, *edge_node;
4588     int flags, vtx_count, edge_count;
4589     int vtx_size = sizeof(CvGraphVtx), edge_size, header_size = sizeof(CvGraph);
4590     int src_vtx_size = 0, src_edge_size;
4591     int fmt_pairs[CV_FS_MAX_FMT_PAIRS], fmt_pair_count;
4592     int vtx_items_per_elem = 0, edge_items_per_elem = 0;
4593     int edge_user_align = sizeof(float);
4594     int read_buf_size;
4595     int i, k;
4596     const char* flags_str;
4597     const char* header_dt;
4598     const char* vtx_dt;
4599     const char* edge_dt;
4600     char* endptr = 0;
4601
4602     flags_str = cvReadStringByName( fs, node, "flags", 0 );
4603     vtx_dt = cvReadStringByName( fs, node, "vertex_dt", 0 );
4604     edge_dt = cvReadStringByName( fs, node, "edge_dt", 0 );
4605     vtx_count = cvReadIntByName( fs, node, "vertex_count", -1 );
4606     edge_count = cvReadIntByName( fs, node, "edge_count", -1 );
4607
4608     if( !flags_str || vtx_count == -1 || edge_count == -1 || !edge_dt )
4609         CV_Error( CV_StsError, "Some of essential graph attributes are absent" );
4610
4611     flags = CV_SET_MAGIC_VAL + CV_GRAPH;
4612
4613     if( isxdigit(flags_str[0]) )
4614     {
4615         const int OLD_SEQ_ELTYPE_BITS = 9;
4616         const int OLD_SEQ_KIND_BITS = 3;
4617         const int OLD_SEQ_FLAG_SHIFT = OLD_SEQ_KIND_BITS + OLD_SEQ_ELTYPE_BITS;
4618         const int OLD_GRAPH_FLAG_ORIENTED = 1 << OLD_SEQ_FLAG_SHIFT;
4619
4620         int flags0 = (int)strtol( flags_str, &endptr, 16 );
4621         if( endptr == flags_str || (flags0 & CV_MAGIC_MASK) != CV_SET_MAGIC_VAL )
4622             CV_Error( CV_StsError, "The sequence flags are invalid" );
4623         if( flags0 & OLD_GRAPH_FLAG_ORIENTED )
4624             flags |= CV_GRAPH_FLAG_ORIENTED;
4625     }
4626     else
4627     {
4628         if( strstr(flags_str, "oriented") )
4629             flags |= CV_GRAPH_FLAG_ORIENTED;
4630     }
4631
4632     header_dt = cvReadStringByName( fs, node, "header_dt", 0 );
4633     header_node = cvGetFileNodeByName( fs, node, "header_user_data" );
4634
4635     if( (header_dt != 0) ^ (header_node != 0) )
4636         CV_Error( CV_StsError,
4637         "One of \"header_dt\" and \"header_user_data\" is there, while the other is not" );
4638
4639     if( header_dt )
4640         header_size = icvCalcElemSize( header_dt, header_size );
4641
4642     if( vtx_dt )
4643     {
4644         src_vtx_size = icvCalcElemSize( vtx_dt, 0 );
4645         vtx_size = icvCalcElemSize( vtx_dt, vtx_size );
4646         fmt_pair_count = icvDecodeFormat( edge_dt,
4647                             fmt_pairs, CV_FS_MAX_FMT_PAIRS );
4648         fmt_pair_count *= 2;
4649         for( i = 0; i < fmt_pair_count; i += 2 )
4650             vtx_items_per_elem += fmt_pairs[i];
4651     }
4652
4653     {
4654         char dst_edge_dt_buf[128];
4655         const char* dst_edge_dt = 0;
4656
4657         fmt_pair_count = icvDecodeFormat( edge_dt,
4658                             fmt_pairs, CV_FS_MAX_FMT_PAIRS );
4659         if( fmt_pair_count < 2 ||
4660             fmt_pairs[0] != 2 || fmt_pairs[1] != CV_32S ||
4661             fmt_pairs[2] < 1 || fmt_pairs[3] != CV_32F )
4662             CV_Error( CV_StsBadArg,
4663             "Graph edges should start with 2 integers and a float" );
4664
4665         // alignment of user part of the edge data following 2if
4666         if( fmt_pair_count > 2 && CV_ELEM_SIZE(fmt_pairs[5]) >= (int)sizeof(double))
4667             edge_user_align = sizeof(double);
4668
4669         fmt_pair_count *= 2;
4670         for( i = 0; i < fmt_pair_count; i += 2 )
4671             edge_items_per_elem += fmt_pairs[i];
4672
4673         if( edge_dt[2] == 'f' || (edge_dt[2] == '1' && edge_dt[3] == 'f') )
4674             dst_edge_dt = edge_dt + 3 + cv_isdigit(edge_dt[2]);
4675         else
4676         {
4677             int val = (int)strtol( edge_dt + 2, &endptr, 10 );
4678             sprintf( dst_edge_dt_buf, "%df%s", val-1, endptr );
4679             dst_edge_dt = dst_edge_dt_buf;
4680         }
4681
4682         edge_size = icvCalcElemSize( dst_edge_dt, sizeof(CvGraphEdge) );
4683         src_edge_size = icvCalcElemSize( edge_dt, 0 );
4684     }
4685
4686     graph = cvCreateGraph( flags, header_size, vtx_size, edge_size, fs->dststorage );
4687
4688     if( header_node )
4689         cvReadRawData( fs, header_node, (char*)graph + sizeof(CvGraph), header_dt );
4690
4691     read_buf_size = MAX( src_vtx_size*3, 1 << 16 );
4692     read_buf_size = MAX( src_edge_size*3, read_buf_size );
4693     read_buf = (char*)cvAlloc( read_buf_size );
4694     vtx_buf = (CvGraphVtx**)cvAlloc( vtx_count * sizeof(vtx_buf[0]) );
4695
4696     vtx_node = cvGetFileNodeByName( fs, node, "vertices" );
4697     edge_node = cvGetFileNodeByName( fs, node, "edges" );
4698     if( !edge_node )
4699         CV_Error( CV_StsBadArg, "No edges data" );
4700     if( vtx_dt && !vtx_node )
4701         CV_Error( CV_StsBadArg, "No vertices data" );
4702
4703     // as vertices and edges are read in similar way,
4704     // do it as a parametrized 2-iteration loop
4705     for( k = 0; k < 2; k++ )
4706     {
4707         const char* dt = k == 0 ? vtx_dt : edge_dt;
4708         int elem_size = k == 0 ? vtx_size : edge_size;
4709         int src_elem_size = k == 0 ? src_vtx_size : src_edge_size;
4710         int items_per_elem = k == 0 ? vtx_items_per_elem : edge_items_per_elem;
4711         int elem_count = k == 0 ? vtx_count : edge_count;
4712         char* dst_ptr = read_buf;
4713         int read_max = read_buf_size /MAX(src_elem_size, 1), read_count = 0;
4714         CvSeqReader reader;
4715         if(dt)
4716             cvStartReadRawData( fs, k == 0 ? vtx_node : edge_node, &reader );
4717
4718         for( i = 0; i < elem_count; i++ )
4719         {
4720             if( read_count == 0 && dt )
4721             {
4722                 int count = MIN( elem_count - i, read_max )*items_per_elem;
4723                 cvReadRawDataSlice( fs, &reader, count, read_buf, dt );
4724                 read_count = count;
4725                 dst_ptr = read_buf;
4726             }
4727
4728             if( k == 0 )
4729             {
4730                 CvGraphVtx* vtx;
4731                 cvGraphAddVtx( graph, 0, &vtx );
4732                 vtx_buf[i] = vtx;
4733                 if( dt )
4734                     memcpy( vtx + 1, dst_ptr, src_elem_size );
4735             }
4736             else
4737             {
4738                 CvGraphEdge* edge = 0;
4739                 int vtx1 = ((int*)dst_ptr)[0];
4740                 int vtx2 = ((int*)dst_ptr)[1];
4741                 int result;
4742
4743                 if( (unsigned)vtx1 >= (unsigned)vtx_count ||
4744                     (unsigned)vtx2 >= (unsigned)vtx_count )
4745                     CV_Error( CV_StsOutOfRange,
4746                     "Some of stored vertex indices are out of range" );
4747
4748                 result = cvGraphAddEdgeByPtr( graph,
4749                     vtx_buf[vtx1], vtx_buf[vtx2], 0, &edge );
4750
4751                 if( result == 0 )
4752                     CV_Error( CV_StsBadArg, "Duplicated edge has occured" );
4753
4754                 edge->weight = *(float*)(dst_ptr + sizeof(int)*2);
4755                 if( elem_size > (int)sizeof(CvGraphEdge) )
4756                 {
4757                     char* dst_ptr2 = (char*)cvAlignPtr( dst_ptr + sizeof(int)*2 +
4758                                                 sizeof(float), edge_user_align );
4759                     memcpy( edge + 1, dst_ptr2, elem_size - sizeof(CvGraphEdge) );
4760                 }
4761             }
4762
4763             dst_ptr += src_elem_size;
4764             read_count--;
4765         }
4766     }
4767
4768     ptr = graph;
4769     cvFree( &read_buf );
4770     cvFree( &vtx_buf );
4771
4772     return ptr;
4773 }
4774
4775 /****************************************************************************************\
4776 *                                    RTTI Functions                                      *
4777 \****************************************************************************************/
4778
4779 CvTypeInfo *CvType::first = 0, *CvType::last = 0;
4780
4781 CvType::CvType( const char* type_name,
4782                 CvIsInstanceFunc is_instance, CvReleaseFunc release,
4783                 CvReadFunc read, CvWriteFunc write, CvCloneFunc clone )
4784 {
4785     CvTypeInfo _info;
4786     _info.flags = 0;
4787     _info.header_size = sizeof(_info);
4788     _info.type_name = type_name;
4789     _info.prev = _info.next = 0;
4790     _info.is_instance = is_instance;
4791     _info.release = release;
4792     _info.clone = clone;
4793     _info.read = read;
4794     _info.write = write;
4795
4796     cvRegisterType( &_info );
4797     info = first;
4798 }
4799
4800
4801 CvType::~CvType()
4802 {
4803     cvUnregisterType( info->type_name );
4804 }
4805
4806
4807 CvType seq_type( CV_TYPE_NAME_SEQ, icvIsSeq, icvReleaseSeq, icvReadSeq,
4808                  icvWriteSeqTree /* this is the entry point for
4809                  writing a single sequence too */, icvCloneSeq );
4810
4811 CvType seq_tree_type( CV_TYPE_NAME_SEQ_TREE, icvIsSeq, icvReleaseSeq,
4812                       icvReadSeqTree, icvWriteSeqTree, icvCloneSeq );
4813
4814 CvType seq_graph_type( CV_TYPE_NAME_GRAPH, icvIsGraph, icvReleaseGraph,
4815                        icvReadGraph, icvWriteGraph, icvCloneGraph );
4816
4817 CvType sparse_mat_type( CV_TYPE_NAME_SPARSE_MAT, icvIsSparseMat,
4818                         (CvReleaseFunc)cvReleaseSparseMat, icvReadSparseMat,
4819                         icvWriteSparseMat, (CvCloneFunc)cvCloneSparseMat );
4820
4821 CvType image_type( CV_TYPE_NAME_IMAGE, icvIsImage, (CvReleaseFunc)cvReleaseImage,
4822                    icvReadImage, icvWriteImage, (CvCloneFunc)cvCloneImage );
4823
4824 CvType mat_type( CV_TYPE_NAME_MAT, icvIsMat, (CvReleaseFunc)cvReleaseMat,
4825                  icvReadMat, icvWriteMat, (CvCloneFunc)cvCloneMat );
4826
4827 CvType matnd_type( CV_TYPE_NAME_MATND, icvIsMatND, (CvReleaseFunc)cvReleaseMatND,
4828                    icvReadMatND, icvWriteMatND, (CvCloneFunc)cvCloneMatND );
4829
4830 CV_IMPL  void
4831 cvRegisterType( const CvTypeInfo* _info )
4832 {
4833     CvTypeInfo* info = 0;
4834     int i, len;
4835     char c;
4836
4837     //if( !CvType::first )
4838     //    icvCreateStandardTypes();
4839
4840     if( !_info || _info->header_size != sizeof(CvTypeInfo) )
4841         CV_Error( CV_StsBadSize, "Invalid type info" );
4842
4843     if( !_info->is_instance || !_info->release ||
4844         !_info->read || !_info->write )
4845         CV_Error( CV_StsNullPtr,
4846         "Some of required function pointers "
4847         "(is_instance, release, read or write) are NULL");
4848
4849     c = _info->type_name[0];
4850     if( !cv_isalpha(c) && c != '_' )
4851         CV_Error( CV_StsBadArg, "Type name should start with a letter or _" );
4852
4853     len = (int)strlen(_info->type_name);
4854
4855     for( i = 0; i < len; i++ )
4856     {
4857         c = _info->type_name[i];
4858         if( !cv_isalnum(c) && c != '-' && c != '_' )
4859             CV_Error( CV_StsBadArg,
4860             "Type name should contain only letters, digits, - and _" );
4861     }
4862
4863     info = (CvTypeInfo*)malloc( sizeof(*info) + len + 1 );
4864
4865     *info = *_info;
4866     info->type_name = (char*)(info + 1);
4867     memcpy( (char*)info->type_name, _info->type_name, len + 1 );
4868
4869     info->flags = 0;
4870     info->next = CvType::first;
4871     info->prev = 0;
4872     if( CvType::first )
4873         CvType::first->prev = info;
4874     else
4875         CvType::last = info;
4876     CvType::first = info;
4877 }
4878
4879
4880 CV_IMPL void
4881 cvUnregisterType( const char* type_name )
4882 {
4883     CvTypeInfo* info;
4884
4885     info = cvFindType( type_name );
4886     if( info )
4887     {
4888         if( info->prev )
4889             info->prev->next = info->next;
4890         else
4891             CvType::first = info->next;
4892
4893         if( info->next )
4894             info->next->prev = info->prev;
4895         else
4896             CvType::last = info->prev;
4897
4898         if( !CvType::first || !CvType::last )
4899             CvType::first = CvType::last = 0;
4900
4901         free( info );
4902     }
4903 }
4904
4905
4906 CV_IMPL CvTypeInfo*
4907 cvFirstType( void )
4908 {
4909     return CvType::first;
4910 }
4911
4912
4913 CV_IMPL CvTypeInfo*
4914 cvFindType( const char* type_name )
4915 {
4916     CvTypeInfo* info = 0;
4917
4918     if (type_name)
4919       for( info = CvType::first; info != 0; info = info->next )
4920         if( strcmp( info->type_name, type_name ) == 0 )
4921       break;
4922
4923     return info;
4924 }
4925
4926
4927 CV_IMPL CvTypeInfo*
4928 cvTypeOf( const void* struct_ptr )
4929 {
4930     CvTypeInfo* info = 0;
4931
4932     if( struct_ptr )
4933     {
4934         for( info = CvType::first; info != 0; info = info->next )
4935             if( info->is_instance( struct_ptr ))
4936                 break;
4937     }
4938
4939     return info;
4940 }
4941
4942
4943 /* universal functions */
4944 CV_IMPL void
4945 cvRelease( void** struct_ptr )
4946 {
4947     CvTypeInfo* info;
4948
4949     if( !struct_ptr )
4950         CV_Error( CV_StsNullPtr, "NULL double pointer" );
4951
4952     if( *struct_ptr )
4953     {
4954         info = cvTypeOf( *struct_ptr );
4955         if( !info )
4956             CV_Error( CV_StsError, "Unknown object type" );
4957         if( !info->release )
4958             CV_Error( CV_StsError, "release function pointer is NULL" );
4959
4960         info->release( struct_ptr );
4961         *struct_ptr = 0;
4962     }
4963 }
4964
4965
4966 void* cvClone( const void* struct_ptr )
4967 {
4968     void* struct_copy = 0;
4969     CvTypeInfo* info;
4970
4971     if( !struct_ptr )
4972         CV_Error( CV_StsNullPtr, "NULL structure pointer" );
4973
4974     info = cvTypeOf( struct_ptr );
4975     if( !info )
4976         CV_Error( CV_StsError, "Unknown object type" );
4977     if( !info->clone )
4978         CV_Error( CV_StsError, "clone function pointer is NULL" );
4979
4980     struct_copy = info->clone( struct_ptr );
4981     return struct_copy;
4982 }
4983
4984
4985 /* reads matrix, image, sequence, graph etc. */
4986 CV_IMPL void*
4987 cvRead( CvFileStorage* fs, CvFileNode* node, CvAttrList* list )
4988 {
4989     void* obj = 0;
4990     CV_CHECK_FILE_STORAGE( fs );
4991
4992     if( !node )
4993         return 0;
4994
4995     if( !CV_NODE_IS_USER(node->tag) || !node->info )
4996         CV_Error( CV_StsError, "The node does not represent a user object (unknown type?)" );
4997
4998     obj = node->info->read( fs, node );
4999     if( list )
5000         *list = cvAttrList(0,0);
5001
5002     return obj;
5003 }
5004
5005
5006 /* writes matrix, image, sequence, graph etc. */
5007 CV_IMPL void
5008 cvWrite( CvFileStorage* fs, const char* name,
5009          const void* ptr, CvAttrList attributes )
5010 {
5011     CvTypeInfo* info;
5012
5013     CV_CHECK_OUTPUT_FILE_STORAGE( fs );
5014
5015     if( !ptr )
5016         CV_Error( CV_StsNullPtr, "Null pointer to the written object" );
5017
5018     info = cvTypeOf( ptr );
5019     if( !info )
5020         CV_Error( CV_StsBadArg, "Unknown object" );
5021
5022     if( !info->write )
5023         CV_Error( CV_StsBadArg, "The object does not have write function" );
5024
5025     info->write( fs, name, ptr, attributes );
5026 }
5027
5028
5029 /* simple API for reading/writing data */
5030 CV_IMPL void
5031 cvSave( const char* filename, const void* struct_ptr,
5032         const char* _name, const char* comment, CvAttrList attributes )
5033 {
5034     CvFileStorage* fs = 0;
5035
5036     if( !struct_ptr )
5037         CV_Error( CV_StsNullPtr, "NULL object pointer" );
5038
5039     fs = cvOpenFileStorage( filename, 0, CV_STORAGE_WRITE );
5040     if( !fs )
5041         CV_Error( CV_StsError, "Could not open the file storage. Check the path and permissions" );
5042
5043     cv::string name = _name ? cv::string(_name) : cv::FileStorage::getDefaultObjectName(filename);
5044
5045     if( comment )
5046         cvWriteComment( fs, comment, 0 );
5047     cvWrite( fs, name.c_str(), struct_ptr, attributes );
5048     cvReleaseFileStorage( &fs );
5049 }
5050
5051 CV_IMPL void*
5052 cvLoad( const char* filename, CvMemStorage* memstorage,
5053         const char* name, const char** _real_name )
5054 {
5055     void* ptr = 0;
5056     const char* real_name = 0;
5057     cv::FileStorage fs(cvOpenFileStorage(filename, memstorage, CV_STORAGE_READ));
5058
5059     CvFileNode* node = 0;
5060
5061     if( !fs.isOpened() )
5062         return 0;
5063
5064     if( name )
5065     {
5066         node = cvGetFileNodeByName( *fs, 0, name );
5067     }
5068     else
5069     {
5070         int i, k;
5071         for( k = 0; k < (*fs)->roots->total; k++ )
5072         {
5073             CvSeq* seq;
5074             CvSeqReader reader;
5075
5076             node = (CvFileNode*)cvGetSeqElem( (*fs)->roots, k );
5077             if( !CV_NODE_IS_MAP( node->tag ))
5078                 return 0;
5079             seq = node->data.seq;
5080             node = 0;
5081
5082             cvStartReadSeq( seq, &reader, 0 );
5083
5084             // find the first element in the map
5085             for( i = 0; i < seq->total; i++ )
5086             {
5087                 if( CV_IS_SET_ELEM( reader.ptr ))
5088                 {
5089                     node = (CvFileNode*)reader.ptr;
5090                     goto stop_search;
5091                 }
5092                 CV_NEXT_SEQ_ELEM( seq->elem_size, reader );
5093             }
5094         }
5095
5096 stop_search:
5097         ;
5098     }
5099
5100     if( !node )
5101         CV_Error( CV_StsObjectNotFound, "Could not find the/an object in file storage" );
5102
5103     real_name = cvGetFileNodeName( node );
5104     ptr = cvRead( *fs, node, 0 );
5105
5106     // sanity check
5107     if( !memstorage && (CV_IS_SEQ( ptr ) || CV_IS_SET( ptr )) )
5108         CV_Error( CV_StsNullPtr,
5109         "NULL memory storage is passed - the loaded dynamic structure can not be stored" );
5110
5111     if( cvGetErrStatus() < 0 )
5112     {
5113         cvRelease( (void**)&ptr );
5114         real_name = 0;
5115     }
5116
5117     if( _real_name)
5118     {
5119     if (real_name)
5120     {
5121         *_real_name = (const char*)cvAlloc(strlen(real_name));
5122             memcpy((void*)*_real_name, real_name, strlen(real_name));
5123     } else {
5124         *_real_name = 0;
5125     }
5126     }
5127
5128     return ptr;
5129 }
5130
5131
5132 ///////////////////////// new C++ interface for CvFileStorage ///////////////////////////
5133
5134 namespace cv
5135 {
5136
5137 static void getElemSize( const string& fmt, size_t& elemSize, size_t& cn )
5138 {
5139     const char* dt = fmt.c_str();
5140     cn = 1;
5141     if( cv_isdigit(dt[0]) )
5142     {
5143         cn = dt[0] - '0';
5144         dt++;
5145     }
5146     char c = dt[0];
5147     elemSize = cn*(c == 'u' || c == 'c' ? sizeof(uchar) : c == 'w' || c == 's' ? sizeof(ushort) :
5148         c == 'i' ? sizeof(int) : c == 'f' ? sizeof(float) : c == 'd' ? sizeof(double) :
5149         c == 'r' ? sizeof(void*) : (size_t)0);
5150 }
5151
5152 FileStorage::FileStorage()
5153 {
5154     state = UNDEFINED;
5155 }
5156
5157 FileStorage::FileStorage(const string& filename, int flags, const string& encoding)
5158 {
5159     state = UNDEFINED;
5160     open( filename, flags, encoding );
5161 }
5162
5163 FileStorage::FileStorage(CvFileStorage* _fs)
5164 {
5165     fs = Ptr<CvFileStorage>(_fs);
5166     state = _fs ? NAME_EXPECTED + INSIDE_MAP : UNDEFINED;
5167 }
5168
5169 FileStorage::~FileStorage()
5170 {
5171     while( structs.size() > 0 )
5172     {
5173         cvEndWriteStruct(fs);
5174         structs.pop_back();
5175     }
5176 }
5177
5178 bool FileStorage::open(const string& filename, int flags, const string& encoding)
5179 {
5180     release();
5181     fs = Ptr<CvFileStorage>(cvOpenFileStorage( filename.c_str(), 0, flags,
5182                                                !encoding.empty() ? encoding.c_str() : 0));
5183     bool ok = isOpened();
5184     state = ok ? NAME_EXPECTED + INSIDE_MAP : UNDEFINED;
5185     return ok;
5186 }
5187
5188 bool FileStorage::isOpened() const
5189 {
5190     return !fs.empty() && fs.obj->is_opened;
5191 }
5192
5193 void FileStorage::release()
5194 {
5195     fs.release();
5196     structs.clear();
5197     state = UNDEFINED;
5198 }
5199
5200 string FileStorage::releaseAndGetString()
5201 {
5202     string buf;
5203     if( fs.obj && fs.obj->outbuf )
5204         icvClose(fs.obj, &buf);
5205     
5206     release();
5207         return buf;
5208 }    
5209     
5210 FileNode FileStorage::root(int streamidx) const
5211 {
5212     return isOpened() ? FileNode(fs, cvGetRootFileNode(fs, streamidx)) : FileNode();
5213 }
5214
5215 FileStorage& operator << (FileStorage& fs, const string& str)
5216 {
5217     enum { NAME_EXPECTED = FileStorage::NAME_EXPECTED,
5218         VALUE_EXPECTED = FileStorage::VALUE_EXPECTED,
5219         INSIDE_MAP = FileStorage::INSIDE_MAP };
5220     const char* _str = str.c_str();
5221     if( !fs.isOpened() || !_str )
5222         return fs;
5223     if( *_str == '}' || *_str == ']' )
5224     {
5225         if( fs.structs.empty() )
5226             CV_Error_( CV_StsError, ("Extra closing '%c'", *_str) );
5227         if( (*_str == ']' ? '[' : '{') != fs.structs.back() )
5228             CV_Error_( CV_StsError,
5229             ("The closing '%c' does not match the opening '%c'", *_str, fs.structs.back()));
5230         fs.structs.pop_back();
5231         fs.state = fs.structs.empty() || fs.structs.back() == '{' ?
5232             INSIDE_MAP + NAME_EXPECTED : VALUE_EXPECTED;
5233         cvEndWriteStruct( *fs );
5234         fs.elname = string();
5235     }
5236     else if( fs.state == NAME_EXPECTED + INSIDE_MAP )
5237     {
5238         if( !cv_isalpha(*_str) )
5239             CV_Error_( CV_StsError, ("Incorrect element name %s", _str) );
5240         fs.elname = str;
5241         fs.state = VALUE_EXPECTED + INSIDE_MAP;
5242     }
5243     else if( (fs.state & 3) == VALUE_EXPECTED )
5244     {
5245         if( *_str == '{' || *_str == '[' )
5246         {
5247             fs.structs.push_back(*_str);
5248             int flags = *_str++ == '{' ? CV_NODE_MAP : CV_NODE_SEQ;
5249             fs.state = flags == CV_NODE_MAP ? INSIDE_MAP +
5250                 NAME_EXPECTED : VALUE_EXPECTED;
5251             if( *_str == ':' )
5252             {
5253                 flags |= CV_NODE_FLOW;
5254                 _str++;
5255             }
5256             cvStartWriteStruct( *fs, fs.elname.size() > 0 ? fs.elname.c_str() : 0,
5257                 flags, *_str ? _str : 0 );
5258             fs.elname = string();
5259         }
5260         else
5261         {
5262             write( fs, fs.elname, (_str[0] == '\\' && (_str[1] == '{' || _str[1] == '}' ||
5263                 _str[1] == '[' || _str[1] == ']')) ? string(_str+1) : str );
5264             if( fs.state == INSIDE_MAP + VALUE_EXPECTED )
5265                 fs.state = INSIDE_MAP + NAME_EXPECTED;
5266         }
5267     }
5268     else
5269         CV_Error( CV_StsError, "Invalid fs.state" );
5270     return fs;
5271 }
5272
5273
5274 void FileStorage::writeRaw( const string& fmt, const uchar* vec, size_t len )
5275 {
5276     if( !isOpened() )
5277         return;
5278     size_t elemSize, cn;
5279     getElemSize( fmt, elemSize, cn );
5280     CV_Assert( len % elemSize == 0 );
5281     cvWriteRawData( fs, vec, (int)(len/elemSize), fmt.c_str());
5282 }
5283
5284
5285 void FileStorage::writeObj( const string& name, const void* obj )
5286 {
5287     if( !isOpened() )
5288         return;
5289     cvWrite( fs, name.size() > 0 ? name.c_str() : 0, obj );
5290 }
5291
5292
5293 FileNode FileStorage::operator[](const string& nodename) const
5294 {
5295     return FileNode(fs, cvGetFileNodeByName(fs, 0, nodename.c_str()));
5296 }
5297
5298 FileNode FileStorage::operator[](const char* nodename) const
5299 {
5300     return FileNode(fs, cvGetFileNodeByName(fs, 0, nodename));
5301 }
5302
5303 FileNode FileNode::operator[](const string& nodename) const
5304 {
5305     return FileNode(fs, cvGetFileNodeByName(fs, node, nodename.c_str()));
5306 }
5307
5308 FileNode FileNode::operator[](const char* nodename) const
5309 {
5310     return FileNode(fs, cvGetFileNodeByName(fs, node, nodename));
5311 }
5312
5313 FileNode FileNode::operator[](int i) const
5314 {
5315     return isSeq() ? FileNode(fs, (CvFileNode*)cvGetSeqElem(node->data.seq, i)) :
5316         i == 0 ? *this : FileNode();
5317 }
5318
5319 string FileNode::name() const
5320 {
5321     const char* str;
5322     return !node || (str = cvGetFileNodeName(node)) == 0 ? string() : string(str);
5323 }
5324
5325 void* FileNode::readObj() const
5326 {
5327     if( !fs || !node )
5328         return 0;
5329     return cvRead( (CvFileStorage*)fs, (CvFileNode*)node );
5330 }
5331
5332 FileNodeIterator::FileNodeIterator()
5333 {
5334     fs = 0;
5335     container = 0;
5336     reader.ptr = 0;
5337     remaining = 0;
5338 }
5339
5340 FileNodeIterator::FileNodeIterator(const CvFileStorage* _fs,
5341                                    const CvFileNode* _node, size_t _ofs)
5342 {
5343     if( _fs && _node && CV_NODE_TYPE(_node->tag) != CV_NODE_NONE )
5344     {
5345         int node_type = _node->tag & FileNode::TYPE_MASK;
5346         fs = _fs;
5347         container = _node;
5348         if( !(_node->tag & FileNode::USER) && (node_type == FileNode::SEQ || node_type == FileNode::MAP) )
5349         {
5350             cvStartReadSeq( _node->data.seq, &reader );
5351             remaining = FileNode(_fs, _node).size();
5352         }
5353         else
5354         {
5355             reader.ptr = (schar*)_node;
5356             reader.seq = 0;
5357             remaining = 1;
5358         }
5359         (*this) += (int)_ofs;
5360     }
5361     else
5362     {
5363         fs = 0;
5364         container = 0;
5365         reader.ptr = 0;
5366         remaining = 0;
5367     }
5368 }
5369
5370 FileNodeIterator::FileNodeIterator(const FileNodeIterator& it)
5371 {
5372     fs = it.fs;
5373     container = it.container;
5374     reader = it.reader;
5375     remaining = it.remaining;
5376 }
5377
5378 FileNodeIterator& FileNodeIterator::operator ++()
5379 {
5380     if( remaining > 0 )
5381     {
5382         if( reader.seq )
5383             CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
5384         remaining--;
5385     }
5386     return *this;
5387 }
5388
5389 FileNodeIterator FileNodeIterator::operator ++(int)
5390 {
5391     FileNodeIterator it = *this;
5392     ++(*this);
5393     return it;
5394 }
5395
5396 FileNodeIterator& FileNodeIterator::operator --()
5397 {
5398     if( remaining < FileNode(fs, container).size() )
5399     {
5400         if( reader.seq )
5401             CV_PREV_SEQ_ELEM( reader.seq->elem_size, reader );
5402         remaining++;
5403     }
5404     return *this;
5405 }
5406
5407 FileNodeIterator FileNodeIterator::operator --(int)
5408 {
5409     FileNodeIterator it = *this;
5410     --(*this);
5411     return it;
5412 }
5413
5414 FileNodeIterator& FileNodeIterator::operator += (int ofs)
5415 {
5416     if( ofs == 0 )
5417         return *this;
5418     if( ofs > 0 )
5419         ofs = std::min(ofs, (int)remaining);
5420     else
5421     {
5422         size_t count = FileNode(fs, container).size();
5423         ofs = (int)(remaining - std::min(remaining - ofs, count));
5424     }
5425     remaining -= ofs;
5426     if( reader.seq )
5427         cvSetSeqReaderPos( &reader, ofs, 1 );
5428     return *this;
5429 }
5430
5431 FileNodeIterator& FileNodeIterator::operator -= (int ofs)
5432 {
5433     return operator += (-ofs);
5434 }
5435
5436
5437 FileNodeIterator& FileNodeIterator::readRaw( const string& fmt, uchar* vec, size_t maxCount )
5438 {
5439     if( fs && container && remaining > 0 )
5440     {
5441         size_t elem_size, cn;
5442         getElemSize( fmt, elem_size, cn );
5443         CV_Assert( elem_size > 0 );
5444         size_t count = std::min(remaining, maxCount);
5445
5446         if( reader.seq )
5447         {
5448             cvReadRawDataSlice( fs, &reader, (int)count, vec, fmt.c_str() );
5449             remaining -= count*cn;
5450         }
5451         else
5452         {
5453             cvReadRawData( fs, container, vec, fmt.c_str() );
5454             remaining = 0;
5455         }
5456     }
5457     return *this;
5458 }
5459
5460
5461 void write( FileStorage& fs, const string& name, int value )
5462 { cvWriteInt( *fs, name.size() ? name.c_str() : 0, value ); }
5463
5464 void write( FileStorage& fs, const string& name, float value )
5465 { cvWriteReal( *fs, name.size() ? name.c_str() : 0, value ); }
5466
5467 void write( FileStorage& fs, const string& name, double value )
5468 { cvWriteReal( *fs, name.size() ? name.c_str() : 0, value ); }
5469
5470 void write( FileStorage& fs, const string& name, const string& value )
5471 { cvWriteString( *fs, name.size() ? name.c_str() : 0, value.c_str() ); }
5472
5473 void writeScalar(FileStorage& fs, int value )
5474 { cvWriteInt( *fs, 0, value ); }
5475
5476 void writeScalar(FileStorage& fs, float value )
5477 { cvWriteReal( *fs, 0, value ); }
5478
5479 void writeScalar(FileStorage& fs, double value )
5480 { cvWriteReal( *fs, 0, value ); }
5481
5482 void writeScalar(FileStorage& fs, const string& value )
5483 { cvWriteString( *fs, 0, value.c_str() ); }
5484
5485
5486 void write( FileStorage& fs, const string& name, const Mat& value )
5487 {
5488     if( value.dims <= 2 )
5489     {
5490         CvMat mat = value;
5491         cvWrite( *fs, name.size() ? name.c_str() : 0, &mat );
5492     }
5493     else
5494     {
5495         CvMatND mat = value;
5496         cvWrite( *fs, name.size() ? name.c_str() : 0, &mat );
5497     }
5498 }
5499
5500 // TODO: the 4 functions below need to be implemented more efficiently
5501 void write( FileStorage& fs, const string& name, const SparseMat& value )
5502 {
5503     Ptr<CvSparseMat> mat = (CvSparseMat*)value;
5504     cvWrite( *fs, name.size() ? name.c_str() : 0, mat );
5505 }
5506
5507
5508 WriteStructContext::WriteStructContext(FileStorage& _fs, const string& name,
5509                    int flags, const string& typeName) : fs(&_fs)
5510 {
5511     cvStartWriteStruct(**fs, !name.empty() ? name.c_str() : 0, flags,
5512                        !typeName.empty() ? typeName.c_str() : 0);
5513 }
5514
5515 WriteStructContext::~WriteStructContext() { cvEndWriteStruct(**fs); }
5516
5517
5518 void read( const FileNode& node, Mat& mat, const Mat& default_mat )
5519 {
5520     if( node.empty() )
5521     {
5522         default_mat.copyTo(mat);
5523         return;
5524     }
5525     void* obj = cvRead((CvFileStorage*)node.fs, (CvFileNode*)*node);
5526     if(CV_IS_MAT_HDR_Z(obj))
5527     {
5528         Mat((const CvMat*)obj).copyTo(mat);
5529         cvReleaseMat((CvMat**)&obj);
5530     }
5531     else if(CV_IS_MATND_HDR(obj))
5532     {
5533         Mat((const CvMatND*)obj).copyTo(mat);
5534         cvReleaseMatND((CvMatND**)&obj);
5535     }
5536     else
5537     {
5538         cvRelease(&obj);
5539         CV_Error(CV_StsBadArg, "Unknown array type");
5540     }
5541 }
5542
5543 void read( const FileNode& node, SparseMat& mat, const SparseMat& default_mat )
5544 {
5545     if( node.empty() )
5546     {
5547         default_mat.copyTo(mat);
5548         return;
5549     }
5550     Ptr<CvSparseMat> m = (CvSparseMat*)cvRead((CvFileStorage*)node.fs, (CvFileNode*)*node);
5551     CV_Assert(CV_IS_SPARSE_MAT(m));
5552     SparseMat(m).copyTo(mat);
5553 }
5554
5555 }
5556
5557 /* End of file. */