From 127c7534fc4aaa9499c552fdd3e4e9e56c16ca40 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 11 Oct 2012 01:19:10 +1100 Subject: [PATCH] rfc822: Index headers by name Replace the current brute force implementation of rfc822_next_header_of_name() with one using a hash table to (lazily) index the header fields by name. Signed-off-by: David Gibson --- ccan/rfc822/rfc822.c | 113 +++++++++++++++++++++++++++++++++++++++++-- ccan/rfc822/rfc822.h | 5 +- 2 files changed, 111 insertions(+), 7 deletions(-) diff --git a/ccan/rfc822/rfc822.c b/ccan/rfc822/rfc822.c index f5ffd958..7f9442a8 100644 --- a/ccan/rfc822/rfc822.c +++ b/ccan/rfc822/rfc822.c @@ -57,10 +57,18 @@ void rfc822_set_allocation_failure_handler(void (*h)(const char *)) } \ } while (0) +/* + * No real point doing fancy resizing hashes, when any given mail + * message is unlikely to have more than a fairly small number of + * distinct header types. This should be ample. + */ +#define INDEX_HASH_SIZE 63 + struct rfc822_msg { const char *data, *end; const char *remainder; struct list_head headers; + struct list_head header_index[INDEX_HASH_SIZE]; const char *body; }; @@ -68,6 +76,14 @@ struct rfc822_header { struct bytestring all, rawname, rawvalue; struct bytestring unfolded; struct list_node list; + struct rfc822_header *name_next; +}; + +struct rfc822_headers_of_name { + struct bytestring name; + struct rfc822_header *first; + struct rfc822_header **lastptr; + struct list_node bucket; }; struct rfc822_msg *rfc822_check(const struct rfc822_msg *msg, @@ -88,6 +104,7 @@ struct rfc822_msg *rfc822_check(const struct rfc822_msg *msg, struct rfc822_msg *rfc822_start(const void *ctx, const char *p, size_t len) { struct rfc822_msg *msg; + int i; msg = talloc(ctx, struct rfc822_msg); ALLOC_CHECK(msg, NULL); @@ -100,6 +117,9 @@ struct rfc822_msg *rfc822_start(const void *ctx, const char *p, size_t len) list_head_init(&msg->headers); + for (i = 0; i < INDEX_HASH_SIZE; i++) + list_head_init(&msg->header_index[i]); + CHECK(msg, "header_index[hash], hn, bucket) { + if (hdr_name_eq(hn->name, name)) + return hn; + } + + return NULL; +} + +static struct rfc822_header *index_header(struct rfc822_msg *msg, + struct rfc822_header *hdr) +{ + struct bytestring hname = rfc822_header_raw_name(msg, hdr); + struct rfc822_headers_of_name *hn = headers_of_name(msg, hname); + + if (!hn) { + unsigned hash = headerhash(hname); + + hn = talloc_zero(msg, struct rfc822_headers_of_name); + ALLOC_CHECK(hn, NULL); + + hn->name = hname; + hn->first = NULL; + hn->lastptr = &hn->first; + list_add_tail(&msg->header_index[hash], &hn->bucket); + } + + hdr->name_next = NULL; + *(hn->lastptr) = hdr; + hn->lastptr = &hdr->name_next; + return hdr; +} + +struct rfc822_header *rfc822_first_header_of_name(struct rfc822_msg *msg, + const char *name) +{ + struct bytestring namebs = bytestring_from_string(name); + struct rfc822_headers_of_name *hn = headers_of_name(msg, namebs); + struct rfc822_header *hdr; + + if (hn) + return hn->first; + + do { + hdr = next_header_parse(msg); + if (hdr && rfc822_header_is(msg, hdr, name)) + return hdr; + } while (hdr); + + return NULL; +} + struct rfc822_header *rfc822_next_header_of_name(struct rfc822_msg *msg, struct rfc822_header *hdr, const char *name) { + if (!hdr) + return rfc822_first_header_of_name(msg, name); + + if (hdr->name_next) { + assert(rfc822_header_is(msg, hdr->name_next, name)); + return hdr->name_next; + } + do { - hdr = rfc822_next_header(msg, hdr); - } while (hdr && !rfc822_header_is(msg, hdr, name)); + hdr = next_header_parse(msg); + if (hdr && rfc822_header_is(msg, hdr, name)) + return hdr; + } while (hdr); - return hdr; + return NULL; } diff --git a/ccan/rfc822/rfc822.h b/ccan/rfc822/rfc822.h index 19fccea8..24fb3cc3 100644 --- a/ccan/rfc822/rfc822.h +++ b/ccan/rfc822/rfc822.h @@ -180,11 +180,10 @@ struct bytestring rfc822_header_unfolded_value(struct rfc822_msg *msg, bool rfc822_header_is(struct rfc822_msg *msg, struct rfc822_header *hdr, const char *name); +struct rfc822_header *rfc822_first_header_of_name(struct rfc822_msg *msg, + const char *name); struct rfc822_header *rfc822_next_header_of_name(struct rfc822_msg *msg, struct rfc822_header *hdr, const char *name); -#define rfc822_first_header_of_name(_msg, _name) \ - (rfc822_next_header_of_name((_msg), NULL, (_name))) - #endif /* CCAN_RFC822_H_ */ -- 2.39.2