parent
0c80c38475
commit
d1f78c88fa
10 changed files with 279 additions and 356 deletions
@ -1,50 +0,0 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2009 Joshua Haberman. See LICENSE for details. |
||||
*/ |
||||
|
||||
#include "upb_fieldmap.h" |
||||
|
||||
#include <stdlib.h> |
||||
|
||||
void pbstream_init_fieldmap(struct pbstream_fieldmap *fieldmap, |
||||
struct pbstream_field *fields, |
||||
int num_fields) |
||||
{ |
||||
qsort(fields, num_fields, sizeof(*fields), compare_fields); |
||||
|
||||
/* Find the largest n for which at least half the fieldnums <n are used.
|
||||
* Start at 8 to avoid noise of small numbers. */ |
||||
pbstream_field_number_t n = 0, maybe_n; |
||||
for(int i = 0; i < num_fields; i++) { |
||||
maybe_n = fields[i].field_number; |
||||
if(maybe_n > 8 && maybe_n/(i+1) >= 2) break; |
||||
n = maybe_n; |
||||
} |
||||
|
||||
fieldmap->num_fields = num_fields; |
||||
fieldmap->fields = malloc(sizeof(*fieldmap->fields)*num_fields); |
||||
memcpy(fieldmap->fields, fields, sizeof(*fields)*num_fields); |
||||
|
||||
fieldmap->array_size = n; |
||||
fieldmap->array = malloc(sizeof(*fieldmap->array)*n); |
||||
memset(fieldmap->array, 0, sizeof(*fieldmap->array)*n); |
||||
|
||||
for (int i = 0; i < num_fields && fields[i].field_number <= n; i++) |
||||
fieldmap->array[fields[i].field_number-1] = &fieldmap->fields[i]; |
||||
|
||||
/* Until we support the hashtable part... */ |
||||
assert(n == fields[num_fields-1].field_number); |
||||
} |
||||
|
||||
void pbstream_free_fieldmap(struct pbstream_fieldmap *fieldmap) |
||||
{ |
||||
free(fieldmap->fields); |
||||
free(fieldmap->array); |
||||
} |
||||
|
||||
/* Emit definition for inline function. */ |
||||
extern void *upb_fieldmap_find(struct upb_fieldmap *fm, |
||||
pbstream_field_number_t num, |
||||
size_t info_size); |
@ -1,53 +0,0 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2009 Joshua Haberman. See LICENSE for details. |
||||
* |
||||
* A fieldmap is a data structure that supports fast lookup of fields by |
||||
* number. It is logically a map of {field_number -> <field info>}, where |
||||
* <field info> is any struct that begins with the field number. Fast lookup |
||||
* is important, because it is in the critical path of parsing. */ |
||||
|
||||
#ifndef UPB_FIELDMAP_H_ |
||||
#define UPB_FIELDMAP_H_ |
||||
|
||||
#include "upb.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
struct upb_fieldmap { |
||||
int array_size; |
||||
void *array; |
||||
/* TODO: the hashtable part. */ |
||||
}; |
||||
|
||||
/* Takes an array of num_fields fields and builds an optimized table for fast
|
||||
* lookup of fields by number. The input fields need not be sorted. This |
||||
* fieldmap must be freed with upb_free_fieldmap(). */ |
||||
void upb_init_fieldmap(struct upb_fieldmap *fieldmap, |
||||
void *fields, |
||||
int num_fields, |
||||
int field_size); |
||||
void upb_free_fieldmap(struct upb_fieldmap *fieldmap); |
||||
|
||||
/* Looks the given field number up in the fieldmap, and returns the
|
||||
* corresponding field definition (or NULL if this field number does not exist |
||||
* in this fieldmap). */ |
||||
inline void *upb_fieldmap_find(struct upb_fieldmap *fm, |
||||
upb_field_number_t num, |
||||
size_t info_size) |
||||
{ |
||||
if (num < array_size) { |
||||
return (char*)fs->array + (num*info_size); |
||||
} else { |
||||
/* TODO: the hashtable part. */ |
||||
} |
||||
} |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* UPB_PARSE_H_ */ |
@ -0,0 +1,89 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2009 Joshua Haberman. See LICENSE for details. |
||||
*/ |
||||
|
||||
#include "upb_table.h" |
||||
|
||||
#include <stdlib.h> |
||||
|
||||
static int compare_entries(const void *f1, const void *f2) |
||||
{ |
||||
return ((struct upb_inttable_entry*)f1)->key - |
||||
((struct upb_inttable_entry*)f2)->key; |
||||
} |
||||
|
||||
static uint32_t max(uint32_t a, uint32_t b) { return a > b ? a : b; } |
||||
|
||||
static uint32_t round_up_to_pow2(uint32_t v) |
||||
{ |
||||
/* cf. Bit Twiddling Hacks:
|
||||
* http://www-graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */
|
||||
v--; |
||||
v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; |
||||
v++; |
||||
return v; |
||||
} |
||||
|
||||
void upb_inttable_init(struct upb_inttable *table, void *entries, |
||||
int num_entries, int entry_size) |
||||
{ |
||||
qsort(entries, num_entries, entry_size, compare_entries); |
||||
|
||||
/* Find the largest n for which at least half the keys <n are used. We
|
||||
* make sure our table size is at least n. This allows all keys <n to be |
||||
* in their main position (as if it were an array) and only numbers >n might |
||||
* possibly have collisions. Start at 8 to avoid noise of small numbers. */ |
||||
upb_inttable_key_t n = 0, maybe_n; |
||||
bool all_in_array = true; |
||||
for(int i = 0; i < num_entries; i++) { |
||||
struct upb_inttable_entry *e = |
||||
upb_inttable_entry_get(entries, i, entry_size); |
||||
maybe_n = e->key; |
||||
if(maybe_n > 8 && maybe_n/(i+1) >= 2) { |
||||
all_in_array = false; |
||||
break; |
||||
} |
||||
n = maybe_n; |
||||
} |
||||
|
||||
/* TODO: measure, tweak, optimize this choice of table size. Possibly test
|
||||
* (at runtime) maximum chain length for each proposed size. */ |
||||
uint32_t min_size_by_load = all_in_array ? n : (double)num_entries / 0.85; |
||||
uint32_t min_size = max(n, min_size_by_load); |
||||
table->size = round_up_to_pow2(min_size); |
||||
|
||||
table->entries = malloc(table->size * entry_size); |
||||
/* Initialize to empty. */ |
||||
for(size_t i = 0; i < table->size; i++) { |
||||
struct upb_inttable_entry *e = upb_inttable_get(table, i, entry_size); |
||||
e->key = UPB_END_OF_CHAIN; |
||||
e->next = UPB_END_OF_CHAIN; |
||||
} |
||||
|
||||
/* Insert the elements. */ |
||||
for(int i = 0; i < num_entries; i++) { |
||||
struct upb_inttable_entry *e = |
||||
upb_inttable_entry_get(entries, i, entry_size); |
||||
int32_t hash = upb_inttable_hash(table, e->key); |
||||
struct upb_inttable_entry *table_e = |
||||
upb_inttable_get(table, hash, entry_size); |
||||
if(table_e->key != UPB_END_OF_CHAIN) { /* Collision. */ |
||||
if(hash == upb_inttable_hash(table, table_e->key)) { |
||||
/* Existing element is in its main posisiton. Find an empty slot to
|
||||
* place our new element. */ |
||||
} else { |
||||
/* Existing element is not in its main position. Move it to an empty
|
||||
* slot and put our element in its main position. */ |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void upb_inttable_free(struct upb_inttable *table) |
||||
{ |
||||
free(table->entries); |
||||
} |
||||
|
||||
/* Emit definition for inline functions. */ |
@ -0,0 +1,75 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2009 Joshua Haberman. See LICENSE for details. |
||||
*/ |
||||
|
||||
#ifndef UPB_TABLE_H_ |
||||
#define UPB_TABLE_H_ |
||||
|
||||
#include "upb.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
typedef int32_t upb_inttable_key_t; |
||||
|
||||
#define UPB_END_OF_CHAIN (upb_inttable_key_t)-1 |
||||
|
||||
struct upb_inttable_entry { |
||||
upb_inttable_key_t key; |
||||
int32_t next; |
||||
}; |
||||
|
||||
struct upb_inttable { |
||||
uint32_t size; /* Is a power of two. */ |
||||
void *entries; |
||||
}; |
||||
|
||||
/* Builds an int32_t -> <entry> table, optimized for very fast lookup by
|
||||
* number. table is a pointer to a previously allocated upb_inttable. |
||||
* entries points to an array of the desired entries themselves, each of size |
||||
* entry_size. The table is allocated in dynamic memory, and does not reference |
||||
* the data in entries. Entries may be modified by the function. |
||||
* |
||||
* The table must be freed with upb_inttable_free. */ |
||||
void upb_inttable_init(struct upb_inttable *table, void *entries, |
||||
int num_entries, int entry_size); |
||||
|
||||
inline int32_t upb_inttable_hash(struct upb_inttable * table, |
||||
upb_inttable_key_t key) { |
||||
return key & (table->size-1); |
||||
} |
||||
|
||||
/* Frees any data that was allocated by upb_inttable_init. */ |
||||
void upb_inttable_free(struct upb_inttable *table); |
||||
|
||||
inline struct upb_inttable_entry *upb_inttable_entry_get( |
||||
void *entries, int32_t pos, int entry_size) { |
||||
return (struct upb_inttable_entry*)((char*)entries) + pos*entry_size; |
||||
} |
||||
|
||||
inline struct upb_inttable_entry *upb_inttable_get( |
||||
struct upb_inttable *table, int32_t pos, int entry_size) { |
||||
return upb_inttable_entry_get(table->entries, pos, entry_size); |
||||
} |
||||
|
||||
/* Lookups up key in this table. Inlined because this is in the critical path
|
||||
* of parsing. */ |
||||
inline void *upb_inttable_lookup(struct upb_inttable *table, int32_t key, |
||||
int entry_size) { |
||||
int32_t pos = upb_inttable_hash(table, key); |
||||
do { |
||||
struct upb_inttable_entry *e = upb_inttable_get(table, pos, entry_size); |
||||
if (key == e->key) return e; |
||||
pos = e->next; |
||||
} while (pos != UPB_END_OF_CHAIN); |
||||
return NULL; /* Not found. */ |
||||
} |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* UPB_TABLE_H_ */ |
Loading…
Reference in new issue