From c9aeda7dbd44bb7d4a3aaaeddb425b4ffa0459cb Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Thu, 29 Dec 2022 18:10:29 -0800 Subject: [PATCH] Support NBT lists of type TAG_End (#181) In NBT, lists are allowed to have the element type TAG_End iff their length is zero. This adds an explicit `List::End` enum variant to valence_nbt. --- crates/valence_nbt/src/from_binary_slice.rs | 2 +- crates/valence_nbt/src/tests.rs | 1 + crates/valence_nbt/src/to_binary_writer.rs | 37 ++++++++++++--------- crates/valence_nbt/src/value.rs | 3 ++ 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/crates/valence_nbt/src/from_binary_slice.rs b/crates/valence_nbt/src/from_binary_slice.rs index ab0b66e..be62bf9 100644 --- a/crates/valence_nbt/src/from_binary_slice.rs +++ b/crates/valence_nbt/src/from_binary_slice.rs @@ -158,7 +158,7 @@ impl DecodeState<'_, '_> { fn read_any_list(&mut self) -> Result { match self.read_tag()? { Tag::End => match self.read_int()? { - 0 => Ok(List::Byte(vec![])), + 0 => Ok(List::End), len => Err(Error::new_owned(format!( "TAG_End list with nonzero length of {len}" ))), diff --git a/crates/valence_nbt/src/tests.rs b/crates/valence_nbt/src/tests.rs index ed46b52..ee93bc7 100644 --- a/crates/valence_nbt/src/tests.rs +++ b/crates/valence_nbt/src/tests.rs @@ -130,6 +130,7 @@ fn example_compound() -> Compound { "bar".to_owned(), "baz".to_owned() ]), + "list_of_end" => List::End, "string" => "aé日", "compound" => inner(), "list_of_compound" => List::Compound(vec![ diff --git a/crates/valence_nbt/src/to_binary_writer.rs b/crates/valence_nbt/src/to_binary_writer.rs index 7344c7b..9003ad0 100644 --- a/crates/valence_nbt/src/to_binary_writer.rs +++ b/crates/valence_nbt/src/to_binary_writer.rs @@ -23,7 +23,7 @@ pub fn to_binary_writer(writer: W, compound: &Compound, root_name: &st } pub(crate) fn written_size(compound: &Compound, root_name: &str) -> usize { - fn value_len(val: &Value) -> usize { + fn value_size(val: &Value) -> usize { match val { Value::Byte(_) => 1, Value::Short(_) => 2, @@ -32,16 +32,17 @@ pub(crate) fn written_size(compound: &Compound, root_name: &str) -> usize { Value::Float(_) => 4, Value::Double(_) => 8, Value::ByteArray(ba) => 4 + ba.len(), - Value::String(s) => string_len(s), - Value::List(l) => list_len(l), - Value::Compound(c) => compound_len(c), + Value::String(s) => string_size(s), + Value::List(l) => list_size(l), + Value::Compound(c) => compound_size(c), Value::IntArray(ia) => 4 + ia.len() * 4, Value::LongArray(la) => 4 + la.len() * 8, } } - fn list_len(l: &List) -> usize { - let elems_len = match l { + fn list_size(l: &List) -> usize { + let elems_size = match l { + List::End => 0, List::Byte(b) => b.len(), List::Short(s) => s.len() * 2, List::Int(i) => i.len() * 4, @@ -49,28 +50,28 @@ pub(crate) fn written_size(compound: &Compound, root_name: &str) -> usize { List::Float(f) => f.len() * 4, List::Double(d) => d.len() * 8, List::ByteArray(ba) => ba.iter().map(|b| 4 + b.len()).sum(), - List::String(s) => s.iter().map(|s| string_len(s)).sum(), - List::List(l) => l.iter().map(list_len).sum(), - List::Compound(c) => c.iter().map(compound_len).sum(), + List::String(s) => s.iter().map(|s| string_size(s)).sum(), + List::List(l) => l.iter().map(list_size).sum(), + List::Compound(c) => c.iter().map(compound_size).sum(), List::IntArray(i) => i.iter().map(|i| 4 + i.len() * 4).sum(), List::LongArray(l) => l.iter().map(|l| 4 + l.len() * 8).sum(), }; - 1 + 4 + elems_len + 1 + 4 + elems_size } - fn string_len(s: &str) -> usize { + fn string_size(s: &str) -> usize { 2 + modified_utf8::encoded_len(s) } - fn compound_len(c: &Compound) -> usize { + fn compound_size(c: &Compound) -> usize { c.iter() - .map(|(k, v)| 1 + string_len(k) + value_len(v)) + .map(|(k, v)| 1 + string_size(k) + value_size(v)) .sum::() + 1 } - 1 + string_len(root_name) + compound_len(compound) + 1 + string_size(root_name) + compound_size(compound) } struct EncodeState { @@ -166,6 +167,12 @@ impl EncodeState { fn write_any_list(&mut self, list: &List) -> Result<()> { match list { + List::End => { + self.write_tag(Tag::End)?; + // Length + self.writer.write_i32::(0)?; + Ok(()) + } List::Byte(bl) => { self.write_tag(Tag::Byte)?; @@ -204,7 +211,7 @@ impl EncodeState { } } - fn write_list(&mut self, list: &Vec, elem_type: Tag, mut write_elem: F) -> Result<()> + fn write_list(&mut self, list: &[T], elem_type: Tag, mut write_elem: F) -> Result<()> where F: FnMut(&mut Self, &T) -> Result<()>, { diff --git a/crates/valence_nbt/src/value.rs b/crates/valence_nbt/src/value.rs index 663ba9e..07d91d3 100644 --- a/crates/valence_nbt/src/value.rs +++ b/crates/valence_nbt/src/value.rs @@ -33,6 +33,8 @@ pub enum Value { /// heterogeneous lists are unrepresentable. #[derive(Clone, PartialEq, Debug)] pub enum List { + /// The list with the element type of `TAG_End` and length of zero. + End, Byte(Vec), Short(Vec), Int(Vec), @@ -51,6 +53,7 @@ impl List { /// Returns the length of this list. pub fn len(&self) -> usize { match self { + List::End => 0, List::Byte(l) => l.len(), List::Short(l) => l.len(), List::Int(l) => l.len(),