nstd_sys/
string.rs

1//! Dynamically sized UTF-8 encoded byte string.
2extern crate alloc;
3use crate::{
4    core::{
5        alloc::{NSTDAllocError, NSTDAllocator},
6        def::NSTDByte,
7        optional::NSTDOptional,
8        slice::{nstd_core_slice_new_unchecked, NSTDSlice},
9        str::{
10            nstd_core_str_as_bytes, nstd_core_str_from_bytes_unchecked, nstd_core_str_len,
11            nstd_core_str_mut_from_bytes_unchecked, NSTDStr, NSTDStrMut,
12        },
13        unichar::{NSTDOptionalUnichar, NSTDUnichar},
14    },
15    vec::{
16        nstd_vec_allocator, nstd_vec_as_ptr, nstd_vec_as_slice, nstd_vec_as_slice_mut,
17        nstd_vec_cap, nstd_vec_clear, nstd_vec_clone, nstd_vec_extend, nstd_vec_from_slice,
18        nstd_vec_len, nstd_vec_new, nstd_vec_new_with_cap, nstd_vec_truncate, NSTDVec,
19    },
20    NSTDFloat32, NSTDFloat64, NSTDInt, NSTDInt16, NSTDInt32, NSTDInt64, NSTDInt8, NSTDUInt,
21    NSTDUInt16, NSTDUInt32, NSTDUInt64, NSTDUInt8,
22};
23use alloc::string::{String, ToString};
24use nstdapi::nstdapi;
25
26/// Generates the `nstd_string_from_[i|u|f]*` functions.
27macro_rules! gen_from_primitive {
28    (
29        $(#[$meta:meta])*
30        $name: ident, $FromT: ty
31    ) => {
32        $(#[$meta])*
33        #[inline]
34        #[nstdapi]
35        pub fn $name(v: $FromT) -> NSTDString<'static> {
36            NSTDString::from_string(v.to_string())
37        }
38    };
39}
40
41/// Dynamically sized UTF-8 encoded byte string.
42#[nstdapi]
43pub struct NSTDString<'a> {
44    /// The underlying UTF-8 encoded byte buffer.
45    bytes: NSTDVec<'a>,
46}
47impl<'a> NSTDString<'a> {
48    /// Creates a new [`NSTDString`] from a Rust [String].
49    #[inline]
50    pub(crate) fn from_string(string: String) -> NSTDString<'a> {
51        NSTDString {
52            bytes: NSTDVec::from_vec(string.into_bytes()),
53        }
54    }
55
56    /// Returns a mutable reference to the string's buffer.
57    ///
58    /// # Safety
59    ///
60    /// When mutating the returned buffer, the buffer's data must remain valid UTF-8.
61    #[inline]
62    #[allow(dead_code)]
63    pub(crate) unsafe fn as_mut_vec(&mut self) -> &mut NSTDVec<'a> {
64        &mut self.bytes
65    }
66}
67
68/// Represents an optional value of type `NSTDString`.
69pub type NSTDOptionalString<'a> = NSTDOptional<NSTDString<'a>>;
70
71/// Creates a new instance of `NSTDString`.
72///
73/// # Parameters:
74///
75/// - `const NSTDAllocator *allocator` - The memory allocator.
76///
77/// # Returns
78///
79/// `NSTDString string` - The new string.
80///
81/// # Example
82///
83/// ```
84/// use nstd_sys::{alloc::NSTD_ALLOCATOR, string::nstd_string_new};
85///
86/// let string = unsafe { nstd_string_new(&NSTD_ALLOCATOR) };
87/// ```
88#[inline]
89#[nstdapi]
90pub const fn nstd_string_new(allocator: &NSTDAllocator) -> NSTDString<'_> {
91    NSTDString {
92        bytes: nstd_vec_new(allocator, 1, 1),
93    }
94}
95
96/// Creates a new string initialized with the given capacity.
97///
98/// # Parameters:
99///
100/// - `const NSTDAllocator *allocator` - The memory allocator.
101///
102/// - `NSTDUInt cap` - The number of bytes to allocate ahead of time.
103///
104/// # Returns
105///
106/// `NSTDOptionalString string` - The new string on success, or an uninitialized "none" variant if
107/// allocating fails.
108///
109/// # Example
110///
111/// ```
112/// use nstd_sys::{alloc::NSTD_ALLOCATOR, string::nstd_string_new_with_cap};
113///
114/// let string = unsafe { nstd_string_new_with_cap(&NSTD_ALLOCATOR, 20) };
115/// ```
116#[inline]
117#[nstdapi]
118pub fn nstd_string_new_with_cap(
119    allocator: &NSTDAllocator,
120    cap: NSTDUInt,
121) -> NSTDOptionalString<'_> {
122    match nstd_vec_new_with_cap(allocator, 1, 1, cap) {
123        NSTDOptional::Some(bytes) => NSTDOptional::Some(NSTDString { bytes }),
124        NSTDOptional::None => NSTDOptional::None,
125    }
126}
127
128/// Creates an owned version of an unowned string slice.
129///
130/// # Parameters:
131///
132/// - `const NSTDAllocator *allocator` - The memory allocator.
133///
134/// - `const NSTDStr *str` - The unowned string slice.
135///
136/// # Returns
137///
138/// `NSTDOptionalString string` - The new owned version of `str` on success, or an uninitialized
139/// "none" variant if allocating fails.
140///
141/// # Safety
142///
143/// The caller of this function must ensure that `str`'s data is valid for reads.
144///
145/// # Example
146///
147/// ```
148/// use nstd_sys::{
149///     alloc::NSTD_ALLOCATOR, core::str::nstd_core_str_from_raw_cstr, string::nstd_string_from_str,
150/// };
151///
152/// unsafe {
153///     let str = nstd_core_str_from_raw_cstr("Hello, world!\0".as_ptr().cast()).unwrap();
154///     let string = nstd_string_from_str(&NSTD_ALLOCATOR, &str);
155/// }
156/// ```
157#[inline]
158#[nstdapi]
159pub unsafe fn nstd_string_from_str<'a>(
160    allocator: &'a NSTDAllocator,
161    str: &NSTDStr,
162) -> NSTDOptionalString<'a> {
163    let bytes = nstd_core_str_as_bytes(str);
164    match nstd_vec_from_slice(allocator, &bytes) {
165        NSTDOptional::Some(bytes) => NSTDOptional::Some(NSTDString { bytes }),
166        NSTDOptional::None => NSTDOptional::None,
167    }
168}
169
170/// Creates a new string from owned UTF-8 data.
171///
172/// # Parameters:
173///
174/// - `NSTDVec bytes` - The owned UTF-8 encoded buffer to take ownership of.
175///
176/// # Returns
177///
178/// `NSTDOptionalString string` - The new UTF-8 encoded string with ownership of `bytes` on success
179/// or an uninitialized "none" variant if `bytes` contains invalid UTF-8.
180///
181/// # Panics
182///
183/// This operation will panic if `bytes`'s stride is not 1.
184#[inline]
185#[nstdapi]
186pub fn nstd_string_from_bytes(bytes: NSTDVec<'_>) -> NSTDOptionalString<'_> {
187    // SAFETY: We're ensuring that the vector is properly encoded as UTF-8.
188    match core::str::from_utf8(unsafe { bytes.as_slice() }).is_ok() {
189        true => NSTDOptional::Some(NSTDString { bytes }),
190        false => NSTDOptional::None,
191    }
192}
193
194/// Creates a deep copy of a string.
195///
196/// # Parameters:
197///
198/// - `const NSTDString *string` - The string to create a deep copy of.
199///
200/// # Returns
201///
202/// `NSTDOptionalString cloned` - A new deep copy of `string` on success, or an uninitialized
203/// "none" variant if allocating fails.
204#[inline]
205#[nstdapi]
206pub fn nstd_string_clone<'a>(string: &NSTDString<'a>) -> NSTDOptionalString<'a> {
207    match nstd_vec_clone(&string.bytes) {
208        NSTDOptional::Some(bytes) => NSTDOptional::Some(NSTDString { bytes }),
209        NSTDOptional::None => NSTDOptional::None,
210    }
211}
212
213/// Returns an immutable reference to a string's allocator.
214///
215/// # Parameters:
216///
217/// - `const NSTDString *string` - The string.
218///
219/// # Returns
220///
221/// `const NSTDAllocator *allocator` - The string's allocator.
222#[inline]
223#[nstdapi]
224pub const fn nstd_string_allocator<'a>(string: &NSTDString<'a>) -> &'a NSTDAllocator {
225    nstd_vec_allocator(&string.bytes)
226}
227
228/// Creates a string slice containing the contents of `string`.
229///
230/// # Parameters:
231///
232/// - `const NSTDString *string` - The string.
233///
234/// # Returns
235///
236/// `NSTDStr str` - The new string slice.
237#[inline]
238#[nstdapi]
239pub const fn nstd_string_as_str(string: &NSTDString<'_>) -> NSTDStr {
240    let bytes = nstd_vec_as_slice(&string.bytes);
241    // SAFETY: The string's bytes are always be UTF-8 encoded.
242    unsafe { nstd_core_str_from_bytes_unchecked(&bytes) }
243}
244
245/// Creates a string slice containing the contents of `string`.
246///
247/// # Parameters:
248///
249/// - `NSTDString *string` - The string.
250///
251/// # Returns
252///
253/// `NSTDStrMut str` - The new string slice.
254#[inline]
255#[nstdapi]
256pub fn nstd_string_as_str_mut(string: &mut NSTDString<'_>) -> NSTDStrMut {
257    let mut bytes = nstd_vec_as_slice_mut(&mut string.bytes);
258    // SAFETY: The string's bytes are always be UTF-8 encoded.
259    unsafe { nstd_core_str_mut_from_bytes_unchecked(&mut bytes) }
260}
261
262/// Returns an immutable byte slice of the string's active data.
263///
264/// # Parameters:
265///
266/// - `const NSTDString *string` - The string.
267///
268/// # Returns
269///
270/// `NSTDSlice bytes` - The string's active data.
271#[inline]
272#[nstdapi]
273pub const fn nstd_string_as_bytes(string: &NSTDString<'_>) -> NSTDSlice {
274    nstd_vec_as_slice(&string.bytes)
275}
276
277/// Returns a raw pointer to a string's memory.
278///
279/// # Parameters:
280///
281/// - `const NSTDString *string` - The string.
282///
283/// # Returns
284///
285/// `const NSTDByte *ptr` - A raw pointer to a string's memory.
286#[inline]
287#[nstdapi]
288pub const fn nstd_string_as_ptr(string: &NSTDString<'_>) -> *const NSTDByte {
289    nstd_vec_as_ptr(&string.bytes).cast()
290}
291
292/// Returns ownership of an `NSTDString`'s raw data, taking ownership of said string.
293///
294/// # Parameters:
295///
296/// - `NSTDString string` - The string.
297///
298/// # Returns
299///
300/// `NSTDVec bytes` - The string's raw data.
301#[inline]
302#[nstdapi]
303#[allow(clippy::missing_const_for_fn)]
304pub fn nstd_string_into_bytes(string: NSTDString<'_>) -> NSTDVec<'_> {
305    string.bytes
306}
307
308/// Returns the number of Unicode characters in a string.
309///
310/// # Parameters:
311///
312/// - `const NSTDString *string` - The string.
313///
314/// # Returns
315///
316/// `NSTDUInt len` - The length of the string.
317#[inline]
318#[nstdapi]
319pub fn nstd_string_len(string: &NSTDString<'_>) -> NSTDUInt {
320    let str = nstd_string_as_str(string);
321    // SAFETY: The string's data is valid here.
322    unsafe { nstd_core_str_len(&str) }
323}
324
325/// Returns the number of bytes a string contains.
326///
327/// # Parameters:
328///
329/// - `const NSTDString *string` - The string.
330///
331/// # Returns
332///
333/// `NSTDUInt byte_len` - The number of bytes in the string.
334#[inline]
335#[nstdapi]
336pub const fn nstd_string_byte_len(string: &NSTDString<'_>) -> NSTDUInt {
337    nstd_vec_len(&string.bytes)
338}
339
340/// Returns a string's capacity.
341///
342/// This is the max number of *bytes* the string can contain without reallocating.
343///
344/// # Parameters:
345///
346/// - `const NSTDString *string` - The string.
347///
348/// # Returns
349///
350/// `NSTDUInt cap` - The string's capacity.
351#[inline]
352#[nstdapi]
353pub const fn nstd_string_cap(string: &NSTDString<'_>) -> NSTDUInt {
354    nstd_vec_cap(&string.bytes)
355}
356
357/// Pushes an `NSTDUnichar` onto the end of a string.
358///
359/// # Parameters:
360///
361/// - `NSTDString *string` - The string to append the character to.
362///
363/// - `NSTDUnichar chr` - The Unicode character to append to the string.
364///
365/// # Returns
366///
367/// `NSTDAllocError errc` - The allocation operation error code.
368///
369/// # Example
370///
371/// ```
372/// use nstd_sys::{
373///     alloc::NSTD_ALLOCATOR,
374///     core::alloc::NSTDAllocError::NSTD_ALLOC_ERROR_NONE,
375///     string::{nstd_string_new, nstd_string_push},
376/// };
377///
378/// unsafe {
379///     let mut string = nstd_string_new(&NSTD_ALLOCATOR);
380///     assert!(nstd_string_push(&mut string, '🦀'.into()) == NSTD_ALLOC_ERROR_NONE);
381/// }
382/// ```
383#[nstdapi]
384pub fn nstd_string_push(string: &mut NSTDString<'_>, chr: NSTDUnichar) -> NSTDAllocError {
385    let chr = char::from(chr);
386    let mut buf = [0; 4];
387    chr.encode_utf8(&mut buf);
388    // SAFETY: `buf`'s data is stored on the stack, UTF-8 characters never occupy more than 4
389    // bytes.
390    unsafe {
391        let buf = nstd_core_slice_new_unchecked(buf.as_ptr().cast(), 1, 1, chr.len_utf8());
392        nstd_vec_extend(&mut string.bytes, &buf)
393    }
394}
395
396/// Appends a string slice to the end of a string.
397///
398/// # Parameters:
399///
400/// - `NSTDString *string` - The string.
401///
402/// - `const NSTDStr *str` - The string slice to append to the end of `string`.
403///
404/// # Returns
405///
406/// `NSTDAllocError errc` - The allocation operation error code.
407///
408/// # Safety
409///
410/// This function will cause undefined behavior in the case where `str`'s data is no longer valid.
411///
412/// # Example
413///
414/// ```
415/// use nstd_sys::{
416///     alloc::NSTD_ALLOCATOR,
417///     core::{alloc::NSTDAllocError::NSTD_ALLOC_ERROR_NONE, str::nstd_core_str_from_raw_cstr},
418///     string::{nstd_string_new, nstd_string_push_str},
419/// };
420///
421/// unsafe {
422///     let str = nstd_core_str_from_raw_cstr("Hello, 🌎!\0".as_ptr().cast()).unwrap();
423///     let mut string = nstd_string_new(&NSTD_ALLOCATOR);
424///     assert!(nstd_string_push_str(&mut string, &str) == NSTD_ALLOC_ERROR_NONE);
425/// }
426/// ```
427#[inline]
428#[nstdapi]
429pub unsafe fn nstd_string_push_str(string: &mut NSTDString<'_>, str: &NSTDStr) -> NSTDAllocError {
430    let str_bytes = nstd_core_str_as_bytes(str);
431    nstd_vec_extend(&mut string.bytes, &str_bytes)
432}
433
434/// Removes the last character from a string and returns it.
435///
436/// # Parameters:
437///
438/// - `NSTDString *string` - The string to pop.
439///
440/// # Returns
441///
442/// `NSTDOptionalUnichar chr` - The removed character on success.
443///
444/// # Example
445///
446/// ```
447/// use nstd_sys::{
448///     alloc::NSTD_ALLOCATOR,
449///     core::{optional::NSTDOptional, str::nstd_core_str_from_raw_cstr_with_null},
450///     string::{nstd_string_from_str, nstd_string_pop},
451/// };
452///
453/// unsafe {
454///     let str = nstd_core_str_from_raw_cstr_with_null("Hello, world!\0".as_ptr().cast()).unwrap();
455///     let mut string = nstd_string_from_str(&NSTD_ALLOCATOR, &str).unwrap();
456///     assert!(nstd_string_pop(&mut string) == NSTDOptional::Some('\0'.into()));
457/// }
458/// ```
459#[nstdapi]
460pub fn nstd_string_pop(string: &mut NSTDString<'_>) -> NSTDOptionalUnichar {
461    // SAFETY: `NSTDString` is always UTF-8 encoded.
462    let str = unsafe { core::str::from_utf8_unchecked(string.bytes.as_slice()) };
463    if let Some(chr) = str.chars().last() {
464        #[allow(clippy::arithmetic_side_effects)]
465        let len = nstd_vec_len(&string.bytes) - chr.len_utf8();
466        nstd_vec_truncate(&mut string.bytes, len);
467        return NSTDOptional::Some(chr.into());
468    }
469    NSTDOptional::None
470}
471
472/// Sets a string's length to zero.
473///
474/// # Parameters:
475///
476/// - `NSTDString *string` - The string to clear.
477#[inline]
478#[nstdapi]
479pub fn nstd_string_clear(string: &mut NSTDString<'_>) {
480    nstd_vec_clear(&mut string.bytes);
481}
482
483gen_from_primitive!(
484    /// Creates a new `NSTDString` from an `NSTDFloat32`.
485    ///
486    /// # Parameters:
487    ///
488    /// - `NSTDFloat32 v` - The 32-bit floating-point value.
489    ///
490    /// # Returns
491    ///
492    /// `NSTDString string` - The 32-bit floating-point value as a string.
493    nstd_string_from_f32,
494    NSTDFloat32
495);
496gen_from_primitive!(
497    /// Creates a new `NSTDString` from an `NSTDFloat64`.
498    ///
499    /// # Parameters:
500    ///
501    /// - `NSTDFloat64 v` - The 64-bit floating-point value.
502    ///
503    /// # Returns
504    ///
505    /// `NSTDString string` - The 64-bit floating-point value as a string.
506    nstd_string_from_f64,
507    NSTDFloat64
508);
509gen_from_primitive!(
510    /// Creates a new `NSTDString` from an `NSTDInt`.
511    ///
512    /// # Parameters:
513    ///
514    /// - `NSTDInt v` - The arch-bit signed integer value.
515    ///
516    /// # Returns
517    ///
518    /// `NSTDString string` - The arch-bit signed integer value as a string.
519    nstd_string_from_int,
520    NSTDInt
521);
522gen_from_primitive!(
523    /// Creates a new `NSTDString` from an `NSTDUInt`.
524    ///
525    /// # Parameters:
526    ///
527    /// - `NSTDUInt v` - The arch-bit unsigned integer value.
528    ///
529    /// # Returns
530    ///
531    /// `NSTDString string` - The arch-bit unsigned integer value as a string.
532    nstd_string_from_uint,
533    NSTDUInt
534);
535gen_from_primitive!(
536    /// Creates a new `NSTDString` from an `NSTDInt8`.
537    ///
538    /// # Parameters:
539    ///
540    /// - `NSTDInt8 v` - The 8-bit signed integer value.
541    ///
542    /// # Returns
543    ///
544    /// `NSTDString string` - The 8-bit signed integer value as a string.
545    nstd_string_from_i8,
546    NSTDInt8
547);
548gen_from_primitive!(
549    /// Creates a new `NSTDString` from an `NSTDUInt8`.
550    ///
551    /// # Parameters:
552    ///
553    /// - `NSTDUInt8 v` - The 8-bit unsigned integer value.
554    ///
555    /// # Returns
556    ///
557    /// `NSTDString string` - The 8-bit unsigned integer value as a string.
558    nstd_string_from_u8,
559    NSTDUInt8
560);
561gen_from_primitive!(
562    /// Creates a new `NSTDString` from an `NSTDInt16`.
563    ///
564    /// # Parameters:
565    ///
566    /// - `NSTDInt16 v` - The 16-bit signed integer value.
567    ///
568    /// # Returns
569    ///
570    /// `NSTDString string` - The 16-bit signed integer value as a string.
571    nstd_string_from_i16,
572    NSTDInt16
573);
574gen_from_primitive!(
575    /// Creates a new `NSTDString` from an `NSTDUInt16`.
576    ///
577    /// # Parameters:
578    ///
579    /// - `NSTDUInt16 v` - The 16-bit unsigned integer value.
580    ///
581    /// # Returns
582    ///
583    /// `NSTDString string` - The 16-bit unsigned integer value as a string.
584    nstd_string_from_u16,
585    NSTDUInt16
586);
587gen_from_primitive!(
588    /// Creates a new `NSTDString` from an `NSTDInt32`.
589    ///
590    /// # Parameters:
591    ///
592    /// - `NSTDInt32 v` - The 32-bit signed integer value.
593    ///
594    /// # Returns
595    ///
596    /// `NSTDString string` - The 32-bit signed integer value as a string.
597    nstd_string_from_i32,
598    NSTDInt32
599);
600gen_from_primitive!(
601    /// Creates a new `NSTDString` from an `NSTDUInt32`.
602    ///
603    /// # Parameters:
604    ///
605    /// - `NSTDUInt32 v` - The 32-bit unsigned integer value.
606    ///
607    /// # Returns
608    ///
609    /// `NSTDString string` - The 32-bit unsigned integer value as a string.
610    nstd_string_from_u32,
611    NSTDUInt32
612);
613gen_from_primitive!(
614    /// Creates a new `NSTDString` from an `NSTDInt64`.
615    ///
616    /// # Parameters:
617    ///
618    /// - `NSTDInt64 v` - The 64-bit signed integer value.
619    ///
620    /// # Returns
621    ///
622    /// `NSTDString string` - The 64-bit signed integer value as a string.
623    nstd_string_from_i64,
624    NSTDInt64
625);
626gen_from_primitive!(
627    /// Creates a new `NSTDString` from an `NSTDUInt64`.
628    ///
629    /// # Parameters:
630    ///
631    /// - `NSTDUInt64 v` - The 64-bit unsigned integer value.
632    ///
633    /// # Returns
634    ///
635    /// `NSTDString string` - The 64-bit unsigned integer value as a string.
636    nstd_string_from_u64,
637    NSTDUInt64
638);
639
640/// Frees an instance of `NSTDString`.
641///
642/// # Parameters:
643///
644/// - `NSTDString string` - The string to free.
645#[inline]
646#[nstdapi]
647#[allow(
648    unused_variables,
649    clippy::missing_const_for_fn,
650    clippy::needless_pass_by_value
651)]
652pub fn nstd_string_free(string: NSTDString<'_>) {}