2010-02-22 15:46:18 +02:00
|
|
|
/*
|
|
|
|
* WrapSix
|
2012-03-24 09:26:13 +02:00
|
|
|
* Copyright (C) 2008-2012 Michal Zima <xhire@mujmalysvet.cz>
|
2010-02-22 15:46:18 +02:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Affero General Public License as
|
|
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
|
|
* License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Affero General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2012-03-24 09:26:13 +02:00
|
|
|
#include <stdlib.h> /* free, malloc */
|
|
|
|
#include <string.h> /* memcpy */
|
|
|
|
|
2012-07-04 12:37:42 +03:00
|
|
|
#include "log.h"
|
2012-03-24 09:26:13 +02:00
|
|
|
#include "radixtree.h"
|
|
|
|
|
2012-07-03 12:15:10 +03:00
|
|
|
/**
|
|
|
|
* Creates root of radix tree.
|
|
|
|
*
|
|
|
|
* @return Root of new radix tree
|
|
|
|
*/
|
2012-03-24 09:26:13 +02:00
|
|
|
radixtree_t *radixtree_create(void)
|
2010-02-22 15:46:18 +02:00
|
|
|
{
|
|
|
|
radixtree_t *radixtree;
|
|
|
|
|
|
|
|
if ((radixtree = (radixtree_t *) malloc(sizeof(radixtree_t))) == NULL) {
|
2012-07-04 12:37:42 +03:00
|
|
|
log_error("Lack of free memory");
|
2010-02-22 15:46:18 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
memset(radixtree, 0, sizeof(radixtree_t));
|
|
|
|
radixtree->count = 0;
|
|
|
|
|
|
|
|
return radixtree;
|
|
|
|
}
|
|
|
|
|
2012-07-03 12:15:10 +03:00
|
|
|
/**
|
|
|
|
* Destroys a radix tree.
|
|
|
|
*
|
|
|
|
* @param root Root of the tree to destroy
|
|
|
|
* @param depth Depth of the tree
|
|
|
|
*/
|
|
|
|
void radixtree_destroy(radixtree_t *root, unsigned char depth)
|
2010-02-22 15:46:18 +02:00
|
|
|
{
|
|
|
|
unsigned char i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE; i++) {
|
2012-07-03 12:15:10 +03:00
|
|
|
if (depth != 1 && root->array[i] != NULL) {
|
|
|
|
radixtree_destroy(root->array[i], depth - 1);
|
2010-02-22 15:46:18 +02:00
|
|
|
} else if (depth == 1) {
|
2012-07-03 12:15:10 +03:00
|
|
|
free(root->array[i]);
|
2010-02-22 15:46:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-03 12:15:10 +03:00
|
|
|
free(root);
|
2010-02-22 15:46:18 +02:00
|
|
|
}
|
|
|
|
|
2012-07-03 12:15:10 +03:00
|
|
|
/**
|
|
|
|
* Inserts new data entry into the tree.
|
|
|
|
*
|
|
|
|
* @param root Root of the radix tree
|
|
|
|
* @param chunker Function to use to get chunks for indexing internal array
|
|
|
|
* @param search_data Key used to search in the tree
|
|
|
|
* @param size Length of the key
|
|
|
|
* @param data Data to store in the tree
|
|
|
|
*/
|
2010-02-22 15:46:18 +02:00
|
|
|
void radixtree_insert(radixtree_t *root,
|
2012-04-27 16:25:29 +03:00
|
|
|
unsigned char *(chunker)(void *data, unsigned char size, unsigned char *count),
|
|
|
|
void *search_data, unsigned char size, void *data)
|
2010-02-22 15:46:18 +02:00
|
|
|
{
|
|
|
|
radixtree_t *tmp;
|
|
|
|
unsigned char i, chunk_count;
|
|
|
|
unsigned char *chunks;
|
|
|
|
|
2012-04-27 16:25:29 +03:00
|
|
|
chunks = chunker(search_data, size, &chunk_count);
|
2010-02-22 15:46:18 +02:00
|
|
|
|
|
|
|
tmp = root;
|
|
|
|
tmp->count++;
|
|
|
|
|
|
|
|
for (i = 0; i < chunk_count; i++) {
|
|
|
|
unsigned char id;
|
|
|
|
|
2012-03-24 09:26:13 +02:00
|
|
|
id = chunks[i];
|
2010-02-22 15:46:18 +02:00
|
|
|
|
|
|
|
if (i == chunk_count - 1) {
|
|
|
|
tmp->array[id] = data;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tmp->array[id] == NULL) {
|
|
|
|
tmp->array[id] = radixtree_create();
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = tmp->array[id];
|
|
|
|
tmp->count++;
|
|
|
|
}
|
2012-03-24 09:26:13 +02:00
|
|
|
|
|
|
|
free(chunks);
|
2010-02-22 15:46:18 +02:00
|
|
|
}
|
|
|
|
|
2012-07-03 12:15:10 +03:00
|
|
|
/**
|
|
|
|
* Deletes an entry from the tree.
|
|
|
|
*
|
|
|
|
* @param root Root of the radix tree
|
|
|
|
* @param chunker Function to use to get chunks for indexing internal array
|
|
|
|
* @param data Key used to search in the tree
|
|
|
|
* @param size Length of the key
|
|
|
|
*/
|
2010-02-22 15:46:18 +02:00
|
|
|
void radixtree_delete(radixtree_t *root,
|
2012-04-27 16:25:29 +03:00
|
|
|
unsigned char *(chunker)(void *data, unsigned char size, unsigned char *count),
|
|
|
|
void *data, unsigned char size)
|
2010-02-22 15:46:18 +02:00
|
|
|
{
|
|
|
|
radixtree_t *tmp,
|
|
|
|
*next;
|
|
|
|
unsigned char i, chunk_count;
|
|
|
|
unsigned char *chunks;
|
2012-04-27 16:25:29 +03:00
|
|
|
unsigned int flags = 0;
|
2010-02-22 15:46:18 +02:00
|
|
|
|
2012-04-27 16:25:29 +03:00
|
|
|
chunks = chunker(data, size, &chunk_count);
|
2010-02-22 15:46:18 +02:00
|
|
|
|
2012-07-03 12:15:10 +03:00
|
|
|
for (i = 0, tmp = root; i < chunk_count && tmp != NULL; tmp = tmp->array[chunks[i++]]) {
|
2012-04-27 16:25:29 +03:00
|
|
|
flags = tmp->count == 1 ? flags | (0x1 << i) : 0;
|
2010-02-22 15:46:18 +02:00
|
|
|
|
2012-04-27 16:25:29 +03:00
|
|
|
if (i + 1 == chunk_count) {
|
|
|
|
break;
|
2010-02-22 15:46:18 +02:00
|
|
|
}
|
2012-04-27 16:25:29 +03:00
|
|
|
}
|
2010-02-22 15:46:18 +02:00
|
|
|
|
2012-04-27 16:25:29 +03:00
|
|
|
/* if was at leaf, decrement counter */
|
|
|
|
if (i + 1 == chunk_count) {
|
|
|
|
tmp->count--;
|
|
|
|
}
|
2010-02-22 15:46:18 +02:00
|
|
|
|
2012-04-27 16:25:29 +03:00
|
|
|
tmp = root;
|
|
|
|
flags &= 0xfffffffe; /* unflag root */
|
2010-02-22 15:46:18 +02:00
|
|
|
|
2012-04-27 16:25:29 +03:00
|
|
|
for (i = 0; i < chunk_count; i++) {
|
|
|
|
next = tmp->array[chunks[i]];
|
|
|
|
if (flags & (0x1 << (i + 1))) {
|
|
|
|
tmp->array[chunks[i]] = NULL;
|
|
|
|
tmp->count--;
|
2010-02-22 15:46:18 +02:00
|
|
|
}
|
2012-04-27 16:25:29 +03:00
|
|
|
if (flags & (0x1 << i)) {
|
2010-02-22 15:46:18 +02:00
|
|
|
free(tmp);
|
|
|
|
}
|
|
|
|
tmp = next;
|
|
|
|
}
|
2012-03-24 09:26:13 +02:00
|
|
|
|
|
|
|
free(chunks);
|
2010-02-22 15:46:18 +02:00
|
|
|
}
|
|
|
|
|
2012-07-03 12:15:10 +03:00
|
|
|
/**
|
|
|
|
* Lookups an entry in the tree.
|
|
|
|
*
|
|
|
|
* @param root Root of the radix tree
|
|
|
|
* @param chunker Function to use to get chunks for indexing internal array
|
|
|
|
* @param data Key used to search in the tree
|
|
|
|
* @param size Length of the key
|
|
|
|
*/
|
2012-03-24 09:26:13 +02:00
|
|
|
void *radixtree_lookup(radixtree_t *root,
|
2012-04-27 16:25:29 +03:00
|
|
|
unsigned char *(chunker)(void *data, unsigned char size, unsigned char *count),
|
|
|
|
void *data, unsigned char size)
|
2010-02-22 15:46:18 +02:00
|
|
|
{
|
|
|
|
radixtree_t *tmp;
|
2012-03-24 09:26:13 +02:00
|
|
|
unsigned char i, chunk_count, id;
|
2010-02-22 15:46:18 +02:00
|
|
|
unsigned char *chunks;
|
|
|
|
|
2012-04-27 16:25:29 +03:00
|
|
|
chunks = chunker(data, size, &chunk_count);
|
2010-02-22 15:46:18 +02:00
|
|
|
|
|
|
|
tmp = root;
|
|
|
|
|
2012-03-24 09:26:13 +02:00
|
|
|
for (i = 0;; i++) {
|
|
|
|
id = chunks[i];
|
2010-02-22 15:46:18 +02:00
|
|
|
|
2012-03-24 09:26:13 +02:00
|
|
|
/* leave the for cycle before tmp gets overwritten,
|
|
|
|
* if the final result was found */
|
2010-02-22 15:46:18 +02:00
|
|
|
if (i == chunk_count - 1) {
|
2012-03-24 09:26:13 +02:00
|
|
|
free(chunks);
|
2010-02-22 15:46:18 +02:00
|
|
|
return tmp->array[id];
|
|
|
|
}
|
|
|
|
|
2012-03-24 09:26:13 +02:00
|
|
|
/* leave if there is no result */
|
2010-02-22 15:46:18 +02:00
|
|
|
if (tmp->array[id] == NULL) {
|
2012-03-24 09:26:13 +02:00
|
|
|
free(chunks);
|
2010-02-22 15:46:18 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-03-24 09:26:13 +02:00
|
|
|
/* go deeper */
|
2010-02-22 15:46:18 +02:00
|
|
|
tmp = tmp->array[id];
|
|
|
|
}
|
2012-03-24 09:26:13 +02:00
|
|
|
|
|
|
|
/* we never get here ;c) */
|
2010-02-22 15:46:18 +02:00
|
|
|
}
|
|
|
|
|
2012-04-27 16:25:29 +03:00
|
|
|
/**
|
|
|
|
* Universal chunker.
|
|
|
|
*
|
|
|
|
* @param data Data to chunk
|
|
|
|
* @param size Size of the data; must be divisible by 3
|
|
|
|
* @param count Variable into which is put information about number of
|
|
|
|
* chunks produced
|
|
|
|
*
|
2012-07-04 12:37:42 +03:00
|
|
|
* @return Array of chunks
|
2012-04-27 16:25:29 +03:00
|
|
|
*/
|
|
|
|
unsigned char *radixtree_chunker(void *data, unsigned char size, unsigned char *count)
|
2010-02-22 15:46:18 +02:00
|
|
|
{
|
|
|
|
short i;
|
|
|
|
unsigned char counter;
|
2012-03-24 09:26:13 +02:00
|
|
|
unsigned char *chunks;
|
|
|
|
unsigned char *cdata = (unsigned char *) data;
|
2010-02-22 15:46:18 +02:00
|
|
|
|
2012-04-27 16:25:29 +03:00
|
|
|
counter = size * 8 / 6;
|
2010-02-22 15:46:18 +02:00
|
|
|
memcpy(count, &counter, sizeof(unsigned char));
|
|
|
|
|
2012-03-24 09:26:13 +02:00
|
|
|
if ((chunks = (unsigned char *) malloc(counter * sizeof(unsigned char))) == NULL) {
|
2012-07-04 12:37:42 +03:00
|
|
|
log_error("Lack of free memory");
|
2012-03-24 09:26:13 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2010-02-22 15:46:18 +02:00
|
|
|
|
2012-03-24 09:26:13 +02:00
|
|
|
/*
|
|
|
|
* 3 == 0000 0011 == 0x03
|
|
|
|
* 15 == 0000 1111 == 0x0f
|
|
|
|
* 48 == 0011 0000 == 0x30
|
|
|
|
* 60 == 0011 1100 == 0x3c
|
|
|
|
* 63 == 0011 1111 == 0x3f
|
|
|
|
* 192 == 1100 0000 == 0xc0
|
|
|
|
* 240 == 1111 0000 == 0xf0
|
|
|
|
* 252 == 1111 1100 == 0xfc
|
|
|
|
*/
|
2012-04-27 16:25:29 +03:00
|
|
|
/* processes 3 bytes at a time */
|
2012-03-24 09:26:13 +02:00
|
|
|
for (i = 0, counter = 0; counter < *count; i++) {
|
|
|
|
chunks[counter++] = cdata[i] & 0x3f;
|
|
|
|
chunks[counter++] = ((cdata[i] & 0xc0) >> 6) | ((cdata[i + 1] & 0x0f) << 2);
|
|
|
|
i++;
|
|
|
|
chunks[counter++] = ((cdata[i] & 0xf0) >> 4) | ((cdata[i + 1] & 0x03) << 4);
|
|
|
|
i++;
|
|
|
|
chunks[counter++] = ((cdata[i] & 0xfc) >> 2);
|
2010-02-22 15:46:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return chunks;
|
|
|
|
}
|