#ifndef lint
static char *rcsid = "$Id: ucsmap.c,v 1.1.1.1 2003-06-04 00:26:14 marka Exp $";
#endif
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <idn/result.h>
#include <idn/assert.h>
#include <idn/log.h>
#include <idn/logmacro.h>
#include <idn/ucsmap.h>
#define INIT_SIZE 50
#define DEFAULT_BUF_SIZE 500
#define UCSMAP_HASH_SIZE 103
#define MAX_MAPLEN 0xffff
typedef struct {
short hidx;
unsigned short len;
unsigned long ucs;
unsigned long *map;
} ucsmap_entry_t;
typedef struct {
ucsmap_entry_t *entry;
int n;
} ucsmap_hash_t;
typedef struct ucsmap_buf {
struct ucsmap_buf *next;
unsigned long buf[1];
} ucsmap_buf_t;
typedef struct idn_ucsmap {
ucsmap_hash_t hash[UCSMAP_HASH_SIZE];
ucsmap_entry_t *entries;
size_t entry_size;
size_t nentries;
ucsmap_buf_t *mapdata;
size_t mapdata_size;
size_t mapdata_used;
int fixed;
int refcnt;
} ucsmap_t;
static int ucsmap_hash(unsigned long v);
static unsigned long *save_mapped_sequence(idn_ucsmap_t ctx,
unsigned long *map,
size_t maplen);
static void free_mapbuf(ucsmap_buf_t *buf);
static int comp_entry(const void *v1, const void *v2);
idn_result_t
idn_ucsmap_create(idn_ucsmap_t *ctxp) {
idn_ucsmap_t ctx;
assert(ctxp != NULL);
TRACE(("idn_ucsmap_create()\n"));
if ((ctx = malloc(sizeof(*ctx))) == NULL) {
WARNING(("idn_ucsmap_create: malloc failed\n"));
return (idn_nomemory);
}
ctx->entry_size = 0;
ctx->nentries = 0;
ctx->entries = NULL;
ctx->mapdata = NULL;
ctx->mapdata_size = 0;
ctx->mapdata_used = 0;
ctx->fixed = 0;
ctx->refcnt = 1;
*ctxp = ctx;
return (idn_success);
}
void
idn_ucsmap_destroy(idn_ucsmap_t ctx) {
assert(ctx != NULL && ctx->refcnt > 0);
TRACE(("idn_ucsmap_destroy()\n"));
if (--ctx->refcnt == 0) {
if (ctx->entries != NULL)
free(ctx->entries);
if (ctx->mapdata != NULL)
free_mapbuf(ctx->mapdata);
free(ctx);
}
}
void
idn_ucsmap_incrref(idn_ucsmap_t ctx) {
assert(ctx != NULL && ctx->refcnt > 0);
ctx->refcnt++;
}
idn_result_t
idn_ucsmap_add(idn_ucsmap_t ctx, unsigned long ucs,
unsigned long *map, size_t maplen)
{
ucsmap_entry_t *e;
ucsmap_entry_t *newbuf;
assert(ctx != NULL && ctx->refcnt > 0);
TRACE(("idn_ucsmap_add(ucs=U+%lX, maplen=%u)\n", ucs, maplen));
if (ctx->fixed) {
WARNING(("idn_ucsmap_add: attempt to add to fixed map\n"));
return (idn_failure);
}
if (maplen > MAX_MAPLEN) {
WARNING(("idn_ucsmap_add: maplen too large (> %d)\n",
MAX_MAPLEN));
return (idn_failure);
}
if (ctx->nentries >= ctx->entry_size) {
if (ctx->entry_size == 0)
ctx->entry_size = INIT_SIZE;
else
ctx->entry_size *= 2;
newbuf = realloc(ctx->entries, sizeof(*e) * ctx->entry_size);
if (newbuf == NULL)
return (idn_nomemory);
ctx->entries = newbuf;
}
e = &ctx->entries[ctx->nentries];
e->hidx = ucsmap_hash(ucs);
e->len = maplen;
e->ucs = ucs;
if (maplen > 0) {
e->map = save_mapped_sequence(ctx, map, maplen);
if (e->map == NULL)
return (idn_nomemory);
} else {
e->map = NULL;
}
ctx->nentries++;
return (idn_success);
}
void
idn_ucsmap_fix(idn_ucsmap_t ctx) {
ucsmap_entry_t *e;
int last_hidx;
int i;
assert(ctx != NULL && ctx->refcnt > 0);
TRACE(("idn_ucsmap_fix()\n"));
if (ctx->fixed)
return;
ctx->fixed = 1;
for (i = 0; i < UCSMAP_HASH_SIZE; i++) {
ctx->hash[i].entry = NULL;
ctx->hash[i].n = 0;
}
if (ctx->nentries == 0)
return;
qsort(ctx->entries, ctx->nentries, sizeof(ucsmap_entry_t), comp_entry);
last_hidx = -1;
for (i = 0, e = ctx->entries; i < ctx->nentries; i++, e++) {
if (e->hidx != last_hidx) {
ctx->hash[e->hidx].entry = e;
last_hidx = e->hidx;
}
ctx->hash[last_hidx].n++;
}
}
idn_result_t
idn_ucsmap_map(idn_ucsmap_t ctx, unsigned long v, unsigned long *to,
size_t tolen, size_t *maplenp) {
int hash;
ucsmap_entry_t *e;
int n;
int hi, lo, mid;
assert(ctx != NULL && ctx->refcnt > 0 && to != NULL &&
maplenp != NULL);
TRACE(("idn_ucsmap_map(v=U+%lX)\n", v));
if (!ctx->fixed) {
WARNING(("idn_ucsmap_map: not fixed yet\n"));
return (idn_failure);
}
hash = ucsmap_hash(v);
if ((n = ctx->hash[hash].n) == 0)
goto nomap;
e = ctx->hash[hash].entry;
lo = 0;
hi = n - 1;
while (lo <= hi) {
mid = (lo + hi) / 2;
if (v < e[mid].ucs)
hi = mid - 1;
else if (v > e[mid].ucs)
lo = mid + 1;
else {
if (tolen < e[mid].len)
return (idn_buffer_overflow);
memcpy(to, e[mid].map, sizeof(*to) * e[mid].len);
*maplenp = e[mid].len;
return (idn_success);
}
}
nomap:
if (tolen < 1)
return (idn_buffer_overflow);
*to = v;
*maplenp = 1;
return (idn_nomapping);
}
static int
ucsmap_hash(unsigned long v) {
return (v % UCSMAP_HASH_SIZE);
}
static unsigned long *
save_mapped_sequence(idn_ucsmap_t ctx, unsigned long *map, size_t maplen) {
ucsmap_buf_t *buf;
unsigned long *p;
size_t allocsize;
if (ctx->mapdata_used + maplen > ctx->mapdata_size) {
if (maplen > DEFAULT_BUF_SIZE)
allocsize = maplen * 2;
else
allocsize = DEFAULT_BUF_SIZE;
buf = malloc(sizeof(ucsmap_hash_t) +
sizeof(unsigned long) * allocsize);
if (buf == NULL)
return (NULL);
buf->next = ctx->mapdata;
ctx->mapdata = buf;
ctx->mapdata_size = allocsize;
ctx->mapdata_used = 0;
}
p = ctx->mapdata->buf + ctx->mapdata_used;
memcpy(p, map, sizeof(unsigned long) * maplen);
ctx->mapdata_used += maplen;
return (p);
}
static void
free_mapbuf(ucsmap_buf_t *buf) {
while (buf != NULL) {
ucsmap_buf_t *next = buf->next;
free(buf);
buf = next;
}
}
static int
comp_entry(const void *v1, const void *v2) {
const ucsmap_entry_t *e1 = v1;
const ucsmap_entry_t *e2 = v2;
if (e1->hidx < e2->hidx)
return (-1);
else if (e1->hidx > e2->hidx)
return (1);
else if (e1->ucs < e2->ucs)
return (-1);
else if (e1->ucs > e2->ucs)
return (1);
else
return (0);
}