Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add nbt::from_slice and zero copy support for strings #63

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 29 additions & 8 deletions src/blob.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::Map;
use crate::{
raw::{Read, SliceRead},
Map,
};
use std::fmt;
use std::io;
use std::ops::Index;
Expand All @@ -16,7 +19,7 @@ use value::Value;
///
/// This is essentially a map of names to `Value`s, with an optional top-level
/// name of its own. It can be created in a similar way to a `HashMap`, or read
/// from an `io::Read` source, and its binary representation can be written to
/// from an `io::Read` or `&[u8]` source, and its binary representation can be written to
/// an `io::Write` destination.
///
/// These read and write methods support both uncompressed and compressed
Expand Down Expand Up @@ -61,28 +64,46 @@ impl Blob {
}
}

/// Extracts an `Blob` object from an `io::Read` source.
pub fn from_reader<R>(src: &mut R) -> Result<Blob>
/// Extracts an `Blob` object from an `Read` source.
fn from_trait<'de, R>(src: &mut R) -> Result<Blob>
where
R: io::Read,
R: Read<'de>,
{
let (tag, title) = raw::emit_next_header(src)?;
let (tag, title) = src.emit_next_header(None)?;
// Although it would be possible to read NBT format files composed of
// arbitrary objects using the current API, by convention all files
// have a top-level Compound.
if tag != 0x0a {
return Err(Error::NoRootCompound);
}
let content = Value::from_reader(tag, src)?;
let content = Value::from_trait(tag, src)?;
match content {
Value::Compound(map) => Ok(Blob {
title,
title: title.into_owned(),
content: map,
}),
_ => Err(Error::NoRootCompound),
}
}

