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}