From: Florent Bruneau Date: Tue, 11 Nov 2008 11:25:41 +0000 (+0100) Subject: Avoid useless strlen, faster query_format. X-Git-Url: http://git.madism.org/?a=commitdiff_plain;h=8847829f22135d594241a9c51877966e7cb0716a;p=apps%2Fpfixtools.git Avoid useless strlen, faster query_format. Signed-off-by: Florent Bruneau --- diff --git a/common/str.h b/common/str.h index bc1cf9e..bd2d052 100644 --- a/common/str.h +++ b/common/str.h @@ -371,5 +371,18 @@ m_stristr(const char *haystack, const char *needle) { return m_stristrn(haystack, needle, m_strlen(needle)); } +/****************************************************************************/ +/* static strings */ +/****************************************************************************/ + +/** Store a pointer to a string with a pre-computed length. + * This intends to store pointers to a part of a longer string and to avoid + * useless strlen. + */ +typedef struct static_str_t { + const char *str; + ssize_t len; +} static_str_t; + /*@}*/ #endif /* PFIXTOOLS_STR_H */ diff --git a/postlicyd/Makefile b/postlicyd/Makefile index cdbca81..e0b8474 100644 --- a/postlicyd/Makefile +++ b/postlicyd/Makefile @@ -36,7 +36,7 @@ GENERATED = policy_tokens.h policy_tokens.c \ filter_tokens.h filter_tokens.c \ hook_tokens.h hook_tokens.c \ param_tokens.h param_tokens.c -TESTS = tst-rbl tst-filters tst-greylist +TESTS = tst-rbl tst-filters tst-greylist tst-qf UB_LIBS = -lunbound @@ -54,9 +54,11 @@ tst-filters_LIBADD = $(UB_LIBS) $(TC_LIBS) -lev tst-greylist_SOURCES = tst-greylist.c resources.c ../common/lib.a tst-greylist_LIBADD = $(TC_LIBS) -hook_tokens.h hook_tokens.c: $(FILTERS) -param_tokens.c param_tokens.h: $(FILTERS) config.c +tst-qf_SOURCES = tst-qf.c query.c ../common/lib.a $(GENERATED) all: +hook_tokens.h hook_tokens.c: $(FILTERS) +param_tokens.c param_tokens.h: $(FILTERS) config.c + include ../mk/common.mk diff --git a/postlicyd/greylist.c b/postlicyd/greylist.c index 9626c20..beba34d 100644 --- a/postlicyd/greylist.c +++ b/postlicyd/greylist.c @@ -39,6 +39,7 @@ #include "str.h" #include "resources.h" +static const static_str_t static_cleanup = { "@@cleanup@@", 11 }; typedef struct greylist_config_t { unsigned lookup_by_host : 1; @@ -113,7 +114,7 @@ static inline bool greylist_db_need_cleanup(const greylist_config_t *config, TCB { int len = 0; time_t now = time(NULL); - const time_t *last_cleanup = tcbdbget3(db, "@@cleanup@@", strlen("@@cleanup@@"), &len); + const time_t *last_cleanup = tcbdbget3(db, static_cleanup.str, static_cleanup.len, &len); if (last_cleanup == NULL) { debug("No last cleanup time"); } else { @@ -193,7 +194,7 @@ static TCBDB **greylist_db_get(const greylist_config_t *config, const char *path } ++old_count; } while (tcbdbcurnext(cur)); - tcbdbput(tmp_db, "@@cleanup@@", strlen("@@cleanup@@"), &now, sizeof(now)); + tcbdbput(tmp_db, static_cleanup.str, static_cleanup.len, &now, sizeof(now)); } tcxstrdel(key); tcxstrdel(value); @@ -364,15 +365,15 @@ static const char *c_net(const greylist_config_t *config, static bool try_greylist(const greylist_config_t *config, - const char *sender, const char *c_addr, - const char *c_name, const char *rcpt) + const static_str_t *sender, const static_str_t *c_addr, + const static_str_t *c_name, const static_str_t *rcpt) { #define INCR_AWL \ aent.count++; \ aent.last = now; \ debug("whitelist entry for %.*s updated, count %d", \ - c_addrlen, c_addr, aent.count); \ - tcbdbput(awl_db, c_addr, c_addrlen, &aent, sizeof(aent)); + c_addr->len, c_addr->str, aent.count); \ + tcbdbput(awl_db, c_addr->str, c_addr->len, &aent, sizeof(aent)); char sbuf[BUFSIZ], cnet[64], key[BUFSIZ]; const void *res; @@ -381,31 +382,31 @@ static bool try_greylist(const greylist_config_t *config, struct obj_entry oent = { now, now }; struct awl_entry aent = { 0, 0 }; - int len, klen, c_addrlen = strlen(c_addr); + int len, klen; TCBDB * const awl_db = config->awl_db ? *(config->awl_db) : NULL; TCBDB * const obj_db = config->obj_db ? *(config->obj_db) : NULL; /* Auto whitelist clients. */ if (config->client_awl) { - res = tcbdbget3(awl_db, c_addr, c_addrlen, &len); + res = tcbdbget3(awl_db, c_addr->str, c_addr->len, &len); if (res && len == sizeof(aent)) { memcpy(&aent, res, len); debug("client %.*s has a whitelist entry, count is %d", - c_addrlen, c_addr, aent.count); + c_addr->len, c_addr->str, aent.count); } if (!greylist_check_awlentry(config, &aent, now)) { aent.count = 0; aent.last = 0; debug("client %.*s whitelist entry too old", - c_addrlen, c_addr); + c_addr->len, c_addr->str); } /* Whitelist if count is enough. */ if (aent.count >= config->client_awl) { - debug("client %.*s whitelisted", c_addrlen, c_addr); + debug("client %.*s whitelisted", c_addr->len, c_addr->str); if (now < aent.last + 3600) { INCR_AWL } @@ -419,9 +420,9 @@ static bool try_greylist(const greylist_config_t *config, /* Lookup. */ klen = snprintf(key, sizeof(key), "%s/%s/%s", - c_net(config, c_addr, c_name, cnet, sizeof(cnet)), - config->no_sender ? "" : sender_normalize(sender, sbuf, sizeof(sbuf)), - config->no_recipient ? "" : rcpt); + c_net(config, c_addr->str, c_name->str, cnet, sizeof(cnet)), + config->no_sender ? "" : sender_normalize(sender->str, sbuf, sizeof(sbuf)), + config->no_recipient ? "" : rcpt->str); klen = MIN(klen, ssizeof(key) - 1); res = tcbdbget3(obj_db, key, klen, &len); @@ -550,8 +551,8 @@ static filter_result_t greylist_filter(const filter_t *filter, return HTK_ABORT; } - return try_greylist(config, query->sender, query->client_address, - query->client_name, query->recipient) ? + return try_greylist(config, &query->sender, &query->client_address, + &query->client_name, &query->recipient) ? HTK_WHITELIST : HTK_GREYLIST; } diff --git a/postlicyd/iplist.c b/postlicyd/iplist.c index 353dc08..efc3b86 100644 --- a/postlicyd/iplist.c +++ b/postlicyd/iplist.c @@ -487,13 +487,13 @@ static filter_result_t iplist_filter(const filter_t *filter, const query_t *quer const iplist_filter_t *data = filter->data; bool error = true; - if (parse_ipv4(query->client_address, &end, &ip) != 0) { - if (strchr(query->client_address, ':')) { + if (parse_ipv4(query->client_address.str, &end, &ip) != 0) { + if (strchr(query->client_address.str, ':')) { /* iplist only works on IPv4 */ return HTK_FAIL; } warn("invalid client address: %s, expected ipv4", - query->client_address); + query->client_address.str); return HTK_ERROR; } for (uint32_t i = 0 ; i < data->rbls.len ; ++i) { diff --git a/postlicyd/main-postlicyd.c b/postlicyd/main-postlicyd.c index a4543ec..b6d3e2d 100644 --- a/postlicyd/main-postlicyd.c +++ b/postlicyd/main-postlicyd.c @@ -167,7 +167,7 @@ static bool policy_process(client_t *pcy, const config_t *mconfig) const query_t* query = &context->query; const filter_t *filter; if (mconfig->entry_points[query->state] == -1) { - warn("no filter defined for current protocol_state (%s)", smtp_state_names[query->state]); + warn("no filter defined for current protocol_state (%s)", smtp_state_names[query->state].str); return false; } if (context->context.current_filter != NULL) { @@ -225,9 +225,10 @@ static int policy_run(client_t *pcy, void* vconfig) query->eoq = eoq + strlen("\n\n"); /* The instance changed => reset the static context */ - if (query->instance == NULL || strcmp(context->context.instance, query->instance) != 0) { + if (query->instance.str == NULL || query->instance.len == 0 + || strcmp(context->context.instance, query->instance.str) != 0) { filter_context_clean(&context->context); - m_strcat(context->context.instance, 64, query->instance); + m_strcat(context->context.instance, 64, query->instance.str); } client_io_none(pcy); return policy_process(pcy, mconfig) ? 0 : -1; diff --git a/postlicyd/match.c b/postlicyd/match.c index 558bc88..d00b6d1 100644 --- a/postlicyd/match.c +++ b/postlicyd/match.c @@ -49,8 +49,7 @@ typedef struct match_condition_t { MATCH_EMPTY, } condition; - char *value; - ssize_t value_len; + static_str_t value; } match_condition_t; ARRAY(match_condition_t) @@ -63,7 +62,7 @@ static const char *condition_names[] = { "is empty" }; -#define CONDITION_INIT { PTK_UNKNOWN, false, MATCH_UNKNOWN, NULL, 0 } +#define CONDITION_INIT { PTK_UNKNOWN, false, MATCH_UNKNOWN, { NULL, 0 } } typedef struct match_config_t { A(match_condition_t) conditions; @@ -77,8 +76,10 @@ static match_config_t *match_config_new(void) static inline void match_condition_wipe(match_condition_t *condition) { - p_delete(&condition->value); - condition->value_len = 0; + char *str = (char*)condition->value.str; + p_delete(&str); + condition->value.str = NULL; + condition->value.len = 0; } static void match_config_delete(match_config_t **config) @@ -157,8 +158,8 @@ static bool match_filter_constructor(filter_t *filter) if (condition.condition != MATCH_EMPTY) { p = skipspaces(n + 1); PARSE_CHECK(*p, "no value defined to check the condition"); - condition.value_len = param->value_len - (p - param->value); - condition.value = p_dupstr(p, condition.value_len); + condition.value.len = param->value_len - (p - param->value); + condition.value.str = p_dupstr(p, condition.value.len); } array_add(config->conditions, condition); } break; @@ -182,50 +183,50 @@ static void match_filter_destructor(filter_t *filter) static inline bool match_condition(const match_condition_t *cond, const query_t *query) { - const char *field = query_field_for_id(query, cond->field); + const static_str_t *field = query_field_for_id(query, cond->field); debug("running condition: \"%s\" %s %s\"%s\"", - field, condition_names[cond->condition], + field->str, condition_names[cond->condition], cond->case_sensitive ? "" : "(alternative) ", - cond->value ? cond->value : "(none)"); + cond->value.str ? cond->value.str : "(none)"); switch (cond->condition) { case MATCH_EQUAL: case MATCH_DIFFER: - if (field == NULL) { + if (field == NULL || field->str == NULL) { return cond->condition != MATCH_DIFFER; } if (cond->case_sensitive) { - return !!((strcmp(field, cond->value) == 0) + return !!((strcmp(field->str, cond->value.str) == 0) ^ (cond->condition == MATCH_DIFFER)); } else { - return !!((ascii_strcasecmp(field, cond->value) == 0) + return !!((ascii_strcasecmp(field->str, cond->value.str) == 0) ^ (cond->condition == MATCH_DIFFER)); } break; case MATCH_CONTAINS: - if (field == NULL) { + if (field == NULL || field->str == NULL) { return false; } if (cond->case_sensitive) { - return strstr(field, cond->value); + return strstr(field->str, cond->value.str); } else { - return m_stristrn(field, cond->value, cond->value_len); + return m_stristrn(field->str, cond->value.str, cond->value.len); } break; case MATCH_CONTAINED: - if (field == NULL) { + if (field == NULL || field->str == NULL) { return false; } if (cond->case_sensitive) { - return strstr(cond->value, field); + return strstr(cond->value.str, field->str); } else { - return m_stristr(cond->value, field); + return m_stristr(cond->value.str, field->str); } break; case MATCH_EMPTY: - return !!((field == NULL || *field == '\0') ^ (!cond->case_sensitive)); + return !!((field == NULL || field->len == 0) ^ (!cond->case_sensitive)); default: assert(false && "invalid condition type"); diff --git a/postlicyd/query.c b/postlicyd/query.c index 599b9ec..f24a5d2 100644 --- a/postlicyd/query.c +++ b/postlicyd/query.c @@ -38,17 +38,20 @@ #include "policy_tokens.h" #include "str.h" -const char *smtp_state_names[SMTP_count] = { - "CONNECT", - "HELO", - "MAIL", - "RCPT", - "DATA", - "END-OF-MESSAGE", - "VRFY", - "ETRN", +const static_str_t smtp_state_names[SMTP_count] = { + { "CONNECT", 7 }, + { "HELO", 4 }, + { "MAIL", 4 }, + { "RCPT", 4 }, + { "DATA", 4 }, + { "END-OF-MESSAGE", 14 }, + { "VRFY", 4 }, + { "ETRN", 4 }, }; +static const static_str_t static_ESMTP = { "ESMTP", 5 }; +static const static_str_t static_SMTP = { "SMTP", 4 }; + bool query_parse(query_t *query, char *p) { #define PARSE_CHECK(expr, error, ...) \ @@ -81,7 +84,7 @@ bool query_parse(query_t *query, char *p) vtk = policy_tokenize(v, vlen); switch (policy_tokenize(k, klen)) { -#define CASE(up, low) case PTK_##up: query->low = v; v[vlen] = '\0'; break; +#define CASE(up, low) case PTK_##up: query->low.str = v; query->low.len = vlen; v[vlen] = '\0'; break; CASE(HELO_NAME, helo_name); CASE(QUEUE_ID, queue_id); CASE(RECIPIENT_COUNT, recipient_count); @@ -104,20 +107,27 @@ bool query_parse(query_t *query, char *p) #undef CASE case PTK_SENDER: - query->sender = v; + query->sender.str = v; + query->sender.len = vlen; v[vlen] = '\0'; - query->sender_domain = memchr(query->sender, '@', vlen); - if (query->sender_domain != NULL) { - ++query->sender_domain; + query->sender_domain.str = memchr(query->sender.str, '@', vlen); + if (query->sender_domain.str != NULL) { + ++query->sender_domain.str; + query->sender_domain.len = query->sender.len + - (query->sender_domain.str - query->sender.str); } break; case PTK_RECIPIENT: - query->recipient = v; + query->recipient.str = v; + query->recipient.len = vlen; v[vlen] = '\0'; - query->recipient_domain = memchr(query->recipient, '@', vlen); - if (query->recipient_domain != NULL) { - ++query->recipient_domain; + query->recipient_domain.str = memchr(query->recipient.str, '@', vlen); + if (query->recipient_domain.str != NULL) { + ++query->recipient_domain.str; + query->recipient_domain.len = query->recipient.len + - (query->recipient_domain.str - query->recipient.str); + } break; @@ -161,11 +171,11 @@ bool query_parse(query_t *query, char *p) #undef PARSE_CHECK } -const char *query_field_for_id(const query_t *query, postlicyd_token id) +const static_str_t *query_field_for_id(const query_t *query, postlicyd_token id) { switch (id) { #define CASE(Up, Low) \ - case PTK_ ## Up: return query->Low; + case PTK_ ## Up: return &query->Low; CASE(HELO_NAME, helo_name) CASE(QUEUE_ID, queue_id) CASE(SENDER, sender) @@ -191,16 +201,16 @@ const char *query_field_for_id(const query_t *query, postlicyd_token id) CASE(STRESS, stress) #undef CASE case PTK_PROTOCOL_NAME: - return query->esmtp ? "ESMTP" : "SMTP"; + return query->esmtp ? &static_ESMTP : &static_SMTP; case PTK_PROTOCOL_STATE: - return smtp_state_names[query->state]; + return &smtp_state_names[query->state]; default: return NULL; } } -const char *query_field_for_name(const query_t *query, const char *name) +const static_str_t *query_field_for_name(const query_t *query, const char *name) { postlicyd_token id = policy_tokenize(name, strlen(name)); if (id == PTK_UNKNOWN) { @@ -214,7 +224,6 @@ ssize_t query_format(char *dest, size_t len, const char *fmt, const query_t *que { size_t written = 0; size_t pos = 0; - const char *end = fmt + m_strlen(fmt); #define WRITE(Src, Len) \ do { \ @@ -227,9 +236,12 @@ ssize_t query_format(char *dest, size_t len, const char *fmt, const query_t *que pos += __len; \ } while (0) while (*fmt != '\0') { - const char *next_format = strstr(fmt, "${"); + const char *next_format = strchr(fmt, '$'); + while (next_format != NULL && next_format[1] != '{') { + next_format = strchr(next_format + 1, '$'); + } if (next_format == NULL) { - next_format = end; + next_format = fmt + m_strlen(fmt); } WRITE(fmt, next_format - fmt); fmt = next_format; @@ -244,11 +256,12 @@ ssize_t query_format(char *dest, size_t len, const char *fmt, const query_t *que if (tok == PTK_UNKNOWN) { warn("unknown field name \"%.*s\"", (int)(next_format - fmt), fmt); } - const char *field = query == NULL ? NULL : query_field_for_id(query, tok); + const static_str_t *field = query == NULL ? NULL + : query_field_for_id(query, tok); if (field == NULL) { WRITE("(null)", 6); } else { - WRITE(field, m_strlen(field)); + WRITE(field->str, field->len); } fmt = next_format + 1; } diff --git a/postlicyd/query.h b/postlicyd/query.h index 7e9dc7f..2081e59 100644 --- a/postlicyd/query.h +++ b/postlicyd/query.h @@ -56,44 +56,44 @@ enum smtp_state { SMTP_UNKNOWN, }; -extern const char *smtp_state_names[SMTP_count]; +extern const static_str_t smtp_state_names[SMTP_count]; /* \see http://www.postfix.org/SMTPD_POLICY_README.html */ typedef struct query_t { unsigned state : 4; unsigned esmtp : 1; - const char *helo_name; - const char *queue_id; - const char *sender; - const char *recipient; - const char *recipient_count; - const char *client_address; - const char *client_name; - const char *reverse_client_name; - const char *instance; + static_str_t helo_name; + static_str_t queue_id; + static_str_t sender; + static_str_t recipient; + static_str_t recipient_count; + static_str_t client_address; + static_str_t client_name; + static_str_t reverse_client_name; + static_str_t instance; /* useful data extracted from previous ones */ - const char *sender_domain; - const char *recipient_domain; + static_str_t sender_domain; + static_str_t recipient_domain; /* postfix 2.2+ */ - const char *sasl_method; - const char *sasl_username; - const char *sasl_sender; - const char *size; - const char *ccert_subject; - const char *ccert_issuer; - const char *ccert_fingerprint; + static_str_t sasl_method; + static_str_t sasl_username; + static_str_t sasl_sender; + static_str_t size; + static_str_t ccert_subject; + static_str_t ccert_issuer; + static_str_t ccert_fingerprint; /* postfix 2.3+ */ - const char *encryption_protocol; - const char *encryption_cipher; - const char *encryption_keysize; - const char *etrn_domain; + static_str_t encryption_protocol; + static_str_t encryption_cipher; + static_str_t encryption_keysize; + static_str_t etrn_domain; /* postfix 2.5+ */ - const char *stress; + static_str_t stress; const char *eoq; } query_t; @@ -109,12 +109,12 @@ bool query_parse(query_t *query, char *p); /** Return the value of the field with the given name. */ __attribute__((nonnull(1,2))) -const char *query_field_for_name(const query_t *query, const char *name); +const static_str_t *query_field_for_name(const query_t *query, const char *name); /** Returns the value of the field with the given id. */ __attribute__((nonnull)) -const char *query_field_for_id(const query_t *query, postlicyd_token id); +const static_str_t *query_field_for_id(const query_t *query, postlicyd_token id); /** Formats the given string by replacing ${field_name} with the content * of the query. diff --git a/postlicyd/strlist.c b/postlicyd/strlist.c index e8202ff..4208f7c 100644 --- a/postlicyd/strlist.c +++ b/postlicyd/strlist.c @@ -681,9 +681,9 @@ static filter_result_t strlist_filter(const filter_t *filter, const query_t *que } #define LOOKUP(Flag, Field) \ if (config->match_ ## Flag) { \ - const int len = m_strlen(query->Field); \ - strlist_copy(normal, query->Field, len, false); \ - strlist_copy(reverse, query->Field, len, true); \ + const int len = query->Field.len; \ + strlist_copy(normal, query->Field.str, len, false); \ + strlist_copy(reverse, query->Field.str, len, true); \ foreach (strlist_local_t *entry, config->locals) { \ if ((!entry->partial && trie_lookup(*(entry->db), \ entry->reverse ? reverse : normal)) \ @@ -699,8 +699,8 @@ static filter_result_t strlist_filter(const filter_t *filter, const query_t *que } #define DNS(Flag, Field) \ if (config->match_ ## Flag) { \ - const int len = m_strlen(query->Field); \ - strlist_copy(normal, query->Field, len, false); \ + const int len = query->Field.len; \ + strlist_copy(normal, query->Field.str, len, false); \ for (uint32_t i = 0 ; len > 0 && i < config->host_offsets.len ; ++i) { \ const char *rbl = array_ptr(config->hosts, \ array_elt(config->host_offsets, i)); \ diff --git a/postlicyd/tst-qf.c b/postlicyd/tst-qf.c new file mode 100644 index 0000000..1d8343b --- /dev/null +++ b/postlicyd/tst-qf.c @@ -0,0 +1,105 @@ +/******************************************************************************/ +/* pfixtools: a collection of postfix related tools */ +/* ~~~~~~~~~ */ +/* ________________________________________________________________________ */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ +/* */ +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in the */ +/* documentation and/or other materials provided with the distribution. */ +/* 3. The names of its contributors may not be used to endorse or promote */ +/* products derived from this software without specific prior written */ +/* permission. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND */ +/* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE */ +/* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */ +/* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS */ +/* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR */ +/* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF */ +/* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS */ +/* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN */ +/* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) */ +/* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF */ +/* THE POSSIBILITY OF SUCH DAMAGE. */ +/******************************************************************************/ + +/* + * Copyright © 2008 Florent Bruneau + */ + +#include "common.h" +#include "file.h" +#include "query.h" + +static bool read_query(const char *base, const char *file, query_t *query, + char *buff) +{ + char path[FILENAME_MAX]; + snprintf(path, FILENAME_MAX, "%s%s", base, file); + { + file_map_t map; + if (!file_map_open(&map, path, false)) { + UNIXERR("open"); + return false; + } + if (map.end - map.map >= BUFSIZ) { + err("File too large for a testcase: %s", path); + file_map_close(&map); + return false; + } + memcpy(buff, map.map, map.end - map.map); + buff[map.end - map.map] = '\0'; + file_map_close(&map); + } + + char *eoq = strstr(buff, "\n\n"); + if (eoq == NULL) { + return false; + } + if (!query_parse(query, buff)) { + err("Cannot parse query from file %s", path); + return false; + } + return true; +} + +int main(int argc, char *argv[]) +{ + char basepath[FILENAME_MAX]; + char buff[BUFSIZ]; + char *p; + + p = strrchr(argv[0], '/'); + if (p == NULL) { + p = argv[0]; + } else { + ++p; + } + snprintf(basepath, FILENAME_MAX, "%.*sdata/", p - argv[0], argv[0]); + + query_t q; + if (!read_query(basepath, "testcase_1", &q, buff)) { + return EXIT_FAILURE; + } + + static const char *format = "${sender} ${recipient} and ${client_name}[${client_address}] at ${protocol_state}"; + + time_t now = time(0); + + char str[BUFSIZ]; + static const int iterations = 10000000; + for (int i = 0 ; i < iterations ; ++i) { + query_format(str, BUFSIZ, format, &q); + } + + time_t ellapsed = time(0) - now; + printf("Done %d iterations in %us (%d format per second)\n", iterations, + (uint32_t)ellapsed, (int)(iterations / ellapsed)); + return 0; +}