use crate::TypeCodec;
use mcproto_codec::{
error::{CodecError, CodecKind, CodecOperation, InvalidEncodingReason},
io::{read_exact_counted, write_all_counted},
varint::{VarIntRead, VarIntWrite},
};
use std::{
fmt,
io::{Read, Write},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Boolean(pub bool);
impl TypeCodec for Boolean {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
let byte = if self.0 { 1u8 } else { 0u8 };
write_all_counted(writer, &[byte], CodecKind::Boolean, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError>
where
Self: Sized,
{
let mut buf = [0u8; 1];
read_exact_counted(reader, &mut buf, CodecKind::Boolean, 0)?;
match buf[0] {
0 => Ok(Boolean(false)),
1 => Ok(Boolean(true)),
_ => Err(CodecError::invalid_encoding(
CodecKind::Boolean,
1,
InvalidEncodingReason::InvalidBooleanValue { value: buf[0] },
)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Byte(pub i8);
impl TypeCodec for Byte {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Byte, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 1];
read_exact_counted(reader, &mut bytes, CodecKind::Byte, 0)?;
Ok(Self(i8::from_be_bytes(bytes)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct UnsignedByte(pub u8);
impl TypeCodec for UnsignedByte {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::UnsignedByte, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 1];
read_exact_counted(reader, &mut bytes, CodecKind::UnsignedByte, 0)?;
Ok(Self(u8::from_be_bytes(bytes)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Short(pub i16);
impl TypeCodec for Short {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Short, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 2];
read_exact_counted(reader, &mut bytes, CodecKind::Short, 0)?;
Ok(Self(i16::from_be_bytes(bytes)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct UnsignedShort(pub u16);
impl TypeCodec for UnsignedShort {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::UnsignedShort, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 2];
read_exact_counted(reader, &mut bytes, CodecKind::UnsignedShort, 0)?;
Ok(Self(u16::from_be_bytes(bytes)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Int(pub i32);
impl TypeCodec for Int {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Int, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 4];
read_exact_counted(reader, &mut bytes, CodecKind::Int, 0)?;
Ok(Self(i32::from_be_bytes(bytes)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Long(pub i64);
impl TypeCodec for Long {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Long, 0)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let mut bytes = [0; 8];
read_exact_counted(reader, &mut bytes, CodecKind::Long, 0)?;
Ok(Self(i64::from_be_bytes(bytes)))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct PrefixedString(pub String);
impl PrefixedString {
pub const MAX_UTF16_CODE_UNITS: usize = 0x7fff;
pub const MAX_BYTES: usize = Self::MAX_UTF16_CODE_UNITS * 3;
fn validate_value(
value: &str,
operation: CodecOperation,
bytes_processed: usize,
) -> Result<(), CodecError> {
let bytes = value.as_bytes();
if bytes.len() > Self::MAX_BYTES {
return Err(CodecError::invalid_encoding_for_operation(
CodecKind::String,
operation,
bytes_processed,
InvalidEncodingReason::StringTooLong {
max_bytes: Self::MAX_BYTES,
},
));
}
if value.encode_utf16().count() > Self::MAX_UTF16_CODE_UNITS {
return Err(CodecError::invalid_encoding_for_operation(
CodecKind::String,
operation,
bytes_processed,
InvalidEncodingReason::TooManyUtf16CodeUnits {
max_code_units: Self::MAX_UTF16_CODE_UNITS,
},
));
}
Ok(())
}
fn encode_value(value: &str, writer: &mut impl Write) -> Result<(), CodecError> {
Self::validate_value(value, CodecOperation::Write, 0)?;
let bytes = value.as_bytes();
let prefix_size = writer
.write_varint_with_size(bytes.len() as i32)
.map_err(|error| error.with_context(CodecKind::String))?;
write_all_counted(writer, bytes, CodecKind::String, prefix_size)
}
fn decode_value(reader: &mut impl Read) -> Result<(String, usize), CodecError> {
let (byte_length, prefix_size) = reader
.read_varint_with_size()
.map_err(|error| error.with_context(CodecKind::String))?;
let byte_length = usize::try_from(byte_length).map_err(|_| {
CodecError::invalid_encoding(
CodecKind::String,
prefix_size,
InvalidEncodingReason::NegativeLength { value: byte_length },
)
})?;
if byte_length > Self::MAX_BYTES {
return Err(CodecError::invalid_encoding(
CodecKind::String,
prefix_size,
InvalidEncodingReason::StringTooLong {
max_bytes: Self::MAX_BYTES,
},
));
}
let mut bytes = vec![0; byte_length];
read_exact_counted(reader, &mut bytes, CodecKind::String, prefix_size)?;
let bytes_processed = prefix_size + byte_length;
let value = String::from_utf8(bytes).map_err(|error| {
let utf8_error = error.utf8_error();
CodecError::invalid_encoding(
CodecKind::String,
bytes_processed,
InvalidEncodingReason::InvalidUtf8 {
valid_up_to: utf8_error.valid_up_to(),
error_len: utf8_error.error_len(),
},
)
})?;
Self::validate_value(&value, CodecOperation::Read, bytes_processed)?;
Ok((value, bytes_processed))
}
}
impl TypeCodec for PrefixedString {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
Self::encode_value(&self.0, writer)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
Self::decode_value(reader).map(|(value, _)| Self(value))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Identifier(String);
impl Identifier {
pub const MAX_UTF16_CODE_UNITS: usize = PrefixedString::MAX_UTF16_CODE_UNITS;
pub const MAX_BYTES: usize = PrefixedString::MAX_BYTES;
pub const MAX_ENCODED_BYTES: usize = Self::MAX_BYTES + 3;
pub fn new(value: impl Into<String>) -> Result<Self, InvalidIdentifier> {
let value = value.into();
validate_identifier(&value)?;
Ok(Self(value))
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn into_inner(self) -> String {
self.0
}
}
impl TypeCodec for Identifier {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
PrefixedString::encode_value(&self.0, writer)
.map_err(|error| error.with_context(CodecKind::Identifier))
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let (value, bytes_processed) = PrefixedString::decode_value(reader)
.map_err(|error| error.with_context(CodecKind::Identifier))?;
Self::new(value).map_err(|_| {
CodecError::invalid_encoding(
CodecKind::Identifier,
bytes_processed,
InvalidEncodingReason::InvalidIdentifier,
)
})
}
}
fn validate_identifier(value: &str) -> Result<(), InvalidIdentifier> {
let (namespace, path) = match value.split_once(':') {
Some((namespace, path)) => (namespace, path),
None => ("minecraft", value),
};
let namespace_is_valid = !namespace.is_empty()
&& namespace.bytes().all(|byte| {
byte.is_ascii_lowercase() || byte.is_ascii_digit() || b"_.-".contains(&byte)
});
let path_is_valid = !path.is_empty()
&& path.bytes().all(|byte| {
byte.is_ascii_lowercase() || byte.is_ascii_digit() || b"/._-".contains(&byte)
});
if namespace_is_valid && path_is_valid && !path.contains(':') {
Ok(())
} else {
Err(InvalidIdentifier)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InvalidIdentifier;
impl fmt::Display for InvalidIdentifier {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("invalid Minecraft identifier")
}
}
impl std::error::Error for InvalidIdentifier {}