25 #include <boost/algorithm/string.hpp> 28 #include <type_traits> 30 #ifdef SERIALBOX_ASYNC_API 37 int SerializerImpl::enabled_ = 0;
40 const std::string& prefix,
const std::string& archiveName)
41 : mode_(mode), directory_(directory), prefix_(prefix) {
44 const char* envvar = std::getenv(
"SERIALBOX_SERIALIZATION_DISABLED");
45 enabled_ = (envvar && std::atoi(envvar) > 0) ? -1 : 1;
48 metaDataFile_ = directory_ / (
"MetaData-" + prefix +
".json");
50 savepointVector_ = std::make_shared<SavepointVector>();
51 fieldMap_ = std::make_shared<FieldMap>();
52 globalMetainfo_ = std::make_shared<MetainfoMapImpl>();
54 LOG(info) <<
"Creating Serializer (mode = " << mode_ <<
") from directory " << directory_;
59 if(mode_ == OpenModeKind::Read && !filesystem::exists(directory_))
61 throw Exception(
"cannot create Serializer: directory %s does not exist", directory_);
62 }
catch(filesystem::filesystem_error& e) {
63 throw Exception(
"filesystem error: %s", e.what());
74 if(mode_ == OpenModeKind::Write)
79 savepointVector_->clear();
81 globalMetainfo_->clear();
86 std::vector<std::string> fields;
87 fields.reserve(fieldMap_->size());
88 for(
auto it = fieldMap_->begin(), end = fieldMap_->end(); it != end; ++it)
89 fields.push_back(it->first);
93 static inline bool dimsEqual(
const std::vector<int>& dims1,
const std::vector<int>& dims2) {
94 if(dims1.size() != dims2.size())
99 for(std::size_t i = 0; i < dims1.size(); ++i)
100 if(dims1[i] != dims2[i] && !(dims1[i] <= 1 && dims2[i] <= 1))
105 std::shared_ptr<FieldMetainfoImpl>
109 auto fieldIt = fieldMap_->findField(name);
110 if(fieldIt == fieldMap_->end())
111 throw Exception(
"field '%s' is not registerd within the Serializer", name);
116 if(fieldInfo.
type() != storageView.
type())
117 throw Exception(
"field '%s' has type '%s' but was registrered as type '%s'", name,
121 if(!dimsEqual(fieldInfo.
dims(), storageView.
dims())) {
122 throw Exception(
"dimensions of field '%s' do not match regsitered ones:" 123 "\nRegistred as: [ %s ]" 124 "\nGiven as: [ %s ]",
128 return fieldIt->second;
140 LOG(info) <<
"Serializing field \"" << name <<
"\" at savepoint \"" << savepoint <<
"\" ... ";
142 if(mode_ == OpenModeKind::Read)
143 throw Exception(
"serializer not open in write mode, but write operation requested");
153 int savepointIdx = savepointVector_->find(savepoint);
155 if(savepointIdx == -1) {
156 LOG(info) <<
"Registering new savepoint \"" << savepoint <<
"\"";
157 savepointIdx = savepointVector_->insert(savepoint);
163 if(savepointVector_->hasField(savepointIdx, name))
164 throw Exception(
"field '%s' already saved at savepoint '%s'", name,
165 (*savepointVector_)[savepointIdx].toString());
170 FieldID fieldID = archive_->write(storageView, name, info);
175 savepointVector_->addField(savepointIdx, fieldID);
182 LOG(info) <<
"Successfully serialized field \"" << name <<
"\"";
194 LOG(info) <<
"Deserializing field \"" << name <<
"\" at savepoint \"" << savepoint <<
"\" ... ";
204 int savepointIdx = savepointVector_->find(savepoint);
206 if(savepointIdx == -1)
207 throw Exception(
"savepoint '%s' does not exist", savepoint.toString());
210 while (savepointIdx >= 0)
212 if (savepointVector_->hasField(savepointIdx, name) || !alsoPrevious)
214 fieldID = savepointVector_->getFieldID(savepointIdx, name);
225 if(savepointIdx == -1)
226 throw Exception(
"field '%s' not found at or before savepoint '%s'", name, savepoint.toString());
231 archive_->read(storageView, fieldID, info);
233 LOG(info) <<
"Successfully deserialized field \"" << name <<
"\"";
238 if(!archive_->isSlicedReadingSupported())
239 throw Exception(
"archive '%s' does not support sliced reading", archive_->name());
242 this->
read(name, savepoint, storageView);
249 #ifdef SERIALBOX_ASYNC_API 251 static std::vector<std::future<void>> tasks;
257 this->
read(name, savepoint, storageView);
262 #ifdef SERIALBOX_ASYNC_API 263 if(!archive_->isReadingThreadSafe())
264 this->
read(name, savepoint, storageView);
269 name, savepoint, storageView));
271 this->
read(name, savepoint, storageView);
276 #ifdef SERIALBOX_ASYNC_API 278 for(
auto& task : global::tasks)
281 global::tasks.clear();
284 global::tasks.clear();
293 LOG(info) <<
"Constructing Serializer from MetaData ... ";
296 if(!filesystem::exists(metaDataFile_)) {
297 if(mode_ != OpenModeKind::Read)
300 throw Exception(
"cannot create Serializer: MetaData-%s.json not found in %s", prefix_,
306 std::ifstream fs(metaDataFile_.string(), std::ios::in);
310 throw Exception(
"JSON parser error: %s", e.what());
315 if(!jsonNode.count(
"serialbox_version"))
316 throw Exception(
"node 'serialbox_version' not found");
318 int serialboxVersion = jsonNode[
"serialbox_version"];
322 "serialbox version of MetaData (%s) does not match the version of the library (%s)",
326 if(!jsonNode.count(
"prefix"))
327 throw Exception(
"node 'prefix' not found");
329 if(jsonNode[
"prefix"] != prefix_)
330 throw Exception(
"inconsistent prefixes: expected '%s' got '%s'", jsonNode[
"prefix"], prefix_);
333 if(jsonNode.count(
"global_meta_info"))
334 globalMetainfo_->fromJSON(jsonNode[
"global_meta_info"]);
337 if(jsonNode.count(
"savepoint_vector"))
338 savepointVector_->fromJSON(jsonNode[
"savepoint_vector"]);
341 if(jsonNode.count(
"field_map"))
342 fieldMap_->fromJSON(jsonNode[
"field_map"]);
345 throw Exception(
"error while parsing %s: %s", metaDataFile_, e.what());
350 std::stringstream ss;
351 ss <<
"mode = " << mode_ <<
"\n";
352 ss <<
"directory = " << directory_ <<
"\n";
353 ss <<
"prefix = \"" << prefix_ <<
"\"\n";
354 ss <<
"archive = \"" << archive_->name() <<
"\"\n";
355 ss <<
"metainfo = " << *globalMetainfo_ <<
"\n";
356 ss <<
"savepoints = [";
357 for(
const auto& sp : savepointVector_->savepoints())
359 ss << (savepointVector_->savepoints().empty() ?
"" :
"\n") <<
"]\n";
360 ss <<
"fieldmetainfo = [";
361 for(
auto it = fieldMap_->begin(), end = fieldMap_->end(); it != end; ++it)
362 ss <<
"\n " << it->first <<
": " << *it->second;
363 ss << (fieldMap_->empty() ?
"" :
"\n") <<
"]";
372 LOG(info) <<
"Converting Serializer MetaData to JSON";
377 jsonNode[
"serialbox_version"] =
378 100 * SERIALBOX_VERSION_MAJOR + 10 * SERIALBOX_VERSION_MINOR + SERIALBOX_VERSION_PATCH;
381 jsonNode[
"prefix"] = prefix_;
384 jsonNode[
"global_meta_info"] = globalMetainfo_->toJSON();
387 jsonNode[
"savepoint_vector"] = savepointVector_->toJSON();
390 jsonNode[
"field_map"] = fieldMap_->toJSON();
396 LOG(info) <<
"Update MetaData of Serializer";
398 json::json jsonNode =
toJSON();
402 std::ofstream fs(metaDataFile_.string(), std::ios::out | std::ios::trunc);
404 throw Exception(
"cannot open file: %s", metaDataFile_);
405 fs << jsonNode.dump(1) << std::endl;
409 archive_->updateMetaData();
421 filesystem::path oldMetaDataFile = directory_ / (prefix_ +
".json");
429 if(!filesystem::exists(oldMetaDataFile))
432 LOG(info) <<
"Detected old serialbox meta-data " << oldMetaDataFile;
435 if(filesystem::exists(metaDataFile_) && (filesystem::last_write_time(oldMetaDataFile) <
436 filesystem::last_write_time(metaDataFile_))) {
439 }
catch(filesystem::filesystem_error& e) {
440 throw Exception(
"filesystem error: %s", e.what());
443 LOG(info) <<
"Upgrading meta-data to serialbox version (" << SERIALBOX_VERSION_STRING <<
") ...";
445 if(mode_ != OpenModeKind::Read)
446 throw Exception(
"old serialbox archives cannot be opened in 'Write' or 'Append' mode");
449 std::ifstream ifs(oldMetaDataFile.string());
451 throw Exception(
"upgrade failed: cannot open %s", oldMetaDataFile);
461 TypeID globalMetainfoFloatType = TypeID::Float64;
462 if(oldJson.count(
"FieldsTable") && oldJson[
"FieldsTable"].size() > 0) {
463 if(oldJson[
"FieldsTable"][0][
"__elementtype"] ==
"float")
464 globalMetainfoFloatType = TypeID::Float32;
467 LOG(info) <<
"Deduced float type of global meta-info as: " << globalMetainfoFloatType;
469 if(oldJson.count(
"GlobalMetainfo")) {
471 LOG(info) <<
"Upgrading global meta-info ...";
473 for(
auto it = oldJson[
"GlobalMetainfo"].begin(), end = oldJson[
"GlobalMetainfo"].end();
476 LOG(info) <<
"Inserting global meta-info: key = " << it.key() <<
", value = " << it.value();
478 std::string key = it.key();
479 if(!boost::algorithm::starts_with(key,
"__")) {
480 if(it.value().is_string()) {
481 std::string value = it.value();
483 }
else if(it.value().is_boolean()) {
485 }
else if(it.value().is_number_integer()) {
487 }
else if(it.value().is_number_float()) {
488 if(globalMetainfoFloatType == TypeID::Float32)
493 throw Exception(
"failed to upgrade: cannot deduce type of globalMetainfo '%s'", it.key());
497 LOG(info) <<
"Successfully upgraded global meta-info";
504 if(oldJson.count(
"FieldsTable")) {
506 LOG(info) <<
"Upgrading fields table ...";
508 const auto& fieldsTable = oldJson[
"FieldsTable"];
509 for(std::size_t i = 0; i < fieldsTable.size(); ++i) {
510 auto& fieldInfo = fieldsTable[i];
511 std::string name = fieldInfo[
"__name"];
513 LOG(info) <<
"Inserting field: " << name;
516 std::string elementtype = fieldInfo[
"__elementtype"];
517 TypeID type = TypeID::Float64;
519 if(elementtype ==
"int")
520 type = TypeID::Int32;
521 else if(elementtype ==
"float")
522 type = TypeID::Float32;
523 else if(elementtype ==
"double")
524 type = TypeID::Float64;
527 std::vector<int> dims(3, 1);
528 dims[0] = int(fieldInfo[
"__isize"]);
529 dims[1] = int(fieldInfo[
"__jsize"]);
530 dims[2] = int(fieldInfo[
"__ksize"]);
532 if(fieldInfo.count(
"__lsize"))
533 dims.push_back(
int(fieldInfo[
"__lsize"]));
537 for(
auto it = fieldInfo.begin(), end = fieldInfo.end(); it != end; ++it) {
538 std::string key = it.key();
539 if(it.value().is_string()) {
540 std::string value = it.value();
541 metaInfo.
insert(key, value);
542 }
else if(it.value().is_boolean()) {
543 metaInfo.
insert(key,
bool(it.value()));
544 }
else if(it.value().is_number_integer()) {
545 metaInfo.
insert(key,
int(it.value()));
546 }
else if(it.value().is_number_float()) {
547 if(globalMetainfoFloatType == TypeID::Float32)
548 metaInfo.
insert(key,
float(it.value()));
550 metaInfo.
insert(key,
double(it.value()));
552 throw Exception(
"failed to upgrade: Cannot deduce type of meta-info '%s' of field '%s'",
556 fieldMap_->insert(name, type, dims, metaInfo);
559 LOG(info) <<
"Successfully upgraded fields table";
567 archive_ = std::make_unique<BinaryArchive>(mode_, directory_.string(), prefix_,
true);
574 if(oldJson.count(
"OffsetTable")) {
576 LOG(info) <<
"Upgrading offset table ...";
578 const auto& offsetTable = oldJson[
"OffsetTable"];
579 for(std::size_t i = 0; i < offsetTable.size(); ++i) {
580 auto& offsetTableEntry = offsetTable[i];
583 std::string name = offsetTableEntry[
"__name"];
587 for(
auto it = offsetTableEntry.begin(), end = offsetTableEntry.end(); it != end; ++it) {
588 std::string key = it.key();
589 if(!boost::algorithm::starts_with(key,
"__")) {
590 if(it.value().is_string()) {
591 std::string value = it.value();
593 }
else if(it.value().is_boolean()) {
595 }
else if(it.value().is_number_integer()) {
597 }
else if(it.value().is_number_float()) {
598 if(globalMetainfoFloatType == TypeID::Float32)
599 savepoint.
addMetainfo(it.key(), float(it.value()));
601 savepoint.
addMetainfo(it.key(), double(it.value()));
604 "failed to upgrade: Cannot deduce type of meta-info '%s' of savepoint '%s'",
609 LOG(info) <<
"Adding savepoint: " << savepoint;
612 int savepointIdx = savepointVector_->insert(savepoint);
613 assert(savepointIdx != -1);
616 for(
auto it = offsetTableEntry[
"__offsets"].begin(),
617 end = offsetTableEntry[
"__offsets"].end();
619 std::string fieldname = it.key();
626 auto fieldTableIt = fieldTable.find(fieldname);
627 if(fieldTableIt != fieldTable.end()) {
629 bool fieldAlreadySerialized =
false;
632 for(std::size_t i = 0; i < fieldOffsetTable.size(); ++i)
633 if(fileOffset.checksum == fieldOffsetTable[i].checksum) {
634 fieldAlreadySerialized =
true;
640 if(!fieldAlreadySerialized) {
641 assert(fileOffset.offset != 0);
642 fieldID.id = fieldOffsetTable.size();
643 fieldOffsetTable.push_back(fileOffset);
646 assert(fileOffset.offset == 0);
648 fieldTable.insert(BinaryArchive::FieldTable::value_type(
653 savepointVector_->addField(savepointIdx, fieldID);
655 LOG(info) <<
"Adding field '" << fieldID <<
"' to savepoint " << savepoint;
659 LOG(info) <<
"Successfully upgraded offset table";
667 LOG(warning) <<
"Failed to write upgraded meta-data to disk: " << e.what();
670 LOG(info) <<
"Successfully upgraded MetaData to serialbox version (" << SERIALBOX_VERSION_STRING
static std::string toString(const Array< T > &array)
Convert to string.
void readAsync(const std::string &name, const SavepointImpl &savepoint, StorageView &storageView)
Asynchronously deserialize field name (given as storageView) at savepoint from disk using std::async...
static std::string toString(int version)
Convert to string.
void constructArchive(const std::string &archiveName)
Construct Archive from JSON.
void updateMetaData()
Convert meta-data to JSON and serialize to MetaData-prefix.json and ArchiveMetaData-prefix.json.
static int serializationStatus() noexcept
Get the status of serialization.
void readAsyncImpl(const std::string name, const SavepointImpl savepoint, StorageView storageView)
Implementation of SerializerImpl::readAsync.
std::vector< FileOffsetType > FieldOffsetTable
Table of ids and corresponding offsets whithin in each field (i.e file)
void addGlobalMetainfo(StringType &&key, ValueType &&value)
Add a new key-value pair to the global meta-information of the Serializer.
#define LOG(severity)
Logging infrastructure.
friend std::ostream & operator<<(std::ostream &stream, const SerializerImpl &s)
Convert to stream.
Represent a mutable view to a multi-dimensional storage.
static std::unique_ptr< Hash > create(const std::string &name)
Construct an instance of the Hash name
void setSlice(Slice slice)
Set the slice of the StorageView
TypeID type() const noexcept
Get type.
void addMetainfo(StringType &&key, ValueType &&value)
Add a new key = value pair to the metaInfo of the Savepoint.
Namespace of the serialbox library.
TypeID
Type-id of types recognized by serialbox.
std::string toString() const
Convert to string.
std::shared_ptr< FieldMetainfoImpl > checkStorageView(const std::string &name, const StorageView &storageView) const
Check if storageView is consistent with the field name
bool upgradeMetaData()
Check if the current directory contains meta-information of an older version of serialbox and upgrade...
Shared implementation of the Serializer.
Uniquely identifiy a field.
static std::unique_ptr< Archive > create(const std::string &name, OpenModeKind mode, const std::string &directory, const std::string &prefix)
Construct an instance of the archive ´name´
const std::vector< int > & dims() const noexcept
Get dimensions.
std::vector< std::string > fieldnames() const
Get a vector of all registered fields.
static std::string toString(TypeID id)
Convert to string.
void constructMetaDataFromJson()
Construct meta-data from JSON.
std::unordered_map< std::string, FieldOffsetTable > FieldTable
Table of all fields owned by this archive, each field has a corresponding file.
void write(const std::string &name, const SavepointImpl &savepoint, const StorageView &storageView)
Serialize field name (given as storageView) at savepoint to disk.
Non-portable binary archive.
OpenModeKind
Policy for opening files in the Serializer and Archive.
static bool isCompatible(int version) noexcept
Check if the given version is compatible with the current library version (i.e. is older) ...
json::json toJSON() const
Convert all members of the Serializer to JSON.
void readSliced(const std::string &name, const SavepointImpl &savepoint, StorageView &storageView, Slice slice)
Deserialize sliced field name (given as storageView and slice) at savepoint from disk.
std::string archiveName() const noexcept
Name of the archive in use.
Specification of the slice indices which is used for partial loading of serialized data...
Exception class which stores a human-readable error description.
void clear() noexcept
Drop all field and savepoint meta-data.
SerializerImpl(const SerializerImpl &)=delete
Copy constructor [deleted].
Shared implementation of the Savepoint.
void read(const std::string &name, const SavepointImpl &savepoint, StorageView &storageView, bool alsoPrevious=false)
Deserialize field name (given as storageView) at savepoint from disk.
void waitForAll()
Wait for all pending asynchronous read operations and reset the internal queue.