quakeforge/libs/gamecode/builtins/bi_strhash.c

441 lines
11 KiB
C
Raw Normal View History

/*
bi_inputline.c
CSQC string hashes builtins
Copyright (C) 2002 Robin Redeker <elmex@x-paste.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
static const char rcsid[] =
"$Id$";
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include "QF/console.h"
#include "QF/draw.h"
#include "QF/progs.h"
#include "QF/zone.h"
#define MAX_SH_VALUES 16
/*
DESIGN NOTE (by elmex):
This file contains code for QuakeC hashes.
The hases are designed to have around 10 sub-values for
one key.
Hashes are stored as hash-ids in the QC-environment.
The key->value pairs in the hashes are stored in the
order they are set. So its easy to make array-hashes.
The function which gets the Lenght of a hash is specialy
made for array purposes.
TODO: Check the FIXME's below in the code.
(please taniwha ;)
(about memory leaks)
*/
// a hash element
typedef struct {
const char * key;
const char * values[MAX_SH_VALUES];
} str_hash_elem;
// a structure of a hash
typedef struct {
str_hash_elem **elements;
int cnt_elements;
} str_hash;
// a list structure of hashes
typedef struct {
str_hash **hashes;
int cnt_hashes;
} strh_resources_t;
/*
bi_StringHash_Create
Creates a hash structure and gives back the hash-id to the
QC-environment
*/
static void
bi_StringHash_Create (progs_t *pr)
{
strh_resources_t *res = PR_Resources_Find (pr, "StringHash");
int i;
int hash_id=-1;
// check if there is a empty hash in the array
for(i = 0; i < res->cnt_hashes; i++) {
if(res->hashes[i]->cnt_elements == 0) {
// we found a empty already allocated hash
hash_id = i;
break;
}
}
if(hash_id == -1) {
/* allocate a new hash struct, if
there is no old one */
if(res->hashes == NULL) { // allocate a new list of hashes
res->hashes = (str_hash **) malloc(
sizeof(str_hash*) * (res->cnt_hashes + 1));
} else { // reallocate the list of hashes
res->hashes = (str_hash **) realloc(res->hashes,
sizeof(str_hash*) * (res->cnt_hashes + 1));
}
hash_id = res->cnt_hashes;
res->hashes[hash_id] =
(str_hash*) malloc(sizeof(str_hash));
// dont forge to clean the hash
memset(res->hashes[hash_id],0,sizeof(str_hash));
res->cnt_hashes++; // increase cnt of allocated hashes
}
G_INT (pr, OFS_RETURN) = hash_id;
}
/*
bi_StringHash_Destroy
Destroys a hash
*/
static void
bi_StringHash_Destroy (progs_t *pr)
{
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
int hash_id = G_INT (pr, OFS_PARM0);
str_hash *sh = NULL;
int i;
if(hash_id >= res->cnt_hashes || hash_id < 0) {
G_INT (pr, OFS_RETURN) = 0;
return;
}
sh = res->hashes[hash_id];
/* we dont really destroy the allocated hash,
but we free the elements of the hash
and mark it for reuseing */
for(i = 0; i < sh->cnt_elements; i++) {
if(sh->elements[i] != NULL) {
/* they should never be NULL,
buy, who knows? */
PR_Error(pr, "NULL hash-element found -> not supposed!");
} else {
free(sh->elements[i]); // free str_hash_elem structs
}
/*
FIXME: HELP: taniwha??? do i have to FREE
the strings ??
*/
}
free(sh->elements); // free the list pointer
sh->elements = NULL;
sh->cnt_elements = 0;
G_INT (pr, OFS_RETURN) = 1;
}
/*
bi_StringHash_Set
Sets a the key-value (with a special id) in a hash
to a value.
If a non existing key is given a element if generated for it.
FIXME: look if this functions does mem-leak
*/
static void
bi_StringHash_Set (progs_t *pr)
{
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
int hash_id = G_INT (pr, OFS_PARM0);
const char *key = G_STRING (pr, OFS_PARM1);
const char *val = G_STRING (pr, OFS_PARM2);
int val_id = G_INT (pr, OFS_PARM3);
str_hash *sh = NULL;
int i,found_fl=0;
// validate the hash ID
if(res->hashes == NULL ||
(hash_id >= res->cnt_hashes || hash_id < 0) ||
(val_id < 0 || val_id >= MAX_SH_VALUES))
{
G_INT (pr, OFS_RETURN) = 0;
return;
}
sh = res->hashes[hash_id];
// first search for existing key
for(i = 0; i < sh->cnt_elements; i++) {
if(strcmp(sh->elements[i]->key, key) == 0) {
// found already a element with that key
if(sh->elements[i]->values[val_id] == NULL) { // empty val
sh->elements[i]->values[val_id] = val;
} else {
/* FIXME: taniwha, do i have to free it?
* this is a potential memory leak, maybe, bu
* i dont know how Hunk_Alloc* stuff works
* ITS NOT DOCUMENTED!
*/
sh->elements[i]->values[val_id] = val;
}
found_fl = 1;
}
}
if(!found_fl) { // add a new element
if(sh->elements == NULL) { // alloc new elements list pointer
sh->elements = (str_hash_elem**) malloc(sizeof(str_hash_elem*));
sh->cnt_elements = 0; // 0 because usage as index here
} else {
sh->elements = (str_hash_elem**) realloc(sh->elements,
sizeof(str_hash_elem*) * (sh->cnt_elements+1));
}
sh->elements[sh->cnt_elements] = malloc(sizeof(str_hash_elem));
memset(sh->elements[sh->cnt_elements],0,sizeof(str_hash_elem));
sh->elements[sh->cnt_elements]->key = key;
sh->elements[sh->cnt_elements]->values[val_id] = val;
sh->cnt_elements++;
}
G_INT (pr, OFS_RETURN) = 1;
return;
}
/*
bi_StringHash_SetIdx
Sets a the key-value (with a special id) in a hash
to a value. This function works by index of the element.
A element in hash is NOT generated automatically.
FIXME: look if this functions does mem-leak
*/
static void
bi_StringHash_SetIdx (progs_t *pr)
{
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
int hash_id = G_INT (pr, OFS_PARM0);
int idx = G_INT (pr, OFS_PARM1);
const char *val = G_STRING (pr, OFS_PARM2);
int val_id = G_INT (pr, OFS_PARM3);
str_hash *sh = NULL;
// validate the hash ID
if(res->hashes == NULL ||
(hash_id >= res->cnt_hashes || hash_id < 0) ||
(val_id < 0 || val_id >= MAX_SH_VALUES))
{
G_INT (pr, OFS_RETURN) = 0;
return;
}
sh = res->hashes[hash_id];
if(idx < 0 || idx >= sh->cnt_elements || sh->elements[idx] == NULL) {
if(sh->elements[idx] == NULL)
PR_Error(pr, "NULL hash-element found -> not supposed!");
G_INT (pr, OFS_RETURN) = 0;
return;
}
sh->elements[idx]->values[val_id] = val;
G_INT (pr, OFS_RETURN) = 1;
return;
}
/*
bi_StringHash_Get
Gets the value of a key and its id in a hash
*/
static void
bi_StringHash_Get (progs_t *pr)
{
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
int hash_id = G_INT (pr, OFS_PARM0);
const char *key = G_STRING (pr, OFS_PARM1);
int val_id = G_INT (pr, OFS_PARM2);
str_hash *sh = NULL;
int i,found_fl=0;
const char *retstr = NULL;
// validate the hash ID
if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0 ||
val_id >= MAX_SH_VALUES)
{
retstr = "";
RETURN_STRING(pr, retstr);
return;
}
sh = res->hashes[hash_id];
// first search for existing key
for(i = 0; i < sh->cnt_elements; i++) {
if(strcmp(sh->elements[i]->key, key) == 0) {
if(sh->elements[i]->values[val_id] != NULL) {
retstr = sh->elements[i]->values[val_id];
} else {
retstr = "";
}
found_fl = 1;
}
}
if(!found_fl) {
retstr = "";
}
RETURN_STRING(pr, retstr);
}
/*
bi_StringHash_Length
Gets the count of the elements in a hash
*/
static void
bi_StringHash_Length (progs_t *pr)
{
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
int hash_id = G_INT (pr, OFS_PARM0);
str_hash *sh = NULL;
// validate the hash ID
if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0) {
G_INT (pr, OFS_RETURN) = 0;
return;
}
sh = res->hashes[hash_id];
G_INT (pr, OFS_RETURN) = sh->cnt_elements;
}
/*
bi_StringHash_GetIdx
Gets a hash elment by its index
special: if the val_id is -1 the key of the element will
be returned
*/
static void
bi_StringHash_GetIdx (progs_t *pr)
{
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
int hash_id = G_INT (pr, OFS_PARM0);
int idx = G_INT (pr, OFS_PARM1);
int val_id = G_INT (pr, OFS_PARM2);
str_hash *sh = NULL;
const char *retstr = NULL;
// validate the hash ID
if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0) {
retstr = NULL;
}
sh = res->hashes[hash_id];
if(idx < 0 || idx >= sh->cnt_elements ||
(val_id < -1 || val_id >= MAX_SH_VALUES))
{
retstr = NULL;
} else {
if(val_id == -1) {
retstr = sh->elements[idx]->key;
} else {
retstr = sh->elements[idx]->values[val_id];
}
}
if(retstr == NULL) { retstr = ""; }
RETURN_STRING(pr, retstr);
}
/*
bi_strh_clear
Free the dynamic allocated memory
XXX: taniwha: i dont know what to free
exactly, could you validate this code?
*/
static void
bi_strh_clear (progs_t *pr, void *data)
{
strh_resources_t *res = (strh_resources_t *)data;
int i,d;
for (i = 0; i < res->cnt_hashes; i++) {
if (res->hashes[i]) {
for(d = 0; d < res->hashes[i]->cnt_elements; d++) {
free(res->hashes[i]->elements[d]);
}
free(res->hashes[i]->elements);
free(res->hashes[i]);
res->hashes[i] = 0;
}
}
}
/*
StringHash_Progs_Init
Inits the Progs-system with StringHash resources and
functions.
*/
void
StringHash_Progs_Init (progs_t *pr)
{
strh_resources_t *res = malloc (sizeof (strh_resources_t));
res->cnt_hashes = 0;
res->hashes = NULL;
PR_Resources_Register (pr, "StringHash", res, bi_strh_clear);
PR_AddBuiltin (pr, "StringHash_Create", bi_StringHash_Create, -1);
PR_AddBuiltin (pr, "StringHash_Destroy", bi_StringHash_Destroy, -1);
PR_AddBuiltin (pr, "StringHash_Set", bi_StringHash_Set, -1);
PR_AddBuiltin (pr, "StringHash_Get", bi_StringHash_Get, -1);
PR_AddBuiltin (pr, "StringHash_Length", bi_StringHash_Length, -1);
PR_AddBuiltin (pr, "StringHash_SetIdx", bi_StringHash_SetIdx, -1);
PR_AddBuiltin (pr, "StringHash_GetIdx", bi_StringHash_GetIdx, -1);
}
/*
XXX NOTE by elmex:
A file, decripted like this is what
i want to see everywhere in qf-cvs =)
No excuse for undocumented code and design without
a reason for it.
We/I want to know why something was designed how it is.
*/