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