1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
//! This module contains types enabling 'zero-copy' capture of the array NBT
//! types. These types retain a reference to the input data when deserializing,
//! meaning the input has to live as long as the deserialized object. This can
//! be hard to manage, but offers potential performance improvements. Measure!
//! Usually the dominating factor in deserialization is decompressing the NBT
//! data.
//!
//! The [`ByteArray`], [`IntArray`], and [`LongArray`] types are the types to
//! use in your own data structures. They all implement an `iter()` method to
//! allow you to iterate over the data they contain.
//!
//! For versions that own their data, see
//! `fasnbt::{`[`ByteArray`][`crate::ByteArray`],
//! [`IntArray`][`crate::IntArray`], [`LongArray`][`crate::LongArray`]`}`.
//!
//! The `iter()` methods return an iterator to the values read on demand from an
//! internal reference to the input data.
//!
//! # Example
//!
//! ```no_run
//! use fastnbt::borrow::LongArray;
//! use serde::Deserialize;
//!
//! #[derive(Deserialize)]
//! #[serde(rename_all = "PascalCase")]
//! pub struct Section<'a> {
//! #[serde(borrow)]
//! pub block_states: Option<LongArray<'a>>,
//! pub y: i8,
//! }
//!
//!# fn main(){
//! let buf: &[u8] = unimplemented!("get a buffer from somewhere");
//! let section: Section = fastnbt::de::from_bytes(buf).unwrap();
//! let states = section.block_states.unwrap();
//!
//! for long in states.iter() {
//! // do something
//! }
//!# }
use std::{borrow::Cow, fmt};
use byteorder::{BigEndian, ReadBytesExt};
use serde::Deserialize;
use crate::{CompTag, BYTE_ARRAY_TAG, INT_ARRAY_TAG, LONG_ARRAY_TAG};
/// ByteArray can be used to deserialize the NBT data of the same name. This
/// borrows from the original input data when deserializing. The carving masks
/// in a chunk use this type, for example.
#[derive(Deserialize, Debug, Clone, Copy)]
pub struct ByteArray<'a> {
tag: CompTag<BYTE_ARRAY_TAG>,
data: &'a [u8],
}
impl<'a> ByteArray<'a> {
/// Create an iterator over the bytes.
pub fn iter(&self) -> ByteIter<'a> {
ByteIter(*self)
}
}
pub struct ByteIter<'a>(ByteArray<'a>);
impl<'a> Iterator for ByteIter<'a> {
type Item = i8;
fn next(&mut self) -> Option<Self::Item> {
self.0.data.read_i8().ok()
}
}
/// IntArray can be used to deserialize the NBT data of the same name. This
/// borrows from the original input data when deserializing. Biomes in the chunk
/// format are an example of this data type.
#[derive(Deserialize, Debug, Clone, Copy)]
pub struct IntArray<'a> {
tag: CompTag<INT_ARRAY_TAG>,
data: &'a [u8],
}
impl<'a> IntArray<'a> {
/// Create an iterator over the i32s
pub fn iter(&self) -> IntIter<'a> {
IntIter(*self)
}
}
pub struct IntIter<'a>(IntArray<'a>);
impl<'a> Iterator for IntIter<'a> {
type Item = i32;
fn next(&mut self) -> Option<Self::Item> {
self.0.data.read_i32::<BigEndian>().ok()
}
}
/// LongArray can be used to deserialize the NBT data of the same name. This
/// borrows from the original input data when deserializing. Block states
/// (storage of all the blocks in a chunk) are an exmple of when this is used.
#[derive(Deserialize, Debug, Clone, Copy)]
pub struct LongArray<'a> {
tag: CompTag<LONG_ARRAY_TAG>,
data: &'a [u8],
}
impl<'a> LongArray<'a> {
/// Create an iterator over the i64s
pub fn iter(&self) -> LongIter<'a> {
LongIter(*self)
}
}
pub struct LongIter<'a>(LongArray<'a>);
impl<'a> Iterator for LongIter<'a> {
type Item = i64;
fn next(&mut self) -> Option<Self::Item> {
self.0.data.read_i64::<BigEndian>().ok()
}
}
struct CowStr<'a>(Cow<'a, str>);
impl<'de> serde::Deserialize<'de> for CowStr<'de> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_str(CowStrVisitor)
}
}
struct CowStrVisitor;
impl<'de> serde::de::Visitor<'de> for CowStrVisitor {
type Value = CowStr<'de>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string")
}
// Borrowed directly from the input string, which has lifetime 'de
// The input must outlive the resulting Cow.
fn visit_borrowed_str<E>(self, value: &'de str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(CowStr(Cow::Borrowed(value)))
}
// A string that currently only lives in a temporary buffer -- we need a copy
// (Example: serde is reading from a BufRead)
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(CowStr(Cow::Owned(value.to_owned())))
}
// An optimisation of visit_str for situations where the deserializer has
// already taken ownership. For example, the string contains escaped characters.
fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(CowStr(Cow::Owned(value)))
}
}
pub fn deserialize_cow_str<'de, D>(deserializer: D) -> Result<Cow<'de, str>, D::Error>
where
D: serde::Deserializer<'de>,
{
let wrapper = CowStr::deserialize(deserializer)?;
Ok(wrapper.0)
}