File: hashid.c

package info (click to toggle)
yorick 2.2.02%2Bdfsg-6
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 9,456 kB
  • sloc: ansic: 85,382; sh: 1,665; cpp: 1,282; lisp: 1,231; makefile: 1,028; fortran: 19
file content (178 lines) | stat: -rw-r--r-- 4,163 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/*
 * $Id: hashid.c,v 1.1 2005-09-18 22:05:42 dhmunro Exp $
 * global name string to id number correspondence
 */
/* Copyright (c) 2005, The Regents of the University of California.
 * All rights reserved.
 * This file is part of yorick (http://yorick.sourceforge.net).
 * Read the accompanying LICENSE file for details.
 */

#include "config.h"
#include "phash.h"
#include "pstdlib.h"

#include <string.h>

typedef struct id_name id_name;
struct id_name {
  union {
    char *ame;
    id_name *ext;
  } n;
  /* uses >= 0 indicates number of additional uses, ordinary case
   * uses ==-1 indicates idstatic, n.ame will never be freed
   * uses <=-2 indicates locked id_name struct, but
   *           n.ame can still be freed (true uses = -2-uses) */
  long uses;
};

static p_hashtab *id_table = 0;
static id_name *id_freelist = 0;
static id_name id_null;
extern int p_id_collisions; 
int p_id_collisions = 0;  /* expect to be tiny */

static p_hashkey id_hash(const char *name, int len);
static id_name *id_block(void);
static int idnm_compare(const char *n1, const char *n2, int len);

p_hashkey
p_id(const char *name, int len)
{
  p_hashkey h = id_hash(name, len);
  if (id_table) {
    id_name *idnm;
    p_hashkey rehash = h&0xfff;

    for (;;) {
      if (!h) h = 1;
      idnm = p_hfind(id_table, h);
      if (!idnm || !idnm->n.ame) return 0;
      if (!idnm_compare(name, idnm->n.ame, len)) return h;
      if (!rehash) rehash = 3691;
      h += rehash;
    }
  }
  return 0;
}

p_hashkey
p_idmake(const char *name, int len)
{
  id_name *idnm = 0;
  p_hashkey h = id_hash(name, len);
  p_hashkey rehash = h&0xfff;

  if (!id_table) {
    id_table = p_halloc(64);
    id_null.n.ame = 0;
    id_null.uses = -1;
    p_hinsert(id_table, 0, &id_null);
  }

  for (;;) {
    if (!h) h = 1;
    idnm = p_hfind(id_table, h);
    if (!idnm || !idnm->n.ame) break;
    if (!idnm_compare(name, idnm->n.ame, len)) {
      if (idnm->uses>=0) idnm->uses++;
      else if (idnm->uses<-1) idnm->uses--;
      return h;
    }
    if (!rehash) rehash = 3691;
    h += rehash;
    /* a collision locks the hashkey and id_name struct forever */
    if (idnm->uses>=0) {
      idnm->uses = -2-idnm->uses;
      p_id_collisions++;
    }
  }

  if (!idnm) {
    idnm = id_freelist;
    if (!idnm) idnm = id_block();
    idnm->uses = 0;
    id_freelist = idnm->n.ext;
    idnm->n.ame = len>0? p_strncat(0, name, len) : p_strcpy(name);
    p_hinsert(id_table, h, idnm);
  } else {
    idnm->n.ame = len>0? p_strncat(0, name, len) : p_strcpy(name);
  }

  return h;
}

static int
idnm_compare(const char *n1, const char *n2, int len)
{
  if (len) return strncmp(n1, n2, len) || n2[len];
  else     return strcmp(n1, n2);
}

p_hashkey
p_idstatic(char *name)
{
  p_hashkey id = p_idmake(name, 0);
  id_name *idnm = p_hfind(id_table, id);  /* never 0 or idnm->n.ame==0 */
  if (idnm->uses>=0) {
    char *nm = idnm->n.ame;
    idnm->uses = -1;
    idnm->n.ame = name;
    p_free(nm);
  }
  return id;
}

void
p_idfree(p_hashkey id)
{
  if (id_table) {
    id_name *idnm = p_hfind(id_table, id);
    if (idnm && idnm->n.ame) {
      if (!idnm->uses) {
        char *name = idnm->n.ame;
        p_hinsert(id_table, id, (void *)0);
        idnm->n.ext = id_freelist;
        id_freelist = idnm;
        p_free(name);
      } else if (idnm->uses>0) {
        idnm->uses--;
      } else if (idnm->uses==-2) {
        char *name = idnm->n.ame;
        idnm->n.ame = 0;
        p_free(name);
      } else if (idnm->uses<-2) {
        idnm->uses++;
      }
    }
  }
}

char *
p_idname(p_hashkey id)
{
  id_name *idnm = id_table? p_hfind(id_table, id) : 0;
  return idnm? idnm->n.ame : 0;
}

static id_name *
id_block(void)
{
  int i, n = 128;
  id_name *idnm = p_malloc(sizeof(id_name)*n);
  for (i=0 ; i<n-1 ; i++) idnm[i].n.ext = &idnm[i+1];
  idnm[i].n.ext = 0;
  return id_freelist = idnm;
}

static p_hashkey
id_hash(const char *name, int len)
{
  p_hashkey h = 0x34da3723;  /* random bits */
  if (len) for (; len && name[0] ; len--,name++)
    h = ((h<<8) + (h>>1)) ^ (unsigned char)name[0];
  else while (*name)
    h = ((h<<8) + (h>>1)) ^ (unsigned char)(*name++);
  return h;
}