aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2019-05-03 10:14:25 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2019-05-03 10:14:25 -0300
commitb14609032cf328dea48b0803f3e585e223283b3d (patch)
tree6f59256fb668d15a17ec41feef0d01304c2a1ed6
parentb36e26f51b117df98f0f5376f352c2381df3025f (diff)
downloadlua-b14609032cf328dea48b0803f3e585e223283b3d.tar.gz
lua-b14609032cf328dea48b0803f3e585e223283b3d.tar.bz2
lua-b14609032cf328dea48b0803f3e585e223283b3d.zip
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.)
-rw-r--r--lgc.h2
-rw-r--r--llimits.h2
-rw-r--r--loadlib.c89
-rw-r--r--testes/main.lua27
4 files changed, 79 insertions, 41 deletions
diff --git a/lgc.h b/lgc.h
index 9ba7ecb0..b972472f 100644
--- a/lgc.h
+++ b/lgc.h
@@ -127,7 +127,7 @@
127 127
128/* 128/*
129** some gc parameters are stored divided by 4 to allow a maximum value 129** some gc parameters are stored divided by 4 to allow a maximum value
130** larger than 1000 in a 'lu_byte'. 130** up to 1023 in a 'lu_byte'.
131*/ 131*/
132#define getgcparam(p) ((p) * 4) 132#define getgcparam(p) ((p) * 4)
133#define setgcparam(p,v) ((p) = (v) / 4) 133#define setgcparam(p,v) ((p) = (v) / 4)
diff --git a/llimits.h b/llimits.h
index cc983972..febf7555 100644
--- a/llimits.h
+++ b/llimits.h
@@ -39,7 +39,7 @@ typedef signed char ls_byte;
39/* maximum value for size_t */ 39/* maximum value for size_t */
40#define MAX_SIZET ((size_t)(~(size_t)0)) 40#define MAX_SIZET ((size_t)(~(size_t)0))
41 41
42/* maximum size visible for Lua (must be representable in a lua_Integer */ 42/* maximum size visible for Lua (must be representable in a lua_Integer) */
43#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \ 43#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \
44 : (size_t)(LUA_MAXINTEGER)) 44 : (size_t)(LUA_MAXINTEGER))
45 45
diff --git a/loadlib.c b/loadlib.c
index 4cf9aec3..ff73a459 100644
--- a/loadlib.c
+++ b/loadlib.c
@@ -290,22 +290,33 @@ static int noenv (lua_State *L) {
290static void setpath (lua_State *L, const char *fieldname, 290static void setpath (lua_State *L, const char *fieldname,
291 const char *envname, 291 const char *envname,
292 const char *dft) { 292 const char *dft) {
293 const char *dftmark;
293 const char *nver = lua_pushfstring(L, "%s%s", envname, LUA_VERSUFFIX); 294 const char *nver = lua_pushfstring(L, "%s%s", envname, LUA_VERSUFFIX);
294 const char *path = getenv(nver); /* use versioned name */ 295 const char *path = getenv(nver); /* try versioned name */
295 if (path == NULL) /* no environment variable? */ 296 if (path == NULL) /* no versioned environment variable? */
296 path = getenv(envname); /* try unversioned name */ 297 path = getenv(envname); /* try unversioned name */
297 if (path == NULL || noenv(L)) /* no environment variable? */ 298 if (path == NULL || noenv(L)) /* no environment variable? */
298 lua_pushstring(L, dft); /* use default */ 299 lua_pushstring(L, dft); /* use default */
299 else { 300 else if ((dftmark = strstr(path, LUA_PATH_SEP LUA_PATH_SEP)) == NULL)
300 /* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */ 301 lua_pushstring(L, path); /* nothing to change */
301 path = luaL_gsub(L, path, LUA_PATH_SEP LUA_PATH_SEP, 302 else { /* path contains a ";;": insert default path in its place */
302 LUA_PATH_SEP AUXMARK LUA_PATH_SEP); 303 size_t len = strlen(path);
303 luaL_gsub(L, path, AUXMARK, dft); 304 luaL_Buffer b;
304 lua_remove(L, -2); /* remove result from 1st 'gsub' */ 305 luaL_buffinit(L, &b);
306 if (path < dftmark) { /* is there a prefix before ';;'? */
307 luaL_addlstring(&b, path, dftmark - path); /* add it */
308 luaL_addchar(&b, *LUA_PATH_SEP);
309 }
310 luaL_addstring(&b, dft); /* add default */
311 if (dftmark < path + len - 2) { /* is there a sufix after ';;'? */
312 luaL_addchar(&b, *LUA_PATH_SEP);
313 luaL_addlstring(&b, dftmark + 2, (path + len - 2) - dftmark);
314 }
315 luaL_pushresult(&b);
305 } 316 }
306 setprogdir(L); 317 setprogdir(L);
307 lua_setfield(L, -3, fieldname); /* package[fieldname] = path value */ 318 lua_setfield(L, -3, fieldname); /* package[fieldname] = path value */
308 lua_pop(L, 1); /* pop versioned variable name */ 319 lua_pop(L, 1); /* pop versioned variable name ('nver') */
309} 320}
310 321
311/* }================================================================== */ 322/* }================================================================== */
@@ -421,17 +432,26 @@ static int readable (const char *filename) {
421} 432}
422 433
423 434
424static const char *pushnextfilename (lua_State *L, const char *path) { 435/*
425 const char *l; 436** Get the next name in '*path' = 'name1;name2;name3;...', changing
426 if (*path == *LUA_PATH_SEP) 437** the ending ';' to '\0' to create a zero-terminated string. Return
427 path++; /* skip separator */ 438** NULL when list ends.
428 if (*path == '\0') 439*/
440static const char *getnextfilename (char **path, char *end) {
441 char *sep;
442 char *name = *path;
443 if (name == end)
429 return NULL; /* no more names */ 444 return NULL; /* no more names */
430 l = strchr(path, *LUA_PATH_SEP); /* find next separator */ 445 else if (*name == '\0') { /* from previous iteration? */
431 if (l == NULL) /* no more separators? */ 446 *name = *LUA_PATH_SEP; /* restore separator */
432 l = path + strlen(path); /* go until the end */ 447 name++; /* skip it */
433 lua_pushlstring(L, path, l - path); /* file name */ 448 }
434 return l; /* rest of the path */ 449 sep = strchr(name, *LUA_PATH_SEP); /* find next separator */
450 if (sep == NULL) /* separator not found? */
451 sep = end; /* name goes until the end */
452 *sep = '\0'; /* finish file name */
453 *path = sep; /* will start next search from here */
454 return name;
435} 455}
436 456
437 457
@@ -442,12 +462,12 @@ static const char *pushnextfilename (lua_State *L, const char *path) {
442** no file 'blublu.so' 462** no file 'blublu.so'
443*/ 463*/
444static void pusherrornotfound (lua_State *L, const char *path) { 464static void pusherrornotfound (lua_State *L, const char *path) {
445 if (*path == *LUA_PATH_SEP) 465 luaL_Buffer b;
446 path++; /* skip separator */ 466 luaL_buffinit(L, &b);
447 lua_pushstring(L, "\n\tno file '"); 467 luaL_addstring(&b, "\n\tno file '");
448 luaL_gsub(L, path, LUA_PATH_SEP, "'\n\tno file '"); 468 luaL_addgsub(&b, path, LUA_PATH_SEP, "'\n\tno file '");
449 lua_pushstring(L, "'"); 469 luaL_addstring(&b, "'");
450 lua_concat(L, 3); 470 luaL_pushresult(&b);
451} 471}
452 472
453 473
@@ -455,17 +475,24 @@ static const char *searchpath (lua_State *L, const char *name,
455 const char *path, 475 const char *path,
456 const char *sep, 476 const char *sep,
457 const char *dirsep) { 477 const char *dirsep) {
478 luaL_Buffer buff;
479 char *pathname; /* path with name inserted */
480 char *endpathname; /* its end */
481 const char *filename;
458 /* separator is non-empty and appears in 'name'? */ 482 /* separator is non-empty and appears in 'name'? */
459 if (*sep != '\0' && strchr(name, *sep) != NULL) 483 if (*sep != '\0' && strchr(name, *sep) != NULL)
460 name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */ 484 name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */
461 /* replace marks ('?') in 'path' by the file name */ 485 luaL_buffinit(L, &buff);
462 path = luaL_gsub(L, path, LUA_PATH_MARK, name); 486 /* add path to the buffer, replacing marks ('?') with the file name */
463 while ((path = pushnextfilename(L, path)) != NULL) { 487 luaL_addgsub(&buff, path, LUA_PATH_MARK, name);
464 const char *filename = lua_tostring(L, -1); 488 luaL_addchar(&buff, '\0');
489 pathname = luaL_buffaddr(&buff); /* writable list of file names */
490 endpathname = pathname + luaL_bufflen(&buff) - 1;
491 while ((filename = getnextfilename(&pathname, endpathname)) != NULL) {
465 if (readable(filename)) /* does file exist and is readable? */ 492 if (readable(filename)) /* does file exist and is readable? */
466 return filename; /* return that file name */ 493 return lua_pushstring(L, filename); /* save and return name */
467 lua_pop(L, 1); /* else remove file name */
468 } 494 }
495 luaL_pushresult(&buff); /* push path to create error message */
469 pusherrornotfound(L, lua_tostring(L, -1)); /* create error message */ 496 pusherrornotfound(L, lua_tostring(L, -1)); /* create error message */
470 return NULL; /* not found */ 497 return NULL; /* not found */
471} 498}
diff --git a/testes/main.lua b/testes/main.lua
index b9dcab1c..aab490c8 100644
--- a/testes/main.lua
+++ b/testes/main.lua
@@ -142,12 +142,18 @@ do
142 prepfile("print(package.path, package.cpath)") 142 prepfile("print(package.path, package.cpath)")
143 RUN('env LUA_INIT="error(10)" LUA_PATH=xxx LUA_CPATH=xxx lua -E %s > %s', 143 RUN('env LUA_INIT="error(10)" LUA_PATH=xxx LUA_CPATH=xxx lua -E %s > %s',
144 prog, out) 144 prog, out)
145 local output = getoutput()
146 defaultpath = string.match(output, "^(.-)\t")
147 defaultCpath = string.match(output, "\t(.-)$")
148
149 -- running with an empty environment
150 RUN('env -i lua %s > %s', prog, out)
145 local out = getoutput() 151 local out = getoutput()
146 defaultpath = string.match(out, "^(.-)\t") 152 assert(defaultpath == string.match(output, "^(.-)\t"))
147 defaultCpath = string.match(out, "\t(.-)$") 153 assert(defaultCpath == string.match(output, "\t(.-)$"))
148end 154end
149 155
150-- paths did not changed 156-- paths did not change
151assert(not string.find(defaultpath, "xxx") and 157assert(not string.find(defaultpath, "xxx") and
152 string.find(defaultpath, "lua") and 158 string.find(defaultpath, "lua") and
153 not string.find(defaultCpath, "xxx") and 159 not string.find(defaultCpath, "xxx") and
@@ -160,15 +166,20 @@ local function convert (p)
160 RUN('env LUA_PATH="%s" lua %s > %s', p, prog, out) 166 RUN('env LUA_PATH="%s" lua %s > %s', p, prog, out)
161 local expected = getoutput() 167 local expected = getoutput()
162 expected = string.sub(expected, 1, -2) -- cut final end of line 168 expected = string.sub(expected, 1, -2) -- cut final end of line
163 assert(string.gsub(p, ";;", ";"..defaultpath..";") == expected) 169 if string.find(p, ";;") then
170 p = string.gsub(p, ";;", ";"..defaultpath..";")
171 p = string.gsub(p, "^;", "") -- remove ';' at the beginning
172 p = string.gsub(p, ";$", "") -- remove ';' at the end
173 end
174 assert(p == expected)
164end 175end
165 176
166convert(";") 177convert(";")
167convert(";;") 178convert(";;")
168convert(";;;") 179convert("a;;b")
169convert(";;;;") 180convert(";;b")
170convert(";;;;;") 181convert("a;;")
171convert(";;a;;;bc") 182convert("a;b;;c")
172 183
173 184
174-- test -l over multiple libraries 185-- test -l over multiple libraries