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