/*
 * Copyright (c) 2002-2006 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: cstr.h,v 1.28 2006/10/05 04:27:35 ca Exp $
 */

#ifndef SM_CSTR_H
#define SM_CSTR_H 1

#include "sm/generic.h"
#include "sm/types.h"
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/heap.h"
#include "sm/limits.h"
#if MTA_USE_PTHREADS
# include "sm/pthread.h"
#endif

#ifndef SM_CSTR_CHECK
# define SM_CSTR_CHECK	1
#endif

#ifndef CSTR_MAX_SIZE
# define CSTR_MAX_SIZE	(SIZE_T_MAX / 2)
#endif


typedef struct sm_cstr_S sm_cstr_T, *sm_cstr_P;

/*
**  sm_cstr_T -- Stores a constant string: the string can only be
**		allocated once and thereafter only read/copied/freed.
**
**  Members:
**	sm_cstr_base -- uchar * to data (doesn't need to end with '\0').
**	sm_cstr_len -- Total number of characters in buf.
**	sm_cstr_refcnt -- number of references
**
**  We could "embed" the string directly in the struct,
**  it would allow us to use only one malloc/free, but it
**  would keep us from using a preallocated string.
*/

struct sm_cstr_S
{
	sm_magic_T	 sm_magic;
	uchar		*sm_cstr_base;
	uint		 sm_cstr_len;
	uint		 sm_cstr_refcnt;

#if MTA_USE_PTHREADS
	/* mutex to protect changes of the reference counter */
	pthread_mutex_t	 sm_cstr_mutex;
#endif
};

/* extern sm_cstr_P	 sm_cstr_new(uint _len); */
sm_cstr_P	 sm_cstr_crt(uchar *_str, uint _len);

/* assign necessary elements to an existing str_buf */
#define sm_cstr_assign(str, s, len) \
	do	\
	{	\
		(str).sm_cstr_base = (s); \
		(str).sm_cstr_len = (len); \
		(str).sm_cstr_refcnt = 1; \
	} while (0)

#if MTA_USE_PTHREADS
/* WARNING: this asserts that lock/unlock succeeds! */
# define SM_CSTR_LOCK(cstr)	SM_ASSERT(pthread_mutex_lock(&(cstr)->sm_cstr_mutex) == 0)
# define SM_CSTR_UNLOCK(cstr)	SM_ASSERT(pthread_mutex_unlock(&(cstr)->sm_cstr_mutex) == 0)
# define SM_CSTR_MUT_FREE(cstr)	SM_ASSERT(pthread_mutex_destroy(&(cstr)->sm_cstr_mutex) == 0)
#else
# define SM_CSTR_LOCK(cstr)	SM_NOOP
# define SM_CSTR_UNLOCK(cstr)	SM_NOOP
# define SM_CSTR_MUT_FREE(cstr)	SM_NOOP
#endif

#if SM_CSTR_CHECK
bool		 sm_cstr_eq(const sm_cstr_P _s1, const sm_cstr_P _s2);
bool		 sm_cstr_caseq(const sm_cstr_P _s1, const sm_cstr_P _s2);
# define SM_CSTR_EQ(s1, s2)	sm_cstr_eq((s1), (s2))
sm_cstr_P	 sm_cstr_dup(sm_cstr_P _src);
# define SM_CSTR_DUP(src)	sm_cstr_dup(src)

#else /* SM_CSTR_CHECK */

# define SM_CSTR_EQ(s1, s2)	\
	((s1) == (s2) || (((s1)->sm_cstr_len == (s2)->sm_cstr_len) && \
		sm_memeq((s1)->sm_cstr_base, (s2)->sm_cstr_base, (s1)->sm_cstr_len)))
# define SM_CSTR_DUP(src)	(SM_CSTR_LOCK(src), (src)->sm_cstr_refcnt++, SM_CSTR_UNLOCK(src), src)


#endif /* SM_CSTR_CHECK */

#define SM_CSTR_CASEQ(s1, s2)	\
	((s1) == (s2) || (((s1)->sm_cstr_len == (s2)->sm_cstr_len) && \
	sm_memcaseeq((s1)->sm_cstr_base, (s2)->sm_cstr_base, (s1)->sm_cstr_len)))

/* Add SM_ASSERT(i < ((str)->sm_cstr_len)) ? */
#define sm_cstr_rd_elem(str, i)	((str)->sm_cstr_base[i])

#define sm_cstr_getlen(str)	((str)->sm_cstr_len)
#define sm_cstr_data(str)	((str)->sm_cstr_base)

/* inline version of sm_cstr_free() with check whether cstr != NULL */
#define SM_CSTR_FREE(cstr) do					\
	{							\
		if ((cstr) != NULL)				\
		{						\
			SM_CSTR_LOCK(cstr);			\
			SM_ASSERT(((cstr)->sm_cstr_refcnt) > 0);	\
			if ((--((cstr)->sm_cstr_refcnt)) == 0)	\
			{					\
				SM_CSTR_UNLOCK(cstr);		\
				sm_free(cstr->sm_cstr_base);	\
				SM_CSTR_MUT_FREE(cstr);		\
				sm_free(cstr);			\
			}					\
			else					\
				SM_CSTR_UNLOCK(cstr);		\
			(cstr) = NULL;				\
		}						\
	} while (0)

/* last character if string is non-empty otherwise -1 (XXX is -1 ok?) */
# define SM_CSTR_LAST(str) ((str)->sm_cstr_len > 0 ? ((str)->sm_cstr_base[(str)->sm_cstr_len - 1]) : (-1))

#if 0
/* UGLY: add '\0' when cstr is created? can't change it afterwards! */
uchar		*sm_cstr_getdata(sm_cstr_P _cstr);
#endif

int		 sm_cstr_cmp(const sm_cstr_P _s1, const sm_cstr_P _s2);
sm_cstr_P	 sm_cstr_scpyn(const uchar *_src, uint _n);
sm_cstr_P	 sm_cstr_scpyn0(const uchar *_src, uint _n);
sm_cstr_P	 sm_cstr_scpyvn0(const uchar *_src, uint _n, ...);
void		 sm_cstr_free(sm_cstr_P _str);

#define SM_IS_CSTR(str)	SM_REQUIRE_ISA((str), SM_CSTR_MAGIC)

#endif /* SM_CSTR_H */


syntax highlighted by Code2HTML, v. 0.9.1