/* $Id$
 *
 * shared memory, multi-process safe, pool based version of f_malloc
 *
 * Copyright (C) 2007 iptelorg GmbH
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * History:
 * --------
 *              created by andrei
 *  2003-07-06  added fm_realloc (andrei)
 *  2004-07-19  fragments book keeping code and support for 64 bits
 *               memory blocks (64 bits machine & size >=2^32) 
 *              GET_HASH s/</<=/ (avoids waste of 1 hash cell)   (andrei)
 *  2004-11-10  support for > 4Gb mem., switched to long (andrei)
 *  2005-03-02  added fm_info() (andrei)
 *  2005-12-12  fixed realloc shrink real_used accounting (andrei)
 *              fixed initial size (andrei)
 *  2006-02-03  fixed realloc out of mem. free bug (andrei)
 *  2006-04-07  s/DBG/MDBG (andrei)
 *  2007-02-23  added fm_available() (andrei)
 *  2007-06-09  forked from the fm_maloc code (andrei)
 */


#ifdef SF_MALLOC

#include <string.h>
#include <stdlib.h>

#include "sf_malloc.h"
#include "../dprint.h"
#include "../globals.h"
#include "memdbg.h"

#define MAX_POOL_FRAGS 10000 /* max fragments per pool hash bucket */
#define MIN_POOL_FRAGS 10    /* min fragments per pool hash bucket */

/*useful macros*/

#define FRAG_NEXT(f) \
	((struct sfm_frag*)((char*)(f)+sizeof(struct sfm_frag)+(f)->size ))


/* SF_ROUNDTO= 2^k so the following works */
#define ROUNDTO_MASK	(~((unsigned long)SF_ROUNDTO-1))
#define ROUNDUP(s)		(((s)+(SF_ROUNDTO-1))&ROUNDTO_MASK)
#define ROUNDDOWN(s)	((s)&ROUNDTO_MASK)

#define FRAG_OVERHEAD	(sizeof(struct sfm_frag))
#define INIT_OVERHEAD	\
	(ROUNDUP(sizeof(struct sfm_block))+sizeof(struct sfm_frag))



/* finds hash if s <=SF_MALLOC_OPTIMIZE */
#define GET_SMALL_HASH(s) (unsigned long)(s)/SF_ROUNDTO
/* finds hash if s > SF_MALLOC_OPTIMIZE */
#define GET_BIG_HASH(s) \
	(SF_MALLOC_OPTIMIZE/SF_ROUNDTO+big_hash_idx((s))-SF_MALLOC_OPTIMIZE_FACTOR+1)

/* finds the hash value for s, s=SF_ROUNDTO multiple*/
#define GET_HASH(s)   ( ((unsigned long)(s)<=SF_MALLOC_OPTIMIZE)?\
							GET_SMALL_HASH(s): GET_BIG_HASH(s) )


#define UN_HASH_SMALL(h) ((unsigned long)(h)*SF_ROUNDTO)
#define UN_HASH_BIG(h) (1UL<<((unsigned long)(h)-SF_MALLOC_OPTIMIZE/SF_ROUNDTO+\
							SF_MALLOC_OPTIMIZE_FACTOR-1))

#define UN_HASH(h)	( ((unsigned long)(h)<=(SF_MALLOC_OPTIMIZE/SF_ROUNDTO))?\
						UN_HASH_SMALL(h): UN_HASH_BIG(h) )

#define BITMAP_BITS (sizeof(((struct sfm_block*)0)->bitmap)*8)
#define BITMAP_BLOCK_SIZE ((SF_MALLOC_OPTIMIZE/SF_ROUNDTO)/ BITMAP_BITS)
/* only for "small" hashes (up to HASH(SF_MALLOC_OPTIMIZE) */
#define HASH_BIT_POS(h) (((unsigned long)(h))/BITMAP_BLOCK_SIZE)
#define HASH_TO_BITMAP(h) (1UL<<HASH_BIT_POS(h))
#define BIT_TO_HASH(b) ((b)*BITMAP_BLOCK_SIZE)



/* mark/test used/unused frags */
#define FRAG_MARK_USED(f)
#define FRAG_CLEAR_USED(f)
#define FRAG_WAS_USED(f)   (1)

/* other frag related defines:
 * MEM_COALESCE_FRAGS 
 * MEM_FRAG_AVOIDANCE
 */
#define MEM_FRAG_AVOIDANCE


#define SFM_REALLOC_REMALLOC

/* computes hash number for big buckets*/
inline static unsigned long big_hash_idx(unsigned long s)
{
	unsigned long idx;
	/* s is rounded => s = k*2^n (SF_ROUNDTO=2^n) 
	 * index= i such that 2^i > s >= 2^(i-1)
	 *
	 * => index = number of the first non null bit in s*/
	idx=sizeof(long)*8-1;
	for (; !(s&(1UL<<(sizeof(long)*8-1))) ; s<<=1, idx--);
	return idx;
}


#ifdef DBG_F_MALLOC
#define ST_CHECK_PATTERN   0xf0f0f0f0
#define END_CHECK_PATTERN1 0xc0c0c0c0
#define END_CHECK_PATTERN2 0xabcdefed
#endif


#ifdef SFM_ONE_LOCK

