Skip to content

Commit

Permalink
Make read functions return NamedTag with empty name when reading un…
Browse files Browse the repository at this point in the history
…named tags.
  • Loading branch information
shBLOCK committed Oct 9, 2024
1 parent 1f091fc commit 72c734b
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 117 deletions.
8 changes: 4 additions & 4 deletions src/amulet_nbt/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1599,7 +1599,7 @@ def read_nbt(
:param filepath_or_buffer: A string path to a file on disk, a bytes or memory view object containing the binary NBT or a file-like object to read the binary data from.
:param preset: The encoding preset. If this is defined little_endian and string_encoding have no effect.
:param named: If the tag to read is named.
:param named: If the tag to read is named, if not, return NamedTag with empty name.
:param read_offset: Optional ReadOffset object to get read end offset.
:raises: IndexError if the data is not long enough.
"""
Expand All @@ -1620,7 +1620,7 @@ def read_nbt(
:param compressed: Is the binary data gzip compressed.
:param little_endian: Are the numerical values stored as little endian. True for Bedrock, False for Java.
:param string_encoding: The bytes decoder function to parse strings. mutf8_encoding for Java, utf8_escape_encoding for Bedrock.
:param named: If the tag to read is named.
:param named: If the tag to read is named, if not, return NamedTag with empty name.
:param read_offset: Optional ReadOffset object to get read end offset.
:raises: IndexError if the data is not long enough.
"""
Expand All @@ -1639,7 +1639,7 @@ def read_nbt_array(
:param filepath_or_buffer: A string path to a file on disk, a bytes or memory view object containing the binary NBT or a file-like object to read the binary data from.
:param count: The number of binary NBT objects to read. Use -1 to exhaust the buffer.
:param preset: The encoding preset. If this is defined little_endian and string_encoding have no effect.
:param named: If the tags to read are named.
:param named: If the tags to read are named, if not, return NamedTags with empty name.
:param read_offset: Optional ReadOffset object to get read end offset.
:raises: IndexError if the data is not long enough.
"""
Expand All @@ -1662,7 +1662,7 @@ def read_nbt_array(
:param compressed: Is the binary data gzip compressed. This only supports the whole buffer compressed as one.
:param little_endian: Are the numerical values stored as little endian. True for Bedrock, False for Java.
:param string_encoding: The bytes decoder function to parse strings. mutf8.decode_modified_utf8 for Java, amulet_nbt.utf8_escape_decoder for Bedrock.
:param named: If the tags to read are named.
:param named: If the tags to read are named, if not, return NamedTags with empty name.
:param read_offset: Optional ReadOffset object to get read end offset.
:raises: IndexError if the data is not long enough.
"""
Expand Down
64 changes: 16 additions & 48 deletions src/amulet_nbt/cpp/nbt_encoding/binary/read_binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,78 +163,46 @@ inline AmuletNBT::TagNode read_node(AmuletNBT::BinaryReader& reader, std::uint8_


namespace AmuletNBT {
AmuletNBT::NamedTag read_nbt(AmuletNBT::BinaryReader& reader){
AmuletNBT::NamedTag read_nbt(AmuletNBT::BinaryReader& reader, bool named){
std::uint8_t tag_id = reader.readNumeric<std::uint8_t>();
std::string name = read_string_tag(reader);
std::string name = named ? read_string_tag(reader) : "";
AmuletNBT::TagNode node = read_node(reader, tag_id);
return AmuletNBT::NamedTag(name, node);
}

AmuletNBT::TagNode read_nbt_unnamed(AmuletNBT::BinaryReader& reader){
std::uint8_t tag_id = reader.readNumeric<std::uint8_t>();
return read_node(reader, tag_id);
}

// Read one named tag from the string at position offset.
AmuletNBT::NamedTag read_nbt(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, size_t& offset){
AmuletNBT::BinaryReader reader(raw, offset, endianness, string_decode);
return read_nbt(reader);
}

// Read one named tag from the string at position offset.
AmuletNBT::TagNode read_nbt_unnamed(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, size_t& offset){
// Read one (un)named tag from the string at position offset.
AmuletNBT::NamedTag read_nbt(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, bool named, size_t& offset){
AmuletNBT::BinaryReader reader(raw, offset, endianness, string_decode);
return read_nbt_unnamed(reader);
return read_nbt(reader, named);
}

// Read one named tag from the string.
AmuletNBT::NamedTag read_nbt(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode){
// Read one (un)named tag from the string.
AmuletNBT::NamedTag read_nbt(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, bool named){
size_t offset = 0;
return read_nbt(raw, endianness, string_decode, offset);
return read_nbt(raw, endianness, string_decode, named, offset);
}

// Read one named tag from the string.
AmuletNBT::TagNode read_nbt_unnamed(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode){
size_t offset = 0;
return read_nbt_unnamed(raw, endianness, string_decode, offset);
AmuletNBT::NamedTag read_nbt(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode){
return read_nbt(raw, endianness, string_decode, true);
}

// Read count named tags from the string at position offset.
std::vector<AmuletNBT::NamedTag> read_nbt_array(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, size_t& offset, size_t count){
// Read count (un)named tags from the string at position offset.
std::vector<AmuletNBT::NamedTag> read_nbt_array(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, bool named, size_t& offset, size_t count){
AmuletNBT::BinaryReader reader(raw, offset, endianness, string_decode);
std::vector<AmuletNBT::NamedTag> out;
for (size_t i = 0; i < count; i++){
out.push_back(read_nbt(reader));
}
return out;
}

// Read count named tags from the string at position offset.
std::vector<AmuletNBT::TagNode> read_nbt_array_unnamed(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, size_t& offset, size_t count){
AmuletNBT::BinaryReader reader(raw, offset, endianness, string_decode);
std::vector<AmuletNBT::TagNode> out;
for (size_t i = 0; i < count; i++){
out.push_back(read_nbt_unnamed(reader));
out.push_back(read_nbt(reader, named));
}
return out;
}

// Read all named tags from the string at position offset.
std::vector<AmuletNBT::NamedTag> read_nbt_array(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, size_t& offset){
// Read all (un)named tags from the string at position offset.
std::vector<AmuletNBT::NamedTag> read_nbt_array(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, bool named, size_t& offset){
AmuletNBT::BinaryReader reader(raw, offset, endianness, string_decode);
std::vector<AmuletNBT::NamedTag> out;
while (reader.has_more_data()){
out.push_back(read_nbt(reader));
}
return out;
}

// Read all named tags from the string at position offset.
std::vector<AmuletNBT::TagNode> read_nbt_array_unnamed(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, size_t& offset){
AmuletNBT::BinaryReader reader(raw, offset, endianness, string_decode);
std::vector<AmuletNBT::TagNode> out;
while (reader.has_more_data()){
out.push_back(read_nbt_unnamed(reader));
out.push_back(read_nbt(reader, named));
}
return out;
}
Expand Down
15 changes: 5 additions & 10 deletions src/amulet_nbt/include/amulet_nbt/nbt_encoding/binary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,12 @@
#include <amulet_nbt/string_encoding.hpp>

namespace AmuletNBT {
NamedTag read_nbt(BinaryReader& reader);
NamedTag read_nbt(const std::string&, std::endian, StringDecode, size_t& offset);
NamedTag read_nbt(BinaryReader& reader, bool named);
NamedTag read_nbt(const std::string&, std::endian, StringDecode, bool named, size_t& offset);
NamedTag read_nbt(const std::string&, std::endian, StringDecode, bool named);
NamedTag read_nbt(const std::string&, std::endian, StringDecode);
std::vector<NamedTag> read_nbt_array(const std::string&, std::endian, StringDecode, size_t& offset);
std::vector<NamedTag> read_nbt_array(const std::string&, std::endian, StringDecode, size_t& offset, size_t count);

TagNode read_nbt_unnamed(BinaryReader& reader);
TagNode read_nbt_unnamed(const std::string&, std::endian, StringDecode, size_t& offset);
TagNode read_nbt_unnamed(const std::string&, std::endian, StringDecode);
std::vector<TagNode> read_nbt_array_unnamed(const std::string&, std::endian, StringDecode, size_t& offset);
std::vector<TagNode> read_nbt_array_unnamed(const std::string&, std::endian, StringDecode, size_t& offset, size_t count);
std::vector<NamedTag> read_nbt_array(const std::string&, std::endian, StringDecode, bool named, size_t& offset);
std::vector<NamedTag> read_nbt_array(const std::string&, std::endian, StringDecode, bool named, size_t& offset, size_t count);

void write_nbt(BinaryWriter&, const std::optional<std::string>& name, const ByteTag&);
void write_nbt(BinaryWriter&, const std::optional<std::string>& name, const ShortTag&);
Expand Down
78 changes: 25 additions & 53 deletions src/amulet_nbt/pybind/bnbt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,41 +82,31 @@ void init_bnbt(py::module& m) {
return data;
};

typedef std::variant<AmuletNBT::TagNode, AmuletNBT::NamedTag> NodeOrNamedTag;
typedef std::variant<std::vector<AmuletNBT::TagNode>, std::vector<AmuletNBT::NamedTag>> NodeOrNamedTagVector;

auto read_nbt = [get_buffer](
py::object filepath_or_buffer,
bool compressed,
std::endian endianness,
AmuletNBT::StringDecode string_decoder,
bool named,
py::object read_offset_py
) -> NodeOrNamedTag {
) {
std::string buffer = get_buffer(filepath_or_buffer, compressed);
if (py::isinstance<AmuletNBT::ReadOffset>(read_offset_py)){
AmuletNBT::ReadOffset& read_offset = read_offset_py.cast<AmuletNBT::ReadOffset&>();
return named ? NodeOrNamedTag(AmuletNBT::read_nbt(
buffer,
endianness,
string_decoder,
read_offset.offset
)) : NodeOrNamedTag(AmuletNBT::read_nbt_unnamed(
return AmuletNBT::read_nbt(
buffer,
endianness,
string_decoder,
named,
read_offset.offset
));
);
} else if (read_offset_py.is(py::none())){
return named ? NodeOrNamedTag(AmuletNBT::read_nbt(
return AmuletNBT::read_nbt(
buffer,
endianness,
string_decoder
)) : NodeOrNamedTag(AmuletNBT::read_nbt_unnamed(
buffer,
endianness,
string_decoder
));
string_decoder,
named
);
} else {
throw std::invalid_argument("read_offset must be ReadOffset or None");
}
Expand Down Expand Up @@ -149,7 +139,7 @@ void init_bnbt(py::module& m) {
"\n"
":param filepath_or_buffer: A string path to a file on disk, a bytes or memory view object containing the binary NBT or a file-like object to read the binary data from.\n"
":param preset: The encoding preset. If this is defined little_endian and string_encoding have no effect.\n"
":param named: If the tag to read is named.\n"
":param named: If the tag to read is named, if not, return NamedTag with empty name.\n"
":param read_offset: Optional ReadOffset object to get read end offset.\n"
":raises: IndexError if the data is not long enough."
)
Expand Down Expand Up @@ -187,7 +177,7 @@ void init_bnbt(py::module& m) {
":param compressed: Is the binary data gzip compressed.\n"
":param little_endian: Are the numerical values stored as little endian. True for Bedrock, False for Java.\n"
":param string_encoding: The bytes decoder function to parse strings. mutf8_encoding for Java, utf8_escape_encoding for Bedrock.\n"
":param named: If the tag to read is named.\n"
":param named: If the tag to read is named, if not, return NamedTag with empty name.\n"
":param read_offset: Optional ReadOffset object to get read end offset.\n"
":raises: IndexError if the data is not long enough."
)
Expand All @@ -201,68 +191,50 @@ void init_bnbt(py::module& m) {
AmuletNBT::StringDecode string_decoder,
bool named,
py::object read_offset_py
) -> NodeOrNamedTagVector {
) {
if (count < -1){
throw std::invalid_argument("count must be -1 or higher");
}
std::string buffer = get_buffer(filepath_or_buffer, compressed);
if (py::isinstance<AmuletNBT::ReadOffset>(read_offset_py)){
AmuletNBT::ReadOffset& read_offset = read_offset_py.cast<AmuletNBT::ReadOffset&>();
if (count == -1){
return named ? NodeOrNamedTagVector(AmuletNBT::read_nbt_array(
buffer,
endianness,
string_decoder,
read_offset.offset
)) : NodeOrNamedTagVector(AmuletNBT::read_nbt_array(
return AmuletNBT::read_nbt_array(
buffer,
endianness,
string_decoder,
named,
read_offset.offset
));
);
} else {
return named ? NodeOrNamedTagVector(AmuletNBT::read_nbt_array(
return AmuletNBT::read_nbt_array(
buffer,
endianness,
string_decoder,
named,
read_offset.offset,
count
)) : NodeOrNamedTagVector(AmuletNBT::read_nbt_array_unnamed(
buffer,
endianness,
string_decoder,
read_offset.offset,
count
));
);
}
} else if (read_offset_py.is(py::none())){
size_t offset = 0;
if (count == -1){
return named ? NodeOrNamedTagVector(AmuletNBT::read_nbt_array(
buffer,
endianness,
string_decoder,
offset
)) : NodeOrNamedTagVector(AmuletNBT::read_nbt_array_unnamed(
return AmuletNBT::read_nbt_array(
buffer,
endianness,
string_decoder,
named,
offset
));
);
} else {
return named ? NodeOrNamedTagVector(AmuletNBT::read_nbt_array(
buffer,
endianness,
string_decoder,
offset,
count
)) : NodeOrNamedTagVector(AmuletNBT::read_nbt_array_unnamed(
return AmuletNBT::read_nbt_array(
buffer,
endianness,
string_decoder,
named,
offset,
count
));
);
}
} else {
throw std::invalid_argument("read_offset must be ReadOffset or None");
Expand Down Expand Up @@ -299,7 +271,7 @@ void init_bnbt(py::module& m) {
":param filepath_or_buffer: A string path to a file on disk, a bytes or memory view object containing the binary NBT or a file-like object to read the binary data from.\n"
":param count: The number of binary NBT objects to read. Use -1 to exhaust the buffer.\n"
":param preset: The encoding preset. If this is defined little_endian and string_encoding have no effect.\n"
":param named: If the tags to read are named.\n"
":param named: If the tags to read are named, if not, return NamedTags with empty name.\n"
":param read_offset: Optional ReadOffset object to get read end offset.\n"
":raises: IndexError if the data is not long enough."
)
Expand Down Expand Up @@ -342,7 +314,7 @@ void init_bnbt(py::module& m) {
":param compressed: Is the binary data gzip compressed. This only supports the whole buffer compressed as one.\n"
":param little_endian: Are the numerical values stored as little endian. True for Bedrock, False for Java.\n"
":param string_encoding: The bytes decoder function to parse strings. mutf8.decode_modified_utf8 for Java, amulet_nbt.utf8_escape_decoder for Bedrock.\n"
":param named: If the tags to read are named.\n"
":param named: If the tags to read are named, if not, return NamedTags with empty name.\n"
":param read_offset: Optional ReadOffset object to get read end offset.\n"
":raises: IndexError if the data is not long enough."
)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_nbt.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ def test_unnamed(self) -> None:
# Only one case is tested as the implementation of this is shared among all tag types and thus behaves the same
self.assertEqual(
amulet_nbt.read_nbt(b"\x01\x05", named=False, compressed=False, little_endian=False),
amulet_nbt.ByteTag(5),
amulet_nbt.NamedTag(amulet_nbt.ByteTag(5), ""),
"reading unnamed tag"
)
self.assertEqual(
amulet_nbt.read_nbt_array(b"\x01\x05\x01\x06\x01\x07", named=False, count=-1, compressed=False, little_endian=False),
[amulet_nbt.ByteTag(i) for i in (5, 6, 7)],
[amulet_nbt.NamedTag(amulet_nbt.ByteTag(i), "") for i in (5, 6, 7)],
"reading unnamed tag array"
)
self.assertEqual(
Expand Down

0 comments on commit 72c734b

Please sign in to comment.