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