/*
 * Copyright (C) 2011, 2012, 2013 Citrix Systems
 *
 * 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.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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.
 */

#include "ns_turn_maps.h"

#include "ns_turn_ioalib.h"

#include "ns_turn_khash.h"

KHASH_MAP_INIT_INT64(3, ur_map_value_type)

#define MAGIC_HASH ((u64bits)(0x90ABCDEFL))

struct _ur_map {
  khash_t(3) *h;
  u64bits magic;
  TURN_MUTEX_DECLARE(mutex)
};

static int ur_map_init(ur_map* map) {
  if(map) {
    map->h=kh_init(3);
    if(map->h) {
      map->magic=MAGIC_HASH;
      TURN_MUTEX_INIT_RECURSIVE(&(map->mutex));
      return 0;
    }
  }
  return -1;
}

static int ur_map_valid(const ur_map *map) {
  return (map && map->h && map->magic==MAGIC_HASH);
}

ur_map* ur_map_create() {
  ur_map *map=(ur_map*)turn_malloc(sizeof(ur_map));
  if(ur_map_init(map)<0) {
    turn_free(map,sizeof(ur_map));
    return NULL;
  }
  return map;
}

/**
 * @ret:
 * 0 - success
 * -1 - error
 */
int ur_map_put(ur_map* map, ur_map_key_type key, ur_map_value_type value) {
  if(!ur_map_valid(map)) return -1;
  else {

    int ret=0;
    khiter_t k;

    k = kh_get(3, map->h, key);
    if(k != kh_end(map->h)) {
      kh_del(3, map->h, k);
    }
    
    k = kh_put(3,map->h,key,&ret);

    if (!ret) {
      kh_del(3, map->h, k);
      return -1;
    }

    kh_value(map->h, k) = value;

    return 0;
  }
}

/**
 * @ret:
 * 1 - success
 * 0 - not found
 */
int ur_map_get(const ur_map* map, ur_map_key_type key, ur_map_value_type *value) {
  if(!ur_map_valid(map)) return 0;
  else {

    khiter_t k;

    k = kh_get(3, map->h, key);
    if((k != kh_end(map->h)) && kh_exist(map->h,k)) {
      if(value) *value=kh_value(map->h,k);
      return 1;
    }

    return 0;
  }
}

/**
 * @ret:
 * 1 - success
 * 0 - not found
 */
int ur_map_del(ur_map* map, ur_map_key_type key,ur_map_del_func delfunc) {
  if(!ur_map_valid(map)) return 0;
  else {

    khiter_t k;

    k = kh_get(3, map->h, key);
    if((k != kh_end(map->h)) && kh_exist(map->h,k)) {
      if(delfunc) {
	delfunc(kh_value(map->h,k));
      }
      kh_del(3,map->h,k);
      return 1;
    }

    return 0;
  }
}

/**
 * @ret:
 * 1 - success
 * 0 - not found
 */
int ur_map_exist(const ur_map* map, ur_map_key_type key) {
  if(!ur_map_valid(map)) return 0;
  else {

    khiter_t k;

    k = kh_get(3, map->h, key);
    if((k != kh_end(map->h)) && kh_exist(map->h,k)) {
      return 1;
    }

    return 0;
  }
}

void ur_map_free(ur_map** map) {
  if(map && ur_map_valid(*map)) {
    kh_destroy(3,(*map)->h);
    (*map)->h=NULL;
    (*map)->magic=0;
    TURN_MUTEX_DESTROY(&((*map)->mutex));
    turn_free(*map,sizeof(ur_map));
    *map=NULL;
  }
}

size_t ur_map_size(const ur_map* map) {
  if(ur_map_valid(map)) {
    return kh_size(map->h);
  } else {
    return 0;
  }
}

int ur_map_foreach(ur_map* map, foreachcb_type func) {
  if(map && func && ur_map_valid(map)) {
    khiter_t k;
    for (k = kh_begin((*map)->h); k != kh_end(map->h); ++k) {
      if (kh_exist(map->h, k)) {
	if(func((ur_map_key_type)(kh_key(map->h, k)),
		(ur_map_value_type)(kh_value(map->h, k)))) {
	  return 1;
	}
      }
    }
  }
  return 0;
}

