diff options
Diffstat (limited to 'fitsy/strm.C')
-rw-r--r-- | fitsy/strm.C | 803 |
1 files changed, 803 insertions, 0 deletions
diff --git a/fitsy/strm.C b/fitsy/strm.C new file mode 100644 index 0000000..9560d5e --- /dev/null +++ b/fitsy/strm.C @@ -0,0 +1,803 @@ +// Copyright (C) 1999-2018 +// Smithsonian Astrophysical Observatory, Cambridge, MA, USA +// For conditions of distribution and use, see copyright notice in "copyright" + +#include <string.h> +#include <stdio.h> + +#ifndef __WIN32 +#include <sys/socket.h> +#endif + +#include <tcl.h> + +#include "strm.h" +#include "util.h" + +template<class T> FitsStream<T>::FitsStream() +{ + stream_ =0; + flush_ = NOFLUSH; + dataManage_ = 0; +} + +template<class T> FitsStream<T>::~FitsStream() +{ + if (dataManage_ && data_) + delete [] (T*)data_; +} + +// FILE* + +template <> size_t FitsStream<FILE*>::read(char* where, size_t size) +{ + // size_t size is unsigned + long long ss =size; + size_t rr = 0; + int r = 0; + + do { + r = fread(where+rr, 1, (ss>B1MB) ? B1MB : ss, stream_); + ss -= r; + rr += r; + } while (r>0 && rr<size); + + return rr; +} + +template <> void FitsStream<FILE*>::close() +{ + fclose(stream_); +} + +// Socket + +#ifndef __WIN32 + +template <> size_t FitsStream<int>::read(char* where, size_t size) +{ + // size_t size is unsigned + long long ss =size; + size_t rr =0; + int r; + + do { + r = recv(stream_ , where+rr, (ss>B4KB) ? B4KB : ss, 0); + ss -= r; + rr += r; + } while (r>0 && rr<size); + + return rr; +} + +#else + +template <> size_t FitsStream<int>::read(char* where, size_t size) +{ + return 0; +} + +#endif + +template <> void FitsStream<int>::close() {} + +// gzStream + +#ifndef __WIN32 + +template <> size_t FitsStream<gzStream>::read(char* where, size_t size) +{ + // size_t size is unsigned + long long ss =size; + size_t rr = 0; + int r = 0; + + if (stream_->transparent) { + if (stream_->useHeader) { + memcpy(where,stream_->header,2); + ss -= 2; + rr += 2; + stream_->useHeader = 0; + } + + do { + r = recv(stream_->id , where+rr, (ss>B4KB) ? B4KB : ss, 0); + ss -= r; + rr += r; + } while (r>0 && rr<size); + + return rr; + } + else { + ((z_stream*)stream_)->avail_out = size; + ((z_stream*)stream_)->next_out = (unsigned char*)where; + + if (DebugGZ) + cerr << "***read init " << ((z_stream*)stream_)->avail_out + << " bytes" << endl; + + do { + if (((z_stream*)stream_)->avail_in == 0) { + ((z_stream*)stream_)->next_in = stream_->buf; + int aa = recv(stream_->id , stream_->buf, B4KB, 0); + if (aa<0) + return rr; + + ((z_stream*)stream_)->avail_in = aa; + if (DebugGZ) + cerr << " read from socket " << aa << " bytes" << endl; + } + + if (DebugGZ) + cerr << " inflate Start: avail_in " + << ((z_stream*)stream_)->avail_in + << " avail_out " << ((z_stream*)stream_)->avail_out << endl; + + int before = ((z_stream*)stream_)->avail_out; + int result = inflate(((z_stream*)stream_), Z_NO_FLUSH); + r = before - ((z_stream*)stream_)->avail_out; + rr += r; + + switch (result) { + case Z_OK: + if (DebugGZ) + cerr << " inflate OK: avail_in " << ((z_stream*)stream_)->avail_in + << " avail_out " << ((z_stream*)stream_)->avail_out << endl; + break; + case Z_STREAM_END: + if (DebugGZ) + cerr << " inflate STRM_END: avail_in " + << ((z_stream*)stream_)->avail_in + << " avail_out " << ((z_stream*)stream_)->avail_out + << " total_in " << ((z_stream*)stream_)->total_in + << " total_out " << ((z_stream*)stream_)->total_out << endl; + return rr; + default: + internalError("Fitsy++ strm inflate error"); + return rr; + } + } while (r>0 && rr<size); + + if (DebugGZ) + cerr << "***read finish" << endl; + + return rr; + } +} + +template <> void FitsStream<gzStream>::close() +{ + if (!stream_->transparent) { + if (inflateEnd((z_stream*)stream_) != Z_OK) + internalError("Fitsy++ strm inflateEnd error"); + + if (DebugGZ) + cerr << "inflateEnd: avail_in " << ((z_stream*)stream_)->avail_in + << " avail_out " << ((z_stream*)stream_)->avail_out << endl; + } +} + +#else + +template <> size_t FitsStream<gzStream>::read(char* where, size_t size) +{ + return 0; +} +template <> void FitsStream<gzStream>::close() {} + +#endif + +// Tcl_Channel + +template <> size_t FitsStream<Tcl_Channel>::read(char* where, size_t size) +{ + // size_t size is unsigned + long long ss =size; + size_t rr = 0; + int r = 0; + + do { + r = Tcl_Read(stream_, where+rr, (ss>B1MB) ? B1MB : ss); + ss -= r; + rr += r; + } while (r>0 && rr<size); + + return rr; +} + +template <> void FitsStream<Tcl_Channel>::close() {} + +// gzFile + +template <> size_t FitsStream<gzFile>::read(char* where, size_t size) +{ + // size_t size is unsigned + long long ss =size; + size_t rr = 0; + int r = 0; + + do { + r = gzread(stream_, where+rr, (ss>B1MB) ? B1MB : ss); + ss -= r; + rr += r; + } while (r>0 && rr<size); + + return rr; +} + +template <> void FitsStream<gzFile>::close() +{ + gzclose(stream_); +} + +template<class T> FitsHead* FitsStream<T>::headRead() +{ + // read first block + char* cards = new char[FTY_BLOCK]; + if (!cards) + return NULL; + + memset(cards, ' ', FTY_BLOCK); + if (read(cards, FTY_BLOCK) != FTY_BLOCK) { + delete [] cards; + return NULL; + } + + // simple FITS file check + if (strncmp(cards, "SIMPLE =", 9) && strncmp(cards, "XTENSION=", 9)) { + delete [] cards; + return NULL; + } + + // read remaining blocks + int numblks = 1; + char* current = cards; + while (1) { + if (findEnd(current)) + break; + + // this is check + // for TCL channels, the stream gets corrupted, so END is never found + // so bail out after an extreme number of blocks have been read + // + // this does not work because some fits files have 100's or 1000's of + // history/comments. lets hope a corrupted stream is caught below + // if (numblks>10) { + // delete [] cards; + // return NULL; + // } + + char* tmp = new char[(numblks+1)*FTY_BLOCK]; + memcpy(tmp, cards, numblks*FTY_BLOCK); + delete [] cards; + cards = tmp; + current = cards + numblks*FTY_BLOCK; + memset(current, ' ', FTY_BLOCK); + + if (read(current, FTY_BLOCK) != FTY_BLOCK) { + delete [] cards; + return NULL; + } + + numblks++; + } + + // create header + FitsHead* fits = new FitsHead(cards, numblks*FTY_BLOCK, FitsHead::ALLOC); + if (!fits->isValid()) { + delete fits; + return NULL; + } + + return fits; +} + +template<class T> int FitsStream<T>::dataRead(size_t bytes, int validate) +{ + data_ = NULL; + + dataSize_ = 0; + dataSkip_ = 0; + dataManage_ = 0; + + if (!bytes) + return 0; + + data_ = new char[bytes]; + if (!data_) + return 0; + + size_t rr = read((char*)data_, bytes); + if (validate && rr != bytes) { + delete (char*)data_; + data_ = NULL; + + dataSize_ = 0; + dataSkip_ = 0; + dataManage_ = 0; + + return 0; + } + + dataSize_ = bytes; + dataManage_ = 1; + + return 1; +} + +template<class T> void FitsStream<T>::dataSkip(size_t bytes) +{ + // size_t bytes is unsigned + if (bytes) { + char block[FTY_BLOCK]; + + do { + read(block, (bytes < FTY_BLOCK ? bytes : FTY_BLOCK)); + if (bytes>FTY_BLOCK) + bytes -= FTY_BLOCK; + else + break; + } while (1); + } +} + +template<class T> void FitsStream<T>::dataSkipBlock(size_t blk) +{ + char block[FTY_BLOCK]; + for (size_t ii=0; ii<blk; ii++) + read(block, FTY_BLOCK); +} + +template<class T> void FitsStream<T>::skipEnd() +{ + char block[FTY_BLOCK]; + + int bytes; + do + bytes = read(block, FTY_BLOCK); + while (bytes > 0); +} + +template<class T> void FitsStream<T>::found() +{ + // only read allbytes, since the data seg maybe short + if (!this->dataRead(head_->allbytes())) { + error(); + return; + } + + // read any dead space til next block + if (head_->padbytes()>0) + this->dataSkip(head_->padbytes()); + + inherit_ = head_->inherit(); + valid_ = 1; + + if (flush_ == FLUSH) + skipEnd(); +} + +template<class T> void FitsStream<T>::error() +{ + // try to clean up + if ((flush_ == FLUSH) && (head_ || primary_)) + skipEnd(); + + if (manageHead_ && head_) + delete head_; + head_ = NULL; + + if (managePrimary_ && primary_) + delete primary_; + primary_ = NULL; + + data_ = NULL; + + dataSize_ = 0; + dataSkip_ = 0; + dataManage_ = 0; + + valid_ = 0; +} + +template class FitsStream<FILE*>; +template class FitsStream<Tcl_Channel>; +template class FitsStream<int>; +template class FitsStream<gzFile>; +template class FitsStream<gzStream>; + +template<class T> FitsFitsStream<T>::FitsFitsStream(FitsFile::ScanMode mode, + FitsFile::FlushMode f) +{ + if (!this->valid_) + return; + + this->flush_ = f; + + if (mode == this->EXACT || this->pExt_ || this->pIndex_>-1) + processExact(); + else + processRelax(); +} + +template<class T> void FitsFitsStream<T>::processExact() +{ + if (!(this->pExt_ || (this->pIndex_>0))) { + + // we are only looking for a primary image + if ((this->head_ = this->headRead())) { + this->found(); + return; + } + } + else { + + // we are looking for an extension + // keep the primary header + this->primary_ = this->headRead(); + this->managePrimary_ = 1; + if (!this->primary_) { + this->error(); + return; + } + this->dataSkipBlock(this->primary_->datablocks()); + + if (this->pExt_) { + while (1) { + if (!(this->head_ = this->headRead())) { + this->error(); + return; + } + this->ext_++; + + if (this->head_->extname()) { + char* a = toUpper(this->head_->extname()); + char* b = toUpper(this->pExt_); + if (!strncmp(a,b,strlen(b))) { + delete [] a; + delete [] b; + this->found(); + return; + } + delete [] a; + delete [] b; + } + + this->dataSkipBlock(this->head_->datablocks()); + delete this->head_; + this->head_ = NULL; + } + } + else { + for (int i=1; i<this->pIndex_; i++) { + if (!(this->head_ = this->headRead())) { + this->error(); + return; + } + this->ext_++; + + this->dataSkipBlock(this->head_->datablocks()); + delete this->head_; + this->head_ = NULL; + } + + if ((this->head_ = this->headRead())) { + this->ext_++; + this->found(); + return; + } + } + } + + // we must have an error + this->error(); +} + +template<class T> void FitsFitsStream<T>::processRelax() +{ + // check to see if there is an image in the primary + if (!(this->head_ = this->headRead())) { + this->error(); + return; + } + else { + if (this->head_->isValid() && + this->head_->naxes() > 0 && + this->head_->naxis(0) > 0 && + this->head_->naxis(1) > 0) { + this->found(); + return; + } + } + + // ok, no image, save primary and lets check extensions + this->primary_ = this->head_; + this->managePrimary_ = 1; + this->dataSkipBlock(this->head_->datablocks()); + this->head_ = NULL; + + // ok, no image, lets check extensions + while (1) { + if (!(this->head_ = this->headRead())) { + this->error(); + return; + } + this->ext_++; + + if (this->head_->isImage()) { + this->found(); + return; + } + + // else, check for compressed image + if (this->head_->isBinTable() && this->head_->find("ZIMAGE")) { + this->found(); + return; + } + + // else, check for bin table named STDEVT, EVENTS, RAYEVENT + if (this->head_->isBinTable() && this->head_->extname()) { + char* a = toUpper(this->head_->extname()); + if (!strncmp("STDEVT", a, 6) || + !strncmp("EVENTS", a, 6) || + !strncmp("RAYEVENT", a, 8)) { + delete [] a; + this->found(); + return; + } + else + delete [] a; + } + + // else, check for bin table with keyword PIXTYPE = 'HEALPIX ' + if (this->head_->isBinTable() && this->head_->find("PIXTYPE") && + (!strncmp(this->head_->getString("PIXTYPE"),"HEALPIX",4))) { + this->found(); + return; + } + + // else, check for bin table with keyword NSIDE (also HEALPIX) + if (this->head_->isBinTable() && this->head_->find("NSIDE")) { + this->found(); + return; + } + + this->dataSkipBlock(this->head_->datablocks()); + delete this->head_; + this->head_ = NULL; + } + + this->error(); +} + +template class FitsFitsStream<FILE*>; +template class FitsFitsStream<Tcl_Channel>; +template class FitsFitsStream<int>; +template class FitsFitsStream<gzFile>; +template class FitsFitsStream<gzStream>; + +template<class T> FitsFitsNextStream<T>::FitsFitsNextStream(FitsFile* p) +{ + FitsStream<T>* prev = (FitsStream<T>*)p; + + this->primary_ = prev->primary(); + this->managePrimary_ = 0; + + this->head_ = prev->head(); + this->manageHead_ = 0; + + FitsImageHDU* hdu = (FitsImageHDU*)this->head_->hdu(); + this->data_ = (char*)prev->data() + hdu->imgbytes(); + this->dataSize_ = 0; + this->dataSkip_ = 0; + + this->ext_ = prev->ext(); + this->inherit_ = prev->inherit(); + this->byteswap_ = prev->byteswap(); + this->endian_ = prev->endian(); + this->valid_ = 1; + + this->pcoord_ = prev->pcoord(); + this->pxvalid_ = prev->pxvalid(); + this->pxmin_ = prev->pxmin(); + this->pxmax_ = prev->pxmax(); + this->pyvalid_ = prev->pyvalid(); + this->pymin_ = prev->pymin(); + this->pymax_ = prev->pymax(); + this->pzvalid_ = prev->pzvalid(); + this->pzmin_ = prev->pzmin(); + this->pzmax_ = prev->pzmax(); + this->pbvalid_ = prev->pbvalid(); + this->pblock_ = prev->pblock(); + + this->stream_ = prev->stream(); + this->flush_ = prev->flush(); + this->dataManage_ = 0; +} + +template class FitsFitsNextStream<FILE*>; +template class FitsFitsNextStream<Tcl_Channel>; +template class FitsFitsNextStream<int>; +template class FitsFitsNextStream<gzFile>; +template class FitsFitsNextStream<gzStream>; + +template<class T> FitsArrStream<T>::FitsArrStream(FitsFile::FlushMode f) +{ + if (!this->valid_) + return; + + this->flush_ = f; + + this->valid_=0; + + // check to see if we have a nonzero width, height, and bitpix + if (!this->validParams()) { + return; + } + + // skip header + if (this->pSkip_) + this->dataSkip(this->pSkip_); + + // read data + if (!this->dataRead((size_t)this->pWidth_*this->pHeight_*this->pDepth_*abs(this->pBitpix_)/8)) { + if ((this->flush_ == this->FLUSH) && this->data_) + this->skipEnd(); + return; + } + + // create blank header + this->head_ = new FitsHead(this->pWidth_, this->pHeight_, + this->pDepth_, this->pBitpix_); + if (!this->head_->isValid()) { + this->error(); + return; + } + + // do we need to byteswap? + this->setByteSwap(); + + // made it this far, must be good + this->valid_ = 1; + + if (this->flush_ == this->FLUSH) + this->skipEnd(); +} + +template class FitsArrStream<FILE*>; +template class FitsArrStream<Tcl_Channel>; +template class FitsArrStream<int>; +template class FitsArrStream<gzFile>; +template class FitsArrStream<gzStream>; + +template<class T> FitsNRRDStream<T>::FitsNRRDStream(FitsFile::FlushMode f) +{ + if (!this->valid_) + return; + + this->flush_ = f; + + this->valid_=0; + + // header + { + char buf[1024]; + char* dptr = buf; + int cnt =0; + do { + int cc = this->read(dptr,1); + if (cc != 1 || (*dptr == '\n' && *(dptr-1) == '\n')) { + break; + } + cnt++; + dptr++; + } while (cnt<1024); + *dptr = '\0'; + + string x(buf); + istringstream str(x); + this->parseNRRD(str); + } + + // check to see if we have a nonzero width, height, and bitpix + if (!this->validParams()) + return; + + // read data + this->dataRead((size_t)this->pWidth_*this->pHeight_*this->pDepth_*abs(this->pBitpix_)/8, 0); + + // create blank header + this->head_ = new FitsHead(this->pWidth_, this->pHeight_, + this->pDepth_, this->pBitpix_); + if (!this->head_->isValid()) { + this->error(); + return; + } + + // do we need to byteswap? + this->setByteSwap(); + + // made it this far, must be good + this->valid_ = 1; + + if (this->flush_ == this->FLUSH) + this->skipEnd(); +} + +template class FitsNRRDStream<FILE*>; +template class FitsNRRDStream<Tcl_Channel>; +template class FitsNRRDStream<int>; +template class FitsNRRDStream<gzFile>; +template class FitsNRRDStream<gzStream>; + +template<class T> FitsMosaicStream<T>::FitsMosaicStream(FitsFile::FlushMode f) +{ + if (!this->valid_) + return; + + this->flush_ = f; + this->primary_ = this->headRead(); + this->managePrimary_ = 1; + if (!(this->primary_ && this->primary_->isValid())) { + this->error(); + return; + } + this->dataSkipBlock(this->primary_->datablocks()); + + // first extension + this->head_ = this->headRead(); + if (!(this->head_ && this->head_->isValid())) { + this->error(); + return; + } + this->ext_++; + + // be sure to read all blocks, so that the next call starts on a boundary + if (!this->dataRead(this->head_->datablocks()*FTY_BLOCK)) { + this->error(); + return; + } + + // don't flush, more to come + this->inherit_ = this->head_->inherit(); + this->valid_ = 1; +} + +template class FitsMosaicStream<FILE*>; +template class FitsMosaicStream<Tcl_Channel>; +template class FitsMosaicStream<int>; +template class FitsMosaicStream<gzFile>; +template class FitsMosaicStream<gzStream>; + +template<class T> FitsMosaicNextStream<T>::FitsMosaicNextStream(FitsFile* p, + FitsFile::FlushMode f) +{ + this->flush_ = f; + FitsStream<T>* prev = (FitsStream<T>*)p; + this->primary_ = prev->primary(); + this->managePrimary_ = 0; + this->stream_ = prev->stream(); + this->ext_ = prev->ext(); + + this->head_ = this->headRead(); + if (!(this->head_ && this->head_->isValid())) { + this->error(); + return; + } + this->ext_++; + + // be sure to read all blocks, so that the next call starts on a boundary + if (!this->dataRead(this->head_->datablocks()*FTY_BLOCK)) { + this->error(); + return; + } + + this->inherit_ = this->head_->inherit(); + this->valid_ = 1; +} + +template class FitsMosaicNextStream<FILE*>; +template class FitsMosaicNextStream<Tcl_Channel>; +template class FitsMosaicNextStream<int>; +template class FitsMosaicNextStream<gzFile>; +template class FitsMosaicNextStream<gzStream>; |