1
2
3 #include "tstream.h"
4 #include "tpersist.h"
5 #include "tfilepath_io.h"
6 #include "tconvert.h"
7 #include "tsystem.h"
8 #include "tutil.h"
9
10 #if defined(LZ4_STATIC)
11 #include "lz4frame_static.h"
12 #else
13 #include "lz4frame.h"
14 #endif
15
16 #include <sstream>
17 #include <memory>
18
19 using namespace std;
20
21 //===============================================================
22 namespace {
23
escape(string v)24 string escape(string v) {
25 int i = 0;
26 for (;;) {
27 // Removing escaping of apostrophe from Windows and OSX as it's not needed and
28 // causes problems
29 #if defined(LINUX) || defined(FREEBSD)
30 i = v.find_first_of("\\\'\"", i);
31 #else
32 i = v.find_first_of("\\\"", i);
33 #endif
34 if (i == (int)string::npos) break;
35 string h = "\\" + v[i];
36 v.insert(i, "\\");
37 i = i + 2;
38 }
39 return v;
40 }
41
42 //===============================================================
43
writeCompressedFile(TFilePath dst,const std::string & str)44 void writeCompressedFile(TFilePath dst, const std::string &str) {}
45
46 //===================================================================
47
readCompressedFile(string & str,TFilePath src)48 void readCompressedFile(string &str, TFilePath src) {
49 TFileStatus status(src);
50 if (!status.doesExist()) return;
51
52 size_t in_len = status.getSize();
53 char *in = (char *)malloc(in_len);
54 {
55 Tifstream is(src);
56 is.read((char *)in, in_len);
57 }
58
59 LZ4F_decompressionContext_t lz4dctx;
60
61 LZ4F_errorCode_t err =
62 LZ4F_createDecompressionContext(&lz4dctx, LZ4F_VERSION);
63 if (LZ4F_isError(err)) return;
64
65 size_t in_len_read = 0;
66
67 size_t out_len = 1000000, out_len_written, out_len_moved = 0;
68 void *out = (void *)malloc(out_len);
69
70 while (in_len_read < in_len) {
71 out_len_written = out_len;
72
73 size_t remaining =
74 LZ4F_decompress(lz4dctx, out, &out_len_written, in, &in_len, NULL);
75 if (LZ4F_isError(remaining)) break;
76
77 str.resize(out_len_moved + out_len_written);
78
79 memcpy((void *)(str.c_str() + out_len_moved), (void *)out, out_len_written);
80 out_len_moved += out_len_written;
81 }
82
83 LZ4F_freeDecompressionContext(lz4dctx);
84
85 free(in);
86 free(out);
87 }
88
89 namespace {
90 // TODO: Unify with tcodec.cpp's version
lz4decompress(LZ4F_decompressionContext_t lz4dctx,char * out,size_t * out_len_res,const char * in,size_t in_len)91 bool lz4decompress(LZ4F_decompressionContext_t lz4dctx, char *out,
92 size_t *out_len_res, const char *in, size_t in_len) {
93 size_t out_len = *out_len_res, in_read, out_written;
94
95 *out_len_res = 0;
96
97 while (in_len) {
98 out_written = out_len;
99 in_read = in_len;
100
101 size_t res = LZ4F_decompress(lz4dctx, (void *)out, &out_written,
102 (const void *)in, &in_read, NULL);
103
104 if (LZ4F_isError(res)) return false;
105
106 *out_len_res += out_written;
107
108 out += out_written;
109 out_len -= out_written;
110
111 in += in_read;
112 in_len -= in_read;
113 }
114
115 return true;
116 }
117 } // namespace
118
119 //===============================================================
120
121 class StreamTag {
122 public:
123 string m_name;
124 std::map<std::string, string> m_attributes;
125 enum Type { BeginTag, EndTag, BeginEndTag };
126 Type m_type;
StreamTag()127 StreamTag() : m_type(BeginTag) {}
128
operator bool() const129 operator bool() const { return m_name != ""; }
130
dump()131 void dump() {
132 cout << "name = '" << m_name << "'" << endl;
133 cout << "type = ";
134 switch (m_type) {
135 case BeginTag:
136 cout << "begin Tag";
137 break;
138 case EndTag:
139 cout << "end Tag";
140 break;
141 case BeginEndTag:
142 cout << "begin/end Tag";
143 break;
144 default:
145 cout << "**bad Tag**";
146 break;
147 }
148 cout << endl;
149 std::map<std::string, string>::iterator it;
150 for (it = m_attributes.begin(); it != m_attributes.end(); ++it) {
151 cout << " '" << it->first << "' = '" << it->second << "'" << endl;
152 }
153 }
154 };
155
156 //--------------------------------
157
158 class TPersistFactory {
159 typedef std::map<std::string, TPersistDeclaration *> Table;
160 static TPersistFactory *m_factory;
161 Table m_table;
TPersistFactory()162 TPersistFactory() {}
163
164 public:
instance()165 static TPersistFactory *instance() {
166 if (!m_factory) m_factory = new TPersistFactory;
167 return m_factory;
168 }
add(string name,TPersistDeclaration * decl)169 void add(string name, TPersistDeclaration *decl) { m_table[name] = decl; }
create(string name)170 TPersist *create(string name) {
171 Table::iterator it = m_table.find(name);
172 if (it != m_table.end())
173 return (it->second)->create();
174 else
175 return 0;
176 }
177 };
178
179 //--------------------------------
180
181 TPersistFactory *TPersistFactory::m_factory = 0;
182
183 } // namespace
184
185 //--------------------------------
186
TPersistDeclaration(const std::string & id)187 TPersistDeclaration::TPersistDeclaration(const std::string &id) : m_id(id) {
188 TPersistFactory::instance()->add(id, this);
189 }
190
191 //===============================================================
192
create(const std::string & name)193 TPersist *TPersist::create(const std::string &name) {
194 return TPersistFactory::instance()->create(name);
195 }
196
197 //===============================================================
198
199 class TOStream::Imp {
200 public:
201 ostream *m_os;
202 bool m_chanOwner;
203 bool m_compressed;
204 ostringstream m_ostringstream;
205
206 vector<std::string> m_tagStack;
207 int m_tab;
208 bool m_justStarted;
209 typedef map<TPersist *, int> PersistTable;
210 PersistTable m_table;
211 int m_maxId;
212 TFilePath m_filepath;
213
Imp()214 Imp()
215 : m_os(0)
216 , m_chanOwner(false)
217 , m_tab(0)
218 , m_justStarted(true)
219 , m_maxId(0)
220 , m_compressed(false) {}
221 };
222
223 //---------------------------------------------------------------
224
TOStream(const TFilePath & fp,bool compressed)225 TOStream::TOStream(const TFilePath &fp, bool compressed) : m_imp(new Imp) {
226 m_imp->m_filepath = fp;
227
228 if (compressed) {
229 m_imp->m_os = &m_imp->m_ostringstream;
230 m_imp->m_compressed = true;
231 m_imp->m_chanOwner = false;
232 } else {
233 std::unique_ptr<Tofstream> os(new Tofstream(fp));
234 m_imp->m_os = os->isOpen() ? os.release() : 0;
235 m_imp->m_chanOwner = true;
236 }
237
238 m_imp->m_justStarted = true;
239 }
240
241 //---------------------------------------------------------------
242
TOStream(std::shared_ptr<Imp> imp)243 TOStream::TOStream(std::shared_ptr<Imp> imp) : m_imp(std::move(imp)) {
244 assert(!m_imp->m_tagStack.empty());
245 ostream &os = *m_imp->m_os;
246 if (!m_imp->m_justStarted) cr();
247 os << "<" << m_imp->m_tagStack.back() << ">";
248 m_imp->m_tab++;
249 cr();
250 m_imp->m_justStarted = true;
251 }
252
TOStream(TOStream && that)253 TOStream::TOStream(TOStream &&that) : m_imp(std::move(that.m_imp)) {}
254
operator =(TOStream && that)255 TOStream &TOStream::operator=(TOStream &&that) {
256 if (this != &that) {
257 this->m_imp = std::move(that.m_imp);
258 }
259 return *this;
260 }
261
262 //---------------------------------------------------------------
263
~TOStream()264 TOStream::~TOStream() {
265 if (!m_imp) {
266 return;
267 }
268 try {
269 if (!m_imp->m_tagStack.empty()) {
270 string tagName = m_imp->m_tagStack.back();
271 m_imp->m_tagStack.pop_back();
272 assert(tagName != "");
273 ostream &os = *m_imp->m_os;
274 m_imp->m_tab--;
275 if (!m_imp->m_justStarted) cr();
276 os << "</" << tagName << ">";
277 cr();
278 m_imp->m_justStarted = true;
279 } else {
280 if (m_imp->m_compressed) {
281 std::string tmp = m_imp->m_ostringstream.str();
282 const void *in = (const void *)tmp.c_str();
283
284 size_t in_len = strlen((char *)in);
285
286 size_t out_len = LZ4F_compressFrameBound(in_len, NULL);
287 void *out = malloc(out_len);
288
289 out_len = LZ4F_compressFrame(out, out_len, in, in_len, NULL);
290 if (!LZ4F_isError(out_len)) {
291 Tofstream os(m_imp->m_filepath);
292 // TNZC <lunghezza dati decompress> <lunghezza dati compresso> <dati
293 // compressi>
294 os.write("TABc", 4);
295 TINT32 v;
296 v = 0x0A0B0C0D;
297 os.write((char *)&v, sizeof v);
298 v = in_len;
299 os.write((char *)&v, sizeof v);
300 v = out_len;
301 os.write((char *)&v, sizeof v);
302 os.write((char *)out, out_len);
303 }
304
305 free(out);
306 }
307 if (m_imp->m_chanOwner) delete m_imp->m_os;
308 }
309 } catch (...) {
310 }
311 }
312
313 //---------------------------------------------------------------
314
getFilePath()315 TFilePath TOStream::getFilePath() { return m_imp->m_filepath; }
316
317 //---------------------------------------------------------------
318
getRepositoryPath()319 TFilePath TOStream::getRepositoryPath() {
320 TFilePath fp = m_imp->m_filepath;
321 return fp.getParentDir() + (fp.getName() + "_files");
322 }
323
324 //---------------------------------------------------------------
325
operator <<(int v)326 TOStream &TOStream::operator<<(int v) {
327 *(m_imp->m_os) << v << " ";
328 m_imp->m_justStarted = false;
329 return *this;
330 }
331
332 //---------------------------------------------------------------
333
operator <<(double v)334 TOStream &TOStream::operator<<(double v) {
335 if (areAlmostEqual(v, 0)) // con valori molto piccoli (es. 1.4e-310) non
336 // riesce a rileggerli!
337 v = 0;
338
339 *(m_imp->m_os) << v << " ";
340 m_imp->m_justStarted = false;
341 return *this;
342 }
343
344 //---------------------------------------------------------------
345
operator <<(string v)346 TOStream &TOStream::operator<<(string v) {
347 ostream &os = *(m_imp->m_os);
348 int len = v.length();
349 if (len == 0) {
350 os << "\"\""
351 << " ";
352 m_imp->m_justStarted = false;
353 return *this;
354 }
355 int i;
356 for (i = 0; i < len; i++)
357 if ((!iswalnum(v[i]) && v[i] != '_' && v[i] != '%') ||
358 v[i] < 32 // Less than ASCII for SPACE
359 || v[i] > 126 // Greater than ASCII for ~
360 )
361 break;
362 if (i == len)
363 os << v << " ";
364 else {
365 os << '"' << escape(v) << '"';
366 }
367 m_imp->m_justStarted = false;
368 return *this;
369 }
370
371 //---------------------------------------------------------------
372
operator <<(QString _v)373 TOStream &TOStream::operator<<(QString _v) {
374 string v = _v.toStdString();
375
376 ostream &os = *(m_imp->m_os);
377 int len = v.length();
378 if (len == 0) {
379 os << "\"\""
380 << " ";
381 m_imp->m_justStarted = false;
382 return *this;
383 }
384 int i;
385 for (i = 0; i < len; i++)
386 if ((!iswalnum(v[i]) && v[i] != '_' && v[i] != '%') ||
387 v[i] < 32 // Less than ASCII for SPACE
388 || v[i] > 126 // Greater than ASCII for ~
389 )
390 break;
391 if (i == len)
392 os << v << " ";
393 else {
394 os << '"' << escape(v) << '"';
395 }
396 m_imp->m_justStarted = false;
397 return *this;
398 }
399
400 //---------------------------------------------------------------
401
operator <<(std::wstring v)402 TOStream &TOStream::operator<<(std::wstring v) {
403 return operator<<(::to_string(v));
404 /*
405 ostream &os = *(m_imp->m_os);
406 int len = v.length();
407 if(len==0)
408 {
409 os << "\"" << "\"" << " ";
410 m_imp->m_justStarted = false;
411 return *this;
412 }
413 int i;
414 for(i=0;i<len;i++)
415 if(!iswalnum(v[i]) && v[i]!=L'_')
416 break;
417 if(i==len)
418 {
419 os << v;
420 os << " ";
421 }
422 else
423 {
424 os.put('"');
425 for(i=0;i<len;i++)
426 if(iswalnum(v[i]))
427 os.put((char)v[i]);
428 else if(v[i]=='"')
429 os << "\\\"";
430 else if(v[i]=='\n')
431 os << "\\n";
432 else if(iswprint(v[i]))
433 os << v[i];
434 else
435 {os.put('\\'); os << (int)v[i];}
436 os << "\" ";
437 }
438 m_imp->m_justStarted = false;
439 return *this;
440 */
441 }
442
443 //---------------------------------------------------------------
444
operator <<(const TFilePath & v)445 TOStream &TOStream::operator<<(const TFilePath &v) {
446 return operator<<(v.getWideString());
447 }
448
449 //---------------------------------------------------------------
450
operator <<(const TPixel32 & v)451 TOStream &TOStream::operator<<(const TPixel32 &v) {
452 ostream &os = *(m_imp->m_os);
453 os << (int)v.r << " " << (int)v.g << " " << (int)v.b << " " << (int)v.m
454 << " ";
455 m_imp->m_justStarted = false;
456 return *this;
457 }
458
459 //---------------------------------------------------------------
460
operator <<(const TPixel64 & v)461 TOStream &TOStream::operator<<(const TPixel64 &v) {
462 ostream &os = *(m_imp->m_os);
463 os << (int)v.r << " " << (int)v.g << " " << (int)v.b << " " << (int)v.m
464 << " ";
465 m_imp->m_justStarted = false;
466 return *this;
467 }
468
469 //---------------------------------------------------------------
470
cr()471 void TOStream::cr() {
472 *(m_imp->m_os) << endl;
473 for (int i = 0; i < m_imp->m_tab; i++) *(m_imp->m_os) << " ";
474 m_imp->m_justStarted = false;
475 }
476
477 //---------------------------------------------------------------
478
operator bool() const479 TOStream::operator bool() const { return (m_imp->m_os && *m_imp->m_os); }
480
481 //---------------------------------------------------------------
child(string tagName)482 TOStream TOStream::child(string tagName) {
483 assert(tagName != "");
484 m_imp->m_tagStack.push_back(tagName);
485 return TOStream(m_imp);
486 }
487
488 //---------------------------------------------------------------
489
openChild(string tagName)490 void TOStream::openChild(string tagName) {
491 assert(tagName != "");
492 m_imp->m_tagStack.push_back(tagName);
493 if (m_imp->m_justStarted == false) cr();
494 *(m_imp->m_os) << "<" << m_imp->m_tagStack.back() << ">";
495 m_imp->m_tab++;
496 cr();
497 m_imp->m_justStarted = true;
498 }
499
500 //---------------------------------------------------------------
501
openChild(string tagName,const map<std::string,string> & attributes)502 void TOStream::openChild(string tagName,
503 const map<std::string, string> &attributes) {
504 assert(tagName != "");
505 m_imp->m_tagStack.push_back(tagName);
506 if (m_imp->m_justStarted == false) cr();
507 *(m_imp->m_os) << "<" << m_imp->m_tagStack.back();
508 for (std::map<std::string, string>::const_iterator it = attributes.begin();
509 it != attributes.end(); ++it) {
510 *(m_imp->m_os) << " " << it->first << "=\"" << escape(it->second) << "\"";
511 }
512 *(m_imp->m_os) << ">";
513 m_imp->m_tab++;
514 cr();
515 m_imp->m_justStarted = true;
516 }
517
518 //---------------------------------------------------------------
519
closeChild()520 void TOStream::closeChild() {
521 assert(!m_imp->m_tagStack.empty());
522 string tagName = m_imp->m_tagStack.back();
523 m_imp->m_tagStack.pop_back();
524 assert(tagName != "");
525 // ostream &os = *m_imp->m_os; //os non e' usato
526 m_imp->m_tab--;
527 if (!m_imp->m_justStarted) cr();
528 *(m_imp->m_os) << "</" << tagName << ">";
529 cr();
530 m_imp->m_justStarted = true;
531 }
532
533 //---------------------------------------------------------------
534
openCloseChild(string tagName,const map<std::string,string> & attributes)535 void TOStream::openCloseChild(string tagName,
536 const map<std::string, string> &attributes) {
537 assert(tagName != "");
538 // m_imp->m_tagStack.push_back(tagName);
539 if (m_imp->m_justStarted == false) cr();
540 *(m_imp->m_os) << "<" << tagName;
541 for (std::map<std::string, string>::const_iterator it = attributes.begin();
542 it != attributes.end(); ++it) {
543 *(m_imp->m_os) << " " << it->first << "=\"" << escape(it->second) << "\"";
544 }
545 *(m_imp->m_os) << "/>";
546 // m_imp->m_tab++;
547 cr();
548 m_imp->m_justStarted = true;
549 }
550
551 //---------------------------------------------------------------
552
operator <<(TPersist & v)553 TOStream &TOStream::operator<<(TPersist &v) {
554 v.saveData(*this);
555 return *this;
556 }
557
558 //---------------------------------------------------------------
559
operator <<(TPersist * v)560 TOStream &TOStream::operator<<(TPersist *v) {
561 Imp::PersistTable::iterator it = m_imp->m_table.find(v);
562 if (it != m_imp->m_table.end()) {
563 *(m_imp->m_os) << "<" << v->getStreamTag() << " id='" << it->second
564 << "'/>";
565 m_imp->m_justStarted = false;
566 } else {
567 int id = ++m_imp->m_maxId;
568 m_imp->m_table[v] = id;
569 *(m_imp->m_os) << "<" << v->getStreamTag() << " id='" << id << "'>";
570 m_imp->m_tab++;
571 cr();
572 v->saveData(*this);
573 m_imp->m_tab--;
574 cr();
575 *(m_imp->m_os) << "</" << v->getStreamTag() << ">";
576 cr();
577 }
578 return *this;
579 }
580
581 //---------------------------------------------------------------
582
checkStatus() const583 bool TOStream::checkStatus() const {
584 if (!m_imp->m_os) return false;
585
586 m_imp->m_os->flush();
587 return m_imp->m_os->rdstate() == ios_base::goodbit;
588 }
589
getCurrentTagName()590 std::string TOStream::getCurrentTagName() {
591 return (m_imp->m_tagStack.empty()) ? "" : m_imp->m_tagStack.back();
592 }
593
594 //===============================================================
595 /*!
596 This class contains TIStream's attributes.
597 It is created by memory allocation in the TIStream's constructor.
598 */
599 class TIStream::Imp {
600 public:
601 istream *m_is;
602 bool m_chanOwner;
603 int m_line;
604 string m_strbuffer;
605 bool m_compressed;
606
607 vector<std::string> m_tagStack;
608
609 typedef map<int, TPersist *> PersistTable;
610 PersistTable m_table;
611
612 StreamTag m_currentTag;
613
614 TFilePath m_filepath;
615
616 VersionNumber m_versionNumber;
617
Imp()618 Imp()
619 : m_is(0)
620 , m_chanOwner(false)
621 , m_line(0)
622 , m_compressed(false)
623 , m_versionNumber(0, 0) {}
624
625 // update m_line if necessary; returns -e if eof
626 int getNextChar();
627
628 inline void skipBlanks();
629 bool matchTag();
630 inline bool match(char c);
631 bool matchIdent(string &ident);
632 bool matchValue(string &value);
633
634 void skipCurrentTag();
635 };
636
637 //---------------------------------------------------------------
638
getFilePath()639 TFilePath TIStream::getFilePath() { return m_imp->m_filepath; }
640
641 //---------------------------------------------------------------
642
getRepositoryPath()643 TFilePath TIStream::getRepositoryPath() {
644 TFilePath fp = m_imp->m_filepath;
645 return fp.getParentDir() + (fp.getName() + "_files");
646 }
647
648 //---------------------------------------------------------------
649
getNextChar()650 int TIStream::Imp::getNextChar() {
651 char c;
652 m_is->get(c);
653 if (m_is->eof()) return -1;
654 if (c == '\r') m_line++;
655 return c;
656 }
657
658 //---------------------------------------------------------------
659
skipBlanks()660 void TIStream::Imp::skipBlanks() {
661 istream &is = *m_is;
662 istream::int_type c;
663 while (c = is.peek(), (isspace(c) || c == '\r')) getNextChar();
664 }
665
666 //---------------------------------------------------------------
667
match(char c)668 bool TIStream::Imp::match(char c) {
669 if (m_is->peek() == c) {
670 getNextChar();
671 return true;
672 } else
673 return false;
674 }
675
676 //---------------------------------------------------------------
677
matchIdent(string & ident)678 bool TIStream::Imp::matchIdent(string &ident) {
679 istream &is = *m_is;
680 if (!isalnum(is.peek())) return false;
681 ident = "";
682 char c;
683 is.get(c);
684 ident.append(1, c);
685 while (c = is.peek(), isalnum(c) || c == '_' || c == '.' || c == '-') {
686 is.get(c);
687 ident.append(1, c);
688 }
689 return true;
690 }
691
692 //---------------------------------------------------------------
693
matchValue(string & str)694 bool TIStream::Imp::matchValue(string &str) {
695 istream &is = *m_is;
696 char quote = is.peek();
697 char c;
698 if (!is || (quote != '\'' && quote != '\"')) return false;
699 is.get(c);
700 str = "";
701 for (;;) {
702 is.get(c);
703 if (!is) throw TException("expected '\"'");
704 if (c == quote) break;
705 if (c == '\\') {
706 is.get(c);
707 if (!is) throw TException("unexpected EOF");
708 if (c != '\'' && c != '\"' && c != '\\')
709 throw TException("bad escape sequence");
710 }
711 str.append(1, c);
712 }
713 if (c != quote) throw TException("missing '\"'");
714 return true;
715 }
716
717 //---------------------------------------------------------------
718
matchTag()719 bool TIStream::Imp::matchTag() {
720 if (m_currentTag) return true;
721 StreamTag &tag = m_currentTag;
722 tag = StreamTag();
723 skipBlanks();
724 if (!match('<')) return false;
725 skipBlanks();
726 if (match('!')) {
727 skipBlanks();
728 if (!match('-') || !match('-')) throw TException("expected '<!--' tag");
729 istream &is = *m_is;
730 char c;
731 int status = 1;
732 while (status != 0 && is.get(c)) switch (status) {
733 case 1:
734 if (c == '-') status = 2;
735 break;
736 case 2:
737 if (c == '-')
738 status = 3;
739 else
740 status = 1;
741 break;
742 case 3:
743 if (c == '>')
744 status = 0;
745 else if (c == '-') {
746 } else
747 status = 1;
748 break;
749 }
750 return matchTag();
751 }
752 if (match('/')) {
753 tag.m_type = StreamTag::EndTag;
754 skipBlanks();
755 }
756
757 if (!matchIdent(tag.m_name)) throw TException("expected identifier");
758 skipBlanks();
759 for (;;) {
760 if (match('>')) break;
761 if (match('/')) {
762 tag.m_type = StreamTag::BeginEndTag;
763 skipBlanks();
764 if (match('>')) break;
765 throw TException("expected '>'");
766 }
767 string name;
768 if (!matchIdent(name)) throw TException("expected identifier");
769 skipBlanks();
770 if (match('=')) {
771 string value;
772 skipBlanks();
773 if (!matchValue(value)) throw TException("expected value");
774 tag.m_attributes[name] = value;
775 skipBlanks();
776 }
777 }
778 return true;
779 }
780
781 //---------------------------------------------------------------
782
skipCurrentTag()783 void TIStream::Imp::skipCurrentTag() {
784 if (m_currentTag.m_type == StreamTag::BeginEndTag) return;
785 istream &is = *m_is;
786 int level = 1;
787 int c;
788 for (;;) {
789 if (is.eof()) break; // unexpected eof
790 c = is.peek();
791 if (c != '<') {
792 getNextChar();
793 continue;
794 }
795
796 // tag found
797 c = getNextChar();
798 if (c < 0) break;
799
800 c = getNextChar();
801 if (c < 0) break;
802
803 if (c == '/') {
804 // end tag
805 do
806 c = getNextChar();
807 while (c >= 0 && c != '>');
808 if (c < 0) break; // unexpected eof
809 if (--level <= 0) {
810 // m_currentTag.m_type = StreamTag::EndTag;
811 m_tagStack.pop_back();
812 m_currentTag = StreamTag();
813 break;
814 }
815 } else {
816 // tag
817 int oldC;
818 do {
819 oldC = c;
820 c = getNextChar();
821 } while (c >= 0 && c != '>');
822 if (c < 0) break; // unexpected eof
823 if (oldC != '/') level++;
824 }
825 }
826 }
827
828 //---------------------------------------------------------------
829
TIStream(const TFilePath & fp)830 TIStream::TIStream(const TFilePath &fp) : m_imp(new Imp) {
831 m_imp->m_filepath = fp;
832 m_imp->m_is = new Tifstream(fp);
833
834 if (m_imp->m_is->peek() == 'T') // non comincia con '<' dev'essere compresso
835 {
836 bool swapForEndianess = false;
837
838 unique_ptr<std::istream> is(m_imp->m_is);
839 m_imp->m_is = 0;
840
841 char magicBuffer[4];
842 is->read(magicBuffer, 4);
843 string magic(magicBuffer, 4);
844 size_t in_len, out_len;
845
846 if (magic == "TNZC") {
847 // Tab3.0 beta
848 is->read((char *)&out_len, sizeof out_len);
849 is->read((char *)&in_len, sizeof in_len);
850 } else if (magic == "TABc") {
851 TINT32 v;
852 is->read((char *)&v, sizeof v);
853 printf("magic = %08X\n", v);
854
855 if (v == 0x0A0B0C0D)
856 swapForEndianess = false;
857 else if (v == 0x0D0C0B0A)
858 swapForEndianess = true;
859 else {
860 swapForEndianess = true;
861 printf("UH OH!\n");
862 }
863
864 is->read((char *)&v, sizeof v);
865 out_len = swapForEndianess ? swapTINT32(v) : v;
866 is->read((char *)&v, sizeof v);
867 in_len = swapForEndianess ? swapTINT32(v) : v;
868 } else
869 throw TException("Bad magic number");
870
871 if (in_len <= 0 || in_len > 100000000) // 100M di tnzfile (compresso)
872 // sembrano proprio esagerati
873 throw TException("Corrupted file");
874
875 LZ4F_decompressionContext_t lz4dctx;
876
877 LZ4F_errorCode_t err =
878 LZ4F_createDecompressionContext(&lz4dctx, LZ4F_VERSION);
879 if (LZ4F_isError(err)) throw TException("Couldn't decompress file");
880
881 char *in = (char *)malloc(in_len);
882 is->read((char *)in, in_len);
883
884 m_imp->m_strbuffer.resize(out_len + 1000); // per prudenza
885 char *out = (char *)m_imp->m_strbuffer.c_str();
886
887 size_t check_len = out_len;
888
889 // size_t remaining = LZ4F_decompress(lz4dctx, out, &out_len, in, &in_len,
890 // NULL);
891 bool ok = lz4decompress(lz4dctx, out, &out_len, in, in_len);
892 LZ4F_freeDecompressionContext(lz4dctx);
893
894 free(in);
895
896 if (!ok) throw TException("Couldn't decompress file");
897
898 if (check_len != out_len) throw TException("corrupted file");
899
900 m_imp->m_is = new istringstream(std::string(out, out_len));
901 }
902
903 m_imp->m_chanOwner = true;
904 }
905
906 //---------------------------------------------------------------
907
908 /*
909 TIStream::TIStream(istream &is)
910 : m_imp(new Imp)
911 {
912 m_imp->m_is = &is;
913 }
914 */
915
916 //---------------------------------------------------------------
917
~TIStream()918 TIStream::~TIStream() {
919 if (m_imp->m_chanOwner) delete m_imp->m_is;
920 }
921
922 //---------------------------------------------------------------
923
operator >>(int & v)924 TIStream &TIStream::operator>>(int &v) {
925 *(m_imp->m_is) >> v;
926 return *this;
927 }
928
929 //---------------------------------------------------------------
930
operator >>(double & v)931 TIStream &TIStream::operator>>(double &v) {
932 *(m_imp->m_is) >> v;
933 return *this;
934 }
935 //---------------------------------------------------------------
936
operator >>(std::wstring & v)937 TIStream &TIStream::operator>>(std::wstring &v) {
938 string s;
939 operator>>(s);
940 v = ::to_wstring(s);
941 return *this;
942 }
943
944 //---------------------------------------------------------------
945
operator >>(string & v)946 TIStream &TIStream::operator>>(string &v) {
947 istream &is = *(m_imp->m_is);
948 v = "";
949 m_imp->skipBlanks();
950 char c;
951 is.get(c);
952 if (c == '\"') {
953 is.get(c);
954 while (is && c != '"') {
955 if (c == '\\') {
956 is.get(c);
957 if (!is) throw TException("unexpected EOF");
958 if (c == '"')
959 v.append(1, '"');
960 else if (c == '\\')
961 v.append(1, '\\');
962 else if (c == '\'')
963 v.append(1, '\'');
964 else {
965 v.append(1, '\\');
966 v.append(1, c);
967 }
968 } else
969 v.append(1, c);
970 is.get(c);
971 }
972 } else {
973 v.append(1, c);
974 while (c = is.peek(), isalnum(c) || c == '_' || c == '&' || c == '#' ||
975 c == ';' || c == '%') {
976 is.get(c);
977 v.append(1, c);
978 }
979 }
980
981 return *this;
982 }
983
984 //---------------------------------------------------------------
985
operator >>(QString & v)986 TIStream &TIStream::operator>>(QString &v) {
987 istream &is = *(m_imp->m_is);
988 v = "";
989 m_imp->skipBlanks();
990 char c;
991 is.get(c);
992 if (c == '\"') {
993 is.get(c);
994 while (is && c != '"') {
995 if (c == '\\') {
996 is.get(c);
997 if (!is) throw TException("unexpected EOF");
998 if (c == '"')
999 v.append('"');
1000 else if (c == '\\')
1001 v.append('\\');
1002 else if (c == '\'')
1003 v.append('\'');
1004 else {
1005 v.append('\\');
1006 v.append(c);
1007 }
1008 } else
1009 v.append(c);
1010 is.get(c);
1011 }
1012 } else {
1013 v.append(c);
1014 while (c = is.peek(), isalnum(c) || c == '_' || c == '&' || c == '#' ||
1015 c == ';' || c == '%') {
1016 is.get(c);
1017 v.append(c);
1018 }
1019 }
1020
1021 return *this;
1022 }
1023
1024 //---------------------------------------------------------------
1025
getString()1026 string TIStream::getString() {
1027 istream &is = *(m_imp->m_is);
1028 string v = "";
1029 m_imp->skipBlanks();
1030 char c = is.peek();
1031 while (c != '<') {
1032 is.get(c);
1033 c = is.peek();
1034 if (!is) throw TException("unexpected EOF");
1035 v.append(1, c);
1036 }
1037 return v;
1038 }
1039
1040 //---------------------------------------------------------------
1041
operator >>(TPixel32 & v)1042 TIStream &TIStream::operator>>(TPixel32 &v) {
1043 istream &is = *(m_imp->m_is);
1044 int r, g, b, m;
1045 is >> r;
1046 is >> g;
1047 is >> b;
1048 is >> m;
1049 v.r = r;
1050 v.g = g;
1051 v.b = b;
1052 v.m = m;
1053 return *this;
1054 }
1055 //---------------------------------------------------------------
1056
operator >>(TPixel64 & v)1057 TIStream &TIStream::operator>>(TPixel64 &v) {
1058 istream &is = *(m_imp->m_is);
1059 int r, g, b, m;
1060 is >> r;
1061 is >> g;
1062 is >> b;
1063 is >> m;
1064 v.r = r;
1065 v.g = g;
1066 v.b = b;
1067 v.m = m;
1068 return *this;
1069 }
1070
1071 //---------------------------------------------------------------
1072
operator >>(TFilePath & v)1073 TIStream &TIStream::operator>>(TFilePath &v) {
1074 istream &is = *(m_imp->m_is);
1075 string s;
1076 char c;
1077 m_imp->skipBlanks();
1078 is.get(c);
1079 if (c == '"') {
1080 is.get(c);
1081 bool escapeChar = false;
1082 // If processing double-quote ("), if it's escaped, keep reading.
1083 while (is && (c != '"' || escapeChar)) {
1084 // if(c=='\\')
1085 // is.get(c);
1086 if (c == '\\' && !escapeChar)
1087 escapeChar = true;
1088 else
1089 escapeChar = false;
1090 s.append(1, c);
1091 is.get(c);
1092 }
1093 } else {
1094 // il filepath non e' fra virgolette:
1095 // puo' contenere solo caratteri alfanumerici, % e _
1096 s.append(1, c);
1097 while (is) {
1098 c = is.peek();
1099 if (!isalnum(c) && c != '%' && c != '_') break;
1100 is.get(c);
1101 s.append(1, c);
1102 }
1103 }
1104
1105 v = TFilePath(s);
1106 return *this;
1107 }
1108 //---------------------------------------------------------------
1109
operator >>(TPersist & v)1110 TIStream &TIStream::operator>>(TPersist &v) {
1111 v.loadData(*this);
1112 return *this;
1113 }
1114
1115 //---------------------------------------------------------------
1116
operator >>(TPersist * & v)1117 TIStream &TIStream::operator>>(TPersist *&v) {
1118 if (!m_imp->matchTag() || m_imp->m_currentTag.m_type == StreamTag::EndTag) {
1119 throw TException("expected begin tag");
1120 }
1121 StreamTag tag = m_imp->m_currentTag;
1122 m_imp->m_currentTag = StreamTag();
1123 string tagName = tag.m_name;
1124 std::map<std::string, string>::iterator it;
1125 int id = -1;
1126 it = tag.m_attributes.find("id");
1127 if (it != tag.m_attributes.end()) id = atoi(it->second.c_str());
1128 // cout << "tagname = " << tagName << " id = " << id << endl;
1129
1130 Imp::PersistTable::iterator pit = m_imp->m_table.find(id);
1131 if (pit == m_imp->m_table.end()) {
1132 v = TPersistFactory::instance()->create(tagName);
1133 if (!v) throw TException("unable to create a persistent '" + tagName + "'");
1134 m_imp->m_table[id] = v;
1135 if (tag.m_type != StreamTag::BeginTag)
1136 throw TException("expected begin tag");
1137 m_imp->m_tagStack.push_back(tag.m_name);
1138 v->loadData(*this);
1139 m_imp->matchTag();
1140 if (!m_imp->m_currentTag || m_imp->m_currentTag.m_type != StreamTag::EndTag)
1141 throw TException("expected end tag");
1142 if (m_imp->m_currentTag.m_name != m_imp->m_tagStack.back())
1143 throw TException("end tag mismatch");
1144 m_imp->m_tagStack.pop_back();
1145 m_imp->m_currentTag = StreamTag();
1146 } else {
1147 v = pit->second;
1148 if (tag.m_type != StreamTag::BeginEndTag)
1149 throw TException("expected begin/end tag");
1150 }
1151 return *this;
1152 }
1153
1154 //---------------------------------------------------------------
1155
matchEndTag()1156 bool TIStream::matchEndTag() {
1157 if (m_imp->m_tagStack.empty()) throw TException("tag stack emtpy");
1158 if (!m_imp->matchTag()) return false;
1159 if (m_imp->m_currentTag.m_type != StreamTag::EndTag) return false;
1160 if (m_imp->m_currentTag.m_name != m_imp->m_tagStack.back())
1161 throw TException("end tag mismatch");
1162 m_imp->m_tagStack.pop_back();
1163 m_imp->m_currentTag = StreamTag();
1164 return true;
1165 }
1166
1167 //---------------------------------------------------------------
1168
eos()1169 bool TIStream::eos() {
1170 if (m_imp->matchTag())
1171 return m_imp->m_currentTag.m_type == StreamTag::EndTag;
1172 else
1173 return !(*m_imp->m_is);
1174 }
1175
1176 //---------------------------------------------------------------
1177
matchTag(string & tagName)1178 bool TIStream::matchTag(string &tagName) {
1179 if (!m_imp->matchTag()) return false;
1180 if (m_imp->m_currentTag.m_type == StreamTag::EndTag) return false;
1181 tagName = m_imp->m_currentTag.m_name;
1182 m_imp->m_currentTag.m_name = "";
1183 if (m_imp->m_currentTag.m_type != StreamTag::BeginEndTag)
1184 m_imp->m_tagStack.push_back(tagName);
1185 return true;
1186 }
1187
1188 //---------------------------------------------------------------
1189
getTagAttribute(string name) const1190 string TIStream::getTagAttribute(string name) const {
1191 StreamTag &tag = m_imp->m_currentTag;
1192 std::map<std::string, string>::const_iterator it =
1193 tag.m_attributes.find(name);
1194 if (it == tag.m_attributes.end())
1195 return "";
1196 else
1197 return it->second;
1198 }
1199
1200 //---------------------------------------------------------------
1201
getTagParam(string paramName,string & value)1202 bool TIStream::getTagParam(string paramName, string &value) {
1203 if (m_imp->m_tagStack.empty()) return false;
1204 StreamTag &tag = m_imp->m_currentTag;
1205 std::map<std::string, string>::const_iterator it =
1206 tag.m_attributes.find(paramName);
1207 if (it == tag.m_attributes.end()) return false;
1208 value = it->second;
1209 return true;
1210 }
1211
1212 //---------------------------------------------------------------
1213
getTagParam(string paramName,int & value)1214 bool TIStream::getTagParam(string paramName, int &value) {
1215 string svalue;
1216 if (!getTagParam(paramName, svalue)) return false;
1217 istringstream is(svalue);
1218 value = 0;
1219 is >> value;
1220 return true;
1221 }
1222
1223 //---------------------------------------------------------------
1224
isBeginEndTag()1225 bool TIStream::isBeginEndTag() {
1226 return m_imp->m_currentTag.m_type == StreamTag::BeginEndTag;
1227 }
1228
1229 //---------------------------------------------------------------
1230
openChild(string & tagName)1231 bool TIStream::openChild(string &tagName) {
1232 if (!m_imp->matchTag()) return false;
1233 if (m_imp->m_currentTag.m_type != StreamTag::BeginTag) return false;
1234 tagName = m_imp->m_currentTag.m_name;
1235 m_imp->m_currentTag.m_name = "";
1236 m_imp->m_tagStack.push_back(tagName);
1237 return true;
1238 }
1239
1240 //---------------------------------------------------------------
1241
closeChild()1242 void TIStream::closeChild() {
1243 if (!matchEndTag()) {
1244 string tagName;
1245 if (!m_imp->m_tagStack.empty()) tagName = m_imp->m_tagStack.back();
1246 if (tagName != "")
1247 throw TException("Expected \"" + tagName + "\" end tag");
1248 else
1249 throw TException("expected EndTag");
1250 }
1251 }
1252
1253 //---------------------------------------------------------------
1254
match(char c) const1255 bool TIStream::match(char c) const {
1256 m_imp->skipBlanks();
1257 if (m_imp->m_is->peek() != c) return false;
1258 m_imp->m_is->get(c);
1259 if (c == '\r') m_imp->m_line++;
1260 return true;
1261 }
1262
1263 //---------------------------------------------------------------
1264
operator bool() const1265 TIStream::operator bool() const { return (m_imp->m_is && *m_imp->m_is); }
1266
1267 //---------------------------------------------------------------
1268
getLine() const1269 int TIStream::getLine() const { return m_imp->m_line + 1; }
1270
1271 //---------------------------------------------------------------
1272
getVersion() const1273 VersionNumber TIStream::getVersion() const { return m_imp->m_versionNumber; }
1274
1275 //---------------------------------------------------------------
1276
setVersion(const VersionNumber & version)1277 void TIStream::setVersion(const VersionNumber &version) {
1278 m_imp->m_versionNumber = version;
1279 }
1280
1281 //---------------------------------------------------------------
1282
skipCurrentTag()1283 void TIStream::skipCurrentTag() { m_imp->skipCurrentTag(); }
1284
1285 //---------------------------------------------------------------
1286
getCurrentTagName()1287 std::string TIStream::getCurrentTagName() { return m_imp->m_tagStack.back(); }