reactphysics3d/testbed/nanogui/src/serializer.cpp

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)