xref: /aosp_15_r20/bootable/libbootloader/gbl/libc/src/strcmp.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 strcmp libc functions family.
16 //! https://en.cppreference.com/w/c/string/byte/strncmp
17 
18 use core::cmp::Ordering;
19 use core::ffi::{c_char, c_int, CStr};
20 
21 /// int strcmp(const char *s1, const char *s2);
22 ///
23 /// # Safety
24 ///
25 /// * `s1` and `s2` must be valid pointers to null terminated C strings.
26 #[no_mangle]
strcmp(s1: *const c_char, s2: *const c_char) -> c_int27 pub unsafe extern "C" fn strcmp(s1: *const c_char, s2: *const c_char) -> c_int {
28     // SAFETY: `s1` and `s2` are valid null-terminated strings. References are only used
29     // within function.
30     let (lhs, rhs) = unsafe { (CStr::from_ptr(s1.cast()), CStr::from_ptr(s2.cast())) };
31     Ord::cmp(lhs, rhs) as _
32 }
33 
34 /// int strncmp(const char *s1, const char *s2, size_t n);
35 ///
36 /// # Safety
37 ///
38 /// * `s1` and `s2` must be at least nth sized or null terminated arrays.
39 #[no_mangle]
strncmp(s1: *const c_char, s2: *const c_char, n: usize) -> c_int40 pub unsafe extern "C" fn strncmp(s1: *const c_char, s2: *const c_char, n: usize) -> c_int {
41     for i in 0..n {
42         // SAFETY: `i` is always within the bounds of `s1` and `s2` because it is limited by `n`,
43         // and this statement is unreachable if a null character is already encountered in `s1`
44         // or `s2`.
45         let (l, r) = unsafe { (*s1.add(i), *s2.add(i)) };
46 
47         let cmp = l.cmp(&r);
48         if cmp != Ordering::Equal || l == 0 {
49             return cmp as _;
50         }
51     }
52     Ordering::Equal as _
53 }
54 
55 #[cfg(test)]
56 mod test {
57     use super::*;
58     use std::ffi::CString;
59 
to_cstr(s: &str) -> CString60     fn to_cstr(s: &str) -> CString {
61         CString::new(s).unwrap()
62     }
63 
do_strcmp(left: &str, right: &str) -> c_int64     fn do_strcmp(left: &str, right: &str) -> c_int {
65         let left_cstr = to_cstr(left);
66         let right_cstr = to_cstr(right);
67         // SAFETY: `left_cstr` and `right_cstr` are null terminated strings.
68         unsafe { strcmp(left_cstr.as_ptr(), right_cstr.as_ptr()) }
69     }
70 
do_strncmp(left: &str, right: &str, n: usize) -> c_int71     fn do_strncmp(left: &str, right: &str, n: usize) -> c_int {
72         let left_cstr = to_cstr(left);
73         let right_cstr = to_cstr(right);
74         // SAFETY: `left_cstr` and `right_cstr` are null terminated strings.
75         unsafe { strncmp(left_cstr.as_ptr(), right_cstr.as_ptr(), n) }
76     }
77 
do_strncmp_bytes(left: &[u8], right: &[u8], n: usize) -> c_int78     fn do_strncmp_bytes(left: &[u8], right: &[u8], n: usize) -> c_int {
79         // SAFETY: `left` and `right` are not null.
80         unsafe { strncmp(left.as_ptr().cast(), right.as_ptr().cast(), n) }
81     }
82 
83     // strcmp tests
84 
85     #[test]
strcmp_same()86     fn strcmp_same() {
87         assert_eq!(do_strcmp("first", "first"), 0);
88     }
89 
90     #[test]
strcmp_same_special_characters()91     fn strcmp_same_special_characters() {
92         assert_eq!(do_strcmp("!@#", "!@#"), 0);
93     }
94 
95     #[test]
strcmp_left_smaller()96     fn strcmp_left_smaller() {
97         assert_eq!(do_strcmp("first", "second"), -1);
98     }
99 
100     #[test]
strcmp_left_is_prefix_of_right()101     fn strcmp_left_is_prefix_of_right() {
102         assert_eq!(do_strcmp("first", "firstly"), -1);
103     }
104 
105     #[test]
strcmp_right_is_prefix_of_left()106     fn strcmp_right_is_prefix_of_left() {
107         assert_eq!(do_strcmp("firstly", "first"), 1);
108     }
109 
110     #[test]
strcmp_empty()111     fn strcmp_empty() {
112         assert_eq!(do_strcmp("", ""), 0);
113     }
114 
115     #[test]
strcmp_empty_vs_non_empty()116     fn strcmp_empty_vs_non_empty() {
117         assert_eq!(do_strcmp("", "nonempty"), -1);
118     }
119 
120     #[test]
strcmp_non_empty_vs_empty()121     fn strcmp_non_empty_vs_empty() {
122         assert_eq!(do_strcmp("nonempty", ""), 1);
123     }
124 
125     #[test]
strcmp_case_sensitivity()126     fn strcmp_case_sensitivity() {
127         assert_eq!(do_strcmp("First", "first"), -1);
128     }
129 
130     // strncmp tests
131 
132     #[test]
strncmp_same_exact_length()133     fn strncmp_same_exact_length() {
134         assert_eq!(do_strncmp("hello", "hello", 5), 0);
135     }
136 
137     #[test]
strncmp_same_partial_length()138     fn strncmp_same_partial_length() {
139         assert_eq!(do_strncmp("hello", "hello", 3), 0);
140     }
141 
142     #[test]
strncmp_same_overflow()143     fn strncmp_same_overflow() {
144         assert_eq!(do_strncmp("hello", "hello", 100), 0);
145     }
146 
147     #[test]
strncmp_same_special_characters()148     fn strncmp_same_special_characters() {
149         assert_eq!(do_strncmp("!@#", "!@#", 3), 0);
150     }
151 
152     #[test]
strncmp_different_exact_length()153     fn strncmp_different_exact_length() {
154         assert_eq!(do_strncmp("hello", "world", 5), -1);
155     }
156 
157     #[test]
strncmp_different_partial_length()158     fn strncmp_different_partial_length() {
159         assert_eq!(do_strncmp("hello", "world", 3), -1);
160     }
161 
162     #[test]
strncmp_left_is_prefix_of_right()163     fn strncmp_left_is_prefix_of_right() {
164         assert_eq!(do_strncmp("abc", "abcdef", 6), -1);
165     }
166 
167     #[test]
strncmp_right_is_prefix_of_left()168     fn strncmp_right_is_prefix_of_left() {
169         assert_eq!(do_strncmp("abcdef", "abc", 6), 1);
170     }
171 
172     #[test]
strncmp_empty_strings()173     fn strncmp_empty_strings() {
174         assert_eq!(do_strncmp("", "", 5), 0);
175     }
176 
177     #[test]
strncmp_empty_vs_non_empty()178     fn strncmp_empty_vs_non_empty() {
179         assert_eq!(do_strncmp("", "hello", 5), -1);
180     }
181 
182     #[test]
strncmp_non_empty_vs_empty()183     fn strncmp_non_empty_vs_empty() {
184         assert_eq!(do_strncmp("hello", "", 5), 1);
185     }
186 
187     #[test]
strncmp_case_sensitivity()188     fn strncmp_case_sensitivity() {
189         assert_eq!(do_strncmp("Hello", "hello", 5), -1);
190     }
191 
192     #[test]
strncmp_bytes_array_same_exact_length()193     fn strncmp_bytes_array_same_exact_length() {
194         assert_eq!(do_strncmp_bytes(b"hello", b"hello", 5), 0);
195     }
196 
197     #[test]
strncmp_bytes_array_right_terminated()198     fn strncmp_bytes_array_right_terminated() {
199         assert_eq!(do_strncmp_bytes(b"hello", b"hel\0", 5), 1);
200     }
201 
202     #[test]
strncmp_bytes_array_left_terminated()203     fn strncmp_bytes_array_left_terminated() {
204         assert_eq!(do_strncmp_bytes(b"hel\0", b"hello", 5), -1);
205     }
206 }
207