Avoid the creation of too many strings in 'package'

Both when setting a path and searching for a file ('searchpath'),
this commit reduces the number of intermediate strings created
in Lua.
(For setting a path the change is not relevant, because this is
done only twice when loading the module. Anyway, it is a nice example
of how to use auxlib buffers to manipulate strings in the C API.)
This commit is contained in:
Roberto Ierusalimschy 2019-05-03 10:14:25 -03:00
parent b36e26f51b
commit b14609032c
4 changed files with 79 additions and 41 deletions

2
lgc.h
View File

@ -127,7 +127,7 @@
/* /*
** some gc parameters are stored divided by 4 to allow a maximum value ** some gc parameters are stored divided by 4 to allow a maximum value
** larger than 1000 in a 'lu_byte'. ** up to 1023 in a 'lu_byte'.
*/ */
#define getgcparam(p) ((p) * 4) #define getgcparam(p) ((p) * 4)
#define setgcparam(p,v) ((p) = (v) / 4) #define setgcparam(p,v) ((p) = (v) / 4)

View File

@ -39,7 +39,7 @@ typedef signed char ls_byte;
/* maximum value for size_t */ /* maximum value for size_t */
#define MAX_SIZET ((size_t)(~(size_t)0)) #define MAX_SIZET ((size_t)(~(size_t)0))
/* maximum size visible for Lua (must be representable in a lua_Integer */ /* maximum size visible for Lua (must be representable in a lua_Integer) */
#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \ #define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \
: (size_t)(LUA_MAXINTEGER)) : (size_t)(LUA_MAXINTEGER))

View File

@ -290,22 +290,33 @@ static int noenv (lua_State *L) {
static void setpath (lua_State *L, const char *fieldname, static void setpath (lua_State *L, const char *fieldname,
const char *envname, const char *envname,
const char *dft) { const char *dft) {
const char *dftmark;
const char *nver = lua_pushfstring(L, "%s%s", envname, LUA_VERSUFFIX); const char *nver = lua_pushfstring(L, "%s%s", envname, LUA_VERSUFFIX);
const char *path = getenv(nver); /* use versioned name */ const char *path = getenv(nver); /* try versioned name */
if (path == NULL) /* no environment variable? */ if (path == NULL) /* no versioned environment variable? */
path = getenv(envname); /* try unversioned name */ path = getenv(envname); /* try unversioned name */
if (path == NULL || noenv(L)) /* no environment variable? */ if (path == NULL || noenv(L)) /* no environment variable? */
lua_pushstring(L, dft); /* use default */ lua_pushstring(L, dft); /* use default */
else { else if ((dftmark = strstr(path, LUA_PATH_SEP LUA_PATH_SEP)) == NULL)
/* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */ lua_pushstring(L, path); /* nothing to change */
path = luaL_gsub(L, path, LUA_PATH_SEP LUA_PATH_SEP, else { /* path contains a ";;": insert default path in its place */
LUA_PATH_SEP AUXMARK LUA_PATH_SEP); size_t len = strlen(path);
luaL_gsub(L, path, AUXMARK, dft); luaL_Buffer b;
lua_remove(L, -2); /* remove result from 1st 'gsub' */ luaL_buffinit(L, &b);
if (path < dftmark) { /* is there a prefix before ';;'? */
luaL_addlstring(&b, path, dftmark - path); /* add it */
luaL_addchar(&b, *LUA_PATH_SEP);
}
luaL_addstring(&b, dft); /* add default */
if (dftmark < path + len - 2) { /* is there a sufix after ';;'? */
luaL_addchar(&b, *LUA_PATH_SEP);
luaL_addlstring(&b, dftmark + 2, (path + len - 2) - dftmark);
}
luaL_pushresult(&b);
} }
setprogdir(L); setprogdir(L);
lua_setfield(L, -3, fieldname); /* package[fieldname] = path value */ lua_setfield(L, -3, fieldname); /* package[fieldname] = path value */
lua_pop(L, 1); /* pop versioned variable name */ lua_pop(L, 1); /* pop versioned variable name ('nver') */
} }
/* }================================================================== */ /* }================================================================== */
@ -421,17 +432,26 @@ static int readable (const char *filename) {
} }
static const char *pushnextfilename (lua_State *L, const char *path) { /*
const char *l; ** Get the next name in '*path' = 'name1;name2;name3;...', changing
if (*path == *LUA_PATH_SEP) ** the ending ';' to '\0' to create a zero-terminated string. Return
path++; /* skip separator */ ** NULL when list ends.
if (*path == '\0') */
static const char *getnextfilename (char **path, char *end) {
char *sep;
char *name = *path;
if (name == end)
return NULL; /* no more names */ return NULL; /* no more names */
l = strchr(path, *LUA_PATH_SEP); /* find next separator */ else if (*name == '\0') { /* from previous iteration? */
if (l == NULL) /* no more separators? */ *name = *LUA_PATH_SEP; /* restore separator */
l = path + strlen(path); /* go until the end */ name++; /* skip it */
lua_pushlstring(L, path, l - path); /* file name */ }
return l; /* rest of the path */ sep = strchr(name, *LUA_PATH_SEP); /* find next separator */
if (sep == NULL) /* separator not found? */
sep = end; /* name goes until the end */
*sep = '\0'; /* finish file name */
*path = sep; /* will start next search from here */
return name;
} }
@ -442,12 +462,12 @@ static const char *pushnextfilename (lua_State *L, const char *path) {
** no file 'blublu.so' ** no file 'blublu.so'
*/ */
static void pusherrornotfound (lua_State *L, const char *path) { static void pusherrornotfound (lua_State *L, const char *path) {
if (*path == *LUA_PATH_SEP) luaL_Buffer b;
path++; /* skip separator */ luaL_buffinit(L, &b);
lua_pushstring(L, "\n\tno file '"); luaL_addstring(&b, "\n\tno file '");
luaL_gsub(L, path, LUA_PATH_SEP, "'\n\tno file '"); luaL_addgsub(&b, path, LUA_PATH_SEP, "'\n\tno file '");
lua_pushstring(L, "'"); luaL_addstring(&b, "'");
lua_concat(L, 3); luaL_pushresult(&b);
} }
@ -455,17 +475,24 @@ static const char *searchpath (lua_State *L, const char *name,
const char *path, const char *path,
const char *sep, const char *sep,
const char *dirsep) { const char *dirsep) {
luaL_Buffer buff;
char *pathname; /* path with name inserted */
char *endpathname; /* its end */
const char *filename;
/* separator is non-empty and appears in 'name'? */ /* separator is non-empty and appears in 'name'? */
if (*sep != '\0' && strchr(name, *sep) != NULL) if (*sep != '\0' && strchr(name, *sep) != NULL)
name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */ name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */
/* replace marks ('?') in 'path' by the file name */ luaL_buffinit(L, &buff);
path = luaL_gsub(L, path, LUA_PATH_MARK, name); /* add path to the buffer, replacing marks ('?') with the file name */
while ((path = pushnextfilename(L, path)) != NULL) { luaL_addgsub(&buff, path, LUA_PATH_MARK, name);
const char *filename = lua_tostring(L, -1); luaL_addchar(&buff, '\0');
pathname = luaL_buffaddr(&buff); /* writable list of file names */
endpathname = pathname + luaL_bufflen(&buff) - 1;
while ((filename = getnextfilename(&pathname, endpathname)) != NULL) {
if (readable(filename)) /* does file exist and is readable? */ if (readable(filename)) /* does file exist and is readable? */
return filename; /* return that file name */ return lua_pushstring(L, filename); /* save and return name */
lua_pop(L, 1); /* else remove file name */
} }
luaL_pushresult(&buff); /* push path to create error message */
pusherrornotfound(L, lua_tostring(L, -1)); /* create error message */ pusherrornotfound(L, lua_tostring(L, -1)); /* create error message */
return NULL; /* not found */ return NULL; /* not found */
} }

