/* -*- linux-c -*- 
 * Map Functions
 * Copyright (C) 2005-2009 Red Hat Inc.
 *
 * This file is part of systemtap, and is free software.  You can
 * redistribute it and/or modify it under the terms of the GNU General
 * Public License (GPL); either version 2, or (at your option) any
 * later version.
 */

#ifndef _MAP_C_
#define _MAP_C_

/** @file map.c
 * @brief Implements maps (associative arrays) and lists
 */

#include "sym.c"
#include "stat-common.c"
#include "map-stat.c"

static int map_sizes[] = {
        sizeof(int64_t),
        MAP_STRING_LENGTH,
        sizeof(stat),
        0
};

static unsigned int int64_hash (const int64_t v)
{
	return (unsigned int)hash_long (((unsigned long)v) ^ stap_hash_seed, HASH_TABLE_BITS);
}

static int int64_eq_p (int64_t key1, int64_t key2)
{
	return key1 == key2;
}

static void str_copy(char *dest, char *src)
{
	if (src)
		strlcpy(dest, src, MAP_STRING_LENGTH);
	else
		*dest = 0;
}

static void str_add(void *dest, char *val)
{
	char *dst = (char *)dest;
	strlcat(dst, val, MAP_STRING_LENGTH);
}

static int str_eq_p (char *key1, char *key2)
{
	return strncmp(key1, key2, MAP_STRING_LENGTH - 1) == 0;
}

static unsigned long partial_str_hash(unsigned long c, unsigned long prevhash)
{
	return (prevhash + (c << 4) + (c >> 4)) * 11;
}

static unsigned int str_hash(const char *key1)
{
	unsigned long hash = 0;
	int count = 0;
	char *v1 = (char *)key1;
	while (*v1 && count++ < MAP_STRING_LENGTH)
		hash = partial_str_hash(*v1++, hash);
	return (unsigned int)hash_long(hash^stap_hash_seed, HASH_TABLE_BITS);
}

/** @addtogroup maps 
 * Implements maps (associative arrays) and lists
 * @{ 
 */

/** Return an int64 from a map node.
 * This function will return the int64 value of a map_node
 * from a map containing int64s. You can get the map_nodes in a map
 * with _stp_map_start(), _stp_map_iter() and foreach().
 * @param m pointer to the map_node. 
 * @returns an int64 value.
 */
static int64_t _stp_get_int64(struct map_node *m)
{
	if (!m || m->map->type != INT64)
		return 0;
	return *(int64_t *)((long)m + m->map->data_offset);
}

/** Return a string from a map node.
 * This function will return the string value of a map_node
 * from a map containing strings. You can get the map_nodes in a map
 * with _stp_map_start(), _stp_map_iter() and foreach().
 * @param m pointer to the map_node.
 * @returns a pointer to a string. 
 */
static char *_stp_get_str(struct map_node *m)
{
	if (!m || m->map->type != STRING)
		return "bad type";
	return (char *)((long)m + m->map->data_offset);
}

/** Return a stat pointer from a map node.
 * This function will return the stats of a map_node
 * from a map containing stats. You can get the map_nodes in a map
 * with _stp_map_start(), _stp_map_iter() and foreach().
 * @param m pointer to the map_node.
 * @returns A pointer to the stats.  
 */
static stat *_stp_get_stat(struct map_node *m)
{
	if (!m || m->map->type != STAT)
		return 0;
	return (stat *)((long)m + m->map->data_offset);
}

/** Return an int64 key from a map node.
 * This function will return an int64 key from a map_node.
 * @param mn pointer to the map_node.
 * @param n key number
 * @returns an int64
 * @sa key1int(), key2int()
 */
static int64_t _stp_key_get_int64 (struct map_node *mn, int n)
{
	int type;
	int64_t res = 0;

	if (mn) {
		res = (*mn->map->get_key)(mn, n, &type).val;
		if (type != INT64)
			res = 0;
	}
	return res;
}

/** Return a string key from a map node.
 * This function will return an string key from a map_node.
 * @param mn pointer to the map_node.
 * @param n key number
 * @returns a pointer to a string
 * @sa key1str(), key2str()
 */
static char *_stp_key_get_str (struct map_node *mn, int n)
{
	int type;
	char *str = "";

	if (mn) {
		str = (*mn->map->get_key)(mn, n, &type).strp;
		if (type != STRING)
			str = "bad type";
	}
	return str;
}



