summaryrefslogtreecommitdiff
path: root/upb/pb
diff options
context:
space:
mode:
Diffstat (limited to 'upb/pb')
-rw-r--r--upb/pb/decoder.c252
-rw-r--r--upb/pb/decoder.h61
-rw-r--r--upb/pb/decoder_x64.dasc646
-rw-r--r--upb/pb/glue.c14
-rw-r--r--upb/pb/glue.h26
-rw-r--r--upb/pb/textprinter.c217
-rw-r--r--upb/pb/textprinter.h5
-rw-r--r--upb/pb/varint.c59
-rw-r--r--upb/pb/varint.h88
9 files changed, 820 insertions, 548 deletions
diff --git a/upb/pb/decoder.c b/upb/pb/decoder.c
index 30f7c65..065c495 100644
--- a/upb/pb/decoder.c
+++ b/upb/pb/decoder.c
@@ -5,17 +5,13 @@
* Author: Josh Haberman <jhaberman@gmail.com>
*/
+#include <inttypes.h>
#include <stddef.h>
#include <stdlib.h>
#include "upb/bytestream.h"
-#include "upb/msg.h"
#include "upb/pb/decoder.h"
#include "upb/pb/varint.h"
-#ifndef UINT32_MAX
-#define UINT32_MAX 0xffffffff
-#endif
-
typedef struct {
uint8_t native_wire_type;
bool is_numeric;
@@ -62,11 +58,12 @@ static const upb_decoder_typeinfo upb_decoder_types[] = {
#include "upb/pb/decoder_x64.h"
#endif
-upb_decoderplan *upb_decoderplan_new(upb_handlers *h, bool allowjit) {
+upb_decoderplan *upb_decoderplan_new(const upb_handlers *h, bool allowjit) {
+ UPB_UNUSED(allowjit);
upb_decoderplan *p = malloc(sizeof(*p));
+ assert(upb_handlers_isfrozen(h));
p->handlers = h;
- upb_handlers_ref(h);
- h->should_jit = allowjit;
+ upb_handlers_ref(h, p);
#ifdef UPB_USE_JIT_X64
p->jit_code = NULL;
if (allowjit) upb_decoderplan_makejit(p);
@@ -76,7 +73,7 @@ upb_decoderplan *upb_decoderplan_new(upb_handlers *h, bool allowjit) {
void upb_decoderplan_unref(upb_decoderplan *p) {
// TODO: make truly refcounted.
- upb_handlers_unref(p->handlers);
+ upb_handlers_unref(p->handlers, p);
#ifdef UPB_USE_JIT_X64
if (p->jit_code) upb_decoderplan_freejit(p);
#endif
@@ -100,8 +97,8 @@ bool upb_decoderplan_hasjitcode(upb_decoderplan *p) {
// configuration. But emperically on a Core i7, performance increases 30-50%
// with these annotations. Every instance where these appear, gcc 4.2.1 made
// the wrong decision and degraded performance in benchmarks.
-#define FORCEINLINE static __attribute__((__always_inline__))
-#define NOINLINE static __attribute__((__noinline__))
+#define FORCEINLINE static inline __attribute__((always_inline))
+#define NOINLINE static __attribute__((noinline))
UPB_NORETURN static void upb_decoder_exitjmp(upb_decoder *d) {
// Resumable decoder would back out to completed_ptr (and possibly get a
@@ -141,14 +138,23 @@ uint64_t upb_decoder_bufendofs(upb_decoder *d) {
return d->bufstart_ofs + (d->end - d->buf);
}
+static bool upb_decoder_islegalend(upb_decoder *d) {
+ if (d->top == d->stack) return true;
+ if (d->top - 1 == d->stack &&
+ d->top->is_sequence && !d->top->is_packed) return true;
+ return false;
+}
+
+// Calculates derived values that we cache for speed. These reflect a
+// combination of the current buffer and the stack, so must be called whenever
+// either is updated.
static void upb_decoder_setmsgend(upb_decoder *d) {
- upb_dispatcher_frame *f = d->dispatcher.top;
+ upb_decoder_frame *f = d->top;
size_t delimlen = f->end_ofs - d->bufstart_ofs;
size_t buflen = d->end - d->buf;
d->delim_end = (f->end_ofs != UPB_NONDELIMITED && delimlen <= buflen) ?
d->buf + delimlen : NULL; // NULL if not in this buf.
d->top_is_packed = f->is_packed;
- d->dispatch_table = &d->dispatcher.msgent->fieldtab;
}
static void upb_decoder_skiptonewbuf(upb_decoder *d, uint64_t ofs) {
@@ -201,11 +207,11 @@ static void upb_pullbuf(upb_decoder *d) {
if (!upb_trypullbuf(d)) upb_decoder_abortjmp(d, "Unexpected EOF");
}
-void upb_decoder_checkpoint(upb_decoder *d) {
+static void upb_decoder_checkpoint(upb_decoder *d) {
upb_byteregion_discard(d->input, upb_decoder_offset(d));
}
-void upb_decoder_discardto(upb_decoder *d, uint64_t ofs) {
+static void upb_decoder_discardto(upb_decoder *d, uint64_t ofs) {
if (ofs <= upb_decoder_bufendofs(d)) {
upb_decoder_advance(d, ofs - upb_decoder_offset(d));
} else {
@@ -214,7 +220,7 @@ void upb_decoder_discardto(upb_decoder *d, uint64_t ofs) {
upb_decoder_checkpoint(d);
}
-void upb_decoder_discard(upb_decoder *d, size_t bytes) {
+static void upb_decoder_discard(upb_decoder *d, size_t bytes) {
upb_decoder_discardto(d, upb_decoder_offset(d) + bytes);
}
@@ -259,7 +265,7 @@ done:
// Returns true on success or false if we've hit a valid EOF.
FORCEINLINE bool upb_trydecode_varint32(upb_decoder *d, uint32_t *val) {
if (upb_decoder_bufleft(d) == 0 &&
- upb_dispatcher_islegalend(&d->dispatcher) &&
+ upb_decoder_islegalend(d) &&
!upb_trypullbuf(d)) {
return false;
}
@@ -319,21 +325,45 @@ FORCEINLINE uint64_t upb_decode_fixed64(upb_decoder *d) {
return u64; // TODO: proper byte swapping for big-endian machines.
}
-INLINE upb_byteregion *upb_decode_string(upb_decoder *d) {
- uint32_t strlen = upb_decode_varint32(d);
- uint64_t offset = upb_decoder_offset(d);
- if (offset + strlen > upb_byteregion_endofs(d->input))
- upb_decoder_abortjmp(d, "Unexpected EOF");
- upb_byteregion_reset(&d->str_byteregion, d->input, offset, strlen);
- // Could make it an option on the callback whether we fetchall() first or not.
- if (upb_byteregion_fetchall(&d->str_byteregion) != UPB_BYTE_OK)
- upb_decoder_abortjmp(d, "Couldn't fetchall() on string.");
- upb_decoder_discardto(d, offset + strlen);
- return &d->str_byteregion;
+INLINE void upb_push_msg(upb_decoder *d, const upb_fielddef *f, uint64_t end) {
+ upb_decoder_frame *fr = d->top + 1;
+ if (!upb_sink_startsubmsg(&d->sink, f) || fr > d->limit) {
+ upb_decoder_abortjmp(d, "Nesting too deep.");
+ }
+ fr->f = f;
+ fr->is_sequence = false;
+ fr->is_packed = false;
+ fr->end_ofs = end;
+ fr->group_fieldnum = end == UPB_NONDELIMITED ?
+ (int32_t)upb_fielddef_number(f) : -1;
+ d->top = fr;
+ upb_decoder_setmsgend(d);
}
-INLINE void upb_push_msg(upb_decoder *d, upb_fhandlers *f, uint64_t end) {
- upb_dispatch_startsubmsg(&d->dispatcher, f)->end_ofs = end;
+INLINE void upb_push_seq(upb_decoder *d, const upb_fielddef *f, bool packed,
+ uint64_t end_ofs) {
+ upb_decoder_frame *fr = d->top + 1;
+ if (!upb_sink_startseq(&d->sink, f) || fr > d->limit) {
+ upb_decoder_abortjmp(d, "Nesting too deep.");
+ }
+ fr->f = f;
+ fr->is_sequence = true;
+ fr->group_fieldnum = -1;
+ fr->is_packed = packed;
+ fr->end_ofs = end_ofs;
+ d->top = fr;
+ upb_decoder_setmsgend(d);
+}
+
+INLINE void upb_pop_submsg(upb_decoder *d) {
+ upb_sink_endsubmsg(&d->sink, d->top->f);
+ d->top--;
+ upb_decoder_setmsgend(d);
+}
+
+INLINE void upb_pop_seq(upb_decoder *d) {
+ upb_sink_endseq(&d->sink, d->top->f);
+ d->top--;
upb_decoder_setmsgend(d);
}
@@ -344,13 +374,14 @@ INLINE void upb_push_msg(upb_decoder *d, upb_fhandlers *f, uint64_t end) {
// properly sign-extended. We could detect this and error about the data loss,
// but proto2 does not do this, so we pass.
-#define T(type, wt, valtype, convfunc) \
- INLINE void upb_decode_ ## type(upb_decoder *d, upb_fhandlers *f) { \
- upb_value val; \
- upb_value_set ## valtype(&val, (convfunc)(upb_decode_ ## wt(d))); \
- upb_dispatch_value(&d->dispatcher, f, val); \
+#define T(type, wt, name, convfunc) \
+ INLINE void upb_decode_ ## type(upb_decoder *d, const upb_fielddef *f) { \
+ upb_sink_put ## name(&d->sink, f, (convfunc)(upb_decode_ ## wt(d))); \
} \
+static double upb_asdouble(uint64_t n) { double d; memcpy(&d, &n, 8); return d; }
+static float upb_asfloat(uint32_t n) { float f; memcpy(&f, &n, 4); return f; }
+
T(INT32, varint, int32, int32_t)
T(INT64, varint, int64, int64_t)
T(UINT32, varint, uint32, uint32_t)
@@ -361,43 +392,44 @@ T(SFIXED32, fixed32, int32, int32_t)
T(SFIXED64, fixed64, int64, int64_t)
T(BOOL, varint, bool, bool)
T(ENUM, varint, int32, int32_t)
+T(DOUBLE, fixed64, double, upb_asdouble)
+T(FLOAT, fixed32, float, upb_asfloat)
T(SINT32, varint, int32, upb_zzdec_32)
T(SINT64, varint, int64, upb_zzdec_64)
-T(STRING, string, byteregion, upb_byteregion*)
-
#undef T
-INLINE void upb_decode_DOUBLE(upb_decoder *d, upb_fhandlers *f) {
- upb_value val;
- double dbl;
- uint64_t wireval = upb_decode_fixed64(d);
- memcpy(&dbl, &wireval, 8);
- upb_value_setdouble(&val, dbl);
- upb_dispatch_value(&d->dispatcher, f, val);
-}
-
-INLINE void upb_decode_FLOAT(upb_decoder *d, upb_fhandlers *f) {
- upb_value val;
- float flt;
- uint64_t wireval = upb_decode_fixed32(d);
- memcpy(&flt, &wireval, 4);
- upb_value_setfloat(&val, flt);
- upb_dispatch_value(&d->dispatcher, f, val);
-}
-
-static void upb_decode_GROUP(upb_decoder *d, upb_fhandlers *f) {
+static void upb_decode_GROUP(upb_decoder *d, const upb_fielddef *f) {
upb_push_msg(d, f, UPB_NONDELIMITED);
}
-static void upb_endgroup(upb_decoder *d, upb_fhandlers *f) {
- (void)f;
- upb_dispatch_endsubmsg(&d->dispatcher);
- upb_decoder_setmsgend(d);
-}
-static void upb_decode_MESSAGE(upb_decoder *d, upb_fhandlers *f) {
+
+static void upb_decode_MESSAGE(upb_decoder *d, const upb_fielddef *f) {
uint32_t len = upb_decode_varint32(d);
upb_push_msg(d, f, upb_decoder_offset(d) + len);
}
+static void upb_decode_STRING(upb_decoder *d, const upb_fielddef *f) {
+ uint32_t strlen = upb_decode_varint32(d);
+ uint64_t offset = upb_decoder_offset(d);
+ uint64_t end = offset + strlen;
+ if (end > upb_byteregion_endofs(d->input))
+ upb_decoder_abortjmp(d, "Unexpected EOF");
+ upb_sink_startstr(&d->sink, f, strlen);
+ while (strlen > 0) {
+ if (upb_byteregion_available(d->input, offset) == 0)
+ upb_pullbuf(d);
+ size_t len;
+ const char *ptr = upb_byteregion_getptr(d->input, offset, &len);
+ len = UPB_MIN(len, strlen);
+ len = upb_sink_putstring(&d->sink, f, ptr, len);
+ if (len > strlen)
+ upb_decoder_abortjmp(d, "Skipped too many bytes.");
+ offset += len;
+ strlen -= len;
+ upb_decoder_discardto(d, offset);
+ }
+ upb_sink_endstr(&d->sink, f);
+}
+
/* The main decoding loop *****************************************************/
@@ -410,33 +442,33 @@ static void upb_decoder_checkdelim(upb_decoder *d) {
// handler).
while (d->delim_end != NULL && d->ptr >= d->delim_end) {
if (d->ptr > d->delim_end) upb_decoder_abortjmp(d, "Bad submessage end");
- if (d->dispatcher.top->is_sequence) {
- upb_dispatch_endseq(&d->dispatcher);
+ if (d->top->is_sequence) {
+ upb_pop_seq(d);
} else {
- upb_dispatch_endsubmsg(&d->dispatcher);
+ upb_pop_submsg(d);
}
- upb_decoder_setmsgend(d);
}
}
-INLINE upb_fhandlers *upb_decode_tag(upb_decoder *d) {
+INLINE const upb_fielddef *upb_decode_tag(upb_decoder *d) {
while (1) {
uint32_t tag;
if (!upb_trydecode_varint32(d, &tag)) return NULL;
uint8_t wire_type = tag & 0x7;
- uint32_t fieldnum = tag >> 3;
- const upb_value *val = upb_inttable_lookup32(d->dispatch_table, fieldnum);
- upb_fhandlers *f = val ? upb_value_getptr(*val) : NULL;
- bool is_packed = false;
+ uint32_t fieldnum = tag >> 3; const upb_fielddef *f = NULL;
+ const upb_handlers *h = upb_sink_tophandlers(&d->sink);
+ f = upb_msgdef_itof(upb_handlers_msgdef(h), fieldnum);
+ bool packed = false;
if (f) {
// Wire type check.
- if (wire_type == upb_decoder_types[f->type].native_wire_type) {
+ upb_fieldtype_t type = upb_fielddef_type(f);
+ if (wire_type == upb_decoder_types[type].native_wire_type) {
// Wire type is ok.
} else if ((wire_type == UPB_WIRE_TYPE_DELIMITED &&
- upb_decoder_types[f->type].is_numeric)) {
+ upb_decoder_types[type].is_numeric)) {
// Wire type is ok (and packed).
- is_packed = true;
+ packed = true;
} else {
f = NULL;
}
@@ -445,29 +477,24 @@ INLINE upb_fhandlers *upb_decode_tag(upb_decoder *d) {
// There are no explicit "startseq" or "endseq" markers in protobuf
// streams, so we have to infer them by noticing when a repeated field
// starts or ends.
- upb_dispatcher_frame *fr = d->dispatcher.top;
+ upb_decoder_frame *fr = d->top;
if (fr->is_sequence && fr->f != f) {
- upb_dispatch_endseq(&d->dispatcher);
- upb_decoder_setmsgend(d);
- fr = d->dispatcher.top;
+ upb_pop_seq(d);
+ fr = d->top;
}
- if (f && f->repeated && !fr->is_sequence) {
- upb_dispatcher_frame *fr2 = upb_dispatch_startseq(&d->dispatcher, f);
- if (is_packed) {
- // Packed primitive field.
+
+ if (f && upb_fielddef_isseq(f) && !fr->is_sequence) {
+ if (packed) {
uint32_t len = upb_decode_varint32(d);
- fr2->end_ofs = upb_decoder_offset(d) + len;
- fr2->is_packed = true;
+ upb_push_seq(d, f, true, upb_decoder_offset(d) + len);
} else {
- // Non-packed field -- this tag pertains to only a single message.
- fr2->end_ofs = fr->end_ofs;
+ upb_push_seq(d, f, false, fr->end_ofs);
}
- upb_decoder_setmsgend(d);
}
if (f) return f;
- // Unknown field.
+ // Unknown field or ENDGROUP.
if (fieldnum == 0 || fieldnum > UPB_MAX_FIELDNUMBER)
upb_decoder_abortjmp(d, "Invalid field number");
switch (wire_type) {
@@ -479,7 +506,12 @@ INLINE upb_fhandlers *upb_decode_tag(upb_decoder *d) {
case UPB_WIRE_TYPE_START_GROUP:
upb_decoder_abortjmp(d, "Can't handle unknown groups yet");
case UPB_WIRE_TYPE_END_GROUP:
- upb_decoder_abortjmp(d, "Unmatched ENDGROUP tag");
+ if (fieldnum != fr->group_fieldnum)
+ upb_decoder_abortjmp(d, "Unmatched ENDGROUP tag");
+ upb_sink_endsubmsg(&d->sink, fr->f);
+ d->top--;
+ upb_decoder_setmsgend(d);
+ break;
default:
upb_decoder_abortjmp(d, "Invalid wire type");
}
@@ -495,30 +527,30 @@ upb_success_t upb_decoder_decode(upb_decoder *d) {
assert(!upb_ok(&d->status));
return UPB_ERROR;
}
- upb_dispatch_startmsg(&d->dispatcher);
+ upb_sink_startmsg(&d->sink);
// Prime the buf so we can hit the JIT immediately.
upb_trypullbuf(d);
- upb_fhandlers *f = d->dispatcher.top->f;
+ const upb_fielddef *f = d->top->f;
while(1) {
- upb_decoder_checkdelim(d);
#ifdef UPB_USE_JIT_X64
upb_decoder_enterjit(d);
upb_decoder_checkpoint(d);
+ upb_decoder_setmsgend(d);
#endif
+ upb_decoder_checkdelim(d);
if (!d->top_is_packed) f = upb_decode_tag(d);
if (!f) {
// Sucessful EOF. We may need to dispatch a top-level implicit frame.
- if (d->dispatcher.top->is_sequence) {
- assert(d->dispatcher.top == d->dispatcher.stack + 1);
- upb_dispatch_endseq(&d->dispatcher);
+ if (d->top->is_sequence) {
+ assert(d->sink.top == d->sink.stack + 1);
+ upb_pop_seq(d);
}
- assert(d->dispatcher.top == d->dispatcher.stack);
- upb_dispatch_endmsg(&d->dispatcher, &d->status);
+ assert(d->top == d->stack);
+ upb_sink_endmsg(&d->sink, &d->status);
return UPB_OK;
}
- switch (f->type) {
- case UPB_TYPE_ENDGROUP: upb_endgroup(d, f); break;
+ switch (upb_fielddef_type(f)) {
case UPB_TYPE(DOUBLE): upb_decode_DOUBLE(d, f); break;
case UPB_TYPE(FLOAT): upb_decode_FLOAT(d, f); break;
case UPB_TYPE(INT64): upb_decode_INT64(d, f); break;
@@ -545,28 +577,29 @@ upb_success_t upb_decoder_decode(upb_decoder *d) {
void upb_decoder_init(upb_decoder *d) {
upb_status_init(&d->status);
- upb_dispatcher_init(&d->dispatcher, &d->status, &upb_decoder_exitjmp2, d);
d->plan = NULL;
d->input = NULL;
+ d->limit = &d->stack[UPB_MAX_NESTING];
}
-void upb_decoder_resetplan(upb_decoder *d, upb_decoderplan *p, int msg_offset) {
- assert(msg_offset >= 0);
- assert(msg_offset < p->handlers->msgs_len);
+void upb_decoder_resetplan(upb_decoder *d, upb_decoderplan *p) {
d->plan = p;
- d->msg_offset = msg_offset;
d->input = NULL;
+ upb_sink_init(&d->sink, p->handlers);
}
void upb_decoder_resetinput(upb_decoder *d, upb_byteregion *input,
- void *closure) {
+ void *c) {
assert(d->plan);
- upb_dispatcher_frame *f =
- upb_dispatcher_reset(&d->dispatcher, closure, d->plan->handlers->msgs[0]);
upb_status_clear(&d->status);
- f->end_ofs = UPB_NONDELIMITED;
+ upb_sink_reset(&d->sink, c);
d->input = input;
- d->str_byteregion.bytesrc = input->bytesrc;
+
+ d->top = d->stack;
+ d->top->is_sequence = false;
+ d->top->is_packed = false;
+ d->top->group_fieldnum = UINT32_MAX;
+ d->top->end_ofs = UPB_NONDELIMITED;
// Protect against assert in skiptonewbuf().
d->bufstart_ofs = 0;
@@ -576,6 +609,5 @@ void upb_decoder_resetinput(upb_decoder *d, upb_byteregion *input,
}
void upb_decoder_uninit(upb_decoder *d) {
- upb_dispatcher_uninit(&d->dispatcher);
upb_status_uninit(&d->status);
}
diff --git a/upb/pb/decoder.h b/upb/pb/decoder.h
index df65468..690ebb9 100644
--- a/upb/pb/decoder.h
+++ b/upb/pb/decoder.h
@@ -13,9 +13,8 @@
#define UPB_DECODER_H_
#include <setjmp.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include "upb/handlers.h"
+#include "upb/bytestream.h"
+#include "upb/sink.h"
#ifdef __cplusplus
extern "C" {
@@ -34,9 +33,12 @@ extern "C" {
struct _upb_decoderplan;
typedef struct _upb_decoderplan upb_decoderplan;
-// TODO: add parameter for a list of other decoder plans that we can share
-// generated code with.
-upb_decoderplan *upb_decoderplan_new(upb_handlers *h, bool allowjit);
+// TODO(haberman):
+// - add support for letting any message in the plan be at the top level.
+// - make this object a handlers instead (when bytesrc/bytesink are merged
+// into handlers).
+// - add support for sharing code with previously-built plans/handlers.
+upb_decoderplan *upb_decoderplan_new(const upb_handlers *h, bool allowjit);
void upb_decoderplan_unref(upb_decoderplan *p);
// Returns true if the plan contains JIT-ted code. This may not be the same as
@@ -49,15 +51,28 @@ bool upb_decoderplan_hasjitcode(upb_decoderplan *p);
struct dasm_State;
+typedef struct {
+ const upb_fielddef *f;
+ uint64_t end_ofs;
+ uint32_t group_fieldnum; // UINT32_MAX for non-groups.
+ bool is_sequence; // frame represents seq or submsg? (f might be both).
+ bool is_packed; // !upb_issubmsg(f) && end_ofs != UINT64_MAX
+ // (strings aren't pushed).
+} upb_decoder_frame;
+
typedef struct _upb_decoder {
upb_decoderplan *plan;
- int msg_offset; // Which message from the plan is top-level.
upb_byteregion *input; // Input data (serialized), not owned.
- upb_dispatcher dispatcher; // Dispatcher to which we push parsed data.
upb_status status; // Where we store errors that occur.
- upb_byteregion str_byteregion; // For passing string data to callbacks.
- upb_inttable *dispatch_table;
+ // Where we push parsed data.
+ // TODO(haberman): make this a pointer and make upb_decoder_resetinput() take
+ // one of these instead of a void*.
+ upb_sink sink;
+
+ // Our internal stack.
+ upb_decoder_frame *top, *limit;
+ upb_decoder_frame stack[UPB_MAX_NESTING];
// Current input buffer and its stream offset.
const char *buf, *ptr, *end;
@@ -70,7 +85,11 @@ typedef struct _upb_decoder {
#ifdef UPB_USE_JIT_X64
// For JIT, which doesn't do bounds checks in the middle of parsing a field.
- const char *jit_end, *effective_end; // == MIN(jit_end, submsg_end)
+ const char *jit_end, *effective_end; // == MIN(jit_end, delim_end)
+
+ // Used momentarily by the generated code to store a value while a user
+ // function is called.
+ uint32_t tmp_len;
#endif
// For exiting the decoder on error.
@@ -88,7 +107,7 @@ void upb_decoder_uninit(upb_decoder *d);
// must live until the decoder is destroyed or reset to a different plan.
//
// Must be called before upb_decoder_resetinput() or upb_decoder_decode().
-void upb_decoder_resetplan(upb_decoder *d, upb_decoderplan *p, int msg_offset);
+void upb_decoder_resetplan(upb_decoder *d, upb_decoderplan *p);
// Resets the input of an already-allocated decoder. This puts it in a state
// where it has not seen any data, and expects the next data to be from the
@@ -111,7 +130,8 @@ INLINE const upb_status *upb_decoder_status(upb_decoder *d) {
// Implementation details
struct _upb_decoderplan {
- upb_handlers *handlers; // owns reference.
+ // The top-level handlers that this plan calls into. We own a ref.
+ const upb_handlers *handlers;
#ifdef UPB_USE_JIT_X64
// JIT-generated machine code (else NULL).
@@ -119,8 +139,23 @@ struct _upb_decoderplan {
size_t jit_size;
char *debug_info;
+ // For storing upb_jitmsginfo, which contains per-msg runtime data needed
+ // by the JIT.
+ // Maps upb_handlers* -> upb_jitmsginfo.
+ upb_inttable msginfo;
+
+ // The following members are used only while the JIT is being built.
+
// This pointer is allocated by dasm_init() and freed by dasm_free().
struct dasm_State *dynasm;
+
+ // For storing pclabel bases while we are building the JIT.
+ // Maps (upb_handlers* or upb_fielddef*) -> int32 pclabel_base
+ upb_inttable pclabels;
+
+ // This is not the same as len(pclabels) because the table only contains base
+ // offsets for each def, but each def can have many pclabels.
+ uint32_t pclabel_count;
#endif
};
diff --git a/upb/pb/decoder_x64.dasc b/upb/pb/decoder_x64.dasc
index f58e403..cd09cfe 100644
--- a/upb/pb/decoder_x64.dasc
+++ b/upb/pb/decoder_x64.dasc
@@ -12,6 +12,7 @@
|// function) we must respect alignment rules. All x86-64 systems require
|// 16-byte stack alignment.
+#include <stdio.h>
#include <sys/mman.h>
#include "dynasm/dasm_x86.h"
@@ -28,6 +29,44 @@
#define MAP_32BIT 0
#endif
+// These are used to track jump targets for messages and fields.
+enum {
+ STARTMSG = 0,
+ AFTER_STARTMSG = 1,
+ ENDOFBUF = 2,
+ ENDOFMSG = 3,
+ DYNDISPATCH = 4,
+ TOTAL_MSG_PCLABELS = 5,
+};
+
+enum {
+ FIELD = 0,
+ FIELD_NO_TYPECHECK = 1,
+ TOTAL_FIELD_PCLABELS = 2,
+};
+
+typedef struct {
+ uint32_t max_field_number;
+ // Currently keyed on field number. Could also try keying it
+ // on encoded or decoded tag, or on encoded field number.
+ void **tablearray;
+ // Pointer to the JIT code for parsing this message.
+ void *jit_func;
+} upb_jitmsginfo;
+
+static uint32_t upb_getpclabel(upb_decoderplan *plan, const void *obj, int n) {
+ const upb_value *v = upb_inttable_lookupptr(&plan->pclabels, obj);
+ assert(v);
+ return upb_value_getuint32(*v) + n;
+}
+
+static upb_jitmsginfo *upb_getmsginfo(upb_decoderplan *plan,
+ const upb_handlers *h) {
+ const upb_value *v = upb_inttable_lookupptr(&plan->msginfo, h);
+ assert(v);
+ return upb_value_getptr(*v);
+}
+
// To debug JIT-ted code with GDB we need to tell GDB about the JIT-ted code
// at runtime. GDB 7.x+ has defined an interface for doing this, and these
// structure/function defintions are copied out of gdb/jit.h
@@ -66,7 +105,9 @@ typedef struct {
gdb_jit_descriptor __jit_debug_descriptor = {1, GDB_JIT_NOACTION, NULL, NULL};
-void __attribute__((noinline)) __jit_debug_register_code() { __asm__ __volatile__(""); }
+void __attribute__((noinline)) __jit_debug_register_code() {
+ __asm__ __volatile__("");
+}
void upb_reg_jit_gdb(upb_decoderplan *plan) {
// Create debug info.
@@ -120,7 +161,8 @@ static void upb_assert_notnull(void *addr) { assert(addr != NULL); (void)addr; }
|.define ARG3_32, edx
|.define ARG3_64, rdx
|.define ARG4_64, rcx
-|.define ARG5_32, r8d
+|.define XMMARG1, xmm0
+
|
|// Register allocation / type map.
|// ALL of the code in this file uses these register allocations.
@@ -128,13 +170,15 @@ static void upb_assert_notnull(void *addr) { assert(addr != NULL); (void)addr; }
|// conventions, but of course when calling to user callbacks we must.
|.define PTR, rbx // Writing this to DECODER->ptr commits our progress.
|.define CLOSURE, r12
-|.type FRAME, upb_dispatcher_frame, r13
-|.type BYTEREGION,upb_byteregion, r14
+|.type SINKFRAME, upb_sink_frame, r13
+|.type FRAME, upb_decoder_frame, r14
|.type DECODER, upb_decoder, r15
-|.type STDARRAY, upb_stdarray
|
|.macro callp, addr
|| upb_assert_notnull(addr);
+|// TODO(haberman): fix this. I believe the predicate we should actually be
+|// testing is whether the jump distance is greater than INT32_MAX, not the
+|// absolute address of the target.
|| if ((uintptr_t)addr < 0xffffffff) {
| call &addr
|| } else {
@@ -143,14 +187,22 @@ static void upb_assert_notnull(void *addr) { assert(addr != NULL); (void)addr; }
|| }
|.endmacro
|
-|// Checks PTR for end-of-buffer.
-|.macro check_eob, m
+|// Checkpoints our progress by writing PTR to DECODER, and
+|// checks for end-of-buffer.
+|.macro checkpoint, h
+| mov DECODER->ptr, PTR
| cmp PTR, DECODER->effective_end
-|| if (m->is_group) {
- | jae ->exit_jit
-|| } else {
- | jae =>m->jit_endofbuf_pclabel
-|| }
+| jae =>upb_getpclabel(plan, h, ENDOFBUF)
+|.endmacro
+|
+|.macro check_bool_ret
+| test al, al
+| jz ->exit_jit
+|.endmacro
+|
+|.macro check_ptr_ret
+| test rax, rax
+| jz ->exit_jit
|.endmacro
|
|// Decodes varint from [PTR + offset] -> ARG3.
@@ -172,8 +224,7 @@ static void upb_assert_notnull(void *addr) { assert(addr != NULL); (void)addr; }
| mov ARG1_64, rax
| mov ARG2_32, ARG3_32
| callp upb_vdecode_max8_fast
-| test rax, rax
-| jz ->exit_jit // >10-byte varint.
+| check_ptr_ret // Check for unterminated, >10-byte varint.
|9:
|.endmacro
|
@@ -187,74 +238,103 @@ static void upb_assert_notnull(void *addr) { assert(addr != NULL); (void)addr; }
|// Could specialize this by avoiding the value masking: could just key the
|// table on the raw (length-masked) varint to save 3-4 cycles of latency.
|// Currently only support tables where all entries are in the array part.
-|.macro dyndispatch_, m
-|=>m->jit_dyndispatch_pclabel:
+|.macro dyndispatch_, h
+|=>upb_getpclabel(plan, h, DYNDISPATCH):
| decode_loaded_varint, 0
| mov ecx, edx
| shr ecx, 3
-| and edx, 0x7 // For the type check that will happen later.
-| cmp ecx, m->max_field_number // Bounds-check the field.
-| ja ->exit_jit // In the future; could be unknown label
-|| if ((uintptr_t)m->tablearray < 0xffffffff) {
+| and edx, 0x7 // Note: this value is used in the FIELD pclabel below.
+| cmp edx, UPB_WIRE_TYPE_END_GROUP
+| je >1
+|| upb_jitmsginfo *mi = upb_getmsginfo(plan, h);
+| cmp ecx, mi->max_field_number // Bounds-check the field.
+| ja ->exit_jit // In the future; could be unknown label
+|| if ((uintptr_t)mi->tablearray < 0xffffffff) {
| // TODO: support hybrid array/hash tables.
-| mov rax, qword [rcx*8 + m->tablearray]
+| mov rax, qword [rcx*8 + mi->tablearray]
|| } else {
-| mov64 rax, (uintptr_t)m->tablearray
+| mov64 rax, (uintptr_t)mi->tablearray
| mov rax, qword [rax + rcx*8]
|| }
| jmp rax // Dispatch: unpredictable jump.
+|1:
+|// End group.
+| cmp ecx, FRAME->group_fieldnum
+| jne ->exit_jit // Unexpected END_GROUP tag.
+| mov PTR, rax // rax came from decode_loaded_varint
+| mov DECODER->ptr, PTR
+| jmp =>upb_getpclabel(plan, h, ENDOFMSG)
|.endmacro
|
|.if 1
| // Replicated dispatch: larger code, but better branch prediction.
| .define dyndispatch, dyndispatch_
|.else
-| .macro dyndispatch, m
-| jmp =>m->jit_dyndispatch_pclabel
+| // Single dispatch: smaller code, could be faster because of reduced
+| // icache usage. We keep this around to allow for easy comparison between
+| // the two.
+| .macro dyndispatch, h
+| jmp =>upb_getpclabel(plan, h, DYNDISPATCH)
| .endmacro
|.endif
|
|// Push a stack frame (not the CPU stack, the upb_decoder stack).
-|.macro pushframe, f, end_offset_, is_sequence_
-| lea rax, [FRAME + sizeof(upb_dispatcher_frame)] // rax for shorter addressing.
-| cmp rax, qword DECODER->dispatcher.limit
+|.macro pushframe, h, field, end_offset_, endtype
+|// Decoder Frame.
+| lea rax, [FRAME + sizeof(upb_decoder_frame)] // rax for short addressing
+| cmp rax, DECODER->limit
| jae ->exit_jit // Frame stack overflow.
-| mov64 r8, (uintptr_t)f
-| mov qword FRAME:rax->f, r8
+| mov64 r10, (uintptr_t)field
+| mov FRAME:rax->f, r10
| mov qword FRAME:rax->end_ofs, end_offset_
-| mov byte FRAME:rax->is_sequence, is_sequence_
-| mov DECODER->dispatcher.top, rax
+| mov byte FRAME:rax->is_sequence, (endtype == UPB_HANDLER_ENDSEQ)
+| mov byte FRAME:rax->is_packed, 0
+|| if (upb_fielddef_type(field) == UPB_TYPE_GROUP &&
+|| endtype == UPB_HANDLER_ENDSUBMSG) {
+| mov dword FRAME:rax->group_fieldnum, upb_fielddef_number(field)
+|| } else {
+| mov dword FRAME:rax->group_fieldnum, 0xffffffff
+|| }
+| mov DECODER->top, rax
| mov FRAME, rax
+|// Sink Frame.
+| lea rcx, [SINKFRAME + sizeof(upb_sink_frame)] // rcx for short addressing
+| cmp rcx, DECODER->sink.limit
+| jae ->exit_jit // Frame stack overflow.
+| mov dword SINKFRAME:rcx->end, getselector(field, endtype)
+|| if (upb_fielddef_issubmsg(field)) {
+| mov64 r9, (uintptr_t)upb_handlers_getsubhandlers(h, field)
+|| } else {
+| mov64 r9, (uintptr_t)h
+|| }
+| mov SINKFRAME:rcx->h, r9
+| mov DECODER->sink.top, rcx
+| mov SINKFRAME, rcx
|.endmacro
|
-|.macro popframe, m
-| sub FRAME, sizeof(upb_dispatcher_frame)
-| mov DECODER->dispatcher.top, FRAME
-| setmsgend m
-| mov CLOSURE, FRAME->closure
+|.macro popframe
+| sub FRAME, sizeof(upb_decoder_frame)
+| mov DECODER->top, FRAME
+| sub SINKFRAME, sizeof(upb_sink_frame)
+| mov DECODER->sink.top, SINKFRAME
+| setmsgend
+| mov CLOSURE, SINKFRAME->closure
|.endmacro
|
-|.macro setmsgend, m
-| mov rsi, DECODER->jit_end
-|| if (m->is_group) {
-| mov64 rax, 0xffffffffffffffff
-| mov qword DECODER->delim_end, rax
-| mov DECODER->effective_end, rsi
-|| } else {
-| // Could store a correctly-biased version in the frame, at the cost of
-| // a larger stack.
-| mov eax, dword FRAME->end_ofs
-| add rax, qword DECODER->buf
-| mov DECODER->delim_end, rax // delim_end = d->buf + f->end_ofs
-| cmp rax, rsi
-| jb >8
-| mov rax, rsi // effective_end = min(d->delim_end, d->jit_end)
+|.macro setmsgend
+| mov rsi, DECODER->jit_end
+| mov rax, qword FRAME->end_ofs // Will be UINT64_MAX for groups.
+| sub rax, qword DECODER->bufstart_ofs
+| add rax, qword DECODER->buf // rax = d->buf + f->end_ofs - d->bufstart_ofs
+| jc >8 // If the addition overflowed, use jit_end
+| cmp rax, rsi
+| ja >8 // If jit_end is less, use jit_end
+| mov rsi, rax // Use frame end.
|8:
-| mov DECODER->effective_end, rax
-|| }
+| mov DECODER->effective_end, rsi
|.endmacro
|
-|// rax contains the tag, compare it against "tag", but since it is a varint
+|// rcx contains the tag, compare it against "tag", but since it is a varint
|// we must only compare as many bytes as actually have data.
|.macro checktag, tag
|| switch (upb_value_size(tag)) {
@@ -279,22 +359,6 @@ static void upb_assert_notnull(void *addr) { assert(addr != NULL); (void)addr; }
|| }
|.endmacro
|
-|// TODO: optimize for 0 (xor) and 32-bits.
-|.macro loadfval, f
-||#ifndef NDEBUG
-||// Since upb_value carries type information in debug mode
-||// only, we need to pass the arguments slightly differently.
-| mov ARG3_32, f->fval.type
-||#endif
-|| if (f->fval.val.uint64 == 0) {
-| xor ARG2_32, ARG2_32
-|| } else if (f->fval.val.uint64 < 0xffffffff) {
-| mov ARG2_32, f->fval.val.uint64
-|| } else {
-| mov64 ARG2_64, f->fval.val.uint64
-|| }
-|.endmacro
-|
|.macro sethas, reg, hasbit
|| if (hasbit >= 0) {
| or byte [reg + ((uint32_t)hasbit / 8)], (1 << ((uint32_t)hasbit % 8))
@@ -304,14 +368,37 @@ static void upb_assert_notnull(void *addr) { assert(addr != NULL); (void)addr; }
#include <stdlib.h>
#include "upb/pb/varint.h"
-#include "upb/msg.h"
+
+static upb_selector_t getselector(const upb_fielddef *f,
+ upb_handlertype_t type) {
+ upb_selector_t selector;
+ bool ok = upb_getselector(f, type, &selector);
+ UPB_ASSERT_VAR(ok, ok);
+ return selector;
+}
+
+static upb_func *gethandler(const upb_handlers *h, const upb_fielddef *f,
+ upb_handlertype_t type) {
+ return upb_handlers_gethandler(h, getselector(f, type));
+}
+
+static uintptr_t gethandlerdata(const upb_handlers *h, const upb_fielddef *f,
+ upb_handlertype_t type) {
+ return (uintptr_t)upb_handlers_gethandlerdata(h, getselector(f, type));
+}
// Decodes the next val into ARG3, advances PTR.
static void upb_decoderplan_jit_decodefield(upb_decoderplan *plan,
- uint8_t type, size_t tag_size) {
+ uint8_t type, size_t tag_size,
+ const upb_handlers *h,
+ const upb_fielddef *f) {
// Decode the value into arg 3 for the callback.
switch (type) {
case UPB_TYPE(DOUBLE):
+ | movsd XMMARG1, qword [PTR + tag_size]
+ | add PTR, 8 + tag_size
+ break;
+
case UPB_TYPE(FIXED64):
case UPB_TYPE(SFIXED64):
| mov ARG3_64, qword [PTR + tag_size]
@@ -319,6 +406,10 @@ static void upb_decoderplan_jit_decodefield(upb_decoderplan *plan,
break;
case UPB_TYPE(FLOAT):
+ | movss XMMARG1, dword [PTR + tag_size]
+ | add PTR, 4 + tag_size
+ break;
+
case UPB_TYPE(FIXED32):
case UPB_TYPE(SFIXED32):
| mov ARG3_32, dword [PTR + tag_size]
@@ -362,7 +453,7 @@ static void upb_decoderplan_jit_decodefield(upb_decoderplan *plan,
break;
case UPB_TYPE(STRING):
- case UPB_TYPE(BYTES):
+ case UPB_TYPE(BYTES): {
// We only handle the case where the entire string is in our current
// buf, which sidesteps any security problems. The C path has more
// robust checks.
@@ -372,22 +463,42 @@ static void upb_decoderplan_jit_decodefield(upb_decoderplan *plan,
| sub rdi, rax
| cmp ARG3_64, rdi // if (len > d->end - str)
| ja ->exit_jit // Can't deliver, whole string not in buf.
+ | mov PTR, rax
+
+ upb_func *handler = gethandler(h, f, UPB_HANDLER_STARTSTR);
+ if (handler) {
+ | mov DECODER->tmp_len, ARG3_64
+ | mov ARG1_64, CLOSURE
+ | mov64 ARG2_64, gethandlerdata(h, f, UPB_HANDLER_STARTSTR)
+ | callp handler
+ | check_ptr_ret
+ | mov ARG1_64, rax // sub-closure
+ | mov ARG4_64, DECODER->tmp_len
+ } else {
+ | mov ARG1_64, CLOSURE
+ | mov ARG4_64, ARG3_64
+ }
+
+ handler = gethandler(h, f, UPB_HANDLER_STRING);
+ if (handler) {
+ | mov64 ARG2_64, gethandlerdata(h, f, UPB_HANDLER_STRING)
+ | mov ARG3_64, PTR
+ | callp handler
+ // TODO: properly handle returns other than "n" (the whole string).
+ | add PTR, rax
+ } else {
+ | add PTR, ARG4_64
+ }
- // Update PTR to point past end of string.
- | mov rdi, rax
- | add rdi, ARG3_64
- | mov PTR, rdi
-
- // Populate BYTEREGION appropriately.
- | sub rax, DECODER->buf
- | add rax, DECODER->bufstart_ofs // = d->ptr - d->buf + d->bufstart_ofs
- | mov BYTEREGION->start, rax
- | mov BYTEREGION->discard, rax
- | add rax, ARG3_64
- | mov BYTEREGION->end, rax
- | mov BYTEREGION->fetch, rax // Fast path ensures whole string is loaded
- | mov ARG3_64, BYTEREGION
+ handler = gethandler(h, f, UPB_HANDLER_ENDSTR);
+ if (handler) {
+ | mov ARG1_64, CLOSURE
+ | mov64 ARG2_64, gethandlerdata(h, f, UPB_HANDLER_ENDSTR)
+ | callp handler
+ | check_bool_ret
+ }
break;
+ }
// Will dispatch callbacks and call submessage in a second.
case UPB_TYPE(MESSAGE):
@@ -402,85 +513,85 @@ static void upb_decoderplan_jit_decodefield(upb_decoderplan *plan,
}
static void upb_decoderplan_jit_callcb(upb_decoderplan *plan,
- upb_fhandlers *f) {
+ const upb_handlers *h,
+ const upb_fielddef *f) {
// Call callbacks. Specializing the append accessors didn't yield a speed
// increase in benchmarks.
- if (upb_issubmsgtype(f->type)) {
- if (f->type == UPB_TYPE(MESSAGE)) {
+ if (upb_fielddef_issubmsg(f)) {
+ if (upb_fielddef_type(f) == UPB_TYPE(MESSAGE)) {
| mov rsi, PTR
| sub rsi, DECODER->buf
| add rsi, ARG3_64 // = (d->ptr - d->buf) + delim_len
} else {
- assert(f->type == UPB_TYPE(GROUP));
+ assert(upb_fielddef_type(f) == UPB_TYPE(GROUP));
| mov rsi, UPB_NONDELIMITED
}
- | pushframe f, rsi, false
+ | pushframe h, f, rsi, UPB_HANDLER_ENDSUBMSG
// Call startsubmsg handler (if any).
- if (f->startsubmsg) {
+ upb_func *startsubmsg = gethandler(h, f, UPB_HANDLER_STARTSUBMSG);
+ if (startsubmsg) {
// upb_sflow_t startsubmsg(void *closure, upb_value fval)
| mov ARG1_64, CLOSURE
- | loadfval f
- | callp f->startsubmsg
- | sethas CLOSURE, f->hasbit
- | mov CLOSURE, rdx
- } else {
- | sethas CLOSURE, f->hasbit
+ | mov64 ARG2_64, gethandlerdata(h, f, UPB_HANDLER_STARTSUBMSG);
+ | callp startsubmsg
+ | check_ptr_ret
+ | mov CLOSURE, rax
}
- | mov qword FRAME->closure, CLOSURE
- // TODO: Handle UPB_SKIPSUBMSG, UPB_BREAK
- | mov DECODER->ptr, PTR
+ | mov qword SINKFRAME->closure, CLOSURE
- const upb_mhandlers *sub_m = upb_fhandlers_getsubmsg(f);
- | call =>sub_m->jit_startmsg_pclabel;
- | popframe upb_fhandlers_getmsg(f)
+ // TODO: have to decide what to do with NULLs subhandlers (or whether to
+ // disallow them and require a full handlers tree to match the def tree).
+ const upb_handlers *sub_h = upb_handlers_getsubhandlers(h, f);
+ assert(sub_h);
+ | call =>upb_getpclabel(plan, sub_h, STARTMSG)
+ | popframe
// Call endsubmsg handler (if any).
- if (f->endsubmsg) {
+ upb_func *endsubmsg = gethandler(h, f, UPB_HANDLER_ENDSUBMSG);
+ if (endsubmsg) {
// upb_flow_t endsubmsg(void *closure, upb_value fval);
| mov ARG1_64, CLOSURE
- | loadfval f
- | callp f->endsubmsg
+ | mov64 ARG2_64, gethandlerdata(h, f, UPB_HANDLER_ENDSUBMSG);
+ | callp endsubmsg
+ | check_bool_ret
}
- // TODO: Handle UPB_SKIPSUBMSG, UPB_BREAK
- | mov DECODER->ptr, PTR
- } else {
+ } else if (!upb_fielddef_isstring(f)) {
| mov ARG1_64, CLOSURE
+ upb_handlertype_t handlertype = upb_handlers_getprimitivehandlertype(f);
+ upb_func *handler = gethandler(h, f, handlertype);
+ const upb_stdmsg_fval *fv = (void*)gethandlerdata(h, f, handlertype);
// Test for callbacks we can specialize.
// Can't switch() on function pointers.
- if (f->value == &upb_stdmsg_setint64 ||
- f->value == &upb_stdmsg_setuint64 ||
- f->value == &upb_stdmsg_setptr ||
- f->value == &upb_stdmsg_setdouble) {
- const upb_fielddef *fd = upb_value_getfielddef(f->fval);
- | mov [ARG1_64 + fd->offset], ARG3_64
- } else if (f->value == &upb_stdmsg_setint32 ||
- f->value == &upb_stdmsg_setuint32 ||
- f->value == &upb_stdmsg_setfloat) {
- const upb_fielddef *fd = upb_value_getfielddef(f->fval);
- | mov [ARG1_64 + fd->offset], ARG3_32
- } else if (f->value == &upb_stdmsg_setbool) {
- const upb_fielddef *fd = upb_value_getfielddef(f->fval);
- | mov [ARG1_64 + fd->offset], ARG3_8
- } else if (f->value) {
+ if (handler == (void*)&upb_stdmsg_setint64 ||
+ handler == (void*)&upb_stdmsg_setuint64) {
+ | mov [ARG1_64 + fv->offset], ARG3_64
+ | sethas CLOSURE, fv->hasbit
+ } else if (handler == (void*)&upb_stdmsg_setdouble) {
+ | movsd qword [ARG1_64 + fv->offset], XMMARG1
+ | sethas CLOSURE, fv->hasbit
+ } else if (handler == (void*)&upb_stdmsg_setint32 ||
+ handler == (void*)&upb_stdmsg_setuint32) {
+ | mov [ARG1_64 + fv->offset], ARG3_32
+ | sethas CLOSURE, fv->hasbit
+ } else if (handler == (void*)&upb_stdmsg_setfloat) {
+ | movss dword [ARG1_64 + fv->offset], XMMARG1
+ | sethas CLOSURE, fv->hasbit
+ } else if (handler == (void*)&upb_stdmsg_setbool) {
+ | mov [ARG1_64 + fv->offset], ARG3_8
+ | sethas CLOSURE, fv->hasbit
+ } else if (handler) {
// Load closure and fval into arg registers.
- ||#ifndef NDEBUG
- ||// Since upb_value carries type information in debug mode
- ||// only, we need to pass the arguments slightly differently.
- | mov ARG4_64, ARG3_64
- | mov ARG5_32, upb_types[f->type].inmemory_type
- ||#endif
- | loadfval f
- | callp f->value
+ | mov64 ARG2_64, gethandlerdata(h, f, handlertype);
+ | callp handler
+ | check_bool_ret
}
- | sethas CLOSURE, f->hasbit
- // TODO: Handle UPB_SKIPSUBMSG, UPB_BREAK
- | mov DECODER->ptr, PTR
}
}
-static uint64_t upb_get_encoded_tag(upb_fhandlers *f) {
- uint32_t tag = (f->number << 3) | upb_decoder_types[f->type].native_wire_type;
+static uint64_t upb_get_encoded_tag(const upb_fielddef *f) {
+ uint32_t tag = (upb_fielddef_number(f) << 3) |
+ upb_decoder_types[upb_fielddef_type(f)].native_wire_type;
uint64_t encoded_tag = upb_vencode32(tag);
// No tag should be greater than 5 bytes.
assert(encoded_tag <= 0xffffffffff);
@@ -488,118 +599,121 @@ static uint64_t upb_get_encoded_tag(upb_fhandlers *f) {
}
// PTR should point to the beginning of the tag.
-static void upb_decoderplan_jit_field(upb_decoderplan *plan, upb_mhandlers *m,
- upb_fhandlers *f, upb_fhandlers *next_f) {
+static void upb_decoderplan_jit_field(upb_decoderplan *plan,
+ const upb_handlers *h,
+ const upb_fielddef *f,
+ const upb_fielddef *next_f) {
uint64_t tag = upb_get_encoded_tag(f);
uint64_t next_tag = next_f ? upb_get_encoded_tag(next_f) : 0;
+ int tag_size = upb_value_size(tag);
// PC-label for the dispatch table.
// We check the wire type (which must be loaded in edx) because the
// table is keyed on field number, not type.
- |=>f->jit_pclabel:
+ |=>upb_getpclabel(plan, f, FIELD):
| cmp edx, (tag & 0x7)
| jne ->exit_jit // In the future: could be an unknown field or packed.
- |=>f->jit_pclabel_notypecheck:
- if (f->repeated) {
+ |=>upb_getpclabel(plan, f, FIELD_NO_TYPECHECK):
+ if (upb_fielddef_isseq(f)) {
| mov rsi, FRAME->end_ofs
- | pushframe f, rsi, true
- if (f->startseq) {
+ | pushframe h, f, rsi, UPB_HANDLER_ENDSEQ
+ upb_func *startseq = gethandler(h, f, UPB_HANDLER_STARTSEQ);
+ if (startseq) {
| mov ARG1_64, CLOSURE
- | loadfval f
- | callp f->startseq
- | sethas CLOSURE, f->hasbit
- | mov CLOSURE, rdx
- } else {
- | sethas CLOSURE, f->hasbit
+ | mov64 ARG2_64, gethandlerdata(h, f, UPB_HANDLER_STARTSEQ);
+ | callp startseq
+ | check_ptr_ret
+ | mov CLOSURE, rax
}
- | mov qword FRAME->closure, CLOSURE
+ | mov qword SINKFRAME->closure, CLOSURE
}
|1: // Label for repeating this field.
- int tag_size = upb_value_size(tag);
- if (f->type == UPB_TYPE_ENDGROUP) {
- | add PTR, tag_size
- | jmp =>m->jit_endofmsg_pclabel
- return;
- }
-
- upb_decoderplan_jit_decodefield(plan, f->type, tag_size);
- upb_decoderplan_jit_callcb(plan, f);
+ upb_decoderplan_jit_decodefield(plan, upb_fielddef_type(f), tag_size, h, f);
+ upb_decoderplan_jit_callcb(plan, h, f);
// Epilogue: load next tag, check for repeated field.
- | check_eob m
+ | checkpoint h
| mov rcx, qword [PTR]
- if (f->repeated) {
+ if (upb_fielddef_isseq(f)) {
| checktag tag
| je <1
- if (f->endseq) {
+ upb_func *endseq = gethandler(h, f, UPB_HANDLER_ENDSEQ);
+ if (endseq) {
| mov ARG1_64, CLOSURE
- | loadfval f
- | callp f->endseq
+ | mov64 ARG2_64, gethandlerdata(h, f, UPB_HANDLER_ENDSEQ);
+ | callp endseq
}
- | popframe m
+ | popframe
+ // Load next tag again (popframe clobbered it).
+ | mov rcx, qword [PTR]
}
+
if (next_tag != 0) {
| checktag next_tag
- | je =>next_f->jit_pclabel_notypecheck
+ | je =>upb_getpclabel(plan, next_f, FIELD_NO_TYPECHECK)
}
// Fall back to dynamic dispatch.
- | dyndispatch m
- |1:
+ | dyndispatch h
}
static int upb_compare_uint32(const void *a, const void *b) {
- // TODO: always put ENDGROUP at the end.
return *(uint32_t*)a - *(uint32_t*)b;
}
-static void upb_decoderplan_jit_msg(upb_decoderplan *plan, upb_mhandlers *m) {
- |=>m->jit_afterstartmsg_pclabel:
+static void upb_decoderplan_jit_msg(upb_decoderplan *plan,
+ const upb_handlers *h) {
+ |=>upb_getpclabel(plan, h, AFTER_STARTMSG):
// There was a call to get here, so we need to align the stack.
| sub rsp, 8
| jmp >1
- |=>m->jit_startmsg_pclabel:
+ |=>upb_getpclabel(plan, h, STARTMSG):
// There was a call to get here, so we need to align the stack.
| sub rsp, 8
// Call startmsg handler (if any):
- if (m->startmsg) {
+ upb_startmsg_handler *startmsg = upb_handlers_getstartmsg(h);
+ if (startmsg) {
// upb_flow_t startmsg(void *closure);
- | mov ARG1_64, FRAME->closure
- | callp m->startmsg
- // TODO: Handle UPB_SKIPSUBMSG, UPB_BREAK
+ | mov ARG1_64, SINKFRAME->closure
+ | callp startmsg
+ | check_bool_ret
}
|1:
- | setmsgend m
- | check_eob m
+ | setmsgend
+ | checkpoint h
| mov ecx, dword [PTR]
- | dyndispatch_ m
+ | dyndispatch_ h
// --------- New code section (does not fall through) ------------------------
// Emit code for parsing each field (dynamic dispatch contains pointers to
// all of these).
- // Create an ordering over the fields (inttable ordering is undefined).
- int num_keys = upb_inttable_count(&m->fieldtab);
+ // Create an ordering over the fields in field number order.
+ // Parsing will theoretically be fastest if we emit code in the same
+ // order as field numbers are seen on-the-wire because of an optimization
+ // in the generated code that skips dynamic dispatch if the next field is
+ // as expected.
+ const upb_msgdef *md = upb_handlers_msgdef(h);
+ int num_keys = upb_msgdef_numfields(md);
uint32_t *keys = malloc(num_keys * sizeof(*keys));
int idx = 0;
- upb_inttable_iter i;
- upb_inttable_begin(&i, &m->fieldtab);
- for(; !upb_inttable_done(&i); upb_inttable_next(&i)) {
- keys[idx++] = upb_inttable_iter_key(&i);
+ upb_msg_iter i;
+ for(upb_msg_begin(&i, md); !upb_msg_done(&i); upb_msg_next(&i)) {
+ keys[idx++] = upb_fielddef_number(upb_msg_iter_field(&i));
}
qsort(keys, num_keys, sizeof(uint32_t), &upb_compare_uint32);
for(int i = 0; i < num_keys; i++) {
- upb_fhandlers *f = upb_mhandlers_lookup(m, keys[i]);
- upb_fhandlers *next_f =
- (i + 1 < num_keys) ? upb_mhandlers_lookup(m, keys[i + 1]) : NULL;
- upb_decoderplan_jit_field(plan, m, f, next_f);
+ const upb_fielddef *f = upb_msgdef_itof(md, keys[i]);
+ const upb_fielddef *next_f =
+ (i + 1 < num_keys) ? upb_msgdef_itof(md, keys[i + 1]) : NULL;
+ upb_decoderplan_jit_field(plan, h, f, next_f);
}
free(keys);
@@ -607,27 +721,19 @@ static void upb_decoderplan_jit_msg(upb_decoderplan *plan, upb_mhandlers *m) {
// --------- New code section (does not fall through) ------------------------
// End-of-buf / end-of-message.
- if (!m->is_group) {
- // This case doesn't exist for groups, because there eob really means
- // eob, so that case just exits the jit directly.
- |=>m->jit_endofbuf_pclabel:
- | cmp PTR, DECODER->delim_end
- | jb ->exit_jit // We are at eob, but not end-of-submsg.
- }
+ // We hit a buffer limit; either we hit jit_end or end-of-submessage.
+ |=>upb_getpclabel(plan, h, ENDOFBUF):
+ | cmp PTR, DECODER->jit_end
+ | jae ->exit_jit
- |=>m->jit_endofmsg_pclabel:
+ |=>upb_getpclabel(plan, h, ENDOFMSG):
// We are at end-of-submsg: call endmsg handler (if any):
- if (m->endmsg) {
+ upb_endmsg_handler *endmsg = upb_handlers_getendmsg(h);
+ if (endmsg) {
// void endmsg(void *closure, upb_status *status) {
- | mov ARG1_64, FRAME->closure
- | lea ARG2_64, DECODER->dispatcher.status
- | callp m->endmsg
- }
-
- if (m->is_group) {
- // Advance past the "end group" tag.
- // TODO: Handle UPB_BREAK
- | mov DECODER->ptr, PTR
+ | mov ARG1_64, SINKFRAME->closure
+ | lea ARG2_64, DECODER->sink.status
+ | callp endmsg
}
// Counter previous alignment.
@@ -657,9 +763,9 @@ static void upb_decoderplan_jit(upb_decoderplan *plan) {
// Align stack.
| sub rsp, 8
| mov DECODER, ARG1_64
- | mov FRAME, DECODER:ARG1_64->dispatcher.top
- | lea BYTEREGION, DECODER:ARG1_64->str_byteregion
- | mov CLOSURE, FRAME->closure
+ | mov FRAME, DECODER:ARG1_64->top
+ | mov SINKFRAME, DECODER:ARG1_64->sink.top
+ | mov CLOSURE, SINKFRAME->closure
| mov PTR, DECODER->ptr
// TODO: push return addresses for re-entry (will be necessary for multiple
@@ -680,54 +786,65 @@ static void upb_decoderplan_jit(upb_decoderplan *plan) {
| leave
| ret
- upb_handlers *h = plan->handlers;
- for (int i = 0; i < h->msgs_len; i++)
- upb_decoderplan_jit_msg(plan, h->msgs[i]);
-}
-
-static void upb_decoderplan_jit_assignfieldlabs(upb_fhandlers *f,
- uint32_t *pclabel_count) {
- f->jit_pclabel = (*pclabel_count)++;
- f->jit_pclabel_notypecheck = (*pclabel_count)++;
-}
-
-static void upb_decoderplan_jit_assignmsglabs(upb_mhandlers *m,
- uint32_t *pclabel_count) {
- m->jit_startmsg_pclabel = (*pclabel_count)++;
- m->jit_afterstartmsg_pclabel = (*pclabel_count)++;
- m->jit_endofbuf_pclabel = (*pclabel_count)++;
- m->jit_endofmsg_pclabel = (*pclabel_count)++;
- m->jit_dyndispatch_pclabel = (*pclabel_count)++;
- m->jit_unknownfield_pclabel = (*pclabel_count)++;
- m->max_field_number = 0;
upb_inttable_iter i;
- upb_inttable_begin(&i, &m->fieldtab);
+ upb_inttable_begin(&i, &plan->msginfo);
for(; !upb_inttable_done(&i); upb_inttable_next(&i)) {
- uint32_t key = upb_inttable_iter_key(&i);
- m->max_field_number = UPB_MAX(m->max_field_number, key);
- upb_fhandlers *f = upb_value_getptr(upb_inttable_iter_value(&i));
- upb_decoderplan_jit_assignfieldlabs(f, pclabel_count);
+ const upb_handlers *h = (const upb_handlers*)upb_inttable_iter_key(&i);
+ upb_decoderplan_jit_msg(plan, h);
+ }
+}
+
+static void upb_decoderplan_jit_assignpclabels(upb_decoderplan *plan,
+ const upb_handlers *h) {
+ // Limit the DFS.
+ if (upb_inttable_lookupptr(&plan->pclabels, h)) return;
+
+ upb_inttable_insertptr(&plan->pclabels, h,
+ upb_value_uint32(plan->pclabel_count));
+ plan->pclabel_count += TOTAL_MSG_PCLABELS;
+
+ upb_jitmsginfo *info = malloc(sizeof(*info));
+ info->max_field_number = 0;
+ upb_inttable_insertptr(&plan->msginfo, h, upb_value_ptr(info));
+
+ upb_msg_iter i;
+ upb_msg_begin(&i, upb_handlers_msgdef(h));
+ for(; !upb_msg_done(&i); upb_msg_next(&i)) {
+ const upb_fielddef *f = upb_msg_iter_field(&i);
+ info->max_field_number =
+ UPB_MAX(info->max_field_number, upb_fielddef_number(f));
+ upb_inttable_insertptr(&plan->pclabels, f,
+ upb_value_uint32(plan->pclabel_count));
+ plan->pclabel_count += TOTAL_FIELD_PCLABELS;
+
+ // Discover the whole graph of handlers depth-first. We will probably
+ // revise this later to be more explicit about the list of handlers that
+ // the plan should include.
+ if (upb_fielddef_issubmsg(f)) {
+ const upb_handlers *subh = upb_handlers_getsubhandlers(h, f);
+ if (subh) upb_decoderplan_jit_assignpclabels(plan, subh);
+ }
}
// TODO: support large field numbers by either using a hash table or
// generating code for a binary search. For now large field numbers
// will just fall back to the table decoder.
- m->max_field_number = UPB_MIN(m->max_field_number, 16000);
- m->tablearray = malloc((m->max_field_number + 1) * sizeof(void*));
+ info->max_field_number = UPB_MIN(info->max_field_number, 16000);
+ info->tablearray = malloc((info->max_field_number + 1) * sizeof(void*));
}
static void upb_decoderplan_makejit(upb_decoderplan *plan) {
+ upb_inttable_init(&plan->msginfo, UPB_CTYPE_PTR);
plan->debug_info = NULL;
// Assign pclabels.
- uint32_t pclabel_count = 0;
- upb_handlers *h = plan->handlers;
- for (int i = 0; i < h->msgs_len; i++)
- upb_decoderplan_jit_assignmsglabs(h->msgs[i], &pclabel_count);
+ plan->pclabel_count = 0;
+ upb_inttable_init(&plan->pclabels, UPB_CTYPE_UINT32);
+ upb_decoderplan_jit_assignpclabels(plan, plan->handlers);
void **globals = malloc(UPB_JIT_GLOBAL__MAX * sizeof(*globals));
dasm_init(plan, 1);
dasm_setupglobal(plan, globals, UPB_JIT_GLOBAL__MAX);
- dasm_growpc(plan, pclabel_count);
+ dasm_growpc(plan, plan->pclabel_count);
dasm_setup(plan, upb_jit_actionlist);
upb_decoderplan_jit(plan);
@@ -744,38 +861,53 @@ static void upb_decoderplan_makejit(upb_decoderplan *plan) {
dasm_encode(plan, plan->jit_code);
// Create dispatch tables.
- for (int i = 0; i < h->msgs_len; i++) {
- upb_mhandlers *m = h->msgs[i];
+ upb_inttable_iter i;
+ upb_inttable_begin(&i, &plan->msginfo);
+ for(; !upb_inttable_done(&i); upb_inttable_next(&i)) {
+ const upb_handlers *h = (const upb_handlers*)upb_inttable_iter_key(&i);
+ upb_jitmsginfo *mi = upb_getmsginfo(plan, h);
// We jump to after the startmsg handler since it is called before entering
// the JIT (either by upb_decoder or by a previous call to the JIT).
- m->jit_func =
- plan->jit_code + dasm_getpclabel(plan, m->jit_afterstartmsg_pclabel);
- for (uint32_t j = 0; j <= m->max_field_number; j++) {
- upb_fhandlers *f = upb_mhandlers_lookup(m, j);
+ mi->jit_func = plan->jit_code +
+ dasm_getpclabel(plan, upb_getpclabel(plan, h, AFTER_STARTMSG));
+ for (uint32_t j = 0; j <= mi->max_field_number; j++) {
+ const upb_fielddef *f = upb_msgdef_itof(upb_handlers_msgdef(h), j);
if (f) {
- m->tablearray[j] =
- plan->jit_code + dasm_getpclabel(plan, f->jit_pclabel);
+ mi->tablearray[j] = plan->jit_code +
+ dasm_getpclabel(plan, upb_getpclabel(plan, f, FIELD));
} else {
// TODO: extend the JIT to handle unknown fields.
// For the moment we exit the JIT for any unknown field.
- m->tablearray[j] = globals[UPB_JIT_GLOBAL_exit_jit];
+ mi->tablearray[j] = globals[UPB_JIT_GLOBAL_exit_jit];
}
}
}
+ upb_inttable_uninit(&plan->pclabels);
+
dasm_free(plan);
free(globals);
mprotect(plan->jit_code, plan->jit_size, PROT_EXEC | PROT_READ);
+#ifndef NDEBUG
// View with: objdump -M intel -D -b binary -mi386 -Mx86-64 /tmp/machine-code
// Or: ndisasm -b 64 /tmp/machine-code
FILE *f = fopen("/tmp/machine-code", "wb");
fwrite(plan->jit_code, plan->jit_size, 1, f);
fclose(f);
+#endif
}
static void upb_decoderplan_freejit(upb_decoderplan *plan) {
+ upb_inttable_iter i;
+ upb_inttable_begin(&i, &plan->msginfo);
+ for(; !upb_inttable_done(&i); upb_inttable_next(&i)) {
+ upb_jitmsginfo *mi = upb_value_getptr(upb_inttable_iter_value(&i));
+ free(mi->tablearray);
+ free(mi);
+ }
+ upb_inttable_uninit(&plan->msginfo);
munmap(plan->jit_code, plan->jit_size);
free(plan->debug_info);
// TODO: unregister
@@ -783,7 +915,7 @@ static void upb_decoderplan_freejit(upb_decoderplan *plan) {
static void upb_decoder_enterjit(upb_decoder *d) {
if (d->plan->jit_code &&
- d->dispatcher.top == d->dispatcher.stack &&
+ d->sink.top == d->sink.stack &&
d->ptr && d->ptr < d->jit_end) {
#ifndef NDEBUG
register uint64_t rbx asm ("rbx") = 11;
@@ -795,7 +927,9 @@ static void upb_decoder_enterjit(upb_decoder *d) {
// Decodes as many fields as possible, updating d->ptr appropriately,
// before falling through to the slow(er) path.
void (*upb_jit_decode)(upb_decoder *d, void*) = (void*)d->plan->jit_code;
- upb_jit_decode(d, d->plan->handlers->msgs[d->msg_offset]->jit_func);
+ upb_jitmsginfo *mi = upb_getmsginfo(d->plan, d->plan->handlers);
+ assert(mi);
+ upb_jit_decode(d, mi->jit_func);
assert(d->ptr <= d->end);
// Test that callee-save registers were properly restored.
diff --git a/upb/pb/glue.c b/upb/pb/glue.c
index 40b901d..4e69c0c 100644
--- a/upb/pb/glue.c
+++ b/upb/pb/glue.c
@@ -5,10 +5,14 @@
* Author: Josh Haberman <jhaberman@gmail.com>
*/
+#include "upb/pb/glue.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include "upb/bytestream.h"
#include "upb/descriptor/reader.h"
#include "upb/pb/decoder.h"
-#include "upb/pb/glue.h"
upb_def **upb_load_defs_from_descriptor(const char *str, size_t len, int *n,
void *owner, upb_status *status) {
@@ -16,16 +20,14 @@ upb_def **upb_load_defs_from_descriptor(const char *str, size_t len, int *n,
upb_stringsrc_init(&strsrc);
upb_stringsrc_reset(&strsrc, str, len);
- upb_handlers *h = upb_handlers_new();
- upb_descreader_reghandlers(h);
-
+ const upb_handlers *h = upb_descreader_newhandlers(&h);
upb_decoderplan *p = upb_decoderplan_new(h, false);
upb_decoder d;
upb_decoder_init(&d);
- upb_handlers_unref(h);
+ upb_handlers_unref(h, &h);
upb_descreader r;
upb_descreader_init(&r);
- upb_decoder_resetplan(&d, p, 0);
+ upb_decoder_resetplan(&d, p);
upb_decoder_resetinput(&d, upb_stringsrc_allbytes(&strsrc), &r);
upb_success_t ret = upb_decoder_decode(&d);
diff --git a/upb/pb/glue.h b/upb/pb/glue.h
index 6179d8d..4bbc975 100644
--- a/upb/pb/glue.h
+++ b/upb/pb/glue.h
@@ -27,8 +27,7 @@
#define UPB_GLUE_H
#include <stdbool.h>
-#include "upb/upb.h"
-#include "upb/def.h"
+#include "upb/symtab.h"
#ifdef __cplusplus
extern "C" {
@@ -55,6 +54,29 @@ char *upb_readfile(const char *filename, size_t *len);
#ifdef __cplusplus
} /* extern "C" */
+
+namespace upb {
+
+// All routines that load descriptors expect the descriptor to be a
+// FileDescriptorSet.
+inline bool LoadDescriptorFileIntoSymtab(SymbolTable* s, const char *fname,
+ Status* status) {
+ return upb_load_descriptor_file_into_symtab(s, fname, status);
+}
+
+inline bool LoadDescriptorIntoSymtab(SymbolTable* s, const char* str,
+ size_t len, Status* status) {
+ return upb_load_descriptor_into_symtab(s, str, len, status);
+}
+
+// Templated so it can accept both string and std::string.
+template <typename T>
+bool LoadDescriptorIntoSymtab(SymbolTable* s, const T& desc, Status* status) {
+ return upb_load_descriptor_into_symtab(s, desc.c_str(), desc.size(), status);
+}
+
+} // namespace upb
+
#endif
#endif
diff --git a/upb/pb/textprinter.c b/upb/pb/textprinter.c
index 2fe3452..3770afc 100644
--- a/upb/pb/textprinter.c
+++ b/upb/pb/textprinter.c
@@ -5,11 +5,14 @@
* Author: Josh Haberman <jhaberman@gmail.com>
*/
+#include "upb/pb/textprinter.h"
+
#include <ctype.h>
#include <float.h>
#include <inttypes.h>
+#include <stdio.h>
#include <stdlib.h>
-#include "upb/pb/textprinter.h"
+#include <string.h>
struct _upb_textprinter {
upb_bytesink *sink;
@@ -20,7 +23,7 @@ struct _upb_textprinter {
#define CHECK(x) if ((x) < 0) goto err;
-static int upb_textprinter_indent(upb_textprinter *p) {
+static int indent(upb_textprinter *p) {
if (!p->single_line)
CHECK(upb_bytesink_putrepeated(p->sink, ' ', p->indent_depth*2));
return 0;
@@ -28,37 +31,32 @@ err:
return -1;
}
-static int upb_textprinter_endfield(upb_textprinter *p) {
+static int endfield(upb_textprinter *p) {
CHECK(upb_bytesink_putc(p->sink, p->single_line ? ' ' : '\n'));
return 0;
err:
return -1;
}
-static int upb_textprinter_putescaped(upb_textprinter *p,
- const upb_byteregion *bytes,
- bool preserve_utf8) {
+static int putescaped(upb_textprinter *p, const char *buf, size_t len,
+ bool preserve_utf8) {
// Based on CEscapeInternal() from Google's protobuf release.
- // TODO; we could read directly from a bytesrc's buffer instead.
- // TODO; we could write byteregions to the sink when possible.
- char dstbuf[512], *dst = dstbuf, *dstend = dstbuf + sizeof(dstbuf);
- char *buf = malloc(upb_byteregion_len(bytes)), *src = buf;
- char *end = src + upb_byteregion_len(bytes);
- upb_byteregion_copyall(bytes, buf);
+ char dstbuf[4096], *dst = dstbuf, *dstend = dstbuf + sizeof(dstbuf);
+ const char *end = buf + len;
// I think hex is prettier and more useful, but proto2 uses octal; should
// investigate whether it can parse hex also.
const bool use_hex = false;
bool last_hex_escape = false; // true if last output char was \xNN
- for (; src < end; src++) {
+ for (; buf < end; buf++) {
if (dstend - dst < 4) {
CHECK(upb_bytesink_write(p->sink, dstbuf, dst - dstbuf));
dst = dstbuf;
}
bool is_hex_escape = false;
- switch (*src) {
+ switch (*buf) {
case '\n': *(dst++) = '\\'; *(dst++) = 'n'; break;
case '\r': *(dst++) = '\\'; *(dst++) = 'r'; break;
case '\t': *(dst++) = '\\'; *(dst++) = 't'; break;
@@ -66,123 +64,123 @@ static int upb_textprinter_putescaped(upb_textprinter *p,
case '\'': *(dst++) = '\\'; *(dst++) = '\''; break;
case '\\': *(dst++) = '\\'; *(dst++) = '\\'; break;
default:
- // Note that if we emit \xNN and the src character after that is a hex
+ // Note that if we emit \xNN and the buf character after that is a hex
// digit then that digit must be escaped too to prevent it being
// interpreted as part of the character code by C.
- if ((!preserve_utf8 || (uint8_t)*src < 0x80) &&
- (!isprint(*src) || (last_hex_escape && isxdigit(*src)))) {
- sprintf(dst, (use_hex ? "\\x%02x" : "\\%03o"), (uint8_t)*src);
+ if ((!preserve_utf8 || (uint8_t)*buf < 0x80) &&
+ (!isprint(*buf) || (last_hex_escape && isxdigit(*buf)))) {
+ sprintf(dst, (use_hex ? "\\x%02x" : "\\%03o"), (uint8_t)*buf);
is_hex_escape = use_hex;
dst += 4;
} else {
- *(dst++) = *src; break;
+ *(dst++) = *buf; break;
}
}
last_hex_escape = is_hex_escape;
}
// Flush remaining data.
CHECK(upb_bytesink_write(p->sink, dst, dst - dstbuf));
- free(buf);
return 0;
err:
- free(buf);
return -1;
}
-#define TYPE(member, fmt) \
- static upb_flow_t upb_textprinter_put ## member(void *_p, upb_value fval, \
- upb_value val) { \
+#define TYPE(name, ctype, fmt) \
+ static bool put ## name(void *_p, void *fval, ctype val) { \
upb_textprinter *p = _p; \
- const upb_fielddef *f = upb_value_getfielddef(fval); \
- uint64_t start_ofs = upb_bytesink_getoffset(p->sink); \
- CHECK(upb_textprinter_indent(p)); \
+ const upb_fielddef *f = fval; \
+ CHECK(indent(p)); \
CHECK(upb_bytesink_writestr(p->sink, upb_fielddef_name(f))); \
CHECK(upb_bytesink_writestr(p->sink, ": ")); \
- CHECK(upb_bytesink_printf(p->sink, fmt, upb_value_get ## member(val))); \
- CHECK(upb_textprinter_endfield(p)); \
- return UPB_CONTINUE; \
+ CHECK(upb_bytesink_printf(p->sink, fmt, val)); \
+ CHECK(endfield(p)); \
+ return true; \
err: \
- upb_bytesink_rewind(p->sink, start_ofs); \
- return UPB_BREAK; \
+ return false; \
}
#define STRINGIFY_HELPER(x) #x
#define STRINGIFY_MACROVAL(x) STRINGIFY_HELPER(x)
-TYPE(double, "%." STRINGIFY_MACROVAL(DBL_DIG) "g")
-TYPE(float, "%." STRINGIFY_MACROVAL(FLT_DIG) "g")
-TYPE(int64, "%" PRId64)
-TYPE(uint64, "%" PRIu64)
-TYPE(int32, "%" PRId32)
-TYPE(uint32, "%" PRIu32);
-TYPE(bool, "%hhu");
+TYPE(int32, int32_t, "%" PRId32)
+TYPE(int64, int64_t, "%" PRId64)
+TYPE(uint32, uint32_t, "%" PRIu32);
+TYPE(uint64, uint64_t, "%" PRIu64)
+TYPE(float, float, "%." STRINGIFY_MACROVAL(FLT_DIG) "g")
+TYPE(double, double, "%." STRINGIFY_MACROVAL(DBL_DIG) "g")
+TYPE(bool, bool, "%hhu");
// Output a symbolic value from the enum if found, else just print as int32.
-static upb_flow_t upb_textprinter_putenum(void *_p, upb_value fval,
- upb_value val) {
+static bool putenum(void *_p, void *fval, int32_t val) {
upb_textprinter *p = _p;
- uint64_t start_ofs = upb_bytesink_getoffset(p->sink);
- const upb_fielddef *f = upb_value_getfielddef(fval);
- const upb_enumdef *enum_def =
- upb_downcast_enumdef_const(upb_fielddef_subdef(f));
- const char *label = upb_enumdef_iton(enum_def, upb_value_getint32(val));
+ const upb_fielddef *f = fval;
+ const upb_enumdef *enum_def = upb_downcast_enumdef(upb_fielddef_subdef(f));
+ const char *label = upb_enumdef_iton(enum_def, val);
if (label) {
CHECK(upb_bytesink_writestr(p->sink, label));
} else {
- CHECK(upb_textprinter_putint32(_p, fval, val));
+ CHECK(putint32(_p, fval, val));
}
- return UPB_CONTINUE;
+ return true;
err:
- upb_bytesink_rewind(p->sink, start_ofs);
- return UPB_BREAK;
+ return false;
}
-static upb_flow_t upb_textprinter_putstr(void *_p, upb_value fval,
- upb_value val) {
+static void *startstr(void *_p, void *fval, size_t size_hint) {
+ UPB_UNUSED(size_hint);
+ UPB_UNUSED(fval);
upb_textprinter *p = _p;
- uint64_t start_ofs = upb_bytesink_getoffset(p->sink);
- const upb_fielddef *f = upb_value_getfielddef(fval);
- CHECK(upb_bytesink_putc(p->sink, '"'));
- CHECK(upb_textprinter_putescaped(p, upb_value_getbyteregion(val),
- f->type == UPB_TYPE(STRING)));
CHECK(upb_bytesink_putc(p->sink, '"'));
- return UPB_CONTINUE;
+ return p;
err:
- upb_bytesink_rewind(p->sink, start_ofs);
return UPB_BREAK;
}
-static upb_sflow_t upb_textprinter_startsubmsg(void *_p, upb_value fval) {
+static bool endstr(void *_p, void *fval) {
+ UPB_UNUSED(fval);
+ upb_textprinter *p = _p;
+ CHECK(upb_bytesink_putc(p->sink, '"'));
+ return true;
+err:
+ return false;
+}
+
+static size_t putstr(void *_p, void *fval, const char *buf, size_t len) {
upb_textprinter *p = _p;
- uint64_t start_ofs = upb_bytesink_getoffset(p->sink);
- const upb_fielddef *f = upb_value_getfielddef(fval);
- CHECK(upb_textprinter_indent(p));
+ const upb_fielddef *f = fval;
+ CHECK(putescaped(p, buf, len, upb_fielddef_type(f) == UPB_TYPE(STRING)));
+ return len;
+err:
+ return 0;
+}
+
+static void *startsubmsg(void *_p, void *fval) {
+ upb_textprinter *p = _p;
+ const upb_fielddef *f = fval;
+ CHECK(indent(p));
CHECK(upb_bytesink_printf(p->sink, "%s {", upb_fielddef_name(f)));
if (!p->single_line)
CHECK(upb_bytesink_putc(p->sink, '\n'));
p->indent_depth++;
- return UPB_CONTINUE_WITH(_p);
+ return _p;
err:
- upb_bytesink_rewind(p->sink, start_ofs);
- return UPB_SBREAK;
+ return UPB_BREAK;
}
-static upb_flow_t upb_textprinter_endsubmsg(void *_p, upb_value fval) {
- (void)fval;
+static bool endsubmsg(void *_p, void *fval) {
+ UPB_UNUSED(fval);
upb_textprinter *p = _p;
- uint64_t start_ofs = upb_bytesink_getoffset(p->sink);
p->indent_depth--;
- CHECK(upb_textprinter_indent(p));
+ CHECK(indent(p));
CHECK(upb_bytesink_putc(p->sink, '}'));
- CHECK(upb_textprinter_endfield(p));
- return UPB_CONTINUE;
+ CHECK(endfield(p));
+ return true;
err:
- upb_bytesink_rewind(p->sink, start_ofs);
- return UPB_BREAK;
+ return false;
}
-upb_textprinter *upb_textprinter_new(void) {
+upb_textprinter *upb_textprinter_new() {
upb_textprinter *p = malloc(sizeof(*p));
return p;
}
@@ -196,22 +194,61 @@ void upb_textprinter_reset(upb_textprinter *p, upb_bytesink *sink,
p->indent_depth = 0;
}
-static void upb_textprinter_onfreg(void *c, upb_fhandlers *fh, const upb_fielddef *f) {
+static void onmreg(void *c, upb_handlers *h) {
(void)c;
- upb_fhandlers_setstartsubmsg(fh, &upb_textprinter_startsubmsg);
- upb_fhandlers_setendsubmsg(fh, &upb_textprinter_endsubmsg);
-#define F(type) &upb_textprinter_put ## type
- static upb_value_handler *fptrs[] = {NULL, F(double), F(float), F(int64),
- F(uint64), F(int32), F(uint64), F(uint32), F(bool), F(str),
- NULL, NULL, F(str), F(uint32), F(enum), F(int32),
- F(int64), F(int32), F(int64)};
- upb_fhandlers_setvalue(fh, fptrs[f->type]);
- upb_value fval;
- upb_value_setfielddef(&fval, f);
- upb_fhandlers_setfval(fh, fval);
+ const upb_msgdef *m = upb_handlers_msgdef(h);
+ upb_msg_iter i;
+ for(upb_msg_begin(&i, m); !upb_msg_done(&i); upb_msg_next(&i)) {
+ upb_fielddef *f = upb_msg_iter_field(&i);
+ switch (upb_fielddef_type(f)) {
+ case UPB_TYPE_INT32:
+ case UPB_TYPE_SINT32:
+ case UPB_TYPE_SFIXED32:
+ upb_handlers_setint32(h, f, putint32, f, NULL);
+ break;
+ case UPB_TYPE_SINT64:
+ case UPB_TYPE_SFIXED64:
+ case UPB_TYPE_INT64:
+ upb_handlers_setint64(h, f, putint64, f, NULL);
+ break;
+ case UPB_TYPE_UINT32:
+ case UPB_TYPE_FIXED32:
+ upb_handlers_setuint32(h, f, putuint32, f, NULL);
+ break;
+ case UPB_TYPE_UINT64:
+ case UPB_TYPE_FIXED64:
+ upb_handlers_setuint64(h, f, putuint64, f, NULL);
+ break;
+ case UPB_TYPE_FLOAT:
+ upb_handlers_setfloat(h, f, putfloat, f, NULL);
+ break;
+ case UPB_TYPE_DOUBLE:
+ upb_handlers_setdouble(h, f, putdouble, f, NULL);
+ break;
+ case UPB_TYPE_BOOL:
+ upb_handlers_setbool(h, f, putbool, f, NULL);
+ break;
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES:
+ upb_handlers_setstartstr(h, f, startstr, f, NULL);
+ upb_handlers_setstring(h, f, putstr, f, NULL);
+ upb_handlers_setendstr(h, f, endstr, f, NULL);
+ break;
+ case UPB_TYPE_GROUP:
+ case UPB_TYPE_MESSAGE:
+ upb_handlers_setstartsubmsg(h, f, &startsubmsg, f, NULL);
+ upb_handlers_setendsubmsg(h, f, &endsubmsg, f, NULL);
+ break;
+ case UPB_TYPE_ENUM:
+ upb_handlers_setint32(h, f, putenum, f, NULL);
+ default:
+ assert(false);
+ break;
+ }
+ }
}
-upb_mhandlers *upb_textprinter_reghandlers(upb_handlers *h, const upb_msgdef *m) {
- return upb_handlers_regmsgdef(
- h, m, NULL, &upb_textprinter_onfreg, NULL);
+const upb_handlers *upb_textprinter_newhandlers(const void *owner,
+ const upb_msgdef *m) {
+ return upb_handlers_newfrozen(m, owner, &onmreg, NULL);
}
diff --git a/upb/pb/textprinter.h b/upb/pb/textprinter.h
index 174148e..6d111d2 100644
--- a/upb/pb/textprinter.h
+++ b/upb/pb/textprinter.h
@@ -18,11 +18,12 @@ extern "C" {
struct _upb_textprinter;
typedef struct _upb_textprinter upb_textprinter;
-upb_textprinter *upb_textprinter_new(void);
+upb_textprinter *upb_textprinter_new();
void upb_textprinter_free(upb_textprinter *p);
void upb_textprinter_reset(upb_textprinter *p, upb_bytesink *sink,
bool single_line);
-upb_mhandlers *upb_textprinter_reghandlers(upb_handlers *h, const upb_msgdef *m);
+const upb_handlers *upb_textprinter_newhandlers(const void *owner,
+ const upb_msgdef *m);
#ifdef __cplusplus
} /* extern "C" */
diff --git a/upb/pb/varint.c b/upb/pb/varint.c
index 45caec1..d6d6161 100644
--- a/upb/pb/varint.c
+++ b/upb/pb/varint.c
@@ -7,16 +7,64 @@
#include "upb/pb/varint.h"
+// A basic branch-based decoder, uses 32-bit values to get good performance
+// on 32-bit architectures (but performs well on 64-bits also).
+// This scheme comes from the original Google Protobuf implementation (proto2).
+upb_decoderet upb_vdecode_max8_branch32(upb_decoderet r) {
+ upb_decoderet err = {NULL, 0};
+ const char *p = r.p;
+ uint32_t low = (uint32_t)r.val;
+ uint32_t high = 0;
+ uint32_t b;
+ b = *(p++); low |= (b & 0x7fU) << 14; if (!(b & 0x80)) goto done;
+ b = *(p++); low |= (b & 0x7fU) << 21; if (!(b & 0x80)) goto done;
+ b = *(p++); low |= (b & 0x7fU) << 28;
+ high = (b & 0x7fU) >> 4; if (!(b & 0x80)) goto done;
+ b = *(p++); high |= (b & 0x7fU) << 3; if (!(b & 0x80)) goto done;
+ b = *(p++); high |= (b & 0x7fU) << 10; if (!(b & 0x80)) goto done;
+ b = *(p++); high |= (b & 0x7fU) << 17; if (!(b & 0x80)) goto done;
+ b = *(p++); high |= (b & 0x7fU) << 24; if (!(b & 0x80)) goto done;
+ b = *(p++); high |= (b & 0x7fU) << 31; if (!(b & 0x80)) goto done;
+ return err;
+
+done:
+ r.val = ((uint64_t)high << 32) | low;
+ r.p = p;
+ return r;
+}
+
+// Like the previous, but uses 64-bit values.
+upb_decoderet upb_vdecode_max8_branch64(upb_decoderet r) {
+ const char *p = r.p;
+ uint64_t val = r.val;
+ uint64_t b;
+ upb_decoderet err = {NULL, 0};
+ b = *(p++); val |= (b & 0x7fU) << 14; if (!(b & 0x80)) goto done;
+ b = *(p++); val |= (b & 0x7fU) << 21; if (!(b & 0x80)) goto done;
+ b = *(p++); val |= (b & 0x7fU) << 28; if (!(b & 0x80)) goto done;
+ b = *(p++); val |= (b & 0x7fU) << 35; if (!(b & 0x80)) goto done;
+ b = *(p++); val |= (b & 0x7fU) << 42; if (!(b & 0x80)) goto done;
+ b = *(p++); val |= (b & 0x7fU) << 49; if (!(b & 0x80)) goto done;
+ b = *(p++); val |= (b & 0x7fU) << 56; if (!(b & 0x80)) goto done;
+ b = *(p++); val |= (b & 0x7fU) << 63; if (!(b & 0x80)) goto done;
+ return err;
+
+done:
+ r.val = val;
+ r.p = p;
+ return r;
+}
+
// Given an encoded varint v, returns an integer with a single bit set that
// indicates the end of the varint. Subtracting one from this value will
// yield a mask that leaves only bits that are part of the varint. Returns
// 0 if the varint is unterminated.
-INLINE uint64_t upb_get_vstopbit(uint64_t v) {
+static uint64_t upb_get_vstopbit(uint64_t v) {
uint64_t cbits = v | 0x7f7f7f7f7f7f7f7fULL;
return ~cbits & (cbits+1);
}
-INLINE uint64_t upb_get_vmask(uint64_t v) { return upb_get_vstopbit(v) - 1; }
+// A branchless decoder. Credit to Pascal Massimino for the bit-twiddling.
upb_decoderet upb_vdecode_max8_massimino(upb_decoderet r) {
uint64_t b;
memcpy(&b, r.p, sizeof(b));
@@ -35,14 +83,15 @@ upb_decoderet upb_vdecode_max8_massimino(upb_decoderet r) {
return my_r;
}
+// A branchless decoder. Credit to Daniel Wright for the bit-twiddling.
upb_decoderet upb_vdecode_max8_wright(upb_decoderet r) {
uint64_t b;
memcpy(&b, r.p, sizeof(b));
uint64_t stop_bit = upb_get_vstopbit(b);
b &= (stop_bit - 1);
- b = ((b & 0x7f007f007f007f00) >> 1) | (b & 0x007f007f007f007f);
- b = ((b & 0xffff0000ffff0000) >> 2) | (b & 0x0000ffff0000ffff);
- b = ((b & 0xffffffff00000000) >> 4) | (b & 0x00000000ffffffff);
+ b = ((b & 0x7f007f007f007f00ULL) >> 1) | (b & 0x007f007f007f007fULL);
+ b = ((b & 0xffff0000ffff0000ULL) >> 2) | (b & 0x0000ffff0000ffffULL);
+ b = ((b & 0xffffffff00000000ULL) >> 4) | (b & 0x00000000ffffffffULL);
if (stop_bit == 0) {
// Error: unterminated varint.
upb_decoderet err_r = {(void*)0, 0};
diff --git a/upb/pb/varint.h b/upb/pb/varint.h
index c0e0134..c4d67ba 100644
--- a/upb/pb/varint.h
+++ b/upb/pb/varint.h
@@ -49,71 +49,32 @@ typedef struct {
uint64_t val;
} upb_decoderet;
-// A basic branch-based decoder, uses 32-bit values to get good performance
-// on 32-bit architectures (but performs well on 64-bits also).
-INLINE upb_decoderet upb_vdecode_branch32(const char *p) {
- upb_decoderet r = {NULL, 0};
- uint32_t low, high = 0;
- uint32_t b;
- b = *(p++); low = (b & 0x7f) ; if(!(b & 0x80)) goto done;
- b = *(p++); low |= (b & 0x7f) << 7; if(!(b & 0x80)) goto done;
- b = *(p++); low |= (b & 0x7f) << 14; if(!(b & 0x80)) goto done;
- b = *(p++); low |= (b & 0x7f) << 21; if(!(b & 0x80)) goto done;
- b = *(p++); low |= (b & 0x7f) << 28;
- high = (b & 0x7f) >> 4; if(!(b & 0x80)) goto done;
- b = *(p++); high |= (b & 0x7f) << 3; if(!(b & 0x80)) goto done;
- b = *(p++); high |= (b & 0x7f) << 10; if(!(b & 0x80)) goto done;
- b = *(p++); high |= (b & 0x7f) << 17; if(!(b & 0x80)) goto done;
- b = *(p++); high |= (b & 0x7f) << 24; if(!(b & 0x80)) goto done;
- b = *(p++); high |= (b & 0x7f) << 31; if(!(b & 0x80)) goto done;
- return r;
-
-done:
- r.val = ((uint64_t)high << 32) | low;
- r.p = p;
- return r;
-}
-
-// Like the previous, but uses 64-bit values.
-INLINE upb_decoderet upb_vdecode_branch64(const char *p) {
- uint64_t val;
- uint64_t b;
- upb_decoderet r = {NULL, 0};
- b = *(p++); val = (b & 0x7f) ; if(!(b & 0x80)) goto done;
- b = *(p++); val |= (b & 0x7f) << 7; if(!(b & 0x80)) goto done;
- b = *(p++); val |= (b & 0x7f) << 14; if(!(b & 0x80)) goto done;
- b = *(p++); val |= (b & 0x7f) << 21; if(!(b & 0x80)) goto done;
- b = *(p++); val |= (b & 0x7f) << 28; if(!(b & 0x80)) goto done;
- b = *(p++); val |= (b & 0x7f) << 35; if(!(b & 0x80)) goto done;
- b = *(p++); val |= (b & 0x7f) << 42; if(!(b & 0x80)) goto done;
- b = *(p++); val |= (b & 0x7f) << 49; if(!(b & 0x80)) goto done;
- b = *(p++); val |= (b & 0x7f) << 56; if(!(b & 0x80)) goto done;
- b = *(p++); val |= (b & 0x7f) << 63; if(!(b & 0x80)) goto done;
- return r;
-
-done:
- r.val = val;
- r.p = p;
- return r;
-}
-
-// Decodes a varint of at most 8 bytes without branching (except for error).
+// Four functions for decoding a varint of at most eight bytes. They are all
+// functionally identical, but are implemented in different ways and likely have
+// different performance profiles. We keep them around for performance testing.
+//
+// Note that these functions may not read byte-by-byte, so they must not be used
+// unless there are at least eight bytes left in the buffer!
+upb_decoderet upb_vdecode_max8_branch32(upb_decoderet r);
+upb_decoderet upb_vdecode_max8_branch64(upb_decoderet r);
upb_decoderet upb_vdecode_max8_wright(upb_decoderet r);
-
-// Another implementation of the previous.
upb_decoderet upb_vdecode_max8_massimino(upb_decoderet r);
// Template for a function that checks the first two bytes with branching
-// and dispatches 2-10 bytes with a separate function.
-#define UPB_VARINT_DECODER_CHECK2(name, decode_max8_function) \
-INLINE upb_decoderet upb_vdecode_check2_ ## name(const char *_p) { \
- uint8_t *p = (uint8_t*)_p; \
- if ((*p & 0x80) == 0) { upb_decoderet r = {_p + 1, *p & 0x7f}; return r; } \
- upb_decoderet r = {_p + 2, (*p & 0x7f) | ((*(p + 1) & 0x7f) << 7)}; \
- if ((*(p + 1) & 0x80) == 0) return r; \
- return decode_max8_function(r); \
+// and dispatches 2-10 bytes with a separate function. Note that this may read
+// up to 10 bytes, so it must not be used unless there are at least ten bytes
+// left in the buffer!
+#define UPB_VARINT_DECODER_CHECK2(name, decode_max8_function) \
+INLINE upb_decoderet upb_vdecode_check2_ ## name(const char *_p) { \
+ uint8_t *p = (uint8_t*)_p; \
+ if ((*p & 0x80) == 0) { upb_decoderet r = {_p + 1, *p & 0x7fU}; return r; } \
+ upb_decoderet r = {_p + 2, (*p & 0x7fU) | ((*(p + 1) & 0x7fU) << 7)}; \
+ if ((*(p + 1) & 0x80) == 0) return r; \
+ return decode_max8_function(r); \
}
+UPB_VARINT_DECODER_CHECK2(branch32, upb_vdecode_max8_branch32);
+UPB_VARINT_DECODER_CHECK2(branch64, upb_vdecode_max8_branch64);
UPB_VARINT_DECODER_CHECK2(wright, upb_vdecode_max8_wright);
UPB_VARINT_DECODER_CHECK2(massimino, upb_vdecode_max8_massimino);
#undef UPB_VARINT_DECODER_CHECK2
@@ -121,11 +82,10 @@ UPB_VARINT_DECODER_CHECK2(massimino, upb_vdecode_max8_massimino);
// Our canonical functions for decoding varints, based on the currently
// favored best-performing implementations.
INLINE upb_decoderet upb_vdecode_fast(const char *p) {
- // Use nobranch2 on 64-bit, branch32 on 32-bit.
if (sizeof(long) == 8)
return upb_vdecode_check2_massimino(p);
else
- return upb_vdecode_branch32(p);
+ return upb_vdecode_check2_branch32(p);
}
INLINE upb_decoderet upb_vdecode_max8_fast(upb_decoderet r) {
@@ -154,9 +114,9 @@ INLINE size_t upb_vencode64(uint64_t val, char *buf) {
if (val == 0) { buf[0] = 0; return 1; }
size_t i = 0;
while (val) {
- uint8_t byte = val & 0x7f;
+ uint8_t byte = val & 0x7fU;
val >>= 7;
- if (val) byte |= 0x80;
+ if (val) byte |= 0x80U;
buf[i++] = byte;
}
return i;
@@ -169,7 +129,7 @@ INLINE uint64_t upb_vencode32(uint32_t val) {
uint64_t ret = 0;
assert(bytes <= 5);
memcpy(&ret, buf, bytes);
- assert(ret <= 0xffffffffff);
+ assert(ret <= 0xffffffffffU);
return ret;
}
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback