/*
 * Copyright (c) 2001, 2002, 2003, 2004, 2005  Netli, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id: bstr.c,v 1.1 2005/05/26 12:08:19 vlm Exp $
 */
/*
 * Implementation of the reference-counted, length-contained
 * basic ASCIIZ string. Modelled after well-known Microsoft
 * Windows "Basic String" type, bstr_t.
 */

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include "bstr.h"

/*
 * Shadow structure behind the bstr_t pointer.
 * This structure will be properly aligned.
 */
typedef struct bstr_shadow_s {
	union {
		struct {
			int refs;	/* Reference counter */
			int len;	/* String length (always positive) */
		} life;
		struct {
			bstr_t next;
			int chain_size;
		} death;
	} bstr_shadow_union;
#define	b_refs	bstr_shadow_union.life.refs
#define	b_len	bstr_shadow_union.life.len
#define	b_next	bstr_shadow_union.death.next
#define	b_chain	bstr_shadow_union.death.chain_size
} bstr_shadow_t;

#define	SHADOW(bstr)	((bstr_shadow_t *)(((char *)bstr) \
			- sizeof(bstr_shadow_t)))
#define	ROUND_SIZE	(16)
#define	ROUND_BITS	(4)
#define	ROUND_MASK	((ROUND_SIZE)-1)
#define	ROUND(foo)	( ((foo) + (ROUND_MASK)) & (~(ROUND_MASK)) )

#define	BSTR_FREE_STORAGE_SIZE	256
#define	BSTR_MAX_CHAIN_SIZE	256

static bstr_t _bstr_free_storage[BSTR_FREE_STORAGE_SIZE];
static bstr_t _bstr_get(int len);
static int mem_required(int strlen_ex_null);

/*
 * calc the mem required of a bstr
 * @param
 * @int str_len_wo_null: string's length excluding the null terminator
 */
static inline int
mem_required(int strlen_ex_null)
{
	return ROUND(sizeof(bstr_shadow_t) + strlen_ex_null + 1);
}

/*
 * Create a new basic string.
 * Initialize the reference counter to ONE.
 */
bstr_t
str2bstr(const char *optStr, int optLen) {
	bstr_t bs;

	if(!optStr && optLen < 0) {
		errno = EINVAL;
		return (bstr_t)0;
	}

	if(optLen < 0)
		optLen = strlen(optStr);

	bs = _bstr_get(optLen);
	if(bs == NULL) {
		return NULL;
	}

	SHADOW(bs)->b_refs = 1;
	SHADOW(bs)->b_len = optLen;
	if(optStr) memcpy(bs, optStr, optLen);
	bs[optLen] = '\0';

	return bs;
}

/*
 * Get a pointer to string, incrementing it's reference counter.
 */
bstr_t
bstr_ref(bstr_t src) {
	if(src == NULL) {
		errno = EINVAL;
		return NULL;
	}

	assert(SHADOW(src)->b_refs >= 0);
	SHADOW(src)->b_refs++;

	return src;
}

/*
 * Make a private copy of the given string.
 * Initialize reference counter to one.
 */
bstr_t
bstr_copy(bstr_t src) {

	if(src == NULL) {
		errno = EINVAL;
		return NULL;
	}

	assert(SHADOW(src)->b_refs > 0);

	return str2bstr(src, bstr_len(src));
}


/*
 * Decrement reference counter. If it reaches zero, move this storage
 * into special memory area, or free it, if inappropriate.
 * Depending on the flag, it may also memset area to 0 before freenig.
 */
static inline void
bstr_free_impl(bstr_t bs, int clear_me) {
	int len;
	int mem_size;

	if(bs == NULL) {
		errno = EINVAL;
		return;
	}

	if(--(SHADOW(bs)->b_refs) > 0) {
		/*
		 * Don't allow double bstr_free'ing.
		 * Note that b_next and b_refs is the same memory location.
		 */
		assert( (((int)(SHADOW(bs)->b_next) >> 24) == 0) );
		return;
	}

	if(clear_me && SHADOW(bs)->b_len)
		memset(bs, 0, SHADOW(bs)->b_len);

	mem_size = mem_required(SHADOW(bs)->b_len);
 	len = mem_size >> ROUND_BITS;

	/*
	 * We don't post-keep strings with length more than
	 * (BSTR_FREE_STORAGE_SIZE << ROUND_BITS)
	 */
	if(len >= BSTR_FREE_STORAGE_SIZE) {
		free( SHADOW(bs) );
		return;
	}

	if(_bstr_free_storage[len]) {
		SHADOW(bs)->b_chain
			= SHADOW(_bstr_free_storage[len])->b_chain + 1;
		if(SHADOW(bs)->b_chain > BSTR_MAX_CHAIN_SIZE) {
			free( SHADOW(bs) );
			/* Don't create too long chain of freed strings. */
			return;
		}
		SHADOW(bs)->b_next = _bstr_free_storage[len];
	} else {
		SHADOW(bs)->b_next = 0;
		SHADOW(bs)->b_chain = 1;
	}

	_bstr_free_storage[len] = bs;
}

void
bstr_free_zero(bstr_t bs) {
    bstr_free_impl(bs,1);
}

void
bstr_free(bstr_t bs) {
    bstr_free_impl(bs,0);
}


/* Get the string length */
int
bstr_len(bstr_t bs) {
	return bs ? SHADOW(bs)->b_len : 0;
}

/* Get the reference count */
int
bstr_refs(bstr_t bs) {
	if(bs) {
		return SHADOW(bs)->b_refs;
	} else {
		return 0;
	}
}

/* Local service functions */

/* return a bstr_t. it will try to find in the
 * bstr pool.  If it missed, it malloc new memory
 *
 * @param
 * @int len: string length _EXCLUDING_ the null terminator
 */
static bstr_t
_bstr_get(int len) {
	bstr_t bs;
	int mem_size = mem_required(len);

 	len = mem_size >> ROUND_BITS;
	
	if(len < BSTR_FREE_STORAGE_SIZE && (bs = _bstr_free_storage[len])) {
		/* great! found in bstr pool */
		_bstr_free_storage[len] = SHADOW(bs)->b_next;
		return bs;
	} else {
		/* bstr pool missed.  malloc now */
		char *new_bs = (char*)malloc(mem_size);
		if(new_bs) {
			return (bstr_t)(new_bs + sizeof(bstr_shadow_t));
		} else {
			return NULL;
		}
	}
}

void
bstr_flush_cache() {
	bstr_t bs;
	int i;
	
	for(i = 0; i < BSTR_FREE_STORAGE_SIZE; i++) {
		int mem_size = i << ROUND_BITS;
		while((bs = _bstr_free_storage[i])) {
			_bstr_free_storage[i] = SHADOW(bs)->b_next;
			free(SHADOW(bs));
		}
	}
}



syntax highlighted by Code2HTML, v. 0.9.1