|
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