From b79fd65a83e6563393807efbc44be3e2bdb16537 Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Tue, 11 Sep 2018 10:59:08 -0700 Subject: WIP. --- upb/msgfactory.c | 1 + 1 file changed, 1 insertion(+) (limited to 'upb/msgfactory.c') diff --git a/upb/msgfactory.c b/upb/msgfactory.c index 593c9dc..63df49e 100644 --- a/upb/msgfactory.c +++ b/upb/msgfactory.c @@ -1,4 +1,5 @@ +#include "upb/handlers.h" #include "upb/msgfactory.h" static bool is_power_of_two(size_t val) { -- cgit v1.2.3 From 8afe0b03a349cc259fb731ff2d2e0a13e47c166a Mon Sep 17 00:00:00 2001 From: Joshua Haberman Date: Thu, 20 Dec 2018 09:53:13 -0800 Subject: Some fixes for Ruby. --- upb/def.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- upb/def.h | 3 +++ upb/msgfactory.c | 9 --------- 3 files changed, 55 insertions(+), 12 deletions(-) (limited to 'upb/msgfactory.c') diff --git a/upb/def.c b/upb/def.c index ccbf407..83e5a99 100644 --- a/upb/def.c +++ b/upb/def.c @@ -355,6 +355,10 @@ const char *upb_enumdef_name(const upb_enumdef *e) { return shortdefname(e->full_name); } +const upb_filedef *upb_enumdef_file(const upb_enumdef *e) { + return e->file; +} + int32_t upb_enumdef_default(const upb_enumdef *e) { UPB_ASSERT(upb_enumdef_iton(e, e->defaultval)); return e->defaultval; @@ -606,6 +610,12 @@ bool upb_fielddef_hassubdef(const upb_fielddef *f) { return upb_fielddef_issubmsg(f) || upb_fielddef_type(f) == UPB_TYPE_ENUM; } +bool upb_fielddef_haspresence(const upb_fielddef *f) { + if (upb_fielddef_isseq(f)) return false; + if (upb_fielddef_issubmsg(f)) return true; + return f->file->syntax == UPB_SYNTAX_PROTO2; +} + static bool between(int32_t x, int32_t low, int32_t high) { return x >= low && x <= high; } @@ -624,6 +634,10 @@ const char *upb_msgdef_fullname(const upb_msgdef *m) { return m->full_name; } +const upb_filedef *upb_msgdef_file(const upb_msgdef *m) { + return m->file; +} + const char *upb_msgdef_name(const upb_msgdef *m) { return shortdefname(m->full_name); } @@ -1166,6 +1180,12 @@ static bool create_fielddef( * to the field_proto until later when we can properly resolve it. */ f->sub.unresolved = field_proto; + if (f->label_ == UPB_LABEL_REQUIRED && f->file->syntax == UPB_SYNTAX_PROTO3) { + upb_status_seterrf(ctx->status, "proto3 fields cannot be required (%s)", + f->full_name); + return false; + } + if (google_protobuf_FieldDescriptorProto_has_oneof_index(field_proto)) { int oneof_index = google_protobuf_FieldDescriptorProto_oneof_index(field_proto); @@ -1229,6 +1249,13 @@ static bool create_enumdef( values = google_protobuf_EnumDescriptorProto_value(enum_proto, &n); + if (n == 0) { + upb_status_seterrf(ctx->status, + "enums must contain at least one value (%s)", + e->full_name); + return false; + } + for (i = 0; i < n; i++) { const google_protobuf_EnumValueDescriptorProto *value = values[i]; upb_stringview name = google_protobuf_EnumValueDescriptorProto_name(value); @@ -1236,6 +1263,13 @@ static bool create_enumdef( int32_t num = google_protobuf_EnumValueDescriptorProto_number(value); upb_value v = upb_value_int32(num); + if (n == 0 && e->file->syntax == UPB_SYNTAX_PROTO3 && num != 0) { + upb_status_seterrf(ctx->status, + "for proto3, the first enum value must be zero (%s)", + e->full_name); + return false; + } + if (upb_strtable_lookup(&e->ntoi, name2, NULL)) { upb_status_seterrf(ctx->status, "duplicate enum label '%s'", name2); return false; @@ -1399,9 +1433,24 @@ static bool resolve_fielddef(const symtab_addctx *ctx, const char *prefix, if (google_protobuf_FieldDescriptorProto_has_default_value(field_proto)) { upb_stringview defaultval = google_protobuf_FieldDescriptorProto_default_value(field_proto); + + if (f->file->syntax == UPB_SYNTAX_PROTO3) { + upb_status_seterrf(ctx->status, + "proto3 fields cannot have explicit defaults (%s)", + f->full_name); + return false; + } + + if (upb_fielddef_issubmsg(f)) { + upb_status_seterrf(ctx->status, + "message fields cannot have explicit defaults (%s)", + f->full_name); + return false; + } + if (!parse_default(ctx, defaultval.data, defaultval.size, f)) { - upb_status_seterrf(ctx->status, "bad default '" UPB_STRINGVIEW_FORMAT "'", - UPB_STRINGVIEW_ARGS(defaultval)); + upb_status_seterrf(ctx->status, "couldn't parse default for field (%s)", + f->full_name); return false; } } @@ -1532,7 +1581,7 @@ static bool build_filedef( } return true; -} + } static bool upb_symtab_addtotabs(upb_symtab *s, symtab_addctx *ctx, upb_status *status) { diff --git a/upb/def.h b/upb/def.h index e6fdf21..8664fb5 100644 --- a/upb/def.h +++ b/upb/def.h @@ -202,6 +202,7 @@ float upb_fielddef_defaultfloat(const upb_fielddef *f); double upb_fielddef_defaultdouble(const upb_fielddef *f); const char *upb_fielddef_defaultstr(const upb_fielddef *f, size_t *len); bool upb_fielddef_hassubdef(const upb_fielddef *f); +bool upb_fielddef_haspresence(const upb_fielddef *f); const upb_msgdef *upb_fielddef_msgsubdef(const upb_fielddef *f); const upb_enumdef *upb_fielddef_enumsubdef(const upb_fielddef *f); @@ -349,6 +350,7 @@ class upb::MessageDef { UPB_BEGIN_EXTERN_C const char *upb_msgdef_fullname(const upb_msgdef *m); +const upb_filedef *upb_msgdef_file(const upb_msgdef *m); const char *upb_msgdef_name(const upb_msgdef *m); int upb_msgdef_numoneofs(const upb_msgdef *m); upb_syntax_t upb_msgdef_syntax(const upb_msgdef *m); @@ -485,6 +487,7 @@ UPB_BEGIN_EXTERN_C const char *upb_enumdef_fullname(const upb_enumdef *e); const char *upb_enumdef_name(const upb_enumdef *e); +const upb_filedef *upb_enumdef_file(const upb_enumdef *e); int32_t upb_enumdef_default(const upb_enumdef *e); int upb_enumdef_numvals(const upb_enumdef *e); diff --git a/upb/msgfactory.c b/upb/msgfactory.c index 73347b8..63df49e 100644 --- a/upb/msgfactory.c +++ b/upb/msgfactory.c @@ -46,15 +46,6 @@ static uint8_t upb_msg_fielddefsize(const upb_fielddef *f) { } } -static bool upb_fielddef_haspresence(const upb_fielddef *f) { - if (upb_fielddef_isseq(f)) return false; - if (upb_fielddef_issubmsg(f)) return true; - - /* Primitive field: return true unless there is a message that specifies - * presence should not exist. */ - return upb_msgdef_syntax(upb_fielddef_containingtype(f)) == UPB_SYNTAX_PROTO2; -} - /** upb_msglayout *************************************************************/ -- cgit v1.2.3 From 0553eff64a87eceff0de3b6260b4f2d45b61703a Mon Sep 17 00:00:00 2001 From: Joshua Haberman Date: Wed, 9 Jan 2019 22:40:50 -0800 Subject: upb_refcounted is gone! Some tests still to fix. --- BUILD | 49 +-- tests/json/test_json.cc | 23 +- tests/pb/test_encoder.cc | 22 +- upb/bindings/lua/def.c | 18 +- upb/bindings/lua/upb.h | 7 +- upb/handlers-inl.h | 34 +- upb/handlers.c | 366 ++++++-------------- upb/handlers.h | 84 ++--- upb/json/parser.c | 320 +++++++++--------- upb/json/parser.h | 50 ++- upb/json/parser.rl | 214 ++++++------ upb/json/printer.c | 17 +- upb/json/printer.h | 17 +- upb/msgfactory.c | 9 - upb/pb/compile_decoder.c | 176 ++++------ upb/pb/decoder.h | 128 ++----- upb/pb/decoder.int.h | 63 ++-- upb/pb/encoder.c | 5 +- upb/pb/encoder.h | 12 +- upb/pb/textprinter.c | 5 +- upb/pb/textprinter.h | 11 +- upb/refcounted.c | 851 ----------------------------------------------- upb/refcounted.h | 348 ------------------- 23 files changed, 666 insertions(+), 2163 deletions(-) delete mode 100644 upb/refcounted.c delete mode 100644 upb/refcounted.h (limited to 'upb/msgfactory.c') diff --git a/BUILD b/BUILD index bf988f9..ffb5bbd 100644 --- a/BUILD +++ b/BUILD @@ -1,16 +1,19 @@ load( ":build_defs.bzl", + "generated_file_staleness_test", + "lua_binary", "lua_cclibrary", "lua_library", - "lua_binary", "lua_test", - "generated_file_staleness_test", "make_shell_script", "upb_amalgamation", "upb_proto_library", "upb_proto_reflection_library", ) +# Remove once our C++ wrappers are more sane. +CXX_COPTS = ["-Wno-unused-private-field"] + # C/C++ rules ################################################################## cc_library( @@ -26,7 +29,6 @@ cc_library( "upb/msgfactory.c", "upb/port_def.inc", "upb/port_undef.inc", - "upb/refcounted.c", "upb/sink.c", "upb/structs.int.h", "upb/table.c", @@ -42,7 +44,6 @@ cc_library( "upb/handlers.h", "upb/msg.h", "upb/msgfactory.h", - "upb/refcounted.h", "upb/sink.h", "upb/upb.h", ], @@ -161,8 +162,8 @@ cc_test( upb_proto_reflection_library( name = "descriptor_upbproto", - deps = ["descriptor_proto"], upbc = ":protoc-gen-upb", + deps = ["descriptor_proto"], ) cc_test( @@ -178,14 +179,14 @@ cc_test( proto_library( name = "test_decoder_proto", srcs = [ - "tests/pb/test_decoder.proto" - ] + "tests/pb/test_decoder.proto", + ], ) upb_proto_reflection_library( name = "test_decoder_upbproto", - deps = ["test_decoder_proto"], upbc = ":protoc-gen-upb", + deps = ["test_decoder_proto"], ) cc_test( @@ -201,6 +202,7 @@ cc_test( cc_test( name = "test_encoder", srcs = ["tests/pb/test_encoder.cc"], + copts = CXX_COPTS, data = ["google/protobuf/descriptor.pb"], deps = [ ":upb_cc_bindings", @@ -212,19 +214,20 @@ cc_test( proto_library( name = "test_cpp_proto", srcs = [ - "tests/test_cpp.proto" - ] + "tests/test_cpp.proto", + ], ) upb_proto_reflection_library( name = "test_cpp_upbproto", - deps = ["test_cpp_proto"], upbc = ":protoc-gen-upb", + deps = ["test_cpp_proto"], ) cc_test( name = "test_cpp", srcs = ["tests/test_cpp.cc"], + copts = CXX_COPTS, deps = [ ":test_cpp_upbproto", ":upb", @@ -249,8 +252,8 @@ proto_library( upb_proto_reflection_library( name = "test_json_upbproto", - deps = ["test_json_proto"], upbc = ":protoc-gen-upb", + deps = ["test_json_proto"], ) cc_test( @@ -258,6 +261,7 @@ cc_test( srcs = [ "tests/json/test_json.cc", ], + copts = CXX_COPTS, deps = [ ":test_json_upbproto", ":upb_json", @@ -279,11 +283,11 @@ cc_binary( srcs = [ "tests/conformance_upb.c", ], + copts = ["-Ibazel-out/k8-fastbuild/bin"], deps = [ ":conformance_proto_upb", ":upb", ], - copts = ["-Ibazel-out/k8-fastbuild/bin"], ) make_shell_script( @@ -363,12 +367,16 @@ lua_test( cc_library( name = "upbc_generator", + srcs = [ + "upbc/generator.cc", + "upbc/message_layout.cc", + "upbc/message_layout.h", + ], hdrs = ["upbc/generator.h"], - srcs = ["upbc/generator.cc", "upbc/message_layout.h", "upbc/message_layout.cc"], deps = [ + "@absl//absl/strings", "@com_google_protobuf//:protobuf", "@com_google_protobuf//:protoc_lib", - "@absl//absl/strings", ], ) @@ -386,7 +394,7 @@ cc_binary( make_shell_script( name = "gen_run_cmake_build", out = "run_cmake_build.sh", - contents = "mkdir build && cd build && cmake .. && make -j8 && make test" + contents = "mkdir build && cd build && cmake .. && make -j8 && make test", ) sh_test( @@ -447,10 +455,13 @@ genrule( genrule( name = "gen_cmakelists", + srcs = [ + "BUILD", + "WORKSPACE", + ], outs = ["generated/CMakeLists.txt"], - srcs = ["BUILD", "WORKSPACE"], + cmd = "$(location :make_cmakelists) $@", tools = [":make_cmakelists"], - cmd = "$(location :make_cmakelists) $@" ) proto_library( @@ -469,8 +480,8 @@ genrule( ], cmd = "$(location @com_google_protobuf//:protoc) $< --upb_out=$(GENDIR)/generated --plugin=protoc-gen-upb=$(location :protoc-gen-upb)", tools = [ + ":protoc-gen-upb", "@com_google_protobuf//:protoc", - ":protoc-gen-upb" ], ) diff --git a/tests/json/test_json.cc b/tests/json/test_json.cc index 98bf59e..b9b50cd 100644 --- a/tests/json/test_json.cc +++ b/tests/json/test_json.cc @@ -204,12 +204,15 @@ void test_json_roundtrip_message(const char* json_src, // and compares the result. void test_json_roundtrip() { upb::SymbolTable* symtab = upb::SymbolTable::New(); + upb::HandlerCache* serialize_handlercache = upb::json::Printer::NewCache(false); + upb::json::CodeCache* parse_codecache = upb::json::CodeCache::New(); + const upb::MessageDef* md = upb_test_json_TestMessage_getmsgdef(symtab); ASSERT(md); - upb::reffed_ptr serialize_handlers( - upb::json::Printer::NewHandlers(md, false)); - upb::reffed_ptr parser_method( - upb::json::ParserMethod::New(md)); + const upb::Handlers* serialize_handlers = serialize_handlercache->Get(md); + const upb::json::ParserMethod* parser_method = parse_codecache->Get(md); + ASSERT(serialize_handlers); + ASSERT(parser_method); for (const TestCase* test_case = kTestRoundtripMessages; test_case->input != NULL; test_case++) { @@ -220,12 +223,13 @@ void test_json_roundtrip() { for (size_t i = 0; i < strlen(test_case->input); i++) { test_json_roundtrip_message(test_case->input, expected, - serialize_handlers.get(), parser_method.get(), - i); + serialize_handlers, parser_method, i); } } - serialize_handlers = upb::json::Printer::NewHandlers(md, true); + upb::HandlerCache::Free(serialize_handlercache); + serialize_handlercache = upb::json::Printer::NewCache(true); + serialize_handlers = serialize_handlercache->Get(md); for (const TestCase* test_case = kTestRoundtripMessagesPreserve; test_case->input != NULL; test_case++) { @@ -236,11 +240,12 @@ void test_json_roundtrip() { for (size_t i = 0; i < strlen(test_case->input); i++) { test_json_roundtrip_message(test_case->input, expected, - serialize_handlers.get(), parser_method.get(), - i); + serialize_handlers, parser_method, i); } } + upb::HandlerCache::Free(serialize_handlercache); + upb::json::CodeCache::Free(parse_codecache); upb::SymbolTable::Free(symtab); } diff --git a/tests/pb/test_encoder.cc b/tests/pb/test_encoder.cc index a0f8453..fac0dae 100644 --- a/tests/pb/test_encoder.cc +++ b/tests/pb/test_encoder.cc @@ -19,6 +19,11 @@ std::string read_string(const char *filename) { void test_pb_roundtrip() { std::string input = read_string("google/protobuf/descriptor.pb"); upb::SymbolTable* symtab = upb::SymbolTable::New(); + upb::HandlerCache* encoder_cache = upb::pb::Encoder::NewCache(); + upb::pb::CodeCache* decoder_cache = upb::pb::CodeCache::New(encoder_cache); + ASSERT(symtab); + ASSERT(encoder_cache); + ASSERT(decoder_cache); upb::Arena arena; google_protobuf_FileDescriptorSet *set = google_protobuf_FileDescriptorSet_parsenew( @@ -37,24 +42,23 @@ void test_pb_roundtrip() { const upb::MessageDef *md = symtab->LookupMessage("google.protobuf.FileDescriptorSet"); ASSERT(md); - printf("name: %s\n", md->full_name()); - upb::reffed_ptr encoder_handlers( - upb::pb::Encoder::NewHandlers(md)); - upb::reffed_ptr method( - upb::pb::DecoderMethod::New( - upb::pb::DecoderMethodOptions(encoder_handlers.get()))); + const upb::Handlers* encoder_handlers = encoder_cache->Get(md); + ASSERT(encoder_handlers); + const upb::pb::DecoderMethod* method = decoder_cache->Get(md); + ASSERT(method); upb::InlinedEnvironment<512> env; std::string output; upb::StringSink string_sink(&output); upb::pb::Encoder* encoder = - upb::pb::Encoder::Create(&env, encoder_handlers.get(), - string_sink.input()); + upb::pb::Encoder::Create(&env, encoder_handlers, string_sink.input()); upb::pb::Decoder* decoder = - upb::pb::Decoder::Create(&env, method.get(), encoder->input()); + upb::pb::Decoder::Create(&env, method, encoder->input()); ok = upb::BufferSource::PutBuffer(input, decoder->input()); ASSERT(ok); ASSERT(input == output); + upb::pb::CodeCache::Free(decoder_cache); + upb::HandlerCache::Free(encoder_cache); upb::SymbolTable::Free(symtab); } diff --git a/upb/bindings/lua/def.c b/upb/bindings/lua/def.c index 00b75c9..76510be 100644 --- a/upb/bindings/lua/def.c +++ b/upb/bindings/lua/def.c @@ -22,15 +22,9 @@ } while (0) -/* lupb_refcounted ************************************************************/ +/* lupb_wrapper ***************************************************************/ -/* All upb objects that use upb_refcounted have a userdata that begins with a - * pointer to that object. Each type has its own metatable. Objects are cached - * in a weak table indexed by the C pointer of the object they are caching. - * - * Note that we consistently use memcpy() to read to/from the object. This - * allows the userdata to use its own struct without violating aliasing, as - * long as it begins with a pointer. */ +/* Wrappers around upb objects. */ /* Checks type; if it matches, pulls the pointer out of the wrapper. */ void *lupb_checkwrapper(lua_State *L, int narg, const char *type) { @@ -263,7 +257,7 @@ void lupb_oneofdef_pushwrapper(lua_State *L, const upb_oneofdef *o) { } const upb_oneofdef *lupb_oneofdef_check(lua_State *L, int narg) { - return lupb_refcounted_check(L, narg, LUPB_ONEOFDEF); + return lupb_checkwrapper(L, narg, LUPB_ONEOFDEF); } static int lupb_oneofdef_containingtype(lua_State *L) { @@ -345,7 +339,7 @@ void lupb_msgdef_pushwrapper(lua_State *L, const upb_msgdef *m) { } const upb_msgdef *lupb_msgdef_check(lua_State *L, int narg) { - return lupb_refcounted_check(L, narg, LUPB_MSGDEF); + return lupb_checkwrapper(L, narg, LUPB_MSGDEF); } static int lupb_msgdef_len(lua_State *L) { @@ -453,7 +447,7 @@ static const struct luaL_Reg lupb_msgdef_m[] = { /* lupb_enumdef ***************************************************************/ const upb_enumdef *lupb_enumdef_check(lua_State *L, int narg) { - return lupb_refcounted_check(L, narg, LUPB_ENUMDEF); + return lupb_checkwrapper(L, narg, LUPB_ENUMDEF); } static void lupb_enumdef_pushwrapper(lua_State *L, const upb_enumdef *e) { @@ -527,7 +521,7 @@ void lupb_filedef_pushwrapper(lua_State *L, const upb_filedef *f) { } const upb_filedef *lupb_filedef_check(lua_State *L, int narg) { - return lupb_refcounted_check(L, narg, LUPB_FILEDEF); + return lupb_checkwrapper(L, narg, LUPB_FILEDEF); } static int lupb_filedef_dep(lua_State *L) { diff --git a/upb/bindings/lua/upb.h b/upb/bindings/lua/upb.h index 9e58f03..6861286 100644 --- a/upb/bindings/lua/upb.h +++ b/upb/bindings/lua/upb.h @@ -84,9 +84,7 @@ void lupb_pushuint32(lua_State *L, uint32_t val); void lupb_pushdouble(lua_State *L, double val); void lupb_pushfloat(lua_State *L, float val); -/* Registers a type with the given name, methods, and metamethods. - * If "refcount_gc" is true, adds a __gc metamethod that does an unref. - * Refcounted types must be allocated with lupb_refcounted_push[new]wrapper. */ +/* Registers a type with the given name, methods, and metamethods. */ void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m, const luaL_Reg *mm); @@ -98,7 +96,6 @@ void lupb_checkstatus(lua_State *L, upb_status *s); upb_fieldtype_t lupb_checkfieldtype(lua_State *L, int narg); -void *lupb_refcounted_check(lua_State *L, int narg, const char *type); const upb_msgdef *lupb_msgdef_check(lua_State *L, int narg); const upb_enumdef *lupb_enumdef_check(lua_State *L, int narg); const upb_fielddef *lupb_fielddef_check(lua_State *L, int narg); @@ -106,8 +103,6 @@ upb_symtab *lupb_symtab_check(lua_State *L, int narg); void lupb_def_registertypes(lua_State *L); -int lupb_refcounted_gc(lua_State *L); - /** From msg.c. ***************************************************************/ diff --git a/upb/handlers-inl.h b/upb/handlers-inl.h index afc1382..eb9a0fa 100644 --- a/upb/handlers-inl.h +++ b/upb/handlers-inl.h @@ -997,32 +997,12 @@ const T* BufferHandle::GetAttachedObject() const { : NULL; } -inline reffed_ptr Handlers::New(const MessageDef *m) { - upb_handlers *h = upb_handlers_new(m, &h); - return reffed_ptr(h, &h); -} -inline reffed_ptr Handlers::NewFrozen( - const MessageDef *m, upb_handlers_callback *callback, - const void *closure) { - const upb_handlers *h = upb_handlers_newfrozen(m, &h, callback, closure); - return reffed_ptr(h, &h); -} inline const Status* Handlers::status() { return upb_handlers_status(this); } inline void Handlers::ClearError() { return upb_handlers_clearerr(this); } -inline bool Handlers::Freeze(Status *s) { - upb::Handlers* h = this; - return upb_handlers_freeze(&h, 1, s); -} -inline bool Handlers::Freeze(Handlers *const *handlers, int n, Status *s) { - return upb_handlers_freeze(handlers, n, s); -} -inline bool Handlers::Freeze(const std::vector& h, Status* status) { - return upb_handlers_freeze((Handlers* const*)&h[0], h.size(), status); -} inline const MessageDef *Handlers::message_def() const { return upb_handlers_msgdef(this); } @@ -1092,9 +1072,6 @@ inline bool Handlers::SetEndSequenceHandler(const FieldDef *f, handler.AddCleanup(this); return upb_handlers_setendseq(this, f, handler.handler_, &handler.attr_); } -inline bool Handlers::SetSubHandlers(const FieldDef *f, const Handlers *sub) { - return upb_handlers_setsubhandlers(this, f, sub); -} inline const Handlers *Handlers::GetSubHandlers(const FieldDef *f) const { return upb_handlers_getsubhandlers(this, f); } @@ -1116,6 +1093,17 @@ inline const void *Handlers::GetHandlerData(Handlers::Selector selector) { return upb_handlers_gethandlerdata(this, selector); } +inline HandlerCache *HandlerCache::New(upb_handlers_callback *callback, + const void *closure) { + return upb_handlercache_new(callback, closure); +} +inline void HandlerCache::Free(HandlerCache* cache) { + return upb_handlercache_free(cache); +} +const Handlers* HandlerCache::Get(const MessageDef* md) { + return upb_handlercache_get(this, md); +} + inline BytesHandler::BytesHandler() { upb_byteshandler_init(this); } diff --git a/upb/handlers.c b/upb/handlers.c index fa75a48..90fb7b8 100644 --- a/upb/handlers.c +++ b/upb/handlers.c @@ -9,8 +9,8 @@ #include "upb/sink.h" -static void *upb_calloc(size_t size) { - void *mem = upb_gmalloc(size); +static void *upb_calloc(upb_arena *arena, size_t size) { + void *mem = upb_malloc(upb_arena_alloc(arena), size); if (mem) { memset(mem, 0, size); } @@ -21,86 +21,6 @@ static void *upb_calloc(size_t size) { * UPB_NO_CLOSURE. */ char _upb_noclosure; -static void freehandlers(upb_refcounted *r) { - upb_handlers *h = (upb_handlers*)r; - - upb_inttable_iter i; - upb_inttable_begin(&i, &h->cleanup_); - for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { - void *val = (void*)upb_inttable_iter_key(&i); - upb_value func_val = upb_inttable_iter_value(&i); - upb_handlerfree *func = upb_value_getfptr(func_val); - func(val); - } - - upb_inttable_uninit(&h->cleanup_); - upb_gfree(h->sub); - upb_gfree(h); -} - -static void visithandlers(const upb_refcounted *r, upb_refcounted_visit *visit, - void *closure) { - const upb_handlers *h = (const upb_handlers*)r; - upb_msg_field_iter i; - for(upb_msg_field_begin(&i, h->msg); - !upb_msg_field_done(&i); - upb_msg_field_next(&i)) { - upb_fielddef *f = upb_msg_iter_field(&i); - const upb_handlers *sub; - if (!upb_fielddef_issubmsg(f)) continue; - sub = upb_handlers_getsubhandlers(h, f); - if (sub) visit(r, upb_handlers_upcast(sub), closure); - } -} - -static const struct upb_refcounted_vtbl vtbl = {visithandlers, freehandlers}; - -typedef struct { - upb_inttable tab; /* maps upb_msgdef* -> upb_handlers*. */ - upb_handlers_callback *callback; - const void *closure; -} dfs_state; - -/* TODO(haberman): discard upb_handlers* objects that do not actually have any - * handlers set and cannot reach any upb_handlers* object that does. This is - * slightly tricky to do correctly. */ -static upb_handlers *newformsg(const upb_msgdef *m, const void *owner, - dfs_state *s) { - upb_msg_field_iter i; - upb_handlers *h = upb_handlers_new(m, owner); - if (!h) return NULL; - if (!upb_inttable_insertptr(&s->tab, m, upb_value_ptr(h))) goto oom; - - s->callback(s->closure, h); - - /* For each submessage field, get or create a handlers object and set it as - * the subhandlers. */ - for(upb_msg_field_begin(&i, m); - !upb_msg_field_done(&i); - upb_msg_field_next(&i)) { - upb_fielddef *f = upb_msg_iter_field(&i); - const upb_msgdef *subdef; - upb_value subm_ent; - - if (!upb_fielddef_issubmsg(f)) continue; - - subdef = upb_fielddef_msgsubdef(f); - if (upb_inttable_lookupptr(&s->tab, subdef, &subm_ent)) { - upb_handlers_setsubhandlers(h, f, upb_value_getptr(subm_ent)); - } else { - upb_handlers *sub_mh = newformsg(subdef, &sub_mh, s); - if (!sub_mh) goto oom; - upb_handlers_setsubhandlers(h, f, sub_mh); - upb_handlers_unref(sub_mh, &sub_mh); - } - } - return h; - -oom: - upb_handlers_unref(h, owner); - return NULL; -} - /* Given a selector for a STARTSUBMSG handler, resolves to a pointer to the * subhandlers for this submessage field. */ #define SUBH(h, selector) (h->sub[selector]) @@ -111,20 +31,13 @@ oom: static int32_t trygetsel(upb_handlers *h, const upb_fielddef *f, upb_handlertype_t type) { upb_selector_t sel; - UPB_ASSERT(!upb_handlers_isfrozen(h)); - if (upb_handlers_msgdef(h) != upb_fielddef_containingtype(f)) { - upb_status_seterrf( - &h->status_, "type mismatch: field %s does not belong to message %s", - upb_fielddef_name(f), upb_msgdef_fullname(upb_handlers_msgdef(h))); - return -1; - } - if (!upb_handlers_getselector(f, type, &sel)) { - upb_status_seterrf( - &h->status_, - "type mismatch: cannot register handler type %d for field %s", - type, upb_fielddef_name(f)); - return -1; - } + bool ok; + + ok = upb_handlers_getselector(f, type, &sel); + + UPB_ASSERT(upb_handlers_msgdef(h) == upb_fielddef_containingtype(f)); + UPB_ASSERT(ok); + return sel; } @@ -147,19 +60,7 @@ static bool doset(upb_handlers *h, int32_t sel, const upb_fielddef *f, const void *closure_type; const void **context_closure_type; - UPB_ASSERT(!upb_handlers_isfrozen(h)); - - if (sel < 0) { - upb_status_seterrmsg(&h->status_, - "incorrect handler type for this field."); - return false; - } - - if (h->table[sel].func) { - upb_status_seterrmsg(&h->status_, - "cannot change handler once it has been set."); - return false; - } + UPB_ASSERT(!h->table[sel].func); if (attr) { set_attr = *attr; @@ -181,16 +82,7 @@ static bool doset(upb_handlers *h, int32_t sel, const upb_fielddef *f, if (closure_type && *context_closure_type && closure_type != *context_closure_type) { - /* TODO(haberman): better message for debugging. */ - if (f) { - upb_status_seterrf(&h->status_, - "closure type does not match for field %s", - upb_fielddef_name(f)); - } else { - upb_status_seterrmsg( - &h->status_, "closure type does not match for message-level handler"); - } - return false; + UPB_ASSERT(false); } if (closure_type) @@ -203,8 +95,7 @@ static bool doset(upb_handlers *h, int32_t sel, const upb_fielddef *f, const void *table_return_type = upb_handlerattr_returnclosuretype(&h->table[sel].attr); if (return_type && table_return_type && return_type != table_return_type) { - upb_status_seterrmsg(&h->status_, "closure return type does not match"); - return false; + UPB_ASSERT(false); } if (table_return_type && !return_type) @@ -268,80 +159,36 @@ bool checkstart(upb_handlers *h, const upb_fielddef *f, upb_handlertype_t type, return_closure_type = upb_handlerattr_returnclosuretype(attr); if (closure_type && return_closure_type && closure_type != return_closure_type) { - upb_status_seterrf(status, - "expected start handler to return sub type for field %f", - upb_fielddef_name(f)); - return false; + UPB_ASSERT(false); } return true; } -/* Public interface ***********************************************************/ - -upb_handlers *upb_handlers_new(const upb_msgdef *md, const void *owner) { +static upb_handlers *upb_handlers_new(const upb_msgdef *md, upb_handlercache *cache) { int extra; upb_handlers *h; extra = sizeof(upb_handlers_tabent) * (upb_msgdef_selectorcount(md) - 1); - h = upb_calloc(sizeof(*h) + extra); + h = upb_calloc(&cache->arena, sizeof(*h) + extra); if (!h) return NULL; + h->cache = cache; h->msg = md; - upb_status_clear(&h->status_); if (upb_msgdef_submsgfieldcount(md) > 0) { - h->sub = upb_calloc(upb_msgdef_submsgfieldcount(md) * sizeof(*h->sub)); - if (!h->sub) goto oom; + size_t bytes = upb_msgdef_submsgfieldcount(md) * sizeof(*h->sub); + h->sub = upb_calloc(&cache->arena, bytes); + if (!h->sub) return NULL; } else { h->sub = 0; } - if (!upb_refcounted_init(upb_handlers_upcast_mutable(h), &vtbl, owner)) - goto oom; - if (!upb_inttable_init(&h->cleanup_, UPB_CTYPE_FPTR)) goto oom; - /* calloc() above initialized all handlers to NULL. */ return h; - -oom: - freehandlers(upb_handlers_upcast_mutable(h)); - return NULL; } -const upb_handlers *upb_handlers_newfrozen(const upb_msgdef *m, - const void *owner, - upb_handlers_callback *callback, - const void *closure) { - dfs_state state; - upb_handlers *ret; - bool ok; - upb_refcounted *r; - - state.callback = callback; - state.closure = closure; - if (!upb_inttable_init(&state.tab, UPB_CTYPE_PTR)) return NULL; - - ret = newformsg(m, owner, &state); - - upb_inttable_uninit(&state.tab); - if (!ret) return NULL; - - r = upb_handlers_upcast_mutable(ret); - ok = upb_refcounted_freeze(&r, 1, NULL, UPB_MAX_HANDLER_DEPTH); - UPB_ASSERT(ok); - return ret; -} - -const upb_status *upb_handlers_status(upb_handlers *h) { - UPB_ASSERT(!upb_handlers_isfrozen(h)); - return &h->status_; -} - -void upb_handlers_clearerr(upb_handlers *h) { - UPB_ASSERT(!upb_handlers_isfrozen(h)); - upb_status_clear(&h->status_); -} +/* Public interface ***********************************************************/ #define SETTER(name, handlerctype, handlertype) \ bool upb_handlers_set ## name(upb_handlers *h, const upb_fielddef *f, \ @@ -381,7 +228,6 @@ bool upb_handlers_setstartmsg(upb_handlers *h, upb_startmsg_handlerfunc *func, bool upb_handlers_setendmsg(upb_handlers *h, upb_endmsg_handlerfunc *func, upb_handlerattr *attr) { - UPB_ASSERT(!upb_handlers_isfrozen(h)); return doset(h, UPB_ENDMSG_SELECTOR, NULL, UPB_HANDLER_INT32, (upb_func *)func, attr); } @@ -389,14 +235,12 @@ bool upb_handlers_setendmsg(upb_handlers *h, upb_endmsg_handlerfunc *func, bool upb_handlers_setsubhandlers(upb_handlers *h, const upb_fielddef *f, const upb_handlers *sub) { UPB_ASSERT(sub); - UPB_ASSERT(!upb_handlers_isfrozen(h)); UPB_ASSERT(upb_fielddef_issubmsg(f)); if (SUBH_F(h, f)) return false; /* Can't reset. */ if (upb_handlers_msgdef(sub) != upb_fielddef_msgsubdef(f)) { return false; } SUBH_F(h, f) = sub; - upb_ref2(sub, h); return true; } @@ -424,101 +268,14 @@ const upb_msgdef *upb_handlers_msgdef(const upb_handlers *h) { return h->msg; } bool upb_handlers_addcleanup(upb_handlers *h, void *p, upb_handlerfree *func) { bool ok; - if (upb_inttable_lookupptr(&h->cleanup_, p, NULL)) { + if (upb_inttable_lookupptr(&h->cache->cleanup_, p, NULL)) { return false; } - ok = upb_inttable_insertptr(&h->cleanup_, p, upb_value_fptr(func)); + ok = upb_inttable_insertptr(&h->cache->cleanup_, p, upb_value_fptr(func)); UPB_ASSERT(ok); return true; } - -/* "Static" methods ***********************************************************/ - -bool upb_handlers_freeze(upb_handlers *const*handlers, int n, upb_status *s) { - /* TODO: verify we have a transitive closure. */ - int i; - for (i = 0; i < n; i++) { - upb_msg_field_iter j; - upb_handlers *h = handlers[i]; - - if (!upb_ok(&h->status_)) { - upb_status_seterrf(s, "handlers for message %s had error status: %s", - upb_msgdef_fullname(upb_handlers_msgdef(h)), - upb_status_errmsg(&h->status_)); - return false; - } - - /* Check that there are no closure mismatches due to missing Start* handlers - * or subhandlers with different type-level types. */ - for(upb_msg_field_begin(&j, h->msg); - !upb_msg_field_done(&j); - upb_msg_field_next(&j)) { - - const upb_fielddef *f = upb_msg_iter_field(&j); - if (upb_fielddef_isseq(f)) { - if (!checkstart(h, f, UPB_HANDLER_STARTSEQ, s)) - return false; - } - - if (upb_fielddef_isstring(f)) { - if (!checkstart(h, f, UPB_HANDLER_STARTSTR, s)) - return false; - } - - if (upb_fielddef_issubmsg(f)) { - bool hashandler = false; - if (upb_handlers_gethandler( - h, handlers_getsel(h, f, UPB_HANDLER_STARTSUBMSG)) || - upb_handlers_gethandler( - h, handlers_getsel(h, f, UPB_HANDLER_ENDSUBMSG))) { - hashandler = true; - } - - if (upb_fielddef_isseq(f) && - (upb_handlers_gethandler( - h, handlers_getsel(h, f, UPB_HANDLER_STARTSEQ)) || - upb_handlers_gethandler( - h, handlers_getsel(h, f, UPB_HANDLER_ENDSEQ)))) { - hashandler = true; - } - - if (hashandler && !upb_handlers_getsubhandlers(h, f)) { - /* For now we add an empty subhandlers in this case. It makes the - * decoder code generator simpler, because it only has to handle two - * cases (submessage has handlers or not) as opposed to three - * (submessage has handlers in enclosing message but no subhandlers). - * - * This makes parsing less efficient in the case that we want to - * notice a submessage but skip its contents (like if we're testing - * for submessage presence or counting the number of repeated - * submessages). In this case we will end up parsing the submessage - * field by field and throwing away the results for each, instead of - * skipping the whole delimited thing at once. If this is an issue we - * can revisit it, but do remember that this only arises when you have - * handlers (startseq/startsubmsg/endsubmsg/endseq) set for the - * submessage but no subhandlers. The uses cases for this are - * limited. */ - upb_handlers *sub = upb_handlers_new(upb_fielddef_msgsubdef(f), &sub); - upb_handlers_setsubhandlers(h, f, sub); - upb_handlers_unref(sub, &sub); - } - - /* TODO(haberman): check type of submessage. - * This is slightly tricky; also consider whether we should check that - * they match at setsubhandlers time. */ - } - } - } - - if (!upb_refcounted_freeze((upb_refcounted*const*)handlers, n, s, - UPB_MAX_HANDLER_DEPTH)) { - return false; - } - - return true; -} - upb_handlertype_t upb_handlers_getprimitivehandlertype(const upb_fielddef *f) { switch (upb_fielddef_type(f)) { case UPB_TYPE_INT32: @@ -616,6 +373,85 @@ uint32_t upb_handlers_selectorcount(const upb_fielddef *f) { return ret; } +/* upb_handlercache ***********************************************************/ + +const upb_handlers *upb_handlercache_get(upb_handlercache *c, + const upb_msgdef *md) { + upb_msg_field_iter i; + upb_value v; + upb_handlers *h; + + if (upb_inttable_lookupptr(&c->tab, md, &v)) { + return upb_value_getptr(v); + } + + h = upb_handlers_new(md, c); + v = upb_value_ptr(h); + + if (!h) return NULL; + if (!upb_inttable_insertptr(&c->tab, md, v)) return NULL; + + c->callback(c->closure, h); + + /* For each submessage field, get or create a handlers object and set it as + * the subhandlers. */ + for(upb_msg_field_begin(&i, md); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { + upb_fielddef *f = upb_msg_iter_field(&i); + + if (upb_fielddef_issubmsg(f)) { + const upb_msgdef *subdef = upb_fielddef_msgsubdef(f); + const upb_handlers *sub_mh = upb_handlercache_get(c, subdef); + + if (!sub_mh) return NULL; + + upb_handlers_setsubhandlers(h, f, sub_mh); + } + } + + return h; +} + + +upb_handlercache *upb_handlercache_new(upb_handlers_callback *callback, + const void *closure) { + upb_handlercache *cache = upb_gmalloc(sizeof(*cache)); + + if (!cache) return NULL; + + upb_arena_init(&cache->arena); + + cache->callback = callback; + cache->closure = closure; + + if (!upb_inttable_init(&cache->tab, UPB_CTYPE_PTR)) goto oom; + if (!upb_inttable_init(&cache->cleanup_, UPB_CTYPE_FPTR)) goto oom; + + return cache; + +oom: + upb_gfree(cache); + return NULL; +} + +void upb_handlercache_free(upb_handlercache *cache) { + upb_inttable_iter i; + + upb_inttable_begin(&i, &cache->cleanup_); + for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { + void *val = (void*)upb_inttable_iter_key(&i); + upb_value func_val = upb_inttable_iter_value(&i); + upb_handlerfree *func = upb_value_getfptr(func_val); + func(val); + } + + upb_inttable_uninit(&cache->tab); + upb_inttable_uninit(&cache->cleanup_); + upb_arena_uninit(&cache->arena); + upb_gfree(cache); +} + /* upb_handlerattr ************************************************************/ diff --git a/upb/handlers.h b/upb/handlers.h index 15d06a0..741bd48 100644 --- a/upb/handlers.h +++ b/upb/handlers.h @@ -21,7 +21,6 @@ #include "upb/def.h" #include "upb/table.int.h" -#include "upb/refcounted.h" #ifdef __cplusplus namespace upb { @@ -29,6 +28,7 @@ class BufferHandle; class BytesHandler; class HandlerAttributes; class Handlers; +class HandlerCache; template class Handler; template struct CanonicalType; } /* namespace upb */ @@ -37,8 +37,8 @@ template struct CanonicalType; UPB_DECLARE_TYPE(upb::BufferHandle, upb_bufhandle) UPB_DECLARE_TYPE(upb::BytesHandler, upb_byteshandler) UPB_DECLARE_TYPE(upb::HandlerAttributes, upb_handlerattr) -UPB_DECLARE_DERIVED_TYPE(upb::Handlers, upb::RefCounted, - upb_handlers, upb_refcounted) +UPB_DECLARE_TYPE(upb::Handlers, upb_handlers) +UPB_DECLARE_TYPE(upb::HandlerCache, upb_handlercache) /* The maximum depth that the handler graph can have. This is a resource limit * for the C stack since we sometimes need to recursively traverse the graph. @@ -275,22 +275,6 @@ class upb::Handlers { typedef void HandlersCallback(const void *closure, upb_handlers *h); - /* Returns a new handlers object for the given frozen msgdef. - * Returns NULL if memory allocation failed. */ - static reffed_ptr New(const MessageDef *m); - - /* Convenience function for registering a graph of handlers that mirrors the - * graph of msgdefs for some message. For "m" and all its children a new set - * of handlers will be created and the given callback will be invoked, - * allowing the client to register handlers for this message. Note that any - * subhandlers set by the callback will be overwritten. */ - static reffed_ptr NewFrozen(const MessageDef *m, - HandlersCallback *callback, - const void *closure); - - /* Functionality from upb::RefCounted. */ - UPB_REFCOUNTED_CPPMETHODS - /* All handler registration functions return bool to indicate success or * failure; details about failures are stored in this status object. If a * failure does occur, it must be cleared before the Handlers are frozen, @@ -299,16 +283,6 @@ class upb::Handlers { const Status* status(); void ClearError(); - /* Call to freeze these Handlers. Requires that any SubHandlers are already - * frozen. For cycles, you must use the static version below and freeze the - * whole graph at once. */ - bool Freeze(Status* s); - - /* Freezes the given set of handlers. You may not freeze a handler without - * also freezing any handlers they point to. */ - static bool Freeze(Handlers*const* handlers, int n, Status* s); - static bool Freeze(const std::vector& handlers, Status* s); - /* Returns the msgdef associated with this handlers object. */ const MessageDef* message_def() const; @@ -473,9 +447,8 @@ class upb::Handlers { */ bool SetEndSequenceHandler(const FieldDef* f, const EndFieldHandler& h); - /* Sets or gets the object that specifies handlers for the given field, which + /* Gets the object that specifies handlers for the given field, which * must be a submessage or group. Returns NULL if no handlers are set. */ - bool SetSubHandlers(const FieldDef* f, const Handlers* sub); const Handlers* GetSubHandlers(const FieldDef* f) const; /* Equivalent to GetSubHandlers, but takes the STARTSUBMSG selector for the @@ -519,13 +492,10 @@ class upb::Handlers { #else struct upb_handlers { #endif - upb_refcounted base; - + upb_handlercache *cache; const upb_msgdef *msg; const upb_handlers **sub; const void *top_closure_type; - upb_inttable cleanup_; - upb_status status_; /* Used only when mutable. */ upb_handlers_tabent table[1]; /* Dynamically-sized field handler array. */ }; @@ -675,17 +645,6 @@ UPB_INLINE const void *upb_handlerattr_handlerdata( } /* upb_handlers */ -typedef void upb_handlers_callback(const void *closure, upb_handlers *h); -upb_handlers *upb_handlers_new(const upb_msgdef *m, - const void *owner); -const upb_handlers *upb_handlers_newfrozen(const upb_msgdef *m, - const void *owner, - upb_handlers_callback *callback, - const void *closure); - -/* Include refcounted methods like upb_handlers_ref(). */ -UPB_REFCOUNTED_CMETHODS(upb_handlers, upb_handlers_upcast) - const upb_status *upb_handlers_status(upb_handlers *h); void upb_handlers_clearerr(upb_handlers *h); const upb_msgdef *upb_handlers_msgdef(const upb_handlers *h); @@ -737,8 +696,6 @@ bool upb_handlers_setendseq(upb_handlers *h, const upb_fielddef *f, upb_endfield_handlerfunc *func, upb_handlerattr *attr); -bool upb_handlers_setsubhandlers(upb_handlers *h, const upb_fielddef *f, - const upb_handlers *sub); const upb_handlers *upb_handlers_getsubhandlers(const upb_handlers *h, const upb_fielddef *f); const upb_handlers *upb_handlers_getsubhandlers_sel(const upb_handlers *h, @@ -757,6 +714,36 @@ UPB_INLINE const void *upb_handlers_gethandlerdata(const upb_handlers *h, return upb_handlerattr_handlerdata(&h->table[s].attr); } +typedef void upb_handlers_callback(const void *closure, upb_handlers *h); + +#ifdef __cplusplus + +class upb::HandlerCache { + public: + static HandlerCache *New(upb_handlers_callback *callback, + const void *closure); + static void Free(HandlerCache* cache); + + const Handlers* Get(const MessageDef* md); + + private: + UPB_DISALLOW_POD_OPS(HandlerCache, upb::pb::HandlerCache) +#else +struct upb_handlercache { +#endif + upb_arena arena; + upb_inttable tab; /* maps upb_msgdef* -> upb_handlers*. */ + upb_inttable cleanup_; + upb_handlers_callback *callback; + const void *closure; +}; + +upb_handlercache *upb_handlercache_new(upb_handlers_callback *callback, + const void *closure); +void upb_handlercache_free(upb_handlercache *cache); +const upb_handlers *upb_handlercache_get(upb_handlercache *cache, + const upb_msgdef *md); + #ifdef __cplusplus /* Handler types for single fields. @@ -788,7 +775,6 @@ bool upb_byteshandler_setendstr(upb_byteshandler *h, upb_endfield_handlerfunc *func, void *d); /* "Static" methods */ -bool upb_handlers_freeze(upb_handlers *const *handlers, int n, upb_status *s); upb_handlertype_t upb_handlers_getprimitivehandlertype(const upb_fielddef *f); bool upb_handlers_getselector(const upb_fielddef *f, upb_handlertype_t type, upb_selector_t *s); diff --git a/upb/json/parser.c b/upb/json/parser.c index 90a401d..4bc9163 100644 --- a/upb/json/parser.c +++ b/upb/json/parser.c @@ -154,14 +154,13 @@ void upb_stringsink_uninit(upb_stringsink *sink) { free(sink->ptr); } typedef struct { /* For encoding Any value field in binary format. */ - const upb_handlers *encoder_handlers; - upb_pb_encoder *encoder; + upb_handlercache *encoder_handlercache; upb_stringsink stringsink; /* For decoding Any value field in json format. */ - upb_json_parsermethod *parser_method; - upb_json_parser* parser; + upb_json_codecache *parser_codecache; upb_sink sink; + upb_json_parser *parser; /* Mark the range of uninterpreted values in json input before type url. */ const char *before_type_url_start; @@ -180,7 +179,7 @@ typedef struct { const upb_fielddef *f; /* The table mapping json name to fielddef for this message. */ - upb_strtable *name_table; + const upb_strtable *name_table; /* We are in a repeated-field context, ready to emit mapentries as * submessages. This flag alters the start-of-object (open-brace) behavior to @@ -259,60 +258,67 @@ struct upb_json_parser { struct tm tm; }; -struct upb_json_parsermethod { - upb_refcounted base; +struct upb_json_codecache { + upb_arena arena; + upb_inttable methods; /* upb_msgdef* -> upb_json_parsermethod* */ +}; +struct upb_json_parsermethod { + const upb_json_codecache *cache; upb_byteshandler input_handler_; - /* Keys are upb_msgdef*, values are upb_strtable (json_name -> fielddef) */ - upb_inttable name_tables; + /* Maps json_name -> fielddef */ + upb_strtable name_table; }; #define PARSER_CHECK_RETURN(x) if (!(x)) return false -static void json_parser_any_frame_reset(upb_jsonparser_any_frame *frame) { - frame->encoder_handlers = NULL; - frame->encoder = NULL; - frame->parser_method = NULL; +static upb_jsonparser_any_frame *json_parser_any_frame_new( + upb_json_parser *p) { + upb_jsonparser_any_frame *frame; + + frame = upb_env_malloc(p->env, sizeof(upb_jsonparser_any_frame)); + + frame->encoder_handlercache = upb_pb_encoder_newcache(); + frame->parser_codecache = upb_json_codecache_new(); frame->parser = NULL; frame->before_type_url_start = NULL; frame->before_type_url_end = NULL; frame->after_type_url_start = NULL; + + upb_stringsink_init(&frame->stringsink); + + return frame; } static void json_parser_any_frame_set_payload_type( upb_json_parser *p, upb_jsonparser_any_frame *frame, const upb_msgdef *payload_type) { + const upb_handlers *h; + const upb_json_parsermethod *parser_method; + upb_pb_encoder *encoder; + /* Initialize encoder. */ - frame->encoder_handlers = - upb_pb_encoder_newhandlers(payload_type, &frame->encoder_handlers); - upb_stringsink_init(&frame->stringsink); - frame->encoder = - upb_pb_encoder_create( - p->env, frame->encoder_handlers, - &frame->stringsink.sink); + h = upb_handlercache_get(frame->encoder_handlercache, payload_type); + encoder = upb_pb_encoder_create(p->env, h, &frame->stringsink.sink); /* Initialize parser. */ - frame->parser_method = - upb_json_parsermethod_new(payload_type, &frame->parser_method); - upb_sink_reset(&frame->sink, frame->encoder_handlers, frame->encoder); - frame->parser = - upb_json_parser_create(p->env, frame->parser_method, p->symtab, - &frame->sink, p->ignore_json_unknown); + parser_method = upb_json_codecache_get(frame->parser_codecache, payload_type); + upb_sink_reset(&frame->sink, h, encoder); + frame->parser = upb_json_parser_create(p->env, parser_method, p->symtab, + &frame->sink, p->ignore_json_unknown); } static void json_parser_any_frame_free(upb_jsonparser_any_frame *frame) { - upb_handlers_unref(frame->encoder_handlers, - &frame->encoder_handlers); - upb_json_parsermethod_unref(frame->parser_method, - &frame->parser_method); + upb_handlercache_free(frame->encoder_handlercache); + upb_json_codecache_free(frame->parser_codecache); upb_stringsink_uninit(&frame->stringsink); } static bool json_parser_any_frame_has_type_url( upb_jsonparser_any_frame *frame) { - return frame->encoder != NULL; + return frame->parser != NULL; } static bool json_parser_any_frame_has_value_before_type_url( @@ -334,7 +340,7 @@ static bool json_parser_any_frame_has_value( static void json_parser_any_frame_set_before_type_url_end( upb_jsonparser_any_frame *frame, const char *ptr) { - if (frame->encoder == NULL) { + if (frame->parser == NULL) { frame->before_type_url_end = ptr; } } @@ -376,9 +382,12 @@ static bool check_stack(upb_json_parser *p) { static void set_name_table(upb_json_parser *p, upb_jsonparser_frame *frame) { upb_value v; - bool ok = upb_inttable_lookupptr(&p->method->name_tables, frame->m, &v); + const upb_json_codecache *cache = p->method->cache; + bool ok = upb_inttable_lookupptr(&cache->methods, frame->m, &v); + const upb_json_parsermethod *method = upb_value_getptr(v); UPB_ASSERT(ok); - frame->name_table = upb_value_getptr(v); + + frame->name_table = &method->name_table; } /* There are GCC/Clang built-ins for overflow checking which we could start @@ -1929,9 +1938,7 @@ static bool start_subobject(upb_json_parser *p) { if (is_wellknown_msg(p, UPB_WELLKNOWN_ANY)) { p->top->is_any = true; - p->top->any_frame = - upb_env_malloc(p->env, sizeof(upb_jsonparser_any_frame)); - json_parser_any_frame_reset(p->top->any_frame); + p->top->any_frame = json_parser_any_frame_new(p); } else { p->top->is_any = false; p->top->any_frame = NULL; @@ -2409,11 +2416,11 @@ static bool is_string_wrapper_object(upb_json_parser *p) { * final state once, when the closing '"' is seen. */ -#line 2571 "upb/json/parser.rl" +#line 2578 "upb/json/parser.rl" -#line 2417 "upb/json/parser.c" +#line 2424 "upb/json/parser.c" static const char _json_actions[] = { 0, 1, 0, 1, 1, 1, 3, 1, 4, 1, 6, 1, 7, 1, 8, 1, @@ -2660,7 +2667,7 @@ static const int json_en_value_machine = 75; static const int json_en_main = 1; -#line 2574 "upb/json/parser.rl" +#line 2581 "upb/json/parser.rl" size_t parse(void *closure, const void *hd, const char *buf, size_t size, const upb_bufhandle *handle) { @@ -2683,7 +2690,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, capture_resume(parser, buf); -#line 2687 "upb/json/parser.c" +#line 2694 "upb/json/parser.c" { int _klen; unsigned int _trans; @@ -2758,83 +2765,83 @@ _match: switch ( *_acts++ ) { case 1: -#line 2422 "upb/json/parser.rl" +#line 2429 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 2: -#line 2424 "upb/json/parser.rl" +#line 2431 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 23;goto _again;} } break; case 3: -#line 2428 "upb/json/parser.rl" +#line 2435 "upb/json/parser.rl" { start_text(parser, p); } break; case 4: -#line 2429 "upb/json/parser.rl" +#line 2436 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_text(parser, p)); } break; case 5: -#line 2435 "upb/json/parser.rl" +#line 2442 "upb/json/parser.rl" { start_hex(parser); } break; case 6: -#line 2436 "upb/json/parser.rl" +#line 2443 "upb/json/parser.rl" { hexdigit(parser, p); } break; case 7: -#line 2437 "upb/json/parser.rl" +#line 2444 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_hex(parser)); } break; case 8: -#line 2443 "upb/json/parser.rl" +#line 2450 "upb/json/parser.rl" { CHECK_RETURN_TOP(escape(parser, p)); } break; case 9: -#line 2449 "upb/json/parser.rl" +#line 2456 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 10: -#line 2461 "upb/json/parser.rl" +#line 2468 "upb/json/parser.rl" { start_duration_base(parser, p); } break; case 11: -#line 2462 "upb/json/parser.rl" +#line 2469 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_duration_base(parser, p)); } break; case 12: -#line 2464 "upb/json/parser.rl" +#line 2471 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 13: -#line 2469 "upb/json/parser.rl" +#line 2476 "upb/json/parser.rl" { start_timestamp_base(parser, p); } break; case 14: -#line 2470 "upb/json/parser.rl" +#line 2477 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_timestamp_base(parser, p)); } break; case 15: -#line 2472 "upb/json/parser.rl" +#line 2479 "upb/json/parser.rl" { start_timestamp_fraction(parser, p); } break; case 16: -#line 2473 "upb/json/parser.rl" +#line 2480 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_timestamp_fraction(parser, p)); } break; case 17: -#line 2475 "upb/json/parser.rl" +#line 2482 "upb/json/parser.rl" { start_timestamp_zone(parser, p); } break; case 18: -#line 2476 "upb/json/parser.rl" +#line 2483 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_timestamp_zone(parser, p)); } break; case 19: -#line 2478 "upb/json/parser.rl" +#line 2485 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 20: -#line 2483 "upb/json/parser.rl" +#line 2490 "upb/json/parser.rl" { if (is_wellknown_msg(parser, UPB_WELLKNOWN_TIMESTAMP)) { {stack[top++] = cs; cs = 47;goto _again;} @@ -2846,11 +2853,11 @@ _match: } break; case 21: -#line 2494 "upb/json/parser.rl" +#line 2501 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 75;goto _again;} } break; case 22: -#line 2499 "upb/json/parser.rl" +#line 2506 "upb/json/parser.rl" { if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) { start_any_member(parser, p); @@ -2860,11 +2867,11 @@ _match: } break; case 23: -#line 2506 "upb/json/parser.rl" +#line 2513 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_membername(parser)); } break; case 24: -#line 2509 "upb/json/parser.rl" +#line 2516 "upb/json/parser.rl" { if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) { end_any_member(parser, p); @@ -2874,7 +2881,7 @@ _match: } break; case 25: -#line 2520 "upb/json/parser.rl" +#line 2527 "upb/json/parser.rl" { if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) { start_any_object(parser, p); @@ -2884,7 +2891,7 @@ _match: } break; case 26: -#line 2529 "upb/json/parser.rl" +#line 2536 "upb/json/parser.rl" { if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) { CHECK_RETURN_TOP(end_any_object(parser, p)); @@ -2894,54 +2901,54 @@ _match: } break; case 27: -#line 2541 "upb/json/parser.rl" +#line 2548 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_array(parser)); } break; case 28: -#line 2545 "upb/json/parser.rl" +#line 2552 "upb/json/parser.rl" { end_array(parser); } break; case 29: -#line 2550 "upb/json/parser.rl" +#line 2557 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_number(parser, p)); } break; case 30: -#line 2551 "upb/json/parser.rl" +#line 2558 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 31: -#line 2553 "upb/json/parser.rl" +#line 2560 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_stringval(parser)); } break; case 32: -#line 2554 "upb/json/parser.rl" +#line 2561 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_stringval(parser)); } break; case 33: -#line 2556 "upb/json/parser.rl" +#line 2563 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_bool(parser, true)); } break; case 34: -#line 2558 "upb/json/parser.rl" +#line 2565 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_bool(parser, false)); } break; case 35: -#line 2560 "upb/json/parser.rl" +#line 2567 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_null(parser)); } break; case 36: -#line 2562 "upb/json/parser.rl" +#line 2569 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_subobject_full(parser)); } break; case 37: -#line 2563 "upb/json/parser.rl" +#line 2570 "upb/json/parser.rl" { end_subobject_full(parser); } break; case 38: -#line 2568 "upb/json/parser.rl" +#line 2575 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; -#line 2945 "upb/json/parser.c" +#line 2952 "upb/json/parser.c" } } @@ -2958,32 +2965,32 @@ _again: while ( __nacts-- > 0 ) { switch ( *__acts++ ) { case 0: -#line 2420 "upb/json/parser.rl" +#line 2427 "upb/json/parser.rl" { p--; {cs = stack[--top]; if ( p == pe ) goto _test_eof; goto _again;} } break; case 30: -#line 2551 "upb/json/parser.rl" +#line 2558 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 33: -#line 2556 "upb/json/parser.rl" +#line 2563 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_bool(parser, true)); } break; case 34: -#line 2558 "upb/json/parser.rl" +#line 2565 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_bool(parser, false)); } break; case 35: -#line 2560 "upb/json/parser.rl" +#line 2567 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_null(parser)); } break; case 37: -#line 2563 "upb/json/parser.rl" +#line 2570 "upb/json/parser.rl" { end_subobject_full(parser); } break; -#line 2987 "upb/json/parser.c" +#line 2994 "upb/json/parser.c" } } } @@ -2991,7 +2998,7 @@ goto _again;} } _out: {} } -#line 2596 "upb/json/parser.rl" +#line 2603 "upb/json/parser.rl" if (p != pe) { upb_status_seterrf(&parser->status, "Parse error at '%.*s'\n", pe - p, p); @@ -3039,13 +3046,13 @@ static void json_parser_reset(upb_json_parser *p) { /* Emit Ragel initialization of the parser. */ -#line 3043 "upb/json/parser.c" +#line 3050 "upb/json/parser.c" { cs = json_start; top = 0; } -#line 2643 "upb/json/parser.rl" +#line 2650 "upb/json/parser.rl" p->current_state = cs; p->parser_top = top; accumulate_clear(p); @@ -3055,70 +3062,46 @@ static void json_parser_reset(upb_json_parser *p) { upb_status_clear(&p->status); } -static void free_json_parsermethod(upb_refcounted *r) { - upb_json_parsermethod *method = (upb_json_parsermethod*)r; - - upb_inttable_iter i; - upb_inttable_begin(&i, &method->name_tables); - for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { - upb_value val = upb_inttable_iter_value(&i); - upb_strtable *t = upb_value_getptr(val); - upb_strtable_uninit(t); - upb_gfree(t); - } - - upb_inttable_uninit(&method->name_tables); +static upb_json_parsermethod *parsermethod_new(upb_json_codecache *c, + const upb_msgdef *md) { + upb_msg_field_iter i; + upb_alloc *alloc = upb_arena_alloc(&c->arena); - upb_gfree(r); -} + upb_json_parsermethod *m = upb_gmalloc(sizeof(*m)); -static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) { - upb_msg_field_iter i; - upb_strtable *t; + m->cache = c; - /* It would be nice to stack-allocate this, but protobufs do not limit the - * length of fields to any reasonable limit. */ - char *buf = NULL; - size_t len = 0; + upb_byteshandler_init(&m->input_handler_); + upb_byteshandler_setstring(&m->input_handler_, parse, m); + upb_byteshandler_setendstr(&m->input_handler_, end, m); - if (upb_inttable_lookupptr(&m->name_tables, md, NULL)) { - return; - } + upb_strtable_init2(&m->name_table, UPB_CTYPE_CONSTPTR, alloc); - /* TODO(haberman): handle malloc failure. */ - t = upb_gmalloc(sizeof(*t)); - upb_strtable_init(t, UPB_CTYPE_CONSTPTR); - upb_inttable_insertptr(&m->name_tables, md, upb_value_ptr(t)); + /* Build name_table */ for(upb_msg_field_begin(&i, md); !upb_msg_field_done(&i); upb_msg_field_next(&i)) { const upb_fielddef *f = upb_msg_iter_field(&i); + upb_value v = upb_value_constptr(f); + char *buf; /* Add an entry for the JSON name. */ - size_t field_len = upb_fielddef_getjsonname(f, buf, len); - if (field_len > len) { - size_t len2; - buf = upb_grealloc(buf, 0, field_len); - len = field_len; - len2 = upb_fielddef_getjsonname(f, buf, len); - UPB_ASSERT(len == len2); - } - upb_strtable_insert(t, buf, upb_value_constptr(f)); + size_t len = upb_fielddef_getjsonname(f, NULL, 0); + buf = upb_malloc(alloc, len); + upb_fielddef_getjsonname(f, buf, len); + upb_strtable_insert3(&m->name_table, buf, len, v, alloc); if (strcmp(buf, upb_fielddef_name(f)) != 0) { /* Since the JSON name is different from the regular field name, add an * entry for the raw name (compliant proto3 JSON parsers must accept * both). */ - upb_strtable_insert(t, upb_fielddef_name(f), upb_value_constptr(f)); - } - - if (upb_fielddef_issubmsg(f)) { - add_jsonname_table(m, upb_fielddef_msgsubdef(f)); + const char *name = upb_fielddef_name(f); + upb_strtable_insert3(&m->name_table, name, strlen(name), v, alloc); } } - upb_gfree(buf); + return m; } /* Public API *****************************************************************/ @@ -3146,9 +3129,7 @@ upb_json_parser *upb_json_parser_create(upb_env *env, p->top->m = upb_handlers_msgdef(output->handlers); if (is_wellknown_msg(p, UPB_WELLKNOWN_ANY)) { p->top->is_any = true; - p->top->any_frame = - upb_env_malloc(p->env, sizeof(upb_jsonparser_any_frame)); - json_parser_any_frame_reset(p->top->any_frame); + p->top->any_frame = json_parser_any_frame_new(p); } else { p->top->is_any = false; p->top->any_frame = NULL; @@ -3169,24 +3150,61 @@ upb_bytessink *upb_json_parser_input(upb_json_parser *p) { return &p->input_; } -upb_json_parsermethod *upb_json_parsermethod_new(const upb_msgdef* md, - const void* owner) { - static const struct upb_refcounted_vtbl vtbl = {NULL, free_json_parsermethod}; - upb_json_parsermethod *ret = upb_gmalloc(sizeof(*ret)); - upb_refcounted_init(upb_json_parsermethod_upcast_mutable(ret), &vtbl, owner); +const upb_byteshandler *upb_json_parsermethod_inputhandler( + const upb_json_parsermethod *m) { + return &m->input_handler_; +} + +upb_json_codecache *upb_json_codecache_new() { + upb_alloc *alloc; + upb_json_codecache *c; - upb_byteshandler_init(&ret->input_handler_); - upb_byteshandler_setstring(&ret->input_handler_, parse, ret); - upb_byteshandler_setendstr(&ret->input_handler_, end, ret); + c = upb_gmalloc(sizeof(*c)); - upb_inttable_init(&ret->name_tables, UPB_CTYPE_PTR); + upb_arena_init(&c->arena); + alloc = upb_arena_alloc(&c->arena); - add_jsonname_table(ret, md); + upb_inttable_init2(&c->methods, UPB_CTYPE_CONSTPTR, alloc); - return ret; + return c; } -const upb_byteshandler *upb_json_parsermethod_inputhandler( - const upb_json_parsermethod *m) { - return &m->input_handler_; +void upb_json_codecache_free(upb_json_codecache *c) { + upb_arena_uninit(&c->arena); + upb_gfree(c); +} + +upb_json_parsermethod *upb_json_codecache_get(upb_json_codecache *c, + const upb_msgdef *md) { + upb_json_parsermethod *m; + upb_value v; + upb_msg_field_iter i; + + if (upb_inttable_lookupptr(&c->methods, md, &v)) { + return upb_value_getptr(v); + } + + m = parsermethod_new(c, md); + v = upb_value_ptr(m); + + if (!m) return NULL; + if (!upb_inttable_insertptr(&c->methods, m, v)) return NULL; + + /* Populate parser methods for all submessages, so the name tables will + * be available during parsing. */ + for(upb_msg_field_begin(&i, md); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { + upb_fielddef *f = upb_msg_iter_field(&i); + + if (upb_fielddef_issubmsg(f)) { + const upb_msgdef *subdef = upb_fielddef_msgsubdef(f); + const upb_json_parsermethod *sub_method = + upb_json_codecache_get(c, subdef); + + if (!sub_method) return NULL; + } + } + + return m; } diff --git a/upb/json/parser.h b/upb/json/parser.h index 91b08d8..d5ec396 100644 --- a/upb/json/parser.h +++ b/upb/json/parser.h @@ -13,6 +13,7 @@ #ifdef __cplusplus namespace upb { namespace json { +class CodeCache; class Parser; class ParserMethod; } /* namespace json */ @@ -20,8 +21,8 @@ class ParserMethod; #endif UPB_DECLARE_TYPE(upb::json::Parser, upb_json_parser) -UPB_DECLARE_DERIVED_TYPE(upb::json::ParserMethod, upb::RefCounted, - upb_json_parsermethod, upb_refcounted) +UPB_DECLARE_TYPE(upb::json::ParserMethod, upb_json_parsermethod) +UPB_DECLARE_TYPE(upb::json::CodeCache, upb_json_codecache) /* upb::json::Parser **********************************************************/ @@ -49,18 +50,6 @@ class upb::json::Parser { class upb::json::ParserMethod { public: - /* Include base methods from upb::ReferenceCounted. */ - UPB_REFCOUNTED_CPPMETHODS - - /* Returns handlers for parsing according to the specified schema. - * The MessageDef must outlive the ParserMethod. */ - static reffed_ptr New(const upb::MessageDef* md); - - /* The destination handlers that are statically bound to this method. - * This method is only capable of outputting to a sink that uses these - * handlers. */ - const Handlers* dest_handlers() const; - /* The input handlers for this decoder method. */ const BytesHandler* input_handler() const; @@ -68,6 +57,19 @@ class upb::json::ParserMethod { UPB_DISALLOW_POD_OPS(ParserMethod, upb::json::ParserMethod) }; +class upb::json::CodeCache { + public: + static CodeCache* New(); + static void Free(CodeCache* cache); + + /* Returns a DecoderMethod that can push data to the given handlers. + * If a suitable method already exists, it will be returned from the cache. */ + const ParserMethod *Get(const MessageDef* md); + + private: + UPB_DISALLOW_POD_OPS(CodeCache, upb::json::CodeCache) +}; + #endif UPB_BEGIN_EXTERN_C @@ -79,15 +81,13 @@ upb_json_parser* upb_json_parser_create(upb_env* e, bool ignore_json_unknown); upb_bytessink *upb_json_parser_input(upb_json_parser *p); -upb_json_parsermethod* upb_json_parsermethod_new(const upb_msgdef* md, - const void* owner); -const upb_handlers *upb_json_parsermethod_desthandlers( - const upb_json_parsermethod *m); const upb_byteshandler *upb_json_parsermethod_inputhandler( const upb_json_parsermethod *m); -/* Include refcounted methods like upb_json_parsermethod_ref(). */ -UPB_REFCOUNTED_CMETHODS(upb_json_parsermethod, upb_json_parsermethod_upcast) +upb_json_codecache *upb_json_codecache_new(); +void upb_json_codecache_free(upb_json_codecache *cache); +upb_json_parsermethod* upb_json_codecache_get(upb_json_codecache* cache, + const upb_msgdef* md); UPB_END_EXTERN_C @@ -105,17 +105,12 @@ inline BytesSink* Parser::input() { return upb_json_parser_input(this); } -inline const Handlers* ParserMethod::dest_handlers() const { - return upb_json_parsermethod_desthandlers(this); -} inline const BytesHandler* ParserMethod::input_handler() const { return upb_json_parsermethod_inputhandler(this); } /* static */ -inline reffed_ptr ParserMethod::New( - const MessageDef* md) { - const upb_json_parsermethod *m = upb_json_parsermethod_new(md, &m); - return reffed_ptr(m, &m); +inline const ParserMethod* CodeCache::Get(const MessageDef* md) { + return upb_json_codecache_get(this, md); } } /* namespace json */ @@ -123,5 +118,4 @@ inline reffed_ptr ParserMethod::New( #endif - #endif /* UPB_JSON_PARSER_H_ */ diff --git a/upb/json/parser.rl b/upb/json/parser.rl index 3a32fd9..c2866c9 100644 --- a/upb/json/parser.rl +++ b/upb/json/parser.rl @@ -152,14 +152,13 @@ void upb_stringsink_uninit(upb_stringsink *sink) { free(sink->ptr); } typedef struct { /* For encoding Any value field in binary format. */ - const upb_handlers *encoder_handlers; - upb_pb_encoder *encoder; + upb_handlercache *encoder_handlercache; upb_stringsink stringsink; /* For decoding Any value field in json format. */ - upb_json_parsermethod *parser_method; - upb_json_parser* parser; + upb_json_codecache *parser_codecache; upb_sink sink; + upb_json_parser *parser; /* Mark the range of uninterpreted values in json input before type url. */ const char *before_type_url_start; @@ -178,7 +177,7 @@ typedef struct { const upb_fielddef *f; /* The table mapping json name to fielddef for this message. */ - upb_strtable *name_table; + const upb_strtable *name_table; /* We are in a repeated-field context, ready to emit mapentries as * submessages. This flag alters the start-of-object (open-brace) behavior to @@ -257,60 +256,67 @@ struct upb_json_parser { struct tm tm; }; -struct upb_json_parsermethod { - upb_refcounted base; +struct upb_json_codecache { + upb_arena arena; + upb_inttable methods; /* upb_msgdef* -> upb_json_parsermethod* */ +}; +struct upb_json_parsermethod { + const upb_json_codecache *cache; upb_byteshandler input_handler_; - /* Keys are upb_msgdef*, values are upb_strtable (json_name -> fielddef) */ - upb_inttable name_tables; + /* Maps json_name -> fielddef */ + upb_strtable name_table; }; #define PARSER_CHECK_RETURN(x) if (!(x)) return false -static void json_parser_any_frame_reset(upb_jsonparser_any_frame *frame) { - frame->encoder_handlers = NULL; - frame->encoder = NULL; - frame->parser_method = NULL; +static upb_jsonparser_any_frame *json_parser_any_frame_new( + upb_json_parser *p) { + upb_jsonparser_any_frame *frame; + + frame = upb_env_malloc(p->env, sizeof(upb_jsonparser_any_frame)); + + frame->encoder_handlercache = upb_pb_encoder_newcache(); + frame->parser_codecache = upb_json_codecache_new(); frame->parser = NULL; frame->before_type_url_start = NULL; frame->before_type_url_end = NULL; frame->after_type_url_start = NULL; + + upb_stringsink_init(&frame->stringsink); + + return frame; } static void json_parser_any_frame_set_payload_type( upb_json_parser *p, upb_jsonparser_any_frame *frame, const upb_msgdef *payload_type) { + const upb_handlers *h; + const upb_json_parsermethod *parser_method; + upb_pb_encoder *encoder; + /* Initialize encoder. */ - frame->encoder_handlers = - upb_pb_encoder_newhandlers(payload_type, &frame->encoder_handlers); - upb_stringsink_init(&frame->stringsink); - frame->encoder = - upb_pb_encoder_create( - p->env, frame->encoder_handlers, - &frame->stringsink.sink); + h = upb_handlercache_get(frame->encoder_handlercache, payload_type); + encoder = upb_pb_encoder_create(p->env, h, &frame->stringsink.sink); /* Initialize parser. */ - frame->parser_method = - upb_json_parsermethod_new(payload_type, &frame->parser_method); - upb_sink_reset(&frame->sink, frame->encoder_handlers, frame->encoder); - frame->parser = - upb_json_parser_create(p->env, frame->parser_method, p->symtab, - &frame->sink, p->ignore_json_unknown); + parser_method = upb_json_codecache_get(frame->parser_codecache, payload_type); + upb_sink_reset(&frame->sink, h, encoder); + frame->parser = upb_json_parser_create(p->env, parser_method, p->symtab, + &frame->sink, p->ignore_json_unknown); } static void json_parser_any_frame_free(upb_jsonparser_any_frame *frame) { - upb_handlers_unref(frame->encoder_handlers, - &frame->encoder_handlers); - upb_json_parsermethod_unref(frame->parser_method, - &frame->parser_method); + upb_handlercache_free(frame->encoder_handlercache); + upb_json_codecache_free(frame->parser_codecache); upb_stringsink_uninit(&frame->stringsink); } static bool json_parser_any_frame_has_type_url( upb_jsonparser_any_frame *frame) { - return frame->encoder != NULL; + return frame->parser != NULL; } static bool json_parser_any_frame_has_value_before_type_url( @@ -332,7 +338,7 @@ static bool json_parser_any_frame_has_value( static void json_parser_any_frame_set_before_type_url_end( upb_jsonparser_any_frame *frame, const char *ptr) { - if (frame->encoder == NULL) { + if (frame->parser == NULL) { frame->before_type_url_end = ptr; } } @@ -374,9 +380,12 @@ static bool check_stack(upb_json_parser *p) { static void set_name_table(upb_json_parser *p, upb_jsonparser_frame *frame) { upb_value v; - bool ok = upb_inttable_lookupptr(&p->method->name_tables, frame->m, &v); + const upb_json_codecache *cache = p->method->cache; + bool ok = upb_inttable_lookupptr(&cache->methods, frame->m, &v); + const upb_json_parsermethod *method = upb_value_getptr(v); UPB_ASSERT(ok); - frame->name_table = upb_value_getptr(v); + + frame->name_table = &method->name_table; } /* There are GCC/Clang built-ins for overflow checking which we could start @@ -1927,9 +1936,7 @@ static bool start_subobject(upb_json_parser *p) { if (is_wellknown_msg(p, UPB_WELLKNOWN_ANY)) { p->top->is_any = true; - p->top->any_frame = - upb_env_malloc(p->env, sizeof(upb_jsonparser_any_frame)); - json_parser_any_frame_reset(p->top->any_frame); + p->top->any_frame = json_parser_any_frame_new(p); } else { p->top->is_any = false; p->top->any_frame = NULL; @@ -2649,70 +2656,46 @@ static void json_parser_reset(upb_json_parser *p) { upb_status_clear(&p->status); } -static void free_json_parsermethod(upb_refcounted *r) { - upb_json_parsermethod *method = (upb_json_parsermethod*)r; +static upb_json_parsermethod *parsermethod_new(upb_json_codecache *c, + const upb_msgdef *md) { + upb_msg_field_iter i; + upb_alloc *alloc = upb_arena_alloc(&c->arena); - upb_inttable_iter i; - upb_inttable_begin(&i, &method->name_tables); - for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { - upb_value val = upb_inttable_iter_value(&i); - upb_strtable *t = upb_value_getptr(val); - upb_strtable_uninit(t); - upb_gfree(t); - } + upb_json_parsermethod *m = upb_gmalloc(sizeof(*m)); - upb_inttable_uninit(&method->name_tables); + m->cache = c; - upb_gfree(r); -} + upb_byteshandler_init(&m->input_handler_); + upb_byteshandler_setstring(&m->input_handler_, parse, m); + upb_byteshandler_setendstr(&m->input_handler_, end, m); -static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) { - upb_msg_field_iter i; - upb_strtable *t; + upb_strtable_init2(&m->name_table, UPB_CTYPE_CONSTPTR, alloc); - /* It would be nice to stack-allocate this, but protobufs do not limit the - * length of fields to any reasonable limit. */ - char *buf = NULL; - size_t len = 0; - - if (upb_inttable_lookupptr(&m->name_tables, md, NULL)) { - return; - } - - /* TODO(haberman): handle malloc failure. */ - t = upb_gmalloc(sizeof(*t)); - upb_strtable_init(t, UPB_CTYPE_CONSTPTR); - upb_inttable_insertptr(&m->name_tables, md, upb_value_ptr(t)); + /* Build name_table */ for(upb_msg_field_begin(&i, md); !upb_msg_field_done(&i); upb_msg_field_next(&i)) { const upb_fielddef *f = upb_msg_iter_field(&i); + upb_value v = upb_value_constptr(f); + char *buf; /* Add an entry for the JSON name. */ - size_t field_len = upb_fielddef_getjsonname(f, buf, len); - if (field_len > len) { - size_t len2; - buf = upb_grealloc(buf, 0, field_len); - len = field_len; - len2 = upb_fielddef_getjsonname(f, buf, len); - UPB_ASSERT(len == len2); - } - upb_strtable_insert(t, buf, upb_value_constptr(f)); + size_t len = upb_fielddef_getjsonname(f, NULL, 0); + buf = upb_malloc(alloc, len); + upb_fielddef_getjsonname(f, buf, len); + upb_strtable_insert3(&m->name_table, buf, len, v, alloc); if (strcmp(buf, upb_fielddef_name(f)) != 0) { /* Since the JSON name is different from the regular field name, add an * entry for the raw name (compliant proto3 JSON parsers must accept * both). */ - upb_strtable_insert(t, upb_fielddef_name(f), upb_value_constptr(f)); - } - - if (upb_fielddef_issubmsg(f)) { - add_jsonname_table(m, upb_fielddef_msgsubdef(f)); + const char *name = upb_fielddef_name(f); + upb_strtable_insert3(&m->name_table, name, strlen(name), v, alloc); } } - upb_gfree(buf); + return m; } /* Public API *****************************************************************/ @@ -2740,9 +2723,7 @@ upb_json_parser *upb_json_parser_create(upb_env *env, p->top->m = upb_handlers_msgdef(output->handlers); if (is_wellknown_msg(p, UPB_WELLKNOWN_ANY)) { p->top->is_any = true; - p->top->any_frame = - upb_env_malloc(p->env, sizeof(upb_jsonparser_any_frame)); - json_parser_any_frame_reset(p->top->any_frame); + p->top->any_frame = json_parser_any_frame_new(p); } else { p->top->is_any = false; p->top->any_frame = NULL; @@ -2763,24 +2744,61 @@ upb_bytessink *upb_json_parser_input(upb_json_parser *p) { return &p->input_; } -upb_json_parsermethod *upb_json_parsermethod_new(const upb_msgdef* md, - const void* owner) { - static const struct upb_refcounted_vtbl vtbl = {NULL, free_json_parsermethod}; - upb_json_parsermethod *ret = upb_gmalloc(sizeof(*ret)); - upb_refcounted_init(upb_json_parsermethod_upcast_mutable(ret), &vtbl, owner); +const upb_byteshandler *upb_json_parsermethod_inputhandler( + const upb_json_parsermethod *m) { + return &m->input_handler_; +} + +upb_json_codecache *upb_json_codecache_new() { + upb_alloc *alloc; + upb_json_codecache *c; - upb_byteshandler_init(&ret->input_handler_); - upb_byteshandler_setstring(&ret->input_handler_, parse, ret); - upb_byteshandler_setendstr(&ret->input_handler_, end, ret); + c = upb_gmalloc(sizeof(*c)); - upb_inttable_init(&ret->name_tables, UPB_CTYPE_PTR); + upb_arena_init(&c->arena); + alloc = upb_arena_alloc(&c->arena); - add_jsonname_table(ret, md); + upb_inttable_init2(&c->methods, UPB_CTYPE_CONSTPTR, alloc); - return ret; + return c; } -const upb_byteshandler *upb_json_parsermethod_inputhandler( - const upb_json_parsermethod *m) { - return &m->input_handler_; +void upb_json_codecache_free(upb_json_codecache *c) { + upb_arena_uninit(&c->arena); + upb_gfree(c); +} + +upb_json_parsermethod *upb_json_codecache_get(upb_json_codecache *c, + const upb_msgdef *md) { + upb_json_parsermethod *m; + upb_value v; + upb_msg_field_iter i; + + if (upb_inttable_lookupptr(&c->methods, md, &v)) { + return upb_value_getptr(v); + } + + m = parsermethod_new(c, md); + v = upb_value_ptr(m); + + if (!m) return NULL; + if (!upb_inttable_insertptr(&c->methods, m, v)) return NULL; + + /* Populate parser methods for all submessages, so the name tables will + * be available during parsing. */ + for(upb_msg_field_begin(&i, md); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { + upb_fielddef *f = upb_msg_iter_field(&i); + + if (upb_fielddef_issubmsg(f)) { + const upb_msgdef *subdef = upb_fielddef_msgsubdef(f); + const upb_json_parsermethod *sub_method = + upb_json_codecache_get(c, subdef); + + if (!sub_method) return NULL; + } + } + + return m; } diff --git a/upb/json/printer.c b/upb/json/printer.c index 95a4ad5..444916f 100644 --- a/upb/json/printer.c +++ b/upb/json/printer.c @@ -48,6 +48,10 @@ void freestrpc(void *ptr) { upb_gfree(pc); } +typedef struct { + bool preserve_fieldnames; +} upb_json_printercache; + /* Convert fielddef name to JSON name and return as a string piece. */ strpc *newstrpc(upb_handlers *h, const upb_fielddef *f, bool preserve_fieldnames) { @@ -1112,8 +1116,8 @@ void printer_sethandlers(const void *closure, upb_handlers *h) { bool is_mapentry = upb_msgdef_mapentry(md); upb_handlerattr empty_attr = UPB_HANDLERATTR_INITIALIZER; upb_msg_field_iter i; - const bool *preserve_fieldnames_ptr = closure; - const bool preserve_fieldnames = *preserve_fieldnames_ptr; + const upb_json_printercache *cache = closure; + const bool preserve_fieldnames = cache->preserve_fieldnames; if (is_mapentry) { /* mapentry messages are sufficiently different that we handle them @@ -1281,9 +1285,8 @@ upb_sink *upb_json_printer_input(upb_json_printer *p) { return &p->input_; } -const upb_handlers *upb_json_printer_newhandlers(const upb_msgdef *md, - bool preserve_fieldnames, - const void *owner) { - return upb_handlers_newfrozen( - md, owner, printer_sethandlers, &preserve_fieldnames); +upb_handlercache *upb_json_printer_newcache(bool preserve_proto_fieldnames) { + upb_json_printercache *cache = upb_gmalloc(sizeof(*cache)); + cache->preserve_fieldnames = preserve_proto_fieldnames; + return upb_handlercache_new(printer_sethandlers, cache); } diff --git a/upb/json/printer.h b/upb/json/printer.h index 80644f1..fe9c8f1 100644 --- a/upb/json/printer.h +++ b/upb/json/printer.h @@ -35,14 +35,8 @@ class upb::json::Printer { /* The input to the printer. */ Sink* input(); - /* Returns handlers for printing according to the specified schema. - * If preserve_proto_fieldnames is true, the output JSON will use the - * original .proto field names (ie. {"my_field":3}) instead of using - * camelCased names, which is the default: (eg. {"myField":3}). */ - static reffed_ptr NewHandlers(const upb::MessageDef* md, - bool preserve_proto_fieldnames); - static const size_t kSize = UPB_JSON_PRINTER_SIZE; + static upb_handlercache* NewCache(bool preserve_proto_fieldnames); private: UPB_DISALLOW_POD_OPS(Printer, upb::json::Printer) @@ -60,6 +54,8 @@ const upb_handlers *upb_json_printer_newhandlers(const upb_msgdef *md, bool preserve_fieldnames, const void *owner); +upb_handlercache *upb_json_printer_newcache(bool preserve_proto_fieldnames); + UPB_END_EXTERN_C #ifdef __cplusplus @@ -71,11 +67,8 @@ inline Printer* Printer::Create(Environment* env, const upb::Handlers* handlers, return upb_json_printer_create(env, handlers, output); } inline Sink* Printer::input() { return upb_json_printer_input(this); } -inline reffed_ptr Printer::NewHandlers( - const upb::MessageDef *md, bool preserve_proto_fieldnames) { - const Handlers* h = upb_json_printer_newhandlers( - md, preserve_proto_fieldnames, &h); - return reffed_ptr(h, &h); +inline upb_handlercache* Printer::NewCache(bool preserve_proto_fieldnames) { + return upb_json_printer_newcache(preserve_proto_fieldnames); } } /* namespace json */ } /* namespace upb */ diff --git a/upb/msgfactory.c b/upb/msgfactory.c index 63df49e..23dd79a 100644 --- a/upb/msgfactory.c +++ b/upb/msgfactory.c @@ -197,7 +197,6 @@ static bool upb_msglayout_init(const upb_msgdef *m, struct upb_msgfactory { const upb_symtab *symtab; /* We own a ref. */ upb_inttable layouts; - upb_inttable mergehandlers; }; upb_msgfactory *upb_msgfactory_new(const upb_symtab *symtab) { @@ -205,7 +204,6 @@ upb_msgfactory *upb_msgfactory_new(const upb_symtab *symtab) { ret->symtab = symtab; upb_inttable_init(&ret->layouts, UPB_CTYPE_PTR); - upb_inttable_init(&ret->mergehandlers, UPB_CTYPE_CONSTPTR); return ret; } @@ -218,14 +216,7 @@ void upb_msgfactory_free(upb_msgfactory *f) { upb_msglayout_free(l); } - upb_inttable_begin(&i, &f->mergehandlers); - for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { - const upb_handlers *h = upb_value_getconstptr(upb_inttable_iter_value(&i)); - upb_handlers_unref(h, f); - } - upb_inttable_uninit(&f->layouts); - upb_inttable_uninit(&f->mergehandlers); upb_gfree(f); } diff --git a/upb/pb/compile_decoder.c b/upb/pb/compile_decoder.c index d147edf..02f5179 100644 --- a/upb/pb/compile_decoder.c +++ b/upb/pb/compile_decoder.c @@ -23,80 +23,23 @@ #define MAXLABEL 5 #define EMPTYLABEL -1 -/* mgroup *********************************************************************/ - -static void freegroup(upb_refcounted *r) { - mgroup *g = (mgroup*)r; - upb_inttable_uninit(&g->methods); -#ifdef UPB_USE_JIT_X64 - upb_pbdecoder_freejit(g); -#endif - upb_gfree(g->bytecode); - upb_gfree(g); -} - -static void visitgroup(const upb_refcounted *r, upb_refcounted_visit *visit, - void *closure) { - const mgroup *g = (const mgroup*)r; - upb_inttable_iter i; - upb_inttable_begin(&i, &g->methods); - for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { - upb_pbdecodermethod *method = upb_value_getptr(upb_inttable_iter_value(&i)); - visit(r, upb_pbdecodermethod_upcast(method), closure); - } -} - -mgroup *newgroup(const void *owner) { - mgroup *g = upb_gmalloc(sizeof(*g)); - static const struct upb_refcounted_vtbl vtbl = {visitgroup, freegroup}; - upb_refcounted_init(mgroup_upcast_mutable(g), &vtbl, owner); - upb_inttable_init(&g->methods, UPB_CTYPE_PTR); - g->bytecode = NULL; - g->bytecode_end = NULL; - return g; -} - - /* upb_pbdecodermethod ********************************************************/ -static void freemethod(upb_refcounted *r) { - upb_pbdecodermethod *method = (upb_pbdecodermethod*)r; - - if (method->dest_handlers_) { - upb_handlers_unref(method->dest_handlers_, method); - } - +static void freemethod(upb_pbdecodermethod *method) { upb_inttable_uninit(&method->dispatch); upb_gfree(method); } -static void visitmethod(const upb_refcounted *r, upb_refcounted_visit *visit, - void *closure) { - const upb_pbdecodermethod *m = (const upb_pbdecodermethod*)r; - visit(r, m->group, closure); -} - static upb_pbdecodermethod *newmethod(const upb_handlers *dest_handlers, mgroup *group) { - static const struct upb_refcounted_vtbl vtbl = {visitmethod, freemethod}; upb_pbdecodermethod *ret = upb_gmalloc(sizeof(*ret)); - upb_refcounted_init(upb_pbdecodermethod_upcast_mutable(ret), &vtbl, &ret); upb_byteshandler_init(&ret->input_handler_); - /* The method references the group and vice-versa, in a circular reference. */ - upb_ref2(ret, group); - upb_ref2(group, ret); - upb_inttable_insertptr(&group->methods, dest_handlers, upb_value_ptr(ret)); - upb_pbdecodermethod_unref(ret, &ret); - - ret->group = mgroup_upcast_mutable(group); + ret->group = group; ret->dest_handlers_ = dest_handlers; ret->is_native_ = false; /* If we JIT, it will update this later. */ upb_inttable_init(&ret->dispatch, UPB_CTYPE_UINT64); - if (ret->dest_handlers_) { - upb_handlers_ref(ret->dest_handlers_, ret); - } return ret; } @@ -114,16 +57,31 @@ bool upb_pbdecodermethod_isnative(const upb_pbdecodermethod *m) { return m->is_native_; } -const upb_pbdecodermethod *upb_pbdecodermethod_new( - const upb_pbdecodermethodopts *opts, const void *owner) { - const upb_pbdecodermethod *ret; - upb_pbcodecache cache; - upb_pbcodecache_init(&cache); - ret = upb_pbcodecache_getdecodermethod(&cache, opts); - upb_pbdecodermethod_ref(ret, owner); - upb_pbcodecache_uninit(&cache); - return ret; +/* mgroup *********************************************************************/ + +static void freegroup(mgroup *g) { + upb_inttable_iter i; + + upb_inttable_begin(&i, &g->methods); + for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { + freemethod(upb_value_getptr(upb_inttable_iter_value(&i))); + } + + upb_inttable_uninit(&g->methods); +#ifdef UPB_USE_JIT_X64 + upb_pbdecoder_freejit(g); +#endif + upb_gfree(g->bytecode); + upb_gfree(g); +} + +mgroup *newgroup() { + mgroup *g = upb_gmalloc(sizeof(*g)); + upb_inttable_init(&g->methods, UPB_CTYPE_PTR); + g->bytecode = NULL; + g->bytecode_end = NULL; + return g; } @@ -814,10 +772,13 @@ static void find_methods(compiler *c, const upb_handlers *h) { upb_value v; upb_msg_field_iter i; const upb_msgdef *md; + upb_pbdecodermethod *method; if (upb_inttable_lookupptr(&c->group->methods, h, &v)) return; - newmethod(h, c->group); + + method = newmethod(h, c->group); + upb_inttable_insertptr(&c->group->methods, h, upb_value_ptr(method)); /* Find submethods. */ md = upb_handlers_msgdef(h); @@ -893,15 +854,13 @@ static void sethandlers(mgroup *g, bool allowjit) { /* TODO(haberman): allow this to be constructed for an arbitrary set of dest * handlers and other mgroups (but verify we have a transitive closure). */ -const mgroup *mgroup_new(const upb_handlers *dest, bool allowjit, bool lazy, - const void *owner) { +const mgroup *mgroup_new(const upb_handlers *dest, bool allowjit, bool lazy) { mgroup *g; compiler *c; UPB_UNUSED(allowjit); - UPB_ASSERT(upb_handlers_isfrozen(dest)); - g = newgroup(owner); + g = newgroup(); c = newcompiler(g, lazy); find_methods(c, dest); @@ -939,56 +898,63 @@ const mgroup *mgroup_new(const upb_handlers *dest, bool allowjit, bool lazy, /* upb_pbcodecache ************************************************************/ -void upb_pbcodecache_init(upb_pbcodecache *c) { - upb_inttable_init(&c->groups, UPB_CTYPE_CONSTPTR); - c->allow_jit_ = true; +upb_pbcodecache *upb_pbcodecache_new(upb_handlercache *dest) { + upb_pbcodecache *c = upb_gmalloc(sizeof(*c)); + + if (!c) return NULL; + + c->dest = dest; + c->allow_jit = true; + c->lazy = false; + + upb_arena_init(&c->arena); + if (!upb_inttable_init(&c->groups, UPB_CTYPE_CONSTPTR)) return NULL; + + return c; } -void upb_pbcodecache_uninit(upb_pbcodecache *c) { - upb_inttable_iter i; - upb_inttable_begin(&i, &c->groups); - for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { - const mgroup *group = upb_value_getconstptr(upb_inttable_iter_value(&i)); - mgroup_unref(group, c); +void upb_pbcodecache_free(upb_pbcodecache *c) { + size_t i; + + for (i = 0; i < upb_inttable_count(&c->groups); i++) { + upb_value v; + bool ok = upb_inttable_lookup(&c->groups, i, &v); + UPB_ASSERT(ok); + freegroup((void*)upb_value_getconstptr(v)); } + upb_inttable_uninit(&c->groups); + upb_gfree(c); } bool upb_pbcodecache_allowjit(const upb_pbcodecache *c) { - return c->allow_jit_; + return c->allow_jit; } -bool upb_pbcodecache_setallowjit(upb_pbcodecache *c, bool allow) { - if (upb_inttable_count(&c->groups) > 0) - return false; - c->allow_jit_ = allow; - return true; +void upb_pbcodecache_setallowjit(upb_pbcodecache *c, bool allow) { + UPB_ASSERT(upb_inttable_count(&c->groups) == 0); + c->allow_jit = allow; } -const upb_pbdecodermethod *upb_pbcodecache_getdecodermethod( - upb_pbcodecache *c, const upb_pbdecodermethodopts *opts) { +void upb_pbdecodermethodopts_setlazy(upb_pbcodecache *c, bool lazy) { + UPB_ASSERT(upb_inttable_count(&c->groups) == 0); + c->lazy = lazy; +} + +const upb_pbdecodermethod *upb_pbcodecache_get(upb_pbcodecache *c, + const upb_msgdef *md) { upb_value v; bool ok; + const upb_handlers *h; + const mgroup *g; /* Right now we build a new DecoderMethod every time. * TODO(haberman): properly cache methods by their true key. */ - const mgroup *g = mgroup_new(opts->handlers, c->allow_jit_, opts->lazy, c); + h = upb_handlercache_get(c->dest, md); + g = mgroup_new(h, c->allow_jit, c->lazy); upb_inttable_push(&c->groups, upb_value_constptr(g)); - ok = upb_inttable_lookupptr(&g->methods, opts->handlers, &v); + ok = upb_inttable_lookupptr(&g->methods, h, &v); UPB_ASSERT(ok); return upb_value_getptr(v); } - - -/* upb_pbdecodermethodopts ****************************************************/ - -void upb_pbdecodermethodopts_init(upb_pbdecodermethodopts *opts, - const upb_handlers *h) { - opts->handlers = h; - opts->lazy = false; -} - -void upb_pbdecodermethodopts_setlazy(upb_pbdecodermethodopts *opts, bool lazy) { - opts->lazy = lazy; -} diff --git a/upb/pb/decoder.h b/upb/pb/decoder.h index 7c1877a..1a00801 100644 --- a/upb/pb/decoder.h +++ b/upb/pb/decoder.h @@ -30,10 +30,7 @@ class DecoderMethodOptions; UPB_DECLARE_TYPE(upb::pb::CodeCache, upb_pbcodecache) UPB_DECLARE_TYPE(upb::pb::Decoder, upb_pbdecoder) -UPB_DECLARE_TYPE(upb::pb::DecoderMethodOptions, upb_pbdecodermethodopts) - -UPB_DECLARE_DERIVED_TYPE(upb::pb::DecoderMethod, upb::RefCounted, - upb_pbdecodermethod, upb_refcounted) +UPB_DECLARE_TYPE(upb::pb::DecoderMethod, upb_pbdecodermethod) /* The maximum number of bytes we are required to buffer internally between * calls to the decoder. The value is 14: a 5 byte unknown tag plus ten-byte @@ -44,35 +41,10 @@ UPB_DECLARE_DERIVED_TYPE(upb::pb::DecoderMethod, upb::RefCounted, #ifdef __cplusplus -/* The parameters one uses to construct a DecoderMethod. - * TODO(haberman): move allowjit here? Seems more convenient for users. - * TODO(haberman): move this to be heap allocated for ABI stability. */ -class upb::pb::DecoderMethodOptions { - public: - /* Parameter represents the destination handlers that this method will push - * to. */ - explicit DecoderMethodOptions(const Handlers* dest_handlers); - - /* Should the decoder push submessages to lazy handlers for fields that have - * them? The caller should set this iff the lazy handlers expect data that is - * in protobuf binary format and the caller wishes to lazy parse it. */ - void set_lazy(bool lazy); -#else -struct upb_pbdecodermethodopts { -#endif - const upb_handlers *handlers; - bool lazy; -}; - -#ifdef __cplusplus - /* Represents the code to parse a protobuf according to a destination * Handlers. */ class upb::pb::DecoderMethod { public: - /* Include base methods from upb::ReferenceCounted. */ - UPB_REFCOUNTED_CPPMETHODS - /* The destination handlers that are statically bound to this method. * This method is only capable of outputting to a sink that uses these * handlers. */ @@ -84,10 +56,6 @@ class upb::pb::DecoderMethod { /* Whether this method is native. */ bool is_native() const; - /* Convenience method for generating a DecoderMethod without explicitly - * creating a CodeCache. */ - static reffed_ptr New(const DecoderMethodOptions& opts); - private: UPB_DISALLOW_POD_OPS(DecoderMethod, upb::pb::DecoderMethod) }; @@ -147,20 +115,14 @@ class upb::pb::Decoder { UPB_DISALLOW_POD_OPS(Decoder, upb::pb::Decoder) }; -#endif /* __cplusplus */ - -#ifdef __cplusplus - /* A class for caching protobuf processing code, whether bytecode for the * interpreted decoder or machine code for the JIT. * - * This class is not thread-safe. - * - * TODO(haberman): move this to be heap allocated for ABI stability. */ + * This class is not thread-safe. */ class upb::pb::CodeCache { public: - CodeCache(); - ~CodeCache(); + static CodeCache* New(HandlerCache* dest); + static void Free(CodeCache* cache); /* Whether the cache is allowed to generate machine code. Defaults to true. * There is no real reason to turn it off except for testing or if you are @@ -172,33 +134,24 @@ class upb::pb::CodeCache { bool allow_jit() const; /* This may only be called when the object is first constructed, and prior to - * any code generation, otherwise returns false and does nothing. */ - bool set_allow_jit(bool allow); + * any code generation. */ + void set_allow_jit(bool allow); - /* Returns a DecoderMethod that can push data to the given handlers. - * If a suitable method already exists, it will be returned from the cache. - * - * Specifying the destination handlers here allows the DecoderMethod to be - * statically bound to the destination handlers if possible, which can allow - * more efficient decoding. However the returned method may or may not - * actually be statically bound. But in all cases, the returned method can - * push data to the given handlers. */ - const DecoderMethod *GetDecoderMethod(const DecoderMethodOptions& opts); + /* Should the decoder push submessages to lazy handlers for fields that have + * them? The caller should set this iff the lazy handlers expect data that is + * in protobuf binary format and the caller wishes to lazy parse it. */ + void set_lazy(bool lazy); - /* If/when someone needs to explicitly create a dynamically-bound - * DecoderMethod*, we can add a method to get it here. */ + /* Returns a DecoderMethod that can push data to the given handlers. + * If a suitable method already exists, it will be returned from the cache. */ + const DecoderMethod *Get(const MessageDef* md); private: - UPB_DISALLOW_COPY_AND_ASSIGN(CodeCache) -#else -struct upb_pbcodecache { -#endif - bool allow_jit_; - - /* Array of mgroups. */ - upb_inttable groups; + UPB_DISALLOW_POD_OPS(CodeCache, upb::pb::CodeCache) }; +#endif + UPB_BEGIN_EXTERN_C upb_pbdecoder *upb_pbdecoder_create(upb_env *e, @@ -211,28 +164,21 @@ size_t upb_pbdecoder_maxnesting(const upb_pbdecoder *d); bool upb_pbdecoder_setmaxnesting(upb_pbdecoder *d, size_t max); void upb_pbdecoder_reset(upb_pbdecoder *d); -void upb_pbdecodermethodopts_init(upb_pbdecodermethodopts *opts, - const upb_handlers *h); -void upb_pbdecodermethodopts_setlazy(upb_pbdecodermethodopts *opts, bool lazy); -/* Include refcounted methods like upb_pbdecodermethod_ref(). */ -UPB_REFCOUNTED_CMETHODS(upb_pbdecodermethod, upb_pbdecodermethod_upcast) - const upb_handlers *upb_pbdecodermethod_desthandlers( const upb_pbdecodermethod *m); const upb_byteshandler *upb_pbdecodermethod_inputhandler( const upb_pbdecodermethod *m); bool upb_pbdecodermethod_isnative(const upb_pbdecodermethod *m); -const upb_pbdecodermethod *upb_pbdecodermethod_new( - const upb_pbdecodermethodopts *opts, const void *owner); -void upb_pbcodecache_init(upb_pbcodecache *c); -void upb_pbcodecache_uninit(upb_pbcodecache *c); +upb_pbcodecache *upb_pbcodecache_new(upb_handlercache *dest); +void upb_pbcodecache_free(upb_pbcodecache *c); bool upb_pbcodecache_allowjit(const upb_pbcodecache *c); -bool upb_pbcodecache_setallowjit(upb_pbcodecache *c, bool allow); -const upb_pbdecodermethod *upb_pbcodecache_getdecodermethod( - upb_pbcodecache *c, const upb_pbdecodermethodopts *opts); +void upb_pbcodecache_setallowjit(upb_pbcodecache *c, bool allow); +void upb_pbcodecache_setlazy(upb_pbcodecache *c, bool lazy); +const upb_pbdecodermethod *upb_pbcodecache_get(upb_pbcodecache *c, + const upb_msgdef *md); UPB_END_EXTERN_C @@ -264,13 +210,6 @@ inline bool Decoder::set_max_nesting(size_t max) { } inline void Decoder::Reset() { upb_pbdecoder_reset(this); } -inline DecoderMethodOptions::DecoderMethodOptions(const Handlers* h) { - upb_pbdecodermethodopts_init(this, h); -} -inline void DecoderMethodOptions::set_lazy(bool lazy) { - upb_pbdecodermethodopts_setlazy(this, lazy); -} - inline const Handlers* DecoderMethod::dest_handlers() const { return upb_pbdecodermethod_desthandlers(this); } @@ -280,28 +219,21 @@ inline const BytesHandler* DecoderMethod::input_handler() const { inline bool DecoderMethod::is_native() const { return upb_pbdecodermethod_isnative(this); } -/* static */ -inline reffed_ptr DecoderMethod::New( - const DecoderMethodOptions &opts) { - const upb_pbdecodermethod *m = upb_pbdecodermethod_new(&opts, &m); - return reffed_ptr(m, &m); -} -inline CodeCache::CodeCache() { - upb_pbcodecache_init(this); +inline CodeCache* CodeCache::New(HandlerCache* dest) { + return upb_pbcodecache_new(dest); } -inline CodeCache::~CodeCache() { - upb_pbcodecache_uninit(this); +inline void CodeCache::Free(CodeCache* cache) { + upb_pbcodecache_free(cache); } inline bool CodeCache::allow_jit() const { return upb_pbcodecache_allowjit(this); } -inline bool CodeCache::set_allow_jit(bool allow) { - return upb_pbcodecache_setallowjit(this, allow); +inline void CodeCache::set_allow_jit(bool allow) { + upb_pbcodecache_setallowjit(this, allow); } -inline const DecoderMethod *CodeCache::GetDecoderMethod( - const DecoderMethodOptions& opts) { - return upb_pbcodecache_getdecodermethod(this, &opts); +inline const DecoderMethod *CodeCache::Get(const MessageDef *md) { + return upb_pbcodecache_get(this, md); } } /* namespace pb */ diff --git a/upb/pb/decoder.int.h b/upb/pb/decoder.int.h index f02bdd5..8d464fa 100644 --- a/upb/pb/decoder.int.h +++ b/upb/pb/decoder.int.h @@ -11,17 +11,6 @@ #include "upb/sink.h" #include "upb/table.int.h" -/* C++ names are not actually used since this type isn't exposed to users. */ -#ifdef __cplusplus -namespace upb { -namespace pb { -class MessageGroup; -} /* namespace pb */ -} /* namespace upb */ -#endif -UPB_DECLARE_DERIVED_TYPE(upb::pb::MessageGroup, upb::RefCounted, - mgroup, upb_refcounted) - /* Opcode definitions. The canonical meaning of each opcode is its * implementation in the interpreter (the JIT is written to match this). * @@ -83,30 +72,25 @@ typedef enum { UPB_INLINE opcode getop(uint32_t instr) { return instr & 0xff; } +struct upb_pbcodecache { + upb_arena arena; + upb_handlercache *dest; + bool allow_jit; + bool lazy; + + /* Array of mgroups. */ + upb_inttable groups; +}; + /* Method group; represents a set of decoder methods that had their code - * emitted together, and must therefore be freed together. Immutable once - * created. It is possible we may want to expose this to users at some point. - * - * Overall ownership of Decoder objects looks like this: - * - * +----------+ - * | | <---> DecoderMethod - * | method | - * CodeCache ---> | group | <---> DecoderMethod - * | | - * | (mgroup) | <---> DecoderMethod - * +----------+ - */ -struct mgroup { - upb_refcounted base; - - /* Maps upb_msgdef/upb_handlers -> upb_pbdecodermethod. We own refs on the - * methods. */ + * emitted together. Immutable once created. */ +typedef struct { + /* Maps upb_msgdef/upb_handlers -> upb_pbdecodermethod. Owned by us. + * + * Ideally this would be on pbcodecache (if we were actually caching code). + * Right now we don't actually cache anything, which is wasteful. */ upb_inttable methods; - /* When we add the ability to link to previously existing mgroups, we'll - * need an array of mgroups we reference here, and own refs on them. */ - /* The bytecode for our methods, if any exists. Owned by us. */ uint32_t *bytecode; uint32_t *bytecode_end; @@ -119,7 +103,7 @@ struct mgroup { char *debug_info; void *dl; #endif -}; +} mgroup; /* The maximum that any submessages can be nested. Matches proto2's limit. * This specifies the size of the decoder's statically-sized array and therefore @@ -159,8 +143,6 @@ typedef struct { } upb_pbdecoder_frame; struct upb_pbdecodermethod { - upb_refcounted base; - /* While compiling, the base is relative in "ofs", after compiling it is * absolute in "ptr". */ union { @@ -168,14 +150,8 @@ struct upb_pbdecodermethod { void *ptr; /* Pointer to bytecode or machine code for this method. */ } code_base; - /* The decoder method group to which this method belongs. We own a ref. - * Owning a ref on the entire group is more coarse-grained than is strictly - * necessary; all we truly require is that methods we directly reference - * outlive us, while the group could contain many other messages we don't - * require. But the group represents the messages that were - * allocated+compiled together, so it makes the most sense to free them - * together also. */ - const upb_refcounted *group; + /* The decoder method group to which this method belongs. */ + const mgroup *group; /* Whether this method is native code or bytecode. */ bool is_native_; @@ -276,7 +252,6 @@ const char *upb_pbdecoder_getopname(unsigned int op); /* JIT codegen entry point. */ void upb_pbdecoder_jit(mgroup *group); void upb_pbdecoder_freejit(mgroup *group); -UPB_REFCOUNTED_CMETHODS(mgroup, mgroup_upcast) /* A special label that means "do field dispatch for this message and branch to * wherever that takes you." */ diff --git a/upb/pb/encoder.c b/upb/pb/encoder.c index 839ede0..ca3ca5c 100644 --- a/upb/pb/encoder.c +++ b/upb/pb/encoder.c @@ -526,9 +526,8 @@ void upb_pb_encoder_reset(upb_pb_encoder *e) { /* public API *****************************************************************/ -const upb_handlers *upb_pb_encoder_newhandlers(const upb_msgdef *m, - const void *owner) { - return upb_handlers_newfrozen(m, owner, newhandlers_callback, NULL); +upb_handlercache *upb_pb_encoder_newcache() { + return upb_handlercache_new(newhandlers_callback, NULL); } upb_pb_encoder *upb_pb_encoder_create(upb_env *env, const upb_handlers *h, diff --git a/upb/pb/encoder.h b/upb/pb/encoder.h index 41b7e7b..eefa385 100644 --- a/upb/pb/encoder.h +++ b/upb/pb/encoder.h @@ -47,7 +47,7 @@ class upb::pb::Encoder { Sink* input(); /* Creates a new set of handlers for this MessageDef. */ - static reffed_ptr NewHandlers(const MessageDef* msg); + static upb_handlercache* NewCache(); static const size_t kSize = UPB_PB_ENCODER_SIZE; @@ -59,12 +59,12 @@ class upb::pb::Encoder { UPB_BEGIN_EXTERN_C -const upb_handlers *upb_pb_encoder_newhandlers(const upb_msgdef *m, - const void *owner); upb_sink *upb_pb_encoder_input(upb_pb_encoder *p); upb_pb_encoder* upb_pb_encoder_create(upb_env* e, const upb_handlers* h, upb_bytessink* output); +upb_handlercache *upb_pb_encoder_newcache(); + UPB_END_EXTERN_C #ifdef __cplusplus @@ -78,10 +78,8 @@ inline Encoder* Encoder::Create(Environment* env, const Handlers* handlers, inline Sink* Encoder::input() { return upb_pb_encoder_input(this); } -inline reffed_ptr Encoder::NewHandlers( - const upb::MessageDef *md) { - const Handlers* h = upb_pb_encoder_newhandlers(md, &h); - return reffed_ptr(h, &h); +inline upb_handlercache* Encoder::NewCache() { + return upb_pb_encoder_newcache(); } } /* namespace pb */ } /* namespace upb */ diff --git a/upb/pb/textprinter.c b/upb/pb/textprinter.c index e8033f8..b6f8024 100644 --- a/upb/pb/textprinter.c +++ b/upb/pb/textprinter.c @@ -327,9 +327,8 @@ upb_textprinter *upb_textprinter_create(upb_env *env, const upb_handlers *h, return p; } -const upb_handlers *upb_textprinter_newhandlers(const upb_msgdef *m, - const void *owner) { - return upb_handlers_newfrozen(m, owner, &onmreg, NULL); +upb_handlercache *upb_textprinter_newcache() { + return upb_handlercache_new(&onmreg, NULL); } upb_sink *upb_textprinter_input(upb_textprinter *p) { return &p->input_; } diff --git a/upb/pb/textprinter.h b/upb/pb/textprinter.h index 2f40ed8..06ff7d5 100644 --- a/upb/pb/textprinter.h +++ b/upb/pb/textprinter.h @@ -34,7 +34,7 @@ class upb::pb::TextPrinter { /* If handler caching becomes a requirement we can add a code cache as in * decoder.h */ - static reffed_ptr NewHandlers(const MessageDef* md); + static HandlerCache* NewCache(); }; #endif @@ -47,8 +47,7 @@ upb_textprinter *upb_textprinter_create(upb_env *env, const upb_handlers *h, void upb_textprinter_setsingleline(upb_textprinter *p, bool single_line); upb_sink *upb_textprinter_input(upb_textprinter *p); -const upb_handlers *upb_textprinter_newhandlers(const upb_msgdef *m, - const void *owner); +upb_handlercache *upb_textprinter_newcache(); UPB_END_EXTERN_C @@ -67,10 +66,8 @@ inline void TextPrinter::SetSingleLineMode(bool single_line) { inline Sink* TextPrinter::input() { return upb_textprinter_input(this); } -inline reffed_ptr TextPrinter::NewHandlers( - const MessageDef *md) { - const Handlers* h = upb_textprinter_newhandlers(md, &h); - return reffed_ptr(h, &h); +inline HandlerCache* TextPrinter::NewCache() { + return upb_textprinter_newcache(); } } /* namespace pb */ } /* namespace upb */ diff --git a/upb/refcounted.c b/upb/refcounted.c deleted file mode 100644 index f00dbb7..0000000 --- a/upb/refcounted.c +++ /dev/null @@ -1,851 +0,0 @@ -/* -** upb::RefCounted Implementation -** -** Our key invariants are: -** 1. reference cycles never span groups -** 2. for ref2(to, from), we increment to's count iff group(from) != group(to) -** -** The previous two are how we avoid leaking cycles. Other important -** invariants are: -** 3. for mutable objects "from" and "to", if there exists a ref2(to, from) -** this implies group(from) == group(to). (In practice, what we implement -** is even stronger; "from" and "to" will share a group if there has *ever* -** been a ref2(to, from), but all that is necessary for correctness is the -** weaker one). -** 4. mutable and immutable objects are never in the same group. -*/ - -#include "upb/refcounted.h" - -#include - -static void freeobj(upb_refcounted *o); - -const char untracked_val; -const void *UPB_UNTRACKED_REF = &untracked_val; - -/* arch-specific atomic primitives *******************************************/ - -#ifdef UPB_THREAD_UNSAFE /*---------------------------------------------------*/ - -static void atomic_inc(uint32_t *a) { (*a)++; } -static bool atomic_dec(uint32_t *a) { return --(*a) == 0; } - -#elif defined(__GNUC__) || defined(__clang__) /*------------------------------*/ - -static void atomic_inc(uint32_t *a) { __sync_fetch_and_add(a, 1); } -static bool atomic_dec(uint32_t *a) { return __sync_sub_and_fetch(a, 1) == 0; } - -#elif defined(WIN32) /*-------------------------------------------------------*/ - -#include - -static void atomic_inc(upb_atomic_t *a) { InterlockedIncrement(&a->val); } -static bool 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 - -/* All static objects point to this refcount. - * It is special-cased in ref/unref below. */ -uint32_t static_refcount = -1; - -/* We can avoid atomic ops for statically-declared objects. - * This is a minor optimization but nice since we can avoid degrading under - * contention in this case. */ - -static void refgroup(uint32_t *group) { - if (group != &static_refcount) - atomic_inc(group); -} - -static bool unrefgroup(uint32_t *group) { - if (group == &static_refcount) { - return false; - } else { - return atomic_dec(group); - } -} - - -/* Reference tracking (debug only) ********************************************/ - -#ifdef UPB_DEBUG_REFS - -#ifdef UPB_THREAD_UNSAFE - -static void upb_lock() {} -static void upb_unlock() {} - -#else - -/* User must define functions that lock/unlock a global mutex and link this - * file against them. */ -void upb_lock(); -void upb_unlock(); - -#endif - -/* UPB_DEBUG_REFS mode counts on being able to malloc() memory in some - * code-paths that can normally never fail, like upb_refcounted_ref(). Since - * we have no way to propagage out-of-memory errors back to the user, and since - * these errors can only occur in UPB_DEBUG_REFS mode, we use an allocator that - * immediately aborts on failure (avoiding the global allocator, which might - * inject failures). */ - -#include - -static void *upb_debugrefs_allocfunc(upb_alloc *alloc, void *ptr, - size_t oldsize, size_t size) { - UPB_UNUSED(alloc); - UPB_UNUSED(oldsize); - if (size == 0) { - free(ptr); - return NULL; - } else { - void *ret = realloc(ptr, size); - - if (!ret) { - abort(); - } - - return ret; - } -} - -upb_alloc upb_alloc_debugrefs = {&upb_debugrefs_allocfunc}; - -typedef struct { - int count; /* How many refs there are (duplicates only allowed for ref2). */ - bool is_ref2; -} trackedref; - -static trackedref *trackedref_new(bool is_ref2) { - trackedref *ret = upb_malloc(&upb_alloc_debugrefs, sizeof(*ret)); - ret->count = 1; - ret->is_ref2 = is_ref2; - return ret; -} - -static void track(const upb_refcounted *r, const void *owner, bool ref2) { - upb_value v; - - UPB_ASSERT(owner); - if (owner == UPB_UNTRACKED_REF) return; - - upb_lock(); - if (upb_inttable_lookupptr(r->refs, owner, &v)) { - trackedref *ref = upb_value_getptr(v); - /* Since we allow multiple ref2's for the same to/from pair without - * allocating separate memory for each one, we lose the fine-grained - * tracking behavior we get with regular refs. Since ref2s only happen - * inside upb, we'll accept this limitation until/unless there is a really - * difficult upb-internal bug that can't be figured out without it. */ - UPB_ASSERT(ref2); - UPB_ASSERT(ref->is_ref2); - ref->count++; - } else { - trackedref *ref = trackedref_new(ref2); - upb_inttable_insertptr2(r->refs, owner, upb_value_ptr(ref), - &upb_alloc_debugrefs); - if (ref2) { - /* We know this cast is safe when it is a ref2, because it's coming from - * another refcounted object. */ - const upb_refcounted *from = owner; - UPB_ASSERT(!upb_inttable_lookupptr(from->ref2s, r, NULL)); - upb_inttable_insertptr2(from->ref2s, r, upb_value_ptr(NULL), - &upb_alloc_debugrefs); - } - } - upb_unlock(); -} - -static void untrack(const upb_refcounted *r, const void *owner, bool ref2) { - upb_value v; - bool found; - trackedref *ref; - - UPB_ASSERT(owner); - if (owner == UPB_UNTRACKED_REF) return; - - upb_lock(); - found = upb_inttable_lookupptr(r->refs, owner, &v); - /* This assert will fail if an owner attempts to release a ref it didn't have. */ - UPB_ASSERT(found); - ref = upb_value_getptr(v); - UPB_ASSERT(ref->is_ref2 == ref2); - if (--ref->count == 0) { - free(ref); - upb_inttable_removeptr(r->refs, owner, NULL); - if (ref2) { - /* We know this cast is safe when it is a ref2, because it's coming from - * another refcounted object. */ - const upb_refcounted *from = owner; - bool removed = upb_inttable_removeptr(from->ref2s, r, NULL); - UPB_ASSERT(removed); - } - } - upb_unlock(); -} - -static void checkref(const upb_refcounted *r, const void *owner, bool ref2) { - upb_value v; - bool found; - trackedref *ref; - - upb_lock(); - found = upb_inttable_lookupptr(r->refs, owner, &v); - UPB_ASSERT(found); - ref = upb_value_getptr(v); - UPB_ASSERT(ref->is_ref2 == ref2); - upb_unlock(); -} - -/* Populates the given UPB_CTYPE_INT32 inttable with counts of ref2's that - * originate from the given owner. */ -static void getref2s(const upb_refcounted *owner, upb_inttable *tab) { - upb_inttable_iter i; - - upb_lock(); - upb_inttable_begin(&i, owner->ref2s); - for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { - upb_value v; - upb_value count; - trackedref *ref; - bool found; - - upb_refcounted *to = (upb_refcounted*)upb_inttable_iter_key(&i); - - /* To get the count we need to look in the target's table. */ - found = upb_inttable_lookupptr(to->refs, owner, &v); - UPB_ASSERT(found); - ref = upb_value_getptr(v); - count = upb_value_int32(ref->count); - - upb_inttable_insertptr2(tab, to, count, &upb_alloc_debugrefs); - } - upb_unlock(); -} - -typedef struct { - upb_inttable ref2; - const upb_refcounted *obj; -} check_state; - -static void visit_check(const upb_refcounted *obj, const upb_refcounted *subobj, - void *closure) { - check_state *s = closure; - upb_inttable *ref2 = &s->ref2; - upb_value v; - bool removed; - int32_t newcount; - - UPB_ASSERT(obj == s->obj); - UPB_ASSERT(subobj); - removed = upb_inttable_removeptr(ref2, subobj, &v); - /* The following assertion will fail if the visit() function visits a subobj - * that it did not have a ref2 on, or visits the same subobj too many times. */ - UPB_ASSERT(removed); - newcount = upb_value_getint32(v) - 1; - if (newcount > 0) { - upb_inttable_insert2(ref2, (uintptr_t)subobj, upb_value_int32(newcount), - &upb_alloc_debugrefs); - } -} - -static void visit(const upb_refcounted *r, upb_refcounted_visit *v, - void *closure) { - /* In DEBUG_REFS mode we know what existing ref2 refs there are, so we know - * exactly the set of nodes that visit() should visit. So we verify visit()'s - * correctness here. */ - check_state state; - state.obj = r; - upb_inttable_init2(&state.ref2, UPB_CTYPE_INT32, &upb_alloc_debugrefs); - getref2s(r, &state.ref2); - - /* This should visit any children in the ref2 table. */ - if (r->vtbl->visit) r->vtbl->visit(r, visit_check, &state); - - /* This assertion will fail if the visit() function missed any children. */ - UPB_ASSERT(upb_inttable_count(&state.ref2) == 0); - upb_inttable_uninit2(&state.ref2, &upb_alloc_debugrefs); - if (r->vtbl->visit) r->vtbl->visit(r, v, closure); -} - -static void trackinit(upb_refcounted *r) { - r->refs = upb_malloc(&upb_alloc_debugrefs, sizeof(*r->refs)); - r->ref2s = upb_malloc(&upb_alloc_debugrefs, sizeof(*r->ref2s)); - upb_inttable_init2(r->refs, UPB_CTYPE_PTR, &upb_alloc_debugrefs); - upb_inttable_init2(r->ref2s, UPB_CTYPE_PTR, &upb_alloc_debugrefs); -} - -static void trackfree(const upb_refcounted *r) { - upb_inttable_uninit2(r->refs, &upb_alloc_debugrefs); - upb_inttable_uninit2(r->ref2s, &upb_alloc_debugrefs); - upb_free(&upb_alloc_debugrefs, r->refs); - upb_free(&upb_alloc_debugrefs, r->ref2s); -} - -#else - -static void track(const upb_refcounted *r, const void *owner, bool ref2) { - UPB_UNUSED(r); - UPB_UNUSED(owner); - UPB_UNUSED(ref2); -} - -static void untrack(const upb_refcounted *r, const void *owner, bool ref2) { - UPB_UNUSED(r); - UPB_UNUSED(owner); - UPB_UNUSED(ref2); -} - -static void checkref(const upb_refcounted *r, const void *owner, bool ref2) { - UPB_UNUSED(r); - UPB_UNUSED(owner); - UPB_UNUSED(ref2); -} - -static void trackinit(upb_refcounted *r) { - UPB_UNUSED(r); -} - -static void trackfree(const upb_refcounted *r) { - UPB_UNUSED(r); -} - -static void visit(const upb_refcounted *r, upb_refcounted_visit *v, - void *closure) { - if (r->vtbl->visit) r->vtbl->visit(r, v, closure); -} - -#endif /* UPB_DEBUG_REFS */ - - -/* freeze() *******************************************************************/ - -/* The freeze() operation is by far the most complicated part of this scheme. - * We compute strongly-connected components and then mutate the graph such that - * we preserve the invariants documented at the top of this file. And we must - * handle out-of-memory errors gracefully (without leaving the graph - * inconsistent), which adds to the fun. */ - -/* The state used by the freeze operation (shared across many functions). */ -typedef struct { - int depth; - int maxdepth; - uint64_t index; - /* Maps upb_refcounted* -> attributes (color, etc). attr layout varies by - * color. */ - upb_inttable objattr; - upb_inttable stack; /* stack of upb_refcounted* for Tarjan's algorithm. */ - upb_inttable groups; /* array of uint32_t*, malloc'd refcounts for new groups */ - upb_status *status; - jmp_buf err; -} tarjan; - -static void release_ref2(const upb_refcounted *obj, - const upb_refcounted *subobj, - void *closure); - -/* Node attributes -----------------------------------------------------------*/ - -/* After our analysis phase all nodes will be either GRAY or WHITE. */ - -typedef enum { - BLACK = 0, /* Object has not been seen. */ - GRAY, /* Object has been found via a refgroup but may not be reachable. */ - GREEN, /* Object is reachable and is currently on the Tarjan stack. */ - WHITE /* Object is reachable and has been assigned a group (SCC). */ -} color_t; - -UPB_NORETURN static void err(tarjan *t) { longjmp(t->err, 1); } -UPB_NORETURN static void oom(tarjan *t) { - upb_status_seterrmsg(t->status, "out of memory"); - err(t); -} - -static uint64_t trygetattr(const tarjan *t, const upb_refcounted *r) { - upb_value v; - return upb_inttable_lookupptr(&t->objattr, r, &v) ? - upb_value_getuint64(v) : 0; -} - -static uint64_t getattr(const tarjan *t, const upb_refcounted *r) { - upb_value v; - bool found = upb_inttable_lookupptr(&t->objattr, r, &v); - UPB_ASSERT(found); - return upb_value_getuint64(v); -} - -static void setattr(tarjan *t, const upb_refcounted *r, uint64_t attr) { - upb_inttable_removeptr(&t->objattr, r, NULL); - upb_inttable_insertptr(&t->objattr, r, upb_value_uint64(attr)); -} - -static color_t color(tarjan *t, const upb_refcounted *r) { - return trygetattr(t, r) & 0x3; /* Color is always stored in the low 2 bits. */ -} - -static void set_gray(tarjan *t, const upb_refcounted *r) { - UPB_ASSERT(color(t, r) == BLACK); - setattr(t, r, GRAY); -} - -/* Pushes an obj onto the Tarjan stack and sets it to GREEN. */ -static void push(tarjan *t, const upb_refcounted *r) { - UPB_ASSERT(color(t, r) == BLACK || color(t, r) == GRAY); - /* This defines the attr layout for the GREEN state. "index" and "lowlink" - * get 31 bits, which is plenty (limit of 2B objects frozen at a time). */ - setattr(t, r, GREEN | (t->index << 2) | (t->index << 33)); - if (++t->index == 0x80000000) { - upb_status_seterrmsg(t->status, "too many objects to freeze"); - err(t); - } - upb_inttable_push(&t->stack, upb_value_ptr((void*)r)); -} - -/* Pops an obj from the Tarjan stack and sets it to WHITE, with a ptr to its - * SCC group. */ -static upb_refcounted *pop(tarjan *t) { - upb_refcounted *r = upb_value_getptr(upb_inttable_pop(&t->stack)); - UPB_ASSERT(color(t, r) == GREEN); - /* This defines the attr layout for nodes in the WHITE state. - * Top of group stack is [group, NULL]; we point at group. */ - setattr(t, r, WHITE | (upb_inttable_count(&t->groups) - 2) << 8); - return r; -} - -static void tarjan_newgroup(tarjan *t) { - uint32_t *group = upb_gmalloc(sizeof(*group)); - if (!group) oom(t); - /* Push group and empty group leader (we'll fill in leader later). */ - if (!upb_inttable_push(&t->groups, upb_value_ptr(group)) || - !upb_inttable_push(&t->groups, upb_value_ptr(NULL))) { - upb_gfree(group); - oom(t); - } - *group = 0; -} - -static uint32_t idx(tarjan *t, const upb_refcounted *r) { - UPB_ASSERT(color(t, r) == GREEN); - return (getattr(t, r) >> 2) & 0x7FFFFFFF; -} - -static uint32_t lowlink(tarjan *t, const upb_refcounted *r) { - if (color(t, r) == GREEN) { - return getattr(t, r) >> 33; - } else { - return UINT32_MAX; - } -} - -static void set_lowlink(tarjan *t, const upb_refcounted *r, uint32_t lowlink) { - UPB_ASSERT(color(t, r) == GREEN); - setattr(t, r, ((uint64_t)lowlink << 33) | (getattr(t, r) & 0x1FFFFFFFF)); -} - -static uint32_t *group(tarjan *t, upb_refcounted *r) { - uint64_t groupnum; - upb_value v; - bool found; - - UPB_ASSERT(color(t, r) == WHITE); - groupnum = getattr(t, r) >> 8; - found = upb_inttable_lookup(&t->groups, groupnum, &v); - UPB_ASSERT(found); - return upb_value_getptr(v); -} - -/* If the group leader for this object's group has not previously been set, - * the given object is assigned to be its leader. */ -static upb_refcounted *groupleader(tarjan *t, upb_refcounted *r) { - uint64_t leader_slot; - upb_value v; - bool found; - - UPB_ASSERT(color(t, r) == WHITE); - leader_slot = (getattr(t, r) >> 8) + 1; - found = upb_inttable_lookup(&t->groups, leader_slot, &v); - UPB_ASSERT(found); - if (upb_value_getptr(v)) { - return upb_value_getptr(v); - } else { - upb_inttable_remove(&t->groups, leader_slot, NULL); - upb_inttable_insert(&t->groups, leader_slot, upb_value_ptr(r)); - return r; - } -} - - -/* Tarjan's algorithm --------------------------------------------------------*/ - -/* See: - * http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm */ -static void do_tarjan(const upb_refcounted *obj, tarjan *t); - -static void tarjan_visit(const upb_refcounted *obj, - const upb_refcounted *subobj, - void *closure) { - tarjan *t = closure; - if (++t->depth > t->maxdepth) { - upb_status_seterrf(t->status, "graph too deep to freeze (%d)", t->maxdepth); - err(t); - } else if (subobj->is_frozen || color(t, subobj) == WHITE) { - /* Do nothing: we don't want to visit or color already-frozen nodes, - * and WHITE nodes have already been assigned a SCC. */ - } else if (color(t, subobj) < GREEN) { - /* Subdef has not yet been visited; recurse on it. */ - do_tarjan(subobj, t); - set_lowlink(t, obj, UPB_MIN(lowlink(t, obj), lowlink(t, subobj))); - } else if (color(t, subobj) == GREEN) { - /* Subdef is in the stack and hence in the current SCC. */ - set_lowlink(t, obj, UPB_MIN(lowlink(t, obj), idx(t, subobj))); - } - --t->depth; -} - -static void do_tarjan(const upb_refcounted *obj, tarjan *t) { - if (color(t, obj) == BLACK) { - /* We haven't seen this object's group; mark the whole group GRAY. */ - const upb_refcounted *o = obj; - do { set_gray(t, o); } while ((o = o->next) != obj); - } - - push(t, obj); - visit(obj, tarjan_visit, t); - if (lowlink(t, obj) == idx(t, obj)) { - tarjan_newgroup(t); - while (pop(t) != obj) - ; - } -} - - -/* freeze() ------------------------------------------------------------------*/ - -static void crossref(const upb_refcounted *r, const upb_refcounted *subobj, - void *_t) { - tarjan *t = _t; - UPB_ASSERT(color(t, r) > BLACK); - if (color(t, subobj) > BLACK && r->group != subobj->group) { - /* Previously this ref was not reflected in subobj->group because they - * were in the same group; now that they are split a ref must be taken. */ - refgroup(subobj->group); - } -} - -static bool freeze(upb_refcounted *const*roots, int n, upb_status *s, - int maxdepth) { - volatile bool ret = false; - int i; - upb_inttable_iter iter; - - /* We run in two passes so that we can allocate all memory before performing - * any mutation of the input -- this allows us to leave the input unchanged - * in the case of memory allocation failure. */ - tarjan t; - t.index = 0; - t.depth = 0; - t.maxdepth = maxdepth; - t.status = s; - if (!upb_inttable_init(&t.objattr, UPB_CTYPE_UINT64)) goto err1; - if (!upb_inttable_init(&t.stack, UPB_CTYPE_PTR)) goto err2; - if (!upb_inttable_init(&t.groups, UPB_CTYPE_PTR)) goto err3; - if (setjmp(t.err) != 0) goto err4; - - - for (i = 0; i < n; i++) { - if (color(&t, roots[i]) < GREEN) { - do_tarjan(roots[i], &t); - } - } - - /* If we've made it this far, no further errors are possible so it's safe to - * mutate the objects without risk of leaving them in an inconsistent state. */ - ret = true; - - /* The transformation that follows requires care. The preconditions are: - * - all objects in attr map are WHITE or GRAY, and are in mutable groups - * (groups of all mutable objs) - * - no ref2(to, from) refs have incremented count(to) if both "to" and - * "from" are in our attr map (this follows from invariants (2) and (3)) */ - - /* Pass 1: we remove WHITE objects from their mutable groups, and add them to - * new groups according to the SCC's we computed. These new groups will - * consist of only frozen objects. None will be immediately collectible, - * because WHITE objects are by definition reachable from one of "roots", - * which the caller must own refs on. */ - upb_inttable_begin(&iter, &t.objattr); - for(; !upb_inttable_done(&iter); upb_inttable_next(&iter)) { - upb_refcounted *obj = (upb_refcounted*)upb_inttable_iter_key(&iter); - /* Since removal from a singly-linked list requires access to the object's - * predecessor, we consider obj->next instead of obj for moving. With the - * while() loop we guarantee that we will visit every node's predecessor. - * Proof: - * 1. every node's predecessor is in our attr map. - * 2. though the loop body may change a node's predecessor, it will only - * change it to be the node we are currently operating on, so with a - * while() loop we guarantee ourselves the chance to remove each node. */ - while (color(&t, obj->next) == WHITE && - group(&t, obj->next) != obj->next->group) { - upb_refcounted *leader; - - /* Remove from old group. */ - upb_refcounted *move = obj->next; - if (obj == move) { - /* Removing the last object from a group. */ - UPB_ASSERT(*obj->group == obj->individual_count); - upb_gfree(obj->group); - } else { - obj->next = move->next; - /* This may decrease to zero; we'll collect GRAY objects (if any) that - * remain in the group in the third pass. */ - UPB_ASSERT(*move->group >= move->individual_count); - *move->group -= move->individual_count; - } - - /* Add to new group. */ - leader = groupleader(&t, move); - if (move == leader) { - /* First object added to new group is its leader. */ - move->group = group(&t, move); - move->next = move; - *move->group = move->individual_count; - } else { - /* Group already has at least one object in it. */ - UPB_ASSERT(leader->group == group(&t, move)); - move->group = group(&t, move); - move->next = leader->next; - leader->next = move; - *move->group += move->individual_count; - } - - move->is_frozen = true; - } - } - - /* Pass 2: GRAY and WHITE objects "obj" with ref2(to, obj) references must - * increment count(to) if group(obj) != group(to) (which could now be the - * case if "to" was just frozen). */ - upb_inttable_begin(&iter, &t.objattr); - for(; !upb_inttable_done(&iter); upb_inttable_next(&iter)) { - upb_refcounted *obj = (upb_refcounted*)upb_inttable_iter_key(&iter); - visit(obj, crossref, &t); - } - - /* Pass 3: GRAY objects are collected if their group's refcount dropped to - * zero when we removed its white nodes. This can happen if they had only - * been kept alive by virtue of sharing a group with an object that was just - * frozen. - * - * It is important that we do this last, since the GRAY object's free() - * function could call unref2() on just-frozen objects, which will decrement - * refs that were added in pass 2. */ - upb_inttable_begin(&iter, &t.objattr); - for(; !upb_inttable_done(&iter); upb_inttable_next(&iter)) { - upb_refcounted *obj = (upb_refcounted*)upb_inttable_iter_key(&iter); - if (obj->group == NULL || *obj->group == 0) { - if (obj->group) { - upb_refcounted *o; - - /* We eagerly free() the group's count (since we can't easily determine - * the group's remaining size it's the easiest way to ensure it gets - * done). */ - upb_gfree(obj->group); - - /* Visit to release ref2's (done in a separate pass since release_ref2 - * depends on o->group being unmodified so it can test merged()). */ - o = obj; - do { visit(o, release_ref2, NULL); } while ((o = o->next) != obj); - - /* Mark "group" fields as NULL so we know to free the objects later in - * this loop, but also don't try to delete the group twice. */ - o = obj; - do { o->group = NULL; } while ((o = o->next) != obj); - } - freeobj(obj); - } - } - -err4: - if (!ret) { - upb_inttable_begin(&iter, &t.groups); - for(; !upb_inttable_done(&iter); upb_inttable_next(&iter)) - upb_gfree(upb_value_getptr(upb_inttable_iter_value(&iter))); - } - upb_inttable_uninit(&t.groups); -err3: - upb_inttable_uninit(&t.stack); -err2: - upb_inttable_uninit(&t.objattr); -err1: - return ret; -} - - -/* Misc internal functions ***************************************************/ - -static bool merged(const upb_refcounted *r, const upb_refcounted *r2) { - return r->group == r2->group; -} - -static void merge(upb_refcounted *r, upb_refcounted *from) { - upb_refcounted *base; - upb_refcounted *tmp; - - if (merged(r, from)) return; - *r->group += *from->group; - upb_gfree(from->group); - base = from; - - /* Set all refcount pointers in the "from" chain to the merged refcount. - * - * TODO(haberman): this linear algorithm can result in an overall O(n^2) bound - * if the user continuously extends a group by one object. Prevent this by - * using one of the techniques in this paper: - * http://bioinfo.ict.ac.cn/~dbu/AlgorithmCourses/Lectures/Union-Find-Tarjan.pdf */ - do { from->group = r->group; } while ((from = from->next) != base); - - /* Merge the two circularly linked lists by swapping their next pointers. */ - tmp = r->next; - r->next = base->next; - base->next = tmp; -} - -static void unref(const upb_refcounted *r); - -static void release_ref2(const upb_refcounted *obj, - const upb_refcounted *subobj, - void *closure) { - UPB_UNUSED(closure); - untrack(subobj, obj, true); - if (!merged(obj, subobj)) { - UPB_ASSERT(subobj->is_frozen); - unref(subobj); - } -} - -static void unref(const upb_refcounted *r) { - if (unrefgroup(r->group)) { - const upb_refcounted *o; - - upb_gfree(r->group); - - /* In two passes, since release_ref2 needs a guarantee that any subobjs - * are alive. */ - o = r; - do { visit(o, release_ref2, NULL); } while((o = o->next) != r); - - o = r; - do { - const upb_refcounted *next = o->next; - UPB_ASSERT(o->is_frozen || o->individual_count == 0); - freeobj((upb_refcounted*)o); - o = next; - } while(o != r); - } -} - -static void freeobj(upb_refcounted *o) { - trackfree(o); - o->vtbl->free((upb_refcounted*)o); -} - - -/* Public interface ***********************************************************/ - -bool upb_refcounted_init(upb_refcounted *r, - const struct upb_refcounted_vtbl *vtbl, - const void *owner) { -#ifndef NDEBUG - /* Endianness check. This is unrelated to upb_refcounted, it's just a - * convenient place to put the check that we can be assured will run for - * basically every program using upb. */ - const int x = 1; -#ifdef UPB_BIG_ENDIAN - UPB_ASSERT(*(char*)&x != 1); -#else - UPB_ASSERT(*(char*)&x == 1); -#endif -#endif - - r->next = r; - r->vtbl = vtbl; - r->individual_count = 0; - r->is_frozen = false; - r->group = upb_gmalloc(sizeof(*r->group)); - if (!r->group) return false; - *r->group = 0; - trackinit(r); - upb_refcounted_ref(r, owner); - return true; -} - -bool upb_refcounted_isfrozen(const upb_refcounted *r) { - return r->is_frozen; -} - -void upb_refcounted_ref(const upb_refcounted *r, const void *owner) { - track(r, owner, false); - if (!r->is_frozen) - ((upb_refcounted*)r)->individual_count++; - refgroup(r->group); -} - -void upb_refcounted_unref(const upb_refcounted *r, const void *owner) { - untrack(r, owner, false); - if (!r->is_frozen) - ((upb_refcounted*)r)->individual_count--; - unref(r); -} - -void upb_refcounted_ref2(const upb_refcounted *r, upb_refcounted *from) { - UPB_ASSERT(!from->is_frozen); /* Non-const pointer implies this. */ - track(r, from, true); - if (r->is_frozen) { - refgroup(r->group); - } else { - merge((upb_refcounted*)r, from); - } -} - -void upb_refcounted_unref2(const upb_refcounted *r, upb_refcounted *from) { - UPB_ASSERT(!from->is_frozen); /* Non-const pointer implies this. */ - untrack(r, from, true); - if (r->is_frozen) { - unref(r); - } else { - UPB_ASSERT(merged(r, from)); - } -} - -void upb_refcounted_donateref( - const upb_refcounted *r, const void *from, const void *to) { - UPB_ASSERT(from != to); - if (to != NULL) - upb_refcounted_ref(r, to); - if (from != NULL) - upb_refcounted_unref(r, from); -} - -void upb_refcounted_checkref(const upb_refcounted *r, const void *owner) { - checkref(r, owner, false); -} - -bool upb_refcounted_freeze(upb_refcounted *const*roots, int n, upb_status *s, - int maxdepth) { - int i; - bool ret; - for (i = 0; i < n; i++) { - UPB_ASSERT(!roots[i]->is_frozen); - } - ret = freeze(roots, n, s, maxdepth); - UPB_ASSERT(!s || ret == upb_ok(s)); - return ret; -} diff --git a/upb/refcounted.h b/upb/refcounted.h deleted file mode 100644 index 6698d38..0000000 --- a/upb/refcounted.h +++ /dev/null @@ -1,348 +0,0 @@ -/* -** upb::RefCounted (upb_refcounted) -** -** A refcounting scheme that supports circular refs. It accomplishes this by -** partitioning the set of objects into groups such that no cycle spans groups; -** we can then reference-count the group as a whole and ignore refs within the -** group. When objects are mutable, these groups are computed very -** conservatively; we group any objects that have ever had a link between them. -** When objects are frozen, we compute strongly-connected components which -** allows us to be precise and only group objects that are actually cyclic. -** -** This is a mixed C/C++ interface that offers a full API to both languages. -** See the top-level README for more information. -*/ - -#ifndef UPB_REFCOUNTED_H_ -#define UPB_REFCOUNTED_H_ - -#include "upb/table.int.h" - -/* Reference tracking will check ref()/unref() operations to make sure the - * ref ownership is correct. Where possible it will also make tools like - * Valgrind attribute ref leaks to the code that took the leaked ref, not - * the code that originally created the object. - * - * Enabling this requires the application to define upb_lock()/upb_unlock() - * functions that acquire/release a global mutex (or #define UPB_THREAD_UNSAFE). - * For this reason we don't enable it by default, even in debug builds. - */ - -/* #define UPB_DEBUG_REFS */ - -#ifdef __cplusplus -namespace upb { -class RefCounted; -template class reffed_ptr; -} -#endif - -UPB_DECLARE_TYPE(upb::RefCounted, upb_refcounted) - -struct upb_refcounted_vtbl; - -#ifdef __cplusplus - -class upb::RefCounted { - public: - /* Returns true if the given object is frozen. */ - bool IsFrozen() const; - - /* Increases the ref count, the new ref is owned by "owner" which must not - * already own a ref (and should not itself be a refcounted object if the ref - * could possibly be circular; see below). - * Thread-safe iff "this" is frozen. */ - void Ref(const void *owner) const; - - /* Release a ref that was acquired from upb_refcounted_ref() and collects any - * objects it can. */ - void Unref(const void *owner) const; - - /* Moves an existing ref from "from" to "to", without changing the overall - * ref count. DonateRef(foo, NULL, owner) is the same as Ref(foo, owner), - * but "to" may not be NULL. */ - void DonateRef(const void *from, const void *to) const; - - /* Verifies that a ref to the given object is currently held by the given - * owner. Only effective in UPB_DEBUG_REFS builds. */ - void CheckRef(const void *owner) const; - - private: - UPB_DISALLOW_POD_OPS(RefCounted, upb::RefCounted) -#else -struct upb_refcounted { -#endif - /* TODO(haberman): move the actual structure definition to structdefs.int.h. - * The only reason they are here is because inline functions need to see the - * definition of upb_handlers, which needs to see this definition. But we - * can change the upb_handlers inline functions to deal in raw offsets - * instead. - */ - - /* A single reference count shared by all objects in the group. */ - uint32_t *group; - - /* A singly-linked list of all objects in the group. */ - upb_refcounted *next; - - /* Table of function pointers for this type. */ - const struct upb_refcounted_vtbl *vtbl; - - /* Maintained only when mutable, this tracks the number of refs (but not - * ref2's) to this object. *group should be the sum of all individual_count - * in the group. */ - uint32_t individual_count; - - bool is_frozen; - -#ifdef UPB_DEBUG_REFS - upb_inttable *refs; /* Maps owner -> trackedref for incoming refs. */ - upb_inttable *ref2s; /* Set of targets for outgoing ref2s. */ -#endif -}; - -#ifdef UPB_DEBUG_REFS -extern upb_alloc upb_alloc_debugrefs; -#define UPB_REFCOUNT_INIT(vtbl, refs, ref2s) \ - {&static_refcount, NULL, vtbl, 0, true, refs, ref2s} -#else -#define UPB_REFCOUNT_INIT(vtbl, refs, ref2s) \ - {&static_refcount, NULL, vtbl, 0, true} -#endif - -UPB_BEGIN_EXTERN_C - -/* It is better to use tracked refs when possible, for the extra debugging - * capability. But if this is not possible (because you don't have easy access - * to a stable pointer value that is associated with the ref), you can pass - * UPB_UNTRACKED_REF instead. */ -extern const void *UPB_UNTRACKED_REF; - -/* Native C API. */ -bool upb_refcounted_isfrozen(const upb_refcounted *r); -void upb_refcounted_ref(const upb_refcounted *r, const void *owner); -void upb_refcounted_unref(const upb_refcounted *r, const void *owner); -void upb_refcounted_donateref( - const upb_refcounted *r, const void *from, const void *to); -void upb_refcounted_checkref(const upb_refcounted *r, const void *owner); - -#define UPB_REFCOUNTED_CMETHODS(type, upcastfunc) \ - UPB_INLINE bool type ## _isfrozen(const type *v) { \ - return upb_refcounted_isfrozen(upcastfunc(v)); \ - } \ - UPB_INLINE void type ## _ref(const type *v, const void *owner) { \ - upb_refcounted_ref(upcastfunc(v), owner); \ - } \ - UPB_INLINE void type ## _unref(const type *v, const void *owner) { \ - upb_refcounted_unref(upcastfunc(v), owner); \ - } \ - UPB_INLINE void type ## _donateref(const type *v, const void *from, const void *to) { \ - upb_refcounted_donateref(upcastfunc(v), from, to); \ - } \ - UPB_INLINE void type ## _checkref(const type *v, const void *owner) { \ - upb_refcounted_checkref(upcastfunc(v), owner); \ - } - -#define UPB_REFCOUNTED_CPPMETHODS \ - bool IsFrozen() const { \ - return upb::upcast_to(this)->IsFrozen(); \ - } \ - void Ref(const void *owner) const { \ - return upb::upcast_to(this)->Ref(owner); \ - } \ - void Unref(const void *owner) const { \ - return upb::upcast_to(this)->Unref(owner); \ - } \ - void DonateRef(const void *from, const void *to) const { \ - return upb::upcast_to(this)->DonateRef(from, to); \ - } \ - void CheckRef(const void *owner) const { \ - return upb::upcast_to(this)->CheckRef(owner); \ - } - -/* Internal-to-upb Interface **************************************************/ - -typedef void upb_refcounted_visit(const upb_refcounted *r, - const upb_refcounted *subobj, - void *closure); - -struct upb_refcounted_vtbl { - /* Must visit all subobjects that are currently ref'd via upb_refcounted_ref2. - * Must be longjmp()-safe. */ - void (*visit)(const upb_refcounted *r, upb_refcounted_visit *visit, void *c); - - /* Must free the object and release all references to other objects. */ - void (*free)(upb_refcounted *r); -}; - -/* Initializes the refcounted with a single ref for the given owner. Returns - * false if memory could not be allocated. */ -bool upb_refcounted_init(upb_refcounted *r, - const struct upb_refcounted_vtbl *vtbl, - const void *owner); - -/* Adds a ref from one refcounted object to another ("from" must not already - * own a ref). These refs may be circular; cycles will be collected correctly - * (if conservatively). These refs do not need to be freed in from's free() - * function. */ -void upb_refcounted_ref2(const upb_refcounted *r, upb_refcounted *from); - -/* Removes a ref that was acquired from upb_refcounted_ref2(), and collects any - * object it can. This is only necessary when "from" no longer points to "r", - * and not from from's "free" function. */ -void upb_refcounted_unref2(const upb_refcounted *r, upb_refcounted *from); - -#define upb_ref2(r, from) \ - upb_refcounted_ref2((const upb_refcounted*)r, (upb_refcounted*)from) -#define upb_unref2(r, from) \ - upb_refcounted_unref2((const upb_refcounted*)r, (upb_refcounted*)from) - -/* Freezes all mutable object reachable by ref2() refs from the given roots. - * This will split refcounting groups into precise SCC groups, so that - * refcounting of frozen objects can be more aggressive. If memory allocation - * fails, or if more than 2**31 mutable objects are reachable from "roots", or - * if the maximum depth of the graph exceeds "maxdepth", false is returned and - * the objects are unchanged. - * - * After this operation succeeds, the objects are frozen/const, and may not be - * used through non-const pointers. In particular, they may not be passed as - * the second parameter of upb_refcounted_{ref,unref}2(). On the upside, all - * operations on frozen refcounteds are threadsafe, and objects will be freed - * at the precise moment that they become unreachable. - * - * Caller must own refs on each object in the "roots" list. */ -bool upb_refcounted_freeze(upb_refcounted *const*roots, int n, upb_status *s, - int maxdepth); - -/* Shared by all compiled-in refcounted objects. */ -extern uint32_t static_refcount; - -UPB_END_EXTERN_C - -#ifdef __cplusplus -/* C++ Wrappers. */ -namespace upb { -inline bool RefCounted::IsFrozen() const { - return upb_refcounted_isfrozen(this); -} -inline void RefCounted::Ref(const void *owner) const { - upb_refcounted_ref(this, owner); -} -inline void RefCounted::Unref(const void *owner) const { - upb_refcounted_unref(this, owner); -} -inline void RefCounted::DonateRef(const void *from, const void *to) const { - upb_refcounted_donateref(this, from, to); -} -inline void RefCounted::CheckRef(const void *owner) const { - upb_refcounted_checkref(this, owner); -} -} /* namespace upb */ -#endif - - -/* upb::reffed_ptr ************************************************************/ - -#ifdef __cplusplus - -#include /* For std::swap(). */ - -/* Provides RAII semantics for upb refcounted objects. Each reffed_ptr owns a - * ref on whatever object it points to (if any). */ -template class upb::reffed_ptr { - public: - reffed_ptr() : ptr_(NULL) {} - - /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */ - template - reffed_ptr(U* val, const void* ref_donor = NULL) - : ptr_(upb::upcast(val)) { - if (ref_donor) { - UPB_ASSERT(ptr_); - ptr_->DonateRef(ref_donor, this); - } else if (ptr_) { - ptr_->Ref(this); - } - } - - template - reffed_ptr(const reffed_ptr& other) - : ptr_(upb::upcast(other.get())) { - if (ptr_) ptr_->Ref(this); - } - - reffed_ptr(const reffed_ptr& other) - : ptr_(upb::upcast(other.get())) { - if (ptr_) ptr_->Ref(this); - } - - ~reffed_ptr() { if (ptr_) ptr_->Unref(this); } - - template - reffed_ptr& operator=(const reffed_ptr& other) { - reset(other.get()); - return *this; - } - - reffed_ptr& operator=(const reffed_ptr& other) { - reset(other.get()); - return *this; - } - - /* TODO(haberman): add C++11 move construction/assignment for greater - * efficiency. */ - - void swap(reffed_ptr& other) { - if (ptr_ == other.ptr_) { - return; - } - - if (ptr_) ptr_->DonateRef(this, &other); - if (other.ptr_) other.ptr_->DonateRef(&other, this); - std::swap(ptr_, other.ptr_); - } - - T& operator*() const { - UPB_ASSERT(ptr_); - return *ptr_; - } - - T* operator->() const { - UPB_ASSERT(ptr_); - return ptr_; - } - - T* get() const { return ptr_; } - - /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */ - template - void reset(U* ptr = NULL, const void* ref_donor = NULL) { - reffed_ptr(ptr, ref_donor).swap(*this); - } - - template - reffed_ptr down_cast() { - return reffed_ptr(upb::down_cast(get())); - } - - template - reffed_ptr dyn_cast() { - return reffed_ptr(upb::dyn_cast(get())); - } - - /* Plain release() is unsafe; if we were the only owner, it would leak the - * object. Instead we provide this: */ - T* ReleaseTo(const void* new_owner) { - T* ret = NULL; - ptr_->DonateRef(this, new_owner); - std::swap(ret, ptr_); - return ret; - } - - private: - T* ptr_; -}; - -#endif /* __cplusplus */ - -#endif /* UPB_REFCOUNT_H_ */ -- cgit v1.2.3