azalea_nbt/
tag.rs

1use compact_str::CompactString;
2use enum_as_inner::EnumAsInner;
3#[cfg(feature = "serde")]
4use serde::{ser::SerializeMap, Deserialize, Serialize};
5
6pub type NbtByte = i8;
7pub type NbtShort = i16;
8pub type NbtInt = i32;
9pub type NbtLong = i64;
10pub type NbtFloat = f32;
11pub type NbtDouble = f64;
12pub type NbtByteArray = Vec<u8>;
13pub type NbtString = CompactString;
14pub type NbtIntArray = Vec<i32>;
15pub type NbtLongArray = Vec<i64>;
16
17pub const END_ID: u8 = 0;
18pub const BYTE_ID: u8 = 1;
19pub const SHORT_ID: u8 = 2;
20pub const INT_ID: u8 = 3;
21pub const LONG_ID: u8 = 4;
22pub const FLOAT_ID: u8 = 5;
23pub const DOUBLE_ID: u8 = 6;
24pub const BYTE_ARRAY_ID: u8 = 7;
25pub const STRING_ID: u8 = 8;
26pub const LIST_ID: u8 = 9;
27pub const COMPOUND_ID: u8 = 10;
28pub const INT_ARRAY_ID: u8 = 11;
29pub const LONG_ARRAY_ID: u8 = 12;
30
31/// An NBT value.
32#[derive(Clone, Debug, PartialEq, Default, EnumAsInner)]
33#[repr(u8)]
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(untagged))]
35pub enum Nbt {
36    #[default]
37    End = END_ID,
38    Byte(NbtByte) = BYTE_ID,
39    Short(NbtShort) = SHORT_ID,
40    Int(NbtInt) = INT_ID,
41    Long(NbtLong) = LONG_ID,
42    Float(NbtFloat) = FLOAT_ID,
43    Double(NbtDouble) = DOUBLE_ID,
44    ByteArray(NbtByteArray) = BYTE_ARRAY_ID,
45    String(NbtString) = STRING_ID,
46    List(NbtList) = LIST_ID,
47    Compound(NbtCompound) = COMPOUND_ID,
48    IntArray(NbtIntArray) = INT_ARRAY_ID,
49    LongArray(NbtLongArray) = LONG_ARRAY_ID,
50}
51impl Nbt {
52    /// Get the numerical ID of the tag type.
53    #[inline]
54    pub fn id(&self) -> u8 {
55        // SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)`
56        // `union` between `repr(C)` structs, each of which has the `u8`
57        // discriminant as its first field, so we can read the discriminant
58        // without offsetting the pointer.
59        unsafe { *<*const _>::from(self).cast::<u8>() }
60    }
61}
62
63/// An NBT value.
64#[derive(Clone, Debug, PartialEq)]
65#[repr(u8)]
66#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(untagged))]
67pub enum NbtList {
68    Empty = END_ID,
69    Byte(Vec<NbtByte>) = BYTE_ID,
70    Short(Vec<NbtShort>) = SHORT_ID,
71    Int(Vec<NbtInt>) = INT_ID,
72    Long(Vec<NbtLong>) = LONG_ID,
73    Float(Vec<NbtFloat>) = FLOAT_ID,
74    Double(Vec<NbtDouble>) = DOUBLE_ID,
75    ByteArray(Vec<NbtByteArray>) = BYTE_ARRAY_ID,
76    String(Vec<NbtString>) = STRING_ID,
77    List(Vec<NbtList>) = LIST_ID,
78    Compound(Vec<NbtCompound>) = COMPOUND_ID,
79    IntArray(Vec<NbtIntArray>) = INT_ARRAY_ID,
80    LongArray(Vec<NbtLongArray>) = LONG_ARRAY_ID,
81}
82
83impl NbtList {
84    /// Get the numerical ID of the tag type.
85    #[inline]
86    pub fn id(&self) -> u8 {
87        // SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)`
88        // `union` between `repr(C)` structs, each of which has the `u8`
89        // discriminant as its first field, so we can read the discriminant
90        // without offsetting the pointer.
91        unsafe { *<*const _>::from(self).cast::<u8>() }
92    }
93}
94impl From<Vec<NbtByte>> for NbtList {
95    fn from(v: Vec<NbtByte>) -> Self {
96        Self::Byte(v)
97    }
98}
99impl From<Vec<NbtShort>> for NbtList {
100    fn from(v: Vec<NbtShort>) -> Self {
101        Self::Short(v)
102    }
103}
104impl From<Vec<NbtInt>> for NbtList {
105    fn from(v: Vec<NbtInt>) -> Self {
106        Self::Int(v)
107    }
108}
109impl From<Vec<NbtLong>> for NbtList {
110    fn from(v: Vec<NbtLong>) -> Self {
111        Self::Long(v)
112    }
113}
114impl From<Vec<NbtFloat>> for NbtList {
115    fn from(v: Vec<NbtFloat>) -> Self {
116        Self::Float(v)
117    }
118}
119impl From<Vec<NbtDouble>> for NbtList {
120    fn from(v: Vec<NbtDouble>) -> Self {
121        Self::Double(v)
122    }
123}
124impl From<Vec<NbtByteArray>> for NbtList {
125    fn from(v: Vec<NbtByteArray>) -> Self {
126        Self::ByteArray(v)
127    }
128}
129impl From<Vec<NbtString>> for NbtList {
130    fn from(v: Vec<NbtString>) -> Self {
131        Self::String(v)
132    }
133}
134impl From<Vec<NbtList>> for NbtList {
135    fn from(v: Vec<NbtList>) -> Self {
136        Self::List(v)
137    }
138}
139impl From<Vec<NbtCompound>> for NbtList {
140    fn from(v: Vec<NbtCompound>) -> Self {
141        Self::Compound(v)
142    }
143}
144impl From<Vec<NbtIntArray>> for NbtList {
145    fn from(v: Vec<NbtIntArray>) -> Self {
146        Self::IntArray(v)
147    }
148}
149impl From<Vec<NbtLongArray>> for NbtList {
150    fn from(v: Vec<NbtLongArray>) -> Self {
151        Self::LongArray(v)
152    }
153}
154
155// thanks to Moulberry/Graphite for the idea to use a vec and binary search
156#[derive(Debug, Clone, Default, PartialEq)]
157pub struct NbtCompound {
158    inner: Vec<(NbtString, Nbt)>,
159}
160impl NbtCompound {
161    #[inline]
162    pub fn with_capacity(capacity: usize) -> Self {
163        Self {
164            inner: Vec::with_capacity(capacity),
165        }
166    }
167
168    #[inline]
169    fn binary_search(&self, key: &NbtString) -> Result<usize, usize> {
170        self.inner.binary_search_by(|(k, _)| k.cmp(key))
171    }
172
173    /// Get a reference to the value corresponding to the key in this compound.
174    ///
175    /// If you previously used [`Self::insert_unsorted`] without [`Self::sort`],
176    /// this function may return incorrect results.
177    #[inline]
178    pub fn get(&self, key: &str) -> Option<&Nbt> {
179        if self.is_worth_sorting() {
180            let key = NbtString::from(key);
181            self.binary_search(&key).ok().map(|i| &self.inner[i].1)
182        } else {
183            for (k, v) in &self.inner {
184                if &key == k {
185                    return Some(v);
186                }
187            }
188            None
189        }
190    }
191
192    #[inline]
193    pub fn insert_unsorted(&mut self, key: NbtString, value: Nbt) {
194        self.inner.push((key, value));
195    }
196
197    /// Insert an item into the compound, returning the previous value if it
198    /// existed.
199    ///
200    /// If you're adding many items at once, it's more efficient to use
201    /// [`Self::insert_unsorted`] and then [`Self::sort`] after everything is
202    /// inserted.
203    #[inline]
204    pub fn insert(&mut self, key: NbtString, value: Nbt) {
205        self.inner.push((key, value));
206        self.sort()
207    }
208
209    #[inline]
210    pub fn sort(&mut self) {
211        if !self.is_worth_sorting() {
212            return;
213        }
214        self.inner.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
215    }
216
217    #[inline]
218    pub fn iter(&self) -> std::slice::Iter<'_, (CompactString, Nbt)> {
219        self.inner.iter()
220    }
221
222    #[inline]
223    fn is_worth_sorting(&self) -> bool {
224        // i don't actually know when binary search starts being better, but it's at
225        // least more than 12
226        self.inner.len() >= 32
227    }
228}
229#[cfg(feature = "serde")]
230impl Serialize for NbtCompound {
231    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
232        let mut map = serializer.serialize_map(Some(self.inner.len()))?;
233        for (key, value) in &self.inner {
234            map.serialize_entry(key, value)?;
235        }
236        map.end()
237    }
238}
239#[cfg(feature = "serde")]
240impl<'de> Deserialize<'de> for NbtCompound {
241    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
242        use std::collections::BTreeMap;
243        let map = <BTreeMap<NbtString, Nbt> as Deserialize>::deserialize(deserializer)?;
244        Ok(Self {
245            inner: map.into_iter().collect(),
246        })
247    }
248}
249
250impl FromIterator<(NbtString, Nbt)> for NbtCompound {
251    fn from_iter<T: IntoIterator<Item = (NbtString, Nbt)>>(iter: T) -> Self {
252        let inner = iter.into_iter().collect::<Vec<_>>();
253        Self { inner }
254    }
255}
256
257impl From<Vec<(NbtString, Nbt)>> for NbtCompound {
258    fn from(inner: Vec<(NbtString, Nbt)>) -> Self {
259        Self { inner }
260    }
261}