summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Haberman <jhaberman@gmail.com>2019-01-09 22:40:50 -0800
committerJoshua Haberman <jhaberman@gmail.com>2019-01-09 22:40:50 -0800
commit0553eff64a87eceff0de3b6260b4f2d45b61703a (patch)
tree5b051740c041786655d1794a5034ccf20928e8ee
parent31e0997c1abaa531505d28e36473f1c972ca0849 (diff)
upb_refcounted is gone! Some tests still to fix.
-rw-r--r--BUILD49
-rw-r--r--tests/json/test_json.cc23
-rw-r--r--tests/pb/test_encoder.cc22
-rw-r--r--upb/bindings/lua/def.c18
-rw-r--r--upb/bindings/lua/upb.h7
-rw-r--r--upb/handlers-inl.h34
-rw-r--r--upb/handlers.c366
-rw-r--r--upb/handlers.h84
-rw-r--r--upb/json/parser.c320
-rw-r--r--upb/json/parser.h50
-rw-r--r--upb/json/parser.rl214
-rw-r--r--upb/json/printer.c17
-rw-r--r--upb/json/printer.h17
-rw-r--r--upb/msgfactory.c9
-rw-r--r--upb/pb/compile_decoder.c176
-rw-r--r--upb/pb/decoder.h128
-rw-r--r--upb/pb/decoder.int.h63
-rw-r--r--upb/pb/encoder.c5
-rw-r--r--upb/pb/encoder.h12
-rw-r--r--upb/pb/textprinter.c5
-rw-r--r--upb/pb/textprinter.h11
-rw-r--r--upb/refcounted.c851
-rw-r--r--upb/refcounted.h348
23 files changed, 666 insertions, 2163 deletions
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<const upb::Handlers> serialize_handlers(
- upb::json::Printer::NewHandlers(md, false));
- upb::reffed_ptr<const upb::json::ParserMethod> 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<const upb::Handlers> encoder_handlers(
- upb::pb::Encoder::NewHandlers(md));
- upb::reffed_ptr<const upb::pb::DecoderMethod> 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> Handlers::New(const MessageDef *m) {
- upb_handlers *h = upb_handlers_new(m, &h);
- return reffed_ptr<Handlers>(h, &h);
-}
-inline reffed_ptr<const Handlers> 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<const Handlers>(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<Handlers*>& 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 T> class Handler;
template <class T> struct CanonicalType;
} /* namespace upb */
@@ -37,8 +37,8 @@ template <class T> 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<Handlers> 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<const Handlers> 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*>& 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<const ParserMethod> 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<const ParserMethod> ParserMethod::New(
- const MessageDef* md) {
- const upb_json_parsermethod *m = upb_json_parsermethod_new(md, &m);
- return reffed_ptr<const ParserMethod>(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<const ParserMethod> 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<const Handlers> 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<const Handlers> 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<const Handlers>(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<const DecoderMethod> 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<const DecoderMethod> DecoderMethod::New(
- const DecoderMethodOptions &opts) {
- const upb_pbdecodermethod *m = upb_pbdecodermethod_new(&opts, &m);
- return reffed_ptr<const DecoderMethod>(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<const Handlers> 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<const Handlers> Encoder::NewHandlers(
- const upb::MessageDef *md) {
- const Handlers* h = upb_pb_encoder_newhandlers(md, &h);
- return reffed_ptr<const Handlers>(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<const Handlers> 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<const Handlers> TextPrinter::NewHandlers(
- const MessageDef *md) {
- const Handlers* h = upb_textprinter_newhandlers(md, &h);
- return reffed_ptr<const Handlers>(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 <setjmp.h>
-
-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 <Windows.h>
-
-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 <stdlib.h>
-
-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 T> 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<const upb::RefCounted>(this)->IsFrozen(); \
- } \
- void Ref(const void *owner) const { \
- return upb::upcast_to<const upb::RefCounted>(this)->Ref(owner); \
- } \
- void Unref(const void *owner) const { \
- return upb::upcast_to<const upb::RefCounted>(this)->Unref(owner); \
- } \
- void DonateRef(const void *from, const void *to) const { \
- return upb::upcast_to<const upb::RefCounted>(this)->DonateRef(from, to); \
- } \
- void CheckRef(const void *owner) const { \
- return upb::upcast_to<const upb::RefCounted>(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 <algorithm> /* 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 T> class upb::reffed_ptr {
- public:
- reffed_ptr() : ptr_(NULL) {}
-
- /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */
- template <class U>
- 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 <class U>
- reffed_ptr(const reffed_ptr<U>& 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 <class U>
- reffed_ptr& operator=(const reffed_ptr<U>& 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 <class U>
- void reset(U* ptr = NULL, const void* ref_donor = NULL) {
- reffed_ptr(ptr, ref_donor).swap(*this);
- }
-
- template <class U>
- reffed_ptr<U> down_cast() {
- return reffed_ptr<U>(upb::down_cast<U*>(get()));
- }
-
- template <class U>
- reffed_ptr<U> dyn_cast() {
- return reffed_ptr<U>(upb::dyn_cast<U*>(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_ */
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback