Skip to content

Commit

Permalink
Implement explicit serde array serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
Schuwi committed Jun 17, 2020
1 parent 761026c commit f820d92
Show file tree
Hide file tree
Showing 4 changed files with 274 additions and 32 deletions.
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ pub use value::Value;
pub use de::{from_gzip_reader, from_reader, from_zlib_reader};
#[cfg(feature = "serde")]
#[doc(inline)]
pub use ser::{i32_array, i64_array, i8_array};
#[cfg(feature = "serde")]
#[doc(inline)]
pub use ser::{to_gzip_writer, to_writer, to_zlib_writer};

mod blob;
Expand Down
36 changes: 36 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,39 @@ macro_rules! unrepresentable {
$(return_expr_for_serialized_types_helper!{Err(Error::UnrepresentableType(stringify!($type))), $type})*
};
}

/// Serde `serialize_with` implementation for array serialization.
///
/// This macro provides the function body for `i8_array`, `i32_array` and `i64_array`
/// in [`self::ser`], providing NBT `ByteArray`, `IntArray` and `LongArray`
/// serialization with serde.
macro_rules! array_serializer {
($func_name:literal, $arr: ident, $serializer: ident) => {{
use serde::ser::SerializeTupleStruct;
use std::borrow::Borrow;

let error = concat!(
$func_name,
" serializer may only be used with known-length collections"
);
let magic = concat!("__hematite_nbt_", $func_name, "__");

let mut iter = $arr.into_iter();
let (length, max_length) = iter.size_hint();

if max_length.is_none() || length != max_length.unwrap() {
return Err(SerError::custom(error));
}

let mut seq = $serializer.serialize_tuple_struct(magic, length)?;
for _i in 0..length {
seq.serialize_field(iter.next().ok_or(SerError::custom(error))?.borrow())?;
}

if iter.next().is_some() {
Err(SerError::custom(error))
} else {
seq.end()
}
}};
}
208 changes: 198 additions & 10 deletions src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use serde::ser;
use raw;

use error::{Error, Result};
use serde::ser::Error as SerError;

/// Encode `value` in Named Binary Tag format to the given `io::Write`
/// destination, with an optional header.
Expand Down Expand Up @@ -109,11 +110,15 @@ where
}
}

fn for_seq(outer: &'a mut Encoder<'b, W>, length: i32) -> Result<Self> {
// For an empty list, write TAG_End as the tag type.
if length == 0 {
raw::write_bare_byte(&mut outer.writer, 0x00)?;
raw::write_bare_int(&mut outer.writer, 0)?;
fn for_seq(outer: &'a mut Encoder<'b, W>, length: i32, array: bool) -> Result<Self> {
if length == 0 || array {
// Write sigil for empty list or typed array, because SerializeSeq::serialize_element is never called
if !array {
// For an empty list, write TAG_End as the tag type.
raw::write_bare_byte(&mut outer.writer, 0x00)?;
}
// Write list/array length
raw::write_bare_int(&mut outer.writer, length)?;
}
Ok(Compound {
outer,
Expand Down Expand Up @@ -150,6 +155,25 @@ where
}
}

impl<'a, 'b, W> ser::SerializeTupleStruct for Compound<'a, 'b, W>
where
W: io::Write,
{
type Ok = ();
type Error = Error;

fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
where
T: serde::Serialize,
{
value.serialize(&mut InnerEncoder::from_outer(self.outer))
}

fn end(self) -> Result<()> {
Ok(())
}
}

impl<'a, 'b, W> ser::SerializeStruct for Compound<'a, 'b, W>
where
W: io::Write,
Expand Down Expand Up @@ -268,14 +292,14 @@ where
type Error = Error;
type SerializeSeq = Compound<'a, 'b, W>;
type SerializeTuple = ser::Impossible<(), Error>;
type SerializeTupleStruct = ser::Impossible<(), Error>;
type SerializeTupleStruct = Compound<'a, 'b, W>;
type SerializeTupleVariant = ser::Impossible<(), Error>;
type SerializeMap = Compound<'a, 'b, W>;
type SerializeStruct = Compound<'a, 'b, W>;
type SerializeStructVariant = ser::Impossible<(), Error>;

unrepresentable!(
u8 u16 u32 u64 char unit unit_variant newtype_variant tuple tuple_struct
u8 u16 u32 u64 char unit unit_variant newtype_variant tuple
tuple_variant struct_variant
);

Expand Down Expand Up @@ -353,7 +377,7 @@ where
#[inline]
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
if let Some(l) = len {
Compound::for_seq(self.outer, l as i32)
Compound::for_seq(self.outer, l as i32, false)
} else {
Err(Error::UnrepresentableType("unsized list"))
}
Expand All @@ -368,6 +392,19 @@ where
fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
Ok(Compound::from_outer(self.outer))
}

fn serialize_tuple_struct(
self,
name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct> {
match name {
"__hematite_nbt_i8_array__"
| "__hematite_nbt_i32_array__"
| "__hematite_nbt_i64_array__" => Compound::for_seq(self.outer, len as i32, true),
_ => Err(Error::UnrepresentableType(stringify!(tuple_struct))),
}
}
}

/// A serializer for valid map keys, i.e. strings.
Expand Down Expand Up @@ -452,14 +489,14 @@ where
type Error = Error;
type SerializeSeq = NoOp;
type SerializeTuple = ser::Impossible<(), Error>;
type SerializeTupleStruct = ser::Impossible<(), Error>;
type SerializeTupleStruct = NoOp;
type SerializeTupleVariant = ser::Impossible<(), Error>;
type SerializeMap = NoOp;
type SerializeStruct = NoOp;
type SerializeStructVariant = ser::Impossible<(), Error>;

unrepresentable!(
u8 u16 u32 u64 char unit unit_variant newtype_variant tuple tuple_struct
u8 u16 u32 u64 char unit unit_variant newtype_variant tuple
tuple_variant struct_variant
);

Expand Down Expand Up @@ -555,6 +592,21 @@ where
self.write_header(0x0a)?;
Ok(NoOp)
}

fn serialize_tuple_struct(
self,
name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct> {
match name {
"__hematite_nbt_i8_array__" => self.write_header(0x07)?,
"__hematite_nbt_i32_array__" => self.write_header(0x0b)?,
"__hematite_nbt_i64_array__" => self.write_header(0x0c)?,
_ => return Err(Error::UnrepresentableType("tuple struct")),
}

Ok(NoOp)
}
}

/// This empty serializer provides a way to serialize only headers/tags for
Expand All @@ -577,6 +629,22 @@ impl ser::SerializeSeq for NoOp {
}
}

