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 -