use crate::nbt::tag::{NbtCompound, NbtTag, tag_id};
use crate::{Encode, EncodedSize, Result};
fn encode_nbt_string(s: &str, buf: &mut Vec<u8>) -> Result<()> {
let bytes = s.as_bytes();
(bytes.len() as u16).encode(buf)?;
buf.extend_from_slice(bytes);
Ok(())
}
fn nbt_string_size(s: &str) -> usize {
2 + s.len()
}
fn encode_tag_payload(tag: &NbtTag, buf: &mut Vec<u8>) -> Result<()> {
match tag {
NbtTag::Byte(v) => v.encode(buf),
NbtTag::Short(v) => v.encode(buf),
NbtTag::Int(v) => v.encode(buf),
NbtTag::Long(v) => v.encode(buf),
NbtTag::Float(v) => v.encode(buf),
NbtTag::Double(v) => v.encode(buf),
NbtTag::ByteArray(v) => {
(v.len() as i32).encode(buf)?;
for &b in v {
b.encode(buf)?;
}
Ok(())
}
NbtTag::String(v) => encode_nbt_string(v, buf),
NbtTag::List(list) => {
list.element_type.encode(buf)?;
(list.elements.len() as i32).encode(buf)?;
for elem in &list.elements {
encode_tag_payload(elem, buf)?;
}
Ok(())
}
NbtTag::Compound(compound) => {
encode_compound_payload(compound, buf)?;
Ok(())
}
NbtTag::IntArray(v) => {
(v.len() as i32).encode(buf)?;
for &val in v {
val.encode(buf)?;
}
Ok(())
}
NbtTag::LongArray(v) => {
(v.len() as i32).encode(buf)?;
for &val in v {
val.encode(buf)?;
}
Ok(())
}
}
}
fn encode_compound_payload(compound: &NbtCompound, buf: &mut Vec<u8>) -> Result<()> {
for (name, tag) in compound.iter() {
tag.tag_id().encode(buf)?;
encode_nbt_string(name, buf)?;
encode_tag_payload(tag, buf)?;
}
tag_id::END.encode(buf)?;
Ok(())
}
fn tag_payload_size(tag: &NbtTag) -> usize {
match tag {
NbtTag::Byte(_) => 1,
NbtTag::Short(_) => 2,
NbtTag::Int(_) => 4,
NbtTag::Long(_) => 8,
NbtTag::Float(_) => 4,
NbtTag::Double(_) => 8,
NbtTag::ByteArray(v) => 4 + v.len(),
NbtTag::String(v) => nbt_string_size(v),
NbtTag::List(list) => {
1 + 4 + list.elements.iter().map(tag_payload_size).sum::<usize>()
}
NbtTag::Compound(compound) => compound_payload_size(compound),
NbtTag::IntArray(v) => 4 + v.len() * 4,
NbtTag::LongArray(v) => 4 + v.len() * 8,
}
}
fn compound_payload_size(compound: &NbtCompound) -> usize {
let entries_size: usize = compound
.iter()
.map(|(name, tag)| {
1 + nbt_string_size(name) + tag_payload_size(tag)
})
.sum();
entries_size + 1 }
impl Encode for NbtCompound {
fn encode(&self, buf: &mut Vec<u8>) -> Result<()> {
tag_id::COMPOUND.encode(buf)?;
encode_compound_payload(self, buf)
}
}
impl EncodedSize for NbtCompound {
fn encoded_size(&self) -> usize {
1 + compound_payload_size(self)
}
}
impl Encode for NbtTag {
fn encode(&self, buf: &mut Vec<u8>) -> Result<()> {
match self {
NbtTag::Compound(compound) => compound.encode(buf),
_ => {
let mut wrapper = NbtCompound::new();
wrapper.insert("", self.clone());
wrapper.encode(buf)
}
}
}
}
impl EncodedSize for NbtTag {
fn encoded_size(&self) -> usize {
match self {
NbtTag::Compound(compound) => compound.encoded_size(),
_ => {
let mut wrapper = NbtCompound::new();
wrapper.insert("", self.clone());
wrapper.encoded_size()
}
}
}
}