fts-search.c revision c8b5a21a139992e66b4ad02adb69eaf929b3d024
76b43e4417bab52e913da39b5f5bc2a130d3f149Timo Sirainen/* Copyright (c) 2006-2008 Dovecot authors, see the included COPYING file */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "lib.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "array.h"
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen#include "str.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "seq-range-array.h"
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen#include "charset-utf8.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "mail-search.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "mail-storage-private.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "fts-api-private.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen#include "fts-storage.h"
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenstatic void
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenuid_range_to_seqs(struct mailbox *box, const ARRAY_TYPE(seq_range) *uid_range,
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen ARRAY_TYPE(seq_range) *seq_range)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen{
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen const struct seq_range *range;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen unsigned int i, count;
c8b5a21a139992e66b4ad02adb69eaf929b3d024Timo Sirainen uint32_t seq1, seq2;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen range = array_get(uid_range, &count);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen i_array_init(seq_range, count);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen for (i = 0; i < count; i++) {
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen mailbox_get_uids(box, range[i].seq1, range[i].seq2,
c8b5a21a139992e66b4ad02adb69eaf929b3d024Timo Sirainen &seq1, &seq2);
c8b5a21a139992e66b4ad02adb69eaf929b3d024Timo Sirainen if (seq1 != 0)
c8b5a21a139992e66b4ad02adb69eaf929b3d024Timo Sirainen seq_range_array_add_range(seq_range, seq1, seq2);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen }
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen}
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenstatic void fts_uid_results_to_seq(struct fts_search_context *fctx)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen{
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen ARRAY_TYPE(seq_range) uid_range;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen uid_range = fctx->definite_seqs;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen uid_range_to_seqs(fctx->t->box, &uid_range, &fctx->definite_seqs);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen array_free(&uid_range);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen uid_range = fctx->maybe_seqs;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen uid_range_to_seqs(fctx->t->box, &uid_range, &fctx->maybe_seqs);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen array_free(&uid_range);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen}
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenstatic int fts_search_lookup_arg(struct fts_search_context *fctx,
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen struct mail_search_arg *arg, bool filter)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen{
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen struct fts_backend *backend;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen enum fts_lookup_flags flags = 0;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen const char *key;
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen string_t *key_utf8;
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen enum charset_result result;
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen int ret;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen switch (arg->type) {
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen case SEARCH_HEADER:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen /* we can filter out messages that don't have the header,
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen but we can't trust definite results list. */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen flags = FTS_LOOKUP_FLAG_HEADER;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen backend = fctx->fbox->backend_substr;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen key = arg->value.str;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (*key == '\0') {
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen /* we're only checking the existence
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen of the header. */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen key = arg->hdr_field_name;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen }
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen break;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen case SEARCH_TEXT:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen case SEARCH_TEXT_FAST:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen flags = FTS_LOOKUP_FLAG_HEADER;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen case SEARCH_BODY:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen case SEARCH_BODY_FAST:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen flags |= FTS_LOOKUP_FLAG_BODY;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen key = arg->value.str;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen backend = fctx->fbox->backend_fast != NULL &&
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen (arg->type == SEARCH_TEXT_FAST ||
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen arg->type == SEARCH_BODY_FAST) ?
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen fctx->fbox->backend_fast : fctx->fbox->backend_substr;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen break;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen default:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen /* can't filter this */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen i_assert(filter);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return 0;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen }
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (arg->not)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen flags |= FTS_LOOKUP_FLAG_INVERT;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen /* convert key to titlecase */
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen key_utf8 = t_str_new(128);
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen if (charset_to_utf8_str(fctx->charset, CHARSET_FLAG_DECOMP_TITLECASE,
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen key, key_utf8, &result) < 0) {
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen /* unknown charset, can't handle this */
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen ret = 0;
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen } else if (result != CHARSET_RET_OK) {
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen /* let the core code handle this error */
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen ret = 0;
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen } else if (!backend->locked && fts_backend_lock(backend) <= 0)
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen ret = -1;
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen else if (!filter) {
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen ret = fts_backend_lookup(backend, str_c(key_utf8), flags,
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen &fctx->definite_seqs,
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen &fctx->maybe_seqs);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen } else {
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen ret = fts_backend_filter(backend, str_c(key_utf8), flags,
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen &fctx->definite_seqs,
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen &fctx->maybe_seqs);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen }
f2f86ec77d1e4986e95990976447c2d1520a8357Timo Sirainen return ret;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen}
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenvoid fts_search_lookup(struct fts_search_context *fctx)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen{
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen struct mail_search_arg *arg;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen int ret;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (fctx->best_arg == NULL)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen i_array_init(&fctx->definite_seqs, 64);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen i_array_init(&fctx->maybe_seqs, 64);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen /* start filtering with the best arg */
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen T_BEGIN {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen ret = fts_search_lookup_arg(fctx, fctx->best_arg, FALSE);
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen } T_END;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen /* filter the rest */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen for (arg = fctx->args; arg != NULL && ret == 0; arg = arg->next) {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (arg != fctx->best_arg) {
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen T_BEGIN {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen ret = fts_search_lookup_arg(fctx, arg, TRUE);
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen } T_END;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen }
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen }
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (fctx->fbox->backend_fast != NULL &&
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen fctx->fbox->backend_fast->locked)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen fts_backend_unlock(fctx->fbox->backend_fast);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (fctx->fbox->backend_substr != NULL &&
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen fctx->fbox->backend_substr->locked)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen fts_backend_unlock(fctx->fbox->backend_substr);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (ret == 0) {
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen fctx->seqs_set = TRUE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen fts_uid_results_to_seq(fctx);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen }
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen}
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenstatic bool arg_is_better(const struct mail_search_arg *new_arg,
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen const struct mail_search_arg *old_arg)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen{
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (old_arg == NULL)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return TRUE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (new_arg == NULL)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return FALSE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen /* avoid NOTs */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (old_arg->not && !new_arg->not)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return TRUE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (!old_arg->not && new_arg->not)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return FALSE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen /* prefer not to use headers. they have a larger possibility of
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen having lots of identical strings */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (old_arg->type == SEARCH_HEADER)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return TRUE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen else if (new_arg->type == SEARCH_HEADER)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return FALSE;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen return strlen(new_arg->value.str) > strlen(old_arg->value.str);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen}
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenstatic void
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenfts_search_args_find_best(struct mail_search_arg *args,
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen struct mail_search_arg **best_fast_arg,
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen struct mail_search_arg **best_substr_arg)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen{
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen for (; args != NULL; args = args->next) {
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen switch (args->type) {
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen case SEARCH_BODY_FAST:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen case SEARCH_TEXT_FAST:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (arg_is_better(args, *best_fast_arg))
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen *best_fast_arg = args;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen break;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen case SEARCH_BODY:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen case SEARCH_TEXT:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen case SEARCH_HEADER:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (arg_is_better(args, *best_substr_arg))
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen *best_substr_arg = args;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen break;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen default:
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen break;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen }
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen }
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen}
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainenvoid fts_search_analyze(struct fts_search_context *fctx)
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen{
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen struct mail_search_arg *best_fast_arg = NULL, *best_substr_arg = NULL;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen fts_search_args_find_best(fctx->args, &best_fast_arg, &best_substr_arg);
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen if (best_fast_arg != NULL && fctx->fbox->backend_fast != NULL) {
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen /* use fast backend whenever possible */
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen fctx->best_arg = best_fast_arg;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen fctx->build_backend = fctx->fbox->backend_fast;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen } else if (best_fast_arg != NULL || best_substr_arg != NULL) {
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen fctx->build_backend = fctx->fbox->backend_substr;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen fctx->best_arg = arg_is_better(best_substr_arg, best_fast_arg) ?
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen best_substr_arg : best_fast_arg;
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen }
67f1723e1685b4bf73c1cca0a1e08a0a87ffd410Timo Sirainen}