1use crate::abi::allocator::xmlMalloc;
7use crate::abi::types::xmlChar;
8use core::ffi::c_void;
9use core::ptr;
10use std::os::raw::c_char;
11use std::slice;
12
13#[inline]
23pub(crate) unsafe fn xml_strlen(str: *const xmlChar) -> usize {
24 if str.is_null() {
25 return 0;
26 }
27 let mut len: usize = 0;
28 while *str.add(len) != 0 {
29 len += 1;
30 }
31 len
32}
33
34#[inline]
45pub(crate) unsafe fn xml_strdup(str: *const xmlChar) -> *mut xmlChar {
46 if str.is_null() {
47 return ptr::null_mut();
48 }
49 let len = xml_strlen(str);
50 let copy = xmlMalloc(len + 1) as *mut xmlChar;
51 if copy.is_null() {
52 return ptr::null_mut();
53 }
54 ptr::copy_nonoverlapping(str, copy, len + 1);
55 copy
56}
57
58#[inline]
64pub(crate) unsafe fn c_strdup(str: *const c_char) -> *mut c_char {
65 if str.is_null() {
66 return ptr::null_mut();
67 }
68 let len = libc::strlen(str);
69 let copy = xmlMalloc(len + 1) as *mut c_char;
70 if copy.is_null() {
71 return ptr::null_mut();
72 }
73 ptr::copy_nonoverlapping(str as *const u8, copy as *mut u8, len + 1);
74 copy
75}
76
77pub(crate) unsafe fn bytes_to_xmlstr(bytes: &[u8]) -> *mut xmlChar {
83 let len = bytes.len();
84 let ptr = xmlMalloc(len + 1) as *mut xmlChar;
85 if ptr.is_null() {
86 return ptr::null_mut();
87 }
88 ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, len);
89 *ptr.add(len) = 0; ptr
91}
92
93#[inline]
100pub(crate) unsafe fn xmlstr_to_bytes(str: *const xmlChar) -> &'static [u8] {
101 if str.is_null() {
102 return &[];
103 }
104 let len = xml_strlen(str);
105 slice::from_raw_parts(str, len)
106}
107
108#[inline]
116pub(crate) unsafe fn xml_strcmp(str1: *const xmlChar, str2: *const xmlChar) -> i32 {
117 if str1 == str2 {
118 return 0;
119 }
120 if str1.is_null() {
121 return -1;
122 }
123 if str2.is_null() {
124 return 1;
125 }
126 let mut i: usize = 0;
127 loop {
128 let a = *str1.add(i);
129 let b = *str2.add(i);
130 if a != b {
131 return a as i32 - b as i32;
132 }
133 if a == 0 {
134 return 0;
135 }
136 i += 1;
137 }
138}
139
140#[inline]
148pub(crate) unsafe fn xml_strcat(str1: *const xmlChar, str2: *const xmlChar) -> *mut xmlChar {
149 let len1 = xml_strlen(str1);
150 let len2 = xml_strlen(str2);
151 let result = xmlMalloc(len1 + len2 + 1) as *mut xmlChar;
152 if result.is_null() {
153 return ptr::null_mut();
154 }
155 if !str1.is_null() {
156 ptr::copy_nonoverlapping(str1, result, len1);
157 }
158 if !str2.is_null() {
159 ptr::copy_nonoverlapping(str2, result.add(len1), len2);
160 }
161 *result.add(len1 + len2) = 0;
162 result
163}
164
165#[inline]
171pub(crate) unsafe fn xml_str_starts_with(str: *const xmlChar, prefix: *const xmlChar) -> bool {
172 if str.is_null() || prefix.is_null() {
173 return false;
174 }
175 let mut i: usize = 0;
176 loop {
177 let p = *prefix.add(i);
178 if p == 0 {
179 return true; }
181 if *str.add(i) != p {
182 return false;
183 }
184 i += 1;
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191 use crate::abi::allocator::xmlFree;
192
193 #[test]
194 fn test_xml_strlen() {
195 unsafe {
196 assert_eq!(xml_strlen(ptr::null()), 0);
197 let s = b"hello\0" as *const u8 as *const xmlChar;
198 assert_eq!(xml_strlen(s), 5);
199 let empty = b"\0" as *const u8 as *const xmlChar;
200 assert_eq!(xml_strlen(empty), 0);
201 }
202 }
203
204 #[test]
205 fn test_xml_strdup() {
206 unsafe {
207 assert!(xml_strdup(ptr::null()).is_null());
208 let s = b"hello\0" as *const u8 as *const xmlChar;
209 let dup = xml_strdup(s);
210 assert!(!dup.is_null());
211 assert_eq!(xml_strlen(dup), 5);
212 assert_eq!(*dup.add(0), b'h');
213 assert_eq!(*dup.add(4), b'o');
214 assert_eq!(*dup.add(5), 0);
215 xmlFree(dup as *mut c_void);
216 }
217 }
218
219 #[test]
220 fn test_xml_strcmp() {
221 unsafe {
222 assert_eq!(xml_strcmp(ptr::null(), ptr::null()), 0);
223 assert!(xml_strcmp(b"a\0" as *const u8 as *const xmlChar, ptr::null()) > 0);
224 let a = b"abc\0" as *const u8 as *const xmlChar;
225 let b = b"abc\0" as *const u8 as *const xmlChar;
226 assert_eq!(xml_strcmp(a, b), 0);
227 let c = b"abd\0" as *const u8 as *const xmlChar;
228 assert!(xml_strcmp(a, c) < 0);
229 assert!(xml_strcmp(c, a) > 0);
230 }
231 }
232
233 #[test]
234 fn test_xml_strcat() {
235 unsafe {
236 let a = b"hello \0" as *const u8 as *const xmlChar;
237 let b = b"world\0" as *const u8 as *const xmlChar;
238 let result = xml_strcat(a, b);
239 assert!(!result.is_null());
240 assert_eq!(xml_strlen(result), 11);
241 let expected = b"hello world\0";
242 let mut i = 0;
243 while expected[i] != 0 {
244 assert_eq!(*result.add(i), expected[i]);
245 i += 1;
246 }
247 xmlFree(result as *mut c_void);
248 }
249 }
250
251 #[test]
252 fn test_bytes_to_xmlstr() {
253 unsafe {
254 let bytes = b"hello";
255 let ptr = bytes_to_xmlstr(bytes);
256 assert!(!ptr.is_null());
257 assert_eq!(xml_strlen(ptr), 5);
258 assert_eq!(*ptr.add(5), 0);
259 xmlFree(ptr as *mut c_void);
260 }
261 }
262
263 #[test]
264 fn test_xml_str_starts_with() {
265 unsafe {
266 let s = b"hello world\0" as *const u8 as *const xmlChar;
267 let prefix = b"hello\0" as *const u8 as *const xmlChar;
268 let not_prefix = b"world\0" as *const u8 as *const xmlChar;
269 assert!(xml_str_starts_with(s, prefix));
270 assert!(!xml_str_starts_with(s, not_prefix));
271 assert!(!xml_str_starts_with(ptr::null(), prefix));
272 assert!(!xml_str_starts_with(s, ptr::null()));
273 }
274 }
275}