Sigrid Solveig Haflínudóttirinitial import8808c535 months agoHistory
672 lines · 19.7 KB · C
RawDownload
#ifndef Plan9#include <inttypes.h>#include <stdarg.h>#include <stdint.h>#include <stdio.h>#include <string.h>#endif#include "pb.h" #define MAX(a, b) (a > b ? a : b)#define MIN(a, b) (a < b ? a : b) static int bytes_size(const struct pb_field *f, const void *data, const struct pb_msg *msg); static intis_default(const uint8_t *o, const struct pb_field *f, const struct pb_data *pb, const struct pb_msg *msg){	const struct pb_default *dflt;	const void *fin;	int i, type, sizebytes; 	if((f->flags & PB_FLAG_REQUIRED) != 0)		return 0;	if(f->num_at >= 0)		return *(int*)(o + f->num_at) == 0;	dflt = f->dfltidx > 0 ? &pb->dflts[f->dfltidx-1] : NULL;	type = f->flags & PB_FLAG_MASK_TYPE;	fin = o + f->offset;	if(f->flags & PB_FLAG_POINTER)		fin = *(const void**)fin;	if(type == PB_TYPE_STRING){		if(dflt != NULL)			return fin != NULL && strcmp(fin, dflt->pointer) == 0;		return fin == NULL || *(const char*)fin == 0;	}	if(type == PB_TYPE_INTEGER && (f->flags & PB_FLAG_POINTER) == 0 && dflt != NULL){		if(f->size == 4) return *(int32_t*)fin == (int32_t)dflt->integer;		if(f->size == 8) return *(int64_t*)fin == (int64_t)dflt->integer;		if(f->size == 2) return *(int16_t*)fin == (int16_t)dflt->integer;		return *(int8_t*)fin == (int8_t)dflt->integer;	}	if(type == PB_TYPE_MSG){		for(i = 0; fin != NULL && i < pb->msgs[f->msgidx].num_fields; i++){			if(!is_default(fin, &pb->msgs[f->msgidx].fields[i], pb, msg))				return 0;		}		return 1;	}	if(f->flags & PB_FLAG_POINTER)		return fin == NULL; 	sizebytes = f->size;	if(type == PB_TYPE_BYTES && (sizebytes = bytes_size(f, o, msg)) < 0)		return 0; // it could be -1, but we do this check later	for(i = 0; i < sizebytes; i++)		if(((uint8_t*)fin)[i] != 0)			return 0; 	return 1;} static void *errorf(const struct pb_msg *, const char *, int, const char *, ...) PB_GNUC_ATTR(format(printf, 4, 5)); #ifndef PB_PRINTERS#define fname(f) "?"#else#define fname(f) (f)->name char *seprintf(char *s, char *e, char *fmt, ...){	va_list arg;	int n; 	va_start(arg, fmt);	n = vsnprintf(s, e-s, fmt, arg);	va_end(arg); 	return e-s > n ? s+n : e;} static char *print_single(char *s, char *e, const uint8_t *fin, const struct pb_field *f, int left, int indent, const void *msg, const struct pb_data *pb, const struct pb_msg *pbmsg){	int i, sz, type; 	type = f->flags & PB_FLAG_MASK_TYPE;	if(f->seprint != NULL){		s = f->seprint(fin, s, e, left, indent, f);	}else if(type == PB_TYPE_MSG){		s = pb_seprint(fin, s, e, &pb->msgs[f->msgidx], pb, left, indent);	}else if(type == PB_TYPE_INTEGER){		if((f->flags & PB_FLAG_SIGNED) != 0 || (f->flags & PB_FLAG_TYPE_UNSIGNED) == 0){			     if(f->size == 4) s = seprintf(s, e, "%d", *(int32_t*)fin);			else if(f->size == 8) s = seprintf(s, e, "%"PRId64, *(int64_t*)fin);			else if(f->size == 2) s = seprintf(s, e, "%d", *(int16_t*)fin);			else                  s = seprintf(s, e, "%d", *(int8_t*)fin);		}else{			     if(f->size == 4) s = seprintf(s, e, "%u", *(uint32_t*)fin);			else if(f->size == 8) s = seprintf(s, e, "%"PRIu64, *(uint64_t*)fin);			else if(f->size == 2) s = seprintf(s, e, "%u", *(uint16_t*)fin);			else                  s = seprintf(s, e, "%u", *(uint8_t*)fin);		}	}else if(type == PB_TYPE_STRING){		s = seprintf(s, e, "\"%s\"", (char*)fin);	}else if(type == PB_TYPE_BYTES){		if((sz = bytes_size(f, msg, pbmsg)) < 0)			return NULL;		s = seprintf(s, e, "0x");		for(i = 0; i < sz; i++)			s = seprintf(s, e, "%02x", fin[i]);		s = seprintf(s, e, " (%d byte%s)", sz, sz == 1 ? "" : "s");	} 	return s;} char *pb_seprint(const void *in, char *s, char *e, const struct pb_msg *msg, const struct pb_data *pb, int left, int indent){	const struct pb_field *f;	const uint8_t *o, *fin;	char *old;	int i, j, num, sz, type; 	o = in;	old = s = seprintf(s, e, "{\n");	for(i = 0; i < msg->num_fields; i++){		f = &msg->fields[msg->indices[i]];		if(f->flags & PB_FLAG_NO_PRINT)			continue;		if(is_default(o, f, pb, msg))			continue; 		type = f->flags & PB_FLAG_MASK_TYPE;		if(f->num_at >= 0 && type != PB_TYPE_BYTES){			s = seprintf(s, e, "%*s%s = [", left+indent, "", fname(f));			if(f->index > 0)				s = seprintf(s, e, " // #%d", f->index);			s = seprintf(s, e, "\n");			num = *(int*)(o + f->num_at);			for(j = 0; j < num && s != NULL; j++){				fin = o + f->offset;				if(f->flags & PB_FLAG_POINTER)					fin = *(uint8_t**)fin;				fin += j*f->size;				s = seprintf(s, e, "%*s", left+indent+indent, "");				s = print_single(s, e, fin, f, left+indent+indent, indent, o, pb, msg);				s = seprintf(s, e, ",\n");			}			s = seprintf(s, e, "%*s] (%d item%s)", left+indent, "", num, num == 1 ? "" : "s");		}else{			fin = o + f->offset;			if(f->flags & PB_FLAG_POINTER)				fin = *(uint8_t**)fin;			if(type == PB_TYPE_BYTES){				if((sz = bytes_size(f, in, msg)) < 0)					return NULL;				if(sz < 1)					continue;			}			s = seprintf(s, e, "%*s%s = ", left+indent, "", fname(f));			s = print_single(s, e, fin, f, left+indent, indent, o, pb, msg);		}		s = seprintf(s, e, ",");		if(f->index > 0)			s = seprintf(s, e, " // #%d", f->index);		s = seprintf(s, e, "\n");	}	if(old != s)		s = seprintf(s, e, "%*s}", left, "");	else		s = seprintf(old-1, e, "}"); 	return s;}#endif static void *errorf(const struct pb_msg *msg, const char *f, int findex, const char *fmt, ...){	va_list ap; 	va_start(ap, fmt);#ifdef PB_PRINTERS	fprintf(stderr, "pb: %s.%s (#%d): ", msg->name, f, findex);#else	(void)msg;	(void)f;	fprintf(stderr, "pb: #%d: ", findex);#endif	vfprintf(stderr, fmt, ap);	va_end(ap); 	return NULL;} static intbytes_size(const struct pb_field *f, const void *data, const struct pb_msg *msg){	int sz;	if(f->flags & PB_FLAG_CONST_SIZE)		return f->size;	if(f->num_at < 0)		return f->num;	sz = *(int*)((uint8_t*)data + f->num_at);	if(sz < 0 || ((f->flags & PB_FLAG_POINTER) == 0 && sz > f->num)){		errorf(msg, fname(f), f->index, "invalid size %d\n", sz);		return -1;	}	return sz;} static voidset_default(void *out, const struct pb_field *f, const struct pb_data *pb){	const struct pb_default *dflt;	uint8_t *o;	int i, type, sizebytes; 	o = out;	type = f->flags & PB_FLAG_MASK_TYPE;	dflt = (pb->dflts != NULL && f->dfltidx > 0) ? &pb->dflts[f->dfltidx-1] : NULL;	if(f->flags & PB_FLAG_POINTER){		*(void**)(o + f->offset) = NULL;	}else if(type == PB_TYPE_MSG){		i = 0;		do{			pb_reset(o + f->offset + i*f->size, &pb->msgs[f->msgidx], pb);		}while(++i < f->num);	}else if(type == PB_TYPE_INTEGER && dflt != NULL){		     if(f->size == 4) *(int32_t*)(o + f->offset) = dflt->integer;		else if(f->size == 8) *(int64_t*)(o + f->offset) = dflt->integer;		else if(f->size == 2) *(int16_t*)(o + f->offset) = dflt->integer;		else                  *(uint8_t*)(o + f->offset) = dflt->integer;	}else if(dflt != NULL && dflt->size > 0){		memmove(o + f->offset, dflt->pointer, MIN(dflt->size, f->size));	}else{		sizebytes = f->size;		if(type == PB_TYPE_BYTES && (f->flags & PB_FLAG_CONST_SIZE) == 0)			sizebytes = f->num;		memset(o + f->offset, 0, sizebytes);	} 	if(f->num_at >= 0)		*(int*)(o + f->num_at) = 0;} voidpb_reset(void *out, const struct pb_msg *msg, const struct pb_data *pb){	const struct pb_field *f;	uint8_t *o;	int i; 	for(i = 0, f = msg->fields; i < msg->num_fields; i++, f++)		set_default(out, f, pb);	o = out;	if(msg->rest_at >= 0)		memset(o + msg->rest_at, 0, 2);	if(msg->buffer_at >= 0)		memset(o + msg->buffer_at, 0, 2);} static const uint8_t *varint_read(uint64_t *out, const struct pb_msg *msg, const uint8_t *b, const uint8_t *e, const char *what){	int varint_bits; 	for(varint_bits = 0, *out = 0; varint_bits < 64; varint_bits += 7, b++){		if(b == e)			return errorf(msg, what, -1, "short read: need 1, have 0\n");		*out |= (uint64_t)(*b & 0x7f) << varint_bits;		if((*b & 0x80) == 0){			b++;			break;		}	}	if(varint_bits >= 64 || (varint_bits == 63 && b[-1] > 1))		return errorf(msg, what, -1, "too many bits in a varint\n"); 	return b;} static intvarint_outofrange(const struct pb_field *f, int64_t varint){	if((f->flags & PB_FLAG_SIGNED) != 0 || (f->flags & PB_FLAG_TYPE_UNSIGNED) == 0){		     if(f->size == 4) return varint < -0x80000000LL || varint > 0x7fffffffLL;		else if(f->size == 2) return varint < -0x8000LL || varint > 0x7fffLL;		else if(f->size == 1) return varint < -0x80LL || varint > 0x7fLL;	}else{		     if(f->size == 4) return varint < 0LL || varint > 0xffffffffLL;		else if(f->size == 2) return varint < 0LL || varint > 0xffffLL;		else if(f->size == 1) return varint < 0LL || varint > 0xffLL;	} 	return 0;} static intru16(const uint8_t *b){	return (int)b[0] | (int)b[1]<<8;} static voidwu16(uint8_t *b, uint32_t x){	b[0] = x;	b[1] = x>>8;} const uint8_t *pb_decode(void *out, const uint8_t *s, const uint8_t *e, const struct pb_msg *msg, const struct pb_data *pb){	uint64_t varint;	const struct pb_field *f;	uint8_t *o, *oldvalue, *value, **assignvalue;	const uint8_t *b;	const void *cb;	uint64_t fields_set[2];	int fsetidx, fsetshift, pbtype;	int i, res, datasz, max_bytes_size, raw_size, rest_left, rest_sz, written;	uint8_t type, fieldn; 	o = out;	b = s;	if(msg->rest_at >= 0)		memset(o + msg->rest_at, 0, 2);	if(msg->buffer_at >= 0)		memset(o + msg->buffer_at, 0, 2); 	fields_set[0] = 1; /* index=0 is non-protobuf fields, don't reset them after parsing */	fields_set[1] = 0;	for(; b != e;){		s = b;		if((b = varint_read(&varint, msg, b, e, "?tag")) == NULL)			return NULL;		type = varint & 7;		varint >>= 3;		fieldn = varint;		if(type != 0 && type != 2) /* no, we don't need it in the "rest" either */			return errorf(msg, "?", fieldn, "unsupported protobuf type %d\n", type);		fsetidx = fieldn/64;		fsetshift = fieldn%64; 		if((b = varint_read(&varint, msg, b, e, "?sz")) == NULL)			return NULL; 		datasz = 0;		if(type == 2){ /* string, bytes, message, packed repeated */			datasz = varint;			if(datasz < 0)				return errorf(msg, "?sz", fieldn, "invalid data size: %d\n", datasz);			if(datasz > e-b)				return errorf(msg, "?sz", fieldn, "short read: need %d, have %d\n", datasz, (int)(e-b));		} 		/* search for the field */		for(f = msg->fields, i = 0; i < msg->num_fields && (f->index == 0 || f->index != fieldn); i++, f++);		if(i >= msg->num_fields){ /* wasn't found */			if(msg->rest_at >= 0){				raw_size = b - s + datasz;				rest_sz = ru16(o + msg->rest_at);				rest_left = msg->rest_size - rest_sz - 2;				if(rest_left < raw_size)					return errorf(msg, "?", fieldn, "no space in \"rest\": need %d, have %d\n", raw_size, rest_left);				memmove(o + msg->rest_at + 2 + rest_sz, s, raw_size);				wu16(o + msg->rest_at, rest_sz + raw_size);			}			b += datasz;			continue;		} 		cb = (f->cbidx > 0 && pb->cbs != NULL) ? pb->cbs[f->cbidx-1] : NULL; 		value = o + f->offset;		if(f->num_at >= 0 && f->num > 0){ /* if it's an array of constant size */			if((fields_set[fsetidx] & (1ULL<<fsetshift)) == 0) /* reset "num" the first time we see the field */				*(int*)(o + f->num_at) = 0; 			if(*(int*)(o + f->num_at) >= f->num){				/* nowhere to save */				b += datasz;				continue;			}			value += *(int*)(o + f->num_at) * f->size;		}else if(f->num_at < 0 && (fields_set[fsetidx] & (1ULL<<fsetshift)) != 0){			/* repeated value, but the field isn't array and is already set, so just keep the first value */			b += datasz;			continue;		}		pbtype = f->flags & PB_FLAG_MASK_TYPE;		max_bytes_size = 0;		if(f->flags & PB_FLAG_POINTER){			assignvalue = (void*)value;			value = *(uint8_t**)value;			if(f->flags & PB_FLAG_IS_CONST){				max_bytes_size = 0x1000000; /* arbitrary limit, randomly picked */			}else if(pbtype == PB_TYPE_STRING || pbtype == PB_TYPE_BYTES){				max_bytes_size = MAX(0, msg->buffer_size - ru16(o + msg->buffer_at) - 2);				value = o + msg->buffer_at + 2 + ru16(o + msg->buffer_at);			}else if(value == NULL){				b += datasz;				continue;			}		}else{			assignvalue = NULL;			max_bytes_size = f->size;			if(pbtype == PB_TYPE_BYTES && (f->flags & PB_FLAG_CONST_SIZE) == 0)				max_bytes_size = f->num;		}		if(pbtype == PB_TYPE_STRING) /* needs to be null-terminated */			max_bytes_size--; 		if(type == 0){ /* varint */			if(pbtype != PB_TYPE_INTEGER)				return errorf(msg, fname(f), f->index, "expected type %d, got varint instead\n", pbtype);			if(f->flags & PB_FLAG_SIGNED)				varint = varint>>1 ^ -(varint&1);			if(varint_outofrange(f, varint)){				return errorf(msg, fname(f), f->index, "varint out of range: can't fit %"PRId64" in %d bits (%ssigned)\n",					varint, f->size*8, (f->flags & PB_FLAG_SIGNED) ? "" : "un");			} 			     if(f->size == 4) *(int32_t*)value = varint;			else if(f->size == 8) *(int64_t*)value = varint;			else if(f->size == 2) *(int16_t*)value = varint;			else                  *(uint8_t*)value = varint;			res = cb == NULL ? PB_CALLBACK_OK : ((int (*)(void*, void*))cb)(out, value);			if(res < 0)				return NULL;			if(res == PB_CALLBACK_THROW_AWAY){				b += datasz;				continue;			}			fields_set[fsetidx] |= 1ULL<<fsetshift;		}else if(type == 2){			if(pbtype == PB_TYPE_INTEGER){ /* packed integers */				oldvalue = value;				for(s = b; b != s+datasz;){					if((b = varint_read(&varint, msg, b, e, "?int")) == NULL)						return NULL;					if(f->flags & PB_FLAG_SIGNED)						varint = varint>>1 ^ -(varint&1);					if(varint_outofrange(f, varint)){						return errorf(msg, fname(f), f->index, "varint out of range: can't fit %"PRId64" in %d bits (%ssigned)\n",							varint, f->size*8, (f->flags & PB_FLAG_SIGNED) ? "" : "un");					} 					     if(f->size == 4) *(int32_t*)value = varint;					else if(f->size == 8) *(int64_t*)value = varint;					else if(f->size == 2) *(int16_t*)value = varint;					else                  *(uint8_t*)value = varint; 					res = cb == NULL ? PB_CALLBACK_OK : ((int (*)(void*, void*))cb)(out, value);					if(res < 0)						return NULL;					if(res != PB_CALLBACK_THROW_AWAY)						value += f->size;					if(f->num_at < 0 || ++(*(int*)(o + f->num_at)) >= f->num)						break;				}				b = s + datasz;				if(oldvalue != value)					fields_set[fsetidx] |= 1ULL<<fsetshift;				continue;			} 			if(pbtype == PB_TYPE_MSG){				if(pb_decode(value, b, b+datasz, &pb->msgs[f->msgidx], pb) == NULL)					return NULL;				res = cb == NULL ? PB_CALLBACK_OK : ((int (*)(void*, void*))cb)(out, value);				if(res < 0)					return NULL;				if(res == PB_CALLBACK_THROW_AWAY){					b += datasz;					continue;				}				fields_set[fsetidx] |= 1ULL<<fsetshift;			}else{				if((datasz > max_bytes_size && (f->flags & PB_FLAG_TRUNCATE) == 0) ||				   (datasz != max_bytes_size && (f->flags & PB_FLAG_CONST_SIZE) != 0)){					return errorf(msg, fname(f), f->index, "unexpected size: should be %s%d, have %d\n",						(f->flags & PB_FLAG_CONST_SIZE) ? "==" : "<=", max_bytes_size, datasz);				} 				written = MAX(0, MIN(datasz, max_bytes_size));				res = PB_CALLBACK_OK;				if(written > 0){					if(f->flags & PB_FLAG_IS_CONST){						*assignvalue = value = (void*)b;					}else{						memmove(value, b, written);						if(pbtype == PB_TYPE_STRING)							value[written++] = 0;						if((f->flags & PB_FLAG_POINTER) != 0 && msg->buffer_at >= 0){							*assignvalue = value;							wu16(o + msg->buffer_at, ru16(o + msg->buffer_at)+written);

Lines 1–500 of 672.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t