From 1aafd4111b9b6d08d2d0937b0f396a4caa9ea04d Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Sat, 8 Jul 2017 00:00:05 -0700 Subject: A good start on upb_encode and upb_decode. --- tools/make_c_api.lua | 139 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 111 insertions(+), 28 deletions(-) (limited to 'tools/make_c_api.lua') diff --git a/tools/make_c_api.lua b/tools/make_c_api.lua index aaf5d1e..62fd370 100644 --- a/tools/make_c_api.lua +++ b/tools/make_c_api.lua @@ -47,7 +47,6 @@ local function to_preproc(...) return string.upper(to_cident(...)) end - -- Strips away last path element, ie: -- foo.Bar.Baz -> foo.Bar local function remove_name(name) @@ -60,6 +59,10 @@ local function remove_name(name) return string.sub(name, 1, package_end) end +local function enum_value_symbol(enumdef, name) + return to_cident(remove_name(enumdef:full_name())) .. "_" .. name +end + local function dump_enum_vals(enumdef, append) local enum_vals = {} @@ -96,7 +99,7 @@ local function dump_enum_vals(enumdef, append) local cident = to_cident(remove_name(enumdef:full_name())) for i, pair in ipairs(enum_vals) do k, v = pair[1], pair[2] - append(' %s = %d', cident .. "_" .. k, v) + append(' %s = %d', enum_value_symbol(enumdef, k), v) if i == #enum_vals then append('\n') else @@ -105,6 +108,20 @@ local function dump_enum_vals(enumdef, append) end end +local function field_default(field) + if field:type() == upb.TYPE_MESSAGE then + return "NULL" + elseif field:type() == upb.TYPE_STRING or + field:type() == upb.TYPE_BYTES then + local default = field:default() or "" + return string.format('upb_stringview_make("%s", strlen("%s"))', field:default(), field:default()) + elseif field:type() == upb.TYPE_ENUM then + return enum_value_symbol(field:subdef(), field:default()) + else + return field:default(); + end +end + local function ctype(field) if field:label() == upb.LABEL_REPEATED then return "upb_array*" @@ -134,19 +151,18 @@ end local function field_layout_rank(field) -- Order: -- 1, 2, 3. primitive fields (8, 4, 1 byte) - -- 4. oneof fields - -- 5. string fields - -- 6. submessage fields - -- 7. repeated fields + -- 4. string fields + -- 5. submessage fields + -- 6. repeated fields local rank if field:containing_oneof() then - rank = 4 + rank = 100 -- These go last (actually we skip them). elseif field:label() == upb.LABEL_REPEATED then - rank = 7 - elseif field:type() == upb.TYPE_MESSAGE then rank = 6 - elseif field:type() == upb.TYPE_STRING or field:type() == upb.TYPE_BYTES then + elseif field:type() == upb.TYPE_MESSAGE then rank = 5 + elseif field:type() == upb.TYPE_STRING or field:type() == upb.TYPE_BYTES then + rank = 4 elseif field:type() == upb.TYPE_BOOL then rank = 3 elseif field:type() == upb.TYPE_FLOAT or @@ -257,6 +273,8 @@ local function write_c_file(filedef, hfilename, append) emit_file_warning(filedef, append) append('#include \n') + append('#include "upb/decode.h"\n\n') + append('#include "upb/encode.h"\n\n') append('#include "upb/msg.h"\n') append('#include "upb/upb.h"\n') append('#include "%s"\n\n', hfilename) @@ -273,13 +291,29 @@ local function write_c_file(filedef, hfilename, append) local fields_array_ref = "NULL" local submsgs_array_ref = "NULL" + local oneofs_array_ref = "NULL" local field_count = 0 local submsg_count = 0 local submsg_set = {} local submsg_indexes = {} local hasbit_count = 0 local hasbit_indexes = {} - -- TODO(haberman): oneofs + local oneof_count = 0 + local oneof_indexes = {} + + -- Create a layout order for oneofs. + local oneofs_layout_order = {} + for oneof in msg:oneofs() do + table.insert(oneofs_layout_order, oneof) + end + table.sort(oneofs_layout_order, function(a, b) + return a:name() < b:name() + end) + + for _, oneof in ipairs(oneofs_layout_order) do + oneof_indexes[oneof] = oneof_count + oneof_count = oneof_count + 1 + end -- Create a layout order for fields. We use this order for the struct and -- for offsets, but our list of fields we keep in field number order. @@ -301,6 +335,8 @@ local function write_c_file(filedef, hfilename, append) end) append('struct %s {\n', msgname) + + -- Non-oneof fields. for _, field in ipairs(fields_layout_order) do field_count = field_count + 1 @@ -309,15 +345,46 @@ local function write_c_file(filedef, hfilename, append) submsg_set[field:subdef()] = true end - if has_hasbit(field) then - hasbit_indexes[field] = hasbit_count - hasbit_count = hasbit_count + 1 + if field:containing_oneof() then + -- Do nothing now + else + if has_hasbit(field) then + hasbit_indexes[field] = hasbit_count + hasbit_count = hasbit_count + 1 + end + + append(' %s %s;\n', ctype(field), field:name()) end + end - append(' %s %s;\n', ctype(field), field:name()) + local oneof_last_fields = {} + -- Oneof fields. + for oneof in msg:oneofs() do + local fullname = to_cident(oneof:containing_type():full_name() .. "." .. oneof:name()) + append(' union {\n') + oneof_last_fields[oneof] = "" + for field in oneof:fields() do + oneof_last_fields[oneof] = field:name() + append(' %s %s;\n', ctype(field), field:name()) + end + append(' } %s;\n', oneof:name()) + append(' %s_oneofcases %s_case;\n', fullname, oneof:name()) end + append('};\n\n') + if oneof_count > 0 then + local oneofs_array_name = msgname .. "_oneofs" + oneofs_array_ref = "&" .. oneofs_array_name .. "[0]" + append('static const upb_msglayout_oneofinit_v1 %s[%s] = {\n', + oneofs_array_name, oneof_count) + for _, oneof in ipairs(oneofs_layout_order) do + append(' {offsetof(%s, %s), offsetof(%s, %s_case)},\n', + msgname, oneof:name(), msgname, oneof:name()) + end + append('};\n\n') + end + if submsg_count > 0 then -- TODO(haberman): could save a little bit of space by only generating a -- "submsgs" array for every strongly-connected component. @@ -354,11 +421,14 @@ local function write_c_file(filedef, hfilename, append) if field:type() == upb.TYPE_MESSAGE then submsg_index = submsg_indexes[field:subdef()] end + if field:containing_oneof() then + oneof_index = oneof_indexes[field:containing_oneof()] + end -- TODO(haberman): oneofs. append(' {%s, offsetof(%s, %s), %s, %s, %s, %s, %s},\n', field:number(), msgname, - field:name(), + (field:containing_oneof() and field:containing_oneof():name()) or field:name(), hasbit_indexes[field] or "-1", oneof_index, submsg_index, @@ -371,7 +441,7 @@ local function write_c_file(filedef, hfilename, append) append('const upb_msglayout_msginit_v1 %s_msginit = {\n', msgname) append(' %s,\n', submsgs_array_ref) append(' %s,\n', fields_array_ref) - append(' NULL, /* TODO. oneofs */\n') + append(' %s,\n', oneofs_array_ref) append(' NULL, /* TODO. default_msg */\n') append(' UPB_ALIGNED_SIZEOF(%s), %s, %s, %s, %s\n', msgname, field_count, @@ -390,36 +460,49 @@ local function write_c_file(filedef, hfilename, append) append('%s *%s_parsenew(upb_stringview buf, upb_env *env) {\n', msgname, msgname) - append(' UPB_UNUSED(buf);\n') - append(' UPB_UNUSED(env);\n') - append(' return NULL;\n') + append(' %s *msg = %s_new(env);\n', msgname, msgname) + append(' if (upb_decode(buf, msg, &%s_msginit, env)) {\n', msgname) + append(' return msg;\n') + append(' } else {\n') + append(' return NULL;\n') + append(' }\n') append('}\n') append('char *%s_serialize(%s *msg, upb_env *env, size_t *size) {\n', msgname, msgname) - append(' UPB_UNUSED(msg);\n') - append(' UPB_UNUSED(env);\n') - append(' UPB_UNUSED(size);\n') - append(' return NULL; /* TODO. */\n') + append(' return upb_encode(msg, &%s_msginit, env, size);\n', msgname) append('}\n') for field in msg:fields() do local typename = ctype(field) append('%s %s_%s(const %s *msg) {\n', typename, msgname, field:name(), msgname); - append(' return msg->%s;\n', field:name()) + if field:containing_oneof() then + local oneof = field:containing_oneof() + append(' return msg->%s_case == %s ? msg->%s.%s : %s;\n', + oneof:name(), field:number(), oneof:name(), field:name(), + field_default(field)) + else + append(' return msg->%s;\n', field:name()) + end append('}\n') append('void %s_set_%s(%s *msg, %s value) {\n', msgname, field:name(), msgname, typename); - append(' msg->%s = value;\n', field:name()) + if field:containing_oneof() then + local oneof = field:containing_oneof() + append(' msg->%s.%s = value;\n', oneof:name(), field:name()) + append(' msg->%s_case = %s;\n', oneof:name(), field:number()) + else + append(' msg->%s = value;\n', field:name()) + end append('}\n') end for oneof in msg:oneofs() do local fullname = to_cident(oneof:containing_type():full_name() .. "." .. oneof:name()) append('%s_oneofcases %s_case(const %s *msg) {\n', fullname, fullname, msgname) - append(' return 0; /* TODO. */') - append('}') + append(' return msg->%s_case;\n', oneof:name()) + append('}\n') end end end -- cgit v1.2.3