/* * upb - a minimalist implementation of protocol buffers. * * Copyright (c) 2009 Joshua Haberman. See LICENSE for details. * * This file defines very fast int->struct (inttable) and string->struct * (strtable) hash tables. The struct can be of any size, and it is stored * in the table itself, for cache-friendly performance. * * The table uses internal chaining with Brent's variation (inspired by the * Lua implementation of hash tables). The hash function for strings is * Austin Appleby's "MurmurHash." */ #ifndef UPB_TABLE_H_ #define UPB_TABLE_H_ #include "upb.h" #ifdef __cplusplus extern "C" { #endif typedef uint32_t upb_inttable_key_t; struct upb_inttable_entry { upb_inttable_key_t key; uint32_t next; /* Internal chaining. */ }; /* TODO: consider storing the hash in the entry. This would avoid the need to * rehash on table resizes, but more importantly could possibly improve lookup * performance by letting us compare hashes before comparing lengths or the * strings themselves. */ struct upb_strtable_entry { struct upb_string key; uint32_t next; /* Internal chaining. */ }; struct upb_table { void *entries; uint32_t count; /* How many elements are currently in the table? */ uint16_t entry_size; /* How big is each entry? */ uint8_t size_lg2; /* The table is 2^size_lg2 in size. */ }; struct upb_strtable { struct upb_table t; }; struct upb_inttable { struct upb_table t; }; /* Initialize and free a table, respectively. Specify the initial size * with 'size' (the size will be increased as necessary). Entry size * specifies how many bytes each entry in the table is. */ void upb_inttable_init(struct upb_inttable *table, uint32_t size, uint16_t entry_size); void upb_inttable_free(struct upb_inttable *table); void upb_strtable_init(struct upb_strtable *table, uint32_t size, uint16_t entry_size); void upb_strtable_free(struct upb_strtable *table); INLINE uint32_t upb_table_size(struct upb_table *t) { return 1 << t->size_lg2; } INLINE uint32_t upb_inttable_size(struct upb_inttable *t) { return upb_table_size(&t->t); } INLINE uint32_t upb_strtable_size(struct upb_strtable *t) { return upb_table_size(&t->t); } /* Inserts the given key into the hashtable with the given value. The key must * not already exist in the hash table. The data will be copied from e into * the hashtable (the amount of data copied comes from entry_size when the * table was constructed). Therefore the data at val may be freed once the * call returns. */ void upb_inttable_insert(struct upb_inttable *t, struct upb_inttable_entry *e); void upb_strtable_insert(struct upb_strtable *t, struct upb_strtable_entry *e); INLINE uint32_t upb_inttable_hash(struct upb_inttable *t, upb_inttable_key_t k) { return k & (upb_inttable_size(t)-1); /* Identity hash for ints. */ } /* Looks up key in this table. Inlined because this is in the critical path * of parsing. We have the caller specify the entry_size because fixing * this as a literal (instead of reading table->entry_size) gives the * compiler more ability to optimize. */ INLINE void *upb_inttable_lookup(struct upb_inttable *t, uint32_t key, uint32_t entry_size) { uint32_t hash = upb_inttable_hash(t, key); while(1) { struct upb_inttable_entry *e = (struct upb_inttable_entry*)(char*)t->t.entries + hash*entry_size; if(e->key == 0) return NULL; else if(e->key == key) return e; hash = e->next; } } void *upb_strtable_lookup(struct upb_strtable *t, struct upb_string *key); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* UPB_TABLE_H_ */