|
1 | 1 | #include "rmanifest.hpp" |
2 | 2 |
|
| 3 | +#include <json_struct/json_struct.h> |
3 | 4 | #include <zstd.h> |
4 | 5 |
|
| 6 | +#include <charconv> |
5 | 7 | #include <cstring> |
6 | 8 | #include <limits> |
7 | | -#include <nlohmann/json.hpp> |
8 | 9 | #include <regex> |
9 | 10 | #include <stdexcept> |
10 | 11 | #include <type_traits> |
|
15 | 16 | #include "iofile.hpp" |
16 | 17 |
|
17 | 18 | using namespace rlib; |
18 | | -using json = nlohmann::json; |
| 19 | + |
| 20 | +namespace JS { |
| 21 | + template <typename T> |
| 22 | + struct TypeHandlerHex { |
| 23 | + static inline Error to(T& to_type, ParseContext& context) { |
| 24 | + auto const beg = context.token.value.data; |
| 25 | + auto const end = beg + context.token.value.size; |
| 26 | + auto value = std::uint64_t{}; |
| 27 | + auto const ec_ptr = std::from_chars(beg, end, value, 16); |
| 28 | + if (ec_ptr.ec != std::errc{}) [[unlikely]] { |
| 29 | + return Error::FailedToParseInt; |
| 30 | + } |
| 31 | + to_type = static_cast<T>(value); |
| 32 | + return Error::NoError; |
| 33 | + } |
| 34 | + |
| 35 | + static void from(T const& from_type, Token& token, Serializer& serializer) { |
| 36 | + std::string buf = fmt::format("{}", from_type); |
| 37 | + token.value_type = Type::String; |
| 38 | + token.value.data = buf.data(); |
| 39 | + token.value.size = buf.size(); |
| 40 | + serializer.write(token); |
| 41 | + } |
| 42 | + }; |
| 43 | + |
| 44 | + template <typename T, auto MINIMUM, auto MAXIMUM, typename U = std::underlying_type_t<T>> |
| 45 | + struct TypeHandlerEnum { |
| 46 | + static inline Error to(T& to_type, ParseContext& context) { |
| 47 | + auto tmp = U{}; |
| 48 | + auto error = TypeHandler<U>::to(tmp, context); |
| 49 | + if (error == Error::NoError) { |
| 50 | + if (!(tmp >= MINIMUM && tmp <= MAXIMUM)) [[unlikely]] { |
| 51 | + error = Error::IllegalDataValue; |
| 52 | + } else { |
| 53 | + to_type = static_cast<T>(tmp); |
| 54 | + } |
| 55 | + } |
| 56 | + return error; |
| 57 | + } |
| 58 | + |
| 59 | + static void from(T const& from_type, Token& token, Serializer& serializer) { |
| 60 | + TypeHandler<U>::from(static_cast<U>(from_type), token, serializer); |
| 61 | + } |
| 62 | + }; |
| 63 | + |
| 64 | + template <> |
| 65 | + struct TypeHandler<HashType> : TypeHandlerEnum<HashType, 0, 3> {}; |
| 66 | + |
| 67 | + template <> |
| 68 | + struct TypeHandler<FileID> : TypeHandlerHex<FileID> {}; |
| 69 | + |
| 70 | + template <> |
| 71 | + struct TypeHandler<ChunkID> : TypeHandlerHex<ChunkID> {}; |
| 72 | +} |
| 73 | + |
| 74 | +JS_OBJ_EXT(rlib::RChunk::Dst, chunkId, hash_type, uncompressed_size); |
| 75 | +JS_OBJ_EXT(rlib::RMAN::File, chunks, fileId, langs, link, path, permissions, size); |
19 | 76 |
|
20 | 77 | auto RMAN::Filter::operator()(File const& file) const noexcept -> bool { |
21 | 78 | if (langs && !std::regex_search(file.langs, *langs)) { |
@@ -447,46 +504,24 @@ auto RMAN::lookup() const -> std::unordered_map<std::string, File const*> { |
447 | 504 | } |
448 | 505 |
|
449 | 506 | auto RMAN::File::dump() const -> std::string { |
450 | | - auto const& file = *this; |
451 | | - auto jfile = json{ |
452 | | - {"permissions", file.permissions}, |
453 | | - {"fileId", fmt::format("{}", file.fileId)}, |
454 | | - {"path", file.path}, |
455 | | - {"link", file.link}, |
456 | | - {"langs", file.langs}, |
457 | | - {"size", file.size}, |
458 | | - {"chunks", json::array()}, |
459 | | - }; |
460 | | - for (auto const& chunk : file.chunks) { |
461 | | - jfile["chunks"].emplace_back() = json{ |
462 | | - {"chunkId", fmt::format("{}", chunk.chunkId)}, |
463 | | - {"uncompressed_size", chunk.uncompressed_size}, |
464 | | - {"hash_type", chunk.hash_type}, |
465 | | - }; |
466 | | - } |
467 | | - auto result = jfile.dump(); |
| 507 | + auto result = JS::serializeStruct(*this, JS::SerializerOptions(JS::SerializerOptions::Compact)); |
468 | 508 | result.push_back('\n'); |
469 | 509 | return result; |
470 | 510 | } |
471 | 511 |
|
472 | 512 | auto RMAN::File::undump(std::string_view data) -> File { |
| 513 | + auto context = JS::ParseContext(data.data(), data.size()); |
473 | 514 | auto file = File{}; |
474 | | - auto jfile = json::parse(data); |
475 | | - file.permissions = jfile.at("permissions"); |
476 | | - file.fileId = (FileID)from_hex(jfile.at("fileId")).value(); |
477 | | - file.path = jfile.at("path"); |
478 | | - file.link = jfile.at("link"); |
479 | | - file.langs = jfile.at("langs"); |
480 | | - file.size = jfile.at("size"); |
481 | | - for (std::uint64_t uncompressed_offset = 0; auto const& jchunk : jfile.at("chunks")) { |
482 | | - auto& chunk = file.chunks.emplace_back(); |
483 | | - chunk.chunkId = (ChunkID)from_hex(jchunk.at("chunkId")).value(); |
484 | | - chunk.uncompressed_size = jchunk.at("uncompressed_size"); |
485 | | - chunk.hash_type = jchunk.at("hash_type"); |
| 515 | + auto error = context.parseTo(file); |
| 516 | + if (error != JS::Error::NoError) { |
| 517 | + rlib_error(context.makeErrorString().c_str()); |
| 518 | + } |
| 519 | + auto uncompressed_offset = std::uint64_t{}; |
| 520 | + for (auto& chunk : file.chunks) { |
486 | 521 | chunk.uncompressed_offset = uncompressed_offset; |
487 | 522 | uncompressed_offset += chunk.uncompressed_size; |
488 | | - rlib_assert((unsigned)chunk.hash_type > 0 && (unsigned)chunk.hash_type <= 3); |
489 | 523 | } |
| 524 | + rlib_assert(uncompressed_offset == file.size); |
490 | 525 | return file; |
491 | 526 | } |
492 | 527 |
|
|
0 commit comments