1use crate::abi::allocator::{xmlFree, xmlMalloc, xmlRealloc};
7use crate::abi::types::xmlChar;
8use core::ffi::c_void;
9use core::ptr;
10use std::os::raw::{c_char, c_int};
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]
173pub(crate) unsafe fn xml_strndup(str: *const xmlChar, len: usize) -> *mut xmlChar {
174 if str.is_null() {
175 return ptr::null_mut();
176 }
177 let p = unsafe { xmlMalloc(len + 1) as *mut xmlChar };
178 if p.is_null() {
179 return ptr::null_mut();
180 }
181 unsafe {
182 ptr::copy_nonoverlapping(str, p, len);
183 *p.add(len) = 0;
184 }
185 p
186}
187
188pub(crate) unsafe fn xmlstr_to_string(str: *const xmlChar) -> String {
195 if str.is_null() {
196 return String::new();
197 }
198 let bytes = unsafe { xmlstr_to_bytes(str) };
199 String::from_utf8_lossy(bytes).to_string()
200}
201
202pub unsafe fn build_qname(
212 ncname: *const xmlChar,
213 prefix: *const xmlChar,
214 memory: *mut xmlChar,
215 len: c_int,
216) -> *mut xmlChar {
217 if ncname.is_null() {
218 return ptr::null_mut();
219 }
220 if prefix.is_null() {
221 return ncname as *mut xmlChar;
222 }
223 unsafe {
224 let lenn = xml_strlen(ncname);
225 let lenp = xml_strlen(prefix);
226 let ret = if memory.is_null() || (len as usize) < lenn + lenp + 2 {
227 let p = xmlMalloc(lenn + lenp + 2) as *mut xmlChar;
228 if p.is_null() {
229 return ptr::null_mut();
230 }
231 p
232 } else {
233 memory
234 };
235 ptr::copy_nonoverlapping(prefix, ret, lenp);
236 *ret.add(lenp) = b':' as xmlChar;
237 ptr::copy_nonoverlapping(ncname, ret.add(lenp + 1), lenn);
238 *ret.add(lenn + lenp + 1) = 0;
239 ret
240 }
241}
242
243pub unsafe fn split_qname2(name: *const xmlChar, prefix: *mut *mut xmlChar) -> *mut xmlChar {
253 if prefix.is_null() {
254 return ptr::null_mut();
255 }
256 unsafe {
257 *prefix = ptr::null_mut();
258 }
259 if name.is_null() {
260 return ptr::null_mut();
261 }
262 unsafe {
263 if *name == b':' as xmlChar {
265 return ptr::null_mut();
266 }
267 let mut len: usize = 0;
268 while *name.add(len) != 0 && *name.add(len) != b':' as xmlChar {
269 len += 1;
270 }
271 if *name.add(len) == 0 || *name.add(len + 1) == 0 {
272 return ptr::null_mut();
273 }
274 let p = xml_strndup(name, len);
275 if p.is_null() {
276 return ptr::null_mut();
277 }
278 *prefix = p;
279 let ret = xml_strdup(name.add(len + 1));
280 if ret.is_null() {
281 xmlFree(*prefix as *mut c_void);
282 *prefix = ptr::null_mut();
283 return ptr::null_mut();
284 }
285 ret
286 }
287}
288
289pub unsafe fn split_qname3(name: *const xmlChar, len: *mut c_int) -> c_int {
297 if name.is_null() || len.is_null() {
298 return 0;
299 }
300 unsafe {
301 if *name == b':' as xmlChar {
302 return 0;
303 }
304 let mut l: usize = 0;
305 while *name.add(l) != 0 && *name.add(l) != b':' as xmlChar {
306 l += 1;
307 }
308 if *name.add(l) == 0 {
309 return 0;
310 }
311 *len = l as c_int;
312 l as c_int
313 }
314}
315
316pub unsafe fn utf8_strlen(utf: *const xmlChar) -> c_int {
323 if utf.is_null() {
324 return 0;
325 }
326 unsafe {
327 let mut n: c_int = 0;
328 let mut cur = utf;
329 while *cur != 0 {
330 let c = *cur;
331 if c & 0x80 == 0 {
332 cur = cur.add(1);
333 } else if c & 0xe0 == 0xc0 {
334 cur = cur.add(2);
335 } else if c & 0xf0 == 0xe0 {
336 cur = cur.add(3);
337 } else if c & 0xf8 == 0xf0 {
338 cur = cur.add(4);
339 } else {
340 return n;
342 }
343 n += 1;
344 }
345 n
346 }
347}
348
349pub unsafe fn utf8_size(utf: *const xmlChar) -> c_int {
357 if utf.is_null() {
358 return -1;
359 }
360 unsafe {
361 let c = *utf;
362 if c == 0 {
363 return 0;
364 }
365 if c & 0x80 == 0 {
366 1
367 } else if c & 0xe0 == 0xc0 {
368 2
369 } else if c & 0xf0 == 0xe0 {
370 3
371 } else if c & 0xf8 == 0xf0 {
372 4
373 } else {
374 -1
375 }
376 }
377}
378
379pub unsafe fn check_utf8(utf: *const xmlChar) -> c_int {
386 if utf.is_null() {
387 return 0;
388 }
389 unsafe {
390 let mut cur = utf;
391 while *cur != 0 {
392 let c = *cur;
393 if c & 0x80 == 0 {
394 cur = cur.add(1);
395 } else if c & 0xe0 == 0xc0 {
396 let c1 = *cur.add(1);
398 if c1 & 0xc0 != 0x80 {
399 return 0;
400 }
401 cur = cur.add(2);
402 } else if c & 0xf0 == 0xe0 {
403 let c1 = *cur.add(1);
404 let c2 = *cur.add(2);
405 if c1 & 0xc0 != 0x80 || c2 & 0xc0 != 0x80 {
406 return 0;
407 }
408 cur = cur.add(3);
409 } else if c & 0xf8 == 0xf0 {
410 let c1 = *cur.add(1);
411 let c2 = *cur.add(2);
412 let c3 = *cur.add(3);
413 if c1 & 0xc0 != 0x80 || c2 & 0xc0 != 0x80 || c3 & 0xc0 != 0x80 {
414 return 0;
415 }
416 cur = cur.add(4);
417 } else {
418 return 0;
419 }
420 }
421 1
422 }
423}
424#[inline]
425pub(crate) unsafe fn xml_str_starts_with(str: *const xmlChar, prefix: *const xmlChar) -> bool {
426 if str.is_null() || prefix.is_null() {
427 return false;
428 }
429 let mut i: usize = 0;
430 loop {
431 let p = *prefix.add(i);
432 if p == 0 {
433 return true; }
435 if *str.add(i) != p {
436 return false;
437 }
438 i += 1;
439 }
440}
441
442#[cfg(test)]
443mod tests {
444 use super::*;
445 use crate::abi::allocator::xmlFree;
446
447 #[test]
448 fn test_xml_strlen() {
449 unsafe {
450 assert_eq!(xml_strlen(ptr::null()), 0);
451 let s = b"hello\0" as *const u8 as *const xmlChar;
452 assert_eq!(xml_strlen(s), 5);
453 let empty = b"\0" as *const u8 as *const xmlChar;
454 assert_eq!(xml_strlen(empty), 0);
455 }
456 }
457
458 #[test]
459 fn test_xml_strdup() {
460 unsafe {
461 assert!(xml_strdup(ptr::null()).is_null());
462 let s = b"hello\0" as *const u8 as *const xmlChar;
463 let dup = xml_strdup(s);
464 assert!(!dup.is_null());
465 assert_eq!(xml_strlen(dup), 5);
466 assert_eq!(*dup.add(0), b'h');
467 assert_eq!(*dup.add(4), b'o');
468 assert_eq!(*dup.add(5), 0);
469 xmlFree(dup as *mut c_void);
470 }
471 }
472
473 #[test]
474 fn test_xml_strcmp() {
475 unsafe {
476 assert_eq!(xml_strcmp(ptr::null(), ptr::null()), 0);
477 assert!(xml_strcmp(b"a\0" as *const u8 as *const xmlChar, ptr::null()) > 0);
478 let a = b"abc\0" as *const u8 as *const xmlChar;
479 let b = b"abc\0" as *const u8 as *const xmlChar;
480 assert_eq!(xml_strcmp(a, b), 0);
481 let c = b"abd\0" as *const u8 as *const xmlChar;
482 assert!(xml_strcmp(a, c) < 0);
483 assert!(xml_strcmp(c, a) > 0);
484 }
485 }
486
487 #[test]
488 fn test_xml_strcat() {
489 unsafe {
490 let a = b"hello \0" as *const u8 as *const xmlChar;
491 let b = b"world\0" as *const u8 as *const xmlChar;
492 let result = xml_strcat(a, b);
493 assert!(!result.is_null());
494 assert_eq!(xml_strlen(result), 11);
495 let expected = b"hello world\0";
496 let mut i = 0;
497 while expected[i] != 0 {
498 assert_eq!(*result.add(i), expected[i]);
499 i += 1;
500 }
501 xmlFree(result as *mut c_void);
502 }
503 }
504
505 #[test]
506 fn test_bytes_to_xmlstr() {
507 unsafe {
508 let bytes = b"hello";
509 let ptr = bytes_to_xmlstr(bytes);
510 assert!(!ptr.is_null());
511 assert_eq!(xml_strlen(ptr), 5);
512 assert_eq!(*ptr.add(5), 0);
513 xmlFree(ptr as *mut c_void);
514 }
515 }
516
517 #[test]
518 fn test_xml_str_starts_with() {
519 unsafe {
520 let s = b"hello world\0" as *const u8 as *const xmlChar;
521 let prefix = b"hello\0" as *const u8 as *const xmlChar;
522 let not_prefix = b"world\0" as *const u8 as *const xmlChar;
523 assert!(xml_str_starts_with(s, prefix));
524 assert!(!xml_str_starts_with(s, not_prefix));
525 assert!(!xml_str_starts_with(ptr::null(), prefix));
526 assert!(!xml_str_starts_with(s, ptr::null()));
527 }
528 }
529}