#define SFM_MAIN_HASH_LOCK(qm, hash) lock_get(&(qm)->lock)
#define SFM_MAIN_HASH_UNLOCK(qm, hash) lock_release(&(qm)->lock)
#define SFM_POOL_LOCK(p, hash) lock_get(&(p)->lock)
#define SFM_POOL_UNLOCK(p, hash) lock_release(&(p)->lock)

#warn "degraded performance, only one lock"

#elif defined SFM_LOCK_PER_BUCKET

#define SFM_MAIN_HASH_LOCK(qm, hash) \
	lock_get(&(qm)->free_hash[(hash)].lock)
#define SFM_MAIN_HASH_UNLOCK(qm, hash)  \
	lock_release(&(qm)->free_hash[(hash)].lock)
#define SFM_POOL_LOCK(p, hash) lock_get(&(p)->pool_hash[(hash)].lock)
#define SFM_POOL_UNLOCK(p, hash) lock_release(&(p)->pool_hash[(hash)].lock)
#else
#error no locks defined
#endif /* SFM_ONE_LOCK/SFM_LOCK_PER_BUCKET */

#define SFM_BIG_GET_AND_SPLIT_LOCK(qm)   lock_get(&(qm)->get_and_split)
#define SFM_BIG_GET_AND_SPLIT_UNLOCK(qm) lock_release(&(qm)->get_and_split)

static unsigned long sfm_max_hash=0; /* maximum hash value (no point in
										 searching further) */
static unsigned long pool_id=(unsigned long)-1;


/* call for each child */
int sfm_pool_reset()
{
	pool_id=(unsigned long)-1;
	return 0;
}


#define sfm_fix_pool_id(qm) \
	do{ \
		if (unlikely(pool_id>=SFM_POOLS_NO)) \
			pool_id=((unsigned)atomic_add(&(qm)->crt_id, 1))%SFM_POOLS_NO; \
	}while(0)



static inline void frag_push(struct sfm_frag** head, struct sfm_frag* frag)
{
	frag->u.nxt_free=*head;
	*head=frag;
}


static inline struct sfm_frag* frag_pop(struct sfm_frag** head)
{
	struct sfm_frag* frag;
	frag=*head;
	*head=frag->u.nxt_free;
	return frag;
}

static inline void sfm_pool_insert (struct sfm_pool* pool, int hash,
								struct sfm_frag* frag)
{
	unsigned long hash_bit;

	SFM_POOL_LOCK(pool, hash);
	frag_push(&pool->pool_hash[hash].first, frag);
	pool->pool_hash[hash].no++;
	/* set it only if not already set (avoids an expensive
	 * cache trashing atomic write op) */
	hash_bit=HASH_TO_BITMAP(hash);
	if  (!(atomic_get_long((long*)&pool->bitmap) & hash_bit))
		atomic_or_long((long*)&pool->bitmap, hash_bit);
	SFM_POOL_UNLOCK(pool, hash);
}



/* returns 1 if it's ok to add a fragm. to pool p_id @ hash, 0 otherwise */
static inline int sfm_check_pool(struct sfm_block* qm, unsigned long p_id,
									int hash, int split)
{
	/* TODO: come up with something better
	 * if fragment is some  split/rest from an allocation, that is
	 *  >= requested size, accept it, else
	 *  look at misses and current fragments and decide based on them */
	return (p_id<SFM_POOLS_NO) && (split ||
			( (qm->pool[p_id].pool_hash[hash].no < MIN_POOL_FRAGS) ||
			  ((qm->pool[p_id].pool_hash[hash].misses > 
				 qm->pool[p_id].pool_hash[hash].no) &&
				(qm->pool[p_id].pool_hash[hash].no<MAX_POOL_FRAGS) ) ) );
}


/* choose on which pool to add a free'd packet
 * return - pool idx or -1 if it should be added to main*/
static inline unsigned long  sfm_choose_pool(struct sfm_block* qm,
												struct sfm_frag* frag,
												int hash, int split)
{
	/* check original pool first */
	if (sfm_check_pool(qm, frag->id, hash, split))
		return frag->id;
	else{
		/* check if our pool is properly set */
		sfm_fix_pool_id(qm);
		/* check if my pool needs some frags */
		if ((pool_id!=frag->id) && (sfm_check_pool(qm,  pool_id, hash, 0))){
			frag->id=pool_id;
			return pool_id;
		}
	}
	/* else add it back to main */
	frag->id=(unsigned long)(-1);
	return frag->id;
}