int ur_map_foreach_arg(ur_map* map, foreachcb_arg_type func, void* arg) {
  if(map && func && ur_map_valid(map)) {
    khiter_t k;
    for (k = kh_begin((*map)->h); k != kh_end(map->h); ++k) {
      if (kh_exist(map->h, k)) {
	if(func((ur_map_key_type)(kh_key(map->h, k)),
		(ur_map_value_type)(kh_value(map->h, k)),
		arg)
	   ) {
	  return 1;
	}
      }
    }
  }
  return 0;
}

int ur_map_lock(const ur_map* map) {
  if(ur_map_valid(map)) {
    TURN_MUTEX_LOCK((const turn_mutex*)&(map->mutex));
    return 0;
  }
  return -1;
}

int ur_map_unlock(const ur_map* map) {
  if(ur_map_valid(map)) {
    TURN_MUTEX_UNLOCK((const turn_mutex*)&(map->mutex));
    return 0;
  }
  return -1;
}

//////////////////// LOCAL MAPS ////////////////////////////////////

void lm_map_init(lm_map *map)
{
	if(map) {
		ns_bzero(map,sizeof(lm_map));
	}
}

/**
 * @ret:
 * 0 - success
 * -1 - error
 */

int lm_map_put(lm_map* map, ur_map_key_type key, ur_map_value_type value)
{
	int ret = -1;
	if(map && key && value) {

		size_t index = (size_t)(key & (LM_MAP_HASH_SIZE - 1));
		lm_map_array *a = &(map->table[index]);

		size_t i;

		for(i=0;i<LM_MAP_ARRAY_SIZE;++i) {

			ur_map_key_type key0 = a->main_keys[i];
			ur_map_value_type value0 = a->main_values[i];

			if(key0 == key) {
				if(value0 == value) {
					return 0;
				} else {
					return -1;
				}
			}

			if(!key0 || !value0) {
				a->main_keys[i] = key;
				a->main_values[i] = value;
				return 0;
			}
		}

		size_t esz = a->extra_sz;
		if(esz && a->extra_keys && a->extra_values) {
			for(i=0;i<esz;++i) {
				ur_map_key_type *keyp = a->extra_keys[i];
				ur_map_value_type *valuep = a->extra_values[i];
				if(keyp && valuep) {
					if(!(*keyp) || !(*valuep)) {
						*keyp = key;
						*valuep = value;
						return 0;
					}
				} else {
					if(!(*keyp)) {
						a->extra_keys[i] = (ur_map_key_type*)turn_malloc(sizeof(ur_map_key_type));
						keyp = a->extra_keys[i];
					}
					if(!(*valuep)) {
						a->extra_values[i] = (ur_map_value_type*)turn_malloc(sizeof(ur_map_value_type));
						valuep = a->extra_values[i];
					}
					*keyp = key;
					*valuep = value;
					return 0;
				}
			}
		}

		size_t old_sz = esz;
		size_t old_sz_mem = esz * sizeof(ur_map_key_type*);
		a->extra_keys = (ur_map_key_type**)turn_realloc(a->extra_keys,old_sz_mem,old_sz_mem + sizeof(ur_map_key_type*));
		a->extra_keys[old_sz] = (ur_map_key_type*)turn_malloc(sizeof(ur_map_key_type));
		*(a->extra_keys[old_sz]) = key;

		old_sz_mem = esz * sizeof(ur_map_value_type*);
		a->extra_values = (ur_map_value_type**)turn_realloc(a->extra_values,old_sz_mem,old_sz_mem + sizeof(ur_map_value_type*));
		a->extra_values[old_sz] = (ur_map_value_type*)turn_malloc(sizeof(ur_map_value_type));
		*(a->extra_values[old_sz]) = value;

		a->extra_sz += 1;

		return 0;
	}
	return ret;
}

/**
 * @ret:
 * 1 - success
 * 0 - not found
 */

