aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjörn Gustavsson <[email protected]>2017-02-09 11:45:09 +0100
committerGitHub <[email protected]>2017-02-09 11:45:09 +0100
commit9fd347f4fb1443ff5a342177bc8780e174cd67da (patch)
treea2546ff745886c25e565b9b9bef6047256060ce3
parentaff4e82fce59e4eaefa84ed0d1dd5c40927637e5 (diff)
parent8a7f914affce3102e4889c2973ea2d2e99ad633d (diff)
downloadotp-9fd347f4fb1443ff5a342177bc8780e174cd67da.tar.gz
otp-9fd347f4fb1443ff5a342177bc8780e174cd67da.tar.bz2
otp-9fd347f4fb1443ff5a342177bc8780e174cd67da.zip
Merge pull request #1327 from bjorng/bjorn/asn1/maps-instead-of-records/OTP-14004
Teach the ASN.1 compiler the 'maps' option OTP-14219
-rw-r--r--lib/asn1/doc/src/asn1_getting_started.xml77
-rw-r--r--lib/asn1/doc/src/asn1ct.xml17
-rw-r--r--lib/asn1/src/asn1_db.erl26
-rw-r--r--lib/asn1/src/asn1_records.hrl12
-rw-r--r--lib/asn1/src/asn1ct.erl218
-rw-r--r--lib/asn1/src/asn1ct_check.erl29
-rw-r--r--lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl300
-rw-r--r--lib/asn1/src/asn1ct_constructed_per.erl900
-rw-r--r--lib/asn1/src/asn1ct_eval_ext.funcs1
-rw-r--r--lib/asn1/src/asn1ct_gen.erl373
-rw-r--r--lib/asn1/src/asn1ct_gen_ber_bin_v2.erl12
-rw-r--r--lib/asn1/src/asn1ct_gen_check.erl191
-rw-r--r--lib/asn1/src/asn1ct_gen_per.erl12
-rw-r--r--lib/asn1/src/asn1ct_imm.erl64
-rw-r--r--lib/asn1/src/asn1ct_value.erl24
-rw-r--r--lib/asn1/src/asn1rtt_ext.erl62
-rw-r--r--lib/asn1/test/Makefile1
-rw-r--r--lib/asn1/test/asn1_SUITE.erl76
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Maps.asn117
-rw-r--r--lib/asn1/test/asn1_SUITE_data/SeqExtension.asn111
-rw-r--r--lib/asn1/test/asn1_SUITE_data/nbapsystem/NBAP-PDU-Descriptions.asn (renamed from lib/asn1/test/asn1_SUITE_data/nbapsystem/NBAP-PDU-Discriptions.asn)2
-rw-r--r--lib/asn1/test/asn1_SUITE_data/test_records.erl2
-rw-r--r--lib/asn1/test/asn1_test_lib.erl105
-rw-r--r--lib/asn1/test/h323test.erl29
-rw-r--r--lib/asn1/test/testContextSwitchingTypes.erl1
-rw-r--r--lib/asn1/test/testInfObj.erl1
-rw-r--r--lib/asn1/test/testMaps.erl50
-rw-r--r--lib/asn1/test/testMultipleLevels.erl6
-rw-r--r--lib/asn1/test/testNBAPsystem.erl14
-rw-r--r--lib/asn1/test/testRfcs.erl50
-rw-r--r--lib/asn1/test/testSeqExtension.erl38
-rw-r--r--lib/asn1/test/testTCAP.erl1
-rw-r--r--lib/asn1/test/testTimer.erl131
-rw-r--r--lib/asn1/test/testUniqueObjectSets.erl1
-rw-r--r--lib/asn1/test/test_compile_options.erl28
35 files changed, 1897 insertions, 985 deletions
diff --git a/lib/asn1/doc/src/asn1_getting_started.xml b/lib/asn1/doc/src/asn1_getting_started.xml
index d40b294c39..d2b73d63c3 100644
--- a/lib/asn1/doc/src/asn1_getting_started.xml
+++ b/lib/asn1/doc/src/asn1_getting_started.xml
@@ -187,6 +187,14 @@ erlc -o ../asnfiles -I ../asnfiles -I /usr/local/standards/asn1 Person.asn</pre>
<item>
<p>DER encoding rule. Only when using option <c>-ber</c>.</p>
</item>
+ <tag><c>+maps</c></tag>
+ <item>
+ <p>Use maps instead of records to represent the <c>SEQUENCE</c> and
+ <c>SET</c> types. No <c>.hrl</c> files will be generated.
+ See the Section <seealso marker="asn1_getting_started#MAP_SEQ_SET">
+ Map representation for SEQUENCE and SET</seealso>
+ for more information.</p>
+ </item>
<tag><c>+asn1config</c></tag>
<item>
<p>This functionality works together with option
@@ -766,8 +774,11 @@ Pdu ::= SEQUENCE {
b REAL,
c OBJECT IDENTIFIER,
d NULL } </pre>
- <p>This is a 4-component structure called <c>Pdu</c>. The record format
- is the major format for representation of <c>SEQUENCE</c> in Erlang.
+ <p>This is a 4-component structure called <c>Pdu</c>. By default,
+ a <c>SEQUENCE</c> is represented by a record in Erlang.
+ It can also be represented as a map; see
+ <seealso marker="asn1_getting_started#MAP_SEQ_SET">
+ Map representation for SEQUENCE and SET</seealso>.
For each <c>SEQUENCE</c> and <c>SET</c> in an ASN.1 module an Erlang
record declaration is generated. For <c>Pdu</c>, a record
like the following is defined:</p>
@@ -878,6 +889,48 @@ SExt ::= SEQUENCE {
</section>
<section>
+ <marker id="MAP_SEQ_SET"></marker>
+ <title>Map representation for SEQUENCE and SET</title>
+ <p>If the ASN.1 module has been compiled with option <c>maps</c>,
+ the types <c>SEQUENCE</c> and <c>SET</c> are represented as maps.</p>
+ <p>In the following example, this ASN.1 specification is used:</p>
+ <pre>
+File DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+Seq1 ::= SEQUENCE {
+ a INTEGER DEFAULT 42,
+ b BOOLEAN OPTIONAL,
+ c IA5String
+}
+END </pre>
+
+ <p>Optional fields are to be omitted from the map if they have
+ no value:</p>
+
+ <pre>
+1> <input>asn1ct:compile('File', [per,maps]).</input>
+ok
+2> <input>{ok,E} = 'File':encode('Seq1', #{a=>0,c=>"string"}).</input>
+{ok,&lt;&lt;128,1,0,6,115,116,114,105,110,103&gt;&gt;} </pre>
+
+ <p>When decoding, optional fields will be omitted from the map:</p>
+
+ <pre>
+3> <input>'File':decode('Seq1', E).</input>
+{ok,#{a => 0,c => "string"}} </pre>
+
+ <p>Default values can be omitted from the map:</p>
+ <pre>
+4> <input>{ok,E2} = 'File':encode('Seq1', #{c=>"string"}).</input>
+{ok,&lt;&lt;0,6,115,116,114,105,110,103&gt;&gt;}
+5> <input>'File':decode('Seq1', E2).</input>
+{ok,#{a => 42,c => "string"}} </pre>
+
+ <note><p>It is not allowed to use the atoms <c>asn1_VALUE</c> and
+ <c>asn1_DEFAULT</c> with maps.</p></note>
+ </section>
+
+ <section>
<marker id="CHOICE"></marker>
<title>CHOICE</title>
<p>The type <c>CHOICE</c> is a space saver and is similar to the
@@ -1004,11 +1057,16 @@ T ::= CHOICE {
<section>
<title>Naming of Records in .hrl Files</title>
+ <p>When the option <c>maps</c> is given, no <c>.hrl</c> files
+ will be generated. The rest of this section describes the behavior
+ of the compiler when <c>maps</c> is not used.</p>
+
<p>When an ASN.1 specification is compiled, all defined types of type
- <c>SET</c> or <c>SEQUENCE</c> result in a corresponding record in the
- generated <c>.hrl</c> file. This is because the values for
- <c>SET</c> and <c>SEQUENCE</c> are represented as records as
- mentioned earlier.</p>
+ <c>SET</c> or <c>SEQUENCE</c> result in a corresponding record in the
+ generated <c>.hrl</c> file. This is because the values for
+ <c>SET</c> and <c>SEQUENCE</c> are represented as records
+ by default.</p>
+
<p>Some special cases of this functionality are presented in the
next section.</p>
@@ -1144,9 +1202,10 @@ SS ::= SET {
<p>This example shows that a function is generated by the compiler
that returns a valid Erlang representation of the value, although
the value is of a complex type.</p>
- <p>Furthermore, a macro is generated for each value in the <c>.hrl</c>
- file. So, the defined value <c>tt</c> can also be extracted by
- <c>?tt</c> in application code.</p>
+ <p>Furthermore, if the option <c>maps</c> is not used,
+ a macro is generated for each value in the <c>.hrl</c>
+ file. So, the defined value <c>tt</c> can also be extracted by
+ <c>?tt</c> in application code.</p>
</section>
<section>
diff --git a/lib/asn1/doc/src/asn1ct.xml b/lib/asn1/doc/src/asn1ct.xml
index ebe1ce44dc..859d6a50bb 100644
--- a/lib/asn1/doc/src/asn1ct.xml
+++ b/lib/asn1/doc/src/asn1ct.xml
@@ -170,11 +170,24 @@ File3.asn</pre>
as for <c>ber</c>.
</p>
</item>
+ <tag><c>maps</c></tag>
+ <item>
+ <p>This option changes the representation of the types
+ <c>SEQUENCE</c> and <c>SET</c> to use maps (instead of
+ records). This option also suppresses the generation of
+ <c>.hrl</c> files.</p>
+ <p>For details, see Section
+ <seealso marker="asn1_getting_started#MAP_SEQ_SET">
+ Map representation for SEQUENCE and SET</seealso>
+ in the User's Guide.
+ </p>
+ </item>
<tag><c>compact_bit_string</c></tag>
<item>
<p>
The <c>BIT STRING</c> type is decoded to "compact notation".
<em>This option is not recommended for new code.</em>
+ This option cannot be combined with the option <c>maps</c>.
</p>
<p>For details, see Section
<seealso marker="asn1_getting_started#BIT STRING">
@@ -188,6 +201,7 @@ File3.asn</pre>
The <c>BIT STRING</c> type is decoded to the legacy
format, that is, a list of zeroes and ones.
<em>This option is not recommended for new code.</em>
+ This option cannot be combined with the option <c>maps</c>.
</p>
<p>For details, see Section
<seealso marker="asn1_getting_started#BIT STRING">BIT STRING</seealso>
@@ -202,7 +216,8 @@ File3.asn</pre>
marker="asn1_getting_started#BIT STRING">BIT STRING</seealso> and Section
<seealso marker="asn1_getting_started#OCTET STRING">OCTET
STRING</seealso> in the User's Guide.</p>
- <p><em>This option is not recommended for new code.</em></p>
+ <p><em>This option is not recommended for new code.</em>
+ This option cannot be combined with the option <c>maps</c>.</p>
</item>
<tag><c>{n2n, EnumTypeName}</c></tag>
<item>
diff --git a/lib/asn1/src/asn1_db.erl b/lib/asn1/src/asn1_db.erl
index 869ea310aa..a3e45ca915 100644
--- a/lib/asn1/src/asn1_db.erl
+++ b/lib/asn1/src/asn1_db.erl
@@ -20,7 +20,7 @@
%%
-module(asn1_db).
--export([dbstart/1,dbnew/2,dbload/1,dbload/3,dbsave/2,dbput/2,
+-export([dbstart/1,dbnew/3,dbload/1,dbload/4,dbsave/2,dbput/2,
dbput/3,dbget/2]).
-export([dbstop/0]).
@@ -37,13 +37,13 @@ dbstart(Includes0) ->
put(?MODULE, spawn_link(fun() -> init(Parent, Includes) end)),
ok.
-dbload(Module, Erule, Mtime) ->
- req({load, Module, Erule, Mtime}).
+dbload(Module, Erule, Maps, Mtime) ->
+ req({load, Module, {Erule,Maps}, Mtime}).
dbload(Module) ->
req({load, Module, any, {{0,0,0},{0,0,0}}}).
-dbnew(Module, Erule) -> req({new, Module, Erule}).
+dbnew(Module, Erule, Maps) -> req({new, Module, {Erule,Maps}}).
dbsave(OutFile, Module) -> cast({save, OutFile, Module}).
dbput(Module, K, V) -> cast({set, Module, K, V}).
dbput(Module, Kvs) -> cast({set, Module, Kvs}).
@@ -110,19 +110,19 @@ loop(#state{parent = Parent, monitor = MRef, table = Table,
ok = ets:tab2file(Mtab, TempFile),
ok = file:rename(TempFile, OutFile),
loop(State);
- {From, {new, Mod, Erule}} ->
+ {From, {new, Mod, EruleMaps}} ->
[] = ets:lookup(Table, Mod), %Assertion.
ModTableId = ets:new(list_to_atom(lists:concat(["asn1_",Mod])), []),
ets:insert(Table, {Mod, ModTableId}),
- ets:insert(ModTableId, {?MAGIC_KEY, info(Erule)}),
+ ets:insert(ModTableId, {?MAGIC_KEY, info(EruleMaps)}),
reply(From, ok),
loop(State);
- {From, {load, Mod, Erule, Mtime}} ->
+ {From, {load, Mod, EruleMaps, Mtime}} ->
case ets:member(Table, Mod) of
true ->
reply(From, ok);
false ->
- case load_table(Mod, Erule, Mtime, Includes) of
+ case load_table(Mod, EruleMaps, Mtime, Includes) of
{ok, ModTableId} ->
ets:insert(Table, {Mod, ModTableId}),
reply(From, ok);
@@ -151,20 +151,20 @@ lookup(Tab, K) ->
[{K,V}] -> V
end.
-info(Erule) ->
- {asn1ct:vsn(),Erule}.
+info(EruleMaps) ->
+ {asn1ct:vsn(),EruleMaps}.
-load_table(Mod, Erule, Mtime, Includes) ->
+load_table(Mod, EruleMaps, Mtime, Includes) ->
Base = lists:concat([Mod, ".asn1db"]),
case path_find(Includes, Mtime, Base) of
error ->
error;
- {ok,ModTab} when Erule =:= any ->
+ {ok,ModTab} when EruleMaps =:= any ->
{ok,ModTab};
{ok,ModTab} ->
Vsn = asn1ct:vsn(),
case ets:lookup(ModTab, ?MAGIC_KEY) of
- [{_,{Vsn,Erule}}] ->
+ [{_,{Vsn,EruleMaps}}] ->
%% Correct version and encoding rule.
{ok,ModTab};
_ ->
diff --git a/lib/asn1/src/asn1_records.hrl b/lib/asn1/src/asn1_records.hrl
index af10c1771c..d3d76f9566 100644
--- a/lib/asn1/src/asn1_records.hrl
+++ b/lib/asn1/src/asn1_records.hrl
@@ -28,6 +28,7 @@
-define('COMPLETE_ENCODE',1).
-define('TLV_DECODE',2).
+-define(MISSING_IN_MAP, asn1__MISSING_IN_MAP).
-record(module,{pos,name,defid,tagdefault='EXPLICIT',exports={exports,[]},imports={imports,[]}, extensiondefault=empty,typeorval}).
@@ -96,6 +97,17 @@
error_context %Top-level thingie (contains line numbers)
}).
+%% Code generation parameters and options.
+-record(gen,
+ {erule=ber :: 'ber' | 'per',
+ der=false :: boolean(),
+ aligned=false :: boolean(),
+ rec_prefix="" :: string(),
+ macro_prefix="" :: string(),
+ pack=record :: 'record' | 'map',
+ options=[] :: [any()]
+ }).
+
%% state record used by back-end at partial decode
%% active is set to 'yes' when a partial decode function is generated.
%% prefix is set to 'dec-inc-' or 'dec-partial-' is for
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index 4e030861f5..d27f8897af 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -193,7 +193,7 @@ check_pass(#st{code=M,file=File,includes=Includes,
erule=Erule,dbfile=DbFile,opts=Opts,
inputmodules=InputModules}=St) ->
start(Includes),
- case asn1ct_check:storeindb(#state{erule=Erule}, M) of
+ case asn1ct_check:storeindb(#state{erule=Erule,options=Opts}, M) of
ok ->
Module = asn1_db:dbget(M#module.name, 'MODULE'),
State = #state{mname=Module#module.name,
@@ -216,8 +216,8 @@ check_pass(#st{code=M,file=File,includes=Includes,
{error,St#st{error=Reason}}
end.
-save_pass(#st{code=M,erule=Erule}=St) ->
- ok = asn1ct_check:storeindb(#state{erule=Erule}, M),
+save_pass(#st{code=M,erule=Erule,opts=Opts}=St) ->
+ ok = asn1ct_check:storeindb(#state{erule=Erule,options=Opts}, M),
{ok,St}.
parse_listing(#st{code=Code,outfile=OutFile0}=St) ->
@@ -838,33 +838,53 @@ generate({M,GenTOrV}, OutFile, EncodingRule, Options) ->
debug_on(Options),
setup_bit_string_format(Options),
setup_legacy_erlang_types(Options),
- put(encoding_options,Options),
asn1ct_table:new(check_functions),
+ Gen = init_gen_record(EncodingRule, Options),
+
+ check_maps_option(Gen),
+
%% create decoding function names and taglists for partial decode
- case (catch specialized_decode_prepare(EncodingRule,M,GenTOrV,Options)) of
- {error, Reason} -> warning("Error in configuration file: ~n~p~n",
- [Reason], Options,
- "Error in configuration file");
- _ -> ok
+ try
+ specialized_decode_prepare(Gen, M)
+ catch
+ throw:{error, Reason} ->
+ warning("Error in configuration file: ~n~p~n",
+ [Reason], Options,
+ "Error in configuration file")
end,
- Result =
- case (catch asn1ct_gen:pgen(OutFile,EncodingRule,
- M#module.name,GenTOrV,Options)) of
- {'EXIT',Reason2} ->
- error("~p~n",[Reason2],Options),
- {error,Reason2};
- _ ->
- ok
- end,
+ Res = case catch asn1ct_gen:pgen(OutFile, Gen, M#module.name, GenTOrV) of
+ {'EXIT',Reason2} ->
+ error("~p~n",[Reason2],Options),
+ {error,Reason2};
+ _ ->
+ ok
+ end,
debug_off(Options),
- erase(encoding_options),
cleanup_bit_string_format(),
erase(tlv_format), % used in ber
erase(class_default_type),% used in ber
asn1ct_table:delete(check_functions),
- Result.
+ Res.
+
+init_gen_record(EncodingRule, Options) ->
+ Erule = case EncodingRule of
+ uper -> per;
+ _ -> EncodingRule
+ end,
+ Der = proplists:get_bool(der, Options),
+ Aligned = EncodingRule =:= per,
+ RecPrefix = proplists:get_value(record_name_prefix, Options, ""),
+ MacroPrefix = proplists:get_value(macro_name_prefix, Options, ""),
+ Pack = case proplists:get_value(maps, Options, false) of
+ true -> map;
+ false -> record
+ end,
+ #gen{erule=Erule,der=Der,aligned=Aligned,
+ rec_prefix=RecPrefix,macro_prefix=MacroPrefix,
+ pack=Pack,options=Options}.
+
setup_legacy_erlang_types(Opts) ->
F = case lists:member(legacy_erlang_types, Opts) of
@@ -910,6 +930,26 @@ cleanup_bit_string_format() ->
get_bit_string_format() ->
get(bit_string_format).
+check_maps_option(#gen{pack=map}) ->
+ case get_bit_string_format() of
+ bitstring ->
+ ok;
+ _ ->
+ Message1 = "The 'maps' option must not be combined with "
+ "'compact_bit_string' or 'legacy_bit_string'",
+ exit({error,{asn1,Message1}})
+ end,
+ case use_legacy_types() of
+ false ->
+ ok;
+ true ->
+ Message2 = "The 'maps' option must not be combined with "
+ "'legacy_erlang_types'",
+ exit({error,{asn1,Message2}})
+ end;
+check_maps_option(#gen{}) ->
+ ok.
+
%% parse_and_save parses an asn1 spec and saves the unchecked parse
%% tree in a data base file.
@@ -919,22 +959,27 @@ parse_and_save(Module,S) ->
SourceDir = S#state.sourcedir,
Includes = [I || {i,I} <- Options],
Erule = S#state.erule,
+ Maps = lists:member(maps, Options),
case get_input_file(Module, [SourceDir|Includes]) of
%% search for asn1 source
{file,SuffixedASN1source} ->
Mtime = filelib:last_modified(SuffixedASN1source),
- case asn1_db:dbload(Module, Erule, Mtime) of
+ case asn1_db:dbload(Module, Erule, Maps, Mtime) of
ok -> ok;
error -> parse_and_save1(S, SuffixedASN1source, Options)
end;
- Err ->
+ Err when not Maps ->
case asn1_db:dbload(Module) of
ok ->
+ %% FIXME: This should be an error.
warning("could not do a consistency check of the ~p file: no asn1 source file was found.~n",
[lists:concat([Module,".asn1db"])],Options);
error ->
ok
end,
+ {error,{asn1,input_file_error,Err}};
+ Err ->
+ %% Always fail directly when the 'maps' option is used.
{error,{asn1,input_file_error,Err}}
end.
@@ -997,9 +1042,8 @@ input_file_type(File) ->
end
end;
".asn1config" ->
- case read_config_file(File,asn1_module) of
+ case read_config_file_info(File, asn1_module) of
{ok,Asn1Module} ->
-% put(asn1_config_file,File),
input_file_type(Asn1Module);
Error ->
Error
@@ -1092,16 +1136,27 @@ translate_options([H|T]) ->
translate_options([]) -> [].
remove_asn_flags(Options) ->
- [X || X <- Options,
- X /= get_rule(Options),
- X /= optimize,
- X /= compact_bit_string,
- X /= legacy_bit_string,
- X /= legacy_erlang_types,
- X /= debug,
- X /= asn1config,
- X /= record_name_prefix].
-
+ [X || X <- Options, not is_asn1_flag(X)].
+
+is_asn1_flag(asn1config) -> true;
+is_asn1_flag(ber) -> true;
+is_asn1_flag(compact_bit_string) -> true;
+is_asn1_flag(debug) -> true;
+is_asn1_flag(der) -> true;
+is_asn1_flag(legacy_bit_string) -> true;
+is_asn1_flag({macro_name_prefix,_}) -> true;
+is_asn1_flag({n2n,_}) -> true;
+is_asn1_flag(noobj) -> true;
+is_asn1_flag(no_ok_wrapper) -> true;
+is_asn1_flag(optimize) -> true;
+is_asn1_flag(per) -> true;
+is_asn1_flag({record_name_prefix,_}) -> true;
+is_asn1_flag(undec_rec) -> true;
+is_asn1_flag(uper) -> true;
+is_asn1_flag(verbose) -> true;
+%% 'warnings_as_errors' is intentionally passed through to the compiler.
+is_asn1_flag(_) -> false.
+
debug_on(Options) ->
case lists:member(debug,Options) of
true ->
@@ -1370,25 +1425,26 @@ prepare_bytes(Bytes) -> list_to_binary(Bytes).
vsn() ->
?vsn.
-specialized_decode_prepare(Erule,M,TsAndVs,Options) ->
- case lists:member(asn1config,Options) of
+specialized_decode_prepare(#gen{erule=ber,options=Options}=Gen, M) ->
+ case lists:member(asn1config, Options) of
true ->
- partial_decode_prepare(Erule,M,TsAndVs,Options);
- _ ->
+ special_decode_prepare_1(Gen, M);
+ false ->
ok
- end.
+ end;
+specialized_decode_prepare(_, _) ->
+ ok.
+
%% Reads the configuration file if it exists and stores information
%% about partial decode and incomplete decode
-partial_decode_prepare(ber,M,TsAndVs,Options) when is_tuple(TsAndVs) ->
+special_decode_prepare_1(#gen{options=Options}=Gen, M) ->
%% read configure file
-
- ModName =
- case lists:keysearch(asn1config,1,Options) of
- {value,{_,MName}} -> MName;
- _ -> M#module.name
- end,
+ ModName = case lists:keyfind(asn1config, 1, Options) of
+ {_,MName} -> MName;
+ false -> M#module.name
+ end,
%% io:format("ModName: ~p~nM#module.name: ~p~n~n",[ModName,M#module.name]),
- case read_config_file(ModName) of
+ case read_config_file(Gen, ModName) of
no_config_file ->
ok;
CfgList ->
@@ -1407,11 +1463,7 @@ partial_decode_prepare(ber,M,TsAndVs,Options) when is_tuple(TsAndVs) ->
Part_inc_tlv_tags = tlv_tags(CommandList2),
save_config(partial_incomplete_decode,Part_inc_tlv_tags),
save_gen_state(exclusive_decode,ExclusiveDecode,Part_inc_tlv_tags)
- end;
-partial_decode_prepare(_,_,_,_) ->
- ok.
-
-
+ end.
%% create_partial_inc_decode_gen_info/2
%%
@@ -1863,46 +1915,38 @@ tlv_tag1(<<0:1,PartialTag:7>>,Acc) ->
tlv_tag1(<<1:1,PartialTag:7,Buffer/binary>>,Acc) ->
tlv_tag1(Buffer,(Acc bsl 7) bor PartialTag).
-%% reads the content from the configuration file and returns the
-%% selected part choosen by InfoType. Assumes that the config file
+%% Reads the content from the configuration file and returns the
+%% selected part chosen by InfoType. Assumes that the config file
%% content is an Erlang term.
-read_config_file(ModuleName,InfoType) when is_atom(InfoType) ->
- CfgList = read_config_file(ModuleName),
- get_config_info(CfgList,InfoType).
+read_config_file_info(ModuleName, InfoType) when is_atom(InfoType) ->
+ Name = ensure_ext(ModuleName, ".asn1config"),
+ CfgList = read_config_file0(Name, []),
+ get_config_info(CfgList, InfoType).
+read_config_file(#gen{options=Options}, ModuleName) ->
+ Name = ensure_ext(ModuleName, ".asn1config"),
+ Includes = [I || {i,I} <- Options],
+ read_config_file0(Name, ["."|Includes]).
-read_config_file(ModuleName) ->
- case file:consult(lists:concat([ModuleName,'.asn1config'])) of
+read_config_file0(Name, [D|Dirs]) ->
+ case file:consult(filename:join(D, Name)) of
{ok,CfgList} ->
CfgList;
{error,enoent} ->
- Options = get(encoding_options),
- Includes = [I || {i,I} <- Options],
- read_config_file1(ModuleName,Includes);
+ read_config_file0(Name, Dirs);
{error,Reason} ->
Error = "error reading asn1 config file: " ++
file:format_error(Reason),
throw({error,Error})
- end.
-read_config_file1(ModuleName,[]) ->
- case filename:extension(ModuleName) of
- ".asn1config" ->
- no_config_file;
- _ ->
- read_config_file(lists:concat([ModuleName,".asn1config"]))
end;
-read_config_file1(ModuleName,[H|T]) ->
-% File = filename:join([H,lists:concat([ModuleName,'.asn1config'])]),
- File = filename:join([H,ModuleName]),
- case file:consult(File) of
- {ok,CfgList} ->
- CfgList;
- {error,enoent} ->
- read_config_file1(ModuleName,T);
- {error,Reason} ->
- Error = "error reading asn1 config file: " ++
- file:format_error(Reason),
- throw({error,Error})
+read_config_file0(_, []) ->
+ no_config_file.
+
+ensure_ext(ModuleName, Ext) ->
+ Name = filename:join([ModuleName]),
+ case filename:extension(Name) of
+ Ext -> Name;
+ _ -> Name ++ Ext
end.
get_config_info(CfgList,InfoType) ->
@@ -2382,8 +2426,10 @@ format_error({write_error,File,Reason}) ->
io_lib:format("writing output file ~s failed: ~s",
[File,file:format_error(Reason)]).
-is_error(S) when is_record(S, state) ->
- is_error(S#state.options);
+is_error(#state{options=Opts}) ->
+ is_error(Opts);
+is_error(#gen{options=Opts}) ->
+ is_error(Opts);
is_error(O) ->
lists:member(errors, O) orelse is_verbose(O).
@@ -2392,8 +2438,10 @@ is_warning(S) when is_record(S, state) ->
is_warning(O) ->
lists:member(warnings, O) orelse is_verbose(O).
-is_verbose(S) when is_record(S, state) ->
- is_verbose(S#state.options);
+is_verbose(#state{options=Opts}) ->
+ is_verbose(Opts);
+is_verbose(#gen{options=Opts}) ->
+ is_verbose(Opts);
is_verbose(O) ->
lists:member(verbose, O).
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index f2c895bfaa..321f4147f5 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -2239,12 +2239,18 @@ normalized_record(SorS,S,Value,Components,NameList) ->
case is_record_normalized(S,NewName,Value,length(Components)) of
true ->
Value;
- _ ->
+ false ->
NoComps = length(Components),
ListOfVals = normalize_seq_or_set(SorS,S,Value,Components,NameList,[]),
- NoComps = length(ListOfVals), %% Assert
- list_to_tuple([NewName|ListOfVals])
+ NoComps = length(ListOfVals), %Assertion.
+ case use_maps(S) of
+ false ->
+ list_to_tuple([NewName|ListOfVals]);
+ true ->
+ create_map_value(Components, ListOfVals)
+ end
end.
+
is_record_normalized(S,Name,V = #'Externalvaluereference'{},NumComps) ->
case get_referenced_type(S,V) of
{_M,#valuedef{type=_T1,value=V2}} ->
@@ -2253,9 +2259,20 @@ is_record_normalized(S,Name,V = #'Externalvaluereference'{},NumComps) ->
end;
is_record_normalized(_S,Name,Value,NumComps) when is_tuple(Value) ->
(tuple_size(Value) =:= (NumComps + 1)) andalso (element(1, Value) =:= Name);
+is_record_normalized(_S, _Name, Value, _NumComps) when is_map(Value) ->
+ true;
is_record_normalized(_,_,_,_) ->
false.
+use_maps(#state{options=Opts}) ->
+ lists:member(maps, Opts).
+
+create_map_value(Components, ListOfVals) ->
+ Zipped = lists:zip(Components, ListOfVals),
+ L = [{Name,V} || {#'ComponentType'{name=Name},V} <- Zipped,
+ V =/= asn1_NOVALUE],
+ maps:from_list(L).
+
normalize_seq_or_set(SorS, S,
[{#seqtag{val=Cname},V}|Vs],
[#'ComponentType'{name=Cname,typespec=TS}|Cs],
@@ -4192,7 +4209,7 @@ iof_associated_type1(S,C) ->
%% fieldname=[{typefieldreference,'Type'}],
fieldname={'Type',[]},
type=Typefield_type},
- IOFComponents =
+ IOFComponents0 =
[#'ComponentType'{name='type-id',
typespec=#type{tag=C1TypeTag,
def=ObjectIdentifier,
@@ -4209,6 +4226,7 @@ iof_associated_type1(S,C) ->
tablecinf=Comp2tablecinf},
prop=mandatory,
tags=[{'CONTEXT',0}]}],
+ IOFComponents = textual_order(IOFComponents0),
#'SEQUENCE'{tablecinf=TableCInf,
components=simplify_comps(IOFComponents)}.
@@ -5673,7 +5691,8 @@ storeindb(S0, #module{name=ModName,typeorval=TVlist0}=M) ->
storeindb_1(S, #module{name=ModName}=M, TVlist0, TVlist) ->
NewM = M#module{typeorval=findtypes_and_values(TVlist0)},
- asn1_db:dbnew(ModName, S#state.erule),
+ Maps = lists:member(maps, S#state.options),
+ asn1_db:dbnew(ModName, S#state.erule, Maps),
asn1_db:dbput(ModName, 'MODULE', NewM),
asn1_db:dbput(ModName, TVlist),
include_default_class(S, NewM#module.name),
diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
index 325bea5879..16af09bca9 100644
--- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
@@ -32,7 +32,7 @@
-include("asn1_records.hrl").
--import(asn1ct_gen, [emit/1,demit/1,get_record_name_prefix/0]).
+-import(asn1ct_gen, [emit/1,demit/1,get_record_name_prefix/1]).
-define(ASN1CT_GEN_BER,asn1ct_gen_ber_bin_v2).
@@ -57,7 +57,7 @@
%%===============================================================================
%%===============================================================================
-gen_encode_sequence(Erules,Typename,D) when is_record(D,type) ->
+gen_encode_sequence(Gen, Typename, #type{}=D) ->
asn1ct_name:start(),
asn1ct_name:new(term),
asn1ct_name:new(bytes),
@@ -67,8 +67,12 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) ->
ValName =
case Typename of
['EXTERNAL'] ->
+ Tr = case Gen of
+ #gen{pack=record} -> transform_to_EXTERNAL1990;
+ #gen{pack=map} -> transform_to_EXTERNAL1990_maps
+ end,
emit([indent(4),"NewVal = ",
- {call,ext,transform_to_EXTERNAL1990,["Val"]},
+ {call,ext,Tr,["Val"]},
com,nl]),
"NewVal";
_ ->
@@ -90,18 +94,9 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) ->
{Rl,El} -> Rl ++ El;
_ -> CompList
end,
-
-%% don't match recordname for now, because of compatibility reasons
-%% emit(["{'",asn1ct_gen:list2rname(Typename),"'"]),
- emit(["{_"]),
- case length(CompList1) of
- 0 ->
- true;
- CompListLen ->
- emit([","]),
- mkcindexlist([Tc || Tc <- lists:seq(1,CompListLen)])
- end,
- emit(["} = ",ValName,",",nl]),
+
+ enc_match_input(Gen, ValName, CompList1),
+
EncObj =
case TableConsInfo of
#simpletableattributes{usedclassfield=Used,
@@ -125,7 +120,7 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) ->
emit([ObjectEncode," = ",nl,
" ",{asis,ObjSetMod},":'getenc_",ObjSetName,
"'("]),
- ValueMatch = value_match(ValueIndex,
+ ValueMatch = value_match(Gen, ValueIndex,
lists:concat(["Cindex",N])),
emit([indent(35),ValueMatch,"),",nl]),
{AttrN,ObjectEncode};
@@ -144,7 +139,7 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) ->
end
end,
- gen_enc_sequence_call(Erules,Typename,CompList1,1,Ext,EncObj),
+ gen_enc_sequence_call(Gen, Typename, CompList1, 1, Ext, EncObj),
emit([nl," BytesSoFar = "]),
case SeqOrSet of
@@ -168,7 +163,36 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) ->
call(encode_tags, ["TagIn","BytesSoFar","LenSoFar"]),
emit([".",nl]).
-gen_decode_sequence(Erules,Typename,D) when is_record(D,type) ->
+enc_match_input(#gen{pack=record}, ValName, CompList) ->
+ Len = length(CompList),
+ Vars = [lists:concat(["Cindex",N]) || N <- lists:seq(1, Len)],
+ RecordName = "_",
+ emit(["{",lists:join(",", [RecordName|Vars]),"} = ",ValName,com,nl]);
+enc_match_input(#gen{pack=map}, ValName, CompList) ->
+ Len = length(CompList),
+ Vars = [lists:concat(["Cindex",N]) || N <- lists:seq(1, Len)],
+ Zipped = lists:zip(CompList, Vars),
+ M = [[{asis,Name},":=",Var] ||
+ {#'ComponentType'{prop=mandatory,name=Name},Var} <- Zipped],
+ case M of
+ [] ->
+ ok;
+ [_|_] ->
+ emit(["#{",lists:join(",", M),"} = ",ValName,com,nl])
+ end,
+ Os0 = [{Name,Var} ||
+ {#'ComponentType'{prop=Prop,name=Name},Var} <- Zipped,
+ Prop =/= mandatory],
+ F = fun({Name,Var}) ->
+ [Var," = case ",ValName," of\n"
+ " #{",{asis,Name},":=",Var,"_0} -> ",
+ Var,"_0;\n"
+ " _ -> ",atom_to_list(?MISSING_IN_MAP),"\n"
+ "end"]
+ end,
+ emit(lists:join(",\n", [F(E) || E <- Os0]++[[]])).
+
+gen_decode_sequence(Gen, Typename, #type{}=D) ->
asn1ct_name:start(),
asn1ct_name:new(tag),
#'SEQUENCE'{tablecinf=TableConsInfo,components=CList0} = D#type.def,
@@ -225,15 +249,20 @@ gen_decode_sequence(Erules,Typename,D) when is_record(D,type) ->
_ ->
{false,false}
end,
- RecordName = lists:concat([get_record_name_prefix(),
- asn1ct_gen:list2rname(Typename)]),
- case gen_dec_sequence_call(Erules,Typename,CompList2,Ext,DecObjInf) of
- no_terms -> % an empty sequence
- emit([nl,nl]),
- demit(["Result = "]), %dbg
- %% return value as record
+ RecordName0 = lists:concat([get_record_name_prefix(Gen),
+ asn1ct_gen:list2rname(Typename)]),
+ RecordName = list_to_atom(RecordName0),
+ case gen_dec_sequence_call(Gen, Typename, CompList2, Ext, DecObjInf) of
+ no_terms -> % an empty sequence
asn1ct_name:new(rb),
- emit([" {'",RecordName,"'}.",nl,nl]);
+ case Gen of
+ #gen{pack=record} ->
+ emit([nl,nl,
+ " {'",RecordName,"'}.",nl,nl]);
+ #gen{pack=map} ->
+ emit([nl,nl,
+ " #{}.",nl,nl])
+ end;
{LeadingAttrTerm,PostponedDecArgs} ->
emit([nl]),
case {LeadingAttrTerm,PostponedDecArgs} of
@@ -243,7 +272,7 @@ gen_decode_sequence(Erules,Typename,D) when is_record(D,type) ->
ok;
{[{ObjSetRef,LeadingAttr,Term}],PostponedDecArgs} ->
DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])),
- ValueMatch = value_match(ValueIndex,Term),
+ ValueMatch = value_match(Gen, ValueIndex,Term),
{ObjSetMod,ObjSetName} = ObjSetRef,
emit([DecObj," =",nl,
" ",{asis,ObjSetMod},":'getdec_",ObjSetName,"'(",
@@ -263,22 +292,64 @@ gen_decode_sequence(Erules,Typename,D) when is_record(D,type) ->
"end,",nl])
end,
asn1ct_name:new(rb),
- case Typename of
- ['EXTERNAL'] ->
- emit([" OldFormat={'",RecordName,
- "', "]),
- mkvlist(asn1ct_name:all(term)),
- emit(["},",nl]),
- emit([" ",
- {call,ext,transform_to_EXTERNAL1994,
- ["OldFormat"]},".",nl]);
- _ ->
- emit([" {'",RecordName,"', "]),
- mkvlist(asn1ct_name:all(term)),
- emit(["}.",nl,nl])
- end
+ gen_dec_pack(Gen, RecordName, Typename, CompList),
+ emit([".",nl])
end.
+gen_dec_pack(Gen, RecordName, Typename, CompList) ->
+ case Typename of
+ ['EXTERNAL'] ->
+ dec_external(Gen, RecordName);
+ _ ->
+ asn1ct_name:new(res),
+ gen_dec_do_pack(Gen, RecordName, CompList),
+ emit([com,nl,
+ {curr,res}])
+ end.
+
+dec_external(#gen{pack=record}, RecordName) ->
+ All = [{var,Term} || Term <- asn1ct_name:all(term)],
+ Record = [{asis,RecordName}|All],
+ emit(["OldFormat={",lists:join(",", Record),"},",nl,
+ {call,ext,transform_to_EXTERNAL1994,
+ ["OldFormat"]}]);
+dec_external(#gen{pack=map}, _RecordName) ->
+ Vars = asn1ct_name:all(term),
+ Names = ['direct-reference','indirect-reference',
+ 'data-value-descriptor',encoding],
+ Zipped = lists:zip(Names, Vars),
+ MapInit = lists:join(",", [["'",N,"'=>",{var,V}] || {N,V} <- Zipped]),
+ emit(["OldFormat = #{",MapInit,"}",com,nl,
+ "ASN11994Format =",nl,
+ {call,ext,transform_to_EXTERNAL1994_maps,
+ ["OldFormat"]}]).
+
+gen_dec_do_pack(#gen{pack=record}, RecordName, _CompList) ->
+ All = asn1ct_name:all(term),
+ L = [{asis,RecordName}|[{var,Var} || Var <- All]],
+ emit([{curr,res}," = {",lists:join(",", L),"}"]);
+gen_dec_do_pack(#gen{pack=map}, _, CompList) ->
+ Zipped = lists:zip(CompList, asn1ct_name:all(term)),
+ PF = fun({#'ComponentType'{prop='OPTIONAL'},_}) -> false;
+ ({_,_}) -> true
+ end,
+ {Mandatory,Optional} = lists:partition(PF, Zipped),
+ L = [[{asis,Name},"=>",{var,Var}] ||
+ {#'ComponentType'{name=Name},Var} <- Mandatory],
+ emit([{curr,res}," = #{",lists:join(",", L),"}"]),
+ gen_dec_map_optional(Optional).
+
+gen_dec_map_optional([{#'ComponentType'{name=Name},Var}|T]) ->
+ asn1ct_name:new(res),
+ emit([com,nl,
+ {curr,res}," = case ",{var,Var}," of",nl,
+ " asn1_NOVALUE -> ",{prev,res},";",nl,
+ " _ -> ",{prev,res},"#{",{asis,Name},"=>",{var,Var},"}",nl,
+ "end"]),
+ gen_dec_map_optional(T);
+gen_dec_map_optional([]) ->
+ ok.
+
gen_dec_postponed_decs(_,[]) ->
emit(nl);
gen_dec_postponed_decs(DecObj,[{_Cname,{FirstPFN,PFNList},Term,
@@ -327,7 +398,7 @@ emit_opt_or_mand_check(Value,TmpTerm) ->
gen_encode_set(Erules,Typename,D) when is_record(D,type) ->
gen_encode_sequence(Erules,Typename,D).
-gen_decode_set(Erules,Typename,D) when is_record(D,type) ->
+gen_decode_set(Gen, Typename, #type{}=D) ->
asn1ct_name:start(),
%% asn1ct_name:new(term),
asn1ct_name:new(tag),
@@ -393,7 +464,7 @@ gen_decode_set(Erules,Typename,D) when is_record(D,type) ->
_ ->
emit(["SetFun = fun(FunTlv) ->", nl]),
emit(["case FunTlv of ",nl]),
- NextNum = gen_dec_set_cases(Erules,Typename,CompList,1),
+ NextNum = gen_dec_set_cases(Gen, Typename, CompList, 1),
emit([indent(6), {curr,else}," -> ",nl,
indent(9),"{",NextNum,", ",{curr,else},"}",nl]),
emit([indent(3),"end",nl]),
@@ -405,14 +476,17 @@ gen_decode_set(Erules,Typename,D) when is_record(D,type) ->
asn1ct_name:new(tlv)
end,
- RecordName = lists:concat([get_record_name_prefix(),
- asn1ct_gen:list2rname(Typename)]),
- case gen_dec_sequence_call(Erules,Typename,CompList,Ext,DecObjInf) of
- no_terms -> % an empty sequence
- emit([nl,nl]),
- demit(["Result = "]), %dbg
- %% return value as record
- emit([" {'",RecordName,"'}.",nl]);
+ RecordName0 = lists:concat([get_record_name_prefix(Gen),
+ asn1ct_gen:list2rname(Typename)]),
+ RecordName = list_to_atom(RecordName0),
+ case gen_dec_sequence_call(Gen, Typename, CompList, Ext, DecObjInf) of
+ no_terms -> % an empty SET
+ case Gen of
+ #gen{pack=record} ->
+ emit([nl,nl," {'",RecordName,"'}.",nl,nl]);
+ #gen{pack=map} ->
+ emit([nl,nl," #{}.",nl,nl])
+ end;
{LeadingAttrTerm,PostponedDecArgs} ->
emit([nl]),
case {LeadingAttrTerm,PostponedDecArgs} of
@@ -422,7 +496,7 @@ gen_decode_set(Erules,Typename,D) when is_record(D,type) ->
ok;
{[{ObjSetRef,LeadingAttr,Term}],PostponedDecArgs} ->
DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])),
- ValueMatch = value_match(ValueIndex,Term),
+ ValueMatch = value_match(Gen, ValueIndex, Term),
{ObjSetMod,ObjSetName} = ObjSetRef,
emit([DecObj," =",nl,
" ",{asis,ObjSetMod},":'getdec_",ObjSetName,"'(",
@@ -441,9 +515,8 @@ gen_decode_set(Erules,Typename,D) when is_record(D,type) ->
"}}}) % extra fields not allowed",nl,
"end,",nl])
end,
- emit([" {'",RecordName,"', "]),
- mkvlist(asn1ct_name:all(term)),
- emit(["}.",nl])
+ gen_dec_pack(Gen, RecordName, Typename, CompList),
+ emit([".",nl])
end.
@@ -504,10 +577,8 @@ gen_decode_sof(Erules,TypeName,_InnerTypeName,D) when is_record(D,type) ->
emit([" || ",{curr,v}," <- ",{curr,tlv},"].",nl,nl,nl]).
-gen_encode_sof_components(Erules,Typename,SeqOrSetOf,Cont)
- when is_record(Cont,type)->
-
- {Objfun,Objfun_novar,EncObj} =
+gen_encode_sof_components(Gen, Typename, SeqOrSetOf, #type{}=Cont) ->
+ {Objfun,Objfun_novar,EncObj} =
case Cont#type.tablecinf of
[{objfun,_}|_R] ->
{", ObjFun",", _",{no_attr,"ObjFun"}};
@@ -517,20 +588,19 @@ gen_encode_sof_components(Erules,Typename,SeqOrSetOf,Cont)
emit(["'enc_",asn1ct_gen:list2name(Typename),
"_components'([]",Objfun_novar,", AccBytes, AccLen) -> ",nl]),
- case catch lists:member(der,get(encoding_options)) of
- true when SeqOrSetOf=='SET OF'->
+ case {Gen,SeqOrSetOf} of
+ {#gen{der=true},'SET OF'} ->
asn1ct_func:need({ber,dynamicsort_SETOF,1}),
emit([indent(3),
"{dynamicsort_SETOF(AccBytes),AccLen};",nl,nl]);
- _ ->
+ {_,_} ->
emit([indent(3),"{lists:reverse(AccBytes),AccLen};",nl,nl])
end,
emit(["'enc_",asn1ct_gen:list2name(Typename),
"_components'([H|T]",Objfun,",AccBytes, AccLen) ->",nl]),
TypeNameSuffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,Cont#type.def),
- gen_enc_line(Erules,Typename,TypeNameSuffix,Cont,"H",3,
-% mandatory,"{EncBytes,EncLen} = ",EncObj),
- mandatory,EncObj),
+ gen_enc_line(Gen, Typename, TypeNameSuffix, Cont, "H", 3,
+ mandatory, EncObj),
emit([",",nl]),
emit([indent(3),"'enc_",asn1ct_gen:list2name(Typename),
"_components'(T",Objfun,","]),
@@ -1028,35 +1098,44 @@ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,Assign,EncObj)
emit([nl,indent(7),"end"])
end.
-gen_optormand_case(mandatory, _Erules, _TopType, _Cname, _Type, _Element) ->
+gen_optormand_case(mandatory, _Gen, _TopType, _Cname, _Type, _Element) ->
ok;
-gen_optormand_case('OPTIONAL', Erules, _TopType, _Cname, _Type, Element) ->
+gen_optormand_case('OPTIONAL', Gen, _TopType, _Cname, _Type, Element) ->
emit([" case ",Element," of",nl]),
- emit([indent(9),"asn1_NOVALUE -> {",
- empty_lb(Erules),",0};",nl]),
+ Missing = case Gen of
+ #gen{pack=record} -> asn1_NOVALUE;
+ #gen{pack=map} -> ?MISSING_IN_MAP
+ end,
+ emit([indent(9),Missing," -> {",
+ empty_lb(Gen),",0};",nl]),
emit([indent(9),"_ ->",nl,indent(12)]);
-gen_optormand_case({'DEFAULT',DefaultValue}, Erules, _TopType,
+gen_optormand_case({'DEFAULT',DefaultValue}, Gen, _TopType,
_Cname, Type, Element) ->
CurrMod = get(currmod),
- case catch lists:member(der,get(encoding_options)) of
- true ->
- asn1ct_gen_check:emit(Type, DefaultValue, Element);
- _ ->
- emit([" case ",Element," of",nl]),
- emit([indent(9),"asn1_DEFAULT -> {",
- empty_lb(Erules),
- ",0};",nl]),
- case DefaultValue of
- #'Externalvaluereference'{module=CurrMod,
- value=V} ->
- emit([indent(9),"?",{asis,V}," -> {",
- empty_lb(Erules),",0};",nl]);
- _ ->
- emit([indent(9),{asis,
- DefaultValue}," -> {",
- empty_lb(Erules),",0};",nl])
- end,
- emit([indent(9),"_ ->",nl,indent(12)])
+ case Gen of
+ #gen{erule=ber,der=true} ->
+ asn1ct_gen_check:emit(Gen, Type, DefaultValue, Element);
+ #gen{erule=ber,der=false,pack=Pack} ->
+ Ind9 = indent(9),
+ DefMarker = case Pack of
+ record -> asn1_DEFAULT;
+ map -> ?MISSING_IN_MAP
+ end,
+ emit([" case ",Element," of",nl,
+ Ind9,{asis,DefMarker}," ->",nl,
+ Ind9,indent(3),"{",empty_lb(Gen),",0};",nl,
+ Ind9,"_ when ",Element," =:= "]),
+ Dv = case DefaultValue of
+ #'Externalvaluereference'{module=CurrMod,
+ value=V} ->
+ ["?",{asis,V}];
+ _ ->
+ [{asis,DefaultValue}]
+ end,
+ emit(Dv++[" ->",nl,
+ Ind9,indent(3),"{",empty_lb(Gen),",0};",nl,
+ Ind9,"_ ->",nl,
+ indent(12)])
end.
%% Use for SEQUENCE OF and CHOICE.
@@ -1207,7 +1286,7 @@ gen_dec_call({typefield,_},_,_,Cname,Type,BytesVar,Tag,_,_,_DecObjInf,OptOrMandC
(Type#type.def)#'ObjectClassFieldType'.fieldname,
[{Cname,RefedFieldName,asn1ct_gen:mk_var(asn1ct_name:curr(term)),
asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),Tag,OptOrMandComp}];
-gen_dec_call(InnerType, _Erules, TopType, Cname, Type, BytesVar,
+gen_dec_call(InnerType, Gen, TopType, Cname, Type, BytesVar,
Tag, _PrimOptOrMand, _OptOrMand, DecObjInf,_) ->
WhatKind = asn1ct_gen:type(InnerType),
gen_dec_call1(WhatKind, InnerType, TopType, Cname,
@@ -1215,7 +1294,7 @@ gen_dec_call(InnerType, _Erules, TopType, Cname, Type, BytesVar,
case DecObjInf of
{Cname,{_,OSet,_UniqueFName,ValIndex}} ->
Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
- ValueMatch = value_match(ValIndex,Term),
+ ValueMatch = value_match(Gen, ValIndex, Term),
{ObjSetMod,ObjSetName} = OSet,
emit([",",nl,"ObjFun = ",{asis,ObjSetMod},":'getdec_",ObjSetName,
"'(",ValueMatch,")"]);
@@ -1340,19 +1419,6 @@ gen_dec_call1(WhatKind, _, TopType, Cname, Type, BytesVar, Tag) ->
indent(N) ->
lists:duplicate(N,32). % 32 = space
-mkcindexlist([H,T1|T], Sep) -> % Sep is a string e.g ", " or "+ "
- emit(["Cindex",H,Sep]),
- mkcindexlist([T1|T], Sep);
-mkcindexlist([H|T], Sep) ->
- emit(["Cindex",H]),
- mkcindexlist(T, Sep);
-mkcindexlist([], _) ->
- true.
-
-mkcindexlist(L) ->
- mkcindexlist(L,", ").
-
-
mkvlist([H,T1|T], Sep) -> % Sep is a string e.g ", " or "+ "
emit([{var,H},Sep]),
mkvlist([T1|T], Sep);
@@ -1429,19 +1495,25 @@ mkfuncname(TopType,Cname,WhatKind,Prefix,Suffix) ->
{F, "?MODULE", F}
end.
-empty_lb(ber) ->
+empty_lb(#gen{erule=ber}) ->
"<<>>".
-value_match(Index,Value) when is_atom(Value) ->
- value_match(Index,atom_to_list(Value));
-value_match([],Value) ->
+value_match(#gen{pack=record}, VIs, Value) ->
+ value_match_rec(VIs, Value);
+value_match(#gen{pack=map}, VIs, Value) ->
+ value_match_map(VIs, Value).
+
+value_match_rec([], Value) ->
+ Value;
+value_match_rec([{VI,_}|VIs], Value0) ->
+ Value = value_match_rec(VIs, Value0),
+ lists:concat(["element(",VI,", ",Value,")"]).
+
+value_match_map([], Value) ->
Value;
-value_match([{VI,_}|VIs],Value) ->
- value_match1(Value,VIs,lists:concat(["element(",VI,","]),1).
-value_match1(Value,[],Acc,Depth) ->
- Acc ++ Value ++ lists:concat(lists:duplicate(Depth,")"));
-value_match1(Value,[{VI,_}|VIs],Acc,Depth) ->
- value_match1(Value,VIs,Acc++lists:concat(["element(",VI,","]),Depth+1).
+value_match_map([{_,Name}|VIs], Value0) ->
+ Value = value_match_map(VIs, Value0),
+ lists:concat(["maps:get(",Name,", ",Value,")"]).
call(F, Args) ->
asn1ct_func:call(ber, F, Args).
diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl
index a34b25182c..b7579c8065 100644
--- a/lib/asn1/src/asn1ct_constructed_per.erl
+++ b/lib/asn1/src/asn1ct_constructed_per.erl
@@ -32,17 +32,27 @@
-include("asn1_records.hrl").
%-compile(export_all).
--import(asn1ct_gen, [emit/1,demit/1,get_record_name_prefix/0]).
--import(asn1ct_func, [call/3]).
+-import(asn1ct_gen, [emit/1,demit/1,get_record_name_prefix/1]).
+
+-type type_name() :: any().
+
%% ENCODE GENERATOR FOR SEQUENCE TYPE ** **********
-gen_encode_set(Erules,TypeName,D) ->
- gen_encode_constructed(Erules,TypeName,D).
+-spec gen_encode_set(Gen, TypeName, #type{}) -> 'ok' when
+ Gen :: #gen{},
+ TypeName :: type_name().
+
+gen_encode_set(Gen, TypeName, D) ->
+ gen_encode_constructed(Gen, TypeName, D).
+
+-spec gen_encode_sequence(Gen, TypeName, #type{}) -> 'ok' when
+ Gen :: #gen{},
+ TypeName :: type_name().
-gen_encode_sequence(Erules,TypeName,D) ->
- gen_encode_constructed(Erules,TypeName,D).
+gen_encode_sequence(Gen, TypeName, D) ->
+ gen_encode_constructed(Gen, TypeName, D).
gen_encode_constructed(Erule, Typename, #type{}=D) ->
asn1ct_name:start(),
@@ -50,88 +60,23 @@ gen_encode_constructed(Erule, Typename, #type{}=D) ->
asn1ct_imm:enc_cg(Imm, is_aligned(Erule)),
emit([".",nl]).
-gen_encode_constructed_imm(Erule, Typename, #type{}=D) ->
- {ExtAddGroup,TmpCompList,TableConsInfo} =
- case D#type.def of
- #'SEQUENCE'{tablecinf=TCI,components=CL,extaddgroup=ExtAddGroup0} ->
- {ExtAddGroup0,CL,TCI};
- #'SET'{tablecinf=TCI,components=CL} ->
- {undefined,CL,TCI}
- end,
-
- CompList = case ExtAddGroup of
- undefined ->
- TmpCompList;
- _ when is_integer(ExtAddGroup) ->
- %% This is a fake SEQUENCE representing an ExtensionAdditionGroup
- %% Reset the textual order so we get the right
- %% index of the components
- [Comp#'ComponentType'{textual_order=undefined}||
- Comp<-TmpCompList]
- end,
- ExternalImm =
- case Typename of
- ['EXTERNAL'] ->
- Next = asn1ct_gen:mk_var(asn1ct_name:next(val)),
- Curr = asn1ct_gen:mk_var(asn1ct_name:curr(val)),
- asn1ct_name:new(val),
- [{call,ext,transform_to_EXTERNAL1990,[{var,Curr}],{var,Next}}];
- _ ->
- []
- end,
- Aligned = is_aligned(Erule),
- Value0 = make_var(val),
+gen_encode_constructed_imm(Gen, Typename, #type{}=D) ->
+ {CompList,TableConsInfo} = enc_complist(D),
+ ExternalImm = external_imm(Gen, Typename),
Optionals = optionals(to_textual_order(CompList)),
- ImmOptionals = [asn1ct_imm:per_enc_optional(Value0, Opt, Aligned) ||
- Opt <- Optionals],
+ ImmOptionals = enc_optionals(Gen, Optionals),
Ext = extensible_enc(CompList),
+ Aligned = is_aligned(Gen),
ExtImm = case Ext of
{ext,ExtPos,NumExt} when NumExt > 0 ->
- gen_encode_extaddgroup(CompList),
+ gen_encode_extaddgroup(Gen, CompList),
Value = make_var(val),
- asn1ct_imm:per_enc_extensions(Value, ExtPos,
- NumExt, Aligned);
+ enc_extensions(Gen, Value, ExtPos, NumExt, Aligned);
_ ->
[]
end,
- {EncObj,ObjSetImm} =
- case TableConsInfo of
- #simpletableattributes{usedclassfield=Used,
- uniqueclassfield=Unique} when Used /= Unique ->
- {false,[]};
- %% ObjectSet, name of the object set in constraints
- %%
- %%{ObjectSet,AttrN,N,UniqueFieldName} -> %% N is index of attribute that determines constraint
- #simpletableattributes{objectsetname=ObjectSet,
- c_name=AttrN,
- c_index=N,
- usedclassfield=UniqueFieldName,
- uniqueclassfield=UniqueFieldName,
- valueindex=ValueIndex0
- } -> %% N is index of attribute that determines constraint
- {Module,ObjSetName} = ObjectSet,
- #typedef{typespec=#'ObjectSet'{gen=Gen}} =
- asn1_db:dbget(Module, ObjSetName),
- case Gen of
- true ->
- ValueIndex = ValueIndex0 ++ [{N+1,top}],
- Val = make_var(val),
- {ObjSetImm0,Dst} = enc_dig_out_value(ValueIndex, Val),
- {{AttrN,Dst},ObjSetImm0};
- false ->
- {false,[]}
- end;
- _ ->
- case D#type.tablecinf of
- [{objfun,_}|_] ->
- %% when the simpletableattributes was at an outer
- %% level and the objfun has been passed through the
- %% function call
- {{"got objfun through args",{var,"ObjFun"}},[]};
- _ ->
- {false,[]}
- end
- end,
+ MatchImm = enc_map_match(Gen, CompList),
+ {EncObj,ObjSetImm} = enc_table(Gen, TableConsInfo, D),
ImmSetExt =
case Ext of
{ext,_Pos,NumExt2} when NumExt2 > 0 ->
@@ -141,38 +86,195 @@ gen_encode_constructed_imm(Erule, Typename, #type{}=D) ->
_ ->
[]
end,
- ImmBody = gen_enc_components_call(Erule, Typename, CompList, EncObj, Ext),
- ExternalImm ++ ExtImm ++ ObjSetImm ++
+ ImmBody = gen_enc_components_call(Gen, Typename, CompList, EncObj, Ext),
+ ExternalImm ++ MatchImm ++ ExtImm ++ ObjSetImm ++
asn1ct_imm:enc_append([ImmSetExt] ++ ImmOptionals ++ ImmBody).
-gen_encode_extaddgroup(CompList) ->
+external_imm(Gen, ['EXTERNAL']) ->
+ Next = asn1ct_gen:mk_var(asn1ct_name:next(val)),
+ Curr = asn1ct_gen:mk_var(asn1ct_name:curr(val)),
+ asn1ct_name:new(val),
+ F = case Gen of
+ #gen{pack=record} -> transform_to_EXTERNAL1990;
+ #gen{pack=map} -> transform_to_EXTERNAL1990_maps
+ end,
+ [{call,ext,F,[{var,Curr}],{var,Next}}];
+external_imm(_, _) ->
+ [].
+
+enc_extensions(#gen{pack=record}, Value, ExtPos, NumExt, Aligned) ->
+ asn1ct_imm:per_enc_extensions(Value, ExtPos, NumExt, Aligned);
+enc_extensions(#gen{pack=map}, Value, ExtPos, NumExt, Aligned) ->
+ Vars = [{var,lists:concat(["Input@",Pos])} ||
+ Pos <- lists:seq(ExtPos, ExtPos+NumExt-1)],
+ Undefined = atom_to_list(?MISSING_IN_MAP),
+ asn1ct_imm:per_enc_extensions_map(Value, Vars, Undefined, Aligned).
+
+enc_complist(#type{def=Def}) ->
+ case Def of
+ #'SEQUENCE'{tablecinf=TCI,components=CL0,extaddgroup=ExtAddGroup} ->
+ case ExtAddGroup of
+ undefined ->
+ {CL0,TCI};
+ _ when is_integer(ExtAddGroup) ->
+ %% This is a fake SEQUENCE representing an
+ %% ExtensionAdditionGroup. Renumber the textual
+ %% order so we get the right index of the
+ %% components.
+ CL = add_textual_order(CL0),
+ {CL,TCI}
+ end;
+ #'SET'{tablecinf=TCI,components=CL} ->
+ {CL,TCI}
+ end.
+
+enc_table(Gen, #simpletableattributes{objectsetname=ObjectSet,
+ c_name=AttrN,
+ c_index=N,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValueIndex0}, _) ->
+ {Module,ObjSetName} = ObjectSet,
+ #typedef{typespec=#'ObjectSet'{gen=MustGen}} =
+ asn1_db:dbget(Module, ObjSetName),
+ case MustGen of
+ true ->
+ ValueIndex = ValueIndex0 ++ [{N+1,'ASN1_top'}],
+ Val = make_var(val),
+ {ObjSetImm,Dst} = enc_dig_out_value(Gen, ValueIndex, Val),
+ {{AttrN,Dst},ObjSetImm};
+ false ->
+ {false,[]}
+ end;
+enc_table(_Gen, #simpletableattributes{}, _) ->
+ {false,[]};
+enc_table(_Gen, _, #type{tablecinf=TCInf}) ->
+ case TCInf of
+ [{objfun,_}|_] ->
+ %% The simpletableattributes was at an outer
+ %% level and the objfun has been passed through the
+ %% function call.
+ {{"got objfun through args",{var,"ObjFun"}},[]};
+ _ ->
+ {false,[]}
+ end.
+
+enc_optionals(Gen, Optionals) ->
+ Var = make_var(val),
+ enc_optionals_1(Gen, Optionals, Var).
+
+enc_optionals_1(#gen{pack=record}=Gen, [{Pos,DefVals}|T], Var) ->
+ {Imm0,Element} = asn1ct_imm:enc_element(Pos+1, Var),
+ Imm = asn1ct_imm:per_enc_optional(Element, DefVals),
+ [Imm0++Imm|enc_optionals_1(Gen, T, Var)];
+enc_optionals_1(#gen{pack=map}=Gen, [{Pos,DefVals0}|T], V) ->
+ Var = {var,lists:concat(["Input@",Pos])},
+ DefVals = translate_missing_value(Gen, DefVals0),
+ Imm = asn1ct_imm:per_enc_optional(Var, DefVals),
+ [Imm|enc_optionals_1(Gen, T, V)];
+enc_optionals_1(_, [], _) ->
+ [].
+
+enc_map_match(#gen{pack=record}, _Cs) ->
+ [];
+enc_map_match(#gen{pack=map}, Cs0) ->
+ Var0 = "Input",
+ Cs = enc_flatten_components(Cs0),
+ M = [[quote_atom(Name),":=",lists:concat([Var0,"@",Order])] ||
+ #'ComponentType'{prop=mandatory,name=Name,
+ textual_order=Order} <- Cs],
+ Mand = case M of
+ [] ->
+ [];
+ [_|_] ->
+ Patt = {expr,lists:flatten(["#{",lists:join(",", M),"}"])},
+ [{assign,Patt,{var,asn1ct_name:curr(val)}}]
+ end,
+
+ Os0 = [{Name,Order} ||
+ #'ComponentType'{prop=Prop,name=Name,
+ textual_order=Order} <- Cs,
+ Prop =/= mandatory],
+ {var,Val} = make_var(val),
+ F = fun({Name,Order}) ->
+ Var = lists:concat([Var0,"@",Order]),
+ P0 = ["case ",Val," of\n"
+ " #{",quote_atom(Name),":=",Var,"_0} -> ",
+ Var,"_0;\n"
+ " _ -> ",atom_to_list(?MISSING_IN_MAP),"\n"
+ "end"],
+ P = lists:flatten(P0),
+ {assign,{var,Var},P}
+ end,
+ Os = [F(O) || O <- Os0],
+ Mand ++ Os.
+
+enc_flatten_components({Root1,Ext0,Root2}=CL) ->
+ {_,Gs} = extgroup_pos_and_length(CL),
+ Ext = wrap_extensionAdditionGroups(Ext0, Gs),
+ Root1 ++ Root2 ++ [mark_optional(C) || C <- Ext];
+enc_flatten_components({Root,Ext}) ->
+ enc_flatten_components({Root,Ext,[]});
+enc_flatten_components(Cs) ->
+ Cs.
+
+gen_encode_extaddgroup(#gen{pack=record}, CompList) ->
case extgroup_pos_and_length(CompList) of
{extgrouppos,[]} ->
ok;
{extgrouppos,ExtGroupPosLenList} ->
- _ = [do_gen_encode_extaddgroup(G) || G <- ExtGroupPosLenList],
+ _ = [gen_encode_eag_record(G) ||
+ G <- ExtGroupPosLenList],
ok
- end.
+ end;
+gen_encode_extaddgroup(#gen{pack=map}, Cs0) ->
+ Cs = enc_flatten_components(Cs0),
+ gen_encode_eag_map(Cs).
+
+gen_encode_eag_map([#'ComponentType'{name=Group,typespec=Type}|Cs]) ->
+ case Type of
+ #type{def=#'SEQUENCE'{extaddgroup=G,components=GCs0}}
+ when is_integer(G) ->
+ Ns = [N || #'ComponentType'{name=N,prop=mandatory} <- GCs0],
+ test_for_mandatory(Ns, Group),
+ gen_encode_eag_map(Cs);
+ _ ->
+ gen_encode_eag_map(Cs)
+ end;
+gen_encode_eag_map([]) ->
+ ok.
+
+test_for_mandatory([Mand|_], Group) ->
+ emit([{next,val}," = case ",{curr,val}," of",nl,
+ "#{",quote_atom(Mand),":=_} -> ",
+ {curr,val},"#{",{asis,Group},"=>",{curr,val},"};",nl,
+ "#{} -> ",{curr,val},nl,
+ "end,",nl]),
+ asn1ct_name:new(val);
+test_for_mandatory([], _) ->
+ ok.
-do_gen_encode_extaddgroup({ActualGroupPos,GroupVirtualPos,GroupLen}) ->
+gen_encode_eag_record({ActualPos,VirtualPos,Len}) ->
Val = asn1ct_gen:mk_var(asn1ct_name:curr(val)),
- Elements = make_elements(GroupVirtualPos+1,
- Val,
- lists:seq(1, GroupLen)),
- Expr = any_non_value(GroupVirtualPos+1, Val, GroupLen, ""),
+ Elements = get_input_vars(Val, VirtualPos, Len),
+ Expr = any_non_value(Val, VirtualPos, Len),
emit([{next,val}," = case ",Expr," of",nl,
- "false -> setelement(",{asis,ActualGroupPos+1},", ",
+ "false -> setelement(",{asis,ActualPos+1},", ",
{curr,val},", asn1_NOVALUE);",nl,
- "true -> setelement(",{asis,ActualGroupPos+1},", ",
+ "true -> setelement(",{asis,ActualPos+1},", ",
{curr,val},", {extaddgroup,", Elements,"})",nl,
"end,",nl]),
asn1ct_name:new(val).
-any_non_value(_, _, 0, _) ->
+any_non_value(Val, Pos, N) ->
+ L = any_non_value_1(Val, Pos, N),
+ lists:join(" orelse ", L).
+
+any_non_value_1(_, _, 0) ->
[];
-any_non_value(Pos, Val, N, Sep) ->
- Sep ++ [make_element(Pos, Val)," =/= asn1_NOVALUE"] ++
- any_non_value(Pos+1, Val, N-1, [" orelse",nl]).
+any_non_value_1(Val, Pos, N) ->
+ Var = get_input_var(Val, Pos),
+ [Var ++ " =/= asn1_NOVALUE"|any_non_value_1(Val, Pos+1, N-1)].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% generate decode function for SEQUENCE and SET
@@ -306,55 +408,105 @@ gen_dec_constructed_imm(Erule, Typename, #type{}=D) ->
{DecObjInf,_,_} = ObjSetInfo,
EmitComp = gen_dec_components_call(Erule, Typename, CompList,
DecObjInf, Ext, length(Optionals)),
- EmitRest = fun({AccTerm,AccBytes}) ->
- gen_dec_constructed_imm_2(Erule, Typename,
- CompList,
- ObjSetInfo,
- AccTerm, AccBytes)
- end,
- [EmitExt,EmitOpt|EmitComp++[{safe,EmitRest}]].
+ EmitObjSets = gen_dec_objsets_fun(Erule, ObjSetInfo),
+ EmitPack = fun(_) ->
+ gen_dec_pack(Erule, Typename, CompList)
+ end,
+ RestGroup = {group,[{safe,EmitObjSets},{safe,EmitPack}]},
+ [EmitExt,EmitOpt|EmitComp++[RestGroup]].
+
+gen_dec_objsets_fun(Gen, ObjSetInfo) ->
+ fun({AccTerm,AccBytes}) ->
+ {_,_UniqueFName,ValueIndex} = ObjSetInfo,
+ case {AccTerm,AccBytes} of
+ {[],[]} ->
+ ok;
+ {_,[]} ->
+ ok;
+ {[{ObjSet,LeadingAttr,Term}],ListOfOpenTypes} ->
+ ValueMatch = value_match(Gen, ValueIndex, Term),
+ _ = [begin
+ gen_dec_open_type(Gen, ValueMatch, ObjSet,
+ LeadingAttr, T),
+ emit([com,nl])
+ end || T <- ListOfOpenTypes],
+ ok
+ end
+ end.
-gen_dec_constructed_imm_2(Erule, Typename, CompList,
- ObjSetInfo, AccTerm, AccBytes) ->
- {_,_UniqueFName,ValueIndex} = ObjSetInfo,
- case {AccTerm,AccBytes} of
- {[],[]} ->
- ok;
- {_,[]} ->
- ok;
- {[{ObjSet,LeadingAttr,Term}],ListOfOpenTypes} ->
- ValueMatch = value_match(ValueIndex, Term),
- _ = [begin
- gen_dec_open_type(Erule, ValueMatch, ObjSet,
- LeadingAttr, T),
- emit([com,nl])
- end || T <- ListOfOpenTypes],
- ok
- end,
- %% we don't return named lists any more Cnames = mkcnamelist(CompList),
- demit({"Result = "}), %dbg
- %% return value as record
- RecordName = record_name(Typename),
+gen_dec_pack(Gen, Typename, CompList) ->
case Typename of
['EXTERNAL'] ->
- emit({" OldFormat={'",RecordName,
- "'"}),
- mkvlist(asn1ct_name:all(term)),
- emit({"},",nl}),
- emit([" ASN11994Format =",nl,
- " ",
- {call,ext,transform_to_EXTERNAL1994,
- ["OldFormat"]},com,nl]),
- emit(" {ASN11994Format,");
+ dec_external(Gen, Typename);
_ ->
- emit(["{{'",RecordName,"'"]),
- %% CompList is used here because we don't want
- %% ExtensionAdditionGroups to be wrapped in SEQUENCES when
- %% we are ordering the fields according to textual order
- mkvlist(textual_order(to_encoding_order(CompList),asn1ct_name:all(term))),
- emit("},")
- end,
- emit({{curr,bytes},"}"}).
+ asn1ct_name:new(res),
+ gen_dec_do_pack(Gen, Typename, CompList),
+ emit([com,nl,
+ "{",{curr,res},",",{curr,bytes},"}"])
+ end.
+
+dec_external(#gen{pack=record}=Gen, Typename) ->
+ RecordName = list_to_atom(record_name(Gen, Typename)),
+ All = [{var,Term} || Term <- asn1ct_name:all(term)],
+ Record = [{asis,RecordName}|All],
+ emit(["OldFormat={",lists:join(",", Record),"},",nl,
+ "ASN11994Format =",nl,
+ {call,ext,transform_to_EXTERNAL1994,
+ ["OldFormat"]},com,nl,
+ "{ASN11994Format,",{curr,bytes},"}"]);
+dec_external(#gen{pack=map}, _Typename) ->
+ Vars = asn1ct_name:all(term),
+ Names = ['direct-reference','indirect-reference',
+ 'data-value-descriptor',encoding],
+ Zipped = lists:zip(Names, Vars),
+ MapInit = lists:join(",", [["'",N,"'=>",{var,V}] || {N,V} <- Zipped]),
+ emit(["OldFormat = #{",MapInit,"}",com,nl,
+ "ASN11994Format =",nl,
+ {call,ext,transform_to_EXTERNAL1994_maps,
+ ["OldFormat"]},com,nl,
+ "{ASN11994Format,",{curr,bytes},"}"]).
+
+gen_dec_do_pack(#gen{pack=record}=Gen, TypeName, CompList) ->
+ Zipped0 = zip_components(CompList, asn1ct_name:all(term)),
+ Zipped = textual_order(Zipped0),
+ RecordName = ["'",record_name(Gen, TypeName),"'"],
+ L = [RecordName|[{var,Var} || {_,Var} <- Zipped]],
+ emit([{curr,res}," = {",lists:join(",", L),"}"]);
+gen_dec_do_pack(#gen{pack=map}, _, CompList0) ->
+ CompList = enc_flatten_components(CompList0),
+ Zipped0 = zip_components(CompList, asn1ct_name:all(term)),
+ Zipped = textual_order(Zipped0),
+ PF = fun({#'ComponentType'{prop='OPTIONAL'},_}) -> false;
+ ({_,_}) -> true
+ end,
+ {Mandatory,Optional} = lists:partition(PF, Zipped),
+ L = [[{asis,Name},"=>",{var,Var}] ||
+ {#'ComponentType'{name=Name},Var} <- Mandatory],
+ emit([{curr,res}," = #{",lists:join(",", L),"}"]),
+ gen_dec_map_optional(Optional),
+ gen_dec_merge_maps(asn1ct_name:all(map)).
+
+gen_dec_map_optional([{#'ComponentType'{name=Name},Var}|T]) ->
+ asn1ct_name:new(res),
+ emit([com,nl,
+ {curr,res}," = case ",{var,Var}," of",nl,
+ " asn1_NOVALUE -> ",{prev,res},";",nl,
+ " _ -> ",{prev,res},"#{",{asis,Name},"=>",{var,Var},"}",nl,
+ "end"]),
+ gen_dec_map_optional(T);
+gen_dec_map_optional([]) ->
+ ok.
+
+gen_dec_merge_maps([M|Ms]) ->
+ asn1ct_name:new(res),
+ emit([com,nl,
+ {curr,res}," = maps:merge(",{prev,res},", ",{var,M},")"]),
+ gen_dec_merge_maps(Ms);
+gen_dec_merge_maps([]) ->
+ ok.
+
+quote_atom(A) when is_atom(A) ->
+ io_lib:format("~p", [A]).
%% record_name([TypeName]) -> RecordNameString
%% Construct a record name for the constructed type, ignoring any
@@ -362,10 +514,10 @@ gen_dec_constructed_imm_2(Erule, Typename, CompList,
%% group. Such fake sequences never appear as a top type, and their
%% name always start with "ExtAddGroup".
-record_name(Typename0) ->
+record_name(Gen, Typename0) ->
[TopType|Typename1] = lists:reverse(Typename0),
Typename = filter_ext_add_groups(Typename1, [TopType]),
- lists:concat([get_record_name_prefix(),
+ lists:concat([get_record_name_prefix(Gen),
asn1ct_gen:list2rname(Typename)]).
filter_ext_add_groups([H|T], Acc) when is_atom(H) ->
@@ -379,17 +531,26 @@ filter_ext_add_groups([H|T], Acc) ->
filter_ext_add_groups(T, [H|Acc]);
filter_ext_add_groups([], Acc) -> Acc.
-textual_order([#'ComponentType'{textual_order=undefined}|_],TermList) ->
- TermList;
-textual_order(CompList,TermList) when is_list(CompList) ->
- OrderList = [Ix||#'ComponentType'{textual_order=Ix} <- CompList],
- [Term||{_,Term}<-
- lists:sort(lists:zip(OrderList,
- lists:sublist(TermList,length(OrderList))))];
- %% sublist is just because Termlist can sometimes be longer than
- %% OrderList, which it really shouldn't
-textual_order({Root,Ext},TermList) ->
- textual_order(Root ++ Ext,TermList).
+zip_components({Root,Ext}, Vars) ->
+ zip_components({Root,Ext,[]}, Vars);
+zip_components({R1,Ext0,R2}, Vars) ->
+ Ext = [mark_optional(C) || C <- Ext0],
+ zip_components(R1++R2++Ext, Vars);
+zip_components(Cs, Vars) when is_list(Cs) ->
+ zip_components_1(Cs, Vars).
+
+zip_components_1([#'ComponentType'{}=C|Cs], [V|Vs]) ->
+ [{C,V}|zip_components_1(Cs, Vs)];
+zip_components_1([_|Cs], Vs) ->
+ zip_components_1(Cs, Vs);
+zip_components_1([], []) ->
+ [].
+
+textual_order([{#'ComponentType'{textual_order=undefined},_}|_]=L) ->
+ L;
+textual_order(L0) ->
+ L = [{Ix,P} || {#'ComponentType'{textual_order=Ix},_}=P <- L0],
+ [C || {_,C} <- lists:sort(L)].
to_textual_order({Root,Ext}) ->
{to_textual_order(Root),Ext};
@@ -458,7 +619,7 @@ dec_objset_default(N, _, _, true) ->
end]).
dec_objset_1(Erule, N, {Id,Obj}, RestFields, Typename) ->
- emit([{asis,N},"(Bytes, ",{asis,Id},") ->",nl]),
+ emit([{asis,N},"(Bytes, Id) when Id =:= ",{asis,Id}," ->",nl]),
dec_objset_2(Erule, Obj, RestFields, Typename).
dec_objset_2(Erule, Obj, RestFields0, Typename) ->
@@ -595,8 +756,7 @@ do_gen_decode_sof(Erules, Typename, SeqOrSetOf, D) ->
emit([",",nl,
{asis,F},"(",Num,", ",Buf,ObjFun,", [])"]).
-is_aligned(per) -> true;
-is_aligned(uper) -> false.
+is_aligned(#gen{erule=per,aligned=Aligned}) -> Aligned.
gen_decode_length(Constraint, Erule) ->
emit(["%% Length with constraint ",{asis,Constraint},nl]),
@@ -640,22 +800,7 @@ gen_decode_sof_components(Erule, Name, Typename, SeqOrSetOf, Cont) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% General and special help functions (not exported)
-
-mkvlist([H|T]) ->
- emit(","),
- mkvlist2([H|T]);
-mkvlist([]) ->
- true.
-mkvlist2([H,T1|T]) ->
- emit({{var,H},","}),
- mkvlist2([T1|T]);
-mkvlist2([H|T]) ->
- emit({{var,H}}),
- mkvlist2(T);
-mkvlist2([]) ->
- true.
-
+%% General and special help functions (not exported)
extensible_dec(CompList) when is_list(CompList) ->
noext;
@@ -728,28 +873,26 @@ gen_dec_optionals(Optionals) ->
{imm,Imm0,E}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Produce a list with positions (in the Value record) where
-%% there are optional components, start with 2 because first element
-%% is the record name
-
-optionals({L1,Ext,L2}) ->
- Opt1 = optionals(L1,[],2),
- ExtComps = length([C||C = #'ComponentType'{}<-Ext]),
- Opt2 = optionals(L2,[],2+length(L1)+ExtComps),
- Opt1 ++ Opt2;
-optionals({L,_Ext}) -> optionals(L,[],2);
-optionals(L) -> optionals(L,[],2).
-optionals([#'ComponentType'{prop='OPTIONAL'}|Rest], Acc, Pos) ->
- optionals(Rest, [Pos|Acc], Pos+1);
-optionals([#'ComponentType'{typespec=T,prop={'DEFAULT',Val}}|Rest],
- Acc, Pos) ->
+optionals({Root1,Ext,Root2}) ->
+ Opt1 = optionals(Root1, 1),
+ ExtComps = length([C || C = #'ComponentType'{} <- Ext]),
+ Opt2 = optionals(Root2, 1 + length(Root1) + ExtComps),
+ Opt1 ++ Opt2;
+optionals({L,_Ext}) ->
+ optionals(L, 1);
+optionals(L) ->
+ optionals(L, 1).
+
+optionals([#'ComponentType'{prop='OPTIONAL'}|Rest], Pos) ->
+ [{Pos,[asn1_NOVALUE]}|optionals(Rest, Pos+1)];
+optionals([#'ComponentType'{typespec=T,prop={'DEFAULT',Val}}|Cs], Pos) ->
Vals = def_values(T, Val),
- optionals(Rest, [{Pos,Vals}|Acc], Pos+1);
-optionals([#'ComponentType'{}|Rest], Acc, Pos) ->
- optionals(Rest, Acc, Pos+1);
-optionals([], Acc, _) ->
- lists:reverse(Acc).
+ [{Pos,Vals}|optionals(Cs, Pos+1)];
+optionals([#'ComponentType'{}|Rest], Pos) ->
+ optionals(Rest, Pos+1);
+optionals([], _) ->
+ [].
%%%%%%%%%%%%%%%%%%%%%%
%% create_optionality_table(Cs=[#'ComponentType'{textual_order=undefined}|_]) ->
@@ -779,13 +922,6 @@ get_optionality_pos(TextPos,OptTable) ->
no_num
end.
-to_encoding_order(Cs) when is_list(Cs) ->
- Cs;
-to_encoding_order(Cs = {_Root,_Ext}) ->
- Cs;
-to_encoding_order({R1,Ext,R2}) ->
- {R1++R2,Ext}.
-
add_textual_order(Cs) when is_list(Cs) ->
{NewCs,_} = add_textual_order1(Cs,1),
NewCs;
@@ -810,69 +946,77 @@ add_textual_order1(Cs,NumIn) ->
end,
NumIn,Cs).
-gen_enc_components_call(Erule,TopType,{Root,ExtList}, DynamicEnc,Ext) ->
- gen_enc_components_call(Erule,TopType,{Root,ExtList,[]}, DynamicEnc,Ext);
-gen_enc_components_call(Erule,TopType,CL={Root,ExtList,Root2}, DynamicEnc,Ext) ->
- %% The type has extensionmarker
- {Imm0,Rpos} = gen_enc_components_call1(Erule,TopType,Root++Root2,1, DynamicEnc,noext,[]),
+gen_enc_components_call(Erule, TopType, {Root,ExtList}, DynamicEnc, Ext) ->
+ gen_enc_components_call(Erule, TopType, {Root,ExtList,[]}, DynamicEnc, Ext);
+gen_enc_components_call(Erule, TopType, {R1,ExtList0,R2}=CL, DynamicEnc, Ext) ->
+ Root = R1 ++ R2,
+ Imm0 = gen_enc_components_call1(Erule, TopType, Root, DynamicEnc, noext),
ExtImm = case Ext of
{ext,_,ExtNum} when ExtNum > 0 ->
[{var,"Extensions"}];
_ ->
[]
end,
- %handle extensions
{extgrouppos,ExtGroupPosLen} = extgroup_pos_and_length(CL),
- NewExtList = wrap_extensionAdditionGroups(ExtList,ExtGroupPosLen),
- {Imm1,_} = gen_enc_components_call1(Erule,TopType,NewExtList,Rpos,DynamicEnc,Ext,[]),
+ ExtList1 = wrap_extensionAdditionGroups(ExtList0, ExtGroupPosLen),
+ ExtList = [mark_optional(C) || C <- ExtList1],
+ Imm1 = gen_enc_components_call1(Erule, TopType, ExtList, DynamicEnc, Ext),
Imm0 ++ [ExtImm|Imm1];
-gen_enc_components_call(Erule,TopType, CompList, DynamicEnc, Ext) ->
- %% The type has no extensionmarker
- {Imm,_} = gen_enc_components_call1(Erule,TopType,CompList,1,DynamicEnc,Ext,[]),
- Imm.
-
-gen_enc_components_call1(Erule,TopType,
- [C=#'ComponentType'{name=Cname,typespec=Type,prop=Prop}|Rest],
- Tpos,
- DynamicEnc, Ext, Acc) ->
-
- TermNo =
- case C#'ComponentType'.textual_order of
- undefined ->
- Tpos;
- CanonicalNum ->
- CanonicalNum
- end,
- Val = make_var(val),
- {Imm0,Element} = asn1ct_imm:enc_element(TermNo+1, Val),
- Imm1 = gen_enc_line_imm(Erule, TopType, Cname, Type, Element, DynamicEnc, Ext),
- Category = case {Prop,Ext} of
- {'OPTIONAL',_} ->
- optional;
- {{'DEFAULT',DefVal},_} ->
- {default,DefVal};
- {_,{ext,ExtPos,_}} when Tpos >= ExtPos ->
- optional;
- {_,_} ->
- mandatory
- end,
- Imm2 = case Category of
+gen_enc_components_call(Erule, TopType, CompList, DynamicEnc, Ext) ->
+ %% No extension marker.
+ gen_enc_components_call1(Erule, TopType, CompList, DynamicEnc, Ext).
+
+mark_optional(#'ComponentType'{prop=Prop0}=C) ->
+ Prop = case Prop0 of
+ mandatory -> 'OPTIONAL';
+ 'OPTIONAL'=Keep -> Keep;
+ {'DEFAULT',_}=Keep -> Keep
+ end,
+ C#'ComponentType'{prop=Prop};
+mark_optional(Other) ->
+ Other.
+
+gen_enc_components_call1(Gen, TopType, [C|Rest], DynamicEnc, Ext) ->
+ #'ComponentType'{name=Cname,typespec=Type,
+ prop=Prop,textual_order=Num} = C,
+ {Imm0,Element} = enc_fetch_field(Gen, Num, Prop),
+ Imm1 = gen_enc_line_imm(Gen, TopType, Cname, Type,
+ Element, DynamicEnc, Ext),
+ Imm2 = case Prop of
mandatory ->
Imm1;
- optional ->
- asn1ct_imm:enc_absent(Element, [asn1_NOVALUE], Imm1);
- {default,Def} ->
+ 'OPTIONAL' ->
+ enc_absent(Gen, Element, [asn1_NOVALUE], Imm1);
+ {'DEFAULT',Def} ->
DefValues = def_values(Type, Def),
- asn1ct_imm:enc_absent(Element, DefValues, Imm1)
+ enc_absent(Gen, Element, DefValues, Imm1)
end,
Imm = case Imm2 of
[] -> [];
_ -> Imm0 ++ Imm2
end,
- gen_enc_components_call1(Erule, TopType, Rest, Tpos+1, DynamicEnc, Ext, [Imm|Acc]);
-gen_enc_components_call1(_Erule,_TopType,[],Pos,_,_, Acc) ->
- ImmList = lists:reverse(Acc),
- {ImmList,Pos}.
+ [Imm|gen_enc_components_call1(Gen, TopType, Rest, DynamicEnc, Ext)];
+gen_enc_components_call1(_Gen, _TopType, [], _, _) ->
+ [].
+
+enc_absent(Gen, Var, Absent0, Imm) ->
+ Absent = translate_missing_value(Gen, Absent0),
+ asn1ct_imm:enc_absent(Var, Absent, Imm).
+
+translate_missing_value(#gen{pack=record}, Optionals) ->
+ Optionals;
+translate_missing_value(#gen{pack=map}, Optionals) ->
+ case Optionals of
+ [asn1_NOVALUE|T] -> [?MISSING_IN_MAP|T];
+ [asn1_DEFAULT|T] -> [?MISSING_IN_MAP|T];
+ {call,_,_,_} -> Optionals
+ end.
+
+enc_fetch_field(#gen{pack=record}, Num, _Prop) ->
+ Val = make_var(val),
+ asn1ct_imm:enc_element(Num+1, Val);
+enc_fetch_field(#gen{pack=map}, Num, _) ->
+ {[],{var,lists:concat(["Input@",Num])}}.
def_values(#type{def=#'Externaltypereference'{module=Mod,type=Type}}, Def) ->
#typedef{typespec=T} = asn1_db:dbget(Mod, Type),
@@ -1115,27 +1259,31 @@ gen_dec_components_call(Erule, TopType, {Root,ExtList},
DecInfObj, Ext, NumberOfOptionals) ->
gen_dec_components_call(Erule,TopType,{Root,ExtList,[]},
DecInfObj,Ext,NumberOfOptionals);
-gen_dec_components_call(Erule,TopType,CL={Root1,ExtList,Root2},
- DecInfObj,Ext,NumberOfOptionals) ->
+gen_dec_components_call(Gen, TopType, {Root1,ExtList,Root2}=CL,
+ DecInfObj, Ext, NumberOfOptionals) ->
%% The type has extensionmarker
OptTable = create_optionality_table(Root1++Root2),
Init = {ignore,fun(_) -> {[],[]} end},
{EmitRoot,Tpos} =
- gen_dec_comp_calls(Root1++Root2, Erule, TopType, OptTable,
+ gen_dec_comp_calls(Root1++Root2, Gen, TopType, OptTable,
DecInfObj, noext, NumberOfOptionals,
1, []),
- EmitGetExt = gen_dec_get_extension(Erule),
+ EmitGetExt = gen_dec_get_extension(Gen),
{extgrouppos,ExtGroupPosLen} = extgroup_pos_and_length(CL),
NewExtList = wrap_extensionAdditionGroups(ExtList, ExtGroupPosLen),
- {EmitExts,_} = gen_dec_comp_calls(NewExtList, Erule, TopType, OptTable,
+ {EmitExts,_} = gen_dec_comp_calls(NewExtList, Gen, TopType, OptTable,
DecInfObj, Ext, NumberOfOptionals,
Tpos, []),
NumExtsToSkip = ext_length(ExtList),
Finish =
fun(St) ->
emit([{next,bytes},"= "]),
- call(Erule, skipextensions,
- [{curr,bytes},NumExtsToSkip+1,"Extensions"]),
+ Mod = case Gen of
+ #gen{erule=per,aligned=false} -> uper;
+ #gen{erule=per,aligned=true} -> per
+ end,
+ asn1ct_func:call(Mod, skipextensions,
+ [{curr,bytes},NumExtsToSkip+1,"Extensions"]),
asn1ct_name:new(bytes),
St
end,
@@ -1178,7 +1326,7 @@ gen_dec_comp_calls([C|Cs], Erule, TopType, OptTable, DecInfObj,
gen_dec_comp_calls([], _, _, _, _, _, _, Tpos, Acc) ->
{lists:append(lists:reverse(Acc)),Tpos}.
-gen_dec_comp_call(Comp, Erule, TopType, Tpos, OptTable, DecInfObj,
+gen_dec_comp_call(Comp, Gen, TopType, Tpos, OptTable, DecInfObj,
Ext, NumberOfOptionals) ->
#'ComponentType'{typespec=Type,prop=Prop,textual_order=TextPos} = Comp,
Pos = case Ext of
@@ -1219,15 +1367,9 @@ gen_dec_comp_call(Comp, Erule, TopType, Tpos, OptTable, DecInfObj,
_ ->
case Type of
#type{def=#'SEQUENCE'{
- extaddgroup=Number1,
- components=ExtGroupCompList1}} when is_integer(Number1)->
- fun(St) ->
- emit(["{{_,"]),
- emit_extaddgroupTerms(term,ExtGroupCompList1),
- emit(["}"]),
- emit([",",{next,bytes},"} = "]),
- St
- end;
+ extaddgroup=GroupNum,
+ components=CompList}} when is_integer(GroupNum)->
+ dec_match_extadd_fun(Gen, CompList);
_ ->
fun(St) ->
asn1ct_name:new(term),
@@ -1237,9 +1379,9 @@ gen_dec_comp_call(Comp, Erule, TopType, Tpos, OptTable, DecInfObj,
end
end
end,
- {Pre,Post} = comp_call_pre_post(Ext, Prop, Pos, Type, TextPos,
+ {Pre,Post} = comp_call_pre_post(Gen, Ext, Prop, Pos, Type, TextPos,
OptTable, NumberOfOptionals, Ext),
- Lines = gen_dec_seq_line_imm(Erule, TopType, Comp, Tpos, DecInfObj, Ext),
+ Lines = gen_dec_seq_line_imm(Gen, TopType, Comp, Tpos, DecInfObj, Ext),
AdvBuffer = {ignore,fun(St) ->
asn1ct_name:new(bytes),
St
@@ -1247,9 +1389,24 @@ gen_dec_comp_call(Comp, Erule, TopType, Tpos, OptTable, DecInfObj,
[{group,[{safe,Comment},{safe,Preamble}] ++ Pre ++
Lines ++ Post ++ [{safe,AdvBuffer}]}].
-comp_call_pre_post(noext, mandatory, _, _, _, _, _, _) ->
+dec_match_extadd_fun(#gen{pack=record}, CompList) ->
+ fun(St) ->
+ emit(["{{_,"]),
+ emit_extaddgroupTerms(term, CompList),
+ emit(["}"]),
+ emit([",",{next,bytes},"} = "]),
+ St
+ end;
+dec_match_extadd_fun(#gen{pack=map}, _CompList) ->
+ fun(St) ->
+ asn1ct_name:new(map),
+ emit(["{",{curr,map},",",{next,bytes},"} = "]),
+ St
+ end.
+
+comp_call_pre_post(_Gen, noext, mandatory, _, _, _, _, _, _) ->
{[],[]};
-comp_call_pre_post(noext, Prop, _, Type, TextPos,
+comp_call_pre_post(_Gen, noext, Prop, _, Type, TextPos,
OptTable, NumOptionals, Ext) ->
%% OPTIONAL or DEFAULT
OptPos = get_optionality_pos(TextPos, OptTable),
@@ -1273,32 +1430,53 @@ comp_call_pre_post(noext, Prop, _, Type, TextPos,
"end"]),
St
end]};
-comp_call_pre_post({ext,_,_}, Prop, Pos, Type, _, _, _, Ext) ->
+comp_call_pre_post(Gen, {ext,_,_}, Prop, Pos, Type, _, _, _, Ext) ->
%% Extension
{[fun(St) ->
emit(["case Extensions of",nl,
" <<_:",Pos-1,",1:1,_/bitstring>> ->",nl]),
St
end],
- [fun(St) ->
- emit([";",nl,
- "_ ->",nl,
- "{"]),
- case Type of
- #type{def=#'SEQUENCE'{
- extaddgroup=Number2,
- components=ExtGroupCompList2}}
- when is_integer(Number2)->
- emit("{extAddGroup,"),
- gen_dec_extaddGroup_no_val(Ext, Type, ExtGroupCompList2),
- emit("}");
- _ ->
- gen_dec_component_no_val(Ext, Type, Prop)
- end,
- emit([",",{curr,bytes},"}",nl,
- "end"]),
- St
- end]}.
+ [extadd_group_fun(Gen, Prop, Type, Ext)]}.
+
+extadd_group_fun(#gen{pack=record}, Prop, Type, Ext) ->
+ fun(St) ->
+ emit([";",nl,
+ "_ ->",nl,
+ "{"]),
+ case Type of
+ #type{def=#'SEQUENCE'{
+ extaddgroup=Number2,
+ components=ExtGroupCompList2}}
+ when is_integer(Number2)->
+ emit("{extAddGroup,"),
+ gen_dec_extaddGroup_no_val(Ext, Type, ExtGroupCompList2),
+ emit("}");
+ _ ->
+ gen_dec_component_no_val(Ext, Type, Prop)
+ end,
+ emit([",",{curr,bytes},"}",nl,
+ "end"]),
+ St
+ end;
+extadd_group_fun(#gen{pack=map}, Prop, Type, Ext) ->
+ fun(St) ->
+ emit([";",nl,
+ "_ ->",nl,
+ "{"]),
+ case Type of
+ #type{def=#'SEQUENCE'{
+ extaddgroup=Number2,
+ components=Comp}}
+ when is_integer(Number2)->
+ dec_map_extaddgroup_no_val(Ext, Type, Comp);
+ _ ->
+ gen_dec_component_no_val(Ext, Type, Prop)
+ end,
+ emit([",",{curr,bytes},"}",nl,
+ "end"]),
+ St
+ end.
is_mandatory_predef_tab_c(noext, mandatory,
{"got objfun through args","ObjFun"}) ->
@@ -1325,7 +1503,20 @@ gen_dec_component_no_val(_, _, 'OPTIONAL') ->
emit({"asn1_NOVALUE"});
gen_dec_component_no_val({ext,_,_}, _, mandatory) ->
emit({"asn1_NOVALUE"}).
-
+
+dec_map_extaddgroup_no_val(Ext, Type, Comp) ->
+ L0 = [dec_map_extaddgroup_no_val_1(N, P, Ext, Type) ||
+ #'ComponentType'{name=N,prop=P} <- Comp],
+ L = [E || E <- L0, E =/= []],
+ emit(["#{",lists:join(",", L),"}"]).
+
+dec_map_extaddgroup_no_val_1(Name, {'DEFAULT',DefVal0}, _Ext, Type) ->
+ DefVal = asn1ct_gen:conform_value(Type, DefVal0),
+ [Name,"=>",{asis,DefVal}];
+dec_map_extaddgroup_no_val_1(_Name, 'OPTIONAL', _, _) ->
+ [];
+dec_map_extaddgroup_no_val_1(_Name, mandatory, {ext,_,_}, _) ->
+ [].
gen_dec_choice_line(Erule, TopType, Comp, Pre) ->
Imm0 = gen_dec_line_imm(Erule, TopType, Comp, false, Pre),
@@ -1461,29 +1652,29 @@ gen_dec_line_special(Erule, {typefield,_}, _TopType, Comp,
Prop}],PrevSt}
end
end;
-gen_dec_line_special(Erule, Atype, TopType, Comp, DecInfObj) ->
- case gen_dec_line_other(Erule, Atype, TopType, Comp) of
+gen_dec_line_special(Gen, Atype, TopType, Comp, DecInfObj) ->
+ case gen_dec_line_other(Gen, Atype, TopType, Comp) of
Fun when is_function(Fun, 1) ->
fun({BytesVar,PrevSt}) ->
Fun(BytesVar),
- gen_dec_line_dec_inf(Comp, DecInfObj),
+ gen_dec_line_dec_inf(Gen,Comp, DecInfObj),
{[],PrevSt}
end;
Imm0 ->
{imm,Imm0,
fun(Imm, {BytesVar,PrevSt}) ->
asn1ct_imm:dec_code_gen(Imm, BytesVar),
- gen_dec_line_dec_inf(Comp, DecInfObj),
+ gen_dec_line_dec_inf(Gen, Comp, DecInfObj),
{[],PrevSt}
end}
end.
-gen_dec_line_dec_inf(Comp, DecInfObj) ->
+gen_dec_line_dec_inf(Gen, Comp, DecInfObj) ->
#'ComponentType'{name=Cname} = Comp,
case DecInfObj of
{Cname,{_,_OSet,_UniqueFName,ValIndex}} ->
Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
- ValueMatch = value_match(ValIndex,Term),
+ ValueMatch = value_match(Gen, ValIndex,Term),
emit([",",nl,
"ObjFun = ",ValueMatch]);
_ ->
@@ -1705,20 +1896,17 @@ gen_dec_choice2(Erule, TopType, [H0|T], Pos, Sep0, Pre) ->
gen_dec_choice2(Erule, TopType, T, Pos+1, Sep, Pre);
gen_dec_choice2(_, _, [], _, _, _) -> ok.
-make_elements(I,Val,ExtCnames) ->
- make_elements(I,Val,ExtCnames,[]).
+get_input_vars(Val, I, N) ->
+ L = get_input_vars_1(Val, I, N),
+ lists:join(",", L).
-make_elements(I,Val,[_ExtCname],Acc)-> % the last one, no comma needed
- Element = make_element(I, Val),
- make_elements(I+1,Val,[],[Element|Acc]);
-make_elements(I,Val,[_ExtCname|Rest],Acc)->
- Element = make_element(I, Val),
- make_elements(I+1,Val,Rest,[", ",Element|Acc]);
-make_elements(_I,_,[],Acc) ->
- lists:reverse(Acc).
+get_input_vars_1(_Val, _I, 0) ->
+ [];
+get_input_vars_1(Val, I, N) ->
+ [get_input_var(Val, I)|get_input_vars_1(Val, I+1, N-1)].
-make_element(I, Val) ->
- lists:flatten(io_lib:format("element(~w, ~s)", [I,Val])).
+get_input_var(Val, I) ->
+ lists:flatten(io_lib:format("element(~w, ~s)", [I+1,Val])).
emit_extaddgroupTerms(VarSeries,[_]) ->
asn1ct_name:new(VarSeries),
@@ -1735,62 +1923,66 @@ flat_complist({Rl1,El,Rl2}) -> Rl1 ++ El ++ Rl2;
flat_complist({Rl,El}) -> Rl ++ El;
flat_complist(CompList) -> CompList.
-%%wrap_compList({Root1,Ext,Root2}) ->
-%% {Root1,wrap_extensionAdditionGroups(Ext),Root2};
-%%wrap_compList({Root1,Ext}) ->
-%% {Root1,wrap_extensionAdditionGroups(Ext)};
-%%wrap_compList(CompList) ->
-%% CompList.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Will convert all componentTypes following 'ExtensionAdditionGroup'
+%% Convert all componentTypes following 'ExtensionAdditionGroup'
%% up to the matching 'ExtensionAdditionGroupEnd' into one componentType
-%% of type SEQUENCE with the componentTypes as components
+%% of type SEQUENCE with the componentTypes as components.
%%
-wrap_extensionAdditionGroups(ExtCompList,ExtGroupPosLen) ->
- wrap_extensionAdditionGroups(ExtCompList,ExtGroupPosLen,[],0,0).
+wrap_extensionAdditionGroups(ExtCompList, ExtGroupPosLen) ->
+ wrap_eags(ExtCompList, ExtGroupPosLen, 0, 0).
-wrap_extensionAdditionGroups([{'ExtensionAdditionGroup',_Number}|Rest],
- [{ActualPos,_,_}|ExtGroupPosLenRest],Acc,_ExtAddGroupDiff,ExtGroupNum) ->
- {ExtGroupCompList,['ExtensionAdditionGroupEnd'|Rest2]} =
+wrap_eags([{'ExtensionAdditionGroup',_Number}|T0],
+ [{ActualPos,_,_}|Gs], _ExtAddGroupDiff, ExtGroupNum) ->
+ {ExtGroupCompList,['ExtensionAdditionGroupEnd'|T]} =
lists:splitwith(fun(#'ComponentType'{}) -> true;
(_) -> false
- end,
- Rest),
- wrap_extensionAdditionGroups(Rest2,ExtGroupPosLenRest,
- [#'ComponentType'{
- name=list_to_atom("ExtAddGroup"++
- integer_to_list(ExtGroupNum+1)),
- typespec=#type{def=#'SEQUENCE'{
- extaddgroup=ExtGroupNum+1,
- components=ExtGroupCompList}},
- textual_order = ActualPos,
- prop='OPTIONAL'}|Acc],length(ExtGroupCompList)-1,
- ExtGroupNum+1);
-wrap_extensionAdditionGroups([H=#'ComponentType'{textual_order=Tord}|T],
- ExtAddGrpLenPos,Acc,ExtAddGroupDiff,ExtGroupNum) when is_integer(Tord) ->
- wrap_extensionAdditionGroups(T,ExtAddGrpLenPos,[H#'ComponentType'{
- textual_order=Tord - ExtAddGroupDiff}|Acc],ExtAddGroupDiff,ExtGroupNum);
-wrap_extensionAdditionGroups([H|T],ExtAddGrpLenPos,Acc,ExtAddGroupDiff,ExtGroupNum) ->
- wrap_extensionAdditionGroups(T,ExtAddGrpLenPos,[H|Acc],ExtAddGroupDiff,ExtGroupNum);
-wrap_extensionAdditionGroups([],_,Acc,_,_) ->
- lists:reverse(Acc).
-
-value_match(Index,Value) when is_atom(Value) ->
- value_match(Index,atom_to_list(Value));
-value_match([],Value) ->
+ end, T0),
+ Name = list_to_atom(lists:concat(["ExtAddGroup",ExtGroupNum+1])),
+ Seq = #type{def=#'SEQUENCE'{extaddgroup=ExtGroupNum+1,
+ components=ExtGroupCompList}},
+ Comp = #'ComponentType'{name=Name,
+ typespec=Seq,
+ textual_order=ActualPos,
+ prop='OPTIONAL'},
+ [Comp|wrap_eags(T, Gs, length(ExtGroupCompList)-1, ExtGroupNum+1)];
+wrap_eags([#'ComponentType'{textual_order=Tord}=H|T],
+ ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum)
+ when is_integer(Tord) ->
+ Comp = H#'ComponentType'{textual_order=Tord - ExtAddGroupDiff},
+ [Comp|wrap_eags(T, ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum)];
+wrap_eags([H|T], ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum) ->
+ [H|wrap_eags(T, ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum)];
+wrap_eags([], _, _, _) ->
+ [].
+
+value_match(#gen{pack=record}, VIs, Value) ->
+ value_match_rec(VIs, Value);
+value_match(#gen{pack=map}, VIs, Value) ->
+ value_match_map(VIs, Value).
+
+value_match_rec([], Value) ->
Value;
-value_match([{VI,_}|VIs],Value) ->
- value_match1(Value,VIs,lists:concat(["element(",VI,","]),1).
-value_match1(Value,[],Acc,Depth) ->
- Acc ++ Value ++ lists:concat(lists:duplicate(Depth,")"));
-value_match1(Value,[{VI,_}|VIs],Acc,Depth) ->
- value_match1(Value,VIs,Acc++lists:concat(["element(",VI,","]),Depth+1).
-
-enc_dig_out_value([], Value) ->
+value_match_rec([{VI,_}|VIs], Value0) ->
+ Value = value_match_rec(VIs, Value0),
+ lists:concat(["element(",VI,", ",Value,")"]).
+
+value_match_map([], Value) ->
+ Value;
+value_match_map([{_,Name}|VIs], Value0) ->
+ Value = value_match_map(VIs, Value0),
+ lists:concat(["maps:get(",Name,", ",Value,")"]).
+
+enc_dig_out_value(_Gen, [], Value) ->
{[],Value};
-enc_dig_out_value([{N,_}|T], Value) ->
- {Imm0,Dst0} = enc_dig_out_value(T, Value),
+enc_dig_out_value(#gen{pack=record}=Gen, [{N,_}|T], Value) ->
+ {Imm0,Dst0} = enc_dig_out_value(Gen, T, Value),
{Imm,Dst} = asn1ct_imm:enc_element(N, Dst0),
+ {Imm0++Imm,Dst};
+enc_dig_out_value(#gen{pack=map}, [{N,'ASN1_top'}], _Value) ->
+ {[],{var,lists:concat(["Input@",N-1])}};
+enc_dig_out_value(#gen{pack=map}=Gen, [{_,Name}|T], Value) ->
+ {Imm0,Dst0} = enc_dig_out_value(Gen, T, Value),
+ {Imm,Dst} = asn1ct_imm:enc_maps_get(Name, Dst0),
{Imm0++Imm,Dst}.
make_var(Base) ->
diff --git a/lib/asn1/src/asn1ct_eval_ext.funcs b/lib/asn1/src/asn1ct_eval_ext.funcs
index 5761901f89..01c67e7b5a 100644
--- a/lib/asn1/src/asn1ct_eval_ext.funcs
+++ b/lib/asn1/src/asn1ct_eval_ext.funcs
@@ -1 +1,2 @@
{ext,transform_to_EXTERNAL1994,1}.
+{ext,transform_to_EXTERNAL1994_maps,1}.
diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl
index bfaffa13bf..4fa830d7d9 100644
--- a/lib/asn1/src/asn1ct_gen.erl
+++ b/lib/asn1/src/asn1ct_gen.erl
@@ -34,10 +34,10 @@
insert_once/2,
ct_gen_module/1,
index2suffix/1,
- get_record_name_prefix/0,
+ get_record_name_prefix/1,
conform_value/2,
named_bitstring_value/2]).
--export([pgen/5,
+-export([pgen/4,
mk_var/1,
un_hyphen_var/1]).
-export([gen_encode_constructed/4,
@@ -45,23 +45,20 @@
-define(SUPPRESSION_FUNC, 'dialyzer-suppressions').
+
%% pgen(Outfile, Erules, Module, TypeOrVal, Options)
-%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module
-%% .hrl file is only generated if necessary
-%% Erules = per | ber
-%% Module = atom()
-%% TypeOrVal = {TypeList,ValueList}
-%% TypeList = ValueList = [atom()]
-%% Options = [Options] from asn1ct:compile()
-
-pgen(OutFile,Erules,Module,TypeOrVal,Options) ->
- pgen_module(OutFile,Erules,Module,TypeOrVal,Options,true).
-
-
-pgen_module(OutFile,Erules,Module,
- TypeOrVal = {Types,_Values,_Ptypes,_Classes,_Objects,_ObjectSets},
- Options,Indent) ->
- N2nConvEnums = [CName|| {n2n,CName} <- get(encoding_options)],
+%% Generate Erlang module (.erl) and (.hrl) file corresponding to
+%% an ASN.1 module. The .hrl file is only generated if necessary.
+
+-spec pgen(Outfile, Gen, Module, Contents) -> 'ok' when
+ Outfile :: any(),
+ Gen :: #gen{},
+ Module :: module(),
+ Contents :: tuple().
+
+pgen(OutFile, #gen{options=Options}=Gen, Module, Contents) ->
+ {Types,_Values,_Ptypes,_Classes,_Objects,_ObjectSets} = Contents,
+ N2nConvEnums = [CName|| {n2n,CName} <- Options],
case N2nConvEnums -- Types of
[] ->
ok;
@@ -70,29 +67,28 @@ pgen_module(OutFile,Erules,Module,
UnmatchedTypes})
end,
put(outfile,OutFile),
- HrlGenerated = pgen_hrl(Erules,Module,TypeOrVal,Options,Indent),
+ put(currmod, Module),
+ HrlGenerated = pgen_hrl(Gen, Module, Contents),
asn1ct_name:start(),
ErlFile = lists:concat([OutFile,".erl"]),
_ = open_output_file(ErlFile),
asn1ct_func:start_link(),
- gen_head(Erules,Module,HrlGenerated),
- pgen_exports(Erules,Module,TypeOrVal),
- pgen_dispatcher(Erules,Module,TypeOrVal),
+ gen_head(Gen, Module, HrlGenerated),
+ pgen_exports(Gen, Module, Contents),
+ pgen_dispatcher(Gen, Contents),
pgen_info(),
- pgen_typeorval(Erules,Module,N2nConvEnums,TypeOrVal),
- pgen_partial_incomplete_decode(Erules),
-% gen_vars(asn1_db:mod_to_vars(Module)),
-% gen_tag_table(AllTypes),
+ pgen_typeorval(Gen, Module, N2nConvEnums, Contents),
+ pgen_partial_incomplete_decode(Gen),
emit([nl,
"%%%",nl,
"%%% Run-time functions.",nl,
"%%%",nl]),
- dialyzer_suppressions(Erules),
+ dialyzer_suppressions(Gen),
Fd = get(gen_file_out),
asn1ct_func:generate(Fd),
close_output_file(),
_ = erase(outfile),
- asn1ct:verbose("--~p--~n",[{generated,ErlFile}],Options).
+ asn1ct:verbose("--~p--~n", [{generated,ErlFile}], Gen).
dialyzer_suppressions(Erules) ->
emit([nl,
@@ -181,10 +177,10 @@ pgen_objectsets(Rtmod,Erules,Module,[H|T]) ->
Rtmod:gen_objectset_code(Erules,TypeDef),
pgen_objectsets(Rtmod,Erules,Module,T).
-pgen_partial_decode(Rtmod,Erule,Module) when Erule == ber ->
- pgen_partial_inc_dec(Rtmod,Erule,Module),
- pgen_partial_dec(Rtmod,Erule,Module);
-pgen_partial_decode(_,_,_) ->
+pgen_partial_decode(Rtmod, #gen{erule=ber}=Gen, Module) ->
+ pgen_partial_inc_dec(Rtmod, Gen, Module),
+ pgen_partial_dec(Rtmod, Gen, Module);
+pgen_partial_decode(_, _, _) ->
ok.
pgen_partial_inc_dec(Rtmod,Erules,Module) ->
@@ -225,7 +221,7 @@ pgen_partial_inc_dec1(Rtmod,Erules,Module,[P|Ps]) ->
pgen_partial_inc_dec1(_,_,_,[]) ->
ok.
-gen_partial_inc_dec_refed_funcs(Rtmod,Erule) when Erule == ber ->
+gen_partial_inc_dec_refed_funcs(Rtmod, #gen{erule=ber}=Gen) ->
case asn1ct:next_refed_func() of
[] ->
ok;
@@ -233,19 +229,17 @@ gen_partial_inc_dec_refed_funcs(Rtmod,Erule) when Erule == ber ->
TypeDef = asn1_db:dbget(M,Name),
asn1ct:update_gen_state(namelist,Pattern),
asn1ct:set_current_sindex(Sindex),
- Rtmod:gen_inc_decode(Erule,TypeDef),
- gen_dec_part_inner_constr(Rtmod,Erule,TypeDef,[Name]),
- gen_partial_inc_dec_refed_funcs(Rtmod,Erule);
+ Rtmod:gen_inc_decode(Gen, TypeDef),
+ gen_dec_part_inner_constr(Rtmod, Gen, TypeDef, [Name]),
+ gen_partial_inc_dec_refed_funcs(Rtmod, Gen);
{Name,Sindex,Pattern,Type} ->
TypeDef=#typedef{name=asn1ct_gen:list2name(Name),typespec=Type},
asn1ct:update_gen_state(namelist,Pattern),
asn1ct:set_current_sindex(Sindex),
- Rtmod:gen_inc_decode(Erule,TypeDef),
- gen_dec_part_inner_constr(Rtmod,Erule,TypeDef,Name),
- gen_partial_inc_dec_refed_funcs(Rtmod,Erule)
- end;
-gen_partial_inc_dec_refed_funcs(_,_) ->
- ok.
+ Rtmod:gen_inc_decode(Gen, TypeDef),
+ gen_dec_part_inner_constr(Rtmod, Gen, TypeDef, Name),
+ gen_partial_inc_dec_refed_funcs(Rtmod, Gen)
+ end.
pgen_partial_dec(_Rtmod,Erules,_Module) ->
Type_pattern = asn1ct:get_gen_state_field(type_pattern),
@@ -254,16 +248,16 @@ pgen_partial_dec(_Rtmod,Erules,_Module) ->
pgen_partial_types(Erules,Type_pattern),
ok.
-pgen_partial_types(Erules,Type_pattern) ->
- % until this functionality works on all back-ends
- Options = get(encoding_options),
- case lists:member(asn1config,Options) of
+pgen_partial_types(#gen{options=Options}=Gen, TypePattern) ->
+ %% until this functionality works on all back-ends
+ case lists:member(asn1config, Options) of
true ->
- pgen_partial_types1(Erules,Type_pattern);
- _ -> ok
+ pgen_partial_types1(Gen, TypePattern);
+ false ->
+ ok
end.
-
+
pgen_partial_types1(Erules,[{FuncName,[TopType|RestTypes]}|Rest]) ->
% emit([FuncName,"(Bytes) ->",nl]),
CurrMod = get(currmod),
@@ -441,7 +435,8 @@ pgen_partial_incomplete_decode(Erule) ->
_ ->
ok
end.
-pgen_partial_incomplete_decode1(ber) ->
+
+pgen_partial_incomplete_decode1(#gen{erule=ber}) ->
case asn1ct:read_config_data(partial_incomplete_decode) of
undefined ->
ok;
@@ -451,7 +446,7 @@ pgen_partial_incomplete_decode1(ber) ->
GeneratedFs= asn1ct:get_gen_state_field(gen_refed_funcs),
% io:format("GeneratedFs :~n~p~n",[GeneratedFs]),
gen_part_decode_funcs(GeneratedFs,0);
-pgen_partial_incomplete_decode1(_) -> ok.
+pgen_partial_incomplete_decode1(#gen{}) -> ok.
emit_partial_incomplete_decode({FuncName,TopType,Pattern}) ->
TypePattern = asn1ct:get_gen_state_field(inc_type_pattern),
@@ -654,29 +649,30 @@ gen_decode_constructed(Erules,Typename,InnerType,D) when is_record(D,typedef) ->
gen_decode_constructed(Erules,Typename,InnerType,D#typedef.typespec).
-pgen_exports(Erules,_Module,{Types,Values,_,_,Objects,ObjectSets}) ->
- emit(["-export([encoding_rule/0,bit_string_format/0,",nl,
+pgen_exports(#gen{options=Options}=Gen, _Module, Contents) ->
+ {Types,Values,_,_,Objects,ObjectSets} = Contents,
+ emit(["-export([encoding_rule/0,maps/0,bit_string_format/0,",nl,
" legacy_erlang_types/0]).",nl]),
emit(["-export([",{asis,?SUPPRESSION_FUNC},"/1]).",nl]),
case Types of
[] -> ok;
_ ->
emit({"-export([",nl}),
- case Erules of
- ber ->
+ case Gen of
+ #gen{erule=ber} ->
gen_exports1(Types,"enc_",2);
_ ->
gen_exports1(Types,"enc_",1)
end,
emit({"-export([",nl}),
- case Erules of
- ber ->
+ case Gen of
+ #gen{erule=ber} ->
gen_exports1(Types, "dec_", 2);
_ ->
gen_exports1(Types, "dec_", 1)
end
end,
- case [X || {n2n,X} <- get(encoding_options)] of
+ case [X || {n2n,X} <- Options] of
[] -> ok;
A2nNames ->
emit({"-export([",nl}),
@@ -693,10 +689,10 @@ pgen_exports(Erules,_Module,{Types,Values,_,_,Objects,ObjectSets}) ->
case Objects of
[] -> ok;
_ ->
- case erule(Erules) of
- per ->
- ok;
- ber ->
+ case Gen of
+ #gen{erule=per} ->
+ ok;
+ #gen{erule=ber} ->
emit({"-export([",nl}),
gen_exports1(Objects,"enc_",3),
emit({"-export([",nl}),
@@ -706,10 +702,10 @@ pgen_exports(Erules,_Module,{Types,Values,_,_,Objects,ObjectSets}) ->
case ObjectSets of
[] -> ok;
_ ->
- case erule(Erules) of
- per ->
- ok;
- ber ->
+ case Gen of
+ #gen{erule=per} ->
+ ok;
+ #gen{erule=ber} ->
emit({"-export([",nl}),
gen_exports1(ObjectSets, "getenc_",1),
emit({"-export([",nl}),
@@ -735,16 +731,17 @@ gen_partial_inc_decode_exports() ->
{_,undefined} ->
ok;
{Data,_} ->
- gen_partial_inc_decode_exports(Data),
+ gen_partial_inc_decode_exports0(Data),
emit(["-export([decode_part/2]).",nl])
end.
-gen_partial_inc_decode_exports([]) ->
+
+gen_partial_inc_decode_exports0([]) ->
ok;
-gen_partial_inc_decode_exports([{Name,_,_}|Rest]) ->
+gen_partial_inc_decode_exports0([{Name,_,_}|Rest]) ->
emit(["-export([",Name,"/1"]),
gen_partial_inc_decode_exports1(Rest);
-gen_partial_inc_decode_exports([_|Rest]) ->
- gen_partial_inc_decode_exports(Rest).
+gen_partial_inc_decode_exports0([_|Rest]) ->
+ gen_partial_inc_decode_exports0(Rest).
gen_partial_inc_decode_exports1([]) ->
emit(["]).",nl]);
@@ -773,27 +770,27 @@ gen_selected_decode_exports1([{FuncName,_}|Rest]) ->
emit([",",nl," ",FuncName,"/1"]),
gen_selected_decode_exports1(Rest).
-pgen_dispatcher(Erules,_Module,{[],_Values,_,_,_Objects,_ObjectSets}) ->
+pgen_dispatcher(Erules, {[],_Values,_,_,_Objects,_ObjectSets}) ->
gen_info_functions(Erules);
-pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) ->
+pgen_dispatcher(Gen, {Types,_Values,_,_,_Objects,_ObjectSets}) ->
emit(["-export([encode/2,decode/2]).",nl,nl]),
- gen_info_functions(Erules),
+ gen_info_functions(Gen),
- Options = get(encoding_options),
+ Options = Gen#gen.options,
NoFinalPadding = lists:member(no_final_padding, Options),
NoOkWrapper = proplists:get_bool(no_ok_wrapper, Options),
- Call = case Erules of
- per ->
- asn1ct_func:need({Erules,complete,1}),
+ Call = case Gen of
+ #gen{erule=per,aligned=true} ->
+ asn1ct_func:need({per,complete,1}),
"complete(encode_disp(Type, Data))";
- ber ->
+ #gen{erule=ber} ->
"iolist_to_binary(element(1, encode_disp(Type, Data)))";
- uper when NoFinalPadding == true ->
- asn1ct_func:need({Erules,complete_NFP,1}),
+ #gen{erule=per,aligned=false} when NoFinalPadding ->
+ asn1ct_func:need({uper,complete_NFP,1}),
"complete_NFP(encode_disp(Type, Data))";
- uper ->
- asn1ct_func:need({Erules,complete,1}),
+ #gen{erule=per,aligned=false} ->
+ asn1ct_func:need({uper,complete,1}),
"complete(encode_disp(Type, Data))"
end,
@@ -809,36 +806,36 @@ pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) ->
end,
emit([nl,nl]),
- Return_rest = proplists:get_bool(undec_rest, Options),
- Data = case {Erules,Return_rest} of
- {ber,true} -> "Data0";
- _ -> "Data"
+ ReturnRest = proplists:get_bool(undec_rest, Gen#gen.options),
+ Data = case Gen#gen.erule =:= ber andalso ReturnRest of
+ true -> "Data0";
+ false -> "Data"
end,
emit(["decode(Type,",Data,") ->",nl]),
DecWrap =
- case {Erules,Return_rest} of
- {ber,false} ->
+ case {Gen,ReturnRest} of
+ {#gen{erule=ber},false} ->
asn1ct_func:need({ber,ber_decode_nif,1}),
"element(1, ber_decode_nif(Data))";
- {ber,true} ->
+ {#gen{erule=ber},true} ->
asn1ct_func:need({ber,ber_decode_nif,1}),
emit(["{Data,Rest} = ber_decode_nif(Data0),",nl]),
"Data";
- _ ->
+ {_,_} ->
"Data"
end,
emit([case NoOkWrapper of
false -> "try";
true -> "case"
end, " decode_disp(Type, ",DecWrap,") of",nl]),
- case erule(Erules) of
- ber ->
+ case Gen of
+ #gen{erule=ber} ->
emit([" Result ->",nl]);
- per ->
+ #gen{erule=per} ->
emit([" {Result,Rest} ->",nl])
end,
- case Return_rest of
+ case ReturnRest of
false -> result_line(NoOkWrapper, ["Result"]);
true -> result_line(NoOkWrapper, ["Result","Rest"])
end,
@@ -849,14 +846,14 @@ pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) ->
emit([nl,"end.",nl,nl])
end,
- gen_decode_partial_incomplete(Erules),
+ gen_decode_partial_incomplete(Gen),
- case Erules of
- ber ->
+ case Gen of
+ #gen{erule=ber} ->
gen_dispatcher(Types,"encode_disp","enc_",""),
gen_dispatcher(Types,"decode_disp","dec_",""),
gen_partial_inc_dispatcher();
- _PerOrPer_bin ->
+ #gen{} ->
gen_dispatcher(Types,"encode_disp","enc_",""),
gen_dispatcher(Types,"decode_disp","dec_","")
end,
@@ -885,15 +882,26 @@ try_catch() ->
" end",nl,
"end."].
-gen_info_functions(Erules) ->
+gen_info_functions(Gen) ->
+ Erule = case Gen of
+ #gen{erule=ber} -> ber;
+ #gen{erule=per,aligned=false} -> uper;
+ #gen{erule=per,aligned=true} -> per
+ end,
+ Maps = case Gen of
+ #gen{pack=record} -> false;
+ #gen{pack=map} -> true
+ end,
emit(["encoding_rule() -> ",
- {asis,Erules},".",nl,nl,
+ {asis,Erule},".",nl,nl,
+ "maps() -> ",
+ {asis,Maps},".",nl,nl,
"bit_string_format() -> ",
{asis,asn1ct:get_bit_string_format()},".",nl,nl,
"legacy_erlang_types() -> ",
{asis,asn1ct:use_legacy_types()},".",nl,nl]).
-gen_decode_partial_incomplete(ber) ->
+gen_decode_partial_incomplete(#gen{erule=ber}) ->
case {asn1ct:read_config_data(partial_incomplete_decode),
asn1ct:get_gen_state_field(inc_type_pattern)} of
{undefined,_} ->
@@ -931,7 +939,7 @@ gen_decode_partial_incomplete(ber) ->
EmitCaseClauses(),
emit([".",nl,nl])
end;
-gen_decode_partial_incomplete(_Erule) ->
+gen_decode_partial_incomplete(#gen{}) ->
ok.
gen_partial_inc_dispatcher() ->
@@ -1092,22 +1100,21 @@ open_output_file(F) ->
close_output_file() ->
ok = file:close(erase(gen_file_out)).
-pgen_hrl(Erules,Module,TypeOrVal,Options,_Indent) ->
- put(currmod,Module),
- {Types,Values,Ptypes,_,_,_} = TypeOrVal,
+pgen_hrl(#gen{pack=record}=Gen, Module, Contents) ->
+ {Types,Values,Ptypes,_,_,_} = Contents,
Ret =
- case pgen_hrltypes(Erules,Module,Ptypes++Types,0) of
+ case pgen_hrltypes(Gen, Module, Ptypes++Types, 0) of
0 ->
case Values of
[] ->
0;
_ ->
- open_hrl(get(outfile),get(currmod)),
- pgen_macros(Erules,Module,Values),
+ open_hrl(get(outfile), Module),
+ pgen_macros(Gen, Module, Values),
1
end;
X ->
- pgen_macros(Erules,Module,Values),
+ pgen_macros(Gen, Module, Values),
X
end,
case Ret of
@@ -1119,62 +1126,61 @@ pgen_hrl(Erules,Module,TypeOrVal,Options,_Indent) ->
close_output_file(),
asn1ct:verbose("--~p--~n",
[{generated,lists:concat([get(outfile),".hrl"])}],
- Options),
+ Gen),
Y
- end.
+ end;
+pgen_hrl(#gen{pack=map}, _, _) ->
+ 0.
pgen_macros(_,_,[]) ->
true;
-pgen_macros(Erules,Module,[H|T]) ->
- Valuedef = asn1_db:dbget(Module,H),
- gen_macro(Valuedef),
- pgen_macros(Erules,Module,T).
+pgen_macros(Gen, Module, [H|T]) ->
+ Valuedef = asn1_db:dbget(Module, H),
+ gen_macro(Gen, Valuedef),
+ pgen_macros(Gen, Module, T).
pgen_hrltypes(_,_,[],NumRecords) ->
NumRecords;
-pgen_hrltypes(Erules,Module,[H|T],NumRecords) ->
-% io:format("records = ~p~n",NumRecords),
- Typedef = asn1_db:dbget(Module,H),
- AddNumRecords = gen_record(Typedef,NumRecords),
- pgen_hrltypes(Erules,Module,T,NumRecords+AddNumRecords).
+pgen_hrltypes(Gen, Module, [H|T], NumRecords) ->
+ Typedef = asn1_db:dbget(Module, H),
+ AddNumRecords = gen_record(Gen, Typedef, NumRecords),
+ pgen_hrltypes(Gen, Module, T, NumRecords+AddNumRecords).
%% Generates a macro for value Value defined in the ASN.1 module
-gen_macro(Value) when is_record(Value,valuedef) ->
- Prefix = get_macro_name_prefix(),
- emit({"-define('",Prefix,Value#valuedef.name,"', ",
- {asis,Value#valuedef.value},").",nl}).
+gen_macro(Gen, #valuedef{name=Name,value=Value}) ->
+ Prefix = get_macro_name_prefix(Gen),
+ emit(["-define('",Prefix,Name,"', ",{asis,Value},").",nl]).
%% Generate record functions **************
%% Generates an Erlang record for each named and unnamed SEQUENCE and SET in the ASN.1
%% module. If no SEQUENCE or SET is found there is no .hrl file generated
-gen_record(Tdef,NumRecords) when is_record(Tdef,typedef) ->
+gen_record(Gen, #typedef{}=Tdef, NumRecords) ->
Name = [Tdef#typedef.name],
Type = Tdef#typedef.typespec,
- gen_record(type,Name,Type,NumRecords);
-
-gen_record(Tdef,NumRecords) when is_record(Tdef,ptypedef) ->
+ gen_record(Gen, type, Name, Type, NumRecords);
+gen_record(Gen, #ptypedef{}=Tdef, NumRecords) ->
Name = [Tdef#ptypedef.name],
Type = Tdef#ptypedef.typespec,
- gen_record(ptype,Name,Type,NumRecords).
-
-gen_record(TorPtype,Name,[#'ComponentType'{name=Cname,typespec=Type}|T],Num) ->
- Num2 = gen_record(TorPtype,[Cname|Name],Type,Num),
- gen_record(TorPtype,Name,T,Num2);
-gen_record(TorPtype,Name,{Clist1,Clist2},Num)
+ gen_record(Gen, ptype, Name, Type, NumRecords).
+
+gen_record(Gen, TorPtype, Name,
+ [#'ComponentType'{name=Cname,typespec=Type}|T], Num) ->
+ Num2 = gen_record(Gen, TorPtype, [Cname|Name], Type, Num),
+ gen_record(Gen, TorPtype, Name, T, Num2);
+gen_record(Gen, TorPtype, Name, {Clist1,Clist2}, Num)
when is_list(Clist1), is_list(Clist2) ->
- gen_record(TorPtype,Name,Clist1++Clist2,Num);
-gen_record(TorPtype,Name,{Clist1,EClist,Clist2},Num)
+ gen_record(Gen, TorPtype, Name, Clist1++Clist2, Num);
+gen_record(Gen, TorPtype, Name, {Clist1,EClist,Clist2}, Num)
when is_list(Clist1), is_list(EClist), is_list(Clist2) ->
- gen_record(TorPtype,Name,Clist1++EClist++Clist2,Num);
-gen_record(TorPtype,Name,[_|T],Num) -> % skip EXTENSIONMARK
- gen_record(TorPtype,Name,T,Num);
-gen_record(_TorPtype,_Name,[],Num) ->
+ gen_record(Gen, TorPtype, Name, Clist1++EClist++Clist2, Num);
+gen_record(Gen, TorPtype, Name, [_|T], Num) -> % skip EXTENSIONMARK
+ gen_record(Gen, TorPtype, Name, T, Num);
+gen_record(_Gen, _TorPtype, _Name, [], Num) ->
Num;
-
-gen_record(TorPtype,Name,Type,Num) when is_record(Type,type) ->
+gen_record(Gen, TorPtype, Name, #type{}=Type, Num) ->
Def = Type#type.def,
Rec = case Def of
Seq when is_record(Seq,'SEQUENCE') ->
@@ -1209,7 +1215,7 @@ gen_record(TorPtype,Name,Type,Num) when is_record(Type,type) ->
0 -> open_hrl(get(outfile),get(currmod));
_ -> true
end,
- Prefix = get_record_name_prefix(),
+ Prefix = get_record_name_prefix(Gen),
emit({"-record('",Prefix,list2name(Name),"',{",nl}),
RootList = case CompList of
_ when is_list(CompList) ->
@@ -1260,33 +1266,28 @@ gen_record(TorPtype,Name,Type,Num) when is_record(Type,type) ->
emit({"}).",nl,nl}),
CompList
end,
- gen_record(TorPtype,Name,NewCompList,Num+1);
+ gen_record(Gen, TorPtype, Name, NewCompList, Num+1);
{inner,{'CHOICE', CompList}} ->
- gen_record(TorPtype,Name,CompList,Num);
+ gen_record(Gen, TorPtype, Name, CompList, Num);
{NewName,{_, CompList}} ->
- gen_record(TorPtype,NewName,CompList,Num)
+ gen_record(Gen, TorPtype, NewName, CompList, Num)
end;
-gen_record(_,_,_,NumRecords) -> % skip CLASS etc for now.
+gen_record(_, _, _, _, NumRecords) -> % skip CLASS etc for now.
NumRecords.
-
-gen_head(Erules,Mod,Hrl) ->
- Options = get(encoding_options),
- case Erules of
- per ->
- emit(["%% Generated by the Erlang ASN.1 PER-"
- "compiler version, utilizing bit-syntax:",
- asn1ct:vsn(),nl]);
- ber ->
- emit(["%% Generated by the Erlang ASN.1 BER_V2-"
- "compiler version, utilizing bit-syntax:",
- asn1ct:vsn(),nl]);
- uper ->
- emit(["%% Generated by the Erlang ASN.1 UNALIGNED"
- " PER-compiler version, utilizing bit-syntax:",
- asn1ct:vsn(),nl])
- end,
- emit({"%% Purpose: encoder and decoder to the types in mod ",Mod,nl,nl}),
- emit({"-module('",Mod,"').",nl}),
+
+gen_head(#gen{options=Options}=Gen, Mod, Hrl) ->
+ Name = case Gen of
+ #gen{erule=per,aligned=false} ->
+ "PER (unaligned)";
+ #gen{erule=per,aligned=true} ->
+ "PER (aligned)";
+ #gen{erule=ber} ->
+ "BER"
+ end,
+ emit(["%% Generated by the Erlang ASN.1 ",Name,
+ "compiler. Version:",asn1ct:vsn(),nl,
+ "%% Purpose: encoder and decoder of the types in ",Mod,nl,nl,
+ "-module('",Mod,"').",nl]),
put(currmod,Mod),
emit({"-compile(nowarn_unused_vars).",nl}),
emit({"-dialyzer(no_improper_lists).",nl}),
@@ -1297,7 +1298,7 @@ gen_head(Erules,Mod,Hrl) ->
emit(["-asn1_info([{vsn,'",asn1ct:vsn(),"'},",nl,
" {module,'",Mod,"'},",nl,
" {options,",io_lib:format("~p",[Options]),"}]).",nl,nl]).
-
+
gen_hrlhead(Mod) ->
emit({"%% Generated by the Erlang ASN.1 compiler version:",asn1ct:vsn(),nl}),
@@ -1585,27 +1586,19 @@ constructed_suffix('SEQUENCE OF',_) ->
constructed_suffix('SET OF',_) ->
'SETOF'.
-erule(ber) -> ber;
-erule(per) -> per;
-erule(uper) -> per.
-
index2suffix(0) ->
"";
index2suffix(N) ->
lists:concat(["_",N]).
-ct_gen_module(ber) ->
+ct_gen_module(#gen{erule=ber}) ->
asn1ct_gen_ber_bin_v2;
-ct_gen_module(per) ->
- asn1ct_gen_per;
-ct_gen_module(uper) ->
+ct_gen_module(#gen{erule=per}) ->
asn1ct_gen_per.
-ct_constructed_module(ber) ->
+ct_constructed_module(#gen{erule=ber}) ->
asn1ct_constructed_ber_bin_v2;
-ct_constructed_module(per) ->
- asn1ct_constructed_per;
-ct_constructed_module(uper) ->
+ct_constructed_module(#gen{erule=per}) ->
asn1ct_constructed_per.
get_constraint(C,Key) ->
@@ -1617,19 +1610,9 @@ get_constraint(C,Key) ->
{value,Cnstr} ->
Cnstr
end.
-
-get_record_name_prefix() ->
- case lists:keysearch(record_name_prefix,1,get(encoding_options)) of
- false ->
- "";
- {value,{_,Prefix}} ->
- Prefix
- end.
-get_macro_name_prefix() ->
- case lists:keysearch(macro_name_prefix,1,get(encoding_options)) of
- false ->
- "";
- {value,{_,Prefix}} ->
- Prefix
- end.
+get_record_name_prefix(#gen{rec_prefix=Prefix}) ->
+ Prefix.
+
+get_macro_name_prefix(#gen{macro_prefix=Prefix}) ->
+ Prefix.
diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
index b884d14b0d..6c6d4193f3 100644
--- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
@@ -1200,11 +1200,13 @@ gen_objset_enc(Erules, ObjSetName, UniqueName,
{no_mod,no_name} ->
gen_inlined_enc_funs(Fields, ClFields, ObjSetName, Val, NthObj);
{CurrMod,Name} ->
- emit(["'getenc_",ObjSetName,"'(",{asis,Val},") ->",nl,
+ emit(["'getenc_",ObjSetName,"'(Id) when Id =:= ",
+ {asis,Val}," ->",nl,
" fun 'enc_",Name,"'/3;",nl]),
{[],NthObj};
{ModuleName,Name} ->
- emit(["'getenc_",ObjSetName,"'(",{asis,Val},") ->",nl]),
+ emit(["'getenc_",ObjSetName,"'(Id) when Id =:= ",
+ {asis,Val}," ->",nl]),
emit_ext_fun(enc,ModuleName,Name),
emit([";",nl]),
{[],NthObj};
@@ -1382,11 +1384,13 @@ gen_objset_dec(Erules, ObjSName, UniqueName, [{ObjName,Val,Fields}|T],
{no_mod,no_name} ->
gen_inlined_dec_funs(Fields,ClFields,ObjSName,Val,NthObj);
{CurrMod,Name} ->
- emit(["'getdec_",ObjSName,"'(",{asis,Val},") ->",nl,
+ emit(["'getdec_",ObjSName,"'(Id) when Id =:= ",
+ {asis,Val}," ->",nl,
" fun 'dec_",Name,"'/3;", nl]),
NthObj;
{ModuleName,Name} ->
- emit(["'getdec_",ObjSName,"'(",{asis,Val},") ->",nl]),
+ emit(["'getdec_",ObjSName,"'(Id) when Id =:= ",
+ {asis,Val}," ->",nl]),
emit_ext_fun(dec,ModuleName,Name),
emit([";",nl]),
NthObj;
diff --git a/lib/asn1/src/asn1ct_gen_check.erl b/lib/asn1/src/asn1ct_gen_check.erl
index abe77dd0cb..ccc62a3ce3 100644
--- a/lib/asn1/src/asn1ct_gen_check.erl
+++ b/lib/asn1/src/asn1ct_gen_check.erl
@@ -21,45 +21,51 @@
%%
-module(asn1ct_gen_check).
--export([emit/3]).
+-export([emit/4]).
-import(asn1ct_gen, [emit/1]).
-include("asn1_records.hrl").
-emit(Type, Default, Value) ->
+emit(Gen, Type, Default, Value) ->
Key = {Type,Default},
- Gen = fun(Fd, Name) ->
- file:write(Fd, gen(Name, Type, Default))
- end,
+ DoGen = fun(Fd, Name) ->
+ file:write(Fd, gen(Gen, Name, Type, Default))
+ end,
emit(" case "),
- asn1ct_func:call_gen("is_default_", Key, Gen, [Value]),
+ asn1ct_func:call_gen("is_default_", Key, DoGen, [Value]),
emit([" of",nl,
"true -> {[],0};",nl,
"false ->",nl]).
-gen(Name, #type{def=T}, Default) ->
+gen(#gen{pack=Pack}=Gen, Name, #type{def=T}, Default) ->
+ DefMarker = case Pack of
+ record -> "asn1_DEFAULT";
+ map -> atom_to_list(?MISSING_IN_MAP)
+ end,
NameStr = atom_to_list(Name),
- [NameStr,"(asn1_DEFAULT) ->\n",
- "true;\n"|case do_gen(T, Default) of
- {literal,Literal} ->
- [NameStr,"(",term2str(Literal),") ->\n","true;\n",
- NameStr,"(_) ->\n","false.\n\n"];
- {exception,Func,Args} ->
- [NameStr,"(Value) ->\n",
- "try ",Func,"(Value",arg2str(Args),") of\n",
- "_ -> true\n"
- "catch throw:false -> false\n"
- "end.\n\n"]
- end].
+ [NameStr,"(",DefMarker,") ->\n",
+ "true;\n"|
+ case do_gen(Gen, T, Default) of
+ {literal,Literal} ->
+ [NameStr,"(Def) when Def =:= ",term2str(Literal)," ->\n",
+ "true;\n",
+ NameStr,"(_) ->\n","false.\n\n"];
+ {exception,Func,Args} ->
+ [NameStr,"(Value) ->\n",
+ "try ",Func,"(Value",arg2str(Args),") of\n",
+ "_ -> true\n"
+ "catch throw:false -> false\n"
+ "end.\n\n"]
+ end].
-do_gen(_, asn1_NOVALUE) ->
+do_gen(_Gen, _, asn1_NOVALUE) ->
{literal,asn1_NOVALUE};
-do_gen(#'Externaltypereference'{module=M,type=T}, Default) ->
+do_gen(Gen, #'Externaltypereference'{module=M,type=T}, Default) ->
#typedef{typespec=#type{def=Td}} = asn1_db:dbget(M, T),
- do_gen(Td, Default);
-do_gen('BOOLEAN', Default) ->
+ do_gen(Gen, Td, Default);
+do_gen(_Gen, 'BOOLEAN', Default) ->
{literal,Default};
-do_gen({'BIT STRING',[]}, Default) ->
+do_gen(_Gen, {'BIT STRING',[]}, Default) ->
true = is_bitstring(Default), %Assertion.
case asn1ct:use_legacy_types() of
false ->
@@ -67,17 +73,17 @@ do_gen({'BIT STRING',[]}, Default) ->
true ->
{exception,need(check_legacy_bitstring, 2),[Default]}
end;
-do_gen({'BIT STRING',[_|_]=NBL}, Default) ->
+do_gen(_Gen, {'BIT STRING',[_|_]=NBL}, Default) ->
do_named_bitstring(NBL, Default);
-do_gen({'ENUMERATED',_}, Default) ->
+do_gen(_Gen, {'ENUMERATED',_}, Default) ->
{literal,Default};
-do_gen('INTEGER', Default) ->
+do_gen(_Gen, 'INTEGER', Default) ->
{literal,Default};
-do_gen({'INTEGER',NNL}, Default) ->
+do_gen(_Gen, {'INTEGER',NNL}, Default) ->
{exception,need(check_int, 3),[Default,NNL]};
-do_gen('NULL', Default) ->
+do_gen(_Gen, 'NULL', Default) ->
{literal,Default};
-do_gen('OCTET STRING', Default) ->
+do_gen(_Gen, 'OCTET STRING', Default) ->
true = is_binary(Default), %Assertion.
case asn1ct:use_legacy_types() of
false ->
@@ -85,34 +91,34 @@ do_gen('OCTET STRING', Default) ->
true ->
{exception,need(check_octetstring, 2),[Default]}
end;
-do_gen('OBJECT IDENTIFIER', Default0) ->
+do_gen(_Gen, 'OBJECT IDENTIFIER', Default0) ->
Default = pre_process_oid(Default0),
{exception,need(check_objectidentifier, 2),[Default]};
-do_gen({'CHOICE',Cs}, Default) ->
+do_gen(Gen, {'CHOICE',Cs}, Default) ->
{Tag,Value} = Default,
[Type] = [Type || #'ComponentType'{name=T,typespec=Type} <- Cs,
T =:= Tag],
- case do_gen(Type#type.def, Value) of
+ case do_gen(Gen, Type#type.def, Value) of
{literal,Lit} ->
{literal,{Tag,Lit}};
{exception,Func0,Args} ->
Key = {Tag,Func0,Args},
- Gen = fun(Fd, Name) ->
- S = gen_choice(Name, Tag, Func0, Args),
- ok = file:write(Fd, S)
+ DoGen = fun(Fd, Name) ->
+ S = gen_choice(Name, Tag, Func0, Args),
+ ok = file:write(Fd, S)
end,
- Func = asn1ct_func:call_gen("is_default_choice", Key, Gen),
+ Func = asn1ct_func:call_gen("is_default_choice", Key, DoGen),
{exception,atom_to_list(Func),[]}
end;
-do_gen(#'SEQUENCE'{components=Cs}, Default) ->
- do_seq_set(Cs, Default);
-do_gen({'SEQUENCE OF',Type}, Default) ->
- do_sof(Type, Default);
-do_gen(#'SET'{components=Cs}, Default) ->
- do_seq_set(Cs, Default);
-do_gen({'SET OF',Type}, Default) ->
- do_sof(Type, Default);
-do_gen(Type, Default) ->
+do_gen(Gen, #'SEQUENCE'{components=Cs}, Default) ->
+ do_seq_set(Gen, Cs, Default);
+do_gen(Gen, {'SEQUENCE OF',Type}, Default) ->
+ do_sof(Gen, Type, Default);
+do_gen(Gen, #'SET'{components=Cs}, Default) ->
+ do_seq_set(Gen, Cs, Default);
+do_gen(Gen, {'SET OF',Type}, Default) ->
+ do_sof(Gen, Type, Default);
+do_gen(_Gen, Type, Default) ->
case asn1ct_gen:unify_if_string(Type) of
restrictedstring ->
{exception,need(check_restrictedstring, 2),[Default]};
@@ -136,39 +142,58 @@ do_named_bitstring(_, Default) when is_bitstring(Default) ->
end,
{exception,need(Func, 3),[Default,bit_size(Default)]}.
-do_seq_set(Cs0, Default) ->
+do_seq_set(#gen{pack=record}=Gen, Cs0, Default) ->
Tag = element(1, Default),
Cs1 = [T || #'ComponentType'{typespec=T} <- Cs0],
- Cs = components(Cs1, tl(tuple_to_list(Default))),
+ Cs = components(Gen, Cs1, tl(tuple_to_list(Default))),
case are_all_literals(Cs) of
true ->
Literal = list_to_tuple([Tag|[L || {literal,L} <- Cs]]),
{literal,Literal};
false ->
Key = {Cs,Default},
- Gen = fun(Fd, Name) ->
- S = gen_components(Name, Tag, Cs),
- ok = file:write(Fd, S)
- end,
- Func = asn1ct_func:call_gen("is_default_cs_", Key, Gen),
+ DoGen = fun(Fd, Name) ->
+ S = gen_components(Name, Tag, Cs),
+ ok = file:write(Fd, S)
+ end,
+ Func = asn1ct_func:call_gen("is_default_cs_", Key, DoGen),
+ {exception,atom_to_list(Func),[]}
+ end;
+do_seq_set(#gen{pack=map}=Gen, Cs0, Default) ->
+ Cs1 = [{N,T} || #'ComponentType'{name=N,typespec=T} <- Cs0],
+ Cs = map_components(Gen, Cs1, Default),
+ AllLiterals = lists:all(fun({_,{literal,_}}) -> true;
+ ({_,_}) -> false
+ end, Cs),
+ case AllLiterals of
+ true ->
+ L = [{Name,Lit} || {Name,{literal,Lit}} <- Cs],
+ {literal,maps:from_list(L)};
+ false ->
+ Key = {Cs,Default},
+ DoGen = fun(Fd, Name) ->
+ S = gen_map_components(Name, Cs),
+ ok = file:write(Fd, S)
+ end,
+ Func = asn1ct_func:call_gen("is_default_cs_", Key, DoGen),
{exception,atom_to_list(Func),[]}
end.
-do_sof(Type, Default0) ->
+do_sof(Gen, Type, Default0) ->
Default = lists:sort(Default0),
Cs0 = lists:duplicate(length(Default), Type),
- Cs = components(Cs0, Default),
+ Cs = components(Gen, Cs0, Default),
case are_all_literals(Cs) of
true ->
Literal = [Lit || {literal,Lit} <- Cs],
{exception,need(check_literal_sof, 2),[Literal]};
false ->
Key = Cs,
- Gen = fun(Fd, Name) ->
- S = gen_sof(Name, Cs),
- ok = file:write(Fd, S)
+ DoGen = fun(Fd, Name) ->
+ S = gen_sof(Name, Cs),
+ ok = file:write(Fd, S)
end,
- Func = asn1ct_func:call_gen("is_default_sof", Key, Gen),
+ Func = asn1ct_func:call_gen("is_default_sof", Key, DoGen),
{exception,atom_to_list(Func),[]}
end.
@@ -199,6 +224,39 @@ gen_cs_2([], _) ->
"throw(false)\n"
"end.\n"].
+gen_map_components(Name, Cs) ->
+ [atom_to_list(Name),"(Value) ->\n",
+ "case Value of\n",
+ "#{"|gen_map_cs_1(Cs, 1, "", [])].
+
+gen_map_cs_1([{Name,{literal,Lit}}|T], I, Sep, Acc) ->
+ Var = "E"++integer_to_list(I),
+ G = Var ++ " =:= " ++ term2str(Lit),
+ [Sep,term2str(Name),":=",Var|
+ gen_map_cs_1(T, I+1, ",\n", [{guard,G}|Acc])];
+gen_map_cs_1([{Name,Exc}|T], I, Sep, Acc) ->
+ Var = "E"++integer_to_list(I),
+ [Sep,term2str(Name),":=",Var|
+ gen_map_cs_1(T, I+1, ",\n", [{exc,{Var,Exc}}|Acc])];
+gen_map_cs_1([], _, _, Acc) ->
+ G = lists:join(", ", [S || {guard,S} <- Acc]),
+ Exc = [E || {exc,E} <- Acc],
+ Body = gen_map_cs_2(Exc, ""),
+ case G of
+ [] ->
+ ["} ->\n"|Body];
+ [_|_] ->
+ ["} when ",G," ->\n"|Body]
+ end.
+
+gen_map_cs_2([{Var,{exception,Func,Args}}|T], Sep) ->
+ [Sep,Func,"(",Var,arg2str(Args),")"|gen_map_cs_2(T, ",\n")];
+gen_map_cs_2([], _) ->
+ [";\n",
+ "_ ->\n"
+ "throw(false)\n"
+ "end.\n"].
+
gen_sof(Name, Cs) ->
[atom_to_list(Name),"(Value) ->\n",
"case length(Value) of\n",
@@ -221,9 +279,18 @@ gen_sof_1([{exception,Func,Args}|Cs], I) ->
gen_sof_1([], _) ->
".\n".
-components([#type{def=Def}|Ts], [V|Vs]) ->
- [do_gen(Def, V)|components(Ts, Vs)];
-components([], []) -> [].
+components(Gen, [#type{def=Def}|Ts], [V|Vs]) ->
+ [do_gen(Gen, Def, V)|components(Gen, Ts, Vs)];
+components(_Gen, [], []) -> [].
+
+map_components(Gen, [{Name,#type{def=Def}}|Ts], Value) ->
+ case maps:find(Name, Value) of
+ {ok,V} ->
+ [{Name,do_gen(Gen, Def, V)}|map_components(Gen, Ts, Value)];
+ error ->
+ map_components(Gen, Ts, Value)
+ end;
+map_components(_Gen, [], _Value) -> [].
gen_choice(Name, Tag, Func, Args) ->
NameStr = atom_to_list(Name),
diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl
index aa7223904e..9671a566bf 100644
--- a/lib/asn1/src/asn1ct_gen_per.erl
+++ b/lib/asn1/src/asn1ct_gen_per.erl
@@ -113,11 +113,7 @@ gen_encode_prim(Erules, D) ->
Value = {var,atom_to_list(asn1ct_gen:mk_var(asn1ct_name:curr(val)))},
gen_encode_prim(Erules, D, Value).
-gen_encode_prim(Erules, #type{}=D, Value) ->
- Aligned = case Erules of
- uper -> false;
- per -> true
- end,
+gen_encode_prim(#gen{erule=per,aligned=Aligned}, #type{}=D, Value) ->
Imm = gen_encode_prim_imm(Value, D, Aligned),
asn1ct_imm:enc_cg(Imm, Aligned).
@@ -284,11 +280,7 @@ gen_dec_external(Ext, BytesVar) ->
_ -> [{asis,Mod},":"]
end,{asis,dec_func(Type)},"(",BytesVar,")"]).
-gen_dec_imm(Erule, #type{def=Name,constraint=C}) ->
- Aligned = case Erule of
- uper -> false;
- per -> true
- end,
+gen_dec_imm(#gen{erule=per,aligned=Aligned}, #type{def=Name,constraint=C}) ->
gen_dec_imm_1(Name, C, Aligned).
gen_dec_imm_1('ASN1_OPEN_TYPE', Constraint, Aligned) ->
diff --git a/lib/asn1/src/asn1ct_imm.erl b/lib/asn1/src/asn1ct_imm.erl
index 8b96242c56..2ab848652e 100644
--- a/lib/asn1/src/asn1ct_imm.erl
+++ b/lib/asn1/src/asn1ct_imm.erl
@@ -37,9 +37,11 @@
per_enc_open_type/2,
per_enc_restricted_string/3,
per_enc_small_number/2]).
--export([per_enc_extension_bit/2,per_enc_extensions/4,per_enc_optional/3]).
+-export([per_enc_extension_bit/2,per_enc_extensions/4,
+ per_enc_extensions_map/4,
+ per_enc_optional/2]).
-export([per_enc_sof/5]).
--export([enc_absent/3,enc_append/1,enc_element/2]).
+-export([enc_absent/3,enc_append/1,enc_element/2,enc_maps_get/2]).
-export([enc_cg/2]).
-export([optimize_alignment/1,optimize_alignment/2,
dec_slim_cg/2,dec_code_gen/2]).
@@ -349,27 +351,32 @@ per_enc_extensions(Val0, Pos0, NumBits, Aligned) when NumBits > 0 ->
['_'|Length ++ PutBits]]}],
{var,"Extensions"}}].
-per_enc_optional(Val0, {Pos,DefVals}, _Aligned) when is_integer(Pos),
- is_list(DefVals) ->
- {B,Val} = enc_element(Pos, Val0),
+per_enc_extensions_map(Val0, Vars, Undefined, Aligned) ->
+ NumBits = length(Vars),
+ {B,[_Val,Bitmap]} = mk_vars(Val0, [bitmap]),
+ Length = per_enc_small_length(NumBits, Aligned),
+ PutBits = case NumBits of
+ 1 -> [{put_bits,1,1,[1]}];
+ _ -> [{put_bits,Bitmap,NumBits,[1]}]
+ end,
+ BitmapExpr = extensions_bitmap(Vars, Undefined),
+ B++[{assign,Bitmap,BitmapExpr},
+ {list,[{'cond',[[{eq,Bitmap,0}],
+ ['_'|Length ++ PutBits]]}],
+ {var,"Extensions"}}].
+
+per_enc_optional(Val, DefVals) when is_list(DefVals) ->
Zero = {put_bits,0,1,[1]},
One = {put_bits,1,1,[1]},
- B++[{'cond',
- [[{eq,Val,DefVal},Zero] || DefVal <- DefVals] ++ [['_',One]]}];
-per_enc_optional(Val0, {Pos,{call,M,F,A}}, _Aligned) when is_integer(Pos) ->
- {B,Val} = enc_element(Pos, Val0),
+ [{'cond',
+ [[{eq,Val,DefVal},Zero] || DefVal <- DefVals] ++ [['_',One]]}];
+per_enc_optional(Val, {call,M,F,A}) ->
{[],[[],Tmp]} = mk_vars([], [tmp]),
Zero = {put_bits,0,1,[1]},
One = {put_bits,1,1,[1]},
- B++[{call,M,F,[Val|A],Tmp},
- {'cond',
- [[{eq,Tmp,true},Zero],['_',One]]}];
-per_enc_optional(Val0, Pos, _Aligned) when is_integer(Pos) ->
- {B,Val} = enc_element(Pos, Val0),
- Zero = {put_bits,0,1,[1]},
- One = {put_bits,1,1,[1]},
- B++[{'cond',[[{eq,Val,asn1_NOVALUE},Zero],
- ['_',One]]}].
+ [{call,M,F,[Val|A],Tmp},
+ {'cond',
+ [[{eq,Tmp,true},Zero],['_',One]]}].
per_enc_sof(Val0, Constraint, ElementVar, ElementImm, Aligned) ->
{B,[Val,Len]} = mk_vars(Val0, [len]),
@@ -423,6 +430,13 @@ enc_element(N, Val0) ->
{[],[Val,Dst]} = mk_vars(Val0, [element]),
{[{call,erlang,element,[N,Val],Dst}],Dst}.
+enc_maps_get(N, Val0) ->
+ {[],[Val,Dst0]} = mk_vars(Val0, [element]),
+ {var,Dst} = Dst0,
+ DstExpr = {expr,lists:concat(["#{",N,":=",Dst,"}"])},
+ {var,SrcVar} = Val,
+ {[{assign,DstExpr,SrcVar}],Dst0}.
+
enc_cg(Imm0, false) ->
Imm1 = enc_cse(Imm0),
Imm2 = enc_pre_cg(Imm1),
@@ -1240,6 +1254,20 @@ enc_length(Len, {Lb,Ub}, Aligned) when is_integer(Lb) ->
enc_length(Len, Sv, _Aligned) when is_integer(Sv) ->
[{'cond',[[{eq,Len,Sv}]]}].
+extensions_bitmap(Vs, Undefined) ->
+ Highest = 1 bsl (length(Vs)-1),
+ Cs = extensions_bitmap_1(Vs, Undefined, Highest),
+ lists:flatten(lists:join(" bor ", Cs)).
+
+extensions_bitmap_1([{var,V}|Vs], Undefined, Power) ->
+ S = ["case ",V," of\n",
+ " ",Undefined," -> 0;\n"
+ " _ -> ",integer_to_list(Power),"\n"
+ "end"],
+ [S|extensions_bitmap_1(Vs, Undefined, Power bsr 1)];
+extensions_bitmap_1([], _, _) ->
+ [].
+
put_bits_binary(Bin, _Unit, Aligned) when is_binary(Bin) ->
Sz = byte_size(Bin),
<<Int:Sz/unit:8>> = Bin,
diff --git a/lib/asn1/src/asn1ct_value.erl b/lib/asn1/src/asn1ct_value.erl
index b3d41dd9f3..8bd99d995b 100644
--- a/lib/asn1/src/asn1ct_value.erl
+++ b/lib/asn1/src/asn1ct_value.erl
@@ -64,7 +64,11 @@ from_type(M,Typename,Type) when is_record(Type,type) ->
end;
{constructed,bif} when Typename == ['EXTERNAL'] ->
Val=from_type_constructed(M,Typename,InnerType,Type),
- asn1ct_eval_ext:transform_to_EXTERNAL1994(Val);
+ T = case M:maps() of
+ false -> transform_to_EXTERNAL1994;
+ true -> transform_to_EXTERNAL1994_maps
+ end,
+ asn1ct_eval_ext:T(Val);
{constructed,bif} ->
from_type_constructed(M,Typename,InnerType,Type)
end;
@@ -118,11 +122,13 @@ get_sequence(M,Typename,Type) ->
#'SEQUENCE'{components=Cl} -> {'SEQUENCE',Cl};
#'SET'{components=Cl} -> {'SET',to_textual_order(Cl)}
end,
- case get_components(M,Typename,CompList) of
- [] ->
- {list_to_atom(asn1ct_gen:list2rname(Typename))};
- C ->
- list_to_tuple([list_to_atom(asn1ct_gen:list2rname(Typename))|C])
+ Cs = get_components(M, Typename, CompList),
+ case M:maps() of
+ false ->
+ RecordTag = list_to_atom(asn1ct_gen:list2rname(Typename)),
+ list_to_tuple([RecordTag|[Val || {_,Val} <- Cs]]);
+ true ->
+ maps:from_list(Cs)
end.
get_components(M,Typename,{Root,Ext}) ->
@@ -130,9 +136,9 @@ get_components(M,Typename,{Root,Ext}) ->
%% Should enhance this *** HERE *** with proper handling of extensions
-get_components(M,Typename,[H|T]) ->
- [from_type(M,Typename,H)|
- get_components(M,Typename,T)];
+get_components(M, Typename, [H|T]) ->
+ #'ComponentType'{name=Name} = H,
+ [{Name,from_type(M, Typename, H)}|get_components(M, Typename, T)];
get_components(_,_,[]) ->
[].
diff --git a/lib/asn1/src/asn1rtt_ext.erl b/lib/asn1/src/asn1rtt_ext.erl
index 3bf01823db..161b2db691 100644
--- a/lib/asn1/src/asn1rtt_ext.erl
+++ b/lib/asn1/src/asn1rtt_ext.erl
@@ -19,7 +19,8 @@
%%
-module(asn1rtt_ext).
--export([transform_to_EXTERNAL1990/1,transform_to_EXTERNAL1994/1]).
+-export([transform_to_EXTERNAL1990/1,transform_to_EXTERNAL1990_maps/1,
+ transform_to_EXTERNAL1994/1,transform_to_EXTERNAL1994_maps/1]).
transform_to_EXTERNAL1990({_,_,_,_}=Val) ->
transform_to_EXTERNAL1990(tuple_to_list(Val), []);
@@ -51,6 +52,30 @@ transform_to_EXTERNAL1990([Data_value], Acc)
list_to_tuple(lists:reverse([{'octet-aligned',Data_value}|Acc])).
+transform_to_EXTERNAL1990_maps(#{identification:=Id,'data-value':=Value}=V) ->
+ M0 = case Id of
+ {syntax,DRef} ->
+ #{'direct-reference'=>DRef};
+ {'presentation-context-id',IndRef} ->
+ #{'indirect-reference'=>IndRef};
+ {'context-negotiation',
+ #{'presentation-context-id':=IndRef,
+ 'transfer-syntax':=DRef}} ->
+ #{'direct-reference'=>DRef,
+ 'indirect-reference'=>IndRef}
+ end,
+ M = case V of
+ #{'data-value-descriptor':=Dvd} ->
+ M0#{'data-value-descriptor'=>Dvd};
+ #{} ->
+ M0
+ end,
+ M#{encoding=>{'octet-aligned',Value}};
+transform_to_EXTERNAL1990_maps(#{encoding:=_}=V) ->
+ %% Already in the EXTERNAL 1990 format.
+ V.
+
+
transform_to_EXTERNAL1994({'EXTERNAL',DRef,IndRef,Data_v_desc,Encoding}=V) ->
Identification =
case {DRef,IndRef} of
@@ -71,3 +96,38 @@ transform_to_EXTERNAL1994({'EXTERNAL',DRef,IndRef,Data_v_desc,Encoding}=V) ->
%% information.
V
end.
+
+transform_to_EXTERNAL1994_maps(V0) ->
+ Identification =
+ case V0 of
+ #{'direct-reference':=DRef,
+ 'indirect-reference':=asn1_NOVALUE} ->
+ {syntax,DRef};
+ #{'direct-reference':=asn1_NOVALUE,
+ 'indirect-reference':=IndRef} ->
+ {'presentation-context-id',IndRef};
+ #{'direct-reference':=DRef,
+ 'indirect-reference':=IndRef} ->
+ {'context-negotiation',
+ #{'transfer-syntax'=>DRef,
+ 'presentation-context-id'=>IndRef}}
+ end,
+ case V0 of
+ #{encoding:={'octet-aligned',Val}}
+ when is_list(Val); is_binary(Val) ->
+ %% Transform to the EXTERNAL 1994 definition.
+ V = #{identification=>Identification,
+ 'data-value'=>Val},
+ case V0 of
+ #{'data-value-descriptor':=asn1_NOVALUE} ->
+ V;
+ #{'data-value-descriptor':=Dvd} ->
+ V#{'data-value-descriptor'=>Dvd}
+ end;
+ _ ->
+ %% Keep the EXTERNAL 1990 definition to avoid losing
+ %% information.
+ V = [{K,V} || {K,V} <- maps:to_list(V0),
+ V =/= asn1_NOVALUE],
+ maps:from_list(V)
+ end.
diff --git a/lib/asn1/test/Makefile b/lib/asn1/test/Makefile
index 40575e8a2f..d346bb9e12 100644
--- a/lib/asn1/test/Makefile
+++ b/lib/asn1/test/Makefile
@@ -82,6 +82,7 @@ MODULES= \
testInfObjExtract \
testParameterizedInfObj \
testFragmented \
+ testMaps \
testMergeCompile \
testMultipleLevels \
testDeepTConstr \
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index b6430134ab..6769a38b12 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -21,6 +21,9 @@
-module(asn1_SUITE).
+%% Suppress compilation of an addititional module compiled for maps.
+-define(NO_MAPS_MODULE, asn1_test_lib_no_maps).
+
-define(only_ber(Func),
if Rule =:= ber -> Func;
true -> ok
@@ -102,6 +105,7 @@ groups() ->
testMultipleLevels,
testOpt,
testSeqDefault,
+ testMaps,
% Uses 'External'
{group, [], [testExternal,
testSeqExtension]},
@@ -176,8 +180,11 @@ groups() ->
{performance, [],
[testTimer_ber,
+ testTimer_ber_maps,
testTimer_per,
- testTimer_uper]}].
+ testTimer_per_maps,
+ testTimer_uper,
+ testTimer_uper_maps]}].
%%------------------------------------------------------------------------------
%% Init/end
@@ -441,6 +448,16 @@ testDEFAULT(Config, Rule, Opts) ->
testDef:main(Rule),
testSeqSetDefaultVal:main(Rule, Opts).
+testMaps(Config) ->
+ test(Config, fun testMaps/3,
+ [{ber,[maps,no_ok_wrapper]},
+ {ber,[maps,der,no_ok_wrapper]},
+ {per,[maps,no_ok_wrapper]},
+ {uper,[maps,no_ok_wrapper]}]).
+testMaps(Config, Rule, Opts) ->
+ asn1_test_lib:compile_all(['Maps'], Config, [Rule|Opts]),
+ testMaps:main(Rule).
+
testOpt(Config) -> test(Config, fun testOpt/3).
testOpt(Config, Rule, Opts) ->
asn1_test_lib:compile("Opt", Config, [Rule|Opts]),
@@ -614,12 +631,12 @@ parse(Config) ->
[asn1_test_lib:compile(M, Config, [abs]) || M <- test_modules()].
per(Config) ->
- test(Config, fun per/3, [per,uper]).
+ test(Config, fun per/3, [per,uper,{per,[maps]},{uper,[maps]}]).
per(Config, Rule, Opts) ->
[module_test(M, Config, Rule, Opts) || M <- per_modules()].
ber_other(Config) ->
- test(Config, fun ber_other/3, [ber]).
+ test(Config, fun ber_other/3, [ber,{ber,[maps]}]).
ber_other(Config, Rule, Opts) ->
[module_test(M, Config, Rule, Opts) || M <- ber_modules()].
@@ -628,7 +645,7 @@ der(Config) ->
asn1_test_lib:compile_all(ber_modules(), Config, [der]).
module_test(M0, Config, Rule, Opts) ->
- asn1_test_lib:compile(M0, Config, [Rule|Opts]),
+ asn1_test_lib:compile(M0, Config, [Rule,?NO_MAPS_MODULE|Opts]),
case list_to_atom(M0) of
'LDAP' ->
%% Because of the recursive definition of 'Filter' in
@@ -995,7 +1012,9 @@ testS1AP(Config, Rule, Opts) ->
testRfcs() ->
[{timetrap,{minutes,90}}].
-testRfcs(Config) -> test(Config, fun testRfcs/3, [{ber,[der]}]).
+testRfcs(Config) -> test(Config, fun testRfcs/3,
+ [{ber,[der,?NO_MAPS_MODULE]},
+ {ber,[der,maps]}]).
testRfcs(Config, Rule, Opts) ->
case erlang:system_info(system_architecture) of
"sparc-sun-solaris2.10" ->
@@ -1010,7 +1029,8 @@ test_compile_options(Config) ->
ok = test_compile_options:path(Config),
ok = test_compile_options:noobj(Config),
ok = test_compile_options:record_name_prefix(Config),
- ok = test_compile_options:verbose(Config).
+ ok = test_compile_options:verbose(Config),
+ ok = test_compile_options:maps(Config).
testDoubleEllipses(Config) -> test(Config, fun testDoubleEllipses/3).
testDoubleEllipses(Config, Rule, Opts) ->
@@ -1069,7 +1089,7 @@ test_x691(Config, Rule, Opts) ->
ok.
ticket_6143(Config) ->
- ok = test_compile_options:ticket_6143(Config).
+ asn1_test_lib:compile("AA1", Config, [?NO_MAPS_MODULE]).
testExtensionAdditionGroup(Config) ->
test(Config, fun testExtensionAdditionGroup/3).
@@ -1157,20 +1177,33 @@ END
ok = asn1ct:compile(File, [{outdir, PrivDir}]).
-timer_compile(Config, Rule) ->
- asn1_test_lib:compile_all(["H235-SECURITY-MESSAGES", "H323-MESSAGES"],
- Config, [no_ok_wrapper,Rule]).
+timer_compile(Config, Opts0) ->
+ Files = ["H235-SECURITY-MESSAGES", "H323-MESSAGES"],
+ Opts = [no_ok_wrapper,?NO_MAPS_MODULE|Opts0],
+ asn1_test_lib:compile_all(Files, Config, Opts).
testTimer_ber(Config) ->
- timer_compile(Config, ber),
+ timer_compile(Config, [ber]),
testTimer:go().
testTimer_per(Config) ->
- timer_compile(Config, per),
+ timer_compile(Config, [per]),
testTimer:go().
testTimer_uper(Config) ->
- timer_compile(Config, uper),
+ timer_compile(Config, [uper]),
+ testTimer:go().
+
+testTimer_ber_maps(Config) ->
+ timer_compile(Config, [ber,maps]),
+ testTimer:go().
+
+testTimer_per_maps(Config) ->
+ timer_compile(Config, [per,maps]),
+ testTimer:go().
+
+testTimer_uper_maps(Config) ->
+ timer_compile(Config, [uper,maps]),
testTimer:go().
%% Test of multiple-line comment, OTP-8043
@@ -1179,9 +1212,11 @@ testComment(Config) ->
asn1_test_lib:roundtrip('Comment', 'Seq', {'Seq',12,true}).
testName2Number(Config) ->
- N2NOptions = [{n2n,Type} || Type <- ['CauseMisc', 'CauseProtocol',
- 'CauseRadioNetwork',
- 'CauseTransport','CauseNas']],
+ N2NOptions0 = [{n2n,Type} ||
+ Type <- ['CauseMisc', 'CauseProtocol',
+ 'CauseRadioNetwork',
+ 'CauseTransport','CauseNas']],
+ N2NOptions = [?NO_MAPS_MODULE|N2NOptions0],
asn1_test_lib:compile("S1AP-IEs", Config, N2NOptions),
0 = 'S1AP-IEs':name2num_CauseMisc('control-processing-overload'),
@@ -1191,8 +1226,9 @@ testName2Number(Config) ->
%% Test that n2n option generates name2num and num2name functions supporting
%% values not within the extension root if the enumeration type has an
%% extension marker.
- N2NOptionsExt = [{n2n, 'NoExt'}, {n2n, 'Ext'}, {n2n, 'Ext2'}],
+ N2NOptionsExt = [?NO_MAPS_MODULE,{n2n,'NoExt'},{n2n,'Ext'},{n2n,'Ext2'}],
asn1_test_lib:compile("EnumN2N", Config, N2NOptionsExt),
+
%% Previously, name2num and num2name was not generated if the type didn't
%% have an extension marker:
0 = 'EnumN2N':name2num_NoExt('blue'),
@@ -1210,9 +1246,11 @@ testName2Number(Config) ->
ok.
ticket_7407(Config) ->
- asn1_test_lib:compile("EUTRA-extract-7407", Config, [uper]),
+ Opts = [uper,?NO_MAPS_MODULE],
+ asn1_test_lib:compile("EUTRA-extract-7407", Config, Opts),
ticket_7407_code(true),
- asn1_test_lib:compile("EUTRA-extract-7407", Config, [uper,no_final_padding]),
+ asn1_test_lib:compile("EUTRA-extract-7407", Config,
+ [no_final_padding|Opts]),
ticket_7407_code(false).
ticket_7407_code(FinalPadding) ->
diff --git a/lib/asn1/test/asn1_SUITE_data/Maps.asn1 b/lib/asn1/test/asn1_SUITE_data/Maps.asn1
new file mode 100644
index 0000000000..fd5f373e45
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/Maps.asn1
@@ -0,0 +1,17 @@
+Maps DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+
+XY ::= SEQUENCE { x INTEGER DEFAULT 0, y INTEGER DEFAULT 0 }
+
+xy1 XY ::= { x 42, y 17 }
+xy2 XY ::= { }
+xy3 XY ::= { y 999 }
+
+S ::= SEQUENCE {
+ xy XY DEFAULT { x 100, y 100 },
+ os OCTET STRING OPTIONAL
+}
+
+s1 S ::= {}
+
+END
diff --git a/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1 b/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1
index 5fda19303a..e866ef2f4f 100644
--- a/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1
+++ b/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1
@@ -48,6 +48,17 @@ SeqExt6 ::= SEQUENCE
[[ i6 [106] INTEGER, i7 [107] INTEGER ]]
}
+SeqExt7 ::= SEQUENCE
+{
+ -- The spaces between the ellipsis and the comma will prevent them
+ -- from being removed.
+ ... ,
+ [[ a INTEGER (0..65535) OPTIONAL,
+ b OCTET STRING OPTIONAL,
+ c BOOLEAN
+ ]]
+}
+
SeqExt1X ::= XSeqExt1
SeqExt2X ::= XSeqExt2
diff --git a/lib/asn1/test/asn1_SUITE_data/nbapsystem/NBAP-PDU-Discriptions.asn b/lib/asn1/test/asn1_SUITE_data/nbapsystem/NBAP-PDU-Descriptions.asn
index b9be9934e4..12a4475422 100644
--- a/lib/asn1/test/asn1_SUITE_data/nbapsystem/NBAP-PDU-Discriptions.asn
+++ b/lib/asn1/test/asn1_SUITE_data/nbapsystem/NBAP-PDU-Descriptions.asn
@@ -4,7 +4,7 @@
--
-- **************************************************************
-NBAP-PDU-Discriptions {
+NBAP-PDU-Descriptions {
itu-t (0) identified-organization (4) etsi (0) mobileDomain (0)
umts-Access (20) modules (3) nbap (2) version1 (1) nbap-PDU-Descriptions (0) }
diff --git a/lib/asn1/test/asn1_SUITE_data/test_records.erl b/lib/asn1/test/asn1_SUITE_data/test_records.erl
index 9fd07c1449..afb1c8c80b 100644
--- a/lib/asn1/test/asn1_SUITE_data/test_records.erl
+++ b/lib/asn1/test/asn1_SUITE_data/test_records.erl
@@ -25,7 +25,7 @@
-define(line,put(test_server_loc,{?MODULE,?LINE}),).
--include("NBAP-PDU-Discriptions.hrl").
+-include("NBAP-PDU-Descriptions.hrl").
-include("NBAP-PDU-Contents.hrl").
-include("NBAP-Containers.hrl").
-include("NBAP-CommonDataTypes.hrl").
diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl
index dc614db4f2..a79958d229 100644
--- a/lib/asn1/test/asn1_test_lib.erl
+++ b/lib/asn1/test/asn1_test_lib.erl
@@ -25,7 +25,8 @@
hex_to_bin/1,
match_value/2,
parallel/0,
- roundtrip/3,roundtrip/4,roundtrip_enc/3,roundtrip_enc/4]).
+ roundtrip/3,roundtrip/4,roundtrip_enc/3,roundtrip_enc/4,
+ map_roundtrip/3]).
-include_lib("common_test/include/ct.hrl").
@@ -94,15 +95,58 @@ module(F0) ->
list_to_atom(F).
%% filename:join(CaseDir, F ++ ".beam").
-compile_file(File, Options) ->
+compile_file(File, Options0) ->
+ Options = [warnings_as_errors|Options0],
try
- ok = asn1ct:compile(File, [warnings_as_errors|Options])
+ ok = asn1ct:compile(File, Options),
+ ok = compile_maps(File, Options)
catch
_:Reason ->
ct:print("Failed to compile ~s\n~p", [File,Reason]),
error
end.
+compile_maps(File, Options) ->
+ unload_map_mod(File),
+ Incompat = [abs,compact_bit_string,legacy_bit_string,
+ legacy_erlang_types,maps,asn1_test_lib_no_maps],
+ case lists:any(fun(E) -> lists:member(E, Incompat) end, Options) of
+ true ->
+ ok;
+ false ->
+ compile_maps_1(File, Options)
+ end.
+
+compile_maps_1(File, Options) ->
+ ok = asn1ct:compile(File, [maps,no_ok_wrapper,noobj|Options]),
+ OutDir = proplists:get_value(outdir, Options),
+ Base0 = filename:rootname(filename:basename(File)),
+ Base = case filename:extension(Base0) of
+ ".set" ->
+ filename:rootname(Base0);
+ _ ->
+ Base0
+ end,
+ ErlBase = Base ++ ".erl",
+ ErlFile = filename:join(OutDir, ErlBase),
+ {ok,Erl0} = file:read_file(ErlFile),
+ Erl = re:replace(Erl0, <<"-module\\('">>, "&maps_"),
+ MapsErlFile = filename:join(OutDir, "maps_" ++ ErlBase),
+ ok = file:write_file(MapsErlFile, Erl),
+ {ok,_} = compile:file(MapsErlFile, [report,{outdir,OutDir},{i,OutDir}]),
+ ok.
+
+unload_map_mod(File0) ->
+ File1 = filename:basename(File0),
+ File2 = filename:rootname(File1, ".asn"),
+ File3 = filename:rootname(File2, ".asn1"),
+ File4 = filename:rootname(File3, ".py"),
+ File = filename:rootname(File4, ".set"),
+ MapMod = list_to_atom("maps_"++File),
+ code:delete(MapMod),
+ code:purge(MapMod),
+ ok.
+
compile_erlang(Mod, Config, Options) ->
DataDir = proplists:get_value(data_dir, Config),
CaseDir = proplists:get_value(case_dir, Config),
@@ -147,24 +191,60 @@ roundtrip(Mod, Type, Value) ->
roundtrip(Mod, Type, Value, Value).
roundtrip(Mod, Type, Value, ExpectedValue) ->
- {ok,Encoded} = Mod:encode(Type, Value),
- {ok,ExpectedValue} = Mod:decode(Type, Encoded),
- test_ber_indefinite(Mod, Type, Encoded, ExpectedValue),
- ok.
+ roundtrip_enc(Mod, Type, Value, ExpectedValue).
roundtrip_enc(Mod, Type, Value) ->
roundtrip_enc(Mod, Type, Value, Value).
roundtrip_enc(Mod, Type, Value, ExpectedValue) ->
- {ok,Encoded} = Mod:encode(Type, Value),
- {ok,ExpectedValue} = Mod:decode(Type, Encoded),
+ case Mod:encode(Type, Value) of
+ {ok,Encoded} ->
+ {ok,ExpectedValue} = Mod:decode(Type, Encoded);
+ Encoded when is_binary(Encoded) ->
+ ExpectedValue = Mod:decode(Type, Encoded)
+ end,
+ map_roundtrip(Mod, Type, Encoded),
test_ber_indefinite(Mod, Type, Encoded, ExpectedValue),
Encoded.
+map_roundtrip(Mod, Type, Encoded) ->
+ MapMod = list_to_atom("maps_"++atom_to_list(Mod)),
+ try MapMod:maps() of
+ true ->
+ map_roundtrip_1(MapMod, Type, Encoded)
+ catch
+ error:undef ->
+ ok
+ end.
+
%%%
%%% Internal functions.
%%%
+map_roundtrip_1(Mod, Type, Encoded) ->
+ Decoded = Mod:decode(Type, Encoded),
+ case Mod:encode(Type, Decoded) of
+ Encoded ->
+ ok;
+ OtherEncoding ->
+ case is_named_bitstring(Decoded) of
+ true ->
+ %% In BER, named BIT STRINGs with different number of
+ %% trailing zeroes decode to the same value.
+ ok;
+ false ->
+ error({encode_mismatch,Decoded,Encoded,OtherEncoding})
+ end
+ end,
+ ok.
+
+is_named_bitstring([H|T]) ->
+ is_atom(H) andalso is_named_bitstring(T);
+is_named_bitstring([]) ->
+ true;
+is_named_bitstring(_) ->
+ false.
+
hex2num(C) when $0 =< C, C =< $9 -> C - $0;
hex2num(C) when $A =< C, C =< $F -> C - $A + 10;
hex2num(C) when $a =< C, C =< $f -> C - $a + 10.
@@ -179,7 +259,12 @@ test_ber_indefinite(Mod, Type, Encoded, ExpectedValue) ->
case Mod:encoding_rule() of
ber ->
Indefinite = iolist_to_binary(ber_indefinite(Encoded)),
- {ok,ExpectedValue} = Mod:decode(Type, Indefinite);
+ case Mod:decode(Type, Indefinite) of
+ {ok,ExpectedValue} ->
+ ok;
+ ExpectedValue ->
+ ok
+ end;
_ ->
ok
end.
diff --git a/lib/asn1/test/h323test.erl b/lib/asn1/test/h323test.erl
index 935af0ba09..41a9159335 100644
--- a/lib/asn1/test/h323test.erl
+++ b/lib/asn1/test/h323test.erl
@@ -27,6 +27,8 @@ run(per) -> run();
run(_Rules) -> ok.
run() ->
+ roundtrip('EndpointType', endpoint()),
+ roundtrip('Alerting-UUIE', alerting_uuie()),
roundtrip('H323-UserInformation', alerting_val(), alerting_enc()),
roundtrip('H323-UserInformation', connect_val(), connect_enc()),
general_string(),
@@ -36,18 +38,24 @@ alerting_val() ->
{'H323-UserInformation',
{'H323-UU-PDU',
{alerting,
- {'Alerting-UUIE',
- {0,0,8,2250,0,2},
- {'EndpointType',asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,
- asn1_NOVALUE,asn1_NOVALUE,
- {'TerminalInfo',asn1_NOVALUE},
- false,false},
- asn1_NOVALUE,
- {'CallIdentifier',<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>},
- asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}},
+ alerting_uuie()},
asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE},
asn1_NOVALUE}.
+endpoint() ->
+ {'EndpointType',asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,
+ asn1_NOVALUE,asn1_NOVALUE,
+ {'TerminalInfo',asn1_NOVALUE},
+ false,false}.
+
+alerting_uuie() ->
+ {'Alerting-UUIE',
+ {0,0,8,2250,0,2},
+ endpoint(),
+ asn1_NOVALUE,
+ {'CallIdentifier',<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>},
+ asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}.
+
alerting_enc() ->
"0380060008914a0002020120110000000000000000000000000000000000".
@@ -82,6 +90,9 @@ general_string() ->
UI = <<109,64,1,57>>,
{ok, _V} = 'MULTIMEDIA-SYSTEM-CONTROL':decode(Type, UI).
+roundtrip(T, V) ->
+ asn1_test_lib:roundtrip('H323-MESSAGES', T, V).
+
roundtrip(T, V, HexString) ->
Enc = asn1_test_lib:hex_to_bin(HexString),
Enc = asn1_test_lib:roundtrip_enc('H323-MESSAGES', T, V),
diff --git a/lib/asn1/test/testContextSwitchingTypes.erl b/lib/asn1/test/testContextSwitchingTypes.erl
index 10012908a9..5688d8afd6 100644
--- a/lib/asn1/test/testContextSwitchingTypes.erl
+++ b/lib/asn1/test/testContextSwitchingTypes.erl
@@ -90,5 +90,6 @@ check_object_identifier(Tuple) when is_tuple(Tuple) ->
enc_dec(T, V0) ->
M = 'ContextSwitchingTypes',
{ok,Enc} = M:encode(T, V0),
+ asn1_test_lib:map_roundtrip(M, T, Enc),
{ok,V} = M:decode(T, Enc),
V.
diff --git a/lib/asn1/test/testInfObj.erl b/lib/asn1/test/testInfObj.erl
index 5a9f47d865..c519c70cdf 100644
--- a/lib/asn1/test/testInfObj.erl
+++ b/lib/asn1/test/testInfObj.erl
@@ -197,5 +197,6 @@ roundtrip(M, T, V) ->
enc_dec(M, T, V0) ->
{ok,Enc} = M:encode(T, V0),
+ asn1_test_lib:map_roundtrip(M, T, Enc),
{ok,V} = M:decode(T, Enc),
V.
diff --git a/lib/asn1/test/testMaps.erl b/lib/asn1/test/testMaps.erl
new file mode 100644
index 0000000000..45dd2255ba
--- /dev/null
+++ b/lib/asn1/test/testMaps.erl
@@ -0,0 +1,50 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(testMaps).
+
+-export([main/1]).
+
+main(_) ->
+ M = 'Maps',
+ true = M:maps(),
+
+ true = M:xy1() =:= #{x=>42,y=>17},
+ true = M:xy2() =:= #{x=>0,y=>0},
+ true = M:xy3() =:= #{x=>0,y=>999},
+ true = M:s1() =:= #{xy=>#{x=>100,y=>100}},
+
+ roundtrip('XY', M:xy1()),
+ roundtrip('XY', M:xy2()),
+ roundtrip('XY', M:xy3()),
+ roundtrip('XY', #{}, #{x=>0,y=>0}),
+
+ roundtrip('S', M:s1()),
+ roundtrip('S', #{}, #{xy=>#{x=>100,y=>100}}),
+ roundtrip('S', #{os=><<1,2,3>>}, #{xy=>#{x=>100,y=>100},
+ os=><<1,2,3>>}),
+
+ ok.
+
+roundtrip(Type, Value) ->
+ roundtrip(Type, Value, Value).
+
+roundtrip(Type, Value, Expected) ->
+ asn1_test_lib:roundtrip('Maps', Type, Value, Expected).
diff --git a/lib/asn1/test/testMultipleLevels.erl b/lib/asn1/test/testMultipleLevels.erl
index c610e59f3d..e9d83665aa 100644
--- a/lib/asn1/test/testMultipleLevels.erl
+++ b/lib/asn1/test/testMultipleLevels.erl
@@ -24,5 +24,7 @@
main(_) ->
Data = {'Top',{short,"abc"},{long,"a long string follows here"}},
- {ok,B} = 'MultipleLevels':encode('Top', Data),
- {ok,Data} = 'MultipleLevels':decode('Top', iolist_to_binary(B)).
+ roundtrip('Top', Data).
+
+roundtrip(T, V) ->
+ asn1_test_lib:roundtrip('MultipleLevels', T, V).
diff --git a/lib/asn1/test/testNBAPsystem.erl b/lib/asn1/test/testNBAPsystem.erl
index 1af283af42..8d61ca18ce 100644
--- a/lib/asn1/test/testNBAPsystem.erl
+++ b/lib/asn1/test/testNBAPsystem.erl
@@ -84,7 +84,7 @@ compile(Config, Options) ->
M <- ["NBAP-CommonDataTypes.asn",
"NBAP-IEs.asn",
"NBAP-PDU-Contents.asn",
- "NBAP-PDU-Discriptions.asn",
+ "NBAP-PDU-Descriptions.asn",
"NBAP-Constants.asn",
"NBAP-Containers.asn"]],
asn1_test_lib:compile_all(Fs, Config, Options),
@@ -98,16 +98,16 @@ test(_Erule,Config) ->
ticket_5812(Config) ->
Msg = v_5812(),
- {ok,B2} = 'NBAP-PDU-Discriptions':encode('NBAP-PDU', Msg),
+ {ok,B2} = 'NBAP-PDU-Descriptions':encode('NBAP-PDU', Msg),
V = <<0,28,74,0,3,48,0,0,1,0,123,64,41,0,0,0,126,64,35,95,208,2,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,145,0,1,205,0,0,0,0,2,98,64,1,128>>,
ok = compare(V,B2),
- {ok,Msg2} = 'NBAP-PDU-Discriptions':decode('NBAP-PDU', B2),
+ {ok,Msg2} = 'NBAP-PDU-Descriptions':decode('NBAP-PDU', B2),
ok = check_record_names(Msg2,Config).
enc_audit_req_msg() ->
Msg = {initiatingMessage, audit_req_msg()},
- {ok,B} = 'NBAP-PDU-Discriptions':encode('NBAP-PDU', Msg),
- {ok,_Msg} = 'NBAP-PDU-Discriptions':decode('NBAP-PDU', B),
+ {ok,B} = 'NBAP-PDU-Descriptions':encode('NBAP-PDU', Msg),
+ {ok,_Msg} = 'NBAP-PDU-Descriptions':decode('NBAP-PDU', B),
{initiatingMessage,
#'InitiatingMessage'{value=#'AuditRequest'{protocolIEs=[{_,114,ignore,_}],
protocolExtensions = asn1_NOVALUE}}} = _Msg,
@@ -116,8 +116,8 @@ enc_audit_req_msg() ->
cell_setup_req_msg_test() ->
Msg = {initiatingMessage, cell_setup_req_msg()},
- {ok,B} = 'NBAP-PDU-Discriptions':encode('NBAP-PDU', Msg),
- {ok,_Msg} = 'NBAP-PDU-Discriptions':decode('NBAP-PDU', B),
+ {ok,B} = 'NBAP-PDU-Descriptions':encode('NBAP-PDU', Msg),
+ {ok,_Msg} = 'NBAP-PDU-Descriptions':decode('NBAP-PDU', B),
io:format("Msg: ~P~n~n_Msg: ~P~n",[Msg,15,_Msg,15]),
ok.
diff --git a/lib/asn1/test/testRfcs.erl b/lib/asn1/test/testRfcs.erl
index da7333ef98..20176e35eb 100644
--- a/lib/asn1/test/testRfcs.erl
+++ b/lib/asn1/test/testRfcs.erl
@@ -35,22 +35,27 @@ compile(Config, Erules, Options0) ->
asn1_test_lib:compile_all(Specs, Config, [Erules,{i,CaseDir}|Options]).
test() ->
- {1,3,6,1,5,5,7,48,1,2} =
- IdPkixOcspNonce =
- 'OCSP-2009':'id-pkix-ocsp-nonce'(),
- roundtrip('OCSP-2009', 'OCSPRequest',
- {'OCSPRequest',
- {'TBSRequest',
- 0,
- {rfc822Name,"name string"},
- [{'Request',
- {'CertID',{'_',{2,9,3,4,5},asn1_NOVALUE},
- <<"POTATOHASH">>,<<"HASHBROWN">>,42},
- [{'_',IdPkixOcspNonce,true,<<34,159,16,57,199>>}]}],
- asn1_NOVALUE},
- asn1_NOVALUE}),
- otp_7759(),
- ok.
+ M = 'OCSP-2009',
+ case M:maps() of
+ false ->
+ {1,3,6,1,5,5,7,48,1,2} =
+ IdPkixOcspNonce =
+ 'OCSP-2009':'id-pkix-ocsp-nonce'(),
+ roundtrip('OCSP-2009', 'OCSPRequest',
+ {'OCSPRequest',
+ {'TBSRequest',
+ 0,
+ {rfc822Name,"name string"},
+ [{'Request',
+ {'CertID',{'_',{2,9,3,4,5},asn1_NOVALUE},
+ <<"POTATOHASH">>,<<"HASHBROWN">>,42},
+ [{'_',IdPkixOcspNonce,true,<<34,159,16,57,199>>}]}],
+ asn1_NOVALUE},
+ asn1_NOVALUE}),
+ otp_7759(records);
+ true ->
+ otp_7759(maps)
+ end.
roundtrip(Module, Type, Value0) ->
Enc = Module:encode(Type, Value0),
@@ -58,7 +63,7 @@ roundtrip(Module, Type, Value0) ->
asn1_test_lib:match_value(Value0, Value1),
ok.
-otp_7759() ->
+otp_7759(Pack) ->
%% The release note for asn-1.6.6 says:
%% Decode of an open_type when the value was empty tagged
%% type encoded with indefinite length failed.
@@ -66,10 +71,15 @@ otp_7759() ->
Encoded = encoded_msg(),
ContentInfo = Mod:decode('ContentInfo', Encoded),
io:format("~p\n", [ContentInfo]),
- {'ContentInfo',_Id,PKCS7_content} = ContentInfo,
- X = Mod:decode('SignedData', PKCS7_content),
+ Content = case ContentInfo of
+ {'ContentInfo',_Id,Content0} when Pack =:= records ->
+ Content0;
+ #{'content-type':=_,'pkcs7-content':=Content0}
+ when Pack =:= maps ->
+ Content0
+ end,
+ X = Mod:decode('SignedData', Content),
io:format("~p\n", [X]),
- io:nl(),
ok.
encoded_msg() ->
diff --git a/lib/asn1/test/testSeqExtension.erl b/lib/asn1/test/testSeqExtension.erl
index f7885cb002..be1d1c2490 100644
--- a/lib/asn1/test/testSeqExtension.erl
+++ b/lib/asn1/test/testSeqExtension.erl
@@ -31,6 +31,7 @@
-record('SeqExt4',{bool, int}).
-record('SeqExt5',{name, shoesize}).
-record('SeqExt6',{i1,i2,i3,i4,i5,i6,i7}).
+-record('SeqExt7',{a=asn1_NOVALUE,b=asn1_NOVALUE,c}).
-record('SuperSeq',{s1,s2,s3,s4,s5,s6,i}).
main(Erule, DataDir, Opts) ->
@@ -45,8 +46,35 @@ main(Erule, DataDir, Opts) ->
roundtrip('SeqExt4', #'SeqExt4'{bool=true,int=12345}),
roundtrip('SeqExt4', #'SeqExt4'{bool=false,int=123456}),
+ case Erule of
+ ber ->
+ %% BER currently does not handle Extension Addition Groups
+ %% correctly.
+ ok;
+ _ ->
+ v_roundtrip3('SeqExt5', #'SeqExt5'{name=asn1_NOVALUE,
+ shoesize=asn1_NOVALUE},
+ Erule, #{per=>"00",
+ uper=>"00"}),
+ v_roundtrip3('SeqExt7', #'SeqExt7'{c=asn1_NOVALUE},
+ Erule, #{per=>"00",
+ uper=>"00"})
+ end,
roundtrip('SeqExt5', #'SeqExt5'{name = <<"Arne">>,shoesize=47}),
+ v_roundtrip3('SeqExt7', #'SeqExt7'{c=false},
+ Erule, #{per=>"80800100",
+ uper=>"80808000"}),
+ v_roundtrip3('SeqExt7', #'SeqExt7'{c=true},
+ Erule, #{per=>"80800120",
+ uper=>"80809000"}),
+ v_roundtrip3('SeqExt7', #'SeqExt7'{a=777,b = <<16#AA>>,c=false},
+ Erule, #{per=>"808006C0 030901AA 00",
+ uper=>"8082E061 20354000"}),
+ v_roundtrip3('SeqExt7', #'SeqExt7'{a=8888,c=false},
+ Erule, #{per=>"80800480 22B800",
+ uper=>"8081C457 0000"}),
+
%% Encode a value with this version of the specification.
BigInt = 128638468966,
SuperSeq = #'SuperSeq'{s1=#'SeqExt1'{},
@@ -106,6 +134,7 @@ main(Erule, DataDir, Opts) ->
v_roundtrip2(Erule, 'SeqExt130',
list_to_tuple(['SeqExt130'|
lists:duplicate(129, asn1_NOVALUE)++[199]])),
+
ok.
roundtrip(Type, Value) ->
@@ -118,6 +147,15 @@ v_roundtrip2(Erule, Type, Value) ->
roundtrip2(Type, Value) ->
asn1_test_lib:roundtrip_enc('SeqExtension2', Type, Value).
+v_roundtrip3(Type, Value, Erule, Map) ->
+ case maps:find(Erule, Map) of
+ {ok,Hex} ->
+ Encoded = asn1_test_lib:hex_to_bin(Hex),
+ Encoded = asn1_test_lib:roundtrip_enc('SeqExtension', Type, Value);
+ error ->
+ asn1_test_lib:roundtrip('SeqExtension', Type, Value)
+ end.
+
v(ber, 'SeqExt66') -> "30049F41 017D";
v(per, 'SeqExt66') -> "C0420000 00000000 00004001 FA";
v(uper, 'SeqExt66') -> "D0800000 00000000 00101FA0";
diff --git a/lib/asn1/test/testTCAP.erl b/lib/asn1/test/testTCAP.erl
index 422ae1f0fc..a6f0f9fad7 100644
--- a/lib/asn1/test/testTCAP.erl
+++ b/lib/asn1/test/testTCAP.erl
@@ -92,5 +92,6 @@ test_asn1config() ->
enc_dec(T, V0) ->
M = 'TCAPPackage',
{ok,Enc} = M:encode(T, V0),
+ asn1_test_lib:map_roundtrip(M, T, Enc),
{ok,V} = M:decode(T, Enc),
V.
diff --git a/lib/asn1/test/testTimer.erl b/lib/asn1/test/testTimer.erl
index bd8da85735..3edeb1b712 100644
--- a/lib/asn1/test/testTimer.erl
+++ b/lib/asn1/test/testTimer.erl
@@ -25,7 +25,42 @@
-define(times, 5000).
-val() ->
+go() ->
+ Module = 'H323-MESSAGES',
+ Type = 'H323-UserInformation',
+ Value = case Module:maps() of
+ false -> val_records();
+ true -> val_maps()
+ end,
+ Bytes = Module:encode(Type, Value),
+ Value = Module:decode(Type, Bytes),
+
+ {ValWr,done} = timer:tc(fun() -> encode(?times, Module, Type, Value) end),
+ io:format("ASN.1 encoding: ~p micro~n", [ValWr / ?times]),
+
+ done = decode(2, Module, Type, Bytes),
+
+ {ValRead,done} = timer:tc(fun() -> decode(?times, Module, Type, Bytes) end),
+ io:format("ASN.1 decoding: ~p micro~n", [ValRead /?times]),
+
+ Comment = "encode: "++integer_to_list(round(ValWr/?times)) ++
+ " micro, decode: "++integer_to_list(round(ValRead /?times)) ++
+ " micro. [" ++ atom_to_list(Module:encoding_rule()) ++ "]",
+ {comment,Comment}.
+
+encode(0, _Module,_Type,_Value) ->
+ done;
+encode(N, Module,Type,Value) ->
+ Module:encode(Type, Value),
+ encode(N-1, Module, Type, Value).
+
+decode(0, _Module, _Type, _Value) ->
+ done;
+decode(N, Module, Type, Value) ->
+ Module:decode(Type, Value),
+ decode(N-1, Module, Type, Value).
+
+val_records() ->
{'H323-UserInformation',{'H323-UU-PDU',
{callProceeding,
{'CallProceeding-UUIE',
@@ -126,34 +161,66 @@ val() ->
{'H323-UserInformation_user-data',24,<<"O">>}}.
-go() ->
- Module = 'H323-MESSAGES',
- Type = 'H323-UserInformation',
- Value = val(),
- Bytes = Module:encode(Type, Value),
- Value = Module:decode(Type, Bytes),
-
- {ValWr,done} = timer:tc(fun() -> encode(?times, Module, Type, Value) end),
- io:format("ASN.1 encoding: ~p micro~n", [ValWr / ?times]),
-
- done = decode(2, Module, Type, Bytes),
-
- {ValRead,done} = timer:tc(fun() -> decode(?times, Module, Type, Bytes) end),
- io:format("ASN.1 decoding: ~p micro~n", [ValRead /?times]),
-
- Comment = "encode: "++integer_to_list(round(ValWr/?times)) ++
- " micro, decode: "++integer_to_list(round(ValRead /?times)) ++
- " micro. [" ++ atom_to_list(Module:encoding_rule()) ++ "]",
- {comment,Comment}.
-
-encode(0, _Module,_Type,_Value) ->
- done;
-encode(N, Module,Type,Value) ->
- Module:encode(Type, Value),
- encode(N-1, Module, Type, Value).
-
-decode(0, _Module, _Type, _Value) ->
- done;
-decode(N, Module, Type, Value) ->
- Module:decode(Type, Value),
- decode(N-1, Module, Type, Value).
+val_maps() ->
+#{'h323-uu-pdu' => #{h245Control => [],
+ h245Tunneling => true,
+ 'h323-message-body' => {callProceeding,#{callIdentifier => #{guid => <<"OCTET STRINGOCTE">>},
+ cryptoTokens => [{cryptoGKPwdEncr,#{algorithmOID => {1,18,467,467},
+ encryptedData => <<"OC">>,
+ paramS => #{iv8 => <<"OCTET ST">>,
+ ranInt => -7477016}}},
+ {cryptoGKPwdEncr,#{algorithmOID => {1,19,486,486},
+ encryptedData => <<>>,
+ paramS => #{iv8 => <<"OCTET ST">>,
+ ranInt => -2404513}}}],
+ destinationInfo => #{gatekeeper => #{nonStandardData => #{data => <<"O">>,
+ nonStandardIdentifier => {object,{0,10,260}}}},
+ gateway => #{nonStandardData => #{data => <<"O">>,
+ nonStandardIdentifier => {object,{0,13,326}}},
+ protocol => [{h320,#{dataRatesSupported => [#{channelMultiplier => 78,
+ channelRate => 1290470518,
+ nonStandardData => #{data => <<"O">>,
+ nonStandardIdentifier => {object,{0,11,295}}}}],
+ nonStandardData => #{data => <<"O">>,
+ nonStandardIdentifier => {object,{0,11,282}}},
+ supportedPrefixes => [#{nonStandardData => #{data => <<"O">>,
+ nonStandardIdentifier => {object,{0,12,312}}},
+ prefix => {'h323-ID',"BM"}}]}}]},
+ mc => true,
+ mcu => #{nonStandardData => #{data => <<"OC">>,
+ nonStandardIdentifier => {object,{1,13,340,340}}}},
+ nonStandardData => #{data => <<"O">>,nonStandardIdentifier => {object,{0,9,237}}},
+ terminal => #{nonStandardData => #{data => <<"OC">>,
+ nonStandardIdentifier => {object,{1,14,353,354}}}},
+ undefinedNode => true,
+ vendor => #{productId => <<"OC">>,
+ vendor => #{manufacturerCode => 16282,
+ t35CountryCode => 62,
+ t35Extension => 63},
+ versionId => <<"OC">>}},
+ fastStart => [],
+ h245Address => {ipxAddress,#{netnum => <<"OCTE">>,
+ node => <<"OCTET ">>,
+ port => <<"OC">>}},
+ h245SecurityMode => {noSecurity,'NULL'},
+ protocolIdentifier => {0,8,222},
+ tokens => [#{certificate => #{certificate => <<"OC">>,type => {1,16,405,406}},
+ challenge => <<"OCTET STR">>,
+ dhkey => #{generator => <<1:1>>,halfkey => <<1:1>>,modSize => <<1:1>>},
+ generalID => "BMP",
+ nonStandard => #{data => <<"OC">>,nonStandardIdentifier => {1,16,414,415}},
+ password => "BM",
+ random => -26430296,
+ timeStamp => 1667517741},
+ #{certificate => #{certificate => <<"OC">>,type => {1,17,442,443}},
+ challenge => <<"OCTET STRI">>,
+ dhkey => #{generator => <<1:1>>,halfkey => <<1:1>>,modSize => <<1:1>>},
+ generalID => "BMP",
+ nonStandard => #{data => <<"OC">>,nonStandardIdentifier => {1,18,452,452}},
+ password => "BMP",
+ random => -16356110,
+ timeStamp => 1817656756}]}},
+ h4501SupplementaryService => [],
+ nonStandardControl => [],
+ nonStandardData => #{data => <<>>,nonStandardIdentifier => {object,{0,3,84}}}},
+ 'user-data' => #{'protocol-discriminator' => 24,'user-information' => <<"O">>}}.
diff --git a/lib/asn1/test/testUniqueObjectSets.erl b/lib/asn1/test/testUniqueObjectSets.erl
index 4d3ec94391..30cbceb577 100644
--- a/lib/asn1/test/testUniqueObjectSets.erl
+++ b/lib/asn1/test/testUniqueObjectSets.erl
@@ -27,6 +27,7 @@ seq_roundtrip(I, D0) ->
M = 'UniqueObjectSets',
try
{ok,Enc} = M:encode('Seq', {'Seq',I,D0}),
+ asn1_test_lib:map_roundtrip(M, 'Seq', Enc),
{ok,{'Seq',I,D}} = M:decode('Seq', Enc),
D
catch C:E ->
diff --git a/lib/asn1/test/test_compile_options.erl b/lib/asn1/test/test_compile_options.erl
index ac74470537..c15e61550c 100644
--- a/lib/asn1/test/test_compile_options.erl
+++ b/lib/asn1/test/test_compile_options.erl
@@ -24,8 +24,8 @@
-include_lib("common_test/include/ct.hrl").
--export([wrong_path/1,comp/2,path/1,ticket_6143/1,noobj/1,
- record_name_prefix/1,verbose/1]).
+-export([wrong_path/1,comp/2,path/1,noobj/1,
+ record_name_prefix/1,verbose/1,maps/1]).
%% OTP-5689
wrong_path(Config) ->
@@ -64,8 +64,6 @@ path(Config) ->
file:set_cwd(CWD),
ok.
-ticket_6143(Config) -> asn1_test_lib:compile("AA1", Config, []).
-
noobj(Config) ->
DataDir = proplists:get_value(data_dir,Config),
OutDir = proplists:get_value(priv_dir,Config),
@@ -130,6 +128,28 @@ verbose(Config) when is_list(Config) ->
[] = test_server:capture_get(),
ok.
+maps(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ OutDir = proplists:get_value(case_dir, Config),
+ InFile = filename:join(DataDir, "P-Record"),
+
+ do_maps(ber, InFile, OutDir),
+ do_maps(per, InFile, OutDir),
+ do_maps(uper, InFile, OutDir).
+
+do_maps(Erule, InFile, OutDir) ->
+ Opts = [Erule,maps,{outdir,OutDir}],
+ ok = asn1ct:compile(InFile, Opts),
+
+ %% Make sure that no .hrl files are generated.
+ [] = filelib:wildcard(filename:join(OutDir, "*.hrl")),
+
+ %% Remove all generated files.
+ All = filelib:wildcard(filename:join(OutDir, "*")),
+ _ = [file:delete(N) || N <- All],
+
+ ok.
+
outfiles_check(OutDir) ->
outfiles_check(OutDir,outfiles1()).