#include "cmp.h"
#include <fontconfig/fontconfig.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
FcStrSet *decodeStrSet(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_array(bytes, &size)) return NULL;
FcStrSet *ret = FcStrSetCreate();
if (ret == NULL) return NULL;
for (uint32_t i = 0; i < size; i++) {
char text[512]; // What's the appropriate size here?
uint32_t str_size = 512;
if (!cmp_read_str(bytes, text, &str_size)) goto fail;
if (!FcStrSetAdd(ret, text)) goto fail;
}
return ret;
fail:
FcStrSetDestroy(ret);
return NULL;
}
FcStrSet *decodeFileSet(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_array(bytes, &size)) return NULL;
FcStrSet *ret = FcStrSetCreate();
if (ret == NULL) return NULL;
for (uint32_t i = 0; i < size; i++) {
char text[512]; // What's the appropriate size here?
uint32_t str_size = 512;
if (!cmp_read_str(bytes, text, &str_size)) goto fail;
if (!FcStrSetAddFilename(ret, text)) goto fail;
}
return ret;
fail:
FcStrSetDestroy(ret);
return NULL;
}
bool encodeStrList(cmp_ctx_t *bytes, FcStrList *iter) {
uint32_t size = 0;
FcStrListFirst(iter);
while (FcStrListNext(iter) != NULL) size++;
if (!cmp_write_array(bytes, size)) return false;
FcStrListFirst(iter);
char *text = FcStrListNext(iter);
while (text != NULL) {
if (!cmp_write_str(bytes, text, strlen(text))) return false;
text = FcStrListNext(iter);
}
return true;
}
bool encodeStrSet(cmp_ctx_t *bytes, FcStrSet *data) {
if (bytes == NULL || data == NULL) return false;
FcStrList *iter = FcStrListCreate(data);
if (iter == NULL) return false;
bool ret = encodeStrList(bytes, iter);
FcStrListDone(iter);
return ret;
}
FcCharSet *decodeCharSet(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_array(bytes, &size)) return NULL;
FcCharSet *ret = FcCharSetCreate();
if (ret == NULL) return NULL;
FcChar32 prev = 0;
for (uint32_t i = 0; i < size; i++) {
uint32_t x;
if (!cmp_read_uint(bytes, &x)) goto fail;
prev += x;
if (!FcCharSetAddChar(ret, prev)) goto fail;
}
return ret;
fail:
FcCharSetDestroy(ret);
return NULL;
}
bool encodeCharSet(cmp_ctx_t *bytes, const FcCharSet *data) {
if (bytes == NULL || data == NULL) return false;
FcChar32 size = FcCharSetCount(data);
FcChar32 count = 0; // For validation
if (!cmp_write_array(bytes, size)) return false;
FcChar32 map[FC_CHARSET_MAP_SIZE];
FcChar32 next;
FcChar32 c = FcCharSetFirstPage(data, map, &next);
FcChar32 prev = 0;
while (c != FC_CHARSET_DONE) {
if (!cmp_write_uinteger(bytes, c - prev)) return false;
prev = c;
count++;
c = FcCharSetNextPage(data, map, &next);
}
assert(size == count);
return true;
}
FcLangSet *decodeLangSet(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_array(bytes, &size)) return NULL;
FcLangSet *ret = FcLangSetCreate();
if (ret == NULL) return NULL;
for (uint32_t i = 0; i < size; i++) {
char lang[10];
uint32_t str_size = 10;
if (!cmp_read_str(bytes, lang, &str_size)) goto fail;
char *lang2 = FcLangNormalize(lang);
if (lang2 == NULL) goto fail;
if (!FcLangSetAdd(ret, lang2)) goto fail;
free(lang2);
}
return ret;
fail:
FcLangSetDestroy(ret);
return NULL;
}
bool encodeLangSet(cmp_ctx_t *bytes, const FcLangSet *data) {
if (bytes == NULL || data == NULL) return false;
FcStrSet *langs = FcLangSetGetLangs(data);
if (langs == NULL) return false;
bool ret = encodeStrSet(bytes, langs);
FcStrSetDestroy(langs);
return ret;
}
FcObjectSet *decodeObjectSet(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_array(bytes, &size)) return NULL;
FcObjectSet *ret = FcObjectSetCreate();
for (uint32_t i = 0; i < size; i++) {
char object[20];
uint32_t o_size = 20;
if (!cmp_read_str(bytes, object, &o_size)) goto fail;
if (!FcObjectSetAdd(ret, object)) goto fail;
}
return ret;
fail:
FcObjectSetDestroy(ret);
return NULL;
}
// No corresponding objectset encoder.
FcRange *decodeRange(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_map(bytes, &size)) return NULL;
double range[2];
for (uint32_t i = 0; i < size; i++) {
uint32_t j;
if (!cmp_read_uint(bytes, &j)) return NULL;
if (!cmp_read_double(bytes, &range[j])) return NULL;
}
return FcRangeCreateDouble(range[0], range[1]);
}
bool encodeRange(cmp_ctx_t *bytes, const FcRange *data) {
if (bytes == NULL || data == NULL) return false;
double begin, end;
if (!FcRangeGetDouble(data, &begin, &end)) return false;
if (!cmp_write_map(bytes, 2)) return false;
if (!cmp_write_uint(bytes, 0)) return false;
if (!cmp_write_double(bytes, begin)) return false;
if (!cmp_write_uint(bytes, 1)) return false;
if (!cmp_write_double(bytes, end)) return false;
return true;
}
FcMatrix *decodeMatrix(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_array(bytes, &size) || size != 4) return NULL;
FcMatrix *ret = malloc(sizeof(FcMatrix));
if (!cmp_read_double(bytes, &ret->xx)) goto fail;
if (!cmp_read_double(bytes, &ret->xy)) goto fail;
if (!cmp_read_double(bytes, &ret->yx)) goto fail;
if (!cmp_read_double(bytes, &ret->yy)) goto fail;
return ret;
fail:
free(ret);
return NULL;
}
bool encodeMatrix(cmp_ctx_t *bytes, const FcMatrix *data) {
if (bytes == NULL || data == NULL) return NULL;
if (!cmp_write_array(bytes, 4)) return false;
if (!cmp_write_double(bytes, data->xx)) return false;
if (!cmp_write_double(bytes, data->xy)) return false;
if (!cmp_write_double(bytes, data->yx)) return false;
if (!cmp_write_double(bytes, data->yy)) return false;
return true;
}
bool decodeValue(cmp_ctx_t *bytes, FcValue *out) {
if (bytes == NULL || out == NULL) return false;
bool b;
uint32_t str_size;
if (cmp_read_nil(bytes)) out->type = FcTypeVoid;
else if (cmp_read_int(bytes, &out->u.i)) out->type = FcTypeInteger;
else if (cmp_read_double(bytes, &out->u.d)) out->type = FcTypeDouble;
else if (cmp_read_str_size(bytes, &str_size)) {
out->type = FcTypeString;
char *str = malloc(str_size);
out->u.s = str;
return cmp_read_str(bytes, str, &str_size);
} else if (cmp_read_bool(bytes, &b)) {
out->type = FcTypeBool;
out->u.b = b ? FcTrue : FcFalse; // Didn't auto-convert.
} else if ((out->u.m = decodeMatrix(bytes)) != NULL) out->type = FcTypeMatrix;
else if ((out->u.c = decodeCharSet(bytes)) != NULL) out->type = FcTypeCharSet;
// Not supporting FcTypeFcFace
else if ((out->u.l = decodeLangSet(bytes)) != NULL) out->type = FcTypeLangSet;
else if ((out->u.r = decodeRange(bytes)) != NULL) out->type = FcTypeRange;
else return false;
return true;
}
bool encodeValue(cmp_ctx_t *bytes, FcValue *data) {
if (bytes == NULL || data == NULL) return false;
switch (data->type) {
case FcTypeVoid:
return cmp_write_nil(bytes);
case FcTypeInteger:
return cmp_write_int(bytes, data->u.i);
case FcTypeDouble:
return cmp_write_double(bytes, data->u.d);
case FcTypeString:
return cmp_write_str(bytes, data->u.s, strlen(data->u.s));
case FcTypeBool:
return cmp_write_bool(bytes, data->u.b);
case FcTypeMatrix:
return encodeMatrix(bytes, data->u.m);
case FcTypeCharSet:
return encodeCharSet(bytes, data->u.c);
case FcTypeFTFace:
return true; // Not supporting this yet...
case FcTypeLangSet:
return encodeLangSet(bytes, data->u.l);
case FcTypeRange:
return encodeRange(bytes, data->u.r);
default:
return false;
}
}
FcPattern *decodePattern(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_map(bytes, &size)) return NULL;
FcPattern *ret = FcPatternCreate();
for (uint32_t i = 0; i < size; i++) {
char object[20];
uint32_t osize = 20;
if (!cmp_read_str(bytes, object, &osize)) goto fail;
uint32_t vsize;
if (!cmp_read_array(bytes, &vsize)) goto fail;
for (uint32_t j = 0; j < vsize; j++) {
uint32_t tsize;
if (!cmp_read_array(bytes, &tsize) || tsize != 2) goto fail;
bool is_strong = false;
if (cmp_read_bool(bytes, &is_strong)) {}
else if (cmp_read_nil(bytes)) {}
else goto fail;
FcValue val;
if (!decodeValue(bytes, &val)) goto fail;
if (is_strong) {
if (!FcPatternAdd(ret, object, val, true)) goto fail;
} else {
if (!FcPatternAddWeak(ret, object, val, true)) goto fail;
}
}
}
return ret;
fail:
FcPatternDestroy(ret);
return NULL;
}
bool encodePattern(cmp_ctx_t *bytes, FcPattern *data) {
if (bytes == NULL || data == NULL) return false;
int size = FcPatternObjectCount(data);
if (!cmp_write_map(bytes, size)) return false;
FcPatternIter iter;
FcPatternIterStart(data, &iter);
int count = 0;
while (FcPatternIterNext(data, &iter)) {
count++;
const char *obj = FcPatternIterGetObject(data, &iter);
if (!cmp_write_str(bytes, obj, strlen(obj))) return false;
int nvalues = FcPatternIterValueCount(data, &iter);
if (!cmp_write_array(bytes, nvalues)) return false;
for (int j = 0; j < nvalues; j++) {
FcValue val;
FcValueBinding weight;
if (FcPatternIterGetValue(data, &iter, j, &val, &weight) != FcResultMatch)
return false;
if (!cmp_write_array(bytes, 2)) return false;
switch (weight) {
case FcValueBindingWeak:
if (!cmp_write_bool(bytes, false)) return false;
break;
case FcValueBindingStrong:
if (!cmp_write_bool(bytes, true)) return false;
break;
case FcValueBindingSame:
if (!cmp_write_nil(bytes)) return false;
break;
default:
return false;
}
if (!encodeValue(bytes, &val)) return false;
}
}
assert(size == count);
return true;
}
FcFontSet *decodeFontSet(cmp_ctx_t *bytes) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_array(bytes, &size)) return NULL;
FcFontSet *ret = FcFontSetCreate();
if (ret == NULL) return NULL;
for (uint32_t i = 0; i < size; i++) {
FcPattern *font = decodePattern(bytes);
if (font == NULL) goto fail;
if (!FcFontSetAdd(ret, font)) goto fail;
}
return ret;
fail:
FcFontSetDestroy(ret);
return NULL;
}
FcFontSet **decodeFontSets(cmp_ctx_t *bytes, size_t *nsets) {
if (bytes == NULL) return NULL;
uint32_t size;
if (!cmp_read_array(bytes, &size)) return NULL;
if (nsets != NULL) *nsets = size;
FcFontSet **ret = calloc(sizeof(FcFontSet *), size);
uint32_t i;
for (i = 0; i < size; i++) {
ret[i] = decodeFontSet(bytes);
if (ret[i] == NULL) goto fail;
i++;
}
return ret;
fail:
for (uint32_t j = 0; j < i; j++) FcFontSetDestroy(ret[j]);
free(ret);
return NULL;
}
bool encodeFontSet(cmp_ctx_t *bytes, FcFontSet *data) {
if (bytes == NULL || data == NULL) return NULL;
if (!cmp_write_array(bytes, data->nfont)) return false;
for (int i = 0; i < data->nfont; i++) {
if (!encodePattern(bytes, data->fonts[i])) return false;
}
return true;
}
bool encodeRenderableFontSet(cmp_ctx_t *bytes, FcConfig *conf, FcPattern *pat, FcFontSet *data) {
if (bytes == NULL || data == NULL) return false;
if (!cmp_write_array(bytes, data->nfont)) return false;
for (int i = 0; i < data->nfont; i++) {
FcPattern *postprocessed = FcFontRenderPrepare(conf, pat, data->fonts[i]);
if (postprocessed == NULL) return false;
bool ok = encodePattern(bytes, postprocessed);
FcPatternDestroy(postprocessed);
if (!ok) return false;
}
return true;
}
bool encodeResult(cmp_ctx_t *bytes, FcResult res) {
switch (res) {
case FcResultMatch: // Should be handled by caller! Can't do anything sensible here.
case FcResultNoMatch: // May be handled by caller.
return cmp_write_nil(bytes);
case FcResultTypeMismatch:
return cmp_write_str(bytes, "ErrType", strlen("ErrType"));
case FcResultNoId:
return cmp_write_str(bytes, "ErrNoId", strlen("ErrNoId"));
case FcResultOutOfMemory:
return cmp_write_str(bytes, "ErrOOM", strlen("ErrOOM"));
default:
// Should never happen!
return cmp_write_str(bytes, "ErrOther", strlen("ErrOther"));
}
}
struct bytes {
uint8_t *start;
size_t offset;
size_t length;
};
bool bytes_reader(struct cmp_ctx_s *ctx, void *data, size_t limit) {
struct bytes *bytes = ctx->buf;
if (bytes->offset + limit > bytes->length) return false;
memcpy(data, bytes->start + bytes->offset, limit);
bytes->offset += limit;
return true;
}
bool bytes_skipper(struct cmp_ctx_s *ctx, size_t count) {
struct bytes *bytes = ctx->buf;
if (bytes->offset + count > bytes->length) return false;
bytes->offset += count;
return true;
}
size_t bytes_writer(struct cmp_ctx_s *ctx, const void *data, size_t count) {
struct bytes *bytes = ctx->buf;
if (bytes->offset + count > bytes->length) {
bytes->start = realloc(bytes->start, 2*bytes->length);
if (bytes->start == NULL) return 0;
bytes->length = 2*bytes->length;
}
memcpy(bytes->start + bytes->offset, data, count);
bytes->offset += count;
return count;
}
bool cmp_bytes_init(cmp_ctx_t *ctx, uint8_t *buf, size_t length) {
struct bytes *inner = malloc(sizeof(struct bytes));
if (inner == NULL) return false;
inner->start = buf;
inner->offset = 0;
inner->length = length;
cmp_init(ctx, inner, bytes_reader, bytes_skipper, bytes_writer);
return true;
}
bool cmp_bytes_alloc(cmp_ctx_t *ctx, size_t length) {
uint8_t *bytes = malloc(length);
if (bytes == NULL) return false;
return cmp_bytes_init(ctx, bytes, length);
}
void cmp_bytes_free(cmp_ctx_t *ctx) {
struct bytes *bytes = ctx->buf;
free(bytes->start);
free(bytes);
}
uint8_t *cmp_bytes_take(cmp_ctx_t *ctx, size_t *length) {
struct bytes *bytes = ctx->buf;
uint8_t *ret = bytes->start;
if (length != NULL) *length = bytes->offset;
free(bytes);
return ret;
}