static inline void sfm_insert_free(struct sfm_block* qm, struct sfm_frag* frag,
									int split)
{
	struct sfm_frag** f;
	unsigned long p_id;
	int hash;
	unsigned long hash_bit;
	
	if (likely(frag->size<=SF_POOL_MAX_SIZE)){
		hash=GET_SMALL_HASH(frag->size);
		if (unlikely((p_id=sfm_choose_pool(qm, frag, hash, split))==
					(unsigned long)-1)){
			/* add it back to the "main" hash */
			SFM_MAIN_HASH_LOCK(qm, hash);
				frag->id=(unsigned long)(-1); /* main hash marker */
				/*insert it here*/
				frag_push(&(qm->free_hash[hash].first), frag);
				qm->free_hash[hash].no++;
				/* set it only if not already set (avoids an expensive
		 		* cache trashing atomic write op) */
				hash_bit=HASH_TO_BITMAP(hash);
				if  (!(atomic_get_long((long*)&qm->bitmap) & hash_bit))
					atomic_or_long((long*)&qm->bitmap, hash_bit);
			SFM_MAIN_HASH_UNLOCK(qm, hash);
		}else{
			/* add it to one of the pools pool */
			sfm_pool_insert(&qm->pool[p_id], hash, frag);
		}
	}else{
		hash=GET_BIG_HASH(frag->size);
		SFM_MAIN_HASH_LOCK(qm, hash);
			f=&(qm->free_hash[hash].first);
			for(; *f; f=&((*f)->u.nxt_free))
				if (frag->size <= (*f)->size) break;
			frag->id=(unsigned long)(-1); /* main hash marker */
			/*insert it here*/
			frag->u.nxt_free=*f;
			*f=frag;
			qm->free_hash[hash].no++;
			/* inc. big hash free size ? */
		SFM_MAIN_HASH_UNLOCK(qm, hash);
	}
	
}



 /* size should be already rounded-up */
static inline
#ifdef DBG_F_MALLOC 
void sfm_split_frag(struct sfm_block* qm, struct sfm_frag* frag,
					unsigned long size,
					const char* file, const char* func, unsigned int line)
#else
void sfm_split_frag(struct sfm_block* qm, struct sfm_frag* frag,
					unsigned long size)