/** Create a new map.
 * Maps must be created at module initialization time.
 * @param max_entries The maximum number of entries allowed. Currently that number will
 * be preallocated.  If more entries are required, the oldest ones will be deleted. This makes
 * it effectively a circular buffer.  If max_entries is 0, there will be no maximum and entries
 * will be allocated dynamically.
 * @param type Type of values stored in this map. 
 * @return A MAP on success or NULL on failure.
 * @ingroup map_create
 */

static int _stp_map_init(MAP m, unsigned max_entries, int type, int key_size, int data_size, int cpu)
{
	int size;
	size_t hash_size = sizeof(struct hlist_head) * HASH_TABLE_SIZE;
	if(cpu < 0)
		m->hashes = (struct hlist_head *) _stp_kmalloc_gfp(hash_size, STP_ALLOC_SLEEP_FLAGS);
	else
		m->hashes = (struct hlist_head *) _stp_kmalloc_node_gfp(hash_size, cpu_to_node(cpu), STP_ALLOC_SLEEP_FLAGS);
	if(m->hashes == NULL) {
		_stp_error("error allocating hash\n");
		return -1;
	}
	memset(m->hashes, 0, hash_size);
	m->maxnum = max_entries;
	m->type = type;
	if (type >= END) {
		_stp_error("unknown map type %d\n", type);
		return -1;
	}
	if (max_entries) {
		unsigned i;
		void *tmp;
		
		/* size is the size of the map_node. */
		/* add space for the value. */
		key_size = ALIGN(key_size,4);
		m->data_offset = key_size;
		if (data_size == 0)
			data_size = map_sizes[type];
		data_size = ALIGN(data_size,4);
		size = key_size + data_size;
		
		
		for (i = 0; i < max_entries; i++) {
			/* Called at module init time, so user context.  */
			if (cpu < 0)
				tmp = _stp_kmalloc_gfp(size, STP_ALLOC_SLEEP_FLAGS);
			else
				tmp = _stp_kmalloc_node_gfp(size, cpu_to_node(cpu), STP_ALLOC_SLEEP_FLAGS);
		
			if (!tmp)
				return -1;
			
//			dbug ("allocated %lx\n", (long)tmp);
			list_add((struct list_head *)tmp, &m->pool);
			((struct map_node *)tmp)->map = m;
		}
	}
	if (type == STAT)
		m->hist.type = HIST_NONE;
	return 0;
}


static MAP _stp_map_new(unsigned max_entries, int type, int key_size, int data_size)
{
	/* Called from module_init, so user context, may sleep alloc. */
	MAP m = (MAP) _stp_kzalloc_gfp(sizeof(struct map_root), STP_ALLOC_SLEEP_FLAGS);
	if (m == NULL)
		return NULL;

	INIT_LIST_HEAD(&m->pool);
	INIT_LIST_HEAD(&m->head);
	if (_stp_map_init(m, max_entries, type, key_size, data_size, -1)) {
		_stp_map_del(m);
		return NULL;
	}
	return m;
}

static PMAP _stp_pmap_new(unsigned max_entries, int type, int key_size, int data_size)
{
	int i;
	MAP map, m;

	/* Called from module_init, so user context, may sleep alloc. */
	PMAP pmap = (PMAP) _stp_kzalloc_gfp(sizeof(struct pmap), STP_ALLOC_SLEEP_FLAGS);
	if (pmap == NULL)
		return NULL;

	pmap->map = map = (MAP) _stp_alloc_percpu (sizeof(struct map_root));
	if (map == NULL) 
		goto err;

	/* initialize the memory lists first so if allocations fail */
        /* at some point, it is easy to clean up. */
	stp_for_each_cpu(i) {
		m = per_cpu_ptr (map, i);
		INIT_LIST_HEAD(&m->pool);
		INIT_LIST_HEAD(&m->head);
	}
	INIT_LIST_HEAD(&pmap->agg.pool);
	INIT_LIST_HEAD(&pmap->agg.head);

	stp_for_each_cpu(i) {
		m = per_cpu_ptr (map, i);
		if (_stp_map_init(m, max_entries, type, key_size, data_size, i)) {
			goto err1;
		}
	}

	if (_stp_map_init(&pmap->agg, max_entries, type, key_size, data_size, -1))
		goto err1;
	
	return pmap;

err1:
	stp_for_each_cpu(i) {
		m = per_cpu_ptr (map, i);
		__stp_map_del(m);
	}
	_stp_free_percpu(map);
err:
	_stp_kfree(pmap);
	return NULL;
}


