1use std::any::Any;
21use std::cell::RefCell;
22use std::rc::Rc;
23
24use crate::state_stub::{lua_CFunction, upvalue_index, LuaState, LuaStateStubExt as _};
25use lua_types::arith::ArithOp;
26use lua_types::error::LuaError;
27use lua_types::value::LuaValue;
28use lua_types::LuaType;
29
30const LUA_MAX_CAPTURES: usize = 32;
35
36const MAX_CC_CALLS: i32 = 200;
37
38const NO_DEPTH_LIMIT: i32 = i32::MAX;
43
44const L_ESC: u8 = b'%';
45
46const SPECIALS: &[u8] = b"^$*+?.([%-";
47
48const CAP_UNFINISHED: isize = -1;
49
50const CAP_POSITION: isize = -2;
51
52const MAX_INT_SIZE: usize = 16;
53
54const PACK_MAXSIZE: usize = i32::MAX as usize;
58
59const NB: u32 = 8;
60
61const MC: u8 = 0xFF;
62
63const SZINT: usize = 8; const PACK_PAD_BYTE: u8 = 0x00;
66
67#[derive(Copy, Clone)]
76struct Capture {
77 init: usize,
79 len: isize,
81}
82
83impl Default for Capture {
84 fn default() -> Self {
85 Capture {
86 init: 0,
87 len: CAP_UNFINISHED,
88 }
89 }
90}
91
92struct MatchState<'a> {
96 src: &'a [u8],
98 pat: &'a [u8],
100 matchdepth: i32,
108 level: u8,
110 captures: [Capture; LUA_MAX_CAPTURES],
112 steps: u64,
116 step_limit: u64,
119 aborted: bool,
122}
123
124impl<'a> MatchState<'a> {
125 #[inline]
130 fn new(src: &'a [u8], pat: &'a [u8], step_limit: u64, bound_depth: bool) -> Self {
131 let matchdepth = if bound_depth {
132 MAX_CC_CALLS
133 } else {
134 NO_DEPTH_LIMIT
135 };
136 MatchState {
137 src,
138 pat,
139 matchdepth,
140 level: 0,
141 captures: [Capture::default(); LUA_MAX_CAPTURES],
142 steps: 0,
143 step_limit,
144 aborted: false,
145 }
146 }
147
148 fn reset_level(&mut self) {
149 self.level = 0;
150 debug_assert!(self.matchdepth == MAX_CC_CALLS || self.matchdepth == NO_DEPTH_LIMIT);
151 }
152}
153
154struct GMatchIterState {
155 pos: usize,
157 last_match: Option<usize>,
159 bound_depth: bool,
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
173enum KOption {
174 Int, Uint, Float, Number, Double, Char, Kstring, Zstr, Padding, Paddalign, Nop, }
186
187struct Header {
190 is_little: bool,
191 max_align: usize,
192 wide_size: bool,
196}
197
198impl Header {
199 fn new(wide_size: bool) -> Self {
200 Header {
201 is_little: cfg!(target_endian = "little"),
202 max_align: 1,
203 wide_size,
204 }
205 }
206}
207
208fn pos_relat_i(pos: i64, len: usize) -> usize {
216 if pos > 0 {
217 pos as usize
218 } else if pos == 0 {
219 1
220 } else if pos < -(len as i64) {
221 1
222 } else {
223 len.wrapping_add(pos as usize).wrapping_add(1)
224 }
225}
226
227fn posrelat_53(pos: i64, len: usize) -> usize {
234 if pos >= 0 {
235 pos as usize
236 } else if (pos as i128).unsigned_abs() > len as u128 {
237 0
238 } else {
239 (len as i64 + pos + 1) as usize
240 }
241}
242
243fn get_end_pos(pos: i64, len: usize) -> usize {
247 if pos > len as i64 {
248 len
249 } else if pos >= 0 {
250 pos as usize
251 } else if pos < -(len as i64) {
252 0
253 } else {
254 len.wrapping_add(pos as usize).wrapping_add(1)
255 }
256}
257
258fn matcher_bounds_depth(version: lua_types::LuaVersion) -> bool {
264 version != lua_types::LuaVersion::V51
265}
266
267fn matcher_dedups_empty_match(version: lua_types::LuaVersion) -> bool {
273 matches!(
274 version,
275 lua_types::LuaVersion::V53 | lua_types::LuaVersion::V54 | lua_types::LuaVersion::V55
276 )
277}
278
279pub fn str_len(state: &mut LuaState) -> Result<usize, LuaError> {
290 let l = match state.to_lua_string_len(1) {
291 Some(n) => n,
292 None => {
293 state.check_arg_string(1)?;
294 unreachable!("check_arg_string raises when arg #1 is not a string");
295 }
296 };
297 state.push(LuaValue::Int(l as i64));
298 Ok(1)
299}
300
301pub fn str_sub(state: &mut LuaState) -> Result<usize, LuaError> {
309 let s_ref = match state.to_lua_string(1) {
310 Some(r) => r,
311 None => {
312 state.check_arg_string(1)?;
313 unreachable!("check_arg_string raises when arg #1 is not a string");
314 }
315 };
316 let s: &[u8] = s_ref.as_bytes();
317 let l = s.len();
318 let start = pos_relat_i(state.check_arg_integer(2)?, l);
319 let end_pos_raw = state.opt_arg_integer(3, -1)?;
320 let end = get_end_pos(end_pos_raw, l);
321 if start <= end {
322 let slice = &s[(start - 1)..end];
323 state.push_string(slice)?;
324 } else {
325 state.push_string(b"")?;
326 }
327 Ok(1)
328}
329
330pub fn str_reverse(state: &mut LuaState) -> Result<usize, LuaError> {
336 let s_ref = match state.to_lua_string(1) {
337 Some(r) => r,
338 None => {
339 state.check_arg_string(1)?;
340 unreachable!("check_arg_string raises when arg #1 is not a string");
341 }
342 };
343 let s: &[u8] = s_ref.as_bytes();
344 let buf: Vec<u8> = s.iter().copied().rev().collect();
345 state.push_bytes(&buf)?;
346 Ok(1)
347}
348
349pub fn str_lower(state: &mut LuaState) -> Result<usize, LuaError> {
355 let s_ref = match state.to_lua_string(1) {
356 Some(r) => r,
357 None => {
358 state.check_arg_string(1)?;
359 unreachable!("check_arg_string raises when arg #1 is not a string");
360 }
361 };
362 let s: &[u8] = s_ref.as_bytes();
363 let buf: Vec<u8> = s.iter().map(|&c| c.to_ascii_lowercase()).collect();
364 state.push_bytes(&buf)?;
365 Ok(1)
366}
367
368pub fn str_upper(state: &mut LuaState) -> Result<usize, LuaError> {
375 let s_ref = match state.to_lua_string(1) {
376 Some(r) => r,
377 None => {
378 state.check_arg_string(1)?;
379 unreachable!("check_arg_string raises when arg #1 is not a string");
380 }
381 };
382 let s: &[u8] = s_ref.as_bytes();
383 let buf: Vec<u8> = s.iter().map(|&c| c.to_ascii_uppercase()).collect();
384 state.push_bytes(&buf)?;
385 Ok(1)
386}
387
388pub fn str_rep(state: &mut LuaState) -> Result<usize, LuaError> {
397 let s_ref = match state.to_lua_string(1) {
398 Some(r) => r,
399 None => {
400 state.check_arg_string(1)?;
401 unreachable!("check_arg_string raises when arg #1 is not a string");
402 }
403 };
404 let s: &[u8] = s_ref.as_bytes();
405 let l = s.len();
406 let n = state.check_arg_integer(2)?;
407 let sep_owned = if state.global().lua_version == lua_types::LuaVersion::V51 {
408 Vec::new()
409 } else {
410 state.opt_arg_string(3, b"")?
411 };
412 let sep: &[u8] = &sep_owned;
413 let lsep = sep.len();
414
415 if n <= 0 {
416 state.push_string(b"")?;
417 } else {
418 const MAXSIZE: usize = i32::MAX as usize;
419 let per = l
420 .checked_add(lsep)
421 .ok_or_else(|| LuaError::runtime(format_args!("resulting string too large")))?;
422 if per > MAXSIZE / (n as usize) {
423 return Err(LuaError::runtime(format_args!(
424 "resulting string too large"
425 )));
426 }
427 let total = per * (n as usize) - lsep;
428
429 if let Some(err) = state.sandbox_reserve(total) {
430 return Err(err);
431 }
432
433 let mut buf: Vec<u8> = Vec::with_capacity(total);
434 for i in 0..(n as usize) {
435 buf.extend_from_slice(s);
436 if i < (n as usize - 1) && lsep > 0 {
437 buf.extend_from_slice(sep);
438 }
439 }
440 state.push_bytes(&buf)?;
441 }
442 Ok(1)
443}
444
445pub fn str_byte(state: &mut LuaState) -> Result<usize, LuaError> {
454 let s_ref = match state.to_lua_string(1) {
455 Some(r) => r,
456 None => {
457 state.check_arg_string(1)?;
458 unreachable!("check_arg_string raises when arg #1 is not a string");
459 }
460 };
461 let s: &[u8] = s_ref.as_bytes();
462 let l = s.len();
463 let pi = state.opt_arg_integer(2, 1)?;
464 let posi = pos_relat_i(pi, l);
465 let pose_raw = state.opt_arg_integer(3, pi)?;
466 let pose = get_end_pos(pose_raw, l);
467
468 if posi > pose {
469 return Ok(0);
470 }
471 let count = pose.saturating_sub(posi - 1) + 1;
472 if count > i32::MAX as usize {
473 return Err(LuaError::runtime(format_args!("string slice too long")));
474 }
475 let n = (pose - posi + 1) as usize;
476 state.ensure_stack(n as i32, "string slice too long")?;
477
478 for i in 0..n {
479 state.push(LuaValue::Int(s[posi - 1 + i] as i64));
480 }
481 Ok(n)
482}
483
484pub fn str_char(state: &mut LuaState) -> Result<usize, LuaError> {
487 let n = state.get_top();
488 let mut buf = Vec::with_capacity(n as usize);
489 for i in 1..=n {
490 let c = state.check_arg_integer(i)? as u64;
491 if c > u8::MAX as u64 {
492 return Err(lua_vm::debug::arg_error_impl(
493 state,
494 i,
495 b"value out of range",
496 ));
497 }
498 buf.push(c as u8);
499 }
500 state.push_bytes(&buf)?;
501 Ok(1)
502}
503
504pub fn str_dump(state: &mut LuaState) -> Result<usize, LuaError> {
508 state.check_arg_type(1, LuaType::Function)?;
509 let strip = state.arg_to_bool(2);
510 lua_vm::api::set_top(state, 1)?;
513 let bytes = state
514 .dump_function(strip)
515 .map_err(|_| LuaError::runtime(format_args!("unable to dump given function")))?;
516 state.push_bytes(&bytes)?;
517 Ok(1)
518}
519
520fn tonum(state: &mut LuaState, arg: i32) -> Result<bool, LuaError> {
528 if state.type_at(arg) == LuaType::Number {
529 state.push_value_at(arg)?;
530 Ok(true)
531 } else {
532 if let Some(s) = state.to_lua_string_bytes(arg) {
533 let len = s.len();
534 let pushed = state.string_to_number_push(&s)?;
535 let ok = pushed == len + 1;
536 if ok
541 && matches!(
542 state.global().lua_version,
543 lua_types::LuaVersion::V51
544 | lua_types::LuaVersion::V52
545 | lua_types::LuaVersion::V53
546 )
547 {
548 if let Some(f) = lua_vm::api::to_number_x(state, -1) {
549 state.pop();
550 state.push(LuaValue::Float(f));
551 }
552 }
553 Ok(ok)
554 } else {
555 Ok(false)
556 }
557 }
558}
559
560fn trymt(state: &mut LuaState, mtname: &[u8]) -> Result<(), LuaError> {
563 lua_vm::api::set_top(state, 2)?;
567 let t2_is_string = state.type_at(2) == LuaType::String;
568 if t2_is_string || !state.get_meta_field(2, mtname)? {
574 let op = &mtname[2..]; let msg = format!(
576 "attempt to {} a '{}' with a '{}'",
577 op.escape_ascii(),
578 state.type_name_at(-2).escape_ascii(),
579 state.type_name_at(-1).escape_ascii(),
580 );
581 return crate::auxlib::lua_error(state, msg.as_bytes()).map(|_| ());
582 }
583 state.insert(-3)?;
584 state.call(2, 1)?;
585 Ok(())
586}
587
588fn arith(state: &mut LuaState, op: ArithOp, mtname: &[u8]) -> Result<usize, LuaError> {
591 if tonum(state, 1)? && tonum(state, 2)? {
592 state.arith(op)?;
593 } else {
594 trymt(state, mtname)?;
595 }
596 Ok(1)
597}
598
599pub fn arith_add(state: &mut LuaState) -> Result<usize, LuaError> {
600 arith(state, ArithOp::Add, b"__add")
601}
602pub fn arith_sub(state: &mut LuaState) -> Result<usize, LuaError> {
603 arith(state, ArithOp::Sub, b"__sub")
604}
605pub fn arith_mul(state: &mut LuaState) -> Result<usize, LuaError> {
606 arith(state, ArithOp::Mul, b"__mul")
607}
608pub fn arith_mod(state: &mut LuaState) -> Result<usize, LuaError> {
609 arith(state, ArithOp::Mod, b"__mod")
610}
611pub fn arith_pow(state: &mut LuaState) -> Result<usize, LuaError> {
612 arith(state, ArithOp::Pow, b"__pow")
613}
614pub fn arith_div(state: &mut LuaState) -> Result<usize, LuaError> {
615 arith(state, ArithOp::Div, b"__div")
616}
617pub fn arith_idiv(state: &mut LuaState) -> Result<usize, LuaError> {
618 arith(state, ArithOp::Idiv, b"__idiv")
619}
620pub fn arith_unm(state: &mut LuaState) -> Result<usize, LuaError> {
621 arith(state, ArithOp::Unm, b"__unm")
622}
623
624#[inline(always)]
631fn match_class(c: u8, cl: u8) -> bool {
632 let res = match cl.to_ascii_lowercase() {
633 b'a' => c.is_ascii_alphabetic(),
634 b'c' => c.is_ascii_control(),
635 b'd' => c.is_ascii_digit(),
636 b'g' => c.is_ascii_graphic(),
637 b'l' => c.is_ascii_lowercase(),
638 b'p' => c.is_ascii_punctuation(),
639 b's' => c.is_ascii_whitespace(),
640 b'u' => c.is_ascii_uppercase(),
641 b'w' => c.is_ascii_alphanumeric(),
642 b'x' => c.is_ascii_hexdigit(),
643 b'z' => c == 0,
644 _ => return cl == c,
645 };
646 if cl.is_ascii_lowercase() {
647 res
648 } else {
649 !res
650 }
651}
652
653#[inline]
657fn matchbracketclass(pat: &[u8], c: u8, mut p: usize, ec: usize) -> bool {
658 let sig = if p + 1 < pat.len() && pat[p + 1] == b'^' {
659 p += 1; false
661 } else {
662 true
663 };
664 p += 1; while p < ec {
666 if pat[p] == L_ESC {
667 p += 1;
668 if p < ec && match_class(c, pat[p]) {
669 return sig;
670 }
671 } else if p + 1 < ec && pat[p + 1] == b'-' && p + 2 < ec {
672 let lo = pat[p];
673 p += 2;
674 let hi = pat[p];
675 if lo <= c && c <= hi {
676 return sig;
677 }
678 } else if pat[p] == c {
679 return sig;
680 }
681 p += 1;
682 }
683 !sig
684}
685
686#[inline(always)]
690fn singlematch(ms: &MatchState, s: usize, p: usize, ep: usize) -> bool {
691 if s >= ms.src.len() {
692 return false;
693 }
694 let c = ms.src[s];
695 match ms.pat[p] {
696 b'.' => true,
697 L_ESC => match_class(c, ms.pat[p + 1]),
698 b'[' => matchbracketclass(ms.pat, c, p, ep - 1),
699 pc => pc == c,
700 }
701}
702
703#[inline(always)]
707fn classend(ms: &MatchState, p: usize) -> Result<usize, LuaError> {
708 let pat = ms.pat;
709 match pat.get(p).copied() {
710 Some(L_ESC) => {
711 if p + 1 >= pat.len() {
712 return Err(LuaError::runtime(format_args!(
713 "malformed pattern (ends with '%')"
714 )));
715 }
716 Ok(p + 2)
717 }
718 Some(b'[') => {
719 let mut q = p + 1;
720 if q < pat.len() && pat[q] == b'^' {
721 q += 1;
722 }
723 loop {
724 if q >= pat.len() {
725 return Err(LuaError::runtime(format_args!(
726 "malformed pattern (missing ']')"
727 )));
728 }
729 let ch = pat[q];
730 q += 1;
731 if ch == L_ESC && q < pat.len() {
732 q += 1;
733 }
734 if q < pat.len() && pat[q] == b']' {
735 return Ok(q + 1);
736 }
737 }
738 }
739 Some(_) => Ok(p + 1),
740 None => Ok(p),
741 }
742}
743
744fn check_capture(ms: &MatchState, l: u8) -> Result<usize, LuaError> {
748 let signed = (l as i32) - (b'1' as i32);
749 if signed < 0 || signed >= ms.level as i32 || ms.captures[signed as usize].len == CAP_UNFINISHED
750 {
751 return Err(LuaError::runtime(format_args!(
752 "invalid capture index %{}",
753 signed + 1
754 )));
755 }
756 Ok(signed as usize)
757}
758
759fn capture_to_close(ms: &MatchState) -> Result<usize, LuaError> {
762 let mut level = ms.level as usize;
763 while level > 0 {
764 level -= 1;
765 if ms.captures[level].len == CAP_UNFINISHED {
766 return Ok(level);
767 }
768 }
769 Err(LuaError::runtime(format_args!("invalid pattern capture")))
770}
771
772fn matchbalance(ms: &MatchState, s: usize, p: usize) -> Result<Option<usize>, LuaError> {
776 if p + 1 >= ms.pat.len() {
777 return Err(LuaError::runtime(format_args!(
778 "malformed pattern (missing arguments to '%b')"
779 )));
780 }
781 let b = ms.pat[p];
782 let e = ms.pat[p + 1];
783 if s >= ms.src.len() || ms.src[s] != b {
784 return Ok(None);
785 }
786 let mut cont = 1i32;
787 let mut s = s + 1;
788 while s < ms.src.len() {
789 if ms.src[s] == e {
790 cont -= 1;
791 if cont == 0 {
792 return Ok(Some(s + 1));
793 }
794 } else if ms.src[s] == b {
795 cont += 1;
796 }
797 s += 1;
798 }
799 Ok(None)
800}
801
802fn max_expand(
805 ms: &mut MatchState,
806 s: usize,
807 p: usize,
808 ep: usize,
809) -> Result<Option<usize>, LuaError> {
810 let mut count: isize = 0;
811 while singlematch(ms, s + count as usize, p, ep) {
812 count += 1;
813 }
814 while count >= 0 {
815 let res = match_pat(ms, s + count as usize, ep + 1)?;
816 if res.is_some() {
817 return Ok(res);
818 }
819 count -= 1;
820 }
821 Ok(None)
822}
823
824fn min_expand(
827 ms: &mut MatchState,
828 mut s: usize,
829 p: usize,
830 ep: usize,
831) -> Result<Option<usize>, LuaError> {
832 loop {
833 let res = match_pat(ms, s, ep + 1)?;
834 if res.is_some() {
835 return Ok(res);
836 } else if singlematch(ms, s, p, ep) {
837 s += 1;
838 } else {
839 return Ok(None);
840 }
841 }
842}
843
844fn start_capture(
847 ms: &mut MatchState,
848 s: usize,
849 p: usize,
850 what: isize,
851) -> Result<Option<usize>, LuaError> {
852 let level = ms.level as usize;
853 if level >= LUA_MAX_CAPTURES {
854 return Err(LuaError::runtime(format_args!("too many captures")));
855 }
856 ms.captures[level].init = s;
857 ms.captures[level].len = what;
858 ms.level += 1;
859 let res = match_pat(ms, s, p)?;
860 if res.is_none() {
861 ms.level -= 1; }
863 Ok(res)
864}
865
866fn end_capture(ms: &mut MatchState, s: usize, p: usize) -> Result<Option<usize>, LuaError> {
869 let l = capture_to_close(ms)?;
870 ms.captures[l].len = (s - ms.captures[l].init) as isize;
871 let res = match_pat(ms, s, p)?;
872 if res.is_none() {
873 ms.captures[l].len = CAP_UNFINISHED; }
875 Ok(res)
876}
877
878fn match_capture(ms: &MatchState, s: usize, l: u8) -> Result<Option<usize>, LuaError> {
881 let idx = check_capture(ms, l)?;
882 let cap_len = ms.captures[idx].len as usize;
883 let cap_init = ms.captures[idx].init;
884 if ms.src.len() - s >= cap_len
885 && &ms.src[s..s + cap_len] == &ms.src[cap_init..cap_init + cap_len]
886 {
887 Ok(Some(s + cap_len))
888 } else {
889 Ok(None)
890 }
891}
892
893fn match_pat(ms: &mut MatchState, mut s: usize, mut p: usize) -> Result<Option<usize>, LuaError> {
908 if ms.aborted {
909 return Ok(None);
910 }
911 ms.steps += 1;
912 if ms.step_limit != 0 && ms.steps > ms.step_limit {
913 ms.aborted = true;
914 return Ok(None);
915 }
916 ms.matchdepth -= 1;
917 if ms.matchdepth < 0 {
918 ms.matchdepth = 0;
919 return Err(LuaError::runtime(format_args!("pattern too complex")));
920 }
921
922 let result = 'outer: loop {
924 if p >= ms.pat.len() {
925 break 'outer Ok(Some(s));
927 }
928
929 match ms.pat[p] {
930 b'(' => {
931 let s2 = if p + 1 < ms.pat.len() && ms.pat[p + 1] == b')' {
932 start_capture(ms, s, p + 2, CAP_POSITION)?
934 } else {
935 start_capture(ms, s, p + 1, CAP_UNFINISHED)?
936 };
937 break 'outer Ok(s2);
938 }
939 b')' => {
940 let s2 = end_capture(ms, s, p + 1)?;
941 break 'outer Ok(s2);
942 }
943 b'$' => {
944 if p + 1 != ms.pat.len() {
945 let ep = classend(ms, p)?;
947 let s2 = handle_class_with_suffix(ms, s, p, ep)?;
948 break 'outer Ok(s2);
949 }
950 break 'outer Ok(if s == ms.src.len() { Some(s) } else { None });
951 }
952 L_ESC => {
953 match ms.pat.get(p + 1).copied().unwrap_or(0) {
954 b'b' => {
955 let s2 = matchbalance(ms, s, p + 2)?;
956 if let Some(ns) = s2 {
957 s = ns;
958 p += 4;
959 continue 'outer; }
961 break 'outer Ok(None);
962 }
963 b'f' => {
964 p += 2;
965 if ms.pat.get(p).copied() != Some(b'[') {
966 return Err(LuaError::runtime(format_args!(
967 "missing '[' after '%f' in pattern"
968 )));
969 }
970 let ep = classend(ms, p)?;
971 let previous = if s == 0 { 0u8 } else { ms.src[s - 1] };
972 let current = ms.src.get(s).copied().unwrap_or(0);
973 if !matchbracketclass(ms.pat, previous, p, ep - 1)
974 && matchbracketclass(ms.pat, current, p, ep - 1)
975 {
976 p = ep;
977 continue 'outer; }
979 break 'outer Ok(None);
980 }
981 c @ b'0'..=b'9' => {
982 let s2 = match_capture(ms, s, c)?;
983 if let Some(ns) = s2 {
984 s = ns;
985 p += 2;
986 continue 'outer; }
988 break 'outer Ok(None);
989 }
990 _ => {
991 let ep = classend(ms, p)?;
993 let s2 = handle_class_with_suffix(ms, s, p, ep)?;
994 break 'outer Ok(s2);
995 }
996 }
997 }
998 _ => {
999 let ep = classend(ms, p)?;
1001 let s2 = handle_class_with_suffix(ms, s, p, ep)?;
1002 break 'outer Ok(s2);
1003 }
1004 }
1005 };
1006
1007 ms.matchdepth += 1;
1008 result
1009}
1010
1011#[inline(always)]
1016fn handle_class_with_suffix(
1017 ms: &mut MatchState,
1018 s: usize,
1019 p: usize,
1020 ep: usize,
1021) -> Result<Option<usize>, LuaError> {
1022 let matched_once = singlematch(ms, s, p, ep);
1023 if !matched_once {
1024 match ms.pat.get(ep).copied() {
1025 Some(b'*') | Some(b'?') | Some(b'-') => {
1026 return match_pat(ms, s, ep + 1);
1030 }
1031 _ => return Ok(None),
1032 }
1033 }
1034
1035 match ms.pat.get(ep).copied() {
1037 Some(b'?') => {
1038 let res = match_pat(ms, s + 1, ep + 1)?;
1040 if res.is_some() {
1041 Ok(res)
1042 } else {
1043 match_pat(ms, s, ep + 1)
1044 }
1045 }
1046 Some(b'+') => {
1047 max_expand(ms, s + 1, p, ep)
1049 }
1050 Some(b'*') => {
1051 max_expand(ms, s, p, ep)
1053 }
1054 Some(b'-') => {
1055 min_expand(ms, s, p, ep)
1057 }
1058 _ => {
1059 match_pat(ms, s + 1, ep)
1061 }
1062 }
1063}
1064
1065fn lmemfind(haystack: &[u8], needle: &[u8]) -> Option<usize> {
1073 if needle.is_empty() {
1074 return Some(0);
1075 }
1076 if needle.len() > haystack.len() {
1077 return None;
1078 }
1079 let first = needle[0];
1080 let rest = &needle[1..];
1081 let limit = haystack.len() - rest.len();
1082 let mut s = 0;
1083 while s <= limit {
1084 if let Some(pos) = haystack[s..].iter().position(|&b| b == first) {
1085 let pos = s + pos;
1086 if pos + 1 + rest.len() <= haystack.len()
1087 && &haystack[pos + 1..pos + 1 + rest.len()] == rest
1088 {
1089 return Some(pos);
1090 }
1091 s = pos + 1;
1092 } else {
1093 break;
1094 }
1095 }
1096 None
1097}
1098
1099fn required_start_byte(pat: &[u8]) -> Option<u8> {
1100 let (byte, ep) = match pat.first().copied()? {
1101 L_ESC => {
1102 let escaped = *pat.get(1)?;
1103 if escaped.is_ascii_alphanumeric() {
1104 return None;
1105 }
1106 (escaped, 2)
1107 }
1108 c if !SPECIALS.contains(&c) => (c, 1),
1109 _ => return None,
1110 };
1111 match pat.get(ep).copied() {
1112 Some(b'*') | Some(b'?') | Some(b'-') => None,
1113 _ => Some(byte),
1114 }
1115}
1116
1117fn next_start_with_byte(src: &[u8], pos: usize, byte: u8) -> Option<usize> {
1118 src.get(pos..)?
1119 .iter()
1120 .position(|&c| c == byte)
1121 .map(|offset| pos + offset)
1122}
1123
1124fn nospecials(pat: &[u8]) -> bool {
1127 !pat.iter().any(|b| SPECIALS.contains(b))
1128}
1129
1130enum CaptureInfo<'a> {
1132 Position(i64),
1134 Bytes(&'a [u8]),
1136}
1137
1138fn get_one_capture<'a>(
1142 ms: &'a MatchState,
1143 i: usize,
1144 s: usize,
1145 e: usize,
1146) -> Result<CaptureInfo<'a>, LuaError> {
1147 if i >= ms.level as usize {
1148 if i != 0 {
1149 return Err(LuaError::runtime(format_args!(
1150 "invalid capture index %{}",
1151 i + 1
1152 )));
1153 }
1154 return Ok(CaptureInfo::Bytes(&ms.src[s..e]));
1156 }
1157 let cap = &ms.captures[i];
1158 if cap.len == CAP_UNFINISHED {
1159 return Err(LuaError::runtime(format_args!("unfinished capture")));
1160 }
1161 if cap.len == CAP_POSITION {
1162 return Ok(CaptureInfo::Position((cap.init + 1) as i64));
1163 }
1164 let len = cap.len as usize;
1165 Ok(CaptureInfo::Bytes(&ms.src[cap.init..cap.init + len]))
1166}
1167
1168fn push_captures(
1176 state: &mut LuaState,
1177 ms: &MatchState,
1178 span: Option<(usize, usize)>,
1179) -> Result<usize, LuaError> {
1180 let nlevels = if ms.level == 0 && span.is_some() {
1181 1
1182 } else {
1183 ms.level as usize
1184 };
1185 state.ensure_stack(nlevels as i32, "too many captures")?;
1186 let (s, e) = span.unwrap_or((0, 0));
1187 for i in 0..nlevels {
1188 match get_one_capture(ms, i, s, e)? {
1189 CaptureInfo::Position(n) => state.push(LuaValue::Int(n)),
1190 CaptureInfo::Bytes(b) => state.push_bytes(b)?,
1191 }
1192 }
1193 Ok(nlevels)
1194}
1195
1196fn str_find_aux(state: &mut LuaState, find: bool) -> Result<usize, LuaError> {
1203 let s_ref = match state.to_lua_string(1) {
1204 Some(r) => r,
1205 None => {
1206 state.check_arg_string(1)?;
1207 unreachable!("check_arg_string raises when arg #1 is not a string");
1208 }
1209 };
1210 let p_ref = match state.to_lua_string(2) {
1211 Some(r) => r,
1212 None => {
1213 state.check_arg_string(2)?;
1214 unreachable!("check_arg_string raises when arg #2 is not a string");
1215 }
1216 };
1217 let s: &[u8] = s_ref.as_bytes();
1218 let p: &[u8] = p_ref.as_bytes();
1219 let ls = s.len();
1220 let lp = p.len();
1221 let init_raw = state.opt_arg_integer(3, 1)?;
1222 let init = pos_relat_i(init_raw, ls).saturating_sub(1);
1223
1224 if init > ls {
1225 state.push(LuaValue::Nil);
1226 return Ok(1);
1227 }
1228
1229 if find && (state.arg_to_bool(4) || nospecials(p)) {
1230 if let Some(pos) = lmemfind(&s[init..], p) {
1232 let abs = init + pos;
1233 state.push(LuaValue::Int((abs + 1) as i64));
1234 state.push(LuaValue::Int((abs + lp) as i64));
1235 return Ok(2);
1236 }
1237 } else {
1238 let step_limit = state.sandbox_match_step_limit();
1239 let bound_depth = matcher_bounds_depth(state.global().lua_version);
1240 let mut ms = MatchState::new(s, p, step_limit, bound_depth);
1241 let anchor = p.first() == Some(&b'^');
1242 let p_slice = if anchor { &p[1..] } else { p };
1243 ms.pat = p_slice;
1244 let start_byte = if anchor {
1245 None
1246 } else {
1247 required_start_byte(ms.pat)
1248 };
1249
1250 let mut s1 = init;
1251 let mut matched: Option<usize> = None;
1252 loop {
1253 if let Some(byte) = start_byte {
1254 let Some(next) = next_start_with_byte(ms.src, s1, byte) else {
1255 break;
1256 };
1257 s1 = next;
1258 }
1259 ms.reset_level();
1260 if let Some(res) = match_pat(&mut ms, s1, 0)? {
1261 matched = Some(res);
1262 break;
1263 }
1264 if ms.aborted || s1 >= ms.src.len() || anchor {
1265 break;
1266 }
1267 s1 += 1;
1268 }
1269
1270 if let Some(err) = state.sandbox_charge(ms.steps) {
1271 return Err(err);
1272 }
1273
1274 if let Some(res) = matched {
1275 if find {
1276 state.push(LuaValue::Int((s1 + 1) as i64));
1277 state.push(LuaValue::Int(res as i64));
1278 let nc = push_captures(state, &ms, None)?;
1279 return Ok(nc + 2);
1280 } else {
1281 return push_captures(state, &ms, Some((s1, res)));
1282 }
1283 }
1284 }
1285
1286 state.push(LuaValue::Nil);
1287 Ok(1)
1288}
1289
1290pub fn str_find(state: &mut LuaState) -> Result<usize, LuaError> {
1293 str_find_aux(state, true)
1294}
1295
1296pub fn str_match(state: &mut LuaState) -> Result<usize, LuaError> {
1299 str_find_aux(state, false)
1300}
1301
1302pub fn gmatch_aux(state: &mut LuaState) -> Result<usize, LuaError> {
1317 gmatch_step::<true>(state)
1318}
1319
1320pub fn gmatch_aux_legacy(state: &mut LuaState) -> Result<usize, LuaError> {
1323 gmatch_step::<false>(state)
1324}
1325
1326#[inline(always)]
1327fn gmatch_step<const DEDUP: bool>(state: &mut LuaState) -> Result<usize, LuaError> {
1328 let s_val = state.value_at(upvalue_index(1));
1329 let p_val = state.value_at(upvalue_index(2));
1330 let (LuaValue::Str(s_str), LuaValue::Str(p_str)) = (&s_val, &p_val) else {
1331 return Ok(0);
1332 };
1333 let iter_val = state.value_at(upvalue_index(3));
1334 let LuaValue::UserData(iter_ud) = iter_val else {
1335 return Ok(0);
1336 };
1337 let Some(host) = iter_ud.host_value() else {
1338 return Ok(0);
1339 };
1340 let Ok(iter_state) = host.downcast::<RefCell<GMatchIterState>>() else {
1341 return Ok(0);
1342 };
1343
1344 let s: &[u8] = s_str.as_bytes();
1345 let p: &[u8] = p_str.as_bytes();
1346 let (start_pos, last_match, stored_bound_depth) = {
1347 let iter = iter_state.borrow();
1348 (iter.pos, iter.last_match, iter.bound_depth)
1349 };
1350 let bound_depth = if DEDUP { true } else { stored_bound_depth };
1353
1354 let ls = s.len();
1355
1356 let step_limit = state.sandbox_match_step_limit();
1357 let mut ms = MatchState::new(s, p, step_limit, bound_depth);
1358 let start_byte = required_start_byte(p);
1359
1360 let mut src = start_pos;
1361 let mut hit: Option<(usize, usize)> = None;
1362 while src <= ls {
1363 if let Some(byte) = start_byte {
1364 let Some(next) = next_start_with_byte(s, src, byte) else {
1365 break;
1366 };
1367 src = next;
1368 }
1369 ms.reset_level();
1370 if let Some(e) = match_pat(&mut ms, src, 0)? {
1371 if !DEDUP || Some(e) != last_match {
1372 hit = Some((src, e));
1373 break;
1374 }
1375 }
1376 if ms.aborted {
1377 break;
1378 }
1379 src += 1;
1380 }
1381
1382 if let Some(err) = state.sandbox_charge(ms.steps) {
1383 return Err(err);
1384 }
1385
1386 if let Some((src, e)) = hit {
1387 {
1388 let mut iter = iter_state.borrow_mut();
1389 iter.pos = if !DEDUP && e == src { e + 1 } else { e };
1393 iter.last_match = Some(e);
1394 }
1395 return push_captures(state, &ms, Some((src, e)));
1396 }
1397
1398 Ok(0)
1399}
1400
1401pub fn gmatch(state: &mut LuaState) -> Result<usize, LuaError> {
1410 let s_ref = match state.to_lua_string(1) {
1411 Some(r) => r,
1412 None => {
1413 state.check_arg_string(1)?;
1414 unreachable!("check_arg_string raises when arg #1 is not a string");
1415 }
1416 };
1417 let ls = s_ref.len();
1418 match state.to_lua_string(2) {
1419 Some(_) => {}
1420 None => {
1421 state.check_arg_string(2)?;
1422 unreachable!("check_arg_string raises when arg #2 is not a string");
1423 }
1424 };
1425 let init_raw = state.opt_arg_integer(3, 1)?;
1426 let mut init = pos_relat_i(init_raw, ls).saturating_sub(1);
1427 if init > ls {
1428 init = ls + 1;
1429 }
1430
1431 let version = state.global().lua_version;
1432 let dedup = matcher_dedups_empty_match(version);
1433
1434 lua_vm::api::set_top(state, 2)?;
1435
1436 state.push_value_at(1)?;
1437 state.push_value_at(2)?;
1438 let iter_ud = state.new_userdata_typed(b"string.gmatch.state", 0, 0)?;
1439 let iter_state: Rc<dyn Any> = Rc::new(RefCell::new(GMatchIterState {
1440 pos: init,
1441 last_match: None,
1442 bound_depth: matcher_bounds_depth(version),
1443 }));
1444 iter_ud.set_host_value(Some(iter_state));
1445
1446 let aux = if dedup { gmatch_aux } else { gmatch_aux_legacy };
1447 state.push_c_closure(aux, 3)?;
1448 Ok(1)
1449}
1450
1451fn add_s(
1454 state: &mut LuaState,
1455 ms: &MatchState,
1456 buf: &mut Vec<u8>,
1457 s: usize,
1458 e: usize,
1459) -> Result<(), LuaError> {
1460 let news_bytes = state.to_lua_string_bytes(3).unwrap_or_default();
1461 let mut i = 0usize;
1462 while i < news_bytes.len() {
1463 if news_bytes[i] != L_ESC {
1464 buf.push(news_bytes[i]);
1465 i += 1;
1466 } else {
1467 i += 1; if i >= news_bytes.len() {
1469 break;
1470 }
1471 let c = news_bytes[i];
1472 if c == L_ESC {
1473 buf.push(L_ESC);
1474 } else if c == b'0' {
1475 buf.extend_from_slice(&ms.src[s..e]);
1476 } else if c.is_ascii_digit() {
1477 match get_one_capture(ms, (c - b'1') as usize, s, e)? {
1478 CaptureInfo::Position(n) => {
1479 let formatted = format!("{}", n).into_bytes();
1481 buf.extend_from_slice(&formatted);
1482 }
1483 CaptureInfo::Bytes(b) => {
1484 buf.extend_from_slice(b);
1485 }
1486 }
1487 } else {
1488 return Err(LuaError::runtime(format_args!(
1489 "invalid use of '{}' in replacement string",
1490 L_ESC as char
1491 )));
1492 }
1493 i += 1;
1494 }
1495 }
1496 Ok(())
1497}
1498
1499fn add_value(
1507 state: &mut LuaState,
1508 ms: &MatchState,
1509 buf: &mut Vec<u8>,
1510 s: usize,
1511 e: usize,
1512 tr: LuaType,
1513) -> Result<bool, LuaError> {
1514 match tr {
1515 LuaType::Function => {
1516 state.push_value_at(3)?;
1517 let n = push_captures(state, ms, Some((s, e)))?;
1518 state.call(n as i32, 1)?;
1519 }
1520 LuaType::Table => {
1521 match get_one_capture(ms, 0, s, e)? {
1522 CaptureInfo::Position(n) => state.push(LuaValue::Int(n)),
1523 CaptureInfo::Bytes(b) => state.push_bytes(b)?,
1524 }
1525 state.get_table(3)?;
1526 }
1527 _ => {
1528 add_s(state, ms, buf, s, e)?;
1530 return Ok(true);
1531 }
1532 }
1533
1534 let top_bool = state.arg_to_bool(-1);
1535 if !top_bool {
1536 state.pop_n(1);
1537 buf.extend_from_slice(&ms.src[s..e]);
1538 return Ok(false);
1539 }
1540 let ty = state.type_at(-1);
1541 if ty != LuaType::String && ty != LuaType::Number {
1542 let tname = state.type_name_at(-1).to_owned();
1543 return Err(LuaError::runtime(format_args!(
1544 "invalid replacement value (a {})",
1545 tname.escape_ascii()
1546 )));
1547 }
1548 let v = match state.to_string_coerced(-1) {
1549 Some(b) => b,
1550 None => Vec::new(),
1551 };
1552 state.pop();
1553 buf.extend_from_slice(&v);
1554 Ok(true)
1555}
1556
1557pub fn str_gsub(state: &mut LuaState) -> Result<usize, LuaError> {
1560 let src_ref = match state.to_lua_string(1) {
1561 Some(r) => r,
1562 None => {
1563 state.check_arg_string(1)?;
1564 unreachable!("check_arg_string raises when arg #1 is not a string");
1565 }
1566 };
1567 let pat_ref = match state.to_lua_string(2) {
1568 Some(r) => r,
1569 None => {
1570 state.check_arg_string(2)?;
1571 unreachable!("check_arg_string raises when arg #2 is not a string");
1572 }
1573 };
1574 let src: &[u8] = src_ref.as_bytes();
1575 let pat: &[u8] = pat_ref.as_bytes();
1576 let src_len = src.len();
1577 let max_s = state.opt_arg_integer(4, (src_len + 1) as i64)?;
1578 let tr = state.type_at(3);
1579
1580 if !matches!(
1581 tr,
1582 LuaType::Number | LuaType::String | LuaType::Function | LuaType::Table
1583 ) {
1584 let v = state.arg(3);
1585 return Err(LuaError::type_arg_error(3, "string/function/table", &v));
1586 }
1587
1588 let anchor = pat.first() == Some(&b'^');
1589 let pat_slice = if anchor { &pat[1..] } else { pat };
1590
1591 let version = state.global().lua_version;
1592 let dedup = matcher_dedups_empty_match(version);
1593
1594 let step_limit = state.sandbox_match_step_limit();
1595 let mut ms = MatchState::new(src, pat_slice, step_limit, matcher_bounds_depth(version));
1596 let start_byte = if anchor {
1597 None
1598 } else {
1599 required_start_byte(ms.pat)
1600 };
1601 let mut buf: Vec<u8> = Vec::with_capacity(src_len);
1602 let mut src_pos = 0usize;
1603 let mut last_match: Option<usize> = None;
1604 let mut n: i64 = 0;
1605 let mut changed = false;
1606
1607 while n < max_s {
1608 if let Some(byte) = start_byte {
1609 let Some(next) = next_start_with_byte(ms.src, src_pos, byte) else {
1610 buf.extend_from_slice(&ms.src[src_pos..]);
1611 src_pos = ms.src.len();
1612 break;
1613 };
1614 if next > src_pos {
1615 buf.extend_from_slice(&ms.src[src_pos..next]);
1616 src_pos = next;
1617 }
1618 }
1619 ms.reset_level();
1620 let maybe_e = match_pat(&mut ms, src_pos, 0)?;
1621 if dedup {
1622 if let Some(e) = maybe_e {
1626 if last_match != Some(e) {
1627 n += 1;
1628 let delta = add_value(state, &ms, &mut buf, src_pos, e, tr)?;
1629 changed |= delta;
1630 src_pos = e;
1631 last_match = Some(e);
1632 } else if src_pos < ms.src.len() {
1633 buf.push(ms.src[src_pos]);
1634 src_pos += 1;
1635 } else {
1636 break;
1637 }
1638 } else if src_pos < ms.src.len() {
1639 buf.push(ms.src[src_pos]);
1640 src_pos += 1;
1641 } else {
1642 break;
1643 }
1644 } else {
1645 if let Some(e) = maybe_e {
1651 n += 1;
1652 let delta = add_value(state, &ms, &mut buf, src_pos, e, tr)?;
1653 changed |= delta;
1654 if e > src_pos {
1655 src_pos = e;
1656 } else if src_pos < ms.src.len() {
1657 buf.push(ms.src[src_pos]);
1658 src_pos += 1;
1659 } else {
1660 break;
1661 }
1662 } else if src_pos < ms.src.len() {
1663 buf.push(ms.src[src_pos]);
1664 src_pos += 1;
1665 } else {
1666 break;
1667 }
1668 }
1669 if ms.aborted || anchor {
1670 break;
1671 }
1672 }
1673
1674 if let Some(err) = state.sandbox_charge(ms.steps) {
1675 return Err(err);
1676 }
1677
1678 if !changed {
1679 state.push_value_at(1)?;
1680 } else {
1681 buf.extend_from_slice(&ms.src[src_pos..]);
1682 state.push_bytes(&buf)?;
1683 }
1684 state.push(LuaValue::Int(n));
1685 Ok(2)
1686}
1687
1688fn adddigit(buf: &mut Vec<u8>, x: f64) -> f64 {
1695 let dd = x.floor();
1696 let d = dd as i32;
1697 let c = if d < 10 {
1698 b'0' + d as u8
1699 } else {
1700 b'a' + (d - 10) as u8
1701 };
1702 buf.push(c);
1703 x - dd
1704}
1705
1706fn num2straux(x: f64) -> Vec<u8> {
1711 format_hex_float(x, None)
1712}
1713
1714fn format_hex_float(x: f64, precision: Option<usize>) -> Vec<u8> {
1722 if x.is_nan() {
1723 return b"nan".to_vec();
1724 }
1725 if x.is_infinite() {
1726 return if x < 0.0 {
1727 b"-inf".to_vec()
1728 } else {
1729 b"inf".to_vec()
1730 };
1731 }
1732 if x == 0.0 {
1733 let sign: &[u8] = if x.is_sign_negative() { b"-" } else { b"" };
1734 return match precision {
1735 None => [sign, b"0x0p+0"].concat(),
1736 Some(0) => [sign, b"0x0p+0"].concat(),
1737 Some(p) => {
1738 let zeros = "0".repeat(p);
1739 [sign, b"0x0.", zeros.as_bytes(), b"p+0"].concat()
1740 }
1741 };
1742 }
1743
1744 let (m_raw, exp) = frexp(x);
1745 let mut buf: Vec<u8> = Vec::new();
1746 let mut m = m_raw;
1747 if m < 0.0 {
1748 buf.push(b'-');
1749 m = -m;
1750 }
1751 buf.extend_from_slice(b"0x");
1752
1753 let nbfd = 1;
1754 m = adddigit(&mut buf, m * (1 << nbfd) as f64);
1755 let e = exp - nbfd;
1756
1757 match precision {
1758 None => {
1759 if m > 0.0 {
1760 buf.push(b'.');
1761 while m > 0.0 {
1762 m = adddigit(&mut buf, m * 16.0);
1763 }
1764 }
1765 }
1766 Some(0) => {}
1767 Some(p) => {
1768 buf.push(b'.');
1769 for _ in 0..p {
1770 if m > 0.0 {
1771 m = adddigit(&mut buf, m * 16.0);
1772 } else {
1773 buf.push(b'0');
1774 }
1775 }
1776 }
1777 }
1778
1779 let exp_str = format!("p{:+}", e);
1780 buf.extend_from_slice(exp_str.as_bytes());
1781 buf
1782}
1783
1784fn frexp(x: f64) -> (f64, i32) {
1789 if x == 0.0 || x.is_nan() || x.is_infinite() {
1790 return (x, 0);
1791 }
1792 let bits = x.to_bits();
1793 let sign_bit = bits & 0x8000_0000_0000_0000u64;
1794 let exp_bits = ((bits >> 52) & 0x7FF) as i32;
1795 if exp_bits == 0 {
1796 let (m, e) = frexp(x * (1u64 << 52) as f64);
1797 return (m, e - 52);
1798 }
1799 let exp = exp_bits - 1022;
1800 let mantissa_bits = sign_bit | (bits & 0x000F_FFFF_FFFF_FFFF) | 0x3FE0_0000_0000_0000;
1801 (f64::from_bits(mantissa_bits), exp)
1802}
1803
1804fn quotefloat(n: f64, version: lua_types::LuaVersion) -> Vec<u8> {
1810 if n == f64::INFINITY {
1811 return if version == lua_types::LuaVersion::V53 {
1812 b"inf".to_vec()
1813 } else {
1814 b"1e9999".to_vec()
1815 };
1816 } else if n == f64::NEG_INFINITY {
1817 return if version == lua_types::LuaVersion::V53 {
1818 b"-inf".to_vec()
1819 } else {
1820 b"-1e9999".to_vec()
1821 };
1822 } else if n.is_nan() {
1823 return if version == lua_types::LuaVersion::V53 {
1824 b"nan".to_vec()
1825 } else {
1826 b"(0/0)".to_vec()
1827 };
1828 }
1829 num2straux(n)
1832}
1833
1834fn addquoted(buf: &mut Vec<u8>, s: &[u8]) {
1840 buf.push(b'"');
1841 for (idx, &c) in s.iter().enumerate() {
1842 if c == b'"' || c == b'\\' || c == b'\n' {
1843 buf.push(b'\\');
1844 buf.push(c);
1845 } else if c.is_ascii_control() {
1846 let next_is_digit = s.get(idx + 1).map_or(false, |n| n.is_ascii_digit());
1847 let formatted = if next_is_digit {
1848 format!("\\{:03}", c)
1849 } else {
1850 format!("\\{}", c)
1851 };
1852 buf.extend_from_slice(formatted.as_bytes());
1853 } else {
1854 buf.push(c);
1855 }
1856 }
1857 buf.push(b'"');
1858}
1859
1860fn addquoted_51(buf: &mut Vec<u8>, s: &[u8]) {
1867 buf.push(b'"');
1868 for &c in s.iter() {
1869 match c {
1870 b'"' | b'\\' | b'\n' => {
1871 buf.push(b'\\');
1872 buf.push(c);
1873 }
1874 b'\r' => buf.extend_from_slice(b"\\r"),
1875 0 => buf.extend_from_slice(b"\\000"),
1876 _ => buf.push(c),
1877 }
1878 }
1879 buf.push(b'"');
1880}
1881
1882fn addliteral(state: &mut LuaState, buf: &mut Vec<u8>, arg: i32) -> Result<(), LuaError> {
1885 match state.type_at(arg) {
1886 LuaType::String => {
1887 let s = state.check_arg_string(arg)?.to_vec();
1888 addquoted(buf, &s);
1889 }
1890 LuaType::Number => {
1891 if state.is_integer(arg) {
1892 let n = state.to_integer(arg).unwrap_or(0);
1893 let formatted = if n == i64::MIN {
1894 format!("0x{:016x}", n as u64)
1895 } else {
1896 format!("{}", n)
1897 };
1898 buf.extend_from_slice(formatted.as_bytes());
1899 } else {
1900 let version = state.global().lua_version;
1901 let n = state.to_number(arg).unwrap_or(0.0);
1902 let hex = quotefloat(n, version);
1903 buf.extend_from_slice(&hex);
1904 }
1905 }
1906 LuaType::Nil => {
1907 buf.extend_from_slice(b"nil");
1908 }
1909 LuaType::Boolean => {
1910 buf.extend_from_slice(if state.to_boolean(arg) {
1911 b"true"
1912 } else {
1913 b"false"
1914 });
1915 }
1916 _ => {
1917 return Err(LuaError::arg_error(arg, "value has no literal form"));
1918 }
1919 }
1920 Ok(())
1921}
1922
1923const FMT_FLAGS_F: &[u8] = b"-+#0 ";
1926const FMT_FLAGS_X: &[u8] = b"-#0";
1927const FMT_FLAGS_I: &[u8] = b"-+0 ";
1928const FMT_FLAGS_U: &[u8] = b"-0";
1929const FMT_FLAGS_C: &[u8] = b"-";
1930
1931fn check_conv_spec(
1941 state: &mut LuaState,
1942 form: &[u8],
1943 flags: &[u8],
1944 allow_precision: bool,
1945) -> Result<(), LuaError> {
1946 let mut i = 1usize; while i < form.len() && flags.contains(&form[i]) {
1948 i += 1;
1949 }
1950 if i < form.len() && form[i] == b'0' {
1951 return Err(invalid_conv_spec(state, form));
1952 }
1953 if i < form.len() && form[i].is_ascii_digit() {
1954 i += 1;
1955 if i < form.len() && form[i].is_ascii_digit() {
1956 i += 1;
1957 }
1958 }
1959 if allow_precision && i < form.len() && form[i] == b'.' {
1960 i += 1;
1961 if i < form.len() && form[i].is_ascii_digit() {
1962 i += 1;
1963 if i < form.len() && form[i].is_ascii_digit() {
1964 i += 1;
1965 }
1966 }
1967 }
1968 if i != form.len() - 1 {
1969 return Err(invalid_conv_spec(state, form));
1970 }
1971 Ok(())
1972}
1973
1974fn invalid_conv_spec(state: &mut LuaState, form: &[u8]) -> LuaError {
1981 let msg: Vec<u8> = if state.global().lua_version == lua_types::LuaVersion::V53 {
1982 b"invalid format (width or precision too long)".to_vec()
1983 } else {
1984 let mut m = b"invalid conversion specification: '".to_vec();
1985 m.extend_from_slice(form);
1986 m.push(b'\'');
1987 m
1988 };
1989 lua_vm::debug::c_api_runtime(state, msg)
1990}
1991
1992#[derive(Default)]
1994struct FmtSpec {
1995 left_align: bool,
1996 plus_sign: bool,
1997 space_sign: bool,
1998 alt_form: bool,
1999 zero_pad: bool,
2000 width: usize,
2001 precision: Option<usize>,
2002}
2003
2004fn parse_fmt_spec(spec: &[u8]) -> FmtSpec {
2005 let mut s = FmtSpec::default();
2006 let mut i = 0;
2007 while i < spec.len() {
2008 match spec[i] {
2009 b'-' => s.left_align = true,
2010 b'+' => s.plus_sign = true,
2011 b' ' => s.space_sign = true,
2012 b'#' => s.alt_form = true,
2013 b'0' => s.zero_pad = true,
2014 _ => break,
2015 }
2016 i += 1;
2017 }
2018 while i < spec.len() && spec[i].is_ascii_digit() {
2019 s.width = s.width * 10 + (spec[i] - b'0') as usize;
2020 i += 1;
2021 }
2022 if i < spec.len() && spec[i] == b'.' {
2023 i += 1;
2024 let mut p = 0usize;
2025 while i < spec.len() && spec[i].is_ascii_digit() {
2026 p = p * 10 + (spec[i] - b'0') as usize;
2027 i += 1;
2028 }
2029 s.precision = Some(p);
2030 }
2031 s
2032}
2033
2034fn pad_str(buf: &mut Vec<u8>, body: &[u8], spec: &FmtSpec) {
2035 let body = match spec.precision {
2036 Some(p) if body.len() > p => &body[..p],
2037 _ => body,
2038 };
2039 if body.len() >= spec.width {
2040 buf.extend_from_slice(body);
2041 return;
2042 }
2043 let pad = spec.width - body.len();
2044 if spec.left_align {
2045 buf.extend_from_slice(body);
2046 for _ in 0..pad {
2047 buf.push(b' ');
2048 }
2049 } else {
2050 for _ in 0..pad {
2051 buf.push(b' ');
2052 }
2053 buf.extend_from_slice(body);
2054 }
2055}
2056
2057fn pad_int(buf: &mut Vec<u8>, sign_prefix: &[u8], digits: &[u8], spec: &FmtSpec) {
2058 let min_digits = spec.precision.unwrap_or(0);
2059 let zeroes_for_prec = if digits.len() < min_digits {
2060 min_digits - digits.len()
2061 } else {
2062 0
2063 };
2064 let core_len = sign_prefix.len() + zeroes_for_prec + digits.len();
2065 if core_len >= spec.width {
2066 buf.extend_from_slice(sign_prefix);
2067 for _ in 0..zeroes_for_prec {
2068 buf.push(b'0');
2069 }
2070 buf.extend_from_slice(digits);
2071 return;
2072 }
2073 let pad = spec.width - core_len;
2074 let use_zero_pad = spec.zero_pad && !spec.left_align && spec.precision.is_none();
2075 if spec.left_align {
2076 buf.extend_from_slice(sign_prefix);
2077 for _ in 0..zeroes_for_prec {
2078 buf.push(b'0');
2079 }
2080 buf.extend_from_slice(digits);
2081 for _ in 0..pad {
2082 buf.push(b' ');
2083 }
2084 } else if use_zero_pad {
2085 buf.extend_from_slice(sign_prefix);
2086 for _ in 0..pad {
2087 buf.push(b'0');
2088 }
2089 for _ in 0..zeroes_for_prec {
2090 buf.push(b'0');
2091 }
2092 buf.extend_from_slice(digits);
2093 } else {
2094 for _ in 0..pad {
2095 buf.push(b' ');
2096 }
2097 buf.extend_from_slice(sign_prefix);
2098 for _ in 0..zeroes_for_prec {
2099 buf.push(b'0');
2100 }
2101 buf.extend_from_slice(digits);
2102 }
2103}
2104
2105fn signed_int_parts(n: i64, spec: &FmtSpec) -> (Vec<u8>, Vec<u8>) {
2106 if n == 0 && spec.precision == Some(0) {
2107 return (Vec::new(), Vec::new());
2108 }
2109 let (sign, abs_digits) = if n < 0 {
2110 (b"-".to_vec(), {
2111 let u = (n as i128).unsigned_abs();
2112 format!("{}", u).into_bytes()
2113 })
2114 } else {
2115 let s: Vec<u8> = if spec.plus_sign {
2116 b"+".to_vec()
2117 } else if spec.space_sign {
2118 b" ".to_vec()
2119 } else {
2120 Vec::new()
2121 };
2122 (s, format!("{}", n).into_bytes())
2123 };
2124 (sign, abs_digits)
2125}
2126
2127fn unsigned_int_parts(n: u64, base: u32, upper: bool, spec: &FmtSpec) -> (Vec<u8>, Vec<u8>) {
2128 let digits = if n == 0 && spec.precision == Some(0) {
2129 Vec::new()
2130 } else {
2131 match base {
2132 8 => format!("{:o}", n).into_bytes(),
2133 16 if upper => format!("{:X}", n).into_bytes(),
2134 16 => format!("{:x}", n).into_bytes(),
2135 _ => format!("{}", n).into_bytes(),
2136 }
2137 };
2138 let prefix: Vec<u8> = if spec.alt_form && n != 0 {
2139 match base {
2140 8 => b"0".to_vec(),
2141 16 if upper => b"0X".to_vec(),
2142 16 => b"0x".to_vec(),
2143 _ => Vec::new(),
2144 }
2145 } else {
2146 Vec::new()
2147 };
2148 (prefix, digits)
2149}
2150
2151fn format_float(n: f64, conv: u8, spec: &FmtSpec) -> Vec<u8> {
2152 let prec = spec.precision.unwrap_or(6);
2153 if n.is_nan() {
2154 return if conv.is_ascii_uppercase() {
2155 b"NAN".to_vec()
2156 } else {
2157 b"nan".to_vec()
2158 };
2159 }
2160 if n.is_infinite() {
2161 let s: &[u8] = if conv.is_ascii_uppercase() {
2162 if n < 0.0 {
2163 b"-INF"
2164 } else {
2165 b"INF"
2166 }
2167 } else if n < 0.0 {
2168 b"-inf"
2169 } else {
2170 b"inf"
2171 };
2172 return s.to_vec();
2173 }
2174 match conv {
2175 b'f' | b'F' => {
2176 let mut result = format!("{:.*}", prec, n).into_bytes();
2177 if spec.alt_form && !result.contains(&b'.') {
2178 result.push(b'.');
2179 }
2180 result
2181 }
2182 b'e' => format_exp(n, prec, false, spec.alt_form),
2183 b'E' => {
2184 let mut v = format_exp(n, prec, false, spec.alt_form);
2185 for b in v.iter_mut() {
2186 if *b == b'e' {
2187 *b = b'E';
2188 }
2189 }
2190 v
2191 }
2192 b'g' | b'G' => {
2193 let p = if prec == 0 { 1 } else { prec };
2194 let v = format_g(n, p, spec.alt_form);
2195 if conv == b'G' {
2196 v.into_iter()
2197 .map(|b| if b == b'e' { b'E' } else { b })
2198 .collect()
2199 } else {
2200 v
2201 }
2202 }
2203 _ => format!("{}", n).into_bytes(),
2204 }
2205}
2206
2207fn format_exp(n: f64, prec: usize, _upper: bool, alt: bool) -> Vec<u8> {
2213 if n == 0.0 {
2214 let neg = if n.is_sign_negative() { "-" } else { "" };
2215 let mantissa: String = if prec == 0 {
2216 if alt {
2217 "0.".to_string()
2218 } else {
2219 "0".to_string()
2220 }
2221 } else {
2222 format!("0.{}", "0".repeat(prec))
2223 };
2224 return format!("{}{}e+00", neg, mantissa).into_bytes();
2225 }
2226 let abs = n.abs();
2227 let exp = abs.log10().floor() as i32;
2228 let mantissa = n / 10f64.powi(exp);
2229 let mantissa_str = format!("{:.*}", prec, mantissa);
2230 let (mant_final, exp_final) = if let Some(dot_pos) = mantissa_str.find('.') {
2231 let int_part = &mantissa_str[..dot_pos];
2232 let abs_int = int_part.trim_start_matches('-');
2233 if abs_int.len() > 1 {
2234 let new_mant = if prec == 0 {
2235 mantissa_str[..mantissa_str.len() - 1].to_string()
2236 } else {
2237 let neg = if int_part.starts_with('-') { "-" } else { "" };
2238 let frac = &mantissa_str[dot_pos + 1..];
2239 format!("{}{}.{}{}", neg, &abs_int[..1], &abs_int[1..], frac)
2240 };
2241 (new_mant, exp + (abs_int.len() as i32 - 1))
2242 } else {
2243 (mantissa_str, exp)
2244 }
2245 } else if mantissa_str.trim_start_matches('-').len() > 1 {
2246 let neg = if mantissa_str.starts_with('-') {
2247 "-"
2248 } else {
2249 ""
2250 };
2251 let body = mantissa_str.trim_start_matches('-');
2252 let bumped = format!("{}{}.{}", neg, &body[..1], &body[1..]);
2253 (bumped, exp + (body.len() as i32 - 1))
2254 } else {
2255 (mantissa_str, exp)
2256 };
2257 let sign = if exp_final < 0 { '-' } else { '+' };
2258 let mant_out = if alt && !mant_final.contains('.') {
2259 format!("{}.", mant_final)
2260 } else {
2261 mant_final
2262 };
2263 format!("{}e{}{:02}", mant_out, sign, exp_final.abs()).into_bytes()
2264}
2265
2266fn format_g(n: f64, prec: usize, alt: bool) -> Vec<u8> {
2272 if n == 0.0 {
2273 let neg = if n.is_sign_negative() { "-" } else { "" };
2274 return if alt {
2275 format!("{}0.{}", neg, "0".repeat(prec.saturating_sub(1))).into_bytes()
2276 } else {
2277 format!("{}0", neg).into_bytes()
2278 };
2279 }
2280 let abs = n.abs();
2281 let exp = abs.log10().floor() as i32;
2282 if exp < -4 || exp >= prec as i32 {
2283 let ep = if prec == 0 { 0 } else { prec - 1 };
2284 let mut v = format_exp(n, ep, false, alt);
2285 if !alt {
2286 v = strip_trailing_zeros_exp(&v);
2287 }
2288 v
2289 } else {
2290 let dec_places = (prec as i32 - 1 - exp).max(0) as usize;
2291 let mut v = format!("{:.*}", dec_places, n).into_bytes();
2292 if !alt {
2293 v = strip_trailing_zeros_fixed(&v);
2294 }
2295 v
2296 }
2297}
2298
2299fn strip_trailing_zeros_fixed(s: &[u8]) -> Vec<u8> {
2300 if !s.contains(&b'.') {
2301 return s.to_vec();
2302 }
2303 let mut end = s.len();
2304 while end > 0 && s[end - 1] == b'0' {
2305 end -= 1;
2306 }
2307 if end > 0 && s[end - 1] == b'.' {
2308 end -= 1;
2309 }
2310 s[..end].to_vec()
2311}
2312
2313fn strip_trailing_zeros_exp(s: &[u8]) -> Vec<u8> {
2314 let e_pos = match s.iter().position(|&b| b == b'e' || b == b'E') {
2315 Some(p) => p,
2316 None => return s.to_vec(),
2317 };
2318 let mantissa = &s[..e_pos];
2319 let exp_part = &s[e_pos..];
2320 if !mantissa.contains(&b'.') {
2321 let mut out = mantissa.to_vec();
2322 out.extend_from_slice(exp_part);
2323 return out;
2324 }
2325 let mut end = mantissa.len();
2326 while end > 0 && mantissa[end - 1] == b'0' {
2327 end -= 1;
2328 }
2329 if end > 0 && mantissa[end - 1] == b'.' {
2330 end -= 1;
2331 }
2332 let mut out = mantissa[..end].to_vec();
2333 out.extend_from_slice(exp_part);
2334 out
2335}
2336
2337fn format_int_arg(state: &mut LuaState, arg: i32) -> Result<i64, LuaError> {
2350 if state.global().lua_version.number_model() != lua_types::NumberModel::FloatOnly {
2351 return state.check_arg_integer(arg);
2352 }
2353 let n = state.check_arg_number(arg)?;
2354 let t = n.trunc();
2355 if t.is_finite() && (-9223372036854775808.0..=9223372036854775808.0).contains(&t) {
2356 Ok(t as i64)
2357 } else {
2358 Err(LuaError::arg_error(
2359 arg,
2360 "number has no integer representation",
2361 ))
2362 }
2363}
2364
2365fn format_uint_arg(state: &mut LuaState, arg: i32) -> Result<u64, LuaError> {
2379 if state.global().lua_version.number_model() != lua_types::NumberModel::FloatOnly {
2380 return Ok(format_int_arg(state, arg)? as u64);
2381 }
2382 let n = state.check_arg_number(arg)?;
2383 if state.global().lua_version == lua_types::LuaVersion::V52
2384 && !(n >= 0.0 && n <= 18446744073709551616.0)
2385 {
2386 return Err(lua_vm::debug::arg_error_impl(
2387 state,
2388 arg,
2389 b"not a non-negative number in proper range",
2390 ));
2391 }
2392 Ok(n as u64)
2393}
2394
2395pub fn str_format(state: &mut LuaState) -> Result<usize, LuaError> {
2396 let top = state.get_top();
2397 let mut arg = 1i32;
2398 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2399 let mut buf: Vec<u8> = Vec::new();
2400 let mut i = 0usize;
2401
2402 while i < fmt_bytes.len() {
2403 let c = fmt_bytes[i];
2404 if c != L_ESC {
2405 buf.push(c);
2406 i += 1;
2407 continue;
2408 }
2409 i += 1;
2410 if i >= fmt_bytes.len() {
2411 break;
2412 }
2413 if fmt_bytes[i] == L_ESC {
2414 buf.push(L_ESC);
2415 i += 1;
2416 continue;
2417 }
2418
2419 arg += 1;
2421 if arg > top {
2422 return Err(lua_vm::debug::arg_error_impl(state, arg, b"no value"));
2423 }
2424
2425 let spec_start = i - 1; while i < fmt_bytes.len() && b"-+#0 ".contains(&fmt_bytes[i]) {
2429 i += 1;
2430 }
2431 if state.global().lua_version == lua_types::LuaVersion::V53 && i - (spec_start + 1) >= 6 {
2435 return Err(lua_vm::debug::c_api_runtime(
2436 state,
2437 b"invalid format (repeated flags)".to_vec(),
2438 ));
2439 }
2440 if i < fmt_bytes.len() && fmt_bytes[i] != b'0' {
2442 while i < fmt_bytes.len() && fmt_bytes[i].is_ascii_digit() {
2443 i += 1;
2444 }
2445 }
2446 if i < fmt_bytes.len() && fmt_bytes[i] == b'.' {
2448 i += 1;
2449 while i < fmt_bytes.len() && fmt_bytes[i].is_ascii_digit() {
2450 i += 1;
2451 }
2452 }
2453
2454 if i >= fmt_bytes.len() {
2455 let form: Vec<u8> = fmt_bytes[spec_start..].to_vec();
2456 return Err(invalid_conv_spec(state, &form));
2457 }
2458
2459 let conv = fmt_bytes[i];
2460 i += 1;
2461
2462 let spec_slice = &fmt_bytes[spec_start + 1..i - 1];
2463 let form = &fmt_bytes[spec_start..i];
2464
2465 if spec_slice.len() + 1 >= 22 {
2467 return Err(lua_vm::debug::c_api_runtime(
2468 state,
2469 b"invalid format (too long)".to_vec(),
2470 ));
2471 }
2472
2473 let spec = parse_fmt_spec(spec_slice);
2474
2475 match conv {
2476 b'c' => {
2477 check_conv_spec(state, form, FMT_FLAGS_C, false)?;
2478 let n = state.check_arg_integer(arg)?;
2479 let body = vec![n as u8];
2480 pad_str(&mut buf, &body, &spec);
2481 }
2482 b'd' | b'i' => {
2483 check_conv_spec(state, form, FMT_FLAGS_I, true)?;
2484 let n = format_int_arg(state, arg)?;
2485 let (sign, digits) = signed_int_parts(n, &spec);
2486 pad_int(&mut buf, &sign, &digits, &spec);
2487 }
2488 b'u' => {
2489 check_conv_spec(state, form, FMT_FLAGS_U, true)?;
2490 let n = format_uint_arg(state, arg)?;
2491 let (prefix, digits) = unsigned_int_parts(n, 10, false, &spec);
2492 pad_int(&mut buf, &prefix, &digits, &spec);
2493 }
2494 b'o' => {
2495 check_conv_spec(state, form, FMT_FLAGS_X, true)?;
2496 let n = format_uint_arg(state, arg)?;
2497 let (prefix, digits) = unsigned_int_parts(n, 8, false, &spec);
2498 pad_int(&mut buf, &prefix, &digits, &spec);
2499 }
2500 b'x' => {
2501 check_conv_spec(state, form, FMT_FLAGS_X, true)?;
2502 let n = format_uint_arg(state, arg)?;
2503 let (prefix, digits) = unsigned_int_parts(n, 16, false, &spec);
2504 pad_int(&mut buf, &prefix, &digits, &spec);
2505 }
2506 b'X' => {
2507 check_conv_spec(state, form, FMT_FLAGS_X, true)?;
2508 let n = format_uint_arg(state, arg)?;
2509 let (prefix, digits) = unsigned_int_parts(n, 16, true, &spec);
2510 pad_int(&mut buf, &prefix, &digits, &spec);
2511 }
2512 b'a' | b'A' => {
2513 check_conv_spec(state, form, FMT_FLAGS_F, true)?;
2514 let n = state.check_arg_number(arg)?;
2515 let body = format_hex_float(n, spec.precision);
2516 let body: Vec<u8> = if conv == b'A' {
2517 body.into_iter().map(|b| b.to_ascii_uppercase()).collect()
2518 } else {
2519 body
2520 };
2521 let (sign, digits): (Vec<u8>, Vec<u8>) =
2522 if !body.is_empty() && (body[0] == b'-' || body[0] == b'+') {
2523 (vec![body[0]], body[1..].to_vec())
2524 } else if spec.plus_sign {
2525 (b"+".to_vec(), body)
2526 } else if spec.space_sign {
2527 (b" ".to_vec(), body)
2528 } else {
2529 (Vec::new(), body)
2530 };
2531 let no_prec_spec = FmtSpec {
2532 left_align: spec.left_align,
2533 plus_sign: spec.plus_sign,
2534 space_sign: spec.space_sign,
2535 alt_form: spec.alt_form,
2536 zero_pad: spec.zero_pad,
2537 width: spec.width,
2538 precision: None,
2539 };
2540 pad_int(&mut buf, &sign, &digits, &no_prec_spec);
2541 }
2542 b'f' | b'e' | b'E' | b'g' | b'G' => {
2543 check_conv_spec(state, form, FMT_FLAGS_F, true)?;
2544 let n = state.check_arg_number(arg)?;
2545 let body = format_float(n, conv, &spec);
2546 let (sign, digits): (Vec<u8>, Vec<u8>) =
2547 if !body.is_empty() && (body[0] == b'-' || body[0] == b'+') {
2548 (vec![body[0]], body[1..].to_vec())
2549 } else if n >= 0.0 && spec.plus_sign {
2550 (b"+".to_vec(), body)
2551 } else if n >= 0.0 && spec.space_sign {
2552 (b" ".to_vec(), body)
2553 } else {
2554 (Vec::new(), body)
2555 };
2556 let no_prec_spec = FmtSpec {
2557 left_align: spec.left_align,
2558 plus_sign: spec.plus_sign,
2559 space_sign: spec.space_sign,
2560 alt_form: spec.alt_form,
2561 zero_pad: spec.zero_pad,
2562 width: spec.width,
2563 precision: None,
2564 };
2565 pad_int(&mut buf, &sign, &digits, &no_prec_spec);
2566 }
2567 b'p' => {
2568 check_conv_spec(state, form, FMT_FLAGS_C, false)?;
2569 let s: Vec<u8> = match lua_vm::api::to_pointer(state, arg) {
2570 Some(p) => format!("0x{:x}", p).into_bytes(),
2571 None => b"(null)".to_vec(),
2572 };
2573 pad_str(
2574 &mut buf,
2575 &s,
2576 &FmtSpec {
2577 precision: None,
2578 ..spec
2579 },
2580 );
2581 }
2582 b'q' => {
2583 if matches!(
2584 state.global().lua_version,
2585 lua_types::LuaVersion::V51 | lua_types::LuaVersion::V52
2586 ) {
2587 let s = state.check_arg_string(arg)?;
2588 if state.global().lua_version == lua_types::LuaVersion::V51 {
2589 addquoted_51(&mut buf, &s);
2590 } else {
2591 addquoted(&mut buf, &s);
2592 }
2593 } else {
2594 if form.len() > 2 {
2595 return Err(LuaError::runtime(format_args!(
2596 "specifier '%q' cannot have modifiers"
2597 )));
2598 }
2599 addliteral(state, &mut buf, arg)?;
2600 }
2601 }
2602 b's' => {
2603 check_conv_spec(state, form, FMT_FLAGS_C, true)?;
2604 let pushed = matches!(state.global().lua_version, lua_types::LuaVersion::V51);
2605 let s = if pushed {
2606 state.check_arg_string(arg)?
2607 } else {
2608 state.to_display_string(arg)?
2609 };
2610 let has_modifiers = spec.width != 0 || spec.precision.is_some();
2611 if has_modifiers && s.contains(&0u8) {
2612 return Err(lua_vm::debug::arg_error_impl(
2613 state,
2614 arg,
2615 b"string contains zeros",
2616 ));
2617 }
2618 pad_str(&mut buf, &s, &spec);
2619 if !pushed {
2620 state.pop_n(1);
2621 }
2622 }
2623 _ => {
2624 let verb: &[u8] = if state.global().lua_version == lua_types::LuaVersion::V53 {
2625 b"option"
2626 } else {
2627 b"conversion"
2628 };
2629 let mut msg = b"invalid ".to_vec();
2630 msg.extend_from_slice(verb);
2631 msg.extend_from_slice(b" '");
2632 msg.extend_from_slice(form);
2633 msg.extend_from_slice(b"' to 'format'");
2634 return Err(lua_vm::debug::c_api_runtime(state, msg));
2635 }
2636 }
2637 }
2638
2639 state.push_bytes(&buf)?;
2640 Ok(1)
2641}
2642
2643fn is_digit(c: u8) -> bool {
2649 c.is_ascii_digit()
2650}
2651
2652fn getnum(fmt: &[u8], pos: &mut usize, df: i64, wide: bool) -> i64 {
2660 if *pos >= fmt.len() || !is_digit(fmt[*pos]) {
2661 return df;
2662 }
2663 let cap: i64 = if wide { i64::MAX } else { i32::MAX as i64 };
2664 let mut a = 0i64;
2665 while *pos < fmt.len() && is_digit(fmt[*pos]) {
2666 a = a * 10 + (fmt[*pos] - b'0') as i64;
2667 *pos += 1;
2668 if a > (cap - 9) / 10 {
2669 break;
2670 }
2671 }
2672 a
2673}
2674
2675fn getnumlimit(fmt: &[u8], pos: &mut usize, df: i64) -> Result<usize, LuaError> {
2678 let sz = getnum(fmt, pos, df, false);
2679 if sz > MAX_INT_SIZE as i64 || sz <= 0 {
2680 return Err(LuaError::runtime(format_args!(
2681 "integral size ({}) out of limits [1,{}]",
2682 sz, MAX_INT_SIZE
2683 )));
2684 }
2685 Ok(sz as usize)
2686}
2687
2688fn getoption(
2691 h: &mut Header,
2692 fmt: &[u8],
2693 pos: &mut usize,
2694 size: &mut usize,
2695) -> Result<KOption, LuaError> {
2696 const NATIVE_MAX_ALIGN: usize = std::mem::align_of::<f64>();
2698
2699 if *pos >= fmt.len() {
2700 return Ok(KOption::Nop);
2701 }
2702 let opt = fmt[*pos];
2703 *pos += 1;
2704 *size = 0;
2705
2706 match opt {
2707 b'b' => {
2708 *size = 1;
2709 Ok(KOption::Int)
2710 }
2711 b'B' => {
2712 *size = 1;
2713 Ok(KOption::Uint)
2714 }
2715 b'h' => {
2716 *size = 2;
2717 Ok(KOption::Int)
2718 }
2719 b'H' => {
2720 *size = 2;
2721 Ok(KOption::Uint)
2722 }
2723 b'l' => {
2724 *size = 8;
2725 Ok(KOption::Int)
2726 } b'L' => {
2728 *size = 8;
2729 Ok(KOption::Uint)
2730 }
2731 b'j' => {
2732 *size = SZINT;
2733 Ok(KOption::Int)
2734 }
2735 b'J' => {
2736 *size = SZINT;
2737 Ok(KOption::Uint)
2738 }
2739 b'T' => {
2740 *size = std::mem::size_of::<usize>();
2741 Ok(KOption::Uint)
2742 }
2743 b'f' => {
2744 *size = 4;
2745 Ok(KOption::Float)
2746 }
2747 b'n' => {
2748 *size = 8;
2749 Ok(KOption::Number)
2750 } b'd' => {
2752 *size = 8;
2753 Ok(KOption::Double)
2754 } b'i' => {
2756 *size = getnumlimit(fmt, pos, 4)?;
2757 Ok(KOption::Int)
2758 }
2759 b'I' => {
2760 *size = getnumlimit(fmt, pos, 4)?;
2761 Ok(KOption::Uint)
2762 }
2763 b's' => {
2764 *size = getnumlimit(fmt, pos, std::mem::size_of::<usize>() as i64)?;
2765 Ok(KOption::Kstring)
2766 }
2767 b'c' => {
2768 let n = getnum(fmt, pos, -1, h.wide_size);
2769 if n == -1 {
2770 return Err(LuaError::runtime(format_args!(
2771 "missing size for format option 'c'"
2772 )));
2773 }
2774 *size = n as usize;
2775 Ok(KOption::Char)
2776 }
2777 b'z' => Ok(KOption::Zstr),
2778 b'x' => {
2779 *size = 1;
2780 Ok(KOption::Padding)
2781 }
2782 b'X' => Ok(KOption::Paddalign),
2783 b' ' => Ok(KOption::Nop),
2784 b'<' => {
2785 h.is_little = true;
2786 Ok(KOption::Nop)
2787 }
2788 b'>' => {
2789 h.is_little = false;
2790 Ok(KOption::Nop)
2791 }
2792 b'=' => {
2793 h.is_little = cfg!(target_endian = "little");
2794 Ok(KOption::Nop)
2795 }
2796 b'!' => {
2797 let n = getnum(fmt, pos, NATIVE_MAX_ALIGN as i64, false);
2798 h.max_align = getnumlimit(fmt, pos, n)?;
2799 Ok(KOption::Nop)
2800 }
2801 _ => Err(LuaError::runtime(format_args!(
2802 "invalid format option '{}'",
2803 opt as char
2804 ))),
2805 }
2806}
2807
2808fn getdetails(
2811 state: &mut LuaState,
2812 h: &mut Header,
2813 total_size: usize,
2814 fmt: &[u8],
2815 pos: &mut usize,
2816 psize: &mut usize,
2817 ntoalign: &mut usize,
2818) -> Result<KOption, LuaError> {
2819 let opt = getoption(h, fmt, pos, psize)?;
2820 let mut align = *psize;
2821
2822 if opt == KOption::Paddalign {
2823 if *pos >= fmt.len() {
2824 return Err(lua_vm::debug::arg_error_impl(
2825 state,
2826 1,
2827 b"invalid next option for option 'X'",
2828 ));
2829 }
2830 let mut dummy_size = 0usize;
2831 let next_opt = getoption(h, fmt, pos, &mut dummy_size)?;
2832 align = dummy_size;
2833 if next_opt == KOption::Char || align == 0 {
2834 return Err(lua_vm::debug::arg_error_impl(
2835 state,
2836 1,
2837 b"invalid next option for option 'X'",
2838 ));
2839 }
2840 }
2841
2842 if align <= 1 || opt == KOption::Char {
2843 *ntoalign = 0;
2844 } else {
2845 if align > h.max_align {
2846 align = h.max_align;
2847 }
2848 if (align & (align - 1)) != 0 {
2849 return Err(lua_vm::debug::arg_error_impl(
2850 state,
2851 1,
2852 b"format asks for alignment not power of 2",
2853 ));
2854 }
2855 *ntoalign = (align - (total_size & (align - 1))) & (align - 1);
2856 }
2857 Ok(opt)
2858}
2859
2860fn packint(buf: &mut Vec<u8>, mut n: u64, is_little: bool, size: usize, neg: bool) {
2863 let start = buf.len();
2864 buf.resize(start + size, 0);
2865 let slice = &mut buf[start..start + size];
2866 for i in 0..size {
2868 slice[if is_little { i } else { size - 1 - i }] = (n & MC as u64) as u8;
2869 n >>= NB;
2870 }
2871 if neg && size > SZINT {
2873 for i in SZINT..size {
2874 slice[if is_little { i } else { size - 1 - i }] = MC;
2875 }
2876 }
2877}
2878
2879fn copywithendian(dest: &mut [u8], src: &[u8], is_little: bool) {
2882 debug_assert_eq!(dest.len(), src.len());
2883 if is_little == cfg!(target_endian = "little") {
2884 dest.copy_from_slice(src);
2885 } else {
2886 for (d, s) in dest.iter_mut().zip(src.iter().rev()) {
2887 *d = *s;
2888 }
2889 }
2890}
2891
2892fn unpackint(
2895 _state: &LuaState,
2896 data: &[u8],
2897 is_little: bool,
2898 size: usize,
2899 is_signed: bool,
2900) -> Result<i64, LuaError> {
2901 let limit = size.min(SZINT);
2902 let mut res: u64 = 0;
2903 for i in (0..limit).rev() {
2904 res <<= NB;
2905 let byte_idx = if is_little { i } else { size - 1 - i };
2906 res |= data[byte_idx] as u64;
2907 }
2908
2909 if size < SZINT {
2910 if is_signed {
2911 let mask: u64 = 1u64 << (size * NB as usize - 1);
2912 res = (res ^ mask).wrapping_sub(mask);
2913 }
2914 } else if size > SZINT {
2915 let mask = if !is_signed || (res as i64) >= 0 {
2916 0u8
2917 } else {
2918 MC
2919 };
2920 for i in limit..size {
2921 let byte_idx = if is_little { i } else { size - 1 - i };
2922 if data[byte_idx] != mask {
2923 return Err(LuaError::runtime(format_args!(
2924 "{}-byte integer does not fit into Lua Integer",
2925 size
2926 )));
2927 }
2928 }
2929 }
2930 Ok(res as i64)
2931}
2932
2933pub fn str_pack(state: &mut LuaState) -> Result<usize, LuaError> {
2936 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2937 let fmt = &fmt_bytes[..];
2938 let mut h = Header::new(state.global().lua_version == lua_types::LuaVersion::V55);
2939 let mut arg = 1i32;
2940 let mut total_size = 0usize;
2941 let mut buf: Vec<u8> = Vec::new();
2942 let mut pos = 0usize;
2943
2944 while pos < fmt.len() {
2945 let mut size = 0usize;
2946 let mut ntoalign = 0usize;
2947 let opt = getdetails(
2948 state,
2949 &mut h,
2950 total_size,
2951 fmt,
2952 &mut pos,
2953 &mut size,
2954 &mut ntoalign,
2955 )?;
2956 if h.wide_size {
2960 let space = ntoalign + size;
2961 if space > (i64::MAX as usize) || total_size > (i64::MAX as usize) - space {
2962 return Err(lua_vm::debug::arg_error_impl(
2963 state,
2964 arg,
2965 b"result too long",
2966 ));
2967 }
2968 }
2969 total_size += ntoalign + size;
2970 for _ in 0..ntoalign {
2971 buf.push(PACK_PAD_BYTE);
2972 }
2973 arg += 1;
2974
2975 match opt {
2976 KOption::Int => {
2977 let n = state.check_arg_integer(arg)?;
2978 if size < SZINT {
2979 let lim: i64 = 1i64 << (size * NB as usize - 1);
2980 if !(-lim <= n && n < lim) {
2981 return Err(lua_vm::debug::arg_error_impl(
2982 state,
2983 arg,
2984 b"integer overflow",
2985 ));
2986 }
2987 }
2988 packint(&mut buf, n as u64, h.is_little, size, n < 0);
2989 }
2990 KOption::Uint => {
2991 let n = state.check_arg_integer(arg)?;
2992 if size < SZINT {
2993 let lim: u64 = 1u64 << (size * NB as usize);
2994 if (n as u64) >= lim {
2995 return Err(lua_vm::debug::arg_error_impl(
2996 state,
2997 arg,
2998 b"unsigned overflow",
2999 ));
3000 }
3001 }
3002 packint(&mut buf, n as u64, h.is_little, size, false);
3003 }
3004 KOption::Float => {
3005 let f = state.check_arg_number(arg)? as f32;
3006 let start = buf.len();
3007 buf.resize(start + 4, 0);
3008 copywithendian(
3009 &mut buf[start..start + 4],
3010 &f.to_bits().to_ne_bytes(),
3011 h.is_little,
3012 );
3013 }
3014 KOption::Number => {
3015 let f = state.check_arg_number(arg)?;
3016 let start = buf.len();
3017 buf.resize(start + 8, 0);
3018 copywithendian(
3019 &mut buf[start..start + 8],
3020 &f.to_bits().to_ne_bytes(),
3021 h.is_little,
3022 );
3023 }
3024 KOption::Double => {
3025 let f = state.check_arg_number(arg)? as f64;
3026 let start = buf.len();
3027 buf.resize(start + 8, 0);
3028 copywithendian(
3029 &mut buf[start..start + 8],
3030 &f.to_bits().to_ne_bytes(),
3031 h.is_little,
3032 );
3033 }
3034 KOption::Char => {
3035 let s = state.check_arg_string(arg)?.to_vec();
3036 if s.len() > size {
3037 return Err(lua_vm::debug::arg_error_impl(
3038 state,
3039 arg,
3040 b"string longer than given size",
3041 ));
3042 }
3043 buf.extend_from_slice(&s);
3044 let pad = size - s.len();
3045 for _ in 0..pad {
3046 buf.push(PACK_PAD_BYTE);
3047 }
3048 }
3049 KOption::Kstring => {
3050 let s = state.check_arg_string(arg)?.to_vec();
3051 let len = s.len();
3052 if size < SZINT && len >= (1usize << (size * 8)) {
3053 return Err(lua_vm::debug::arg_error_impl(
3054 state,
3055 arg,
3056 b"string length does not fit in given size",
3057 ));
3058 }
3059 packint(&mut buf, len as u64, h.is_little, size, false);
3060 buf.extend_from_slice(&s);
3061 total_size += len;
3062 }
3063 KOption::Zstr => {
3064 let s = state.check_arg_string(arg)?.to_vec();
3065 if s.contains(&0) {
3066 return Err(lua_vm::debug::arg_error_impl(
3067 state,
3068 arg,
3069 b"string contains zeros",
3070 ));
3071 }
3072 buf.extend_from_slice(&s);
3073 buf.push(0);
3074 total_size += s.len() + 1;
3075 }
3076 KOption::Padding => {
3077 buf.push(PACK_PAD_BYTE);
3078 arg -= 1; }
3080 KOption::Paddalign | KOption::Nop => {
3081 arg -= 1; }
3083 }
3084 }
3085
3086 state.push_bytes(&buf)?;
3087 Ok(1)
3088}
3089
3090pub fn str_packsize(state: &mut LuaState) -> Result<usize, LuaError> {
3093 let fmt_bytes = state.check_arg_string(1)?.to_vec();
3094 let fmt = &fmt_bytes[..];
3095 let mut h = Header::new(state.global().lua_version == lua_types::LuaVersion::V55);
3096 let mut total_size = 0usize;
3097 let mut pos = 0usize;
3098
3099 while pos < fmt.len() {
3100 let mut size = 0usize;
3101 let mut ntoalign = 0usize;
3102 let opt = getdetails(
3103 state,
3104 &mut h,
3105 total_size,
3106 fmt,
3107 &mut pos,
3108 &mut size,
3109 &mut ntoalign,
3110 )?;
3111 if opt == KOption::Kstring || opt == KOption::Zstr {
3112 return Err(lua_vm::debug::arg_error_impl(
3113 state,
3114 1,
3115 b"variable-length format",
3116 ));
3117 }
3118 let space = ntoalign + size;
3119 let max_total: usize = if h.wide_size {
3120 i64::MAX as usize
3121 } else {
3122 PACK_MAXSIZE
3123 };
3124 if space > max_total || total_size > max_total - space {
3125 return Err(lua_vm::debug::arg_error_impl(
3126 state,
3127 1,
3128 b"format result too large",
3129 ));
3130 }
3131 total_size += space;
3132 }
3133 state.push(LuaValue::Int(total_size as i64));
3134 Ok(1)
3135}
3136
3137pub fn str_unpack(state: &mut LuaState) -> Result<usize, LuaError> {
3140 let fmt_bytes = state.check_arg_string(1)?.to_vec();
3141 let data_bytes = state.check_arg_string(2)?.to_vec();
3142 let ld = data_bytes.len();
3143 let pos_raw = state.opt_arg_integer(3, 1)?;
3144 let mut pos = if matches!(state.global().lua_version, lua_types::LuaVersion::V53) {
3145 posrelat_53(pos_raw, ld).wrapping_sub(1)
3146 } else {
3147 pos_relat_i(pos_raw, ld).saturating_sub(1)
3148 };
3149
3150 if pos > ld {
3151 return Err(lua_vm::debug::arg_error_impl(
3152 state,
3153 3,
3154 b"initial position out of string",
3155 ));
3156 }
3157
3158 let fmt = &fmt_bytes[..];
3159 let data = &data_bytes[..];
3160 let mut h = Header::new(state.global().lua_version == lua_types::LuaVersion::V55);
3161 let mut fmt_pos = 0usize;
3162 let mut n = 0usize;
3163
3164 while fmt_pos < fmt.len() {
3165 let mut size = 0usize;
3166 let mut ntoalign = 0usize;
3167 let opt = getdetails(
3168 state,
3169 &mut h,
3170 pos,
3171 fmt,
3172 &mut fmt_pos,
3173 &mut size,
3174 &mut ntoalign,
3175 )?;
3176
3177 if ntoalign + size > ld - pos {
3178 return Err(lua_vm::debug::arg_error_impl(
3179 state,
3180 2,
3181 b"data string too short",
3182 ));
3183 }
3184 pos += ntoalign;
3185 state.ensure_stack(2, "too many results")?;
3186 n += 1;
3187
3188 match opt {
3189 KOption::Int => {
3190 let v = unpackint(state, &data[pos..pos + size], h.is_little, size, true)?;
3191 state.push(LuaValue::Int(v));
3192 }
3193 KOption::Uint => {
3194 let v = unpackint(state, &data[pos..pos + size], h.is_little, size, false)?;
3195 state.push(LuaValue::Int(v));
3196 }
3197 KOption::Float => {
3198 let mut bytes = [0u8; 4];
3199 copywithendian(&mut bytes, &data[pos..pos + 4], h.is_little);
3200 let f = f32::from_bits(u32::from_ne_bytes(bytes));
3201 state.push(LuaValue::Float(f as f64));
3202 }
3203 KOption::Number => {
3204 let mut bytes = [0u8; 8];
3205 copywithendian(&mut bytes, &data[pos..pos + 8], h.is_little);
3206 let f = f64::from_bits(u64::from_ne_bytes(bytes));
3207 state.push(LuaValue::Float(f));
3208 }
3209 KOption::Double => {
3210 let mut bytes = [0u8; 8];
3211 copywithendian(&mut bytes, &data[pos..pos + 8], h.is_little);
3212 let f = f64::from_bits(u64::from_ne_bytes(bytes));
3213 state.push(LuaValue::Float(f));
3214 }
3215 KOption::Char => {
3216 state.push_bytes(&data[pos..pos + size])?;
3217 }
3218 KOption::Kstring => {
3219 let len =
3220 unpackint(state, &data[pos..pos + size], h.is_little, size, false)? as usize;
3221 if len > ld - pos - size {
3222 return Err(lua_vm::debug::arg_error_impl(
3223 state,
3224 2,
3225 b"data string too short",
3226 ));
3227 }
3228 state.push_bytes(&data[pos + size..pos + size + len])?;
3229 pos += len;
3230 }
3231 KOption::Zstr => {
3232 let found = data[pos..].iter().position(|&b| b == 0);
3233 let end = match found {
3234 Some(e) => e,
3235 None => {
3236 return Err(lua_vm::debug::arg_error_impl(
3237 state,
3238 2,
3239 b"unfinished string for format 'z'",
3240 ))
3241 }
3242 };
3243 if pos + end >= ld {
3244 return Err(lua_vm::debug::arg_error_impl(
3245 state,
3246 2,
3247 b"unfinished string for format 'z'",
3248 ));
3249 }
3250 state.push_bytes(&data[pos..pos + end])?;
3251 pos += end + 1;
3252 }
3253 KOption::Paddalign | KOption::Padding | KOption::Nop => {
3254 n -= 1; }
3256 }
3257 pos += size;
3258 }
3259
3260 state.push(LuaValue::Int((pos + 1) as i64));
3261 Ok(n + 1)
3262}
3263
3264pub const STRING_LIB: &[(&[u8], lua_CFunction)] = &[
3271 (b"byte", str_byte),
3272 (b"char", str_char),
3273 (b"dump", str_dump),
3274 (b"find", str_find),
3275 (b"format", str_format),
3276 (b"gmatch", gmatch),
3277 (b"gsub", str_gsub),
3278 (b"len", str_len),
3279 (b"lower", str_lower),
3280 (b"match", str_match),
3281 (b"rep", str_rep),
3282 (b"reverse", str_reverse),
3283 (b"sub", str_sub),
3284 (b"upper", str_upper),
3285];
3286
3287const STRING_PACK_LIB: &[(&[u8], lua_CFunction)] = &[
3293 (b"pack", str_pack),
3294 (b"packsize", str_packsize),
3295 (b"unpack", str_unpack),
3296];
3297
3298pub const STRING_META_METHODS: &[(&[u8], lua_CFunction)] = &[
3301 (b"__add", arith_add),
3302 (b"__sub", arith_sub),
3303 (b"__mul", arith_mul),
3304 (b"__mod", arith_mod),
3305 (b"__pow", arith_pow),
3306 (b"__div", arith_div),
3307 (b"__idiv", arith_idiv),
3308 (b"__unm", arith_unm),
3309];
3310
3311pub fn createmetatable(state: &mut LuaState) -> Result<(), LuaError> {
3314 state.new_lib_table(STRING_META_METHODS)?;
3315 state.set_funcs(STRING_META_METHODS, 0)?;
3316 state.push_string(b"")?;
3317 let mt_idx = state.top_idx() - 2;
3318 let mt = state.get_at(mt_idx);
3319 state.push(mt);
3320 state.set_metatable(-2)?;
3321 state.pop_n(1);
3322 let strlib_idx = state.top_idx() - 2;
3323 let strlib = state.get_at(strlib_idx);
3324 state.push(strlib);
3325 state.set_field(-2, b"__index")?;
3326 state.pop_n(1);
3327 Ok(())
3328}
3329
3330pub fn luaopen_string(state: &mut LuaState) -> Result<usize, LuaError> {
3333 state.new_lib(STRING_LIB)?;
3334 if matches!(state.global().lua_version, lua_types::LuaVersion::V51) {
3339 state.push_c_function(gmatch)?;
3340 state.set_field(-2, b"gfind")?;
3341 }
3342 if state.global().lua_version != lua_types::LuaVersion::V51
3343 && state.global().lua_version != lua_types::LuaVersion::V52
3344 {
3345 for (name, f) in STRING_PACK_LIB {
3346 state.push_c_function(*f)?;
3347 state.set_field(-2, name)?;
3348 }
3349 }
3350 createmetatable(state)?;
3351 Ok(1)
3352}
3353
3354