adding pods method of package managing
This commit is contained in:
+621
@@ -0,0 +1,621 @@
|
||||
|
||||
#include <string.h>
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/upb.h"
|
||||
#else
|
||||
#include "upb/upb.h"
|
||||
#endif
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/decode.h"
|
||||
#else
|
||||
#include "upb/decode.h"
|
||||
#endif
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/port_def.inc"
|
||||
#else
|
||||
#include "upb/port_def.inc"
|
||||
#endif
|
||||
|
||||
/* Maps descriptor type -> upb field type. */
|
||||
const uint8_t upb_desctype_to_fieldtype[] = {
|
||||
UPB_WIRE_TYPE_END_GROUP, /* ENDGROUP */
|
||||
UPB_TYPE_DOUBLE, /* DOUBLE */
|
||||
UPB_TYPE_FLOAT, /* FLOAT */
|
||||
UPB_TYPE_INT64, /* INT64 */
|
||||
UPB_TYPE_UINT64, /* UINT64 */
|
||||
UPB_TYPE_INT32, /* INT32 */
|
||||
UPB_TYPE_UINT64, /* FIXED64 */
|
||||
UPB_TYPE_UINT32, /* FIXED32 */
|
||||
UPB_TYPE_BOOL, /* BOOL */
|
||||
UPB_TYPE_STRING, /* STRING */
|
||||
UPB_TYPE_MESSAGE, /* GROUP */
|
||||
UPB_TYPE_MESSAGE, /* MESSAGE */
|
||||
UPB_TYPE_BYTES, /* BYTES */
|
||||
UPB_TYPE_UINT32, /* UINT32 */
|
||||
UPB_TYPE_ENUM, /* ENUM */
|
||||
UPB_TYPE_INT32, /* SFIXED32 */
|
||||
UPB_TYPE_INT64, /* SFIXED64 */
|
||||
UPB_TYPE_INT32, /* SINT32 */
|
||||
UPB_TYPE_INT64, /* SINT64 */
|
||||
};
|
||||
|
||||
/* Data pertaining to the parse. */
|
||||
typedef struct {
|
||||
const char *ptr; /* Current parsing position. */
|
||||
const char *field_start; /* Start of this field. */
|
||||
const char *limit; /* End of delimited region or end of buffer. */
|
||||
upb_arena *arena;
|
||||
int depth;
|
||||
uint32_t end_group; /* Set to field number of END_GROUP tag, if any. */
|
||||
} upb_decstate;
|
||||
|
||||
/* Data passed by value to each parsing function. */
|
||||
typedef struct {
|
||||
char *msg;
|
||||
const upb_msglayout *layout;
|
||||
upb_decstate *state;
|
||||
} upb_decframe;
|
||||
|
||||
#define CHK(x) if (!(x)) { return 0; }
|
||||
|
||||
static bool upb_skip_unknowngroup(upb_decstate *d, int field_number);
|
||||
static bool upb_decode_message(upb_decstate *d, char *msg,
|
||||
const upb_msglayout *l);
|
||||
|
||||
static bool upb_decode_varint(const char **ptr, const char *limit,
|
||||
uint64_t *val) {
|
||||
uint8_t byte;
|
||||
int bitpos = 0;
|
||||
const char *p = *ptr;
|
||||
*val = 0;
|
||||
|
||||
do {
|
||||
CHK(bitpos < 70 && p < limit);
|
||||
byte = *p;
|
||||
*val |= (uint64_t)(byte & 0x7F) << bitpos;
|
||||
p++;
|
||||
bitpos += 7;
|
||||
} while (byte & 0x80);
|
||||
|
||||
*ptr = p;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool upb_decode_varint32(const char **ptr, const char *limit,
|
||||
uint32_t *val) {
|
||||
uint64_t u64;
|
||||
CHK(upb_decode_varint(ptr, limit, &u64) && u64 <= UINT32_MAX);
|
||||
*val = (uint32_t)u64;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool upb_decode_64bit(const char **ptr, const char *limit,
|
||||
uint64_t *val) {
|
||||
CHK(limit - *ptr >= 8);
|
||||
memcpy(val, *ptr, 8);
|
||||
*ptr += 8;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool upb_decode_32bit(const char **ptr, const char *limit,
|
||||
uint32_t *val) {
|
||||
CHK(limit - *ptr >= 4);
|
||||
memcpy(val, *ptr, 4);
|
||||
*ptr += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int32_t upb_zzdecode_32(uint32_t n) {
|
||||
return (n >> 1) ^ -(int32_t)(n & 1);
|
||||
}
|
||||
|
||||
static int64_t upb_zzdecode_64(uint64_t n) {
|
||||
return (n >> 1) ^ -(int64_t)(n & 1);
|
||||
}
|
||||
|
||||
static bool upb_decode_string(const char **ptr, const char *limit,
|
||||
int *outlen) {
|
||||
uint32_t len;
|
||||
|
||||
CHK(upb_decode_varint32(ptr, limit, &len) &&
|
||||
len < INT32_MAX &&
|
||||
limit - *ptr >= (int32_t)len);
|
||||
|
||||
*outlen = len;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void upb_set32(void *msg, size_t ofs, uint32_t val) {
|
||||
memcpy((char*)msg + ofs, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static bool upb_append_unknown(upb_decstate *d, upb_decframe *frame) {
|
||||
upb_msg_addunknown(frame->msg, d->field_start, d->ptr - d->field_start,
|
||||
d->arena);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool upb_skip_unknownfielddata(upb_decstate *d, uint32_t tag,
|
||||
uint32_t group_fieldnum) {
|
||||
switch (tag & 7) {
|
||||
case UPB_WIRE_TYPE_VARINT: {
|
||||
uint64_t val;
|
||||
return upb_decode_varint(&d->ptr, d->limit, &val);
|
||||
}
|
||||
case UPB_WIRE_TYPE_32BIT: {
|
||||
uint32_t val;
|
||||
return upb_decode_32bit(&d->ptr, d->limit, &val);
|
||||
}
|
||||
case UPB_WIRE_TYPE_64BIT: {
|
||||
uint64_t val;
|
||||
return upb_decode_64bit(&d->ptr, d->limit, &val);
|
||||
}
|
||||
case UPB_WIRE_TYPE_DELIMITED: {
|
||||
int len;
|
||||
CHK(upb_decode_string(&d->ptr, d->limit, &len));
|
||||
d->ptr += len;
|
||||
return true;
|
||||
}
|
||||
case UPB_WIRE_TYPE_START_GROUP:
|
||||
return upb_skip_unknowngroup(d, tag >> 3);
|
||||
case UPB_WIRE_TYPE_END_GROUP:
|
||||
return (tag >> 3) == group_fieldnum;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool upb_skip_unknowngroup(upb_decstate *d, int field_number) {
|
||||
while (d->ptr < d->limit && d->end_group == 0) {
|
||||
uint32_t tag = 0;
|
||||
CHK(upb_decode_varint32(&d->ptr, d->limit, &tag));
|
||||
CHK(upb_skip_unknownfielddata(d, tag, field_number));
|
||||
}
|
||||
|
||||
CHK(d->end_group == field_number);
|
||||
d->end_group = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool upb_array_grow(upb_array *arr, size_t elements, size_t elem_size,
|
||||
upb_arena *arena) {
|
||||
size_t needed = arr->len + elements;
|
||||
size_t new_size = UPB_MAX(arr->size, 8);
|
||||
size_t new_bytes;
|
||||
size_t old_bytes;
|
||||
void *new_data;
|
||||
upb_alloc *alloc = upb_arena_alloc(arena);
|
||||
|
||||
while (new_size < needed) {
|
||||
new_size *= 2;
|
||||
}
|
||||
|
||||
old_bytes = arr->len * elem_size;
|
||||
new_bytes = new_size * elem_size;
|
||||
new_data = upb_realloc(alloc, arr->data, old_bytes, new_bytes);
|
||||
CHK(new_data);
|
||||
|
||||
arr->data = new_data;
|
||||
arr->size = new_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void *upb_array_reserve(upb_array *arr, size_t elements,
|
||||
size_t elem_size, upb_arena *arena) {
|
||||
if (arr->size - arr->len < elements) {
|
||||
CHK(upb_array_grow(arr, elements, elem_size, arena));
|
||||
}
|
||||
return (char*)arr->data + (arr->len * elem_size);
|
||||
}
|
||||
|
||||
bool upb_array_add(upb_array *arr, size_t elements, size_t elem_size,
|
||||
const void *data, upb_arena *arena) {
|
||||
void *dest = upb_array_reserve(arr, elements, elem_size, arena);
|
||||
|
||||
CHK(dest);
|
||||
arr->len += elements;
|
||||
memcpy(dest, data, elements * elem_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static upb_array *upb_getarr(upb_decframe *frame,
|
||||
const upb_msglayout_field *field) {
|
||||
UPB_ASSERT(field->label == UPB_LABEL_REPEATED);
|
||||
return *(upb_array**)&frame->msg[field->offset];
|
||||
}
|
||||
|
||||
static upb_array *upb_getorcreatearr(upb_decframe *frame,
|
||||
const upb_msglayout_field *field) {
|
||||
upb_array *arr = upb_getarr(frame, field);
|
||||
|
||||
if (!arr) {
|
||||
arr = upb_array_new(frame->state->arena);
|
||||
CHK(arr);
|
||||
*(upb_array**)&frame->msg[field->offset] = arr;
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
static upb_msg *upb_getorcreatemsg(upb_decframe *frame,
|
||||
const upb_msglayout_field *field,
|
||||
const upb_msglayout **subm) {
|
||||
upb_msg **submsg = (void*)(frame->msg + field->offset);
|
||||
*subm = frame->layout->submsgs[field->submsg_index];
|
||||
|
||||
UPB_ASSERT(field->label != UPB_LABEL_REPEATED);
|
||||
|
||||
if (!*submsg) {
|
||||
*submsg = upb_msg_new(*subm, frame->state->arena);
|
||||
CHK(*submsg);
|
||||
}
|
||||
|
||||
return *submsg;
|
||||
}
|
||||
|
||||
static upb_msg *upb_addmsg(upb_decframe *frame,
|
||||
const upb_msglayout_field *field,
|
||||
const upb_msglayout **subm) {
|
||||
upb_msg *submsg;
|
||||
upb_array *arr = upb_getorcreatearr(frame, field);
|
||||
|
||||
UPB_ASSERT(field->label == UPB_LABEL_REPEATED);
|
||||
UPB_ASSERT(field->descriptortype == UPB_DESCRIPTOR_TYPE_MESSAGE ||
|
||||
field->descriptortype == UPB_DESCRIPTOR_TYPE_GROUP);
|
||||
|
||||
*subm = frame->layout->submsgs[field->submsg_index];
|
||||
submsg = upb_msg_new(*subm, frame->state->arena);
|
||||
CHK(submsg);
|
||||
upb_array_add(arr, 1, sizeof(submsg), &submsg, frame->state->arena);
|
||||
|
||||
return submsg;
|
||||
}
|
||||
|
||||
static void upb_sethasbit(upb_decframe *frame,
|
||||
const upb_msglayout_field *field) {
|
||||
int32_t hasbit = field->presence;
|
||||
UPB_ASSERT(field->presence > 0);
|
||||
frame->msg[hasbit / 8] |= (1 << (hasbit % 8));
|
||||
}
|
||||
|
||||
static void upb_setoneofcase(upb_decframe *frame,
|
||||
const upb_msglayout_field *field) {
|
||||
UPB_ASSERT(field->presence < 0);
|
||||
upb_set32(frame->msg, ~field->presence, field->number);
|
||||
}
|
||||
|
||||
static bool upb_decode_addval(upb_decframe *frame,
|
||||
const upb_msglayout_field *field, void *val,
|
||||
size_t size) {
|
||||
char *field_mem = frame->msg + field->offset;
|
||||
upb_array *arr;
|
||||
|
||||
if (field->label == UPB_LABEL_REPEATED) {
|
||||
arr = upb_getorcreatearr(frame, field);
|
||||
CHK(arr);
|
||||
field_mem = upb_array_reserve(arr, 1, size, frame->state->arena);
|
||||
CHK(field_mem);
|
||||
}
|
||||
|
||||
memcpy(field_mem, val, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void upb_decode_setpresent(upb_decframe *frame,
|
||||
const upb_msglayout_field *field) {
|
||||
if (field->label == UPB_LABEL_REPEATED) {
|
||||
upb_array *arr = upb_getarr(frame, field);
|
||||
UPB_ASSERT(arr->len < arr->size);
|
||||
arr->len++;
|
||||
} else if (field->presence < 0) {
|
||||
upb_setoneofcase(frame, field);
|
||||
} else if (field->presence > 0) {
|
||||
upb_sethasbit(frame, field);
|
||||
}
|
||||
}
|
||||
|
||||
static bool upb_decode_msgfield(upb_decstate *d, upb_msg *msg,
|
||||
const upb_msglayout *layout, int limit) {
|
||||
const char* saved_limit = d->limit;
|
||||
d->limit = d->ptr + limit;
|
||||
CHK(--d->depth >= 0);
|
||||
upb_decode_message(d, msg, layout);
|
||||
d->depth++;
|
||||
d->limit = saved_limit;
|
||||
CHK(d->end_group == 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool upb_decode_groupfield(upb_decstate *d, upb_msg *msg,
|
||||
const upb_msglayout *layout,
|
||||
int field_number) {
|
||||
CHK(--d->depth >= 0);
|
||||
upb_decode_message(d, msg, layout);
|
||||
d->depth++;
|
||||
CHK(d->end_group == field_number);
|
||||
d->end_group = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool upb_decode_varintfield(upb_decstate *d, upb_decframe *frame,
|
||||
const upb_msglayout_field *field) {
|
||||
uint64_t val;
|
||||
CHK(upb_decode_varint(&d->ptr, d->limit, &val));
|
||||
|
||||
switch (field->descriptortype) {
|
||||
case UPB_DESCRIPTOR_TYPE_INT64:
|
||||
case UPB_DESCRIPTOR_TYPE_UINT64:
|
||||
CHK(upb_decode_addval(frame, field, &val, sizeof(val)));
|
||||
break;
|
||||
case UPB_DESCRIPTOR_TYPE_INT32:
|
||||
case UPB_DESCRIPTOR_TYPE_UINT32:
|
||||
case UPB_DESCRIPTOR_TYPE_ENUM: {
|
||||
uint32_t val32 = (uint32_t)val;
|
||||
CHK(upb_decode_addval(frame, field, &val32, sizeof(val32)));
|
||||
break;
|
||||
}
|
||||
case UPB_DESCRIPTOR_TYPE_BOOL: {
|
||||
bool valbool = val != 0;
|
||||
CHK(upb_decode_addval(frame, field, &valbool, sizeof(valbool)));
|
||||
break;
|
||||
}
|
||||
case UPB_DESCRIPTOR_TYPE_SINT32: {
|
||||
int32_t decoded = upb_zzdecode_32((uint32_t)val);
|
||||
CHK(upb_decode_addval(frame, field, &decoded, sizeof(decoded)));
|
||||
break;
|
||||
}
|
||||
case UPB_DESCRIPTOR_TYPE_SINT64: {
|
||||
int64_t decoded = upb_zzdecode_64(val);
|
||||
CHK(upb_decode_addval(frame, field, &decoded, sizeof(decoded)));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return upb_append_unknown(d, frame);
|
||||
}
|
||||
|
||||
upb_decode_setpresent(frame, field);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool upb_decode_64bitfield(upb_decstate *d, upb_decframe *frame,
|
||||
const upb_msglayout_field *field) {
|
||||
uint64_t val;
|
||||
CHK(upb_decode_64bit(&d->ptr, d->limit, &val));
|
||||
|
||||
switch (field->descriptortype) {
|
||||
case UPB_DESCRIPTOR_TYPE_DOUBLE:
|
||||
case UPB_DESCRIPTOR_TYPE_FIXED64:
|
||||
case UPB_DESCRIPTOR_TYPE_SFIXED64:
|
||||
CHK(upb_decode_addval(frame, field, &val, sizeof(val)));
|
||||
break;
|
||||
default:
|
||||
return upb_append_unknown(d, frame);
|
||||
}
|
||||
|
||||
upb_decode_setpresent(frame, field);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool upb_decode_32bitfield(upb_decstate *d, upb_decframe *frame,
|
||||
const upb_msglayout_field *field) {
|
||||
uint32_t val;
|
||||
CHK(upb_decode_32bit(&d->ptr, d->limit, &val));
|
||||
|
||||
switch (field->descriptortype) {
|
||||
case UPB_DESCRIPTOR_TYPE_FLOAT:
|
||||
case UPB_DESCRIPTOR_TYPE_FIXED32:
|
||||
case UPB_DESCRIPTOR_TYPE_SFIXED32:
|
||||
CHK(upb_decode_addval(frame, field, &val, sizeof(val)));
|
||||
break;
|
||||
default:
|
||||
return upb_append_unknown(d, frame);
|
||||
}
|
||||
|
||||
upb_decode_setpresent(frame, field);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool upb_decode_fixedpacked(upb_decstate *d, upb_array *arr,
|
||||
uint32_t len, int elem_size) {
|
||||
size_t elements = len / elem_size;
|
||||
|
||||
CHK((size_t)(elements * elem_size) == len);
|
||||
CHK(upb_array_add(arr, elements, elem_size, d->ptr, d->arena));
|
||||
d->ptr += len;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static upb_strview upb_decode_strfield(upb_decstate *d, uint32_t len) {
|
||||
upb_strview ret;
|
||||
ret.data = d->ptr;
|
||||
ret.size = len;
|
||||
d->ptr += len;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool upb_decode_toarray(upb_decstate *d, upb_decframe *frame,
|
||||
const upb_msglayout_field *field, int len) {
|
||||
upb_array *arr = upb_getorcreatearr(frame, field);
|
||||
CHK(arr);
|
||||
|
||||
#define VARINT_CASE(ctype, decode) \
|
||||
VARINT_CASE_EX(ctype, decode, decode)
|
||||
|
||||
#define VARINT_CASE_EX(ctype, decode, dtype) \
|
||||
{ \
|
||||
const char *ptr = d->ptr; \
|
||||
const char *limit = ptr + len; \
|
||||
while (ptr < limit) { \
|
||||
uint64_t val; \
|
||||
ctype decoded; \
|
||||
CHK(upb_decode_varint(&ptr, limit, &val)); \
|
||||
decoded = (decode)((dtype)val); \
|
||||
CHK(upb_array_add(arr, 1, sizeof(decoded), &decoded, d->arena)); \
|
||||
} \
|
||||
d->ptr = ptr; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
switch (field->descriptortype) {
|
||||
case UPB_DESCRIPTOR_TYPE_STRING:
|
||||
case UPB_DESCRIPTOR_TYPE_BYTES: {
|
||||
upb_strview str = upb_decode_strfield(d, len);
|
||||
return upb_array_add(arr, 1, sizeof(str), &str, d->arena);
|
||||
}
|
||||
case UPB_DESCRIPTOR_TYPE_FLOAT:
|
||||
case UPB_DESCRIPTOR_TYPE_FIXED32:
|
||||
case UPB_DESCRIPTOR_TYPE_SFIXED32:
|
||||
return upb_decode_fixedpacked(d, arr, len, sizeof(int32_t));
|
||||
case UPB_DESCRIPTOR_TYPE_DOUBLE:
|
||||
case UPB_DESCRIPTOR_TYPE_FIXED64:
|
||||
case UPB_DESCRIPTOR_TYPE_SFIXED64:
|
||||
return upb_decode_fixedpacked(d, arr, len, sizeof(int64_t));
|
||||
case UPB_DESCRIPTOR_TYPE_INT32:
|
||||
case UPB_DESCRIPTOR_TYPE_UINT32:
|
||||
case UPB_DESCRIPTOR_TYPE_ENUM:
|
||||
VARINT_CASE(uint32_t, uint32_t);
|
||||
case UPB_DESCRIPTOR_TYPE_INT64:
|
||||
case UPB_DESCRIPTOR_TYPE_UINT64:
|
||||
VARINT_CASE(uint64_t, uint64_t);
|
||||
case UPB_DESCRIPTOR_TYPE_BOOL:
|
||||
VARINT_CASE(bool, bool);
|
||||
case UPB_DESCRIPTOR_TYPE_SINT32:
|
||||
VARINT_CASE_EX(int32_t, upb_zzdecode_32, uint32_t);
|
||||
case UPB_DESCRIPTOR_TYPE_SINT64:
|
||||
VARINT_CASE_EX(int64_t, upb_zzdecode_64, uint64_t);
|
||||
case UPB_DESCRIPTOR_TYPE_MESSAGE: {
|
||||
const upb_msglayout *subm;
|
||||
upb_msg *submsg = upb_addmsg(frame, field, &subm);
|
||||
CHK(submsg);
|
||||
return upb_decode_msgfield(d, submsg, subm, len);
|
||||
}
|
||||
case UPB_DESCRIPTOR_TYPE_GROUP:
|
||||
return upb_append_unknown(d, frame);
|
||||
}
|
||||
#undef VARINT_CASE
|
||||
UPB_UNREACHABLE();
|
||||
}
|
||||
|
||||
static bool upb_decode_delimitedfield(upb_decstate *d, upb_decframe *frame,
|
||||
const upb_msglayout_field *field) {
|
||||
int len;
|
||||
|
||||
CHK(upb_decode_string(&d->ptr, d->limit, &len));
|
||||
|
||||
if (field->label == UPB_LABEL_REPEATED) {
|
||||
return upb_decode_toarray(d, frame, field, len);
|
||||
} else {
|
||||
switch (field->descriptortype) {
|
||||
case UPB_DESCRIPTOR_TYPE_STRING:
|
||||
case UPB_DESCRIPTOR_TYPE_BYTES: {
|
||||
upb_strview str = upb_decode_strfield(d, len);
|
||||
CHK(upb_decode_addval(frame, field, &str, sizeof(str)));
|
||||
break;
|
||||
}
|
||||
case UPB_DESCRIPTOR_TYPE_MESSAGE: {
|
||||
const upb_msglayout *subm;
|
||||
upb_msg *submsg = upb_getorcreatemsg(frame, field, &subm);
|
||||
CHK(submsg);
|
||||
CHK(upb_decode_msgfield(d, submsg, subm, len));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* TODO(haberman): should we accept the last element of a packed? */
|
||||
d->ptr += len;
|
||||
return upb_append_unknown(d, frame);
|
||||
}
|
||||
upb_decode_setpresent(frame, field);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static const upb_msglayout_field *upb_find_field(const upb_msglayout *l,
|
||||
uint32_t field_number) {
|
||||
/* Lots of optimization opportunities here. */
|
||||
int i;
|
||||
for (i = 0; i < l->field_count; i++) {
|
||||
if (l->fields[i].number == field_number) {
|
||||
return &l->fields[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL; /* Unknown field. */
|
||||
}
|
||||
|
||||
static bool upb_decode_field(upb_decstate *d, upb_decframe *frame) {
|
||||
uint32_t tag;
|
||||
const upb_msglayout_field *field;
|
||||
int field_number;
|
||||
|
||||
d->field_start = d->ptr;
|
||||
CHK(upb_decode_varint32(&d->ptr, d->limit, &tag));
|
||||
field_number = tag >> 3;
|
||||
field = upb_find_field(frame->layout, field_number);
|
||||
|
||||
if (field) {
|
||||
switch (tag & 7) {
|
||||
case UPB_WIRE_TYPE_VARINT:
|
||||
return upb_decode_varintfield(d, frame, field);
|
||||
case UPB_WIRE_TYPE_32BIT:
|
||||
return upb_decode_32bitfield(d, frame, field);
|
||||
case UPB_WIRE_TYPE_64BIT:
|
||||
return upb_decode_64bitfield(d, frame, field);
|
||||
case UPB_WIRE_TYPE_DELIMITED:
|
||||
return upb_decode_delimitedfield(d, frame, field);
|
||||
case UPB_WIRE_TYPE_START_GROUP: {
|
||||
const upb_msglayout *layout;
|
||||
upb_msg *group;
|
||||
|
||||
if (field->label == UPB_LABEL_REPEATED) {
|
||||
group = upb_addmsg(frame, field, &layout);
|
||||
} else {
|
||||
group = upb_getorcreatemsg(frame, field, &layout);
|
||||
}
|
||||
|
||||
return upb_decode_groupfield(d, group, layout, field_number);
|
||||
}
|
||||
case UPB_WIRE_TYPE_END_GROUP:
|
||||
d->end_group = field_number;
|
||||
return true;
|
||||
default:
|
||||
CHK(false);
|
||||
}
|
||||
} else {
|
||||
CHK(field_number != 0);
|
||||
CHK(upb_skip_unknownfielddata(d, tag, -1));
|
||||
CHK(upb_append_unknown(d, frame));
|
||||
return true;
|
||||
}
|
||||
UPB_UNREACHABLE();
|
||||
}
|
||||
|
||||
static bool upb_decode_message(upb_decstate *d, char *msg, const upb_msglayout *l) {
|
||||
upb_decframe frame;
|
||||
frame.msg = msg;
|
||||
frame.layout = l;
|
||||
frame.state = d;
|
||||
|
||||
while (d->ptr < d->limit) {
|
||||
CHK(upb_decode_field(d, &frame));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool upb_decode(const char *buf, size_t size, void *msg, const upb_msglayout *l,
|
||||
upb_arena *arena) {
|
||||
upb_decstate state;
|
||||
state.ptr = buf;
|
||||
state.limit = buf + size;
|
||||
state.arena = arena;
|
||||
state.depth = 64;
|
||||
state.end_group = 0;
|
||||
|
||||
CHK(upb_decode_message(&state, msg, l));
|
||||
return state.end_group == 0;
|
||||
}
|
||||
|
||||
#undef CHK
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
** upb_decode: parsing into a upb_msg using a upb_msglayout.
|
||||
*/
|
||||
|
||||
#ifndef UPB_DECODE_H_
|
||||
#define UPB_DECODE_H_
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/msg.h"
|
||||
#else
|
||||
#include "upb/msg.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool upb_decode(const char *buf, size_t size, upb_msg *msg,
|
||||
const upb_msglayout *l, upb_arena *arena);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* UPB_DECODE_H_ */
|
||||
+394
@@ -0,0 +1,394 @@
|
||||
/* We encode backwards, to avoid pre-computing lengths (one-pass encode). */
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/encode.h"
|
||||
#else
|
||||
#include "upb/encode.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/msg.h"
|
||||
#else
|
||||
#include "upb/msg.h"
|
||||
#endif
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/upb.h"
|
||||
#else
|
||||
#include "upb/upb.h"
|
||||
#endif
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/port_def.inc"
|
||||
#else
|
||||
#include "upb/port_def.inc"
|
||||
#endif
|
||||
|
||||
#define UPB_PB_VARINT_MAX_LEN 10
|
||||
#define CHK(x) do { if (!(x)) { return false; } } while(0)
|
||||
|
||||
static size_t upb_encode_varint(uint64_t val, char *buf) {
|
||||
size_t i;
|
||||
if (val < 128) { buf[0] = val; return 1; }
|
||||
i = 0;
|
||||
while (val) {
|
||||
uint8_t byte = val & 0x7fU;
|
||||
val >>= 7;
|
||||
if (val) byte |= 0x80U;
|
||||
buf[i++] = byte;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static uint32_t upb_zzencode_32(int32_t n) { return ((uint32_t)n << 1) ^ (n >> 31); }
|
||||
static uint64_t upb_zzencode_64(int64_t n) { return ((uint64_t)n << 1) ^ (n >> 63); }
|
||||
|
||||
typedef struct {
|
||||
upb_alloc *alloc;
|
||||
char *buf, *ptr, *limit;
|
||||
} upb_encstate;
|
||||
|
||||
static size_t upb_roundup_pow2(size_t bytes) {
|
||||
size_t ret = 128;
|
||||
while (ret < bytes) {
|
||||
ret *= 2;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool upb_encode_growbuffer(upb_encstate *e, size_t bytes) {
|
||||
size_t old_size = e->limit - e->buf;
|
||||
size_t new_size = upb_roundup_pow2(bytes + (e->limit - e->ptr));
|
||||
char *new_buf = upb_realloc(e->alloc, e->buf, old_size, new_size);
|
||||
CHK(new_buf);
|
||||
|
||||
/* We want previous data at the end, realloc() put it at the beginning. */
|
||||
if (old_size > 0) {
|
||||
memmove(new_buf + new_size - old_size, e->buf, old_size);
|
||||
}
|
||||
|
||||
e->ptr = new_buf + new_size - (e->limit - e->ptr);
|
||||
e->limit = new_buf + new_size;
|
||||
e->buf = new_buf;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Call to ensure that at least "bytes" bytes are available for writing at
|
||||
* e->ptr. Returns false if the bytes could not be allocated. */
|
||||
static bool upb_encode_reserve(upb_encstate *e, size_t bytes) {
|
||||
CHK(UPB_LIKELY((size_t)(e->ptr - e->buf) >= bytes) ||
|
||||
upb_encode_growbuffer(e, bytes));
|
||||
|
||||
e->ptr -= bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Writes the given bytes to the buffer, handling reserve/advance. */
|
||||
static bool upb_put_bytes(upb_encstate *e, const void *data, size_t len) {
|
||||
CHK(upb_encode_reserve(e, len));
|
||||
memcpy(e->ptr, data, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool upb_put_fixed64(upb_encstate *e, uint64_t val) {
|
||||
/* TODO(haberman): byte-swap for big endian. */
|
||||
return upb_put_bytes(e, &val, sizeof(uint64_t));
|
||||
}
|
||||
|
||||
static bool upb_put_fixed32(upb_encstate *e, uint32_t val) {
|
||||
/* TODO(haberman): byte-swap for big endian. */
|
||||
return upb_put_bytes(e, &val, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static bool upb_put_varint(upb_encstate *e, uint64_t val) {
|
||||
size_t len;
|
||||
char *start;
|
||||
CHK(upb_encode_reserve(e, UPB_PB_VARINT_MAX_LEN));
|
||||
len = upb_encode_varint(val, e->ptr);
|
||||
start = e->ptr + UPB_PB_VARINT_MAX_LEN - len;
|
||||
memmove(start, e->ptr, len);
|
||||
e->ptr = start;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool upb_put_double(upb_encstate *e, double d) {
|
||||
uint64_t u64;
|
||||
UPB_ASSERT(sizeof(double) == sizeof(uint64_t));
|
||||
memcpy(&u64, &d, sizeof(uint64_t));
|
||||
return upb_put_fixed64(e, u64);
|
||||
}
|
||||
|
||||
static bool upb_put_float(upb_encstate *e, float d) {
|
||||
uint32_t u32;
|
||||
UPB_ASSERT(sizeof(float) == sizeof(uint32_t));
|
||||
memcpy(&u32, &d, sizeof(uint32_t));
|
||||
return upb_put_fixed32(e, u32);
|
||||
}
|
||||
|
||||
static uint32_t upb_readcase(const char *msg, const upb_msglayout_field *f) {
|
||||
uint32_t ret;
|
||||
uint32_t offset = ~f->presence;
|
||||
memcpy(&ret, msg + offset, sizeof(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool upb_readhasbit(const char *msg, const upb_msglayout_field *f) {
|
||||
uint32_t hasbit = f->presence;
|
||||
UPB_ASSERT(f->presence > 0);
|
||||
return msg[hasbit / 8] & (1 << (hasbit % 8));
|
||||
}
|
||||
|
||||
static bool upb_put_tag(upb_encstate *e, int field_number, int wire_type) {
|
||||
return upb_put_varint(e, (field_number << 3) | wire_type);
|
||||
}
|
||||
|
||||
static bool upb_put_fixedarray(upb_encstate *e, const upb_array *arr,
|
||||
size_t size) {
|
||||
size_t bytes = arr->len * size;
|
||||
return upb_put_bytes(e, arr->data, bytes) && upb_put_varint(e, bytes);
|
||||
}
|
||||
|
||||
bool upb_encode_message(upb_encstate *e, const char *msg,
|
||||
const upb_msglayout *m, size_t *size);
|
||||
|
||||
static bool upb_encode_array(upb_encstate *e, const char *field_mem,
|
||||
const upb_msglayout *m,
|
||||
const upb_msglayout_field *f) {
|
||||
const upb_array *arr = *(const upb_array**)field_mem;
|
||||
|
||||
if (arr == NULL || arr->len == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#define VARINT_CASE(ctype, encode) { \
|
||||
ctype *start = arr->data; \
|
||||
ctype *ptr = start + arr->len; \
|
||||
size_t pre_len = e->limit - e->ptr; \
|
||||
do { \
|
||||
ptr--; \
|
||||
CHK(upb_put_varint(e, encode)); \
|
||||
} while (ptr != start); \
|
||||
CHK(upb_put_varint(e, e->limit - e->ptr - pre_len)); \
|
||||
} \
|
||||
break; \
|
||||
do { ; } while(0)
|
||||
|
||||
switch (f->descriptortype) {
|
||||
case UPB_DESCRIPTOR_TYPE_DOUBLE:
|
||||
CHK(upb_put_fixedarray(e, arr, sizeof(double)));
|
||||
break;
|
||||
case UPB_DESCRIPTOR_TYPE_FLOAT:
|
||||
CHK(upb_put_fixedarray(e, arr, sizeof(float)));
|
||||
break;
|
||||
case UPB_DESCRIPTOR_TYPE_SFIXED64:
|
||||
case UPB_DESCRIPTOR_TYPE_FIXED64:
|
||||
CHK(upb_put_fixedarray(e, arr, sizeof(uint64_t)));
|
||||
break;
|
||||
case UPB_DESCRIPTOR_TYPE_FIXED32:
|
||||
case UPB_DESCRIPTOR_TYPE_SFIXED32:
|
||||
CHK(upb_put_fixedarray(e, arr, sizeof(uint32_t)));
|
||||
break;
|
||||
case UPB_DESCRIPTOR_TYPE_INT64:
|
||||
case UPB_DESCRIPTOR_TYPE_UINT64:
|
||||
VARINT_CASE(uint64_t, *ptr);
|
||||
case UPB_DESCRIPTOR_TYPE_UINT32:
|
||||
VARINT_CASE(uint32_t, *ptr);
|
||||
case UPB_DESCRIPTOR_TYPE_INT32:
|
||||
case UPB_DESCRIPTOR_TYPE_ENUM:
|
||||
VARINT_CASE(int32_t, (int64_t)*ptr);
|
||||
case UPB_DESCRIPTOR_TYPE_BOOL:
|
||||
VARINT_CASE(bool, *ptr);
|
||||
case UPB_DESCRIPTOR_TYPE_SINT32:
|
||||
VARINT_CASE(int32_t, upb_zzencode_32(*ptr));
|
||||
case UPB_DESCRIPTOR_TYPE_SINT64:
|
||||
VARINT_CASE(int64_t, upb_zzencode_64(*ptr));
|
||||
case UPB_DESCRIPTOR_TYPE_STRING:
|
||||
case UPB_DESCRIPTOR_TYPE_BYTES: {
|
||||
upb_strview *start = arr->data;
|
||||
upb_strview *ptr = start + arr->len;
|
||||
do {
|
||||
ptr--;
|
||||
CHK(upb_put_bytes(e, ptr->data, ptr->size) &&
|
||||
upb_put_varint(e, ptr->size) &&
|
||||
upb_put_tag(e, f->number, UPB_WIRE_TYPE_DELIMITED));
|
||||
} while (ptr != start);
|
||||
return true;
|
||||
}
|
||||
case UPB_DESCRIPTOR_TYPE_GROUP: {
|
||||
void **start = arr->data;
|
||||
void **ptr = start + arr->len;
|
||||
const upb_msglayout *subm = m->submsgs[f->submsg_index];
|
||||
do {
|
||||
size_t size;
|
||||
ptr--;
|
||||
CHK(upb_put_tag(e, f->number, UPB_WIRE_TYPE_END_GROUP) &&
|
||||
upb_encode_message(e, *ptr, subm, &size) &&
|
||||
upb_put_tag(e, f->number, UPB_WIRE_TYPE_START_GROUP));
|
||||
} while (ptr != start);
|
||||
return true;
|
||||
}
|
||||
case UPB_DESCRIPTOR_TYPE_MESSAGE: {
|
||||
void **start = arr->data;
|
||||
void **ptr = start + arr->len;
|
||||
const upb_msglayout *subm = m->submsgs[f->submsg_index];
|
||||
do {
|
||||
size_t size;
|
||||
ptr--;
|
||||
CHK(upb_encode_message(e, *ptr, subm, &size) &&
|
||||
upb_put_varint(e, size) &&
|
||||
upb_put_tag(e, f->number, UPB_WIRE_TYPE_DELIMITED));
|
||||
} while (ptr != start);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#undef VARINT_CASE
|
||||
|
||||
/* We encode all primitive arrays as packed, regardless of what was specified
|
||||
* in the .proto file. Could special case 1-sized arrays. */
|
||||
CHK(upb_put_tag(e, f->number, UPB_WIRE_TYPE_DELIMITED));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool upb_encode_scalarfield(upb_encstate *e, const char *field_mem,
|
||||
const upb_msglayout *m,
|
||||
const upb_msglayout_field *f,
|
||||
bool skip_zero_value) {
|
||||
#define CASE(ctype, type, wire_type, encodeval) do { \
|
||||
ctype val = *(ctype*)field_mem; \
|
||||
if (skip_zero_value && val == 0) { \
|
||||
return true; \
|
||||
} \
|
||||
return upb_put_ ## type(e, encodeval) && \
|
||||
upb_put_tag(e, f->number, wire_type); \
|
||||
} while(0)
|
||||
|
||||
switch (f->descriptortype) {
|
||||
case UPB_DESCRIPTOR_TYPE_DOUBLE:
|
||||
CASE(double, double, UPB_WIRE_TYPE_64BIT, val);
|
||||
case UPB_DESCRIPTOR_TYPE_FLOAT:
|
||||
CASE(float, float, UPB_WIRE_TYPE_32BIT, val);
|
||||
case UPB_DESCRIPTOR_TYPE_INT64:
|
||||
case UPB_DESCRIPTOR_TYPE_UINT64:
|
||||
CASE(uint64_t, varint, UPB_WIRE_TYPE_VARINT, val);
|
||||
case UPB_DESCRIPTOR_TYPE_UINT32:
|
||||
CASE(uint32_t, varint, UPB_WIRE_TYPE_VARINT, val);
|
||||
case UPB_DESCRIPTOR_TYPE_INT32:
|
||||
case UPB_DESCRIPTOR_TYPE_ENUM:
|
||||
CASE(int32_t, varint, UPB_WIRE_TYPE_VARINT, (int64_t)val);
|
||||
case UPB_DESCRIPTOR_TYPE_SFIXED64:
|
||||
case UPB_DESCRIPTOR_TYPE_FIXED64:
|
||||
CASE(uint64_t, fixed64, UPB_WIRE_TYPE_64BIT, val);
|
||||
case UPB_DESCRIPTOR_TYPE_FIXED32:
|
||||
case UPB_DESCRIPTOR_TYPE_SFIXED32:
|
||||
CASE(uint32_t, fixed32, UPB_WIRE_TYPE_32BIT, val);
|
||||
case UPB_DESCRIPTOR_TYPE_BOOL:
|
||||
CASE(bool, varint, UPB_WIRE_TYPE_VARINT, val);
|
||||
case UPB_DESCRIPTOR_TYPE_SINT32:
|
||||
CASE(int32_t, varint, UPB_WIRE_TYPE_VARINT, upb_zzencode_32(val));
|
||||
case UPB_DESCRIPTOR_TYPE_SINT64:
|
||||
CASE(int64_t, varint, UPB_WIRE_TYPE_VARINT, upb_zzencode_64(val));
|
||||
case UPB_DESCRIPTOR_TYPE_STRING:
|
||||
case UPB_DESCRIPTOR_TYPE_BYTES: {
|
||||
upb_strview view = *(upb_strview*)field_mem;
|
||||
if (skip_zero_value && view.size == 0) {
|
||||
return true;
|
||||
}
|
||||
return upb_put_bytes(e, view.data, view.size) &&
|
||||
upb_put_varint(e, view.size) &&
|
||||
upb_put_tag(e, f->number, UPB_WIRE_TYPE_DELIMITED);
|
||||
}
|
||||
case UPB_DESCRIPTOR_TYPE_GROUP: {
|
||||
size_t size;
|
||||
void *submsg = *(void **)field_mem;
|
||||
const upb_msglayout *subm = m->submsgs[f->submsg_index];
|
||||
if (submsg == NULL) {
|
||||
return true;
|
||||
}
|
||||
return upb_put_tag(e, f->number, UPB_WIRE_TYPE_END_GROUP) &&
|
||||
upb_encode_message(e, submsg, subm, &size) &&
|
||||
upb_put_tag(e, f->number, UPB_WIRE_TYPE_START_GROUP);
|
||||
}
|
||||
case UPB_DESCRIPTOR_TYPE_MESSAGE: {
|
||||
size_t size;
|
||||
void *submsg = *(void **)field_mem;
|
||||
const upb_msglayout *subm = m->submsgs[f->submsg_index];
|
||||
if (submsg == NULL) {
|
||||
return true;
|
||||
}
|
||||
return upb_encode_message(e, submsg, subm, &size) &&
|
||||
upb_put_varint(e, size) &&
|
||||
upb_put_tag(e, f->number, UPB_WIRE_TYPE_DELIMITED);
|
||||
}
|
||||
}
|
||||
#undef CASE
|
||||
UPB_UNREACHABLE();
|
||||
}
|
||||
|
||||
bool upb_encode_message(upb_encstate *e, const char *msg,
|
||||
const upb_msglayout *m, size_t *size) {
|
||||
int i;
|
||||
size_t pre_len = e->limit - e->ptr;
|
||||
const char *unknown;
|
||||
size_t unknown_size;
|
||||
|
||||
for (i = m->field_count - 1; i >= 0; i--) {
|
||||
const upb_msglayout_field *f = &m->fields[i];
|
||||
|
||||
if (f->label == UPB_LABEL_REPEATED) {
|
||||
CHK(upb_encode_array(e, msg + f->offset, m, f));
|
||||
} else {
|
||||
bool skip_empty = false;
|
||||
if (f->presence == 0) {
|
||||
/* Proto3 presence. */
|
||||
skip_empty = true;
|
||||
} else if (f->presence > 0) {
|
||||
/* Proto2 presence: hasbit. */
|
||||
if (!upb_readhasbit(msg, f)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
/* Field is in a oneof. */
|
||||
if (upb_readcase(msg, f) != f->number) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
CHK(upb_encode_scalarfield(e, msg + f->offset, m, f, skip_empty));
|
||||
}
|
||||
}
|
||||
|
||||
unknown = upb_msg_getunknown(msg, &unknown_size);
|
||||
|
||||
if (unknown) {
|
||||
upb_put_bytes(e, unknown, unknown_size);
|
||||
}
|
||||
|
||||
*size = (e->limit - e->ptr) - pre_len;
|
||||
return true;
|
||||
}
|
||||
|
||||
char *upb_encode(const void *msg, const upb_msglayout *m, upb_arena *arena,
|
||||
size_t *size) {
|
||||
upb_encstate e;
|
||||
e.alloc = upb_arena_alloc(arena);
|
||||
e.buf = NULL;
|
||||
e.limit = NULL;
|
||||
e.ptr = NULL;
|
||||
|
||||
if (!upb_encode_message(&e, msg, m, size)) {
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*size = e.limit - e.ptr;
|
||||
|
||||
if (*size == 0) {
|
||||
static char ch;
|
||||
return &ch;
|
||||
} else {
|
||||
UPB_ASSERT(e.ptr);
|
||||
return e.ptr;
|
||||
}
|
||||
}
|
||||
|
||||
#undef CHK
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
** upb_encode: parsing into a upb_msg using a upb_msglayout.
|
||||
*/
|
||||
|
||||
#ifndef UPB_ENCODE_H_
|
||||
#define UPB_ENCODE_H_
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/msg.h"
|
||||
#else
|
||||
#include "upb/msg.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
char *upb_encode(const void *msg, const upb_msglayout *l, upb_arena *arena,
|
||||
size_t *size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* UPB_ENCODE_H_ */
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
** Functions for use by generated code. These are not public and users must
|
||||
** not call them directly.
|
||||
*/
|
||||
|
||||
#ifndef UPB_GENERATED_UTIL_H_
|
||||
#define UPB_GENERATED_UTIL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/msg.h"
|
||||
#else
|
||||
#include "upb/msg.h"
|
||||
#endif
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/port_def.inc"
|
||||
#else
|
||||
#include "upb/port_def.inc"
|
||||
#endif
|
||||
|
||||
#define PTR_AT(msg, ofs, type) (type*)((const char*)msg + ofs)
|
||||
|
||||
UPB_INLINE const void *_upb_array_accessor(const void *msg, size_t ofs,
|
||||
size_t *size) {
|
||||
const upb_array *arr = *PTR_AT(msg, ofs, const upb_array*);
|
||||
if (arr) {
|
||||
if (size) *size = arr->len;
|
||||
return arr->data;
|
||||
} else {
|
||||
if (size) *size = 0;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
UPB_INLINE void *_upb_array_mutable_accessor(void *msg, size_t ofs,
|
||||
size_t *size) {
|
||||
upb_array *arr = *PTR_AT(msg, ofs, upb_array*);
|
||||
if (arr) {
|
||||
if (size) *size = arr->len;
|
||||
return arr->data;
|
||||
} else {
|
||||
if (size) *size = 0;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO(haberman): this is a mess. It will improve when upb_array no longer
|
||||
* carries reflective state (type, elem_size). */
|
||||
UPB_INLINE void *_upb_array_resize_accessor(void *msg, size_t ofs, size_t size,
|
||||
size_t elem_size,
|
||||
upb_fieldtype_t type,
|
||||
upb_arena *arena) {
|
||||
upb_array *arr = *PTR_AT(msg, ofs, upb_array*);
|
||||
|
||||
if (!arr) {
|
||||
arr = upb_array_new(arena);
|
||||
if (!arr) return NULL;
|
||||
*PTR_AT(msg, ofs, upb_array*) = arr;
|
||||
}
|
||||
|
||||
if (size > arr->size) {
|
||||
size_t new_size = UPB_MAX(arr->size, 4);
|
||||
size_t old_bytes = arr->size * elem_size;
|
||||
size_t new_bytes;
|
||||
while (new_size < size) new_size *= 2;
|
||||
new_bytes = new_size * elem_size;
|
||||
arr->data = upb_arena_realloc(arena, arr->data, old_bytes, new_bytes);
|
||||
if (!arr->data) {
|
||||
return NULL;
|
||||
}
|
||||
arr->size = new_size;
|
||||
}
|
||||
|
||||
arr->len = size;
|
||||
return arr->data;
|
||||
}
|
||||
|
||||
UPB_INLINE bool _upb_array_append_accessor(void *msg, size_t ofs,
|
||||
size_t elem_size,
|
||||
upb_fieldtype_t type,
|
||||
const void *value,
|
||||
upb_arena *arena) {
|
||||
upb_array *arr = *PTR_AT(msg, ofs, upb_array*);
|
||||
size_t i = arr ? arr->len : 0;
|
||||
void *data =
|
||||
_upb_array_resize_accessor(msg, ofs, i + 1, elem_size, type, arena);
|
||||
if (!data) return false;
|
||||
memcpy(PTR_AT(data, i * elem_size, char), value, elem_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
UPB_INLINE bool _upb_has_field(const void *msg, size_t idx) {
|
||||
return (*PTR_AT(msg, idx / 8, const char) & (1 << (idx % 8))) != 0;
|
||||
}
|
||||
|
||||
UPB_INLINE bool _upb_sethas(const void *msg, size_t idx) {
|
||||
return (*PTR_AT(msg, idx / 8, char)) |= (char)(1 << (idx % 8));
|
||||
}
|
||||
|
||||
UPB_INLINE bool _upb_clearhas(const void *msg, size_t idx) {
|
||||
return (*PTR_AT(msg, idx / 8, char)) &= (char)(~(1 << (idx % 8)));
|
||||
}
|
||||
|
||||
UPB_INLINE bool _upb_has_oneof_field(const void *msg, size_t case_ofs, int32_t num) {
|
||||
return *PTR_AT(msg, case_ofs, int32_t) == num;
|
||||
}
|
||||
|
||||
#undef PTR_AT
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/port_undef.inc"
|
||||
#else
|
||||
#include "upb/port_undef.inc"
|
||||
#endif
|
||||
|
||||
#endif /* UPB_GENERATED_UTIL_H_ */
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/msg.h"
|
||||
#else
|
||||
#include "upb/msg.h"
|
||||
#endif
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/table.int.h"
|
||||
#else
|
||||
#include "upb/table.int.h"
|
||||
#endif
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/port_def.inc"
|
||||
#else
|
||||
#include "upb/port_def.inc"
|
||||
#endif
|
||||
|
||||
#define VOIDPTR_AT(msg, ofs) (void*)((char*)msg + (int)ofs)
|
||||
|
||||
/* Internal members of a upb_msg. We can change this without breaking binary
|
||||
* compatibility. We put these before the user's data. The user's upb_msg*
|
||||
* points after the upb_msg_internal. */
|
||||
|
||||
/* Used when a message is not extendable. */
|
||||
typedef struct {
|
||||
char *unknown;
|
||||
size_t unknown_len;
|
||||
size_t unknown_size;
|
||||
} upb_msg_internal;
|
||||
|
||||
/* Used when a message is extendable. */
|
||||
typedef struct {
|
||||
upb_inttable *extdict;
|
||||
upb_msg_internal base;
|
||||
} upb_msg_internal_withext;
|
||||
|
||||
static int upb_msg_internalsize(const upb_msglayout *l) {
|
||||
return sizeof(upb_msg_internal) - l->extendable * sizeof(void *);
|
||||
}
|
||||
|
||||
static size_t upb_msg_sizeof(const upb_msglayout *l) {
|
||||
return l->size + upb_msg_internalsize(l);
|
||||
}
|
||||
|
||||
static upb_msg_internal *upb_msg_getinternal(upb_msg *msg) {
|
||||
return VOIDPTR_AT(msg, -sizeof(upb_msg_internal));
|
||||
}
|
||||
|
||||
static const upb_msg_internal *upb_msg_getinternal_const(const upb_msg *msg) {
|
||||
return VOIDPTR_AT(msg, -sizeof(upb_msg_internal));
|
||||
}
|
||||
|
||||
static upb_msg_internal_withext *upb_msg_getinternalwithext(
|
||||
upb_msg *msg, const upb_msglayout *l) {
|
||||
UPB_ASSERT(l->extendable);
|
||||
return VOIDPTR_AT(msg, -sizeof(upb_msg_internal_withext));
|
||||
}
|
||||
|
||||
upb_msg *upb_msg_new(const upb_msglayout *l, upb_arena *a) {
|
||||
upb_alloc *alloc = upb_arena_alloc(a);
|
||||
void *mem = upb_malloc(alloc, upb_msg_sizeof(l));
|
||||
upb_msg_internal *in;
|
||||
upb_msg *msg;
|
||||
|
||||
if (!mem) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg = VOIDPTR_AT(mem, upb_msg_internalsize(l));
|
||||
|
||||
/* Initialize normal members. */
|
||||
memset(msg, 0, l->size);
|
||||
|
||||
/* Initialize internal members. */
|
||||
in = upb_msg_getinternal(msg);
|
||||
in->unknown = NULL;
|
||||
in->unknown_len = 0;
|
||||
in->unknown_size = 0;
|
||||
|
||||
if (l->extendable) {
|
||||
upb_msg_getinternalwithext(msg, l)->extdict = NULL;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
upb_array *upb_array_new(upb_arena *a) {
|
||||
upb_array *ret = upb_arena_malloc(a, sizeof(upb_array));
|
||||
|
||||
if (!ret) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->data = NULL;
|
||||
ret->len = 0;
|
||||
ret->size = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void upb_msg_addunknown(upb_msg *msg, const char *data, size_t len,
|
||||
upb_arena *arena) {
|
||||
upb_msg_internal *in = upb_msg_getinternal(msg);
|
||||
if (len > in->unknown_size - in->unknown_len) {
|
||||
upb_alloc *alloc = upb_arena_alloc(arena);
|
||||
size_t need = in->unknown_size + len;
|
||||
size_t newsize = UPB_MAX(in->unknown_size * 2, need);
|
||||
in->unknown = upb_realloc(alloc, in->unknown, in->unknown_size, newsize);
|
||||
in->unknown_size = newsize;
|
||||
}
|
||||
memcpy(in->unknown + in->unknown_len, data, len);
|
||||
in->unknown_len += len;
|
||||
}
|
||||
|
||||
const char *upb_msg_getunknown(const upb_msg *msg, size_t *len) {
|
||||
const upb_msg_internal* in = upb_msg_getinternal_const(msg);
|
||||
*len = in->unknown_len;
|
||||
return in->unknown;
|
||||
}
|
||||
|
||||
#undef VOIDPTR_AT
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
** Data structures for message tables, used for parsing and serialization.
|
||||
** This are much lighter-weight than full reflection, but they are do not
|
||||
** have enough information to convert to text format, JSON, etc.
|
||||
**
|
||||
** The definitions in this file are internal to upb.
|
||||
**/
|
||||
|
||||
#ifndef UPB_MSG_H_
|
||||
#define UPB_MSG_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/upb.h"
|
||||
#else
|
||||
#include "upb/upb.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void upb_msg;
|
||||
|
||||
/** upb_msglayout *************************************************************/
|
||||
|
||||
/* upb_msglayout represents the memory layout of a given upb_msgdef. The
|
||||
* members are public so generated code can initialize them, but users MUST NOT
|
||||
* read or write any of its members. */
|
||||
|
||||
typedef struct {
|
||||
uint32_t number;
|
||||
uint16_t offset;
|
||||
int16_t presence; /* If >0, hasbit_index+1. If <0, oneof_index+1. */
|
||||
uint16_t submsg_index; /* undefined if descriptortype != MESSAGE or GROUP. */
|
||||
uint8_t descriptortype;
|
||||
uint8_t label;
|
||||
} upb_msglayout_field;
|
||||
|
||||
typedef struct upb_msglayout {
|
||||
const struct upb_msglayout *const* submsgs;
|
||||
const upb_msglayout_field *fields;
|
||||
/* Must be aligned to sizeof(void*). Doesn't include internal members like
|
||||
* unknown fields, extension dict, pointer to msglayout, etc. */
|
||||
uint16_t size;
|
||||
uint16_t field_count;
|
||||
bool extendable;
|
||||
} upb_msglayout;
|
||||
|
||||
/** Message internal representation *******************************************/
|
||||
|
||||
/* Our internal representation for repeated fields. */
|
||||
typedef struct {
|
||||
void *data; /* Each element is element_size. */
|
||||
size_t len; /* Measured in elements. */
|
||||
size_t size; /* Measured in elements. */
|
||||
} upb_array;
|
||||
|
||||
upb_msg *upb_msg_new(const upb_msglayout *l, upb_arena *a);
|
||||
upb_msg *upb_msg_new(const upb_msglayout *l, upb_arena *a);
|
||||
|
||||
void upb_msg_addunknown(upb_msg *msg, const char *data, size_t len,
|
||||
upb_arena *arena);
|
||||
const char *upb_msg_getunknown(const upb_msg *msg, size_t *len);
|
||||
|
||||
upb_array *upb_array_new(upb_arena *a);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* UPB_MSG_H_ */
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/port_def.inc"
|
||||
#else
|
||||
#include "upb/port_def.inc"
|
||||
#endif
|
||||
|
||||
#ifdef UPB_MSVC_VSNPRINTF
|
||||
/* Visual C++ earlier than 2015 doesn't have standard C99 snprintf and
|
||||
* vsnprintf. To support them, missing functions are manually implemented
|
||||
* using the existing secure functions. */
|
||||
int msvc_vsnprintf(char* s, size_t n, const char* format, va_list arg) {
|
||||
if (!s) {
|
||||
return _vscprintf(format, arg);
|
||||
}
|
||||
int ret = _vsnprintf_s(s, n, _TRUNCATE, format, arg);
|
||||
if (ret < 0) {
|
||||
ret = _vscprintf(format, arg);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int msvc_snprintf(char* s, size_t n, const char* format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
int ret = msvc_vsnprintf(s, n, format, arg);
|
||||
va_end(arg);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* This is where we define macros used across upb.
|
||||
*
|
||||
* All of these macros are undef'd in port_undef.inc to avoid leaking them to
|
||||
* users.
|
||||
*
|
||||
* The correct usage is:
|
||||
*
|
||||
* #include "upb/foobar.h"
|
||||
* #include "upb/baz.h"
|
||||
*
|
||||
* // MUST be last included header.
|
||||
* #include "upb/port_def.inc"
|
||||
*
|
||||
* // Code for this file.
|
||||
* // <...>
|
||||
*
|
||||
* // Can be omitted for .c files, required for .h.
|
||||
* #include "upb/port_undef.inc"
|
||||
*
|
||||
* This file is private and must not be included by users!
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
#if UINTPTR_MAX == 0xffffffff
|
||||
#define UPB_SIZE(size32, size64) size32
|
||||
#else
|
||||
#define UPB_SIZE(size32, size64) size64
|
||||
#endif
|
||||
|
||||
#define UPB_FIELD_AT(msg, fieldtype, offset) \
|
||||
*(fieldtype*)((const char*)(msg) + offset)
|
||||
|
||||
#define UPB_READ_ONEOF(msg, fieldtype, offset, case_offset, case_val, default) \
|
||||
UPB_FIELD_AT(msg, int, case_offset) == case_val \
|
||||
? UPB_FIELD_AT(msg, fieldtype, offset) \
|
||||
: default
|
||||
|
||||
#define UPB_WRITE_ONEOF(msg, fieldtype, offset, value, case_offset, case_val) \
|
||||
UPB_FIELD_AT(msg, int, case_offset) = case_val; \
|
||||
UPB_FIELD_AT(msg, fieldtype, offset) = value;
|
||||
|
||||
/* UPB_INLINE: inline if possible, emit standalone code if required. */
|
||||
#ifdef __cplusplus
|
||||
#define UPB_INLINE inline
|
||||
#elif defined (__GNUC__) || defined(__clang__)
|
||||
#define UPB_INLINE static __inline__
|
||||
#else
|
||||
#define UPB_INLINE static
|
||||
#endif
|
||||
|
||||
/* Hints to the compiler about likely/unlikely branches. */
|
||||
#if defined (__GNUC__) || defined(__clang__)
|
||||
#define UPB_LIKELY(x) __builtin_expect((x),1)
|
||||
#define UPB_UNLIKELY(x) __builtin_expect((x),0)
|
||||
#else
|
||||
#define UPB_LIKELY(x) (x)
|
||||
#define UPB_UNLIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
/* Define UPB_BIG_ENDIAN manually if you're on big endian and your compiler
|
||||
* doesn't provide these preprocessor symbols. */
|
||||
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||
#define UPB_BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
/* Macros for function attributes on compilers that support them. */
|
||||
#ifdef __GNUC__
|
||||
#define UPB_FORCEINLINE __inline__ __attribute__((always_inline))
|
||||
#define UPB_NOINLINE __attribute__((noinline))
|
||||
#define UPB_NORETURN __attribute__((__noreturn__))
|
||||
#else /* !defined(__GNUC__) */
|
||||
#define UPB_FORCEINLINE
|
||||
#define UPB_NOINLINE
|
||||
#define UPB_NORETURN
|
||||
#endif
|
||||
|
||||
#if __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L
|
||||
/* C99/C++11 versions. */
|
||||
#include <stdio.h>
|
||||
#define _upb_snprintf snprintf
|
||||
#define _upb_vsnprintf vsnprintf
|
||||
#define _upb_va_copy(a, b) va_copy(a, b)
|
||||
#elif defined(_MSC_VER)
|
||||
/* Microsoft C/C++ versions. */
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#if _MSC_VER < 1900
|
||||
int msvc_snprintf(char* s, size_t n, const char* format, ...);
|
||||
int msvc_vsnprintf(char* s, size_t n, const char* format, va_list arg);
|
||||
#define UPB_MSVC_VSNPRINTF
|
||||
#define _upb_snprintf msvc_snprintf
|
||||
#define _upb_vsnprintf msvc_vsnprintf
|
||||
#else
|
||||
#define _upb_snprintf snprintf
|
||||
#define _upb_vsnprintf vsnprintf
|
||||
#endif
|
||||
#define _upb_va_copy(a, b) va_copy(a, b)
|
||||
#elif defined __GNUC__
|
||||
/* A few hacky workarounds for functions not in C89.
|
||||
* For internal use only!
|
||||
* TODO(haberman): fix these by including our own implementations, or finding
|
||||
* another workaround.
|
||||
*/
|
||||
#define _upb_snprintf __builtin_snprintf
|
||||
#define _upb_vsnprintf __builtin_vsnprintf
|
||||
#define _upb_va_copy(a, b) __va_copy(a, b)
|
||||
#else
|
||||
#error Need implementations of [v]snprintf and va_copy
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) || \
|
||||
(defined(_MSC_VER) && _MSC_VER >= 1900)
|
||||
/* C++11 is present */
|
||||
#else
|
||||
#error upb requires C++11 for C++ support
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define UPB_MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||
#define UPB_MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
#define UPB_UNUSED(var) (void)var
|
||||
|
||||
/* UPB_ASSERT(): in release mode, we use the expression without letting it be
|
||||
* evaluated. This prevents "unused variable" warnings. */
|
||||
#ifdef NDEBUG
|
||||
#define UPB_ASSERT(expr) do {} while (false && (expr))
|
||||
#else
|
||||
#define UPB_ASSERT(expr) assert(expr)
|
||||
#endif
|
||||
|
||||
/* UPB_ASSERT_DEBUGVAR(): assert that uses functions or variables that only
|
||||
* exist in debug mode. This turns into regular assert. */
|
||||
#define UPB_ASSERT_DEBUGVAR(expr) assert(expr)
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define UPB_UNREACHABLE() do { assert(0); __builtin_unreachable(); } while(0)
|
||||
#else
|
||||
#define UPB_UNREACHABLE() do { assert(0); } while(0)
|
||||
#endif
|
||||
|
||||
/* UPB_INFINITY representing floating-point positive infinity. */
|
||||
#include <math.h>
|
||||
#ifdef INFINITY
|
||||
#define UPB_INFINITY INFINITY
|
||||
#else
|
||||
#define UPB_INFINITY (1.0 / 0.0)
|
||||
#endif
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/* See port_def.inc. This should #undef all macros #defined there. */
|
||||
|
||||
#undef UPB_SIZE
|
||||
#undef UPB_FIELD_AT
|
||||
#undef UPB_READ_ONEOF
|
||||
#undef UPB_WRITE_ONEOF
|
||||
#undef UPB_INLINE
|
||||
#undef UPB_FORCEINLINE
|
||||
#undef UPB_NOINLINE
|
||||
#undef UPB_NORETURN
|
||||
#undef UPB_MAX
|
||||
#undef UPB_MIN
|
||||
#undef UPB_UNUSED
|
||||
#undef UPB_ASSERT
|
||||
#undef UPB_ASSERT_DEBUGVAR
|
||||
#undef UPB_UNREACHABLE
|
||||
#undef UPB_INFINITY
|
||||
#undef UPB_MSVC_VSNPRINTF
|
||||
#undef _upb_snprintf
|
||||
#undef _upb_vsnprintf
|
||||
#undef _upb_va_copy
|
||||
+921
@@ -0,0 +1,921 @@
|
||||
/*
|
||||
** upb_table Implementation
|
||||
**
|
||||
** Implementation is heavily inspired by Lua's ltable.c.
|
||||
*/
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/table.int.h"
|
||||
#else
|
||||
#include "upb/table.int.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/port_def.inc"
|
||||
#else
|
||||
#include "upb/port_def.inc"
|
||||
#endif
|
||||
|
||||
#define UPB_MAXARRSIZE 16 /* 64k. */
|
||||
|
||||
/* From Chromium. */
|
||||
#define ARRAY_SIZE(x) \
|
||||
((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
|
||||
|
||||
static void upb_check_alloc(upb_table *t, upb_alloc *a) {
|
||||
UPB_UNUSED(t);
|
||||
UPB_UNUSED(a);
|
||||
UPB_ASSERT_DEBUGVAR(t->alloc == a);
|
||||
}
|
||||
|
||||
static const double MAX_LOAD = 0.85;
|
||||
|
||||
/* The minimum utilization of the array part of a mixed hash/array table. This
|
||||
* is a speed/memory-usage tradeoff (though it's not straightforward because of
|
||||
* cache effects). The lower this is, the more memory we'll use. */
|
||||
static const double MIN_DENSITY = 0.1;
|
||||
|
||||
bool is_pow2(uint64_t v) { return v == 0 || (v & (v - 1)) == 0; }
|
||||
|
||||
int log2ceil(uint64_t v) {
|
||||
int ret = 0;
|
||||
bool pow2 = is_pow2(v);
|
||||
while (v >>= 1) ret++;
|
||||
ret = pow2 ? ret : ret + 1; /* Ceiling. */
|
||||
return UPB_MIN(UPB_MAXARRSIZE, ret);
|
||||
}
|
||||
|
||||
char *upb_strdup(const char *s, upb_alloc *a) {
|
||||
return upb_strdup2(s, strlen(s), a);
|
||||
}
|
||||
|
||||
char *upb_strdup2(const char *s, size_t len, upb_alloc *a) {
|
||||
size_t n;
|
||||
char *p;
|
||||
|
||||
/* Prevent overflow errors. */
|
||||
if (len == SIZE_MAX) return NULL;
|
||||
/* Always null-terminate, even if binary data; but don't rely on the input to
|
||||
* have a null-terminating byte since it may be a raw binary buffer. */
|
||||
n = len + 1;
|
||||
p = upb_malloc(a, n);
|
||||
if (p) {
|
||||
memcpy(p, s, len);
|
||||
p[len] = 0;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* A type to represent the lookup key of either a strtable or an inttable. */
|
||||
typedef union {
|
||||
uintptr_t num;
|
||||
struct {
|
||||
const char *str;
|
||||
size_t len;
|
||||
} str;
|
||||
} lookupkey_t;
|
||||
|
||||
static lookupkey_t strkey2(const char *str, size_t len) {
|
||||
lookupkey_t k;
|
||||
k.str.str = str;
|
||||
k.str.len = len;
|
||||
return k;
|
||||
}
|
||||
|
||||
static lookupkey_t intkey(uintptr_t key) {
|
||||
lookupkey_t k;
|
||||
k.num = key;
|
||||
return k;
|
||||
}
|
||||
|
||||
typedef uint32_t hashfunc_t(upb_tabkey key);
|
||||
typedef bool eqlfunc_t(upb_tabkey k1, lookupkey_t k2);
|
||||
|
||||
/* Base table (shared code) ***************************************************/
|
||||
|
||||
/* For when we need to cast away const. */
|
||||
static upb_tabent *mutable_entries(upb_table *t) {
|
||||
return (upb_tabent*)t->entries;
|
||||
}
|
||||
|
||||
static bool isfull(upb_table *t) {
|
||||
if (upb_table_size(t) == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return ((double)(t->count + 1) / upb_table_size(t)) > MAX_LOAD;
|
||||
}
|
||||
}
|
||||
|
||||
static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2,
|
||||
upb_alloc *a) {
|
||||
size_t bytes;
|
||||
|
||||
t->count = 0;
|
||||
t->ctype = ctype;
|
||||
t->size_lg2 = size_lg2;
|
||||
t->mask = upb_table_size(t) ? upb_table_size(t) - 1 : 0;
|
||||
#ifndef NDEBUG
|
||||
t->alloc = a;
|
||||
#endif
|
||||
bytes = upb_table_size(t) * sizeof(upb_tabent);
|
||||
if (bytes > 0) {
|
||||
t->entries = upb_malloc(a, bytes);
|
||||
if (!t->entries) return false;
|
||||
memset(mutable_entries(t), 0, bytes);
|
||||
} else {
|
||||
t->entries = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void uninit(upb_table *t, upb_alloc *a) {
|
||||
upb_check_alloc(t, a);
|
||||
upb_free(a, mutable_entries(t));
|
||||
}
|
||||
|
||||
static upb_tabent *emptyent(upb_table *t) {
|
||||
upb_tabent *e = mutable_entries(t) + upb_table_size(t);
|
||||
while (1) { if (upb_tabent_isempty(--e)) return e; UPB_ASSERT(e > t->entries); }
|
||||
}
|
||||
|
||||
static upb_tabent *getentry_mutable(upb_table *t, uint32_t hash) {
|
||||
return (upb_tabent*)upb_getentry(t, hash);
|
||||
}
|
||||
|
||||
static const upb_tabent *findentry(const upb_table *t, lookupkey_t key,
|
||||
uint32_t hash, eqlfunc_t *eql) {
|
||||
const upb_tabent *e;
|
||||
|
||||
if (t->size_lg2 == 0) return NULL;
|
||||
e = upb_getentry(t, hash);
|
||||
if (upb_tabent_isempty(e)) return NULL;
|
||||
while (1) {
|
||||
if (eql(e->key, key)) return e;
|
||||
if ((e = e->next) == NULL) return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static upb_tabent *findentry_mutable(upb_table *t, lookupkey_t key,
|
||||
uint32_t hash, eqlfunc_t *eql) {
|
||||
return (upb_tabent*)findentry(t, key, hash, eql);
|
||||
}
|
||||
|
||||
static bool lookup(const upb_table *t, lookupkey_t key, upb_value *v,
|
||||
uint32_t hash, eqlfunc_t *eql) {
|
||||
const upb_tabent *e = findentry(t, key, hash, eql);
|
||||
if (e) {
|
||||
if (v) {
|
||||
_upb_value_setval(v, e->val.val, t->ctype);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* The given key must not already exist in the table. */
|
||||
static void insert(upb_table *t, lookupkey_t key, upb_tabkey tabkey,
|
||||
upb_value val, uint32_t hash,
|
||||
hashfunc_t *hashfunc, eqlfunc_t *eql) {
|
||||
upb_tabent *mainpos_e;
|
||||
upb_tabent *our_e;
|
||||
|
||||
UPB_ASSERT(findentry(t, key, hash, eql) == NULL);
|
||||
UPB_ASSERT_DEBUGVAR(val.ctype == t->ctype);
|
||||
|
||||
t->count++;
|
||||
mainpos_e = getentry_mutable(t, hash);
|
||||
our_e = mainpos_e;
|
||||
|
||||
if (upb_tabent_isempty(mainpos_e)) {
|
||||
/* Our main position is empty; use it. */
|
||||
our_e->next = NULL;
|
||||
} else {
|
||||
/* Collision. */
|
||||
upb_tabent *new_e = emptyent(t);
|
||||
/* Head of collider's chain. */
|
||||
upb_tabent *chain = getentry_mutable(t, hashfunc(mainpos_e->key));
|
||||
if (chain == mainpos_e) {
|
||||
/* Existing ent is in its main posisiton (it has the same hash as us, and
|
||||
* is the head of our chain). Insert to new ent and append to this chain. */
|
||||
new_e->next = mainpos_e->next;
|
||||
mainpos_e->next = new_e;
|
||||
our_e = new_e;
|
||||
} else {
|
||||
/* Existing ent is not in its main position (it is a node in some other
|
||||
* chain). This implies that no existing ent in the table has our hash.
|
||||
* Evict it (updating its chain) and use its ent for head of our chain. */
|
||||
*new_e = *mainpos_e; /* copies next. */
|
||||
while (chain->next != mainpos_e) {
|
||||
chain = (upb_tabent*)chain->next;
|
||||
UPB_ASSERT(chain);
|
||||
}
|
||||
chain->next = new_e;
|
||||
our_e = mainpos_e;
|
||||
our_e->next = NULL;
|
||||
}
|
||||
}
|
||||
our_e->key = tabkey;
|
||||
our_e->val.val = val.val;
|
||||
UPB_ASSERT(findentry(t, key, hash, eql) == our_e);
|
||||
}
|
||||
|
||||
static bool rm(upb_table *t, lookupkey_t key, upb_value *val,
|
||||
upb_tabkey *removed, uint32_t hash, eqlfunc_t *eql) {
|
||||
upb_tabent *chain = getentry_mutable(t, hash);
|
||||
if (upb_tabent_isempty(chain)) return false;
|
||||
if (eql(chain->key, key)) {
|
||||
/* Element to remove is at the head of its chain. */
|
||||
t->count--;
|
||||
if (val) _upb_value_setval(val, chain->val.val, t->ctype);
|
||||
if (removed) *removed = chain->key;
|
||||
if (chain->next) {
|
||||
upb_tabent *move = (upb_tabent*)chain->next;
|
||||
*chain = *move;
|
||||
move->key = 0; /* Make the slot empty. */
|
||||
} else {
|
||||
chain->key = 0; /* Make the slot empty. */
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
/* Element to remove is either in a non-head position or not in the
|
||||
* table. */
|
||||
while (chain->next && !eql(chain->next->key, key)) {
|
||||
chain = (upb_tabent*)chain->next;
|
||||
}
|
||||
if (chain->next) {
|
||||
/* Found element to remove. */
|
||||
upb_tabent *rm = (upb_tabent*)chain->next;
|
||||
t->count--;
|
||||
if (val) _upb_value_setval(val, chain->next->val.val, t->ctype);
|
||||
if (removed) *removed = rm->key;
|
||||
rm->key = 0; /* Make the slot empty. */
|
||||
chain->next = rm->next;
|
||||
return true;
|
||||
} else {
|
||||
/* Element to remove is not in the table. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static size_t next(const upb_table *t, size_t i) {
|
||||
do {
|
||||
if (++i >= upb_table_size(t))
|
||||
return SIZE_MAX;
|
||||
} while(upb_tabent_isempty(&t->entries[i]));
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static size_t begin(const upb_table *t) {
|
||||
return next(t, -1);
|
||||
}
|
||||
|
||||
|
||||
/* upb_strtable ***************************************************************/
|
||||
|
||||
/* A simple "subclass" of upb_table that only adds a hash function for strings. */
|
||||
|
||||
static upb_tabkey strcopy(lookupkey_t k2, upb_alloc *a) {
|
||||
uint32_t len = (uint32_t) k2.str.len;
|
||||
char *str = upb_malloc(a, k2.str.len + sizeof(uint32_t) + 1);
|
||||
if (str == NULL) return 0;
|
||||
memcpy(str, &len, sizeof(uint32_t));
|
||||
memcpy(str + sizeof(uint32_t), k2.str.str, k2.str.len);
|
||||
str[sizeof(uint32_t) + k2.str.len] = '\0';
|
||||
return (uintptr_t)str;
|
||||
}
|
||||
|
||||
static uint32_t strhash(upb_tabkey key) {
|
||||
uint32_t len;
|
||||
char *str = upb_tabstr(key, &len);
|
||||
return upb_murmur_hash2(str, len, 0);
|
||||
}
|
||||
|
||||
static bool streql(upb_tabkey k1, lookupkey_t k2) {
|
||||
uint32_t len;
|
||||
char *str = upb_tabstr(k1, &len);
|
||||
return len == k2.str.len && memcmp(str, k2.str.str, len) == 0;
|
||||
}
|
||||
|
||||
bool upb_strtable_init2(upb_strtable *t, upb_ctype_t ctype, upb_alloc *a) {
|
||||
return init(&t->t, ctype, 2, a);
|
||||
}
|
||||
|
||||
void upb_strtable_uninit2(upb_strtable *t, upb_alloc *a) {
|
||||
size_t i;
|
||||
for (i = 0; i < upb_table_size(&t->t); i++)
|
||||
upb_free(a, (void*)t->t.entries[i].key);
|
||||
uninit(&t->t, a);
|
||||
}
|
||||
|
||||
bool upb_strtable_resize(upb_strtable *t, size_t size_lg2, upb_alloc *a) {
|
||||
upb_strtable new_table;
|
||||
upb_strtable_iter i;
|
||||
|
||||
upb_check_alloc(&t->t, a);
|
||||
|
||||
if (!init(&new_table.t, t->t.ctype, size_lg2, a))
|
||||
return false;
|
||||
upb_strtable_begin(&i, t);
|
||||
for ( ; !upb_strtable_done(&i); upb_strtable_next(&i)) {
|
||||
upb_strtable_insert3(
|
||||
&new_table,
|
||||
upb_strtable_iter_key(&i),
|
||||
upb_strtable_iter_keylength(&i),
|
||||
upb_strtable_iter_value(&i),
|
||||
a);
|
||||
}
|
||||
upb_strtable_uninit2(t, a);
|
||||
*t = new_table;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool upb_strtable_insert3(upb_strtable *t, const char *k, size_t len,
|
||||
upb_value v, upb_alloc *a) {
|
||||
lookupkey_t key;
|
||||
upb_tabkey tabkey;
|
||||
uint32_t hash;
|
||||
|
||||
upb_check_alloc(&t->t, a);
|
||||
|
||||
if (isfull(&t->t)) {
|
||||
/* Need to resize. New table of double the size, add old elements to it. */
|
||||
if (!upb_strtable_resize(t, t->t.size_lg2 + 1, a)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
key = strkey2(k, len);
|
||||
tabkey = strcopy(key, a);
|
||||
if (tabkey == 0) return false;
|
||||
|
||||
hash = upb_murmur_hash2(key.str.str, key.str.len, 0);
|
||||
insert(&t->t, key, tabkey, v, hash, &strhash, &streql);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool upb_strtable_lookup2(const upb_strtable *t, const char *key, size_t len,
|
||||
upb_value *v) {
|
||||
uint32_t hash = upb_murmur_hash2(key, len, 0);
|
||||
return lookup(&t->t, strkey2(key, len), v, hash, &streql);
|
||||
}
|
||||
|
||||
bool upb_strtable_remove3(upb_strtable *t, const char *key, size_t len,
|
||||
upb_value *val, upb_alloc *alloc) {
|
||||
uint32_t hash = upb_murmur_hash2(key, len, 0);
|
||||
upb_tabkey tabkey;
|
||||
if (rm(&t->t, strkey2(key, len), val, &tabkey, hash, &streql)) {
|
||||
upb_free(alloc, (void*)tabkey);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Iteration */
|
||||
|
||||
static const upb_tabent *str_tabent(const upb_strtable_iter *i) {
|
||||
return &i->t->t.entries[i->index];
|
||||
}
|
||||
|
||||
void upb_strtable_begin(upb_strtable_iter *i, const upb_strtable *t) {
|
||||
i->t = t;
|
||||
i->index = begin(&t->t);
|
||||
}
|
||||
|
||||
void upb_strtable_next(upb_strtable_iter *i) {
|
||||
i->index = next(&i->t->t, i->index);
|
||||
}
|
||||
|
||||
bool upb_strtable_done(const upb_strtable_iter *i) {
|
||||
if (!i->t) return true;
|
||||
return i->index >= upb_table_size(&i->t->t) ||
|
||||
upb_tabent_isempty(str_tabent(i));
|
||||
}
|
||||
|
||||
const char *upb_strtable_iter_key(const upb_strtable_iter *i) {
|
||||
UPB_ASSERT(!upb_strtable_done(i));
|
||||
return upb_tabstr(str_tabent(i)->key, NULL);
|
||||
}
|
||||
|
||||
size_t upb_strtable_iter_keylength(const upb_strtable_iter *i) {
|
||||
uint32_t len;
|
||||
UPB_ASSERT(!upb_strtable_done(i));
|
||||
upb_tabstr(str_tabent(i)->key, &len);
|
||||
return len;
|
||||
}
|
||||
|
||||
upb_value upb_strtable_iter_value(const upb_strtable_iter *i) {
|
||||
UPB_ASSERT(!upb_strtable_done(i));
|
||||
return _upb_value_val(str_tabent(i)->val.val, i->t->t.ctype);
|
||||
}
|
||||
|
||||
void upb_strtable_iter_setdone(upb_strtable_iter *i) {
|
||||
i->t = NULL;
|
||||
i->index = SIZE_MAX;
|
||||
}
|
||||
|
||||
bool upb_strtable_iter_isequal(const upb_strtable_iter *i1,
|
||||
const upb_strtable_iter *i2) {
|
||||
if (upb_strtable_done(i1) && upb_strtable_done(i2))
|
||||
return true;
|
||||
return i1->t == i2->t && i1->index == i2->index;
|
||||
}
|
||||
|
||||
|
||||
/* upb_inttable ***************************************************************/
|
||||
|
||||
/* For inttables we use a hybrid structure where small keys are kept in an
|
||||
* array and large keys are put in the hash table. */
|
||||
|
||||
static uint32_t inthash(upb_tabkey key) { return upb_inthash(key); }
|
||||
|
||||
static bool inteql(upb_tabkey k1, lookupkey_t k2) {
|
||||
return k1 == k2.num;
|
||||
}
|
||||
|
||||
static upb_tabval *mutable_array(upb_inttable *t) {
|
||||
return (upb_tabval*)t->array;
|
||||
}
|
||||
|
||||
static upb_tabval *inttable_val(upb_inttable *t, uintptr_t key) {
|
||||
if (key < t->array_size) {
|
||||
return upb_arrhas(t->array[key]) ? &(mutable_array(t)[key]) : NULL;
|
||||
} else {
|
||||
upb_tabent *e =
|
||||
findentry_mutable(&t->t, intkey(key), upb_inthash(key), &inteql);
|
||||
return e ? &e->val : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const upb_tabval *inttable_val_const(const upb_inttable *t,
|
||||
uintptr_t key) {
|
||||
return inttable_val((upb_inttable*)t, key);
|
||||
}
|
||||
|
||||
size_t upb_inttable_count(const upb_inttable *t) {
|
||||
return t->t.count + t->array_count;
|
||||
}
|
||||
|
||||
static void check(upb_inttable *t) {
|
||||
UPB_UNUSED(t);
|
||||
#if defined(UPB_DEBUG_TABLE) && !defined(NDEBUG)
|
||||
{
|
||||
/* This check is very expensive (makes inserts/deletes O(N)). */
|
||||
size_t count = 0;
|
||||
upb_inttable_iter i;
|
||||
upb_inttable_begin(&i, t);
|
||||
for(; !upb_inttable_done(&i); upb_inttable_next(&i), count++) {
|
||||
UPB_ASSERT(upb_inttable_lookup(t, upb_inttable_iter_key(&i), NULL));
|
||||
}
|
||||
UPB_ASSERT(count == upb_inttable_count(t));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool upb_inttable_sizedinit(upb_inttable *t, upb_ctype_t ctype,
|
||||
size_t asize, int hsize_lg2, upb_alloc *a) {
|
||||
size_t array_bytes;
|
||||
|
||||
if (!init(&t->t, ctype, hsize_lg2, a)) return false;
|
||||
/* Always make the array part at least 1 long, so that we know key 0
|
||||
* won't be in the hash part, which simplifies things. */
|
||||
t->array_size = UPB_MAX(1, asize);
|
||||
t->array_count = 0;
|
||||
array_bytes = t->array_size * sizeof(upb_value);
|
||||
t->array = upb_malloc(a, array_bytes);
|
||||
if (!t->array) {
|
||||
uninit(&t->t, a);
|
||||
return false;
|
||||
}
|
||||
memset(mutable_array(t), 0xff, array_bytes);
|
||||
check(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool upb_inttable_init2(upb_inttable *t, upb_ctype_t ctype, upb_alloc *a) {
|
||||
return upb_inttable_sizedinit(t, ctype, 0, 4, a);
|
||||
}
|
||||
|
||||
void upb_inttable_uninit2(upb_inttable *t, upb_alloc *a) {
|
||||
uninit(&t->t, a);
|
||||
upb_free(a, mutable_array(t));
|
||||
}
|
||||
|
||||
bool upb_inttable_insert2(upb_inttable *t, uintptr_t key, upb_value val,
|
||||
upb_alloc *a) {
|
||||
upb_tabval tabval;
|
||||
tabval.val = val.val;
|
||||
UPB_ASSERT(upb_arrhas(tabval)); /* This will reject (uint64_t)-1. Fix this. */
|
||||
|
||||
upb_check_alloc(&t->t, a);
|
||||
|
||||
if (key < t->array_size) {
|
||||
UPB_ASSERT(!upb_arrhas(t->array[key]));
|
||||
t->array_count++;
|
||||
mutable_array(t)[key].val = val.val;
|
||||
} else {
|
||||
if (isfull(&t->t)) {
|
||||
/* Need to resize the hash part, but we re-use the array part. */
|
||||
size_t i;
|
||||
upb_table new_table;
|
||||
|
||||
if (!init(&new_table, t->t.ctype, t->t.size_lg2 + 1, a)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = begin(&t->t); i < upb_table_size(&t->t); i = next(&t->t, i)) {
|
||||
const upb_tabent *e = &t->t.entries[i];
|
||||
uint32_t hash;
|
||||
upb_value v;
|
||||
|
||||
_upb_value_setval(&v, e->val.val, t->t.ctype);
|
||||
hash = upb_inthash(e->key);
|
||||
insert(&new_table, intkey(e->key), e->key, v, hash, &inthash, &inteql);
|
||||
}
|
||||
|
||||
UPB_ASSERT(t->t.count == new_table.count);
|
||||
|
||||
uninit(&t->t, a);
|
||||
t->t = new_table;
|
||||
}
|
||||
insert(&t->t, intkey(key), key, val, upb_inthash(key), &inthash, &inteql);
|
||||
}
|
||||
check(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool upb_inttable_lookup(const upb_inttable *t, uintptr_t key, upb_value *v) {
|
||||
const upb_tabval *table_v = inttable_val_const(t, key);
|
||||
if (!table_v) return false;
|
||||
if (v) _upb_value_setval(v, table_v->val, t->t.ctype);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool upb_inttable_replace(upb_inttable *t, uintptr_t key, upb_value val) {
|
||||
upb_tabval *table_v = inttable_val(t, key);
|
||||
if (!table_v) return false;
|
||||
table_v->val = val.val;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val) {
|
||||
bool success;
|
||||
if (key < t->array_size) {
|
||||
if (upb_arrhas(t->array[key])) {
|
||||
upb_tabval empty = UPB_TABVALUE_EMPTY_INIT;
|
||||
t->array_count--;
|
||||
if (val) {
|
||||
_upb_value_setval(val, t->array[key].val, t->t.ctype);
|
||||
}
|
||||
mutable_array(t)[key] = empty;
|
||||
success = true;
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
} else {
|
||||
success = rm(&t->t, intkey(key), val, NULL, upb_inthash(key), &inteql);
|
||||
}
|
||||
check(t);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a) {
|
||||
upb_check_alloc(&t->t, a);
|
||||
return upb_inttable_insert2(t, upb_inttable_count(t), val, a);
|
||||
}
|
||||
|
||||
upb_value upb_inttable_pop(upb_inttable *t) {
|
||||
upb_value val;
|
||||
bool ok = upb_inttable_remove(t, upb_inttable_count(t) - 1, &val);
|
||||
UPB_ASSERT(ok);
|
||||
return val;
|
||||
}
|
||||
|
||||
bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val,
|
||||
upb_alloc *a) {
|
||||
upb_check_alloc(&t->t, a);
|
||||
return upb_inttable_insert2(t, (uintptr_t)key, val, a);
|
||||
}
|
||||
|
||||
bool upb_inttable_lookupptr(const upb_inttable *t, const void *key,
|
||||
upb_value *v) {
|
||||
return upb_inttable_lookup(t, (uintptr_t)key, v);
|
||||
}
|
||||
|
||||
bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val) {
|
||||
return upb_inttable_remove(t, (uintptr_t)key, val);
|
||||
}
|
||||
|
||||
void upb_inttable_compact2(upb_inttable *t, upb_alloc *a) {
|
||||
/* A power-of-two histogram of the table keys. */
|
||||
size_t counts[UPB_MAXARRSIZE + 1] = {0};
|
||||
|
||||
/* The max key in each bucket. */
|
||||
uintptr_t max[UPB_MAXARRSIZE + 1] = {0};
|
||||
|
||||
upb_inttable_iter i;
|
||||
size_t arr_count;
|
||||
int size_lg2;
|
||||
upb_inttable new_t;
|
||||
|
||||
upb_check_alloc(&t->t, a);
|
||||
|
||||
upb_inttable_begin(&i, t);
|
||||
for (; !upb_inttable_done(&i); upb_inttable_next(&i)) {
|
||||
uintptr_t key = upb_inttable_iter_key(&i);
|
||||
int bucket = log2ceil(key);
|
||||
max[bucket] = UPB_MAX(max[bucket], key);
|
||||
counts[bucket]++;
|
||||
}
|
||||
|
||||
/* Find the largest power of two that satisfies the MIN_DENSITY
|
||||
* definition (while actually having some keys). */
|
||||
arr_count = upb_inttable_count(t);
|
||||
|
||||
for (size_lg2 = ARRAY_SIZE(counts) - 1; size_lg2 > 0; size_lg2--) {
|
||||
if (counts[size_lg2] == 0) {
|
||||
/* We can halve again without losing any entries. */
|
||||
continue;
|
||||
} else if (arr_count >= (1 << size_lg2) * MIN_DENSITY) {
|
||||
break;
|
||||
}
|
||||
|
||||
arr_count -= counts[size_lg2];
|
||||
}
|
||||
|
||||
UPB_ASSERT(arr_count <= upb_inttable_count(t));
|
||||
|
||||
{
|
||||
/* Insert all elements into new, perfectly-sized table. */
|
||||
size_t arr_size = max[size_lg2] + 1; /* +1 so arr[max] will fit. */
|
||||
size_t hash_count = upb_inttable_count(t) - arr_count;
|
||||
size_t hash_size = hash_count ? (hash_count / MAX_LOAD) + 1 : 0;
|
||||
int hashsize_lg2 = log2ceil(hash_size);
|
||||
|
||||
upb_inttable_sizedinit(&new_t, t->t.ctype, arr_size, hashsize_lg2, a);
|
||||
upb_inttable_begin(&i, t);
|
||||
for (; !upb_inttable_done(&i); upb_inttable_next(&i)) {
|
||||
uintptr_t k = upb_inttable_iter_key(&i);
|
||||
upb_inttable_insert2(&new_t, k, upb_inttable_iter_value(&i), a);
|
||||
}
|
||||
UPB_ASSERT(new_t.array_size == arr_size);
|
||||
UPB_ASSERT(new_t.t.size_lg2 == hashsize_lg2);
|
||||
}
|
||||
upb_inttable_uninit2(t, a);
|
||||
*t = new_t;
|
||||
}
|
||||
|
||||
/* Iteration. */
|
||||
|
||||
static const upb_tabent *int_tabent(const upb_inttable_iter *i) {
|
||||
UPB_ASSERT(!i->array_part);
|
||||
return &i->t->t.entries[i->index];
|
||||
}
|
||||
|
||||
static upb_tabval int_arrent(const upb_inttable_iter *i) {
|
||||
UPB_ASSERT(i->array_part);
|
||||
return i->t->array[i->index];
|
||||
}
|
||||
|
||||
void upb_inttable_begin(upb_inttable_iter *i, const upb_inttable *t) {
|
||||
i->t = t;
|
||||
i->index = -1;
|
||||
i->array_part = true;
|
||||
upb_inttable_next(i);
|
||||
}
|
||||
|
||||
void upb_inttable_next(upb_inttable_iter *iter) {
|
||||
const upb_inttable *t = iter->t;
|
||||
if (iter->array_part) {
|
||||
while (++iter->index < t->array_size) {
|
||||
if (upb_arrhas(int_arrent(iter))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
iter->array_part = false;
|
||||
iter->index = begin(&t->t);
|
||||
} else {
|
||||
iter->index = next(&t->t, iter->index);
|
||||
}
|
||||
}
|
||||
|
||||
bool upb_inttable_done(const upb_inttable_iter *i) {
|
||||
if (!i->t) return true;
|
||||
if (i->array_part) {
|
||||
return i->index >= i->t->array_size ||
|
||||
!upb_arrhas(int_arrent(i));
|
||||
} else {
|
||||
return i->index >= upb_table_size(&i->t->t) ||
|
||||
upb_tabent_isempty(int_tabent(i));
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t upb_inttable_iter_key(const upb_inttable_iter *i) {
|
||||
UPB_ASSERT(!upb_inttable_done(i));
|
||||
return i->array_part ? i->index : int_tabent(i)->key;
|
||||
}
|
||||
|
||||
upb_value upb_inttable_iter_value(const upb_inttable_iter *i) {
|
||||
UPB_ASSERT(!upb_inttable_done(i));
|
||||
return _upb_value_val(
|
||||
i->array_part ? i->t->array[i->index].val : int_tabent(i)->val.val,
|
||||
i->t->t.ctype);
|
||||
}
|
||||
|
||||
void upb_inttable_iter_setdone(upb_inttable_iter *i) {
|
||||
i->t = NULL;
|
||||
i->index = SIZE_MAX;
|
||||
i->array_part = false;
|
||||
}
|
||||
|
||||
bool upb_inttable_iter_isequal(const upb_inttable_iter *i1,
|
||||
const upb_inttable_iter *i2) {
|
||||
if (upb_inttable_done(i1) && upb_inttable_done(i2))
|
||||
return true;
|
||||
return i1->t == i2->t && i1->index == i2->index &&
|
||||
i1->array_part == i2->array_part;
|
||||
}
|
||||
|
||||
#if defined(UPB_UNALIGNED_READS_OK) || defined(__s390x__)
|
||||
/* -----------------------------------------------------------------------------
|
||||
* MurmurHash2, by Austin Appleby (released as public domain).
|
||||
* Reformatted and C99-ified by Joshua Haberman.
|
||||
* Note - This code makes a few assumptions about how your machine behaves -
|
||||
* 1. We can read a 4-byte value from any address without crashing
|
||||
* 2. sizeof(int) == 4 (in upb this limitation is removed by using uint32_t
|
||||
* And it has a few limitations -
|
||||
* 1. It will not work incrementally.
|
||||
* 2. It will not produce the same results on little-endian and big-endian
|
||||
* machines. */
|
||||
uint32_t upb_murmur_hash2(const void *key, size_t len, uint32_t seed) {
|
||||
/* 'm' and 'r' are mixing constants generated offline.
|
||||
* They're not really 'magic', they just happen to work well. */
|
||||
const uint32_t m = 0x5bd1e995;
|
||||
const int32_t r = 24;
|
||||
|
||||
/* Initialize the hash to a 'random' value */
|
||||
uint32_t h = seed ^ len;
|
||||
|
||||
/* Mix 4 bytes at a time into the hash */
|
||||
const uint8_t * data = (const uint8_t *)key;
|
||||
while(len >= 4) {
|
||||
uint32_t k;
|
||||
memcpy(&k, data, sizeof(k));
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h *= m;
|
||||
h ^= k;
|
||||
|
||||
data += 4;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
/* Handle the last few bytes of the input array */
|
||||
switch(len) {
|
||||
case 3: h ^= data[2] << 16;
|
||||
case 2: h ^= data[1] << 8;
|
||||
case 1: h ^= data[0]; h *= m;
|
||||
};
|
||||
|
||||
/* Do a few final mixes of the hash to ensure the last few
|
||||
* bytes are well-incorporated. */
|
||||
h ^= h >> 13;
|
||||
h *= m;
|
||||
h ^= h >> 15;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
#else /* !UPB_UNALIGNED_READS_OK */
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* MurmurHashAligned2, by Austin Appleby
|
||||
* Same algorithm as MurmurHash2, but only does aligned reads - should be safer
|
||||
* on certain platforms.
|
||||
* Performance will be lower than MurmurHash2 */
|
||||
|
||||
#define MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; }
|
||||
|
||||
uint32_t upb_murmur_hash2(const void * key, size_t len, uint32_t seed) {
|
||||
const uint32_t m = 0x5bd1e995;
|
||||
const int32_t r = 24;
|
||||
const uint8_t * data = (const uint8_t *)key;
|
||||
uint32_t h = (uint32_t)(seed ^ len);
|
||||
uint8_t align = (uintptr_t)data & 3;
|
||||
|
||||
if(align && (len >= 4)) {
|
||||
/* Pre-load the temp registers */
|
||||
uint32_t t = 0, d = 0;
|
||||
int32_t sl;
|
||||
int32_t sr;
|
||||
|
||||
switch(align) {
|
||||
case 1: t |= data[2] << 16;
|
||||
case 2: t |= data[1] << 8;
|
||||
case 3: t |= data[0];
|
||||
}
|
||||
|
||||
t <<= (8 * align);
|
||||
|
||||
data += 4-align;
|
||||
len -= 4-align;
|
||||
|
||||
sl = 8 * (4-align);
|
||||
sr = 8 * align;
|
||||
|
||||
/* Mix */
|
||||
|
||||
while(len >= 4) {
|
||||
uint32_t k;
|
||||
|
||||
d = *(uint32_t *)data;
|
||||
t = (t >> sr) | (d << sl);
|
||||
|
||||
k = t;
|
||||
|
||||
MIX(h,k,m);
|
||||
|
||||
t = d;
|
||||
|
||||
data += 4;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
/* Handle leftover data in temp registers */
|
||||
|
||||
d = 0;
|
||||
|
||||
if(len >= align) {
|
||||
uint32_t k;
|
||||
|
||||
switch(align) {
|
||||
case 3: d |= data[2] << 16;
|
||||
case 2: d |= data[1] << 8;
|
||||
case 1: d |= data[0];
|
||||
}
|
||||
|
||||
k = (t >> sr) | (d << sl);
|
||||
MIX(h,k,m);
|
||||
|
||||
data += align;
|
||||
len -= align;
|
||||
|
||||
/* ----------
|
||||
* Handle tail bytes */
|
||||
|
||||
switch(len) {
|
||||
case 3: h ^= data[2] << 16;
|
||||
case 2: h ^= data[1] << 8;
|
||||
case 1: h ^= data[0]; h *= m;
|
||||
};
|
||||
} else {
|
||||
switch(len) {
|
||||
case 3: d |= data[2] << 16;
|
||||
case 2: d |= data[1] << 8;
|
||||
case 1: d |= data[0];
|
||||
case 0: h ^= (t >> sr) | (d << sl); h *= m;
|
||||
}
|
||||
}
|
||||
|
||||
h ^= h >> 13;
|
||||
h *= m;
|
||||
h ^= h >> 15;
|
||||
|
||||
return h;
|
||||
} else {
|
||||
while(len >= 4) {
|
||||
uint32_t k = *(uint32_t *)data;
|
||||
|
||||
MIX(h,k,m);
|
||||
|
||||
data += 4;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* Handle tail bytes */
|
||||
|
||||
switch(len) {
|
||||
case 3: h ^= data[2] << 16;
|
||||
case 2: h ^= data[1] << 8;
|
||||
case 1: h ^= data[0]; h *= m;
|
||||
};
|
||||
|
||||
h ^= h >> 13;
|
||||
h *= m;
|
||||
h ^= h >> 15;
|
||||
|
||||
return h;
|
||||
}
|
||||
}
|
||||
#undef MIX
|
||||
|
||||
#endif /* UPB_UNALIGNED_READS_OK */
|
||||
+519
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
** upb_table
|
||||
**
|
||||
** This header is INTERNAL-ONLY! Its interfaces are not public or stable!
|
||||
** This file defines very fast int->upb_value (inttable) and string->upb_value
|
||||
** (strtable) hash tables.
|
||||
**
|
||||
** The table uses chained scatter with Brent's variation (inspired by the Lua
|
||||
** implementation of hash tables). The hash function for strings is Austin
|
||||
** Appleby's "MurmurHash."
|
||||
**
|
||||
** The inttable uses uintptr_t as its key, which guarantees it can be used to
|
||||
** store pointers or integers of at least 32 bits (upb isn't really useful on
|
||||
** systems where sizeof(void*) < 4).
|
||||
**
|
||||
** The table must be homogenous (all values of the same type). In debug
|
||||
** mode, we check this on insert and lookup.
|
||||
*/
|
||||
|
||||
#ifndef UPB_TABLE_H_
|
||||
#define UPB_TABLE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/upb.h"
|
||||
#else
|
||||
#include "upb/upb.h"
|
||||
#endif
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/port_def.inc"
|
||||
#else
|
||||
#include "upb/port_def.inc"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* upb_value ******************************************************************/
|
||||
|
||||
/* A tagged union (stored untagged inside the table) so that we can check that
|
||||
* clients calling table accessors are correctly typed without having to have
|
||||
* an explosion of accessors. */
|
||||
typedef enum {
|
||||
UPB_CTYPE_INT32 = 1,
|
||||
UPB_CTYPE_INT64 = 2,
|
||||
UPB_CTYPE_UINT32 = 3,
|
||||
UPB_CTYPE_UINT64 = 4,
|
||||
UPB_CTYPE_BOOL = 5,
|
||||
UPB_CTYPE_CSTR = 6,
|
||||
UPB_CTYPE_PTR = 7,
|
||||
UPB_CTYPE_CONSTPTR = 8,
|
||||
UPB_CTYPE_FPTR = 9,
|
||||
UPB_CTYPE_FLOAT = 10,
|
||||
UPB_CTYPE_DOUBLE = 11
|
||||
} upb_ctype_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t val;
|
||||
#ifndef NDEBUG
|
||||
/* In debug mode we carry the value type around also so we can check accesses
|
||||
* to be sure the right member is being read. */
|
||||
upb_ctype_t ctype;
|
||||
#endif
|
||||
} upb_value;
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define SET_TYPE(dest, val) UPB_UNUSED(val)
|
||||
#else
|
||||
#define SET_TYPE(dest, val) dest = val
|
||||
#endif
|
||||
|
||||
/* Like strdup(), which isn't always available since it's not ANSI C. */
|
||||
char *upb_strdup(const char *s, upb_alloc *a);
|
||||
/* Variant that works with a length-delimited rather than NULL-delimited string,
|
||||
* as supported by strtable. */
|
||||
char *upb_strdup2(const char *s, size_t len, upb_alloc *a);
|
||||
|
||||
UPB_INLINE char *upb_gstrdup(const char *s) {
|
||||
return upb_strdup(s, &upb_alloc_global);
|
||||
}
|
||||
|
||||
UPB_INLINE void _upb_value_setval(upb_value *v, uint64_t val,
|
||||
upb_ctype_t ctype) {
|
||||
v->val = val;
|
||||
SET_TYPE(v->ctype, ctype);
|
||||
}
|
||||
|
||||
UPB_INLINE upb_value _upb_value_val(uint64_t val, upb_ctype_t ctype) {
|
||||
upb_value ret;
|
||||
_upb_value_setval(&ret, val, ctype);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* For each value ctype, define the following set of functions:
|
||||
*
|
||||
* // Get/set an int32 from a upb_value.
|
||||
* int32_t upb_value_getint32(upb_value val);
|
||||
* void upb_value_setint32(upb_value *val, int32_t cval);
|
||||
*
|
||||
* // Construct a new upb_value from an int32.
|
||||
* upb_value upb_value_int32(int32_t val); */
|
||||
#define FUNCS(name, membername, type_t, converter, proto_type) \
|
||||
UPB_INLINE void upb_value_set ## name(upb_value *val, type_t cval) { \
|
||||
val->val = (converter)cval; \
|
||||
SET_TYPE(val->ctype, proto_type); \
|
||||
} \
|
||||
UPB_INLINE upb_value upb_value_ ## name(type_t val) { \
|
||||
upb_value ret; \
|
||||
upb_value_set ## name(&ret, val); \
|
||||
return ret; \
|
||||
} \
|
||||
UPB_INLINE type_t upb_value_get ## name(upb_value val) { \
|
||||
UPB_ASSERT_DEBUGVAR(val.ctype == proto_type); \
|
||||
return (type_t)(converter)val.val; \
|
||||
}
|
||||
|
||||
FUNCS(int32, int32, int32_t, int32_t, UPB_CTYPE_INT32)
|
||||
FUNCS(int64, int64, int64_t, int64_t, UPB_CTYPE_INT64)
|
||||
FUNCS(uint32, uint32, uint32_t, uint32_t, UPB_CTYPE_UINT32)
|
||||
FUNCS(uint64, uint64, uint64_t, uint64_t, UPB_CTYPE_UINT64)
|
||||
FUNCS(bool, _bool, bool, bool, UPB_CTYPE_BOOL)
|
||||
FUNCS(cstr, cstr, char*, uintptr_t, UPB_CTYPE_CSTR)
|
||||
FUNCS(ptr, ptr, void*, uintptr_t, UPB_CTYPE_PTR)
|
||||
FUNCS(constptr, constptr, const void*, uintptr_t, UPB_CTYPE_CONSTPTR)
|
||||
FUNCS(fptr, fptr, upb_func*, uintptr_t, UPB_CTYPE_FPTR)
|
||||
|
||||
#undef FUNCS
|
||||
|
||||
UPB_INLINE void upb_value_setfloat(upb_value *val, float cval) {
|
||||
memcpy(&val->val, &cval, sizeof(cval));
|
||||
SET_TYPE(val->ctype, UPB_CTYPE_FLOAT);
|
||||
}
|
||||
|
||||
UPB_INLINE void upb_value_setdouble(upb_value *val, double cval) {
|
||||
memcpy(&val->val, &cval, sizeof(cval));
|
||||
SET_TYPE(val->ctype, UPB_CTYPE_DOUBLE);
|
||||
}
|
||||
|
||||
UPB_INLINE upb_value upb_value_float(float cval) {
|
||||
upb_value ret;
|
||||
upb_value_setfloat(&ret, cval);
|
||||
return ret;
|
||||
}
|
||||
|
||||
UPB_INLINE upb_value upb_value_double(double cval) {
|
||||
upb_value ret;
|
||||
upb_value_setdouble(&ret, cval);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#undef SET_TYPE
|
||||
|
||||
|
||||
/* upb_tabkey *****************************************************************/
|
||||
|
||||
/* Either:
|
||||
* 1. an actual integer key, or
|
||||
* 2. a pointer to a string prefixed by its uint32_t length, owned by us.
|
||||
*
|
||||
* ...depending on whether this is a string table or an int table. We would
|
||||
* make this a union of those two types, but C89 doesn't support statically
|
||||
* initializing a non-first union member. */
|
||||
typedef uintptr_t upb_tabkey;
|
||||
|
||||
UPB_INLINE char *upb_tabstr(upb_tabkey key, uint32_t *len) {
|
||||
char* mem = (char*)key;
|
||||
if (len) memcpy(len, mem, sizeof(*len));
|
||||
return mem + sizeof(*len);
|
||||
}
|
||||
|
||||
|
||||
/* upb_tabval *****************************************************************/
|
||||
|
||||
typedef struct {
|
||||
uint64_t val;
|
||||
} upb_tabval;
|
||||
|
||||
#define UPB_TABVALUE_EMPTY_INIT {-1}
|
||||
|
||||
|
||||
/* upb_table ******************************************************************/
|
||||
|
||||
typedef struct _upb_tabent {
|
||||
upb_tabkey key;
|
||||
upb_tabval val;
|
||||
|
||||
/* Internal chaining. This is const so we can create static initializers for
|
||||
* tables. We cast away const sometimes, but *only* when the containing
|
||||
* upb_table is known to be non-const. This requires a bit of care, but
|
||||
* the subtlety is confined to table.c. */
|
||||
const struct _upb_tabent *next;
|
||||
} upb_tabent;
|
||||
|
||||
typedef struct {
|
||||
size_t count; /* Number of entries in the hash part. */
|
||||
size_t mask; /* Mask to turn hash value -> bucket. */
|
||||
upb_ctype_t ctype; /* Type of all values. */
|
||||
uint8_t size_lg2; /* Size of the hashtable part is 2^size_lg2 entries. */
|
||||
|
||||
/* Hash table entries.
|
||||
* Making this const isn't entirely accurate; what we really want is for it to
|
||||
* have the same const-ness as the table it's inside. But there's no way to
|
||||
* declare that in C. So we have to make it const so that we can statically
|
||||
* initialize const hash tables. Then we cast away const when we have to.
|
||||
*/
|
||||
const upb_tabent *entries;
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* This table's allocator. We make the user pass it in to every relevant
|
||||
* function and only use this to check it in debug mode. We do this solely
|
||||
* to keep upb_table as small as possible. This might seem slightly paranoid
|
||||
* but the plan is to use upb_table for all map fields and extension sets in
|
||||
* a forthcoming message representation, so there could be a lot of these.
|
||||
* If this turns out to be too annoying later, we can change it (since this
|
||||
* is an internal-only header file). */
|
||||
upb_alloc *alloc;
|
||||
#endif
|
||||
} upb_table;
|
||||
|
||||
typedef struct {
|
||||
upb_table t;
|
||||
} upb_strtable;
|
||||
|
||||
typedef struct {
|
||||
upb_table t; /* For entries that don't fit in the array part. */
|
||||
const upb_tabval *array; /* Array part of the table. See const note above. */
|
||||
size_t array_size; /* Array part size. */
|
||||
size_t array_count; /* Array part number of elements. */
|
||||
} upb_inttable;
|
||||
|
||||
#define UPB_INTTABLE_INIT(count, mask, ctype, size_lg2, ent, a, asize, acount) \
|
||||
{UPB_TABLE_INIT(count, mask, ctype, size_lg2, ent), a, asize, acount}
|
||||
|
||||
#define UPB_EMPTY_INTTABLE_INIT(ctype) \
|
||||
UPB_INTTABLE_INIT(0, 0, ctype, 0, NULL, NULL, 0, 0)
|
||||
|
||||
#define UPB_ARRAY_EMPTYENT -1
|
||||
|
||||
UPB_INLINE size_t upb_table_size(const upb_table *t) {
|
||||
if (t->size_lg2 == 0)
|
||||
return 0;
|
||||
else
|
||||
return 1 << t->size_lg2;
|
||||
}
|
||||
|
||||
/* Internal-only functions, in .h file only out of necessity. */
|
||||
UPB_INLINE bool upb_tabent_isempty(const upb_tabent *e) {
|
||||
return e->key == 0;
|
||||
}
|
||||
|
||||
/* Used by some of the unit tests for generic hashing functionality. */
|
||||
uint32_t upb_murmur_hash2(const void * key, size_t len, uint32_t seed);
|
||||
|
||||
UPB_INLINE uintptr_t upb_intkey(uintptr_t key) {
|
||||
return key;
|
||||
}
|
||||
|
||||
UPB_INLINE uint32_t upb_inthash(uintptr_t key) {
|
||||
return (uint32_t)key;
|
||||
}
|
||||
|
||||
static const upb_tabent *upb_getentry(const upb_table *t, uint32_t hash) {
|
||||
return t->entries + (hash & t->mask);
|
||||
}
|
||||
|
||||
UPB_INLINE bool upb_arrhas(upb_tabval key) {
|
||||
return key.val != (uint64_t)-1;
|
||||
}
|
||||
|
||||
/* Initialize and uninitialize a table, respectively. If memory allocation
|
||||
* failed, false is returned that the table is uninitialized. */
|
||||
bool upb_inttable_init2(upb_inttable *table, upb_ctype_t ctype, upb_alloc *a);
|
||||
bool upb_strtable_init2(upb_strtable *table, upb_ctype_t ctype, upb_alloc *a);
|
||||
void upb_inttable_uninit2(upb_inttable *table, upb_alloc *a);
|
||||
void upb_strtable_uninit2(upb_strtable *table, upb_alloc *a);
|
||||
|
||||
UPB_INLINE bool upb_inttable_init(upb_inttable *table, upb_ctype_t ctype) {
|
||||
return upb_inttable_init2(table, ctype, &upb_alloc_global);
|
||||
}
|
||||
|
||||
UPB_INLINE bool upb_strtable_init(upb_strtable *table, upb_ctype_t ctype) {
|
||||
return upb_strtable_init2(table, ctype, &upb_alloc_global);
|
||||
}
|
||||
|
||||
UPB_INLINE void upb_inttable_uninit(upb_inttable *table) {
|
||||
upb_inttable_uninit2(table, &upb_alloc_global);
|
||||
}
|
||||
|
||||
UPB_INLINE void upb_strtable_uninit(upb_strtable *table) {
|
||||
upb_strtable_uninit2(table, &upb_alloc_global);
|
||||
}
|
||||
|
||||
/* Returns the number of values in the table. */
|
||||
size_t upb_inttable_count(const upb_inttable *t);
|
||||
UPB_INLINE size_t upb_strtable_count(const upb_strtable *t) {
|
||||
return t->t.count;
|
||||
}
|
||||
|
||||
void upb_inttable_packedsize(const upb_inttable *t, size_t *size);
|
||||
void upb_strtable_packedsize(const upb_strtable *t, size_t *size);
|
||||
upb_inttable *upb_inttable_pack(const upb_inttable *t, void *p, size_t *ofs,
|
||||
size_t size);
|
||||
upb_strtable *upb_strtable_pack(const upb_strtable *t, void *p, size_t *ofs,
|
||||
size_t size);
|
||||
|
||||
/* Inserts the given key into the hashtable with the given value. The key must
|
||||
* not already exist in the hash table. For string tables, the key must be
|
||||
* NULL-terminated, and the table will make an internal copy of the key.
|
||||
* Inttables must not insert a value of UINTPTR_MAX.
|
||||
*
|
||||
* If a table resize was required but memory allocation failed, false is
|
||||
* returned and the table is unchanged. */
|
||||
bool upb_inttable_insert2(upb_inttable *t, uintptr_t key, upb_value val,
|
||||
upb_alloc *a);
|
||||
bool upb_strtable_insert3(upb_strtable *t, const char *key, size_t len,
|
||||
upb_value val, upb_alloc *a);
|
||||
|
||||
UPB_INLINE bool upb_inttable_insert(upb_inttable *t, uintptr_t key,
|
||||
upb_value val) {
|
||||
return upb_inttable_insert2(t, key, val, &upb_alloc_global);
|
||||
}
|
||||
|
||||
UPB_INLINE bool upb_strtable_insert2(upb_strtable *t, const char *key,
|
||||
size_t len, upb_value val) {
|
||||
return upb_strtable_insert3(t, key, len, val, &upb_alloc_global);
|
||||
}
|
||||
|
||||
/* For NULL-terminated strings. */
|
||||
UPB_INLINE bool upb_strtable_insert(upb_strtable *t, const char *key,
|
||||
upb_value val) {
|
||||
return upb_strtable_insert2(t, key, strlen(key), val);
|
||||
}
|
||||
|
||||
/* Looks up key in this table, returning "true" if the key was found.
|
||||
* If v is non-NULL, copies the value for this key into *v. */
|
||||
bool upb_inttable_lookup(const upb_inttable *t, uintptr_t key, upb_value *v);
|
||||
bool upb_strtable_lookup2(const upb_strtable *t, const char *key, size_t len,
|
||||
upb_value *v);
|
||||
|
||||
/* For NULL-terminated strings. */
|
||||
UPB_INLINE bool upb_strtable_lookup(const upb_strtable *t, const char *key,
|
||||
upb_value *v) {
|
||||
return upb_strtable_lookup2(t, key, strlen(key), v);
|
||||
}
|
||||
|
||||
/* Removes an item from the table. Returns true if the remove was successful,
|
||||
* and stores the removed item in *val if non-NULL. */
|
||||
bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val);
|
||||
bool upb_strtable_remove3(upb_strtable *t, const char *key, size_t len,
|
||||
upb_value *val, upb_alloc *alloc);
|
||||
|
||||
UPB_INLINE bool upb_strtable_remove2(upb_strtable *t, const char *key,
|
||||
size_t len, upb_value *val) {
|
||||
return upb_strtable_remove3(t, key, len, val, &upb_alloc_global);
|
||||
}
|
||||
|
||||
/* For NULL-terminated strings. */
|
||||
UPB_INLINE bool upb_strtable_remove(upb_strtable *t, const char *key,
|
||||
upb_value *v) {
|
||||
return upb_strtable_remove2(t, key, strlen(key), v);
|
||||
}
|
||||
|
||||
/* Updates an existing entry in an inttable. If the entry does not exist,
|
||||
* returns false and does nothing. Unlike insert/remove, this does not
|
||||
* invalidate iterators. */
|
||||
bool upb_inttable_replace(upb_inttable *t, uintptr_t key, upb_value val);
|
||||
|
||||
/* Handy routines for treating an inttable like a stack. May not be mixed with
|
||||
* other insert/remove calls. */
|
||||
bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a);
|
||||
upb_value upb_inttable_pop(upb_inttable *t);
|
||||
|
||||
UPB_INLINE bool upb_inttable_push(upb_inttable *t, upb_value val) {
|
||||
return upb_inttable_push2(t, val, &upb_alloc_global);
|
||||
}
|
||||
|
||||
/* Convenience routines for inttables with pointer keys. */
|
||||
bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val,
|
||||
upb_alloc *a);
|
||||
bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val);
|
||||
bool upb_inttable_lookupptr(
|
||||
const upb_inttable *t, const void *key, upb_value *val);
|
||||
|
||||
UPB_INLINE bool upb_inttable_insertptr(upb_inttable *t, const void *key,
|
||||
upb_value val) {
|
||||
return upb_inttable_insertptr2(t, key, val, &upb_alloc_global);
|
||||
}
|
||||
|
||||
/* Optimizes the table for the current set of entries, for both memory use and
|
||||
* lookup time. Client should call this after all entries have been inserted;
|
||||
* inserting more entries is legal, but will likely require a table resize. */
|
||||
void upb_inttable_compact2(upb_inttable *t, upb_alloc *a);
|
||||
|
||||
UPB_INLINE void upb_inttable_compact(upb_inttable *t) {
|
||||
upb_inttable_compact2(t, &upb_alloc_global);
|
||||
}
|
||||
|
||||
/* A special-case inlinable version of the lookup routine for 32-bit
|
||||
* integers. */
|
||||
UPB_INLINE bool upb_inttable_lookup32(const upb_inttable *t, uint32_t key,
|
||||
upb_value *v) {
|
||||
*v = upb_value_int32(0); /* Silence compiler warnings. */
|
||||
if (key < t->array_size) {
|
||||
upb_tabval arrval = t->array[key];
|
||||
if (upb_arrhas(arrval)) {
|
||||
_upb_value_setval(v, arrval.val, t->t.ctype);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const upb_tabent *e;
|
||||
if (t->t.entries == NULL) return false;
|
||||
for (e = upb_getentry(&t->t, upb_inthash(key)); true; e = e->next) {
|
||||
if ((uint32_t)e->key == key) {
|
||||
_upb_value_setval(v, e->val.val, t->t.ctype);
|
||||
return true;
|
||||
}
|
||||
if (e->next == NULL) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Exposed for testing only. */
|
||||
bool upb_strtable_resize(upb_strtable *t, size_t size_lg2, upb_alloc *a);
|
||||
|
||||
/* Iterators ******************************************************************/
|
||||
|
||||
/* Iterators for int and string tables. We are subject to some kind of unusual
|
||||
* design constraints:
|
||||
*
|
||||
* For high-level languages:
|
||||
* - we must be able to guarantee that we don't crash or corrupt memory even if
|
||||
* the program accesses an invalidated iterator.
|
||||
*
|
||||
* For C++11 range-based for:
|
||||
* - iterators must be copyable
|
||||
* - iterators must be comparable
|
||||
* - it must be possible to construct an "end" value.
|
||||
*
|
||||
* Iteration order is undefined.
|
||||
*
|
||||
* Modifying the table invalidates iterators. upb_{str,int}table_done() is
|
||||
* guaranteed to work even on an invalidated iterator, as long as the table it
|
||||
* is iterating over has not been freed. Calling next() or accessing data from
|
||||
* an invalidated iterator yields unspecified elements from the table, but it is
|
||||
* guaranteed not to crash and to return real table elements (except when done()
|
||||
* is true). */
|
||||
|
||||
|
||||
/* upb_strtable_iter **********************************************************/
|
||||
|
||||
/* upb_strtable_iter i;
|
||||
* upb_strtable_begin(&i, t);
|
||||
* for(; !upb_strtable_done(&i); upb_strtable_next(&i)) {
|
||||
* const char *key = upb_strtable_iter_key(&i);
|
||||
* const upb_value val = upb_strtable_iter_value(&i);
|
||||
* // ...
|
||||
* }
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
const upb_strtable *t;
|
||||
size_t index;
|
||||
} upb_strtable_iter;
|
||||
|
||||
void upb_strtable_begin(upb_strtable_iter *i, const upb_strtable *t);
|
||||
void upb_strtable_next(upb_strtable_iter *i);
|
||||
bool upb_strtable_done(const upb_strtable_iter *i);
|
||||
const char *upb_strtable_iter_key(const upb_strtable_iter *i);
|
||||
size_t upb_strtable_iter_keylength(const upb_strtable_iter *i);
|
||||
upb_value upb_strtable_iter_value(const upb_strtable_iter *i);
|
||||
void upb_strtable_iter_setdone(upb_strtable_iter *i);
|
||||
bool upb_strtable_iter_isequal(const upb_strtable_iter *i1,
|
||||
const upb_strtable_iter *i2);
|
||||
|
||||
|
||||
/* upb_inttable_iter **********************************************************/
|
||||
|
||||
/* upb_inttable_iter i;
|
||||
* upb_inttable_begin(&i, t);
|
||||
* for(; !upb_inttable_done(&i); upb_inttable_next(&i)) {
|
||||
* uintptr_t key = upb_inttable_iter_key(&i);
|
||||
* upb_value val = upb_inttable_iter_value(&i);
|
||||
* // ...
|
||||
* }
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
const upb_inttable *t;
|
||||
size_t index;
|
||||
bool array_part;
|
||||
} upb_inttable_iter;
|
||||
|
||||
void upb_inttable_begin(upb_inttable_iter *i, const upb_inttable *t);
|
||||
void upb_inttable_next(upb_inttable_iter *i);
|
||||
bool upb_inttable_done(const upb_inttable_iter *i);
|
||||
uintptr_t upb_inttable_iter_key(const upb_inttable_iter *i);
|
||||
upb_value upb_inttable_iter_value(const upb_inttable_iter *i);
|
||||
void upb_inttable_iter_setdone(upb_inttable_iter *i);
|
||||
bool upb_inttable_iter_isequal(const upb_inttable_iter *i1,
|
||||
const upb_inttable_iter *i2);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/port_undef.inc"
|
||||
#else
|
||||
#include "upb/port_undef.inc"
|
||||
#endif
|
||||
|
||||
#endif /* UPB_TABLE_H_ */
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/upb.h"
|
||||
#else
|
||||
#include "upb/upb.h"
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/port_def.inc"
|
||||
#else
|
||||
#include "upb/port_def.inc"
|
||||
#endif
|
||||
|
||||
/* Guarantee null-termination and provide ellipsis truncation.
|
||||
* It may be tempting to "optimize" this by initializing these final
|
||||
* four bytes up-front and then being careful never to overwrite them,
|
||||
* this is safer and simpler. */
|
||||
static void nullz(upb_status *status) {
|
||||
const char *ellipsis = "...";
|
||||
size_t len = strlen(ellipsis);
|
||||
UPB_ASSERT(sizeof(status->msg) > len);
|
||||
memcpy(status->msg + sizeof(status->msg) - len, ellipsis, len);
|
||||
}
|
||||
|
||||
/* upb_status *****************************************************************/
|
||||
|
||||
void upb_status_clear(upb_status *status) {
|
||||
if (!status) return;
|
||||
status->ok = true;
|
||||
status->msg[0] = '\0';
|
||||
}
|
||||
|
||||
bool upb_ok(const upb_status *status) { return status->ok; }
|
||||
|
||||
const char *upb_status_errmsg(const upb_status *status) { return status->msg; }
|
||||
|
||||
void upb_status_seterrmsg(upb_status *status, const char *msg) {
|
||||
if (!status) return;
|
||||
status->ok = false;
|
||||
strncpy(status->msg, msg, sizeof(status->msg));
|
||||
nullz(status);
|
||||
}
|
||||
|
||||
void upb_status_seterrf(upb_status *status, const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
upb_status_vseterrf(status, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args) {
|
||||
if (!status) return;
|
||||
status->ok = false;
|
||||
_upb_vsnprintf(status->msg, sizeof(status->msg), fmt, args);
|
||||
nullz(status);
|
||||
}
|
||||
|
||||
/* upb_alloc ******************************************************************/
|
||||
|
||||
static void *upb_global_allocfunc(upb_alloc *alloc, void *ptr, size_t oldsize,
|
||||
size_t size) {
|
||||
UPB_UNUSED(alloc);
|
||||
UPB_UNUSED(oldsize);
|
||||
if (size == 0) {
|
||||
free(ptr);
|
||||
return NULL;
|
||||
} else {
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
}
|
||||
|
||||
upb_alloc upb_alloc_global = {&upb_global_allocfunc};
|
||||
|
||||
/* upb_arena ******************************************************************/
|
||||
|
||||
/* Be conservative and choose 16 in case anyone is using SSE. */
|
||||
static const size_t maxalign = 16;
|
||||
|
||||
static size_t align_up_max(size_t size) {
|
||||
return ((size + maxalign - 1) / maxalign) * maxalign;
|
||||
}
|
||||
|
||||
struct upb_arena {
|
||||
/* We implement the allocator interface.
|
||||
* This must be the first member of upb_arena! */
|
||||
upb_alloc alloc;
|
||||
|
||||
/* Allocator to allocate arena blocks. We are responsible for freeing these
|
||||
* when we are destroyed. */
|
||||
upb_alloc *block_alloc;
|
||||
|
||||
size_t bytes_allocated;
|
||||
size_t next_block_size;
|
||||
size_t max_block_size;
|
||||
|
||||
/* Linked list of blocks. Points to an arena_block, defined in env.c */
|
||||
void *block_head;
|
||||
|
||||
/* Cleanup entries. Pointer to a cleanup_ent, defined in env.c */
|
||||
void *cleanup_head;
|
||||
};
|
||||
|
||||
typedef struct mem_block {
|
||||
struct mem_block *next;
|
||||
size_t size;
|
||||
size_t used;
|
||||
bool owned;
|
||||
/* Data follows. */
|
||||
} mem_block;
|
||||
|
||||
typedef struct cleanup_ent {
|
||||
struct cleanup_ent *next;
|
||||
upb_cleanup_func *cleanup;
|
||||
void *ud;
|
||||
} cleanup_ent;
|
||||
|
||||
static void upb_arena_addblock(upb_arena *a, void *ptr, size_t size,
|
||||
bool owned) {
|
||||
mem_block *block = ptr;
|
||||
|
||||
block->next = a->block_head;
|
||||
block->size = size;
|
||||
block->used = align_up_max(sizeof(mem_block));
|
||||
block->owned = owned;
|
||||
|
||||
a->block_head = block;
|
||||
|
||||
/* TODO(haberman): ASAN poison. */
|
||||
}
|
||||
|
||||
static mem_block *upb_arena_allocblock(upb_arena *a, size_t size) {
|
||||
size_t block_size = UPB_MAX(size, a->next_block_size) + sizeof(mem_block);
|
||||
mem_block *block = upb_malloc(a->block_alloc, block_size);
|
||||
|
||||
if (!block) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
upb_arena_addblock(a, block, block_size, true);
|
||||
a->next_block_size = UPB_MIN(block_size * 2, a->max_block_size);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
static void *upb_arena_doalloc(upb_alloc *alloc, void *ptr, size_t oldsize,
|
||||
size_t size) {
|
||||
upb_arena *a = (upb_arena*)alloc; /* upb_alloc is initial member. */
|
||||
mem_block *block = a->block_head;
|
||||
void *ret;
|
||||
|
||||
if (size == 0) {
|
||||
return NULL; /* We are an arena, don't need individual frees. */
|
||||
}
|
||||
|
||||
size = align_up_max(size);
|
||||
|
||||
/* TODO(haberman): special-case if this is a realloc of the last alloc? */
|
||||
|
||||
if (!block || block->size - block->used < size) {
|
||||
/* Slow path: have to allocate a new block. */
|
||||
block = upb_arena_allocblock(a, size);
|
||||
|
||||
if (!block) {
|
||||
return NULL; /* Out of memory. */
|
||||
}
|
||||
}
|
||||
|
||||
ret = (char*)block + block->used;
|
||||
block->used += size;
|
||||
|
||||
if (oldsize > 0) {
|
||||
memcpy(ret, ptr, oldsize); /* Preserve existing data. */
|
||||
}
|
||||
|
||||
/* TODO(haberman): ASAN unpoison. */
|
||||
|
||||
a->bytes_allocated += size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Public Arena API ***********************************************************/
|
||||
|
||||
#define upb_alignof(type) offsetof (struct { char c; type member; }, member)
|
||||
|
||||
upb_arena *upb_arena_init(void *mem, size_t n, upb_alloc *alloc) {
|
||||
const size_t first_block_overhead = sizeof(upb_arena) + sizeof(mem_block);
|
||||
upb_arena *a;
|
||||
bool owned = false;
|
||||
|
||||
/* Round block size down to alignof(*a) since we will allocate the arena
|
||||
* itself at the end. */
|
||||
n &= ~(upb_alignof(upb_arena) - 1);
|
||||
|
||||
if (n < first_block_overhead) {
|
||||
/* We need to malloc the initial block. */
|
||||
n = first_block_overhead + 256;
|
||||
owned = true;
|
||||
if (!alloc || !(mem = upb_malloc(alloc, n))) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
a = (void*)((char*)mem + n - sizeof(*a));
|
||||
n -= sizeof(*a);
|
||||
|
||||
a->alloc.func = &upb_arena_doalloc;
|
||||
a->block_alloc = &upb_alloc_global;
|
||||
a->bytes_allocated = 0;
|
||||
a->next_block_size = 256;
|
||||
a->max_block_size = 16384;
|
||||
a->cleanup_head = NULL;
|
||||
a->block_head = NULL;
|
||||
a->block_alloc = alloc;
|
||||
|
||||
upb_arena_addblock(a, mem, n, owned);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
#undef upb_alignof
|
||||
|
||||
void upb_arena_free(upb_arena *a) {
|
||||
cleanup_ent *ent = a->cleanup_head;
|
||||
mem_block *block = a->block_head;
|
||||
|
||||
while (ent) {
|
||||
ent->cleanup(ent->ud);
|
||||
ent = ent->next;
|
||||
}
|
||||
|
||||
/* Must do this after running cleanup functions, because this will delete
|
||||
* the memory we store our cleanup entries in! */
|
||||
while (block) {
|
||||
/* Load first since we are deleting block. */
|
||||
mem_block *next = block->next;
|
||||
|
||||
if (block->owned) {
|
||||
upb_free(a->block_alloc, block);
|
||||
}
|
||||
|
||||
block = next;
|
||||
}
|
||||
}
|
||||
|
||||
bool upb_arena_addcleanup(upb_arena *a, void *ud, upb_cleanup_func *func) {
|
||||
cleanup_ent *ent = upb_malloc(&a->alloc, sizeof(cleanup_ent));
|
||||
if (!ent) {
|
||||
return false; /* Out of memory. */
|
||||
}
|
||||
|
||||
ent->cleanup = func;
|
||||
ent->ud = ud;
|
||||
ent->next = a->cleanup_head;
|
||||
a->cleanup_head = ent;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t upb_arena_bytesallocated(const upb_arena *a) {
|
||||
return a->bytes_allocated;
|
||||
}
|
||||
+372
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
** This file contains shared definitions that are widely used across upb.
|
||||
**
|
||||
** This is a mixed C/C++ interface that offers a full API to both languages.
|
||||
** See the top-level README for more information.
|
||||
*/
|
||||
|
||||
#ifndef UPB_H_
|
||||
#define UPB_H_
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <memory>
|
||||
namespace upb {
|
||||
class Arena;
|
||||
class Status;
|
||||
template <int N> class InlinedArena;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/port_def.inc"
|
||||
#else
|
||||
#include "upb/port_def.inc"
|
||||
#endif
|
||||
|
||||
/* upb_status *****************************************************************/
|
||||
|
||||
/* upb_status represents a success or failure status and error message.
|
||||
* It owns no resources and allocates no memory, so it should work
|
||||
* even in OOM situations. */
|
||||
|
||||
/* The maximum length of an error message before it will get truncated. */
|
||||
#define UPB_STATUS_MAX_MESSAGE 127
|
||||
|
||||
typedef struct {
|
||||
bool ok;
|
||||
char msg[UPB_STATUS_MAX_MESSAGE]; /* Error message; NULL-terminated. */
|
||||
} upb_status;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
const char *upb_status_errmsg(const upb_status *status);
|
||||
bool upb_ok(const upb_status *status);
|
||||
|
||||
/* Any of the functions that write to a status object allow status to be NULL,
|
||||
* to support use cases where the function's caller does not care about the
|
||||
* status message. */
|
||||
void upb_status_clear(upb_status *status);
|
||||
void upb_status_seterrmsg(upb_status *status, const char *msg);
|
||||
void upb_status_seterrf(upb_status *status, const char *fmt, ...);
|
||||
void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args);
|
||||
|
||||
UPB_INLINE void upb_status_setoom(upb_status *status) {
|
||||
upb_status_seterrmsg(status, "out of memory");
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
class upb::Status {
|
||||
public:
|
||||
Status() { upb_status_clear(&status_); }
|
||||
|
||||
upb_status* ptr() { return &status_; }
|
||||
|
||||
/* Returns true if there is no error. */
|
||||
bool ok() const { return upb_ok(&status_); }
|
||||
|
||||
/* Guaranteed to be NULL-terminated. */
|
||||
const char *error_message() const { return upb_status_errmsg(&status_); }
|
||||
|
||||
/* The error message will be truncated if it is longer than
|
||||
* UPB_STATUS_MAX_MESSAGE-4. */
|
||||
void SetErrorMessage(const char *msg) { upb_status_seterrmsg(&status_, msg); }
|
||||
void SetFormattedErrorMessage(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
upb_status_vseterrf(&status_, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/* Resets the status to a successful state with no message. */
|
||||
void Clear() { upb_status_clear(&status_); }
|
||||
|
||||
private:
|
||||
upb_status status_;
|
||||
};
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/** upb_strview ************************************************************/
|
||||
|
||||
typedef struct {
|
||||
const char *data;
|
||||
size_t size;
|
||||
} upb_strview;
|
||||
|
||||
UPB_INLINE upb_strview upb_strview_make(const char *data, size_t size) {
|
||||
upb_strview ret;
|
||||
ret.data = data;
|
||||
ret.size = size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
UPB_INLINE upb_strview upb_strview_makez(const char *data) {
|
||||
return upb_strview_make(data, strlen(data));
|
||||
}
|
||||
|
||||
UPB_INLINE bool upb_strview_eql(upb_strview a, upb_strview b) {
|
||||
return a.size == b.size && memcmp(a.data, b.data, a.size) == 0;
|
||||
}
|
||||
|
||||
#define UPB_STRVIEW_INIT(ptr, len) {ptr, len}
|
||||
|
||||
#define UPB_STRVIEW_FORMAT "%.*s"
|
||||
#define UPB_STRVIEW_ARGS(view) (int)(view).size, (view).data
|
||||
|
||||
/** upb_alloc *****************************************************************/
|
||||
|
||||
/* A upb_alloc is a possibly-stateful allocator object.
|
||||
*
|
||||
* It could either be an arena allocator (which doesn't require individual
|
||||
* free() calls) or a regular malloc() (which does). The client must therefore
|
||||
* free memory unless it knows that the allocator is an arena allocator. */
|
||||
|
||||
struct upb_alloc;
|
||||
typedef struct upb_alloc upb_alloc;
|
||||
|
||||
/* A malloc()/free() function.
|
||||
* If "size" is 0 then the function acts like free(), otherwise it acts like
|
||||
* realloc(). Only "oldsize" bytes from a previous allocation are preserved. */
|
||||
typedef void *upb_alloc_func(upb_alloc *alloc, void *ptr, size_t oldsize,
|
||||
size_t size);
|
||||
|
||||
struct upb_alloc {
|
||||
upb_alloc_func *func;
|
||||
};
|
||||
|
||||
UPB_INLINE void *upb_malloc(upb_alloc *alloc, size_t size) {
|
||||
UPB_ASSERT(alloc);
|
||||
return alloc->func(alloc, NULL, 0, size);
|
||||
}
|
||||
|
||||
UPB_INLINE void *upb_realloc(upb_alloc *alloc, void *ptr, size_t oldsize,
|
||||
size_t size) {
|
||||
UPB_ASSERT(alloc);
|
||||
return alloc->func(alloc, ptr, oldsize, size);
|
||||
}
|
||||
|
||||
UPB_INLINE void upb_free(upb_alloc *alloc, void *ptr) {
|
||||
assert(alloc);
|
||||
alloc->func(alloc, ptr, 0, 0);
|
||||
}
|
||||
|
||||
/* The global allocator used by upb. Uses the standard malloc()/free(). */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern upb_alloc upb_alloc_global;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
/* Functions that hard-code the global malloc.
|
||||
*
|
||||
* We still get benefit because we can put custom logic into our global
|
||||
* allocator, like injecting out-of-memory faults in debug/testing builds. */
|
||||
|
||||
UPB_INLINE void *upb_gmalloc(size_t size) {
|
||||
return upb_malloc(&upb_alloc_global, size);
|
||||
}
|
||||
|
||||
UPB_INLINE void *upb_grealloc(void *ptr, size_t oldsize, size_t size) {
|
||||
return upb_realloc(&upb_alloc_global, ptr, oldsize, size);
|
||||
}
|
||||
|
||||
UPB_INLINE void upb_gfree(void *ptr) {
|
||||
upb_free(&upb_alloc_global, ptr);
|
||||
}
|
||||
|
||||
/* upb_arena ******************************************************************/
|
||||
|
||||
/* upb_arena is a specific allocator implementation that uses arena allocation.
|
||||
* The user provides an allocator that will be used to allocate the underlying
|
||||
* arena blocks. Arenas by nature do not require the individual allocations
|
||||
* to be freed. However the Arena does allow users to register cleanup
|
||||
* functions that will run when the arena is destroyed.
|
||||
*
|
||||
* A upb_arena is *not* thread-safe.
|
||||
*
|
||||
* You could write a thread-safe arena allocator that satisfies the
|
||||
* upb_alloc interface, but it would not be as efficient for the
|
||||
* single-threaded case. */
|
||||
|
||||
typedef void upb_cleanup_func(void *ud);
|
||||
|
||||
struct upb_arena;
|
||||
typedef struct upb_arena upb_arena;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Creates an arena from the given initial block (if any -- n may be 0).
|
||||
* Additional blocks will be allocated from |alloc|. If |alloc| is NULL, this
|
||||
* is a fixed-size arena and cannot grow. */
|
||||
upb_arena *upb_arena_init(void *mem, size_t n, upb_alloc *alloc);
|
||||
void upb_arena_free(upb_arena *a);
|
||||
bool upb_arena_addcleanup(upb_arena *a, void *ud, upb_cleanup_func *func);
|
||||
size_t upb_arena_bytesallocated(const upb_arena *a);
|
||||
|
||||
UPB_INLINE upb_alloc *upb_arena_alloc(upb_arena *a) { return (upb_alloc*)a; }
|
||||
|
||||
/* Convenience wrappers around upb_alloc functions. */
|
||||
|
||||
UPB_INLINE void *upb_arena_malloc(upb_arena *a, size_t size) {
|
||||
return upb_malloc(upb_arena_alloc(a), size);
|
||||
}
|
||||
|
||||
UPB_INLINE void *upb_arena_realloc(upb_arena *a, void *ptr, size_t oldsize,
|
||||
size_t size) {
|
||||
return upb_realloc(upb_arena_alloc(a), ptr, oldsize, size);
|
||||
}
|
||||
|
||||
UPB_INLINE upb_arena *upb_arena_new(void) {
|
||||
return upb_arena_init(NULL, 0, &upb_alloc_global);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
class upb::Arena {
|
||||
public:
|
||||
/* A simple arena with no initial memory block and the default allocator. */
|
||||
Arena() : ptr_(upb_arena_new(), upb_arena_free) {}
|
||||
|
||||
upb_arena* ptr() { return ptr_.get(); }
|
||||
|
||||
/* Allows this arena to be used as a generic allocator.
|
||||
*
|
||||
* The arena does not need free() calls so when using Arena as an allocator
|
||||
* it is safe to skip them. However they are no-ops so there is no harm in
|
||||
* calling free() either. */
|
||||
upb_alloc *allocator() { return upb_arena_alloc(ptr_.get()); }
|
||||
|
||||
/* Add a cleanup function to run when the arena is destroyed.
|
||||
* Returns false on out-of-memory. */
|
||||
bool AddCleanup(void *ud, upb_cleanup_func* func) {
|
||||
return upb_arena_addcleanup(ptr_.get(), ud, func);
|
||||
}
|
||||
|
||||
/* Total number of bytes that have been allocated. It is undefined what
|
||||
* Realloc() does to &arena_ counter. */
|
||||
size_t BytesAllocated() const { return upb_arena_bytesallocated(ptr_.get()); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<upb_arena, decltype(&upb_arena_free)> ptr_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/* upb::InlinedArena **********************************************************/
|
||||
|
||||
/* upb::InlinedArena seeds the arenas with a predefined amount of memory. No
|
||||
* heap memory will be allocated until the initial block is exceeded.
|
||||
*
|
||||
* These types only exist in C++ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
template <int N> class upb::InlinedArena : public upb::Arena {
|
||||
public:
|
||||
InlinedArena() : ptr_(upb_arena_new(&initial_block_, N, &upb_alloc_global)) {}
|
||||
|
||||
upb_arena* ptr() { return ptr_.get(); }
|
||||
|
||||
private:
|
||||
InlinedArena(const InlinedArena*) = delete;
|
||||
InlinedArena& operator=(const InlinedArena*) = delete;
|
||||
|
||||
std::unique_ptr<upb_arena, decltype(&upb_arena_free)> ptr_;
|
||||
char initial_block_[N];
|
||||
};
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* Constants ******************************************************************/
|
||||
|
||||
/* Generic function type. */
|
||||
typedef void upb_func(void);
|
||||
|
||||
/* A list of types as they are encoded on-the-wire. */
|
||||
typedef enum {
|
||||
UPB_WIRE_TYPE_VARINT = 0,
|
||||
UPB_WIRE_TYPE_64BIT = 1,
|
||||
UPB_WIRE_TYPE_DELIMITED = 2,
|
||||
UPB_WIRE_TYPE_START_GROUP = 3,
|
||||
UPB_WIRE_TYPE_END_GROUP = 4,
|
||||
UPB_WIRE_TYPE_32BIT = 5
|
||||
} upb_wiretype_t;
|
||||
|
||||
/* The types a field can have. Note that this list is not identical to the
|
||||
* types defined in descriptor.proto, which gives INT32 and SINT32 separate
|
||||
* types (we distinguish the two with the "integer encoding" enum below). */
|
||||
typedef enum {
|
||||
/* Types stored in 1 byte. */
|
||||
UPB_TYPE_BOOL = 1,
|
||||
/* Types stored in 4 bytes. */
|
||||
UPB_TYPE_FLOAT = 2,
|
||||
UPB_TYPE_INT32 = 3,
|
||||
UPB_TYPE_UINT32 = 4,
|
||||
UPB_TYPE_ENUM = 5, /* Enum values are int32. */
|
||||
/* Types stored as pointers (probably 4 or 8 bytes). */
|
||||
UPB_TYPE_STRING = 6,
|
||||
UPB_TYPE_BYTES = 7,
|
||||
UPB_TYPE_MESSAGE = 8,
|
||||
/* Types stored as 8 bytes. */
|
||||
UPB_TYPE_DOUBLE = 9,
|
||||
UPB_TYPE_INT64 = 10,
|
||||
UPB_TYPE_UINT64 = 11
|
||||
} upb_fieldtype_t;
|
||||
|
||||
/* The repeated-ness of each field; this matches descriptor.proto. */
|
||||
typedef enum {
|
||||
UPB_LABEL_OPTIONAL = 1,
|
||||
UPB_LABEL_REQUIRED = 2,
|
||||
UPB_LABEL_REPEATED = 3
|
||||
} upb_label_t;
|
||||
|
||||
/* Descriptor types, as defined in descriptor.proto. */
|
||||
typedef enum {
|
||||
UPB_DESCRIPTOR_TYPE_DOUBLE = 1,
|
||||
UPB_DESCRIPTOR_TYPE_FLOAT = 2,
|
||||
UPB_DESCRIPTOR_TYPE_INT64 = 3,
|
||||
UPB_DESCRIPTOR_TYPE_UINT64 = 4,
|
||||
UPB_DESCRIPTOR_TYPE_INT32 = 5,
|
||||
UPB_DESCRIPTOR_TYPE_FIXED64 = 6,
|
||||
UPB_DESCRIPTOR_TYPE_FIXED32 = 7,
|
||||
UPB_DESCRIPTOR_TYPE_BOOL = 8,
|
||||
UPB_DESCRIPTOR_TYPE_STRING = 9,
|
||||
UPB_DESCRIPTOR_TYPE_GROUP = 10,
|
||||
UPB_DESCRIPTOR_TYPE_MESSAGE = 11,
|
||||
UPB_DESCRIPTOR_TYPE_BYTES = 12,
|
||||
UPB_DESCRIPTOR_TYPE_UINT32 = 13,
|
||||
UPB_DESCRIPTOR_TYPE_ENUM = 14,
|
||||
UPB_DESCRIPTOR_TYPE_SFIXED32 = 15,
|
||||
UPB_DESCRIPTOR_TYPE_SFIXED64 = 16,
|
||||
UPB_DESCRIPTOR_TYPE_SINT32 = 17,
|
||||
UPB_DESCRIPTOR_TYPE_SINT64 = 18
|
||||
} upb_descriptortype_t;
|
||||
|
||||
extern const uint8_t upb_desctype_to_fieldtype[];
|
||||
|
||||
#if COCOAPODS==1
|
||||
#include "third_party/upb/upb/port_undef.inc"
|
||||
#else
|
||||
#include "upb/port_undef.inc"
|
||||
#endif
|
||||
|
||||
#endif /* UPB_H_ */
|
||||
Reference in New Issue
Block a user