int lm_map_get(const lm_map* map, ur_map_key_type key, ur_map_value_type *value)
{
	int ret = 0;
	if(map && key) {
		size_t index = (size_t)(key & (LM_MAP_HASH_SIZE - 1));
		const lm_map_array *a = &(map->table[index]);

		size_t i;

		for(i=0;i<LM_MAP_ARRAY_SIZE;++i) {

			ur_map_key_type key0 = a->main_keys[i];
			if((key0 == key) && a->main_values[i]) {
				if(value) {
					*value = a->main_values[i];
				}
				return 1;
			}
		}

		size_t esz = a->extra_sz;
		if(esz && a->extra_keys && a->extra_values) {
			for(i=0;i<esz;++i) {
				ur_map_key_type *keyp = a->extra_keys[i];
				ur_map_value_type *valuep = a->extra_values[i];
				if(keyp && valuep) {
					if(*keyp == key) {
						if(value)
							*value = *valuep;
						return 1;
					}
				}
			}
		}
	}

	return ret;
}
/**
 * @ret:
 * 1 - success
 * 0 - not found
 */

int lm_map_del(lm_map* map, ur_map_key_type key,ur_map_del_func delfunc)
{
	int ret = 0;

	if(map && key) {
		size_t index = (size_t)(key & (LM_MAP_HASH_SIZE - 1));
		lm_map_array *a = &(map->table[index]);

		size_t i;

		for(i=0;i<LM_MAP_ARRAY_SIZE;++i) {

			ur_map_key_type key0 = a->main_keys[i];

			if((key0 == key) && a->main_values[i]) {
				if(delfunc) {
					delfunc(a->main_values[i]);
				}
				a->main_keys[i] = 0;
				a->main_values[i] = 0;
				return 1;
			}
		}

		size_t esz = a->extra_sz;
		if(esz && a->extra_keys && a->extra_values) {
			for(i=0;i<esz;++i) {
				ur_map_key_type *keyp = a->extra_keys[i];
				ur_map_value_type *valuep = a->extra_values[i];
				if(keyp && valuep) {
					if(*keyp == key) {
						if(delfunc)
							delfunc(*valuep);
						*keyp = 0;
						*valuep = 0;
						return 1;
					}
				}
			}
		}
	}

	return ret;
}
/**
 * @ret:
 * 1 - success
 * 0 - not found
 */

int lm_map_exist(const lm_map* map, ur_map_key_type key)
{
	return lm_map_get(map, key, NULL);
}

void lm_map_clean(lm_map* map)
{
	size_t j;
	for(j=0;j<LM_MAP_HASH_SIZE;++j) {

		lm_map_array *a = &(map->table[j]);

		size_t esz = a->extra_sz;
		if(esz) {
			size_t i;
			if(a->extra_keys) {
				for(i=0;i<esz;++i) {
					ur_map_key_type *keyp = a->extra_keys[i];
					if(keyp) {
						*keyp = 0;
						turn_free(keyp,sizeof(ur_map_key_type));
					}
				}
				turn_free(a->extra_keys,esz * sizeof(ur_map_key_type));
				a->extra_keys = NULL;
			}
			if(a->extra_values) {
				for(i=0;i<esz;++i) {
					ur_map_value_type *valuep = a->extra_values[i];
					if(valuep) {
						*valuep = 0;
						turn_free(valuep,sizeof(ur_map_value_type));
					}
				}
				turn_free(a->extra_values,esz * sizeof(ur_map_value_type));
				a->extra_values = NULL;
			}
		}
	}

	lm_map_init(map);
}

size_t lm_map_size(const lm_map* map)
{
	size_t ret = 0;

	if(map) {

		size_t i;

		for(i=0;i<LM_MAP_HASH_SIZE;++i) {

			const lm_map_array *a = &(map->table[i]);

			size_t j;

			for(j=0;j<LM_MAP_ARRAY_SIZE;++j) {
				if(a->main_keys[j] && a->main_values[j]) {
					++ret;
				}
			}

			size_t esz = a->extra_sz;
			if(esz && a->extra_values && a->extra_keys) {
				for(j=0;j<esz;++j) {
					if(*(a->extra_keys[j]) && *(a->extra_values[j])) {
						++ret;
					}
				}
			}
		}
	}

	return ret;
}

