1*5225e6b1SAndroid Build Coastguard Worker // Copyright 2024, The Android Open Source Project
2*5225e6b1SAndroid Build Coastguard Worker //
3*5225e6b1SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*5225e6b1SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*5225e6b1SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*5225e6b1SAndroid Build Coastguard Worker //
7*5225e6b1SAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0
8*5225e6b1SAndroid Build Coastguard Worker //
9*5225e6b1SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*5225e6b1SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*5225e6b1SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*5225e6b1SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*5225e6b1SAndroid Build Coastguard Worker // limitations under the License.
14*5225e6b1SAndroid Build Coastguard Worker
15*5225e6b1SAndroid Build Coastguard Worker //! This library provides implementation for strchr libc functions family.
16*5225e6b1SAndroid Build Coastguard Worker //! https://en.cppreference.com/w/c/string/byte/strchr
17*5225e6b1SAndroid Build Coastguard Worker
18*5225e6b1SAndroid Build Coastguard Worker use core::ffi::{c_char, c_int, CStr};
19*5225e6b1SAndroid Build Coastguard Worker use core::ptr::null_mut;
20*5225e6b1SAndroid Build Coastguard Worker
21*5225e6b1SAndroid Build Coastguard Worker /// char *strchr(const char *str, int c);
22*5225e6b1SAndroid Build Coastguard Worker ///
23*5225e6b1SAndroid Build Coastguard Worker /// # Safety
24*5225e6b1SAndroid Build Coastguard Worker ///
25*5225e6b1SAndroid Build Coastguard Worker /// * `str` must be a valid null-terminated C string.
26*5225e6b1SAndroid Build Coastguard Worker #[no_mangle]
strchr(ptr: *const c_char, ch: c_int) -> *mut c_char27*5225e6b1SAndroid Build Coastguard Worker pub unsafe extern "C" fn strchr(ptr: *const c_char, ch: c_int) -> *mut c_char {
28*5225e6b1SAndroid Build Coastguard Worker assert!(!ptr.is_null());
29*5225e6b1SAndroid Build Coastguard Worker // SAFETY: `str` is a valid null terminated string.
30*5225e6b1SAndroid Build Coastguard Worker let bytes = unsafe { CStr::from_ptr(ptr) }.to_bytes_with_nul();
31*5225e6b1SAndroid Build Coastguard Worker let target = (ch & 0xff) as u8;
32*5225e6b1SAndroid Build Coastguard Worker for c in bytes.iter() {
33*5225e6b1SAndroid Build Coastguard Worker if *c == target {
34*5225e6b1SAndroid Build Coastguard Worker return c as *const _ as *mut _;
35*5225e6b1SAndroid Build Coastguard Worker }
36*5225e6b1SAndroid Build Coastguard Worker }
37*5225e6b1SAndroid Build Coastguard Worker null_mut()
38*5225e6b1SAndroid Build Coastguard Worker }
39*5225e6b1SAndroid Build Coastguard Worker
40*5225e6b1SAndroid Build Coastguard Worker /// char *strrchr(const char *str, int c);
41*5225e6b1SAndroid Build Coastguard Worker ///
42*5225e6b1SAndroid Build Coastguard Worker /// # Safety
43*5225e6b1SAndroid Build Coastguard Worker ///
44*5225e6b1SAndroid Build Coastguard Worker /// * `str` must be a valid null-terminated C string.
45*5225e6b1SAndroid Build Coastguard Worker #[no_mangle]
strrchr(ptr: *const c_char, ch: c_int) -> *mut c_char46*5225e6b1SAndroid Build Coastguard Worker pub unsafe extern "C" fn strrchr(ptr: *const c_char, ch: c_int) -> *mut c_char {
47*5225e6b1SAndroid Build Coastguard Worker assert!(!ptr.is_null());
48*5225e6b1SAndroid Build Coastguard Worker // SAFETY: `str` is a null terminated string.
49*5225e6b1SAndroid Build Coastguard Worker let bytes = unsafe { CStr::from_ptr(ptr) }.to_bytes_with_nul();
50*5225e6b1SAndroid Build Coastguard Worker let target = (ch & 0xff) as u8;
51*5225e6b1SAndroid Build Coastguard Worker for c in bytes.iter().rev() {
52*5225e6b1SAndroid Build Coastguard Worker if *c == target {
53*5225e6b1SAndroid Build Coastguard Worker return c as *const _ as *mut _;
54*5225e6b1SAndroid Build Coastguard Worker }
55*5225e6b1SAndroid Build Coastguard Worker }
56*5225e6b1SAndroid Build Coastguard Worker null_mut()
57*5225e6b1SAndroid Build Coastguard Worker }
58*5225e6b1SAndroid Build Coastguard Worker
59*5225e6b1SAndroid Build Coastguard Worker #[cfg(test)]
60*5225e6b1SAndroid Build Coastguard Worker mod test {
61*5225e6b1SAndroid Build Coastguard Worker use super::*;
62*5225e6b1SAndroid Build Coastguard Worker use std::ffi::CString;
63*5225e6b1SAndroid Build Coastguard Worker
to_cstr(s: &str) -> CString64*5225e6b1SAndroid Build Coastguard Worker fn to_cstr(s: &str) -> CString {
65*5225e6b1SAndroid Build Coastguard Worker CString::new(s).unwrap()
66*5225e6b1SAndroid Build Coastguard Worker }
67*5225e6b1SAndroid Build Coastguard Worker
do_strchr(input: &str, c: char) -> Option<usize>68*5225e6b1SAndroid Build Coastguard Worker fn do_strchr(input: &str, c: char) -> Option<usize> {
69*5225e6b1SAndroid Build Coastguard Worker let input_cstr = to_cstr(input);
70*5225e6b1SAndroid Build Coastguard Worker // SAFETY: `input_cstr` is a null terminated string.
71*5225e6b1SAndroid Build Coastguard Worker let result = unsafe { strchr(input_cstr.as_ptr(), c as c_int) };
72*5225e6b1SAndroid Build Coastguard Worker
73*5225e6b1SAndroid Build Coastguard Worker if result.is_null() {
74*5225e6b1SAndroid Build Coastguard Worker None
75*5225e6b1SAndroid Build Coastguard Worker } else {
76*5225e6b1SAndroid Build Coastguard Worker let start_ptr = input_cstr.as_ptr();
77*5225e6b1SAndroid Build Coastguard Worker // SAFETY: `result` is a pointer within the string that `start_ptr` points to.
78*5225e6b1SAndroid Build Coastguard Worker Some(unsafe { result.offset_from(start_ptr) as usize })
79*5225e6b1SAndroid Build Coastguard Worker }
80*5225e6b1SAndroid Build Coastguard Worker }
81*5225e6b1SAndroid Build Coastguard Worker
do_strrchr(input: &str, c: char) -> Option<usize>82*5225e6b1SAndroid Build Coastguard Worker fn do_strrchr(input: &str, c: char) -> Option<usize> {
83*5225e6b1SAndroid Build Coastguard Worker let input_cstr = to_cstr(input);
84*5225e6b1SAndroid Build Coastguard Worker // SAFETY: `input_cstr` is a null terminated string.
85*5225e6b1SAndroid Build Coastguard Worker let result = unsafe { strrchr(input_cstr.as_ptr(), c as c_int) };
86*5225e6b1SAndroid Build Coastguard Worker
87*5225e6b1SAndroid Build Coastguard Worker if result.is_null() {
88*5225e6b1SAndroid Build Coastguard Worker None
89*5225e6b1SAndroid Build Coastguard Worker } else {
90*5225e6b1SAndroid Build Coastguard Worker let start_ptr = input_cstr.as_ptr();
91*5225e6b1SAndroid Build Coastguard Worker // SAFETY: `result` is a pointer within the string that `start_ptr` points to.
92*5225e6b1SAndroid Build Coastguard Worker Some(unsafe { result.offset_from(start_ptr) as usize })
93*5225e6b1SAndroid Build Coastguard Worker }
94*5225e6b1SAndroid Build Coastguard Worker }
95*5225e6b1SAndroid Build Coastguard Worker
96*5225e6b1SAndroid Build Coastguard Worker // strchr tests
97*5225e6b1SAndroid Build Coastguard Worker
98*5225e6b1SAndroid Build Coastguard Worker #[test]
strchr_find_first_occurrence()99*5225e6b1SAndroid Build Coastguard Worker fn strchr_find_first_occurrence() {
100*5225e6b1SAndroid Build Coastguard Worker let offset = do_strchr("hello", 'e');
101*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, Some(1));
102*5225e6b1SAndroid Build Coastguard Worker }
103*5225e6b1SAndroid Build Coastguard Worker
104*5225e6b1SAndroid Build Coastguard Worker #[test]
strchr_find_first_occurrence_special_character()105*5225e6b1SAndroid Build Coastguard Worker fn strchr_find_first_occurrence_special_character() {
106*5225e6b1SAndroid Build Coastguard Worker let offset = do_strchr("he!lo", '!');
107*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, Some(2));
108*5225e6b1SAndroid Build Coastguard Worker }
109*5225e6b1SAndroid Build Coastguard Worker
110*5225e6b1SAndroid Build Coastguard Worker #[test]
strchr_character_not_present()111*5225e6b1SAndroid Build Coastguard Worker fn strchr_character_not_present() {
112*5225e6b1SAndroid Build Coastguard Worker let offset = do_strchr("hello", 'z');
113*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, None);
114*5225e6b1SAndroid Build Coastguard Worker }
115*5225e6b1SAndroid Build Coastguard Worker
116*5225e6b1SAndroid Build Coastguard Worker #[test]
strchr_find_first_occurrence_at_start()117*5225e6b1SAndroid Build Coastguard Worker fn strchr_find_first_occurrence_at_start() {
118*5225e6b1SAndroid Build Coastguard Worker let offset = do_strchr("hello", 'h');
119*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, Some(0));
120*5225e6b1SAndroid Build Coastguard Worker }
121*5225e6b1SAndroid Build Coastguard Worker
122*5225e6b1SAndroid Build Coastguard Worker #[test]
strchr_find_first_occurrence_at_end()123*5225e6b1SAndroid Build Coastguard Worker fn strchr_find_first_occurrence_at_end() {
124*5225e6b1SAndroid Build Coastguard Worker let offset = do_strchr("hello", 'o');
125*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, Some(4));
126*5225e6b1SAndroid Build Coastguard Worker }
127*5225e6b1SAndroid Build Coastguard Worker
128*5225e6b1SAndroid Build Coastguard Worker #[test]
strchr_empty_string()129*5225e6b1SAndroid Build Coastguard Worker fn strchr_empty_string() {
130*5225e6b1SAndroid Build Coastguard Worker let offset = do_strchr("", 'a');
131*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, None);
132*5225e6b1SAndroid Build Coastguard Worker }
133*5225e6b1SAndroid Build Coastguard Worker
134*5225e6b1SAndroid Build Coastguard Worker #[test]
strchr_find_first_occurrence_multiple()135*5225e6b1SAndroid Build Coastguard Worker fn strchr_find_first_occurrence_multiple() {
136*5225e6b1SAndroid Build Coastguard Worker let offset = do_strchr("hellohello", 'l');
137*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, Some(2));
138*5225e6b1SAndroid Build Coastguard Worker }
139*5225e6b1SAndroid Build Coastguard Worker
140*5225e6b1SAndroid Build Coastguard Worker #[test]
strchr_case_sensitivity()141*5225e6b1SAndroid Build Coastguard Worker fn strchr_case_sensitivity() {
142*5225e6b1SAndroid Build Coastguard Worker let offset = do_strchr("Hello", 'h');
143*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, None);
144*5225e6b1SAndroid Build Coastguard Worker }
145*5225e6b1SAndroid Build Coastguard Worker
146*5225e6b1SAndroid Build Coastguard Worker #[test]
strchr_find_null_character()147*5225e6b1SAndroid Build Coastguard Worker fn strchr_find_null_character() {
148*5225e6b1SAndroid Build Coastguard Worker let offset = do_strchr("Hello", '\0');
149*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, Some(5));
150*5225e6b1SAndroid Build Coastguard Worker }
151*5225e6b1SAndroid Build Coastguard Worker
152*5225e6b1SAndroid Build Coastguard Worker // strrchr tests
153*5225e6b1SAndroid Build Coastguard Worker
154*5225e6b1SAndroid Build Coastguard Worker #[test]
strrchr_find_last_occurrence()155*5225e6b1SAndroid Build Coastguard Worker fn strrchr_find_last_occurrence() {
156*5225e6b1SAndroid Build Coastguard Worker let offset = do_strrchr("hello", 'l');
157*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, Some(3));
158*5225e6b1SAndroid Build Coastguard Worker }
159*5225e6b1SAndroid Build Coastguard Worker
160*5225e6b1SAndroid Build Coastguard Worker #[test]
strrchr_find_last_occurrence_special_character()161*5225e6b1SAndroid Build Coastguard Worker fn strrchr_find_last_occurrence_special_character() {
162*5225e6b1SAndroid Build Coastguard Worker let offset = do_strrchr("he!lo!lo", '!');
163*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, Some(5));
164*5225e6b1SAndroid Build Coastguard Worker }
165*5225e6b1SAndroid Build Coastguard Worker
166*5225e6b1SAndroid Build Coastguard Worker #[test]
strrchr_character_not_present()167*5225e6b1SAndroid Build Coastguard Worker fn strrchr_character_not_present() {
168*5225e6b1SAndroid Build Coastguard Worker let offset = do_strrchr("hello", 'z');
169*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, None);
170*5225e6b1SAndroid Build Coastguard Worker }
171*5225e6b1SAndroid Build Coastguard Worker
172*5225e6b1SAndroid Build Coastguard Worker #[test]
strrchr_find_last_occurrence_at_start()173*5225e6b1SAndroid Build Coastguard Worker fn strrchr_find_last_occurrence_at_start() {
174*5225e6b1SAndroid Build Coastguard Worker let offset = do_strrchr("hello", 'h');
175*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, Some(0));
176*5225e6b1SAndroid Build Coastguard Worker }
177*5225e6b1SAndroid Build Coastguard Worker
178*5225e6b1SAndroid Build Coastguard Worker #[test]
strrchr_find_last_occurrence_at_end()179*5225e6b1SAndroid Build Coastguard Worker fn strrchr_find_last_occurrence_at_end() {
180*5225e6b1SAndroid Build Coastguard Worker let offset = do_strrchr("hello", 'o');
181*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, Some(4));
182*5225e6b1SAndroid Build Coastguard Worker }
183*5225e6b1SAndroid Build Coastguard Worker
184*5225e6b1SAndroid Build Coastguard Worker #[test]
strrchr_empty_string()185*5225e6b1SAndroid Build Coastguard Worker fn strrchr_empty_string() {
186*5225e6b1SAndroid Build Coastguard Worker let offset = do_strrchr("", 'a');
187*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, None);
188*5225e6b1SAndroid Build Coastguard Worker }
189*5225e6b1SAndroid Build Coastguard Worker
190*5225e6b1SAndroid Build Coastguard Worker #[test]
strrchr_find_last_occurrence_multiple()191*5225e6b1SAndroid Build Coastguard Worker fn strrchr_find_last_occurrence_multiple() {
192*5225e6b1SAndroid Build Coastguard Worker let offset = do_strrchr("hellohello", 'l');
193*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, Some(8));
194*5225e6b1SAndroid Build Coastguard Worker }
195*5225e6b1SAndroid Build Coastguard Worker
196*5225e6b1SAndroid Build Coastguard Worker #[test]
strrchr_case_sensitivity()197*5225e6b1SAndroid Build Coastguard Worker fn strrchr_case_sensitivity() {
198*5225e6b1SAndroid Build Coastguard Worker let offset = do_strrchr("Hello", 'h');
199*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, None);
200*5225e6b1SAndroid Build Coastguard Worker }
201*5225e6b1SAndroid Build Coastguard Worker
202*5225e6b1SAndroid Build Coastguard Worker #[test]
strrchr_find_null_character()203*5225e6b1SAndroid Build Coastguard Worker fn strrchr_find_null_character() {
204*5225e6b1SAndroid Build Coastguard Worker let offset = do_strchr("Hello", '\0');
205*5225e6b1SAndroid Build Coastguard Worker assert_eq!(offset, Some(5));
206*5225e6b1SAndroid Build Coastguard Worker }
207*5225e6b1SAndroid Build Coastguard Worker }
208