1use std::any::Any;
14use std::cell::RefCell;
15use std::rc::Rc;
16
17use crate::state_stub::{lua_CFunction, upvalue_index, LuaState, LuaStateStubExt as _};
18use lua_types::arith::ArithOp;
19use lua_types::error::LuaError;
20use lua_types::value::LuaValue;
21use lua_types::LuaType;
22
23const LUA_MAX_CAPTURES: usize = 32;
28
29const MAX_CC_CALLS: i32 = 200;
30
31const L_ESC: u8 = b'%';
32
33const SPECIALS: &[u8] = b"^$*+?.([%-";
34
35const CAP_UNFINISHED: isize = -1;
36
37const CAP_POSITION: isize = -2;
38
39#[expect(
40 dead_code,
41 reason = "ported stdlib helper; not yet wired into the runtime"
42)]
43const MAX_ITEM: usize = 120;
44
45#[expect(
46 dead_code,
47 reason = "ported stdlib helper; not yet wired into the runtime"
48)]
49const MAX_ITEM_F: usize = 418;
50
51#[expect(
52 dead_code,
53 reason = "ported stdlib helper; not yet wired into the runtime"
54)]
55const MAX_FORMAT: usize = 32;
56
57const MAX_INT_SIZE: usize = 16;
58
59const PACK_MAXSIZE: usize = i32::MAX as usize;
63
64const NB: u32 = 8;
65
66const MC: u8 = 0xFF;
67
68const SZINT: usize = 8; const PACK_PAD_BYTE: u8 = 0x00;
71
72#[derive(Copy, Clone)]
81struct Capture {
82 init: usize,
84 len: isize,
86}
87
88impl Default for Capture {
89 fn default() -> Self {
90 Capture {
91 init: 0,
92 len: CAP_UNFINISHED,
93 }
94 }
95}
96
97struct MatchState<'a> {
101 src: &'a [u8],
103 pat: &'a [u8],
105 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 fn new(src: &'a [u8], pat: &'a [u8], step_limit: u64) -> Self {
125 MatchState {
126 src,
127 pat,
128 matchdepth: MAX_CC_CALLS,
129 level: 0,
130 captures: [Capture::default(); LUA_MAX_CAPTURES],
131 steps: 0,
132 step_limit,
133 aborted: false,
134 }
135 }
136
137 fn reset_level(&mut self) {
138 self.level = 0;
139 debug_assert!(self.matchdepth == MAX_CC_CALLS);
140 }
141}
142
143struct GMatchIterState {
144 pos: usize,
146 last_match: Option<usize>,
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157enum KOption {
158 Int, Uint, Float, Number, Double, Char, Kstring, Zstr, Padding, Paddalign, Nop, }
170
171struct Header {
174 is_little: bool,
175 max_align: usize,
176 wide_size: bool,
180}
181
182impl Header {
183 fn new(wide_size: bool) -> Self {
184 Header {
185 is_little: cfg!(target_endian = "little"),
186 max_align: 1,
187 wide_size,
188 }
189 }
190}
191
192fn pos_relat_i(pos: i64, len: usize) -> usize {
200 if pos > 0 {
201 pos as usize
202 } else if pos == 0 {
203 1
204 } else if pos < -(len as i64) {
205 1
206 } else {
207 len.wrapping_add(pos as usize).wrapping_add(1)
208 }
209}
210
211fn posrelat_53(pos: i64, len: usize) -> usize {
218 if pos >= 0 {
219 pos as usize
220 } else if (pos as i128).unsigned_abs() > len as u128 {
221 0
222 } else {
223 (len as i64 + pos + 1) as usize
224 }
225}
226
227fn get_end_pos(pos: i64, len: usize) -> usize {
231 if pos > len as i64 {
232 len
233 } else if pos >= 0 {
234 pos as usize
235 } else if pos < -(len as i64) {
236 0
237 } else {
238 len.wrapping_add(pos as usize).wrapping_add(1)
239 }
240}
241
242pub fn str_len(state: &mut LuaState) -> Result<usize, LuaError> {
253 let l = match state.to_lua_string_len(1) {
254 Some(n) => n,
255 None => {
256 state.check_arg_string(1)?;
257 unreachable!("check_arg_string raises when arg #1 is not a string");
258 }
259 };
260 state.push(LuaValue::Int(l as i64));
261 Ok(1)
262}
263
264pub fn str_sub(state: &mut LuaState) -> Result<usize, LuaError> {
272 let s_ref = match state.to_lua_string(1) {
273 Some(r) => r,
274 None => {
275 state.check_arg_string(1)?;
276 unreachable!("check_arg_string raises when arg #1 is not a string");
277 }
278 };
279 let s: &[u8] = s_ref.as_bytes();
280 let l = s.len();
281 let start = pos_relat_i(state.check_arg_integer(2)?, l);
282 let end_pos_raw = state.opt_arg_integer(3, -1)?;
283 let end = get_end_pos(end_pos_raw, l);
284 if start <= end {
285 let slice = &s[(start - 1)..end];
286 state.push_string(slice)?;
287 } else {
288 state.push_string(b"")?;
289 }
290 Ok(1)
291}
292
293pub fn str_reverse(state: &mut LuaState) -> Result<usize, LuaError> {
299 let s_ref = match state.to_lua_string(1) {
300 Some(r) => r,
301 None => {
302 state.check_arg_string(1)?;
303 unreachable!("check_arg_string raises when arg #1 is not a string");
304 }
305 };
306 let s: &[u8] = s_ref.as_bytes();
307 let buf: Vec<u8> = s.iter().copied().rev().collect();
308 state.push_bytes(&buf)?;
309 Ok(1)
310}
311
312pub fn str_lower(state: &mut LuaState) -> Result<usize, LuaError> {
318 let s_ref = match state.to_lua_string(1) {
319 Some(r) => r,
320 None => {
321 state.check_arg_string(1)?;
322 unreachable!("check_arg_string raises when arg #1 is not a string");
323 }
324 };
325 let s: &[u8] = s_ref.as_bytes();
326 let buf: Vec<u8> = s.iter().map(|&c| c.to_ascii_lowercase()).collect();
327 state.push_bytes(&buf)?;
328 Ok(1)
329}
330
331pub fn str_upper(state: &mut LuaState) -> Result<usize, LuaError> {
338 let s_ref = match state.to_lua_string(1) {
339 Some(r) => r,
340 None => {
341 state.check_arg_string(1)?;
342 unreachable!("check_arg_string raises when arg #1 is not a string");
343 }
344 };
345 let s: &[u8] = s_ref.as_bytes();
346 let buf: Vec<u8> = s.iter().map(|&c| c.to_ascii_uppercase()).collect();
347 state.push_bytes(&buf)?;
348 Ok(1)
349}
350
351pub fn str_rep(state: &mut LuaState) -> Result<usize, LuaError> {
358 let s_ref = match state.to_lua_string(1) {
359 Some(r) => r,
360 None => {
361 state.check_arg_string(1)?;
362 unreachable!("check_arg_string raises when arg #1 is not a string");
363 }
364 };
365 let s: &[u8] = s_ref.as_bytes();
366 let l = s.len();
367 let n = state.check_arg_integer(2)?;
368 let sep_owned = state.opt_arg_string(3, b"")?;
369 let sep: &[u8] = &sep_owned;
370 let lsep = sep.len();
371
372 if n <= 0 {
373 state.push_string(b"")?;
374 } else {
375 const MAXSIZE: usize = i32::MAX as usize;
376 let per = l
377 .checked_add(lsep)
378 .ok_or_else(|| LuaError::runtime(format_args!("resulting string too large")))?;
379 if per > MAXSIZE / (n as usize) {
380 return Err(LuaError::runtime(format_args!(
381 "resulting string too large"
382 )));
383 }
384 let total = per * (n as usize) - lsep;
385
386 if let Some(err) = state.sandbox_reserve(total) {
387 return Err(err);
388 }
389
390 let mut buf: Vec<u8> = Vec::with_capacity(total);
391 for i in 0..(n as usize) {
392 buf.extend_from_slice(s);
393 if i < (n as usize - 1) && lsep > 0 {
394 buf.extend_from_slice(sep);
395 }
396 }
397 state.push_bytes(&buf)?;
398 }
399 Ok(1)
400}
401
402pub fn str_byte(state: &mut LuaState) -> Result<usize, LuaError> {
411 let s_ref = match state.to_lua_string(1) {
412 Some(r) => r,
413 None => {
414 state.check_arg_string(1)?;
415 unreachable!("check_arg_string raises when arg #1 is not a string");
416 }
417 };
418 let s: &[u8] = s_ref.as_bytes();
419 let l = s.len();
420 let pi = state.opt_arg_integer(2, 1)?;
421 let posi = pos_relat_i(pi, l);
422 let pose_raw = state.opt_arg_integer(3, pi)?;
423 let pose = get_end_pos(pose_raw, l);
424
425 if posi > pose {
426 return Ok(0);
427 }
428 let count = pose.saturating_sub(posi - 1) + 1;
429 if count > i32::MAX as usize {
430 return Err(LuaError::runtime(format_args!("string slice too long")));
431 }
432 let n = (pose - posi + 1) as usize;
433 state.ensure_stack(n as i32, "string slice too long")?;
434
435 for i in 0..n {
436 state.push(LuaValue::Int(s[posi - 1 + i] as i64));
437 }
438 Ok(n)
439}
440
441pub fn str_char(state: &mut LuaState) -> Result<usize, LuaError> {
444 let n = state.get_top();
445 let mut buf = Vec::with_capacity(n as usize);
446 for i in 1..=n {
447 let c = state.check_arg_integer(i)? as u64;
448 if c > u8::MAX as u64 {
449 return Err(lua_vm::debug::arg_error_impl(
450 state,
451 i,
452 b"value out of range",
453 ));
454 }
455 buf.push(c as u8);
456 }
457 state.push_bytes(&buf)?;
458 Ok(1)
459}
460
461pub fn str_dump(state: &mut LuaState) -> Result<usize, LuaError> {
465 state.check_arg_type(1, LuaType::Function)?;
466 let strip = state.arg_to_bool(2);
467 lua_vm::api::set_top(state, 1)?;
470 let bytes = state
474 .dump_function(strip)
475 .map_err(|_| LuaError::runtime(format_args!("unable to dump given function")))?;
476 state.push_bytes(&bytes)?;
477 Ok(1)
478}
479
480fn tonum(state: &mut LuaState, arg: i32) -> Result<bool, LuaError> {
488 if state.type_at(arg) == LuaType::Number {
489 state.push_value_at(arg)?;
490 Ok(true)
491 } else {
492 if let Some(s) = state.to_lua_string_bytes(arg) {
495 let len = s.len();
496 let pushed = state.string_to_number_push(&s)?;
498 let ok = pushed == len + 1;
499 if ok
504 && matches!(
505 state.global().lua_version,
506 lua_types::LuaVersion::V51
507 | lua_types::LuaVersion::V52
508 | lua_types::LuaVersion::V53
509 )
510 {
511 if let Some(f) = lua_vm::api::to_number_x(state, -1) {
512 state.pop();
513 state.push(LuaValue::Float(f));
514 }
515 }
516 Ok(ok)
517 } else {
518 Ok(false)
519 }
520 }
521}
522
523fn trymt(state: &mut LuaState, mtname: &[u8]) -> Result<(), LuaError> {
526 lua_vm::api::set_top(state, 2)?;
530 let t2_is_string = state.type_at(2) == LuaType::String;
531 if t2_is_string || !state.get_meta_field(2, mtname)? {
537 let op = &mtname[2..]; let msg = format!(
539 "attempt to {} a '{}' with a '{}'",
540 op.escape_ascii(),
541 state.type_name_at(-2).escape_ascii(),
542 state.type_name_at(-1).escape_ascii(),
543 );
544 return crate::auxlib::lua_error(state, msg.as_bytes()).map(|_| ());
545 }
546 state.insert(-3)?;
547 state.call(2, 1)?;
548 Ok(())
549}
550
551fn arith(state: &mut LuaState, op: ArithOp, mtname: &[u8]) -> Result<usize, LuaError> {
554 if tonum(state, 1)? && tonum(state, 2)? {
555 state.arith(op)?;
556 } else {
557 trymt(state, mtname)?;
558 }
559 Ok(1)
560}
561
562pub fn arith_add(state: &mut LuaState) -> Result<usize, LuaError> {
563 arith(state, ArithOp::Add, b"__add")
564}
565pub fn arith_sub(state: &mut LuaState) -> Result<usize, LuaError> {
566 arith(state, ArithOp::Sub, b"__sub")
567}
568pub fn arith_mul(state: &mut LuaState) -> Result<usize, LuaError> {
569 arith(state, ArithOp::Mul, b"__mul")
570}
571pub fn arith_mod(state: &mut LuaState) -> Result<usize, LuaError> {
572 arith(state, ArithOp::Mod, b"__mod")
573}
574pub fn arith_pow(state: &mut LuaState) -> Result<usize, LuaError> {
575 arith(state, ArithOp::Pow, b"__pow")
576}
577pub fn arith_div(state: &mut LuaState) -> Result<usize, LuaError> {
578 arith(state, ArithOp::Div, b"__div")
579}
580pub fn arith_idiv(state: &mut LuaState) -> Result<usize, LuaError> {
581 arith(state, ArithOp::Idiv, b"__idiv")
582}
583pub fn arith_unm(state: &mut LuaState) -> Result<usize, LuaError> {
584 arith(state, ArithOp::Unm, b"__unm")
585}
586
587#[inline(always)]
594fn match_class(c: u8, cl: u8) -> bool {
595 let res = match cl.to_ascii_lowercase() {
596 b'a' => c.is_ascii_alphabetic(),
597 b'c' => c.is_ascii_control(),
598 b'd' => c.is_ascii_digit(),
599 b'g' => c.is_ascii_graphic(),
600 b'l' => c.is_ascii_lowercase(),
601 b'p' => c.is_ascii_punctuation(),
602 b's' => c.is_ascii_whitespace(),
603 b'u' => c.is_ascii_uppercase(),
604 b'w' => c.is_ascii_alphanumeric(),
605 b'x' => c.is_ascii_hexdigit(),
606 b'z' => c == 0,
607 _ => return cl == c,
608 };
609 if cl.is_ascii_lowercase() {
610 res
611 } else {
612 !res
613 }
614}
615
616#[inline]
620fn matchbracketclass(pat: &[u8], c: u8, mut p: usize, ec: usize) -> bool {
621 let sig = if p + 1 < pat.len() && pat[p + 1] == b'^' {
622 p += 1; false
624 } else {
625 true
626 };
627 p += 1; while p < ec {
629 if pat[p] == L_ESC {
630 p += 1;
631 if p < ec && match_class(c, pat[p]) {
632 return sig;
633 }
634 } else if p + 1 < ec && pat[p + 1] == b'-' && p + 2 < ec {
635 let lo = pat[p];
636 p += 2;
637 let hi = pat[p];
638 if lo <= c && c <= hi {
639 return sig;
640 }
641 } else if pat[p] == c {
642 return sig;
643 }
644 p += 1;
645 }
646 !sig
647}
648
649#[inline(always)]
653fn singlematch(ms: &MatchState, s: usize, p: usize, ep: usize) -> bool {
654 if s >= ms.src.len() {
655 return false;
656 }
657 let c = ms.src[s];
658 match ms.pat[p] {
659 b'.' => true,
660 L_ESC => match_class(c, ms.pat[p + 1]),
661 b'[' => matchbracketclass(ms.pat, c, p, ep - 1),
662 pc => pc == c,
663 }
664}
665
666#[inline(always)]
670fn classend(ms: &MatchState, p: usize) -> Result<usize, LuaError> {
671 let pat = ms.pat;
672 match pat.get(p).copied() {
673 Some(L_ESC) => {
674 if p + 1 >= pat.len() {
675 return Err(LuaError::runtime(format_args!(
676 "malformed pattern (ends with '%')"
677 )));
678 }
679 Ok(p + 2)
680 }
681 Some(b'[') => {
682 let mut q = p + 1;
683 if q < pat.len() && pat[q] == b'^' {
684 q += 1;
685 }
686 loop {
687 if q >= pat.len() {
688 return Err(LuaError::runtime(format_args!(
689 "malformed pattern (missing ']')"
690 )));
691 }
692 let ch = pat[q];
693 q += 1;
694 if ch == L_ESC && q < pat.len() {
695 q += 1;
696 }
697 if q < pat.len() && pat[q] == b']' {
698 return Ok(q + 1);
699 }
700 }
701 }
702 Some(_) => Ok(p + 1),
703 None => Ok(p),
704 }
705}
706
707fn check_capture(ms: &MatchState, l: u8) -> Result<usize, LuaError> {
711 let signed = (l as i32) - (b'1' as i32);
712 if signed < 0 || signed >= ms.level as i32 || ms.captures[signed as usize].len == CAP_UNFINISHED
713 {
714 return Err(LuaError::runtime(format_args!(
715 "invalid capture index %{}",
716 signed + 1
717 )));
718 }
719 Ok(signed as usize)
720}
721
722fn capture_to_close(ms: &MatchState) -> Result<usize, LuaError> {
725 let mut level = ms.level as usize;
726 while level > 0 {
727 level -= 1;
728 if ms.captures[level].len == CAP_UNFINISHED {
729 return Ok(level);
730 }
731 }
732 Err(LuaError::runtime(format_args!("invalid pattern capture")))
733}
734
735fn matchbalance(ms: &MatchState, s: usize, p: usize) -> Result<Option<usize>, LuaError> {
739 if p + 1 >= ms.pat.len() {
740 return Err(LuaError::runtime(format_args!(
741 "malformed pattern (missing arguments to '%b')"
742 )));
743 }
744 let b = ms.pat[p];
745 let e = ms.pat[p + 1];
746 if s >= ms.src.len() || ms.src[s] != b {
747 return Ok(None);
748 }
749 let mut cont = 1i32;
750 let mut s = s + 1;
751 while s < ms.src.len() {
752 if ms.src[s] == e {
753 cont -= 1;
754 if cont == 0 {
755 return Ok(Some(s + 1));
756 }
757 } else if ms.src[s] == b {
758 cont += 1;
759 }
760 s += 1;
761 }
762 Ok(None)
763}
764
765fn max_expand(
768 ms: &mut MatchState,
769 s: usize,
770 p: usize,
771 ep: usize,
772) -> Result<Option<usize>, LuaError> {
773 let mut count: isize = 0;
774 while singlematch(ms, s + count as usize, p, ep) {
775 count += 1;
776 }
777 while count >= 0 {
778 let res = match_pat(ms, s + count as usize, ep + 1)?;
779 if res.is_some() {
780 return Ok(res);
781 }
782 count -= 1;
783 }
784 Ok(None)
785}
786
787fn min_expand(
790 ms: &mut MatchState,
791 mut s: usize,
792 p: usize,
793 ep: usize,
794) -> Result<Option<usize>, LuaError> {
795 loop {
796 let res = match_pat(ms, s, ep + 1)?;
797 if res.is_some() {
798 return Ok(res);
799 } else if singlematch(ms, s, p, ep) {
800 s += 1;
801 } else {
802 return Ok(None);
803 }
804 }
805}
806
807fn start_capture(
810 ms: &mut MatchState,
811 s: usize,
812 p: usize,
813 what: isize,
814) -> Result<Option<usize>, LuaError> {
815 let level = ms.level as usize;
816 if level >= LUA_MAX_CAPTURES {
817 return Err(LuaError::runtime(format_args!("too many captures")));
818 }
819 ms.captures[level].init = s;
820 ms.captures[level].len = what;
821 ms.level += 1;
822 let res = match_pat(ms, s, p)?;
823 if res.is_none() {
824 ms.level -= 1; }
826 Ok(res)
827}
828
829fn end_capture(ms: &mut MatchState, s: usize, p: usize) -> Result<Option<usize>, LuaError> {
832 let l = capture_to_close(ms)?;
833 ms.captures[l].len = (s - ms.captures[l].init) as isize;
834 let res = match_pat(ms, s, p)?;
835 if res.is_none() {
836 ms.captures[l].len = CAP_UNFINISHED; }
838 Ok(res)
839}
840
841fn match_capture(ms: &MatchState, s: usize, l: u8) -> Result<Option<usize>, LuaError> {
844 let idx = check_capture(ms, l)?;
845 let cap_len = ms.captures[idx].len as usize;
846 let cap_init = ms.captures[idx].init;
847 if ms.src.len() - s >= cap_len
848 && &ms.src[s..s + cap_len] == &ms.src[cap_init..cap_init + cap_len]
849 {
850 Ok(Some(s + cap_len))
851 } else {
852 Ok(None)
853 }
854}
855
856fn match_pat(ms: &mut MatchState, mut s: usize, mut p: usize) -> Result<Option<usize>, LuaError> {
861 if ms.aborted {
862 return Ok(None);
863 }
864 ms.steps += 1;
865 if ms.step_limit != 0 && ms.steps > ms.step_limit {
866 ms.aborted = true;
867 return Ok(None);
868 }
869 ms.matchdepth -= 1;
870 if ms.matchdepth < 0 {
871 ms.matchdepth = 0;
872 return Err(LuaError::runtime(format_args!("pattern too complex")));
873 }
874
875 let result = 'outer: loop {
877 if p >= ms.pat.len() {
878 break 'outer Ok(Some(s));
880 }
881
882 match ms.pat[p] {
883 b'(' => {
884 let s2 = if p + 1 < ms.pat.len() && ms.pat[p + 1] == b')' {
885 start_capture(ms, s, p + 2, CAP_POSITION)?
887 } else {
888 start_capture(ms, s, p + 1, CAP_UNFINISHED)?
889 };
890 break 'outer Ok(s2);
891 }
892 b')' => {
893 let s2 = end_capture(ms, s, p + 1)?;
894 break 'outer Ok(s2);
895 }
896 b'$' => {
897 if p + 1 != ms.pat.len() {
898 let ep = classend(ms, p)?;
900 let s2 = handle_class_with_suffix(ms, s, p, ep)?;
901 break 'outer Ok(s2);
902 }
903 break 'outer Ok(if s == ms.src.len() { Some(s) } else { None });
904 }
905 L_ESC => {
906 match ms.pat.get(p + 1).copied().unwrap_or(0) {
907 b'b' => {
908 let s2 = matchbalance(ms, s, p + 2)?;
909 if let Some(ns) = s2 {
910 s = ns;
911 p += 4;
912 continue 'outer; }
914 break 'outer Ok(None);
915 }
916 b'f' => {
917 p += 2;
918 if ms.pat.get(p).copied() != Some(b'[') {
919 return Err(LuaError::runtime(format_args!(
920 "missing '[' after '%f' in pattern"
921 )));
922 }
923 let ep = classend(ms, p)?;
924 let previous = if s == 0 { 0u8 } else { ms.src[s - 1] };
925 let current = ms.src.get(s).copied().unwrap_or(0);
926 if !matchbracketclass(ms.pat, previous, p, ep - 1)
927 && matchbracketclass(ms.pat, current, p, ep - 1)
928 {
929 p = ep;
930 continue 'outer; }
932 break 'outer Ok(None);
933 }
934 c @ b'0'..=b'9' => {
935 let s2 = match_capture(ms, s, c)?;
936 if let Some(ns) = s2 {
937 s = ns;
938 p += 2;
939 continue 'outer; }
941 break 'outer Ok(None);
942 }
943 _ => {
944 let ep = classend(ms, p)?;
946 let s2 = handle_class_with_suffix(ms, s, p, ep)?;
947 break 'outer Ok(s2);
948 }
949 }
950 }
951 _ => {
952 let ep = classend(ms, p)?;
954 let s2 = handle_class_with_suffix(ms, s, p, ep)?;
955 break 'outer Ok(s2);
956 }
957 }
958 };
959
960 ms.matchdepth += 1;
961 result
962}
963
964#[inline(always)]
969fn handle_class_with_suffix(
970 ms: &mut MatchState,
971 s: usize,
972 p: usize,
973 ep: usize,
974) -> Result<Option<usize>, LuaError> {
975 let matched_once = singlematch(ms, s, p, ep);
976 if !matched_once {
977 match ms.pat.get(ep).copied() {
979 Some(b'*') | Some(b'?') | Some(b'-') => {
980 return match_pat(ms, s, ep + 1);
984 }
985 _ => return Ok(None),
986 }
987 }
988
989 match ms.pat.get(ep).copied() {
991 Some(b'?') => {
992 let res = match_pat(ms, s + 1, ep + 1)?;
994 if res.is_some() {
995 Ok(res)
996 } else {
997 match_pat(ms, s, ep + 1)
998 }
999 }
1000 Some(b'+') => {
1001 max_expand(ms, s + 1, p, ep)
1003 }
1004 Some(b'*') => {
1005 max_expand(ms, s, p, ep)
1007 }
1008 Some(b'-') => {
1009 min_expand(ms, s, p, ep)
1011 }
1012 _ => {
1013 match_pat(ms, s + 1, ep)
1015 }
1016 }
1017}
1018
1019fn lmemfind(haystack: &[u8], needle: &[u8]) -> Option<usize> {
1027 if needle.is_empty() {
1028 return Some(0);
1029 }
1030 if needle.len() > haystack.len() {
1031 return None;
1032 }
1033 let first = needle[0];
1034 let rest = &needle[1..];
1035 let limit = haystack.len() - rest.len();
1036 let mut s = 0;
1037 while s <= limit {
1038 if let Some(pos) = haystack[s..].iter().position(|&b| b == first) {
1039 let pos = s + pos;
1040 if pos + 1 + rest.len() <= haystack.len()
1041 && &haystack[pos + 1..pos + 1 + rest.len()] == rest
1042 {
1043 return Some(pos);
1044 }
1045 s = pos + 1;
1046 } else {
1047 break;
1048 }
1049 }
1050 None
1051}
1052
1053fn required_start_byte(pat: &[u8]) -> Option<u8> {
1054 let (byte, ep) = match pat.first().copied()? {
1055 L_ESC => {
1056 let escaped = *pat.get(1)?;
1057 if escaped.is_ascii_alphanumeric() {
1058 return None;
1059 }
1060 (escaped, 2)
1061 }
1062 c if !SPECIALS.contains(&c) => (c, 1),
1063 _ => return None,
1064 };
1065 match pat.get(ep).copied() {
1066 Some(b'*') | Some(b'?') | Some(b'-') => None,
1067 _ => Some(byte),
1068 }
1069}
1070
1071fn next_start_with_byte(src: &[u8], pos: usize, byte: u8) -> Option<usize> {
1072 src.get(pos..)?
1073 .iter()
1074 .position(|&c| c == byte)
1075 .map(|offset| pos + offset)
1076}
1077
1078fn nospecials(pat: &[u8]) -> bool {
1081 !pat.iter().any(|b| SPECIALS.contains(b))
1082}
1083
1084enum CaptureInfo<'a> {
1086 Position(i64),
1088 Bytes(&'a [u8]),
1090}
1091
1092fn get_one_capture<'a>(
1096 ms: &'a MatchState,
1097 i: usize,
1098 s: usize,
1099 e: usize,
1100) -> Result<CaptureInfo<'a>, LuaError> {
1101 if i >= ms.level as usize {
1102 if i != 0 {
1103 return Err(LuaError::runtime(format_args!(
1104 "invalid capture index %{}",
1105 i + 1
1106 )));
1107 }
1108 return Ok(CaptureInfo::Bytes(&ms.src[s..e]));
1110 }
1111 let cap = &ms.captures[i];
1112 if cap.len == CAP_UNFINISHED {
1113 return Err(LuaError::runtime(format_args!("unfinished capture")));
1114 }
1115 if cap.len == CAP_POSITION {
1116 return Ok(CaptureInfo::Position((cap.init + 1) as i64));
1117 }
1118 let len = cap.len as usize;
1119 Ok(CaptureInfo::Bytes(&ms.src[cap.init..cap.init + len]))
1120}
1121
1122fn push_captures(
1130 state: &mut LuaState,
1131 ms: &MatchState,
1132 span: Option<(usize, usize)>,
1133) -> Result<usize, LuaError> {
1134 let nlevels = if ms.level == 0 && span.is_some() {
1135 1
1136 } else {
1137 ms.level as usize
1138 };
1139 state.ensure_stack(nlevels as i32, "too many captures")?;
1140 let (s, e) = span.unwrap_or((0, 0));
1141 for i in 0..nlevels {
1142 match get_one_capture(ms, i, s, e)? {
1143 CaptureInfo::Position(n) => state.push(LuaValue::Int(n)),
1144 CaptureInfo::Bytes(b) => state.push_bytes(b)?,
1145 }
1146 }
1147 Ok(nlevels)
1148}
1149
1150fn str_find_aux(state: &mut LuaState, find: bool) -> Result<usize, LuaError> {
1157 let s_ref = match state.to_lua_string(1) {
1158 Some(r) => r,
1159 None => {
1160 state.check_arg_string(1)?;
1161 unreachable!("check_arg_string raises when arg #1 is not a string");
1162 }
1163 };
1164 let p_ref = match state.to_lua_string(2) {
1165 Some(r) => r,
1166 None => {
1167 state.check_arg_string(2)?;
1168 unreachable!("check_arg_string raises when arg #2 is not a string");
1169 }
1170 };
1171 let s: &[u8] = s_ref.as_bytes();
1172 let p: &[u8] = p_ref.as_bytes();
1173 let ls = s.len();
1174 let lp = p.len();
1175 let init_raw = state.opt_arg_integer(3, 1)?;
1176 let init = pos_relat_i(init_raw, ls).saturating_sub(1);
1177
1178 if init > ls {
1179 state.push(LuaValue::Nil);
1180 return Ok(1);
1181 }
1182
1183 if find && (state.arg_to_bool(4) || nospecials(p)) {
1184 if let Some(pos) = lmemfind(&s[init..], p) {
1186 let abs = init + pos;
1187 state.push(LuaValue::Int((abs + 1) as i64));
1188 state.push(LuaValue::Int((abs + lp) as i64));
1189 return Ok(2);
1190 }
1191 } else {
1192 let step_limit = state.sandbox_match_step_limit();
1193 let mut ms = MatchState::new(s, p, step_limit);
1194 let anchor = p.first() == Some(&b'^');
1195 let p_slice = if anchor { &p[1..] } else { p };
1196 ms.pat = p_slice;
1197 let start_byte = if anchor {
1198 None
1199 } else {
1200 required_start_byte(ms.pat)
1201 };
1202
1203 let mut s1 = init;
1204 let mut matched: Option<usize> = None;
1205 loop {
1206 if let Some(byte) = start_byte {
1207 let Some(next) = next_start_with_byte(ms.src, s1, byte) else {
1208 break;
1209 };
1210 s1 = next;
1211 }
1212 ms.reset_level();
1213 if let Some(res) = match_pat(&mut ms, s1, 0)? {
1214 matched = Some(res);
1215 break;
1216 }
1217 if ms.aborted || s1 >= ms.src.len() || anchor {
1218 break;
1219 }
1220 s1 += 1;
1221 }
1222
1223 if let Some(err) = state.sandbox_charge(ms.steps) {
1224 return Err(err);
1225 }
1226
1227 if let Some(res) = matched {
1228 if find {
1229 state.push(LuaValue::Int((s1 + 1) as i64));
1230 state.push(LuaValue::Int(res as i64));
1231 let nc = push_captures(state, &ms, None)?;
1232 return Ok(nc + 2);
1233 } else {
1234 return push_captures(state, &ms, Some((s1, res)));
1235 }
1236 }
1237 }
1238
1239 state.push(LuaValue::Nil);
1240 Ok(1)
1241}
1242
1243pub fn str_find(state: &mut LuaState) -> Result<usize, LuaError> {
1246 str_find_aux(state, true)
1247}
1248
1249pub fn str_match(state: &mut LuaState) -> Result<usize, LuaError> {
1252 str_find_aux(state, false)
1253}
1254
1255pub fn gmatch_aux(state: &mut LuaState) -> Result<usize, LuaError> {
1263 let s_val = state.value_at(upvalue_index(1));
1264 let p_val = state.value_at(upvalue_index(2));
1265 let (LuaValue::Str(s_str), LuaValue::Str(p_str)) = (&s_val, &p_val) else {
1266 return Ok(0);
1267 };
1268 let iter_val = state.value_at(upvalue_index(3));
1269 let LuaValue::UserData(iter_ud) = iter_val else {
1270 return Ok(0);
1271 };
1272 let Some(host) = iter_ud.host_value() else {
1273 return Ok(0);
1274 };
1275 let Ok(iter_state) = host.downcast::<RefCell<GMatchIterState>>() else {
1276 return Ok(0);
1277 };
1278
1279 let s: &[u8] = s_str.as_bytes();
1280 let p: &[u8] = p_str.as_bytes();
1281 let (start_pos, last_match) = {
1282 let iter = iter_state.borrow();
1283 (iter.pos, iter.last_match)
1284 };
1285
1286 let ls = s.len();
1287
1288 let step_limit = state.sandbox_match_step_limit();
1289 let mut ms = MatchState::new(s, p, step_limit);
1290 let start_byte = required_start_byte(p);
1291
1292 let mut src = start_pos;
1293 let mut hit: Option<(usize, usize)> = None;
1294 while src <= ls {
1295 if let Some(byte) = start_byte {
1296 let Some(next) = next_start_with_byte(s, src, byte) else {
1297 break;
1298 };
1299 src = next;
1300 }
1301 ms.reset_level();
1302 if let Some(e) = match_pat(&mut ms, src, 0)? {
1303 if Some(e) != last_match {
1304 hit = Some((src, e));
1305 break;
1306 }
1307 }
1308 if ms.aborted {
1309 break;
1310 }
1311 src += 1;
1312 }
1313
1314 if let Some(err) = state.sandbox_charge(ms.steps) {
1315 return Err(err);
1316 }
1317
1318 if let Some((src, e)) = hit {
1319 {
1320 let mut iter = iter_state.borrow_mut();
1321 iter.pos = e;
1322 iter.last_match = Some(e);
1323 }
1324 return push_captures(state, &ms, Some((src, e)));
1325 }
1326
1327 Ok(0)
1328}
1329
1330pub fn gmatch(state: &mut LuaState) -> Result<usize, LuaError> {
1337 let s_ref = match state.to_lua_string(1) {
1338 Some(r) => r,
1339 None => {
1340 state.check_arg_string(1)?;
1341 unreachable!("check_arg_string raises when arg #1 is not a string");
1342 }
1343 };
1344 let ls = s_ref.len();
1345 match state.to_lua_string(2) {
1346 Some(_) => {}
1347 None => {
1348 state.check_arg_string(2)?;
1349 unreachable!("check_arg_string raises when arg #2 is not a string");
1350 }
1351 };
1352 let init_raw = state.opt_arg_integer(3, 1)?;
1353 let mut init = pos_relat_i(init_raw, ls).saturating_sub(1);
1354 if init > ls {
1355 init = ls + 1;
1356 }
1357
1358 lua_vm::api::set_top(state, 2)?;
1359
1360 state.push_value_at(1)?;
1361 state.push_value_at(2)?;
1362 let iter_ud = state.new_userdata_typed(b"string.gmatch.state", 0, 0)?;
1363 let iter_state: Rc<dyn Any> = Rc::new(RefCell::new(GMatchIterState {
1364 pos: init,
1365 last_match: None,
1366 }));
1367 iter_ud.set_host_value(Some(iter_state));
1368
1369 state.push_c_closure(gmatch_aux, 3)?;
1370 Ok(1)
1371}
1372
1373fn add_s(
1376 state: &mut LuaState,
1377 ms: &MatchState,
1378 buf: &mut Vec<u8>,
1379 s: usize,
1380 e: usize,
1381) -> Result<(), LuaError> {
1382 let news_bytes = state.to_lua_string_bytes(3).unwrap_or_default();
1383 let mut i = 0usize;
1384 while i < news_bytes.len() {
1385 if news_bytes[i] != L_ESC {
1386 buf.push(news_bytes[i]);
1387 i += 1;
1388 } else {
1389 i += 1; if i >= news_bytes.len() {
1391 break;
1392 }
1393 let c = news_bytes[i];
1394 if c == L_ESC {
1395 buf.push(L_ESC);
1396 } else if c == b'0' {
1397 buf.extend_from_slice(&ms.src[s..e]);
1398 } else if c.is_ascii_digit() {
1399 match get_one_capture(ms, (c - b'1') as usize, s, e)? {
1400 CaptureInfo::Position(n) => {
1401 let formatted = format!("{}", n).into_bytes();
1403 buf.extend_from_slice(&formatted);
1404 }
1405 CaptureInfo::Bytes(b) => {
1406 buf.extend_from_slice(b);
1407 }
1408 }
1409 } else {
1410 return Err(LuaError::runtime(format_args!(
1411 "invalid use of '{}' in replacement string",
1412 L_ESC as char
1413 )));
1414 }
1415 i += 1;
1416 }
1417 }
1418 Ok(())
1419}
1420
1421fn add_value(
1425 state: &mut LuaState,
1426 ms: &MatchState,
1427 buf: &mut Vec<u8>,
1428 s: usize,
1429 e: usize,
1430 tr: LuaType,
1431) -> Result<bool, LuaError> {
1432 match tr {
1433 LuaType::Function => {
1434 state.push_value_at(3)?;
1435 let n = push_captures(state, ms, Some((s, e)))?;
1436 state.call(n as i32, 1)?;
1437 }
1438 LuaType::Table => {
1439 match get_one_capture(ms, 0, s, e)? {
1440 CaptureInfo::Position(n) => state.push(LuaValue::Int(n)),
1441 CaptureInfo::Bytes(b) => state.push_bytes(b)?,
1442 }
1443 state.get_table(3)?;
1444 }
1445 _ => {
1446 add_s(state, ms, buf, s, e)?;
1448 return Ok(true);
1449 }
1450 }
1451
1452 let top_bool = state.arg_to_bool(-1);
1453 if !top_bool {
1454 state.pop_n(1);
1455 buf.extend_from_slice(&ms.src[s..e]);
1456 return Ok(false);
1457 }
1458 if state.type_at(-1) != LuaType::String {
1459 let tname = state.type_name_at(-1).to_owned();
1460 return Err(LuaError::runtime(format_args!(
1461 "invalid replacement value (a {})",
1462 tname.escape_ascii()
1463 )));
1464 }
1465 let v = state.to_bytes(-1).unwrap_or_default();
1466 state.pop();
1467 buf.extend_from_slice(&v);
1468 Ok(true)
1469}
1470
1471pub fn str_gsub(state: &mut LuaState) -> Result<usize, LuaError> {
1474 let src_ref = match state.to_lua_string(1) {
1475 Some(r) => r,
1476 None => {
1477 state.check_arg_string(1)?;
1478 unreachable!("check_arg_string raises when arg #1 is not a string");
1479 }
1480 };
1481 let pat_ref = match state.to_lua_string(2) {
1482 Some(r) => r,
1483 None => {
1484 state.check_arg_string(2)?;
1485 unreachable!("check_arg_string raises when arg #2 is not a string");
1486 }
1487 };
1488 let src: &[u8] = src_ref.as_bytes();
1489 let pat: &[u8] = pat_ref.as_bytes();
1490 let src_len = src.len();
1491 let max_s = state.opt_arg_integer(4, (src_len + 1) as i64)?;
1492 let tr = state.type_at(3);
1493
1494 if !matches!(
1495 tr,
1496 LuaType::Number | LuaType::String | LuaType::Function | LuaType::Table
1497 ) {
1498 let v = state.arg(3);
1499 return Err(LuaError::type_arg_error(3, "string/function/table", &v));
1500 }
1501
1502 let anchor = pat.first() == Some(&b'^');
1503 let pat_slice = if anchor { &pat[1..] } else { pat };
1504
1505 let step_limit = state.sandbox_match_step_limit();
1506 let mut ms = MatchState::new(src, pat_slice, step_limit);
1507 let start_byte = if anchor {
1508 None
1509 } else {
1510 required_start_byte(ms.pat)
1511 };
1512 let mut buf: Vec<u8> = Vec::with_capacity(src_len);
1513 let mut src_pos = 0usize;
1514 let mut last_match: Option<usize> = None;
1515 let mut n: i64 = 0;
1516 let mut changed = false;
1517
1518 while n < max_s {
1519 if let Some(byte) = start_byte {
1520 let Some(next) = next_start_with_byte(ms.src, src_pos, byte) else {
1521 buf.extend_from_slice(&ms.src[src_pos..]);
1522 src_pos = ms.src.len();
1523 break;
1524 };
1525 if next > src_pos {
1526 buf.extend_from_slice(&ms.src[src_pos..next]);
1527 src_pos = next;
1528 }
1529 }
1530 ms.reset_level();
1531 let maybe_e = match_pat(&mut ms, src_pos, 0)?;
1532 if let Some(e) = maybe_e {
1533 if last_match != Some(e) {
1534 n += 1;
1535 let delta = add_value(state, &ms, &mut buf, src_pos, e, tr)?;
1536 changed |= delta;
1537 src_pos = e;
1538 last_match = Some(e);
1539 } else if src_pos < ms.src.len() {
1540 buf.push(ms.src[src_pos]);
1541 src_pos += 1;
1542 } else {
1543 break;
1544 }
1545 } else if src_pos < ms.src.len() {
1546 buf.push(ms.src[src_pos]);
1547 src_pos += 1;
1548 } else {
1549 break;
1550 }
1551 if ms.aborted || anchor {
1552 break;
1553 }
1554 }
1555
1556 if let Some(err) = state.sandbox_charge(ms.steps) {
1557 return Err(err);
1558 }
1559
1560 if !changed {
1561 state.push_value_at(1)?;
1562 } else {
1563 buf.extend_from_slice(&ms.src[src_pos..]);
1564 state.push_bytes(&buf)?;
1565 }
1566 state.push(LuaValue::Int(n));
1567 Ok(2)
1568}
1569
1570fn adddigit(buf: &mut Vec<u8>, x: f64) -> f64 {
1577 let dd = x.floor();
1578 let d = dd as i32;
1579 let c = if d < 10 {
1580 b'0' + d as u8
1581 } else {
1582 b'a' + (d - 10) as u8
1583 };
1584 buf.push(c);
1585 x - dd
1586}
1587
1588fn num2straux(x: f64) -> Vec<u8> {
1593 format_hex_float(x, None)
1594}
1595
1596fn format_hex_float(x: f64, precision: Option<usize>) -> Vec<u8> {
1604 if x.is_nan() {
1605 return b"nan".to_vec();
1606 }
1607 if x.is_infinite() {
1608 return if x < 0.0 {
1609 b"-inf".to_vec()
1610 } else {
1611 b"inf".to_vec()
1612 };
1613 }
1614 if x == 0.0 {
1615 let sign: &[u8] = if x.is_sign_negative() { b"-" } else { b"" };
1616 return match precision {
1617 None => [sign, b"0x0p+0"].concat(),
1618 Some(0) => [sign, b"0x0p+0"].concat(),
1619 Some(p) => {
1620 let zeros = "0".repeat(p);
1621 [sign, b"0x0.", zeros.as_bytes(), b"p+0"].concat()
1622 }
1623 };
1624 }
1625
1626 let (m_raw, exp) = frexp(x);
1627 let mut buf: Vec<u8> = Vec::new();
1628 let mut m = m_raw;
1629 if m < 0.0 {
1630 buf.push(b'-');
1631 m = -m;
1632 }
1633 buf.extend_from_slice(b"0x");
1634
1635 let nbfd = 1;
1636 m = adddigit(&mut buf, m * (1 << nbfd) as f64);
1637 let e = exp - nbfd;
1638
1639 match precision {
1640 None => {
1641 if m > 0.0 {
1642 buf.push(b'.');
1643 while m > 0.0 {
1644 m = adddigit(&mut buf, m * 16.0);
1645 }
1646 }
1647 }
1648 Some(0) => {}
1649 Some(p) => {
1650 buf.push(b'.');
1651 for _ in 0..p {
1652 if m > 0.0 {
1653 m = adddigit(&mut buf, m * 16.0);
1654 } else {
1655 buf.push(b'0');
1656 }
1657 }
1658 }
1659 }
1660
1661 let exp_str = format!("p{:+}", e);
1662 buf.extend_from_slice(exp_str.as_bytes());
1663 buf
1664}
1665
1666fn frexp(x: f64) -> (f64, i32) {
1671 if x == 0.0 || x.is_nan() || x.is_infinite() {
1672 return (x, 0);
1673 }
1674 let bits = x.to_bits();
1675 let sign_bit = bits & 0x8000_0000_0000_0000u64;
1676 let exp_bits = ((bits >> 52) & 0x7FF) as i32;
1677 if exp_bits == 0 {
1678 let (m, e) = frexp(x * (1u64 << 52) as f64);
1679 return (m, e - 52);
1680 }
1681 let exp = exp_bits - 1022;
1682 let mantissa_bits = sign_bit | (bits & 0x000F_FFFF_FFFF_FFFF) | 0x3FE0_0000_0000_0000;
1683 (f64::from_bits(mantissa_bits), exp)
1684}
1685
1686fn quotefloat(n: f64) -> Vec<u8> {
1689 if n == f64::INFINITY {
1690 return b"1e9999".to_vec();
1691 } else if n == f64::NEG_INFINITY {
1692 return b"-1e9999".to_vec();
1693 } else if n.is_nan() {
1694 return b"(0/0)".to_vec();
1695 }
1696 let buf = num2straux(n);
1698 if !buf.contains(&b'.') && !buf.contains(&b'p') {
1699 }
1702 buf
1703}
1704
1705fn addquoted(buf: &mut Vec<u8>, s: &[u8]) {
1708 buf.push(b'"');
1709 for (idx, &c) in s.iter().enumerate() {
1710 if c == b'"' || c == b'\\' || c == b'\n' {
1711 buf.push(b'\\');
1712 buf.push(c);
1713 } else if c.is_ascii_control() {
1714 let next_is_digit = s.get(idx + 1).map_or(false, |n| n.is_ascii_digit());
1715 let formatted = if next_is_digit {
1716 format!("\\{:03}", c)
1717 } else {
1718 format!("\\{}", c)
1719 };
1720 buf.extend_from_slice(formatted.as_bytes());
1721 } else {
1722 buf.push(c);
1723 }
1724 }
1725 buf.push(b'"');
1726}
1727
1728fn addliteral(state: &mut LuaState, buf: &mut Vec<u8>, arg: i32) -> Result<(), LuaError> {
1731 match state.type_at(arg) {
1732 LuaType::String => {
1733 let s = state.check_arg_string(arg)?.to_vec();
1734 addquoted(buf, &s);
1735 }
1736 LuaType::Number => {
1737 if state.is_integer(arg) {
1738 let n = state.to_integer(arg).unwrap_or(0);
1739 let formatted = if n == i64::MIN {
1740 format!("0x{:016x}", n as u64)
1741 } else {
1742 format!("{}", n)
1743 };
1744 buf.extend_from_slice(formatted.as_bytes());
1745 } else {
1746 let n = state.to_number(arg).unwrap_or(0.0);
1747 let hex = quotefloat(n);
1748 buf.extend_from_slice(&hex);
1749 }
1750 }
1751 LuaType::Nil => {
1752 buf.extend_from_slice(b"nil");
1753 }
1754 LuaType::Boolean => {
1755 buf.extend_from_slice(if state.to_boolean(arg) {
1756 b"true"
1757 } else {
1758 b"false"
1759 });
1760 }
1761 _ => {
1762 return Err(LuaError::arg_error(arg, "value has no literal form"));
1763 }
1764 }
1765 Ok(())
1766}
1767
1768const FMT_FLAGS_F: &[u8] = b"-+#0 ";
1770const FMT_FLAGS_X: &[u8] = b"-#0";
1771const FMT_FLAGS_I: &[u8] = b"-+0 ";
1772const FMT_FLAGS_U: &[u8] = b"-0";
1773const FMT_FLAGS_C: &[u8] = b"-";
1774
1775fn check_conv_spec(
1785 state: &mut LuaState,
1786 form: &[u8],
1787 flags: &[u8],
1788 allow_precision: bool,
1789) -> Result<(), LuaError> {
1790 let mut i = 1usize; while i < form.len() && flags.contains(&form[i]) {
1792 i += 1;
1793 }
1794 if i < form.len() && form[i] == b'0' {
1795 return Err(invalid_conv_spec(state, form));
1796 }
1797 if i < form.len() && form[i].is_ascii_digit() {
1798 i += 1;
1799 if i < form.len() && form[i].is_ascii_digit() {
1800 i += 1;
1801 }
1802 }
1803 if allow_precision && i < form.len() && form[i] == b'.' {
1804 i += 1;
1805 if i < form.len() && form[i].is_ascii_digit() {
1806 i += 1;
1807 if i < form.len() && form[i].is_ascii_digit() {
1808 i += 1;
1809 }
1810 }
1811 }
1812 if i != form.len() - 1 {
1813 return Err(invalid_conv_spec(state, form));
1814 }
1815 Ok(())
1816}
1817
1818fn invalid_conv_spec(state: &mut LuaState, form: &[u8]) -> LuaError {
1825 let msg: Vec<u8> = if state.global().lua_version == lua_types::LuaVersion::V53 {
1826 b"invalid format (width or precision too long)".to_vec()
1827 } else {
1828 let mut m = b"invalid conversion specification: '".to_vec();
1829 m.extend_from_slice(form);
1830 m.push(b'\'');
1831 m
1832 };
1833 lua_vm::debug::c_api_runtime(state, msg)
1834}
1835
1836#[derive(Default)]
1838struct FmtSpec {
1839 left_align: bool,
1840 plus_sign: bool,
1841 space_sign: bool,
1842 alt_form: bool,
1843 zero_pad: bool,
1844 width: usize,
1845 precision: Option<usize>,
1846}
1847
1848fn parse_fmt_spec(spec: &[u8]) -> FmtSpec {
1849 let mut s = FmtSpec::default();
1850 let mut i = 0;
1851 while i < spec.len() {
1852 match spec[i] {
1853 b'-' => s.left_align = true,
1854 b'+' => s.plus_sign = true,
1855 b' ' => s.space_sign = true,
1856 b'#' => s.alt_form = true,
1857 b'0' => s.zero_pad = true,
1858 _ => break,
1859 }
1860 i += 1;
1861 }
1862 while i < spec.len() && spec[i].is_ascii_digit() {
1863 s.width = s.width * 10 + (spec[i] - b'0') as usize;
1864 i += 1;
1865 }
1866 if i < spec.len() && spec[i] == b'.' {
1867 i += 1;
1868 let mut p = 0usize;
1869 while i < spec.len() && spec[i].is_ascii_digit() {
1870 p = p * 10 + (spec[i] - b'0') as usize;
1871 i += 1;
1872 }
1873 s.precision = Some(p);
1874 }
1875 s
1876}
1877
1878fn pad_str(buf: &mut Vec<u8>, body: &[u8], spec: &FmtSpec) {
1879 let body = match spec.precision {
1880 Some(p) if body.len() > p => &body[..p],
1881 _ => body,
1882 };
1883 if body.len() >= spec.width {
1884 buf.extend_from_slice(body);
1885 return;
1886 }
1887 let pad = spec.width - body.len();
1888 if spec.left_align {
1889 buf.extend_from_slice(body);
1890 for _ in 0..pad {
1891 buf.push(b' ');
1892 }
1893 } else {
1894 for _ in 0..pad {
1895 buf.push(b' ');
1896 }
1897 buf.extend_from_slice(body);
1898 }
1899}
1900
1901fn pad_int(buf: &mut Vec<u8>, sign_prefix: &[u8], digits: &[u8], spec: &FmtSpec) {
1902 let min_digits = spec.precision.unwrap_or(0);
1903 let zeroes_for_prec = if digits.len() < min_digits {
1904 min_digits - digits.len()
1905 } else {
1906 0
1907 };
1908 let core_len = sign_prefix.len() + zeroes_for_prec + digits.len();
1909 if core_len >= spec.width {
1910 buf.extend_from_slice(sign_prefix);
1911 for _ in 0..zeroes_for_prec {
1912 buf.push(b'0');
1913 }
1914 buf.extend_from_slice(digits);
1915 return;
1916 }
1917 let pad = spec.width - core_len;
1918 let use_zero_pad = spec.zero_pad && !spec.left_align && spec.precision.is_none();
1919 if spec.left_align {
1920 buf.extend_from_slice(sign_prefix);
1921 for _ in 0..zeroes_for_prec {
1922 buf.push(b'0');
1923 }
1924 buf.extend_from_slice(digits);
1925 for _ in 0..pad {
1926 buf.push(b' ');
1927 }
1928 } else if use_zero_pad {
1929 buf.extend_from_slice(sign_prefix);
1930 for _ in 0..pad {
1931 buf.push(b'0');
1932 }
1933 for _ in 0..zeroes_for_prec {
1934 buf.push(b'0');
1935 }
1936 buf.extend_from_slice(digits);
1937 } else {
1938 for _ in 0..pad {
1939 buf.push(b' ');
1940 }
1941 buf.extend_from_slice(sign_prefix);
1942 for _ in 0..zeroes_for_prec {
1943 buf.push(b'0');
1944 }
1945 buf.extend_from_slice(digits);
1946 }
1947}
1948
1949fn signed_int_parts(n: i64, spec: &FmtSpec) -> (Vec<u8>, Vec<u8>) {
1950 if n == 0 && spec.precision == Some(0) {
1951 return (Vec::new(), Vec::new());
1952 }
1953 let (sign, abs_digits) = if n < 0 {
1954 (b"-".to_vec(), {
1955 let u = (n as i128).unsigned_abs();
1956 format!("{}", u).into_bytes()
1957 })
1958 } else {
1959 let s: Vec<u8> = if spec.plus_sign {
1960 b"+".to_vec()
1961 } else if spec.space_sign {
1962 b" ".to_vec()
1963 } else {
1964 Vec::new()
1965 };
1966 (s, format!("{}", n).into_bytes())
1967 };
1968 (sign, abs_digits)
1969}
1970
1971fn unsigned_int_parts(n: u64, base: u32, upper: bool, spec: &FmtSpec) -> (Vec<u8>, Vec<u8>) {
1972 let digits = if n == 0 && spec.precision == Some(0) {
1973 Vec::new()
1974 } else {
1975 match base {
1976 8 => format!("{:o}", n).into_bytes(),
1977 16 if upper => format!("{:X}", n).into_bytes(),
1978 16 => format!("{:x}", n).into_bytes(),
1979 _ => format!("{}", n).into_bytes(),
1980 }
1981 };
1982 let prefix: Vec<u8> = if spec.alt_form && n != 0 {
1983 match base {
1984 8 => b"0".to_vec(),
1985 16 if upper => b"0X".to_vec(),
1986 16 => b"0x".to_vec(),
1987 _ => Vec::new(),
1988 }
1989 } else {
1990 Vec::new()
1991 };
1992 (prefix, digits)
1993}
1994
1995fn format_float(n: f64, conv: u8, spec: &FmtSpec) -> Vec<u8> {
1996 let prec = spec.precision.unwrap_or(6);
1997 if n.is_nan() {
1998 return if conv.is_ascii_uppercase() {
1999 b"NAN".to_vec()
2000 } else {
2001 b"nan".to_vec()
2002 };
2003 }
2004 if n.is_infinite() {
2005 let s: &[u8] = if conv.is_ascii_uppercase() {
2006 if n < 0.0 {
2007 b"-INF"
2008 } else {
2009 b"INF"
2010 }
2011 } else if n < 0.0 {
2012 b"-inf"
2013 } else {
2014 b"inf"
2015 };
2016 return s.to_vec();
2017 }
2018 match conv {
2019 b'f' | b'F' => {
2020 let mut result = format!("{:.*}", prec, n).into_bytes();
2021 if spec.alt_form && !result.contains(&b'.') {
2022 result.push(b'.');
2023 }
2024 result
2025 }
2026 b'e' => format_exp(n, prec, false, spec.alt_form),
2027 b'E' => {
2028 let mut v = format_exp(n, prec, false, spec.alt_form);
2029 for b in v.iter_mut() {
2030 if *b == b'e' {
2031 *b = b'E';
2032 }
2033 }
2034 v
2035 }
2036 b'g' | b'G' => {
2037 let p = if prec == 0 { 1 } else { prec };
2038 let v = format_g(n, p, spec.alt_form);
2039 if conv == b'G' {
2040 v.into_iter()
2041 .map(|b| if b == b'e' { b'E' } else { b })
2042 .collect()
2043 } else {
2044 v
2045 }
2046 }
2047 _ => format!("{}", n).into_bytes(),
2048 }
2049}
2050
2051fn format_exp(n: f64, prec: usize, _upper: bool, alt: bool) -> Vec<u8> {
2052 if n == 0.0 {
2053 let mantissa: String = if prec == 0 {
2054 if alt {
2055 "0.".to_string()
2056 } else {
2057 "0".to_string()
2058 }
2059 } else {
2060 format!("0.{}", "0".repeat(prec))
2061 };
2062 return format!("{}e+00", mantissa).into_bytes();
2063 }
2064 let abs = n.abs();
2065 let exp = abs.log10().floor() as i32;
2066 let mantissa = n / 10f64.powi(exp);
2067 let mantissa_str = format!("{:.*}", prec, mantissa);
2068 let (mant_final, exp_final) = if let Some(dot_pos) = mantissa_str.find('.') {
2069 let int_part = &mantissa_str[..dot_pos];
2070 let abs_int = int_part.trim_start_matches('-');
2071 if abs_int.len() > 1 {
2072 let new_mant = if prec == 0 {
2073 mantissa_str[..mantissa_str.len() - 1].to_string()
2074 } else {
2075 let neg = if int_part.starts_with('-') { "-" } else { "" };
2076 let frac = &mantissa_str[dot_pos + 1..];
2077 format!("{}{}.{}{}", neg, &abs_int[..1], &abs_int[1..], frac)
2078 };
2079 (new_mant, exp + (abs_int.len() as i32 - 1))
2080 } else {
2081 (mantissa_str, exp)
2082 }
2083 } else if mantissa_str.trim_start_matches('-').len() > 1 {
2084 let neg = if mantissa_str.starts_with('-') {
2085 "-"
2086 } else {
2087 ""
2088 };
2089 let body = mantissa_str.trim_start_matches('-');
2090 let bumped = format!("{}{}.{}", neg, &body[..1], &body[1..]);
2091 (bumped, exp + (body.len() as i32 - 1))
2092 } else {
2093 (mantissa_str, exp)
2094 };
2095 let sign = if exp_final < 0 { '-' } else { '+' };
2096 let mant_out = if alt && !mant_final.contains('.') {
2097 format!("{}.", mant_final)
2098 } else {
2099 mant_final
2100 };
2101 format!("{}e{}{:02}", mant_out, sign, exp_final.abs()).into_bytes()
2102}
2103
2104fn format_g(n: f64, prec: usize, alt: bool) -> Vec<u8> {
2105 if n == 0.0 {
2106 return if alt {
2107 format!("0.{}", "0".repeat(prec.saturating_sub(1))).into_bytes()
2108 } else {
2109 b"0".to_vec()
2110 };
2111 }
2112 let abs = n.abs();
2113 let exp = abs.log10().floor() as i32;
2114 if exp < -4 || exp >= prec as i32 {
2115 let ep = if prec == 0 { 0 } else { prec - 1 };
2116 let mut v = format_exp(n, ep, false, alt);
2117 if !alt {
2118 v = strip_trailing_zeros_exp(&v);
2119 }
2120 v
2121 } else {
2122 let dec_places = (prec as i32 - 1 - exp).max(0) as usize;
2123 let mut v = format!("{:.*}", dec_places, n).into_bytes();
2124 if !alt {
2125 v = strip_trailing_zeros_fixed(&v);
2126 }
2127 v
2128 }
2129}
2130
2131fn strip_trailing_zeros_fixed(s: &[u8]) -> Vec<u8> {
2132 if !s.contains(&b'.') {
2133 return s.to_vec();
2134 }
2135 let mut end = s.len();
2136 while end > 0 && s[end - 1] == b'0' {
2137 end -= 1;
2138 }
2139 if end > 0 && s[end - 1] == b'.' {
2140 end -= 1;
2141 }
2142 s[..end].to_vec()
2143}
2144
2145fn strip_trailing_zeros_exp(s: &[u8]) -> Vec<u8> {
2146 let e_pos = match s.iter().position(|&b| b == b'e' || b == b'E') {
2147 Some(p) => p,
2148 None => return s.to_vec(),
2149 };
2150 let mantissa = &s[..e_pos];
2151 let exp_part = &s[e_pos..];
2152 if !mantissa.contains(&b'.') {
2153 let mut out = mantissa.to_vec();
2154 out.extend_from_slice(exp_part);
2155 return out;
2156 }
2157 let mut end = mantissa.len();
2158 while end > 0 && mantissa[end - 1] == b'0' {
2159 end -= 1;
2160 }
2161 if end > 0 && mantissa[end - 1] == b'.' {
2162 end -= 1;
2163 }
2164 let mut out = mantissa[..end].to_vec();
2165 out.extend_from_slice(exp_part);
2166 out
2167}
2168
2169fn format_int_arg(state: &mut LuaState, arg: i32) -> Result<i64, LuaError> {
2182 if state.global().lua_version.number_model() != lua_types::NumberModel::FloatOnly {
2183 return state.check_arg_integer(arg);
2184 }
2185 let n = state.check_arg_number(arg)?;
2186 let t = n.trunc();
2187 if t.is_finite() && (-9223372036854775808.0..=9223372036854775808.0).contains(&t) {
2188 Ok(t as i64)
2189 } else {
2190 Err(LuaError::arg_error(
2191 arg,
2192 "number has no integer representation",
2193 ))
2194 }
2195}
2196
2197pub fn str_format(state: &mut LuaState) -> Result<usize, LuaError> {
2198 let top = state.get_top();
2199 let mut arg = 1i32;
2200 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2201 let mut buf: Vec<u8> = Vec::new();
2202 let mut i = 0usize;
2203
2204 while i < fmt_bytes.len() {
2205 let c = fmt_bytes[i];
2206 if c != L_ESC {
2207 buf.push(c);
2208 i += 1;
2209 continue;
2210 }
2211 i += 1;
2212 if i >= fmt_bytes.len() {
2213 break;
2214 }
2215 if fmt_bytes[i] == L_ESC {
2216 buf.push(L_ESC);
2217 i += 1;
2218 continue;
2219 }
2220
2221 arg += 1;
2223 if arg > top {
2224 return Err(lua_vm::debug::arg_error_impl(state, arg, b"no value"));
2225 }
2226
2227 let spec_start = i - 1; while i < fmt_bytes.len() && b"-+#0 ".contains(&fmt_bytes[i]) {
2231 i += 1;
2232 }
2233 if state.global().lua_version == lua_types::LuaVersion::V53 && i - (spec_start + 1) >= 6 {
2237 return Err(lua_vm::debug::c_api_runtime(
2238 state,
2239 b"invalid format (repeated flags)".to_vec(),
2240 ));
2241 }
2242 if i < fmt_bytes.len() && fmt_bytes[i] != b'0' {
2244 while i < fmt_bytes.len() && fmt_bytes[i].is_ascii_digit() {
2245 i += 1;
2246 }
2247 }
2248 if i < fmt_bytes.len() && fmt_bytes[i] == b'.' {
2250 i += 1;
2251 while i < fmt_bytes.len() && fmt_bytes[i].is_ascii_digit() {
2252 i += 1;
2253 }
2254 }
2255
2256 if i >= fmt_bytes.len() {
2257 let form: Vec<u8> = fmt_bytes[spec_start..].to_vec();
2258 return Err(invalid_conv_spec(state, &form));
2259 }
2260
2261 let conv = fmt_bytes[i];
2262 i += 1;
2263
2264 let spec_slice = &fmt_bytes[spec_start + 1..i - 1];
2265 let form = &fmt_bytes[spec_start..i];
2266
2267 if spec_slice.len() + 1 >= 22 {
2269 return Err(lua_vm::debug::c_api_runtime(
2270 state,
2271 b"invalid format (too long)".to_vec(),
2272 ));
2273 }
2274
2275 let spec = parse_fmt_spec(spec_slice);
2276
2277 match conv {
2278 b'c' => {
2279 check_conv_spec(state, form, FMT_FLAGS_C, false)?;
2280 let n = state.check_arg_integer(arg)?;
2281 let body = vec![n as u8];
2282 pad_str(&mut buf, &body, &spec);
2283 }
2284 b'd' | b'i' => {
2285 check_conv_spec(state, form, FMT_FLAGS_I, true)?;
2286 let n = format_int_arg(state, arg)?;
2287 let (sign, digits) = signed_int_parts(n, &spec);
2288 pad_int(&mut buf, &sign, &digits, &spec);
2289 }
2290 b'u' => {
2291 check_conv_spec(state, form, FMT_FLAGS_U, true)?;
2292 let n = format_int_arg(state, arg)? as u64;
2293 let (prefix, digits) = unsigned_int_parts(n, 10, false, &spec);
2294 pad_int(&mut buf, &prefix, &digits, &spec);
2295 }
2296 b'o' => {
2297 check_conv_spec(state, form, FMT_FLAGS_X, true)?;
2298 let n = format_int_arg(state, arg)? as u64;
2299 let (prefix, digits) = unsigned_int_parts(n, 8, false, &spec);
2300 pad_int(&mut buf, &prefix, &digits, &spec);
2301 }
2302 b'x' => {
2303 check_conv_spec(state, form, FMT_FLAGS_X, true)?;
2304 let n = format_int_arg(state, arg)? as u64;
2305 let (prefix, digits) = unsigned_int_parts(n, 16, false, &spec);
2306 pad_int(&mut buf, &prefix, &digits, &spec);
2307 }
2308 b'X' => {
2309 check_conv_spec(state, form, FMT_FLAGS_X, true)?;
2310 let n = format_int_arg(state, arg)? as u64;
2311 let (prefix, digits) = unsigned_int_parts(n, 16, true, &spec);
2312 pad_int(&mut buf, &prefix, &digits, &spec);
2313 }
2314 b'a' | b'A' => {
2315 check_conv_spec(state, form, FMT_FLAGS_F, true)?;
2316 let n = state.check_arg_number(arg)?;
2317 let body = format_hex_float(n, spec.precision);
2318 let body: Vec<u8> = if conv == b'A' {
2319 body.into_iter().map(|b| b.to_ascii_uppercase()).collect()
2320 } else {
2321 body
2322 };
2323 let (sign, digits): (Vec<u8>, Vec<u8>) =
2324 if !body.is_empty() && (body[0] == b'-' || body[0] == b'+') {
2325 (vec![body[0]], body[1..].to_vec())
2326 } else if spec.plus_sign {
2327 (b"+".to_vec(), body)
2328 } else if spec.space_sign {
2329 (b" ".to_vec(), body)
2330 } else {
2331 (Vec::new(), body)
2332 };
2333 let no_prec_spec = FmtSpec {
2334 left_align: spec.left_align,
2335 plus_sign: spec.plus_sign,
2336 space_sign: spec.space_sign,
2337 alt_form: spec.alt_form,
2338 zero_pad: spec.zero_pad,
2339 width: spec.width,
2340 precision: None,
2341 };
2342 pad_int(&mut buf, &sign, &digits, &no_prec_spec);
2343 }
2344 b'f' | b'e' | b'E' | b'g' | b'G' => {
2345 check_conv_spec(state, form, FMT_FLAGS_F, true)?;
2346 let n = state.check_arg_number(arg)?;
2347 let body = format_float(n, conv, &spec);
2348 let (sign, digits): (Vec<u8>, Vec<u8>) =
2349 if !body.is_empty() && (body[0] == b'-' || body[0] == b'+') {
2350 (vec![body[0]], body[1..].to_vec())
2351 } else if n >= 0.0 && spec.plus_sign {
2352 (b"+".to_vec(), body)
2353 } else if n >= 0.0 && spec.space_sign {
2354 (b" ".to_vec(), body)
2355 } else {
2356 (Vec::new(), body)
2357 };
2358 let no_prec_spec = FmtSpec {
2359 left_align: spec.left_align,
2360 plus_sign: spec.plus_sign,
2361 space_sign: spec.space_sign,
2362 alt_form: spec.alt_form,
2363 zero_pad: spec.zero_pad,
2364 width: spec.width,
2365 precision: None,
2366 };
2367 pad_int(&mut buf, &sign, &digits, &no_prec_spec);
2368 }
2369 b'p' => {
2370 check_conv_spec(state, form, FMT_FLAGS_C, false)?;
2371 let s: Vec<u8> = match lua_vm::api::to_pointer(state, arg) {
2372 Some(p) => format!("0x{:x}", p).into_bytes(),
2373 None => b"(null)".to_vec(),
2374 };
2375 pad_str(
2376 &mut buf,
2377 &s,
2378 &FmtSpec {
2379 precision: None,
2380 ..spec
2381 },
2382 );
2383 }
2384 b'q' => {
2385 if form.len() > 2 {
2386 return Err(LuaError::runtime(format_args!(
2387 "specifier '%q' cannot have modifiers"
2388 )));
2389 }
2390 addliteral(state, &mut buf, arg)?;
2391 }
2392 b's' => {
2393 check_conv_spec(state, form, FMT_FLAGS_C, true)?;
2394 let s = state.to_display_string(arg)?;
2395 let has_modifiers = spec.width != 0 || spec.precision.is_some();
2396 if has_modifiers && s.contains(&0u8) {
2397 return Err(lua_vm::debug::arg_error_impl(
2398 state,
2399 arg,
2400 b"string contains zeros",
2401 ));
2402 }
2403 pad_str(&mut buf, &s, &spec);
2404 state.pop_n(1);
2405 }
2406 _ => {
2407 let verb: &[u8] = if state.global().lua_version == lua_types::LuaVersion::V53 {
2408 b"option"
2409 } else {
2410 b"conversion"
2411 };
2412 let mut msg = b"invalid ".to_vec();
2413 msg.extend_from_slice(verb);
2414 msg.extend_from_slice(b" '");
2415 msg.extend_from_slice(form);
2416 msg.extend_from_slice(b"' to 'format'");
2417 return Err(lua_vm::debug::c_api_runtime(state, msg));
2418 }
2419 }
2420 }
2421
2422 state.push_bytes(&buf)?;
2423 Ok(1)
2424}
2425
2426fn is_digit(c: u8) -> bool {
2432 c.is_ascii_digit()
2433}
2434
2435fn getnum(fmt: &[u8], pos: &mut usize, df: i64, wide: bool) -> i64 {
2443 if *pos >= fmt.len() || !is_digit(fmt[*pos]) {
2444 return df;
2445 }
2446 let cap: i64 = if wide { i64::MAX } else { i32::MAX as i64 };
2447 let mut a = 0i64;
2448 while *pos < fmt.len() && is_digit(fmt[*pos]) {
2449 a = a * 10 + (fmt[*pos] - b'0') as i64;
2450 *pos += 1;
2451 if a > (cap - 9) / 10 {
2452 break;
2453 }
2454 }
2455 a
2456}
2457
2458fn getnumlimit(fmt: &[u8], pos: &mut usize, df: i64) -> Result<usize, LuaError> {
2461 let sz = getnum(fmt, pos, df, false);
2462 if sz > MAX_INT_SIZE as i64 || sz <= 0 {
2463 return Err(LuaError::runtime(format_args!(
2464 "integral size ({}) out of limits [1,{}]",
2465 sz, MAX_INT_SIZE
2466 )));
2467 }
2468 Ok(sz as usize)
2469}
2470
2471fn getoption(
2474 h: &mut Header,
2475 fmt: &[u8],
2476 pos: &mut usize,
2477 size: &mut usize,
2478) -> Result<KOption, LuaError> {
2479 const NATIVE_MAX_ALIGN: usize = std::mem::align_of::<f64>();
2481
2482 if *pos >= fmt.len() {
2483 return Ok(KOption::Nop);
2484 }
2485 let opt = fmt[*pos];
2486 *pos += 1;
2487 *size = 0;
2488
2489 match opt {
2490 b'b' => {
2491 *size = 1;
2492 Ok(KOption::Int)
2493 }
2494 b'B' => {
2495 *size = 1;
2496 Ok(KOption::Uint)
2497 }
2498 b'h' => {
2499 *size = 2;
2500 Ok(KOption::Int)
2501 }
2502 b'H' => {
2503 *size = 2;
2504 Ok(KOption::Uint)
2505 }
2506 b'l' => {
2507 *size = 8;
2508 Ok(KOption::Int)
2509 } b'L' => {
2511 *size = 8;
2512 Ok(KOption::Uint)
2513 }
2514 b'j' => {
2515 *size = SZINT;
2516 Ok(KOption::Int)
2517 }
2518 b'J' => {
2519 *size = SZINT;
2520 Ok(KOption::Uint)
2521 }
2522 b'T' => {
2523 *size = std::mem::size_of::<usize>();
2524 Ok(KOption::Uint)
2525 }
2526 b'f' => {
2527 *size = 4;
2528 Ok(KOption::Float)
2529 }
2530 b'n' => {
2531 *size = 8;
2532 Ok(KOption::Number)
2533 } b'd' => {
2535 *size = 8;
2536 Ok(KOption::Double)
2537 } b'i' => {
2539 *size = getnumlimit(fmt, pos, 4)?;
2540 Ok(KOption::Int)
2541 }
2542 b'I' => {
2543 *size = getnumlimit(fmt, pos, 4)?;
2544 Ok(KOption::Uint)
2545 }
2546 b's' => {
2547 *size = getnumlimit(fmt, pos, std::mem::size_of::<usize>() as i64)?;
2548 Ok(KOption::Kstring)
2549 }
2550 b'c' => {
2551 let n = getnum(fmt, pos, -1, h.wide_size);
2552 if n == -1 {
2553 return Err(LuaError::runtime(format_args!(
2554 "missing size for format option 'c'"
2555 )));
2556 }
2557 *size = n as usize;
2558 Ok(KOption::Char)
2559 }
2560 b'z' => Ok(KOption::Zstr),
2561 b'x' => {
2562 *size = 1;
2563 Ok(KOption::Padding)
2564 }
2565 b'X' => Ok(KOption::Paddalign),
2566 b' ' => Ok(KOption::Nop),
2567 b'<' => {
2568 h.is_little = true;
2569 Ok(KOption::Nop)
2570 }
2571 b'>' => {
2572 h.is_little = false;
2573 Ok(KOption::Nop)
2574 }
2575 b'=' => {
2576 h.is_little = cfg!(target_endian = "little");
2577 Ok(KOption::Nop)
2578 }
2579 b'!' => {
2580 let n = getnum(fmt, pos, NATIVE_MAX_ALIGN as i64, false);
2581 h.max_align = getnumlimit(fmt, pos, n)?;
2582 Ok(KOption::Nop)
2583 }
2584 _ => Err(LuaError::runtime(format_args!(
2585 "invalid format option '{}'",
2586 opt as char
2587 ))),
2588 }
2589}
2590
2591fn getdetails(
2594 state: &mut LuaState,
2595 h: &mut Header,
2596 total_size: usize,
2597 fmt: &[u8],
2598 pos: &mut usize,
2599 psize: &mut usize,
2600 ntoalign: &mut usize,
2601) -> Result<KOption, LuaError> {
2602 let opt = getoption(h, fmt, pos, psize)?;
2603 let mut align = *psize;
2604
2605 if opt == KOption::Paddalign {
2606 if *pos >= fmt.len() {
2607 return Err(lua_vm::debug::arg_error_impl(
2608 state,
2609 1,
2610 b"invalid next option for option 'X'",
2611 ));
2612 }
2613 let mut dummy_size = 0usize;
2614 let next_opt = getoption(h, fmt, pos, &mut dummy_size)?;
2615 align = dummy_size;
2616 if next_opt == KOption::Char || align == 0 {
2617 return Err(lua_vm::debug::arg_error_impl(
2618 state,
2619 1,
2620 b"invalid next option for option 'X'",
2621 ));
2622 }
2623 }
2624
2625 if align <= 1 || opt == KOption::Char {
2626 *ntoalign = 0;
2627 } else {
2628 if align > h.max_align {
2629 align = h.max_align;
2630 }
2631 if (align & (align - 1)) != 0 {
2632 return Err(lua_vm::debug::arg_error_impl(
2633 state,
2634 1,
2635 b"format asks for alignment not power of 2",
2636 ));
2637 }
2638 *ntoalign = (align - (total_size & (align - 1))) & (align - 1);
2639 }
2640 Ok(opt)
2641}
2642
2643fn packint(buf: &mut Vec<u8>, mut n: u64, is_little: bool, size: usize, neg: bool) {
2646 let start = buf.len();
2647 buf.resize(start + size, 0);
2648 let slice = &mut buf[start..start + size];
2649 for i in 0..size {
2651 slice[if is_little { i } else { size - 1 - i }] = (n & MC as u64) as u8;
2652 n >>= NB;
2653 }
2654 if neg && size > SZINT {
2656 for i in SZINT..size {
2657 slice[if is_little { i } else { size - 1 - i }] = MC;
2658 }
2659 }
2660}
2661
2662fn copywithendian(dest: &mut [u8], src: &[u8], is_little: bool) {
2665 debug_assert_eq!(dest.len(), src.len());
2666 if is_little == cfg!(target_endian = "little") {
2667 dest.copy_from_slice(src);
2668 } else {
2669 for (d, s) in dest.iter_mut().zip(src.iter().rev()) {
2670 *d = *s;
2671 }
2672 }
2673}
2674
2675fn unpackint(
2678 _state: &LuaState,
2679 data: &[u8],
2680 is_little: bool,
2681 size: usize,
2682 is_signed: bool,
2683) -> Result<i64, LuaError> {
2684 let limit = size.min(SZINT);
2685 let mut res: u64 = 0;
2686 for i in (0..limit).rev() {
2687 res <<= NB;
2688 let byte_idx = if is_little { i } else { size - 1 - i };
2689 res |= data[byte_idx] as u64;
2690 }
2691
2692 if size < SZINT {
2693 if is_signed {
2694 let mask: u64 = 1u64 << (size * NB as usize - 1);
2695 res = (res ^ mask).wrapping_sub(mask);
2696 }
2697 } else if size > SZINT {
2698 let mask = if !is_signed || (res as i64) >= 0 {
2699 0u8
2700 } else {
2701 MC
2702 };
2703 for i in limit..size {
2704 let byte_idx = if is_little { i } else { size - 1 - i };
2705 if data[byte_idx] != mask {
2706 return Err(LuaError::runtime(format_args!(
2707 "{}-byte integer does not fit into Lua Integer",
2708 size
2709 )));
2710 }
2711 }
2712 }
2713 Ok(res as i64)
2714}
2715
2716pub fn str_pack(state: &mut LuaState) -> Result<usize, LuaError> {
2719 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2720 let fmt = &fmt_bytes[..];
2721 let mut h = Header::new(state.global().lua_version == lua_types::LuaVersion::V55);
2722 let mut arg = 1i32;
2723 let mut total_size = 0usize;
2724 let mut buf: Vec<u8> = Vec::new();
2725 let mut pos = 0usize;
2726
2727 while pos < fmt.len() {
2728 let mut size = 0usize;
2729 let mut ntoalign = 0usize;
2730 let opt = getdetails(
2731 state,
2732 &mut h,
2733 total_size,
2734 fmt,
2735 &mut pos,
2736 &mut size,
2737 &mut ntoalign,
2738 )?;
2739 if h.wide_size {
2743 let space = ntoalign + size;
2744 if space > (i64::MAX as usize) || total_size > (i64::MAX as usize) - space {
2745 return Err(lua_vm::debug::arg_error_impl(
2746 state,
2747 arg,
2748 b"result too long",
2749 ));
2750 }
2751 }
2752 total_size += ntoalign + size;
2753 for _ in 0..ntoalign {
2754 buf.push(PACK_PAD_BYTE);
2755 }
2756 arg += 1;
2757
2758 match opt {
2759 KOption::Int => {
2760 let n = state.check_arg_integer(arg)?;
2761 if size < SZINT {
2762 let lim: i64 = 1i64 << (size * NB as usize - 1);
2763 if !(-lim <= n && n < lim) {
2764 return Err(lua_vm::debug::arg_error_impl(
2765 state,
2766 arg,
2767 b"integer overflow",
2768 ));
2769 }
2770 }
2771 packint(&mut buf, n as u64, h.is_little, size, n < 0);
2772 }
2773 KOption::Uint => {
2774 let n = state.check_arg_integer(arg)?;
2775 if size < SZINT {
2776 let lim: u64 = 1u64 << (size * NB as usize);
2777 if (n as u64) >= lim {
2778 return Err(lua_vm::debug::arg_error_impl(
2779 state,
2780 arg,
2781 b"unsigned overflow",
2782 ));
2783 }
2784 }
2785 packint(&mut buf, n as u64, h.is_little, size, false);
2786 }
2787 KOption::Float => {
2788 let f = state.check_arg_number(arg)? as f32;
2789 let start = buf.len();
2790 buf.resize(start + 4, 0);
2791 copywithendian(
2792 &mut buf[start..start + 4],
2793 &f.to_bits().to_ne_bytes(),
2794 h.is_little,
2795 );
2796 }
2797 KOption::Number => {
2798 let f = state.check_arg_number(arg)?;
2799 let start = buf.len();
2800 buf.resize(start + 8, 0);
2801 copywithendian(
2802 &mut buf[start..start + 8],
2803 &f.to_bits().to_ne_bytes(),
2804 h.is_little,
2805 );
2806 }
2807 KOption::Double => {
2808 let f = state.check_arg_number(arg)? as f64;
2809 let start = buf.len();
2810 buf.resize(start + 8, 0);
2811 copywithendian(
2812 &mut buf[start..start + 8],
2813 &f.to_bits().to_ne_bytes(),
2814 h.is_little,
2815 );
2816 }
2817 KOption::Char => {
2818 let s = state.check_arg_string(arg)?.to_vec();
2819 if s.len() > size {
2820 return Err(lua_vm::debug::arg_error_impl(
2821 state,
2822 arg,
2823 b"string longer than given size",
2824 ));
2825 }
2826 buf.extend_from_slice(&s);
2827 let pad = size - s.len();
2828 for _ in 0..pad {
2829 buf.push(PACK_PAD_BYTE);
2830 }
2831 }
2832 KOption::Kstring => {
2833 let s = state.check_arg_string(arg)?.to_vec();
2834 let len = s.len();
2835 if size < SZINT && len >= (1usize << (size * 8)) {
2836 return Err(lua_vm::debug::arg_error_impl(
2837 state,
2838 arg,
2839 b"string length does not fit in given size",
2840 ));
2841 }
2842 packint(&mut buf, len as u64, h.is_little, size, false);
2843 buf.extend_from_slice(&s);
2844 total_size += len;
2845 }
2846 KOption::Zstr => {
2847 let s = state.check_arg_string(arg)?.to_vec();
2848 if s.contains(&0) {
2849 return Err(lua_vm::debug::arg_error_impl(
2850 state,
2851 arg,
2852 b"string contains zeros",
2853 ));
2854 }
2855 buf.extend_from_slice(&s);
2856 buf.push(0);
2857 total_size += s.len() + 1;
2858 }
2859 KOption::Padding => {
2860 buf.push(PACK_PAD_BYTE);
2861 arg -= 1; }
2863 KOption::Paddalign | KOption::Nop => {
2864 arg -= 1; }
2866 }
2867 }
2868
2869 state.push_bytes(&buf)?;
2870 Ok(1)
2871}
2872
2873pub fn str_packsize(state: &mut LuaState) -> Result<usize, LuaError> {
2876 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2877 let fmt = &fmt_bytes[..];
2878 let mut h = Header::new(state.global().lua_version == lua_types::LuaVersion::V55);
2879 let mut total_size = 0usize;
2880 let mut pos = 0usize;
2881
2882 while pos < fmt.len() {
2883 let mut size = 0usize;
2884 let mut ntoalign = 0usize;
2885 let opt = getdetails(
2886 state,
2887 &mut h,
2888 total_size,
2889 fmt,
2890 &mut pos,
2891 &mut size,
2892 &mut ntoalign,
2893 )?;
2894 if opt == KOption::Kstring || opt == KOption::Zstr {
2895 return Err(lua_vm::debug::arg_error_impl(
2896 state,
2897 1,
2898 b"variable-length format",
2899 ));
2900 }
2901 let space = ntoalign + size;
2902 let max_total: usize = if h.wide_size {
2903 i64::MAX as usize
2904 } else {
2905 PACK_MAXSIZE
2906 };
2907 if space > max_total || total_size > max_total - space {
2908 return Err(lua_vm::debug::arg_error_impl(
2909 state,
2910 1,
2911 b"format result too large",
2912 ));
2913 }
2914 total_size += space;
2915 }
2916 state.push(LuaValue::Int(total_size as i64));
2917 Ok(1)
2918}
2919
2920pub fn str_unpack(state: &mut LuaState) -> Result<usize, LuaError> {
2923 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2924 let data_bytes = state.check_arg_string(2)?.to_vec();
2925 let ld = data_bytes.len();
2926 let pos_raw = state.opt_arg_integer(3, 1)?;
2927 let mut pos = if matches!(state.global().lua_version, lua_types::LuaVersion::V53) {
2928 posrelat_53(pos_raw, ld).wrapping_sub(1)
2929 } else {
2930 pos_relat_i(pos_raw, ld).saturating_sub(1)
2931 };
2932
2933 if pos > ld {
2934 return Err(lua_vm::debug::arg_error_impl(
2935 state,
2936 3,
2937 b"initial position out of string",
2938 ));
2939 }
2940
2941 let fmt = &fmt_bytes[..];
2942 let data = &data_bytes[..];
2943 let mut h = Header::new(state.global().lua_version == lua_types::LuaVersion::V55);
2944 let mut fmt_pos = 0usize;
2945 let mut n = 0usize;
2946
2947 while fmt_pos < fmt.len() {
2948 let mut size = 0usize;
2949 let mut ntoalign = 0usize;
2950 let opt = getdetails(
2951 state,
2952 &mut h,
2953 pos,
2954 fmt,
2955 &mut fmt_pos,
2956 &mut size,
2957 &mut ntoalign,
2958 )?;
2959
2960 if ntoalign + size > ld - pos {
2961 return Err(lua_vm::debug::arg_error_impl(
2962 state,
2963 2,
2964 b"data string too short",
2965 ));
2966 }
2967 pos += ntoalign;
2968 state.ensure_stack(2, "too many results")?;
2969 n += 1;
2970
2971 match opt {
2972 KOption::Int => {
2973 let v = unpackint(state, &data[pos..pos + size], h.is_little, size, true)?;
2974 state.push(LuaValue::Int(v));
2975 }
2976 KOption::Uint => {
2977 let v = unpackint(state, &data[pos..pos + size], h.is_little, size, false)?;
2978 state.push(LuaValue::Int(v));
2979 }
2980 KOption::Float => {
2981 let mut bytes = [0u8; 4];
2982 copywithendian(&mut bytes, &data[pos..pos + 4], h.is_little);
2983 let f = f32::from_bits(u32::from_ne_bytes(bytes));
2984 state.push(LuaValue::Float(f as f64));
2985 }
2986 KOption::Number => {
2987 let mut bytes = [0u8; 8];
2988 copywithendian(&mut bytes, &data[pos..pos + 8], h.is_little);
2989 let f = f64::from_bits(u64::from_ne_bytes(bytes));
2990 state.push(LuaValue::Float(f));
2991 }
2992 KOption::Double => {
2993 let mut bytes = [0u8; 8];
2994 copywithendian(&mut bytes, &data[pos..pos + 8], h.is_little);
2995 let f = f64::from_bits(u64::from_ne_bytes(bytes));
2996 state.push(LuaValue::Float(f));
2997 }
2998 KOption::Char => {
2999 state.push_bytes(&data[pos..pos + size])?;
3000 }
3001 KOption::Kstring => {
3002 let len =
3003 unpackint(state, &data[pos..pos + size], h.is_little, size, false)? as usize;
3004 if len > ld - pos - size {
3005 return Err(lua_vm::debug::arg_error_impl(
3006 state,
3007 2,
3008 b"data string too short",
3009 ));
3010 }
3011 state.push_bytes(&data[pos + size..pos + size + len])?;
3012 pos += len;
3013 }
3014 KOption::Zstr => {
3015 let found = data[pos..].iter().position(|&b| b == 0);
3016 let end = match found {
3017 Some(e) => e,
3018 None => {
3019 return Err(lua_vm::debug::arg_error_impl(
3020 state,
3021 2,
3022 b"unfinished string for format 'z'",
3023 ))
3024 }
3025 };
3026 if pos + end >= ld {
3027 return Err(lua_vm::debug::arg_error_impl(
3028 state,
3029 2,
3030 b"unfinished string for format 'z'",
3031 ));
3032 }
3033 state.push_bytes(&data[pos..pos + end])?;
3034 pos += end + 1;
3035 }
3036 KOption::Paddalign | KOption::Padding | KOption::Nop => {
3037 n -= 1; }
3039 }
3040 pos += size;
3041 }
3042
3043 state.push(LuaValue::Int((pos + 1) as i64));
3044 Ok(n + 1)
3045}
3046
3047pub const STRING_LIB: &[(&[u8], lua_CFunction)] = &[
3054 (b"byte", str_byte),
3055 (b"char", str_char),
3056 (b"dump", str_dump),
3057 (b"find", str_find),
3058 (b"format", str_format),
3059 (b"gmatch", gmatch),
3060 (b"gsub", str_gsub),
3061 (b"len", str_len),
3062 (b"lower", str_lower),
3063 (b"match", str_match),
3064 (b"rep", str_rep),
3065 (b"reverse", str_reverse),
3066 (b"sub", str_sub),
3067 (b"upper", str_upper),
3068 (b"pack", str_pack),
3069 (b"packsize", str_packsize),
3070 (b"unpack", str_unpack),
3071];
3072
3073pub const STRING_META_METHODS: &[(&[u8], lua_CFunction)] = &[
3076 (b"__add", arith_add),
3077 (b"__sub", arith_sub),
3078 (b"__mul", arith_mul),
3079 (b"__mod", arith_mod),
3080 (b"__pow", arith_pow),
3081 (b"__div", arith_div),
3082 (b"__idiv", arith_idiv),
3083 (b"__unm", arith_unm),
3084];
3085
3086pub fn createmetatable(state: &mut LuaState) -> Result<(), LuaError> {
3089 state.new_lib_table(STRING_META_METHODS)?;
3090 state.set_funcs(STRING_META_METHODS, 0)?;
3091 state.push_string(b"")?;
3092 let mt_idx = state.top_idx() - 2;
3093 let mt = state.get_at(mt_idx);
3094 state.push(mt);
3095 state.set_metatable(-2)?;
3096 state.pop_n(1);
3097 let strlib_idx = state.top_idx() - 2;
3098 let strlib = state.get_at(strlib_idx);
3099 state.push(strlib);
3100 state.set_field(-2, b"__index")?;
3101 state.pop_n(1);
3102 Ok(())
3103}
3104
3105pub fn luaopen_string(state: &mut LuaState) -> Result<usize, LuaError> {
3108 state.new_lib(STRING_LIB)?;
3109 if matches!(state.global().lua_version, lua_types::LuaVersion::V51) {
3114 state.push_c_function(gmatch)?;
3115 state.set_field(-2, b"gfind")?;
3116 }
3117 createmetatable(state)?;
3118 Ok(1)
3119}
3120
3121