16cd6a6acSopenharmony_ci
26cd6a6acSopenharmony_ci/* Author : Stephen Smalley, <sds@tycho.nsa.gov> */
36cd6a6acSopenharmony_ci
46cd6a6acSopenharmony_ci/*
56cd6a6acSopenharmony_ci * Updated : Karl MacMillan <kmacmillan@mentalrootkit.com>
66cd6a6acSopenharmony_ci *
76cd6a6acSopenharmony_ci * Copyright (C) 2007 Red Hat, Inc.
86cd6a6acSopenharmony_ci *
96cd6a6acSopenharmony_ci * This library is free software; you can redistribute it and/or
106cd6a6acSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
116cd6a6acSopenharmony_ci * License as published by the Free Software Foundation; either
126cd6a6acSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
136cd6a6acSopenharmony_ci *
146cd6a6acSopenharmony_ci * This library is distributed in the hope that it will be useful,
156cd6a6acSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
166cd6a6acSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
176cd6a6acSopenharmony_ci * Lesser General Public License for more details.
186cd6a6acSopenharmony_ci *
196cd6a6acSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
206cd6a6acSopenharmony_ci * License along with this library; if not, write to the Free Software
216cd6a6acSopenharmony_ci * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
226cd6a6acSopenharmony_ci */
236cd6a6acSopenharmony_ci
246cd6a6acSopenharmony_ci
256cd6a6acSopenharmony_ci/* FLASK */
266cd6a6acSopenharmony_ci
276cd6a6acSopenharmony_ci/*
286cd6a6acSopenharmony_ci * Implementation of the hash table type.
296cd6a6acSopenharmony_ci */
306cd6a6acSopenharmony_ci
316cd6a6acSopenharmony_ci#include <stdlib.h>
326cd6a6acSopenharmony_ci#include <string.h>
336cd6a6acSopenharmony_ci#include <sepol/policydb/hashtab.h>
346cd6a6acSopenharmony_ci
356cd6a6acSopenharmony_ci#include "private.h"
366cd6a6acSopenharmony_ci
376cd6a6acSopenharmony_cihashtab_t hashtab_create(unsigned int (*hash_value) (hashtab_t h,
386cd6a6acSopenharmony_ci						     const_hashtab_key_t key),
396cd6a6acSopenharmony_ci			 int (*keycmp) (hashtab_t h,
406cd6a6acSopenharmony_ci					const_hashtab_key_t key1,
416cd6a6acSopenharmony_ci					const_hashtab_key_t key2),
426cd6a6acSopenharmony_ci			 unsigned int size)
436cd6a6acSopenharmony_ci{
446cd6a6acSopenharmony_ci
456cd6a6acSopenharmony_ci	hashtab_t p;
466cd6a6acSopenharmony_ci
476cd6a6acSopenharmony_ci	p = (hashtab_t) malloc(sizeof(hashtab_val_t));
486cd6a6acSopenharmony_ci	if (p == NULL)
496cd6a6acSopenharmony_ci		return p;
506cd6a6acSopenharmony_ci
516cd6a6acSopenharmony_ci	memset(p, 0, sizeof(hashtab_val_t));
526cd6a6acSopenharmony_ci	p->size = size;
536cd6a6acSopenharmony_ci	p->nel = 0;
546cd6a6acSopenharmony_ci	p->hash_value = hash_value;
556cd6a6acSopenharmony_ci	p->keycmp = keycmp;
566cd6a6acSopenharmony_ci	p->htable = (hashtab_ptr_t *) calloc(size, sizeof(hashtab_ptr_t));
576cd6a6acSopenharmony_ci	if (p->htable == NULL) {
586cd6a6acSopenharmony_ci		free(p);
596cd6a6acSopenharmony_ci		return NULL;
606cd6a6acSopenharmony_ci	}
616cd6a6acSopenharmony_ci
626cd6a6acSopenharmony_ci	return p;
636cd6a6acSopenharmony_ci}
646cd6a6acSopenharmony_ci
656cd6a6acSopenharmony_cistatic void hashtab_check_resize(hashtab_t h)
666cd6a6acSopenharmony_ci{
676cd6a6acSopenharmony_ci	unsigned int hvalue, i, old_size, new_size = h->size;
686cd6a6acSopenharmony_ci	hashtab_ptr_t *new_htable, *dst, cur, next;
696cd6a6acSopenharmony_ci
706cd6a6acSopenharmony_ci	while (new_size <= h->nel && new_size * 2 != 0)
716cd6a6acSopenharmony_ci		new_size *= 2;
726cd6a6acSopenharmony_ci
736cd6a6acSopenharmony_ci	if (h->size == new_size)
746cd6a6acSopenharmony_ci		return;
756cd6a6acSopenharmony_ci
766cd6a6acSopenharmony_ci	new_htable = calloc(new_size, sizeof(*new_htable));
776cd6a6acSopenharmony_ci	if (!new_htable)
786cd6a6acSopenharmony_ci		return;
796cd6a6acSopenharmony_ci
806cd6a6acSopenharmony_ci	old_size = h->size;
816cd6a6acSopenharmony_ci	h->size = new_size;
826cd6a6acSopenharmony_ci
836cd6a6acSopenharmony_ci	/* Move all elements to the new htable */
846cd6a6acSopenharmony_ci	for (i = 0; i < old_size; i++) {
856cd6a6acSopenharmony_ci		cur = h->htable[i];
866cd6a6acSopenharmony_ci		while (cur != NULL) {
876cd6a6acSopenharmony_ci			hvalue = h->hash_value(h, cur->key);
886cd6a6acSopenharmony_ci			dst = &new_htable[hvalue];
896cd6a6acSopenharmony_ci			while (*dst && h->keycmp(h, cur->key, (*dst)->key) > 0)
906cd6a6acSopenharmony_ci				dst = &(*dst)->next;
916cd6a6acSopenharmony_ci
926cd6a6acSopenharmony_ci			next = cur->next;
936cd6a6acSopenharmony_ci
946cd6a6acSopenharmony_ci			cur->next = *dst;
956cd6a6acSopenharmony_ci			*dst = cur;
966cd6a6acSopenharmony_ci
976cd6a6acSopenharmony_ci			cur = next;
986cd6a6acSopenharmony_ci		}
996cd6a6acSopenharmony_ci	}
1006cd6a6acSopenharmony_ci	free(h->htable);
1016cd6a6acSopenharmony_ci	h->htable = new_htable;
1026cd6a6acSopenharmony_ci}
1036cd6a6acSopenharmony_ci
1046cd6a6acSopenharmony_ciint hashtab_insert(hashtab_t h, hashtab_key_t key, hashtab_datum_t datum)
1056cd6a6acSopenharmony_ci{
1066cd6a6acSopenharmony_ci	int hvalue;
1076cd6a6acSopenharmony_ci	hashtab_ptr_t prev, cur, newnode;
1086cd6a6acSopenharmony_ci
1096cd6a6acSopenharmony_ci	if (!h)
1106cd6a6acSopenharmony_ci		return SEPOL_ENOMEM;
1116cd6a6acSopenharmony_ci
1126cd6a6acSopenharmony_ci	hashtab_check_resize(h);
1136cd6a6acSopenharmony_ci
1146cd6a6acSopenharmony_ci	hvalue = h->hash_value(h, key);
1156cd6a6acSopenharmony_ci	prev = NULL;
1166cd6a6acSopenharmony_ci	cur = h->htable[hvalue];
1176cd6a6acSopenharmony_ci	while (cur && h->keycmp(h, key, cur->key) > 0) {
1186cd6a6acSopenharmony_ci		prev = cur;
1196cd6a6acSopenharmony_ci		cur = cur->next;
1206cd6a6acSopenharmony_ci	}
1216cd6a6acSopenharmony_ci
1226cd6a6acSopenharmony_ci	if (cur && (h->keycmp(h, key, cur->key) == 0))
1236cd6a6acSopenharmony_ci		return SEPOL_EEXIST;
1246cd6a6acSopenharmony_ci
1256cd6a6acSopenharmony_ci	newnode = (hashtab_ptr_t) malloc(sizeof(hashtab_node_t));
1266cd6a6acSopenharmony_ci	if (newnode == NULL)
1276cd6a6acSopenharmony_ci		return SEPOL_ENOMEM;
1286cd6a6acSopenharmony_ci	memset(newnode, 0, sizeof(struct hashtab_node));
1296cd6a6acSopenharmony_ci	newnode->key = key;
1306cd6a6acSopenharmony_ci	newnode->datum = datum;
1316cd6a6acSopenharmony_ci	if (prev) {
1326cd6a6acSopenharmony_ci		newnode->next = prev->next;
1336cd6a6acSopenharmony_ci		prev->next = newnode;
1346cd6a6acSopenharmony_ci	} else {
1356cd6a6acSopenharmony_ci		newnode->next = h->htable[hvalue];
1366cd6a6acSopenharmony_ci		h->htable[hvalue] = newnode;
1376cd6a6acSopenharmony_ci	}
1386cd6a6acSopenharmony_ci
1396cd6a6acSopenharmony_ci	h->nel++;
1406cd6a6acSopenharmony_ci	return SEPOL_OK;
1416cd6a6acSopenharmony_ci}
1426cd6a6acSopenharmony_ci
1436cd6a6acSopenharmony_ciint hashtab_remove(hashtab_t h, hashtab_key_t key,
1446cd6a6acSopenharmony_ci		   void (*destroy) (hashtab_key_t k,
1456cd6a6acSopenharmony_ci				    hashtab_datum_t d, void *args), void *args)
1466cd6a6acSopenharmony_ci{
1476cd6a6acSopenharmony_ci	int hvalue;
1486cd6a6acSopenharmony_ci	hashtab_ptr_t cur, last;
1496cd6a6acSopenharmony_ci
1506cd6a6acSopenharmony_ci	if (!h)
1516cd6a6acSopenharmony_ci		return SEPOL_ENOENT;
1526cd6a6acSopenharmony_ci
1536cd6a6acSopenharmony_ci	hvalue = h->hash_value(h, key);
1546cd6a6acSopenharmony_ci	last = NULL;
1556cd6a6acSopenharmony_ci	cur = h->htable[hvalue];
1566cd6a6acSopenharmony_ci	while (cur != NULL && h->keycmp(h, key, cur->key) > 0) {
1576cd6a6acSopenharmony_ci		last = cur;
1586cd6a6acSopenharmony_ci		cur = cur->next;
1596cd6a6acSopenharmony_ci	}
1606cd6a6acSopenharmony_ci
1616cd6a6acSopenharmony_ci	if (cur == NULL || (h->keycmp(h, key, cur->key) != 0))
1626cd6a6acSopenharmony_ci		return SEPOL_ENOENT;
1636cd6a6acSopenharmony_ci
1646cd6a6acSopenharmony_ci	if (last == NULL)
1656cd6a6acSopenharmony_ci		h->htable[hvalue] = cur->next;
1666cd6a6acSopenharmony_ci	else
1676cd6a6acSopenharmony_ci		last->next = cur->next;
1686cd6a6acSopenharmony_ci
1696cd6a6acSopenharmony_ci	if (destroy)
1706cd6a6acSopenharmony_ci		destroy(cur->key, cur->datum, args);
1716cd6a6acSopenharmony_ci	free(cur);
1726cd6a6acSopenharmony_ci	h->nel--;
1736cd6a6acSopenharmony_ci	return SEPOL_OK;
1746cd6a6acSopenharmony_ci}
1756cd6a6acSopenharmony_ci
1766cd6a6acSopenharmony_cihashtab_datum_t hashtab_search(hashtab_t h, const_hashtab_key_t key)
1776cd6a6acSopenharmony_ci{
1786cd6a6acSopenharmony_ci
1796cd6a6acSopenharmony_ci	int hvalue;
1806cd6a6acSopenharmony_ci	hashtab_ptr_t cur;
1816cd6a6acSopenharmony_ci
1826cd6a6acSopenharmony_ci	if (!h)
1836cd6a6acSopenharmony_ci		return NULL;
1846cd6a6acSopenharmony_ci
1856cd6a6acSopenharmony_ci	hvalue = h->hash_value(h, key);
1866cd6a6acSopenharmony_ci	cur = h->htable[hvalue];
1876cd6a6acSopenharmony_ci	while (cur != NULL && h->keycmp(h, key, cur->key) > 0)
1886cd6a6acSopenharmony_ci		cur = cur->next;
1896cd6a6acSopenharmony_ci
1906cd6a6acSopenharmony_ci	if (cur == NULL || (h->keycmp(h, key, cur->key) != 0))
1916cd6a6acSopenharmony_ci		return NULL;
1926cd6a6acSopenharmony_ci
1936cd6a6acSopenharmony_ci	return cur->datum;
1946cd6a6acSopenharmony_ci}
1956cd6a6acSopenharmony_ci
1966cd6a6acSopenharmony_civoid hashtab_destroy(hashtab_t h)
1976cd6a6acSopenharmony_ci{
1986cd6a6acSopenharmony_ci	unsigned int i;
1996cd6a6acSopenharmony_ci	hashtab_ptr_t cur, temp;
2006cd6a6acSopenharmony_ci
2016cd6a6acSopenharmony_ci	if (!h)
2026cd6a6acSopenharmony_ci		return;
2036cd6a6acSopenharmony_ci
2046cd6a6acSopenharmony_ci	for (i = 0; i < h->size; i++) {
2056cd6a6acSopenharmony_ci		cur = h->htable[i];
2066cd6a6acSopenharmony_ci		while (cur != NULL) {
2076cd6a6acSopenharmony_ci			temp = cur;
2086cd6a6acSopenharmony_ci			cur = cur->next;
2096cd6a6acSopenharmony_ci			free(temp);
2106cd6a6acSopenharmony_ci		}
2116cd6a6acSopenharmony_ci		h->htable[i] = NULL;
2126cd6a6acSopenharmony_ci	}
2136cd6a6acSopenharmony_ci
2146cd6a6acSopenharmony_ci	free(h->htable);
2156cd6a6acSopenharmony_ci	h->htable = NULL;
2166cd6a6acSopenharmony_ci
2176cd6a6acSopenharmony_ci	free(h);
2186cd6a6acSopenharmony_ci}
2196cd6a6acSopenharmony_ci
2206cd6a6acSopenharmony_ciint hashtab_map(hashtab_t h,
2216cd6a6acSopenharmony_ci		int (*apply) (hashtab_key_t k,
2226cd6a6acSopenharmony_ci			      hashtab_datum_t d, void *args), void *args)
2236cd6a6acSopenharmony_ci{
2246cd6a6acSopenharmony_ci	unsigned int i;
2256cd6a6acSopenharmony_ci	hashtab_ptr_t cur;
2266cd6a6acSopenharmony_ci	int ret;
2276cd6a6acSopenharmony_ci
2286cd6a6acSopenharmony_ci	if (!h)
2296cd6a6acSopenharmony_ci		return SEPOL_OK;
2306cd6a6acSopenharmony_ci
2316cd6a6acSopenharmony_ci	for (i = 0; i < h->size; i++) {
2326cd6a6acSopenharmony_ci		cur = h->htable[i];
2336cd6a6acSopenharmony_ci		while (cur != NULL) {
2346cd6a6acSopenharmony_ci			ret = apply(cur->key, cur->datum, args);
2356cd6a6acSopenharmony_ci			if (ret)
2366cd6a6acSopenharmony_ci				return ret;
2376cd6a6acSopenharmony_ci			cur = cur->next;
2386cd6a6acSopenharmony_ci		}
2396cd6a6acSopenharmony_ci	}
2406cd6a6acSopenharmony_ci	return SEPOL_OK;
2416cd6a6acSopenharmony_ci}
2426cd6a6acSopenharmony_ci
2436cd6a6acSopenharmony_civoid hashtab_hash_eval(hashtab_t h, char *tag)
2446cd6a6acSopenharmony_ci{
2456cd6a6acSopenharmony_ci	unsigned int i;
2466cd6a6acSopenharmony_ci	int chain_len, slots_used, max_chain_len;
2476cd6a6acSopenharmony_ci	hashtab_ptr_t cur;
2486cd6a6acSopenharmony_ci
2496cd6a6acSopenharmony_ci	slots_used = 0;
2506cd6a6acSopenharmony_ci	max_chain_len = 0;
2516cd6a6acSopenharmony_ci	for (i = 0; i < h->size; i++) {
2526cd6a6acSopenharmony_ci		cur = h->htable[i];
2536cd6a6acSopenharmony_ci		if (cur) {
2546cd6a6acSopenharmony_ci			slots_used++;
2556cd6a6acSopenharmony_ci			chain_len = 0;
2566cd6a6acSopenharmony_ci			while (cur) {
2576cd6a6acSopenharmony_ci				chain_len++;
2586cd6a6acSopenharmony_ci				cur = cur->next;
2596cd6a6acSopenharmony_ci			}
2606cd6a6acSopenharmony_ci
2616cd6a6acSopenharmony_ci			if (chain_len > max_chain_len)
2626cd6a6acSopenharmony_ci				max_chain_len = chain_len;
2636cd6a6acSopenharmony_ci		}
2646cd6a6acSopenharmony_ci	}
2656cd6a6acSopenharmony_ci
2666cd6a6acSopenharmony_ci	printf
2676cd6a6acSopenharmony_ci	    ("%s:  %d entries and %d/%d buckets used, longest chain length %d\n",
2686cd6a6acSopenharmony_ci	     tag, h->nel, slots_used, h->size, max_chain_len);
2696cd6a6acSopenharmony_ci}
270