1#include <nanogui/serializer/core.h>
2#include <iostream>
3
4NAMESPACE_BEGIN(nanogui)
5
6static const char *serialized_header_id = "SER_V1";
7static const int serialized_header_id_length = 6;
8static const int serialized_header_size =
9 serialized_header_id_length + sizeof(uint64_t) + sizeof(uint32_t);
10
11Serializer::Serializer(const std::string &filename, bool write_)
12 : mFilename(filename), mWrite(write_), mCompatibility(false) {
13 mFile.open(filename, write_ ? (std::ios::out | std::ios::trunc | std::ios::binary)
14 : (std::ios::in | std::ios::binary));
15 if (!mFile.is_open())
16 throw std::runtime_error("Could not open \"" + filename + "\"!");
17
18 if (!mWrite)
19 readTOC();
20 seek(serialized_header_size);
21 mPrefixStack.push_back("");
22}
23
24Serializer::~Serializer() {
25 if (mWrite)
26 writeTOC();
27}
28
29bool Serializer::isSerializedFile(const std::string &filename) {
30 try {
31 Serializer s(filename, false);
32 return true;
33 } catch (const std::exception &) {
34 return false;
35 }
36}
37
38size_t Serializer::size() {
39 mFile.seekg(0, std::ios_base::end);
40 return (uint64_t) mFile.tellg();
41}
42
43void Serializer::push(const std::string &name) {
44 mPrefixStack.push_back(mPrefixStack.back() + name + ".");
45}
46
47void Serializer::pop() {
48 mPrefixStack.pop_back();
49}
50
51std::vector<std::string> Serializer::keys() const {
52 const std::string &prefix = mPrefixStack.back();
53 std::vector<std::string> result;
54 for (auto const &kv : mTOC) {
55 if (kv.first.substr(0, prefix.length()) == prefix)
56 result.push_back(kv.first.substr(prefix.length()));
57 }
58 return result;
59}
60
61bool Serializer::get_base(const std::string &name,
62 const std::string &type_id) {
63 if (mWrite)
64 throw std::runtime_error("\"" + mFilename +
65 "\": not open for reading!");
66
67 std::string fullName = mPrefixStack.back() + name;
68
69 auto it = mTOC.find(fullName);
70 if (it == mTOC.end()) {
71 std::string message = "\"" + mFilename +
72 "\": unable to find field named \"" +
73 fullName + "\"!";
74 if (!mCompatibility)
75 throw std::runtime_error(message);
76 else
77 std::cerr << "Warning: " << message << std::endl;
78
79 return false;
80 }
81
82 const auto &record = it->second;
83 if (record.first != type_id)
84 throw std::runtime_error(
85 "\"" + mFilename + "\": field named \"" + fullName +
86 "\" has an incompatible type (expected \"" + type_id +
87 "\", got \"" + record.first + "\")!");
88
89 seek((size_t) record.second);
90
91 return true;
92}
93
94void Serializer::set_base(const std::string &name,
95 const std::string &type_id) {
96 if (!mWrite)
97 throw std::runtime_error("\"" + mFilename + "\": not open for writing!");
98
99 std::string fullName = mPrefixStack.back() + name;
100 auto it = mTOC.find(fullName);
101 if (it != mTOC.end())
102 throw std::runtime_error("\"" + mFilename + "\": field named \"" +
103 fullName + "\" already exists!");
104
105 mTOC[fullName] = std::make_pair(type_id, (uint64_t) mFile.tellp());
106}
107
108void Serializer::writeTOC() {
109 uint64_t trailer_offset = (uint64_t) mFile.tellp();
110 uint32_t nItems = (uint32_t) mTOC.size();
111
112 seek(0);
113 write(serialized_header_id, serialized_header_id_length);
114 write(&trailer_offset, sizeof(uint64_t));
115 write(&nItems, sizeof(uint32_t));
116 seek((size_t) trailer_offset);
117
118 for (auto item : mTOC) {
119 uint16_t size = (uint16_t) item.first.length();
120 write(&size, sizeof(uint16_t));
121 write(item.first.c_str(), size);
122 size = (uint16_t) item.second.first.length();
123 write(&size, sizeof(uint16_t));
124 write(item.second.first.c_str(), size);
125
126 write(&item.second.second, sizeof(uint64_t));
127 }
128}
129
130void Serializer::readTOC() {
131 uint64_t trailer_offset = 0;
132 uint32_t nItems = 0;
133 char header[serialized_header_id_length];
134
135 read(header, serialized_header_id_length);
136 if (memcmp(header, serialized_header_id, serialized_header_id_length) != 0)
137 throw std::runtime_error("\"" + mFilename + "\": invalid file format!");
138 read(&trailer_offset, sizeof(uint64_t));
139 read(&nItems, sizeof(uint32_t));
140 mFile.seekg(trailer_offset);
141
142 for (uint32_t i = 0; i < nItems; ++i) {
143 std::string field_name, type_id;
144 uint16_t size;
145 uint64_t offset;
146
147 read(&size, sizeof(uint16_t)); field_name.resize(size);
148 read((char *) field_name.data(), size);
149 read(&size, sizeof(uint16_t)); type_id.resize(size);
150 read((char *) type_id.data(), size);
151 read(&offset, sizeof(uint64_t));
152
153 mTOC[field_name] = std::make_pair(type_id, offset);
154 }
155}
156
157void Serializer::read(void *p, size_t size) {
158 mFile.read((char *) p, size);
159 if (!mFile.good())
160 throw std::runtime_error("\"" + mFilename +
161 "\": I/O error while attempting to read " +
162 std::to_string(size) + " bytes.");
163}
164
165void Serializer::write(const void *p, size_t size) {
166 mFile.write((char *) p, size);
167 if (!mFile.good())
168 throw std::runtime_error(
169 "\"" + mFilename + "\": I/O error while attempting to write " +
170 std::to_string(size) + " bytes.");
171}
172
173void Serializer::seek(size_t pos) {
174 if (mWrite)
175 mFile.seekp(pos);
176 else
177 mFile.seekg(pos);
178
179 if (!mFile.good())
180 throw std::runtime_error(
181 "\"" + mFilename +
182 "\": I/O error while attempting to seek to offset " +
183 std::to_string(pos) + ".");
184}
185
186NAMESPACE_END(nanogui)
187