int lm_map_foreach(lm_map* map, foreachcb_type func)
{
	size_t ret = 0;

	if(map) {

		size_t i;

		for(i=0;i<LM_MAP_HASH_SIZE;++i) {

			lm_map_array *a = &(map->table[i]);

			size_t j;

			for(j=0;j<LM_MAP_ARRAY_SIZE;++j) {
				if(a->main_keys[j] && a->main_values[j]) {
					if(func((ur_map_key_type)a->main_keys[j],
						(ur_map_value_type)a->main_values[j])) {
						return 1;
					}
				}
			}

			size_t esz = a->extra_sz;
			if(esz && a->extra_values && a->extra_keys) {
				for(j=0;j<esz;++j) {
					if(*(a->extra_keys[j]) && *(a->extra_values[j])) {
						if(func((ur_map_key_type)*(a->extra_keys[j]),
							(ur_map_value_type)*(a->extra_values[j]))) {
							return 1;
						}
					}
				}
			}
		}
	}

	return ret;
}

int lm_map_foreach_arg(lm_map* map, foreachcb_arg_type func, void* arg)
{
	size_t ret = 0;

	if(map) {

		size_t i;

		for(i=0;i<LM_MAP_HASH_SIZE;++i) {

			lm_map_array *a = &(map->table[i]);

			size_t j;

			for(j=0;j<LM_MAP_ARRAY_SIZE;++j) {
				if(a->main_keys[j] && a->main_values[j]) {
					if(func((ur_map_key_type)a->main_keys[j],
						(ur_map_value_type)a->main_values[j],
						arg)) {
						return 1;
					}
				}
			}

			size_t esz = a->extra_sz;
			if(esz && a->extra_values && a->extra_keys) {
				for(j=0;j<esz;++j) {
					if(*(a->extra_keys[j]) && *(a->extra_values[j])) {
						if(func((ur_map_key_type)*(a->extra_keys[j]),
							(ur_map_value_type)*(a->extra_values[j]),
							arg)) {
							return 1;
						}
					}
				}
			}
		}
	}

	return ret;
}

////////////////////  ADDR LISTS ///////////////////////////////////

static void addr_list_free(addr_list_header* slh) {
  if(slh) {
    if(slh->extra_list) {
      turn_free(slh->extra_list,sizeof(addr_elem)*(slh->extra_sz));
    }
    ns_bzero(slh,sizeof(addr_list_header));
  }
}
    
static void addr_list_add(addr_list_header* slh, const ioa_addr* key,  ur_addr_map_value_type value) {

  if(!key || !value) return;

  addr_elem *elem = NULL;
  size_t i;

  for(i=0;i<ADDR_ARRAY_SIZE;++i) {
	  if(!(slh->main_list[i].value)) {
		  elem = &(slh->main_list[i]);
		  break;
	  }
  }

  if(!elem && slh->extra_list) {
	  for(i=0;i<slh->extra_sz;++i) {
		  if(!(slh->extra_list[i].value)) {
			  elem = &(slh->extra_list[i]);
			  break;
		  }
	  }
  }

  if(!elem) {
	  size_t old_sz = slh->extra_sz;
	  size_t old_sz_mem = old_sz * sizeof(addr_elem);
	  slh->extra_list = (addr_elem*)turn_realloc(slh->extra_list, old_sz_mem, old_sz_mem + sizeof(addr_elem));
	  elem = &(slh->extra_list[old_sz]);
	  slh->extra_sz += 1;
  }

  addr_cpy(&(elem->key),key);
  elem->value=value;
}

static void addr_list_remove(addr_list_header* slh, const ioa_addr* key,
				   ur_addr_map_func delfunc, int *counter) {
  if(!slh || !key) return;

  if(counter)
	  *counter = 0;

  size_t i;

  for(i=0;i<ADDR_ARRAY_SIZE;++i) {
	addr_elem *elem=&(slh->main_list[i]);
	if(elem->value) {
		if(addr_eq(&(elem->key),key)) {
			if(delfunc && elem->value)
				delfunc(elem->value);
			elem->value = 0;
			if(counter) {
			  *counter += 1;
			}
		}
	}
  }

  if(slh->extra_list) {
  	  for(i=0;i<slh->extra_sz;++i) {
  		  addr_elem *elem=&(slh->extra_list[i]);
  		  if(elem->value) {
  			  if(addr_eq(&(elem->key),key)) {
  				  if(delfunc && elem->value)
  					  delfunc(elem->value);
  				  elem->value = 0;
  				  if(counter) {
  					  *counter += 1;
  				  }
  			  }
  		  }
  	  }
  }
}

