coar 98/06/12 08:53:26
Modified: src CHANGES src/modules/standard mod_autoindex.c Log: I've been meaning to clean up these integer-as-string comparisons for a while, and encountered the misplacement of the parent directory entry during testing. Needing the parent-directory test in a second location prompted streamlining simplification of the method. Revision Changes Path 1.911 +9 -0 apache-1.3/src/CHANGES Index: CHANGES =================================================================== RCS file: /export/home/cvs/apache-1.3/src/CHANGES,v retrieving revision 1.910 retrieving revision 1.911 diff -u -r1.910 -r1.911 --- CHANGES 1998/06/10 21:13:26 1.910 +++ CHANGES 1998/06/12 15:53:21 1.911 @@ -1,5 +1,14 @@ Changes with Apache 1.3.1 + *) Improve performance of directory listings (mod_autoindex) by comparing + integer keys (last-modified and size) as integers rather than converting + them to strings first. Also use a set of explicit byte tests rather + than strcmp() to check for parent directory-ness of an entry. Oh, and + make sure the parent directory (if displayed) is *always* listed first + regardless of the sort key. Overall performance winnage should be good + in CPU time, instruction cache, and memory usage, particularly for large + directories. [Ken Coar] + *) Add httpd -t (test) option for running configuration syntax tests only. If something is broken it complains and exits with a return code non-equal to 0. This can be used manually by the user to check the Apache 1.79 +119 -60 apache-1.3/src/modules/standard/mod_autoindex.c Index: mod_autoindex.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/modules/standard/mod_autoindex.c,v retrieving revision 1.78 retrieving revision 1.79 diff -u -r1.78 -r1.79 --- mod_autoindex.c 1998/06/12 11:20:56 1.78 +++ mod_autoindex.c 1998/06/12 15:53:23 1.79 @@ -136,6 +136,37 @@ #define BY_PATH &c_by_path /* + * Return true if the specified string refers to the parent directory (i.e., + * matches ".." or "../"). Hopefully this one call is significantly less + * expensive than multiple strcmp() calls. + */ +static int is_parent(const char *name) +{ + /* + * If it's no name at all, it isn't our parent. + */ + if (name == NULL) { + return 0; + } + /* + * Nor is it if the name isn't long enough. + */ + if ((name[0] == '\0') || (name[1] == '\0')) { + return 0; + } + /* + * Now, IFF the first two bytes are dots, and the third byte is either + * EOS (\0) or a slash followed by EOS, we have a match. + */ + if (((name[0] == '.') && (name[1] == '.')) + && ((name[2] == '\0') + || ((name[2] == '/') && (name[3] == '\0')))) { + return 1; + } + return 0; +} + +/* * This routine puts the standard HTML header at the top of the index page. * We include the DOCTYPE because we may be using features therefrom (i.e., * HEIGHT and WIDTH attributes on the icons if we're FancyIndexing). @@ -417,9 +448,7 @@ char *alt; char *desc; size_t size; - char *size_cmp; time_t lm; - char *lm_cmp; struct ent *next; int ascending; char key; @@ -750,20 +779,15 @@ } ap_destroy_sub_req(rr); - } - if (keyid == K_SIZE) { - p->size_cmp = ap_palloc(r->pool, 14); - ap_snprintf(p->size_cmp, 14, "%013d", p->size); } + /* + * We don't need to take any special action for the file size key. If + * we did, it would go here. + */ if (keyid == K_LAST_MOD) { - struct tm *ts = localtime(&p->lm); - - if(ts) { - p->lm_cmp = ap_palloc(r->pool, 15); - strftime(p->lm_cmp, 15, "%Y%m%d%H%M%S", ts); + if (p->lm < 0) { + p->lm = 0; } - else - p->lm_cmp=NULL; } return (p); } @@ -892,7 +916,7 @@ ap_clear_pool(scratch); - if ((!strcmp(ar[x]->name, "../")) || (!strcmp(ar[x]->name, ".."))) { + if (is_parent(ar[x]->name)) { t = ap_make_full_path(scratch, name, "../"); ap_getparents(t); if (t[0] == '\0') { @@ -986,73 +1010,108 @@ } } +/* + * Compare two file entries according to the sort criteria. The return + * is essentially a signum function value. + */ static int dsortf(struct ent **e1, struct ent **e2) { char *s1; char *s2; - char *s3; + struct ent *c1; + struct ent *c2; int result; + int compare_by_string = 1; /* + * First, see if either of the entries is for the parent directory. + * If so, that *always* sorts lower than anything else. + */ + if (is_parent((*e1)->name)) { + return -1; + } + if (is_parent((*e2)->name)) { + return 1; + } + /* + * All of our comparisons will be of the c1 entry against the c2 one, + * so assign them appropriately to take care of the ordering. + */ + if ((*e1)->ascending) { + c1 = *e1; + c2 = *e2; + } + else { + c1 = *e2; + c2 = *e1; + } + /* * Choose the right values for the sort keys. */ - switch ((*e1)->key) { + switch (c1->key) { case K_LAST_MOD: - s1 = (*e1)->lm_cmp; - s2 = (*e2)->lm_cmp; + /* + * Since the last-modified time is a monotonically increasing integer, + * we can short-circuit this process with a simple comparison. + */ + result = c1->lm - c2->lm; + if (result != 0) { + result = (result > 0) ? 1 : -1; + } + compare_by_string = 0; break; case K_SIZE: - s1 = (*e1)->size_cmp; - s2 = (*e2)->size_cmp; + /* + * We can pull the same trick with the size as with the mtime. + */ + result = c1->size - c2->size; + if (result != 0) { + result = (result > 0) ? 1 : -1; + } + compare_by_string = 0; break; case K_DESC: - s1 = (*e1)->desc; - s2 = (*e2)->desc; + s1 = c1->desc; + s2 = c2->desc; break; case K_NAME: default: - s1 = (*e1)->name; - s2 = (*e2)->name; + s1 = c1->name; + s2 = c2->name; break; } - /* - * If we're supposed to sort in DEscending order, reverse the arguments. - */ - if (!(*e1)->ascending) { - s3 = s1; - s1 = s2; - s2 = s3; - } - /* - * Take some care, here, in case one string or the other (or both) is - * NULL. - */ - - /* - * Two valid strings, compare normally. - */ - if ((s1 != NULL) && (s2 != NULL)) { - result = strcmp(s1, s2); - } - /* - * Two NULL strings - primary keys are equal (fake it). - */ - else if ((s1 == NULL) && (s2 == NULL)) { - result = 0; - } - /* - * s1 is NULL, but s2 is a string - so s2 wins. - */ - else if (s1 == NULL) { - result = -1; - } - /* - * Last case: s1 is a string and s2 is NULL, so s1 wins. - */ - else { - result = 1; + if (compare_by_string) { + /* + * Take some care, here, in case one string or the other (or both) is + * NULL. + */ + + /* + * Two valid strings, compare normally. + */ + if ((s1 != NULL) && (s2 != NULL)) { + result = strcmp(s1, s2); + } + /* + * Two NULL strings - primary keys are equal (fake it). + */ + else if ((s1 == NULL) && (s2 == NULL)) { + result = 0; + } + /* + * s1 is NULL, but s2 is a string - so s2 wins. + */ + else if (s1 == NULL) { + result = -1; + } + /* + * Last case: s1 is a string and s2 is NULL, so s1 wins. + */ + else { + result = 1; + } } /* * If the keys were equal, the file name is *always* the secondary key -