[dovecot-cvs] dovecot/src/imap cmd-sort.c, 1.20, 1.21 imap-sort.c,
1.28, 1.29
tss-movial at dovecot.org
tss-movial at dovecot.org
Thu Jun 8 15:49:43 EEST 2006
Update of /var/lib/cvs/dovecot/src/imap
In directory talvi:/tmp/cvs-serv22007/imap
Modified Files:
cmd-sort.c imap-sort.c
Log Message:
Changed mail-storage API to do the mail sorting internally. Optimized it
internally to keep a 32bit sort_id field in index for each used primary sort
condition. Practically this should mean less disk I/O, memory and CPU usage
when SORT command is used.
Index: cmd-sort.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/cmd-sort.c,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -d -r1.20 -r1.21
--- cmd-sort.c 13 Jan 2006 20:25:59 -0000 1.20
+++ cmd-sort.c 8 Jun 2006 12:49:40 -0000 1.21
@@ -20,29 +20,33 @@
{ MAIL_SORT_SUBJECT, "subject" },
{ MAIL_SORT_TO, "to" },
- { MAIL_SORT_REVERSE, "reverse" },
{ MAIL_SORT_END, NULL }
};
-static enum mail_sort_type *
-get_sort_program(struct client_command_context *cmd, struct imap_arg *args)
+static int
+get_sort_program(struct client_command_context *cmd, struct imap_arg *args,
+ enum mail_sort_type program[MAX_SORT_PROGRAM_SIZE])
{
- enum mail_sort_type type;
- buffer_t *buf;
- int i;
+ enum mail_sort_type mask = 0;
+ unsigned int i, pos;
+ bool reverse;
if (args->type == IMAP_ARG_EOL) {
/* empyty list */
client_send_command_error(cmd, "Empty sort program.");
- return NULL;
+ return -1;
}
- buf = buffer_create_dynamic(pool_datastack_create(),
- 32 * sizeof(enum mail_sort_type));
-
- while (args->type == IMAP_ARG_ATOM || args->type == IMAP_ARG_STRING) {
+ pos = 0; reverse = FALSE;
+ for (; args->type == IMAP_ARG_ATOM || args->type == IMAP_ARG_STRING;
+ args++) {
const char *arg = IMAP_ARG_STR(args);
+ if (strcasecmp(arg, "reverse") == 0) {
+ reverse = !reverse;
+ continue;
+ }
+
for (i = 0; sort_names[i].type != MAIL_SORT_END; i++) {
if (strcasecmp(arg, sort_names[i].name) == 0)
break;
@@ -51,31 +55,37 @@
if (sort_names[i].type == MAIL_SORT_END) {
client_send_command_error(cmd, t_strconcat(
"Unknown sort argument: ", arg, NULL));
- return NULL;
+ return -1;
}
- buffer_append(buf, &sort_names[i].type,
- sizeof(enum mail_sort_type));
- args++;
+ if ((mask & sort_names[i].type) != 0)
+ continue;
+ mask |= sort_names[i].type;
+
+ /* @UNSAFE: mask check should prevent us from ever
+ overflowing */
+ i_assert(pos < MAX_SORT_PROGRAM_SIZE-1);
+ program[pos++] = sort_names[i].type |
+ (reverse ? MAIL_SORT_FLAG_REVERSE : 0);
+ reverse = FALSE;
}
- type = MAIL_SORT_END;
- buffer_append(buf, &type, sizeof(type));
+ program[pos++] = MAIL_SORT_END;
if (args->type != IMAP_ARG_EOL) {
client_send_command_error(cmd,
"Invalid sort list argument.");
- return NULL;
+ return -1;
}
- return buffer_free_without_data(buf);
+ return 0;
}
bool cmd_sort(struct client_command_context *cmd)
{
struct client *client = cmd->client;
struct mail_search_arg *sargs;
- enum mail_sort_type *sorting;
+ enum mail_sort_type sorting[MAX_SORT_PROGRAM_SIZE];
struct imap_arg *args;
int args_count;
pool_t pool;
@@ -100,8 +110,7 @@
return TRUE;
}
- sorting = get_sort_program(cmd, IMAP_ARG_LIST(args)->args);
- if (sorting == NULL)
+ if (get_sort_program(cmd, IMAP_ARG_LIST(args)->args, sorting) < 0)
return TRUE;
args++;
Index: imap-sort.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/imap-sort.c,v
retrieving revision 1.28
retrieving revision 1.29
diff -u -d -r1.28 -r1.29
--- imap-sort.c 14 Feb 2006 18:59:10 -0000 1.28
+++ imap-sort.c 8 Jun 2006 12:49:40 -0000 1.29
@@ -6,7 +6,7 @@
reasonably fast. */
#include "common.h"
-#include "buffer.h"
+#include "array.h"
#include "hash.h"
#include "ostream.h"
#include "str.h"
@@ -28,701 +28,87 @@
((type) == MAIL_SORT_ARRIVAL || (type) == MAIL_SORT_DATE)
struct sort_context {
- struct mail_search_context *search_ctx;
- struct mailbox_transaction_context *t;
- struct mail *other_mail;
-
enum mail_sort_type sort_program[MAX_SORT_PROGRAM_SIZE];
- enum mail_sort_type common_mask, cache_mask;
struct mailbox *box;
struct ostream *output;
string_t *str;
- buffer_t *sort_buffer;
- size_t sort_element_size;
-
- pool_t str_pool;
- struct hash_table *string_table;
-
- time_t last_arrival, last_date;
- uoff_t last_size;
- char *last_cc, *last_from, *last_subject, *last_to;
-
- bool written, id_is_uid;
+ bool written;
};
-static void mail_sort_input(struct sort_context *ctx, struct mail *mail);
-static void mail_sort_flush(struct sort_context *ctx);
-
-static void
-mail_sort_normalize(const enum mail_sort_type *input, buffer_t *output)
-{
- enum mail_sort_type type, mask = 0;
- bool reverse;
-
- reverse = FALSE;
- for (; *input != MAIL_SORT_END; input++) {
- if (*input == MAIL_SORT_REVERSE)
- reverse = !reverse;
- else {
- if ((mask & *input) == 0) {
- if (reverse) {
- type = MAIL_SORT_REVERSE;
- buffer_append(output,
- &type, sizeof(type));
- }
-
- buffer_append(output, input, sizeof(*input));
- mask |= *input;
- }
-
- reverse = FALSE;
- }
- }
-
- type = MAIL_SORT_END;
- buffer_append(output, &type, sizeof(type));
-}
-
-static enum mail_sort_type
-mail_sort_get_common_mask(const enum mail_sort_type *sort1,
- const enum mail_sort_type *sort2,
- unsigned int *count)
-{
- enum mail_sort_type mask = 0;
-
- *count = 0;
- while (*sort1 == *sort2 && *sort1 != MAIL_SORT_END) {
- if (*sort1 != MAIL_SORT_REVERSE)
- mask |= *sort1;
- sort1++; sort2++; (*count)++;
- }
-
- return mask;
-}
-
-static enum mail_fetch_field
-init_sort_elements(struct sort_context *ctx,
- const char *wanted_headers[MAX_WANTED_HEADERS])
-{
- unsigned int i;
- enum mail_fetch_field fields;
-
- /* figure out what data we'd like to cache */
- ctx->sort_element_size = sizeof(unsigned int);
- ctx->cache_mask = 0;
-
- for (i = 0; ctx->sort_program[i] != MAIL_SORT_END; i++) {
- enum mail_sort_type type = ctx->sort_program[i];
-
- if (IS_SORT_STRING(type)) {
- ctx->sort_element_size += sizeof(const char *);
-
- /* cache the second rule as well, if available */
- if (ctx->cache_mask != 0) {
- ctx->cache_mask |= type;
- break;
- }
- ctx->cache_mask |= type;
- } else if (IS_SORT_TIME(type)) {
- ctx->sort_element_size += sizeof(time_t);
- ctx->cache_mask |= type;
- break;
- } else if (type == MAIL_SORT_SIZE) {
- ctx->sort_element_size += sizeof(uoff_t);
- ctx->cache_mask |= type;
- break;
- }
- }
-
- fields = 0;
- if (ctx->cache_mask & MAIL_SORT_ARRIVAL)
- fields |= MAIL_FETCH_RECEIVED_DATE;
- if (ctx->cache_mask & MAIL_SORT_DATE)
- fields |= MAIL_FETCH_DATE;
- if (ctx->cache_mask & MAIL_SORT_SIZE)
- fields |= MAIL_FETCH_VIRTUAL_SIZE;
-
- /* @UNSAFE */
- i_assert(MAX_WANTED_HEADERS > 4);
- i = 0;
- if (ctx->cache_mask & MAIL_SORT_CC)
- wanted_headers[i++] = "cc";
- if (ctx->cache_mask & MAIL_SORT_FROM)
- wanted_headers[i++] = "from";
- if (ctx->cache_mask & MAIL_SORT_TO)
- wanted_headers[i++] = "to";
- if (ctx->cache_mask & MAIL_SORT_SUBJECT)
- wanted_headers[i++] = "subject";
- wanted_headers[i] = NULL;
-
- if ((ctx->cache_mask & MAIL_SORT_CC) ||
- (ctx->cache_mask & MAIL_SORT_FROM) ||
- (ctx->cache_mask & MAIL_SORT_TO) ||
- (ctx->cache_mask & MAIL_SORT_SUBJECT)) {
- ctx->str_pool = pool_alloconly_create("sort str", 8192);
- ctx->string_table = hash_create(default_pool, ctx->str_pool,
- 0, str_hash,
- (hash_cmp_callback_t *)strcmp);
- }
-
- return fields;
-}
-
-static void mail_sort_deinit(struct sort_context *ctx)
-{
- mail_sort_flush(ctx);
-
- if (ctx->string_table != NULL)
- hash_destroy(ctx->string_table);
- if (ctx->str_pool != NULL)
- pool_unref(ctx->str_pool);
- buffer_free(ctx->sort_buffer);
-
- i_free(ctx->last_cc);
- i_free(ctx->last_from);
- i_free(ctx->last_subject);
- i_free(ctx->last_to);
-}
-
int imap_sort(struct client_command_context *cmd, const char *charset,
struct mail_search_arg *args,
const enum mail_sort_type *sort_program)
{
struct client *client = cmd->client;
- enum mail_sort_type norm_prog[MAX_SORT_PROGRAM_SIZE];
- enum mail_fetch_field wanted_fields;
- const char *wanted_headers[MAX_WANTED_HEADERS];
+ const char *wanted_headers[2];
+ enum mail_fetch_field wanted_fields;
+ struct mail_search_context *search_ctx;
+ struct mailbox_transaction_context *t;
struct mailbox_header_lookup_ctx *headers_ctx;
- struct sort_context *ctx;
struct mail *mail;
- buffer_t *buf;
- unsigned int count;
+ string_t *str;
+ bool written = FALSE;
int ret;
- ctx = t_new(struct sort_context, 1);
-
- /* normalize sorting program. note that although we're using a hard
- buffer size here, it shouldn't be possible to overflow it since
- the normalized sort program can't exceed MAX_SORT_PROGRAM_SIZE. */
- buf = buffer_create_data(pool_datastack_create(),
- norm_prog, sizeof(norm_prog));
- mail_sort_normalize(sort_program, buf);
- memcpy(ctx->sort_program, norm_prog, sizeof(ctx->sort_program));
-
- /* remove the common part from sort program, we already know input is
- sorted that much so we don't have to worry about it. */
- if (mailbox_search_get_sorting(client->mailbox, norm_prog) < 0)
- return -1;
- ctx->common_mask = mail_sort_get_common_mask(ctx->sort_program,
- norm_prog, &count);
- if (count > 0) {
- memmove(ctx->sort_program, ctx->sort_program + count,
- sizeof(ctx->sort_program) -
- sizeof(ctx->sort_program[0]) * count);
+ wanted_fields = 0;
+ wanted_headers[0] = wanted_headers[1] = NULL;
+ switch (*sort_program & MAIL_SORT_MASK) {
+ case MAIL_SORT_ARRIVAL:
+ wanted_fields = MAIL_FETCH_RECEIVED_DATE;
+ break;
+ case MAIL_SORT_CC:
+ wanted_headers[0] = "Cc";
+ break;
+ case MAIL_SORT_DATE:
+ wanted_fields = MAIL_FETCH_DATE;
+ break;
+ case MAIL_SORT_FROM:
+ wanted_headers[0] = "From";
+ break;
+ case MAIL_SORT_SIZE:
+ wanted_fields = MAIL_FETCH_VIRTUAL_SIZE;
+ break;
+ case MAIL_SORT_SUBJECT:
+ wanted_headers[0] = "Subject";
+ break;
+ case MAIL_SORT_TO:
+ wanted_headers[0] = "To";
+ break;
}
- memset(wanted_headers, 0, sizeof(wanted_headers));
- wanted_fields = init_sort_elements(ctx, wanted_headers);
- headers_ctx = *wanted_headers == NULL ? NULL :
+ headers_ctx = wanted_headers[0] == NULL ? NULL :
mailbox_header_lookup_init(client->mailbox, wanted_headers);
- /* initialize searching */
- ctx->t = mailbox_transaction_begin(client->mailbox, 0);
- ctx->search_ctx = mailbox_search_init(ctx->t, charset, args, norm_prog);
- ctx->other_mail = mail_alloc(ctx->t, wanted_fields, headers_ctx);
-
- ctx->box = client->mailbox;
- ctx->output = client->output;
- ctx->sort_buffer = buffer_create_dynamic(system_pool,
- 128 * ctx->sort_element_size);
-
- ctx->str = t_str_new(STRBUF_SIZE);
- str_append(ctx->str, "* SORT");
-
- ctx->id_is_uid = cmd->uid;
-
- mail = mail_alloc(ctx->t, wanted_fields, headers_ctx);
- while (mailbox_search_next(ctx->search_ctx, mail) > 0)
- mail_sort_input(ctx, mail);
+ t = mailbox_transaction_begin(client->mailbox, 0);
+ search_ctx = mailbox_search_init(t, charset, args, sort_program);
- mail_sort_flush(ctx);
- ret = mailbox_search_deinit(&ctx->search_ctx);
+ str = t_str_new(STRBUF_SIZE);
+ str_append(str, "* SORT");
+ mail = mail_alloc(t, wanted_fields, headers_ctx);
+ while (mailbox_search_next(search_ctx, mail) > 0) {
+ if (str_len(str) >= STRBUF_SIZE-MAX_INT_STRLEN) {
+ o_stream_send(client->output, str_data(str),
+ str_len(str));
+ str_truncate(str, 0);
+ written = TRUE;
+ }
+ str_printfa(str, " %u", cmd->uid ? mail->uid : mail->seq);
+ }
+ ret = mailbox_search_deinit(&search_ctx);
mail_free(&mail);
- mail_free(&ctx->other_mail);
- if (mailbox_transaction_commit(&ctx->t, 0) < 0)
+ if (mailbox_transaction_commit(&t, 0) < 0)
ret = -1;
- if (ctx->written || ret == 0) {
- str_append(ctx->str, "\r\n");
- o_stream_send(client->output, str_data(ctx->str),
- str_len(ctx->str));
+ if (written || ret == 0) {
+ str_append(str, "\r\n");
+ o_stream_send(client->output, str_data(str), str_len(str));
}
if (headers_ctx != NULL)
mailbox_header_lookup_deinit(&headers_ctx);
- mail_sort_deinit(ctx);
return ret;
}
-
-static const char *string_table_get(struct sort_context *ctx, const char *str)
-{
- char *value;
-
- if (str == NULL)
- return NULL;
- if (*str == '\0')
- return "";
-
- value = hash_lookup(ctx->string_table, str);
- if (value == NULL) {
- value = p_strdup(ctx->str_pool, str);
- hash_insert(ctx->string_table, value, value);
- }
-
- return value;
-}
-
-static const char *get_first_mailbox(struct mail *mail, const char *field)
-{
- struct message_address *addr;
- const char *str;
-
- str = mail_get_first_header(mail, field);
- if (str == NULL)
- return NULL;
-
- addr = message_address_parse(pool_datastack_create(),
- (const unsigned char *)str,
- strlen(str), 1);
- return addr != NULL ? addr->mailbox : NULL;
-}
-
-static void mail_sort_check_flush(struct sort_context *ctx, struct mail *mail)
-{
- const char *str;
- time_t t;
- uoff_t size;
- bool changed = FALSE;
-
- if (ctx->common_mask & MAIL_SORT_ARRIVAL) {
- t = mail_get_received_date(mail);
- if (t != ctx->last_arrival) {
- ctx->last_arrival = t;
- changed = TRUE;
- }
- }
-
- if (ctx->common_mask & MAIL_SORT_CC) {
- str = get_first_mailbox(mail, "cc");
- if (str != NULL)
- str = t_str_ucase(str);
-
- if (null_strcmp(str, ctx->last_cc) != 0) {
- i_free(ctx->last_cc);
- ctx->last_cc = i_strdup(str);
- changed = TRUE;
- }
- }
-
- if (ctx->common_mask & MAIL_SORT_DATE) {
- t = mail_get_date(mail, NULL);
- if (t != ctx->last_date) {
- ctx->last_date = t;
- changed = TRUE;
- }
- }
-
- if (ctx->common_mask & MAIL_SORT_FROM) {
- str = get_first_mailbox(mail, "from");
- if (str != NULL)
- str = t_str_ucase(str);
-
- if (null_strcmp(str, ctx->last_from) != 0) {
- i_free(ctx->last_from);
- ctx->last_from = i_strdup(str);
- changed = TRUE;
- }
- }
-
- if (ctx->common_mask & MAIL_SORT_SIZE) {
- size = mail_get_virtual_size(mail);
- if (size != ctx->last_size) {
- ctx->last_size = size;
- changed = TRUE;
- }
- }
-
- if (ctx->common_mask & MAIL_SORT_SUBJECT) {
- str = mail_get_first_header(mail, "subject");
- if (str != NULL) {
- str = imap_get_base_subject_cased(
- pool_datastack_create(), str, NULL);
- }
-
- if (null_strcmp(str, ctx->last_subject) != 0) {
- i_free(ctx->last_subject);
- ctx->last_subject = i_strdup(str);
- changed = TRUE;
- }
- }
-
- if (ctx->common_mask & MAIL_SORT_TO) {
- str = get_first_mailbox(mail, "to");
- if (str != NULL)
- str = t_str_ucase(str);
-
- if (null_strcmp(str, ctx->last_to) != 0) {
- i_free(ctx->last_to);
- ctx->last_to = i_strdup(str);
- changed = TRUE;
- }
- }
-
- if (changed)
- mail_sort_flush(ctx);
-}
-
-static void mail_sort_input(struct sort_context *ctx, struct mail *mail)
-{
- /* @UNSAFE */
- unsigned char *buf;
- unsigned int id;
- time_t t;
- uoff_t size;
- const char *str;
- size_t pos;
-
- t_push();
- if (ctx->common_mask != 0)
- mail_sort_check_flush(ctx, mail);
-
- buf = buffer_append_space_unsafe(ctx->sort_buffer,
- ctx->sort_element_size);
- id = ctx->id_is_uid ? mail->uid : mail->seq;
- memcpy(buf, &id, sizeof(id)); pos = sizeof(id);
-
- if (ctx->cache_mask & MAIL_SORT_ARRIVAL) {
- if (ctx->common_mask & MAIL_SORT_ARRIVAL)
- t = ctx->last_arrival;
- else
- t = mail_get_received_date(mail);
- memcpy(buf + pos, &t, sizeof(t)); pos += sizeof(t);
- }
-
- if (ctx->cache_mask & MAIL_SORT_DATE) {
- if (ctx->common_mask & MAIL_SORT_DATE)
- t = ctx->last_date;
- else
- t = mail_get_date(mail, NULL);
- memcpy(buf + pos, &t, sizeof(t)); pos += sizeof(t);
- }
-
- if (ctx->cache_mask & MAIL_SORT_SIZE) {
- if (ctx->common_mask & MAIL_SORT_SIZE)
- size = ctx->last_size;
- else
- size = mail_get_virtual_size(mail);
-
- memcpy(buf + pos, &size, sizeof(size)); pos += sizeof(size);
- }
-
- if (ctx->cache_mask & MAIL_SORT_CC) {
- if (ctx->common_mask & MAIL_SORT_CC)
- str = ctx->last_cc;
- else {
- str = get_first_mailbox(mail, "cc");
- if (str != NULL)
- str = t_str_ucase(str);
- }
- str = string_table_get(ctx, str);
-
- memcpy(buf + pos, &str, sizeof(const char *));
- pos += sizeof(const char *);
- }
-
- if (ctx->cache_mask & MAIL_SORT_FROM) {
- if (ctx->common_mask & MAIL_SORT_FROM)
- str = ctx->last_from;
- else {
- str = get_first_mailbox(mail, "from");
- if (str != NULL)
- str = t_str_ucase(str);
- }
- str = string_table_get(ctx, str);
-
- memcpy(buf + pos, &str, sizeof(const char *));
- pos += sizeof(const char *);
- }
-
- if (ctx->cache_mask & MAIL_SORT_TO) {
- if (ctx->common_mask & MAIL_SORT_TO)
- str = ctx->last_to;
- else {
- str = get_first_mailbox(mail, "to");
- if (str != NULL)
- str = t_str_ucase(str);
- }
- str = string_table_get(ctx, str);
-
- memcpy(buf + pos, &str, sizeof(const char *));
- pos += sizeof(const char *);
- }
-
- if (ctx->cache_mask & MAIL_SORT_SUBJECT) {
- if (ctx->common_mask & MAIL_SORT_SUBJECT)
- str = ctx->last_subject;
- else {
- str = mail_get_first_header(mail, "subject");
-
- if (str != NULL) {
- str = imap_get_base_subject_cased(
- pool_datastack_create(), str, NULL);
- }
- }
- str = string_table_get(ctx, str);
-
- memcpy(buf + pos, &str, sizeof(const char *));
- pos += sizeof(const char *);
- }
-
- i_assert(pos == ctx->sort_element_size);
-
- t_pop();
-}
-
-static struct sort_context *qsort_context;
-
-static struct mail *get_mail(struct sort_context *ctx, const unsigned char *buf)
-{
- unsigned int id = *((const unsigned int *)buf);
- uint32_t seq;
-
- if (!ctx->id_is_uid)
- seq = id;
- else {
- if (mailbox_get_uids(ctx->box, id, id, &seq, &seq) < 0)
- return NULL;
- if (seq == 0)
- return NULL;
- }
-
- if (mail_set_seq(ctx->other_mail, seq) < 0)
- return NULL;
- return ctx->other_mail;
-
-}
-
-static time_t get_time(enum mail_sort_type type, const unsigned char *buf,
- struct sort_context *ctx)
-{
- time_t t;
-
- if ((ctx->cache_mask & type) == 0) {
- struct mail *mail = get_mail(ctx, buf);
-
- if (mail == NULL)
- return 0;
-
- switch (type) {
- case MAIL_SORT_ARRIVAL:
- return mail_get_received_date(mail);
- case MAIL_SORT_DATE:
- t = mail_get_date(mail, NULL);
- if (t == (time_t)-1)
- t = 0;
- return t;
- default:
- i_unreached();
- return 0;
- }
- }
-
- /* use memcpy() to avoid any alignment problems */
- memcpy(&t, buf + sizeof(unsigned int), sizeof(t));
- return t;
-}
-
-static uoff_t get_uofft(enum mail_sort_type type, const unsigned char *buf,
- struct sort_context *ctx)
-{
- uoff_t size;
-
- if ((ctx->cache_mask & type) == 0) {
- struct mail *mail = get_mail(ctx, buf);
-
- if (mail == NULL)
- return 0;
-
- i_assert(type == MAIL_SORT_SIZE);
-
- return mail_get_virtual_size(mail);
- }
-
- /* use memcpy() to avoid any alignment problems */
- memcpy(&size, buf + sizeof(unsigned int), sizeof(size));
- return size;
-}
-
-static const char *get_str(enum mail_sort_type type, const unsigned char *buf,
- struct sort_context *ctx)
-{
- const char *str;
- enum mail_sort_type type2;
- pool_t pool;
- int pos;
-
- if ((ctx->cache_mask & type) == 0) {
- struct mail *mail = get_mail(ctx, buf);
-
- if (mail == NULL)
- return NULL;
-
- switch (type) {
- case MAIL_SORT_SUBJECT:
- str = mail_get_first_header(mail, "subject");
- if (str == NULL)
- return NULL;
-
- pool = pool_datastack_create();
- return imap_get_base_subject_cased(pool, str, NULL);
- case MAIL_SORT_CC:
- str = get_first_mailbox(mail, "cc");
- break;
- case MAIL_SORT_FROM:
- str = get_first_mailbox(mail, "from");
- break;
- case MAIL_SORT_TO:
- str = get_first_mailbox(mail, "to");
- break;
- default:
- i_unreached();
- }
-
- if (str != NULL)
- str = t_str_ucase(str);
- return str;
- }
-
- /* figure out where it is. pretty ugly. */
- type2 = (ctx->cache_mask & ~type);
-
- if (type2 == 0)
- pos = 0;
- else if (IS_SORT_TIME(type2))
- pos = sizeof(time_t);
- else if (type2 == MAIL_SORT_SIZE)
- pos = sizeof(uoff_t);
- else {
- if (type == MAIL_SORT_SUBJECT)
- pos = sizeof(const char *);
- else if (type2 != MAIL_SORT_SUBJECT && type > type2)
- pos = sizeof(const char *);
- else
- pos = 0;
- }
-
- /* use memcpy() to avoid any alignment problems */
- memcpy(&str, buf + pos + sizeof(unsigned int), sizeof(const char *));
- return str;
-}
-
-static int mail_sort_qsort_func(const void *p1, const void *p2)
-{
- enum mail_sort_type *sorting;
- int ret;
- bool reverse = FALSE;
-
- sorting = qsort_context->sort_program;
-
- t_push();
-
- ret = 0;
- for (; *sorting != MAIL_SORT_END && ret == 0; sorting++) {
- if (*sorting == MAIL_SORT_REVERSE) {
- reverse = !reverse;
- continue;
- }
-
- switch (*sorting) {
- case MAIL_SORT_ARRIVAL:
- case MAIL_SORT_DATE: {
- time_t r1, r2;
-
- r1 = get_time(*sorting, p1, qsort_context);
- r2 = get_time(*sorting, p2, qsort_context);
- ret = r1 < r2 ? -1 : r1 > r2 ? 1 : 0;
- break;
- }
- case MAIL_SORT_SIZE: {
- uoff_t r1, r2;
-
- r1 = get_uofft(*sorting, p1, qsort_context);
- r2 = get_uofft(*sorting, p2, qsort_context);
- ret = r1 < r2 ? -1 : r1 > r2 ? 1 : 0;
- break;
- }
- case MAIL_SORT_CC:
- case MAIL_SORT_FROM:
- case MAIL_SORT_TO:
- case MAIL_SORT_SUBJECT:
- ret = null_strcmp(get_str(*sorting, p1, qsort_context),
- get_str(*sorting, p2, qsort_context));
- break;
- default:
- i_unreached();
- }
-
- if (reverse) {
- if (ret > 0)
- ret = -1;
- else if (ret < 0)
- ret = 1;
- }
-
- reverse = FALSE;
- }
-
- t_pop();
-
- return ret != 0 ? ret :
- (*((const unsigned int *)p1) <
- *((const unsigned int *)p2) ? -1 : 1);
-}
-
-static void mail_sort_flush(struct sort_context *ctx)
-{
- unsigned char *arr;
- size_t i, count;
-
- qsort_context = ctx;
-
- arr = buffer_get_modifyable_data(ctx->sort_buffer, NULL);
- count = buffer_get_used_size(ctx->sort_buffer) / ctx->sort_element_size;
- if (count == 0)
- return;
-
- qsort(arr, count, ctx->sort_element_size, mail_sort_qsort_func);
-
- for (i = 0; i < count; i++, arr += ctx->sort_element_size) {
- if (str_len(ctx->str) >= STRBUF_SIZE-MAX_INT_STRLEN) {
- /* flush */
- o_stream_send(ctx->output,
- str_data(ctx->str), str_len(ctx->str));
- str_truncate(ctx->str, 0);
- ctx->written = TRUE;
- }
-
- str_printfa(ctx->str, " %u", *((unsigned int *) arr));
- }
-
- buffer_set_used_size(ctx->sort_buffer, 0);
-
- if (ctx->string_table != NULL) {
- hash_clear(ctx->string_table, TRUE);
- p_clear(ctx->str_pool);
- }
-}
More information about the dovecot-cvs
mailing list