static void addr_list_foreach(addr_list_header* slh,  ur_addr_map_func func) {
  if(slh && func) {

    	  size_t i;

    	  for(i=0;i<ADDR_ARRAY_SIZE;++i) {
    		  addr_elem *elem=&(slh->main_list[i]);
    		  if(elem->value) {
    			  func(elem->value);
    		  }
    	  }

	  if(slh->extra_list) {
	    	  for(i=0;i<slh->extra_sz;++i) {
	    		  addr_elem *elem=&(slh->extra_list[i]);
	    		  if(elem->value) {
	    			func(elem->value);
	    		  }
	    	  }
	    }
  }
}

static addr_elem* addr_list_get(addr_list_header* slh, const ioa_addr* key) {

  if(!slh || !key) return NULL;

  size_t i;

  for(i=0;i<ADDR_ARRAY_SIZE;++i) {
	  addr_elem *elem=&(slh->main_list[i]);
	  if(elem->value) {
		  if(addr_eq(&(elem->key),key)) {
			  return elem;
		  }
	  }
  }

  if(slh->extra_list) {
    	  for(i=0;i<slh->extra_sz;++i) {
    		  addr_elem *elem=&(slh->extra_list[i]);
    		  if(elem->value) {
    			  if(addr_eq(&(elem->key),key)) {
    				  return elem;
    			  }
    		  }
    	  }
  }

  return NULL;
}

static const addr_elem* addr_list_get_const(const addr_list_header* slh, const ioa_addr* key) {

  if(!slh || !key) return NULL;

  size_t i;

  for(i=0;i<ADDR_ARRAY_SIZE;++i) {
	  const addr_elem *elem=&(slh->main_list[i]);
	  if(elem->value) {
		  if(addr_eq(&(elem->key),key)) {
			  return elem;
		  }
	  }
  }

  if(slh->extra_list) {
    	  for(i=0;i<slh->extra_sz;++i) {
    		  const addr_elem *elem=&(slh->extra_list[i]);
    		  if(elem->value) {
    			  if(addr_eq(&(elem->key),key)) {
    				  return elem;
    			  }
    		  }
    	  }
  }

  return NULL;
}

////////// ADDR MAPS ////////////////////////////////////////////

static int addr_map_index(ioa_addr* key) {
  u32bits hash = addr_hash(key);
  hash = hash & (ADDR_MAP_SIZE - 1);
  return (int)hash;
}

static addr_list_header* get_addr_list_header(ur_addr_map *map, ioa_addr* key) {
  return &(map->lists[addr_map_index(key)]);
}

static const addr_list_header* get_addr_list_header_const(const ur_addr_map *map, ioa_addr* key) {
  return &(map->lists[addr_map_index(key)]);
}

static int ur_addr_map_valid(const ur_addr_map *map) {
  return (map && map->magic==MAGIC_HASH);
}

void ur_addr_map_init(ur_addr_map* map) {
  if(map) {
    ns_bzero(map,sizeof(ur_addr_map));
    map->magic=MAGIC_HASH;
  }
}

void ur_addr_map_clean(ur_addr_map* map) {
  if(map && ur_addr_map_valid(map)) {
    u32bits i=0;
    for(i=0;i<ADDR_MAP_SIZE;i++) {
      addr_list_free(&(map->lists[i]));
    }
    ns_bzero(map,sizeof(ur_addr_map));
  }
}

/**
 * @ret:
 * 0 - success
 * -1 - error
 * if the addr key exists, the value is updated.
 */
int ur_addr_map_put(ur_addr_map* map, ioa_addr* key, ur_addr_map_value_type value) {

  if(!ur_addr_map_valid(map)) return -1;

  else {

    addr_list_header* slh = get_addr_list_header(map, key);

    addr_elem* elem = addr_list_get(slh, key);
    if(elem) {
      elem->value=value;
    } else {
      addr_list_add(slh,key,value);
    }

    return 0;
  }
}

/**
 * @ret:
 * 1 - success
 * 0 - not found
 */
