156 lines
3.5 KiB
Rust
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 {}
|
|
}
|