/** Get the first element in a map.
 * @param map 
 * @returns a pointer to the first element.
 * This is typically used with _stp_map_iter().  See the foreach() macro
 * for typical usage.  It probably does what you want anyway.
 * @sa foreach
 */

static struct map_node *_stp_map_start(MAP map)
{
	if (map == NULL)
		return NULL;

	//dbug ("%lx\n", (long)map->head.next);

	if (list_empty(&map->head))
		return NULL;

	return (struct map_node *)map->head.next;
}

/** Get the next element in a map.
 * @param map 
 * @param m a pointer to the current element, returned from _stp_map_start()
 * or _stp_map_iter().
 * @returns a pointer to the next element.
 * This is typically used with _stp_map_start().  See the foreach() macro
 * for typical usage.  It probably does what you want anyway.
 * @sa foreach
 */

static struct map_node *_stp_map_iter(MAP map, struct map_node *m)
{
	if (map == NULL)
		return NULL;

	if (m->lnode.next == &map->head)
		return NULL;

	return (struct map_node *)m->lnode.next;
}

/** Clears all the elements in a map.
 * @param map 
 */

static void _stp_map_clear(MAP map)
{
	struct map_node *m;

	if (map == NULL)
		return;

	map->num = 0;

	while (!list_empty(&map->head)) {
		m = (struct map_node *)map->head.next;
		
		/* remove node from old hash list */
		hlist_del_init(&m->hnode);
		
		/* remove from entry list */
		list_del(&m->lnode);
		
		/* add to free pool */
		list_add(&m->lnode, &map->pool);
	}
}

static void _stp_pmap_clear(PMAP pmap)
{
	int i;

	if (pmap == NULL)
		return;

	stp_for_each_cpu(i) {
		MAP m = per_cpu_ptr (pmap->map, i);
#if NEED_MAP_LOCKS
		spin_lock(&m->lock);
#endif
		_stp_map_clear(m);
#if NEED_MAP_LOCKS
		spin_unlock(&m->lock);
#endif
	}
	_stp_map_clear(&pmap->agg);
}

static void __stp_map_del(MAP map)
{
	struct list_head *p, *tmp;

	/* free unused pool */
	list_for_each_safe(p, tmp, &map->pool) {
		list_del(p);
		_stp_kfree(p);
	}

	/* free used list */
	list_for_each_safe(p, tmp, &map->head) {
		list_del(p);
		_stp_kfree(p);
	}
	/* free used hash */
	_stp_kfree(map->hashes);
}

/** Deletes a map.
 * Deletes a map, freeing all memory in all elements.  Normally done only when the module exits.
 * @param map
 */

static void _stp_map_del(MAP map)
{
	if (map == NULL)
		return;

	__stp_map_del(map);

	_stp_kfree(map);
}

static void _stp_pmap_del(PMAP pmap)
{
	int i;

	if (pmap == NULL)
		return;

	stp_for_each_cpu(i) {
		MAP m = per_cpu_ptr (pmap->map, i);
		__stp_map_del(m);
	}
	_stp_free_percpu(pmap->map);

	/* free agg map elements */
	__stp_map_del(&pmap->agg);
	
	_stp_kfree(pmap);
}

/* sort keynum values */
#define SORT_COUNT -5 /* see also translate.cxx:visit_foreach_loop */
#define SORT_SUM   -4
#define SORT_MIN   -3
#define SORT_MAX   -2
#define SORT_AVG   -1

/* comparison function for sorts. */
static int _stp_cmp (struct list_head *a, struct list_head *b, int keynum, int dir, int type)
{
	struct map_node *m1 = (struct map_node *)a;
	struct map_node *m2 = (struct map_node *)b;
	if (type == STRING) {
		int ret;
		if (keynum)
			ret = strcmp(_stp_key_get_str(m1, keynum), _stp_key_get_str(m2, keynum));
		else
			ret = strcmp(_stp_get_str(m1), _stp_get_str(m2));
		if ((ret < 0 && dir > 0) || (ret > 0 && dir < 0))
			ret = 1;
		else
			ret = 0;
		//dbug ("comparing %s and %s and returning %d\n", _stp_get_str(m1), _stp_get_str(m2), ret);
		return ret;
	} else {
		int64_t a,b;
		if (keynum > 0) {
			a = _stp_key_get_int64(m1, keynum);
			b = _stp_key_get_int64(m2, keynum);
		} else if (keynum < 0) {
			stat *sd1 = (stat *)((long)m1 + m1->map->data_offset);
			stat *sd2 = (stat *)((long)m2 + m2->map->data_offset);
			switch (keynum) {
			case SORT_COUNT:
				a = sd1->count;
				b = sd2->count;
				break;
			case SORT_SUM:
				a = sd1->sum;
				b = sd2->sum;
				break;
			case SORT_MIN:
				a = sd1->min;
				b = sd2->min;
				break;
			case SORT_MAX:
				a = sd1->max;
				b = sd2->max;
				break;
			case SORT_AVG:
				a = _stp_div64 (NULL, sd1->sum, sd1->count);
				b = _stp_div64 (NULL, sd2->sum, sd2->count);
				break;
			default:
				/* should never happen */
				a = b = 0;
			}
		} else {
			a = _stp_get_int64(m1);
			b = _stp_get_int64(m2);
		}
		if ((a < b && dir > 0) || (a > b && dir < 0))
			return 1;
		return 0;
	}
}

