@@ -32,36 +32,13 @@ static int ll_getlistlength(lua_State *L)
3232 return 1 ;
3333}
3434
35- static int _to_positive_index (lua_State *L, int len, int idx, bool compat_mode)
36- {
37- if (!compat_mode)
38- {
39- if (idx == 0 )
40- {
41- luaL_error (L, " passed 0 when a 1-based index was expected" );
42- }
43- else if (idx > 0 )
44- {
45- // positive indices need to be shifted down to be 0-based.
46- // negative indices are handled later.
47- idx -= 1 ;
48- }
49- }
50-
51- if (idx < 0 )
52- {
53- return len + idx;
54- }
55- return idx;
56- }
57-
5835static int _list_accessor_helper (lua_State *L, LSLIType type)
5936{
6037 luaL_checktype (L, 1 , LUA_TTABLE);
6138 auto *h = hvalue (luaA_toobject (L, 1 ));
6239 int len = luaH_getn (h);
6340 bool compat_mode = lua_toboolean (L, lua_upvalueindex (1 ));
64- int idx = _to_positive_index (L, len, luaL_checkinteger (L, 2 ) , compat_mode);
41+ int idx = luaSL_checkobjectindex (L, len, 2 , compat_mode);
6542 if (idx < len && idx >= 0 )
6643 {
6744 lua_pushcfunction (L, lsl_cast_list_elem, " lsl_cast_list_elem" );
@@ -121,7 +98,7 @@ static int ll_list2vector(lua_State *L)
12198
12299 bool compat_mode = lua_toboolean (L, lua_upvalueindex (1 ));
123100
124- int idx = _to_positive_index (L, len, luaL_checkinteger (L, 2 ) , compat_mode);
101+ int idx = luaSL_checkobjectindex (L, len, 2 , compat_mode);
125102 if (idx < len && idx >= 0 )
126103 {
127104 // This accessor does NOT auto-cast!
@@ -144,7 +121,7 @@ static int ll_list2rot(lua_State *L)
144121
145122 bool compat_mode = lua_toboolean (L, lua_upvalueindex (1 ));
146123
147- int idx = _to_positive_index (L, len, luaL_checkinteger (L, 2 ) , compat_mode);
124+ int idx = luaSL_checkobjectindex (L, len, 2 , compat_mode);
148125 if (idx < len && idx >= 0 )
149126 {
150127 // This accessor does NOT auto-cast!
@@ -167,7 +144,7 @@ static int ll_getlistentrytype(lua_State *L)
167144
168145 bool compat_mode = lua_toboolean (L, lua_upvalueindex (1 ));
169146
170- int idx = _to_positive_index (L, len, luaL_checkinteger (L, 2 ) , compat_mode);
147+ int idx = luaSL_checkobjectindex (L, len, 2 , compat_mode);
171148 if (idx < len && idx >= 0 )
172149 {
173150 luaSL_pushinteger (L, lua_lsl_type (&h->array [idx]));
@@ -194,8 +171,8 @@ static int ll_list2list(lua_State *L)
194171
195172 bool compat_mode = lua_toboolean (L, lua_upvalueindex (1 ));
196173
197- int target1 = _to_positive_index (L, len, luaL_checkunsigned (L, 2 ) , compat_mode);
198- int target2 = _to_positive_index (L, len, luaL_checkunsigned (L, 3 ) , compat_mode);
174+ int target1 = luaSL_checkobjectindex (L, len, 2 , compat_mode);
175+ int target2 = luaSL_checkobjectindex (L, len, 3 , compat_mode);
199176
200177 int wanted_len = 0 ;
201178
@@ -337,8 +314,8 @@ static int ll_deletesublist(lua_State *L)
337314
338315 bool compat_mode = lua_toboolean (L, lua_upvalueindex (1 ));
339316
340- int target1 = _to_positive_index (L, len, luaL_checkunsigned (L, 2 ) , compat_mode);
341- int target2 = _to_positive_index (L, len, luaL_checkunsigned (L, 3 ) , compat_mode);
317+ int target1 = luaSL_checkobjectindex (L, len, 2 , compat_mode);
318+ int target2 = luaSL_checkobjectindex (L, len, 3 , compat_mode);
342319
343320 if (target1 <= target2)
344321 {
@@ -420,7 +397,7 @@ static int ll_listinsertlist(lua_State *L)
420397
421398 bool compat_mode = lua_toboolean (L, lua_upvalueindex (1 ));
422399
423- int target = _to_positive_index (L, dest_len, luaL_checkunsigned (L, 3 ) , compat_mode);
400+ int target = luaSL_checkobjectindex (L, dest_len, 3 , compat_mode);
424401
425402 LuaTable *cloned_h = nullptr ;
426403 TValue new_tv;
@@ -498,7 +475,7 @@ static int ll_listreplacelist(lua_State *L)
498475
499476 bool compat_mode = lua_toboolean (L, lua_upvalueindex (1 ));
500477
501- int target = _to_positive_index (L, orig_len, luaL_checkunsigned (L, 3 ) , compat_mode);
478+ int target = luaSL_checkobjectindex (L, orig_len, 3 , compat_mode);
502479
503480 LuaTable *cloned_h = nullptr ;
504481 TValue new_tv;
@@ -813,7 +790,6 @@ static const luaL_Reg llcompateligiblelib[] = {
813790 {NULL , NULL },
814791};
815792
816-
817793// These are functions that may be used in tests or in the REPL,
818794// but are not to be used in user-provided scripts
819795static int ll_stringlength (lua_State *L)
@@ -824,14 +800,35 @@ static int ll_stringlength(lua_State *L)
824800
825801static int ll_getsubstring (lua_State* L)
826802{
827- // Not even close to LSL semantics, also not memory-safe, but
828- // useful for demo purposes. Should never be used outside of tests.
829- // TODO: Make this crap use the utf8 <-> codepoint stuff.
830- const auto *str_val = luaL_checkstring (L, 1 );
831- const auto start_idx = luaL_checkinteger (L, 2 );
832- const auto end_idx = luaL_checkinteger (L, 3 );
803+ // UTF-8 aware substring extraction with 1-based indexing (SLua mode)
804+ // or 0-based indexing (LSL compat mode). Supports negative indices.
805+ // For demo/test purposes only, as it doesn't truncate at null like the real one.
806+ size_t str_len;
807+ const char *str_val = luaL_checklstring (L, 1 , &str_len);
808+ bool compat_mode = lua_toboolean (L, lua_upvalueindex (1 ));
809+
810+ // Convert UTF-8 string to codepoints
811+ CodepointString codepoints = utf8str_to_codepoints (str_val, str_len);
812+ auto num_codes = (ptrdiff_t )codepoints.length ();
833813
834- lua_pushlstring (L, str_val + start_idx, end_idx - start_idx + 1 );
814+ // Normalize indices (handles 1-based vs 0-based and negative indices)
815+ int start_idx = luaSL_checkobjectindex (L, (int )num_codes, 2 , compat_mode);
816+ int end_idx = luaSL_checkobjectindex (L, (int )num_codes, 3 , compat_mode);
817+
818+ // Bounds checking - return empty string if out of bounds
819+ if (start_idx < 0 || start_idx >= num_codes || end_idx < 0 || end_idx >= num_codes || start_idx > end_idx)
820+ {
821+ lua_pushstring (L, " " );
822+ return 1 ;
823+ }
824+
825+ // Extract substring from codepoint array
826+ ptrdiff_t substr_len = end_idx - start_idx + 1 ;
827+ CodepointString substr = codepoints.substr (start_idx, substr_len);
828+
829+ // Convert back to UTF-8
830+ std::string result = codepoints_to_utf8str (substr, substr.length ());
831+ lua_pushlstring (L, result.c_str (), result.length ());
835832 return 1 ;
836833}
837834
@@ -893,9 +890,13 @@ static int ll_generatekey(lua_State *L)
893890 return 1 ;
894891}
895892
896- static const luaL_Reg lltestlib[] = {
897- // This requires weird unicode semantics we shouldn't re-implement by hand.
893+ // Test functions that need compat mode handling for index semantics
894+ static const luaL_Reg lltestcompateligiblelib[] = {
898895 {" GetSubString" , ll_getsubstring},
896+ {NULL , NULL },
897+ };
898+
899+ static const luaL_Reg lltestlib[] = {
899900 {" OwnerSay" , ll_ownersay},
900901 // We have to use LL's internal implementation because there are unicode
901902 // semantics we need to consider here.
@@ -922,6 +923,8 @@ int luaopen_ll(lua_State* L, int testing_funcs)
922923 // Pepper in some extra functions if we're testing
923924 luaL_register_noclobber (L, LUA_LLLIBNAME, lltestlib);
924925 lua_pop (L, 1 );
926+ luaL_register_noclobber_compat (L, LUA_LLLIBNAME, lltestcompateligiblelib, true );
927+ lua_pop (L, 1 );
925928 }
926929 }
927930 else
@@ -940,8 +943,12 @@ int luaopen_ll(lua_State* L, int testing_funcs)
940943 // Pepper in some extra functions if we're testing
941944 luaL_register_noclobber (L, LUA_LLLIBNAME, lltestlib);
942945 lua_pop (L, 1 );
946+ luaL_register_noclobber_compat (L, LUA_LLLIBNAME, lltestcompateligiblelib, false );
947+ lua_pop (L, 1 );
943948 luaL_register_noclobber (L, LUA_LLCOMPATLIBNAME, lltestlib);
944949 lua_pop (L, 1 );
950+ luaL_register_noclobber_compat (L, LUA_LLCOMPATLIBNAME, lltestcompateligiblelib, true );
951+ lua_pop (L, 1 );
945952 }
946953 }
947954 return 1 ;
0 commit comments