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