Many improvements, too many to mention. One significant perf regression warrants investigation: omitfp.parsetoproto2_googlemessage1.upb_jit: 343 -> 252 (-26.53) plain.parsetoproto2_googlemessage1.upb_jit: 334 -> 251 (-24.85) 25% regression for this benchmark is bad, but since I don't think there's any fundamental design issue that caused it I'm going to go ahead with the commit anyway. Can investigate and fix later. Other benchmarks were neutral or showed slight improvement.pull/13171/head
parent
db59a5198f
commit
86bad61b76
49 changed files with 4584 additions and 3633 deletions
@ -1,320 +1,61 @@ |
||||
// This file is a crime against software engineering. It breaks the
|
||||
// encapsulation of proto2 in numerous ways, violates the C++ standard
|
||||
// in others, and generally deserves to have comtempt and scorn heaped
|
||||
// upon it.
|
||||
//
|
||||
// Its purpose is to get an accurate benchmark for how fast upb can
|
||||
// parse into proto2 data structures. To add proper support for this
|
||||
// functionality, proto2 would need to expose actual support for the
|
||||
// operations we are trying to perform here.
|
||||
// Tests speed of upb parsing into proto2 generated classes.
|
||||
|
||||
#define __STDC_LIMIT_MACROS 1 |
||||
#include "main.c" |
||||
|
||||
#include <stdint.h> |
||||
#include "upb/bytestream.h" |
||||
#include "upb/def.h" |
||||
#include "upb/msg.h" |
||||
#include "upb/pb/decoder.h" |
||||
#include "upb/bytestream.hpp" |
||||
#include "upb/def.hpp" |
||||
#include "upb/msg.hpp" |
||||
#include "upb/pb/decoder.hpp" |
||||
#include "upb/pb/glue.h" |
||||
|
||||
// Need to violate the encapsulation of GeneratedMessageReflection -- see below.
|
||||
#define private public |
||||
#include "upb/proto2_bridge.hpp" |
||||
#include MESSAGE_HFILE |
||||
#include <google/protobuf/descriptor.h> |
||||
#undef private |
||||
|
||||
static size_t len; |
||||
const char *str; |
||||
size_t len; |
||||
MESSAGE_CIDENT msg[NUM_MESSAGES]; |
||||
MESSAGE_CIDENT msg2; |
||||
static upb_stringsrc strsrc; |
||||
static upb_decoder d; |
||||
static const upb_msgdef *def; |
||||
static upb_decoderplan *p; |
||||
char *str; |
||||
|
||||
#define PROTO2_APPEND(type, ctype) \ |
||||
upb_flow_t proto2_append_ ## type(void *_r, upb_value fval, upb_value val) { \
|
||||
(void)fval; \
|
||||
typedef google::protobuf::RepeatedField<ctype> R; \
|
||||
R *r = (R*)_r; \
|
||||
r->Add(upb_value_get ## type(val)); \
|
||||
return UPB_CONTINUE; \
|
||||
} |
||||
|
||||
PROTO2_APPEND(double, double) |
||||
PROTO2_APPEND(float, float) |
||||
PROTO2_APPEND(uint64, uint64_t) |
||||
PROTO2_APPEND(int64, int64_t) |
||||
PROTO2_APPEND(int32, int32_t) |
||||
PROTO2_APPEND(uint32, uint32_t) |
||||
PROTO2_APPEND(bool, bool) |
||||
|
||||
upb_flow_t proto2_setstr(void *m, upb_value fval, upb_value val) { |
||||
assert(m != NULL); |
||||
const upb_fielddef *f = upb_value_getfielddef(fval); |
||||
std::string **str = (std::string**)UPB_INDEX(m, f->offset, 1); |
||||
if (*str == f->default_ptr) *str = new std::string; |
||||
const upb_byteregion *reg = upb_value_getbyteregion(val); |
||||
size_t len; |
||||
(*str)->assign( |
||||
upb_byteregion_getptr(reg, upb_byteregion_startofs(reg), &len), |
||||
upb_byteregion_len(reg)); |
||||
// XXX: only supports contiguous strings atm.
|
||||
assert(len == upb_byteregion_len(reg)); |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
upb_flow_t proto2_append_str(void *_r, upb_value fval, upb_value val) { |
||||
assert(_r != NULL); |
||||
typedef google::protobuf::RepeatedPtrField<std::string> R; |
||||
(void)fval; |
||||
R *r = (R*)_r; |
||||
const upb_byteregion *reg = upb_value_getbyteregion(val); |
||||
size_t len; |
||||
r->Add()->assign( |
||||
upb_byteregion_getptr(reg, upb_byteregion_startofs(reg), &len), |
||||
upb_byteregion_len(reg)); |
||||
// XXX: only supports contiguous strings atm.
|
||||
assert(len == upb_byteregion_len(reg)); |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
upb_sflow_t proto2_startseq(void *m, upb_value fval) { |
||||
assert(m != NULL); |
||||
const upb_fielddef *f = upb_value_getfielddef(fval); |
||||
return UPB_CONTINUE_WITH(UPB_INDEX(m, f->offset, 1)); |
||||
} |
||||
|
||||
upb_sflow_t proto2_startsubmsg(void *m, upb_value fval) { |
||||
assert(m != NULL); |
||||
const upb_fielddef *f = upb_value_getfielddef(fval); |
||||
google::protobuf::Message *prototype = (google::protobuf::Message*)f->prototype; |
||||
void **subm = (void**)UPB_INDEX(m, f->offset, 1); |
||||
if (*subm == NULL || *subm == f->default_ptr) |
||||
*subm = prototype->New(); |
||||
assert(*subm != NULL); |
||||
return UPB_CONTINUE_WITH(*subm); |
||||
} |
||||
|
||||
class UpbRepeatedPtrField : public google::protobuf::internal::RepeatedPtrFieldBase { |
||||
public: |
||||
class TypeHandler { |
||||
public: |
||||
typedef void Type; |
||||
// AddAllocated() calls this, but only if other objects are sitting
|
||||
// around waiting for reuse, which we will not do.
|
||||
static void Delete(Type*) { assert(false); } |
||||
}; |
||||
void *Add(google::protobuf::Message *m) { |
||||
void *submsg = RepeatedPtrFieldBase::AddFromCleared<TypeHandler>(); |
||||
if (!submsg) { |
||||
submsg = m->New(); |
||||
RepeatedPtrFieldBase::AddAllocated<TypeHandler>(submsg); |
||||
} |
||||
return submsg; |
||||
} |
||||
}; |
||||
|
||||
upb_sflow_t proto2_startsubmsg_r(void *_r, upb_value fval) { |
||||
assert(_r != NULL); |
||||
// Compared to the other writers, this implementation is particularly sketchy.
|
||||
// The object we are modifying is a RepeatedPtrField<SubType>*, but we can't
|
||||
// properly declare that templated pointer because we don't have access to
|
||||
// that type at compile-time (and wouldn't want to create a separate callback
|
||||
// for each type anyway). Instead we access the pointer as a
|
||||
// RepeatedPtrFieldBase, which is indeed a superclass of RepeatedPtrField.
|
||||
// But we can't properly declare a TypeHandler for the submessage's type,
|
||||
// for the same reason that we can't create a RepeatedPtrField<SubType>*.
|
||||
// Instead we treat it as a void*, and create the submessage using
|
||||
// google::protobuf::Message::New() if we need to.
|
||||
class TypeHandler { |
||||
public: |
||||
typedef void Type; |
||||
}; |
||||
const upb_fielddef *f = upb_value_getfielddef(fval); |
||||
UpbRepeatedPtrField *r = (UpbRepeatedPtrField*)_r; |
||||
void *submsg = r->Add((google::protobuf::Message*)f->prototype); |
||||
assert(submsg != NULL); |
||||
return UPB_CONTINUE_WITH(submsg); |
||||
} |
||||
|
||||
#define PROTO2MSG(type, size) { static upb_accessor_vtbl vtbl = { \ |
||||
&proto2_startsubmsg, \
|
||||
&upb_stdmsg_set ## type, \
|
||||
&proto2_startseq, \
|
||||
&proto2_startsubmsg_r, \
|
||||
&proto2_append_ ## type, \
|
||||
NULL, NULL, NULL, NULL, NULL, NULL}; \
|
||||
return &vtbl; } |
||||
|
||||
static upb_accessor_vtbl *proto2_accessor(upb_fielddef *f) { |
||||
switch (f->type) { |
||||
case UPB_TYPE(DOUBLE): PROTO2MSG(double, 8) |
||||
case UPB_TYPE(FLOAT): PROTO2MSG(float, 4) |
||||
case UPB_TYPE(UINT64): |
||||
case UPB_TYPE(FIXED64): PROTO2MSG(uint64, 8) |
||||
case UPB_TYPE(INT64): |
||||
case UPB_TYPE(SFIXED64): |
||||
case UPB_TYPE(SINT64): PROTO2MSG(int64, 8) |
||||
case UPB_TYPE(INT32): |
||||
case UPB_TYPE(SINT32): |
||||
case UPB_TYPE(ENUM): |
||||
case UPB_TYPE(SFIXED32): PROTO2MSG(int32, 4) |
||||
case UPB_TYPE(UINT32): |
||||
case UPB_TYPE(FIXED32): PROTO2MSG(uint32, 4) |
||||
case UPB_TYPE(BOOL): PROTO2MSG(bool, 1) |
||||
case UPB_TYPE(STRING): |
||||
case UPB_TYPE(BYTES): |
||||
case UPB_TYPE(GROUP): |
||||
case UPB_TYPE(MESSAGE): { |
||||
static upb_accessor_vtbl vtbl = { |
||||
&proto2_startsubmsg, |
||||
&proto2_setstr, |
||||
&proto2_startseq, |
||||
&proto2_startsubmsg_r, |
||||
&proto2_append_str, |
||||
NULL, NULL, NULL, NULL, NULL, NULL}; |
||||
return &vtbl; |
||||
} |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
static void layout_msgdef_from_proto2(upb_msgdef *upb_md, |
||||
const google::protobuf::Message *m, |
||||
const google::protobuf::Descriptor *proto2_d) { |
||||
// Hack: we break the encapsulation of GeneratedMessageReflection to get at
|
||||
// the offsets we need. If/when we do this for real, we will need
|
||||
// GeneratedMessageReflection to expose those offsets publicly.
|
||||
const google::protobuf::internal::GeneratedMessageReflection *r = |
||||
(google::protobuf::internal::GeneratedMessageReflection*)m->GetReflection(); |
||||
for (int i = 0; i < proto2_d->field_count(); i++) { |
||||
const google::protobuf::FieldDescriptor *proto2_f = proto2_d->field(i); |
||||
upb_fielddef *upb_f = upb_msgdef_itof(upb_md, proto2_f->number()); |
||||
assert(upb_f); |
||||
|
||||
// Encapsulation violation BEGIN
|
||||
uint32_t data_offset = r->offsets_[proto2_f->index()]; |
||||
uint32_t hasbit = (r->has_bits_offset_ * 8) + proto2_f->index(); |
||||
// Encapsulation violation END
|
||||
|
||||
if (upb_isseq(upb_f)) { |
||||
// proto2 does not store hasbits for repeated fields.
|
||||
upb_f->hasbit = -1; |
||||
} else { |
||||
upb_f->hasbit = hasbit; |
||||
} |
||||
upb_f->offset = data_offset; |
||||
upb_fielddef_setaccessor(upb_f, proto2_accessor(upb_f)); |
||||
|
||||
if (upb_isstring(upb_f) && !upb_isseq(upb_f)) { |
||||
upb_f->default_ptr = &r->GetStringReference(*m, proto2_f, NULL); |
||||
} else if (upb_issubmsg(upb_f)) { |
||||
// XXX: skip leading "."
|
||||
const google::protobuf::Descriptor *subm_descriptor = |
||||
google::protobuf::DescriptorPool::generated_pool()-> |
||||
FindMessageTypeByName(upb_fielddef_typename(upb_f) + 1); |
||||
assert(subm_descriptor); |
||||
upb_f->prototype = google::protobuf::MessageFactory::generated_factory()->GetPrototype(subm_descriptor); |
||||
if (!upb_isseq(upb_f)) |
||||
upb_f->default_ptr = &r->GetMessage(*m, proto2_f); |
||||
} |
||||
} |
||||
} |
||||
upb::StringSource strsrc; |
||||
upb::Decoder d; |
||||
const upb::MessageDef *def; |
||||
upb::DecoderPlan* plan; |
||||
|
||||
static bool initialize() |
||||
{ |
||||
// Initialize upb state, decode descriptor.
|
||||
upb_status status = UPB_STATUS_INIT; |
||||
upb_symtab *s = upb_symtab_new(); |
||||
|
||||
char *data = upb_readfile(MESSAGE_DESCRIPTOR_FILE, &len); |
||||
if (!data) { |
||||
fprintf(stderr, "Couldn't read file: " MESSAGE_DESCRIPTOR_FILE); |
||||
return false; |
||||
} |
||||
int n; |
||||
upb_def **defs = upb_load_defs_from_descriptor(data, len, &n, &status); |
||||
free(data); |
||||
if(!upb_ok(&status)) { |
||||
fprintf(stderr, "Error reading descriptor: %s\n", |
||||
upb_status_getstr(&status)); |
||||
return false; |
||||
} |
||||
|
||||
// Setup offsets and accessors to properly write into a proto2 generated
|
||||
// class.
|
||||
for (int i = 0; i < n; i++) { |
||||
upb_def *def = defs[i]; |
||||
upb_msgdef *upb_md = upb_dyncast_msgdef(def); |
||||
if (!upb_md) continue; |
||||
const google::protobuf::Descriptor *proto2_md = |
||||
google::protobuf::DescriptorPool::generated_pool()-> |
||||
FindMessageTypeByName(upb_def_fqname(def)); |
||||
if (!proto2_md) abort(); |
||||
const google::protobuf::Message *proto2_m = |
||||
google::protobuf::MessageFactory::generated_factory()->GetPrototype(proto2_md); |
||||
layout_msgdef_from_proto2(upb_md, proto2_m, proto2_md); |
||||
} |
||||
|
||||
upb_symtab_add(s, defs, n, &status); |
||||
if(!upb_ok(&status)) { |
||||
fprintf(stderr, "Error reading adding to symtab: %s\n", |
||||
upb_status_getstr(&status)); |
||||
return false; |
||||
} |
||||
for(int i = 0; i < n; i++) upb_def_unref(defs[i]); |
||||
free(defs); |
||||
|
||||
def = upb_dyncast_msgdef_const(upb_symtab_lookup(s, MESSAGE_NAME)); |
||||
if(!def) { |
||||
fprintf(stderr, "Error finding symbol '%s'.\n", MESSAGE_NAME); |
||||
return false; |
||||
} |
||||
upb_symtab_unref(s); |
||||
|
||||
// Read the message data itself.
|
||||
str = upb_readfile(MESSAGE_FILE, &len); |
||||
if(str == NULL) { |
||||
fprintf(stderr, "Error reading " MESSAGE_FILE "\n"); |
||||
return false; |
||||
} |
||||
upb_status_uninit(&status); |
||||
|
||||
def = upb::proto2_bridge::NewFinalMessageDef(msg2, &def); |
||||
|
||||
msg2.ParseFromArray(str, len); |
||||
|
||||
upb_stringsrc_init(&strsrc); |
||||
upb_handlers *h = upb_handlers_new(); |
||||
upb_accessors_reghandlers(h, def); |
||||
p = upb_decoderplan_new(h, JIT); |
||||
upb_decoder_init(&d); |
||||
upb_decoder_resetplan(&d, p, 0); |
||||
upb_handlers_unref(h); |
||||
upb::Handlers* h = upb::Handlers::New(); |
||||
upb::RegisterWriteHandlers(h, def); |
||||
plan = upb::DecoderPlan::New(h, JIT); |
||||
d.ResetPlan(plan, 0); |
||||
h->Unref(); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
static void cleanup() { |
||||
upb_stringsrc_uninit(&strsrc); |
||||
upb_decoder_uninit(&d); |
||||
upb_def_unref(UPB_UPCAST(def)); |
||||
upb_decoderplan_unref(p); |
||||
free(str); |
||||
def->Unref(&def); |
||||
plan->Unref(); |
||||
} |
||||
|
||||
static size_t run(int i) |
||||
{ |
||||
(void)i; |
||||
upb_status status = UPB_STATUS_INIT; |
||||
static size_t run(int i) { |
||||
msg[i % NUM_MESSAGES].Clear(); |
||||
upb_stringsrc_reset(&strsrc, str, len); |
||||
upb_decoder_resetinput( |
||||
&d, upb_stringsrc_allbytes(&strsrc), &msg[i % NUM_MESSAGES]); |
||||
if (upb_decoder_decode(&d) != UPB_OK) goto err; |
||||
strsrc.Reset(str, len); |
||||
d.ResetInput(strsrc.AllBytes(), &msg[i % NUM_MESSAGES]); |
||||
if (d.Decode() != UPB_OK) goto err; |
||||
return len; |
||||
|
||||
err: |
||||
fprintf(stderr, "Decode error: %s", upb_status_getstr(&status)); |
||||
fprintf(stderr, "Decode error: %s", d.status().GetString()); |
||||
return 0; |
||||
} |
||||
|
@ -1,85 +0,0 @@ |
||||
|
||||
#include "main.c" |
||||
|
||||
#include "upb/bytestream.h" |
||||
#include "upb/def.h" |
||||
#include "upb/msg.h" |
||||
#include "upb/pb/decoder.h" |
||||
#include "upb/pb/glue.h" |
||||
|
||||
static const upb_msgdef *def; |
||||
static size_t len; |
||||
static void *msg[NUM_MESSAGES]; |
||||
static upb_stringsrc strsrc; |
||||
static upb_decoder d; |
||||
static upb_decoderplan *p; |
||||
char *str; |
||||
|
||||
static bool initialize() |
||||
{ |
||||
// Initialize upb state, decode descriptor.
|
||||
upb_status status = UPB_STATUS_INIT; |
||||
upb_symtab *s = upb_symtab_new(); |
||||
upb_load_descriptor_file_into_symtab(s, MESSAGE_DESCRIPTOR_FILE, &status); |
||||
if(!upb_ok(&status)) { |
||||
fprintf(stderr, "Error reading descriptor: %s\n", |
||||
upb_status_getstr(&status)); |
||||
return false; |
||||
} |
||||
|
||||
def = upb_dyncast_msgdef_const(upb_symtab_lookup(s, MESSAGE_NAME)); |
||||
if(!def) { |
||||
fprintf(stderr, "Error finding symbol '%s'.\n", MESSAGE_NAME); |
||||
return false; |
||||
} |
||||
upb_symtab_unref(s); |
||||
|
||||
// Read the message data itself.
|
||||
str = upb_readfile(MESSAGE_FILE, &len); |
||||
if(str == NULL) { |
||||
fprintf(stderr, "Error reading " MESSAGE_FILE "\n"); |
||||
return false; |
||||
} |
||||
upb_status_uninit(&status); |
||||
for (int i = 0; i < NUM_MESSAGES; i++) |
||||
msg[i] = upb_stdmsg_new(def); |
||||
|
||||
upb_stringsrc_init(&strsrc); |
||||
upb_handlers *h = upb_handlers_new(); |
||||
upb_accessors_reghandlers(h, def); |
||||
p = upb_decoderplan_new(h, JIT); |
||||
upb_decoder_init(&d); |
||||
upb_handlers_unref(h); |
||||
upb_decoder_resetplan(&d, p, 0); |
||||
|
||||
if (!BYREF) { |
||||
// TODO: use byref/byval accessors.
|
||||
} |
||||
return true; |
||||
} |
||||
|
||||
static void cleanup() |
||||
{ |
||||
for (int i = 0; i < NUM_MESSAGES; i++) |
||||
upb_stdmsg_free(msg[i], def); |
||||
upb_def_unref(UPB_UPCAST(def)); |
||||
upb_stringsrc_uninit(&strsrc); |
||||
upb_decoder_uninit(&d); |
||||
upb_decoderplan_unref(p); |
||||
free(str); |
||||
} |
||||
|
||||
static size_t run(int i) |
||||
{ |
||||
upb_status status = UPB_STATUS_INIT; |
||||
i %= NUM_MESSAGES; |
||||
upb_msg_clear(msg[i], def); |
||||
upb_stringsrc_reset(&strsrc, str, len); |
||||
upb_decoder_resetinput(&d, upb_stringsrc_allbytes(&strsrc), msg[i]); |
||||
if (upb_decoder_decode(&d) != UPB_OK) goto err; |
||||
return len; |
||||
|
||||
err: |
||||
fprintf(stderr, "Decode error: %s", upb_status_getstr(&status)); |
||||
return 0; |
||||
} |
@ -0,0 +1,39 @@ |
||||
//
|
||||
// upb - a minimalist implementation of protocol buffers.
|
||||
//
|
||||
// Copyright (c) 2011 Google Inc. See LICENSE for details.
|
||||
// Author: Josh Haberman <jhaberman@gmail.com>
|
||||
|
||||
#include "handlers.hpp" |
||||
|
||||
#include "def.hpp" |
||||
|
||||
namespace upb { |
||||
|
||||
namespace { |
||||
|
||||
void MessageCallbackWrapper( |
||||
void* closure, upb_mhandlers* mh, const upb_msgdef* m) { |
||||
Handlers::MessageRegistrationVisitor* visitor = |
||||
static_cast<Handlers::MessageRegistrationVisitor*>(closure); |
||||
visitor->OnMessage(static_cast<MessageHandlers*>(mh), |
||||
static_cast<const MessageDef*>(m)); |
||||
} |
||||
|
||||
void FieldCallbackWrapper( |
||||
void* closure, upb_fhandlers* fh, const upb_fielddef* f) { |
||||
Handlers::MessageRegistrationVisitor* visitor = |
||||
static_cast<Handlers::MessageRegistrationVisitor*>(closure); |
||||
visitor->OnField(static_cast<FieldHandlers*>(fh), |
||||
static_cast<const FieldDef*>(f)); |
||||
} |
||||
} // namepace
|
||||
|
||||
MessageHandlers* Handlers::RegisterMessageDef( |
||||
const MessageDef& m, Handlers::MessageRegistrationVisitor* visitor) { |
||||
upb_mhandlers* mh = upb_handlers_regmsgdef( |
||||
this, &m, &MessageCallbackWrapper, &FieldCallbackWrapper, &visitor); |
||||
return static_cast<MessageHandlers*>(mh); |
||||
} |
||||
|
||||
} // namespace upb
|
@ -0,0 +1,62 @@ |
||||
//
|
||||
// upb - a minimalist implementation of protocol buffers.
|
||||
//
|
||||
// Copyright (c) 2011 Google Inc. See LICENSE for details.
|
||||
// Author: Josh Haberman <jhaberman@gmail.com>
|
||||
// Routines for reading and writing message data to an in-memory structure,
|
||||
// similar to a C struct.
|
||||
//
|
||||
// upb does not define one single message object that everyone must use.
|
||||
// Rather it defines an abstract interface for reading and writing members
|
||||
// of a message object, and all of the parsers and serializers use this
|
||||
// abstract interface. This allows upb's parsers and serializers to be used
|
||||
// regardless of what memory management scheme or synchronization model the
|
||||
// application is using.
|
||||
//
|
||||
// A standard set of accessors is provided for doing simple reads and writes at
|
||||
// a known offset into the message. These accessors should be used when
|
||||
// possible, because they are specially optimized -- for example, the JIT can
|
||||
// recognize them and emit specialized code instead of having to call the
|
||||
// function at all. The application can substitute its own accessors when the
|
||||
// standard accessors are not suitable.
|
||||
|
||||
#ifndef UPB_MSG_HPP |
||||
#define UPB_MSG_HPP |
||||
|
||||
#include "upb/msg.h" |
||||
#include "upb/handlers.hpp" |
||||
|
||||
namespace upb { |
||||
|
||||
typedef upb_accessor_vtbl AccessorVTable; |
||||
|
||||
// Registers handlers for writing into a message of the given type using
|
||||
// whatever accessors it has defined.
|
||||
inline MessageHandlers* RegisterWriteHandlers(upb::Handlers* handlers, |
||||
const upb::MessageDef* md) { |
||||
return MessageHandlers::Cast( |
||||
upb_accessors_reghandlers(handlers, md)); |
||||
} |
||||
|
||||
template <typename T> static FieldHandlers::ValueHandler* GetValueHandler(); |
||||
|
||||
// A handy templated function that will retrieve a value handler for a given
|
||||
// C++ type.
|
||||
#define GET_VALUE_HANDLER(type, ctype) \ |
||||
template <> \
|
||||
FieldHandlers::ValueHandler* GetValueHandler<ctype>() { \
|
||||
return &upb_stdmsg_set ## type; \
|
||||
} |
||||
|
||||
GET_VALUE_HANDLER(double, double); |
||||
GET_VALUE_HANDLER(float, float); |
||||
GET_VALUE_HANDLER(uint64, uint64_t); |
||||
GET_VALUE_HANDLER(uint32, uint32_t); |
||||
GET_VALUE_HANDLER(int64, int64_t); |
||||
GET_VALUE_HANDLER(int32, int32_t); |
||||
GET_VALUE_HANDLER(bool, bool); |
||||
#undef GET_VALUE_HANDLER |
||||
|
||||
} // namespace
|
||||
|
||||
#endif |
@ -0,0 +1,892 @@ |
||||
//
|
||||
// upb - a minimalist implementation of protocol buffers.
|
||||
//
|
||||
// Copyright (c) 2011-2012 Google Inc. See LICENSE for details.
|
||||
// Author: Josh Haberman <jhaberman@gmail.com>
|
||||
|
||||
#include <string> |
||||
#include <typeinfo> |
||||
#include "upb/bytestream.hpp" |
||||
#include "upb/def.hpp" |
||||
#include "upb/handlers.hpp" |
||||
#include "upb/msg.hpp" |
||||
#include "upb/proto2_bridge.hpp" |
||||
|
||||
namespace { |
||||
|
||||
static void* GetFieldPointer(void *message, const upb::FieldDef* f) { |
||||
return static_cast<char*>(message) + f->offset(); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef UPB_GOOGLE3 |
||||
|
||||
// TODO(haberman): friend upb so that this isn't required.
|
||||
#define protected public |
||||
#include "net/proto2/public/repeated_field.h" |
||||
#undef private |
||||
|
||||
#define private public |
||||
#include "net/proto/proto2_reflection.h" |
||||
#undef private |
||||
|
||||
#include "net/proto2/proto/descriptor.pb.h" |
||||
#include "net/proto2/public/descriptor.h" |
||||
#include "net/proto2/public/generated_message_reflection.h" |
||||
#include "net/proto2/public/lazy_field.h" |
||||
#include "net/proto2/public/message.h" |
||||
#include "net/proto2/public/string_piece_field_support.h" |
||||
#include "net/proto/internal_layout.h" |
||||
#include "strings/cord.h" |
||||
using ::proto2::Descriptor; |
||||
using ::proto2::EnumDescriptor; |
||||
using ::proto2::EnumValueDescriptor; |
||||
using ::proto2::FieldDescriptor; |
||||
using ::proto2::FieldOptions; |
||||
using ::proto2::FileDescriptor; |
||||
using ::proto2::internal::GeneratedMessageReflection; |
||||
using ::proto2::internal::RepeatedPtrFieldBase; |
||||
using ::proto2::internal::StringPieceField; |
||||
using ::proto2::Message; |
||||
using ::proto2::MessageFactory; |
||||
using ::proto2::Reflection; |
||||
using ::proto2::RepeatedField; |
||||
using ::proto2::RepeatedPtrField; |
||||
|
||||
namespace upb { |
||||
|
||||
static const Message* GetPrototypeForField(const Message& m, |
||||
const FieldDescriptor* f); |
||||
|
||||
namespace proto2_bridge_google3 { class FieldAccessor; } |
||||
|
||||
using ::upb::proto2_bridge_google3::FieldAccessor; |
||||
|
||||
namespace proto2_bridge_google3 { |
||||
|
||||
static void AssignToCord(const ByteRegion* r, Cord* cord) { |
||||
// TODO(haberman): ref source data if source is a cord.
|
||||
cord->Clear(); |
||||
uint64_t ofs = r->start_ofs(); |
||||
while (ofs < r->end_ofs()) { |
||||
size_t len; |
||||
const char *buf = r->GetPtr(ofs, &len); |
||||
cord->Append(StringPiece(buf, len)); |
||||
ofs += len; |
||||
} |
||||
} |
||||
|
||||
#else |
||||
|
||||
// TODO(haberman): friend upb so that this isn't required.
|
||||
#define protected public |
||||
#include "google/protobuf/repeated_field.h" |
||||
#undef protected |
||||
|
||||
#define private public |
||||
#include "google/protobuf/generated_message_reflection.h" |
||||
#undef private |
||||
|
||||
#include "google/protobuf/descriptor.h" |
||||
#include "google/protobuf/descriptor.pb.h" |
||||
#include "google/protobuf/message.h" |
||||
using ::google::protobuf::Descriptor; |
||||
using ::google::protobuf::EnumDescriptor; |
||||
using ::google::protobuf::EnumValueDescriptor; |
||||
using ::google::protobuf::FieldDescriptor; |
||||
using ::google::protobuf::FieldOptions; |
||||
using ::google::protobuf::FileDescriptor; |
||||
using ::google::protobuf::internal::GeneratedMessageReflection; |
||||
using ::google::protobuf::internal::RepeatedPtrFieldBase; |
||||
using ::google::protobuf::Message; |
||||
using ::google::protobuf::MessageFactory; |
||||
using ::google::protobuf::Reflection; |
||||
using ::google::protobuf::RepeatedField; |
||||
using ::google::protobuf::RepeatedPtrField; |
||||
|
||||
namespace upb { |
||||
static const Message* GetPrototypeForField(const Message& m, |
||||
const FieldDescriptor* f); |
||||
|
||||
namespace proto2_bridge_opensource { class FieldAccessor; } |
||||
|
||||
using ::upb::proto2_bridge_opensource::FieldAccessor; |
||||
|
||||
namespace proto2_bridge_opensource { |
||||
|
||||
#endif // ifdef UPB_GOOGLE3
|
||||
|
||||
// Have to define this manually since older versions of proto2 didn't define
|
||||
// an enum value for STRING.
|
||||
#define UPB_CTYPE_STRING 0 |
||||
|
||||
// The code in this class depends on the internal representation of the proto2
|
||||
// generated classes, which is an internal implementation detail of proto2 and
|
||||
// is not a public interface. As a result, this class's implementation may
|
||||
// need to be changed if/when proto2 changes its internal representation. It
|
||||
// is intended that this class is the only code that depends on these internal,
|
||||
// non-public interfaces.
|
||||
//
|
||||
// This class only works with messages that use GeneratedMessageReflection.
|
||||
// Other reflection classes will need other accessor implementations.
|
||||
class FieldAccessor { |
||||
public: |
||||
// Returns true if we were able to set an accessor and any other properties
|
||||
// of the FieldDef that are necessary to read/write this field to a
|
||||
// proto2::Message.
|
||||
static bool TrySet(const FieldDescriptor* proto2_f, |
||||
const upb::MessageDef* md, |
||||
upb::FieldDef* upb_f) { |
||||
const Message* prototype = static_cast<const Message*>(md->prototype); |
||||
const Reflection* base_r = prototype->GetReflection(); |
||||
const GeneratedMessageReflection* r = |
||||
dynamic_cast<const GeneratedMessageReflection*>(base_r); |
||||
// Old versions of the open-source protobuf release erroneously default to
|
||||
// Cord even though that has never been supported in the open-source
|
||||
// release.
|
||||
int32_t ctype = proto2_f->options().has_ctype() ? |
||||
proto2_f->options().ctype() : UPB_CTYPE_STRING; |
||||
if (!r) return false; |
||||
// Extensions not supported yet.
|
||||
if (proto2_f->is_extension()) return false; |
||||
|
||||
upb_f->set_accessor(GetForFieldDescriptor(proto2_f, ctype)); |
||||
upb_f->set_hasbit(GetHasbit(proto2_f, r)); |
||||
upb_f->set_offset(GetOffset(proto2_f, r)); |
||||
if (upb_f->IsSubmessage()) { |
||||
upb_f->set_subtype_name(proto2_f->message_type()->full_name()); |
||||
upb_f->prototype = GetPrototypeForField(*prototype, proto2_f); |
||||
} |
||||
|
||||
if (upb_f->IsString() && !upb_f->IsSequence() && |
||||
ctype == UPB_CTYPE_STRING) { |
||||
upb_f->prototype = &r->GetStringReference(*prototype, proto2_f, NULL); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
static MessageFactory* GetMessageFactory(const Message& m) { |
||||
const GeneratedMessageReflection* r = |
||||
dynamic_cast<const GeneratedMessageReflection*>(m.GetReflection()); |
||||
return r ? r->message_factory_ : NULL; |
||||
} |
||||
|
||||
private: |
||||
static int64_t GetHasbit(const FieldDescriptor* f, |
||||
const GeneratedMessageReflection* r) { |
||||
if (f->is_repeated()) { |
||||
// proto2 does not store hasbits for repeated fields.
|
||||
return -1; |
||||
} else { |
||||
return (r->has_bits_offset_ * 8) + f->index(); |
||||
} |
||||
} |
||||
|
||||
static uint16_t GetOffset(const FieldDescriptor* f, |
||||
const GeneratedMessageReflection* r) { |
||||
return r->offsets_[f->index()]; |
||||
} |
||||
|
||||
static AccessorVTable *GetForFieldDescriptor(const FieldDescriptor* f, |
||||
int32_t ctype) { |
||||
switch (f->cpp_type()) { |
||||
case FieldDescriptor::CPPTYPE_ENUM: |
||||
// Should handlers validate enum membership to match proto2?
|
||||
case FieldDescriptor::CPPTYPE_INT32: return Get<int32_t>(); |
||||
case FieldDescriptor::CPPTYPE_INT64: return Get<int64_t>(); |
||||
case FieldDescriptor::CPPTYPE_UINT32: return Get<uint32_t>(); |
||||
case FieldDescriptor::CPPTYPE_UINT64: return Get<uint64_t>(); |
||||
case FieldDescriptor::CPPTYPE_DOUBLE: return Get<double>(); |
||||
case FieldDescriptor::CPPTYPE_FLOAT: return Get<float>(); |
||||
case FieldDescriptor::CPPTYPE_BOOL: return Get<bool>(); |
||||
case FieldDescriptor::CPPTYPE_STRING: |
||||
switch (ctype) { |
||||
#ifdef UPB_GOOGLE3 |
||||
case FieldOptions::STRING: |
||||
return GetForString<string>(); |
||||
case FieldOptions::CORD: |
||||
return GetForCord(); |
||||
case FieldOptions::STRING_PIECE: |
||||
return GetForStringPiece(); |
||||
#else |
||||
case UPB_CTYPE_STRING: |
||||
return GetForString<std::string>(); |
||||
#endif |
||||
default: return NULL; |
||||
} |
||||
case FieldDescriptor::CPPTYPE_MESSAGE: |
||||
#ifdef UPB_GOOGLE3 |
||||
if (f->options().lazy()) { |
||||
return NULL; // Not yet implemented.
|
||||
} else { |
||||
return GetForMessage(); |
||||
} |
||||
#else |
||||
return GetForMessage(); |
||||
#endif |
||||
default: return NULL; |
||||
} |
||||
} |
||||
|
||||
// PushOffset handler (used for StartSequence and others) ///////////////////
|
||||
|
||||
static SubFlow PushOffset(void *m, Value fval) { |
||||
const FieldDef *f = GetValue<const FieldDef*>(fval); |
||||
return UPB_CONTINUE_WITH(GetFieldPointer(m, f)); |
||||
} |
||||
|
||||
// Primitive Value (numeric, enum, bool) /////////////////////////////////////
|
||||
|
||||
template <typename T> static AccessorVTable *Get() { |
||||
static upb_accessor_vtbl vtbl = { |
||||
NULL, // StartSubMessage handler
|
||||
GetValueHandler<T>(), |
||||
&PushOffset, // StartSequence handler
|
||||
NULL, // StartRepeatedSubMessage handler
|
||||
&Append<T>, |
||||
NULL, NULL, NULL, NULL, NULL, NULL}; |
||||
return &vtbl; |
||||
} |
||||
|
||||
template <typename T> |
||||
static Flow Append(void *_r, Value fval, Value val) { |
||||
(void)fval; |
||||
RepeatedField<T>* r = static_cast<RepeatedField<T>*>(_r); |
||||
r->Add(GetValue<T>(val)); |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
// String ////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T> static AccessorVTable *GetForString() { |
||||
static upb_accessor_vtbl vtbl = { |
||||
NULL, // StartSubMessage handler
|
||||
&SetString<T>, |
||||
&PushOffset, // StartSequence handler
|
||||
NULL, // StartRepeatedSubMessage handler
|
||||
&AppendString<T>, |
||||
NULL, NULL, NULL, NULL, NULL, NULL}; |
||||
return &vtbl; |
||||
} |
||||
|
||||
// This needs to be templated because google3 string is not std::string.
|
||||
template <typename T> static Flow SetString(void *m, Value fval, Value val) { |
||||
const FieldDef* f = GetValue<const FieldDef*>(fval); |
||||
T **str = static_cast<T**>(GetFieldPointer(m, f)); |
||||
// If it points to the default instance, we must create a new instance.
|
||||
if (*str == f->prototype) *str = new T(); |
||||
GetValue<ByteRegion*>(val)->AssignToString(*str); |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
template <typename T> |
||||
static Flow AppendString(void *_r, Value fval, Value val) { |
||||
(void)fval; |
||||
RepeatedPtrField<T>* r = static_cast<RepeatedPtrField<T>*>(_r); |
||||
GetValue<ByteRegion*>(val)->AssignToString(r->Add()); |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
// SubMessage ////////////////////////////////////////////////////////////////
|
||||
|
||||
static AccessorVTable *GetForMessage() { |
||||
static upb_accessor_vtbl vtbl = { |
||||
&StartSubMessage, |
||||
NULL, // Value handler
|
||||
&PushOffset, // StartSequence handler
|
||||
&StartRepeatedSubMessage, |
||||
NULL, // Repeated value handler
|
||||
NULL, NULL, NULL, NULL, NULL, NULL}; |
||||
return &vtbl; |
||||
} |
||||
|
||||
static SubFlow StartSubMessage(void *m, Value fval) { |
||||
const FieldDef* f = GetValue<const FieldDef*>(fval); |
||||
void **subm = static_cast<void**>(GetFieldPointer(m, f)); |
||||
if (*subm == NULL || *subm == f->prototype) { |
||||
const Message* prototype = static_cast<const Message*>(f->prototype); |
||||
*subm = prototype->New(); |
||||
} |
||||
return UPB_CONTINUE_WITH(*subm); |
||||
} |
||||
|
||||
class RepeatedMessageTypeHandler { |
||||
public: |
||||
typedef void Type; |
||||
// AddAllocated() calls this, but only if other objects are sitting
|
||||
// around waiting for reuse, which we will not do.
|
||||
static void Delete(Type* t) { |
||||
(void)t; |
||||
assert(false); |
||||
} |
||||
}; |
||||
|
||||
// Closure is a RepeatedPtrField<SubMessageType>*, but we access it through
|
||||
// its base class RepeatedPtrFieldBase*.
|
||||
static SubFlow StartRepeatedSubMessage(void* _r, Value fval) { |
||||
const FieldDef* f = GetValue<const FieldDef*>(fval); |
||||
RepeatedPtrFieldBase *r = static_cast<RepeatedPtrFieldBase*>(_r); |
||||
void *submsg = r->AddFromCleared<RepeatedMessageTypeHandler>(); |
||||
if (!submsg) { |
||||
const Message* prototype = static_cast<const Message*>(f->prototype); |
||||
submsg = prototype->New(); |
||||
r->AddAllocated<RepeatedMessageTypeHandler>(submsg); |
||||
} |
||||
return UPB_CONTINUE_WITH(submsg); |
||||
} |
||||
|
||||
// TODO(haberman): handle Extensions, Unknown Fields.
|
||||
|
||||
#ifdef UPB_GOOGLE3 |
||||
// Handlers for types/features only included in internal proto2 release:
|
||||
// Cord, StringPiece, LazyField, and MessageSet.
|
||||
// TODO(haberman): LazyField, MessageSet.
|
||||
|
||||
// Cord //////////////////////////////////////////////////////////////////////
|
||||
|
||||
static AccessorVTable *GetForCord() { |
||||
static upb_accessor_vtbl vtbl = { |
||||
NULL, // StartSubMessage handler
|
||||
&SetCord, |
||||
&PushOffset, // StartSequence handler
|
||||
NULL, // StartRepeatedSubMessage handler
|
||||
&AppendCord, |
||||
NULL, NULL, NULL, NULL, NULL, NULL}; |
||||
return &vtbl; |
||||
} |
||||
|
||||
static Flow SetCord(void *m, Value fval, Value val) { |
||||
const FieldDef* f = GetValue<const FieldDef*>(fval); |
||||
Cord* field = static_cast<Cord*>(GetFieldPointer(m, f)); |
||||
AssignToCord(GetValue<ByteRegion*>(val), field); |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
static Flow AppendCord(void *_r, Value fval, Value val) { |
||||
RepeatedField<Cord>* r = static_cast<RepeatedField<Cord>*>(_r); |
||||
AssignToCord(GetValue<ByteRegion*>(val), r->Add()); |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
// StringPiece ///////////////////////////////////////////////////////////////
|
||||
|
||||
static AccessorVTable *GetForStringPiece() { |
||||
static upb_accessor_vtbl vtbl = { |
||||
NULL, // StartSubMessage handler
|
||||
&SetStringPiece, |
||||
&PushOffset, // StartSequence handler
|
||||
NULL, // StartRepeatedSubMessage handler
|
||||
&AppendStringPiece, |
||||
NULL, NULL, NULL, NULL, NULL, NULL}; |
||||
return &vtbl; |
||||
} |
||||
|
||||
static void AssignToStringPieceField(const ByteRegion* r, |
||||
proto2::internal::StringPieceField* f) { |
||||
// TODO(haberman): alias if possible and enabled on the input stream.
|
||||
// TODO(haberman): add a method to StringPieceField that lets us avoid
|
||||
// this copy/malloc/free.
|
||||
char *data = new char[r->Length()]; |
||||
r->Copy(r->start_ofs(), r->Length(), data); |
||||
f->CopyFrom(StringPiece(data, r->Length())); |
||||
delete[] data; |
||||
} |
||||
|
||||
static Flow SetStringPiece(void *m, Value fval, Value val) { |
||||
const FieldDef* f = GetValue<const FieldDef*>(fval); |
||||
StringPieceField* field = |
||||
static_cast<StringPieceField*>(GetFieldPointer(m, f)); |
||||
AssignToStringPieceField(GetValue<ByteRegion*>(val), field); |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
static Flow AppendStringPiece(void* _r, Value fval, Value val) { |
||||
RepeatedPtrField<StringPieceField>* r = |
||||
static_cast<RepeatedPtrField<StringPieceField>*>(_r); |
||||
AssignToStringPieceField(GetValue<ByteRegion*>(val), r->Add()); |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
#endif // UPB_GOOGLE3
|
||||
}; |
||||
|
||||
#ifdef UPB_GOOGLE3 |
||||
|
||||
// Proto1 accessor -- only needed inside Google.
|
||||
class Proto1FieldAccessor { |
||||
public: |
||||
// Returns true if we were able to set an accessor and any other properties
|
||||
// of the FieldDef that are necessary to read/write this field to a
|
||||
// proto2::Message.
|
||||
static bool TrySet(const FieldDescriptor* proto2_f, |
||||
const upb::MessageDef* md, |
||||
upb::FieldDef* upb_f) { |
||||
const Message* m = static_cast<const Message*>(md->prototype); |
||||
const proto2::Reflection* base_r = m->GetReflection(); |
||||
const _pi::Proto2Reflection* r = |
||||
dynamic_cast<const _pi::Proto2Reflection*>(base_r); |
||||
if (!r) return false; |
||||
// Extensions not supported yet.
|
||||
if (proto2_f->is_extension()) return false; |
||||
|
||||
const _pi::Field* f = r->GetFieldLayout(proto2_f); |
||||
|
||||
if (f->crep == _pi::CREP_OPTIONAL_FOREIGN_WEAK) { |
||||
// Override the BYTES type that proto2 descriptors have for weak fields.
|
||||
upb_f->set_type(UPB_TYPE(MESSAGE)); |
||||
} |
||||
|
||||
if (upb_f->IsSubmessage()) { |
||||
const Message* prototype = upb::GetPrototypeForField(*m, proto2_f); |
||||
upb_f->set_subtype_name(prototype->GetDescriptor()->full_name()); |
||||
upb_f->prototype = prototype; |
||||
} |
||||
|
||||
upb_f->set_accessor(GetForCrep(f->crep)); |
||||
upb_f->set_hasbit(GetHasbit(proto2_f, r)); |
||||
upb_f->set_offset(GetOffset(proto2_f, r)); |
||||
return true; |
||||
} |
||||
|
||||
private: |
||||
static int16_t GetHasbit(const FieldDescriptor* f, |
||||
const _pi::Proto2Reflection* r) { |
||||
if (f->is_repeated()) { |
||||
// proto1 does not store hasbits for repeated fields.
|
||||
return -1; |
||||
} else { |
||||
return (r->layout_->has_bit_offset * 8) + r->GetFieldLayout(f)->has_index; |
||||
} |
||||
} |
||||
|
||||
static uint16_t GetOffset(const FieldDescriptor* f, |
||||
const _pi::Proto2Reflection* r) { |
||||
return r->GetFieldLayout(f)->offset; |
||||
} |
||||
|
||||
static AccessorVTable *GetForCrep(int crep) { |
||||
#define PRIMITIVE(name, type_name) \ |
||||
case _pi::CREP_REQUIRED_ ## name: \
|
||||
case _pi::CREP_OPTIONAL_ ## name: \
|
||||
case _pi::CREP_REPEATED_ ## name: return Get<type_name>(); |
||||
|
||||
switch (crep) { |
||||
PRIMITIVE(DOUBLE, double); |
||||
PRIMITIVE(FLOAT, float); |
||||
PRIMITIVE(INT64, int64_t); |
||||
PRIMITIVE(UINT64, uint64_t); |
||||
PRIMITIVE(INT32, int32_t); |
||||
PRIMITIVE(FIXED64, uint64_t); |
||||
PRIMITIVE(FIXED32, uint32_t); |
||||
PRIMITIVE(BOOL, bool); |
||||
case _pi::CREP_REQUIRED_STRING: |
||||
case _pi::CREP_OPTIONAL_STRING: |
||||
case _pi::CREP_REPEATED_STRING: return GetForString(); |
||||
case _pi::CREP_OPTIONAL_OUTOFLINE_STRING: return GetForOutOfLineString(); |
||||
case _pi::CREP_REQUIRED_CORD: |
||||
case _pi::CREP_OPTIONAL_CORD: |
||||
case _pi::CREP_REPEATED_CORD: return GetForCord(); |
||||
case _pi::CREP_REQUIRED_GROUP: |
||||
case _pi::CREP_REQUIRED_FOREIGN: |
||||
case _pi::CREP_REQUIRED_FOREIGN_PROTO2: return GetForRequiredMessage(); |
||||
case _pi::CREP_OPTIONAL_GROUP: |
||||
case _pi::CREP_REPEATED_GROUP: |
||||
case _pi::CREP_OPTIONAL_FOREIGN: |
||||
case _pi::CREP_REPEATED_FOREIGN: |
||||
case _pi::CREP_OPTIONAL_FOREIGN_PROTO2: |
||||
case _pi::CREP_REPEATED_FOREIGN_PROTO2: return GetForMessage(); |
||||
case _pi::CREP_OPTIONAL_FOREIGN_WEAK: return GetForWeakMessage(); |
||||
default: assert(false); return NULL; |
||||
} |
||||
#undef PRIMITIVE |
||||
} |
||||
|
||||
// PushOffset handler (used for StartSequence and others) ///////////////////
|
||||
|
||||
// We can find a RepeatedField* or a RepeatedPtrField* at f->offset().
|
||||
static SubFlow PushOffset(void *m, Value fval) { |
||||
const FieldDef *f = GetValue<const FieldDef*>(fval); |
||||
return UPB_CONTINUE_WITH(GetFieldPointer(m, f)); |
||||
} |
||||
|
||||
// Primitive Value (numeric, enum, bool) /////////////////////////////////////
|
||||
|
||||
template <typename T> static AccessorVTable *Get() { |
||||
static upb_accessor_vtbl vtbl = { |
||||
NULL, // StartSubMessage handler
|
||||
GetValueHandler<T>(), |
||||
&PushOffset, // StartSequence handler
|
||||
NULL, // StartRepeatedSubMessage handler
|
||||
&Append<T>, |
||||
NULL, NULL, NULL, NULL, NULL, NULL}; |
||||
return &vtbl; |
||||
} |
||||
|
||||
template <typename T> |
||||
static Flow Append(void *_r, Value fval, Value val) { |
||||
(void)fval; |
||||
// Proto1's ProtoArray class derives from RepeatedField.
|
||||
RepeatedField<T>* r = static_cast<RepeatedField<T>*>(_r); |
||||
r->Add(GetValue<T>(val)); |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
// String ////////////////////////////////////////////////////////////////////
|
||||
|
||||
static AccessorVTable *GetForString() { |
||||
static upb_accessor_vtbl vtbl = { |
||||
NULL, // StartSubMessage handler
|
||||
&SetString, |
||||
&PushOffset, // StartSequence handler
|
||||
NULL, // StartRepeatedSubMessage handler
|
||||
&AppendString, |
||||
NULL, NULL, NULL, NULL, NULL, NULL}; |
||||
return &vtbl; |
||||
} |
||||
|
||||
static Flow SetString(void *m, Value fval, Value val) { |
||||
const FieldDef* f = GetValue<const FieldDef*>(fval); |
||||
string *str = static_cast<string*>(GetFieldPointer(m, f)); |
||||
GetValue<ByteRegion*>(val)->AssignToString(str); |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
static Flow AppendString(void *_r, Value fval, Value val) { |
||||
(void)fval; |
||||
RepeatedPtrField<string>* r = static_cast<RepeatedPtrField<string>*>(_r); |
||||
GetValue<ByteRegion*>(val)->AssignToString(r->Add()); |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
// Out-of-line string ////////////////////////////////////////////////////////
|
||||
|
||||
static AccessorVTable *GetForOutOfLineString() { |
||||
static upb_accessor_vtbl vtbl = { |
||||
NULL, &SetOutOfLineString, |
||||
// This type is only used for non-repeated string fields.
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; |
||||
return &vtbl; |
||||
} |
||||
|
||||
static Flow SetOutOfLineString(void *m, Value fval, Value val) { |
||||
const FieldDef* f = GetValue<const FieldDef*>(fval); |
||||
string **str = static_cast<string**>(GetFieldPointer(m, f)); |
||||
if (*str == &::ProtocolMessage::___empty_internal_proto_string_) |
||||
*str = new string(); |
||||
GetValue<ByteRegion*>(val)->AssignToString(*str); |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
// Cord //////////////////////////////////////////////////////////////////////
|
||||
|
||||
static AccessorVTable *GetForCord() { |
||||
static upb_accessor_vtbl vtbl = { |
||||
NULL, // StartSubMessage handler
|
||||
&SetCord, |
||||
&PushOffset, // StartSequence handler
|
||||
NULL, // StartRepeatedSubMessage handler
|
||||
&AppendCord, |
||||
NULL, NULL, NULL, NULL, NULL, NULL}; |
||||
return &vtbl; |
||||
} |
||||
|
||||
static Flow SetCord(void *m, Value fval, Value val) { |
||||
const FieldDef* f = GetValue<const FieldDef*>(fval); |
||||
Cord* field = static_cast<Cord*>(GetFieldPointer(m, f)); |
||||
AssignToCord(GetValue<ByteRegion*>(val), field); |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
static Flow AppendCord(void *_r, Value fval, Value val) { |
||||
RepeatedField<Cord>* r = static_cast<RepeatedField<Cord>*>(_r); |
||||
AssignToCord(GetValue<ByteRegion*>(val), r->Add()); |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
// SubMessage ////////////////////////////////////////////////////////////////
|
||||
|
||||
static AccessorVTable *GetForRequiredMessage() { |
||||
static upb_accessor_vtbl vtbl = { |
||||
&PushOffset, // StartSubMessage handler
|
||||
NULL, // Value handler
|
||||
&PushOffset, // StartSequence handler
|
||||
&StartRepeatedSubMessage, |
||||
NULL, // Repeated value handler
|
||||
NULL, NULL, NULL, NULL, NULL, NULL}; |
||||
return &vtbl; |
||||
} |
||||
|
||||
static AccessorVTable *GetForWeakMessage() { |
||||
static upb_accessor_vtbl vtbl = { |
||||
&StartWeakSubMessage, // StartSubMessage handler
|
||||
NULL, // Value handler
|
||||
&PushOffset, // StartSequence handler
|
||||
&StartRepeatedSubMessage, |
||||
NULL, // Repeated value handler
|
||||
NULL, NULL, NULL, NULL, NULL, NULL}; |
||||
return &vtbl; |
||||
} |
||||
|
||||
static AccessorVTable *GetForMessage() { |
||||
static upb_accessor_vtbl vtbl = { |
||||
&StartSubMessage, |
||||
NULL, // Value handler
|
||||
&PushOffset, // StartSequence handler
|
||||
&StartRepeatedSubMessage, |
||||
NULL, // Repeated value handler
|
||||
NULL, NULL, NULL, NULL, NULL, NULL}; |
||||
return &vtbl; |
||||
} |
||||
|
||||
static SubFlow StartSubMessage(void *m, Value fval) { |
||||
const FieldDef* f = GetValue<const FieldDef*>(fval); |
||||
Message **subm = static_cast<Message**>(GetFieldPointer(m, f)); |
||||
if (*subm == f->prototype) *subm = (*subm)->New(); |
||||
return UPB_CONTINUE_WITH(*subm); |
||||
} |
||||
|
||||
static SubFlow StartWeakSubMessage(void *m, Value fval) { |
||||
const FieldDef* f = GetValue<const FieldDef*>(fval); |
||||
Message **subm = static_cast<Message**>(GetFieldPointer(m, f)); |
||||
if (*subm == NULL) { |
||||
const Message* prototype = static_cast<const Message*>(f->prototype); |
||||
*subm = prototype->New(); |
||||
} |
||||
return UPB_CONTINUE_WITH(*subm); |
||||
} |
||||
|
||||
class RepeatedMessageTypeHandler { |
||||
public: |
||||
typedef void Type; |
||||
// AddAllocated() calls this, but only if other objects are sitting
|
||||
// around waiting for reuse, which we will not do.
|
||||
static void Delete(Type* t) { |
||||
(void)t; |
||||
assert(false); |
||||
} |
||||
}; |
||||
|
||||
// Closure is a RepeatedPtrField<SubMessageType>*, but we access it through
|
||||
// its base class RepeatedPtrFieldBase*.
|
||||
static SubFlow StartRepeatedSubMessage(void* _r, Value fval) { |
||||
const FieldDef* f = GetValue<const FieldDef*>(fval); |
||||
RepeatedPtrFieldBase *r = static_cast<RepeatedPtrFieldBase*>(_r); |
||||
void *submsg = r->AddFromCleared<RepeatedMessageTypeHandler>(); |
||||
if (!submsg) { |
||||
const Message* prototype = static_cast<const Message*>(f->prototype); |
||||
submsg = prototype->New(); |
||||
r->AddAllocated<RepeatedMessageTypeHandler>(submsg); |
||||
} |
||||
return UPB_CONTINUE_WITH(submsg); |
||||
} |
||||
}; |
||||
|
||||
#endif |
||||
|
||||
} // namespace proto2_bridge_{google3,opensource}
|
||||
|
||||
static const Message* GetPrototypeForMessage(const Message& m) { |
||||
const Message* ret = NULL; |
||||
MessageFactory* factory = FieldAccessor::GetMessageFactory(m); |
||||
if (factory) { |
||||
// proto2 generated message or DynamicMessage.
|
||||
ret = factory->GetPrototype(m.GetDescriptor()); |
||||
assert(ret); |
||||
} else { |
||||
// Proto1 message; since proto1 has no dynamic message, it must be
|
||||
// from the generated factory.
|
||||
ret = MessageFactory::generated_factory()->GetPrototype(m.GetDescriptor()); |
||||
assert(ret); // If NULL, then wasn't a proto1 message, can't handle it.
|
||||
} |
||||
assert(ret->GetReflection() == m.GetReflection()); |
||||
return ret; |
||||
} |
||||
|
||||
static const Message* GetPrototypeForField(const Message& m, |
||||
const FieldDescriptor* f) { |
||||
#ifdef UPB_GOOGLE3 |
||||
if (f->type() == FieldDescriptor::TYPE_BYTES) { |
||||
// Proto1 weak field: the proto2 descriptor says their type is BYTES.
|
||||
const _pi::Proto2Reflection* r = |
||||
dynamic_cast<const _pi::Proto2Reflection*>(m.GetReflection()); |
||||
assert(r); |
||||
const _pi::Field* field = r->GetFieldLayout(f); |
||||
assert(field->crep == _pi::CREP_OPTIONAL_FOREIGN_WEAK); |
||||
return GetPrototypeForMessage( |
||||
*static_cast<const Message*>(field->weak_layout()->default_instance)); |
||||
} else if (dynamic_cast<const _pi::Proto2Reflection*>(m.GetReflection())) { |
||||
// Proto1 message; since proto1 has no dynamic message, it must be from
|
||||
// the generated factory.
|
||||
const Message* ret = |
||||
MessageFactory::generated_factory()->GetPrototype(f->message_type()); |
||||
assert(ret); |
||||
return ret; |
||||
} |
||||
#endif |
||||
assert(f->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE); |
||||
// We assume that all submessages (and extensions) will be constructed using
|
||||
// the same MessageFactory as this message. This doesn't cover the case of
|
||||
// CodedInputStream::SetExtensionRegistry().
|
||||
MessageFactory* factory = FieldAccessor::GetMessageFactory(m); |
||||
assert(factory); // If neither proto1 nor proto2 we can't handle it.
|
||||
const Message* ret = factory->GetPrototype(f->message_type()); |
||||
assert(ret); |
||||
return ret; |
||||
} |
||||
|
||||
namespace proto2_bridge { |
||||
|
||||
upb::FieldDef* AddFieldDef(const FieldDescriptor* f, upb::MessageDef* md) { |
||||
upb::FieldDef* upb_f = upb::FieldDef::New(&upb_f); |
||||
upb_f->set_number(f->number()); |
||||
upb_f->set_name(f->name()); |
||||
upb_f->set_label(static_cast<upb::Label>(f->label())); |
||||
upb_f->set_type(static_cast<upb::FieldType>(f->type())); |
||||
|
||||
if (!FieldAccessor::TrySet(f, md, upb_f) |
||||
#ifdef UPB_GOOGLE3 |
||||
&& !proto2_bridge_google3::Proto1FieldAccessor::TrySet(f, md, upb_f) |
||||
#endif |
||||
) { |
||||
// Unsupported reflection class.
|
||||
assert(false); |
||||
} |
||||
|
||||
if (upb_f->type() == UPB_TYPE(ENUM)) { |
||||
// We set the enum default symbolically.
|
||||
upb_f->set_default(f->default_value_enum()->name()); |
||||
upb_f->set_subtype_name(f->enum_type()->full_name()); |
||||
} else { |
||||
// Set field default for primitive types. Need to switch on the upb type
|
||||
// rather than the proto2 type, because upb_f->type() may have been changed
|
||||
// from BYTES to MESSAGE for a weak field.
|
||||
switch (upb_types[upb_f->type()].inmemory_type) { |
||||
case UPB_CTYPE_INT32: |
||||
upb_f->set_default(MakeValue(f->default_value_int32())); |
||||
break; |
||||
case UPB_CTYPE_INT64: |
||||
upb_f->set_default( |
||||
MakeValue(static_cast<int64_t>(f->default_value_int64()))); |
||||
break; |
||||
case UPB_CTYPE_UINT32: |
||||
upb_f->set_default(MakeValue(f->default_value_uint32())); |
||||
break; |
||||
case UPB_CTYPE_UINT64: |
||||
upb_f->set_default( |
||||
MakeValue(static_cast<uint64_t>(f->default_value_uint64()))); |
||||
break; |
||||
case UPB_CTYPE_DOUBLE: |
||||
upb_f->set_default(MakeValue(f->default_value_double())); |
||||
break; |
||||
case UPB_CTYPE_FLOAT: |
||||
upb_f->set_default(MakeValue(f->default_value_float())); |
||||
break; |
||||
case UPB_CTYPE_BOOL: |
||||
upb_f->set_default(MakeValue(f->default_value_bool())); |
||||
break; |
||||
case UPB_CTYPE_BYTEREGION: |
||||
upb_f->set_default(f->default_value_string()); |
||||
break; |
||||
} |
||||
} |
||||
return md->AddField(upb_f, &upb_f) ? upb_f : NULL; |
||||
} |
||||
|
||||
upb::MessageDef *NewEmptyMessageDef(const Message& m, void *owner) { |
||||
upb::MessageDef *md = upb::MessageDef::New(owner); |
||||
md->set_full_name(m.GetDescriptor()->full_name()); |
||||
md->prototype = GetPrototypeForMessage(m); |
||||
return md; |
||||
} |
||||
|
||||
upb::EnumDef* NewEnumDef(const EnumDescriptor* desc, void *owner) { |
||||
upb::EnumDef* e = upb::EnumDef::New(owner); |
||||
e->set_full_name(desc->full_name()); |
||||
for (int i = 0; i < desc->value_count(); i++) { |
||||
const EnumValueDescriptor* val = desc->value(i); |
||||
bool success = e->AddValue(val->name(), val->number()); |
||||
assert(success); |
||||
(void)success; |
||||
} |
||||
return e; |
||||
} |
||||
|
||||
void AddAllFields(upb::MessageDef* md) { |
||||
const Descriptor* d = |
||||
static_cast<const Message*>(md->prototype)->GetDescriptor(); |
||||
for (int i = 0; i < d->field_count(); i++) { |
||||
#ifdef UPB_GOOGLE3 |
||||
// Skip lazy fields for now since we can't properly handle them.
|
||||
if (d->field(i)->options().lazy()) continue; |
||||
#endif |
||||
// Extensions not supported yet.
|
||||
if (d->field(i)->is_extension()) continue; |
||||
AddFieldDef(d->field(i), md); |
||||
} |
||||
} |
||||
|
||||
upb::MessageDef *NewFullMessageDef(const Message& m, void *owner) { |
||||
upb::MessageDef* md = NewEmptyMessageDef(m, owner); |
||||
AddAllFields(md); |
||||
// TODO(haberman): add unknown field handler and extensions.
|
||||
return md; |
||||
} |
||||
|
||||
typedef std::map<std::string, upb::Def*> SymbolMap; |
||||
|
||||
static upb::MessageDef* NewFinalMessageDefHelper(const Message& m, void *owner, |
||||
SymbolMap* symbols) { |
||||
upb::MessageDef* md = NewFullMessageDef(m, owner); |
||||
// Must do this before processing submessages to prevent infinite recursion.
|
||||
(*symbols)[std::string(md->full_name())] = md->AsDef(); |
||||
|
||||
for (upb::MessageDef::Iterator i(md); !i.Done(); i.Next()) { |
||||
upb::FieldDef* f = i.field(); |
||||
if (!f->HasSubDef()) continue; |
||||
SymbolMap::iterator iter = symbols->find(f->subtype_name()); |
||||
upb::Def* subdef; |
||||
if (iter != symbols->end()) { |
||||
subdef = iter->second; |
||||
} else { |
||||
const FieldDescriptor* proto2_f = |
||||
m.GetDescriptor()->FindFieldByNumber(f->number()); |
||||
if (f->type() == UPB_TYPE(ENUM)) { |
||||
subdef = NewEnumDef(proto2_f->enum_type(), owner)->AsDef(); |
||||
(*symbols)[std::string(subdef->full_name())] = subdef; |
||||
} else { |
||||
assert(f->IsSubmessage()); |
||||
const Message* prototype = GetPrototypeForField(m, proto2_f); |
||||
subdef = NewFinalMessageDefHelper(*prototype, owner, symbols)->AsDef(); |
||||
} |
||||
} |
||||
f->set_subdef(subdef); |
||||
} |
||||
return md; |
||||
} |
||||
|
||||
const upb::MessageDef* NewFinalMessageDef(const Message& m, void *owner) { |
||||
SymbolMap symbols; |
||||
upb::MessageDef* ret = NewFinalMessageDefHelper(m, owner, &symbols); |
||||
|
||||
// Finalize defs.
|
||||
std::vector<upb::Def*> defs; |
||||
SymbolMap::iterator iter; |
||||
for (iter = symbols.begin(); iter != symbols.end(); ++iter) { |
||||
defs.push_back(iter->second); |
||||
} |
||||
Status status; |
||||
bool success = Def::Finalize(defs, &status); |
||||
assert(success); |
||||
(void)success; |
||||
|
||||
// Unref all defs except the top-level one that we are returning.
|
||||
for (int i = 0; i < static_cast<int>(defs.size()); i++) { |
||||
if (defs[i] != ret->AsDef()) defs[i]->Unref(owner); |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
} // namespace proto2_bridge
|
||||
} // namespace upb
|
@ -0,0 +1,170 @@ |
||||
//
|
||||
// upb - a minimalist implementation of protocol buffers.
|
||||
//
|
||||
// Copyright (c) 2011-2012 Google Inc. See LICENSE for details.
|
||||
// Author: Josh Haberman <jhaberman@gmail.com>
|
||||
//
|
||||
// A bridge between upb and proto2, allows populating proto2 generated
|
||||
// classes using upb's parser, translating between descriptors and defs, etc.
|
||||
//
|
||||
// This is designed to be able to be compiled against either the open-source
|
||||
// version of protocol buffers or the Google-internal proto2. The two are
|
||||
// the same in most ways, but live in different namespaces (proto2 vs
|
||||
// google::protobuf) and have a few other more minor differences.
|
||||
//
|
||||
// The bridge gives you a lot of control over which fields will be written to
|
||||
// the message (fields that are not written will just be skipped), and whether
|
||||
// unknown fields are written to the UnknownFieldSet. This can save a lot of
|
||||
// work if the client only cares about some subset of the fields.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// // Build a def that will have all fields and parse just like proto2 would.
|
||||
// const upb::MessageDef* md = upb::proto2_bridge::NewMessageDef(&MyProto());
|
||||
//
|
||||
// // JIT the parser; should only be done once ahead-of-time.
|
||||
// upb::Handlers* handlers = upb::NewHandlersForMessage(md);
|
||||
// upb::DecoderPlan* plan = upb::DecoderPlan::New(handlers);
|
||||
// handlers->Unref();
|
||||
//
|
||||
// // The actual parsing.
|
||||
// MyProto proto;
|
||||
// upb::Decoder decoder;
|
||||
// upb::StringSource source(buf, len);
|
||||
// decoder.ResetPlan(plan, 0);
|
||||
// decoder.ResetInput(source.AllBytes(), &proto);
|
||||
// CHECK(decoder.Decode() == UPB_OK) << decoder.status();
|
||||
//
|
||||
// To parse only one field and skip all others:
|
||||
//
|
||||
// const upb::MessageDef* md =
|
||||
// upb::proto2_bridge::NewEmptyMessageDef(MyProto().GetPrototype());
|
||||
// upb::proto2_bridge::AddFieldDef(
|
||||
// MyProto::descriptor()->FindFieldByName("my_field"), md);
|
||||
// upb::Finalize(md);
|
||||
//
|
||||
// // Now continue with "JIT the parser" from above.
|
||||
//
|
||||
// Note that there is currently no support for
|
||||
// CodedInputStream::SetExtensionRegistry(), which allows specifying a separate
|
||||
// DescriptorPool and MessageFactory for extensions. Since this is a property
|
||||
// of the input in proto2, it's difficult to build a plan ahead-of-time that
|
||||
// can properly support this. If it's an important use case, the caller should
|
||||
// probably build a upb plan explicitly.
|
||||
|
||||
#ifndef UPB_PROTO2_BRIDGE |
||||
#define UPB_PROTO2_BRIDGE |
||||
|
||||
#include <vector> |
||||
|
||||
namespace google { |
||||
namespace protobuf { |
||||
class Descriptor; |
||||
class EnumDescriptor; |
||||
class FieldDescriptor; |
||||
class FileDescriptor; |
||||
class Message; |
||||
} // namespace google
|
||||
} // namespace protobuf
|
||||
|
||||
namespace proto2 { |
||||
class Descriptor; |
||||
class EnumDescriptor; |
||||
class FieldDescriptor; |
||||
class FileDescriptor; |
||||
class Message; |
||||
} // namespace proto2
|
||||
|
||||
|
||||
namespace upb { |
||||
|
||||
class Def; |
||||
class FieldDef; |
||||
class MessageDef; |
||||
|
||||
namespace proto2_bridge { |
||||
|
||||
// Unfinalized defs ////////////////////////////////////////////////////////////
|
||||
|
||||
// Creating of UNFINALIZED defs. All of these functions return defs that are
|
||||
// still mutable and have not been finalized. They must be finalized before
|
||||
// using them to parse anything. This is useful if you want more control over
|
||||
// the process of constructing defs, eg. to add the specific set of fields you
|
||||
// care about.
|
||||
|
||||
// Creates a new upb::MessageDef that corresponds to the type in the given
|
||||
// prototype message. The MessageDef will not have any fields added to it.
|
||||
upb::MessageDef *NewEmptyMessageDef(const proto2::Message& m, void *owner); |
||||
upb::MessageDef *NewEmptyMessageDef(const google::protobuf::Message& desc, |
||||
void *owner); |
||||
|
||||
// Adds a new upb::FieldDef to the given MessageDef corresponding to the given
|
||||
// FieldDescriptor. The FieldDef will be given an accessor and offset so that
|
||||
// it can be used to read and write data into the proto2::Message classes.
|
||||
// The given MessageDef must have been constructed with NewEmptyDefForMessage()
|
||||
// and f->containing_type() must correspond to the message that was used.
|
||||
//
|
||||
// Any submessage, group, or enum fields will be given symbolic references to
|
||||
// the subtype, which must be resolved before the MessageDef can be finalized.
|
||||
//
|
||||
// On success, returns the FieldDef that was added (caller does not own a ref).
|
||||
// If an existing field had the same name or number, returns NULL.
|
||||
upb::FieldDef* AddFieldDef(const proto2::FieldDescriptor* f, |
||||
upb::MessageDef* md); |
||||
upb::FieldDef* AddFieldDef(const google::protobuf::FieldDescriptor* f, |
||||
upb::MessageDef* md); |
||||
|
||||
// Given a MessageDef that was constructed with NewEmptyDefForMessage(), adds
|
||||
// FieldDefs for all fields defined in the original message, but not for any
|
||||
// extensions or unknown fields. The given MessageDef must not have any fields
|
||||
// that have the same name or number as any of the fields we are adding (the
|
||||
// easiest way to guarantee this is to start with an empty MessageDef).
|
||||
//
|
||||
// Returns true on success or false if any of the fields could not be added.
|
||||
void AddAllFields(upb::MessageDef* md); |
||||
|
||||
// TODO(haberman): Add:
|
||||
// // Adds a handler that will store unknown fields in the UnknownFieldSet.
|
||||
// void AddUnknownFieldHandler(upb::MessageDef* md);
|
||||
|
||||
// Returns a new upb::MessageDef that contains handlers for all fields, unknown
|
||||
// fields, and any extensions in the descriptor's pool. The resulting
|
||||
// def/handlers should be equivalent to the generated code constructed by the
|
||||
// protobuf compiler (or the code in DynamicMessage) for the given type.
|
||||
// The subdefs for message/enum fields (if any) will be referenced symbolically,
|
||||
// and will need to be resolved before being finalized.
|
||||
//
|
||||
// TODO(haberman): Add missing support (LazyField, MessageSet, and extensions).
|
||||
//
|
||||
// TODO(haberman): possibly add a similar function that lets you supply a
|
||||
// separate DescriptorPool and MessageFactory for extensions, to support
|
||||
// proto2's io::CodedInputStream::SetExtensionRegistry().
|
||||
upb::MessageDef* NewFullMessageDef(const proto2::Message& m, void *owner); |
||||
upb::MessageDef* NewFullMessageDef(const google::protobuf::Message& m, |
||||
void *owner); |
||||
|
||||
// Returns a new upb::EnumDef that corresponds to the given EnumDescriptor.
|
||||
// Caller owns a ref on the returned EnumDef.
|
||||
upb::EnumDef* NewEnumDef(const proto2::EnumDescriptor* desc, void *owner); |
||||
upb::EnumDef* NewEnumDef(const google::protobuf::EnumDescriptor* desc, |
||||
void *owner); |
||||
|
||||
// Finalized defs //////////////////////////////////////////////////////////////
|
||||
|
||||
// These functions return FINALIZED defs, meaning that they are immutable and
|
||||
// ready for use. Since they are immutable you cannot make any further changes
|
||||
// to eg. the set of fields, but these functions are more convenient if you
|
||||
// simply want to parse a message exactly how the built-in proto2 parser would.
|
||||
|
||||
// Creates a returns a finalized MessageDef for the give message and its entire
|
||||
// type tree that will include all fields and unknown handlers (ie. it will
|
||||
// parse just like proto2 would).
|
||||
const upb::MessageDef* NewFinalMessageDef(const proto2::Message& m, |
||||
void *owner); |
||||
const upb::MessageDef* NewFinalMessageDef(const google::protobuf::Message& m, |
||||
void *owner); |
||||
|
||||
} // namespace proto2_bridge
|
||||
} // namespace upb
|
||||
|
||||
#endif |
@ -1,19 +1,174 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2011 Google Inc. See LICENSE for details. |
||||
* |
||||
* Test of defs and symtab. There should be far more tests of edge conditions |
||||
* (like attempts to link defs that don't have required properties set). |
||||
*/ |
||||
|
||||
#undef NDEBUG /* ensure tests always assert. */ |
||||
#include "upb/def.h" |
||||
#include "upb/pb/glue.h" |
||||
#include "upb_test.h" |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
int main() { |
||||
upb_symtab *s = upb_symtab_new(); |
||||
const char *descriptor_file; |
||||
|
||||
// Will be empty atm since we haven't added anything to the symtab.
|
||||
static void test_empty_symtab() { |
||||
upb_symtab *s = upb_symtab_new(); |
||||
int count; |
||||
const upb_def **defs = upb_symtab_getdefs(s, &count, UPB_DEF_ANY); |
||||
for (int i = 0; i < count; i++) { |
||||
upb_def_unref(defs[i]); |
||||
} |
||||
const upb_def **defs = upb_symtab_getdefs(s, &count, UPB_DEF_ANY, NULL); |
||||
ASSERT(count == 0); |
||||
free(defs); |
||||
upb_symtab_unref(s); |
||||
} |
||||
|
||||
static upb_symtab *load_test_proto() { |
||||
upb_symtab *s = upb_symtab_new(); |
||||
ASSERT(s); |
||||
upb_status status = UPB_STATUS_INIT; |
||||
if (!upb_load_descriptor_file_into_symtab(s, descriptor_file, &status)) { |
||||
fprintf(stderr, "Error loading descriptor file: %s\n", |
||||
upb_status_getstr(&status)); |
||||
exit(1); |
||||
} |
||||
upb_status_uninit(&status); |
||||
return s; |
||||
} |
||||
|
||||
static void test_cycles() { |
||||
upb_symtab *s = load_test_proto(); |
||||
|
||||
// Test cycle detection by making a cyclic def's main refcount go to zero
|
||||
// and then be incremented to one again.
|
||||
const upb_def *def = upb_symtab_lookup(s, "A", &def); |
||||
ASSERT(def); |
||||
ASSERT(upb_def_isfinalized(def)); |
||||
upb_symtab_unref(s); |
||||
|
||||
// Message A has only one subfield: "optional B b = 1".
|
||||
const upb_msgdef *m = upb_downcast_msgdef_const(def); |
||||
upb_fielddef *f = upb_msgdef_itof(m, 1); |
||||
ASSERT(f); |
||||
ASSERT(upb_hassubdef(f)); |
||||
const upb_def *def2 = upb_fielddef_subdef(f); |
||||
ASSERT(upb_downcast_msgdef_const(def2)); |
||||
ASSERT(strcmp(upb_def_fullname(def2), "B") == 0); |
||||
|
||||
upb_def_ref(def2, &def2); |
||||
upb_def_unref(def, &def); |
||||
upb_def_unref(def2, &def2); |
||||
} |
||||
|
||||
static void test_fielddef_unref() { |
||||
upb_symtab *s = load_test_proto(); |
||||
const upb_msgdef *md = upb_symtab_lookupmsg(s, "A", &md); |
||||
upb_fielddef *f = upb_msgdef_itof(md, 1); |
||||
upb_fielddef_ref(f, &f); |
||||
|
||||
// Unref symtab and msgdef; now fielddef is the only thing keeping the msgdef
|
||||
// alive.
|
||||
upb_symtab_unref(s); |
||||
upb_msgdef_unref(md, &md); |
||||
// Check that md is still alive.
|
||||
ASSERT(strcmp(upb_def_fullname(UPB_UPCAST(md)), "A") == 0); |
||||
|
||||
// Check that unref of fielddef frees the whole remaining graph.
|
||||
upb_fielddef_unref(f, &f); |
||||
} |
||||
|
||||
static void test_fielddef_accessors() { |
||||
upb_fielddef *f1 = upb_fielddef_new(&f1); |
||||
upb_fielddef *f2 = upb_fielddef_new(&f2); |
||||
|
||||
ASSERT(upb_fielddef_ismutable(f1)); |
||||
upb_fielddef_setname(f1, "f1"); |
||||
upb_fielddef_setnumber(f1, 1937); |
||||
upb_fielddef_settype(f1, UPB_TYPE(FIXED64)); |
||||
upb_fielddef_setlabel(f1, UPB_LABEL(REPEATED)); |
||||
ASSERT(upb_fielddef_number(f1) == 1937); |
||||
|
||||
ASSERT(upb_fielddef_ismutable(f2)); |
||||
upb_fielddef_setname(f2, "f2"); |
||||
upb_fielddef_setnumber(f2, 1572); |
||||
upb_fielddef_settype(f2, UPB_TYPE(BYTES)); |
||||
upb_fielddef_setlabel(f2, UPB_LABEL(REPEATED)); |
||||
ASSERT(upb_fielddef_number(f2) == 1572); |
||||
|
||||
upb_fielddef_unref(f1, &f1); |
||||
upb_fielddef_unref(f2, &f2); |
||||
} |
||||
|
||||
static upb_fielddef *newfield( |
||||
const char *name, int32_t num, uint8_t type, uint8_t label, |
||||
const char *type_name, void *owner) { |
||||
upb_fielddef *f = upb_fielddef_new(owner); |
||||
upb_fielddef_setname(f, name); |
||||
upb_fielddef_setnumber(f, num); |
||||
upb_fielddef_settype(f, type); |
||||
upb_fielddef_setlabel(f, label); |
||||
upb_fielddef_setsubtypename(f, type_name); |
||||
return f; |
||||
} |
||||
|
||||
static upb_msgdef *upb_msgdef_newnamed(const char *name, void *owner) { |
||||
upb_msgdef *m = upb_msgdef_new(owner); |
||||
upb_def_setfullname(UPB_UPCAST(m), name); |
||||
return m; |
||||
} |
||||
|
||||
INLINE upb_enumdef *upb_enumdef_newnamed(const char *name, void *owner) { |
||||
upb_enumdef *e = upb_enumdef_new(owner); |
||||
upb_def_setfullname(UPB_UPCAST(e), name); |
||||
return e; |
||||
} |
||||
|
||||
void test_replacement() { |
||||
upb_symtab *s = upb_symtab_new(); |
||||
|
||||
upb_msgdef *m = upb_msgdef_newnamed("MyMessage", &s); |
||||
upb_msgdef_addfield(m, newfield( |
||||
"field1", 1, UPB_TYPE(ENUM), UPB_LABEL(OPTIONAL), ".MyEnum", &s), &s); |
||||
upb_msgdef *m2 = upb_msgdef_newnamed("MyMessage2", &s); |
||||
upb_enumdef *e = upb_enumdef_newnamed("MyEnum", &s); |
||||
|
||||
upb_def *newdefs[] = {UPB_UPCAST(m), UPB_UPCAST(m2), UPB_UPCAST(e)}; |
||||
upb_status status = UPB_STATUS_INIT; |
||||
ASSERT_STATUS(upb_symtab_add(s, newdefs, 3, &s, &status), &status); |
||||
|
||||
// Try adding a new definition of MyEnum, MyMessage should get replaced with
|
||||
// a new version.
|
||||
upb_enumdef *e2 = upb_enumdef_new(&s); |
||||
upb_def_setfullname(UPB_UPCAST(e2), "MyEnum"); |
||||
upb_def *newdefs2[] = {UPB_UPCAST(e2)}; |
||||
ASSERT_STATUS(upb_symtab_add(s, newdefs2, 1, &s, &status), &status); |
||||
|
||||
const upb_msgdef *m3 = upb_symtab_lookupmsg(s, "MyMessage", &m3); |
||||
ASSERT(m3); |
||||
// Must be different because it points to MyEnum which was replaced.
|
||||
ASSERT(m3 != m); |
||||
upb_msgdef_unref(m3, &m3); |
||||
|
||||
m3 = upb_symtab_lookupmsg(s, "MyMessage2", &m3); |
||||
// Should be the same because it was not replaced, nor were any defs that
|
||||
// are reachable from it.
|
||||
ASSERT(m3 == m2); |
||||
upb_msgdef_unref(m3, &m3); |
||||
|
||||
upb_symtab_unref(s); |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) { |
||||
if (argc < 2) { |
||||
fprintf(stderr, "Usage: test_def <test.proto.pb>\n"); |
||||
return 1; |
||||
} |
||||
descriptor_file = argv[1]; |
||||
test_empty_symtab(); |
||||
test_cycles(); |
||||
test_fielddef_accessors(); |
||||
test_fielddef_unref(); |
||||
test_replacement(); |
||||
return 0; |
||||
} |
||||
|
@ -1,121 +0,0 @@ |
||||
|
||||
|
||||
#include <assert.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include "upb/def.h" |
||||
#include "upb/handlers.h" |
||||
#include "upb/pb/decoder.h" |
||||
#include "upb/pb/glue.h" |
||||
#include "upb_test.h" |
||||
|
||||
const char *descriptor_file; |
||||
|
||||
static upb_symtab *load_test_proto() { |
||||
upb_symtab *s = upb_symtab_new(); |
||||
ASSERT(s); |
||||
upb_status status = UPB_STATUS_INIT; |
||||
if (!upb_load_descriptor_file_into_symtab(s, descriptor_file, &status)) { |
||||
fprintf(stderr, "Error loading descriptor file: %s\n", |
||||
upb_status_getstr(&status)); |
||||
exit(1); |
||||
} |
||||
upb_status_uninit(&status); |
||||
return s; |
||||
} |
||||
|
||||
static upb_flow_t upb_test_onvalue(void *c, upb_value fval, upb_value val) { |
||||
(void)c; |
||||
(void)fval; |
||||
(void)val; |
||||
return UPB_CONTINUE; |
||||
} |
||||
|
||||
static void test_upb_jit() { |
||||
upb_symtab *s = load_test_proto(); |
||||
const upb_def *def = upb_symtab_lookup(s, "SimplePrimitives"); |
||||
ASSERT(def); |
||||
|
||||
upb_handlers *h = upb_handlers_new(); |
||||
upb_handlerset hset = {NULL, NULL, &upb_test_onvalue, NULL, NULL, NULL, NULL}; |
||||
upb_handlers_reghandlerset(h, upb_downcast_msgdef_const(def), &hset); |
||||
upb_decoderplan *p = upb_decoderplan_new(h, true); |
||||
#ifdef UPB_USE_JIT_X64 |
||||
ASSERT(upb_decoderplan_hasjitcode(p)); |
||||
#else |
||||
ASSERT(!upb_decoderplan_hasjitcode(p)); |
||||
#endif |
||||
upb_decoderplan_unref(p); |
||||
upb_symtab_unref(s); |
||||
upb_def_unref(def); |
||||
upb_handlers_unref(h); |
||||
} |
||||
|
||||
static void test_upb_symtab() { |
||||
upb_symtab *s = load_test_proto(); |
||||
|
||||
// Test cycle detection by making a cyclic def's main refcount go to zero
|
||||
// and then be incremented to one again.
|
||||
const upb_def *def = upb_symtab_lookup(s, "A"); |
||||
ASSERT(def); |
||||
upb_symtab_unref(s); |
||||
const upb_msgdef *m = upb_downcast_msgdef_const(def); |
||||
upb_msg_iter i = upb_msg_begin(m); |
||||
ASSERT(!upb_msg_done(i)); |
||||
upb_fielddef *f = upb_msg_iter_field(i); |
||||
ASSERT(upb_hassubdef(f)); |
||||
upb_def *def2 = f->def; |
||||
|
||||
i = upb_msg_next(m, i); |
||||
ASSERT(upb_msg_done(i)); // "A" should only have one field.
|
||||
|
||||
ASSERT(upb_downcast_msgdef(def2)); |
||||
upb_def_ref(def2); |
||||
upb_def_unref(def); |
||||
upb_def_unref(def2); |
||||
} |
||||
|
||||
static void test_upb_two_fielddefs() { |
||||
upb_fielddef *f1 = upb_fielddef_new(); |
||||
upb_fielddef *f2 = upb_fielddef_new(); |
||||
|
||||
ASSERT(upb_fielddef_ismutable(f1)); |
||||
upb_fielddef_setname(f1, ""); |
||||
upb_fielddef_setnumber(f1, 1937); |
||||
upb_fielddef_settype(f1, UPB_TYPE(FIXED64)); |
||||
upb_fielddef_setlabel(f1, UPB_LABEL(REPEATED)); |
||||
upb_fielddef_settypename(f1, ""); |
||||
ASSERT(upb_fielddef_number(f1) == 1937); |
||||
|
||||
ASSERT(upb_fielddef_ismutable(f2)); |
||||
upb_fielddef_setname(f2, ""); |
||||
upb_fielddef_setnumber(f2, 1572); |
||||
upb_fielddef_settype(f2, UPB_TYPE(BYTES)); |
||||
upb_fielddef_setlabel(f2, UPB_LABEL(REPEATED)); |
||||
upb_fielddef_settypename(f2, ""); |
||||
ASSERT(upb_fielddef_number(f2) == 1572); |
||||
|
||||
upb_fielddef_unref(f1); |
||||
upb_fielddef_unref(f2); |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) |
||||
{ |
||||
if (argc < 2) { |
||||
fprintf(stderr, "Usage: test_cpp <descriptor file>\n"); |
||||
return 1; |
||||
} |
||||
descriptor_file = argv[1]; |
||||
#define TEST(func) do { \ |
||||
int assertions_before = num_assertions; \
|
||||
printf("Running " #func "..."); fflush(stdout); \
|
||||
func(); \
|
||||
printf("ok (%d assertions).\n", num_assertions - assertions_before); \
|
||||
} while (0) |
||||
|
||||
TEST(test_upb_symtab); |
||||
TEST(test_upb_jit); |
||||
TEST(test_upb_two_fielddefs); |
||||
printf("All tests passed (%d assertions).\n", num_assertions); |
||||
return 0; |
||||
} |
@ -1,181 +0,0 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2009 Google Inc. See LICENSE for details. |
||||
* Author: Josh Haberman <jhaberman@gmail.com> |
||||
* |
||||
* Only a very small part of upb is thread-safe. Notably, individual |
||||
* messages, arrays, and strings are *not* thread safe for mutating. |
||||
* However, we do make message *metadata* such as upb_msgdef and |
||||
* upb_symtab thread-safe, and their ownership is tracked via atomic |
||||
* refcounting. This header implements the small number of atomic |
||||
* primitives required to support this. The primitives we implement |
||||
* are: |
||||
* |
||||
* - a reader/writer lock (wrappers around platform-provided mutexes). |
||||
* - an atomic refcount. |
||||
* |
||||
* TODO: This needs some revisiting/refinement, see: |
||||
* http://code.google.com/p/upb/issues/detail?id=8
|
||||
*/ |
||||
|
||||
#ifndef UPB_ATOMIC_H_ |
||||
#define UPB_ATOMIC_H_ |
||||
|
||||
#include <stdbool.h> |
||||
#include <assert.h> |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/* inline if possible, emit standalone code if required. */ |
||||
#ifndef INLINE |
||||
#define INLINE static inline |
||||
#endif |
||||
|
||||
// Until this stuff is actually working, make thread-unsafe the default.
|
||||
#define UPB_THREAD_UNSAFE |
||||
|
||||
#ifdef UPB_THREAD_UNSAFE |
||||
|
||||
/* Non-thread-safe implementations. ******************************************/ |
||||
|
||||
typedef struct { |
||||
int v; |
||||
} upb_atomic_t; |
||||
|
||||
#define UPB_ATOMIC_INIT(x) {x} |
||||
|
||||
INLINE void upb_atomic_init(upb_atomic_t *a, int val) { a->v = val; } |
||||
INLINE bool upb_atomic_ref(upb_atomic_t *a) { return a->v++ == 0; } |
||||
INLINE bool upb_atomic_unref(upb_atomic_t *a) { assert(a->v > 0); return --a->v == 0; } |
||||
INLINE int upb_atomic_read(upb_atomic_t *a) { return a->v; } |
||||
INLINE bool upb_atomic_add(upb_atomic_t *a, int val) { |
||||
a->v += val; |
||||
return a->v == 0; |
||||
} |
||||
|
||||
#endif |
||||
|
||||
/* Atomic refcount ************************************************************/ |
||||
|
||||
#ifdef UPB_THREAD_UNSAFE |
||||
|
||||
/* Already defined above. */ |
||||
|
||||
#elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || __GNUC__ > 4 |
||||
|
||||
/* GCC includes atomic primitives. */ |
||||
|
||||
typedef struct { |
||||
volatile int v; |
||||
} upb_atomic_t; |
||||
|
||||
INLINE void upb_atomic_init(upb_atomic_t *a, int val) { |
||||
a->v = val; |
||||
__sync_synchronize(); /* Ensure the initialized value is visible. */ |
||||
} |
||||
|
||||
INLINE bool upb_atomic_ref(upb_atomic_t *a) { |
||||
return __sync_fetch_and_add(&a->v, 1) == 0; |
||||
} |
||||
|
||||
INLINE bool upb_atomic_add(upb_atomic_t *a, int n) { |
||||
return __sync_add_and_fetch(&a->v, n) == 0; |
||||
} |
||||
|
||||
INLINE bool upb_atomic_unref(upb_atomic_t *a) { |
||||
return __sync_sub_and_fetch(&a->v, 1) == 0; |
||||
} |
||||
|
||||
INLINE bool upb_atomic_read(upb_atomic_t *a) { |
||||
return __sync_fetch_and_add(&a->v, 0); |
||||
} |
||||
|
||||
#elif defined(WIN32) |
||||
|
||||
/* Windows defines atomic increment/decrement. */ |
||||
#include <Windows.h> |
||||
|
||||
typedef struct { |
||||
volatile LONG val; |
||||
} upb_atomic_t; |
||||
|
||||
INLINE void upb_atomic_init(upb_atomic_t *a, int val) { |
||||
InterlockedExchange(&a->val, val); |
||||
} |
||||
|
||||
INLINE bool upb_atomic_ref(upb_atomic_t *a) { |
||||
return InterlockedIncrement(&a->val) == 1; |
||||
} |
||||
|
||||
INLINE bool upb_atomic_unref(upb_atomic_t *a) { |
||||
return InterlockedDecrement(&a->val) == 0; |
||||
} |
||||
|
||||
#else |
||||
#error Atomic primitives not defined for your platform/CPU. \ |
||||
Implement them or compile with UPB_THREAD_UNSAFE. |
||||
#endif |
||||
|
||||
INLINE bool upb_atomic_only(upb_atomic_t *a) { |
||||
return upb_atomic_read(a) == 1; |
||||
} |
||||
|
||||
/* Reader/Writer lock. ********************************************************/ |
||||
|
||||
#ifdef UPB_THREAD_UNSAFE |
||||
|
||||
typedef struct { |
||||
} upb_rwlock_t; |
||||
|
||||
INLINE void upb_rwlock_init(const upb_rwlock_t *l) { (void)l; } |
||||
INLINE void upb_rwlock_destroy(const upb_rwlock_t *l) { (void)l; } |
||||
INLINE void upb_rwlock_rdlock(const upb_rwlock_t *l) { (void)l; } |
||||
INLINE void upb_rwlock_wrlock(const upb_rwlock_t *l) { (void)l; } |
||||
INLINE void upb_rwlock_unlock(const upb_rwlock_t *l) { (void)l; } |
||||
|
||||
#elif defined(UPB_USE_PTHREADS) |
||||
|
||||
#include <pthread.h> |
||||
|
||||
typedef struct { |
||||
pthread_rwlock_t lock; |
||||
} upb_rwlock_t; |
||||
|
||||
INLINE void upb_rwlock_init(const upb_rwlock_t *l) { |
||||
/* TODO: check return value. */ |
||||
pthread_rwlock_init(&l->lock, NULL); |
||||
} |
||||
|
||||
INLINE void upb_rwlock_destroy(const upb_rwlock_t *l) { |
||||
/* TODO: check return value. */ |
||||
pthread_rwlock_destroy(&l->lock); |
||||
} |
||||
|
||||
INLINE void upb_rwlock_rdlock(const upb_rwlock_t *l) { |
||||
/* TODO: check return value. */ |
||||
pthread_rwlock_rdlock(&l->lock); |
||||
} |
||||
|
||||
INLINE void upb_rwlock_wrlock(const upb_rwlock_t *l) { |
||||
/* TODO: check return value. */ |
||||
pthread_rwlock_wrlock(&l->lock); |
||||
} |
||||
|
||||
INLINE void upb_rwlock_unlock(const upb_rwlock_t *l) { |
||||
/* TODO: check return value. */ |
||||
pthread_rwlock_unlock(&l->lock); |
||||
} |
||||
|
||||
#else |
||||
#error Reader/writer lock is not defined for your platform/CPU. \ |
||||
Implement it or compile with UPB_THREAD_UNSAFE. |
||||
#endif |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* UPB_ATOMIC_H_ */ |
@ -0,0 +1,224 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2012 Google Inc. See LICENSE for details. |
||||
* Author: Josh Haberman <jhaberman@gmail.com> |
||||
*/ |
||||
|
||||
#include <stdlib.h> |
||||
#include <limits.h> |
||||
#include "upb/refcount.h" |
||||
|
||||
// TODO(haberman): require client to define these if ref debugging is on.
|
||||
#ifndef UPB_LOCK |
||||
#define UPB_LOCK |
||||
#endif |
||||
|
||||
#ifndef UPB_UNLOCK |
||||
#define UPB_UNLOCK |
||||
#endif |
||||
|
||||
/* arch-specific atomic primitives *******************************************/ |
||||
|
||||
#ifdef UPB_THREAD_UNSAFE //////////////////////////////////////////////////////
|
||||
|
||||
INLINE void upb_atomic_inc(uint32_t *a) { (*a)++; } |
||||
INLINE bool upb_atomic_dec(uint32_t *a) { return --(*a) == 0; } |
||||
|
||||
#elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || __GNUC__ > 4 ///////////////////
|
||||
|
||||
INLINE void upb_atomic_inc(uint32_t *a) { __sync_fetch_and_add(a, 1); } |
||||
INLINE bool upb_atomic_dec(uint32_t *a) { |
||||
return __sync_sub_and_fetch(a, 1) == 0; |
||||
} |
||||
|
||||
#elif defined(WIN32) ///////////////////////////////////////////////////////////
|
||||
|
||||
#include <Windows.h> |
||||
|
||||
INLINE void upb_atomic_inc(upb_atomic_t *a) { InterlockedIncrement(&a->val); } |
||||
INLINE bool upb_atomic_dec(upb_atomic_t *a) { |
||||
return InterlockedDecrement(&a->val) == 0; |
||||
} |
||||
|
||||
#else |
||||
#error Atomic primitives not defined for your platform/CPU. \ |
||||
Implement them or compile with UPB_THREAD_UNSAFE. |
||||
#endif |
||||
|
||||
// Reserved index values.
|
||||
#define UPB_INDEX_UNDEFINED UINT16_MAX |
||||
#define UPB_INDEX_NOT_IN_STACK (UINT16_MAX - 1) |
||||
|
||||
static void upb_refcount_merge(upb_refcount *r, upb_refcount *from) { |
||||
if (upb_refcount_merged(r, from)) return; |
||||
*r->count += *from->count; |
||||
free(from->count); |
||||
upb_refcount *base = from; |
||||
|
||||
// Set all refcount pointers in the "from" chain to the merged refcount.
|
||||
do { from->count = r->count; } while ((from = from->next) != base); |
||||
|
||||
// Merge the two circularly linked lists by swapping their next pointers.
|
||||
upb_refcount *tmp = r->next; |
||||
r->next = base->next; |
||||
base->next = tmp; |
||||
} |
||||
|
||||
// Tarjan's algorithm, see:
|
||||
// http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
|
||||
|
||||
typedef struct { |
||||
int index; |
||||
upb_refcount **stack; |
||||
int stack_len; |
||||
upb_getsuccessors *func; |
||||
} upb_tarjan_state; |
||||
|
||||
static void upb_refcount_dofindscc(upb_refcount *obj, upb_tarjan_state *state); |
||||
|
||||
void upb_refcount_visit(upb_refcount *obj, upb_refcount *subobj, void *_state) { |
||||
upb_tarjan_state *state = _state; |
||||
if (subobj->index == UPB_INDEX_UNDEFINED) { |
||||
// Subdef has not yet been visited; recurse on it.
|
||||
upb_refcount_dofindscc(subobj, state); |
||||
obj->lowlink = UPB_MIN(obj->lowlink, subobj->lowlink); |
||||
} else if (subobj->index != UPB_INDEX_NOT_IN_STACK) { |
||||
// Subdef is in the stack and hence in the current SCC.
|
||||
obj->lowlink = UPB_MIN(obj->lowlink, subobj->index); |
||||
} |
||||
} |
||||
|
||||
static void upb_refcount_dofindscc(upb_refcount *obj, upb_tarjan_state *state) { |
||||
obj->index = state->index; |
||||
obj->lowlink = state->index; |
||||
state->index++; |
||||
state->stack[state->stack_len++] = obj; |
||||
|
||||
state->func(obj, state); // Visit successors.
|
||||
|
||||
if (obj->lowlink == obj->index) { |
||||
upb_refcount *scc_obj; |
||||
while ((scc_obj = state->stack[--state->stack_len]) != obj) { |
||||
upb_refcount_merge(obj, scc_obj); |
||||
scc_obj->index = UPB_INDEX_NOT_IN_STACK; |
||||
} |
||||
obj->index = UPB_INDEX_NOT_IN_STACK; |
||||
} |
||||
} |
||||
|
||||
bool upb_refcount_findscc(upb_refcount **refs, int n, upb_getsuccessors *func) { |
||||
// TODO(haberman): allocate less memory. We can't use n as a bound because
|
||||
// it doesn't include fielddefs. Could either use a dynamically-resizing
|
||||
// array or think of some other way.
|
||||
upb_tarjan_state state = {0, malloc(UINT16_MAX * sizeof(void*)), 0, func}; |
||||
if (state.stack == NULL) return false; |
||||
for (int i = 0; i < n; i++) |
||||
if (refs[i]->index == UPB_INDEX_UNDEFINED) |
||||
upb_refcount_dofindscc(refs[i], &state); |
||||
free(state.stack); |
||||
return true; |
||||
} |
||||
|
||||
|
||||
/* upb_refcount **************************************************************/ |
||||
|
||||
bool upb_refcount_init(upb_refcount *r, void *owner) { |
||||
r->count = malloc(sizeof(uint32_t)); |
||||
if (!r->count) return false; |
||||
// Initializing this here means upb_refcount_findscc() can only run once for
|
||||
// each refcount; may need to revise this to be more flexible.
|
||||
r->index = UPB_INDEX_UNDEFINED; |
||||
r->next = r; |
||||
#ifdef UPB_DEBUG_REFS |
||||
// We don't detect malloc() failures for UPB_DEBUG_REFS.
|
||||
upb_inttable_init(&r->refs); |
||||
*r->count = 0; |
||||
upb_refcount_ref(r, owner); |
||||
#else |
||||
*r->count = 1; |
||||
#endif |
||||
return true; |
||||
} |
||||
|
||||
void upb_refcount_uninit(upb_refcount *r) { |
||||
(void)r; |
||||
#ifdef UPB_DEBUG_REFS |
||||
assert(upb_inttable_count(&r->refs) == 0); |
||||
upb_inttable_uninit(&r->refs); |
||||
#endif |
||||
} |
||||
|
||||
// Moves an existing ref from ref_donor to new_owner, without changing the
|
||||
// overall ref count.
|
||||
void upb_refcount_donateref(upb_refcount *r, void *from, void *to) { |
||||
(void)r; (void)from; (void)to; |
||||
assert(from != to); |
||||
#ifdef UPB_DEBUG_REFS |
||||
upb_refcount_ref(r, to); |
||||
upb_refcount_unref(r, from); |
||||
#endif |
||||
} |
||||
|
||||
// Thread-safe operations //////////////////////////////////////////////////////
|
||||
|
||||
// Ref and unref are thread-safe.
|
||||
void upb_refcount_ref(upb_refcount *r, void *owner) { |
||||
(void)owner; |
||||
upb_atomic_inc(r->count); |
||||
#ifdef UPB_DEBUG_REFS |
||||
UPB_LOCK; |
||||
// Caller must not already own a ref.
|
||||
assert(upb_inttable_lookup(&r->refs, (uintptr_t)owner) == NULL); |
||||
|
||||
// If a ref is leaked we want to blame the leak on the whoever leaked the
|
||||
// ref, not on who originally allocated the refcounted object. We accomplish
|
||||
// this as follows. When a ref is taken in DEBUG_REFS mode, we malloc() some
|
||||
// memory and arrange setup pointers like so:
|
||||
//
|
||||
// upb_refcount
|
||||
// +----------+ +---------+
|
||||
// | count |<-+ |
|
||||
// +----------+ +----------+
|
||||
// | table |---X-->| malloc'd |
|
||||
// +----------+ | memory |
|
||||
// +----------+
|
||||
//
|
||||
// Since the "malloc'd memory" is allocated inside of "ref" and free'd in
|
||||
// unref, it will cause a leak if not unref'd. And since the leaked memory
|
||||
// points to the object itself, the object will be considered "indirectly
|
||||
// lost" by tools like Valgrind and not shown unless requested (which is good
|
||||
// because the object's creator may not be responsible for the leak). But we
|
||||
// have to hide the pointer marked "X" above from Valgrind, otherwise the
|
||||
// malloc'd memory will appear to be indirectly leaked and the object itself
|
||||
// will still be considered the primary leak. We hide this pointer from
|
||||
// Valgrind (et all) by doing a bitwise not on it.
|
||||
upb_refcount **target = malloc(sizeof(void*)); |
||||
uintptr_t obfuscated = ~(uintptr_t)target; |
||||
*target = r; |
||||
upb_inttable_insert(&r->refs, (uintptr_t)owner, upb_value_uint64(obfuscated)); |
||||
UPB_UNLOCK; |
||||
#endif |
||||
} |
||||
|
||||
bool upb_refcount_unref(upb_refcount *r, void *owner) { |
||||
(void)owner; |
||||
bool ret = upb_atomic_dec(r->count); |
||||
#ifdef UPB_DEBUG_REFS |
||||
UPB_LOCK; |
||||
upb_value v; |
||||
bool success = upb_inttable_remove(&r->refs, (uintptr_t)owner, &v); |
||||
assert(success); |
||||
if (success) { |
||||
// Must un-obfuscate the pointer (see above).
|
||||
free((void*)(~upb_value_getuint64(v))); |
||||
} |
||||
UPB_UNLOCK; |
||||
#endif |
||||
if (ret) free(r->count); |
||||
return ret; |
||||
} |
||||
|
||||
bool upb_refcount_merged(const upb_refcount *r, const upb_refcount *r2) { |
||||
return r->count == r2->count; |
||||
} |
@ -0,0 +1,70 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2009 Google Inc. See LICENSE for details. |
||||
* Author: Josh Haberman <jhaberman@gmail.com> |
||||
* |
||||
* A thread-safe refcount that can optionally track references for debugging |
||||
* purposes. It helps avoid circular references by allowing a |
||||
* strongly-connected component in the graph to share a refcount. |
||||
* |
||||
* This interface is internal to upb. |
||||
*/ |
||||
|
||||
#ifndef UPB_REFCOUNT_H_ |
||||
#define UPB_REFCOUNT_H_ |
||||
|
||||
#include <stdbool.h> |
||||
#include <stdint.h> |
||||
#include "upb/table.h" |
||||
|
||||
#ifndef NDEBUG |
||||
#define UPB_DEBUG_REFS |
||||
#endif |
||||
|
||||
typedef struct _upb_refcount { |
||||
uint32_t *count; |
||||
struct _upb_refcount *next; // Circularly-linked list of this SCC.
|
||||
uint16_t index; // For SCC algorithm.
|
||||
uint16_t lowlink; // For SCC algorithm.
|
||||
#ifdef UPB_DEBUG_REFS |
||||
upb_inttable refs; |
||||
#endif |
||||
} upb_refcount; |
||||
|
||||
// NON THREAD SAFE operations //////////////////////////////////////////////////
|
||||
|
||||
// Initializes the refcount with a single ref for the given owner. Returns
|
||||
// NULL if memory could not be allocated.
|
||||
bool upb_refcount_init(upb_refcount *r, void *owner); |
||||
|
||||
// Uninitializes the refcount. May only be called after unref() returns true.
|
||||
void upb_refcount_uninit(upb_refcount *r); |
||||
|
||||
// Moves an existing ref from ref_donor to new_owner, without changing the
|
||||
// overall ref count.
|
||||
void upb_refcount_donateref(upb_refcount *r, void *from, void *to); |
||||
|
||||
// Finds strongly-connected components among some set of objects and merges all
|
||||
// refcounts that share a SCC. The given function will be called when the
|
||||
// algorithm needs to visit children of a particular object; the function
|
||||
// should call upb_refcount_visit() once for each child obj.
|
||||
//
|
||||
// Returns false if memory allocation failed.
|
||||
typedef void upb_getsuccessors(upb_refcount *obj, void*); |
||||
bool upb_refcount_findscc(upb_refcount **objs, int n, upb_getsuccessors *func); |
||||
void upb_refcount_visit(upb_refcount *obj, upb_refcount *subobj, void *closure); |
||||
|
||||
// Thread-safe operations //////////////////////////////////////////////////////
|
||||
|
||||
// Increases the ref count, the new ref is owned by "owner" which must not
|
||||
// already own a ref. Circular reference chains are not allowed.
|
||||
void upb_refcount_ref(upb_refcount *r, void *owner); |
||||
|
||||
// Release a ref owned by owner, returns true if that was the last ref.
|
||||
bool upb_refcount_unref(upb_refcount *r, void *owner); |
||||
|
||||
// Returns true if these two objects share a refcount.
|
||||
bool upb_refcount_merged(const upb_refcount *r, const upb_refcount *r2); |
||||
|
||||
#endif // UPB_REFCOUNT_H_
|
Loading…
Reference in new issue