/*
 * Copyright (c) 2003-2005 Sendmail, Inc. and its suppliers.
 *      All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 *
 *	$Id: edbc.h,v 1.17 2005/11/18 18:15:44 ca Exp $
 */

#ifndef SM_EDBC_H
#define SM_EDBC_H 1

#include "sm/generic.h"
#include "sm/error.h"
#include "sm/magic.h"
#include "sm/time.h"
#include "sm/mta.h"
#include "sm/pthread.h"
#include "sm/bsd-tree.h"
#include "sm/queue.h"

/*
**  Envelope Database Cache: this structure stores the recipients IDs
**  to try in the order of "next time to try" (next_try, ntt for short).
**  It is organized as an AVL tree where each node is the head of a list
**  in which all recipient IDs with the same "next_try" are stored.
**  Note: it would be nice to write a generic structure for this
**  (similar to a hash table) so it can be used in other places too.
**
**  The key must be next_try but that isn't unique...
**  Which data structure allows for that?
**  Do we have to combine trees with list? That is, if the entry is
**  already there, just add it to the node (compare hash tables)?
**  Some of the algorithms below would be simpler if the list (edbc_node)
**  has a pointer back to the "head" (the tnode). Then the time (next_try)
**  would only be stored in the head since it is the same for every element
**  in the list.
**
**  It might be useful to keep some unused nodes in a list for faster
**  (re)allocation.  Could we make this somehow generic ("slab" allocator)?
*/

typedef struct edbc_tnode_S	edbc_tnode_T, *edbc_tnode_P;
typedef struct edbc_node_S	edbc_node_T, *edbc_node_P;
typedef struct edbc_tree_S	edbc_tree_T, *edbc_tree_P;
typedef struct edbc_ctx_S	edbc_ctx_T, *edbc_ctx_P;
typedef TAILQ_HEAD(, edbc_node_S)	ecn_hd_T, *ecn_hd_P;

/*
**  Envelope database cache context.
**  Contains link to root of tree.
*/

struct edbc_ctx_S
{
	sm_magic_T	 sm_magic;
	pthread_mutex_t	 edbc_mutex;
	uint		 edbc_max_entries;
	uint		 edbc_entries;
	uint		 edbc_flags;
#if 0
	time_T		 edbc_max_ntt;	/* maximum valid next time to try */
#endif
	RB_HEAD(edbc_tree_S, edbc_tnode_S)	edbc_root;
};

#define EDBC_FL_NONE	0x00000000

/* AQ is currently "full", at least wrt putting entries from EDB into it */
#define EDBC_FL_AQFULL	0x00000001

#if 0
/* EDBC is currently full, edbc_max_ntt is valid */
#define EDBC_FL_FULL	0x00000002
#endif

#define EDBC_SET_FLAG(edbc_ctx, fl) (edbc_ctx)->edbc_flags |= (fl)
#define EDBC_CLR_FLAG(edbc_ctx, fl) (edbc_ctx)->edbc_flags &= (fl)
#define EDBC_IS_FLAG(edbc_ctx, fl) (((edbc_ctx)->edbc_flags & (fl)) != 0)

#define SM_IS_EDBC(edbc_ctx)	SM_REQUIRE_ISA((edbc_ctx), SM_EDBC_MAGIC)

/*
**  Envelope database cache node which contains the data.
**  Has link to other nodes with same key (next_try).
**  Should next_try be only in the head of the list? That makes access
**  to it more complicated (see also edbc_next() about access to head).
**  Hmm, can we trade time_T with a pointer (same amount of memory)?
*/

struct edbc_node_S
{
	rcpt_id_T			 ecn_rcpt_id;
	time_T				 ecn_next_try;
	TAILQ_ENTRY(edbc_node_S)	 ecn_link;	/* link to next elem */
};

/*
**  Note: it might be useful to add a locktype parameter to most functions.
**  Currently locking is provided by the caller, see edbc.c
*/

sm_ret_T edbc_open(edbc_ctx_P *_pedbc_ctx, uint _size, uint _max_size);
sm_ret_T edbc_close(edbc_ctx_P _edbc_ctx);
sm_ret_T edbc_add(edbc_ctx_P _edbc_ctx, rcpt_id_T _rcpt_id, time_T _next_try, bool _check);
bool	edbc_exists(edbc_ctx_P _edbc_ctx, rcpt_id_T _rcpt_id, edbc_node_P *_pedbc_node);
sm_ret_T edbc_rm(edbc_ctx_P _edbc_ctx, edbc_node_P _edbc_node);
sm_ret_T edbc_mv(edbc_ctx_P _edbc_ctx, edbc_node_P _edbc_node, time_T _next_try);
sm_ret_T edbc_rmentry(edbc_ctx_P _edbc_ctx, rcpt_id_T _rcpt_id);

edbc_node_P	edbc_first(edbc_ctx_P _edbc_ctx);
edbc_node_P	edbc_next(edbc_ctx_P _edbc_ctx, edbc_node_P _edbc_node);

#if QMGR_DEBUG
void	 ebdc_print(edbc_ctx_P _edbc_ctx);
#endif

#if 0
/* put into "local" include file? */
RB_PROTOTYPE(edbc_tree_S, edbc_tnode_S, ectn_entry, edbc_cmp)
#endif

sm_ret_T edbct_rm(edbc_ctx_P _edbc_ctx, edbc_tnode_P _edbc_tnode);
edbc_tnode_P	 edbct_last(edbc_ctx_P _edbc_ctx);
sm_ret_T edbc_insert(edbc_ctx_P _edbc_ctx, rcpt_id_T _rcpt_id, time_T _next_try, bool _check);

#endif /* SM_EDBC_H */


syntax highlighted by Code2HTML, v. 0.9.1