/* swap function for bubble sort */
static inline void _stp_swap (struct list_head *a, struct list_head *b)
{
	a->prev->next = b;
	b->next->prev = a;
	a->next = b->next;
	b->prev = a->prev;
	a->prev = b;
	b->next = a;
}


/** Sort an entire array.
 * Sorts an entire array using merge sort.
 *
 * @param map Map
 * @param keynum 0 for the value, or a positive number for the key number to sort on.
 * @param dir Sort Direction. -1 for low-to-high. 1 for high-to-low.
 * @sa _stp_map_sortn()
 */

static void _stp_map_sort (MAP map, int keynum, int dir)
{
        struct list_head *p, *q, *e, *tail;
        int nmerges, psize, qsize, i, type, insize = 1;
	struct list_head *head = &map->head;

	if (list_empty(head))
		return;

	if (keynum > 0)
		(*map->get_key)((struct map_node *)head->next, keynum, &type);
	else if (keynum < 0)
		type = INT64;
	else
		type = ((struct map_node *)head->next)->map->type;

        do {
		tail = head;
		p = head->next;
                nmerges = 0;

                while (p) {
                        nmerges++;
                        q = p;
                        psize = 0;
                        for (i = 0; i < insize; i++) {
                                psize++;
                                q = q->next == head ? NULL : q->next;
                                if (!q)
                                        break;
                        }

                        qsize = insize;
                        while (psize > 0 || (qsize > 0 && q)) {
                                if (psize && (!qsize || !q || !_stp_cmp(p, q, keynum, dir, type))) {
                                        e = p;
                                        p = p->next == head ? NULL : p->next;
                                        psize--;
                                } else {
                                        e = q;
                                        q = q->next == head ? NULL : q->next;
                                        qsize--;
                                }
				
				/* now put 'e' on tail of list and make it our new tail */
				list_del(e);
				list_add(e, tail);
				tail = e;
                        }
                        p = q;
                }
                insize += insize;
        } while (nmerges > 1);
}

/** Get the top values from an array.
 * Sorts an array such that the start of the array contains the top
 * or bottom 'n' values. Use this when sorting the entire array
 * would be too time-consuming and you are only interested in the
 * highest or lowest values.
 *
 * @param map Map
 * @param n Top (or bottom) number of elements. 0 sorts the entire array.
 * @param keynum 0 for the value, or a positive number for the key number to sort on.
 * @param dir Sort Direction. -1 for low-to-high. 1 for high-to-low.
 * @sa _stp_map_sort()
 */
static void _stp_map_sortn(MAP map, int n, int keynum, int dir)
{
	if (n == 0 || n > 30) {
		_stp_map_sort(map, keynum, dir);
	} else {
		struct list_head *head = &map->head;
		struct list_head *c, *a, *last, *tmp;
		int type, num, swaps = 1;
		
		if (list_empty(head))
			return;
		
		if (keynum > 0)
			(*map->get_key)((struct map_node *)head->next, keynum, &type);
		else if (keynum < 0)
			type = INT64;
		else
			type = ((struct map_node *)head->next)->map->type;
		
		/* start off with a modified bubble sort of the first n elements */
		while (swaps) {
			num = n;
			swaps = 0;
			a = head->next;
			c = a->next->next;
			while ((a->next != head) && (--num > 0)) {
				if (_stp_cmp(a, a->next, keynum, dir, type)) {
					swaps++;
					_stp_swap(a, a->next);
				}
				a = c->prev;
				c = c->next;
			}
		}
		
		/* Now use a kind of insertion sort for the rest of the array. */
		/* Each element is tested to see if it should be be in the top 'n' */
		last = a;
		a = a->next;
		while (a != head) {
			tmp = a->next;
			c = last;
			while (c != head && _stp_cmp(c, a, keynum, dir, type)) 
				c = c->prev;
			if (c != last) {
				list_del(a);
				list_add(a, c);
				last = last->prev;
			}
			a = tmp;
		}
	}
}

