summaryrefslogblamecommitdiffstats
path: root/libparsesfnt_parse___.c
blob: 322ed24766e250d87e3072f506810e4ea8e92a54 (plain) (tree)




















































































                                                                                              
                              













                                                     














                                                                














                                                  
                                                        








                                                                         










                                                                                  



                                                                                          

















                                                                          


                           









                                                                                
                         

                                       












                       
/* See LICENSE file for copyright and license details. */
#include "common.h"


static uint16_t
parse16(const char *data)
{
	uint16_t b1 = (uint16_t)((const uint8_t *)data)[0];
	uint16_t b2 = (uint16_t)((const uint8_t *)data)[1];
	b1 = (uint16_t)(b1 << 8);
	b2 = (uint16_t)(b2 << 0);
	return (uint16_t)(b1 | b2);
}

static uint32_t
parse32(const char *data)
{
	uint32_t b1 = (uint32_t)((const uint8_t *)data)[0];
	uint32_t b2 = (uint32_t)((const uint8_t *)data)[1];
	uint32_t b3 = (uint32_t)((const uint8_t *)data)[2];
	uint32_t b4 = (uint32_t)((const uint8_t *)data)[3];
	b1 = (uint32_t)(b1 << 24);
	b2 = (uint32_t)(b2 << 16);
	b3 = (uint32_t)(b3 << 8);
	b4 = (uint32_t)(b4 << 0);
	return (uint32_t)(b1 | b2 | b3 | b4);
}

static uint64_t
parse64(const char *data)
{
	uint64_t b1 = (uint64_t)((const uint8_t *)data)[0];
	uint64_t b2 = (uint64_t)((const uint8_t *)data)[1];
	uint64_t b3 = (uint64_t)((const uint8_t *)data)[2];
	uint64_t b4 = (uint64_t)((const uint8_t *)data)[3];
	uint64_t b5 = (uint64_t)((const uint8_t *)data)[4];
	uint64_t b6 = (uint64_t)((const uint8_t *)data)[5];
	uint64_t b7 = (uint64_t)((const uint8_t *)data)[6];
	uint64_t b8 = (uint64_t)((const uint8_t *)data)[7];
	b1 = (uint32_t)(b1 << 56);
	b2 = (uint32_t)(b2 << 48);
	b3 = (uint32_t)(b3 << 40);
	b4 = (uint32_t)(b4 << 32);
	b5 = (uint32_t)(b5 << 24);
	b6 = (uint32_t)(b6 << 16);
	b7 = (uint32_t)(b7 << 8);
	b8 = (uint32_t)(b8 << 0);
	return (uint32_t)(b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8);
}


static void
sign8(union {uint8_t u; int8_t s;} *info)
{
	info->s = ((info->u >> 7) ? -(int8_t)(~info->u + 1) : (int8_t)info->u);
}

static void
sign16(union {uint16_t u; int16_t s;} *info)
{
	info->s = ((info->u >> 15) ? -(int16_t)(~info->u + 1) : (int16_t)info->u);
}

static void
sign32(union {uint32_t u; int32_t s;} *info)
{
	info->s = ((info->u >> 31) ? -(int32_t)(~info->u + 1) : (int32_t)info->u);
}

static void
sign64(union {uint64_t u; int64_t s;} *info)
{
	info->s = ((info->u >> 63) ? -(int64_t)(~info->u + 1) : (int64_t)info->u);
}


int
libparsesfnt_parse___(const char *data, size_t size, void *infop, size_t esize,
                      const char *fmt, size_t offset, size_t offset2, size_t offset3,
                      size_t first, size_t count, const struct libparsesfnt_tabdir_entry *tag)
{
	const char *s;
	size_t tag_offset = 0;
	size_t need = 0;
	size_t padding = 0;
	size_t multiplier = 1;
	int is_signed = 0;
	char *info = infop;

	if (offset > size || offset2 > size - offset)
		goto ebfont;
	offset += offset2;
	if (offset3 > size - offset)
		goto ebfont;
	offset += offset3;

	if (tag)
		tag_offset = offset - tag->offset;

	for (s = fmt; *s; s++) {
		if (*s == 'p') {
			padding += multiplier * 1;
		} else if (*s == '1' || *s == '.') {
			need += multiplier * 1;
		} else if (*s == '2') {
			need += multiplier * 2;
		} else if (*s == '4') {
			need += multiplier * 4;
		} else if (*s == '8') {
			need += multiplier * 8;
		} else if (*s == '{') {
			multiplier = strtoul(s, (void *)&s, 10);
			continue;
		}
		multiplier = 1;
	}

	if (count > SIZE_MAX - first ||
	    first > (SIZE_MAX - offset) / need)
		goto efbig;
	offset += first * need;

	if (offset > size || need > size - offset)
		goto ebfont;

	if (count > (SIZE_MAX - offset) / need)
		goto efbig;
	if (offset + count * need > size ||
	    (tag && (tag->length < count * need ||
	             tag->length > size ||
	             tag->offset > size - tag->length ||
	             tag_offset > tag->length ||
	             first + count > (tag->length - tag_offset) / need)))
		goto ebfont;

	data = &data[offset];

	padding = esize - need - padding;
	for (; count--; info = &info[padding]) {
		for (s = fmt; *s; s++) {
			while (multiplier--) {
				if (*s == 'p') {
					*info++ = 0;
				} else if (*s == '.') {
					*info++ = *data++;
				} else if (*s == '1') {
					*(uint8_t *)info = *(const uint8_t *)data;
					if (is_signed)
						sign8((void *)info);
					data = &data[1];
					info = &info[1];
#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-align" /* in practice alignment will be correct */
#endif
				} else if (*s == '2') {
					*(uint16_t *)info = parse16(data);
					if (is_signed)
						sign16((void *)info);
					data = &data[2];
					info = &info[2];
				} else if (*s == '4') {
					*(uint32_t *)info = parse32(data);
					if (is_signed)
						sign32((void *)info);
					data = &data[4];
					info = &info[4];
				} else if (*s == '8') {
					*(uint64_t *)info = parse64(data);
					if (is_signed)
						sign64((void *)info);
					data = &data[8];
					info = &info[8];
#ifdef __GNUC__
# pragma GCC diagnostic pop
#endif
				} else if (*s == '-') {
					is_signed = 1;
				} else if (*s == '+') {
					is_signed = 0;
				} else if (*s == '{') {
					multiplier = strtoul(s, (void *)&s, 10);
					goto continue_s;
				} else {
					abort();
				}
			}
			multiplier = 1;
		continue_s:;
		}
	}

	return 0;

efbig:
	errno = EFBIG;
	return -1;

ebfont:
	errno = EBFONT;
	return -1;
}