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<'_>) {}