view src/lib-index/mail-transaction-util.c @ 1915:79790750c349 HEAD

importing new index code. mbox still broken.
author Timo Sirainen <tss@iki.fi>
date Tue, 27 Apr 2004 23:25:52 +0300
parents
children e4c30cb5d094
line wrap: on
line source

/* Copyright (C) 2004 Timo Sirainen */

#include "lib.h"
#include "buffer.h"
#include "mail-index-private.h"
#include "mail-transaction-log.h"
#include "mail-transaction-util.h"

struct mail_transaction_expunge_traverse_ctx {
	const struct mail_transaction_expunge *expunges;
	size_t expunges_count, cur_idx, old_idx;
	uint32_t cur_seq, expunges_before;
	uint32_t old_seq, old_expunges_before;
};

const struct mail_transaction_type_map mail_transaction_type_map[] = {
	{ MAIL_TRANSACTION_APPEND, MAIL_INDEX_SYNC_TYPE_APPEND,
	  sizeof(struct mail_index_record) },
	{ MAIL_TRANSACTION_EXPUNGE, MAIL_INDEX_SYNC_TYPE_EXPUNGE,
	  sizeof(struct mail_transaction_expunge) },
	{ MAIL_TRANSACTION_FLAG_UPDATE, MAIL_INDEX_SYNC_TYPE_FLAGS,
	  sizeof(struct mail_transaction_flag_update) },
	{ MAIL_TRANSACTION_CACHE_UPDATE, 0,
	  sizeof(struct mail_transaction_cache_update) },
	{ 0, 0, 0 }
};

const struct mail_transaction_type_map *
mail_transaction_type_lookup(enum mail_transaction_type type)
{
	int i;

	for (i = 0; mail_transaction_type_map[i].type != 0; i++) {
		if ((mail_transaction_type_map[i].type & type) != 0)
			return &mail_transaction_type_map[i];
	}
	return NULL;
}

enum mail_transaction_type
mail_transaction_type_mask_get(enum mail_index_sync_type sync_type)
{
        enum mail_transaction_type type = 0;
	int i;

	for (i = 0; mail_transaction_type_map[i].type != 0; i++) {
		if ((mail_transaction_type_map[i].sync_type & sync_type) != 0)
			type |= mail_transaction_type_map[i].type;
	}
	return type;
}

int mail_transaction_map(const struct mail_transaction_header *hdr,
			 const void *data,
			 struct mail_transaction_map_functions *map,
			 void *context)
{
	int ret = 0;

	switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
	case MAIL_TRANSACTION_APPEND: {
		const struct mail_index_record *rec, *end;

		if (map->append == NULL)
			break;

		end = CONST_PTR_OFFSET(data, hdr->size);
		for (rec = data; rec != end; rec++) {
			ret = map->append(rec, context);
			if (ret <= 0)
				break;
		}
		break;
	}
	case MAIL_TRANSACTION_EXPUNGE: {
		const struct mail_transaction_expunge *rec, *end;

		if (map->expunge == NULL)
			break;

		end = CONST_PTR_OFFSET(data, hdr->size);
		for (rec = data; rec != end; rec++) {
			ret = map->expunge(rec, context);
			if (ret <= 0)
				break;
		}
		break;
	}
	case MAIL_TRANSACTION_FLAG_UPDATE: {
		const struct mail_transaction_flag_update *rec, *end;

		if (map->flag_update == NULL)
			break;

		end = CONST_PTR_OFFSET(data, hdr->size);
		for (rec = data; rec != end; rec++) {
			ret = map->flag_update(rec, context);
			if (ret <= 0)
				break;
		}
		break;
	}
	case MAIL_TRANSACTION_CACHE_UPDATE: {
		const struct mail_transaction_cache_update *rec, *end;

		if (map->cache_update == NULL)
			break;

		end = CONST_PTR_OFFSET(data, hdr->size);
		for (rec = data; rec != end; rec++) {
			ret = map->cache_update(rec, context);
			if (ret <= 0)
				break;
		}
		break;
	}
	default:
		i_unreached();
	}

	return ret;
}

