187 lines
5.6 KiB
C++
187 lines
5.6 KiB
C++
#include <nanogui/serializer/core.h>
|
|
#include <iostream>
|
|
|
|
NAMESPACE_BEGIN(nanogui)
|
|
|
|
static const char *serialized_header_id = "SER_V1";
|
|
static const int serialized_header_id_length = 6;
|
|
static const int serialized_header_size =
|
|
serialized_header_id_length + sizeof(uint64_t) + sizeof(uint32_t);
|
|
|
|
Serializer::Serializer(const std::string &filename, bool write_)
|
|
: mFilename(filename), mWrite(write_), mCompatibility(false) {
|
|
mFile.open(filename, write_ ? (std::ios::out | std::ios::trunc | std::ios::binary)
|
|
: (std::ios::in | std::ios::binary));
|
|
if (!mFile.is_open())
|
|
throw std::runtime_error("Could not open \"" + filename + "\"!");
|
|
|
|
if (!mWrite)
|
|
readTOC();
|
|
seek(serialized_header_size);
|
|
mPrefixStack.push_back("");
|
|
}
|
|
|
|
Serializer::~Serializer() {
|
|
if (mWrite)
|
|
writeTOC();
|
|
}
|
|
|
|
bool Serializer::isSerializedFile(const std::string &filename) {
|
|
try {
|
|
Serializer s(filename, false);
|
|
return true;
|
|
} catch (const std::exception &e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
size_t Serializer::size() {
|
|
mFile.seekg(0, std::ios_base::end);
|
|
return (uint64_t) mFile.tellg();
|
|
}
|
|
|
|
void Serializer::push(const std::string &name) {
|
|
mPrefixStack.push_back(mPrefixStack.back() + name + ".");
|
|
}
|
|
|
|
void Serializer::pop() {
|
|
mPrefixStack.pop_back();
|
|
}
|
|
|
|
std::vector<std::string> Serializer::keys() const {
|
|
const std::string &prefix = mPrefixStack.back();
|
|
std::vector<std::string> result;
|
|
for (auto const &kv : mTOC) {
|
|
if (kv.first.substr(0, prefix.length()) == prefix)
|
|
result.push_back(kv.first.substr(prefix.length()));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool Serializer::get_base(const std::string &name,
|
|
const std::string &type_id) {
|
|
if (mWrite)
|
|
throw std::runtime_error("\"" + mFilename +
|
|
"\": not open for reading!");
|
|
|
|
std::string fullName = mPrefixStack.back() + name;
|
|
|
|
auto it = mTOC.find(fullName);
|
|
if (it == mTOC.end()) {
|
|
std::string message = "\"" + mFilename +
|
|
"\": unable to find field named \"" +
|
|
fullName + "\"!";
|
|
if (!mCompatibility)
|
|
throw std::runtime_error(message);
|
|
else
|
|
std::cerr << "Warning: " << message << std::endl;
|
|
|
|
return false;
|
|
}
|
|
|
|
const auto &record = it->second;
|
|
if (record.first != type_id)
|
|
throw std::runtime_error(
|
|
"\"" + mFilename + "\": field named \"" + fullName +
|
|
"\" has an incompatible type (expected \"" + type_id +
|
|
"\", got \"" + record.first + "\")!");
|
|
|
|
seek((size_t) record.second);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Serializer::set_base(const std::string &name,
|
|
const std::string &type_id) {
|
|
if (!mWrite)
|
|
throw std::runtime_error("\"" + mFilename + "\": not open for writing!");
|
|
|
|
std::string fullName = mPrefixStack.back() + name;
|
|
auto it = mTOC.find(fullName);
|
|
if (it != mTOC.end())
|
|
throw std::runtime_error("\"" + mFilename + "\": field named \"" +
|
|
fullName + "\" already exists!");
|
|
|
|
mTOC[fullName] = std::make_pair(type_id, (uint64_t) mFile.tellp());
|
|
}
|
|
|
|
void Serializer::writeTOC() {
|
|
uint64_t trailer_offset = (uint64_t) mFile.tellp();
|
|
uint32_t nItems = mTOC.size();
|
|
|
|
seek(0);
|
|
write(serialized_header_id, serialized_header_id_length);
|
|
write(&trailer_offset, sizeof(uint64_t));
|
|
write(&nItems, sizeof(uint32_t));
|
|
seek((size_t) trailer_offset);
|
|
|
|
for (auto item : mTOC) {
|
|
uint16_t size = (uint16_t) item.first.length();
|
|
write(&size, sizeof(uint16_t));
|
|
write(item.first.c_str(), size);
|
|
size = (uint16_t) item.second.first.length();
|
|
write(&size, sizeof(uint16_t));
|
|
write(item.second.first.c_str(), size);
|
|
|
|
write(&item.second.second, sizeof(uint64_t));
|
|
}
|
|
}
|
|
|
|
void Serializer::readTOC() {
|
|
uint64_t trailer_offset = 0;
|
|
uint32_t nItems = 0;
|
|
char header[serialized_header_id_length];
|
|
|
|
read(header, serialized_header_id_length);
|
|
if (memcmp(header, serialized_header_id, serialized_header_id_length) != 0)
|
|
throw std::runtime_error("\"" + mFilename + "\": invalid file format!");
|
|
read(&trailer_offset, sizeof(uint64_t));
|
|
read(&nItems, sizeof(uint32_t));
|
|
mFile.seekg(trailer_offset);
|
|
|
|
for (uint32_t i = 0; i < nItems; ++i) {
|
|
std::string field_name, type_id;
|
|
uint16_t size;
|
|
uint64_t offset;
|
|
|
|
read(&size, sizeof(uint16_t)); field_name.resize(size);
|
|
read((char *) field_name.data(), size);
|
|
read(&size, sizeof(uint16_t)); type_id.resize(size);
|
|
read((char *) type_id.data(), size);
|
|
read(&offset, sizeof(uint64_t));
|
|
|
|
mTOC[field_name] = std::make_pair(type_id, offset);
|
|
}
|
|
}
|
|
|
|
void Serializer::read(void *p, size_t size) {
|
|
mFile.read((char *) p, size);
|
|
if (!mFile.good())
|
|
throw std::runtime_error("\"" + mFilename +
|
|
"\": I/O error while attempting to read " +
|
|
std::to_string(size) + " bytes.");
|
|
}
|
|
|
|
void Serializer::write(const void *p, size_t size) {
|
|
mFile.write((char *) p, size);
|
|
if (!mFile.good())
|
|
throw std::runtime_error(
|
|
"\"" + mFilename + "\": I/O error while attempting to write " +
|
|
std::to_string(size) + " bytes.");
|
|
}
|
|
|
|
void Serializer::seek(size_t pos) {
|
|
if (mWrite)
|
|
mFile.seekp(pos);
|
|
else
|
|
mFile.seekg(pos);
|
|
|
|
if (!mFile.good())
|
|
throw std::runtime_error(
|
|
"\"" + mFilename +
|
|
"\": I/O error while attempting to seek to offset " +
|
|
std::to_string(pos) + ".");
|
|
}
|
|
|
|
NAMESPACE_END(nanogui)
|