root/branches/volker_dev/hash.c

Revision 728, 11.7 kB (checked in by michael, 2 years ago)

changed $Revision to $Rev

  • Property svn:keywords set to Id URL Rev
Line 
1/* $Id$
2 * $URL$
3 *
4 * hashes (associative arrays)
5 *
6 * Copyright (C) 2003 Michael Reinelt <reinelt@eunet.at>
7 * Copyright (C) 2004 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
8 *
9 * This file is part of LCD4Linux.
10 *
11 * LCD4Linux is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2, or (at your option)
14 * any later version.
15 *
16 * LCD4Linux is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 */
26
27/*
28 * exported functions:
29 *
30 * void hash_create (HASH *Hash);
31 *   initializes hash
32 *
33 * int hash_age (HASH *Hash, char *key, char **value);
34 *   return time of last hash_put
35 *
36 * void hash_put (HASH *Hash, char *key, char *val);
37 *   set an entry in the hash
38 *
39 * void hash_put_delta (HASH *Hash, char *key, char *val);
40 *   set a delta entry in the hash
41 *
42 * char *hash_get (HASH *Hash, char *key);
43 *   fetch an entry from the hash
44 *
45 * double hash_get_delta (HASH *Hash, char *key, int delay);
46 *   fetch a delta antry from the hash
47 *
48 * double hash_get_regex (HASH *Hash, char *key, int delay);
49 *   fetch one or more entries from the hash
50 *
51 * void hash_destroy (HASH *Hash);
52 *   releases hash
53 *
54 */
55
56
57#include "config.h"
58
59#include <stdlib.h>
60#include <stdio.h>
61#include <string.h>
62#include <regex.h>
63
64#include "debug.h"
65#include "hash.h"
66
67#ifdef WITH_DMALLOC
68#include <dmalloc.h>
69#endif
70
71/* number of slots for delta processing */
72#define DELTA_SLOTS 64
73
74/* string buffer chunk size */
75#define CHUNK_SIZE 16
76
77
78/* initialize a new hash table */
79void hash_create(HASH * Hash)
80{
81    Hash->sorted = 0;
82
83    Hash->timestamp.tv_sec = 0;
84    Hash->timestamp.tv_usec = 0;
85
86    Hash->nItems = 0;
87    Hash->Items = NULL;
88
89    Hash->nColumns = 0;
90    Hash->Columns = NULL;
91
92    Hash->delimiter = strdup(" \t\n");
93}
94
95
96/* bsearch compare function for hash items */
97static int hash_lookup_item(const void *a, const void *b)
98{
99    char *key = (char *) a;
100    HASH_ITEM *item = (HASH_ITEM *) b;
101
102    return strcasecmp(key, item->key);
103}
104
105
106/* qsort compare function for hash items */
107static int hash_sort_item(const void *a, const void *b)
108{
109    HASH_ITEM *ha = (HASH_ITEM *) a;
110    HASH_ITEM *hb = (HASH_ITEM *) b;
111
112    return strcasecmp(ha->key, hb->key);
113}
114
115
116/* bsearch compare function for hash headers */
117static int hash_lookup_column(const void *a, const void *b)
118{
119    char *key = (char *) a;
120    HASH_COLUMN *column = (HASH_COLUMN *) b;
121
122    return strcasecmp(key, column->key);
123}
124
125
126/* qsort compare function for hash headers */
127static int hash_sort_column(const void *a, const void *b)
128{
129    HASH_COLUMN *ha = (HASH_COLUMN *) a;
130    HASH_COLUMN *hb = (HASH_COLUMN *) b;
131
132    return strcasecmp(ha->key, hb->key);
133}
134
135
136/* split a value into columns and  */
137/* return the nth column in a string */
138/* WARNING: does return a pointer to a static string!! */
139static char *split(const char *val, const int column, const char *delimiter)
140{
141    static char buffer[256];
142    int num;
143    size_t len;
144    const char *beg, *end;
145
146    if (column < 0)
147  return (char *) val;
148    if (val == NULL)
149  return NULL;
150
151    num = 0;
152    len = 0;
153    beg = val;
154    end = beg;
155    while (beg && *beg) {
156  while (strchr(delimiter, *beg))
157      beg++;
158  end = strpbrk(beg, delimiter);
159  if (num++ == column)
160      break;
161  beg = end ? end + 1 : NULL;
162    }
163    if (beg != NULL) {
164  len = end ? (size_t) (end - beg) : strlen(beg);
165  if (len >= sizeof(buffer))
166      len = sizeof(buffer) - 1;
167  strncpy(buffer, beg, len);
168    }
169
170    buffer[len] = '\0';
171    return buffer;
172}
173
174
175/* search an entry in the hash table: */
176/* If the table is flagged "sorted", the entry is looked */
177/* up using the bsearch function. If the table is  */
178/* unsorted, it will be searched in a linear way */
179static HASH_ITEM *hash_lookup(HASH * Hash, const char *key, const int do_sort)
180{
181    HASH_ITEM *Item = NULL;
182
183    /* maybe sort the array */
184    if (do_sort && !Hash->sorted) {
185  qsort(Hash->Items, Hash->nItems, sizeof(HASH_ITEM), hash_sort_item);
186  Hash->sorted = 1;
187    }
188
189    /* no key was passed */
190    if (key == NULL)
191  return NULL;
192
193    /* lookup using bsearch */
194    if (Hash->sorted) {
195  Item = bsearch(key, Hash->Items, Hash->nItems, sizeof(HASH_ITEM), hash_lookup_item);
196    }
197
198    /* linear search */
199    if (Item == NULL) {
200  int i;
201  for (i = 0; i < Hash->nItems; i++) {
202      if (strcmp(key, Hash->Items[i].key) == 0) {
203    Item = &(Hash->Items[i]);
204    break;
205      }
206  }
207    }
208
209    return Item;
210
211}
212
213
214/* return the age in milliseconds of an entry from the hash table */
215/* or from the hash table itself if key is NULL */
216/* returns -1 if entry does not exist */
217int hash_age(HASH * Hash, const char *key)
218{
219    HASH_ITEM *Item;
220    struct timeval now, *timestamp;
221
222    if (key == NULL) {
223  timestamp = &(Hash->timestamp);
224    } else {
225  Item = hash_lookup(Hash, key, 1);
226  if (Item == NULL)
227      return -1;
228  timestamp = &(Item->Slot[Item->index].timestamp);
229    }
230
231    gettimeofday(&now, NULL);
232
233    return (now.tv_sec - timestamp->tv_sec) * 1000 + (now.tv_usec - timestamp->tv_usec) / 1000;
234}
235
236
237/* add an entry to the column header table */
238void hash_set_column(HASH * Hash, const int number, const char *column)
239{
240    if (Hash == NULL)
241  return;
242
243    Hash->nColumns++;
244    Hash->Columns = realloc(Hash->Columns, Hash->nColumns * sizeof(HASH_COLUMN));
245    Hash->Columns[Hash->nColumns - 1].key = strdup(column);
246    Hash->Columns[Hash->nColumns - 1].val = number;
247
248    qsort(Hash->Columns, Hash->nColumns, sizeof(HASH_COLUMN), hash_sort_column);
249
250}
251
252
253/* fetch a column number by column header */
254static int hash_get_column(HASH * Hash, const char *key)
255{
256    HASH_COLUMN *Column;
257
258    if (key == NULL || *key == '\0')
259  return -1;
260
261    Column = bsearch(key, Hash->Columns, Hash->nColumns, sizeof(HASH_COLUMN), hash_lookup_column);
262    if (Column == NULL)
263  return -1;
264
265    return Column->val;
266}
267
268
269/* set column delimiters */
270void hash_set_delimiter(HASH * Hash, const char *delimiter)
271{
272    if (Hash->delimiter != NULL)
273  free(Hash->delimiter);
274    Hash->delimiter = strdup(delimiter);
275}
276
277
278/* get a string from the hash table */
279char *hash_get(HASH * Hash, const char *key, const char *column)
280{
281    HASH_ITEM *Item;
282    int c;
283
284    Item = hash_lookup(Hash, key, 1);
285    if (Item == NULL)
286  return NULL;
287
288    c = hash_get_column(Hash, column);
289    return split(Item->Slot[Item->index].value, c, Hash->delimiter);
290}
291
292
293/* get a delta value from the delta table */
294double hash_get_delta(HASH * Hash, const char *key, const char *column, const int delay)
295{
296    HASH_ITEM *Item;
297    HASH_SLOT *Slot1, *Slot2;
298    int i, c;
299    double v1, v2;
300    double dv, dt;
301    struct timeval now, end;
302
303    /* lookup item */
304    Item = hash_lookup(Hash, key, 1);
305    if (Item == NULL)
306  return 0.0;
307
308    /* this is the "current" Slot */
309    Slot1 = &(Item->Slot[Item->index]);
310
311    /* fetch column number */
312    c = hash_get_column(Hash, column);
313
314    /* if delay is zero, return absolute value */
315    if (delay == 0)
316  return atof(split(Slot1->value, c, Hash->delimiter));
317
318    /* prepare timing values */
319    now = Slot1->timestamp;
320    end.tv_sec = now.tv_sec;
321    end.tv_usec = now.tv_usec - 1000 * delay;
322    while (end.tv_usec < 0) {
323  end.tv_sec--;
324  end.tv_usec += 1000000;
325    }
326
327    /* search delta slot */
328    Slot2 = &(Item->Slot[Item->index]);
329    for (i = 1; i < Item->nSlot; i++) {
330  Slot2 = &(Item->Slot[(Item->index + i) % Item->nSlot]);
331  if (Slot2->timestamp.tv_sec == 0)
332      break;
333  if (timercmp(&(Slot2->timestamp), &end, <))
334      break;
335    }
336
337    /* empty slot => try the one before */
338    if (Slot2->timestamp.tv_sec == 0) {
339  i--;
340  Slot2 = &(Item->Slot[(Item->index + i) % Item->nSlot]);
341    }
342
343    /* not enough slots available... */
344    if (i == 0)
345  return 0.0;
346
347    /* delta value, delta time */
348    v1 = atof(split(Slot1->value, c, Hash->delimiter));
349    v2 = atof(split(Slot2->value, c, Hash->delimiter));
350    dv = v1 - v2;
351    dt = (Slot1->timestamp.tv_sec - Slot2->timestamp.tv_sec)
352  + (Slot1->timestamp.tv_usec - Slot2->timestamp.tv_usec) / 1000000.0;
353
354    if (dt > 0.0 && dv >= 0.0)
355  return dv / dt;
356    return 0.0;
357}
358
359
360/* get a delta value from the delta table */
361/* key may contain regular expressions, and the sum  */
362/* of all matching entries is returned. */
363double hash_get_regex(HASH * Hash, const char *key, const char *column, const int delay)
364{
365    double sum;
366    regex_t preg;
367    int i, err;
368
369    err = regcomp(&preg, key, REG_ICASE | REG_NOSUB);
370    if (err != 0) {
371  char buffer[32];
372  regerror(err, &preg, buffer, sizeof(buffer));
373  error("error in regular expression: %s", buffer);
374  regfree(&preg);
375  return 0.0;
376    }
377
378    /* force the table to be sorted by requesting anything */
379    hash_lookup(Hash, NULL, 1);
380
381    sum = 0.0;
382    for (i = 0; i < Hash->nItems; i++) {
383  if (regexec(&preg, Hash->Items[i].key, 0, NULL, 0) == 0) {
384      sum += hash_get_delta(Hash, Hash->Items[i].key, column, delay);
385  }
386    }
387    regfree(&preg);
388    return sum;
389}
390
391
392/* insert a key/val pair into the hash table */
393/* If the entry does already exist, it will be overwritten, */
394/* and the table stays sorted (if it has been before). */
395/* Otherwise, the entry is appended at the end, and  */
396/* the table will be flagged 'unsorted' afterwards */
397
398static HASH_ITEM *hash_set(HASH * Hash, const char *key, const char *value, const int delta)
399{
400    HASH_ITEM *Item;
401    HASH_SLOT *Slot;
402    int size;
403
404    Item = hash_lookup(Hash, key, 0);
405
406    if (Item == NULL) {
407
408  /* add entry */
409  Hash->sorted = 0;
410  Hash->nItems++;
411  Hash->Items = realloc(Hash->Items, Hash->nItems * sizeof(HASH_ITEM));
412
413  Item = &(Hash->Items[Hash->nItems - 1]);
414  Item->key = strdup(key);
415  Item->index = 0;
416  Item->nSlot = delta;
417  Item->Slot = malloc(Item->nSlot * sizeof(HASH_SLOT));
418  memset(Item->Slot, 0, Item->nSlot * sizeof(HASH_SLOT));
419
420    } else {
421
422  /* maybe enlarge delta table */
423  if (Item->nSlot < delta) {
424      Item->nSlot = delta;
425      Item->Slot = realloc(Item->Slot, Item->nSlot * sizeof(HASH_SLOT));
426  }
427
428    }
429
430    if (Item->nSlot > 1) {
431  /* move the pointer to the next free slot, wrap around if necessary */
432  if (--Item->index < 0)
433      Item->index = Item->nSlot - 1;
434    }
435
436    /* create entry */
437    Slot = &(Item->Slot[Item->index]);
438    size = strlen(value) + 1;
439
440    /* maybe enlarge value buffer */
441    if (size > Slot->size) {
442  /* buffer is either empty or too small */
443  /* allocate memory in multiples of CHUNK_SIZE */
444  Slot->size = CHUNK_SIZE * (size / CHUNK_SIZE + 1);
445  Slot->value = realloc(Slot->value, Slot->size);
446    }
447
448    /* set value */
449    strcpy(Slot->value, value);
450
451    /* set timestamps */
452    gettimeofday(&(Hash->timestamp), NULL);
453    Slot->timestamp = Hash->timestamp;
454
455    return Item;
456}
457
458
459/* insert a string into the hash table */
460/* without delta processing */
461void hash_put(HASH * Hash, const char *key, const char *value)
462{
463    hash_set(Hash, key, value, 1);
464}
465
466
467/* insert a string into the hash table */
468/* with delta processing */
469void hash_put_delta(HASH * Hash, const char *key, const char *value)
470{
471    hash_set(Hash, key, value, DELTA_SLOTS);
472}
473
474
475void hash_destroy(HASH * Hash)
476{
477    int i;
478
479    if (Hash->Items) {
480
481  /* free all headers */
482  for (i = 0; i < Hash->nColumns; i++) {
483      if (Hash->Columns[i].key)
484    free(Hash->Columns[i].key);
485  }
486
487  /* free header table */
488  free(Hash->Columns);
489
490  /* free all items */
491  for (i = 0; i < Hash->nItems; i++) {
492      if (Hash->Items[i].key)
493    free(Hash->Items[i].key);
494      if (Hash->Items[i].Slot)
495    free(Hash->Items[i].Slot);
496  }
497
498  /* free items table */
499  free(Hash->Items);
500    }
501
502    Hash->sorted = 0;
503    Hash->nItems = 0;
504    Hash->Items = NULL;
505}
Note: See TracBrowser for help on using the browser.