mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 21:57:57 +03:00
397 lines
8.6 KiB
C
397 lines
8.6 KiB
C
![]() |
/******
|
||
|
gbucket.c
|
||
|
GameSpy Stats/Tracking SDK
|
||
|
|
||
|
Copyright 1999-2007 GameSpy Industries, Inc
|
||
|
|
||
|
devsupport@gamespy.com
|
||
|
|
||
|
******/
|
||
|
|
||
|
/********
|
||
|
INCLUDES
|
||
|
********/
|
||
|
#include "../common/gsCommon.h"
|
||
|
#include "gbucket.h"
|
||
|
#include "../hashtable.h"
|
||
|
#include <ctype.h>
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
extern "C" {
|
||
|
#endif
|
||
|
|
||
|
/********
|
||
|
TYPEDEFS
|
||
|
********/
|
||
|
struct bucketset_s
|
||
|
{
|
||
|
HashTable buckets;
|
||
|
};
|
||
|
|
||
|
|
||
|
typedef struct bucket_s
|
||
|
{
|
||
|
char *name;
|
||
|
BucketType type;
|
||
|
int nvals; //for averaging
|
||
|
union
|
||
|
{
|
||
|
int ival;
|
||
|
double fval;
|
||
|
char *sval;
|
||
|
} vals;
|
||
|
} bucket_t;
|
||
|
|
||
|
typedef struct dumpdata_s
|
||
|
{
|
||
|
char *data;
|
||
|
unsigned int maxlen;
|
||
|
unsigned int len;
|
||
|
} dumpdata_t;
|
||
|
|
||
|
/********
|
||
|
PROTOTYPES
|
||
|
********/
|
||
|
static void DumpMap(void *elem, void *clientData);
|
||
|
static void BucketFree(void *elem);
|
||
|
static int BucketCompare(const void *entry1, const void *entry2);
|
||
|
static int BucketHash(const void *elem, int numbuckets);
|
||
|
static void *DoSet(bucket_t *pbucket, void *value);
|
||
|
static void *DoGet(bucket_t *pbucket);
|
||
|
static bucket_t *DoFind(bucketset_t set, char *name);
|
||
|
|
||
|
|
||
|
/********
|
||
|
VARS
|
||
|
********/
|
||
|
bucketset_t g_buckets;
|
||
|
|
||
|
/****************************************************************************/
|
||
|
/* PUBLIC FUNCTIONS */
|
||
|
/****************************************************************************/
|
||
|
bucketset_t NewBucketSet(void)
|
||
|
{
|
||
|
bucketset_t set;
|
||
|
|
||
|
set = (bucketset_t)gsimalloc(sizeof (struct bucketset_s));
|
||
|
assert(set);
|
||
|
set->buckets = TableNew(sizeof(bucket_t),32,BucketHash, BucketCompare, BucketFree);
|
||
|
|
||
|
g_buckets = set;
|
||
|
return set;
|
||
|
}
|
||
|
|
||
|
void FreeBucketSet(bucketset_t set)
|
||
|
{
|
||
|
assert(set);
|
||
|
assert(set->buckets);
|
||
|
TableFree(set->buckets);
|
||
|
gsifree(set);
|
||
|
}
|
||
|
|
||
|
char *DumpBucketSet(bucketset_t set)
|
||
|
{
|
||
|
dumpdata_t data;
|
||
|
if (set == NULL)
|
||
|
set = g_buckets;
|
||
|
assert(set);
|
||
|
data.data = (char *)gsimalloc(128); //alloc an initial buffer
|
||
|
data.data[0] = 0;
|
||
|
data.len = 0;
|
||
|
data.maxlen = 128;
|
||
|
TableMap(set->buckets,DumpMap, &data);
|
||
|
return data.data;
|
||
|
}
|
||
|
|
||
|
void *BucketNew(bucketset_t set, char *name, BucketType type, void *initialvalue)
|
||
|
{
|
||
|
bucket_t bucket;
|
||
|
|
||
|
if (set == NULL)
|
||
|
set = g_buckets;
|
||
|
assert(set);
|
||
|
bucket.name = goastrdup(name);
|
||
|
bucket.type = type;
|
||
|
bucket.vals.sval = NULL;
|
||
|
bucket.nvals = 1;
|
||
|
DoSet(&bucket, initialvalue);
|
||
|
TableEnter(set->buckets,&bucket);
|
||
|
return DoGet(DoFind(set, name));
|
||
|
}
|
||
|
|
||
|
void *BucketSet(bucketset_t set, char *name,void *value)
|
||
|
{
|
||
|
bucket_t *pbucket = DoFind(set, name);
|
||
|
if (!pbucket)
|
||
|
return NULL;
|
||
|
|
||
|
pbucket->nvals = 0;
|
||
|
return DoSet(pbucket,value);
|
||
|
}
|
||
|
|
||
|
void *BucketGet(bucketset_t set, char *name)
|
||
|
{
|
||
|
return DoGet(DoFind(set,name));
|
||
|
}
|
||
|
|
||
|
void *BucketAdd(bucketset_t set, char *name, void *value)
|
||
|
{
|
||
|
bucket_t *pbucket = DoFind(set, name);
|
||
|
if (!pbucket)
|
||
|
return NULL;
|
||
|
|
||
|
if (pbucket->type == bt_int)
|
||
|
return DoSet(pbucket, bint( (*(int *)DoGet(pbucket)) + (*(int *)value)));
|
||
|
if (pbucket->type == bt_float)
|
||
|
return DoSet(pbucket, bfloat( (*(double *)DoGet(pbucket)) + (*(double *)value)));
|
||
|
//else, string -- just concat
|
||
|
return BucketConcat(set, name, value);
|
||
|
}
|
||
|
|
||
|
void *BucketSub(bucketset_t set, char *name, void *value)
|
||
|
{
|
||
|
bucket_t *pbucket = DoFind(set, name);
|
||
|
if (!pbucket)
|
||
|
return NULL;
|
||
|
|
||
|
if (pbucket->type == bt_int)
|
||
|
return DoSet(pbucket, bint( (*(int *)DoGet(pbucket)) - (*(int *)value)));
|
||
|
if (pbucket->type == bt_float)
|
||
|
return DoSet(pbucket, bfloat( (*(double *)DoGet(pbucket)) - (*(double *)value)));
|
||
|
//else, string -- just ignore
|
||
|
return DoGet(pbucket);
|
||
|
|
||
|
}
|
||
|
|
||
|
void *BucketMult(bucketset_t set, char *name, void *value)
|
||
|
{
|
||
|
bucket_t *pbucket = DoFind(set, name);
|
||
|
if (!pbucket)
|
||
|
return NULL;
|
||
|
|
||
|
if (pbucket->type == bt_int)
|
||
|
return DoSet(pbucket, bint( (*(int *)DoGet(pbucket)) * (*(int *)value)));
|
||
|
if (pbucket->type == bt_float)
|
||
|
return DoSet(pbucket, bfloat( (*(double *)DoGet(pbucket)) * (*(double *)value)));
|
||
|
//else, string -- just ignore
|
||
|
return DoGet(pbucket);
|
||
|
}
|
||
|
|
||
|
void *BucketDiv(bucketset_t set, char *name, void *value)
|
||
|
{
|
||
|
bucket_t *pbucket = DoFind(set, name);
|
||
|
if (!pbucket)
|
||
|
return NULL;
|
||
|
|
||
|
if (pbucket->type == bt_int)
|
||
|
return DoSet(pbucket, bint( (*(int *)DoGet(pbucket)) / (*(int *)value)));
|
||
|
if (pbucket->type == bt_float)
|
||
|
return DoSet(pbucket, bfloat( (*(double *)DoGet(pbucket)) / (*(double *)value)));
|
||
|
//else, string -- just ignore
|
||
|
return DoGet(pbucket);
|
||
|
}
|
||
|
|
||
|
void *BucketConcat(bucketset_t set, char *name, void *value)
|
||
|
{
|
||
|
bucket_t *pbucket = DoFind(set, name);
|
||
|
char *temp, *s;
|
||
|
if (!pbucket)
|
||
|
return NULL;
|
||
|
|
||
|
assert(pbucket->type == bt_string);
|
||
|
s = DoGet(pbucket);
|
||
|
temp = (char *)gsimalloc(strlen(s) + strlen(value) + 1);
|
||
|
strcpy(temp,s);
|
||
|
strcat(temp, value);
|
||
|
|
||
|
DoSet(pbucket, temp);
|
||
|
gsifree(temp);
|
||
|
|
||
|
return DoGet(pbucket);
|
||
|
}
|
||
|
|
||
|
#define AVG(cur, new, num) (((cur * num) + new) / (++num))
|
||
|
void *BucketAvg(bucketset_t set, char *name, void *value)
|
||
|
{
|
||
|
bucket_t *pbucket = DoFind(set, name);
|
||
|
if (!pbucket)
|
||
|
return NULL;
|
||
|
|
||
|
if (pbucket->type == bt_int)
|
||
|
return DoSet(pbucket, bint( AVG((*(int *)DoGet(pbucket)), (*(int *)value), pbucket->nvals)));
|
||
|
if (pbucket->type == bt_float)
|
||
|
return DoSet(pbucket, bfloat( AVG((*(double *)DoGet(pbucket)), (*(double *)value), pbucket->nvals)));
|
||
|
//else, string -- just ignore
|
||
|
return DoGet(pbucket);
|
||
|
}
|
||
|
|
||
|
/* Note: these are NOT thread safe! */
|
||
|
void *bint(int i)
|
||
|
{
|
||
|
static int j;
|
||
|
j=i;
|
||
|
return &j;
|
||
|
}
|
||
|
|
||
|
void *bfloat(double f)
|
||
|
{
|
||
|
static double g;
|
||
|
g=f;
|
||
|
return &g;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/***********
|
||
|
* UTILITY FUNCTIONS
|
||
|
**********/
|
||
|
static void DumpMap(void *elem, void *clientData)
|
||
|
{
|
||
|
bucket_t *bucket = (bucket_t *)elem;
|
||
|
dumpdata_t *data = (dumpdata_t *)clientData;
|
||
|
unsigned int minlen;
|
||
|
|
||
|
//find out if we need to resize!
|
||
|
minlen = strlen(bucket->name) + 3;
|
||
|
if (bucket->type == bt_int || bucket->type == bt_float)
|
||
|
minlen += data->len + 16;
|
||
|
else if (bucket->type == bt_string)
|
||
|
minlen += data->len + strlen(bucket->vals.sval);
|
||
|
|
||
|
if (data->maxlen <= minlen)
|
||
|
{
|
||
|
if (data->maxlen == 0)
|
||
|
data->maxlen = minlen * 2;
|
||
|
else
|
||
|
data->maxlen *= 2;
|
||
|
data->data = gsirealloc(data->data, data->maxlen);
|
||
|
|
||
|
}
|
||
|
|
||
|
switch (bucket->type)
|
||
|
{
|
||
|
case bt_int:
|
||
|
data->len += (unsigned int)sprintf(data->data + data->len,"\\%s\\%d",bucket->name,bucket->vals.ival);
|
||
|
break;
|
||
|
case bt_float:
|
||
|
data->len += (unsigned int)sprintf(data->data + data->len,"\\%s\\%f",bucket->name,bucket->vals.fval);
|
||
|
break;
|
||
|
case bt_string:
|
||
|
data->len += (unsigned int)sprintf(data->data + data->len,"\\%s\\%s",bucket->name,bucket->vals.sval);
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static char *stripchars(char *s)
|
||
|
{
|
||
|
char *p = s;
|
||
|
while (*s)
|
||
|
{
|
||
|
if (*s == '\\')
|
||
|
*s = '/';
|
||
|
s++;
|
||
|
}
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
static void *DoSet(bucket_t *pbucket, void *value)
|
||
|
{
|
||
|
if (pbucket->type == bt_int)
|
||
|
pbucket->vals.ival = *(int*)value;
|
||
|
else if (pbucket->type == bt_float)
|
||
|
pbucket->vals.fval = *(double *)value;
|
||
|
else if (pbucket->type == bt_string)
|
||
|
{
|
||
|
if (pbucket->vals.sval != NULL)
|
||
|
gsifree(pbucket->vals.sval);
|
||
|
pbucket->vals.sval = (value == NULL ? NULL : stripchars(goastrdup((char *)value)));
|
||
|
}
|
||
|
return DoGet(pbucket);
|
||
|
}
|
||
|
|
||
|
static void *DoGet(bucket_t *pbucket)
|
||
|
{
|
||
|
if (!pbucket)
|
||
|
return NULL;
|
||
|
if (pbucket->type == bt_string)
|
||
|
return pbucket->vals.sval;
|
||
|
else //since it's a union, we can return any member
|
||
|
return &pbucket->vals.sval;
|
||
|
|
||
|
}
|
||
|
|
||
|
static bucket_t *DoFind(bucketset_t set, char *name)
|
||
|
{
|
||
|
bucket_t tbucket;
|
||
|
|
||
|
if (set == NULL)
|
||
|
set = g_buckets;
|
||
|
assert(set);
|
||
|
|
||
|
tbucket.name = name;
|
||
|
return (bucket_t *)TableLookup(set->buckets,&tbucket);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* NonTermHash
|
||
|
* ----------
|
||
|
* The hash code is computed using a method called "linear congruence."
|
||
|
* This hash function has the additional feature of being case-insensitive,
|
||
|
*/
|
||
|
#define MULTIPLIER -1664117991
|
||
|
static int BucketHash(const void *elem, int numbuckets)
|
||
|
{
|
||
|
unsigned int i;
|
||
|
unsigned int len;
|
||
|
unsigned int hashcode = 0;
|
||
|
|
||
|
char *s = ((bucket_t *)elem)->name;
|
||
|
len = strlen(s);
|
||
|
for (i = 0; i < len ; i++) {
|
||
|
hashcode = (unsigned int)((int)hashcode * MULTIPLIER + tolower(s[i]));
|
||
|
}
|
||
|
return (int)(hashcode % numbuckets);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* CaseInsensitiveCompare
|
||
|
* ----------------------
|
||
|
* Comparison function passed to qsort to sort an array of
|
||
|
* strings in alphabetical order. It uses strcasecmp which is
|
||
|
* identical to strcmp, except that it doesn't consider case of the
|
||
|
* characters when comparing them, thus it sorts case-insensitively.
|
||
|
*/
|
||
|
static int CaseInsensitiveCompare(const void *entry1, const void *entry2)
|
||
|
{
|
||
|
return strcasecmp(*(char **)entry1,*(char **)entry2);
|
||
|
}
|
||
|
|
||
|
/* keyval
|
||
|
* Compares two buckets (case insensative)
|
||
|
*/
|
||
|
static int BucketCompare(const void *entry1, const void *entry2)
|
||
|
{
|
||
|
|
||
|
return CaseInsensitiveCompare(&((bucket_t *)entry1)->name,
|
||
|
&((bucket_t *)entry2)->name);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* KeyValFree
|
||
|
* Frees the memory INSIDE a Bucket structure
|
||
|
*/
|
||
|
static void BucketFree(void *elem)
|
||
|
{
|
||
|
gsifree(((bucket_t *)elem)->name);
|
||
|
if (((bucket_t *)elem)->type == bt_string && ((bucket_t *)elem)->vals.sval != NULL)
|
||
|
gsifree(((bucket_t *)elem)->vals.sval);
|
||
|
|
||
|
}
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
}
|
||
|
#endif
|