static struct map_node *_stp_new_agg(MAP agg, struct hlist_head *ahead, struct map_node *ptr)
{
	struct map_node *aptr;
	/* copy keys and aggregate */
	aptr = _new_map_create(agg, ahead);
	if (aptr == NULL)
		return NULL;
	(*agg->copy)(aptr, ptr);
	switch (agg->type) {
	case INT64:
		_new_map_set_int64(agg, 
				   aptr, 
				   *(int64_t *)((long)ptr + ptr->map->data_offset),
				   0);
		break;
	case STRING:
		_new_map_set_str(agg, 
				 aptr, 
				 (char *)((long)ptr + ptr->map->data_offset),
				 0);
		break;
	case STAT: {
		stat *sd1 = (stat *)((long)aptr + agg->data_offset);
		stat *sd2 = (stat *)((long)ptr + ptr->map->data_offset);
		Hist st = &agg->hist;
		sd1->count = sd2->count;
		sd1->sum = sd2->sum;
		sd1->min = sd2->min;
		sd1->max = sd2->max;
		if (st->type != HIST_NONE) {
			int j;
			for (j = 0; j < st->buckets; j++)
				sd1->histogram[j] = sd2->histogram[j];
		}
		break;
	}
	default:
		_stp_error("Attempted to aggregate map of type %d\n", agg->type);
	}
	return aptr;
}

static void _stp_add_agg(struct map_node *aptr, struct map_node *ptr)
{
	switch (aptr->map->type) {
	case INT64:
		_new_map_set_int64(aptr->map, 
				   aptr, 
				   *(int64_t *)((long)ptr + ptr->map->data_offset),
				   1);
		break;
	case STRING:
		_new_map_set_str(aptr->map, 
				 aptr, 
				 (char *)((long)ptr + ptr->map->data_offset),
				 1);
		break;
	case STAT: {
		stat *sd1 = (stat *)((long)aptr + aptr->map->data_offset);
		stat *sd2 = (stat *)((long)ptr + ptr->map->data_offset);
		Hist st = &aptr->map->hist;
		if (sd1->count == 0) {
			sd1->count = sd2->count;
			sd1->min = sd2->min;
			sd1->max = sd2->max;
			sd1->sum = sd2->sum;
		} else {
			sd1->count += sd2->count;
			sd1->sum += sd2->sum;
			if (sd2->min < sd1->min)
				sd1->min = sd2->min;
			if (sd2->max > sd1->max)
				sd1->max = sd2->max;
		}
		if (st->type != HIST_NONE) {
			int j;
			for (j = 0; j < st->buckets; j++)
				sd1->histogram[j] += sd2->histogram[j];
		}
		break;
	}
	default:
		_stp_error("Attempted to aggregate map of type %d\n", aptr->map->type);
	}
}

/** Aggregate per-cpu maps.
 * This function aggregates the per-cpu maps into an aggregated
 * map. A pointer to that aggregated map is returned.
 * 
 * A write lock must be held on the map during this function.
 *
 * @param map A pointer to a pmap.
 * @returns a pointer to the aggregated map. Null on failure.
 */
static MAP _stp_pmap_agg (PMAP pmap)
{
	int i, hash;
	MAP m, agg;
	struct map_node *ptr, *aptr = NULL;
	struct hlist_head *head, *ahead;
	struct hlist_node *e, *f;

	agg = &pmap->agg;
	
        /* FIXME. we either clear the aggregation map or clear each local map */
	/* every time we aggregate. which would be best? */
	_stp_map_clear (agg);

	stp_for_each_cpu(i) {
		m = per_cpu_ptr (pmap->map, i);
#if NEED_MAP_LOCKS
		spin_lock(&m->lock);
#endif
		/* walk the hash chains. */
		for (hash = 0; hash < HASH_TABLE_SIZE; hash++) {
			head = &m->hashes[hash];
			ahead = &agg->hashes[hash];
			hlist_for_each(e, head) {
				int match = 0;
				ptr = (struct map_node *)((long)e - sizeof(struct list_head));
				hlist_for_each(f, ahead) {
					aptr = (struct map_node *)((long)f - sizeof(struct list_head));
					if ((*m->cmp)(ptr, aptr)) {
						match = 1;
						break;
					}
				}
				if (match)
					_stp_add_agg(aptr, ptr);
				else {
					if (!_stp_new_agg(agg, ahead, ptr)) {
#if NEED_MAP_LOCKS
						spin_unlock(&m->lock);
#endif
						return NULL;
					}
				}
			}
		}
#if NEED_MAP_LOCKS
		spin_unlock(&m->lock);
#endif
	}
	return agg;
}

