# include "dgd.h"
# include "hash.h"
# include "str.h"
# include "array.h"
# include "object.h"
# include "data.h"
# define STR_CHUNK 128
typedef struct _strh_ {
hte chain; /* hash table chain */
string *str; /* string entry */
Uint index; /* building index */
struct _strh_ **link; /* next in list */
} strh;
typedef struct _strhchunk_ {
struct _strhchunk_ *next; /* next in list */
strh sh[STR_CHUNK]; /* chunk of strh entries */
} strhchunk;
static hashtab *ht; /* string merge table */
static strh **slink; /* linked list of merged strings */
static strhchunk *shlist; /* list of all strh chunks */
static int strhchunksz; /* size of current strh chunk */
/*
* NAME: string->init()
* DESCRIPTION: initialize string handling
*/
void str_init()
{
ht = ht_new(STRMERGETABSZ, STRMERGEHASHSZ);
strhchunksz = STR_CHUNK;
}
/*
* NAME: string->alloc()
* DESCRIPTION: Create a new string. The text can be a NULL pointer, in which
* case it must be filled in later.
*/
string *str_alloc(text, len)
char *text;
register long len;
{
register string *s;
string dummy;
/* allocate string struct & text in one block */
s = (string *) ALLOC(char, dummy.text - (char *) &dummy + 1 + len);
if (text != (char *) NULL && len > 0) {
memcpy(s->text, text, (unsigned int) len);
}
s->text[s->len = len] = '\0';
s->ref = 0;
s->primary = (strref *) NULL;
return s;
}
/*
* NAME: string->new()
* DESCRIPTION: create a new string with size check
*/
string *str_new(text, len)
char *text;
long len;
{
if (len > (unsigned long) MAX_STRLEN) {
error("String too long");
}
return str_alloc(text, len);
}
/*
* NAME: string->del()
* DESCRIPTION: remove a reference from a string. If there are none left, the
* string is removed.
*/
void str_del(s)
register string *s;
{
if (--(s->ref) == 0) {
FREE(s);
}
}
/*
* NAME: string->put()
* DESCRIPTION: put a string in the string merge table
*/
Uint str_put(str, n)
register string *str;
register Uint n;
{
register strh **h;
h = (strh **) ht_lookup(ht, str->text, FALSE);
for (;;) {
/*
* The hasher doesn't handle \0 in strings, and so may not have
* found the proper string. Follow the hash table chain until
* the end is reached, or until a match is found using str_cmp().
*/
if (*h == (strh *) NULL) {
register strh *s;
/*
* Not in the hash table. Make a new entry.
*/
if (strhchunksz == STR_CHUNK) {
register strhchunk *l;
l = ALLOC(strhchunk, 1);
l->next = shlist;
shlist = l;
strhchunksz = 0;
}
s = *h = &shlist->sh[strhchunksz++];
s->chain.next = (hte *) NULL;
s->chain.name = str->text;
s->str = str;
s->index = n;
s->link = slink;
slink = h;
return n;
} else if (str_cmp(str, (*h)->str) == 0) {
/* already in the hash table */
return (*h)->index;
}
h = (strh **) &(*h)->chain.next;
}
}
/*
* NAME: string->clear()
* DESCRIPTION: clear the string merge table. All entries are in a separate
* linked list so this is simple. Note that this routine makes
* assumptions about how the hash table is constructed.
*/
void str_clear()
{
register strh **h;
register strhchunk *l;
for (h = slink; h != (strh **) NULL; ) {
register strh *f;
f = *h;
*h = (strh *) NULL;
h = f->link;
}
slink = (strh **) NULL;
for (l = shlist; l != (strhchunk *) NULL; ) {
register strhchunk *f;
f = l;
l = l->next;
FREE(f);
}
shlist = (strhchunk *) NULL;
strhchunksz = STR_CHUNK;
}
/*
* NAME: string->cmp()
* DESCRIPTION: compare two strings
*/
int str_cmp(s1, s2)
string *s1, *s2;
{
if (s1 == s2) {
return 0;
} else {
register ssizet len;
register char *p, *q;
long cmplen;
int cmp;
cmplen = (long) s1->len - s2->len;
if (cmplen > 0) {
/* s1 longer */
cmplen = 1;
len = s2->len;
} else {
/* s2 longer or equally long */
if (cmplen < 0) {
cmplen = -1;
}
len = s1->len;
}
for (p = s1->text, q = s2->text; len > 0 && *p == *q; p++, q++, --len) ;
cmp = UCHAR(*p) - UCHAR(*q);
return (cmp != 0) ? cmp : cmplen;
}
}
/*
* NAME: string->add()
* DESCRIPTION: add two strings
*/
string *str_add(s1, s2)
register string *s1, *s2;
{
register string *s;
s = str_new((char *) NULL, (long) s1->len + s2->len);
memcpy(s->text, s1->text, s1->len);
memcpy(s->text + s1->len, s2->text, s2->len);
return s;
}
/*
* NAME: string->index()
* DESCRIPTION: index a string
*/
ssizet str_index(s, l)
string *s;
register long l;
{
if (l < 0 || l >= (long) s->len) {
error("String index out of range");
}
return l;
}
/*
* NAME: string->ckrange()
* DESCRIPTION: check a string subrange
*/
void str_ckrange(s, l1, l2)
string *s;
register long l1, l2;
{
if (l1 < 0 || l1 > l2 + 1 || l2 >= (long) s->len) {
error("Invalid string range");
}
}
/*
* NAME: string->range()
* DESCRIPTION: return a subrange of a string
*/
string *str_range(s, l1, l2)
register string *s;
register long l1, l2;
{
if (l1 < 0 || l1 > l2 + 1 || l2 >= (long) s->len) {
error("Invalid string range");
}
return str_new(s->text + l1, l2 - l1 + 1);
}