Tor  0.4.6.0-alpha-dev
ht.h
1 /* Copyright (c) 2002, Christopher Clark.
2  * Copyright (c) 2005-2006, Nick Mathewson.
3  * Copyright (c) 2007-2019, The Tor Project, Inc. */
4 /* See license at end. */
5 
6 /* Based on ideas by Christopher Clark and interfaces from Niels Provos. */
7 
8 /*
9  These macros provide an intrustive implementation for a typesafe chaining
10  hash table, loosely based on the BSD tree.h and queue.h macros. Here's
11  how to use them.
12 
13  First, pick a the structure that you'll be storing in the hashtable. Let's
14  say that's "struct dinosaur". To this structure, you add an HT_ENTRY()
15  member, as such:
16 
17  struct dinosaur {
18  HT_ENTRY(dinosaur) node; // The name inside the () must match the
19  // struct.
20 
21  // These are just fields from the dinosaur structure...
22  long dinosaur_id;
23  char *name;
24  long age;
25  int is_ornithischian;
26  int is_herbivorous;
27  };
28 
29  You can declare the hashtable itself as:
30 
31  HT_HEAD(dinosaur_ht, dinosaur);
32 
33  This declares a new 'struct dinosaur_ht' type.
34 
35  Now you need to declare two functions to help implement the hashtable: one
36  compares two dinosaurs for equality, and one computes the hash of a
37  dinosaur. Let's say that two dinosaurs are equal if they have the same ID
38  and name.
39 
40  int
41  dinosaurs_equal(const struct dinosaur *d1, const struct dinosaur *d2)
42  {
43  return d1->dinosaur_id == d2->dinosaur_id &&
44  0 == strcmp(d1->name, d2->name);
45  }
46 
47  unsigned
48  dinosaur_hash(const struct dinosaur *d)
49  {
50  // This is a very bad hash function. Use siphash24g instead.
51  return (d->dinosaur_id + d->name[0] ) * 1337 + d->name[1] * 1337;
52  }
53 
54  Now you'll need to declare the functions that manipulate the hash table.
55  To do this, you put this declaration either in a header file, or inside
56  a regular module, depending on what visibility you want.
57 
58  HT_PROTOTYPE(dinosaur_ht, // The name of the hashtable struct
59  dinosaur, // The name of the element struct,
60  node, // The name of HT_ENTRY member
61  dinosaur_hash, dinosaurs_equal);
62 
63  Later, inside a C function, you use this macro to declare the hashtable
64  functions.
65 
66  HT_GENERATE2(dinosaur_ht, dinosaur, node, dinosaur_hash, dinosaurs_equal,
67  0.6, tor_reallocarray, tor_free_);
68 
69  Note the use of tor_free_, not tor_free. The 0.6 is magic.
70 
71  Now you can use the hashtable! You can initialize one with
72 
73  struct dinosaur_ht my_dinos = HT_INITIALIZER();
74 
75  Or create one in core with
76 
77  struct dinosaur_ht *dinos = tor_malloc(sizeof(dinosaur_ht));
78  HT_INIT(dinosaur_ht, dinos);
79 
80  To the hashtable, you use the HT_FOO(dinosaur_ht, ...) macros. For
81  example, to put new_dino into dinos, you say:
82 
83  HT_REPLACE(dinosaur_ht, dinos, new_dino);
84 
85  If you're searching for an element, you need to use a dummy 'key' element in
86  the search. For example.
87 
88  struct dinosaur dino_key;
89  dino_key.dinosaur_id = 12345;
90  dino_key.name = tor_strdup("Atrociraptor");
91 
92  struct dinosaur *found = HT_FIND(dinosaurs_ht, dinos, &dino_key);
93 
94  Have fun with your hash table!
95 
96  */
97 
98 #ifndef HT_H_INCLUDED_
99 #define HT_H_INCLUDED_
100 
101 #define HT_HEAD(name, type) \
102  struct name { \
103  /* The hash table itself. */ \
104  struct type **hth_table; \
105  /* How long is the hash table? */ \
106  unsigned hth_table_length; \
107  /* How many elements does the table contain? */ \
108  unsigned hth_n_entries; \
109  /* How many elements will we allow in the table before resizing it? */ \
110  unsigned hth_load_limit; \
111  /* Position of hth_table_length in the primes table. */ \
112  int hth_prime_idx; \
113  }
114 
115 #define HT_INITIALIZER() \
116  { NULL, 0, 0, 0, -1 }
117 
118 #ifdef HT_NO_CACHE_HASH_VALUES
119 #define HT_ENTRY(type) \
120  struct { \
121  struct type *hte_next; \
122  }
123 #else
124 #define HT_ENTRY(type) \
125  struct { \
126  struct type *hte_next; \
127  unsigned hte_hash; \
128  }
129 #endif
130 
131 /* || 0 is for -Wparentheses-equality (-Wall?) appeasement under clang */
132 #define HT_EMPTY(head) \
133  (((head)->hth_n_entries == 0) || 0)
134 
135 /* How many elements in 'head'? */
136 #define HT_SIZE(head) \
137  ((head)->hth_n_entries)
138 
139 /* Return memory usage for a hashtable (not counting the entries themselves) */
140 #define HT_MEM_USAGE(head) \
141  (sizeof(*head) + (head)->hth_table_length * sizeof(void*))
142 
143 #define HT_FIND(name, head, elm) name##_HT_FIND((head), (elm))
144 #define HT_INSERT(name, head, elm) name##_HT_INSERT((head), (elm))
145 #define HT_REPLACE(name, head, elm) name##_HT_REPLACE((head), (elm))
146 #define HT_REMOVE(name, head, elm) name##_HT_REMOVE((head), (elm))
147 #define HT_START(name, head) name##_HT_START(head)
148 #define HT_NEXT(name, head, elm) name##_HT_NEXT((head), (elm))
149 #define HT_NEXT_RMV(name, head, elm) name##_HT_NEXT_RMV((head), (elm))
150 #define HT_CLEAR(name, head) name##_HT_CLEAR(head)
151 #define HT_INIT(name, head) name##_HT_INIT(head)
152 #define HT_REP_IS_BAD_(name, head) name##_HT_REP_IS_BAD_(head)
153 #define HT_FOREACH_FN(name, head, fn, data) \
154  name##_HT_FOREACH_FN((head), (fn), (data))
155 /* Helper: */
156 static inline unsigned
157 ht_improve_hash(unsigned h)
158 {
159  /* Aim to protect against poor hash functions by adding logic here
160  * - logic taken from java 1.4 hashtable source */
161  h += ~(h << 9);
162  h ^= ((h >> 14) | (h << 18)); /* >>> */
163  h += (h << 4);
164  h ^= ((h >> 10) | (h << 22)); /* >>> */
165  return h;
166 }
167 
168 #if 0
169 /** Basic string hash function, from Java standard String.hashCode(). */
170 static inline unsigned
171 ht_string_hash(const char *s)
172 {
173  unsigned h = 0;
174  int m = 1;
175  while (*s) {
176  h += ((signed char)*s++)*m;
177  m = (m<<5)-1; /* m *= 31 */
178  }
179  return h;
180 }
181 #endif
182 
183 #if 0
184 /** Basic string hash function, from Python's str.__hash__() */
185 static inline unsigned
186 ht_string_hash(const char *s)
187 {
188  unsigned h;
189  const unsigned char *cp = (const unsigned char *)s;
190  h = *cp << 7;
191  while (*cp) {
192  h = (1000003*h) ^ *cp++;
193  }
194  /* This conversion truncates the length of the string, but that's ok. */
195  h ^= (unsigned)(cp-(const unsigned char*)s);
196  return h;
197 }
198 #endif
199 
200 #ifndef HT_NO_CACHE_HASH_VALUES
201 #define HT_SET_HASH_(elm, field, hashfn) \
202  do { (elm)->field.hte_hash = hashfn(elm); } while (0)
203 #define HT_SET_HASHVAL_(elm, field, val) \
204  do { (elm)->field.hte_hash = (val); } while (0)
205 #define HT_ELT_HASH_(elm, field, hashfn) \
206  ((elm)->field.hte_hash)
207 #else
208 #define HT_SET_HASH_(elm, field, hashfn) \
209  ((void)0)
210 #define HT_ELT_HASH_(elm, field, hashfn) \
211  (hashfn(elm))
212 #define HT_SET_HASHVAL_(elm, field, val) \
213  ((void)0)
214 #endif
215 
216 #define HT_BUCKET_NUM_(head, field, elm, hashfn) \
217  (HT_ELT_HASH_(elm,field,hashfn) % head->hth_table_length)
218 
219 /* Helper: alias for the bucket containing 'elm'. */
220 #define HT_BUCKET_(head, field, elm, hashfn) \
221  ((head)->hth_table[HT_BUCKET_NUM_(head, field, elm, hashfn)])
222 
223 #define HT_FOREACH(x, name, head) \
224  for ((x) = HT_START(name, head); \
225  (x) != NULL; \
226  (x) = HT_NEXT(name, head, x))
227 
228 #ifndef HT_NDEBUG
229 #include "lib/err/torerr.h"
230 #define HT_ASSERT_(x) raw_assert(x)
231 #else
232 #define HT_ASSERT_(x) (void)0
233 #endif
234 
235 /* Macro put at the end of the end of a macro definition so that it
236  * consumes the following semicolon at file scope. Used only inside ht.h. */
237 #define HT_EAT_SEMICOLON__ struct ht_semicolon_eater
238 
239 #define HT_PROTOTYPE(name, type, field, hashfn, eqfn) \
240  int name##_HT_GROW(struct name *ht, unsigned min_capacity); \
241  void name##_HT_CLEAR(struct name *ht); \
242  int name##_HT_REP_IS_BAD_(const struct name *ht); \
243  static inline void \
244  name##_HT_INIT(struct name *head) { \
245  head->hth_table_length = 0; \
246  head->hth_table = NULL; \
247  head->hth_n_entries = 0; \
248  head->hth_load_limit = 0; \
249  head->hth_prime_idx = -1; \
250  } \
251  /* Helper: returns a pointer to the right location in the table \
252  * 'head' to find or insert the element 'elm'. */ \
253  static inline struct type ** \
254  name##_HT_FIND_P_(struct name *head, struct type *elm) \
255  { \
256  struct type **p; \
257  if (!head->hth_table) \
258  return NULL; \
259  p = &HT_BUCKET_(head, field, elm, hashfn); \
260  while (*p) { \
261  if (eqfn(*p, elm)) \
262  return p; \
263  p = &(*p)->field.hte_next; \
264  } \
265  return p; \
266  } \
267  /* Return a pointer to the element in the table 'head' matching 'elm', \
268  * or NULL if no such element exists */ \
269  ATTR_UNUSED static inline struct type * \
270  name##_HT_FIND(const struct name *head, struct type *elm) \
271  { \
272  struct type **p; \
273  struct name *h = (struct name *) head; \
274  HT_SET_HASH_(elm, field, hashfn); \
275  p = name##_HT_FIND_P_(h, elm); \
276  return p ? *p : NULL; \
277  } \
278  /* Insert the element 'elm' into the table 'head'. Do not call this \
279  * function if the table might already contain a matching element. */ \
280  ATTR_UNUSED static inline void \
281  name##_HT_INSERT(struct name *head, struct type *elm) \
282  { \
283  struct type **p; \
284  if (!head->hth_table || head->hth_n_entries >= head->hth_load_limit) \
285  name##_HT_GROW(head, head->hth_n_entries+1); \
286  ++head->hth_n_entries; \
287  HT_SET_HASH_(elm, field, hashfn); \
288  p = &HT_BUCKET_(head, field, elm, hashfn); \
289  elm->field.hte_next = *p; \
290  *p = elm; \
291  } \
292  /* Insert the element 'elm' into the table 'head'. If there already \
293  * a matching element in the table, replace that element and return \
294  * it. */ \
295  ATTR_UNUSED static inline struct type * \
296  name##_HT_REPLACE(struct name *head, struct type *elm) \
297  { \
298  struct type **p, *r; \
299  if (!head->hth_table || head->hth_n_entries >= head->hth_load_limit) \
300  name##_HT_GROW(head, head->hth_n_entries+1); \
301  HT_SET_HASH_(elm, field, hashfn); \
302  p = name##_HT_FIND_P_(head, elm); \
303  HT_ASSERT_(p != NULL); /* this holds because we called HT_GROW */ \
304  r = *p; \
305  *p = elm; \
306  if (r && (r!=elm)) { \
307  elm->field.hte_next = r->field.hte_next; \
308  r->field.hte_next = NULL; \
309  return r; \
310  } else { \
311  ++head->hth_n_entries; \
312  return NULL; \
313  } \
314  } \
315  /* Remove any element matching 'elm' from the table 'head'. If such \
316  * an element is found, return it; otherwise return NULL. */ \
317  ATTR_UNUSED static inline struct type * \
318  name##_HT_REMOVE(struct name *head, struct type *elm) \
319  { \
320  struct type **p, *r; \
321  HT_SET_HASH_(elm, field, hashfn); \
322  p = name##_HT_FIND_P_(head,elm); \
323  if (!p || !*p) \
324  return NULL; \
325  r = *p; \
326  *p = r->field.hte_next; \
327  r->field.hte_next = NULL; \
328  --head->hth_n_entries; \
329  return r; \
330  } \
331  /* Invoke the function 'fn' on every element of the table 'head', \
332  * using 'data' as its second argument. If the function returns \
333  * nonzero, remove the most recently examined element before invoking \
334  * the function again. */ \
335  ATTR_UNUSED static inline void \
336  name##_HT_FOREACH_FN(struct name *head, \
337  int (*fn)(struct type *, void *), \
338  void *data) \
339 { \
340  unsigned idx; \
341  struct type **p, **nextp, *next; \
342  if (!head->hth_table) \
343  return; \
344  for (idx=0; idx < head->hth_table_length; ++idx) { \
345  p = &head->hth_table[idx]; \
346  while (*p) { \
347  nextp = &(*p)->field.hte_next; \
348  next = *nextp; \
349  if (fn(*p, data)) { \
350  --head->hth_n_entries; \
351  *p = next; \
352  } else { \
353  p = nextp; \
354  } \
355  } \
356  } \
357  } \
358  /* Return a pointer to the first element in the table 'head', under \
359  * an arbitrary order. This order is stable under remove operations, \
360  * but not under others. If the table is empty, return NULL. */ \
361  ATTR_UNUSED static inline struct type ** \
362  name##_HT_START(struct name *head) \
363  { \
364  unsigned b = 0; \
365  while (b < head->hth_table_length) { \
366  if (head->hth_table[b]) { \
367  HT_ASSERT_(b == \
368  HT_BUCKET_NUM_(head,field,head->hth_table[b],hashfn)); \
369  return &head->hth_table[b]; \
370  } \
371  ++b; \
372  } \
373  return NULL; \
374  } \
375  /* Return the next element in 'head' after 'elm', under the arbitrary \
376  * order used by HT_START. If there are no more elements, return \
377  * NULL. If 'elm' is to be removed from the table, you must call \
378  * this function for the next value before you remove it, or use \
379  * HT_NEXT_RMV instead. \
380  */ \
381  ATTR_UNUSED static inline struct type ** \
382  name##_HT_NEXT(struct name *head, struct type **elm) \
383  { \
384  if ((*elm)->field.hte_next) { \
385  HT_ASSERT_(HT_BUCKET_NUM_(head,field,*elm,hashfn) == \
386  HT_BUCKET_NUM_(head,field,(*elm)->field.hte_next,hashfn)); \
387  return &(*elm)->field.hte_next; \
388  } else { \
389  unsigned b = HT_BUCKET_NUM_(head,field,*elm,hashfn)+1; \
390  while (b < head->hth_table_length) { \
391  if (head->hth_table[b]) { \
392  HT_ASSERT_(b == \
393  HT_BUCKET_NUM_(head,field,head->hth_table[b],hashfn)); \
394  return &head->hth_table[b]; \
395  } \
396  ++b; \
397  } \
398  return NULL; \
399  } \
400  } \
401  /* As HT_NEXT, but also remove the current element 'elm' from the \
402  * table. */ \
403  ATTR_UNUSED static inline struct type ** \
404  name##_HT_NEXT_RMV(struct name *head, struct type **elm) \
405  { \
406  unsigned h = HT_ELT_HASH_(*elm, field, hashfn); \
407  *elm = (*elm)->field.hte_next; \
408  --head->hth_n_entries; \
409  if (*elm) { \
410  return elm; \
411  } else { \
412  unsigned b = (h % head->hth_table_length)+1; \
413  while (b < head->hth_table_length) { \
414  if (head->hth_table[b]) \
415  return &head->hth_table[b]; \
416  ++b; \
417  } \
418  return NULL; \
419  } \
420  } \
421  HT_EAT_SEMICOLON__
422 
423 #define HT_GENERATE2(name, type, field, hashfn, eqfn, load, reallocarrayfn, \
424  freefn) \
425  /* Primes that aren't too far from powers of two. We stop at */ \
426  /* P=402653189 because P*sizeof(void*) is less than SSIZE_MAX */ \
427  /* even on a 32-bit platform. */ \
428  static unsigned name##_PRIMES[] = { \
429  53, 97, 193, 389, \
430  769, 1543, 3079, 6151, \
431  12289, 24593, 49157, 98317, \
432  196613, 393241, 786433, 1572869, \
433  3145739, 6291469, 12582917, 25165843, \
434  50331653, 100663319, 201326611, 402653189 \
435  }; \
436  static unsigned name##_N_PRIMES = \
437  (unsigned)(sizeof(name##_PRIMES)/sizeof(name##_PRIMES[0])); \
438  /* Expand the internal table of 'head' until it is large enough to \
439  * hold 'size' elements. Return 0 on success, -1 on allocation \
440  * failure. */ \
441  int \
442  name##_HT_GROW(struct name *head, unsigned size) \
443  { \
444  unsigned new_len, new_load_limit; \
445  int prime_idx; \
446  struct type **new_table; \
447  if (head->hth_prime_idx == (int)name##_N_PRIMES - 1) \
448  return 0; \
449  if (head->hth_load_limit > size) \
450  return 0; \
451  prime_idx = head->hth_prime_idx; \
452  do { \
453  new_len = name##_PRIMES[++prime_idx]; \
454  new_load_limit = (unsigned)(load*new_len); \
455  } while (new_load_limit <= size && \
456  prime_idx < (int)name##_N_PRIMES); \
457  if ((new_table = reallocarrayfn(NULL, new_len, sizeof(struct type*)))) { \
458  unsigned b; \
459  memset(new_table, 0, new_len*sizeof(struct type*)); \
460  for (b = 0; b < head->hth_table_length; ++b) { \
461  struct type *elm, *next; \
462  unsigned b2; \
463  elm = head->hth_table[b]; \
464  while (elm) { \
465  next = elm->field.hte_next; \
466  b2 = HT_ELT_HASH_(elm, field, hashfn) % new_len; \
467  elm->field.hte_next = new_table[b2]; \
468  new_table[b2] = elm; \
469  elm = next; \
470  } \
471  } \
472  if (head->hth_table) \
473  freefn(head->hth_table); \
474  head->hth_table = new_table; \
475  } else { \
476  unsigned b, b2; \
477  new_table = reallocarrayfn(head->hth_table, new_len, sizeof(struct type*)); \
478  if (!new_table) return -1; \
479  memset(new_table + head->hth_table_length, 0, \
480  (new_len - head->hth_table_length)*sizeof(struct type*)); \
481  for (b=0; b < head->hth_table_length; ++b) { \
482  struct type *e, **pE; \
483  for (pE = &new_table[b], e = *pE; e != NULL; e = *pE) { \
484  b2 = HT_ELT_HASH_(e, field, hashfn) % new_len; \
485  if (b2 == b) { \
486  pE = &e->field.hte_next; \
487  } else { \
488  *pE = e->field.hte_next; \
489  e->field.hte_next = new_table[b2]; \
490  new_table[b2] = e; \
491  } \
492  } \
493  } \
494  head->hth_table = new_table; \
495  } \
496  head->hth_table_length = new_len; \
497  head->hth_prime_idx = prime_idx; \
498  head->hth_load_limit = new_load_limit; \
499  return 0; \
500  } \
501  /* Free all storage held by 'head'. Does not free 'head' itself, or \
502  * individual elements. */ \
503  void \
504  name##_HT_CLEAR(struct name *head) \
505  { \
506  if (head->hth_table) \
507  freefn(head->hth_table); \
508  head->hth_table_length = 0; \
509  name##_HT_INIT(head); \
510  } \
511  /* Debugging helper: return false iff the representation of 'head' is \
512  * internally consistent. */ \
513  int \
514  name##_HT_REP_IS_BAD_(const struct name *head) \
515  { \
516  unsigned n, i; \
517  struct type *elm; \
518  if (!head->hth_table_length) { \
519  if (!head->hth_table && !head->hth_n_entries && \
520  !head->hth_load_limit && head->hth_prime_idx == -1) \
521  return 0; \
522  else \
523  return 1; \
524  } \
525  if (!head->hth_table || head->hth_prime_idx < 0 || \
526  !head->hth_load_limit) \
527  return 2; \
528  if (head->hth_n_entries > head->hth_load_limit) \
529  return 3; \
530  if (head->hth_table_length != name##_PRIMES[head->hth_prime_idx]) \
531  return 4; \
532  if (head->hth_load_limit != (unsigned)(load*head->hth_table_length)) \
533  return 5; \
534  for (n = i = 0; i < head->hth_table_length; ++i) { \
535  for (elm = head->hth_table[i]; elm; elm = elm->field.hte_next) { \
536  if (HT_ELT_HASH_(elm, field, hashfn) != hashfn(elm)) \
537  return 1000 + i; \
538  if (HT_BUCKET_NUM_(head,field,elm,hashfn) != i) \
539  return 10000 + i; \
540  ++n; \
541  } \
542  } \
543  if (n != head->hth_n_entries) \
544  return 6; \
545  return 0; \
546  } \
547  HT_EAT_SEMICOLON__
548 
549 #define HT_GENERATE(name, type, field, hashfn, eqfn, load, mallocfn, \
550  reallocfn, freefn) \
551  static void * \
552  name##_reallocarray(void *arg, size_t a, size_t b) \
553  { \
554  if ((b) && (a) > SIZE_MAX / (b)) \
555  return NULL; \
556  if (arg) \
557  return reallocfn((arg),(a)*(b)); \
558  else \
559  return mallocfn((a)*(b)); \
560  } \
561  HT_GENERATE2(name, type, field, hashfn, eqfn, load, \
562  name##_reallocarray, freefn)
563 
564 /** Implements an over-optimized "find and insert if absent" block;
565  * not meant for direct usage by typical code, or usage outside the critical
566  * path.*/
567 #define HT_FIND_OR_INSERT_(name, field, hashfn, head, eltype, elm, var, y, n) \
568  { \
569  struct name *var##_head_ = head; \
570  struct eltype **var; \
571  if (!var##_head_->hth_table || \
572  var##_head_->hth_n_entries >= var##_head_->hth_load_limit) \
573  name##_HT_GROW(var##_head_, var##_head_->hth_n_entries+1); \
574  HT_SET_HASH_((elm), field, hashfn); \
575  var = name##_HT_FIND_P_(var##_head_, (elm)); \
576  HT_ASSERT_(var); /* Holds because we called HT_GROW */ \
577  if (*var) { \
578  y; \
579  } else { \
580  n; \
581  } \
582  }
583 #define HT_FOI_INSERT_(field, head, elm, newent, var) \
584  { \
585  HT_SET_HASHVAL_(newent, field, (elm)->field.hte_hash); \
586  newent->field.hte_next = NULL; \
587  *var = newent; \
588  ++((head)->hth_n_entries); \
589  }
590 
591 /*
592  * Copyright 2005, Nick Mathewson. Implementation logic is adapted from code
593  * by Christopher Clark, retrofit to allow drop-in memory management, and to
594  * use the same interface as Niels Provos's tree.h. This is probably still
595  * a derived work, so the original license below still applies.
596  *
597  * Copyright (c) 2002, Christopher Clark
598  * All rights reserved.
599  *
600  * Redistribution and use in source and binary forms, with or without
601  * modification, are permitted provided that the following conditions
602  * are met:
603  *
604  * * Redistributions of source code must retain the above copyright
605  * notice, this list of conditions and the following disclaimer.
606  *
607  * * Redistributions in binary form must reproduce the above copyright
608  * notice, this list of conditions and the following disclaimer in the
609  * documentation and/or other materials provided with the distribution.
610  *
611  * * Neither the name of the original author; nor the names of any contributors
612  * may be used to endorse or promote products derived from this software
613  * without specific prior written permission.
614  *
615  *
616  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
617  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
618  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
619  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
620  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
621  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
622  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
623  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
624  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
625  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
626  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
627 */
628 
629 #endif
torerr.h
Headers for torerr.c.