2018-02-25 19:38:06 -06:00
|
|
|
// 25 february 2018
|
2018-03-10 18:21:39 -06:00
|
|
|
#include <stdlib.h>
|
2018-03-03 20:27:01 -06:00
|
|
|
#include "../ui.h"
|
|
|
|
#include "uipriv.h"
|
|
|
|
#include "attrstr.h"
|
2018-02-25 19:38:06 -06:00
|
|
|
|
|
|
|
struct feature {
|
|
|
|
char a;
|
|
|
|
char b;
|
|
|
|
char c;
|
|
|
|
char d;
|
|
|
|
uint32_t value;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct uiOpenTypeFeatures {
|
|
|
|
struct feature *data;
|
|
|
|
size_t len;
|
|
|
|
size_t cap;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define bytecount(n) ((n) * sizeof (struct feature))
|
|
|
|
|
|
|
|
uiOpenTypeFeatures *uiNewOpenTypeFeatures(void)
|
|
|
|
{
|
|
|
|
uiOpenTypeFeatures *otf;
|
|
|
|
|
|
|
|
otf = uiprivNew(uiOpenTypeFeatures);
|
|
|
|
otf->cap = 16;
|
|
|
|
otf->data = (struct feature *) uiprivAlloc(bytecount(otf->cap), "struct feature[]");
|
|
|
|
otf->len = 0;
|
|
|
|
return otf;
|
|
|
|
}
|
|
|
|
|
|
|
|
void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf)
|
|
|
|
{
|
|
|
|
uiprivFree(otf->data);
|
|
|
|
uiprivFree(otf);
|
|
|
|
}
|
|
|
|
|
2018-02-28 18:43:29 -06:00
|
|
|
uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf)
|
2018-02-25 19:38:06 -06:00
|
|
|
{
|
|
|
|
uiOpenTypeFeatures *ret;
|
|
|
|
|
|
|
|
ret = uiprivNew(uiOpenTypeFeatures);
|
|
|
|
ret->len = otf->len;
|
|
|
|
ret->cap = otf->cap;
|
|
|
|
ret->data = (struct feature *) uiprivAlloc(bytecount(ret->cap), "struct feature[]");
|
|
|
|
memset(ret->data, 0, bytecount(ret->cap));
|
|
|
|
memmove(ret->data, otf->data, bytecount(ret->len));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define intdiff(a, b) (((int) (a)) - ((int) (b)))
|
|
|
|
|
|
|
|
static int featurecmp(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
const struct feature *f = (const struct feature *) a;
|
|
|
|
const struct feature *g = (const struct feature *) b;
|
|
|
|
|
|
|
|
if (f->a != g->a)
|
|
|
|
return intdiff(f->a, g->a);
|
|
|
|
if (f->b != g->b)
|
|
|
|
return intdiff(f->b, g->b);
|
|
|
|
if (f->c != g->c)
|
|
|
|
return intdiff(f->c, g->c);
|
|
|
|
return intdiff(f->d, g->d);
|
|
|
|
}
|
|
|
|
|
2018-02-28 18:43:29 -06:00
|
|
|
static struct feature mkkey(char a, char b, char c, char d)
|
|
|
|
{
|
|
|
|
struct feature f;
|
|
|
|
|
|
|
|
f.a = a;
|
|
|
|
f.b = b;
|
|
|
|
f.c = c;
|
|
|
|
f.d = d;
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define find(pkey, otf) bsearch(pkey, otf->data, otf->len, sizeof (struct feature), featurecmp)
|
|
|
|
|
2018-02-25 19:38:06 -06:00
|
|
|
void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value)
|
|
|
|
{
|
|
|
|
struct feature *f;
|
2018-02-28 18:43:29 -06:00
|
|
|
struct feature key;
|
2018-02-25 19:38:06 -06:00
|
|
|
|
|
|
|
// replace existing value if any
|
2018-02-28 18:43:29 -06:00
|
|
|
key = mkkey(a, b, c, d);
|
|
|
|
f = (struct feature *) find(&key, otf);
|
2018-02-25 19:38:06 -06:00
|
|
|
if (f != NULL) {
|
|
|
|
f->value = value;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we got here, the tag is new
|
|
|
|
if (otf->len == otf->cap) {
|
|
|
|
otf->cap *= 2;
|
|
|
|
otf->data = (struct feature *) uiprivRealloc(otf->data, bytecount(otf->cap), "struct feature[]");
|
|
|
|
}
|
|
|
|
f = otf->data + otf->len;
|
|
|
|
f->a = a;
|
|
|
|
f->b = b;
|
|
|
|
f->c = c;
|
|
|
|
f->d = d;
|
|
|
|
f->value = value;
|
2018-02-25 19:40:23 -06:00
|
|
|
// LONGTERM qsort here is overkill
|
2018-02-25 19:38:06 -06:00
|
|
|
otf->len++;
|
|
|
|
qsort(otf->data, otf->len, sizeof (struct feature), featurecmp);
|
|
|
|
}
|
|
|
|
|
2018-02-28 18:43:29 -06:00
|
|
|
void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d)
|
2018-02-25 19:38:06 -06:00
|
|
|
{
|
|
|
|
struct feature *f;
|
2018-02-28 18:43:29 -06:00
|
|
|
struct feature key;
|
2018-02-25 19:38:06 -06:00
|
|
|
ptrdiff_t index;
|
|
|
|
size_t count;
|
|
|
|
|
2018-02-28 18:43:29 -06:00
|
|
|
key = mkkey(a, b, c, d);
|
|
|
|
f = (struct feature *) find(&key, otf);
|
2018-02-25 19:38:06 -06:00
|
|
|
if (f == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
index = f - otf->data;
|
|
|
|
count = otf->len - index - 1;
|
|
|
|
memmove(f + 1, f, bytecount(count));
|
|
|
|
otf->len--;
|
|
|
|
}
|
|
|
|
|
|
|
|
int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value)
|
|
|
|
{
|
|
|
|
const struct feature *f;
|
2018-02-28 18:43:29 -06:00
|
|
|
struct feature key;
|
2018-02-25 19:38:06 -06:00
|
|
|
|
2018-02-28 18:43:29 -06:00
|
|
|
key = mkkey(a, b, c, d);
|
|
|
|
f = (const struct feature *) find(&key, otf);
|
2018-02-25 19:38:06 -06:00
|
|
|
if (f == NULL)
|
|
|
|
return 0;
|
|
|
|
*value = f->value;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data)
|
|
|
|
{
|
|
|
|
size_t n;
|
|
|
|
const struct feature *p;
|
|
|
|
uiForEach ret;
|
|
|
|
|
|
|
|
p = otf->data;
|
|
|
|
for (n = 0; n < otf->len; n++) {
|
2018-02-28 18:43:29 -06:00
|
|
|
ret = (*f)(otf, p->a, p->b, p->c, p->d, p->value, data);
|
2018-02-25 19:38:06 -06:00
|
|
|
// TODO for all: require exact match?
|
|
|
|
if (ret == uiForEachStop)
|
|
|
|
return;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int uiprivOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b)
|
|
|
|
{
|
|
|
|
if (a == b)
|
|
|
|
return 1;
|
|
|
|
if (a->len != b->len)
|
|
|
|
return 0;
|
|
|
|
return memcmp(a->data, b->data, bytecount(a->len)) == 0;
|
|
|
|
}
|