View File

@ -142,12 +142,18 @@ do
prepfile("print(package.path, package.cpath)") prepfile("print(package.path, package.cpath)")
RUN('env LUA_INIT="error(10)" LUA_PATH=xxx LUA_CPATH=xxx lua -E %s > %s', RUN('env LUA_INIT="error(10)" LUA_PATH=xxx LUA_CPATH=xxx lua -E %s > %s',
prog, out) prog, out)
local output = getoutput()
defaultpath = string.match(output, "^(.-)\t")
defaultCpath = string.match(output, "\t(.-)$")
-- running with an empty environment
RUN('env -i lua %s > %s', prog, out)
local out = getoutput() local out = getoutput()
defaultpath = string.match(out, "^(.-)\t") assert(defaultpath == string.match(output, "^(.-)\t"))
defaultCpath = string.match(out, "\t(.-)$") assert(defaultCpath == string.match(output, "\t(.-)$"))
end end
-- paths did not changed -- paths did not change
assert(not string.find(defaultpath, "xxx") and assert(not string.find(defaultpath, "xxx") and
string.find(defaultpath, "lua") and string.find(defaultpath, "lua") and
not string.find(defaultCpath, "xxx") and not string.find(defaultCpath, "xxx") and
@ -160,15 +166,20 @@ local function convert (p)
RUN('env LUA_PATH="%s" lua %s > %s', p, prog, out) RUN('env LUA_PATH="%s" lua %s > %s', p, prog, out)
local expected = getoutput() local expected = getoutput()
expected = string.sub(expected, 1, -2) -- cut final end of line expected = string.sub(expected, 1, -2) -- cut final end of line
assert(string.gsub(p, ";;", ";"..defaultpath..";") == expected) if string.find(p, ";;") then
p = string.gsub(p, ";;", ";"..defaultpath..";")
p = string.gsub(p, "^;", "") -- remove ';' at the beginning
p = string.gsub(p, ";$", "") -- remove ';' at the end
end
assert(p == expected)
end end
convert(";") convert(";")
convert(";;") convert(";;")
convert(";;;") convert("a;;b")
convert(";;;;") convert(";;b")
convert(";;;;;") convert("a;;")
convert(";;a;;;bc") convert("a;b;;c")
-- test -l over multiple libraries -- test -l over multiple libraries