diff options
Diffstat (limited to 'lib/names-hash.c')
-rw-r--r-- | lib/names-hash.c | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/lib/names-hash.c b/lib/names-hash.c new file mode 100644 index 0000000..2f5bc3c --- /dev/null +++ b/lib/names-hash.c @@ -0,0 +1,128 @@ +/* + * The PCI Library -- ID to Name Hash + * + * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include <string.h> + +#include "internal.h" +#include "names.h" + +struct id_bucket { + struct id_bucket *next; + unsigned int full; +}; + +#ifdef __GNUC__ +#define BUCKET_ALIGNMENT __alignof__(struct id_bucket) +#else +union id_align { + struct id_bucket *next; + unsigned int full; +}; +#define BUCKET_ALIGNMENT sizeof(union id_align) +#endif +#define BUCKET_ALIGN(n) ((n)+BUCKET_ALIGNMENT-(n)%BUCKET_ALIGNMENT) + +static void *id_alloc(struct pci_access *a, unsigned int size) +{ + struct id_bucket *buck = a->current_id_bucket; + unsigned int pos; + + if (!a->id_hash) + { + a->id_hash = pci_malloc(a, sizeof(struct id_entry *) * HASH_SIZE); + memset(a->id_hash, 0, sizeof(struct id_entry *) * HASH_SIZE); + } + + if (!buck || buck->full + size > BUCKET_SIZE) + { + buck = pci_malloc(a, BUCKET_SIZE); + buck->next = a->current_id_bucket; + a->current_id_bucket = buck; + buck->full = BUCKET_ALIGN(sizeof(struct id_bucket)); + } + pos = buck->full; + buck->full = BUCKET_ALIGN(buck->full + size); + return (byte *)buck + pos; +} + +static inline unsigned int id_hash(int cat, u32 id12, u32 id34) +{ + unsigned int h; + + h = id12 ^ (id34 << 3) ^ (cat << 5); + return h % HASH_SIZE; +} + +int +pci_id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text, enum id_entry_src src) +{ + u32 id12 = id_pair(id1, id2); + u32 id34 = id_pair(id3, id4); + unsigned int h = id_hash(cat, id12, id34); + struct id_entry *n = a->id_hash ? a->id_hash[h] : NULL; + int len = strlen(text); + + while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat)) + n = n->next; + if (n) + return 1; + n = id_alloc(a, sizeof(struct id_entry) + len); + n->id12 = id12; + n->id34 = id34; + n->cat = cat; + n->src = src; + memcpy(n->name, text, len+1); + n->next = a->id_hash[h]; + a->id_hash[h] = n; + return 0; +} + +char +*pci_id_lookup(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4) +{ + struct id_entry *n, *best; + u32 id12 = id_pair(id1, id2); + u32 id34 = id_pair(id3, id4); + + if (a->id_hash) + { + n = a->id_hash[id_hash(cat, id12, id34)]; + best = NULL; + for (; n; n=n->next) + { + if (n->id12 != id12 || n->id34 != id34 || n->cat != cat) + continue; + if (n->src == SRC_LOCAL && (flags & PCI_LOOKUP_SKIP_LOCAL)) + continue; + if (n->src == SRC_NET && !(flags & PCI_LOOKUP_NETWORK)) + continue; + if (n->src == SRC_CACHE && !(flags & PCI_LOOKUP_CACHE)) + continue; + if (n->src == SRC_HWDB && (flags & (PCI_LOOKUP_SKIP_LOCAL | PCI_LOOKUP_NO_HWDB))) + continue; + if (!best || best->src < n->src) + best = n; + } + if (best) + return best->name; + } + return NULL; +} + +void +pci_id_hash_free(struct pci_access *a) +{ + pci_mfree(a->id_hash); + a->id_hash = NULL; + while (a->current_id_bucket) + { + struct id_bucket *buck = a->current_id_bucket; + a->current_id_bucket = buck->next; + pci_mfree(buck); + } +} |