Skip to content

Deprecate LSL String functions#74

Open
tapple wants to merge 2 commits intosecondlife:mainfrom
tapple:deprecate-lsl-string
Open

Deprecate LSL String functions#74
tapple wants to merge 2 commits intosecondlife:mainfrom
tapple:deprecate-lsl-string

Conversation

@tapple
Copy link
Contributor

@tapple tapple commented Mar 13, 2026

Functions I don't expect to be controversial:

Functions that might be controversial:

  • ll.GetSubString
  • ll.SubStringIndex
  • ll.DeleteSubString
  • ll.ReplaceSubString
  • ll.Ord
  • ll.Char

Functions I'm considering deprecating the luau version of instead:

DeleteKeyValue: (Key: string) -> uuid,
DeleteSubList: <T>(Source: {T}, Start: number, End: number) -> {T},
DeleteSubString: (Source: string, Start: number, End: number) -> string,
DeleteSubString: @[deprecated {use='string.sub'}](Source: string, Start: number, End: number) -> string,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

takes byte index rather than codepoint index

GetStaticPath: (Start: vector, End: vector, Radius: number, Parameters: list) -> {any},
GetStatus: (StatusFlag: number) -> boolean,
GetSubString: (String: string, Start: number, End: number) -> string,
GetSubString: @[deprecated {use='string.sub'}](String: string, Start: number, End: number) -> string,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

takes byte index rather than codepoint index

StringToBase64: @[deprecated {use='llbase64.encode'}](Text: string) -> string,
StringTrim: (Text: string, TrimType: number) -> string,
SubStringIndex: (Text: string, Sequence: string) -> number?,
SubStringIndex: @[deprecated {use='string.find'}](Text: string, Sequence: string) -> number?,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

returns byte index rather than codepoint index

ReplaceAgentEnvironment: (agent_id: uuid, transition: number, environment: string | uuid) -> number,
ReplaceEnvironment: (position: vector, environment: string | uuid, track_no: number, day_length: number, day_offset: number) -> number,
ReplaceSubString: (InitialString: string, SubString: string, NewSubString: string, Count: number) -> string,
ReplaceSubString: @[deprecated {use='string.gsub'}](InitialString: string, SubString: string, NewSubString: string, Count: number) -> string,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not equivilant if the pattern contains regex characters

WorldPosToHUD: (world_pos: vector) -> vector,
XorBase64: (Text1: string, Text2: string) -> string,
XorBase64Strings: @[deprecated {use='ll.XorBase64'}](Text1: string, Text2: string) -> string,
XorBase64StringsCorrect: @[deprecated {use='ll.XorBase64'}](Text1: string, Text2: string) -> string,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surprised there isn't a buffer.xor to replace this with

HTTPRequest: (URL: string, Parameters: list, Body: string) -> uuid,
HTTPResponse: (HTTPRequestID: uuid, Status: number, Body: string) -> (),
Hash: (value: string) -> number,
InsertString: (TargetVariable: string, Position: number, SourceVariable: string) -> string,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought of deprecating ll.InsertString in favor of string interpolation, but, I don't think it's a good replacement

@Kristyku
Copy link

Kristyku commented Mar 13, 2026

ll.Ord
ll.Char
ll.ToUpper
ll.ToLower

Cannot be deprecated, they support Unicode while Luau does not.

TextBox: (AvatarID: uuid, Text: string, Channel: number) -> (),
ToLower: (Text: string) -> string,
ToUpper: (Text: string) -> string,
ToLower: @[deprecated {use='string.lower'}](Text: string) -> string,
Copy link

@Suzanna-Linn Suzanna-Linn Mar 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ll.ToLower()is useful with accents:

print(string.lower("CAFÉ"))  -- > cafÉ
print(ll.ToLower("CAFÉ"))    -- > café

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

print(string.lower("ДдAa")) -- Ддaa
print(ll.ToLower("ДдAa")) -- ддaa

Sounds like maybe string.lower should be deprecated instead

ToLower: (Text: string) -> string,
ToUpper: (Text: string) -> string,
ToLower: @[deprecated {use='string.lower'}](Text: string) -> string,
ToUpper: @[deprecated {use='string.upper'}](Text: string) -> string,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ll.ToUpper()is useful with accents:

print(string.upper("café"))  -- > CAFé
print(ll.ToUpper("café"))    -- > CAFÉ

ToLower: (Text: string) -> string,
ToUpper: (Text: string) -> string,
ToLower: @[deprecated {use='string.lower'}](Text: string) -> string,
ToUpper: @[deprecated {use='string.upper'}](Text: string) -> string,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

print(string.upper("ДдAa")) -- ДдAA
print(ll.ToUpper("ДдAa")) --  ДДAA

sounds like maybe string.upper should be deprecated instead

@Suzanna-Linn
Copy link

Suzanna-Linn commented Mar 13, 2026

