1use bytes::{BufMut, BytesMut};
8
9use crate::codec::{DecodeResult, EncodeResult, Reader};
10use crate::error::EncodeError;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
14pub struct BlockPos {
15 pub x: i32,
17 pub y: i32,
19 pub z: i32,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum PositionLayout {
26 Xyz,
28 Xzy,
30}
31
32#[inline]
33const fn sign_extend(v: u64, bits: u32) -> i32 {
34 let shift = 64 - bits;
35 (((v << shift) as i64) >> shift) as i32
36}
37
38#[inline]
39const fn fits(v: i32, bits: u32) -> bool {
40 let min = -(1i64 << (bits - 1));
41 let max = (1i64 << (bits - 1)) - 1;
42 (v as i64) >= min && (v as i64) <= max
43}
44
45impl BlockPos {
46 #[inline]
48 pub const fn new(x: i32, y: i32, z: i32) -> Self {
49 BlockPos { x, y, z }
50 }
51
52 #[inline]
54 pub const fn unpack(v: u64, layout: PositionLayout) -> Self {
55 match layout {
56 PositionLayout::Xyz => BlockPos {
57 x: sign_extend(v >> 38, 26),
58 y: sign_extend((v >> 26) & 0xfff, 12),
59 z: sign_extend(v & 0x3ff_ffff, 26),
60 },
61 PositionLayout::Xzy => BlockPos {
62 x: sign_extend(v >> 38, 26),
63 z: sign_extend((v >> 12) & 0x3ff_ffff, 26),
64 y: sign_extend(v & 0xfff, 12),
65 },
66 }
67 }
68
69 #[inline]
71 pub const fn is_packable(&self) -> bool {
72 fits(self.x, 26) && fits(self.y, 12) && fits(self.z, 26)
73 }
74
75 #[inline]
77 pub fn pack(&self, layout: PositionLayout) -> Result<u64, EncodeError> {
78 if !self.is_packable() {
79 return Err(EncodeError::OutOfRange("position"));
80 }
81 let x = (self.x as u64) & 0x3ff_ffff;
82 let y = (self.y as u64) & 0xfff;
83 let z = (self.z as u64) & 0x3ff_ffff;
84 Ok(match layout {
85 PositionLayout::Xyz => (x << 38) | (y << 26) | z,
86 PositionLayout::Xzy => (x << 38) | (z << 12) | y,
87 })
88 }
89
90 #[inline]
92 pub fn read(r: &mut Reader<'_>, layout: PositionLayout) -> DecodeResult<Self> {
93 Ok(Self::unpack(r.read_u64()?, layout))
94 }
95
96 #[inline]
98 pub fn write(&self, w: &mut BytesMut, layout: PositionLayout) -> EncodeResult {
99 w.put_u64(self.pack(layout)?);
100 Ok(())
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use bytes::Bytes;
108
109 #[test]
110 fn known_answers() {
111 let p = BlockPos::new(18357644, 831, -20882616);
113 let z = (-20882616i64 as u64) & 0x3ff_ffff;
114 let xyz = (0x118_1d8cu64 << 38) | (0x33fu64 << 26) | z;
115 let xzy = (0x118_1d8cu64 << 38) | (z << 12) | 0x33f;
116 assert_eq!(p.pack(PositionLayout::Xyz), Ok(xyz));
117 assert_eq!(p.pack(PositionLayout::Xzy), Ok(xzy));
118 assert_eq!(BlockPos::unpack(xyz, PositionLayout::Xyz), p);
119 assert_eq!(BlockPos::unpack(xzy, PositionLayout::Xzy), p);
120
121 let m = BlockPos::new(-1, -1, -1);
123 assert_eq!(m.pack(PositionLayout::Xyz), Ok(u64::MAX));
124 assert_eq!(m.pack(PositionLayout::Xzy), Ok(u64::MAX));
125
126 let mut w = BytesMut::new();
128 BlockPos::new(1, 2, 3)
129 .write(&mut w, PositionLayout::Xyz)
130 .unwrap();
131 assert_eq!(&w[..], &[0x00, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x03]);
132 let b = Bytes::from(w.to_vec());
133 assert_eq!(
134 BlockPos::read(&mut Reader::new(&b), PositionLayout::Xyz),
135 Ok(BlockPos::new(1, 2, 3))
136 );
137 let mut w = BytesMut::new();
139 BlockPos::new(1, 2, 3)
140 .write(&mut w, PositionLayout::Xzy)
141 .unwrap();
142 assert_eq!(&w[..], &[0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x30, 0x02]);
143 }
144
145 #[test]
146 fn range_checks() {
147 assert!(BlockPos::new(33554431, 2047, -33554432).is_packable());
148 assert_eq!(
149 BlockPos::new(33554432, 0, 0).pack(PositionLayout::Xyz),
150 Err(EncodeError::OutOfRange("position"))
151 );
152 assert!(BlockPos::new(0, 2048, 0).pack(PositionLayout::Xzy).is_err());
153 assert!(
154 BlockPos::new(0, -2049, 0)
155 .pack(PositionLayout::Xzy)
156 .is_err()
157 );
158 }
159}