1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2004-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22-module(ei_decode_encode_SUITE).
23
24-include_lib("common_test/include/ct.hrl").
25-include("ei_decode_encode_SUITE_data/ei_decode_encode_test_cases.hrl").
26
27-export([all/0, suite/0,
28         init_per_testcase/2,
29         test_ei_decode_encode/1]).
30
31suite() ->
32    [{ct_hooks,[ts_install_cth]}].
33
34all() ->
35    [test_ei_decode_encode].
36
37init_per_testcase(Case, Config) ->
38    rand:uniform(), % Make sure rand is initialized and seeded.
39    io:format("** rand seed = ~p\n", [rand:export_seed()]),
40    runner:init_per_testcase(?MODULE, Case, Config).
41
42%% ---------------------------------------------------------------------------
43
44% NOTE: these types have no meaning on the C side so we pass them
45%       to C and back just to see they are the same.
46
47
48%% ######################################################################## %%
49
50test_ei_decode_encode(Config) when is_list(Config) ->
51    P = runner:start(Config, ?test_ei_decode_encode),
52
53    Fun1  = fun (X) -> {X,true} end,
54    Fun2  = fun runner:init_per_testcase/3,
55    Pid   = self(),
56    Port  = case os:type() of
57                {win32,_} ->
58                    open_port({spawn,"sort"},[]);
59                {unix, darwin} ->
60                    open_port({spawn,"/usr/bin/true"},[]);
61                _ ->
62                    open_port({spawn,"/bin/true"},[])
63            end,
64    Ref   = make_ref(),
65    Trace = {1,2,3,self(),4},			% FIXME how to construct?!
66
67
68    BigSmallA = 1696192905348584855517250509684275447603964214606878827319923580493120589769459602596313014087329389174229999430092223701630077631205171572331191216670754029016160388576759960413039261647653627052707047,
69    BigSmallB = 43581177444506616087519351724629421082877485633442736512567383077022781906420535744195118099822189576169114064491200598595995538299156626345938812352676950427869649947439032133573270227067833308153431095,
70    BigSmallC = 52751775381034251994634567029696659541685100826881826508158083211003576763074162948462801435204697796532659535818017760528684167216110865807581759669824808936751316879636014972704885388116861127856231,
71
72    BigLargeA = 1 bsl 11111 + BigSmallA,
73    BigLargeB = 1 bsl 11112 + BigSmallB,
74    BigLargeC = BigSmallA * BigSmallB * BigSmallC * BigSmallA,
75
76    send_rec(P, Fun1),
77    send_rec(P, Fun2),
78    send_rec(P, Pid),
79    send_rec(P, Port),
80    send_rec(P, Ref),
81    send_rec(P, Trace),
82
83    % bigs
84
85    send_rec(P, BigSmallA),
86    send_rec(P, BigSmallB),
87    send_rec(P, BigSmallC),
88
89    send_rec(P, BigLargeA),
90    send_rec(P, BigLargeB),
91    send_rec(P, BigLargeC),
92
93    %% Test large node containers...
94
95    ThisNode = {node(), erlang:system_info(creation)},
96    TXPid = mk_pid(ThisNode, 32767, 8191),
97    TXPort = mk_port(ThisNode, 268435455),
98    TXRef = mk_ref(ThisNode, [262143, 4294967295, 4294967295]),
99
100    send_rec(P, TXPid),
101    send_rec(P, TXPort),
102    send_rec(P, TXRef),
103
104    [begin OtherNode = {gurka@sallad, Creation},
105	   send_rec(P, mk_pid(OtherNode, 32767, 8191)),
106	   send_rec(P, mk_port(OtherNode, 268435455)),
107	   send_rec(P, mk_ref(OtherNode, [262143, 4294967295, 4294967295])),
108	   send_rec(P, mk_ref(OtherNode, [262143, 4294967295, 4294967295, 4294967294, 4294967293])),
109	   void
110     end || Creation <- [1, 2, 3, 4, 16#adec0ded]],
111
112    %% Full 64-bit pids
113    [send_rec(P, mk_pid({gurka@sallad, 77},
114                        rand:uniform(1 bsl Bits)-1,
115                        rand:uniform(1 bsl Bits)-1))
116     || Bits <- lists:seq(16,32)],
117
118    %% Full 64-bit ports
119    [send_rec(P, mk_port({gurka@sallad, 4711},
120                         rand:uniform(1 bsl Bits)-1))
121     || Bits <- lists:seq(24,40)],
122
123    %% Unicode atoms
124    [begin send_rec(P, Atom),
125           send_rec(P, mk_pid({Atom,1}, 23434, 3434)),
126           send_rec(P, mk_port({Atom,1}, 2343434)),
127           send_rec(P, mk_ref({Atom,1}, [262143, 8723648, 24097245])),
128           void
129     end || Atom <- unicode_atom_data()],
130
131    send_rec(P, {}),
132    send_rec(P, {atom, Pid, Port, Ref}),
133    send_rec(P, [atom, Pid, Port, Ref]),
134    send_rec(P, [atom | Fun1]),
135    send_rec(P, #{}),
136    send_rec(P, #{key => value}),
137    send_rec(P, maps:put(Port, Ref, #{key => value, key2 => Pid})),
138
139    [send_rec(P, <<16#dec0deb175:B/little>>) || B <- lists:seq(0,48)],
140
141    % And last an ugly duckling to test ei_encode_bitstring with bitoffs != 0
142    encode_bitstring(P),
143
144    runner:recv_eot(P),
145    ok.
146
147encode_bitstring(P) ->
148    %% Send one bitstring to c-node
149    Bits = <<16#18f6d4b2907e5c3a1:66>>,
150    P ! {self(), {command, term_to_binary(Bits, [{minor_version, 2}])}},
151
152    %% and then receive and verify a number of different sub-bitstrings
153    receive_sub_bitstring(P, Bits, 0, bit_size(Bits)).
154
155receive_sub_bitstring(_, _, _, NBits) when NBits < 0 ->
156    ok;
157receive_sub_bitstring(P, Bits, BitOffs, NBits) ->
158    <<_:BitOffs, Sub:NBits/bits, _/bits>> = Bits,
159    %%io:format("expecting term_to_binary(~p) = ~p\n", [Sub, term_to_binary(Sub)]),
160    {_B,Sub} = get_buf_and_term(P),
161    receive_sub_bitstring(P, Bits, BitOffs+1, NBits - ((NBits div 20)+1)).
162
163
164
165%% ######################################################################## %%
166
167% We read two packets for each test, the ei_decode_encode and ei_x_decode_encode  version....
168
169send_rec(P, Term) when is_port(P) ->
170    P ! {self(), {command, term_to_binary(Term, [{minor_version, 2}])}},
171    {_B,Term} = get_buf_and_term(P).
172
173
174get_buf_and_term(P) ->
175    B = get_binaries(P),
176    case B of
177        <<131>> ->
178            io:format("(got single magic, no content)\n",[]),
179            {B,'$$magic$$'};
180        <<131,_>> ->
181            T = binary_to_term(B),
182            io:format("~w\n~w\n(got magic)\n",[B,T]),
183            {B,T};
184        _ ->
185            B1 = list_to_binary([131,B]),	% No magic, add
186            T = binary_to_term(B1),
187            %io:format("~w\n~w\n(got no magic)\n",[B,T]),
188            {B,T}
189    end.
190
191
192get_binaries(P) ->
193    B1 = get_binary(P),
194    B2 = get_binary(P),
195    B1 = B2.
196
197get_binary(P) ->
198    case runner:get_term(P) of
199        {bytes,L} ->
200            B = list_to_binary(L),
201            %%io:format("~w\n",[L]),
202            % For strange reasons <<131>> show up as <>....
203            %	    io:format("~w\n",[B]),
204            B;
205        Other ->
206            Other
207    end.
208
209%%
210%% Node container constructor functions
211%%
212
213-define(VERSION_MAGIC,       131).
214
215-define(REFERENCE_EXT,       101).
216-define(PORT_EXT,            102).
217-define(PID_EXT,             103).
218-define(NEW_REFERENCE_EXT,   114).
219-define(NEW_PID_EXT,         $X).
220-define(NEW_PORT_EXT,        $Y).
221-define(NEWER_REFERENCE_EXT, $Z).
222-define(V4_PORT_EXT,         $x).
223
224-define(OLD_MAX_PIDS_PORTS, ((1 bsl 28) - 1)).
225
226uint64_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 64 ->
227    [(Uint bsr 56) band 16#ff,
228     (Uint bsr 48) band 16#ff,
229     (Uint bsr 40) band 16#ff,
230     (Uint bsr 32) band 16#ff,
231     (Uint bsr 24) band 16#ff,
232     (Uint bsr 16) band 16#ff,
233     (Uint bsr 8) band 16#ff,
234     Uint band 16#ff];
235uint64_be(Uint) ->
236    exit({badarg, uint64_be, [Uint]}).
237
238uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
239    [(Uint bsr 24) band 16#ff,
240     (Uint bsr 16) band 16#ff,
241     (Uint bsr 8) band 16#ff,
242     Uint band 16#ff];
243uint32_be(Uint) ->
244    exit({badarg, uint32_be, [Uint]}).
245
246
247uint16_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 16 ->
248    [(Uint bsr 8) band 16#ff,
249     Uint band 16#ff];
250uint16_be(Uint) ->
251    exit({badarg, uint16_be, [Uint]}).
252
253uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 ->
254    Uint band 16#ff;
255uint8(Uint) ->
256    exit({badarg, uint8, [Uint]}).
257
258pid_tag(Creation) when Creation =< 3 -> ?PID_EXT;
259pid_tag(_Creation) -> ?NEW_PID_EXT.
260
261enc_creation(Creation) when Creation =< 3 -> uint8(Creation);
262enc_creation(Creation) -> uint32_be(Creation).
263
264mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
265    <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
266    mk_pid({NodeNameExt, Creation}, Number, Serial);
267mk_pid({NodeNameExt, Creation}, Number, Serial) ->
268    case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
269					      pid_tag(Creation),
270					      NodeNameExt,
271					      uint32_be(Number),
272					      uint32_be(Serial),
273					      enc_creation(Creation)])) of
274	Pid when is_pid(Pid) ->
275	    Pid;
276	{'EXIT', {badarg, _}} ->
277	    exit({badarg, mk_pid, [{NodeNameExt, Creation}, Number, Serial]});
278	Other ->
279	    exit({unexpected_binary_to_term_result, Other})
280    end.
281
282port_tag(Num, Creation) when 0 =< Num, Num =< ?OLD_MAX_PIDS_PORTS, Creation =< 3 ->
283    ?PORT_EXT;
284port_tag(Num, _Creation) when 0 =< Num, Num =< ?OLD_MAX_PIDS_PORTS ->
285    ?NEW_PORT_EXT;
286port_tag(_Num, _Creation) ->
287    ?V4_PORT_EXT.
288
289mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
290    <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
291    mk_port({NodeNameExt, Creation}, Number);
292mk_port({NodeNameExt, Creation}, Number) ->
293    case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
294					      port_tag(Number, Creation),
295					      NodeNameExt,
296					      case Number > ?OLD_MAX_PIDS_PORTS of
297						  true -> uint64_be(Number);
298						  false -> uint32_be(Number)
299					      end,
300					      enc_creation(Creation)])) of
301	Port when is_port(Port) ->
302	    Port;
303	{'EXIT', {badarg, _}} ->
304	    exit({badarg, mk_port, [{NodeNameExt, Creation}, Number]});
305	Other ->
306	    exit({unexpected_binary_to_term_result, Other})
307    end.
308
309ref_tag(Creation) when Creation =< 3 -> ?NEW_REFERENCE_EXT;
310ref_tag(_Creation) -> ?NEWER_REFERENCE_EXT.
311
312mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
313                                           is_integer(Creation),
314                                           is_list(Numbers) ->
315    <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
316    mk_ref({NodeNameExt, Creation}, Numbers);
317mk_ref({NodeNameExt, Creation}, [Number]) when is_binary(NodeNameExt),
318					       is_integer(Creation),
319					       Creation =< 3,
320					       is_integer(Number) ->
321    case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
322                                              ?REFERENCE_EXT,
323                                              NodeNameExt,
324                                              uint32_be(Number),
325                                              uint8(Creation)])) of
326        Ref when is_reference(Ref) ->
327            Ref;
328        {'EXIT', {badarg, _}} ->
329            exit({badarg, mk_ref, [{NodeNameExt, Creation}, [Number]]});
330        Other ->
331            exit({unexpected_binary_to_term_result, Other})
332    end;
333mk_ref({NodeNameExt, Creation}, Numbers) when is_binary(NodeNameExt),
334                                              is_integer(Creation),
335                                              is_list(Numbers) ->
336    case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
337					      ref_tag(Creation),
338					      uint16_be(length(Numbers)),
339					      NodeNameExt,
340					      enc_creation(Creation),
341					      lists:map(fun (N) ->
342								uint32_be(N)
343							end,
344							Numbers)])) of
345	Ref when is_reference(Ref) ->
346	    Ref;
347	{'EXIT', {badarg, _}} ->
348	    exit({badarg, mk_ref, [{NodeNameExt, Creation}, Numbers]});
349	Other ->
350	    exit({unexpected_binary_to_term_result, Other})
351    end.
352
353
354
355unicode_atom_data() ->
356    [uc_atup(lists:seq(16#1f600, 16#1f600+254)),
357     uc_atup(lists:seq(16#1f600, 16#1f600+63)),
358     uc_atup(lists:seq(1, 255)),
359     uc_atup(lists:seq(100, 163)),
360     uc_atup(lists:seq(200, 354)),
361     uc_atup(lists:seq(200, 263)),
362     uc_atup(lists:seq(2000, 2254)),
363     uc_atup(lists:seq(2000, 2063)),
364     uc_atup(lists:seq(65500, 65754)),
365     uc_atup(lists:seq(65500, 65563))
366     | lists:map(fun (N) ->
367                         Pow2 = (1 bsl N),
368                         uc_atup(lists:seq(Pow2 - 127, Pow2 + 127))
369                 end,
370                 lists:seq(7, 20))
371    ].
372
373uc_atup(ATxt) ->
374    string_to_atom(ATxt).
375
376string_to_atom(String) ->
377    Utf8List = string_to_utf8_list(String),
378    Len = length(Utf8List),
379    TagLen = case Len < 256 of
380                 true -> [119, Len];
381                 false -> [118, Len bsr 8, Len band 16#ff]
382             end,
383    binary_to_term(list_to_binary([131, TagLen, Utf8List])).
384
385string_to_utf8_list([]) ->
386    [];
387string_to_utf8_list([CP|CPs]) when is_integer(CP),
388                                   0 =< CP,
389                                   CP =< 16#7F ->
390    [CP | string_to_utf8_list(CPs)];
391string_to_utf8_list([CP|CPs]) when is_integer(CP),
392                                   16#80 =< CP,
393                                   CP =< 16#7FF ->
394    [16#C0 bor (CP bsr 6),
395     16#80 bor (16#3F band CP)
396     | string_to_utf8_list(CPs)];
397string_to_utf8_list([CP|CPs]) when is_integer(CP),
398                                   16#800 =< CP,
399                                   CP =< 16#FFFF ->
400    [16#E0 bor (CP bsr 12),
401     16#80 bor (16#3F band (CP bsr 6)),
402     16#80 bor (16#3F band CP)
403     | string_to_utf8_list(CPs)];
404string_to_utf8_list([CP|CPs]) when is_integer(CP),
405                                   16#10000 =< CP,
406                                   CP =< 16#10FFFF ->
407    [16#F0 bor (CP bsr 18),
408     16#80 bor (16#3F band (CP bsr 12)),
409     16#80 bor (16#3F band (CP bsr 6)),
410     16#80 bor (16#3F band CP)
411     | string_to_utf8_list(CPs)].
412