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;