void
mail_transaction_log_sort_expunges(buffer_t *expunges_buf,
				   const struct mail_transaction_expunge *src,
				   size_t src_buf_size)
{
	const struct mail_transaction_expunge *src_end;
	struct mail_transaction_expunge *dest;
	struct mail_transaction_expunge new_exp;
	uint32_t cur_seq, prev_seq, expunges_before, count;
	size_t first, i, dest_count;

	i_assert(src_buf_size % sizeof(*src) == 0);
	src_end = CONST_PTR_OFFSET(src, src_buf_size);

	/* @UNSAFE */
	dest = buffer_get_modifyable_data(expunges_buf, &dest_count);
	dest_count /= sizeof(*dest);

	cur_seq = prev_seq = 1; expunges_before = 0;
	for (i = 0; src != src_end; src++) {
		for (; i < dest_count; i++) {
			count = dest[i].seq1 - prev_seq;
			if (cur_seq + count > src->seq1)
				break;
			cur_seq += count;

			expunges_before += dest[i].seq2 - dest[i].seq1 + 1;
			prev_seq = dest[i].seq2+1;
		}

		new_exp.seq1 = src->seq1 + expunges_before;
		new_exp.seq2 = src->seq2 + expunges_before;

		/* if src[] is in format {1,2}{1,2} rather than {1,2}{3,4}:
		   expunges_before += new_exp.seq2 - new_exp.seq1 + 1;*/

		first = i;
		while (i < dest_count && new_exp.seq2 >= dest[i].seq1-1) {
			/* we can/must merge with next record */
			count = dest[i].seq2 - dest[i].seq1 + 1;
			expunges_before += count;
			new_exp.seq2 += count;
			i++;
		}

		if (first > 0 && new_exp.seq1 == dest[first-1].seq2+1) {
			/* continue previous record */
			dest[first-1].seq2 = new_exp.seq2;
		} else if (i == first) {
			buffer_insert(expunges_buf, i * sizeof(new_exp),
				      &new_exp, sizeof(new_exp));
			i++; first++;

			dest = buffer_get_modifyable_data(expunges_buf, NULL);
			dest_count++;
		} else {
			/* use next record */
			dest[first].seq1 = new_exp.seq1;
			dest[first].seq2 = new_exp.seq2;
			first++;
		}

		if (i > first) {
			buffer_delete(expunges_buf, first * sizeof(new_exp),
				      (i - first) * sizeof(new_exp));

			dest = buffer_get_modifyable_data(expunges_buf, NULL);
			dest_count -= i - first;
			i = first + 1;
		}
	}
}

struct mail_transaction_expunge_traverse_ctx *
mail_transaction_expunge_traverse_init(const buffer_t *expunges_buf)
{
	struct mail_transaction_expunge_traverse_ctx *ctx;

	ctx = i_new(struct mail_transaction_expunge_traverse_ctx, 1);
	ctx->cur_seq = 1;
	ctx->old_seq = 1;

	if (expunges_buf != NULL) {
		ctx->expunges =
			buffer_get_data(expunges_buf, &ctx->expunges_count);
		ctx->expunges_count /= sizeof(*ctx->expunges);
	}
	return ctx;
}

void mail_transaction_expunge_traverse_deinit(
	struct mail_transaction_expunge_traverse_ctx *ctx)
{
	i_free(ctx);
}

uint32_t mail_transaction_expunge_traverse_to(
	struct mail_transaction_expunge_traverse_ctx *ctx, uint32_t seq)
{
	uint32_t idx, count, last_seq;

	if (seq < ctx->cur_seq) {
		/* allow seeking one back */
		ctx->cur_idx = ctx->old_idx;
		ctx->cur_seq = ctx->old_seq;
		ctx->expunges_before = ctx->old_expunges_before;
	} else {
		ctx->old_idx = ctx->cur_idx;
		ctx->old_seq = ctx->cur_seq;
		ctx->old_expunges_before = ctx->expunges_before;
	}
	i_assert(seq >= ctx->cur_seq);

	idx = ctx->cur_idx;
	last_seq = idx == 0 ? 1 : ctx->expunges[idx-1].seq2 + 1;
	for (; idx < ctx->expunges_count; idx++) {
		count = ctx->expunges[idx].seq1 - last_seq;
		if (ctx->cur_seq + count > seq)
			break;
		ctx->cur_seq += count;

		ctx->expunges_before += ctx->expunges[idx].seq2 -
			ctx->expunges[idx].seq1 + 1;
		last_seq = ctx->expunges[idx].seq2+1;
	}

	ctx->cur_idx = idx;
	return ctx->expunges_before;
}