Harden strsplit against third-party global clobbering

BigWigs (SpellRequests) redefines the global string:split to return a table,
and another addon clobbers the strsplit global too. pfUI.api.strsplit and the
bare strsplit callers inherited the broken versions depending on load order,
producing 'attempt to compare number with nil' from GetStringColor.

- Make pfUI.api.strsplit fully self-contained (no delegation to global
  strsplit / string.split), so no override can reach it.
- Optimize the hot path: single-char delimiters use plain-text find (no
  pattern compilation, no per-call char-class string); localize string.find
  and string.sub.
- Route the per-frame castbar/nameplate color splits through GetStringColor,
  which caches, instead of re-splitting a constant string every update.
This commit is contained in:
Brues
2026-07-29 10:36:26 -05:00
parent 6b945c2c97
commit 0156d9dfec
3 changed files with 15 additions and 18 deletions
+11 -12
View File
@@ -92,25 +92,24 @@ gfind = string.gmatch or string.gfind
mod = math.mod or mod
-- [ strsplit ]
-- Splits a string using a delimiter. Thin wrapper that delegates to
-- ClassicAPI's C-level strsplit, kept as a pfUI.api entry point for
-- backwards compatibility with addons that call pfUI.api.strsplit.
-- Note: unlike the old Lua implementation, empty fields are preserved
-- (e.g. "a,,b" -> "a", "", "b"), matching real strsplit semantics.
-- Splits a string using a delimiter. Self-contained on purpose: it does NOT
-- delegate to the global strsplit / string.split, because third-party addons
-- clobber those (e.g. BigWigs' SpellRequests redefines string:split to return
-- a table, which would make this return a single table instead of r,g,b,a and
-- break color/version parsing depending on load order). Delimiter chars are
-- treated as a set (any one char splits), and empty fields are preserved
-- ("a,,b" -> "a", "", "b"), matching real strsplit semantics.
-- 'delimiter' [string] characters that will be interpreted as delimiter
-- characters (bytes) in the string.
-- 'subject' [string] String to split.
-- return: [list] a list of strings.
local stringsplit = _G.string.split
local format, sgsub = string.format, string.gsub
function pfUI.api.strsplit(delimiter, subject)
if not subject then return nil end
delimiter = delimiter or ":"
if stringsplit then
return stringsplit(delimiter, subject)
end
local fields = {}
local pattern = string.format("([^%s]+)", delimiter)
string.gsub(subject, pattern, function(c) fields[table.getn(fields)+1] = c end)
delimiter = delimiter or ":"
local pattern = format("([^%s]+)", delimiter)
sgsub(subject, pattern, function(c) fields[table.getn(fields)+1] = c end)
return unpack(fields)
end