00001 #include <cerrno>
00002 #include <cctype>
00003 #include <cstdlib>
00004 #include <cstring>
00005 #include <climits>
00006 #include <cassert>
00007
00008 #include "Alignment/Geners/interface/BinaryArchiveBase.hh"
00009 #include "Alignment/Geners/interface/CatalogIO.hh"
00010 #include "Alignment/Geners/interface/binaryIO.hh"
00011
00012 #ifdef GENERS_BINARY_ARCHIVE_FORMAT_ID
00013 #undef GENERS_BINARY_ARCHIVE_FORMAT_ID
00014 #endif
00015 #define GENERS_BINARY_ARCHIVE_FORMAT_ID (0x1f2e3d4c)
00016
00017 static bool parse_unsigned(std::ostringstream& err,
00018 const char *c, unsigned *result)
00019 {
00020 char *endptr;
00021 errno = 0;
00022 const unsigned long value = strtoul(c, &endptr, 0);
00023 if (errno || *endptr != '\0')
00024 {
00025 err << "expected an unsigned integer, got \"" << c << '"';
00026 if (errno) err << ", " << strerror(errno);
00027 return false;
00028 }
00029 if (value > UINT_MAX)
00030 {
00031 err << "unsigned value \"" << c << "\" is out of range";
00032 return false;
00033 }
00034 *result = value;
00035 return true;
00036 }
00037
00038 static bool parse_int(std::ostringstream& err,
00039 const char *c, int *result)
00040 {
00041 char *endptr;
00042 errno = 0;
00043 const long value = strtol(c, &endptr, 0);
00044 if (errno || *endptr != '\0')
00045 {
00046 err << "expected an integer, got \"" << c << '"';
00047 if (errno) err << ", " << strerror(errno);
00048 return false;
00049 }
00050 if (value < INT_MIN || value > INT_MAX)
00051 {
00052 err << "integer value \"" << c << "\" is out of range";
00053 return false;
00054 }
00055 *result = value;
00056 return true;
00057 }
00058
00059 namespace gs {
00060 BinaryArchiveBase::BinaryArchiveBase(const char* name, const char* mode)
00061 : AbsArchive(name),
00062 mode_(parseMode(mode)),
00063 errorStream_(0),
00064 cStream_(0),
00065 catalog_(0),
00066 storedEntryId_(0),
00067 storedLocationId_(0),
00068 catalogIsSet_(false),
00069 addCatalogToData_(false)
00070 {
00071 CStringStream::CompressionMode m = CStringStream::NOT_COMPRESSED;
00072 int compressionLevel = -1;
00073 unsigned minSizeToCompress = 1024U;
00074 unsigned bufSize = 1048576U;
00075
00076 std::ostringstream err;
00077 modeIsValid_ = parseArchiveOptions(err, mode, &m, &compressionLevel,
00078 &minSizeToCompress, &bufSize,
00079 &addCatalogToData_);
00080 if (modeIsValid_)
00081 cStream_ = new CStringStream(m, compressionLevel,
00082 minSizeToCompress, bufSize);
00083 else
00084 {
00085 errorStream() << "In BinaryArchiveBase constructor: "
00086 << "invalid archive opening mode \"" << mode << '"';
00087 const std::string& errInfo = err.str();
00088 if (!errInfo.empty())
00089 errorStream() << ": " << errInfo;
00090 }
00091 }
00092
00093
00094 void BinaryArchiveBase::releaseClassIds()
00095 {
00096 delete storedEntryId_; storedEntryId_ = 0;
00097 delete storedLocationId_; storedLocationId_ = 0;
00098 }
00099
00100
00101 BinaryArchiveBase::~BinaryArchiveBase()
00102 {
00103 releaseClassIds();
00104 delete errorStream_;
00105 delete catalog_;
00106 delete cStream_;
00107 }
00108
00109
00110 void BinaryArchiveBase::writeHeader(std::ostream& os)
00111 {
00112 const unsigned format = GENERS_BINARY_ARCHIVE_FORMAT_ID;
00113 write_pod(os, format);
00114
00115
00116 const unsigned multiplex = addCatalogToData_ ? 1 : 0;
00117 const unsigned sizeoflong = sizeof(long);
00118 const unsigned infoword = (sizeoflong << 1) | multiplex;
00119 write_pod(os, infoword);
00120
00121 if (multiplex)
00122 {
00123
00124 releaseClassIds();
00125 storedEntryId_ = new ClassId(ClassId::makeId<CatalogEntry>());
00126 storedEntryId_->write(os);
00127 storedLocationId_ = new ClassId(ClassId::makeId<ItemLocation>());
00128 storedLocationId_->write(os);
00129 }
00130 }
00131
00132
00133 bool BinaryArchiveBase::readHeader(std::istream& is)
00134 {
00135 const unsigned expectedFormat = GENERS_BINARY_ARCHIVE_FORMAT_ID;
00136 is.seekg(0, std::ios_base::beg);
00137 unsigned format = 0;
00138 read_pod(is, &format);
00139 if (format != expectedFormat)
00140 return false;
00141
00142 unsigned infoword = 0xffffffff;
00143 read_pod(is, &infoword);
00144 const unsigned multiplex = infoword & 0x1U;
00145 const unsigned sizeoflong = infoword >> 1;
00146
00147
00148
00149
00150 if (sizeoflong != sizeof(long))
00151 return false;
00152
00153 addCatalogToData_ = multiplex;
00154 if (addCatalogToData_)
00155 {
00156 releaseClassIds();
00157 storedEntryId_ = new ClassId(is, 1);
00158 storedLocationId_ = new ClassId(is, 1);
00159
00160
00161
00162
00163 if (mode_ & std::ios_base::out)
00164 {
00165 const ClassId& entryId = ClassId::makeId<CatalogEntry>();
00166 const ClassId& locId = ClassId::makeId<ItemLocation>();
00167 if (entryId != *storedEntryId_ || locId != *storedLocationId_)
00168 throw IOInvalidData(
00169 "In gs::BinaryArchiveBase::readHeader: this "
00170 "archive can no longer be open for update as it was "
00171 "created using an older version of I/O software");
00172 }
00173 }
00174 return !is.fail();
00175 }
00176
00177
00178 void BinaryArchiveBase::openDataFile(std::fstream& stream,
00179 const char* filename)
00180 {
00181 assert(filename);
00182 if (stream.is_open())
00183 stream.close();
00184 stream.clear();
00185 stream.open(filename, mode_);
00186 if (!stream.is_open())
00187 throw IOOpeningFailure("gs::BinaryArchiveBase::openDataFile",
00188 filename);
00189
00190
00191 bool writeHead = false;
00192 if (mode_ & std::ios_base::out)
00193 {
00194 if (mode_ & std::ios_base::trunc)
00195 writeHead = true;
00196 else if (isEmptyFile(stream))
00197 writeHead = true;
00198 }
00199
00200 if (writeHead)
00201 {
00202 writeHeader(stream);
00203 if (stream.fail())
00204 {
00205 stream.close();
00206 std::string e = "In gs::BinaryArchiveBase::openDataFile: "
00207 "failed to write archive header to file \"";
00208 e += filename;
00209 e += "\"";
00210 throw IOWriteFailure(e);
00211 }
00212 }
00213 else
00214 {
00215 if (!readHeader(stream))
00216 {
00217 const bool failed = stream.fail();
00218 stream.close();
00219 std::string e = "In gs::BinaryArchiveBase::openDataFile: ";
00220 if (failed)
00221 {
00222 e += "could not read archive header from file \"";
00223 e += filename;
00224 e += "\"";
00225 throw IOReadFailure(e);
00226 }
00227 else
00228 {
00229 e += "no valid archive header in file \"";
00230 e += filename;
00231 e += "\"";
00232 throw IOInvalidData(e);
00233 }
00234 }
00235 }
00236 }
00237
00238
00239 void BinaryArchiveBase::setCatalog(AbsCatalog* c)
00240 {
00241 if (c)
00242 {
00243 assert(!catalogIsSet_);
00244 catalogIsSet_ = true;
00245 }
00246 delete catalog_;
00247 catalog_ = c;
00248 }
00249
00250
00251 void BinaryArchiveBase::itemSearch(
00252 const SearchSpecifier& namePattern,
00253 const SearchSpecifier& categoryPattern,
00254 std::vector<unsigned long long>* idsFound) const
00255 {
00256 if (catalog_)
00257 catalog_->search(namePattern, categoryPattern, idsFound);
00258 else
00259 {
00260 assert(idsFound);
00261 idsFound->clear();
00262 }
00263 }
00264
00265
00266 bool BinaryArchiveBase::parseArchiveOptions(
00267 std::ostringstream& err,
00268 const char* modeIn, CStringStream::CompressionMode* m,
00269 int* compressionLevel, unsigned* minSizeToCompress,
00270 unsigned* bufSize, bool* multiplexCatalog)
00271 {
00272 if (!modeIn)
00273 return true;
00274 std::string cmode(modeIn ? modeIn : "");
00275 if (cmode.empty())
00276 return true;
00277 char* mode = const_cast<char*>(cmode.c_str());
00278
00279 unsigned cnt = 0;
00280 for (char* opt = strtok(mode, ":"); opt; opt = strtok(0, ":"), ++cnt)
00281 {
00282
00283 if (!cnt)
00284 continue;
00285 char* eq = strchr(opt, '=');
00286 if (eq)
00287 {
00288
00289 char* optname = opt;
00290 while (isspace(*optname) && optname < eq)
00291 ++optname;
00292 if (optname == eq)
00293 {
00294 err << "invalid binary archive option \"\"";
00295 return false;
00296 }
00297 char* optend = eq - 1;
00298 while (isspace(*optend))
00299 --optend;
00300 ++optend;
00301 *optend = '\0';
00302
00303
00304 char* optval = eq + 1;
00305 while (*optval && isspace(*optval))
00306 ++optval;
00307 if (!*optval)
00308 {
00309 err << "invalid binary archive option value \"\"";
00310 return false;
00311 }
00312 char* valend = opt + strlen(opt) - 1;
00313 while (isspace(*valend))
00314 --valend;
00315 ++valend;
00316 *valend = '\0';
00317 if (strlen(optval) == 0)
00318 {
00319 err << "invalid binary archive option value \"\"";
00320 return false;
00321 }
00322
00323
00324 if (!strcasecmp(optname, "z"))
00325 {
00326
00327 if (!CStringStream::getCompressionModeByName(optval, m))
00328 {
00329 err << "invalid compression type \"" << optval << '"';
00330 return false;
00331 }
00332 }
00333 else if (!strcasecmp(optname, "cl"))
00334 {
00335
00336 if (!parse_int(err, optval, compressionLevel))
00337 return false;
00338 if (*compressionLevel < -1 || *compressionLevel > 9)
00339 {
00340 err << "compression level is out of range";
00341 return false;
00342 }
00343 }
00344 else if (!strcasecmp(optname, "cb"))
00345 {
00346
00347 if (!parse_unsigned(err, optval, bufSize))
00348 return false;
00349 }
00350 else if (!strcasecmp(optname, "cm"))
00351 {
00352
00353 if (!parse_unsigned(err, optval, minSizeToCompress))
00354 return false;
00355 }
00356 else if (!strcasecmp(optname, "cat"))
00357 {
00358
00359 if (optval[0] == 'i' || optval[0] == 'I')
00360 *multiplexCatalog = true;
00361 else if (optval[0] == 's' || optval[0] == 'S')
00362 *multiplexCatalog = false;
00363 else
00364 {
00365 err << "invalid catalog mode \"" << optval << '"';
00366 return false;
00367 }
00368 }
00369 else
00370 {
00371
00372 err << "unrecognized binary archive option \""
00373 << optname << '"';
00374 return false;
00375 }
00376 }
00377 else
00378 {
00379 err << "invalid binary archive option \"" << opt << '"';
00380 return false;
00381 }
00382 }
00383 return true;
00384 }
00385
00386
00387 std::ios_base::openmode BinaryArchiveBase::parseMode(const char* mode)
00388 {
00389 std::ios_base::openmode m = std::ios_base::binary;
00390 if (mode)
00391 {
00392 const unsigned len = strlen(mode);
00393 for (unsigned i=0; i<len; ++i)
00394 {
00395
00396
00397 if (mode[i] == 'r')
00398 m |= std::ios_base::in;
00399 else if (mode[i] == 'w')
00400 m |= (std::ios_base::out | std::ios_base::trunc);
00401 else if (mode[i] == 'a')
00402 m |= (std::ios_base::out | std::ios_base::app);
00403 else if (mode[i] == '+')
00404 m |= (std::ios_base::in | std::ios_base::out);
00405 else if (mode[i] == ':')
00406 break;
00407 }
00408 }
00409
00410
00411 if (!(m & (std::ios_base::in | std::ios_base::out)))
00412 m |= std::ios_base::in;
00413 return m;
00414 }
00415
00416
00417 void BinaryArchiveBase::search(AbsReference& reference)
00418 {
00419 if (catalog_)
00420 {
00421 std::vector<unsigned long long> idlist;
00422 catalog_->search(reference.namePattern(),
00423 reference.categoryPattern(),
00424 &idlist);
00425 const unsigned long nfound = idlist.size();
00426 for (unsigned long i=0; i<nfound; ++i)
00427 {
00428 CPP11_shared_ptr<const CatalogEntry> pentry =
00429 catalog_->retrieveEntry(idlist[i]);
00430 if (reference.isIOCompatible(*pentry))
00431 addItemToReference(reference, idlist[i]);
00432 }
00433 }
00434 }
00435
00436
00437 bool BinaryArchiveBase::isEmptyFile(std::fstream& s)
00438 {
00439 s.seekg(0, std::ios_base::end);
00440 return s.tellg() == std::streampos(0);
00441 }
00442
00443
00444 std::istream& BinaryArchiveBase::inputStream(const unsigned long long id)
00445 {
00446 unsigned long long length = 0;
00447 unsigned compressionCode = 0;
00448 std::istream& is = plainInputStream(id, &compressionCode, &length);
00449 if (cStream_->compressionMode() == CStringStream::NOT_COMPRESSED)
00450 return is;
00451 else
00452 {
00453 cStream_->readCompressed(is, compressionCode, length);
00454 return *cStream_;
00455 }
00456 }
00457
00458
00459 std::ostream& BinaryArchiveBase::outputStream()
00460 {
00461 return plainOutputStream();
00462 }
00463
00464
00465 std::ostream& BinaryArchiveBase::compressedStream(std::ostream& os)
00466 {
00467 if (cStream_->compressionMode() == CStringStream::NOT_COMPRESSED)
00468 return os;
00469 else
00470 {
00471 cStream_->reset();
00472 cStream_->setSink(os);
00473 return *cStream_;
00474 }
00475 }
00476
00477
00478 unsigned BinaryArchiveBase::flushCompressedRecord(std::ostream&)
00479 {
00480 CStringStream::CompressionMode m = cStream_->compressionMode();
00481 if (m != CStringStream::NOT_COMPRESSED)
00482 {
00483 cStream_->flush();
00484 m = cStream_->writeCompressed();
00485 }
00486 return static_cast<unsigned>(m);
00487 }
00488 }