1use lua_types::error::LuaError;
14use lua_types::value::LuaValue;
15use lua_types::arith::ArithOp;
16use lua_types::gc::GcRef;
17use lua_types::string::LuaString;
18use lua_types::{LuaType, LuaStatus};
19use lua_vm::state::LuaTableRefExt as _;
20use crate::state_stub::{LuaState, LuaStateStubExt as _, lua_CFunction, upvalue_index, CompareOp, LuaDebug};
21
22const LUA_MAX_CAPTURES: usize = 32;
28
29const MAX_CC_CALLS: i32 = 200;
31
32const L_ESC: u8 = b'%';
34
35const SPECIALS: &[u8] = b"^$*+?.([%-";
37
38const CAP_UNFINISHED: isize = -1;
40
41const CAP_POSITION: isize = -2;
43
44const MAX_ITEM: usize = 120;
46
47const MAX_ITEM_F: usize = 418;
49
50const MAX_FORMAT: usize = 32;
52
53const MAX_INT_SIZE: usize = 16;
55
56const PACK_MAXSIZE: usize = i32::MAX as usize;
61
62const NB: u32 = 8;
64
65const MC: u8 = 0xFF;
67
68const SZINT: usize = 8; const PACK_PAD_BYTE: u8 = 0x00;
73
74#[derive(Copy, Clone)]
84struct Capture {
85 init: usize,
87 len: isize,
89}
90
91impl Default for Capture {
92 fn default() -> Self {
93 Capture { init: 0, len: CAP_UNFINISHED }
94 }
95}
96
97struct MatchState<'a> {
102 src: &'a [u8],
104 pat: &'a [u8],
106 matchdepth: i32,
108 level: u8,
110 captures: [Capture; LUA_MAX_CAPTURES],
112}
113
114impl<'a> MatchState<'a> {
115 fn new(src: &'a [u8], pat: &'a [u8]) -> Self {
116 MatchState {
117 src,
118 pat,
119 matchdepth: MAX_CC_CALLS,
120 level: 0,
121 captures: [Capture::default(); LUA_MAX_CAPTURES],
122 }
123 }
124
125 fn reset_level(&mut self) {
126 self.level = 0;
127 debug_assert!(self.matchdepth == MAX_CC_CALLS);
128 }
129}
130
131struct GMatchState {
141 src_pos: usize,
143 pat: Vec<u8>,
145 last_match: Option<usize>,
147 src: Vec<u8>,
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159enum KOption {
160 Int, Uint, Float, Number, Double, Char, Kstring, Zstr, Padding, Paddalign, Nop, }
172
173struct Header {
177 is_little: bool,
178 max_align: usize,
179}
180
181impl Header {
182 fn new() -> Self {
183 Header {
184 is_little: cfg!(target_endian = "little"),
185 max_align: 1,
186 }
187 }
188}
189
190fn pos_relat_i(pos: i64, len: usize) -> usize {
199 if pos > 0 {
200 pos as usize
201 } else if pos == 0 {
202 1
203 } else if pos < -(len as i64) {
204 1
205 } else {
206 len.wrapping_add(pos as usize).wrapping_add(1)
207 }
208}
209
210fn get_end_pos(pos: i64, len: usize) -> usize {
215 if pos > len as i64 {
216 len
217 } else if pos >= 0 {
218 pos as usize
219 } else if pos < -(len as i64) {
220 0
221 } else {
222 len.wrapping_add(pos as usize).wrapping_add(1)
223 }
224}
225
226pub fn str_len(state: &mut LuaState) -> Result<usize, LuaError> {
238 let l = match state.to_lua_string_len(1) {
239 Some(n) => n,
240 None => {
241 state.check_arg_string(1)?;
242 unreachable!("check_arg_string raises when arg #1 is not a string");
243 }
244 };
245 state.push(LuaValue::Int(l as i64));
246 Ok(1)
247}
248
249pub fn str_sub(state: &mut LuaState) -> Result<usize, LuaError> {
258 let s_ref = match state.to_lua_string(1) {
259 Some(r) => r,
260 None => {
261 state.check_arg_string(1)?;
262 unreachable!("check_arg_string raises when arg #1 is not a string");
263 }
264 };
265 let s: &[u8] = s_ref.as_bytes();
266 let l = s.len();
267 let start = pos_relat_i(state.check_arg_integer(2)?, l);
268 let end_pos_raw = state.opt_arg_integer(3, -1)?;
269 let end = get_end_pos(end_pos_raw, l);
270 if start <= end {
271 let slice = &s[(start - 1)..end];
272 state.push_string(slice)?;
273 } else {
274 state.push_string(b"")?;
275 }
276 Ok(1)
277}
278
279pub fn str_reverse(state: &mut LuaState) -> Result<usize, LuaError> {
286 let s_ref = match state.to_lua_string(1) {
287 Some(r) => r,
288 None => {
289 state.check_arg_string(1)?;
290 unreachable!("check_arg_string raises when arg #1 is not a string");
291 }
292 };
293 let s: &[u8] = s_ref.as_bytes();
294 let buf: Vec<u8> = s.iter().copied().rev().collect();
295 state.push_bytes(&buf)?;
296 Ok(1)
297}
298
299pub fn str_lower(state: &mut LuaState) -> Result<usize, LuaError> {
306 let s_ref = match state.to_lua_string(1) {
307 Some(r) => r,
308 None => {
309 state.check_arg_string(1)?;
310 unreachable!("check_arg_string raises when arg #1 is not a string");
311 }
312 };
313 let s: &[u8] = s_ref.as_bytes();
314 let buf: Vec<u8> = s.iter().map(|&c| c.to_ascii_lowercase()).collect();
315 state.push_bytes(&buf)?;
316 Ok(1)
317}
318
319pub fn str_upper(state: &mut LuaState) -> Result<usize, LuaError> {
327 let s_ref = match state.to_lua_string(1) {
328 Some(r) => r,
329 None => {
330 state.check_arg_string(1)?;
331 unreachable!("check_arg_string raises when arg #1 is not a string");
332 }
333 };
334 let s: &[u8] = s_ref.as_bytes();
335 let buf: Vec<u8> = s.iter().map(|&c| c.to_ascii_uppercase()).collect();
336 state.push_bytes(&buf)?;
337 Ok(1)
338}
339
340pub fn str_rep(state: &mut LuaState) -> Result<usize, LuaError> {
348 let s_ref = match state.to_lua_string(1) {
349 Some(r) => r,
350 None => {
351 state.check_arg_string(1)?;
352 unreachable!("check_arg_string raises when arg #1 is not a string");
353 }
354 };
355 let s: &[u8] = s_ref.as_bytes();
356 let l = s.len();
357 let n = state.check_arg_integer(2)?;
358 let sep_owned = state.opt_arg_string(3, b"")?;
359 let sep: &[u8] = &sep_owned;
360 let lsep = sep.len();
361
362 if n <= 0 {
363 state.push_string(b"")?;
364 } else {
365 const MAXSIZE: usize = i32::MAX as usize;
366 let per = l.checked_add(lsep)
367 .ok_or_else(|| LuaError::runtime(format_args!("resulting string too large")))?;
368 if per > MAXSIZE / (n as usize) {
369 return Err(LuaError::runtime(format_args!("resulting string too large")));
370 }
371 let total = per * (n as usize) - lsep;
372
373 let mut buf: Vec<u8> = Vec::with_capacity(total);
374 for i in 0..(n as usize) {
375 buf.extend_from_slice(s);
376 if i < (n as usize - 1) && lsep > 0 {
377 buf.extend_from_slice(sep);
378 }
379 }
380 state.push_bytes(&buf)?;
381 }
382 Ok(1)
383}
384
385pub fn str_byte(state: &mut LuaState) -> Result<usize, LuaError> {
395 let s_ref = match state.to_lua_string(1) {
396 Some(r) => r,
397 None => {
398 state.check_arg_string(1)?;
399 unreachable!("check_arg_string raises when arg #1 is not a string");
400 }
401 };
402 let s: &[u8] = s_ref.as_bytes();
403 let l = s.len();
404 let pi = state.opt_arg_integer(2, 1)?;
405 let posi = pos_relat_i(pi, l);
406 let pose_raw = state.opt_arg_integer(3, pi)?;
407 let pose = get_end_pos(pose_raw, l);
408
409 if posi > pose {
410 return Ok(0);
411 }
412 let count = pose.saturating_sub(posi - 1) + 1;
413 if count > i32::MAX as usize {
414 return Err(LuaError::runtime(format_args!("string slice too long")));
415 }
416 let n = (pose - posi + 1) as usize;
417 state.ensure_stack(n as i32, "string slice too long")?;
418
419 for i in 0..n {
420 state.push(LuaValue::Int(s[posi - 1 + i] as i64));
421 }
422 Ok(n)
423}
424
425pub fn str_char(state: &mut LuaState) -> Result<usize, LuaError> {
429 let n = state.get_top();
430 let mut buf = Vec::with_capacity(n as usize);
431 for i in 1..=n {
432 let c = state.check_arg_integer(i)? as u64;
433 if c > u8::MAX as u64 {
434 return Err(LuaError::arg_error(i, "value out of range"));
435 }
436 buf.push(c as u8);
437 }
438 state.push_bytes(&buf)?;
439 Ok(1)
440}
441
442pub fn str_dump(state: &mut LuaState) -> Result<usize, LuaError> {
447 state.check_arg_type(1, LuaType::Function)?;
448 let strip = state.arg_to_bool(2);
449 lua_vm::api::set_top(state, 1)?;
453 let bytes = state.dump_function(strip)
457 .map_err(|_| LuaError::runtime(format_args!("unable to dump given function")))?;
458 state.push_bytes(&bytes)?;
459 Ok(1)
460}
461
462fn tonum(state: &mut LuaState, arg: i32) -> Result<bool, LuaError> {
471 if state.type_at(arg) == LuaType::Number {
472 state.push_value_at(arg)?;
473 Ok(true)
474 } else {
475 if let Some(s) = state.to_lua_string_bytes(arg) {
479 let len = s.len();
480 let pushed = state.string_to_number_push(&s)?;
482 Ok(pushed == len + 1)
483 } else {
484 Ok(false)
485 }
486 }
487}
488
489fn trymt(state: &mut LuaState, mtname: &[u8]) -> Result<(), LuaError> {
493 lua_vm::api::set_top(state, 2)?;
498 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!(
506 "attempt to {} a '{}' with a '{}'",
507 op.escape_ascii(),
508 state.type_name_at(-2).escape_ascii(),
509 state.type_name_at(-1).escape_ascii(),
510 )));
511 }
512 state.insert(-3)?;
514 state.call(2, 1)?;
515 Ok(())
516}
517
518fn arith(state: &mut LuaState, op: ArithOp, mtname: &[u8]) -> Result<usize, LuaError> {
522 if tonum(state, 1)? && tonum(state, 2)? {
523 state.arith(op)?;
524 } else {
525 trymt(state, mtname)?;
526 }
527 Ok(1)
528}
529
530pub 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> {
536 arith(state, ArithOp::Sub, b"__sub")
537}
538pub fn arith_mul(state: &mut LuaState) -> Result<usize, LuaError> {
540 arith(state, ArithOp::Mul, b"__mul")
541}
542pub fn arith_mod(state: &mut LuaState) -> Result<usize, LuaError> {
544 arith(state, ArithOp::Mod, b"__mod")
545}
546pub fn arith_pow(state: &mut LuaState) -> Result<usize, LuaError> {
548 arith(state, ArithOp::Pow, b"__pow")
549}
550pub fn arith_div(state: &mut LuaState) -> Result<usize, LuaError> {
552 arith(state, ArithOp::Div, b"__div")
553}
554pub fn arith_idiv(state: &mut LuaState) -> Result<usize, LuaError> {
556 arith(state, ArithOp::Idiv, b"__idiv")
557}
558pub fn arith_unm(state: &mut LuaState) -> Result<usize, LuaError> {
560 arith(state, ArithOp::Unm, b"__unm")
561}
562
563fn match_class(c: u8, cl: u8) -> bool {
571 let res = match cl.to_ascii_lowercase() {
572 b'a' => c.is_ascii_alphabetic(),
573 b'c' => c.is_ascii_control(),
574 b'd' => c.is_ascii_digit(),
575 b'g' => c.is_ascii_graphic(),
576 b'l' => c.is_ascii_lowercase(),
577 b'p' => c.is_ascii_punctuation(),
578 b's' => c.is_ascii_whitespace(),
579 b'u' => c.is_ascii_uppercase(),
580 b'w' => c.is_ascii_alphanumeric(),
581 b'x' => c.is_ascii_hexdigit(),
582 b'z' => c == 0,
583 _ => return cl == c,
584 };
585 if cl.is_ascii_lowercase() { res } else { !res }
586}
587
588fn matchbracketclass(pat: &[u8], c: u8, mut p: usize, ec: usize) -> bool {
593 let sig = if p + 1 < pat.len() && pat[p + 1] == b'^' {
594 p += 1; false
596 } else {
597 true
598 };
599 p += 1; while p < ec {
601 if pat[p] == L_ESC {
602 p += 1;
603 if p < ec && match_class(c, pat[p]) {
604 return sig;
605 }
606 } else if p + 1 < ec && pat[p + 1] == b'-' && p + 2 < ec {
607 let lo = pat[p];
608 p += 2;
609 let hi = pat[p];
610 if lo <= c && c <= hi {
611 return sig;
612 }
613 } else if pat[p] == c {
614 return sig;
615 }
616 p += 1;
617 }
618 !sig
619}
620
621fn singlematch(ms: &MatchState, s: usize, p: usize, ep: usize) -> bool {
626 if s >= ms.src.len() {
627 return false;
628 }
629 let c = ms.src[s];
630 match ms.pat[p] {
631 b'.' => true,
632 L_ESC => match_class(c, ms.pat[p + 1]),
633 b'[' => matchbracketclass(ms.pat, c, p, ep - 1),
634 pc => pc == c,
635 }
636}
637
638fn classend(ms: &MatchState, p: usize) -> Result<usize, LuaError> {
643 let pat = ms.pat;
644 match pat.get(p).copied() {
645 Some(L_ESC) => {
646 if p + 1 >= pat.len() {
648 return Err(LuaError::runtime(format_args!(
649 "malformed pattern (ends with '%')"
650 )));
651 }
652 Ok(p + 2)
653 }
654 Some(b'[') => {
655 let mut q = p + 1;
656 if q < pat.len() && pat[q] == b'^' {
657 q += 1;
658 }
659 loop {
660 if q >= pat.len() {
661 return Err(LuaError::runtime(format_args!(
662 "malformed pattern (missing ']')"
663 )));
664 }
665 let ch = pat[q];
666 q += 1;
667 if ch == L_ESC && q < pat.len() {
668 q += 1;
669 }
670 if q < pat.len() && pat[q] == b']' {
671 return Ok(q + 1);
672 }
673 }
674 }
675 Some(_) => Ok(p + 1),
676 None => Ok(p),
677 }
678}
679
680fn check_capture(ms: &MatchState, l: u8) -> Result<usize, LuaError> {
685 let signed = (l as i32) - (b'1' as i32);
686 if signed < 0
687 || signed >= ms.level as i32
688 || ms.captures[signed as usize].len == CAP_UNFINISHED
689 {
690 return Err(LuaError::runtime(format_args!(
691 "invalid capture index %{}",
692 signed + 1
693 )));
694 }
695 Ok(signed as usize)
696}
697
698fn capture_to_close(ms: &MatchState) -> Result<usize, LuaError> {
702 let mut level = ms.level as usize;
703 while level > 0 {
704 level -= 1;
705 if ms.captures[level].len == CAP_UNFINISHED {
706 return Ok(level);
707 }
708 }
709 Err(LuaError::runtime(format_args!("invalid pattern capture")))
710}
711
712fn matchbalance(ms: &MatchState, s: usize, p: usize) -> Result<Option<usize>, LuaError> {
717 if p + 1 >= ms.pat.len() {
719 return Err(LuaError::runtime(format_args!(
720 "malformed pattern (missing arguments to '%b')"
721 )));
722 }
723 let b = ms.pat[p];
724 let e = ms.pat[p + 1];
725 if s >= ms.src.len() || ms.src[s] != b {
726 return Ok(None);
727 }
728 let mut cont = 1i32;
729 let mut s = s + 1;
730 while s < ms.src.len() {
731 if ms.src[s] == e {
732 cont -= 1;
733 if cont == 0 {
734 return Ok(Some(s + 1));
735 }
736 } else if ms.src[s] == b {
737 cont += 1;
738 }
739 s += 1;
740 }
741 Ok(None)
742}
743
744fn max_expand(
748 ms: &mut MatchState,
749 s: usize,
750 p: usize,
751 ep: usize,
752) -> Result<Option<usize>, LuaError> {
753 let mut count: isize = 0;
754 while singlematch(ms, s + count as usize, p, ep) {
755 count += 1;
756 }
757 while count >= 0 {
758 let res = match_pat(ms, s + count as usize, ep + 1)?;
759 if res.is_some() {
760 return Ok(res);
761 }
762 count -= 1;
763 }
764 Ok(None)
765}
766
767fn min_expand(
771 ms: &mut MatchState,
772 mut s: usize,
773 p: usize,
774 ep: usize,
775) -> Result<Option<usize>, LuaError> {
776 loop {
777 let res = match_pat(ms, s, ep + 1)?;
778 if res.is_some() {
779 return Ok(res);
780 } else if singlematch(ms, s, p, ep) {
781 s += 1;
782 } else {
783 return Ok(None);
784 }
785 }
786}
787
788fn start_capture(
792 ms: &mut MatchState,
793 s: usize,
794 p: usize,
795 what: isize,
796) -> Result<Option<usize>, LuaError> {
797 let level = ms.level as usize;
798 if level >= LUA_MAX_CAPTURES {
799 return Err(LuaError::runtime(format_args!("too many captures")));
800 }
801 ms.captures[level].init = s;
802 ms.captures[level].len = what;
803 ms.level += 1;
804 let res = match_pat(ms, s, p)?;
805 if res.is_none() {
806 ms.level -= 1; }
808 Ok(res)
809}
810
811fn end_capture(ms: &mut MatchState, s: usize, p: usize) -> Result<Option<usize>, LuaError> {
815 let l = capture_to_close(ms)?;
816 ms.captures[l].len = (s - ms.captures[l].init) as isize;
817 let res = match_pat(ms, s, p)?;
818 if res.is_none() {
819 ms.captures[l].len = CAP_UNFINISHED; }
821 Ok(res)
822}
823
824fn match_capture(ms: &MatchState, s: usize, l: u8) -> Result<Option<usize>, LuaError> {
828 let idx = check_capture(ms, l)?;
829 let cap_len = ms.captures[idx].len as usize;
830 let cap_init = ms.captures[idx].init;
831 if ms.src.len() - s >= cap_len
832 && &ms.src[s..s + cap_len] == &ms.src[cap_init..cap_init + cap_len]
833 {
834 Ok(Some(s + cap_len))
835 } else {
836 Ok(None)
837 }
838}
839
840fn match_pat(ms: &mut MatchState, mut s: usize, mut p: usize) -> Result<Option<usize>, LuaError> {
846 ms.matchdepth -= 1;
848 if ms.matchdepth < 0 {
849 ms.matchdepth = 0;
850 return Err(LuaError::runtime(format_args!("pattern too complex")));
851 }
852
853 let result = 'outer: loop {
855 if p >= ms.pat.len() {
856 break 'outer Ok(Some(s));
858 }
859
860 match ms.pat[p] {
861 b'(' => {
862 let s2 = if p + 1 < ms.pat.len() && ms.pat[p + 1] == b')' {
864 start_capture(ms, s, p + 2, CAP_POSITION)?
866 } else {
867 start_capture(ms, s, p + 1, CAP_UNFINISHED)?
868 };
869 break 'outer Ok(s2);
870 }
871 b')' => {
872 let s2 = end_capture(ms, s, p + 1)?;
873 break 'outer Ok(s2);
874 }
875 b'$' => {
876 if p + 1 != ms.pat.len() {
878 let ep = classend(ms, p)?;
880 let s2 = handle_class_with_suffix(ms, s, p, ep)?;
881 break 'outer Ok(s2);
882 }
883 break 'outer Ok(if s == ms.src.len() { Some(s) } else { None });
885 }
886 L_ESC => {
887 match ms.pat.get(p + 1).copied().unwrap_or(0) {
888 b'b' => {
889 let s2 = matchbalance(ms, s, p + 2)?;
891 if let Some(ns) = s2 {
892 s = ns;
893 p += 4;
894 continue 'outer; }
896 break 'outer Ok(None);
897 }
898 b'f' => {
899 p += 2;
901 if ms.pat.get(p).copied() != Some(b'[') {
902 return Err(LuaError::runtime(format_args!(
903 "missing '[' after '%f' in pattern"
904 )));
905 }
906 let ep = classend(ms, p)?;
907 let previous = if s == 0 { 0u8 } else { ms.src[s - 1] };
908 let current = ms.src.get(s).copied().unwrap_or(0);
909 if !matchbracketclass(ms.pat, previous, p, ep - 1)
910 && matchbracketclass(ms.pat, current, p, ep - 1)
911 {
912 p = ep;
913 continue 'outer; }
915 break 'outer Ok(None);
916 }
917 c @ b'0'..=b'9' => {
918 let s2 = match_capture(ms, s, c)?;
920 if let Some(ns) = s2 {
921 s = ns;
922 p += 2;
923 continue 'outer; }
925 break 'outer Ok(None);
926 }
927 _ => {
928 let ep = classend(ms, p)?;
930 let s2 = handle_class_with_suffix(ms, s, p, ep)?;
931 break 'outer Ok(s2);
932 }
933 }
934 }
935 _ => {
936 let ep = classend(ms, p)?;
938 let s2 = handle_class_with_suffix(ms, s, p, ep)?;
939 break 'outer Ok(s2);
940 }
941 }
942 };
943
944 ms.matchdepth += 1;
945 result
946}
947
948fn handle_class_with_suffix(
953 ms: &mut MatchState,
954 s: usize,
955 p: usize,
956 ep: usize,
957) -> Result<Option<usize>, LuaError> {
958 let matched_once = singlematch(ms, s, p, ep);
959 if !matched_once {
960 match ms.pat.get(ep).copied() {
963 Some(b'*') | Some(b'?') | Some(b'-') => {
964 return match_pat(ms, s, ep + 1);
968 }
969 _ => return Ok(None),
970 }
971 }
972
973 match ms.pat.get(ep).copied() {
975 Some(b'?') => {
976 let res = match_pat(ms, s + 1, ep + 1)?;
978 if res.is_some() {
979 Ok(res)
980 } else {
981 match_pat(ms, s, ep + 1)
982 }
983 }
984 Some(b'+') => {
985 max_expand(ms, s + 1, p, ep)
987 }
988 Some(b'*') => {
989 max_expand(ms, s, p, ep)
991 }
992 Some(b'-') => {
993 min_expand(ms, s, p, ep)
995 }
996 _ => {
997 match_pat(ms, s + 1, ep)
999 }
1000 }
1001}
1002
1003fn lmemfind(haystack: &[u8], needle: &[u8]) -> Option<usize> {
1012 if needle.is_empty() {
1013 return Some(0);
1014 }
1015 if needle.len() > haystack.len() {
1016 return None;
1017 }
1018 let first = needle[0];
1019 let rest = &needle[1..];
1020 let limit = haystack.len() - rest.len();
1021 let mut s = 0;
1022 while s <= limit {
1023 if let Some(pos) = haystack[s..].iter().position(|&b| b == first) {
1024 let pos = s + pos;
1025 if pos + 1 + rest.len() <= haystack.len()
1026 && &haystack[pos + 1..pos + 1 + rest.len()] == rest
1027 {
1028 return Some(pos);
1029 }
1030 s = pos + 1;
1031 } else {
1032 break;
1033 }
1034 }
1035 None
1036}
1037
1038fn nospecials(pat: &[u8]) -> bool {
1042 !pat.iter().any(|b| SPECIALS.contains(b))
1043}
1044
1045enum CaptureInfo<'a> {
1047 Position(i64),
1049 Bytes(&'a [u8]),
1051}
1052
1053fn get_one_capture<'a>(
1058 ms: &'a MatchState,
1059 i: usize,
1060 s: usize,
1061 e: usize,
1062) -> Result<CaptureInfo<'a>, LuaError> {
1063 if i >= ms.level as usize {
1064 if i != 0 {
1066 return Err(LuaError::runtime(format_args!(
1067 "invalid capture index %{}",
1068 i + 1
1069 )));
1070 }
1071 return Ok(CaptureInfo::Bytes(&ms.src[s..e]));
1073 }
1074 let cap = &ms.captures[i];
1075 if cap.len == CAP_UNFINISHED {
1076 return Err(LuaError::runtime(format_args!("unfinished capture")));
1077 }
1078 if cap.len == CAP_POSITION {
1079 return Ok(CaptureInfo::Position((cap.init + 1) as i64));
1081 }
1082 let len = cap.len as usize;
1083 Ok(CaptureInfo::Bytes(&ms.src[cap.init..cap.init + len]))
1084}
1085
1086fn push_captures(
1091 state: &mut LuaState,
1092 ms: &MatchState,
1093 s: usize,
1094 e: usize,
1095) -> Result<usize, LuaError> {
1096 let nlevels = if ms.level == 0 { 1 } else { ms.level as usize };
1097 state.ensure_stack(nlevels as i32, "too many captures")?;
1098 for i in 0..nlevels {
1099 match get_one_capture(ms, i, s, e)? {
1100 CaptureInfo::Position(n) => state.push(LuaValue::Int(n)),
1101 CaptureInfo::Bytes(b) => state.push_bytes(b)?,
1102 }
1103 }
1104 Ok(nlevels)
1105}
1106
1107fn str_find_aux(state: &mut LuaState, find: bool) -> Result<usize, LuaError> {
1115 let s_ref = match state.to_lua_string(1) {
1116 Some(r) => r,
1117 None => {
1118 state.check_arg_string(1)?;
1119 unreachable!("check_arg_string raises when arg #1 is not a string");
1120 }
1121 };
1122 let p_ref = match state.to_lua_string(2) {
1123 Some(r) => r,
1124 None => {
1125 state.check_arg_string(2)?;
1126 unreachable!("check_arg_string raises when arg #2 is not a string");
1127 }
1128 };
1129 let s: &[u8] = s_ref.as_bytes();
1130 let p: &[u8] = p_ref.as_bytes();
1131 let ls = s.len();
1132 let lp = p.len();
1133 let init_raw = state.opt_arg_integer(3, 1)?;
1134 let init = pos_relat_i(init_raw, ls).saturating_sub(1);
1135
1136 if init > ls {
1137 state.push(LuaValue::Nil);
1138 return Ok(1);
1139 }
1140
1141 if find && (state.arg_to_bool(4) || nospecials(p)) {
1143 if let Some(pos) = lmemfind(&s[init..], p) {
1145 let abs = init + pos;
1146 state.push(LuaValue::Int((abs + 1) as i64));
1147 state.push(LuaValue::Int((abs + lp) as i64));
1148 return Ok(2);
1149 }
1150 } else {
1151 let mut ms = MatchState::new(s, p);
1152 let anchor = p.first() == Some(&b'^');
1153 let (p_start, p_slice) = if anchor {
1154 (0, &p[1..])
1155 } else {
1156 (0, p)
1157 };
1158 ms.pat = p_slice;
1159
1160 let mut s1 = init;
1161 loop {
1162 ms.reset_level();
1163 if let Some(res) = match_pat(&mut ms, s1, 0)? {
1164 if find {
1165 state.push(LuaValue::Int((s1 + 1) as i64));
1166 state.push(LuaValue::Int(res as i64));
1167 let nc = push_captures(state, &ms, 0, 0)?;
1168 return Ok(nc + 2);
1169 } else {
1170 return push_captures(state, &ms, s1, res);
1171 }
1172 }
1173 if s1 >= ms.src.len() || anchor {
1174 break;
1175 }
1176 s1 += 1;
1177 }
1178 }
1179
1180 state.push(LuaValue::Nil);
1182 Ok(1)
1183}
1184
1185pub fn str_find(state: &mut LuaState) -> Result<usize, LuaError> {
1189 str_find_aux(state, true)
1190}
1191
1192pub fn str_match(state: &mut LuaState) -> Result<usize, LuaError> {
1196 str_find_aux(state, false)
1197}
1198
1199pub fn gmatch_aux(state: &mut LuaState) -> Result<usize, LuaError> {
1224 let upval = state.value_at(upvalue_index(1));
1225 let tbl = match upval {
1226 LuaValue::Table(t) => t,
1227 _ => return Ok(0),
1228 };
1229
1230 let s_val = tbl.get_int(1);
1231 let p_val = tbl.get_int(2);
1232 let (LuaValue::Str(s_str), LuaValue::Str(p_str)) = (&s_val, &p_val) else {
1233 return Ok(0);
1234 };
1235 let s: &[u8] = s_str.as_bytes();
1236 let p: &[u8] = p_str.as_bytes();
1237
1238 let pos = match tbl.get_int(3) {
1239 LuaValue::Int(n) => n,
1240 _ => 1,
1241 };
1242 let lastmatch_raw = match tbl.get_int(4) {
1243 LuaValue::Int(n) => n,
1244 _ => 0,
1245 };
1246 let last_match: Option<usize> = if lastmatch_raw <= 0 {
1247 None
1248 } else {
1249 Some((lastmatch_raw - 1) as usize)
1250 };
1251
1252 let ls = s.len();
1253 let start_pos = if pos < 1 { 0usize } else { (pos - 1) as usize };
1254
1255 let mut ms = MatchState::new(s, p);
1256
1257 let mut src = start_pos;
1258 while src <= ls {
1259 ms.reset_level();
1260 if let Some(e) = match_pat(&mut ms, src, 0)? {
1261 if Some(e) != last_match {
1262 let e_val = LuaValue::Int((e + 1) as i64);
1263 tbl.raw_set_int(state, 3, e_val.clone())?;
1264 tbl.raw_set_int(state, 4, e_val)?;
1265 return push_captures(state, &ms, src, e);
1266 }
1267 }
1268 src += 1;
1269 }
1270
1271 Ok(0)
1272}
1273
1274pub fn gmatch(state: &mut LuaState) -> Result<usize, LuaError> {
1283 let s_ref = match state.to_lua_string(1) {
1284 Some(r) => r,
1285 None => {
1286 state.check_arg_string(1)?;
1287 unreachable!("check_arg_string raises when arg #1 is not a string");
1288 }
1289 };
1290 let ls = s_ref.len();
1291 match state.to_lua_string(2) {
1292 Some(_) => {}
1293 None => {
1294 state.check_arg_string(2)?;
1295 unreachable!("check_arg_string raises when arg #2 is not a string");
1296 }
1297 };
1298 drop(s_ref);
1299 let init_raw = state.opt_arg_integer(3, 1)?;
1300 let mut init = pos_relat_i(init_raw, ls).saturating_sub(1);
1301 if init > ls {
1302 init = ls + 1;
1303 }
1304
1305 lua_vm::api::set_top(state, 2)?;
1306
1307 state.create_table(4, 0)?;
1308 let tbl_idx = state.top();
1309 state.push_value_at(1)?;
1310 state.raw_seti(tbl_idx, 1)?;
1311 state.push_value_at(2)?;
1312 state.raw_seti(tbl_idx, 2)?;
1313 state.push(LuaValue::Int((init + 1) as i64));
1314 state.raw_seti(tbl_idx, 3)?;
1315 state.push(LuaValue::Int(0));
1316 state.raw_seti(tbl_idx, 4)?;
1317
1318 state.push_c_closure(gmatch_aux, 1)?;
1319 Ok(1)
1320}
1321
1322fn add_s(
1326 state: &mut LuaState,
1327 ms: &MatchState,
1328 buf: &mut Vec<u8>,
1329 s: usize,
1330 e: usize,
1331) -> Result<(), LuaError> {
1332 let news_bytes = state.to_lua_string_bytes(3).unwrap_or_default();
1334 let mut i = 0usize;
1335 while i < news_bytes.len() {
1336 if news_bytes[i] != L_ESC {
1337 buf.push(news_bytes[i]);
1338 i += 1;
1339 } else {
1340 i += 1; if i >= news_bytes.len() {
1342 break;
1343 }
1344 let c = news_bytes[i];
1345 if c == L_ESC {
1346 buf.push(L_ESC);
1347 } else if c == b'0' {
1348 buf.extend_from_slice(&ms.src[s..e]);
1349 } else if c.is_ascii_digit() {
1350 match get_one_capture(ms, (c - b'1') as usize, s, e)? {
1351 CaptureInfo::Position(n) => {
1352 let formatted = format!("{}", n).into_bytes();
1355 buf.extend_from_slice(&formatted);
1356 }
1357 CaptureInfo::Bytes(b) => {
1358 buf.extend_from_slice(b);
1359 }
1360 }
1361 } else {
1362 return Err(LuaError::runtime(format_args!(
1363 "invalid use of '{}' in replacement string",
1364 L_ESC as char
1365 )));
1366 }
1367 i += 1;
1368 }
1369 }
1370 Ok(())
1371}
1372
1373fn add_value(
1378 state: &mut LuaState,
1379 ms: &MatchState,
1380 buf: &mut Vec<u8>,
1381 s: usize,
1382 e: usize,
1383 tr: LuaType,
1384) -> Result<bool, LuaError> {
1385 match tr {
1386 LuaType::Function => {
1387 state.push_value_at(3)?;
1389 let n = push_captures(state, ms, s, e)?;
1390 state.call(n as i32, 1)?;
1391 }
1392 LuaType::Table => {
1393 match get_one_capture(ms, 0, s, e)? {
1395 CaptureInfo::Position(n) => state.push(LuaValue::Int(n)),
1396 CaptureInfo::Bytes(b) => state.push_bytes(b)?,
1397 }
1398 state.get_table(3)?;
1399 }
1400 _ => {
1401 add_s(state, ms, buf, s, e)?;
1403 return Ok(true);
1404 }
1405 }
1406
1407 let top_bool = state.arg_to_bool(-1);
1409 if !top_bool {
1410 state.pop_n(1);
1411 buf.extend_from_slice(&ms.src[s..e]);
1412 return Ok(false);
1413 }
1414 if state.type_at(-1) != LuaType::String {
1415 let tname = state.type_name_at(-1).to_owned();
1416 return Err(LuaError::runtime(format_args!(
1417 "invalid replacement value (a {})", tname.escape_ascii()
1418 )));
1419 }
1420 let v = state.to_bytes(-1).unwrap_or_default();
1422 state.pop();
1423 buf.extend_from_slice(&v);
1424 Ok(true)
1425}
1426
1427pub fn str_gsub(state: &mut LuaState) -> Result<usize, LuaError> {
1431 let src_bytes = state.check_arg_string(1)?;
1432 let pat_bytes = state.check_arg_string(2)?;
1433 let src_len = src_bytes.len();
1434 let max_s = state.opt_arg_integer(4, (src_len + 1) as i64)?;
1435 let tr = state.type_at(3);
1436
1437 if !matches!(tr, LuaType::Number | LuaType::String | LuaType::Function | LuaType::Table) {
1439 let v = state.arg(3);
1440 return Err(LuaError::type_arg_error(3, "string/function/table", &v));
1441 }
1442
1443 let src_owned = src_bytes;
1444 let pat_owned = pat_bytes;
1445
1446 let anchor = pat_owned.first() == Some(&b'^');
1447 let pat_slice = if anchor { &pat_owned[1..] } else { &pat_owned[..] };
1448
1449 let mut ms = MatchState::new(&src_owned, pat_slice);
1450 let mut buf: Vec<u8> = Vec::new();
1451 let mut src_pos = 0usize;
1452 let mut last_match: Option<usize> = None;
1453 let mut n: i64 = 0;
1454 let mut changed = false;
1455
1456 while n < max_s {
1457 ms.reset_level();
1458 let maybe_e = match_pat(&mut ms, src_pos, 0)?;
1459 if let Some(e) = maybe_e {
1460 if last_match != Some(e) {
1461 n += 1;
1462 let delta = add_value(state, &ms, &mut buf, src_pos, e, tr)?;
1463 changed |= delta;
1464 src_pos = e;
1465 last_match = Some(e);
1466 } else if src_pos < ms.src.len() {
1467 buf.push(ms.src[src_pos]);
1468 src_pos += 1;
1469 } else {
1470 break;
1471 }
1472 } else if src_pos < ms.src.len() {
1473 buf.push(ms.src[src_pos]);
1474 src_pos += 1;
1475 } else {
1476 break;
1477 }
1478 if anchor {
1479 break;
1480 }
1481 }
1482
1483 if !changed {
1484 state.push_value_at(1)?;
1486 } else {
1487 buf.extend_from_slice(&ms.src[src_pos..]);
1488 state.push_bytes(&buf)?;
1489 }
1490 state.push(LuaValue::Int(n));
1491 Ok(2)
1492}
1493
1494fn adddigit(buf: &mut Vec<u8>, x: f64) -> f64 {
1502 let dd = x.floor();
1503 let d = dd as i32;
1504 let c = if d < 10 { b'0' + d as u8 } else { b'a' + (d - 10) as u8 };
1505 buf.push(c);
1506 x - dd
1507}
1508
1509fn num2straux(x: f64) -> Vec<u8> {
1515 format_hex_float(x, None)
1516}
1517
1518fn format_hex_float(x: f64, precision: Option<usize>) -> Vec<u8> {
1526 if x.is_nan() {
1527 return b"nan".to_vec();
1528 }
1529 if x.is_infinite() {
1530 return if x < 0.0 { b"-inf".to_vec() } else { b"inf".to_vec() };
1531 }
1532 if x == 0.0 {
1533 let sign: &[u8] = if x.is_sign_negative() { b"-" } else { b"" };
1534 return match precision {
1535 None => [sign, b"0x0p+0"].concat(),
1536 Some(0) => [sign, b"0x0p+0"].concat(),
1537 Some(p) => {
1538 let zeros = "0".repeat(p);
1539 [sign, b"0x0.", zeros.as_bytes(), b"p+0"].concat()
1540 }
1541 };
1542 }
1543
1544 let (m_raw, exp) = frexp(x);
1545 let mut buf: Vec<u8> = Vec::new();
1546 let mut m = m_raw;
1547 if m < 0.0 {
1548 buf.push(b'-');
1549 m = -m;
1550 }
1551 buf.extend_from_slice(b"0x");
1552
1553 let nbfd = 1;
1554 m = adddigit(&mut buf, m * (1 << nbfd) as f64);
1555 let e = exp - nbfd;
1556
1557 match precision {
1558 None => {
1559 if m > 0.0 {
1560 buf.push(b'.');
1561 while m > 0.0 {
1562 m = adddigit(&mut buf, m * 16.0);
1563 }
1564 }
1565 }
1566 Some(0) => {}
1567 Some(p) => {
1568 buf.push(b'.');
1569 for _ in 0..p {
1570 if m > 0.0 {
1571 m = adddigit(&mut buf, m * 16.0);
1572 } else {
1573 buf.push(b'0');
1574 }
1575 }
1576 }
1577 }
1578
1579 let exp_str = format!("p{:+}", e);
1580 buf.extend_from_slice(exp_str.as_bytes());
1581 buf
1582}
1583
1584fn frexp(x: f64) -> (f64, i32) {
1589 if x == 0.0 || x.is_nan() || x.is_infinite() {
1590 return (x, 0);
1591 }
1592 let bits = x.to_bits();
1593 let sign_bit = bits & 0x8000_0000_0000_0000u64;
1594 let exp_bits = ((bits >> 52) & 0x7FF) as i32;
1595 if exp_bits == 0 {
1596 let (m, e) = frexp(x * (1u64 << 52) as f64);
1597 return (m, e - 52);
1598 }
1599 let exp = exp_bits - 1022;
1600 let mantissa_bits = sign_bit | (bits & 0x000F_FFFF_FFFF_FFFF) | 0x3FE0_0000_0000_0000;
1601 (f64::from_bits(mantissa_bits), exp)
1602}
1603
1604fn quotefloat(n: f64) -> Vec<u8> {
1608 if n == f64::INFINITY {
1609 return b"1e9999".to_vec();
1610 } else if n == f64::NEG_INFINITY {
1611 return b"-1e9999".to_vec();
1612 } else if n.is_nan() {
1613 return b"(0/0)".to_vec();
1614 }
1615 let mut buf = num2straux(n);
1617 if !buf.contains(&b'.') && !buf.contains(&b'p') {
1618 }
1621 buf
1622}
1623
1624fn addquoted(buf: &mut Vec<u8>, s: &[u8]) {
1628 buf.push(b'"');
1629 for (idx, &c) in s.iter().enumerate() {
1630 if c == b'"' || c == b'\\' || c == b'\n' {
1631 buf.push(b'\\');
1632 buf.push(c);
1633 } else if c.is_ascii_control() {
1634 let next_is_digit = s.get(idx + 1).map_or(false, |n| n.is_ascii_digit());
1635 let formatted = if next_is_digit {
1636 format!("\\{:03}", c)
1637 } else {
1638 format!("\\{}", c)
1639 };
1640 buf.extend_from_slice(formatted.as_bytes());
1641 } else {
1642 buf.push(c);
1643 }
1644 }
1645 buf.push(b'"');
1646}
1647
1648fn addliteral(state: &mut LuaState, buf: &mut Vec<u8>, arg: i32) -> Result<(), LuaError> {
1652 match state.type_at(arg) {
1653 LuaType::String => {
1654 let s = state.check_arg_string(arg)?.to_vec();
1655 addquoted(buf, &s);
1656 }
1657 LuaType::Number => {
1658 if state.is_integer(arg) {
1659 let n = state.to_integer(arg).unwrap_or(0);
1660 let formatted = if n == i64::MIN {
1661 format!("0x{:016x}", n as u64)
1662 } else {
1663 format!("{}", n)
1664 };
1665 buf.extend_from_slice(formatted.as_bytes());
1666 } else {
1667 let n = state.to_number(arg).unwrap_or(0.0);
1668 let hex = quotefloat(n);
1669 buf.extend_from_slice(&hex);
1670 }
1671 }
1672 LuaType::Nil => {
1673 buf.extend_from_slice(b"nil");
1674 }
1675 LuaType::Boolean => {
1676 buf.extend_from_slice(if state.to_boolean(arg) { b"true" } else { b"false" });
1677 }
1678 _ => {
1679 return Err(LuaError::arg_error(arg, "value has no literal form"));
1680 }
1681 }
1682 Ok(())
1683}
1684
1685const FMT_FLAGS_F: &[u8] = b"-+#0 ";
1689const FMT_FLAGS_X: &[u8] = b"-#0";
1690const FMT_FLAGS_I: &[u8] = b"-+0 ";
1691const FMT_FLAGS_U: &[u8] = b"-0";
1692const FMT_FLAGS_C: &[u8] = b"-";
1693
1694fn check_conv_spec(form: &[u8], flags: &[u8], allow_precision: bool) -> Result<(), LuaError> {
1704 let mut i = 1usize; while i < form.len() && flags.contains(&form[i]) {
1706 i += 1;
1707 }
1708 if i < form.len() && form[i] == b'0' {
1709 return Err(LuaError::runtime(format_args!("invalid conversion specification")));
1710 }
1711 if i < form.len() && form[i].is_ascii_digit() {
1712 i += 1;
1713 if i < form.len() && form[i].is_ascii_digit() {
1714 i += 1;
1715 }
1716 }
1717 if allow_precision && i < form.len() && form[i] == b'.' {
1718 i += 1;
1719 if i < form.len() && form[i].is_ascii_digit() {
1720 i += 1;
1721 if i < form.len() && form[i].is_ascii_digit() {
1722 i += 1;
1723 }
1724 }
1725 }
1726 if i != form.len() - 1 {
1727 return Err(LuaError::runtime(format_args!("invalid conversion specification")));
1728 }
1729 Ok(())
1730}
1731
1732#[derive(Default)]
1734struct FmtSpec {
1735 left_align: bool,
1736 plus_sign: bool,
1737 space_sign: bool,
1738 alt_form: bool,
1739 zero_pad: bool,
1740 width: usize,
1741 precision: Option<usize>,
1742}
1743
1744fn parse_fmt_spec(spec: &[u8]) -> FmtSpec {
1745 let mut s = FmtSpec::default();
1746 let mut i = 0;
1747 while i < spec.len() {
1748 match spec[i] {
1749 b'-' => s.left_align = true,
1750 b'+' => s.plus_sign = true,
1751 b' ' => s.space_sign = true,
1752 b'#' => s.alt_form = true,
1753 b'0' => s.zero_pad = true,
1754 _ => break,
1755 }
1756 i += 1;
1757 }
1758 while i < spec.len() && spec[i].is_ascii_digit() {
1759 s.width = s.width * 10 + (spec[i] - b'0') as usize;
1760 i += 1;
1761 }
1762 if i < spec.len() && spec[i] == b'.' {
1763 i += 1;
1764 let mut p = 0usize;
1765 while i < spec.len() && spec[i].is_ascii_digit() {
1766 p = p * 10 + (spec[i] - b'0') as usize;
1767 i += 1;
1768 }
1769 s.precision = Some(p);
1770 }
1771 s
1772}
1773
1774fn pad_str(buf: &mut Vec<u8>, body: &[u8], spec: &FmtSpec) {
1775 let body = match spec.precision {
1776 Some(p) if body.len() > p => &body[..p],
1777 _ => body,
1778 };
1779 if body.len() >= spec.width {
1780 buf.extend_from_slice(body);
1781 return;
1782 }
1783 let pad = spec.width - body.len();
1784 if spec.left_align {
1785 buf.extend_from_slice(body);
1786 for _ in 0..pad { buf.push(b' '); }
1787 } else {
1788 for _ in 0..pad { buf.push(b' '); }
1789 buf.extend_from_slice(body);
1790 }
1791}
1792
1793fn pad_int(buf: &mut Vec<u8>, sign_prefix: &[u8], digits: &[u8], spec: &FmtSpec) {
1794 let min_digits = spec.precision.unwrap_or(0);
1795 let zeroes_for_prec = if digits.len() < min_digits { min_digits - digits.len() } else { 0 };
1796 let core_len = sign_prefix.len() + zeroes_for_prec + digits.len();
1797 if core_len >= spec.width {
1798 buf.extend_from_slice(sign_prefix);
1799 for _ in 0..zeroes_for_prec { buf.push(b'0'); }
1800 buf.extend_from_slice(digits);
1801 return;
1802 }
1803 let pad = spec.width - core_len;
1804 let use_zero_pad = spec.zero_pad && !spec.left_align && spec.precision.is_none();
1805 if spec.left_align {
1806 buf.extend_from_slice(sign_prefix);
1807 for _ in 0..zeroes_for_prec { buf.push(b'0'); }
1808 buf.extend_from_slice(digits);
1809 for _ in 0..pad { buf.push(b' '); }
1810 } else if use_zero_pad {
1811 buf.extend_from_slice(sign_prefix);
1812 for _ in 0..pad { buf.push(b'0'); }
1813 for _ in 0..zeroes_for_prec { buf.push(b'0'); }
1814 buf.extend_from_slice(digits);
1815 } else {
1816 for _ in 0..pad { buf.push(b' '); }
1817 buf.extend_from_slice(sign_prefix);
1818 for _ in 0..zeroes_for_prec { buf.push(b'0'); }
1819 buf.extend_from_slice(digits);
1820 }
1821}
1822
1823fn signed_int_parts(n: i64, spec: &FmtSpec) -> (Vec<u8>, Vec<u8>) {
1824 if n == 0 && spec.precision == Some(0) {
1825 return (Vec::new(), Vec::new());
1826 }
1827 let (sign, abs_digits) = if n < 0 {
1828 (b"-".to_vec(), {
1829 let u = (n as i128).unsigned_abs();
1830 format!("{}", u).into_bytes()
1831 })
1832 } else {
1833 let s: Vec<u8> = if spec.plus_sign {
1834 b"+".to_vec()
1835 } else if spec.space_sign {
1836 b" ".to_vec()
1837 } else {
1838 Vec::new()
1839 };
1840 (s, format!("{}", n).into_bytes())
1841 };
1842 (sign, abs_digits)
1843}
1844
1845fn unsigned_int_parts(n: u64, base: u32, upper: bool, spec: &FmtSpec) -> (Vec<u8>, Vec<u8>) {
1846 let digits = if n == 0 && spec.precision == Some(0) {
1847 Vec::new()
1848 } else {
1849 match base {
1850 8 => format!("{:o}", n).into_bytes(),
1851 16 if upper => format!("{:X}", n).into_bytes(),
1852 16 => format!("{:x}", n).into_bytes(),
1853 _ => format!("{}", n).into_bytes(),
1854 }
1855 };
1856 let prefix: Vec<u8> = if spec.alt_form && n != 0 {
1857 match base {
1858 8 => b"0".to_vec(),
1859 16 if upper => b"0X".to_vec(),
1860 16 => b"0x".to_vec(),
1861 _ => Vec::new(),
1862 }
1863 } else {
1864 Vec::new()
1865 };
1866 (prefix, digits)
1867}
1868
1869fn format_float(n: f64, conv: u8, spec: &FmtSpec) -> Vec<u8> {
1870 let prec = spec.precision.unwrap_or(6);
1871 if n.is_nan() {
1872 return if conv.is_ascii_uppercase() { b"NAN".to_vec() } else { b"nan".to_vec() };
1873 }
1874 if n.is_infinite() {
1875 let s: &[u8] = if conv.is_ascii_uppercase() {
1876 if n < 0.0 { b"-INF" } else { b"INF" }
1877 } else if n < 0.0 { b"-inf" } else { b"inf" };
1878 return s.to_vec();
1879 }
1880 match conv {
1881 b'f' | b'F' => {
1882 let mut result = format!("{:.*}", prec, n).into_bytes();
1883 if spec.alt_form && !result.contains(&b'.') {
1884 result.push(b'.');
1885 }
1886 result
1887 }
1888 b'e' => format_exp(n, prec, false, spec.alt_form),
1889 b'E' => {
1890 let mut v = format_exp(n, prec, false, spec.alt_form);
1891 for b in v.iter_mut() { if *b == b'e' { *b = b'E'; } }
1892 v
1893 }
1894 b'g' | b'G' => {
1895 let p = if prec == 0 { 1 } else { prec };
1896 let v = format_g(n, p, spec.alt_form);
1897 if conv == b'G' {
1898 v.into_iter().map(|b| if b == b'e' { b'E' } else { b }).collect()
1899 } else { v }
1900 }
1901 _ => format!("{}", n).into_bytes(),
1902 }
1903}
1904
1905fn format_exp(n: f64, prec: usize, _upper: bool, alt: bool) -> Vec<u8> {
1906 if n == 0.0 {
1907 let mantissa: String = if prec == 0 {
1908 if alt { "0.".to_string() } else { "0".to_string() }
1909 } else {
1910 format!("0.{}", "0".repeat(prec))
1911 };
1912 return format!("{}e+00", mantissa).into_bytes();
1913 }
1914 let abs = n.abs();
1915 let exp = abs.log10().floor() as i32;
1916 let mantissa = n / 10f64.powi(exp);
1917 let mantissa_str = format!("{:.*}", prec, mantissa);
1918 let (mant_final, exp_final) = if let Some(dot_pos) = mantissa_str.find('.') {
1919 let int_part = &mantissa_str[..dot_pos];
1920 let abs_int = int_part.trim_start_matches('-');
1921 if abs_int.len() > 1 {
1922 let new_mant = if prec == 0 {
1923 mantissa_str[..mantissa_str.len()-1].to_string()
1924 } else {
1925 let neg = if int_part.starts_with('-') { "-" } else { "" };
1926 let frac = &mantissa_str[dot_pos+1..];
1927 format!("{}{}.{}{}", neg, &abs_int[..1], &abs_int[1..], frac)
1928 };
1929 (new_mant, exp + (abs_int.len() as i32 - 1))
1930 } else {
1931 (mantissa_str, exp)
1932 }
1933 } else if mantissa_str.trim_start_matches('-').len() > 1 {
1934 let neg = if mantissa_str.starts_with('-') { "-" } else { "" };
1935 let body = mantissa_str.trim_start_matches('-');
1936 let bumped = format!("{}{}.{}", neg, &body[..1], &body[1..]);
1937 (bumped, exp + (body.len() as i32 - 1))
1938 } else {
1939 (mantissa_str, exp)
1940 };
1941 let sign = if exp_final < 0 { '-' } else { '+' };
1942 let mant_out = if alt && !mant_final.contains('.') {
1943 format!("{}.", mant_final)
1944 } else { mant_final };
1945 format!("{}e{}{:02}", mant_out, sign, exp_final.abs()).into_bytes()
1946}
1947
1948fn format_g(n: f64, prec: usize, alt: bool) -> Vec<u8> {
1949 if n == 0.0 {
1950 return if alt { format!("0.{}", "0".repeat(prec.saturating_sub(1))).into_bytes() } else { b"0".to_vec() };
1951 }
1952 let abs = n.abs();
1953 let exp = abs.log10().floor() as i32;
1954 if exp < -4 || exp >= prec as i32 {
1955 let ep = if prec == 0 { 0 } else { prec - 1 };
1956 let mut v = format_exp(n, ep, false, alt);
1957 if !alt {
1958 v = strip_trailing_zeros_exp(&v);
1959 }
1960 v
1961 } else {
1962 let dec_places = (prec as i32 - 1 - exp).max(0) as usize;
1963 let mut v = format!("{:.*}", dec_places, n).into_bytes();
1964 if !alt {
1965 v = strip_trailing_zeros_fixed(&v);
1966 }
1967 v
1968 }
1969}
1970
1971fn strip_trailing_zeros_fixed(s: &[u8]) -> Vec<u8> {
1972 if !s.contains(&b'.') { return s.to_vec(); }
1973 let mut end = s.len();
1974 while end > 0 && s[end-1] == b'0' { end -= 1; }
1975 if end > 0 && s[end-1] == b'.' { end -= 1; }
1976 s[..end].to_vec()
1977}
1978
1979fn strip_trailing_zeros_exp(s: &[u8]) -> Vec<u8> {
1980 let e_pos = match s.iter().position(|&b| b == b'e' || b == b'E') {
1981 Some(p) => p,
1982 None => return s.to_vec(),
1983 };
1984 let mantissa = &s[..e_pos];
1985 let exp_part = &s[e_pos..];
1986 if !mantissa.contains(&b'.') {
1987 let mut out = mantissa.to_vec();
1988 out.extend_from_slice(exp_part);
1989 return out;
1990 }
1991 let mut end = mantissa.len();
1992 while end > 0 && mantissa[end-1] == b'0' { end -= 1; }
1993 if end > 0 && mantissa[end-1] == b'.' { end -= 1; }
1994 let mut out = mantissa[..end].to_vec();
1995 out.extend_from_slice(exp_part);
1996 out
1997}
1998
1999pub fn str_format(state: &mut LuaState) -> Result<usize, LuaError> {
2003 let top = state.get_top();
2004 let mut arg = 1i32;
2005 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2006 let mut buf: Vec<u8> = Vec::new();
2007 let mut i = 0usize;
2008
2009 while i < fmt_bytes.len() {
2010 let c = fmt_bytes[i];
2011 if c != L_ESC {
2012 buf.push(c);
2013 i += 1;
2014 continue;
2015 }
2016 i += 1;
2017 if i >= fmt_bytes.len() {
2018 break;
2019 }
2020 if fmt_bytes[i] == L_ESC {
2021 buf.push(L_ESC);
2022 i += 1;
2023 continue;
2024 }
2025
2026 arg += 1;
2028 if arg > top {
2029 return Err(LuaError::arg_error(arg, "no value"));
2030 }
2031
2032 let spec_start = i - 1; while i < fmt_bytes.len() && b"-+#0 ".contains(&fmt_bytes[i]) {
2036 i += 1;
2037 }
2038 if i < fmt_bytes.len() && fmt_bytes[i] != b'0' {
2040 while i < fmt_bytes.len() && fmt_bytes[i].is_ascii_digit() {
2041 i += 1;
2042 }
2043 }
2044 if i < fmt_bytes.len() && fmt_bytes[i] == b'.' {
2046 i += 1;
2047 while i < fmt_bytes.len() && fmt_bytes[i].is_ascii_digit() {
2048 i += 1;
2049 }
2050 }
2051
2052 if i >= fmt_bytes.len() {
2053 return Err(LuaError::runtime(format_args!("invalid conversion specification")));
2054 }
2055
2056 let conv = fmt_bytes[i];
2057 i += 1;
2058
2059 let spec_slice = &fmt_bytes[spec_start + 1..i - 1];
2060 let form = &fmt_bytes[spec_start..i];
2061
2062 if spec_slice.len() + 1 >= 22 {
2065 return Err(LuaError::runtime(format_args!("invalid format (too long)")));
2066 }
2067
2068 let spec = parse_fmt_spec(spec_slice);
2069
2070 match conv {
2071 b'c' => {
2072 check_conv_spec(form, FMT_FLAGS_C, false)?;
2073 let n = state.check_arg_integer(arg)?;
2074 let body = vec![n as u8];
2075 pad_str(&mut buf, &body, &spec);
2076 }
2077 b'd' | b'i' => {
2078 check_conv_spec(form, FMT_FLAGS_I, true)?;
2079 let n = state.check_arg_integer(arg)?;
2080 let (sign, digits) = signed_int_parts(n, &spec);
2081 pad_int(&mut buf, &sign, &digits, &spec);
2082 }
2083 b'u' => {
2084 check_conv_spec(form, FMT_FLAGS_U, true)?;
2085 let n = state.check_arg_integer(arg)? as u64;
2086 let (prefix, digits) = unsigned_int_parts(n, 10, false, &spec);
2087 pad_int(&mut buf, &prefix, &digits, &spec);
2088 }
2089 b'o' => {
2090 check_conv_spec(form, FMT_FLAGS_X, true)?;
2091 let n = state.check_arg_integer(arg)? as u64;
2092 let (prefix, digits) = unsigned_int_parts(n, 8, false, &spec);
2093 pad_int(&mut buf, &prefix, &digits, &spec);
2094 }
2095 b'x' => {
2096 check_conv_spec(form, FMT_FLAGS_X, true)?;
2097 let n = state.check_arg_integer(arg)? as u64;
2098 let (prefix, digits) = unsigned_int_parts(n, 16, false, &spec);
2099 pad_int(&mut buf, &prefix, &digits, &spec);
2100 }
2101 b'X' => {
2102 check_conv_spec(form, FMT_FLAGS_X, true)?;
2103 let n = state.check_arg_integer(arg)? as u64;
2104 let (prefix, digits) = unsigned_int_parts(n, 16, true, &spec);
2105 pad_int(&mut buf, &prefix, &digits, &spec);
2106 }
2107 b'a' | b'A' => {
2108 check_conv_spec(form, FMT_FLAGS_F, true)?;
2109 let n = state.check_arg_number(arg)?;
2110 let body = format_hex_float(n, spec.precision);
2111 let body: Vec<u8> = if conv == b'A' {
2112 body.into_iter().map(|b| b.to_ascii_uppercase()).collect()
2113 } else {
2114 body
2115 };
2116 let (sign, digits): (Vec<u8>, Vec<u8>) =
2117 if !body.is_empty() && (body[0] == b'-' || body[0] == b'+') {
2118 (vec![body[0]], body[1..].to_vec())
2119 } else if spec.plus_sign {
2120 (b"+".to_vec(), body)
2121 } else if spec.space_sign {
2122 (b" ".to_vec(), body)
2123 } else {
2124 (Vec::new(), body)
2125 };
2126 let no_prec_spec = FmtSpec {
2127 left_align: spec.left_align,
2128 plus_sign: spec.plus_sign,
2129 space_sign: spec.space_sign,
2130 alt_form: spec.alt_form,
2131 zero_pad: spec.zero_pad,
2132 width: spec.width,
2133 precision: None,
2134 };
2135 pad_int(&mut buf, &sign, &digits, &no_prec_spec);
2136 }
2137 b'f' | b'e' | b'E' | b'g' | b'G' => {
2138 check_conv_spec(form, FMT_FLAGS_F, true)?;
2139 let n = state.check_arg_number(arg)?;
2140 let body = format_float(n, conv, &spec);
2141 let (sign, digits): (Vec<u8>, Vec<u8>) = if !body.is_empty() && (body[0] == b'-' || body[0] == b'+') {
2142 (vec![body[0]], body[1..].to_vec())
2143 } else if n >= 0.0 && spec.plus_sign {
2144 (b"+".to_vec(), body)
2145 } else if n >= 0.0 && spec.space_sign {
2146 (b" ".to_vec(), body)
2147 } else {
2148 (Vec::new(), body)
2149 };
2150 let no_prec_spec = FmtSpec {
2151 left_align: spec.left_align,
2152 plus_sign: spec.plus_sign,
2153 space_sign: spec.space_sign,
2154 alt_form: spec.alt_form,
2155 zero_pad: spec.zero_pad,
2156 width: spec.width,
2157 precision: None,
2158 };
2159 pad_int(&mut buf, &sign, &digits, &no_prec_spec);
2160 }
2161 b'p' => {
2162 check_conv_spec(form, FMT_FLAGS_C, false)?;
2163 let s: Vec<u8> = match lua_vm::api::to_pointer(state, arg) {
2164 Some(p) => format!("0x{:x}", p).into_bytes(),
2165 None => b"(null)".to_vec(),
2166 };
2167 pad_str(&mut buf, &s, &FmtSpec { precision: None, ..spec });
2168 }
2169 b'q' => {
2170 if form.len() > 2 {
2171 return Err(LuaError::runtime(format_args!(
2172 "specifier '%q' cannot have modifiers"
2173 )));
2174 }
2175 addliteral(state, &mut buf, arg)?;
2176 }
2177 b's' => {
2178 check_conv_spec(form, FMT_FLAGS_C, true)?;
2179 let s = state.to_display_string(arg)?;
2180 let has_modifiers = spec.width != 0 || spec.precision.is_some();
2181 if has_modifiers && s.contains(&0u8) {
2182 return Err(LuaError::arg_error(
2183 arg,
2184 "string contains zeros",
2185 ));
2186 }
2187 pad_str(&mut buf, &s, &spec);
2188 state.pop_n(1);
2189 }
2190 _ => {
2191 return Err(LuaError::runtime(format_args!(
2192 "invalid conversion '%{}' to 'format'", conv as char
2193 )));
2194 }
2195 }
2196 }
2197
2198 state.push_bytes(&buf)?;
2199 Ok(1)
2200}
2201
2202fn is_digit(c: u8) -> bool {
2208 c.is_ascii_digit()
2209}
2210
2211fn getnum(fmt: &[u8], pos: &mut usize, df: i32) -> i32 {
2215 if *pos >= fmt.len() || !is_digit(fmt[*pos]) {
2216 return df;
2217 }
2218 let mut a = 0i32;
2219 while *pos < fmt.len() && is_digit(fmt[*pos]) {
2220 a = a * 10 + (fmt[*pos] - b'0') as i32;
2221 *pos += 1;
2222 if a > (i32::MAX - 9) / 10 {
2223 break;
2224 }
2225 }
2226 a
2227}
2228
2229fn getnumlimit(fmt: &[u8], pos: &mut usize, df: i32) -> Result<usize, LuaError> {
2233 let sz = getnum(fmt, pos, df);
2234 if sz > MAX_INT_SIZE as i32 || sz <= 0 {
2235 return Err(LuaError::runtime(format_args!(
2236 "integral size ({}) out of limits [1,{}]",
2237 sz, MAX_INT_SIZE
2238 )));
2239 }
2240 Ok(sz as usize)
2241}
2242
2243fn getoption(h: &mut Header, fmt: &[u8], pos: &mut usize, size: &mut usize) -> Result<KOption, LuaError> {
2247 const NATIVE_MAX_ALIGN: usize = std::mem::align_of::<f64>();
2250
2251 if *pos >= fmt.len() {
2252 return Ok(KOption::Nop);
2253 }
2254 let opt = fmt[*pos];
2255 *pos += 1;
2256 *size = 0;
2257
2258 match opt {
2259 b'b' => { *size = 1; Ok(KOption::Int) }
2260 b'B' => { *size = 1; Ok(KOption::Uint) }
2261 b'h' => { *size = 2; Ok(KOption::Int) }
2262 b'H' => { *size = 2; Ok(KOption::Uint) }
2263 b'l' => { *size = 8; Ok(KOption::Int) } b'L' => { *size = 8; Ok(KOption::Uint) }
2265 b'j' => { *size = SZINT; Ok(KOption::Int) }
2266 b'J' => { *size = SZINT; Ok(KOption::Uint) }
2267 b'T' => { *size = std::mem::size_of::<usize>(); Ok(KOption::Uint) }
2268 b'f' => { *size = 4; Ok(KOption::Float) }
2269 b'n' => { *size = 8; Ok(KOption::Number) } b'd' => { *size = 8; Ok(KOption::Double) } b'i' => { *size = getnumlimit(fmt, pos, 4)?; Ok(KOption::Int) }
2272 b'I' => { *size = getnumlimit(fmt, pos, 4)?; Ok(KOption::Uint) }
2273 b's' => { *size = getnumlimit(fmt, pos, std::mem::size_of::<usize>() as i32)?; Ok(KOption::Kstring) }
2274 b'c' => {
2275 let n = getnum(fmt, pos, -1);
2276 if n == -1 {
2277 return Err(LuaError::runtime(format_args!("missing size for format option 'c'")));
2278 }
2279 *size = n as usize;
2280 Ok(KOption::Char)
2281 }
2282 b'z' => Ok(KOption::Zstr),
2283 b'x' => { *size = 1; Ok(KOption::Padding) }
2284 b'X' => Ok(KOption::Paddalign),
2285 b' ' => Ok(KOption::Nop),
2286 b'<' => { h.is_little = true; Ok(KOption::Nop) }
2287 b'>' => { h.is_little = false; Ok(KOption::Nop) }
2288 b'=' => { h.is_little = cfg!(target_endian = "little"); Ok(KOption::Nop) }
2289 b'!' => {
2290 let n = getnum(fmt, pos, NATIVE_MAX_ALIGN as i32);
2291 h.max_align = getnumlimit(fmt, pos, n)?;
2292 Ok(KOption::Nop)
2293 }
2294 _ => Err(LuaError::runtime(format_args!("invalid format option '{}'", opt as char)))
2295 }
2296}
2297
2298fn getdetails(
2302 h: &mut Header,
2303 total_size: usize,
2304 fmt: &[u8],
2305 pos: &mut usize,
2306 psize: &mut usize,
2307 ntoalign: &mut usize,
2308) -> Result<KOption, LuaError> {
2309 let opt = getoption(h, fmt, pos, psize)?;
2310 let mut align = *psize;
2311
2312 if opt == KOption::Paddalign {
2313 if *pos >= fmt.len() {
2315 return Err(LuaError::arg_error(1, "invalid next option for option 'X'"));
2316 }
2317 let mut dummy_size = 0usize;
2318 let next_opt = getoption(h, fmt, pos, &mut dummy_size)?;
2319 align = dummy_size;
2320 if next_opt == KOption::Char || align == 0 {
2321 return Err(LuaError::arg_error(1, "invalid next option for option 'X'"));
2322 }
2323 }
2324
2325 if align <= 1 || opt == KOption::Char {
2326 *ntoalign = 0;
2327 } else {
2328 if align > h.max_align {
2329 align = h.max_align;
2330 }
2331 if (align & (align - 1)) != 0 {
2332 return Err(LuaError::arg_error(1, "format asks for alignment not power of 2"));
2333 }
2334 *ntoalign = (align - (total_size & (align - 1))) & (align - 1);
2335 }
2336 Ok(opt)
2337}
2338
2339fn packint(buf: &mut Vec<u8>, mut n: u64, is_little: bool, size: usize, neg: bool) {
2343 let start = buf.len();
2344 buf.resize(start + size, 0);
2345 let slice = &mut buf[start..start + size];
2346 for i in 0..size {
2348 slice[if is_little { i } else { size - 1 - i }] = (n & MC as u64) as u8;
2349 n >>= NB;
2350 }
2351 if neg && size > SZINT {
2353 for i in SZINT..size {
2354 slice[if is_little { i } else { size - 1 - i }] = MC;
2355 }
2356 }
2357}
2358
2359fn copywithendian(dest: &mut [u8], src: &[u8], is_little: bool) {
2363 debug_assert_eq!(dest.len(), src.len());
2364 if is_little == cfg!(target_endian = "little") {
2365 dest.copy_from_slice(src);
2366 } else {
2367 for (d, s) in dest.iter_mut().zip(src.iter().rev()) {
2368 *d = *s;
2369 }
2370 }
2371}
2372
2373fn unpackint(state: &LuaState, data: &[u8], is_little: bool, size: usize, is_signed: bool) -> Result<i64, LuaError> {
2377 let limit = size.min(SZINT);
2378 let mut res: u64 = 0;
2379 for i in (0..limit).rev() {
2380 res <<= NB;
2381 let byte_idx = if is_little { i } else { size - 1 - i };
2382 res |= data[byte_idx] as u64;
2383 }
2384
2385 if size < SZINT {
2386 if is_signed {
2387 let mask: u64 = 1u64 << (size * NB as usize - 1);
2388 res = (res ^ mask).wrapping_sub(mask);
2389 }
2390 } else if size > SZINT {
2391 let mask = if !is_signed || (res as i64) >= 0 { 0u8 } else { MC };
2392 for i in limit..size {
2393 let byte_idx = if is_little { i } else { size - 1 - i };
2394 if data[byte_idx] != mask {
2395 return Err(LuaError::runtime(format_args!(
2396 "{}-byte integer does not fit into Lua Integer", size
2397 )));
2398 }
2399 }
2400 }
2401 Ok(res as i64)
2402}
2403
2404pub fn str_pack(state: &mut LuaState) -> Result<usize, LuaError> {
2408 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2409 let fmt = &fmt_bytes[..];
2410 let mut h = Header::new();
2411 let mut arg = 1i32;
2412 let mut total_size = 0usize;
2413 let mut buf: Vec<u8> = Vec::new();
2414 let mut pos = 0usize;
2415
2416 while pos < fmt.len() {
2417 let mut size = 0usize;
2418 let mut ntoalign = 0usize;
2419 let opt = getdetails(&mut h, total_size, fmt, &mut pos, &mut size, &mut ntoalign)?;
2420 total_size += ntoalign + size;
2421 for _ in 0..ntoalign {
2422 buf.push(PACK_PAD_BYTE);
2423 }
2424 arg += 1;
2425
2426 match opt {
2427 KOption::Int => {
2428 let n = state.check_arg_integer(arg)?;
2429 if size < SZINT {
2430 let lim: i64 = 1i64 << (size * NB as usize - 1);
2431 if !(-lim <= n && n < lim) {
2432 return Err(LuaError::arg_error(arg, "integer overflow"));
2433 }
2434 }
2435 packint(&mut buf, n as u64, h.is_little, size, n < 0);
2436 }
2437 KOption::Uint => {
2438 let n = state.check_arg_integer(arg)?;
2439 if size < SZINT {
2440 let lim: u64 = 1u64 << (size * NB as usize);
2441 if (n as u64) >= lim {
2442 return Err(LuaError::arg_error(arg, "unsigned overflow"));
2443 }
2444 }
2445 packint(&mut buf, n as u64, h.is_little, size, false);
2446 }
2447 KOption::Float => {
2448 let f = state.check_arg_number(arg)? as f32;
2449 let start = buf.len();
2450 buf.resize(start + 4, 0);
2451 copywithendian(&mut buf[start..start + 4], &f.to_bits().to_ne_bytes(), h.is_little);
2452 }
2453 KOption::Number => {
2454 let f = state.check_arg_number(arg)?;
2455 let start = buf.len();
2456 buf.resize(start + 8, 0);
2457 copywithendian(&mut buf[start..start + 8], &f.to_bits().to_ne_bytes(), h.is_little);
2458 }
2459 KOption::Double => {
2460 let f = state.check_arg_number(arg)? as f64;
2461 let start = buf.len();
2462 buf.resize(start + 8, 0);
2463 copywithendian(&mut buf[start..start + 8], &f.to_bits().to_ne_bytes(), h.is_little);
2464 }
2465 KOption::Char => {
2466 let s = state.check_arg_string(arg)?.to_vec();
2467 if s.len() > size {
2468 return Err(LuaError::arg_error(arg, "string longer than given size"));
2469 }
2470 buf.extend_from_slice(&s);
2471 let pad = size - s.len();
2472 for _ in 0..pad {
2473 buf.push(PACK_PAD_BYTE);
2474 }
2475 }
2476 KOption::Kstring => {
2477 let s = state.check_arg_string(arg)?.to_vec();
2478 let len = s.len();
2479 if size < SZINT && len >= (1usize << (size * 8)) {
2480 return Err(LuaError::arg_error(arg, "string length does not fit in given size"));
2481 }
2482 packint(&mut buf, len as u64, h.is_little, size, false);
2483 buf.extend_from_slice(&s);
2484 total_size += len;
2485 }
2486 KOption::Zstr => {
2487 let s = state.check_arg_string(arg)?.to_vec();
2488 if s.contains(&0) {
2489 return Err(LuaError::arg_error(arg, "string contains zeros"));
2490 }
2491 buf.extend_from_slice(&s);
2492 buf.push(0);
2493 total_size += s.len() + 1;
2494 }
2495 KOption::Padding => {
2496 buf.push(PACK_PAD_BYTE);
2497 arg -= 1; }
2499 KOption::Paddalign | KOption::Nop => {
2500 arg -= 1; }
2502 }
2503 }
2504
2505 state.push_bytes(&buf)?;
2506 Ok(1)
2507}
2508
2509pub fn str_packsize(state: &mut LuaState) -> Result<usize, LuaError> {
2513 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2514 let fmt = &fmt_bytes[..];
2515 let mut h = Header::new();
2516 let mut total_size = 0usize;
2517 let mut pos = 0usize;
2518
2519 while pos < fmt.len() {
2520 let mut size = 0usize;
2521 let mut ntoalign = 0usize;
2522 let opt = getdetails(&mut h, total_size, fmt, &mut pos, &mut size, &mut ntoalign)?;
2523 if opt == KOption::Kstring || opt == KOption::Zstr {
2524 return Err(LuaError::arg_error(1, "variable-length format"));
2525 }
2526 let space = ntoalign + size;
2527 if total_size > PACK_MAXSIZE - space {
2528 return Err(LuaError::arg_error(1, "format result too large"));
2529 }
2530 total_size += space;
2531 }
2532 state.push(LuaValue::Int(total_size as i64));
2533 Ok(1)
2534}
2535
2536pub fn str_unpack(state: &mut LuaState) -> Result<usize, LuaError> {
2540 let fmt_bytes = state.check_arg_string(1)?.to_vec();
2541 let data_bytes = state.check_arg_string(2)?.to_vec();
2542 let ld = data_bytes.len();
2543 let pos_raw = state.opt_arg_integer(3, 1)?;
2544 let mut pos = pos_relat_i(pos_raw, ld).saturating_sub(1);
2545
2546 if pos > ld {
2547 return Err(LuaError::arg_error(3, "initial position out of string"));
2548 }
2549
2550 let fmt = &fmt_bytes[..];
2551 let data = &data_bytes[..];
2552 let mut h = Header::new();
2553 let mut fmt_pos = 0usize;
2554 let mut n = 0usize;
2555
2556 while fmt_pos < fmt.len() {
2557 let mut size = 0usize;
2558 let mut ntoalign = 0usize;
2559 let opt = getdetails(&mut h, pos, fmt, &mut fmt_pos, &mut size, &mut ntoalign)?;
2560
2561 if ntoalign + size > ld - pos {
2562 return Err(LuaError::arg_error(2, "data string too short"));
2563 }
2564 pos += ntoalign;
2565 state.ensure_stack(2, "too many results")?;
2566 n += 1;
2567
2568 match opt {
2569 KOption::Int => {
2570 let v = unpackint(state, &data[pos..pos + size], h.is_little, size, true)?;
2571 state.push(LuaValue::Int(v));
2572 }
2573 KOption::Uint => {
2574 let v = unpackint(state, &data[pos..pos + size], h.is_little, size, false)?;
2575 state.push(LuaValue::Int(v));
2576 }
2577 KOption::Float => {
2578 let mut bytes = [0u8; 4];
2579 copywithendian(&mut bytes, &data[pos..pos + 4], h.is_little);
2580 let f = f32::from_bits(u32::from_ne_bytes(bytes));
2581 state.push(LuaValue::Float(f as f64));
2582 }
2583 KOption::Number => {
2584 let mut bytes = [0u8; 8];
2585 copywithendian(&mut bytes, &data[pos..pos + 8], h.is_little);
2586 let f = f64::from_bits(u64::from_ne_bytes(bytes));
2587 state.push(LuaValue::Float(f));
2588 }
2589 KOption::Double => {
2590 let mut bytes = [0u8; 8];
2591 copywithendian(&mut bytes, &data[pos..pos + 8], h.is_little);
2592 let f = f64::from_bits(u64::from_ne_bytes(bytes));
2593 state.push(LuaValue::Float(f));
2594 }
2595 KOption::Char => {
2596 state.push_bytes(&data[pos..pos + size])?;
2597 }
2598 KOption::Kstring => {
2599 let len = unpackint(state, &data[pos..pos + size], h.is_little, size, false)? as usize;
2600 if len > ld - pos - size {
2601 return Err(LuaError::arg_error(2, "data string too short"));
2602 }
2603 state.push_bytes(&data[pos + size..pos + size + len])?;
2604 pos += len;
2605 }
2606 KOption::Zstr => {
2607 let end = data[pos..].iter().position(|&b| b == 0)
2608 .ok_or_else(|| LuaError::arg_error(2, "unfinished string for format 'z'"))?;
2609 if pos + end >= ld {
2610 return Err(LuaError::arg_error(2, "unfinished string for format 'z'"));
2611 }
2612 state.push_bytes(&data[pos..pos + end])?;
2613 pos += end + 1;
2614 }
2615 KOption::Paddalign | KOption::Padding | KOption::Nop => {
2616 n -= 1; }
2618 }
2619 pos += size;
2620 }
2621
2622 state.push(LuaValue::Int((pos + 1) as i64));
2623 Ok(n + 1)
2624}
2625
2626pub const STRING_LIB: &[(&[u8], lua_CFunction)] = &[
2634 (b"byte", str_byte),
2635 (b"char", str_char),
2636 (b"dump", str_dump),
2637 (b"find", str_find),
2638 (b"format", str_format),
2639 (b"gmatch", gmatch),
2640 (b"gsub", str_gsub),
2641 (b"len", str_len),
2642 (b"lower", str_lower),
2643 (b"match", str_match),
2644 (b"rep", str_rep),
2645 (b"reverse", str_reverse),
2646 (b"sub", str_sub),
2647 (b"upper", str_upper),
2648 (b"pack", str_pack),
2649 (b"packsize", str_packsize),
2650 (b"unpack", str_unpack),
2651];
2652
2653pub const STRING_META_METHODS: &[(&[u8], lua_CFunction)] = &[
2657 (b"__add", arith_add),
2658 (b"__sub", arith_sub),
2659 (b"__mul", arith_mul),
2660 (b"__mod", arith_mod),
2661 (b"__pow", arith_pow),
2662 (b"__div", arith_div),
2663 (b"__idiv", arith_idiv),
2664 (b"__unm", arith_unm),
2665];
2666
2667pub fn createmetatable(state: &mut LuaState) -> Result<(), LuaError> {
2671 state.new_lib_table(STRING_META_METHODS)?;
2673 state.set_funcs(STRING_META_METHODS, 0)?;
2675 state.push_string(b"")?;
2677 let mt_idx = state.top_idx() - 2;
2678 let mt = state.get_at(mt_idx);
2679 state.push(mt);
2680 state.set_metatable(-2)?;
2681 state.pop_n(1);
2683 let strlib_idx = state.top_idx() - 2;
2685 let strlib = state.get_at(strlib_idx);
2686 state.push(strlib);
2687 state.set_field(-2, b"__index")?;
2688 state.pop_n(1);
2690 Ok(())
2691}
2692
2693pub fn luaopen_string(state: &mut LuaState) -> Result<usize, LuaError> {
2697 state.new_lib(STRING_LIB)?;
2699 createmetatable(state)?;
2700 Ok(1)
2701}
2702
2703