use crate::{
core::{
alloc::{
NSTDAllocError::{self, NSTD_ALLOC_ERROR_NONE},
NSTDAllocator,
},
cstr::{
nstd_core_cstr_as_bytes, nstd_core_cstr_get_null, nstd_core_cstr_is_null_terminated,
nstd_core_cstr_new_unchecked, NSTDCStr,
},
optional::NSTDOptional,
slice::NSTDSlice,
},
vec::{
nstd_vec_allocator, nstd_vec_as_ptr, nstd_vec_as_slice, nstd_vec_cap, nstd_vec_clear,
nstd_vec_clone, nstd_vec_extend, nstd_vec_from_slice, nstd_vec_get_mut, nstd_vec_len,
nstd_vec_new_with_cap, nstd_vec_pop, nstd_vec_push, nstd_vec_stride, NSTDVec,
},
NSTDChar, NSTDUInt,
};
use core::ptr::addr_of;
use nstdapi::nstdapi;
#[nstdapi]
pub struct NSTDCString<'a> {
bytes: NSTDVec<'a>,
}
pub type NSTDOptionalCString<'a> = NSTDOptional<NSTDCString<'a>>;
#[inline]
#[nstdapi]
pub fn nstd_cstring_new(allocator: &NSTDAllocator) -> NSTDOptionalCString<'_> {
nstd_cstring_new_with_cap(allocator, 1)
}
#[inline]
#[nstdapi]
pub fn nstd_cstring_new_with_cap(
allocator: &NSTDAllocator,
cap: NSTDUInt,
) -> NSTDOptionalCString<'_> {
if let NSTDOptional::Some(mut bytes) = nstd_vec_new_with_cap(allocator, 1, 1, cap) {
let nul: NSTDChar = 0;
if unsafe { nstd_vec_push(&mut bytes, addr_of!(nul).cast()) } == NSTD_ALLOC_ERROR_NONE {
return NSTDOptional::Some(NSTDCString { bytes });
}
}
NSTDOptional::None
}
#[inline]
#[nstdapi]
pub unsafe fn nstd_cstring_from_cstr<'a>(
allocator: &'a NSTDAllocator,
cstr: &NSTDCStr,
) -> NSTDOptionalCString<'a> {
match nstd_core_cstr_get_null(cstr).is_null() {
true => nstd_cstring_from_cstr_unchecked(allocator, cstr),
false => NSTDOptional::None,
}
}
#[nstdapi]
pub unsafe fn nstd_cstring_from_cstr_unchecked<'a>(
allocator: &'a NSTDAllocator,
cstr: &NSTDCStr,
) -> NSTDOptionalCString<'a> {
let bytes = nstd_core_cstr_as_bytes(cstr);
if let NSTDOptional::Some(mut bytes) = nstd_vec_from_slice(allocator, &bytes) {
let null: NSTDChar = 0;
let null = addr_of!(null).cast();
if nstd_vec_push(&mut bytes, null) == NSTD_ALLOC_ERROR_NONE {
return NSTDOptional::Some(NSTDCString { bytes });
}
}
NSTDOptional::None
}
#[nstdapi]
pub fn nstd_cstring_from_bytes(bytes: NSTDVec<'_>) -> NSTDOptionalCString<'_> {
assert!(nstd_vec_stride(&bytes) == 1);
let ptr = nstd_vec_as_ptr(&bytes).cast();
let cstr = unsafe { nstd_core_cstr_new_unchecked(ptr, nstd_vec_len(&bytes)) };
match unsafe { nstd_core_cstr_is_null_terminated(&cstr) } {
true => NSTDOptional::Some(NSTDCString { bytes }),
false => NSTDOptional::None,
}
}
#[inline]
#[nstdapi]
pub fn nstd_cstring_clone<'a>(cstring: &NSTDCString<'a>) -> NSTDOptionalCString<'a> {
match nstd_vec_clone(&cstring.bytes) {
NSTDOptional::Some(bytes) => NSTDOptional::Some(NSTDCString { bytes }),
NSTDOptional::None => NSTDOptional::None,
}
}
#[inline]
#[nstdapi]
pub const fn nstd_cstring_allocator<'a>(cstring: &NSTDCString<'a>) -> &'a NSTDAllocator {
nstd_vec_allocator(&cstring.bytes)
}
#[nstdapi]
pub const fn nstd_cstring_as_cstr(cstring: &NSTDCString<'_>) -> NSTDCStr {
let ptr = nstd_vec_as_ptr(&cstring.bytes);
let len = nstd_vec_len(&cstring.bytes);
unsafe { nstd_core_cstr_new_unchecked(ptr.cast(), len) }
}
#[inline]
#[nstdapi]
pub const fn nstd_cstring_as_bytes(cstring: &NSTDCString<'_>) -> NSTDSlice {
nstd_vec_as_slice(&cstring.bytes)
}
#[inline]
#[nstdapi]
pub const fn nstd_cstring_as_ptr(cstring: &NSTDCString<'_>) -> *const NSTDChar {
nstd_vec_as_ptr(&cstring.bytes).cast()
}
#[inline]
#[nstdapi]
#[allow(clippy::missing_const_for_fn)]
pub fn nstd_cstring_into_bytes(cstring: NSTDCString<'_>) -> NSTDVec<'_> {
cstring.bytes
}
#[inline]
#[nstdapi]
#[allow(clippy::arithmetic_side_effects)]
pub const fn nstd_cstring_len(cstring: &NSTDCString<'_>) -> NSTDUInt {
nstd_vec_len(&cstring.bytes) - 1
}
#[inline]
#[nstdapi]
pub const fn nstd_cstring_len_with_null(cstring: &NSTDCString<'_>) -> NSTDUInt {
nstd_vec_len(&cstring.bytes)
}
#[inline]
#[nstdapi]
pub const fn nstd_cstring_cap(cstring: &NSTDCString<'_>) -> NSTDUInt {
nstd_vec_cap(&cstring.bytes)
}
#[nstdapi]
pub fn nstd_cstring_push(cstring: &mut NSTDCString<'_>, chr: NSTDChar) -> NSTDAllocError {
if chr != 0 {
unsafe {
let nul: NSTDChar = 0;
let errc = nstd_vec_push(&mut cstring.bytes, addr_of!(nul).cast());
if errc != NSTD_ALLOC_ERROR_NONE {
return errc;
}
#[allow(clippy::arithmetic_side_effects)]
let nulpos = nstd_vec_len(&cstring.bytes) - 2;
let nul = nstd_vec_get_mut(&mut cstring.bytes, nulpos).cast();
*nul = chr;
}
}
NSTD_ALLOC_ERROR_NONE
}
#[nstdapi]
pub unsafe fn nstd_cstring_push_cstr(
cstring: &mut NSTDCString<'_>,
cstr: &NSTDCStr,
) -> NSTDAllocError {
assert!(nstd_core_cstr_get_null(cstr).is_null());
let nul = *nstd_vec_pop(&mut cstring.bytes).cast::<NSTDChar>();
let bytes = nstd_core_cstr_as_bytes(cstr);
let errc = nstd_vec_extend(&mut cstring.bytes, &bytes);
let pusherrc = nstd_vec_push(&mut cstring.bytes, addr_of!(nul).cast());
assert!(pusherrc == NSTD_ALLOC_ERROR_NONE);
errc
}
#[nstdapi]
pub fn nstd_cstring_pop(cstring: &mut NSTDCString<'_>) -> NSTDChar {
let mut ret = 0;
let len = nstd_cstring_len(cstring);
if len > 0 {
unsafe {
#[allow(clippy::arithmetic_side_effects)]
let last = nstd_vec_get_mut(&mut cstring.bytes, len - 1).cast::<NSTDChar>();
ret = *last;
*last = 0;
nstd_vec_pop(&mut cstring.bytes);
}
}
ret
}
#[inline]
#[nstdapi]
pub fn nstd_cstring_clear(cstring: &mut NSTDCString<'_>) {
nstd_vec_clear(&mut cstring.bytes);
}
#[inline]
#[nstdapi]
#[allow(
unused_variables,
clippy::missing_const_for_fn,
clippy::needless_pass_by_value
)]
pub fn nstd_cstring_free(cstring: NSTDCString<'_>) {}