Win32: reduce environment array reallocations

Move environment array reallocation from do_putenv to the respective callers. Keep track of the environment size in a global variable. Use ALLOC_GROW in mingw_putenv to reduce reallocations. Allocate a sufficiently sized environment array in make_environment_block to prevent reallocations. Signed-off-by: Karsten Blees <blees@dcon.de> Signed-off-by: Stepan Kasal <kasal@ucw.cz> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Karsten Blees committed Jul 17, 2014 at 17:38 UTC f279242d5e3f17e01e1d70010c4f072798899a47
1 file changed +35 -27
compat/mingw.c
+35 -27
@@ -899,7 +899,12 @@ static char *path_lookup(const char *cmd, char **path, int exe_only)
899 return prog;
900 }
901
902 -static char **do_putenv(char **env, const char *name, int free_old);
902 +static int do_putenv(char **env, const char *name, int size, int free_old);
903 +
904 +/* used number of elements of environ array, including terminating NULL */
905 +static int environ_size = 0;
906 +/* allocated size of environ array, in bytes */
907 +static int environ_alloc = 0;
908
909 static int compareenv(const void *a, const void *b)
910 {
@@ -915,31 +920,28 @@ static int compareenv(const void *a, const void *b)
920 static wchar_t *make_environment_block(char **deltaenv)
921 {
922 wchar_t *wenvblk = NULL;
918 - int count = 0;
919 - char **e, **tmpenv;
920 - int size = 0, wenvsz = 0, wenvpos = 0;
923 + char **tmpenv;
924 + int i = 0, size = environ_size, wenvsz = 0, wenvpos = 0;
925
922 - while (environ[count])
923 - count++;
926 + while (deltaenv && deltaenv[i])
927 + i++;
928
925 - /* copy the environment */
926 - tmpenv = xmalloc(sizeof(*tmpenv) * (count + 1));
927 - memcpy(tmpenv, environ, sizeof(*tmpenv) * (count + 1));
929 + /* copy the environment, leaving space for changes */
930 + tmpenv = xmalloc((size + i) * sizeof(char*));
931 + memcpy(tmpenv, environ, size * sizeof(char*));
932
933 /* merge supplied environment changes into the temporary environment */
930 - for (e = deltaenv; e && *e; e++)
931 - tmpenv = do_putenv(tmpenv, *e, 0);
934 + for (i = 0; deltaenv && deltaenv[i]; i++)
935 + size = do_putenv(tmpenv, deltaenv[i], size, 0);
936
937 /* environment must be sorted */
934 - for (count = 0; tmpenv[count]; )
935 - count++;
936 - qsort(tmpenv, count, sizeof(*tmpenv), compareenv);
938 + qsort(tmpenv, size - 1, sizeof(char*), compareenv);
939
940 /* create environment block from temporary environment */
939 - for (e = tmpenv; *e; e++) {
940 - size = 2 * strlen(*e) + 2; /* +2 for final \0 */
941 + for (i = 0; tmpenv[i]; i++) {
942 + size = 2 * strlen(tmpenv[i]) + 2; /* +2 for final \0 */
943 ALLOC_GROW(wenvblk, (wenvpos + size) * sizeof(wchar_t), wenvsz);
942 - wenvpos += xutftowcs(&wenvblk[wenvpos], *e, size) + 1;
944 + wenvpos += xutftowcs(&wenvblk[wenvpos], tmpenv[i], size) + 1;
945 }
946 /* add final \0 terminator */
947 wenvblk[wenvpos] = 0;
@@ -1206,19 +1208,19 @@ static int lookupenv(char **env, const char *name, size_t nmln)
1208
1209 /*
1210 * If name contains '=', then sets the variable, otherwise it unsets it
1211 + * Size includes the terminating NULL. Env must have room for size + 1 entries
1212 + * (in case of insert). Returns the new size. Optionally frees removed entries.
1213 */
1210 -static char **do_putenv(char **env, const char *name, int free_old)
1214 +static int do_putenv(char **env, const char *name, int size, int free_old)
1215 {
1216 char *eq = strchrnul(name, '=');
1217 int i = lookupenv(env, name, eq-name);
1218
1219 if (i < 0) {
1220 if (*eq) {
1217 - for (i = 0; env[i]; i++)
1218 - ;
1219 - env = xrealloc(env, (i+2)*sizeof(*env));
1220 - env[i] = (char*) name;
1221 - env[i+1] = NULL;
1221 + env[size - 1] = (char*) name;
1222 + env[size] = NULL;
1223 + size++;
1224 }
1225 }
1226 else {
@@ -1226,11 +1228,13 @@ static char **do_putenv(char **env, const char *name, int free_old)
1228 free(env[i]);
1229 if (*eq)
1230 env[i] = (char*) name;
1229 - else
1231 + else {
1232 for (; env[i]; i++)
1233 env[i] = env[i+1];
1234 + size--;
1235 + }
1236 }
1233 - return env;
1237 + return size;
1238 }
1239
1240 #undef getenv
@@ -1248,7 +1252,8 @@ char *mingw_getenv(const char *name)
1252
1253 int mingw_putenv(const char *namevalue)
1254 {
1251 - environ = do_putenv(environ, namevalue, 1);
1255 + ALLOC_GROW(environ, (environ_size + 1) * sizeof(char*), environ_alloc);
1256 + environ_size = do_putenv(environ, namevalue, environ_size, 1);
1257 return 0;
1258 }
1259
@@ -2048,7 +2053,9 @@ void mingw_startup()
2053 maxlen = max(maxlen, wcslen(wenv[i]));
2054
2055 /* nedmalloc can't free CRT memory, allocate resizable environment list */
2051 - environ = xcalloc(i + 1, sizeof(char*));
2056 + environ = NULL;
2057 + environ_size = i + 1;
2058 + ALLOC_GROW(environ, environ_size * sizeof(char*), environ_alloc);
2059
2060 /* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */
2061 maxlen = 3 * maxlen + 1;
@@ -2065,6 +2072,7 @@ void mingw_startup()
2072 len = xwcstoutf(buffer, wenv[i], maxlen);
2073 environ[i] = xmemdupz(buffer, len);
2074 }
2075 + environ[i] = NULL;
2076 free(buffer);
2077
2078 /* initialize critical section for waitpid pinfo_t list */