impl ser::SerializeTupleStruct for NoOp {
type Ok = ();
type Error = Error;

fn serialize_field<T: ?Sized>(&mut self, _value: &T) -> Result<()>
where
T: serde::Serialize,
{
Ok(())
}

fn end(self) -> Result<()> {
Ok(())
}
}

impl ser::SerializeStruct for NoOp {
type Ok = ();
type Error = Error;
Expand Down Expand Up @@ -615,3 +683,123 @@ impl ser::SerializeMap for NoOp {
Ok(())
}
}

/// This function provides serde serialization support for NBT type `ByteArray`.
///
/// It should be used in conjunction with serde's field annotation `serialize_with`.
/// In the following example `byte_data` will be serialized as a `ByteArray`
/// instead of a `List` of `Byte`s:
///
/// ```
/// extern crate serde;
/// use nbt::to_writer;
/// use serde::Serialize;
///
/// let mut serialized = Vec::new();
///
/// // Declare your struct
/// #[derive(Serialize)]
/// struct Sheep {
/// #[serde(serialize_with="nbt::i8_array")]
/// byte_data: Vec<i8>,
/// }
///
/// // Serialize to NBT!
/// to_writer(
/// &mut serialized,
/// &Sheep {
/// byte_data: vec![0x62, 0x69, 0x6E, 0x61, 0x72, 0x79, 0x20, 0x73, 0x68, 0x65, 0x65, 0x70],
/// },
/// None
/// ).unwrap();
///
/// print!("Serialized: {:?}", serialized);
/// ```
pub fn i8_array<'a, T, S>(array: &'a T, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
&'a T: IntoIterator,
<&'a T as IntoIterator>::Item: std::borrow::Borrow<i8>,
S: serde::ser::Serializer,
{
array_serializer!("i8_array", array, serializer)
}

/// This function provides serde serialization support for NBT type `IntArray`.
///
/// It should be used in conjunction with serde's field annotation `serialize_with`.
/// In the following example `int_data` will be serialized as an `IntArray`
/// instead of a `List` of `Int`s:
///
/// ```
/// extern crate serde;
/// use nbt::to_writer;
/// use serde::Serialize;
///
/// let mut serialized = Vec::new();
///
/// // Declare your struct
/// #[derive(Serialize)]
/// struct Cow {
/// #[serde(serialize_with="nbt::i32_array")]
/// int_data: Vec<i32>,
/// }
///
/// // Serialize to NBT!
/// to_writer(
/// &mut serialized,
/// &Cow {
/// int_data: vec![1, 8, 64, 512, 4096, 32768, 262144],
/// },
/// None
/// ).unwrap();
///
/// print!("Serialized: {:?}", serialized);
/// ```
pub fn i32_array<'a, T, S>(array: &'a T, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
&'a T: IntoIterator,
<&'a T as IntoIterator>::Item: std::borrow::Borrow<i32>,
S: serde::ser::Serializer,
{
array_serializer!("i32_array", array, serializer)
}

/// This function provides serde serialization support for NBT type `LongArray`.
///
/// It should be used in conjunction with serde's field annotation `serialize_with`.
/// In the following example `int_data` will be serialized as a `LongArray`
/// instead of a `List` of `Int`s:
///
/// ```
/// extern crate serde;
/// use nbt::to_writer;
/// use serde::Serialize;
///
/// let mut serialized = Vec::new();
///
/// // Declare your struct
/// #[derive(Serialize)]
/// struct Enderman {
/// #[serde(serialize_with="nbt::i64_array")]
/// long_data: Vec<i64>,
/// }
///
/// // Serialize to NBT!
/// to_writer(
/// &mut serialized,
/// &Enderman {
/// long_data: vec![0x1848ccd2157df10e, 0x64c5efff28280e9a],
/// },
/// None
/// ).unwrap();
///
/// print!("Serialized: {:?}", serialized);
/// ```
pub fn i64_array<'a, T, S>(array: &'a T, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
&'a T: IntoIterator,
<&'a T as IntoIterator>::Item: std::borrow::Borrow<i64>,
S: serde::ser::Serializer,
{
array_serializer!("i64_array", array, serializer)
}
Loading

0 comments on commit f820d92

Please sign in to comment.