/// Extracts an `Blob` object from an `&[u8]` source.
pub fn from_slice<'de, R>(src: &'de [u8]) -> Result<(&'de [u8], Blob)>
where
R: Read<'de>,
{
let mut slice_read = SliceRead::new(src);
let res = Self::from_trait(&mut slice_read)?;
Ok((slice_read.get_inner(), res))
}

/// Extracts an `Blob` object from an `io::Read` source.
pub fn from_reader<R>(src: &mut R) -> Result<Blob>
where
R: io::Read,
{
Self::from_trait(src)
}

/// Extracts an `Blob` object from an `io::Read` source that is
/// compressed using the Gzip format.
pub fn from_gzip_reader<R>(src: &mut R) -> Result<Blob>
Expand Down
105 changes: 68 additions & 37 deletions src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,24 @@ use std::io;
use flate2::read;
use serde::de;

use raw;

use error::{Error, Result};

use crate::raw::{Read, Reference, SliceRead};

/// Decode an object from Named Binary Tag (NBT) format.
///
/// Note that only maps and structs can be decoded, because the NBT format does
/// not support bare types. Other types will return `Error::NoRootCompound`.
pub fn from_slice<'a, T>(src: &'a [u8]) -> Result<(&'a [u8], T)>
where
T: de::Deserialize<'a>,
{
let src = SliceRead::new(src);
let mut decoder = Decoder::new(src);
let res = de::Deserialize::deserialize(&mut decoder)?;
Ok((decoder.reader.get_inner(), res))
}

/// Decode an object from Named Binary Tag (NBT) format.
///
/// Note that only maps and structs can be decoded, because the NBT format does
Expand Down Expand Up @@ -54,19 +68,20 @@ where
/// not support bare types. Other types will return `Error::NoRootCompound`.
pub struct Decoder<R> {
reader: R,
scratch: Vec<u8>,
}

impl<R> Decoder<R>
where
R: io::Read,
{
/// Create an NBT Decoder from a given `io::Read` source.
impl<R> Decoder<R> {
/// Create an NBT Decoder from a given source.
pub fn new(src: R) -> Self {
Decoder { reader: src }
Decoder {
reader: src,
scratch: Vec::new(),
}
}
}

impl<'de: 'a, 'a, R: io::Read> de::Deserializer<'de> for &'a mut Decoder<R> {
impl<'de: 'a, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Decoder<R> {
type Error = Error;

fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value>
Expand Down Expand Up @@ -110,7 +125,7 @@ impl<'de: 'a, 'a, R: io::Read> de::Deserializer<'de> for &'a mut Decoder<R> {
V: de::Visitor<'de>,
{
// Ignore the header (if there is one).
let (tag, _) = raw::emit_next_header(&mut self.reader)?;
let (tag, _) = self.reader.emit_next_header(Some(&mut self.scratch))?;

match tag {
0x0a => visitor.visit_map(MapDecoder::new(self)),
Expand All @@ -125,28 +140,25 @@ impl<'de: 'a, 'a, R: io::Read> de::Deserializer<'de> for &'a mut Decoder<R> {
}

/// Decoder for map-like types.
struct MapDecoder<'a, R: io::Read + 'a> {
struct MapDecoder<'a, R: 'a> {
outer: &'a mut Decoder<R>,
tag: Option<u8>,
}

impl<'a, R> MapDecoder<'a, R>
where
R: io::Read,
{
impl<'a, R: 'a> MapDecoder<'a, R> {
fn new(outer: &'a mut Decoder<R>) -> Self {
MapDecoder { outer, tag: None }
}
}

impl<'de: 'a, 'a, R: io::Read + 'a> de::MapAccess<'de> for MapDecoder<'a, R> {
impl<'de: 'a, 'a, R: Read<'de> + 'a> de::MapAccess<'de> for MapDecoder<'a, R> {
type Error = Error;

fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
where
K: de::DeserializeSeed<'de>,
{
let tag = raw::read_bare_byte(&mut self.outer.reader)?;
let tag = self.outer.reader.read_bare_byte()?;

// NBT indicates the end of a compound type with a 0x00 tag.
if tag == 0x00 {
Expand Down Expand Up @@ -181,20 +193,20 @@ impl<'de: 'a, 'a, R: io::Read + 'a> de::MapAccess<'de> for MapDecoder<'a, R> {
}

/// Decoder for list-like types.
struct SeqDecoder<'a, R: io::Read + 'a> {
struct SeqDecoder<'a, R: 'a> {
outer: &'a mut Decoder<R>,
tag: u8,
length: i32,
current: i32,
}

impl<'a, R> SeqDecoder<'a, R>
impl<'a, 'de, R> SeqDecoder<'a, R>
where
R: io::Read,
R: Read<'de>,
{
fn list(outer: &'a mut Decoder<R>) -> Result<Self> {
let tag = raw::read_bare_byte(&mut outer.reader)?;
let length = raw::read_bare_int(&mut outer.reader)?;
let tag = outer.reader.read_bare_byte()?;
let length = outer.reader.read_bare_int()?;
Ok(SeqDecoder {
outer,
tag: tag as u8,
Expand All @@ -204,7 +216,7 @@ where
}

fn byte_array(outer: &'a mut Decoder<R>) -> Result<Self> {
let length = raw::read_bare_int(&mut outer.reader)?;
let length = outer.reader.read_bare_int()?;
Ok(SeqDecoder {
outer,
tag: 0x01,
Expand All @@ -214,7 +226,7 @@ where
}

fn int_array(outer: &'a mut Decoder<R>) -> Result<Self> {
let length = raw::read_bare_int(&mut outer.reader)?;
let length = outer.reader.read_bare_int()?;
Ok(SeqDecoder {
outer,
tag: 0x03,
Expand All @@ -224,7 +236,7 @@ where
}

fn long_array(outer: &'a mut Decoder<R>) -> Result<Self> {
let length = raw::read_bare_int(&mut outer.reader)?;
let length = outer.reader.read_bare_int()?;
Ok(SeqDecoder {
outer,
tag: 0x04,
Expand All @@ -234,7 +246,7 @@ where
}
}

impl<'de: 'a, 'a, R: io::Read + 'a> de::SeqAccess<'de> for SeqDecoder<'a, R> {
impl<'de: 'a, 'a, R: Read<'de> + 'a> de::SeqAccess<'de> for SeqDecoder<'a, R> {
type Error = Error;

fn next_element_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
Expand Down Expand Up @@ -263,12 +275,12 @@ impl<'de: 'a, 'a, R: io::Read + 'a> de::SeqAccess<'de> for SeqDecoder<'a, R> {
}

/// Private inner decoder, for decoding raw (i.e. non-Compound) types.
struct InnerDecoder<'a, R: io::Read + 'a> {
struct InnerDecoder<'a, R: 'a> {
outer: &'a mut Decoder<R>,
tag: u8,
}

impl<'a, 'b: 'a, 'de, R: io::Read> de::Deserializer<'de> for &'b mut InnerDecoder<'a, R> {
impl<'a, 'b: 'a, 'de, R: Read<'de>> de::Deserializer<'de> for &'b mut InnerDecoder<'a, R> {
type Error = Error;

fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
Expand All @@ -278,14 +290,18 @@ impl<'a, 'b: 'a, 'de, R: io::Read> de::Deserializer<'de> for &'b mut InnerDecode
let outer = &mut self.outer;

match self.tag {
0x01 => visitor.visit_i8(raw::read_bare_byte(&mut outer.reader)?),
0x02 => visitor.visit_i16(raw::read_bare_short(&mut outer.reader)?),
0x03 => visitor.visit_i32(raw::read_bare_int(&mut outer.reader)?),
0x04 => visitor.visit_i64(raw::read_bare_long(&mut outer.reader)?),
0x05 => visitor.visit_f32(raw::read_bare_float(&mut outer.reader)?),
0x06 => visitor.visit_f64(raw::read_bare_double(&mut outer.reader)?),
0x01 => visitor.visit_i8(outer.reader.read_bare_byte()?),
0x02 => visitor.visit_i16(outer.reader.read_bare_short()?),
0x03 => visitor.visit_i32(outer.reader.read_bare_int()?),
0x04 => visitor.visit_i64(outer.reader.read_bare_long()?),
0x05 => visitor.visit_f32(outer.reader.read_bare_float()?),
0x06 => visitor.visit_f64(outer.reader.read_bare_double()?),
0x07 => visitor.visit_seq(SeqDecoder::byte_array(outer)?),
0x08 => visitor.visit_string(raw::read_bare_string(&mut outer.reader)?),
0x08 => match outer.reader.read_bare_string(Some(&mut outer.scratch))? {
Reference::Borrowed(b) => visitor.visit_borrowed_str(b),
Reference::Copied(c) => visitor.visit_str(c),
Reference::Owned(o) => visitor.visit_string(o),
},
0x09 => visitor.visit_seq(SeqDecoder::list(outer)?),
0x0a => visitor.visit_map(MapDecoder::new(outer)),
0x0b => visitor.visit_seq(SeqDecoder::int_array(outer)?),
Expand All @@ -302,7 +318,7 @@ impl<'a, 'b: 'a, 'de, R: io::Read> de::Deserializer<'de> for &'b mut InnerDecode
match self.tag {
0x01 => {
let reader = &mut self.outer.reader;
let value = raw::read_bare_byte(reader)?;
let value = reader.read_bare_byte()?;
match value {
0 => visitor.visit_bool(false),
1 => visitor.visit_bool(true),
Expand Down Expand Up @@ -343,8 +359,23 @@ impl<'a, 'b: 'a, 'de, R: io::Read> de::Deserializer<'de> for &'b mut InnerDecode
visitor.visit_newtype_struct(self)
}

/// Deserialize an owned string. This disables the scratch space optimization to bypass copying into the scratch space
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.tag {
0x08 => {
let reader = &mut self.outer.reader;
let value = reader.read_bare_string(None)?;
visitor.visit_string(value.into_owned())
}
_ => Err(Error::TagMismatch(self.tag, 0x08)),
}
}

forward_to_deserialize_any! {
u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string bytes byte_buf seq
u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str bytes byte_buf seq
map tuple_struct struct tuple enum identifier ignored_any
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub use std::collections::HashMap as Map;

#[cfg(feature = "serde")]
#[doc(inline)]
pub use de::{from_gzip_reader, from_reader, from_zlib_reader};
pub use de::{from_gzip_reader, from_reader, from_slice, from_zlib_reader};
#[cfg(feature = "serde")]
#[doc(inline)]
pub use ser::{i32_array, i64_array, i8_array};
Expand Down
Loading