package string import ( "math/rand" "regexp" "strings" "time" "unicode/utf8" luajit "git.sharkk.net/Sky/LuaJIT-to-Go" ) const ( maxStringLength = 10_000_000 // 10MB limit for safety maxRandomLength = 100_000 // Reasonable limit for random strings ) func GetFunctionList() map[string]luajit.GoFunction { return map[string]luajit.GoFunction{ "string_split": string_split, "string_join": string_join, "string_slice": string_slice, "string_reverse": string_reverse, "string_length": string_length, "string_byte_length": string_byte_length, "regex_match": regex_match, "regex_find": regex_find, "regex_find_all": regex_find_all, "regex_replace": regex_replace, "random_string": random_string, "string_is_valid_utf8": string_is_valid_utf8, } } func string_split(s *luajit.State) int { str := s.ToString(1) sep := s.ToString(2) if len(str) > maxStringLength { s.PushNil() s.PushString("string too large") return 2 } // Handle empty separator - split into characters if sep == "" { runes := []rune(str) parts := make([]string, len(runes)) for i, r := range runes { parts[i] = string(r) } s.PushValue(parts) return 1 } parts := strings.Split(str, sep) s.PushValue(parts) return 1 } func string_join(s *luajit.State) int { arr, err := s.ToValue(1) if err != nil { s.PushNil() s.PushString("invalid array") return 2 } sep := s.ToString(2) var parts []string switch v := arr.(type) { case []string: parts = v case []any: parts = make([]string, len(v)) for i, val := range v { if val == nil { parts[i] = "" } else { parts[i] = s.ToString(-1) // Convert via Lua } } case map[string]any: // Empty table {} from Lua becomes map[string]any{} if len(v) == 0 { parts = []string{} // Empty array } else { s.PushNil() s.PushString("not an array") return 2 } default: s.PushNil() s.PushString("not an array") return 2 } result := strings.Join(parts, sep) if len(result) > maxStringLength { s.PushNil() s.PushString("result too large") return 2 } s.PushString(result) return 1 } func string_slice(s *luajit.State) int { str := s.ToString(1) start := int(s.ToNumber(2)) if !utf8.ValidString(str) { s.PushNil() s.PushString("invalid UTF-8") return 2 } runes := []rune(str) length := len(runes) startIdx := start - 1 // Convert from 1-indexed if startIdx < 0 { startIdx = 0 } if startIdx >= length { s.PushString("") return 1 } endIdx := length if s.GetTop() >= 3 && !s.IsNil(3) { end := int(s.ToNumber(3)) if end < 0 { endIdx = length + end + 1 } else { endIdx = end } if endIdx < 0 { endIdx = 0 } if endIdx > length { endIdx = length } } if startIdx >= endIdx { s.PushString("") return 1 } s.PushString(string(runes[startIdx:endIdx])) return 1 } func string_reverse(s *luajit.State) int { str := s.ToString(1) if !utf8.ValidString(str) { s.PushNil() s.PushString("invalid UTF-8") return 2 } runes := []rune(str) for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { runes[i], runes[j] = runes[j], runes[i] } s.PushString(string(runes)) return 1 } func string_length(s *luajit.State) int { str := s.ToString(1) s.PushNumber(float64(utf8.RuneCountInString(str))) return 1 } func string_byte_length(s *luajit.State) int { str := s.ToString(1) s.PushNumber(float64(len(str))) return 1 } func regex_match(s *luajit.State) int { pattern := s.ToString(1) str := s.ToString(2) re, err := regexp.Compile(pattern) if err != nil { s.PushBoolean(false) return 1 } s.PushBoolean(re.MatchString(str)) return 1 } func regex_find(s *luajit.State) int { pattern := s.ToString(1) str := s.ToString(2) re, err := regexp.Compile(pattern) if err != nil { s.PushNil() return 1 } match := re.FindString(str) if match == "" { s.PushNil() } else { s.PushString(match) } return 1 } func regex_find_all(s *luajit.State) int { pattern := s.ToString(1) str := s.ToString(2) re, err := regexp.Compile(pattern) if err != nil { s.PushValue([]string{}) return 1 } matches := re.FindAllString(str, -1) if matches == nil { matches = []string{} } s.PushValue(matches) return 1 } func regex_replace(s *luajit.State) int { pattern := s.ToString(1) str := s.ToString(2) replacement := s.ToString(3) re, err := regexp.Compile(pattern) if err != nil { s.PushString(str) return 1 } result := re.ReplaceAllString(str, replacement) s.PushString(result) return 1 } func random_string(s *luajit.State) int { length := int(s.ToNumber(1)) if length < 0 || length > maxRandomLength { s.PushNil() s.PushString("invalid length") return 2 } charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" if s.GetTop() >= 2 && !s.IsNil(2) { charset = s.ToString(2) } if length == 0 { s.PushString("") return 1 } if !utf8.ValidString(charset) { s.PushNil() s.PushString("invalid charset") return 2 } charsetRunes := []rune(charset) if len(charsetRunes) == 0 { s.PushNil() s.PushString("empty charset") return 2 } result := make([]rune, length) rnd := rand.New(rand.NewSource(time.Now().UnixNano())) for i := range result { result[i] = charsetRunes[rnd.Intn(len(charsetRunes))] } s.PushString(string(result)) return 1 } func string_is_valid_utf8(s *luajit.State) int { str := s.ToString(1) s.PushBoolean(utf8.ValidString(str)) return 1 }