diff --git a/cpp/.gitignore b/cpp/.gitignore new file mode 100644 index 0000000..41cdd7c --- /dev/null +++ b/cpp/.gitignore @@ -0,0 +1,3 @@ +build +*cmake-build-debug* +*CMakeFiles* \ No newline at end of file diff --git a/cpp/na_nbt_impl.hpp b/cpp/na_nbt_impl.hpp new file mode 100644 index 0000000..ad9bba1 --- /dev/null +++ b/cpp/na_nbt_impl.hpp @@ -0,0 +1,2437 @@ +#pragma once +#define use_fastio +#ifdef use_fastio +#include "./fast_io/include/fast_io.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +namespace na::nbt::v7 { +namespace impl { +enum class nbt_parse_error +{ + end_of_file, + invalid +}; + +struct nbt_document; + +namespace swapper { +template +inline T& byte_as_type(std::byte* ptr) noexcept; + +template +requires(nbt_endian == std::endian::big || nbt_endian == std::endian::little) inline T endian_get(std::byte* ptr) noexcept; + +template +requires(nbt_endian == std::endian::big || nbt_endian == std::endian::little) inline T endian_make_native_get(std::byte* ptr) noexcept; + +template +requires(nbt_endian == std::endian::big || nbt_endian == std::endian::little) inline T endian_get_make_native(std::byte* ptr) noexcept; + +template +requires(nbt_endian == std::endian::big || nbt_endian == std::endian::little) inline void endian_make_native(std::byte* ptr) noexcept; + +template +concept read_swapper_for_endian = requires(std::byte * &t) +{ + read_swapper::template tag_byte(t); + read_swapper::template tag_short(t); + read_swapper::template tag_int(t); + read_swapper::template tag_long(t); + read_swapper::template tag_float(t); + read_swapper::template tag_double(t); + read_swapper::template tag_byte_array(t); + read_swapper::template tag_string(t); + read_swapper::template tag_int_array(t); + read_swapper::template tag_long_array(t); +}; +template +concept read_swapper = + read_swapper_for_endian && + read_swapper_for_endian; + +template +concept write_swapper_for_endian = requires(std::byte * &t) +{ + write_swapper::template tag_byte(t); + write_swapper::template tag_short(t); + write_swapper::template tag_int(t); + write_swapper::template tag_long(t); + write_swapper::template tag_float(t); + write_swapper::template tag_double(t); + write_swapper::template tag_byte_array(t); + write_swapper::template tag_string(t); + write_swapper::template tag_int_array(t); + write_swapper::template tag_long_array(t); +}; +template +concept write_swapper = + write_swapper_for_endian && + write_swapper_for_endian; + +} // namespace swapper +namespace read_write { +template +[[nodiscard]] inline auto read(std::byte* source, std::size_t source_len) -> nbt_document; + +} // namespace read_write +union mark_t +{ + struct + { + uint32_t general_parrent_offset; // 4 + uint32_t list_current_length; // 4 + uint32_t list_total_length; // 4 + uint16_t list_type; // 2 + uint16_t general_is_compound; // 2 + } cache; + struct + { + uint64_t flat_next_mark; // offset from this + std::byte* end; + } store; +}; + +struct nbt_document +{ + mark_t* mark_m; + std::byte* source_m; + + mark_t* mark; + std::byte* source; + std::size_t mark_len; //可能小于 mark_m 已分配内存 + std::size_t source_len; //可能小于 source_m 已分配内存 +}; + +enum class nbt_type : ::std::uint8_t +{ + tag_end = 0, + tag_byte = 1, + tag_short = 2, + tag_int = 3, + tag_long = 4, + tag_float = 5, + tag_double = 6, + tag_byte_array = 7, + tag_string = 8, + tag_list = 9, + tag_compound = 10, + tag_int_array = 11, + tag_long_array = 12 +}; + +struct any_tag +{ + mark_t* mark; + std::byte* source; + +#ifndef NDEBUG + nbt_type type; +#endif +}; + +struct nbt_list +{ + struct iterator + { + mark_t* mark; + std::byte* source; + + std::int32_t index; + +#ifndef NDEBUG + nbt_type type; +#endif + }; +#ifndef NDEBUG + nbt_type element_type; +#endif + std::int32_t length; + mark_t* mark; + std::byte* source; +}; + +struct nbt_compound +{ + struct iterator + { + mark_t* mark; + std::byte* source; + }; + mark_t* mark; + std::byte* source; +}; + +namespace nbt_document_function { + +/// +/// 释放 doc 所有的内存并置空所有指针. +/// +/// +inline void nbt_document_free(nbt_document* doc) noexcept +{ + doc->source = nullptr; + doc->mark = nullptr; + if (doc->source_m != nullptr) + { + #ifdef use_fastio + fast_io::native_global_allocator::deallocate(doc->source_m); + #else + free(doc->source_m); + #endif + } + if (doc->mark_m != nullptr) + { + #ifdef use_fastio + fast_io::native_global_allocator::deallocate(doc->mark_m); + #else + free(doc->mark_m); + #endif + } +} + +/// +/// 将 from 中已分配内存的所有权转移到 to. +/// to 中不能所有已分配内存,否则会导致泄漏. +/// 移动之后,from 中所有指针被置空. +/// +/// +/// +inline void nbt_document_move(nbt_document* from, nbt_document* to) noexcept +{ + *to = *from; + from->mark_m = nullptr; + from->source_m = nullptr; + from->mark = nullptr; + from->source = nullptr; +} + +inline const std::u8string_view nbt_document_root_key(const nbt_document* doc) noexcept +{ + if (static_cast(swapper::byte_as_type(doc->source)) == nbt_type::tag_end) [[unlikely]] + { + return {}; + } + return std::u8string_view( + reinterpret_cast(doc->source + sizeof(std::uint8_t)), + swapper::byte_as_type(doc->source + sizeof(std::uint8_t))); +} + +inline any_tag nbt_document_root_value(const nbt_document* doc) noexcept +{ + return any_tag{ + .mark = doc->mark, + .source = doc->source + sizeof(std::uint8_t) + sizeof(std::uint16_t) + swapper::byte_as_type(doc->source + sizeof(std::uint8_t)) +#ifndef NDEBUG + , + .type = static_cast(swapper::byte_as_type(doc->source)) +#endif + }; +} +} // namespace nbt_document_function +namespace any_tag_function { + +inline auto any_tag_get_end(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_end); + return; +} + +inline auto any_tag_get_byte(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_byte); + return swapper::byte_as_type(tag->source); +} + +inline auto any_tag_get_short(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_short); + return swapper::byte_as_type(tag->source); +} + +inline auto any_tag_get_int(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_int); + return swapper::byte_as_type(tag->source); +} + +inline auto any_tag_get_long(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_long); + return swapper::byte_as_type(tag->source); +} + +inline auto any_tag_get_float(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_float); + return swapper::byte_as_type(tag->source); +} + +inline auto any_tag_get_double(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_double); + return swapper::byte_as_type(tag->source); +} + +inline auto any_tag_get_byte_array(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_byte_array); + return std::span(reinterpret_cast(tag->source + sizeof(std::int32_t)), swapper::byte_as_type(tag->source)); +} + +inline auto any_tag_get_string(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_string); + return std::u8string_view(reinterpret_cast(tag->source + sizeof(std::uint16_t)), swapper::byte_as_type(tag->source)); +} + +inline auto any_tag_get_list_end(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_list); + assert(static_cast(swapper::byte_as_type(tag->source)) == nbt_type::tag_end); + return; +} + +inline auto any_tag_get_list_byte(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_list); + assert(static_cast(swapper::byte_as_type(tag->source)) == nbt_type::tag_byte); + return std::span( + reinterpret_cast(tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)), + swapper::byte_as_type(tag->source + sizeof(std::uint8_t))); +} + +inline auto any_tag_get_list_short(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_list); + assert(static_cast(swapper::byte_as_type(tag->source)) == nbt_type::tag_short); + return std::span( + reinterpret_cast(tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)), + swapper::byte_as_type(tag->source + sizeof(std::uint8_t))); +} + +inline auto any_tag_get_list_int(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_list); + assert(static_cast(swapper::byte_as_type(tag->source)) == nbt_type::tag_int); + return std::span( + reinterpret_cast(tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)), + swapper::byte_as_type(tag->source + sizeof(std::uint8_t))); +} + +inline auto any_tag_get_list_long(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_list); + assert(static_cast(swapper::byte_as_type(tag->source)) == nbt_type::tag_long); + return std::span( + reinterpret_cast(tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)), + swapper::byte_as_type(tag->source + sizeof(std::uint8_t))); +} + +inline auto any_tag_get_list_float(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_list); + assert(static_cast(swapper::byte_as_type(tag->source)) == nbt_type::tag_float); + return std::span( + reinterpret_cast(tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)), + swapper::byte_as_type(tag->source + sizeof(std::uint8_t))); +} + +inline auto any_tag_get_list_double(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_list); + assert(static_cast(swapper::byte_as_type(tag->source)) == nbt_type::tag_double); + return std::span( + reinterpret_cast(tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)), + swapper::byte_as_type(tag->source + sizeof(std::uint8_t))); +} + +inline auto any_tag_get_list_byte_array(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_list); + assert(static_cast(swapper::byte_as_type(tag->source)) == nbt_type::tag_byte_array); + return nbt_list{ +#ifndef NDEBUG + .element_type = nbt_type::tag_byte_array, +#endif + .length = swapper::byte_as_type(tag->source + sizeof(std::uint8_t)), + .mark = tag->mark, + .source = tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)}; +} + +inline auto any_tag_get_list_string(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_list); + assert(static_cast(swapper::byte_as_type(tag->source)) == nbt_type::tag_string); + return nbt_list{ +#ifndef NDEBUG + .element_type = nbt_type::tag_string, +#endif + .length = swapper::byte_as_type(tag->source + sizeof(std::uint8_t)), + .mark = tag->mark, + .source = tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)}; +} + +inline auto any_tag_get_list_list(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_list); + assert(static_cast(swapper::byte_as_type(tag->source)) == nbt_type::tag_list); + return nbt_list{ +#ifndef NDEBUG + .element_type = nbt_type::tag_list, +#endif + .length = swapper::byte_as_type(tag->source + sizeof(std::uint8_t)), + .mark = tag->mark, + .source = tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)}; +} + +inline auto any_tag_get_list_compound(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_list); + assert(static_cast(swapper::byte_as_type(tag->source)) == nbt_type::tag_compound); + return nbt_list{ +#ifndef NDEBUG + .element_type = nbt_type::tag_compound, +#endif + .length = swapper::byte_as_type(tag->source + sizeof(std::uint8_t)), + .mark = tag->mark, + .source = tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)}; +} + +inline auto any_tag_get_list_int_array(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_list); + assert(static_cast(swapper::byte_as_type(tag->source)) == nbt_type::tag_int_array); + return nbt_list{ +#ifndef NDEBUG + .element_type = nbt_type::tag_int_array, +#endif + .length = swapper::byte_as_type(tag->source + sizeof(std::uint8_t)), + .mark = tag->mark, + .source = tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)}; +} + +inline auto any_tag_get_list_long_array(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_list); + assert(static_cast(swapper::byte_as_type(tag->source)) == nbt_type::tag_long_array); + return nbt_list{ +#ifndef NDEBUG + .element_type = nbt_type::tag_long_array, +#endif + .length = swapper::byte_as_type(tag->source + sizeof(std::uint8_t)), + .mark = tag->mark, + .source = tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)}; +} + +inline auto any_tag_get_compound(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_compound); + return nbt_compound{ + .mark = tag->mark, + .source = tag->source}; +} + +inline auto any_tag_get_int_array(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_int_array); + return std::span(reinterpret_cast(tag->source + sizeof(std::int32_t)), swapper::byte_as_type(tag->source)); +} + +inline auto any_tag_get_long_array(const any_tag* tag) noexcept +{ + assert(tag->type == nbt_type::tag_long_array); + return std::span(reinterpret_cast(tag->source + sizeof(std::int32_t)), swapper::byte_as_type(tag->source)); +} + +inline auto any_tag_valid(const any_tag* tag) noexcept +{ + return tag->mark != nullptr && tag->source != nullptr +#ifndef NDEBUG + && tag->type == nbt_type::tag_end +#endif + ; +} + +} // namespace any_tag_function +namespace nbt_compound_function { +inline auto nbt_compound_iterator_begin(const nbt_compound* comp) noexcept +{ + return nbt_compound::iterator{ + .mark = comp->mark + 1, + .source = comp->source}; +} +inline auto nbt_compound_iterator_end(const nbt_compound* comp) noexcept +{ + return nbt_compound::iterator{ + .mark = nullptr, + .source = comp->mark->store.end - 1 //at tag_end + }; +} +inline void nbt_compound_iterator_next(nbt_compound::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + auto type = static_cast(swapper::byte_as_type(iter->source)); + assert(type != nbt_type::tag_end); + auto len = swapper::byte_as_type<::std::uint16_t>(iter->source + 1); //name length + + switch (type) + { + case nbt_type::tag_byte: + iter->source = iter->source + 1 + 2 + len + 1; + return; + case nbt_type::tag_short: + iter->source = iter->source + 1 + 2 + len + 2; + return; + case nbt_type::tag_int: + iter->source = iter->source + 1 + 2 + len + 4; + return; + case nbt_type::tag_long: + iter->source = iter->source + 1 + 2 + len + 8; + return; + case nbt_type::tag_float: + iter->source = iter->source + 1 + 2 + len + 4; + return; + case nbt_type::tag_double: + iter->source = iter->source + 1 + 2 + len + 8; + return; + case nbt_type::tag_string: + iter->source = iter->source + 1 + 2 + len; //payload begin + iter->source = iter->source + 2 + swapper::byte_as_type<::std::uint16_t>(iter->source); + return; + case nbt_type::tag_byte_array: + iter->source = iter->source + 1 + 2 + len; //payload begin + iter->source = iter->source + 4 + swapper::byte_as_type<::std::int32_t>(iter->source); + return; + case nbt_type::tag_int_array: + iter->source = iter->source + 1 + 2 + len; //payload begin + iter->source = iter->source + 4 + static_cast(swapper::byte_as_type<::std::int32_t>(iter->source)) * 4; + return; + case nbt_type::tag_long_array: + iter->source = iter->source + 1 + 2 + len; //payload begin + iter->source = iter->source + 4 + static_cast(swapper::byte_as_type<::std::int32_t>(iter->source)) * 8; + return; + case nbt_type::tag_list: + case nbt_type::tag_compound: + iter->source = iter->mark->store.end; + iter->mark = iter->mark + iter->mark->store.flat_next_mark; + return; + default: + std::unreachable(); + } +} + +inline bool nbt_compound_iter_equal(const nbt_compound::iterator* left, const nbt_compound::iterator* right) +{ + assert(left->source != nullptr); + assert(right->source != nullptr); + return left->source == right->source; +} +inline const std::u8string_view nbt_compound_iter_key(const nbt_compound::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + assert(static_cast(swapper::byte_as_type(iter->source)) != nbt_type::tag_end); + return std::u8string_view(reinterpret_cast(iter->source + sizeof(std::uint8_t) + sizeof(std::uint16_t)), swapper::byte_as_type(iter->source + sizeof(std::uint8_t))); +} +inline any_tag nbt_compound_iter_value(const nbt_compound::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + assert(static_cast(swapper::byte_as_type(iter->source)) != nbt_type::tag_end); + return any_tag{ + .mark = iter->mark, + .source = iter->source + sizeof(std::uint8_t) + sizeof(std::uint16_t) + swapper::byte_as_type(iter->source + sizeof(std::uint8_t)) +#ifndef NDEBUG + , + .type = static_cast(swapper::byte_as_type(iter->source)) +#endif + }; +} + +inline auto nbt_compound_find_value(const std::u8string_view key, const nbt_compound::iterator* begin, const nbt_compound::iterator* end) noexcept +{ + for (auto iter{*begin}; !nbt_compound_iter_equal(std::addressof(iter), end); nbt_compound_iterator_next(std::addressof(iter))) + { + if (nbt_compound_iter_key(std::addressof(iter)).compare(key) == 0) + { + return iter; + } + } + return *end; +} + +inline auto nbt_compound_find_value(const nbt_compound* comp, const std::u8string_view key) noexcept +{ + auto begin{nbt_compound_iterator_begin(comp)}; + auto end{nbt_compound_iterator_end(comp)}; + return nbt_compound_find_value(key, std::addressof(begin), std::addressof(end)); +} +} // namespace nbt_compound_function +namespace nbt_list_function { +inline auto nbt_list_iterator_begin(const nbt_list* list) noexcept +{ + return nbt_list::iterator{ + .mark = list->mark + 1, + .source = list->source, + .index = 0 +#ifndef NDEBUG + , + .type = list->element_type +#endif + }; +} +inline auto nbt_list_iterator_end(const nbt_list* list) noexcept +{ + return nbt_list::iterator{ + .mark = nullptr, + .source = list->mark->store.end, + .index = list->length}; +} + +inline void nbt_list_iterator_next_end(nbt_list::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + assert(iter->type == nbt_type::tag_end); + + iter->index++; + return; +} + +inline void nbt_list_iterator_next_byte(nbt_list::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + assert(iter->type == nbt_type::tag_byte); + + iter->index++; + iter->source = iter->source + 1; + return; +} + +inline void nbt_list_iterator_next_short(nbt_list::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + assert(iter->type == nbt_type::tag_short); + + iter->index++; + iter->source = iter->source + 2; + return; +} + +inline void nbt_list_iterator_next_int(nbt_list::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + assert(iter->type == nbt_type::tag_int); + + iter->index++; + iter->source = iter->source + 4; + return; +} + +inline void nbt_list_iterator_next_long(nbt_list::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + assert(iter->type == nbt_type::tag_long); + + iter->index++; + iter->source = iter->source + 8; + return; +} + +inline void nbt_list_iterator_next_float(nbt_list::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + assert(iter->type == nbt_type::tag_float); + + iter->index++; + iter->source = iter->source + 4; + return; +} + +inline void nbt_list_iterator_next_double(nbt_list::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + assert(iter->type == nbt_type::tag_double); + + iter->index++; + iter->source = iter->source + 8; + return; +} + +inline void nbt_list_iterator_next_string(nbt_list::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + assert(iter->type == nbt_type::tag_string); + + iter->index++; + iter->source = iter->source + 2 + swapper::byte_as_type<::std::uint16_t>(iter->source); + return; +} + +inline void nbt_list_iterator_next_byte_array(nbt_list::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + assert(iter->type == nbt_type::tag_byte_array); + + iter->index++; + iter->source = iter->source + 4 + swapper::byte_as_type<::std::int32_t>(iter->source); + return; +} + +inline void nbt_list_iterator_next_int_array(nbt_list::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + assert(iter->type == nbt_type::tag_int_array); + + iter->index++; + iter->source = iter->source + 4 + swapper::byte_as_type<::std::int32_t>(iter->source) * 4; + return; +} + +inline void nbt_list_iterator_next_long_array(nbt_list::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + assert(iter->type == nbt_type::tag_long_array); + + iter->index++; + iter->source = iter->source + 4 + swapper::byte_as_type<::std::int32_t>(iter->source) * 8; + return; +} + +inline void nbt_list_iterator_next_list(nbt_list::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + assert(iter->type == nbt_type::tag_list); + + iter->index++; + iter->source = iter->mark->store.end; + iter->mark = iter->mark + iter->mark->store.flat_next_mark; + return; +} + +inline void nbt_list_iterator_next_compound(nbt_list::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + assert(iter->type == nbt_type::tag_compound); + + iter->index++; + iter->source = iter->mark->store.end; + iter->mark = iter->mark + iter->mark->store.flat_next_mark; + return; +} + +inline bool nbt_list_iter_equal(const nbt_list::iterator* left, const nbt_list::iterator* right) +{ + assert(left->source != nullptr); + assert(right->source != nullptr); + return left->source == right->source; +} +inline any_tag nbt_list_iter_value(const nbt_list::iterator* iter) noexcept +{ + assert(iter->mark != nullptr); + assert(iter->source != nullptr); + return any_tag{ + .mark = iter->mark, + .source = iter->source +#ifndef NDEBUG + , + .type = iter->type +#endif + }; +} + +inline auto nbt_list_find_value_end(std::int32_t index, const nbt_list::iterator* begin, const nbt_list::iterator* end) noexcept +{ + auto iter{*begin}; + while (index--) + { + nbt_list_iterator_next_end(std::addressof(iter)); + } + return iter; +} + +inline auto nbt_list_find_value_byte(std::int32_t index, const nbt_list::iterator* begin, const nbt_list::iterator* end) noexcept +{ + auto iter{*begin}; + while (index--) + { + nbt_list_iterator_next_byte(std::addressof(iter)); + } + return iter; +} + +inline auto nbt_list_find_value_short(std::int32_t index, const nbt_list::iterator* begin, const nbt_list::iterator* end) noexcept +{ + auto iter{*begin}; + while (index--) + { + nbt_list_iterator_next_short(std::addressof(iter)); + } + return iter; +} + +inline auto nbt_list_find_value_int(std::int32_t index, const nbt_list::iterator* begin, const nbt_list::iterator* end) noexcept +{ + auto iter{*begin}; + while (index--) + { + nbt_list_iterator_next_int(std::addressof(iter)); + } + return iter; +} + +inline auto nbt_list_find_value_long(std::int32_t index, const nbt_list::iterator* begin, const nbt_list::iterator* end) noexcept +{ + auto iter{*begin}; + while (index--) + { + nbt_list_iterator_next_long(std::addressof(iter)); + } + return iter; +} + +inline auto nbt_list_find_value_float(std::int32_t index, const nbt_list::iterator* begin, const nbt_list::iterator* end) noexcept +{ + auto iter{*begin}; + while (index--) + { + nbt_list_iterator_next_float(std::addressof(iter)); + } + return iter; +} + +inline auto nbt_list_find_value_double(std::int32_t index, const nbt_list::iterator* begin, const nbt_list::iterator* end) noexcept +{ + auto iter{*begin}; + while (index--) + { + nbt_list_iterator_next_double(std::addressof(iter)); + } + return iter; +} + +inline auto nbt_list_find_value_byte_array(std::int32_t index, const nbt_list::iterator* begin, const nbt_list::iterator* end) noexcept +{ + auto iter{*begin}; + while (index--) + { + nbt_list_iterator_next_byte_array(std::addressof(iter)); + } + return iter; +} + +inline auto nbt_list_find_value_string(std::int32_t index, const nbt_list::iterator* begin, const nbt_list::iterator* end) noexcept +{ + auto iter{*begin}; + while (index--) + { + nbt_list_iterator_next_string(std::addressof(iter)); + } + return iter; +} + +inline auto nbt_list_find_value_list(std::int32_t index, const nbt_list::iterator* begin, const nbt_list::iterator* end) noexcept +{ + auto iter{*begin}; + while (index--) + { + nbt_list_iterator_next_list(std::addressof(iter)); + } + return iter; +} + +inline auto nbt_list_find_value_compound(std::int32_t index, const nbt_list::iterator* begin, const nbt_list::iterator* end) noexcept +{ + auto iter{*begin}; + while (index--) + { + nbt_list_iterator_next_compound(std::addressof(iter)); + } + return iter; +} + +inline auto nbt_list_find_value_int_array(std::int32_t index, const nbt_list::iterator* begin, const nbt_list::iterator* end) noexcept +{ + auto iter{*begin}; + while (index--) + { + nbt_list_iterator_next_int_array(std::addressof(iter)); + } + return iter; +} + +inline auto nbt_list_find_value_long_array(std::int32_t index, const nbt_list::iterator* begin, const nbt_list::iterator* end) noexcept +{ + auto iter{*begin}; + while (index--) + { + nbt_list_iterator_next_long_array(std::addressof(iter)); + } + return iter; +} + +inline auto nbt_list_find_value_end(const nbt_list* list, std::int32_t index) noexcept +{ + assert(list->mark != nullptr); + assert(list->source != nullptr); + assert(list->element_type == nbt_type::tag_end); + auto begin{nbt_list_iterator_begin(list)}; + auto end{nbt_list_iterator_end(list)}; + if (index >= list->length) + { + return end; + } + return nbt_list_find_value_end(index, std::addressof(begin), std::addressof(end)); +} + +inline auto nbt_list_find_value_byte(const nbt_list* list, std::int32_t index) noexcept +{ + assert(list->mark != nullptr); + assert(list->source != nullptr); + assert(list->element_type == nbt_type::tag_byte); + auto begin{nbt_list_iterator_begin(list)}; + auto end{nbt_list_iterator_end(list)}; + if (index >= list->length) + { + return end; + } + return nbt_list_find_value_byte(index, std::addressof(begin), std::addressof(end)); +} + +inline auto nbt_list_find_value_short(const nbt_list* list, std::int32_t index) noexcept +{ + assert(list->mark != nullptr); + assert(list->source != nullptr); + assert(list->element_type == nbt_type::tag_short); + auto begin{nbt_list_iterator_begin(list)}; + auto end{nbt_list_iterator_end(list)}; + if (index >= list->length) + { + return end; + } + return nbt_list_find_value_short(index, std::addressof(begin), std::addressof(end)); +} + +inline auto nbt_list_find_value_int(const nbt_list* list, std::int32_t index) noexcept +{ + assert(list->mark != nullptr); + assert(list->source != nullptr); + assert(list->element_type == nbt_type::tag_int); + auto begin{nbt_list_iterator_begin(list)}; + auto end{nbt_list_iterator_end(list)}; + if (index >= list->length) + { + return end; + } + return nbt_list_find_value_int(index, std::addressof(begin), std::addressof(end)); +} + +inline auto nbt_list_find_value_long(const nbt_list* list, std::int32_t index) noexcept +{ + assert(list->mark != nullptr); + assert(list->source != nullptr); + assert(list->element_type == nbt_type::tag_long); + auto begin{nbt_list_iterator_begin(list)}; + auto end{nbt_list_iterator_end(list)}; + if (index >= list->length) + { + return end; + } + return nbt_list_find_value_long(index, std::addressof(begin), std::addressof(end)); +} + +inline auto nbt_list_find_value_float(const nbt_list* list, std::int32_t index) noexcept +{ + assert(list->mark != nullptr); + assert(list->source != nullptr); + assert(list->element_type == nbt_type::tag_float); + auto begin{nbt_list_iterator_begin(list)}; + auto end{nbt_list_iterator_end(list)}; + if (index >= list->length) + { + return end; + } + return nbt_list_find_value_float(index, std::addressof(begin), std::addressof(end)); +} + +inline auto nbt_list_find_value_double(const nbt_list* list, std::int32_t index) noexcept +{ + assert(list->mark != nullptr); + assert(list->source != nullptr); + assert(list->element_type == nbt_type::tag_double); + auto begin{nbt_list_iterator_begin(list)}; + auto end{nbt_list_iterator_end(list)}; + if (index >= list->length) + { + return end; + } + return nbt_list_find_value_double(index, std::addressof(begin), std::addressof(end)); +} + +inline auto nbt_list_find_value_byte_array(const nbt_list* list, std::int32_t index) noexcept +{ + assert(list->mark != nullptr); + assert(list->source != nullptr); + assert(list->element_type == nbt_type::tag_byte_array); + auto begin{nbt_list_iterator_begin(list)}; + auto end{nbt_list_iterator_end(list)}; + if (index >= list->length) + { + return end; + } + return nbt_list_find_value_byte_array(index, std::addressof(begin), std::addressof(end)); +} + +inline auto nbt_list_find_value_string(const nbt_list* list, std::int32_t index) noexcept +{ + assert(list->mark != nullptr); + assert(list->source != nullptr); + assert(list->element_type == nbt_type::tag_string); + auto begin{nbt_list_iterator_begin(list)}; + auto end{nbt_list_iterator_end(list)}; + if (index >= list->length) + { + return end; + } + return nbt_list_find_value_string(index, std::addressof(begin), std::addressof(end)); +} + +inline auto nbt_list_find_value_list(const nbt_list* list, std::int32_t index) noexcept +{ + assert(list->mark != nullptr); + assert(list->source != nullptr); + assert(list->element_type == nbt_type::tag_list); + auto begin{nbt_list_iterator_begin(list)}; + auto end{nbt_list_iterator_end(list)}; + if (index >= list->length) + { + return end; + } + return nbt_list_find_value_list(index, std::addressof(begin), std::addressof(end)); +} + +inline auto nbt_list_find_value_compound(const nbt_list* list, std::int32_t index) noexcept +{ + assert(list->mark != nullptr); + assert(list->source != nullptr); + assert(list->element_type == nbt_type::tag_compound); + auto begin{nbt_list_iterator_begin(list)}; + auto end{nbt_list_iterator_end(list)}; + if (index >= list->length) + { + return end; + } + return nbt_list_find_value_compound(index, std::addressof(begin), std::addressof(end)); +} + +inline auto nbt_list_find_value_int_array(const nbt_list* list, std::int32_t index) noexcept +{ + assert(list->mark != nullptr); + assert(list->source != nullptr); + assert(list->element_type == nbt_type::tag_int_array); + auto begin{nbt_list_iterator_begin(list)}; + auto end{nbt_list_iterator_end(list)}; + if (index >= list->length) + { + return end; + } + return nbt_list_find_value_int_array(index, std::addressof(begin), std::addressof(end)); +} + +inline auto nbt_list_find_value_long_array(const nbt_list* list, std::int32_t index) noexcept +{ + assert(list->mark != nullptr); + assert(list->source != nullptr); + assert(list->element_type == nbt_type::tag_long_array); + auto begin{nbt_list_iterator_begin(list)}; + auto end{nbt_list_iterator_end(list)}; + if (index >= list->length) + { + return end; + } + return nbt_list_find_value_long_array(index, std::addressof(begin), std::addressof(end)); +} + +} // namespace nbt_list_function +namespace swapper { + +template +inline T& byte_as_type(std::byte* ptr) noexcept +{ + return *reinterpret_cast(ptr); +} + +template +requires(nbt_endian == std::endian::big || nbt_endian == std::endian::little) inline T endian_get(std::byte* ptr) noexcept +{ + if constexpr (nbt_endian == std::endian::big) + { + return fast_io::big_endian(byte_as_type>(ptr)); + } + else + { + return fast_io::little_endian(byte_as_type>(ptr)); + } +} + +template +requires(nbt_endian == std::endian::big || nbt_endian == std::endian::little) inline T endian_make_native_get(std::byte* ptr) noexcept +{ + using unsigned_T = std::make_unsigned_t; + T& t = byte_as_type(ptr); + if constexpr (nbt_endian == std::endian::big) + { + t = static_cast(fast_io::big_endian(static_cast(t))); + } + else + { + t = static_cast(fast_io::little_endian(static_cast(t))); + } + return t; +} + +template +requires(nbt_endian == std::endian::big || nbt_endian == std::endian::little) inline T endian_get_make_native(std::byte* ptr) noexcept +{ + using unsigned_T = std::make_unsigned_t; + T& t = byte_as_type(ptr); + T ret = t; + if constexpr (nbt_endian == std::endian::big) + { + t = static_cast(fast_io::big_endian(static_cast(t))); + } + else + { + t = static_cast(fast_io::little_endian(static_cast(t))); + } + return ret; +} + +template +requires(nbt_endian == std::endian::big || nbt_endian == std::endian::little) inline void endian_make_native(std::byte* ptr) noexcept +{ + using unsigned_T = std::make_unsigned_t; + unsigned_T& t = byte_as_type(ptr); + if constexpr (nbt_endian == std::endian::big) + { + t = fast_io::big_endian(t); + } + else + { + t = fast_io::little_endian(t); + } + return; +} + +struct default_read_swapper +{ + template + static constexpr inline void tag_byte(std::byte*& current_pos) noexcept + { + current_pos += (sizeof(std::int8_t)); + } + template + static constexpr inline void tag_short(std::byte*& current_pos) noexcept + { + endian_make_native(current_pos); + current_pos += (sizeof(std::int16_t)); + } + template + static constexpr inline void tag_int(std::byte*& current_pos) noexcept + { + endian_make_native(current_pos); + current_pos += (sizeof(std::int32_t)); + } + template + static constexpr inline void tag_long(std::byte*& current_pos) noexcept + { + endian_make_native(current_pos); + current_pos += (sizeof(std::int64_t)); + } + template + static constexpr inline void tag_float(std::byte*& current_pos) noexcept + { + endian_make_native(current_pos); + current_pos += (sizeof(std::float_t)); + } + template + static constexpr inline void tag_double(std::byte*& current_pos) noexcept + { + endian_make_native(current_pos); + current_pos += (sizeof(std::double_t)); + } + template + static constexpr inline void tag_byte_array(std::byte*& current_pos) noexcept + { + auto len = endian_make_native_get(current_pos); + current_pos += sizeof(std::int32_t); + current_pos += sizeof(std::int8_t) * len; + } + template + static constexpr inline void tag_string(std::byte*& current_pos) noexcept + { + auto len = endian_make_native_get(current_pos); + current_pos += sizeof(std::uint16_t); + current_pos += sizeof(char) * len; + } + template + static constexpr inline void tag_int_array(std::byte*& current_pos) noexcept + { + auto len = endian_make_native_get(current_pos); + current_pos += sizeof(std::int32_t); + if constexpr (nbt_endian == std::endian::native) + { + current_pos += sizeof(std::int32_t) * len; + } + else + { + for (std::int32_t _index = 0; _index < len; _index++) + { + endian_make_native(current_pos); + current_pos += sizeof(std::int32_t); + } + } + } + template + static constexpr inline void tag_long_array(std::byte*& current_pos) noexcept + { + auto len = endian_make_native_get(current_pos); + current_pos += sizeof(std::int32_t); + current_pos += sizeof(std::int32_t); + if constexpr (nbt_endian == std::endian::native) + { + current_pos += sizeof(std::int64_t) * len; + } + else + { + for (std::int32_t _index = 0; _index < len; _index++) + { + endian_make_native(current_pos); + current_pos += sizeof(std::int64_t); + } + } + } +}; + +struct default_write_swapper +{ + template + static constexpr inline void tag_byte(std::byte*& current_pos) noexcept + { + current_pos += (sizeof(std::int8_t)); + } + template + static constexpr inline void tag_short(std::byte*& current_pos) noexcept + { + endian_make_native(current_pos); + current_pos += (sizeof(std::int16_t)); + } + template + static constexpr inline void tag_int(std::byte*& current_pos) noexcept + { + endian_make_native(current_pos); + current_pos += (sizeof(std::int32_t)); + } + template + static constexpr inline void tag_long(std::byte*& current_pos) noexcept + { + endian_make_native(current_pos); + current_pos += (sizeof(std::int64_t)); + } + template + static constexpr inline void tag_float(std::byte*& current_pos) noexcept + { + endian_make_native(current_pos); + current_pos += (sizeof(std::float_t)); + } + template + static constexpr inline void tag_double(std::byte*& current_pos) noexcept + { + endian_make_native(current_pos); + current_pos += (sizeof(std::double_t)); + } + template + static constexpr inline void tag_byte_array(std::byte*& current_pos) noexcept + { + auto len = endian_get_make_native(current_pos); + current_pos += sizeof(std::int32_t); + current_pos += sizeof(std::int8_t) * len; + } + template + static constexpr inline void tag_string(std::byte*& current_pos) noexcept + { + auto len = endian_get_make_native(current_pos); + current_pos += sizeof(std::uint16_t); + current_pos += sizeof(char) * len; + } + template + static constexpr inline void tag_int_array(std::byte*& current_pos) noexcept + { + auto len = endian_get_make_native(current_pos); + current_pos += sizeof(std::int32_t); + //current_pos += sizeof(std::int32_t) * len; + for (std::int32_t _index = 0; _index < len; _index++) + { + endian_make_native(current_pos); + current_pos += sizeof(std::int32_t); + } + } + template + static constexpr inline void tag_long_array(std::byte*& current_pos) noexcept + { + auto len = endian_get_make_native(current_pos); + current_pos += sizeof(std::int32_t); + //current_pos += sizeof(std::int64_t) * len; + for (std::int32_t _index = 0; _index < len; _index++) + { + endian_make_native(current_pos); + current_pos += sizeof(std::int64_t); + } + } +}; + +} // namespace swapper +namespace read_write { + +template +[[nodiscard]] inline auto read(std::byte* source, std::size_t source_len) -> nbt_document +{ +#define bound_check_return(pos) \ + if ((pos) > source_len) [[unlikely]] \ + { \ + throw nbt_parse_error::end_of_file; \ + } + + nbt_document t{}; + if constexpr (in_place) + { + t.source_m = nullptr; + t.source = source; + t.source_len = source_len; + } + else + { + t.source_m = static_cast(fast_io::native_global_allocator::allocate(source_len)); + t.source = t.source_m; + t.source_len = source_len; + fast_io::freestanding::non_overlapped_copy_n(source, source_len, t.source); + } + auto src{t.source}; + std::size_t readed_length{0}; + + auto current_pos{src + 1}; //read first byte (id), +1 + if constexpr (bound_check) + { + readed_length += 1; + bound_check_return(readed_length); + } + if (swapper::endian_get(src) != 0) [[likely]] //is tag_end, ignore tag name and payload + { + //read tag name + { + if constexpr (bound_check) + { + readed_length += sizeof(std::uint16_t); + bound_check_return(readed_length); + auto len = swapper::endian_get(src + 1); + readed_length += len; + bound_check_return(readed_length); + } + std::uint16_t len = swapper::endian_make_native_get(src + 1); //read name length + current_pos += sizeof(std::uint16_t); + current_pos += len; // read name, +2 +length*1 + } + switch (swapper::endian_get(src)) + { + case 1: //tag_byte + if constexpr (bound_check) + { + readed_length += 1; + bound_check_return(readed_length); + } + rswap::template tag_byte(current_pos); + break; + case 2: //tag_short + if constexpr (bound_check) + { + readed_length += 2; + bound_check_return(readed_length); + } + rswap::template tag_short(current_pos); + break; + case 3: //tag_int + if constexpr (bound_check) + { + readed_length += 4; + bound_check_return(readed_length); + } + rswap::template tag_int(current_pos); + break; + case 4: //tag_long + if constexpr (bound_check) + { + readed_length += 8; + bound_check_return(readed_length); + } + rswap::template tag_long(current_pos); + break; + case 5: //tag_float + if constexpr (bound_check) + { + readed_length += 4; + bound_check_return(readed_length); + } + rswap::template tag_float(current_pos); + break; + case 6: //tag_double + if constexpr (bound_check) + { + readed_length += 8; + bound_check_return(readed_length); + } + rswap::template tag_double(current_pos); + break; + case 7: //tag_byte_array + if constexpr (bound_check) + { + readed_length += 4; + bound_check_return(readed_length); + auto len = swapper::endian_get(current_pos); + readed_length += len; + bound_check_return(readed_length); + } + rswap::template tag_byte_array(current_pos); + break; + case 8: //tag_string + + if constexpr (bound_check) + { + readed_length += 2; + bound_check_return(readed_length); + auto len = swapper::endian_get(current_pos); + readed_length += len; + bound_check_return(readed_length); + } + rswap::template tag_string(current_pos); + break; + case 9: //tag_list + goto general_start; + case 10: //tag_compound + goto general_start; + case 11: //tag_int_array + if constexpr (bound_check) + { + readed_length += 4; + bound_check_return(readed_length); + auto len = swapper::endian_get(current_pos); + readed_length += len * sizeof(std::int32_t); + bound_check_return(readed_length); + } + rswap::template tag_int_array(current_pos); + break; + case 12: //tag_long_array + if constexpr (bound_check) + { + readed_length += 4; + bound_check_return(readed_length); + auto len = swapper::endian_get(current_pos); + readed_length += len * sizeof(std::int64_t); + bound_check_return(readed_length); + } + rswap::template tag_long_array(current_pos); + break; + default: + throw nbt_parse_error::invalid; + } + } + t.mark_m = nullptr; + t.mark = t.mark_m; + t.mark_len = 0; + return t; +general_start: + using mark = mark_t; + std::size_t alc_len{static_cast(t.source_len) / 32 /*todo: magic number*/ + 4 /*avoid zero*/}; + mark* mark_hdr{static_cast(fast_io::native_global_allocator::allocate(alc_len * sizeof(mark)))}; //first element of mark array + mark* mark_end{mark_hdr + alc_len}; //end+1 of mark array + mark* use_end{mark_hdr}; //last element used + mark* _mark_tmp{nullptr}; //tmp + mark* current{use_end}; //current container + mark* parent{use_end}; //parent container + + current->cache.general_parrent_offset = 0; + + if (swapper::endian_get(src) == 9) //list + { + goto list_general_begin; + } + else //compound + { + goto comp_general_begin; + } + +comp_begin: + parent = current; + use_end++; + if (use_end > mark_end) [[unlikely]] + { + alc_len += alc_len / 2; + if (std::is_constant_evaluated()) + { + _mark_tmp = reinterpret_cast(fast_io::native_global_allocator::reallocate_n(mark_hdr, (mark_end - mark_hdr) * sizeof(mark), alc_len * sizeof(mark))); + } + else + { + _mark_tmp = reinterpret_cast(fast_io::native_global_allocator::reallocate(mark_hdr, alc_len * sizeof(mark))); //reallocate should make sure that the allocation is successful. + } + use_end = _mark_tmp + (use_end - mark_hdr); + parent = _mark_tmp + (parent - mark_hdr); + mark_hdr = _mark_tmp; + mark_end = _mark_tmp + alc_len; + } + current = use_end; +comp_general_begin: + current->cache.general_parrent_offset = static_cast(use_end - parent); + current->cache.general_is_compound = 1; + // goto comp_item_begin +comp_item_begin: +{ + if constexpr (bound_check) + { + readed_length++; + bound_check_return(readed_length); + } + const std::uint8_t id{swapper::endian_get(current_pos)}; + current_pos++; + if (id == 0) [[unlikely]] + { + goto comp_end; + } + if constexpr (bound_check) + { + readed_length += sizeof(std::uint16_t); + bound_check_return(readed_length); + auto len = swapper::endian_get(current_pos); + readed_length += len; + bound_check_return(readed_length); + } + auto len = swapper::endian_make_native_get(current_pos); + current_pos += sizeof(std::uint16_t); + current_pos += (len * sizeof(char)); + switch (id) + { + case 1: + { + if constexpr (bound_check) + { + readed_length += 1; + bound_check_return(readed_length); + } + rswap::template tag_byte(current_pos); + break; + } + case 2: + { + if constexpr (bound_check) + { + readed_length += 2; + bound_check_return(readed_length); + } + rswap::template tag_short(current_pos); + break; + } + case 3: + { + if constexpr (bound_check) + { + readed_length += 4; + bound_check_return(readed_length); + } + rswap::template tag_int(current_pos); + break; + } + case 4: + { + if constexpr (bound_check) + { + readed_length += 8; + bound_check_return(readed_length); + } + rswap::template tag_long(current_pos); + break; + } + case 5: + { + if constexpr (bound_check) + { + readed_length += 4; + bound_check_return(readed_length); + } + rswap::template tag_float(current_pos); + break; + } + case 6: + { + if constexpr (bound_check) + { + readed_length += 8; + bound_check_return(readed_length); + } + rswap::template tag_double(current_pos); + break; + } + case 7: + { + if constexpr (bound_check) + { + readed_length += 4; + bound_check_return(readed_length); + auto arrlen = swapper::endian_get(current_pos); + readed_length += arrlen; + bound_check_return(readed_length); + } + rswap::template tag_byte_array(current_pos); + break; + } + case 8: + { + if constexpr (bound_check) + { + readed_length += 2; + bound_check_return(readed_length); + auto arrlen = swapper::endian_get(current_pos); + readed_length += arrlen; + bound_check_return(readed_length); + } + rswap::template tag_string(current_pos); + break; + } + case 9: + { + goto list_begin; + } + case 10: + { + goto comp_begin; + } + case 11: + { + if constexpr (bound_check) + { + readed_length += 4; + bound_check_return(readed_length); + std::size_t arrlen = swapper::endian_get(current_pos); + readed_length += arrlen * sizeof(std::int32_t); + bound_check_return(readed_length); + } + rswap::template tag_int_array(current_pos); + break; + } + case 12: + { + if constexpr (bound_check) + { + readed_length += 4; + bound_check_return(readed_length); + std::size_t arrlen = swapper::endian_get(current_pos); + readed_length += arrlen * sizeof(std::int64_t); + bound_check_return(readed_length); + } + rswap::template tag_long_array(current_pos); + break; + } + default: + fast_io::native_global_allocator::deallocate_n(mark_hdr, alc_len * sizeof(mark)); + throw nbt_parse_error::invalid; + }; + goto comp_item_begin; +} +comp_end: + if (current->cache.general_parrent_offset == 0) [[unlikely]] + { + current->store.end = current_pos; + current->store.flat_next_mark = use_end - current + 1; + goto read_finish; + } + current->store.end = current_pos; + current->store.flat_next_mark = use_end - current + 1; + current = parent; + parent = parent - (parent->cache.general_parrent_offset); + if (current->cache.general_is_compound == 1) + { + goto comp_item_begin; + } + else + { + goto list_item_begin; + } +list_begin: + parent = current; + use_end++; + if (use_end > mark_end) [[unlikely]] + { + alc_len += alc_len / 2; + if (std::is_constant_evaluated()) + { + #ifdef use_fastio + _mark_tmp = reinterpret_cast(fast_io::native_global_allocator::reallocate_n(mark_hdr, (mark_end - mark_hdr) * sizeof(mark), alc_len * sizeof(mark))); + #else + _mark_tmp = reinterpret_cast(realloc(mark_hdr, (mark_end - mark_hdr) * sizeof(mark), alc_len * sizeof(mark))); + #endif + } + else + { + _mark_tmp = reinterpret_cast(fast_io::native_global_allocator::reallocate(mark_hdr, alc_len * sizeof(mark))); //reallocate should make sure that the allocation is successful. + } + use_end = _mark_tmp + (use_end - mark_hdr); + parent = _mark_tmp + (parent - mark_hdr); + mark_hdr = _mark_tmp; + mark_end = _mark_tmp + alc_len; + } + current = use_end; +list_general_begin: + if constexpr (bound_check) + { + readed_length++; + readed_length += sizeof(std::uint32_t); + bound_check_return(readed_length); + auto id = swapper::endian_get(current_pos); + if (id >= 0 && id <= 6) + { + const std::size_t len = swapper::endian_get(current_pos + 1); + switch (id) + { + case 0: + break; + case 1: + readed_length += len; + break; + case 2: + readed_length += len * 2; + break; + case 3: + readed_length += len * 4; + break; + case 4: + readed_length += len * 8; + break; + case 5: + readed_length += len * 4; + break; + case 6: + readed_length += len * 8; + break; + default: + break; + } + bound_check_return(readed_length); + } + } + current->cache.list_type = swapper::endian_get(current_pos); + current->cache.general_parrent_offset = static_cast(current - parent); + current->cache.general_is_compound = 0; + current_pos++; + current->cache.list_total_length = swapper::endian_make_native_get(current_pos); + current->cache.list_current_length = 0; + current_pos += sizeof(std::uint32_t); + goto list_item_begin; +list_item_begin: + if (current->cache.list_current_length >= current->cache.list_total_length) [[unlikely]] + { + goto list_end; + } + + current->cache.list_current_length++; + switch (current->cache.list_type) + { + case 0: + { + break; + } + case 1: + { + rswap::template tag_byte(current_pos); + break; + } + case 2: + { + rswap::template tag_short(current_pos); + break; + } + case 3: + { + rswap::template tag_int(current_pos); + break; + } + case 4: + { + rswap::template tag_long(current_pos); + break; + } + case 5: + { + rswap::template tag_float(current_pos); + break; + } + case 6: + { + rswap::template tag_double(current_pos); + break; + } + case 7: + { + if constexpr (bound_check) + { + readed_length += 4; + bound_check_return(readed_length); + auto len = swapper::endian_get(current_pos); + readed_length += len; + bound_check_return(readed_length); + } + rswap::template tag_byte_array(current_pos); + break; + } + case 8: + { + if constexpr (bound_check) + { + readed_length += 2; + bound_check_return(readed_length); + auto len = swapper::endian_get(current_pos); + readed_length += len; + bound_check_return(readed_length); + } + rswap::template tag_string(current_pos); + break; + } + case 9: + { + goto list_begin; + } + case 10: + { + goto comp_begin; + } + case 11: + { + if constexpr (bound_check) + { + readed_length += 4; + bound_check_return(readed_length); + auto len = swapper::endian_get(current_pos); + readed_length += len * sizeof(std::int32_t); + bound_check_return(readed_length); + } + rswap::template tag_int_array(current_pos); + break; + } + case 12: + { + if constexpr (bound_check) + { + readed_length += 4; + bound_check_return(readed_length); + auto len = swapper::endian_get(current_pos); + readed_length += len * sizeof(std::int64_t); + bound_check_return(readed_length); + } + rswap::template tag_long_array(current_pos); + break; + } + default: + fast_io::native_global_allocator::deallocate_n(mark_hdr, alc_len * sizeof(mark)); + throw nbt_parse_error::invalid; + }; + goto list_item_begin; +list_end: + if (current->cache.general_parrent_offset == 0) [[unlikely]] + { + current->store.end = current_pos; + current->store.flat_next_mark = use_end - current + 1; + goto read_finish; + } + current->store.end = current_pos; + current->store.flat_next_mark = use_end - current + 1; + current = parent; + parent = parent - (parent->cache.general_parrent_offset); + if (current->cache.general_is_compound == 1) + { + goto comp_item_begin; + } + else + { + goto list_item_begin; + } + +read_finish: + t.mark_m = mark_hdr; + t.mark = t.mark_m; + t.mark_len = (mark_end - mark_hdr); + return t; + +#undef bound_check_return +} +} // namespace read_write +} // namespace impl + +using nbt_parse_error = impl::nbt_parse_error; +using nbt_type = impl::nbt_type; +struct nbt_type_error +{ + nbt_type is; + nbt_type as; +}; +class nbt_document; +template +class nbt_any_tag; +template +class nbt_list; +class nbt_compound; + +template +[[nodiscard]] inline auto read(std::span source) -> nbt_document; + +template +class nbt_list +{ + private: + impl::nbt_list impl_list; + + template + friend class nbt_any_tag; + + public: + class iterator + { + private: + impl::nbt_list::iterator impl_iterator; + + public: + inline iterator& operator++() noexcept + { + if constexpr (list_element_type == nbt_type::tag_end) + { + impl::nbt_list_function::nbt_list_iterator_next_end(std::addressof(impl_iterator)); + } + else if constexpr (list_element_type == nbt_type::tag_byte) + { + impl::nbt_list_function::nbt_list_iterator_next_byte(std::addressof(impl_iterator)); + } + else if constexpr (list_element_type == nbt_type::tag_short) + { + impl::nbt_list_function::nbt_list_iterator_next_short(std::addressof(impl_iterator)); + } + else if constexpr (list_element_type == nbt_type::tag_int) + { + impl::nbt_list_function::nbt_list_iterator_next_int(std::addressof(impl_iterator)); + } + else if constexpr (list_element_type == nbt_type::tag_long) + { + impl::nbt_list_function::nbt_list_iterator_next_long(std::addressof(impl_iterator)); + } + else if constexpr (list_element_type == nbt_type::tag_float) + { + impl::nbt_list_function::nbt_list_iterator_next_float(std::addressof(impl_iterator)); + } + else if constexpr (list_element_type == nbt_type::tag_double) + { + impl::nbt_list_function::nbt_list_iterator_next_double(std::addressof(impl_iterator)); + } + else if constexpr (list_element_type == nbt_type::tag_byte_array) + { + impl::nbt_list_function::nbt_list_iterator_next_byte_array(std::addressof(impl_iterator)); + } + else if constexpr (list_element_type == nbt_type::tag_string) + { + impl::nbt_list_function::nbt_list_iterator_next_string(std::addressof(impl_iterator)); + } + else if constexpr (list_element_type == nbt_type::tag_list) + { + impl::nbt_list_function::nbt_list_iterator_next_list(std::addressof(impl_iterator)); + } + else if constexpr (list_element_type == nbt_type::tag_compound) + { + impl::nbt_list_function::nbt_list_iterator_next_compound(std::addressof(impl_iterator)); + } + else if constexpr (list_element_type == nbt_type::tag_int_array) + { + impl::nbt_list_function::nbt_list_iterator_next_int_array(std::addressof(impl_iterator)); + } + else + { + impl::nbt_list_function::nbt_list_iterator_next_long_array(std::addressof(impl_iterator)); + } + return *this; + } + inline const iterator operator++(int) noexcept + { + auto old = *this; + ++(*this); + return old; + } + [[nodiscard]] inline bool operator==(const iterator& x) const noexcept + { + return impl::nbt_list_function::nbt_list_iter_equal(std::addressof(impl_iterator), std::addressof(x.impl_iterator)); + } + [[nodiscard]] inline bool operator!=(const iterator& x) const noexcept + { + return !impl::nbt_list_function::nbt_list_iter_equal(std::addressof(impl_iterator), std::addressof(x.impl_iterator)); + } + + template + [[nodiscard]] inline auto value() const + { + nbt_any_tag tag{}; + if constexpr (list_element_type == nbt_type::tag_list) + { + auto list_type{ + static_cast(*impl_iterator.source)}; + if (list_type != element_type) + { + throw nbt_type_error{.is = list_type, .as = element_type}; + } + } + tag.impl_any_tag = impl::nbt_list_function::nbt_list_iter_value(std::addressof(impl_iterator)); + return tag.get(); + } + + [[nodiscard]] inline consteval nbt_type type() const noexcept + { + return list_element_type; + } + + [[nodiscard]] inline nbt_type element_type() const noexcept + { + return static_cast(*impl_iterator.source); + } + + friend class nbt_list; + }; + + [[nodiscard]] inline iterator begin() const noexcept + { + iterator iter{}; + iter.impl_iterator = impl::nbt_list_function::nbt_list_iterator_begin(std::addressof(impl_list)); + return iter; + } + + [[nodiscard]] inline iterator end() const noexcept + { + iterator iter{}; + iter.impl_iterator = impl::nbt_list_function::nbt_list_iterator_end(std::addressof(impl_list)); + return iter; + } + [[nodiscard]] inline std::size_t size() const noexcept + { + return impl_list.length; + } + [[nodiscard]] inline auto operator[](std::int32_t index) const noexcept + { + iterator iter{}; + if constexpr (list_element_type == nbt_type::tag_end) + { + iter.impl_iterator = (impl::nbt_list_function::nbt_list_find_value_end(std::addressof(impl_list), index)); + } + else if constexpr (list_element_type == nbt_type::tag_byte) + { + iter.impl_iterator = (impl::nbt_list_function::nbt_list_find_value_byte(std::addressof(impl_list), index)); + } + else if constexpr (list_element_type == nbt_type::tag_short) + { + iter.impl_iterator = (impl::nbt_list_function::nbt_list_find_value_short(std::addressof(impl_list), index)); + } + else if constexpr (list_element_type == nbt_type::tag_int) + { + iter.impl_iterator = (impl::nbt_list_function::nbt_list_find_value_int(std::addressof(impl_list), index)); + } + else if constexpr (list_element_type == nbt_type::tag_long) + { + iter.impl_iterator = (impl::nbt_list_function::nbt_list_find_value_long(std::addressof(impl_list), index)); + } + else if constexpr (list_element_type == nbt_type::tag_float) + { + iter.impl_iterator = (impl::nbt_list_function::nbt_list_find_value_float(std::addressof(impl_list), index)); + } + else if constexpr (list_element_type == nbt_type::tag_double) + { + iter.impl_iterator = (impl::nbt_list_function::nbt_list_find_value_double(std::addressof(impl_list), index)); + } + else if constexpr (list_element_type == nbt_type::tag_byte_array) + { + iter.impl_iterator = (impl::nbt_list_function::nbt_list_find_value_byte_array(std::addressof(impl_list), index)); + } + else if constexpr (list_element_type == nbt_type::tag_string) + { + iter.impl_iterator = (impl::nbt_list_function::nbt_list_find_value_string(std::addressof(impl_list), index)); + } + else if constexpr (list_element_type == nbt_type::tag_list) + { + iter.impl_iterator = (impl::nbt_list_function::nbt_list_find_value_list(std::addressof(impl_list), index)); + } + else if constexpr (list_element_type == nbt_type::tag_compound) + { + iter.impl_iterator = (impl::nbt_list_function::nbt_list_find_value_compound(std::addressof(impl_list), index)); + } + else if constexpr (list_element_type == nbt_type::tag_int_array) + { + iter.impl_iterator = (impl::nbt_list_function::nbt_list_find_value_int_array(std::addressof(impl_list), index)); + } + else + { + iter.impl_iterator = (impl::nbt_list_function::nbt_list_find_value_long_array(std::addressof(impl_list), index)); + } + return iter; + } +}; + +class nbt_compound +{ + private: + impl::nbt_compound impl_compound; + + template + friend class nbt_any_tag; + + public: + class iterator + { + private: + impl::nbt_compound::iterator impl_iterator; + + public: + inline iterator& operator++() noexcept + { + impl::nbt_compound_function::nbt_compound_iterator_next(std::addressof(impl_iterator)); + return *this; + } + inline const iterator operator++(int) noexcept + { + auto old = *this; + ++(*this); + return old; + } + [[nodiscard]] inline bool operator==(const iterator& x) const noexcept + { + return impl::nbt_compound_function::nbt_compound_iter_equal(std::addressof(impl_iterator), std::addressof(x.impl_iterator)); + } + [[nodiscard]] inline bool operator!=(const iterator& x) const noexcept + { + return !impl::nbt_compound_function::nbt_compound_iter_equal(std::addressof(impl_iterator), std::addressof(x.impl_iterator)); + } + + template + [[nodiscard]] inline auto value() const + { + auto real_type{static_cast(impl::swapper::byte_as_type(impl_iterator.source))}; + if (real_type != tag_type) + { + throw nbt_type_error{.is = real_type, .as = tag_type}; + } + if constexpr (tag_type == nbt_type::tag_list) + { + auto list_type{ + static_cast(impl::swapper::byte_as_type( + impl_iterator.source + sizeof(std::uint8_t) + sizeof(std::uint16_t) + + impl::swapper::byte_as_type( + impl_iterator.source + sizeof(std::uint8_t))))}; + if (list_type != list_element_type) + { + throw nbt_type_error{.is = list_type, .as = list_element_type}; + } + } + nbt_any_tag tag{}; + tag.impl_any_tag = impl::nbt_compound_function::nbt_compound_iter_value(std::addressof(impl_iterator)); + return tag.get(); + } + + [[nodiscard]] inline std::u8string_view key() const noexcept + { + return impl::nbt_compound_function::nbt_compound_iter_key(std::addressof(impl_iterator)); + } + + [[nodiscard]] inline nbt_type type() const noexcept + { + return static_cast(impl::swapper::byte_as_type(impl_iterator.source)); + } + + [[nodiscard]] inline nbt_type element_type() const noexcept + { + return static_cast(impl::swapper::byte_as_type( + impl_iterator.source + sizeof(std::uint8_t) + sizeof(std::uint16_t) + + impl::swapper::byte_as_type( + impl_iterator.source + sizeof(std::uint8_t)))); + } + + using difference_type = std::ptrdiff_t; + + friend class nbt_compound; + }; + + [[nodiscard]] inline iterator begin() const noexcept + { + iterator iter{}; + iter.impl_iterator = impl::nbt_compound_function::nbt_compound_iterator_begin(std::addressof(impl_compound)); + return iter; + } + + [[nodiscard]] inline iterator end() const noexcept + { + iterator iter{}; + iter.impl_iterator = impl::nbt_compound_function::nbt_compound_iterator_end(std::addressof(impl_compound)); + return iter; + } + + [[nodiscard]] inline iterator at(std::u8string_view key) const + { + iterator iter{}; + iter.impl_iterator = impl::nbt_compound_function::nbt_compound_find_value(std::addressof(impl_compound), key); + return iter; + } +}; + +template +class nbt_any_tag +{ + private: + impl::any_tag impl_any_tag; + + friend class nbt_document; + template + friend class nbt_list; + friend class nbt_compound; + + public: + [[nodiscard]] inline consteval nbt_type type() noexcept + { + return tag_type; + } + + [[nodiscard]] inline consteval nbt_type element_type() noexcept + { + return list_element_type; + } + + [[nodiscard]] inline auto get() noexcept + { + if constexpr (tag_type == nbt_type::tag_end) + { + return; + } + else if constexpr (tag_type == nbt_type::tag_byte) + { + return impl::any_tag_function::any_tag_get_byte(std::addressof(impl_any_tag)); + } + else if constexpr (tag_type == nbt_type::tag_short) + { + return impl::any_tag_function::any_tag_get_short(std::addressof(impl_any_tag)); + } + else if constexpr (tag_type == nbt_type::tag_int) + { + return impl::any_tag_function::any_tag_get_int(std::addressof(impl_any_tag)); + } + else if constexpr (tag_type == nbt_type::tag_long) + { + return impl::any_tag_function::any_tag_get_long(std::addressof(impl_any_tag)); + } + else if constexpr (tag_type == nbt_type::tag_float) + { + return impl::any_tag_function::any_tag_get_float(std::addressof(impl_any_tag)); + } + else if constexpr (tag_type == nbt_type::tag_double) + { + return impl::any_tag_function::any_tag_get_double(std::addressof(impl_any_tag)); + } + else if constexpr (tag_type == nbt_type::tag_byte_array) + { + return impl::any_tag_function::any_tag_get_byte_array(std::addressof(impl_any_tag)); + } + else if constexpr (tag_type == nbt_type::tag_string) + { + return impl::any_tag_function::any_tag_get_string(std::addressof(impl_any_tag)); + } + else if constexpr (tag_type == nbt_type::tag_list) + { + if constexpr (list_element_type == nbt_type::tag_end) + { + return; + } + else if constexpr (list_element_type == nbt_type::tag_byte) + { + return impl::any_tag_function::any_tag_get_list_byte(std::addressof(impl_any_tag)); + } + else if constexpr (list_element_type == nbt_type::tag_short) + { + return impl::any_tag_function::any_tag_get_list_short(std::addressof(impl_any_tag)); + } + else if constexpr (list_element_type == nbt_type::tag_int) + { + return impl::any_tag_function::any_tag_get_list_int(std::addressof(impl_any_tag)); + } + else if constexpr (list_element_type == nbt_type::tag_long) + { + return impl::any_tag_function::any_tag_get_list_long(std::addressof(impl_any_tag)); + } + else if constexpr (list_element_type == nbt_type::tag_float) + { + return impl::any_tag_function::any_tag_get_list_float(std::addressof(impl_any_tag)); + } + else if constexpr (list_element_type == nbt_type::tag_double) + { + return impl::any_tag_function::any_tag_get_list_double(std::addressof(impl_any_tag)); + } + else if constexpr (list_element_type == nbt_type::tag_byte_array) + { + nbt_list list{}; + list.impl_list = impl::any_tag_function::any_tag_get_list_byte_array(std::addressof(impl_any_tag)); + return list; + } + else if constexpr (list_element_type == nbt_type::tag_string) + { + nbt_list list{}; + list.impl_list = impl::any_tag_function::any_tag_get_list_string(std::addressof(impl_any_tag)); + return list; + } + else if constexpr (list_element_type == nbt_type::tag_list) + { + nbt_list list{}; + list.impl_list = impl::any_tag_function::any_tag_get_list_list(std::addressof(impl_any_tag)); + return list; + } + else if constexpr (list_element_type == nbt_type::tag_compound) + { + nbt_list list{}; + list.impl_list = impl::any_tag_function::any_tag_get_list_compound(std::addressof(impl_any_tag)); + return list; + } + else if constexpr (list_element_type == nbt_type::tag_int_array) + { + nbt_list list{}; + list.impl_list = impl::any_tag_function::any_tag_get_list_int_array(std::addressof(impl_any_tag)); + return list; + } + else + { + nbt_list list{}; + list.impl_list = impl::any_tag_function::any_tag_get_list_long_array(std::addressof(impl_any_tag)); + return list; + } + } + else if constexpr (tag_type == nbt_type::tag_compound) + { + nbt_compound comp{}; + comp.impl_compound = impl::any_tag_function::any_tag_get_compound(std::addressof(impl_any_tag)); + return comp; + } + else if constexpr (tag_type == nbt_type::tag_int_array) + { + return impl::any_tag_function::any_tag_get_int_array(std::addressof(impl_any_tag)); + } + else + { + return impl::any_tag_function::any_tag_get_long_array(std::addressof(impl_any_tag)); + } + } +}; + +class nbt_document +{ + private: + impl::nbt_document impl_document; + + template + friend inline auto read(std::span source) -> nbt_document; + + public: + nbt_document(const nbt_document&) = delete; + nbt_document& operator=(const nbt_document&) = delete; + + inline nbt_document& operator=(nbt_document&& right) noexcept + { + impl::nbt_document_function::nbt_document_move( + std::addressof(right.impl_document), + std::addressof(this->impl_document)); + return *this; + } + inline ~nbt_document() noexcept + { + impl::nbt_document_function::nbt_document_free( + std::addressof(this->impl_document)); + } + inline nbt_document() noexcept = default; + inline nbt_document(nbt_document&& right) noexcept + { + impl::nbt_document_function::nbt_document_move( + std::addressof(right.impl_document), + std::addressof(this->impl_document)); + } + + public: + [[nodiscard]] inline nbt_type type() noexcept + { + return static_cast(impl::swapper::byte_as_type(this->impl_document.source)); + } + [[nodiscard]] inline nbt_type element_type() noexcept + { + return static_cast(impl::swapper::byte_as_type( + this->impl_document.source + sizeof(std::uint8_t) + sizeof(std::uint16_t) + + impl::swapper::byte_as_type( + this->impl_document.source + sizeof(std::uint8_t)))); + } + [[nodiscard]] inline auto key() noexcept + { + return impl::nbt_document_function::nbt_document_root_key(std::addressof(this->impl_document)); + } + template + [[nodiscard]] inline auto value() + { + auto real_type{static_cast(impl::swapper::byte_as_type(this->impl_document.source))}; + if (real_type != tag_type) + { + throw nbt_type_error{.is = real_type, .as = tag_type}; + } + if constexpr (tag_type == nbt_type::tag_list) + { + auto list_type{ + static_cast(impl::swapper::byte_as_type( + this->impl_document.source + sizeof(std::uint8_t) + sizeof(std::uint16_t) + + impl::swapper::byte_as_type( + this->impl_document.source + sizeof(std::uint8_t))))}; + if (list_type != list_element_type) + { + throw nbt_type_error{.is = list_type, .as = list_element_type}; + } + } + nbt_any_tag tag{}; + tag.impl_any_tag = impl::nbt_document_function::nbt_document_root_value(std::addressof(this->impl_document)); + return tag.get(); + } +}; + +template +[[nodiscard]] auto read(std::span source) -> nbt_document +{ + nbt_document doc{}; + doc.impl_document = impl::read_write::read(source.data(), source.size()); + return doc; +} +} // namespace na::nbt \ No newline at end of file diff --git a/cpp/test.cpp b/cpp/test.cpp new file mode 100644 index 0000000..3f94344 --- /dev/null +++ b/cpp/test.cpp @@ -0,0 +1,32 @@ +#include "./fast_io/include/fast_io.h" +#include "./fast_io/include/fast_io_device.h" +#include "na_nbt_impl.hpp" +#include +int main(int argc, char** argv) +{ + { + fast_io::native_file_loader nfl(fast_io::mnp::os_c_str(argv[1])); + std::system("pause"); + { + std::vector data{ + reinterpret_cast(nfl.begin()), + reinterpret_cast(nfl.end())}; + fast_io::io::println(fast_io::out(), "test big_endian read in_place with bound_check"); + + using na::nbt::nbt_type; + std::system("pause"); + auto beforetime = std::chrono::steady_clock::now(); + auto document{na::nbt::read(std::span(data))}; + auto aftertime = std::chrono::steady_clock::now(); + std::system("pause"); + double duration_second = std::chrono::duration(aftertime - beforetime).count(); + auto root = document.value(); + + fast_io::io::println(fast_io::out(), duration_second, "s"); + fast_io::io::println(fast_io::out(), "size@ ", nfl.size(), "bytes"); + fast_io::io::println(fast_io::out(), "speed@ ", nfl.size() / 1000.0 / 1000.0 / duration_second, "mb/s"); + fast_io::io::println(fast_io::out(), ""); + } + std::system("pause"); + } +} \ No newline at end of file