int ur_addr_map_get(const ur_addr_map* map, ioa_addr* key, ur_addr_map_value_type *value) {

  if(!ur_addr_map_valid(map)) return 0;

  else {

    const addr_list_header* slh = get_addr_list_header_const(map, key);

    const addr_elem *elem = addr_list_get_const(slh, key);
    if(elem) {
      if(value) *value=elem->value;
      return 1;
    }

    return 0;
  }
}

/**
 * @ret:
 * 1 - success
 * 0 - not found
 */
int ur_addr_map_del(ur_addr_map* map, ioa_addr* key,ur_addr_map_func delfunc) {

  if(!ur_addr_map_valid(map)) return 0;

  else {

    addr_list_header* slh = get_addr_list_header(map, key);

    int counter=0;

    addr_list_remove(slh, key, delfunc, &counter);

    return (counter>0);
  }
}

/**
 * @ret:
 * 1 - success
 * 0 - not found
 */
void ur_addr_map_foreach(ur_addr_map* map, ur_addr_map_func func) {

  if(ur_addr_map_valid(map)) {

    u32bits i=0;
    for(i=0;i<ADDR_MAP_SIZE;i++) {
      
      addr_list_header* slh = &(map->lists[i]);
      
      addr_list_foreach(slh, func);
    }
  }
}

////////////////////  STRING LISTS ///////////////////////////////////

typedef struct _string_list {
  struct _string_list* next;
} string_list;

typedef struct _string_elem {
  string_list list;
  ur_string_map_key_type key;
  u32bits key_size;
  ur_string_map_value_type value;
} string_elem;

typedef struct _string_list_header {
  string_list *list;
} string_list_header;

static size_t string_list_size(const string_list *sl) {
  if(!sl) return 0;
  return 1+string_list_size(sl->next);
}

static void string_list_free(string_list_header* slh, ur_string_map_func del_value_func) {
  if(slh) {
    string_list* list=slh->list;
    while(list) {
      string_elem *elem=(string_elem*)list;
      string_list* tail=elem->list.next;
      if(elem->key) turn_free(elem->key,elem->key_size);
      if(del_value_func && elem->value)
	      del_value_func(elem->value);
      turn_free(elem,sizeof(string_elem));
      list=tail;
    }
    slh->list=NULL;
  }
}

static string_list* string_list_add(string_list* sl, const ur_string_map_key_type key, ur_string_map_value_type value) {
  if(!key) return sl;
  string_elem *elem=(string_elem*)turn_malloc(sizeof(string_elem));
  elem->list.next=sl;
  elem->key_size = strlen(key)+1;
  elem->key=(s08bits*)turn_malloc(elem->key_size);
  ns_bcopy(key,elem->key,elem->key_size);
  elem->value=value;
  return &(elem->list);
}

static string_list* string_list_remove(string_list* sl, const ur_string_map_key_type key,
					ur_string_map_func del_value_func, int *counter) {
  if(!sl || !key) return sl;
  string_elem *elem=(string_elem*)sl;
  string_list* tail=elem->list.next;
  if(strcmp(elem->key,key)==0) {
    turn_free(elem->key,elem->key_size);
    if(del_value_func)
	    del_value_func(elem->value);
    turn_free(elem,sizeof(string_elem));
    if(counter) *counter+=1;
    sl=string_list_remove(tail, key, del_value_func, counter);
  } else {
    elem->list.next=string_list_remove(tail,key,del_value_func,counter);
  }
  return sl;
}

static string_elem* string_list_get(string_list* sl, const ur_string_map_key_type key) {

  if(!sl || !key) return NULL;

  string_elem *elem=(string_elem*)sl;
  if(strcmp(elem->key,key)==0) {
    return elem;
  } else {
    return string_list_get(elem->list.next, key);
  }
}

////////// STRING MAPS ////////////////////////////////////////////

#define STRING_MAP_SIZE (1024)

struct _ur_string_map {
  string_list_header lists[STRING_MAP_SIZE];
  u64bits magic;
  ur_string_map_func del_value_func;
  TURN_MUTEX_DECLARE(mutex)
};

static unsigned long string_hash(const ur_string_map_key_type key) {

  u08bits *str=(u08bits*)key;

  unsigned long hash = 0;
  int c = 0;

  while ((c = *str++))
    hash = c + (hash << 6) + (hash << 16) - hash;

  return hash;
}

