#![cfg_attr(test, allow(clippy::float_cmp))] #![deny(rust_2018_idioms, broken_intra_doc_links)]
#![deny(missing_docs)]
pub mod tcp_server;
use cty::c_char;
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct OutputCode(f64);
impl OutputCode {
pub const SUCCESS: OutputCode = OutputCode(1.0);
pub const FAILURE: OutputCode = OutputCode(0.0);
pub const fn custom(code: f64) -> Self {
Self(code)
}
}
impl<T, E> From<Result<T, E>> for OutputCode {
fn from(o: Result<T, E>) -> Self {
if o.is_ok() {
OutputCode::SUCCESS
} else {
OutputCode::FAILURE
}
}
}
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct GmPtr(*const c_char);
impl GmPtr {
pub fn new(ptr: *const c_char) -> Self {
Self(ptr)
}
pub const fn null() -> Self {
Self(core::ptr::null())
}
pub const fn inner(self) -> *const c_char {
self.0
}
pub fn to_str(self) -> Result<&'static str, core::str::Utf8Error> {
unsafe { cstr_core::CStr::from_ptr(self.0) }.to_str()
}
}
impl core::ops::Deref for GmPtr {
type Target = *const c_char;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct GmId(f64);
impl GmId {
#[cfg(test)]
pub const fn new(id: f64) -> Self {
Self(id)
}
pub const fn dummy() -> Self {
Self(f64::MAX)
}
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct GmReal(pub f64);
impl GmReal {
pub const fn new(id: f64) -> Self {
Self(id)
}
pub const fn as_usize(self) -> usize {
self.0 as usize
}
pub const fn as_f64(self) -> f64 {
self.0
}
pub const fn inner(self) -> f64 {
self.0
}
pub const fn dummy() -> Self {
Self(f64::MAX)
}
}
#[derive(Debug)]
pub struct GmBuffer<T: 'static> {
id: GmId,
pub buffer: &'static mut [T],
}
impl<T> GmBuffer<T> {
pub unsafe fn new(gm_id: GmId, gm_ptr: GmPtr, len: usize) -> Self {
let buffer = {
let buf = gm_ptr.inner() as *mut c_char as *mut T;
core::slice::from_raw_parts_mut(buf, len)
};
Self { id: gm_id, buffer }
}
pub fn id(self) -> GmId {
self.id
}
}
impl<T> core::ops::Index<usize> for GmBuffer<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
&self.buffer[index]
}
}
impl<T> core::ops::IndexMut<usize> for GmBuffer<T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.buffer[index]
}
}
pub struct Bridge(GmBuffer<u32>);
impl Bridge {
pub fn new(buf: GmBuffer<u32>) -> Self {
debug_assert!(
buf.buffer.len() >= 256,
"your backing buffer needs to be at least 256 bytes"
);
Self(buf)
}
pub fn writer(&mut self) -> BridgeWriter<'_> {
BridgeWriter::new(self)
}
}
pub struct BridgeWriter<'a>(&'a mut Bridge, usize);
impl<'a> BridgeWriter<'a> {
fn new(bridge: &'a mut Bridge) -> Self {
Self(bridge, 0)
}
pub fn write_u32(&mut self, value: u32) {
self.0 .0[self.1] = value;
self.1 += 1;
}
pub fn write_f32(&mut self, value: f32) {
self.0 .0[self.1] = value.to_bits();
self.1 += 1;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn make_string_ptr() {
GmPtr::new("Hello, world!\0".as_ptr() as *const cty::c_char);
}
#[test]
fn read_string_ptr() {
let ptr = GmPtr::new("Hello, world!\0".as_ptr() as *const cty::c_char);
let out = ptr.to_str().unwrap();
assert_eq!(out, "Hello, world!");
}
#[test]
fn bridge() {
let buf = vec![0u32; 256];
let gm_ptr = GmPtr::new(buf.as_ptr() as *const _);
let mut bridge = unsafe { Bridge::new(GmBuffer::new(GmId::new(0.0), gm_ptr, 256)) };
let mut writer = bridge.writer();
writer.write_u32(18);
writer.write_f32(4.2);
assert_eq!(buf[0], 18);
assert_eq!(f32::from_bits(buf[1]), 4.2);
let mut writer = bridge.writer();
writer.write_f32(44.3);
writer.write_f32(22.2);
assert_eq!(f32::from_bits(buf[0]), 44.3);
assert_eq!(f32::from_bits(buf[1]), 22.2);
}
}