/*
 * Copyright (c) 2002, 2004, 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.
 *
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: t-rsct-0.c,v 1.15 2005/09/29 17:17:49 ca Exp $")

#include <stdio.h>

#include "sm/memops.h"
#include "sm/assert.h"
#include "sm/rpool.h"
#include "sm/rsct.h"
#include "sm/test.h"

#define CSIZE	13
#define HTSIZE	25
#define CHARS	64
#define NH	61	/* must be less than CHARS */
#define HLEN	12
#define KLEN	(HLEN - 2)
#define TYPES	(2u)

int Verbose = 0;
char hashes[NH][HLEN];

typedef struct cdctx_S	cdctx_T, *cdctx_P;
struct cdctx_S
{
	sm_rpool_P	ctx_rpool;
};

static bool
chktype(const char *str, uint type)
{
	uint i;

	for (i = 0; i < NH; i++)
	{
		if (strcmp(str, hashes[i]) == 0)
			return (type == (i % TYPES));
	}
	return false;
}

static void *
create(const char *key, uint len, void *value, void *ctx, uint type)
{
	char *n;
	cdctx_P cdctx;
	sm_rpool_P rpool;

	cdctx = (cdctx_P) ctx;
	rpool = cdctx->ctx_rpool;
	n = sm_rpool_strdup(rpool, value);
	SM_TEST(chktype(key, type));
	if (Verbose > 1)
		fprintf(stderr, "create(%s, %u)=%s\n", key, type, n == NULL ? "<NULL>" : n);
	return n;
}

static sm_ret_T
delete(void *data, void *ctx, uint type)
{
	cdctx_P cdctx;
	sm_rpool_P rpool;

	cdctx = (cdctx_P) ctx;
	rpool = cdctx->ctx_rpool;
	SM_TEST(chktype((char *) data, type));
	if (Verbose > 1)
		fprintf(stderr, "delete(%s, %u)\n", (char *)data, type);
	sm_rpool_free(rpool, data);
	return SM_SUCCESS;
}

static int cnt = 0;

static void
action(const char *key, const void *value, void *ctx, void *walk_ctx, uint type)
{
	if (Verbose > 1)
		fprintf(stderr, "action(%s, %d)=%s\n", key, type, (char *) value);
	++cnt;
}

static void
testh(uint csize, uint htsize)
{
	int i, j, k;
	rsct_P ct;
	void *ctx;
	char *res;
	sm_ret_T ret;
#if SM_BHTABLE_PERF
	char buf[1024];
#endif
	cdctx_T cdctx;

	cdctx.ctx_rpool = NULL;
	ctx = (void *) &cdctx;
	ct = rsct_new(csize, htsize, create, delete, ctx);
	SM_TEST(ct != NULL);
	if (ct == NULL)
		return;
	for (i = 0; i < NH; i++)
	{
		for (j = 0; j < HLEN; j++)
		{
			hashes[i][j] = 'A' + ((i + j) % CHARS);
		}
		hashes[i][HLEN - 1] = '\0';
	}

	/* add CSIZE entries to RSC */
	for (j = 0; j < CSIZE; j++)
	{
		i = j % NH;
		ret = rsct_add(ct, false, i % TYPES, hashes[i], KLEN, hashes[i],
				(void **)&res, THR_LOCK_UNLOCK);
		SM_TEST(!sm_is_err(ret));
		SM_TEST(strcmp(res, hashes[i]) == 0);
		cnt = 0;
		rsct_walk(ct, action, (void *) 0, THR_LOCK_UNLOCK);
		SM_TEST(cnt == j + 1);
		k = rsct_usage(ct);
		SM_TEST(k == (int) ((j + 1) * 100 / CSIZE));
	}

	/* make sure the rest isn't in the RSC */
	for (k = CSIZE; k < NH; k++)
	{
		res = (char *) rsct_lookup(ct, hashes[k], KLEN,
					THR_LOCK_UNLOCK);
		SM_TEST(res == NULL);
		if (Verbose > 0 && res != NULL)
			fprintf(stderr, "j=%d, k=%d, res=%s\n",
				j, k, res);
		k = rsct_usage(ct);
		SM_TEST(k == 100);
	}

	/* check entries in RSC */
	for (j = 0; j < CSIZE; j++)
	{
		i = j % NH;
		res = (char *) rsct_lookup(ct, hashes[i], KLEN,
					THR_LOCK_UNLOCK);
		SM_TEST(strcmp(res, hashes[i]) == 0);
	}

	for (j = 0; j < CSIZE * 3; j++)
	{
		i = j % NH;
		ret = rsct_add(ct, true, i % TYPES, hashes[i], KLEN, hashes[i],
				(void **)&res, THR_LOCK_UNLOCK);
		SM_TEST(!sm_is_err(ret));
		SM_TEST(strcmp(res, hashes[i]) == 0);
		cnt = 0;
		rsct_walk(ct, action, (void *) 0, THR_LOCK_UNLOCK);
		SM_TEST(cnt == CSIZE);

		/* make sure that the other entries are not in the cache */
		if (j > CSIZE)
		{
			for (k = j + 1; k < NH; k++)
			{
				res = (char *) rsct_lookup(ct, hashes[k], KLEN,
							THR_LOCK_UNLOCK);
				SM_TEST(res == NULL);
				if (Verbose > 0 && res != NULL)
					fprintf(stderr, "j=%d, k=%d, res=%s\n",
						j, k, res);
			}
		}
		k = rsct_usage(ct);
		SM_TEST(k == 100);
	}

	/* XXX need a better test that shows the MRU feature */

#if SM_BHTABLE_PERF
	if (Verbose > 0)
	{
		rsct_stats(ct, buf, sizeof(buf));
		printf("%s", buf);
	}
#endif /* SM_BHTABLE_PERF */
	rsct_free(ct);
}

int
main(int argc, char *argv[])
{
	int c;
	uint csize, htsize;

	csize = CSIZE;
	htsize = HTSIZE;
	while ((c = getopt(argc, argv, "c:h:V")) != -1)
	{
		switch (c)
		{
		  case 'c':
			csize = (uint) atoi(optarg);
			break;
		  case 'h':
			htsize = (uint) atoi(optarg);
			break;
		  case 'V':
			++Verbose;
			break;
		  default:
			return(1);
		}
	}
	sm_test_begin(argc, argv, "test rsct 0");
	testh(csize, htsize);
	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1