1use crate::abi::allocator::{xmlFreeImpl, xmlMallocImpl};
57use crate::abi::types::xmlChar;
58use core::ffi::c_void;
59use core::ptr;
60use std::os::raw::{c_char, c_int};
61use std::slice;
62
63#[inline]
73pub(crate) const unsafe fn xml_strlen(str: *const xmlChar) -> usize {
74 if str.is_null() {
75 return 0;
76 }
77 let mut len: usize = 0;
78 while *str.add(len) != 0 {
79 len += 1;
80 }
81 len
82}
83
84#[inline]
95pub(crate) unsafe fn xml_strdup(str: *const xmlChar) -> *mut xmlChar {
96 if str.is_null() {
97 return ptr::null_mut();
98 }
99 let len = xml_strlen(str);
100 let copy = xmlMallocImpl(len + 1) as *mut xmlChar;
101 if copy.is_null() {
102 return ptr::null_mut();
103 }
104 ptr::copy_nonoverlapping(str, copy, len + 1);
105 copy
106}
107
108#[inline]
114pub(crate) unsafe fn c_strdup(str: *const c_char) -> *mut c_char {
115 if str.is_null() {
116 return ptr::null_mut();
117 }
118 let len = libc::strlen(str);
119 let copy = xmlMallocImpl(len + 1) as *mut c_char;
120 if copy.is_null() {
121 return ptr::null_mut();
122 }
123 ptr::copy_nonoverlapping(str as *const u8, copy as *mut u8, len + 1);
124 copy
125}
126
127pub(crate) unsafe fn bytes_to_xmlstr(bytes: &[u8]) -> *mut xmlChar {
133 let len = bytes.len();
134 let ptr = xmlMallocImpl(len + 1) as *mut xmlChar;
135 if ptr.is_null() {
136 return ptr::null_mut();
137 }
138 ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, len);
139 *ptr.add(len) = 0; ptr
141}
142
143#[inline]
150pub(crate) const unsafe fn xmlstr_to_bytes(str: *const xmlChar) -> &'static [u8] {
151 if str.is_null() {
152 return &[];
153 }
154 let len = xml_strlen(str);
155 slice::from_raw_parts(str, len)
156}
157
158#[inline]
166pub(crate) unsafe fn xml_strcmp(str1: *const xmlChar, str2: *const xmlChar) -> i32 {
167 if str1 == str2 {
168 return 0;
169 }
170 if str1.is_null() {
171 return -1;
172 }
173 if str2.is_null() {
174 return 1;
175 }
176 let mut i: usize = 0;
177 loop {
178 let a = *str1.add(i);
179 let b = *str2.add(i);
180 if a != b {
181 return a as i32 - b as i32;
182 }
183 if a == 0 {
184 return 0;
185 }
186 i += 1;
187 }
188}
189
190#[inline]
198#[allow(dead_code)]
199pub(crate) unsafe fn xml_strcat(str1: *const xmlChar, str2: *const xmlChar) -> *mut xmlChar {
200 let len1 = xml_strlen(str1);
201 let len2 = xml_strlen(str2);
202 let result = xmlMallocImpl(len1 + len2 + 1) as *mut xmlChar;
203 if result.is_null() {
204 return ptr::null_mut();
205 }
206 if !str1.is_null() {
207 ptr::copy_nonoverlapping(str1, result, len1);
208 }
209 if !str2.is_null() {
210 ptr::copy_nonoverlapping(str2, result.add(len1), len2);
211 }
212 *result.add(len1 + len2) = 0;
213 result
214}
215
216#[inline]
224pub(crate) unsafe fn xml_strndup(str: *const xmlChar, len: usize) -> *mut xmlChar {
225 if str.is_null() {
226 return ptr::null_mut();
227 }
228 let p = unsafe { xmlMallocImpl(len + 1) as *mut xmlChar };
229 if p.is_null() {
230 return ptr::null_mut();
231 }
232 unsafe {
233 ptr::copy_nonoverlapping(str, p, len);
234 *p.add(len) = 0;
235 }
236 p
237}
238
239pub(crate) unsafe fn xmlstr_to_string(str: *const xmlChar) -> String {
246 if str.is_null() {
247 return String::new();
248 }
249 let bytes = unsafe { xmlstr_to_bytes(str) };
250 String::from_utf8_lossy(bytes).to_string()
251}
252
253pub unsafe fn build_qname(
263 ncname: *const xmlChar,
264 prefix: *const xmlChar,
265 memory: *mut xmlChar,
266 len: c_int,
267) -> *mut xmlChar {
268 if ncname.is_null() {
269 return ptr::null_mut();
270 }
271 if prefix.is_null() {
272 return ncname as *mut xmlChar;
273 }
274 unsafe {
275 let lenn = xml_strlen(ncname);
276 let lenp = xml_strlen(prefix);
277 let ret = if memory.is_null() || (len as usize) < lenn + lenp + 2 {
278 let p = xmlMallocImpl(lenn + lenp + 2) as *mut xmlChar;
279 if p.is_null() {
280 return ptr::null_mut();
281 }
282 p
283 } else {
284 memory
285 };
286 ptr::copy_nonoverlapping(prefix, ret, lenp);
287 *ret.add(lenp) = b':' as xmlChar;
288 ptr::copy_nonoverlapping(ncname, ret.add(lenp + 1), lenn);
289 *ret.add(lenn + lenp + 1) = 0;
290 ret
291 }
292}
293
294pub unsafe fn split_qname2(name: *const xmlChar, prefix: *mut *mut xmlChar) -> *mut xmlChar {
304 if prefix.is_null() {
305 return ptr::null_mut();
306 }
307 unsafe {
308 *prefix = ptr::null_mut();
309 }
310 if name.is_null() {
311 return ptr::null_mut();
312 }
313 unsafe {
314 if *name == b':' as xmlChar {
316 return ptr::null_mut();
317 }
318 let mut len: usize = 0;
319 while *name.add(len) != 0 && *name.add(len) != b':' as xmlChar {
320 len += 1;
321 }
322 if *name.add(len) == 0 || *name.add(len + 1) == 0 {
323 return ptr::null_mut();
324 }
325 let p = xml_strndup(name, len);
326 if p.is_null() {
327 return ptr::null_mut();
328 }
329 *prefix = p;
330 let ret = xml_strdup(name.add(len + 1));
331 if ret.is_null() {
332 xmlFreeImpl(*prefix as *mut c_void);
333 *prefix = ptr::null_mut();
334 return ptr::null_mut();
335 }
336 ret
337 }
338}
339
340pub unsafe fn split_qname3(name: *const xmlChar, len: *mut c_int) -> c_int {
348 if name.is_null() || len.is_null() {
349 return 0;
350 }
351 unsafe {
352 if *name == b':' as xmlChar {
353 return 0;
354 }
355 let mut l: usize = 0;
356 while *name.add(l) != 0 && *name.add(l) != b':' as xmlChar {
357 l += 1;
358 }
359 if *name.add(l) == 0 {
360 return 0;
361 }
362 *len = l as c_int;
363 l as c_int
364 }
365}
366
367pub const unsafe fn utf8_strlen(utf: *const xmlChar) -> c_int {
374 if utf.is_null() {
375 return 0;
376 }
377 unsafe {
378 let mut n: c_int = 0;
379 let mut cur = utf;
380 while *cur != 0 {
381 let c = *cur;
382 if c & 0x80 == 0 {
383 cur = cur.add(1);
384 } else if c & 0xe0 == 0xc0 {
385 cur = cur.add(2);
386 } else if c & 0xf0 == 0xe0 {
387 cur = cur.add(3);
388 } else if c & 0xf8 == 0xf0 {
389 cur = cur.add(4);
390 } else {
391 return n;
393 }
394 n += 1;
395 }
396 n
397 }
398}
399
400pub const unsafe fn utf8_size(utf: *const xmlChar) -> c_int {
408 if utf.is_null() {
409 return -1;
410 }
411 unsafe {
412 let c = *utf;
413 if c == 0 {
414 return 0;
415 }
416 if c & 0x80 == 0 {
417 1
418 } else if c & 0xe0 == 0xc0 {
419 2
420 } else if c & 0xf0 == 0xe0 {
421 3
422 } else if c & 0xf8 == 0xf0 {
423 4
424 } else {
425 -1
426 }
427 }
428}
429
430pub const unsafe fn check_utf8(utf: *const xmlChar) -> c_int {
437 if utf.is_null() {
438 return 0;
439 }
440 unsafe {
441 let mut cur = utf;
442 while *cur != 0 {
443 let c = *cur;
444 if c & 0x80 == 0 {
445 cur = cur.add(1);
446 } else if c & 0xe0 == 0xc0 {
447 let c1 = *cur.add(1);
449 if c1 & 0xc0 != 0x80 {
450 return 0;
451 }
452 cur = cur.add(2);
453 } else if c & 0xf0 == 0xe0 {
454 let c1 = *cur.add(1);
455 let c2 = *cur.add(2);
456 if c1 & 0xc0 != 0x80 || c2 & 0xc0 != 0x80 {
457 return 0;
458 }
459 cur = cur.add(3);
460 } else if c & 0xf8 == 0xf0 {
461 let c1 = *cur.add(1);
462 let c2 = *cur.add(2);
463 let c3 = *cur.add(3);
464 if c1 & 0xc0 != 0x80 || c2 & 0xc0 != 0x80 || c3 & 0xc0 != 0x80 {
465 return 0;
466 }
467 cur = cur.add(4);
468 } else {
469 return 0;
470 }
471 }
472 1
473 }
474}
475#[inline]
476pub(crate) const unsafe fn xml_str_starts_with(
477 str: *const xmlChar,
478 prefix: *const xmlChar,
479) -> bool {
480 if str.is_null() || prefix.is_null() {
481 return false;
482 }
483 let mut i: usize = 0;
484 loop {
485 let p = *prefix.add(i);
486 if p == 0 {
487 return true; }
489 if *str.add(i) != p {
490 return false;
491 }
492 i += 1;
493 }
494}
495
496#[cfg(test)]
497mod tests {
498 use super::*;
499 use crate::abi::allocator::xmlFreeImpl;
500
501 #[test]
502 fn test_xml_strlen() {
503 unsafe {
504 assert_eq!(xml_strlen(ptr::null()), 0);
505 let s = b"hello\0" as *const u8 as *const xmlChar;
506 assert_eq!(xml_strlen(s), 5);
507 let empty = b"\0" as *const u8 as *const xmlChar;
508 assert_eq!(xml_strlen(empty), 0);
509 }
510 }
511
512 #[test]
513 fn test_xml_strdup() {
514 unsafe {
515 assert!(xml_strdup(ptr::null()).is_null());
516 let s = b"hello\0" as *const u8 as *const xmlChar;
517 let dup = xml_strdup(s);
518 assert!(!dup.is_null());
519 assert_eq!(xml_strlen(dup), 5);
520 assert_eq!(*dup.add(0), b'h');
521 assert_eq!(*dup.add(4), b'o');
522 assert_eq!(*dup.add(5), 0);
523 xmlFreeImpl(dup as *mut c_void);
524 }
525 }
526
527 #[test]
528 fn test_xml_strcmp() {
529 unsafe {
530 assert_eq!(xml_strcmp(ptr::null(), ptr::null()), 0);
531 assert!(xml_strcmp(b"a\0" as *const u8 as *const xmlChar, ptr::null()) > 0);
532 let a = b"abc\0" as *const u8 as *const xmlChar;
533 let b = b"abc\0" as *const u8 as *const xmlChar;
534 assert_eq!(xml_strcmp(a, b), 0);
535 let c = b"abd\0" as *const u8 as *const xmlChar;
536 assert!(xml_strcmp(a, c) < 0);
537 assert!(xml_strcmp(c, a) > 0);
538 }
539 }
540
541 #[test]
542 fn test_xml_strcat() {
543 unsafe {
544 let a = b"hello \0" as *const u8 as *const xmlChar;
545 let b = b"world\0" as *const u8 as *const xmlChar;
546 let result = xml_strcat(a, b);
547 assert!(!result.is_null());
548 assert_eq!(xml_strlen(result), 11);
549 let expected = b"hello world\0";
550 let mut i = 0;
551 while expected[i] != 0 {
552 assert_eq!(*result.add(i), expected[i]);
553 i += 1;
554 }
555 xmlFreeImpl(result as *mut c_void);
556 }
557 }
558
559 #[test]
560 fn test_bytes_to_xmlstr() {
561 unsafe {
562 let bytes = b"hello";
563 let ptr = bytes_to_xmlstr(bytes);
564 assert!(!ptr.is_null());
565 assert_eq!(xml_strlen(ptr), 5);
566 assert_eq!(*ptr.add(5), 0);
567 xmlFreeImpl(ptr as *mut c_void);
568 }
569 }
570
571 #[test]
572 fn test_xml_str_starts_with() {
573 unsafe {
574 let s = b"hello world\0" as *const u8 as *const xmlChar;
575 let prefix = b"hello\0" as *const u8 as *const xmlChar;
576 let not_prefix = b"world\0" as *const u8 as *const xmlChar;
577 assert!(xml_str_starts_with(s, prefix));
578 assert!(!xml_str_starts_with(s, not_prefix));
579 assert!(!xml_str_starts_with(ptr::null(), prefix));
580 assert!(!xml_str_starts_with(s, ptr::null()));
581 }
582 }
583}