use crate::wasm_common::{new_maze, to_cell_type_enum, MazeWasm};
use data_model::MazePoint;
use maze::{MazeSolver, MazeSolution};
use std::alloc::{alloc, dealloc, Layout};
use std::ptr;
#[no_mangle]
pub extern "C" fn new_maze_wasm() -> *mut MazeWasm {
let maze = Box::new(MazeWasm { maze: new_maze() });
increment_num_objects_allocated();
Box::into_raw(maze)
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn free_maze_wasm(maze_wasm: *mut MazeWasm) {
if !maze_wasm.is_null() {
unsafe {
let _ = Box::from_raw(maze_wasm); decrement_num_objects_allocated();
}
}
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn maze_wasm_get_row_count(maze_wasm: *mut MazeWasm) -> u32 {
let maze_wasm = unsafe { &*maze_wasm };
maze_wasm.maze.definition.row_count() as u32
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn maze_wasm_get_col_count(maze_wasm: *mut MazeWasm) -> u32 {
let maze_wasm = unsafe { &*maze_wasm };
maze_wasm.maze.definition.col_count() as u32
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn maze_wasm_get_cell_type(maze_wasm: *mut MazeWasm, row: u32, col: u32) -> u32 {
let maze_wasm = unsafe { &*maze_wasm };
let row = row as usize;
let col = col as usize;
if row >= maze_wasm.maze.definition.row_count() {
return create_maze_wasm_error_result(Some(&format!("row index ({}) out of bounds", row)));
}
if col >= maze_wasm.maze.definition.col_count() {
return create_maze_wasm_error_result(Some(&format!(
"column index ({}) out of bounds",
col
)));
}
create_maze_wasm_enum_result(to_cell_type_enum(maze_wasm.maze.definition.grid[row][col]) as u32)
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn maze_wasm_set_start_cell(
maze_wasm: *mut MazeWasm,
start_row: u32,
start_col: u32,
) -> u32 {
let maze_wasm = unsafe { &mut *maze_wasm };
let mut error_ptr: u32 = 0;
if let Err(error) = maze_wasm.maze.definition.set_start(Some(MazePoint {
row: start_row as usize,
col: start_col as usize,
})) {
error_ptr = create_maze_wasm_error_ptr(error.to_string().as_str());
}
error_ptr
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn maze_wasm_get_start_cell(maze_wasm: *mut MazeWasm) -> u32 {
let maze_wasm = unsafe { &*maze_wasm };
if let Some(start) = maze_wasm.maze.definition.get_start() {
return create_maze_wasm_point_result(&start);
}
create_maze_wasm_error_result(Some("no start cell defined"))
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn maze_wasm_set_finish_cell(
maze_wasm: *mut MazeWasm,
finish_row: u32,
finish_col: u32,
) -> u32 {
let maze_wasm = unsafe { &mut *maze_wasm };
let mut error_ptr: u32 = 0;
if let Err(error) = maze_wasm.maze.definition.set_finish(Some(MazePoint {
row: finish_row as usize,
col: finish_col as usize,
})) {
error_ptr = create_maze_wasm_error_ptr(error.to_string().as_str());
}
error_ptr
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn maze_wasm_get_finish_cell(maze_wasm: *mut MazeWasm) -> u32 {
let maze_wasm = unsafe { &*maze_wasm };
if let Some(finish) = maze_wasm.maze.definition.get_finish() {
return create_maze_wasm_point_result(&finish);
}
create_maze_wasm_error_result(Some("no finish cell defined"))
}
fn set_cell_values(
maze_wasm: *mut MazeWasm,
start_row: u32,
start_col: u32,
end_row: u32,
end_col: u32,
modify_char: char,
) -> u32 {
let maze_wasm = unsafe { &mut *maze_wasm };
let mut error_ptr: u32 = 0;
if let Err(error) = maze_wasm.maze.definition.set_value(
MazePoint {
row: start_row as usize,
col: start_col as usize,
},
MazePoint {
row: end_row as usize,
col: end_col as usize,
},
modify_char,
) {
error_ptr = create_maze_wasm_error_ptr(error.to_string().as_str());
}
error_ptr
}
#[no_mangle]
pub extern "C" fn maze_wasm_set_wall_cells(
maze_wasm: *mut MazeWasm,
start_row: u32,
start_col: u32,
end_row: u32,
end_col: u32,
) -> u32 {
set_cell_values(maze_wasm, start_row, start_col, end_row, end_col, 'W')
}
#[no_mangle]
pub extern "C" fn maze_wasm_clear_cells(
maze_wasm: *mut MazeWasm,
start_row: u32,
start_col: u32,
end_row: u32,
end_col: u32,
) -> u32 {
set_cell_values(maze_wasm, start_row, start_col, end_row, end_col, ' ')
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn maze_wasm_resize(
maze_wasm: *mut MazeWasm,
new_row_count: u32,
new_col_count: u32,
) {
let maze_wasm = unsafe { &mut *maze_wasm };
maze_wasm
.maze
.definition
.resize(new_row_count as usize, new_col_count as usize);
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn maze_wasm_reset(maze_wasm: *mut MazeWasm) {
let maze_wasm = unsafe { &mut *maze_wasm };
maze_wasm.maze.definition.reset();
}
#[repr(C)]
pub struct MazeWasmError {
message_ptr: u32,
}
#[no_mangle]
pub extern "C" fn free_maze_wasm_error(error_ptr: u32) {
let error_ptr = error_ptr as *mut MazeWasmError;
if !error_ptr.is_null() {
unsafe {
if (*error_ptr).message_ptr != 0 {
free_sized_memory((*error_ptr).message_ptr as *mut u8);
(*error_ptr).message_ptr = 0;
}
let _ = Box::from_raw(error_ptr);
decrement_num_objects_allocated();
}
}
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn maze_wasm_insert_rows(
maze_wasm: *mut MazeWasm,
start_row: u32,
count: u32,
) -> u32 {
let mut error_ptr: u32 = 0;
let maze_wasm = unsafe { &mut *maze_wasm };
if let Err(error) = maze_wasm
.maze
.definition
.insert_rows(start_row as usize, count as usize)
{
error_ptr = create_maze_wasm_error_ptr(error.to_string().as_str());
}
error_ptr
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn maze_wasm_delete_rows(
maze_wasm: *mut MazeWasm,
start_row: u32,
count: u32,
) -> u32 {
let mut error_ptr: u32 = 0;
let maze_wasm = unsafe { &mut *maze_wasm };
if let Err(error) = maze_wasm
.maze
.definition
.delete_rows(start_row as usize, count as usize)
{
error_ptr = create_maze_wasm_error_ptr(error.to_string().as_str());
}
error_ptr
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub fn maze_wasm_insert_cols(maze_wasm: *mut MazeWasm, start_col: u32, count: u32) -> u32 {
let mut error_ptr: u32 = 0;
let maze_wasm = unsafe { &mut *maze_wasm };
if let Err(error) = maze_wasm
.maze
.definition
.insert_cols(start_col as usize, count as usize)
{
error_ptr = create_maze_wasm_error_ptr(error.to_string().as_str());
}
error_ptr
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub fn maze_wasm_delete_cols(maze_wasm: *mut MazeWasm, start_col: u32, count: u32) -> u32 {
let mut error_ptr: u32 = 0;
let maze_wasm = unsafe { &mut *maze_wasm };
if let Err(error) = maze_wasm
.maze
.definition
.delete_cols(start_col as usize, count as usize)
{
error_ptr = create_maze_wasm_error_ptr(error.to_string().as_str());
}
error_ptr
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub fn maze_wasm_is_empty(maze_wasm: *mut MazeWasm) -> bool {
let maze_wasm = unsafe { &mut *maze_wasm };
maze_wasm.maze.definition.is_empty()
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub fn maze_wasm_from_json(maze_wasm: *mut MazeWasm, json_string_ptr: *mut u8) -> u32 {
let mut error_ptr: u32 = 0;
let maze_wasm = unsafe { &mut *maze_wasm };
let json_str = ptr_to_string(json_string_ptr);
if let Err(error) = maze_wasm.maze.from_json(&json_str) {
error_ptr = create_maze_wasm_error_ptr(error.to_string().as_str());
}
error_ptr
}
#[cfg_attr(not(feature = "wasm-bindgen"), repr(C))]
pub enum MazeWasmResultValueType {
None = 0,
String = 1,
Enum = 2,
Point = 3,
Solution = 4,
}
fn to_maze_result_value_type(value_type: u8) -> MazeWasmResultValueType {
match value_type {
1 => MazeWasmResultValueType::String,
2 => MazeWasmResultValueType::Enum,
3 => MazeWasmResultValueType::Point,
4 => MazeWasmResultValueType::Solution,
_ => MazeWasmResultValueType::None,
}
}
#[repr(C)]
pub struct MazeWasmResult {
value_type: u8,
value_ptr: u32,
error_ptr: u32,
}
fn to_maze_wasm_result_ptr(value_type: u8, value_ptr: u32, error: Option<&str>) -> u32 {
let mut error_ptr: u32 = 0;
if let Some(error_str) = error {
error_ptr = create_maze_wasm_error_ptr(error_str);
}
let result = MazeWasmResult {
value_type,
value_ptr,
error_ptr,
};
let boxed_result = Box::new(result);
increment_num_objects_allocated();
Box::into_raw(boxed_result) as u32
}
fn create_maze_wasm_string_result(value: &str) -> u32 {
to_maze_wasm_result_ptr(
MazeWasmResultValueType::String as u8,
to_string_ptr(value),
None,
)
}
fn create_maze_wasm_enum_result(value: u32) -> u32 {
to_maze_wasm_result_ptr(MazeWasmResultValueType::Enum as u8, value, None)
}
fn create_maze_wasm_point_result(point: &MazePoint) -> u32 {
to_maze_wasm_result_ptr(
MazeWasmResultValueType::Point as u8,
to_point_ptr(point),
None,
)
}
fn create_maze_wasm_solution_result(solution: MazeSolution) -> u32 {
to_maze_wasm_result_ptr(
MazeWasmResultValueType::Solution as u8,
to_solution_ptr(solution),
None,
)
}
fn create_maze_wasm_error_result(error: Option<&str>) -> u32 {
to_maze_wasm_result_ptr(MazeWasmResultValueType::None as u8, 0, error)
}
#[no_mangle]
pub extern "C" fn free_maze_wasm_result(result_ptr: u32, free_value_ptr: bool) {
let result_ptr = result_ptr as *mut MazeWasmResult;
if !result_ptr.is_null() {
unsafe {
if free_value_ptr && (*result_ptr).value_ptr != 0 {
match to_maze_result_value_type((*result_ptr).value_type) {
MazeWasmResultValueType::String | MazeWasmResultValueType::Point => {
free_sized_memory((*result_ptr).value_ptr as *mut u8);
}
MazeWasmResultValueType::Solution => {
free_maze_wasm_solution((*result_ptr).value_ptr as *mut MazeSolution);
}
_ => {}
}
(*result_ptr).value_ptr = 0;
}
if (*result_ptr).error_ptr != 0 {
free_maze_wasm_error((*result_ptr).error_ptr);
(*result_ptr).error_ptr = 0;
}
let _ = Box::from_raw(result_ptr);
decrement_num_objects_allocated();
}
}
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn maze_wasm_to_json(maze_wasm: *mut MazeWasm) -> u32 {
let maze_wasm = unsafe { &mut *maze_wasm };
match maze_wasm.maze.to_json() {
Err(error) => create_maze_wasm_error_result(Some(error.to_string().as_str())),
Ok(json_str) => create_maze_wasm_string_result(json_str.as_str()),
}
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn maze_wasm_solve(maze_wasm: *mut MazeWasm) -> u32 {
let maze_wasm = unsafe { &mut *maze_wasm };
match maze_wasm.maze.solve() {
Err(error) => create_maze_wasm_error_result(Some(error.to_string().as_str())),
Ok(solution) => create_maze_wasm_solution_result(solution),
}
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn maze_wasm_solution_get_path_points(solution: *mut MazeSolution) -> u32 {
let solution = unsafe { &mut *solution };
let num_points = solution.path.points.len();
let length = 4 + num_points * 8;
let mem_ptr = allocate_sized_memory(length);
unsafe {
let mut points_data_ptr = mem_ptr.add(4);
ptr::write(points_data_ptr as *mut u32, num_points as u32);
points_data_ptr = points_data_ptr.add(4);
for point in &solution.path.points {
points_data_ptr = write_point(points_data_ptr, point);
}
mem_ptr as u32
}
}
fn create_maze_wasm_error_ptr(error_str: &str) -> u32 {
let error = MazeWasmError {
message_ptr: to_string_ptr(error_str),
};
let boxed_error = Box::new(error);
increment_num_objects_allocated();
Box::into_raw(boxed_error) as u32
}
fn to_string_ptr(str: &str) -> u32 {
let length = str.len();
let string_ptr = allocate_sized_memory(length);
unsafe {
let string_data_ptr = string_ptr.add(4);
ptr::copy_nonoverlapping(str.as_ptr(), string_data_ptr, length);
}
string_ptr as u32
}
fn to_point_ptr(point: &MazePoint) -> u32 {
let point_ptr = allocate_sized_memory(8);
unsafe {
let point_data_ptr = point_ptr.add(4);
let _ = write_point(point_data_ptr, point);
}
point_ptr as u32
}
fn write_point(ptr: *mut u8, point: &MazePoint) -> *mut u8 {
unsafe {
ptr::write(ptr as *mut u32, point.row as u32);
let ptr = ptr.add(4);
ptr::write(ptr as *mut u32, point.col as u32);
ptr.add(4)
}
}
fn to_solution_ptr(solution: MazeSolution) -> u32 {
let boxed_solution = Box::new(solution);
increment_num_objects_allocated();
Box::into_raw(boxed_solution) as u32
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn free_maze_wasm_solution(solution_ptr: *mut MazeSolution) {
if !solution_ptr.is_null() {
unsafe {
let _ = Box::from_raw(solution_ptr);
decrement_num_objects_allocated();
}
}
}
fn ptr_length(ptr: *const u8) -> usize {
let mut length: usize = 0;
if !ptr.is_null() {
length = unsafe { ptr::read(ptr as *const u32) as usize };
}
length
}
fn ptr_to_string(ptr: *const u8) -> String {
let length = ptr_length(ptr);
let string_slice = unsafe {
let data_ptr = ptr.add(4); let slice = std::slice::from_raw_parts(data_ptr, length);
std::str::from_utf8(slice).unwrap()
};
string_slice.to_string()
}
static mut TOTAL_SIZED_MEM_USED: i64 = 0;
#[no_mangle]
pub extern "C" fn get_sized_memory_used() -> i64 {
unsafe { TOTAL_SIZED_MEM_USED }
}
#[no_mangle]
pub extern "C" fn allocate_sized_memory(size: usize) -> *mut u8 {
let total_size = size + 4;
let layout = Layout::from_size_align(total_size, 1).unwrap();
let ptr = unsafe { alloc(layout) };
if ptr.is_null() {
return std::ptr::null_mut();
}
unsafe {
TOTAL_SIZED_MEM_USED += total_size as i64;
}
unsafe {
ptr::write(ptr as *mut u32, size as u32);
}
ptr
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn free_sized_memory(ptr: *mut u8) {
if !ptr.is_null() {
unsafe {
let size = ptr::read(ptr as *const u32) as usize;
let total_size = size + 4;
let layout = Layout::from_size_align(total_size, 1).unwrap();
dealloc(ptr, layout);
TOTAL_SIZED_MEM_USED -= total_size as i64;
}
}
}
static mut TOTAL_NUM_OBJECTS_ALLOCATED: i64 = 0;
#[no_mangle]
pub extern "C" fn get_num_objects_allocated() -> i64 {
unsafe { TOTAL_NUM_OBJECTS_ALLOCATED }
}
fn increment_num_objects_allocated() {
unsafe {
TOTAL_NUM_OBJECTS_ALLOCATED += 1;
}
}
fn decrement_num_objects_allocated() {
unsafe {
TOTAL_NUM_OBJECTS_ALLOCATED -= 1;
}
}