From 205521d3927ed6f53c9a6fa3095f8a879bdca929 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 11 Jun 2017 13:59:02 +0200 Subject: Move (most of) diameter_gen.hrl to diameter_gen.erl To remove the requirement that dictionary modules be recompiled whenever the encode/decode implementation changes. The included diameter_gen.hrl now only contains trivial functions that call info diameter_gen.erl. --- lib/diameter/include/diameter_gen.hrl | 659 +--------------------------------- 1 file changed, 18 insertions(+), 641 deletions(-) (limited to 'lib/diameter/include') diff --git a/lib/diameter/include/diameter_gen.hrl b/lib/diameter/include/diameter_gen.hrl index 83f87ea4c4..fb6370fe54 100644 --- a/lib/diameter/include/diameter_gen.hrl +++ b/lib/diameter/include/diameter_gen.hrl @@ -20,659 +20,36 @@ %% %% This file contains code that's included by encode/decode modules -%% generated by diameter_codegen.erl. This code does most of the work, the -%% generated code being kept simple. +%% generated by diameter_codegen.erl. This code used to do most of the +%% work, but now passes it off to module diameter_gen. %% --define(THROW(T), throw({?MODULE, T})). - -%% Tag common to generated dictionaries. --define(TAG, diameter_gen). - --type parent_name() :: atom(). %% parent = Message or AVP --type parent_record() :: tuple(). %% --type avp_name() :: atom(). --type avp_record() :: tuple(). --type avp_values() :: [{avp_name(), term()}]. - --type non_grouped_avp() :: #diameter_avp{}. --type grouped_avp() :: nonempty_improper_list(#diameter_avp{}, [avp()]). --type avp() :: non_grouped_avp() | grouped_avp(). - -%% --------------------------------------------------------------------------- -%% # encode_avps/3 -%% --------------------------------------------------------------------------- - --spec encode_avps(parent_name(), parent_record() | avp_values(), map()) - -> iolist() - | no_return(). +%% encode_avps/3 encode_avps(Name, Vals, Opts) -> - try - encode(Name, Vals, Opts) - catch - throw: {?MODULE, Reason} -> - diameter_lib:log({encode, error}, - ?MODULE, - ?LINE, - {Reason, Name, Vals}), - erlang:error(list_to_tuple(Reason ++ [Name])); - error: Reason -> - Stack = erlang:get_stacktrace(), - diameter_lib:log({encode, failure}, - ?MODULE, - ?LINE, - {Reason, Name, Vals, Stack}), - erlang:error({encode_failure, Reason, Name, Stack}) - end. - -%% encode/3 - -encode(Name, Vals, #{ordered_encode := false} = Opts) - when is_list(Vals) -> - lists:map(fun({F,V}) -> encode(Name, F, V, Opts) end, Vals); - -encode(Name, Vals, Opts) - when is_list(Vals) -> - encode(Name, '#set-'(Vals, newrec(Name)), Opts); - -encode(Name, Rec, Opts) -> - [encode(Name, F, V, Opts) || {F,V} <- '#get-'(Rec)]. - -%% encode/4 - -encode(Name, AvpName, Values, Opts) -> - enc(Name, AvpName, avp_arity(Name, AvpName), Values, Opts). - -%% enc/5 - -enc(_, AvpName, 1, undefined, _) -> - ?THROW([mandatory_avp_missing, AvpName]); - -enc(Name, AvpName, 1, Value, Opts) -> - enc(Name, AvpName, [Value], Opts); - -enc(_, _, {0,_}, [], _) -> - []; - -enc(_, AvpName, _, T, _) - when not is_list(T) -> - ?THROW([repeated_avp_as_non_list, AvpName, T]); - -enc(_, AvpName, {Min, _}, L, _) - when length(L) < Min -> - ?THROW([repeated_avp_insufficient_arity, AvpName, Min, L]); - -enc(_, AvpName, {_, Max}, L, _) - when Max < length(L) -> - ?THROW([repeated_avp_excessive_arity, AvpName, Max, L]); + diameter_gen:encode_avps(Name, Vals, Opts#{module => ?MODULE}). -enc(Name, AvpName, _, Values, Opts) -> - enc(Name, AvpName, Values, Opts). - -%% enc/4 - -enc(Name, 'AVP', Values, Opts) -> - [enc_AVP(Name, A, Opts) || A <- Values]; - -enc(_, AvpName, Values, Opts) -> - enc(AvpName, Values, Opts). - -%% env/3 - -enc(AvpName, Values, Opts) -> - H = avp_header(AvpName), - [diameter_codec:pack_data(H, avp(encode, V, AvpName, Opts)) - || V <- Values]. - -%% enc_AVP/3 - -%% No value: assume AVP data is already encoded. The normal case will -%% be when this is passed back from #diameter_packet.errors as a -%% consequence of a failed decode. Any AVP can be encoded this way -%% however, which side-steps any arity checks for known AVP's and -%% could potentially encode something unfortunate. -enc_AVP(_, #diameter_avp{value = undefined} = A, Opts) -> - diameter_codec:pack_avp(A, Opts); - -%% Missing name for value encode. -enc_AVP(_, #diameter_avp{name = N, value = V}, _) - when N == undefined; - N == 'AVP' -> - ?THROW([value_with_nameless_avp, N, V]); - -%% Or not. Ensure that 'AVP' is the appropriate field. Note that if we -%% don't know this AVP at all then the encode will fail. -enc_AVP(Name, #diameter_avp{name = AvpName, value = Data}, Opts) -> - 0 == avp_arity(Name, AvpName) - orelse ?THROW([known_avp_as_AVP, Name, AvpName, Data]), - enc(AvpName, [Data], Opts); - -%% The backdoor ... -enc_AVP(_, {AvpName, Value}, Opts) -> - enc(AvpName, [Value], Opts); - -%% ... and the side door. -enc_AVP(_Name, {_Dict, _AvpName, _Data} = T, Opts) -> - diameter_codec:pack_avp(#diameter_avp{data = T}, Opts). - -%% --------------------------------------------------------------------------- -%% # decode_avps/2 -%% --------------------------------------------------------------------------- - --spec decode_avps(parent_name(), [#diameter_avp{}], map()) - -> {parent_record(), [avp()], Failed} - when Failed :: [{5000..5999, #diameter_avp{}}]. +%% decode_avps/2 decode_avps(Name, Recs, Opts) -> - {Avps, {Rec, Failed}} - = mapfoldl(fun(T,A) -> decode(Name, Opts, T, A) end, - {newrec(Name), []}, - Recs), - {Rec, Avps, Failed ++ missing(Rec, Name, Failed)}. -%% Append 5005 errors so that errors are reported in the order -%% encountered. Failed-AVP should typically contain the first -%% encountered error accordg to the RFC. - -newrec(Name) -> - '#new-'(name2rec(Name)). - -%% mapfoldl/3 -%% -%% Like lists:mapfoldl/3, but don't reverse the list. - -mapfoldl(F, Acc, List) -> - mapfoldl(F, Acc, List, []). - -mapfoldl(F, Acc0, [T|Rest], List) -> - {B, Acc} = F(T, Acc0), - mapfoldl(F, Acc, Rest, [B|List]); -mapfoldl(_, Acc, [], List) -> - {List, Acc}. - -%% 3588: -%% -%% DIAMETER_MISSING_AVP 5005 -%% The request did not contain an AVP that is required by the Command -%% Code definition. If this value is sent in the Result-Code AVP, a -%% Failed-AVP AVP SHOULD be included in the message. The Failed-AVP -%% AVP MUST contain an example of the missing AVP complete with the -%% Vendor-Id if applicable. The value field of the missing AVP -%% should be of correct minimum length and contain zeros. - -missing(Rec, Name, Failed) -> - Avps = lists:foldl(fun({_, #diameter_avp{code = C, vendor_id = V}}, A) -> - maps:put({C,V}, true, A) - end, - maps:new(), - Failed), - missing(avp_arity(Name), tl(tuple_to_list(Rec)), Avps, []). - -missing([{Name, Arity} | As], [Value | Vs], Avps, Acc) -> - missing(As, Vs, Avps, case - [H || missing_arity(Arity, Value), - {C,_,V} = H <- [avp_header(Name)], - not maps:is_key({C,V}, Avps)] - of - [H] -> - [{5005, empty_avp(Name, H)} | Acc]; - [] -> - Acc - end); - -missing([], [], _, Acc) -> - Acc. - -%% Maximum arities have already been checked in building the record. - -missing_arity(1, V) -> - V == undefined; -missing_arity({0, _}, _) -> - false; -missing_arity({1, _}, L) -> - [] == L; -missing_arity({Min, _}, L) -> - not has_prefix(Min, L). - -%% Compare a non-negative integer and the length of a list without -%% computing the length. -has_prefix(0, _) -> - true; -has_prefix(_, []) -> - false; -has_prefix(N, [_|L]) -> - has_prefix(N-1, L). - -%% empty_avp/2 - -empty_avp(Name, {Code, Flags, VId}) -> - {Name, Type} = avp_name(Code, VId), - #diameter_avp{name = Name, - code = Code, - vendor_id = VId, - is_mandatory = 0 /= (Flags band 2#01000000), - need_encryption = 0 /= (Flags band 2#00100000), - data = empty_value(Name), - type = Type}. - -%% 3588, ch 7: -%% -%% The Result-Code AVP describes the error that the Diameter node -%% encountered in its processing. In case there are multiple errors, -%% the Diameter node MUST report only the first error it encountered -%% (detected possibly in some implementation dependent order). The -%% specific errors that can be described by this AVP are described in -%% the following section. - -%% decode/4 - -decode(Name, Opts, #diameter_avp{code = Code, vendor_id = Vid} = Avp, Acc) -> - decode(Name, Opts, avp_name(Code, Vid), Avp, Acc). - -%% decode/5 - -%% AVP not in dictionary. -decode(Name, Opts, 'AVP', Avp, Acc) -> - decode_AVP(Name, Avp, Opts, Acc); - -%% 6733, 4.4: -%% -%% Receivers of a Grouped AVP that does not have the 'M' (mandatory) -%% bit set and one or more of the encapsulated AVPs within the group -%% has the 'M' (mandatory) bit set MAY simply be ignored if the -%% Grouped AVP itself is unrecognized. The rule applies even if the -%% encapsulated AVP with its 'M' (mandatory) bit set is further -%% encapsulated within other sub-groups, i.e., other Grouped AVPs -%% embedded within the Grouped AVP. -%% -%% The first sentence is slightly mangled, but take it to mean this: -%% -%% An unrecognized AVP of type Grouped that does not set the 'M' bit -%% MAY be ignored even if one of its encapsulated AVPs sets the 'M' -%% bit. -%% -%% The text above is a change from RFC 3588, which instead says this: -%% -%% Further, if any of the AVPs encapsulated within a Grouped AVP has -%% the 'M' (mandatory) bit set, the Grouped AVP itself MUST also -%% include the 'M' bit set. -%% -%% Both of these texts have problems. If the AVP is unknown then its -%% type is unknown since the type isn't sent over the wire, so the -%% 6733 text becomes a non-statement: don't know that the AVP not -%% setting the M-bit is of type Grouped, therefore can't know that its -%% data consists of encapsulated AVPs, therefore can't but ignore that -%% one of these might set the M-bit. It should be no worse if we know -%% the AVP to have type Grouped. -%% -%% Similarly, for the 3588 text: if we receive an AVP that doesn't set -%% the M-bit and don't know that the AVP has type Grouped then we -%% can't realize that its data contains an AVP that sets the M-bit, so -%% can't regard the AVP as erroneous on this account. Again, it should -%% be no worse if the type is known to be Grouped, but in this case -%% the RFC forces us to regard the AVP as erroneous. This is -%% inconsistent, and the 3588 text has never been enforced. -%% -%% So, if an AVP doesn't set the M-bit then we're free to ignore it, -%% regardless of the AVP's type. If we know the type to be Grouped -%% then we must ignore the M-bit on an encapsulated AVP. That means -%% packing such an encapsulated AVP into an 'AVP' field if need be, -%% not regarding the lack of a specific field as an error as is -%% otherwise the case. (The lack of an AVP-specific field being how we -%% defined the RFC's "unrecognized", which is slightly stronger than -%% "not defined".) - -decode(Name, Opts0, {AvpName, Type}, Avp, Acc) -> - #diameter_avp{data = Data, is_mandatory = M} - = Avp, - - %% Whether or not to ignore an M-bit on an encapsulated AVP, or on - %% all AVPs with the service_opt() strict_mbit. - Opts1 = set_strict(Type, M, Opts0), - - %% Whether or not we're decoding within Failed-AVP and should - %% ignore decode errors. - #{dictionary := AppMod, failed_avp := Failed} - = Opts - = set_failed(Name, Opts1), %% Not AvpName or else a failed Failed-AVP - %% decode is packed into 'AVP'. - - %% Reset the dictionary for best-effort decode of Failed-AVP. - Mod = if Failed -> - AppMod; - true -> - ?MODULE - end, - - %% On decode, a Grouped AVP is represented as a #diameter_avp{} - %% list with AVP as head and component AVPs as tail. On encode, - %% data can be a list of component AVPs. - - try Mod:avp(decode, Data, AvpName, Opts) of - {Rec, As} when Type == 'Grouped' -> - A = Avp#diameter_avp{name = AvpName, - value = Rec, - type = Type}, - {[A|As], pack_avp(Name, A, Opts, Acc)}; - - V when Type /= 'Grouped' -> - A = Avp#diameter_avp{name = AvpName, - value = V, - type = Type}, - {A, pack_avp(Name, A, Opts, Acc)} - catch - throw: {?TAG, {grouped, Error, ComponentAvps}} -> - decode_error(Name, - Error, - ComponentAvps, - Opts, - Avp#diameter_avp{name = AvpName, - data = trim(Avp#diameter_avp.data), - type = Type}, - Acc); - - error: Reason -> - decode_error(Name, - Reason, - Opts, - Avp#diameter_avp{name = AvpName, - data = trim(Avp#diameter_avp.data), - type = Type}, - Acc) - end. - -%% trim/1 -%% -%% Remove any extra bit that was added in diameter_codec to induce a -%% 5014 error. - -trim(#diameter_avp{data = Data} = Avp) -> - Avp#diameter_avp{data = trim(Data)}; - -trim({5014, Bin}) -> - Bin; - -trim(Avps) - when is_list(Avps) -> - lists:map(fun trim/1, Avps); - -trim(Avp) -> - Avp. - -%% decode_error/6 - -decode_error(Name, [_ | Rec], _, #{failed_avp := true} = Opts, Avp, Acc) -> - decode_AVP(Name, Avp#diameter_avp{value = Rec}, Opts, Acc); - -decode_error(Name, _, _, #{failed_avp := true} = Opts, Avp, Acc) -> - decode_AVP(Name, Avp, Opts, Acc); - -decode_error(_, [Error | _], ComponentAvps, _, Avp, Acc) -> - decode_error(Error, Avp, Acc, ComponentAvps); - -decode_error(_, Error, ComponentAvps, _, Avp, Acc) -> - decode_error(Error, Avp, Acc, ComponentAvps). - -%% decode_error/5 - -decode_error(Name, _Reason, #{failed_avp := true} = Opts, Avp, Acc) -> - decode_AVP(Name, Avp, Opts, Acc); - -decode_error(Name, Reason, _, Avp, {Rec, Failed}) -> - Stack = diameter_lib:get_stacktrace(), - diameter_lib:log(decode_error, - ?MODULE, - ?LINE, - {Name, Avp#diameter_avp.name, Stack}), - {Avp, {Rec, [rc(Reason, Avp) | Failed]}}. - -%% decode_error/4 - -decode_error({RC, ErrorData}, Avp, {Rec, Failed}, ComponentAvps) -> - E = Avp#diameter_avp{data = [ErrorData]}, - {[Avp | trim(ComponentAvps)], {Rec, [{RC, E} | Failed]}}. - -%% set_strict/3 - -%% Set false as soon as we see a Grouped AVP that doesn't set the -%% M-bit, to ignore the M-bit on an encapsulated AVP. -set_strict('Grouped', false = M, #{strict_mbit := true} = Opts) -> - Opts#{strict_mbit := M}; -set_strict(_, _, Opts) -> - Opts. - -%% set_failed/2 -%% -%% Set true as soon as we see Failed-AVP. Matching on 'Failed-AVP' -%% assumes that this is the RFC AVP. Strictly, this doesn't need to be -%% the case. - -set_failed('Failed-AVP', #{failed_avp := false} = Opts) -> - Opts#{failed_avp := true}; -set_failed(_, Opts) -> - Opts. - -%% decode_AVP/4 -%% -%% Don't know this AVP: see if it can be packed in an 'AVP' field -%% undecoded. Note that the type field is 'undefined' in this case. - -decode_AVP(Name, Avp, Opts, Acc) -> - {trim(Avp), pack_AVP(Name, Avp, Opts, Acc)}. - -%% rc/1 - -%% diameter_types will raise an error of this form to communicate -%% DIAMETER_INVALID_AVP_LENGTH (5014). A module specified to a -%% @custom_types tag in a dictionary file can also raise an error of -%% this form. -rc({'DIAMETER', 5014 = RC, _}, #diameter_avp{name = AvpName} = Avp) -> - {RC, Avp#diameter_avp{data = empty_value(AvpName)}}; - -%% 3588: -%% -%% DIAMETER_INVALID_AVP_VALUE 5004 -%% The request contained an AVP with an invalid value in its data -%% portion. A Diameter message indicating this error MUST include -%% the offending AVPs within a Failed-AVP AVP. -rc(_, Avp) -> - {5004, Avp}. - -%% pack_avp/4 - -pack_avp(Name, #diameter_avp{name = AvpName} = Avp, Opts, Acc) -> - pack_avp(Name, avp_arity(Name, AvpName), Avp, Opts, Acc). - -%% pack_avp/5 - -pack_avp(Name, 0, Avp, Opts, Acc) -> - pack_AVP(Name, Avp, Opts, Acc); - -pack_avp(_, Arity, #diameter_avp{name = AvpName} = Avp, _, Acc) -> - pack(Arity, AvpName, Avp, Acc). - -%% pack_AVP/4 - -%% Length failure was induced because of a header/payload length -%% mismatch. The AVP Length is reset to match the received data if -%% this AVP is encoded in an answer message, since the length is -%% computed. -%% -%% Data is a truncated header if command_code = undefined, otherwise -%% payload bytes. The former is padded to the length of a header if -%% the AVP reaches an outgoing encode in diameter_codec. -%% -%% RFC 6733 says that an AVP returned with 5014 can contain a minimal -%% payload for the AVP's type, but in this case we don't know the -%% type. - -pack_AVP(_, #diameter_avp{data = {5014 = RC, Data}} = Avp, _, Acc) -> - {Rec, Failed} = Acc, - {Rec, [{RC, Avp#diameter_avp{data = Data}} | Failed]}; - -pack_AVP(Name, Avp, Opts, Acc) -> - pack_AVP(pack_arity(Name, Opts, Avp), Avp, Acc). - -%% pack_AVP/3 - -pack_AVP(0, #diameter_avp{is_mandatory = M} = Avp, Acc) -> - {Rec, Failed} = Acc, - {Rec, [{if M -> 5001; true -> 5008 end, Avp} | Failed]}; - -pack_AVP(Arity, Avp, Acc) -> - pack(Arity, 'AVP', Avp, Acc). - -%% Give Failed-AVP special treatment since (1) it'll contain any -%% unrecognized mandatory AVP's and (2) the RFC 3588 grammar failed to -%% allow for Failed-AVP in an answer-message. - -pack_arity(Name, - #{strict_mbit := Strict, - failed_avp := Failed}, - #diameter_avp{is_mandatory = M, - name = AvpName}) -> - - %% Not testing just Name /= 'Failed-AVP' means we're changing the - %% packing of AVPs nested within Failed-AVP, but the point of - %% ignoring errors within Failed-AVP is to decode as much as - %% possible, and failing because a mandatory AVP couldn't be - %% packed into a dedicated field defeats that point. - - if Failed == true; - Name == 'Failed-AVP'; - Name == 'answer-message', AvpName == 'Failed-AVP'; - not M; - not Strict -> - avp_arity(Name, 'AVP'); - true -> - 0 - end. - -%% 3588: -%% -%% DIAMETER_AVP_UNSUPPORTED 5001 -%% The peer received a message that contained an AVP that is not -%% recognized or supported and was marked with the Mandatory bit. A -%% Diameter message with this error MUST contain one or more Failed- -%% AVP AVP containing the AVPs that caused the failure. -%% -%% DIAMETER_AVP_NOT_ALLOWED 5008 -%% A message was received with an AVP that MUST NOT be present. The -%% Failed-AVP AVP MUST be included and contain a copy of the -%% offending AVP. - -%% pack/4 - -pack(Arity, FieldName, Avp, {Rec, _} = Acc) -> - pack('#get-'(FieldName, Rec), Arity, FieldName, Avp, Acc). - -%% pack/5 - -pack(undefined, 1, 'AVP' = F, Avp, {Rec, Failed}) -> %% unlikely - {'#set-'({F, Avp}, Rec), Failed}; - -pack(undefined, 1, F, #diameter_avp{value = V}, {Rec, Failed}) -> - {'#set-'({F, V}, Rec), Failed}; - -%% 3588: -%% -%% DIAMETER_AVP_OCCURS_TOO_MANY_TIMES 5009 -%% A message was received that included an AVP that appeared more -%% often than permitted in the message definition. The Failed-AVP -%% AVP MUST be included and contain a copy of the first instance of -%% the offending AVP that exceeded the maximum number of occurrences -%% - -pack(_, 1, _, Avp, {Rec, Failed}) -> - {Rec, [{5009, Avp} | Failed]}; - -pack(L, {_, Max}, F, Avp, {Rec, Failed}) -> - case '*' /= Max andalso has_prefix(Max+1, L) of - true -> - {Rec, [{5009, Avp} | Failed]}; - false when F == 'AVP' -> - {'#set-'({F, [Avp | L]}, Rec), Failed}; - false -> - {'#set-'({F, [Avp#diameter_avp.value | L]}, Rec), Failed} - end. - -%% --------------------------------------------------------------------------- -%% # grouped_avp/3 -%% --------------------------------------------------------------------------- - --spec grouped_avp(decode, avp_name(), binary() | {5014, binary()}, term()) - -> {avp_record(), [avp()]}; - (encode, avp_name(), avp_record() | avp_values(), term()) - -> iolist() - | no_return(). - -%% Length error induced by diameter_codec:collect_avps/1: the AVP -%% length in the header was too short (insufficient for the extracted -%% header) or too long (past the end of the message). An empty payload -%% is sufficient according to the RFC text for 5014. -grouped_avp(decode, _Name, {5014 = RC, _Bin}, _) -> - throw({?TAG, {grouped, {RC, []}, []}}); - -grouped_avp(decode, Name, Data, Opts) -> - grouped_decode(Name, diameter_codec:collect_avps(Data), Opts); - -grouped_avp(encode, Name, Data, Opts) -> - encode_avps(Name, Data, Opts). - -%% grouped_decode/2 -%% -%% Note that Grouped is the only AVP type that doesn't just return a -%% decoded value, also returning the list of component diameter_avp -%% records. - -%% Length error in trailing component AVP. -grouped_decode(_Name, {Error, Acc}, _) -> - {5014, Avp} = Error, - throw({?TAG, {grouped, Error, [Avp | Acc]}}); - -%% 7.5. Failed-AVP AVP + diameter_gen:decode_avps(Name, Recs, Opts#{module => ?MODULE}). -%% In the case where the offending AVP is embedded within a Grouped AVP, -%% the Failed-AVP MAY contain the grouped AVP, which in turn contains -%% the single offending AVP. The same method MAY be employed if the -%% grouped AVP itself is embedded in yet another grouped AVP and so on. -%% In this case, the Failed-AVP MAY contain the grouped AVP hierarchy up -%% to the single offending AVP. This enables the recipient to detect -%% the location of the offending AVP when embedded in a group. +%% avp/5 -%% An error in decoding a component AVP throws the first faulty -%% component, which the catch in d/3 wraps in the Grouped AVP in -%% question. A partially decoded record is only used when ignoring -%% errors in Failed-AVP. -grouped_decode(Name, ComponentAvps, Opts) -> - {Rec, Avps, Es} = decode_avps(Name, ComponentAvps, Opts), - [] == Es orelse throw({?TAG, {grouped, [{_,_} = hd(Es) | Rec], Avps}}), - {Rec, Avps}. +avp(T, Data, Name, Opts, Mod) -> + Mod:avp(T, Data, Name, Opts#{module := Mod}). -%% --------------------------------------------------------------------------- -%% # empty_group/1 -%% --------------------------------------------------------------------------- +%% grouped_avp/4 -empty_group(Name) -> - list_to_binary([z(F,A) || {F,A} <- avp_arity(Name)]). +grouped_avp(T, Name, Data, Opts) -> + diameter_gen:grouped_avp(T, Name, Data, Opts). -z(Name, 1) -> - z(Name); -z(_, {0,_}) -> - []; -z(Name, {Min, _}) -> - binary:copy(z(Name), Min). +%% empty_group/2 -z('AVP') -> - <<0:64>>; %% minimal header -z(Name) -> - Bin = diameter_codec:pack_data(avp_header(Name), empty_value(Name)), - Sz = iolist_size(Bin), - <<0:Sz/unit:8>>. +empty_group(Name, Opts) -> + diameter_gen:empty_group(Name, Opts). -%% --------------------------------------------------------------------------- -%% # empty/1 -%% --------------------------------------------------------------------------- +%% empty/2 -empty(AvpName) -> - avp(encode, zero, AvpName, _Opts = #{}). +empty(Name, Opts) -> + diameter_gen:empty(Name, Opts). -- cgit v1.2.3