/** Get the aggregation map for a pmap.
 * This function returns a pointer to the aggregation map.
 * It does not do any aggregation.
 * @param map A pointer to a pmap.
 * @returns a pointer to an aggregated map. 
 * @sa _stp_pmap_agg()
 */
#define _stp_pmap_get_agg(pmap) (&pmap->agg)

/* #define _stp_pmap_printn(map,n,fmt) _stp_map_printn (_stp_pmap_agg(map), n, fmt) */
/* #define _stp_pmap_print(map,fmt) _stp_map_printn(_stp_pmap_agg(map),0,fmt) */

static void _new_map_clear_node (struct map_node *m)
{
	switch (m->map->type) {
	case INT64:
		*(int64_t *)((long)m + m->map->data_offset) = 0;
		break;
	case STRING:
		*(char *)((long)m + m->map->data_offset) = 0;
		break;
	case STAT: 
	{
		stat *sd = (stat *)((long)m + m->map->data_offset);
		Hist st = &m->map->hist;
		sd->count = 0;
		if (st->type != HIST_NONE) {
			int j;
			for (j = 0; j < st->buckets; j++)
				sd->histogram[j] = 0;
		}
		break;
	}
	}
}

static struct map_node *_new_map_create (MAP map, struct hlist_head *head)
{
	struct map_node *m;
	if (list_empty(&map->pool)) {
		if (!map->wrap) {
			/* ERROR. no space left */
			return NULL;
		}
		m = (struct map_node *)map->head.next;
		hlist_del_init(&m->hnode);
	} else {
		m = (struct map_node *)map->pool.next;
		map->num++;
	}
	list_move_tail(&m->lnode, &map->head);
	
	/* add node to new hash list */
	hlist_add_head(&m->hnode, head);
	return m;
}

static void _new_map_del_node (MAP map, struct map_node *n)
{
	/* remove node from old hash list */
	hlist_del_init(&n->hnode);
	
	/* remove from entry list */
	list_del(&n->lnode);
	
	/* add it back to the pool */
	list_add(&n->lnode, &map->pool);
	
	map->num--;
}

static int _new_map_set_int64 (MAP map, struct map_node *n, int64_t val, int add)
{
	if (map == NULL || n == NULL)
		return -2;

	if (add)
		*(int64_t *)((long)n + map->data_offset) += val;
	else
		*(int64_t *)((long)n + map->data_offset) = val;

	return 0;
}

static int _new_map_set_str (MAP map, struct map_node *n, char *val, int add)
{
	if (map == NULL ||  n == NULL)
		return -2;

	if (add)
		str_add((void *)((long)n + map->data_offset), val);
	else
		str_copy((void *)((long)n + map->data_offset), val);

	return 0;
}

static int _new_map_set_stat (MAP map, struct map_node *n, int64_t val, int add)
{
	stat *sd;

	if (map == NULL || n == NULL)
		return -2;

	sd = (stat *)((long)n + map->data_offset);
	if (!add) {
		Hist st = &map->hist;
		sd->count = 0;
		if (st->type != HIST_NONE) {
			int j;
			for (j = 0; j < st->buckets; j++)
				sd->histogram[j] = 0;
		}
	}
	__stp_stat_add (&map->hist, sd, val);
	return 0;
}

/** Return the number of elements in a map
 * This function will return the number of active elements
 * in a map.
 * @param map 
 * @returns an int
 */
#define _stp_map_size(map) (map->num)

/** Return the number of elements in a pmap
 * This function will return the number of active elements
 * in all the per-cpu maps in a pmap. This is a quick sum and is
 * not the same as the number of unique elements that would
 * be in the aggragated map.
 * @param pmap 
 * @returns an int
 */
static int _stp_pmap_size (PMAP pmap)
{
	int i, num = 0;

	stp_for_each_cpu(i) {
		MAP m = per_cpu_ptr (pmap->map, i);
		num += m->num;
	}
	return num;
}
#endif /* _MAP_C_ */

