1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
//! C/C++ API for BVE-Reborn high performance libraries. //! //! This library isn't made to be used directly from rust and can actually create UB fairly easily if used from Rust. //! C/C++ Headers will be generated in `bve-native/include` by `bve-build`. //! //! Documentation is automatically copied to the C headers, but is missing module documentation. Therefore it is //! recommended to use the Rust documentation for this library while writing C/C++. Attempts have been made to make //! it as easy as possible to figure out what C/C++ code to write from the Rust documentation. //! //! While code is separated into modules in the rust documentation, all functions and types are exported to c++ in //! global scope with no name mangling. //! //! The modules correspond 1:1 with the modules in the `bve` crate. //! //! # Use Warning //! //! ***DO NOT CALL ANY OTHER FUNCTION BEFORE YOU CALL INIT***. //! //! Libraries ***must*** be initialized by calling [`bve_init`] before calling anything else. The library //! does not need to be de-initialized. If this is not done, panics may propagate beyond the C -> Rust boundary, //! leading to undefined behavior. //! //! # API Basics //! //! The API tries to be as consistent and predictable as possible. //! //! Structs are all prefixed with `BVE_` in C mode to help eliminate conflicts. C++ code is all in the `bve` namespace. //! Type names are kept as short as is reasonable, while still having clear connection with the underlying rust code. //! //! Free functions have their rust path loosely encoded in the name. For example [`bve::parse::mesh::mesh_from_str`] is //! [`bve_parse_mesh_from_string`](parse::mesh::bve_parse_mesh_from_string). Duplicate names are removed and idioms are //! changed to be comprehensible from the interface language. //! //! Free functions that are acting like member functions come in the form `BVE_Struct_Name_member_function_name`. They //! take their first argument by pointer. //! //! All pointers are assumed to not take ownership, unless otherwise specified. Non-obvious lifespans will //! be noted in documentation. // Rust warnings #![warn(unused)] #![deny(future_incompatible)] #![deny(nonstandard_style)] #![deny(rust_2018_idioms)] #![allow(non_snake_case)] // Naming in FFI is weird #![allow(non_camel_case_types)] // Naming in FFI is weird #![allow(unsafe_code)] // We're doing FFI // Rustdoc Warnings #![deny(intra_doc_link_resolution_failure)] // Clippy warnings #![warn(clippy::cargo)] #![warn(clippy::nursery)] #![warn(clippy::pedantic)] #![warn(clippy::restriction)] // Annoying regular clippy warnings #![allow(clippy::cast_sign_loss)] // Annoying #![allow(clippy::cast_precision_loss)] // Annoying #![allow(clippy::cast_possible_truncation)] // Annoying #![allow(clippy::cognitive_complexity)] // This is dumb #![allow(clippy::too_many_lines)] // This is also dumb // Annoying/irrelevant clippy Restrictions #![allow(clippy::as_conversions)] #![allow(clippy::crosspointer_transmute)] // Useful for nasty ffi crap #![allow(clippy::decimal_literal_representation)] #![allow(clippy::else_if_without_else)] #![allow(clippy::float_arithmetic)] #![allow(clippy::float_cmp_const)] #![allow(clippy::implicit_return)] #![allow(clippy::indexing_slicing)] #![allow(clippy::integer_arithmetic)] #![allow(clippy::integer_division)] #![allow(clippy::let_underscore_must_use)] #![allow(clippy::match_bool)] // prettier #![allow(clippy::mem_forget)] // Useful for FFI #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::missing_inline_in_public_items)] #![allow(clippy::module_name_repetitions)] #![allow(clippy::multiple_crate_versions)] // Cargo deny's job #![allow(clippy::non_ascii_literal)] #![allow(clippy::option_expect_used)] #![allow(clippy::panic)] #![allow(clippy::result_expect_used)] #![allow(clippy::shadow_reuse)] #![allow(clippy::shadow_same)] #![allow(clippy::unreachable)] #![allow(clippy::wildcard_enum_match_arm)] pub mod filesystem; pub mod panic; use std::borrow::Cow; use std::ffi::{CStr, CString}; use std::os::raw::*; use std::ptr::null_mut; /// C safe wrapper for an [`Option`]. /// /// # Safety /// /// Reading from the `value` member is undefined behavior if `exists` is false. In practice it is zeroed. #[repr(C)] pub struct COption<T> { /// Actual value inside the option. Reading this is undefined behavior if exists is false. pub value: T, /// Flag if the value exists or not. pub exists: bool, } impl<T, U> From<Option<U>> for COption<T> where U: Into<T>, { #[inline] #[must_use] fn from(other: Option<U>) -> Self { match other { Some(v) => Self { value: v.into(), exists: true, }, None => Self { value: unsafe { std::mem::zeroed() }, exists: false, }, } } } impl<T> Into<Option<T>> for COption<T> { #[inline] #[must_use] fn into(self) -> Option<T> { match self.exists { true => Some(self.value), false => None, } } } /// C safe wrapper for a [`Vec`]. /// /// # Safety /// /// - Modifying the contents in the array is valid. /// - Increasing `count` such that `count <= capacity` is valid. /// - Do not manually delete/realloc the pointer. Must use the deleter for the container where this vector was found. #[repr(C)] pub struct CVector<T> { /// Ptr to the array of elements pub ptr: *mut T, /// Count of elements, do not run beyond this amount pub count: libc::size_t, /// Capacity of the underlying buffer pub capacity: libc::size_t, } impl<T, U> From<Vec<U>> for CVector<T> where U: Into<T>, { #[inline] #[must_use] fn from(other: Vec<U>) -> Self { let mut converted: Vec<T> = other.into_iter().map(std::convert::Into::into).collect(); let ret = Self { ptr: converted.as_mut_ptr(), count: converted.len(), capacity: converted.capacity(), }; std::mem::forget(converted); ret } } impl<T, U> Into<Vec<U>> for CVector<T> where T: Into<U>, { #[inline] #[must_use] fn into(self) -> Vec<U> { let converted: Vec<T> = unsafe { Vec::from_raw_parts(self.ptr, self.count, self.capacity) }; let other: Vec<U> = converted.into_iter().map(std::convert::Into::into).collect(); other } } /// Initialize the runtime functionality of BVE. Initializes minimal global state to make the rest /// of the API safe to call. ***DO NOT CALL ANY OTHER FUNCTION BEFORE YOU CALL INIT***. /// /// This function is not protected against panics as it must not panic due to the handler not being set up. /// /// May be called multiple times, but all global state will be reset. #[no_mangle] pub extern "C" fn bve_init() { panic::init_panic_handler(); } /// Converts a given non-owning slice to a owning C pointer fn string_to_owned_ptr(input: &str) -> *mut c_char { CString::new(input).map(CString::into_raw).unwrap_or(null_mut()) } /// Consumes the given owning pointer and converts it to a rust string. /// /// # Safety /// /// - `input` **ASSUMES OWNERSHIP** must be a valid pointer. It must be zero terminated. unsafe fn owned_ptr_to_string(input: *const c_char) -> String { // This cast is valid as the underlying data will never be changed // Either way we own it and it's being destroyed. CString::from_raw(input as *mut c_char).to_string_lossy().into() } /// Translates a non-owning pointer to a rust str. If it is not valid utf8, it is converted to /// being owning through [`Cow`]. /// /// # Safety /// /// - `input` must be a valid pointer. It must be zero terminated. unsafe fn unowned_ptr_to_str(input: &*const c_char) -> Cow<'_, str> { CStr::from_ptr(*input).to_string_lossy() } /// Takes an owning pointer to a rust-generated string and deletes it. /// /// # Safety /// /// - `ptr` **ASSUMES OWNERSHIP** must be a valid pointer and the string must have been allocated in Rust. It must be /// zero terminated. #[bve_derive::c_interface] pub unsafe extern "C" fn bve_delete_string(ptr: *mut c_char) { CString::from_raw(ptr); } pub mod parse;