use core::ffi::c_void;
use core::ptr;
use std::os::raw::c_int;
use crate::abi::allocator;
pub type xmlListDeallocator = unsafe extern "C" fn(*mut c_void);
pub type xmlListDataCompare = unsafe extern "C" fn(*const c_void, *const c_void) -> c_int;
pub type xmlListWalker = unsafe extern "C" fn(*mut c_void, *mut c_void) -> c_int;
struct ListNode {
data: *mut c_void,
prev: *mut ListNode,
next: *mut ListNode,
}
pub struct List {
front: *mut ListNode,
back: *mut ListNode,
count: usize,
deallocator: Option<xmlListDeallocator>,
comparator: Option<xmlListDataCompare>,
}
pub fn list_create(
deallocator: Option<xmlListDeallocator>,
comparator: Option<xmlListDataCompare>,
) -> *mut List {
let list = Box::new(List {
front: ptr::null_mut(),
back: ptr::null_mut(),
count: 0,
deallocator,
comparator,
});
Box::into_raw(list)
}
pub unsafe fn list_delete(l: *mut List) {
if l.is_null() {
return;
}
let list = unsafe { &mut *l };
let mut cur = list.front;
while !cur.is_null() {
let next = unsafe { (*cur).next };
if let Some(dealloc) = list.deallocator {
unsafe { dealloc((*cur).data) };
}
unsafe { allocator::xmlFree(cur as *mut c_void) };
cur = next;
}
drop(Box::from_raw(l));
}
pub unsafe fn list_search(l: *mut List, data: *const c_void) -> *mut c_void {
if l.is_null() {
return ptr::null_mut();
}
let list = unsafe { &*l };
let comparator = match list.comparator {
Some(c) => c,
None => return ptr::null_mut(),
};
let mut cur = list.front;
while !cur.is_null() {
let node = unsafe { &*cur };
if unsafe { comparator(node.data as *const c_void, data) == 0 } {
return node.data;
}
cur = node.next;
}
ptr::null_mut()
}
pub unsafe fn list_walk(l: *mut List, walker: Option<xmlListWalker>, data: *mut c_void) {
if l.is_null() || walker.is_none() {
return;
}
let walker = walker.unwrap();
let list = unsafe { &*l };
let mut cur = list.front;
while !cur.is_null() {
let node = unsafe { &*cur };
if unsafe { walker(node.data, data) != 0 } {
break;
}
cur = node.next;
}
}
pub unsafe fn list_push_back(l: *mut List, data: *mut c_void) -> c_int {
if l.is_null() {
return -1;
}
let list = unsafe { &mut *l };
let node = allocator::xmlMallocZero(size_of::<ListNode>() as usize) as *mut ListNode;
if node.is_null() {
return -1;
}
unsafe {
(*node).data = data;
(*node).prev = list.back;
(*node).next = ptr::null_mut();
}
if list.back.is_null() {
list.front = node;
list.back = node;
} else {
unsafe { (*list.back).next = node };
list.back = node;
}
list.count += 1;
0
}
pub unsafe fn list_push_front(l: *mut List, data: *mut c_void) -> c_int {
if l.is_null() {
return -1;
}
let list = unsafe { &mut *l };
let node = allocator::xmlMallocZero(size_of::<ListNode>() as usize) as *mut ListNode;
if node.is_null() {
return -1;
}
unsafe {
(*node).data = data;
(*node).prev = ptr::null_mut();
(*node).next = list.front;
}
if list.front.is_null() {
list.front = node;
list.back = node;
} else {
unsafe { (*list.front).prev = node };
list.front = node;
}
list.count += 1;
0
}
pub unsafe fn list_pop_back(l: *mut List) {
if l.is_null() {
return;
}
let list = unsafe { &mut *l };
if list.back.is_null() {
return;
}
let node = list.back;
let prev = unsafe { (*node).prev };
if let Some(dealloc) = list.deallocator {
unsafe { dealloc((*node).data) };
}
unsafe { allocator::xmlFree(node as *mut c_void) };
list.back = prev;
if prev.is_null() {
list.front = ptr::null_mut();
} else {
unsafe { (*prev).next = ptr::null_mut() };
}
list.count = list.count.saturating_sub(1);
}
pub unsafe fn list_pop_front(l: *mut List) {
if l.is_null() {
return;
}
let list = unsafe { &mut *l };
if list.front.is_null() {
return;
}
let node = list.front;
let next = unsafe { (*node).next };
if let Some(dealloc) = list.deallocator {
unsafe { dealloc((*node).data) };
}
unsafe { allocator::xmlFree(node as *mut c_void) };
list.front = next;
if next.is_null() {
list.back = ptr::null_mut();
} else {
unsafe { (*next).prev = ptr::null_mut() };
}
list.count = list.count.saturating_sub(1);
}
pub unsafe fn list_insert(l: *mut List, data: *mut c_void) -> c_int {
if l.is_null() {
return -1;
}
let list = unsafe { &mut *l };
let comparator = match list.comparator {
Some(c) => c,
None => return list_push_back(l, data),
};
let mut cur = list.front;
while !cur.is_null() {
let node = unsafe { &*cur };
if unsafe { comparator(data as *const c_void, node.data as *const c_void) <= 0 } {
let new_node =
allocator::xmlMallocZero(size_of::<ListNode>() as usize) as *mut ListNode;
if new_node.is_null() {
return -1;
}
unsafe {
(*new_node).data = data;
(*new_node).prev = node.prev;
(*new_node).next = cur;
if !node.prev.is_null() {
(*node.prev).next = new_node;
} else {
list.front = new_node;
}
(*cur).prev = new_node;
}
list.count += 1;
return 0;
}
cur = node.next;
}
list_push_back(l, data)
}
pub unsafe fn list_append(l: *mut List, data: *mut c_void) -> c_int {
list_push_back(l, data)
}
pub unsafe fn list_remove_first(l: *mut List, data: *const c_void) -> c_int {
if l.is_null() {
return -1;
}
let list = unsafe { &mut *l };
let comparator = match list.comparator {
Some(c) => c,
None => return -1,
};
let mut cur = list.front;
while !cur.is_null() {
let node = unsafe { &*cur };
let next = node.next;
if unsafe { comparator(node.data as *const c_void, data) == 0 } {
if !node.prev.is_null() {
unsafe { (*node.prev).next = node.next };
} else {
list.front = node.next;
}
if !node.next.is_null() {
unsafe { (*node.next).prev = node.prev };
} else {
list.back = node.prev;
}
if let Some(dealloc) = list.deallocator {
unsafe { dealloc(node.data) };
}
unsafe { allocator::xmlFree(cur as *mut c_void) };
list.count = list.count.saturating_sub(1);
return 0;
}
cur = next;
}
-1
}
pub unsafe fn list_remove_last(l: *mut List, data: *const c_void) -> c_int {
if l.is_null() {
return -1;
}
let list = unsafe { &mut *l };
let comparator = match list.comparator {
Some(c) => c,
None => return -1,
};
let mut cur = list.back;
while !cur.is_null() {
let node = unsafe { &*cur };
let prev = node.prev;
if unsafe { comparator(node.data as *const c_void, data) == 0 } {
if !node.prev.is_null() {
unsafe { (*node.prev).next = node.next };
} else {
list.front = node.next;
}
if !node.next.is_null() {
unsafe { (*node.next).prev = node.prev };
} else {
list.back = node.prev;
}
if let Some(dealloc) = list.deallocator {
unsafe { dealloc(node.data) };
}
unsafe { allocator::xmlFree(cur as *mut c_void) };
list.count = list.count.saturating_sub(1);
return 0;
}
cur = prev;
}
-1
}
pub unsafe fn list_remove_all(l: *mut List, data: *const c_void) -> c_int {
if l.is_null() {
return 0;
}
let list = unsafe { &mut *l };
let comparator = match list.comparator {
Some(c) => c,
None => return 0,
};
let mut removed = 0;
let mut cur = list.front;
while !cur.is_null() {
let node = unsafe { &*cur };
let next = node.next;
if unsafe { comparator(node.data as *const c_void, data) == 0 } {
if !node.prev.is_null() {
unsafe { (*node.prev).next = node.next };
} else {
list.front = node.next;
}
if !node.next.is_null() {
unsafe { (*node.next).prev = node.prev };
} else {
list.back = node.prev;
}
if let Some(dealloc) = list.deallocator {
unsafe { dealloc(node.data) };
}
unsafe { allocator::xmlFree(cur as *mut c_void) };
list.count = list.count.saturating_sub(1);
removed += 1;
}
cur = next;
}
removed
}
pub unsafe fn list_clear(l: *mut List) {
if l.is_null() {
return;
}
let list = unsafe { &mut *l };
let mut cur = list.front;
while !cur.is_null() {
let next = unsafe { (*cur).next };
if let Some(dealloc) = list.deallocator {
unsafe { dealloc((*cur).data) };
}
unsafe { allocator::xmlFree(cur as *mut c_void) };
cur = next;
}
list.front = ptr::null_mut();
list.back = ptr::null_mut();
list.count = 0;
}
pub fn list_empty(l: *mut List) -> c_int {
if l.is_null() {
return 1;
}
let list = unsafe { &*l };
if list.front.is_null() {
1
} else {
0
}
}
pub fn list_front(l: *mut List) -> *mut c_void {
if l.is_null() {
return ptr::null_mut();
}
let list = unsafe { &*l };
if list.front.is_null() {
ptr::null_mut()
} else {
unsafe { (*list.front).data }
}
}
pub fn list_back(l: *mut List) -> *mut c_void {
if l.is_null() {
return ptr::null_mut();
}
let list = unsafe { &*l };
if list.back.is_null() {
ptr::null_mut()
} else {
unsafe { (*list.back).data }
}
}
pub fn list_size(l: *mut List) -> c_int {
if l.is_null() {
return -1;
}
let list = unsafe { &*l };
list.count as c_int
}
pub unsafe fn list_sort(l: *mut List) {
if l.is_null() {
return;
}
let list = unsafe { &mut *l };
if list.count <= 1 {
return;
}
let comparator = match list.comparator {
Some(c) => c,
None => return,
};
let mut nodes: Vec<*mut ListNode> = Vec::with_capacity(list.count);
let mut cur = list.front;
while !cur.is_null() {
nodes.push(cur);
cur = unsafe { (*cur).next };
}
for i in 0..nodes.len() {
for j in 0..nodes.len() - 1 - i {
let a = unsafe { &*nodes[j] };
let b = unsafe { &*nodes[j + 1] };
if unsafe { comparator(a.data as *const c_void, b.data as *const c_void) > 0 } {
nodes.swap(j, j + 1);
}
}
}
list.front = nodes[0];
list.back = nodes[nodes.len() - 1];
for i in 0..nodes.len() {
unsafe {
(*nodes[i]).prev = if i > 0 { nodes[i - 1] } else { ptr::null_mut() };
(*nodes[i]).next = if i + 1 < nodes.len() {
nodes[i + 1]
} else {
ptr::null_mut()
};
}
}
}
pub unsafe fn list_reverse(l: *mut List) {
if l.is_null() {
return;
}
let list = unsafe { &mut *l };
let mut cur = list.front;
list.front = list.back;
list.back = cur;
while !cur.is_null() {
let next = unsafe { (*cur).next };
unsafe {
(*cur).next = (*cur).prev;
(*cur).prev = next;
}
cur = next;
}
}
pub unsafe fn list_reverse_splice(l1: *mut List, l2: *mut List) {
if l1.is_null() || l2.is_null() {
return;
}
let list1 = unsafe { &mut *l1 };
let list2 = unsafe { &mut *l2 };
if list2.front.is_null() {
return;
}
list_reverse(l2);
unsafe {
(*list2.back).next = list1.front;
if !list1.front.is_null() {
(*list1.front).prev = list2.back;
} else {
list1.back = list2.back;
}
list1.front = list2.front;
}
list1.count += list2.count;
list2.front = ptr::null_mut();
list2.back = ptr::null_mut();
list2.count = 0;
}
pub unsafe fn list_merge(l1: *mut List, l2: *mut List) {
if l1.is_null() || l2.is_null() {
return;
}
let list1 = unsafe { &mut *l1 };
let list2 = unsafe { &mut *l2 };
if list2.front.is_null() {
return;
}
let comparator = match list1.comparator {
Some(c) => c,
None => {
if !list1.back.is_null() {
unsafe { (*list1.back).next = list2.front };
unsafe { (*list2.front).prev = list1.back };
} else {
list1.front = list2.front;
}
list1.back = list2.back;
list1.count += list2.count;
list2.front = ptr::null_mut();
list2.back = ptr::null_mut();
list2.count = 0;
return;
}
};
let mut cur2 = list2.front;
let mut insert_before = list1.front;
while !cur2.is_null() {
let next2 = unsafe { (*cur2).next };
while !insert_before.is_null() {
if unsafe {
comparator(
(*cur2).data as *const c_void,
(*insert_before).data as *const c_void,
) <= 0
} {
break;
}
insert_before = unsafe { (*insert_before).next };
}
if insert_before.is_null() {
if list1.back.is_null() {
list1.front = cur2;
list1.back = cur2;
unsafe {
(*cur2).prev = ptr::null_mut();
(*cur2).next = ptr::null_mut();
}
} else {
unsafe {
(*cur2).prev = list1.back;
(*cur2).next = ptr::null_mut();
(*list1.back).next = cur2;
}
list1.back = cur2;
}
} else {
unsafe {
(*cur2).prev = (*insert_before).prev;
(*cur2).next = insert_before;
if !(*insert_before).prev.is_null() {
(*(*insert_before).prev).next = cur2;
} else {
list1.front = cur2;
}
(*insert_before).prev = cur2;
}
}
list1.count += 1;
cur2 = next2;
}
list2.front = ptr::null_mut();
list2.back = ptr::null_mut();
list2.count = 0;
}
#[cfg(test)]
mod tests {
use super::*;
unsafe extern "C" fn int_compare(a: *const c_void, b: *const c_void) -> c_int {
let ai = *(a as *const i32);
let bi = *(b as *const i32);
ai.cmp(&bi) as c_int
}
#[test]
fn test_list_create_delete() {
unsafe {
let list = list_create(None, None);
assert!(!list.is_null());
list_delete(list);
}
}
#[test]
fn test_list_push_pop() {
unsafe {
let list = list_create(None, None);
let v1 = &mut 1 as *mut c_int as *mut c_void;
let v2 = &mut 2 as *mut c_int as *mut c_void;
list_push_back(list, v1);
list_push_back(list, v2);
assert_eq!(list_size(list), 2);
assert_eq!(*(list_front(list) as *const i32), 1);
assert_eq!(*(list_back(list) as *const i32), 2);
list_pop_back(list);
assert_eq!(list_size(list), 1);
assert_eq!(*(list_back(list) as *const i32), 1);
list_pop_front(list);
assert_eq!(list_size(list), 0);
assert_eq!(list_empty(list), 1);
list_delete(list);
}
}
#[test]
fn test_list_push_front() {
unsafe {
let list = list_create(None, None);
let v1 = &mut 1 as *mut c_int as *mut c_void;
let v2 = &mut 2 as *mut c_int as *mut c_void;
list_push_front(list, v1);
list_push_front(list, v2);
assert_eq!(*(list_front(list) as *const i32), 2);
assert_eq!(*(list_back(list) as *const i32), 1);
list_delete(list);
}
}
#[test]
fn test_list_insert_sorted() {
unsafe {
let list = list_create(None, Some(int_compare));
let v2 = &mut 2 as *mut c_int as *mut c_void;
let v1 = &mut 1 as *mut c_int as *mut c_void;
let v3 = &mut 3 as *mut c_int as *mut c_void;
list_insert(list, v2);
list_insert(list, v1);
list_insert(list, v3);
assert_eq!(*(list_front(list) as *const i32), 1);
assert_eq!(*(list_back(list) as *const i32), 3);
assert_eq!(list_size(list), 3);
list_delete(list);
}
}
#[test]
fn test_list_remove_first() {
unsafe {
let list = list_create(None, Some(int_compare));
let v1 = &mut 1 as *mut c_int as *mut c_void;
let v2 = &mut 2 as *mut c_int as *mut c_void;
list_push_back(list, v1);
list_push_back(list, v2);
let one: i32 = 1;
let result = list_remove_first(list, &one as *const i32 as *const c_void);
assert_eq!(result, 0);
assert_eq!(list_size(list), 1);
assert_eq!(*(list_front(list) as *const i32), 2);
list_delete(list);
}
}
#[test]
fn test_list_clear() {
unsafe {
let list = list_create(None, None);
list_push_back(list, &mut 1 as *mut c_int as *mut c_void);
list_push_back(list, &mut 2 as *mut c_int as *mut c_void);
assert_eq!(list_size(list), 2);
list_clear(list);
assert_eq!(list_empty(list), 1);
assert_eq!(list_size(list), 0);
list_delete(list);
}
}
#[test]
fn test_list_reverse() {
unsafe {
let list = list_create(None, None);
let v1 = &mut 1 as *mut c_int as *mut c_void;
let v2 = &mut 2 as *mut c_int as *mut c_void;
let v3 = &mut 3 as *mut c_int as *mut c_void;
list_push_back(list, v1);
list_push_back(list, v2);
list_push_back(list, v3);
list_reverse(list);
assert_eq!(*(list_front(list) as *const i32), 3);
assert_eq!(*(list_back(list) as *const i32), 1);
list_delete(list);
}
}
#[test]
fn test_list_null_handling() {
unsafe {
assert_eq!(list_empty(ptr::null_mut()), 1);
assert_eq!(list_size(ptr::null_mut()), -1);
assert!(list_front(ptr::null_mut()).is_null());
assert!(list_back(ptr::null_mut()).is_null());
list_delete(ptr::null_mut()); list_clear(ptr::null_mut()); list_pop_front(ptr::null_mut()); list_pop_back(ptr::null_mut()); }
}
}