/src/opencv/modules/core/src/persistence.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // This file is part of OpenCV project. |
2 | | // It is subject to the license terms in the LICENSE file found in the top-level directory |
3 | | // of this distribution and at http://opencv.org/license.html |
4 | | |
5 | | #include "precomp.hpp" |
6 | | #include "persistence.hpp" |
7 | | #include "persistence_impl.hpp" |
8 | | #include "persistence_base64_encoding.hpp" |
9 | | #include <unordered_map> |
10 | | #include <iterator> |
11 | | |
12 | | #include <opencv2/core/utils/logger.hpp> |
13 | | |
14 | | namespace cv |
15 | | { |
16 | | |
17 | | namespace fs |
18 | | { |
19 | | |
20 | | int strcasecmp(const char* s1, const char* s2) |
21 | 0 | { |
22 | 0 | const char* dummy=""; |
23 | 0 | if(!s1) s1=dummy; |
24 | 0 | if(!s2) s2=dummy; |
25 | |
|
26 | 0 | size_t len1 = strlen(s1); |
27 | 0 | size_t len2 = strlen(s2); |
28 | 0 | size_t i, len = std::min(len1, len2); |
29 | 0 | for( i = 0; i < len; i++ ) |
30 | 0 | { |
31 | 0 | int d = tolower((int)s1[i]) - tolower((int)s2[i]); |
32 | 0 | if( d != 0 ) |
33 | 0 | return d; |
34 | 0 | } |
35 | 0 | return len1 < len2 ? -1 : len1 > len2 ? 1 : 0; |
36 | 0 | } |
37 | | |
38 | | char* itoa( int _val, char* buffer, int /*radix*/ ) |
39 | 0 | { |
40 | 0 | const int radix = 10; |
41 | 0 | char* ptr=buffer + 23 /* enough even for 64-bit integers */; |
42 | 0 | unsigned val = abs(_val); |
43 | |
|
44 | 0 | *ptr = '\0'; |
45 | 0 | do |
46 | 0 | { |
47 | 0 | unsigned r = val / radix; |
48 | 0 | *--ptr = (char)(val - (r*radix) + '0'); |
49 | 0 | val = r; |
50 | 0 | } |
51 | 0 | while( val != 0 ); |
52 | |
|
53 | 0 | if( _val < 0 ) |
54 | 0 | *--ptr = '-'; |
55 | |
|
56 | 0 | return ptr; |
57 | 0 | } |
58 | | |
59 | | char* doubleToString( char* buf, size_t bufSize, double value, bool explicitZero ) |
60 | 0 | { |
61 | 0 | Cv64suf val; |
62 | 0 | unsigned ieee754_hi; |
63 | |
|
64 | 0 | val.f = value; |
65 | 0 | ieee754_hi = (unsigned)(val.u >> 32); |
66 | |
|
67 | 0 | if( (ieee754_hi & 0x7ff00000) != 0x7ff00000 ) |
68 | 0 | { |
69 | 0 | int ivalue = cvRound(value); |
70 | 0 | if( ivalue == value ) |
71 | 0 | { |
72 | 0 | if( explicitZero ) |
73 | 0 | snprintf( buf, bufSize, "%d.0", ivalue ); |
74 | 0 | else |
75 | 0 | snprintf( buf, bufSize, "%d.", ivalue ); |
76 | 0 | } |
77 | 0 | else |
78 | 0 | { |
79 | 0 | static const char* fmt = "%.16e"; |
80 | 0 | char* ptr = buf; |
81 | 0 | snprintf( buf, bufSize, fmt, value ); |
82 | 0 | if( *ptr == '+' || *ptr == '-' ) |
83 | 0 | ptr++; |
84 | 0 | for( ; cv_isdigit(*ptr); ptr++ ) |
85 | 0 | ; |
86 | 0 | if( *ptr == ',' ) |
87 | 0 | *ptr = '.'; |
88 | 0 | } |
89 | 0 | } |
90 | 0 | else |
91 | 0 | { |
92 | 0 | unsigned ieee754_lo = (unsigned)val.u; |
93 | 0 | if( (ieee754_hi & 0x7fffffff) + (ieee754_lo != 0) > 0x7ff00000 ) |
94 | 0 | strcpy( buf, ".Nan" ); |
95 | 0 | else |
96 | 0 | strcpy( buf, (int)ieee754_hi < 0 ? "-.Inf" : ".Inf" ); |
97 | 0 | } |
98 | |
|
99 | 0 | return buf; |
100 | 0 | } |
101 | | |
102 | | char* floatToString( char* buf, size_t bufSize, float value, bool halfprecision, bool explicitZero ) |
103 | 0 | { |
104 | 0 | Cv32suf val; |
105 | 0 | unsigned ieee754; |
106 | 0 | val.f = value; |
107 | 0 | ieee754 = val.u; |
108 | |
|
109 | 0 | if( (ieee754 & 0x7f800000) != 0x7f800000 ) |
110 | 0 | { |
111 | 0 | int ivalue = cvRound(value); |
112 | 0 | if( ivalue == value ) |
113 | 0 | { |
114 | 0 | if( explicitZero ) |
115 | 0 | snprintf( buf, bufSize, "%d.0", ivalue ); |
116 | 0 | else |
117 | 0 | snprintf( buf, bufSize, "%d.", ivalue ); |
118 | 0 | } |
119 | 0 | else |
120 | 0 | { |
121 | 0 | char* ptr = buf; |
122 | 0 | if (halfprecision) |
123 | 0 | snprintf(buf, bufSize, "%.4e", value); |
124 | 0 | else |
125 | 0 | snprintf(buf, bufSize, "%.8e", value); |
126 | 0 | if( *ptr == '+' || *ptr == '-' ) |
127 | 0 | ptr++; |
128 | 0 | for( ; cv_isdigit(*ptr); ptr++ ) |
129 | 0 | ; |
130 | 0 | if( *ptr == ',' ) |
131 | 0 | *ptr = '.'; |
132 | 0 | } |
133 | 0 | } |
134 | 0 | else |
135 | 0 | { |
136 | 0 | if( (ieee754 & 0x7fffffff) != 0x7f800000 ) |
137 | 0 | strcpy( buf, ".Nan" ); |
138 | 0 | else |
139 | 0 | strcpy( buf, (int)ieee754 < 0 ? "-.Inf" : ".Inf" ); |
140 | 0 | } |
141 | |
|
142 | 0 | return buf; |
143 | 0 | } |
144 | | |
145 | | static const char symbols[9] = "ucwsifdh"; |
146 | | |
147 | | static char typeSymbol(int depth) |
148 | 0 | { |
149 | 0 | CV_StaticAssert(CV_64F == 6, ""); |
150 | 0 | CV_CheckDepth(depth, depth >=0 && depth <= CV_16F, ""); |
151 | 0 | return symbols[depth]; |
152 | 0 | } |
153 | | |
154 | | static int symbolToType(char c) |
155 | 0 | { |
156 | 0 | if (c == 'r') |
157 | 0 | return CV_SEQ_ELTYPE_PTR; |
158 | 0 | const char* pos = strchr( symbols, c ); |
159 | 0 | if( !pos ) |
160 | 0 | CV_Error( cv::Error::StsBadArg, "Invalid data type specification" ); |
161 | 0 | return static_cast<int>(pos - symbols); |
162 | 0 | } |
163 | | |
164 | | char* encodeFormat(int elem_type, char* dt, size_t dt_len) |
165 | 0 | { |
166 | 0 | int cn = (elem_type == CV_SEQ_ELTYPE_PTR/*CV_USRTYPE1*/) ? 1 : CV_MAT_CN(elem_type); |
167 | 0 | char symbol = (elem_type == CV_SEQ_ELTYPE_PTR/*CV_USRTYPE1*/) ? 'r' : typeSymbol(CV_MAT_DEPTH(elem_type)); |
168 | 0 | snprintf(dt, dt_len, "%d%c", cn, symbol); |
169 | 0 | return dt + (cn == 1 ? 1 : 0); |
170 | 0 | } |
171 | | |
172 | | // Deprecated due to size of dt buffer being unknowable. |
173 | | char* encodeFormat(int elem_type, char* dt) |
174 | 0 | { |
175 | 0 | constexpr size_t max = 20+1+1; // UINT64_MAX + one char + nul termination. |
176 | 0 | return encodeFormat(elem_type, dt, max); |
177 | 0 | } |
178 | | |
179 | | int decodeFormat( const char* dt, int* fmt_pairs, int max_len ) |
180 | 0 | { |
181 | 0 | int fmt_pair_count = 0; |
182 | 0 | int i = 0, k = 0, len = dt ? (int)strlen(dt) : 0; |
183 | |
|
184 | 0 | if( !dt || !len ) |
185 | 0 | return 0; |
186 | | |
187 | 0 | CV_Assert( fmt_pairs != 0 && max_len > 0 ); |
188 | 0 | fmt_pairs[0] = 0; |
189 | 0 | max_len *= 2; |
190 | |
|
191 | 0 | for( ; k < len; k++ ) |
192 | 0 | { |
193 | 0 | char c = dt[k]; |
194 | |
|
195 | 0 | if( cv_isdigit(c) ) |
196 | 0 | { |
197 | 0 | int count = c - '0'; |
198 | 0 | if( cv_isdigit(dt[k+1]) ) |
199 | 0 | { |
200 | 0 | char* endptr = 0; |
201 | 0 | count = (int)strtol( dt+k, &endptr, 10 ); |
202 | 0 | k = (int)(endptr - dt) - 1; |
203 | 0 | } |
204 | |
|
205 | 0 | if( count <= 0 ) |
206 | 0 | CV_Error( cv::Error::StsBadArg, "Invalid data type specification" ); |
207 | | |
208 | 0 | fmt_pairs[i] = count; |
209 | 0 | } |
210 | 0 | else |
211 | 0 | { |
212 | 0 | int depth = symbolToType(c); |
213 | 0 | if( fmt_pairs[i] == 0 ) |
214 | 0 | fmt_pairs[i] = 1; |
215 | 0 | fmt_pairs[i+1] = depth; |
216 | 0 | if( i > 0 && fmt_pairs[i+1] == fmt_pairs[i-1] ) |
217 | 0 | fmt_pairs[i-2] += fmt_pairs[i]; |
218 | 0 | else |
219 | 0 | { |
220 | 0 | i += 2; |
221 | 0 | if( i >= max_len ) |
222 | 0 | CV_Error( cv::Error::StsBadArg, "Too long data type specification" ); |
223 | 0 | } |
224 | 0 | fmt_pairs[i] = 0; |
225 | 0 | } |
226 | 0 | } |
227 | | |
228 | 0 | fmt_pair_count = i/2; |
229 | 0 | return fmt_pair_count; |
230 | 0 | } |
231 | | |
232 | | int calcElemSize( const char* dt, int initial_size ) |
233 | 0 | { |
234 | 0 | int size = 0; |
235 | 0 | int fmt_pairs[CV_FS_MAX_FMT_PAIRS], i, fmt_pair_count; |
236 | 0 | int comp_size; |
237 | |
|
238 | 0 | fmt_pair_count = decodeFormat( dt, fmt_pairs, CV_FS_MAX_FMT_PAIRS ); |
239 | 0 | fmt_pair_count *= 2; |
240 | 0 | for( i = 0, size = initial_size; i < fmt_pair_count; i += 2 ) |
241 | 0 | { |
242 | 0 | comp_size = CV_ELEM_SIZE(fmt_pairs[i+1]); |
243 | 0 | size = cvAlign( size, comp_size ); |
244 | 0 | size += comp_size * fmt_pairs[i]; |
245 | 0 | } |
246 | 0 | if( initial_size == 0 ) |
247 | 0 | { |
248 | 0 | comp_size = CV_ELEM_SIZE(fmt_pairs[1]); |
249 | 0 | size = cvAlign( size, comp_size ); |
250 | 0 | } |
251 | 0 | return size; |
252 | 0 | } |
253 | | |
254 | | |
255 | | int calcStructSize( const char* dt, int initial_size ) |
256 | 0 | { |
257 | 0 | int size = calcElemSize( dt, initial_size ); |
258 | 0 | size_t elem_max_size = 0; |
259 | 0 | for ( const char * type = dt; *type != '\0'; type++ ) |
260 | 0 | { |
261 | 0 | char v = *type; |
262 | 0 | if (v >= '0' && v <= '9') |
263 | 0 | continue; // skip vector size |
264 | 0 | switch (v) |
265 | 0 | { |
266 | 0 | case 'u': { elem_max_size = std::max( elem_max_size, sizeof(uchar ) ); break; } |
267 | 0 | case 'c': { elem_max_size = std::max( elem_max_size, sizeof(schar ) ); break; } |
268 | 0 | case 'w': { elem_max_size = std::max( elem_max_size, sizeof(ushort) ); break; } |
269 | 0 | case 's': { elem_max_size = std::max( elem_max_size, sizeof(short ) ); break; } |
270 | 0 | case 'i': { elem_max_size = std::max( elem_max_size, sizeof(int ) ); break; } |
271 | 0 | case 'f': { elem_max_size = std::max( elem_max_size, sizeof(float ) ); break; } |
272 | 0 | case 'd': { elem_max_size = std::max( elem_max_size, sizeof(double) ); break; } |
273 | 0 | case 'h': { elem_max_size = std::max(elem_max_size, sizeof(float16_t)); break; } |
274 | 0 | default: |
275 | 0 | CV_Error_(Error::StsNotImplemented, ("Unknown type identifier: '%c' in '%s'", (char)(*type), dt)); |
276 | 0 | } |
277 | 0 | } |
278 | 0 | size = cvAlign( size, static_cast<int>(elem_max_size) ); |
279 | 0 | return size; |
280 | 0 | } |
281 | | |
282 | | int decodeSimpleFormat( const char* dt ) |
283 | 0 | { |
284 | 0 | int elem_type = -1; |
285 | 0 | int fmt_pairs[CV_FS_MAX_FMT_PAIRS], fmt_pair_count; |
286 | |
|
287 | 0 | fmt_pair_count = decodeFormat( dt, fmt_pairs, CV_FS_MAX_FMT_PAIRS ); |
288 | 0 | if( fmt_pair_count != 1 || fmt_pairs[0] >= CV_CN_MAX) |
289 | 0 | CV_Error( cv::Error::StsError, "Too complex format for the matrix" ); |
290 | | |
291 | 0 | elem_type = CV_MAKETYPE( fmt_pairs[1], fmt_pairs[0] ); |
292 | |
|
293 | 0 | return elem_type; |
294 | 0 | } |
295 | | |
296 | | } |
297 | | |
298 | | #if defined __i386__ || defined(_M_IX86) || defined __x86_64__ || defined(_M_X64) || \ |
299 | | (defined (__LITTLE_ENDIAN__) && __LITTLE_ENDIAN__) |
300 | | #define CV_LITTLE_ENDIAN_MEM_ACCESS 1 |
301 | | #else |
302 | | #define CV_LITTLE_ENDIAN_MEM_ACCESS 0 |
303 | | #endif |
304 | | |
305 | | static inline int readInt(const uchar* p) |
306 | 0 | { |
307 | | // On little endian CPUs, both branches produce the same result. On big endian, only the else branch does. |
308 | 0 | #if CV_LITTLE_ENDIAN_MEM_ACCESS |
309 | 0 | int val; |
310 | 0 | memcpy(&val, p, sizeof(val)); |
311 | 0 | return val; |
312 | | #else |
313 | | int val = (int)(p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24)); |
314 | | return val; |
315 | | #endif |
316 | 0 | } |
317 | | |
318 | | static inline double readReal(const uchar* p) |
319 | 0 | { |
320 | | // On little endian CPUs, both branches produce the same result. On big endian, only the else branch does. |
321 | 0 | #if CV_LITTLE_ENDIAN_MEM_ACCESS |
322 | 0 | double val; |
323 | 0 | memcpy(&val, p, sizeof(val)); |
324 | 0 | return val; |
325 | | #else |
326 | | unsigned val0 = (unsigned)(p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24)); |
327 | | unsigned val1 = (unsigned)(p[4] | (p[5] << 8) | (p[6] << 16) | (p[7] << 24)); |
328 | | Cv64suf val; |
329 | | val.u = val0 | ((uint64)val1 << 32); |
330 | | return val.f; |
331 | | #endif |
332 | 0 | } |
333 | | |
334 | | static inline void writeInt(uchar* p, int ival) |
335 | 0 | { |
336 | | // On little endian CPUs, both branches produce the same result. On big endian, only the else branch does. |
337 | 0 | #if CV_LITTLE_ENDIAN_MEM_ACCESS |
338 | 0 | memcpy(p, &ival, sizeof(ival)); |
339 | | #else |
340 | | p[0] = (uchar)ival; |
341 | | p[1] = (uchar)(ival >> 8); |
342 | | p[2] = (uchar)(ival >> 16); |
343 | | p[3] = (uchar)(ival >> 24); |
344 | | #endif |
345 | 0 | } |
346 | | |
347 | | static inline void writeReal(uchar* p, double fval) |
348 | 0 | { |
349 | | // On little endian CPUs, both branches produce the same result. On big endian, only the else branch does. |
350 | 0 | #if CV_LITTLE_ENDIAN_MEM_ACCESS |
351 | 0 | memcpy(p, &fval, sizeof(fval)); |
352 | | #else |
353 | | Cv64suf v; |
354 | | v.f = fval; |
355 | | p[0] = (uchar)v.u; |
356 | | p[1] = (uchar)(v.u >> 8); |
357 | | p[2] = (uchar)(v.u >> 16); |
358 | | p[3] = (uchar)(v.u >> 24); |
359 | | p[4] = (uchar)(v.u >> 32); |
360 | | p[5] = (uchar)(v.u >> 40); |
361 | | p[6] = (uchar)(v.u >> 48); |
362 | | p[7] = (uchar)(v.u >> 56); |
363 | | #endif |
364 | 0 | } |
365 | | |
366 | | |
367 | | |
368 | 3.40k | void FileStorage::Impl::init() { |
369 | 3.40k | flags = 0; |
370 | 3.40k | buffer.clear(); |
371 | 3.40k | bufofs = 0; |
372 | 3.40k | state = UNDEFINED; |
373 | 3.40k | is_using_base64 = false; |
374 | 3.40k | state_of_writing_base64 = FileStorage_API::Base64State::Uncertain; |
375 | 3.40k | is_write_struct_delayed = false; |
376 | 3.40k | delayed_struct_key = nullptr; |
377 | 3.40k | delayed_struct_flags = 0; |
378 | 3.40k | delayed_type_name = nullptr; |
379 | 3.40k | base64_writer = nullptr; |
380 | 3.40k | is_opened = false; |
381 | 3.40k | dummy_eof = false; |
382 | 3.40k | write_mode = false; |
383 | 3.40k | mem_mode = false; |
384 | 3.40k | space = 0; |
385 | 3.40k | wrap_margin = 71; |
386 | 3.40k | fmt = 0; |
387 | 3.40k | file = 0; |
388 | 3.40k | gzfile = 0; |
389 | 3.40k | empty_stream = true; |
390 | | |
391 | 3.40k | strbufv.clear(); |
392 | 3.40k | strbuf = 0; |
393 | 3.40k | strbufsize = strbufpos = 0; |
394 | 3.40k | roots.clear(); |
395 | | |
396 | 3.40k | fs_data.clear(); |
397 | 3.40k | fs_data_ptrs.clear(); |
398 | 3.40k | fs_data_blksz.clear(); |
399 | 3.40k | freeSpaceOfs = 0; |
400 | | |
401 | 3.40k | str_hash.clear(); |
402 | 3.40k | str_hash_data.clear(); |
403 | 3.40k | str_hash_data.resize(1); |
404 | 3.40k | str_hash_data[0] = '\0'; |
405 | | |
406 | 3.40k | filename.clear(); |
407 | 3.40k | lineno = 0; |
408 | 3.40k | } |
409 | | |
410 | 995 | FileStorage::Impl::Impl(FileStorage *_fs) { |
411 | 995 | fs_ext = _fs; |
412 | 995 | init(); |
413 | 995 | } |
414 | | |
415 | 995 | FileStorage::Impl::~Impl() { |
416 | 995 | release(); |
417 | 995 | } |
418 | | |
419 | 2.40k | void FileStorage::Impl::release(String *out) { |
420 | 2.40k | if (is_opened) { |
421 | 0 | if (out) |
422 | 0 | out->clear(); |
423 | 0 | if (write_mode) { |
424 | 0 | while (write_stack.size() > 1) { |
425 | 0 | endWriteStruct(); |
426 | 0 | } |
427 | 0 | flush(); |
428 | 0 | if (fmt == FileStorage::FORMAT_XML) |
429 | 0 | puts("</opencv_storage>\n"); |
430 | 0 | else if (fmt == FileStorage::FORMAT_JSON) |
431 | 0 | puts("}\n"); |
432 | 0 | } |
433 | 0 | if (mem_mode && out) { |
434 | 0 | *out = cv::String(outbuf.begin(), outbuf.end()); |
435 | 0 | } |
436 | 0 | } |
437 | 2.40k | closeFile(); |
438 | 2.40k | init(); |
439 | 2.40k | } |
440 | | |
441 | 994 | void FileStorage::Impl::analyze_file_name(const std::string &file_name, std::vector<std::string> ¶ms) { |
442 | 994 | params.clear(); |
443 | 994 | static const char not_file_name = '\n'; |
444 | 994 | static const char parameter_begin = '?'; |
445 | 994 | static const char parameter_separator = '&'; |
446 | | |
447 | 994 | if (file_name.find(not_file_name, (size_t) 0) != std::string::npos) |
448 | 54 | return; |
449 | | |
450 | 940 | size_t beg = file_name.find_last_of(parameter_begin); |
451 | 940 | params.push_back(file_name.substr((size_t) 0, beg)); |
452 | | |
453 | 940 | if (beg != std::string::npos) { |
454 | 553 | size_t end = file_name.size(); |
455 | 553 | beg++; |
456 | 553 | for (size_t param_beg = beg, param_end = beg; |
457 | 7.78M | param_end < end; |
458 | 7.78M | param_beg = param_end + 1) { |
459 | 7.78M | param_end = file_name.find_first_of(parameter_separator, param_beg); |
460 | 7.78M | if ((param_end == std::string::npos || param_end != param_beg) && param_beg + 1 < end) { |
461 | 7.78M | params.push_back(file_name.substr(param_beg, param_end - param_beg)); |
462 | 7.78M | } |
463 | 7.78M | } |
464 | 553 | } |
465 | 940 | } |
466 | | |
467 | 994 | bool FileStorage::Impl::open(const char *filename_or_buf, int _flags, const char *encoding) { |
468 | 994 | bool ok = true; |
469 | 994 | release(); |
470 | | |
471 | 994 | bool append = (_flags & 3) == FileStorage::APPEND; |
472 | 994 | mem_mode = (_flags & FileStorage::MEMORY) != 0; |
473 | | |
474 | 994 | write_mode = (_flags & 3) != 0; |
475 | 994 | bool write_base64 = (write_mode || append) && (_flags & FileStorage::BASE64) != 0; |
476 | | |
477 | 994 | bool isGZ = false; |
478 | 994 | size_t fnamelen = 0; |
479 | | |
480 | 994 | std::vector<std::string> params; |
481 | | //if ( !mem_mode ) |
482 | 994 | { |
483 | 994 | analyze_file_name(filename_or_buf, params); |
484 | 994 | if (!params.empty()) |
485 | 940 | filename = params[0]; |
486 | | |
487 | 994 | if (!write_base64 && params.size() >= 2 && |
488 | 994 | std::find(params.begin() + 1, params.end(), std::string("base64")) != params.end()) |
489 | 4 | write_base64 = (write_mode || append); |
490 | 994 | } |
491 | | |
492 | 994 | if (filename.size() == 0 && !mem_mode && !write_mode) |
493 | 392 | CV_Error(cv::Error::StsNullPtr, "NULL or empty filename"); |
494 | | |
495 | 602 | if (mem_mode && append) |
496 | 0 | CV_Error(cv::Error::StsBadFlag, "FileStorage::APPEND and FileStorage::MEMORY are not currently compatible"); |
497 | | |
498 | 602 | flags = _flags; |
499 | | |
500 | 602 | if (!mem_mode) { |
501 | 602 | char *dot_pos = strrchr((char *) filename.c_str(), '.'); |
502 | 602 | char compression = '\0'; |
503 | | |
504 | 602 | if (dot_pos && dot_pos[1] == 'g' && dot_pos[2] == 'z' && |
505 | 602 | (dot_pos[3] == '\0' || (cv_isdigit(dot_pos[3]) && dot_pos[4] == '\0'))) { |
506 | 28 | if (append) { |
507 | 0 | CV_Error(cv::Error::StsNotImplemented, "Appending data to compressed file is not implemented"); |
508 | 0 | } |
509 | 28 | isGZ = true; |
510 | 28 | compression = dot_pos[3]; |
511 | 28 | if (compression) |
512 | 26 | dot_pos[3] = '\0', fnamelen--; |
513 | 28 | } |
514 | | |
515 | 602 | if (!isGZ) { |
516 | 574 | file = fopen(filename.c_str(), !write_mode ? "rt" : !append ? "wt" : "a+t"); |
517 | 574 | if (!file) |
518 | 548 | { |
519 | 548 | CV_LOG_ERROR(NULL, "Can't open file: '" << filename << "' in " << (!write_mode ? "read" : !append ? "write" : "append") << " mode"); |
520 | 548 | return false; |
521 | 548 | } |
522 | 574 | } else { |
523 | 28 | #if USE_ZLIB |
524 | 28 | char mode[] = {write_mode ? 'w' : 'r', 'b', compression ? compression : '3', '\0'}; |
525 | 28 | gzfile = gzopen(filename.c_str(), mode); |
526 | 28 | if (!gzfile) |
527 | 28 | { |
528 | 28 | CV_LOG_ERROR(NULL, "Can't open archive: '" << filename << "' mode=" << mode); |
529 | 28 | return false; |
530 | 28 | } |
531 | | #else |
532 | | CV_Error(cv::Error::StsNotImplemented, "There is no compressed file storage support in this configuration"); |
533 | | #endif |
534 | 28 | } |
535 | 602 | } |
536 | | |
537 | | // FIXIT release() must do that, use CV_Assert() here instead |
538 | 26 | roots.clear(); |
539 | 26 | fs_data.clear(); |
540 | | |
541 | 26 | wrap_margin = 71; |
542 | 26 | fmt = FileStorage::FORMAT_AUTO; |
543 | | |
544 | 26 | if (write_mode) { |
545 | 0 | fmt = flags & FileStorage::FORMAT_MASK; |
546 | |
|
547 | 0 | if (mem_mode) |
548 | 0 | outbuf.clear(); |
549 | |
|
550 | 0 | if (fmt == FileStorage::FORMAT_AUTO && !filename.empty()) { |
551 | 0 | const char *dot_pos = NULL; |
552 | 0 | const char *dot_pos2 = NULL; |
553 | | // like strrchr() implementation, but save two last positions simultaneously |
554 | 0 | for (const char *pos = &filename[0]; pos[0] != 0; pos++) { |
555 | 0 | if (pos[0] == '.') { |
556 | 0 | dot_pos2 = dot_pos; |
557 | 0 | dot_pos = pos; |
558 | 0 | } |
559 | 0 | } |
560 | 0 | if (fs::strcasecmp(dot_pos, ".gz") == 0 && dot_pos2 != NULL) { |
561 | 0 | dot_pos = dot_pos2; |
562 | 0 | } |
563 | 0 | fmt = (fs::strcasecmp(dot_pos, ".xml") == 0 || fs::strcasecmp(dot_pos, ".xml.gz") == 0) |
564 | 0 | ? FileStorage::FORMAT_XML |
565 | 0 | : (fs::strcasecmp(dot_pos, ".json") == 0 || fs::strcasecmp(dot_pos, ".json.gz") == 0) |
566 | 0 | ? FileStorage::FORMAT_JSON |
567 | 0 | : FileStorage::FORMAT_YAML; |
568 | 0 | } else if (fmt == FileStorage::FORMAT_AUTO) { |
569 | 0 | fmt = FileStorage::FORMAT_XML; |
570 | 0 | } |
571 | | |
572 | | // we use factor=6 for XML (the longest characters (' and ") are encoded with 6 bytes (' and ") |
573 | | // and factor=4 for YAML ( as we use 4 bytes for non ASCII characters (e.g. \xAB)) |
574 | 0 | int buf_size = CV_FS_MAX_LEN * (fmt == FileStorage::FORMAT_XML ? 6 : 4) + 1024; |
575 | |
|
576 | 0 | if (append) { |
577 | 0 | fseek(file, 0, SEEK_END); |
578 | 0 | if (ftell(file) == 0) |
579 | 0 | append = false; |
580 | 0 | } |
581 | |
|
582 | 0 | write_stack.clear(); |
583 | 0 | empty_stream = true; |
584 | 0 | write_stack.push_back(FStructData("", FileNode::MAP | FileNode::EMPTY, 0)); |
585 | 0 | buffer.reserve(buf_size + 1024); |
586 | 0 | buffer.resize(buf_size); |
587 | 0 | bufofs = 0; |
588 | 0 | is_using_base64 = write_base64; |
589 | 0 | state_of_writing_base64 = FileStorage_API::Base64State::Uncertain; |
590 | |
|
591 | 0 | if (fmt == FileStorage::FORMAT_XML) { |
592 | 0 | size_t file_size = file ? (size_t) ftell(file) : (size_t) 0; |
593 | 0 | if (!append || file_size == 0) { |
594 | 0 | if (encoding && *encoding != '\0') { |
595 | 0 | if (fs::strcasecmp(encoding, "UTF-16") == 0) { |
596 | 0 | release(); |
597 | 0 | CV_Error(cv::Error::StsBadArg, "UTF-16 XML encoding is not supported! Use 8-bit encoding\n"); |
598 | 0 | } |
599 | | |
600 | 0 | CV_Assert(strlen(encoding) < 1000); |
601 | 0 | char buf[1100]; |
602 | 0 | snprintf(buf, sizeof(buf), "<?xml version=\"1.0\" encoding=\"%s\"?>\n", encoding); |
603 | 0 | puts(buf); |
604 | 0 | } else |
605 | 0 | puts("<?xml version=\"1.0\"?>\n"); |
606 | 0 | puts("<opencv_storage>\n"); |
607 | 0 | } else { |
608 | 0 | int xml_buf_size = 1 << 10; |
609 | 0 | char substr[] = "</opencv_storage>"; |
610 | 0 | int last_occurrence = -1; |
611 | 0 | xml_buf_size = MIN(xml_buf_size, int(file_size)); |
612 | 0 | fseek(file, -xml_buf_size, SEEK_END); |
613 | | // find the last occurrence of </opencv_storage> |
614 | 0 | for (;;) { |
615 | 0 | int line_offset = (int) ftell(file); |
616 | 0 | const char *ptr0 = this->gets(xml_buf_size); |
617 | 0 | const char *ptr = NULL; |
618 | 0 | if (!ptr0) |
619 | 0 | break; |
620 | 0 | ptr = ptr0; |
621 | 0 | for (;;) { |
622 | 0 | ptr = strstr(ptr, substr); |
623 | 0 | if (!ptr) |
624 | 0 | break; |
625 | 0 | last_occurrence = line_offset + (int) (ptr - ptr0); |
626 | 0 | ptr += strlen(substr); |
627 | 0 | } |
628 | 0 | } |
629 | 0 | if (last_occurrence < 0) { |
630 | 0 | release(); |
631 | 0 | CV_Error(cv::Error::StsError, "Could not find </opencv_storage> in the end of file.\n"); |
632 | 0 | } |
633 | 0 | closeFile(); |
634 | 0 | file = fopen(filename.c_str(), "r+t"); |
635 | 0 | CV_Assert(file != 0); |
636 | 0 | fseek(file, last_occurrence, SEEK_SET); |
637 | | // replace the last "</opencv_storage>" with " <!-- resumed -->", which has the same length |
638 | 0 | puts(" <!-- resumed -->"); |
639 | 0 | fseek(file, 0, SEEK_END); |
640 | 0 | puts("\n"); |
641 | 0 | } |
642 | | |
643 | 0 | emitter_do_not_use_direct_dereference = createXMLEmitter(this); |
644 | 0 | } else if (fmt == FileStorage::FORMAT_YAML) { |
645 | 0 | if (!append) |
646 | 0 | puts("%YAML:1.0\n---\n"); |
647 | 0 | else |
648 | 0 | puts("...\n---\n"); |
649 | |
|
650 | 0 | emitter_do_not_use_direct_dereference = createYAMLEmitter(this); |
651 | 0 | } else { |
652 | 0 | CV_Assert(fmt == FileStorage::FORMAT_JSON); |
653 | 0 | if (!append) |
654 | 0 | puts("{\n"); |
655 | 0 | else { |
656 | 0 | bool valid = false; |
657 | 0 | long roffset = 0; |
658 | 0 | for (; |
659 | 0 | fseek(file, roffset, SEEK_END) == 0; |
660 | 0 | roffset -= 1) { |
661 | 0 | const char end_mark = '}'; |
662 | 0 | if (fgetc(file) == end_mark) { |
663 | 0 | fseek(file, roffset, SEEK_END); |
664 | 0 | valid = true; |
665 | 0 | break; |
666 | 0 | } |
667 | 0 | } |
668 | |
|
669 | 0 | if (valid) { |
670 | 0 | closeFile(); |
671 | 0 | file = fopen(filename.c_str(), "r+t"); |
672 | 0 | CV_Assert(file != 0); |
673 | 0 | fseek(file, roffset, SEEK_END); |
674 | 0 | fputs(",", file); |
675 | 0 | } else { |
676 | 0 | CV_Error(cv::Error::StsError, "Could not find '}' in the end of file.\n"); |
677 | 0 | } |
678 | 0 | } |
679 | 0 | write_stack.back().indent = 4; |
680 | 0 | emitter_do_not_use_direct_dereference = createJSONEmitter(this); |
681 | 0 | } |
682 | 0 | is_opened = true; |
683 | 26 | } else { |
684 | 26 | const size_t buf_size0 = 40; |
685 | 26 | buffer.resize(buf_size0); |
686 | 26 | if (mem_mode) { |
687 | 0 | strbuf = (char *) filename_or_buf; |
688 | 0 | strbufsize = strlen(strbuf); |
689 | 0 | } |
690 | | |
691 | 26 | const char *yaml_signature = "%YAML"; |
692 | 26 | const char *json_signature = "{"; |
693 | 26 | const char *xml_signature = "<?xml"; |
694 | 26 | char *buf = this->gets(16); |
695 | 26 | CV_Assert(buf); |
696 | 6 | char *bufPtr = cv_skip_BOM(buf); |
697 | 6 | size_t bufOffset = bufPtr - buf; |
698 | | |
699 | 6 | if (strncmp(bufPtr, yaml_signature, strlen(yaml_signature)) == 0) |
700 | 0 | fmt = FileStorage::FORMAT_YAML; |
701 | 6 | else if (strncmp(bufPtr, json_signature, strlen(json_signature)) == 0) |
702 | 0 | fmt = FileStorage::FORMAT_JSON; |
703 | 6 | else if (strncmp(bufPtr, xml_signature, strlen(xml_signature)) == 0) |
704 | 0 | fmt = FileStorage::FORMAT_XML; |
705 | 6 | else if (strbufsize == bufOffset) |
706 | 6 | CV_Error(cv::Error::StsBadArg, "Input file is invalid"); |
707 | 0 | else |
708 | 0 | CV_Error(cv::Error::StsBadArg, "Unsupported file storage format"); |
709 | | |
710 | 0 | rewind(); |
711 | 0 | strbufpos = bufOffset; |
712 | 0 | bufofs = 0; |
713 | |
|
714 | 0 | try { |
715 | 0 | char *ptr = bufferStart(); |
716 | 0 | ptr[0] = ptr[1] = ptr[2] = '\0'; |
717 | 0 | FileNode root_nodes(fs_ext, 0, 0); |
718 | |
|
719 | 0 | uchar *rptr = reserveNodeSpace(root_nodes, 9); |
720 | 0 | *rptr = FileNode::SEQ; |
721 | 0 | writeInt(rptr + 1, 4); |
722 | 0 | writeInt(rptr + 5, 0); |
723 | |
|
724 | 0 | roots.clear(); |
725 | |
|
726 | 0 | switch (fmt) { |
727 | 0 | case FileStorage::FORMAT_XML: |
728 | 0 | parser_do_not_use_direct_dereference = createXMLParser(this); |
729 | 0 | break; |
730 | 0 | case FileStorage::FORMAT_YAML: |
731 | 0 | parser_do_not_use_direct_dereference = createYAMLParser(this); |
732 | 0 | break; |
733 | 0 | case FileStorage::FORMAT_JSON: |
734 | 0 | parser_do_not_use_direct_dereference = createJSONParser(this); |
735 | 0 | break; |
736 | 0 | default: |
737 | 0 | parser_do_not_use_direct_dereference = Ptr<FileStorageParser>(); |
738 | 0 | } |
739 | | |
740 | 0 | if (!parser_do_not_use_direct_dereference.empty()) { |
741 | 0 | ok = getParser().parse(ptr); |
742 | 0 | if (ok) { |
743 | 0 | finalizeCollection(root_nodes); |
744 | |
|
745 | 0 | CV_Assert(!fs_data_ptrs.empty()); |
746 | 0 | FileNode roots_node(fs_ext, 0, 0); |
747 | 0 | size_t i, nroots = roots_node.size(); |
748 | 0 | FileNodeIterator it = roots_node.begin(); |
749 | |
|
750 | 0 | for (i = 0; i < nroots; i++, ++it) |
751 | 0 | roots.push_back(*it); |
752 | 0 | } |
753 | 0 | } |
754 | 0 | } |
755 | 0 | catch (...) |
756 | 0 | { |
757 | | // FIXIT log error message |
758 | 0 | is_opened = true; |
759 | 0 | release(); |
760 | 0 | throw; |
761 | 0 | } |
762 | | |
763 | | // release resources that we do not need anymore |
764 | 0 | closeFile(); |
765 | 0 | is_opened = true; |
766 | 0 | std::vector<char> tmpbuf; |
767 | 0 | std::swap(buffer, tmpbuf); |
768 | 0 | bufofs = 0; |
769 | 0 | } |
770 | 0 | return ok; |
771 | 26 | } |
772 | | |
773 | 0 | void FileStorage::Impl::puts(const char *str) { |
774 | 0 | CV_Assert(write_mode); |
775 | 0 | if (mem_mode) |
776 | 0 | std::copy(str, str + strlen(str), std::back_inserter(outbuf)); |
777 | 0 | else if (file) |
778 | 0 | fputs(str, file); |
779 | 0 | #if USE_ZLIB |
780 | 0 | else if (gzfile) |
781 | 0 | gzputs(gzfile, str); |
782 | 0 | #endif |
783 | 0 | else |
784 | 0 | CV_Error(cv::Error::StsError, "The storage is not opened"); |
785 | 0 | } |
786 | | |
787 | 37 | char *FileStorage::Impl::getsFromFile(char *buf, int count) { |
788 | 37 | if (file) |
789 | 37 | return fgets(buf, count, file); |
790 | 0 | #if USE_ZLIB |
791 | 0 | if (gzfile) |
792 | 0 | return gzgets(gzfile, buf, count); |
793 | 0 | #endif |
794 | 0 | CV_Error(cv::Error::StsError, "The storage is not opened"); |
795 | 0 | } |
796 | | |
797 | 26 | char *FileStorage::Impl::gets(size_t maxCount) { |
798 | 26 | if (strbuf) { |
799 | 0 | size_t i = strbufpos, len = strbufsize; |
800 | 0 | const char *instr = strbuf; |
801 | 0 | for (; i < len; i++) { |
802 | 0 | char c = instr[i]; |
803 | 0 | if (c == '\0' || c == '\n') { |
804 | 0 | if (c == '\n') |
805 | 0 | i++; |
806 | 0 | break; |
807 | 0 | } |
808 | 0 | } |
809 | 0 | size_t count = i - strbufpos; |
810 | 0 | if (maxCount == 0 || maxCount > count) |
811 | 0 | maxCount = count; |
812 | 0 | buffer.resize(std::max(buffer.size(), maxCount + 8)); |
813 | 0 | memcpy(&buffer[0], instr + strbufpos, maxCount); |
814 | 0 | buffer[maxCount] = '\0'; |
815 | 0 | strbufpos = i; |
816 | 0 | return maxCount > 0 ? &buffer[0] : 0; |
817 | 0 | } |
818 | | |
819 | 26 | const size_t MAX_BLOCK_SIZE = INT_MAX / 2; // hopefully, that will be enough |
820 | 26 | if (maxCount == 0) |
821 | 0 | maxCount = MAX_BLOCK_SIZE; |
822 | 26 | else |
823 | 26 | CV_Assert(maxCount < MAX_BLOCK_SIZE); |
824 | 26 | size_t ofs = 0; |
825 | | |
826 | 37 | for (;;) { |
827 | 37 | int count = (int) std::min(buffer.size() - ofs - 16, maxCount); |
828 | 37 | char *ptr = getsFromFile(&buffer[ofs], count + 1); |
829 | 37 | if (!ptr) |
830 | 20 | break; |
831 | 17 | int delta = (int) strlen(ptr); |
832 | 17 | ofs += delta; |
833 | 17 | maxCount -= delta; |
834 | 17 | if (delta == 0 || ptr[delta - 1] == '\n' || maxCount == 0) |
835 | 6 | break; |
836 | 11 | if (delta == count) |
837 | 0 | buffer.resize((size_t) (buffer.size() * 1.5)); |
838 | 11 | } |
839 | 26 | return ofs > 0 ? &buffer[0] : 0; |
840 | 26 | } |
841 | | |
842 | 0 | char *FileStorage::Impl::gets() { |
843 | 0 | char *ptr = this->gets(0); |
844 | 0 | if (!ptr) { |
845 | 0 | ptr = bufferStart(); // FIXIT Why do we need this hack? What is about other parsers JSON/YAML? |
846 | 0 | *ptr = '\0'; |
847 | 0 | setEof(); |
848 | 0 | return 0; |
849 | 0 | } else { |
850 | 0 | size_t l = strlen(ptr); |
851 | 0 | if (l > 0 && ptr[l - 1] != '\n' && ptr[l - 1] != '\r' && !eof()) { |
852 | 0 | ptr[l] = '\n'; |
853 | 0 | ptr[l + 1] = '\0'; |
854 | 0 | } |
855 | 0 | } |
856 | 0 | lineno++; |
857 | 0 | return ptr; |
858 | 0 | } |
859 | | |
860 | 0 | bool FileStorage::Impl::eof() { |
861 | 0 | if (dummy_eof) |
862 | 0 | return true; |
863 | 0 | if (strbuf) |
864 | 0 | return strbufpos >= strbufsize; |
865 | 0 | if (file) |
866 | 0 | return feof(file) != 0; |
867 | 0 | #if USE_ZLIB |
868 | 0 | if (gzfile) |
869 | 0 | return gzeof(gzfile) != 0; |
870 | 0 | #endif |
871 | 0 | return false; |
872 | 0 | } |
873 | | |
874 | 0 | void FileStorage::Impl::setEof() { |
875 | 0 | dummy_eof = true; |
876 | 0 | } |
877 | | |
878 | 2.40k | void FileStorage::Impl::closeFile() { |
879 | 2.40k | if (file) |
880 | 26 | fclose(file); |
881 | 2.38k | #if USE_ZLIB |
882 | 2.38k | else if (gzfile) |
883 | 0 | gzclose(gzfile); |
884 | 2.40k | #endif |
885 | 2.40k | file = 0; |
886 | 2.40k | gzfile = 0; |
887 | 2.40k | strbuf = 0; |
888 | 2.40k | strbufpos = 0; |
889 | 2.40k | is_opened = false; |
890 | 2.40k | } |
891 | | |
892 | 0 | void FileStorage::Impl::rewind() { |
893 | 0 | if (file) |
894 | 0 | ::rewind(file); |
895 | 0 | #if USE_ZLIB |
896 | 0 | else if (gzfile) |
897 | 0 | gzrewind(gzfile); |
898 | 0 | #endif |
899 | 0 | strbufpos = 0; |
900 | 0 | } |
901 | | |
902 | 0 | char *FileStorage::Impl::resizeWriteBuffer(char *ptr, int len) { |
903 | 0 | const char *buffer_end = &buffer[0] + buffer.size(); |
904 | 0 | if (ptr + len < buffer_end) |
905 | 0 | return ptr; |
906 | | |
907 | 0 | const char *buffer_start = &buffer[0]; |
908 | 0 | int written_len = (int) (ptr - buffer_start); |
909 | |
|
910 | 0 | CV_Assert(written_len <= (int) buffer.size()); |
911 | 0 | int new_size = (int) ((buffer_end - buffer_start) * 3 / 2); |
912 | 0 | new_size = MAX(written_len + len, new_size); |
913 | 0 | buffer.reserve(new_size + 256); |
914 | 0 | buffer.resize(new_size); |
915 | 0 | bufofs = written_len; |
916 | 0 | return &buffer[0] + bufofs; |
917 | 0 | } |
918 | | |
919 | 0 | char *FileStorage::Impl::flush() { |
920 | 0 | char *buffer_start = &buffer[0]; |
921 | 0 | char *ptr = buffer_start + bufofs; |
922 | |
|
923 | 0 | if (ptr > buffer_start + space) { |
924 | 0 | ptr[0] = '\n'; |
925 | 0 | ptr[1] = '\0'; |
926 | 0 | puts(buffer_start); |
927 | 0 | bufofs = 0; |
928 | 0 | } |
929 | |
|
930 | 0 | int indent = write_stack.back().indent; |
931 | |
|
932 | 0 | if (space != indent) { |
933 | 0 | memset(buffer_start, ' ', indent); |
934 | 0 | space = indent; |
935 | 0 | } |
936 | 0 | bufofs = space; |
937 | 0 | ptr = buffer_start + bufofs; |
938 | |
|
939 | 0 | return ptr; |
940 | 0 | } |
941 | | |
942 | 0 | void FileStorage::Impl::endWriteStruct() { |
943 | 0 | CV_Assert(write_mode); |
944 | | |
945 | 0 | check_if_write_struct_is_delayed(false); |
946 | 0 | if (state_of_writing_base64 != FileStorage_API::Uncertain) |
947 | 0 | switch_to_Base64_state(FileStorage_API::Uncertain); |
948 | |
|
949 | 0 | CV_Assert(!write_stack.empty()); |
950 | | |
951 | 0 | FStructData ¤t_struct = write_stack.back(); |
952 | 0 | if (fmt == FileStorage::FORMAT_JSON && !FileNode::isFlow(current_struct.flags) && write_stack.size() > 1) |
953 | 0 | current_struct.indent = write_stack[write_stack.size() - 2].indent; |
954 | |
|
955 | 0 | getEmitter().endWriteStruct(current_struct); |
956 | |
|
957 | 0 | write_stack.pop_back(); |
958 | 0 | if (!write_stack.empty()) |
959 | 0 | write_stack.back().flags &= ~FileNode::EMPTY; |
960 | 0 | } |
961 | | |
962 | | void FileStorage::Impl::startWriteStruct_helper(const char *key, int struct_flags, |
963 | 0 | const char *type_name) { |
964 | 0 | CV_Assert(write_mode); |
965 | | |
966 | 0 | struct_flags = (struct_flags & (FileNode::TYPE_MASK | FileNode::FLOW)) | FileNode::EMPTY; |
967 | 0 | if (!FileNode::isCollection(struct_flags)) |
968 | 0 | CV_Error(cv::Error::StsBadArg, |
969 | 0 | "Some collection type: FileNode::SEQ or FileNode::MAP must be specified"); |
970 | | |
971 | 0 | if (type_name && type_name[0] == '\0') |
972 | 0 | type_name = 0; |
973 | |
|
974 | 0 | FStructData s = getEmitter().startWriteStruct(write_stack.back(), key, struct_flags, type_name); |
975 | |
|
976 | 0 | write_stack.push_back(s); |
977 | 0 | size_t write_stack_size = write_stack.size(); |
978 | 0 | if (write_stack_size > 1) |
979 | 0 | write_stack[write_stack_size - 2].flags &= ~FileNode::EMPTY; |
980 | |
|
981 | 0 | if (fmt != FileStorage::FORMAT_JSON && !FileNode::isFlow(s.flags)) |
982 | 0 | flush(); |
983 | |
|
984 | 0 | if (fmt == FileStorage::FORMAT_JSON && type_name && type_name[0] && FileNode::isMap(struct_flags)) { |
985 | 0 | getEmitter().write("type_id", type_name, false); |
986 | 0 | } |
987 | 0 | } |
988 | | |
989 | | void FileStorage::Impl::startWriteStruct(const char *key, int struct_flags, |
990 | 0 | const char *type_name) { |
991 | 0 | check_if_write_struct_is_delayed(false); |
992 | 0 | if (state_of_writing_base64 == FileStorage_API::NotUse) |
993 | 0 | switch_to_Base64_state(FileStorage_API::Uncertain); |
994 | |
|
995 | 0 | if (state_of_writing_base64 == FileStorage_API::Uncertain && FileNode::isSeq(struct_flags) |
996 | 0 | && is_using_base64 && type_name == 0) { |
997 | | /* Uncertain whether output Base64 data */ |
998 | 0 | make_write_struct_delayed(key, struct_flags, type_name); |
999 | 0 | } else if (type_name && memcmp(type_name, "binary", 6) == 0) { |
1000 | | /* Must output Base64 data */ |
1001 | 0 | if ((FileNode::TYPE_MASK & struct_flags) != FileNode::SEQ) |
1002 | 0 | CV_Error(cv::Error::StsBadArg, "must set 'struct_flags |= CV_NODE_SEQ' if using Base64."); |
1003 | 0 | else if (state_of_writing_base64 != FileStorage_API::Uncertain) |
1004 | 0 | CV_Error(cv::Error::StsError, "function \'cvStartWriteStruct\' calls cannot be nested if using Base64."); |
1005 | | |
1006 | 0 | startWriteStruct_helper(key, struct_flags, "binary"); |
1007 | |
|
1008 | 0 | if (state_of_writing_base64 != FileStorage_API::Uncertain) |
1009 | 0 | switch_to_Base64_state(FileStorage_API::Uncertain); |
1010 | 0 | switch_to_Base64_state(FileStorage_API::InUse); |
1011 | 0 | } else { |
1012 | | /* Won't output Base64 data */ |
1013 | 0 | if (state_of_writing_base64 == FileStorage_API::InUse) |
1014 | 0 | CV_Error(cv::Error::StsError, "At the end of the output Base64, `cvEndWriteStruct` is needed."); |
1015 | | |
1016 | 0 | startWriteStruct_helper(key, struct_flags, type_name); |
1017 | |
|
1018 | 0 | if (state_of_writing_base64 != FileStorage_API::Uncertain) |
1019 | 0 | switch_to_Base64_state(FileStorage_API::Uncertain); |
1020 | 0 | switch_to_Base64_state(FileStorage_API::NotUse); |
1021 | 0 | } |
1022 | 0 | } |
1023 | | |
1024 | 0 | void FileStorage::Impl::writeComment(const char *comment, bool eol_comment) { |
1025 | 0 | CV_Assert(write_mode); |
1026 | 0 | getEmitter().writeComment(comment, eol_comment); |
1027 | 0 | } |
1028 | | |
1029 | 0 | void FileStorage::Impl::startNextStream() { |
1030 | 0 | CV_Assert(write_mode); |
1031 | 0 | if (!empty_stream) { |
1032 | 0 | while (!write_stack.empty()) |
1033 | 0 | endWriteStruct(); |
1034 | 0 | flush(); |
1035 | 0 | getEmitter().startNextStream(); |
1036 | 0 | empty_stream = true; |
1037 | 0 | write_stack.push_back(FStructData("", FileNode::EMPTY, 0)); |
1038 | 0 | bufofs = 0; |
1039 | 0 | } |
1040 | 0 | } |
1041 | | |
1042 | 0 | void FileStorage::Impl::write(const String &key, int value) { |
1043 | 0 | CV_Assert(write_mode); |
1044 | 0 | getEmitter().write(key.c_str(), value); |
1045 | 0 | } |
1046 | | |
1047 | 0 | void FileStorage::Impl::write(const String &key, double value) { |
1048 | 0 | CV_Assert(write_mode); |
1049 | 0 | getEmitter().write(key.c_str(), value); |
1050 | 0 | } |
1051 | | |
1052 | 0 | void FileStorage::Impl::write(const String &key, const String &value) { |
1053 | 0 | CV_Assert(write_mode); |
1054 | 0 | getEmitter().write(key.c_str(), value.c_str(), false); |
1055 | 0 | } |
1056 | | |
1057 | 0 | void FileStorage::Impl::writeRawData(const std::string &dt, const void *_data, size_t len) { |
1058 | 0 | CV_Assert(write_mode); |
1059 | | |
1060 | 0 | if (is_using_base64 || state_of_writing_base64 == FileStorage_API::Base64State::InUse) { |
1061 | 0 | writeRawDataBase64(_data, len, dt.c_str()); |
1062 | 0 | return; |
1063 | 0 | } else if (state_of_writing_base64 == FileStorage_API::Base64State::Uncertain) { |
1064 | 0 | switch_to_Base64_state(FileStorage_API::Base64State::NotUse); |
1065 | 0 | } |
1066 | | |
1067 | 0 | size_t elemSize = fs::calcStructSize(dt.c_str(), 0); |
1068 | 0 | CV_Assert(elemSize); |
1069 | 0 | CV_Assert(len % elemSize == 0); |
1070 | 0 | len /= elemSize; |
1071 | |
|
1072 | 0 | bool explicitZero = fmt == FileStorage::FORMAT_JSON; |
1073 | 0 | const uchar *data0 = (const uchar *) _data; |
1074 | 0 | int fmt_pairs[CV_FS_MAX_FMT_PAIRS * 2], k, fmt_pair_count; |
1075 | 0 | char buf[256] = ""; |
1076 | |
|
1077 | 0 | fmt_pair_count = fs::decodeFormat(dt.c_str(), fmt_pairs, CV_FS_MAX_FMT_PAIRS); |
1078 | |
|
1079 | 0 | if (!len) |
1080 | 0 | return; |
1081 | | |
1082 | 0 | if (!data0) |
1083 | 0 | CV_Error(cv::Error::StsNullPtr, "Null data pointer"); |
1084 | | |
1085 | 0 | if (fmt_pair_count == 1) { |
1086 | 0 | fmt_pairs[0] *= (int) len; |
1087 | 0 | len = 1; |
1088 | 0 | } |
1089 | |
|
1090 | 0 | for (; len--; data0 += elemSize) { |
1091 | 0 | int offset = 0; |
1092 | 0 | for (k = 0; k < fmt_pair_count; k++) { |
1093 | 0 | int i, count = fmt_pairs[k * 2]; |
1094 | 0 | int elem_type = fmt_pairs[k * 2 + 1]; |
1095 | 0 | int elem_size = CV_ELEM_SIZE(elem_type); |
1096 | 0 | const char *ptr; |
1097 | |
|
1098 | 0 | offset = cvAlign(offset, elem_size); |
1099 | 0 | const uchar *data = data0 + offset; |
1100 | |
|
1101 | 0 | for (i = 0; i < count; i++) { |
1102 | 0 | switch (elem_type) { |
1103 | 0 | case CV_8U: |
1104 | 0 | ptr = fs::itoa(*(uchar *) data, buf, 10); |
1105 | 0 | data++; |
1106 | 0 | break; |
1107 | 0 | case CV_8S: |
1108 | 0 | ptr = fs::itoa(*(char *) data, buf, 10); |
1109 | 0 | data++; |
1110 | 0 | break; |
1111 | 0 | case CV_16U: |
1112 | 0 | ptr = fs::itoa(*(ushort *) data, buf, 10); |
1113 | 0 | data += sizeof(ushort); |
1114 | 0 | break; |
1115 | 0 | case CV_16S: |
1116 | 0 | ptr = fs::itoa(*(short *) data, buf, 10); |
1117 | 0 | data += sizeof(short); |
1118 | 0 | break; |
1119 | 0 | case CV_32S: |
1120 | 0 | ptr = fs::itoa(*(int *) data, buf, 10); |
1121 | 0 | data += sizeof(int); |
1122 | 0 | break; |
1123 | 0 | case CV_32F: |
1124 | 0 | ptr = fs::floatToString(buf, sizeof(buf), *(float *) data, false, explicitZero); |
1125 | 0 | data += sizeof(float); |
1126 | 0 | break; |
1127 | 0 | case CV_64F: |
1128 | 0 | ptr = fs::doubleToString(buf, sizeof(buf), *(double *) data, explicitZero); |
1129 | 0 | data += sizeof(double); |
1130 | 0 | break; |
1131 | 0 | case CV_16F: /* reference */ |
1132 | 0 | ptr = fs::floatToString(buf, sizeof(buf), (float) *(float16_t *) data, true, explicitZero); |
1133 | 0 | data += sizeof(float16_t); |
1134 | 0 | break; |
1135 | 0 | default: |
1136 | 0 | CV_Error(cv::Error::StsUnsupportedFormat, "Unsupported type"); |
1137 | 0 | return; |
1138 | 0 | } |
1139 | | |
1140 | 0 | getEmitter().writeScalar(0, ptr); |
1141 | 0 | } |
1142 | | |
1143 | 0 | offset = (int) (data - data0); |
1144 | 0 | } |
1145 | 0 | } |
1146 | 0 | } |
1147 | | |
1148 | 0 | void FileStorage::Impl::workaround() { |
1149 | 0 | check_if_write_struct_is_delayed(false); |
1150 | |
|
1151 | 0 | if (state_of_writing_base64 != FileStorage_API::Base64State::Uncertain) |
1152 | 0 | switch_to_Base64_state(FileStorage_API::Base64State::Uncertain); |
1153 | 0 | } |
1154 | | |
1155 | 0 | void FileStorage::Impl::switch_to_Base64_state(FileStorage_API::Base64State new_state) { |
1156 | 0 | const char *err_unkonwn_state = "Unexpected error, unable to determine the Base64 state."; |
1157 | 0 | const char *err_unable_to_switch = "Unexpected error, unable to switch to this state."; |
1158 | | |
1159 | | /* like a finite state machine */ |
1160 | 0 | switch (state_of_writing_base64) { |
1161 | 0 | case FileStorage_API::Base64State::Uncertain: |
1162 | 0 | switch (new_state) { |
1163 | 0 | case FileStorage_API::Base64State::InUse: |
1164 | 0 | { |
1165 | 0 | CV_DbgAssert(base64_writer == 0); |
1166 | 0 | bool can_indent = (fmt != cv::FileStorage::Mode::FORMAT_JSON); |
1167 | 0 | base64_writer = new base64::Base64Writer(*this, can_indent); |
1168 | 0 | if (!can_indent) { |
1169 | 0 | char *ptr = bufferPtr(); |
1170 | 0 | *ptr++ = '\0'; |
1171 | 0 | puts(bufferStart()); |
1172 | 0 | setBufferPtr(bufferStart()); |
1173 | 0 | memset(bufferStart(), 0, static_cast<int>(space)); |
1174 | 0 | puts("\"$base64$"); |
1175 | 0 | } |
1176 | 0 | break; |
1177 | 0 | } |
1178 | 0 | case FileStorage_API::Base64State::Uncertain: |
1179 | 0 | break; |
1180 | 0 | case FileStorage_API::Base64State::NotUse: |
1181 | 0 | break; |
1182 | 0 | default: |
1183 | 0 | CV_Error(cv::Error::StsError, err_unkonwn_state); |
1184 | 0 | break; |
1185 | 0 | } |
1186 | 0 | break; |
1187 | 0 | case FileStorage_API::Base64State::InUse: |
1188 | 0 | switch (new_state) { |
1189 | 0 | case FileStorage_API::Base64State::InUse: |
1190 | 0 | case FileStorage_API::Base64State::NotUse: |
1191 | 0 | CV_Error(cv::Error::StsError, err_unable_to_switch); |
1192 | 0 | break; |
1193 | 0 | case FileStorage_API::Base64State::Uncertain: |
1194 | 0 | delete base64_writer; |
1195 | 0 | base64_writer = 0; |
1196 | 0 | if ( fmt == cv::FileStorage::FORMAT_JSON ) |
1197 | 0 | { |
1198 | 0 | puts("\""); |
1199 | 0 | setBufferPtr(bufferStart()); |
1200 | 0 | flush(); |
1201 | 0 | memset(bufferStart(), 0, static_cast<int>(space) ); |
1202 | 0 | setBufferPtr(bufferStart()); |
1203 | 0 | } |
1204 | 0 | break; |
1205 | 0 | default: |
1206 | 0 | CV_Error(cv::Error::StsError, err_unkonwn_state); |
1207 | 0 | break; |
1208 | 0 | } |
1209 | 0 | break; |
1210 | 0 | case FileStorage_API::Base64State::NotUse: |
1211 | 0 | switch (new_state) { |
1212 | 0 | case FileStorage_API::Base64State::InUse: |
1213 | 0 | case FileStorage_API::Base64State::NotUse: |
1214 | 0 | CV_Error(cv::Error::StsError, err_unable_to_switch); |
1215 | 0 | break; |
1216 | 0 | case FileStorage_API::Base64State::Uncertain: |
1217 | 0 | break; |
1218 | 0 | default: |
1219 | 0 | CV_Error(cv::Error::StsError, err_unkonwn_state); |
1220 | 0 | break; |
1221 | 0 | } |
1222 | 0 | break; |
1223 | 0 | default: |
1224 | 0 | CV_Error(cv::Error::StsError, err_unkonwn_state); |
1225 | 0 | break; |
1226 | 0 | } |
1227 | | |
1228 | 0 | state_of_writing_base64 = new_state; |
1229 | 0 | } |
1230 | | |
1231 | 0 | void FileStorage::Impl::make_write_struct_delayed(const char *key, int struct_flags, const char *type_name) { |
1232 | 0 | CV_Assert(is_write_struct_delayed == false); |
1233 | 0 | CV_DbgAssert(delayed_struct_key == nullptr); |
1234 | 0 | CV_DbgAssert(delayed_struct_flags == 0); |
1235 | 0 | CV_DbgAssert(delayed_type_name == nullptr); |
1236 | |
|
1237 | 0 | delayed_struct_flags = struct_flags; |
1238 | |
|
1239 | 0 | if (key != nullptr) { |
1240 | 0 | delayed_struct_key = new char[strlen(key) + 1U]; |
1241 | 0 | strcpy(delayed_struct_key, key); |
1242 | 0 | } |
1243 | |
|
1244 | 0 | if (type_name != nullptr) { |
1245 | 0 | delayed_type_name = new char[strlen(type_name) + 1U]; |
1246 | 0 | strcpy(delayed_type_name, type_name); |
1247 | 0 | } |
1248 | |
|
1249 | 0 | is_write_struct_delayed = true; |
1250 | 0 | } |
1251 | | |
1252 | 0 | void FileStorage::Impl::check_if_write_struct_is_delayed(bool change_type_to_base64) { |
1253 | 0 | if (is_write_struct_delayed) { |
1254 | | /* save data to prevent recursive call errors */ |
1255 | 0 | std::string struct_key; |
1256 | 0 | std::string type_name; |
1257 | 0 | int struct_flags = delayed_struct_flags; |
1258 | |
|
1259 | 0 | if (delayed_struct_key != nullptr && *delayed_struct_key != '\0') { |
1260 | 0 | struct_key.assign(delayed_struct_key); |
1261 | 0 | } |
1262 | 0 | if (delayed_type_name != nullptr && *delayed_type_name != '\0') { |
1263 | 0 | type_name.assign(delayed_type_name); |
1264 | 0 | } |
1265 | | |
1266 | | /* reset */ |
1267 | 0 | delete[] delayed_struct_key; |
1268 | 0 | delete[] delayed_type_name; |
1269 | 0 | delayed_struct_key = nullptr; |
1270 | 0 | delayed_struct_flags = 0; |
1271 | 0 | delayed_type_name = nullptr; |
1272 | |
|
1273 | 0 | is_write_struct_delayed = false; |
1274 | | |
1275 | | /* call */ |
1276 | 0 | if (change_type_to_base64) { |
1277 | 0 | startWriteStruct_helper(struct_key.c_str(), struct_flags, "binary"); |
1278 | 0 | if (state_of_writing_base64 != FileStorage_API::Uncertain) |
1279 | 0 | switch_to_Base64_state(FileStorage_API::Uncertain); |
1280 | 0 | switch_to_Base64_state(FileStorage_API::InUse); |
1281 | 0 | } else { |
1282 | 0 | startWriteStruct_helper(struct_key.c_str(), struct_flags, type_name.c_str()); |
1283 | 0 | if (state_of_writing_base64 != FileStorage_API::Uncertain) |
1284 | 0 | switch_to_Base64_state(FileStorage_API::Uncertain); |
1285 | 0 | switch_to_Base64_state(FileStorage_API::NotUse); |
1286 | 0 | } |
1287 | 0 | } |
1288 | 0 | } |
1289 | | |
1290 | 0 | void FileStorage::Impl::writeRawDataBase64(const void *_data, size_t len, const char *dt) { |
1291 | 0 | CV_Assert(write_mode); |
1292 | | |
1293 | 0 | check_if_write_struct_is_delayed(true); |
1294 | |
|
1295 | 0 | if (state_of_writing_base64 == FileStorage_API::Base64State::Uncertain) { |
1296 | 0 | switch_to_Base64_state(FileStorage_API::Base64State::InUse); |
1297 | 0 | } else if (state_of_writing_base64 != FileStorage_API::Base64State::InUse) { |
1298 | 0 | CV_Error(cv::Error::StsError, "Base64 should not be used at present."); |
1299 | 0 | } |
1300 | | |
1301 | 0 | base64_writer->write(_data, len, dt); |
1302 | 0 | } |
1303 | | |
1304 | 0 | FileNode FileStorage::Impl::getFirstTopLevelNode() const { |
1305 | 0 | return roots.empty() ? FileNode() : roots[0]; |
1306 | 0 | } |
1307 | | |
1308 | 0 | FileNode FileStorage::Impl::root(int streamIdx) const { |
1309 | 0 | return streamIdx >= 0 && streamIdx < (int) roots.size() ? roots[streamIdx] : FileNode(); |
1310 | 0 | } |
1311 | | |
1312 | 0 | FileNode FileStorage::Impl::operator[](const String &nodename) const { |
1313 | 0 | return this->operator[](nodename.c_str()); |
1314 | 0 | } |
1315 | | |
1316 | 0 | FileNode FileStorage::Impl::operator[](const char * /*nodename*/) const { |
1317 | 0 | return FileNode(); |
1318 | 0 | } |
1319 | | |
1320 | 0 | int FileStorage::Impl::getFormat() const { return fmt; } |
1321 | | |
1322 | 0 | char *FileStorage::Impl::bufferPtr() const { return (char *) (&buffer[0] + bufofs); } |
1323 | | |
1324 | 0 | char *FileStorage::Impl::bufferStart() const { return (char *) &buffer[0]; } |
1325 | | |
1326 | 0 | char *FileStorage::Impl::bufferEnd() const { return (char *) (&buffer[0] + buffer.size()); } |
1327 | | |
1328 | 0 | void FileStorage::Impl::setBufferPtr(char *ptr) { |
1329 | 0 | char *bufferstart = bufferStart(); |
1330 | 0 | CV_Assert(ptr >= bufferstart && ptr <= bufferEnd()); |
1331 | 0 | bufofs = ptr - bufferstart; |
1332 | 0 | } |
1333 | | |
1334 | 0 | int FileStorage::Impl::wrapMargin() const { return wrap_margin; } |
1335 | | |
1336 | 0 | FStructData &FileStorage::Impl::getCurrentStruct() { |
1337 | 0 | CV_Assert(!write_stack.empty()); |
1338 | 0 | return write_stack.back(); |
1339 | 0 | } |
1340 | | |
1341 | 0 | void FileStorage::Impl::setNonEmpty() { |
1342 | 0 | empty_stream = false; |
1343 | 0 | } |
1344 | | |
1345 | 0 | void FileStorage::Impl::processSpecialDouble(char *buf, double *value, char **endptr) { |
1346 | 0 | FileStorage_API *fs = this; |
1347 | 0 | char c = buf[0]; |
1348 | 0 | int inf_hi = 0x7ff00000; |
1349 | |
|
1350 | 0 | if (c == '-' || c == '+') { |
1351 | 0 | inf_hi = c == '-' ? 0xfff00000 : 0x7ff00000; |
1352 | 0 | c = *++buf; |
1353 | 0 | } |
1354 | |
|
1355 | 0 | if (c != '.') |
1356 | 0 | CV_PARSE_ERROR_CPP("Bad format of floating-point constant"); |
1357 | |
|
1358 | 0 | Cv64suf v; |
1359 | 0 | v.f = 0.; |
1360 | 0 | if (toupper(buf[1]) == 'I' && toupper(buf[2]) == 'N' && toupper(buf[3]) == 'F') |
1361 | 0 | v.u = (uint64) inf_hi << 32; |
1362 | 0 | else if (toupper(buf[1]) == 'N' && toupper(buf[2]) == 'A' && toupper(buf[3]) == 'N') |
1363 | 0 | v.u = (uint64) -1; |
1364 | 0 | else |
1365 | 0 | CV_PARSE_ERROR_CPP("Bad format of floating-point constant"); |
1366 | 0 | *value = v.f; |
1367 | 0 | *endptr = buf + 4; |
1368 | 0 | } |
1369 | | |
1370 | 0 | double FileStorage::Impl::strtod(char *ptr, char **endptr) { |
1371 | 0 | double fval = ::strtod(ptr, endptr); |
1372 | 0 | if (**endptr == '.') { |
1373 | 0 | char *dot_pos = *endptr; |
1374 | 0 | *dot_pos = ','; |
1375 | 0 | double fval2 = ::strtod(ptr, endptr); |
1376 | 0 | *dot_pos = '.'; |
1377 | 0 | if (*endptr > dot_pos) |
1378 | 0 | fval = fval2; |
1379 | 0 | else |
1380 | 0 | *endptr = dot_pos; |
1381 | 0 | } |
1382 | |
|
1383 | 0 | if (*endptr == ptr || cv_isalpha(**endptr)) |
1384 | 0 | processSpecialDouble(ptr, &fval, endptr); |
1385 | |
|
1386 | 0 | return fval; |
1387 | 0 | } |
1388 | | |
1389 | 0 | void FileStorage::Impl::convertToCollection(int type, FileNode &node) { |
1390 | 0 | CV_Assert(type == FileNode::SEQ || type == FileNode::MAP); |
1391 | | |
1392 | 0 | int node_type = node.type(); |
1393 | 0 | if (node_type == type) |
1394 | 0 | return; |
1395 | | |
1396 | 0 | bool named = node.isNamed(); |
1397 | 0 | uchar *ptr = node.ptr() + 1 + (named ? 4 : 0); |
1398 | |
|
1399 | 0 | int ival = 0; |
1400 | 0 | double fval = 0; |
1401 | 0 | std::string sval; |
1402 | 0 | bool add_first_scalar = false; |
1403 | |
|
1404 | 0 | if (node_type != FileNode::NONE) { |
1405 | | // scalar nodes can only be converted to sequences, e.g. in XML: |
1406 | | // <a>5[parser_position]... => create 5 with name "a" |
1407 | | // <a>5 6[parser_position]... => 5 is converted to [5] and then 6 is added to it |
1408 | | // |
1409 | | // otherwise we don't know where to get the element names from |
1410 | 0 | CV_Assert(type == FileNode::SEQ); |
1411 | 0 | if (node_type == FileNode::INT) { |
1412 | 0 | ival = readInt(ptr); |
1413 | 0 | add_first_scalar = true; |
1414 | 0 | } else if (node_type == FileNode::REAL) { |
1415 | 0 | fval = readReal(ptr); |
1416 | 0 | add_first_scalar = true; |
1417 | 0 | } else if (node_type == FileNode::STRING) { |
1418 | 0 | sval = std::string(node); |
1419 | 0 | add_first_scalar = true; |
1420 | 0 | } else |
1421 | 0 | CV_Error_(Error::StsError, ("The node of type %d cannot be converted to collection", node_type)); |
1422 | 0 | } |
1423 | | |
1424 | 0 | ptr = reserveNodeSpace(node, 1 + (named ? 4 : 0) + 4 + 4); |
1425 | 0 | *ptr++ = (uchar) (type | (named ? FileNode::NAMED : 0)); |
1426 | | // name has been copied automatically |
1427 | 0 | if (named) |
1428 | 0 | ptr += 4; |
1429 | | // set raw_size(collection)==4, nelems(collection)==1 |
1430 | 0 | writeInt(ptr, 4); |
1431 | 0 | writeInt(ptr + 4, 0); |
1432 | |
|
1433 | 0 | if (add_first_scalar) |
1434 | 0 | addNode(node, std::string(), node_type, |
1435 | 0 | node_type == FileNode::INT ? (const void *) &ival : |
1436 | 0 | node_type == FileNode::REAL ? (const void *) &fval : |
1437 | 0 | node_type == FileNode::STRING ? (const void *) sval.c_str() : 0, |
1438 | 0 | -1); |
1439 | 0 | } |
1440 | | |
1441 | | // a) allocates new FileNode (for that just set blockIdx to the last block and ofs to freeSpaceOfs) or |
1442 | | // b) reallocates just created new node (blockIdx and ofs must be taken from FileNode). |
1443 | | // If there is no enough space in the current block (it should be the last block added so far), |
1444 | | // the last block is shrunk so that it ends immediately before the reallocated node. Then, |
1445 | | // a new block of sufficient size is allocated and the FileNode is placed in the beginning of it. |
1446 | | // The case (a) can be used to allocate the very first node by setting blockIdx == ofs == 0. |
1447 | | // In the case (b) the existing tag and the name are copied automatically. |
1448 | 0 | uchar *FileStorage::Impl::reserveNodeSpace(FileNode &node, size_t sz) { |
1449 | 0 | bool shrinkBlock = false; |
1450 | 0 | size_t shrinkBlockIdx = 0, shrinkSize = 0; |
1451 | |
|
1452 | 0 | uchar *ptr = 0, *blockEnd = 0; |
1453 | |
|
1454 | 0 | if (!fs_data_ptrs.empty()) { |
1455 | 0 | size_t blockIdx = node.blockIdx; |
1456 | 0 | size_t ofs = node.ofs; |
1457 | 0 | CV_Assert(blockIdx == fs_data_ptrs.size() - 1); |
1458 | 0 | CV_Assert(ofs <= fs_data_blksz[blockIdx]); |
1459 | 0 | CV_Assert(freeSpaceOfs <= fs_data_blksz[blockIdx]); |
1460 | | //CV_Assert( freeSpaceOfs <= ofs + sz ); |
1461 | | |
1462 | 0 | ptr = fs_data_ptrs[blockIdx] + ofs; |
1463 | 0 | blockEnd = fs_data_ptrs[blockIdx] + fs_data_blksz[blockIdx]; |
1464 | |
|
1465 | 0 | CV_Assert(ptr >= fs_data_ptrs[blockIdx] && ptr <= blockEnd); |
1466 | 0 | if (ptr + sz <= blockEnd) { |
1467 | 0 | freeSpaceOfs = ofs + sz; |
1468 | 0 | return ptr; |
1469 | 0 | } |
1470 | | |
1471 | 0 | if (ofs == |
1472 | 0 | 0) // FileNode is a first component of this block. Resize current block instead of allocation of new one. |
1473 | 0 | { |
1474 | 0 | fs_data[blockIdx]->resize(sz); |
1475 | 0 | ptr = &fs_data[blockIdx]->at(0); |
1476 | 0 | fs_data_ptrs[blockIdx] = ptr; |
1477 | 0 | fs_data_blksz[blockIdx] = sz; |
1478 | 0 | freeSpaceOfs = sz; |
1479 | 0 | return ptr; |
1480 | 0 | } |
1481 | | |
1482 | 0 | shrinkBlock = true; |
1483 | 0 | shrinkBlockIdx = blockIdx; |
1484 | 0 | shrinkSize = ofs; |
1485 | 0 | } |
1486 | | |
1487 | 0 | size_t blockSize = std::max((size_t) CV_FS_MAX_LEN * 4 - 256, sz) + 256; |
1488 | 0 | Ptr<std::vector<uchar> > pv = makePtr<std::vector<uchar> >(blockSize); |
1489 | 0 | fs_data.push_back(pv); |
1490 | 0 | uchar *new_ptr = &pv->at(0); |
1491 | 0 | fs_data_ptrs.push_back(new_ptr); |
1492 | 0 | fs_data_blksz.push_back(blockSize); |
1493 | 0 | node.blockIdx = fs_data_ptrs.size() - 1; |
1494 | 0 | node.ofs = 0; |
1495 | 0 | freeSpaceOfs = sz; |
1496 | |
|
1497 | 0 | if (ptr && ptr + 5 <= blockEnd) { |
1498 | 0 | new_ptr[0] = ptr[0]; |
1499 | 0 | if (ptr[0] & FileNode::NAMED) { |
1500 | 0 | new_ptr[1] = ptr[1]; |
1501 | 0 | new_ptr[2] = ptr[2]; |
1502 | 0 | new_ptr[3] = ptr[3]; |
1503 | 0 | new_ptr[4] = ptr[4]; |
1504 | 0 | } |
1505 | 0 | } |
1506 | |
|
1507 | 0 | if (shrinkBlock) { |
1508 | 0 | fs_data[shrinkBlockIdx]->resize(shrinkSize); |
1509 | 0 | fs_data_blksz[shrinkBlockIdx] = shrinkSize; |
1510 | 0 | } |
1511 | |
|
1512 | 0 | return new_ptr; |
1513 | 0 | } |
1514 | | |
1515 | 0 | unsigned FileStorage::Impl::getStringOfs(const std::string &key) const { |
1516 | 0 | str_hash_t::const_iterator it = str_hash.find(key); |
1517 | 0 | return it != str_hash.end() ? it->second : 0; |
1518 | 0 | } |
1519 | | |
1520 | | FileNode FileStorage::Impl::addNode(FileNode &collection, const std::string &key, |
1521 | 0 | int elem_type, const void *value, int len) { |
1522 | 0 | FileStorage_API *fs = this; |
1523 | 0 | bool noname = key.empty() || (fmt == FileStorage::FORMAT_XML && strcmp(key.c_str(), "_") == 0); |
1524 | 0 | convertToCollection(noname ? FileNode::SEQ : FileNode::MAP, collection); |
1525 | |
|
1526 | 0 | bool isseq = collection.empty() ? false : collection.isSeq(); |
1527 | 0 | if (noname != isseq) |
1528 | 0 | CV_PARSE_ERROR_CPP(noname ? "Map element should have a name" : |
1529 | 0 | "Sequence element should not have name (use <_></_>)"); |
1530 | 0 | unsigned strofs = 0; |
1531 | 0 | if (!noname) { |
1532 | 0 | strofs = getStringOfs(key); |
1533 | 0 | if (!strofs) { |
1534 | 0 | strofs = (unsigned) str_hash_data.size(); |
1535 | 0 | size_t keysize = key.size() + 1; |
1536 | 0 | str_hash_data.resize(strofs + keysize); |
1537 | 0 | memcpy(&str_hash_data[0] + strofs, &key[0], keysize); |
1538 | 0 | str_hash.insert(std::make_pair(key, strofs)); |
1539 | 0 | } |
1540 | 0 | } |
1541 | |
|
1542 | 0 | uchar *cp = collection.ptr(); |
1543 | |
|
1544 | 0 | size_t blockIdx = fs_data_ptrs.size() - 1; |
1545 | 0 | size_t ofs = freeSpaceOfs; |
1546 | 0 | FileNode node(fs_ext, blockIdx, ofs); |
1547 | |
|
1548 | 0 | size_t sz0 = 1 + (noname ? 0 : 4) + 8; |
1549 | 0 | uchar *ptr = reserveNodeSpace(node, sz0); |
1550 | |
|
1551 | 0 | *ptr++ = (uchar) (elem_type | (noname ? 0 : FileNode::NAMED)); |
1552 | 0 | if (elem_type == FileNode::NONE) |
1553 | 0 | freeSpaceOfs -= 8; |
1554 | |
|
1555 | 0 | if (!noname) { |
1556 | 0 | writeInt(ptr, (int) strofs); |
1557 | 0 | ptr += 4; |
1558 | 0 | } |
1559 | |
|
1560 | 0 | if (elem_type == FileNode::SEQ || elem_type == FileNode::MAP) { |
1561 | 0 | writeInt(ptr, 4); |
1562 | 0 | writeInt(ptr, 0); |
1563 | 0 | } |
1564 | |
|
1565 | 0 | if (value) |
1566 | 0 | node.setValue(elem_type, value, len); |
1567 | |
|
1568 | 0 | if (collection.isNamed()) |
1569 | 0 | cp += 4; |
1570 | 0 | int nelems = readInt(cp + 5); |
1571 | 0 | writeInt(cp + 5, nelems + 1); |
1572 | |
|
1573 | 0 | return node; |
1574 | 0 | } |
1575 | | |
1576 | 0 | void FileStorage::Impl::finalizeCollection(FileNode &collection) { |
1577 | 0 | if (!collection.isSeq() && !collection.isMap()) |
1578 | 0 | return; |
1579 | 0 | uchar *ptr0 = collection.ptr(), *ptr = ptr0 + 1; |
1580 | 0 | if (*ptr0 & FileNode::NAMED) |
1581 | 0 | ptr += 4; |
1582 | 0 | size_t blockIdx = collection.blockIdx; |
1583 | 0 | size_t ofs = collection.ofs + (size_t) (ptr + 8 - ptr0); |
1584 | 0 | size_t rawSize = 4; |
1585 | 0 | unsigned sz = (unsigned) readInt(ptr + 4); |
1586 | 0 | if (sz > 0) { |
1587 | 0 | size_t lastBlockIdx = fs_data_ptrs.size() - 1; |
1588 | |
|
1589 | 0 | for (; blockIdx < lastBlockIdx; blockIdx++) { |
1590 | 0 | rawSize += fs_data_blksz[blockIdx] - ofs; |
1591 | 0 | ofs = 0; |
1592 | 0 | } |
1593 | 0 | } |
1594 | 0 | rawSize += freeSpaceOfs - ofs; |
1595 | 0 | writeInt(ptr, (int) rawSize); |
1596 | 0 | } |
1597 | | |
1598 | 0 | void FileStorage::Impl::normalizeNodeOfs(size_t &blockIdx, size_t &ofs) const { |
1599 | 0 | while (ofs >= fs_data_blksz[blockIdx]) { |
1600 | 0 | if (blockIdx == fs_data_blksz.size() - 1) { |
1601 | 0 | CV_Assert(ofs == fs_data_blksz[blockIdx]); |
1602 | 0 | break; |
1603 | 0 | } |
1604 | 0 | ofs -= fs_data_blksz[blockIdx]; |
1605 | 0 | blockIdx++; |
1606 | 0 | } |
1607 | 0 | } |
1608 | | |
1609 | 0 | FileStorage::Impl::Base64State FileStorage::Impl::get_state_of_writing_base64() { |
1610 | 0 | return state_of_writing_base64; |
1611 | 0 | } |
1612 | | |
1613 | 0 | int FileStorage::Impl::get_space() { |
1614 | 0 | return space; |
1615 | 0 | } |
1616 | | |
1617 | | |
1618 | 995 | FileStorage::Impl::Base64Decoder::Base64Decoder() { |
1619 | 995 | ofs = 0; |
1620 | 995 | ptr = 0; |
1621 | 995 | indent = 0; |
1622 | 995 | totalchars = 0; |
1623 | 995 | eos = true; |
1624 | 995 | } |
1625 | | |
1626 | 0 | void FileStorage::Impl::Base64Decoder::init(const Ptr<FileStorageParser> &_parser, char *_ptr, int _indent) { |
1627 | 0 | parser_do_not_use_direct_dereference = _parser; |
1628 | 0 | ptr = _ptr; |
1629 | 0 | indent = _indent; |
1630 | 0 | encoded.clear(); |
1631 | 0 | decoded.clear(); |
1632 | 0 | ofs = 0; |
1633 | 0 | totalchars = 0; |
1634 | 0 | eos = false; |
1635 | 0 | } |
1636 | | |
1637 | 0 | bool FileStorage::Impl::Base64Decoder::readMore(int needed) { |
1638 | 0 | static const uchar base64tab[] = |
1639 | 0 | { |
1640 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1641 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1642 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, |
1643 | 0 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, |
1644 | 0 | 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
1645 | 0 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, |
1646 | 0 | 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, |
1647 | 0 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, |
1648 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1649 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1650 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1651 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1652 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1653 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1654 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1655 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
1656 | 0 | }; |
1657 | |
|
1658 | 0 | if (eos) |
1659 | 0 | return false; |
1660 | | |
1661 | 0 | size_t sz = decoded.size(); |
1662 | 0 | CV_Assert(ofs <= sz); |
1663 | 0 | sz -= ofs; |
1664 | 0 | for (size_t i = 0; i < sz; i++) |
1665 | 0 | decoded[i] = decoded[ofs + i]; |
1666 | |
|
1667 | 0 | decoded.resize(sz); |
1668 | 0 | ofs = 0; |
1669 | |
|
1670 | 0 | CV_Assert(ptr); |
1671 | 0 | char *beg = 0, *end = 0; |
1672 | 0 | bool ok = getParser().getBase64Row(ptr, indent, beg, end); |
1673 | 0 | ptr = end; |
1674 | 0 | std::copy(beg, end, std::back_inserter(encoded)); |
1675 | 0 | totalchars += end - beg; |
1676 | |
|
1677 | 0 | if (!ok || beg == end) { |
1678 | | // in the end of base64 sequence pad it with '=' characters so that |
1679 | | // its total length is multiple of |
1680 | 0 | eos = true; |
1681 | 0 | size_t tc = totalchars; |
1682 | 0 | for (; tc % 4 != 0; tc++) |
1683 | 0 | encoded.push_back('='); |
1684 | 0 | } |
1685 | |
|
1686 | 0 | int i = 0, j, n = (int) encoded.size(); |
1687 | 0 | if (n > 0) { |
1688 | 0 | const uchar *tab = base64tab; |
1689 | 0 | char *src = &encoded[0]; |
1690 | |
|
1691 | 0 | for (; i <= n - 4; i += 4) { |
1692 | | // dddddd cccccc bbbbbb aaaaaa => ddddddcc ccccbbbb bbaaaaaa |
1693 | 0 | uchar d = tab[(int) (uchar) src[i]], c = tab[(int) (uchar) src[i + 1]]; |
1694 | 0 | uchar b = tab[(int) (uchar) src[i + 2]], a = tab[(int) (uchar) src[i + 3]]; |
1695 | |
|
1696 | 0 | decoded.push_back((uchar) ((d << 2) | (c >> 4))); |
1697 | 0 | decoded.push_back((uchar) ((c << 4) | (b >> 2))); |
1698 | 0 | decoded.push_back((uchar) ((b << 6) | a)); |
1699 | 0 | } |
1700 | 0 | } |
1701 | |
|
1702 | 0 | if (i > 0 && encoded[i - 1] == '=') { |
1703 | 0 | if (i > 1 && encoded[i - 2] == '=' && !decoded.empty()) |
1704 | 0 | decoded.pop_back(); |
1705 | 0 | if (!decoded.empty()) |
1706 | 0 | decoded.pop_back(); |
1707 | 0 | } |
1708 | |
|
1709 | 0 | n -= i; |
1710 | 0 | for (j = 0; j < n; j++) |
1711 | 0 | encoded[j] = encoded[i + j]; |
1712 | 0 | encoded.resize(n); |
1713 | |
|
1714 | 0 | return (int) decoded.size() >= needed; |
1715 | 0 | } |
1716 | | |
1717 | 0 | uchar FileStorage::Impl::Base64Decoder::getUInt8() { |
1718 | 0 | size_t sz = decoded.size(); |
1719 | 0 | if (ofs >= sz && !readMore(1)) |
1720 | 0 | return (uchar) 0; |
1721 | 0 | return decoded[ofs++]; |
1722 | 0 | } |
1723 | | |
1724 | 0 | ushort FileStorage::Impl::Base64Decoder::getUInt16() { |
1725 | 0 | size_t sz = decoded.size(); |
1726 | 0 | if (ofs + 2 > sz && !readMore(2)) |
1727 | 0 | return (ushort) 0; |
1728 | 0 | ushort val = (decoded[ofs] + (decoded[ofs + 1] << 8)); |
1729 | 0 | ofs += 2; |
1730 | 0 | return val; |
1731 | 0 | } |
1732 | | |
1733 | 0 | int FileStorage::Impl::Base64Decoder::getInt32() { |
1734 | 0 | size_t sz = decoded.size(); |
1735 | 0 | if (ofs + 4 > sz && !readMore(4)) |
1736 | 0 | return 0; |
1737 | 0 | int ival = readInt(&decoded[ofs]); |
1738 | 0 | ofs += 4; |
1739 | 0 | return ival; |
1740 | 0 | } |
1741 | | |
1742 | 0 | double FileStorage::Impl::Base64Decoder::getFloat64() { |
1743 | 0 | size_t sz = decoded.size(); |
1744 | 0 | if (ofs + 8 > sz && !readMore(8)) |
1745 | 0 | return 0; |
1746 | 0 | double fval = readReal(&decoded[ofs]); |
1747 | 0 | ofs += 8; |
1748 | 0 | return fval; |
1749 | 0 | } |
1750 | | |
1751 | 0 | bool FileStorage::Impl::Base64Decoder::endOfStream() const { return eos; } |
1752 | | |
1753 | 0 | char *FileStorage::Impl::Base64Decoder::getPtr() const { return ptr; } |
1754 | | |
1755 | | |
1756 | 0 | char *FileStorage::Impl::parseBase64(char *ptr, int indent, FileNode &collection) { |
1757 | 0 | const int BASE64_HDR_SIZE = 24; |
1758 | 0 | char dt[BASE64_HDR_SIZE + 1] = {0}; |
1759 | 0 | base64decoder.init(parser_do_not_use_direct_dereference, ptr, indent); |
1760 | |
|
1761 | 0 | int i, k; |
1762 | |
|
1763 | 0 | for (i = 0; i < BASE64_HDR_SIZE; i++) |
1764 | 0 | dt[i] = (char) base64decoder.getUInt8(); |
1765 | 0 | for (i = 0; i < BASE64_HDR_SIZE; i++) |
1766 | 0 | if (isspace(dt[i])) |
1767 | 0 | break; |
1768 | 0 | dt[i] = '\0'; |
1769 | |
|
1770 | 0 | CV_Assert(!base64decoder.endOfStream()); |
1771 | | |
1772 | 0 | int fmt_pairs[CV_FS_MAX_FMT_PAIRS * 2]; |
1773 | 0 | int fmt_pair_count = fs::decodeFormat(dt, fmt_pairs, CV_FS_MAX_FMT_PAIRS); |
1774 | 0 | int ival = 0; |
1775 | 0 | double fval = 0; |
1776 | |
|
1777 | 0 | for (;;) { |
1778 | 0 | for (k = 0; k < fmt_pair_count; k++) { |
1779 | 0 | int elem_type = fmt_pairs[k * 2 + 1]; |
1780 | 0 | int count = fmt_pairs[k * 2]; |
1781 | |
|
1782 | 0 | for (i = 0; i < count; i++) { |
1783 | 0 | int node_type = FileNode::INT; |
1784 | 0 | switch (elem_type) { |
1785 | 0 | case CV_8U: |
1786 | 0 | ival = base64decoder.getUInt8(); |
1787 | 0 | break; |
1788 | 0 | case CV_8S: |
1789 | 0 | ival = (char) base64decoder.getUInt8(); |
1790 | 0 | break; |
1791 | 0 | case CV_16U: |
1792 | 0 | ival = base64decoder.getUInt16(); |
1793 | 0 | break; |
1794 | 0 | case CV_16S: |
1795 | 0 | ival = (short) base64decoder.getUInt16(); |
1796 | 0 | break; |
1797 | 0 | case CV_32S: |
1798 | 0 | ival = base64decoder.getInt32(); |
1799 | 0 | break; |
1800 | 0 | case CV_32F: { |
1801 | 0 | Cv32suf v; |
1802 | 0 | v.i = base64decoder.getInt32(); |
1803 | 0 | fval = v.f; |
1804 | 0 | node_type = FileNode::REAL; |
1805 | 0 | } |
1806 | 0 | break; |
1807 | 0 | case CV_64F: |
1808 | 0 | fval = base64decoder.getFloat64(); |
1809 | 0 | node_type = FileNode::REAL; |
1810 | 0 | break; |
1811 | 0 | case CV_16F: |
1812 | 0 | fval = (float) float16_t::fromBits(base64decoder.getUInt16()); |
1813 | 0 | node_type = FileNode::REAL; |
1814 | 0 | break; |
1815 | 0 | default: |
1816 | 0 | CV_Error(Error::StsUnsupportedFormat, "Unsupported type"); |
1817 | 0 | } |
1818 | | |
1819 | 0 | if (base64decoder.endOfStream()) |
1820 | 0 | break; |
1821 | 0 | addNode(collection, std::string(), node_type, |
1822 | 0 | node_type == FileNode::INT ? (void *) &ival : (void *) &fval, -1); |
1823 | 0 | } |
1824 | 0 | } |
1825 | 0 | if (base64decoder.endOfStream()) |
1826 | 0 | break; |
1827 | 0 | } |
1828 | | |
1829 | 0 | finalizeCollection(collection); |
1830 | 0 | return base64decoder.getPtr(); |
1831 | 0 | } |
1832 | | |
1833 | | void FileStorage::Impl::parseError(const char *func_name, const std::string &err_msg, const char *source_file, |
1834 | 0 | int source_line) { |
1835 | 0 | std::string msg = format("%s(%d): %s", filename.c_str(), lineno, err_msg.c_str()); |
1836 | 0 | error(Error::StsParseError, func_name, msg.c_str(), source_file, source_line); |
1837 | 0 | } |
1838 | | |
1839 | 0 | const uchar *FileStorage::Impl::getNodePtr(size_t blockIdx, size_t ofs) const { |
1840 | 0 | CV_Assert(blockIdx < fs_data_ptrs.size()); |
1841 | 0 | CV_Assert(ofs < fs_data_blksz[blockIdx]); |
1842 | | |
1843 | 0 | return fs_data_ptrs[blockIdx] + ofs; |
1844 | 0 | } |
1845 | | |
1846 | 0 | std::string FileStorage::Impl::getName(size_t nameofs) const { |
1847 | 0 | CV_Assert(nameofs < str_hash_data.size()); |
1848 | 0 | return std::string(&str_hash_data[nameofs]); |
1849 | 0 | } |
1850 | | |
1851 | 0 | FileStorage *FileStorage::Impl::getFS() { return fs_ext; } |
1852 | | |
1853 | | |
1854 | | FileStorage::FileStorage() |
1855 | | : state(0) |
1856 | 995 | { |
1857 | 995 | p = makePtr<FileStorage::Impl>(this); |
1858 | 995 | } |
1859 | | |
1860 | | FileStorage::FileStorage(const String& filename, int flags, const String& encoding) |
1861 | | : state(0) |
1862 | 0 | { |
1863 | 0 | p = makePtr<FileStorage::Impl>(this); |
1864 | 0 | bool ok = p->open(filename.c_str(), flags, encoding.c_str()); |
1865 | 0 | if(ok) |
1866 | 0 | state = FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP; |
1867 | 0 | } |
1868 | | |
1869 | | void FileStorage::startWriteStruct(const String& name, int struct_flags, const String& typeName) |
1870 | 0 | { |
1871 | 0 | p->startWriteStruct(name.size() ? name.c_str() : 0, struct_flags, typeName.size() ? typeName.c_str() : 0); |
1872 | 0 | elname = String(); |
1873 | 0 | if ((struct_flags & FileNode::TYPE_MASK) == FileNode::SEQ) |
1874 | 0 | state = FileStorage::VALUE_EXPECTED; |
1875 | 0 | else |
1876 | 0 | state = FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP; |
1877 | 0 | } |
1878 | | |
1879 | | void FileStorage::endWriteStruct() |
1880 | 0 | { |
1881 | 0 | p->endWriteStruct(); |
1882 | 0 | state = p->write_stack.empty() || FileNode::isMap(p->write_stack.back().flags) ? |
1883 | 0 | FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP : |
1884 | 0 | FileStorage::VALUE_EXPECTED; |
1885 | 0 | elname = String(); |
1886 | 0 | } |
1887 | | |
1888 | | FileStorage::~FileStorage() |
1889 | 995 | { |
1890 | 995 | } |
1891 | | |
1892 | | bool FileStorage::open(const String& filename, int flags, const String& encoding) |
1893 | 994 | { |
1894 | 994 | try |
1895 | 994 | { |
1896 | 994 | bool ok = p->open(filename.c_str(), flags, encoding.c_str()); |
1897 | 994 | if(ok) |
1898 | 0 | state = FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP; |
1899 | 994 | return ok; |
1900 | 994 | } |
1901 | 994 | catch (...) |
1902 | 994 | { |
1903 | 418 | release(); |
1904 | 418 | throw; // re-throw |
1905 | 418 | } |
1906 | 994 | } |
1907 | | |
1908 | 0 | bool FileStorage::isOpened() const { return p->is_opened; } |
1909 | | |
1910 | | void FileStorage::release() |
1911 | 418 | { |
1912 | 418 | p->release(); |
1913 | 418 | } |
1914 | | |
1915 | | FileNode FileStorage::root(int i) const |
1916 | 0 | { |
1917 | 0 | if( p.empty() || p->roots.empty() || i < 0 || i >= (int)p->roots.size() ) |
1918 | 0 | return FileNode(); |
1919 | | |
1920 | 0 | return p->roots[i]; |
1921 | 0 | } |
1922 | | |
1923 | | FileNode FileStorage::getFirstTopLevelNode() const |
1924 | 0 | { |
1925 | 0 | FileNode r = root(); |
1926 | 0 | FileNodeIterator it = r.begin(); |
1927 | 0 | return it != r.end() ? *it : FileNode(); |
1928 | 0 | } |
1929 | | |
1930 | | std::string FileStorage::getDefaultObjectName(const std::string& _filename) |
1931 | 0 | { |
1932 | 0 | static const char* stubname = "unnamed"; |
1933 | 0 | const char* filename = _filename.c_str(); |
1934 | 0 | const char* ptr2 = filename + _filename.size(); |
1935 | 0 | const char* ptr = ptr2 - 1; |
1936 | 0 | cv::AutoBuffer<char> name_buf(_filename.size()+1); |
1937 | |
|
1938 | 0 | while( ptr >= filename && *ptr != '\\' && *ptr != '/' && *ptr != ':' ) |
1939 | 0 | { |
1940 | 0 | if( *ptr == '.' && (!*ptr2 || strncmp(ptr2, ".gz", 3) == 0) ) |
1941 | 0 | ptr2 = ptr; |
1942 | 0 | ptr--; |
1943 | 0 | } |
1944 | 0 | ptr++; |
1945 | 0 | if( ptr == ptr2 ) |
1946 | 0 | CV_Error( cv::Error::StsBadArg, "Invalid filename" ); |
1947 | | |
1948 | 0 | char* name = name_buf.data(); |
1949 | | |
1950 | | // name must start with letter or '_' |
1951 | 0 | if( !cv_isalpha(*ptr) && *ptr!= '_' ){ |
1952 | 0 | *name++ = '_'; |
1953 | 0 | } |
1954 | |
|
1955 | 0 | while( ptr < ptr2 ) |
1956 | 0 | { |
1957 | 0 | char c = *ptr++; |
1958 | 0 | if( !cv_isalnum(c) && c != '-' && c != '_' ) |
1959 | 0 | c = '_'; |
1960 | 0 | *name++ = c; |
1961 | 0 | } |
1962 | 0 | *name = '\0'; |
1963 | 0 | name = name_buf.data(); |
1964 | 0 | if( strcmp( name, "_" ) == 0 ) |
1965 | 0 | strcpy( name, stubname ); |
1966 | 0 | return name; |
1967 | 0 | } |
1968 | | |
1969 | | |
1970 | | int FileStorage::getFormat() const |
1971 | 0 | { |
1972 | 0 | return p->fmt; |
1973 | 0 | } |
1974 | | |
1975 | | FileNode FileStorage::operator [](const char* key) const |
1976 | 0 | { |
1977 | 0 | return this->operator[](std::string(key)); |
1978 | 0 | } |
1979 | | |
1980 | | FileNode FileStorage::operator [](const std::string& key) const |
1981 | 0 | { |
1982 | 0 | FileNode res; |
1983 | 0 | for (size_t i = 0; i < p->roots.size(); i++) |
1984 | 0 | { |
1985 | 0 | res = p->roots[i][key]; |
1986 | 0 | if (!res.empty()) |
1987 | 0 | break; |
1988 | 0 | } |
1989 | 0 | return res; |
1990 | 0 | } |
1991 | | |
1992 | | String FileStorage::releaseAndGetString() |
1993 | 0 | { |
1994 | 0 | String buf; |
1995 | 0 | p->release(&buf); |
1996 | 0 | return buf; |
1997 | 0 | } |
1998 | | |
1999 | | void FileStorage::writeRaw( const String& fmt, const void* vec, size_t len ) |
2000 | 0 | { |
2001 | 0 | p->writeRawData(fmt, (const uchar*)vec, len); |
2002 | 0 | } |
2003 | | |
2004 | | void FileStorage::writeComment( const String& comment, bool eol_comment ) |
2005 | 0 | { |
2006 | 0 | p->writeComment(comment.c_str(), eol_comment); |
2007 | 0 | } |
2008 | | |
2009 | | void writeScalar( FileStorage& fs, int value ) |
2010 | 0 | { |
2011 | 0 | fs.p->write(String(), value); |
2012 | 0 | } |
2013 | | |
2014 | | void writeScalar( FileStorage& fs, float value ) |
2015 | 0 | { |
2016 | 0 | fs.p->write(String(), (double)value); |
2017 | 0 | } |
2018 | | |
2019 | | void writeScalar( FileStorage& fs, double value ) |
2020 | 0 | { |
2021 | 0 | fs.p->write(String(), value); |
2022 | 0 | } |
2023 | | |
2024 | | void writeScalar( FileStorage& fs, const String& value ) |
2025 | 0 | { |
2026 | 0 | fs.p->write(String(), value); |
2027 | 0 | } |
2028 | | |
2029 | | void write( FileStorage& fs, const String& name, int value ) |
2030 | 0 | { |
2031 | 0 | fs.p->write(name, value); |
2032 | 0 | } |
2033 | | |
2034 | | void write( FileStorage& fs, const String& name, float value ) |
2035 | 0 | { |
2036 | 0 | fs.p->write(name, (double)value); |
2037 | 0 | } |
2038 | | |
2039 | | void write( FileStorage& fs, const String& name, double value ) |
2040 | 0 | { |
2041 | 0 | fs.p->write(name, value); |
2042 | 0 | } |
2043 | | |
2044 | | void write( FileStorage& fs, const String& name, const String& value ) |
2045 | 0 | { |
2046 | 0 | fs.p->write(name, value); |
2047 | 0 | } |
2048 | | |
2049 | 0 | void FileStorage::write(const String& name, int val) { p->write(name, val); } |
2050 | 0 | void FileStorage::write(const String& name, double val) { p->write(name, val); } |
2051 | 0 | void FileStorage::write(const String& name, const String& val) { p->write(name, val); } |
2052 | 0 | void FileStorage::write(const String& name, const Mat& val) { cv::write(*this, name, val); } |
2053 | 0 | void FileStorage::write(const String& name, const std::vector<String>& val) { cv::write(*this, name, val); } |
2054 | | |
2055 | | FileStorage& operator << (FileStorage& fs, const String& str) |
2056 | 0 | { |
2057 | 0 | enum { NAME_EXPECTED = FileStorage::NAME_EXPECTED, |
2058 | 0 | VALUE_EXPECTED = FileStorage::VALUE_EXPECTED, |
2059 | 0 | INSIDE_MAP = FileStorage::INSIDE_MAP }; |
2060 | 0 | const char* _str = str.c_str(); |
2061 | 0 | if( !fs.isOpened() || !_str ) |
2062 | 0 | return fs; |
2063 | 0 | Ptr<FileStorage::Impl>& fs_impl = fs.p; |
2064 | 0 | char c = *_str; |
2065 | |
|
2066 | 0 | if( c == '}' || c == ']' ) |
2067 | 0 | { |
2068 | 0 | if( fs_impl->write_stack.empty() ) |
2069 | 0 | CV_Error_( cv::Error::StsError, ("Extra closing '%c'", *_str) ); |
2070 | | |
2071 | 0 | fs_impl->workaround(); |
2072 | |
|
2073 | 0 | int struct_flags = fs_impl->write_stack.back().flags; |
2074 | 0 | char expected_bracket = FileNode::isMap(struct_flags) ? '}' : ']'; |
2075 | 0 | if( c != expected_bracket ) |
2076 | 0 | CV_Error_( cv::Error::StsError, ("The closing '%c' does not match the opening '%c'", c, expected_bracket)); |
2077 | 0 | fs_impl->endWriteStruct(); |
2078 | 0 | CV_Assert(!fs_impl->write_stack.empty()); |
2079 | 0 | struct_flags = fs_impl->write_stack.back().flags; |
2080 | 0 | fs.state = FileNode::isMap(struct_flags) ? INSIDE_MAP + NAME_EXPECTED : VALUE_EXPECTED; |
2081 | 0 | fs.elname = String(); |
2082 | 0 | } |
2083 | 0 | else if( fs.state == NAME_EXPECTED + INSIDE_MAP ) |
2084 | 0 | { |
2085 | 0 | if (!cv_isalpha(c) && c != '_') |
2086 | 0 | CV_Error_( cv::Error::StsError, ("Incorrect element name %s; should start with a letter or '_'", _str) ); |
2087 | 0 | fs.elname = str; |
2088 | 0 | fs.state = VALUE_EXPECTED + INSIDE_MAP; |
2089 | 0 | } |
2090 | 0 | else if( (fs.state & 3) == VALUE_EXPECTED ) |
2091 | 0 | { |
2092 | 0 | if( c == '{' || c == '[' ) |
2093 | 0 | { |
2094 | 0 | int struct_flags = c == '{' ? FileNode::MAP : FileNode::SEQ; |
2095 | 0 | fs.state = struct_flags == FileNode::MAP ? INSIDE_MAP + NAME_EXPECTED : VALUE_EXPECTED; |
2096 | 0 | _str++; |
2097 | 0 | if( *_str == ':' ) |
2098 | 0 | { |
2099 | 0 | _str++; |
2100 | 0 | if( !*_str ) |
2101 | 0 | struct_flags |= FileNode::FLOW; |
2102 | 0 | } |
2103 | 0 | fs_impl->startWriteStruct(!fs.elname.empty() ? fs.elname.c_str() : 0, struct_flags, *_str ? _str : 0 ); |
2104 | 0 | fs.elname = String(); |
2105 | 0 | } |
2106 | 0 | else |
2107 | 0 | { |
2108 | 0 | write( fs, fs.elname, (c == '\\' && (_str[1] == '{' || _str[1] == '}' || |
2109 | 0 | _str[1] == '[' || _str[1] == ']')) ? String(_str+1) : str ); |
2110 | 0 | if( fs.state == INSIDE_MAP + VALUE_EXPECTED ) |
2111 | 0 | fs.state = INSIDE_MAP + NAME_EXPECTED; |
2112 | 0 | } |
2113 | 0 | } |
2114 | 0 | else |
2115 | 0 | CV_Error( cv::Error::StsError, "Invalid fs.state" ); |
2116 | 0 | return fs; |
2117 | 0 | } |
2118 | | |
2119 | | |
2120 | | FileNode::FileNode() |
2121 | | : fs(NULL) |
2122 | 0 | { |
2123 | 0 | blockIdx = ofs = 0; |
2124 | 0 | } |
2125 | | |
2126 | | FileNode::FileNode(FileStorage::Impl* _fs, size_t _blockIdx, size_t _ofs) |
2127 | | : fs(_fs) |
2128 | 0 | { |
2129 | 0 | blockIdx = _blockIdx; |
2130 | 0 | ofs = _ofs; |
2131 | 0 | } |
2132 | | |
2133 | | FileNode::FileNode(const FileStorage* _fs, size_t _blockIdx, size_t _ofs) |
2134 | | : FileNode(_fs->p.get(), _blockIdx, _ofs) |
2135 | 0 | { |
2136 | | // nothing |
2137 | 0 | } |
2138 | | |
2139 | | FileNode::FileNode(const FileNode& node) |
2140 | 0 | { |
2141 | 0 | fs = node.fs; |
2142 | 0 | blockIdx = node.blockIdx; |
2143 | 0 | ofs = node.ofs; |
2144 | 0 | } |
2145 | | |
2146 | | FileNode& FileNode::operator=(const FileNode& node) |
2147 | 0 | { |
2148 | 0 | fs = node.fs; |
2149 | 0 | blockIdx = node.blockIdx; |
2150 | 0 | ofs = node.ofs; |
2151 | 0 | return *this; |
2152 | 0 | } |
2153 | | |
2154 | | FileNode FileNode::operator[](const std::string& nodename) const |
2155 | 0 | { |
2156 | 0 | if(!fs) |
2157 | 0 | return FileNode(); |
2158 | | |
2159 | 0 | CV_Assert( isMap() ); |
2160 | | |
2161 | 0 | unsigned key = fs->getStringOfs(nodename); |
2162 | 0 | size_t i, sz = size(); |
2163 | 0 | FileNodeIterator it = begin(); |
2164 | |
|
2165 | 0 | for( i = 0; i < sz; i++, ++it ) |
2166 | 0 | { |
2167 | 0 | FileNode n = *it; |
2168 | 0 | const uchar* p = n.ptr(); |
2169 | 0 | unsigned key2 = (unsigned)readInt(p + 1); |
2170 | 0 | CV_Assert( key2 < fs->str_hash_data.size() ); |
2171 | 0 | if( key == key2 ) |
2172 | 0 | return n; |
2173 | 0 | } |
2174 | 0 | return FileNode(); |
2175 | 0 | } |
2176 | | |
2177 | | FileNode FileNode::operator[](const char* nodename) const |
2178 | 0 | { |
2179 | 0 | return this->operator[](std::string(nodename)); |
2180 | 0 | } |
2181 | | |
2182 | | FileNode FileNode::operator[](int i) const |
2183 | 0 | { |
2184 | 0 | if(!fs) |
2185 | 0 | return FileNode(); |
2186 | | |
2187 | 0 | CV_Assert( isSeq() ); |
2188 | | |
2189 | 0 | int sz = (int)size(); |
2190 | 0 | CV_Assert( 0 <= i && i < sz ); |
2191 | | |
2192 | 0 | FileNodeIterator it = begin(); |
2193 | 0 | it += i; |
2194 | |
|
2195 | 0 | return *it; |
2196 | 0 | } |
2197 | | |
2198 | | std::vector<String> FileNode::keys() const |
2199 | 0 | { |
2200 | 0 | CV_Assert(isMap()); |
2201 | | |
2202 | 0 | std::vector<String> res; |
2203 | 0 | res.reserve(size()); |
2204 | 0 | for (FileNodeIterator it = begin(); it != end(); ++it) |
2205 | 0 | { |
2206 | 0 | res.push_back((*it).name()); |
2207 | 0 | } |
2208 | 0 | return res; |
2209 | 0 | } |
2210 | | |
2211 | | int FileNode::type() const |
2212 | 0 | { |
2213 | 0 | const uchar* p = ptr(); |
2214 | 0 | if(!p) |
2215 | 0 | return NONE; |
2216 | 0 | return (*p & TYPE_MASK); |
2217 | 0 | } |
2218 | | |
2219 | 0 | bool FileNode::isMap(int flags) { return (flags & TYPE_MASK) == MAP; } |
2220 | 0 | bool FileNode::isSeq(int flags) { return (flags & TYPE_MASK) == SEQ; } |
2221 | 0 | bool FileNode::isCollection(int flags) { return isMap(flags) || isSeq(flags); } |
2222 | 0 | bool FileNode::isFlow(int flags) { return (flags & FLOW) != 0; } |
2223 | 0 | bool FileNode::isEmptyCollection(int flags) { return (flags & EMPTY) != 0; } |
2224 | | |
2225 | 0 | bool FileNode::empty() const { return fs == 0; } |
2226 | 0 | bool FileNode::isNone() const { return type() == NONE; } |
2227 | 0 | bool FileNode::isSeq() const { return type() == SEQ; } |
2228 | 0 | bool FileNode::isMap() const { return type() == MAP; } |
2229 | 0 | bool FileNode::isInt() const { return type() == INT; } |
2230 | 0 | bool FileNode::isReal() const { return type() == REAL; } |
2231 | 0 | bool FileNode::isString() const { return type() == STRING; } |
2232 | | bool FileNode::isNamed() const |
2233 | 0 | { |
2234 | 0 | const uchar* p = ptr(); |
2235 | 0 | if(!p) |
2236 | 0 | return false; |
2237 | 0 | return (*p & NAMED) != 0; |
2238 | 0 | } |
2239 | | |
2240 | | std::string FileNode::name() const |
2241 | 0 | { |
2242 | 0 | const uchar* p = ptr(); |
2243 | 0 | if(!p) |
2244 | 0 | return std::string(); |
2245 | 0 | size_t nameofs = p[1] | (p[2]<<8) | (p[3]<<16) | (p[4]<<24); |
2246 | 0 | return fs->getName(nameofs); |
2247 | 0 | } |
2248 | | |
2249 | | FileNode::operator int() const |
2250 | 0 | { |
2251 | 0 | const uchar* p = ptr(); |
2252 | 0 | if(!p) |
2253 | 0 | return 0; |
2254 | 0 | int tag = *p; |
2255 | 0 | int type = (tag & TYPE_MASK); |
2256 | 0 | p += (tag & NAMED) ? 5 : 1; |
2257 | |
|
2258 | 0 | if( type == INT ) |
2259 | 0 | { |
2260 | 0 | return readInt(p); |
2261 | 0 | } |
2262 | 0 | else if( type == REAL ) |
2263 | 0 | { |
2264 | 0 | return cvRound(readReal(p)); |
2265 | 0 | } |
2266 | 0 | else |
2267 | 0 | return 0x7fffffff; |
2268 | 0 | } |
2269 | | |
2270 | | FileNode::operator float() const |
2271 | 0 | { |
2272 | 0 | const uchar* p = ptr(); |
2273 | 0 | if(!p) |
2274 | 0 | return 0.f; |
2275 | 0 | int tag = *p; |
2276 | 0 | int type = (tag & TYPE_MASK); |
2277 | 0 | p += (tag & NAMED) ? 5 : 1; |
2278 | |
|
2279 | 0 | if( type == INT ) |
2280 | 0 | { |
2281 | 0 | return (float)readInt(p); |
2282 | 0 | } |
2283 | 0 | else if( type == REAL ) |
2284 | 0 | { |
2285 | 0 | return (float)readReal(p); |
2286 | 0 | } |
2287 | 0 | else |
2288 | 0 | return FLT_MAX; |
2289 | 0 | } |
2290 | | |
2291 | | FileNode::operator double() const |
2292 | 0 | { |
2293 | 0 | const uchar* p = ptr(); |
2294 | 0 | if(!p) |
2295 | 0 | return 0.f; |
2296 | 0 | int tag = *p; |
2297 | 0 | int type = (tag & TYPE_MASK); |
2298 | 0 | p += (tag & NAMED) ? 5 : 1; |
2299 | |
|
2300 | 0 | if( type == INT ) |
2301 | 0 | { |
2302 | 0 | return (double)readInt(p); |
2303 | 0 | } |
2304 | 0 | else if( type == REAL ) |
2305 | 0 | { |
2306 | 0 | return readReal(p); |
2307 | 0 | } |
2308 | 0 | else |
2309 | 0 | return DBL_MAX; |
2310 | 0 | } |
2311 | | |
2312 | 0 | double FileNode::real() const { return double(*this); } |
2313 | | std::string FileNode::string() const |
2314 | 0 | { |
2315 | 0 | const uchar* p = ptr(); |
2316 | 0 | if( !p || (*p & TYPE_MASK) != STRING ) |
2317 | 0 | return std::string(); |
2318 | 0 | p += (*p & NAMED) ? 5 : 1; |
2319 | 0 | size_t sz = (size_t)(unsigned)readInt(p); |
2320 | 0 | return std::string((const char*)(p + 4), sz - 1); |
2321 | 0 | } |
2322 | 0 | Mat FileNode::mat() const { Mat value; read(*this, value, Mat()); return value; } |
2323 | | |
2324 | 0 | FileNodeIterator FileNode::begin() const { return FileNodeIterator(*this, false); } |
2325 | 0 | FileNodeIterator FileNode::end() const { return FileNodeIterator(*this, true); } |
2326 | | |
2327 | | void FileNode::readRaw( const std::string& fmt, void* vec, size_t len ) const |
2328 | 0 | { |
2329 | 0 | FileNodeIterator it = begin(); |
2330 | 0 | it.readRaw( fmt, vec, len ); |
2331 | 0 | } |
2332 | | |
2333 | | size_t FileNode::size() const |
2334 | 0 | { |
2335 | 0 | const uchar* p = ptr(); |
2336 | 0 | if( !p ) |
2337 | 0 | return 0; |
2338 | 0 | int tag = *p; |
2339 | 0 | int tp = tag & TYPE_MASK; |
2340 | 0 | if( tp == MAP || tp == SEQ ) |
2341 | 0 | { |
2342 | 0 | if( tag & NAMED ) |
2343 | 0 | p += 4; |
2344 | 0 | return (size_t)(unsigned)readInt(p + 5); |
2345 | 0 | } |
2346 | 0 | return tp != NONE; |
2347 | 0 | } |
2348 | | |
2349 | | size_t FileNode::rawSize() const |
2350 | 0 | { |
2351 | 0 | const uchar* p0 = ptr(), *p = p0; |
2352 | 0 | if( !p ) |
2353 | 0 | return 0; |
2354 | 0 | int tag = *p++; |
2355 | 0 | int tp = tag & TYPE_MASK; |
2356 | 0 | if( tag & NAMED ) |
2357 | 0 | p += 4; |
2358 | 0 | size_t sz0 = (size_t)(p - p0); |
2359 | 0 | if( tp == INT ) |
2360 | 0 | return sz0 + 4; |
2361 | 0 | if( tp == REAL ) |
2362 | 0 | return sz0 + 8; |
2363 | 0 | if( tp == NONE ) |
2364 | 0 | return sz0; |
2365 | 0 | CV_Assert( tp == STRING || tp == SEQ || tp == MAP ); |
2366 | 0 | return sz0 + 4 + readInt(p); |
2367 | 0 | } |
2368 | | |
2369 | | uchar* FileNode::ptr() |
2370 | 0 | { |
2371 | 0 | return !fs ? 0 : (uchar*)fs->getNodePtr(blockIdx, ofs); |
2372 | 0 | } |
2373 | | |
2374 | | const uchar* FileNode::ptr() const |
2375 | 0 | { |
2376 | 0 | return !fs ? 0 : fs->getNodePtr(blockIdx, ofs); |
2377 | 0 | } |
2378 | | |
2379 | | void FileNode::setValue( int type, const void* value, int len ) |
2380 | 0 | { |
2381 | 0 | uchar *p = ptr(); |
2382 | 0 | CV_Assert(p != 0); |
2383 | | |
2384 | 0 | int tag = *p; |
2385 | 0 | int current_type = tag & TYPE_MASK; |
2386 | 0 | CV_Assert( current_type == NONE || current_type == type ); |
2387 | | |
2388 | 0 | int sz = 1; |
2389 | |
|
2390 | 0 | if( tag & NAMED ) |
2391 | 0 | sz += 4; |
2392 | |
|
2393 | 0 | if( type == INT ) |
2394 | 0 | sz += 4; |
2395 | 0 | else if( type == REAL ) |
2396 | 0 | sz += 8; |
2397 | 0 | else if( type == STRING ) |
2398 | 0 | { |
2399 | 0 | if( len < 0 ) |
2400 | 0 | len = (int)strlen((const char*)value); |
2401 | 0 | sz += 4 + len + 1; // besides the string content, |
2402 | | // take the size (4 bytes) and the final '\0' into account |
2403 | 0 | } |
2404 | 0 | else |
2405 | 0 | CV_Error(Error::StsNotImplemented, "Only scalar types can be dynamically assigned to a file node"); |
2406 | | |
2407 | 0 | p = fs->reserveNodeSpace(*this, sz); |
2408 | 0 | *p++ = (uchar)(type | (tag & NAMED)); |
2409 | 0 | if( tag & NAMED ) |
2410 | 0 | p += 4; |
2411 | |
|
2412 | 0 | if( type == INT ) |
2413 | 0 | { |
2414 | 0 | int ival = *(const int*)value; |
2415 | 0 | writeInt(p, ival); |
2416 | 0 | } |
2417 | 0 | else if( type == REAL ) |
2418 | 0 | { |
2419 | 0 | double dbval = *(const double*)value; |
2420 | 0 | writeReal(p, dbval); |
2421 | 0 | } |
2422 | 0 | else if( type == STRING ) |
2423 | 0 | { |
2424 | 0 | const char* str = (const char*)value; |
2425 | 0 | writeInt(p, len + 1); |
2426 | 0 | memcpy(p + 4, str, len); |
2427 | 0 | p[4 + len] = (uchar)'\0'; |
2428 | 0 | } |
2429 | 0 | } |
2430 | | |
2431 | | FileNodeIterator::FileNodeIterator() |
2432 | 0 | { |
2433 | 0 | fs = 0; |
2434 | 0 | blockIdx = 0; |
2435 | 0 | ofs = 0; |
2436 | 0 | blockSize = 0; |
2437 | 0 | nodeNElems = 0; |
2438 | 0 | idx = 0; |
2439 | 0 | } |
2440 | | |
2441 | | FileNodeIterator::FileNodeIterator( const FileNode& node, bool seekEnd ) |
2442 | 0 | { |
2443 | 0 | fs = node.fs; |
2444 | 0 | idx = 0; |
2445 | 0 | if( !fs ) |
2446 | 0 | blockIdx = ofs = blockSize = nodeNElems = 0; |
2447 | 0 | else |
2448 | 0 | { |
2449 | 0 | blockIdx = node.blockIdx; |
2450 | 0 | ofs = node.ofs; |
2451 | |
|
2452 | 0 | bool collection = node.isSeq() || node.isMap(); |
2453 | 0 | if( node.isNone() ) |
2454 | 0 | { |
2455 | 0 | nodeNElems = 0; |
2456 | 0 | } |
2457 | 0 | else if( !collection ) |
2458 | 0 | { |
2459 | 0 | nodeNElems = 1; |
2460 | 0 | if( seekEnd ) |
2461 | 0 | { |
2462 | 0 | idx = 1; |
2463 | 0 | ofs += node.rawSize(); |
2464 | 0 | } |
2465 | 0 | } |
2466 | 0 | else |
2467 | 0 | { |
2468 | 0 | nodeNElems = node.size(); |
2469 | 0 | const uchar* p0 = node.ptr(), *p = p0 + 1; |
2470 | 0 | if(*p0 & FileNode::NAMED ) |
2471 | 0 | p += 4; |
2472 | 0 | if( !seekEnd ) |
2473 | 0 | ofs += (p - p0) + 8; |
2474 | 0 | else |
2475 | 0 | { |
2476 | 0 | size_t rawsz = (size_t)(unsigned)readInt(p); |
2477 | 0 | ofs += (p - p0) + 4 + rawsz; |
2478 | 0 | idx = nodeNElems; |
2479 | 0 | } |
2480 | 0 | } |
2481 | 0 | fs->normalizeNodeOfs(blockIdx, ofs); |
2482 | 0 | blockSize = fs->fs_data_blksz[blockIdx]; |
2483 | 0 | } |
2484 | 0 | } |
2485 | | |
2486 | | FileNodeIterator::FileNodeIterator(const FileNodeIterator& it) |
2487 | 0 | { |
2488 | 0 | fs = it.fs; |
2489 | 0 | blockIdx = it.blockIdx; |
2490 | 0 | ofs = it.ofs; |
2491 | 0 | blockSize = it.blockSize; |
2492 | 0 | nodeNElems = it.nodeNElems; |
2493 | 0 | idx = it.idx; |
2494 | 0 | } |
2495 | | |
2496 | | FileNodeIterator& FileNodeIterator::operator=(const FileNodeIterator& it) |
2497 | 0 | { |
2498 | 0 | fs = it.fs; |
2499 | 0 | blockIdx = it.blockIdx; |
2500 | 0 | ofs = it.ofs; |
2501 | 0 | blockSize = it.blockSize; |
2502 | 0 | nodeNElems = it.nodeNElems; |
2503 | 0 | idx = it.idx; |
2504 | 0 | return *this; |
2505 | 0 | } |
2506 | | |
2507 | | FileNode FileNodeIterator::operator *() const |
2508 | 0 | { |
2509 | 0 | return FileNode(idx < nodeNElems ? fs : NULL, blockIdx, ofs); |
2510 | 0 | } |
2511 | | |
2512 | | FileNodeIterator& FileNodeIterator::operator ++ () |
2513 | 0 | { |
2514 | 0 | if( idx == nodeNElems || !fs ) |
2515 | 0 | return *this; |
2516 | 0 | idx++; |
2517 | 0 | FileNode n(fs, blockIdx, ofs); |
2518 | 0 | ofs += n.rawSize(); |
2519 | 0 | if( ofs >= blockSize ) |
2520 | 0 | { |
2521 | 0 | fs->normalizeNodeOfs(blockIdx, ofs); |
2522 | 0 | blockSize = fs->fs_data_blksz[blockIdx]; |
2523 | 0 | } |
2524 | 0 | return *this; |
2525 | 0 | } |
2526 | | |
2527 | | FileNodeIterator FileNodeIterator::operator ++ (int) |
2528 | 0 | { |
2529 | 0 | FileNodeIterator it = *this; |
2530 | 0 | ++(*this); |
2531 | 0 | return it; |
2532 | 0 | } |
2533 | | |
2534 | | FileNodeIterator& FileNodeIterator::operator += (int _ofs) |
2535 | 0 | { |
2536 | 0 | CV_Assert( _ofs >= 0 ); |
2537 | 0 | for( ; _ofs > 0; _ofs-- ) |
2538 | 0 | this->operator ++(); |
2539 | 0 | return *this; |
2540 | 0 | } |
2541 | | |
2542 | | FileNodeIterator& FileNodeIterator::readRaw( const String& fmt, void* _data0, size_t maxsz) |
2543 | 0 | { |
2544 | 0 | if( fs && idx < nodeNElems ) |
2545 | 0 | { |
2546 | 0 | uchar* data0 = (uchar*)_data0; |
2547 | 0 | int fmt_pairs[CV_FS_MAX_FMT_PAIRS*2]; |
2548 | 0 | int fmt_pair_count = fs::decodeFormat( fmt.c_str(), fmt_pairs, CV_FS_MAX_FMT_PAIRS ); |
2549 | 0 | size_t esz = fs::calcStructSize( fmt.c_str(), 0 ); |
2550 | |
|
2551 | 0 | CV_Assert( maxsz % esz == 0 ); |
2552 | 0 | maxsz /= esz; |
2553 | |
|
2554 | 0 | for( ; maxsz > 0; maxsz--, data0 += esz ) |
2555 | 0 | { |
2556 | 0 | size_t offset = 0; |
2557 | 0 | for( int k = 0; k < fmt_pair_count; k++ ) |
2558 | 0 | { |
2559 | 0 | int elem_type = fmt_pairs[k*2+1]; |
2560 | 0 | int elem_size = CV_ELEM_SIZE(elem_type); |
2561 | |
|
2562 | 0 | int count = fmt_pairs[k*2]; |
2563 | 0 | offset = alignSize( offset, elem_size ); |
2564 | 0 | uchar* data = data0 + offset; |
2565 | |
|
2566 | 0 | for( int i = 0; i < count; i++, ++(*this) ) |
2567 | 0 | { |
2568 | 0 | FileNode node = *(*this); |
2569 | 0 | if( node.isInt() ) |
2570 | 0 | { |
2571 | 0 | int ival = (int)node; |
2572 | 0 | switch( elem_type ) |
2573 | 0 | { |
2574 | 0 | case CV_8U: |
2575 | 0 | *(uchar*)data = saturate_cast<uchar>(ival); |
2576 | 0 | data++; |
2577 | 0 | break; |
2578 | 0 | case CV_8S: |
2579 | 0 | *(char*)data = saturate_cast<schar>(ival); |
2580 | 0 | data++; |
2581 | 0 | break; |
2582 | 0 | case CV_16U: |
2583 | 0 | *(ushort*)data = saturate_cast<ushort>(ival); |
2584 | 0 | data += sizeof(ushort); |
2585 | 0 | break; |
2586 | 0 | case CV_16S: |
2587 | 0 | *(short*)data = saturate_cast<short>(ival); |
2588 | 0 | data += sizeof(short); |
2589 | 0 | break; |
2590 | 0 | case CV_32S: |
2591 | 0 | *(int*)data = ival; |
2592 | 0 | data += sizeof(int); |
2593 | 0 | break; |
2594 | 0 | case CV_32F: |
2595 | 0 | *(float*)data = (float)ival; |
2596 | 0 | data += sizeof(float); |
2597 | 0 | break; |
2598 | 0 | case CV_64F: |
2599 | 0 | *(double*)data = (double)ival; |
2600 | 0 | data += sizeof(double); |
2601 | 0 | break; |
2602 | 0 | case CV_16F: |
2603 | 0 | *(float16_t*)data = float16_t((float)ival); |
2604 | 0 | data += sizeof(float16_t); |
2605 | 0 | break; |
2606 | 0 | default: |
2607 | 0 | CV_Error( Error::StsUnsupportedFormat, "Unsupported type" ); |
2608 | 0 | } |
2609 | 0 | } |
2610 | 0 | else if( node.isReal() ) |
2611 | 0 | { |
2612 | 0 | double fval = (double)node; |
2613 | |
|
2614 | 0 | switch( elem_type ) |
2615 | 0 | { |
2616 | 0 | case CV_8U: |
2617 | 0 | *(uchar*)data = saturate_cast<uchar>(fval); |
2618 | 0 | data++; |
2619 | 0 | break; |
2620 | 0 | case CV_8S: |
2621 | 0 | *(char*)data = saturate_cast<schar>(fval); |
2622 | 0 | data++; |
2623 | 0 | break; |
2624 | 0 | case CV_16U: |
2625 | 0 | *(ushort*)data = saturate_cast<ushort>(fval); |
2626 | 0 | data += sizeof(ushort); |
2627 | 0 | break; |
2628 | 0 | case CV_16S: |
2629 | 0 | *(short*)data = saturate_cast<short>(fval); |
2630 | 0 | data += sizeof(short); |
2631 | 0 | break; |
2632 | 0 | case CV_32S: |
2633 | 0 | *(int*)data = saturate_cast<int>(fval); |
2634 | 0 | data += sizeof(int); |
2635 | 0 | break; |
2636 | 0 | case CV_32F: |
2637 | 0 | *(float*)data = (float)fval; |
2638 | 0 | data += sizeof(float); |
2639 | 0 | break; |
2640 | 0 | case CV_64F: |
2641 | 0 | *(double*)data = fval; |
2642 | 0 | data += sizeof(double); |
2643 | 0 | break; |
2644 | 0 | case CV_16F: |
2645 | 0 | *(float16_t*)data = float16_t((float)fval); |
2646 | 0 | data += sizeof(float16_t); |
2647 | 0 | break; |
2648 | 0 | default: |
2649 | 0 | CV_Error( Error::StsUnsupportedFormat, "Unsupported type" ); |
2650 | 0 | } |
2651 | 0 | } |
2652 | 0 | else |
2653 | 0 | CV_Error( Error::StsError, "readRawData can only be used to read plain sequences of numbers" ); |
2654 | 0 | } |
2655 | 0 | offset = (int)(data - data0); |
2656 | 0 | } |
2657 | 0 | } |
2658 | 0 | } |
2659 | | |
2660 | 0 | return *this; |
2661 | 0 | } |
2662 | | |
2663 | | bool FileNodeIterator::equalTo(const FileNodeIterator& it) const |
2664 | 0 | { |
2665 | 0 | return fs == it.fs && blockIdx == it.blockIdx && ofs == it.ofs && |
2666 | 0 | idx == it.idx && nodeNElems == it.nodeNElems; |
2667 | 0 | } |
2668 | | |
2669 | | size_t FileNodeIterator::remaining() const |
2670 | 0 | { |
2671 | 0 | return nodeNElems - idx; |
2672 | 0 | } |
2673 | | |
2674 | | bool operator == ( const FileNodeIterator& it1, const FileNodeIterator& it2 ) |
2675 | 0 | { |
2676 | 0 | return it1.equalTo(it2); |
2677 | 0 | } |
2678 | | |
2679 | | bool operator != ( const FileNodeIterator& it1, const FileNodeIterator& it2 ) |
2680 | 0 | { |
2681 | 0 | return !it1.equalTo(it2); |
2682 | 0 | } |
2683 | | |
2684 | | void read(const FileNode& node, int& val, int default_val) |
2685 | 0 | { |
2686 | 0 | val = default_val; |
2687 | 0 | if( !node.empty() ) |
2688 | 0 | { |
2689 | 0 | val = (int)node; |
2690 | 0 | } |
2691 | 0 | } |
2692 | | |
2693 | | void read(const FileNode& node, double& val, double default_val) |
2694 | 0 | { |
2695 | 0 | val = default_val; |
2696 | 0 | if( !node.empty() ) |
2697 | 0 | { |
2698 | 0 | val = (double)node; |
2699 | 0 | } |
2700 | 0 | } |
2701 | | |
2702 | | void read(const FileNode& node, float& val, float default_val) |
2703 | 0 | { |
2704 | 0 | val = default_val; |
2705 | 0 | if( !node.empty() ) |
2706 | 0 | { |
2707 | 0 | val = (float)node; |
2708 | 0 | } |
2709 | 0 | } |
2710 | | |
2711 | | void read(const FileNode& node, std::string& val, const std::string& default_val) |
2712 | 0 | { |
2713 | 0 | val = default_val; |
2714 | 0 | if( !node.empty() ) |
2715 | 0 | { |
2716 | 0 | val = (std::string)node; |
2717 | 0 | } |
2718 | 0 | } |
2719 | | |
2720 | 995 | FileStorage_API::~FileStorage_API() {} |
2721 | | |
2722 | | namespace internal |
2723 | | { |
2724 | | |
2725 | | WriteStructContext::WriteStructContext(FileStorage& _fs, const std::string& name, |
2726 | | int flags, const std::string& typeName) |
2727 | 0 | { |
2728 | 0 | fs = &_fs; |
2729 | 0 | fs->startWriteStruct(name, flags, typeName); |
2730 | 0 | } |
2731 | | |
2732 | | WriteStructContext::~WriteStructContext() |
2733 | 0 | { |
2734 | 0 | fs->endWriteStruct(); |
2735 | 0 | } |
2736 | | |
2737 | | } |
2738 | | |
2739 | | } |