nbt-rust/cpp/na_nbt_impl.hpp

2437 lines
83 KiB
C++
Raw Normal View History

2024-01-12 18:18:55 +08:00
#pragma once
#define use_fastio
#ifdef use_fastio
#include "./fast_io/include/fast_io.h"
#endif
#include <bit>
#include <cassert>
#include <cmath>
#include <concepts>
#include <span>
#include <string>
#include <utility>
namespace na::nbt::v7 {
namespace impl {
enum class nbt_parse_error
{
end_of_file,
invalid
};
struct nbt_document;
namespace swapper {
template<typename T>
inline T& byte_as_type(std::byte* ptr) noexcept;
template<typename T, std::endian nbt_endian>
requires(nbt_endian == std::endian::big || nbt_endian == std::endian::little) inline T endian_get(std::byte* ptr) noexcept;
template<typename T, std::endian nbt_endian>
requires(nbt_endian == std::endian::big || nbt_endian == std::endian::little) inline T endian_make_native_get(std::byte* ptr) noexcept;
template<typename T, std::endian nbt_endian>
requires(nbt_endian == std::endian::big || nbt_endian == std::endian::little) inline T endian_get_make_native(std::byte* ptr) noexcept;
template<typename T, std::endian nbt_endian>
requires(nbt_endian == std::endian::big || nbt_endian == std::endian::little) inline void endian_make_native(std::byte* ptr) noexcept;
template<typename read_swapper, std::endian nbt_endian>
concept read_swapper_for_endian = requires(std::byte * &t)
{
read_swapper::template tag_byte<nbt_endian>(t);
read_swapper::template tag_short<nbt_endian>(t);
read_swapper::template tag_int<nbt_endian>(t);
read_swapper::template tag_long<nbt_endian>(t);
read_swapper::template tag_float<nbt_endian>(t);
read_swapper::template tag_double<nbt_endian>(t);
read_swapper::template tag_byte_array<nbt_endian>(t);
read_swapper::template tag_string<nbt_endian>(t);
read_swapper::template tag_int_array<nbt_endian>(t);
read_swapper::template tag_long_array<nbt_endian>(t);
};
template<typename read_swapper_t>
concept read_swapper =
read_swapper_for_endian<read_swapper_t, std::endian::big> &&
read_swapper_for_endian<read_swapper_t, std::endian::little>;
template<typename write_swapper, std::endian nbt_endian>
concept write_swapper_for_endian = requires(std::byte * &t)
{
write_swapper::template tag_byte<nbt_endian>(t);
write_swapper::template tag_short<nbt_endian>(t);
write_swapper::template tag_int<nbt_endian>(t);
write_swapper::template tag_long<nbt_endian>(t);
write_swapper::template tag_float<nbt_endian>(t);
write_swapper::template tag_double<nbt_endian>(t);
write_swapper::template tag_byte_array<nbt_endian>(t);
write_swapper::template tag_string<nbt_endian>(t);
write_swapper::template tag_int_array<nbt_endian>(t);
write_swapper::template tag_long_array<nbt_endian>(t);
};
template<typename write_swapper_t>
concept write_swapper =
write_swapper_for_endian<write_swapper_t, std::endian::big> &&
write_swapper_for_endian<write_swapper_t, std::endian::little>;
} // namespace swapper
namespace read_write {
template<bool in_place, bool bound_check, std::endian nbt_endian, swapper::read_swapper rswap>
[[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 {
/// <summary>
/// 释放 doc 所有的内存并置空所有指针.
/// </summary>
/// <param name="doc"></param>
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
}
}
/// <summary>
/// 将 from 中已分配内存的所有权转移到 to.
/// to 中不能所有已分配内存,否则会导致泄漏.
/// 移动之后from 中所有指针被置空.
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
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<nbt_type>(swapper::byte_as_type<std::uint8_t>(doc->source)) == nbt_type::tag_end) [[unlikely]]
{
return {};
}
return std::u8string_view(
reinterpret_cast<char8_t*>(doc->source + sizeof(std::uint8_t)),
swapper::byte_as_type<std::uint16_t>(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<std::uint16_t>(doc->source + sizeof(std::uint8_t))
#ifndef NDEBUG
,
.type = static_cast<nbt_type>(swapper::byte_as_type<std::uint8_t>(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<std::int8_t>(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<std::int16_t>(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<std::int32_t>(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<std::int64_t>(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<std::float_t>(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<std::double_t>(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<const std::int8_t, std::dynamic_extent>(reinterpret_cast<std::int8_t*>(tag->source + sizeof(std::int32_t)), swapper::byte_as_type<std::int32_t>(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<char8_t*>(tag->source + sizeof(std::uint16_t)), swapper::byte_as_type<std::uint16_t>(tag->source));
}
inline auto any_tag_get_list_end(const any_tag* tag) noexcept
{
assert(tag->type == nbt_type::tag_list);
assert(static_cast<nbt_type>(swapper::byte_as_type<std::uint8_t>(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<nbt_type>(swapper::byte_as_type<std::uint8_t>(tag->source)) == nbt_type::tag_byte);
return std::span<std::int8_t, std::dynamic_extent>(
reinterpret_cast<std::int8_t*>(tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)),
swapper::byte_as_type<std::int32_t>(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<nbt_type>(swapper::byte_as_type<std::uint8_t>(tag->source)) == nbt_type::tag_short);
return std::span<std::int16_t, std::dynamic_extent>(
reinterpret_cast<std::int16_t*>(tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)),
swapper::byte_as_type<std::int32_t>(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<nbt_type>(swapper::byte_as_type<std::uint8_t>(tag->source)) == nbt_type::tag_int);
return std::span<std::int32_t, std::dynamic_extent>(
reinterpret_cast<std::int32_t*>(tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)),
swapper::byte_as_type<std::int32_t>(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<nbt_type>(swapper::byte_as_type<std::uint8_t>(tag->source)) == nbt_type::tag_long);
return std::span<std::int64_t, std::dynamic_extent>(
reinterpret_cast<std::int64_t*>(tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)),
swapper::byte_as_type<std::int32_t>(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<nbt_type>(swapper::byte_as_type<std::uint8_t>(tag->source)) == nbt_type::tag_float);
return std::span<std::float_t, std::dynamic_extent>(
reinterpret_cast<std::float_t*>(tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)),
swapper::byte_as_type<std::int32_t>(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<nbt_type>(swapper::byte_as_type<std::uint8_t>(tag->source)) == nbt_type::tag_double);
return std::span<std::double_t, std::dynamic_extent>(
reinterpret_cast<std::double_t*>(tag->source + sizeof(std::uint8_t) + sizeof(std::int32_t)),
swapper::byte_as_type<std::int32_t>(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<nbt_type>(swapper::byte_as_type<std::uint8_t>(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<std::int32_t>(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<nbt_type>(swapper::byte_as_type<std::uint8_t>(tag->source)) == nbt_type::tag_string);
return nbt_list{
#ifndef NDEBUG
.element_type = nbt_type::tag_string,
#endif
.length = swapper::byte_as_type<std::int32_t>(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<nbt_type>(swapper::byte_as_type<std::uint8_t>(tag->source)) == nbt_type::tag_list);
return nbt_list{
#ifndef NDEBUG
.element_type = nbt_type::tag_list,
#endif
.length = swapper::byte_as_type<std::int32_t>(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<nbt_type>(swapper::byte_as_type<std::uint8_t>(tag->source)) == nbt_type::tag_compound);
return nbt_list{
#ifndef NDEBUG
.element_type = nbt_type::tag_compound,
#endif
.length = swapper::byte_as_type<std::int32_t>(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<nbt_type>(swapper::byte_as_type<std::uint8_t>(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<std::int32_t>(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<nbt_type>(swapper::byte_as_type<std::uint8_t>(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<std::int32_t>(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<const std::int32_t, std::dynamic_extent>(reinterpret_cast<std::int32_t*>(tag->source + sizeof(std::int32_t)), swapper::byte_as_type<std::int32_t>(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<const std::int64_t, std::dynamic_extent>(reinterpret_cast<std::int64_t*>(tag->source + sizeof(std::int32_t)), swapper::byte_as_type<std::int32_t>(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<nbt_type>(swapper::byte_as_type<std::uint8_t>(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<std::ptrdiff_t>(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<std::ptrdiff_t>(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<nbt_type>(swapper::byte_as_type<std::uint8_t>(iter->source)) != nbt_type::tag_end);
return std::u8string_view(reinterpret_cast<char8_t*>(iter->source + sizeof(std::uint8_t) + sizeof(std::uint16_t)), swapper::byte_as_type<std::uint16_t>(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<nbt_type>(swapper::byte_as_type<std::uint8_t>(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<std::uint16_t>(iter->source + sizeof(std::uint8_t))
#ifndef NDEBUG
,
.type = static_cast<nbt_type>(swapper::byte_as_type<std::uint8_t>(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<typename T>
inline T& byte_as_type(std::byte* ptr) noexcept
{
return *reinterpret_cast<T*>(ptr);
}
template<typename T, std::endian nbt_endian>
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<std::make_unsigned_t<T>>(ptr));
}
else
{
return fast_io::little_endian(byte_as_type<std::make_unsigned_t<T>>(ptr));
}
}
template<typename T, std::endian nbt_endian>
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& t = byte_as_type<T>(ptr);
if constexpr (nbt_endian == std::endian::big)
{
t = static_cast<T>(fast_io::big_endian(static_cast<unsigned_T>(t)));
}
else
{
t = static_cast<T>(fast_io::little_endian(static_cast<unsigned_T>(t)));
}
return t;
}
template<typename T, std::endian nbt_endian>
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& t = byte_as_type<T>(ptr);
T ret = t;
if constexpr (nbt_endian == std::endian::big)
{
t = static_cast<T>(fast_io::big_endian(static_cast<unsigned_T>(t)));
}
else
{
t = static_cast<T>(fast_io::little_endian(static_cast<unsigned_T>(t)));
}
return ret;
}
template<typename T, std::endian nbt_endian>
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<T>;
unsigned_T& t = byte_as_type<unsigned_T>(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<std::endian nbt_endian>
static constexpr inline void tag_byte(std::byte*& current_pos) noexcept
{
current_pos += (sizeof(std::int8_t));
}
template<std::endian nbt_endian>
static constexpr inline void tag_short(std::byte*& current_pos) noexcept
{
endian_make_native<std::uint16_t, nbt_endian>(current_pos);
current_pos += (sizeof(std::int16_t));
}
template<std::endian nbt_endian>
static constexpr inline void tag_int(std::byte*& current_pos) noexcept
{
endian_make_native<std::uint32_t, nbt_endian>(current_pos);
current_pos += (sizeof(std::int32_t));
}
template<std::endian nbt_endian>
static constexpr inline void tag_long(std::byte*& current_pos) noexcept
{
endian_make_native<std::uint64_t, nbt_endian>(current_pos);
current_pos += (sizeof(std::int64_t));
}
template<std::endian nbt_endian>
static constexpr inline void tag_float(std::byte*& current_pos) noexcept
{
endian_make_native<std::uint32_t, nbt_endian>(current_pos);
current_pos += (sizeof(std::float_t));
}
template<std::endian nbt_endian>
static constexpr inline void tag_double(std::byte*& current_pos) noexcept
{
endian_make_native<std::uint64_t, nbt_endian>(current_pos);
current_pos += (sizeof(std::double_t));
}
template<std::endian nbt_endian>
static constexpr inline void tag_byte_array(std::byte*& current_pos) noexcept
{
auto len = endian_make_native_get<std::int32_t, nbt_endian>(current_pos);
current_pos += sizeof(std::int32_t);
current_pos += sizeof(std::int8_t) * len;
}
template<std::endian nbt_endian>
static constexpr inline void tag_string(std::byte*& current_pos) noexcept
{
auto len = endian_make_native_get<std::uint16_t, nbt_endian>(current_pos);
current_pos += sizeof(std::uint16_t);
current_pos += sizeof(char) * len;
}
template<std::endian nbt_endian>
static constexpr inline void tag_int_array(std::byte*& current_pos) noexcept
{
auto len = endian_make_native_get<std::int32_t, nbt_endian>(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<std::int32_t, nbt_endian>(current_pos);
current_pos += sizeof(std::int32_t);
}
}
}
template<std::endian nbt_endian>
static constexpr inline void tag_long_array(std::byte*& current_pos) noexcept
{
auto len = endian_make_native_get<std::int32_t, nbt_endian>(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<std::int64_t, nbt_endian>(current_pos);
current_pos += sizeof(std::int64_t);
}
}
}
};
struct default_write_swapper
{
template<std::endian nbt_endian>
static constexpr inline void tag_byte(std::byte*& current_pos) noexcept
{
current_pos += (sizeof(std::int8_t));
}
template<std::endian nbt_endian>
static constexpr inline void tag_short(std::byte*& current_pos) noexcept
{
endian_make_native<std::uint16_t, nbt_endian>(current_pos);
current_pos += (sizeof(std::int16_t));
}
template<std::endian nbt_endian>
static constexpr inline void tag_int(std::byte*& current_pos) noexcept
{
endian_make_native<std::uint32_t, nbt_endian>(current_pos);
current_pos += (sizeof(std::int32_t));
}
template<std::endian nbt_endian>
static constexpr inline void tag_long(std::byte*& current_pos) noexcept
{
endian_make_native<std::uint64_t, nbt_endian>(current_pos);
current_pos += (sizeof(std::int64_t));
}
template<std::endian nbt_endian>
static constexpr inline void tag_float(std::byte*& current_pos) noexcept
{
endian_make_native<std::uint32_t, nbt_endian>(current_pos);
current_pos += (sizeof(std::float_t));
}
template<std::endian nbt_endian>
static constexpr inline void tag_double(std::byte*& current_pos) noexcept
{
endian_make_native<std::uint64_t, nbt_endian>(current_pos);
current_pos += (sizeof(std::double_t));
}
template<std::endian nbt_endian>
static constexpr inline void tag_byte_array(std::byte*& current_pos) noexcept
{
auto len = endian_get_make_native<std::int32_t, nbt_endian>(current_pos);
current_pos += sizeof(std::int32_t);
current_pos += sizeof(std::int8_t) * len;
}
template<std::endian nbt_endian>
static constexpr inline void tag_string(std::byte*& current_pos) noexcept
{
auto len = endian_get_make_native<std::uint16_t, nbt_endian>(current_pos);
current_pos += sizeof(std::uint16_t);
current_pos += sizeof(char) * len;
}
template<std::endian nbt_endian>
static constexpr inline void tag_int_array(std::byte*& current_pos) noexcept
{
auto len = endian_get_make_native<std::int32_t, nbt_endian>(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<std::int32_t, nbt_endian>(current_pos);
current_pos += sizeof(std::int32_t);
}
}
template<std::endian nbt_endian>
static constexpr inline void tag_long_array(std::byte*& current_pos) noexcept
{
auto len = endian_get_make_native<std::int32_t, nbt_endian>(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<std::int64_t, nbt_endian>(current_pos);
current_pos += sizeof(std::int64_t);
}
}
};
} // namespace swapper
namespace read_write {
template<bool in_place = true, bool bound_check = false, std::endian nbt_endian = std::endian::big, swapper::read_swapper rswap = swapper::default_read_swapper>
[[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<std::byte*>(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<std::uint8_t, nbt_endian>(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<std::uint16_t, nbt_endian>(src + 1);
readed_length += len;
bound_check_return(readed_length);
}
std::uint16_t len = swapper::endian_make_native_get<std::uint16_t, nbt_endian>(src + 1); //read name length
current_pos += sizeof(std::uint16_t);
current_pos += len; // read name, +2 +length*1
}
switch (swapper::endian_get<std::uint8_t, nbt_endian>(src))
{
case 1: //tag_byte
if constexpr (bound_check)
{
readed_length += 1;
bound_check_return(readed_length);
}
rswap::template tag_byte<nbt_endian>(current_pos);
break;
case 2: //tag_short
if constexpr (bound_check)
{
readed_length += 2;
bound_check_return(readed_length);
}
rswap::template tag_short<nbt_endian>(current_pos);
break;
case 3: //tag_int
if constexpr (bound_check)
{
readed_length += 4;
bound_check_return(readed_length);
}
rswap::template tag_int<nbt_endian>(current_pos);
break;
case 4: //tag_long
if constexpr (bound_check)
{
readed_length += 8;
bound_check_return(readed_length);
}
rswap::template tag_long<nbt_endian>(current_pos);
break;
case 5: //tag_float
if constexpr (bound_check)
{
readed_length += 4;
bound_check_return(readed_length);
}
rswap::template tag_float<nbt_endian>(current_pos);
break;
case 6: //tag_double
if constexpr (bound_check)
{
readed_length += 8;
bound_check_return(readed_length);
}
rswap::template tag_double<nbt_endian>(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<std::int32_t, nbt_endian>(current_pos);
readed_length += len;
bound_check_return(readed_length);
}
rswap::template tag_byte_array<nbt_endian>(current_pos);
break;
case 8: //tag_string
if constexpr (bound_check)
{
readed_length += 2;
bound_check_return(readed_length);
auto len = swapper::endian_get<std::uint16_t, nbt_endian>(current_pos);
readed_length += len;
bound_check_return(readed_length);
}
rswap::template tag_string<nbt_endian>(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<std::int32_t, nbt_endian>(current_pos);
readed_length += len * sizeof(std::int32_t);
bound_check_return(readed_length);
}
rswap::template tag_int_array<nbt_endian>(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<std::int32_t, nbt_endian>(current_pos);
readed_length += len * sizeof(std::int64_t);
bound_check_return(readed_length);
}
rswap::template tag_long_array<nbt_endian>(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<std::size_t>(t.source_len) / 32 /*todo: magic number*/ + 4 /*avoid zero*/};
mark* mark_hdr{static_cast<mark*>(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<std::uint8_t, nbt_endian>(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<mark*>(fast_io::native_global_allocator::reallocate_n(mark_hdr, (mark_end - mark_hdr) * sizeof(mark), alc_len * sizeof(mark)));
}
else
{
_mark_tmp = reinterpret_cast<mark*>(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<std::uint32_t>(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<std::uint8_t, nbt_endian>(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<std::uint16_t, nbt_endian>(current_pos);
readed_length += len;
bound_check_return(readed_length);
}
auto len = swapper::endian_make_native_get<std::uint16_t, nbt_endian>(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<nbt_endian>(current_pos);
break;
}
case 2:
{
if constexpr (bound_check)
{
readed_length += 2;
bound_check_return(readed_length);
}
rswap::template tag_short<nbt_endian>(current_pos);
break;
}
case 3:
{
if constexpr (bound_check)
{
readed_length += 4;
bound_check_return(readed_length);
}
rswap::template tag_int<nbt_endian>(current_pos);
break;
}
case 4:
{
if constexpr (bound_check)
{
readed_length += 8;
bound_check_return(readed_length);
}
rswap::template tag_long<nbt_endian>(current_pos);
break;
}
case 5:
{
if constexpr (bound_check)
{
readed_length += 4;
bound_check_return(readed_length);
}
rswap::template tag_float<nbt_endian>(current_pos);
break;
}
case 6:
{
if constexpr (bound_check)
{
readed_length += 8;
bound_check_return(readed_length);
}
rswap::template tag_double<nbt_endian>(current_pos);
break;
}
case 7:
{
if constexpr (bound_check)
{
readed_length += 4;
bound_check_return(readed_length);
auto arrlen = swapper::endian_get<std::int32_t, nbt_endian>(current_pos);
readed_length += arrlen;
bound_check_return(readed_length);
}
rswap::template tag_byte_array<nbt_endian>(current_pos);
break;
}
case 8:
{
if constexpr (bound_check)
{
readed_length += 2;
bound_check_return(readed_length);
auto arrlen = swapper::endian_get<std::uint16_t, nbt_endian>(current_pos);
readed_length += arrlen;
bound_check_return(readed_length);
}
rswap::template tag_string<nbt_endian>(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<std::int32_t, nbt_endian>(current_pos);
readed_length += arrlen * sizeof(std::int32_t);
bound_check_return(readed_length);
}
rswap::template tag_int_array<nbt_endian>(current_pos);
break;
}
case 12:
{
if constexpr (bound_check)
{
readed_length += 4;
bound_check_return(readed_length);
std::size_t arrlen = swapper::endian_get<std::int32_t, nbt_endian>(current_pos);
readed_length += arrlen * sizeof(std::int64_t);
bound_check_return(readed_length);
}
rswap::template tag_long_array<nbt_endian>(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<mark*>(fast_io::native_global_allocator::reallocate_n(mark_hdr, (mark_end - mark_hdr) * sizeof(mark), alc_len * sizeof(mark)));
#else
_mark_tmp = reinterpret_cast<mark*>(realloc(mark_hdr, (mark_end - mark_hdr) * sizeof(mark), alc_len * sizeof(mark)));
#endif
}
else
{
_mark_tmp = reinterpret_cast<mark*>(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<std::uint8_t, nbt_endian>(current_pos);
if (id >= 0 && id <= 6)
{
const std::size_t len = swapper::endian_get<std::uint32_t, nbt_endian>(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<std::uint8_t, nbt_endian>(current_pos);
current->cache.general_parrent_offset = static_cast<std::uint32_t>(current - parent);
current->cache.general_is_compound = 0;
current_pos++;
current->cache.list_total_length = swapper::endian_make_native_get<std::uint32_t, nbt_endian>(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<nbt_endian>(current_pos);
break;
}
case 2:
{
rswap::template tag_short<nbt_endian>(current_pos);
break;
}
case 3:
{
rswap::template tag_int<nbt_endian>(current_pos);
break;
}
case 4:
{
rswap::template tag_long<nbt_endian>(current_pos);
break;
}
case 5:
{
rswap::template tag_float<nbt_endian>(current_pos);
break;
}
case 6:
{
rswap::template tag_double<nbt_endian>(current_pos);
break;
}
case 7:
{
if constexpr (bound_check)
{
readed_length += 4;
bound_check_return(readed_length);
auto len = swapper::endian_get<std::int32_t, nbt_endian>(current_pos);
readed_length += len;
bound_check_return(readed_length);
}
rswap::template tag_byte_array<nbt_endian>(current_pos);
break;
}
case 8:
{
if constexpr (bound_check)
{
readed_length += 2;
bound_check_return(readed_length);
auto len = swapper::endian_get<std::uint16_t, nbt_endian>(current_pos);
readed_length += len;
bound_check_return(readed_length);
}
rswap::template tag_string<nbt_endian>(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<std::int32_t, nbt_endian>(current_pos);
readed_length += len * sizeof(std::int32_t);
bound_check_return(readed_length);
}
rswap::template tag_int_array<nbt_endian>(current_pos);
break;
}
case 12:
{
if constexpr (bound_check)
{
readed_length += 4;
bound_check_return(readed_length);
auto len = swapper::endian_get<std::int32_t, nbt_endian>(current_pos);
readed_length += len * sizeof(std::int64_t);
bound_check_return(readed_length);
}
rswap::template tag_long_array<nbt_endian>(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<nbt_type tag_type, nbt_type list_element_type>
class nbt_any_tag;
template<nbt_type list_element_type>
class nbt_list;
class nbt_compound;
template<bool in_place, bool bound_check, std::endian nbt_endian, impl::swapper::read_swapper rswap>
[[nodiscard]] inline auto read(std::span<std::byte, std::dynamic_extent> source) -> nbt_document;
template<nbt_type list_element_type>
class nbt_list
{
private:
impl::nbt_list impl_list;
template<nbt_type tag_type, nbt_type list_element_type_>
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<nbt_type element_type = nbt_type::tag_end>
[[nodiscard]] inline auto value() const
{
nbt_any_tag<list_element_type, element_type> tag{};
if constexpr (list_element_type == nbt_type::tag_list)
{
auto list_type{
static_cast<nbt_type>(*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<nbt_type>(*impl_iterator.source);
}
friend class nbt_list<list_element_type>;
};
[[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<nbt_type tag_type, nbt_type list_element_type>
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<nbt_type tag_type, nbt_type list_element_type = nbt_type::tag_end>
[[nodiscard]] inline auto value() const
{
auto real_type{static_cast<nbt_type>(impl::swapper::byte_as_type<std::uint8_t>(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<nbt_type>(impl::swapper::byte_as_type<std::uint8_t>(
impl_iterator.source + sizeof(std::uint8_t) + sizeof(std::uint16_t) +
impl::swapper::byte_as_type<std::uint16_t>(
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_type, list_element_type> 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<nbt_type>(impl::swapper::byte_as_type<std::uint8_t>(impl_iterator.source));
}
[[nodiscard]] inline nbt_type element_type() const noexcept
{
return static_cast<nbt_type>(impl::swapper::byte_as_type<std::uint8_t>(
impl_iterator.source + sizeof(std::uint8_t) + sizeof(std::uint16_t) +
impl::swapper::byte_as_type<std::uint16_t>(
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<nbt_type tag_type, nbt_type list_element_type = nbt_type::tag_end>
class nbt_any_tag
{
private:
impl::any_tag impl_any_tag;
friend class nbt_document;
template<nbt_type list_element_type_>
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_element_type> 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_element_type> 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_element_type> 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_element_type> 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_element_type> 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_element_type> 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<bool in_place, bool bound_check, std::endian nbt_endian, impl::swapper::read_swapper rswap>
friend inline auto read(std::span<std::byte, std::dynamic_extent> 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<nbt_type>(impl::swapper::byte_as_type<std::uint8_t>(this->impl_document.source));
}
[[nodiscard]] inline nbt_type element_type() noexcept
{
return static_cast<nbt_type>(impl::swapper::byte_as_type<std::uint8_t>(
this->impl_document.source + sizeof(std::uint8_t) + sizeof(std::uint16_t) +
impl::swapper::byte_as_type<std::uint16_t>(
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<nbt_type tag_type, nbt_type list_element_type = nbt_type::tag_end>
[[nodiscard]] inline auto value()
{
auto real_type{static_cast<nbt_type>(impl::swapper::byte_as_type<std::uint8_t>(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<nbt_type>(impl::swapper::byte_as_type<std::uint8_t>(
this->impl_document.source + sizeof(std::uint8_t) + sizeof(std::uint16_t) +
impl::swapper::byte_as_type<std::uint16_t>(
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_type, list_element_type> tag{};
tag.impl_any_tag = impl::nbt_document_function::nbt_document_root_value(std::addressof(this->impl_document));
return tag.get();
}
};
template<bool in_place = true, bool bound_check = false, std::endian nbt_endian = std::endian::big, impl::swapper::read_swapper rswap = impl::swapper::default_read_swapper>
[[nodiscard]] auto read(std::span<std::byte, std::dynamic_extent> source) -> nbt_document
{
nbt_document doc{};
doc.impl_document = impl::read_write::read<in_place, bound_check, nbt_endian, rswap>(source.data(), source.size());
return doc;
}
} // namespace na::nbt