Protocol Buffers - Google's data interchange format (grpc依赖)
https://developers.google.com/protocol-buffers/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
423 lines
11 KiB
423 lines
11 KiB
/* |
|
* upb - a minimalist implementation of protocol buffers. |
|
* |
|
* Copyright (c) 2011-2012 Google Inc. See LICENSE for details. |
|
* Author: Josh Haberman <jhaberman@gmail.com> |
|
*/ |
|
|
|
#include "upb/sink.h" |
|
|
|
#include <stdlib.h> |
|
#include <string.h> |
|
|
|
static void upb_sink_init(upb_sink *s, const upb_handlers *h, upb_pipeline *p); |
|
static void upb_sink_resetobj(void *obj); |
|
static const upb_frametype upb_sink_frametype; |
|
|
|
static bool chkstack(upb_sink *s) { |
|
if (s->top + 1 >= s->limit) { |
|
upb_status_seterrliteral(&s->pipeline_->status_, "Nesting too deep."); |
|
return false; |
|
} else { |
|
return true; |
|
} |
|
} |
|
|
|
#define alignof(type) offsetof (struct { char c; type member; }, member) |
|
|
|
typedef union { double u; void *p; long l; } maxalign_t; |
|
static const size_t maxalign = alignof(maxalign_t); |
|
|
|
static void *align_up(void *p) { |
|
if (!p) return NULL; |
|
uintptr_t val = (uintptr_t)p; |
|
uintptr_t aligned = |
|
val % maxalign == 0 ? val : val + maxalign - (val % maxalign); |
|
return (void*)aligned; |
|
} |
|
|
|
void *upb_realloc(void *ud, void *ptr, size_t size) { |
|
UPB_UNUSED(ud); |
|
return realloc(ptr, size); |
|
} |
|
|
|
|
|
/* upb_pipeline ***************************************************************/ |
|
|
|
// For the moment we get fixed-size blocks of this size, but we could change |
|
// this strategy if necessary. |
|
#define BLOCK_SIZE 8192 |
|
|
|
struct region { |
|
struct region *prev; |
|
maxalign_t data[1]; // Region data follows. |
|
}; |
|
|
|
size_t regionsize(size_t usable_size) { |
|
return sizeof(struct region) - sizeof(maxalign_t) + usable_size; |
|
} |
|
|
|
struct obj { |
|
struct obj *prev; |
|
const upb_frametype *ft; |
|
maxalign_t data; // Region data follows. |
|
}; |
|
|
|
size_t objsize(size_t memsize) { |
|
return sizeof(struct obj) - sizeof(maxalign_t) + memsize; |
|
} |
|
|
|
void upb_pipeline_init(upb_pipeline *p, void *initial_mem, size_t initial_size, |
|
void *(*realloc)(void *ud, void *ptr, size_t bytes), |
|
void *ud) { |
|
p->realloc = realloc; |
|
p->ud = ud; |
|
p->bump_top = initial_mem; |
|
p->bump_limit = initial_mem ? initial_mem + initial_size : NULL; |
|
p->region_head = NULL; |
|
p->obj_head = NULL; |
|
p->last_alloc = NULL; |
|
upb_status_init(&p->status_); |
|
} |
|
|
|
void upb_pipeline_uninit(upb_pipeline *p) { |
|
for (struct obj *o = p->obj_head; o; o = o->prev) { |
|
if (o->ft->uninit) |
|
o->ft->uninit(&o->data); |
|
} |
|
|
|
for (struct region *r = p->region_head; r; ) { |
|
struct region *prev = r->prev; |
|
p->realloc(p->ud, r, 0); |
|
r = prev; |
|
} |
|
upb_status_uninit(&p->status_); |
|
} |
|
|
|
void *upb_pipeline_alloc(upb_pipeline *p, size_t bytes) { |
|
void *mem = align_up(p->bump_top); |
|
if (!mem || mem > p->bump_limit || p->bump_limit - mem < bytes) { |
|
size_t size = regionsize(UPB_MAX(BLOCK_SIZE, bytes)); |
|
struct region *r; |
|
if (!p->realloc || !(r = p->realloc(p->ud, NULL, size))) { |
|
return NULL; |
|
} |
|
r->prev = p->region_head; |
|
p->region_head = r; |
|
p->bump_limit = (char*)r + size; |
|
mem = &r->data[0]; |
|
assert(p->bump_limit > mem); |
|
assert(p->bump_limit - mem >= bytes); |
|
} |
|
p->bump_top = mem + bytes; |
|
p->last_alloc = mem; |
|
return mem; |
|
} |
|
|
|
void *upb_pipeline_realloc(upb_pipeline *p, void *ptr, |
|
size_t oldsize, size_t bytes) { |
|
if (ptr && ptr == p->last_alloc && |
|
p->bump_limit - ptr >= bytes) { |
|
p->bump_top = ptr + bytes; |
|
return ptr; |
|
} else { |
|
void *mem = upb_pipeline_alloc(p, bytes); |
|
memcpy(mem, ptr, oldsize); |
|
return mem; |
|
} |
|
} |
|
|
|
void *upb_pipeline_allocobj(upb_pipeline *p, const upb_frametype *ft) { |
|
struct obj *obj = upb_pipeline_alloc(p, objsize(ft->size)); |
|
if (!obj) return NULL; |
|
|
|
obj->prev = p->obj_head; |
|
obj->ft = ft; |
|
p->obj_head = obj; |
|
if (ft->init) ft->init(&obj->data, p); |
|
return &obj->data; |
|
} |
|
|
|
void upb_pipeline_reset(upb_pipeline *p) { |
|
upb_status_clear(&p->status_); |
|
for (struct obj *o = p->obj_head; o; o = o->prev) { |
|
if (o->ft->reset) |
|
o->ft->reset(&o->data); |
|
} |
|
} |
|
|
|
upb_sink *upb_pipeline_newsink(upb_pipeline *p, const upb_handlers *handlers) { |
|
upb_sink *s = upb_pipeline_allocobj(p, &upb_sink_frametype); |
|
upb_sink_init(s, handlers, p); |
|
return s; |
|
} |
|
|
|
const upb_status *upb_pipeline_status(const upb_pipeline *p) { |
|
return &p->status_; |
|
} |
|
|
|
typedef struct { |
|
const upb_handlers *h; |
|
} handlersref_t; |
|
|
|
static void freehandlersref(void *r) { |
|
handlersref_t *ref = r; |
|
upb_handlers_unref(ref->h, &ref->h); |
|
} |
|
|
|
static const upb_frametype handlersref_frametype = { |
|
sizeof(handlersref_t), |
|
NULL, |
|
freehandlersref, |
|
NULL, |
|
}; |
|
|
|
void upb_pipeline_donateref( |
|
upb_pipeline *p, const upb_handlers *h, const void *owner) { |
|
handlersref_t *ref = upb_pipeline_allocobj(p, &handlersref_frametype); |
|
upb_handlers_donateref(h, owner, &ref->h); |
|
ref->h = h; |
|
} |
|
|
|
|
|
/* upb_sink *******************************************************************/ |
|
|
|
static const upb_frametype upb_sink_frametype = { |
|
sizeof(upb_sink), |
|
NULL, |
|
NULL, |
|
upb_sink_resetobj, |
|
}; |
|
|
|
void upb_sink_reset(upb_sink *s, void *closure) { |
|
s->top = s->stack; |
|
s->top->closure = closure; |
|
} |
|
|
|
static void upb_sink_resetobj(void *obj) { |
|
upb_sink *s = obj; |
|
s->top = s->stack; |
|
} |
|
|
|
static void upb_sink_init(upb_sink *s, const upb_handlers *h, upb_pipeline *p) { |
|
s->pipeline_ = p; |
|
s->stack = upb_pipeline_alloc(p, sizeof(*s->stack) * UPB_MAX_NESTING); |
|
s->top = s->stack; |
|
s->limit = s->stack + UPB_MAX_NESTING; |
|
s->top->h = h; |
|
if (h->ft) { |
|
s->top->closure = upb_pipeline_allocobj(p, h->ft); |
|
} |
|
} |
|
|
|
upb_pipeline *upb_sink_pipeline(const upb_sink *s) { |
|
return s->pipeline_; |
|
} |
|
|
|
void *upb_sink_getobj(const upb_sink *s) { |
|
return s->stack[0].closure; |
|
} |
|
|
|
bool upb_sink_startmsg(upb_sink *s) { |
|
const upb_handlers *h = s->top->h; |
|
upb_startmsg_handler *startmsg = |
|
(upb_startmsg_handler *)upb_handlers_gethandler(h, UPB_STARTMSG_SELECTOR); |
|
if (startmsg) { |
|
const void *hd = upb_handlers_gethandlerdata(h, UPB_STARTMSG_SELECTOR); |
|
bool ok = startmsg(s->top->closure, hd); |
|
if (!ok) return false; |
|
} |
|
return true; |
|
} |
|
|
|
bool upb_sink_endmsg(upb_sink *s) { |
|
assert(s->top == s->stack); |
|
const upb_handlers *h = s->top->h; |
|
upb_endmsg_handler *endmsg = |
|
(upb_endmsg_handler *)upb_handlers_gethandler(h, UPB_ENDMSG_SELECTOR); |
|
if (endmsg) { |
|
const void *hd = upb_handlers_gethandlerdata(h, UPB_ENDMSG_SELECTOR); |
|
bool ok = endmsg(s->top->closure, hd, &s->pipeline_->status_); |
|
if (!ok) return false; |
|
} |
|
return true; |
|
} |
|
|
|
#define PUTVAL(type, ctype) \ |
|
bool upb_sink_put ## type(upb_sink *s, upb_selector_t sel, ctype val) { \ |
|
const upb_handlers *h = s->top->h; \ |
|
upb_ ## type ## _handler *handler = (upb_ ## type ## _handler*) \ |
|
upb_handlers_gethandler(h, sel); \ |
|
if (handler) { \ |
|
const void *hd = upb_handlers_gethandlerdata(h, sel); \ |
|
bool ok = handler(s->top->closure, hd, val); \ |
|
if (!ok) return false; \ |
|
} \ |
|
return true; \ |
|
} |
|
|
|
PUTVAL(int32, int32_t); |
|
PUTVAL(int64, int64_t); |
|
PUTVAL(uint32, uint32_t); |
|
PUTVAL(uint64, uint64_t); |
|
PUTVAL(float, float); |
|
PUTVAL(double, double); |
|
PUTVAL(bool, bool); |
|
#undef PUTVAL |
|
|
|
size_t upb_sink_putstring(upb_sink *s, upb_selector_t sel, |
|
const char *buf, size_t n) { |
|
const upb_handlers *h = s->top->h; |
|
upb_string_handler *handler = |
|
(upb_string_handler*)upb_handlers_gethandler(h, sel); |
|
|
|
if (handler) { |
|
const void *hd = upb_handlers_gethandlerdata(h, sel);; |
|
n = handler(s->top->closure, hd, buf, n); |
|
} |
|
|
|
return n; |
|
} |
|
|
|
bool upb_sink_startseq(upb_sink *s, upb_selector_t sel) { |
|
if (!chkstack(s)) return false; |
|
|
|
void *subc = s->top->closure; |
|
const upb_handlers *h = s->top->h; |
|
upb_startfield_handler *startseq = |
|
(upb_startfield_handler*)upb_handlers_gethandler(h, sel); |
|
|
|
if (startseq) { |
|
const void *hd = upb_handlers_gethandlerdata(h, sel); |
|
subc = startseq(s->top->closure, hd); |
|
if (subc == UPB_BREAK) { |
|
return false; |
|
} |
|
} |
|
|
|
s->top->selector = upb_handlers_getendselector(sel); |
|
++s->top; |
|
s->top->h = h; |
|
s->top->closure = subc; |
|
return true; |
|
} |
|
|
|
bool upb_sink_endseq(upb_sink *s, upb_selector_t sel) { |
|
--s->top; |
|
assert(sel == s->top->selector); |
|
|
|
const upb_handlers *h = s->top->h; |
|
upb_endfield_handler *endseq = |
|
(upb_endfield_handler*)upb_handlers_gethandler(h, sel); |
|
|
|
if (endseq) { |
|
const void *hd = upb_handlers_gethandlerdata(h, sel); |
|
bool ok = endseq(s->top->closure, hd); |
|
if (!ok) { |
|
++s->top; |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool upb_sink_startstr(upb_sink *s, upb_selector_t sel, size_t size_hint) { |
|
if (!chkstack(s)) return false; |
|
|
|
void *subc = s->top->closure; |
|
const upb_handlers *h = s->top->h; |
|
upb_startstr_handler *startstr = |
|
(upb_startstr_handler*)upb_handlers_gethandler(h, sel); |
|
|
|
if (startstr) { |
|
const void *hd = upb_handlers_gethandlerdata(h, sel); |
|
subc = startstr(s->top->closure, hd, size_hint); |
|
if (subc == UPB_BREAK) { |
|
return false; |
|
} |
|
} |
|
|
|
s->top->selector = upb_handlers_getendselector(sel); |
|
++s->top; |
|
s->top->h = h; |
|
s->top->closure = subc; |
|
return true; |
|
} |
|
|
|
bool upb_sink_endstr(upb_sink *s, upb_selector_t sel) { |
|
--s->top; |
|
assert(sel == s->top->selector); |
|
const upb_handlers *h = s->top->h; |
|
upb_endfield_handler *endstr = |
|
(upb_endfield_handler*)upb_handlers_gethandler(h, sel); |
|
|
|
if (endstr) { |
|
const void *hd = upb_handlers_gethandlerdata(h, sel); |
|
bool ok = endstr(s->top->closure, hd); |
|
if (!ok) { |
|
++s->top; |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool upb_sink_startsubmsg(upb_sink *s, upb_selector_t sel) { |
|
if (!chkstack(s)) return false; |
|
|
|
void *subc = s->top->closure; |
|
const upb_handlers *h = s->top->h; |
|
upb_startfield_handler *startsubmsg = |
|
(upb_startfield_handler*)upb_handlers_gethandler(h, sel); |
|
|
|
if (startsubmsg) { |
|
const void *hd = upb_handlers_gethandlerdata(h, sel); |
|
subc = startsubmsg(s->top->closure, hd); |
|
if (subc == UPB_BREAK) { |
|
return false; |
|
} |
|
} |
|
|
|
s->top->selector= upb_handlers_getendselector(sel); |
|
++s->top; |
|
s->top->h = upb_handlers_getsubhandlers_sel(h, sel); |
|
// TODO: should add support for submessages without any handlers |
|
assert(s->top->h); |
|
s->top->closure = subc; |
|
upb_sink_startmsg(s); |
|
return true; |
|
} |
|
|
|
bool upb_sink_endsubmsg(upb_sink *s, upb_selector_t sel) { |
|
upb_endmsg_handler *endmsg = (upb_endmsg_handler *)upb_handlers_gethandler( |
|
s->top->h, UPB_ENDMSG_SELECTOR); |
|
if (endmsg) { |
|
// TODO(haberman): check return value. |
|
const void *hd = |
|
upb_handlers_gethandlerdata(s->top->h, UPB_ENDMSG_SELECTOR); |
|
endmsg(s->top->closure, hd, &s->pipeline_->status_); |
|
} |
|
--s->top; |
|
|
|
assert(sel == s->top->selector); |
|
const upb_handlers *h = s->top->h; |
|
upb_endfield_handler *endsubmsg = |
|
(upb_endfield_handler*)upb_handlers_gethandler(h, sel); |
|
|
|
if (endsubmsg) { |
|
const void *hd = upb_handlers_gethandlerdata(h, sel); |
|
bool ok = endsubmsg(s->top->closure, hd); |
|
if (!ok) { |
|
++s->top; |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
const upb_handlers *upb_sink_tophandlers(upb_sink *s) { |
|
return s->top->h; |
|
}
|
|
|