cosmopolitan/third_party/ctags/keyword.c

223 lines
5.4 KiB
C

/*
* $Id: keyword.c 715 2009-07-06 03:31:00Z dhiebert $
*
* Copyright (c) 1998-2002, Darren Hiebert
*
* This source code is released for free distribution under the terms of the
* GNU General Public License.
*
* Manages a keyword hash.
*/
#include "third_party/ctags/general.h"
/* must always come first */
#include "third_party/ctags/debug.h"
#include "third_party/ctags/keyword.h"
#include "third_party/ctags/options.h"
#include "third_party/ctags/routines.h"
/*
* MACROS
*/
#define HASH_EXPONENT 7 /* must be less than 17 */
/*
* DATA DECLARATIONS
*/
typedef struct sHashEntry {
struct sHashEntry *next;
const char *string;
langType language;
int value;
} hashEntry;
/*
* DATA DEFINITIONS
*/
static const unsigned int TableSize = 1 << HASH_EXPONENT;
static hashEntry **HashTable = NULL;
/*
* FUNCTION DEFINITIONS
*/
static hashEntry **getHashTable(void) {
static boolean allocated = FALSE;
if (!allocated) {
unsigned int i;
HashTable = xMalloc(TableSize, hashEntry *);
for (i = 0; i < TableSize; ++i) HashTable[i] = NULL;
allocated = TRUE;
}
return HashTable;
}
static hashEntry *getHashTableEntry(unsigned long hashedValue) {
hashEntry **const table = getHashTable();
hashEntry *entry;
Assert(hashedValue < TableSize);
entry = table[hashedValue];
return entry;
}
static unsigned long hashValue(const char *const string) {
unsigned long value = 0;
const unsigned char *p;
Assert(string != NULL);
/* We combine the various words of the multiword key using the method
* described on page 512 of Vol. 3 of "The Art of Computer Programming".
*/
for (p = (const unsigned char *)string; *p != '\0'; ++p) {
value <<= 1;
if (value & 0x00000100L) value = (value & 0x000000ffL) + 1L;
value ^= *p;
}
/* Algorithm from page 509 of Vol. 3 of "The Art of Computer Programming"
* Treats "value" as a 16-bit integer plus 16-bit fraction.
*/
value *= 40503L; /* = 2^16 * 0.6180339887 ("golden ratio") */
value &= 0x0000ffffL; /* keep fractional part */
value >>= 16 - HASH_EXPONENT; /* scale up by hash size and move down */
return value;
}
static hashEntry *newEntry(const char *const string, langType language,
int value) {
hashEntry *const entry = xMalloc(1, hashEntry);
entry->next = NULL;
entry->string = string;
entry->language = language;
entry->value = value;
return entry;
}
/* Note that it is assumed that a "value" of zero means an undefined keyword
* and clients of this function should observe this. Also, all keywords added
* should be added in lower case. If we encounter a case-sensitive language
* whose keywords are in upper case, we will need to redesign this.
*/
extern void addKeyword(const char *const string, langType language, int value) {
const unsigned long hashedValue = hashValue(string);
hashEntry *entry = getHashTableEntry(hashedValue);
if (entry == NULL) {
hashEntry **const table = getHashTable();
table[hashedValue] = newEntry(string, language, value);
} else {
hashEntry *prev = NULL;
while (entry != NULL) {
if (language == entry->language && strcmp(string, entry->string) == 0) {
Assert(("Already in table" == NULL));
}
prev = entry;
entry = entry->next;
}
if (entry == NULL) {
Assert(prev != NULL);
prev->next = newEntry(string, language, value);
}
}
}
extern int lookupKeyword(const char *const string, langType language) {
const unsigned long hashedValue = hashValue(string);
hashEntry *entry = getHashTableEntry(hashedValue);
int result = -1;
while (entry != NULL) {
if (language == entry->language && strcmp(string, entry->string) == 0) {
result = entry->value;
break;
}
entry = entry->next;
}
return result;
}
extern void freeKeywordTable(void) {
if (HashTable != NULL) {
unsigned int i;
for (i = 0; i < TableSize; ++i) {
hashEntry *entry = HashTable[i];
while (entry != NULL) {
hashEntry *next = entry->next;
eFree(entry);
entry = next;
}
}
eFree(HashTable);
}
}
extern int analyzeToken(vString *const name, langType language) {
vString *keyword = vStringNew();
int result;
vStringCopyToLower(keyword, name);
result = lookupKeyword(vStringValue(keyword), language);
vStringDelete(keyword);
return result;
}
#ifdef DEBUG
static void printEntry(const hashEntry *const entry) {
printf(" %-15s %-7s\n", entry->string, getLanguageName(entry->language));
}
static unsigned int printBucket(const unsigned int i) {
hashEntry **const table = getHashTable();
hashEntry *entry = table[i];
unsigned int measure = 1;
boolean first = TRUE;
printf("%2d:", i);
if (entry == NULL)
printf("\n");
else
while (entry != NULL) {
if (!first)
printf(" ");
else {
printf(" ");
first = FALSE;
}
printEntry(entry);
entry = entry->next;
measure = 2 * measure;
}
return measure - 1;
}
extern void printKeywordTable(void) {
unsigned long emptyBucketCount = 0;
unsigned long measure = 0;
unsigned int i;
for (i = 0; i < TableSize; ++i) {
const unsigned int pass = printBucket(i);
measure += pass;
if (pass == 0) ++emptyBucketCount;
}
printf("spread measure = %ld\n", measure);
printf("%ld empty buckets\n", emptyBucketCount);
}
#endif
/* vi:set tabstop=4 shiftwidth=4: */