Skip to main content

mcproto_types/
basic.rs

1//! Basic Minecraft protocol types and their wire encodings.
2//!
3//! This module includes primitive numeric values, booleans, variable-length
4//! integers, length-prefixed strings, and resource identifiers.
5
6use crate::TypeCodec;
7use mcproto_codec::{
8    error::{CodecError, CodecKind, CodecOperation, InvalidEncodingReason},
9    io::{read_exact_counted, write_all_counted},
10    varint::{VarIntRead, VarIntWrite},
11    varlong::{VarLongRead, VarLongWrite},
12};
13use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _};
14use std::{
15    fmt,
16    io::{Read, Write},
17};
18
19/// A boolean encoded as `0x00` for false or `0x01` for true.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
21pub struct Boolean(
22    /// The boolean value.
23    pub bool,
24);
25
26impl TypeCodec for Boolean {
27    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
28        let byte = if self.0 { 1u8 } else { 0u8 };
29        write_all_counted(writer, &[byte], CodecKind::Boolean, 0)
30    }
31
32    fn decode(reader: &mut impl Read) -> Result<Self, CodecError>
33    where
34        Self: Sized,
35    {
36        let mut buf = [0u8; 1];
37        read_exact_counted(reader, &mut buf, CodecKind::Boolean, 0)?;
38        match buf[0] {
39            0 => Ok(Boolean(false)),
40            1 => Ok(Boolean(true)),
41            _ => Err(CodecError::invalid_encoding(
42                CodecKind::Boolean,
43                1,
44                InvalidEncodingReason::InvalidBooleanValue { value: buf[0] },
45            )),
46        }
47    }
48}
49
50/// A two's-complement signed 8-bit integer from -128 through 127.
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
52pub struct Byte(
53    /// The integer value.
54    pub i8,
55);
56
57impl TypeCodec for Byte {
58    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
59        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Byte, 0)
60    }
61
62    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
63        let mut bytes = [0; 1];
64        read_exact_counted(reader, &mut bytes, CodecKind::Byte, 0)?;
65        Ok(Self(i8::from_be_bytes(bytes)))
66    }
67}
68
69/// An unsigned 8-bit integer from 0 through 255.
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
71pub struct UnsignedByte(
72    /// The integer value.
73    pub u8,
74);
75
76impl TypeCodec for UnsignedByte {
77    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
78        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::UnsignedByte, 0)
79    }
80
81    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
82        let mut bytes = [0; 1];
83        read_exact_counted(reader, &mut bytes, CodecKind::UnsignedByte, 0)?;
84        Ok(Self(u8::from_be_bytes(bytes)))
85    }
86}
87
88/// A two's-complement signed 16-bit integer from -32,768 through 32,767.
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
90pub struct Short(
91    /// The integer value.
92    pub i16,
93);
94
95impl TypeCodec for Short {
96    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
97        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Short, 0)
98    }
99
100    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
101        let mut bytes = [0; 2];
102        read_exact_counted(reader, &mut bytes, CodecKind::Short, 0)?;
103        Ok(Self(i16::from_be_bytes(bytes)))
104    }
105}
106
107/// An unsigned 16-bit integer from 0 through 65,535.
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
109pub struct UnsignedShort(
110    /// The integer value.
111    pub u16,
112);
113
114impl TypeCodec for UnsignedShort {
115    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
116        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::UnsignedShort, 0)
117    }
118
119    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
120        let mut bytes = [0; 2];
121        read_exact_counted(reader, &mut bytes, CodecKind::UnsignedShort, 0)?;
122        Ok(Self(u16::from_be_bytes(bytes)))
123    }
124}
125
126/// A two's-complement signed 32-bit integer from -2,147,483,648 through
127/// 2,147,483,647.
128#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
129pub struct Int(
130    /// The integer value.
131    pub i32,
132);
133
134impl TypeCodec for Int {
135    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
136        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Int, 0)
137    }
138
139    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
140        let mut bytes = [0; 4];
141        read_exact_counted(reader, &mut bytes, CodecKind::Int, 0)?;
142        Ok(Self(i32::from_be_bytes(bytes)))
143    }
144}
145
146/// A two's-complement signed 64-bit integer from -9,223,372,036,854,775,808
147/// through 9,223,372,036,854,775,807.
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
149pub struct Long(
150    /// The integer value.
151    pub i64,
152);
153
154impl TypeCodec for Long {
155    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
156        write_all_counted(writer, &self.0.to_be_bytes(), CodecKind::Long, 0)
157    }
158
159    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
160        let mut bytes = [0; 8];
161        read_exact_counted(reader, &mut bytes, CodecKind::Long, 0)?;
162        Ok(Self(i64::from_be_bytes(bytes)))
163    }
164}
165
166/// A UTF-8 string prefixed by its byte length as a VarInt.
167///
168/// The protocol limits both the UTF-8 payload size and the number of UTF-16
169/// code units. Supplementary [Unicode scalar values] count as two UTF-16
170/// code units. The general protocol limit is 32,767 UTF-16 code units and
171/// three UTF-8 bytes per permitted code unit; a particular field may impose
172/// a lower limit.
173///
174/// [Unicode scalar values]: https://www.unicode.org/glossary/#unicode_scalar_value
175#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
176pub struct PrefixedString(
177    /// The string value.
178    pub String,
179);
180
181impl PrefixedString {
182    /// The maximum number of UTF-16 code units permitted in the string.
183    pub const MAX_UTF16_CODE_UNITS: usize = 0x7fff;
184    /// The maximum size of the UTF-8 payload, excluding its VarInt length prefix.
185    pub const MAX_BYTES: usize = Self::MAX_UTF16_CODE_UNITS * 3;
186
187    fn encode_value(value: &str, writer: &mut impl Write) -> Result<(), CodecError> {
188        encode_prefixed_string(
189            value,
190            writer,
191            CodecKind::String,
192            Self::MAX_BYTES,
193            Self::MAX_UTF16_CODE_UNITS,
194        )
195    }
196
197    fn decode_value(reader: &mut impl Read) -> Result<(String, usize), CodecError> {
198        decode_prefixed_string(
199            reader,
200            CodecKind::String,
201            Self::MAX_BYTES,
202            Self::MAX_UTF16_CODE_UNITS,
203        )
204    }
205}
206
207/// Encodes `value` as a VarInt-length-prefixed string.
208///
209/// `codec` identifies the codec in errors, `max_bytes` limits the UTF-8
210/// payload, and `max_code_units` limits the number of UTF-16 code units.
211/// Supplementary Unicode scalar values count as two UTF-16 code units.
212///
213/// # Errors
214///
215/// Returns a [`CodecError`] if the string exceeds `max_bytes` or
216/// `max_code_units`, or if the underlying writer fails while writing the length
217/// prefix or payload.
218pub(crate) fn encode_prefixed_string(
219    value: &str,
220    writer: &mut impl Write,
221    codec: CodecKind,
222    max_bytes: usize,
223    max_code_units: usize,
224) -> Result<(), CodecError> {
225    if value.len() > max_bytes {
226        return Err(CodecError::invalid_encoding_for_operation(
227            codec,
228            CodecOperation::Write,
229            0,
230            InvalidEncodingReason::StringTooLong { max_bytes },
231        ));
232    }
233    if value.encode_utf16().count() > max_code_units {
234        return Err(CodecError::invalid_encoding_for_operation(
235            codec,
236            CodecOperation::Write,
237            0,
238            InvalidEncodingReason::TooManyUtf16CodeUnits { max_code_units },
239        ));
240    }
241
242    let bytes = value.as_bytes();
243    let prefix_size = writer
244        .write_varint_with_size(bytes.len() as i32)
245        .map_err(|error| error.with_context(codec))?;
246    write_all_counted(writer, bytes, codec, prefix_size)
247}
248
249/// Decodes a VarInt-length-prefixed string and returns its value and the total
250/// number of bytes processed, including the length prefix.
251///
252/// `codec` is the codec in errors, `max_bytes` limits the UTF-8 payload, and
253/// `max_code_units` limits the number of UTF-16 code units.
254///
255/// # Errors
256///
257/// Returns a [`CodecError`] if the length prefix is negative, the payload
258/// exceeds `max_bytes`, the payload contains invalid UTF-8 or more than
259/// `max_code_units` UTF-16 code units, or the reader reaches an unexpected end
260/// of input.
261pub(crate) fn decode_prefixed_string(
262    reader: &mut impl Read,
263    codec: CodecKind,
264    max_bytes: usize,
265    max_code_units: usize,
266) -> Result<(String, usize), CodecError> {
267    let (byte_length, prefix_size) = reader
268        .read_varint_with_size()
269        .map_err(|error| error.with_context(codec))?;
270    let byte_length = usize::try_from(byte_length).map_err(|_| {
271        CodecError::invalid_encoding(
272            codec,
273            prefix_size,
274            InvalidEncodingReason::NegativeLength { value: byte_length },
275        )
276    })?;
277
278    if byte_length > max_bytes {
279        return Err(CodecError::invalid_encoding(
280            codec,
281            prefix_size,
282            InvalidEncodingReason::StringTooLong { max_bytes },
283        ));
284    }
285
286    let mut bytes = vec![0; byte_length];
287    read_exact_counted(reader, &mut bytes, codec, prefix_size)?;
288    let bytes_processed = prefix_size + byte_length;
289    let value = String::from_utf8(bytes).map_err(|error| {
290        let utf8_error = error.utf8_error();
291        CodecError::invalid_encoding(
292            codec,
293            bytes_processed,
294            InvalidEncodingReason::InvalidUtf8 {
295                valid_up_to: utf8_error.valid_up_to(),
296                error_len: utf8_error.error_len(),
297            },
298        )
299    })?;
300    if value.len() > max_bytes {
301        return Err(CodecError::invalid_encoding(
302            codec,
303            bytes_processed,
304            InvalidEncodingReason::StringTooLong { max_bytes },
305        ));
306    }
307    if value.encode_utf16().count() > max_code_units {
308        return Err(CodecError::invalid_encoding(
309            codec,
310            bytes_processed,
311            InvalidEncodingReason::TooManyUtf16CodeUnits { max_code_units },
312        ));
313    }
314    Ok((value, bytes_processed))
315}
316
317impl TypeCodec for PrefixedString {
318    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
319        Self::encode_value(&self.0, writer)
320    }
321
322    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
323        Self::decode_value(reader).map(|(value, _)| Self(value))
324    }
325}
326
327/// A resource identifier encoded as a [`PrefixedString`].
328///
329/// The namespace permits `[a-z0-9._-]`; the value permits
330/// `[a-z0-9._/-]`. See the protocol's [identifier format] for details.
331///
332/// [identifier format]: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Identifier
333#[derive(Debug, Clone, PartialEq, Eq, Hash)]
334pub struct Identifier(String);
335
336impl Identifier {
337    /// The maximum number of UTF-16 code units permitted in the identifier.
338    pub const MAX_UTF16_CODE_UNITS: usize = PrefixedString::MAX_UTF16_CODE_UNITS;
339    /// The maximum size of the UTF-8 payload, excluding its VarInt length prefix.
340    pub const MAX_BYTES: usize = PrefixedString::MAX_BYTES;
341    /// The maximum encoded size, including the VarInt length prefix.
342    pub const MAX_ENCODED_BYTES: usize = Self::MAX_BYTES + 3;
343
344    /// Creates an identifier after validating its namespace and path.
345    ///
346    /// An identifier without an explicit namespace is validated as belonging
347    /// to the `minecraft` namespace, but its original spelling is preserved.
348    ///
349    /// # Errors
350    ///
351    /// Returns [`InvalidIdentifier`] if the namespace or path is empty or
352    /// contains a character not permitted by the identifier format.
353    pub fn new(value: impl Into<String>) -> Result<Self, InvalidIdentifier> {
354        let value = value.into();
355        validate_identifier(&value)?;
356        Ok(Self(value))
357    }
358
359    /// Returns the identifier as a string slice.
360    pub fn as_str(&self) -> &str {
361        &self.0
362    }
363
364    /// Returns the owned identifier string.
365    pub fn into_inner(self) -> String {
366        self.0
367    }
368}
369
370impl fmt::Display for Identifier {
371    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
372        formatter.write_str(&self.0)
373    }
374}
375
376impl Serialize for Identifier {
377    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
378    where
379        S: Serializer,
380    {
381        serializer.serialize_str(&self.0)
382    }
383}
384
385impl<'de> Deserialize<'de> for Identifier {
386    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
387    where
388        D: Deserializer<'de>,
389    {
390        Self::new(String::deserialize(deserializer)?).map_err(D::Error::custom)
391    }
392}
393
394impl TypeCodec for Identifier {
395    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
396        PrefixedString::encode_value(&self.0, writer)
397            .map_err(|error| error.with_context(CodecKind::Identifier))
398    }
399
400    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
401        let (value, bytes_processed) = PrefixedString::decode_value(reader)
402            .map_err(|error| error.with_context(CodecKind::Identifier))?;
403        Self::new(value).map_err(|_| {
404            CodecError::invalid_encoding(
405                CodecKind::Identifier,
406                bytes_processed,
407                InvalidEncodingReason::InvalidIdentifier,
408            )
409        })
410    }
411}
412
413/// Returns whether a string is a valid Minecraft resource identifier.
414///
415/// An identifier without an explicit namespace is validated as belonging to
416/// the `minecraft` namespace. The namespace permits `[a-z0-9._-]`; the path
417/// permits `[a-z0-9._/-]`. See the protocol's [identifier format].
418///
419/// [identifier format]: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Identifier
420pub(crate) fn is_valid_identifier(value: &str) -> bool {
421    let (namespace, path) = match value.split_once(':') {
422        Some((namespace, path)) => (namespace, path),
423        None => ("minecraft", value),
424    };
425    let namespace_is_valid = !namespace.is_empty()
426        && namespace.bytes().all(|byte| {
427            byte.is_ascii_lowercase() || byte.is_ascii_digit() || b"_.-".contains(&byte)
428        });
429    let path_is_valid = !path.is_empty()
430        && path.bytes().all(|byte| {
431            byte.is_ascii_lowercase() || byte.is_ascii_digit() || b"/._-".contains(&byte)
432        });
433    namespace_is_valid && path_is_valid && !path.contains(':')
434}
435
436fn validate_identifier(value: &str) -> Result<(), InvalidIdentifier> {
437    if is_valid_identifier(value) {
438        Ok(())
439    } else {
440        Err(InvalidIdentifier)
441    }
442}
443
444/// An error returned when a string is not a valid Minecraft resource identifier.
445#[derive(Debug, Clone, Copy, PartialEq, Eq)]
446pub struct InvalidIdentifier;
447
448impl fmt::Display for InvalidIdentifier {
449    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
450        formatter.write_str("invalid Minecraft identifier")
451    }
452}
453
454impl std::error::Error for InvalidIdentifier {}
455
456/// A variable-length two's-complement signed 32-bit integer.
457///
458/// VarInts use one to five bytes on the wire. Each byte carries seven payload
459/// bits; the most-significant bit is set on every byte except the last.
460///
461/// # Examples
462///
463/// ```
464/// use mcproto_types::{TypeCodec, basic::VarInt};
465///
466/// let mut encoded = Vec::new();
467/// VarInt(25565).encode(&mut encoded)?;
468/// assert_eq!(encoded, [0xdd, 0xc7, 0x01]);
469///
470/// let mut input = encoded.as_slice();
471/// assert_eq!(VarInt::decode(&mut input)?, VarInt(25565));
472/// assert!(input.is_empty());
473/// # Ok::<(), mcproto_codec::error::CodecError>(())
474/// ```
475#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
476pub struct VarInt(
477    /// The integer value.
478    pub i32,
479);
480
481impl TypeCodec for VarInt {
482    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
483        writer.write_varint(self.0)
484    }
485
486    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
487        reader.read_varint().map(Self)
488    }
489}
490
491/// A variable-length two's-complement signed 64-bit integer.
492///
493/// VarLongs use one to ten bytes on the wire. Each byte carries seven payload
494/// bits; the most-significant bit is set on every byte except the last.
495///
496/// # Examples
497///
498/// ```
499/// use mcproto_types::{TypeCodec, basic::VarLong};
500///
501/// let mut encoded = Vec::new();
502/// VarLong(9_223_372_036_854_775_000).encode(&mut encoded)?;
503///
504/// let mut input = encoded.as_slice();
505/// assert_eq!(
506///     VarLong::decode(&mut input)?,
507///     VarLong(9_223_372_036_854_775_000),
508/// );
509/// assert!(input.is_empty());
510/// # Ok::<(), mcproto_codec::error::CodecError>(())
511/// ```
512#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
513pub struct VarLong(
514    /// The integer value.
515    pub i64,
516);
517
518impl TypeCodec for VarLong {
519    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
520        writer.write_varlong(self.0)
521    }
522
523    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
524        reader.read_varlong().map(Self)
525    }
526}
527
528/// A Minecraft protocol block position packed into a 64-bit value.
529///
530/// The wire format stores `x` in the 26 most-significant bits, `z` in the
531/// middle 26 bits, and `y` in the 12 least-significant bits. Each component is
532/// a signed two's-complement integer:
533///
534/// ```text
535/// x: 26 bits | z: 26 bits | y: 12 bits
536/// ```
537///
538/// The value is packed as:
539///
540/// ```text
541/// ((x & 0x3FFFFFF) << 38) | ((z & 0x3FFFFFF) << 12) | (y & 0xFFF)
542/// ```
543///
544/// # Examples
545///
546/// ```
547/// use mcproto_types::{TypeCodec, basic::Position};
548///
549/// let position = Position {
550///     x: 18_357_644,
551///     y: 831,
552///     z: -20_882_616,
553/// };
554///
555/// let mut encoded = Vec::new();
556/// position.encode(&mut encoded)?;
557/// assert_eq!(
558///     encoded,
559///     [0x46, 0x07, 0x63, 0x2c, 0x15, 0xb4, 0x83, 0x3f]
560/// );
561///
562/// let mut input = encoded.as_slice();
563/// assert_eq!(Position::decode(&mut input)?, position);
564/// assert!(input.is_empty());
565/// # Ok::<(), mcproto_codec::error::CodecError>(())
566/// ```
567#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
568pub struct Position {
569    /// The x coordinate, from -33,554,432 through 33,554,431.
570    pub x: i32,
571    /// The y coordinate, from -2,048 through 2,047.
572    pub y: i16,
573    /// The z coordinate, from -33,554,432 through 33,554,431.
574    pub z: i32,
575}
576
577impl TypeCodec for Position {
578    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
579        let value = ((self.x as i64 & 0x3ff_ffff) << 38)
580            | ((self.z as i64 & 0x3ff_ffff) << 12)
581            | (self.y as i64 & 0xfff);
582        write_all_counted(writer, &value.to_be_bytes(), CodecKind::Position, 0)
583    }
584
585    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
586        let mut bytes = [0; 8];
587        read_exact_counted(reader, &mut bytes, CodecKind::Position, 0)?;
588        let value = i64::from_be_bytes(bytes);
589
590        Ok(Self {
591            x: (value >> 38) as i32,
592            y: ((value << 52) >> 52) as i16,
593            z: ((value << 26) >> 38) as i32,
594        })
595    }
596}
597
598/// A rotation angle encoded in steps of 1/256 of a full turn.
599///
600/// The wire value is a single byte. Because 256 steps represent one full turn,
601/// the value wraps around and its signedness does not matter.
602///
603/// # Examples
604///
605/// ```
606/// use mcproto_types::{TypeCodec, basic::Angle};
607///
608/// let angle = Angle(64);
609///
610/// let mut encoded = Vec::new();
611/// angle.encode(&mut encoded)?;
612/// assert_eq!(encoded, [64]);
613///
614/// let mut input = encoded.as_slice();
615/// assert_eq!(Angle::decode(&mut input)?, angle);
616///
617/// assert_eq!(angle.to_degrees(), 90.0);
618/// assert!((angle.to_radians() - std::f64::consts::FRAC_PI_2).abs() < 1e-12);
619/// # Ok::<(), mcproto_codec::error::CodecError>(())
620/// ```
621#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
622pub struct Angle(
623    /// The raw angle step count, from 0 through 255.
624    pub u8,
625);
626
627impl Angle {
628    /// Returns the angle in degrees.
629    ///
630    /// A full turn of 256 steps is equivalent to 360 degrees.
631    pub fn to_degrees(&self) -> f64 {
632        f64::from(self.0) * 360.0 / 256.0
633    }
634
635    /// Returns the angle in radians.
636    ///
637    /// A full turn of 256 steps is equivalent to 2π radians.
638    pub fn to_radians(&self) -> f64 {
639        f64::from(self.0) * std::f64::consts::TAU / 256.0
640    }
641}
642
643impl TypeCodec for Angle {
644    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
645        write_all_counted(writer, &[self.0], CodecKind::Angle, 0)
646    }
647
648    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
649        let mut bytes = [0; 1];
650        read_exact_counted(reader, &mut bytes, CodecKind::Angle, 0)?;
651        Ok(Self(bytes[0]))
652    }
653}
654
655/// A 128-bit universally unique identifier.
656///
657/// Encoded as an unsigned 128-bit integer (or two unsigned 64-bit integers: the
658/// most significant 64 bits and then the least significant 64 bits).
659///
660/// See [Universally unique identifier](https://en.wikipedia.org/wiki/Universally_unique_identifier).
661///
662/// # Examples
663///
664/// ```
665/// use mcproto_types::{TypeCodec, basic::Uuid};
666///
667/// let value = Uuid::from_bytes([
668///     0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
669///     0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21,
670/// ]);
671///
672/// let mut encoded = Vec::new();
673/// value.encode(&mut encoded)?;
674/// assert_eq!(
675///     encoded,
676///     [
677///         0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
678///         0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21,
679///     ]
680/// );
681///
682/// let mut input = encoded.as_slice();
683/// assert_eq!(Uuid::decode(&mut input)?, value);
684/// assert!(input.is_empty());
685/// # Ok::<(), mcproto_codec::error::CodecError>(())
686/// ```
687#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
688pub struct Uuid(
689    /// The UUID value.
690    pub uuid::Uuid,
691);
692
693impl Uuid {
694    /// Creates a UUID from 16 bytes in big-endian order.
695    pub fn from_bytes(bytes: [u8; 16]) -> Self {
696        Self(uuid::Uuid::from_bytes(bytes))
697    }
698
699    /// Returns the UUID as 16 bytes in big-endian order.
700    pub fn into_bytes(self) -> [u8; 16] {
701        self.0.into_bytes()
702    }
703}
704
705impl TypeCodec for Uuid {
706    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
707        write_all_counted(writer, &self.0.into_bytes(), CodecKind::Uuid, 0)
708    }
709
710    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
711        let mut bytes = [0; 16];
712        read_exact_counted(reader, &mut bytes, CodecKind::Uuid, 0)?;
713        Ok(Self::from_bytes(bytes))
714    }
715}
716
717/// A length-prefixed bit set.
718///
719/// The wire representation is a VarInt length prefix followed by that many
720/// 64-bit words, encoded in big-endian order. The `i`th bit is set when:
721///
722/// ```text
723/// (data[i / 64] & (1 << (i % 64))) != 0
724/// ```
725///
726/// # Examples
727///
728/// ```
729/// use mcproto_types::{TypeCodec, basic::BitSet};
730///
731/// let bits = BitSet(vec![0b0000_0101]);
732///
733/// let mut encoded = Vec::new();
734/// bits.encode(&mut encoded)?;
735/// assert_eq!(
736///     encoded,
737///     [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05]
738/// );
739///
740/// let mut input = encoded.as_slice();
741/// assert_eq!(BitSet::decode(&mut input)?, bits);
742/// assert!(input.is_empty());
743///
744/// assert!(bits.contains(0));
745/// assert!(!bits.contains(1));
746/// assert!(bits.contains(2));
747/// # Ok::<(), mcproto_codec::error::CodecError>(())
748/// ```
749#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
750pub struct BitSet(
751    /// The packed 64-bit words, in little-endian bit order.
752    pub Vec<u64>,
753);
754
755impl BitSet {
756    /// Returns whether the bit at `index` is set.
757    pub fn contains(&self, index: usize) -> bool {
758        match self.0.get(index / 64) {
759            Some(word) => (word & (1 << (index % 64))) != 0,
760            None => false,
761        }
762    }
763}
764
765impl TypeCodec for BitSet {
766    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
767        let prefix_size = writer
768            .write_varint_with_size(self.0.len() as i32)
769            .map_err(|error| error.with_context(CodecKind::BitSet))?;
770
771        let mut bytes_processed = prefix_size;
772        for word in &self.0 {
773            write_all_counted(
774                writer,
775                &word.to_be_bytes(),
776                CodecKind::BitSet,
777                bytes_processed,
778            )?;
779            bytes_processed += 8;
780        }
781
782        Ok(())
783    }
784
785    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
786        let (length, prefix_size) = reader
787            .read_varint_with_size()
788            .map_err(|error| error.with_context(CodecKind::BitSet))?;
789        let length = usize::try_from(length).map_err(|_| {
790            CodecError::invalid_encoding(
791                CodecKind::BitSet,
792                prefix_size,
793                InvalidEncodingReason::NegativeLength { value: length },
794            )
795        })?;
796
797        let mut words = Vec::with_capacity(length);
798        let mut bytes_processed = prefix_size;
799        for _ in 0..length {
800            let mut bytes = [0; 8];
801            read_exact_counted(reader, &mut bytes, CodecKind::BitSet, bytes_processed)?;
802            words.push(u64::from_be_bytes(bytes));
803            bytes_processed += 8;
804        }
805
806        Ok(Self(words))
807    }
808}
809
810/// A bit set with a fixed length of `N` bits.
811///
812/// A fixed bit set is encoded as exactly `ceil(N / 8)` bytes, without a
813/// length prefix. This differs from [`BitSet`], which is prefixed by a VarInt
814/// and stores packed 64-bit words. The packed bytes follow the same bit order
815/// as Java's `BitSet.toByteArray`: bit `i` is set when the following expression
816/// is non-zero:
817///
818/// ```text
819/// (data[i / 8] & (1 << (i % 8))) != 0
820/// ```
821///
822/// The final byte is padded with zero bits when `N` is not divisible by eight.
823///
824/// # Examples
825///
826/// ```
827/// use mcproto_types::{TypeCodec, basic::FixedBitSet};
828///
829/// // Bits 0, 7, and 8 are set in a nine-bit set.
830/// let bits = FixedBitSet::<9>(vec![0b1000_0001, 0b0000_0001]);
831///
832/// let mut encoded = Vec::new();
833/// bits.encode(&mut encoded)?;
834/// assert_eq!(encoded, [0b1000_0001, 0b0000_0001]);
835///
836/// let mut input = encoded.as_slice();
837/// assert_eq!(FixedBitSet::<9>::decode(&mut input)?, bits);
838/// assert!(bits.contains(0));
839/// assert!(bits.contains(7));
840/// assert!(bits.contains(8));
841/// assert!(!bits.contains(9));
842/// # Ok::<(), mcproto_codec::error::CodecError>(())
843/// ```
844#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
845pub struct FixedBitSet<const N: usize>(
846    /// The packed bytes, in little-endian bit order within each byte.
847    pub Vec<u8>,
848);
849
850impl<const N: usize> FixedBitSet<N> {
851    /// The number of packed bytes used by this fixed bit set.
852    pub const BYTE_LEN: usize = N.div_ceil(8);
853
854    /// Returns whether the bit at `index` is set.
855    ///
856    /// Indices outside this set's fixed length return `false`.
857    pub fn contains(&self, index: usize) -> bool {
858        index < N
859            && self
860                .0
861                .get(index / 8)
862                .is_some_and(|byte| (byte & (1 << (index % 8))) != 0)
863    }
864
865    fn validate(
866        &self,
867        operation: CodecOperation,
868        bytes_processed: usize,
869    ) -> Result<(), CodecError> {
870        if self.0.len() != Self::BYTE_LEN {
871            return Err(CodecError::invalid_encoding_for_operation(
872                CodecKind::FixedBitSet,
873                operation,
874                bytes_processed,
875                InvalidEncodingReason::InvalidFixedBitSetLength {
876                    expected: Self::BYTE_LEN,
877                    actual: self.0.len(),
878                },
879            ));
880        }
881
882        if let Some(last_byte) = self.0.last()
883            && N % 8 != 0
884        {
885            let allowed_mask = (1u8 << (N % 8)) - 1;
886            if last_byte & !allowed_mask != 0 {
887                return Err(CodecError::invalid_encoding_for_operation(
888                    CodecKind::FixedBitSet,
889                    operation,
890                    bytes_processed,
891                    InvalidEncodingReason::ValueOutOfRange {
892                        terminal_byte: *last_byte,
893                        allowed_mask,
894                    },
895                ));
896            }
897        }
898
899        Ok(())
900    }
901}
902
903impl<const N: usize> TypeCodec for FixedBitSet<N> {
904    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
905        self.validate(CodecOperation::Write, 0)?;
906        write_all_counted(writer, &self.0, CodecKind::FixedBitSet, 0)
907    }
908
909    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
910        let mut bytes = vec![0; Self::BYTE_LEN];
911        read_exact_counted(reader, &mut bytes, CodecKind::FixedBitSet, 0)?;
912
913        let value = Self(bytes);
914        value.validate(CodecOperation::Read, Self::BYTE_LEN)?;
915        Ok(value)
916    }
917}