source: trunk/Mars/mcore/ofits.h@ 17223

Last change on this file since 17223 was 17221, checked in by lyard, 11 years ago
better version of zofits. New factofits. Only cleanup, defines, comments and catalog shrinkage are missing
File size: 28.9 KB
Line 
1#ifndef MARS_ofits
2#define MARS_ofits
3
4#include <string>
5#include <string.h>
6#include <algorithm>
7#include <sstream>
8#include <iostream>
9#include <fstream>
10#include <iomanip>
11#include <vector>
12#include <algorithm>
13#include <stdexcept>
14
15#ifdef __CINT__
16#define off_t size_t
17#endif
18
19#ifndef __MARS__
20#define gLog cerr
21#define ___err___ ""
22#define ___warn___ ""
23#define ___all___ ""
24#else
25#include "MLog.h"
26#include "MLogManip.h"
27#define ___err___ err
28#define ___warn___ warn
29#define ___all___ all
30#endif
31
32#include "checksum.h"
33
34#ifndef __MARS__
35namespace std
36{
37#else
38using namespace std;
39#endif
40
41// Sloppy: allow / <--- left
42// allow all characters (see specs for what is possible)
43
44// units: m kg s rad sr K A mol cd Hz J W V N Pa C Ohm S F Wb T Hlm lx
45
46class ofits : public ofstream
47{
48public:
49 struct Key
50 {
51 string key;
52 bool delim;
53 string value;
54 string comment;
55 string fitsString;
56
57 off_t offset; // offset in file
58
59 bool changed; // For closing the file
60
61 Key(const string &k="") : key(k), delim(false), fitsString(""), offset(0), changed(true) { }
62
63 string Trim(const string &str)
64 {
65 // Trim Both leading and trailing spaces
66 const size_t first = str.find_first_not_of(' '); // Find the first character position after excluding leading blank spaces
67 const size_t last = str.find_last_not_of(' '); // Find the first character position from reverse af
68
69 // if all spaces or empty return an empty string
70 if (string::npos==first || string::npos==last)
71 return string();
72
73 return str.substr(first, last-first+1);
74 }
75
76 bool FormatKey()
77 {
78 key = Trim(key);
79 if (key.empty())
80 {
81#ifdef __EXCEPTIONS
82 throw runtime_error("Key name empty.");
83#else
84 gLog << ___err___ << "ERROR - Key name empty." << endl;
85 return false;
86#endif
87 }
88 if (key.size()>8)
89 {
90 ostringstream sout;
91 sout << "Key '" << key << "' exceeds 8 bytes.";
92#ifdef __EXCEPTIONS
93 throw runtime_error(sout.str());
94#else
95 gLog << ___err___ << "ERROR - " << sout.str() << endl;
96 return false;
97#endif
98 }
99
100 //transform(key.begin(), key.end(), key.begin(), toupper);
101
102 for (string::const_iterator c=key.begin(); c<key.end(); c++)
103 if ((*c<'A' || *c>'Z') && (*c<'0' || *c>'9') && *c!='-' && *c!='_')
104 {
105 ostringstream sout;
106 sout << "Invalid character '" << *c << "' found in key '" << key << "'";
107#ifdef __EXCEPTIONS
108 throw runtime_error(sout.str());
109#else
110 gLog << ___err___ << "ERROR - " << sout.str() << endl;
111 return false;
112#endif
113 }
114
115 return true;
116 }
117
118 bool FormatComment()
119 {
120 comment = Trim(comment);
121
122 for (string::const_iterator c=key.begin(); c<key.end(); c++)
123 if (*c<32 || *c>126)
124 {
125 ostringstream sout;
126 sout << "Invalid character '" << *c << "' [" << int(*c) << "] found in comment '" << comment << "'";
127#ifdef __EXCEPTIONS
128 throw runtime_error(sout.str());
129#else
130 gLog << ___err___ << "ERROR - " << sout.str() << endl;
131 return false;
132#endif
133 }
134
135 return true;
136 }
137
138 bool check(bool trim=false)
139 {
140 if (!FormatKey())
141 return false;
142
143 if (!FormatComment())
144 return false;
145
146 size_t sz = CalcSize();
147 if (sz<=80)
148 return true;
149
150 if (!trim)
151 {
152 ostringstream sout;
153 sout << "Size " << sz << " of entry for key '" << key << "' exceeds 80 characters.";
154#ifdef __EXCEPTIONS
155 throw runtime_error(sout.str());
156#else
157 gLog << ___err___ << "ERROR - " << sout.str() << endl;
158#endif
159 return false;
160 }
161
162 //looks like something went wrong. Maybe entry is too long ?
163 //try to remove the comment
164 comment = "";
165
166 sz = CalcSize();
167 if (sz<=80)
168 {
169#ifndef __EXCEPTIONS
170 ostringstream sout;
171 sout << "Size " << sz << " of entry for key '" << key << "' exceeds 80 characters... removed comment.";
172 gLog << ___warn___ << "WARNING - " << sout.str() << endl;
173#endif
174 return true;
175 }
176
177 ostringstream sout;
178 sout << "Size " << sz << " of entry for key '" << key << "' exceeds 80 characters even without comment.";
179#ifdef __EXCEPTIONS
180 throw runtime_error(sout.str());
181#else
182 gLog << ___err___ << "ERROR - " << sout.str() << endl;
183 return false;
184#endif
185 }
186
187 size_t CalcSize() const
188 {
189 if (!delim)
190 return 10+comment.size();
191
192 return 10 + (value.size()<20?20:value.size()) + 3 + comment.size();
193 }
194
195 string Compile()
196 {
197
198 if (fitsString != "")
199 return fitsString;
200
201 ostringstream sout;
202 sout << std::left << setw(8) << key;
203
204 if (!delim)
205 {
206 sout << " " << comment;
207 return sout.str();
208 }
209
210 sout << "= ";
211 sout << (!value.empty() && value[0]=='\''?std::left:std::right);
212 sout << setw(20) << value << std::left;
213
214 if (!comment.empty())
215 sout << " / " << comment;
216
217 return sout.str();
218 }
219
220 Checksum checksum;
221
222 void Out(ofstream &fout)
223 {
224 if (!changed)
225 return;
226
227 string str = Compile();
228 str.insert(str.end(), 80-str.size(), ' ');
229
230 if (offset==0)
231 offset = fout.tellp();
232
233 //cout << "Write[" << offset << "]: " << key << "/" << value << endl;
234
235 fout.seekp(offset);
236 fout << str;
237
238 checksum.reset();
239 checksum.add(str.c_str(), 80);
240
241 changed = false;
242 }
243 /*
244 void Out(ostream &out)
245 {
246 string str = Compile();
247
248 str.insert(str.end(), 80-str.size(), ' ');
249
250 out << str;
251 changed = false;
252 }*/
253 };
254
255private:
256 vector<Key> fKeys;
257
258 vector<Key>::iterator findkey(const string &key)
259 {
260 for (auto it=fKeys.begin(); it!=fKeys.end(); it++)
261 if (key==it->key)
262 return it;
263
264 return fKeys.end();
265 }
266
267 bool Set(const string &key="", bool delim=false, const string &value="", const string &comment="")
268 {
269 // If no delimit add the row no matter if it alread exists
270 if (delim)
271 {
272 // if the row already exists: update it
273 auto it = findkey(key);
274 if (it!=fKeys.end())
275 {
276 it->value = value;
277 it->changed = true;
278 return true;
279 }
280 }
281
282 if (fTable.num_rows>0)
283 {
284 ostringstream sout;
285 sout << "No new header key can be defined, rows were already written to the file... ignoring new key '" << key << "'";
286#ifdef __EXCEPTIONS
287 throw runtime_error(sout.str());
288#else
289 gLog << ___err___ << "ERROR - " << sout.str() << endl;
290 return false;
291#endif
292 }
293
294 Key entry;
295
296 entry.key = key;
297 entry.delim = delim;
298 entry.value = value;
299 entry.comment = comment;
300 entry.offset = 0;
301 entry.changed = true;
302
303 if (!entry.check(fCommentTrimming))
304 return false;
305
306 fKeys.push_back(entry);
307 return true;
308 }
309
310protected:
311 struct Table
312 {
313 off_t offset;
314
315 size_t bytes_per_row;
316 size_t num_rows;
317 size_t num_cols;
318
319 struct Column
320 {
321 string name;
322 size_t offset;
323 size_t num;
324 size_t size;
325 char type;
326 };
327
328 vector<Column> cols;
329
330 Table() : offset(0), bytes_per_row(0), num_rows(0), num_cols(0)
331 {
332 }
333 };
334
335
336 Table fTable;
337
338 vector<char> fOutputBuffer;
339
340 vector<Table::Column>::iterator findcol(const string &name)
341 {
342 for (auto it=fTable.cols.begin(); it!=fTable.cols.end(); it++)
343 if (name==it->name)
344 return it;
345
346 return fTable.cols.end();
347 }
348
349 Checksum fDataSum;
350 Checksum fHeaderSum;
351
352 bool fCommentTrimming;
353 bool fManualExtName;
354
355public:
356 ofits() : fCommentTrimming(false),
357 fManualExtName(false)
358 {
359 }
360 ofits(const char *fname) : ofstream(),
361 fCommentTrimming(false),
362 fManualExtName(false)
363 {
364 this->open(fname);
365 }
366 virtual ~ofits() { if (is_open()) close(); }
367
368 virtual void open(const char * filename, bool addEXTNAMEKey=true)
369 {
370 fDataSum = 0;
371 fHeaderSum = 0;
372
373 fTable = Table();
374 fKeys.clear();
375
376 SetStr("XTENSION", "BINTABLE", "binary table extension");
377 SetInt("BITPIX", 8, "8-bit bytes");
378 SetInt("NAXIS", 2, "2-dimensional binary table");
379 SetInt("NAXIS1", 0, "width of table in bytes");
380 SetInt("NAXIS2", 0, "number of rows in table");
381 SetInt("PCOUNT", 0, "size of special data area");
382 SetInt("GCOUNT", 1, "one data group (required keyword)");
383 SetInt("TFIELDS", 0, "number of fields in each row");
384 if (addEXTNAMEKey)
385 SetStr("EXTNAME", "", "name of extension table");
386 else
387 fManualExtName = true;
388 SetStr("CHECKSUM", "0000000000000000", "Checksum for the whole HDU");
389 SetStr("DATASUM", " 0", "Checksum for the data block");
390
391 ofstream::open(filename);
392 }
393
394 void AllowCommentsTrimming(bool allow)
395 {
396 fCommentTrimming = allow;
397 }
398 //Etienne: required to enable 1 to 1 reconstruction of files
399 bool SetKeyComment(const string& key, const string& comment)
400 {
401 auto it = findkey(key);
402 if (it==fKeys.end())
403 return false;
404 it->comment = comment;
405 it->changed = true;
406 return true;
407 }
408 bool SetKeyFromFitsString(const string& fitsString)
409 {
410 if (fTable.num_rows>0)
411 {
412 ostringstream sout;
413 sout << "No new header key can be defined, rows were already written to the file... ignoring new key '" << fitsString << "'";
414#ifdef __EXCEPTIONS
415 throw runtime_error(sout.str());
416#else
417 gLog << ___err___ << "ERROR - " << sout.str() << endl;
418 return false;
419#endif
420 }
421
422 Key entry;
423 entry.fitsString = fitsString;
424 entry.changed = true;
425 fKeys.push_back(entry);
426 return true;
427 }
428 bool SetRaw(const string &key, const string &val, const string &comment)
429 {
430 return Set(key, true, val, comment);
431 }
432
433 bool SetBool(const string &key, bool b, const string &comment="")
434 {
435 return Set(key, true, b?"T":"F", comment);
436 }
437
438 bool AddEmpty(const string &key, const string &comment="")
439 {
440 return Set(key, true, "", comment);
441 }
442
443 bool SetStr(const string &key, string s, const string &comment="")
444 {
445 for (uint i=0; i<s.length(); i++)
446 if (s[i]=='\'')
447 s.insert(i++, "\'");
448
449 return Set(key, true, "'"+s+"'", comment);
450 }
451
452 bool SetInt(const string &key, int64_t i, const string &comment="")
453 {
454 ostringstream sout;
455 sout << i;
456
457 return Set(key, true, sout.str(), comment);
458 }
459
460 bool SetFloat(const string &key, double f, int p, const string &comment="")
461 {
462 ostringstream sout;
463
464 if (p<0)
465 sout << setprecision(-p) << fixed;
466 if (p>0)
467 sout << setprecision(p);
468 if (p==0)
469 sout << setprecision(f>1e-100 && f<1e100 ? 15 : 14);
470
471 sout << f;
472
473 string str = sout.str();
474
475 replace(str.begin(), str.end(), 'e', 'E');
476
477 if (str.find_first_of('E')==string::npos && str.find_first_of('.')==string::npos)
478 str += ".";
479
480 return Set(key, true, str, comment);
481 }
482
483 bool SetFloat(const string &key, double f, const string &comment="")
484 {
485 return SetFloat(key, f, 0, comment);
486 }
487
488 bool SetHex(const string &key, uint64_t i, const string &comment="")
489 {
490 ostringstream sout;
491 sout << std::hex << "0x" << i;
492 return SetStr(key, sout.str(), comment);
493 }
494
495 bool AddComment(const string &comment)
496 {
497 return Set("COMMENT", false, "", comment);
498 }
499
500 bool AddHistory(const string &comment)
501 {
502 return Set("HISTORY", false, "", comment);
503 }
504
505 void End()
506 {
507 Set("END");
508 while (fKeys.size()%36!=0)
509 fKeys.emplace_back();
510 }
511
512 string CommentFromType(char type)
513 {
514 string comment;
515
516 switch (type)
517 {
518 case 'L': comment = "1-byte BOOL]"; break;
519 case 'A': comment = "1-byte CHAR]"; break;
520 case 'B': comment = "1-byte BOOL]"; break;
521 case 'I': comment = "2-byte INT]"; break;
522 case 'J': comment = "4-byte INT]"; break;
523 case 'K': comment = "8-byte INT]"; break;
524 case 'E': comment = "4-byte FLOAT]"; break;
525 case 'D': comment = "8-byte FLOAT]"; break;
526 case 'Q': comment = "var. Length]"; break;
527 }
528
529 return comment;
530 }
531
532 uint32_t SizeFromType(char type)
533 {
534 size_t size = 0;
535
536 switch (type)
537 {
538 case 'L': size = 1; break;
539 case 'A': size = 1; break;
540 case 'B': size = 1; break;
541 case 'I': size = 2; break;
542 case 'J': size = 4; break;
543 case 'K': size = 8; break;
544 case 'E': size = 4; break;
545 case 'D': size = 8; break;
546 case 'Q': size = 16; break;
547 }
548
549 return size;
550 }
551 //ETIENNE to be able to restore the file 1 to 1, I must restore the header keys myself
552 virtual bool AddColumn(uint32_t cnt, char typechar, const string &name, const string &unit, const string &comment="", bool addHeaderKeys=true)
553 {
554 if (tellp()<0)
555 {
556 ostringstream sout;
557 sout << "File not open... ignoring column '" << name << "'";
558#ifdef __EXCEPTIONS
559 throw runtime_error(sout.str());
560#else
561 gLog << ___err___ << "ERROR - " << sout.str() << endl;
562 return false;
563#endif
564 }
565
566 if (tellp()>0)
567 {
568 ostringstream sout;
569 sout << "Header already written, no new column can be defined... ignoring column '" << name << "'";
570#ifdef __EXCEPTIONS
571 throw runtime_error(sout.str());
572#else
573 gLog << ___err___ << "ERROR - " << sout.str() << endl;
574 return false;
575#endif
576 }
577
578 if (findcol(name)!=fTable.cols.end())
579 {
580 ostringstream sout;
581 sout << "A column with the name '" << name << "' already exists.";
582#ifdef __EXCEPTIONS
583 throw runtime_error(sout.str());
584#else
585 gLog << ___err___ << "ERROR - " << sout.str() << endl;
586 return false;
587#endif
588 }
589
590 typechar = toupper(typechar);
591
592 static const string allow("LABIJKEDQ");
593 if (std::find(allow.begin(), allow.end(), typechar)==allow.end())
594 {
595 ostringstream sout;
596 sout << "Column type '" << typechar << "' not supported.";
597#ifdef __EXCEPTIONS
598 throw runtime_error(sout.str());
599#else
600 gLog << ___err___ << "ERROR - " << sout.str() << endl;
601 return false;
602#endif
603 }
604
605 ostringstream type;
606 type << cnt;
607 if (typechar=='Q')
608 type << "QB";
609 else
610 type << typechar;
611
612 fTable.num_cols++;
613
614 ostringstream typekey, formkey, unitkey, unitcom, typecom;
615 typekey << "TTYPE" << fTable.num_cols;
616 formkey << "TFORM" << fTable.num_cols;
617 unitkey << "TUNIT" << fTable.num_cols;
618 unitcom << "unit of " << name;
619
620 typecom << "format of " << name << " [";
621
622 typecom << CommentFromType(typechar);
623
624 if (addHeaderKeys)
625 {
626 SetStr(formkey.str(), type.str(), typecom.str());
627 SetStr(typekey.str(), name, comment);
628 if (!unit.empty())
629 SetStr(unitkey.str(), unit, unitcom.str());
630 }
631 size_t size = SizeFromType(typechar);
632
633 Table::Column col;
634
635 col.name = name;
636 col.type = typechar;
637 col.num = cnt;
638 col.size = size;
639 col.offset = fTable.bytes_per_row;
640
641 fTable.cols.push_back(col);
642
643 fTable.bytes_per_row += size*cnt;
644
645 // Align to four bytes
646 fOutputBuffer.resize(fTable.bytes_per_row + 4-fTable.bytes_per_row%4);
647
648 return true;
649 }
650
651 virtual bool AddColumn(uint32_t cnt, char typechar, const string& name, const string& unit, const BlockHeaderWriter& header, const string& comment="", bool addHeaderKeys=true)
652 {
653 return AddColumn(cnt, typechar, name, unit, comment, addHeaderKeys);
654 }
655 virtual bool AddColumn(const string& compressionScheme, uint32_t cnt, char typechar, const string& name, const string& unit, const string& comment="", bool addHeaderKeys=true)
656 {
657 if (compressionScheme != "" &&
658 compressionScheme != "RAW")
659 {
660#ifdef __EXCEPTIONS
661 throw runtime_error("Trying to add a compressed column to an uncompressed file");
662#else
663 gLog << ___err___ << "ERROR - Trying to add a compressed column to an uncompressed file" << endl;
664 return false;
665#endif
666 }
667 return AddColumn(cnt, typechar, name, unit, comment, addHeaderKeys);
668 }
669
670 bool AddColumnShort(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
671 { return AddColumn(cnt, 'I', name, unit, comment); }
672 bool AddColumnInt(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
673 { return AddColumn(cnt, 'J', name, unit, comment); }
674 bool AddColumnLong(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
675 { return AddColumn(cnt, 'K', name, unit, comment); }
676 bool AddColumnFloat(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
677 { return AddColumn(cnt, 'E', name, unit, comment); }
678 bool AddColumnDouble(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
679 { return AddColumn(cnt, 'D', name, unit, comment); }
680 bool AddColumnChar(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
681 { return AddColumn(cnt, 'A', name, unit, comment); }
682 bool AddColumnByte(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
683 { return AddColumn(cnt, 'B', name, unit, comment); }
684 bool AddColumnBool(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
685 { return AddColumn(cnt, 'L', name, unit, comment); }
686
687 bool AddColumnShort(const string &name, const string &unit="", const string &comment="")
688 { return AddColumn(1, 'I', name, unit, comment); }
689 bool AddColumnInt(const string &name, const string &unit="", const string &comment="")
690 { return AddColumn(1, 'J', name, unit, comment); }
691 bool AddColumnLong(const string &name, const string &unit="", const string &comment="")
692 { return AddColumn(1, 'K', name, unit, comment); }
693 bool AddColumnFloat(const string &name, const string &unit="", const string &comment="")
694 { return AddColumn(1, 'E', name, unit, comment); }
695 bool AddColumnDouble(const string &name, const string &unit="", const string &comment="")
696 { return AddColumn(1, 'D', name, unit, comment); }
697 bool AddColumnChar(const string &name, const string &unit="", const string &comment="")
698 { return AddColumn(1, 'A', name, unit, comment); }
699 bool AddColumnByte(const string &name, const string &unit="", const string &comment="")
700 { return AddColumn(1, 'B', name, unit, comment); }
701 bool AddColumnBool(const string &name, const string &unit="", const string &comment="")
702 { return AddColumn(1, 'L', name, unit, comment); }
703
704 /*
705 bool AddKey(string key, double d, const string &comment)
706 {
707 ostringstream out;
708 out << d;
709
710 string s = out.str();
711
712 replace(s.begin(), s.end(), "e", "E");
713
714 return AddKey(key, s, comment);
715 }*/
716
717
718 Checksum WriteHeader(ofstream &fout)
719 {
720 Checksum sum;
721 uint32_t count=0;
722 for (auto it=fKeys.begin(); it!=fKeys.end(); it++)
723 {
724 it->Out(fout);
725 sum += it->checksum;
726 count++;
727 }
728 fout.flush();
729
730 return sum;
731 }
732
733 Checksum WriteHeader()
734 {
735 return WriteHeader(*this);
736 }
737
738 void FlushHeader()
739 {
740 const off_t pos = tellp();
741 WriteHeader();
742 seekp(pos);
743 }
744
745 Checksum WriteFitsHeader()
746 {
747 ofits h;
748
749 h.SetBool("SIMPLE", true, "file does conform to FITS standard");
750 h.SetInt ("BITPIX", 8, "number of bits per data pixel");
751 h.SetInt ("NAXIS", 0, "number of data axes");
752 h.SetBool("EXTEND", true, "FITS dataset may contain extensions");
753 h.SetStr ("CHECKSUM","0000000000000000", "Checksum for the whole HDU");
754 h.SetStr ("DATASUM", " 0", "Checksum for the data block");
755 h.AddComment("FITS (Flexible Image Transport System) format is defined in 'Astronomy");
756 h.AddComment("and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H");
757 h.End();
758
759 const Checksum sum = h.WriteHeader(*this);
760
761 h.SetStr("CHECKSUM", sum.str());
762
763 const size_t offset = tellp();
764 h.WriteHeader(*this);
765 seekp(offset);
766
767 return sum;
768 }
769
770 virtual bool WriteDrsOffsetsTable ()
771 {
772 return true;
773 }
774
775 virtual bool WriteCatalog()
776 {
777 return true;
778 }
779
780 virtual bool WriteTableHeader(const char *name="DATA")
781 {
782 if (tellp()>0)
783 {
784#ifdef __EXCEPTIONS
785 throw runtime_error("File not empty anymore.");
786#else
787 gLog << ___err___ << "ERROR - File not empty anymore." << endl;
788 return false;
789#endif
790 }
791
792 fHeaderSum = WriteFitsHeader();
793
794 WriteDrsOffsetsTable();
795
796 if (!fManualExtName)
797 SetStr("EXTNAME", name);
798 SetInt("NAXIS1", fTable.bytes_per_row);
799 SetInt("TFIELDS", fTable.cols.size());
800
801 End();
802
803 WriteHeader();
804
805 WriteCatalog();
806
807 return good();
808 }
809
810 template<size_t N>
811 void revcpy(char *dest, const char *src, int num)
812 {
813 const char *pend = src + num*N;
814 for (const char *ptr = src; ptr<pend; ptr+=N, dest+=N)
815 reverse_copy(ptr, ptr+N, dest);
816 }
817
818 virtual uint32_t GetBytesPerRow() const { return fTable.bytes_per_row; }
819
820 virtual bool WriteRow(const void *ptr, size_t cnt, bool byte_swap=true)
821 {
822 // FIXME: Make sure that header was already written
823 // or write header now!
824 if (cnt!=fTable.bytes_per_row)
825 {
826 ostringstream sout;
827 sout << "WriteRow - Size " << cnt << " does not match expected size " << fTable.bytes_per_row;
828#ifdef __EXCEPTIONS
829 throw runtime_error(sout.str());
830#else
831 gLog << ___err___ << "ERROR - " << sout.str() << endl;
832 return false;
833#endif
834 }
835
836 // For the checksum we need everything to be correctly aligned
837 const uint8_t offset = fTable.offset%4;
838
839 char *buffer = fOutputBuffer.data() + offset;
840
841 auto ib = fOutputBuffer.begin();
842 auto ie = fOutputBuffer.rbegin();
843 *ib++ = 0;
844 *ib++ = 0;
845 *ib++ = 0;
846 *ib = 0;
847
848 *ie++ = 0;
849 *ie++ = 0;
850 *ie++ = 0;
851 *ie = 0;
852
853 if (!byte_swap)
854 memcpy(buffer, ptr, cnt);
855 else
856 {
857 for (auto it=fTable.cols.begin(); it!=fTable.cols.end(); it++)
858 {
859 const char *src = reinterpret_cast<const char*>(ptr) + it->offset;
860 char *dest = buffer + it->offset;
861
862 // Let the compiler do some optimization by
863 // knowing the we only have 1, 2, 4 and 8
864 switch (it->size)
865 {
866 case 1: memcpy (dest, src, it->num*it->size); break;
867 case 2: revcpy<2>(dest, src, it->num); break;
868 case 4: revcpy<4>(dest, src, it->num); break;
869 case 8: revcpy<8>(dest, src, it->num); break;
870 }
871 }
872 }
873
874 write(buffer, cnt);
875 fDataSum.add(fOutputBuffer);
876
877 fTable.num_rows++;
878 fTable.offset += cnt;
879 return good();
880 }
881
882 template<typename N>
883 bool WriteRow(const vector<N> &vec)
884 {
885 return WriteRow(vec.data(), vec.size()*sizeof(N));
886 }
887
888 // Flushes the number of rows to the header on disk
889 virtual void FlushNumRows()
890 {
891 SetInt("NAXIS2", fTable.num_rows);
892 FlushHeader();
893 }
894
895 size_t GetNumRows() const { return fTable.num_rows; }
896
897 void AlignTo2880Bytes()
898 {
899 if (tellp()%(80*36)>0)
900 {
901 vector<char> filler(80*36-tellp()%(80*36));
902 write(filler.data(), filler.size());
903 }
904 }
905
906 Checksum UpdateHeaderChecksum()
907 {
908
909 ostringstream dataSumStr;
910 dataSumStr << fDataSum.val();
911 SetStr("DATASUM", dataSumStr.str());
912
913 const Checksum sum = WriteHeader();
914
915 //sum += headersum;
916
917 SetStr("CHECKSUM", (sum+fDataSum).str());
918
919 return WriteHeader();
920 }
921 virtual bool close()
922 {
923 if (tellp()<0)
924 return false;
925
926 AlignTo2880Bytes();
927
928 // We don't have to jump back to the end of the file
929 SetInt("NAXIS2", fTable.num_rows);
930
931
932 const Checksum chk = UpdateHeaderChecksum();
933
934 ofstream::close();
935
936 if ((chk+fDataSum).valid())
937 return true;
938
939 ostringstream sout;
940 sout << "Checksum (" << std::hex << chk.val() << ") invalid.";
941#ifdef __EXCEPTIONS
942 throw runtime_error(sout.str());
943#else
944 gLog << ___err___ << "ERROR - " << sout.str() << endl;
945 return false;
946#endif
947 }
948
949 pair<string, int> GetChecksumData()
950 {
951 string datasum;
952 string checksum;
953 //cannot directly use the Get methods, because they are only in fits.h
954 for (vector<Key>::const_iterator it=fKeys.begin(); it!= fKeys.end(); it++)
955 {
956 if (it->key == "CHECKSUM") checksum = it->value;
957 if (it->key == "DATASUM") datasum = it->value;
958 }
959 if (checksum[0] == '\'')
960 checksum = checksum.substr(1,checksum.size()-2);
961 if (datasum[0] == '\'')
962 datasum = datasum.substr(1, datasum.size()-2);
963 return make_pair(checksum, atoi(datasum.c_str()));
964 }
965};
966
967#ifndef __MARS__
968};
969#endif
970
971#if 0
972#include "fits.h"
973
974int main()
975{
976 using namespace std;
977
978 ofits h2("delme.fits");
979
980 h2.SetInt("KEY1", 1, "comment 1");
981 h2.AddColumnInt(2, "testcol1", "counts", "My comment");
982 h2.AddColumnInt("testcol2", "counts", "My comment");
983 //h2.AddColumnInt("testcol2", "counts", "My comment");
984 h2.SetInt("KEY2", 2, "comment 2");
985
986 /*
987 AddFloat("X0", 0.000123456, "number of fields in each row");
988 AddFloat("X1", 0, "number of fields in each row");
989 AddFloat("X2", 12345, "number of fields in each row");
990 AddFloat("X3", 123456.67890, "number of fields in each row");
991 AddFloat("X4", 1234567890123456789.12345678901234567890, "number of fields in each row");
992 AddFloat("X5", 1234567890.1234567890e20, "number of fields in each row");
993 AddFloat("X6", 1234567890.1234567890e-20, "number of fields in each row");
994 AddFloat("XB", 1234567890.1234567890e-111, "number of fields in each row");
995 AddFloat("X7", 1e-5, "number of fields in each row");
996 AddFloat("X8", 1e-6, "number of fields in each row");
997 //AddStr("12345678", "123456789012345678", "12345678901234567890123456789012345678901234567");
998 */
999 // -
1000
1001 h2.WriteTableHeader("TABLE_NAME");
1002
1003 for (int i=0; i<10; i++)
1004 {
1005 int j[3] = { i+10, i*10, i*100 };
1006 h2.WriteRow(j, 3*sizeof(i));
1007 }
1008
1009 //h2.AddColumnInt("testcol2", "counts", "My comment");
1010 //h2.SetInt("KEY3", 2, "comment 2");
1011 h2.SetInt("KEY2", 2, "comment 2xxx");
1012 h2.SetInt("KEY1", 11);
1013
1014 h2.close();
1015
1016 cout << "---" << endl;
1017
1018 fits f("delme.fits");
1019 if (!f)
1020 throw runtime_error("xxx");
1021
1022 cout << "Header is valid: " << f.IsHeaderOk() << endl;
1023
1024 while (f.GetNextRow());
1025
1026 cout << "File is valid: " << f.IsFileOk() << endl;
1027
1028 cout << "---" << endl;
1029
1030 return 0;
1031}
1032#endif
1033
1034#endif
Note: See TracBrowser for help on using the repository browser.