I think that most of the LL string functions are useful when working with extended characters.

The advantage of the SLua string library is that is faster (because they don't need to look at what kind of characters are in the string).

The best is to use ones or the others depending on what strings we have.

@Kristyku
Copy link

Here's code:

print("Дд")
print("Luau Lower: " .. string.lower("Дд"))
print("Luau Upper: " .. string.upper("Дд"))

print("SL Lower: " .. ll.ToLower("Дд"))
print("SL Upper: " .. ll.ToUpper("Дд"))

print("Luau Ord: " .. string.byte("Д"))
print("SL Ord: " .. ll.Ord("Д", 1))

local x = pcall(function() return string.char(1044) end)
print("Luau Char: " .. tostring(x))
print("SL Char: " .. ll.Char(1044))

and the output:

Object: Дд
Object: Luau Lower: Дд
Object: Luau Upper: Дд
Object: SL Lower: дд
Object: SL Upper: ДД
Object: Luau Ord: 208
Object: SL Ord: 1044
Object: Luau Char: false
Object: SL Char: Д

OpenFloater: (floater_name: string, url: string, params: list) -> number,
OpenRemoteDataChannel: @deprecated () -> (),
Ord: (value: string, index: number) -> number,
Ord: @[deprecated {reason="Use 'utf8.codepoint' or 'string.byte' instead."}](value: string, index: number) -> number,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find an issue with ord:

print("Luau Ord: " .. utf8.codepoint("Д")) -- 1044
print("SL Ord: " .. ll.Ord("Д", 1)) -- 1044

Copy link
Contributor Author

@tapple tapple Mar 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Takes byte index rather than codepoint index:

print(ll.Ord("ДД", 2)) -- 1044
print(utf8.codepoint("ДД", 2)) -- invalid UTF-8 code
print(utf8.codepoint("ДД", 3)) -- 1044

CastRay: (Start: vector, End: vector, Options: list) -> {any},
Ceil: (Value: number) -> number,
Char: (value: number) -> string,
Char: @[deprecated {reason="Use 'utf8.char' or 'string.char' instead."}](value: number) -> string,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find an issue with char:

print("Luau Char: " .. utf8.char(1044)) -- Д
print("SL Char: " .. ll.Char(1044)) -- Д

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to do a full spectrum check, as per the caveats I added on the wiki page for llChar

Copy link
Contributor Author

@tapple tapple Mar 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrote a verification script:

for i = 0, 0x10FFFF do
--for i = 0, 0x10 do
--for i = 0xFFEE, 0x10FFFF do
--for i = 0xD800, 0xFFFF do
    if 0xD800 <= i and i < 0xE000 then
        -- surrogate pairs
        local luau_char = utf8.char(i)
        local utf8_raw = string.char(
            bit32.bor(0xE0, bit32.rshift(i, 12)),
            bit32.bor(0x80, bit32.band(0x3F, bit32.rshift(i, 6))),
            bit32.bor(0x80, bit32.band(0x3F, i))
        )
        assert(luau_char == utf8_raw)--, string.format('utf8.char(0x%x) incorrect', i))
        local luau_ord_valid, luau_ord = pcall(utf8.codepoint, luau_char)
        assert(not luau_ord_valid)--, string.format('"utf8.codepoint("\\u{%x}") did not error', i))
        assert(luau_ord == "invalid UTF-8 code") 
        -- lsl silliness --
        assert(ll.Char(i) == '\u{FFFD}')--, string.format('ll.Char(0x%x) incorrect', i))
        assert(ll.Ord(luau_char, 1) == 0x3F)--, string.format('ll.Ord("\\u{%x}", 1) incorrect', i))
    else
        local lsl_char = ll.Char(i)
        local luau_char = utf8.char(i)
        local lsl_ord = ll.Ord(luau_char, 1)
        local luau_ord = utf8.codepoint(luau_char)
        assert(luau_ord == i)--, string.format('utf8.codepoint("%s") = 0x%x ~= 0x%x', luau_char, luau_ord, i))
        if 0xFFFE <= i and i <= 0xFFFF then -- lsl silliness --
            assert(lsl_char == '\u{FFFD}')--, string.format('ll.Char(0x%x) incorrect', i))
        elseif i == 0 then -- lsl silliness --
            assert(lsl_char == '')--, string.format('ll.Char(0x%x) incorrect', i))
        else
            assert(lsl_char == luau_char)--, string.format('ll.Char(0x%x) = "%s" ~= utf8.char(0x%x) = "%s"', i, lsl_char, i, luau_char))
        end
        if 0xFFFE == i then -- lsl silliness --
            assert(lsl_ord == 0x3F)--, string.format('ll.Ord("\\u{%x}", 1) incorrect', i))
        else
            assert(lsl_ord == i)--, string.format('ll.Ord("%s") = 0x%x ~= 0x%x', luau_char, lsl_ord, i))
        end
    end
end
print("done")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants