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