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