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