cmd: Avoid rereading batch file for every call/goto executed (try 4)
Frédéric Delanoy
frederic.delanoy at gmail.com
Mon Sep 19 18:10:05 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 test suite.
try 4: Use MAXSTRING instead of MAX_PATH as WCHAR array size in build_label_cache_context function
try 3: code cleanup/factorisation
try 2: store labels cache in batch context
---
programs/cmd/batch.c | 11 ++++
programs/cmd/builtins.c | 149 ++++++++++++++++++++++++++++++++++++++++-------
programs/cmd/wcmd.h | 10 +++
3 files changed, 148 insertions(+), 22 deletions(-)
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c
index f988d95..0432b9a 100644
--- a/programs/cmd/batch.c
+++ b/programs/cmd/batch.c
@@ -48,6 +48,7 @@ void WCMD_batch (WCHAR *file, WCHAR *command, int called, WCHAR *startLabel, HAN
HANDLE h = INVALID_HANDLE_VALUE;
BATCH_CONTEXT *prev_context;
+ SIZE_T i;
if (startLabel == NULL) {
h = CreateFileW (file, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
@@ -73,6 +74,9 @@ void WCMD_batch (WCHAR *file, WCHAR *command, int called, WCHAR *startLabel, HAN
context->batchfileW = WCMD_strdupW(file);
context -> command = command;
memset(context -> shift_count, 0x00, sizeof(context -> shift_count));
+ context->labels.names = 0;
+ context->labels.positions = 0;
+ context->labels.count = 0;
context -> prev_context = prev_context;
context -> skip_rest = FALSE;
@@ -103,6 +107,13 @@ void WCMD_batch (WCHAR *file, WCHAR *command, int called, WCHAR *startLabel, HAN
*/
HeapFree(GetProcessHeap(), 0, context->batchfileW);
+ if (context->labels.names) {
+ for (i=0; i < context->labels.count; i++) {
+ HeapFree(GetProcessHeap(), 0, context->labels.names[i]);
+ }
+ HeapFree(GetProcessHeap(), 0, context->labels.names);
+ HeapFree(GetProcessHeap(), 0, context->labels.positions);
+ }
LocalFree (context);
if ((prev_context != NULL) && (!called)) {
prev_context -> skip_rest = TRUE;
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index 99a2314..da0dac6 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -1358,10 +1358,127 @@ void WCMD_give_help (const WCHAR *command) {
return;
}
+/*
+ * Builds the label cache for a given batch context (with no initialized label cache).
+ * Returns TRUE on success, FALSE otherwise
+ */
+static BOOL build_label_cache_context(BATCH_CONTEXT *context)
+{
+ WCHAR string[MAXSTRING];
+ WCHAR *str;
+ SIZE_T elems_count = 0, capacity = 8;
+ WCHAR **keys = HeapAlloc(GetProcessHeap(), 0, capacity * sizeof(*keys));
+ LARGE_INTEGER *vals = HeapAlloc(GetProcessHeap(), 0, capacity * sizeof(*vals));
+
+ if (!keys || !vals)
+ goto err;
+
+ SetFilePointer (context -> h, 0, NULL, FILE_BEGIN);
+ while (WCMD_fgets(string, sizeof(string)/sizeof(WCHAR), context->h)) {
+ WCHAR *key, *end;
+ LARGE_INTEGER val;
+
+ str = string;
+ while (isspaceW (*str)) str++;
+ if (*str != ':') continue;
+
+ for (end = ++str; *end && !isspaceW(*end); end++) ;
+ *end = '\0';
+
+ if (elems_count == capacity) {
+ WCHAR **_keys;
+ LARGE_INTEGER *_vals;
+ capacity *= 2;
+ if (!(_keys = HeapReAlloc(GetProcessHeap(), 0, keys, capacity * sizeof(*keys))))
+ goto err;
+ keys = _keys;
+ if (!(_vals = HeapReAlloc(GetProcessHeap(), 0, vals, capacity * sizeof(*vals))))
+ goto err;
+ vals = _vals;
+ }
+
+ if (!(key = WCMD_strdupW(str)))
+ goto err;
+ keys[elems_count] = key;
+ val.u.HighPart = 0;
+ val.u.LowPart = SetFilePointer(context -> h, 0, &val.u.HighPart, FILE_CURRENT);
+ vals[elems_count] = val;
+ elems_count++;
+ }
+ context->labels.names = keys;
+ context->labels.positions = vals;
+ context->labels.count = elems_count;
+
+ return TRUE;
+
+err:
+ HeapFree(GetProcessHeap(), 0, keys);
+ HeapFree(GetProcessHeap(), 0, vals);
+ WINE_ERR("Unable to allocate memory for labels cache!\n");
+ return FALSE;
+}
+
+/*
+ * Returns batch context containing the label cache associated to a given
+ * batch context, or NULL on error
+ */
+static BATCH_CONTEXT *get_label_cache_context(BATCH_CONTEXT *context)
+{
+ BATCH_CONTEXT *cache_context;
+
+ /* Locate (and possibly create) nearest cache, i.e. that of first non-label
+ * context ancestor (or self) */
+ cache_context = context;
+ while (*cache_context->command == ':')
+ cache_context = cache_context->prev_context;
+
+ if (cache_context->labels.names)
+ return cache_context;
+
+ return build_label_cache_context(cache_context) ? cache_context : NULL;
+}
+
+/*******************************************************************
+ * 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)
+{
+ SIZE_T i;
+ BATCH_CONTEXT *cache_context;
+
+ if (!(cache_context = get_label_cache_context(context)))
+ return FALSE;
+
+ /* 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 < cache_context->labels.count; i++) {
+ if (!lstrcmpiW(cache_context->labels.names[i], label)) {
+ *pos = cache_context->labels.positions[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.
@@ -1369,15 +1486,14 @@ 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;
if (context != NULL) {
- WCHAR *paramStart = param1, *str;
+ WCHAR *paramStart = param1;
static const WCHAR eofW[] = {':','e','o','f','\0'};
+ BOOL label_found;
+ LARGE_INTEGER pos;
if (param1[0] == 0x00) {
WCMD_output (WCMD_LoadMessage(WCMD_NOARG));
@@ -1393,24 +1509,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;
}
/*****************************************************************************
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h
index b7bc175..468871e 100644
--- a/programs/cmd/wcmd.h
+++ b/programs/cmd/wcmd.h
@@ -118,6 +118,14 @@ void WCMD_execute (const WCHAR *orig_command, const WCHAR *redirects,
const WCHAR *parameter, const WCHAR *substitution,
CMD_LIST **cmdList);
+/* Data structure to hold label/file position cache */
+
+typedef struct {
+ WCHAR **names; /* List of labels, in file order */
+ LARGE_INTEGER *positions; /* Associated file offsets */
+ SIZE_T count; /* Number of (labels/pos) stored */
+} CMD_LABELS;
+
/* Data structure to hold context when executing batch files */
typedef struct _BATCH_CONTEXT {
@@ -125,6 +133,8 @@ typedef struct _BATCH_CONTEXT {
HANDLE h; /* Handle to the open batch file */
WCHAR *batchfileW; /* Name of same */
int shift_count[10]; /* Offset in terms of shifts for %0 - %9 */
+ CMD_LABELS labels; /* Labels and associated file positions (offsets) from
+ beginning of the file */
struct _BATCH_CONTEXT *prev_context; /* Pointer to the previous context block */
BOOL skip_rest; /* Skip the rest of the batch program and exit */
CMD_LIST *toExecute; /* Commands left to be executed */
--
1.7.6.3
More information about the wine-patches
mailing list