Skip to main content

relibc/header/strings/
mod.rs

1//! `strings.h` implementation.
2//!
3//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/strings.h.html>.
4
5use core::{
6    arch,
7    iter::{once, zip},
8    ptr,
9};
10
11use crate::{
12    header::{ctype, string},
13    iter::NulTerminated,
14    platform::types::{c_char, c_int, c_long, c_longlong, c_void, size_t},
15};
16
17/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/bcmp.html>.
18///
19/// # Deprecation
20/// The `bcmp()` function was marked legacy in the Open Group Base
21/// Specifications Issue 6, and removed in Issue 7.
22#[deprecated]
23#[unsafe(no_mangle)]
24pub unsafe extern "C" fn bcmp(first: *const c_void, second: *const c_void, n: size_t) -> c_int {
25    unsafe { string::memcmp(first, second, n) }
26}
27
28/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/bcopy.html>.
29///
30/// # Deprecation
31/// The `bcopy()` function was marked legacy in the Open Group Base
32/// Specifications Issue 6, and removed in Issue 7.
33#[deprecated]
34#[unsafe(no_mangle)]
35pub unsafe extern "C" fn bcopy(src: *const c_void, dst: *mut c_void, n: size_t) {
36    unsafe {
37        ptr::copy(src.cast::<u8>(), dst.cast::<u8>(), n);
38    }
39}
40
41/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/bzero.html>.
42///
43/// # Deprecation
44/// The `bzero()` function was marked legacy in the Open Group Base
45/// Specifications Issue 6, and removed in Issue 7.
46#[deprecated]
47#[unsafe(no_mangle)]
48pub unsafe extern "C" fn bzero(dst: *mut c_void, n: size_t) {
49    unsafe {
50        ptr::write_bytes(dst.cast::<u8>(), 0, n);
51    }
52}
53
54/// Non-POSIX, see <https://man7.org/linux/man-pages/man3/bzero.3.html>.
55#[unsafe(no_mangle)]
56pub unsafe extern "C" fn explicit_bzero(s: *mut c_void, n: size_t) {
57    for i in 0..n {
58        unsafe {
59            *s.cast::<u8>().add(i) = 0_u8;
60        }
61    }
62    unsafe {
63        arch::asm!("");
64    }
65}
66
67/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ffs.html>.
68#[unsafe(no_mangle)]
69pub extern "C" fn ffs(i: c_int) -> c_int {
70    if i == 0 {
71        return 0;
72    }
73    1 + i.trailing_zeros() as c_int
74}
75
76/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ffs.html>.
77#[unsafe(no_mangle)]
78pub extern "C" fn ffsl(i: c_long) -> c_int {
79    if i == 0 {
80        return 0;
81    }
82    1 + i.trailing_zeros() as c_int
83}
84
85/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ffs.html>.
86#[unsafe(no_mangle)]
87pub extern "C" fn ffsll(i: c_longlong) -> c_int {
88    if i == 0 {
89        return 0;
90    }
91    1 + i.trailing_zeros() as c_int
92}
93
94/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/index.html>.
95///
96/// # Deprecation
97/// The `index()` function was marked legacy in the Open Group Base
98/// Specifications Issue 6, and removed in Issue 7.
99#[deprecated]
100#[unsafe(no_mangle)]
101pub unsafe extern "C" fn index(s: *const c_char, c: c_int) -> *mut c_char {
102    unsafe { string::strchr(s, c) }
103}
104
105/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/rindex.html>.
106///
107/// # Deprecation
108/// The `rindex()` function was marked legacy in the Open Group Base
109/// Specifications Issue 6, and removed in Issue 7.
110#[deprecated]
111#[unsafe(no_mangle)]
112pub unsafe extern "C" fn rindex(s: *const c_char, c: c_int) -> *mut c_char {
113    unsafe { string::strrchr(s, c) }
114}
115
116/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strcasecmp.html>.
117#[unsafe(no_mangle)]
118pub unsafe extern "C" fn strcasecmp(s1: *const c_char, s2: *const c_char) -> c_int {
119    // SAFETY: the caller must ensure that s1 and s2 point to nul-terminated buffers.
120    let s1_iter = unsafe { NulTerminated::new(s1).unwrap() }.chain(once(&0));
121    let s2_iter = unsafe { NulTerminated::new(s2).unwrap() }.chain(once(&0));
122
123    let zipped = zip(s1_iter, s2_iter);
124    inner_casecmp(zipped)
125}
126
127// TODO: needs locale_t
128// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strcasecmp.html>.
129// #[unsafe(no_mangle)]
130/*pub extern "C" fn strcasecmp_l(s1: *const c_char, s2: *const c_char, locale: locale_t) -> c_int {
131    unimplemented!();
132}*/
133
134/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strcasecmp.html>.
135#[unsafe(no_mangle)]
136pub unsafe extern "C" fn strncasecmp(s1: *const c_char, s2: *const c_char, n: size_t) -> c_int {
137    // SAFETY: the caller must ensure that s1 and s2 point to nul-terminated buffers.
138    let s1_iter = unsafe { NulTerminated::new(s1).unwrap() }.chain(once(&0));
139    let s2_iter = unsafe { NulTerminated::new(s2).unwrap() }.chain(once(&0));
140
141    let zipped = zip(s1_iter, s2_iter).take(n);
142    inner_casecmp(zipped)
143}
144
145// TODO: needs locale_t
146// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strcasecmp.html>.
147// #[unsafe(no_mangle)]
148/*pub extern "C" fn strncasecmp_l(s1: *const c_char, s2: *const c_char, n: size_t, locale: locale_t) -> c_int {
149    unimplemented!();
150}*/
151
152/// Given two zipped `&c_char` iterators, either find the first comparison != 0, or return 0.
153fn inner_casecmp<'a>(iterator: impl Iterator<Item = (&'a c_char, &'a c_char)>) -> c_int {
154    let cmp_iter = iterator.map(|(&c1, &c2)| ctype::tolower(c1.into()) - ctype::tolower(c2.into()));
155    let mut skip_iter = cmp_iter.skip_while(|&cmp| cmp == 0);
156    skip_iter.next().unwrap_or(0)
157}