abi_definitions/
string.rs1use core::{
2 cmp::{Ordering, min},
3 ffi::{c_char, c_int},
4 ptr::null_mut,
5 slice,
6};
7
8use alloc::str;
9
10use crate::memory::xila_memory_allocate_core;
11
12unsafe fn c_str_to_str(ptr: *const c_char) -> Result<&'static str, ()> {
14 unsafe {
15 if ptr.is_null() {
16 return Err(());
17 }
18
19 let len = xila_string_get_length(ptr);
20 let slice = slice::from_raw_parts(ptr as *const u8, len);
21 core::str::from_utf8(slice).map_err(|_| ())
22 }
23}
24
25#[unsafe(no_mangle)]
30pub unsafe extern "C" fn xila_string_get_length(str: *const c_char) -> usize {
31 unsafe {
32 if str.is_null() {
33 return 0;
34 }
35
36 let mut len = 0;
37 let mut ptr = str;
38 while *ptr != 0 {
39 len += 1;
40 ptr = ptr.add(1);
41 }
42 len
43 }
44}
45
46#[unsafe(no_mangle)]
51pub unsafe extern "C" fn xila_string_get_length_bounded(
52 str: *const c_char,
53 maxlen: usize,
54) -> usize {
55 unsafe {
56 if str.is_null() {
57 return 0;
58 }
59
60 let mut len = 0;
61 let mut ptr = str;
62 while len < maxlen && *ptr != 0 {
63 len += 1;
64 ptr = ptr.add(1);
65 }
66 len
67 }
68}
69
70#[unsafe(no_mangle)]
75pub unsafe extern "C" fn xila_string_compare(str1: *const c_char, str2: *const c_char) -> c_int {
76 unsafe {
77 if str1.is_null() || str2.is_null() {
78 return if str1.is_null() && str2.is_null() {
79 0
80 } else if str1.is_null() {
81 -1
82 } else {
83 1
84 };
85 }
86
87 let len1 = xila_string_get_length(str1);
88 let len2 = xila_string_get_length(str2);
89
90 let slice1 = slice::from_raw_parts(str1 as *const u8, len1);
92 let slice2 = slice::from_raw_parts(str2 as *const u8, len2);
93
94 match slice1.cmp(slice2) {
95 Ordering::Less => -1,
96 Ordering::Equal => 0,
97 Ordering::Greater => 1,
98 }
99 }
100}
101
102#[unsafe(no_mangle)]
107pub unsafe extern "C" fn xila_string_compare_bounded(
108 str1: *const c_char,
109 str2: *const c_char,
110 num: usize,
111) -> c_int {
112 unsafe {
113 if str1.is_null() || str2.is_null() || num == 0 {
114 return if str1.is_null() && str2.is_null() {
115 0
116 } else if str1.is_null() {
117 -1
118 } else {
119 1
120 };
121 }
122
123 let len1 = min(xila_string_get_length(str1), num);
124 let len2 = min(xila_string_get_length(str2), num);
125 let min_len = min(len1, len2);
126
127 let slice1 = slice::from_raw_parts(str1 as *const u8, min_len);
129 let slice2 = slice::from_raw_parts(str2 as *const u8, min_len);
130
131 use core::cmp::Ordering;
132 match slice1.cmp(slice2) {
133 Ordering::Less => -1,
134 Ordering::Greater => 1,
135 Ordering::Equal => {
136 match len1.cmp(&len2) {
138 Ordering::Less => -1,
139 Ordering::Equal => 0,
140 Ordering::Greater => 1,
141 }
142 }
143 }
144 }
145}
146
147#[unsafe(no_mangle)]
152pub unsafe extern "C" fn xila_string_copy(
153 destination: *mut c_char,
154 source: *const c_char,
155) -> *mut c_char {
156 if destination.is_null() || source.is_null() {
157 return destination;
158 }
159
160 let mut dst_ptr = destination;
161 let mut src_ptr = source;
162
163 unsafe {
164 while *src_ptr != 0 {
166 *dst_ptr = *src_ptr;
167 dst_ptr = dst_ptr.add(1);
168 src_ptr = src_ptr.add(1);
169 }
170
171 *dst_ptr = 0;
173 }
174
175 destination
176}
177
178#[unsafe(no_mangle)]
183pub unsafe extern "C" fn xila_string_copy_bounded(
184 destination: *mut c_char,
185 source: *const c_char,
186 num: usize,
187) -> *mut c_char {
188 unsafe {
189 if destination.is_null() || source.is_null() || num == 0 {
190 return destination;
191 }
192
193 let mut dst_ptr = destination;
194 let mut src_ptr = source;
195 let mut count = 0;
196
197 while count < num && *src_ptr != 0 {
199 *dst_ptr = *src_ptr;
200 dst_ptr = dst_ptr.add(1);
201 src_ptr = src_ptr.add(1);
202 count += 1;
203 }
204
205 while count < num {
207 *dst_ptr = 0;
208 dst_ptr = dst_ptr.add(1);
209 count += 1;
210 }
211
212 destination
213 }
214}
215
216#[unsafe(no_mangle)]
221pub unsafe extern "C" fn xila_string_tokenize(
222 _string: *mut c_char,
223 delimiters: *const c_char,
224) -> *mut c_char {
225 if delimiters.is_null() {
228 return null_mut();
229 }
230
231 null_mut()
234}
235
236#[unsafe(no_mangle)]
241pub unsafe extern "C" fn xila_string_find_substring(
242 haystack: *const c_char,
243 needle: *const c_char,
244) -> *mut c_char {
245 unsafe {
246 if haystack.is_null() || needle.is_null() {
247 return null_mut();
248 }
249
250 let needle_len = xila_string_get_length(needle);
251 if needle_len == 0 {
252 return haystack as *mut c_char;
253 }
254
255 let haystack_len = xila_string_get_length(haystack);
256 if needle_len > haystack_len {
257 return null_mut();
258 }
259
260 let haystack_slice = slice::from_raw_parts(haystack as *const u8, haystack_len);
262 let needle_slice = slice::from_raw_parts(needle as *const u8, needle_len);
263
264 for (i, window) in haystack_slice.windows(needle_len).enumerate() {
266 if window == needle_slice {
267 return haystack.add(i) as *mut c_char;
268 }
269 }
270
271 null_mut()
272 }
273}
274
275#[unsafe(no_mangle)]
280pub unsafe extern "C" fn xila_string_to_double(
281 nptr: *const c_char,
282 endptr: *mut *mut c_char,
283) -> f64 {
284 unsafe {
285 if nptr.is_null() {
286 if !endptr.is_null() {
287 *endptr = nptr as *mut c_char;
288 }
289 return 0.0;
290 }
291
292 if let Ok(s) = c_str_to_str(nptr) {
294 let trimmed = s.trim_start();
295 if let Ok(value) = trimmed.parse::<f64>() {
296 if !endptr.is_null() {
297 let consumed = s.len() - trimmed.len()
299 + trimmed.chars().take_while(|c| !c.is_whitespace()).count();
300 *endptr = nptr.add(consumed) as *mut c_char;
301 }
302 return value;
303 }
304 }
305
306 if !endptr.is_null() {
307 *endptr = nptr as *mut c_char;
308 }
309 0.0
310 }
311}
312
313#[unsafe(no_mangle)]
318pub unsafe extern "C" fn xila_string_compare_case_insensitive_bounded(
319 str1: *const c_char,
320 str2: *const c_char,
321 num: usize,
322) -> c_int {
323 unsafe {
324 if str1.is_null() || str2.is_null() || num == 0 {
325 return 0;
326 }
327
328 let len1 = min(xila_string_get_length(str1), num);
329 let len2 = min(xila_string_get_length(str2), num);
330 let min_len = min(len1, len2);
331
332 let slice1 = slice::from_raw_parts(str1 as *const u8, min_len);
334 let slice2 = slice::from_raw_parts(str2 as *const u8, min_len);
335
336 for (a, b) in slice1.iter().zip(slice2.iter()) {
337 let lower_a = a.to_ascii_lowercase();
338 let lower_b = b.to_ascii_lowercase();
339 if lower_a != lower_b {
340 return if lower_a < lower_b { -1 } else { 1 };
341 }
342 }
343
344 use core::cmp::Ordering;
346 match len1.cmp(&len2) {
347 Ordering::Less => -1,
348 Ordering::Equal => 0,
349 Ordering::Greater => 1,
350 }
351 }
352}
353
354#[unsafe(no_mangle)]
359pub unsafe extern "C" fn xila_string_to_unsigned_long(
360 nptr: *const c_char,
361 endptr: *mut *mut c_char,
362 base: c_int,
363) -> u64 {
364 unsafe {
365 if nptr.is_null() {
366 if !endptr.is_null() {
367 *endptr = nptr as *mut c_char;
368 }
369 return 0;
370 }
371
372 if let Ok(s) = c_str_to_str(nptr) {
374 let trimmed = s.trim_start();
375 let radix = if base == 0 {
376 if trimmed.starts_with("0x") || trimmed.starts_with("0X") {
378 16
379 } else if trimmed.starts_with("0") && trimmed.len() > 1 {
380 8
381 } else {
382 10
383 }
384 } else {
385 base as u32
386 };
387
388 if (2..=36).contains(&radix) {
389 let parse_str =
390 if radix == 16 && (trimmed.starts_with("0x") || trimmed.starts_with("0X")) {
391 &trimmed[2..]
392 } else {
393 trimmed
394 };
395
396 if let Ok(value) = u64::from_str_radix(parse_str, radix) {
397 if !endptr.is_null() {
398 let consumed = s.len() - trimmed.len()
399 + if radix == 16 && parse_str != trimmed {
400 2
401 } else {
402 0
403 }
404 + parse_str
405 .chars()
406 .take_while(|c| c.is_ascii_alphanumeric())
407 .count();
408 *endptr = nptr.add(consumed) as *mut c_char;
409 }
410 return value;
411 }
412 }
413 }
414
415 if !endptr.is_null() {
416 *endptr = nptr as *mut c_char;
417 }
418 0
419 }
420}
421
422#[unsafe(no_mangle)]
427pub unsafe extern "C" fn xila_string_find_character(s: *const c_char, c: c_int) -> *mut c_char {
428 unsafe {
429 if s.is_null() {
430 return null_mut();
431 }
432
433 let target = c as u8;
434 let len = xila_string_get_length(s);
435 let slice = slice::from_raw_parts(s as *const u8, len + 1); if let Some(pos) = slice.iter().position(|&byte| byte == target) {
439 return s.add(pos) as *mut c_char;
440 }
441
442 null_mut()
443 }
444}
445
446#[unsafe(no_mangle)]
451pub unsafe extern "C" fn xila_string_to_float(
452 nptr: *const c_char,
453 endptr: *mut *mut c_char,
454) -> f32 {
455 unsafe {
456 if nptr.is_null() {
457 if !endptr.is_null() {
458 *endptr = nptr as *mut c_char;
459 }
460 return 0.0;
461 }
462
463 if let Ok(s) = c_str_to_str(nptr) {
465 let trimmed = s.trim_start();
466 if let Ok(value) = trimmed.parse::<f32>() {
467 if !endptr.is_null() {
468 let consumed = s.len() - trimmed.len()
470 + trimmed.chars().take_while(|c| !c.is_whitespace()).count();
471 *endptr = nptr.add(consumed) as *mut c_char;
472 }
473 return value;
474 }
475 }
476
477 if !endptr.is_null() {
478 *endptr = nptr as *mut c_char;
479 }
480 0.0
481 }
482}
483
484#[unsafe(no_mangle)]
489pub unsafe extern "C" fn xila_string_span_complement(
490 s: *const c_char,
491 reject: *const c_char,
492) -> usize {
493 unsafe {
494 if s.is_null() || reject.is_null() {
495 return 0;
496 }
497
498 let s_len = xila_string_get_length(s);
499 let reject_len = xila_string_get_length(reject);
500
501 let s_slice = slice::from_raw_parts(s as *const u8, s_len);
502 let reject_slice = slice::from_raw_parts(reject as *const u8, reject_len);
503
504 s_slice
506 .iter()
507 .position(|&byte| reject_slice.contains(&byte))
508 .unwrap_or(s_len)
509 }
510}
511
512#[unsafe(no_mangle)]
517pub unsafe extern "C" fn xila_string_span(s: *const c_char, accept: *const c_char) -> usize {
518 unsafe {
519 if s.is_null() || accept.is_null() {
520 return 0;
521 }
522
523 let s_len = xila_string_get_length(s);
524 let accept_len = xila_string_get_length(accept);
525
526 let s_slice = slice::from_raw_parts(s as *const u8, s_len);
527 let accept_slice = slice::from_raw_parts(accept as *const u8, accept_len);
528
529 s_slice
531 .iter()
532 .position(|&byte| !accept_slice.contains(&byte))
533 .unwrap_or(s_len)
534 }
535}
536
537#[unsafe(no_mangle)]
542pub unsafe extern "C" fn xila_string_to_unsigned_long_long(
543 nptr: *const c_char,
544 endptr: *mut *mut c_char,
545 base: c_int,
546) -> u64 {
547 unsafe {
548 xila_string_to_unsigned_long(nptr, endptr, base)
550 }
551}
552
553#[unsafe(no_mangle)]
558pub unsafe extern "C" fn xila_string_duplicate(string: *const c_char) -> *mut c_char {
559 if string.is_null() {
560 return null_mut();
561 }
562
563 let new_string = unsafe {
564 let length = xila_string_get_length(string);
565 xila_memory_allocate_core(length + 1)
566 };
567
568 if new_string.is_null() {
569 return null_mut();
570 }
571
572 unsafe {
574 xila_string_copy(new_string as *mut c_char, string);
575 }
576
577 new_string as *mut c_char
578}
579
580#[unsafe(no_mangle)]
585pub unsafe extern "C" fn xila_string_duplicate_bounded(
586 string: *const c_char,
587 max_length: usize,
588) -> *mut c_char {
589 if string.is_null() || max_length == 0 {
590 return null_mut();
591 }
592
593 let length = unsafe { xila_string_get_length_bounded(string, max_length) };
594 let new_string = unsafe { xila_memory_allocate_core(length + 1) };
595
596 if new_string.is_null() {
597 return null_mut();
598 }
599
600 unsafe {
602 xila_string_copy_bounded(new_string as *mut c_char, string, length);
603 }
604
605 new_string as *mut c_char
606}
607
608#[unsafe(no_mangle)]
613pub unsafe extern "C" fn xila_string_parse_integer(string: *const c_char) -> c_int {
614 unsafe {
615 if string.is_null() {
616 return 0;
617 }
618
619 if let Ok(s) = c_str_to_str(string) {
621 if let Ok(value) = s.trim().parse::<c_int>() {
623 return value;
624 }
625 }
626
627 0 }
629}
630
631#[unsafe(no_mangle)]
636pub unsafe extern "C" fn xila_string_concatenate(
637 destination: *mut c_char,
638 mut source: *const c_char,
639) -> *mut c_char {
640 if destination.is_null() || source.is_null() {
641 return destination;
642 }
643
644 let mut destination_end = destination;
646 unsafe {
647 while *destination_end != 0 {
648 destination_end = destination_end.add(1);
649 }
650 }
651
652 unsafe {
653 while *source != 0 {
655 *destination_end = *source;
656 destination_end = destination_end.add(1);
657 source = source.add(1);
658 }
659 *destination_end = 0;
661 }
662
663 destination
664}