diff --git a/ChangeLog b/ChangeLog
index a8d3f3a63a..4a5d98edd2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -12,6 +12,12 @@ Changes in version 0.2.0.8-alpha - 2007-??-??
- Use annotations to record the source for each descriptor.
- Use annotations to record the purpose of each descriptor.
+ o Major bugfixes (performance):
+ - Fix really bad O(n^2) performance when parsing a long list of routers:
+ Instead of searching the entire list for an "extra-info " string which
+ usually wasn't there, once for every routerinfo we read, just scan
+ lines forward until we find one we like. Bugfix on 0.2.0.1.
+
o Minor bugfixes (controller):
- When sending a status event to the controller telling it that an
OR address is readable, set the port correctly. (Previously we
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index adf6ca3145..6526224801 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -855,6 +855,40 @@ check_signature_token(const char *digest,
return 0;
}
+/** DOCDOC */
+static int
+find_start_of_next_router_or_extrainfo(const char **s_ptr,
+ const char *eos,
+ int *is_extrainfo_out)
+{
+ const char *annotations = NULL;
+ const char *s = *s_ptr;
+
+ s = eat_whitespace_eos(s, eos);
+
+ while (s < eos-32) { /* 32 gives enough room for a the first keyword. */
+ /* We're at the start of a line. */
+ tor_assert(*s != '\n');
+
+ if (*s == '@' && !annotations) {
+ annotations = s;
+ } else if (*s == 'r' && !strcmpstart(s, "router ")) {
+ *s_ptr = annotations ? annotations : s;
+ *is_extrainfo_out = 0;
+ return 0;
+ } else if (*s == 'e' && !strcmpstart(s, "extra-info ")) {
+ *s_ptr = annotations ? annotations : s;
+ *is_extrainfo_out = 1;
+ return 0;
+ }
+
+ if (!(s = memchr(s+1, '\n', eos-(s+1))))
+ break;
+ s = eat_whitespace_eos(s, eos);
+ }
+ return -1;
+}
+
/** Given a string *s containing a concatenated sequence of router
* descriptors (or extra-info documents if is_extrainfo is set), parses
* them and stores the result in dest. All routers are marked running
@@ -893,40 +927,9 @@ router_parse_list_from_string(const char **s, const char *eos,
tor_assert(eos >= *s);
while (1) {
- *s = eat_whitespace_eos(*s, eos);
- if ((eos - *s) < 32) /* make sure it's long enough. */
+ if (find_start_of_next_router_or_extrainfo(s, eos, &have_extrainfo) < 0)
break;
- /* Don't start parsing the rest of *s unless it contains a router or
- * extra-info. */
- if (strcmpstart(*s, "extra-info ")==0) {
- have_extrainfo = 1;
- } else if (strcmpstart(*s, "router ")==0) {
- have_extrainfo = 0;
- } else {
- /* skip junk. */
- const char *annotation = NULL, *ei, *ri;
- if (**s == '@') {
- annotation = *s;
- } else {
- if ((annotation = tor_memstr(*s, eos-*s, "\n@")))
- ++annotation;
- }
-
- ei = tor_memstr(*s, eos-*s, "\nextra-info ");
- ri = tor_memstr(*s, eos-*s, "\nrouter ");
- if (ri && (!ei || ri < ei)) {
- have_extrainfo = 0;
- *s = ri + 1;
- } else if (ei) {
- have_extrainfo = 1;
- *s = ei + 1;
- } else {
- break;
- }
- if (annotation && annotation < *s)
- *s = annotation;
- }
end = tor_memstr(*s, eos-*s, "\nrouter-signature");
if (end)
end = tor_memstr(end, eos-end, "\n-----END SIGNATURE-----\n");