1%%% Copyright (C) 2017 Tomas Abrahamsson 2%%% 3%%% Author: Tomas Abrahamsson <tab@lysator.liu.se> 4%%% 5%%% This library is free software; you can redistribute it and/or 6%%% modify it under the terms of the GNU Lesser General Public 7%%% License as published by the Free Software Foundation; either 8%%% version 2.1 of the License, or (at your option) any later version. 9%%% 10%%% This library is distributed in the hope that it will be useful, 11%%% but WITHOUT ANY WARRANTY; without even the implied warranty of 12%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13%%% Lesser General Public License for more details. 14%%% 15%%% You should have received a copy of the GNU Lesser General Public 16%%% License along with this library; if not, write to the Free Software 17%%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 18%%% MA 02110-1301 USA 19 20%%% @doc Analyzes proto defs. Result is used by the generator modules 21%%% and functions. 22%%% @private 23 24-module(gpb_analyzer). 25 26-export([analyze_defs/2]). 27 28-include("../include/gpb.hrl"). 29-include("gpb_compile.hrl"). 30 31-define(is_map_type(X), (is_tuple(X) 32 andalso tuple_size(X) =:= 3 33 andalso element(1, X) =:= map)). 34 35%% -- analysis ----------------------------------------------------- 36 37analyze_defs(Defs, Opts) -> 38 MapTypes = find_map_types(Defs), 39 MapsAsMsgs = map_types_to_msgs(MapTypes), 40 MapMsgEnums = enums_for_maps_as_msgs(MapTypes, Defs), 41 Translations = compute_translations(Defs, Opts), 42 KnownMsgSize = find_msgsizes_known_at_compile_time(MapsAsMsgs ++ Defs), 43 #anres{used_types = find_used_types(Defs), 44 known_msg_size = KnownMsgSize, 45 fixlen_types = find_fixlen_types(MapsAsMsgs ++ Defs), 46 num_packed_fields = find_num_packed_fields(MapsAsMsgs ++ Defs), 47 num_fields = find_num_fields(MapsAsMsgs ++ Defs), 48 d_field_pass_method = compute_decode_field_pass_methods( 49 MapsAsMsgs ++ Defs, Opts), 50 maps_as_msgs = MapsAsMsgs ++ MapMsgEnums, 51 translations = Translations, 52 map_types = MapTypes, 53 map_value_types = compute_map_value_types(MapTypes), 54 group_occurrences = find_group_occurrences(Defs), 55 has_p3_opt_strings = has_p3_opt_strings(Defs)}. 56 57find_map_types(Defs) -> 58 gpb_lib:fold_msg_or_group_fields( 59 fun(_, _MsgName, #?gpb_field{type={map,KeyType,ValueType}}, Acc) -> 60 sets:add_element({KeyType,ValueType}, Acc); 61 (_, _MsgName, _Field, Acc) -> 62 Acc 63 end, 64 sets:new(), 65 Defs). 66 67map_types_to_msgs(MapTypes) -> 68 sets:fold(fun({KeyType, ValueType}, Acc) -> 69 [{{msg, gpb_lib:map_type_to_msg_name(KeyType,ValueType)}, 70 gpb:map_item_pseudo_fields(KeyType, ValueType)} | Acc] 71 end, 72 [], 73 MapTypes). 74 75enums_for_maps_as_msgs(MapTypes, Defs) -> 76 MapEnumNames = sets:fold(fun({_Key, {enum, EName}}, Acc) -> [EName | Acc]; 77 ({_KeyType, _ValueType}, Acc) -> Acc 78 end, 79 [], 80 MapTypes), 81 [Enum || {{enum, EnumName}, _}=Enum <- Defs, 82 lists:member(EnumName, MapEnumNames)]. 83 84compute_map_value_types(MapTypes) -> 85 sets:fold( 86 fun({_KT, {msg,_}}, {_SubMsgs, NonSubMsgs}) -> {true, NonSubMsgs}; 87 ({_KT, _VT}, {SubMsgs, _NonSubMsgs}) -> {SubMsgs, true} 88 end, 89 {false, false}, 90 MapTypes). 91 92find_used_types(Defs) -> 93 gpb_lib:fold_msg_or_group_fields( 94 fun(_Type, _MsgName, #?gpb_field{type={map,KeyType,ValueType}}, Acc) -> 95 Acc1 = sets:add_element(KeyType, Acc), 96 sets:add_element(ValueType, Acc1); 97 (_Type, _MsgName, #?gpb_field{type=Type}, Acc) -> 98 sets:add_element(Type, Acc) 99 end, 100 sets:new(), 101 Defs). 102 103find_fixlen_types(Defs) -> 104 gpb_lib:fold_msg_or_group_fields( 105 fun(_, _, #?gpb_field{type=Type, occurrence=Occ}=FieldDef, Acc) -> 106 IsPacked = gpb_lib:is_packed(FieldDef), 107 FixlenTypeInfo = #ft{type = Type, 108 occurrence = Occ, 109 is_packed = IsPacked}, 110 case Type of 111 fixed32 -> sets:add_element(FixlenTypeInfo, Acc); 112 sfixed32 -> sets:add_element(FixlenTypeInfo, Acc); 113 float -> sets:add_element(FixlenTypeInfo, Acc); 114 fixed64 -> sets:add_element(FixlenTypeInfo, Acc); 115 sfixed64 -> sets:add_element(FixlenTypeInfo, Acc); 116 double -> sets:add_element(FixlenTypeInfo, Acc); 117 _ -> Acc 118 end 119 end, 120 sets:new(), 121 Defs). 122 123find_num_packed_fields(Defs) -> 124 gpb_lib:fold_msg_or_group_fields( 125 fun(_, _MsgName, FieldDef, Acc) -> 126 case gpb_lib:is_packed(FieldDef) of 127 true -> Acc + 1; 128 false -> Acc 129 end 130 end, 131 0, 132 Defs). 133 134find_num_fields(Defs) -> 135 lists:foldl(fun({_msg_or_group, MsgName, MsgDef}, Acc) -> 136 dict:store(MsgName, length(MsgDef), Acc) 137 end, 138 dict:new(), 139 gpb_lib:msgs_or_groups(Defs)). 140 141find_msgsizes_known_at_compile_time(Defs) -> 142 T = ets:new(gpb_msg_sizes, [set, public]), 143 [find_msgsize(MsgName, Defs, T) || {{msg,MsgName},_Fields} <- Defs], 144 Result = dict:from_list(ets:tab2list(T)), 145 ets:delete(T), 146 Result. 147 148find_msgsize(MsgName, Defs, T) -> 149 case ets:lookup(T, MsgName) of 150 [] -> 151 {{msg,MsgName}, Fields} = lists:keyfind({msg,MsgName}, 1, Defs), 152 Result = find_msgsize_2(Fields, 0, Defs, T), 153 ets:insert(T, {MsgName, Result}), 154 Result; 155 [{MsgName, Result}] -> 156 Result 157 end. 158 159find_groupsize(GroupName, Defs, T) -> 160 {{group,GroupName}, Fields} = lists:keyfind({group,GroupName}, 1, Defs), 161 find_msgsize_2(Fields, 0, Defs, T). 162 163find_msgsize_2([#gpb_oneof{} | _], _AccSize, _Defs, _T) -> 164 undefined; 165find_msgsize_2([#?gpb_field{occurrence=repeated} | _], _AccSize, _Defs, _T) -> 166 undefined; 167find_msgsize_2([#?gpb_field{occurrence=optional} | _], _AccSize, _Defs, _T) -> 168 undefined; 169find_msgsize_2([#?gpb_field{type=Type, fnum=FNum} | Rest], AccSize, Defs, T) -> 170 FKeySize = 171 case Type of 172 {group, _} -> 173 not_applicable; 174 _ -> 175 FKey = (FNum bsl 3) bor gpb:encode_wiretype(Type), 176 byte_size(gpb:encode_varint(FKey)) 177 end, 178 case Type of 179 sint32 -> undefined; 180 sint64 -> undefined; 181 int32 -> undefined; 182 int64 -> undefined; 183 uint32 -> undefined; 184 uint64 -> undefined; 185 bool -> find_msgsize_2(Rest, AccSize+FKeySize+1, Defs, T); 186 {enum,EnumName} -> 187 case all_enum_values_encode_to_same_size(EnumName, Defs) of 188 {yes, ESize} -> 189 find_msgsize_2(Rest, AccSize+FKeySize+ESize, Defs, T); 190 no -> 191 undefined 192 end; 193 fixed64 -> find_msgsize_2(Rest, AccSize+FKeySize+8, Defs, T); 194 sfixed64 -> find_msgsize_2(Rest, AccSize+FKeySize+8, Defs, T); 195 double -> find_msgsize_2(Rest, AccSize+FKeySize+8, Defs, T); 196 string -> undefined; 197 bytes -> undefined; 198 {msg,MsgName} -> 199 case find_msgsize(MsgName, Defs, T) of 200 MsgSize when is_integer(MsgSize) -> 201 SizeOfLength = byte_size(gpb:encode_varint(MsgSize)), 202 SubMsgFieldSize = FKeySize + SizeOfLength + MsgSize, 203 find_msgsize_2(Rest, AccSize + SubMsgFieldSize, Defs, T); 204 undefined -> 205 undefined 206 end; 207 {group,GroupName} -> 208 case find_groupsize(GroupName, Defs, T) of 209 GroupSize when is_integer(GroupSize) -> 210 StartTag = (FNum bsl 3) + gpb:encode_wiretype(group_start), 211 EndTag = (FNum bsl 3) + gpb:encode_wiretype(group_end), 212 SizeOfStartTag = byte_size(gpb:encode_varint(StartTag)), 213 SizeOfEndTag = byte_size(gpb:encode_varint(EndTag)), 214 GroupFieldSize = SizeOfStartTag + GroupSize + SizeOfEndTag, 215 find_msgsize_2(Rest, AccSize + GroupFieldSize, Defs, T); 216 undefined -> 217 undefined 218 end; 219 fixed32 -> find_msgsize_2(Rest, AccSize+FKeySize+4, Defs, T); 220 sfixed32 -> find_msgsize_2(Rest, AccSize+FKeySize+4, Defs, T); 221 float -> find_msgsize_2(Rest, AccSize+FKeySize+4, Defs, T) 222 end; 223find_msgsize_2([], AccSize, _Defs, _T) -> 224 AccSize. 225 226 227all_enum_values_encode_to_same_size(EnumName, Defs) -> 228 {{enum,EnumName}, EnumDef} = lists:keyfind({enum,EnumName}, 1, Defs), 229 EnumSizes = [begin 230 <<N:64/unsigned-native>> = <<Value:64/signed-native>>, 231 byte_size(gpb:encode_varint(N)) 232 end 233 || {_EnumSym, Value} <- EnumDef], 234 case lists:usort(EnumSizes) of 235 [Size] -> {yes, Size}; 236 _ -> no 237 end. 238 239compute_decode_field_pass_methods(Defs, Opts) -> 240 lists:foldl(fun({_Type, Name, Fields}, D) -> 241 PassHow = d_field_pass_method(Name, Fields, Opts), 242 %% FIXME:GROUP: are all group+msg names unique? 243 dict:store(Name, PassHow, D) 244 end, 245 dict:new(), 246 gpb_lib:msgs_or_groups(Defs)). 247 248d_field_pass_method(MsgName, MsgDef, Opts) -> 249 %% Allow overriding options, mainly intended for testing 250 case proplists:get_value({field_pass_method,MsgName}, Opts) of 251 undefined -> 252 case proplists:get_value(field_pass_method, Opts) of 253 undefined -> 254 d_field_pass_method(MsgDef); 255 Method when Method==pass_as_record; Method==pass_as_params -> 256 Method 257 end; 258 Method when Method==pass_as_record; Method==pass_as_params -> 259 Method 260 end. 261 262d_field_pass_method(MsgDef) -> 263 %% Compute estimated costs: 264 %% Either passing a message record, or pass the fields as parameters 265 %% to the functions, one parameter for each field, then as the last 266 %% operation, stuff all parameters into a record. 267 %% 268 %% There are different advantages and disadvantages: 269 %% - Updating fields in a record means the vm will have to verify 270 %% that the term is a record (for each time a field is parsed/added) 271 %% - Passing the fields eliminates the cost above, but for each 272 %% (non-tail-recursive) function call, the field-parameters will 273 %% be saved to the stack, then restored after the call. 274 %% Such function calls, are: call to unicode:characters_to_list 275 %% for strings, calls to parse sub messages or packed fields and 276 %% final top-level calls to lists:reverse for repeated fields. 277 NF = length(MsgDef), %% num fields (awk-istic terminology) 278 if NF >= 250 -> 279 pass_as_record; %% Functions can take at most 255 arguments 280 NF == 0 -> 281 pass_as_params; 282 true -> 283 NumSubMsgFields = count_submsg_fields(MsgDef), 284 NumMapFields = count_map_fields(MsgDef), 285 NumGroupFields = count_group_fields(MsgDef), 286 IsMsgDominatedBySubMsgsOrMaps = 287 (NumSubMsgFields + NumMapFields + NumGroupFields) / NF > 0.5, 288 if IsMsgDominatedBySubMsgsOrMaps, NF >= 100 -> 289 pass_as_record; 290 true -> 291 pass_as_params 292 end 293 end. 294 295count_submsg_fields(MsgDef) -> 296 gpb_lib:fold_msgdef_fields( 297 fun(#?gpb_field{type={msg,_}}, N) -> N+1; 298 (#?gpb_field{}, N) -> N 299 end, 300 0, 301 MsgDef). 302 303count_map_fields(MsgDef) -> 304 gpb_lib:fold_msgdef_fields( 305 fun(#?gpb_field{type={map,_,_}}, N) -> N+1; 306 (#?gpb_field{}, N) -> N 307 end, 308 0, 309 MsgDef). 310 311count_group_fields(MsgDef) -> 312 gpb_lib:fold_msgdef_fields( 313 fun(#?gpb_field{type={group,_}}, N) -> N+1; 314 (#?gpb_field{}, N) -> N 315 end, 316 0, 317 MsgDef). 318 319%% Returns: [{Path, [{Op, [Calls]}]}] 320compute_translations(Defs, Opts) -> 321 remove_empty_translations( 322 remove_merge_translations_for_repeated_elements( 323 lists:foldl( 324 fun({_Name, Dict}, Acc) -> 325 dict:merge( 326 fun(_Key, Ts1, Ts2) -> merge_transls(Ts1, Ts2) end, 327 Acc, Dict) 328 end, 329 dict:new(), 330 [{map_translations, compute_map_translations(Defs, Opts)}, 331 {type_translations, compute_type_translations(Defs, Opts)}, 332 {field_translations, compute_field_translations(Defs, Opts)}]))). 333 334dict_from_translation_list(PTransls) -> 335 lists:foldl( 336 fun({Path, Transls1}, D) -> 337 case dict:find(Path, D) of 338 {ok, Transls2} -> 339 dict:store(Path, merge_transls(Transls1, Transls2), D); 340 error -> 341 dict:store(Path, merge_transls(Transls1, []), D) 342 end 343 end, 344 dict:new(), 345 PTransls). 346 347merge_transls(Transls1, Transls2) -> 348 dict:to_list( 349 dict:merge( 350 fun(encode, L1, L2) -> L2 ++ L1; 351 (decode, L1, L2) -> L1 ++ L2; 352 (decode_init_default, L1, L2) -> L1 ++ L2; 353 (decode_repeated_add_elem, L1, L2) -> L1 ++ L2; 354 (decode_repeated_finalize, L1, L2) -> L1 ++ L2; 355 (merge, L1, L2) -> L1 ++ L2; 356 (verify, _L1, L2) -> [hd(L2)]; 357 (type_spec, _V1, V2) -> V2 358 end, 359 dict:from_list([{Op,ensure_list(Op,Call)} || {Op,Call} <- Transls1]), 360 dict:from_list([{Op,ensure_list(Op,Call)} || {Op,Call} <- Transls2]))). 361 362ensure_list(_Op, L) when is_list(L) -> L; 363ensure_list(type_spec, Elem) -> Elem; 364ensure_list(_Op, Elem) -> [Elem]. 365 366remove_merge_translations_for_repeated_elements(D) -> 367 dict:map(fun(Key, Ops) -> 368 case is_repeated_element_path(Key) of 369 true -> lists:keydelete(merge, 1, Ops); 370 false -> Ops 371 end 372 end, 373 D). 374 375is_repeated_element_path([_, _, []]) -> true; 376is_repeated_element_path(_) -> false. 377 378remove_empty_translations(D) -> 379 dict:filter(fun(_Key, Ops) -> Ops /= [] end, D). 380 381compute_map_translations(Defs, Opts) -> 382 MapInfos = 383 gpb_lib:fold_msg_fields( 384 fun(MsgName, #?gpb_field{name=FName, type={map,KType,VType}}, Acc) -> 385 [{{MsgName, FName}, {KType, VType}} | Acc]; 386 (_MsgName, _Field, Acc) -> 387 Acc 388 end, 389 [], 390 Defs), 391 MapFieldFmt = gpb_lib:get_2tuples_or_maps_for_maptype_fields_by_opts(Opts), 392 dict_from_translation_list( 393 lists:append( 394 [mk_map_transls(MsgName, FName, KeyType, ValueType, MapFieldFmt) 395 || {{MsgName, FName}, {KeyType, ValueType}} <- MapInfos])). 396 397mk_map_transls(MsgName, FName, KeyType, ValueType, '2tuples')-> 398 MapAsMsgName = gpb_lib:map_type_to_msg_name(KeyType, ValueType), 399 AddItemTrFn = case ValueType of 400 {msg,_} -> mt_add_item_r_verify_value; 401 _ -> mt_add_item_r 402 end, 403 [{[MsgName,FName,[]], 404 [{encode, {mt_maptuple_to_pseudomsg_r, ['$1', MapAsMsgName]}}]}, 405 {[MsgName,FName], 406 [{decode_init_default, {mt_empty_map_r, []}}, 407 {decode_repeated_add_elem, {AddItemTrFn, ['$1', '$2']}}, 408 {decode_repeated_finalize, {mt_finalize_items_r, ['$1']}}, 409 {merge, {mt_merge_maptuples_r, ['$1', '$2']}}]}]; 410mk_map_transls(MsgName, FName, _KeyType, ValueType, maps)-> 411 AddItemTrFn = case ValueType of 412 {msg,_} -> mt_add_item_m_verify_value; 413 _ -> mt_add_item_m 414 end, 415 [{[MsgName,FName,[]], 416 [{encode, {mt_maptuple_to_pseudomsg_m, ['$1']}}]}, 417 {[MsgName,FName], 418 [{encode, {mt_map_to_list_m, ['$1']}}, 419 {decode_init_default, {mt_empty_map_m, []}}, 420 {decode_repeated_add_elem, {AddItemTrFn, ['$1', '$2']}}, 421 {decode_repeated_finalize, {id, ['$1', '$user_data']}}, 422 {merge, {mt_merge_maps_m, ['$1', '$2']}}]}]. 423 424compute_type_translations(Defs, Opts) -> 425 TypeTranslations = 426 lists:foldl(fun({translate_type, {Type, Transls}}, Acc) -> 427 [{Type, Transls} | Acc]; 428 (_Opt, Acc) -> 429 Acc 430 end, 431 [], 432 Opts), 433 %% Traverse all message definitions only when there are translations 434 if TypeTranslations == [] -> 435 dict:new(); 436 true -> 437 compute_type_translations_2(Defs, TypeTranslations) 438 end. 439 440compute_type_translations_2(Defs, TypeTranslations) -> 441 Infos = compute_type_translation_infos(Defs, TypeTranslations), 442 Infos2 = add_type_translation_infos_for_msgs(TypeTranslations, Infos), 443 dict_from_translation_list( 444 [begin 445 Decode = case Type of 446 {map,_,_} -> decode_repeated_finalize; 447 _ -> decode 448 end, 449 Ctxt = {type, Type}, 450 Trs = [{encode, fetch_op_transl(encode, Translations, Ctxt)}, 451 {Decode, fetch_op_transl(Decode, Translations, Ctxt)}, 452 {verify, fetch_op_transl(verify, Translations, Ctxt)} 453 | [{merge, fetch_op_transl(merge, Translations, Ctxt)} 454 || not is_repeated_elem_path(Path), 455 not is_scalar_type(Type)]] 456 ++ type_spec_tr(Translations), 457 {Path, Trs} 458 end 459 || {Type, Path, Translations} <- Infos2]). 460 461is_scalar_type(int32) -> true; 462is_scalar_type(int64) -> true; 463is_scalar_type(uint32) -> true; 464is_scalar_type(uint64) -> true; 465is_scalar_type(sint32) -> true; 466is_scalar_type(sint64) -> true; 467is_scalar_type(fixed32) -> true; 468is_scalar_type(fixed64) -> true; 469is_scalar_type(sfixed32) -> true; 470is_scalar_type(sfixed64) -> true; 471is_scalar_type(bool) -> true; 472is_scalar_type(float) -> true; 473is_scalar_type(double) -> true; 474is_scalar_type({enum,_}) -> true; 475is_scalar_type(string) -> true; 476is_scalar_type(bytes) -> true; 477is_scalar_type(_) -> false. % not: msg | map | group 478 479compute_type_translation_infos(Defs, TypeTranslations) -> 480 gpb_lib:fold_msg_or_group_fields_o( 481 fun(_MsgOrGroup, 482 MsgName, #?gpb_field{name=FName, type=FType, occurrence=Occ}, 483 Oneof, 484 Acc) when not ?is_map_type(FType) -> 485 case lists:keyfind(FType, 1, TypeTranslations) of 486 {FType, Translations} -> 487 Path = 488 case {Oneof, Occ} of 489 {false, repeated} -> [MsgName,FName,[]]; 490 {false, _} -> [MsgName,FName]; 491 {{true,CFName}, _} -> [MsgName,CFName,FName] 492 end, 493 [{FType, Path, Translations} | Acc]; 494 false -> 495 Acc 496 end; 497 (_MsgOrGroup, 498 MsgName, #?gpb_field{name=FName, type={map,KeyType,ValueType}=FType}, 499 _Oneof, 500 Acc) -> 501 %% check for translation of the map<_,_> itself 502 Path = [MsgName,FName], 503 MapTransl = case lists:keyfind(FType, 1, TypeTranslations) of 504 {FType, Translations} -> 505 Ts2 = map_translations_to_internal( 506 Translations), 507 [{FType, Path, Ts2}]; 508 false -> 509 [] 510 end, 511 MsgName2 = gpb_lib:map_type_to_msg_name(KeyType, ValueType), 512 Fields2 = gpb:map_item_pseudo_fields(KeyType, ValueType), 513 Defs2 = [{{msg, MsgName2}, Fields2}], 514 MapTransl 515 ++ compute_type_translation_infos(Defs2, TypeTranslations) 516 ++ Acc; 517 (_Type, _MsgName, _Field, _Oneof, Acc) -> 518 Acc 519 end, 520 [], 521 Defs). 522 523map_translations_to_internal(Translations) -> 524 %% Under the hood, a map<_,_> is a repeated, and it is implemented 525 %% using translations, so adapt type translations accordingly 526 [case Op of 527 encode -> {encode, Tr}; 528 decode -> {decode_repeated_finalize, Tr}; 529 merge -> {merge, Tr}; 530 verify -> {verify, Tr}; 531 _ -> {Op, Tr} 532 end 533 || {Op, Tr} <- Translations]. 534 535add_type_translation_infos_for_msgs(TypeTranslations, Infos) -> 536 lists:foldl( 537 fun({{msg,MsgName}=Type, Translations}, Acc) -> 538 Path = [MsgName], 539 [{Type, Path, Translations} | Acc]; 540 (_OtherTransl, Acc) -> 541 Acc 542 end, 543 Infos, 544 TypeTranslations). 545 546compute_field_translations(Defs, Opts) -> 547 dict_from_translation_list( 548 lists:append( 549 [[{Path, augment_field_translations(Field, Path, Translations, Opts)} 550 || {translate_field, {[_,_|_]=Path, Translations}} <- Opts, 551 Field <- find_field_by_path(Defs, Path)], 552 [{Path, augment_msg_translations(MsgName, Translations)} 553 || {translate_field, {[MsgName]=Path, Translations}} <- Opts, 554 lists:keymember({msg,MsgName}, 1, Defs)]])). 555 556find_field_by_path(Defs, [MsgName,FieldName|RestPath]) -> 557 case find_2_msg(Defs, MsgName) of 558 {ok, {{_,MsgName}, Fields}} -> 559 case find_3_field(Fields, FieldName) of 560 {ok, #gpb_oneof{fields=OFields}} when RestPath =/= [] -> 561 case find_3_field(OFields, hd(RestPath)) of 562 {ok, OField} -> 563 [OField]; 564 error -> 565 [] 566 end; 567 {ok, Field} -> 568 [Field]; 569 error -> 570 [] 571 end; 572 error -> 573 [] 574 end. 575 576find_2_msg([{{msg, Name},_}=M | _], Name) -> {ok, M}; 577find_2_msg([{{group, Name},_}=M | _], Name) -> {ok, M}; 578find_2_msg([_ | Rest], Name) -> find_2_msg(Rest, Name); 579find_2_msg([], _Name) -> error. 580 581find_3_field([#?gpb_field{name=Name}=F | _], Name) -> {ok, F}; 582find_3_field([#gpb_oneof{name=Name}=F | _], Name) -> {ok, F}; 583find_3_field([_ | Rest], Name) -> find_3_field(Rest, Name); 584find_3_field([], _Name) -> error. 585 586augment_field_translations(#?gpb_field{type=Type, occurrence=Occ}, 587 Path, Translations, Opts) -> 588 IsRepeated = Occ == repeated, 589 IsElem = IsRepeated andalso lists:last(Path) == [], 590 IsScalar = is_scalar_type(Type), 591 DoNif = proplists:get_bool(nif, Opts), 592 %% Operations for which we can or sometimes must have translations: 593 Ops = if DoNif -> 594 [merge, verify]; 595 IsElem -> 596 [encode,decode,merge,verify]; 597 IsRepeated -> 598 [encode, 599 decode_init_default, 600 decode_repeated_add_elem, 601 decode_repeated_finalize, 602 merge, 603 verify]; 604 true -> 605 [encode,decode,merge,verify] 606 end -- [merge || IsScalar, not IsRepeated], 607 augment_2_fetch_transl(Ops, Translations, {field, Path}) 608 ++ type_spec_tr(Translations); 609augment_field_translations(#gpb_oneof{}, [_,_]=Path, Translations, Opts) -> 610 DoNif = proplists:get_bool(nif, Opts), 611 %% Operations for which we can or sometimes must have translations: 612 Ops = if DoNif -> 613 [verify]; 614 true -> 615 [encode,decode,verify] 616 end, 617 augment_2_fetch_transl(Ops, Translations, {field, Path}) 618 ++ type_spec_tr(Translations). 619 620augment_2_fetch_transl(Ops, Translations, Ctxt) -> 621 [{Op, fetch_op_transl(Op, Translations, Ctxt)} || Op <- Ops]. 622 623augment_msg_translations(MagName, Translations) -> 624 Ops = [encode,decode,merge,verify], 625 augment_2_fetch_transl(Ops, Translations, {msg,[MagName]}) 626 ++ type_spec_tr(Translations). 627 628type_spec_tr(Translations) -> 629 case lists:keyfind(type_spec, 1, Translations) of 630 {type_spec, TypeStr} -> 631 [{type_spec, TypeStr}]; 632 false -> 633 [{type_spec, '$default'}] 634 end. 635 636fetch_op_transl(Op, Translations, Ctxt) -> 637 Default = fetch_default_transl(Op), 638 fetch_op_translation(Op, Translations, Default, Ctxt). 639 640fetch_default_transl(Op) -> 641 case Op of 642 merge -> gpb_gen_translators:default_merge_translator(); 643 verify -> gpb_gen_translators:default_verify_translator(); 644 _ -> '$no_default' 645 end. 646 647fetch_op_translation(Op, Translations, Default, Ctxt) -> 648 case lists:keyfind(Op, 1, Translations) of 649 false when Default /= '$no_default' -> 650 Default; 651 false when Default == '$no_default' -> 652 error({error, {missing_translation, 653 {op,Op}, Ctxt, 654 Translations}}); 655 {Op, {M,F,ArgTempl}} -> 656 {M,F,ArgTempl}; 657 {Op, {F,ArgTempl}} -> 658 {F,ArgTempl} 659 end. 660 661is_repeated_elem_path([_MsgName,_FName,[]]) -> true; 662is_repeated_elem_path(_) -> false. 663 664find_group_occurrences(Defs) -> 665 gpb_lib:fold_msg_or_group_fields_o( 666 fun(_msg_or_group, _MsgName, 667 #?gpb_field{type={group,GroupName}, occurrence=Occurrence}, 668 _IsOnoeof, D)-> 669 dict:store(GroupName, Occurrence, D); 670 (_msg_or_group, _MsgName, _Field, _IsOnoeof, D) -> 671 D 672 end, 673 dict:new(), 674 Defs). 675 676has_p3_opt_strings(Defs) -> 677 P3Msgs = case lists:keyfind(proto3_msgs, 1, Defs) of 678 {proto3_msgs, Names} -> Names; 679 false -> [] 680 end, 681 try gpb_lib:fold_msg_or_group_fields_o( 682 fun(_msg_or_group, MsgName, #?gpb_field{type=Type,occurrence=Occ}, 683 _IsOneOf, Acc) -> 684 if Type == string, Occ == optional -> 685 case lists:member(MsgName, P3Msgs) of 686 true -> throw(true); 687 false -> Acc 688 end; 689 true -> 690 Acc 691 end 692 end, 693 false, 694 Defs) 695 catch throw:true -> 696 true 697 end. 698