diff --git a/src/ser.rs b/src/ser.rs index 4fd2ad1..36cea69 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -119,7 +119,7 @@ impl<'a, 'b, W> ser::SerializeSeq for Compound<'a, 'b, W> where T: serde::Serialize { if !self.sigil { - value.serialize(&mut TagEncoder::from_outer(self.outer, Option::::None))?; + value.serialize(&mut TagEncoder::::for_seq_sigil(self.outer))?; raw::write_bare_int(&mut self.outer.writer, self.length)?; self.sigil = true; } @@ -141,7 +141,7 @@ impl<'a, 'b, W> ser::SerializeStruct for Compound<'a, 'b, W> -> Result<()> where T: serde::Serialize { - value.serialize(&mut TagEncoder::from_outer(self.outer, Some(key)))?; + value.serialize(&mut TagEncoder::from_outer(self.outer, key))?; value.serialize(&mut InnerEncoder::from_outer(self.outer)) } @@ -172,7 +172,7 @@ impl<'a, 'b, W> ser::SerializeMap for Compound<'a, 'b, W> where K: serde::Serialize, V: serde::Serialize, { - value.serialize(&mut TagEncoder::from_outer(self.outer, Some(key)))?; + value.serialize(&mut TagEncoder::from_outer(self.outer, key))?; value.serialize(&mut InnerEncoder::from_outer(self.outer)) } @@ -350,18 +350,18 @@ impl<'a, 'b, W> serde::Serializer for &'a mut InnerEncoder<'a, 'b, W> where W: i } } -/// A serializer for valid map keys, i.e. strings. -struct MapKeyEncoder<'a, 'b: 'a, W: 'a> { +/// A serializer for valid tag names, i.e. strings. +struct TagNameEncoder<'a, 'b: 'a, W: 'a> { outer: &'a mut Encoder<'b, W>, } -impl<'a, 'b: 'a, W: 'a> MapKeyEncoder<'a, 'b, W> where W: io::Write { +impl<'a, 'b: 'a, W: 'a> TagNameEncoder<'a, 'b, W> where W: io::Write { pub fn from_outer(outer: &'a mut Encoder<'b, W>) -> Self { - MapKeyEncoder { outer: outer } + TagNameEncoder { outer: outer } } } -impl<'a, 'b: 'a, W: 'a> serde::Serializer for &'a mut MapKeyEncoder<'a, 'b, W> +impl<'a, 'b: 'a, W: 'a> serde::Serializer for &'a mut TagNameEncoder<'a, 'b, W> where W: io::Write { type Ok = (); @@ -395,26 +395,76 @@ impl<'a, 'b: 'a, W: 'a> serde::Serializer for &'a mut MapKeyEncoder<'a, 'b, W> } } -/// A serializer for valid map keys. +enum TagType { + Normal(Option), + SeqTag(Option), + SeqSigil, +} + +impl TagType { + fn take_key(&mut self) -> Option { + match self { + TagType::Normal(key) + | TagType::SeqTag(key) => key.take(), + TagType::SeqSigil => None, + } + } +} + +/// A serializer for valid tags. struct TagEncoder<'a, 'b: 'a, W: 'a, K> { outer: &'a mut Encoder<'b, W>, - key: Option, + tag_type: TagType, } impl<'a, 'b: 'a, W: 'a, K> TagEncoder<'a, 'b, W, K> where W: io::Write, K: serde::Serialize { - fn from_outer(outer: &'a mut Encoder<'b, W>, key: Option) -> Self { - TagEncoder { - outer: outer, key: key + fn from_outer(outer: &'a mut Encoder<'b, W>, key: K) -> Self { + Self { + outer, + tag_type: TagType::Normal(Some(key)), + } + } + + fn for_seq_tag(outer: &'a mut Encoder<'b, W>, key: Option) -> Self { + Self { + outer, + tag_type: TagType::SeqTag(key), + } + } + + fn for_seq_sigil(outer: &'a mut Encoder<'b, W>) -> Self { + Self { + outer, + tag_type: TagType::SeqSigil, } } fn write_header(&mut self, tag: i8) -> Result<()> { - use serde::Serialize; + let tag = match self.tag_type { + TagType::Normal( .. ) => tag, + TagType::SeqTag( .. ) => match tag { + 0x01 => 0x07, // Byte array + 0x03 => 0x0b, // Integer array + 0x04 => 0x0c, // Long array + _ => 0x09, // List + }, + TagType::SeqSigil => match tag { + // Don't write headers in byte/integer/long arrays + 0x01 | 0x03 | 0x04 => return Ok(()), + _ => tag, + }, + }; + raw::write_bare_byte(&mut self.outer.writer, tag)?; - self.key.serialize(&mut MapKeyEncoder::from_outer(self.outer)) + + if let Some(key) = self.tag_type.take_key() { + key.serialize(&mut TagNameEncoder::from_outer(self.outer))?; + } + + Ok(()) } } @@ -424,7 +474,7 @@ where W: io::Write, { type Ok = (); type Error = Error; - type SerializeSeq = NoOp; + type SerializeSeq = SequenceTagEncoder<'a, 'b, W, K>; type SerializeTuple = ser::Impossible<(), Error>; type SerializeTupleStruct = ser::Impossible<(), Error>; type SerializeTupleVariant = ser::Impossible<(), Error>; @@ -509,9 +559,18 @@ where W: io::Write, #[inline] fn serialize_seq(self, len: Option) -> Result { - if len.is_some() { - self.write_header(0x09)?; - Ok(NoOp) + if let Some(len) = len { + let key = match (&mut self.tag_type, len) { + (TagType::SeqTag( .. ), _) + | (TagType::Normal( .. ), 0) => { + self.write_header(0x09)?; + return Ok(SequenceTagEncoder::for_empty_sequence()); + }, + (TagType::SeqSigil, _) => None, + | (TagType::Normal(key), _) => key.take(), + }; + + Ok(SequenceTagEncoder::for_non_empty_sequence(self.outer, key)) } else { Err(Error::UnrepresentableType("unsized list")) } @@ -532,17 +591,53 @@ where W: io::Write, } } -/// This empty serializer provides a way to serialize only headers/tags for -/// sequences, maps, and structs. -struct NoOp; +/// This serializer writes the tag (list/array tag, (key)) for non-empty sequences. +/// This infers the sequence element type from the first element written to the sequence +/// to serialize i8, i32 or i64 sequences as ByteArray, IntegerArray or LongArray +/// NBT types respectively. +/// For every other sequence element type a normal NBT List tag is written. +struct SequenceTagEncoder<'a, 'b: 'a, W: 'a, K> { + encoder: Option<&'a mut Encoder<'b, W>>, + key: Option, +} + +impl<'a, 'b: 'a, W: 'a, K> SequenceTagEncoder<'a, 'b, W, K> + where + W: io::Write, + K: serde::Serialize, +{ + fn for_empty_sequence() -> Self { + Self { + encoder: None, + key: None, + } + } + + fn for_non_empty_sequence(encoder: &'a mut Encoder<'b, W>, key: Option) -> Self { + Self { + encoder: Some(encoder), + key, + } + } +} -impl ser::SerializeSeq for NoOp { +impl<'a, 'b: 'a, W: 'a, K> ser::SerializeSeq for SequenceTagEncoder<'a, 'b, W, K> + where + W: io::Write, + K: serde::Serialize, +{ type Ok = (); type Error = Error; - fn serialize_element(&mut self, _value: &T) -> Result<()> - where T: serde::Serialize + fn serialize_element(&mut self, value: &T) -> Result<()> + where + T: serde::Serialize, { + if let Some(encoder) = self.encoder.take() { + // Write type-dependant sequence tag + value.serialize(&mut TagEncoder::for_seq_tag(encoder, self.key.take()))?; + } + Ok(()) } @@ -551,6 +646,10 @@ impl ser::SerializeSeq for NoOp { } } +/// This empty serializer provides a way to serialize only headers/tags for +/// sequences, maps, and structs. +struct NoOp; + impl ser::SerializeStruct for NoOp { type Ok = (); type Error = Error; diff --git a/tests/serde_basics.rs b/tests/serde_basics.rs index 38666f9..85a7d39 100644 --- a/tests/serde_basics.rs +++ b/tests/serde_basics.rs @@ -169,7 +169,7 @@ struct NestedArrayNbt { } #[test] -fn deserialize_nested_array() { +fn roundtrip_nested_array() { let nbt = NestedArrayNbt { data: vec!(vec!(1, 2), vec!(3, 4)) }; let bytes = vec![ @@ -191,13 +191,17 @@ fn deserialize_nested_array() { 0x00 ]; - let read: NestedArrayNbt = from_reader(&bytes[..]).unwrap(); - assert_eq!(read, nbt) + assert_roundtrip_eq(nbt, &bytes, None); +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct ByteArrayNbt { + data: Vec, } #[test] -fn deserialize_byte_array() { - let nbt = BasicListNbt { data: vec![1, 2, 3] }; +fn roundtrip_byte_array() { + let nbt = ByteArrayNbt { data: vec![1, 2, 3] }; let bytes = vec![ 0x0a, @@ -210,8 +214,7 @@ fn deserialize_byte_array() { 0x00 ]; - let read: BasicListNbt = from_reader(&bytes[..]).unwrap(); - assert_eq!(read, nbt) + assert_roundtrip_eq(nbt, &bytes, None); } #[derive(Debug, PartialEq, Serialize, Deserialize)] @@ -238,7 +241,7 @@ fn deserialize_empty_array() { } #[test] -fn deserialize_int_array() { +fn roundtrip_int_array() { let nbt = IntListNbt { data: vec![1, 2, 3] }; let bytes = vec![ @@ -255,8 +258,7 @@ fn deserialize_int_array() { 0x00 ]; - let read: IntListNbt = from_reader(&bytes[..]).unwrap(); - assert_eq!(read, nbt) + assert_roundtrip_eq(nbt, &bytes, None); } #[derive(Debug, PartialEq, Serialize, Deserialize)] @@ -265,7 +267,7 @@ struct LongListNbt { } #[test] -fn deserialize_long_array() { +fn roundtrip_long_array() { let nbt = LongListNbt { data: vec![1, 2, 3] }; let bytes = vec![ @@ -282,8 +284,7 @@ fn deserialize_long_array() { 0x00 ]; - let read: LongListNbt = from_reader(&bytes[..]).unwrap(); - assert_eq!(read, nbt) + assert_roundtrip_eq(nbt, &bytes, None); } #[derive(Debug, PartialEq, Serialize, Deserialize)]