//! logic processors and stuff
use std::borrow::Cow;
use std::string::FromUtf8Error;
use crate::block::simple::*;
use crate::data::dynamic::DynType;
use crate::{block::*, Serializable};
use crate::data::{self, CompressError, DataRead, DataWrite};
make_simple!(
MemoryBlock =>
|_, buff: &mut DataRead| {
// format:
// - iterate [`u32`]
// - memory: [`f64`]
let n = buff.read_u32()? as usize;
buff.skip(n * 8)
}
);
pub struct CanvasBlock {
size: u8,
symmetric: bool,
build_cost: BuildCost,
canvas_size: u8,
}
macro_rules! h {
($x:literal) => {{
let v = color_hex::color_from_hex!($x);
(v[0], v[1], v[2])
}};
}
const PALETTE: &[(u8, u8, u8); 8] = &[
h!("#362944"),
h!("#c45d9f"),
h!("#e39aac"),
h!("#f0dab1"),
h!("#6461c2"),
h!("#2ba9b4"),
h!("#93d4b5"),
h!("#f0f6e8"),
];
impl CanvasBlock {
#[must_use]
pub const fn new(size: u8, symmetric: bool, build_cost: BuildCost, canvas_size: u8) -> Self {
assert!(size != 0, "invalid size");
assert!(canvas_size != 0, "invalid size");
Self {
size,
symmetric,
build_cost,
canvas_size,
}
}
state_impl!(pub Image<Vec<u8>, 1>);
}
fn deser_canvas_image(b: &[u8], size: usize) -> Image<Vec<u8>, 1> {
let mut p = Image::alloc(size as u32, size as u32);
for i in 0..(size * size) {
let offset = i * 3;
let mut n = 0;
for i in 0..3 {
let word = (i + offset) >> 3;
n |= (((b[word] & (1 << ((i + offset) & 7))) != 0) as u8) << i;
}
unsafe { p.set_pixel(i as u32 % size as u32, i as u32 / size as u32, [n]) };
}
p
}
impl BlockLogic for CanvasBlock {
impl_block!();
fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> {
Ok(DynData::Empty)
}
fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError> {
match data {
DynData::ByteArray(b) => Ok(Some(Self::create_state(deser_canvas_image(
&b,
self.canvas_size as usize,
)))),
_ => Err(DeserializeError::InvalidType {
have: data.get_type(),
expect: DynType::String,
}),
}
}
fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
let mut o = vec![0; self.canvas_size as usize * self.canvas_size as usize * 3];
let p = Self::get_state(state);
for i in 0..(self.canvas_size * self.canvas_size) as usize {
let index = unsafe {
p.pixel(
i as u32 % self.canvas_size as u32,
i as u32 / self.canvas_size as u32,
)[0]
};
let offset = i * 3;
for i in 0..3 {
let word = (i + offset) >> 3;
if index >> i & 1 == 0 {
o[word] &= !(1 << ((i + offset) & 7));
} else {
o[word] |= 1 << ((i + offset) & 7);
}
}
}
Ok(DynData::ByteArray(o))
}
/// i thought about drawing the borders and stuff but it felt like too much work
fn draw(
&self,
_: &str,
state: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
s: Scale,
) -> ImageHolder<4> {
if let Some(state) = state {
let p = Self::get_state(state);
let offset = match s {
Scale::Full => 7,
// Scale::Half => 3,
Scale::Quarter => 2,
Scale::Eigth => 1,
};
let mut img = Image::alloc(p.width(), p.height());
for ([r, g, b, a], &y) in img.chunked_mut().zip(p.buffer().iter()) {
(*r, *g, *b) = PALETTE[y as usize];
*a = 255;
}
let img = img.as_mut().scale((s * self.size as u32) - offset * 2);
let mut borders = load!("canvas", s);
unsafe {
borders
.borrow_mut()
.overlay_at(&img.as_ref(), offset, offset)
};
return borders;
}
let mut def = Image::alloc(s * self.size as u32, s * self.size as u32);
for [r, g, b, a] in def.chunked_mut() {
(*r, *g, *b) = PALETTE[0];
*a = 255;
}
ImageHolder::from(def)
}
/// format:
/// - len: [`i32`]
/// - read(len) -> [`deser_canvas_image`]
fn read(&self, build: &mut Build, buff: &mut DataRead) -> Result<(), DataReadError> {
let n = buff.read_i32()? as usize;
let mut b = vec![0; n];
buff.read_bytes(&mut b)?;
build.state = Some(Self::create_state(deser_canvas_image(
&b,
self.canvas_size as usize,
)));
Ok(())
}
}
pub struct MessageLogic {
size: u8,
symmetric: bool,
build_cost: BuildCost,
}
impl MessageLogic {
#[must_use]
pub const fn new(size: u8, symmetric: bool, build_cost: BuildCost) -> Self {
assert!(size != 0, "invalid size");
Self {
size,
symmetric,
build_cost,
}
}
state_impl!(pub String);
}
impl BlockLogic for MessageLogic {
impl_block!();
fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> {
Ok(DynData::Empty)
}
fn draw(
&self,
_: &str,
_: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
_: Scale,
) -> ImageHolder<4> {
unreachable!()
}
fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError> {
match data {
DynData::Empty | DynData::String(None) => Ok(Some(Self::create_state(String::new()))),
DynData::String(Some(s)) => Ok(Some(Self::create_state(s))),
_ => Err(DeserializeError::InvalidType {
have: data.get_type(),
expect: DynType::String,
}),
}
}
fn mirror_state(&self, _: &mut State, _: bool, _: bool) {}
fn rotate_state(&self, _: &mut State, _: bool) {}
fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
Ok(DynData::String(Some(Self::get_state(state).clone())))
}
fn read(&self, b: &mut Build, buff: &mut DataRead) -> Result<(), DataReadError> {
b.state = Some(Self::create_state(buff.read_utf()?.to_string()));
Ok(())
}
}
pub struct SwitchLogic {
size: u8,
symmetric: bool,
build_cost: BuildCost,
}
impl SwitchLogic {
#[must_use]
pub const fn new(size: u8, symmetric: bool, build_cost: BuildCost) -> Self {
assert!(size != 0, "invalid size");
Self {
size,
symmetric,
build_cost,
}
}
state_impl!(pub bool);
}
impl BlockLogic for SwitchLogic {
impl_block!();
fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> {
Ok(DynData::Empty)
}
fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError> {
match data {
DynData::Empty => Ok(Some(Self::create_state(true))),
DynData::Boolean(enabled) => Ok(Some(Self::create_state(enabled))),
_ => Err(DeserializeError::InvalidType {
have: data.get_type(),
expect: DynType::Boolean,
}),
}
}
fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
Ok(DynData::Boolean(*Self::get_state(state)))
}
fn read(&self, build: &mut Build, buff: &mut DataRead) -> Result<(), DataReadError> {
build.state = Some(Self::create_state(buff.read_bool()?));
Ok(())
}
fn draw(
&self,
_: &str,
state: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
s: Scale,
) -> ImageHolder<4> {
let mut base = load!("switch", s);
if let Some(state) = state {
if *Self::get_state(state) {
let on = load!("switch-on", s);
unsafe { base.overlay(&on) };
return base;
}
}
base
}
}
pub struct ProcessorLogic {
size: u8,
symmetric: bool,
build_cost: BuildCost,
}
impl ProcessorLogic {
#[must_use]
pub const fn new(size: u8, symmetric: bool, build_cost: BuildCost) -> Self {
assert!(size != 0, "invalid size");
Self {
size,
symmetric,
build_cost,
}
}
state_impl!(pub ProcessorState);
}
fn read_decompressed(buff: &mut DataRead) -> Result<ProcessorState, ProcessorDeserializeError> {
let ver = buff.read_u8()?;
if ver != 1 {
return Err(ProcessorDeserializeError::Version(ver));
}
let code_len = buff.read_u32()? as usize;
if !(0..=500 * 1024).contains(&code_len) {
return Err(ProcessorDeserializeError::CodeLength(code_len));
}
let mut code = vec![0; code_len];
buff.read_bytes(&mut code)?;
let code = String::from_utf8(code)?;
let link_cnt = buff.read_u32()? as usize;
let mut links = Vec::with_capacity(link_cnt);
for _ in 0..link_cnt {
let name = buff.read_utf()?;
let x = buff.read_i16()?;
let y = buff.read_i16()?;
links.push(ProcessorLink {
name: String::from(name),
x,
y,
});
}
Ok(ProcessorState { code, links })
}
impl BlockLogic for ProcessorLogic {
impl_block!();
fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> {
Ok(DynData::Empty)
}
fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError> {
match data {
DynData::Empty => Ok(Some(Self::create_state(ProcessorState::default()))),
DynData::ByteArray(arr) => {
let input = arr.as_ref();
let buff = DataRead::new(input).deflate()?;
Ok(Some(Self::create_state(
ProcessorDeserializeError::forward(read_decompressed(&mut DataRead::new(
&buff,
)))?,
)))
}
_ => Err(DeserializeError::InvalidType {
have: data.get_type(),
expect: DynType::Boolean,
}),
}
}
fn read(&self, b: &mut Build, buff: &mut DataRead) -> Result<(), DataReadError> {
let n = buff.read_u32()? as usize;
let mut v = vec![0; n];
buff.read_bytes(&mut v)?;
v = DataRead::new(&v).deflate().unwrap();
b.state = Some(Self::create_state(
read_decompressed(&mut DataRead::new(&v)).unwrap(),
));
for _ in 0..buff.read_u32()? {
let _ = buff.read_utf()?;
let _ = DynData::deserialize(buff).unwrap();
}
let memory = buff.read_u32()? as usize;
buff.skip(memory * 8)?;
Ok(())
}
fn mirror_state(&self, state: &mut State, horizontally: bool, vertically: bool) {
for link in &mut Self::get_state_mut(state).links {
if horizontally {
link.x = -link.x;
}
if vertically {
link.y = -link.y;
}
}
}
fn rotate_state(&self, state: &mut State, clockwise: bool) {
for link in &mut Self::get_state_mut(state).links {
let (cdx, cdy) = link.get_pos();
link.x = if clockwise { cdy } else { -cdy };
link.y = if clockwise { -cdx } else { cdx };
}
}
fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
let state = Self::get_state(state);
let mut rbuff = DataWrite::default();
ProcessorSerializeError::forward(rbuff.write_u8(1))?;
assert!(state.code.len() < 500 * 1024);
ProcessorSerializeError::forward(rbuff.write_i32(state.code.len() as i32))?;
ProcessorSerializeError::forward(rbuff.write_bytes(state.code.as_bytes()))?;
assert!(state.links.len() < i32::MAX as usize);
ProcessorSerializeError::forward(rbuff.write_i32(state.links.len() as i32))?;
for link in &state.links {
ProcessorSerializeError::forward(rbuff.write_utf(&link.name))?;
ProcessorSerializeError::forward(rbuff.write_i16(link.x))?;
ProcessorSerializeError::forward(rbuff.write_i16(link.y))?;
}
let mut out = DataWrite::default();
rbuff.inflate(&mut out)?;
Ok(DynData::ByteArray(out.consume()))
}
}
#[derive(Debug, thiserror::Error)]
pub enum ProcessorDeserializeError {
#[error("failed to read state data")]
Read(#[from] data::ReadError),
#[error("malformed utf-8 in processor code")]
FromUtf8(#[from] FromUtf8Error),
#[error("unsupported version ({0})")]
Version(u8),
#[error("invalid code length ({0})")]
CodeLength(usize),
}
impl ProcessorDeserializeError {
pub fn forward<T, E: Into<Self>>(result: Result<T, E>) -> Result<T, DeserializeError> {
match result {
Ok(v) => Ok(v),
Err(e) => Err(DeserializeError::Custom(Box::new(e.into()))),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum ProcessorSerializeError {
#[error("failed to write state data")]
Write(#[from] data::WriteError),
#[error(transparent)]
Compress(#[from] CompressError),
}
impl ProcessorSerializeError {
pub fn forward<T, E: Into<Self>>(result: Result<T, E>) -> Result<T, SerializeError> {
match result {
Ok(v) => Ok(v),
Err(e) => Err(SerializeError::Custom(Box::new(e.into()))),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct ProcessorLink {
name: String,
x: i16,
y: i16,
}
impl ProcessorLink {
#[must_use]
pub fn new(name: Cow<'_, str>, x: i16, y: i16) -> Self {
assert!(
u16::try_from(name.len()).is_ok(),
"name too long ({})",
name.len()
);
Self {
name: name.into_owned(),
x,
y,
}
}
#[must_use]
pub fn get_name(&self) -> &str {
&self.name
}
#[must_use]
pub const fn get_pos(&self) -> (i16, i16) {
(self.x, self.y)
}
}
#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct ProcessorState {
code: String,
links: Vec<ProcessorLink>,
}
impl ProcessorState {
#[must_use]
pub fn get_code(&self) -> &str {
&self.code
}
pub fn set_code(&mut self, code: Cow<'_, str>) -> Result<(), CodeError> {
let as_str = &code as &str;
if as_str.len() > 500 * 1024 {
return Err(CodeError::TooLong(as_str.len()));
}
match code {
Cow::Borrowed(s) => {
self.code.clear();
self.code.push_str(s);
}
Cow::Owned(s) => self.code = s,
}
Ok(())
}
#[must_use]
pub fn get_links(&self) -> &[ProcessorLink] {
&self.links
}
pub fn create_link(
&mut self,
mut name: String,
x: i16,
y: i16,
) -> Result<&ProcessorLink, CreateError> {
if name.len() > u16::MAX as usize {
return Err(CreateError::NameLength(name.len()));
}
for curr in &self.links {
if name == curr.name {
return Err(CreateError::DuplicateName(name));
}
if x == curr.x && y == curr.y {
name.clear();
name.push_str(&curr.name);
return Err(CreateError::DuplicatePos { name, x, y });
}
}
let idx = self.links.len();
self.links.push(ProcessorLink { name, x, y });
Ok(&self.links[idx])
}
pub fn add_link(&mut self, link: ProcessorLink) -> Result<&ProcessorLink, CreateError> {
self.create_link(link.name, link.x, link.y)
}
pub fn remove_link(&mut self, idx: usize) -> Option<ProcessorLink> {
if idx < self.links.len() {
Some(self.links.remove(idx))
} else {
None
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub enum CodeError {
#[error("code too long ({0} bytes)")]
TooLong(usize),
}
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub enum CreateError {
#[error("link name too long ({0} bytes)")]
NameLength(usize),
#[error("there is already a link named {0}")]
DuplicateName(String),
#[error("link {name} already points to ({x}, {y})")]
DuplicatePos { name: String, x: i16, y: i16 },
}