mirror of
https://github.com/netfun2000/lcd4linux.git
synced 2026-02-27 09:44:34 +08:00
big patch from Martin Hejl: - use qprintf() where appropriate - save CPU cycles on gettimeofday() - add quit() functions to free allocated memory - fixed lots of memory leaks git-svn-id: https://ssl.bulix.org/svn/lcd4linux/trunk@384 3ae390bd-cb1e-0410-b409-cd5a39f66f1f
407 lines
9.9 KiB
C
407 lines
9.9 KiB
C
/* $Id: hash.c,v 1.14 2004/03/03 03:47:04 reinelt Exp $
|
|
*
|
|
* hashes (associative arrays)
|
|
*
|
|
* Copyright 1999-2003 Michael Reinelt <reinelt@eunet.at>
|
|
* Copyright 2004 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
|
|
*
|
|
* This file is part of LCD4Linux.
|
|
*
|
|
* LCD4Linux 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, or (at your option)
|
|
* any later version.
|
|
*
|
|
* LCD4Linux 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 the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*
|
|
* $Log: hash.c,v $
|
|
* Revision 1.14 2004/03/03 03:47:04 reinelt
|
|
* big patch from Martin Hejl:
|
|
* - use qprintf() where appropriate
|
|
* - save CPU cycles on gettimeofday()
|
|
* - add quit() functions to free allocated memory
|
|
* - fixed lots of memory leaks
|
|
*
|
|
* Revision 1.13 2004/02/27 06:07:55 reinelt
|
|
* hash improvements from Martin
|
|
*
|
|
* Revision 1.12 2004/01/30 20:57:56 reinelt
|
|
* HD44780 patch from Martin Hejl
|
|
* dmalloc integrated
|
|
*
|
|
* Revision 1.11 2004/01/29 04:40:02 reinelt
|
|
* every .c file includes "config.h" now
|
|
*
|
|
* Revision 1.10 2004/01/27 04:48:57 reinelt
|
|
* bug with hash_age() fixed (thanks to Markus Keil for pointing this out)
|
|
*
|
|
* Revision 1.9 2004/01/22 07:57:45 reinelt
|
|
* several bugs fixed where segfaulting on layout>display
|
|
* Crystalfontz driver optimized, 632 display already works
|
|
*
|
|
* Revision 1.8 2004/01/21 14:29:03 reinelt
|
|
* new helper 'hash_get_regex' which delivers the sum over regex matched items
|
|
* new function 'disk()' which uses this regex matching
|
|
*
|
|
* Revision 1.7 2004/01/21 10:48:17 reinelt
|
|
* hash_age function added
|
|
*
|
|
* Revision 1.6 2004/01/20 05:36:59 reinelt
|
|
* moved text-display-specific stuff to drv_generic_text
|
|
* moved all the bar stuff from drv_generic_bar to generic_text
|
|
*
|
|
* Revision 1.5 2004/01/18 09:01:45 reinelt
|
|
* /proc/stat parsing finished
|
|
*
|
|
* Revision 1.4 2004/01/18 06:54:08 reinelt
|
|
* bug in expr.c fixed (thanks to Xavier)
|
|
* some progress with /proc/stat parsing
|
|
*
|
|
* Revision 1.3 2004/01/16 07:26:25 reinelt
|
|
* moved various /proc parsing to own functions
|
|
* made some progress with /proc/stat parsing
|
|
*
|
|
* Revision 1.2 2004/01/16 05:04:53 reinelt
|
|
* started plugin proc_stat which should parse /proc/stat
|
|
* which again is a paint in the a**
|
|
* thinking over implementation methods of delta functions
|
|
* (CPU load, ...)
|
|
*
|
|
* Revision 1.1 2004/01/13 10:03:01 reinelt
|
|
* new util 'hash' for associative arrays
|
|
* new plugin 'cpuinfo'
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* exported functions:
|
|
*
|
|
* hash_anything
|
|
* Fixme: document me!
|
|
*
|
|
*/
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <regex.h>
|
|
//#include <sys/types.h>
|
|
|
|
#include "debug.h"
|
|
#include "hash.h"
|
|
|
|
#ifdef WITH_DMALLOC
|
|
#include <dmalloc.h>
|
|
#endif
|
|
|
|
|
|
#define DELTA_SLOTS 64
|
|
|
|
static timeval __my_time_of_day;
|
|
|
|
int gettimeofday_ex(timeval *tv, struct timezone *tz) {
|
|
if (tv==NULL) return -1;
|
|
|
|
if (__my_time_of_day.tv_usec == 0 && __my_time_of_day.tv_sec==0) {
|
|
gettimeofday_update();
|
|
}
|
|
|
|
tv->tv_sec = __my_time_of_day.tv_sec;
|
|
tv->tv_usec= __my_time_of_day.tv_usec;
|
|
return 0;
|
|
}
|
|
|
|
void gettimeofday_update(){
|
|
gettimeofday(&__my_time_of_day, NULL);
|
|
}
|
|
|
|
|
|
// bsearch compare function for hash entries
|
|
static int hash_lookup_f (const void *a, const void *b)
|
|
{
|
|
char *key=(char*)a;
|
|
HASH_ITEM *item=(HASH_ITEM*)b;
|
|
|
|
return strcmp(key, item->key);
|
|
}
|
|
|
|
|
|
// qsort compare function for hash tables
|
|
static int hash_sort_f (const void *a, const void *b)
|
|
{
|
|
HASH_ITEM *ha=(HASH_ITEM*)a;
|
|
HASH_ITEM *hb=(HASH_ITEM*)b;
|
|
|
|
return strcasecmp(ha->key, hb->key);
|
|
}
|
|
|
|
|
|
// search an entry in the hash table:
|
|
// If the table is flagged "sorted", the entry is looked
|
|
// up using the bsearch function. If the table is
|
|
// unsorted, it will be searched in a linear way
|
|
|
|
static HASH_ITEM* hash_lookup (HASH *Hash, char *key, int sortit)
|
|
{
|
|
HASH_ITEM *Item=NULL;
|
|
|
|
// maybe sort the array
|
|
if (sortit && !Hash->sorted) {
|
|
qsort(Hash->Items, Hash->nItems, sizeof(HASH_ITEM), hash_sort_f);
|
|
Hash->sorted=1;
|
|
}
|
|
|
|
// lookup using bsearch
|
|
if (Hash->sorted) {
|
|
Item=bsearch(key, Hash->Items, Hash->nItems, sizeof(HASH_ITEM), hash_lookup_f);
|
|
}
|
|
|
|
// linear search
|
|
if (Item==NULL) {
|
|
int i;
|
|
for (i=0;i<Hash->nItems; i++) {
|
|
if (strcmp(key, Hash->Items[i].key)==0) {
|
|
Item=&(Hash->Items[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Item;
|
|
|
|
}
|
|
|
|
|
|
// insert a key/val pair into the hash table
|
|
// If the entry does already exist, it will be overwritten,
|
|
// and the table stays sorted (if it has been before).
|
|
// Otherwise, the entry is appended at the end, and
|
|
// the table will be flagged 'unsorted' afterwards
|
|
|
|
static HASH_ITEM* hash_set_string (HASH *Hash, char *key, char *val)
|
|
{
|
|
HASH_ITEM *Item;
|
|
|
|
Item=hash_lookup (Hash, key, 0);
|
|
|
|
// entry already exists?
|
|
if (Item!=NULL) {
|
|
if (Item->val) free (Item->val);
|
|
Item->val = strdup(val);
|
|
goto hash_got_string;
|
|
}
|
|
|
|
// add entry
|
|
Hash->sorted=0;
|
|
Hash->nItems++;
|
|
Hash->Items=realloc(Hash->Items,Hash->nItems*sizeof(HASH_ITEM));
|
|
|
|
Item=&(Hash->Items[Hash->nItems-1]);
|
|
|
|
Item->key = strdup(key);
|
|
Item->val = strdup(val);
|
|
Item->root = 0;
|
|
Item->Slot = NULL;
|
|
|
|
hash_got_string:
|
|
// set timestamps
|
|
gettimeofday_ex(&Hash->time, NULL);
|
|
Item->time=Hash->time;
|
|
|
|
return Item;
|
|
}
|
|
|
|
|
|
// insert a string into the hash table
|
|
void hash_set (HASH *Hash, char *key, char *val)
|
|
{
|
|
hash_set_string (Hash, key, val);
|
|
}
|
|
|
|
|
|
// insert a string into the hash table
|
|
// convert it into a number, and store it in the
|
|
// delta table, too
|
|
void hash_set_delta (HASH *Hash, char *key, char *val)
|
|
{
|
|
double number=atof(val);
|
|
HASH_ITEM *Item;
|
|
|
|
Item=hash_set_string (Hash, key, val);
|
|
|
|
// allocate delta table
|
|
if (Item->Slot==NULL) {
|
|
Item->root = 0;
|
|
Item->Slot = malloc(DELTA_SLOTS*sizeof(HASH_SLOT));
|
|
memset(Item->Slot, 0, DELTA_SLOTS*sizeof(HASH_SLOT));
|
|
}
|
|
|
|
// shift delta table
|
|
// <--- don't move 63 bytes just to insert one byte - this is _very_
|
|
// slow on my Elan SC520 or GEODE SC1100
|
|
//memmove (Item->Slot+1, Item->Slot, (DELTA_SLOTS-1)*sizeof(HASH_SLOT));
|
|
|
|
// move the pointer to the next free slot (wrapping around if necessary
|
|
if (--Item->root < 0) Item->root = DELTA_SLOTS-1;
|
|
|
|
// set first entry
|
|
gettimeofday_ex(&(Item->Slot[Item->root].time), NULL);
|
|
Item->Slot[Item->root].val=number;
|
|
|
|
}
|
|
|
|
|
|
// get a string from the hash table
|
|
char *hash_get (HASH *Hash, char *key)
|
|
{
|
|
HASH_ITEM *Item=hash_lookup(Hash, key, 1);
|
|
return Item?Item->val:NULL;
|
|
}
|
|
|
|
|
|
// return the age in milliseconds of an entry from the hash table
|
|
// or from the hash table itself if key is NULL
|
|
// returns -1 if entry does not exist
|
|
// if **value is set, return the value, too
|
|
int hash_age (HASH *Hash, char *key, char **value)
|
|
{
|
|
HASH_ITEM *Item;
|
|
timeval now, *stamp;
|
|
int age;
|
|
|
|
gettimeofday_update();
|
|
|
|
if (key!=NULL) {
|
|
Item=hash_lookup(Hash, key, 1);
|
|
if (value) *value=Item?Item->val:NULL;
|
|
if (Item==NULL) {
|
|
return -1;
|
|
}
|
|
stamp=&Item->time;
|
|
} else {
|
|
stamp=&Hash->time;
|
|
}
|
|
|
|
gettimeofday_ex(&now, NULL);
|
|
|
|
age = (now.tv_sec - stamp->tv_sec)*1000 + (now.tv_usec - stamp->tv_usec)/1000;
|
|
|
|
return age;
|
|
}
|
|
|
|
|
|
// get a delta value from the delta table
|
|
double hash_get_delta (HASH *Hash, char *key, int delay)
|
|
{
|
|
HASH_ITEM *Item;
|
|
timeval now, end;
|
|
int i;
|
|
double dv, dt;
|
|
HASH_SLOT *pSlot;
|
|
|
|
// lookup item
|
|
Item=hash_lookup(Hash, key, 1);
|
|
if (Item==NULL) return 0.0;
|
|
if (Item->Slot==NULL) return 0.0;
|
|
|
|
// if delay is zero, return absolute value
|
|
if (delay==0) return Item->Slot[Item->root].val;
|
|
|
|
// prepare timing values
|
|
now=Item->Slot[Item->root].time;
|
|
end.tv_sec = now.tv_sec;
|
|
end.tv_usec = now.tv_usec-1000*delay;
|
|
if (end.tv_usec<0) {
|
|
end.tv_sec--;
|
|
end.tv_usec += 1000000;
|
|
}
|
|
|
|
// search delta slot
|
|
for (i=1; i<DELTA_SLOTS; i++) {
|
|
pSlot = &(Item->Slot[(Item->root+i) % DELTA_SLOTS]);
|
|
if (pSlot->time.tv_sec==0) break;
|
|
if (timercmp(&(pSlot->time), &end, <)) break;
|
|
dt = (now.tv_sec - pSlot->time.tv_sec) + (now.tv_usec - pSlot->time.tv_usec)/1000000.0;
|
|
}
|
|
|
|
|
|
// empty slot => use the one before
|
|
if (pSlot->time.tv_sec==0) {
|
|
i--;
|
|
pSlot = &(Item->Slot[(Item->root+i) % DELTA_SLOTS]);
|
|
}
|
|
|
|
// not enough slots available...
|
|
if (i==0) return 0.0;
|
|
|
|
// delta value, delta time
|
|
dv = Item->Slot[Item->root].val - pSlot->val;
|
|
dt = (now.tv_sec - pSlot->time.tv_sec) + (now.tv_usec - pSlot->time.tv_usec)/1000000.0;
|
|
|
|
if (dt > 0.0 && dv >= 0.0) return dv/dt;
|
|
return 0.0;
|
|
}
|
|
|
|
|
|
// get a delta value from the delta table
|
|
// key may contain regular expressions, and the sum
|
|
// of all matching entries is returned.
|
|
double hash_get_regex (HASH *Hash, char *key, int delay)
|
|
{
|
|
double sum=0.0;
|
|
regex_t preg;
|
|
int i, err;
|
|
|
|
err=regcomp(&preg, key, REG_ICASE|REG_NOSUB);
|
|
if (err!=0) {
|
|
char buffer[32];
|
|
regerror(err, &preg, buffer, sizeof(buffer));
|
|
error ("error in regular expression: %s", buffer);
|
|
return 0.0;
|
|
}
|
|
|
|
// force the table to be sorted by requesting anything
|
|
hash_lookup(Hash, "", 1);
|
|
|
|
for (i=0;i<Hash->nItems; i++) {
|
|
if (regexec(&preg, Hash->Items[i].key, 0, NULL, 0)==0) {
|
|
sum+=hash_get_delta(Hash, Hash->Items[i].key, delay);
|
|
}
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
|
|
void hash_destroy (HASH *Hash)
|
|
{
|
|
int i;
|
|
|
|
if (Hash->Items) {
|
|
|
|
// free all entries
|
|
for (i=0;i<Hash->nItems; i++) {
|
|
if (Hash->Items[i].key) free (Hash->Items[i].key);
|
|
if (Hash->Items[i].val) free (Hash->Items[i].val);
|
|
if (Hash->Items[i].Slot) free (Hash->Items[i].Slot);
|
|
}
|
|
|
|
// free hash table
|
|
free (Hash->Items);
|
|
}
|
|
|
|
Hash->nItems=0;
|
|
Hash->sorted=0;
|
|
Hash->Items=NULL;
|
|
}
|