diff options
author | Rickard Green <rickard@erlang.org> | 2020-04-29 22:09:04 +0200 |
---|---|---|
committer | Rickard Green <rickard@erlang.org> | 2020-05-04 11:56:05 +0200 |
commit | cd3cbb2f519a8868d85b42b7514d4ad69cb06850 (patch) | |
tree | ea3de78bef5cf87d0f9bceaebe527a1c9e3c3602 | |
parent | 337e253aeabe4259ad215b31f6b29a9cb519c415 (diff) | |
download | erlang-cd3cbb2f519a8868d85b42b7514d4ad69cb06850.tar.gz |
Introduce ei_cmp_pids(), ei_cmp_ports(), and ei_cmp_refs()
-rw-r--r-- | lib/erl_interface/doc/src/ei.xml | 51 | ||||
-rw-r--r-- | lib/erl_interface/include/ei.h | 4 | ||||
-rw-r--r-- | lib/erl_interface/src/Makefile.in | 3 | ||||
-rw-r--r-- | lib/erl_interface/src/misc/ei_cmp_nc.c | 117 | ||||
-rw-r--r-- | lib/erl_interface/test/Makefile | 8 | ||||
-rw-r--r-- | lib/erl_interface/test/ei_decode_SUITE.erl | 96 | ||||
-rw-r--r-- | lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c | 81 |
7 files changed, 355 insertions, 5 deletions
diff --git a/lib/erl_interface/doc/src/ei.xml b/lib/erl_interface/doc/src/ei.xml index 820fb51c28..09f526bc9f 100644 --- a/lib/erl_interface/doc/src/ei.xml +++ b/lib/erl_interface/doc/src/ei.xml @@ -122,6 +122,57 @@ typedef enum { <funcs> <func> + <name since="OTP @OTP-16594@"><ret>int</ret><nametext>ei_cmp_pids(erlang_pid *a, erlang_pid *b)</nametext></name> + <fsummary>Compare two pids.</fsummary> + <desc> + <p> + Compare two process identifiers. The comparison is done the same way + as Erlang does. + </p> + <p> + Returns <c>0</c> if <c>a</c> and <c>b</c> are equal. Returns a value + less than <c>0</c> if <c>a</c> compares as less than <c>b</c>. + Returns a value larger than <c>0</c> if <c>a</c> compares as larger + than <c>b</c>. + </p> + </desc> + </func> + + <func> + <name since="OTP @OTP-16594@"><ret>int</ret><nametext>ei_cmp_ports(erlang_port *a, erlang_port *b)</nametext></name> + <fsummary>Compare two ports.</fsummary> + <desc> + <p> + Compare two port identifiers. The comparison is done the same way as + Erlang does. + </p> + <p> + Returns <c>0</c> if <c>a</c> and <c>b</c> are equal. Returns a value + less than <c>0</c> if <c>a</c> compares as less than <c>b</c>. + Returns a value larger than <c>0</c> if <c>a</c> compares as larger + than <c>b</c>. + </p> + </desc> + </func> + + <func> + <name since="OTP @OTP-16594@"><ret>int</ret><nametext>ei_cmp_refs(erlang_ref *a, erlang_ref *b)</nametext></name> + <fsummary>Compare two references.</fsummary> + <desc> + <p> + Compare two references. The comparison is done the same way as Erlang + does. + </p> + <p> + Returns <c>0</c> if <c>a</c> and <c>b</c> are equal. Returns a value + less than <c>0</c> if <c>a</c> compares as less than <c>b</c>. + Returns a value larger than <c>0</c> if <c>a</c> compares as larger + than <c>b</c>. + </p> + </desc> + </func> + + <func> <name since=""><ret>int</ret><nametext>ei_decode_atom(const char *buf, int *index, char *p)</nametext></name> <fsummary>Decode an atom.</fsummary> <desc> diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h index 605caaa0e5..8ade0b2941 100644 --- a/lib/erl_interface/include/ei.h +++ b/lib/erl_interface/include/ei.h @@ -603,6 +603,10 @@ int ei_x_append(ei_x_buff* x, const ei_x_buff* x2); int ei_x_append_buf(ei_x_buff* x, const char* buf, int len); int ei_skip_term(const char* buf, int* index); +int ei_cmp_refs(erlang_ref *a, erlang_ref *b); +int ei_cmp_pids(erlang_pid *a, erlang_pid *b); +int ei_cmp_ports(erlang_port *a, erlang_port *b); + /*************************************************************************** * * Hash types needed by registry types diff --git a/lib/erl_interface/src/Makefile.in b/lib/erl_interface/src/Makefile.in index 5a07b5542a..7ce6ef46f9 100644 --- a/lib/erl_interface/src/Makefile.in +++ b/lib/erl_interface/src/Makefile.in @@ -394,7 +394,8 @@ MISCSRC = \ misc/get_type.c \ misc/show_msg.c \ misc/ei_compat.c \ - misc/ei_init.c + misc/ei_init.c \ + mixc/ei_cmp_nc.c REGISTRYSRC = \ registry/hash_dohash.c \ diff --git a/lib/erl_interface/src/misc/ei_cmp_nc.c b/lib/erl_interface/src/misc/ei_cmp_nc.c new file mode 100644 index 0000000000..73650f429f --- /dev/null +++ b/lib/erl_interface/src/misc/ei_cmp_nc.c @@ -0,0 +1,117 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2020. 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% + * + */ + +#include <string.h> +#include "eidef.h" + +/* + * Comparison of node container types (pid, port, ref). Comparison + * done the same way as ERTS compare them. In ref and port case, + * node info is compared before other data, and in pid case node + * info is compared after other data. + */ + +static int cmp_nodes(char *aname, unsigned int acreation, + char *bname, unsigned int bcreation) +{ + int differ = strcmp(aname, bname); + if (differ) + return differ; + if (acreation == bcreation) + return 0; + if (acreation < bcreation) + return -1; + return 1; +} + +int +ei_cmp_refs(erlang_ref *a, erlang_ref *b) +{ + int differ; + int i, alen, blen; + unsigned int *anum, *bnum; + + differ = cmp_nodes(a->node, a->creation, b->node, b->creation); + if (differ) + return differ; + + alen = a->len; + blen = b->len; + + anum = &a->n[0]; + bnum = &b->n[0]; + + if (alen != blen) { + if (alen > blen) { + do { + if (anum[alen - 1] != 0) + return 1; + alen--; + } while (alen > blen); + } + else { + do { + if (bnum[blen - 1] != 0) + return -1; + blen--; + } while (alen < blen); + } + } + + for (i = alen - 1; i >= 0; i--) + if (anum[i] != bnum[i]) + return anum[i] < bnum[i] ? -1 : 1; + + return 0; +} + +int +ei_cmp_pids(erlang_pid *a, erlang_pid *b) +{ + int differ; + + if (a->serial != b->serial) + return a->serial < b->serial ? -1 : 1; + + if (a->num != b->num) + return a->num < b->num ? -1 : 1; + + differ = cmp_nodes(a->node, a->creation, b->node, b->creation); + if (differ) + return differ; + + return 0; +} + +int +ei_cmp_ports(erlang_port *a, erlang_port *b) +{ + int differ; + + differ = cmp_nodes(a->node, a->creation, b->node, b->creation); + if (differ) + return differ; + + if (a->id != b->id) + return a->id < b->id ? -1 : 1; + + return 0; +} diff --git a/lib/erl_interface/test/Makefile b/lib/erl_interface/test/Makefile index 1816c73d4b..bdfedecc66 100644 --- a/lib/erl_interface/test/Makefile +++ b/lib/erl_interface/test/Makefile @@ -24,7 +24,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk # Target Specs # ---------------------------------------------------- -MODULES= \ +EI_MODULES= \ ei_accept_SUITE \ ei_connect_SUITE \ ei_decode_SUITE \ @@ -38,12 +38,16 @@ MODULES= \ port_call_SUITE \ runner +ERTS_MODULES= erts_test_utils + +MODULES=$(EI_MODULES) $(ERTS_MODULES) + SPEC_FILES = \ erl_interface.spec erl_interface_smoke.spec COVER_FILE = erl_interface.cover -ERL_FILES = $(MODULES:%=%.erl) +ERL_FILES = $(EI_MODULES:%=%.erl) $(ERTS_MODULES:%=$(ERL_TOP)/erts/emulator/test/%.erl) # ---------------------------------------------------- # Release directory specification diff --git a/lib/erl_interface/test/ei_decode_SUITE.erl b/lib/erl_interface/test/ei_decode_SUITE.erl index cde6a687eb..fee99aba7c 100644 --- a/lib/erl_interface/test/ei_decode_SUITE.erl +++ b/lib/erl_interface/test/ei_decode_SUITE.erl @@ -34,7 +34,8 @@ test_ei_decode_nonoptimal/1, test_ei_decode_misc/1, test_ei_decode_utf8_atom/1, - test_ei_decode_iodata/1]). + test_ei_decode_iodata/1, + test_ei_cmp_nc/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -43,7 +44,7 @@ all() -> test_ei_decode_longlong, test_ei_decode_ulonglong, test_ei_decode_char, test_ei_decode_nonoptimal, test_ei_decode_misc, test_ei_decode_utf8_atom, - test_ei_decode_iodata]. + test_ei_decode_iodata, test_ei_cmp_nc]. init_per_testcase(Case, Config) -> runner:init_per_testcase(?MODULE, Case, Config). @@ -280,6 +281,97 @@ check_decode_iodata(P, Data, Valid) -> %% ######################################################################## %% +%% Should be moved to its own suite... + +test_ei_cmp_nc(Config) when is_list(Config) -> + P = runner:start(Config, ?test_ei_cmp_nc), + R0 = make_ref(), + R1 = make_ref(), + check_cmp(P, R0, R0), + check_cmp(P, R0, R1), + check_cmp(P, R1, R0), + check_cmp(P, mk_ref({a@b, 4711}, [17, 17, 17]), mk_ref({a@c, 4711}, [17, 17, 17])), + check_cmp(P, mk_ref({a@b, 4711}, [17, 17, 17]), mk_ref({a@d, 4711}, [17, 17, 17])), + check_cmp(P, mk_ref({a@b, 4711}, [17, 17, 17]), mk_ref({a@bc, 4711}, [17, 17, 17])), + check_cmp(P, mk_ref({a@b, 4712}, [17, 17, 17]), mk_ref({a@b, 4711}, [17, 17, 17])), + check_cmp(P, mk_ref({a@b, 4711}, [17, 17, 17]), mk_ref({a@b, 4711}, [18, 17, 17])), + check_cmp(P, mk_ref({a@b, 4711}, [17, 17, 17]), mk_ref({a@b, 4711}, [17, 18, 17])), + check_cmp(P, mk_ref({a@b, 4711}, [17, 17, 17]), mk_ref({a@b, 4711}, [17, 17, 18])), + check_cmp(P, mk_ref({a@b, 4711}, [0, 17]), mk_ref({a@b, 4711}, [18])), + check_cmp(P, mk_ref({a@b, 4711}, [17, 17]), mk_ref({a@b, 4711}, [17, 18])), + check_cmp(P, mk_ref({a@b, 4711}, [0, 0, 0]), mk_ref({a@b, 4711}, [17])), + check_cmp(P, mk_ref({a@b, 4711}, [0, 0, 17]), mk_ref({a@b, 4711}, [18, 17])), + check_cmp(P, mk_ref({a@b, 4711}, [0, 17, 17]), mk_ref({a@b, 4711}, [18])), + + check_cmp(P, self(), self()), + check_cmp(P, self(), whereis(file_server_2)), + check_cmp(P, whereis(file_server_2), self()), + check_cmp(P, mk_pid({a@b, 4711}, 17, 17), mk_pid({a@c, 4711}, 17, 17)), + check_cmp(P, mk_pid({a@b, 4711}, 17, 17), mk_pid({a@d, 4711}, 17, 17)), + check_cmp(P, mk_pid({a@b, 4711}, 17, 17), mk_pid({a@bc, 4711}, 17, 17)), + check_cmp(P, mk_pid({a@b, 4712}, 17, 17), mk_pid({a@b, 4711}, 17, 17)), + check_cmp(P, mk_pid({a@b, 4711}, 17, 17), mk_pid({a@b, 4711}, 18, 17)), + check_cmp(P, mk_pid({a@b, 4711}, 17, 17), mk_pid({a@b, 4711}, 17, 18)), + + Prt0 = open_port({spawn, "true"},[]), + Prt1 = open_port({spawn, "true"},[]), + + check_cmp(P, Prt0, Prt0), + check_cmp(P, Prt1, Prt0), + check_cmp(P, Prt0, Prt1), + check_cmp(P, mk_port({a@b, 4711}, 17), mk_port({a@b, 4711}, 17)), + check_cmp(P, mk_port({a@b, 4711}, 17), mk_port({a@d, 4711}, 17)), + check_cmp(P, mk_port({a@b, 4711}, 17), mk_port({a@bc, 4711}, 17)), + check_cmp(P, mk_port({a@b, 4712}, 17), mk_port({a@b, 4711}, 17)), + check_cmp(P, mk_port({a@b, 4711}, 17), mk_port({a@b, 4711}, 18)), + + send_raw(P, <<"done">>), + runner:recv_eot(P), + ok. + +mk_pid(Node, Num, Ser) -> + erts_test_utils:mk_ext_pid(Node, Num, Ser). + +mk_port(Node, Id) -> + erts_test_utils:mk_ext_port(Node, Id). + +mk_ref(Node, Numbers) -> + erts_test_utils:mk_ext_ref(Node, Numbers). + +check_cmp(P, A, B) when is_pid(A), is_pid(B) -> + check_cmp(P, {cmp_pids, A, B}); +check_cmp(P, A, B) when is_port(A), is_port(B) -> + check_cmp(P, {cmp_ports, A, B}); +check_cmp(P, A, B) when is_reference(A), is_reference(B) -> + check_cmp(P, {cmp_refs, A, B}). + +check_cmp(P, {_, A, B} = Data) -> + io:format("~n~nChecking: ~p~n", [Data]), + send_term_as_binary(P, Data), + {term, Res} = runner:get_term(P), + io:format("Res = ~p~n", [Res]), + case {{ei_cmp, Res}, {erlang, cmp_nc(A, B)}} of + {{ei_cmp, 0}, {erlang, equal}} -> + ok; + {{ei_cmp, Cmp}, {erlang, less_than}} when is_integer(Cmp), + Cmp < 0 -> + ok; + {{ei_cmp, Cmp}, {erlang, larger_than}} when is_integer(Cmp), + Cmp > 0 -> ok; + Fail -> + ct:fail(Fail) + end. + +cmp_nc(A, A) -> + equal; +cmp_nc(A, B) when A < B -> + less_than; +cmp_nc(_, _) -> + larger_than. + + +%% ######################################################################## %% + send_term_as_binary(Port, Term) when is_port(Port) -> Port ! {self(), {command, term_to_binary(Term)}}. diff --git a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c index bf51e3db87..9eed34b0dd 100644 --- a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c +++ b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c @@ -859,6 +859,87 @@ TESTCASE(test_ei_decode_iodata) /* ******************************************************************** */ +/* + * Does not belong here move to its own suite... + */ +TESTCASE(test_ei_cmp_nc) +{ + char *buf = NULL; + ei_init(); + + while (1) { + int len, index, arity; + char atom[MAXATOMLEN_UTF8]; + ei_x_buff x; + + if (buf) + free_packet(buf); + buf = read_packet(&len); + + if (len == 4 + && buf[0] == 'd' + && buf[1] == 'o' + && buf[2] == 'n' + && buf[3] == 'e') { + break; + } + + ei_x_new_with_version(&x); + index = 0; + if (ei_decode_version(buf, &index, NULL) + || ei_decode_tuple_header(buf, &index, &arity) + || (arity != 3) + || ei_decode_atom(buf, &index, atom)) { + ei_x_encode_atom(&x, "decode_tuple_failed"); + } + else if (strcmp(atom, "cmp_pids") == 0) { + erlang_pid a, b; + if (ei_decode_pid(buf, &index, &a) + || ei_decode_pid(buf, &index, &b)) { + ei_x_encode_atom(&x, "decode_pids_failed"); + } + else { + long res = (long) ei_cmp_pids(&a, &b); + ei_x_encode_long(&x, res); + } + } + else if (strcmp(atom, "cmp_ports") == 0) { + erlang_port a, b; + if (ei_decode_port(buf, &index, &a) + || ei_decode_port(buf, &index, &b)) { + ei_x_encode_atom(&x, "decode_ports_failed"); + } + else { + long res = (long) ei_cmp_ports(&a, &b); + ei_x_encode_long(&x, res); + } + } + else if (strcmp(atom, "cmp_refs") == 0) { + erlang_ref a, b; + if (ei_decode_ref(buf, &index, &a) + || ei_decode_ref(buf, &index, &b)) { + ei_x_encode_atom(&x, "decode_refs_failed"); + } + else { + long res = (long) ei_cmp_refs(&a, &b); + ei_x_encode_long(&x, res); + } + } + else { + ei_x_encode_atom(&x, "unexpected_operation"); + } + + send_bin_term(&x); + ei_x_free(&x); + } + + if (buf) + free_packet(buf); + report(1); +} + +/* ******************************************************************** */ + int ei_decode_my_atom_as(const char *buf, int *index, char *to, struct my_atom *atom) { erlang_char_encoding was,result; |