#endif
{
	unsigned long rest;
	struct sfm_frag* n;
	int bigger_rest;
	
	rest=frag->size-size;
#ifdef MEM_FRAG_AVOIDANCE
	if ((rest> (FRAG_OVERHEAD+SF_MALLOC_OPTIMIZE))||
		(rest>=(FRAG_OVERHEAD+size))){ /* the residue fragm. is big enough*/
		bigger_rest=1;
#else
	if (rest>(FRAG_OVERHEAD+SF_MIN_FRAG_SIZE)){
		bigger_rest=rest>=(size+FRAG_OVERHEAD);
#endif
		frag->size=size;
		/*split the fragment*/
		n=FRAG_NEXT(frag);
		n->size=rest-FRAG_OVERHEAD;
		n->id=pool_id;
		FRAG_CLEAR_USED(n); /* never used */
#ifdef DBG_F_MALLOC
		/* frag created by malloc, mark it*/
		n->file=file;
		n->func="frag. from sfm_malloc";
		n->line=line;
		n->check=ST_CHECK_PATTERN;
#endif
		/* reinsert n in free list*/
		sfm_insert_free(qm, n, bigger_rest);
	}else{
		/* we cannot split this fragment any more => alloc all of it*/
	}
}



/* init malloc and return a sfm_block*/
struct sfm_block* sfm_malloc_init(char* address, unsigned long size)
{
	char* start;
	char* end;
	struct sfm_block* qm;
	unsigned long init_overhead;
	int r;
#ifdef SFM_LOCK_PER_BUCKET
	int i;
#endif
	
	/* make address and size multiple of 8*/
	start=(char*)ROUNDUP((unsigned long) address);
	DBG("sfm_malloc_init: SF_OPTIMIZE=%lu, /SF_ROUNDTO=%lu\n",
			SF_MALLOC_OPTIMIZE, SF_MALLOC_OPTIMIZE/SF_ROUNDTO);
	DBG("sfm_malloc_init: SF_HASH_SIZE=%lu, sfm_block size=%lu\n",
			SF_HASH_SIZE, (long)sizeof(struct sfm_block));
	DBG("sfm_malloc_init(%p, %lu), start=%p\n", address, size, start);

	if (size<start-address) return 0;
	size-=(start-address);
	if (size <(SF_MIN_FRAG_SIZE+FRAG_OVERHEAD)) return 0;
	size=ROUNDDOWN(size);

	init_overhead=INIT_OVERHEAD;
	
	
	if (size < init_overhead)
	{
		/* not enough mem to create our control structures !!!*/
		return 0;
	}
	end=start+size;
	qm=(struct sfm_block*)start;
	memset(qm, 0, sizeof(struct sfm_block));
	qm->size=size;
	size-=init_overhead;
	
	qm->first_frag=(struct sfm_frag*)(start+ROUNDUP(sizeof(struct sfm_block)));
	qm->last_frag=(struct sfm_frag*)(end-sizeof(struct sfm_frag));
	/* init initial fragment*/
	qm->first_frag->size=size;
	qm->first_frag->id=(unsigned long)-1; /* not in a pool */
	qm->last_frag->size=0;
	
#ifdef DBG_F_MALLOC
	qm->first_frag->check=ST_CHECK_PATTERN;
	qm->last_frag->check=END_CHECK_PATTERN1;
#endif
	
	/* link initial fragment into the free list*/
	
	sfm_insert_free(qm, qm->first_frag, 0);
	sfm_max_hash=GET_HASH(size);
	
	/* init locks */
	if (lock_init(&qm->get_and_split)==0)
		goto error;
#ifdef SFM_ONE_LOCK
	if (lock_init(&qm->lock)==0){
		lock_destroy(&qm->get_and_split);
		goto error;
	}
	for (r=0; r<SFM_POOLS_NO; r++){
		if (lock_init(&qm->pool[r].lock)==0){
			for (;r>0; r--) lock_destroy(&qm->pool[r-1].lock);
			lock_destroy(&qm->lock);
			lock_destroy(&qm->get_and_split);
			goto error;
		}
	}
#elif defined(SFM_LOCK_PER_BUCKET)
	for (r=0; r<SF_HASH_SIZE; r++)
		if (lock_init(&qm->free_hash[r].lock)==0){
			for(;r>0; r--) lock_destroy(&qm->free_hash[r-1].lock);
			lock_destroy(&qm->get_and_split);
			goto error;
		}
	for (i=0; i<SFM_POOLS_NO; i++){
		for (r=0; r<SF_HASH_POOL_SIZE; r++)
			if (lock_init(&qm->pool[i].pool_hash[r].lock)==0){
				for(;r>0; r--) lock_destroy(&qm->pool[i].poo_hash[r].lock);
				for(; i>0; i--){
					for (r=0; r<SF_HASH_POOL_SIZE; r++)
						lock_destroy(&qm->pool[i].pool_hash[r].lock);
				}
				for (r=0; r<SF_HASH_SIZE; r++)
					lock_destroy(&qm->free_hash[r].lock);
				lock_destroy(&qm->get_and_split);
				goto error;
			}
	}
#endif
	qm->is_init=1;
	return qm;
error:
	return 0;
}



/* cleanup */
void sfm_malloc_destroy(struct sfm_block* qm)
{
	int r, i;
	/* destroy all the locks */
	if (!qm || !qm->is_init)
		return; /* nothing to do */
	lock_destroy(&qm->get_and_split);
#ifdef SFM_ONE_LOCK
	lock_destroy(&qm->lock);
	for (r=0; r<SFM_POOLS_NO; r++){
		lock_destroy(&qm->pool[r].lock);
	}
#elif defined(SFM_LOCK_PER_BUCKET)
	for (r=0; r<SF_HASH_SIZE; r++)
		lock_destroy(&qm->free_hash[r].lock);
	for (i=0; i<SFM_POOLS_NO; i++){
		for (r=0; r<SF_HASH_POOL_SIZE; r++)
			lock_destroy(&qm->pool[i].pool_hash[r].lock);
	}
#endif
	qm->is_init=0;

}


/* returns next set bit in bitmap, starts at b
 * if b is set, returns b
 * if not found returns BITMAP_BITS */
static inline unsigned long _next_set_bit(unsigned long b,
											unsigned long* bitmap)
{
	for (; !((1UL<<b)& *bitmap) && b<BITMAP_BITS; b++);
	return b;
}

/* returns start of block b and sets *end
 * (handles also the "rest" block at the end ) */
static inline unsigned long _hash_range(unsigned long b, unsigned long* end)
{
	unsigned long s;
	
	if ((unlikely(b>=BITMAP_BITS))){
		s=BIT_TO_HASH(BITMAP_BITS);
		*end=SF_HASH_POOL_SIZE; /* last, possible rest block */
	}else{
		s=BIT_TO_HASH(b);
		*end=s+BITMAP_BLOCK_SIZE;
	}
	return s;
}


#ifdef DBG_F_MALLOC
static inline struct sfm_frag* pool_get_frag(struct sfm_block* qm,
						struct sfm_pool*  pool, int hash, unisgned long size,
						const char* file, const char* func, unsigned int line)
#else
static inline struct sfm_frag* pool_get_frag(struct sfm_block* qm,
											struct sfm_pool*  pool,
											int hash, unsigned long size)
#endif
{
	int r;
	int next_block;
	struct sfm_frag* volatile* f;
	struct sfm_frag* frag;
	unsigned long b;
	unsigned long eob;

	/* special case for r=hash */
	r=hash;
	f=&pool->pool_hash[r].first;
	if (*f==0) 
		goto not_found;
	SFM_POOL_LOCK(pool, r);
	if (unlikely(*f==0)){
		SFM_POOL_UNLOCK(pool, r);
		goto not_found;
	}
found:
	/* detach it from the free list*/
	frag=frag_pop((struct sfm_frag**)f);
	frag->u.nxt_free=0; /* mark it as 'taken' */
	frag->id=pool_id;
	pool->pool_hash[r].no--;
	SFM_POOL_UNLOCK(pool, r);
#ifdef DBG_F_MALLOC
	sfm_split_frag(qm, frag, size, file, func, line);
#else
	sfm_split_frag(qm, frag, size);
#endif
	if (&qm->pool[pool_id]==pool)
		atomic_inc_long((long*)&pool->hits);
	return frag;
	
not_found:
	atomic_inc_long((long*)&pool->pool_hash[r].misses);
	r++;
	b=HASH_BIT_POS(r);
	
	while(r<SF_HASH_POOL_SIZE){
		b=_next_set_bit(b, &pool->bitmap);
		next_block=_hash_range(b, &eob);
		r=(r<next_block)?next_block:r;
		for (; r<eob; r++){
			f=&pool->pool_hash[r].first;
			if (*f){
				SFM_POOL_LOCK(pool, r);
				if (unlikely(*f==0)){
					/* not found */
					SFM_POOL_UNLOCK(pool, r);
				}else
					goto found;
			}
			atomic_inc_long((long*)&pool->pool_hash[r].misses);
		}
		b++;
	}
#if 0 /* EXPENSIVE BUG CHECK */
	for (r=hash; r<SF_HASH_POOL_SIZE; r++){
		f=&pool->pool_hash[r].first;
		if (*f){
				SFM_POOL_LOCK(pool, r);
				if (unlikely(*f==0)){
					/* not found */
					SFM_POOL_UNLOCK(pool, r);
				}else{
					b=_next_set_bit(HASH_BIT_POS(r), &pool->bitmap);
					next_block=_hash_range(b, &eob);
					BUG("pool_get_frag: found fragm. %d at %d (bit %ld range %ld-%ld), next set bit=%ld"
							" bitmap %ld (%p)\n", hash, r, HASH_BIT_POS(r),
							next_block, eob, b, pool->bitmap, &pool->bitmap);
					goto found;
				}
		}
	}
#endif
	atomic_inc_long((long*)&pool->missed);
	return 0;
}



#ifdef DBG_F_MALLOC
static inline struct sfm_frag* main_get_frag(struct sfm_block* qm, int hash,
						unsigned long size,
						const char* file, const char* func, unsigned int line)
#else
static inline struct sfm_frag* main_get_frag(struct sfm_block* qm, int hash,
												unsigned long size)
#endif
{
	int r;
	int next_block;
	struct sfm_frag* volatile* f;
	struct sfm_frag* frag;
	unsigned long b;
	unsigned long eob;

	r=hash;
	b=HASH_BIT_POS(r);
	while(r<=SF_MALLOC_OPTIMIZE/SF_ROUNDTO){
			b=_next_set_bit(b, &qm->bitmap);
			next_block=_hash_range(b, &eob);
			r=(r<next_block)?next_block:r;
			for (; r<eob; r++){
				f=&qm->free_hash[r].first;
				if (*f){
					SFM_MAIN_HASH_LOCK(qm, r);
					if (unlikely(*f==0)){
						/* not found, somebody stole it */
						SFM_MAIN_HASH_UNLOCK(qm, r);
						continue; 
					}
					/* detach it from the free list*/
					frag=frag_pop((struct sfm_frag**)f);
					frag->u.nxt_free=0; /* mark it as 'taken' */
					qm->free_hash[r].no--;
					SFM_MAIN_HASH_UNLOCK(qm, r);
					frag->id=pool_id;
#ifdef DBG_F_MALLOC
					sfm_split_frag(qm, frag, size, file, func, line);
#else
					sfm_split_frag(qm, frag, size);
#endif
					return frag;
				}
			}
			b++;
	}
	/* big fragments */
	SFM_BIG_GET_AND_SPLIT_LOCK(qm);
	for (; r<= sfm_max_hash ; r++){
		f=&qm->free_hash[r].first;
		if (*f){
			SFM_MAIN_HASH_LOCK(qm, r);
			if (unlikely((*f)==0)){
				/* not found */
				SFM_MAIN_HASH_UNLOCK(qm, r);
				continue; 
			}
			for(;(*f); f=&((*f)->u.nxt_free))
				if ((*f)->size>=size){
					/* found, detach it from the free list*/
					frag=*f;
					*f=frag->u.nxt_free;
					frag->u.nxt_free=0; /* mark it as 'taken' */
					qm->free_hash[r].no--;
					SFM_MAIN_HASH_UNLOCK(qm, r);
					frag->id=pool_id;
#ifdef DBG_F_MALLOC
					sfm_split_frag(qm, frag, size, file, func, line);
#else
					sfm_split_frag(qm, frag, size);
#endif
					SFM_BIG_GET_AND_SPLIT_UNLOCK(qm);
					return frag;
				};
			SFM_MAIN_HASH_UNLOCK(qm, r);
			/* try in a bigger bucket */
		}
	}
	SFM_BIG_GET_AND_SPLIT_UNLOCK(qm);
	return 0;
}



#ifdef DBG_F_MALLOC
void* sfm_malloc(struct sfm_block* qm, unsigned long size,
					const char* file, const char* func, unsigned int line)
#else
void* sfm_malloc(struct sfm_block* qm, unsigned long size)
#endif
{
	struct sfm_frag* frag;
	int hash;
	unsigned int i;
	
#ifdef DBG_F_MALLOC
	MDBG("sfm_malloc(%p, %lu) called from %s: %s(%d)\n", qm, size, file, func,
			line);
#endif
	/*size must be a multiple of 8*/
	size=ROUNDUP(size);
/*	if (size>(qm->size-qm->real_used)) return 0; */

	/* check if our pool id is set */
	sfm_fix_pool_id(qm);
	
	/*search for a suitable free frag*/
	if (likely(size<=SF_POOL_MAX_SIZE)){
		hash=GET_SMALL_HASH(size);
		/* try first in our pool */
#ifdef DBG_F_MALLOC
		if (likely((frag=pool_get_frag(qm, &qm->pool[pool_id], hash, size,
										file, func, line))!=0))
			goto found;
		/* try in the "main" free hash, go through all the hash */
		if (likely((frag=main_get_frag(qm, hash, size, file, func, line))!=0))
			goto found;
		/* really low mem , try in other pools */
		for (i=(pool_id+1); i< (pool_id+SFM_POOLS_NO); i++){
			if ((frag=pool_get_frag(qm, &qm->pool[i%SFM_POOLS_NO], hash, size,
										file, func, line))!=0)
				goto found;
		}
#else
		if (likely((frag=pool_get_frag(qm, &qm->pool[pool_id], hash, size))
					!=0 ))
			goto found;
		/* try in the "main" free hash, go through all the hash */
		if (likely((frag=main_get_frag(qm, hash, size))!=0))
			goto found;
		/* really low mem , try in other pools */
		for (i=(pool_id+1); i< (pool_id+SFM_POOLS_NO); i++){
			if ((frag=pool_get_frag(qm, &qm->pool[i%SFM_POOLS_NO], hash, size))
					!=0 )
				goto found;
		}
#endif
		/* not found, bad! */
		return 0;
	}else{
		hash=GET_BIG_HASH(size);
#ifdef DBG_F_MALLOC
		if ((frag=main_get_frag(qm, hash, size, file, func, line))==0)
			return 0; /* not found, bad! */
#else
		if ((frag=main_get_frag(qm, hash, size))==0)
			return 0; /* not found, bad! */
#endif
	}

found:
	/* we found it!*/
#ifdef DBG_F_MALLOC
	frag->file=file;
	frag->func=func;
	frag->line=line;
	frag->check=ST_CHECK_PATTERN;
	MDBG("sfm_malloc(%p, %lu) returns address %p \n", qm, size,
		(char*)frag+sizeof(struct sfm_frag));
#endif
	FRAG_MARK_USED(frag); /* mark it as used */
	return (char*)frag+sizeof(struct sfm_frag);
}



#ifdef DBG_F_MALLOC
void sfm_free(struct sfm_block* qm, void* p, const char* file,
				const char* func, unsigned int line)
#else
void sfm_free(struct sfm_block* qm, void* p)
#endif
{
	struct sfm_frag* f;

#ifdef DBG_F_MALLOC
	MDBG("sfm_free(%p, %p), called from %s: %s(%d)\n", qm, p, file, func,
				line);
	if (p>(void*)qm->last_frag || p<(void*)qm->first_frag){
		LOG(L_CRIT, "BUG: sfm_free: bad pointer %p (out of memory block!) - "
				"aborting\n", p);
		abort();
	}
#endif
	if (unlikely(p==0)) {
		LOG(L_WARN, "WARNING: sfm_free: free(0) called\n");
		return;
	}
	f=(struct sfm_frag*) ((char*)p-sizeof(struct sfm_frag));
#ifdef DBG_F_MALLOC
	MDBG("sfm_free: freeing block alloc'ed from %s: %s(%ld)\n",
			f->file, f->func, f->line);
#endif
#ifdef DBG_F_MALLOC
	f->file=file;
	f->func=func;
	f->line=line;
#endif
	sfm_insert_free(qm, f, 0);
}


#ifdef DBG_F_MALLOC
void* sfm_realloc(struct sfm_block* qm, void* p, unsigned long size,
					const char* file, const char* func, unsigned int line)
#else
void* sfm_realloc(struct sfm_block* qm, void* p, unsigned long size)
#endif
{
	struct sfm_frag *f;
	unsigned long orig_size;
	void *ptr;
#ifndef SFM_REALLOC_REMALLOC
	struct sfm_frag *n;
	struct sfm_frag **pf;
	unsigned long diff;
	unsigned long p_id;
	int hash;
	unsigned long n_size;
	struct sfm_pool * pool;
#endif
	
#ifdef DBG_F_MALLOC
	MDBG("sfm_realloc(%p, %p, %lu) called from %s: %s(%d)\n", qm, p, size,
			file, func, line);
	if ((p)&&(p>(void*)qm->last_frag || p<(void*)qm->first_frag)){
		LOG(L_CRIT, "BUG: sfm_free: bad pointer %p (out of memory block!) - "
				"aborting\n", p);
		abort();
	}
#endif
	if (size==0) {
		if (p)
#ifdef DBG_F_MALLOC
			sfm_free(qm, p, file, func, line);
#else
			sfm_free(qm, p);
#endif
		return 0;
	}
	if (p==0)
#ifdef DBG_F_MALLOC
		return sfm_malloc(qm, size, file, func, line);
#else
		return sfm_malloc(qm, size);
#endif
	f=(struct sfm_frag*) ((char*)p-sizeof(struct sfm_frag));
#ifdef DBG_F_MALLOC
	MDBG("sfm_realloc: realloc'ing frag %p alloc'ed from %s: %s(%ld)\n",
			f, f->file, f->func, f->line);
#endif
	size=ROUNDUP(size);
	orig_size=f->size;
	if (f->size > size){
		/* shrink */
#ifdef DBG_F_MALLOC
		MDBG("sfm_realloc: shrinking from %lu to %lu\n", f->size, size);
		sfm_split_frag(qm, f, size, file, "frag. from sfm_realloc", line);
#else
		sfm_split_frag(qm, f, size);
#endif
	}else if (f->size<size){
		/* grow */
#ifdef DBG_F_MALLOC
		MDBG("sfm_realloc: growing from %lu to %lu\n", f->size, size);
#endif
#ifndef SFM_REALLOC_REMALLOC
		diff=size-f->size;
		n=FRAG_NEXT(f);
		if (((char*)n < (char*)qm->last_frag) && 
				(n->u.nxt_free)&&((n->size+FRAG_OVERHEAD)>=diff)){
			/* join  */
			/* detach n from the free list */
try_again:
			p_id=n->id;
			n_size=n->size;
			if ((unlikely(p_id >=SFM_POOLS_NO))){
				hash=GET_HASH(n_size);
				SFM_MAIN_HASH_LOCK(qm, hash);
				if (unlikely((n->u.nxt_free==0) ||
							((n->size+FRAG_OVERHEAD)<diff))){ 
					SFM_MAIN_HASH_UNLOCK(qm, hash);
					goto not_found;
				}
				if (unlikely((n->id!=p_id) || (n->size!=n_size))){
					/* fragment still free, but changed, either 
					 * moved to another pool or has a diff. size */
					SFM_MAIN_HASH_UNLOCK(qm, hash);
					goto try_again;
				}
				pf=&(qm->free_hash[hash].first);
				/* find it */
				for(;(*pf)&&(*pf!=n); pf=&((*pf)->u.nxt_free));/*FIXME slow */
				if (*pf==0){
					SFM_MAIN_HASH_UNLOCK(qm, hash);
					/* not found, bad! */
					LOG(L_WARN, "WARNING: sfm_realloc: could not find %p in "
							    "free " "list (hash=%d)\n", n, hash);
					/* somebody is in the process of changing it ? */
					goto not_found;
				}
				/* detach */
				*pf=n->u.nxt_free;
				n->u.nxt_free=0; /* mark it immediately as detached */
				qm->free_hash[hash].no--;
				SFM_MAIN_HASH_UNLOCK(qm, hash);
				/* join */
				f->size+=n->size+FRAG_OVERHEAD;
				/* split it if necessary */
				if (f->size > size){
			#ifdef DBG_F_MALLOC
					sfm_split_frag(qm, f, size, file, "fragm. from "
									"sfm_realloc", line);
			#else
					sfm_split_frag(qm, f, size);
			#endif
				}
			}else{ /* p_id < SFM_POOLS_NO (=> in a pool )*/
				hash=GET_SMALL_HASH(n_size);
				pool=&qm->pool[p_id];
				SFM_POOL_LOCK(pool, hash);
				if (unlikely((n->u.nxt_free==0) ||
							((n->size+FRAG_OVERHEAD)<diff))){
					SFM_POOL_UNLOCK(pool, hash);
					goto not_found;
				}
				if (unlikely((n->id!=p_id) || (n->size!=n_size))){
					/* fragment still free, but changed, either 
					 * moved to another pool or has a diff. size */
					SFM_POOL_UNLOCK(pool, hash);
					goto try_again;
				}
				pf=&(pool->pool_hash[hash].first);
				/* find it */
				for(;(*pf)&&(*pf!=n); pf=&((*pf)->u.nxt_free));/*FIXME slow */
				if (*pf==0){
					SFM_POOL_UNLOCK(pool, hash);
					/* not found, bad! */
					LOG(L_WARN, "WARNING: sfm_realloc: could not find %p in "
							    "free " "list (hash=%d)\n", n, hash);
					/* somebody is in the process of changing it ? */
					goto not_found;
				}
				/* detach */
				*pf=n->u.nxt_free;
				n->u.nxt_free=0; /* mark it immediately as detached */
				pool->pool_hash[hash].no--;
				SFM_POOL_UNLOCK(pool, hash);
				/* join */
				f->size+=n->size+FRAG_OVERHEAD;
				/* split it if necessary */
				if (f->size > size){
			#ifdef DBG_F_MALLOC
					sfm_split_frag(qm, f, size, file, "fragm. from "
									"sfm_realloc", line);
			#else
					sfm_split_frag(qm, f, size);
			#endif
				}
			}
		}else{
not_found:
			/* could not join => realloc */
#else/* SFM_REALLOC_REMALLOC */ 
		{
#endif /* SFM_REALLOC_REMALLOC */
	#ifdef DBG_F_MALLOC
			ptr=sfm_malloc(qm, size, file, func, line);
	#else
			ptr=sfm_malloc(qm, size);
	#endif
			if (ptr){
				/* copy, need by libssl */
				memcpy(ptr, p, orig_size);
	#ifdef DBG_F_MALLOC
				sfm_free(qm, p, file, func, line);
	#else
				sfm_free(qm, p);
	#endif
			}
			p=ptr;
		}
	}else{
		/* do nothing */
#ifdef DBG_F_MALLOC
		MDBG("sfm_realloc: doing nothing, same size: %lu - %lu\n", 
				f->size, size);
#endif
	}
#ifdef DBG_F_MALLOC
	MDBG("sfm_realloc: returning %p\n", p);
#endif
	return p;
}



void sfm_status(struct sfm_block* qm)
{
	struct sfm_frag* f;
	int i,j;
	int h;
	int unused;
	unsigned long size;
	int k;

	LOG(memlog, "sfm_status (%p):\n", qm);
	if (!qm) return;

	LOG(memlog, " heap size= %ld\n", qm->size);
	LOG(memlog, "dumping free list:\n");
	for(h=0,i=0,size=0;h<=sfm_max_hash;h++){
		SFM_MAIN_HASH_LOCK(qm, h);
		unused=0;
		for (f=qm->free_hash[h].first,j=0; f;
				size+=f->size,f=f->u.nxt_free,i++,j++){
			if (!FRAG_WAS_USED(f)){
				unused++;
#ifdef DBG_F_MALLOC
				LOG(memlog, "unused fragm.: hash = %3d, fragment %p,"
							" address %p size %lu, created from %s: %s(%ld)\n",
						    h, f, (char*)f+sizeof(struct sfm_frag), f->size,
							f->file, f->func, f->line);
#endif
			};
		}
		if (j) LOG(memlog, "hash = %3d fragments no.: %5d, unused: %5d\n\t\t"
							" bucket size: %9lu - %9lu (first %9lu)\n",
							h, j, unused, UN_HASH(h),
						((h<=SF_MALLOC_OPTIMIZE/SF_ROUNDTO)?1:2)* UN_HASH(h),
							qm->free_hash[h].first->size
				);
		if (j!=qm->free_hash[h].no){
			LOG(L_CRIT, "BUG: sfm_status: different free frag. count: %d!=%ld"
					" for hash %3d\n", j, qm->free_hash[h].no, h);
		}
		SFM_MAIN_HASH_UNLOCK(qm, h);
	}
	for (k=0; k<SFM_POOLS_NO; k++){
		for(h=0;h<SF_HASH_POOL_SIZE;h++){
			SFM_POOL_LOCK(&qm->pool[k], h);
			unused=0;
			for (f=qm->pool[k].pool_hash[h].first,j=0; f;
					size+=f->size,f=f->u.nxt_free,i++,j++){
				if (!FRAG_WAS_USED(f)){
					unused++;
#ifdef DBG_F_MALLOC
					LOG(memlog, "[%2d] unused fragm.: hash = %3d, fragment %p,"
								" address %p size %lu, created from %s: "
								"%s(%ld)\n", k
								h, f, (char*)f+sizeof(struct sfm_frag),
								f->size, f->file, f->func, f->line);
#endif
				};
			}
			if (j) LOG(memlog, "[%2d] hash = %3d fragments no.: %5d, unused: "
								"%5d\n\t\t bucket size: %9lu - %9lu "
								"(first %9lu)\n",
								k, h, j, unused, UN_HASH(h),
							((h<=SF_MALLOC_OPTIMIZE/SF_ROUNDTO)?1:2) *
								UN_HASH(h),
								qm->pool[k].pool_hash[h].first->size
					);
			if (j!=qm->pool[k].pool_hash[h].no){
				LOG(L_CRIT, "BUG: sfm_status: [%d] different free frag."
							" count: %d!=%ld for hash %3d\n",
							k, j, qm->pool[k].pool_hash[h].no, h);
			}
			SFM_POOL_UNLOCK(&qm->pool[k], h);
		}
	}
	LOG(memlog, "TOTAL: %6d free fragments = %6lu free bytes\n", i, size);
	LOG(memlog, "-----------------------------\n");
}



/* fills a malloc info structure with info about the block
 * if a parameter is not supported, it will be filled with 0 */
void sfm_info(struct sfm_block* qm, struct mem_info* info)
{
	int r, k;
	unsigned long total_frags;
	struct sfm_frag* f;
	
	memset(info,0, sizeof(*info));
	total_frags=0;
	info->total_size=qm->size;
	info->min_frag=SF_MIN_FRAG_SIZE;
	/* we'll have to compute it all */
	for (r=0; r<=SF_MALLOC_OPTIMIZE/SF_ROUNDTO; r++){
		info->free+=qm->free_hash[r].no*UN_HASH(r);
		total_frags+=qm->free_hash[r].no;
	}
	for(;r<=sfm_max_hash; r++){
		total_frags+=qm->free_hash[r].no;
		SFM_MAIN_HASH_LOCK(qm, r);
		for(f=qm->free_hash[r].first;f;f=f->u.nxt_free){
			info->free+=f->size;
		}
		SFM_MAIN_HASH_UNLOCK(qm, r);
	}
	for (k=0; k<SFM_POOLS_NO; k++){
		for (r=0; r<SF_HASH_POOL_SIZE; r++){
			info->free+=qm->pool[k].pool_hash[r].no*UN_HASH(r);
			total_frags+=qm->pool[k].pool_hash[r].no;
		}
	}
	info->real_used=info->total_size-info->free;
	info->used=info->real_used-total_frags*FRAG_OVERHEAD-INIT_OVERHEAD
				-FRAG_OVERHEAD;
	info->max_used=0; /* we don't really know */
	info->total_frags=total_frags;
}



/* returns how much free memory is available
 * on error (not compiled with bookkeeping code) returns (unsigned long)(-1) */
unsigned long sfm_available(struct sfm_block* qm)
{
	/* we don't know how much free memory we have and it's to expensive
	 * to compute it */
	return ((unsigned long)-1);
}

#endif