Merge pull request #363 from haberman/delete-handlers
Deleted the legacy "Handlers" APIs. upb can finally be deserving of its name.pull/13171/head
commit
ed5b4108e0
38 changed files with 15 additions and 17846 deletions
@ -1,195 +0,0 @@ |
||||
package( |
||||
default_visibility = ["//visibility:public"], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "ragelc", |
||||
srcs = [ |
||||
"ragel/rubycodegen.cpp", |
||||
"ragel/goipgoto.h", |
||||
"ragel/cdtable.h", |
||||
"ragel/rubycodegen.h", |
||||
"ragel/gotable.h", |
||||
"ragel/gocodegen.cpp", |
||||
"ragel/rubyfflat.cpp", |
||||
"ragel/common.cpp", |
||||
"ragel/gofflat.cpp", |
||||
"ragel/cdtable.cpp", |
||||
"ragel/cdsplit.cpp", |
||||
"ragel/rlparse.cpp", |
||||
"ragel/csfgoto.cpp", |
||||
"ragel/javacodegen.cpp", |
||||
"ragel/gocodegen.h", |
||||
"ragel/mlgoto.cpp", |
||||
"ragel/fsmgraph.cpp", |
||||
"ragel/version.h", |
||||
"ragel/mlfflat.h", |
||||
"ragel/fsmgraph.h", |
||||
"ragel/fsmbase.cpp", |
||||
"ragel/fsmstate.cpp", |
||||
"ragel/gotablish.cpp", |
||||
"ragel/rubyflat.cpp", |
||||
"ragel/cdfgoto.h", |
||||
"ragel/cscodegen.h", |
||||
"ragel/mlflat.cpp", |
||||
"ragel/rubyflat.h", |
||||
"ragel/goftable.h", |
||||
"ragel/rbxgoto.cpp", |
||||
"ragel/csfflat.cpp", |
||||
"ragel/gofgoto.cpp", |
||||
"ragel/gofgoto.h", |
||||
"ragel/ragel.h", |
||||
"ragel/goftable.cpp", |
||||
"ragel/cdcodegen.cpp", |
||||
"ragel/rlparse.h", |
||||
"ragel/cdsplit.h", |
||||
"ragel/xmlcodegen.cpp", |
||||
"ragel/goipgoto.cpp", |
||||
"ragel/dotcodegen.h", |
||||
"ragel/gogoto.cpp", |
||||
"ragel/csflat.h", |
||||
"ragel/csfflat.h", |
||||
#"ragel/config.h.in", |
||||
"ragel/csipgoto.cpp", |
||||
"ragel/mltable.cpp", |
||||
"ragel/mlflat.h", |
||||
"ragel/csftable.cpp", |
||||
"ragel/cdgoto.h", |
||||
"ragel/goflat.cpp", |
||||
"ragel/rubyfflat.h", |
||||
"ragel/mlftable.h", |
||||
"ragel/rubyftable.h", |
||||
"ragel/fsmap.cpp", |
||||
"ragel/redfsm.cpp", |
||||
"ragel/goflat.h", |
||||
"ragel/parsetree.cpp", |
||||
"ragel/fsmmin.cpp", |
||||
"ragel/dotcodegen.cpp", |
||||
"ragel/redfsm.h", |
||||
"ragel/mlcodegen.cpp", |
||||
"ragel/cdfgoto.cpp", |
||||
"ragel/cssplit.cpp", |
||||
"ragel/cstable.cpp", |
||||
"ragel/javacodegen.h", |
||||
"ragel/parsedata.cpp", |
||||
"ragel/buffer.h", |
||||
"ragel/gogoto.h", |
||||
"ragel/csgoto.h", |
||||
"ragel/pcheck.h", |
||||
"ragel/rubyftable.cpp", |
||||
"ragel/csfgoto.h", |
||||
"ragel/common.h", |
||||
"ragel/cdftable.h", |
||||
"ragel/mlgoto.h", |
||||
"ragel/csgoto.cpp", |
||||
"ragel/cdflat.h", |
||||
"ragel/cdipgoto.h", |
||||
"ragel/cstable.h", |
||||
"ragel/gendata.h", |
||||
"ragel/cdfflat.cpp", |
||||
"ragel/gotable.cpp", |
||||
"ragel/cdcodegen.h", |
||||
"ragel/gendata.cpp", |
||||
"ragel/rubytable.h", |
||||
"ragel/csflat.cpp", |
||||
"ragel/inputdata.h", |
||||
"ragel/inputdata.cpp", |
||||
"ragel/rubytable.cpp", |
||||
"ragel/fsmattach.cpp", |
||||
"ragel/csipgoto.h", |
||||
"ragel/cscodegen.cpp", |
||||
"ragel/cdfflat.h", |
||||
"ragel/rbxgoto.h", |
||||
"ragel/xmlcodegen.h", |
||||
"ragel/gofflat.h", |
||||
"ragel/parsedata.h", |
||||
"ragel/mlfgoto.h", |
||||
"ragel/cdflat.cpp", |
||||
"ragel/config.h", |
||||
"ragel/rlscan.cpp", |
||||
"ragel/mlcodegen.h", |
||||
"ragel/mlfflat.cpp", |
||||
"ragel/mlftable.cpp", |
||||
"ragel/mltable.h", |
||||
"ragel/cdipgoto.cpp", |
||||
"ragel/cdftable.cpp", |
||||
"ragel/parsetree.h", |
||||
"ragel/rlscan.h", |
||||
"ragel/main.cpp", |
||||
"ragel/cssplit.h", |
||||
"ragel/mlfgoto.cpp", |
||||
"ragel/csftable.h", |
||||
"ragel/gotablish.h", |
||||
"ragel/cdgoto.cpp", |
||||
"aapl/avlmelkey.h", |
||||
"aapl/dlistmel.h", |
||||
"aapl/avliset.h", |
||||
"aapl/avlkeyless.h", |
||||
"aapl/sbstset.h", |
||||
"aapl/sbsttable.h", |
||||
"aapl/quicksort.h", |
||||
"aapl/avlitree.h", |
||||
"aapl/avlcommon.h", |
||||
"aapl/bstset.h", |
||||
"aapl/avlmel.h", |
||||
"aapl/insertsort.h", |
||||
"aapl/dlist.h", |
||||
"aapl/avlmap.h", |
||||
"aapl/mergesort.h", |
||||
"aapl/resize.h", |
||||
"aapl/bstcommon.h", |
||||
"aapl/bstmap.h", |
||||
"aapl/compare.h", |
||||
"aapl/svector.h", |
||||
"aapl/avlset.h", |
||||
"aapl/bsttable.h", |
||||
"aapl/avlikeyless.h", |
||||
"aapl/bubblesort.h", |
||||
"aapl/table.h", |
||||
"aapl/avlbasic.h", |
||||
"aapl/vector.h", |
||||
"aapl/avlimap.h", |
||||
"aapl/dlistval.h", |
||||
"aapl/dlcommon.h", |
||||
"aapl/avlibasic.h", |
||||
"aapl/sbstmap.h", |
||||
"aapl/avlimel.h", |
||||
"aapl/avlimelkey.h", |
||||
"aapl/avltree.h", |
||||
], |
||||
includes = [ |
||||
"aapl", |
||||
"ragel", |
||||
], |
||||
) |
||||
|
||||
config_h_contents = """ |
||||
#define PACKAGE "ragel" |
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */ |
||||
#define PACKAGE_BUGREPORT "" |
||||
|
||||
/* Define to the full name of this package. */ |
||||
#define PACKAGE_NAME "ragel" |
||||
|
||||
/* Define to the full name and version of this package. */ |
||||
#define PACKAGE_STRING "ragel 6.10" |
||||
|
||||
/* Define to the one symbol short name of this package. */ |
||||
#define PACKAGE_TARNAME "ragel" |
||||
|
||||
/* Define to the home page for this package. */ |
||||
#define PACKAGE_URL "" |
||||
|
||||
/* Define to the version of this package. */ |
||||
#define PACKAGE_VERSION "6.10" |
||||
|
||||
/* Version number of package */ |
||||
#define VERSION "6.10" |
||||
""" |
||||
|
||||
genrule( |
||||
name = "gen_config_h", |
||||
outs = ["ragel/config.h"], |
||||
cmd = "(cat <<'HEREDOC'\n%s\nHEREDOC\n) > $@" % config_h_contents, |
||||
) |
File diff suppressed because it is too large
Load Diff
@ -1,9 +0,0 @@ |
||||
syntax = "proto2"; |
||||
|
||||
import "tests/json/test.proto"; |
||||
|
||||
package upb.test.json; |
||||
|
||||
message ImportEnum { |
||||
optional MyEnum e = 1; |
||||
} |
@ -1,47 +0,0 @@ |
||||
syntax = "proto3"; |
||||
|
||||
package upb.test.json; |
||||
|
||||
message TestMessage { |
||||
int32 optional_int32 = 1; |
||||
int64 optional_int64 = 2; |
||||
int32 optional_uint32 = 3; |
||||
int64 optional_uint64 = 4; |
||||
string optional_string = 5; |
||||
bytes optional_bytes = 6; |
||||
bool optional_bool = 7; |
||||
SubMessage optional_msg = 8; |
||||
MyEnum optional_enum = 9; |
||||
|
||||
repeated int32 repeated_int32 = 11; |
||||
repeated int64 repeated_int64 = 12; |
||||
repeated uint32 repeated_uint32 = 13; |
||||
repeated uint64 repeated_uint64 = 14; |
||||
repeated string repeated_string = 15; |
||||
repeated bytes repeated_bytes = 16; |
||||
repeated bool repeated_bool = 17; |
||||
repeated SubMessage repeated_msg = 18; |
||||
repeated MyEnum repeated_enum = 19; |
||||
|
||||
map<string, string> map_string_string = 20; |
||||
map<int32, string> map_int32_string = 21; |
||||
map<bool, string> map_bool_string = 22; |
||||
map<string, int32> map_string_int32 = 23; |
||||
map<string, bool> map_string_bool = 24; |
||||
map<string, SubMessage> map_string_msg = 25; |
||||
|
||||
oneof o { |
||||
int32 oneof_int32 = 26; |
||||
int64 oneof_int64 = 27; |
||||
} |
||||
} |
||||
|
||||
message SubMessage { |
||||
int32 foo = 1; |
||||
} |
||||
|
||||
enum MyEnum { |
||||
A = 0; |
||||
B = 1; |
||||
C = 2; |
||||
} |
Binary file not shown.
@ -1,336 +0,0 @@ |
||||
/*
|
||||
* |
||||
* A set of tests for JSON parsing and serialization. |
||||
*/ |
||||
|
||||
#include <string> |
||||
|
||||
#include "tests/json/test.upb.h" // Test that it compiles for C++. |
||||
#include "tests/json/test.upbdefs.h" |
||||
#include "tests/test_util.h" |
||||
#include "tests/upb_test.h" |
||||
#include "upb/def.hpp" |
||||
#include "upb/handlers.h" |
||||
#include "upb/json/parser.h" |
||||
#include "upb/json/printer.h" |
||||
#include "upb/port_def.inc" |
||||
#include "upb/upb.h" |
||||
|
||||
// Macros for readability in test case list: allows us to give TEST("...") /
|
||||
// EXPECT("...") pairs.
|
||||
#define TEST(x) x |
||||
#define EXPECT_SAME NULL |
||||
#define EXPECT(x) x |
||||
#define TEST_SENTINEL { NULL, NULL } |
||||
|
||||
struct TestCase { |
||||
const char* input; |
||||
const char* expected; |
||||
}; |
||||
|
||||
bool verbose = false; |
||||
|
||||
static TestCase kTestRoundtripMessages[] = { |
||||
// Test most fields here.
|
||||
{ |
||||
TEST("{\"optionalInt32\":-42,\"optionalString\":\"Test\\u0001Message\"," |
||||
"\"optionalMsg\":{\"foo\":42}," |
||||
"\"optionalBool\":true,\"repeatedMsg\":[{\"foo\":1}," |
||||
"{\"foo\":2}]}"), |
||||
EXPECT_SAME |
||||
}, |
||||
// We must also recognize raw proto names.
|
||||
{ |
||||
TEST("{\"optional_int32\":-42,\"optional_string\":\"Test\\u0001Message\"," |
||||
"\"optional_msg\":{\"foo\":42}," |
||||
"\"optional_bool\":true,\"repeated_msg\":[{\"foo\":1}," |
||||
"{\"foo\":2}]}"), |
||||
EXPECT("{\"optionalInt32\":-42,\"optionalString\":\"Test\\u0001Message\"," |
||||
"\"optionalMsg\":{\"foo\":42}," |
||||
"\"optionalBool\":true,\"repeatedMsg\":[{\"foo\":1}," |
||||
"{\"foo\":2}]}") |
||||
}, |
||||
// Test special escapes in strings.
|
||||
{ |
||||
TEST("{\"repeatedString\":[\"\\b\",\"\\r\",\"\\n\",\"\\f\",\"\\t\"," |
||||
"\"\uFFFF\"]}"), |
||||
EXPECT_SAME |
||||
}, |
||||
// Test enum symbolic names.
|
||||
{ |
||||
// The common case: parse and print the symbolic name.
|
||||
TEST("{\"optionalEnum\":\"A\"}"), |
||||
EXPECT_SAME |
||||
}, |
||||
{ |
||||
// Unknown enum value: will be printed as an integer.
|
||||
TEST("{\"optionalEnum\":42}"), |
||||
EXPECT_SAME |
||||
}, |
||||
{ |
||||
// Known enum value: we're happy to parse an integer but we will re-emit the
|
||||
// symbolic name.
|
||||
TEST("{\"optionalEnum\":1}"), |
||||
EXPECT("{\"optionalEnum\":\"B\"}") |
||||
}, |
||||
// UTF-8 tests: escapes -> literal UTF8 in output.
|
||||
{ |
||||
// Note double escape on \uXXXX: we want the escape to be processed by the
|
||||
// JSON parser, not by the C++ compiler!
|
||||
TEST("{\"optionalString\":\"\\u007F\"}"), |
||||
EXPECT("{\"optionalString\":\"\x7F\"}") |
||||
}, |
||||
{ |
||||
TEST("{\"optionalString\":\"\\u0080\"}"), |
||||
EXPECT("{\"optionalString\":\"\xC2\x80\"}") |
||||
}, |
||||
{ |
||||
TEST("{\"optionalString\":\"\\u07FF\"}"), |
||||
EXPECT("{\"optionalString\":\"\xDF\xBF\"}") |
||||
}, |
||||
{ |
||||
TEST("{\"optionalString\":\"\\u0800\"}"), |
||||
EXPECT("{\"optionalString\":\"\xE0\xA0\x80\"}") |
||||
}, |
||||
{ |
||||
TEST("{\"optionalString\":\"\\uFFFF\"}"), |
||||
EXPECT("{\"optionalString\":\"\xEF\xBF\xBF\"}") |
||||
}, |
||||
// map-field tests
|
||||
{ |
||||
TEST("{\"mapStringString\":{\"a\":\"value1\",\"b\":\"value2\"," |
||||
"\"c\":\"value3\"}}"), |
||||
EXPECT_SAME |
||||
}, |
||||
{ |
||||
TEST("{\"mapInt32String\":{\"1\":\"value1\",\"-1\":\"value2\"," |
||||
"\"1234\":\"value3\"}}"), |
||||
EXPECT_SAME |
||||
}, |
||||
{ |
||||
TEST("{\"mapBoolString\":{\"false\":\"value1\",\"true\":\"value2\"}}"), |
||||
EXPECT_SAME |
||||
}, |
||||
{ |
||||
TEST("{\"mapStringInt32\":{\"asdf\":1234,\"jkl;\":-1}}"), |
||||
EXPECT_SAME |
||||
}, |
||||
{ |
||||
TEST("{\"mapStringBool\":{\"asdf\":true,\"jkl;\":false}}"), |
||||
EXPECT_SAME |
||||
}, |
||||
{ |
||||
TEST("{\"mapStringMsg\":{\"asdf\":{\"foo\":42},\"jkl;\":{\"foo\":84}}}"), |
||||
EXPECT_SAME |
||||
}, |
||||
TEST_SENTINEL |
||||
}; |
||||
|
||||
static TestCase kTestRoundtripMessagesPreserve[] = { |
||||
// Test most fields here.
|
||||
{ |
||||
TEST("{\"optional_int32\":-42,\"optional_string\":\"Test\\u0001Message\"," |
||||
"\"optional_msg\":{\"foo\":42}," |
||||
"\"optional_bool\":true,\"repeated_msg\":[{\"foo\":1}," |
||||
"{\"foo\":2}]}"), |
||||
EXPECT_SAME |
||||
}, |
||||
TEST_SENTINEL |
||||
}; |
||||
|
||||
static TestCase kTestSkipUnknown[] = { |
||||
{ |
||||
TEST("{\"optionalEnum\":\"UNKNOWN_ENUM_VALUE\"}"), |
||||
EXPECT("{}"), |
||||
}, |
||||
TEST_SENTINEL |
||||
}; |
||||
|
||||
static TestCase kTestFailure[] = { |
||||
{ |
||||
TEST("{\"optionalEnum\":\"UNKNOWN_ENUM_VALUE\"}"), |
||||
EXPECT("{}"), /* Actually we expect error, this is checked later. */ |
||||
}, |
||||
TEST_SENTINEL |
||||
}; |
||||
|
||||
class StringSink { |
||||
public: |
||||
StringSink() { |
||||
upb_byteshandler_init(&byteshandler_); |
||||
upb_byteshandler_setstring(&byteshandler_, &str_handler, NULL); |
||||
upb_bytessink_reset(&bytessink_, &byteshandler_, &s_); |
||||
} |
||||
~StringSink() { } |
||||
|
||||
upb_bytessink Sink() { return bytessink_; } |
||||
|
||||
const std::string& Data() { return s_; } |
||||
|
||||
private: |
||||
|
||||
static size_t str_handler(void* _closure, const void* hd, |
||||
const char* data, size_t len, |
||||
const upb_bufhandle* handle) { |
||||
UPB_UNUSED(hd); |
||||
UPB_UNUSED(handle); |
||||
std::string* s = static_cast<std::string*>(_closure); |
||||
std::string appended(data, len); |
||||
s->append(data, len); |
||||
return len; |
||||
} |
||||
|
||||
upb_byteshandler byteshandler_; |
||||
upb_bytessink bytessink_; |
||||
std::string s_; |
||||
}; |
||||
|
||||
void test_json_roundtrip_message(const char* json_src, |
||||
const char* json_expected, |
||||
const upb::Handlers* serialize_handlers, |
||||
const upb::json::ParserMethodPtr parser_method, |
||||
int seam, |
||||
bool ignore_unknown) { |
||||
VerboseParserEnvironment env(verbose); |
||||
StringSink data_sink; |
||||
upb::json::PrinterPtr printer = upb::json::PrinterPtr::Create( |
||||
env.arena(), serialize_handlers, data_sink.Sink()); |
||||
upb::json::ParserPtr parser = upb::json::ParserPtr::Create( |
||||
env.arena(), parser_method, NULL, printer.input(), |
||||
env.status(), ignore_unknown); |
||||
env.ResetBytesSink(parser.input()); |
||||
env.Reset(json_src, strlen(json_src), false, false); |
||||
|
||||
bool ok = env.Start() && |
||||
env.ParseBuffer(seam) && |
||||
env.ParseBuffer(-1) && |
||||
env.End(); |
||||
|
||||
ASSERT(ok); |
||||
ASSERT(env.CheckConsistency()); |
||||
|
||||
if (memcmp(json_expected, |
||||
data_sink.Data().data(), |
||||
data_sink.Data().size())) { |
||||
fprintf(stderr, |
||||
"JSON parse/serialize roundtrip result differs:\n" |
||||
"Expected:\n%s\nParsed/Serialized:\n%s\n", |
||||
json_expected, data_sink.Data().c_str()); |
||||
abort(); |
||||
} |
||||
} |
||||
|
||||
// Starts with a message in JSON format, parses and directly serializes again,
|
||||
// and compares the result.
|
||||
void test_json_roundtrip() { |
||||
upb::SymbolTable symtab; |
||||
upb::HandlerCache serialize_handlercache( |
||||
upb::json::PrinterPtr::NewCache(false)); |
||||
upb::json::CodeCache parse_codecache; |
||||
|
||||
upb::MessageDefPtr md(upb_test_json_TestMessage_getmsgdef(symtab.ptr())); |
||||
ASSERT(md); |
||||
const upb::Handlers* serialize_handlers = serialize_handlercache.Get(md); |
||||
const upb::json::ParserMethodPtr parser_method = parse_codecache.Get(md); |
||||
ASSERT(serialize_handlers); |
||||
|
||||
for (const TestCase* test_case = kTestRoundtripMessages; |
||||
test_case->input != NULL; test_case++) { |
||||
const char *expected = |
||||
(test_case->expected == EXPECT_SAME) ? |
||||
test_case->input : |
||||
test_case->expected; |
||||
|
||||
for (size_t i = 0; i < strlen(test_case->input); i++) { |
||||
test_json_roundtrip_message(test_case->input, expected, |
||||
serialize_handlers, parser_method, (int)i, |
||||
false); |
||||
} |
||||
} |
||||
|
||||
// Tests ignore unknown.
|
||||
for (const TestCase* test_case = kTestSkipUnknown; |
||||
test_case->input != NULL; test_case++) { |
||||
const char *expected = |
||||
(test_case->expected == EXPECT_SAME) ? |
||||
test_case->input : |
||||
test_case->expected; |
||||
|
||||
for (size_t i = 0; i < strlen(test_case->input); i++) { |
||||
test_json_roundtrip_message(test_case->input, expected, |
||||
serialize_handlers, parser_method, (int)i, |
||||
true); |
||||
} |
||||
} |
||||
|
||||
serialize_handlercache = upb::json::PrinterPtr::NewCache(true); |
||||
serialize_handlers = serialize_handlercache.Get(md); |
||||
|
||||
for (const TestCase* test_case = kTestRoundtripMessagesPreserve; |
||||
test_case->input != NULL; test_case++) { |
||||
const char *expected = |
||||
(test_case->expected == EXPECT_SAME) ? |
||||
test_case->input : |
||||
test_case->expected; |
||||
|
||||
for (size_t i = 0; i < strlen(test_case->input); i++) { |
||||
test_json_roundtrip_message(test_case->input, expected, |
||||
serialize_handlers, parser_method, (int)i, |
||||
false); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void test_json_parse_failure(const char* json_src, |
||||
const upb::Handlers* serialize_handlers, |
||||
const upb::json::ParserMethodPtr parser_method, |
||||
int seam) { |
||||
VerboseParserEnvironment env(verbose); |
||||
StringSink data_sink; |
||||
upb::json::PrinterPtr printer = upb::json::PrinterPtr::Create( |
||||
env.arena(), serialize_handlers, data_sink.Sink()); |
||||
upb::json::ParserPtr parser = upb::json::ParserPtr::Create( |
||||
env.arena(), parser_method, NULL, printer.input(), env.status(), false); |
||||
env.ResetBytesSink(parser.input()); |
||||
env.Reset(json_src, strlen(json_src), false, true); |
||||
|
||||
bool ok = env.Start() && |
||||
env.ParseBuffer(seam) && |
||||
env.ParseBuffer(-1) && |
||||
env.End(); |
||||
|
||||
ASSERT(!ok); |
||||
ASSERT(env.CheckConsistency()); |
||||
} |
||||
|
||||
// Starts with a proto message in JSON format, parses and expects failre.
|
||||
void test_json_failure() { |
||||
upb::SymbolTable symtab; |
||||
upb::HandlerCache serialize_handlercache( |
||||
upb::json::PrinterPtr::NewCache(false)); |
||||
upb::json::CodeCache parse_codecache; |
||||
|
||||
upb::MessageDefPtr md(upb_test_json_TestMessage_getmsgdef(symtab.ptr())); |
||||
ASSERT(md); |
||||
const upb::Handlers* serialize_handlers = serialize_handlercache.Get(md); |
||||
const upb::json::ParserMethodPtr parser_method = parse_codecache.Get(md); |
||||
ASSERT(serialize_handlers); |
||||
|
||||
for (const TestCase* test_case = kTestFailure; |
||||
test_case->input != NULL; test_case++) { |
||||
for (size_t i = 0; i < strlen(test_case->input); i++) { |
||||
test_json_parse_failure(test_case->input, serialize_handlers, |
||||
parser_method, (int)i); |
||||
} |
||||
} |
||||
} |
||||
|
||||
extern "C" { |
||||
int run_tests(int argc, char *argv[]) { |
||||
UPB_UNUSED(argc); |
||||
UPB_UNUSED(argv); |
||||
test_json_roundtrip(); |
||||
test_json_failure(); |
||||
return 0; |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -1,128 +0,0 @@ |
||||
|
||||
syntax = "proto2"; |
||||
|
||||
enum TestEnum { |
||||
FOO = 1; |
||||
} |
||||
|
||||
message Empty {} |
||||
|
||||
message DecoderTest { |
||||
optional double f_double = 1; |
||||
optional float f_float = 2; |
||||
optional int64 f_int64 = 3; |
||||
optional uint64 f_uint64 = 4; |
||||
optional int32 f_int32 = 5; |
||||
optional fixed64 f_fixed64 = 6; |
||||
optional fixed32 f_fixed32 = 7; |
||||
optional bool f_bool = 8; |
||||
optional string f_string = 9; |
||||
optional DecoderTest f_message = 11; |
||||
optional bytes f_bytes = 12; |
||||
optional uint32 f_uint32 = 13; |
||||
optional TestEnum f_enum = 14; |
||||
optional sfixed32 f_sfixed32 = 15; |
||||
optional sfixed64 f_sfixed64 = 16; |
||||
optional sint32 f_sint32 = 17; |
||||
optional sint64 f_sint64 = 18; |
||||
|
||||
optional string nop_field = 40; |
||||
|
||||
repeated double r_double = 536869912; |
||||
repeated float r_float = 536869913; |
||||
repeated int64 r_int64 = 536869914; |
||||
repeated uint64 r_uint64 = 536869915; |
||||
repeated int32 r_int32 = 536869916; |
||||
repeated fixed64 r_fixed64 = 536869917; |
||||
repeated fixed32 r_fixed32 = 536869918; |
||||
repeated bool r_bool = 536869919; |
||||
repeated string r_string = 536869920; |
||||
repeated DecoderTest r_message = 536869922; |
||||
repeated bytes r_bytes = 536869923; |
||||
repeated uint32 r_uint32 = 536869924; |
||||
repeated TestEnum r_enum = 536869925; |
||||
repeated sfixed32 r_sfixed32 = 536869926; |
||||
repeated sfixed64 r_sfixed64 = 536869927; |
||||
repeated sint32 r_sint32 = 536869928; |
||||
repeated sint64 r_sint64 = 536869929; |
||||
|
||||
optional group F_group = 10 { |
||||
optional double f_double = 1; |
||||
optional float f_float = 2; |
||||
optional int64 f_int64 = 3; |
||||
optional uint64 f_uint64 = 4; |
||||
optional int32 f_int32 = 5; |
||||
optional fixed64 f_fixed64 = 6; |
||||
optional fixed32 f_fixed32 = 7; |
||||
optional bool f_bool = 8; |
||||
optional string f_string = 9; |
||||
optional DecoderTest f_message = 11; |
||||
optional bytes f_bytes = 12; |
||||
optional uint32 f_uint32 = 13; |
||||
optional TestEnum f_enum = 14; |
||||
optional sfixed32 f_sfixed32 = 15; |
||||
optional sfixed64 f_sfixed64 = 16; |
||||
optional sint32 f_sint32 = 17; |
||||
optional sint64 f_sint64 = 18; |
||||
|
||||
optional string nop_field = 40; |
||||
|
||||
repeated double r_double = 536869912; |
||||
repeated float r_float = 536869913; |
||||
repeated int64 r_int64 = 536869914; |
||||
repeated uint64 r_uint64 = 536869915; |
||||
repeated int32 r_int32 = 536869916; |
||||
repeated fixed64 r_fixed64 = 536869917; |
||||
repeated fixed32 r_fixed32 = 536869918; |
||||
repeated bool r_bool = 536869919; |
||||
repeated string r_string = 536869920; |
||||
repeated DecoderTest r_message = 536869922; |
||||
repeated bytes r_bytes = 536869923; |
||||
repeated uint32 r_uint32 = 536869924; |
||||
repeated TestEnum r_enum = 536869925; |
||||
repeated sfixed32 r_sfixed32 = 536869926; |
||||
repeated sfixed64 r_sfixed64 = 536869927; |
||||
repeated sint32 r_sint32 = 536869928; |
||||
repeated sint64 r_sint64 = 536869929; |
||||
} |
||||
|
||||
optional group R_group = 536869921 { |
||||
optional double f_double = 1; |
||||
optional float f_float = 2; |
||||
optional int64 f_int64 = 3; |
||||
optional uint64 f_uint64 = 4; |
||||
optional int32 f_int32 = 5; |
||||
optional fixed64 f_fixed64 = 6; |
||||
optional fixed32 f_fixed32 = 7; |
||||
optional bool f_bool = 8; |
||||
optional string f_string = 9; |
||||
optional DecoderTest f_message = 11; |
||||
optional bytes f_bytes = 12; |
||||
optional uint32 f_uint32 = 13; |
||||
optional TestEnum f_enum = 14; |
||||
optional sfixed32 f_sfixed32 = 15; |
||||
optional sfixed64 f_sfixed64 = 16; |
||||
optional sint32 f_sint32 = 17; |
||||
optional sint64 f_sint64 = 18; |
||||
|
||||
optional string nop_field = 40; |
||||
|
||||
repeated double r_double = 536869912; |
||||
repeated float r_float = 536869913; |
||||
repeated int64 r_int64 = 536869914; |
||||
repeated uint64 r_uint64 = 536869915; |
||||
repeated int32 r_int32 = 536869916; |
||||
repeated fixed64 r_fixed64 = 536869917; |
||||
repeated fixed32 r_fixed32 = 536869918; |
||||
repeated bool r_bool = 536869919; |
||||
repeated string r_string = 536869920; |
||||
repeated DecoderTest r_message = 536869922; |
||||
repeated bytes r_bytes = 536869923; |
||||
repeated uint32 r_uint32 = 536869924; |
||||
repeated TestEnum r_enum = 536869925; |
||||
repeated sfixed32 r_sfixed32 = 536869926; |
||||
repeated sfixed64 r_sfixed64 = 536869927; |
||||
repeated sint32 r_sint32 = 536869928; |
||||
repeated sint64 r_sint64 = 536869929; |
||||
} |
||||
} |
@ -1,102 +0,0 @@ |
||||
|
||||
#include <iostream> |
||||
|
||||
#include "google/protobuf/descriptor.upb.h" |
||||
#include "google/protobuf/descriptor.upbdefs.h" |
||||
#include "tests/test_util.h" |
||||
#include "tests/upb_test.h" |
||||
#include "upb/pb/decoder.h" |
||||
#include "upb/pb/encoder.h" |
||||
#include "upb/port_def.inc" |
||||
#include "upb/upb.hpp" |
||||
|
||||
template <class T> |
||||
class FillStringHandler { |
||||
public: |
||||
static void SetHandler(upb_byteshandler* handler) { |
||||
upb_byteshandler_setstartstr(handler, &FillStringHandler::StartString, |
||||
NULL); |
||||
upb_byteshandler_setstring(handler, &FillStringHandler::StringBuf, NULL); |
||||
} |
||||
|
||||
private: |
||||
// TODO(haberman): add UpbBind/UpbMakeHandler support to BytesHandler so these
|
||||
// can be prettier callbacks.
|
||||
static void* StartString(void *c, const void *hd, size_t size) { |
||||
UPB_UNUSED(hd); |
||||
UPB_UNUSED(size); |
||||
|
||||
T* str = static_cast<T*>(c); |
||||
str->clear(); |
||||
return c; |
||||
} |
||||
|
||||
static size_t StringBuf(void* c, const void* hd, const char* buf, size_t n, |
||||
const upb_bufhandle* h) { |
||||
UPB_UNUSED(hd); |
||||
UPB_UNUSED(h); |
||||
|
||||
T* str = static_cast<T*>(c); |
||||
try { |
||||
str->append(buf, n); |
||||
return n; |
||||
} catch (const std::exception&) { |
||||
return 0; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
class StringSink { |
||||
public: |
||||
template <class T> |
||||
explicit StringSink(T* target) { |
||||
// TODO(haberman): we need to avoid rebuilding a new handler every time,
|
||||
// but with class globals disallowed for google3 C++ this is tricky.
|
||||
upb_byteshandler_init(&handler_); |
||||
FillStringHandler<T>::SetHandler(&handler_); |
||||
input_.Reset(&handler_, target); |
||||
} |
||||
|
||||
upb::BytesSink input() { return input_; } |
||||
|
||||
private: |
||||
upb_byteshandler handler_; |
||||
upb::BytesSink input_; |
||||
}; |
||||
|
||||
void test_pb_roundtrip() { |
||||
std::string input( |
||||
google_protobuf_descriptor_proto_upbdefinit.descriptor.data, |
||||
google_protobuf_descriptor_proto_upbdefinit.descriptor.size); |
||||
std::cout << input.size() << "\n"; |
||||
upb::SymbolTable symtab; |
||||
upb::HandlerCache encoder_cache(upb::pb::EncoderPtr::NewCache()); |
||||
upb::pb::CodeCache decoder_cache(&encoder_cache); |
||||
upb::Arena arena; |
||||
upb::Status status; |
||||
upb::MessageDefPtr md( |
||||
google_protobuf_FileDescriptorProto_getmsgdef(symtab.ptr())); |
||||
ASSERT(md); |
||||
const upb::Handlers *encoder_handlers = encoder_cache.Get(md); |
||||
ASSERT(encoder_handlers); |
||||
const upb::pb::DecoderMethodPtr method = decoder_cache.Get(md); |
||||
|
||||
std::string output; |
||||
StringSink string_sink(&output); |
||||
upb::pb::EncoderPtr encoder = |
||||
upb::pb::EncoderPtr::Create(&arena, encoder_handlers, string_sink.input()); |
||||
upb::pb::DecoderPtr decoder = |
||||
upb::pb::DecoderPtr::Create(&arena, method, encoder.input(), &status); |
||||
bool ok = upb::PutBuffer(input, decoder.input()); |
||||
ASSERT(ok); |
||||
ASSERT(input == output); |
||||
} |
||||
|
||||
extern "C" { |
||||
int run_tests(int argc, char *argv[]) { |
||||
UPB_UNUSED(argc); |
||||
UPB_UNUSED(argv); |
||||
test_pb_roundtrip(); |
||||
return 0; |
||||
} |
||||
} |
@ -1,923 +0,0 @@ |
||||
/*
|
||||
** Inline definitions for handlers.h, which are particularly long and a bit |
||||
** tricky. |
||||
*/ |
||||
|
||||
#ifndef UPB_HANDLERS_INL_H_ |
||||
#define UPB_HANDLERS_INL_H_ |
||||
|
||||
#include <limits.h> |
||||
#include <stddef.h> |
||||
#include "upb/handlers.h" |
||||
|
||||
#include "upb/port_def.inc" |
||||
|
||||
#ifdef __cplusplus |
||||
|
||||
/* Type detection and typedefs for integer types.
|
||||
* For platforms where there are multiple 32-bit or 64-bit types, we need to be |
||||
* able to enumerate them so we can properly create overloads for all variants. |
||||
* |
||||
* If any platform existed where there were three integer types with the same |
||||
* size, this would have to become more complicated. For example, short, int, |
||||
* and long could all be 32-bits. Even more diabolically, short, int, long, |
||||
* and long long could all be 64 bits and still be standard-compliant. |
||||
* However, few platforms are this strange, and it's unlikely that upb will be |
||||
* used on the strangest ones. */ |
||||
|
||||
/* Can't count on stdint.h limits like INT32_MAX, because in C++ these are
|
||||
* only defined when __STDC_LIMIT_MACROS are defined before the *first* include |
||||
* of stdint.h. We can't guarantee that someone else didn't include these first |
||||
* without defining __STDC_LIMIT_MACROS. */ |
||||
#define UPB_INT32_MAX 0x7fffffffLL |
||||
#define UPB_INT32_MIN (-UPB_INT32_MAX - 1) |
||||
#define UPB_INT64_MAX 0x7fffffffffffffffLL |
||||
#define UPB_INT64_MIN (-UPB_INT64_MAX - 1) |
||||
|
||||
#if INT_MAX == UPB_INT32_MAX && INT_MIN == UPB_INT32_MIN |
||||
#define UPB_INT_IS_32BITS 1 |
||||
#endif |
||||
|
||||
#if LONG_MAX == UPB_INT32_MAX && LONG_MIN == UPB_INT32_MIN |
||||
#define UPB_LONG_IS_32BITS 1 |
||||
#endif |
||||
|
||||
#if LONG_MAX == UPB_INT64_MAX && LONG_MIN == UPB_INT64_MIN |
||||
#define UPB_LONG_IS_64BITS 1 |
||||
#endif |
||||
|
||||
#if LLONG_MAX == UPB_INT64_MAX && LLONG_MIN == UPB_INT64_MIN |
||||
#define UPB_LLONG_IS_64BITS 1 |
||||
#endif |
||||
|
||||
/* We use macros instead of typedefs so we can undefine them later and avoid
|
||||
* leaking them outside this header file. */ |
||||
#if UPB_INT_IS_32BITS |
||||
#define UPB_INT32_T int |
||||
#define UPB_UINT32_T unsigned int |
||||
|
||||
#if UPB_LONG_IS_32BITS |
||||
#define UPB_TWO_32BIT_TYPES 1 |
||||
#define UPB_INT32ALT_T long |
||||
#define UPB_UINT32ALT_T unsigned long |
||||
#endif /* UPB_LONG_IS_32BITS */ |
||||
|
||||
#elif UPB_LONG_IS_32BITS /* && !UPB_INT_IS_32BITS */ |
||||
#define UPB_INT32_T long |
||||
#define UPB_UINT32_T unsigned long |
||||
#endif /* UPB_INT_IS_32BITS */ |
||||
|
||||
|
||||
#if UPB_LONG_IS_64BITS |
||||
#define UPB_INT64_T long |
||||
#define UPB_UINT64_T unsigned long |
||||
|
||||
#if UPB_LLONG_IS_64BITS |
||||
#define UPB_TWO_64BIT_TYPES 1 |
||||
#define UPB_INT64ALT_T long long |
||||
#define UPB_UINT64ALT_T unsigned long long |
||||
#endif /* UPB_LLONG_IS_64BITS */ |
||||
|
||||
#elif UPB_LLONG_IS_64BITS /* && !UPB_LONG_IS_64BITS */ |
||||
#define UPB_INT64_T long long |
||||
#define UPB_UINT64_T unsigned long long |
||||
#endif /* UPB_LONG_IS_64BITS */ |
||||
|
||||
#undef UPB_INT32_MAX |
||||
#undef UPB_INT32_MIN |
||||
#undef UPB_INT64_MAX |
||||
#undef UPB_INT64_MIN |
||||
#undef UPB_INT_IS_32BITS |
||||
#undef UPB_LONG_IS_32BITS |
||||
#undef UPB_LONG_IS_64BITS |
||||
#undef UPB_LLONG_IS_64BITS |
||||
|
||||
|
||||
namespace upb { |
||||
|
||||
typedef void CleanupFunc(void *ptr); |
||||
|
||||
/* Template to remove "const" from "const T*" and just return "T*".
|
||||
* |
||||
* We define a nonsense default because otherwise it will fail to instantiate as |
||||
* a function parameter type even in cases where we don't expect any caller to |
||||
* actually match the overload. */ |
||||
class CouldntRemoveConst {}; |
||||
template <class T> struct remove_constptr { typedef CouldntRemoveConst type; }; |
||||
template <class T> struct remove_constptr<const T *> { typedef T *type; }; |
||||
|
||||
/* Template that we use below to remove a template specialization from
|
||||
* consideration if it matches a specific type. */ |
||||
template <class T, class U> struct disable_if_same { typedef void Type; }; |
||||
template <class T> struct disable_if_same<T, T> {}; |
||||
|
||||
template <class T> void DeletePointer(void *p) { delete static_cast<T>(p); } |
||||
|
||||
template <class T1, class T2> |
||||
struct FirstUnlessVoidOrBool { |
||||
typedef T1 value; |
||||
}; |
||||
|
||||
template <class T2> |
||||
struct FirstUnlessVoidOrBool<void, T2> { |
||||
typedef T2 value; |
||||
}; |
||||
|
||||
template <class T2> |
||||
struct FirstUnlessVoidOrBool<bool, T2> { |
||||
typedef T2 value; |
||||
}; |
||||
|
||||
template<class T, class U> |
||||
struct is_same { |
||||
static bool value; |
||||
}; |
||||
|
||||
template<class T> |
||||
struct is_same<T, T> { |
||||
static bool value; |
||||
}; |
||||
|
||||
template<class T, class U> |
||||
bool is_same<T, U>::value = false; |
||||
|
||||
template<class T> |
||||
bool is_same<T, T>::value = true; |
||||
|
||||
/* FuncInfo *******************************************************************/ |
||||
|
||||
/* Info about the user's original, pre-wrapped function. */ |
||||
template <class C, class R = void> |
||||
struct FuncInfo { |
||||
/* The type of the closure that the function takes (its first param). */ |
||||
typedef C Closure; |
||||
|
||||
/* The return type. */ |
||||
typedef R Return; |
||||
}; |
||||
|
||||
/* Func ***********************************************************************/ |
||||
|
||||
/* Func1, Func2, Func3: Template classes representing a function and its
|
||||
* signature. |
||||
* |
||||
* Since the function is a template parameter, calling the function can be |
||||
* inlined at compile-time and does not require a function pointer at runtime. |
||||
* These functions are not bound to a handler data so have no data or cleanup |
||||
* handler. */ |
||||
struct UnboundFunc { |
||||
CleanupFunc *GetCleanup() { return nullptr; } |
||||
void *GetData() { return nullptr; } |
||||
}; |
||||
|
||||
template <class R, class P1, R F(P1), class I> |
||||
struct Func1 : public UnboundFunc { |
||||
typedef R Return; |
||||
typedef I FuncInfo; |
||||
static R Call(P1 p1) { return F(p1); } |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, R F(P1, P2), class I> |
||||
struct Func2 : public UnboundFunc { |
||||
typedef R Return; |
||||
typedef I FuncInfo; |
||||
static R Call(P1 p1, P2 p2) { return F(p1, p2); } |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3, R F(P1, P2, P3), class I> |
||||
struct Func3 : public UnboundFunc { |
||||
typedef R Return; |
||||
typedef I FuncInfo; |
||||
static R Call(P1 p1, P2 p2, P3 p3) { return F(p1, p2, p3); } |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3, class P4, R F(P1, P2, P3, P4), |
||||
class I> |
||||
struct Func4 : public UnboundFunc { |
||||
typedef R Return; |
||||
typedef I FuncInfo; |
||||
static R Call(P1 p1, P2 p2, P3 p3, P4 p4) { return F(p1, p2, p3, p4); } |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3, class P4, class P5, |
||||
R F(P1, P2, P3, P4, P5), class I> |
||||
struct Func5 : public UnboundFunc { |
||||
typedef R Return; |
||||
typedef I FuncInfo; |
||||
static R Call(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) { |
||||
return F(p1, p2, p3, p4, p5); |
||||
} |
||||
}; |
||||
|
||||
/* BoundFunc ******************************************************************/ |
||||
|
||||
/* BoundFunc2, BoundFunc3: Like Func2/Func3 except also contains a value that
|
||||
* shall be bound to the function's second parameter. |
||||
*
|
||||
* Note that the second parameter is a const pointer, but our stored bound value |
||||
* is non-const so we can free it when the handlers are destroyed. */ |
||||
template <class T> |
||||
struct BoundFunc { |
||||
typedef typename remove_constptr<T>::type MutableP2; |
||||
explicit BoundFunc(MutableP2 data_) : data(data_) {} |
||||
CleanupFunc *GetCleanup() { return &DeletePointer<MutableP2>; } |
||||
MutableP2 GetData() { return data; } |
||||
MutableP2 data; |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, R F(P1, P2), class I> |
||||
struct BoundFunc2 : public BoundFunc<P2> { |
||||
typedef BoundFunc<P2> Base; |
||||
typedef I FuncInfo; |
||||
explicit BoundFunc2(typename Base::MutableP2 arg) : Base(arg) {} |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3, R F(P1, P2, P3), class I> |
||||
struct BoundFunc3 : public BoundFunc<P2> { |
||||
typedef BoundFunc<P2> Base; |
||||
typedef I FuncInfo; |
||||
explicit BoundFunc3(typename Base::MutableP2 arg) : Base(arg) {} |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3, class P4, R F(P1, P2, P3, P4), |
||||
class I> |
||||
struct BoundFunc4 : public BoundFunc<P2> { |
||||
typedef BoundFunc<P2> Base; |
||||
typedef I FuncInfo; |
||||
explicit BoundFunc4(typename Base::MutableP2 arg) : Base(arg) {} |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3, class P4, class P5, |
||||
R F(P1, P2, P3, P4, P5), class I> |
||||
struct BoundFunc5 : public BoundFunc<P2> { |
||||
typedef BoundFunc<P2> Base; |
||||
typedef I FuncInfo; |
||||
explicit BoundFunc5(typename Base::MutableP2 arg) : Base(arg) {} |
||||
}; |
||||
|
||||
/* FuncSig ********************************************************************/ |
||||
|
||||
/* FuncSig1, FuncSig2, FuncSig3: template classes reflecting a function
|
||||
* *signature*, but without a specific function attached. |
||||
* |
||||
* These classes contain member functions that can be invoked with a |
||||
* specific function to return a Func/BoundFunc class. */ |
||||
template <class R, class P1> |
||||
struct FuncSig1 { |
||||
template <R F(P1)> |
||||
Func1<R, P1, F, FuncInfo<P1, R> > GetFunc() { |
||||
return Func1<R, P1, F, FuncInfo<P1, R> >(); |
||||
} |
||||
}; |
||||
|
||||
template <class R, class P1, class P2> |
||||
struct FuncSig2 { |
||||
template <R F(P1, P2)> |
||||
Func2<R, P1, P2, F, FuncInfo<P1, R> > GetFunc() { |
||||
return Func2<R, P1, P2, F, FuncInfo<P1, R> >(); |
||||
} |
||||
|
||||
template <R F(P1, P2)> |
||||
BoundFunc2<R, P1, P2, F, FuncInfo<P1, R> > GetFunc( |
||||
typename remove_constptr<P2>::type param2) { |
||||
return BoundFunc2<R, P1, P2, F, FuncInfo<P1, R> >(param2); |
||||
} |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3> |
||||
struct FuncSig3 { |
||||
template <R F(P1, P2, P3)> |
||||
Func3<R, P1, P2, P3, F, FuncInfo<P1, R> > GetFunc() { |
||||
return Func3<R, P1, P2, P3, F, FuncInfo<P1, R> >(); |
||||
} |
||||
|
||||
template <R F(P1, P2, P3)> |
||||
BoundFunc3<R, P1, P2, P3, F, FuncInfo<P1, R> > GetFunc( |
||||
typename remove_constptr<P2>::type param2) { |
||||
return BoundFunc3<R, P1, P2, P3, F, FuncInfo<P1, R> >(param2); |
||||
} |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3, class P4> |
||||
struct FuncSig4 { |
||||
template <R F(P1, P2, P3, P4)> |
||||
Func4<R, P1, P2, P3, P4, F, FuncInfo<P1, R> > GetFunc() { |
||||
return Func4<R, P1, P2, P3, P4, F, FuncInfo<P1, R> >(); |
||||
} |
||||
|
||||
template <R F(P1, P2, P3, P4)> |
||||
BoundFunc4<R, P1, P2, P3, P4, F, FuncInfo<P1, R> > GetFunc( |
||||
typename remove_constptr<P2>::type param2) { |
||||
return BoundFunc4<R, P1, P2, P3, P4, F, FuncInfo<P1, R> >(param2); |
||||
} |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3, class P4, class P5> |
||||
struct FuncSig5 { |
||||
template <R F(P1, P2, P3, P4, P5)> |
||||
Func5<R, P1, P2, P3, P4, P5, F, FuncInfo<P1, R> > GetFunc() { |
||||
return Func5<R, P1, P2, P3, P4, P5, F, FuncInfo<P1, R> >(); |
||||
} |
||||
|
||||
template <R F(P1, P2, P3, P4, P5)> |
||||
BoundFunc5<R, P1, P2, P3, P4, P5, F, FuncInfo<P1, R> > GetFunc( |
||||
typename remove_constptr<P2>::type param2) { |
||||
return BoundFunc5<R, P1, P2, P3, P4, P5, F, FuncInfo<P1, R> >(param2); |
||||
} |
||||
}; |
||||
|
||||
/* Overloaded template function that can construct the appropriate FuncSig*
|
||||
* class given a function pointer by deducing the template parameters. */ |
||||
template <class R, class P1> |
||||
inline FuncSig1<R, P1> MatchFunc(R (*f)(P1)) { |
||||
UPB_UNUSED(f); /* Only used for template parameter deduction. */ |
||||
return FuncSig1<R, P1>(); |
||||
} |
||||
|
||||
template <class R, class P1, class P2> |
||||
inline FuncSig2<R, P1, P2> MatchFunc(R (*f)(P1, P2)) { |
||||
UPB_UNUSED(f); /* Only used for template parameter deduction. */ |
||||
return FuncSig2<R, P1, P2>(); |
||||
} |
||||
|
||||
template <class R, class P1, class P2, class P3> |
||||
inline FuncSig3<R, P1, P2, P3> MatchFunc(R (*f)(P1, P2, P3)) { |
||||
UPB_UNUSED(f); /* Only used for template parameter deduction. */ |
||||
return FuncSig3<R, P1, P2, P3>(); |
||||
} |
||||
|
||||
template <class R, class P1, class P2, class P3, class P4> |
||||
inline FuncSig4<R, P1, P2, P3, P4> MatchFunc(R (*f)(P1, P2, P3, P4)) { |
||||
UPB_UNUSED(f); /* Only used for template parameter deduction. */ |
||||
return FuncSig4<R, P1, P2, P3, P4>(); |
||||
} |
||||
|
||||
template <class R, class P1, class P2, class P3, class P4, class P5> |
||||
inline FuncSig5<R, P1, P2, P3, P4, P5> MatchFunc(R (*f)(P1, P2, P3, P4, P5)) { |
||||
UPB_UNUSED(f); /* Only used for template parameter deduction. */ |
||||
return FuncSig5<R, P1, P2, P3, P4, P5>(); |
||||
} |
||||
|
||||
/* MethodSig ******************************************************************/ |
||||
|
||||
/* CallMethod*: a function template that calls a given method. */ |
||||
template <class R, class C, R (C::*F)()> |
||||
R CallMethod0(C *obj) { |
||||
return ((*obj).*F)(); |
||||
} |
||||
|
||||
template <class R, class C, class P1, R (C::*F)(P1)> |
||||
R CallMethod1(C *obj, P1 arg1) { |
||||
return ((*obj).*F)(arg1); |
||||
} |
||||
|
||||
template <class R, class C, class P1, class P2, R (C::*F)(P1, P2)> |
||||
R CallMethod2(C *obj, P1 arg1, P2 arg2) { |
||||
return ((*obj).*F)(arg1, arg2); |
||||
} |
||||
|
||||
template <class R, class C, class P1, class P2, class P3, R (C::*F)(P1, P2, P3)> |
||||
R CallMethod3(C *obj, P1 arg1, P2 arg2, P3 arg3) { |
||||
return ((*obj).*F)(arg1, arg2, arg3); |
||||
} |
||||
|
||||
template <class R, class C, class P1, class P2, class P3, class P4, |
||||
R (C::*F)(P1, P2, P3, P4)> |
||||
R CallMethod4(C *obj, P1 arg1, P2 arg2, P3 arg3, P4 arg4) { |
||||
return ((*obj).*F)(arg1, arg2, arg3, arg4); |
||||
} |
||||
|
||||
/* MethodSig: like FuncSig, but for member functions.
|
||||
* |
||||
* GetFunc() returns a normal FuncN object, so after calling GetFunc() no |
||||
* more logic is required to special-case methods. */ |
||||
template <class R, class C> |
||||
struct MethodSig0 { |
||||
template <R (C::*F)()> |
||||
Func1<R, C *, CallMethod0<R, C, F>, FuncInfo<C *, R> > GetFunc() { |
||||
return Func1<R, C *, CallMethod0<R, C, F>, FuncInfo<C *, R> >(); |
||||
} |
||||
}; |
||||
|
||||
template <class R, class C, class P1> |
||||
struct MethodSig1 { |
||||
template <R (C::*F)(P1)> |
||||
Func2<R, C *, P1, CallMethod1<R, C, P1, F>, FuncInfo<C *, R> > GetFunc() { |
||||
return Func2<R, C *, P1, CallMethod1<R, C, P1, F>, FuncInfo<C *, R> >(); |
||||
} |
||||
|
||||
template <R (C::*F)(P1)> |
||||
BoundFunc2<R, C *, P1, CallMethod1<R, C, P1, F>, FuncInfo<C *, R> > GetFunc( |
||||
typename remove_constptr<P1>::type param1) { |
||||
return BoundFunc2<R, C *, P1, CallMethod1<R, C, P1, F>, FuncInfo<C *, R> >( |
||||
param1); |
||||
} |
||||
}; |
||||
|
||||
template <class R, class C, class P1, class P2> |
||||
struct MethodSig2 { |
||||
template <R (C::*F)(P1, P2)> |
||||
Func3<R, C *, P1, P2, CallMethod2<R, C, P1, P2, F>, FuncInfo<C *, R> > |
||||
GetFunc() { |
||||
return Func3<R, C *, P1, P2, CallMethod2<R, C, P1, P2, F>, |
||||
FuncInfo<C *, R> >(); |
||||
} |
||||
|
||||
template <R (C::*F)(P1, P2)> |
||||
BoundFunc3<R, C *, P1, P2, CallMethod2<R, C, P1, P2, F>, FuncInfo<C *, R> > |
||||
GetFunc(typename remove_constptr<P1>::type param1) { |
||||
return BoundFunc3<R, C *, P1, P2, CallMethod2<R, C, P1, P2, F>, |
||||
FuncInfo<C *, R> >(param1); |
||||
} |
||||
}; |
||||
|
||||
template <class R, class C, class P1, class P2, class P3> |
||||
struct MethodSig3 { |
||||
template <R (C::*F)(P1, P2, P3)> |
||||
Func4<R, C *, P1, P2, P3, CallMethod3<R, C, P1, P2, P3, F>, FuncInfo<C *, R> > |
||||
GetFunc() { |
||||
return Func4<R, C *, P1, P2, P3, CallMethod3<R, C, P1, P2, P3, F>, |
||||
FuncInfo<C *, R> >(); |
||||
} |
||||
|
||||
template <R (C::*F)(P1, P2, P3)> |
||||
BoundFunc4<R, C *, P1, P2, P3, CallMethod3<R, C, P1, P2, P3, F>, |
||||
FuncInfo<C *, R> > |
||||
GetFunc(typename remove_constptr<P1>::type param1) { |
||||
return BoundFunc4<R, C *, P1, P2, P3, CallMethod3<R, C, P1, P2, P3, F>, |
||||
FuncInfo<C *, R> >(param1); |
||||
} |
||||
}; |
||||
|
||||
template <class R, class C, class P1, class P2, class P3, class P4> |
||||
struct MethodSig4 { |
||||
template <R (C::*F)(P1, P2, P3, P4)> |
||||
Func5<R, C *, P1, P2, P3, P4, CallMethod4<R, C, P1, P2, P3, P4, F>, |
||||
FuncInfo<C *, R> > |
||||
GetFunc() { |
||||
return Func5<R, C *, P1, P2, P3, P4, CallMethod4<R, C, P1, P2, P3, P4, F>, |
||||
FuncInfo<C *, R> >(); |
||||
} |
||||
|
||||
template <R (C::*F)(P1, P2, P3, P4)> |
||||
BoundFunc5<R, C *, P1, P2, P3, P4, CallMethod4<R, C, P1, P2, P3, P4, F>, |
||||
FuncInfo<C *, R> > |
||||
GetFunc(typename remove_constptr<P1>::type param1) { |
||||
return BoundFunc5<R, C *, P1, P2, P3, P4, |
||||
CallMethod4<R, C, P1, P2, P3, P4, F>, FuncInfo<C *, R> >( |
||||
param1); |
||||
} |
||||
}; |
||||
|
||||
template <class R, class C> |
||||
inline MethodSig0<R, C> MatchFunc(R (C::*f)()) { |
||||
UPB_UNUSED(f); /* Only used for template parameter deduction. */ |
||||
return MethodSig0<R, C>(); |
||||
} |
||||
|
||||
template <class R, class C, class P1> |
||||
inline MethodSig1<R, C, P1> MatchFunc(R (C::*f)(P1)) { |
||||
UPB_UNUSED(f); /* Only used for template parameter deduction. */ |
||||
return MethodSig1<R, C, P1>(); |
||||
} |
||||
|
||||
template <class R, class C, class P1, class P2> |
||||
inline MethodSig2<R, C, P1, P2> MatchFunc(R (C::*f)(P1, P2)) { |
||||
UPB_UNUSED(f); /* Only used for template parameter deduction. */ |
||||
return MethodSig2<R, C, P1, P2>(); |
||||
} |
||||
|
||||
template <class R, class C, class P1, class P2, class P3> |
||||
inline MethodSig3<R, C, P1, P2, P3> MatchFunc(R (C::*f)(P1, P2, P3)) { |
||||
UPB_UNUSED(f); /* Only used for template parameter deduction. */ |
||||
return MethodSig3<R, C, P1, P2, P3>(); |
||||
} |
||||
|
||||
template <class R, class C, class P1, class P2, class P3, class P4> |
||||
inline MethodSig4<R, C, P1, P2, P3, P4> MatchFunc(R (C::*f)(P1, P2, P3, P4)) { |
||||
UPB_UNUSED(f); /* Only used for template parameter deduction. */ |
||||
return MethodSig4<R, C, P1, P2, P3, P4>(); |
||||
} |
||||
|
||||
/* MaybeWrapReturn ************************************************************/ |
||||
|
||||
/* Template class that attempts to wrap the return value of the function so it
|
||||
* matches the expected type. There are two main adjustments it may make: |
||||
* |
||||
* 1. If the function returns void, make it return the expected type and with |
||||
* a value that always indicates success. |
||||
* 2. If the function returns bool, make it return the expected type with a |
||||
* value that indicates success or failure. |
||||
* |
||||
* The "expected type" for return is: |
||||
* 1. void* for start handlers. If the closure parameter has a different type |
||||
* we will cast it to void* for the return in the success case. |
||||
* 2. size_t for string buffer handlers. |
||||
* 3. bool for everything else. */ |
||||
|
||||
/* Template parameters are FuncN type and desired return type. */ |
||||
template <class F, class R, class Enable = void> |
||||
struct MaybeWrapReturn; |
||||
|
||||
/* If the return type matches, return the given function unwrapped. */ |
||||
template <class F> |
||||
struct MaybeWrapReturn<F, typename F::Return> { |
||||
typedef F Func; |
||||
}; |
||||
|
||||
/* Function wrapper that munges the return value from void to (bool)true. */ |
||||
template <class P1, class P2, void F(P1, P2)> |
||||
bool ReturnTrue2(P1 p1, P2 p2) { |
||||
F(p1, p2); |
||||
return true; |
||||
} |
||||
|
||||
template <class P1, class P2, class P3, void F(P1, P2, P3)> |
||||
bool ReturnTrue3(P1 p1, P2 p2, P3 p3) { |
||||
F(p1, p2, p3); |
||||
return true; |
||||
} |
||||
|
||||
/* Function wrapper that munges the return value from void to (void*)arg1 */ |
||||
template <class P1, class P2, void F(P1, P2)> |
||||
void *ReturnClosure2(P1 p1, P2 p2) { |
||||
F(p1, p2); |
||||
return p1; |
||||
} |
||||
|
||||
template <class P1, class P2, class P3, void F(P1, P2, P3)> |
||||
void *ReturnClosure3(P1 p1, P2 p2, P3 p3) { |
||||
F(p1, p2, p3); |
||||
return p1; |
||||
} |
||||
|
||||
/* Function wrapper that munges the return value from R to void*. */ |
||||
template <class R, class P1, class P2, R F(P1, P2)> |
||||
void *CastReturnToVoidPtr2(P1 p1, P2 p2) { |
||||
return F(p1, p2); |
||||
} |
||||
|
||||
template <class R, class P1, class P2, class P3, R F(P1, P2, P3)> |
||||
void *CastReturnToVoidPtr3(P1 p1, P2 p2, P3 p3) { |
||||
return F(p1, p2, p3); |
||||
} |
||||
|
||||
/* Function wrapper that munges the return value from bool to void*. */ |
||||
template <class P1, class P2, bool F(P1, P2)> |
||||
void *ReturnClosureOrBreak2(P1 p1, P2 p2) { |
||||
return F(p1, p2) ? p1 : UPB_BREAK; |
||||
} |
||||
|
||||
template <class P1, class P2, class P3, bool F(P1, P2, P3)> |
||||
void *ReturnClosureOrBreak3(P1 p1, P2 p2, P3 p3) { |
||||
return F(p1, p2, p3) ? p1 : UPB_BREAK; |
||||
} |
||||
|
||||
/* For the string callback, which takes five params, returns the size param. */ |
||||
template <class P1, class P2, |
||||
void F(P1, P2, const char *, size_t, const upb_bufhandle *)> |
||||
size_t ReturnStringLen(P1 p1, P2 p2, const char *p3, size_t p4, |
||||
const upb_bufhandle *p5) { |
||||
F(p1, p2, p3, p4, p5); |
||||
return p4; |
||||
} |
||||
|
||||
/* For the string callback, which takes five params, returns the size param or
|
||||
* zero. */ |
||||
template <class P1, class P2, |
||||
bool F(P1, P2, const char *, size_t, const upb_bufhandle *)> |
||||
size_t ReturnNOr0(P1 p1, P2 p2, const char *p3, size_t p4, |
||||
const upb_bufhandle *p5) { |
||||
return F(p1, p2, p3, p4, p5) ? p4 : 0; |
||||
} |
||||
|
||||
/* If we have a function returning void but want a function returning bool, wrap
|
||||
* it in a function that returns true. */ |
||||
template <class P1, class P2, void F(P1, P2), class I> |
||||
struct MaybeWrapReturn<Func2<void, P1, P2, F, I>, bool> { |
||||
typedef Func2<bool, P1, P2, ReturnTrue2<P1, P2, F>, I> Func; |
||||
}; |
||||
|
||||
template <class P1, class P2, class P3, void F(P1, P2, P3), class I> |
||||
struct MaybeWrapReturn<Func3<void, P1, P2, P3, F, I>, bool> { |
||||
typedef Func3<bool, P1, P2, P3, ReturnTrue3<P1, P2, P3, F>, I> Func; |
||||
}; |
||||
|
||||
/* If our function returns void but we want one returning void*, wrap it in a
|
||||
* function that returns the first argument. */ |
||||
template <class P1, class P2, void F(P1, P2), class I> |
||||
struct MaybeWrapReturn<Func2<void, P1, P2, F, I>, void *> { |
||||
typedef Func2<void *, P1, P2, ReturnClosure2<P1, P2, F>, I> Func; |
||||
}; |
||||
|
||||
template <class P1, class P2, class P3, void F(P1, P2, P3), class I> |
||||
struct MaybeWrapReturn<Func3<void, P1, P2, P3, F, I>, void *> { |
||||
typedef Func3<void *, P1, P2, P3, ReturnClosure3<P1, P2, P3, F>, I> Func; |
||||
}; |
||||
|
||||
/* If our function returns R* but we want one returning void*, wrap it in a
|
||||
* function that casts to void*. */ |
||||
template <class R, class P1, class P2, R *F(P1, P2), class I> |
||||
struct MaybeWrapReturn<Func2<R *, P1, P2, F, I>, void *, |
||||
typename disable_if_same<R *, void *>::Type> { |
||||
typedef Func2<void *, P1, P2, CastReturnToVoidPtr2<R *, P1, P2, F>, I> Func; |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3, R *F(P1, P2, P3), class I> |
||||
struct MaybeWrapReturn<Func3<R *, P1, P2, P3, F, I>, void *, |
||||
typename disable_if_same<R *, void *>::Type> { |
||||
typedef Func3<void *, P1, P2, P3, CastReturnToVoidPtr3<R *, P1, P2, P3, F>, I> |
||||
Func; |
||||
}; |
||||
|
||||
/* If our function returns bool but we want one returning void*, wrap it in a
|
||||
* function that returns either the first param or UPB_BREAK. */ |
||||
template <class P1, class P2, bool F(P1, P2), class I> |
||||
struct MaybeWrapReturn<Func2<bool, P1, P2, F, I>, void *> { |
||||
typedef Func2<void *, P1, P2, ReturnClosureOrBreak2<P1, P2, F>, I> Func; |
||||
}; |
||||
|
||||
template <class P1, class P2, class P3, bool F(P1, P2, P3), class I> |
||||
struct MaybeWrapReturn<Func3<bool, P1, P2, P3, F, I>, void *> { |
||||
typedef Func3<void *, P1, P2, P3, ReturnClosureOrBreak3<P1, P2, P3, F>, I> |
||||
Func; |
||||
}; |
||||
|
||||
/* If our function returns void but we want one returning size_t, wrap it in a
|
||||
* function that returns the size argument. */ |
||||
template <class P1, class P2, |
||||
void F(P1, P2, const char *, size_t, const upb_bufhandle *), class I> |
||||
struct MaybeWrapReturn< |
||||
Func5<void, P1, P2, const char *, size_t, const upb_bufhandle *, F, I>, |
||||
size_t> { |
||||
typedef Func5<size_t, P1, P2, const char *, size_t, const upb_bufhandle *, |
||||
ReturnStringLen<P1, P2, F>, I> Func; |
||||
}; |
||||
|
||||
/* If our function returns bool but we want one returning size_t, wrap it in a
|
||||
* function that returns either 0 or the buf size. */ |
||||
template <class P1, class P2, |
||||
bool F(P1, P2, const char *, size_t, const upb_bufhandle *), class I> |
||||
struct MaybeWrapReturn< |
||||
Func5<bool, P1, P2, const char *, size_t, const upb_bufhandle *, F, I>, |
||||
size_t> { |
||||
typedef Func5<size_t, P1, P2, const char *, size_t, const upb_bufhandle *, |
||||
ReturnNOr0<P1, P2, F>, I> Func; |
||||
}; |
||||
|
||||
/* ConvertParams **************************************************************/ |
||||
|
||||
/* Template class that converts the function parameters if necessary, and
|
||||
* ignores the HandlerData parameter if appropriate. |
||||
* |
||||
* Template parameter is the are FuncN function type. */ |
||||
template <class F, class T> |
||||
struct ConvertParams; |
||||
|
||||
/* Function that discards the handler data parameter. */ |
||||
template <class R, class P1, R F(P1)> |
||||
R IgnoreHandlerData2(void *p1, const void *hd) { |
||||
UPB_UNUSED(hd); |
||||
return F(static_cast<P1>(p1)); |
||||
} |
||||
|
||||
template <class R, class P1, class P2Wrapper, class P2Wrapped, |
||||
R F(P1, P2Wrapped)> |
||||
R IgnoreHandlerData3(void *p1, const void *hd, P2Wrapper p2) { |
||||
UPB_UNUSED(hd); |
||||
return F(static_cast<P1>(p1), p2); |
||||
} |
||||
|
||||
template <class R, class P1, class P2, class P3, R F(P1, P2, P3)> |
||||
R IgnoreHandlerData4(void *p1, const void *hd, P2 p2, P3 p3) { |
||||
UPB_UNUSED(hd); |
||||
return F(static_cast<P1>(p1), p2, p3); |
||||
} |
||||
|
||||
template <class R, class P1, class P2, class P3, class P4, R F(P1, P2, P3, P4)> |
||||
R IgnoreHandlerData5(void *p1, const void *hd, P2 p2, P3 p3, P4 p4) { |
||||
UPB_UNUSED(hd); |
||||
return F(static_cast<P1>(p1), p2, p3, p4); |
||||
} |
||||
|
||||
template <class R, class P1, R F(P1, const char*, size_t)> |
||||
R IgnoreHandlerDataIgnoreHandle(void *p1, const void *hd, const char *p2, |
||||
size_t p3, const upb_bufhandle *handle) { |
||||
UPB_UNUSED(hd); |
||||
UPB_UNUSED(handle); |
||||
return F(static_cast<P1>(p1), p2, p3); |
||||
} |
||||
|
||||
/* Function that casts the handler data parameter. */ |
||||
template <class R, class P1, class P2, R F(P1, P2)> |
||||
R CastHandlerData2(void *c, const void *hd) { |
||||
return F(static_cast<P1>(c), static_cast<P2>(hd)); |
||||
} |
||||
|
||||
template <class R, class P1, class P2, class P3Wrapper, class P3Wrapped, |
||||
R F(P1, P2, P3Wrapped)> |
||||
R CastHandlerData3(void *c, const void *hd, P3Wrapper p3) { |
||||
return F(static_cast<P1>(c), static_cast<P2>(hd), p3); |
||||
} |
||||
|
||||
template <class R, class P1, class P2, class P3, class P4, class P5, |
||||
R F(P1, P2, P3, P4, P5)> |
||||
R CastHandlerData5(void *c, const void *hd, P3 p3, P4 p4, P5 p5) { |
||||
return F(static_cast<P1>(c), static_cast<P2>(hd), p3, p4, p5); |
||||
} |
||||
|
||||
template <class R, class P1, class P2, R F(P1, P2, const char *, size_t)> |
||||
R CastHandlerDataIgnoreHandle(void *c, const void *hd, const char *p3, |
||||
size_t p4, const upb_bufhandle *handle) { |
||||
UPB_UNUSED(handle); |
||||
return F(static_cast<P1>(c), static_cast<P2>(hd), p3, p4); |
||||
} |
||||
|
||||
/* For unbound functions, ignore the handler data. */ |
||||
template <class R, class P1, R F(P1), class I, class T> |
||||
struct ConvertParams<Func1<R, P1, F, I>, T> { |
||||
typedef Func2<R, void *, const void *, IgnoreHandlerData2<R, P1, F>, I> Func; |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, R F(P1, P2), class I, |
||||
class R2, class P1_2, class P2_2, class P3_2> |
||||
struct ConvertParams<Func2<R, P1, P2, F, I>, |
||||
R2 (*)(P1_2, P2_2, P3_2)> { |
||||
typedef Func3<R, void *, const void *, P3_2, |
||||
IgnoreHandlerData3<R, P1, P3_2, P2, F>, I> Func; |
||||
}; |
||||
|
||||
/* For StringBuffer only; this ignores both the handler data and the
|
||||
* upb_bufhandle. */ |
||||
template <class R, class P1, R F(P1, const char *, size_t), class I, class T> |
||||
struct ConvertParams<Func3<R, P1, const char *, size_t, F, I>, T> { |
||||
typedef Func5<R, void *, const void *, const char *, size_t, |
||||
const upb_bufhandle *, IgnoreHandlerDataIgnoreHandle<R, P1, F>, |
||||
I> Func; |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3, class P4, R F(P1, P2, P3, P4), |
||||
class I, class T> |
||||
struct ConvertParams<Func4<R, P1, P2, P3, P4, F, I>, T> { |
||||
typedef Func5<R, void *, const void *, P2, P3, P4, |
||||
IgnoreHandlerData5<R, P1, P2, P3, P4, F>, I> Func; |
||||
}; |
||||
|
||||
/* For bound functions, cast the handler data. */ |
||||
template <class R, class P1, class P2, R F(P1, P2), class I, class T> |
||||
struct ConvertParams<BoundFunc2<R, P1, P2, F, I>, T> { |
||||
typedef Func2<R, void *, const void *, CastHandlerData2<R, P1, P2, F>, I> |
||||
Func; |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3, R F(P1, P2, P3), class I, |
||||
class R2, class P1_2, class P2_2, class P3_2> |
||||
struct ConvertParams<BoundFunc3<R, P1, P2, P3, F, I>, |
||||
R2 (*)(P1_2, P2_2, P3_2)> { |
||||
typedef Func3<R, void *, const void *, P3_2, |
||||
CastHandlerData3<R, P1, P2, P3_2, P3, F>, I> Func; |
||||
}; |
||||
|
||||
/* For StringBuffer only; this ignores the upb_bufhandle. */ |
||||
template <class R, class P1, class P2, R F(P1, P2, const char *, size_t), |
||||
class I, class T> |
||||
struct ConvertParams<BoundFunc4<R, P1, P2, const char *, size_t, F, I>, T> { |
||||
typedef Func5<R, void *, const void *, const char *, size_t, |
||||
const upb_bufhandle *, |
||||
CastHandlerDataIgnoreHandle<R, P1, P2, F>, I> |
||||
Func; |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3, class P4, class P5, |
||||
R F(P1, P2, P3, P4, P5), class I, class T> |
||||
struct ConvertParams<BoundFunc5<R, P1, P2, P3, P4, P5, F, I>, T> { |
||||
typedef Func5<R, void *, const void *, P3, P4, P5, |
||||
CastHandlerData5<R, P1, P2, P3, P4, P5, F>, I> Func; |
||||
}; |
||||
|
||||
/* utype/ltype are upper/lower-case, ctype is canonical C type, vtype is
|
||||
* variant C type. */ |
||||
#define TYPE_METHODS(utype, ltype, ctype, vtype) \ |
||||
template <> \
|
||||
struct CanonicalType<vtype> { \
|
||||
typedef ctype Type; \
|
||||
}; \
|
||||
template <> \
|
||||
inline bool HandlersPtr::SetValueHandler<vtype>( \
|
||||
FieldDefPtr f, const HandlersPtr::utype##Handler &handler) { \
|
||||
handler.AddCleanup(ptr()); \
|
||||
return upb_handlers_set##ltype(ptr(), f.ptr(), handler.handler(), \
|
||||
&handler.attr()); \
|
||||
} |
||||
|
||||
TYPE_METHODS(Double, double, double, double) |
||||
TYPE_METHODS(Float, float, float, float) |
||||
TYPE_METHODS(UInt64, uint64, uint64_t, UPB_UINT64_T) |
||||
TYPE_METHODS(UInt32, uint32, uint32_t, UPB_UINT32_T) |
||||
TYPE_METHODS(Int64, int64, int64_t, UPB_INT64_T) |
||||
TYPE_METHODS(Int32, int32, int32_t, UPB_INT32_T) |
||||
TYPE_METHODS(Bool, bool, bool, bool) |
||||
|
||||
#ifdef UPB_TWO_32BIT_TYPES |
||||
TYPE_METHODS(Int32, int32, int32_t, UPB_INT32ALT_T) |
||||
TYPE_METHODS(UInt32, uint32, uint32_t, UPB_UINT32ALT_T) |
||||
#endif |
||||
|
||||
#ifdef UPB_TWO_64BIT_TYPES |
||||
TYPE_METHODS(Int64, int64, int64_t, UPB_INT64ALT_T) |
||||
TYPE_METHODS(UInt64, uint64, uint64_t, UPB_UINT64ALT_T) |
||||
#endif |
||||
#undef TYPE_METHODS |
||||
|
||||
template <> struct CanonicalType<Status*> { |
||||
typedef Status* Type; |
||||
}; |
||||
|
||||
template <class F> struct ReturnOf; |
||||
|
||||
template <class R, class P1, class P2> |
||||
struct ReturnOf<R (*)(P1, P2)> { |
||||
typedef R Return; |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3> |
||||
struct ReturnOf<R (*)(P1, P2, P3)> { |
||||
typedef R Return; |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3, class P4> |
||||
struct ReturnOf<R (*)(P1, P2, P3, P4)> { |
||||
typedef R Return; |
||||
}; |
||||
|
||||
template <class R, class P1, class P2, class P3, class P4, class P5> |
||||
struct ReturnOf<R (*)(P1, P2, P3, P4, P5)> { |
||||
typedef R Return; |
||||
}; |
||||
|
||||
|
||||
template <class T> |
||||
template <class F> |
||||
inline Handler<T>::Handler(F func) |
||||
: registered_(false), |
||||
cleanup_data_(func.GetData()), |
||||
cleanup_func_(func.GetCleanup()) { |
||||
attr_.handler_data = func.GetData(); |
||||
typedef typename ReturnOf<T>::Return Return; |
||||
typedef typename ConvertParams<F, T>::Func ConvertedParamsFunc; |
||||
typedef typename MaybeWrapReturn<ConvertedParamsFunc, Return>::Func |
||||
ReturnWrappedFunc; |
||||
handler_ = ReturnWrappedFunc().Call; |
||||
|
||||
/* Set attributes based on what templates can statically tell us about the
|
||||
* user's function. */ |
||||
|
||||
/* If the original function returns void, then we know that we wrapped it to
|
||||
* always return ok. */ |
||||
bool always_ok = is_same<typename F::FuncInfo::Return, void>::value; |
||||
attr_.alwaysok = always_ok; |
||||
|
||||
/* Closure parameter and return type. */ |
||||
attr_.closure_type = UniquePtrForType<typename F::FuncInfo::Closure>(); |
||||
|
||||
/* We use the closure type (from the first parameter) if the return type is
|
||||
* void or bool, since these are the two cases we wrap to return the closure's |
||||
* type anyway. |
||||
* |
||||
* This is all nonsense for non START* handlers, but it doesn't matter because |
||||
* in that case the value will be ignored. */ |
||||
typedef typename FirstUnlessVoidOrBool<typename F::FuncInfo::Return, |
||||
typename F::FuncInfo::Closure>::value |
||||
EffectiveReturn; |
||||
attr_.return_closure_type = UniquePtrForType<EffectiveReturn>(); |
||||
} |
||||
|
||||
template <class T> |
||||
inline void Handler<T>::AddCleanup(upb_handlers* h) const { |
||||
UPB_ASSERT(!registered_); |
||||
registered_ = true; |
||||
if (cleanup_func_) { |
||||
bool ok = upb_handlers_addcleanup(h, cleanup_data_, cleanup_func_); |
||||
UPB_ASSERT(ok); |
||||
} |
||||
} |
||||
|
||||
} /* namespace upb */ |
||||
|
||||
#endif /* __cplusplus */ |
||||
|
||||
|
||||
#undef UPB_TWO_32BIT_TYPES |
||||
#undef UPB_TWO_64BIT_TYPES |
||||
#undef UPB_INT32_T |
||||
#undef UPB_UINT32_T |
||||
#undef UPB_INT32ALT_T |
||||
#undef UPB_UINT32ALT_T |
||||
#undef UPB_INT64_T |
||||
#undef UPB_UINT64_T |
||||
#undef UPB_INT64ALT_T |
||||
#undef UPB_UINT64ALT_T |
||||
|
||||
#include "upb/port_undef.inc" |
||||
|
||||
#endif /* UPB_HANDLERS_INL_H_ */ |
@ -1,545 +0,0 @@ |
||||
/*
|
||||
** TODO(haberman): it's unclear whether a lot of the consistency checks should |
||||
** UPB_ASSERT() or return false. |
||||
*/ |
||||
|
||||
#include "upb/handlers.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#include "upb/sink.h" |
||||
|
||||
#include "upb/port_def.inc" |
||||
|
||||
struct upb_handlers { |
||||
upb_handlercache *cache; |
||||
const upb_msgdef *msg; |
||||
const upb_handlers **sub; |
||||
const void *top_closure_type; |
||||
upb_handlers_tabent table[1]; /* Dynamically-sized field handler array. */ |
||||
}; |
||||
|
||||
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); |
||||
} |
||||
return mem; |
||||
} |
||||
|
||||
/* Defined for the sole purpose of having a unique pointer value for
|
||||
* UPB_NO_CLOSURE. */ |
||||
char _upb_noclosure; |
||||
|
||||
/* 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]) |
||||
|
||||
/* The selector for a submessage field is the field index. */ |
||||
#define SUBH_F(h, f) SUBH(h, upb_fielddef_index(f)) |
||||
|
||||
static int32_t trygetsel(upb_handlers *h, const upb_fielddef *f, |
||||
upb_handlertype_t type) { |
||||
upb_selector_t sel; |
||||
bool ok; |
||||
|
||||
ok = upb_handlers_getselector(f, type, &sel); |
||||
|
||||
UPB_ASSERT(upb_handlers_msgdef(h) == upb_fielddef_containingtype(f)); |
||||
UPB_ASSERT(ok); |
||||
|
||||
return sel; |
||||
} |
||||
|
||||
static upb_selector_t handlers_getsel(upb_handlers *h, const upb_fielddef *f, |
||||
upb_handlertype_t type) { |
||||
int32_t sel = trygetsel(h, f, type); |
||||
UPB_ASSERT(sel >= 0); |
||||
return sel; |
||||
} |
||||
|
||||
static const void **returntype(upb_handlers *h, const upb_fielddef *f, |
||||
upb_handlertype_t type) { |
||||
return &h->table[handlers_getsel(h, f, type)].attr.return_closure_type; |
||||
} |
||||
|
||||
static bool doset(upb_handlers *h, int32_t sel, const upb_fielddef *f, |
||||
upb_handlertype_t type, upb_func *func, |
||||
const upb_handlerattr *attr) { |
||||
upb_handlerattr set_attr = UPB_HANDLERATTR_INIT; |
||||
const void *closure_type; |
||||
const void **context_closure_type; |
||||
|
||||
UPB_ASSERT(!h->table[sel].func); |
||||
|
||||
if (attr) { |
||||
set_attr = *attr; |
||||
} |
||||
|
||||
/* Check that the given closure type matches the closure type that has been
|
||||
* established for this context (if any). */ |
||||
closure_type = set_attr.closure_type; |
||||
|
||||
if (type == UPB_HANDLER_STRING) { |
||||
context_closure_type = returntype(h, f, UPB_HANDLER_STARTSTR); |
||||
} else if (f && upb_fielddef_isseq(f) && |
||||
type != UPB_HANDLER_STARTSEQ && |
||||
type != UPB_HANDLER_ENDSEQ) { |
||||
context_closure_type = returntype(h, f, UPB_HANDLER_STARTSEQ); |
||||
} else { |
||||
context_closure_type = &h->top_closure_type; |
||||
} |
||||
|
||||
if (closure_type && *context_closure_type && |
||||
closure_type != *context_closure_type) { |
||||
return false; |
||||
} |
||||
|
||||
if (closure_type) |
||||
*context_closure_type = closure_type; |
||||
|
||||
/* If this is a STARTSEQ or STARTSTR handler, check that the returned pointer
|
||||
* matches any pre-existing expectations about what type is expected. */ |
||||
if (type == UPB_HANDLER_STARTSEQ || type == UPB_HANDLER_STARTSTR) { |
||||
const void *return_type = set_attr.return_closure_type; |
||||
const void *table_return_type = h->table[sel].attr.return_closure_type; |
||||
if (return_type && table_return_type && return_type != table_return_type) { |
||||
return false; |
||||
} |
||||
|
||||
if (table_return_type && !return_type) { |
||||
set_attr.return_closure_type = table_return_type; |
||||
} |
||||
} |
||||
|
||||
h->table[sel].func = (upb_func*)func; |
||||
h->table[sel].attr = set_attr; |
||||
return true; |
||||
} |
||||
|
||||
/* Returns the effective closure type for this handler (which will propagate
|
||||
* from outer frames if this frame has no START* handler). Not implemented for |
||||
* UPB_HANDLER_STRING at the moment since this is not needed. Returns NULL is |
||||
* the effective closure type is unspecified (either no handler was registered |
||||
* to specify it or the handler that was registered did not specify the closure |
||||
* type). */ |
||||
const void *effective_closure_type(upb_handlers *h, const upb_fielddef *f, |
||||
upb_handlertype_t type) { |
||||
const void *ret; |
||||
upb_selector_t sel; |
||||
|
||||
UPB_ASSERT(type != UPB_HANDLER_STRING); |
||||
ret = h->top_closure_type; |
||||
|
||||
if (upb_fielddef_isseq(f) && |
||||
type != UPB_HANDLER_STARTSEQ && |
||||
type != UPB_HANDLER_ENDSEQ && |
||||
h->table[sel = handlers_getsel(h, f, UPB_HANDLER_STARTSEQ)].func) { |
||||
ret = h->table[sel].attr.return_closure_type; |
||||
} |
||||
|
||||
if (type == UPB_HANDLER_STRING && |
||||
h->table[sel = handlers_getsel(h, f, UPB_HANDLER_STARTSTR)].func) { |
||||
ret = h->table[sel].attr.return_closure_type; |
||||
} |
||||
|
||||
/* The effective type of the submessage; not used yet.
|
||||
* if (type == SUBMESSAGE && |
||||
* h->table[sel = handlers_getsel(h, f, UPB_HANDLER_STARTSUBMSG)].func) { |
||||
* ret = h->table[sel].attr.return_closure_type; |
||||
* } */ |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static upb_handlers *upb_handlers_new(const upb_msgdef *md, |
||||
upb_handlercache *cache, |
||||
upb_arena *arena) { |
||||
int extra; |
||||
upb_handlers *h; |
||||
|
||||
extra = |
||||
(int)(sizeof(upb_handlers_tabent) * (upb_msgdef_selectorcount(md) - 1)); |
||||
h = upb_calloc(arena, sizeof(*h) + extra); |
||||
if (!h) return NULL; |
||||
|
||||
h->cache = cache; |
||||
h->msg = md; |
||||
|
||||
if (upb_msgdef_submsgfieldcount(md) > 0) { |
||||
size_t bytes = upb_msgdef_submsgfieldcount(md) * sizeof(*h->sub); |
||||
h->sub = upb_calloc(arena, bytes); |
||||
if (!h->sub) return NULL; |
||||
} else { |
||||
h->sub = 0; |
||||
} |
||||
|
||||
/* calloc() above initialized all handlers to NULL. */ |
||||
return h; |
||||
} |
||||
|
||||
/* Public interface ***********************************************************/ |
||||
|
||||
#define SETTER(name, handlerctype, handlertype) \ |
||||
bool upb_handlers_set##name(upb_handlers *h, const upb_fielddef *f, \
|
||||
handlerctype func, \
|
||||
const upb_handlerattr *attr) { \
|
||||
int32_t sel = trygetsel(h, f, handlertype); \
|
||||
return doset(h, sel, f, handlertype, (upb_func *)func, attr); \
|
||||
} |
||||
|
||||
SETTER(int32, upb_int32_handlerfunc*, UPB_HANDLER_INT32) |
||||
SETTER(int64, upb_int64_handlerfunc*, UPB_HANDLER_INT64) |
||||
SETTER(uint32, upb_uint32_handlerfunc*, UPB_HANDLER_UINT32) |
||||
SETTER(uint64, upb_uint64_handlerfunc*, UPB_HANDLER_UINT64) |
||||
SETTER(float, upb_float_handlerfunc*, UPB_HANDLER_FLOAT) |
||||
SETTER(double, upb_double_handlerfunc*, UPB_HANDLER_DOUBLE) |
||||
SETTER(bool, upb_bool_handlerfunc*, UPB_HANDLER_BOOL) |
||||
SETTER(startstr, upb_startstr_handlerfunc*, UPB_HANDLER_STARTSTR) |
||||
SETTER(string, upb_string_handlerfunc*, UPB_HANDLER_STRING) |
||||
SETTER(endstr, upb_endfield_handlerfunc*, UPB_HANDLER_ENDSTR) |
||||
SETTER(startseq, upb_startfield_handlerfunc*, UPB_HANDLER_STARTSEQ) |
||||
SETTER(startsubmsg, upb_startfield_handlerfunc*, UPB_HANDLER_STARTSUBMSG) |
||||
SETTER(endsubmsg, upb_endfield_handlerfunc*, UPB_HANDLER_ENDSUBMSG) |
||||
SETTER(endseq, upb_endfield_handlerfunc*, UPB_HANDLER_ENDSEQ) |
||||
|
||||
#undef SETTER |
||||
|
||||
bool upb_handlers_setunknown(upb_handlers *h, upb_unknown_handlerfunc *func, |
||||
const upb_handlerattr *attr) { |
||||
return doset(h, UPB_UNKNOWN_SELECTOR, NULL, UPB_HANDLER_INT32, |
||||
(upb_func *)func, attr); |
||||
} |
||||
|
||||
bool upb_handlers_setstartmsg(upb_handlers *h, upb_startmsg_handlerfunc *func, |
||||
const upb_handlerattr *attr) { |
||||
return doset(h, UPB_STARTMSG_SELECTOR, NULL, UPB_HANDLER_INT32, |
||||
(upb_func *)func, attr); |
||||
} |
||||
|
||||
bool upb_handlers_setendmsg(upb_handlers *h, upb_endmsg_handlerfunc *func, |
||||
const upb_handlerattr *attr) { |
||||
return doset(h, UPB_ENDMSG_SELECTOR, NULL, UPB_HANDLER_INT32, |
||||
(upb_func *)func, attr); |
||||
} |
||||
|
||||
bool upb_handlers_setsubhandlers(upb_handlers *h, const upb_fielddef *f, |
||||
const upb_handlers *sub) { |
||||
UPB_ASSERT(sub); |
||||
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; |
||||
return true; |
||||
} |
||||
|
||||
const upb_handlers *upb_handlers_getsubhandlers(const upb_handlers *h, |
||||
const upb_fielddef *f) { |
||||
UPB_ASSERT(upb_fielddef_issubmsg(f)); |
||||
return SUBH_F(h, f); |
||||
} |
||||
|
||||
upb_func *upb_handlers_gethandler(const upb_handlers *h, upb_selector_t s, |
||||
const void **handler_data) { |
||||
upb_func *ret = (upb_func *)h->table[s].func; |
||||
if (ret && handler_data) { |
||||
*handler_data = h->table[s].attr.handler_data; |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
bool upb_handlers_getattr(const upb_handlers *h, upb_selector_t sel, |
||||
upb_handlerattr *attr) { |
||||
if (!upb_handlers_gethandler(h, sel, NULL)) |
||||
return false; |
||||
*attr = h->table[sel].attr; |
||||
return true; |
||||
} |
||||
|
||||
const upb_handlers *upb_handlers_getsubhandlers_sel(const upb_handlers *h, |
||||
upb_selector_t sel) { |
||||
/* STARTSUBMSG selector in sel is the field's selector base. */ |
||||
return SUBH(h, sel - UPB_STATIC_SELECTOR_COUNT); |
||||
} |
||||
|
||||
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) { |
||||
return upb_handlercache_addcleanup(h->cache, p, func); |
||||
} |
||||
|
||||
upb_handlertype_t upb_handlers_getprimitivehandlertype(const upb_fielddef *f) { |
||||
switch (upb_fielddef_type(f)) { |
||||
case UPB_TYPE_INT32: |
||||
case UPB_TYPE_ENUM: return UPB_HANDLER_INT32; |
||||
case UPB_TYPE_INT64: return UPB_HANDLER_INT64; |
||||
case UPB_TYPE_UINT32: return UPB_HANDLER_UINT32; |
||||
case UPB_TYPE_UINT64: return UPB_HANDLER_UINT64; |
||||
case UPB_TYPE_FLOAT: return UPB_HANDLER_FLOAT; |
||||
case UPB_TYPE_DOUBLE: return UPB_HANDLER_DOUBLE; |
||||
case UPB_TYPE_BOOL: return UPB_HANDLER_BOOL; |
||||
default: UPB_ASSERT(false); return -1; /* Invalid input. */ |
||||
} |
||||
} |
||||
|
||||
bool upb_handlers_getselector(const upb_fielddef *f, upb_handlertype_t type, |
||||
upb_selector_t *s) { |
||||
uint32_t selector_base = upb_fielddef_selectorbase(f); |
||||
switch (type) { |
||||
case UPB_HANDLER_INT32: |
||||
case UPB_HANDLER_INT64: |
||||
case UPB_HANDLER_UINT32: |
||||
case UPB_HANDLER_UINT64: |
||||
case UPB_HANDLER_FLOAT: |
||||
case UPB_HANDLER_DOUBLE: |
||||
case UPB_HANDLER_BOOL: |
||||
if (!upb_fielddef_isprimitive(f) || |
||||
upb_handlers_getprimitivehandlertype(f) != type) |
||||
return false; |
||||
*s = selector_base; |
||||
break; |
||||
case UPB_HANDLER_STRING: |
||||
if (upb_fielddef_isstring(f)) { |
||||
*s = selector_base; |
||||
} else if (upb_fielddef_lazy(f)) { |
||||
*s = selector_base + 3; |
||||
} else { |
||||
return false; |
||||
} |
||||
break; |
||||
case UPB_HANDLER_STARTSTR: |
||||
if (upb_fielddef_isstring(f) || upb_fielddef_lazy(f)) { |
||||
*s = selector_base + 1; |
||||
} else { |
||||
return false; |
||||
} |
||||
break; |
||||
case UPB_HANDLER_ENDSTR: |
||||
if (upb_fielddef_isstring(f) || upb_fielddef_lazy(f)) { |
||||
*s = selector_base + 2; |
||||
} else { |
||||
return false; |
||||
} |
||||
break; |
||||
case UPB_HANDLER_STARTSEQ: |
||||
if (!upb_fielddef_isseq(f)) return false; |
||||
*s = selector_base - 2; |
||||
break; |
||||
case UPB_HANDLER_ENDSEQ: |
||||
if (!upb_fielddef_isseq(f)) return false; |
||||
*s = selector_base - 1; |
||||
break; |
||||
case UPB_HANDLER_STARTSUBMSG: |
||||
if (!upb_fielddef_issubmsg(f)) return false; |
||||
/* Selectors for STARTSUBMSG are at the beginning of the table so that the
|
||||
* selector can also be used as an index into the "sub" array of |
||||
* subhandlers. The indexes for the two into these two tables are the |
||||
* same, except that in the handler table the static selectors come first. */ |
||||
*s = upb_fielddef_index(f) + UPB_STATIC_SELECTOR_COUNT; |
||||
break; |
||||
case UPB_HANDLER_ENDSUBMSG: |
||||
if (!upb_fielddef_issubmsg(f)) return false; |
||||
*s = selector_base; |
||||
break; |
||||
} |
||||
UPB_ASSERT((size_t)*s < upb_msgdef_selectorcount(upb_fielddef_containingtype(f))); |
||||
return true; |
||||
} |
||||
|
||||
/* upb_handlercache ***********************************************************/ |
||||
|
||||
struct upb_handlercache { |
||||
upb_arena *arena; |
||||
upb_inttable tab; /* maps upb_msgdef* -> upb_handlers*. */ |
||||
upb_handlers_callback *callback; |
||||
const void *closure; |
||||
}; |
||||
|
||||
const upb_handlers *upb_handlercache_get(upb_handlercache *c, |
||||
const upb_msgdef *md) { |
||||
int i, n; |
||||
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, c->arena); |
||||
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. */ |
||||
n = upb_msgdef_fieldcount(md); |
||||
for (i = 0; i < n; i++) { |
||||
const upb_fielddef *f = upb_msgdef_field(md, 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; |
||||
|
||||
cache->arena = upb_arena_new(); |
||||
|
||||
cache->callback = callback; |
||||
cache->closure = closure; |
||||
|
||||
if (!upb_inttable_init(&cache->tab, UPB_CTYPE_PTR)) goto oom; |
||||
|
||||
return cache; |
||||
|
||||
oom: |
||||
upb_gfree(cache); |
||||
return NULL; |
||||
} |
||||
|
||||
void upb_handlercache_free(upb_handlercache *cache) { |
||||
upb_inttable_uninit(&cache->tab); |
||||
upb_arena_free(cache->arena); |
||||
upb_gfree(cache); |
||||
} |
||||
|
||||
bool upb_handlercache_addcleanup(upb_handlercache *c, void *p, |
||||
upb_handlerfree *func) { |
||||
return upb_arena_addcleanup(c->arena, p, func); |
||||
} |
||||
|
||||
/* upb_byteshandler ***********************************************************/ |
||||
|
||||
bool upb_byteshandler_setstartstr(upb_byteshandler *h, |
||||
upb_startstr_handlerfunc *func, void *d) { |
||||
h->table[UPB_STARTSTR_SELECTOR].func = (upb_func*)func; |
||||
h->table[UPB_STARTSTR_SELECTOR].attr.handler_data = d; |
||||
return true; |
||||
} |
||||
|
||||
bool upb_byteshandler_setstring(upb_byteshandler *h, |
||||
upb_string_handlerfunc *func, void *d) { |
||||
h->table[UPB_STRING_SELECTOR].func = (upb_func*)func; |
||||
h->table[UPB_STRING_SELECTOR].attr.handler_data = d; |
||||
return true; |
||||
} |
||||
|
||||
bool upb_byteshandler_setendstr(upb_byteshandler *h, |
||||
upb_endfield_handlerfunc *func, void *d) { |
||||
h->table[UPB_ENDSTR_SELECTOR].func = (upb_func*)func; |
||||
h->table[UPB_ENDSTR_SELECTOR].attr.handler_data = d; |
||||
return true; |
||||
} |
||||
|
||||
/** Handlers for upb_msg ******************************************************/ |
||||
|
||||
typedef struct { |
||||
size_t offset; |
||||
int32_t hasbit; |
||||
} upb_msg_handlerdata; |
||||
|
||||
/* Fallback implementation if the handler is not specialized by the producer. */ |
||||
#define MSG_WRITER(type, ctype) \ |
||||
bool upb_msg_set ## type (void *c, const void *hd, ctype val) { \
|
||||
uint8_t *m = c; \
|
||||
const upb_msg_handlerdata *d = hd; \
|
||||
if (d->hasbit > 0) \
|
||||
*(uint8_t*)&m[d->hasbit / 8] |= 1 << (d->hasbit % 8); \
|
||||
*(ctype*)&m[d->offset] = val; \
|
||||
return true; \
|
||||
} \
|
||||
|
||||
MSG_WRITER(double, double) |
||||
MSG_WRITER(float, float) |
||||
MSG_WRITER(int32, int32_t) |
||||
MSG_WRITER(int64, int64_t) |
||||
MSG_WRITER(uint32, uint32_t) |
||||
MSG_WRITER(uint64, uint64_t) |
||||
MSG_WRITER(bool, bool) |
||||
|
||||
bool upb_msg_setscalarhandler(upb_handlers *h, const upb_fielddef *f, |
||||
size_t offset, int32_t hasbit) { |
||||
upb_handlerattr attr = UPB_HANDLERATTR_INIT; |
||||
bool ok; |
||||
|
||||
upb_msg_handlerdata *d = upb_gmalloc(sizeof(*d)); |
||||
if (!d) return false; |
||||
d->offset = offset; |
||||
d->hasbit = hasbit; |
||||
|
||||
attr.handler_data = d; |
||||
attr.alwaysok = true; |
||||
upb_handlers_addcleanup(h, d, upb_gfree); |
||||
|
||||
#define TYPE(u, l) \ |
||||
case UPB_TYPE_##u: \
|
||||
ok = upb_handlers_set##l(h, f, upb_msg_set##l, &attr); break; |
||||
|
||||
ok = false; |
||||
|
||||
switch (upb_fielddef_type(f)) { |
||||
TYPE(INT64, int64); |
||||
TYPE(INT32, int32); |
||||
TYPE(ENUM, int32); |
||||
TYPE(UINT64, uint64); |
||||
TYPE(UINT32, uint32); |
||||
TYPE(DOUBLE, double); |
||||
TYPE(FLOAT, float); |
||||
TYPE(BOOL, bool); |
||||
default: UPB_ASSERT(false); break; |
||||
} |
||||
#undef TYPE |
||||
|
||||
return ok; |
||||
} |
||||
|
||||
bool upb_msg_getscalarhandlerdata(const upb_handlers *h, |
||||
upb_selector_t s, |
||||
upb_fieldtype_t *type, |
||||
size_t *offset, |
||||
int32_t *hasbit) { |
||||
const upb_msg_handlerdata *d; |
||||
const void *p; |
||||
upb_func *f = upb_handlers_gethandler(h, s, &p); |
||||
|
||||
if ((upb_int64_handlerfunc*)f == upb_msg_setint64) { |
||||
*type = UPB_TYPE_INT64; |
||||
} else if ((upb_int32_handlerfunc*)f == upb_msg_setint32) { |
||||
*type = UPB_TYPE_INT32; |
||||
} else if ((upb_uint64_handlerfunc*)f == upb_msg_setuint64) { |
||||
*type = UPB_TYPE_UINT64; |
||||
} else if ((upb_uint32_handlerfunc*)f == upb_msg_setuint32) { |
||||
*type = UPB_TYPE_UINT32; |
||||
} else if ((upb_double_handlerfunc*)f == upb_msg_setdouble) { |
||||
*type = UPB_TYPE_DOUBLE; |
||||
} else if ((upb_float_handlerfunc*)f == upb_msg_setfloat) { |
||||
*type = UPB_TYPE_FLOAT; |
||||
} else if ((upb_bool_handlerfunc*)f == upb_msg_setbool) { |
||||
*type = UPB_TYPE_BOOL; |
||||
} else { |
||||
return false; |
||||
} |
||||
|
||||
d = p; |
||||
*offset = d->offset; |
||||
*hasbit = d->hasbit; |
||||
return true; |
||||
} |
@ -1,735 +0,0 @@ |
||||
/*
|
||||
** upb::Handlers (upb_handlers) |
||||
** |
||||
** A upb_handlers is like a virtual table for a upb_msgdef. Each field of the |
||||
** message can have associated functions that will be called when we are |
||||
** parsing or visiting a stream of data. This is similar to how handlers work |
||||
** in SAX (the Simple API for XML). |
||||
** |
||||
** The handlers have no idea where the data is coming from, so a single set of |
||||
** handlers could be used with two completely different data sources (for |
||||
** example, a parser and a visitor over in-memory objects). This decoupling is |
||||
** the most important feature of upb, because it allows parsers and serializers |
||||
** to be highly reusable. |
||||
** |
||||
** 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_HANDLERS_H |
||||
#define UPB_HANDLERS_H |
||||
|
||||
#include "upb/def.h" |
||||
#include "upb/table.int.h" |
||||
|
||||
#ifdef __cplusplus |
||||
#include "upb/def.hpp" |
||||
namespace upb { |
||||
class HandlersPtr; |
||||
class HandlerCache; |
||||
template <class T> class Handler; |
||||
template <class T> struct CanonicalType; |
||||
} /* namespace upb */ |
||||
#endif |
||||
|
||||
/* Must be last. */ |
||||
#include "upb/port_def.inc" |
||||
|
||||
/* 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. |
||||
* Cycles are ok; the traversal will stop when it detects a cycle, but we must |
||||
* hit the cycle before the maximum depth is reached. |
||||
* |
||||
* If having a single static limit is too inflexible, we can add another variant |
||||
* of Handlers::Freeze that allows specifying this as a parameter. */ |
||||
#define UPB_MAX_HANDLER_DEPTH 64 |
||||
|
||||
/* All the different types of handlers that can be registered.
|
||||
* Only needed for the advanced functions in upb::Handlers. */ |
||||
typedef enum { |
||||
UPB_HANDLER_INT32, |
||||
UPB_HANDLER_INT64, |
||||
UPB_HANDLER_UINT32, |
||||
UPB_HANDLER_UINT64, |
||||
UPB_HANDLER_FLOAT, |
||||
UPB_HANDLER_DOUBLE, |
||||
UPB_HANDLER_BOOL, |
||||
UPB_HANDLER_STARTSTR, |
||||
UPB_HANDLER_STRING, |
||||
UPB_HANDLER_ENDSTR, |
||||
UPB_HANDLER_STARTSUBMSG, |
||||
UPB_HANDLER_ENDSUBMSG, |
||||
UPB_HANDLER_STARTSEQ, |
||||
UPB_HANDLER_ENDSEQ |
||||
} upb_handlertype_t; |
||||
|
||||
#define UPB_HANDLER_MAX (UPB_HANDLER_ENDSEQ+1) |
||||
|
||||
#define UPB_BREAK NULL |
||||
|
||||
/* A convenient definition for when no closure is needed. */ |
||||
extern char _upb_noclosure; |
||||
#define UPB_NO_CLOSURE &_upb_noclosure |
||||
|
||||
/* A selector refers to a specific field handler in the Handlers object
|
||||
* (for example: the STARTSUBMSG handler for field "field15"). */ |
||||
typedef int32_t upb_selector_t; |
||||
|
||||
/* Static selectors for upb::Handlers. */ |
||||
#define UPB_STARTMSG_SELECTOR 0 |
||||
#define UPB_ENDMSG_SELECTOR 1 |
||||
#define UPB_UNKNOWN_SELECTOR 2 |
||||
#define UPB_STATIC_SELECTOR_COUNT 3 /* Warning: also in upb/def.c. */ |
||||
|
||||
/* Static selectors for upb::BytesHandler. */ |
||||
#define UPB_STARTSTR_SELECTOR 0 |
||||
#define UPB_STRING_SELECTOR 1 |
||||
#define UPB_ENDSTR_SELECTOR 2 |
||||
|
||||
#ifdef __cplusplus |
||||
template<class T> const void *UniquePtrForType() { |
||||
static const char ch = 0; |
||||
return &ch; |
||||
} |
||||
#endif |
||||
|
||||
/* upb_handlers ************************************************************/ |
||||
|
||||
/* Handler attributes, to be registered with the handler itself. */ |
||||
typedef struct { |
||||
const void *handler_data; |
||||
const void *closure_type; |
||||
const void *return_closure_type; |
||||
bool alwaysok; |
||||
} upb_handlerattr; |
||||
|
||||
#define UPB_HANDLERATTR_INIT {NULL, NULL, NULL, false} |
||||
|
||||
/* Bufhandle, data passed along with a buffer to indicate its provenance. */ |
||||
struct upb_bufhandle { |
||||
/* The beginning of the buffer. This may be different than the pointer
|
||||
* passed to a StringBuf handler because the handler may receive data |
||||
* that is from the middle or end of a larger buffer. */ |
||||
const char *buf; |
||||
|
||||
/* The offset within the attached object where this buffer begins. Only
|
||||
* meaningful if there is an attached object. */ |
||||
size_t objofs; |
||||
|
||||
/* The attached object (if any) and a pointer representing its type. */ |
||||
const void *obj; |
||||
const void *objtype; |
||||
|
||||
#ifdef __cplusplus |
||||
template <class T> |
||||
void SetAttachedObject(const T* _obj) { |
||||
obj = _obj; |
||||
objtype = UniquePtrForType<T>(); |
||||
} |
||||
|
||||
template <class T> |
||||
const T *GetAttachedObject() const { |
||||
return objtype == UniquePtrForType<T>() ? static_cast<const T *>(obj) |
||||
: NULL; |
||||
} |
||||
#endif |
||||
}; |
||||
|
||||
typedef struct upb_bufhandle upb_bufhandle; |
||||
|
||||
#define UPB_BUFHANDLE_INIT {NULL, 0, NULL, NULL} |
||||
|
||||
/* Handler function typedefs. */ |
||||
typedef void upb_handlerfree(void *d); |
||||
typedef bool upb_unknown_handlerfunc(void *c, const void *hd, const char *buf, |
||||
size_t n); |
||||
typedef bool upb_startmsg_handlerfunc(void *c, const void*); |
||||
typedef bool upb_endmsg_handlerfunc(void *c, const void *, upb_status *status); |
||||
typedef void* upb_startfield_handlerfunc(void *c, const void *hd); |
||||
typedef bool upb_endfield_handlerfunc(void *c, const void *hd); |
||||
typedef bool upb_int32_handlerfunc(void *c, const void *hd, int32_t val); |
||||
typedef bool upb_int64_handlerfunc(void *c, const void *hd, int64_t val); |
||||
typedef bool upb_uint32_handlerfunc(void *c, const void *hd, uint32_t val); |
||||
typedef bool upb_uint64_handlerfunc(void *c, const void *hd, uint64_t val); |
||||
typedef bool upb_float_handlerfunc(void *c, const void *hd, float val); |
||||
typedef bool upb_double_handlerfunc(void *c, const void *hd, double val); |
||||
typedef bool upb_bool_handlerfunc(void *c, const void *hd, bool val); |
||||
typedef void *upb_startstr_handlerfunc(void *c, const void *hd, |
||||
size_t size_hint); |
||||
typedef size_t upb_string_handlerfunc(void *c, const void *hd, const char *buf, |
||||
size_t n, const upb_bufhandle* handle); |
||||
|
||||
struct upb_handlers; |
||||
typedef struct upb_handlers upb_handlers; |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/* Mutating accessors. */ |
||||
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); |
||||
bool upb_handlers_addcleanup(upb_handlers *h, void *p, upb_handlerfree *hfree); |
||||
bool upb_handlers_setunknown(upb_handlers *h, upb_unknown_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setstartmsg(upb_handlers *h, upb_startmsg_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setendmsg(upb_handlers *h, upb_endmsg_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setint32(upb_handlers *h, const upb_fielddef *f, |
||||
upb_int32_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setint64(upb_handlers *h, const upb_fielddef *f, |
||||
upb_int64_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setuint32(upb_handlers *h, const upb_fielddef *f, |
||||
upb_uint32_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setuint64(upb_handlers *h, const upb_fielddef *f, |
||||
upb_uint64_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setfloat(upb_handlers *h, const upb_fielddef *f, |
||||
upb_float_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setdouble(upb_handlers *h, const upb_fielddef *f, |
||||
upb_double_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setbool(upb_handlers *h, const upb_fielddef *f, |
||||
upb_bool_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setstartstr(upb_handlers *h, const upb_fielddef *f, |
||||
upb_startstr_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setstring(upb_handlers *h, const upb_fielddef *f, |
||||
upb_string_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setendstr(upb_handlers *h, const upb_fielddef *f, |
||||
upb_endfield_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setstartseq(upb_handlers *h, const upb_fielddef *f, |
||||
upb_startfield_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setstartsubmsg(upb_handlers *h, const upb_fielddef *f, |
||||
upb_startfield_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setendsubmsg(upb_handlers *h, const upb_fielddef *f, |
||||
upb_endfield_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
bool upb_handlers_setendseq(upb_handlers *h, const upb_fielddef *f, |
||||
upb_endfield_handlerfunc *func, |
||||
const upb_handlerattr *attr); |
||||
|
||||
/* Read-only accessors. */ |
||||
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, |
||||
upb_selector_t sel); |
||||
upb_func *upb_handlers_gethandler(const upb_handlers *h, upb_selector_t s, |
||||
const void **handler_data); |
||||
bool upb_handlers_getattr(const upb_handlers *h, upb_selector_t s, |
||||
upb_handlerattr *attr); |
||||
|
||||
/* "Static" methods */ |
||||
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); |
||||
UPB_INLINE upb_selector_t upb_handlers_getendselector(upb_selector_t start) { |
||||
return start + 1; |
||||
} |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
|
||||
namespace upb { |
||||
typedef upb_handlers Handlers; |
||||
} |
||||
|
||||
/* Convenience macros for creating a Handler object that is wrapped with a
|
||||
* type-safe wrapper function that converts the "void*" parameters/returns |
||||
* of the underlying C API into nice C++ function. |
||||
* |
||||
* Sample usage: |
||||
* void OnValue1(MyClosure* c, const MyHandlerData* d, int32_t val) { |
||||
* // do stuff ...
|
||||
* } |
||||
* |
||||
* // Handler that doesn't need any data bound to it.
|
||||
* void OnValue2(MyClosure* c, int32_t val) { |
||||
* // do stuff ...
|
||||
* } |
||||
* |
||||
* // Handler that returns bool so it can return failure if necessary.
|
||||
* bool OnValue3(MyClosure* c, int32_t val) { |
||||
* // do stuff ...
|
||||
* return ok; |
||||
* } |
||||
* |
||||
* // Member function handler.
|
||||
* class MyClosure { |
||||
* public: |
||||
* void OnValue(int32_t val) { |
||||
* // do stuff ...
|
||||
* } |
||||
* }; |
||||
* |
||||
* // Takes ownership of the MyHandlerData.
|
||||
* handlers->SetInt32Handler(f1, UpbBind(OnValue1, new MyHandlerData(...))); |
||||
* handlers->SetInt32Handler(f2, UpbMakeHandler(OnValue2)); |
||||
* handlers->SetInt32Handler(f1, UpbMakeHandler(OnValue3)); |
||||
* handlers->SetInt32Handler(f2, UpbMakeHandler(&MyClosure::OnValue)); |
||||
*/ |
||||
|
||||
/* In C++11, the "template" disambiguator can appear even outside templates,
|
||||
* so all calls can safely use this pair of macros. */ |
||||
|
||||
#define UpbMakeHandler(f) upb::MatchFunc(f).template GetFunc<f>() |
||||
|
||||
/* We have to be careful to only evaluate "d" once. */ |
||||
#define UpbBind(f, d) upb::MatchFunc(f).template GetFunc<f>((d)) |
||||
|
||||
/* Handler: a struct that contains the (handler, data, deleter) tuple that is
|
||||
* used to register all handlers. Users can Make() these directly but it's |
||||
* more convenient to use the UpbMakeHandler/UpbBind macros above. */ |
||||
template <class T> class upb::Handler { |
||||
public: |
||||
/* The underlying, handler function signature that upb uses internally. */ |
||||
typedef T FuncPtr; |
||||
|
||||
/* Intentionally implicit. */ |
||||
template <class F> Handler(F func); |
||||
~Handler() { UPB_ASSERT(registered_); } |
||||
|
||||
void AddCleanup(upb_handlers* h) const; |
||||
FuncPtr handler() const { return handler_; } |
||||
const upb_handlerattr& attr() const { return attr_; } |
||||
|
||||
private: |
||||
Handler(const Handler&) = delete; |
||||
Handler& operator=(const Handler&) = delete; |
||||
|
||||
FuncPtr handler_; |
||||
mutable upb_handlerattr attr_; |
||||
mutable bool registered_; |
||||
void *cleanup_data_; |
||||
upb_handlerfree *cleanup_func_; |
||||
}; |
||||
|
||||
/* A upb::Handlers object represents the set of handlers associated with a
|
||||
* message in the graph of messages. You can think of it as a big virtual |
||||
* table with functions corresponding to all the events that can fire while |
||||
* parsing or visiting a message of a specific type. |
||||
* |
||||
* Any handlers that are not set behave as if they had successfully consumed |
||||
* the value. Any unset Start* handlers will propagate their closure to the |
||||
* inner frame. |
||||
* |
||||
* The easiest way to create the *Handler objects needed by the Set* methods is |
||||
* with the UpbBind() and UpbMakeHandler() macros; see below. */ |
||||
class upb::HandlersPtr { |
||||
public: |
||||
HandlersPtr(upb_handlers* ptr) : ptr_(ptr) {} |
||||
|
||||
upb_handlers* ptr() const { return ptr_; } |
||||
|
||||
typedef upb_selector_t Selector; |
||||
typedef upb_handlertype_t Type; |
||||
|
||||
typedef Handler<void *(*)(void *, const void *)> StartFieldHandler; |
||||
typedef Handler<bool (*)(void *, const void *)> EndFieldHandler; |
||||
typedef Handler<bool (*)(void *, const void *)> StartMessageHandler; |
||||
typedef Handler<bool (*)(void *, const void *, upb_status *)> |
||||
EndMessageHandler; |
||||
typedef Handler<void *(*)(void *, const void *, size_t)> StartStringHandler; |
||||
typedef Handler<size_t (*)(void *, const void *, const char *, size_t, |
||||
const upb_bufhandle *)> |
||||
StringHandler; |
||||
|
||||
template <class T> struct ValueHandler { |
||||
typedef Handler<bool(*)(void *, const void *, T)> H; |
||||
}; |
||||
|
||||
typedef ValueHandler<int32_t>::H Int32Handler; |
||||
typedef ValueHandler<int64_t>::H Int64Handler; |
||||
typedef ValueHandler<uint32_t>::H UInt32Handler; |
||||
typedef ValueHandler<uint64_t>::H UInt64Handler; |
||||
typedef ValueHandler<float>::H FloatHandler; |
||||
typedef ValueHandler<double>::H DoubleHandler; |
||||
typedef ValueHandler<bool>::H BoolHandler; |
||||
|
||||
/* Any function pointer can be converted to this and converted back to its
|
||||
* correct type. */ |
||||
typedef void GenericFunction(); |
||||
|
||||
typedef void HandlersCallback(const void *closure, upb_handlers *h); |
||||
|
||||
/* Returns the msgdef associated with this handlers object. */ |
||||
MessageDefPtr message_def() const { |
||||
return MessageDefPtr(upb_handlers_msgdef(ptr())); |
||||
} |
||||
|
||||
/* Adds the given pointer and function to the list of cleanup functions that
|
||||
* will be run when these handlers are freed. If this pointer has previously |
||||
* been registered, the function returns false and does nothing. */ |
||||
bool AddCleanup(void *ptr, upb_handlerfree *cleanup) { |
||||
return upb_handlers_addcleanup(ptr_, ptr, cleanup); |
||||
} |
||||
|
||||
/* Sets the startmsg handler for the message, which is defined as follows:
|
||||
* |
||||
* bool startmsg(MyType* closure) { |
||||
* // Called when the message begins. Returns true if processing should
|
||||
* // continue.
|
||||
* return true; |
||||
* } |
||||
*/ |
||||
bool SetStartMessageHandler(const StartMessageHandler &h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setstartmsg(ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
/* Sets the endmsg handler for the message, which is defined as follows:
|
||||
* |
||||
* bool endmsg(MyType* closure, upb_status *status) { |
||||
* // Called when processing of this message ends, whether in success or
|
||||
* // failure. "status" indicates the final status of processing, and
|
||||
* // can also be modified in-place to update the final status.
|
||||
* } |
||||
*/ |
||||
bool SetEndMessageHandler(const EndMessageHandler& h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setendmsg(ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
/* Sets the value handler for the given field, which is defined as follows
|
||||
* (this is for an int32 field; other field types will pass their native |
||||
* C/C++ type for "val"): |
||||
* |
||||
* bool OnValue(MyClosure* c, const MyHandlerData* d, int32_t val) { |
||||
* // Called when the field's value is encountered. "d" contains
|
||||
* // whatever data was bound to this field when it was registered.
|
||||
* // Returns true if processing should continue.
|
||||
* return true; |
||||
* } |
||||
* |
||||
* handers->SetInt32Handler(f, UpbBind(OnValue, new MyHandlerData(...))); |
||||
* |
||||
* The value type must exactly match f->type(). |
||||
* For example, a handler that takes an int32_t parameter may only be used for |
||||
* fields of type UPB_TYPE_INT32 and UPB_TYPE_ENUM. |
||||
* |
||||
* Returns false if the handler failed to register; in this case the cleanup |
||||
* handler (if any) will be called immediately. |
||||
*/ |
||||
bool SetInt32Handler(FieldDefPtr f, const Int32Handler &h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setint32(ptr(), f.ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
bool SetInt64Handler (FieldDefPtr f, const Int64Handler& h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setint64(ptr(), f.ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
bool SetUInt32Handler(FieldDefPtr f, const UInt32Handler& h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setuint32(ptr(), f.ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
bool SetUInt64Handler(FieldDefPtr f, const UInt64Handler& h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setuint64(ptr(), f.ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
bool SetFloatHandler (FieldDefPtr f, const FloatHandler& h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setfloat(ptr(), f.ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
bool SetDoubleHandler(FieldDefPtr f, const DoubleHandler& h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setdouble(ptr(), f.ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
bool SetBoolHandler(FieldDefPtr f, const BoolHandler &h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setbool(ptr(), f.ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
/* Like the previous, but templated on the type on the value (ie. int32).
|
||||
* This is mostly useful to call from other templates. To call this you must |
||||
* specify the template parameter explicitly, ie: |
||||
* h->SetValueHandler<T>(f, UpbBind(MyHandler<T>, MyData)); */ |
||||
template <class T> |
||||
bool SetValueHandler( |
||||
FieldDefPtr f, |
||||
const typename ValueHandler<typename CanonicalType<T>::Type>::H &handler); |
||||
|
||||
/* Sets handlers for a string field, which are defined as follows:
|
||||
* |
||||
* MySubClosure* startstr(MyClosure* c, const MyHandlerData* d, |
||||
* size_t size_hint) { |
||||
* // Called when a string value begins. The return value indicates the
|
||||
* // closure for the string. "size_hint" indicates the size of the
|
||||
* // string if it is known, however if the string is length-delimited
|
||||
* // and the end-of-string is not available size_hint will be zero.
|
||||
* // This case is indistinguishable from the case where the size is
|
||||
* // known to be zero.
|
||||
* //
|
||||
* // TODO(haberman): is it important to distinguish these cases?
|
||||
* // If we had ssize_t as a type we could make -1 "unknown", but
|
||||
* // ssize_t is POSIX (not ANSI) and therefore less portable.
|
||||
* // In practice I suspect it won't be important to distinguish.
|
||||
* return closure; |
||||
* } |
||||
* |
||||
* size_t str(MyClosure* closure, const MyHandlerData* d, |
||||
* const char *str, size_t len) { |
||||
* // Called for each buffer of string data; the multiple physical buffers
|
||||
* // are all part of the same logical string. The return value indicates
|
||||
* // how many bytes were consumed. If this number is less than "len",
|
||||
* // this will also indicate that processing should be halted for now,
|
||||
* // like returning false or UPB_BREAK from any other callback. If
|
||||
* // number is greater than "len", the excess bytes will be skipped over
|
||||
* // and not passed to the callback.
|
||||
* return len; |
||||
* } |
||||
* |
||||
* bool endstr(MyClosure* c, const MyHandlerData* d) { |
||||
* // Called when a string value ends. Return value indicates whether
|
||||
* // processing should continue.
|
||||
* return true; |
||||
* } |
||||
*/ |
||||
bool SetStartStringHandler(FieldDefPtr f, const StartStringHandler &h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setstartstr(ptr(), f.ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
bool SetStringHandler(FieldDefPtr f, const StringHandler& h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setstring(ptr(), f.ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
bool SetEndStringHandler(FieldDefPtr f, const EndFieldHandler& h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setendstr(ptr(), f.ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
/* Sets the startseq handler, which is defined as follows:
|
||||
* |
||||
* MySubClosure *startseq(MyClosure* c, const MyHandlerData* d) { |
||||
* // Called when a sequence (repeated field) begins. The returned
|
||||
* // pointer indicates the closure for the sequence (or UPB_BREAK
|
||||
* // to interrupt processing).
|
||||
* return closure; |
||||
* } |
||||
* |
||||
* h->SetStartSequenceHandler(f, UpbBind(startseq, new MyHandlerData(...))); |
||||
* |
||||
* Returns "false" if "f" does not belong to this message or is not a |
||||
* repeated field. |
||||
*/ |
||||
bool SetStartSequenceHandler(FieldDefPtr f, const StartFieldHandler &h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setstartseq(ptr(), f.ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
/* Sets the startsubmsg handler for the given field, which is defined as
|
||||
* follows: |
||||
* |
||||
* MySubClosure* startsubmsg(MyClosure* c, const MyHandlerData* d) { |
||||
* // Called when a submessage begins. The returned pointer indicates the
|
||||
* // closure for the sequence (or UPB_BREAK to interrupt processing).
|
||||
* return closure; |
||||
* } |
||||
* |
||||
* h->SetStartSubMessageHandler(f, UpbBind(startsubmsg, |
||||
* new MyHandlerData(...))); |
||||
* |
||||
* Returns "false" if "f" does not belong to this message or is not a |
||||
* submessage/group field. |
||||
*/ |
||||
bool SetStartSubMessageHandler(FieldDefPtr f, const StartFieldHandler& h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setstartsubmsg(ptr(), f.ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
/* Sets the endsubmsg handler for the given field, which is defined as
|
||||
* follows: |
||||
* |
||||
* bool endsubmsg(MyClosure* c, const MyHandlerData* d) { |
||||
* // Called when a submessage ends. Returns true to continue processing.
|
||||
* return true; |
||||
* } |
||||
* |
||||
* Returns "false" if "f" does not belong to this message or is not a |
||||
* submessage/group field. |
||||
*/ |
||||
bool SetEndSubMessageHandler(FieldDefPtr f, const EndFieldHandler &h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setendsubmsg(ptr(), f.ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
/* Starts the endsubseq handler for the given field, which is defined as
|
||||
* follows: |
||||
* |
||||
* bool endseq(MyClosure* c, const MyHandlerData* d) { |
||||
* // Called when a sequence ends. Returns true continue processing.
|
||||
* return true; |
||||
* } |
||||
* |
||||
* Returns "false" if "f" does not belong to this message or is not a |
||||
* repeated field. |
||||
*/ |
||||
bool SetEndSequenceHandler(FieldDefPtr f, const EndFieldHandler &h) { |
||||
h.AddCleanup(ptr()); |
||||
return upb_handlers_setendseq(ptr(), f.ptr(), h.handler(), &h.attr()); |
||||
} |
||||
|
||||
private: |
||||
upb_handlers* ptr_; |
||||
}; |
||||
|
||||
#endif /* __cplusplus */ |
||||
|
||||
/* upb_handlercache ***********************************************************/ |
||||
|
||||
/* A upb_handlercache lazily builds and caches upb_handlers. You pass it a
|
||||
* function (with optional closure) that can build handlers for a given |
||||
* message on-demand, and the cache maintains a map of msgdef->handlers. */ |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
struct upb_handlercache; |
||||
typedef struct upb_handlercache upb_handlercache; |
||||
|
||||
typedef void upb_handlers_callback(const void *closure, upb_handlers *h); |
||||
|
||||
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); |
||||
bool upb_handlercache_addcleanup(upb_handlercache *h, void *p, |
||||
upb_handlerfree *hfree); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
|
||||
class upb::HandlerCache { |
||||
public: |
||||
HandlerCache(upb_handlers_callback *callback, const void *closure) |
||||
: ptr_(upb_handlercache_new(callback, closure), upb_handlercache_free) {} |
||||
HandlerCache(HandlerCache&&) = default; |
||||
HandlerCache& operator=(HandlerCache&&) = default; |
||||
HandlerCache(upb_handlercache* c) : ptr_(c, upb_handlercache_free) {} |
||||
|
||||
upb_handlercache* ptr() { return ptr_.get(); } |
||||
|
||||
const upb_handlers *Get(MessageDefPtr md) { |
||||
return upb_handlercache_get(ptr_.get(), md.ptr()); |
||||
} |
||||
|
||||
private: |
||||
std::unique_ptr<upb_handlercache, decltype(&upb_handlercache_free)> ptr_; |
||||
}; |
||||
|
||||
#endif /* __cplusplus */ |
||||
|
||||
/* upb_byteshandler ***********************************************************/ |
||||
|
||||
typedef struct { |
||||
upb_func *func; |
||||
|
||||
/* It is wasteful to include the entire attributes here:
|
||||
* |
||||
* * Some of the information is redundant (like storing the closure type |
||||
* separately for each handler that must match). |
||||
* * Some of the info is only needed prior to freeze() (like closure types). |
||||
* * alignment padding wastes a lot of space for alwaysok_. |
||||
* |
||||
* If/when the size and locality of handlers is an issue, we can optimize this |
||||
* not to store the entire attr like this. We do not expose the table's |
||||
* layout to allow this optimization in the future. */ |
||||
upb_handlerattr attr; |
||||
} upb_handlers_tabent; |
||||
|
||||
#define UPB_TABENT_INIT {NULL, UPB_HANDLERATTR_INIT} |
||||
|
||||
typedef struct { |
||||
upb_handlers_tabent table[3]; |
||||
} upb_byteshandler; |
||||
|
||||
#define UPB_BYTESHANDLER_INIT \ |
||||
{ \
|
||||
{ UPB_TABENT_INIT, UPB_TABENT_INIT, UPB_TABENT_INIT } \
|
||||
} |
||||
|
||||
UPB_INLINE void upb_byteshandler_init(upb_byteshandler *handler) { |
||||
upb_byteshandler init = UPB_BYTESHANDLER_INIT; |
||||
*handler = init; |
||||
} |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/* Caller must ensure that "d" outlives the handlers. */ |
||||
bool upb_byteshandler_setstartstr(upb_byteshandler *h, |
||||
upb_startstr_handlerfunc *func, void *d); |
||||
bool upb_byteshandler_setstring(upb_byteshandler *h, |
||||
upb_string_handlerfunc *func, void *d); |
||||
bool upb_byteshandler_setendstr(upb_byteshandler *h, |
||||
upb_endfield_handlerfunc *func, void *d); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
|
||||
namespace upb { |
||||
typedef upb_byteshandler BytesHandler; |
||||
} |
||||
#endif |
||||
|
||||
/** Message handlers ******************************************************************/ |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/* These are the handlers used internally by upb_msgfactory_getmergehandlers().
|
||||
* They write scalar data to a known offset from the message pointer. |
||||
* |
||||
* These would be trivial for anyone to implement themselves, but it's better |
||||
* to use these because some JITs will recognize and specialize these instead |
||||
* of actually calling the function. */ |
||||
|
||||
/* Sets a handler for the given primitive field that will write the data at the
|
||||
* given offset. If hasbit > 0, also sets a hasbit at the given bit offset |
||||
* (addressing each byte low to high). */ |
||||
bool upb_msg_setscalarhandler(upb_handlers *h, |
||||
const upb_fielddef *f, |
||||
size_t offset, |
||||
int32_t hasbit); |
||||
|
||||
/* If the given handler is a msghandlers_primitive field, returns true and sets
|
||||
* *type, *offset and *hasbit. Otherwise returns false. */ |
||||
bool upb_msg_getscalarhandlerdata(const upb_handlers *h, |
||||
upb_selector_t s, |
||||
upb_fieldtype_t *type, |
||||
size_t *offset, |
||||
int32_t *hasbit); |
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#include "upb/port_undef.inc" |
||||
|
||||
#include "upb/handlers-inl.h" |
||||
|
||||
#endif /* UPB_HANDLERS_H */ |
@ -1,140 +0,0 @@ |
||||
/*
|
||||
** upb::json::Parser (upb_json_parser) |
||||
** |
||||
** Parses JSON according to a specific schema. |
||||
** Support for parsing arbitrary JSON (schema-less) will be added later. |
||||
*/ |
||||
|
||||
#ifndef UPB_JSON_PARSER_H_ |
||||
#define UPB_JSON_PARSER_H_ |
||||
|
||||
#include "upb/sink.h" |
||||
|
||||
#ifdef __cplusplus |
||||
namespace upb { |
||||
namespace json { |
||||
class CodeCache; |
||||
class ParserPtr; |
||||
class ParserMethodPtr; |
||||
} /* namespace json */ |
||||
} /* namespace upb */ |
||||
#endif |
||||
|
||||
/* upb_json_parsermethod ******************************************************/ |
||||
|
||||
struct upb_json_parsermethod; |
||||
typedef struct upb_json_parsermethod upb_json_parsermethod; |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
const upb_byteshandler* upb_json_parsermethod_inputhandler( |
||||
const upb_json_parsermethod* m); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
|
||||
class upb::json::ParserMethodPtr { |
||||
public: |
||||
ParserMethodPtr() : ptr_(nullptr) {} |
||||
ParserMethodPtr(const upb_json_parsermethod* ptr) : ptr_(ptr) {} |
||||
|
||||
const upb_json_parsermethod* ptr() const { return ptr_; } |
||||
|
||||
const BytesHandler* input_handler() const { |
||||
return upb_json_parsermethod_inputhandler(ptr()); |
||||
} |
||||
|
||||
private: |
||||
const upb_json_parsermethod* ptr_; |
||||
}; |
||||
|
||||
#endif /* __cplusplus */ |
||||
|
||||
/* upb_json_parser ************************************************************/ |
||||
|
||||
/* Preallocation hint: parser won't allocate more bytes than this when first
|
||||
* constructed. This hint may be an overestimate for some build configurations. |
||||
* But if the parser library is upgraded without recompiling the application, |
||||
* it may be an underestimate. */ |
||||
#define UPB_JSON_PARSER_SIZE 5712 |
||||
|
||||
struct upb_json_parser; |
||||
typedef struct upb_json_parser upb_json_parser; |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
upb_json_parser* upb_json_parser_create(upb_arena* a, |
||||
const upb_json_parsermethod* m, |
||||
const upb_symtab* symtab, |
||||
upb_sink output, |
||||
upb_status *status, |
||||
bool ignore_json_unknown); |
||||
upb_bytessink upb_json_parser_input(upb_json_parser* p); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
|
||||
/* Parses an incoming BytesStream, pushing the results to the destination
|
||||
* sink. */ |
||||
class upb::json::ParserPtr { |
||||
public: |
||||
ParserPtr(upb_json_parser* ptr) : ptr_(ptr) {} |
||||
|
||||
static ParserPtr Create(Arena* arena, ParserMethodPtr method, |
||||
SymbolTable* symtab, Sink output, Status* status, |
||||
bool ignore_json_unknown) { |
||||
upb_symtab* symtab_ptr = symtab ? symtab->ptr() : nullptr; |
||||
return ParserPtr(upb_json_parser_create( |
||||
arena->ptr(), method.ptr(), symtab_ptr, output.sink(), status->ptr(), |
||||
ignore_json_unknown)); |
||||
} |
||||
|
||||
BytesSink input() { return upb_json_parser_input(ptr_); } |
||||
|
||||
private: |
||||
upb_json_parser* ptr_; |
||||
}; |
||||
|
||||
#endif /* __cplusplus */ |
||||
|
||||
/* upb_json_codecache *********************************************************/ |
||||
|
||||
/* Lazily builds and caches decoder methods that will push data to the given
|
||||
* handlers. The upb_symtab object(s) must outlive this object. */ |
||||
|
||||
struct upb_json_codecache; |
||||
typedef struct upb_json_codecache upb_json_codecache; |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
upb_json_codecache *upb_json_codecache_new(void); |
||||
void upb_json_codecache_free(upb_json_codecache *cache); |
||||
const upb_json_parsermethod* upb_json_codecache_get(upb_json_codecache* cache, |
||||
const upb_msgdef* md); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
|
||||
class upb::json::CodeCache { |
||||
public: |
||||
CodeCache() : ptr_(upb_json_codecache_new(), upb_json_codecache_free) {} |
||||
|
||||
/* Returns a DecoderMethod that can push data to the given handlers.
|
||||
* If a suitable method already exists, it will be returned from the cache. */ |
||||
ParserMethodPtr Get(MessageDefPtr md) { |
||||
return upb_json_codecache_get(ptr_.get(), md.ptr()); |
||||
} |
||||
|
||||
private: |
||||
std::unique_ptr<upb_json_codecache, decltype(&upb_json_codecache_free)> ptr_; |
||||
}; |
||||
|
||||
#endif |
||||
|
||||
#endif /* UPB_JSON_PARSER_H_ */ |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,72 +0,0 @@ |
||||
/*
|
||||
** upb::json::Printer |
||||
** |
||||
** Handlers that emit JSON according to a specific protobuf schema. |
||||
*/ |
||||
|
||||
#ifndef UPB_JSON_TYPED_PRINTER_H_ |
||||
#define UPB_JSON_TYPED_PRINTER_H_ |
||||
|
||||
#include "upb/sink.h" |
||||
|
||||
#ifdef __cplusplus |
||||
namespace upb { |
||||
namespace json { |
||||
class PrinterPtr; |
||||
} /* namespace json */ |
||||
} /* namespace upb */ |
||||
#endif |
||||
|
||||
/* upb_json_printer ***********************************************************/ |
||||
|
||||
#define UPB_JSON_PRINTER_SIZE 192 |
||||
|
||||
struct upb_json_printer; |
||||
typedef struct upb_json_printer upb_json_printer; |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/* Native C API. */ |
||||
upb_json_printer *upb_json_printer_create(upb_arena *a, const upb_handlers *h, |
||||
upb_bytessink output); |
||||
upb_sink upb_json_printer_input(upb_json_printer *p); |
||||
const upb_handlers *upb_json_printer_newhandlers(const upb_msgdef *md, |
||||
bool preserve_fieldnames, |
||||
const void *owner); |
||||
|
||||
/* Lazily builds and caches handlers that will push encoded data to a bytessink.
|
||||
* Any msgdef objects used with this object must outlive it. */ |
||||
upb_handlercache *upb_json_printer_newcache(bool preserve_proto_fieldnames); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
|
||||
/* Prints an incoming stream of data to a BytesSink in JSON format. */ |
||||
class upb::json::PrinterPtr { |
||||
public: |
||||
PrinterPtr(upb_json_printer* ptr) : ptr_(ptr) {} |
||||
|
||||
static PrinterPtr Create(Arena *arena, const upb::Handlers *handlers, |
||||
BytesSink output) { |
||||
return PrinterPtr( |
||||
upb_json_printer_create(arena->ptr(), handlers, output.sink())); |
||||
} |
||||
|
||||
/* The input to the printer. */ |
||||
Sink input() { return upb_json_printer_input(ptr_); } |
||||
|
||||
static const size_t kSize = UPB_JSON_PRINTER_SIZE; |
||||
|
||||
static HandlerCache NewCache(bool preserve_proto_fieldnames) { |
||||
return upb_json_printer_newcache(preserve_proto_fieldnames); |
||||
} |
||||
|
||||
private: |
||||
upb_json_printer* ptr_; |
||||
}; |
||||
|
||||
#endif /* __cplusplus */ |
||||
|
||||
#endif /* UPB_JSON_TYPED_PRINTER_H_ */ |
@ -1,919 +0,0 @@ |
||||
/*
|
||||
** protobuf decoder bytecode compiler |
||||
** |
||||
** Code to compile a upb::Handlers into bytecode for decoding a protobuf |
||||
** according to that specific schema and destination handlers. |
||||
** |
||||
** Bytecode definition is in decoder.int.h. |
||||
*/ |
||||
|
||||
#include <stdarg.h> |
||||
#include "upb/pb/decoder.int.h" |
||||
#include "upb/pb/varint.int.h" |
||||
|
||||
#ifdef UPB_DUMP_BYTECODE |
||||
#include <stdio.h> |
||||
#endif |
||||
|
||||
#include "upb/port_def.inc" |
||||
|
||||
#define MAXLABEL 5 |
||||
#define EMPTYLABEL -1 |
||||
|
||||
/* upb_pbdecodermethod ********************************************************/ |
||||
|
||||
static void freemethod(upb_pbdecodermethod *method) { |
||||
upb_inttable_uninit(&method->dispatch); |
||||
upb_gfree(method); |
||||
} |
||||
|
||||
static upb_pbdecodermethod *newmethod(const upb_handlers *dest_handlers, |
||||
mgroup *group) { |
||||
upb_pbdecodermethod *ret = upb_gmalloc(sizeof(*ret)); |
||||
upb_byteshandler_init(&ret->input_handler_); |
||||
|
||||
ret->group = group; |
||||
ret->dest_handlers_ = dest_handlers; |
||||
upb_inttable_init(&ret->dispatch, UPB_CTYPE_UINT64); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
const upb_handlers *upb_pbdecodermethod_desthandlers( |
||||
const upb_pbdecodermethod *m) { |
||||
return m->dest_handlers_; |
||||
} |
||||
|
||||
const upb_byteshandler *upb_pbdecodermethod_inputhandler( |
||||
const upb_pbdecodermethod *m) { |
||||
return &m->input_handler_; |
||||
} |
||||
|
||||
bool upb_pbdecodermethod_isnative(const upb_pbdecodermethod *m) { |
||||
return m->is_native_; |
||||
} |
||||
|
||||
|
||||
/* 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); |
||||
upb_gfree(g->bytecode); |
||||
upb_gfree(g); |
||||
} |
||||
|
||||
mgroup *newgroup(void) { |
||||
mgroup *g = upb_gmalloc(sizeof(*g)); |
||||
upb_inttable_init(&g->methods, UPB_CTYPE_PTR); |
||||
g->bytecode = NULL; |
||||
g->bytecode_end = NULL; |
||||
return g; |
||||
} |
||||
|
||||
|
||||
/* bytecode compiler **********************************************************/ |
||||
|
||||
/* Data used only at compilation time. */ |
||||
typedef struct { |
||||
mgroup *group; |
||||
|
||||
uint32_t *pc; |
||||
int fwd_labels[MAXLABEL]; |
||||
int back_labels[MAXLABEL]; |
||||
|
||||
/* For fields marked "lazy", parse them lazily or eagerly? */ |
||||
bool lazy; |
||||
} compiler; |
||||
|
||||
static compiler *newcompiler(mgroup *group, bool lazy) { |
||||
compiler *ret = upb_gmalloc(sizeof(*ret)); |
||||
int i; |
||||
|
||||
ret->group = group; |
||||
ret->lazy = lazy; |
||||
for (i = 0; i < MAXLABEL; i++) { |
||||
ret->fwd_labels[i] = EMPTYLABEL; |
||||
ret->back_labels[i] = EMPTYLABEL; |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
static void freecompiler(compiler *c) { |
||||
upb_gfree(c); |
||||
} |
||||
|
||||
const size_t ptr_words = sizeof(void*) / sizeof(uint32_t); |
||||
|
||||
/* How many words an instruction is. */ |
||||
static int instruction_len(uint32_t instr) { |
||||
switch (getop(instr)) { |
||||
case OP_SETDISPATCH: return 1 + ptr_words; |
||||
case OP_TAGN: return 3; |
||||
case OP_SETBIGGROUPNUM: return 2; |
||||
default: return 1; |
||||
} |
||||
} |
||||
|
||||
bool op_has_longofs(int32_t instruction) { |
||||
switch (getop(instruction)) { |
||||
case OP_CALL: |
||||
case OP_BRANCH: |
||||
case OP_CHECKDELIM: |
||||
return true; |
||||
/* The "tag" instructions only have 8 bytes available for the jump target,
|
||||
* but that is ok because these opcodes only require short jumps. */ |
||||
case OP_TAG1: |
||||
case OP_TAG2: |
||||
case OP_TAGN: |
||||
return false; |
||||
default: |
||||
UPB_ASSERT(false); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
static int32_t getofs(uint32_t instruction) { |
||||
if (op_has_longofs(instruction)) { |
||||
return (int32_t)instruction >> 8; |
||||
} else { |
||||
return (int8_t)(instruction >> 8); |
||||
} |
||||
} |
||||
|
||||
static void setofs(uint32_t *instruction, int32_t ofs) { |
||||
if (op_has_longofs(*instruction)) { |
||||
*instruction = getop(*instruction) | (uint32_t)ofs << 8; |
||||
} else { |
||||
*instruction = (*instruction & ~0xff00) | ((ofs & 0xff) << 8); |
||||
} |
||||
UPB_ASSERT(getofs(*instruction) == ofs); /* Would fail in cases of overflow. */ |
||||
} |
||||
|
||||
static uint32_t pcofs(compiler *c) { |
||||
return (uint32_t)(c->pc - c->group->bytecode); |
||||
} |
||||
|
||||
/* Defines a local label at the current PC location. All previous forward
|
||||
* references are updated to point to this location. The location is noted |
||||
* for any future backward references. */ |
||||
static void label(compiler *c, unsigned int label) { |
||||
int val; |
||||
uint32_t *codep; |
||||
|
||||
UPB_ASSERT(label < MAXLABEL); |
||||
val = c->fwd_labels[label]; |
||||
codep = (val == EMPTYLABEL) ? NULL : c->group->bytecode + val; |
||||
while (codep) { |
||||
int ofs = getofs(*codep); |
||||
setofs(codep, (int32_t)(c->pc - codep - instruction_len(*codep))); |
||||
codep = ofs ? codep + ofs : NULL; |
||||
} |
||||
c->fwd_labels[label] = EMPTYLABEL; |
||||
c->back_labels[label] = pcofs(c); |
||||
} |
||||
|
||||
/* Creates a reference to a numbered label; either a forward reference
|
||||
* (positive arg) or backward reference (negative arg). For forward references |
||||
* the value returned now is actually a "next" pointer into a linked list of all |
||||
* instructions that use this label and will be patched later when the label is |
||||
* defined with label(). |
||||
* |
||||
* The returned value is the offset that should be written into the instruction. |
||||
*/ |
||||
static int32_t labelref(compiler *c, int label) { |
||||
UPB_ASSERT(label < MAXLABEL); |
||||
if (label == LABEL_DISPATCH) { |
||||
/* No resolving required. */ |
||||
return 0; |
||||
} else if (label < 0) { |
||||
/* Backward local label. Relative to the next instruction. */ |
||||
uint32_t from = (uint32_t)((c->pc + 1) - c->group->bytecode); |
||||
return c->back_labels[-label] - from; |
||||
} else { |
||||
/* Forward local label: prepend to (possibly-empty) linked list. */ |
||||
int *lptr = &c->fwd_labels[label]; |
||||
int32_t ret = (*lptr == EMPTYLABEL) ? 0 : *lptr - pcofs(c); |
||||
*lptr = pcofs(c); |
||||
return ret; |
||||
} |
||||
} |
||||
|
||||
static void put32(compiler *c, uint32_t v) { |
||||
mgroup *g = c->group; |
||||
if (c->pc == g->bytecode_end) { |
||||
int ofs = pcofs(c); |
||||
size_t oldsize = g->bytecode_end - g->bytecode; |
||||
size_t newsize = UPB_MAX(oldsize * 2, 64); |
||||
/* TODO(haberman): handle OOM. */ |
||||
g->bytecode = upb_grealloc(g->bytecode, oldsize * sizeof(uint32_t), |
||||
newsize * sizeof(uint32_t)); |
||||
g->bytecode_end = g->bytecode + newsize; |
||||
c->pc = g->bytecode + ofs; |
||||
} |
||||
*c->pc++ = v; |
||||
} |
||||
|
||||
static void putop(compiler *c, int op, ...) { |
||||
va_list ap; |
||||
va_start(ap, op); |
||||
|
||||
switch (op) { |
||||
case OP_SETDISPATCH: { |
||||
uintptr_t ptr = (uintptr_t)va_arg(ap, void*); |
||||
put32(c, OP_SETDISPATCH); |
||||
put32(c, (uint32_t)ptr); |
||||
if (sizeof(uintptr_t) > sizeof(uint32_t)) |
||||
put32(c, (uint64_t)ptr >> 32); |
||||
break; |
||||
} |
||||
case OP_STARTMSG: |
||||
case OP_ENDMSG: |
||||
case OP_PUSHLENDELIM: |
||||
case OP_POP: |
||||
case OP_SETDELIM: |
||||
case OP_HALT: |
||||
case OP_RET: |
||||
case OP_DISPATCH: |
||||
put32(c, op); |
||||
break; |
||||
case OP_PARSE_DOUBLE: |
||||
case OP_PARSE_FLOAT: |
||||
case OP_PARSE_INT64: |
||||
case OP_PARSE_UINT64: |
||||
case OP_PARSE_INT32: |
||||
case OP_PARSE_FIXED64: |
||||
case OP_PARSE_FIXED32: |
||||
case OP_PARSE_BOOL: |
||||
case OP_PARSE_UINT32: |
||||
case OP_PARSE_SFIXED32: |
||||
case OP_PARSE_SFIXED64: |
||||
case OP_PARSE_SINT32: |
||||
case OP_PARSE_SINT64: |
||||
case OP_STARTSEQ: |
||||
case OP_ENDSEQ: |
||||
case OP_STARTSUBMSG: |
||||
case OP_ENDSUBMSG: |
||||
case OP_STARTSTR: |
||||
case OP_STRING: |
||||
case OP_ENDSTR: |
||||
case OP_PUSHTAGDELIM: |
||||
put32(c, op | va_arg(ap, upb_selector_t) << 8); |
||||
break; |
||||
case OP_SETBIGGROUPNUM: |
||||
put32(c, op); |
||||
put32(c, va_arg(ap, int)); |
||||
break; |
||||
case OP_CALL: { |
||||
const upb_pbdecodermethod *method = va_arg(ap, upb_pbdecodermethod *); |
||||
put32(c, op | (method->code_base.ofs - (pcofs(c) + 1)) << 8); |
||||
break; |
||||
} |
||||
case OP_CHECKDELIM: |
||||
case OP_BRANCH: { |
||||
uint32_t instruction = op; |
||||
int label = va_arg(ap, int); |
||||
setofs(&instruction, labelref(c, label)); |
||||
put32(c, instruction); |
||||
break; |
||||
} |
||||
case OP_TAG1: |
||||
case OP_TAG2: { |
||||
int label = va_arg(ap, int); |
||||
uint64_t tag = va_arg(ap, uint64_t); |
||||
uint32_t instruction = (uint32_t)(op | (tag << 16)); |
||||
UPB_ASSERT(tag <= 0xffff); |
||||
setofs(&instruction, labelref(c, label)); |
||||
put32(c, instruction); |
||||
break; |
||||
} |
||||
case OP_TAGN: { |
||||
int label = va_arg(ap, int); |
||||
uint64_t tag = va_arg(ap, uint64_t); |
||||
uint32_t instruction = op | (upb_value_size(tag) << 16); |
||||
setofs(&instruction, labelref(c, label)); |
||||
put32(c, instruction); |
||||
put32(c, (uint32_t)tag); |
||||
put32(c, tag >> 32); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
va_end(ap); |
||||
} |
||||
|
||||
#if defined(UPB_DUMP_BYTECODE) |
||||
|
||||
const char *upb_pbdecoder_getopname(unsigned int op) { |
||||
#define QUOTE(x) #x |
||||
#define EXPAND_AND_QUOTE(x) QUOTE(x) |
||||
#define OPNAME(x) OP_##x |
||||
#define OP(x) case OPNAME(x): return EXPAND_AND_QUOTE(OPNAME(x)); |
||||
#define T(x) OP(PARSE_##x) |
||||
/* Keep in sync with list in decoder.int.h. */ |
||||
switch ((opcode)op) { |
||||
T(DOUBLE) T(FLOAT) T(INT64) T(UINT64) T(INT32) T(FIXED64) T(FIXED32) |
||||
T(BOOL) T(UINT32) T(SFIXED32) T(SFIXED64) T(SINT32) T(SINT64) |
||||
OP(STARTMSG) OP(ENDMSG) OP(STARTSEQ) OP(ENDSEQ) OP(STARTSUBMSG) |
||||
OP(ENDSUBMSG) OP(STARTSTR) OP(STRING) OP(ENDSTR) OP(CALL) OP(RET) |
||||
OP(PUSHLENDELIM) OP(PUSHTAGDELIM) OP(SETDELIM) OP(CHECKDELIM) |
||||
OP(BRANCH) OP(TAG1) OP(TAG2) OP(TAGN) OP(SETDISPATCH) OP(POP) |
||||
OP(SETBIGGROUPNUM) OP(DISPATCH) OP(HALT) |
||||
} |
||||
return "<unknown op>"; |
||||
#undef OP |
||||
#undef T |
||||
} |
||||
|
||||
#endif |
||||
|
||||
#ifdef UPB_DUMP_BYTECODE |
||||
|
||||
static void dumpbc(uint32_t *p, uint32_t *end, FILE *f) { |
||||
|
||||
uint32_t *begin = p; |
||||
|
||||
while (p < end) { |
||||
fprintf(f, "%p %8tx", p, p - begin); |
||||
uint32_t instr = *p++; |
||||
uint8_t op = getop(instr); |
||||
fprintf(f, " %s", upb_pbdecoder_getopname(op)); |
||||
switch ((opcode)op) { |
||||
case OP_SETDISPATCH: { |
||||
const upb_inttable *dispatch; |
||||
memcpy(&dispatch, p, sizeof(void*)); |
||||
p += ptr_words; |
||||
const upb_pbdecodermethod *method = |
||||
(void *)((char *)dispatch - |
||||
offsetof(upb_pbdecodermethod, dispatch)); |
||||
fprintf(f, " %s", upb_msgdef_fullname( |
||||
upb_handlers_msgdef(method->dest_handlers_))); |
||||
break; |
||||
} |
||||
case OP_DISPATCH: |
||||
case OP_STARTMSG: |
||||
case OP_ENDMSG: |
||||
case OP_PUSHLENDELIM: |
||||
case OP_POP: |
||||
case OP_SETDELIM: |
||||
case OP_HALT: |
||||
case OP_RET: |
||||
break; |
||||
case OP_PARSE_DOUBLE: |
||||
case OP_PARSE_FLOAT: |
||||
case OP_PARSE_INT64: |
||||
case OP_PARSE_UINT64: |
||||
case OP_PARSE_INT32: |
||||
case OP_PARSE_FIXED64: |
||||
case OP_PARSE_FIXED32: |
||||
case OP_PARSE_BOOL: |
||||
case OP_PARSE_UINT32: |
||||
case OP_PARSE_SFIXED32: |
||||
case OP_PARSE_SFIXED64: |
||||
case OP_PARSE_SINT32: |
||||
case OP_PARSE_SINT64: |
||||
case OP_STARTSEQ: |
||||
case OP_ENDSEQ: |
||||
case OP_STARTSUBMSG: |
||||
case OP_ENDSUBMSG: |
||||
case OP_STARTSTR: |
||||
case OP_STRING: |
||||
case OP_ENDSTR: |
||||
case OP_PUSHTAGDELIM: |
||||
fprintf(f, " %d", instr >> 8); |
||||
break; |
||||
case OP_SETBIGGROUPNUM: |
||||
fprintf(f, " %d", *p++); |
||||
break; |
||||
case OP_CHECKDELIM: |
||||
case OP_CALL: |
||||
case OP_BRANCH: |
||||
fprintf(f, " =>0x%tx", p + getofs(instr) - begin); |
||||
break; |
||||
case OP_TAG1: |
||||
case OP_TAG2: { |
||||
fprintf(f, " tag:0x%x", instr >> 16); |
||||
if (getofs(instr)) { |
||||
fprintf(f, " =>0x%tx", p + getofs(instr) - begin); |
||||
} |
||||
break; |
||||
} |
||||
case OP_TAGN: { |
||||
uint64_t tag = *p++; |
||||
tag |= (uint64_t)*p++ << 32; |
||||
fprintf(f, " tag:0x%llx", (long long)tag); |
||||
fprintf(f, " n:%d", instr >> 16); |
||||
if (getofs(instr)) { |
||||
fprintf(f, " =>0x%tx", p + getofs(instr) - begin); |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
fputs("\n", f); |
||||
} |
||||
} |
||||
|
||||
#endif |
||||
|
||||
static uint64_t get_encoded_tag(const upb_fielddef *f, int wire_type) { |
||||
uint32_t tag = (upb_fielddef_number(f) << 3) | wire_type; |
||||
uint64_t encoded_tag = upb_vencode32(tag); |
||||
/* No tag should be greater than 5 bytes. */ |
||||
UPB_ASSERT(encoded_tag <= 0xffffffffff); |
||||
return encoded_tag; |
||||
} |
||||
|
||||
static void putchecktag(compiler *c, const upb_fielddef *f, |
||||
int wire_type, int dest) { |
||||
uint64_t tag = get_encoded_tag(f, wire_type); |
||||
switch (upb_value_size(tag)) { |
||||
case 1: |
||||
putop(c, OP_TAG1, dest, tag); |
||||
break; |
||||
case 2: |
||||
putop(c, OP_TAG2, dest, tag); |
||||
break; |
||||
default: |
||||
putop(c, OP_TAGN, dest, tag); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
static upb_selector_t getsel(const upb_fielddef *f, upb_handlertype_t type) { |
||||
upb_selector_t selector; |
||||
bool ok = upb_handlers_getselector(f, type, &selector); |
||||
UPB_ASSERT(ok); |
||||
return selector; |
||||
} |
||||
|
||||
/* Takes an existing, primary dispatch table entry and repacks it with a
|
||||
* different alternate wire type. Called when we are inserting a secondary |
||||
* dispatch table entry for an alternate wire type. */ |
||||
static uint64_t repack(uint64_t dispatch, int new_wt2) { |
||||
uint64_t ofs; |
||||
uint8_t wt1; |
||||
uint8_t old_wt2; |
||||
upb_pbdecoder_unpackdispatch(dispatch, &ofs, &wt1, &old_wt2); |
||||
UPB_ASSERT(old_wt2 == NO_WIRE_TYPE); /* wt2 should not be set yet. */ |
||||
return upb_pbdecoder_packdispatch(ofs, wt1, new_wt2); |
||||
} |
||||
|
||||
/* Marks the current bytecode position as the dispatch target for this message,
|
||||
* field, and wire type. */ |
||||
static void dispatchtarget(compiler *c, upb_pbdecodermethod *method, |
||||
const upb_fielddef *f, int wire_type) { |
||||
/* Offset is relative to msg base. */ |
||||
uint64_t ofs = pcofs(c) - method->code_base.ofs; |
||||
uint32_t fn = upb_fielddef_number(f); |
||||
upb_inttable *d = &method->dispatch; |
||||
upb_value v; |
||||
if (upb_inttable_remove(d, fn, &v)) { |
||||
/* TODO: prioritize based on packed setting in .proto file. */ |
||||
uint64_t repacked = repack(upb_value_getuint64(v), wire_type); |
||||
upb_inttable_insert(d, fn, upb_value_uint64(repacked)); |
||||
upb_inttable_insert(d, fn + UPB_MAX_FIELDNUMBER, upb_value_uint64(ofs)); |
||||
} else { |
||||
uint64_t val = upb_pbdecoder_packdispatch(ofs, wire_type, NO_WIRE_TYPE); |
||||
upb_inttable_insert(d, fn, upb_value_uint64(val)); |
||||
} |
||||
} |
||||
|
||||
static void putpush(compiler *c, const upb_fielddef *f) { |
||||
if (upb_fielddef_descriptortype(f) == UPB_DESCRIPTOR_TYPE_MESSAGE) { |
||||
putop(c, OP_PUSHLENDELIM); |
||||
} else { |
||||
uint32_t fn = upb_fielddef_number(f); |
||||
if (fn >= 1 << 24) { |
||||
putop(c, OP_PUSHTAGDELIM, 0); |
||||
putop(c, OP_SETBIGGROUPNUM, fn); |
||||
} else { |
||||
putop(c, OP_PUSHTAGDELIM, fn); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static upb_pbdecodermethod *find_submethod(const compiler *c, |
||||
const upb_pbdecodermethod *method, |
||||
const upb_fielddef *f) { |
||||
const upb_handlers *sub = |
||||
upb_handlers_getsubhandlers(method->dest_handlers_, f); |
||||
upb_value v; |
||||
return upb_inttable_lookupptr(&c->group->methods, sub, &v) |
||||
? upb_value_getptr(v) |
||||
: NULL; |
||||
} |
||||
|
||||
static void putsel(compiler *c, opcode op, upb_selector_t sel, |
||||
const upb_handlers *h) { |
||||
if (upb_handlers_gethandler(h, sel, NULL)) { |
||||
putop(c, op, sel); |
||||
} |
||||
} |
||||
|
||||
/* Puts an opcode to call a callback, but only if a callback actually exists for
|
||||
* this field and handler type. */ |
||||
static void maybeput(compiler *c, opcode op, const upb_handlers *h, |
||||
const upb_fielddef *f, upb_handlertype_t type) { |
||||
putsel(c, op, getsel(f, type), h); |
||||
} |
||||
|
||||
static bool haslazyhandlers(const upb_handlers *h, const upb_fielddef *f) { |
||||
if (!upb_fielddef_lazy(f)) |
||||
return false; |
||||
|
||||
return upb_handlers_gethandler(h, getsel(f, UPB_HANDLER_STARTSTR), NULL) || |
||||
upb_handlers_gethandler(h, getsel(f, UPB_HANDLER_STRING), NULL) || |
||||
upb_handlers_gethandler(h, getsel(f, UPB_HANDLER_ENDSTR), NULL); |
||||
} |
||||
|
||||
|
||||
/* bytecode compiler code generation ******************************************/ |
||||
|
||||
/* Symbolic names for our local labels. */ |
||||
#define LABEL_LOOPSTART 1 /* Top of a repeated field loop. */ |
||||
#define LABEL_LOOPBREAK 2 /* To jump out of a repeated loop */ |
||||
#define LABEL_FIELD 3 /* Jump backward to find the most recent field. */ |
||||
#define LABEL_ENDMSG 4 /* To reach the OP_ENDMSG instr for this msg. */ |
||||
|
||||
/* Generates bytecode to parse a single non-lazy message field. */ |
||||
static void generate_msgfield(compiler *c, const upb_fielddef *f, |
||||
upb_pbdecodermethod *method) { |
||||
const upb_handlers *h = upb_pbdecodermethod_desthandlers(method); |
||||
const upb_pbdecodermethod *sub_m = find_submethod(c, method, f); |
||||
int wire_type; |
||||
|
||||
if (!sub_m) { |
||||
/* Don't emit any code for this field at all; it will be parsed as an
|
||||
* unknown field. |
||||
* |
||||
* TODO(haberman): we should change this to parse it as a string field |
||||
* instead. It will probably be faster, but more importantly, once we |
||||
* start vending unknown fields, a field shouldn't be treated as unknown |
||||
* just because it doesn't have subhandlers registered. */ |
||||
return; |
||||
} |
||||
|
||||
label(c, LABEL_FIELD); |
||||
|
||||
wire_type = |
||||
(upb_fielddef_descriptortype(f) == UPB_DESCRIPTOR_TYPE_MESSAGE) |
||||
? UPB_WIRE_TYPE_DELIMITED |
||||
: UPB_WIRE_TYPE_START_GROUP; |
||||
|
||||
if (upb_fielddef_isseq(f)) { |
||||
putop(c, OP_CHECKDELIM, LABEL_ENDMSG); |
||||
putchecktag(c, f, wire_type, LABEL_DISPATCH); |
||||
dispatchtarget(c, method, f, wire_type); |
||||
putop(c, OP_PUSHTAGDELIM, 0); |
||||
putop(c, OP_STARTSEQ, getsel(f, UPB_HANDLER_STARTSEQ)); |
||||
label(c, LABEL_LOOPSTART); |
||||
putpush(c, f); |
||||
putop(c, OP_STARTSUBMSG, getsel(f, UPB_HANDLER_STARTSUBMSG)); |
||||
putop(c, OP_CALL, sub_m); |
||||
putop(c, OP_POP); |
||||
maybeput(c, OP_ENDSUBMSG, h, f, UPB_HANDLER_ENDSUBMSG); |
||||
if (wire_type == UPB_WIRE_TYPE_DELIMITED) { |
||||
putop(c, OP_SETDELIM); |
||||
} |
||||
putop(c, OP_CHECKDELIM, LABEL_LOOPBREAK); |
||||
putchecktag(c, f, wire_type, LABEL_LOOPBREAK); |
||||
putop(c, OP_BRANCH, -LABEL_LOOPSTART); |
||||
label(c, LABEL_LOOPBREAK); |
||||
putop(c, OP_POP); |
||||
maybeput(c, OP_ENDSEQ, h, f, UPB_HANDLER_ENDSEQ); |
||||
} else { |
||||
putop(c, OP_CHECKDELIM, LABEL_ENDMSG); |
||||
putchecktag(c, f, wire_type, LABEL_DISPATCH); |
||||
dispatchtarget(c, method, f, wire_type); |
||||
putpush(c, f); |
||||
putop(c, OP_STARTSUBMSG, getsel(f, UPB_HANDLER_STARTSUBMSG)); |
||||
putop(c, OP_CALL, sub_m); |
||||
putop(c, OP_POP); |
||||
maybeput(c, OP_ENDSUBMSG, h, f, UPB_HANDLER_ENDSUBMSG); |
||||
if (wire_type == UPB_WIRE_TYPE_DELIMITED) { |
||||
putop(c, OP_SETDELIM); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* Generates bytecode to parse a single string or lazy submessage field. */ |
||||
static void generate_delimfield(compiler *c, const upb_fielddef *f, |
||||
upb_pbdecodermethod *method) { |
||||
const upb_handlers *h = upb_pbdecodermethod_desthandlers(method); |
||||
|
||||
label(c, LABEL_FIELD); |
||||
if (upb_fielddef_isseq(f)) { |
||||
putop(c, OP_CHECKDELIM, LABEL_ENDMSG); |
||||
putchecktag(c, f, UPB_WIRE_TYPE_DELIMITED, LABEL_DISPATCH); |
||||
dispatchtarget(c, method, f, UPB_WIRE_TYPE_DELIMITED); |
||||
putop(c, OP_PUSHTAGDELIM, 0); |
||||
putop(c, OP_STARTSEQ, getsel(f, UPB_HANDLER_STARTSEQ)); |
||||
label(c, LABEL_LOOPSTART); |
||||
putop(c, OP_PUSHLENDELIM); |
||||
putop(c, OP_STARTSTR, getsel(f, UPB_HANDLER_STARTSTR)); |
||||
/* Need to emit even if no handler to skip past the string. */ |
||||
putop(c, OP_STRING, getsel(f, UPB_HANDLER_STRING)); |
||||
maybeput(c, OP_ENDSTR, h, f, UPB_HANDLER_ENDSTR); |
||||
putop(c, OP_POP); |
||||
putop(c, OP_SETDELIM); |
||||
putop(c, OP_CHECKDELIM, LABEL_LOOPBREAK); |
||||
putchecktag(c, f, UPB_WIRE_TYPE_DELIMITED, LABEL_LOOPBREAK); |
||||
putop(c, OP_BRANCH, -LABEL_LOOPSTART); |
||||
label(c, LABEL_LOOPBREAK); |
||||
putop(c, OP_POP); |
||||
maybeput(c, OP_ENDSEQ, h, f, UPB_HANDLER_ENDSEQ); |
||||
} else { |
||||
putop(c, OP_CHECKDELIM, LABEL_ENDMSG); |
||||
putchecktag(c, f, UPB_WIRE_TYPE_DELIMITED, LABEL_DISPATCH); |
||||
dispatchtarget(c, method, f, UPB_WIRE_TYPE_DELIMITED); |
||||
putop(c, OP_PUSHLENDELIM); |
||||
putop(c, OP_STARTSTR, getsel(f, UPB_HANDLER_STARTSTR)); |
||||
putop(c, OP_STRING, getsel(f, UPB_HANDLER_STRING)); |
||||
maybeput(c, OP_ENDSTR, h, f, UPB_HANDLER_ENDSTR); |
||||
putop(c, OP_POP); |
||||
putop(c, OP_SETDELIM); |
||||
} |
||||
} |
||||
|
||||
/* Generates bytecode to parse a single primitive field. */ |
||||
static void generate_primitivefield(compiler *c, const upb_fielddef *f, |
||||
upb_pbdecodermethod *method) { |
||||
const upb_handlers *h = upb_pbdecodermethod_desthandlers(method); |
||||
upb_descriptortype_t descriptor_type = upb_fielddef_descriptortype(f); |
||||
opcode parse_type; |
||||
upb_selector_t sel; |
||||
int wire_type; |
||||
|
||||
label(c, LABEL_FIELD); |
||||
|
||||
/* From a decoding perspective, ENUM is the same as INT32. */ |
||||
if (descriptor_type == UPB_DESCRIPTOR_TYPE_ENUM) |
||||
descriptor_type = UPB_DESCRIPTOR_TYPE_INT32; |
||||
|
||||
parse_type = (opcode)descriptor_type; |
||||
|
||||
/* TODO(haberman): generate packed or non-packed first depending on "packed"
|
||||
* setting in the fielddef. This will favor (in speed) whichever was |
||||
* specified. */ |
||||
|
||||
UPB_ASSERT((int)parse_type >= 0 && parse_type <= OP_MAX); |
||||
sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); |
||||
wire_type = upb_pb_native_wire_types[upb_fielddef_descriptortype(f)]; |
||||
if (upb_fielddef_isseq(f)) { |
||||
putop(c, OP_CHECKDELIM, LABEL_ENDMSG); |
||||
putchecktag(c, f, UPB_WIRE_TYPE_DELIMITED, LABEL_DISPATCH); |
||||
dispatchtarget(c, method, f, UPB_WIRE_TYPE_DELIMITED); |
||||
putop(c, OP_PUSHLENDELIM); |
||||
putop(c, OP_STARTSEQ, getsel(f, UPB_HANDLER_STARTSEQ)); /* Packed */ |
||||
label(c, LABEL_LOOPSTART); |
||||
putop(c, parse_type, sel); |
||||
putop(c, OP_CHECKDELIM, LABEL_LOOPBREAK); |
||||
putop(c, OP_BRANCH, -LABEL_LOOPSTART); |
||||
dispatchtarget(c, method, f, wire_type); |
||||
putop(c, OP_PUSHTAGDELIM, 0); |
||||
putop(c, OP_STARTSEQ, getsel(f, UPB_HANDLER_STARTSEQ)); /* Non-packed */ |
||||
label(c, LABEL_LOOPSTART); |
||||
putop(c, parse_type, sel); |
||||
putop(c, OP_CHECKDELIM, LABEL_LOOPBREAK); |
||||
putchecktag(c, f, wire_type, LABEL_LOOPBREAK); |
||||
putop(c, OP_BRANCH, -LABEL_LOOPSTART); |
||||
label(c, LABEL_LOOPBREAK); |
||||
putop(c, OP_POP); /* Packed and non-packed join. */ |
||||
maybeput(c, OP_ENDSEQ, h, f, UPB_HANDLER_ENDSEQ); |
||||
putop(c, OP_SETDELIM); /* Could remove for non-packed by dup ENDSEQ. */ |
||||
} else { |
||||
putop(c, OP_CHECKDELIM, LABEL_ENDMSG); |
||||
putchecktag(c, f, wire_type, LABEL_DISPATCH); |
||||
dispatchtarget(c, method, f, wire_type); |
||||
putop(c, parse_type, sel); |
||||
} |
||||
} |
||||
|
||||
/* Adds bytecode for parsing the given message to the given decoderplan,
|
||||
* while adding all dispatch targets to this message's dispatch table. */ |
||||
static void compile_method(compiler *c, upb_pbdecodermethod *method) { |
||||
const upb_handlers *h; |
||||
const upb_msgdef *md; |
||||
uint32_t* start_pc; |
||||
int i, n; |
||||
upb_value val; |
||||
|
||||
UPB_ASSERT(method); |
||||
|
||||
/* Clear all entries in the dispatch table. */ |
||||
upb_inttable_uninit(&method->dispatch); |
||||
upb_inttable_init(&method->dispatch, UPB_CTYPE_UINT64); |
||||
|
||||
h = upb_pbdecodermethod_desthandlers(method); |
||||
md = upb_handlers_msgdef(h); |
||||
|
||||
method->code_base.ofs = pcofs(c); |
||||
putop(c, OP_SETDISPATCH, &method->dispatch); |
||||
putsel(c, OP_STARTMSG, UPB_STARTMSG_SELECTOR, h); |
||||
label(c, LABEL_FIELD); |
||||
start_pc = c->pc; |
||||
n = upb_msgdef_fieldcount(md); |
||||
for(i = 0; i < n; i++) { |
||||
const upb_fielddef *f = upb_msgdef_field(md, i); |
||||
upb_fieldtype_t type = upb_fielddef_type(f); |
||||
|
||||
if (type == UPB_TYPE_MESSAGE && !(haslazyhandlers(h, f) && c->lazy)) { |
||||
generate_msgfield(c, f, method); |
||||
} else if (type == UPB_TYPE_STRING || type == UPB_TYPE_BYTES || |
||||
type == UPB_TYPE_MESSAGE) { |
||||
generate_delimfield(c, f, method); |
||||
} else { |
||||
generate_primitivefield(c, f, method); |
||||
} |
||||
} |
||||
|
||||
/* If there were no fields, or if no handlers were defined, we need to
|
||||
* generate a non-empty loop body so that we can at least dispatch for unknown |
||||
* fields and check for the end of the message. */ |
||||
if (c->pc == start_pc) { |
||||
/* Check for end-of-message. */ |
||||
putop(c, OP_CHECKDELIM, LABEL_ENDMSG); |
||||
/* Unconditionally dispatch. */ |
||||
putop(c, OP_DISPATCH, 0); |
||||
} |
||||
|
||||
/* For now we just loop back to the last field of the message (or if none,
|
||||
* the DISPATCH opcode for the message). */ |
||||
putop(c, OP_BRANCH, -LABEL_FIELD); |
||||
|
||||
/* Insert both a label and a dispatch table entry for this end-of-msg. */ |
||||
label(c, LABEL_ENDMSG); |
||||
val = upb_value_uint64(pcofs(c) - method->code_base.ofs); |
||||
upb_inttable_insert(&method->dispatch, DISPATCH_ENDMSG, val); |
||||
|
||||
putsel(c, OP_ENDMSG, UPB_ENDMSG_SELECTOR, h); |
||||
putop(c, OP_RET); |
||||
|
||||
upb_inttable_compact(&method->dispatch); |
||||
} |
||||
|
||||
/* Populate "methods" with new upb_pbdecodermethod objects reachable from "h".
|
||||
* Returns the method for these handlers. |
||||
* |
||||
* Generates a new method for every destination handlers reachable from "h". */ |
||||
static void find_methods(compiler *c, const upb_handlers *h) { |
||||
upb_value v; |
||||
int i, n; |
||||
const upb_msgdef *md; |
||||
upb_pbdecodermethod *method; |
||||
|
||||
if (upb_inttable_lookupptr(&c->group->methods, h, &v)) |
||||
return; |
||||
|
||||
method = newmethod(h, c->group); |
||||
upb_inttable_insertptr(&c->group->methods, h, upb_value_ptr(method)); |
||||
|
||||
/* Find submethods. */ |
||||
md = upb_handlers_msgdef(h); |
||||
n = upb_msgdef_fieldcount(md); |
||||
for (i = 0; i < n; i++) { |
||||
const upb_fielddef *f = upb_msgdef_field(md, i); |
||||
const upb_handlers *sub_h; |
||||
if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE && |
||||
(sub_h = upb_handlers_getsubhandlers(h, f)) != NULL) { |
||||
/* We only generate a decoder method for submessages with handlers.
|
||||
* Others will be parsed as unknown fields. */ |
||||
find_methods(c, sub_h); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* (Re-)compile bytecode for all messages in "msgs."
|
||||
* Overwrites any existing bytecode in "c". */ |
||||
static void compile_methods(compiler *c) { |
||||
upb_inttable_iter i; |
||||
|
||||
/* Start over at the beginning of the bytecode. */ |
||||
c->pc = c->group->bytecode; |
||||
|
||||
upb_inttable_begin(&i, &c->group->methods); |
||||
for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { |
||||
upb_pbdecodermethod *method = upb_value_getptr(upb_inttable_iter_value(&i)); |
||||
compile_method(c, method); |
||||
} |
||||
} |
||||
|
||||
static void set_bytecode_handlers(mgroup *g) { |
||||
upb_inttable_iter i; |
||||
upb_inttable_begin(&i, &g->methods); |
||||
for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { |
||||
upb_pbdecodermethod *m = upb_value_getptr(upb_inttable_iter_value(&i)); |
||||
upb_byteshandler *h = &m->input_handler_; |
||||
|
||||
m->code_base.ptr = g->bytecode + m->code_base.ofs; |
||||
|
||||
upb_byteshandler_setstartstr(h, upb_pbdecoder_startbc, m->code_base.ptr); |
||||
upb_byteshandler_setstring(h, upb_pbdecoder_decode, g); |
||||
upb_byteshandler_setendstr(h, upb_pbdecoder_end, m); |
||||
} |
||||
} |
||||
|
||||
|
||||
/* 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 lazy) { |
||||
mgroup *g; |
||||
compiler *c; |
||||
|
||||
g = newgroup(); |
||||
c = newcompiler(g, lazy); |
||||
find_methods(c, dest); |
||||
|
||||
/* We compile in two passes:
|
||||
* 1. all messages are assigned relative offsets from the beginning of the |
||||
* bytecode (saved in method->code_base). |
||||
* 2. forwards OP_CALL instructions can be correctly linked since message |
||||
* offsets have been previously assigned. |
||||
* |
||||
* Could avoid the second pass by linking OP_CALL instructions somehow. */ |
||||
compile_methods(c); |
||||
compile_methods(c); |
||||
g->bytecode_end = c->pc; |
||||
freecompiler(c); |
||||
|
||||
#ifdef UPB_DUMP_BYTECODE |
||||
{ |
||||
FILE *f = fopen("/tmp/upb-bytecode", "w"); |
||||
UPB_ASSERT(f); |
||||
dumpbc(g->bytecode, g->bytecode_end, stderr); |
||||
dumpbc(g->bytecode, g->bytecode_end, f); |
||||
fclose(f); |
||||
|
||||
f = fopen("/tmp/upb-bytecode.bin", "wb"); |
||||
UPB_ASSERT(f); |
||||
fwrite(g->bytecode, 1, g->bytecode_end - g->bytecode, f); |
||||
fclose(f); |
||||
} |
||||
#endif |
||||
|
||||
set_bytecode_handlers(g); |
||||
return g; |
||||
} |
||||
|
||||
|
||||
/* upb_pbcodecache ************************************************************/ |
||||
|
||||
upb_pbcodecache *upb_pbcodecache_new(upb_handlercache *dest) { |
||||
upb_pbcodecache *c = upb_gmalloc(sizeof(*c)); |
||||
|
||||
if (!c) return NULL; |
||||
|
||||
c->dest = dest; |
||||
c->lazy = false; |
||||
|
||||
c->arena = upb_arena_new(); |
||||
if (!upb_inttable_init(&c->groups, UPB_CTYPE_CONSTPTR)) return NULL; |
||||
|
||||
return c; |
||||
} |
||||
|
||||
void upb_pbcodecache_free(upb_pbcodecache *c) { |
||||
upb_inttable_iter i; |
||||
|
||||
upb_inttable_begin(&i, &c->groups); |
||||
for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { |
||||
upb_value val = upb_inttable_iter_value(&i); |
||||
freegroup((void*)upb_value_getconstptr(val)); |
||||
} |
||||
|
||||
upb_inttable_uninit(&c->groups); |
||||
upb_arena_free(c->arena); |
||||
upb_gfree(c); |
||||
} |
||||
|
||||
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; |
||||
|
||||
h = upb_handlercache_get(c->dest, md); |
||||
if (upb_inttable_lookupptr(&c->groups, md, &v)) { |
||||
g = upb_value_getconstptr(v); |
||||
} else { |
||||
g = mgroup_new(h, c->lazy); |
||||
ok = upb_inttable_insertptr(&c->groups, md, upb_value_constptr(g)); |
||||
UPB_ASSUME(ok); |
||||
} |
||||
|
||||
ok = upb_inttable_lookupptr(&g->methods, h, &v); |
||||
UPB_ASSUME(ok); |
||||
return upb_value_getptr(v); |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -1,242 +0,0 @@ |
||||
/*
|
||||
** upb::pb::Decoder |
||||
** |
||||
** A high performance, streaming, resumable decoder for the binary protobuf |
||||
** format. |
||||
** |
||||
** This interface works the same regardless of what decoder backend is being |
||||
** used. A client of this class does not need to know whether decoding is using |
||||
** a JITted decoder (DynASM, LLVM, etc) or an interpreted decoder. By default, |
||||
** it will always use the fastest available decoder. However, you can call |
||||
** set_allow_jit(false) to disable any JIT decoder that might be available. |
||||
** This is primarily useful for testing purposes. |
||||
*/ |
||||
|
||||
#ifndef UPB_DECODER_H_ |
||||
#define UPB_DECODER_H_ |
||||
|
||||
#include "upb/sink.h" |
||||
|
||||
#ifdef __cplusplus |
||||
namespace upb { |
||||
namespace pb { |
||||
class CodeCache; |
||||
class DecoderPtr; |
||||
class DecoderMethodPtr; |
||||
class DecoderMethodOptions; |
||||
} /* namespace pb */ |
||||
} /* namespace upb */ |
||||
#endif |
||||
|
||||
/* 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 |
||||
* varint, less one because we are buffering an incomplete value. |
||||
* |
||||
* Should only be used by unit tests. */ |
||||
#define UPB_DECODER_MAX_RESIDUAL_BYTES 14 |
||||
|
||||
/* upb_pbdecodermethod ********************************************************/ |
||||
|
||||
struct upb_pbdecodermethod; |
||||
typedef struct upb_pbdecodermethod upb_pbdecodermethod; |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
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); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
|
||||
/* Represents the code to parse a protobuf according to a destination
|
||||
* Handlers. */ |
||||
class upb::pb::DecoderMethodPtr { |
||||
public: |
||||
DecoderMethodPtr() : ptr_(nullptr) {} |
||||
DecoderMethodPtr(const upb_pbdecodermethod* ptr) : ptr_(ptr) {} |
||||
|
||||
const upb_pbdecodermethod* ptr() { return ptr_; } |
||||
|
||||
/* 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 { |
||||
return upb_pbdecodermethod_desthandlers(ptr_); |
||||
} |
||||
|
||||
/* The input handlers for this decoder method. */ |
||||
const BytesHandler* input_handler() const { |
||||
return upb_pbdecodermethod_inputhandler(ptr_); |
||||
} |
||||
|
||||
/* Whether this method is native. */ |
||||
bool is_native() const { |
||||
return upb_pbdecodermethod_isnative(ptr_); |
||||
} |
||||
|
||||
private: |
||||
const upb_pbdecodermethod* ptr_; |
||||
}; |
||||
|
||||
#endif |
||||
|
||||
/* upb_pbdecoder **************************************************************/ |
||||
|
||||
/* Preallocation hint: decoder won't allocate more bytes than this when first
|
||||
* constructed. This hint may be an overestimate for some build configurations. |
||||
* But if the decoder library is upgraded without recompiling the application, |
||||
* it may be an underestimate. */ |
||||
#define UPB_PB_DECODER_SIZE 4416 |
||||
|
||||
struct upb_pbdecoder; |
||||
typedef struct upb_pbdecoder upb_pbdecoder; |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
upb_pbdecoder *upb_pbdecoder_create(upb_arena *arena, |
||||
const upb_pbdecodermethod *method, |
||||
upb_sink output, upb_status *status); |
||||
const upb_pbdecodermethod *upb_pbdecoder_method(const upb_pbdecoder *d); |
||||
upb_bytessink upb_pbdecoder_input(upb_pbdecoder *d); |
||||
uint64_t upb_pbdecoder_bytesparsed(const upb_pbdecoder *d); |
||||
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); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
|
||||
/* A Decoder receives binary protobuf data on its input sink and pushes the
|
||||
* decoded data to its output sink. */ |
||||
class upb::pb::DecoderPtr { |
||||
public: |
||||
DecoderPtr() : ptr_(nullptr) {} |
||||
DecoderPtr(upb_pbdecoder* ptr) : ptr_(ptr) {} |
||||
|
||||
upb_pbdecoder* ptr() { return ptr_; } |
||||
|
||||
/* Constructs a decoder instance for the given method, which must outlive this
|
||||
* decoder. Any errors during parsing will be set on the given status, which |
||||
* must also outlive this decoder. |
||||
* |
||||
* The sink must match the given method. */ |
||||
static DecoderPtr Create(Arena *arena, DecoderMethodPtr method, |
||||
upb::Sink output, Status *status) { |
||||
return DecoderPtr(upb_pbdecoder_create(arena->ptr(), method.ptr(), |
||||
output.sink(), status->ptr())); |
||||
} |
||||
|
||||
/* Returns the DecoderMethod this decoder is parsing from. */ |
||||
const DecoderMethodPtr method() const { |
||||
return DecoderMethodPtr(upb_pbdecoder_method(ptr_)); |
||||
} |
||||
|
||||
/* The sink on which this decoder receives input. */ |
||||
BytesSink input() { return BytesSink(upb_pbdecoder_input(ptr())); } |
||||
|
||||
/* Returns number of bytes successfully parsed.
|
||||
* |
||||
* This can be useful for determining the stream position where an error |
||||
* occurred. |
||||
* |
||||
* This value may not be up-to-date when called from inside a parsing |
||||
* callback. */ |
||||
uint64_t BytesParsed() { return upb_pbdecoder_bytesparsed(ptr()); } |
||||
|
||||
/* Gets/sets the parsing nexting limit. If the total number of nested
|
||||
* submessages and repeated fields hits this limit, parsing will fail. This |
||||
* is a resource limit that controls the amount of memory used by the parsing |
||||
* stack. |
||||
* |
||||
* Setting the limit will fail if the parser is currently suspended at a depth |
||||
* greater than this, or if memory allocation of the stack fails. */ |
||||
size_t max_nesting() { return upb_pbdecoder_maxnesting(ptr()); } |
||||
bool set_max_nesting(size_t max) { |
||||
return upb_pbdecoder_setmaxnesting(ptr(), max); |
||||
} |
||||
|
||||
void Reset() { upb_pbdecoder_reset(ptr()); } |
||||
|
||||
static const size_t kSize = UPB_PB_DECODER_SIZE; |
||||
|
||||
private: |
||||
upb_pbdecoder *ptr_; |
||||
}; |
||||
|
||||
#endif /* __cplusplus */ |
||||
|
||||
/* upb_pbcodecache ************************************************************/ |
||||
|
||||
/* Lazily builds and caches decoder methods that will push data to the given
|
||||
* handlers. The destination handlercache must outlive this object. */ |
||||
|
||||
struct upb_pbcodecache; |
||||
typedef struct upb_pbcodecache upb_pbcodecache; |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
upb_pbcodecache *upb_pbcodecache_new(upb_handlercache *dest); |
||||
void upb_pbcodecache_free(upb_pbcodecache *c); |
||||
bool upb_pbcodecache_allowjit(const upb_pbcodecache *c); |
||||
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); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
|
||||
/* 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. */ |
||||
class upb::pb::CodeCache { |
||||
public: |
||||
CodeCache(upb::HandlerCache *dest) |
||||
: ptr_(upb_pbcodecache_new(dest->ptr()), upb_pbcodecache_free) {} |
||||
CodeCache(CodeCache&&) = default; |
||||
CodeCache& operator=(CodeCache&&) = default; |
||||
|
||||
upb_pbcodecache* ptr() { return ptr_.get(); } |
||||
const upb_pbcodecache* ptr() const { return ptr_.get(); } |
||||
|
||||
/* 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 |
||||
* having a specific problem with the JIT. |
||||
* |
||||
* Note that allow_jit = true does not *guarantee* that the code will be JIT |
||||
* compiled. If this platform is not supported or the JIT was not compiled |
||||
* in, the code may still be interpreted. */ |
||||
bool allow_jit() const { return upb_pbcodecache_allowjit(ptr()); } |
||||
|
||||
/* This may only be called when the object is first constructed, and prior to
|
||||
* any code generation. */ |
||||
void set_allow_jit(bool allow) { upb_pbcodecache_setallowjit(ptr(), allow); } |
||||
|
||||
/* 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) { upb_pbcodecache_setlazy(ptr(), lazy); } |
||||
|
||||
/* 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 DecoderMethodPtr Get(MessageDefPtr md) { |
||||
return DecoderMethodPtr(upb_pbcodecache_get(ptr(), md.ptr())); |
||||
} |
||||
|
||||
private: |
||||
std::unique_ptr<upb_pbcodecache, decltype(&upb_pbcodecache_free)> ptr_; |
||||
}; |
||||
|
||||
#endif /* __cplusplus */ |
||||
|
||||
#endif /* UPB_DECODER_H_ */ |
@ -1,288 +0,0 @@ |
||||
/*
|
||||
** Internal-only definitions for the decoder. |
||||
*/ |
||||
|
||||
#ifndef UPB_DECODER_INT_H_ |
||||
#define UPB_DECODER_INT_H_ |
||||
|
||||
#include "upb/def.h" |
||||
#include "upb/handlers.h" |
||||
#include "upb/pb/decoder.h" |
||||
#include "upb/sink.h" |
||||
#include "upb/table.int.h" |
||||
|
||||
#include "upb/port_def.inc" |
||||
|
||||
/* Opcode definitions. The canonical meaning of each opcode is its
|
||||
* implementation in the interpreter (the JIT is written to match this). |
||||
* |
||||
* All instructions have the opcode in the low byte. |
||||
* Instruction format for most instructions is: |
||||
* |
||||
* +-------------------+--------+ |
||||
* | arg (24) | op (8) | |
||||
* +-------------------+--------+ |
||||
* |
||||
* Exceptions are indicated below. A few opcodes are multi-word. */ |
||||
typedef enum { |
||||
/* Opcodes 1-8, 13, 15-18 parse their respective descriptor types.
|
||||
* Arg for all of these is the upb selector for this field. */ |
||||
#define T(type) OP_PARSE_ ## type = UPB_DESCRIPTOR_TYPE_ ## type |
||||
T(DOUBLE), T(FLOAT), T(INT64), T(UINT64), T(INT32), T(FIXED64), T(FIXED32), |
||||
T(BOOL), T(UINT32), T(SFIXED32), T(SFIXED64), T(SINT32), T(SINT64), |
||||
#undef T |
||||
OP_STARTMSG = 9, /* No arg. */ |
||||
OP_ENDMSG = 10, /* No arg. */ |
||||
OP_STARTSEQ = 11, |
||||
OP_ENDSEQ = 12, |
||||
OP_STARTSUBMSG = 14, |
||||
OP_ENDSUBMSG = 19, |
||||
OP_STARTSTR = 20, |
||||
OP_STRING = 21, |
||||
OP_ENDSTR = 22, |
||||
|
||||
OP_PUSHTAGDELIM = 23, /* No arg. */ |
||||
OP_PUSHLENDELIM = 24, /* No arg. */ |
||||
OP_POP = 25, /* No arg. */ |
||||
OP_SETDELIM = 26, /* No arg. */ |
||||
OP_SETBIGGROUPNUM = 27, /* two words:
|
||||
* | unused (24) | opc (8) | |
||||
* | groupnum (32) | */ |
||||
OP_CHECKDELIM = 28, |
||||
OP_CALL = 29, |
||||
OP_RET = 30, |
||||
OP_BRANCH = 31, |
||||
|
||||
/* Different opcodes depending on how many bytes expected. */ |
||||
OP_TAG1 = 32, /* | match tag (16) | jump target (8) | opc (8) | */ |
||||
OP_TAG2 = 33, /* | match tag (16) | jump target (8) | opc (8) | */ |
||||
OP_TAGN = 34, /* three words: */ |
||||
/* | unused (16) | jump target(8) | opc (8) | */ |
||||
/* | match tag 1 (32) | */ |
||||
/* | match tag 2 (32) | */ |
||||
|
||||
OP_SETDISPATCH = 35, /* N words: */ |
||||
/* | unused (24) | opc | */ |
||||
/* | upb_inttable* (32 or 64) | */ |
||||
|
||||
OP_DISPATCH = 36, /* No arg. */ |
||||
|
||||
OP_HALT = 37 /* No arg. */ |
||||
} opcode; |
||||
|
||||
#define OP_MAX OP_HALT |
||||
|
||||
UPB_INLINE opcode getop(uint32_t instr) { return (opcode)(instr & 0xff); } |
||||
|
||||
struct upb_pbcodecache { |
||||
upb_arena *arena; |
||||
upb_handlercache *dest; |
||||
bool allow_jit; |
||||
bool lazy; |
||||
|
||||
/* Map of upb_msgdef -> mgroup. */ |
||||
upb_inttable groups; |
||||
}; |
||||
|
||||
/* Method group; represents a set of decoder methods that had their code
|
||||
* 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; |
||||
|
||||
/* The bytecode for our methods, if any exists. Owned by us. */ |
||||
uint32_t *bytecode; |
||||
uint32_t *bytecode_end; |
||||
} 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 |
||||
* setting it high will cause the upb::pb::Decoder object to be larger. |
||||
* |
||||
* If necessary we can add a runtime-settable property to Decoder that allow |
||||
* this to be larger than the compile-time setting, but this would add |
||||
* complexity, particularly since we would have to decide how/if to give users |
||||
* the ability to set a custom memory allocation function. */ |
||||
#define UPB_DECODER_MAX_NESTING 64 |
||||
|
||||
/* Internal-only struct used by the decoder. */ |
||||
typedef struct { |
||||
/* Space optimization note: we store two pointers here that the JIT
|
||||
* doesn't need at all; the upb_handlers* inside the sink and |
||||
* the dispatch table pointer. We can optimze so that the JIT uses |
||||
* smaller stack frames than the interpreter. The only thing we need |
||||
* to guarantee is that the fallback routines can find end_ofs. */ |
||||
upb_sink sink; |
||||
|
||||
/* The absolute stream offset of the end-of-frame delimiter.
|
||||
* Non-delimited frames (groups and non-packed repeated fields) reuse the |
||||
* delimiter of their parent, even though the frame may not end there. |
||||
* |
||||
* NOTE: the JIT stores a slightly different value here for non-top frames. |
||||
* It stores the value relative to the end of the enclosed message. But the |
||||
* top frame is still stored the same way, which is important for ensuring |
||||
* that calls from the JIT into C work correctly. */ |
||||
uint64_t end_ofs; |
||||
const uint32_t *base; |
||||
|
||||
/* 0 indicates a length-delimited field.
|
||||
* A positive number indicates a known group. |
||||
* A negative number indicates an unknown group. */ |
||||
int32_t groupnum; |
||||
upb_inttable *dispatch; /* Not used by the JIT. */ |
||||
} upb_pbdecoder_frame; |
||||
|
||||
struct upb_pbdecodermethod { |
||||
/* While compiling, the base is relative in "ofs", after compiling it is
|
||||
* absolute in "ptr". */ |
||||
union { |
||||
uint32_t ofs; /* PC offset of method. */ |
||||
void *ptr; /* Pointer to bytecode or machine code for this method. */ |
||||
} code_base; |
||||
|
||||
/* The decoder method group to which this method belongs. */ |
||||
const mgroup *group; |
||||
|
||||
/* Whether this method is native code or bytecode. */ |
||||
bool is_native_; |
||||
|
||||
/* The handler one calls to invoke this method. */ |
||||
upb_byteshandler input_handler_; |
||||
|
||||
/* The destination handlers this method is bound to. We own a ref. */ |
||||
const upb_handlers *dest_handlers_; |
||||
|
||||
/* Dispatch table -- used by both bytecode decoder and JIT when encountering a
|
||||
* field number that wasn't the one we were expecting to see. See |
||||
* decoder.int.h for the layout of this table. */ |
||||
upb_inttable dispatch; |
||||
}; |
||||
|
||||
struct upb_pbdecoder { |
||||
upb_arena *arena; |
||||
|
||||
/* Our input sink. */ |
||||
upb_bytessink input_; |
||||
|
||||
/* The decoder method we are parsing with (owned). */ |
||||
const upb_pbdecodermethod *method_; |
||||
|
||||
size_t call_len; |
||||
const uint32_t *pc, *last; |
||||
|
||||
/* Current input buffer and its stream offset. */ |
||||
const char *buf, *ptr, *end, *checkpoint; |
||||
|
||||
/* End of the delimited region, relative to ptr, NULL if not in this buf. */ |
||||
const char *delim_end; |
||||
|
||||
/* End of the delimited region, relative to ptr, end if not in this buf. */ |
||||
const char *data_end; |
||||
|
||||
/* Overall stream offset of "buf." */ |
||||
uint64_t bufstart_ofs; |
||||
|
||||
/* Buffer for residual bytes not parsed from the previous buffer. */ |
||||
char residual[UPB_DECODER_MAX_RESIDUAL_BYTES]; |
||||
char *residual_end; |
||||
|
||||
/* Bytes of data that should be discarded from the input beore we start
|
||||
* parsing again. We set this when we internally determine that we can |
||||
* safely skip the next N bytes, but this region extends past the current |
||||
* user buffer. */ |
||||
size_t skip; |
||||
|
||||
/* Stores the user buffer passed to our decode function. */ |
||||
const char *buf_param; |
||||
size_t size_param; |
||||
const upb_bufhandle *handle; |
||||
|
||||
/* Our internal stack. */ |
||||
upb_pbdecoder_frame *stack, *top, *limit; |
||||
const uint32_t **callstack; |
||||
size_t stack_size; |
||||
|
||||
upb_status *status; |
||||
}; |
||||
|
||||
/* Decoder entry points; used as handlers. */ |
||||
void *upb_pbdecoder_startbc(void *closure, const void *pc, size_t size_hint); |
||||
size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf, |
||||
size_t size, const upb_bufhandle *handle); |
||||
bool upb_pbdecoder_end(void *closure, const void *handler_data); |
||||
|
||||
/* Decoder-internal functions that the JIT calls to handle fallback paths. */ |
||||
int32_t upb_pbdecoder_resume(upb_pbdecoder *d, void *p, const char *buf, |
||||
size_t size, const upb_bufhandle *handle); |
||||
size_t upb_pbdecoder_suspend(upb_pbdecoder *d); |
||||
int32_t upb_pbdecoder_skipunknown(upb_pbdecoder *d, int32_t fieldnum, |
||||
uint8_t wire_type); |
||||
int32_t upb_pbdecoder_checktag_slow(upb_pbdecoder *d, uint64_t expected); |
||||
int32_t upb_pbdecoder_decode_varint_slow(upb_pbdecoder *d, uint64_t *u64); |
||||
int32_t upb_pbdecoder_decode_f32(upb_pbdecoder *d, uint32_t *u32); |
||||
int32_t upb_pbdecoder_decode_f64(upb_pbdecoder *d, uint64_t *u64); |
||||
void upb_pbdecoder_seterr(upb_pbdecoder *d, const char *msg); |
||||
|
||||
/* Error messages that are shared between the bytecode and JIT decoders. */ |
||||
extern const char *kPbDecoderStackOverflow; |
||||
extern const char *kPbDecoderSubmessageTooLong; |
||||
|
||||
/* Access to decoderplan members needed by the decoder. */ |
||||
const char *upb_pbdecoder_getopname(unsigned int op); |
||||
|
||||
/* A special label that means "do field dispatch for this message and branch to
|
||||
* wherever that takes you." */ |
||||
#define LABEL_DISPATCH 0 |
||||
|
||||
/* A special slot in the dispatch table that stores the epilogue (ENDMSG and/or
|
||||
* RET) for branching to when we find an appropriate ENDGROUP tag. */ |
||||
#define DISPATCH_ENDMSG 0 |
||||
|
||||
/* It's important to use this invalid wire type instead of 0 (which is a valid
|
||||
* wire type). */ |
||||
#define NO_WIRE_TYPE 0xff |
||||
|
||||
/* The dispatch table layout is:
|
||||
* [field number] -> [ 48-bit offset ][ 8-bit wt2 ][ 8-bit wt1 ] |
||||
* |
||||
* If wt1 matches, jump to the 48-bit offset. If wt2 matches, lookup |
||||
* (UPB_MAX_FIELDNUMBER + fieldnum) and jump there. |
||||
* |
||||
* We need two wire types because of packed/non-packed compatibility. A |
||||
* primitive repeated field can use either wire type and be valid. While we |
||||
* could key the table on fieldnum+wiretype, the table would be 8x sparser. |
||||
* |
||||
* Storing two wire types in the primary value allows us to quickly rule out |
||||
* the second wire type without needing to do a separate lookup (this case is |
||||
* less common than an unknown field). */ |
||||
UPB_INLINE uint64_t upb_pbdecoder_packdispatch(uint64_t ofs, uint8_t wt1, |
||||
uint8_t wt2) { |
||||
return (ofs << 16) | (wt2 << 8) | wt1; |
||||
} |
||||
|
||||
UPB_INLINE void upb_pbdecoder_unpackdispatch(uint64_t dispatch, uint64_t *ofs, |
||||
uint8_t *wt1, uint8_t *wt2) { |
||||
*wt1 = (uint8_t)dispatch; |
||||
*wt2 = (uint8_t)(dispatch >> 8); |
||||
*ofs = dispatch >> 16; |
||||
} |
||||
|
||||
/* All of the functions in decoder.c that return int32_t return values according
|
||||
* to the following scheme: |
||||
* 1. negative values indicate a return code from the following list. |
||||
* 2. positive values indicate that error or end of buffer was hit, and |
||||
* that the decode function should immediately return the given value |
||||
* (the decoder state has already been suspended and is ready to be |
||||
* resumed). */ |
||||
#define DECODE_OK -1 |
||||
#define DECODE_MISMATCH -2 /* Used only from checktag_slow(). */ |
||||
#define DECODE_ENDGROUP -3 /* Used only from checkunknown(). */ |
||||
|
||||
#define CHECK_RETURN(x) { int32_t ret = x; if (ret >= 0) return ret; } |
||||
|
||||
#include "upb/port_undef.inc" |
||||
|
||||
#endif /* UPB_DECODER_INT_H_ */ |
@ -1,563 +0,0 @@ |
||||
/*
|
||||
** upb::Encoder |
||||
** |
||||
** Since we are implementing pure handlers (ie. without any out-of-band access |
||||
** to pre-computed lengths), we have to buffer all submessages before we can |
||||
** emit even their first byte. |
||||
** |
||||
** Not knowing the size of submessages also means we can't write a perfect |
||||
** zero-copy implementation, even with buffering. Lengths are stored as |
||||
** varints, which means that we don't know how many bytes to reserve for the |
||||
** length until we know what the length is. |
||||
** |
||||
** This leaves us with three main choices: |
||||
** |
||||
** 1. buffer all submessage data in a temporary buffer, then copy it exactly |
||||
** once into the output buffer. |
||||
** |
||||
** 2. attempt to buffer data directly into the output buffer, estimating how |
||||
** many bytes each length will take. When our guesses are wrong, use |
||||
** memmove() to grow or shrink the allotted space. |
||||
** |
||||
** 3. buffer directly into the output buffer, allocating a max length |
||||
** ahead-of-time for each submessage length. If we overallocated, we waste |
||||
** space, but no memcpy() or memmove() is required. This approach requires |
||||
** defining a maximum size for submessages and rejecting submessages that |
||||
** exceed that size. |
||||
** |
||||
** (2) and (3) have the potential to have better performance, but they are more |
||||
** complicated and subtle to implement: |
||||
** |
||||
** (3) requires making an arbitrary choice of the maximum message size; it |
||||
** wastes space when submessages are shorter than this and fails |
||||
** completely when they are longer. This makes it more finicky and |
||||
** requires configuration based on the input. It also makes it impossible |
||||
** to perfectly match the output of reference encoders that always use the |
||||
** optimal amount of space for each length. |
||||
** |
||||
** (2) requires guessing the the size upfront, and if multiple lengths are |
||||
** guessed wrong the minimum required number of memmove() operations may |
||||
** be complicated to compute correctly. Implemented properly, it may have |
||||
** a useful amortized or average cost, but more investigation is required |
||||
** to determine this and what the optimal algorithm is to achieve it. |
||||
** |
||||
** (1) makes you always pay for exactly one copy, but its implementation is |
||||
** the simplest and its performance is predictable. |
||||
** |
||||
** So for now, we implement (1) only. If we wish to optimize later, we should |
||||
** be able to do it without affecting users. |
||||
** |
||||
** The strategy is to buffer the segments of data that do *not* depend on |
||||
** unknown lengths in one buffer, and keep a separate buffer of segment pointers |
||||
** and lengths. When the top-level submessage ends, we can go beginning to end, |
||||
** alternating the writing of lengths with memcpy() of the rest of the data. |
||||
** At the top level though, no buffering is required. |
||||
*/ |
||||
|
||||
#include "upb/pb/encoder.h" |
||||
#include "upb/pb/varint.int.h" |
||||
|
||||
#include "upb/port_def.inc" |
||||
|
||||
/* The output buffer is divided into segments; a segment is a string of data
|
||||
* that is "ready to go" -- it does not need any varint lengths inserted into |
||||
* the middle. The seams between segments are where varints will be inserted |
||||
* once they are known. |
||||
* |
||||
* We also use the concept of a "run", which is a range of encoded bytes that |
||||
* occur at a single submessage level. Every segment contains one or more runs. |
||||
* |
||||
* A segment can span messages. Consider: |
||||
* |
||||
* .--Submessage lengths---------. |
||||
* | | | |
||||
* | V V |
||||
* V | |--------------- | |----------------- |
||||
* Submessages: | |----------------------------------------------- |
||||
* Top-level msg: ------------------------------------------------------------ |
||||
* |
||||
* Segments: ----- ------------------- ----------------- |
||||
* Runs: *---- *--------------*--- *---------------- |
||||
* (* marks the start) |
||||
* |
||||
* Note that the top-level menssage is not in any segment because it does not |
||||
* have any length preceding it. |
||||
* |
||||
* A segment is only interrupted when another length needs to be inserted. So |
||||
* observe how the second segment spans both the inner submessage and part of |
||||
* the next enclosing message. */ |
||||
typedef struct { |
||||
uint32_t msglen; /* The length to varint-encode before this segment. */ |
||||
uint32_t seglen; /* Length of the segment. */ |
||||
} upb_pb_encoder_segment; |
||||
|
||||
struct upb_pb_encoder { |
||||
upb_arena *arena; |
||||
|
||||
/* Our input and output. */ |
||||
upb_sink input_; |
||||
upb_bytessink output_; |
||||
|
||||
/* The "subclosure" -- used as the inner closure as part of the bytessink
|
||||
* protocol. */ |
||||
void *subc; |
||||
|
||||
/* The output buffer and limit, and our current write position. "buf"
|
||||
* initially points to "initbuf", but is dynamically allocated if we need to |
||||
* grow beyond the initial size. */ |
||||
char *buf, *ptr, *limit; |
||||
|
||||
/* The beginning of the current run, or undefined if we are at the top
|
||||
* level. */ |
||||
char *runbegin; |
||||
|
||||
/* The list of segments we are accumulating. */ |
||||
upb_pb_encoder_segment *segbuf, *segptr, *seglimit; |
||||
|
||||
/* The stack of enclosing submessages. Each entry in the stack points to the
|
||||
* segment where this submessage's length is being accumulated. */ |
||||
int *stack, *top, *stacklimit; |
||||
|
||||
/* Depth of startmsg/endmsg calls. */ |
||||
int depth; |
||||
}; |
||||
|
||||
/* low-level buffering ********************************************************/ |
||||
|
||||
/* Low-level functions for interacting with the output buffer. */ |
||||
|
||||
/* TODO(haberman): handle pushback */ |
||||
static void putbuf(upb_pb_encoder *e, const char *buf, size_t len) { |
||||
size_t n = upb_bytessink_putbuf(e->output_, e->subc, buf, len, NULL); |
||||
UPB_ASSERT(n == len); |
||||
} |
||||
|
||||
static upb_pb_encoder_segment *top(upb_pb_encoder *e) { |
||||
return &e->segbuf[*e->top]; |
||||
} |
||||
|
||||
/* Call to ensure that at least "bytes" bytes are available for writing at
|
||||
* e->ptr. Returns false if the bytes could not be allocated. */ |
||||
static bool reserve(upb_pb_encoder *e, size_t bytes) { |
||||
if ((size_t)(e->limit - e->ptr) < bytes) { |
||||
/* Grow buffer. */ |
||||
char *new_buf; |
||||
size_t needed = bytes + (e->ptr - e->buf); |
||||
size_t old_size = e->limit - e->buf; |
||||
|
||||
size_t new_size = old_size; |
||||
|
||||
while (new_size < needed) { |
||||
new_size *= 2; |
||||
} |
||||
|
||||
new_buf = upb_arena_realloc(e->arena, e->buf, old_size, new_size); |
||||
|
||||
if (new_buf == NULL) { |
||||
return false; |
||||
} |
||||
|
||||
e->ptr = new_buf + (e->ptr - e->buf); |
||||
e->runbegin = new_buf + (e->runbegin - e->buf); |
||||
e->limit = new_buf + new_size; |
||||
e->buf = new_buf; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/* Call when "bytes" bytes have been writte at e->ptr. The caller *must* have
|
||||
* previously called reserve() with at least this many bytes. */ |
||||
static void encoder_advance(upb_pb_encoder *e, size_t bytes) { |
||||
UPB_ASSERT((size_t)(e->limit - e->ptr) >= bytes); |
||||
e->ptr += bytes; |
||||
} |
||||
|
||||
/* Call when all of the bytes for a handler have been written. Flushes the
|
||||
* bytes if possible and necessary, returning false if this failed. */ |
||||
static bool commit(upb_pb_encoder *e) { |
||||
if (!e->top) { |
||||
/* We aren't inside a delimited region. Flush our accumulated bytes to
|
||||
* the output. |
||||
* |
||||
* TODO(haberman): in the future we may want to delay flushing for |
||||
* efficiency reasons. */ |
||||
putbuf(e, e->buf, e->ptr - e->buf); |
||||
e->ptr = e->buf; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/* Writes the given bytes to the buffer, handling reserve/advance. */ |
||||
static bool encode_bytesval(upb_pb_encoder *e, const void *data, size_t len) { |
||||
if (!reserve(e, len)) { |
||||
return false; |
||||
} |
||||
|
||||
memcpy(e->ptr, data, len); |
||||
encoder_advance(e, len); |
||||
return true; |
||||
} |
||||
|
||||
/* Finish the current run by adding the run totals to the segment and message
|
||||
* length. */ |
||||
static void accumulate(upb_pb_encoder *e) { |
||||
size_t run_len; |
||||
UPB_ASSERT(e->ptr >= e->runbegin); |
||||
run_len = e->ptr - e->runbegin; |
||||
e->segptr->seglen += run_len; |
||||
top(e)->msglen += run_len; |
||||
e->runbegin = e->ptr; |
||||
} |
||||
|
||||
/* Call to indicate the start of delimited region for which the full length is
|
||||
* not yet known. All data will be buffered until the length is known. |
||||
* Delimited regions may be nested; their lengths will all be tracked properly. */ |
||||
static bool start_delim(upb_pb_encoder *e) { |
||||
if (e->top) { |
||||
/* We are already buffering, advance to the next segment and push it on the
|
||||
* stack. */ |
||||
accumulate(e); |
||||
|
||||
if (++e->top == e->stacklimit) { |
||||
/* TODO(haberman): grow stack? */ |
||||
return false; |
||||
} |
||||
|
||||
if (++e->segptr == e->seglimit) { |
||||
/* Grow segment buffer. */ |
||||
size_t old_size = |
||||
(e->seglimit - e->segbuf) * sizeof(upb_pb_encoder_segment); |
||||
size_t new_size = old_size * 2; |
||||
upb_pb_encoder_segment *new_buf = |
||||
upb_arena_realloc(e->arena, e->segbuf, old_size, new_size); |
||||
|
||||
if (new_buf == NULL) { |
||||
return false; |
||||
} |
||||
|
||||
e->segptr = new_buf + (e->segptr - e->segbuf); |
||||
e->seglimit = new_buf + (new_size / sizeof(upb_pb_encoder_segment)); |
||||
e->segbuf = new_buf; |
||||
} |
||||
} else { |
||||
/* We were previously at the top level, start buffering. */ |
||||
e->segptr = e->segbuf; |
||||
e->top = e->stack; |
||||
e->runbegin = e->ptr; |
||||
} |
||||
|
||||
*e->top = (int)(e->segptr - e->segbuf); |
||||
e->segptr->seglen = 0; |
||||
e->segptr->msglen = 0; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/* Call to indicate the end of a delimited region. We now know the length of
|
||||
* the delimited region. If we are not nested inside any other delimited |
||||
* regions, we can now emit all of the buffered data we accumulated. */ |
||||
static bool end_delim(upb_pb_encoder *e) { |
||||
size_t msglen; |
||||
accumulate(e); |
||||
msglen = top(e)->msglen; |
||||
|
||||
if (e->top == e->stack) { |
||||
/* All lengths are now available, emit all buffered data. */ |
||||
char buf[UPB_PB_VARINT_MAX_LEN]; |
||||
upb_pb_encoder_segment *s; |
||||
const char *ptr = e->buf; |
||||
for (s = e->segbuf; s <= e->segptr; s++) { |
||||
size_t lenbytes = upb_vencode64(s->msglen, buf); |
||||
putbuf(e, buf, lenbytes); |
||||
putbuf(e, ptr, s->seglen); |
||||
ptr += s->seglen; |
||||
} |
||||
|
||||
e->ptr = e->buf; |
||||
e->top = NULL; |
||||
} else { |
||||
/* Need to keep buffering; propagate length info into enclosing
|
||||
* submessages. */ |
||||
--e->top; |
||||
top(e)->msglen += msglen + upb_varint_size(msglen); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
|
||||
/* tag_t **********************************************************************/ |
||||
|
||||
/* A precomputed (pre-encoded) tag and length. */ |
||||
|
||||
typedef struct { |
||||
uint8_t bytes; |
||||
char tag[7]; |
||||
} tag_t; |
||||
|
||||
/* Allocates a new tag for this field, and sets it in these handlerattr. */ |
||||
static void new_tag(upb_handlers *h, const upb_fielddef *f, upb_wiretype_t wt, |
||||
upb_handlerattr *attr) { |
||||
uint32_t n = upb_fielddef_number(f); |
||||
|
||||
tag_t *tag = upb_gmalloc(sizeof(tag_t)); |
||||
tag->bytes = upb_vencode64((n << 3) | wt, tag->tag); |
||||
|
||||
attr->handler_data = tag; |
||||
upb_handlers_addcleanup(h, tag, upb_gfree); |
||||
} |
||||
|
||||
static bool encode_tagval(upb_pb_encoder *e, const tag_t *tag) { |
||||
return encode_bytesval(e, tag->tag, tag->bytes); |
||||
} |
||||
|
||||
|
||||
/* encoding of wire types *****************************************************/ |
||||
|
||||
static bool doencode_fixed64(upb_pb_encoder *e, uint64_t val) { |
||||
/* TODO(haberman): byte-swap for big endian. */ |
||||
return encode_bytesval(e, &val, sizeof(uint64_t)); |
||||
} |
||||
|
||||
static bool doencode_fixed32(upb_pb_encoder *e, uint32_t val) { |
||||
/* TODO(haberman): byte-swap for big endian. */ |
||||
return encode_bytesval(e, &val, sizeof(uint32_t)); |
||||
} |
||||
|
||||
static bool doencode_varint(upb_pb_encoder *e, uint64_t val) { |
||||
if (!reserve(e, UPB_PB_VARINT_MAX_LEN)) { |
||||
return false; |
||||
} |
||||
|
||||
encoder_advance(e, upb_vencode64(val, e->ptr)); |
||||
return true; |
||||
} |
||||
|
||||
static uint64_t dbl2uint64(double d) { |
||||
uint64_t ret; |
||||
memcpy(&ret, &d, sizeof(uint64_t)); |
||||
return ret; |
||||
} |
||||
|
||||
static uint32_t flt2uint32(float d) { |
||||
uint32_t ret; |
||||
memcpy(&ret, &d, sizeof(uint32_t)); |
||||
return ret; |
||||
} |
||||
|
||||
|
||||
/* encoding of proto types ****************************************************/ |
||||
|
||||
static bool startmsg(void *c, const void *hd) { |
||||
upb_pb_encoder *e = c; |
||||
UPB_UNUSED(hd); |
||||
if (e->depth++ == 0) { |
||||
upb_bytessink_start(e->output_, 0, &e->subc); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
static bool endmsg(void *c, const void *hd, upb_status *status) { |
||||
upb_pb_encoder *e = c; |
||||
UPB_UNUSED(hd); |
||||
UPB_UNUSED(status); |
||||
if (--e->depth == 0) { |
||||
upb_bytessink_end(e->output_); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
static void *encode_startdelimfield(void *c, const void *hd) { |
||||
bool ok = encode_tagval(c, hd) && commit(c) && start_delim(c); |
||||
return ok ? c : UPB_BREAK; |
||||
} |
||||
|
||||
static bool encode_unknown(void *c, const void *hd, const char *buf, |
||||
size_t len) { |
||||
UPB_UNUSED(hd); |
||||
return encode_bytesval(c, buf, len) && commit(c); |
||||
} |
||||
|
||||
static bool encode_enddelimfield(void *c, const void *hd) { |
||||
UPB_UNUSED(hd); |
||||
return end_delim(c); |
||||
} |
||||
|
||||
static void *encode_startgroup(void *c, const void *hd) { |
||||
return (encode_tagval(c, hd) && commit(c)) ? c : UPB_BREAK; |
||||
} |
||||
|
||||
static bool encode_endgroup(void *c, const void *hd) { |
||||
return encode_tagval(c, hd) && commit(c); |
||||
} |
||||
|
||||
static void *encode_startstr(void *c, const void *hd, size_t size_hint) { |
||||
UPB_UNUSED(size_hint); |
||||
return encode_startdelimfield(c, hd); |
||||
} |
||||
|
||||
static size_t encode_strbuf(void *c, const void *hd, const char *buf, |
||||
size_t len, const upb_bufhandle *h) { |
||||
UPB_UNUSED(hd); |
||||
UPB_UNUSED(h); |
||||
return encode_bytesval(c, buf, len) ? len : 0; |
||||
} |
||||
|
||||
#define T(type, ctype, convert, encode) \ |
||||
static bool encode_scalar_##type(void *e, const void *hd, ctype val) { \
|
||||
return encode_tagval(e, hd) && encode(e, (convert)(val)) && commit(e); \
|
||||
} \
|
||||
static bool encode_packed_##type(void *e, const void *hd, ctype val) { \
|
||||
UPB_UNUSED(hd); \
|
||||
return encode(e, (convert)(val)); \
|
||||
} |
||||
|
||||
T(double, double, dbl2uint64, doencode_fixed64) |
||||
T(float, float, flt2uint32, doencode_fixed32) |
||||
T(int64, int64_t, uint64_t, doencode_varint) |
||||
T(int32, int32_t, int64_t, doencode_varint) |
||||
T(fixed64, uint64_t, uint64_t, doencode_fixed64) |
||||
T(fixed32, uint32_t, uint32_t, doencode_fixed32) |
||||
T(bool, bool, bool, doencode_varint) |
||||
T(uint32, uint32_t, uint32_t, doencode_varint) |
||||
T(uint64, uint64_t, uint64_t, doencode_varint) |
||||
T(enum, int32_t, uint32_t, doencode_varint) |
||||
T(sfixed32, int32_t, uint32_t, doencode_fixed32) |
||||
T(sfixed64, int64_t, uint64_t, doencode_fixed64) |
||||
T(sint32, int32_t, upb_zzenc_32, doencode_varint) |
||||
T(sint64, int64_t, upb_zzenc_64, doencode_varint) |
||||
|
||||
#undef T |
||||
|
||||
|
||||
/* code to build the handlers *************************************************/ |
||||
|
||||
#include <stdio.h> |
||||
static void newhandlers_callback(const void *closure, upb_handlers *h) { |
||||
const upb_msgdef *m; |
||||
int i, n; |
||||
|
||||
UPB_UNUSED(closure); |
||||
|
||||
upb_handlers_setstartmsg(h, startmsg, NULL); |
||||
upb_handlers_setendmsg(h, endmsg, NULL); |
||||
upb_handlers_setunknown(h, encode_unknown, NULL); |
||||
|
||||
m = upb_handlers_msgdef(h); |
||||
n = upb_msgdef_fieldcount(m); |
||||
for(i = 0; i < n; i++) { |
||||
const upb_fielddef *f = upb_msgdef_field(m, i); |
||||
bool packed = upb_fielddef_isseq(f) && upb_fielddef_isprimitive(f) && |
||||
upb_fielddef_packed(f); |
||||
upb_handlerattr attr = UPB_HANDLERATTR_INIT; |
||||
upb_wiretype_t wt = |
||||
packed ? UPB_WIRE_TYPE_DELIMITED |
||||
: upb_pb_native_wire_types[upb_fielddef_descriptortype(f)]; |
||||
|
||||
/* Pre-encode the tag for this field. */ |
||||
new_tag(h, f, wt, &attr); |
||||
|
||||
if (packed) { |
||||
upb_handlers_setstartseq(h, f, encode_startdelimfield, &attr); |
||||
upb_handlers_setendseq(h, f, encode_enddelimfield, &attr); |
||||
} |
||||
|
||||
#define T(upper, lower, upbtype) \ |
||||
case UPB_DESCRIPTOR_TYPE_##upper: \
|
||||
if (packed) { \
|
||||
upb_handlers_set##upbtype(h, f, encode_packed_##lower, &attr); \
|
||||
} else { \
|
||||
upb_handlers_set##upbtype(h, f, encode_scalar_##lower, &attr); \
|
||||
} \
|
||||
break; |
||||
|
||||
switch (upb_fielddef_descriptortype(f)) { |
||||
T(DOUBLE, double, double); |
||||
T(FLOAT, float, float); |
||||
T(INT64, int64, int64); |
||||
T(INT32, int32, int32); |
||||
T(FIXED64, fixed64, uint64); |
||||
T(FIXED32, fixed32, uint32); |
||||
T(BOOL, bool, bool); |
||||
T(UINT32, uint32, uint32); |
||||
T(UINT64, uint64, uint64); |
||||
T(ENUM, enum, int32); |
||||
T(SFIXED32, sfixed32, int32); |
||||
T(SFIXED64, sfixed64, int64); |
||||
T(SINT32, sint32, int32); |
||||
T(SINT64, sint64, int64); |
||||
case UPB_DESCRIPTOR_TYPE_STRING: |
||||
case UPB_DESCRIPTOR_TYPE_BYTES: |
||||
upb_handlers_setstartstr(h, f, encode_startstr, &attr); |
||||
upb_handlers_setendstr(h, f, encode_enddelimfield, &attr); |
||||
upb_handlers_setstring(h, f, encode_strbuf, &attr); |
||||
break; |
||||
case UPB_DESCRIPTOR_TYPE_MESSAGE: |
||||
upb_handlers_setstartsubmsg(h, f, encode_startdelimfield, &attr); |
||||
upb_handlers_setendsubmsg(h, f, encode_enddelimfield, &attr); |
||||
break; |
||||
case UPB_DESCRIPTOR_TYPE_GROUP: { |
||||
/* Endgroup takes a different tag (wire_type = END_GROUP). */ |
||||
upb_handlerattr attr2 = UPB_HANDLERATTR_INIT; |
||||
new_tag(h, f, UPB_WIRE_TYPE_END_GROUP, &attr2); |
||||
|
||||
upb_handlers_setstartsubmsg(h, f, encode_startgroup, &attr); |
||||
upb_handlers_setendsubmsg(h, f, encode_endgroup, &attr2); |
||||
|
||||
break; |
||||
} |
||||
} |
||||
|
||||
#undef T |
||||
} |
||||
} |
||||
|
||||
void upb_pb_encoder_reset(upb_pb_encoder *e) { |
||||
e->segptr = NULL; |
||||
e->top = NULL; |
||||
e->depth = 0; |
||||
} |
||||
|
||||
|
||||
/* public API *****************************************************************/ |
||||
|
||||
upb_handlercache *upb_pb_encoder_newcache(void) { |
||||
return upb_handlercache_new(newhandlers_callback, NULL); |
||||
} |
||||
|
||||
upb_pb_encoder *upb_pb_encoder_create(upb_arena *arena, const upb_handlers *h, |
||||
upb_bytessink output) { |
||||
const size_t initial_bufsize = 256; |
||||
const size_t initial_segbufsize = 16; |
||||
/* TODO(haberman): make this configurable. */ |
||||
const size_t stack_size = 64; |
||||
|
||||
upb_pb_encoder *e = upb_arena_malloc(arena, sizeof(upb_pb_encoder)); |
||||
if (!e) return NULL; |
||||
|
||||
e->buf = upb_arena_malloc(arena, initial_bufsize); |
||||
e->segbuf = upb_arena_malloc(arena, initial_segbufsize * sizeof(*e->segbuf)); |
||||
e->stack = upb_arena_malloc(arena, stack_size * sizeof(*e->stack)); |
||||
|
||||
if (!e->buf || !e->segbuf || !e->stack) { |
||||
return NULL; |
||||
} |
||||
|
||||
e->limit = e->buf + initial_bufsize; |
||||
e->seglimit = e->segbuf + initial_segbufsize; |
||||
e->stacklimit = e->stack + stack_size; |
||||
|
||||
upb_pb_encoder_reset(e); |
||||
upb_sink_reset(&e->input_, h, e); |
||||
|
||||
e->arena = arena; |
||||
e->output_ = output; |
||||
e->subc = output.closure; |
||||
e->ptr = e->buf; |
||||
|
||||
return e; |
||||
} |
||||
|
||||
upb_sink upb_pb_encoder_input(upb_pb_encoder *e) { return e->input_; } |
@ -1,83 +0,0 @@ |
||||
/*
|
||||
** upb::pb::Encoder (upb_pb_encoder) |
||||
** |
||||
** Implements a set of upb_handlers that write protobuf data to the binary wire |
||||
** format. |
||||
** |
||||
** This encoder implementation does not have any access to any out-of-band or |
||||
** precomputed lengths for submessages, so it must buffer submessages internally |
||||
** before it can emit the first byte. |
||||
*/ |
||||
|
||||
#ifndef UPB_ENCODER_H_ |
||||
#define UPB_ENCODER_H_ |
||||
|
||||
#include "upb/sink.h" |
||||
|
||||
#ifdef __cplusplus |
||||
namespace upb { |
||||
namespace pb { |
||||
class EncoderPtr; |
||||
} /* namespace pb */ |
||||
} /* namespace upb */ |
||||
#endif |
||||
|
||||
#define UPB_PBENCODER_MAX_NESTING 100 |
||||
|
||||
/* upb_pb_encoder *************************************************************/ |
||||
|
||||
/* Preallocation hint: decoder won't allocate more bytes than this when first
|
||||
* constructed. This hint may be an overestimate for some build configurations. |
||||
* But if the decoder library is upgraded without recompiling the application, |
||||
* it may be an underestimate. */ |
||||
#define UPB_PB_ENCODER_SIZE 784 |
||||
|
||||
struct upb_pb_encoder; |
||||
typedef struct upb_pb_encoder upb_pb_encoder; |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
upb_sink upb_pb_encoder_input(upb_pb_encoder *p); |
||||
upb_pb_encoder* upb_pb_encoder_create(upb_arena* a, const upb_handlers* h, |
||||
upb_bytessink output); |
||||
|
||||
/* Lazily builds and caches handlers that will push encoded data to a bytessink.
|
||||
* Any msgdef objects used with this object must outlive it. */ |
||||
upb_handlercache *upb_pb_encoder_newcache(void); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" { */ |
||||
|
||||
class upb::pb::EncoderPtr { |
||||
public: |
||||
EncoderPtr(upb_pb_encoder* ptr) : ptr_(ptr) {} |
||||
|
||||
upb_pb_encoder* ptr() { return ptr_; } |
||||
|
||||
/* Creates a new encoder in the given environment. The Handlers must have
|
||||
* come from NewHandlers() below. */ |
||||
static EncoderPtr Create(Arena* arena, const Handlers* handlers, |
||||
BytesSink output) { |
||||
return EncoderPtr( |
||||
upb_pb_encoder_create(arena->ptr(), handlers, output.sink())); |
||||
} |
||||
|
||||
/* The input to the encoder. */ |
||||
upb::Sink input() { return upb_pb_encoder_input(ptr()); } |
||||
|
||||
/* Creates a new set of handlers for this MessageDef. */ |
||||
static HandlerCache NewCache() { |
||||
return HandlerCache(upb_pb_encoder_newcache()); |
||||
} |
||||
|
||||
static const size_t kSize = UPB_PB_ENCODER_SIZE; |
||||
|
||||
private: |
||||
upb_pb_encoder* ptr_; |
||||
}; |
||||
|
||||
#endif /* __cplusplus */ |
||||
|
||||
#endif /* UPB_ENCODER_H_ */ |
@ -1,36 +0,0 @@ |
||||
#!/usr/bin/ruby |
||||
|
||||
puts "set width 0 |
||||
set height 0 |
||||
set verbose off\n\n" |
||||
|
||||
IO.popen("nm -S /tmp/upb-jit-code.so").each_line { |line| |
||||
# Input lines look like this: |
||||
# 000000000000575a T X.0x10.OP_CHECKDELIM |
||||
# |
||||
# For each one we want to emit a command that looks like: |
||||
# b X.0x10.OP_CHECKDELIM |
||||
# commands |
||||
# silent |
||||
# printf "buf_ofs=%d data_rem=%d delim_rem=%d X.0x10.OP_CHECKDELIM\n", $rbx - (long)((upb_pbdecoder*)($r15))->buf, $r12 - $rbx, $rbp - $rbx |
||||
# continue |
||||
# end |
||||
|
||||
parts = line.split |
||||
next if parts[1] != "T" |
||||
sym = parts[2] |
||||
next if sym !~ /X\./; |
||||
if sym =~ /OP_/ then |
||||
printcmd = "printf \"buf_ofs=%d data_rem=%d delim_rem=%d #{sym}\\n\", $rbx - (long)((upb_pbdecoder*)($r15))->buf, $r12 - $rbx, $rbp - $rbx" |
||||
elsif sym =~ /enterjit/ then |
||||
printcmd = "printf \"#{sym} bytes=%d\\n\", $rcx" |
||||
else |
||||
printcmd = "printf \"#{sym}\\n\"" |
||||
end |
||||
puts "b #{sym} |
||||
commands |
||||
silent |
||||
#{printcmd} |
||||
continue |
||||
end\n\n" |
||||
} |
@ -1,339 +0,0 @@ |
||||
/*
|
||||
* upb::pb::TextPrinter |
||||
* |
||||
* OPT: This is not optimized at all. It uses printf() which parses the format |
||||
* string every time, and it allocates memory for every put. |
||||
*/ |
||||
|
||||
#include "upb/pb/textprinter.h" |
||||
|
||||
#include <ctype.h> |
||||
#include <float.h> |
||||
#include <inttypes.h> |
||||
#include <stdarg.h> |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
|
||||
#include "upb/sink.h" |
||||
|
||||
#include "upb/port_def.inc" |
||||
|
||||
struct upb_textprinter { |
||||
upb_sink input_; |
||||
upb_bytessink output_; |
||||
int indent_depth_; |
||||
bool single_line_; |
||||
void *subc; |
||||
}; |
||||
|
||||
#define CHECK(x) if ((x) < 0) goto err; |
||||
|
||||
static const char *shortname(const char *longname) { |
||||
const char *last = strrchr(longname, '.'); |
||||
return last ? last + 1 : longname; |
||||
} |
||||
|
||||
static int indent(upb_textprinter *p) { |
||||
int i; |
||||
if (!p->single_line_) |
||||
for (i = 0; i < p->indent_depth_; i++) |
||||
upb_bytessink_putbuf(p->output_, p->subc, " ", 2, NULL); |
||||
return 0; |
||||
} |
||||
|
||||
static int endfield(upb_textprinter *p) { |
||||
const char ch = (p->single_line_ ? ' ' : '\n'); |
||||
upb_bytessink_putbuf(p->output_, p->subc, &ch, 1, NULL); |
||||
return 0; |
||||
} |
||||
|
||||
static int putescaped(upb_textprinter *p, const char *buf, size_t len, |
||||
bool preserve_utf8) { |
||||
/* Based on CEscapeInternal() from Google's protobuf release. */ |
||||
char dstbuf[4096], *dst = dstbuf, *dstend = dstbuf + sizeof(dstbuf); |
||||
const char *end = buf + len; |
||||
|
||||
/* I think hex is prettier and more useful, but proto2 uses octal; should
|
||||
* investigate whether it can parse hex also. */ |
||||
const bool use_hex = false; |
||||
bool last_hex_escape = false; /* true if last output char was \xNN */ |
||||
|
||||
for (; buf < end; buf++) { |
||||
bool is_hex_escape; |
||||
|
||||
if (dstend - dst < 4) { |
||||
upb_bytessink_putbuf(p->output_, p->subc, dstbuf, dst - dstbuf, NULL); |
||||
dst = dstbuf; |
||||
} |
||||
|
||||
is_hex_escape = false; |
||||
switch (*buf) { |
||||
case '\n': *(dst++) = '\\'; *(dst++) = 'n'; break; |
||||
case '\r': *(dst++) = '\\'; *(dst++) = 'r'; break; |
||||
case '\t': *(dst++) = '\\'; *(dst++) = 't'; break; |
||||
case '\"': *(dst++) = '\\'; *(dst++) = '\"'; break; |
||||
case '\'': *(dst++) = '\\'; *(dst++) = '\''; break; |
||||
case '\\': *(dst++) = '\\'; *(dst++) = '\\'; break; |
||||
default: |
||||
/* Note that if we emit \xNN and the buf character after that is a hex
|
||||
* digit then that digit must be escaped too to prevent it being |
||||
* interpreted as part of the character code by C. */ |
||||
if ((!preserve_utf8 || (uint8_t)*buf < 0x80) && |
||||
(!isprint(*buf) || (last_hex_escape && isxdigit(*buf)))) { |
||||
sprintf(dst, (use_hex ? "\\x%02x" : "\\%03o"), (uint8_t)*buf); |
||||
is_hex_escape = use_hex; |
||||
dst += 4; |
||||
} else { |
||||
*(dst++) = *buf; break; |
||||
} |
||||
} |
||||
last_hex_escape = is_hex_escape; |
||||
} |
||||
/* Flush remaining data. */ |
||||
upb_bytessink_putbuf(p->output_, p->subc, dstbuf, dst - dstbuf, NULL); |
||||
return 0; |
||||
} |
||||
|
||||
bool putf(upb_textprinter *p, const char *fmt, ...) { |
||||
va_list args; |
||||
va_list args_copy; |
||||
char *str; |
||||
int written; |
||||
int len; |
||||
bool ok; |
||||
|
||||
va_start(args, fmt); |
||||
|
||||
/* Run once to get the length of the string. */ |
||||
va_copy(args_copy, args); |
||||
len = vsnprintf(NULL, 0, fmt, args_copy); |
||||
va_end(args_copy); |
||||
|
||||
/* + 1 for NULL terminator (vsprintf() requires it even if we don't). */ |
||||
str = upb_gmalloc(len + 1); |
||||
if (!str) return false; |
||||
written = vsprintf(str, fmt, args); |
||||
va_end(args); |
||||
UPB_ASSERT(written == len); |
||||
|
||||
ok = upb_bytessink_putbuf(p->output_, p->subc, str, len, NULL); |
||||
upb_gfree(str); |
||||
return ok; |
||||
} |
||||
|
||||
|
||||
/* handlers *******************************************************************/ |
||||
|
||||
static bool textprinter_startmsg(void *c, const void *hd) { |
||||
upb_textprinter *p = c; |
||||
UPB_UNUSED(hd); |
||||
if (p->indent_depth_ == 0) { |
||||
upb_bytessink_start(p->output_, 0, &p->subc); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
static bool textprinter_endmsg(void *c, const void *hd, upb_status *s) { |
||||
upb_textprinter *p = c; |
||||
UPB_UNUSED(hd); |
||||
UPB_UNUSED(s); |
||||
if (p->indent_depth_ == 0) { |
||||
upb_bytessink_end(p->output_); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
#define TYPE(name, ctype, fmt) \ |
||||
static bool textprinter_put ## name(void *closure, const void *handler_data, \
|
||||
ctype val) { \
|
||||
upb_textprinter *p = closure; \
|
||||
const upb_fielddef *f = handler_data; \
|
||||
CHECK(indent(p)); \
|
||||
putf(p, "%s: " fmt, upb_fielddef_name(f), val); \
|
||||
CHECK(endfield(p)); \
|
||||
return true; \
|
||||
err: \
|
||||
return false; \
|
||||
} |
||||
|
||||
static bool textprinter_putbool(void *closure, const void *handler_data, |
||||
bool val) { |
||||
upb_textprinter *p = closure; |
||||
const upb_fielddef *f = handler_data; |
||||
CHECK(indent(p)); |
||||
putf(p, "%s: %s", upb_fielddef_name(f), val ? "true" : "false"); |
||||
CHECK(endfield(p)); |
||||
return true; |
||||
err: |
||||
return false; |
||||
} |
||||
|
||||
#define STRINGIFY_HELPER(x) #x |
||||
#define STRINGIFY_MACROVAL(x) STRINGIFY_HELPER(x) |
||||
|
||||
TYPE(int32, int32_t, "%" PRId32) |
||||
TYPE(int64, int64_t, "%" PRId64) |
||||
TYPE(uint32, uint32_t, "%" PRIu32) |
||||
TYPE(uint64, uint64_t, "%" PRIu64) |
||||
TYPE(float, float, "%." STRINGIFY_MACROVAL(FLT_DIG) "g") |
||||
TYPE(double, double, "%." STRINGIFY_MACROVAL(DBL_DIG) "g") |
||||
|
||||
#undef TYPE |
||||
|
||||
/* Output a symbolic value from the enum if found, else just print as int32. */ |
||||
static bool textprinter_putenum(void *closure, const void *handler_data, |
||||
int32_t val) { |
||||
upb_textprinter *p = closure; |
||||
const upb_fielddef *f = handler_data; |
||||
const upb_enumdef *enum_def = upb_fielddef_enumsubdef(f); |
||||
const char *label = upb_enumdef_iton(enum_def, val); |
||||
if (label) { |
||||
indent(p); |
||||
putf(p, "%s: %s", upb_fielddef_name(f), label); |
||||
endfield(p); |
||||
} else { |
||||
if (!textprinter_putint32(closure, handler_data, val)) |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
static void *textprinter_startstr(void *closure, const void *handler_data, |
||||
size_t size_hint) { |
||||
upb_textprinter *p = closure; |
||||
const upb_fielddef *f = handler_data; |
||||
UPB_UNUSED(size_hint); |
||||
indent(p); |
||||
putf(p, "%s: \"", upb_fielddef_name(f)); |
||||
return p; |
||||
} |
||||
|
||||
static bool textprinter_endstr(void *closure, const void *handler_data) { |
||||
upb_textprinter *p = closure; |
||||
UPB_UNUSED(handler_data); |
||||
putf(p, "\""); |
||||
endfield(p); |
||||
return true; |
||||
} |
||||
|
||||
static size_t textprinter_putstr(void *closure, const void *hd, const char *buf, |
||||
size_t len, const upb_bufhandle *handle) { |
||||
upb_textprinter *p = closure; |
||||
const upb_fielddef *f = hd; |
||||
UPB_UNUSED(handle); |
||||
CHECK(putescaped(p, buf, len, upb_fielddef_type(f) == UPB_TYPE_STRING)); |
||||
return len; |
||||
err: |
||||
return 0; |
||||
} |
||||
|
||||
static void *textprinter_startsubmsg(void *closure, const void *handler_data) { |
||||
upb_textprinter *p = closure; |
||||
const char *name = handler_data; |
||||
CHECK(indent(p)); |
||||
putf(p, "%s {%c", name, p->single_line_ ? ' ' : '\n'); |
||||
p->indent_depth_++; |
||||
return p; |
||||
err: |
||||
return UPB_BREAK; |
||||
} |
||||
|
||||
static bool textprinter_endsubmsg(void *closure, const void *handler_data) { |
||||
upb_textprinter *p = closure; |
||||
UPB_UNUSED(handler_data); |
||||
p->indent_depth_--; |
||||
CHECK(indent(p)); |
||||
upb_bytessink_putbuf(p->output_, p->subc, "}", 1, NULL); |
||||
CHECK(endfield(p)); |
||||
return true; |
||||
err: |
||||
return false; |
||||
} |
||||
|
||||
static void onmreg(const void *c, upb_handlers *h) { |
||||
const upb_msgdef *m = upb_handlers_msgdef(h); |
||||
int i, n; |
||||
UPB_UNUSED(c); |
||||
|
||||
upb_handlers_setstartmsg(h, textprinter_startmsg, NULL); |
||||
upb_handlers_setendmsg(h, textprinter_endmsg, NULL); |
||||
|
||||
n = upb_msgdef_fieldcount(m); |
||||
for(i = 0; i < n; i++) { |
||||
const upb_fielddef *f = upb_msgdef_field(m, i); |
||||
upb_handlerattr attr = UPB_HANDLERATTR_INIT; |
||||
attr.handler_data = f; |
||||
switch (upb_fielddef_type(f)) { |
||||
case UPB_TYPE_INT32: |
||||
upb_handlers_setint32(h, f, textprinter_putint32, &attr); |
||||
break; |
||||
case UPB_TYPE_INT64: |
||||
upb_handlers_setint64(h, f, textprinter_putint64, &attr); |
||||
break; |
||||
case UPB_TYPE_UINT32: |
||||
upb_handlers_setuint32(h, f, textprinter_putuint32, &attr); |
||||
break; |
||||
case UPB_TYPE_UINT64: |
||||
upb_handlers_setuint64(h, f, textprinter_putuint64, &attr); |
||||
break; |
||||
case UPB_TYPE_FLOAT: |
||||
upb_handlers_setfloat(h, f, textprinter_putfloat, &attr); |
||||
break; |
||||
case UPB_TYPE_DOUBLE: |
||||
upb_handlers_setdouble(h, f, textprinter_putdouble, &attr); |
||||
break; |
||||
case UPB_TYPE_BOOL: |
||||
upb_handlers_setbool(h, f, textprinter_putbool, &attr); |
||||
break; |
||||
case UPB_TYPE_STRING: |
||||
case UPB_TYPE_BYTES: |
||||
upb_handlers_setstartstr(h, f, textprinter_startstr, &attr); |
||||
upb_handlers_setstring(h, f, textprinter_putstr, &attr); |
||||
upb_handlers_setendstr(h, f, textprinter_endstr, &attr); |
||||
break; |
||||
case UPB_TYPE_MESSAGE: { |
||||
const char *name = |
||||
upb_fielddef_descriptortype(f) == UPB_DESCRIPTOR_TYPE_GROUP |
||||
? shortname(upb_msgdef_fullname(upb_fielddef_msgsubdef(f))) |
||||
: upb_fielddef_name(f); |
||||
attr.handler_data = name; |
||||
upb_handlers_setstartsubmsg(h, f, textprinter_startsubmsg, &attr); |
||||
upb_handlers_setendsubmsg(h, f, textprinter_endsubmsg, &attr); |
||||
break; |
||||
} |
||||
case UPB_TYPE_ENUM: |
||||
upb_handlers_setint32(h, f, textprinter_putenum, &attr); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void textprinter_reset(upb_textprinter *p, bool single_line) { |
||||
p->single_line_ = single_line; |
||||
p->indent_depth_ = 0; |
||||
} |
||||
|
||||
|
||||
/* Public API *****************************************************************/ |
||||
|
||||
upb_textprinter *upb_textprinter_create(upb_arena *arena, const upb_handlers *h, |
||||
upb_bytessink output) { |
||||
upb_textprinter *p = upb_arena_malloc(arena, sizeof(upb_textprinter)); |
||||
if (!p) return NULL; |
||||
|
||||
p->output_ = output; |
||||
upb_sink_reset(&p->input_, h, p); |
||||
textprinter_reset(p, false); |
||||
|
||||
return p; |
||||
} |
||||
|
||||
upb_handlercache *upb_textprinter_newcache(void) { |
||||
return upb_handlercache_new(&onmreg, NULL); |
||||
} |
||||
|
||||
upb_sink upb_textprinter_input(upb_textprinter *p) { return p->input_; } |
||||
|
||||
void upb_textprinter_setsingleline(upb_textprinter *p, bool single_line) { |
||||
p->single_line_ = single_line; |
||||
} |
@ -1,69 +0,0 @@ |
||||
/*
|
||||
** upb::pb::TextPrinter (upb_textprinter) |
||||
** |
||||
** Handlers for writing to protobuf text format. |
||||
*/ |
||||
|
||||
#ifndef UPB_TEXT_H_ |
||||
#define UPB_TEXT_H_ |
||||
|
||||
#include "upb/sink.h" |
||||
|
||||
#ifdef __cplusplus |
||||
namespace upb { |
||||
namespace pb { |
||||
class TextPrinterPtr; |
||||
} /* namespace pb */ |
||||
} /* namespace upb */ |
||||
#endif |
||||
|
||||
/* upb_textprinter ************************************************************/ |
||||
|
||||
struct upb_textprinter; |
||||
typedef struct upb_textprinter upb_textprinter; |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/* C API. */ |
||||
upb_textprinter *upb_textprinter_create(upb_arena *arena, const upb_handlers *h, |
||||
upb_bytessink output); |
||||
void upb_textprinter_setsingleline(upb_textprinter *p, bool single_line); |
||||
upb_sink upb_textprinter_input(upb_textprinter *p); |
||||
upb_handlercache *upb_textprinter_newcache(void); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
|
||||
class upb::pb::TextPrinterPtr { |
||||
public: |
||||
TextPrinterPtr(upb_textprinter* ptr) : ptr_(ptr) {} |
||||
|
||||
/* The given handlers must have come from NewHandlers(). It must outlive the
|
||||
* TextPrinter. */ |
||||
static TextPrinterPtr Create(Arena *arena, upb::HandlersPtr *handlers, |
||||
BytesSink output) { |
||||
return TextPrinterPtr( |
||||
upb_textprinter_create(arena->ptr(), handlers->ptr(), output.sink())); |
||||
} |
||||
|
||||
void SetSingleLineMode(bool single_line) { |
||||
upb_textprinter_setsingleline(ptr_, single_line); |
||||
} |
||||
|
||||
Sink input() { return upb_textprinter_input(ptr_); } |
||||
|
||||
/* If handler caching becomes a requirement we can add a code cache as in
|
||||
* decoder.h */ |
||||
static HandlerCache NewCache() { |
||||
return HandlerCache(upb_textprinter_newcache()); |
||||
} |
||||
|
||||
private: |
||||
upb_textprinter* ptr_; |
||||
}; |
||||
|
||||
#endif |
||||
|
||||
#endif /* UPB_TEXT_H_ */ |
@ -1,74 +0,0 @@ |
||||
|
||||
#include "upb/pb/varint.int.h" |
||||
|
||||
/* Index is descriptor type. */ |
||||
const uint8_t upb_pb_native_wire_types[] = { |
||||
UPB_WIRE_TYPE_END_GROUP, /* ENDGROUP */ |
||||
UPB_WIRE_TYPE_64BIT, /* DOUBLE */ |
||||
UPB_WIRE_TYPE_32BIT, /* FLOAT */ |
||||
UPB_WIRE_TYPE_VARINT, /* INT64 */ |
||||
UPB_WIRE_TYPE_VARINT, /* UINT64 */ |
||||
UPB_WIRE_TYPE_VARINT, /* INT32 */ |
||||
UPB_WIRE_TYPE_64BIT, /* FIXED64 */ |
||||
UPB_WIRE_TYPE_32BIT, /* FIXED32 */ |
||||
UPB_WIRE_TYPE_VARINT, /* BOOL */ |
||||
UPB_WIRE_TYPE_DELIMITED, /* STRING */ |
||||
UPB_WIRE_TYPE_START_GROUP, /* GROUP */ |
||||
UPB_WIRE_TYPE_DELIMITED, /* MESSAGE */ |
||||
UPB_WIRE_TYPE_DELIMITED, /* BYTES */ |
||||
UPB_WIRE_TYPE_VARINT, /* UINT32 */ |
||||
UPB_WIRE_TYPE_VARINT, /* ENUM */ |
||||
UPB_WIRE_TYPE_32BIT, /* SFIXED32 */ |
||||
UPB_WIRE_TYPE_64BIT, /* SFIXED64 */ |
||||
UPB_WIRE_TYPE_VARINT, /* SINT32 */ |
||||
UPB_WIRE_TYPE_VARINT, /* SINT64 */ |
||||
}; |
||||
|
||||
/* A basic branch-based decoder, uses 32-bit values to get good performance
|
||||
* on 32-bit architectures (but performs well on 64-bits also). |
||||
* This scheme comes from the original Google Protobuf implementation |
||||
* (proto2). */ |
||||
upb_decoderet upb_vdecode_max8_branch32(upb_decoderet r) { |
||||
upb_decoderet err = {NULL, 0}; |
||||
const char *p = r.p; |
||||
uint32_t low = (uint32_t)r.val; |
||||
uint32_t high = 0; |
||||
uint32_t b; |
||||
b = *(p++); low |= (b & 0x7fU) << 14; if (!(b & 0x80)) goto done; |
||||
b = *(p++); low |= (b & 0x7fU) << 21; if (!(b & 0x80)) goto done; |
||||
b = *(p++); low |= (b & 0x7fU) << 28; |
||||
high = (b & 0x7fU) >> 4; if (!(b & 0x80)) goto done; |
||||
b = *(p++); high |= (b & 0x7fU) << 3; if (!(b & 0x80)) goto done; |
||||
b = *(p++); high |= (b & 0x7fU) << 10; if (!(b & 0x80)) goto done; |
||||
b = *(p++); high |= (b & 0x7fU) << 17; if (!(b & 0x80)) goto done; |
||||
b = *(p++); high |= (b & 0x7fU) << 24; if (!(b & 0x80)) goto done; |
||||
b = *(p++); high |= (b & 0x7fU) << 31; if (!(b & 0x80)) goto done; |
||||
return err; |
||||
|
||||
done: |
||||
r.val = ((uint64_t)high << 32) | low; |
||||
r.p = p; |
||||
return r; |
||||
} |
||||
|
||||
/* Like the previous, but uses 64-bit values. */ |
||||
upb_decoderet upb_vdecode_max8_branch64(upb_decoderet r) { |
||||
const char *p = r.p; |
||||
uint64_t val = r.val; |
||||
uint64_t b; |
||||
upb_decoderet err = {NULL, 0}; |
||||
b = *(p++); val |= (b & 0x7fU) << 14; if (!(b & 0x80)) goto done; |
||||
b = *(p++); val |= (b & 0x7fU) << 21; if (!(b & 0x80)) goto done; |
||||
b = *(p++); val |= (b & 0x7fU) << 28; if (!(b & 0x80)) goto done; |
||||
b = *(p++); val |= (b & 0x7fU) << 35; if (!(b & 0x80)) goto done; |
||||
b = *(p++); val |= (b & 0x7fU) << 42; if (!(b & 0x80)) goto done; |
||||
b = *(p++); val |= (b & 0x7fU) << 49; if (!(b & 0x80)) goto done; |
||||
b = *(p++); val |= (b & 0x7fU) << 56; if (!(b & 0x80)) goto done; |
||||
b = *(p++); val |= (b & 0x7fU) << 63; if (!(b & 0x80)) goto done; |
||||
return err; |
||||
|
||||
done: |
||||
r.val = val; |
||||
r.p = p; |
||||
return r; |
||||
} |
@ -1,164 +0,0 @@ |
||||
/*
|
||||
** A number of routines for varint manipulation (we keep them all around to |
||||
** have multiple approaches available for benchmarking). |
||||
*/ |
||||
|
||||
#ifndef UPB_VARINT_DECODER_H_ |
||||
#define UPB_VARINT_DECODER_H_ |
||||
|
||||
#include <assert.h> |
||||
#include <stdint.h> |
||||
#include <string.h> |
||||
#include "upb/upb.h" |
||||
|
||||
#include "upb/port_def.inc" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#define UPB_MAX_WIRE_TYPE 5 |
||||
|
||||
/* The maximum number of bytes that it takes to encode a 64-bit varint. */ |
||||
#define UPB_PB_VARINT_MAX_LEN 10 |
||||
|
||||
/* Array of the "native" (ie. non-packed-repeated) wire type for the given a
|
||||
* descriptor type (upb_descriptortype_t). */ |
||||
extern const uint8_t upb_pb_native_wire_types[]; |
||||
|
||||
UPB_INLINE uint64_t byteswap64(uint64_t val) { |
||||
uint64_t byte = 0xff; |
||||
return (val & (byte << 56) >> 56) |
||||
| (val & (byte << 48) >> 40) |
||||
| (val & (byte << 40) >> 24) |
||||
| (val & (byte << 32) >> 8) |
||||
| (val & (byte << 24) << 8) |
||||
| (val & (byte << 16) << 24) |
||||
| (val & (byte << 8) << 40) |
||||
| (val & (byte << 0) << 56); |
||||
} |
||||
|
||||
/* Zig-zag encoding/decoding **************************************************/ |
||||
|
||||
UPB_INLINE int32_t upb_zzdec_32(uint64_t _n) { |
||||
uint32_t n = (uint32_t)_n; |
||||
return (n >> 1) ^ -(int32_t)(n & 1); |
||||
} |
||||
UPB_INLINE int64_t upb_zzdec_64(uint64_t n) { |
||||
return (n >> 1) ^ -(int64_t)(n & 1); |
||||
} |
||||
UPB_INLINE uint32_t upb_zzenc_32(int32_t n) { |
||||
return ((uint32_t)n << 1) ^ (n >> 31); |
||||
} |
||||
UPB_INLINE uint64_t upb_zzenc_64(int64_t n) { |
||||
return ((uint64_t)n << 1) ^ (n >> 63); |
||||
} |
||||
|
||||
/* Decoding *******************************************************************/ |
||||
|
||||
/* All decoding functions return this struct by value. */ |
||||
typedef struct { |
||||
const char *p; /* NULL if the varint was unterminated. */ |
||||
uint64_t val; |
||||
} upb_decoderet; |
||||
|
||||
UPB_INLINE upb_decoderet upb_decoderet_make(const char *p, uint64_t val) { |
||||
upb_decoderet ret; |
||||
ret.p = p; |
||||
ret.val = val; |
||||
return ret; |
||||
} |
||||
|
||||
upb_decoderet upb_vdecode_max8_branch32(upb_decoderet r); |
||||
upb_decoderet upb_vdecode_max8_branch64(upb_decoderet r); |
||||
|
||||
/* Template for a function that checks the first two bytes with branching
|
||||
* and dispatches 2-10 bytes with a separate function. Note that this may read |
||||
* up to 10 bytes, so it must not be used unless there are at least ten bytes |
||||
* left in the buffer! */ |
||||
#define UPB_VARINT_DECODER_CHECK2(name, decode_max8_function) \ |
||||
UPB_INLINE upb_decoderet upb_vdecode_check2_ ## name(const char *_p) { \
|
||||
uint8_t *p = (uint8_t*)_p; \
|
||||
upb_decoderet r; \
|
||||
if ((*p & 0x80) == 0) { \
|
||||
/* Common case: one-byte varint. */ \
|
||||
return upb_decoderet_make(_p + 1, *p & 0x7fU); \
|
||||
} \
|
||||
r = upb_decoderet_make(_p + 2, (*p & 0x7fU) | ((*(p + 1) & 0x7fU) << 7)); \
|
||||
if ((*(p + 1) & 0x80) == 0) { \
|
||||
/* Two-byte varint. */ \
|
||||
return r; \
|
||||
} \
|
||||
/* Longer varint, fallback to out-of-line function. */ \
|
||||
return decode_max8_function(r); \
|
||||
} |
||||
|
||||
UPB_VARINT_DECODER_CHECK2(branch32, upb_vdecode_max8_branch32) |
||||
UPB_VARINT_DECODER_CHECK2(branch64, upb_vdecode_max8_branch64) |
||||
#undef UPB_VARINT_DECODER_CHECK2 |
||||
|
||||
/* Our canonical functions for decoding varints, based on the currently
|
||||
* favored best-performing implementations. */ |
||||
UPB_INLINE upb_decoderet upb_vdecode_fast(const char *p) { |
||||
if (sizeof(long) == 8) |
||||
return upb_vdecode_check2_branch64(p); |
||||
else |
||||
return upb_vdecode_check2_branch32(p); |
||||
} |
||||
|
||||
|
||||
/* Encoding *******************************************************************/ |
||||
|
||||
UPB_INLINE int upb_value_size(uint64_t val) { |
||||
#ifdef __GNUC__ |
||||
/* 0-based, undef if val == 0. */ |
||||
int high_bit = val ? 63 - __builtin_clzll(val) : 0; |
||||
#else |
||||
int high_bit = 0; |
||||
uint64_t tmp = val; |
||||
while(tmp >>= 1) high_bit++; |
||||
#endif |
||||
return val == 0 ? 1 : high_bit / 8 + 1; |
||||
} |
||||
|
||||
/* Encodes a 64-bit varint into buf (which must be >=UPB_PB_VARINT_MAX_LEN
|
||||
* bytes long), returning how many bytes were used. |
||||
* |
||||
* TODO: benchmark and optimize if necessary. */ |
||||
UPB_INLINE size_t upb_vencode64(uint64_t val, char *buf) { |
||||
size_t i; |
||||
if (val == 0) { buf[0] = 0; return 1; } |
||||
i = 0; |
||||
while (val) { |
||||
uint8_t byte = val & 0x7fU; |
||||
val >>= 7; |
||||
if (val) byte |= 0x80U; |
||||
buf[i++] = byte; |
||||
} |
||||
return i; |
||||
} |
||||
|
||||
UPB_INLINE size_t upb_varint_size(uint64_t val) { |
||||
char buf[UPB_PB_VARINT_MAX_LEN]; |
||||
return upb_vencode64(val, buf); |
||||
} |
||||
|
||||
/* Encodes a 32-bit varint, *not* sign-extended. */ |
||||
UPB_INLINE uint64_t upb_vencode32(uint32_t val) { |
||||
char buf[UPB_PB_VARINT_MAX_LEN]; |
||||
size_t bytes = upb_vencode64(val, buf); |
||||
uint64_t ret = 0; |
||||
UPB_ASSERT(bytes <= 5); |
||||
memcpy(&ret, buf, bytes); |
||||
ret = _upb_be_swap64(ret); |
||||
UPB_ASSERT(ret <= 0xffffffffffU); |
||||
return ret; |
||||
} |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#include "upb/port_undef.inc" |
||||
|
||||
#endif /* UPB_VARINT_DECODER_H_ */ |
@ -1,17 +0,0 @@ |
||||
|
||||
#include "upb/sink.h" |
||||
|
||||
bool upb_bufsrc_putbuf(const char *buf, size_t len, upb_bytessink sink) { |
||||
void *subc; |
||||
bool ret; |
||||
upb_bufhandle handle = UPB_BUFHANDLE_INIT; |
||||
handle.buf = buf; |
||||
ret = upb_bytessink_start(sink, len, &subc); |
||||
if (ret && len != 0) { |
||||
ret = (upb_bytessink_putbuf(sink, subc, buf, len, &handle) >= len); |
||||
} |
||||
if (ret) { |
||||
ret = upb_bytessink_end(sink); |
||||
} |
||||
return ret; |
||||
} |
@ -1,517 +0,0 @@ |
||||
/*
|
||||
** upb::Sink (upb_sink) |
||||
** upb::BytesSink (upb_bytessink) |
||||
** |
||||
** A upb_sink is an object that binds a upb_handlers object to some runtime |
||||
** state. It is the object that can actually receive data via the upb_handlers |
||||
** interface. |
||||
** |
||||
** Unlike upb_def and upb_handlers, upb_sink is never frozen, immutable, or |
||||
** thread-safe. You can create as many of them as you want, but each one may |
||||
** only be used in a single thread at a time. |
||||
** |
||||
** If we compare with class-based OOP, a you can think of a upb_def as an |
||||
** abstract base class, a upb_handlers as a concrete derived class, and a |
||||
** upb_sink as an object (class instance). |
||||
*/ |
||||
|
||||
#ifndef UPB_SINK_H |
||||
#define UPB_SINK_H |
||||
|
||||
#include "upb/handlers.h" |
||||
|
||||
#include "upb/port_def.inc" |
||||
|
||||
#ifdef __cplusplus |
||||
namespace upb { |
||||
class BytesSink; |
||||
class Sink; |
||||
} |
||||
#endif |
||||
|
||||
/* upb_sink *******************************************************************/ |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
typedef struct { |
||||
const upb_handlers *handlers; |
||||
void *closure; |
||||
} upb_sink; |
||||
|
||||
#define PUTVAL(type, ctype) \ |
||||
UPB_INLINE bool upb_sink_put##type(upb_sink s, upb_selector_t sel, \
|
||||
ctype val) { \
|
||||
typedef upb_##type##_handlerfunc functype; \
|
||||
functype *func; \
|
||||
const void *hd; \
|
||||
if (!s.handlers) return true; \
|
||||
func = (functype *)upb_handlers_gethandler(s.handlers, sel, &hd); \
|
||||
if (!func) return true; \
|
||||
return func(s.closure, hd, val); \
|
||||
} |
||||
|
||||
PUTVAL(int32, int32_t) |
||||
PUTVAL(int64, int64_t) |
||||
PUTVAL(uint32, uint32_t) |
||||
PUTVAL(uint64, uint64_t) |
||||
PUTVAL(float, float) |
||||
PUTVAL(double, double) |
||||
PUTVAL(bool, bool) |
||||
#undef PUTVAL |
||||
|
||||
UPB_INLINE void upb_sink_reset(upb_sink *s, const upb_handlers *h, void *c) { |
||||
s->handlers = h; |
||||
s->closure = c; |
||||
} |
||||
|
||||
UPB_INLINE size_t upb_sink_putstring(upb_sink s, upb_selector_t sel, |
||||
const char *buf, size_t n, |
||||
const upb_bufhandle *handle) { |
||||
typedef upb_string_handlerfunc func; |
||||
func *handler; |
||||
const void *hd; |
||||
if (!s.handlers) return n; |
||||
handler = (func *)upb_handlers_gethandler(s.handlers, sel, &hd); |
||||
|
||||
if (!handler) return n; |
||||
return handler(s.closure, hd, buf, n, handle); |
||||
} |
||||
|
||||
UPB_INLINE bool upb_sink_putunknown(upb_sink s, const char *buf, size_t n) { |
||||
typedef upb_unknown_handlerfunc func; |
||||
func *handler; |
||||
const void *hd; |
||||
if (!s.handlers) return true; |
||||
handler = |
||||
(func *)upb_handlers_gethandler(s.handlers, UPB_UNKNOWN_SELECTOR, &hd); |
||||
|
||||
if (!handler) return n; |
||||
return handler(s.closure, hd, buf, n); |
||||
} |
||||
|
||||
UPB_INLINE bool upb_sink_startmsg(upb_sink s) { |
||||
typedef upb_startmsg_handlerfunc func; |
||||
func *startmsg; |
||||
const void *hd; |
||||
if (!s.handlers) return true; |
||||
startmsg = |
||||
(func *)upb_handlers_gethandler(s.handlers, UPB_STARTMSG_SELECTOR, &hd); |
||||
|
||||
if (!startmsg) return true; |
||||
return startmsg(s.closure, hd); |
||||
} |
||||
|
||||
UPB_INLINE bool upb_sink_endmsg(upb_sink s, upb_status *status) { |
||||
typedef upb_endmsg_handlerfunc func; |
||||
func *endmsg; |
||||
const void *hd; |
||||
if (!s.handlers) return true; |
||||
endmsg = |
||||
(func *)upb_handlers_gethandler(s.handlers, UPB_ENDMSG_SELECTOR, &hd); |
||||
|
||||
if (!endmsg) return true; |
||||
return endmsg(s.closure, hd, status); |
||||
} |
||||
|
||||
UPB_INLINE bool upb_sink_startseq(upb_sink s, upb_selector_t sel, |
||||
upb_sink *sub) { |
||||
typedef upb_startfield_handlerfunc func; |
||||
func *startseq; |
||||
const void *hd; |
||||
sub->closure = s.closure; |
||||
sub->handlers = s.handlers; |
||||
if (!s.handlers) return true; |
||||
startseq = (func*)upb_handlers_gethandler(s.handlers, sel, &hd); |
||||
|
||||
if (!startseq) return true; |
||||
sub->closure = startseq(s.closure, hd); |
||||
return sub->closure ? true : false; |
||||
} |
||||
|
||||
UPB_INLINE bool upb_sink_endseq(upb_sink s, upb_selector_t sel) { |
||||
typedef upb_endfield_handlerfunc func; |
||||
func *endseq; |
||||
const void *hd; |
||||
if (!s.handlers) return true; |
||||
endseq = (func*)upb_handlers_gethandler(s.handlers, sel, &hd); |
||||
|
||||
if (!endseq) return true; |
||||
return endseq(s.closure, hd); |
||||
} |
||||
|
||||
UPB_INLINE bool upb_sink_startstr(upb_sink s, upb_selector_t sel, |
||||
size_t size_hint, upb_sink *sub) { |
||||
typedef upb_startstr_handlerfunc func; |
||||
func *startstr; |
||||
const void *hd; |
||||
sub->closure = s.closure; |
||||
sub->handlers = s.handlers; |
||||
if (!s.handlers) return true; |
||||
startstr = (func*)upb_handlers_gethandler(s.handlers, sel, &hd); |
||||
|
||||
if (!startstr) return true; |
||||
sub->closure = startstr(s.closure, hd, size_hint); |
||||
return sub->closure ? true : false; |
||||
} |
||||
|
||||
UPB_INLINE bool upb_sink_endstr(upb_sink s, upb_selector_t sel) { |
||||
typedef upb_endfield_handlerfunc func; |
||||
func *endstr; |
||||
const void *hd; |
||||
if (!s.handlers) return true; |
||||
endstr = (func*)upb_handlers_gethandler(s.handlers, sel, &hd); |
||||
|
||||
if (!endstr) return true; |
||||
return endstr(s.closure, hd); |
||||
} |
||||
|
||||
UPB_INLINE bool upb_sink_startsubmsg(upb_sink s, upb_selector_t sel, |
||||
upb_sink *sub) { |
||||
typedef upb_startfield_handlerfunc func; |
||||
func *startsubmsg; |
||||
const void *hd; |
||||
sub->closure = s.closure; |
||||
if (!s.handlers) { |
||||
sub->handlers = NULL; |
||||
return true; |
||||
} |
||||
sub->handlers = upb_handlers_getsubhandlers_sel(s.handlers, sel); |
||||
startsubmsg = (func*)upb_handlers_gethandler(s.handlers, sel, &hd); |
||||
|
||||
if (!startsubmsg) return true; |
||||
sub->closure = startsubmsg(s.closure, hd); |
||||
return sub->closure ? true : false; |
||||
} |
||||
|
||||
UPB_INLINE bool upb_sink_endsubmsg(upb_sink s, upb_sink sub, |
||||
upb_selector_t sel) { |
||||
typedef upb_endfield_handlerfunc func; |
||||
func *endsubmsg; |
||||
const void *hd; |
||||
if (!s.handlers) return true; |
||||
endsubmsg = (func*)upb_handlers_gethandler(s.handlers, sel, &hd); |
||||
|
||||
if (!endsubmsg) return true; |
||||
return endsubmsg(sub.closure, hd); |
||||
} |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
|
||||
/* A upb::Sink is an object that binds a upb::Handlers object to some runtime
|
||||
* state. It represents an endpoint to which data can be sent. |
||||
* |
||||
* TODO(haberman): right now all of these functions take selectors. Should they |
||||
* take selectorbase instead? |
||||
* |
||||
* ie. instead of calling: |
||||
* sink->StartString(FOO_FIELD_START_STRING, ...) |
||||
* a selector base would let you say: |
||||
* sink->StartString(FOO_FIELD, ...) |
||||
* |
||||
* This would make call sites a little nicer and require emitting fewer selector |
||||
* definitions in .h files. |
||||
* |
||||
* But the current scheme has the benefit that you can retrieve a function |
||||
* pointer for any handler with handlers->GetHandler(selector), without having |
||||
* to have a separate GetHandler() function for each handler type. The JIT |
||||
* compiler uses this. To accommodate we'd have to expose a separate |
||||
* GetHandler() for every handler type. |
||||
* |
||||
* Also to ponder: selectors right now are independent of a specific Handlers |
||||
* instance. In other words, they allocate a number to every possible handler |
||||
* that *could* be registered, without knowing anything about what handlers |
||||
* *are* registered. That means that using selectors as table offsets prohibits |
||||
* us from compacting the handler table at Freeze() time. If the table is very |
||||
* sparse, this could be wasteful. |
||||
* |
||||
* Having another selector-like thing that is specific to a Handlers instance |
||||
* would allow this compacting, but then it would be impossible to write code |
||||
* ahead-of-time that can be bound to any Handlers instance at runtime. For |
||||
* example, a .proto file parser written as straight C will not know what |
||||
* Handlers it will be bound to, so when it calls sink->StartString() what |
||||
* selector will it pass? It needs a selector like we have today, that is |
||||
* independent of any particular upb::Handlers. |
||||
* |
||||
* Is there a way then to allow Handlers table compaction? */ |
||||
class upb::Sink { |
||||
public: |
||||
/* Constructor with no initialization; must be Reset() before use. */ |
||||
Sink() {} |
||||
|
||||
Sink(const Sink&) = default; |
||||
Sink& operator=(const Sink&) = default; |
||||
|
||||
Sink(const upb_sink& sink) : sink_(sink) {} |
||||
Sink &operator=(const upb_sink &sink) { |
||||
sink_ = sink; |
||||
return *this; |
||||
} |
||||
|
||||
upb_sink sink() { return sink_; } |
||||
|
||||
/* Constructs a new sink for the given frozen handlers and closure.
|
||||
* |
||||
* TODO: once the Handlers know the expected closure type, verify that T |
||||
* matches it. */ |
||||
template <class T> Sink(const upb_handlers* handlers, T* closure) { |
||||
Reset(handlers, closure); |
||||
} |
||||
|
||||
upb_sink* ptr() { return &sink_; } |
||||
|
||||
/* Resets the value of the sink. */ |
||||
template <class T> void Reset(const upb_handlers* handlers, T* closure) { |
||||
upb_sink_reset(&sink_, handlers, closure); |
||||
} |
||||
|
||||
/* Returns the top-level object that is bound to this sink.
|
||||
* |
||||
* TODO: once the Handlers know the expected closure type, verify that T |
||||
* matches it. */ |
||||
template <class T> T* GetObject() const { |
||||
return static_cast<T*>(sink_.closure); |
||||
} |
||||
|
||||
/* Functions for pushing data into the sink.
|
||||
* |
||||
* These return false if processing should stop (either due to error or just |
||||
* to suspend). |
||||
* |
||||
* These may not be called from within one of the same sink's handlers (in |
||||
* other words, handlers are not re-entrant). */ |
||||
|
||||
/* Should be called at the start and end of every message; both the top-level
|
||||
* message and submessages. This means that submessages should use the |
||||
* following sequence: |
||||
* sink->StartSubMessage(startsubmsg_selector); |
||||
* sink->StartMessage(); |
||||
* // ...
|
||||
* sink->EndMessage(&status); |
||||
* sink->EndSubMessage(endsubmsg_selector); */ |
||||
bool StartMessage() { return upb_sink_startmsg(sink_); } |
||||
bool EndMessage(upb_status *status) { |
||||
return upb_sink_endmsg(sink_, status); |
||||
} |
||||
|
||||
/* Putting of individual values. These work for both repeated and
|
||||
* non-repeated fields, but for repeated fields you must wrap them in |
||||
* calls to StartSequence()/EndSequence(). */ |
||||
bool PutInt32(HandlersPtr::Selector s, int32_t val) { |
||||
return upb_sink_putint32(sink_, s, val); |
||||
} |
||||
|
||||
bool PutInt64(HandlersPtr::Selector s, int64_t val) { |
||||
return upb_sink_putint64(sink_, s, val); |
||||
} |
||||
|
||||
bool PutUInt32(HandlersPtr::Selector s, uint32_t val) { |
||||
return upb_sink_putuint32(sink_, s, val); |
||||
} |
||||
|
||||
bool PutUInt64(HandlersPtr::Selector s, uint64_t val) { |
||||
return upb_sink_putuint64(sink_, s, val); |
||||
} |
||||
|
||||
bool PutFloat(HandlersPtr::Selector s, float val) { |
||||
return upb_sink_putfloat(sink_, s, val); |
||||
} |
||||
|
||||
bool PutDouble(HandlersPtr::Selector s, double val) { |
||||
return upb_sink_putdouble(sink_, s, val); |
||||
} |
||||
|
||||
bool PutBool(HandlersPtr::Selector s, bool val) { |
||||
return upb_sink_putbool(sink_, s, val); |
||||
} |
||||
|
||||
/* Putting of string/bytes values. Each string can consist of zero or more
|
||||
* non-contiguous buffers of data. |
||||
* |
||||
* For StartString(), the function will write a sink for the string to "sub." |
||||
* The sub-sink must be used for any/all PutStringBuffer() calls. */ |
||||
bool StartString(HandlersPtr::Selector s, size_t size_hint, Sink* sub) { |
||||
upb_sink sub_c; |
||||
bool ret = upb_sink_startstr(sink_, s, size_hint, &sub_c); |
||||
*sub = sub_c; |
||||
return ret; |
||||
} |
||||
|
||||
size_t PutStringBuffer(HandlersPtr::Selector s, const char *buf, size_t len, |
||||
const upb_bufhandle *handle) { |
||||
return upb_sink_putstring(sink_, s, buf, len, handle); |
||||
} |
||||
|
||||
bool EndString(HandlersPtr::Selector s) { |
||||
return upb_sink_endstr(sink_, s); |
||||
} |
||||
|
||||
/* For submessage fields.
|
||||
* |
||||
* For StartSubMessage(), the function will write a sink for the string to |
||||
* "sub." The sub-sink must be used for any/all handlers called within the |
||||
* submessage. */ |
||||
bool StartSubMessage(HandlersPtr::Selector s, Sink* sub) { |
||||
upb_sink sub_c; |
||||
bool ret = upb_sink_startsubmsg(sink_, s, &sub_c); |
||||
*sub = sub_c; |
||||
return ret; |
||||
} |
||||
|
||||
bool EndSubMessage(HandlersPtr::Selector s, Sink sub) { |
||||
return upb_sink_endsubmsg(sink_, sub.sink_, s); |
||||
} |
||||
|
||||
/* For repeated fields of any type, the sequence of values must be wrapped in
|
||||
* these calls. |
||||
* |
||||
* For StartSequence(), the function will write a sink for the string to |
||||
* "sub." The sub-sink must be used for any/all handlers called within the |
||||
* sequence. */ |
||||
bool StartSequence(HandlersPtr::Selector s, Sink* sub) { |
||||
upb_sink sub_c; |
||||
bool ret = upb_sink_startseq(sink_, s, &sub_c); |
||||
*sub = sub_c; |
||||
return ret; |
||||
} |
||||
|
||||
bool EndSequence(HandlersPtr::Selector s) { |
||||
return upb_sink_endseq(sink_, s); |
||||
} |
||||
|
||||
/* Copy and assign specifically allowed.
|
||||
* We don't even bother making these members private because so many |
||||
* functions need them and this is mainly just a dumb data container anyway. |
||||
*/ |
||||
|
||||
private: |
||||
upb_sink sink_; |
||||
}; |
||||
|
||||
#endif /* __cplusplus */ |
||||
|
||||
/* upb_bytessink **************************************************************/ |
||||
|
||||
typedef struct { |
||||
const upb_byteshandler *handler; |
||||
void *closure; |
||||
} upb_bytessink ; |
||||
|
||||
UPB_INLINE void upb_bytessink_reset(upb_bytessink* s, const upb_byteshandler *h, |
||||
void *closure) { |
||||
s->handler = h; |
||||
s->closure = closure; |
||||
} |
||||
|
||||
UPB_INLINE bool upb_bytessink_start(upb_bytessink s, size_t size_hint, |
||||
void **subc) { |
||||
typedef upb_startstr_handlerfunc func; |
||||
func *start; |
||||
*subc = s.closure; |
||||
if (!s.handler) return true; |
||||
start = (func *)s.handler->table[UPB_STARTSTR_SELECTOR].func; |
||||
|
||||
if (!start) return true; |
||||
*subc = start(s.closure, |
||||
s.handler->table[UPB_STARTSTR_SELECTOR].attr.handler_data, |
||||
size_hint); |
||||
return *subc != NULL; |
||||
} |
||||
|
||||
UPB_INLINE size_t upb_bytessink_putbuf(upb_bytessink s, void *subc, |
||||
const char *buf, size_t size, |
||||
const upb_bufhandle* handle) { |
||||
typedef upb_string_handlerfunc func; |
||||
func *putbuf; |
||||
if (!s.handler) return true; |
||||
putbuf = (func *)s.handler->table[UPB_STRING_SELECTOR].func; |
||||
|
||||
if (!putbuf) return true; |
||||
return putbuf(subc, s.handler->table[UPB_STRING_SELECTOR].attr.handler_data, |
||||
buf, size, handle); |
||||
} |
||||
|
||||
UPB_INLINE bool upb_bytessink_end(upb_bytessink s) { |
||||
typedef upb_endfield_handlerfunc func; |
||||
func *end; |
||||
if (!s.handler) return true; |
||||
end = (func *)s.handler->table[UPB_ENDSTR_SELECTOR].func; |
||||
|
||||
if (!end) return true; |
||||
return end(s.closure, |
||||
s.handler->table[UPB_ENDSTR_SELECTOR].attr.handler_data); |
||||
} |
||||
|
||||
#ifdef __cplusplus |
||||
|
||||
class upb::BytesSink { |
||||
public: |
||||
BytesSink() {} |
||||
|
||||
BytesSink(const BytesSink&) = default; |
||||
BytesSink& operator=(const BytesSink&) = default; |
||||
|
||||
BytesSink(const upb_bytessink& sink) : sink_(sink) {} |
||||
BytesSink &operator=(const upb_bytessink &sink) { |
||||
sink_ = sink; |
||||
return *this; |
||||
} |
||||
|
||||
upb_bytessink sink() { return sink_; } |
||||
|
||||
/* Constructs a new sink for the given frozen handlers and closure.
|
||||
* |
||||
* TODO(haberman): once the Handlers know the expected closure type, verify |
||||
* that T matches it. */ |
||||
template <class T> BytesSink(const upb_byteshandler* handler, T* closure) { |
||||
upb_bytessink_reset(sink_, handler, closure); |
||||
} |
||||
|
||||
/* Resets the value of the sink. */ |
||||
template <class T> void Reset(const upb_byteshandler* handler, T* closure) { |
||||
upb_bytessink_reset(&sink_, handler, closure); |
||||
} |
||||
|
||||
bool Start(size_t size_hint, void **subc) { |
||||
return upb_bytessink_start(sink_, size_hint, subc); |
||||
} |
||||
|
||||
size_t PutBuffer(void *subc, const char *buf, size_t len, |
||||
const upb_bufhandle *handle) { |
||||
return upb_bytessink_putbuf(sink_, subc, buf, len, handle); |
||||
} |
||||
|
||||
bool End() { |
||||
return upb_bytessink_end(sink_); |
||||
} |
||||
|
||||
private: |
||||
upb_bytessink sink_; |
||||
}; |
||||
|
||||
#endif /* __cplusplus */ |
||||
|
||||
/* upb_bufsrc *****************************************************************/ |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
bool upb_bufsrc_putbuf(const char *buf, size_t len, upb_bytessink sink); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
|
||||
namespace upb { |
||||
template <class T> bool PutBuffer(const T& str, BytesSink sink) { |
||||
return upb_bufsrc_putbuf(str.data(), str.size(), sink.sink()); |
||||
} |
||||
} |
||||
|
||||
#endif /* __cplusplus */ |
||||
|
||||
#include "upb/port_undef.inc" |
||||
|
||||
#endif |
Loading…
Reference in new issue