cmd: Avoid rereading batch file for every call/goto executed (try 2)
Frédéric Delanoy
frederic.delanoy at gmail.com
Sun Sep 4 18:42:23 CDT 2011
Added a labels cache.
Current code was parsing the whole file from start for every call/goto, making
it terribly slow to run the cmd test suite.
---
programs/cmd/builtins.c | 111 +++++++++++++++++++++++++++++++++++++---------
1 files changed, 89 insertions(+), 22 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index bbf2920..901f6a5 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -1342,10 +1342,89 @@ void WCMD_give_help (const WCHAR *command) {
return;
}
+/*******************************************************************
+ * get_label_position
+ *
+ * Retrieves the position of label in the current batch file.
+ *
+ * PARAMS
+ * label [I] input label, non NULL or "eof"
+ * pos [O] pointer to the file position of label, if found
+ *
+ * RETURNS
+ * Success: Returns TRUE.
+ * *pos points to the file position of label, relative to the
+ * beginning of the file.
+ * Failure: Returns FALSE.
+ */
+static BOOL get_label_position(const WCHAR *label, LARGE_INTEGER *pos) {
+
+ static BOOL label_cache_initialised = FALSE;
+ static WCHAR **keys = NULL;
+ WCHAR *key;
+ static LARGE_INTEGER *vals = NULL;
+ LARGE_INTEGER val;
+ static SIZE_T elemsCount = 0;
+ static SIZE_T capacity = 8;
+ int i;
+
+ if (!label_cache_initialised) {
+ /* fill the cache with labels */
+ static WCHAR string[MAX_PATH];
+ WCHAR *str;
+
+ keys = HeapAlloc(GetProcessHeap(), 0, capacity * sizeof(WCHAR*));
+ vals = HeapAlloc(GetProcessHeap(), 0, capacity * sizeof(LARGE_INTEGER));
+
+ SetFilePointer (context -> h, 0, NULL, FILE_BEGIN);
+ while (WCMD_fgets(string, sizeof(string)/sizeof(WCHAR), context->h)) {
+ str = string;
+ while (isspaceW (*str)) str++;
+ if (*str == ':') {
+ DWORD index = 0;
+ str++;
+ while (str[index] && (!isspaceW(str[index])))
+ index++;
+ str[index] = '\0';
+
+ if (elemsCount > 0 && (elemsCount == capacity)) {
+ capacity *= 2;
+ keys = HeapReAlloc(GetProcessHeap(), 0, keys,
+ capacity * sizeof(WCHAR*));
+ vals = HeapReAlloc(GetProcessHeap(), 0, vals,
+ capacity * sizeof(LARGE_INTEGER));
+ }
+
+ key = WCMD_strdupW(str);
+ keys[elemsCount] = key;
+ val.QuadPart = 0;
+ val.u.LowPart = SetFilePointer(context -> h, val.u.LowPart,
+ &val.u.HighPart, FILE_CURRENT);
+ vals[elemsCount] = val;
+ elemsCount++;
+ }
+ }
+ label_cache_initialised = TRUE;
+ }
+
+ /* A simple linear search should be quick and simple enough.
+ * Note: if the same label is "defined" twice, only the first one is used,
+ * as Windows does.
+ */
+ for (i = 0; i < elemsCount; i++) {
+ if (!lstrcmpiW(keys[i], label)) {
+ *pos = vals[i];
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
/****************************************************************************
- * WCMD_go_to
+ * WCMD_goto
*
- * Batch file jump instruction. Not the most efficient algorithm ;-)
+ * Batch file jump instruction.
* Prints error message if the specified label cannot be found - the file pointer is
* then at EOF, effectively stopping the batch file.
* FIXME: DOS is supposed to allow labels with spaces - we don't.
@@ -1353,9 +1432,6 @@ void WCMD_give_help (const WCHAR *command) {
void WCMD_goto (CMD_LIST **cmdList) {
- WCHAR string[MAX_PATH];
- WCHAR current[MAX_PATH];
-
/* Do not process any more parts of a processed multipart or multilines command */
if (cmdList) *cmdList = NULL;
@@ -1364,8 +1440,10 @@ void WCMD_goto (CMD_LIST **cmdList) {
return;
}
if (context != NULL) {
- WCHAR *paramStart = param1, *str;
+ WCHAR *paramStart = param1;
static const WCHAR eofW[] = {':','e','o','f','\0'};
+ BOOL label_found;
+ LARGE_INTEGER pos;
/* Handle special :EOF label */
if (lstrcmpiW (eofW, param1) == 0) {
@@ -1376,24 +1454,13 @@ void WCMD_goto (CMD_LIST **cmdList) {
/* Support goto :label as well as goto label */
if (*paramStart == ':') paramStart++;
- SetFilePointer (context -> h, 0, NULL, FILE_BEGIN);
- while (WCMD_fgets (string, sizeof(string)/sizeof(WCHAR), context -> h)) {
- str = string;
- while (isspaceW (*str)) str++;
- if (*str == ':') {
- DWORD index = 0;
- str++;
- while (((current[index] = str[index])) && (!isspaceW (current[index])))
- index++;
-
- /* ignore space at the end */
- current[index] = 0;
- if (lstrcmpiW (current, paramStart) == 0) return;
- }
+ label_found = get_label_position(paramStart, &pos);
+ if (label_found) {
+ SetFilePointer(context->h, pos.u.LowPart, &pos.u.HighPart, FILE_BEGIN);
+ } else {
+ WCMD_output (WCMD_LoadMessage(WCMD_NOTARGET));
}
- WCMD_output (WCMD_LoadMessage(WCMD_NOTARGET));
}
- return;
}
/*****************************************************************************
--
1.7.6
More information about the wine-patches
mailing list