use std::ffi::{CStr, CString};
use std::os::raw::c_char;
#[no_mangle]
pub unsafe extern "C" fn uniworld_free_string(ptr: *mut c_char) {
if !ptr.is_null() {
drop(CString::from_raw(ptr));
}
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_free_array(ptr: *mut u32, len: u32) {
if !ptr.is_null() && len > 0 {
drop(Vec::from_raw_parts(ptr, len as usize, len as usize));
}
}
unsafe fn cstr_to_str<'a>(ptr: *const c_char) -> Option<&'a str> {
if ptr.is_null() {
return None;
}
CStr::from_ptr(ptr).to_str().ok()
}
fn string_to_cstring(s: String) -> *mut c_char {
CString::new(s).map_or(std::ptr::null_mut(), |c| c.into_raw())
}
fn offsets_to_array(offsets: Vec<usize>, out_len: &mut u32) -> *mut u32 {
let arr: Vec<u32> = offsets.iter().map(|&o| o as u32).collect();
*out_len = arr.len() as u32;
let mut boxed = arr.into_boxed_slice();
let ptr = boxed.as_mut_ptr();
std::mem::forget(boxed);
ptr
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_normalize_nfc(text: *const c_char) -> *mut c_char {
let s = match cstr_to_str(text) {
Some(s) => s,
None => return std::ptr::null_mut(),
};
string_to_cstring(crate::normalize::nfc(s))
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_normalize_nfd(text: *const c_char) -> *mut c_char {
let s = match cstr_to_str(text) {
Some(s) => s,
None => return std::ptr::null_mut(),
};
string_to_cstring(crate::normalize::nfd(s))
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_normalize_nfkc(text: *const c_char) -> *mut c_char {
let s = match cstr_to_str(text) {
Some(s) => s,
None => return std::ptr::null_mut(),
};
string_to_cstring(crate::normalize::nfkc(s))
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_normalize_nfkd(text: *const c_char) -> *mut c_char {
let s = match cstr_to_str(text) {
Some(s) => s,
None => return std::ptr::null_mut(),
};
string_to_cstring(crate::normalize::nfkd(s))
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_to_lowercase(text: *const c_char) -> *mut c_char {
let s = match cstr_to_str(text) {
Some(s) => s,
None => return std::ptr::null_mut(),
};
string_to_cstring(crate::casemap::to_lowercase(s))
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_to_uppercase(text: *const c_char) -> *mut c_char {
let s = match cstr_to_str(text) {
Some(s) => s,
None => return std::ptr::null_mut(),
};
string_to_cstring(crate::casemap::to_uppercase(s))
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_display_width(text: *const c_char) -> u32 {
let s = match cstr_to_str(text) {
Some(s) => s,
None => return 0,
};
crate::width::display_width(s)
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_truncate_display_width(
text: *const c_char,
max_width: u32,
) -> *mut c_char {
let s = match cstr_to_str(text) {
Some(s) => s,
None => return std::ptr::null_mut(),
};
string_to_cstring(crate::truncate::truncate_display_width(s, max_width))
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_grapheme_boundaries(
text: *const c_char,
out_len: *mut u32,
) -> *mut u32 {
let s = match cstr_to_str(text) {
Some(s) => s,
None => {
if !out_len.is_null() { *out_len = 0; }
return std::ptr::null_mut();
}
};
let offsets = crate::segment::grapheme_boundaries(s);
if out_len.is_null() {
return std::ptr::null_mut();
}
offsets_to_array(offsets, &mut *out_len)
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_word_boundaries(
text: *const c_char,
out_len: *mut u32,
) -> *mut u32 {
let s = match cstr_to_str(text) {
Some(s) => s,
None => {
if !out_len.is_null() { *out_len = 0; }
return std::ptr::null_mut();
}
};
let offsets = crate::segment::word_boundaries(s, None);
if out_len.is_null() {
return std::ptr::null_mut();
}
offsets_to_array(offsets, &mut *out_len)
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_sentence_boundaries(
text: *const c_char,
out_len: *mut u32,
) -> *mut u32 {
let s = match cstr_to_str(text) {
Some(s) => s,
None => {
if !out_len.is_null() { *out_len = 0; }
return std::ptr::null_mut();
}
};
let offsets = crate::segment::sentence_boundaries(s, None);
if out_len.is_null() {
return std::ptr::null_mut();
}
offsets_to_array(offsets, &mut *out_len)
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_bidi_levels(
text: *const c_char,
out_len: *mut u32,
) -> *mut u8 {
let s = match cstr_to_str(text) {
Some(s) => s,
None => {
if !out_len.is_null() { *out_len = 0; }
return std::ptr::null_mut();
}
};
let info = crate::bidi::resolve(s, None);
let len = info.levels.len();
if out_len.is_null() {
return std::ptr::null_mut();
}
*out_len = len as u32;
let mut boxed = info.levels.into_boxed_slice();
let ptr = boxed.as_mut_ptr();
std::mem::forget(boxed);
ptr
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_free_u8_array(ptr: *mut u8, len: u32) {
if !ptr.is_null() && len > 0 {
drop(Vec::from_raw_parts(ptr, len as usize, len as usize));
}
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_bidi_paragraph_level(text: *const c_char) -> u8 {
let s = match cstr_to_str(text) {
Some(s) => s,
None => return 0,
};
let info = crate::bidi::resolve(s, None);
info.paragraph_level
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_line_break_opportunities(
text: *const c_char,
out_len: *mut u32,
) -> *mut u32 {
let s = match cstr_to_str(text) {
Some(s) => s,
None => {
if !out_len.is_null() { *out_len = 0; }
return std::ptr::null_mut();
}
};
let breaks = crate::linebreak::line_break_opportunities_with_dictionary(s);
let mut pairs: Vec<u32> = Vec::new();
for (i, action) in breaks.iter().enumerate() {
match action {
crate::linebreak::BreakAction::Mandatory => {
pairs.push(i as u32);
pairs.push(0);
}
crate::linebreak::BreakAction::Allowed => {
pairs.push(i as u32);
pairs.push(1);
}
_ => {}
}
}
if out_len.is_null() {
return std::ptr::null_mut();
}
*out_len = pairs.len() as u32;
let mut boxed = pairs.into_boxed_slice();
let ptr = boxed.as_mut_ptr();
std::mem::forget(boxed);
ptr
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_move_right(text: *const c_char, current: usize) -> usize {
let s = match cstr_to_str(text) {
Some(s) => s,
None => return current,
};
crate::cursor::move_right(s, current)
}
#[no_mangle]
pub unsafe extern "C" fn uniworld_move_left(text: *const c_char, current: usize) -> usize {
let s = match cstr_to_str(text) {
Some(s) => s,
None => return current,
};
crate::cursor::move_left(s, current)
}