Skip to main content

libxml_rs/xml/list/
mod.rs

1//! Linked list — public xmlList API (§85 Phase 1).
2//!
3//! Implements libxml2's linked list.
4//!
5//! # UPSTREAM-PARITY
6//!
7//! The linked list supports:
8//! - Create/delete with custom deallocator
9//! - Push front/back, pop front/back
10//! - Insert/append at arbitrary positions
11//! - Search with custom comparator
12//! - Walk with callback
13//! - Remove first/last/all matching entries
14//! - Clear
15//! - Empty/front/back/size queries
16//! - Sort, reverse, reverse splice, merge
17//!
18//! # Phase 1 status
19//!
20//! Complete — all list operations are implemented.
21
22use core::ffi::c_void;
23use core::ptr;
24use std::os::raw::c_int;
25
26use crate::abi::allocator;
27
28// ═══════════════════════════════════════════════════════════════════════════════
29// Types
30// ═══════════════════════════════════════════════════════════════════════════════
31
32/// Deallocator function for list data.
33pub type xmlListDeallocator = unsafe extern "C" fn(*mut c_void);
34
35/// Data comparator function. Returns 0 if equal, non-zero if different.
36pub type xmlListDataCompare = unsafe extern "C" fn(*const c_void, *const c_void) -> c_int;
37
38/// Walker function for xmlListWalk.
39pub type xmlListWalker = unsafe extern "C" fn(*mut c_void, *mut c_void) -> c_int;
40
41/// A linked list node.
42struct ListNode {
43    data: *mut c_void,
44    prev: *mut ListNode,
45    next: *mut ListNode,
46}
47
48/// The linked list struct.
49pub struct List {
50    front: *mut ListNode,
51    back: *mut ListNode,
52    count: usize,
53    deallocator: Option<xmlListDeallocator>,
54    comparator: Option<xmlListDataCompare>,
55}
56
57// ═══════════════════════════════════════════════════════════════════════════════
58// Public API
59// ═══════════════════════════════════════════════════════════════════════════════
60
61/// Create a new linked list.
62///
63/// # UPSTREAM-PARITY
64///
65/// ```c
66/// xmlListPtr xmlListCreate(xmlListDeallocator deallocator,
67///                          xmlListDataCompare comparator);
68/// ```
69///
70/// Creates a linked list with the given deallocator and comparator.
71/// Both may be NULL.
72pub fn list_create(
73    deallocator: Option<xmlListDeallocator>,
74    comparator: Option<xmlListDataCompare>,
75) -> *mut List {
76    let list = Box::new(List {
77        front: ptr::null_mut(),
78        back: ptr::null_mut(),
79        count: 0,
80        deallocator,
81        comparator,
82    });
83
84    Box::into_raw(list)
85}
86
87/// Delete a linked list and all its nodes.
88///
89/// # UPSTREAM-PARITY
90///
91/// ```c
92/// void xmlListDelete(xmlListPtr l);
93/// ```
94///
95/// # SAFETY
96///
97/// - `l` must be a valid pointer to a List, or NULL.
98pub unsafe fn list_delete(l: *mut List) {
99    if l.is_null() {
100        return;
101    }
102
103    let list = unsafe { &mut *l };
104    let mut cur = list.front;
105
106    while !cur.is_null() {
107        let next = unsafe { (*cur).next };
108        if let Some(dealloc) = list.deallocator {
109            unsafe { dealloc((*cur).data) };
110        }
111        unsafe { allocator::xmlFree(cur as *mut c_void) };
112        cur = next;
113    }
114
115    drop(Box::from_raw(l));
116}
117
118/// Search the list for data matching the given key.
119///
120/// # UPSTREAM-PARITY
121///
122/// ```c
123/// void *xmlListSearch(xmlListPtr l, void *data);
124/// ```
125///
126/// Uses the list's comparator function. Returns the matching data, or NULL.
127///
128/// # SAFETY
129///
130/// - `l` must be a valid pointer to a List, or NULL.
131pub unsafe fn list_search(l: *mut List, data: *const c_void) -> *mut c_void {
132    if l.is_null() {
133        return ptr::null_mut();
134    }
135
136    let list = unsafe { &*l };
137    let comparator = match list.comparator {
138        Some(c) => c,
139        None => return ptr::null_mut(),
140    };
141
142    let mut cur = list.front;
143    while !cur.is_null() {
144        let node = unsafe { &*cur };
145        if unsafe { comparator(node.data as *const c_void, data) == 0 } {
146            return node.data;
147        }
148        cur = node.next;
149    }
150
151    ptr::null_mut()
152}
153
154/// Walk the list, calling the walker function for each element.
155///
156/// # UPSTREAM-PARITY
157///
158/// ```c
159/// void xmlListWalk(xmlListPtr l, xmlListWalker walker, void *data);
160/// ```
161///
162/// # SAFETY
163///
164/// - `l` must be a valid pointer to a List, or NULL.
165/// - `walker` must be a valid function pointer or NULL.
166pub unsafe fn list_walk(l: *mut List, walker: Option<xmlListWalker>, data: *mut c_void) {
167    if l.is_null() || walker.is_none() {
168        return;
169    }
170    let walker = walker.unwrap();
171
172    let list = unsafe { &*l };
173    let mut cur = list.front;
174    while !cur.is_null() {
175        let node = unsafe { &*cur };
176        if unsafe { walker(node.data, data) != 0 } {
177            break;
178        }
179        cur = node.next;
180    }
181}
182
183/// Push data to the back of the list.
184///
185/// # UPSTREAM-PARITY
186///
187/// ```c
188/// int xmlListPushBack(xmlListPtr l, void *data);
189/// ```
190///
191/// Returns 0 on success, -1 on failure.
192///
193/// # SAFETY
194///
195/// - `l` must be a valid pointer to a List, or NULL.
196pub unsafe fn list_push_back(l: *mut List, data: *mut c_void) -> c_int {
197    if l.is_null() {
198        return -1;
199    }
200
201    let list = unsafe { &mut *l };
202
203    let node = allocator::xmlMallocZero(size_of::<ListNode>() as usize) as *mut ListNode;
204    if node.is_null() {
205        return -1;
206    }
207
208    unsafe {
209        (*node).data = data;
210        (*node).prev = list.back;
211        (*node).next = ptr::null_mut();
212    }
213
214    if list.back.is_null() {
215        list.front = node;
216        list.back = node;
217    } else {
218        unsafe { (*list.back).next = node };
219        list.back = node;
220    }
221
222    list.count += 1;
223    0
224}
225
226/// Push data to the front of the list.
227///
228/// # UPSTREAM-PARITY
229///
230/// ```c
231/// int xmlListPushFront(xmlListPtr l, void *data);
232/// ```
233///
234/// Returns 0 on success, -1 on failure.
235///
236/// # SAFETY
237///
238/// - `l` must be a valid pointer to a List, or NULL.
239pub unsafe fn list_push_front(l: *mut List, data: *mut c_void) -> c_int {
240    if l.is_null() {
241        return -1;
242    }
243
244    let list = unsafe { &mut *l };
245
246    let node = allocator::xmlMallocZero(size_of::<ListNode>() as usize) as *mut ListNode;
247    if node.is_null() {
248        return -1;
249    }
250
251    unsafe {
252        (*node).data = data;
253        (*node).prev = ptr::null_mut();
254        (*node).next = list.front;
255    }
256
257    if list.front.is_null() {
258        list.front = node;
259        list.back = node;
260    } else {
261        unsafe { (*list.front).prev = node };
262        list.front = node;
263    }
264
265    list.count += 1;
266    0
267}
268
269/// Pop data from the back of the list.
270///
271/// # UPSTREAM-PARITY
272///
273/// ```c
274/// void xmlListPopBack(xmlListPtr l);
275/// ```
276///
277/// # SAFETY
278///
279/// - `l` must be a valid pointer to a List, or NULL.
280pub unsafe fn list_pop_back(l: *mut List) {
281    if l.is_null() {
282        return;
283    }
284
285    let list = unsafe { &mut *l };
286    if list.back.is_null() {
287        return;
288    }
289
290    let node = list.back;
291    let prev = unsafe { (*node).prev };
292
293    if let Some(dealloc) = list.deallocator {
294        unsafe { dealloc((*node).data) };
295    }
296    unsafe { allocator::xmlFree(node as *mut c_void) };
297
298    list.back = prev;
299    if prev.is_null() {
300        list.front = ptr::null_mut();
301    } else {
302        unsafe { (*prev).next = ptr::null_mut() };
303    }
304
305    list.count = list.count.saturating_sub(1);
306}
307
308/// Pop data from the front of the list.
309///
310/// # UPSTREAM-PARITY
311///
312/// ```c
313/// void xmlListPopFront(xmlListPtr l);
314/// ```
315///
316/// # SAFETY
317///
318/// - `l` must be a valid pointer to a List, or NULL.
319pub unsafe fn list_pop_front(l: *mut List) {
320    if l.is_null() {
321        return;
322    }
323
324    let list = unsafe { &mut *l };
325    if list.front.is_null() {
326        return;
327    }
328
329    let node = list.front;
330    let next = unsafe { (*node).next };
331
332    if let Some(dealloc) = list.deallocator {
333        unsafe { dealloc((*node).data) };
334    }
335    unsafe { allocator::xmlFree(node as *mut c_void) };
336
337    list.front = next;
338    if next.is_null() {
339        list.back = ptr::null_mut();
340    } else {
341        unsafe { (*next).prev = ptr::null_mut() };
342    }
343
344    list.count = list.count.saturating_sub(1);
345}
346
347/// Insert data into the sorted position.
348///
349/// # UPSTREAM-PARITY
350///
351/// ```c
352/// int xmlListInsert(xmlListPtr l, void *data);
353/// ```
354///
355/// Inserts data in sorted order using the list's comparator.
356/// Returns 0 on success, -1 on failure.
357///
358/// # SAFETY
359///
360/// - `l` must be a valid pointer to a List, or NULL.
361pub unsafe fn list_insert(l: *mut List, data: *mut c_void) -> c_int {
362    if l.is_null() {
363        return -1;
364    }
365
366    let list = unsafe { &mut *l };
367
368    // If no comparator, just push back
369    let comparator = match list.comparator {
370        Some(c) => c,
371        None => return list_push_back(l, data),
372    };
373
374    // Find insertion point
375    let mut cur = list.front;
376    while !cur.is_null() {
377        let node = unsafe { &*cur };
378        if unsafe { comparator(data as *const c_void, node.data as *const c_void) <= 0 } {
379            // Insert before cur
380            let new_node =
381                allocator::xmlMallocZero(size_of::<ListNode>() as usize) as *mut ListNode;
382            if new_node.is_null() {
383                return -1;
384            }
385            unsafe {
386                (*new_node).data = data;
387                (*new_node).prev = node.prev;
388                (*new_node).next = cur;
389                if !node.prev.is_null() {
390                    (*node.prev).next = new_node;
391                } else {
392                    list.front = new_node;
393                }
394                (*cur).prev = new_node;
395            }
396            list.count += 1;
397            return 0;
398        }
399        cur = node.next;
400    }
401
402    // Append at end
403    list_push_back(l, data)
404}
405
406/// Append data to the end of the list (alias for push_back).
407///
408/// # UPSTREAM-PARITY
409///
410/// ```c
411/// int xmlListAppend(xmlListPtr l, void *data);
412/// ```
413pub unsafe fn list_append(l: *mut List, data: *mut c_void) -> c_int {
414    list_push_back(l, data)
415}
416
417/// Remove the first matching element.
418///
419/// # UPSTREAM-PARITY
420///
421/// ```c
422/// int xmlListRemoveFirst(xmlListPtr l, void *data);
423/// ```
424///
425/// Returns 0 on success, -1 if not found.
426///
427/// # SAFETY
428///
429/// - `l` must be a valid pointer to a List, or NULL.
430pub unsafe fn list_remove_first(l: *mut List, data: *const c_void) -> c_int {
431    if l.is_null() {
432        return -1;
433    }
434
435    let list = unsafe { &mut *l };
436    let comparator = match list.comparator {
437        Some(c) => c,
438        None => return -1,
439    };
440
441    let mut cur = list.front;
442    while !cur.is_null() {
443        let node = unsafe { &*cur };
444        let next = node.next;
445        if unsafe { comparator(node.data as *const c_void, data) == 0 } {
446            // Remove this node
447            if !node.prev.is_null() {
448                unsafe { (*node.prev).next = node.next };
449            } else {
450                list.front = node.next;
451            }
452            if !node.next.is_null() {
453                unsafe { (*node.next).prev = node.prev };
454            } else {
455                list.back = node.prev;
456            }
457
458            if let Some(dealloc) = list.deallocator {
459                unsafe { dealloc(node.data) };
460            }
461            unsafe { allocator::xmlFree(cur as *mut c_void) };
462            list.count = list.count.saturating_sub(1);
463            return 0;
464        }
465        cur = next;
466    }
467
468    -1
469}
470
471/// Remove the last matching element.
472///
473/// # UPSTREAM-PARITY
474///
475/// ```c
476/// int xmlListRemoveLast(xmlListPtr l, void *data);
477/// ```
478///
479/// Returns 0 on success, -1 if not found.
480///
481/// # SAFETY
482///
483/// - `l` must be a valid pointer to a List, or NULL.
484pub unsafe fn list_remove_last(l: *mut List, data: *const c_void) -> c_int {
485    if l.is_null() {
486        return -1;
487    }
488
489    let list = unsafe { &mut *l };
490    let comparator = match list.comparator {
491        Some(c) => c,
492        None => return -1,
493    };
494
495    let mut cur = list.back;
496    while !cur.is_null() {
497        let node = unsafe { &*cur };
498        let prev = node.prev;
499        if unsafe { comparator(node.data as *const c_void, data) == 0 } {
500            // Remove this node
501            if !node.prev.is_null() {
502                unsafe { (*node.prev).next = node.next };
503            } else {
504                list.front = node.next;
505            }
506            if !node.next.is_null() {
507                unsafe { (*node.next).prev = node.prev };
508            } else {
509                list.back = node.prev;
510            }
511
512            if let Some(dealloc) = list.deallocator {
513                unsafe { dealloc(node.data) };
514            }
515            unsafe { allocator::xmlFree(cur as *mut c_void) };
516            list.count = list.count.saturating_sub(1);
517            return 0;
518        }
519        cur = prev;
520    }
521
522    -1
523}
524
525/// Remove all matching elements.
526///
527/// # UPSTREAM-PARITY
528///
529/// ```c
530/// int xmlListRemoveAll(xmlListPtr l, void *data);
531/// ```
532///
533/// Returns the number of elements removed.
534///
535/// # SAFETY
536///
537/// - `l` must be a valid pointer to a List, or NULL.
538pub unsafe fn list_remove_all(l: *mut List, data: *const c_void) -> c_int {
539    if l.is_null() {
540        return 0;
541    }
542
543    let list = unsafe { &mut *l };
544    let comparator = match list.comparator {
545        Some(c) => c,
546        None => return 0,
547    };
548
549    let mut removed = 0;
550    let mut cur = list.front;
551
552    while !cur.is_null() {
553        let node = unsafe { &*cur };
554        let next = node.next;
555
556        if unsafe { comparator(node.data as *const c_void, data) == 0 } {
557            // Remove this node
558            if !node.prev.is_null() {
559                unsafe { (*node.prev).next = node.next };
560            } else {
561                list.front = node.next;
562            }
563            if !node.next.is_null() {
564                unsafe { (*node.next).prev = node.prev };
565            } else {
566                list.back = node.prev;
567            }
568
569            if let Some(dealloc) = list.deallocator {
570                unsafe { dealloc(node.data) };
571            }
572            unsafe { allocator::xmlFree(cur as *mut c_void) };
573            list.count = list.count.saturating_sub(1);
574            removed += 1;
575        }
576
577        cur = next;
578    }
579
580    removed
581}
582
583/// Clear the list (remove all elements).
584///
585/// # UPSTREAM-PARITY
586///
587/// ```c
588/// void xmlListClear(xmlListPtr l);
589/// ```
590///
591/// # SAFETY
592///
593/// - `l` must be a valid pointer to a List, or NULL.
594pub unsafe fn list_clear(l: *mut List) {
595    if l.is_null() {
596        return;
597    }
598
599    let list = unsafe { &mut *l };
600    let mut cur = list.front;
601
602    while !cur.is_null() {
603        let next = unsafe { (*cur).next };
604        if let Some(dealloc) = list.deallocator {
605            unsafe { dealloc((*cur).data) };
606        }
607        unsafe { allocator::xmlFree(cur as *mut c_void) };
608        cur = next;
609    }
610
611    list.front = ptr::null_mut();
612    list.back = ptr::null_mut();
613    list.count = 0;
614}
615
616/// Check if the list is empty.
617///
618/// # UPSTREAM-PARITY
619///
620/// ```c
621/// int xmlListEmpty(xmlListPtr l);
622/// ```
623///
624/// Returns 1 if empty, 0 if not empty.
625pub fn list_empty(l: *mut List) -> c_int {
626    if l.is_null() {
627        return 1;
628    }
629    let list = unsafe { &*l };
630    if list.front.is_null() {
631        1
632    } else {
633        0
634    }
635}
636
637/// Get the data at the front of the list.
638///
639/// # UPSTREAM-PARITY
640///
641/// ```c
642/// void *xmlListFront(xmlListPtr l);
643/// ```
644///
645/// Returns the data at the front, or NULL if the list is empty.
646pub fn list_front(l: *mut List) -> *mut c_void {
647    if l.is_null() {
648        return ptr::null_mut();
649    }
650    let list = unsafe { &*l };
651    if list.front.is_null() {
652        ptr::null_mut()
653    } else {
654        unsafe { (*list.front).data }
655    }
656}
657
658/// Get the data at the back of the list.
659///
660/// # UPSTREAM-PARITY
661///
662/// ```c
663/// void *xmlListBack(xmlListPtr l);
664/// ```
665///
666/// Returns the data at the back, or NULL if the list is empty.
667pub fn list_back(l: *mut List) -> *mut c_void {
668    if l.is_null() {
669        return ptr::null_mut();
670    }
671    let list = unsafe { &*l };
672    if list.back.is_null() {
673        ptr::null_mut()
674    } else {
675        unsafe { (*list.back).data }
676    }
677}
678
679/// Get the number of elements in the list.
680///
681/// # UPSTREAM-PARITY
682///
683/// ```c
684/// int xmlListSize(xmlListPtr l);
685/// ```
686///
687/// Returns the number of elements, or -1 if the list is NULL.
688pub fn list_size(l: *mut List) -> c_int {
689    if l.is_null() {
690        return -1;
691    }
692    let list = unsafe { &*l };
693    list.count as c_int
694}
695
696/// Sort the list in-place using the comparator.
697///
698/// # UPSTREAM-PARITY
699///
700/// ```c
701/// void xmlListSort(xmlListPtr l);
702/// ```
703///
704/// # SAFETY
705///
706/// - `l` must be a valid pointer to a List, or NULL.
707pub unsafe fn list_sort(l: *mut List) {
708    if l.is_null() {
709        return;
710    }
711
712    let list = unsafe { &mut *l };
713    if list.count <= 1 {
714        return;
715    }
716
717    let comparator = match list.comparator {
718        Some(c) => c,
719        None => return,
720    };
721
722    // Convert to Vec, sort, rebuild
723    let mut nodes: Vec<*mut ListNode> = Vec::with_capacity(list.count);
724    let mut cur = list.front;
725    while !cur.is_null() {
726        nodes.push(cur);
727        cur = unsafe { (*cur).next };
728    }
729
730    // Bubble sort (simple, matches upstream's simple approach)
731    for i in 0..nodes.len() {
732        for j in 0..nodes.len() - 1 - i {
733            let a = unsafe { &*nodes[j] };
734            let b = unsafe { &*nodes[j + 1] };
735            if unsafe { comparator(a.data as *const c_void, b.data as *const c_void) > 0 } {
736                nodes.swap(j, j + 1);
737            }
738        }
739    }
740
741    // Rebuild links
742    list.front = nodes[0];
743    list.back = nodes[nodes.len() - 1];
744
745    for i in 0..nodes.len() {
746        unsafe {
747            (*nodes[i]).prev = if i > 0 { nodes[i - 1] } else { ptr::null_mut() };
748            (*nodes[i]).next = if i + 1 < nodes.len() {
749                nodes[i + 1]
750            } else {
751                ptr::null_mut()
752            };
753        }
754    }
755}
756
757/// Reverse the list in-place.
758///
759/// # UPSTREAM-PARITY
760///
761/// ```c
762/// void xmlListReverse(xmlListPtr l);
763/// ```
764///
765/// # SAFETY
766///
767/// - `l` must be a valid pointer to a List, or NULL.
768pub unsafe fn list_reverse(l: *mut List) {
769    if l.is_null() {
770        return;
771    }
772
773    let list = unsafe { &mut *l };
774
775    let mut cur = list.front;
776    list.front = list.back;
777    list.back = cur;
778
779    while !cur.is_null() {
780        let next = unsafe { (*cur).next };
781        unsafe {
782            (*cur).next = (*cur).prev;
783            (*cur).prev = next;
784        }
785        cur = next;
786    }
787}
788
789/// Reverse splice: move all elements from `l2` to the front of `l1` in reverse order.
790///
791/// # UPSTREAM-PARITY
792///
793/// ```c
794/// void xmlListReverseSplice(xmlListPtr l1, xmlListPtr l2);
795/// ```
796///
797/// # SAFETY
798///
799/// - `l1`, `l2` must be valid pointers to Lists, or NULL.
800pub unsafe fn list_reverse_splice(l1: *mut List, l2: *mut List) {
801    if l1.is_null() || l2.is_null() {
802        return;
803    }
804
805    let list1 = unsafe { &mut *l1 };
806    let list2 = unsafe { &mut *l2 };
807
808    if list2.front.is_null() {
809        return;
810    }
811
812    // Reverse l2 first
813    list_reverse(l2);
814
815    // Move all nodes from l2 to front of l1
816    unsafe {
817        (*list2.back).next = list1.front;
818        if !list1.front.is_null() {
819            (*list1.front).prev = list2.back;
820        } else {
821            list1.back = list2.back;
822        }
823        list1.front = list2.front;
824    }
825
826    list1.count += list2.count;
827
828    // Clear l2
829    list2.front = ptr::null_mut();
830    list2.back = ptr::null_mut();
831    list2.count = 0;
832}
833
834/// Merge two sorted lists into one.
835///
836/// # UPSTREAM-PARITY
837///
838/// ```c
839/// void xmlListMerge(xmlListPtr l1, xmlListPtr l2);
840/// ```
841///
842/// Merges `l2` into `l1` in sorted order. `l2` becomes empty.
843///
844/// # SAFETY
845///
846/// - `l1`, `l2` must be valid pointers to Lists, or NULL.
847pub unsafe fn list_merge(l1: *mut List, l2: *mut List) {
848    if l1.is_null() || l2.is_null() {
849        return;
850    }
851
852    let list1 = unsafe { &mut *l1 };
853    let list2 = unsafe { &mut *l2 };
854
855    if list2.front.is_null() {
856        return;
857    }
858
859    let comparator = match list1.comparator {
860        Some(c) => c,
861        None => {
862            // No comparator — just append all of l2 to l1
863            if !list1.back.is_null() {
864                unsafe { (*list1.back).next = list2.front };
865                unsafe { (*list2.front).prev = list1.back };
866            } else {
867                list1.front = list2.front;
868            }
869            list1.back = list2.back;
870            list1.count += list2.count;
871            list2.front = ptr::null_mut();
872            list2.back = ptr::null_mut();
873            list2.count = 0;
874            return;
875        }
876    };
877
878    // Merge sorted lists
879    let mut cur2 = list2.front;
880    let mut insert_before = list1.front;
881
882    while !cur2.is_null() {
883        let next2 = unsafe { (*cur2).next };
884
885        // Find insertion point
886        while !insert_before.is_null() {
887            if unsafe {
888                comparator(
889                    (*cur2).data as *const c_void,
890                    (*insert_before).data as *const c_void,
891                ) <= 0
892            } {
893                break;
894            }
895            insert_before = unsafe { (*insert_before).next };
896        }
897
898        // Insert cur2 before insert_before
899        if insert_before.is_null() {
900            // Append at end
901            if list1.back.is_null() {
902                list1.front = cur2;
903                list1.back = cur2;
904                unsafe {
905                    (*cur2).prev = ptr::null_mut();
906                    (*cur2).next = ptr::null_mut();
907                }
908            } else {
909                unsafe {
910                    (*cur2).prev = list1.back;
911                    (*cur2).next = ptr::null_mut();
912                    (*list1.back).next = cur2;
913                }
914                list1.back = cur2;
915            }
916        } else {
917            unsafe {
918                (*cur2).prev = (*insert_before).prev;
919                (*cur2).next = insert_before;
920                if !(*insert_before).prev.is_null() {
921                    (*(*insert_before).prev).next = cur2;
922                } else {
923                    list1.front = cur2;
924                }
925                (*insert_before).prev = cur2;
926            }
927        }
928
929        list1.count += 1;
930        cur2 = next2;
931    }
932
933    // Clear l2
934    list2.front = ptr::null_mut();
935    list2.back = ptr::null_mut();
936    list2.count = 0;
937}
938
939// ═══════════════════════════════════════════════════════════════════════════════
940// Tests
941// ═══════════════════════════════════════════════════════════════════════════════
942
943#[cfg(test)]
944mod tests {
945    use super::*;
946
947    unsafe extern "C" fn int_compare(a: *const c_void, b: *const c_void) -> c_int {
948        let ai = *(a as *const i32);
949        let bi = *(b as *const i32);
950        ai.cmp(&bi) as c_int
951    }
952
953    #[test]
954    fn test_list_create_delete() {
955        unsafe {
956            let list = list_create(None, None);
957            assert!(!list.is_null());
958            list_delete(list);
959        }
960    }
961
962    #[test]
963    fn test_list_push_pop() {
964        unsafe {
965            let list = list_create(None, None);
966            let v1 = &mut 1 as *mut c_int as *mut c_void;
967            let v2 = &mut 2 as *mut c_int as *mut c_void;
968
969            list_push_back(list, v1);
970            list_push_back(list, v2);
971            assert_eq!(list_size(list), 2);
972
973            assert_eq!(*(list_front(list) as *const i32), 1);
974            assert_eq!(*(list_back(list) as *const i32), 2);
975
976            list_pop_back(list);
977            assert_eq!(list_size(list), 1);
978            assert_eq!(*(list_back(list) as *const i32), 1);
979
980            list_pop_front(list);
981            assert_eq!(list_size(list), 0);
982            assert_eq!(list_empty(list), 1);
983
984            list_delete(list);
985        }
986    }
987
988    #[test]
989    fn test_list_push_front() {
990        unsafe {
991            let list = list_create(None, None);
992            let v1 = &mut 1 as *mut c_int as *mut c_void;
993            let v2 = &mut 2 as *mut c_int as *mut c_void;
994
995            list_push_front(list, v1);
996            list_push_front(list, v2);
997            assert_eq!(*(list_front(list) as *const i32), 2);
998            assert_eq!(*(list_back(list) as *const i32), 1);
999
1000            list_delete(list);
1001        }
1002    }
1003
1004    #[test]
1005    fn test_list_insert_sorted() {
1006        unsafe {
1007            let list = list_create(None, Some(int_compare));
1008            let v2 = &mut 2 as *mut c_int as *mut c_void;
1009            let v1 = &mut 1 as *mut c_int as *mut c_void;
1010            let v3 = &mut 3 as *mut c_int as *mut c_void;
1011
1012            list_insert(list, v2);
1013            list_insert(list, v1);
1014            list_insert(list, v3);
1015
1016            // Should be 1, 2, 3
1017            assert_eq!(*(list_front(list) as *const i32), 1);
1018            assert_eq!(*(list_back(list) as *const i32), 3);
1019            assert_eq!(list_size(list), 3);
1020
1021            list_delete(list);
1022        }
1023    }
1024
1025    #[test]
1026    fn test_list_remove_first() {
1027        unsafe {
1028            let list = list_create(None, Some(int_compare));
1029            let v1 = &mut 1 as *mut c_int as *mut c_void;
1030            let v2 = &mut 2 as *mut c_int as *mut c_void;
1031
1032            list_push_back(list, v1);
1033            list_push_back(list, v2);
1034
1035            let one: i32 = 1;
1036            let result = list_remove_first(list, &one as *const i32 as *const c_void);
1037            assert_eq!(result, 0);
1038            assert_eq!(list_size(list), 1);
1039            assert_eq!(*(list_front(list) as *const i32), 2);
1040
1041            list_delete(list);
1042        }
1043    }
1044
1045    #[test]
1046    fn test_list_clear() {
1047        unsafe {
1048            let list = list_create(None, None);
1049            list_push_back(list, &mut 1 as *mut c_int as *mut c_void);
1050            list_push_back(list, &mut 2 as *mut c_int as *mut c_void);
1051            assert_eq!(list_size(list), 2);
1052
1053            list_clear(list);
1054            assert_eq!(list_empty(list), 1);
1055            assert_eq!(list_size(list), 0);
1056
1057            list_delete(list);
1058        }
1059    }
1060
1061    #[test]
1062    fn test_list_reverse() {
1063        unsafe {
1064            let list = list_create(None, None);
1065            let v1 = &mut 1 as *mut c_int as *mut c_void;
1066            let v2 = &mut 2 as *mut c_int as *mut c_void;
1067            let v3 = &mut 3 as *mut c_int as *mut c_void;
1068
1069            list_push_back(list, v1);
1070            list_push_back(list, v2);
1071            list_push_back(list, v3);
1072
1073            list_reverse(list);
1074
1075            assert_eq!(*(list_front(list) as *const i32), 3);
1076            assert_eq!(*(list_back(list) as *const i32), 1);
1077
1078            list_delete(list);
1079        }
1080    }
1081
1082    #[test]
1083    fn test_list_null_handling() {
1084        unsafe {
1085            assert_eq!(list_empty(ptr::null_mut()), 1);
1086            assert_eq!(list_size(ptr::null_mut()), -1);
1087            assert!(list_front(ptr::null_mut()).is_null());
1088            assert!(list_back(ptr::null_mut()).is_null());
1089            list_delete(ptr::null_mut()); // Should not crash
1090            list_clear(ptr::null_mut()); // Should not crash
1091            list_pop_front(ptr::null_mut()); // Should not crash
1092            list_pop_back(ptr::null_mut()); // Should not crash
1093        }
1094    }
1095}