Skip to content

Commit

Permalink
Merge pull request #115 from devwckd/feat/network-nbt-serialization
Browse files Browse the repository at this point in the history
feat(fastnbt): add network nbt to serializer
  • Loading branch information
owengage authored May 12, 2024
2 parents 42bcf6b + 442357e commit 08e7abb
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 7 deletions.
25 changes: 24 additions & 1 deletion fastnbt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,20 @@ pub fn to_writer<T: Serialize, W: Write>(writer: W, v: &T) -> Result<()> {
}

/// Options for customizing serialization.
#[derive(Default, Clone)]
#[derive(Clone)]
pub struct SerOpts {
root_name: String,
/// Whether to include the root compound name.
serialize_root_name: bool,
}

impl Default for SerOpts {
fn default() -> Self {
Self {
root_name: Default::default(),
serialize_root_name: true,
}
}
}

impl SerOpts {
Expand All @@ -264,11 +275,21 @@ impl SerOpts {
Default::default()
}

pub fn network_nbt() -> Self {
Self::new().serialize_root_compound_name(false)
}

pub fn serialize_root_compound_name(mut self, serialize_root_name: bool) -> Self {
self.serialize_root_name = serialize_root_name;
self
}

/// Set the root name (top level) of the compound. In most Minecraft data
/// structures this is the empty string. The [`ser`][`crate::ser`] module
/// contains an example.
pub fn root_name(mut self, root_name: impl Into<String>) -> Self {
self.root_name = root_name.into();
self.serialize_root_name = true;
self
}
}
Expand All @@ -281,6 +302,7 @@ pub fn to_bytes_with_opts<T: Serialize>(v: &T, opts: SerOpts) -> Result<Vec<u8>>
let mut serializer = Serializer {
writer: &mut result,
root_name: opts.root_name,
serialize_root_name: opts.serialize_root_name,
};
v.serialize(&mut serializer)?;
Ok(result)
Expand All @@ -293,6 +315,7 @@ pub fn to_writer_with_opts<T: Serialize, W: Write>(writer: W, v: &T, opts: SerOp
let mut serializer = Serializer {
writer,
root_name: opts.root_name,
serialize_root_name: opts.serialize_root_name,
};
v.serialize(&mut serializer)?;
Ok(())
Expand Down
12 changes: 9 additions & 3 deletions fastnbt/src/ser/serializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use super::{
enum DelayedHeader {
List { len: usize }, // header for a list, so element tag and list size.
MapEntry { outer_name: Vec<u8> }, // header for a compound, so tag, name of compound.
Root { root_name: String }, // root compound, special because it isn't allowed to be an array type. Must be compound.
Root { root_name: Option<String> }, // root compound, special because it isn't allowed to be an array type. Must be compound.
}

pub struct Serializer<W: Write> {
Expand All @@ -27,6 +27,7 @@ pub struct Serializer<W: Write> {
// Desired name of the root compound, typically an empty string.
// NOTE: This is `mem:take`en, so is only valid at the start of serialization!
pub(crate) root_name: String,
pub(crate) serialize_root_name: bool,
}

macro_rules! no_root {
Expand Down Expand Up @@ -69,10 +70,13 @@ impl<'a, W: 'a + Write> serde::ser::Serializer for &'a mut Serializer<W> {
// Take the root name to avoid a clone. Need to be careful not to use
// self.root_name elsewhere.
let root_name = mem::take(&mut self.root_name);
let serialize_root_name = mem::take(&mut self.serialize_root_name);
Ok(SerializerMap {
ser: self,
key: None,
header: Some(DelayedHeader::Root { root_name }),
header: Some(DelayedHeader::Root {
root_name: serialize_root_name.then_some(root_name),
}),
trailer: Some(Tag::End),
})
}
Expand Down Expand Up @@ -143,7 +147,9 @@ fn write_header(writer: &mut impl Write, header: DelayedHeader, actual_tag: Tag)
return Err(Error::no_root_compound());
}
writer.write_tag(Tag::Compound)?;
writer.write_size_prefixed_str(&outer_name)?;
if let Some(outer_name) = &outer_name {
writer.write_size_prefixed_str(&outer_name)?;
}
}
DelayedHeader::MapEntry { ref outer_name } => {
writer.write_tag(actual_tag)?;
Expand Down
29 changes: 26 additions & 3 deletions fastnbt/src/test/ser.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::{collections::HashMap, io::Cursor, iter::FromIterator};

use crate::{
borrow, from_bytes,
borrow, from_bytes, from_bytes_with_opts,
test::{resources::CHUNK_RAW_WITH_ENTITIES, Single, Wrap},
to_bytes, to_bytes_with_opts, to_writer_with_opts, ByteArray, IntArray, LongArray, SerOpts,
Tag, Value,
to_bytes, to_bytes_with_opts, to_writer_with_opts, ByteArray, DeOpts, IntArray, LongArray,
SerOpts, Tag, Value,
};
use serde::{ser::SerializeMap, Deserialize, Serialize};
use serde_bytes::{ByteBuf, Bytes};
Expand Down Expand Up @@ -936,3 +936,26 @@ fn serialize_root_with_name() {
assert_eq!(actual_via_writer.into_inner(), expected);
assert_eq!(actual_value, expected);
}

#[test]
fn serialize_networked_compound() {
#[derive(Serialize)]
struct Example {
networked: String,
}

let data = Example {
networked: "compound".to_string(),
};
let bytes: Vec<u8> = to_bytes_with_opts(&data, SerOpts::network_nbt()).unwrap();

assert_eq!(
bytes,
b"\
\x0a\
\x08\
\x00\x09networked\
\x00\x08compound\
\x00"
);
}

0 comments on commit 08e7abb

Please sign in to comment.