summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRickard Green <rickard@erlang.org>2020-04-29 22:09:04 +0200
committerRickard Green <rickard@erlang.org>2020-05-04 11:56:05 +0200
commitcd3cbb2f519a8868d85b42b7514d4ad69cb06850 (patch)
treeea3de78bef5cf87d0f9bceaebe527a1c9e3c3602
parent337e253aeabe4259ad215b31f6b29a9cb519c415 (diff)
downloaderlang-cd3cbb2f519a8868d85b42b7514d4ad69cb06850.tar.gz
Introduce ei_cmp_pids(), ei_cmp_ports(), and ei_cmp_refs()
-rw-r--r--lib/erl_interface/doc/src/ei.xml51
-rw-r--r--lib/erl_interface/include/ei.h4
-rw-r--r--lib/erl_interface/src/Makefile.in3
-rw-r--r--lib/erl_interface/src/misc/ei_cmp_nc.c117
-rw-r--r--lib/erl_interface/test/Makefile8
-rw-r--r--lib/erl_interface/test/ei_decode_SUITE.erl96
-rw-r--r--lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c81
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;