#include #include "geners/BinaryFileArchive.hh" #include "geners/uriUtils.hh" #include "geners/ContiguousCatalog.hh" #include "geners/WriteOnlyCatalog.hh" #include "geners/IOException.hh" #include "geners/streamposIO.hh" namespace gs { BinaryFileArchive::BinaryFileArchive(const char* basename, const char* mode, const char* ann, const unsigned dataFileBufferSize, const unsigned catalogFileBufferSize) : BinaryArchiveBase(basename, mode), filebuf_(0), catabuf_(0), annotation_(ann ? std::string(ann) : std::string("")), dataFileName_(AbsArchive::name() + ".0.gsbd"), // binary data catalogFileName_(AbsArchive::name() + ".gsbmf"), // binary metafile dataFileURI_(localFileURI(dataFileName_.c_str())), lastpos_(0), jumppos_(0), catalogMergeLevel_(1), annotationsMerged_(false), streamFlushed_(true) { if (!modeValid()) return; try { // Get a new buffer for the data and open the data stream if (dataFileBufferSize) filebuf_ = new char[dataFileBufferSize]; dataStream_.rdbuf()->pubsetbuf(filebuf_, dataFileBufferSize); openDataFile(dataStream_, dataFileName_.c_str()); dataStream_.seekp(0, std::ios_base::end); // Get a new buffer for the catalog and open the catalog stream. // We may have to rewrite the complete catalog, so remove the flag // std::ios_base::app from the opening mode. if (catalogFileBufferSize) catabuf_ = new char[catalogFileBufferSize]; catStream_.rdbuf()->pubsetbuf(catabuf_, catalogFileBufferSize); catStream_.open(catalogFileName_.c_str(), openmode() & ~std::ios_base::app); if (!catStream_.is_open()) throw IOOpeningFailure("gs::BinaryFileArchive constructor", catalogFileName_); // Can we use a write-only catalog? if (openmode() & std::ios_base::in) { // Reading is allowed. Have to use in-memory catalog. // If the file data already exists, get the catalog in. if (isEmptyFile(catStream_)) setCatalog(new ContiguousCatalog()); else readCatalog(); } else { // Yes, we can use a write-only catalog. // Is the catalog file empty? If so, write out // the stuff needed at the beginning of the file. // If not, assume that the necessary stuff is // already there. Note that in this case we will // not be able to add the annotation. if (isEmptyFile(catStream_)) { setCatalog(new WriteOnlyCatalog(catStream_)); writeCatalog(); } else { // Have to read in the catalog contents catStream_.close(); catStream_.clear(); catStream_.open(catalogFileName_.c_str(), openmode() | std::ios_base::in); if (!catStream_.is_open()) throw IOOpeningFailure( "gs::BinaryFileArchive constructor", catalogFileName_); readCatalog(); catStream_.seekp(0, std::ios_base::end); } } } catch (std::exception& e) { setCatalog(0); releaseBuffers(); errorStream() << e.what(); } } void BinaryFileArchive::releaseBuffers() { if (dataStream_.is_open()) dataStream_.close(); if (catStream_.is_open()) catStream_.close(); catStream_.rdbuf()->pubsetbuf(0, 0); dataStream_.rdbuf()->pubsetbuf(0, 0); delete [] catabuf_; catabuf_ = 0; delete [] filebuf_; filebuf_ = 0; } BinaryFileArchive::~BinaryFileArchive() { flush(); releaseBuffers(); } void BinaryFileArchive::writeCatalog() { if (isOpen()) { if (!annotationsMerged_) { if (annotation_.size()) catalogAnnotations_.push_back(annotation_); annotationsMerged_ = true; } const unsigned compress = static_cast(compressionMode()); if (!writeBinaryCatalog(catStream_, compress, catalogMergeLevel_, catalogAnnotations_, *catalog())) { std::ostringstream os; os << "In BinaryFileArchive::writeCatalog: " << "failed to write catalog data to file " << catalogFileName_; throw IOWriteFailure(os.str()); } } } std::istream& BinaryFileArchive::plainInputStream( const unsigned long long id, unsigned* compressionCode, unsigned long long* length) { if (isOpen()) { assert(openmode() & std::ios_base::in); if (!id) throw std::invalid_argument( "In gs::BinaryFileArchive::plainInputStream: invalid item id"); std::streampos pos; if (!catalog()->retrieveStreampos( id, compressionCode, length, &pos)) { std::ostringstream os; os << "In gs::BinaryFileArchive::plainInputStream: " << "failed to locate item with id " << id << "in the catalog stored in file " << catalogFileName_; throw std::invalid_argument(os.str()); } if (!streamFlushed_) { dataStream_.flush(); streamFlushed_ = true; } dataStream_.seekg(pos); } return dataStream_; } unsigned long long BinaryFileArchive::addToCatalog( const AbsRecord& record, const unsigned compressionCode, const unsigned long long itemLength) { unsigned long long id = 0; if (isOpen()) { id = catalog()->makeEntry( record, compressionCode, itemLength, ItemLocation(lastpos_, dataFileURI_.c_str())); if (id && injectMetadata()) { const CatalogEntry* entry = catalog()->lastEntryMade(); assert(entry); dataStream_.seekp(0, std::ios_base::end); std::streampos now = dataStream_.tellp(); if (entry->write(dataStream_)) { dataStream_.seekp(jumppos_); write_pod(dataStream_, now); dataStream_.seekp(0, std::ios_base::end); } else id = 0; } } return id; } std::ostream& BinaryFileArchive::plainOutputStream() { if (isOpen()) { assert(openmode() & std::ios_base::out); dataStream_.seekp(0, std::ios_base::end); if (injectMetadata()) { jumppos_ = dataStream_.tellp(); std::streampos catpos(0); write_pod(dataStream_, catpos); } lastpos_ = dataStream_.tellp(); streamFlushed_ = false; } return dataStream_; } void BinaryFileArchive::flush() { if (isOpen()) { if (!streamFlushed_) { dataStream_.flush(); streamFlushed_ = true; } if (openmode() & std::ios_base::out) { if (dynamic_cast(catalog()) == 0) writeCatalog(); catStream_.flush(); } } } }