source: trunk/Mars/mcore/fits.h@ 20040

Last change on this file since 20040 was 19856, checked in by tbretz, 5 years ago
Also the include of unordered_map only needs to be secured for ROOTv5 CINT.
File size: 32.3 KB
Line 
1#ifndef MARS_fits
2#define MARS_fits
3
4#include <stdint.h>
5
6#include <map>
7#include <string>
8#include <fstream>
9#if defined(R__DICTIONARY_FILENAME) && defined(__clang__)
10#undef private
11#endif
12#include <sstream>
13#include <algorithm>
14#include <stdexcept>
15
16#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
17
18// ROOT_VERSION is not defined in ROOTv5, but special treatment is
19// needed depending on whether this is to create the disctionary or not
20// For ROOTv6 now either __CLING__ (dirctionary) or __ROOTv6__ (compiler)
21// should be defined.
22#if defined(ROOT_VERSION_CODE)
23#if ROOT_VERSION_CODE >= ROOT_VERSION(6,00,0)
24#define __ROOTv6__ 1
25#endif
26#endif
27
28#if !defined(__CINT__) || defined(__CLING__) || defined(__ROOTv6__)
29#include <unordered_map>
30#else
31#define off_t size_t
32namespace std
33{
34 template<class T, class S> class unordered_map<T, S>;
35}
36#endif
37
38#if defined(__MARS__) || defined(__CINT__) || defined (__CLING__) || defined (__ROOTv6__)
39#include "MLog.h"
40#include "MLogManip.h"
41#define ___err___ err
42#define ___warn___ warn
43#define ___all___ all
44#else
45#include <vector>
46#include <iomanip>
47#include <iostream>
48#ifndef gLog
49#define gLog std::cerr
50#define ___err___ ""
51#define ___warn___ ""
52#define ___all___ ""
53#endif
54#endif
55
56#if defined(HAVE_ZLIB) || defined(__CINT__) || defined(__CLING__) || defined (__ROOTv6__)
57#include "izstream.h"
58#else
59#include <fstream>
60#define izstream std::ifstream
61#warning Support for zipped FITS files disabled.
62#endif
63
64#include "FITS.h"
65#include "checksum.h"
66
67class fits : public izstream
68{
69public:
70 //I know I know, you're going to yiell that this does not belong here.
71 //It will belong in the global scope eventually, and it makes the coding of zfits much simpler this way.
72 enum Compression_t
73 {
74 kCompUnknown,
75 kCompFACT
76 };
77
78 enum fitsstate
79 {
80 throwbit = 8
81 };
82
83 struct Entry
84 {
85 char type;
86 std::string value;
87 std::string comment;
88 std::string fitsString;
89
90 template<typename T>
91 T Get() const
92 {
93 T t;
94
95 std::istringstream str(value);
96 str >> t;
97
98 return t;
99 }
100
101 /* Specialization needs to be outside namespace-scope, see below
102 template<>
103 std::string fits::Entry::Get<std::string>() const
104 {
105 return value;
106 }*/
107 };
108
109 struct Table
110 {
111 off_t offset;
112
113 bool is_compressed;
114
115 std::string name;
116 size_t bytes_per_row;
117 size_t num_rows;
118 size_t num_cols;
119 size_t total_bytes; // NAXIS1*NAXIS2
120
121 struct Column
122 {
123 size_t id;
124 size_t offset;
125 size_t num;
126 size_t size;
127 size_t bytes; // num*size
128 char type;
129 std::string unit;
130 std::string comment;
131 Compression_t comp;
132 };
133
134 typedef std::map<std::string, Entry> Keys;
135 typedef std::map<std::string, Column> Columns;
136 typedef std::vector<Column> SortedColumns;
137
138 Columns cols;
139 SortedColumns sorted_cols;
140 Keys keys;
141
142 int64_t datasum;
143
144 std::string Trim(const std::string &str, char c=' ') const
145 {
146 // Trim Both leading and trailing spaces
147 const size_t pstart = str.find_first_not_of(c); // Find the first character position after excluding leading blank spaces
148 const size_t pend = str.find_last_not_of(c); // Find the first character position from reverse af
149
150 // if all spaces or empty return an empty string
151 if (std::string::npos==pstart || std::string::npos==pend)
152 return std::string();
153
154 return str.substr(pstart, pend-pstart+1);
155 }
156
157 bool Check(const std::string &key, char type, const std::string &value="") const
158 {
159 const Keys::const_iterator it = keys.find(key);
160 if (it==keys.end())
161 {
162 std::ostringstream str;
163 str << "Key '" << key << "' not found.";
164#ifdef __EXCEPTIONS
165 throw std::runtime_error(str.str());
166#else
167 gLog << ___err___ << "ERROR - " << str.str() << std::endl;
168 return false;
169#endif
170 }
171
172 if (it->second.type!=type)
173 {
174 std::ostringstream str;
175 str << "Wrong type for key '" << key << "': expected " << type << ", found " << it->second.type << ".";
176#ifdef __EXCEPTIONS
177 throw std::runtime_error(str.str());
178#else
179 gLog << ___err___ << "ERROR - " << str.str() << std::endl;
180 return false;
181#endif
182 }
183
184 if (!value.empty() && it->second.value!=value)
185 {
186 std::ostringstream str;
187 str << "Wrong value for key '" << key << "': expected " << value << ", found " << it->second.value << ".";
188#ifdef __EXCEPTIONS
189 throw std::runtime_error(str.str());
190#else
191 gLog << ___err___ << "ERROR - " << str.str() << std::endl;
192 return false;
193#endif
194 }
195
196 return true;
197 }
198
199 Keys ParseBlock(const std::vector<std::string> &vec) const
200 {
201 Keys rc;
202
203 for (unsigned int i=0; i<vec.size(); i++)
204 {
205 const std::string key = Trim(vec[i].substr(0,8));
206 // Keywords without a value, like COMMENT / HISTORY
207 if (vec[i].substr(8,2)!="= ")
208 continue;
209
210 char type = 0;
211
212 std::string com;
213 std::string val = Trim(vec[i].substr(10));
214
215 if (val[0]=='\'')
216 {
217 // First skip all '' in the string
218 size_t p = 1;
219 while (1)
220 {
221 const size_t pp = val.find_first_of('\'', p);
222 if (pp==std::string::npos)
223 break;
224
225 p = val[pp+1]=='\'' ? pp+2 : pp+1;
226 }
227
228 // Now find the comment
229 const size_t ppp = val.find_first_of('/', p);
230
231 // Set value, comment and type
232 // comments could be just spaces. take care of this.
233 if (ppp!=std::string::npos && val.size()!=ppp+1)
234 com = Trim(val.substr(ppp+1));
235
236 val = Trim(val.substr(1, p-2));
237 type = 'T';
238 }
239 else
240 {
241 const size_t p = val.find_first_of('/');
242
243 if (p!=std::string::npos && val.size()!=p+1)
244 com = Trim(val.substr(p+2));
245
246 val = Trim(val.substr(0, p));
247
248 if (val.empty() || val.find_first_of('T')!=std::string::npos || val.find_first_of('F')!=std::string::npos)
249 type = 'B';
250 else
251 type = val.find_last_of('.')==std::string::npos ? 'I' : 'F';
252 }
253
254 const Entry e = { type, val, com, vec[i] };
255
256 rc[key] = e;
257 }
258
259 return rc;
260 }
261
262 Table() : offset(0), is_compressed(false) { }
263 Table(const std::vector<std::string> &vec, off_t off) : offset(off),
264 keys(ParseBlock(vec))
265 {
266 is_compressed = HasKey("ZTABLE") && Check("ZTABLE", 'B', "T");
267
268 if (!Check("XTENSION", 'T', "BINTABLE") ||
269 !Check("NAXIS", 'I', "2") ||
270 !Check("BITPIX", 'I', "8") ||
271 !Check("GCOUNT", 'I', "1") ||
272 !Check("EXTNAME", 'T') ||
273 !Check("NAXIS1", 'I') ||
274 !Check("NAXIS2", 'I') ||
275 !Check("TFIELDS", 'I'))
276 return;
277
278 if (is_compressed)
279 {
280 if (!Check("ZNAXIS1", 'I') ||
281 !Check("ZNAXIS2", 'I') ||
282 !Check("ZPCOUNT", 'I', "0"))
283 return;
284 }
285 else
286 {
287 if (!Check("PCOUNT", 'I', "0"))
288 return;
289 }
290
291 total_bytes = Get<size_t>("NAXIS1")*Get<size_t>("NAXIS2");
292 bytes_per_row = is_compressed ? Get<size_t>("ZNAXIS1") : Get<size_t>("NAXIS1");
293 num_rows = is_compressed ? Get<size_t>("ZNAXIS2") : Get<size_t>("NAXIS2");
294 num_cols = Get<size_t>("TFIELDS");
295 datasum = Get<int64_t>("DATASUM", -1);
296 size_t bytes = 0;
297
298 const std::string tFormName = is_compressed ? "ZFORM" : "TFORM";
299 for (size_t i=1; i<=num_cols; i++)
300 {
301 const std::string num(std::to_string(i));
302
303 if (!Check("TTYPE"+num, 'T') ||
304 !Check(tFormName+num, 'T'))
305 return;
306
307 const std::string id = Get<std::string>("TTYPE"+num);
308 const std::string fmt = Get<std::string>(tFormName+num);
309 const std::string unit = Get<std::string>("TUNIT"+num, "");
310 const std::string comp = Get<std::string>("ZCTYP"+num, "");
311 const std::string comm = Get<std::string>("TCOMM"+num, "");
312
313 Compression_t compress = kCompUnknown;
314 if (comp == "FACT")
315 compress = kCompFACT;
316
317 std::istringstream sin(fmt);
318 size_t n = 0;
319 sin >> n;
320 if (!sin)
321 n = 1;
322
323 const char type = fmt[fmt.length()-1];
324
325 size_t size = 0;
326 switch (type)
327 {
328 // We could use negative values to mark floats
329 // otheriwse we could just cast them to int64_t?
330 case 'L': // logical
331 case 'A': // char
332 case 'B': size = 1; break; // byte
333 case 'I': size = 2; break; // short
334 case 'J': size = 4; break; // int
335 case 'K': size = 8; break; // long long
336 case 'E': size = 4; break; // float
337 case 'D': size = 8; break; // double
338 // case 'X': size = n; break; // bits (n=number of bytes needed to contain all bits)
339 // case 'C': size = 8; break; // complex float
340 // case 'M': size = 16; break; // complex double
341 // case 'P': size = 8; break; // array descriptor (32bit)
342 // case 'Q': size = 16; break; // array descriptor (64bit)
343 default:
344 {
345 std::ostringstream str;
346 str << "FITS format TFORM='" << fmt << "' not yet supported.";
347#ifdef __EXCEPTIONS
348 throw std::runtime_error(str.str());
349#else
350 gLog << ___err___ << "ERROR - " << str.str() << std::endl;
351 return;
352#endif
353 }
354 }
355
356 const Table::Column col = { i, bytes, n, size, n*size, type, unit, comm, compress };
357
358 cols[id] = col;
359 sorted_cols.emplace_back(col);
360 bytes += n*size;
361 }
362
363 if (bytes!=bytes_per_row)
364 {
365 std::ostringstream str;
366 str << "Sum of bytes in columns [" << bytes << "] does not match (Z)NAXIS2 [" << bytes_per_row << "].";
367
368#ifdef __EXCEPTIONS
369 throw std::runtime_error(str.str());
370#else
371 gLog << ___err___ << "ERROR - " << str.str() << std::endl;
372 return;
373#endif
374 }
375
376 name = Get<std::string>("EXTNAME");
377 }
378
379 void PrintKeys(bool display_all=false) const
380 {
381 for (Keys::const_iterator it=keys.cbegin(); it!=keys.cend(); it++)
382 {
383 if (!display_all && FITS::IsReservedKeyWord(it->first))
384 continue;
385
386 gLog << ___all___ << std::setw(2) << it->second.type << '|' << it->first << '=' << it->second.value << '/' << it->second.comment << '|' << std::endl;
387 }
388 }
389
390 void PrintColumns() const
391 {
392 typedef std::map<std::pair<size_t, std::string>, Column> Sorted;
393
394 Sorted sorted;
395
396 for (Columns::const_iterator it=cols.cbegin(); it!=cols.cend(); it++)
397 sorted[std::make_pair(it->second.offset, it->first)] = it->second;
398
399 for (Sorted::const_iterator it=sorted.cbegin(); it!=sorted.cend(); it++)
400 {
401 gLog << ___all___ << std::setw(6) << it->second.offset << "| ";
402 gLog << it->second.num << 'x';
403 switch (it->second.type)
404 {
405 case 'A': gLog << "char(8)"; break;
406 case 'L': gLog << "bool(8)"; break;
407 case 'B': gLog << "byte(8)"; break;
408 case 'I': gLog << "short(16)"; break;
409 case 'J': gLog << "int(32)"; break;
410 case 'K': gLog << "int(64)"; break;
411 case 'E': gLog << "float(32)"; break;
412 case 'D': gLog << "double(64)"; break;
413 }
414 gLog << ": " << it->first.second << " [" << it->second.unit << "]" << std::endl;
415 }
416 }
417
418 operator bool() const { return !name.empty(); }
419
420 bool HasKey(const std::string &key) const
421 {
422 return keys.find(key)!=keys.end();
423 }
424
425 bool HasColumn(const std::string& col) const
426 {
427 return cols.find(col)!=cols.end();
428 }
429
430 const Columns &GetColumns() const
431 {
432 return cols;
433 }
434
435 const Keys &GetKeys() const
436 {
437 return keys;
438 }
439
440 // Values of keys are always signed
441 template<typename T>
442 T Get(const std::string &key) const
443 {
444 const Keys::const_iterator it = keys.find(key);
445 if (it==keys.end())
446 {
447 std::ostringstream str;
448 str << "Key '" << key << "' not found.";
449#ifdef __EXCEPTIONS
450 throw std::runtime_error(str.str());
451#else
452 gLog << ___err___ << "ERROR - " << str.str() << std::endl;
453 return T();
454#endif
455 }
456 return it->second.Get<T>();
457 }
458
459 // Values of keys are always signed
460 template<typename T>
461 T Get(const std::string &key, const T &deflt) const
462 {
463 const Keys::const_iterator it = keys.find(key);
464 return it==keys.end() ? deflt :it->second.Get<T>();
465 }
466
467 size_t GetN(const std::string &key) const
468 {
469 const Columns::const_iterator it = cols.find(key);
470 return it==cols.end() ? 0 : it->second.num;
471 }
472
473
474
475 // There may be a gap between the main table and the start of the heap:
476 // this computes the offset
477 streamoff GetHeapShift() const
478 {
479 if (!HasKey("ZHEAPPTR"))
480 return 0;
481
482 const size_t shift = Get<size_t>("ZHEAPPTR");
483 return shift <= total_bytes ? 0 : shift - total_bytes;
484 }
485
486 // return total number of bytes 'all inclusive'
487 streamoff GetTotalBytes() const
488 {
489 //get offset of special data area from start of main table
490 const streamoff shift = GetHeapShift();
491
492 //and special data area size
493 const streamoff size = HasKey("PCOUNT") ? Get<streamoff>("PCOUNT") : 0;
494
495 // Get the total size
496 const streamoff total = total_bytes + size + shift;
497
498 // check for padding
499 if (total%2880==0)
500 return total;
501
502 // padding necessary
503 return total + (2880 - (total%2880));
504 }
505 };
506
507 void Exception(const std::string &txt)
508 {
509#ifdef __EXCEPTIONS
510 if (exceptions()&throwbit)
511 throw std::runtime_error(txt);
512#endif
513 gLog << ___err___ << "ERROR - " << txt << std::endl;
514 }
515
516 // Public for the root dictionary
517 typedef std::pair<void*, Table::Column> Address;
518 typedef std::vector<Address> Addresses;
519 typedef std::unordered_map<std::string, void*> Pointers;
520
521protected:
522 std::ofstream fCopy;
523 std::vector<std::string> fListOfTables; // List of skipped tables. Last table is open table
524
525 Table fTable;
526
527 //map<void*, Table::Column> fAddresses;
528 Addresses fAddresses;
529
530 Pointers fPointers;
531
532 std::vector<std::vector<char>> fGarbage;
533
534 std::vector<char> fBufferRow;
535 std::vector<char> fBufferDat;
536
537 size_t fRow;
538
539 Checksum fChkHeader;
540 Checksum fChkData;
541
542 bool ReadBlock(std::vector<std::string> &vec)
543 {
544 int endtag = 0;
545 for (int i=0; i<36; i++)
546 {
547 char c[81];
548 c[80] = 0;
549 read(c, 80);
550 if (!good())
551 break;
552
553 fChkHeader.add(c, 80);
554
555// if (c[0]==0)
556// return vector<string>();
557
558 std::string str(c);
559
560// if (!str.empty())
561// cout << setw(2) << i << "|" << str << "|" << (endtag?'-':'+') << endl;
562
563 if (endtag==2 || str=="END ")
564 {
565 endtag = 2; // valid END tag found
566 continue;
567 }
568
569 if (endtag==1 || str==" ")
570 {
571 endtag = 1; // end tag not found, but expected to be there
572 continue;
573 }
574
575 vec.emplace_back(str);
576 }
577
578 // Make sure that no empty vector is returned
579 if (endtag && vec.size()%36==0)
580 vec.emplace_back("END = '' / ");
581
582 return endtag==2;
583 }
584
585 std::string Compile(const std::string &key, int16_t i=-1) const
586 {
587#if GCC_VERSION < 40603
588 return i<0 ? key : key+std::to_string((long long int)(i));
589#else
590 return i<0 ? key : key+std::to_string(i);
591#endif
592 }
593
594 void Constructor(const std::string &fname, std::string fout="", const std::string& tableName="", bool force=false)
595 {
596 char simple[10];
597 read(simple, 10);
598 if (!good())
599 return;
600
601 EnableAddressExceptions();
602
603 if (memcmp(simple, "SIMPLE = ", 10))
604 {
605 clear(rdstate()|std::ios::badbit);
606#ifdef __EXCEPTIONS
607 throw std::runtime_error("File is not a FITS file.");
608#else
609 gLog << ___err___ << "ERROR - File is not a FITS file." << std::endl;
610 return;
611#endif
612 }
613
614 seekg(0);
615
616 while (good())
617 {
618 std::vector<std::string> block;
619 while (1)
620 {
621 // If we search for a table, we implicitly assume that
622 // not finding the table is not an error. The user
623 // can easily check that by eof() && !bad()
624 peek();
625 if (eof() && !bad() && !tableName.empty())
626 {
627 break;
628 }
629 // FIXME: Set limit on memory consumption
630 const int rc = ReadBlock(block);
631 if (!good())
632 {
633 clear(rdstate()|std::ios::badbit);
634#ifdef __EXCEPTIONS
635 throw std::runtime_error("FITS file corrupted.");
636#else
637 gLog << ___err___ << "ERROR - FITS file corrupted." << std::endl;
638 return;
639#endif
640 }
641
642 if (block.size()%36)
643 {
644 if (!rc && !force)
645 {
646 clear(rdstate()|std::ios::badbit);
647#ifdef __EXCEPTIONS
648 throw std::runtime_error("END keyword missing in FITS header.");
649#else
650 gLog << ___err___ << "ERROR - END keyword missing in FITS file... file might be corrupted." << std::endl;
651 return;
652#endif
653 }
654 break;
655 }
656 }
657
658 if (block.empty())
659 break;
660
661 if (block[0].substr(0, 9)=="SIMPLE =")
662 {
663 fChkHeader.reset();
664 continue;
665 }
666
667 if (block[0].substr(0, 9)=="XTENSION=")
668 {
669 fTable = Table(block, tellg());
670 fRow = (size_t)-1;
671
672 if (!fTable)
673 {
674 clear(rdstate()|std::ios::badbit);
675 return;
676 }
677
678 const std::string &tname = fTable.Get<std::string>("EXTNAME");
679
680 fListOfTables.emplace_back(tname);
681
682 // Check for table name. Skip until eof or requested table are found.
683 // skip the current table?
684 if ((!tableName.empty() && tableName!=tname) || (tableName.empty() && "ZDrsCellOffsets"==tname))
685 {
686 const streamoff skip = fTable.GetTotalBytes();
687 seekg(skip, std::ios_base::cur);
688
689 fChkHeader.reset();
690
691 continue;
692 }
693
694 fBufferRow.resize(fTable.bytes_per_row + 8-fTable.bytes_per_row%4);
695 fBufferDat.resize(fTable.bytes_per_row);
696
697 break;
698 }
699 }
700
701 if (fout.empty())
702 return;
703
704 if (*fout.rbegin()=='/')
705 {
706 const size_t p = fname.find_last_of('/');
707 fout.append(fname.substr(p+1));
708 }
709
710 fCopy.open(fout);
711 if (!fCopy)
712 {
713 clear(rdstate()|std::ios::badbit);
714#ifdef __EXCEPTIONS
715 throw std::runtime_error("Could not open output file.");
716#else
717 gLog << ___err___ << "ERROR - Failed to open output file." << std::endl;
718 return;
719#endif
720 }
721
722 const streampos p = tellg();
723 seekg(0);
724
725 std::vector<char> buf(p);
726 read(buf.data(), p);
727
728 fCopy.write(buf.data(), p);
729 if (!fCopy)
730 clear(rdstate()|std::ios::badbit);
731 }
732
733public:
734 fits(const std::string &fname, const std::string& tableName="", bool force=false) : izstream(fname.c_str())
735 {
736 Constructor(fname, "", tableName, force);
737 if ((fTable.is_compressed ||fTable.name=="ZDrsCellOffsets") && !force)
738 {
739#ifdef __EXCEPTIONS
740 throw std::runtime_error("Trying to read a compressed fits with the base fits class. Use factfits instead.");
741#else
742 gLog << ___err___ << "ERROR - Trying to read a compressed fits with the base fits class. Use factfits instead." << std::endl;
743#endif
744 clear(rdstate()|std::ios::badbit);
745 }
746 }
747
748 fits(const std::string &fname, const std::string &fout, const std::string& tableName, bool force=false) : izstream(fname.c_str())
749 {
750 Constructor(fname, fout, tableName, force);
751 if ((fTable.is_compressed || fTable.name=="ZDrsCellOffsets") && !force)
752 {
753#ifdef __EXCEPTIONS
754 throw std::runtime_error("Trying to read a compressed fits with the base fits class. Use factfits instead.");
755#else
756 gLog << ___err___ << "ERROR - Trying to read a compressed fits with the base fits class. Use factfits instead." << std::endl;
757#endif
758 clear(rdstate()|std::ios::badbit);
759 }
760 }
761
762 fits() : izstream()
763 {
764
765 }
766
767 ~fits()
768 {
769 std::copy(std::istreambuf_iterator<char>(*this),
770 std::istreambuf_iterator<char>(),
771 std::ostreambuf_iterator<char>(fCopy));
772 }
773
774 virtual void StageRow(size_t row, char* dest)
775 {
776 // if (row!=fRow+1) // Fast seeking is ensured by izstream
777 seekg(fTable.offset+row*fTable.bytes_per_row);
778 read(dest, fTable.bytes_per_row);
779 //fin.clear(fin.rdstate()&~ios::eofbit);
780 }
781
782 virtual void WriteRowToCopyFile(size_t row)
783 {
784 if (row==fRow+1)
785 {
786 const uint8_t offset = (row*fTable.bytes_per_row)%4;
787
788 fChkData.add(fBufferRow);
789 if (fCopy.is_open() && fCopy.good())
790 fCopy.write(fBufferRow.data()+offset, fTable.bytes_per_row);
791 if (!fCopy)
792 clear(rdstate()|std::ios::badbit);
793 }
794 else
795 if (fCopy.is_open())
796 clear(rdstate()|std::ios::badbit);
797 }
798
799 void ZeroBufferForChecksum(std::vector<char>& vec, const uint64_t extraZeros=0)
800 {
801 auto ib = vec.begin();
802 auto ie = vec.end();
803
804 *ib++ = 0;
805 *ib++ = 0;
806 *ib++ = 0;
807 *ib = 0;
808
809 for (uint64_t i=0;i<extraZeros+8;i++)
810 *--ie = 0;
811 }
812
813 uint8_t ReadRow(size_t row)
814 {
815 // For the checksum we need everything to be correctly aligned
816 const uint8_t offset = (row*fTable.bytes_per_row)%4;
817
818 ZeroBufferForChecksum(fBufferRow);
819
820 StageRow(row, fBufferRow.data()+offset);
821
822 WriteRowToCopyFile(row);
823
824 fRow = row;
825
826 return offset;
827 }
828
829 template<size_t N>
830 void revcpy(char *dest, const char *src, const int &num)
831 {
832 const char *pend = src + num*N;
833 for (const char *ptr = src; ptr<pend; ptr+=N, dest+=N)
834 std::reverse_copy(ptr, ptr+N, dest);
835 }
836
837 virtual void MoveColumnDataToUserSpace(char *dest, const char *src, const Table::Column& c)
838 {
839 // Let the compiler do some optimization by
840 // knowing that we only have 1, 2, 4 and 8
841 switch (c.size)
842 {
843 case 1: memcpy (dest, src, c.bytes); break;
844 case 2: revcpy<2>(dest, src, c.num); break;
845 case 4: revcpy<4>(dest, src, c.num); break;
846 case 8: revcpy<8>(dest, src, c.num); break;
847 }
848 }
849
850 virtual bool GetRow(size_t row, bool check=true)
851 {
852 if (check && row>=fTable.num_rows)
853 return false;
854
855 const uint8_t offset = ReadRow(row);
856 if (!good())
857 return good();
858
859 const char *ptr = fBufferRow.data() + offset;
860
861 for (Addresses::const_iterator it=fAddresses.cbegin(); it!=fAddresses.cend(); it++)
862 {
863 const Table::Column &c = it->second;
864
865 const char *src = ptr + c.offset;
866 char *dest = reinterpret_cast<char*>(it->first);
867
868 MoveColumnDataToUserSpace(dest, src, c);
869 }
870
871 return good();
872 }
873
874 bool GetNextRow(bool check=true)
875 {
876 return GetRow(fRow+1, check);
877 }
878
879 virtual bool SkipNextRow()
880 {
881 seekg(fTable.offset+(++fRow)*fTable.bytes_per_row);
882 return good();
883 }
884
885 static bool Compare(const Address &p1, const Address &p2)
886 {
887 return p1.first>p2.first;
888 }
889
890 template<class T, class S>
891 const T &GetAs(const std::string &name)
892 {
893 return *reinterpret_cast<S*>(fPointers[name]);
894 }
895
896 void EnableAddressExceptions(bool b=true)
897 {
898 if (b)
899 exceptions(iostate(throwbit));
900 else
901 exceptions(iostate(exceptions()&~throwbit));
902 }
903
904 void DisableAddressExceptions()
905 {
906 EnableAddressExceptions(false);
907 }
908
909 void *SetPtrAddress(const std::string &name)
910 {
911 if (fTable.cols.count(name)==0)
912 {
913 std::ostringstream str;
914 str << "SetPtrAddress('" << name << "') - Column not found.";
915 Exception(str.str());
916 return NULL;
917 }
918
919 Pointers::const_iterator it = fPointers.find(name);
920 if (it!=fPointers.end())
921 return it->second;
922
923 fGarbage.emplace_back(fTable.cols[name].bytes);
924
925 void *ptr = fGarbage.back().data();
926
927 fPointers[name] = ptr;
928 fAddresses.emplace_back(ptr, fTable.cols[name]);
929 sort(fAddresses.begin(), fAddresses.end(), Compare);
930 return ptr;
931 }
932
933 template<typename T>
934 bool SetPtrAddress(const std::string &name, T *ptr, size_t cnt)
935 {
936 if (fTable.cols.count(name)==0)
937 {
938 std::ostringstream str;
939 str << "SetPtrAddress('" << name << "') - Column not found.";
940 Exception(str.str());
941 return false;
942 }
943
944 if (sizeof(T)!=fTable.cols[name].size)
945 {
946 std::ostringstream str;
947 str << "SetPtrAddress('" << name << "') - Element size mismatch: expected "
948 << fTable.cols[name].size << " from header, got " << sizeof(T);
949 Exception(str.str());
950 return false;
951 }
952
953 if (cnt!=fTable.cols[name].num)
954 {
955 std::ostringstream str;
956 str << "SetPtrAddress('" << name << "') - Element count mismatch: expected "
957 << fTable.cols[name].num << " from header, got " << cnt;
958 Exception(str.str());
959 return false;
960 }
961
962 // if (fAddresses.count(ptr)>0)
963 // gLog << warn << "SetPtrAddress('" << name << "') - Pointer " << ptr << " already assigned." << endl;
964
965 //fAddresses[ptr] = fTable.cols[name];
966 fPointers[name] = ptr;
967 fAddresses.emplace_back(ptr, fTable.cols[name]);
968 sort(fAddresses.begin(), fAddresses.end(), Compare);
969 return true;
970 }
971
972 template<class T>
973 bool SetRefAddress(const std::string &name, T &ptr)
974 {
975 return SetPtrAddress(name, &ptr, sizeof(ptr)/sizeof(T));
976 }
977
978 template<typename T>
979 bool SetVecAddress(const std::string &name, std::vector<T> &vec)
980 {
981 return SetPtrAddress(name, vec.data(), vec.size());
982 }
983
984 template<typename T>
985 T Get(const std::string &key) const
986 {
987 return fTable.Get<T>(key);
988 }
989
990 template<typename T>
991 T Get(const std::string &key, const std::string &deflt) const
992 {
993 return fTable.Get<T>(key, deflt);
994 }
995
996 bool SetPtrAddress(const std::string &name, void *ptr, size_t cnt=0)
997 {
998 if (fTable.cols.count(name)==0)
999 {
1000 std::ostringstream str;
1001 str <<"SetPtrAddress('" << name << "') - Column not found.";
1002 Exception(str.str());
1003 return false;
1004 }
1005
1006 if (cnt && cnt!=fTable.cols[name].num)
1007 {
1008 std::ostringstream str;
1009 str << "SetPtrAddress('" << name << "') - Element count mismatch: expected "
1010 << fTable.cols[name].num << " from header, got " << cnt;
1011 Exception(str.str());
1012 return false;
1013 }
1014
1015 // if (fAddresses.count(ptr)>0)
1016 // gLog << warn << "SetPtrAddress('" << name << "') - Pointer " << ptr << " already assigned." << endl;
1017
1018 //fAddresses[ptr] = fTable.cols[name];
1019 fPointers[name] = ptr;
1020 fAddresses.emplace_back(ptr, fTable.cols[name]);
1021 sort(fAddresses.begin(), fAddresses.end(), Compare);
1022 return true;
1023 }
1024
1025 bool HasKey(const std::string &key) const { return fTable.HasKey(key); }
1026 bool HasColumn(const std::string& col) const { return fTable.HasColumn(col);}
1027 const Table::Columns &GetColumns() const { return fTable.GetColumns();}
1028 const Table::SortedColumns& GetSortedColumns() const { return fTable.sorted_cols;}
1029 const Table::Keys &GetKeys() const { return fTable.GetKeys();}
1030
1031 int64_t GetInt(const std::string &key) const { return fTable.Get<int64_t>(key); }
1032 uint64_t GetUInt(const std::string &key) const { return fTable.Get<uint64_t>(key); }
1033 double GetFloat(const std::string &key) const { return fTable.Get<double>(key); }
1034 std::string GetStr(const std::string &key) const { return fTable.Get<std::string>(key); }
1035
1036 size_t GetN(const std::string &key) const
1037 {
1038 return fTable.GetN(key);
1039 }
1040
1041// size_t GetNumRows() const { return fTable.num_rows; }
1042 size_t GetRow() const { return fRow==(size_t)-1 ? 0 : fRow; }
1043
1044 operator bool() const { return fTable && fTable.offset!=0; }
1045
1046 void PrintKeys(bool all_keys=false) const { fTable.PrintKeys(all_keys); }
1047 void PrintColumns() const { fTable.PrintColumns(); }
1048
1049 bool IsHeaderOk() const { return fTable.datasum<0?false:(fChkHeader+Checksum(fTable.datasum)).valid(); }
1050 virtual bool IsFileOk() const { return (fChkHeader+fChkData).valid(); }
1051
1052 bool IsCompressedFITS() const { return fTable.is_compressed;}
1053
1054 virtual size_t GetNumRows() const
1055 {
1056 return fTable.Get<size_t>("NAXIS2");
1057 }
1058
1059 virtual size_t GetBytesPerRow() const
1060 {
1061 return fTable.Get<size_t>("NAXIS1");
1062 }
1063
1064 const std::vector<std::string> &GetTables() const
1065 {
1066 return fListOfTables;
1067 }
1068};
1069
1070template<>
1071inline std::string fits::Entry::Get<std::string>() const
1072{
1073 return value;
1074}
1075
1076#endif
Note: See TracBrowser for help on using the repository browser.