static int string_map_index(const ur_string_map_key_type key) {
  return (int)(string_hash(key) % STRING_MAP_SIZE);
}

static string_list_header* get_string_list_header(ur_string_map *map, const ur_string_map_key_type key) {
  return &(map->lists[string_map_index(key)]);
}

static int ur_string_map_init(ur_string_map* map) {
  if(map) {
    ns_bzero(map,sizeof(ur_string_map));
    map->magic=MAGIC_HASH;

    TURN_MUTEX_INIT_RECURSIVE(&(map->mutex));

    return 0;
  }
  return -1;
}

static int ur_string_map_valid(const ur_string_map *map) {
  return (map && map->magic==MAGIC_HASH);
}

ur_string_map* ur_string_map_create(ur_string_map_func del_value_func) {
  ur_string_map *map=(ur_string_map*)turn_malloc(sizeof(ur_string_map));
  if(ur_string_map_init(map)<0) {
    turn_free(map,sizeof(ur_string_map));
    return NULL;
  }
  map->del_value_func = del_value_func;
  return map;
}

/**
 * @ret:
 * 0 - success
 * -1 - error
 * if the string key exists, and the value is different, return error.
 */
int ur_string_map_put(ur_string_map* map, const ur_string_map_key_type key, ur_string_map_value_type value) {

  if(!ur_string_map_valid(map)) return -1;

  else {

    string_list_header* slh = get_string_list_header(map, key);

    string_elem *elem = string_list_get(slh->list, key);
    if(elem) {
      if(elem->value != value) {
	      if(map->del_value_func)
		      map->del_value_func(elem->value);
	      elem->value = value;
      }
      return 0;
    }

    slh->list=string_list_add(slh->list,key,value);

    return 0;
  }
}

/**
 * @ret:
 * 1 - success
 * 0 - not found
 */
int ur_string_map_get(ur_string_map* map, const ur_string_map_key_type key, ur_string_map_value_type *value) {

  if(!ur_string_map_valid(map)) return 0;

  else {

    string_list_header* slh = get_string_list_header(map, key);
    string_elem *elem = string_list_get(slh->list, key);
    if(elem) {
      if(value) *value=elem->value;
      return 1;
    } else {
      return 0;
    }
  }
}

/**
 * @ret:
 * 1 - success
 * 0 - not found
 */
int ur_string_map_del(ur_string_map* map, const ur_string_map_key_type key) {

  if(!ur_string_map_valid(map)) return 0;

  else {

    string_list_header* slh = get_string_list_header(map, key);

    int counter=0;

    slh->list=string_list_remove(slh->list, key, map->del_value_func, &counter);

    return (counter>0);
  }
}

void ur_string_map_clean(ur_string_map* map) {
	if (ur_string_map_valid(map)) {
		int i = 0;
		for (i = 0; i < STRING_MAP_SIZE; i++) {
			string_list_free(&(map->lists[i]), map->del_value_func);
		}
	}
}

void ur_string_map_free(ur_string_map** map) {
  if(map && ur_string_map_valid(*map)) {
    int i=0;
    for(i=0;i<STRING_MAP_SIZE;i++) {
      string_list_free(&((*map)->lists[i]),(*map)->del_value_func);
    }
    (*map)->magic=0;
    TURN_MUTEX_DESTROY(&((*map)->mutex));
    turn_free(*map,sizeof(ur_string_map));
    *map=NULL;
  }
}

size_t ur_string_map_size(const ur_string_map* map) {
  if(ur_string_map_valid(map)) {
    size_t ret=0;
    int i=0;
    for(i=0;i<STRING_MAP_SIZE;i++) {
      ret+=string_list_size(map->lists[i].list);
    }
    return ret;
  } else {
    return 0;
  }
}

int ur_string_map_lock(const ur_string_map* map) {
  if(ur_string_map_valid(map)) {
    TURN_MUTEX_LOCK((const turn_mutex*)&(map->mutex));
    return 0;
  }
  return -1;
}

int ur_string_map_unlock(const ur_string_map* map) {
  if(ur_string_map_valid(map)) {
    TURN_MUTEX_UNLOCK((const turn_mutex*)&(map->mutex));
    return 0;
  }
  return -1;
}

////////////////////////////////////////////////////////////////
