#include #include #include #include #include #include #include "geners/BinaryArchiveBase.hh" #include "geners/CatalogIO.hh" #include "geners/binaryIO.hh" #include "geners/platform.hh" #ifdef USING_MS_WINDOWS_CPP #define strcasecmp _stricmp #endif #ifdef GENERS_BINARY_ARCHIVE_FORMAT_ID #undef GENERS_BINARY_ARCHIVE_FORMAT_ID #endif #define GENERS_BINARY_ARCHIVE_FORMAT_ID (0x1f2e3d4c) static bool parse_unsigned(std::ostringstream& err, const char *c, unsigned *result) { char *endptr; errno = 0; const unsigned long value = strtoul(c, &endptr, 0); if (errno || *endptr != '\0') { err << "expected an unsigned integer, got \"" << c << '"'; if (errno) err << ", " << strerror(errno); return false; } if (value > UINT_MAX) { err << "unsigned value \"" << c << "\" is out of range"; return false; } *result = value; return true; } static bool parse_int(std::ostringstream& err, const char *c, int *result) { char *endptr; errno = 0; const long value = strtol(c, &endptr, 0); if (errno || *endptr != '\0') { err << "expected an integer, got \"" << c << '"'; if (errno) err << ", " << strerror(errno); return false; } if (value < INT_MIN || value > INT_MAX) { err << "integer value \"" << c << "\" is out of range"; return false; } *result = value; return true; } namespace gs { BinaryArchiveBase::BinaryArchiveBase(const char* name, const char* mode) : AbsArchive(name), mode_(parseMode(mode)), errorStream_(0), cStream_(0), catalog_(0), storedEntryId_(0), storedLocationId_(0), catalogIsSet_(false), addCatalogToData_(false) { CStringStream::CompressionMode m = CStringStream::NOT_COMPRESSED; int compressionLevel = -1; unsigned minSizeToCompress = 1024U; unsigned bufSize = 1048576U; // 1024*1024 std::ostringstream err; modeIsValid_ = parseArchiveOptions(err, mode, &m, &compressionLevel, &minSizeToCompress, &bufSize, &addCatalogToData_); if (modeIsValid_) cStream_ = new CStringStream(m, compressionLevel, minSizeToCompress, bufSize); else { errorStream() << "In BinaryArchiveBase constructor: " << "invalid archive opening mode \"" << mode << '"'; const std::string& errInfo = err.str(); if (!errInfo.empty()) errorStream() << ": " << errInfo; } } void BinaryArchiveBase::releaseClassIds() { delete storedEntryId_; storedEntryId_ = 0; delete storedLocationId_; storedLocationId_ = 0; } BinaryArchiveBase::~BinaryArchiveBase() { releaseClassIds(); delete errorStream_; delete catalog_; delete cStream_; } void BinaryArchiveBase::writeHeader(std::ostream& os) { const unsigned format = GENERS_BINARY_ARCHIVE_FORMAT_ID; write_pod(os, format); // Write some other info const unsigned multiplex = addCatalogToData_ ? 1 : 0; const unsigned sizeoflong = sizeof(long); const unsigned infoword = (sizeoflong << 1) | multiplex; write_pod(os, infoword); if (multiplex) { // Write class ids for CatalogEntry and ItemLocation releaseClassIds(); storedEntryId_ = new ClassId(ClassId::makeId()); storedEntryId_->write(os); storedLocationId_ = new ClassId(ClassId::makeId()); storedLocationId_->write(os); } } bool BinaryArchiveBase::readHeader(std::istream& is) { const unsigned expectedFormat = GENERS_BINARY_ARCHIVE_FORMAT_ID; is.seekg(0, std::ios_base::beg); unsigned format = 0; read_pod(is, &format); if (format != expectedFormat) return false; unsigned infoword = 0xffffffff; read_pod(is, &infoword); const unsigned multiplex = infoword & 0x1U; const unsigned sizeoflong = infoword >> 1; // The following check will make sure that we are not reading // an archive created on a 32-bit machine with a 64-bit system // (and otherwise) if (sizeoflong != sizeof(long)) return false; addCatalogToData_ = multiplex; if (addCatalogToData_) { releaseClassIds(); storedEntryId_ = new ClassId(is, 1); storedLocationId_ = new ClassId(is, 1); // Can't open this archive for update if the above class ids // are obsolete -- otherwise we will loose the capability to // restore the catalog if (mode_ & std::ios_base::out) { const ClassId& entryId = ClassId::makeId(); const ClassId& locId = ClassId::makeId(); if (entryId != *storedEntryId_ || locId != *storedLocationId_) throw IOInvalidData( "In gs::BinaryArchiveBase::readHeader: this " "archive can no longer be open for update as it was " "created using an older version of I/O software"); } } return !is.fail(); } void BinaryArchiveBase::openDataFile(std::fstream& stream, const char* filename) { assert(filename); if (stream.is_open()) stream.close(); stream.clear(); stream.open(filename, mode_); if (!stream.is_open()) throw IOOpeningFailure("gs::BinaryArchiveBase::openDataFile", filename); // Do we need to write the header out or to read it in? bool writeHead = false; if (mode_ & std::ios_base::out) { if (mode_ & std::ios_base::trunc) writeHead = true; else if (isEmptyFile(stream)) writeHead = true; } if (writeHead) { writeHeader(stream); if (stream.fail()) { stream.close(); std::string e = "In gs::BinaryArchiveBase::openDataFile: " "failed to write archive header to file \""; e += filename; e += "\""; throw IOWriteFailure(e); } } else { if (!readHeader(stream)) { const bool failed = stream.fail(); stream.close(); std::string e = "In gs::BinaryArchiveBase::openDataFile: "; if (failed) { e += "could not read archive header from file \""; e += filename; e += "\""; throw IOReadFailure(e); } else { e += "no valid archive header in file \""; e += filename; e += "\""; throw IOInvalidData(e); } } } } void BinaryArchiveBase::setCatalog(AbsCatalog* c) { if (c) { assert(!catalogIsSet_); catalogIsSet_ = true; } delete catalog_; catalog_ = c; } void BinaryArchiveBase::itemSearch( const SearchSpecifier& namePattern, const SearchSpecifier& categoryPattern, std::vector* idsFound) const { if (catalog_) catalog_->search(namePattern, categoryPattern, idsFound); else { assert(idsFound); idsFound->clear(); } } bool BinaryArchiveBase::parseArchiveOptions( std::ostringstream& err, const char* modeIn, CStringStream::CompressionMode* m, int* compressionLevel, unsigned* minSizeToCompress, unsigned* bufSize, bool* multiplexCatalog) { if (!modeIn) return true; std::string cmode(modeIn ? modeIn : ""); if (cmode.empty()) return true; char* mode = const_cast(cmode.c_str()); unsigned cnt = 0; for (char* opt = strtok(mode, ":"); opt; opt = strtok(0, ":"), ++cnt) { // Skip the first word -- this is the file opening mode if (!cnt) continue; char* eq = strchr(opt, '='); if (eq) { // Get rid of spaces around option name char* optname = opt; while (isspace(*optname) && optname < eq) ++optname; if (optname == eq) { err << "invalid binary archive option \"\""; return false; } char* optend = eq - 1; while (isspace(*optend)) --optend; ++optend; *optend = '\0'; // Get rid of spaces around option value char* optval = eq + 1; while (*optval && isspace(*optval)) ++optval; if (!*optval) { err << "invalid binary archive option value \"\""; return false; } char* valend = opt + strlen(opt) - 1; while (isspace(*valend)) --valend; ++valend; *valend = '\0'; if (strlen(optval) == 0) { err << "invalid binary archive option value \"\""; return false; } // Go over possible options if (!strcasecmp(optname, "z")) { // Compression type if (!CStringStream::getCompressionModeByName(optval, m)) { err << "invalid compression type \"" << optval << '"'; return false; } } else if (!strcasecmp(optname, "cl")) { // Compression level if (!parse_int(err, optval, compressionLevel)) return false; if (*compressionLevel < -1 || *compressionLevel > 9) { err << "compression level is out of range"; return false; } } else if (!strcasecmp(optname, "cb")) { // Compression buffer size if (!parse_unsigned(err, optval, bufSize)) return false; } else if (!strcasecmp(optname, "cm")) { // Compression minimum size if (!parse_unsigned(err, optval, minSizeToCompress)) return false; } else if (!strcasecmp(optname, "cat")) { // Internal or external catalog if (optval[0] == 'i' || optval[0] == 'I') *multiplexCatalog = true; else if (optval[0] == 's' || optval[0] == 'S') *multiplexCatalog = false; else { err << "invalid catalog mode \"" << optval << '"'; return false; } } else { // Unknown option err << "unrecognized binary archive option \"" << optname << '"'; return false; } } else { err << "invalid binary archive option \"" << opt << '"'; return false; } } return true; } std::ios_base::openmode BinaryArchiveBase::parseMode(const char* mode) { std::ios_base::openmode m = std::ios_base::binary; if (mode) { const unsigned len = strlen(mode); for (unsigned i=0; i idlist; catalog_->search(reference.namePattern(), reference.categoryPattern(), &idlist); const unsigned long nfound = idlist.size(); for (unsigned long i=0; i pentry = catalog_->retrieveEntry(idlist[i]); if (reference.isIOCompatible(*pentry)) addItemToReference(reference, idlist[i]); } } } bool BinaryArchiveBase::isEmptyFile(std::fstream& s) { s.seekg(0, std::ios_base::end); return s.tellg() == std::streampos(0); } std::istream& BinaryArchiveBase::inputStream(const unsigned long long id, long long *sz) { unsigned long long length = 0; unsigned compressionCode = 0; std::istream& is = plainInputStream(id, &compressionCode, &length); if (cStream_->compressionMode() == CStringStream::NOT_COMPRESSED) { if (sz) *sz = -1LL; return is; } else { cStream_->readCompressed(is, compressionCode, length); if (sz) { std::streamoff off = cStream_->tellp(); *sz = off; } return *cStream_; } } std::ostream& BinaryArchiveBase::outputStream() { return plainOutputStream(); } std::ostream& BinaryArchiveBase::compressedStream(std::ostream& os) { if (cStream_->compressionMode() == CStringStream::NOT_COMPRESSED) return os; else { cStream_->reset(); cStream_->setSink(os); return *cStream_; } } unsigned BinaryArchiveBase::flushCompressedRecord(std::ostream&) { CStringStream::CompressionMode m = cStream_->compressionMode(); if (m != CStringStream::NOT_COMPRESSED) { cStream_->flush(); m = cStream_->writeCompressed(); } return static_cast(m); } }