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