#include #include #include #include "IEEEIO.hh" #ifdef WIN32 // Are we Microsoft VC++ 5.0 or 6.0? #if defined(_MSC_VER) && ( (_MSC_VER == 1100) || (_MSC_VER == 1200) || (_MSC_VER == 1300)) // yes we are #include #ifdef O_RDONLY #undef O_RDONLY #endif #define O_RDONLY _O_RDONLY|_O_BINARY #ifdef O_RDWR #undef O_RDWR #endif #define O_RDWR _O_RDWR|_O_BINARY #ifdef O_WRONLY #undef O_WRONLY #endif #define O_WRONLY _O_WRONLY|_O_BINARY #ifdef O_CREAT #undef O_CREAT #endif #define O_CREAT _O_CREAT|_O_BINARY #ifdef O_TRUNC #undef O_TRUNC #endif #define O_TRUNC _O_TRUNC|_O_BINARY #ifdef O_APPEND #undef O_APPEND #endif #define O_APPEND _O_APPEND|_O_BINARY #else // not an MSC compiler (use the old defines) #define O_RDONLY _O_RDONLY #define O_RDWR _O_RDWR #define O_WRONLY _O_WRONLY #define O_CREAT _O_CREAT #define O_TRUNC _O_TRUNC #define O_APPEND _O_APPEND #endif // not an MSC compiler #endif // WIN32 #ifdef FFIO #include static struct ffsw ffopens_status; //#define open(x,y,z) ffopens(x,y,z, 0, 0, &ffopens_status, "bufa.bufsize=256.num_buffers=4") #define LAYERS "" #ifdef T3E #define open(x,y,z) ffopens(x,y,S_IWRITE|S_IREAD,0,100,&ffopens_status,LAYERS); #else // its probably an SGI #define open(x,y,z) ffopens(x,y,S_IWRITE|S_IREAD,0,0,&ffopens_status,LAYERS); #endif #define close(x) ffclose(x) #endif #ifdef T3E int IEEEIO::ffioIsTrue(){ #ifdef FFIO return 1; #else return 0; #endif } #endif // int IEEEIO::nextRecord() (stores state in current_record) // also garauntees integrity of datasetnumber and // the current_rec and current_dat IEEEIO::AttribRef::AttribRef(IEEEIO::AttribRef &src){ //puts("AttribRef::copy constructor"); rec=src.rec; offset=src.offset; strcpy(name,src.name); } IEEEIO::AttribRef::AttribRef(){ //puts("empty attribref constructor"); } /* IEEEIO::AttribRef::~AttribRef(){ //puts("delete an attrib ref"); } IEEEIO::RecRef::~RecRef(){ //puts("delete a recref"); } */ IEEEIO::AttribRef &IEEEIO::AttribRef::operator=(IEEEIO::AttribRef &src){ if(this != &src) { //puts("\tAttribRef::copy operator"); rec=src.rec; offset=src.offset; //name=src.name; // uses the flexarray copy constructor strcpy(name,src.name); } return *this; } IEEEIO::DataRef::DataRef():end_offset(0){ //puts("Empty DataRef Constructor!!"); } IEEEIO::DataRef::DataRef(IEEEIO::DataRef &src){ //puts("DataRef:: copy constructor"); rec=src.rec; offset=src.offset; end_offset=src.end_offset; annotations = src.annotations; attributes = src.attributes; //puts("DataRef:: copy constructor DONE"); } /* IEEEIO::DataRef::~DataRef(){ // puts("dataref destructor"); }*/ IEEEIO::DataRef &IEEEIO::DataRef::operator=(IEEEIO::DataRef &src){ if(this != &src){ rec=src.rec; offset=src.offset; end_offset=src.end_offset; annotations = src.annotations; attributes = src.attributes; } return *this; } void IEEEIO::initPosition(){ // init #ifdef FFIO actual_position = ::ffseek(fid,0,L_INCR); #else actual_position = ::lseek(fid,0,L_INCR); #endif virtual_position = actual_position + writebuffercount; // get actual filesize #ifdef FFIO file_length = ::ffseek(fid,0,L_XTND); ::ffseek(fid,actual_position,L_SET); // seek back to original pos #else #ifdef SGI file_length = ::lseek64(fid,0,L_XTND); ::lseek64(fid,actual_position,L_SET); // seek back to original pos #else file_length = ::lseek(fid,0,L_XTND); ::lseek(fid,actual_position,L_SET); // seek back to original pos #endif #endif if(virtual_position > file_length) file_length = virtual_position; // printf("Init position: a=%ld v=%ld f=%ld\n", // actual_position,virtual_position,file_length); } //long IEEEIO::getPosition(int immediate) { //#ifdef FFIO // if(!writebuffer || immediate) // return ::ffseek(fid,0,L_INCR); // else // return ::ffseek(fid,0,L_INCR)+writebuffercount; //#else // if(!writebuffer || immediate) // return ::lseek(fid,0,L_INCR); // else // return ::lseek(fid,0,L_INCR)+writebuffercount; //#endif //} void IEEEIO::restart(){ // this->initPosition(); //puts("restart seek"); IEEEIO::lseek(FileHdrSize,L_SET); // T3E Kludge //puts("restart"); current_rec_offset=current_dat_offset=FileHdrSize; // T3E Kludge current_rec.recordsize=0; current_dat.datasize=0; current_dat.rank=0; datasetnumber=0; // initial hasread=0; // and it hasn't been read yet... } int IEEEIO::nextRecord(){ RecordHdr rec; // should have it check the datasetnumber before seeking // this will require consistancy checks for the datasetnumber // elsewhere in the code Long8 src=current_rec_offset; Long8 dst=src+current_rec.recordsize; current_rec_offset=IEEEIO::lseek(dst,L_SET); // seek from start // or use rec.read(rec,fid) // or IEEEIO::RecordHdr::read(rec,fid) if(RecordHdr::read(this,rec) <= 0) return 0; current_rec_offset=dst+RecordHdrSize; current_rec=rec; return 1; } int IEEEIO::nextDataRecord(){ hasread=0; // reset the hasread field if(current_rec.recordtype==DataRecord && datasetnumber!=current_rec.sequenceID){ IEEEIO::lseek(current_rec_offset,L_SET); // seek to record start } else { do { // scan through records for the next DataRecord if(!nextRecord()) return 0; // reached end of file } while(current_rec.recordtype!=DataRecord); } // dat_offset is same as current_rec_offset for the data record // so nextRecord() can be used for scanning for annotations current_dat_offset=current_rec_offset; DataRecordHdr::read(this,current_dat); datasetnumber=current_rec.sequenceID; return 1; } //long IEEEIO::getLength(int immediate){ //#ifdef FFIO // long currentpos=::ffseek(fid,0,L_INCR); // find currentposition // long auspos = ::ffseek(fid,0,L_XTND); // seek to end to find length //#else // long currentpos=::lseek(fid,0,L_INCR); // find currentposition // long auspos = ::lseek(fid,0,L_XTND); // seek to end to find length //#endif // if(writebuffer && writebuffercount && !immediate){ // // must compute real end of file // register long pos1=currentpos+writebuffercount; // if(pos1>auspos) auspos=pos1; // } //#ifdef FFIO // ::ffseek(fid,currentpos,L_SET); // seek back to original position //#else // ::lseek(fid,currentpos,L_SET); // seek back to original position //#endif // return auspos; // return end position //} void IEEEIO::byteswapBuffer(void *buf,Long8 nelements,int elementsize){ char *buffer=(char *)buf; // treat as a character buffer if(elementsize<=1) return; for(Long8 i=0;iflush(); //puts("WriteHeader"); for(char c=0;c<4;c++) // make sure magic is correct file_header.magic.c[c]=c+1; if(swapbytes) file_header.byteorder.i=IEEE_REVERSE_MAGIC; else file_header.byteorder.i=IEEE_NATIVE_MAGIC; file_header.majorversion = IEEE_MAJORVERSION; file_header.minorversion = IEEE_MINORVERSION; IEEEIO::lseek(0,L_SET); // will force a flush (if needed) //puts("done writeheader seek"); if(swapbytes) file_header.byteswap(); // T3E Kludge int sz=FileHdr::write(this,file_header); if(swapbytes) file_header.byteswap(); // if(pos>=FileHdrSize) // T3E Kludge // IEEEIO::lseek(pos,L_SET); return sz; } // reads the start of the file (including the magic number) // and determines if this is // 1) A valid IEEEIO file // 2) what the byte order is // 3) How many datasets are contained int IEEEIO::readFileHeader(){ Long8 pos=getPosition(); // use virtual_position IEEEIO::lseek(0,L_SET); // T3E Kludge // int sz=FileHdr::read(this,file_header); FileHdr::read(this,file_header); IEEEIO::lseek(pos,L_SET); // return to original position for(char c=0;c<4;c++){ if((c+1)!=file_header.magic.c[c]){ file_header.ndatasets=0; // fprintf(stderr,"IEEEIO: File %s has the wrong magic number\n",filename); return -1; // bad magic } } // now compare byte order if(file_header.byteorder.i==IEEE_NATIVE_MAGIC) swapbytes=0; else{ swapbytes=1; //puts("byteswapping is On !!!!!!!!!!!!!!!"); } if(swapbytes) file_header.byteswap(); ndatasets=file_header.ndatasets; return 1; } void IEEEIO::rebuildFileHeader(){ int lndatasets; Long8 pos=getPosition(); // save file position (use virtual position restart(); // steal seek loop from the nextDataRecord() for(lndatasets=0; nextDataRecord()>0; lndatasets++){ // now check the length of the record // for each record. } file_header.ndatasets=lndatasets; writeFileHeader(); IEEEIO::lseek(pos,L_SET); } void IEEEIO::appendRecordTable(){ //printf("appending records==================\n"); while(nextRecord()>0){ //printf("appending record\n"); switch(current_rec.recordtype){ case DataRecord: //fprintf(stderr,"DataRecord"); (rec_table[current_rec.sequenceID]).rec=current_rec; // use virtual_position (rec_table[current_rec.sequenceID]).offset=getPosition()-RecordHdrSize; break; case AnnotationRecord:{ //fprintf(stderr,"\tAnnotationRecord\n"); RecRef ref; ref.rec=current_rec; ref.offset=getPosition()- RecordHdrSize; // T3E Kludge (rec_table[current_rec.sequenceID]).annotations.append(ref); } break; case AttributeRecord:{ //fprintf(stderr,"\tAttributeRecord\n"); AttribRef ref,*refp; AttributeRecordHdr attribhdr; ref.rec=current_rec; ref.offset=getPosition()- RecordHdrSize; // T3E Kludge (rec_table[current_rec.sequenceID]).attributes.append(ref); int idx=(rec_table[current_rec.sequenceID]).attributes.getSize()-1; refp = &((rec_table[current_rec.sequenceID]).attributes[idx]); AttributeRecordHdr::read(this,attribhdr); IEEEIO::read(refp->name,attribhdr.namesize); } break; default: fprintf(stderr,"\tIEEEIO::Error UNKNOWN RECORD TYPE [%d]... recovering.", (int)(current_rec.recordtype)); return; } (rec_table[current_rec.sequenceID]).end_offset = current_rec_offset + current_rec.recordsize; } } void IEEEIO::buildRecordTable(){ if(file_header.ndatasets<0) return; // failure restart(); rec_table.setSize(file_header.ndatasets); appendRecordTable(); } void IEEEIO::openFile(CONST char *fname,IObase::AccessMode amode,int swbytes){ swbytes = swbytes; switch(amode){ case IObase::Read: fid=open(fname,O_RDONLY,0644); if(fid<0){ fprintf(stderr,"IEEEIO: Failed to open %s for reading\n",fname); return; } initPosition(); if(readFileHeader()<=0){ close(fid); // fprintf(stderr,"IEEEIO: File %s is empty (opened for reading)\n",fname); fid=-1; // invalid file return; } if(file_header.ndatasets<0){ // must recover from crash close(fid); fid=open(fname,O_RDWR,0644); rebuildFileHeader(); close(fid); fid=open(fname,O_RDONLY,0644); readFileHeader(); // reread.. } buildRecordTable(); restart(); // go to start of file ndatasets = file_header.ndatasets; nextRecord(); // prime the recordnumber break; case IObase::Write: // truncates fid=open(fname,O_WRONLY|O_CREAT|O_TRUNC,0644); if(fid<0){ fprintf(stderr,"IEEEIO: Failed to create %s for writing\n",fname); return; } initPosition(); file_header.ndatasets=-1; ndatasets=0; writeFileHeader(); restart(); break; case IObase::Append: default: fid=open(fname,O_RDWR,0644); // PW: We *don't* want O_APPEND here. // O_Append moves the file ptr to end // of file before a write; this means // when we do the writeHeader() below // this is stuck at the end of this file // which makes it puke. if(fid<0){ fprintf(stderr,"IEEEIO: Failed to open %s for append\n",fname); return; } initPosition(); if(readFileHeader()<=0){ close(fid); fprintf(stderr,"IEEEIO: File %s is empty (opened for append)\n",fname); fid=-1; // invalid file return; } if(file_header.ndatasets<0){ // must recover from crash close(fid); fid=open(fname,O_RDWR,0644); initPosition(); rebuildFileHeader(); close(fid); fid=open(fname,O_RDWR,0644); // PW: We want to open RDWR again initPosition(); readFileHeader(); // reread.. } buildRecordTable(); restart(); // go to start of file ndatasets = file_header.ndatasets; // now we sync up in the way we would for a write file_header.ndatasets=-1; writeFileHeader(); // write header with -1 to indicate we are writing // could refcount by increasing the - of numbers in header // (needs extra flag for synced) restart(); nextRecord(); // prime the recordnumber //seek(ndatasets); break; } } IEEEIO::IEEEIO(IEEEIO *file): // read only dup IObase(file->filename,IObase::Read),fid(dup(file->fid)), datasetnumber(0),ndatasets(file->ndatasets), swapbytes(file->swapbytes),hasread(0), writebuffersize(0),writebuffercount(0),writebuffer(0), savedposition(-1),virtual_position(-1),actual_position(-1), file_length(-1) { if(file->masterfile) masterfile=file->masterfile; else masterfile=file; initPosition(); // initial positional pointers ndatasets = file_header.ndatasets = file->ndatasets; buildRecordTable(); restart(); // go to start of file nextRecord(); // prime the recordnumber } IEEEIO::IEEEIO(CONST char *fname, ExtendedAccessMode amode,int swbytes): IObase(fname,mode(amode)),fid(-1),datasetnumber(0), ndatasets(0),swapbytes(swbytes),hasread(0),masterfile(0), writebuffersize(0),writebuffercount(0),writebuffer(0), savedposition(-1),virtual_position(-1),actual_position(-1), file_length(-1) { if(amode==IEEEIO::SharedRead){ masterfile=this; // we are multi-reading fid=open(fname,O_RDONLY,0644); if(fid<0){ fprintf(stderr,"IEEEIO: Failed to open %s for reading\n",fname); return; } this->initPosition(); if(readFileHeader()<=0){ close(fid); fprintf(stderr,"IEEEIO: File %s is empty (opened for reading)\n",fname); fid=-1; // invalid file return; } buildRecordTable(); restart(); // go to start of file ndatasets = file_header.ndatasets; nextRecord(); // prime the recordnumber } else openFile(fname,mode(amode),swbytes); } IEEEIO::IEEEIO(CONST char *fname,IObase::AccessMode amode,int swbytes): IObase(fname,amode),fid(-1),datasetnumber(0), ndatasets(0),swapbytes(swbytes),hasread(0),masterfile(0), writebuffersize(0),writebuffercount(0),writebuffer(0), savedposition(-1) { // Long8 fpos; openFile(fname,amode,swbytes); } IEEEIO::~IEEEIO(){ if(fid<0 && savedposition>=0) resume(); // resume IO only if it is paused so that // we can do the final writes to the file before // closing //puts("do bufferoff"); if(writebuffer) bufferOff(); // automatically flushes buffer //puts("now rewrite header"); if(fid>=0){ if(accessmode!=IObase::Read){ file_header.ndatasets=ndatasets; writeFileHeader(); } close(fid); } fid=-1; } int IEEEIO::isValid(){ if(fid>=0) return 1; else return 0; } int IEEEIO::write(IObase::DataType typeID,int rank,CONST int *dims,const void *data){ int i; RecordHdr rec; DataRecordHdr hdr; // make sure its the EOF if(accessmode==IObase::Read) return 0; hasread=0; // reset hasread; (JMS: changed from local Thu Mar 12 13:53:13 CST 1998) if(rec_table.getSize()>0){ Long8 endpos = rec_table[rec_table.getSize()-1].end_offset; IEEEIO::lseek(endpos,L_SET); // don't know if this is costly } else IEEEIO::lseek(0,L_XTND); // seek to end of file // if 0-length seeks are costly, then alternative // logic can be constructed to ensure writes to end of file for(i=0,hdr.datasize=1;i=rec_table.getSize()) return 0; // end of file // read the record + header hasread=1; // we've read this, so next time we hit it, we'll increment again IEEEIO::lseek(rec_table[datasetnumber].offset,L_SET); // T3E Kludge RecordHdr::read(this,current_rec); DataRecordHdr::read(this,current_dat); rank=current_dat.rank; if(chunkdims.getSize()maxdims)?maxdims:rank,sizeof(Int)); for(int i=0;i=rec_table.getSize()) return 0; // seek past end. Long8 datapos=rec_table[datasetnumber].offset+RecordHdrSize+DataRecordHdrSize+ sizeof(Int)*current_dat.rank; // should make certain current file position is correct // if(getPosition() != datapos) (redundant call to lseek()) IEEEIO::lseek(datapos,L_SET); // seek to position int sz=IEEEIO::read(data,current_dat.datasize); // read nbytes int typelen = sizeOf(Int2DataType(current_dat.numbertype));// compute elem sz if(swapbytes) byteswapBuffer(data,current_dat.datasize/typelen,typelen); return sz; } int IEEEIO::seek(int idx){ if(accessmode!=IObase::Read && accessmode!=IObase::Append) // can't seek unless readable return -1; // failed to seek if (idx >= ndatasets || idx < 0) { return -1; // do a bounds check (probably should just clip) } // bound the index if((1+idx)>rec_table.getSize()) idx=rec_table.getSize(); if(idx<0) idx=0; index = idx; IEEEIO::lseek(rec_table[index].offset,L_SET); // T3E Kludge RecordHdr::read(this,current_rec); current_rec_offset=getPosition(); datasetnumber=current_rec.sequenceID; // Changed: had -1 hasread=0; // new file position return current_rec.sequenceID; } int IEEEIO::nDatasets() { // can work across threads or across processes since // it gathers information directly from the file. // it does not need to share info with masterfile // in fact you can delete the masterfile if(masterfile){ // we are passive read-only. update nDatasets // seek-scan to find datasets? int oldlength = (rec_table.getSize())?(rec_table[rec_table.getSize()-1].end_offset):0; //printf("oldlength=%u newlength=%u\n",oldlength,(unsigned int)(getLength())); if(getLength()>oldlength){ // lets scan to find the end int lndatasets; puts("We should clearly not be here!!!"); restart(); // go to beginning // steal seek loop from the nextDataRecord() for(lndatasets=0; nextDataRecord()>0; lndatasets++){ //printf("scan dataset[%u]\n",lndatasets); } file_header.ndatasets = lndatasets; rec_table.setSize(lndatasets); // printf("counted %u datasets. old was %u datasets\n",lndatasets,ndatasets); seek(ndatasets); // seek to current last ndatasets = lndatasets; // then reset the last appendRecordTable(); // append new stuff to record table } } return ndatasets; } int IEEEIO::writeAnnotation(CONST char *annotation){ int stringlen; RecRef aref; RecordHdr rec; if(accessmode==IObase::Read || !annotation) return 0; stringlen=strlen(annotation)+1; if(rec_table.getSize()>0){ Long8 endpos = rec_table[rec_table.getSize()-1].end_offset; IEEEIO::lseek(endpos,L_SET); // don't know if this is costly } else IEEEIO::lseek(0,L_XTND); // seek to end of file rec.recordtype=AnnotationRecord; rec.recordsize=stringlen; if(datasetnumber>=0) rec.sequenceID=datasetnumber; else rec.sequenceID=current_rec.sequenceID; // a kludge for error immunity current_rec = rec; // T3E Kludge current_rec_offset = getPosition() + RecordHdrSize; // T3E Kludge RecordHdr::write(this,rec); // if(swapbytes) byteswap(rec); int sz=IEEEIO::write(annotation,stringlen); aref.rec=current_rec; // T3E Kludge aref.offset=current_rec_offset-RecordHdrSize; if(accessmode!=IObase::Write){ // don't write to index cache if in write-only mode rec_table[datasetnumber].annotations.append(aref); rec_table[datasetnumber].end_offset=getPosition(); } return sz; } /* If objects could have all attributes implicitly availible for querying when passed to subroutines. Basic object methods for data movement like linearize() or object.linearized[index] object.linearized.size. Then for objects that a receiver can't deal with either 1: have compiletime "gatekeepers" that restrict allowable types that it can receive 2: Do a treesearch through object space to find a convertor sequence that can convert the current type into the one that the object accepts. If no such object can be found, indicate runtime object adaptor failure. */ int IEEEIO::readAnnotationInfo(int number,int &length){ if(datasetnumber<0 || number>=rec_table[datasetnumber].annotations.getSize()){ length=0; return -1; } length=rec_table[datasetnumber].annotations[number].rec.recordsize; return length; } int IEEEIO::readAnnotation(int number,char *annotation,int maxsize){ // returns actual size of annotation or -1 if error if(datasetnumber<0 || accessmode==IObase::Write || number>=rec_table[datasetnumber].annotations.getSize()){ return -1; } IEEEIO::lseek(rec_table[datasetnumber].annotations[number].offset,L_SET); // T3E Kludge RecordHdr::read(this,current_rec); IEEEIO::read(annotation,(maxsize0){ Long8 endpos = rec_table[rec_table.getSize()-1].end_offset; IEEEIO::lseek(endpos,L_SET); // don't know if this is costly } else IEEEIO::lseek(0,L_XTND); // seek to end of file attrib.datasize=length*sizeOf(typeID); attrib.namesize=stringlen; attrib.numbertype=typeID; rec.recordtype=AttributeRecord; rec.recordsize=attrib.datasize+attrib.namesize+AttributeRecordHdrSize; if(datasetnumber>=0) rec.sequenceID=datasetnumber; else rec.sequenceID=current_rec.sequenceID; // a kludge for error immunity current_rec=rec; current_rec_offset=getPosition() + RecordHdrSize; aref.rec=current_rec; aref.offset=current_rec_offset-RecordHdrSize; // no copy constructor for aref, so must do manually if(accessmode != IObase::Write){ int lastindex; rec_table[datasetnumber].attributes.append(aref); lastindex=rec_table[datasetnumber].attributes.getSize()-1; strcpy(rec_table[datasetnumber].attributes[lastindex].name,aname); } // T3E Kludge RecordHdr::write(this,rec); AttributeRecordHdr::write(this,attrib); //printf("\tnow write the string\n"); IEEEIO::write(aname,stringlen); //printf("\tdone IEEEIO::stringlen=%u typeid=%u\n",attrib.namesize,attrib.numbertype); // data is not byte-reversed... int sz=0; union { void *data; const void *const_data;} cast_union; cast_union.const_data = data; if(swapbytes) byteswapBuffer(cast_union.data,length,sizeOf(typeID)); IEEEIO::write(cast_union.const_data,length*sizeOf(typeID)); if(swapbytes) byteswapBuffer(cast_union.data,length,sizeOf(typeID)); // swap back if(accessmode != IObase::Write) rec_table[datasetnumber].end_offset=getPosition(); // doesn't appear to store attributes properly return sz; } int IEEEIO::readAttributeInfo(int number,char *aname,IObase::DataType &typeID, Long &nelem,int maxnamelen){ if(accessmode==IObase::Write) return -1; FlexArray *attribs= &(rec_table[datasetnumber].attributes); if(number>=(*attribs).getSize()) return -1; // > number of attributes AttribRef *attrib=&((*attribs)[number]); if(strlen(attrib->name)>(size_t)maxnamelen){ strncpy(aname,attrib->name,maxnamelen); // don't we want to copy the other way? aname[maxnamelen-1]='\0'; } else strcpy(aname,attrib->name); aname[maxnamelen-1]='\0'; // make certain it is null capped AttributeRecordHdr attribhdr; IEEEIO::lseek((*attribs)[number].offset,L_SET); RecordHdr::read(this,current_rec); AttributeRecordHdr::read(this,attribhdr); //printf("IEEEIO:attribrechdr = ds,nt,ns %d,%d,%d\n", // attribhdr.datasize, // attribhdr.numbertype, // attribhdr.namesize); typeID = Int2DataType(attribhdr.numbertype); nelem = attribhdr.datasize/sizeOf(typeID); return -1; } int IEEEIO::readAttributeInfo(CONST char *aname,IObase::DataType &typeID,Long &nelem){ // by name if(accessmode==IObase::Write) return -1; FlexArray *attribs= &(rec_table[datasetnumber].attributes); for(int i=0;igetSize();i++){ if(!strcmp(aname,(*attribs)[i].name)){ // must read the record to get this info AttributeRecordHdr attribhdr; IEEEIO::lseek((*attribs)[i].offset,L_SET); RecordHdr::read(this,current_rec); AttributeRecordHdr::read(this,attribhdr); typeID = Int2DataType(attribhdr.numbertype); nelem = attribhdr.datasize/sizeOf(typeID); return i; } } return -1; // Attribute not found } int IEEEIO::readAttribute(int number,void *data){ if(accessmode==IObase::Write) return -1; FlexArray *attribs= &(rec_table[datasetnumber].attributes); if(number>=(*attribs).getSize()) return -1; // > number of attributes AttributeRecordHdr attribhdr; IEEEIO::lseek((*attribs)[number].offset,L_SET); RecordHdr::read(this,current_rec); AttributeRecordHdr::read(this,attribhdr); IObase::DataType typeID = Int2DataType(attribhdr.numbertype); if(typeID==IObase::Error) return -1; // read failed due to bad datatype // Long8 nelem = attribhdr.datasize/sizeOf(typeID); IEEEIO::lseek(attribhdr.namesize,L_INCR); int sz=IEEEIO::read(data,attribhdr.datasize)/sizeOf(typeID); if(swapbytes) byteswapBuffer(data,sz,sizeOf(typeID)); if(typeID==IObase::String){ char *cdata=(char *)data; cdata[sz]='\0'; /* Null Terminate String data */ } return sz; // returns number of elements read } int IEEEIO::nAttributes(){ if(datasetnumber<0 || accessmode == IObase::Write) return -1; return rec_table[datasetnumber].attributes.getSize(); } //================Chunking Interface----------------------- void IEEEIO::clearChunk(int nbytes){ #ifdef T3E #define DISKBLOCKSIZE 128*1024 #else #define DISKBLOCKSIZE 8192 #endif // This is a wasteful feature that will be eliminated in later releases // This is currently done because I needed the disk space to be zero'ed // prior to writing for debugging purposes... Could have a writeStream // class. int nwritten; char dummy[DISKBLOCKSIZE]; for(int i=0;iDISKBLOCKSIZE){ nwritten=IEEEIO::write(dummy,DISKBLOCKSIZE); if(nwritten<=0) return; // this aint working nbytes-=nwritten; } if(nbytes>0) IEEEIO::write(dummy,nbytes); // write the remaining bytes #undef DISKBLOCKSIZE } int IEEEIO::reserveChunk(IObase::DataType typeID,int rank,CONST int *dims){ reserveStream(typeID,rank,dims); // sets up the chunking state machine // clearing does not appear to be needed... just need header //clearChunk(IObase::nBytes(typeID,rank,dims)); // this pre-clears the reserved area to 0. (inefficient) return 1; } // Chunking should probably include striding.... // But thats a pain since you'll need to read + write to // Accomplish that (yuck!). Lots of seeking. // Lets leave striding out for now. // should also check for contiguous data (eg. slicing) and optimize for that layout // for now, the streaming interface best serves contiguous data. int IEEEIO::writeChunk(CONST int *dims,CONST int *origin,const void *data){ int i,sz=-1; // make sure its the EOF if(accessmode==IObase::Read) { fprintf(stderr,"IEEEIO::writeChunk(): Error! Access is ReadOnly\n"); return 0; } // should have a "chunk reserved" flag set. if(current_reserved_chunk!=datasetnumber){ fprintf(stderr,"IEEEIO::writeChunk(): Error! You forgot to reserve space for the chunk using IO::reserveChunk()\n"); return 0; } // now we need to seek to the position of the data int rank = current_dat.rank; IObase::DataType typeID = IObase::Int2DataType(current_dat.numbertype); int typesize=sizeOf(typeID); // Long8 basefileoffset = rec_table[datasetnumber].offset+ Long8 basefileoffset = stream_offset + RecordHdrSize+DataRecordHdrSize+sizeof(Int)*current_dat.rank; // T3E Kludge Long8 chunkcolumnsize=dims[0]*typesize; // stride between columns in chunk Long8 filecolumnsize=chunkdims[0]*typesize; // stride between columns on disk (full array size) Long8 originoffset; Long8 accumdims; for(originoffset=0,accumdims=typesize,i=0;ichunkdims[i]){ fprintf(stderr,"IEEEIO::writeChunk(): ERROR!! specified dims and origin exceed reserved block size\n"); fprintf(stderr,"\tfor dimension %u origin=%u and dims=%u\n",i,origin[i], dims[i]); fprintf(stderr,"\torigin+dims=%u whereas the maximum must be less than %u\n",origin[i]+dims[i],chunkdims[i]); } } originoffset+=basefileoffset; // for absolute seeking Long8 maxindex,minindex; minindex=basefileoffset; maxindex=basefileoffset+current_dat.datasize; int ncolumns; // computed in loop below for(ncolumns=1,i=1;imaxindex){ fprintf(stderr,"WriteChunk() inconsistency. Writing greater than maximum index\n"); fprintf(stderr,"\tCol[%u]: Requested %u, but maxindex= %u\n", i,(unsigned int)originoffset,(unsigned int)maxindex); } if((originoffset+chunkcolumnsize)>maxindex){ fprintf(stderr,"WriteChunk() inconsistency. This write will overrun the reserved data\n"); fprintf(stderr,"\tCol[%u]: Requested %u, maxindex %u, and the write of %u will run to %u\n", i,(unsigned int)originoffset,(unsigned int)maxindex,(unsigned int)chunkcolumnsize,(unsigned int)(originoffset+chunkcolumnsize)); } IEEEIO::lseek(originoffset,L_SET); sz=IEEEIO::write(((const char*)data)+i*chunkcolumnsize,(int)chunkcolumnsize); originoffset+=filecolumnsize; for(Long8 j=1,idx=dims[1],planesize=filecolumnsize; j<(rank-1) && !((i+1)%idx); idx*=dims[++j]){ Long8 extraoffset=planesize*(chunkdims.getData()[j]-dims[j]); originoffset+=extraoffset; planesize*=dims[j]; } } // now swap the data back to native order... if(swapbytes) byteswapBuffer(cast_union.data,IObase::nElements(rank,dims),typesize); return sz; } int IEEEIO::readChunk(CONST int *dims,CONST int *origin,void *data){ int sz=-1,i; if(accessmode!=IObase::Read && accessmode!=IObase::Append) return 0; // gonna have to stride through this sucker... (yuck) // use the same chunkdims for this. Must set during readinfo... // now we need to seek to the position of the data int rank = current_dat.rank; IObase::DataType typeID = IObase::Int2DataType(current_dat.numbertype); int typesize=sizeOf(typeID); Long8 basefileoffset = rec_table[datasetnumber].offset+ // sizeof(RecordHdr)+sizeof(DataRecordHdr)+sizeof(Int)*current_dat.rank; RecordHdrSize+DataRecordHdrSize+sizeof(Int)*current_dat.rank; // T3E Kludge Long8 chunkcolumnsize=dims[0]*typesize; // stride between columns in chunk Long8 filecolumnsize=chunkdims[0]*typesize; // stride between columns on disk (full array size) Long8 originoffset; Long8 accumdims; // compute the offset into the data on disk required by the chunk origin (initial offset) for(originoffset=0,accumdims=typesize,i=0;i0){ Long8 endpos = rec_table[rec_table.getSize()-1].end_offset; IEEEIO::lseek(endpos,L_SET); // don't know if this is costly } else { IEEEIO::lseek(0,L_XTND); // seek to end of file } // if 0-length seeks are costly, then alternative // logic can be constructed to ensure writes to end of file if(chunkdims.getSize() != rank) chunkdims.setSize(rank); // KLUDGE! for(i=0,hdr.datasize=1;i0){ fprintf(stderr,"IEEEIO::bufferOff() ERROR!!!!"); fprintf(stderr,"\tFile %s is either paused or invalid, so I can't deallocate the write buffer safely\n",filename); return; } //printf("bufferoff on a=%ld v=%ld f=%ld b=%d\n", // actual_position,virtual_position,file_length,writebuffercount); this->flush(); #ifdef SGI // block-aligned write buffer free(writebuffer); #else delete writebuffer; #endif writebuffer=0; } int IEEEIO::pause(){ if(fid<0) return 0; // fail this->flush(); // flush the buffer to pause savedposition=getPosition(); close(fid); fid=-1; return 1; // success } int IEEEIO::resume(){ if(fid>=0 || savedposition<0) return 0; // fail switch(IObase::accessmode){ case IObase::Read: fid=open(filename,O_RDONLY,0644); break; case IObase::Write: fid=open(filename,O_WRONLY,0644); break; case IObase::Append: fid=open(filename,O_RDWR,0644); break; } if(fid<0){ printf("IEEEIO::resume failed for file %s\n",filename); return -1; } ::lseek(fid,savedposition,L_SET); // this is OK.. savedposition=-1; return 1; // success } //=================== Experimental MPIO methods ================= void IEEEIO::getFilename(char *fn,int maxlen){ strncpy(fn,filename,maxlen-1); } int IEEEIO::reserveStream(IObase::DataType typeID,int rank,CONST int *dims,Long8 &soffset){ int rv=this->reserveStream(typeID,rank,dims); soffset = stream_offset + RecordHdrSize+DataRecordHdrSize+sizeof(Int)* current_dat.rank; return rv; } // writes to explicit offset in file! // always overrides the current filepointer position! // only useful for the master process *if* it // must write to a non-zero location // *** So the usage scenario is that the master // has the IEEEIO file handle and getFilename() is // used to pass the name of the file to the slave procs // which open it O_WRITE. The master gets the offset // for a particular stream from the special version // of the method reserverStream and uses that to position // their respective file pointers. All slaves and // the master then write to the file simultaneously to // different file offsets. It remains to be seen how // well this works without O_DIRECT. We can use timing // loops to stagger the writes somewhat though. int IEEEIO::writeStream(const void *data,int length,Long8 soffset){ IEEEIO::lseek(soffset,L_SET); streaming=1; return writeStream(data,length); } /*********************Fortran Interface**********************/ Long8 f_ieee_open (char *file,char *accessname,int flen,int alen){ alen = alen; flen = flen; // would have used tolower(), but it doesn't exist everywhere.... :( IObase::AccessMode mode; if(*accessname=='R' || *accessname=='r') mode=IObase::Read; else if(*accessname=='W' || *accessname=='w' || *accessname=='C' || *accessname=='c') mode=IObase::Write; else if(*accessname=='A' || *accessname=='a') mode=IObase::Append; else { fprintf(stderr,"IEEEopen(): Error unknown option [%s] to open file %s\n", accessname,file); return 0; } IObase *fid=new IEEEIO(file,mode); if(fid->isValid()) return (Long8)fid; else delete fid; // file open failed return 0; } /* Fortran passes strings without 0-bytes. C requires a 0-byte at the end of a string. We may not modify the passed string, because it might reside in a read only text segment, thus we need a local copy to append a 0-byte. Performance is not an issue here, because file access is much slower than this little memory shifting anyway. */ Long8 f_ieee_openr(char *filename,int namelen){ char*fn = new char[namelen+1]; strncpy(fn, filename, namelen); fn[namelen]='\0'; Long8 ID = (Long8)(new IEEEIO(filename,IObase::Read)); delete fn; return ID; } Long8 f_ieee_openw(char *filename,int namelen){ char*fn = new char[namelen+1]; strncpy(fn, filename, namelen); fn[namelen]='\0'; Long8 ID = (Long8)(new IEEEIO(filename,IObase::Write)); delete fn; return ID; } Long8 f_ieee_opena(char *filename,int namelen){ char*fn = new char[namelen+1]; strncpy(fn, filename, namelen); fn[namelen]='\0'; Long8 ID = (Long8)(new IEEEIO(filename,IObase::Append)); delete fn; return ID; } void ieee_bufon(Long8 *fileID,int bufsize){ IEEEIO *f=(IEEEIO*)(*fileID); if(bufsize<0) f->bufferOn(); // use default size else f->bufferOn(bufsize); // or specify } void ieee_bufoff(Long8 *fileID){ IEEEIO *f=(IEEEIO*)(*fileID); f->bufferOff(); } IOFile IEEEopen(char *filename,char *accessname){ // Parse all of the ansi stdio access option strings IObase::AccessMode mode; if(!strcmp(accessname,"read") || !strcmp(accessname,"r") || !strcmp(accessname,"rb")) mode=IObase::Read; else if(*accessname=='a') mode=IObase::Append; else if(!strcmp(accessname,"write") || !strcmp(accessname,"create") || !strcmp(accessname,"w") || !strcmp(accessname,"wb") || !strcmp(accessname,"w+") || !strcmp(accessname,"w+b") || !strcmp(accessname,"wb+")) mode=IObase::Write; else{ fprintf(stderr,"IEEEopen(): Error unknown option [%s] to open file %s\n", accessname,filename); return 0; } IObase *fid=new IEEEIO(filename,mode); if(fid->isValid()) return (IOFile)fid; else delete fid; // file open failed return 0; // unknown option } IOFile IEEEopenRead(char *filename){ return (IOFile)(new IEEEIO(filename,IObase::Read)); } IOFile IEEEopenWrite(char *filename){ return (IOFile)(new IEEEIO(filename,IObase::Create)); } IOFile IEEEopenAppend(char *filename){ return (IOFile)(new IEEEIO(filename,IObase::Append)); } void IEEEbufferOn(IOFile fileID,int bufsize){ IEEEIO *f=(IEEEIO*)fileID; if(bufsize<0) f->bufferOn(); // use default size else f->bufferOn(bufsize); // or specify } void IEEEbufferOff(IOFile fileID){ IEEEIO *f=(IEEEIO*)fileID; f->bufferOff(); }