libxml_rs/xslt/sorting/
mod.rs1use crate::abi::allocator::xmlFree;
27use crate::abi::exports_xml2::{
28 xmlStrcmp, xmlXPathCastStringToNumber, xmlXPathCastToString, xmlXPathCmpNodes,
29 xmlXPathEvalExpression, xmlXPathFreeObject,
30};
31use crate::abi::structs::*;
32use crate::abi::types::xmlElementType::XML_ATTRIBUTE_NODE;
33use crate::abi::types::*;
34use crate::xml::tree::node_get_content;
35use std::os::raw::{c_char, c_int};
36use std::ptr;
37
38pub const XSLT_SORT_TEXT: c_int = 0;
40pub const XSLT_SORT_NUMBER: c_int = 1;
41
42pub const XSLT_SORT_ASCENDING: c_int = 0;
44pub const XSLT_SORT_DESCENDING: c_int = 1;
45
46pub const XSLT_SORT_CASE_UPPER_FIRST: c_int = 0;
48pub const XSLT_SORT_CASE_LOWER_FIRST: c_int = 1;
49
50pub unsafe fn xsltCompileSort(style: *mut _xsltStylesheet, inst: *mut _xmlNode) -> *mut _xsltSort {
57 if style.is_null() || inst.is_null() {
58 return ptr::null_mut();
59 }
60 let s = libc::calloc(1, core::mem::size_of::<_xsltSort>()) as *mut _xsltSort;
61 if s.is_null() {
62 return ptr::null_mut();
63 }
64 (*s).inst = inst;
65 (*s).style = style;
66 (*s).next = ptr::null_mut();
67 (*s).isText = 1; (*s).hasConst = 0;
69
70 let mut prop = (*inst).properties;
72 while !prop.is_null() {
73 let name = (*prop).name;
74 if !name.is_null() {
75 let value = node_get_content((*prop).children);
76 if !value.is_null() {
77 let name_str = crate::abi::versioning::c_str_to_bytes(name as *const c_char);
78 match name_str {
79 Some(b"select") => {
80 if (*s).select.is_null() {
81 (*s).select = value;
82 } else {
83 libc::free(value as *mut libc::c_void);
84 }
85 }
86 Some(b"lang") => {
87 (*s).lang = value;
88 }
89 Some(b"data-type") => {
90 (*s).dataType = value;
91 let v = crate::abi::versioning::c_str_to_bytes(value as *const c_char);
92 if v == Some(b"number") {
93 (*s).isText = 0;
94 }
95 }
96 Some(b"order") => {
97 (*s).order = value;
98 }
99 Some(b"case-order") => {
100 (*s).caseOrder = value;
101 }
102 _ => {
103 libc::free(value as *mut libc::c_void);
104 }
105 }
106 }
107 }
108 prop = (*prop).next;
109 }
110 s
111}
112
113pub unsafe fn xsltFreeSort(sort: *mut _xsltSort) {
119 if sort.is_null() {
120 return;
121 }
122 if !(*sort).select.is_null() {
125 libc::free((*sort).select as *mut libc::c_void);
126 }
127 if !(*sort).lang.is_null() {
128 libc::free((*sort).lang as *mut libc::c_void);
129 }
130 if !(*sort).dataType.is_null() {
131 libc::free((*sort).dataType as *mut libc::c_void);
132 }
133 if !(*sort).order.is_null() {
134 libc::free((*sort).order as *mut libc::c_void);
135 }
136 if !(*sort).caseOrder.is_null() {
137 libc::free((*sort).caseOrder as *mut libc::c_void);
138 }
139 (*sort).next = ptr::null_mut();
140 xmlFree(sort as *mut libc::c_void);
141}
142
143pub unsafe fn xsltFreeSortList(sorts: *mut _xsltSort) {
149 let mut cur = sorts;
150 while !cur.is_null() {
151 let next = (*cur).next;
152 xsltFreeSort(cur);
153 cur = next;
154 }
155}
156
157unsafe fn sort_string_value(node: *mut _xmlNode) -> *mut xmlChar {
164 if node.is_null() {
165 return ptr::null_mut();
166 }
167 let typ = (*node).type_;
168 if typ == XML_ATTRIBUTE_NODE as i32 {
169 let content = (*node).children;
171 if !content.is_null() {
172 return node_get_content(content);
173 }
174 return ptr::null_mut();
175 }
176 node_get_content(node)
177}
178
179unsafe fn xsltEvalSortKey(
189 ctxt: *mut _xsltTransformContext,
190 node: *mut _xmlNode,
191 sort: *mut _xsltSort,
192) -> *mut xmlChar {
193 if ctxt.is_null() || node.is_null() || sort.is_null() {
194 return ptr::null_mut();
195 }
196 if (*sort).select.is_null() {
198 return sort_string_value(node);
199 }
200 let xpath_ctxt = (*ctxt).xpathCtxt;
202 if xpath_ctxt.is_null() {
203 return sort_string_value(node);
204 }
205 let select = (*sort).select;
206 let xpath_obj = xmlXPathEvalExpression(select, xpath_ctxt);
207 if xpath_obj.is_null() {
208 return ptr::null_mut();
209 }
210 let result = xmlXPathCastToString(xpath_obj);
211 xmlXPathFreeObject(xpath_obj);
212 result
213}
214
215pub unsafe fn xsltCompareSingle(
229 ctxt: *mut _xsltTransformContext,
230 a: *mut _xmlNode,
231 b: *mut _xmlNode,
232 sort: *mut _xsltSort,
233) -> c_int {
234 if a.is_null() || b.is_null() || sort.is_null() {
235 return 0;
236 }
237 let mut result: c_int = 0;
238 let a_key = xsltEvalSortKey(ctxt, a, sort);
239 let b_key = xsltEvalSortKey(ctxt, b, sort);
240
241 let a_str: *const xmlChar = if a_key.is_null() { ptr::null() } else { a_key };
242 let b_str: *const xmlChar = if b_key.is_null() { ptr::null() } else { b_key };
243
244 if (*sort).isText != 0 {
245 result = match (a_str.is_null(), b_str.is_null()) {
247 (true, true) => 0,
248 (true, false) => -1,
249 (false, true) => 1,
250 (false, false) => {
251 let cmp = xmlStrcmp(a_str, b_str);
254 cmp
255 }
256 };
257 } else {
258 let a_num = if a_str.is_null() {
260 f64::NAN
261 } else {
262 xmlXPathCastStringToNumber(a_str)
263 };
264 let b_num = if b_str.is_null() {
265 f64::NAN
266 } else {
267 xmlXPathCastStringToNumber(b_str)
268 };
269 if a_num.is_nan() && b_num.is_nan() {
270 result = 0;
271 } else if a_num.is_nan() {
272 result = 1; } else if b_num.is_nan() {
274 result = -1;
275 } else if a_num < b_num {
276 result = -1;
277 } else if a_num > b_num {
278 result = 1;
279 } else {
280 result = 0;
281 }
282 }
283
284 let order = (*sort).order;
286 if !order.is_null() {
287 let o = crate::abi::versioning::c_str_to_bytes(order as *const c_char);
288 if o == Some(b"descending") {
289 result = -result;
290 }
291 }
292
293 if !a_key.is_null() {
294 libc::free(a_key as *mut libc::c_void);
295 }
296 if !b_key.is_null() {
297 libc::free(b_key as *mut libc::c_void);
298 }
299 result
300}
301
302pub unsafe fn xsltCompareNodes(
309 ctxt: *mut _xsltTransformContext,
310 a: *mut _xmlNode,
311 b: *mut _xmlNode,
312 sorts: *mut _xsltSort,
313) -> c_int {
314 if a.is_null() || b.is_null() || sorts.is_null() {
315 return 0;
316 }
317 let mut cur = sorts;
318 while !cur.is_null() {
319 let cmp = xsltCompareSingle(ctxt, a, b, cur);
320 if cmp != 0 {
321 return cmp;
322 }
323 cur = (*cur).next;
324 }
325 if a == b {
328 return 0;
329 }
330 xmlXPathCmpNodes(a, b)
331}
332
333pub unsafe fn xsltSortNodeSet(
344 ctxt: *mut _xsltTransformContext,
345 nodes: *mut _xmlNodeSet,
346 sorts: *mut _xsltSort,
347) {
348 if ctxt.is_null() || nodes.is_null() || sorts.is_null() {
349 return;
350 }
351 let nr = (*nodes).nodeNr;
352 if nr <= 1 {
353 return;
354 }
355 let tab = (*nodes).nodeTab;
356 if tab.is_null() {
357 return;
358 }
359
360 if nr < 32 {
364 let mut i = 1usize;
366 while i < nr as usize {
367 let key = *tab.offset(i as isize);
368 let mut j = i as isize - 1;
369 while j >= 0 {
370 let cur = *tab.offset(j);
371 if xsltCompareNodes(ctxt, cur, key, sorts) <= 0 {
372 break;
373 }
374 *tab.offset((j + 1) as isize) = cur;
375 j -= 1;
376 }
377 *tab.offset((j + 1) as isize) = key;
378 i += 1;
379 }
380 } else {
381 let mut indices: Vec<usize> = (0..nr as usize).collect();
383 quicksort_indices(ctxt, tab, &mut indices, sorts);
384 for (new_pos, old_idx) in indices.iter().enumerate() {
385 let old_ptr = *tab.offset(*old_idx as isize);
386 *tab.offset(new_pos as isize) = old_ptr;
387 }
388 }
389}
390
391unsafe fn quicksort_indices(
393 ctxt: *mut _xsltTransformContext,
394 tab: *mut *mut _xmlNode,
395 indices: &mut [usize],
396 sorts: *mut _xsltSort,
397) {
398 if indices.len() <= 1 {
399 return;
400 }
401 let pivot = indices[indices.len() / 2];
402 let mut less: Vec<usize> = Vec::new();
403 let mut greater: Vec<usize> = Vec::new();
404 for (i, idx) in indices.iter().enumerate() {
405 if *idx == pivot {
406 continue;
407 }
408 let cmp = xsltCompareNodes(
409 ctxt,
410 *tab.offset(*idx as isize),
411 *tab.offset(pivot as isize),
412 sorts,
413 );
414 if cmp <= 0 {
415 less.push(*idx);
416 } else {
417 greater.push(*idx);
418 }
419 let _ = i;
420 }
421 let pivot_pos = less.len();
422 quicksort_indices(ctxt, tab, &mut less, sorts);
423 quicksort_indices(ctxt, tab, &mut greater, sorts);
424 for (i, v) in less.into_iter().enumerate() {
425 indices[i] = v;
426 }
427 indices[pivot_pos] = pivot;
428 for (i, v) in greater.into_iter().enumerate() {
429 indices[pivot_pos + 1 + i] = v;
430 }
431}
432
433#[cfg(test)]
436mod tests {
437 use super::*;
438 use core::ptr;
439
440 #[test]
441 fn test_constants() {
442 assert_eq!(XSLT_SORT_TEXT, 0);
443 assert_eq!(XSLT_SORT_NUMBER, 1);
444 assert_eq!(XSLT_SORT_ASCENDING, 0);
445 assert_eq!(XSLT_SORT_DESCENDING, 1);
446 assert_eq!(XSLT_SORT_CASE_UPPER_FIRST, 0);
447 assert_eq!(XSLT_SORT_CASE_LOWER_FIRST, 1);
448 }
449
450 #[test]
451 fn test_compile_sort_null() {
452 unsafe {
453 assert!(xsltCompileSort(ptr::null_mut(), ptr::null_mut()).is_null());
454 }
455 }
456
457 #[test]
458 fn test_free_sort_null() {
459 unsafe {
460 xsltFreeSort(ptr::null_mut());
461 xsltFreeSortList(ptr::null_mut());
462 }
463 }
464}