native-tools/src/lib.rs

156 lines
3.5 KiB
Rust

#![no_std]
#![feature(default_alloc_error_handler, lang_items)]
#![feature(vec_into_raw_parts)]
#![allow(clippy::not_unsafe_ptr_arg_deref)]
#[cfg(test)]
mod test;
mod system;
mod win_c;
extern crate core;
extern crate alloc;
use libc::c_char;
use alloc::{
borrow::ToOwned,
boxed::Box,
string::String,
vec::Vec,
};
use cstr_core::{CStr, CString};
use unicode_segmentation::UnicodeSegmentation;
use crate::system::System;
#[global_allocator]
static ALLOC: System = System;
#[repr(C)]
pub struct RawVec {
pointer: *mut *mut c_char,
length: u32,
capacity: u32,
}
#[no_mangle]
pub extern "C" fn wrap(raw_input: *const c_char, width: u32) -> *mut RawVec {
if raw_input.is_null() {
return core::ptr::null::<RawVec>() as *mut _;
}
let c_input = unsafe {
CStr::from_ptr(raw_input)
};
let input = match c_input.to_str() {
Ok(x) => x,
Err(_) => return core::ptr::null::<RawVec>() as *mut _,
};
let lines = inner_wrap(input, width);
let raw_lines: Vec<*mut c_char> = lines.into_iter()
.map(|line| CString::new(line).unwrap().into_raw())
.collect();
let (pointer, length, capacity) = raw_lines.into_raw_parts();
let raw = RawVec {
pointer,
length: length as u32,
capacity: capacity as u32,
};
Box::into_raw(Box::new(raw))
}
#[no_mangle]
pub extern "C" fn wrap_free(raw: *mut RawVec) {
if raw.is_null() {
return;
}
unsafe {
let raw = Box::from_raw(raw);
let strings = Vec::from_raw_parts(raw.pointer, raw.length as usize, raw.capacity as usize);
for raw_string in strings {
CString::from_raw(raw_string);
}
}
}
pub(crate) fn inner_wrap(input: &str, width: u32) -> Vec<String> {
let width = width as usize;
let mut strings = Vec::new();
let mut last_break = 0;
let mut last_idx = 0;
for (idx, _) in unicode_linebreak::linebreaks(input) {
if idx == input.len() {
continue;
}
if idx > last_break + width {
let segment = input[last_break..last_idx].trim();
if segment.len() <= width {
strings.push(segment.to_owned());
} else {
hard_break(&mut strings, segment, width);
}
last_break = last_idx;
}
last_idx = idx;
}
let last = input[last_break..].trim();
if !last.is_empty() {
if last.len() <= width {
strings.push(last.to_owned());
} else {
hard_break(&mut strings, last, width);
}
}
strings
}
fn hard_break(lines: &mut Vec<String>, segment: &str, width: usize) {
let mut last_char_break = 0;
let mut last_char_idx = 0;
let mut string = String::with_capacity(width);
for (idx, grapheme) in segment.grapheme_indices(true) {
if idx + grapheme.len() > last_char_break + width {
last_char_break = last_char_idx;
lines.push(string.clone());
string.clear();
}
string.push_str(grapheme);
last_char_idx = idx;
}
if !string.is_empty() {
lines.push(string);
}
}
#[lang = "eh_personality"]
extern fn rust_eh_personality() {
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
// Rust needs a CRT runtime on Windows when compiled with MSVC.
#[cfg(all(windows, target_env = "msvc"))]
#[link(name = "msvcrt")]
#[link(name = "libcmt")]
extern {}
#[cfg(debug_assertions)]
#[no_mangle]
fn rust_oom() -> ! {
loop {}
}