85 using length_t = double;
86 using depth_t = std::uint32_t;
87 using dim_t = std::uint32_t;
88 using version_t = std::uint32_t;
90 static constexpr std::string_view
const MAP_HEADER =
"# UFO Map";
91 static constexpr version_t
const CURRENT_MAJOR = 1;
92 static constexpr version_t
const CURRENT_MINOR = 0;
93 static constexpr version_t
const CURRENT_PATCH = 0;
98 version_t minor = std::numeric_limits<version_t>::max();
99 version_t patch = std::numeric_limits<version_t>::max();
102 std::vector<length_t> leaf_node_length;
103 depth_t num_depth_levels{};
104 std::uint64_t num_blocks{};
105 std::uint64_t num_nodes{};
106 std::vector<MapTypeInfo> map_info;
111 template <std::
size_t Dim>
114 : major(CURRENT_MAJOR)
115 , minor(CURRENT_MINOR)
116 , patch(CURRENT_PATCH)
117 , num_depth_levels(num_depth_levels)
119 this->leaf_node_length.reserve(Dim);
120 for (std::size_t i{}; Dim > i; ++i) {
121 this->leaf_node_length.push_back(leaf_node_length[i]);
125 map_info.emplace_back(MapType::NONE);
128 for (std::size_t i{};
129 std::numeric_limits<std::underlying_type_t<MapType>>::digits > i; ++i) {
130 MapType
const type =
static_cast<MapType
>(std::underlying_type_t<MapType>(1) << i);
131 if (type != (map_types & type)) {
134 map_info.emplace_back(type);
144 MapHeader(std::filesystem::path
const& filename) { read(filename); }
146 MapHeader(std::istream& in) { read(in); }
150 [[nodiscard]]
static std::ifstream openRead(std::filesystem::path
const& filename)
153 file.exceptions(std::ifstream::badbit);
154 file.imbue(std::locale());
155 file.open(filename, std::ios::in | std::ios::binary);
159 [[nodiscard]]
static std::ofstream openWrite(std::filesystem::path
const& filename)
162 file.exceptions(std::ifstream::badbit);
163 file.imbue(std::locale());
164 file.open(filename, std::ios::out | std::ios::binary);
168 [[nodiscard]]
static bool isMap(std::filesystem::path
const& filename)
170 auto file = openRead(filename);
174 [[nodiscard]]
static bool isMap(std::istream& in)
177 return std::getline(in, line) && MAP_HEADER == line;
180 [[nodiscard]]
static bool isMap(
ReadBuffer& in)
183 return in.readLine(line) && MAP_HEADER != line;
186 void read(std::filesystem::path
const& filename)
188 auto file = openRead(filename);
192 std::istream& read(std::istream& in)
196 if (std::getline(in, line) && MAP_HEADER != line) {
197 throw std::runtime_error(
"Trying to read non-UFO Map");
202 minor = std::numeric_limits<version_t>::max();
203 patch = std::numeric_limits<version_t>::max();
204 leaf_node_length.clear();
205 num_depth_levels = {};
210 bool reading_map_info =
false;
211 while (std::getline(in, line)) {
212 if (read(line, reading_map_info)) {
217 if (version_t(0) == major || std::numeric_limits<version_t>::max() == minor ||
218 std::numeric_limits<version_t>::max() == patch) {
219 throw std::logic_error(
"Wrong or missing version '" + std::to_string(major) +
"." +
220 std::to_string(minor) +
"." + std::to_string(patch));
232 if (in.readLine(line) && MAP_HEADER != line) {
233 throw std::runtime_error(
"Trying to read non-UFO Map");
237 minor = std::numeric_limits<version_t>::max();
238 patch = std::numeric_limits<version_t>::max();
239 leaf_node_length.clear();
240 num_depth_levels = {};
245 bool reading_map_info =
false;
246 while (in.readLine(line)) {
247 if (read(line, reading_map_info)) {
252 if (version_t(0) == major || std::numeric_limits<version_t>::max() == minor ||
253 std::numeric_limits<version_t>::max() == patch) {
254 throw std::logic_error(
"Wrong or missing version '" + std::to_string(major) +
"." +
255 std::to_string(minor) +
"." + std::to_string(patch));
263 void write(std::filesystem::path
const& filename)
const
265 auto file = openWrite(filename);
269 std::ostream& write(std::ostream& out)
const
271 out << MAP_HEADER <<
'\n';
272 out <<
"VERSION: " << major <<
'.' << minor <<
'.' << patch <<
'\n';
273 out <<
"LEAF_NODE_LENGTH: ";
275 if (2 == leaf_node_length.size()) {
276 out << Vec<2, length_t>(leaf_node_length[0], leaf_node_length[1]);
277 }
else if (3 == leaf_node_length.size()) {
278 out << Vec<3, length_t>(leaf_node_length[0], leaf_node_length[1],
279 leaf_node_length[2]);
280 }
else if (4 == leaf_node_length.size()) {
281 out << Vec<4, length_t>(leaf_node_length[0], leaf_node_length[1],
282 leaf_node_length[2], leaf_node_length[4]);
285 out <<
"NUMBER_OF_DEPTH_LEVELS: " << num_depth_levels <<
'\n';
286 out <<
"NUM_BLOCKS: " << num_blocks <<
'\n';
287 out <<
"NUM_NODES: " << num_nodes <<
'\n';
289 out <<
"MAP_INFO:\n";
291 std::size_t longest_name = 4;
292 std::size_t longest_type = 4;
293 std::size_t longest_size = 12;
294 for (
auto const& info : map_info) {
295 longest_name = std::max(longest_name, to_string(info.type).size());
296 std::stringstream ss;
297 ss << std::hex << std::showbase << to_underlying(info.type);
298 longest_type = std::max(longest_type,
static_cast<std::size_t
>(ss.tellp()));
300 ss << std::hex << std::showbase << info.size;
301 longest_size = std::max(longest_size,
static_cast<std::size_t
>(ss.tellp()));
305 out << std::setw(longest_name) <<
"Name";
306 out <<
": " << std::setw(longest_type) <<
"Type";
307 out <<
", " << std::setw(longest_size) <<
"Size (bytes)";
309 for (
auto const& info : map_info) {
311 if (MapType::NONE == info.type) {
312 out << std::setw(longest_name) <<
"tree";
314 out << std::setw(longest_name) << to_string(info.type);
316 out << std::hex << std::showbase;
317 out <<
": " << std::setw(longest_type) << to_underlying(info.type);
318 out <<
", " << std::setw(longest_size) << info.size;
319 out << std::noshowbase << std::dec <<
'\n';
329 std::ostringstream ss;
331 std::string s = ss.str();
332 out.write(s.data(), s.length());
337 [[nodiscard]]
bool read(std::string& line,
bool& reading_map_info)
339 std::cout << line <<
'\n';
341 if (line.empty() ||
'#' == line[0]) {
345 auto it = std::find(line.begin(), line.end(),
':');
346 std::string param(line.begin(), it);
349 if (line.end() == it) {
350 throw std::logic_error(
"No value for parameter '" + param +
"'");
353 std::string value(it + 1, line.end());
356 if (reading_map_info &&
'-' == param[0]) {
358 std::uint64_t map_type = to_underlying(info.type);
360 std::sscanf(value.c_str(),
"%" SCNx64
", %" SCNx64, &map_type, &info.size)) {
361 throw std::logic_error(
"Wrong map type info");
363 map_info.push_back(info);
367 reading_map_info =
false;
369 if (
"VERSION" == param) {
370 if (3 != std::sscanf(value.c_str(),
"%" SCNu32
".%" SCNu32
".%" SCNu32, &major,
372 throw std::logic_error(
"Wrong version format");
374 }
else if (
"LEAF_NODE_LENGTH" == param) {
375 leaf_node_length.resize(4);
377 std::sscanf(value.c_str(),
"x: %lf y: %lf z: %lf w: %lf", &leaf_node_length[0],
378 &leaf_node_length[1], &leaf_node_length[2], &leaf_node_length[3]);
380 throw std::logic_error(
"Wrong leaf node length format");
382 leaf_node_length.resize(dim);
383 }
else if (
"NUMBER_OF_DEPTH_LEVELS" == param) {
384 num_depth_levels = std::stoul(value);
385 }
else if (
"NUM_BLOCKS" == param) {
386 num_blocks = std::stoull(value);
387 }
else if (
"NUM_NODES" == param) {
388 num_nodes = std::stoull(value);
389 }
else if (
"MAP_INFO" == param) {
390 reading_map_info =
true;
391 }
else if (
"DATA" == param) {