/*
* Copyright (c) 2024 Andrew Brower.
* This file is part of Crawlspace.
*
* Crawlspace is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* Crawlspace is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with Crawlspace. If not, see
* .
*/
use bitfield_struct::bitfield;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use thiserror::Error;
use crate::{ErrorKind, Read, Write};
#[derive(Debug)]
pub struct Position {
pub x: i32,
pub y: i32,
pub z: i32,
}
#[bitfield(u64)]
pub struct PackedPosition {
#[bits(26)]
pub x: i32,
#[bits(26)]
pub z: i32,
#[bits(12)]
pub y: i32,
}
impl Position {
#[must_use]
pub fn new(x: i32, y: i32, z: i32) -> Self {
Self { x, y, z }
}
}
#[derive(Debug, Error)]
pub enum EncodingError {
#[error("value is out of bounds")]
OutOfBounds,
}
impl TryFrom<&Position> for PackedPosition {
type Error = EncodingError;
fn try_from(value: &Position) -> std::result::Result {
match (value.x, value.y, value.z) {
(-0x2000000..=0x1ffffff, -0x800..=0x7ff, -0x2000000..=0x1ffffff) => {
Ok(PackedPosition::new()
.with_x(value.x)
.with_y(value.y)
.with_z(value.z))
}
_ => Err(EncodingError::OutOfBounds),
}
}
}
impl From for Position {
fn from(value: PackedPosition) -> Self {
Self {
x: value.x(),
y: value.y(),
z: value.z(),
}
}
}
impl Write for Position {
fn write(&self, w: &mut impl std::io::Write) -> Result<(), ErrorKind> {
let encoded: PackedPosition = self
.try_into()
.map_err(|_| ErrorKind::InvalidData("Invalid packed position".to_string()))?;
encoded.write(w)
}
}
impl Read for Position {
fn read(r: &mut impl std::io::Read) -> Result {
let bytes = r.read_i64::()?;
Ok(Self {
x: (bytes >> 38) as i32,
y: (bytes << 52 >> 52) as i32,
z: (bytes << 26 >> 38) as i32,
})
}
}
impl Write for PackedPosition {
fn write(&self, w: &mut impl std::io::Write) -> Result<(), ErrorKind> {
Ok(w.write_u64::(self.0)?)
}
}