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