#include "cmp.h" #include #include #include #include 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 encodeStrSet(cmp_ctx_t *bytes, FcStrSet *data) { if (bytes == NULL || data == NULL) return false; bool ret = false; FcStrList *iter = FcStrListCreate(data); if (iter == NULL) return false; uint32_t size = 0; FcStrListFirst(iter); while (FcStrListNext(iter) != NULL) size++; if (!cmp_write_array(bytes, size)) goto exit; FcStrListFirst(iter); char *text = FcStrListNext(iter); while (text != NULL) { if (!cmp_write_str(bytes, text, strlen(text))) goto exit; text = FcStrListNext(iter); } ret = true; exit: 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 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; data = bytes->start + bytes->offset; 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) { struct 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; }