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}
97
98impl<'a> MatchState<'a> {
99 fn new(src: &'a [u8], pat: &'a [u8]) -> Self {
100 MatchState {
101 src,
102 pat,
103 matchdepth: MAX_CC_CALLS,
104 level: 0,
105 captures: [Capture::default(); LUA_MAX_CAPTURES],
106 }
107 }
108
109 fn reset_level(&mut self) {
110 self.level = 0;
111 debug_assert!(self.matchdepth == MAX_CC_CALLS);
112 }
113}
114
115#[expect(dead_code, reason = "ported stdlib helper; not yet wired into the runtime")]
124struct GMatchState {
125 src_pos: usize,
127 pat: Vec<u8>,
129 last_match: Option<usize>,
131 src: Vec<u8>,
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142enum KOption {
143 Int, Uint, Float, Number, Double, Char, Kstring, Zstr, Padding, Paddalign, Nop, }
155
156struct Header {
159 is_little: bool,
160 max_align: usize,
161}
162
163impl Header {
164 fn new() -> Self {
165 Header {
166 is_little: cfg!(target_endian = "little"),
167 max_align: 1,
168 }
169 }
170}
171
172fn pos_relat_i(pos: i64, len: usize) -> usize {
180 if pos > 0 {
181 pos as usize
182 } else if pos == 0 {
183 1
184 } else if pos < -(len as i64) {
185 1
186 } else {
187 len.wrapping_add(pos as usize).wrapping_add(1)
188 }
189}
190
191fn get_end_pos(pos: i64, len: usize) -> usize {
195 if pos > len as i64 {
196 len
197 } else if pos >= 0 {
198 pos as usize
199 } else if pos < -(len as i64) {
200 0
201 } else {
202 len.wrapping_add(pos as usize).wrapping_add(1)
203 }
204}
205
206pub fn str_len(state: &mut LuaState) -> Result<usize, LuaError> {
217 let l = match state.to_lua_string_len(1) {
218 Some(n) => n,
219 None => {
220 state.check_arg_string(1)?;
221 unreachable!("check_arg_string raises when arg #1 is not a string");
222 }
223 };
224 state.push(LuaValue::Int(l as i64));
225 Ok(1)
226}
227
228pub fn str_sub(state: &mut LuaState) -> Result<usize, LuaError> {
236 let s_ref = match state.to_lua_string(1) {
237 Some(r) => r,
238 None => {
239 state.check_arg_string(1)?;
240 unreachable!("check_arg_string raises when arg #1 is not a string");
241 }
242 };
243 let s: &[u8] = s_ref.as_bytes();
244 let l = s.len();
245 let start = pos_relat_i(state.check_arg_integer(2)?, l);
246 let end_pos_raw = state.opt_arg_integer(3, -1)?;
247 let end = get_end_pos(end_pos_raw, l);
248 if start <= end {
249 let slice = &s[(start - 1)..end];
250 state.push_string(slice)?;
251 } else {
252 state.push_string(b"")?;
253 }
254 Ok(1)
255}
256
257pub fn str_reverse(state: &mut LuaState) -> Result<usize, LuaError> {
263 let s_ref = match state.to_lua_string(1) {
264 Some(r) => r,
265 None => {
266 state.check_arg_string(1)?;
267 unreachable!("check_arg_string raises when arg #1 is not a string");
268 }
269 };
270 let s: &[u8] = s_ref.as_bytes();
271 let buf: Vec<u8> = s.iter().copied().rev().collect();
272 state.push_bytes(&buf)?;
273 Ok(1)
274}
275
276pub fn str_lower(state: &mut LuaState) -> Result<usize, LuaError> {
282 let s_ref = match state.to_lua_string(1) {
283 Some(r) => r,
284 None => {
285 state.check_arg_string(1)?;
286 unreachable!("check_arg_string raises when arg #1 is not a string");
287 }
288 };
289 let s: &[u8] = s_ref.as_bytes();
290 let buf: Vec<u8> = s.iter().map(|&c| c.to_ascii_lowercase()).collect();
291 state.push_bytes(&buf)?;
292 Ok(1)
293}
294
295pub fn str_upper(state: &mut LuaState) -> Result<usize, LuaError> {
302 let s_ref = match state.to_lua_string(1) {
303 Some(r) => r,
304 None => {
305 state.check_arg_string(1)?;
306 unreachable!("check_arg_string raises when arg #1 is not a string");
307 }
308 };
309 let s: &[u8] = s_ref.as_bytes();
310 let buf: Vec<u8> = s.iter().map(|&c| c.to_ascii_uppercase()).collect();
311 state.push_bytes(&buf)?;
312 Ok(1)
313}
314
315pub fn str_rep(state: &mut LuaState) -> Result<usize, LuaError> {
322 let s_ref = match state.to_lua_string(1) {
323 Some(r) => r,
324 None => {
325 state.check_arg_string(1)?;
326 unreachable!("check_arg_string raises when arg #1 is not a string");
327 }
328 };
329 let s: &[u8] = s_ref.as_bytes();
330 let l = s.len();
331 let n = state.check_arg_integer(2)?;
332 let sep_owned = state.opt_arg_string(3, b"")?;
333 let sep: &[u8] = &sep_owned;
334 let lsep = sep.len();
335
336 if n <= 0 {
337 state.push_string(b"")?;
338 } else {
339 const MAXSIZE: usize = i32::MAX as usize;
340 let per = l.checked_add(lsep)
341 .ok_or_else(|| LuaError::runtime(format_args!("resulting string too large")))?;
342 if per > MAXSIZE / (n as usize) {
343 return Err(LuaError::runtime(format_args!("resulting string too large")));
344 }
345 let total = per * (n as usize) - lsep;
346
347 let mut buf: Vec<u8> = Vec::with_capacity(total);
348 for i in 0..(n as usize) {
349 buf.extend_from_slice(s);
350 if i < (n as usize - 1) && lsep > 0 {
351 buf.extend_from_slice(sep);
352 }
353 }
354 state.push_bytes(&buf)?;
355 }
356 Ok(1)
357}
358
359pub fn str_byte(state: &mut LuaState) -> Result<usize, LuaError> {
368 let s_ref = match state.to_lua_string(1) {
369 Some(r) => r,
370 None => {
371 state.check_arg_string(1)?;
372 unreachable!("check_arg_string raises when arg #1 is not a string");
373 }
374 };
375 let s: &[u8] = s_ref.as_bytes();
376 let l = s.len();
377 let pi = state.opt_arg_integer(2, 1)?;
378 let posi = pos_relat_i(pi, l);
379 let pose_raw = state.opt_arg_integer(3, pi)?;
380 let pose = get_end_pos(pose_raw, l);
381
382 if posi > pose {
383 return Ok(0);
384 }
385 let count = pose.saturating_sub(posi - 1) + 1;
386 if count > i32::MAX as usize {
387 return Err(LuaError::runtime(format_args!("string slice too long")));
388 }
389 let n = (pose - posi + 1) as usize;
390 state.ensure_stack(n as i32, "string slice too long")?;
391
392 for i in 0..n {
393 state.push(LuaValue::Int(s[posi - 1 + i] as i64));
394 }
395 Ok(n)
396}
397
398pub fn str_char(state: &mut LuaState) -> Result<usize, LuaError> {
401 let n = state.get_top();
402 let mut buf = Vec::with_capacity(n as usize);
403 for i in 1..=n {
404 let c = state.check_arg_integer(i)? as u64;
405 if c > u8::MAX as u64 {
406 return Err(LuaError::arg_error(i, "value out of range"));
407 }
408 buf.push(c as u8);
409 }
410 state.push_bytes(&buf)?;
411 Ok(1)
412}
413
414pub fn str_dump(state: &mut LuaState) -> Result<usize, LuaError> {
418 state.check_arg_type(1, LuaType::Function)?;
419 let strip = state.arg_to_bool(2);
420 lua_vm::api::set_top(state, 1)?;
423 let bytes = state.dump_function(strip)
427 .map_err(|_| LuaError::runtime(format_args!("unable to dump given function")))?;
428 state.push_bytes(&bytes)?;
429 Ok(1)
430}
431
432fn tonum(state: &mut LuaState, arg: i32) -> Result<bool, LuaError> {
440 if state.type_at(arg) == LuaType::Number {
441 state.push_value_at(arg)?;
442 Ok(true)
443 } else {
444 if let Some(s) = state.to_lua_string_bytes(arg) {
447 let len = s.len();
448 let pushed = state.string_to_number_push(&s)?;
450 Ok(pushed == len + 1)
451 } else {
452 Ok(false)
453 }
454 }
455}
456
457fn trymt(state: &mut LuaState, mtname: &[u8]) -> Result<(), LuaError> {
460 lua_vm::api::set_top(state, 2)?;
464 let t2_is_string = state.type_at(2) == LuaType::String;
466 let has_mm = state.get_meta_field(2, mtname)?;
467 if t2_is_string || !has_mm {
468 let op = &mtname[2..]; return Err(LuaError::runtime(format_args!(
470 "attempt to {} a '{}' with a '{}'",
471 op.escape_ascii(),
472 state.type_name_at(-2).escape_ascii(),
473 state.type_name_at(-1).escape_ascii(),
474 )));
475 }
476 state.insert(-3)?;
477 state.call(2, 1)?;
478 Ok(())
479}
480
481fn arith(state: &mut LuaState, op: ArithOp, mtname: &[u8]) -> Result<usize, LuaError> {
484 if tonum(state, 1)? && tonum(state, 2)? {
485 state.arith(op)?;
486 } else {
487 trymt(state, mtname)?;
488 }
489 Ok(1)
490}
491
492pub fn arith_add(state: &mut LuaState) -> Result<usize, LuaError> {
493 arith(state, ArithOp::Add, b"__add")
494}
495pub fn arith_sub(state: &mut LuaState) -> Result<usize, LuaError> {
496 arith(state, ArithOp::Sub, b"__sub")
497}
498pub fn arith_mul(state: &mut LuaState) -> Result<usize, LuaError> {
499 arith(state, ArithOp::Mul, b"__mul")
500}
501pub fn arith_mod(state: &mut LuaState) -> Result<usize, LuaError> {
502 arith(state, ArithOp::Mod, b"__mod")
503}
504pub fn arith_pow(state: &mut LuaState) -> Result<usize, LuaError> {
505 arith(state, ArithOp::Pow, b"__pow")
506}
507pub fn arith_div(state: &mut LuaState) -> Result<usize, LuaError> {
508 arith(state, ArithOp::Div, b"__div")
509}
510pub fn arith_idiv(state: &mut LuaState) -> Result<usize, LuaError> {
511 arith(state, ArithOp::Idiv, b"__idiv")
512}
513pub fn arith_unm(state: &mut LuaState) -> Result<usize, LuaError> {
514 arith(state, ArithOp::Unm, b"__unm")
515}
516
517fn match_class(c: u8, cl: u8) -> bool {
524 let res = match cl.to_ascii_lowercase() {
525 b'a' => c.is_ascii_alphabetic(),
526 b'c' => c.is_ascii_control(),
527 b'd' => c.is_ascii_digit(),
528 b'g' => c.is_ascii_graphic(),
529 b'l' => c.is_ascii_lowercase(),
530 b'p' => c.is_ascii_punctuation(),
531 b's' => c.is_ascii_whitespace(),
532 b'u' => c.is_ascii_uppercase(),
533 b'w' => c.is_ascii_alphanumeric(),
534 b'x' => c.is_ascii_hexdigit(),
535 b'z' => c == 0,
536 _ => return cl == c,
537 };
538 if cl.is_ascii_lowercase() { res } else { !res }
539}
540
541fn matchbracketclass(pat: &[u8], c: u8, mut p: usize, ec: usize) -> bool {
545 let sig = if p + 1 < pat.len() && pat[p + 1] == b'^' {
546 p += 1; false
548 } else {
549 true
550 };
551 p += 1; while p < ec {
553 if pat[p] == L_ESC {
554 p += 1;
555 if p < ec && match_class(c, pat[p]) {
556 return sig;
557 }
558 } else if p + 1 < ec && pat[p + 1] == b'-' && p + 2 < ec {
559 let lo = pat[p];
560 p += 2;
561 let hi = pat[p];
562 if lo <= c && c <= hi {
563 return sig;
564 }
565 } else if pat[p] == c {
566 return sig;
567 }
568 p += 1;
569 }
570 !sig
571}
572
573fn singlematch(ms: &MatchState, s: usize, p: usize, ep: usize) -> bool {
577 if s >= ms.src.len() {
578 return false;
579 }
580 let c = ms.src[s];
581 match ms.pat[p] {
582 b'.' => true,
583 L_ESC => match_class(c, ms.pat[p + 1]),
584 b'[' => matchbracketclass(ms.pat, c, p, ep - 1),
585 pc => pc == c,
586 }
587}
588
589fn classend(ms: &MatchState, p: usize) -> Result<usize, LuaError> {
593 let pat = ms.pat;
594 match pat.get(p).copied() {
595 Some(L_ESC) => {
596 if p + 1 >= pat.len() {
597 return Err(LuaError::runtime(format_args!(
598 "malformed pattern (ends with '%')"
599 )));
600 }
601 Ok(p + 2)
602 }
603 Some(b'[') => {
604 let mut q = p + 1;
605 if q < pat.len() && pat[q] == b'^' {
606 q += 1;
607 }
608 loop {
609 if q >= pat.len() {
610 return Err(LuaError::runtime(format_args!(
611 "malformed pattern (missing ']')"
612 )));
613 }
614 let ch = pat[q];
615 q += 1;
616 if ch == L_ESC && q < pat.len() {
617 q += 1;
618 }
619 if q < pat.len() && pat[q] == b']' {
620 return Ok(q + 1);
621 }
622 }
623 }
624 Some(_) => Ok(p + 1),
625 None => Ok(p),
626 }
627}
628
629fn check_capture(ms: &MatchState, l: u8) -> Result<usize, LuaError> {
633 let signed = (l as i32) - (b'1' as i32);
634 if signed < 0
635 || signed >= ms.level as i32
636 || ms.captures[signed as usize].len == CAP_UNFINISHED
637 {
638 return Err(LuaError::runtime(format_args!(
639 "invalid capture index %{}",
640 signed + 1
641 )));
642 }
643 Ok(signed as usize)
644}
645
646fn capture_to_close(ms: &MatchState) -> Result<usize, LuaError> {
649 let mut level = ms.level as usize;
650 while level > 0 {
651 level -= 1;
652 if ms.captures[level].len == CAP_UNFINISHED {
653 return Ok(level);
654 }
655 }
656 Err(LuaError::runtime(format_args!("invalid pattern capture")))
657}
658
659fn matchbalance(ms: &MatchState, s: usize, p: usize) -> Result<Option<usize>, LuaError> {
663 if p + 1 >= ms.pat.len() {
664 return Err(LuaError::runtime(format_args!(
665 "malformed pattern (missing arguments to '%b')"
666 )));
667 }
668 let b = ms.pat[p];
669 let e = ms.pat[p + 1];
670 if s >= ms.src.len() || ms.src[s] != b {
671 return Ok(None);
672 }
673 let mut cont = 1i32;
674 let mut s = s + 1;
675 while s < ms.src.len() {
676 if ms.src[s] == e {
677 cont -= 1;
678 if cont == 0 {
679 return Ok(Some(s + 1));
680 }
681 } else if ms.src[s] == b {
682 cont += 1;
683 }
684 s += 1;
685 }
686 Ok(None)
687}
688
689fn max_expand(
692 ms: &mut MatchState,
693 s: usize,
694 p: usize,
695 ep: usize,
696) -> Result<Option<usize>, LuaError> {
697 let mut count: isize = 0;
698 while singlematch(ms, s + count as usize, p, ep) {
699 count += 1;
700 }
701 while count >= 0 {
702 let res = match_pat(ms, s + count as usize, ep + 1)?;
703 if res.is_some() {
704 return Ok(res);
705 }
706 count -= 1;
707 }
708 Ok(None)
709}
710
711fn min_expand(
714 ms: &mut MatchState,
715 mut s: usize,
716 p: usize,
717 ep: usize,
718) -> Result<Option<usize>, LuaError> {
719 loop {
720 let res = match_pat(ms, s, ep + 1)?;
721 if res.is_some() {
722 return Ok(res);
723 } else if singlematch(ms, s, p, ep) {
724 s += 1;
725 } else {
726 return Ok(None);
727 }
728 }
729}
730
731fn start_capture(
734 ms: &mut MatchState,
735 s: usize,
736 p: usize,
737 what: isize,
738) -> Result<Option<usize>, LuaError> {
739 let level = ms.level as usize;
740 if level >= LUA_MAX_CAPTURES {
741 return Err(LuaError::runtime(format_args!("too many captures")));
742 }
743 ms.captures[level].init = s;
744 ms.captures[level].len = what;
745 ms.level += 1;
746 let res = match_pat(ms, s, p)?;
747 if res.is_none() {
748 ms.level -= 1; }
750 Ok(res)
751}
752
753fn end_capture(ms: &mut MatchState, s: usize, p: usize) -> Result<Option<usize>, LuaError> {
756 let l = capture_to_close(ms)?;
757 ms.captures[l].len = (s - ms.captures[l].init) as isize;
758 let res = match_pat(ms, s, p)?;
759 if res.is_none() {
760 ms.captures[l].len = CAP_UNFINISHED; }
762 Ok(res)
763}
764
765fn match_capture(ms: &MatchState, s: usize, l: u8) -> Result<Option<usize>, LuaError> {
768 let idx = check_capture(ms, l)?;
769 let cap_len = ms.captures[idx].len as usize;
770 let cap_init = ms.captures[idx].init;
771 if ms.src.len() - s >= cap_len
772 && &ms.src[s..s + cap_len] == &ms.src[cap_init..cap_init + cap_len]
773 {
774 Ok(Some(s + cap_len))
775 } else {
776 Ok(None)
777 }
778}
779
780fn match_pat(ms: &mut MatchState, mut s: usize, mut p: usize) -> Result<Option<usize>, LuaError> {
785 ms.matchdepth -= 1;
786 if ms.matchdepth < 0 {
787 ms.matchdepth = 0;
788 return Err(LuaError::runtime(format_args!("pattern too complex")));
789 }
790
791 let result = 'outer: loop {
793 if p >= ms.pat.len() {
794 break 'outer Ok(Some(s));
796 }
797
798 match ms.pat[p] {
799 b'(' => {
800 let s2 = if p + 1 < ms.pat.len() && ms.pat[p + 1] == b')' {
801 start_capture(ms, s, p + 2, CAP_POSITION)?
803 } else {
804 start_capture(ms, s, p + 1, CAP_UNFINISHED)?
805 };
806 break 'outer Ok(s2);
807 }
808 b')' => {
809 let s2 = end_capture(ms, s, p + 1)?;
810 break 'outer Ok(s2);
811 }
812 b'$' => {
813 if p + 1 != ms.pat.len() {
814 let ep = classend(ms, p)?;
816 let s2 = handle_class_with_suffix(ms, s, p, ep)?;
817 break 'outer Ok(s2);
818 }
819 break 'outer Ok(if s == ms.src.len() { Some(s) } else { None });
820 }
821 L_ESC => {
822 match ms.pat.get(p + 1).copied().unwrap_or(0) {
823 b'b' => {
824 let s2 = matchbalance(ms, s, p + 2)?;
825 if let Some(ns) = s2 {
826 s = ns;
827 p += 4;
828 continue 'outer; }
830 break 'outer Ok(None);
831 }
832 b'f' => {
833 p += 2;
834 if ms.pat.get(p).copied() != Some(b'[') {
835 return Err(LuaError::runtime(format_args!(
836 "missing '[' after '%f' in pattern"
837 )));
838 }
839 let ep = classend(ms, p)?;
840 let previous = if s == 0 { 0u8 } else { ms.src[s - 1] };
841 let current = ms.src.get(s).copied().unwrap_or(0);
842 if !matchbracketclass(ms.pat, previous, p, ep - 1)
843 && matchbracketclass(ms.pat, current, p, ep - 1)
844 {
845 p = ep;
846 continue 'outer; }
848 break 'outer Ok(None);
849 }
850 c @ b'0'..=b'9' => {
851 let s2 = match_capture(ms, s, c)?;
852 if let Some(ns) = s2 {
853 s = ns;
854 p += 2;
855 continue 'outer; }
857 break 'outer Ok(None);
858 }
859 _ => {
860 let ep = classend(ms, p)?;
862 let s2 = handle_class_with_suffix(ms, s, p, ep)?;
863 break 'outer Ok(s2);
864 }
865 }
866 }
867 _ => {
868 let ep = classend(ms, p)?;
870 let s2 = handle_class_with_suffix(ms, s, p, ep)?;
871 break 'outer Ok(s2);
872 }
873 }
874 };
875
876 ms.matchdepth += 1;
877 result
878}
879
880fn handle_class_with_suffix(
885 ms: &mut MatchState,
886 s: usize,
887 p: usize,
888 ep: usize,
889) -> Result<Option<usize>, LuaError> {
890 let matched_once = singlematch(ms, s, p, ep);
891 if !matched_once {
892 match ms.pat.get(ep).copied() {
894 Some(b'*') | Some(b'?') | Some(b'-') => {
895 return match_pat(ms, s, ep + 1);
899 }
900 _ => return Ok(None),
901 }
902 }
903
904 match ms.pat.get(ep).copied() {
906 Some(b'?') => {
907 let res = match_pat(ms, s + 1, ep + 1)?;
909 if res.is_some() {
910 Ok(res)
911 } else {
912 match_pat(ms, s, ep + 1)
913 }
914 }
915 Some(b'+') => {
916 max_expand(ms, s + 1, p, ep)
918 }
919 Some(b'*') => {
920 max_expand(ms, s, p, ep)
922 }
923 Some(b'-') => {
924 min_expand(ms, s, p, ep)
926 }
927 _ => {
928 match_pat(ms, s + 1, ep)
930 }
931 }
932}
933
934fn lmemfind(haystack: &[u8], needle: &[u8]) -> Option<usize> {
942 if needle.is_empty() {
943 return Some(0);
944 }
945 if needle.len() > haystack.len() {
946 return None;
947 }
948 let first = needle[0];
949 let rest = &needle[1..];
950 let limit = haystack.len() - rest.len();
951 let mut s = 0;
952 while s <= limit {
953 if let Some(pos) = haystack[s..].iter().position(|&b| b == first) {
954 let pos = s + pos;
955 if pos + 1 + rest.len() <= haystack.len()
956 && &haystack[pos + 1..pos + 1 + rest.len()] == rest
957 {
958 return Some(pos);
959 }
960 s = pos + 1;
961 } else {
962 break;
963 }
964 }
965 None
966}
967
968fn nospecials(pat: &[u8]) -> bool {
971 !pat.iter().any(|b| SPECIALS.contains(b))
972}
973
974enum CaptureInfo<'a> {
976 Position(i64),
978 Bytes(&'a [u8]),
980}
981
982fn get_one_capture<'a>(
986 ms: &'a MatchState,
987 i: usize,
988 s: usize,
989 e: usize,
990) -> Result<CaptureInfo<'a>, LuaError> {
991 if i >= ms.level as usize {
992 if i != 0 {
993 return Err(LuaError::runtime(format_args!(
994 "invalid capture index %{}",
995 i + 1
996 )));
997 }
998 return Ok(CaptureInfo::Bytes(&ms.src[s..e]));
1000 }
1001 let cap = &ms.captures[i];
1002 if cap.len == CAP_UNFINISHED {
1003 return Err(LuaError::runtime(format_args!("unfinished capture")));
1004 }
1005 if cap.len == CAP_POSITION {
1006 return Ok(CaptureInfo::Position((cap.init + 1) as i64));
1007 }
1008 let len = cap.len as usize;
1009 Ok(CaptureInfo::Bytes(&ms.src[cap.init..cap.init + len]))
1010}
1011
1012fn push_captures(
1016 state: &mut LuaState,
1017 ms: &MatchState,
1018 s: usize,
1019 e: usize,
1020) -> Result<usize, LuaError> {
1021 let nlevels = if ms.level == 0 { 1 } else { ms.level as usize };
1022 state.ensure_stack(nlevels as i32, "too many captures")?;
1023 for i in 0..nlevels {
1024 match get_one_capture(ms, i, s, e)? {
1025 CaptureInfo::Position(n) => state.push(LuaValue::Int(n)),
1026 CaptureInfo::Bytes(b) => state.push_bytes(b)?,
1027 }
1028 }
1029 Ok(nlevels)
1030}
1031
1032fn str_find_aux(state: &mut LuaState, find: bool) -> Result<usize, LuaError> {
1039 let s_ref = match state.to_lua_string(1) {
1040 Some(r) => r,
1041 None => {
1042 state.check_arg_string(1)?;
1043 unreachable!("check_arg_string raises when arg #1 is not a string");
1044 }
1045 };
1046 let p_ref = match state.to_lua_string(2) {
1047 Some(r) => r,
1048 None => {
1049 state.check_arg_string(2)?;
1050 unreachable!("check_arg_string raises when arg #2 is not a string");
1051 }
1052 };
1053 let s: &[u8] = s_ref.as_bytes();
1054 let p: &[u8] = p_ref.as_bytes();
1055 let ls = s.len();
1056 let lp = p.len();
1057 let init_raw = state.opt_arg_integer(3, 1)?;
1058 let init = pos_relat_i(init_raw, ls).saturating_sub(1);
1059
1060 if init > ls {
1061 state.push(LuaValue::Nil);
1062 return Ok(1);
1063 }
1064
1065 if find && (state.arg_to_bool(4) || nospecials(p)) {
1066 if let Some(pos) = lmemfind(&s[init..], p) {
1068 let abs = init + pos;
1069 state.push(LuaValue::Int((abs + 1) as i64));
1070 state.push(LuaValue::Int((abs + lp) as i64));
1071 return Ok(2);
1072 }
1073 } else {
1074 let mut ms = MatchState::new(s, p);
1075 let anchor = p.first() == Some(&b'^');
1076 let (_p_start, p_slice) = if anchor {
1077 (0, &p[1..])
1078 } else {
1079 (0, p)
1080 };
1081 ms.pat = p_slice;
1082
1083 let mut s1 = init;
1084 loop {
1085 ms.reset_level();
1086 if let Some(res) = match_pat(&mut ms, s1, 0)? {
1087 if find {
1088 state.push(LuaValue::Int((s1 + 1) as i64));
1089 state.push(LuaValue::Int(res as i64));
1090 let nc = push_captures(state, &ms, 0, 0)?;
1091 return Ok(nc + 2);
1092 } else {
1093 return push_captures(state, &ms, s1, res);
1094 }
1095 }
1096 if s1 >= ms.src.len() || anchor {
1097 break;
1098 }
1099 s1 += 1;
1100 }
1101 }
1102
1103 state.push(LuaValue::Nil);
1104 Ok(1)
1105}
1106
1107pub fn str_find(state: &mut LuaState) -> Result<usize, LuaError> {
1110 str_find_aux(state, true)
1111}
1112
1113pub fn str_match(state: &mut LuaState) -> Result<usize, LuaError> {
1116 str_find_aux(state, false)
1117}
1118
1119pub fn gmatch_aux(state: &mut LuaState) -> Result<usize, LuaError> {
1143 let upval = state.value_at(upvalue_index(1));
1144 let tbl = match upval {
1145 LuaValue::Table(t) => t,
1146 _ => return Ok(0),
1147 };
1148
1149 let s_val = tbl.get_int(1);
1150 let p_val = tbl.get_int(2);
1151 let (LuaValue::Str(s_str), LuaValue::Str(p_str)) = (&s_val, &p_val) else {
1152 return Ok(0);
1153 };
1154 let s: &[u8] = s_str.as_bytes();
1155 let p: &[u8] = p_str.as_bytes();
1156
1157 let pos = match tbl.get_int(3) {
1158 LuaValue::Int(n) => n,
1159 _ => 1,
1160 };
1161 let lastmatch_raw = match tbl.get_int(4) {
1162 LuaValue::Int(n) => n,
1163 _ => 0,
1164 };
1165 let last_match: Option<usize> = if lastmatch_raw <= 0 {
1166 None
1167 } else {
1168 Some((lastmatch_raw - 1) as usize)
1169 };
1170
1171 let ls = s.len();
1172 let start_pos = if pos < 1 { 0usize } else { (pos - 1) as usize };
1173
1174 let mut ms = MatchState::new(s, p);
1175
1176 let mut src = start_pos;
1177 while src <= ls {
1178 ms.reset_level();
1179 if let Some(e) = match_pat(&mut ms, src, 0)? {
1180 if Some(e) != last_match {
1181 let e_val = LuaValue::Int((e + 1) as i64);
1182 tbl.raw_set_int(state, 3, e_val.clone())?;
1183 tbl.raw_set_int(state, 4, e_val)?;
1184 return push_captures(state, &ms, src, e);
1185 }
1186 }
1187 src += 1;
1188 }
1189
1190 Ok(0)
1191}
1192
1193pub fn gmatch(state: &mut LuaState) -> Result<usize, LuaError> {
1201 let s_ref = match state.to_lua_string(1) {
1202 Some(r) => r,
1203 None => {
1204 state.check_arg_string(1)?;
1205 unreachable!("check_arg_string raises when arg #1 is not a string");
1206 }
1207 };
1208 let ls = s_ref.len();
1209 match state.to_lua_string(2) {
1210 Some(_) => {}
1211 None => {
1212 state.check_arg_string(2)?;
1213 unreachable!("check_arg_string raises when arg #2 is not a string");
1214 }
1215 };
1216 let init_raw = state.opt_arg_integer(3, 1)?;
1217 let mut init = pos_relat_i(init_raw, ls).saturating_sub(1);
1218 if init > ls {
1219 init = ls + 1;
1220 }
1221
1222 lua_vm::api::set_top(state, 2)?;
1223
1224 state.create_table(4, 0)?;
1225 let tbl_idx = state.top();
1226 state.push_value_at(1)?;
1227 state.raw_seti(tbl_idx, 1)?;
1228 state.push_value_at(2)?;
1229 state.raw_seti(tbl_idx, 2)?;
1230 state.push(LuaValue::Int((init + 1) as i64));
1231 state.raw_seti(tbl_idx, 3)?;
1232 state.push(LuaValue::Int(0));
1233 state.raw_seti(tbl_idx, 4)?;
1234
1235 state.push_c_closure(gmatch_aux, 1)?;
1236 Ok(1)
1237}
1238
1239fn add_s(
1242 state: &mut LuaState,
1243 ms: &MatchState,
1244 buf: &mut Vec<u8>,
1245 s: usize,
1246 e: usize,
1247) -> Result<(), LuaError> {
1248 let news_bytes = state.to_lua_string_bytes(3).unwrap_or_default();
1249 let mut i = 0usize;
1250 while i < news_bytes.len() {
1251 if news_bytes[i] != L_ESC {
1252 buf.push(news_bytes[i]);
1253 i += 1;
1254 } else {
1255 i += 1; if i >= news_bytes.len() {
1257 break;
1258 }
1259 let c = news_bytes[i];
1260 if c == L_ESC {
1261 buf.push(L_ESC);
1262 } else if c == b'0' {
1263 buf.extend_from_slice(&ms.src[s..e]);
1264 } else if c.is_ascii_digit() {
1265 match get_one_capture(ms, (c - b'1') as usize, s, e)? {
1266 CaptureInfo::Position(n) => {
1267 let formatted = format!("{}", n).into_bytes();
1269 buf.extend_from_slice(&formatted);
1270 }
1271 CaptureInfo::Bytes(b) => {
1272 buf.extend_from_slice(b);
1273 }
1274 }
1275 } else {
1276 return Err(LuaError::runtime(format_args!(
1277 "invalid use of '{}' in replacement string",
1278 L_ESC as char
1279 )));
1280 }
1281 i += 1;
1282 }
1283 }
1284 Ok(())
1285}
1286
1287fn add_value(
1291 state: &mut LuaState,
1292 ms: &MatchState,
1293 buf: &mut Vec<u8>,
1294 s: usize,
1295 e: usize,
1296 tr: LuaType,
1297) -> Result<bool, LuaError> {
1298 match tr {
1299 LuaType::Function => {
1300 state.push_value_at(3)?;
1301 let n = push_captures(state, ms, s, e)?;
1302 state.call(n as i32, 1)?;
1303 }
1304 LuaType::Table => {
1305 match get_one_capture(ms, 0, s, e)? {
1306 CaptureInfo::Position(n) => state.push(LuaValue::Int(n)),
1307 CaptureInfo::Bytes(b) => state.push_bytes(b)?,
1308 }
1309 state.get_table(3)?;
1310 }
1311 _ => {
1312 add_s(state, ms, buf, s, e)?;
1314 return Ok(true);
1315 }
1316 }
1317
1318 let top_bool = state.arg_to_bool(-1);
1319 if !top_bool {
1320 state.pop_n(1);
1321 buf.extend_from_slice(&ms.src[s..e]);
1322 return Ok(false);
1323 }
1324 if state.type_at(-1) != LuaType::String {
1325 let tname = state.type_name_at(-1).to_owned();
1326 return Err(LuaError::runtime(format_args!(
1327 "invalid replacement value (a {})", tname.escape_ascii()
1328 )));
1329 }
1330 let v = state.to_bytes(-1).unwrap_or_default();
1331 state.pop();
1332 buf.extend_from_slice(&v);
1333 Ok(true)
1334}
1335
1336pub fn str_gsub(state: &mut LuaState) -> Result<usize, LuaError> {
1339 let src_bytes = state.check_arg_string(1)?;
1340 let pat_bytes = state.check_arg_string(2)?;
1341 let src_len = src_bytes.len();
1342 let max_s = state.opt_arg_integer(4, (src_len + 1) as i64)?;
1343 let tr = state.type_at(3);
1344
1345 if !matches!(tr, LuaType::Number | LuaType::String | LuaType::Function | LuaType::Table) {
1346 let v = state.arg(3);
1347 return Err(LuaError::type_arg_error(3, "string/function/table", &v));
1348 }
1349
1350 let src_owned = src_bytes;
1351 let pat_owned = pat_bytes;
1352
1353 let anchor = pat_owned.first() == Some(&b'^');
1354 let pat_slice = if anchor { &pat_owned[1..] } else { &pat_owned[..] };
1355
1356 let mut ms = MatchState::new(&src_owned, pat_slice);
1357 let mut buf: Vec<u8> = Vec::new();
1358 let mut src_pos = 0usize;
1359 let mut last_match: Option<usize> = None;
1360 let mut n: i64 = 0;
1361 let mut changed = false;
1362
1363 while n < max_s {
1364 ms.reset_level();
1365 let maybe_e = match_pat(&mut ms, src_pos, 0)?;
1366 if let Some(e) = maybe_e {
1367 if last_match != Some(e) {
1368 n += 1;
1369 let delta = add_value(state, &ms, &mut buf, src_pos, e, tr)?;
1370 changed |= delta;
1371 src_pos = e;
1372 last_match = Some(e);
1373 } else if src_pos < ms.src.len() {
1374 buf.push(ms.src[src_pos]);
1375 src_pos += 1;
1376 } else {
1377 break;
1378 }
1379 } else if src_pos < ms.src.len() {
1380 buf.push(ms.src[src_pos]);
1381 src_pos += 1;
1382 } else {
1383 break;
1384 }
1385 if anchor {
1386 break;
1387 }
1388 }
1389
1390 if !changed {
1391 state.push_value_at(1)?;
1392 } else {
1393 buf.extend_from_slice(&ms.src[src_pos..]);
1394 state.push_bytes(&buf)?;
1395 }
1396 state.push(LuaValue::Int(n));
1397 Ok(2)
1398}
1399
1400fn adddigit(buf: &mut Vec<u8>, x: f64) -> f64 {
1407 let dd = x.floor();
1408 let d = dd as i32;
1409 let c = if d < 10 { b'0' + d as u8 } else { b'a' + (d - 10) as u8 };
1410 buf.push(c);
1411 x - dd
1412}
1413
1414fn num2straux(x: f64) -> Vec<u8> {
1419 format_hex_float(x, None)
1420}
1421
1422fn format_hex_float(x: f64, precision: Option<usize>) -> Vec<u8> {
1430 if x.is_nan() {
1431 return b"nan".to_vec();
1432 }
1433 if x.is_infinite() {
1434 return if x < 0.0 { b"-inf".to_vec() } else { b"inf".to_vec() };
1435 }
1436 if x == 0.0 {
1437 let sign: &[u8] = if x.is_sign_negative() { b"-" } else { b"" };
1438 return match precision {
1439 None => [sign, b"0x0p+0"].concat(),
1440 Some(0) => [sign, b"0x0p+0"].concat(),
1441 Some(p) => {
1442 let zeros = "0".repeat(p);
1443 [sign, b"0x0.", zeros.as_bytes(), b"p+0"].concat()
1444 }
1445 };
1446 }
1447
1448 let (m_raw, exp) = frexp(x);
1449 let mut buf: Vec<u8> = Vec::new();
1450 let mut m = m_raw;
1451 if m < 0.0 {
1452 buf.push(b'-');
1453 m = -m;
1454 }
1455 buf.extend_from_slice(b"0x");
1456
1457 let nbfd = 1;
1458 m = adddigit(&mut buf, m * (1 << nbfd) as f64);
1459 let e = exp - nbfd;
1460
1461 match precision {
1462 None => {
1463 if m > 0.0 {
1464 buf.push(b'.');
1465 while m > 0.0 {
1466 m = adddigit(&mut buf, m * 16.0);
1467 }
1468 }
1469 }
1470 Some(0) => {}
1471 Some(p) => {
1472 buf.push(b'.');
1473 for _ in 0..p {
1474 if m > 0.0 {
1475 m = adddigit(&mut buf, m * 16.0);
1476 } else {
1477 buf.push(b'0');
1478 }
1479 }
1480 }
1481 }
1482
1483 let exp_str = format!("p{:+}", e);
1484 buf.extend_from_slice(exp_str.as_bytes());
1485 buf
1486}
1487
1488fn frexp(x: f64) -> (f64, i32) {
1493 if x == 0.0 || x.is_nan() || x.is_infinite() {
1494 return (x, 0);
1495 }
1496 let bits = x.to_bits();
1497 let sign_bit = bits & 0x8000_0000_0000_0000u64;
1498 let exp_bits = ((bits >> 52) & 0x7FF) as i32;
1499 if exp_bits == 0 {
1500 let (m, e) = frexp(x * (1u64 << 52) as f64);
1501 return (m, e - 52);
1502 }
1503 let exp = exp_bits - 1022;
1504 let mantissa_bits = sign_bit | (bits & 0x000F_FFFF_FFFF_FFFF) | 0x3FE0_0000_0000_0000;
1505 (f64::from_bits(mantissa_bits), exp)
1506}
1507
1508fn quotefloat(n: f64) -> Vec<u8> {
1511 if n == f64::INFINITY {
1512 return b"1e9999".to_vec();
1513 } else if n == f64::NEG_INFINITY {
1514 return b"-1e9999".to_vec();
1515 } else if n.is_nan() {
1516 return b"(0/0)".to_vec();
1517 }
1518 let buf = num2straux(n);
1520 if !buf.contains(&b'.') && !buf.contains(&b'p') {
1521 }
1524 buf
1525}
1526
1527fn addquoted(buf: &mut Vec<u8>, s: &[u8]) {
1530 buf.push(b'"');
1531 for (idx, &c) in s.iter().enumerate() {
1532 if c == b'"' || c == b'\\' || c == b'\n' {
1533 buf.push(b'\\');
1534 buf.push(c);
1535 } else if c.is_ascii_control() {
1536 let next_is_digit = s.get(idx + 1).map_or(false, |n| n.is_ascii_digit());
1537 let formatted = if next_is_digit {
1538 format!("\\{:03}", c)
1539 } else {
1540 format!("\\{}", c)
1541 };
1542 buf.extend_from_slice(formatted.as_bytes());
1543 } else {
1544 buf.push(c);
1545 }
1546 }
1547 buf.push(b'"');
1548}
1549
1550fn addliteral(state: &mut LuaState, buf: &mut Vec<u8>, arg: i32) -> Result<(), LuaError> {
1553 match state.type_at(arg) {
1554 LuaType::String => {
1555 let s = state.check_arg_string(arg)?.to_vec();
1556 addquoted(buf, &s);
1557 }
1558 LuaType::Number => {
1559 if state.is_integer(arg) {
1560 let n = state.to_integer(arg).unwrap_or(0);
1561 let formatted = if n == i64::MIN {
1562 format!("0x{:016x}", n as u64)
1563 } else {
1564 format!("{}", n)
1565 };
1566 buf.extend_from_slice(formatted.as_bytes());
1567 } else {
1568 let n = state.to_number(arg).unwrap_or(0.0);
1569 let hex = quotefloat(n);
1570 buf.extend_from_slice(&hex);
1571 }
1572 }
1573 LuaType::Nil => {
1574 buf.extend_from_slice(b"nil");
1575 }
1576 LuaType::Boolean => {
1577 buf.extend_from_slice(if state.to_boolean(arg) { b"true" } else { b"false" });
1578 }
1579 _ => {
1580 return Err(LuaError::arg_error(arg, "value has no literal form"));
1581 }
1582 }
1583 Ok(())
1584}
1585
1586
1587const FMT_FLAGS_F: &[u8] = b"-+#0 ";
1589const FMT_FLAGS_X: &[u8] = b"-#0";
1590const FMT_FLAGS_I: &[u8] = b"-+0 ";
1591const FMT_FLAGS_U: &[u8] = b"-0";
1592const FMT_FLAGS_C: &[u8] = b"-";
1593
1594fn check_conv_spec(form: &[u8], flags: &[u8], allow_precision: bool) -> Result<(), LuaError> {
1604 let mut i = 1usize; while i < form.len() && flags.contains(&form[i]) {
1606 i += 1;
1607 }
1608 if i < form.len() && form[i] == b'0' {
1609 return Err(LuaError::runtime(format_args!("invalid conversion specification")));
1610 }
1611 if i < form.len() && form[i].is_ascii_digit() {
1612 i += 1;
1613 if i < form.len() && form[i].is_ascii_digit() {
1614 i += 1;
1615 }
1616 }
1617 if allow_precision && i < form.len() && form[i] == b'.' {
1618 i += 1;
1619 if i < form.len() && form[i].is_ascii_digit() {
1620 i += 1;
1621 if i < form.len() && form[i].is_ascii_digit() {
1622 i += 1;
1623 }
1624 }
1625 }
1626 if i != form.len() - 1 {
1627 return Err(LuaError::runtime(format_args!("invalid conversion specification")));
1628 }
1629 Ok(())
1630}
1631
1632#[derive(Default)]
1634struct FmtSpec {
1635 left_align: bool,
1636 plus_sign: bool,
1637 space_sign: bool,
1638 alt_form: bool,
1639 zero_pad: bool,
1640 width: usize,
1641 precision: Option<usize>,
1642}
1643
1644fn parse_fmt_spec(spec: &[u8]) -> FmtSpec {
1645 let mut s = FmtSpec::default();
1646 let mut i = 0;
1647 while i < spec.len() {
1648 match spec[i] {
1649 b'-' => s.left_align = true,
1650 b'+' => s.plus_sign = true,
1651 b' ' => s.space_sign = true,
1652 b'#' => s.alt_form = true,
1653 b'0' => s.zero_pad = true,
1654 _ => break,
1655 }
1656 i += 1;
1657 }
1658 while i < spec.len() && spec[i].is_ascii_digit() {
1659 s.width = s.width * 10 + (spec[i] - b'0') as usize;
1660 i += 1;
1661 }
1662 if i < spec.len() && spec[i] == b'.' {
1663 i += 1;
1664 let mut p = 0usize;
1665 while i < spec.len() && spec[i].is_ascii_digit() {
1666 p = p * 10 + (spec[i] - b'0') as usize;
1667 i += 1;
1668 }
1669 s.precision = Some(p);
1670 }
1671 s
1672}
1673
1674fn pad_str(buf: &mut Vec<u8>, body: &[u8], spec: &FmtSpec) {
1675 let body = match spec.precision {
1676 Some(p) if body.len() > p => &body[..p],
1677 _ => body,
1678 };
1679 if body.len() >= spec.width {
1680 buf.extend_from_slice(body);
1681 return;
1682 }
1683 let pad = spec.width - body.len();
1684 if spec.left_align {
1685 buf.extend_from_slice(body);
1686 for _ in 0..pad { buf.push(b' '); }
1687 } else {
1688 for _ in 0..pad { buf.push(b' '); }
1689 buf.extend_from_slice(body);
1690 }
1691}
1692
1693fn pad_int(buf: &mut Vec<u8>, sign_prefix: &[u8], digits: &[u8], spec: &FmtSpec) {
1694 let min_digits = spec.precision.unwrap_or(0);
1695 let zeroes_for_prec = if digits.len() < min_digits { min_digits - digits.len() } else { 0 };
1696 let core_len = sign_prefix.len() + zeroes_for_prec + digits.len();
1697 if core_len >= spec.width {
1698 buf.extend_from_slice(sign_prefix);
1699 for _ in 0..zeroes_for_prec { buf.push(b'0'); }
1700 buf.extend_from_slice(digits);
1701 return;
1702 }
1703 let pad = spec.width - core_len;
1704 let use_zero_pad = spec.zero_pad && !spec.left_align && spec.precision.is_none();
1705 if spec.left_align {
1706 buf.extend_from_slice(sign_prefix);
1707 for _ in 0..zeroes_for_prec { buf.push(b'0'); }
1708 buf.extend_from_slice(digits);
1709 for _ in 0..pad { buf.push(b' '); }
1710 } else if use_zero_pad {
1711 buf.extend_from_slice(sign_prefix);
1712 for _ in 0..pad { buf.push(b'0'); }
1713 for _ in 0..zeroes_for_prec { buf.push(b'0'); }
1714 buf.extend_from_slice(digits);
1715 } else {
1716 for _ in 0..pad { buf.push(b' '); }
1717 buf.extend_from_slice(sign_prefix);
1718 for _ in 0..zeroes_for_prec { buf.push(b'0'); }
1719 buf.extend_from_slice(digits);
1720 }
1721}
1722
1723fn signed_int_parts(n: i64, spec: &FmtSpec) -> (Vec<u8>, Vec<u8>) {
1724 if n == 0 && spec.precision == Some(0) {
1725 return (Vec::new(), Vec::new());
1726 }
1727 let (sign, abs_digits) = if n < 0 {
1728 (b"-".to_vec(), {
1729 let u = (n as i128).unsigned_abs();
1730 format!("{}", u).into_bytes()
1731 })
1732 } else {
1733 let s: Vec<u8> = if spec.plus_sign {
1734 b"+".to_vec()
1735 } else if spec.space_sign {
1736 b" ".to_vec()
1737 } else {
1738 Vec::new()
1739 };
1740 (s, format!("{}", n).into_bytes())
1741 };
1742 (sign, abs_digits)
1743}
1744
1745fn unsigned_int_parts(n: u64, base: u32, upper: bool, spec: &FmtSpec) -> (Vec<u8>, Vec<u8>) {
1746 let digits = if n == 0 && spec.precision == Some(0) {
1747 Vec::new()
1748 } else {
1749 match base {
1750 8 => format!("{:o}", n).into_bytes(),
1751 16 if upper => format!("{:X}", n).into_bytes(),
1752 16 => format!("{:x}", n).into_bytes(),
1753 _ => format!("{}", n).into_bytes(),
1754 }
1755 };
1756 let prefix: Vec<u8> = if spec.alt_form && n != 0 {
1757 match base {
1758 8 => b"0".to_vec(),
1759 16 if upper => b"0X".to_vec(),
1760 16 => b"0x".to_vec(),
1761 _ => Vec::new(),
1762 }
1763 } else {
1764 Vec::new()
1765 };
1766 (prefix, digits)
1767}
1768
1769fn format_float(n: f64, conv: u8, spec: &FmtSpec) -> Vec<u8> {
1770 let prec = spec.precision.unwrap_or(6);
1771 if n.is_nan() {
1772 return if conv.is_ascii_uppercase() { b"NAN".to_vec() } else { b"nan".to_vec() };
1773 }
1774 if n.is_infinite() {
1775 let s: &[u8] = if conv.is_ascii_uppercase() {
1776 if n < 0.0 { b"-INF" } else { b"INF" }
1777 } else if n < 0.0 { b"-inf" } else { b"inf" };
1778 return s.to_vec();
1779 }
1780 match conv {
1781 b'f' | b'F' => {
1782 let mut result = format!("{:.*}", prec, n).into_bytes();
1783 if spec.alt_form && !result.contains(&b'.') {
1784 result.push(b'.');
1785 }
1786 result
1787 }
1788 b'e' => format_exp(n, prec, false, spec.alt_form),
1789 b'E' => {
1790 let mut v = format_exp(n, prec, false, spec.alt_form);
1791 for b in v.iter_mut() { if *b == b'e' { *b = b'E'; } }
1792 v
1793 }
1794 b'g' | b'G' => {
1795 let p = if prec == 0 { 1 } else { prec };
1796 let v = format_g(n, p, spec.alt_form);
1797 if conv == b'G' {
1798 v.into_iter().map(|b| if b == b'e' { b'E' } else { b }).collect()
1799 } else { v }
1800 }
1801 _ => format!("{}", n).into_bytes(),
1802 }
1803}
1804
1805fn format_exp(n: f64, prec: usize, _upper: bool, alt: bool) -> Vec<u8> {
1806 if n == 0.0 {
1807 let mantissa: String = if prec == 0 {
1808 if alt { "0.".to_string() } else { "0".to_string() }
1809 } else {
1810 format!("0.{}", "0".repeat(prec))
1811 };
1812 return format!("{}e+00", mantissa).into_bytes();
1813 }
1814 let abs = n.abs();
1815 let exp = abs.log10().floor() as i32;
1816 let mantissa = n / 10f64.powi(exp);
1817 let mantissa_str = format!("{:.*}", prec, mantissa);
1818 let (mant_final, exp_final) = if let Some(dot_pos) = mantissa_str.find('.') {
1819 let int_part = &mantissa_str[..dot_pos];
1820 let abs_int = int_part.trim_start_matches('-');
1821 if abs_int.len() > 1 {
1822 let new_mant = if prec == 0 {
1823 mantissa_str[..mantissa_str.len()-1].to_string()
1824 } else {
1825 let neg = if int_part.starts_with('-') { "-" } else { "" };
1826 let frac = &mantissa_str[dot_pos+1..];
1827 format!("{}{}.{}{}", neg, &abs_int[..1], &abs_int[1..], frac)
1828 };
1829 (new_mant, exp + (abs_int.len() as i32 - 1))
1830 } else {
1831 (mantissa_str, exp)
1832 }
1833 } else if mantissa_str.trim_start_matches('-').len() > 1 {
1834 let neg = if mantissa_str.starts_with('-') { "-" } else { "" };
1835 let body = mantissa_str.trim_start_matches('-');
1836 let bumped = format!("{}{}.{}", neg, &body[..1], &body[1..]);
1837 (bumped, exp + (body.len() as i32 - 1))
1838 } else {
1839 (mantissa_str, exp)
1840 };
1841 let sign = if exp_final < 0 { '-' } else { '+' };
1842 let mant_out = if alt && !mant_final.contains('.') {
1843 format!("{}.", mant_final)
1844 } else { mant_final };
1845 format!("{}e{}{:02}", mant_out, sign, exp_final.abs()).into_bytes()
1846}
1847
1848fn format_g(n: f64, prec: usize, alt: bool) -> Vec<u8> {
1849 if n == 0.0 {
1850 return if alt { format!("0.{}", "0".repeat(prec.saturating_sub(1))).into_bytes() } else { b"0".to_vec() };
1851 }
1852 let abs = n.abs();
1853 let exp = abs.log10().floor() as i32;
1854 if exp < -4 || exp >= prec as i32 {
1855 let ep = if prec == 0 { 0 } else { prec - 1 };
1856 let mut v = format_exp(n, ep, false, alt);
1857 if !alt {
1858 v = strip_trailing_zeros_exp(&v);
1859 }
1860 v
1861 } else {
1862 let dec_places = (prec as i32 - 1 - exp).max(0) as usize;
1863 let mut v = format!("{:.*}", dec_places, n).into_bytes();
1864 if !alt {
1865 v = strip_trailing_zeros_fixed(&v);
1866 }
1867 v
1868 }
1869}
1870
1871fn strip_trailing_zeros_fixed(s: &[u8]) -> Vec<u8> {
1872 if !s.contains(&b'.') { return s.to_vec(); }
1873 let mut end = s.len();
1874 while end > 0 && s[end-1] == b'0' { end -= 1; }
1875 if end > 0 && s[end-1] == b'.' { end -= 1; }
1876 s[..end].to_vec()
1877}
1878
1879fn strip_trailing_zeros_exp(s: &[u8]) -> Vec<u8> {
1880 let e_pos = match s.iter().position(|&b| b == b'e' || b == b'E') {
1881 Some(p) => p,
1882 None => return s.to_vec(),
1883 };
1884 let mantissa = &s[..e_pos];
1885 let exp_part = &s[e_pos..];
1886 if !mantissa.contains(&b'.') {
1887 let mut out = mantissa.to_vec();
1888 out.extend_from_slice(exp_part);
1889 return out;
1890 }
1891 let mut end = mantissa.len();
1892 while end > 0 && mantissa[end-1] == b'0' { end -= 1; }
1893 if end > 0 && mantissa[end-1] == b'.' { end -= 1; }
1894 let mut out = mantissa[..end].to_vec();
1895 out.extend_from_slice(exp_part);
1896 out
1897}
1898
1899pub fn str_format(state: &mut LuaState) -> Result<usize, LuaError> {
1902 let top = state.get_top();
1903 let mut arg = 1i32;
1904 let fmt_bytes = state.check_arg_string(1)?.to_vec();
1905 let mut buf: Vec<u8> = Vec::new();
1906 let mut i = 0usize;
1907
1908 while i < fmt_bytes.len() {
1909 let c = fmt_bytes[i];
1910 if c != L_ESC {
1911 buf.push(c);
1912 i += 1;
1913 continue;
1914 }
1915 i += 1;
1916 if i >= fmt_bytes.len() {
1917 break;
1918 }
1919 if fmt_bytes[i] == L_ESC {
1920 buf.push(L_ESC);
1921 i += 1;
1922 continue;
1923 }
1924
1925 arg += 1;
1927 if arg > top {
1928 return Err(LuaError::arg_error(arg, "no value"));
1929 }
1930
1931 let spec_start = i - 1; while i < fmt_bytes.len() && b"-+#0 ".contains(&fmt_bytes[i]) {
1935 i += 1;
1936 }
1937 if i < fmt_bytes.len() && fmt_bytes[i] != b'0' {
1939 while i < fmt_bytes.len() && fmt_bytes[i].is_ascii_digit() {
1940 i += 1;
1941 }
1942 }
1943 if i < fmt_bytes.len() && fmt_bytes[i] == b'.' {
1945 i += 1;
1946 while i < fmt_bytes.len() && fmt_bytes[i].is_ascii_digit() {
1947 i += 1;
1948 }
1949 }
1950
1951 if i >= fmt_bytes.len() {
1952 return Err(LuaError::runtime(format_args!("invalid conversion specification")));
1953 }
1954
1955 let conv = fmt_bytes[i];
1956 i += 1;
1957
1958 let spec_slice = &fmt_bytes[spec_start + 1..i - 1];
1959 let form = &fmt_bytes[spec_start..i];
1960
1961 if spec_slice.len() + 1 >= 22 {
1963 return Err(LuaError::runtime(format_args!("invalid format (too long)")));
1964 }
1965
1966 let spec = parse_fmt_spec(spec_slice);
1967
1968 match conv {
1969 b'c' => {
1970 check_conv_spec(form, FMT_FLAGS_C, false)?;
1971 let n = state.check_arg_integer(arg)?;
1972 let body = vec![n as u8];
1973 pad_str(&mut buf, &body, &spec);
1974 }
1975 b'd' | b'i' => {
1976 check_conv_spec(form, FMT_FLAGS_I, true)?;
1977 let n = state.check_arg_integer(arg)?;
1978 let (sign, digits) = signed_int_parts(n, &spec);
1979 pad_int(&mut buf, &sign, &digits, &spec);
1980 }
1981 b'u' => {
1982 check_conv_spec(form, FMT_FLAGS_U, true)?;
1983 let n = state.check_arg_integer(arg)? as u64;
1984 let (prefix, digits) = unsigned_int_parts(n, 10, false, &spec);
1985 pad_int(&mut buf, &prefix, &digits, &spec);
1986 }
1987 b'o' => {
1988 check_conv_spec(form, FMT_FLAGS_X, true)?;
1989 let n = state.check_arg_integer(arg)? as u64;
1990 let (prefix, digits) = unsigned_int_parts(n, 8, false, &spec);
1991 pad_int(&mut buf, &prefix, &digits, &spec);
1992 }
1993 b'x' => {
1994 check_conv_spec(form, FMT_FLAGS_X, true)?;
1995 let n = state.check_arg_integer(arg)? as u64;
1996 let (prefix, digits) = unsigned_int_parts(n, 16, false, &spec);
1997 pad_int(&mut buf, &prefix, &digits, &spec);
1998 }
1999 b'X' => {
2000 check_conv_spec(form, FMT_FLAGS_X, true)?;
2001 let n = state.check_arg_integer(arg)? as u64;
2002 let (prefix, digits) = unsigned_int_parts(n, 16, true, &spec);
2003 pad_int(&mut buf, &prefix, &digits, &spec);
2004 }
2005 b'a' | b'A' => {
2006 check_conv_spec(form, FMT_FLAGS_F, true)?;
2007 let n = state.check_arg_number(arg)?;
2008 let body = format_hex_float(n, spec.precision);
2009 let body: Vec<u8> = if conv == b'A' {
2010 body.into_iter().map(|b| b.to_ascii_uppercase()).collect()
2011 } else {
2012 body
2013 };
2014 let (sign, digits): (Vec<u8>, Vec<u8>) =
2015 if !body.is_empty() && (body[0] == b'-' || body[0] == b'+') {
2016 (vec![body[0]], body[1..].to_vec())
2017 } else if spec.plus_sign {
2018 (b"+".to_vec(), body)
2019 } else if spec.space_sign {
2020 (b" ".to_vec(), body)
2021 } else {
2022 (Vec::new(), body)
2023 };
2024 let no_prec_spec = FmtSpec {
2025 left_align: spec.left_align,
2026 plus_sign: spec.plus_sign,
2027 space_sign: spec.space_sign,
2028 alt_form: spec.alt_form,
2029 zero_pad: spec.zero_pad,
2030 width: spec.width,
2031 precision: None,
2032 };
2033 pad_int(&mut buf, &sign, &digits, &no_prec_spec);
2034 }
2035 b'f' | b'e' | b'E' | b'g' | b'G' => {
2036 check_conv_spec(form, FMT_FLAGS_F, true)?;
2037 let n = state.check_arg_number(arg)?;
2038 let body = format_float(n, conv, &spec);
2039 let (sign, digits): (Vec<u8>, Vec<u8>) = if !body.is_empty() && (body[0] == b'-' || body[0] == b'+') {
2040 (vec![body[0]], body[1..].to_vec())
2041 } else if n >= 0.0 && spec.plus_sign {
2042 (b"+".to_vec(), body)
2043 } else if n >= 0.0 && spec.space_sign {
2044 (b" ".to_vec(), body)
2045 } else {
2046 (Vec::new(), body)
2047 };
2048 let no_prec_spec = FmtSpec {
2049 left_align: spec.left_align,
2050 plus_sign: spec.plus_sign,
2051 space_sign: spec.space_sign,
2052 alt_form: spec.alt_form,
2053 zero_pad: spec.zero_pad,
2054 width: spec.width,
2055 precision: None,
2056 };
2057 pad_int(&mut buf, &sign, &digits, &no_prec_spec);
2058 }
2059 b'p' => {
2060 check_conv_spec(form, FMT_FLAGS_C, false)?;
2061 let s: Vec<u8> = match lua_vm::api::to_pointer(state, arg) {
2062 Some(p) => format!("0x{:x}", p).into_bytes(),
2063 None => b"(null)".to_vec(),
2064 };
2065 pad_str(&mut buf, &s, &FmtSpec { precision: None, ..spec });
2066 }
2067 b'q' => {
2068 if form.len() > 2 {
2069 return Err(LuaError::runtime(format_args!(
2070 "specifier '%q' cannot have modifiers"
2071 )));
2072 }
2073 addliteral(state, &mut buf, arg)?;
2074 }
2075 b's' => {
2076 check_conv_spec(form, FMT_FLAGS_C, true)?;
2077 let s = state.to_display_string(arg)?;
2078 let has_modifiers = spec.width != 0 || spec.precision.is_some();
2079 if has_modifiers && s.contains(&0u8) {
2080 return Err(LuaError::arg_error(
2081 arg,
2082 "string contains zeros",
2083 ));
2084 }
2085 pad_str(&mut buf, &s, &spec);
2086 state.pop_n(1);
2087 }
2088 _ => {
2089 return Err(LuaError::runtime(format_args!(
2090 "invalid conversion '%{}' to 'format'", conv as char
2091 )));
2092 }
2093 }
2094 }
2095
2096 state.push_bytes(&buf)?;
2097 Ok(1)
2098}
2099
2100fn is_digit(c: u8) -> bool {
2106 c.is_ascii_digit()
2107}
2108
2109fn getnum(fmt: &[u8], pos: &mut usize, df: i32) -> i32 {
2112 if *pos >= fmt.len() || !is_digit(fmt[*pos]) {
2113 return df;
2114 }
2115 let mut a = 0i32;
2116 while *pos < fmt.len() && is_digit(fmt[*pos]) {
2117 a = a * 10 + (fmt[*pos] - b'0') as i32;
2118 *pos += 1;
2119 if a > (i32::MAX - 9) / 10 {
2120 break;
2121 }
2122 }
2123 a
2124}
2125
2126fn getnumlimit(fmt: &[u8], pos: &mut usize, df: i32) -> Result<usize, LuaError> {
2129 let sz = getnum(fmt, pos, df);
2130 if sz > MAX_INT_SIZE as i32 || sz <= 0 {
2131 return Err(LuaError::runtime(format_args!(
2132 "integral size ({}) out of limits [1,{}]",
2133 sz, MAX_INT_SIZE
2134 )));
2135 }
2136 Ok(sz as usize)
2137}
2138
2139fn getoption(h: &mut Header, fmt: &[u8], pos: &mut usize, size: &mut usize) -> Result<KOption, LuaError> {
2142 const NATIVE_MAX_ALIGN: usize = std::mem::align_of::<f64>();
2144
2145 if *pos >= fmt.len() {
2146 return Ok(KOption::Nop);
2147 }
2148 let opt = fmt[*pos];
2149 *pos += 1;
2150 *size = 0;
2151
2152 match opt {
2153 b'b' => { *size = 1; Ok(KOption::Int) }
2154 b'B' => { *size = 1; Ok(KOption::Uint) }
2155 b'h' => { *size = 2; Ok(KOption::Int) }
2156 b'H' => { *size = 2; Ok(KOption::Uint) }
2157 b'l' => { *size = 8; Ok(KOption::Int) } b'L' => { *size = 8; Ok(KOption::Uint) }
2159 b'j' => { *size = SZINT; Ok(KOption::Int) }
2160 b'J' => { *size = SZINT; Ok(KOption::Uint) }
2161 b'T' => { *size = std::mem::size_of::<usize>(); Ok(KOption::Uint) }
2162 b'f' => { *size = 4; Ok(KOption::Float) }
2163 b'n' => { *size = 8; Ok(KOption::Number) } b'd' => { *size = 8; Ok(KOption::Double) } b'i' => { *size = getnumlimit(fmt, pos, 4)?; Ok(KOption::Int) }
2166 b'I' => { *size = getnumlimit(fmt, pos, 4)?; Ok(KOption::Uint) }
2167 b's' => { *size = getnumlimit(fmt, pos, std::mem::size_of::<usize>() as i32)?; Ok(KOption::Kstring) }
2168 b'c' => {
2169 let n = getnum(fmt, pos, -1);
2170 if n == -1 {
2171 return Err(LuaError::runtime(format_args!("missing size for format option 'c'")));
2172 }
2173 *size = n as usize;
2174 Ok(KOption::Char)
2175 }
2176 b'z' => Ok(KOption::Zstr),
2177 b'x' => { *size = 1; Ok(KOption::Padding) }
2178 b'X' => Ok(KOption::Paddalign),
2179 b' ' => Ok(KOption::Nop),
2180 b'<' => { h.is_little = true; Ok(KOption::Nop) }
2181 b'>' => { h.is_little = false; Ok(KOption::Nop) }
2182 b'=' => { h.is_little = cfg!(target_endian = "little"); Ok(KOption::Nop) }
2183 b'!' => {
2184 let n = getnum(fmt, pos, NATIVE_MAX_ALIGN as i32);
2185 h.max_align = getnumlimit(fmt, pos, n)?;
2186 Ok(KOption::Nop)
2187 }
2188 _ => Err(LuaError::runtime(format_args!("invalid format option '{}'", opt as char)))
2189 }
2190}
2191
2192fn getdetails(
2195 h: &mut Header,
2196 total_size: usize,
2197 fmt: &[u8],
2198 pos: &mut usize,
2199 psize: &mut usize,
2200 ntoalign: &mut usize,
2201) -> Result<KOption, LuaError> {
2202 let opt = getoption(h, fmt, pos, psize)?;
2203 let mut align = *psize;
2204
2205 if opt == KOption::Paddalign {
2206 if *pos >= fmt.len() {
2207 return Err(LuaError::arg_error(1, "invalid next option for option 'X'"));
2208 }
2209 let mut dummy_size = 0usize;
2210 let next_opt = getoption(h, fmt, pos, &mut dummy_size)?;
2211 align = dummy_size;
2212 if next_opt == KOption::Char || align == 0 {
2213 return Err(LuaError::arg_error(1, "invalid next option for option 'X'"));
2214 }
2215 }
2216
2217 if align <= 1 || opt == KOption::Char {
2218 *ntoalign = 0;
2219 } else {
2220 if align > h.max_align {
2221 align = h.max_align;
2222 }
2223 if (align & (align - 1)) != 0 {
2224 return Err(LuaError::arg_error(1, "format asks for alignment not power of 2"));
2225 }
2226 *ntoalign = (align - (total_size & (align - 1))) & (align - 1);
2227 }
2228 Ok(opt)
2229}
2230
2231fn packint(buf: &mut Vec<u8>, mut n: u64, is_little: bool, size: usize, neg: bool) {
2234 let start = buf.len();
2235 buf.resize(start + size, 0);
2236 let slice = &mut buf[start..start + size];
2237 for i in 0..size {
2239 slice[if is_little { i } else { size - 1 - i }] = (n & MC as u64) as u8;
2240 n >>= NB;
2241 }
2242 if neg && size > SZINT {
2244 for i in SZINT..size {
2245 slice[if is_little { i } else { size - 1 - i }] = MC;
2246 }
2247 }
2248}
2249
2250fn copywithendian(dest: &mut [u8], src: &[u8], is_little: bool) {
2253 debug_assert_eq!(dest.len(), src.len());
2254 if is_little == cfg!(target_endian = "little") {
2255 dest.copy_from_slice(src);
2256 } else {
2257 for (d, s) in dest.iter_mut().zip(src.iter().rev()) {
2258 *d = *s;
2259 }
2260 }
2261}
2262
2263fn unpackint(_state: &LuaState, data: &[u8], is_little: bool, size: usize, is_signed: bool) -> Result<i64, LuaError> {
2266 let limit = size.min(SZINT);
2267 let mut res: u64 = 0;
2268 for i in (0..limit).rev() {
2269 res <<= NB;
2270 let byte_idx = if is_little { i } else { size - 1 - i };
2271 res |= data[byte_idx] as u64;
2272 }
2273
2274 if size < SZINT {
2275 if is_signed {
2276 let mask: u64 = 1u64 << (size * NB as usize - 1);
2277 res = (res ^ mask).wrapping_sub(mask);
2278 }
2279 } else if size > SZINT {
2280 let mask = if !is_signed || (res as i64) >= 0 { 0u8 } else { MC };
2281 for i in limit..size {
2282 let byte_idx = if is_little { i } else { size - 1 - i };
2283 if data[byte_idx] != mask {
2284 return Err(LuaError::runtime(format_args!(
2285 "{}-byte integer does not fit into Lua Integer", size
2286 )));
2287 }
2288 }
2289 }
2290 Ok(res as i64)
2291}
2292
2293pub fn str_pack(state: &mut LuaState) -> Result<usize, LuaError> {
2296 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2297 let fmt = &fmt_bytes[..];
2298 let mut h = Header::new();
2299 let mut arg = 1i32;
2300 let mut total_size = 0usize;
2301 let mut buf: Vec<u8> = Vec::new();
2302 let mut pos = 0usize;
2303
2304 while pos < fmt.len() {
2305 let mut size = 0usize;
2306 let mut ntoalign = 0usize;
2307 let opt = getdetails(&mut h, total_size, fmt, &mut pos, &mut size, &mut ntoalign)?;
2308 total_size += ntoalign + size;
2309 for _ in 0..ntoalign {
2310 buf.push(PACK_PAD_BYTE);
2311 }
2312 arg += 1;
2313
2314 match opt {
2315 KOption::Int => {
2316 let n = state.check_arg_integer(arg)?;
2317 if size < SZINT {
2318 let lim: i64 = 1i64 << (size * NB as usize - 1);
2319 if !(-lim <= n && n < lim) {
2320 return Err(LuaError::arg_error(arg, "integer overflow"));
2321 }
2322 }
2323 packint(&mut buf, n as u64, h.is_little, size, n < 0);
2324 }
2325 KOption::Uint => {
2326 let n = state.check_arg_integer(arg)?;
2327 if size < SZINT {
2328 let lim: u64 = 1u64 << (size * NB as usize);
2329 if (n as u64) >= lim {
2330 return Err(LuaError::arg_error(arg, "unsigned overflow"));
2331 }
2332 }
2333 packint(&mut buf, n as u64, h.is_little, size, false);
2334 }
2335 KOption::Float => {
2336 let f = state.check_arg_number(arg)? as f32;
2337 let start = buf.len();
2338 buf.resize(start + 4, 0);
2339 copywithendian(&mut buf[start..start + 4], &f.to_bits().to_ne_bytes(), h.is_little);
2340 }
2341 KOption::Number => {
2342 let f = state.check_arg_number(arg)?;
2343 let start = buf.len();
2344 buf.resize(start + 8, 0);
2345 copywithendian(&mut buf[start..start + 8], &f.to_bits().to_ne_bytes(), h.is_little);
2346 }
2347 KOption::Double => {
2348 let f = state.check_arg_number(arg)? as f64;
2349 let start = buf.len();
2350 buf.resize(start + 8, 0);
2351 copywithendian(&mut buf[start..start + 8], &f.to_bits().to_ne_bytes(), h.is_little);
2352 }
2353 KOption::Char => {
2354 let s = state.check_arg_string(arg)?.to_vec();
2355 if s.len() > size {
2356 return Err(LuaError::arg_error(arg, "string longer than given size"));
2357 }
2358 buf.extend_from_slice(&s);
2359 let pad = size - s.len();
2360 for _ in 0..pad {
2361 buf.push(PACK_PAD_BYTE);
2362 }
2363 }
2364 KOption::Kstring => {
2365 let s = state.check_arg_string(arg)?.to_vec();
2366 let len = s.len();
2367 if size < SZINT && len >= (1usize << (size * 8)) {
2368 return Err(LuaError::arg_error(arg, "string length does not fit in given size"));
2369 }
2370 packint(&mut buf, len as u64, h.is_little, size, false);
2371 buf.extend_from_slice(&s);
2372 total_size += len;
2373 }
2374 KOption::Zstr => {
2375 let s = state.check_arg_string(arg)?.to_vec();
2376 if s.contains(&0) {
2377 return Err(LuaError::arg_error(arg, "string contains zeros"));
2378 }
2379 buf.extend_from_slice(&s);
2380 buf.push(0);
2381 total_size += s.len() + 1;
2382 }
2383 KOption::Padding => {
2384 buf.push(PACK_PAD_BYTE);
2385 arg -= 1; }
2387 KOption::Paddalign | KOption::Nop => {
2388 arg -= 1; }
2390 }
2391 }
2392
2393 state.push_bytes(&buf)?;
2394 Ok(1)
2395}
2396
2397pub fn str_packsize(state: &mut LuaState) -> Result<usize, LuaError> {
2400 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2401 let fmt = &fmt_bytes[..];
2402 let mut h = Header::new();
2403 let mut total_size = 0usize;
2404 let mut pos = 0usize;
2405
2406 while pos < fmt.len() {
2407 let mut size = 0usize;
2408 let mut ntoalign = 0usize;
2409 let opt = getdetails(&mut h, total_size, fmt, &mut pos, &mut size, &mut ntoalign)?;
2410 if opt == KOption::Kstring || opt == KOption::Zstr {
2411 return Err(LuaError::arg_error(1, "variable-length format"));
2412 }
2413 let space = ntoalign + size;
2414 if total_size > PACK_MAXSIZE - space {
2415 return Err(LuaError::arg_error(1, "format result too large"));
2416 }
2417 total_size += space;
2418 }
2419 state.push(LuaValue::Int(total_size as i64));
2420 Ok(1)
2421}
2422
2423pub fn str_unpack(state: &mut LuaState) -> Result<usize, LuaError> {
2426 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2427 let data_bytes = state.check_arg_string(2)?.to_vec();
2428 let ld = data_bytes.len();
2429 let pos_raw = state.opt_arg_integer(3, 1)?;
2430 let mut pos = pos_relat_i(pos_raw, ld).saturating_sub(1);
2431
2432 if pos > ld {
2433 return Err(LuaError::arg_error(3, "initial position out of string"));
2434 }
2435
2436 let fmt = &fmt_bytes[..];
2437 let data = &data_bytes[..];
2438 let mut h = Header::new();
2439 let mut fmt_pos = 0usize;
2440 let mut n = 0usize;
2441
2442 while fmt_pos < fmt.len() {
2443 let mut size = 0usize;
2444 let mut ntoalign = 0usize;
2445 let opt = getdetails(&mut h, pos, fmt, &mut fmt_pos, &mut size, &mut ntoalign)?;
2446
2447 if ntoalign + size > ld - pos {
2448 return Err(LuaError::arg_error(2, "data string too short"));
2449 }
2450 pos += ntoalign;
2451 state.ensure_stack(2, "too many results")?;
2452 n += 1;
2453
2454 match opt {
2455 KOption::Int => {
2456 let v = unpackint(state, &data[pos..pos + size], h.is_little, size, true)?;
2457 state.push(LuaValue::Int(v));
2458 }
2459 KOption::Uint => {
2460 let v = unpackint(state, &data[pos..pos + size], h.is_little, size, false)?;
2461 state.push(LuaValue::Int(v));
2462 }
2463 KOption::Float => {
2464 let mut bytes = [0u8; 4];
2465 copywithendian(&mut bytes, &data[pos..pos + 4], h.is_little);
2466 let f = f32::from_bits(u32::from_ne_bytes(bytes));
2467 state.push(LuaValue::Float(f as f64));
2468 }
2469 KOption::Number => {
2470 let mut bytes = [0u8; 8];
2471 copywithendian(&mut bytes, &data[pos..pos + 8], h.is_little);
2472 let f = f64::from_bits(u64::from_ne_bytes(bytes));
2473 state.push(LuaValue::Float(f));
2474 }
2475 KOption::Double => {
2476 let mut bytes = [0u8; 8];
2477 copywithendian(&mut bytes, &data[pos..pos + 8], h.is_little);
2478 let f = f64::from_bits(u64::from_ne_bytes(bytes));
2479 state.push(LuaValue::Float(f));
2480 }
2481 KOption::Char => {
2482 state.push_bytes(&data[pos..pos + size])?;
2483 }
2484 KOption::Kstring => {
2485 let len = unpackint(state, &data[pos..pos + size], h.is_little, size, false)? as usize;
2486 if len > ld - pos - size {
2487 return Err(LuaError::arg_error(2, "data string too short"));
2488 }
2489 state.push_bytes(&data[pos + size..pos + size + len])?;
2490 pos += len;
2491 }
2492 KOption::Zstr => {
2493 let end = data[pos..].iter().position(|&b| b == 0)
2494 .ok_or_else(|| LuaError::arg_error(2, "unfinished string for format 'z'"))?;
2495 if pos + end >= ld {
2496 return Err(LuaError::arg_error(2, "unfinished string for format 'z'"));
2497 }
2498 state.push_bytes(&data[pos..pos + end])?;
2499 pos += end + 1;
2500 }
2501 KOption::Paddalign | KOption::Padding | KOption::Nop => {
2502 n -= 1; }
2504 }
2505 pos += size;
2506 }
2507
2508 state.push(LuaValue::Int((pos + 1) as i64));
2509 Ok(n + 1)
2510}
2511
2512pub const STRING_LIB: &[(&[u8], lua_CFunction)] = &[
2519 (b"byte", str_byte),
2520 (b"char", str_char),
2521 (b"dump", str_dump),
2522 (b"find", str_find),
2523 (b"format", str_format),
2524 (b"gmatch", gmatch),
2525 (b"gsub", str_gsub),
2526 (b"len", str_len),
2527 (b"lower", str_lower),
2528 (b"match", str_match),
2529 (b"rep", str_rep),
2530 (b"reverse", str_reverse),
2531 (b"sub", str_sub),
2532 (b"upper", str_upper),
2533 (b"pack", str_pack),
2534 (b"packsize", str_packsize),
2535 (b"unpack", str_unpack),
2536];
2537
2538pub const STRING_META_METHODS: &[(&[u8], lua_CFunction)] = &[
2541 (b"__add", arith_add),
2542 (b"__sub", arith_sub),
2543 (b"__mul", arith_mul),
2544 (b"__mod", arith_mod),
2545 (b"__pow", arith_pow),
2546 (b"__div", arith_div),
2547 (b"__idiv", arith_idiv),
2548 (b"__unm", arith_unm),
2549];
2550
2551pub fn createmetatable(state: &mut LuaState) -> Result<(), LuaError> {
2554 state.new_lib_table(STRING_META_METHODS)?;
2555 state.set_funcs(STRING_META_METHODS, 0)?;
2556 state.push_string(b"")?;
2557 let mt_idx = state.top_idx() - 2;
2558 let mt = state.get_at(mt_idx);
2559 state.push(mt);
2560 state.set_metatable(-2)?;
2561 state.pop_n(1);
2562 let strlib_idx = state.top_idx() - 2;
2563 let strlib = state.get_at(strlib_idx);
2564 state.push(strlib);
2565 state.set_field(-2, b"__index")?;
2566 state.pop_n(1);
2567 Ok(())
2568}
2569
2570pub fn luaopen_string(state: &mut LuaState) -> Result<usize, LuaError> {
2573 state.new_lib(STRING_LIB)?;
2574 createmetatable(state)?;
2575 Ok(1)
2576}
2577
2578