1 | #include <nanogui/serializer/core.h> |
2 | #include <iostream> |
3 | |
4 | NAMESPACE_BEGIN(nanogui) |
5 | |
6 | static const char * = "SER_V1" ; |
7 | static const int = 6; |
8 | static const int = |
9 | serialized_header_id_length + sizeof(uint64_t) + sizeof(uint32_t); |
10 | |
11 | Serializer::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 | |
24 | Serializer::~Serializer() { |
25 | if (mWrite) |
26 | writeTOC(); |
27 | } |
28 | |
29 | bool 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 | |
38 | size_t Serializer::size() { |
39 | mFile.seekg(0, std::ios_base::end); |
40 | return (uint64_t) mFile.tellg(); |
41 | } |
42 | |
43 | void Serializer::push(const std::string &name) { |
44 | mPrefixStack.push_back(mPrefixStack.back() + name + "." ); |
45 | } |
46 | |
47 | void Serializer::pop() { |
48 | mPrefixStack.pop_back(); |
49 | } |
50 | |
51 | std::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 | |
61 | bool 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 | |
94 | void 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 | |
108 | void 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 | |
130 | void Serializer::readTOC() { |
131 | uint64_t trailer_offset = 0; |
132 | uint32_t nItems = 0; |
133 | char [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 | |
157 | void 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 | |
165 | void 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 | |
173 | void 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 | |
186 | NAMESPACE_END(nanogui) |
187 | |