#ifndef lint
static char *rcsid = "$Id: ucsset.c,v 1.1 2003/06/04 00:26:15 marka Exp $";
#endif
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <idn/result.h>
#include <idn/assert.h>
#include <idn/logmacro.h>
#include <idn/ucsset.h>
#define UCS_MAX 0x80000000UL
#define INIT_SIZE 50
typedef struct {
unsigned long from;
unsigned long to;
} range_t;
typedef struct {
int range_start;
int range_end;
} segment_t;
#define SEG_THLD1 0x10000
#define SEG_THLD2 0x110000
#define SEG_SFT1 10
#define SEG_SFT2 14
#define SEG_SFT3 24
#define SEG_OFF1 (SEG_THLD1 >> SEG_SFT1)
#define SEG_OFF2 (((SEG_THLD2 - SEG_THLD1) >> SEG_SFT2) + SEG_OFF1)
#define SEG_INDEX(v) \
(((v) < SEG_THLD1) ? ((v) >> SEG_SFT1) : \
((v) < SEG_THLD2) ? ((((v) - SEG_THLD1) >> SEG_SFT2) + SEG_OFF1) : \
((((v) - SEG_THLD2) >> SEG_SFT3) + SEG_OFF2))
#define SEG_LEN (SEG_INDEX(UCS_MAX - 1) + 1)
typedef struct idn_ucsset {
segment_t segments[SEG_LEN];
int fixed;
int size;
int nranges;
range_t *ranges;
int refcnt;
} ucsset;
static idn_result_t addrange(idn_ucsset_t ctx, unsigned long from,
unsigned long to, char *func_name);
static int comp_range(const void *v1, const void *v2);
idn_result_t
idn_ucsset_create(idn_ucsset_t *ctx) {
idn_ucsset_t bm;
assert(ctx != NULL);
TRACE(("idn_ucsset_create()\n"));
if ((bm = malloc(sizeof(ucsset))) == NULL) {
WARNING(("idn_ucsset_create: malloc failed\n"));
return idn_nomemory;
}
bm->size = bm->nranges = 0;
bm->ranges = NULL;
bm->fixed = 0;
bm->refcnt = 1;
*ctx = bm;
return (idn_success);
}
void
idn_ucsset_destroy(idn_ucsset_t ctx) {
assert(ctx != NULL && ctx->refcnt > 0);
TRACE(("idn_ucsset_destroy()\n"));
if (--ctx->refcnt == 0) {
if (ctx->ranges != NULL)
free(ctx->ranges);
free(ctx);
}
}
void
idn_ucsset_incrref(idn_ucsset_t ctx) {
assert(ctx != NULL && ctx->refcnt > 0);
TRACE(("idn_ucsset_incrref()\n"));
ctx->refcnt++;
}
idn_result_t
idn_ucsset_add(idn_ucsset_t ctx, unsigned long v) {
assert(ctx != NULL && ctx->refcnt > 0);
TRACE(("idn_ucsset_add(v=U+%lX)\n", v));
return (addrange(ctx, v, v, "idn_ucsset_add"));
}
idn_result_t
idn_ucsset_addrange(idn_ucsset_t ctx, unsigned long from,
unsigned long to)
{
assert(ctx != NULL && ctx->refcnt > 0);
TRACE(("idn_ucsset_addrange(from=U+%lX, to=U+%lX)\n",
from, to));
return (addrange(ctx, from, to, "idn_ucsset_addrange"));
}
void
idn_ucsset_fix(idn_ucsset_t ctx) {
int nranges;
range_t *ranges;
segment_t *segments;
int i, j;
assert(ctx != NULL && ctx->refcnt > 0);
TRACE(("idn_ucsset_fix()\n"));
nranges = ctx->nranges;
ranges = ctx->ranges;
segments = ctx->segments;
if (ctx->fixed)
return;
ctx->fixed = 1;
for (i = 0; i < SEG_LEN; i++) {
segments[i].range_start = -1;
segments[i].range_end = -1;
}
if (nranges == 0)
return;
qsort(ranges, nranges, sizeof(range_t), comp_range);
for (i = 0, j = 1; j < nranges; j++) {
if (ranges[i].to + 1 >= ranges[j].from) {
if (ranges[i].to < ranges[j].to) {
ranges[i].to = ranges[j].to;
}
} else {
i++;
if (i < j)
ranges[i] = ranges[j];
}
}
ctx->nranges = nranges = ++i;
for (i = 0; i < nranges; i++) {
int fidx = SEG_INDEX(ranges[i].from);
int tidx = SEG_INDEX(ranges[i].to);
for (j = fidx; j <= tidx; j++) {
if (segments[j].range_start < 0)
segments[j].range_start = i;
segments[j].range_end = i;
}
}
#if 0
ctx->ranges = realloc(ctx->ranges, ctx->nranges * sizeof(range_t));
#endif
}
idn_result_t
idn_ucsset_lookup(idn_ucsset_t ctx, unsigned long v, int *found) {
int idx;
segment_t *segments;
assert(ctx != NULL && ctx->refcnt > 0 && found != NULL);
TRACE(("idn_ucsset_lookup(v=U+%lX)\n", v));
if (!ctx->fixed) {
WARNING(("idn_ucsset_lookup: not fixed yet\n"));
return (idn_failure);
}
if (v >= UCS_MAX)
return (idn_invalid_codepoint);
segments = ctx->segments;
idx = SEG_INDEX(v);
*found = 0;
if (segments[idx].range_start >= 0) {
int lo = segments[idx].range_start;
int hi = segments[idx].range_end;
range_t *ranges = ctx->ranges;
while (lo <= hi) {
int mid = (lo + hi) / 2;
if (v < ranges[mid].from) {
hi = mid - 1;
} else if (v > ranges[mid].to) {
lo = mid + 1;
} else {
*found = 1;
break;
}
}
}
return (idn_success);
}
static idn_result_t
addrange(idn_ucsset_t ctx, unsigned long from, unsigned long to,
char *func_name)
{
range_t *newbuf;
if (from > UCS_MAX) {
WARNING(("%s: code point out of range (U+%lX)\n",
func_name, from));
return (idn_invalid_codepoint);
} else if (to > UCS_MAX) {
WARNING(("%s: code point out of range (U+%lX)\n",
func_name, to));
return (idn_invalid_codepoint);
} else if (from > to) {
WARNING(("%s: invalid range spec (U+%lX-U+%lX)\n",
func_name, from, to));
return (idn_invalid_codepoint);
}
if (ctx->fixed) {
WARNING(("%s: attempt to add to already fixed object\n",
func_name));
return (idn_failure);
}
if (ctx->nranges >= ctx->size) {
if (ctx->size == 0)
ctx->size = INIT_SIZE;
else
ctx->size *= 2;
newbuf = realloc(ctx->ranges, ctx->size * sizeof(range_t));
if (newbuf == NULL)
return (idn_nomemory);
ctx->ranges = newbuf;
}
ctx->ranges[ctx->nranges].from = from;
ctx->ranges[ctx->nranges].to = to;
ctx->nranges++;
return (idn_success);
}
static int
comp_range(const void *v1, const void *v2) {
const range_t *r1 = v1;
const range_t *r2 = v2;
if (r1->from < r2->from)
return (-1);
else if (r1->from > r2->from)
return (1);
else
return (0);
}