1use core::char;
2use core::convert::TryFrom;
3use core::hash::BuildHasher;
4use core::num::Wrapping;
5use core::ptr;
6use core::slice;
7use core::str;
8use std::collections::TryReserveError;
9use std::ffi::{CStr, c_char, c_double, c_int, c_void};
10use std::io::Write as _;
11use std::ptr::NonNull;
12
13use artichoke_core::convert::Convert;
14use artichoke_core::hash::Hash as _;
15use bstr::ByteSlice;
16use spinoso_exception::ArgumentError;
17use spinoso_exception::NoMemoryError;
18use spinoso_string::{RawParts, String};
19
20use super::trampoline;
21use crate::convert::BoxUnboxVmValue;
22use crate::error;
23use crate::sys;
24use crate::value::Value;
25
26#[unsafe(no_mangle)]
30unsafe extern "C-unwind" fn mrb_str_new_capa(mrb: *mut sys::mrb_state, capa: sys::mrb_int) -> sys::mrb_value {
31 let capa = if let Ok(capa) = usize::try_from(capa) {
32 capa
33 } else {
34 return sys::mrb_sys_nil_value();
35 };
36 unwrap_interpreter!(mrb, to => guard);
37 let result = String::with_capacity(capa);
38 let result = String::alloc_value(result, &mut guard);
39 match result {
40 Ok(value) => value.inner(),
41 Err(exception) => {
42 unsafe { error::raise(guard, exception) }
44 }
45 }
46}
47
48#[unsafe(no_mangle)]
52unsafe extern "C-unwind" fn mrb_str_new(
53 mrb: *mut sys::mrb_state,
54 p: *const c_char,
55 len: sys::mrb_int,
56) -> sys::mrb_value {
57 let len = if let Ok(len) = usize::try_from(len) {
58 len
59 } else {
60 return sys::mrb_sys_nil_value();
61 };
62 unwrap_interpreter!(mrb, to => guard);
63 let s = if p.is_null() {
64 String::utf8(vec![0; len])
65 } else {
66 let bytes = slice::from_raw_parts(p.cast::<u8>(), len);
67 let bytes = bytes.to_vec();
68 String::utf8(bytes)
69 };
70 let result = String::alloc_value(s, &mut guard);
71 match result {
72 Ok(value) => value.inner(),
73 Err(exception) => {
74 unsafe { error::raise(guard, exception) }
76 }
77 }
78}
79
80#[unsafe(no_mangle)]
84unsafe extern "C-unwind" fn mrb_str_new_cstr(mrb: *mut sys::mrb_state, p: *const c_char) -> sys::mrb_value {
85 unwrap_interpreter!(mrb, to => guard);
86 let cstr = CStr::from_ptr(p);
87 let bytes = cstr.to_bytes().to_vec();
88 let result = String::utf8(bytes);
89 let result = String::alloc_value(result, &mut guard);
90 match result {
91 Ok(value) => value.inner(),
92 Err(exception) => {
93 unsafe { error::raise(guard, exception) }
95 }
96 }
97}
98
99#[unsafe(no_mangle)]
103unsafe extern "C-unwind" fn mrb_str_new_static(
104 mrb: *mut sys::mrb_state,
105 p: *const c_char,
106 len: sys::mrb_int,
107) -> sys::mrb_value {
108 mrb_str_new(mrb, p, len)
110}
111
112#[unsafe(no_mangle)]
116#[expect(
117 clippy::cast_possible_wrap,
118 reason = "mruby stores sizes as int64_t instead of size_t"
119)]
120unsafe extern "C-unwind" fn mrb_str_index(
121 mrb: *mut sys::mrb_state,
122 s: sys::mrb_value,
123 sptr: *const c_char,
124 slen: sys::mrb_int,
125 offset: sys::mrb_int,
126) -> sys::mrb_int {
127 unwrap_interpreter!(mrb, to => guard, or_else = -1);
128 let mut value = s.into();
129 let Ok(string) = String::unbox_from_value(&mut value, &mut guard) else {
130 return -1;
131 };
132
133 let mut offset = isize::try_from(offset).unwrap_or(0);
149 let length = isize::try_from(string.len()).unwrap_or(0);
150 if offset < 0 {
151 offset += length;
152 }
153 let Ok(offset) = usize::try_from(offset) else {
154 return -1;
155 };
156 let Some(haystack) = string.get(offset..) else {
157 return -1;
158 };
159 let needle = slice::from_raw_parts(sptr.cast::<u8>(), usize::try_from(slen).unwrap_or(0));
160 if needle.is_empty() {
161 return offset as sys::mrb_int;
162 }
163 haystack.find(needle).map_or(-1, |pos| pos as sys::mrb_int)
164}
165
166#[unsafe(no_mangle)]
170unsafe extern "C-unwind" fn mrb_str_aref(
171 mrb: *mut sys::mrb_state,
172 s: sys::mrb_value,
173 indx: sys::mrb_value,
174 alen: sys::mrb_value,
175) -> sys::mrb_value {
176 unwrap_interpreter!(mrb, to => guard);
177 let value = Value::from(s);
178 let indx = Value::from(indx);
179 let alen = Value::from(alen);
180
181 let alen = if alen.is_unreachable() { None } else { Some(alen) };
182
183 let result = trampoline::aref(&mut guard, value, indx, alen);
184 match result {
185 Ok(value) => value.into(),
186 Err(_) => Value::nil().into(),
187 }
188}
189
190#[unsafe(no_mangle)]
207unsafe extern "C-unwind" fn mrb_str_resize(
208 mrb: *mut sys::mrb_state,
209 s: sys::mrb_value,
210 len: sys::mrb_int,
211) -> sys::mrb_value {
212 fn try_resize(s: &mut String, len: usize) -> Result<(), TryReserveError> {
213 match len.checked_sub(s.len()) {
214 Some(0) => {}
215 Some(additional) => s.try_reserve(additional)?,
216 None => s.truncate(len),
218 }
219 Ok(())
220 }
221
222 unwrap_interpreter!(mrb, to => guard, or_else = s);
223 let mut value = s.into();
224 let Ok(mut string) = String::unbox_from_value(&mut value, &mut guard) else {
225 return s;
226 };
227 let Ok(len) = usize::try_from(len) else {
228 return s;
229 };
230 let string_mut = string.as_inner_mut();
233
234 let result = try_resize(string_mut, len);
235
236 let inner = string.take();
237 let value = String::box_into_value(inner, value, &mut guard).expect("String reboxing should not fail");
238
239 match result {
240 Ok(()) => value.inner(),
241 Err(_) => {
246 let err = NoMemoryError::with_message("out of memory");
250 error::raise(guard, err);
251 }
252 }
253}
254
255#[unsafe(no_mangle)]
278unsafe extern "C-unwind" fn mrb_str_plus(
279 mrb: *mut sys::mrb_state,
280 a: sys::mrb_value,
281 b: sys::mrb_value,
282) -> sys::mrb_value {
283 unwrap_interpreter!(mrb, to => guard);
284 let mut a = Value::from(a);
285 let mut b = Value::from(b);
286
287 let Ok(a) = String::unbox_from_value(&mut a, &mut guard) else {
288 return Value::nil().into();
289 };
290 let Ok(b) = String::unbox_from_value(&mut b, &mut guard) else {
291 return Value::nil().into();
292 };
293
294 let mut s = String::with_capacity_and_encoding(a.len() + b.len(), a.encoding());
295
296 s.extend_from_slice(a.as_slice());
297 s.extend_from_slice(b.as_slice());
298
299 let s = String::alloc_value(s, &mut guard).unwrap_or_default();
300 s.into()
301}
302
303#[unsafe(no_mangle)]
307unsafe extern "C-unwind" fn mrb_str_cmp(
308 mrb: *mut sys::mrb_state,
309 str1: sys::mrb_value,
310 str2: sys::mrb_value,
311) -> c_int {
312 unwrap_interpreter!(mrb, to => guard, or_else = -1);
313 let mut a = Value::from(str1);
314 let mut b = Value::from(str2);
315
316 let Ok(a) = String::unbox_from_value(&mut a, &mut guard) else {
317 return -1;
318 };
319 let Ok(b) = String::unbox_from_value(&mut b, &mut guard) else {
320 return -1;
321 };
322
323 a.cmp(&*b) as c_int
324}
325
326#[unsafe(no_mangle)]
330unsafe extern "C-unwind" fn mrb_str_equal(
331 mrb: *mut sys::mrb_state,
332 str1: sys::mrb_value,
333 str2: sys::mrb_value,
334) -> sys::mrb_bool {
335 unwrap_interpreter!(mrb, to => guard, or_else = false);
336 let mut a = Value::from(str1);
337 let mut b = Value::from(str2);
338
339 let Ok(a) = String::unbox_from_value(&mut a, &mut guard) else {
340 return false;
341 };
342 let Ok(b) = String::unbox_from_value(&mut b, &mut guard) else {
343 return false;
344 };
345
346 *a == *b
347}
348
349#[unsafe(no_mangle)]
369unsafe extern "C-unwind" fn mrb_str_dup(mrb: *mut sys::mrb_state, s: sys::mrb_value) -> sys::mrb_value {
370 unwrap_interpreter!(mrb, to => guard);
371 let mut string = Value::from(s);
372 let basic = sys::mrb_sys_basic_ptr(s).cast::<sys::RString>();
373 let class = (*basic).c;
374
375 let Ok(string) = String::unbox_from_value(&mut string, &mut guard) else {
376 return Value::nil().into();
377 };
378 let dup = string.clone();
379 let Ok(value) = String::alloc_value(dup, &mut guard) else {
380 return Value::nil().into();
381 };
382 let value = value.inner();
383
384 let dup_basic = sys::mrb_sys_basic_ptr(value).cast::<sys::RString>();
386 (*dup_basic).c = class;
387
388 value
389}
390
391#[unsafe(no_mangle)]
395unsafe extern "C-unwind" fn mrb_str_substr(
396 mrb: *mut sys::mrb_state,
397 s: sys::mrb_value,
398 beg: sys::mrb_int,
399 len: sys::mrb_int,
400) -> sys::mrb_value {
401 if len < 0 {
402 return Value::nil().into();
403 }
404 unwrap_interpreter!(mrb, to => guard);
405
406 let mut string = Value::from(s);
407 let Ok(string) = String::unbox_from_value(&mut string, &mut guard) else {
408 return Value::nil().into();
409 };
410
411 let offset = if let Ok(offset) = usize::try_from(beg) {
412 offset
413 } else {
414 let offset = beg
415 .checked_neg()
416 .and_then(|offset| usize::try_from(offset).ok())
417 .and_then(|offset| offset.checked_sub(string.len()));
418 let Some(offset) = offset else {
419 return Value::nil().into();
420 };
421 offset
422 };
423
424 let Some(slice) = string.get(offset..) else {
426 return Value::nil().into();
427 };
428 let substr = String::with_bytes_and_encoding(slice.to_vec(), string.encoding());
429 String::alloc_value(substr, &mut guard).unwrap_or_default().into()
430}
431
432#[unsafe(no_mangle)]
436unsafe extern "C-unwind" fn mrb_ptr_to_str(mrb: *mut sys::mrb_state, p: *mut c_void) -> sys::mrb_value {
437 unwrap_interpreter!(mrb, to => guard);
438 let mut s = String::with_capacity(16 + 2);
439 let _ignore = write!(s, "{p:p}");
440 String::alloc_value(s, &mut guard).unwrap_or_default().into()
441}
442
443#[unsafe(no_mangle)]
455unsafe extern "C-unwind" fn mrb_string_value_cstr(
456 mrb: *mut sys::mrb_state,
457 ptr: *mut sys::mrb_value,
458) -> *const c_char {
459 unwrap_interpreter!(mrb, to => guard, or_else = ptr::null());
460 let mut s = Value::from(*ptr);
461 let Ok(mut string) = String::unbox_from_value(&mut s, &mut guard) else {
462 return ptr::null();
463 };
464 if let Some(b'\0') = string.last() {
465 return string.as_ptr().cast();
466 }
467 let string_mut = string.as_inner_mut();
470 string_mut.push_byte(b'\0');
471 let cstr = string.as_ptr().cast::<c_char>();
474
475 let inner = string.take();
476 String::box_into_value(inner, s, &mut guard).expect("String reboxing should not fail");
477
478 cstr
479}
480
481#[unsafe(no_mangle)]
485unsafe extern "C-unwind" fn mrb_string_cstr(mrb: *mut sys::mrb_state, s: sys::mrb_value) -> *const c_char {
486 unwrap_interpreter!(mrb, to => guard, or_else = ptr::null());
487 let mut s = Value::from(s);
488 let Ok(mut string) = String::unbox_from_value(&mut s, &mut guard) else {
489 return ptr::null();
490 };
491 if let Some(b'\0') = string.last() {
492 return string.as_ptr().cast();
493 }
494 let string_mut = string.as_inner_mut();
497 string_mut.push_byte(b'\0');
498 let cstr = string.as_ptr().cast::<c_char>();
501
502 let inner = string.take();
503 String::box_into_value(inner, s, &mut guard).expect("String reboxing should not fail");
504
505 cstr
506}
507
508#[unsafe(no_mangle)]
516unsafe extern "C-unwind" fn mrb_str_to_integer(
517 mrb: *mut sys::mrb_state,
518 s: sys::mrb_value,
519 base: sys::mrb_int,
520 badcheck: sys::mrb_bool,
521) -> sys::mrb_value {
522 unwrap_interpreter!(mrb, to => guard);
523 let mut s = Value::from(s);
524 let Ok(s) = String::unbox_from_value(&mut s, &mut guard) else {
525 if badcheck {
526 let err = ArgumentError::with_message("not a string");
527 error::raise(guard, err);
528 }
529 return guard.convert(0_i64).into();
530 };
531 let Ok(s) = str::from_utf8(s.as_slice()) else {
532 if badcheck {
533 let err = ArgumentError::with_message("invalid number");
534 error::raise(guard, err);
535 }
536 return guard.convert(-1_i64).into();
537 };
538 let Ok(num) = s.parse::<i64>() else {
539 if badcheck {
540 let err = ArgumentError::with_message("invalid number");
541 error::raise(guard, err);
542 }
543 return guard.convert(0_i64).into();
544 };
545
546 let radix = match u32::try_from(base) {
547 Ok(base) if (2..=36).contains(&base) => base,
548 Ok(_) | Err(_) => {
549 let err = ArgumentError::with_message("illegal radix");
550 error::raise(guard, err);
551 }
552 };
553
554 let mut result = vec![];
555 let mut x = num;
556
557 loop {
558 let m = u32::try_from(x % base).expect("base must be <= 36, which guarantees the result is in range for u32");
559 x /= base;
560
561 result.push(char::from_digit(m, radix).unwrap());
563 if x == 0 {
564 break;
565 }
566 }
567 let int = result.into_iter().rev().collect::<String>();
568 String::alloc_value(int, &mut guard).unwrap_or_default().into()
569}
570
571#[unsafe(no_mangle)]
581unsafe extern "C-unwind" fn mrb_str_to_dbl(
582 mrb: *mut sys::mrb_state,
583 s: sys::mrb_value,
584 badcheck: sys::mrb_bool,
585) -> c_double {
586 unwrap_interpreter!(mrb, to => guard, or_else = 0.0);
587 let mut s = Value::from(s);
588 let Ok(s) = String::unbox_from_value(&mut s, &mut guard) else {
589 if badcheck {
590 let err = ArgumentError::with_message("not a string");
591 error::raise(guard, err);
592 }
593 return 0.0;
594 };
595 let Ok(s) = str::from_utf8(s.as_slice()) else {
596 if badcheck {
597 let err = ArgumentError::with_message("invalid number");
598 error::raise(guard, err);
599 }
600 return 0.0;
601 };
602 let Ok(num) = s.parse::<c_double>() else {
603 if badcheck {
604 let err = ArgumentError::with_message("invalid number");
605 error::raise(guard, err);
606 }
607 return 0.0;
608 };
609 num
610}
611
612#[unsafe(no_mangle)]
616unsafe extern "C-unwind" fn mrb_str_cat(
617 mrb: *mut sys::mrb_state,
618 s: sys::mrb_value,
619 ptr: *const c_char,
620 len: usize,
621) -> sys::mrb_value {
622 unwrap_interpreter!(mrb, to => guard, or_else = s);
623 let mut s = Value::from(s);
624 let Ok(mut string) = String::unbox_from_value(&mut s, &mut guard) else {
625 return s.inner();
626 };
627 let slice = slice::from_raw_parts(ptr.cast::<u8>(), len);
628
629 let string_mut = string.as_inner_mut();
632 string_mut.extend_from_slice(slice);
633 let inner = string.take();
634 let value = String::box_into_value(inner, s, &mut guard).expect("String reboxing should not fail");
635
636 value.inner()
637}
638
639#[unsafe(no_mangle)]
657unsafe extern "C-unwind" fn mrb_str_hash(mrb: *mut sys::mrb_state, s: sys::mrb_value) -> u32 {
658 unwrap_interpreter!(mrb, to => guard, or_else = 0);
659 let mut s = Value::from(s);
660
661 let Ok(s) = String::unbox_from_value(&mut s, &mut guard) else {
662 return 0;
663 };
664
665 let Ok(global_build_hasher) = guard.global_build_hasher() else {
670 return 0;
671 };
672
673 let hash = global_build_hasher.hash_one(s.as_slice());
674 let [one, two, three, four, ..] = hash.to_ne_bytes();
676 u32::from_ne_bytes([one, two, three, four])
677}
678
679const FNV_32_PRIME: Wrapping<u32> = Wrapping(0x0100_0193);
688const FNV1_32_INIT: Wrapping<u32> = Wrapping(0x811c_9dc5);
689
690#[unsafe(no_mangle)]
691unsafe extern "C-unwind" fn mrb_byte_hash(s: *const u8, len: sys::mrb_int) -> u32 {
692 mrb_byte_hash_step(s, len, FNV1_32_INIT)
693}
694
695#[unsafe(no_mangle)]
696#[expect(
697 clippy::cast_possible_truncation,
698 clippy::cast_sign_loss,
699 reason = "mruby stores sizes as int64_t instead of size_t"
700)]
701unsafe extern "C-unwind" fn mrb_byte_hash_step(s: *const u8, len: sys::mrb_int, mut hval: Wrapping<u32>) -> u32 {
702 let slice = slice::from_raw_parts(s, len as usize);
703 for &byte in slice {
705 hval *= FNV_32_PRIME;
707 hval ^= u32::from(byte);
709 }
710 hval.0
712}
713
714#[unsafe(no_mangle)]
715#[expect(
716 clippy::cast_possible_truncation,
717 clippy::cast_sign_loss,
718 reason = "mruby stores sizes as int64_t instead of size_t"
719)]
720unsafe extern "C-unwind" fn mrb_gc_free_str(mrb: *mut sys::mrb_state, string: *mut sys::RString) {
721 let _ = mrb;
722
723 let Some(ptr) = NonNull::<c_char>::new((*string).as_.heap.ptr) else {
724 return;
727 };
728 let length = (*string).as_.heap.len as usize;
729 let capacity = (*string).as_.heap.aux.capa as usize;
730
731 let raw_parts = RawParts {
735 ptr: ptr.cast::<u8>().as_mut(),
736 length,
737 capacity,
738 };
739 drop(String::from_raw_parts(raw_parts));
740}