1use lua_types::error::LuaError;
14use lua_types::value::LuaValue;
15use lua_types::arith::ArithOp;
16use lua_types::{LuaType};
17use lua_vm::state::LuaTableRefExt as _;
18use crate::state_stub::{LuaState, LuaStateStubExt as _, lua_CFunction, upvalue_index};
19
20const LUA_MAX_CAPTURES: usize = 32;
25
26const MAX_CC_CALLS: i32 = 200;
27
28const L_ESC: u8 = b'%';
29
30const SPECIALS: &[u8] = b"^$*+?.([%-";
31
32const CAP_UNFINISHED: isize = -1;
33
34const CAP_POSITION: isize = -2;
35
36#[expect(dead_code, reason = "ported stdlib helper; not yet wired into the runtime")]
37const MAX_ITEM: usize = 120;
38
39#[expect(dead_code, reason = "ported stdlib helper; not yet wired into the runtime")]
40const MAX_ITEM_F: usize = 418;
41
42#[expect(dead_code, reason = "ported stdlib helper; not yet wired into the runtime")]
43const MAX_FORMAT: usize = 32;
44
45const MAX_INT_SIZE: usize = 16;
46
47const PACK_MAXSIZE: usize = i32::MAX as usize;
51
52const NB: u32 = 8;
53
54const MC: u8 = 0xFF;
55
56const SZINT: usize = 8; const PACK_PAD_BYTE: u8 = 0x00;
59
60#[derive(Copy, Clone)]
69struct Capture {
70 init: usize,
72 len: isize,
74}
75
76impl Default for Capture {
77 fn default() -> Self {
78 Capture { init: 0, len: CAP_UNFINISHED }
79 }
80}
81
82struct MatchState<'a> {
86 src: &'a [u8],
88 pat: &'a [u8],
90 matchdepth: i32,
92 level: u8,
94 captures: [Capture; LUA_MAX_CAPTURES],
96 steps: u64,
100 step_limit: u64,
103 aborted: bool,
106}
107
108impl<'a> MatchState<'a> {
109 fn new(src: &'a [u8], pat: &'a [u8], step_limit: u64) -> Self {
110 MatchState {
111 src,
112 pat,
113 matchdepth: MAX_CC_CALLS,
114 level: 0,
115 captures: [Capture::default(); LUA_MAX_CAPTURES],
116 steps: 0,
117 step_limit,
118 aborted: false,
119 }
120 }
121
122 fn reset_level(&mut self) {
123 self.level = 0;
124 debug_assert!(self.matchdepth == MAX_CC_CALLS);
125 }
126}
127
128#[expect(dead_code, reason = "ported stdlib helper; not yet wired into the runtime")]
137struct GMatchState {
138 src_pos: usize,
140 pat: Vec<u8>,
142 last_match: Option<usize>,
144 src: Vec<u8>,
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
155enum KOption {
156 Int, Uint, Float, Number, Double, Char, Kstring, Zstr, Padding, Paddalign, Nop, }
168
169struct Header {
172 is_little: bool,
173 max_align: usize,
174}
175
176impl Header {
177 fn new() -> Self {
178 Header {
179 is_little: cfg!(target_endian = "little"),
180 max_align: 1,
181 }
182 }
183}
184
185fn pos_relat_i(pos: i64, len: usize) -> usize {
193 if pos > 0 {
194 pos as usize
195 } else if pos == 0 {
196 1
197 } else if pos < -(len as i64) {
198 1
199 } else {
200 len.wrapping_add(pos as usize).wrapping_add(1)
201 }
202}
203
204fn get_end_pos(pos: i64, len: usize) -> usize {
208 if pos > len as i64 {
209 len
210 } else if pos >= 0 {
211 pos as usize
212 } else if pos < -(len as i64) {
213 0
214 } else {
215 len.wrapping_add(pos as usize).wrapping_add(1)
216 }
217}
218
219pub fn str_len(state: &mut LuaState) -> Result<usize, LuaError> {
230 let l = match state.to_lua_string_len(1) {
231 Some(n) => n,
232 None => {
233 state.check_arg_string(1)?;
234 unreachable!("check_arg_string raises when arg #1 is not a string");
235 }
236 };
237 state.push(LuaValue::Int(l as i64));
238 Ok(1)
239}
240
241pub fn str_sub(state: &mut LuaState) -> Result<usize, LuaError> {
249 let s_ref = match state.to_lua_string(1) {
250 Some(r) => r,
251 None => {
252 state.check_arg_string(1)?;
253 unreachable!("check_arg_string raises when arg #1 is not a string");
254 }
255 };
256 let s: &[u8] = s_ref.as_bytes();
257 let l = s.len();
258 let start = pos_relat_i(state.check_arg_integer(2)?, l);
259 let end_pos_raw = state.opt_arg_integer(3, -1)?;
260 let end = get_end_pos(end_pos_raw, l);
261 if start <= end {
262 let slice = &s[(start - 1)..end];
263 state.push_string(slice)?;
264 } else {
265 state.push_string(b"")?;
266 }
267 Ok(1)
268}
269
270pub fn str_reverse(state: &mut LuaState) -> Result<usize, LuaError> {
276 let s_ref = match state.to_lua_string(1) {
277 Some(r) => r,
278 None => {
279 state.check_arg_string(1)?;
280 unreachable!("check_arg_string raises when arg #1 is not a string");
281 }
282 };
283 let s: &[u8] = s_ref.as_bytes();
284 let buf: Vec<u8> = s.iter().copied().rev().collect();
285 state.push_bytes(&buf)?;
286 Ok(1)
287}
288
289pub fn str_lower(state: &mut LuaState) -> Result<usize, LuaError> {
295 let s_ref = match state.to_lua_string(1) {
296 Some(r) => r,
297 None => {
298 state.check_arg_string(1)?;
299 unreachable!("check_arg_string raises when arg #1 is not a string");
300 }
301 };
302 let s: &[u8] = s_ref.as_bytes();
303 let buf: Vec<u8> = s.iter().map(|&c| c.to_ascii_lowercase()).collect();
304 state.push_bytes(&buf)?;
305 Ok(1)
306}
307
308pub fn str_upper(state: &mut LuaState) -> Result<usize, LuaError> {
315 let s_ref = match state.to_lua_string(1) {
316 Some(r) => r,
317 None => {
318 state.check_arg_string(1)?;
319 unreachable!("check_arg_string raises when arg #1 is not a string");
320 }
321 };
322 let s: &[u8] = s_ref.as_bytes();
323 let buf: Vec<u8> = s.iter().map(|&c| c.to_ascii_uppercase()).collect();
324 state.push_bytes(&buf)?;
325 Ok(1)
326}
327
328pub fn str_rep(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 l = s.len();
344 let n = state.check_arg_integer(2)?;
345 let sep_owned = state.opt_arg_string(3, b"")?;
346 let sep: &[u8] = &sep_owned;
347 let lsep = sep.len();
348
349 if n <= 0 {
350 state.push_string(b"")?;
351 } else {
352 const MAXSIZE: usize = i32::MAX as usize;
353 let per = l.checked_add(lsep)
354 .ok_or_else(|| LuaError::runtime(format_args!("resulting string too large")))?;
355 if per > MAXSIZE / (n as usize) {
356 return Err(LuaError::runtime(format_args!("resulting string too large")));
357 }
358 let total = per * (n as usize) - lsep;
359
360 if let Some(err) = state.sandbox_reserve(total) {
361 return Err(err);
362 }
363
364 let mut buf: Vec<u8> = Vec::with_capacity(total);
365 for i in 0..(n as usize) {
366 buf.extend_from_slice(s);
367 if i < (n as usize - 1) && lsep > 0 {
368 buf.extend_from_slice(sep);
369 }
370 }
371 state.push_bytes(&buf)?;
372 }
373 Ok(1)
374}
375
376pub fn str_byte(state: &mut LuaState) -> Result<usize, LuaError> {
385 let s_ref = match state.to_lua_string(1) {
386 Some(r) => r,
387 None => {
388 state.check_arg_string(1)?;
389 unreachable!("check_arg_string raises when arg #1 is not a string");
390 }
391 };
392 let s: &[u8] = s_ref.as_bytes();
393 let l = s.len();
394 let pi = state.opt_arg_integer(2, 1)?;
395 let posi = pos_relat_i(pi, l);
396 let pose_raw = state.opt_arg_integer(3, pi)?;
397 let pose = get_end_pos(pose_raw, l);
398
399 if posi > pose {
400 return Ok(0);
401 }
402 let count = pose.saturating_sub(posi - 1) + 1;
403 if count > i32::MAX as usize {
404 return Err(LuaError::runtime(format_args!("string slice too long")));
405 }
406 let n = (pose - posi + 1) as usize;
407 state.ensure_stack(n as i32, "string slice too long")?;
408
409 for i in 0..n {
410 state.push(LuaValue::Int(s[posi - 1 + i] as i64));
411 }
412 Ok(n)
413}
414
415pub fn str_char(state: &mut LuaState) -> Result<usize, LuaError> {
418 let n = state.get_top();
419 let mut buf = Vec::with_capacity(n as usize);
420 for i in 1..=n {
421 let c = state.check_arg_integer(i)? as u64;
422 if c > u8::MAX as u64 {
423 return Err(LuaError::arg_error(i, "value out of range"));
424 }
425 buf.push(c as u8);
426 }
427 state.push_bytes(&buf)?;
428 Ok(1)
429}
430
431pub fn str_dump(state: &mut LuaState) -> Result<usize, LuaError> {
435 state.check_arg_type(1, LuaType::Function)?;
436 let strip = state.arg_to_bool(2);
437 lua_vm::api::set_top(state, 1)?;
440 let bytes = state.dump_function(strip)
444 .map_err(|_| LuaError::runtime(format_args!("unable to dump given function")))?;
445 state.push_bytes(&bytes)?;
446 Ok(1)
447}
448
449fn tonum(state: &mut LuaState, arg: i32) -> Result<bool, LuaError> {
457 if state.type_at(arg) == LuaType::Number {
458 state.push_value_at(arg)?;
459 Ok(true)
460 } else {
461 if let Some(s) = state.to_lua_string_bytes(arg) {
464 let len = s.len();
465 let pushed = state.string_to_number_push(&s)?;
467 Ok(pushed == len + 1)
468 } else {
469 Ok(false)
470 }
471 }
472}
473
474fn trymt(state: &mut LuaState, mtname: &[u8]) -> Result<(), LuaError> {
477 lua_vm::api::set_top(state, 2)?;
481 let t2_is_string = state.type_at(2) == LuaType::String;
483 let has_mm = state.get_meta_field(2, mtname)?;
484 if t2_is_string || !has_mm {
485 let op = &mtname[2..]; return Err(LuaError::runtime(format_args!(
487 "attempt to {} a '{}' with a '{}'",
488 op.escape_ascii(),
489 state.type_name_at(-2).escape_ascii(),
490 state.type_name_at(-1).escape_ascii(),
491 )));
492 }
493 state.insert(-3)?;
494 state.call(2, 1)?;
495 Ok(())
496}
497
498fn arith(state: &mut LuaState, op: ArithOp, mtname: &[u8]) -> Result<usize, LuaError> {
501 if tonum(state, 1)? && tonum(state, 2)? {
502 state.arith(op)?;
503 } else {
504 trymt(state, mtname)?;
505 }
506 Ok(1)
507}
508
509pub fn arith_add(state: &mut LuaState) -> Result<usize, LuaError> {
510 arith(state, ArithOp::Add, b"__add")
511}
512pub fn arith_sub(state: &mut LuaState) -> Result<usize, LuaError> {
513 arith(state, ArithOp::Sub, b"__sub")
514}
515pub fn arith_mul(state: &mut LuaState) -> Result<usize, LuaError> {
516 arith(state, ArithOp::Mul, b"__mul")
517}
518pub fn arith_mod(state: &mut LuaState) -> Result<usize, LuaError> {
519 arith(state, ArithOp::Mod, b"__mod")
520}
521pub fn arith_pow(state: &mut LuaState) -> Result<usize, LuaError> {
522 arith(state, ArithOp::Pow, b"__pow")
523}
524pub fn arith_div(state: &mut LuaState) -> Result<usize, LuaError> {
525 arith(state, ArithOp::Div, b"__div")
526}
527pub fn arith_idiv(state: &mut LuaState) -> Result<usize, LuaError> {
528 arith(state, ArithOp::Idiv, b"__idiv")
529}
530pub fn arith_unm(state: &mut LuaState) -> Result<usize, LuaError> {
531 arith(state, ArithOp::Unm, b"__unm")
532}
533
534fn match_class(c: u8, cl: u8) -> bool {
541 let res = match cl.to_ascii_lowercase() {
542 b'a' => c.is_ascii_alphabetic(),
543 b'c' => c.is_ascii_control(),
544 b'd' => c.is_ascii_digit(),
545 b'g' => c.is_ascii_graphic(),
546 b'l' => c.is_ascii_lowercase(),
547 b'p' => c.is_ascii_punctuation(),
548 b's' => c.is_ascii_whitespace(),
549 b'u' => c.is_ascii_uppercase(),
550 b'w' => c.is_ascii_alphanumeric(),
551 b'x' => c.is_ascii_hexdigit(),
552 b'z' => c == 0,
553 _ => return cl == c,
554 };
555 if cl.is_ascii_lowercase() { res } else { !res }
556}
557
558fn matchbracketclass(pat: &[u8], c: u8, mut p: usize, ec: usize) -> bool {
562 let sig = if p + 1 < pat.len() && pat[p + 1] == b'^' {
563 p += 1; false
565 } else {
566 true
567 };
568 p += 1; while p < ec {
570 if pat[p] == L_ESC {
571 p += 1;
572 if p < ec && match_class(c, pat[p]) {
573 return sig;
574 }
575 } else if p + 1 < ec && pat[p + 1] == b'-' && p + 2 < ec {
576 let lo = pat[p];
577 p += 2;
578 let hi = pat[p];
579 if lo <= c && c <= hi {
580 return sig;
581 }
582 } else if pat[p] == c {
583 return sig;
584 }
585 p += 1;
586 }
587 !sig
588}
589
590fn singlematch(ms: &MatchState, s: usize, p: usize, ep: usize) -> bool {
594 if s >= ms.src.len() {
595 return false;
596 }
597 let c = ms.src[s];
598 match ms.pat[p] {
599 b'.' => true,
600 L_ESC => match_class(c, ms.pat[p + 1]),
601 b'[' => matchbracketclass(ms.pat, c, p, ep - 1),
602 pc => pc == c,
603 }
604}
605
606fn classend(ms: &MatchState, p: usize) -> Result<usize, LuaError> {
610 let pat = ms.pat;
611 match pat.get(p).copied() {
612 Some(L_ESC) => {
613 if p + 1 >= pat.len() {
614 return Err(LuaError::runtime(format_args!(
615 "malformed pattern (ends with '%')"
616 )));
617 }
618 Ok(p + 2)
619 }
620 Some(b'[') => {
621 let mut q = p + 1;
622 if q < pat.len() && pat[q] == b'^' {
623 q += 1;
624 }
625 loop {
626 if q >= pat.len() {
627 return Err(LuaError::runtime(format_args!(
628 "malformed pattern (missing ']')"
629 )));
630 }
631 let ch = pat[q];
632 q += 1;
633 if ch == L_ESC && q < pat.len() {
634 q += 1;
635 }
636 if q < pat.len() && pat[q] == b']' {
637 return Ok(q + 1);
638 }
639 }
640 }
641 Some(_) => Ok(p + 1),
642 None => Ok(p),
643 }
644}
645
646fn check_capture(ms: &MatchState, l: u8) -> Result<usize, LuaError> {
650 let signed = (l as i32) - (b'1' as i32);
651 if signed < 0
652 || signed >= ms.level as i32
653 || ms.captures[signed as usize].len == CAP_UNFINISHED
654 {
655 return Err(LuaError::runtime(format_args!(
656 "invalid capture index %{}",
657 signed + 1
658 )));
659 }
660 Ok(signed as usize)
661}
662
663fn capture_to_close(ms: &MatchState) -> Result<usize, LuaError> {
666 let mut level = ms.level as usize;
667 while level > 0 {
668 level -= 1;
669 if ms.captures[level].len == CAP_UNFINISHED {
670 return Ok(level);
671 }
672 }
673 Err(LuaError::runtime(format_args!("invalid pattern capture")))
674}
675
676fn matchbalance(ms: &MatchState, s: usize, p: usize) -> Result<Option<usize>, LuaError> {
680 if p + 1 >= ms.pat.len() {
681 return Err(LuaError::runtime(format_args!(
682 "malformed pattern (missing arguments to '%b')"
683 )));
684 }
685 let b = ms.pat[p];
686 let e = ms.pat[p + 1];
687 if s >= ms.src.len() || ms.src[s] != b {
688 return Ok(None);
689 }
690 let mut cont = 1i32;
691 let mut s = s + 1;
692 while s < ms.src.len() {
693 if ms.src[s] == e {
694 cont -= 1;
695 if cont == 0 {
696 return Ok(Some(s + 1));
697 }
698 } else if ms.src[s] == b {
699 cont += 1;
700 }
701 s += 1;
702 }
703 Ok(None)
704}
705
706fn max_expand(
709 ms: &mut MatchState,
710 s: usize,
711 p: usize,
712 ep: usize,
713) -> Result<Option<usize>, LuaError> {
714 let mut count: isize = 0;
715 while singlematch(ms, s + count as usize, p, ep) {
716 count += 1;
717 }
718 while count >= 0 {
719 let res = match_pat(ms, s + count as usize, ep + 1)?;
720 if res.is_some() {
721 return Ok(res);
722 }
723 count -= 1;
724 }
725 Ok(None)
726}
727
728fn min_expand(
731 ms: &mut MatchState,
732 mut s: usize,
733 p: usize,
734 ep: usize,
735) -> Result<Option<usize>, LuaError> {
736 loop {
737 let res = match_pat(ms, s, ep + 1)?;
738 if res.is_some() {
739 return Ok(res);
740 } else if singlematch(ms, s, p, ep) {
741 s += 1;
742 } else {
743 return Ok(None);
744 }
745 }
746}
747
748fn start_capture(
751 ms: &mut MatchState,
752 s: usize,
753 p: usize,
754 what: isize,
755) -> Result<Option<usize>, LuaError> {
756 let level = ms.level as usize;
757 if level >= LUA_MAX_CAPTURES {
758 return Err(LuaError::runtime(format_args!("too many captures")));
759 }
760 ms.captures[level].init = s;
761 ms.captures[level].len = what;
762 ms.level += 1;
763 let res = match_pat(ms, s, p)?;
764 if res.is_none() {
765 ms.level -= 1; }
767 Ok(res)
768}
769
770fn end_capture(ms: &mut MatchState, s: usize, p: usize) -> Result<Option<usize>, LuaError> {
773 let l = capture_to_close(ms)?;
774 ms.captures[l].len = (s - ms.captures[l].init) as isize;
775 let res = match_pat(ms, s, p)?;
776 if res.is_none() {
777 ms.captures[l].len = CAP_UNFINISHED; }
779 Ok(res)
780}
781
782fn match_capture(ms: &MatchState, s: usize, l: u8) -> Result<Option<usize>, LuaError> {
785 let idx = check_capture(ms, l)?;
786 let cap_len = ms.captures[idx].len as usize;
787 let cap_init = ms.captures[idx].init;
788 if ms.src.len() - s >= cap_len
789 && &ms.src[s..s + cap_len] == &ms.src[cap_init..cap_init + cap_len]
790 {
791 Ok(Some(s + cap_len))
792 } else {
793 Ok(None)
794 }
795}
796
797fn match_pat(ms: &mut MatchState, mut s: usize, mut p: usize) -> Result<Option<usize>, LuaError> {
802 if ms.aborted {
803 return Ok(None);
804 }
805 ms.steps += 1;
806 if ms.step_limit != 0 && ms.steps > ms.step_limit {
807 ms.aborted = true;
808 return Ok(None);
809 }
810 ms.matchdepth -= 1;
811 if ms.matchdepth < 0 {
812 ms.matchdepth = 0;
813 return Err(LuaError::runtime(format_args!("pattern too complex")));
814 }
815
816 let result = 'outer: loop {
818 if p >= ms.pat.len() {
819 break 'outer Ok(Some(s));
821 }
822
823 match ms.pat[p] {
824 b'(' => {
825 let s2 = if p + 1 < ms.pat.len() && ms.pat[p + 1] == b')' {
826 start_capture(ms, s, p + 2, CAP_POSITION)?
828 } else {
829 start_capture(ms, s, p + 1, CAP_UNFINISHED)?
830 };
831 break 'outer Ok(s2);
832 }
833 b')' => {
834 let s2 = end_capture(ms, s, p + 1)?;
835 break 'outer Ok(s2);
836 }
837 b'$' => {
838 if p + 1 != ms.pat.len() {
839 let ep = classend(ms, p)?;
841 let s2 = handle_class_with_suffix(ms, s, p, ep)?;
842 break 'outer Ok(s2);
843 }
844 break 'outer Ok(if s == ms.src.len() { Some(s) } else { None });
845 }
846 L_ESC => {
847 match ms.pat.get(p + 1).copied().unwrap_or(0) {
848 b'b' => {
849 let s2 = matchbalance(ms, s, p + 2)?;
850 if let Some(ns) = s2 {
851 s = ns;
852 p += 4;
853 continue 'outer; }
855 break 'outer Ok(None);
856 }
857 b'f' => {
858 p += 2;
859 if ms.pat.get(p).copied() != Some(b'[') {
860 return Err(LuaError::runtime(format_args!(
861 "missing '[' after '%f' in pattern"
862 )));
863 }
864 let ep = classend(ms, p)?;
865 let previous = if s == 0 { 0u8 } else { ms.src[s - 1] };
866 let current = ms.src.get(s).copied().unwrap_or(0);
867 if !matchbracketclass(ms.pat, previous, p, ep - 1)
868 && matchbracketclass(ms.pat, current, p, ep - 1)
869 {
870 p = ep;
871 continue 'outer; }
873 break 'outer Ok(None);
874 }
875 c @ b'0'..=b'9' => {
876 let s2 = match_capture(ms, s, c)?;
877 if let Some(ns) = s2 {
878 s = ns;
879 p += 2;
880 continue 'outer; }
882 break 'outer Ok(None);
883 }
884 _ => {
885 let ep = classend(ms, p)?;
887 let s2 = handle_class_with_suffix(ms, s, p, ep)?;
888 break 'outer Ok(s2);
889 }
890 }
891 }
892 _ => {
893 let ep = classend(ms, p)?;
895 let s2 = handle_class_with_suffix(ms, s, p, ep)?;
896 break 'outer Ok(s2);
897 }
898 }
899 };
900
901 ms.matchdepth += 1;
902 result
903}
904
905fn handle_class_with_suffix(
910 ms: &mut MatchState,
911 s: usize,
912 p: usize,
913 ep: usize,
914) -> Result<Option<usize>, LuaError> {
915 let matched_once = singlematch(ms, s, p, ep);
916 if !matched_once {
917 match ms.pat.get(ep).copied() {
919 Some(b'*') | Some(b'?') | Some(b'-') => {
920 return match_pat(ms, s, ep + 1);
924 }
925 _ => return Ok(None),
926 }
927 }
928
929 match ms.pat.get(ep).copied() {
931 Some(b'?') => {
932 let res = match_pat(ms, s + 1, ep + 1)?;
934 if res.is_some() {
935 Ok(res)
936 } else {
937 match_pat(ms, s, ep + 1)
938 }
939 }
940 Some(b'+') => {
941 max_expand(ms, s + 1, p, ep)
943 }
944 Some(b'*') => {
945 max_expand(ms, s, p, ep)
947 }
948 Some(b'-') => {
949 min_expand(ms, s, p, ep)
951 }
952 _ => {
953 match_pat(ms, s + 1, ep)
955 }
956 }
957}
958
959fn lmemfind(haystack: &[u8], needle: &[u8]) -> Option<usize> {
967 if needle.is_empty() {
968 return Some(0);
969 }
970 if needle.len() > haystack.len() {
971 return None;
972 }
973 let first = needle[0];
974 let rest = &needle[1..];
975 let limit = haystack.len() - rest.len();
976 let mut s = 0;
977 while s <= limit {
978 if let Some(pos) = haystack[s..].iter().position(|&b| b == first) {
979 let pos = s + pos;
980 if pos + 1 + rest.len() <= haystack.len()
981 && &haystack[pos + 1..pos + 1 + rest.len()] == rest
982 {
983 return Some(pos);
984 }
985 s = pos + 1;
986 } else {
987 break;
988 }
989 }
990 None
991}
992
993fn nospecials(pat: &[u8]) -> bool {
996 !pat.iter().any(|b| SPECIALS.contains(b))
997}
998
999enum CaptureInfo<'a> {
1001 Position(i64),
1003 Bytes(&'a [u8]),
1005}
1006
1007fn get_one_capture<'a>(
1011 ms: &'a MatchState,
1012 i: usize,
1013 s: usize,
1014 e: usize,
1015) -> Result<CaptureInfo<'a>, LuaError> {
1016 if i >= ms.level as usize {
1017 if i != 0 {
1018 return Err(LuaError::runtime(format_args!(
1019 "invalid capture index %{}",
1020 i + 1
1021 )));
1022 }
1023 return Ok(CaptureInfo::Bytes(&ms.src[s..e]));
1025 }
1026 let cap = &ms.captures[i];
1027 if cap.len == CAP_UNFINISHED {
1028 return Err(LuaError::runtime(format_args!("unfinished capture")));
1029 }
1030 if cap.len == CAP_POSITION {
1031 return Ok(CaptureInfo::Position((cap.init + 1) as i64));
1032 }
1033 let len = cap.len as usize;
1034 Ok(CaptureInfo::Bytes(&ms.src[cap.init..cap.init + len]))
1035}
1036
1037fn push_captures(
1041 state: &mut LuaState,
1042 ms: &MatchState,
1043 s: usize,
1044 e: usize,
1045) -> Result<usize, LuaError> {
1046 let nlevels = if ms.level == 0 { 1 } else { ms.level as usize };
1047 state.ensure_stack(nlevels as i32, "too many captures")?;
1048 for i in 0..nlevels {
1049 match get_one_capture(ms, i, s, e)? {
1050 CaptureInfo::Position(n) => state.push(LuaValue::Int(n)),
1051 CaptureInfo::Bytes(b) => state.push_bytes(b)?,
1052 }
1053 }
1054 Ok(nlevels)
1055}
1056
1057fn str_find_aux(state: &mut LuaState, find: bool) -> Result<usize, LuaError> {
1064 let s_ref = match state.to_lua_string(1) {
1065 Some(r) => r,
1066 None => {
1067 state.check_arg_string(1)?;
1068 unreachable!("check_arg_string raises when arg #1 is not a string");
1069 }
1070 };
1071 let p_ref = match state.to_lua_string(2) {
1072 Some(r) => r,
1073 None => {
1074 state.check_arg_string(2)?;
1075 unreachable!("check_arg_string raises when arg #2 is not a string");
1076 }
1077 };
1078 let s: &[u8] = s_ref.as_bytes();
1079 let p: &[u8] = p_ref.as_bytes();
1080 let ls = s.len();
1081 let lp = p.len();
1082 let init_raw = state.opt_arg_integer(3, 1)?;
1083 let init = pos_relat_i(init_raw, ls).saturating_sub(1);
1084
1085 if init > ls {
1086 state.push(LuaValue::Nil);
1087 return Ok(1);
1088 }
1089
1090 if find && (state.arg_to_bool(4) || nospecials(p)) {
1091 if let Some(pos) = lmemfind(&s[init..], p) {
1093 let abs = init + pos;
1094 state.push(LuaValue::Int((abs + 1) as i64));
1095 state.push(LuaValue::Int((abs + lp) as i64));
1096 return Ok(2);
1097 }
1098 } else {
1099 let step_limit = state.sandbox_match_step_limit();
1100 let mut ms = MatchState::new(s, p, step_limit);
1101 let anchor = p.first() == Some(&b'^');
1102 let p_slice = if anchor { &p[1..] } else { p };
1103 ms.pat = p_slice;
1104
1105 let mut s1 = init;
1106 let mut matched: Option<usize> = None;
1107 loop {
1108 ms.reset_level();
1109 if let Some(res) = match_pat(&mut ms, s1, 0)? {
1110 matched = Some(res);
1111 break;
1112 }
1113 if ms.aborted || s1 >= ms.src.len() || anchor {
1114 break;
1115 }
1116 s1 += 1;
1117 }
1118
1119 if let Some(err) = state.sandbox_charge(ms.steps) {
1120 return Err(err);
1121 }
1122
1123 if let Some(res) = matched {
1124 if find {
1125 state.push(LuaValue::Int((s1 + 1) as i64));
1126 state.push(LuaValue::Int(res as i64));
1127 let nc = push_captures(state, &ms, 0, 0)?;
1128 return Ok(nc + 2);
1129 } else {
1130 return push_captures(state, &ms, s1, res);
1131 }
1132 }
1133 }
1134
1135 state.push(LuaValue::Nil);
1136 Ok(1)
1137}
1138
1139pub fn str_find(state: &mut LuaState) -> Result<usize, LuaError> {
1142 str_find_aux(state, true)
1143}
1144
1145pub fn str_match(state: &mut LuaState) -> Result<usize, LuaError> {
1148 str_find_aux(state, false)
1149}
1150
1151pub fn gmatch_aux(state: &mut LuaState) -> Result<usize, LuaError> {
1175 let upval = state.value_at(upvalue_index(1));
1176 let tbl = match upval {
1177 LuaValue::Table(t) => t,
1178 _ => return Ok(0),
1179 };
1180
1181 let s_val = tbl.get_int(1);
1182 let p_val = tbl.get_int(2);
1183 let (LuaValue::Str(s_str), LuaValue::Str(p_str)) = (&s_val, &p_val) else {
1184 return Ok(0);
1185 };
1186 let s: &[u8] = s_str.as_bytes();
1187 let p: &[u8] = p_str.as_bytes();
1188
1189 let pos = match tbl.get_int(3) {
1190 LuaValue::Int(n) => n,
1191 _ => 1,
1192 };
1193 let lastmatch_raw = match tbl.get_int(4) {
1194 LuaValue::Int(n) => n,
1195 _ => 0,
1196 };
1197 let last_match: Option<usize> = if lastmatch_raw <= 0 {
1198 None
1199 } else {
1200 Some((lastmatch_raw - 1) as usize)
1201 };
1202
1203 let ls = s.len();
1204 let start_pos = if pos < 1 { 0usize } else { (pos - 1) as usize };
1205
1206 let step_limit = state.sandbox_match_step_limit();
1207 let mut ms = MatchState::new(s, p, step_limit);
1208
1209 let mut src = start_pos;
1210 let mut hit: Option<(usize, usize)> = None;
1211 while src <= ls {
1212 ms.reset_level();
1213 if let Some(e) = match_pat(&mut ms, src, 0)? {
1214 if Some(e) != last_match {
1215 hit = Some((src, e));
1216 break;
1217 }
1218 }
1219 if ms.aborted {
1220 break;
1221 }
1222 src += 1;
1223 }
1224
1225 if let Some(err) = state.sandbox_charge(ms.steps) {
1226 return Err(err);
1227 }
1228
1229 if let Some((src, e)) = hit {
1230 let e_val = LuaValue::Int((e + 1) as i64);
1231 tbl.raw_set_int(state, 3, e_val.clone())?;
1232 tbl.raw_set_int(state, 4, e_val)?;
1233 return push_captures(state, &ms, src, e);
1234 }
1235
1236 Ok(0)
1237}
1238
1239pub fn gmatch(state: &mut LuaState) -> Result<usize, LuaError> {
1247 let s_ref = match state.to_lua_string(1) {
1248 Some(r) => r,
1249 None => {
1250 state.check_arg_string(1)?;
1251 unreachable!("check_arg_string raises when arg #1 is not a string");
1252 }
1253 };
1254 let ls = s_ref.len();
1255 match state.to_lua_string(2) {
1256 Some(_) => {}
1257 None => {
1258 state.check_arg_string(2)?;
1259 unreachable!("check_arg_string raises when arg #2 is not a string");
1260 }
1261 };
1262 let init_raw = state.opt_arg_integer(3, 1)?;
1263 let mut init = pos_relat_i(init_raw, ls).saturating_sub(1);
1264 if init > ls {
1265 init = ls + 1;
1266 }
1267
1268 lua_vm::api::set_top(state, 2)?;
1269
1270 state.create_table(4, 0)?;
1271 let tbl_idx = state.top();
1272 state.push_value_at(1)?;
1273 state.raw_seti(tbl_idx, 1)?;
1274 state.push_value_at(2)?;
1275 state.raw_seti(tbl_idx, 2)?;
1276 state.push(LuaValue::Int((init + 1) as i64));
1277 state.raw_seti(tbl_idx, 3)?;
1278 state.push(LuaValue::Int(0));
1279 state.raw_seti(tbl_idx, 4)?;
1280
1281 state.push_c_closure(gmatch_aux, 1)?;
1282 Ok(1)
1283}
1284
1285fn add_s(
1288 state: &mut LuaState,
1289 ms: &MatchState,
1290 buf: &mut Vec<u8>,
1291 s: usize,
1292 e: usize,
1293) -> Result<(), LuaError> {
1294 let news_bytes = state.to_lua_string_bytes(3).unwrap_or_default();
1295 let mut i = 0usize;
1296 while i < news_bytes.len() {
1297 if news_bytes[i] != L_ESC {
1298 buf.push(news_bytes[i]);
1299 i += 1;
1300 } else {
1301 i += 1; if i >= news_bytes.len() {
1303 break;
1304 }
1305 let c = news_bytes[i];
1306 if c == L_ESC {
1307 buf.push(L_ESC);
1308 } else if c == b'0' {
1309 buf.extend_from_slice(&ms.src[s..e]);
1310 } else if c.is_ascii_digit() {
1311 match get_one_capture(ms, (c - b'1') as usize, s, e)? {
1312 CaptureInfo::Position(n) => {
1313 let formatted = format!("{}", n).into_bytes();
1315 buf.extend_from_slice(&formatted);
1316 }
1317 CaptureInfo::Bytes(b) => {
1318 buf.extend_from_slice(b);
1319 }
1320 }
1321 } else {
1322 return Err(LuaError::runtime(format_args!(
1323 "invalid use of '{}' in replacement string",
1324 L_ESC as char
1325 )));
1326 }
1327 i += 1;
1328 }
1329 }
1330 Ok(())
1331}
1332
1333fn add_value(
1337 state: &mut LuaState,
1338 ms: &MatchState,
1339 buf: &mut Vec<u8>,
1340 s: usize,
1341 e: usize,
1342 tr: LuaType,
1343) -> Result<bool, LuaError> {
1344 match tr {
1345 LuaType::Function => {
1346 state.push_value_at(3)?;
1347 let n = push_captures(state, ms, s, e)?;
1348 state.call(n as i32, 1)?;
1349 }
1350 LuaType::Table => {
1351 match get_one_capture(ms, 0, s, e)? {
1352 CaptureInfo::Position(n) => state.push(LuaValue::Int(n)),
1353 CaptureInfo::Bytes(b) => state.push_bytes(b)?,
1354 }
1355 state.get_table(3)?;
1356 }
1357 _ => {
1358 add_s(state, ms, buf, s, e)?;
1360 return Ok(true);
1361 }
1362 }
1363
1364 let top_bool = state.arg_to_bool(-1);
1365 if !top_bool {
1366 state.pop_n(1);
1367 buf.extend_from_slice(&ms.src[s..e]);
1368 return Ok(false);
1369 }
1370 if state.type_at(-1) != LuaType::String {
1371 let tname = state.type_name_at(-1).to_owned();
1372 return Err(LuaError::runtime(format_args!(
1373 "invalid replacement value (a {})", tname.escape_ascii()
1374 )));
1375 }
1376 let v = state.to_bytes(-1).unwrap_or_default();
1377 state.pop();
1378 buf.extend_from_slice(&v);
1379 Ok(true)
1380}
1381
1382pub fn str_gsub(state: &mut LuaState) -> Result<usize, LuaError> {
1385 let src_bytes = state.check_arg_string(1)?;
1386 let pat_bytes = state.check_arg_string(2)?;
1387 let src_len = src_bytes.len();
1388 let max_s = state.opt_arg_integer(4, (src_len + 1) as i64)?;
1389 let tr = state.type_at(3);
1390
1391 if !matches!(tr, LuaType::Number | LuaType::String | LuaType::Function | LuaType::Table) {
1392 let v = state.arg(3);
1393 return Err(LuaError::type_arg_error(3, "string/function/table", &v));
1394 }
1395
1396 let src_owned = src_bytes;
1397 let pat_owned = pat_bytes;
1398
1399 let anchor = pat_owned.first() == Some(&b'^');
1400 let pat_slice = if anchor { &pat_owned[1..] } else { &pat_owned[..] };
1401
1402 let step_limit = state.sandbox_match_step_limit();
1403 let mut ms = MatchState::new(&src_owned, pat_slice, step_limit);
1404 let mut buf: Vec<u8> = Vec::new();
1405 let mut src_pos = 0usize;
1406 let mut last_match: Option<usize> = None;
1407 let mut n: i64 = 0;
1408 let mut changed = false;
1409
1410 while n < max_s {
1411 ms.reset_level();
1412 let maybe_e = match_pat(&mut ms, src_pos, 0)?;
1413 if let Some(e) = maybe_e {
1414 if last_match != Some(e) {
1415 n += 1;
1416 let delta = add_value(state, &ms, &mut buf, src_pos, e, tr)?;
1417 changed |= delta;
1418 src_pos = e;
1419 last_match = Some(e);
1420 } else if src_pos < ms.src.len() {
1421 buf.push(ms.src[src_pos]);
1422 src_pos += 1;
1423 } else {
1424 break;
1425 }
1426 } else if src_pos < ms.src.len() {
1427 buf.push(ms.src[src_pos]);
1428 src_pos += 1;
1429 } else {
1430 break;
1431 }
1432 if ms.aborted || anchor {
1433 break;
1434 }
1435 }
1436
1437 if let Some(err) = state.sandbox_charge(ms.steps) {
1438 return Err(err);
1439 }
1440
1441 if !changed {
1442 state.push_value_at(1)?;
1443 } else {
1444 buf.extend_from_slice(&ms.src[src_pos..]);
1445 state.push_bytes(&buf)?;
1446 }
1447 state.push(LuaValue::Int(n));
1448 Ok(2)
1449}
1450
1451fn adddigit(buf: &mut Vec<u8>, x: f64) -> f64 {
1458 let dd = x.floor();
1459 let d = dd as i32;
1460 let c = if d < 10 { b'0' + d as u8 } else { b'a' + (d - 10) as u8 };
1461 buf.push(c);
1462 x - dd
1463}
1464
1465fn num2straux(x: f64) -> Vec<u8> {
1470 format_hex_float(x, None)
1471}
1472
1473fn format_hex_float(x: f64, precision: Option<usize>) -> Vec<u8> {
1481 if x.is_nan() {
1482 return b"nan".to_vec();
1483 }
1484 if x.is_infinite() {
1485 return if x < 0.0 { b"-inf".to_vec() } else { b"inf".to_vec() };
1486 }
1487 if x == 0.0 {
1488 let sign: &[u8] = if x.is_sign_negative() { b"-" } else { b"" };
1489 return match precision {
1490 None => [sign, b"0x0p+0"].concat(),
1491 Some(0) => [sign, b"0x0p+0"].concat(),
1492 Some(p) => {
1493 let zeros = "0".repeat(p);
1494 [sign, b"0x0.", zeros.as_bytes(), b"p+0"].concat()
1495 }
1496 };
1497 }
1498
1499 let (m_raw, exp) = frexp(x);
1500 let mut buf: Vec<u8> = Vec::new();
1501 let mut m = m_raw;
1502 if m < 0.0 {
1503 buf.push(b'-');
1504 m = -m;
1505 }
1506 buf.extend_from_slice(b"0x");
1507
1508 let nbfd = 1;
1509 m = adddigit(&mut buf, m * (1 << nbfd) as f64);
1510 let e = exp - nbfd;
1511
1512 match precision {
1513 None => {
1514 if m > 0.0 {
1515 buf.push(b'.');
1516 while m > 0.0 {
1517 m = adddigit(&mut buf, m * 16.0);
1518 }
1519 }
1520 }
1521 Some(0) => {}
1522 Some(p) => {
1523 buf.push(b'.');
1524 for _ in 0..p {
1525 if m > 0.0 {
1526 m = adddigit(&mut buf, m * 16.0);
1527 } else {
1528 buf.push(b'0');
1529 }
1530 }
1531 }
1532 }
1533
1534 let exp_str = format!("p{:+}", e);
1535 buf.extend_from_slice(exp_str.as_bytes());
1536 buf
1537}
1538
1539fn frexp(x: f64) -> (f64, i32) {
1544 if x == 0.0 || x.is_nan() || x.is_infinite() {
1545 return (x, 0);
1546 }
1547 let bits = x.to_bits();
1548 let sign_bit = bits & 0x8000_0000_0000_0000u64;
1549 let exp_bits = ((bits >> 52) & 0x7FF) as i32;
1550 if exp_bits == 0 {
1551 let (m, e) = frexp(x * (1u64 << 52) as f64);
1552 return (m, e - 52);
1553 }
1554 let exp = exp_bits - 1022;
1555 let mantissa_bits = sign_bit | (bits & 0x000F_FFFF_FFFF_FFFF) | 0x3FE0_0000_0000_0000;
1556 (f64::from_bits(mantissa_bits), exp)
1557}
1558
1559fn quotefloat(n: f64) -> Vec<u8> {
1562 if n == f64::INFINITY {
1563 return b"1e9999".to_vec();
1564 } else if n == f64::NEG_INFINITY {
1565 return b"-1e9999".to_vec();
1566 } else if n.is_nan() {
1567 return b"(0/0)".to_vec();
1568 }
1569 let buf = num2straux(n);
1571 if !buf.contains(&b'.') && !buf.contains(&b'p') {
1572 }
1575 buf
1576}
1577
1578fn addquoted(buf: &mut Vec<u8>, s: &[u8]) {
1581 buf.push(b'"');
1582 for (idx, &c) in s.iter().enumerate() {
1583 if c == b'"' || c == b'\\' || c == b'\n' {
1584 buf.push(b'\\');
1585 buf.push(c);
1586 } else if c.is_ascii_control() {
1587 let next_is_digit = s.get(idx + 1).map_or(false, |n| n.is_ascii_digit());
1588 let formatted = if next_is_digit {
1589 format!("\\{:03}", c)
1590 } else {
1591 format!("\\{}", c)
1592 };
1593 buf.extend_from_slice(formatted.as_bytes());
1594 } else {
1595 buf.push(c);
1596 }
1597 }
1598 buf.push(b'"');
1599}
1600
1601fn addliteral(state: &mut LuaState, buf: &mut Vec<u8>, arg: i32) -> Result<(), LuaError> {
1604 match state.type_at(arg) {
1605 LuaType::String => {
1606 let s = state.check_arg_string(arg)?.to_vec();
1607 addquoted(buf, &s);
1608 }
1609 LuaType::Number => {
1610 if state.is_integer(arg) {
1611 let n = state.to_integer(arg).unwrap_or(0);
1612 let formatted = if n == i64::MIN {
1613 format!("0x{:016x}", n as u64)
1614 } else {
1615 format!("{}", n)
1616 };
1617 buf.extend_from_slice(formatted.as_bytes());
1618 } else {
1619 let n = state.to_number(arg).unwrap_or(0.0);
1620 let hex = quotefloat(n);
1621 buf.extend_from_slice(&hex);
1622 }
1623 }
1624 LuaType::Nil => {
1625 buf.extend_from_slice(b"nil");
1626 }
1627 LuaType::Boolean => {
1628 buf.extend_from_slice(if state.to_boolean(arg) { b"true" } else { b"false" });
1629 }
1630 _ => {
1631 return Err(LuaError::arg_error(arg, "value has no literal form"));
1632 }
1633 }
1634 Ok(())
1635}
1636
1637
1638const FMT_FLAGS_F: &[u8] = b"-+#0 ";
1640const FMT_FLAGS_X: &[u8] = b"-#0";
1641const FMT_FLAGS_I: &[u8] = b"-+0 ";
1642const FMT_FLAGS_U: &[u8] = b"-0";
1643const FMT_FLAGS_C: &[u8] = b"-";
1644
1645fn check_conv_spec(form: &[u8], flags: &[u8], allow_precision: bool) -> Result<(), LuaError> {
1655 let mut i = 1usize; while i < form.len() && flags.contains(&form[i]) {
1657 i += 1;
1658 }
1659 if i < form.len() && form[i] == b'0' {
1660 return Err(LuaError::runtime(format_args!("invalid conversion specification")));
1661 }
1662 if i < form.len() && form[i].is_ascii_digit() {
1663 i += 1;
1664 if i < form.len() && form[i].is_ascii_digit() {
1665 i += 1;
1666 }
1667 }
1668 if allow_precision && i < form.len() && form[i] == b'.' {
1669 i += 1;
1670 if i < form.len() && form[i].is_ascii_digit() {
1671 i += 1;
1672 if i < form.len() && form[i].is_ascii_digit() {
1673 i += 1;
1674 }
1675 }
1676 }
1677 if i != form.len() - 1 {
1678 return Err(LuaError::runtime(format_args!("invalid conversion specification")));
1679 }
1680 Ok(())
1681}
1682
1683#[derive(Default)]
1685struct FmtSpec {
1686 left_align: bool,
1687 plus_sign: bool,
1688 space_sign: bool,
1689 alt_form: bool,
1690 zero_pad: bool,
1691 width: usize,
1692 precision: Option<usize>,
1693}
1694
1695fn parse_fmt_spec(spec: &[u8]) -> FmtSpec {
1696 let mut s = FmtSpec::default();
1697 let mut i = 0;
1698 while i < spec.len() {
1699 match spec[i] {
1700 b'-' => s.left_align = true,
1701 b'+' => s.plus_sign = true,
1702 b' ' => s.space_sign = true,
1703 b'#' => s.alt_form = true,
1704 b'0' => s.zero_pad = true,
1705 _ => break,
1706 }
1707 i += 1;
1708 }
1709 while i < spec.len() && spec[i].is_ascii_digit() {
1710 s.width = s.width * 10 + (spec[i] - b'0') as usize;
1711 i += 1;
1712 }
1713 if i < spec.len() && spec[i] == b'.' {
1714 i += 1;
1715 let mut p = 0usize;
1716 while i < spec.len() && spec[i].is_ascii_digit() {
1717 p = p * 10 + (spec[i] - b'0') as usize;
1718 i += 1;
1719 }
1720 s.precision = Some(p);
1721 }
1722 s
1723}
1724
1725fn pad_str(buf: &mut Vec<u8>, body: &[u8], spec: &FmtSpec) {
1726 let body = match spec.precision {
1727 Some(p) if body.len() > p => &body[..p],
1728 _ => body,
1729 };
1730 if body.len() >= spec.width {
1731 buf.extend_from_slice(body);
1732 return;
1733 }
1734 let pad = spec.width - body.len();
1735 if spec.left_align {
1736 buf.extend_from_slice(body);
1737 for _ in 0..pad { buf.push(b' '); }
1738 } else {
1739 for _ in 0..pad { buf.push(b' '); }
1740 buf.extend_from_slice(body);
1741 }
1742}
1743
1744fn pad_int(buf: &mut Vec<u8>, sign_prefix: &[u8], digits: &[u8], spec: &FmtSpec) {
1745 let min_digits = spec.precision.unwrap_or(0);
1746 let zeroes_for_prec = if digits.len() < min_digits { min_digits - digits.len() } else { 0 };
1747 let core_len = sign_prefix.len() + zeroes_for_prec + digits.len();
1748 if core_len >= spec.width {
1749 buf.extend_from_slice(sign_prefix);
1750 for _ in 0..zeroes_for_prec { buf.push(b'0'); }
1751 buf.extend_from_slice(digits);
1752 return;
1753 }
1754 let pad = spec.width - core_len;
1755 let use_zero_pad = spec.zero_pad && !spec.left_align && spec.precision.is_none();
1756 if spec.left_align {
1757 buf.extend_from_slice(sign_prefix);
1758 for _ in 0..zeroes_for_prec { buf.push(b'0'); }
1759 buf.extend_from_slice(digits);
1760 for _ in 0..pad { buf.push(b' '); }
1761 } else if use_zero_pad {
1762 buf.extend_from_slice(sign_prefix);
1763 for _ in 0..pad { buf.push(b'0'); }
1764 for _ in 0..zeroes_for_prec { buf.push(b'0'); }
1765 buf.extend_from_slice(digits);
1766 } else {
1767 for _ in 0..pad { buf.push(b' '); }
1768 buf.extend_from_slice(sign_prefix);
1769 for _ in 0..zeroes_for_prec { buf.push(b'0'); }
1770 buf.extend_from_slice(digits);
1771 }
1772}
1773
1774fn signed_int_parts(n: i64, spec: &FmtSpec) -> (Vec<u8>, Vec<u8>) {
1775 if n == 0 && spec.precision == Some(0) {
1776 return (Vec::new(), Vec::new());
1777 }
1778 let (sign, abs_digits) = if n < 0 {
1779 (b"-".to_vec(), {
1780 let u = (n as i128).unsigned_abs();
1781 format!("{}", u).into_bytes()
1782 })
1783 } else {
1784 let s: Vec<u8> = if spec.plus_sign {
1785 b"+".to_vec()
1786 } else if spec.space_sign {
1787 b" ".to_vec()
1788 } else {
1789 Vec::new()
1790 };
1791 (s, format!("{}", n).into_bytes())
1792 };
1793 (sign, abs_digits)
1794}
1795
1796fn unsigned_int_parts(n: u64, base: u32, upper: bool, spec: &FmtSpec) -> (Vec<u8>, Vec<u8>) {
1797 let digits = if n == 0 && spec.precision == Some(0) {
1798 Vec::new()
1799 } else {
1800 match base {
1801 8 => format!("{:o}", n).into_bytes(),
1802 16 if upper => format!("{:X}", n).into_bytes(),
1803 16 => format!("{:x}", n).into_bytes(),
1804 _ => format!("{}", n).into_bytes(),
1805 }
1806 };
1807 let prefix: Vec<u8> = if spec.alt_form && n != 0 {
1808 match base {
1809 8 => b"0".to_vec(),
1810 16 if upper => b"0X".to_vec(),
1811 16 => b"0x".to_vec(),
1812 _ => Vec::new(),
1813 }
1814 } else {
1815 Vec::new()
1816 };
1817 (prefix, digits)
1818}
1819
1820fn format_float(n: f64, conv: u8, spec: &FmtSpec) -> Vec<u8> {
1821 let prec = spec.precision.unwrap_or(6);
1822 if n.is_nan() {
1823 return if conv.is_ascii_uppercase() { b"NAN".to_vec() } else { b"nan".to_vec() };
1824 }
1825 if n.is_infinite() {
1826 let s: &[u8] = if conv.is_ascii_uppercase() {
1827 if n < 0.0 { b"-INF" } else { b"INF" }
1828 } else if n < 0.0 { b"-inf" } else { b"inf" };
1829 return s.to_vec();
1830 }
1831 match conv {
1832 b'f' | b'F' => {
1833 let mut result = format!("{:.*}", prec, n).into_bytes();
1834 if spec.alt_form && !result.contains(&b'.') {
1835 result.push(b'.');
1836 }
1837 result
1838 }
1839 b'e' => format_exp(n, prec, false, spec.alt_form),
1840 b'E' => {
1841 let mut v = format_exp(n, prec, false, spec.alt_form);
1842 for b in v.iter_mut() { if *b == b'e' { *b = b'E'; } }
1843 v
1844 }
1845 b'g' | b'G' => {
1846 let p = if prec == 0 { 1 } else { prec };
1847 let v = format_g(n, p, spec.alt_form);
1848 if conv == b'G' {
1849 v.into_iter().map(|b| if b == b'e' { b'E' } else { b }).collect()
1850 } else { v }
1851 }
1852 _ => format!("{}", n).into_bytes(),
1853 }
1854}
1855
1856fn format_exp(n: f64, prec: usize, _upper: bool, alt: bool) -> Vec<u8> {
1857 if n == 0.0 {
1858 let mantissa: String = if prec == 0 {
1859 if alt { "0.".to_string() } else { "0".to_string() }
1860 } else {
1861 format!("0.{}", "0".repeat(prec))
1862 };
1863 return format!("{}e+00", mantissa).into_bytes();
1864 }
1865 let abs = n.abs();
1866 let exp = abs.log10().floor() as i32;
1867 let mantissa = n / 10f64.powi(exp);
1868 let mantissa_str = format!("{:.*}", prec, mantissa);
1869 let (mant_final, exp_final) = if let Some(dot_pos) = mantissa_str.find('.') {
1870 let int_part = &mantissa_str[..dot_pos];
1871 let abs_int = int_part.trim_start_matches('-');
1872 if abs_int.len() > 1 {
1873 let new_mant = if prec == 0 {
1874 mantissa_str[..mantissa_str.len()-1].to_string()
1875 } else {
1876 let neg = if int_part.starts_with('-') { "-" } else { "" };
1877 let frac = &mantissa_str[dot_pos+1..];
1878 format!("{}{}.{}{}", neg, &abs_int[..1], &abs_int[1..], frac)
1879 };
1880 (new_mant, exp + (abs_int.len() as i32 - 1))
1881 } else {
1882 (mantissa_str, exp)
1883 }
1884 } else if mantissa_str.trim_start_matches('-').len() > 1 {
1885 let neg = if mantissa_str.starts_with('-') { "-" } else { "" };
1886 let body = mantissa_str.trim_start_matches('-');
1887 let bumped = format!("{}{}.{}", neg, &body[..1], &body[1..]);
1888 (bumped, exp + (body.len() as i32 - 1))
1889 } else {
1890 (mantissa_str, exp)
1891 };
1892 let sign = if exp_final < 0 { '-' } else { '+' };
1893 let mant_out = if alt && !mant_final.contains('.') {
1894 format!("{}.", mant_final)
1895 } else { mant_final };
1896 format!("{}e{}{:02}", mant_out, sign, exp_final.abs()).into_bytes()
1897}
1898
1899fn format_g(n: f64, prec: usize, alt: bool) -> Vec<u8> {
1900 if n == 0.0 {
1901 return if alt { format!("0.{}", "0".repeat(prec.saturating_sub(1))).into_bytes() } else { b"0".to_vec() };
1902 }
1903 let abs = n.abs();
1904 let exp = abs.log10().floor() as i32;
1905 if exp < -4 || exp >= prec as i32 {
1906 let ep = if prec == 0 { 0 } else { prec - 1 };
1907 let mut v = format_exp(n, ep, false, alt);
1908 if !alt {
1909 v = strip_trailing_zeros_exp(&v);
1910 }
1911 v
1912 } else {
1913 let dec_places = (prec as i32 - 1 - exp).max(0) as usize;
1914 let mut v = format!("{:.*}", dec_places, n).into_bytes();
1915 if !alt {
1916 v = strip_trailing_zeros_fixed(&v);
1917 }
1918 v
1919 }
1920}
1921
1922fn strip_trailing_zeros_fixed(s: &[u8]) -> Vec<u8> {
1923 if !s.contains(&b'.') { return s.to_vec(); }
1924 let mut end = s.len();
1925 while end > 0 && s[end-1] == b'0' { end -= 1; }
1926 if end > 0 && s[end-1] == b'.' { end -= 1; }
1927 s[..end].to_vec()
1928}
1929
1930fn strip_trailing_zeros_exp(s: &[u8]) -> Vec<u8> {
1931 let e_pos = match s.iter().position(|&b| b == b'e' || b == b'E') {
1932 Some(p) => p,
1933 None => return s.to_vec(),
1934 };
1935 let mantissa = &s[..e_pos];
1936 let exp_part = &s[e_pos..];
1937 if !mantissa.contains(&b'.') {
1938 let mut out = mantissa.to_vec();
1939 out.extend_from_slice(exp_part);
1940 return out;
1941 }
1942 let mut end = mantissa.len();
1943 while end > 0 && mantissa[end-1] == b'0' { end -= 1; }
1944 if end > 0 && mantissa[end-1] == b'.' { end -= 1; }
1945 let mut out = mantissa[..end].to_vec();
1946 out.extend_from_slice(exp_part);
1947 out
1948}
1949
1950pub fn str_format(state: &mut LuaState) -> Result<usize, LuaError> {
1953 let top = state.get_top();
1954 let mut arg = 1i32;
1955 let fmt_bytes = state.check_arg_string(1)?.to_vec();
1956 let mut buf: Vec<u8> = Vec::new();
1957 let mut i = 0usize;
1958
1959 while i < fmt_bytes.len() {
1960 let c = fmt_bytes[i];
1961 if c != L_ESC {
1962 buf.push(c);
1963 i += 1;
1964 continue;
1965 }
1966 i += 1;
1967 if i >= fmt_bytes.len() {
1968 break;
1969 }
1970 if fmt_bytes[i] == L_ESC {
1971 buf.push(L_ESC);
1972 i += 1;
1973 continue;
1974 }
1975
1976 arg += 1;
1978 if arg > top {
1979 return Err(LuaError::arg_error(arg, "no value"));
1980 }
1981
1982 let spec_start = i - 1; while i < fmt_bytes.len() && b"-+#0 ".contains(&fmt_bytes[i]) {
1986 i += 1;
1987 }
1988 if i < fmt_bytes.len() && fmt_bytes[i] != b'0' {
1990 while i < fmt_bytes.len() && fmt_bytes[i].is_ascii_digit() {
1991 i += 1;
1992 }
1993 }
1994 if i < fmt_bytes.len() && fmt_bytes[i] == b'.' {
1996 i += 1;
1997 while i < fmt_bytes.len() && fmt_bytes[i].is_ascii_digit() {
1998 i += 1;
1999 }
2000 }
2001
2002 if i >= fmt_bytes.len() {
2003 return Err(LuaError::runtime(format_args!("invalid conversion specification")));
2004 }
2005
2006 let conv = fmt_bytes[i];
2007 i += 1;
2008
2009 let spec_slice = &fmt_bytes[spec_start + 1..i - 1];
2010 let form = &fmt_bytes[spec_start..i];
2011
2012 if spec_slice.len() + 1 >= 22 {
2014 return Err(LuaError::runtime(format_args!("invalid format (too long)")));
2015 }
2016
2017 let spec = parse_fmt_spec(spec_slice);
2018
2019 match conv {
2020 b'c' => {
2021 check_conv_spec(form, FMT_FLAGS_C, false)?;
2022 let n = state.check_arg_integer(arg)?;
2023 let body = vec![n as u8];
2024 pad_str(&mut buf, &body, &spec);
2025 }
2026 b'd' | b'i' => {
2027 check_conv_spec(form, FMT_FLAGS_I, true)?;
2028 let n = state.check_arg_integer(arg)?;
2029 let (sign, digits) = signed_int_parts(n, &spec);
2030 pad_int(&mut buf, &sign, &digits, &spec);
2031 }
2032 b'u' => {
2033 check_conv_spec(form, FMT_FLAGS_U, true)?;
2034 let n = state.check_arg_integer(arg)? as u64;
2035 let (prefix, digits) = unsigned_int_parts(n, 10, false, &spec);
2036 pad_int(&mut buf, &prefix, &digits, &spec);
2037 }
2038 b'o' => {
2039 check_conv_spec(form, FMT_FLAGS_X, true)?;
2040 let n = state.check_arg_integer(arg)? as u64;
2041 let (prefix, digits) = unsigned_int_parts(n, 8, false, &spec);
2042 pad_int(&mut buf, &prefix, &digits, &spec);
2043 }
2044 b'x' => {
2045 check_conv_spec(form, FMT_FLAGS_X, true)?;
2046 let n = state.check_arg_integer(arg)? as u64;
2047 let (prefix, digits) = unsigned_int_parts(n, 16, false, &spec);
2048 pad_int(&mut buf, &prefix, &digits, &spec);
2049 }
2050 b'X' => {
2051 check_conv_spec(form, FMT_FLAGS_X, true)?;
2052 let n = state.check_arg_integer(arg)? as u64;
2053 let (prefix, digits) = unsigned_int_parts(n, 16, true, &spec);
2054 pad_int(&mut buf, &prefix, &digits, &spec);
2055 }
2056 b'a' | b'A' => {
2057 check_conv_spec(form, FMT_FLAGS_F, true)?;
2058 let n = state.check_arg_number(arg)?;
2059 let body = format_hex_float(n, spec.precision);
2060 let body: Vec<u8> = if conv == b'A' {
2061 body.into_iter().map(|b| b.to_ascii_uppercase()).collect()
2062 } else {
2063 body
2064 };
2065 let (sign, digits): (Vec<u8>, Vec<u8>) =
2066 if !body.is_empty() && (body[0] == b'-' || body[0] == b'+') {
2067 (vec![body[0]], body[1..].to_vec())
2068 } else if spec.plus_sign {
2069 (b"+".to_vec(), body)
2070 } else if spec.space_sign {
2071 (b" ".to_vec(), body)
2072 } else {
2073 (Vec::new(), body)
2074 };
2075 let no_prec_spec = FmtSpec {
2076 left_align: spec.left_align,
2077 plus_sign: spec.plus_sign,
2078 space_sign: spec.space_sign,
2079 alt_form: spec.alt_form,
2080 zero_pad: spec.zero_pad,
2081 width: spec.width,
2082 precision: None,
2083 };
2084 pad_int(&mut buf, &sign, &digits, &no_prec_spec);
2085 }
2086 b'f' | b'e' | b'E' | b'g' | b'G' => {
2087 check_conv_spec(form, FMT_FLAGS_F, true)?;
2088 let n = state.check_arg_number(arg)?;
2089 let body = format_float(n, conv, &spec);
2090 let (sign, digits): (Vec<u8>, Vec<u8>) = if !body.is_empty() && (body[0] == b'-' || body[0] == b'+') {
2091 (vec![body[0]], body[1..].to_vec())
2092 } else if n >= 0.0 && spec.plus_sign {
2093 (b"+".to_vec(), body)
2094 } else if n >= 0.0 && spec.space_sign {
2095 (b" ".to_vec(), body)
2096 } else {
2097 (Vec::new(), body)
2098 };
2099 let no_prec_spec = FmtSpec {
2100 left_align: spec.left_align,
2101 plus_sign: spec.plus_sign,
2102 space_sign: spec.space_sign,
2103 alt_form: spec.alt_form,
2104 zero_pad: spec.zero_pad,
2105 width: spec.width,
2106 precision: None,
2107 };
2108 pad_int(&mut buf, &sign, &digits, &no_prec_spec);
2109 }
2110 b'p' => {
2111 check_conv_spec(form, FMT_FLAGS_C, false)?;
2112 let s: Vec<u8> = match lua_vm::api::to_pointer(state, arg) {
2113 Some(p) => format!("0x{:x}", p).into_bytes(),
2114 None => b"(null)".to_vec(),
2115 };
2116 pad_str(&mut buf, &s, &FmtSpec { precision: None, ..spec });
2117 }
2118 b'q' => {
2119 if form.len() > 2 {
2120 return Err(LuaError::runtime(format_args!(
2121 "specifier '%q' cannot have modifiers"
2122 )));
2123 }
2124 addliteral(state, &mut buf, arg)?;
2125 }
2126 b's' => {
2127 check_conv_spec(form, FMT_FLAGS_C, true)?;
2128 let s = state.to_display_string(arg)?;
2129 let has_modifiers = spec.width != 0 || spec.precision.is_some();
2130 if has_modifiers && s.contains(&0u8) {
2131 return Err(LuaError::arg_error(
2132 arg,
2133 "string contains zeros",
2134 ));
2135 }
2136 pad_str(&mut buf, &s, &spec);
2137 state.pop_n(1);
2138 }
2139 _ => {
2140 return Err(LuaError::runtime(format_args!(
2141 "invalid conversion '%{}' to 'format'", conv as char
2142 )));
2143 }
2144 }
2145 }
2146
2147 state.push_bytes(&buf)?;
2148 Ok(1)
2149}
2150
2151fn is_digit(c: u8) -> bool {
2157 c.is_ascii_digit()
2158}
2159
2160fn getnum(fmt: &[u8], pos: &mut usize, df: i32) -> i32 {
2163 if *pos >= fmt.len() || !is_digit(fmt[*pos]) {
2164 return df;
2165 }
2166 let mut a = 0i32;
2167 while *pos < fmt.len() && is_digit(fmt[*pos]) {
2168 a = a * 10 + (fmt[*pos] - b'0') as i32;
2169 *pos += 1;
2170 if a > (i32::MAX - 9) / 10 {
2171 break;
2172 }
2173 }
2174 a
2175}
2176
2177fn getnumlimit(fmt: &[u8], pos: &mut usize, df: i32) -> Result<usize, LuaError> {
2180 let sz = getnum(fmt, pos, df);
2181 if sz > MAX_INT_SIZE as i32 || sz <= 0 {
2182 return Err(LuaError::runtime(format_args!(
2183 "integral size ({}) out of limits [1,{}]",
2184 sz, MAX_INT_SIZE
2185 )));
2186 }
2187 Ok(sz as usize)
2188}
2189
2190fn getoption(h: &mut Header, fmt: &[u8], pos: &mut usize, size: &mut usize) -> Result<KOption, LuaError> {
2193 const NATIVE_MAX_ALIGN: usize = std::mem::align_of::<f64>();
2195
2196 if *pos >= fmt.len() {
2197 return Ok(KOption::Nop);
2198 }
2199 let opt = fmt[*pos];
2200 *pos += 1;
2201 *size = 0;
2202
2203 match opt {
2204 b'b' => { *size = 1; Ok(KOption::Int) }
2205 b'B' => { *size = 1; Ok(KOption::Uint) }
2206 b'h' => { *size = 2; Ok(KOption::Int) }
2207 b'H' => { *size = 2; Ok(KOption::Uint) }
2208 b'l' => { *size = 8; Ok(KOption::Int) } b'L' => { *size = 8; Ok(KOption::Uint) }
2210 b'j' => { *size = SZINT; Ok(KOption::Int) }
2211 b'J' => { *size = SZINT; Ok(KOption::Uint) }
2212 b'T' => { *size = std::mem::size_of::<usize>(); Ok(KOption::Uint) }
2213 b'f' => { *size = 4; Ok(KOption::Float) }
2214 b'n' => { *size = 8; Ok(KOption::Number) } b'd' => { *size = 8; Ok(KOption::Double) } b'i' => { *size = getnumlimit(fmt, pos, 4)?; Ok(KOption::Int) }
2217 b'I' => { *size = getnumlimit(fmt, pos, 4)?; Ok(KOption::Uint) }
2218 b's' => { *size = getnumlimit(fmt, pos, std::mem::size_of::<usize>() as i32)?; Ok(KOption::Kstring) }
2219 b'c' => {
2220 let n = getnum(fmt, pos, -1);
2221 if n == -1 {
2222 return Err(LuaError::runtime(format_args!("missing size for format option 'c'")));
2223 }
2224 *size = n as usize;
2225 Ok(KOption::Char)
2226 }
2227 b'z' => Ok(KOption::Zstr),
2228 b'x' => { *size = 1; Ok(KOption::Padding) }
2229 b'X' => Ok(KOption::Paddalign),
2230 b' ' => Ok(KOption::Nop),
2231 b'<' => { h.is_little = true; Ok(KOption::Nop) }
2232 b'>' => { h.is_little = false; Ok(KOption::Nop) }
2233 b'=' => { h.is_little = cfg!(target_endian = "little"); Ok(KOption::Nop) }
2234 b'!' => {
2235 let n = getnum(fmt, pos, NATIVE_MAX_ALIGN as i32);
2236 h.max_align = getnumlimit(fmt, pos, n)?;
2237 Ok(KOption::Nop)
2238 }
2239 _ => Err(LuaError::runtime(format_args!("invalid format option '{}'", opt as char)))
2240 }
2241}
2242
2243fn getdetails(
2246 h: &mut Header,
2247 total_size: usize,
2248 fmt: &[u8],
2249 pos: &mut usize,
2250 psize: &mut usize,
2251 ntoalign: &mut usize,
2252) -> Result<KOption, LuaError> {
2253 let opt = getoption(h, fmt, pos, psize)?;
2254 let mut align = *psize;
2255
2256 if opt == KOption::Paddalign {
2257 if *pos >= fmt.len() {
2258 return Err(LuaError::arg_error(1, "invalid next option for option 'X'"));
2259 }
2260 let mut dummy_size = 0usize;
2261 let next_opt = getoption(h, fmt, pos, &mut dummy_size)?;
2262 align = dummy_size;
2263 if next_opt == KOption::Char || align == 0 {
2264 return Err(LuaError::arg_error(1, "invalid next option for option 'X'"));
2265 }
2266 }
2267
2268 if align <= 1 || opt == KOption::Char {
2269 *ntoalign = 0;
2270 } else {
2271 if align > h.max_align {
2272 align = h.max_align;
2273 }
2274 if (align & (align - 1)) != 0 {
2275 return Err(LuaError::arg_error(1, "format asks for alignment not power of 2"));
2276 }
2277 *ntoalign = (align - (total_size & (align - 1))) & (align - 1);
2278 }
2279 Ok(opt)
2280}
2281
2282fn packint(buf: &mut Vec<u8>, mut n: u64, is_little: bool, size: usize, neg: bool) {
2285 let start = buf.len();
2286 buf.resize(start + size, 0);
2287 let slice = &mut buf[start..start + size];
2288 for i in 0..size {
2290 slice[if is_little { i } else { size - 1 - i }] = (n & MC as u64) as u8;
2291 n >>= NB;
2292 }
2293 if neg && size > SZINT {
2295 for i in SZINT..size {
2296 slice[if is_little { i } else { size - 1 - i }] = MC;
2297 }
2298 }
2299}
2300
2301fn copywithendian(dest: &mut [u8], src: &[u8], is_little: bool) {
2304 debug_assert_eq!(dest.len(), src.len());
2305 if is_little == cfg!(target_endian = "little") {
2306 dest.copy_from_slice(src);
2307 } else {
2308 for (d, s) in dest.iter_mut().zip(src.iter().rev()) {
2309 *d = *s;
2310 }
2311 }
2312}
2313
2314fn unpackint(_state: &LuaState, data: &[u8], is_little: bool, size: usize, is_signed: bool) -> Result<i64, LuaError> {
2317 let limit = size.min(SZINT);
2318 let mut res: u64 = 0;
2319 for i in (0..limit).rev() {
2320 res <<= NB;
2321 let byte_idx = if is_little { i } else { size - 1 - i };
2322 res |= data[byte_idx] as u64;
2323 }
2324
2325 if size < SZINT {
2326 if is_signed {
2327 let mask: u64 = 1u64 << (size * NB as usize - 1);
2328 res = (res ^ mask).wrapping_sub(mask);
2329 }
2330 } else if size > SZINT {
2331 let mask = if !is_signed || (res as i64) >= 0 { 0u8 } else { MC };
2332 for i in limit..size {
2333 let byte_idx = if is_little { i } else { size - 1 - i };
2334 if data[byte_idx] != mask {
2335 return Err(LuaError::runtime(format_args!(
2336 "{}-byte integer does not fit into Lua Integer", size
2337 )));
2338 }
2339 }
2340 }
2341 Ok(res as i64)
2342}
2343
2344pub fn str_pack(state: &mut LuaState) -> Result<usize, LuaError> {
2347 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2348 let fmt = &fmt_bytes[..];
2349 let mut h = Header::new();
2350 let mut arg = 1i32;
2351 let mut total_size = 0usize;
2352 let mut buf: Vec<u8> = Vec::new();
2353 let mut pos = 0usize;
2354
2355 while pos < fmt.len() {
2356 let mut size = 0usize;
2357 let mut ntoalign = 0usize;
2358 let opt = getdetails(&mut h, total_size, fmt, &mut pos, &mut size, &mut ntoalign)?;
2359 total_size += ntoalign + size;
2360 for _ in 0..ntoalign {
2361 buf.push(PACK_PAD_BYTE);
2362 }
2363 arg += 1;
2364
2365 match opt {
2366 KOption::Int => {
2367 let n = state.check_arg_integer(arg)?;
2368 if size < SZINT {
2369 let lim: i64 = 1i64 << (size * NB as usize - 1);
2370 if !(-lim <= n && n < lim) {
2371 return Err(LuaError::arg_error(arg, "integer overflow"));
2372 }
2373 }
2374 packint(&mut buf, n as u64, h.is_little, size, n < 0);
2375 }
2376 KOption::Uint => {
2377 let n = state.check_arg_integer(arg)?;
2378 if size < SZINT {
2379 let lim: u64 = 1u64 << (size * NB as usize);
2380 if (n as u64) >= lim {
2381 return Err(LuaError::arg_error(arg, "unsigned overflow"));
2382 }
2383 }
2384 packint(&mut buf, n as u64, h.is_little, size, false);
2385 }
2386 KOption::Float => {
2387 let f = state.check_arg_number(arg)? as f32;
2388 let start = buf.len();
2389 buf.resize(start + 4, 0);
2390 copywithendian(&mut buf[start..start + 4], &f.to_bits().to_ne_bytes(), h.is_little);
2391 }
2392 KOption::Number => {
2393 let f = state.check_arg_number(arg)?;
2394 let start = buf.len();
2395 buf.resize(start + 8, 0);
2396 copywithendian(&mut buf[start..start + 8], &f.to_bits().to_ne_bytes(), h.is_little);
2397 }
2398 KOption::Double => {
2399 let f = state.check_arg_number(arg)? as f64;
2400 let start = buf.len();
2401 buf.resize(start + 8, 0);
2402 copywithendian(&mut buf[start..start + 8], &f.to_bits().to_ne_bytes(), h.is_little);
2403 }
2404 KOption::Char => {
2405 let s = state.check_arg_string(arg)?.to_vec();
2406 if s.len() > size {
2407 return Err(LuaError::arg_error(arg, "string longer than given size"));
2408 }
2409 buf.extend_from_slice(&s);
2410 let pad = size - s.len();
2411 for _ in 0..pad {
2412 buf.push(PACK_PAD_BYTE);
2413 }
2414 }
2415 KOption::Kstring => {
2416 let s = state.check_arg_string(arg)?.to_vec();
2417 let len = s.len();
2418 if size < SZINT && len >= (1usize << (size * 8)) {
2419 return Err(LuaError::arg_error(arg, "string length does not fit in given size"));
2420 }
2421 packint(&mut buf, len as u64, h.is_little, size, false);
2422 buf.extend_from_slice(&s);
2423 total_size += len;
2424 }
2425 KOption::Zstr => {
2426 let s = state.check_arg_string(arg)?.to_vec();
2427 if s.contains(&0) {
2428 return Err(LuaError::arg_error(arg, "string contains zeros"));
2429 }
2430 buf.extend_from_slice(&s);
2431 buf.push(0);
2432 total_size += s.len() + 1;
2433 }
2434 KOption::Padding => {
2435 buf.push(PACK_PAD_BYTE);
2436 arg -= 1; }
2438 KOption::Paddalign | KOption::Nop => {
2439 arg -= 1; }
2441 }
2442 }
2443
2444 state.push_bytes(&buf)?;
2445 Ok(1)
2446}
2447
2448pub fn str_packsize(state: &mut LuaState) -> Result<usize, LuaError> {
2451 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2452 let fmt = &fmt_bytes[..];
2453 let mut h = Header::new();
2454 let mut total_size = 0usize;
2455 let mut pos = 0usize;
2456
2457 while pos < fmt.len() {
2458 let mut size = 0usize;
2459 let mut ntoalign = 0usize;
2460 let opt = getdetails(&mut h, total_size, fmt, &mut pos, &mut size, &mut ntoalign)?;
2461 if opt == KOption::Kstring || opt == KOption::Zstr {
2462 return Err(LuaError::arg_error(1, "variable-length format"));
2463 }
2464 let space = ntoalign + size;
2465 if total_size > PACK_MAXSIZE - space {
2466 return Err(LuaError::arg_error(1, "format result too large"));
2467 }
2468 total_size += space;
2469 }
2470 state.push(LuaValue::Int(total_size as i64));
2471 Ok(1)
2472}
2473
2474pub fn str_unpack(state: &mut LuaState) -> Result<usize, LuaError> {
2477 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2478 let data_bytes = state.check_arg_string(2)?.to_vec();
2479 let ld = data_bytes.len();
2480 let pos_raw = state.opt_arg_integer(3, 1)?;
2481 let mut pos = pos_relat_i(pos_raw, ld).saturating_sub(1);
2482
2483 if pos > ld {
2484 return Err(LuaError::arg_error(3, "initial position out of string"));
2485 }
2486
2487 let fmt = &fmt_bytes[..];
2488 let data = &data_bytes[..];
2489 let mut h = Header::new();
2490 let mut fmt_pos = 0usize;
2491 let mut n = 0usize;
2492
2493 while fmt_pos < fmt.len() {
2494 let mut size = 0usize;
2495 let mut ntoalign = 0usize;
2496 let opt = getdetails(&mut h, pos, fmt, &mut fmt_pos, &mut size, &mut ntoalign)?;
2497
2498 if ntoalign + size > ld - pos {
2499 return Err(LuaError::arg_error(2, "data string too short"));
2500 }
2501 pos += ntoalign;
2502 state.ensure_stack(2, "too many results")?;
2503 n += 1;
2504
2505 match opt {
2506 KOption::Int => {
2507 let v = unpackint(state, &data[pos..pos + size], h.is_little, size, true)?;
2508 state.push(LuaValue::Int(v));
2509 }
2510 KOption::Uint => {
2511 let v = unpackint(state, &data[pos..pos + size], h.is_little, size, false)?;
2512 state.push(LuaValue::Int(v));
2513 }
2514 KOption::Float => {
2515 let mut bytes = [0u8; 4];
2516 copywithendian(&mut bytes, &data[pos..pos + 4], h.is_little);
2517 let f = f32::from_bits(u32::from_ne_bytes(bytes));
2518 state.push(LuaValue::Float(f as f64));
2519 }
2520 KOption::Number => {
2521 let mut bytes = [0u8; 8];
2522 copywithendian(&mut bytes, &data[pos..pos + 8], h.is_little);
2523 let f = f64::from_bits(u64::from_ne_bytes(bytes));
2524 state.push(LuaValue::Float(f));
2525 }
2526 KOption::Double => {
2527 let mut bytes = [0u8; 8];
2528 copywithendian(&mut bytes, &data[pos..pos + 8], h.is_little);
2529 let f = f64::from_bits(u64::from_ne_bytes(bytes));
2530 state.push(LuaValue::Float(f));
2531 }
2532 KOption::Char => {
2533 state.push_bytes(&data[pos..pos + size])?;
2534 }
2535 KOption::Kstring => {
2536 let len = unpackint(state, &data[pos..pos + size], h.is_little, size, false)? as usize;
2537 if len > ld - pos - size {
2538 return Err(LuaError::arg_error(2, "data string too short"));
2539 }
2540 state.push_bytes(&data[pos + size..pos + size + len])?;
2541 pos += len;
2542 }
2543 KOption::Zstr => {
2544 let end = data[pos..].iter().position(|&b| b == 0)
2545 .ok_or_else(|| LuaError::arg_error(2, "unfinished string for format 'z'"))?;
2546 if pos + end >= ld {
2547 return Err(LuaError::arg_error(2, "unfinished string for format 'z'"));
2548 }
2549 state.push_bytes(&data[pos..pos + end])?;
2550 pos += end + 1;
2551 }
2552 KOption::Paddalign | KOption::Padding | KOption::Nop => {
2553 n -= 1; }
2555 }
2556 pos += size;
2557 }
2558
2559 state.push(LuaValue::Int((pos + 1) as i64));
2560 Ok(n + 1)
2561}
2562
2563pub const STRING_LIB: &[(&[u8], lua_CFunction)] = &[
2570 (b"byte", str_byte),
2571 (b"char", str_char),
2572 (b"dump", str_dump),
2573 (b"find", str_find),
2574 (b"format", str_format),
2575 (b"gmatch", gmatch),
2576 (b"gsub", str_gsub),
2577 (b"len", str_len),
2578 (b"lower", str_lower),
2579 (b"match", str_match),
2580 (b"rep", str_rep),
2581 (b"reverse", str_reverse),
2582 (b"sub", str_sub),
2583 (b"upper", str_upper),
2584 (b"pack", str_pack),
2585 (b"packsize", str_packsize),
2586 (b"unpack", str_unpack),
2587];
2588
2589pub const STRING_META_METHODS: &[(&[u8], lua_CFunction)] = &[
2592 (b"__add", arith_add),
2593 (b"__sub", arith_sub),
2594 (b"__mul", arith_mul),
2595 (b"__mod", arith_mod),
2596 (b"__pow", arith_pow),
2597 (b"__div", arith_div),
2598 (b"__idiv", arith_idiv),
2599 (b"__unm", arith_unm),
2600];
2601
2602pub fn createmetatable(state: &mut LuaState) -> Result<(), LuaError> {
2605 state.new_lib_table(STRING_META_METHODS)?;
2606 state.set_funcs(STRING_META_METHODS, 0)?;
2607 state.push_string(b"")?;
2608 let mt_idx = state.top_idx() - 2;
2609 let mt = state.get_at(mt_idx);
2610 state.push(mt);
2611 state.set_metatable(-2)?;
2612 state.pop_n(1);
2613 let strlib_idx = state.top_idx() - 2;
2614 let strlib = state.get_at(strlib_idx);
2615 state.push(strlib);
2616 state.set_field(-2, b"__index")?;
2617 state.pop_n(1);
2618 Ok(())
2619}
2620
2621pub fn luaopen_string(state: &mut LuaState) -> Result<usize, LuaError> {
2624 state.new_lib(STRING_LIB)?;
2625 createmetatable(state)?;
2626 Ok(1)
2627}
2628
2629