Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds some call by id helpers, add a macro for getting string id on init, get string changes #8

Merged
merged 4 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions crates/byondapi-rs-test/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![allow(clippy::missing_safety_doc)]

use byondapi::{
byond_string,
map::{byond_block, byond_length, ByondXYZ},
parse_args,
typecheck_trait::ByondTypeCheck,
Expand Down Expand Up @@ -70,8 +71,7 @@ pub unsafe extern "C" fn test_proc_call(
setup_panic_handler();
let args = parse_args(argc, argv);

// FIXME: Byond will change this in the future
let result = args[0].call("get name", &[]);
let result = args[0].call("get_name", &[]);

match result {
Ok(res) => res,
Expand All @@ -88,6 +88,12 @@ pub unsafe extern "C" fn test_readwrite_var(
let args = parse_args(argc, argv);
let object = &args[0];

object
.read_var_id(byond_string!("name"))
.unwrap()
.get_string()
.unwrap();

match object.read_string("name") {
Ok(res) => res.try_into().unwrap(),
Err(e) => format!("{:#?}", e).try_into().unwrap(),
Expand Down
2 changes: 1 addition & 1 deletion crates/byondapi-rs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "byondapi"
version = "0.2.1"
version = "0.3.0"
authors = ["tigercat2000 <[email protected]>"]
edition = "2021"
description = "Idiomatic Rust bindings for BYONDAPI"
Expand Down
17 changes: 17 additions & 0 deletions crates/byondapi-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,20 @@ inventory::collect!(InitFunc);
///byondapi::inventory::submit! {InitFunc(func)}
///```
pub struct InitFunc(pub fn() -> ());

///This macro caches string ids and returns it instead of doing a stringid lookup everytime
///The macro will panic if the string doesn't already exist on byond init lib
///Example usage:
///```
///byondapi::call_global_id(byond_string!("get_name"),&[]).unwrap()
///```
#[macro_export]
macro_rules! byond_string {
($s:literal) => {{
thread_local! {
static STRING_ID: ::std::cell::OnceCell<u32> = ::std::cell::OnceCell::new();
};
STRING_ID
.with(|cell| *cell.get_or_init(|| ::byondapi::byond_string::str_id_of($s).unwrap()))
}};
}
109 changes: 57 additions & 52 deletions crates/byondapi-rs/src/value/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,59 +32,44 @@ impl ByondValue {

/// Try to get a [`CString`] or fail if this isn't a string type
pub fn get_cstring(&self) -> Result<CString, Error> {
use std::cell::RefCell;
if !self.is_str() {
return Err(Error::NotAString);
}
// add one for le null terminator
let len = self.builtin_length()?.get_number()? as u32 + 1;
let mut buff: Vec<u8> = Vec::with_capacity(len as usize);
let mut capacity = buff.capacity() as u32;
// Safety: buffer capacity is passed to byond, which makes sure it writes in-bound
unsafe {
map_byond_error!(byond().Byond_ToString(
&self.0,
buff.as_mut_ptr().cast(),
&mut capacity
))?

thread_local! {
static BUFFER: RefCell<Vec<u8>> = RefCell::new(Vec::with_capacity(1));
}
// Safety: buffer should be written to at this point, ignoring null terminator
unsafe { buff.set_len(len as usize - 1) };

CString::new(buff).map_err(|_| Error::NonUtf8String)

// use std::cell::RefCell;
// thread_local! {
// static BUFFER: RefCell<Vec<u8>> = RefCell::new(Vec::with_capacity(1));
// }

// let bytes = BUFFER.with_borrow_mut(|buff| -> Result<Vec<u8>, Error> {
// let mut len = buff.capacity() as u32;
// // Safety: buffer capacity is passed to byond, which makes sure it writes in-bound
// let initial_res =
// unsafe { byond().Byond_ToString(&self.0, buff.as_mut_ptr().cast(), &mut len) };
// match (initial_res, len) {
// (false, 1..) => {
// buff.reserve_exact(len as usize - buff.capacity());
// // Safety: buffer capacity is passed to byond, which makes sure it writes in-bound
// unsafe {
// map_byond_error!(byond().Byond_ToString(
// &self.0,
// buff.as_mut_ptr().cast(),
// &mut len
// ))?
// };
// // Safety: buffer should be written to at this point
// unsafe { buff.set_len(len as usize) };
// Ok(std::mem::take(buff))
// }
// (true, _) => {
// // Safety: buffer should be written to at this point
// unsafe { buff.set_len(len as usize) };
// Ok(std::mem::take(buff))
// }
// (false, 0) => Err(Error::get_last_byond_error()),
// }
// })?;

let bytes = BUFFER.with_borrow_mut(|buff| -> Result<Vec<u8>, Error> {
let mut len = buff.capacity() as u32;
// Safety: buffer capacity is passed to byond, which makes sure it writes in-bound
let initial_res =
unsafe { byond().Byond_ToString(&self.0, buff.as_mut_ptr().cast(), &mut len) };
match (initial_res, len) {
(false, 1..) => {
buff.reserve_exact(len as usize);
// Safety: buffer capacity is passed to byond, which makes sure it writes in-bound
unsafe {
map_byond_error!(byond().Byond_ToString(
&self.0,
buff.as_mut_ptr().cast(),
&mut len
))?
};
// Safety: buffer should be written to at this point
unsafe { buff.set_len(len as usize) };
Ok(std::mem::take(buff))
}
(true, _) => {
// Safety: buffer should be written to at this point
unsafe { buff.set_len(len as usize) };
Ok(std::mem::take(buff))
}
(false, 0) => Err(Error::get_last_byond_error()),
}
})?;
CString::from_vec_with_nul(bytes).map_err(|_| Error::NonUtf8String)
}

/// Try to get a [`String`] or fail if this isn't a string type or isn't utf8
Expand Down Expand Up @@ -210,6 +195,9 @@ impl ByondValue {
impl ByondValue {
/// Read a variable through the ref. Fails if this isn't a ref type, or the id is invalid.
pub fn read_var_id(&self, name: u4c) -> Result<ByondValue, Error> {
if self.is_num() || self.is_str() || self.is_ptr() || self.is_null() || self.is_list() {
return Err(Error::NotReferencable);
}
let mut new_value = ByondValue::new();
unsafe {
map_byond_error!(byond().Byond_ReadVarByStrId(&self.0, name, &mut new_value.0))?;
Expand Down Expand Up @@ -263,21 +251,36 @@ impl ByondValue {

/// # Helpers
impl ByondValue {
/// Reads a number through the ref. Fails if this isn't a ref type or this isn't a number.
/// Reads a number from a var. Fails if this isn't a ref type or this isn't a number.
pub fn read_number<T: Into<Vec<u8>>>(&self, name: T) -> Result<f32, Error> {
self.read_var(name)?.get_number()
}

/// Reads a string through the ref. Fails if this isn't a ref type or this isn't a string.
/// Reads a string from a var. Fails if this isn't a ref type or this isn't a string.
pub fn read_string<T: Into<Vec<u8>>>(&self, name: T) -> Result<String, Error> {
self.read_var(name)?.get_string()
}

/// Reads a list through the ref. Fails if this isn't a ref type or this isn't a list.
/// Reads a list from a var. Fails if this isn't a ref type or this isn't a list.
pub fn read_list<T: Into<Vec<u8>>>(&self, name: T) -> Result<Vec<ByondValue>, Error> {
self.read_var(name)?.get_list()
}

/// Reads a number from a var id. Fails if this isn't a ref type or this isn't a number.
pub fn read_number_id(&self, id: u32) -> Result<f32, Error> {
self.read_var_id(id)?.get_number()
}

/// Reads a string from a var id. Fails if this isn't a ref type or this isn't a string.
pub fn read_string_id(&self, id: u32) -> Result<String, Error> {
self.read_var_id(id)?.get_string()
}

/// Reads a list from a var id. Fails if this isn't a ref type or this isn't a list.
pub fn read_list_id(&self, id: u32) -> Result<Vec<ByondValue>, Error> {
self.read_var_id(id)?.get_list()
}

/// Iterates through the assoc values of the list if this value is a list, if the value isn't a list then it returns an error.
/// Non assoc lists will have the second field of the tuple be null
/// (key, value) for proper assoc lists
Expand All @@ -292,6 +295,8 @@ impl ByondValue {
ctr: 1,
})
}

/// Iterates through key values of the list if the list is an assoc list, if not, just iterates through values
pub fn values(&self) -> Result<impl Iterator<Item = ByondValue> + '_, Error> {
if !self.is_list() {
return Err(Error::NotAList);
Expand Down
Loading