aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.in6
-rw-r--r--OTP_VERSION2
-rwxr-xr-xerts/autoconf/configure.vxworks1
-rw-r--r--erts/autoconf/vxworks/sed.general2
-rw-r--r--erts/doc/src/persistent_term.xml13
-rw-r--r--erts/emulator/beam/dist.c42
-rw-r--r--erts/emulator/beam/erl_alloc.c15
-rw-r--r--erts/emulator/beam/erl_alloc.types1
-rw-r--r--erts/emulator/beam/erl_alloc_util.c8
-rw-r--r--erts/emulator/beam/erl_alloc_util.h5
-rw-r--r--erts/emulator/beam/erl_bif_info.c13
-rw-r--r--erts/emulator/beam/erl_bif_persistent.c530
-rw-r--r--erts/emulator/beam/erl_db.c13
-rw-r--r--erts/emulator/beam/erl_db.h1
-rw-r--r--erts/emulator/beam/erl_db_catree.c14
-rw-r--r--erts/emulator/beam/erl_db_catree.h1
-rw-r--r--erts/emulator/beam/erl_db_util.h1
-rw-r--r--erts/emulator/beam/erl_message.c3
-rw-r--r--erts/emulator/beam/erl_nif.c11
-rw-r--r--erts/emulator/beam/erl_process.c14
-rw-r--r--erts/emulator/beam/erl_trace.c6
-rw-r--r--erts/emulator/beam/external.c174
-rw-r--r--erts/emulator/beam/external.h15
-rw-r--r--erts/emulator/beam/io.c5
-rw-r--r--erts/emulator/test/binary_SUITE.erl56
-rw-r--r--erts/emulator/test/distribution_SUITE.erl166
-rw-r--r--erts/emulator/test/dump_SUITE.erl51
-rw-r--r--erts/emulator/test/persistent_term_SUITE.erl73
-rw-r--r--erts/etc/unix/cerl.src2
-rw-r--r--erts/etc/unix/etp-commands.in8
-rw-r--r--erts/etc/unix/to_erl.c2
-rw-r--r--erts/test/z_SUITE.erl4
-rw-r--r--lib/compiler/src/beam_validator.erl22
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl25
-rw-r--r--lib/crypto/c_src/algorithms.c81
-rw-r--r--lib/crypto/c_src/algorithms.h7
-rw-r--r--lib/crypto/c_src/atoms.c2
-rw-r--r--lib/crypto/c_src/atoms.h1
-rw-r--r--lib/crypto/c_src/cipher.c46
-rw-r--r--lib/crypto/c_src/crypto.c7
-rw-r--r--lib/crypto/src/crypto.erl158
-rw-r--r--lib/crypto/test/crypto_SUITE.erl78
-rw-r--r--lib/dialyzer/src/dialyzer.erl7
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl7
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/fun_app_args3
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/fun_app_args.erl12
-rw-r--r--lib/eldap/src/eldap.erl30
-rw-r--r--lib/erl_docgen/src/docgen_edoc_xml_cb.erl1
-rw-r--r--lib/erl_interface/doc/src/ei.xml61
-rw-r--r--lib/erl_interface/include/ei.h40
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c18
-rw-r--r--lib/erl_interface/src/connect/ei_connect_int.h2
-rw-r--r--lib/erl_interface/src/decode/decode_binary.c36
-rw-r--r--lib/erl_interface/src/decode/decode_fun.c98
-rw-r--r--lib/erl_interface/src/decode/decode_skip.c16
-rw-r--r--lib/erl_interface/src/encode/encode_binary.c24
-rw-r--r--lib/erl_interface/src/encode/encode_fun.c114
-rw-r--r--lib/erl_interface/src/legacy/erl_eterm.c14
-rw-r--r--lib/erl_interface/src/misc/ei_compat.c17
-rw-r--r--lib/erl_interface/src/misc/ei_decode_term.c8
-rw-r--r--lib/erl_interface/src/misc/ei_internal.h2
-rw-r--r--lib/erl_interface/src/misc/ei_printterm.c32
-rw-r--r--lib/erl_interface/src/misc/ei_x_encode.c10
-rw-r--r--lib/erl_interface/src/misc/get_type.c11
-rw-r--r--lib/erl_interface/src/misc/show_msg.c15
-rw-r--r--lib/erl_interface/src/prog/ei_fake_prog.c1
-rw-r--r--lib/erl_interface/src/registry/reg_dump.c2
-rw-r--r--lib/erl_interface/test/all_SUITE_data/ei_runner.h1
-rw-r--r--lib/erl_interface/test/ei_accept_SUITE.erl21
-rw-r--r--lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c22
-rw-r--r--lib/erl_interface/test/ei_connect_SUITE.erl5
-rw-r--r--lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c13
-rw-r--r--lib/erl_interface/test/ei_decode_SUITE.erl3
-rw-r--r--lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c193
-rw-r--r--lib/erl_interface/test/ei_decode_encode_SUITE.erl2
-rw-r--r--lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c31
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java3
-rw-r--r--lib/jinterface/test/nc_SUITE.erl3
-rw-r--r--lib/kernel/doc/src/gen_tcp.xml6
-rw-r--r--lib/kernel/doc/src/inet.xml3
-rw-r--r--lib/public_key/doc/src/public_key.xml4
-rw-r--r--lib/public_key/src/pubkey_cert.erl4
-rw-r--r--lib/public_key/src/public_key.erl1
-rw-r--r--lib/ssh/doc/src/notes.xml22
-rw-r--r--lib/ssh/src/ssh.hrl2
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl8
-rw-r--r--lib/ssh/src/ssh_dbg.erl73
-rw-r--r--lib/ssh/src/ssh_sftpd.erl2
-rw-r--r--lib/ssh/src/ssh_transport.erl83
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl2
-rw-r--r--lib/ssh/test/ssh_bench_SUITE.erl10
-rw-r--r--lib/ssh/test/ssh_chan_behaviours_SUITE.erl4
-rw-r--r--lib/ssh/test/ssh_chan_behaviours_client.erl2
-rw-r--r--lib/ssh/test/ssh_chan_behaviours_server.erl2
-rw-r--r--lib/ssh/test/ssh_compat_SUITE.erl17
-rw-r--r--lib/ssh/test/ssh_options_SUITE.erl2
-rw-r--r--lib/ssh/test/ssh_test_lib.erl4
-rw-r--r--lib/ssh/test/ssh_trpt_test_lib.erl69
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/notes.xml17
-rw-r--r--lib/ssl/doc/src/ssl.xml346
-rw-r--r--lib/ssl/doc/src/standards_compliance.xml74
-rw-r--r--lib/ssl/src/ssl.erl391
-rw-r--r--lib/ssl/src/ssl_cipher.hrl4
-rw-r--r--lib/ssl/src/ssl_cipher_format.erl24
-rw-r--r--lib/ssl/src/ssl_connection.erl6
-rw-r--r--lib/ssl/src/ssl_handshake.erl2
-rw-r--r--lib/ssl/src/ssl_internal.hrl54
-rw-r--r--lib/ssl/src/tls_handshake.erl2
-rw-r--r--lib/ssl/src/tls_record.erl20
-rw-r--r--lib/ssl/src/tls_v1.erl8
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE.erl26
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl17
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_test_lib.erl16
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/test/ets_SUITE.erl20
-rw-r--r--lib/tools/test/Makefile3
-rw-r--r--lib/tools/test/emacs_SUITE.erl10
-rw-r--r--lib/tools/test/instrument_SUITE.erl13
-rw-r--r--lib/tools/test/prof_bench_SUITE.erl126
-rw-r--r--lib/tools/test/prof_bench_SUITE_data/sofs_copy.erl2809
-rw-r--r--lib/tools/test/tools_bench.spec1
-rw-r--r--make/otp_patch_solve_forward_merge_version2
-rw-r--r--otp_versions.table2
-rw-r--r--system/doc/system_principles/create_target.xmlsrc2
126 files changed, 5569 insertions, 1315 deletions
diff --git a/Makefile.in b/Makefile.in
index 6cbd92a55b..25003f47a9 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -485,11 +485,11 @@ libs:
ifeq ($(OTP_SMALL_BUILD),true)
$(make_verbose)cd lib && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
- $(MAKE) opt
+ $(MAKE) $(TYPE)
else
$(make_verbose)cd lib && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
- $(MAKE) opt BUILD_ALL=true
+ $(MAKE) $(TYPE) BUILD_ALL=true
$(V_at)test -f $(ERL_TOP)/make/otp_built || echo "OTP built" > $(ERL_TOP)/make/otp_built
endif
@@ -498,7 +498,7 @@ APPS=$(patsubst $(ERL_TOP)/lib/%/doc,%,$(wildcard $(ERL_TOP)/lib/*/doc))
$(APPS):
$(make_verbose)cd lib/$@ && \
ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
- $(MAKE) opt BUILD_ALL=true
+ $(MAKE) $(TYPE) BUILD_ALL=true
preloaded:
$(make_verbose)cd erts/preloaded/src && \
diff --git a/OTP_VERSION b/OTP_VERSION
index a5a30a3cfe..9664138791 100644
--- a/OTP_VERSION
+++ b/OTP_VERSION
@@ -1 +1 @@
-22.0-rc2
+22.0-rc3
diff --git a/erts/autoconf/configure.vxworks b/erts/autoconf/configure.vxworks
index 1893f3f7e0..c3bdfd0095 100755
--- a/erts/autoconf/configure.vxworks
+++ b/erts/autoconf/configure.vxworks
@@ -135,6 +135,7 @@ for file in $CONFIG_FILES; do
-e "s,@HOST_TYPE@,$HOST_TYPE,g" \
-e "s,@WIND_BASE@,$WIND_BASE,g" \
-e "s,@TARGET@,$target,g" \
+ -e "s,@ENV_CFLAGS@,$CFLAGS,g" \
$in_file > $new_name
done
diff --git a/erts/autoconf/vxworks/sed.general b/erts/autoconf/vxworks/sed.general
index d32fbdc5c0..ffd5a8133c 100644
--- a/erts/autoconf/vxworks/sed.general
+++ b/erts/autoconf/vxworks/sed.general
@@ -111,7 +111,7 @@ s|@erlexec@|erl.exec|
s|@EMU_LIBOBJS@||
# General CFLAGS
-s|@GENERAL_CFLAGS@|-DHAVE_LOCALTIME_R -DHAVE_GMTIME_R -DENABLE_ELIB_MALLOC -DELIB_HEAP_USER -DELIB_SORTED_BLOCKS -DWORDS_BIGENDIAN -DELIB_DONT_INITIALIZE -DSIZEOF_CHAR=1 -DSIZEOF_SHORT=2 -DSIZEOF_INT=4 -DSIZEOF_LONG=4 -DSIZEOF_LONG_LONG=8 -DSIZEOF_VOID_P=4 -DERTS_USE_PORT_TASKS=1|g
+s|@GENERAL_CFLAGS@|@ENV_CFLAGS@ -DHAVE_LOCALTIME_R -DHAVE_GMTIME_R -DENABLE_ELIB_MALLOC -DELIB_HEAP_USER -DELIB_SORTED_BLOCKS -DWORDS_BIGENDIAN -DELIB_DONT_INITIALIZE -DSIZEOF_CHAR=1 -DSIZEOF_SHORT=2 -DSIZEOF_INT=4 -DSIZEOF_LONG=4 -DSIZEOF_LONG_LONG=8 -DSIZEOF_VOID_P=4 -DERTS_USE_PORT_TASKS=1|g
s|@WFLAGS@||
# Thread flags for eidefs.mk (erl_interface)
diff --git a/erts/doc/src/persistent_term.xml b/erts/doc/src/persistent_term.xml
index 9d3c9afd80..672b00a83a 100644
--- a/erts/doc/src/persistent_term.xml
+++ b/erts/doc/src/persistent_term.xml
@@ -121,19 +121,6 @@
</section>
<section>
- <title>Warning For Many Persistent Terms</title>
- <p>The runtime system will send a warning report to the
- error logger if more than 20000 persistent terms have been
- created. It will look like this:</p>
-
-<pre>
-More than 20000 persistent terms have been created.
-It is recommended to avoid creating an excessive number of
-persistent terms, as creation and deletion of persistent terms
-will be slower as the number of persistent terms increases.</pre>
- </section>
-
- <section>
<title>Best Practices for Using Persistent Terms</title>
<p>It is recommended to use keys like <c>?MODULE</c> or
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 30fe13fad3..27599f38e0 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -2307,8 +2307,18 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
ctx->data_size = ctx->max_finalize_prepend;
erts_reset_atom_cache_map(ctx->acmp);
- erts_encode_dist_ext_size(ctx->ctl, ctx->flags, ctx->acmp, &ctx->data_size);
+ switch (erts_encode_dist_ext_size(ctx->ctl, ctx->flags,
+ ctx->acmp, &ctx->data_size)) {
+ case ERTS_EXT_SZ_OK:
+ break;
+ case ERTS_EXT_SZ_SYSTEM_LIMIT:
+ retval = ERTS_DSIG_SEND_TOO_LRG;
+ goto done;
+ case ERTS_EXT_SZ_YIELD:
+ ERTS_INTERNAL_ERROR("Unexpected yield result");
+ break;
+ }
if (is_non_value(ctx->msg)) {
ctx->phase = ERTS_DSIG_SEND_PHASE_ALLOC;
break;
@@ -2318,17 +2328,31 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
ctx->u.sc.level = 0;
ctx->phase = ERTS_DSIG_SEND_PHASE_MSG_SIZE;
- case ERTS_DSIG_SEND_PHASE_MSG_SIZE:
- if (!ctx->no_trap) {
- if (erts_encode_dist_ext_size_int(ctx->msg, ctx, &ctx->data_size)) {
- retval = ERTS_DSIG_SEND_CONTINUE;
- goto done;
- }
- } else {
- erts_encode_dist_ext_size(ctx->msg, ctx->flags, ctx->acmp, &ctx->data_size);
+ case ERTS_DSIG_SEND_PHASE_MSG_SIZE: {
+ ErtsExtSzRes sz_res;
+ sz_res = (!ctx->no_trap
+ ? erts_encode_dist_ext_size_ctx(ctx->msg,
+ ctx,
+ &ctx->data_size)
+ : erts_encode_dist_ext_size(ctx->msg,
+ ctx->flags,
+ ctx->acmp,
+ &ctx->data_size));
+ switch (sz_res) {
+ case ERTS_EXT_SZ_OK:
+ break;
+ case ERTS_EXT_SZ_SYSTEM_LIMIT:
+ retval = ERTS_DSIG_SEND_TOO_LRG;
+ goto done;
+ case ERTS_EXT_SZ_YIELD:
+ if (ctx->no_trap)
+ ERTS_INTERNAL_ERROR("Unexpected yield result");
+ retval = ERTS_DSIG_SEND_CONTINUE;
+ goto done;
}
ctx->phase = ERTS_DSIG_SEND_PHASE_ALLOC;
+ }
case ERTS_DSIG_SEND_PHASE_ALLOC:
erts_finalize_atom_cache_map(ctx->acmp, ctx->flags);
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index e6169ebeaa..b9f0334172 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -3904,7 +3904,7 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func)
{
Uint sz;
Uint found_type;
- UWord pre_pattern;
+ UWord pre_pattern, expected_pattern;
UWord post_pattern;
UWord *ui_ptr;
#ifdef HARD_DEBUG
@@ -3914,6 +3914,8 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func)
if (!ptr)
return NULL;
+ expected_pattern = MK_PATTERN(n);
+
ui_ptr = (UWord *) ptr;
pre_pattern = *(--ui_ptr);
*size = sz = *(--ui_ptr);
@@ -3922,7 +3924,13 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func)
#endif
found_type = GET_TYPE_OF_PATTERN(pre_pattern);
- if (pre_pattern != MK_PATTERN(n)) {
+
+ if (found_type != n) {
+ erts_exit(ERTS_ABORT_EXIT, "ERROR: Miss matching allocator types"
+ " used in alloc and free\n");
+ }
+
+ if (pre_pattern != expected_pattern) {
if ((FIXED_FENCE_PATTERN_MASK & pre_pattern) != FIXED_FENCE_PATTERN)
erts_exit(ERTS_ABORT_EXIT,
"ERROR: Fence at beginning of memory block (p=0x%u) "
@@ -3932,8 +3940,7 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func)
sys_memcpy((void *) &post_pattern, (void *) (((char *)ptr)+sz), sizeof(UWord));
- if (post_pattern != MK_PATTERN(n)
- || pre_pattern != post_pattern) {
+ if (post_pattern != expected_pattern || pre_pattern != post_pattern) {
char fbuf[10];
char obuf[10];
char *ftype;
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index e7329daa2d..92e5069c71 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -282,6 +282,7 @@ type ENVIRONMENT SYSTEM SYSTEM environment
type PERSISTENT_TERM LONG_LIVED CODE persisten_term
type PERSISTENT_LOCK_Q SHORT_LIVED SYSTEM persistent_lock_q
+type PERSISTENT_TERM_TMP SHORT_LIVED SYSTEM persistent_term_tmp_table
#
# Types used for special emulators
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index 8d4464969a..25ac3bc5af 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -6567,6 +6567,14 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init)
__FILE__, __LINE__);
}
+ /* The various fields packed into the header word must not overlap */
+ ERTS_CT_ASSERT(!(MBC_ABLK_OFFSET_MASK & MBC_ABLK_SZ_MASK));
+ ERTS_CT_ASSERT(!(MBC_ABLK_OFFSET_MASK & BLK_FLG_MASK));
+ ERTS_CT_ASSERT(!(MBC_ABLK_SZ_MASK & BLK_FLG_MASK));
+ ERTS_CT_ASSERT(!(MBC_FBLK_SZ_MASK & BLK_FLG_MASK));
+ ERTS_CT_ASSERT(!(SBC_BLK_SZ_MASK & BLK_FLG_MASK));
+ ERTS_CT_ASSERT(!(CRR_SZ_MASK & CRR_FLG_MASK));
+
if (!initialized)
goto error;
diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h
index ea1afe8f58..b34916271e 100644
--- a/erts/emulator/beam/erl_alloc_util.h
+++ b/erts/emulator/beam/erl_alloc_util.h
@@ -334,8 +334,9 @@ void erts_alcu_sched_spec_data_init(struct ErtsSchedulerData_ *esdp);
#endif
#if MBC_ABLK_OFFSET_BITS
-# define MBC_ABLK_OFFSET_SHIFT (sizeof(UWord)*8 - MBC_ABLK_OFFSET_BITS)
-# define MBC_ABLK_OFFSET_MASK ((~((UWord)0) << MBC_ABLK_OFFSET_SHIFT) & ~BLK_FLG_MASK)
+/* The shift is reduced by 1 since the highest bit is used for a flag. */
+# define MBC_ABLK_OFFSET_SHIFT (sizeof(UWord)*8 - 1 - MBC_ABLK_OFFSET_BITS)
+# define MBC_ABLK_OFFSET_MASK (((1ul << MBC_ABLK_OFFSET_BITS) - 1ul) << MBC_ABLK_OFFSET_SHIFT)
# define MBC_ABLK_SZ_MASK (~MBC_ABLK_OFFSET_MASK & ~BLK_FLG_MASK)
#else
# define MBC_ABLK_SZ_MASK (~BLK_FLG_MASK)
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 7ff345a54b..0339589b79 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -4236,7 +4236,10 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
Uint dflags = (TERM_TO_BINARY_DFLAGS
& ~DFLAG_EXPORT_PTR_TAG
& ~DFLAG_BIT_BINARIES);
- BIF_RET(erts_term_to_binary(BIF_P, tp[2], 0, dflags));
+ Eterm res = erts_term_to_binary(BIF_P, tp[2], 0, dflags);
+ if (is_value(res))
+ BIF_RET(res);
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
}
else if (ERTS_IS_ATOM_STR("dist_ctrl", tp[1])) {
Eterm res = am_undefined;
@@ -4693,6 +4696,14 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
BIF_RET(am_ok);
}
}
+ else if (ERTS_IS_ATOM_STR("ets_debug_random_split_join", BIF_ARG_1)) {
+ if (is_tuple(BIF_ARG_2)) {
+ Eterm* tpl = tuple_val(BIF_ARG_2);
+
+ if (erts_ets_debug_random_split_join(tpl[1], tpl[2] == am_true))
+ BIF_RET(am_ok);
+ }
+ }
else if (ERTS_IS_ATOM_STR("mbuf", BIF_ARG_1)) {
Uint sz = size_object(BIF_ARG_2);
ErlHeapFragment* frag = new_message_buffer(sz);
diff --git a/erts/emulator/beam/erl_bif_persistent.c b/erts/emulator/beam/erl_bif_persistent.c
index 5a78a043ce..f38e0cc5cb 100644
--- a/erts/emulator/beam/erl_bif_persistent.c
+++ b/erts/emulator/beam/erl_bif_persistent.c
@@ -37,15 +37,6 @@
#include "erl_binary.h"
/*
- * The limit for the number of persistent terms before
- * a warning is issued.
- */
-
-#define WARNING_LIMIT 20000
-#define XSTR(s) STR(s)
-#define STR(s) #s
-
-/*
* Parameters for the hash table.
*/
#define INITIAL_SIZE 8
@@ -73,14 +64,69 @@ typedef struct trap_data {
Uint memory; /* Used by info/0 to count used memory */
} TrapData;
+typedef enum {
+ ERTS_PERSISTENT_TERM_CPY_PLACE_START,
+ ERTS_PERSISTENT_TERM_CPY_PLACE_1,
+ ERTS_PERSISTENT_TERM_CPY_PLACE_2,
+ ERTS_PERSISTENT_TERM_CPY_PLACE_3
+} ErtsPersistentTermCpyTableLocation;
+
+typedef enum {
+ ERTS_PERSISTENT_TERM_CPY_NO_REHASH = 0,
+ ERTS_PERSISTENT_TERM_CPY_REHASH = 1,
+ ERTS_PERSISTENT_TERM_CPY_TEMP = 2
+} ErtsPersistentTermCpyTableType;
+
+typedef struct {
+ HashTable* old_table; /* in param */
+ Uint new_size; /* in param */
+ ErtsPersistentTermCpyTableType copy_type; /* in param */
+ Uint max_iterations; /* in param */
+ ErtsPersistentTermCpyTableLocation location; /* in/out param */
+ Uint iterations_done; /* in/out param */
+ Uint total_iterations_done; /* in/out param */
+ HashTable* new_table; /* out param */
+} ErtsPersistentTermCpyTableCtx;
+
+typedef enum {
+ PUT2_TRAP_LOCATION_NEW_KEY,
+ PUT2_TRAP_LOCATION_REPLACE_VALUE
+} ErtsPersistentTermPut2TrapLocation;
+
+typedef struct {
+ ErtsPersistentTermPut2TrapLocation trap_location;
+ Eterm key;
+ Eterm term;
+ Uint entry_index;
+ HashTable* hash_table;
+ Eterm heap[3];
+ Eterm tuple;
+ ErtsPersistentTermCpyTableCtx cpy_ctx;
+} ErtsPersistentTermPut2Context;
+
+typedef enum {
+ ERASE1_TRAP_LOCATION_TMP_COPY,
+ ERASE1_TRAP_LOCATION_FINAL_COPY
+} ErtsPersistentTermErase1TrapLocation;
+
+typedef struct {
+ ErtsPersistentTermErase1TrapLocation trap_location;
+ Eterm key;
+ HashTable* old_table;
+ HashTable* new_table;
+ Uint entry_index;
+ Eterm old_term;
+ HashTable* tmp_table;
+ ErtsPersistentTermCpyTableCtx cpy_ctx;
+} ErtsPersistentTermErase1Context;
+
/*
* Declarations of local functions.
*/
static HashTable* create_initial_table(void);
static Uint lookup(HashTable* hash_table, Eterm key);
-static HashTable* copy_table(HashTable* old_table, Uint new_size, int rehash);
-static HashTable* tmp_table_copy(HashTable* old_table);
+static HashTable* copy_table(ErtsPersistentTermCpyTableCtx* ctx);
static int try_seize_update_permission(Process* c_p);
static void release_update_permission(int release_updater);
static void table_updater(void* table);
@@ -127,7 +173,6 @@ static Process* updater_process = NULL;
/* Protected by update_table_permission_mtx */
static ErtsThrPrgrLaterOp thr_prog_op;
-static int issued_warning = 0;
/*
* Queue of hash tables to be deleted.
@@ -139,7 +184,7 @@ static HashTable** delete_queue_tail = &delete_queue_head;
/*
* The following variables are only used during crash dumping. They
- * are intialized by erts_init_persistent_dumping().
+ * are initialized by erts_init_persistent_dumping().
*/
ErtsLiteralArea** erts_persistent_areas;
@@ -188,101 +233,181 @@ void erts_init_bif_persistent_term(void)
&persistent_term_info_trap);
}
+/*
+ * Macro used for trapping in persistent_term_put_2 and
+ * persistent_term_erase_1
+ */
+#define TRAPPING_COPY_TABLE(TABLE_DEST, OLD_TABLE, NEW_SIZE, COPY_TYPE, LOC_NAME, TRAP_CODE) \
+ do { \
+ ctx->cpy_ctx = (ErtsPersistentTermCpyTableCtx){ \
+ .old_table = OLD_TABLE, \
+ .new_size = NEW_SIZE, \
+ .copy_type = COPY_TYPE, \
+ .location = ERTS_PERSISTENT_TERM_CPY_PLACE_START \
+ }; \
+ L_ ## LOC_NAME: \
+ ctx->cpy_ctx.max_iterations = MAX(1, max_iterations); \
+ TABLE_DEST = copy_table(&ctx->cpy_ctx); \
+ iterations_until_trap -= ctx->cpy_ctx.total_iterations_done; \
+ if (TABLE_DEST == NULL) { \
+ ctx->trap_location = LOC_NAME; \
+ erts_set_gc_state(BIF_P, 0); \
+ BUMP_ALL_REDS(BIF_P); \
+ TRAP_CODE; \
+ } \
+ } while (0)
+
+static int persistent_term_put_2_ctx_bin_dtor(Binary *context_bin)
+{
+ ErtsPersistentTermPut2Context* ctx = ERTS_MAGIC_BIN_DATA(context_bin);
+ if (ctx->cpy_ctx.new_table != NULL) {
+ erts_free(ERTS_ALC_T_PERSISTENT_TERM, ctx->cpy_ctx.new_table);
+ release_update_permission(0);
+ }
+ return 1;
+}
+/*
+ * A linear congruential generator that is used in the debug emulator
+ * to trap after a random number of iterations in
+ * persistent_term_put_2 and persistent_term_erase_1.
+ *
+ * https://en.wikipedia.org/wiki/Linear_congruential_generator
+ */
+#define GET_SMALL_RANDOM_INT(SEED) \
+ (1103515245 * (SEED) + 12345) % 227
+
BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2)
{
- Eterm key;
- Eterm term;
- Eterm heap[3];
- Eterm tuple;
- HashTable* hash_table;
- Uint term_size;
- Uint lit_area_size;
- ErlOffHeap code_off_heap;
- ErtsLiteralArea* literal_area;
- erts_shcopy_t info;
- Eterm* ptr;
- Uint entry_index;
+ static const Uint ITERATIONS_PER_RED = 32;
+ ErtsPersistentTermPut2Context* ctx;
+ Eterm state_mref = THE_NON_VALUE;
+ long iterations_until_trap;
+ long max_iterations;
+#define PUT_TRAP_CODE \
+ BIF_TRAP2(bif_export[BIF_persistent_term_put_2], BIF_P, state_mref, BIF_ARG_2)
+#define TRAPPING_COPY_TABLE_PUT(TABLE_DEST, OLD_TABLE, NEW_SIZE, COPY_TYPE, LOC_NAME) \
+ TRAPPING_COPY_TABLE(TABLE_DEST, OLD_TABLE, NEW_SIZE, COPY_TYPE, LOC_NAME, PUT_TRAP_CODE)
+
+#ifdef DEBUG
+ (void)ITERATIONS_PER_RED;
+ iterations_until_trap = max_iterations =
+ GET_SMALL_RANDOM_INT(ERTS_BIF_REDS_LEFT(BIF_P) + (Uint)&ctx);
+#else
+ iterations_until_trap = max_iterations =
+ ITERATIONS_PER_RED * ERTS_BIF_REDS_LEFT(BIF_P);
+#endif
+ if (is_internal_magic_ref(BIF_ARG_1) &&
+ (ERTS_MAGIC_BIN_DESTRUCTOR(erts_magic_ref2bin(BIF_ARG_1)) ==
+ persistent_term_put_2_ctx_bin_dtor)) {
+ /* Restore state after a trap */
+ Binary* state_bin;
+ state_mref = BIF_ARG_1;
+ state_bin = erts_magic_ref2bin(state_mref);
+ ctx = ERTS_MAGIC_BIN_DATA(state_bin);
+ ASSERT(BIF_P->flags & F_DISABLE_GC);
+ erts_set_gc_state(BIF_P, 1);
+ switch (ctx->trap_location) {
+ case PUT2_TRAP_LOCATION_NEW_KEY:
+ goto L_PUT2_TRAP_LOCATION_NEW_KEY;
+ case PUT2_TRAP_LOCATION_REPLACE_VALUE:
+ goto L_PUT2_TRAP_LOCATION_REPLACE_VALUE;
+ }
+ } else {
+ /* Save state in magic bin in case trapping is necessary */
+ Eterm* hp;
+ Binary* state_bin = erts_create_magic_binary(sizeof(ErtsPersistentTermPut2Context),
+ persistent_term_put_2_ctx_bin_dtor);
+ hp = HAlloc(BIF_P, ERTS_MAGIC_REF_THING_SIZE);
+ state_mref = erts_mk_magic_ref(&hp, &MSO(BIF_P), state_bin);
+ ctx = ERTS_MAGIC_BIN_DATA(state_bin);
+ /*
+ * IMPORTANT: The following field is used to detect if
+ * persistent_term_put_2_ctx_bin_dtor needs to free memory
+ */
+ ctx->cpy_ctx.new_table = NULL;
+ }
+
if (!try_seize_update_permission(BIF_P)) {
ERTS_BIF_YIELD2(bif_export[BIF_persistent_term_put_2],
BIF_P, BIF_ARG_1, BIF_ARG_2);
}
+ ctx->hash_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
- hash_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
+ ctx->key = BIF_ARG_1;
+ ctx->term = BIF_ARG_2;
- key = BIF_ARG_1;
- term = BIF_ARG_2;
+ ctx->entry_index = lookup(ctx->hash_table, ctx->key);
- entry_index = lookup(hash_table, key);
-
- heap[0] = make_arityval(2);
- heap[1] = key;
- heap[2] = term;
- tuple = make_tuple(heap);
+ ctx->heap[0] = make_arityval(2);
+ ctx->heap[1] = ctx->key;
+ ctx->heap[2] = ctx->term;
+ ctx->tuple = make_tuple(ctx->heap);
- if (is_nil(hash_table->term[entry_index])) {
- Uint size = hash_table->allocated;
- if (MUST_GROW(hash_table)) {
- size *= 2;
+ if (is_nil(ctx->hash_table->term[ctx->entry_index])) {
+ Uint new_size = ctx->hash_table->allocated;
+ if (MUST_GROW(ctx->hash_table)) {
+ new_size *= 2;
}
- hash_table = copy_table(hash_table, size, 0);
- entry_index = lookup(hash_table, key);
- hash_table->num_entries++;
+ TRAPPING_COPY_TABLE_PUT(ctx->hash_table,
+ ctx->hash_table,
+ new_size,
+ ERTS_PERSISTENT_TERM_CPY_NO_REHASH,
+ PUT2_TRAP_LOCATION_NEW_KEY);
+ ctx->entry_index = lookup(ctx->hash_table, ctx->key);
+ ctx->hash_table->num_entries++;
} else {
- Eterm tuple = hash_table->term[entry_index];
+ Eterm tuple = ctx->hash_table->term[ctx->entry_index];
Eterm old_term;
ASSERT(is_tuple_arity(tuple, 2));
old_term = boxed_val(tuple)[2];
- if (EQ(term, old_term)) {
+ if (EQ(ctx->term, old_term)) {
/* Same value. No need to update anything. */
release_update_permission(0);
BIF_RET(am_ok);
} else {
/* Mark the old term for deletion. */
- mark_for_deletion(hash_table, entry_index);
- hash_table = copy_table(hash_table, hash_table->allocated, 0);
+ mark_for_deletion(ctx->hash_table, ctx->entry_index);
+ TRAPPING_COPY_TABLE_PUT(ctx->hash_table,
+ ctx->hash_table,
+ ctx->hash_table->allocated,
+ ERTS_PERSISTENT_TERM_CPY_NO_REHASH,
+ PUT2_TRAP_LOCATION_REPLACE_VALUE);
}
}
- /*
- * Preserve internal sharing in the term by using the
- * sharing-preserving functions. However, literals must
- * be copied in case the module holding them are unloaded.
- */
- INITIALIZE_SHCOPY(info);
- info.copy_literals = 1;
- term_size = copy_shared_calculate(tuple, &info);
- ERTS_INIT_OFF_HEAP(&code_off_heap);
- lit_area_size = ERTS_LITERAL_AREA_ALLOC_SIZE(term_size);
- literal_area = erts_alloc(ERTS_ALC_T_LITERAL, lit_area_size);
- ptr = &literal_area->start[0];
- literal_area->end = ptr + term_size;
- tuple = copy_shared_perform(tuple, term_size, &info, &ptr, &code_off_heap);
- ASSERT(tuple_val(tuple) == literal_area->start);
- literal_area->off_heap = code_off_heap.first;
- DESTROY_SHCOPY(info);
- erts_set_literal_tag(&tuple, literal_area->start, term_size);
- hash_table->term[entry_index] = tuple;
-
- erts_schedule_thr_prgr_later_op(table_updater, hash_table, &thr_prog_op);
- suspend_updater(BIF_P);
-
- /*
- * Issue a warning once if the warning limit has been exceeded.
- */
-
- if (hash_table->num_entries > WARNING_LIMIT && issued_warning == 0) {
- static char w[] =
- "More than " XSTR(WARNING_LIMIT) " persistent terms "
- "have been created.\n"
- "It is recommended to avoid creating an excessive number of\n"
- "persistent terms, as creation and deletion of persistent terms\n"
- "will be slower as the number of persistent terms increases.\n";
- issued_warning = 1;
- erts_send_warning_to_logger_str(BIF_P->group_leader, w);
+ {
+ Uint term_size;
+ Uint lit_area_size;
+ ErlOffHeap code_off_heap;
+ ErtsLiteralArea* literal_area;
+ erts_shcopy_t info;
+ Eterm* ptr;
+ /*
+ * Preserve internal sharing in the term by using the
+ * sharing-preserving functions. However, literals must
+ * be copied in case the module holding them are unloaded.
+ */
+ INITIALIZE_SHCOPY(info);
+ info.copy_literals = 1;
+ term_size = copy_shared_calculate(ctx->tuple, &info);
+ ERTS_INIT_OFF_HEAP(&code_off_heap);
+ lit_area_size = ERTS_LITERAL_AREA_ALLOC_SIZE(term_size);
+ literal_area = erts_alloc(ERTS_ALC_T_LITERAL, lit_area_size);
+ ptr = &literal_area->start[0];
+ literal_area->end = ptr + term_size;
+ ctx->tuple = copy_shared_perform(ctx->tuple, term_size, &info, &ptr, &code_off_heap);
+ ASSERT(tuple_val(ctx->tuple) == literal_area->start);
+ literal_area->off_heap = code_off_heap.first;
+ DESTROY_SHCOPY(info);
+ erts_set_literal_tag(&ctx->tuple, literal_area->start, term_size);
+ ctx->hash_table->term[ctx->entry_index] = ctx->tuple;
+
+ erts_schedule_thr_prgr_later_op(table_updater, ctx->hash_table, &thr_prog_op);
+ suspend_updater(BIF_P);
}
-
+ BUMP_REDS(BIF_P, (max_iterations - iterations_until_trap) / ITERATIONS_PER_RED);
ERTS_BIF_YIELD_RETURN(BIF_P, am_ok);
}
@@ -349,26 +474,84 @@ BIF_RETTYPE persistent_term_get_2(BIF_ALIST_2)
BIF_RET(result);
}
-BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1)
+static int persistent_term_erase_1_ctx_bin_dtor(Binary *context_bin)
{
- Eterm key = BIF_ARG_1;
- HashTable* old_table;
- HashTable* new_table;
- Uint entry_index;
- Eterm old_term;
+ ErtsPersistentTermErase1Context* ctx = ERTS_MAGIC_BIN_DATA(context_bin);
+ if (ctx->cpy_ctx.new_table != NULL) {
+ if (ctx->cpy_ctx.copy_type == ERTS_PERSISTENT_TERM_CPY_TEMP) {
+ erts_free(ERTS_ALC_T_PERSISTENT_TERM_TMP, ctx->cpy_ctx.new_table);
+ } else {
+ erts_free(ERTS_ALC_T_PERSISTENT_TERM, ctx->cpy_ctx.new_table);
+ }
+ if (ctx->tmp_table != NULL) {
+ erts_free(ERTS_ALC_T_PERSISTENT_TERM_TMP, ctx->tmp_table);
+ }
+ release_update_permission(0);
+ }
+ return 1;
+}
+BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1)
+{
+ static const Uint ITERATIONS_PER_RED = 32;
+ ErtsPersistentTermErase1Context* ctx;
+ Eterm state_mref = THE_NON_VALUE;
+ long iterations_until_trap;
+ long max_iterations;
+#ifdef DEBUG
+ (void)ITERATIONS_PER_RED;
+ iterations_until_trap = max_iterations =
+ GET_SMALL_RANDOM_INT(ERTS_BIF_REDS_LEFT(BIF_P) + (Uint)&ctx);
+#else
+ iterations_until_trap = max_iterations =
+ ITERATIONS_PER_RED * ERTS_BIF_REDS_LEFT(BIF_P);
+#endif
+#define ERASE_TRAP_CODE \
+ BIF_TRAP1(bif_export[BIF_persistent_term_erase_1], BIF_P, state_mref);
+#define TRAPPING_COPY_TABLE_ERASE(TABLE_DEST, OLD_TABLE, NEW_SIZE, REHASH, LOC_NAME) \
+ TRAPPING_COPY_TABLE(TABLE_DEST, OLD_TABLE, NEW_SIZE, REHASH, LOC_NAME, ERASE_TRAP_CODE)
+ if (is_internal_magic_ref(BIF_ARG_1) &&
+ (ERTS_MAGIC_BIN_DESTRUCTOR(erts_magic_ref2bin(BIF_ARG_1)) ==
+ persistent_term_erase_1_ctx_bin_dtor)) {
+ /* Restore the state after a trap */
+ Binary* state_bin;
+ state_mref = BIF_ARG_1;
+ state_bin = erts_magic_ref2bin(state_mref);
+ ctx = ERTS_MAGIC_BIN_DATA(state_bin);
+ ASSERT(BIF_P->flags & F_DISABLE_GC);
+ erts_set_gc_state(BIF_P, 1);
+ switch (ctx->trap_location) {
+ case ERASE1_TRAP_LOCATION_TMP_COPY:
+ goto L_ERASE1_TRAP_LOCATION_TMP_COPY;
+ case ERASE1_TRAP_LOCATION_FINAL_COPY:
+ goto L_ERASE1_TRAP_LOCATION_FINAL_COPY;
+ }
+ } else {
+ /* Save state in magic bin in case trapping is necessary */
+ Eterm* hp;
+ Binary* state_bin = erts_create_magic_binary(sizeof(ErtsPersistentTermErase1Context),
+ persistent_term_erase_1_ctx_bin_dtor);
+ hp = HAlloc(BIF_P, ERTS_MAGIC_REF_THING_SIZE);
+ state_mref = erts_mk_magic_ref(&hp, &MSO(BIF_P), state_bin);
+ ctx = ERTS_MAGIC_BIN_DATA(state_bin);
+ /*
+ * IMPORTANT: The following two fields are used to detect if
+ * persistent_term_erase_1_ctx_bin_dtor needs to free memory
+ */
+ ctx->cpy_ctx.new_table = NULL;
+ ctx->tmp_table = NULL;
+ }
if (!try_seize_update_permission(BIF_P)) {
ERTS_BIF_YIELD1(bif_export[BIF_persistent_term_erase_1],
BIF_P, BIF_ARG_1);
}
- old_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
- entry_index = lookup(old_table, key);
- old_term = old_table->term[entry_index];
- if (is_boxed(old_term)) {
+ ctx->key = BIF_ARG_1;
+ ctx->old_table = (HashTable *) erts_atomic_read_nob(&the_hash_table);
+ ctx->entry_index = lookup(ctx->old_table, ctx->key);
+ ctx->old_term = ctx->old_table->term[ctx->entry_index];
+ if (is_boxed(ctx->old_term)) {
Uint new_size;
- HashTable* tmp_table;
-
/*
* Since we don't use any delete markers, we must rehash
* the table when deleting terms to ensure that all terms
@@ -378,8 +561,12 @@ BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1)
* temporary table copy of the same size as the old one.
*/
- ASSERT(is_tuple_arity(old_term, 2));
- tmp_table = tmp_table_copy(old_table);
+ ASSERT(is_tuple_arity(ctx->old_term, 2));
+ TRAPPING_COPY_TABLE_ERASE(ctx->tmp_table,
+ ctx->old_table,
+ ctx->old_table->allocated,
+ ERTS_PERSISTENT_TERM_CPY_TEMP,
+ ERASE1_TRAP_LOCATION_TMP_COPY);
/*
* Delete the term from the temporary table. Then copy the
@@ -387,18 +574,28 @@ BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1)
* while copying.
*/
- tmp_table->term[entry_index] = NIL;
- tmp_table->num_entries--;
- new_size = tmp_table->allocated;
- if (MUST_SHRINK(tmp_table)) {
+ ctx->tmp_table->term[ctx->entry_index] = NIL;
+ ctx->tmp_table->num_entries--;
+ new_size = ctx->tmp_table->allocated;
+ if (MUST_SHRINK(ctx->tmp_table)) {
new_size /= 2;
}
- new_table = copy_table(tmp_table, new_size, 1);
- erts_free(ERTS_ALC_T_TMP, tmp_table);
+ TRAPPING_COPY_TABLE_ERASE(ctx->new_table,
+ ctx->tmp_table,
+ new_size,
+ ERTS_PERSISTENT_TERM_CPY_REHASH,
+ ERASE1_TRAP_LOCATION_FINAL_COPY);
+ erts_free(ERTS_ALC_T_PERSISTENT_TERM_TMP, ctx->tmp_table);
+ /*
+ * IMPORTANT: Memory management depends on that ctx->tmp_table
+ * is set to NULL on the line below
+ */
+ ctx->tmp_table = NULL;
- mark_for_deletion(old_table, entry_index);
- erts_schedule_thr_prgr_later_op(table_updater, new_table, &thr_prog_op);
+ mark_for_deletion(ctx->old_table, ctx->entry_index);
+ erts_schedule_thr_prgr_later_op(table_updater, ctx->new_table, &thr_prog_op);
suspend_updater(BIF_P);
+ BUMP_REDS(BIF_P, (max_iterations - iterations_until_trap) / ITERATIONS_PER_RED);
ERTS_BIF_YIELD_RETURN(BIF_P, am_true);
}
@@ -406,7 +603,7 @@ BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1)
* Key is not present. Nothing to do.
*/
- ASSERT(is_nil(old_term));
+ ASSERT(is_nil(ctx->old_term));
release_update_permission(0);
BIF_RET(am_false);
}
@@ -740,65 +937,104 @@ lookup(HashTable* hash_table, Eterm key)
}
static HashTable*
-tmp_table_copy(HashTable* old_table)
+copy_table(ErtsPersistentTermCpyTableCtx* ctx)
{
- Uint size = old_table->allocated;
- HashTable* tmp_table;
+ Uint old_size = ctx->old_table->allocated;
Uint i;
-
- tmp_table = (HashTable *) erts_alloc(ERTS_ALC_T_TMP,
- sizeof(HashTable) +
- sizeof(Eterm) * (size-1));
- *tmp_table = *old_table;
- for (i = 0; i < size; i++) {
- tmp_table->term[i] = old_table->term[i];
+ ErtsAlcType_t alloc_type;
+ ctx->total_iterations_done = 0;
+ switch(ctx->location) {
+ case ERTS_PERSISTENT_TERM_CPY_PLACE_1: goto L_copy_table_place_1;
+ case ERTS_PERSISTENT_TERM_CPY_PLACE_2: goto L_copy_table_place_2;
+ case ERTS_PERSISTENT_TERM_CPY_PLACE_3: goto L_copy_table_place_3;
+ case ERTS_PERSISTENT_TERM_CPY_PLACE_START:
+ ctx->iterations_done = 0;
}
- return tmp_table;
-}
-
-static HashTable*
-copy_table(HashTable* old_table, Uint new_size, int rehash)
-{
- HashTable* new_table;
- Uint old_size = old_table->allocated;
- Uint i;
-
- new_table = (HashTable *) erts_alloc(ERTS_ALC_T_PERSISTENT_TERM,
- sizeof(HashTable) +
- sizeof(Eterm) * (new_size-1));
- if (old_table->allocated == new_size && !rehash) {
+ if (ctx->copy_type == ERTS_PERSISTENT_TERM_CPY_TEMP) {
+ alloc_type = ERTS_ALC_T_PERSISTENT_TERM_TMP;
+ } else {
+ alloc_type = ERTS_ALC_T_PERSISTENT_TERM;
+ }
+ ctx->new_table = (HashTable *) erts_alloc(alloc_type,
+ sizeof(HashTable) +
+ sizeof(Eterm) * (ctx->new_size-1));
+ if (ctx->old_table->allocated == ctx->new_size &&
+ (ctx->copy_type == ERTS_PERSISTENT_TERM_CPY_NO_REHASH ||
+ ctx->copy_type == ERTS_PERSISTENT_TERM_CPY_TEMP)) {
/*
* Same size and no key deleted. Make an exact copy of the table.
*/
- *new_table = *old_table;
- for (i = 0; i < new_size; i++) {
- new_table->term[i] = old_table->term[i];
+ *ctx->new_table = *ctx->old_table;
+ L_copy_table_place_1:
+ for (i = ctx->iterations_done;
+ i < MIN(ctx->iterations_done + ctx->max_iterations,
+ ctx->new_size);
+ i++) {
+ ctx->new_table->term[i] = ctx->old_table->term[i];
}
+ ctx->total_iterations_done = (i - ctx->iterations_done);
+ if (i < ctx->new_size) {
+ ctx->iterations_done = i;
+ ctx->location = ERTS_PERSISTENT_TERM_CPY_PLACE_1;
+ return NULL;
+ }
+ ctx->iterations_done = 0;
} else {
/*
* The size of the table has changed or an element has been
* deleted. Must rehash, by inserting all old terms into the
* new (empty) table.
*/
- new_table->allocated = new_size;
- new_table->num_entries = old_table->num_entries;
- new_table->mask = new_size - 1;
- for (i = 0; i < new_size; i++) {
- new_table->term[i] = NIL;
+ ctx->new_table->allocated = ctx->new_size;
+ ctx->new_table->num_entries = ctx->old_table->num_entries;
+ ctx->new_table->mask = ctx->new_size - 1;
+ L_copy_table_place_2:
+ for (i = ctx->iterations_done;
+ i < MIN(ctx->iterations_done + ctx->max_iterations,
+ ctx->new_size);
+ i++) {
+ ctx->new_table->term[i] = NIL;
+ }
+ ctx->total_iterations_done = (i - ctx->iterations_done);
+ ctx->max_iterations -= ctx->total_iterations_done;
+ if (i < ctx->new_size) {
+ ctx->iterations_done = i;
+ ctx->location = ERTS_PERSISTENT_TERM_CPY_PLACE_2;
+ return NULL;
}
- for (i = 0; i < old_size; i++) {
- if (is_tuple(old_table->term[i])) {
- Eterm key = tuple_val(old_table->term[i])[1];
- Uint entry_index = lookup(new_table, key);
- ASSERT(is_nil(new_table->term[entry_index]));
- new_table->term[entry_index] = old_table->term[i];
+ ctx->iterations_done = 0;
+ L_copy_table_place_3:
+ for (i = ctx->iterations_done;
+ i < MIN(ctx->iterations_done + ctx->max_iterations,
+ old_size);
+ i++) {
+ if (is_tuple(ctx->old_table->term[i])) {
+ Eterm key = tuple_val(ctx->old_table->term[i])[1];
+ Uint entry_index = lookup(ctx->new_table, key);
+ ASSERT(is_nil(ctx->new_table->term[entry_index]));
+ ctx->new_table->term[entry_index] = ctx->old_table->term[i];
}
}
+ ctx->total_iterations_done += (i - ctx->iterations_done);
+ if (i < old_size) {
+ ctx->iterations_done = i;
+ ctx->location = ERTS_PERSISTENT_TERM_CPY_PLACE_3;
+ return NULL;
+ }
+ ctx->iterations_done = 0;
+ }
+ ctx->new_table->first_to_delete = 0;
+ ctx->new_table->num_to_delete = 0;
+ erts_atomic_init_nob(&ctx->new_table->refc, (erts_aint_t)1);
+ {
+ HashTable* new_table = ctx->new_table;
+ /*
+ * IMPORTANT: Memory management depends on that ctx->new_table is
+ * set to NULL on the line below
+ */
+ ctx->new_table = NULL;
+ return new_table;
}
- new_table->first_to_delete = 0;
- new_table->num_to_delete = 0;
- erts_atomic_init_nob(&new_table->refc, (erts_aint_t)1);
- return new_table;
}
static void
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index c0f5c506f4..d24f30f126 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -4573,3 +4573,16 @@ int erts_ets_force_split(Eterm tid, int on)
db_unlock(tb, LCK_WRITE);
return 1;
}
+
+int erts_ets_debug_random_split_join(Eterm tid, int on)
+{
+ DbTable* tb = tid2tab(tid);
+ if (!tb || !IS_CATREE_TABLE(tb->common.type))
+ return 0;
+
+ db_lock(tb, LCK_WRITE);
+ if (!(tb->common.status & DB_DELETE))
+ db_catree_debug_random_split_join(&tb->catree, on);
+ db_unlock(tb, LCK_WRITE);
+ return 1;
+}
diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h
index b22f35a5ef..b3dc1b9ba3 100644
--- a/erts/emulator/beam/erl_db.h
+++ b/erts/emulator/beam/erl_db.h
@@ -131,6 +131,7 @@ extern erts_atomic_t erts_ets_misc_mem_size;
Eterm erts_ets_colliding_names(Process*, Eterm name, Uint cnt);
int erts_ets_force_split(Eterm tid, int on);
+int erts_ets_debug_random_split_join(Eterm tid, int on);
Uint erts_db_get_max_tabs(void);
Eterm erts_db_make_tid(Process *c_p, DbTableCommon *tb);
diff --git a/erts/emulator/beam/erl_db_catree.c b/erts/emulator/beam/erl_db_catree.c
index 962fe4c4f8..e0d5e44f58 100644
--- a/erts/emulator/beam/erl_db_catree.c
+++ b/erts/emulator/beam/erl_db_catree.c
@@ -647,7 +647,8 @@ static int dbg_fastrand(void)
static void dbg_provoke_random_splitjoin(DbTableCATree* tb,
DbTableCATreeNode* base_node)
{
- if (tb->common.status & DB_CATREE_FORCE_SPLIT)
+ if (tb->common.status & DB_CATREE_FORCE_SPLIT ||
+ !(tb->common.status & DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN))
return;
switch (dbg_fastrand() % 8) {
@@ -1406,6 +1407,9 @@ int db_create_catree(Process *p, DbTable *tbl)
tb->deletion = 0;
tb->base_nodes_to_free_list = NULL;
tb->nr_of_deleted_items = 0;
+#ifdef DEBUG
+ tbl->common.status |= DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN;
+#endif
erts_atomic_init_relb(&(tb->root), (erts_aint_t)root);
return DB_ERROR_NONE;
}
@@ -2257,6 +2261,14 @@ void db_catree_force_split(DbTableCATree* tb, int on)
tb->common.status &= ~DB_CATREE_FORCE_SPLIT;
}
+void db_catree_debug_random_split_join(DbTableCATree* tb, int on)
+{
+ if (on)
+ tb->common.status |= DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN;
+ else
+ tb->common.status &= ~DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN;
+}
+
void db_calc_stats_catree(DbTableCATree* tb, DbCATreeStats* stats)
{
DbTableCATreeNode* stack[ERL_DB_CATREE_MAX_ROUTE_NODE_LAYER_HEIGHT];
diff --git a/erts/emulator/beam/erl_db_catree.h b/erts/emulator/beam/erl_db_catree.h
index fde442eaf5..cf3498dabb 100644
--- a/erts/emulator/beam/erl_db_catree.h
+++ b/erts/emulator/beam/erl_db_catree.h
@@ -124,6 +124,7 @@ void erts_lcnt_enable_db_catree_lock_count(DbTableCATree *tb, int enable);
#endif
void db_catree_force_split(DbTableCATree*, int on);
+void db_catree_debug_random_split_join(DbTableCATree*, int on);
typedef struct {
Uint route_nodes;
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 97f2848679..02d4dd6c9a 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -326,6 +326,7 @@ typedef struct db_table_common {
#define DB_BUSY (1 << 12)
#define DB_CATREE_FORCE_SPLIT (1 << 31) /* erts_debug */
+#define DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN (1 << 30) /* erts_debug */
#define IS_HASH_TABLE(Status) (!!((Status) & \
(DB_BAG | DB_SET | DB_DUPLICATE_BAG)))
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 2a0fb9e2aa..6645341512 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -1590,6 +1590,9 @@ void erts_factory_undo(ErtsHeapFactory* factory)
factory->message->hfrag.next = factory->heap_frags;
else
factory->message->data.heap_frag = factory->heap_frags;
+ /* Set the message to NIL in order for this message not to be
+ treated as a distributed message by the cleanup_messages logic */
+ factory->message->m[0] = NIL;
erts_cleanup_messages(factory->message);
break;
case FACTORY_TMP:
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index deaf35c2a1..1fbe362330 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1344,11 +1344,18 @@ unsigned char* enif_make_new_binary(ErlNifEnv* env, size_t size,
int enif_term_to_binary(ErlNifEnv *dst_env, ERL_NIF_TERM term,
ErlNifBinary *bin)
{
- Sint size;
+ Uint size;
byte *bp;
Binary* refbin;
- size = erts_encode_ext_size(term);
+ switch (erts_encode_ext_size(term, &size)) {
+ case ERTS_EXT_SZ_SYSTEM_LIMIT:
+ return 0; /* system limit */
+ case ERTS_EXT_SZ_YIELD:
+ ERTS_INTERNAL_ERROR("Unexpected yield");
+ case ERTS_EXT_SZ_OK:
+ break;
+ }
if (!enif_alloc_binary(size, bin))
return 0;
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 2b45d2d353..1f6adb98ef 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -12095,6 +12095,7 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
Eterm watched;
Uint watcher_sz, ref_sz;
ErtsHeapFactory factory;
+ Sint reds_consumed = 0;
ASSERT(erts_monitor_is_target(mon) && mon->type == ERTS_MON_TYPE_DIST_PROC);
@@ -12139,15 +12140,18 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
watched,
ref,
reason);
+ reds_consumed = reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
switch (code) {
case ERTS_DSIG_SEND_CONTINUE:
case ERTS_DSIG_SEND_YIELD:
erts_set_gc_state(c_p, 0);
ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx);
+ reds_consumed = reds; /* force yield */
break;
case ERTS_DSIG_SEND_OK:
break;
case ERTS_DSIG_SEND_TOO_LRG:
+ erts_kill_dist_connection(dep, dist->connection_id);
erts_set_gc_state(c_p, 1);
break;
default:
@@ -12163,7 +12167,7 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
erts_monitor_release(mon);
else
erts_monitor_release_both(mdp);
- return reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
+ return reds_consumed;
}
int
@@ -12350,6 +12354,7 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds)
ErtsLink *dlnk;
ErtsLinkData *ldp = NULL;
ErtsHeapFactory factory;
+ Sint reds_consumed = 0;
ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC);
dlnk = erts_link_to_other(lnk, &ldp);
@@ -12386,15 +12391,18 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds)
item,
reason,
SEQ_TRACE_TOKEN(c_p));
+ reds_consumed = reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
switch (code) {
case ERTS_DSIG_SEND_YIELD:
case ERTS_DSIG_SEND_CONTINUE:
erts_set_gc_state(c_p, 0);
ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx);
+ reds_consumed = reds; /* force yield */
break;
case ERTS_DSIG_SEND_OK:
break;
case ERTS_DSIG_SEND_TOO_LRG:
+ erts_kill_dist_connection(dep, dist->connection_id);
erts_set_gc_state(c_p, 1);
break;
default:
@@ -12410,7 +12418,7 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds)
erts_link_release_both(ldp);
else if (lnk)
erts_link_release(lnk);
- return reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
+ return reds_consumed;
}
int
@@ -12891,7 +12899,9 @@ restart:
switch (result) {
case ERTS_DSIG_SEND_OK:
+ break;
case ERTS_DSIG_SEND_TOO_LRG: /*SEND_SYSTEM_LIMIT*/
+ erts_kill_dist_connection(ctx->dep, ctx->connection_id);
break;
case ERTS_DSIG_SEND_YIELD: /*SEND_YIELD_RETURN*/
case ERTS_DSIG_SEND_CONTINUE: { /*SEND_YIELD_CONTINUE*/
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index ae7084b7f4..c85a7df5ec 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -635,9 +635,11 @@ write_sys_msg_to_port(Eterm unused_to,
Eterm message) {
byte *buffer;
byte *ptr;
- unsigned size;
+ Uint size;
+
+ if (erts_encode_ext_size(message, &size) != ERTS_EXT_SZ_OK)
+ erts_exit(ERTS_ERROR_EXIT, "Internal error: System limit\n");
- size = erts_encode_ext_size(message);
buffer = (byte *) erts_alloc(ERTS_ALC_T_TMP, size);
ptr = buffer;
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 395ff51ad3..ec67ab2aed 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -112,13 +112,13 @@ static byte* dec_pid(ErtsDistExternal *, ErtsHeapFactory*, byte*, Eterm*, byte t
static Sint decoded_size(byte *ep, byte* endp, int internal_tags, struct B2TContext_t*);
static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1);
-static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint flags,
- Binary *context_b);
+static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int level,
+ Uint flags, Binary *context_b);
static Uint encode_size_struct2(ErtsAtomCacheMap *, Eterm, unsigned);
struct TTBSizeContext_;
-static int encode_size_struct_int(struct TTBSizeContext_*, ErtsAtomCacheMap *acmp, Eterm obj,
- unsigned dflags, Sint *reds, Uint *res);
+static ErtsExtSzRes encode_size_struct_int(struct TTBSizeContext_*, ErtsAtomCacheMap *acmp,
+ Eterm obj, unsigned dflags, Sint *reds, Uint *res);
static Export binary_to_term_trap_export;
static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1);
@@ -603,49 +603,50 @@ done:
return reds < 0 ? 0 : reds;
}
-int erts_encode_dist_ext_size(Eterm term, Uint32 flags, ErtsAtomCacheMap *acmp,
- Uint* szp)
+ErtsExtSzRes
+erts_encode_dist_ext_size(Eterm term, Uint32 flags, ErtsAtomCacheMap *acmp, Uint* szp)
{
Uint sz;
- if (encode_size_struct_int(NULL, acmp, term, flags, NULL, &sz)) {
- return -1;
- } else {
+ ErtsExtSzRes res = encode_size_struct_int(NULL, acmp, term, flags, NULL, &sz);
+ if (res == ERTS_EXT_SZ_OK) {
#ifndef ERTS_DEBUG_USE_DIST_SEP
if (!(flags & (DFLAG_DIST_HDR_ATOM_CACHE | DFLAG_NO_MAGIC)))
#endif
sz++ /* VERSION_MAGIC */;
*szp += sz;
- return 0;
}
+ return res;
}
-int erts_encode_dist_ext_size_int(Eterm term, ErtsDSigSendContext *ctx, Uint* szp)
+ErtsExtSzRes
+erts_encode_dist_ext_size_ctx(Eterm term, ErtsDSigSendContext *ctx, Uint* szp)
{
Uint sz;
- if (encode_size_struct_int(&ctx->u.sc, ctx->acmp, term, ctx->flags, &ctx->reds, &sz)) {
- return -1;
- } else {
+ ErtsExtSzRes res = encode_size_struct_int(&ctx->u.sc, ctx->acmp, term,
+ ctx->flags, &ctx->reds, &sz);
+ if (res == ERTS_EXT_SZ_OK) {
#ifndef ERTS_DEBUG_USE_DIST_SEP
if (!(ctx->flags & (DFLAG_DIST_HDR_ATOM_CACHE | DFLAG_NO_MAGIC)))
#endif
sz++ /* VERSION_MAGIC */;
*szp += sz;
- return 0;
}
+ return res;
}
-Uint erts_encode_ext_size(Eterm term)
+ErtsExtSzRes erts_encode_ext_size_2(Eterm term, unsigned dflags, Uint *szp)
{
- return encode_size_struct2(NULL, term, TERM_TO_BINARY_DFLAGS)
- + 1 /* VERSION_MAGIC */;
+ ErtsExtSzRes res = encode_size_struct_int(NULL, NULL, term, dflags,
+ NULL, szp);
+ (*szp)++ /* VERSION_MAGIC */;
+ return res;
}
-Uint erts_encode_ext_size_2(Eterm term, unsigned dflags)
+ErtsExtSzRes erts_encode_ext_size(Eterm term, Uint *szp)
{
- return encode_size_struct2(NULL, term, dflags)
- + 1 /* VERSION_MAGIC */;
+ return erts_encode_ext_size_2(term, TERM_TO_BINARY_DFLAGS, szp);
}
Uint erts_encode_ext_size_ets(Eterm term)
@@ -1253,9 +1254,22 @@ static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1)
{
Eterm *tp = tuple_val(BIF_ARG_1);
Eterm Term = tp[1];
- Eterm bt = tp[2];
+ Eterm Opts = tp[2];
+ Eterm bt = tp[3];
Binary *bin = erts_magic_ref2bin(bt);
- Eterm res = erts_term_to_binary_int(BIF_P, Term, 0, 0,bin);
+ Eterm res = erts_term_to_binary_int(BIF_P, Term, Opts, 0, 0,bin);
+ if (is_non_value(res)) {
+ if (erts_set_gc_state(BIF_P, 1)
+ || MSO(BIF_P).overhead > BIN_VHEAP_SZ(BIF_P)) {
+ ERTS_VBUMP_ALL_REDS(BIF_P);
+ }
+ if (Opts == am_undefined)
+ ERTS_BIF_ERROR_TRAPPED1(BIF_P, SYSTEM_LIMIT,
+ bif_export[BIF_term_to_binary_1], Term);
+ else
+ ERTS_BIF_ERROR_TRAPPED2(BIF_P, SYSTEM_LIMIT,
+ bif_export[BIF_term_to_binary_2], Term, Opts);
+ }
if (is_tuple(res)) {
ASSERT(BIF_P->flags & F_DISABLE_GC);
BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res);
@@ -1272,7 +1286,12 @@ HIPE_WRAPPER_BIF_DISABLE_GC(term_to_binary, 1)
BIF_RETTYPE term_to_binary_1(BIF_ALIST_1)
{
- Eterm res = erts_term_to_binary_int(BIF_P, BIF_ARG_1, 0, TERM_TO_BINARY_DFLAGS, NULL);
+ Eterm res = erts_term_to_binary_int(BIF_P, BIF_ARG_1, am_undefined,
+ 0, TERM_TO_BINARY_DFLAGS, NULL);
+ if (is_non_value(res)) {
+ ASSERT(!(BIF_P->flags & F_DISABLE_GC));
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ }
if (is_tuple(res)) {
erts_set_gc_state(BIF_P, 0);
BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res);
@@ -1331,7 +1350,12 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2)
goto error;
}
- res = erts_term_to_binary_int(p, Term, level, flags, NULL);
+ res = erts_term_to_binary_int(p, Term, BIF_ARG_2,
+ level, flags, NULL);
+ if (is_non_value(res)) {
+ ASSERT(!(BIF_P->flags & F_DISABLE_GC));
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ }
if (is_tuple(res)) {
erts_set_gc_state(p, 0);
BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res);
@@ -1880,8 +1904,17 @@ external_size_1(BIF_ALIST_1)
{
Process* p = BIF_P;
Eterm Term = BIF_ARG_1;
+ Uint size;
+
+ switch (erts_encode_ext_size(Term, &size)) {
+ case ERTS_EXT_SZ_SYSTEM_LIMIT:
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ case ERTS_EXT_SZ_YIELD:
+ ERTS_INTERNAL_ERROR("Unexpected yield");
+ case ERTS_EXT_SZ_OK:
+ break;
+ }
- Uint size = erts_encode_ext_size(Term);
if (IS_USMALL(0, size)) {
BIF_RET(make_small(size));
} else {
@@ -1924,7 +1957,15 @@ external_size_2(BIF_ALIST_2)
goto error;
}
- size = erts_encode_ext_size_2(BIF_ARG_1, flags);
+ switch (erts_encode_ext_size_2(BIF_ARG_1, flags, &size)) {
+ case ERTS_EXT_SZ_SYSTEM_LIMIT:
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ case ERTS_EXT_SZ_YIELD:
+ ERTS_INTERNAL_ERROR("Unexpected yield");
+ case ERTS_EXT_SZ_OK:
+ break;
+ }
+
if (IS_USMALL(0, size)) {
BIF_RET(make_small(size));
} else {
@@ -2012,7 +2053,15 @@ erts_term_to_binary_simple(Process* p, Eterm Term, Uint size, int level, Uint fl
Eterm
erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags) {
Uint size;
- size = encode_size_struct2(NULL, Term, flags) + 1 /* VERSION_MAGIC */;
+ switch (encode_size_struct_int(NULL, NULL, Term, flags, NULL, &size)) {
+ case ERTS_EXT_SZ_SYSTEM_LIMIT:
+ return THE_NON_VALUE;
+ case ERTS_EXT_SZ_YIELD:
+ ERTS_INTERNAL_ERROR("Unexpected yield");
+ case ERTS_EXT_SZ_OK:
+ break;
+ }
+ size++; /* VERSION_MAGIC */;
return erts_term_to_binary_simple(p, Term, size, level, flags);
}
@@ -2062,8 +2111,8 @@ static int ttb_context_destructor(Binary *context_bin)
return 1;
}
-static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint flags,
- Binary *context_b)
+static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int level,
+ Uint flags, Binary *context_b)
{
Eterm *hp;
Eterm res;
@@ -2081,18 +2130,17 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla
do { \
if (context_b == NULL) { \
context_b = erts_create_magic_binary(sizeof(TTBContext), \
- ttb_context_destructor); \
+ ttb_context_destructor);\
context = ERTS_MAGIC_BIN_DATA(context_b); \
- sys_memcpy(context,&c_buff,sizeof(TTBContext)); \
+ sys_memcpy(context,&c_buff,sizeof(TTBContext)); \
} \
} while (0)
#define RETURN_STATE() \
do { \
- static const int TUPLE2_SIZE = 2 + 1; \
- hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE + TUPLE2_SIZE); \
+ hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE + 1 + 3); \
c_term = erts_mk_magic_ref(&hp, &MSO(p), context_b); \
- res = TUPLE2(hp, Term, c_term); \
+ res = TUPLE3(hp, Term, opts, c_term); \
BUMP_ALL_REDS(p); \
return res; \
} while (0);
@@ -2118,11 +2166,17 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla
int level;
Uint flags;
/* Try for fast path */
- if (encode_size_struct_int(&context->s.sc, NULL, Term,
- context->s.sc.flags, &reds, &size) < 0) {
+ switch (encode_size_struct_int(&context->s.sc, NULL, Term,
+ context->s.sc.flags, &reds, &size)) {
+ case ERTS_EXT_SZ_SYSTEM_LIMIT:
+ BUMP_REDS(p, (initial_reds - reds) / TERM_TO_BINARY_LOOP_FACTOR);
+ return THE_NON_VALUE;
+ case ERTS_EXT_SZ_YIELD:
EXPORT_CONTEXT();
/* Same state */
RETURN_STATE();
+ case ERTS_EXT_SZ_OK:
+ break;
}
++size; /* VERSION_MAGIC */
/* Move these to next state */
@@ -4184,13 +4238,21 @@ error_hamt:
to a sequence of bytes
N.B. That this must agree with to_external2() above!!!
(except for cached atoms) */
-static Uint encode_size_struct2(ErtsAtomCacheMap *acmp, Eterm obj, unsigned dflags) {
- Uint res;
- (void) encode_size_struct_int(NULL, acmp, obj, dflags, NULL, &res);
- return res;
+static Uint encode_size_struct2(ErtsAtomCacheMap *acmp,
+ Eterm obj,
+ unsigned dflags) {
+ Uint size;
+ ErtsExtSzRes res = encode_size_struct_int(NULL, acmp, obj,
+ dflags, NULL, &size);
+ /*
+ * encode_size_struct2() only allowed when
+ * we know the result will always be OK!
+ */
+ ASSERT(res == ERTS_EXT_SZ_OK); (void) res;
+ return (Uint) size;
}
-static int
+static ErtsExtSzRes
encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
unsigned dflags, Sint *reds, Uint *res)
{
@@ -4223,7 +4285,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
ctx->obj = obj;
ctx->result = result;
WSTACK_SAVE(s, &ctx->wstack);
- return -1;
+ return ERTS_EXT_SZ_YIELD;
}
switch (tag_val_def(obj)) {
case NIL_DEF:
@@ -4399,11 +4461,26 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
result += 32; /* Yes, including the tag */
}
break;
- case BINARY_DEF:
- if (dflags & DFLAG_INTERNAL_TAGS) {
+ case BINARY_DEF: {
+ ProcBin* pb = (ProcBin*) binary_val(obj);
+ Uint tot_bytes = pb->size;
+ if (!(dflags & DFLAG_INTERNAL_TAGS)) {
+#ifdef ARCH_64
+ if (tot_bytes >= (Uint) 0xffffffff) {
+ if (pb->thing_word == HEADER_SUB_BIN) {
+ ErlSubBin* sub = (ErlSubBin*) pb;
+ tot_bytes += (sub->bitoffs + sub->bitsize+ 7) / 8;
+ }
+ if (tot_bytes > (Uint) 0xffffffff) {
+ WSTACK_DESTROY(s);
+ return ERTS_EXT_SZ_SYSTEM_LIMIT;
+ }
+ }
+#endif
+ }
+ else {
ProcBin* pb = (ProcBin*) binary_val(obj);
Uint sub_extra = 0;
- Uint tot_bytes = pb->size;
if (pb->thing_word == HEADER_SUB_BIN) {
ErlSubBin* sub = (ErlSubBin*) pb;
pb = (ProcBin*) binary_val(sub->orig);
@@ -4420,6 +4497,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
result += 1 + 4 + binary_size(obj) +
5; /* For unaligned binary */
break;
+ }
case FUN_DEF:
{
ErlFunThing* funp = (ErlFunThing *) fun_val(obj);
@@ -4452,7 +4530,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
break;
default:
- erts_exit(ERTS_ERROR_EXIT,"Internal data structure error (in encode_size_struct2)%x\n",
+ erts_exit(ERTS_ERROR_EXIT,"Internal data structure error (in encode_size_struct_int) %x\n",
obj);
}
@@ -4492,7 +4570,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
*reds = r < 0 ? 0 : r;
}
*res = result;
- return 0;
+ return ERTS_EXT_SZ_OK;
}
diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h
index f2cc9bf98f..b556c9076c 100644
--- a/erts/emulator/beam/external.h
+++ b/erts/emulator/beam/external.h
@@ -164,14 +164,21 @@ byte *erts_encode_ext_dist_header_setup(byte *, ErtsAtomCacheMap *, Uint, Eterm)
byte *erts_encode_ext_dist_header_fragment(byte **, Uint, Eterm);
Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf*, DistEntry *, Uint32 dflags, Sint reds);
struct erts_dsig_send_context;
-int erts_encode_dist_ext_size(Eterm, Uint32, ErtsAtomCacheMap*, Uint* szp);
-int erts_encode_dist_ext_size_int(Eterm term, struct erts_dsig_send_context* ctx, Uint* szp);
+
+typedef enum {
+ ERTS_EXT_SZ_OK,
+ ERTS_EXT_SZ_YIELD,
+ ERTS_EXT_SZ_SYSTEM_LIMIT
+} ErtsExtSzRes;
+
+ErtsExtSzRes erts_encode_dist_ext_size(Eterm, Uint32, ErtsAtomCacheMap*, Uint* szp);
+ErtsExtSzRes erts_encode_dist_ext_size_ctx(Eterm term, struct erts_dsig_send_context* ctx, Uint* szp);
struct TTBEncodeContext_;
int erts_encode_dist_ext(Eterm, byte **, Uint32, ErtsAtomCacheMap *,
struct TTBEncodeContext_ *, Sint* reds);
-Uint erts_encode_ext_size(Eterm);
-Uint erts_encode_ext_size_2(Eterm, unsigned);
+ErtsExtSzRes erts_encode_ext_size(Eterm, Uint *szp);
+ErtsExtSzRes erts_encode_ext_size_2(Eterm, unsigned, Uint *szp);
Uint erts_encode_ext_size_ets(Eterm);
void erts_encode_ext(Eterm, byte **);
byte* erts_encode_ext_ets(Eterm, byte *, struct erl_off_heap_header** ext_off_heap);
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index b961c639f5..31c941337f 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -4450,6 +4450,7 @@ erts_port_call(Process* c_p,
char input_buf[256];
char *bufp;
byte *endp;
+ Uint uintsz;
ErlDrvSizeT size;
int try_call;
erts_aint32_t sched_flags;
@@ -4462,7 +4463,9 @@ erts_port_call(Process* c_p,
try_call = !(sched_flags & ERTS_PTS_FLGS_FORCE_SCHEDULE_OP);
- size = erts_encode_ext_size(data);
+ if (erts_encode_ext_size(data, &uintsz) != ERTS_EXT_SZ_OK)
+ return ERTS_PORT_OP_BADARG;
+ size = (ErlDrvSizeT) uintsz;
if (!try_call)
bufp = erts_alloc(ERTS_ALC_T_DRV_CALL_DATA, size);
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index 563d60cc3f..4fb339926e 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -71,7 +71,8 @@
term2bin_tuple_fallbacks/1,
robustness/1,otp_8117/1,
otp_8180/1, trapping/1, large/1,
- error_after_yield/1, cmp_old_impl/1]).
+ error_after_yield/1, cmp_old_impl/1,
+ t2b_system_limit/1]).
%% Internal exports.
-export([sleeper/0,trapping_loop/4]).
@@ -79,7 +80,7 @@
suite() -> [{ct_hooks,[ts_install_cth]},
{timetrap,{minutes,4}}].
-all() ->
+all() ->
[copy_terms, conversions, deep_lists, deep_bitstr_lists,
t_split_binary, bad_split,
bad_list_to_binary, bad_binary_to_list, terms,
@@ -90,7 +91,8 @@ all() ->
b2t_used_big,
bad_binary_to_term_2, safe_binary_to_term2,
bad_binary_to_term, bad_terms, t_hash, bad_size,
- bad_term_to_binary, more_bad_terms, otp_5484, otp_5933,
+ bad_term_to_binary, t2b_system_limit, more_bad_terms,
+ otp_5484, otp_5933,
ordering, unaligned_order, gc_test,
bit_sized_binary_sizes, otp_6817, otp_8117, deep,
term2bin_tuple_fallbacks,
@@ -462,6 +464,54 @@ bad_term_to_binary(Config) when is_list(Config) ->
ok.
+t2b_system_limit(Config) when is_list(Config) ->
+ case erlang:system_info(wordsize) of
+ 8 ->
+ case proplists:get_value(system_total_memory,
+ memsup:get_system_memory_data()) of
+ Memory when is_integer(Memory),
+ Memory > 6*1024*1024*1024 ->
+ test_t2b_system_limit(),
+ garbage_collect(),
+ ok;
+ _ ->
+ {skipped, "Not enough memory on this machine"}
+ end;
+ 4 ->
+ {skipped, "Only interesting on 64-bit builds"}
+ end.
+
+test_t2b_system_limit() ->
+ io:format("Creating HugeBin~n", []),
+ Bits = ((1 bsl 32)+1)*8,
+ HugeBin = <<0:Bits>>,
+
+ io:format("Testing term_to_binary(HugeBin)~n", []),
+ {'EXIT',{system_limit,[{erlang,term_to_binary,
+ [HugeBin],
+ _} |_]}} = (catch term_to_binary(HugeBin)),
+
+ io:format("Testing term_to_binary(HugeBin, [compressed])~n", []),
+ {'EXIT',{system_limit,[{erlang,term_to_binary,
+ [HugeBin, [compressed]],
+ _} |_]}} = (catch term_to_binary(HugeBin, [compressed])),
+
+ %% Check that it works also after we have trapped...
+ io:format("Creating HugeListBin~n", []),
+ HugeListBin = [lists:duplicate(2000000,2000000), HugeBin],
+
+ io:format("Testing term_to_binary(HugeListBin)~n", []),
+ {'EXIT',{system_limit,[{erlang,term_to_binary,
+ [HugeListBin],
+ _} |_]}} = (catch term_to_binary(HugeListBin)),
+
+ io:format("Testing term_to_binary(HugeListBin, [compressed])~n", []),
+ {'EXIT',{system_limit,[{erlang,term_to_binary,
+ [HugeListBin, [compressed]],
+ _} |_]}} = (catch term_to_binary(HugeListBin, [compressed])),
+
+ ok.
+
%% Tests binary_to_term/1 and term_to_binary/1.
terms(Config) when is_list(Config) ->
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index 6e31c73e10..7885d35d9d 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -67,7 +67,8 @@
message_latency_large_message/1,
message_latency_large_link_exit/1,
message_latency_large_monitor_exit/1,
- message_latency_large_exit2/1]).
+ message_latency_large_exit2/1,
+ system_limit/1]).
%% Internal exports.
-export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0,
@@ -75,7 +76,7 @@
optimistic_dflags_echo/0, optimistic_dflags_sender/1,
roundtrip/1, bounce/1, do_dist_auto_connect/1, inet_rpc_server/1,
dist_parallel_sender/3, dist_parallel_receiver/0,
- dist_evil_parallel_receiver/0]).
+ dist_evil_parallel_receiver/0, make_busy/2]).
%% epmd_module exports
-export([start_link/0, register_node/2, register_node/3, port_please/2, address_please/3]).
@@ -96,7 +97,7 @@ all() ->
contended_atom_cache_entry, contended_unicode_atom_cache_entry,
{group, message_latency},
{group, bad_dist}, {group, bad_dist_ext},
- start_epmd_false, epmd_module].
+ start_epmd_false, epmd_module, system_limit].
groups() ->
[{bulk_send, [], [bulk_send_small, bulk_send_big, bulk_send_bigbig]},
@@ -1460,11 +1461,14 @@ measure_latency_large_message(Nodename, DataFun) ->
Echo = spawn(N, fun F() -> receive {From, Msg} -> From ! Msg, F() end end),
- case erlang:system_info(build_type) of
- debug ->
+ BuildType = erlang:system_info(build_type),
+ WordSize = erlang:system_info(wordsize),
+
+ if
+ BuildType =/= opt; WordSize =:= 4 ->
%% Test 3.2 MB and 32 MB and test the latency difference of sent messages
Payloads = [{I, <<0:(I * 32 * 1024 * 8)>>} || I <- [1,10]];
- _ ->
+ true ->
%% Test 32 MB and 320 MB and test the latency difference of sent messages
Payloads = [{I, <<0:(I * 32 * 1024 * 1024 * 8)>>} || I <- [1,10]]
end,
@@ -1479,7 +1483,7 @@ measure_latency_large_message(Nodename, DataFun) ->
stop_node(N),
case {lists:max(Times), lists:min(Times)} of
- {Max, Min} when Max * 0.25 > Min ->
+ {Max, Min} when Max * 0.25 > Min, BuildType =:= opt ->
ct:fail({incorrect_latency, IndexTimes});
_ ->
ok
@@ -1504,13 +1508,19 @@ measure_latency(DataFun, Dropper, Echo, Payload) ->
ok
end || _ <- lists:seq(1,10)],
- {TS, _} =
+ {TS, Times} =
timer:tc(fun() ->
[begin
+ T0 = erlang:monotonic_time(),
Echo ! {self(), hello},
- receive hello -> ok end
+ receive hello -> ok end,
+ (erlang:monotonic_time() - T0) / 1000000
end || _ <- lists:seq(1,100)]
end),
+ Avg = lists:sum(Times) / length(Times),
+ StdDev = math:sqrt(lists:sum([math:pow(V - Avg,2) || V <- Times]) / length(Times)),
+ ct:pal("Times: Avg: ~p Max: ~p Min: ~p Var: ~p",
+ [Avg, lists:max(Times), lists:min(Times), StdDev]),
[begin
Sender ! die,
receive
@@ -1528,6 +1538,144 @@ flush() ->
ok
end.
+system_limit(Config) when is_list(Config) ->
+ case erlang:system_info(wordsize) of
+ 8 ->
+ case proplists:get_value(system_total_memory,
+ memsup:get_system_memory_data()) of
+ Memory when is_integer(Memory),
+ Memory > 6*1024*1024*1024 ->
+ test_system_limit(Config),
+ garbage_collect(),
+ ok;
+ _ ->
+ {skipped, "Not enough memory on this machine"}
+ end;
+ 4 ->
+ {skipped, "Only interesting on 64-bit builds"}
+ end.
+
+test_system_limit(Config) when is_list(Config) ->
+ Bits = ((1 bsl 32)+1)*8,
+ HugeBin = <<0:Bits>>,
+ HugeListBin = [lists:duplicate(2000000,2000000), HugeBin],
+ {ok, N1} = start_node(Config),
+ monitor_node(N1, true),
+ receive
+ {nodedown, N1} ->
+ ct:fail({unexpected_nodedown, N1})
+ after 0 ->
+ ok
+ end,
+ P1 = spawn(N1,
+ fun () ->
+ receive after infinity -> ok end
+ end),
+
+ io:format("~n** distributed send **~n~n", []),
+ try
+ P1 ! HugeBin,
+ exit(oops1)
+ catch
+ error:system_limit -> ok
+ end,
+ try
+ P1 ! HugeListBin,
+ exit(oops2)
+ catch
+ error:system_limit -> ok
+ end,
+
+ io:format("~n** distributed exit **~n~n", []),
+ try
+ exit(P1, HugeBin),
+ exit(oops3)
+ catch
+ error:system_limit -> ok
+ end,
+ try
+ exit(P1, HugeListBin),
+ exit(oops4)
+ catch
+ error:system_limit -> ok
+ end,
+
+ io:format("~n** distributed registered send **~n~n", []),
+ try
+ {missing_proc, N1} ! HugeBin,
+ exit(oops5)
+ catch
+ error:system_limit -> ok
+ end,
+ try
+ {missing_proc, N1} ! HugeListBin,
+ exit(oops6)
+ catch
+ error:system_limit -> ok
+ end,
+ receive
+ {nodedown, N1} ->
+ ct:fail({unexpected_nodedown, N1})
+ after 0 ->
+ ok
+ end,
+
+ %%
+ %% system_limit in exit reasons brings the
+ %% connection down...
+ %%
+
+ io:format("~n** distributed link exit **~n~n", []),
+ spawn(fun () ->
+ link(P1),
+ exit(HugeBin)
+ end),
+ receive {nodedown, N1} -> ok end,
+
+ {ok, N2} = start_node(Config),
+ monitor_node(N2, true),
+ P2 = spawn(N2,
+ fun () ->
+ receive after infinity -> ok end
+ end),
+ spawn(fun () ->
+ link(P2),
+ exit(HugeListBin)
+ end),
+ receive {nodedown, N2} -> ok end,
+
+ io:format("~n** distributed monitor down **~n~n", []),
+ {ok, N3} = start_node(Config),
+ monitor_node(N3, true),
+ Go1 = make_ref(),
+ LP1 = spawn(fun () ->
+ receive Go1 -> ok end,
+ exit(HugeBin)
+ end),
+ _ = spawn(N3,
+ fun () ->
+ _ = erlang:monitor(process, LP1),
+ LP1 ! Go1,
+ receive after infinity -> ok end
+ end),
+ receive {nodedown, N3} -> ok end,
+
+ {ok, N4} = start_node(Config),
+ monitor_node(N4, true),
+ Go2 = make_ref(),
+ LP2 = spawn(fun () ->
+ receive Go2 -> ok end,
+ exit(HugeListBin)
+ end),
+ _ = spawn(N4,
+ fun () ->
+ _ = erlang:monitor(process, LP2),
+ LP2 ! Go2,
+ receive after infinity -> ok end
+ end),
+ receive {nodedown, N4} -> ok end,
+ ok.
+
-define(COOKIE, '').
-define(DOP_LINK, 1).
-define(DOP_SEND, 2).
diff --git a/erts/emulator/test/dump_SUITE.erl b/erts/emulator/test/dump_SUITE.erl
index 3b860ebdf6..9f8ac42fa9 100644
--- a/erts/emulator/test/dump_SUITE.erl
+++ b/erts/emulator/test/dump_SUITE.erl
@@ -137,26 +137,43 @@ exiting_dump(Config) when is_list(Config) ->
free_dump(Config) when is_list(Config) ->
Dump = filename:join(proplists:get_value(priv_dir, Config),"signal_abort.dump"),
- {ok, Node} = start_node(Config),
-
- Self = self(),
-
- Pid = spawn_link(Node,
- fun() ->
- Self ! ready,
- receive
- ok ->
- unlink(Self),
- exit(lists:duplicate(1000,1000))
- end
- end),
+ {ok, NodeA} = start_node(Config),
+ {ok, NodeB} = start_node(Config),
- true = rpc:call(Node, os, putenv, ["ERL_CRASH_DUMP",Dump]),
- [erlang:monitor(process, Pid) || _ <- lists:seq(1,10000)],
- receive ready -> unlink(Pid), Pid ! ok end,
+ Self = self(),
- rpc:call(Node, erlang, halt, ["dump"]),
+ PidA = spawn_link(
+ NodeA,
+ fun() ->
+ Self ! ready,
+ receive
+ ok ->
+ spawn(fun() ->
+ erlang:system_monitor(self(), [busy_dist_port]),
+ timer:sleep(5),
+ receive
+ M ->
+ io:format("~p",[M]),
+ erlang:halt("dump")
+ end
+ end),
+ exit(lists:duplicate(1000000,100))
+ end
+ end),
+
+ spawn_link(NodeB,
+ fun() ->
+ [erlang:monitor(process, PidA) || _ <- lists:seq(1,10000)],
+ Self ! done,
+ receive _ -> ok end
+ end),
+
+ receive done -> ok end,
+ true = rpc:call(NodeA, os, putenv, ["ERL_CRASH_DUMP",Dump]),
+ ct:pal("~p",[rpc:call(NodeA, distribution_SUITE, make_busy, [NodeB, 1000])]),
+
+ receive ready -> unlink(PidA), PidA ! ok end,
{ok, Bin} = get_dump_when_done(Dump),
diff --git a/erts/emulator/test/persistent_term_SUITE.erl b/erts/emulator/test/persistent_term_SUITE.erl
index 93eb026ced..c9874e5679 100644
--- a/erts/emulator/test/persistent_term_SUITE.erl
+++ b/erts/emulator/test/persistent_term_SUITE.erl
@@ -25,7 +25,9 @@
basic/1,purging/1,sharing/1,get_trapping/1,
info/1,info_trapping/1,killed_while_trapping/1,
off_heap_values/1,keys/1,collisions/1,
- init_restart/1]).
+ init_restart/1, put_erase_trapping/1,
+ killed_while_trapping_put/1,
+ killed_while_trapping_erase/1]).
%%
-export([test_init_restart_cmd/1]).
@@ -37,7 +39,8 @@ suite() ->
all() ->
[basic,purging,sharing,get_trapping,info,info_trapping,
killed_while_trapping,off_heap_values,keys,collisions,
- init_restart].
+ init_restart, put_erase_trapping, killed_while_trapping_put,
+ killed_while_trapping_erase].
init_per_suite(Config) ->
%% Put a term in the dict so that we know that the testcases handle
@@ -627,3 +630,69 @@ chk_not_stuck(Term) ->
pget({_, Initial}) ->
persistent_term:get() -- Initial.
+
+
+killed_while_trapping_put(_Config) ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ repeat(
+ fun() ->
+ NrOfPutsInChild = 10000,
+ do_puts(2500, my_value),
+ Pid =
+ spawn(fun() ->
+ do_puts(NrOfPutsInChild, my_value2)
+ end),
+ timer:sleep(1),
+ erlang:exit(Pid, kill),
+ do_erases(NrOfPutsInChild)
+ end,
+ 10),
+ erts_debug:set_internal_state(available_internal_state, false).
+
+killed_while_trapping_erase(_Config) ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ repeat(
+ fun() ->
+ NrOfErases = 2500,
+ do_puts(NrOfErases, my_value),
+ Pid =
+ spawn(fun() ->
+ do_erases(NrOfErases)
+ end),
+ timer:sleep(1),
+ erlang:exit(Pid, kill),
+ do_erases(NrOfErases)
+ end,
+ 10),
+ erts_debug:set_internal_state(available_internal_state, false).
+
+put_erase_trapping(_Config) ->
+ NrOfItems = 5000,
+ erts_debug:set_internal_state(available_internal_state, true),
+ do_puts(NrOfItems, first),
+ do_puts(NrOfItems, second),
+ do_erases(NrOfItems),
+ erts_debug:set_internal_state(available_internal_state, false).
+
+do_puts(0, _) -> ok;
+do_puts(NrOfPuts, ValuePrefix) ->
+ Key = {?MODULE, NrOfPuts},
+ Value = {ValuePrefix, NrOfPuts},
+ erts_debug:set_internal_state(reds_left, rand:uniform(250)),
+ persistent_term:put(Key, Value),
+ Value = persistent_term:get(Key),
+ do_puts(NrOfPuts - 1, ValuePrefix).
+
+do_erases(0) -> ok;
+do_erases(NrOfErases) ->
+ Key = {?MODULE,NrOfErases},
+ erts_debug:set_internal_state(reds_left, rand:uniform(500)),
+ persistent_term:erase(Key),
+ not_found = persistent_term:get(Key, not_found),
+ do_erases(NrOfErases - 1).
+
+repeat(_Fun, 0) ->
+ ok;
+repeat(Fun, N) ->
+ Fun(),
+ repeat(Fun, N-1).
diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src
index bcd64d242e..8cfc2d549e 100644
--- a/erts/etc/unix/cerl.src
+++ b/erts/etc/unix/cerl.src
@@ -320,7 +320,7 @@ if [ "x$GDB" = "x" ]; then
if [ "$1" = "-p" ]; then
echo 'set $etp_rr_run_until_beam = 1' >> $cmdfile
fi
- cat $ROOTDIR/erts/etc/unix/etp-commands.in >> $cmdfile
+ cat $ROOTDIR/erts/etc/unix/etp-commands >> $cmdfile
exec rr replay -x $cmdfile $*
elif [ $1 = ps ]; then
shift
diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in
index dc28107ef5..f2b2ab1d9a 100644
--- a/erts/etc/unix/etp-commands.in
+++ b/erts/etc/unix/etp-commands.in
@@ -2090,7 +2090,7 @@ define etp-process-info-int
etp-pid2proc-1 $etp_proc->common.id
etp-process-info $proc
else
- if (*(((Uint32 *) &($etp_proc->state))) & 0x4) == 0
+ if (*(((Uint32 *) &($etp_proc->state))) & 0x800) == 0
if ($etp_proc->common.u.alive.reg)
printf " Registered name: "
etp-1 $etp_proc->common.u.alive.reg->name
@@ -2176,8 +2176,8 @@ end
define etp-processes-free-de-int
set $de_ix = 0
- while $de_ix < ($arg1)
- set $de = ($arg0)+$de_ix
+ set $de = ($arg0)
+ while $de
set $susp = $de->suspended
set $susp_curr = $susp
set $first_loop = 1
@@ -2191,7 +2191,7 @@ define etp-processes-free-de-int
set $first_loop = 0
set $susp_curr = $susp_curr->next
end
- set $de_ix++
+ set $de = $de->next
end
end
diff --git a/erts/etc/unix/to_erl.c b/erts/etc/unix/to_erl.c
index ed4fe12e8b..1448980f77 100644
--- a/erts/etc/unix/to_erl.c
+++ b/erts/etc/unix/to_erl.c
@@ -415,7 +415,7 @@ int main(int argc, char **argv)
if (len) {
#ifdef DEBUG
- (void)write(1, buf, len);
+ write_all(1, buf, len);
#endif
if (write_all(wfd, buf, len) != len) {
fprintf(stderr, "Error in writing to FIFO.\n");
diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl
index 6a34299dd2..536212af2e 100644
--- a/erts/test/z_SUITE.erl
+++ b/erts/test/z_SUITE.erl
@@ -88,10 +88,10 @@ find_cerl(DBTop) ->
[Cerl | _ ] ->
case filelib:is_regular(Cerl) of
true -> Cerl;
- _ -> false
+ _ -> find_cerl(false)
end;
_ ->
- false
+ find_cerl(false)
end.
is_dir(false) ->
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index efd2be94cb..09a5a6c104 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -1604,8 +1604,13 @@ infer_types_1(#value{op={bif,'=:='},args=[LHS,RHS]}) ->
end;
infer_types_1(#value{op={bif,element},args=[{integer,Index}=Key,Tuple]}) ->
fun(Val, S) ->
- Type = get_term_type(Val, S),
- update_type(fun meet/2,{tuple,[Index],#{ Key => Type }}, Tuple, S)
+ case is_value_alive(Tuple, S) of
+ true ->
+ Type = {tuple,[Index], #{ Key => get_term_type(Val, S) }},
+ update_type(fun meet/2, Type, Tuple, S);
+ false ->
+ S
+ end
end;
infer_types_1(#value{op={bif,is_atom},args=[Src]}) ->
infer_type_test_bif({atom,[]}, Src);
@@ -1629,7 +1634,10 @@ infer_types_1(#value{op={bif,is_tuple},args=[Src]}) ->
infer_type_test_bif({tuple,[0],#{}}, Src);
infer_types_1(#value{op={bif,tuple_size}, args=[Tuple]}) ->
fun({integer,Arity}, S) ->
- update_type(fun meet/2, {tuple,Arity,#{}}, Tuple, S);
+ case is_value_alive(Tuple, S) of
+ true -> update_type(fun meet/2, {tuple,Arity,#{}}, Tuple, S);
+ false -> S
+ end;
(_, S) -> S
end;
infer_types_1(_) ->
@@ -1637,7 +1645,10 @@ infer_types_1(_) ->
infer_type_test_bif(Type, Src) ->
fun({atom,true}, S) ->
- update_type(fun meet/2, Type, Src, S);
+ case is_value_alive(Src, S) of
+ true -> update_type(fun meet/2, Type, Src, S);
+ false -> S
+ end;
(_, S) ->
S
end.
@@ -2274,6 +2285,9 @@ get_raw_type(#value_ref{}=Ref, #vst{current=#st{vs=Vs}}) ->
get_raw_type(Src, #vst{}) ->
get_literal_type(Src).
+is_value_alive(#value_ref{}=Ref, #vst{current=#st{vs=Vs}}) ->
+ is_map_key(Ref, Vs).
+
get_literal_type(nil=T) -> T;
get_literal_type({atom,A}=T) when is_atom(A) -> T;
get_literal_type({float,F}=T) when is_float(F) -> T;
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index de5a3c2873..6b1438abdd 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -35,7 +35,7 @@
map_field_lists/1,cover_bin_opt/1,
val_dsetel/1,bad_tuples/1,bad_try_catch_nesting/1,
receive_stacked/1,aliased_types/1,type_conflict/1,
- infer_on_eq/1]).
+ infer_on_eq/1,infer_dead_value/1]).
-include_lib("common_test/include/ct.hrl").
@@ -65,7 +65,7 @@ groups() ->
map_field_lists,cover_bin_opt,val_dsetel,
bad_tuples,bad_try_catch_nesting,
receive_stacked,aliased_types,type_conflict,
- infer_on_eq]}].
+ infer_on_eq,infer_dead_value]}].
init_per_suite(Config) ->
test_lib:recompile(?MODULE),
@@ -679,6 +679,27 @@ infer_on_eq_4(T) ->
true = erlang:tuple_size(T) =:= 1,
{ok, erlang:element(1, T)}.
+%% ERIERL-348; types were inferred for dead values, causing validation to fail.
+
+infer_dead_value(Config) when is_list(Config) ->
+ a = idv_1({a, b, c, d, e, f, g}, {0, 0, 0, 0, 0, 0, 0}),
+ b = idv_1({a, b, c, d, 0, 0, 0}, {a, b, c, d, 0, 0, 0}),
+ c = idv_1({0, 0, 0, 0, 0, f, g}, {0, 0, 0, 0, 0, f, g}),
+ error = idv_1(gurka, gaffel),
+ ok.
+
+idv_1({_A, _B, _C, _D, _E, _F, _G},
+ {0, 0, 0, 0, 0, 0, 0}) ->
+ a;
+idv_1({A, B, C, D,_E, _F, _G}=_Tuple1,
+ {A, B, C, D, 0, 0, 0}=_Tuple2) ->
+ b;
+idv_1({_A, _B, _C, _D, _E, F, G},
+ {0, 0, 0, 0, 0, F, G}) ->
+ c;
+idv_1(_A, _B) ->
+ error.
+
%%%-------------------------------------------------------------------------
transform_remove(Remove, Module) ->
diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c
index 20707c0531..75cddeb1e9 100644
--- a/lib/crypto/c_src/algorithms.c
+++ b/lib/crypto/c_src/algorithms.c
@@ -255,29 +255,66 @@ void init_algorithms_types(ErlNifEnv* env)
ASSERT(algo_rsa_opts_cnt <= sizeof(algo_rsa_opts)/sizeof(ERL_NIF_TERM));
}
-ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+
+ERL_NIF_TERM hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ unsigned int cnt =
+#ifdef FIPS_SUPPORT
+ FIPS_mode() ? algo_hash_fips_cnt :
+#endif
+ algo_hash_cnt;
+
+ return enif_make_list_from_array(env, algo_hash, cnt);
+}
+
+ERL_NIF_TERM pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ unsigned int cnt =
+#ifdef FIPS_SUPPORT
+ FIPS_mode() ? algo_pubkey_fips_cnt :
+#endif
+ algo_pubkey_cnt;
+
+ return enif_make_list_from_array(env, algo_pubkey, cnt);
+}
+
+
+ERL_NIF_TERM cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ return cipher_types_as_list(env); /* Exclude old api ciphers */
+}
+
+ERL_NIF_TERM mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ unsigned int cnt =
+#ifdef FIPS_SUPPORT
+ FIPS_mode() ? algo_mac_fips_cnt :
+#endif
+ algo_mac_cnt;
+
+ return enif_make_list_from_array(env, algo_mac, cnt);
+}
+
+
+ERL_NIF_TERM curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
+ unsigned int cnt =
#ifdef FIPS_SUPPORT
- int fips_mode = FIPS_mode();
+ FIPS_mode() ? algo_curve_fips_cnt :
+#endif
+ algo_curve_cnt;
+
+ return enif_make_list_from_array(env, algo_curve, cnt);
+}
+
+
+ERL_NIF_TERM rsa_opts_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ unsigned int cnt =
+#ifdef FIPS_SUPPORT
+ FIPS_mode() ? algo_rsa_opts_fips_cnt :
+#endif
+ algo_rsa_opts_cnt;
- unsigned int hash_cnt = fips_mode ? algo_hash_fips_cnt : algo_hash_cnt;
- unsigned int pubkey_cnt = fips_mode ? algo_pubkey_fips_cnt : algo_pubkey_cnt;
- unsigned int mac_cnt = fips_mode ? algo_mac_fips_cnt : algo_mac_cnt;
- unsigned int curve_cnt = fips_mode ? algo_curve_fips_cnt : algo_curve_cnt;
- unsigned int rsa_opts_cnt = fips_mode ? algo_rsa_opts_fips_cnt : algo_rsa_opts_cnt;
-#else
- unsigned int hash_cnt = algo_hash_cnt;
- unsigned int pubkey_cnt = algo_pubkey_cnt;
- unsigned int mac_cnt = algo_mac_cnt;
- unsigned int curve_cnt = algo_curve_cnt;
- unsigned int rsa_opts_cnt = algo_rsa_opts_cnt;
-#endif
- return enif_make_tuple6(env,
- enif_make_list_from_array(env, algo_hash, hash_cnt),
- enif_make_list_from_array(env, algo_pubkey, pubkey_cnt),
- cipher_types_as_list(env),
- enif_make_list_from_array(env, algo_mac, mac_cnt),
- enif_make_list_from_array(env, algo_curve, curve_cnt),
- enif_make_list_from_array(env, algo_rsa_opts, rsa_opts_cnt)
- );
+ return enif_make_list_from_array(env, algo_rsa_opts, cnt);
}
diff --git a/lib/crypto/c_src/algorithms.h b/lib/crypto/c_src/algorithms.h
index 068fb661ec..4ad8b56db8 100644
--- a/lib/crypto/c_src/algorithms.h
+++ b/lib/crypto/c_src/algorithms.h
@@ -25,6 +25,11 @@
void init_algorithms_types(ErlNifEnv* env);
-ERL_NIF_TERM algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM hash_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM pubkey_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ERL_NIF_TERM rsa_opts_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
#endif /* E_ALGORITHMS_H__ */
diff --git a/lib/crypto/c_src/atoms.c b/lib/crypto/c_src/atoms.c
index 0793ffa6ca..059c14690f 100644
--- a/lib/crypto/c_src/atoms.c
+++ b/lib/crypto/c_src/atoms.c
@@ -70,6 +70,7 @@ ERL_NIF_TERM atom_onbasis;
ERL_NIF_TERM atom_aes_cfb8;
ERL_NIF_TERM atom_aes_cfb128;
+ERL_NIF_TERM atom_aes_ige256;
#ifdef HAVE_GCM
ERL_NIF_TERM atom_aes_gcm;
#endif
@@ -188,6 +189,7 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM
atom_aes_cfb8 = enif_make_atom(env, "aes_cfb8");
atom_aes_cfb128 = enif_make_atom(env, "aes_cfb128");
+ atom_aes_ige256 = enif_make_atom(env, "aes_ige256");
#ifdef HAVE_GCM
atom_aes_gcm = enif_make_atom(env, "aes_gcm");
#endif
diff --git a/lib/crypto/c_src/atoms.h b/lib/crypto/c_src/atoms.h
index 24f6dc26fd..f5913de96f 100644
--- a/lib/crypto/c_src/atoms.h
+++ b/lib/crypto/c_src/atoms.h
@@ -74,6 +74,7 @@ extern ERL_NIF_TERM atom_onbasis;
extern ERL_NIF_TERM atom_aes_cfb8;
extern ERL_NIF_TERM atom_aes_cfb128;
+extern ERL_NIF_TERM atom_aes_ige256;
#ifdef HAVE_GCM
extern ERL_NIF_TERM atom_aes_gcm;
#endif
diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c
index 8f0c93c5db..00072af632 100644
--- a/lib/crypto/c_src/cipher.c
+++ b/lib/crypto/c_src/cipher.c
@@ -62,34 +62,18 @@ static struct cipher_type_t cipher_types[] =
{{"blowfish_ecb"}, {NULL}, 0, 0},
#endif
- {{"aes_cbc"}, {&EVP_aes_128_cbc}, 16, 0},
- {{"aes_cbc"}, {&EVP_aes_192_cbc}, 24, 0},
- {{"aes_cbc"}, {&EVP_aes_256_cbc}, 32, 0},
-
{{"aes_128_cbc"}, {&EVP_aes_128_cbc}, 16, 0},
{{"aes_192_cbc"}, {&EVP_aes_192_cbc}, 24, 0},
{{"aes_256_cbc"}, {&EVP_aes_256_cbc}, 32, 0},
- {{"aes_cfb8"}, {&EVP_aes_128_cfb8}, 16, NO_FIPS_CIPHER | AES_CFBx},
- {{"aes_cfb8"}, {&EVP_aes_192_cfb8}, 24, NO_FIPS_CIPHER | AES_CFBx},
- {{"aes_cfb8"}, {&EVP_aes_256_cfb8}, 32, NO_FIPS_CIPHER | AES_CFBx},
-
{{"aes_128_cfb8"}, {&EVP_aes_128_cfb8}, 16, NO_FIPS_CIPHER | AES_CFBx},
{{"aes_192_cfb8"}, {&EVP_aes_192_cfb8}, 24, NO_FIPS_CIPHER | AES_CFBx},
{{"aes_256_cfb8"}, {&EVP_aes_256_cfb8}, 32, NO_FIPS_CIPHER | AES_CFBx},
- {{"aes_cfb128"}, {&EVP_aes_128_cfb128}, 16, NO_FIPS_CIPHER | AES_CFBx},
- {{"aes_cfb128"}, {&EVP_aes_192_cfb128}, 24, NO_FIPS_CIPHER | AES_CFBx},
- {{"aes_cfb128"}, {&EVP_aes_256_cfb128}, 32, NO_FIPS_CIPHER | AES_CFBx},
-
{{"aes_128_cfb128"}, {&EVP_aes_128_cfb128}, 16, NO_FIPS_CIPHER | AES_CFBx},
{{"aes_192_cfb128"}, {&EVP_aes_192_cfb128}, 24, NO_FIPS_CIPHER | AES_CFBx},
{{"aes_256_cfb128"}, {&EVP_aes_256_cfb128}, 32, NO_FIPS_CIPHER | AES_CFBx},
- {{"aes_ecb"}, {&EVP_aes_128_ecb}, 16, ECB_BUG_0_9_8L},
- {{"aes_ecb"}, {&EVP_aes_192_ecb}, 24, ECB_BUG_0_9_8L},
- {{"aes_ecb"}, {&EVP_aes_256_ecb}, 32, ECB_BUG_0_9_8L},
-
{{"aes_128_ecb"}, {&EVP_aes_128_ecb}, 16, ECB_BUG_0_9_8L},
{{"aes_192_ecb"}, {&EVP_aes_192_ecb}, 24, ECB_BUG_0_9_8L},
{{"aes_256_ecb"}, {&EVP_aes_256_ecb}, 32, ECB_BUG_0_9_8L},
@@ -98,16 +82,10 @@ static struct cipher_type_t cipher_types[] =
{{"aes_128_ctr"}, {&EVP_aes_128_ctr}, 16, 0},
{{"aes_192_ctr"}, {&EVP_aes_192_ctr}, 24, 0},
{{"aes_256_ctr"}, {&EVP_aes_256_ctr}, 32, 0},
- {{"aes_ctr"}, {&EVP_aes_128_ctr}, 16, 0},
- {{"aes_ctr"}, {&EVP_aes_192_ctr}, 24, 0},
- {{"aes_ctr"}, {&EVP_aes_256_ctr}, 32, 0},
#else
{{"aes_128_ctr"}, {NULL}, 16, AES_CTR_COMPAT},
{{"aes_192_ctr"}, {NULL}, 24, AES_CTR_COMPAT},
{{"aes_256_ctr"}, {NULL}, 32, AES_CTR_COMPAT},
- {{"aes_ctr"}, {NULL}, 16, AES_CTR_COMPAT},
- {{"aes_ctr"}, {NULL}, 24, AES_CTR_COMPAT},
- {{"aes_ctr"}, {NULL}, 32, AES_CTR_COMPAT},
#endif
#if defined(HAVE_CHACHA20)
@@ -124,28 +102,20 @@ static struct cipher_type_t cipher_types[] =
#endif
#if defined(HAVE_GCM)
- {{"aes_gcm"}, {&EVP_aes_128_gcm}, 16, AEAD_CIPHER|GCM_MODE, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}},
- {{"aes_gcm"}, {&EVP_aes_192_gcm}, 24, AEAD_CIPHER|GCM_MODE, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}},
- {{"aes_gcm"}, {&EVP_aes_256_gcm}, 32, AEAD_CIPHER|GCM_MODE, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}},
{{"aes_128_gcm"}, {&EVP_aes_128_gcm}, 16, AEAD_CIPHER|GCM_MODE, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}},
{{"aes_192_gcm"}, {&EVP_aes_192_gcm}, 24, AEAD_CIPHER|GCM_MODE, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}},
{{"aes_256_gcm"}, {&EVP_aes_256_gcm}, 32, AEAD_CIPHER|GCM_MODE, {{EVP_CTRL_GCM_SET_IVLEN,EVP_CTRL_GCM_GET_TAG,EVP_CTRL_GCM_SET_TAG}}},
#else
- {{"aes_gcm"}, {NULL}, 0, AEAD_CIPHER|GCM_MODE, {{0,0,0}}},
{{"aes_128_gcm"}, {NULL}, 16, AEAD_CIPHER|GCM_MODE, {{0,0,0}}},
{{"aes_192_gcm"}, {NULL}, 24, AEAD_CIPHER|GCM_MODE, {{0,0,0}}},
{{"aes_256_gcm"}, {NULL}, 32, AEAD_CIPHER|GCM_MODE, {{0,0,0}}},
#endif
#if defined(HAVE_CCM)
- {{"aes_ccm"}, {&EVP_aes_128_ccm}, 16, AEAD_CIPHER|CCM_MODE, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}},
- {{"aes_ccm"}, {&EVP_aes_192_ccm}, 24, AEAD_CIPHER|CCM_MODE, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}},
- {{"aes_ccm"}, {&EVP_aes_256_ccm}, 32, AEAD_CIPHER|CCM_MODE, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}},
{{"aes_128_ccm"}, {&EVP_aes_128_ccm}, 16, AEAD_CIPHER|CCM_MODE, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}},
{{"aes_192_ccm"}, {&EVP_aes_192_ccm}, 24, AEAD_CIPHER|CCM_MODE, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}},
{{"aes_256_ccm"}, {&EVP_aes_256_ccm}, 32, AEAD_CIPHER|CCM_MODE, {{EVP_CTRL_CCM_SET_IVLEN,EVP_CTRL_CCM_GET_TAG,EVP_CTRL_CCM_SET_TAG}}},
#else
- {{"aes_ccm"}, {NULL}, 0, AEAD_CIPHER|CCM_MODE, {{0,0,0}}},
{{"aes_128_ccm"}, {NULL}, 16, AEAD_CIPHER|CCM_MODE, {{0,0,0}}},
{{"aes_192_ccm"}, {NULL}, 24, AEAD_CIPHER|CCM_MODE, {{0,0,0}}},
{{"aes_256_ccm"}, {NULL}, 32, AEAD_CIPHER|CCM_MODE, {{0,0,0}}},
@@ -359,13 +329,15 @@ ERL_NIF_TERM cipher_types_as_list(ErlNifEnv* env)
prev = atom_undefined;
for (p = cipher_types; (p->type.atom & (p->type.atom != atom_false)); p++) {
- if ((prev != p->type.atom) &&
- ((p->cipher.p != NULL) ||
- (p->flags & (NON_EVP_CIPHER|AES_CTR_COMPAT)) ) && /* Special handling. Bad indeed... */
- ! FORBIDDEN_IN_FIPS(p)
- )
- hd = enif_make_list_cell(env, p->type.atom, hd);
- prev = p->type.atom;
+ if ((prev == p->type.atom) ||
+ FORBIDDEN_IN_FIPS(p) )
+ continue;
+
+ if ((p->cipher.p != NULL) ||
+ (p->type.atom == atom_aes_ige256)) /* Special handling. Bad indeed... */
+ {
+ hd = enif_make_list_cell(env, p->type.atom, hd);
+ }
}
return hd;
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index a8014745c8..d533cba140 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -63,7 +63,12 @@ static ErlNifFunc nif_funcs[] = {
{"info_lib", 0, info_lib, 0},
{"info_fips", 0, info_fips, 0},
{"enable_fips_mode", 1, enable_fips_mode, 0},
- {"algorithms", 0, algorithms, 0},
+ {"hash_algorithms", 0, hash_algorithms, 0},
+ {"pubkey_algorithms", 0, pubkey_algorithms, 0},
+ {"cipher_algorithms", 0, cipher_algorithms, 0},
+ {"mac_algorithms", 0, mac_algorithms, 0},
+ {"curve_algorithms", 0, curve_algorithms, 0},
+ {"rsa_opts_algorithms", 0, rsa_opts_algorithms, 0},
{"hash_info", 1, hash_info_nif, 0},
{"hash_nif", 2, hash_nif, 0},
{"hash_init_nif", 1, hash_init_nif, 0},
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 3b431cceba..98378412d4 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -61,7 +61,8 @@
crypto_one_time/4, crypto_one_time/5,
crypto_one_time_aead/6, crypto_one_time_aead/7,
crypto_dyn_iv_init/3,
- crypto_dyn_iv_update/3
+ crypto_dyn_iv_update/3,
+ supports/1
]).
@@ -499,15 +500,22 @@ stop() ->
Macs :: [hmac | cmac | poly1305],
Curves :: [ec_named_curve() | edwards_curve_dh() | edwards_curve_ed()],
RSAopts :: [rsa_sign_verify_opt() | rsa_opt()] .
-supports()->
- {Hashs, PubKeys, Ciphers, Macs, Curves, RsaOpts} = algorithms(),
- [{hashs, Hashs},
- {ciphers, prepend_cipher_aliases(Ciphers)},
- {public_keys, PubKeys},
- {macs, Macs},
- {curves, Curves},
- {rsa_opts, RsaOpts}
- ].
+supports() ->
+ [{hashs, hash_algorithms()},
+ {ciphers, prepend_old_aliases( cipher_algorithms())},
+ {public_keys, pubkey_algorithms()},
+ {macs, mac_algorithms()},
+ {curves, curve_algorithms()},
+ {rsa_opts, rsa_opts_algorithms()}
+ ].
+
+supports(hashs) -> hash_algorithms();
+supports(public_keys) -> pubkey_algorithms();
+supports(ciphers) -> cipher_algorithms();
+supports(macs) -> mac_algorithms();
+supports(curves) -> curve_algorithms();
+supports(rsa_opts) -> rsa_opts_algorithms().
+
-spec info_lib() -> [{Name,VerNum,VerStr}] when Name :: binary(),
VerNum :: integer(),
@@ -700,7 +708,7 @@ poly1305(Key, Data) ->
| xts_mode
.
-%% These ciphers are not available via the EVP interface on older cryptolibs.
+%% %% These ciphers are not available via the EVP interface on older cryptolibs.
cipher_info(aes_ctr) ->
#{block_size => 1,iv_length => 16,key_length => 32,mode => ctr_mode,type => undefined};
cipher_info(aes_128_ctr) ->
@@ -709,9 +717,36 @@ cipher_info(aes_192_ctr) ->
#{block_size => 1,iv_length => 16,key_length => 24,mode => ctr_mode,type => undefined};
cipher_info(aes_256_ctr) ->
#{block_size => 1,iv_length => 16,key_length => 32,mode => ctr_mode,type => undefined};
-%% This cipher is handled specialy.
+%% %% This cipher is handled specialy.
cipher_info(aes_ige256) ->
#{block_size => 16,iv_length => 32,key_length => 16,mode => ige_mode,type => undefined};
+%% %% These ciphers belong to the "old" interface:
+%% cipher_info(aes_cbc) ->
+%% #{block_size => 16,iv_length => 16,key_length => 24,mode => cbc_mode,type => 423};
+%% cipher_info(aes_cbc128) ->
+%% #{block_size => 16,iv_length => 16,key_length => 16,mode => cbc_mode,type => 419};
+%% cipher_info(aes_cbc256) ->
+%% #{block_size => 16,iv_length => 16,key_length => 32,mode => cbc_mode,type => 427};
+%% cipher_info(aes_ccm) ->
+%% #{block_size => 1,iv_length => 12,key_length => 24,mode => ccm_mode,type => 899};
+%% cipher_info(aes_cfb128) ->
+%% #{block_size => 1,iv_length => 16,key_length => 32,mode => cfb_mode,type => 429};
+%% cipher_info(aes_cfb8) ->
+%% #{block_size => 1,iv_length => 16,key_length => 32,mode => cfb_mode,type => 429};
+%% cipher_info(aes_ecb) ->
+%% #{block_size => 16,iv_length => 0,key_length => 24,mode => ecb_mode,type => 422};
+%% cipher_info(aes_gcm) ->
+%% #{block_size => 1,iv_length => 12,key_length => 24,mode => gcm_mode,type => 898};
+%% cipher_info(des3_cbc) ->
+%% #{block_size => 8,iv_length => 8,key_length => 24,mode => cbc_mode,type => 44};
+%% cipher_info(des3_cbf) ->
+%% #{block_size => 1,iv_length => 8,key_length => 24,mode => cfb_mode,type => 30};
+%% cipher_info(des3_cfb) ->
+%% #{block_size => 1,iv_length => 8,key_length => 24,mode => cfb_mode,type => 30};
+%% cipher_info(des_ede3) ->
+%% #{block_size => 8,iv_length => 8,key_length => 24,mode => cbc_mode,type => 44};
+%% cipher_info(des_ede3_cbf) ->
+%% #{block_size => 1,iv_length => 8,key_length => 24,mode => cfb_mode,type => 30};
cipher_info(Type) ->
cipher_info_nif(alias(Type)).
@@ -1058,20 +1093,34 @@ ng_crypto_one_time_nif(_Cipher, _Key, _IVec, _Data, _EncryptFlg) -> ?nif_stub.
%%%----------------------------------------------------------------
%%% Cipher aliases
%%%
-prepend_cipher_aliases(L0) ->
- L =
- case lists:member(des_ede3_cbc, L0) of
+-define(if_also(Cipher, Ciphers, AliasCiphers),
+ case lists:member(Cipher, Ciphers) of
true ->
- [des3_cbc, des_ede3, des_ede3_cbf, des3_cbf, des3_cfb | L0];
+ AliasCiphers;
false ->
- L0
- end,
- case lists:member(aes_128_cbc, L0) of
- true ->
- [aes_cbc128, aes_cbc256 | L];
- false ->
- L
- end.
+ Ciphers
+ end).
+
+
+prepend_old_aliases(L0) ->
+ L1 = ?if_also(des_ede3_cbc, L0,
+ [des3_cbc, des_ede3, des_ede3_cbf, des3_cbf, des3_cfb | L0]),
+ L2 = ?if_also(aes_128_cbc, L1,
+ [aes_cbc, aes_cbc128, aes_cbc256 | L1]),
+ L3 = ?if_also(aes_128_ctr, L2,
+ [aes_ctr | L2]),
+ L4 = ?if_also(aes_128_ccm, L3,
+ [aes_ccm | L3]),
+ L5 = ?if_also(aes_128_gcm, L4,
+ [aes_gcm | L4]),
+ L6 = ?if_also(aes_128_cfb8, L5,
+ [aes_cfb8 | L5]),
+ L7 = ?if_also(aes_128_cfb128, L6,
+ [aes_cfb128 | L6]),
+ L8 = ?if_also(aes_128_ecb, L7,
+ [aes_ecb | L7]),
+ L8.
+
%%%---- des_ede3_cbc
@@ -1088,42 +1137,37 @@ alias(aes_cbc256) -> aes_256_cbc;
alias(Alg) -> Alg.
-%%%---- des_ede3_cbc
-alias(des3_cbc, _) -> des_ede3_cbc;
-alias(des_ede3, _) -> des_ede3_cbc;
-%%%---- des_ede3_cfb
-alias(des_ede3_cbf,_ ) -> des_ede3_cfb;
-alias(des3_cbf, _) -> des_ede3_cfb;
-alias(des3_cfb, _) -> des_ede3_cfb;
-%%%---- aes_*_cbc
-alias(aes_cbc128, _) -> aes_128_cbc;
-alias(aes_cbc256, _) -> aes_256_cbc;
+alias(Ciph, Key) -> alias2(alias(Ciph), Key).
+
+alias2(aes_cbc, Key) when size(Key)==16 -> aes_128_cbc;
+alias2(aes_cbc, Key) when size(Key)==24 -> aes_192_cbc;
+alias2(aes_cbc, Key) when size(Key)==32 -> aes_256_cbc;
-alias(aes_cbc, Key) when size(Key)==128 -> aes_128_cbc;
-alias(aes_cbc, Key) when size(Key)==192 -> aes_192_cbc;
-alias(aes_cbc, Key) when size(Key)==256 -> aes_256_cbc;
+alias2(aes_cfb8, Key) when size(Key)==16 -> aes_128_cfb8;
+alias2(aes_cfb8, Key) when size(Key)==24 -> aes_192_cfb8;
+alias2(aes_cfb8, Key) when size(Key)==32 -> aes_256_cfb8;
-alias(aes_cfb8, Key) when size(Key)==128 -> aes_128_cfb8;
-alias(aes_cfb8, Key) when size(Key)==192 -> aes_192_cfb8;
-alias(aes_cfb8, Key) when size(Key)==256 -> aes_256_cfb8;
+alias2(aes_cfb128, Key) when size(Key)==16 -> aes_128_cfb128;
+alias2(aes_cfb128, Key) when size(Key)==24 -> aes_192_cfb128;
+alias2(aes_cfb128, Key) when size(Key)==32 -> aes_256_cfb128;
-alias(aes_cfb128, Key) when size(Key)==128 -> aes_128_cfb128;
-alias(aes_cfb128, Key) when size(Key)==192 -> aes_192_cfb128;
-alias(aes_cfb128, Key) when size(Key)==256 -> aes_256_cfb128;
+alias2(aes_ctr, Key) when size(Key)==16 -> aes_128_ctr;
+alias2(aes_ctr, Key) when size(Key)==24 -> aes_192_ctr;
+alias2(aes_ctr, Key) when size(Key)==32 -> aes_256_ctr;
-alias(aes_ctr, Key) when size(Key)==128 -> aes_128_ctr;
-alias(aes_ctr, Key) when size(Key)==192 -> aes_192_ctr;
-alias(aes_ctr, Key) when size(Key)==256 -> aes_256_ctr;
+alias2(aes_ecb, Key) when size(Key)==16 -> aes_128_ecb;
+alias2(aes_ecb, Key) when size(Key)==24 -> aes_192_ecb;
+alias2(aes_ecb, Key) when size(Key)==32 -> aes_256_ecb;
-alias(aes_gcm, Key) when size(Key)==128 -> aes_128_gcm;
-alias(aes_gcm, Key) when size(Key)==192 -> aes_192_gcm;
-alias(aes_gcm, Key) when size(Key)==256 -> aes_256_gcm;
+alias2(aes_gcm, Key) when size(Key)==16 -> aes_128_gcm;
+alias2(aes_gcm, Key) when size(Key)==24 -> aes_192_gcm;
+alias2(aes_gcm, Key) when size(Key)==32 -> aes_256_gcm;
-alias(aes_ccm, Key) when size(Key)==128 -> aes_128_ccm;
-alias(aes_ccm, Key) when size(Key)==192 -> aes_192_ccm;
-alias(aes_ccm, Key) when size(Key)==256 -> aes_256_ccm;
+alias2(aes_ccm, Key) when size(Key)==16 -> aes_128_ccm;
+alias2(aes_ccm, Key) when size(Key)==24 -> aes_192_ccm;
+alias2(aes_ccm, Key) when size(Key)==32 -> aes_256_ccm;
-alias(Alg, _) -> Alg.
+alias2(Alg, _) -> Alg.
%%%================================================================
%%%
@@ -2387,7 +2431,13 @@ exor(Data1, Data2, _Size, MaxByts, Acc) ->
do_exor(_A, _B) -> ?nif_stub.
-algorithms() -> ?nif_stub.
+hash_algorithms() -> ?nif_stub.
+pubkey_algorithms() -> ?nif_stub.
+cipher_algorithms() -> ?nif_stub.
+mac_algorithms() -> ?nif_stub.
+curve_algorithms() -> ?nif_stub.
+rsa_opts_algorithms() -> ?nif_stub.
+
int_to_bin(X) when X < 0 -> int_to_bin_neg(X, []);
int_to_bin(X) -> int_to_bin_pos(X, []).
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 880fd7ab0b..56691223c4 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -116,8 +116,6 @@ groups() ->
{group, blowfish_ecb},
{group, blowfish_ofb64},
- {group, aes_cfb128},
- {group, aes_cfb8},
{group, aes_ige256},
{group, des_cbc},
{group, des_cfb},
@@ -125,7 +123,15 @@ groups() ->
{group, rc4},
?NEW_CIPHER_TYPE_SCHEMA,
- ?RETIRED_TYPE_ALIASES
+ {group, aes_128_cfb128},
+ {group, aes_192_cfb128},
+ {group, aes_256_cfb128},
+ {group, aes_128_cfb8},
+ {group, aes_192_cfb8},
+ {group, aes_256_cfb8},
+ ?RETIRED_TYPE_ALIASES,
+ {group, aes_cfb128},
+ {group, aes_cfb8}
]},
{fips, [], [
{group, no_blake2b},
@@ -210,9 +216,15 @@ groups() ->
{des_ede3_cbc, [], [block, api_ng, api_ng_one_shot, api_ng_tls]},
{des_ede3_cfb, [], [block, api_ng, api_ng_one_shot, api_ng_tls]},
{rc2_cbc, [], [block, api_ng, api_ng_one_shot, api_ng_tls]},
- {aes_cfb8, [], [block, api_ng, api_ng_one_shot, api_ng_tls]},
+ {aes_cfb8, [], [block]},
+ {aes_128_cfb8, [], [block, api_ng, api_ng_one_shot, api_ng_tls]},
+ {aes_192_cfb8, [], [block, api_ng, api_ng_one_shot, api_ng_tls]},
+ {aes_256_cfb8, [], [block, api_ng, api_ng_one_shot, api_ng_tls]},
{no_aes_cfb8, [], [no_support, no_block]},
- {aes_cfb128, [], [block, api_ng, api_ng_one_shot, api_ng_tls]},
+ {aes_cfb128, [], [block]},
+ {aes_128_cfb128, [], [block, api_ng, api_ng_one_shot, api_ng_tls]},
+ {aes_192_cfb128, [], [block, api_ng, api_ng_one_shot, api_ng_tls]},
+ {aes_256_cfb128, [], [block, api_ng, api_ng_one_shot, api_ng_tls]},
{no_aes_cfb128, [], [no_support, no_block]},
{aes_ige256, [], [block]},
{no_aes_ige256, [], [no_support, no_block]},
@@ -478,7 +490,7 @@ poly1305(Config) ->
%%--------------------------------------------------------------------
no_poly1305() ->
[{doc, "Test disabled poly1305 function"}].
-no_poly1305(Config) ->
+no_poly1305(_Config) ->
Key = <<133,214,190,120,87,85,109,51,127,68,82,254,66,213,6,168,1,
3,128,138,251,13,178,253,74,191,246,175,65,73,245,27>>,
Txt = <<"Cryptographic Forum Research Group">>,
@@ -889,8 +901,24 @@ cipher_info(Config) when is_list(Config) ->
#{type := _,key_length := _,iv_length := _,
block_size := _,mode := _} = crypto:cipher_info(aes_128_cbc),
{'EXIT',_} = (catch crypto:cipher_info(not_a_cipher)),
- lists:foreach(fun(C) -> crypto:cipher_info(C) end,
- proplists:get_value(ciphers, crypto:supports())).
+ case lists:foldl(fun(C,Ok) ->
+ try crypto:cipher_info(C)
+ of
+ _ -> Ok
+ catch Cls:Exc ->
+ ct:pal("~p:~p ~p",[Cls,Exc,C]),
+ false
+ end
+ end,
+ true,
+crypto:supports(ciphers)) of
+%% proplists:get_value(ciphers, crypto:supports())) of
+ true ->
+ ok;
+ false ->
+ ct:fail('Cipher unsupported',[])
+ end.
+
%%--------------------------------------------------------------------
hash_info() ->
@@ -1553,7 +1581,7 @@ rand_uniform_aux_test(N) ->
rand_uniform_aux_test(N-1).
crypto_rand_uniform(L,H) ->
- R1 = crypto:rand_uniform(L, H),
+ R1 = (L-1) + rand:uniform(H-L),
case (R1 >= L) and (R1 < H) of
true ->
ok;
@@ -2591,6 +2619,22 @@ aes_cfb8(Config) ->
"CFB8VarTxt256.rsp", "CFB8VarKey256.rsp", "CFB8GFSbox256.rsp", "CFB8KeySbox256.rsp",
"CFB8MMT128.rsp", "CFB8MMT192.rsp", "CFB8MMT256.rsp"]).
+aes_128_cfb8(Config) ->
+ read_rsp(Config, aes_128_cfb8,
+ ["CFB8VarTxt128.rsp", "CFB8VarKey128.rsp", "CFB8GFSbox128.rsp", "CFB8KeySbox128.rsp",
+ "CFB8MMT128.rsp"]).
+
+aes_192_cfb8(Config) ->
+ read_rsp(Config, aes_192_cfb8,
+ ["CFB8VarTxt192.rsp", "CFB8VarKey192.rsp", "CFB8GFSbox192.rsp", "CFB8KeySbox192.rsp",
+ "CFB8MMT192.rsp"]).
+
+aes_256_cfb8(Config) ->
+ read_rsp(Config, aes_256_cfb8,
+ ["CFB8VarTxt256.rsp", "CFB8VarKey256.rsp", "CFB8GFSbox256.rsp", "CFB8KeySbox256.rsp",
+ "CFB8MMT256.rsp"]).
+
+
aes_cfb128(Config) ->
read_rsp(Config, aes_cfb128,
["CFB128VarTxt128.rsp", "CFB128VarKey128.rsp", "CFB128GFSbox128.rsp", "CFB128KeySbox128.rsp",
@@ -2598,6 +2642,22 @@ aes_cfb128(Config) ->
"CFB128VarTxt256.rsp", "CFB128VarKey256.rsp", "CFB128GFSbox256.rsp", "CFB128KeySbox256.rsp",
"CFB128MMT128.rsp", "CFB128MMT192.rsp", "CFB128MMT256.rsp"]).
+aes_128_cfb128(Config) ->
+ read_rsp(Config, aes_128_cfb128,
+ ["CFB128VarTxt128.rsp", "CFB128VarKey128.rsp", "CFB128GFSbox128.rsp", "CFB128KeySbox128.rsp",
+ "CFB128MMT128.rsp"]).
+
+aes_192_cfb128(Config) ->
+ read_rsp(Config, aes_192_cfb128,
+ ["CFB128VarTxt192.rsp", "CFB128VarKey192.rsp", "CFB128GFSbox192.rsp", "CFB128KeySbox192.rsp",
+ "CFB128MMT192.rsp"]).
+
+aes_256_cfb128(Config) ->
+ read_rsp(Config, aes_256_cfb128,
+ ["CFB128VarTxt256.rsp", "CFB128VarKey256.rsp", "CFB128GFSbox256.rsp", "CFB128KeySbox256.rsp",
+ "CFB128MMT256.rsp"]).
+
+
blowfish_cbc(_) ->
[{blowfish_cbc,
hexstr2bin("0123456789ABCDEFF0E1D2C3B4A59687"),
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index 185c8c9ae6..a168b3c8c5 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -320,9 +320,12 @@ message_to_string({call_to_missing, [M, F, A]}) ->
message_to_string({exact_eq, [Type1, Op, Type2]}) ->
io_lib:format("The test ~ts ~s ~ts can never evaluate to 'true'\n",
[Type1, Op, Type2]);
-message_to_string({fun_app_args, [Args, Type]}) ->
+message_to_string({fun_app_args, [ArgNs, Args, Type]}) ->
+ PositionString = form_position_string(ArgNs),
io_lib:format("Fun application with arguments ~ts will fail"
- " since the function has type ~ts\n", [Args, Type]);
+ " since the function has type ~ts,"
+ " which differs in the ~s argument\n",
+ [Args, Type, PositionString]);
message_to_string({fun_app_no_fun, [Op, Type, Arity]}) ->
io_lib:format("Fun application will fail since ~ts :: ~ts"
" is not a function of arity ~w\n", [Op, Type, Arity]);
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 45b4abb253..f7aa167f5c 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -405,8 +405,13 @@ handle_apply(Tree, Map, State) ->
t_fun_args(OpType1, 'universe')),
case any_none(NewArgs) of
true ->
+ EnumNewArgs = lists:zip(lists:seq(1, length(NewArgs)),
+ NewArgs),
+ ArgNs = [Arg ||
+ {Arg, Type} <- EnumNewArgs, t_is_none(Type)],
Msg = {fun_app_args,
- [format_args(Args, ArgTypes, State),
+ [ArgNs,
+ format_args(Args, ArgTypes, State),
format_type(OpType, State)]},
State3 = state__add_warning(State2, ?WARN_FAILING_CALL,
Tree, Msg),
diff --git a/lib/dialyzer/test/small_SUITE_data/results/fun_app_args b/lib/dialyzer/test/small_SUITE_data/results/fun_app_args
new file mode 100644
index 0000000000..ac153a6fb2
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/fun_app_args
@@ -0,0 +1,3 @@
+
+fun_app_args.erl:12: Fun application with arguments ('b',[]) will fail since the function has type 'c' | fun(('a',[]) -> any()), which differs in the 1st argument
+fun_app_args.erl:12: The created fun has no local return
diff --git a/lib/dialyzer/test/small_SUITE_data/src/fun_app_args.erl b/lib/dialyzer/test/small_SUITE_data/src/fun_app_args.erl
new file mode 100644
index 0000000000..b4409bc550
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/fun_app_args.erl
@@ -0,0 +1,12 @@
+-module(fun_app_args).
+
+-export([t/1]).
+
+-type ft() :: fun((a, []) -> any()).
+
+-record(r, {
+ h = c :: c | ft()
+}).
+
+t(#r{h = H}) ->
+ fun(_) -> (H)(b, []) end.
diff --git a/lib/eldap/src/eldap.erl b/lib/eldap/src/eldap.erl
index 6497922852..9b7e254dfe 100644
--- a/lib/eldap/src/eldap.erl
+++ b/lib/eldap/src/eldap.erl
@@ -957,20 +957,20 @@ do_modify_dn_0(Data, Entry, NewRDN, DelOldRDN, NewSup, Controls) ->
do_unbind(Data) ->
Req = "",
log2(Data, "unbind request = ~p (has no reply)~n", [Req]),
- case Data#eldap.using_tls of
- true ->
- send_request(Data#eldap.fd, Data, Data#eldap.id, {unbindRequest, Req}),
- ssl:close(Data#eldap.fd);
- false ->
- OldTrapExit = process_flag(trap_exit, true),
- catch send_request(Data#eldap.fd, Data, Data#eldap.id, {unbindRequest, Req}),
- catch gen_tcp:close(Data#eldap.fd),
- receive
- {'EXIT', _From, _Reason} -> ok
- after 0 -> ok
- end,
- process_flag(trap_exit, OldTrapExit)
- end,
+ _ = case Data#eldap.using_tls of
+ true ->
+ send_request(Data#eldap.fd, Data, Data#eldap.id, {unbindRequest, Req}),
+ ssl:close(Data#eldap.fd);
+ false ->
+ OldTrapExit = process_flag(trap_exit, true),
+ catch send_request(Data#eldap.fd, Data, Data#eldap.id, {unbindRequest, Req}),
+ catch gen_tcp:close(Data#eldap.fd),
+ receive
+ {'EXIT', _From, _Reason} -> ok
+ after 0 -> ok
+ end,
+ process_flag(trap_exit, OldTrapExit)
+ end,
{no_reply, Data#eldap{binddn = (#eldap{})#eldap.binddn,
passwd = (#eldap{})#eldap.passwd,
fd = (#eldap{})#eldap.fd,
@@ -1130,7 +1130,7 @@ ldap_closed_p(Data, Emsg) when Data#eldap.using_tls == true ->
%% Check if the SSL socket seems to be alive or not
case catch ssl:sockname(Data#eldap.fd) of
{error, _} ->
- ssl:close(Data#eldap.fd),
+ _ = ssl:close(Data#eldap.fd),
{error, ldap_closed};
{ok, _} ->
{error, Emsg};
diff --git a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
index 2c9aa2e3a3..5342d02947 100644
--- a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
+++ b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl
@@ -1260,7 +1260,6 @@ get_text(#xmlElement{content=[E]}) ->
%% text_and_name_only(Es) -> {N, Ts}
text_and_a_name_only(Es) ->
- erlang:display(Es),
case [Name || #xmlElement{
name = a,
attributes = [#xmlAttribute{name=name}]}=Name <- Es] of
diff --git a/lib/erl_interface/doc/src/ei.xml b/lib/erl_interface/doc/src/ei.xml
index f081ca926a..254ae27cc8 100644
--- a/lib/erl_interface/doc/src/ei.xml
+++ b/lib/erl_interface/doc/src/ei.xml
@@ -183,6 +183,35 @@ typedef enum {
</func>
<func>
+ <name since="OTP @OTP-15712@"><ret>int</ret><nametext>ei_decode_bitstring(const char *buf, int *index, void *p, size_t plen, size_t *bitsp)</nametext></name>
+ <fsummary>Decode a bitstring.</fsummary>
+ <desc>
+ <p>Decodes a bitstring from the binary format.</p>
+ <taglist>
+ <tag><c>p</c></tag>
+ <item><p>Either <c>NULL</c> or points to a buffer where the bytes of the
+ bitstring will be written.</p>
+ </item>
+ <tag><c>plen</c></tag>
+ <item><p>The max size of the bitstring in <em>bytes</em>, that is the
+ size of the buffer if <c>p != NULL</c>.</p>
+ </item>
+ <tag><c>*bitsp</c></tag>
+ <item><p>If <c>bitsp</c> is not <c>NULL</c>, set to the actual
+ number of <em>bits</em> of the bitstring.</p>
+ </item>
+ </taglist>
+ <p>Returns <c>0</c> if it was a bitstring no longer than <c>plen</c>
+ bytes. The actual length of the bitstring will be
+ <c>(*bitsp+7)/8</c> bytes. If <c>(*bitsp % 8) > 0</c> only the high
+ <c>(*bitsp % 8)</c> bits of the last byte are significant.</p>
+ <p>Number of bits may be divisible by 8, which means a binary
+ decodable by <c>ei_decode_binary</c> is also decodable by
+ <c>ei_decode_bitstring</c>.</p>
+ </desc>
+ </func>
+
+ <func>
<name since=""><ret>int</ret><nametext>ei_decode_boolean(const char *buf, int *index, int *p)</nametext></name>
<fsummary>Decode a boolean.</fsummary>
<desc>
@@ -349,8 +378,10 @@ typedef enum {
<c>t</c> is actually an <c>ETERM**</c> (see
<seealso marker="erl_eterm"><c>erl_eterm</c></seealso>).
The term is later to be deallocated.</p>
- <p>Notice that this function is located in the <c>Erl_Interface</c>
- library.</p>
+ <note><p>This function is deprecated as of OTP 22 and will be removed in
+ OTP 23 together with the old legacy <c>erl_interface</c> library (functions
+ with prefix <c>erl_</c>).</p>
+ </note>
</desc>
</func>
@@ -459,6 +490,18 @@ typedef enum {
</func>
<func>
+ <name since="OTP @OTP-15712@"><ret>int</ret><nametext>ei_encode_bitstring(char *buf, int *index, const void *p, size_t bits)</nametext></name>
+ <name since="OTP @OTP-15712@"><ret>int</ret><nametext>ei_x_encode_bitstring(ei_x_buff* x, const void *p, size_t bits)</nametext></name>
+ <fsummary>Encode a bitstring.</fsummary>
+ <desc>
+ <p>Encodes a bitstring in the binary format. The data is at
+ <c>p</c>. The size of the data is <c>bits</c> bits or
+ <c>(bits+7)/8</c> bytes. If <c>(bits%8) > 0</c> only the high
+ <c>(bits%8)</c> bits of the last byte are significant.</p>
+ </desc>
+ </func>
+
+ <func>
<name since=""><ret>int</ret><nametext>ei_encode_boolean(char *buf, int *index, int p)</nametext></name>
<name since=""><ret>int</ret><nametext>ei_x_encode_boolean(ei_x_buff* x, int p)</nametext></name>
<fsummary>Encode a boolean.</fsummary>
@@ -656,6 +699,10 @@ ei_x_encode_string(&amp;x, "Banana");</pre>
<c>erl_interface</c>. Parameter <c>t</c> is
actually an <c>ETERM</c> pointer. This function
does not free the <c>ETERM</c>.</p>
+ <note><p>These functions are deprecated as of OTP 22 and will be removed in
+ OTP 23 together with the old legacy <c>erl_interface</c> library
+ (functions with prefix <c>erl_</c>).</p>
+ </note>
</desc>
</func>
<func>
@@ -725,12 +772,12 @@ ei_encode_tuple_header(buf, &amp;i, 0);</pre>
<name since=""><ret>int</ret><nametext>ei_get_type(const char *buf, const int *index, int *type, int *size)</nametext></name>
<fsummary>Fetch the type and size of an encoded term.</fsummary>
<desc>
- <p>Returns the type in <c>type</c> and size in
- <c>size</c> of the encoded term. For strings and atoms,
+ <p>Returns the type in <c>*type</c> and size in
+ <c>*size</c> of the encoded term. For strings and atoms,
size is the number of characters <em>not</em> including the
- terminating <c>NULL</c>. For binaries, <c>size</c> is the number of
- bytes. For lists and tuples, <c>size</c> is the arity of
- the object. For other types, <c>size</c> is 0. In all
+ terminating <c>NULL</c>. For binaries and bitstrings, <c>*size</c> is
+ the number of bytes. For lists, tuples and maps, <c>*size</c> is the
+ arity of the object. For other types, <c>*size</c> is 0. In all
cases, <c>index</c> is left unchanged.</p>
</desc>
</func>
diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h
index aa2a49098f..a860df3f77 100644
--- a/lib/erl_interface/include/ei.h
+++ b/lib/erl_interface/include/ei.h
@@ -154,11 +154,14 @@ typedef LONG_PTR ssize_t; /* Sigh... */
#define ERL_STRING_EXT 'k'
#define ERL_LIST_EXT 'l'
#define ERL_BINARY_EXT 'm'
+#define ERL_BIT_BINARY_EXT 'M'
#define ERL_SMALL_BIG_EXT 'n'
#define ERL_LARGE_BIG_EXT 'o'
#define ERL_NEW_FUN_EXT 'p'
#define ERL_MAP_EXT 't'
#define ERL_FUN_EXT 'u'
+#define ERL_EXPORT_EXT 'q'
+
#define ERL_NEW_CACHE 'N' /* c nodes don't know these two */
#define ERL_CACHED_ATOM 'C'
@@ -269,15 +272,23 @@ typedef struct {
typedef struct {
long arity;
char module[MAXATOMLEN_UTF8];
- erlang_char_encoding module_org_enc;
- char md5[16];
- long index;
- long old_index;
- long uniq;
- long n_free_vars;
- erlang_pid pid;
- long free_var_len;
- char* free_vars;
+ enum { EI_FUN_CLOSURE, EI_FUN_EXPORT } type;
+ union {
+ struct {
+ char md5[16];
+ long index;
+ long old_index;
+ long uniq;
+ long n_free_vars;
+ erlang_pid pid;
+ long free_var_len;
+ char* free_vars;
+ } closure;
+ struct {
+ char* func;
+ int func_allocated;
+ } exprt;
+ } u;
} erlang_fun;
/* a big */
@@ -515,7 +526,9 @@ int ei_x_encode_atom_len(ei_x_buff* x, const char* s, int len);
int ei_x_encode_atom_len_as(ei_x_buff* x, const char* s, int len,
erlang_char_encoding from, erlang_char_encoding to);
int ei_encode_binary(char *buf, int *index, const void *p, long len);
+int ei_encode_bitstring(char *buf, int *index, const void *p, size_t bits);
int ei_x_encode_binary(ei_x_buff* x, const void* s, int len);
+int ei_x_encode_bitstring(ei_x_buff* x, const void* p, size_t bits);
int ei_encode_pid(char *buf, int *index, const erlang_pid *p);
int ei_x_encode_pid(ei_x_buff* x, const erlang_pid* pid);
int ei_encode_fun(char* buf, int* index, const erlang_fun* p);
@@ -524,8 +537,8 @@ int ei_encode_port(char *buf, int *index, const erlang_port *p);
int ei_x_encode_port(ei_x_buff* x, const erlang_port *p);
int ei_encode_ref(char *buf, int *index, const erlang_ref *p);
int ei_x_encode_ref(ei_x_buff* x, const erlang_ref *p);
-int ei_encode_term(char *buf, int *index, void *t); /* ETERM* actually */
-int ei_x_encode_term(ei_x_buff* x, void* t);
+int ei_encode_term(char *buf, int *index, void *t) EI_DEPRECATED_ATTR;
+int ei_x_encode_term(ei_x_buff* x, void* t) EI_DEPRECATED_ATTR;
int ei_encode_trace(char *buf, int *index, const erlang_trace *p);
int ei_x_encode_trace(ei_x_buff* x, const erlang_trace *p);
int ei_encode_tuple_header(char *buf, int *index, int arity);
@@ -547,8 +560,6 @@ int ei_x_encode_map_header(ei_x_buff* x, long n);
*/
int ei_get_type(const char *buf, const int *index, int *type, int *size);
-int ei_get_type_internal(const char *buf, const int *index, int *type,
- int *size);
/* Step through buffer, decoding the given type into the buffer
* provided. On success, 0 is returned and index is updated to point
@@ -567,12 +578,13 @@ int ei_decode_string(const char *buf, int *index, char *p);
int ei_decode_atom(const char *buf, int *index, char *p);
int ei_decode_atom_as(const char *buf, int *index, char *p, int destlen, erlang_char_encoding want, erlang_char_encoding* was, erlang_char_encoding* result);
int ei_decode_binary(const char *buf, int *index, void *p, long *len);
+int ei_decode_bitstring(const char *buf, int *index, void *p, size_t plen, size_t *bitsp);
int ei_decode_fun(const char* buf, int* index, erlang_fun* p);
void free_fun(erlang_fun* f);
int ei_decode_pid(const char *buf, int *index, erlang_pid *p);
int ei_decode_port(const char *buf, int *index, erlang_port *p);
int ei_decode_ref(const char *buf, int *index, erlang_ref *p);
-int ei_decode_term(const char *buf, int *index, void *t); /* ETERM** actually */
+int ei_decode_term(const char *buf, int *index, void *t) EI_DEPRECATED_ATTR;
int ei_decode_trace(const char *buf, int *index, erlang_trace *p);
int ei_decode_tuple_header(const char *buf, int *index, int *arity);
int ei_decode_list_header(const char *buf, int *index, int *arity);
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
index 7a304e6d4f..0cbad235cc 100644
--- a/lib/erl_interface/src/connect/ei_connect.c
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -1846,6 +1846,7 @@ static int send_name_or_challenge(ei_socket_callbacks *cbs,
const char* function[] = {"SEND_NAME", "SEND_CHALLENGE"};
int err;
ssize_t len;
+ unsigned int flags;
if (f_chall)
siz += 4;
@@ -1867,7 +1868,7 @@ static int send_name_or_challenge(ei_socket_callbacks *cbs,
}
put8(s, 'n');
put16be(s, version);
- put32be(s, (DFLAG_EXTENDED_REFERENCES
+ flags = (DFLAG_EXTENDED_REFERENCES
| DFLAG_DIST_MONITOR
| DFLAG_EXTENDED_PIDS_PORTS
| DFLAG_FUN_TAGS
@@ -1876,7 +1877,14 @@ static int send_name_or_challenge(ei_socket_callbacks *cbs,
| DFLAG_SMALL_ATOM_TAGS
| DFLAG_UTF8_ATOMS
| DFLAG_MAP_TAG
- | DFLAG_BIG_CREATION));
+ | DFLAG_BIG_CREATION
+ | DFLAG_EXPORT_PTR_TAG
+ | DFLAG_BIT_BINARIES);
+ if (ei_internal_use_21_bitstr_expfun()) {
+ flags &= ~(DFLAG_EXPORT_PTR_TAG
+ | DFLAG_BIT_BINARIES);
+ }
+ put32be(s, flags);
if (f_chall)
put32be(s, challenge);
memcpy(s, nodename, strlen(nodename));
@@ -1941,8 +1949,7 @@ static int recv_challenge(ei_socket_callbacks *cbs, void *ctx,
goto error;
}
- if (!(*flags & DFLAG_EXTENDED_PIDS_PORTS)
- && !ei_internal_use_r9_pids_ports()) {
+ if (!(*flags & DFLAG_EXTENDED_PIDS_PORTS)) {
EI_TRACE_ERR0("recv_challenge","<- RECV_CHALLENGE peer cannot "
"handle extended pids and ports");
erl_errno = EIO;
@@ -2236,8 +2243,7 @@ static int recv_name(ei_socket_callbacks *cbs, void *ctx,
goto error;
}
- if (!(*flags & DFLAG_EXTENDED_PIDS_PORTS)
- && !ei_internal_use_r9_pids_ports()) {
+ if (!(*flags & DFLAG_EXTENDED_PIDS_PORTS)) {
EI_TRACE_ERR0("recv_name","<- RECV_NAME peer cannot "
"handle extended pids and ports");
erl_errno = EIO;
diff --git a/lib/erl_interface/src/connect/ei_connect_int.h b/lib/erl_interface/src/connect/ei_connect_int.h
index 0bcccaa84b..b41a5f2b23 100644
--- a/lib/erl_interface/src/connect/ei_connect_int.h
+++ b/lib/erl_interface/src/connect/ei_connect_int.h
@@ -102,6 +102,8 @@ extern int h_errno;
#define DFLAG_FUN_TAGS 16
#define DFLAG_NEW_FUN_TAGS 0x80
#define DFLAG_EXTENDED_PIDS_PORTS 0x100
+#define DFLAG_EXPORT_PTR_TAG 0x200
+#define DFLAG_BIT_BINARIES 0x400
#define DFLAG_NEW_FLOATS 0x800
#define DFLAG_SMALL_ATOM_TAGS 0x4000
#define DFLAG_UTF8_ATOMS 0x10000
diff --git a/lib/erl_interface/src/decode/decode_binary.c b/lib/erl_interface/src/decode/decode_binary.c
index 5b8d234984..2799438bef 100644
--- a/lib/erl_interface/src/decode/decode_binary.c
+++ b/lib/erl_interface/src/decode/decode_binary.c
@@ -40,4 +40,40 @@ int ei_decode_binary(const char *buf, int *index, void *p, long *lenp)
return 0;
}
+int ei_decode_bitstring(const char *buf, int *index, void *p, size_t plen,
+ size_t *bitsp)
+{
+ const char *s = buf + *index;
+ const char *s0 = s;
+ unsigned long len;
+ unsigned char last_bits;
+ const unsigned char tag = get8(s);
+
+ if (tag == ERL_BINARY_EXT) {
+ long bytes;
+ int ret = ei_decode_binary(buf, index, p, &bytes);
+ if (bitsp)
+ *bitsp = (size_t)bytes * 8;
+ return ret;
+ }
+
+ if (tag != ERL_BIT_BINARY_EXT)
+ return -1;
+
+ len = get32be(s);
+ last_bits = get8(s);
+
+ if (len > plen || ((last_bits==0) != (len==0)) || last_bits > 8)
+ return -1;
+
+ if (p)
+ memcpy(p, s, len);
+ s += len;
+
+ if (bitsp)
+ *bitsp = (len == 0) ? 0 : ((len-1) * 8) + last_bits;
+
+ *index += s-s0;
+ return 0;
+}
diff --git a/lib/erl_interface/src/decode/decode_fun.c b/lib/erl_interface/src/decode/decode_fun.c
index f944c028af..3a7a2b01c1 100644
--- a/lib/erl_interface/src/decode/decode_fun.c
+++ b/lib/erl_interface/src/decode/decode_fun.c
@@ -33,22 +33,20 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p)
int i, ix, ix0, n;
erlang_pid* p_pid;
char* p_module;
- erlang_char_encoding* p_module_org_enc;
long* p_index;
long* p_uniq;
long* p_old_index;
if (p != NULL) {
- p_pid = &p->pid;
+ p_pid = &p->u.closure.pid;
p_module = &p->module[0];
- p_module_org_enc = &p->module_org_enc;
- p_index = &p->index;
- p_uniq = &p->uniq;
- p_old_index = &p->old_index;
+ p_index = &p->u.closure.index;
+ p_uniq = &p->u.closure.uniq;
+ p_old_index = &p->u.closure.old_index;
}
else {
- p_pid = NULL; p_module = NULL; p_module_org_enc = NULL;
p_index = NULL; p_uniq = NULL; p_old_index = NULL;
+ p_pid = NULL; p_module = NULL;
}
switch (get8(s)) {
@@ -63,7 +61,7 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p)
return -1;
/* then the module (atom) */
if (ei_decode_atom_as(s, &ix, p_module, MAXATOMLEN_UTF8, ERLANG_UTF8,
- p_module_org_enc, NULL) < 0)
+ NULL, NULL) < 0)
return -1;
/* then the index */
if (ei_decode_long(s, &ix, p_index) < 0)
@@ -78,11 +76,11 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p)
return -1;
}
if (p != NULL) {
- p->n_free_vars = n;
- p->free_var_len = ix - ix0;
- p->free_vars = ei_malloc(ix - ix0);
- if (!(p->free_vars)) return -1;
- memcpy(p->free_vars, s + ix0, ix - ix0);
+ p->u.closure.n_free_vars = n;
+ p->u.closure.free_var_len = ix - ix0;
+ p->u.closure.free_vars = ei_malloc(ix - ix0);
+ if (!(p->u.closure.free_vars)) return -1;
+ memcpy(p->u.closure.free_vars, s + ix0, ix - ix0);
}
s += ix;
*index += s-s0;
@@ -93,20 +91,23 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p)
n = get32be(s);
/* then the arity */
i = get8(s);
- if (p != NULL) p->arity = i;
- /* then md5 */
- if (p != NULL) memcpy(p->md5, s, 16);
+ if (p != NULL) {
+ p->type = EI_FUN_CLOSURE;
+ p->arity = i;
+ /* then md5 */
+ memcpy(p->u.closure.md5, s, 16);
+ }
s += 16;
/* then index */
i = get32be(s);
- if (p != NULL) p->index = i;
+ if (p != NULL) p->u.closure.index = i;
/* then the number of free vars (environment) */
i = get32be(s);
- if (p != NULL) p->n_free_vars = i;
+ if (p != NULL) p->u.closure.n_free_vars = i;
/* then the module (atom) */
ix = 0;
if (ei_decode_atom_as(s, &ix, p_module, MAXATOMLEN_UTF8, ERLANG_UTF8,
- p_module_org_enc, NULL) < 0)
+ NULL, NULL) < 0)
return -1;
/* then the old_index */
if (ei_decode_long(s, &ix, p_old_index) < 0)
@@ -122,17 +123,56 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p)
n = n - (s - s0) + 1;
if (n < 0) return -1;
if (p != NULL) {
- p->free_var_len = n;
+ p->u.closure.free_var_len = n;
if (n > 0) {
- p->free_vars = malloc(n);
- if (!(p->free_vars)) return -1;
- memcpy(p->free_vars, s, n);
+ p->u.closure.free_vars = malloc(n);
+ if (!(p->u.closure.free_vars)) return -1;
+ memcpy(p->u.closure.free_vars, s, n);
}
}
s += n;
*index += s-s0;
return 0;
break;
+ case ERL_EXPORT_EXT: {
+ char* p_func;
+ long* p_arity;
+ int used;
+
+ if (p) {
+ p->type = EI_FUN_EXPORT;
+ p_arity = &p->arity;
+ }
+ else {
+ p_arity = NULL;
+ }
+ if (ei_decode_atom_as(s, &ix, p_module, MAXATOMLEN_UTF8, ERLANG_UTF8,
+ NULL, NULL) < 0)
+ return -1;
+ if (p) {
+ /* try use module buffer for function name */
+ used = strlen(p->module) + 1;
+ p_func = p->module + used;
+ p->u.exprt.func = p_func;
+ p->u.exprt.func_allocated = 0;
+ }
+ else {
+ used = 0;
+ p_func = NULL;
+ }
+ while (ei_decode_atom_as(s, &ix, p_func, MAXATOMLEN_UTF8-used,
+ ERLANG_UTF8, NULL, NULL) < 0) {
+ if (!used)
+ return -1;
+ p_func = malloc(MAXATOMLEN_UTF8);
+ p->u.exprt.func = p_func;
+ p->u.exprt.func_allocated = 1;
+ used = 0;
+ }
+ if (ei_decode_long(s, &ix, p_arity) < 0)
+ return -1;
+ return 0;
+ }
default:
return -1;
}
@@ -140,6 +180,14 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p)
void free_fun(erlang_fun* f)
{
- if (f->free_var_len > 0)
- ei_free(f->free_vars);
+ switch (f->type) {
+ case EI_FUN_CLOSURE:
+ if (f->u.closure.free_var_len > 0)
+ ei_free(f->u.closure.free_vars);
+ break;
+ case EI_FUN_EXPORT:
+ if (f->u.exprt.func_allocated)
+ ei_free(f->u.exprt.func);
+ break;
+ }
}
diff --git a/lib/erl_interface/src/decode/decode_skip.c b/lib/erl_interface/src/decode/decode_skip.c
index 0db315f09b..11d3bc1786 100644
--- a/lib/erl_interface/src/decode/decode_skip.c
+++ b/lib/erl_interface/src/decode/decode_skip.c
@@ -21,13 +21,21 @@
#include "eiext.h"
#include "decode_skip.h"
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+#ifndef SIZE_MAX
+# define SIZE_MAX (~((size_t)0))
+#endif
+
int ei_skip_term(const char* buf, int* index)
{
int i, n, ty;
/* ASSERT(ep != NULL); */
- ei_get_type_internal(buf, index, &ty, &n);
+ ei_get_type(buf, index, &ty, &n);
switch (ty) {
case ERL_ATOM_EXT:
/* FIXME: what if some weird locale is in use? */
@@ -54,7 +62,7 @@ int ei_skip_term(const char* buf, int* index)
if (ei_decode_list_header(buf, index, &n) < 0) return -1;
for (i = 0; i < n; ++i)
ei_skip_term(buf, index);
- if (ei_get_type_internal(buf, index, &ty, &n) < 0) return -1;
+ if (ei_get_type(buf, index, &ty, &n) < 0) return -1;
if (ty != ERL_NIL_EXT)
ei_skip_term(buf, index);
else
@@ -79,6 +87,10 @@ int ei_skip_term(const char* buf, int* index)
if (ei_decode_binary(buf, index, NULL, NULL) < 0)
return -1;
break;
+ case ERL_BIT_BINARY_EXT:
+ if (ei_decode_bitstring(buf, index, NULL, SIZE_MAX, NULL) < 0)
+ return -1;
+ break;
case ERL_SMALL_INTEGER_EXT:
case ERL_INTEGER_EXT:
if (ei_decode_long(buf, index, NULL) < 0) return -1;
diff --git a/lib/erl_interface/src/encode/encode_binary.c b/lib/erl_interface/src/encode/encode_binary.c
index 4471c51769..4aa9f6bc16 100644
--- a/lib/erl_interface/src/encode/encode_binary.c
+++ b/lib/erl_interface/src/encode/encode_binary.c
@@ -40,3 +40,27 @@ int ei_encode_binary(char *buf, int *index, const void *p, long len)
return 0;
}
+int ei_encode_bitstring(char *buf, int *index, const void *p, size_t bits)
+{
+ char *s = buf + *index;
+ char *s0 = s;
+ size_t bytes = (bits + 7) / 8;
+ char last_bits = bits % 8;
+
+ if (bytes == 0 || last_bits == 0)
+ return ei_encode_binary(buf, index, p, bytes);
+
+ if (!buf) s += 6;
+ else {
+ put8(s, ERL_BIT_BINARY_EXT);
+ put32be(s, bytes);
+ put8(s, last_bits);
+ memcpy(s, p, bytes);
+ s[bytes-1] &= (0xff << (8-last_bits));
+ }
+ s += bytes;
+
+ *index += s-s0;
+
+ return 0;
+}
diff --git a/lib/erl_interface/src/encode/encode_fun.c b/lib/erl_interface/src/encode/encode_fun.c
index 3bfc7530d1..e29424f9f4 100644
--- a/lib/erl_interface/src/encode/encode_fun.c
+++ b/lib/erl_interface/src/encode/encode_fun.c
@@ -26,56 +26,72 @@ int ei_encode_fun(char *buf, int *index, const erlang_fun *p)
{
int ix = *index;
- if (p->arity == -1) {
- /* ERL_FUN_EXT */
- if (buf != NULL) {
- char* s = buf + ix;
- put8(s, ERL_FUN_EXT);
- put32be(s, p->n_free_vars);
- }
- ix += sizeof(char) + 4;
- if (ei_encode_pid(buf, &ix, &p->pid) < 0)
- return -1;
- if (ei_encode_atom_as(buf, &ix, p->module, ERLANG_UTF8, p->module_org_enc) < 0)
- return -1;
- if (ei_encode_long(buf, &ix, p->index) < 0)
- return -1;
- if (ei_encode_long(buf, &ix, p->uniq) < 0)
- return -1;
- if (buf != NULL)
- memcpy(buf + ix, p->free_vars, p->free_var_len);
- ix += p->free_var_len;
- } else {
- char *size_p;
- /* ERL_NEW_FUN_EXT */
- if (buf != NULL) {
- char* s = buf + ix;
- put8(s, ERL_NEW_FUN_EXT);
- size_p = s;
- s += 4;
- put8(s, p->arity);
- memcpy(s, p->md5, sizeof(p->md5));
- s += sizeof(p->md5);
- put32be(s, p->index);
- put32be(s, p->n_free_vars);
- } else
- size_p = NULL;
- ix += 1 + 4 + 1 + sizeof(p->md5) + 4 + 4;
- if (ei_encode_atom_as(buf, &ix, p->module, ERLANG_UTF8, p->module_org_enc) < 0)
- return -1;
- if (ei_encode_long(buf, &ix, p->old_index) < 0)
- return -1;
- if (ei_encode_long(buf, &ix, p->uniq) < 0)
- return -1;
- if (ei_encode_pid(buf, &ix, &p->pid) < 0)
- return -1;
- if (buf != NULL)
- memcpy(buf + ix, p->free_vars, p->free_var_len);
- ix += p->free_var_len;
- if (size_p != NULL) {
- int sz = buf + ix - size_p;
- put32be(size_p, sz);
+ switch (p->type) {
+ case EI_FUN_CLOSURE:
+ if (p->arity == -1) {
+ /* ERL_FUN_EXT */
+ if (buf != NULL) {
+ char* s = buf + ix;
+ put8(s, ERL_FUN_EXT);
+ put32be(s, p->u.closure.n_free_vars);
+ }
+ ix += sizeof(char) + 4;
+ if (ei_encode_pid(buf, &ix, &p->u.closure.pid) < 0)
+ return -1;
+ if (ei_encode_atom_as(buf, &ix, p->module, ERLANG_UTF8, ERLANG_UTF8) < 0)
+ return -1;
+ if (ei_encode_long(buf, &ix, p->u.closure.index) < 0)
+ return -1;
+ if (ei_encode_long(buf, &ix, p->u.closure.uniq) < 0)
+ return -1;
+ if (buf != NULL)
+ memcpy(buf + ix, p->u.closure.free_vars, p->u.closure.free_var_len);
+ ix += p->u.closure.free_var_len;
+ } else {
+ char *size_p;
+ if (buf != NULL) {
+ char* s = buf + ix;
+ put8(s, ERL_NEW_FUN_EXT);
+ size_p = s;
+ s += 4;
+ put8(s, p->arity);
+ memcpy(s, p->u.closure.md5, sizeof(p->u.closure.md5));
+ s += sizeof(p->u.closure.md5);
+ put32be(s, p->u.closure.index);
+ put32be(s, p->u.closure.n_free_vars);
+ } else
+ size_p = NULL;
+ ix += 1 + 4 + 1 + sizeof(p->u.closure.md5) + 4 + 4;
+ if (ei_encode_atom_as(buf, &ix, p->module, ERLANG_UTF8, ERLANG_UTF8) < 0)
+ return -1;
+ if (ei_encode_long(buf, &ix, p->u.closure.old_index) < 0)
+ return -1;
+ if (ei_encode_long(buf, &ix, p->u.closure.uniq) < 0)
+ return -1;
+ if (ei_encode_pid(buf, &ix, &p->u.closure.pid) < 0)
+ return -1;
+ if (buf != NULL)
+ memcpy(buf + ix, p->u.closure.free_vars, p->u.closure.free_var_len);
+ ix += p->u.closure.free_var_len;
+ if (size_p != NULL) {
+ int sz = buf + ix - size_p;
+ put32be(size_p, sz);
+ }
}
+ break;
+ case EI_FUN_EXPORT:
+ if (buf != NULL) {
+ char* s = buf + ix;
+ put8(s, ERL_EXPORT_EXT);
+ }
+ ix++;
+ if (ei_encode_atom_as(buf, &ix, p->module, ERLANG_UTF8, ERLANG_UTF8) < 0)
+ return -1;
+ if (ei_encode_atom_as(buf, &ix, p->u.exprt.func, ERLANG_UTF8, ERLANG_UTF8) < 0)
+ return -1;
+ if (ei_encode_long(buf, &ix, p->arity) < 0)
+ return -1;
+ break;
}
*index = ix;
return 0;
diff --git a/lib/erl_interface/src/legacy/erl_eterm.c b/lib/erl_interface/src/legacy/erl_eterm.c
index 7ed2bdbc93..7ecea83b1a 100644
--- a/lib/erl_interface/src/legacy/erl_eterm.c
+++ b/lib/erl_interface/src/legacy/erl_eterm.c
@@ -299,12 +299,7 @@ void erl_mk_pid_helper(ETERM *ep, unsigned int number,
unsigned int serial, unsigned int creation)
{
ERL_PID_NUMBER(ep) = number & 0x7fff; /* 15 bits */
- if (ei_internal_use_r9_pids_ports()) {
- ERL_PID_SERIAL(ep) = serial & 0x07; /* 3 bits */
- }
- else {
- ERL_PID_SERIAL(ep) = serial & 0x1fff; /* 13 bits */
- }
+ ERL_PID_SERIAL(ep) = serial & 0x1fff; /* 13 bits */
ERL_PID_CREATION(ep) = creation; /* 32 bits */
}
@@ -334,12 +329,7 @@ ETERM *erl_mk_port(const char *node,
void erl_mk_port_helper(ETERM* ep, unsigned number, unsigned int creation)
{
- if (ei_internal_use_r9_pids_ports()) {
- ERL_PORT_NUMBER(ep) = number & 0x3ffff; /* 18 bits */
- }
- else {
- ERL_PORT_NUMBER(ep) = number & 0x0fffffff; /* 18 bits */
- }
+ ERL_PORT_NUMBER(ep) = number & 0x0fffffff; /* 18 bits */
ERL_PORT_CREATION(ep) = creation; /* 32 bits */
}
diff --git a/lib/erl_interface/src/misc/ei_compat.c b/lib/erl_interface/src/misc/ei_compat.c
index 93d7dbfb83..787895992e 100644
--- a/lib/erl_interface/src/misc/ei_compat.c
+++ b/lib/erl_interface/src/misc/ei_compat.c
@@ -22,19 +22,22 @@
#include "ei.h"
#include "ei_internal.h"
-#define EI_COMPAT_NO_REL (~((unsigned) 0))
+#include <limits.h>
-static unsigned compat_rel = EI_COMPAT_NO_REL;
+#ifndef EI_COMPAT
+# define EI_COMPAT UINT_MAX
+#endif
+
+static unsigned compat_rel = EI_COMPAT;
void
ei_set_compat_rel(unsigned rel)
{
- if (compat_rel == EI_COMPAT_NO_REL)
- compat_rel = rel;
+ compat_rel = rel;
}
-int
-ei_internal_use_r9_pids_ports(void)
+int ei_internal_use_21_bitstr_expfun(void)
{
- return compat_rel < 10;
+ return compat_rel < 22;
}
+
diff --git a/lib/erl_interface/src/misc/ei_decode_term.c b/lib/erl_interface/src/misc/ei_decode_term.c
index 63a7034508..8a4f7cc30d 100644
--- a/lib/erl_interface/src/misc/ei_decode_term.c
+++ b/lib/erl_interface/src/misc/ei_decode_term.c
@@ -87,6 +87,14 @@ int ei_decode_ei_term(const char* buf, int* index, ei_term* term)
case ERL_BINARY_EXT:
term->size = get32be(s);
return 0;
+ case ERL_BIT_BINARY_EXT: {
+ int bytes = get32be(s);
+ int last_bits = get8(s);
+ if (((last_bits==0) != (bytes==0)) || last_bits > 8)
+ return -1;
+ term->size = bytes;
+ return 0;
+ }
case ERL_SMALL_BIG_EXT:
if ((term->arity = get8(s)) != 4) return -1;
sign = get8(s);
diff --git a/lib/erl_interface/src/misc/ei_internal.h b/lib/erl_interface/src/misc/ei_internal.h
index f28dd6d668..ab12597c86 100644
--- a/lib/erl_interface/src/misc/ei_internal.h
+++ b/lib/erl_interface/src/misc/ei_internal.h
@@ -157,7 +157,7 @@ int ei_init_connect(void);
void ei_trace_printf(const char *name, int level, const char *format, ...);
-int ei_internal_use_r9_pids_ports(void);
+int ei_internal_use_21_bitstr_expfun(void);
int ei_get_cbs_ctx__(ei_socket_callbacks **cbs, void **ctx, int fd);
diff --git a/lib/erl_interface/src/misc/ei_printterm.c b/lib/erl_interface/src/misc/ei_printterm.c
index 058de00de5..a89b990ac1 100644
--- a/lib/erl_interface/src/misc/ei_printterm.c
+++ b/lib/erl_interface/src/misc/ei_printterm.c
@@ -131,7 +131,7 @@ static int print_term(FILE* fp, ei_x_buff* x,
if (fp == NULL && x == NULL) return -1;
doquote = 0;
- ei_get_type_internal(buf, index, &ty, &n);
+ ei_get_type(buf, index, &ty, &n);
switch (ty) {
case ERL_ATOM_EXT:
case ERL_ATOM_UTF8_EXT:
@@ -189,7 +189,7 @@ static int print_term(FILE* fp, ei_x_buff* x,
xputs(", ", fp, x); ch_written += 2;
}
}
- if (ei_get_type_internal(buf, &tindex, &ty, &n) < 0) goto err;
+ if (ei_get_type(buf, &tindex, &ty, &n) < 0) goto err;
if (ty != ERL_NIL_EXT) {
xputs(" | ", fp, x); ch_written += 3;
r = print_term(fp, x, buf, &tindex);
@@ -249,6 +249,34 @@ static int print_term(FILE* fp, ei_x_buff* x,
xputc('>', fp, x); ++ch_written;
ei_free(p);
break;
+ case ERL_BIT_BINARY_EXT: {
+ size_t bits;
+ int trunc = 0;
+ p = ei_malloc(n);
+ if (p == NULL) goto err;
+ if (ei_decode_bitstring(buf, index, p, n, &bits) < 0) {
+ ei_free(p);
+ goto err;
+ }
+ ch_written += xprintf(fp, x, "#Bits<");
+ m = (bits+7) / 8;
+ if (m > BINPRINTSIZE) {
+ m = BINPRINTSIZE;
+ trunc = 1;
+ }
+ --m;
+ for (i = 0; i < m; ++i) {
+ ch_written += xprintf(fp, x, "%d,", p[i]);
+ }
+ ch_written += xprintf(fp, x, "%d", p[i]);
+ if (trunc)
+ ch_written += xprintf(fp, x, ",...");
+ else if (bits % 8 != 0)
+ ch_written += xprintf(fp, x, ":%u", (unsigned)(bits % 8));
+ xputc('>', fp, x); ++ch_written;
+ ei_free(p);
+ break;
+ }
case ERL_SMALL_INTEGER_EXT:
case ERL_INTEGER_EXT:
if (ei_decode_long(buf, index, &l) < 0) goto err;
diff --git a/lib/erl_interface/src/misc/ei_x_encode.c b/lib/erl_interface/src/misc/ei_x_encode.c
index 4ff5974663..2da271795f 100644
--- a/lib/erl_interface/src/misc/ei_x_encode.c
+++ b/lib/erl_interface/src/misc/ei_x_encode.c
@@ -117,6 +117,16 @@ int ei_x_encode_binary(ei_x_buff* x, const void* p, int len)
return ei_encode_binary(x->buff, &x->index, p, len);
}
+int ei_x_encode_bitstring(ei_x_buff* x, const void* p, size_t bits)
+{
+ int i = x->index;
+ if (ei_encode_bitstring(NULL, &i, p, bits) == -1)
+ return -1;
+ if (!x_fix_buff(x, i))
+ return -1;
+ return ei_encode_bitstring(x->buff, &x->index, p, bits);
+}
+
int ei_x_encode_long(ei_x_buff* x, long n)
{
int i = x->index;
diff --git a/lib/erl_interface/src/misc/get_type.c b/lib/erl_interface/src/misc/get_type.c
index aa69cd4d60..eef58a9363 100644
--- a/lib/erl_interface/src/misc/get_type.c
+++ b/lib/erl_interface/src/misc/get_type.c
@@ -27,17 +27,8 @@
/* for types with meaningful length attributes, return the length too.
In other cases, return length 0 */
-/* FIXME working on this one.... */
-
int ei_get_type(const char *buf, const int *index, int *type, int *len)
{
- return ei_get_type_internal(buf, index, type, len);
-}
-
-
-int ei_get_type_internal(const char *buf, const int *index,
- int *type, int *len)
-{
const char *s = buf + *index;
*type = get8(s);
@@ -64,7 +55,9 @@ int ei_get_type_internal(const char *buf, const int *index,
case ERL_LARGE_TUPLE_EXT:
case ERL_LIST_EXT:
+ case ERL_MAP_EXT:
case ERL_BINARY_EXT:
+ case ERL_BIT_BINARY_EXT:
*len = get32be(s);
break;
diff --git a/lib/erl_interface/src/misc/show_msg.c b/lib/erl_interface/src/misc/show_msg.c
index 5868cccba6..2d49eb6449 100644
--- a/lib/erl_interface/src/misc/show_msg.c
+++ b/lib/erl_interface/src/misc/show_msg.c
@@ -24,6 +24,13 @@
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+#ifndef SIZE_MAX
+# define SIZE_MAX (~((size_t)0))
+#endif
#include <sys/types.h>
@@ -342,7 +349,7 @@ static void show_term(const char *termbuf, int *index, FILE *stream)
int i, len;
char *s;
- ei_get_type_internal(termbuf,index,&type,&len);
+ ei_get_type(termbuf,index,&type,&len);
switch (type) {
case ERL_VERSION_MAGIC:
@@ -455,6 +462,12 @@ static void show_term(const char *termbuf, int *index, FILE *stream)
fprintf(stream,"#Bin<%ld>",num);
break;
+ case ERL_BIT_BINARY_EXT: {
+ size_t bits;
+ ei_decode_bitstring(termbuf, index, NULL, SIZE_MAX, &bits);
+ fprintf(stream, "#Bits<%lu>", (unsigned long)bits);
+ break;
+ }
case ERL_LARGE_BIG_EXT:
/* doesn't actually decode - just skip over it */
/* FIXME if GMP, what to do here?? */
diff --git a/lib/erl_interface/src/prog/ei_fake_prog.c b/lib/erl_interface/src/prog/ei_fake_prog.c
index 158464b385..6f58c9833d 100644
--- a/lib/erl_interface/src/prog/ei_fake_prog.c
+++ b/lib/erl_interface/src/prog/ei_fake_prog.c
@@ -186,7 +186,6 @@ int main(void)
ei_x_encode_empty_list(&eix);
ei_get_type(charp, intp, intp, intp);
- ei_get_type_internal(charp, intp, intp, intp);
ei_decode_version(charp, intp, intp);
ei_decode_long(charp, intp, longp);
diff --git a/lib/erl_interface/src/registry/reg_dump.c b/lib/erl_interface/src/registry/reg_dump.c
index 43c9824433..da0413e6e6 100644
--- a/lib/erl_interface/src/registry/reg_dump.c
+++ b/lib/erl_interface/src/registry/reg_dump.c
@@ -90,7 +90,7 @@ static int mn_start_dump(int fd, const erlang_pid *self,
|| (arity != 2)
|| ei_decode_atom(buf,&index,tmpbuf)
|| strcmp(tmpbuf,"rex")
- || ei_get_type_internal(buf,&index,&type,&arity)
+ || ei_get_type(buf,&index,&type,&arity)
|| (type != ERL_PID_EXT))
return -1; /* bad response from other side */
diff --git a/lib/erl_interface/test/all_SUITE_data/ei_runner.h b/lib/erl_interface/test/all_SUITE_data/ei_runner.h
index 2608661303..7c874ac82e 100644
--- a/lib/erl_interface/test/all_SUITE_data/ei_runner.h
+++ b/lib/erl_interface/test/all_SUITE_data/ei_runner.h
@@ -53,6 +53,7 @@ void free_packet(char*);
#define fail(reason) do_fail(__FILE__, __LINE__, reason)
#define fail1(reason, a1) do_fail(__FILE__, __LINE__, reason, a1)
+#define fail2(reason, a1, a2) do_fail(__FILE__, __LINE__, reason, a1, a2)
#define report(ok) do_report(__FILE__, __LINE__, ok)
void do_report(char* file, int line, int ok);
diff --git a/lib/erl_interface/test/ei_accept_SUITE.erl b/lib/erl_interface/test/ei_accept_SUITE.erl
index 9c9c3f86b6..f40c67375b 100644
--- a/lib/erl_interface/test/ei_accept_SUITE.erl
+++ b/lib/erl_interface/test/ei_accept_SUITE.erl
@@ -43,8 +43,12 @@ init_per_testcase(Case, Config) ->
runner:init_per_testcase(?MODULE, Case, Config).
ei_accept(Config) when is_list(Config) ->
+ ei_accept_do(Config, 0), % default
+ ei_accept_do(Config, 21). % ei_set_compat_rel
+
+ei_accept_do(Config, CompatRel) ->
P = runner:start(Config, ?interpret),
- 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0),
+ 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, CompatRel),
Myname = hd(tl(string:tokens(atom_to_list(node()), "@"))),
io:format("Myname ~p ~n", [Myname]),
@@ -52,15 +56,18 @@ ei_accept(Config) when is_list(Config) ->
io:format("EINode ~p ~n", [EINode]),
%% We take this opportunity to also test export-funs and bit-strings
- %% with (ugly) tuple fallbacks.
+ %% with (ugly) tuple fallbacks in OTP 21 and older.
%% Test both toward pending connection and established connection.
RealTerms = [<<1:1>>, fun lists:map/2],
- Fallbacks = [{<<128>>,1}, {lists,map}],
+ EncTerms = case CompatRel of
+ 0 -> RealTerms;
+ 21 -> [{<<128>>,1}, {lists,map}]
+ end,
Self = self(),
Funny = fun() -> hello end,
TermToSend = {call, Self, "Test", Funny, RealTerms},
- TermToGet = {call, Self, "Test", Funny, Fallbacks},
+ TermToGet = {call, Self, "Test", Funny, EncTerms},
Port = 6543,
{ok, ListenFd} = ei_publish(P, Port),
{any, EINode} ! TermToSend,
@@ -94,7 +101,7 @@ ei_threaded_accept(Config) when is_list(Config) ->
%% Test erlang:monitor toward erl_interface "processes"
monitor_ei_process(Config) when is_list(Config) ->
P = runner:start(Config, ?interpret),
- 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0),
+ 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0, 0),
Myname = hd(tl(string:tokens(atom_to_list(node()), "@"))),
io:format("Myname ~p ~n", [Myname]),
@@ -167,8 +174,8 @@ start_einode(Einode, N, Host) ->
%%% Interface functions for ei (erl_interface) functions.
-ei_connect_init(P, Num, Cookie, Creation) ->
- send_command(P, ei_connect_init, [Num,Cookie,Creation]),
+ei_connect_init(P, Num, Cookie, Creation, Compat) ->
+ send_command(P, ei_connect_init, [Num,Cookie,Creation,Compat]),
case get_term(P) of
{term,Int} when is_integer(Int) -> Int
end.
diff --git a/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c b/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c
index c209f506b1..09b0b5440b 100644
--- a/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c
+++ b/lib/erl_interface/test/ei_accept_SUITE_data/ei_accept_test.c
@@ -58,7 +58,7 @@ static struct {
int num_args; /* Number of arguments. */
void (*func)(char* buf, int len);
} commands[] = {
- "ei_connect_init", 3, cmd_ei_connect_init,
+ "ei_connect_init", 4, cmd_ei_connect_init,
"ei_publish", 1, cmd_ei_publish,
"ei_accept", 1, cmd_ei_accept,
"ei_receive", 1, cmd_ei_receive,
@@ -106,21 +106,25 @@ TESTCASE(interpret)
static void cmd_ei_connect_init(char* buf, int len)
{
int index = 0, r = 0;
- int type, size;
- long l;
- char b[100];
+ long num, creation;
+ unsigned long compat;
+ char node_name[100];
char cookie[MAXATOMLEN], * cp = cookie;
ei_x_buff res;
- if (ei_decode_long(buf, &index, &l) < 0)
+ if (ei_decode_long(buf, &index, &num) < 0)
fail("expected int");
- sprintf(b, "c%d", l);
- /* FIXME don't use internal and maybe use skip?! */
- ei_get_type_internal(buf, &index, &type, &size);
+ sprintf(node_name, "c%d", num);
if (ei_decode_atom(buf, &index, cookie) < 0)
fail("expected atom (cookie)");
if (cookie[0] == '\0')
cp = NULL;
- r = ei_connect_init(&ec, b, cp, 0);
+ if (ei_decode_long(buf, &index, &creation) < 0)
+ fail("expected int");
+ if (ei_decode_long(buf, &index, &compat) < 0)
+ fail("expected uint");
+ if (compat)
+ ei_set_compat_rel(compat);
+ r = ei_connect_init(&ec, node_name, cp, creation);
ei_x_new_with_version(&res);
ei_x_encode_long(&res, r);
send_bin_term(&res);
diff --git a/lib/erl_interface/test/ei_connect_SUITE.erl b/lib/erl_interface/test/ei_connect_SUITE.erl
index 75b6bf18da..6184ce801b 100644
--- a/lib/erl_interface/test/ei_connect_SUITE.erl
+++ b/lib/erl_interface/test/ei_connect_SUITE.erl
@@ -79,9 +79,10 @@ ei_send_funs(Config) when is_list(Config) ->
{ok,Fd} = ei_connect(P, node()),
Fun1 = fun ei_send/1,
- Fun2 = fun(X) -> P, X, Fd, Fun1 end,
+ Fun2 = fun(X) -> {P, X, Fd, Fun1} end,
+ Bits = <<1,2,3:5>>,
- AMsg={Fun1,Fun2},
+ AMsg={Fun1,Fun2,Bits},
%%AMsg={wait_with_funs, new_dist_format},
ok = ei_send_funs(P, Fd, self(), AMsg),
EIMsg = receive M -> M end,
diff --git a/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c b/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c
index 58c0c7f8d8..7c9e79f837 100644
--- a/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c
+++ b/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c
@@ -107,7 +107,6 @@ TESTCASE(interpret)
static void cmd_ei_connect_init(char* buf, int len)
{
int index = 0, r = 0;
- int type, size;
long l;
char b[100];
char cookie[MAXATOMLEN], * cp = cookie;
@@ -115,8 +114,6 @@ static void cmd_ei_connect_init(char* buf, int len)
if (ei_decode_long(buf, &index, &l) < 0)
fail("expected int");
sprintf(b, "c%ld", l);
- /* FIXME don't use internal and maybe use skip?! */
- ei_get_type_internal(buf, &index, &type, &size);
if (ei_decode_atom(buf, &index, cookie) < 0)
fail("expected atom (cookie)");
if (cookie[0] == '\0')
@@ -212,6 +209,8 @@ static void cmd_ei_send_funs(char* buf, int len)
erlang_pid pid;
ei_x_buff x;
erlang_fun fun1, fun2;
+ unsigned char bitstring[10];
+ size_t bits;
if (ei_decode_long(buf, &index, &fd) < 0)
fail("expected long");
@@ -219,20 +218,24 @@ static void cmd_ei_send_funs(char* buf, int len)
fail("expected pid (node)");
if (ei_decode_tuple_header(buf, &index, &n) < 0)
fail("expected tuple");
- if (n != 2)
+ if (n != 3)
fail("expected tuple");
if (ei_decode_fun(buf, &index, &fun1) < 0)
fail("expected Fun1");
if (ei_decode_fun(buf, &index, &fun2) < 0)
fail("expected Fun2");
+ if (ei_decode_bitstring(buf, &index, bitstring, sizeof(bitstring), &bits) < 0)
+ fail("expected bitstring");
if (ei_x_new_with_version(&x) < 0)
fail("ei_x_new_with_version");
- if (ei_x_encode_tuple_header(&x, 2) < 0)
+ if (ei_x_encode_tuple_header(&x, 3) < 0)
fail("encode tuple header");
if (ei_x_encode_fun(&x, &fun1) < 0)
fail("encode fun1");
if (ei_x_encode_fun(&x, &fun2) < 0)
fail("encode fun2");
+ if (ei_x_encode_bitstring(&x, bitstring, bits) < 0)
+ fail("encode bitstring");
free_fun(&fun1);
free_fun(&fun2);
send_errno_result(ei_send(fd, &pid, x.buff, x.index));
diff --git a/lib/erl_interface/test/ei_decode_SUITE.erl b/lib/erl_interface/test/ei_decode_SUITE.erl
index 75560ea7c9..e005ec89c7 100644
--- a/lib/erl_interface/test/ei_decode_SUITE.erl
+++ b/lib/erl_interface/test/ei_decode_SUITE.erl
@@ -194,6 +194,9 @@ test_ei_decode_misc(Config) when is_list(Config) ->
send_term_as_binary(P,<<>>),
send_term_as_binary(P,<<"ÅÄÖåäö">>),
+ send_term_as_binary(P,<<1, 2, 3:5>>),
+ send_term_as_binary(P,<<1:1>>),
+
% send_term_as_binary(P,{}),
% send_term_as_binary(P,[]),
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 e516f310b6..d39970a857 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
@@ -256,66 +256,129 @@ int ei_decode_my_string(const char *buf, int *index, char *to,
//#define EI_DECODE_UTF8_STRING(FUNC,SIZE,VAL)
-#define EI_DECODE_BIN(FUNC,SIZE,VAL,LEN) \
- { \
- char p[1024]; \
- char *buf; \
- long len; \
- int size1 = 0; \
- int size2 = 0; \
- int err; \
- message("ei_" #FUNC " should be " #VAL); \
- buf = read_packet(NULL); \
- err = ei_ ## FUNC(buf+1, &size1, NULL, &len); \
+static void decode_bin(int exp_size, const char* val, int exp_len)
+{
+ char p[1024];
+ char *buf;
+ long len;
+ int size1 = 0;
+ int size2 = 0;
+ int err;
+ message("ei_decode_binary should be %s", val);
+ buf = read_packet(NULL);
+ err = ei_decode_binary(buf+1, &size1, NULL, &len);
message("err = %d, size = %d, len = %d, expected size = %d, expected len = %d\n",\
- err,size1,len,SIZE,LEN); \
- if (err != 0) { \
- if (err != -1) { \
- fail("returned non zero but not -1 if NULL pointer"); \
- } else { \
- fail("returned non zero"); \
- } \
- return; \
- } \
-\
- if (len != LEN) { \
- fail("size is not correct"); \
- return; \
- } \
-\
- err = ei_ ## FUNC(buf+1, &size2, p, &len); \
+ err,size1,len, exp_size, exp_len);
+ if (err != 0) {
+ if (err != -1) {
+ fail("returned non zero but not -1 if NULL pointer");
+ } else {
+ fail("returned non zero");
+ }
+ return;
+ }
+
+ if (len != exp_len) {
+ fail("size is not correct");
+ return;
+ }
+
+ err = ei_decode_binary(buf+1, &size2, p, &len);
message("err = %d, size = %d, len = %d, expected size = %d, expected len = %d\n",\
- err,size2,len,SIZE,LEN); \
- if (err != 0) { \
- if (err != -1) { \
- fail("returned non zero but not -1 if NULL pointer"); \
- } else { \
- fail("returned non zero"); \
- } \
- return; \
- } \
-\
- if (len != LEN) { \
- fail("size is not correct"); \
- return; \
- } \
-\
- if (strncmp(p,VAL,LEN) != 0) { \
- fail("value is not correct"); \
- return; \
- } \
-\
- if (size1 != size2) { \
- fail("size with and without pointer differs"); \
- return; \
- } \
-\
- if (size1 != SIZE) { \
- fail("size of encoded data is incorrect"); \
- return; \
- } \
- free_packet(buf); \
- } \
+ err,size2,len, exp_size, exp_len);
+ if (err != 0) {
+ if (err != -1) {
+ fail("returned non zero but not -1 if NULL pointer");
+ } else {
+ fail("returned non zero");
+ }
+ return;
+ }
+
+ if (len != exp_len) {
+ fail("size is not correct");
+ return;
+ }
+
+ if (strncmp(p,val,exp_len) != 0) {
+ fail("value is not correct");
+ return;
+ }
+
+ if (size1 != size2) {
+ fail("size with and without pointer differs");
+ return;
+ }
+
+ if (size1 != exp_size) {
+ fail("size of encoded data is incorrect");
+ return;
+ }
+ free_packet(buf);
+}
+
+static void decode_bits(int exp_size, const char* val, size_t exp_bits)
+{
+ char p[1024];
+ char *buf;
+ size_t bits;
+ int size1 = 0;
+ int size2 = 0;
+ int err;
+ message("ei_decode_bitstring should be %d bits", (int)exp_bits);
+ buf = read_packet(NULL);
+ err = ei_decode_bitstring(buf+1, &size1, NULL, sizeof(p), &bits);
+ message("err = %d, size = %d, len = %d, expected size = %d, expected bits = %d\n",\
+ err,size1, (int)bits, exp_size, (int)exp_bits);
+
+ if (err != 0) {
+ if (err != -1) {
+ fail("returned non zero but not -1 if NULL pointer");
+ } else {
+ fail("returned non zero");
+ }
+ return;
+ }
+
+ if (bits != exp_bits) {
+ fail("number of bits is not correct");
+ return;
+ }
+
+ err = ei_decode_bitstring(buf+1, &size2, p, sizeof(p), &bits);
+ message("err = %d, size = %d, len = %d, expected size = %d, expected len = %d\n",\
+ err,size2, (int)bits, exp_size, (int)exp_bits);
+ if (err != 0) {
+ if (err != -1) {
+ fail("returned non zero but not -1 if NULL pointer");
+ } else {
+ fail("returned non zero");
+ }
+ return;
+ }
+
+ if (bits != exp_bits) {
+ fail("bits is not correct");
+ return;
+ }
+
+ if (memcmp(p, val, (exp_bits+7)/8) != 0) {
+ fail("value is not correct");
+ return;
+ }
+
+ if (size1 != size2) {
+ fail("size with and without pointer differs");
+ return;
+ }
+
+ if (size1 != exp_size) {
+ fail2("size of encoded data is incorrect %d != %d", size1, exp_size);
+ return;
+ }
+ free_packet(buf);
+}
+
/* ******************************************************************** */
@@ -644,9 +707,17 @@ TESTCASE(test_ei_decode_misc)
EI_DECODE_STRING(decode_my_string, 1, "");
EI_DECODE_STRING(decode_my_string, 9, "������");
- EI_DECODE_BIN(decode_binary, 8, "foo", 3);
- EI_DECODE_BIN(decode_binary, 5, "", 0);
- EI_DECODE_BIN(decode_binary, 11, "������", 6);
+ decode_bin(8, "foo", 3);
+ decode_bin(5, "", 0);
+ decode_bin(11, "������", 6);
+
+#define LAST_BYTE(V, BITS) ((V) << (8-(BITS)))
+ {
+ unsigned char bits1[] = {1, 2, LAST_BYTE(3,5) };
+ unsigned char bits2[] = {LAST_BYTE(1,1) };
+ decode_bits(9, bits1, 21);
+ decode_bits(7, bits2, 1);
+ }
/* FIXME check \0 in strings and atoms? */
/*
diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE.erl b/lib/erl_interface/test/ei_decode_encode_SUITE.erl
index 0f23cdfbb9..d8b0bce3ae 100644
--- a/lib/erl_interface/test/ei_decode_encode_SUITE.erl
+++ b/lib/erl_interface/test/ei_decode_encode_SUITE.erl
@@ -120,6 +120,8 @@ test_ei_decode_encode(Config) when is_list(Config) ->
send_rec(P, #{key => value}),
send_rec(P, maps:put(Port, Ref, #{key => value, key2 => Pid})),
+ [send_rec(P, <<16#dec0deb175:B/little>>) || B <- lists:seq(0,48)],
+
runner:recv_eot(P),
ok.
diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
index 55d9ed1b1a..f9c05b2739 100644
--- a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
+++ b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
@@ -40,6 +40,12 @@ typedef struct
erlang_char_encoding enc;
}my_atom;
+typedef struct
+{
+ char bytes[MAXATOMLEN_UTF8];
+ size_t nbits;
+}my_bitstring;
+
struct my_obj {
union {
erlang_fun fun;
@@ -49,6 +55,7 @@ struct my_obj {
erlang_trace trace;
erlang_big big;
my_atom atom;
+ my_bitstring bits;
int arity;
}u;
@@ -119,6 +126,26 @@ struct Type my_atom_type = {
(encodeFT*)ei_encode_my_atom, (x_encodeFT*)ei_x_encode_my_atom
};
+int ei_decode_my_bits(const char *buf, int *index, my_bitstring* a)
+{
+ return ei_decode_bitstring(buf, index, (a ? a->bytes : NULL),
+ sizeof(a->bytes),
+ (a ? &a->nbits : NULL));
+}
+int ei_encode_my_bits(char *buf, int *index, my_bitstring* a)
+{
+ return ei_encode_bitstring(buf, index, a->bytes, a->nbits);
+}
+int ei_x_encode_my_bits(ei_x_buff* x, my_bitstring* a)
+{
+ return ei_x_encode_bitstring(x, a->bytes, a->nbits);
+}
+
+struct Type my_bitstring_type = {
+ "bits", "my_bitstring", (decodeFT*)ei_decode_my_bits,
+ (encodeFT*)ei_encode_my_bits, (x_encodeFT*)ei_x_encode_my_bits
+};
+
int my_decode_tuple_header(const char *buf, int *index, struct my_obj* obj)
{
@@ -537,6 +564,10 @@ TESTCASE(test_ei_decode_encode)
decode_encode(map, 7);
}
+ for (i=0; i <= 48; i++) {
+ decode_encode_one(&my_bitstring_type);
+ }
+
report(1);
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
index 222330654a..c3f71a84f0 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
@@ -89,7 +89,7 @@ public class AbstractNode implements OtpTransportFactory {
static final int dFlagHiddenAtomCache = 0x40; // NOT SUPPORTED
static final int dflagNewFunTags = 0x80;
static final int dFlagExtendedPidsPorts = 0x100;
- static final int dFlagExportPtrTag = 0x200; // NOT SUPPORTED
+ static final int dFlagExportPtrTag = 0x200;
static final int dFlagBitBinaries = 0x400;
static final int dFlagNewFloats = 0x800;
static final int dFlagUnicodeIo = 0x1000;
@@ -105,6 +105,7 @@ public class AbstractNode implements OtpTransportFactory {
int flags = dFlagExtendedReferences | dFlagExtendedPidsPorts
| dFlagBitBinaries | dFlagNewFloats | dFlagFunTags
| dflagNewFunTags | dFlagUtf8Atoms | dFlagMapTag
+ | dFlagExportPtrTag
| dFlagBigCreation;
/* initialize hostname and default cookie */
diff --git a/lib/jinterface/test/nc_SUITE.erl b/lib/jinterface/test/nc_SUITE.erl
index 4f225a396e..7833d070b7 100644
--- a/lib/jinterface/test/nc_SUITE.erl
+++ b/lib/jinterface/test/nc_SUITE.erl
@@ -142,7 +142,8 @@ fun_roundtrip(Config) when is_list(Config)->
do_echo([fun(A, B) -> A + B end,
fun(A) -> lists:reverse(A) end,
fun() -> ok end,
- fun fun_roundtrip/1],
+ fun fun_roundtrip/1,
+ fun ?MODULE:fun_roundtrip/1],
Config).
port_roundtrip(doc) -> [];
diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml
index fc16473393..f8b41d24e2 100644
--- a/lib/kernel/doc/src/gen_tcp.xml
+++ b/lib/kernel/doc/src/gen_tcp.xml
@@ -259,6 +259,12 @@ do_recv(Sock, Bs) ->
<p>The optional <c><anno>Timeout</anno></c> parameter specifies a
time-out in milliseconds. Defaults to <c>infinity</c>.</p>
<note>
+ <p>Keep in mind that if the underlying OS <c>connect()</c> call returns
+ a timeout, <c>gen_tcp:connect</c> will also return a timeout
+ (i.e. <c>{error, etimedout}</c>), even if a larger <c>Timeout</c> was
+ specified.</p>
+ </note>
+ <note>
<p>The default values for options specified to <c>connect</c> can
be affected by the Kernel configuration parameter
<c>inet_default_connect_options</c>. For details, see
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index 709ba8e8fd..d4678ca5db 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -294,6 +294,9 @@ fe80::204:acff:fe17:bf38
<datatype>
<name name="socket_protocol"/>
</datatype>
+ <datatype>
+ <name name="stat_option"/>
+ </datatype>
</datatypes>
<funcs>
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index 8db5620686..12bb0b21b0 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -286,7 +286,9 @@
entries as ASN.1 DER encoded entities.</fsummary>
<desc>
<p>Decodes PEM binary data and returns entries as ASN.1 DER encoded entities.</p>
- <p>Example <c>{ok, PemBin} = file:read_file("cert.pem").</c></p>
+ <p>Example <c>{ok, PemBin} = file:read_file("cert.pem").
+ PemEntries = public_key:pem_decode(PemBin).
+ </c></p>
</desc>
</func>
diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl
index 61a1239d26..12c61e158f 100644
--- a/lib/public_key/src/pubkey_cert.erl
+++ b/lib/public_key/src/pubkey_cert.erl
@@ -1187,6 +1187,8 @@ sign_algorithm(#'ECPrivateKey'{parameters = Parms}, Opts) ->
parameters = Parms}.
rsa_digest_oid(sha1) ->
?'sha1WithRSAEncryption';
+rsa_digest_oid(sha) ->
+ ?'sha1WithRSAEncryption';
rsa_digest_oid(sha512) ->
?'sha512WithRSAEncryption';
rsa_digest_oid(sha384) ->
@@ -1198,6 +1200,8 @@ rsa_digest_oid(md5) ->
ecdsa_digest_oid(sha1) ->
?'ecdsa-with-SHA1';
+ecdsa_digest_oid(sha) ->
+ ?'ecdsa-with-SHA1';
ecdsa_digest_oid(sha512) ->
?'ecdsa-with-SHA512';
ecdsa_digest_oid(sha384) ->
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 431c77141c..47266c514c 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -112,6 +112,7 @@
-type ssh_file() :: openssh_public_key | rfc4716_public_key | known_hosts |
auth_keys.
-type digest_type() :: none % None is for backwards compatibility
+ | sha1 % Backwards compatibility
| crypto:rsa_digest_type()
| crypto:dss_digest_type()
| crypto:ecdsa_digest_type().
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index bad5815f40..78990c48f2 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,28 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.7.6</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ When an SSH server receives the very first message on a
+ new TCP connection, and that message is not the expected
+ one, the 64 first bytes of the received message are now
+ dumped in the INFO REPORT that reports the Protocol
+ Error.</p>
+ <p>
+ This facilitates the debugging of who sends the bad
+ message or of detecting a possible port scanning.</p>
+ <p>
+ Own Id: OTP-15772</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.7.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index 923e9309f4..04453e6ef0 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -396,11 +396,13 @@
recv_mac_size = 0,
encrypt = none, %% encrypt algorithm
+ encrypt_cipher, %% cipher. could be different from the algorithm
encrypt_keys, %% encrypt keys
encrypt_block_size = 8,
encrypt_ctx,
decrypt = none, %% decrypt algorithm
+ decrypt_cipher, %% cipher. could be different from the algorithm
decrypt_keys, %% decrypt keys
decrypt_block_size = 8,
decrypt_ctx, %% Decryption context
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 7c87591cf2..8f32966a12 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -594,7 +594,7 @@ handle_event(_, socket_control, {hello,_}=StateName, D) ->
{stop, {shutdown,{unexpected_getopts_return, Other}}}
end;
-handle_event(_, {info_line,_Line}, {hello,Role}=StateName, D) ->
+handle_event(_, {info_line,Line}, {hello,Role}=StateName, D) ->
case Role of
client ->
%% The server may send info lines to the client before the version_exchange
@@ -605,9 +605,9 @@ handle_event(_, {info_line,_Line}, {hello,Role}=StateName, D) ->
%% But the client may NOT send them to the server. Openssh answers with cleartext,
%% and so do we
send_bytes("Protocol mismatch.", D),
- ?call_disconnectfun_and_log_cond("Protocol mismatch.",
- "Protocol mismatch in version exchange. Client sent info lines.",
- StateName, D),
+ Msg = io_lib:format("Protocol mismatch in version exchange. Client sent info lines.~n~s",
+ [ssh_dbg:hex_dump(Line, 64)]),
+ ?call_disconnectfun_and_log_cond("Protocol mismatch.", Msg, StateName, D),
{stop, {shutdown,"Protocol mismatch in version exchange. Client sent info lines."}}
end;
diff --git a/lib/ssh/src/ssh_dbg.erl b/lib/ssh/src/ssh_dbg.erl
index 4fe15b24d3..43ac4c0ccf 100644
--- a/lib/ssh/src/ssh_dbg.erl
+++ b/lib/ssh/src/ssh_dbg.erl
@@ -60,6 +60,7 @@
cbuf_stop_clear/0,
cbuf_in/1,
cbuf_list/0,
+ hex_dump/1, hex_dump/2,
fmt_cbuf_items/0, fmt_cbuf_item/1
]).
@@ -439,3 +440,75 @@ fmt_value(#circ_buf_entry{module = M,
io_lib:format("~p:~p ~p/~p ~p~n~s",[M,L,F,A,Pid,fmt_value(V)]);
fmt_value(Value) ->
io_lib:format("~p",[Value]).
+
+%%%================================================================
+
+-record(h, {max_bytes = 65536,
+ bytes_per_line = 16,
+ address_len = 4
+ }).
+
+
+hex_dump(Data) -> hex_dump1(Data, hd_opts([])).
+
+hex_dump(X, Max) when is_integer(Max) ->
+ hex_dump(X, [{max_bytes,Max}]);
+hex_dump(X, OptList) when is_list(OptList) ->
+ hex_dump1(X, hd_opts(OptList)).
+
+hex_dump1(B, Opts) when is_binary(B) -> hex_dump1(binary_to_list(B), Opts);
+hex_dump1(L, Opts) when is_list(L), length(L) > Opts#h.max_bytes ->
+ io_lib:format("~s---- skip ~w bytes----~n", [hex_dump1(lists:sublist(L,Opts#h.max_bytes), Opts),
+ length(L) - Opts#h.max_bytes
+ ]);
+hex_dump1(L, Opts0) when is_list(L) ->
+ Opts = Opts0#h{address_len = num_hex_digits(Opts0#h.max_bytes)},
+ Result = hex_dump(L, [{0,[],[]}], Opts),
+ [io_lib:format("~*.s | ~*s | ~s~n"
+ "~*.c-+-~*c-+-~*c~n",
+ [Opts#h.address_len, lists:sublist("Address",Opts#h.address_len),
+ -3*Opts#h.bytes_per_line, lists:sublist("Hexdump",3*Opts#h.bytes_per_line),
+ "ASCII",
+ Opts#h.address_len, $-,
+ 3*Opts#h.bytes_per_line, $-,
+ Opts#h.bytes_per_line, $-
+ ]) |
+ [io_lib:format("~*.16.0b | ~s~*c | ~s~n",[Opts#h.address_len, N*Opts#h.bytes_per_line,
+ lists:reverse(Hexs),
+ 3*(Opts#h.bytes_per_line-length(Hexs)), $ ,
+ lists:reverse(Chars)])
+ || {N,Hexs,Chars} <- lists:reverse(Result)
+ ]
+ ].
+
+
+hd_opts(L) -> lists:foldl(fun hd_opt/2, #h{}, L).
+
+hd_opt({max_bytes,M}, O) -> O#h{max_bytes=M};
+hd_opt({bytes_per_line,M}, O) -> O#h{bytes_per_line=M}.
+
+
+num_hex_digits(N) when N<16 -> 1;
+num_hex_digits(N) -> trunc(math:ceil(math:log2(N)/4)).
+
+
+hex_dump([L|Cs], Result0, Opts) when is_list(L) ->
+ Result = hex_dump(L,Result0, Opts),
+ hex_dump(Cs, Result, Opts);
+
+hex_dump(Cs, [{N0,_,Chars}|_]=Lines, Opts) when length(Chars) == Opts#h.bytes_per_line ->
+ hex_dump(Cs, [{N0+1,[],[]}|Lines], Opts);
+
+hex_dump([C|Cs], [{N,Hexs,Chars}|Lines], Opts) ->
+ Asc = if
+ 16#20 =< C,C =< 16#7E -> C;
+ true -> $.
+ end,
+ Hex = io_lib:format("~2.16.0b ", [C]),
+ hex_dump(Cs, [{N, [Hex|Hexs], [Asc|Chars]} | Lines], Opts);
+
+hex_dump([], Result, _) ->
+ Result.
+
+
+
diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl
index aa9ba0f9bb..5ec12e2d04 100644
--- a/lib/ssh/src/ssh_sftpd.erl
+++ b/lib/ssh/src/ssh_sftpd.erl
@@ -508,7 +508,7 @@ close_our_file({_,Fd}, FileMod, FS0) ->
FS1.
%%% stat: do the stat
-stat(Vsn, ReqId, Data, State, F) ->
+stat(_Vsn, ReqId, Data, State, F) ->
<<?UINT32(BLen), BPath:BLen/binary, _/binary>> = Data,
stat(ReqId, unicode:characters_to_list(BPath), State, F).
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 2299346a30..eaab13433a 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -1328,13 +1328,15 @@ verify(PlainText, HashAlg, Sig, Key, _) ->
%%% Start of a more parameterized crypto handling.
cipher('AEAD_AES_128_GCM') ->
- #cipher{key_bytes = 16,
+ #cipher{impl = aes_128_gcm,
+ key_bytes = 16,
iv_bytes = 12,
block_bytes = 16,
pkt_type = aead};
cipher('AEAD_AES_256_GCM') ->
- #cipher{key_bytes = 32,
+ #cipher{impl = aes_256_gcm,
+ key_bytes = 32,
iv_bytes = 12,
block_bytes = 16,
pkt_type = aead};
@@ -1346,7 +1348,7 @@ cipher('3des-cbc') ->
block_bytes = 8};
cipher('aes128-cbc') ->
- #cipher{impl = aes_cbc,
+ #cipher{impl = aes_128_cbc,
key_bytes = 16,
iv_bytes = 16,
block_bytes = 16};
@@ -1370,7 +1372,8 @@ cipher('aes256-ctr') ->
block_bytes = 16};
cipher('[email protected]') -> % FIXME: Verify!!
- #cipher{key_bytes = 32,
+ #cipher{impl = chacha20_poly1305,
+ key_bytes = 32,
iv_bytes = 12,
block_bytes = 8,
pkt_type = aead};
@@ -1407,12 +1410,14 @@ encrypt_init(#ssh{encrypt = '[email protected]', role = Role} = Ssh)
encrypt_init(#ssh{encrypt = SshCipher, role = Role} = Ssh) when SshCipher == 'AEAD_AES_128_GCM';
SshCipher == 'AEAD_AES_256_GCM' ->
{IvMagic, KeyMagic} = encrypt_magic(Role),
- #cipher{key_bytes = KeyBytes,
+ #cipher{impl = CryptoCipher,
+ key_bytes = KeyBytes,
iv_bytes = IvBytes,
block_bytes = BlockBytes} = cipher(SshCipher),
IV = hash(Ssh, IvMagic, 8*IvBytes),
K = hash(Ssh, KeyMagic, 8*KeyBytes),
- {ok, Ssh#ssh{encrypt_keys = K,
+ {ok, Ssh#ssh{encrypt_cipher = CryptoCipher,
+ encrypt_keys = K,
encrypt_block_size = BlockBytes,
encrypt_ctx = IV}};
@@ -1425,11 +1430,12 @@ encrypt_init(#ssh{encrypt = SshCipher, role = Role} = Ssh) ->
IV = hash(Ssh, IvMagic, 8*IvBytes),
K = hash(Ssh, KeyMagic, 8*KeyBytes),
Ctx0 = crypto:crypto_init(CryptoCipher, K, IV, true),
- {ok, Ssh#ssh{encrypt_block_size = BlockBytes,
+ {ok, Ssh#ssh{encrypt_cipher = CryptoCipher,
+ encrypt_block_size = BlockBytes,
encrypt_ctx = Ctx0}}.
encrypt_final(Ssh) ->
- {ok, Ssh#ssh{encrypt = none,
+ {ok, Ssh#ssh{encrypt = none,
encrypt_keys = undefined,
encrypt_block_size = 8,
encrypt_ctx = undefined
@@ -1457,18 +1463,19 @@ encrypt(#ssh{encrypt = '[email protected]',
{Ssh, {EncBytes,Ctag}};
encrypt(#ssh{encrypt = SshCipher,
+ encrypt_cipher = CryptoCipher,
encrypt_keys = K,
encrypt_ctx = IV0} = Ssh,
<<LenData:4/binary, PayloadData/binary>>) when SshCipher == 'AEAD_AES_128_GCM' ;
SshCipher == 'AEAD_AES_256_GCM' ->
- {Ctext,Ctag} = crypto:block_encrypt(aes_gcm, K, IV0, {LenData,PayloadData}),
+ {Ctext,Ctag} = crypto:crypto_one_time_aead(CryptoCipher, K, IV0, PayloadData, LenData, true),
IV = next_gcm_iv(IV0),
{Ssh#ssh{encrypt_ctx = IV}, {<<LenData/binary,Ctext/binary>>,Ctag}};
encrypt(#ssh{encrypt_ctx = Ctx0} = Ssh, Data) ->
Enc = crypto:crypto_update(Ctx0, Data),
{Ssh, Enc}.
-
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Decryption
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1485,12 +1492,14 @@ decrypt_init(#ssh{decrypt = '[email protected]', role = Role} = Ssh)
decrypt_init(#ssh{decrypt = SshCipher, role = Role} = Ssh) when SshCipher == 'AEAD_AES_128_GCM';
SshCipher == 'AEAD_AES_256_GCM' ->
{IvMagic, KeyMagic} = decrypt_magic(Role),
- #cipher{key_bytes = KeyBytes,
+ #cipher{impl = CryptoCipher,
+ key_bytes = KeyBytes,
iv_bytes = IvBytes,
block_bytes = BlockBytes} = cipher(SshCipher),
IV = hash(Ssh, IvMagic, 8*IvBytes),
K = hash(Ssh, KeyMagic, 8*KeyBytes),
- {ok, Ssh#ssh{decrypt_keys = K,
+ {ok, Ssh#ssh{decrypt_cipher = CryptoCipher,
+ decrypt_keys = K,
decrypt_block_size = BlockBytes,
decrypt_ctx = IV}};
@@ -1503,9 +1512,11 @@ decrypt_init(#ssh{decrypt = SshCipher, role = Role} = Ssh) ->
IV = hash(Ssh, IvMagic, 8*IvBytes),
K = hash(Ssh, KeyMagic, 8*KeyBytes),
Ctx0 = crypto:crypto_init(CryptoCipher, K, IV, false),
- {ok, Ssh#ssh{decrypt_block_size = BlockBytes,
+ {ok, Ssh#ssh{decrypt_cipher = CryptoCipher,
+ decrypt_block_size = BlockBytes,
decrypt_ctx = Ctx0}}.
+
decrypt_final(Ssh) ->
{ok, Ssh#ssh {decrypt = none,
decrypt_keys = undefined,
@@ -1517,35 +1528,37 @@ decrypt(Ssh, <<>>) ->
{Ssh, <<>>};
decrypt(#ssh{decrypt = '[email protected]',
- decrypt_keys = {K1,_K2},
- recv_sequence = Seq} = Ssh, {length,EncryptedLen}) ->
- PacketLenBin = crypto:crypto_one_time(chacha20, K1, <<0:8/unit:8, Seq:8/unit:8>>, EncryptedLen, false),
- {Ssh, PacketLenBin};
-
-decrypt(#ssh{decrypt = '[email protected]',
- decrypt_keys = {_K1,K2},
- recv_sequence = Seq} = Ssh, {AAD,Ctext,Ctag}) ->
- %% The length is already decoded and used to divide the input
- %% Check the mac (important that it is timing-safe):
- PolyKey = crypto:crypto_one_time(chacha20, K2, <<0:8/unit:8,Seq:8/unit:8>>, <<0:32/unit:8>>, false),
- case equal_const_time(Ctag, crypto:poly1305(PolyKey, <<AAD/binary,Ctext/binary>>)) of
- true ->
- %% MAC is ok, decode
- IV2 = <<1:8/little-unit:8, Seq:8/unit:8>>,
- PlainText = crypto:crypto_one_time(chacha20, K2, IV2, Ctext, false),
- {Ssh, PlainText};
- false ->
- {Ssh,error}
+ decrypt_keys = {K1,K2},
+ recv_sequence = Seq} = Ssh, Data) ->
+ case Data of
+ {length,EncryptedLen} ->
+ %% The length is decrypted separately in a first step
+ PacketLenBin = crypto:crypto_one_time(chacha20, K1, <<0:8/unit:8, Seq:8/unit:8>>, EncryptedLen, false),
+ {Ssh, PacketLenBin};
+ {AAD,Ctext,Ctag} ->
+ %% The length is already decrypted and used to divide the input
+ %% Check the mac (important that it is timing-safe):
+ PolyKey = crypto:crypto_one_time(chacha20, K2, <<0:8/unit:8,Seq:8/unit:8>>, <<0:32/unit:8>>, false),
+ case equal_const_time(Ctag, crypto:poly1305(PolyKey, <<AAD/binary,Ctext/binary>>)) of
+ true ->
+ %% MAC is ok, decode
+ IV2 = <<1:8/little-unit:8, Seq:8/unit:8>>,
+ PlainText = crypto:crypto_one_time(chacha20, K2, IV2, Ctext, false),
+ {Ssh, PlainText};
+ false ->
+ {Ssh,error}
+ end
end;
decrypt(#ssh{decrypt = none} = Ssh, Data) ->
{Ssh, Data};
decrypt(#ssh{decrypt = SshCipher,
+ decrypt_cipher = CryptoCipher,
decrypt_keys = K,
- decrypt_ctx = IV0} = Ssh, Data = {_AAD,_Ctext,_Ctag}) when SshCipher == 'AEAD_AES_128_GCM' ;
- SshCipher == 'AEAD_AES_256_GCM' ->
- Dec = crypto:block_decrypt(aes_gcm, K, IV0, Data), % Dec = PlainText | error
+ decrypt_ctx = IV0} = Ssh, {AAD,Ctext,Ctag}) when SshCipher == 'AEAD_AES_128_GCM' ;
+ SshCipher == 'AEAD_AES_256_GCM' ->
+ Dec = crypto:crypto_one_time_aead(CryptoCipher, K, IV0, Ctext, AAD, Ctag, false),
IV = next_gcm_iv(IV0),
{Ssh#ssh{decrypt_ctx = IV}, Dec};
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 5de6d52092..9b987dea5a 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -1399,7 +1399,7 @@ rekey_chk(Config, RLdaemon, RLclient) ->
Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
%% Make both sides send something:
- {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
+ {ok, _SftpPid} = ssh_sftp:start_channel(ConnectionRef),
%% Check rekeying
timer:sleep(?REKEY_DATA_TMO),
diff --git a/lib/ssh/test/ssh_bench_SUITE.erl b/lib/ssh/test/ssh_bench_SUITE.erl
index 2ac4e5636a..880c519a5e 100644
--- a/lib/ssh/test/ssh_bench_SUITE.erl
+++ b/lib/ssh/test/ssh_bench_SUITE.erl
@@ -178,7 +178,7 @@ gen_data(DataSz) ->
connect_measure(Port, Cipher, Mac, Data, Options) ->
- AES_GCM = {cipher,
+ _AES_GCM = {cipher,
[]},
@@ -187,22 +187,22 @@ connect_measure(Port, Cipher, Mac, Data, Options) ->
{none,none} ->
[{modify_algorithms,[{prepend, [{cipher,[Cipher]},
{mac,[Mac]}]}
-%%% ,{rm,[AES_GCM]}
+%%% ,{rm,[_AES_GCM]}
]}];
{none,_} ->
[{modify_algorithms,[{prepend, [{cipher,[Cipher]}]}
-%%% ,{rm,[AES_GCM]}
+%%% ,{rm,[_AES_GCM]}
]},
{preferred_algorithms, [{mac,[Mac]}]}];
{_,none} ->
[{modify_algorithms,[{prepend, [{mac,[Mac]}]}
-%%% ,{rm,[AES_GCM]}
+%%% ,{rm,[_AES_GCM]}
]},
{preferred_algorithms, [{cipher,[Cipher]}]}];
_ ->
[{preferred_algorithms, [{cipher,[Cipher]},
{mac,[Mac]}]}
-%%% ,{modify_algorithms, [{rm,[AES_GCM]}]}
+%%% ,{modify_algorithms, [{rm,[_AES_GCM]}]}
]
end,
Times =
diff --git a/lib/ssh/test/ssh_chan_behaviours_SUITE.erl b/lib/ssh/test/ssh_chan_behaviours_SUITE.erl
index 16ed152bcd..103d7253fd 100644
--- a/lib/ssh/test/ssh_chan_behaviours_SUITE.erl
+++ b/lib/ssh/test/ssh_chan_behaviours_SUITE.erl
@@ -128,8 +128,8 @@ subsystem_client(Config) ->
C = proplists:get_value(connref, Config),
{ok,ChRef} = ssh_chan_behaviours_client:start_link(C),
- IDclt = ?EXPECT({{C,Ch1clt}, {ssh_channel_up,Ch1clt,C}}, {C,Ch1clt}),
- IDsrv = ?EXPECT({{_Csrv,Ch1srv}, {ssh_channel_up,Ch1srv,_Csrv}}, {_Csrv,Ch1srv}),
+ IDclt = ?EXPECT({{C,_Ch1clt}, {ssh_channel_up,_Ch1clt,C}}, {C,_Ch1clt}),
+ IDsrv = ?EXPECT({{_Csrv,_Ch1srv}, {ssh_channel_up,_Ch1srv,_Csrv}}, {_Csrv,_Ch1srv}),
ok = ssh_chan_behaviours_client:stop(ChRef),
?EXPECT({IDclt, {terminate,normal}}, []), % From the proper channel handler
diff --git a/lib/ssh/test/ssh_chan_behaviours_client.erl b/lib/ssh/test/ssh_chan_behaviours_client.erl
index 15f17733d6..8dd18973ad 100644
--- a/lib/ssh/test/ssh_chan_behaviours_client.erl
+++ b/lib/ssh/test/ssh_chan_behaviours_client.erl
@@ -94,7 +94,7 @@ handle_ssh_msg({ssh_cm, C, {eof, Ch}}=M, #state{ch=Ch,cm=C} = State) ->
?DBG(State, "eof",[]),
{ok, State};
-handle_ssh_msg({ssh_cm, C, {signal, _Ch, _SigNameStr}=Sig} = M, #state{ch=Ch,cm=C} = State) ->
+handle_ssh_msg({ssh_cm, C, {signal, Ch, _SigNameStr}=Sig} = M, #state{ch=Ch,cm=C} = State) ->
%% Ignore signals according to RFC 4254 section 6.9.
tell_parent(M, State),
?DBG(State, "~p",[Sig]),
diff --git a/lib/ssh/test/ssh_chan_behaviours_server.erl b/lib/ssh/test/ssh_chan_behaviours_server.erl
index 1408675a6e..1d504b1bc6 100644
--- a/lib/ssh/test/ssh_chan_behaviours_server.erl
+++ b/lib/ssh/test/ssh_chan_behaviours_server.erl
@@ -65,7 +65,7 @@ handle_ssh_msg({ssh_cm, C, {eof, Ch}}=M, #state{ch=Ch,cm=C} = State) ->
?DBG(State, "eof",[]),
{ok, State};
-handle_ssh_msg({ssh_cm, C, {signal, _Ch, _SigNameStr}=Sig} = M, #state{ch=Ch,cm=C} = State) ->
+handle_ssh_msg({ssh_cm, C, {signal, Ch, _SigNameStr}=Sig} = M, #state{ch=Ch,cm=C} = State) ->
%% Ignore signals according to RFC 4254 section 6.9.
tell_parent(M, State),
?DBG(State, "~p",[Sig]),
diff --git a/lib/ssh/test/ssh_compat_SUITE.erl b/lib/ssh/test/ssh_compat_SUITE.erl
index 8e82527c6e..06ed9082cf 100644
--- a/lib/ssh/test/ssh_compat_SUITE.erl
+++ b/lib/ssh/test/ssh_compat_SUITE.erl
@@ -150,8 +150,7 @@ init_per_group(G, Config0) ->
stop_docker(ID),
{fail, "Can't contact docker sshd"}
catch
- Class:Exc ->
- ST = erlang:get_stacktrace(),
+ Class:Exc:ST ->
ct:log("common_algs: ~p:~p~n~p",[Class,Exc,ST]),
stop_docker(ID),
{fail, "Failed during setup"}
@@ -160,8 +159,7 @@ init_per_group(G, Config0) ->
cant_start_docker ->
{skip, "Can't start docker"};
- C:E ->
- ST = erlang:get_stacktrace(),
+ C:E:ST ->
ct:log("No ~p~n~p:~p~n~p",[G,C,E,ST]),
{skip, "Can't start docker"}
end;
@@ -1026,8 +1024,7 @@ receive_hello(S) ->
Result ->
Result
catch
- Class:Error ->
- ST = erlang:get_stacktrace(),
+ Class:Error:ST ->
{error, {Class,Error,ST}}
end.
@@ -1104,8 +1101,7 @@ sftp_tests_erl_server(Config, ServerIP, ServerPort, ServerRootDir, UserDir) ->
call_sftp_in_docker(Config, ServerIP, ServerPort, Cmnds, UserDir),
check_local_directory(ServerRootDir)
catch
- Class:Error ->
- ST = erlang:get_stacktrace(),
+ Class:Error:ST ->
{error, {Class,Error,ST}}
end.
@@ -1133,7 +1129,7 @@ check_local_directory(ServerRootDir) ->
check_local_directory(ServerRootDir, SleepTime, N) ->
case do_check_local_directory(ServerRootDir) of
- {error,Error} when N>0 ->
+ {error,_Error} when N>0 ->
%% Could be that the erlang side is faster and the docker's operations
%% are not yet finalized.
%% Sleep for a while and retry a few times:
@@ -1347,8 +1343,7 @@ one_test_erl_client(SFTP, Id, C) when SFTP==sftp ; SFTP==sftp_async ->
catch ssh_sftp:stop_channel(Ch),
R
catch
- Class:Error ->
- ST = erlang:get_stacktrace(),
+ Class:Error:ST ->
{error, {SFTP,Id,Class,Error,ST}}
end.
diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl
index 60d0da2a39..bf90f74324 100644
--- a/lib/ssh/test/ssh_options_SUITE.erl
+++ b/lib/ssh/test/ssh_options_SUITE.erl
@@ -214,7 +214,7 @@ init_per_testcase(_TestCase, Config) ->
file:make_dir(UserDir),
[{user_dir,UserDir}|Config].
-end_per_testcase(_TestCase, Config) ->
+end_per_testcase(_TestCase, _Config) ->
ssh:stop(),
ok.
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index a1a7eebcde..1129303414 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -409,7 +409,7 @@ ct:log("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file
setup_ecdsa_auth_keys(Size, DataDir, UserDir).
setup_eddsa(Alg, DataDir, UserDir) ->
- {IdPriv, IdPub, HostPriv, HostPub} =
+ {IdPriv, _IdPub, HostPriv, HostPub} =
case Alg of
ed25519 -> {"id_ed25519", "id_ed25519.pub", "ssh_host_ed25519_key", "ssh_host_ed25519_key.pub"};
ed448 -> {"id_ed448", "id_ed448.pub", "ssh_host_ed448_key", "ssh_host_ed448_key.pub"}
@@ -970,7 +970,7 @@ expected_state(_) -> false.
%%%----------------------------------------------------------------
%%% Return a string with N random characters
%%%
-random_chars(N) -> [crypto:rand_uniform($a,$z) || _<-lists:duplicate(N,x)].
+random_chars(N) -> [($a-1)+rand:uniform($z-$a) || _<-lists:duplicate(N,x)].
create_random_dir(Config) ->
diff --git a/lib/ssh/test/ssh_trpt_test_lib.erl b/lib/ssh/test/ssh_trpt_test_lib.erl
index f2c9892f95..3f4df2c986 100644
--- a/lib/ssh/test/ssh_trpt_test_lib.erl
+++ b/lib/ssh/test/ssh_trpt_test_lib.erl
@@ -570,75 +570,6 @@ receive_binary_msg(S0=#s{}) ->
-old_receive_binary_msg(S0=#s{ssh=C0=#ssh{decrypt_block_size = BlockSize,
- recv_mac_size = MacSize
- }
- }) ->
- case size(S0#s.encrypted_data_buffer) >= max(8,BlockSize) of
- false ->
- %% Need more bytes to decode the packet_length field
- Remaining = max(8,BlockSize) - size(S0#s.encrypted_data_buffer),
- receive_binary_msg( receive_wait(Remaining, S0) );
- true ->
- %% Has enough bytes to decode the packet_length field
- {_, <<?UINT32(PacketLen), _/binary>>, _} =
- ssh_transport:decrypt_blocks(S0#s.encrypted_data_buffer, BlockSize, C0), % FIXME: BlockSize should be at least 4
-
- %% FIXME: Check that ((4+PacketLen) rem BlockSize) == 0 ?
-
- S1 = if
- PacketLen > ?SSH_MAX_PACKET_SIZE ->
- fail({too_large_message,PacketLen},S0); % FIXME: disconnect
-
- ((4+PacketLen) rem BlockSize) =/= 0 ->
- fail(bad_packet_length_modulo, S0); % FIXME: disconnect
-
- size(S0#s.encrypted_data_buffer) >= (4 + PacketLen + MacSize) ->
- %% has the whole packet
- S0;
-
- true ->
- %% need more bytes to get have the whole packet
- Remaining = (4 + PacketLen + MacSize) - size(S0#s.encrypted_data_buffer),
- receive_wait(Remaining, S0)
- end,
-
- %% Decrypt all, including the packet_length part (re-use the initial #ssh{})
- {C1, SshPacket = <<?UINT32(_),?BYTE(PadLen),Tail/binary>>, EncRest} =
- ssh_transport:decrypt_blocks(S1#s.encrypted_data_buffer, PacketLen+4, C0),
-
- PayloadLen = PacketLen - 1 - PadLen,
- <<CompressedPayload:PayloadLen/binary, _Padding:PadLen/binary>> = Tail,
-
- {C2, Payload} = ssh_transport:decompress(C1, CompressedPayload),
-
- <<Mac:MacSize/binary, Rest/binary>> = EncRest,
-
- case {ssh_transport:is_valid_mac(Mac, SshPacket, C2),
- catch ssh_message:decode(set_prefix_if_trouble(Payload,S1))}
- of
- {false, _} -> fail(bad_mac,S1);
- {_, {'EXIT',_}} -> fail(decode_failed,S1);
-
- {true, Msg} ->
- C3 = case Msg of
- #ssh_msg_kexinit{} ->
- ssh_transport:key_init(opposite_role(C2), C2, Payload);
- _ ->
- C2
- end,
- S2 = opt(print_messages, S1,
- fun(X) when X==true;X==detail -> {"Recv~n~s~n",[format_msg(Msg)]} end),
- S3 = opt(print_messages, S2,
- fun(detail) -> {"decrypted bytes ~p~n",[SshPacket]} end),
- S3#s{ssh = inc_recv_seq_num(C3),
- encrypted_data_buffer = Rest,
- return_value = Msg
- }
- end
- end.
-
-
set_prefix_if_trouble(Msg = <<?BYTE(Op),_/binary>>, #s{alg=#alg{kex=Kex}})
when Op == 30;
Op == 31
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 2947f82556..837da27ab0 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,4 +1,4 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.7.5
+SSH_VSN = 4.7.6
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index a511cb4db3..f0231da2ad 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -27,6 +27,23 @@
</header>
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 9.2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ With the default BEAST Mitigation strategy for TLS 1.0 an
+ empty TLS fragment could be sent after a one-byte
+ fragment. This glitch has been fixed.</p>
+ <p>
+ Own Id: OTP-15054 Aux Id: ERIERL-346 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 9.2.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 74a0a0a03e..d626748af6 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -128,7 +128,7 @@
<name name="hostname"/>
</datatype>
- <datatype>
+ <datatype>
<name name="ip_address"/>
</datatype>
@@ -136,14 +136,14 @@
<name name="protocol_version"/>
</datatype>
- <datatype>
+ <datatype>
<name name="tls_version"/>
</datatype>
-
+
<datatype>
<name name="dtls_version"/>
</datatype>
-
+
<datatype>
<name name="tls_legacy_version"/>
</datatype>
@@ -151,9 +151,8 @@
<datatype>
<name name="dtls_legacy_version"/>
</datatype>
-
-
- <datatype>
+
+ <datatype>
<name name="prf_random"/>
</datatype>
@@ -218,10 +217,6 @@
</datatype>
<datatype>
- <name name="eccs"/>
- </datatype>
-
- <datatype>
<name name="named_curve"/>
</datatype>
@@ -253,6 +248,10 @@
<name name="tls_alert"/>
</datatype>
+ <datatype>
+ <name name="reason"/>
+ </datatype>
+
<datatype_title>TLS/DTLS OPTION DESCRIPTIONS - COMMON for SERVER and CLIENT</datatype_title>
<datatype>
@@ -343,13 +342,6 @@
</datatype>
<datatype>
- <name name="eccs"/>
- <desc><p> Allows to specify the order of preference for named curves
- and to restrict their usage when using a cipher suite supporting them.</p>
- </desc>
- </datatype>
-
- <datatype>
<name name="signature_schemes"/>
<desc>
<p>
@@ -1109,13 +1101,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
<funcs>
<func>
- <name since="OTP 20.3">append_cipher_suites(Deferred, Suites) -> ciphers() </name>
+ <name name="append_cipher_suites" arity="2" since="OTP 20.3"/>
<fsummary></fsummary>
- <type>
- <v>Deferred = <seealso marker="#type-ciphers">ciphers()</seealso> |
- <seealso marker="#type-cipher_filters">cipher_filters()</seealso></v>
- <v>Suites = <seealso marker="#type-ciphers">ciphers()</seealso></v>
- </type>
<desc><p>Make <c>Deferred</c> suites become the least preferred
suites, that is put them at the end of the cipher suite list
<c>Suites</c> after removing them from <c>Suites</c> if
@@ -1126,25 +1113,18 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP R14B">cipher_suites() -></name>
- <name since="OTP R14B">cipher_suites(Type) -> [old_cipher_suite()]</name>
+ <name name="cipher_suites" arity="0" since="OTP R14B"/>
+ <name name="cipher_suites" arity="1" since="OTP R14B"/>
<fsummary>Returns a list of supported cipher suites.</fsummary>
- <type>
- <v>Type = erlang | openssl | all</v>
- </type>
<desc>
<p>Deprecated in OTP 21, use <seealso marker="#cipher_suites-2">cipher_suites/2</seealso> instead.</p>
</desc>
</func>
<func>
- <name since="OTP 20.3">cipher_suites(Supported, Version) -> ciphers()</name>
+ <name name="cipher_suites" arity="2" since="OTP 20.3"/>
<fsummary>Returns a list of all default or
all supported cipher suites.</fsummary>
- <type>
- <v> Supported = default | all | anonymous </v>
- <v> Version = <seealso marker="#type-protocol_version">protocol_version() </seealso></v>
- </type>
<desc><p>Returns all default or all supported (except anonymous),
or all anonymous cipher suites for a
TLS version</p>
@@ -1152,16 +1132,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP 19.2">eccs() -></name>
- <name since="OTP 19.2">eccs(Version) -> NamedCurves</name>
+ <name name="eccs" arity="0" since="OTP 19.2"/>
+ <name name="eccs" arity="1" since="OTP 19.2"/>
<fsummary>Returns a list of supported ECCs.</fsummary>
-
- <type>
- <v> Version = <seealso marker="#type-protocol_version">protocol_version() </seealso></v>
- <v> NamedCurves = <seealso marker="#type-named_curve">[named_curve()] </seealso></v>
-
- </type>
-
<desc><p>Returns a list of supported ECCs. <c>eccs()</c>
is equivalent to calling <c>eccs(Protocol)</c> with all
supported protocols and then deduplicating the output.</p>
@@ -1169,9 +1142,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP 17.5">clear_pem_cache() -> ok </name>
+ <name name="clear_pem_cache" arity="0" since="OTP 17.5"/>
<fsummary> Clears the pem cache</fsummary>
-
<desc><p>PEM files, used by ssl API-functions, are cached. The
cache is regularly checked to see if any cache entries should be
invalidated, however this function provides a way to
@@ -1181,19 +1153,10 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP R14B">connect(Socket, Options) -> </name>
- <name since="">connect(Socket, Options, Timeout) -> {ok, SslSocket} | {ok, SslSocket, Ext}
- | {error, Reason}</name>
+ <name name="connect" arity="2" since="OTP R14B"/>
+ <name name="connect" arity="3" clause_i="1" since=""/>
<fsummary>Upgrades a <c>gen_tcp</c>, or
equivalent, connected socket to an TLS socket.</fsummary>
- <type>
- <v>Socket = <seealso marker="#type-socket"> socket() </seealso></v>
- <v>Options = <seealso marker="#type-tls_client_option"> [tls_client_option()] </seealso></v>
- <v>Timeout = timeout()</v>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Ext = <seealso marker="#type-protocol_extensions">protocol_extensions()</seealso></v>
- <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v>
- </type>
<desc><p>Upgrades a <c>gen_tcp</c>, or equivalent,
connected socket to an TLS socket, that is, performs the
client-side TLS handshake.</p>
@@ -1225,18 +1188,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="">connect(Host, Port, Options) -></name>
- <name since="">connect(Host, Port, Options, Timeout) ->
- {ok, SslSocket}| {ok, SslSocket, Ext} | {error, Reason}</name>
+ <name since="" name="connect" arity="3" clause_i="2"/>
+ <name since="" name="connect" arity="4"/>
<fsummary>Opens an TLS/DTLS connection to <c>Host</c>, <c>Port</c>.</fsummary>
- <type>
- <v>Host =<seealso marker="#type-host"> host() </seealso> </v>
- <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v>
- <v>Options = <seealso marker="#type-tls_client_option"> [tls_client_option()]</seealso></v>
- <v>Timeout = timeout()</v>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v>
- </type>
<desc><p>Opens an TLS/DTLS connection to <c>Host</c>, <c>Port</c>.</p>
<p> When the option <c>verify</c> is set to <c>verify_peer</c> the check
@@ -1273,24 +1227,15 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="">close(SslSocket) -> ok | {error, Reason}</name>
+ <name since="" name="close" arity="1" />
<fsummary>Closes an TLS/DTLS connection.</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Reason = term()</v>
- </type>
<desc><p>Closes an TLS/DTLS connection.</p>
</desc>
</func>
<func>
- <name since="OTP 18.1">close(SslSocket, How) -> ok | {ok, port()} | {error, Reason}</name>
+ <name since="OTP 18.1" name="close" arity="2"/>
<fsummary>Closes an TLS connection.</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>How = timeout() | {NewController::pid(), timeout()} </v>
- <v>Reason = term()</v>
- </type>
<desc><p>Closes or downgrades an TLS connection. In the latter case the transport
connection will be handed over to the <c>NewController</c> process after receiving
the TLS close alert from the peer. The returned transport socket will have
@@ -1299,15 +1244,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="">controlling_process(SslSocket, NewOwner) ->
- ok | {error, Reason}</name>
+ <name since="" name="controlling_process" arity="2" />
<fsummary>Assigns a new controlling process to the
TLS/DTLS socket.</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>NewOwner = pid()</v>
- <v>Reason = term()</v>
- </type>
<desc><p>Assigns a new controlling process to the SSL socket. A
controlling process is the owner of an SSL socket, and receives
all messages from the socket.</p>
@@ -1315,17 +1254,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP 18.0">connection_information(SslSocket) ->
- {ok, Result} | {error, Reason} </name>
+ <name since="OTP 18.0" name="connection_information" arity="1"/>
<fsummary>Returns all the connection information.
</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Item = protocol | selected_cipher_suite | sni_hostname | ecc | session_id | atom()</v>
- <d>Meaningful atoms, not specified above, are the ssl option names.</d>
- <v>Result = [{Item::atom(), Value::term()}]</v>
- <v>Reason = term()</v>
- </type>
<desc><p>Returns the most relevant information about the connection, ssl options that
are undefined will be filtered out. Note that values that affect the security of the
connection will only be returned if explicitly requested by connection_information/2.</p>
@@ -1336,34 +1267,23 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP 18.0">connection_information(SslSocket, Items) ->
- {ok, Result} | {error, Reason} </name>
+ <name since="OTP 18.0" name="connection_information" arity="2"/>
<fsummary>Returns the requested connection information.
</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Items = [Item]</v>
- <v>Item = protocol | cipher_suite | sni_hostname | ecc | session_id | client_random
- | server_random | master_secret | atom()</v>
- <d>Note that client_random, server_random and master_secret are values
- that affect the security of connection. Meaningful atoms, not specified above, are the ssl option names.</d>
- <v>Result = [{Item::atom(), Value::term()}]</v>
- <v>Reason = term()</v>
- </type>
<desc><p>Returns the requested information items about the connection,
if they are defined.</p>
+ <p>Note that client_random, server_random and master_secret are values
+ that affect the security of connection. Meaningful atoms, not specified
+ above, are the ssl option names.</p>
+
<note><p>If only undefined options are requested the
resulting list can be empty.</p></note>
</desc>
</func>
<func>
- <name since="OTP 20.3">filter_cipher_suites(Suites, Filters) -> ciphers()</name>
+ <name since="OTP 20.3" name="filter_cipher_suites" arity="2" />
<fsummary></fsummary>
- <type>
- <v> Suites = <seealso marker="#type-ciphers"> ciphers() </seealso></v>
- <v> Filters = <seealso marker="#type-cipher_filters"> cipher_filters() </seealso></v>
- </type>
<desc><p>Removes cipher suites if any of the filter functions
returns false for any part of the cipher suite. This function
also calls default filter functions to make sure the cipher
@@ -1373,24 +1293,16 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="">format_error(Reason) -> string()</name>
+ <name since="" name="format_error" arity="1" />
<fsummary>Returns an error string.</fsummary>
- <type>
- <v>Reason = term()</v>
- </type>
<desc>
<p>Presents the error returned by an SSL function as a printable string.</p>
</desc>
</func>
<func>
- <name since="">getopts(SslSocket, OptionNames) ->
- {ok, [socketoption()]} | {error, Reason}</name>
+ <name since="" name="getopts" arity="2" />
<fsummary>Gets the values of the specified options.</fsummary>
- <type>
- <v>Socket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>OptionNames = [atom()]</v>
- </type>
<desc>
<p>Gets the values of the specified socket options.
</p>
@@ -1398,16 +1310,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP 19.0">getstat(SslSocket) ->
- {ok, OptionValues} | {error, inet:posix()}</name>
- <name since="OTP 19.0">getstat(SslSocket, OptionNames) ->
- {ok, OptionValues} | {error, inet:posix()}</name>
+ <name since="OTP 19.0" name="getstat" arity="1" />
+ <name since="OTP 19.0" name="getstat" arity="2" />
<fsummary>Get one or more statistic options for a socket</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>OptionNames = [atom()]</v>
- <v>OptionValues = [{inet:stat_option(), integer()}]</v>
- </type>
<desc>
<p>Gets one or more statistic options for the underlying TCP socket.</p>
<p>See inet:getstat/2 for statistic options description.</p>
@@ -1415,14 +1320,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP 21.0">handshake(HsSocket) -> </name>
- <name since="OTP 21.0">handshake(HsSocket, Timeout) -> {ok, SslSocket} | {error, Reason}</name>
+ <name since="OTP 21.0" name="handshake" arity="1" />
+ <name since="OTP 21.0" name="handshake" arity="2" clause_i="1" />
<fsummary>Performs server-side SSL/TLS handshake.</fsummary>
- <type>
- <v>HsSocket = SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Timeout = timeout()</v>
- <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v>
- </type>
<desc>
<p>Performs the SSL/TLS/DTLS server-side handshake.</p>
<p>Returns a new TLS/DTLS socket if the handshake is successful.</p>
@@ -1435,17 +1335,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP 21.0">handshake(Socket, Options) -> </name>
- <name since="OTP 21.0">handshake(Socket, Options, Timeout) -> {ok, SslSocket} | {ok, SslSocket, Ext} | {error, Reason}</name>
+ <name since="OTP 21.0" name="handshake" arity="2" clause_i="2" />
+ <name since="OTP 21.0" name="handshake" arity="3" />
<fsummary>Performs server-side SSL/TLS/DTLS handshake.</fsummary>
- <type>
- <v>Socket = socket() | <seealso marker="#type-sslsocket"> socket() </seealso> </v>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso> </v>
- <v>Ext = <seealso marker="#type-protocol_extensions">protocol_extensions()</seealso></v>
- <v>Options = <seealso marker="#type-tls_server_option"> [server_option()] </seealso> </v>
- <v>Timeout = timeout()</v>
- <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v>
- </type>
<desc>
<p>If <c>Socket</c> is a ordinary <c>socket()</c>: upgrades a <c>gen_tcp</c>,
or equivalent, socket to an SSL socket, that is, performs
@@ -1481,52 +1373,33 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP 21.0">handshake_cancel(SslSocket) -> ok </name>
+ <name since="OTP 21.0" name="handshake_cancel" arity="1" />
<fsummary>Cancel handshake with a fatal alert</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- </type>
<desc>
<p>Cancel the handshake with a fatal <c>USER_CANCELED</c> alert.</p>
</desc>
</func>
<func>
- <name since="OTP 21.0">handshake_continue(HsSocket, Options) -> {ok, SslSocket} | {error, Reason}</name>
- <name since="OTP 21.0">handshake_continue(HsSocket, Options, Timeout) -> {ok, SslSocket} | {error, Reason}</name>
+ <name since="OTP 21.0" name="handshake_continue" arity="2" />
+ <name since="OTP 21.0" name="handshake_continue" arity="3" />
<fsummary>Continue the SSL/TLS handshake.</fsummary>
- <type>
- <v>HsSocket = SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Options = <seealso marker="#type-tls_option"> tls_option() </seealso> </v>
- <v>Timeout = timeout()</v>
- <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v>
- </type>
<desc>
<p>Continue the SSL/TLS handshake possiby with new, additional or changed options.</p>
</desc>
</func>
<func>
- <name since="">listen(Port, Options) ->
- {ok, ListenSocket} | {error, Reason}</name>
+ <name since="" name="listen" arity="2" />
<fsummary>Creates an SSL listen socket.</fsummary>
- <type>
- <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v>
- <v>Options = <seealso marker="#type-tls_server_option"> [server_option()] </seealso></v>
- <v>ListenSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- </type>
<desc>
<p>Creates an SSL listen socket.</p>
</desc>
</func>
<func>
- <name since="OTP 18.0">negotiated_protocol(SslSocket) -> {ok, Protocol} | {error, protocol_not_negotiated}</name>
+ <name since="OTP 18.0" name="negotiated_protocol" arity="1" />
<fsummary>Returns the protocol negotiated through ALPN or NPN extensions.</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Protocol = binary()</v>
- </type>
<desc>
<p>
Returns the protocol negotiated through ALPN or NPN extensions.
@@ -1535,12 +1408,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="">peercert(SslSocket) -> {ok, Cert} | {error, Reason}</name>
+ <name since="" name="peercert" arity="1" />
<fsummary>Returns the peer certificate.</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Cert = binary()</v>
- </type>
<desc>
<p>The peer certificate is returned as a DER-encoded binary.
The certificate can be decoded with
@@ -1550,27 +1419,16 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="">peername(SslSocket) -> {ok, {Address, Port}} |
- {error, Reason}</name>
+ <name since="" name="peername" arity="1" />
<fsummary>Returns the peer address and port.</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Address = ipaddress()</v>
- <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v>
- </type>
<desc>
<p>Returns the address and port number of the peer.</p>
</desc>
</func>
<func>
- <name since="OTP 20.3">prepend_cipher_suites(Preferred, Suites) -> ciphers()</name>
+ <name since="OTP 20.3" name="prepend_cipher_suites" arity="2" />
<fsummary></fsummary>
- <type>
- <v>Preferred = <seealso marker="#type-ciphers">ciphers()</seealso> |
- <seealso marker="#type-cipher_filters">cipher_filters()</seealso></v>
- <v>Suites = <seealso marker="#type-ciphers">ciphers()</seealso></v>
- </type>
<desc><p>Make <c>Preferred</c> suites become the most preferred
suites that is put them at the head of the cipher suite list
<c>Suites</c> after removing them from <c>Suites</c> if
@@ -1581,15 +1439,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP R15B01">prf(Socket, Secret, Label, Seed, WantedLength) -> {ok, binary()} | {error, reason()}</name>
+ <name since="OTP R15B01" name="prf" arity="5" />
<fsummary>Uses a session Pseudo-Random Function to generate key material.</fsummary>
- <type>
- <v>Socket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Secret = binary() | master_secret</v>
- <v>Label = binary()</v>
- <v>Seed = [binary() | <seealso marker="#type-prf_random"> prf_random()</seealso>]</v>
- <v>WantedLength = non_neg_integer()</v>
- </type>
<desc>
<p>Uses the Pseudo-Random Function (PRF) of a TLS session to generate
extra key material. It either takes user-generated values for
@@ -1601,16 +1452,14 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="">recv(SslSocket, Length) -> </name>
- <name since="">recv(SslSocket, Length, Timeout) -> {ok, Data} | {error,
- Reason}</name>
+ <name since="" name="recv" arity="2" />
+ <name since="" name="recv" arity="3" />
<fsummary>Receives data on a socket.</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Length = integer()</v>
- <v>Timeout = timeout()</v>
- <v>Data = [char()] | binary()</v>
- </type>
+ <type_desc variable="HttpPacket">See the description of
+ <c>HttpPacket</c> in
+ <seealso marker="erts:erlang#decode_packet/3"><c>erlang:decode_packet/3</c></seealso>
+ in ERTS.
+ </type_desc>
<desc>
<p>Receives a packet from a socket in passive
mode. A closed socket is indicated by return value
@@ -1628,11 +1477,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP R14B">renegotiate(SslSocket) -> ok | {error, Reason}</name>
+ <name since="OTP R14B" name="renegotiate" arity="1" />
<fsummary>Initiates a new handshake.</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- </type>
<desc><p>Initiates a new handshake. A notable return value is
<c>{error, renegotiation_rejected}</c> indicating that the peer
refused to go through with the renegotiation, but the connection
@@ -1641,40 +1487,27 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="">send(SslSocket, Data) -> ok | {error, Reason}</name>
+ <name since="" name="send" arity="2" />
<fsummary>Writes data to a socket.</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Data = iodata()</v>
- </type>
<desc>
- <p>Writes <c>Data</c> to <c>Socket</c>.</p>
+ <p>Writes <c>Data</c> to <c>SslSocket</c>.</p>
<p>A notable return value is <c>{error, closed}</c> indicating that
the socket is closed.</p>
</desc>
</func>
<func>
- <name since="">setopts(SslSocket, Options) -> ok | {error, Reason}</name>
+ <name since="" name="setopts" arity="2" />
<fsummary>Sets socket options.</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Options = <seealso marker="#type-socket_option"> [socket_option()] </seealso></v>
- </type>
<desc>
<p>Sets options according to <c>Options</c> for socket
- <c>Socket</c>.</p>
+ <c>SslSocket</c>.</p>
</desc>
</func>
<func>
- <name since="OTP R14B">shutdown(SslSocket, How) -> ok | {error, Reason}</name>
+ <name since="OTP R14B" name="shutdown" arity="2" />
<fsummary>Immediately closes a socket.</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>How = read | write | read_write</v>
- <v>Reason = reason()</v>
- </type>
<desc>
<p>Immediately closes a socket in one or two directions.</p>
<p><c>How == write</c> means closing the socket for writing,
@@ -1686,14 +1519,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="">ssl_accept(SslSocket) -> </name>
- <name since="">ssl_accept(SslSocket, Timeout) -> ok | {error, Reason}</name>
+ <name since="" name="ssl_accept" arity="1" />
+ <name since="" name="ssl_accept" arity="2" />
<fsummary>Performs server-side SSL/TLS handshake.</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Timeout = timeout()</v>
- <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v>
- </type>
<desc>
<p>Deprecated in OTP 21, use <seealso marker="#handshake-1">handshake/[1,2]</seealso> instead.</p>
<note><p>handshake/[1,2] always returns a new socket.</p></note>
@@ -1701,15 +1529,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="">ssl_accept(Socket, Options) -> </name>
- <name since="OTP R14B">ssl_accept(Socket, Options, Timeout) -> {ok, Socket} | ok | {error, Reason}</name>
+ <name since="OTP R14B" name="ssl_accept" arity="3" />
<fsummary>Performs server-side SSL/TLS/DTLS handshake.</fsummary>
- <type>
- <v>Socket = socket() | <seealso marker="#type-sslsocket"> sslsocket() </seealso> </v>
- <v>Options = <seealso marker="#type-tls_server_option"> [server_option()] </seealso> </v>
- <v>Timeout = timeout()</v>
- <v>Reason = closed | timeout | <seealso marker="#type-error_alert"> error_alert() </seealso></v>
- </type>
<desc>
<p>Deprecated in OTP 21, use <seealso marker="#handshake-3">handshake/[2,3]</seealso> instead.</p>
<note><p>handshake/[2,3] always returns a new socket.</p></note>
@@ -1717,27 +1538,18 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="">sockname(SslSocket) -> {ok, {Address, Port}} |
- {error, Reason}</name>
+ <name since="" name="sockname" arity="1" />
<fsummary>Returns the local address and port.</fsummary>
- <type>
- <v>SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Address = <seealso marker="#type-ip_address">ip_address()</seealso></v>
- <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v>
- </type>
<desc>
<p>Returns the local address and port number of socket
- <c>Socket</c>.</p>
+ <c>SslSocket</c>.</p>
</desc>
</func>
<func>
- <name since="OTP R14B">start() -> </name>
+ <name since="OTP R14B" name="start" arity="0" />
<name since="OTP R14B">start(Type) -> ok | {error, Reason}</name>
<fsummary>Starts the SSL application.</fsummary>
- <type>
- <v>Type = permanent | transient | temporary</v>
- </type>
<desc>
<p>Starts the SSL application. Default type
is <c>temporary</c>.</p>
@@ -1745,7 +1557,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP R14B">stop() -> ok </name>
+ <name since="OTP R14B" name="stop" arity="0" />
<fsummary>Stops the SSL application.</fsummary>
<desc>
<p>Stops the SSL application.</p>
@@ -1753,28 +1565,18 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP 21.0">suite_to_str(CipherSuite) -> String</name>
+ <name since="OTP 21.0" name="suite_to_str" arity="1" clause_i="1" />
<fsummary>Returns the string representation of a cipher suite.</fsummary>
- <type>
- <v>CipherSuite = <seealso marker="#type-erl_cipher_suite"> erl_cipher_suite() </seealso></v>
- <v>String = string()</v>
- </type>
<desc>
<p>Returns the string representation of a cipher suite.</p>
</desc>
</func>
<func>
- <name since="">transport_accept(ListenSocket) -></name>
- <name since="">transport_accept(ListenSocket, Timeout) ->
- {ok, SslSocket} | {error, Reason}</name>
+ <name since="" name="transport_accept" arity="1" />
+ <name since="" name="transport_accept" arity="2" />
<fsummary>Accepts an incoming connection and
prepares for <c>ssl_accept</c>.</fsummary>
- <type>
- <v>ListenSocket = SslSocket = <seealso marker="#type-sslsocket"> sslsocket() </seealso></v>
- <v>Timeout = timeout()</v>
- <v>Reason = reason()</v>
- </type>
<desc>
<p>Accepts an incoming connection request on a listen socket.
<c>ListenSocket</c> must be a socket returned from
@@ -1800,13 +1602,9 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
- <name since="OTP R14B">versions() -> [versions_info()]</name>
+ <name since="OTP R14B" name="versions" arity="0" />
<fsummary>Returns version information relevant for the
SSL application.</fsummary>
- <type>
- <v>versions_info() = {app_vsn, string()} | {supported | available, [ssl_tls_protocol()]} |
- {supported_dtls | available_dtls, [dtls_protocol()]} </v>
- </type>
<desc>
<p>Returns version information relevant for the SSL
application.</p>
diff --git a/lib/ssl/doc/src/standards_compliance.xml b/lib/ssl/doc/src/standards_compliance.xml
index c20bab4e50..ca98385f85 100644
--- a/lib/ssl/doc/src/standards_compliance.xml
+++ b/lib/ssl/doc/src/standards_compliance.xml
@@ -126,8 +126,34 @@
<section>
<title>TLS 1.3</title>
- <p> This section describes the current state of standards compliance for TLS 1.3.</p>
- <p>(C = Compliant, NC = Non-Compliant, P = Partially-Compliant, NA = Not Applicable)</p>
+ <p>OTP-22 introduces basic support for TLS 1.3 on the server side. Basic functionality
+ covers a simple TLS 1.3 handshake with support of the mandatory extensions
+ (supported_groups, signature_algorithms, key_share, supported_versions and
+ signature_algorithms_cert). The server supports a selective set of cryptographic algorithms:</p>
+ <list type="bulleted">
+ <item>Key Exchange: ECDHE</item>
+ <item>Groups: all standard groups supported for the Diffie-Hellman key exchange</item>
+ <item>Ciphers: TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384,
+ TLS_CHACHA20_POLY1305_SHA256 and TLS_AES_128_CCM_SHA256</item>
+ <item>Signature Algorithms: RSA and RSA PSS</item>
+ <item>Certificates: currently only certificates with RSA keys are supported</item>
+ </list>
+ <p>Other notable features:</p>
+ <list type="bulleted">
+ <item>The server supports the HelloRetryRequest mechanism</item>
+ <item>PSK and session resumption not supported</item>
+ <item>Early data and 0-RTT not supported</item>
+ <item>Key and Initialization Vector Update not supported</item>
+ </list>
+ <p>For more detailed information see the
+ <seealso marker="#soc_table">Standards Compliance</seealso> below.</p>
+ <warning><p>Note that the client side is not yet functional. It is planned to be released
+ later in OTP-22.</p></warning>
+
+ <p> The following table describes the current state of standards compliance for TLS 1.3.</p>
+ <p>(<em>C</em> = Compliant, <em>NC</em> = Non-Compliant, <em>PC</em> = Partially-Compliant,
+ <em>NA</em> = Not Applicable)</p>
+ <marker id="soc_table"/>
<table>
<row>
<cell align="left" valign="middle"><em>Section</em></cell>
@@ -155,7 +181,7 @@
<row>
<cell align="left" valign="middle"></cell>
<cell align="left" valign="middle">RSASSA-PSS signature schemes</cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle">22</cell>
</row>
<row>
@@ -178,7 +204,7 @@
</url>
</cell>
<cell align="left" valign="middle"></cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle"><em>22</em></cell>
</row>
<row>
@@ -240,7 +266,7 @@
</url>
</cell>
<cell align="left" valign="middle"></cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle"><em>22</em></cell>
</row>
<row>
@@ -783,7 +809,7 @@
<row>
<cell align="left" valign="middle"></cell>
<cell align="left" valign="middle"><em>Server</em></cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle"><em>22</em></cell>
</row>
<row>
@@ -1265,7 +1291,7 @@
<row>
<cell align="left" valign="middle"></cell>
<cell align="left" valign="middle"><em>Server</em></cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle"><em>22</em></cell>
</row>
<row>
@@ -1379,7 +1405,7 @@
<row>
<cell align="left" valign="middle"></cell>
<cell align="left" valign="middle"><em>Server</em></cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle"><em>22</em></cell>
</row>
<row>
@@ -1456,7 +1482,7 @@
<row>
<cell align="left" valign="middle"></cell>
<cell align="left" valign="middle"><em>Server</em></cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle"><em>22</em></cell>
</row>
<row>
@@ -1527,7 +1553,7 @@
<row>
<cell align="left" valign="middle"></cell>
<cell align="left" valign="middle"><em>Server</em></cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle"><em></em></cell>
</row>
<row>
@@ -1762,7 +1788,7 @@
</url>
</cell>
<cell align="left" valign="middle"><em></em></cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle"><em>22</em></cell>
</row>
<row>
@@ -1924,7 +1950,7 @@
</url>
</cell>
<cell align="left" valign="middle"><em></em></cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle"><em>22</em></cell>
</row>
<row>
@@ -1942,14 +1968,14 @@
<row>
<cell align="left" valign="middle"></cell>
<cell align="left" valign="middle">SHOULD implement the TLS_CHACHA20_POLY1305_SHA256</cell>
- <cell align="left" valign="middle"><em>NC</em></cell>
- <cell align="left" valign="middle"></cell>
+ <cell align="left" valign="middle"><em>C</em></cell>
+ <cell align="left" valign="middle">22</cell>
</row>
<row>
<cell align="left" valign="middle"></cell>
<cell align="left" valign="middle"><em>Digital signatures</em></cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle"><em>22</em></cell>
</row>
<row>
@@ -1997,7 +2023,7 @@
</url>
</cell>
<cell align="left" valign="middle"><em></em></cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle"><em>22</em></cell>
</row>
<row>
@@ -2108,7 +2134,7 @@
<row>
<cell align="left" valign="middle"></cell>
<cell align="left" valign="middle"><em>TLS 1.3 ServerHello</em></cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle"><em>22</em></cell>
</row>
<row>
@@ -2160,7 +2186,7 @@
</url>
</cell>
<cell align="left" valign="middle"><em></em></cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle"><em>22</em></cell>
</row>
<row>
@@ -2178,14 +2204,14 @@
<row>
<cell align="left" valign="middle"></cell>
<cell align="left" valign="middle">TLS_CHACHA20_POLY1305_SHA256</cell>
- <cell align="left" valign="middle"><em>NC</em></cell>
- <cell align="left" valign="middle"></cell>
+ <cell align="left" valign="middle"><em>C</em></cell>
+ <cell align="left" valign="middle">22</cell>
</row>
<row>
<cell align="left" valign="middle"></cell>
<cell align="left" valign="middle">TLS_AES_128_CCM_SHA256</cell>
- <cell align="left" valign="middle"><em>NC</em></cell>
- <cell align="left" valign="middle"></cell>
+ <cell align="left" valign="middle"><em>C</em></cell>
+ <cell align="left" valign="middle">22</cell>
</row>
<row>
<cell align="left" valign="middle"></cell>
@@ -2223,7 +2249,7 @@
</url>
</cell>
<cell align="left" valign="middle"><em></em></cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle"><em>22</em></cell>
</row>
@@ -2289,7 +2315,7 @@
</url>
</cell>
<cell align="left" valign="middle"><em></em></cell>
- <cell align="left" valign="middle"><em>P</em></cell>
+ <cell align="left" valign="middle"><em>PC</em></cell>
<cell align="left" valign="middle"><em>22</em></cell>
</row>
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 74511dce4b..e3bb4df1ac 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -86,27 +86,28 @@
srp_param_type/0]).
%% -------------------------------------------------------------------------------------------------------
--type socket() :: gen_tcp:socket().
--type socket_option() :: gen_tcp:connect_option() | gen_tcp:listen_option() | gen_udp:option().
--type sslsocket() :: any().
--type tls_option() :: tls_client_option() | tls_server_option().
--type tls_client_option() :: client_option() | common_option() | socket_option() | transport_option().
--type tls_server_option() :: server_option() | common_option() | socket_option() | transport_option().
+
+-type socket() :: gen_tcp:socket(). % exported
+-type socket_option() :: gen_tcp:connect_option() | gen_tcp:listen_option() | gen_udp:option(). % exported
+-type sslsocket() :: any(). % exported
+-type tls_option() :: tls_client_option() | tls_server_option(). % exported
+-type tls_client_option() :: client_option() | common_option() | socket_option() | transport_option(). % exported
+-type tls_server_option() :: server_option() | common_option() | socket_option() | transport_option(). % exported
-type active_msgs() :: {ssl, sslsocket(), Data::binary() | list()} | {ssl_closed, sslsocket()} |
- {ssl_error, sslsocket(), Reason::term()} | {ssl_passive, sslsocket()}.
+ {ssl_error, sslsocket(), Reason::any()} | {ssl_passive, sslsocket()}. % exported
-type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(),
ClosedTag::atom(), ErrTag::atom()}} |
{cb_info, {CallbackModule::atom(), DataTag::atom(),
ClosedTag::atom(), ErrTag::atom(), PassiveTag::atom()}}.
--type host() :: hostname() | ip_address().
+-type host() :: hostname() | ip_address(). % exported
-type hostname() :: string().
-type ip_address() :: inet:ip_address().
--type session_id() :: binary().
--type protocol_version() :: tls_version() | dtls_version().
+-type session_id() :: binary(). % exported
+-type protocol_version() :: tls_version() | dtls_version(). % exported
-type tls_version() :: 'tlsv1.2' | 'tlsv1.3' | tls_legacy_version().
-type dtls_version() :: 'dtlsv1.2' | dtls_legacy_version().
-type tls_legacy_version() :: tlsv1 | 'tlsv1.1' | sslv3.
--type dtls_legacy_version() :: 'dtlsv1'.
+-type dtls_legacy_version() :: 'dtlsv1'.
-type verify_type() :: verify_none | verify_peer.
-type cipher() :: aes_128_cbc |
aes_256_cbc |
@@ -117,14 +118,14 @@
aes_128_ccm_8 |
aes_256_ccm_8 |
chacha20_poly1305 |
- legacy_cipher().
+ legacy_cipher(). % exported
-type legacy_cipher() :: rc4_128 |
des_cbc |
'3des_ede_cbc'.
-type hash() :: sha |
sha2() |
- legacy_hash().
+ legacy_hash(). % exported
-type sha2() :: sha224 |
sha256 |
@@ -133,7 +134,7 @@
-type legacy_hash() :: md5.
--type sign_algo() :: rsa | dsa | ecdsa.
+-type sign_algo() :: rsa | dsa | ecdsa. % exported
-type sign_scheme() :: rsa_pkcs1_sha256
| rsa_pkcs1_sha384
@@ -155,7 +156,7 @@
srp_rsa| srp_dss |
psk | dhe_psk | rsa_psk |
dh_anon | ecdh_anon | srp_anon |
- any. %% TLS 1.3
+ any. %% TLS 1.3 , exported
-type erl_cipher_suite() :: #{key_exchange := kex_algo(),
cipher := cipher(),
mac := hash() | aead,
@@ -195,15 +196,18 @@
secp160r1 |
secp160r2.
+-type group() :: secp256r1 | secp384r1 | secp521r1 | ffdhe2048 |
+ ffdhe3072 | ffdhe4096 | ffdhe6144 | ffdhe8192.
+
-type srp_param_type() :: srp_1024 |
srp_1536 |
srp_2048 |
srp_3072 |
srp_4096 |
srp_6144 |
- srp_8192.
+ srp_8192. % exported
--type error_alert() :: {tls_alert, {tls_alert(), Description::string()}}.
+-type error_alert() :: {tls_alert, {tls_alert(), Description::string()}}. % exported
-type tls_alert() :: close_notify |
unexpected_message |
@@ -234,6 +238,7 @@
bad_certificate_hash_value |
unknown_psk_identity |
no_application_protocol.
+
%% -------------------------------------------------------------------------------------------------------
-type common_option() :: {protocol, protocol()} |
{handshake, handshake_completion()} |
@@ -243,7 +248,7 @@
{keyfile, key_pem()} |
{password, key_password()} |
{ciphers, cipher_suites()} |
- {eccs, eccs()} |
+ {eccs, [named_curve()]} |
{signature_algs_cert, signature_schemes()} |
{secure_renegotiate, secure_renegotiation()} |
{depth, allowed_cert_chain_length()} |
@@ -270,29 +275,28 @@
#{algorithm := rsa | dss | ecdsa,
engine := crypto:engine_ref(),
key_id := crypto:key_id(),
- password => crypto:password()}.
+ password => crypto:password()}. % exported
-type key_pem() :: file:filename().
-type key_password() :: string().
-type cipher_suites() :: ciphers().
-type ciphers() :: [erl_cipher_suite()] |
- string(). % (according to old API)
+ string(). % (according to old API) exported
-type cipher_filters() :: list({key_exchange | cipher | mac | prf,
- algo_filter()}).
+ algo_filter()}). % exported
-type algo_filter() :: fun((kex_algo()|cipher()|hash()|aead|default_prf) -> true | false).
--type eccs() :: [named_curve()].
-type secure_renegotiation() :: boolean().
-type allowed_cert_chain_length() :: integer().
--type custom_verify() :: {Verifyfun :: fun(), InitialUserState :: term()}.
+-type custom_verify() :: {Verifyfun :: fun(), InitialUserState :: any()}.
-type crl_check() :: boolean() | peer | best_effort.
--type crl_cache_opts() :: [term()].
+-type crl_cache_opts() :: [any()].
-type handshake_size() :: integer().
-type hibernate_after() :: timeout().
-type root_fun() :: fun().
-type protocol_versions() :: [protocol_version()].
-type signature_algs() :: [{hash(), sign_algo()}].
-type signature_schemes() :: [sign_scheme()].
--type custom_user_lookup() :: {Lookupfun :: fun(), UserState :: term()}.
+-type custom_user_lookup() :: {Lookupfun :: fun(), UserState :: any()}.
-type padding_check() :: boolean().
-type beast_mitigation() :: one_n_minus_one | zero_n | disabled.
-type srp_identity() :: {Username :: string(), Password :: string()}.
@@ -375,7 +379,7 @@
-type honor_ecc_order() :: boolean().
-type client_renegotiation() :: boolean().
%% -------------------------------------------------------------------------------------------------------
--type prf_random() :: client_random | server_random.
+-type prf_random() :: client_random | server_random. % exported
-type protocol_extensions() :: #{renegotiation_info => binary(),
signature_algs => signature_algs(),
alpn => app_level_protocol(),
@@ -383,7 +387,7 @@
next_protocol => app_level_protocol(),
ec_point_formats => [0..2],
elliptic_curves => [public_key:oid()],
- sni => hostname()}.
+ sni => hostname()}. % exported
%% -------------------------------------------------------------------------------------------------------
%%%--------------------------------------------------------------------
@@ -419,14 +423,31 @@ stop() ->
%%
%% Description: Connect to an ssl server.
%%--------------------------------------------------------------------
--spec connect(host() | port(), [tls_client_option()]) -> {ok, #sslsocket{}} |
- {error, reason()}.
+
+-spec connect(TCPSocket, TLSOptions) ->
+ {ok, sslsocket()} |
+ {error, reason()} |
+ {option_not_a_key_value_tuple, any()} when
+ TCPSocket :: socket(),
+ TLSOptions :: [tls_client_option()].
+
connect(Socket, SslOptions) when is_port(Socket) ->
connect(Socket, SslOptions, infinity).
--spec connect(host() | port(), [tls_client_option()] | inet:port_number(),
- timeout() | list()) ->
- {ok, #sslsocket{}} | {error, reason()}.
+-spec connect(TCPSocket, TLSOptions, Timeout) ->
+ {ok, sslsocket()} | {error, reason()} when
+ TCPSocket :: socket(),
+ TLSOptions :: [tls_client_option()],
+ Timeout :: timeout();
+ (Host, Port, TLSOptions) ->
+ {ok, sslsocket()} |
+ {ok, sslsocket(),Ext :: protocol_extensions()} |
+ {error, reason()} |
+ {option_not_a_key_value_tuple, any()} when
+ Host :: host(),
+ Port :: inet:port_number(),
+ TLSOptions :: [tls_client_option()].
+
connect(Socket, SslOptions0, Timeout) when is_port(Socket),
(is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
CbInfo = handle_option(cb_info, SslOptions0, default_cb_info(tls)),
@@ -443,8 +464,16 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket),
connect(Host, Port, Options) ->
connect(Host, Port, Options, infinity).
--spec connect(host() | port(), inet:port_number(), list(), timeout()) ->
- {ok, #sslsocket{}} | {error, reason()}.
+
+-spec connect(Host, Port, TLSOptions, Timeout) ->
+ {ok, sslsocket()} |
+ {ok, sslsocket(),Ext :: protocol_extensions()} |
+ {error, reason()} |
+ {option_not_a_key_value_tuple, any()} when
+ Host :: host(),
+ Port :: inet:port_number(),
+ TLSOptions :: [tls_client_option()],
+ Timeout :: timeout().
connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
try
@@ -461,7 +490,10 @@ connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout
end.
%%--------------------------------------------------------------------
--spec listen(inet:port_number(), [tls_server_option()]) ->{ok, #sslsocket{}} | {error, reason()}.
+-spec listen(Port, Options) -> {ok, ListenSocket} | {error, reason()} when
+ Port::inet:port_number(),
+ Options::[tls_server_option()],
+ ListenSocket :: sslsocket().
%%
%% Description: Creates an ssl listen socket.
@@ -480,13 +512,20 @@ listen(Port, Options0) ->
%%
%% Description: Performs transport accept on an ssl listen socket
%%--------------------------------------------------------------------
--spec transport_accept(#sslsocket{}) -> {ok, #sslsocket{}} |
- {error, reason()}.
+-spec transport_accept(ListenSocket) -> {ok, SslSocket} |
+ {error, reason()} when
+ ListenSocket :: sslsocket(),
+ SslSocket :: sslsocket().
+
transport_accept(ListenSocket) ->
transport_accept(ListenSocket, infinity).
--spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} |
- {error, reason()}.
+-spec transport_accept(ListenSocket, Timeout) -> {ok, SslSocket} |
+ {error, reason()} when
+ ListenSocket :: sslsocket(),
+ Timeout :: timeout(),
+ SslSocket :: sslsocket().
+
transport_accept(#sslsocket{pid = {ListenSocket,
#config{connection_cb = ConnectionCb} = Config}}, Timeout)
when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
@@ -502,12 +541,22 @@ transport_accept(#sslsocket{pid = {ListenSocket,
%% Description: Performs accept on an ssl listen socket. e.i. performs
%% ssl handshake.
%%--------------------------------------------------------------------
--spec ssl_accept(#sslsocket{}) -> ok | {error, timeout | closed | {options, any()}| error_alert()}.
+-spec ssl_accept(SslSocket) ->
+ ok |
+ {error, Reason} when
+ SslSocket :: sslsocket(),
+ Reason :: closed | timeout | error_alert().
+
ssl_accept(ListenSocket) ->
ssl_accept(ListenSocket, [], infinity).
--spec ssl_accept(#sslsocket{} | port(), timeout()| [tls_server_option()]) ->
- ok | {ok, #sslsocket{}} | {error, timeout | closed | {options, any()}| error_alert()}.
+-spec ssl_accept(Socket, TimeoutOrOptions) ->
+ ok |
+ {ok, sslsocket()} | {error, Reason} when
+ Socket :: sslsocket() | socket(),
+ TimeoutOrOptions :: timeout() | [tls_server_option()],
+ Reason :: timeout | closed | {options, any()} | error_alert().
+
ssl_accept(Socket, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
ssl_accept(Socket, [], Timeout);
ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) ->
@@ -515,8 +564,13 @@ ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) ->
ssl_accept(Socket, Timeout) ->
ssl_accept(Socket, [], Timeout).
--spec ssl_accept(#sslsocket{} | port(), [tls_server_option()], timeout()) ->
- ok | {ok, #sslsocket{}} | {error, timeout | closed | {options, any()}| error_alert()}.
+-spec ssl_accept(Socket, Options, Timeout) ->
+ ok | {ok, sslsocket()} | {error, Reason} when
+ Socket :: sslsocket() | socket(),
+ Options :: [tls_server_option()],
+ Timeout :: timeout(),
+ Reason :: timeout | closed | {options, any()} | error_alert().
+
ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
handshake(Socket, SslOptions, Timeout);
ssl_accept(Socket, SslOptions, Timeout) ->
@@ -533,13 +587,28 @@ ssl_accept(Socket, SslOptions, Timeout) ->
%%--------------------------------------------------------------------
%% Performs the SSL/TLS/DTLS server-side handshake.
--spec handshake(#sslsocket{}) -> {ok, #sslsocket{}} | {error, timeout | closed | {options, any()} | error_alert()}.
+-spec handshake(HsSocket) -> {ok, SslSocket} | {ok, SslSocket, Ext} | {error, Reason} when
+ HsSocket :: sslsocket(),
+ SslSocket :: sslsocket(),
+ Ext :: protocol_extensions(),
+ Reason :: closed | timeout | error_alert().
handshake(ListenSocket) ->
handshake(ListenSocket, infinity).
--spec handshake(#sslsocket{} | port(), timeout()| [tls_server_option()]) ->
- {ok, #sslsocket{}} | {error, timeout | closed | {options, any()} | error_alert()}.
+-spec handshake(HsSocket, Timeout) -> {ok, SslSocket} | {ok, SslSocket, Ext} | {error, Reason} when
+ HsSocket :: sslsocket(),
+ Timeout :: timeout(),
+ SslSocket :: sslsocket(),
+ Ext :: protocol_extensions(),
+ Reason :: closed | timeout | error_alert();
+ (Socket, Options) -> {ok, SslSocket} | {ok, SslSocket, Ext} | {error, Reason} when
+ Socket :: socket() | sslsocket(),
+ SslSocket :: sslsocket(),
+ Options :: [server_option()],
+ Ext :: protocol_extensions(),
+ Reason :: closed | timeout | error_alert().
+
handshake(#sslsocket{} = Socket, Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or
(Timeout == infinity) ->
ssl_connection:handshake(Socket, Timeout);
@@ -553,8 +622,17 @@ handshake(#sslsocket{} = Socket, Timeout) when (is_integer(Timeout) andalso Tim
handshake(ListenSocket, SslOptions) when is_port(ListenSocket) ->
handshake(ListenSocket, SslOptions, infinity).
--spec handshake(#sslsocket{} | port(), [tls_server_option()], timeout()) ->
- {ok, #sslsocket{}} | {error, timeout | closed | {options, any()} | error_alert()}.
+-spec handshake(Socket, Options, Timeout) ->
+ {ok, SslSocket} |
+ {ok, SslSocket, Ext} |
+ {error, Reason} when
+ Socket :: socket() | sslsocket(),
+ SslSocket :: sslsocket(),
+ Options :: [server_option()],
+ Timeout :: timeout(),
+ Ext :: protocol_extensions(),
+ Reason :: closed | timeout | {options, any()} | error_alert().
+
handshake(#sslsocket{} = Socket, [], Timeout) when (is_integer(Timeout) andalso Timeout >= 0) or
(Timeout == infinity)->
handshake(Socket, Timeout);
@@ -597,8 +675,12 @@ handshake(Socket, SslOptions, Timeout) when is_port(Socket),
%%--------------------------------------------------------------------
--spec handshake_continue(#sslsocket{}, [tls_client_option() | tls_server_option()]) ->
- {ok, #sslsocket{}} | {error, reason()}.
+-spec handshake_continue(HsSocket, Options) ->
+ {ok, SslSocket} | {error, Reason} when
+ HsSocket :: sslsocket(),
+ Options :: [tls_client_option() | tls_server_option()],
+ SslSocket :: sslsocket(),
+ Reason :: closed | timeout | error_alert().
%%
%%
%% Description: Continues the handshke possible with newly supplied options.
@@ -606,8 +688,13 @@ handshake(Socket, SslOptions, Timeout) when is_port(Socket),
handshake_continue(Socket, SSLOptions) ->
handshake_continue(Socket, SSLOptions, infinity).
%%--------------------------------------------------------------------
--spec handshake_continue(#sslsocket{}, [tls_client_option() | tls_server_option()], timeout()) ->
- {ok, #sslsocket{}} | {error, reason()}.
+-spec handshake_continue(HsSocket, Options, Timeout) ->
+ {ok, SslSocket} | {error, Reason} when
+ HsSocket :: sslsocket(),
+ Options :: [tls_client_option() | tls_server_option()],
+ Timeout :: timeout(),
+ SslSocket :: sslsocket(),
+ Reason :: closed | timeout | error_alert().
%%
%%
%% Description: Continues the handshke possible with newly supplied options.
@@ -615,7 +702,7 @@ handshake_continue(Socket, SSLOptions) ->
handshake_continue(Socket, SSLOptions, Timeout) ->
ssl_connection:handshake_continue(Socket, SSLOptions, Timeout).
%%--------------------------------------------------------------------
--spec handshake_cancel(#sslsocket{}) -> term().
+-spec handshake_cancel(#sslsocket{}) -> any().
%%
%% Description: Cancels the handshakes sending a close alert.
%%--------------------------------------------------------------------
@@ -623,7 +710,9 @@ handshake_cancel(Socket) ->
ssl_connection:handshake_cancel(Socket).
%%--------------------------------------------------------------------
--spec close(#sslsocket{}) -> term().
+-spec close(SslSocket) -> ok | {error, Reason} when
+ SslSocket :: sslsocket(),
+ Reason :: any().
%%
%% Description: Close an ssl connection
%%--------------------------------------------------------------------
@@ -635,7 +724,10 @@ close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_,_,_,_}
Transport:close(ListenSocket).
%%--------------------------------------------------------------------
--spec close(#sslsocket{}, timeout() | {pid(), integer()}) -> term().
+-spec close(SslSocket, How) -> ok | {ok, port()} | {error,Reason} when
+ SslSocket :: sslsocket(),
+ How :: timeout() | {NewController::pid(), timeout()},
+ Reason :: any().
%%
%% Description: Close an ssl connection
%%--------------------------------------------------------------------
@@ -651,7 +743,9 @@ close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_,_,_,_}
Transport:close(ListenSocket).
%%--------------------------------------------------------------------
--spec send(#sslsocket{}, iodata()) -> ok | {error, reason()}.
+-spec send(SslSocket, Data) -> ok | {error, reason()} when
+ SslSocket :: sslsocket(),
+ Data :: iodata().
%%
%% Description: Sends data over the ssl connection
%%--------------------------------------------------------------------
@@ -671,11 +765,22 @@ send(#sslsocket{pid = {ListenSocket, #config{transport_info = Info}}}, Data) ->
%%
%% Description: Receives data when active = false
%%--------------------------------------------------------------------
--spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}.
+-spec recv(SslSocket, Length) -> {ok, Data} | {error, reason()} when
+ SslSocket :: sslsocket(),
+ Length :: integer(),
+ Data :: binary() | list() | HttpPacket,
+ HttpPacket :: any().
+
recv(Socket, Length) ->
recv(Socket, Length, infinity).
--spec recv(#sslsocket{}, integer(), timeout()) -> {ok, binary()| list()} | {error, reason()}.
+-spec recv(SslSocket, Length, Timeout) -> {ok, Data} | {error, reason()} when
+ SslSocket :: sslsocket(),
+ Length :: integer(),
+ Data :: binary() | list() | HttpPacket,
+ Timeout :: timeout(),
+ HttpPacket :: any().
+
recv(#sslsocket{pid = [Pid|_]}, Length, Timeout) when is_pid(Pid),
(is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity)->
ssl_connection:recv(Pid, Length, Timeout);
@@ -687,7 +792,10 @@ recv(#sslsocket{pid = {Listen,
Transport:recv(Listen, 0). %% {error,enotconn}
%%--------------------------------------------------------------------
--spec controlling_process(#sslsocket{}, pid()) -> ok | {error, reason()}.
+-spec controlling_process(SslSocket, NewOwner) -> ok | {error, Reason} when
+ SslSocket :: sslsocket(),
+ NewOwner :: pid(),
+ Reason :: any().
%%
%% Description: Changes process that receives the messages when active = true
%% or once.
@@ -706,7 +814,11 @@ controlling_process(#sslsocket{pid = {Listen,
%%--------------------------------------------------------------------
--spec connection_information(#sslsocket{}) -> {ok, list()} | {error, reason()}.
+-spec connection_information(SslSocket) -> {ok, Result} | {error, reason()} when
+ SslSocket :: sslsocket(),
+ Result :: [{OptionName, OptionValue}],
+ OptionName :: atom(),
+ OptionValue :: any().
%%
%% Description: Return SSL information for the connection
%%--------------------------------------------------------------------
@@ -723,7 +835,12 @@ connection_information(#sslsocket{pid = {dtls,_}}) ->
{error,enotconn}.
%%--------------------------------------------------------------------
--spec connection_information(#sslsocket{}, [atom()]) -> {ok, list()} | {error, reason()}.
+-spec connection_information(SslSocket, Items) -> {ok, Result} | {error, reason()} when
+ SslSocket :: sslsocket(),
+ Items :: [OptionName],
+ Result :: [{OptionName, OptionValue}],
+ OptionName :: atom(),
+ OptionValue :: any().
%%
%% Description: Return SSL information for the connection
%%--------------------------------------------------------------------
@@ -737,7 +854,11 @@ connection_information(#sslsocket{pid = [Pid|_]}, Items) when is_pid(Pid) ->
end.
%%--------------------------------------------------------------------
--spec peername(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}.
+-spec peername(SslSocket) -> {ok, {Address, Port}} |
+ {error, reason()} when
+ SslSocket :: sslsocket(),
+ Address :: inet:ip_address(),
+ Port :: inet:port_number().
%%
%% Description: same as inet:peername/1.
%%--------------------------------------------------------------------
@@ -753,7 +874,9 @@ peername(#sslsocket{pid = {dtls,_}}) ->
{error,enotconn}.
%%--------------------------------------------------------------------
--spec peercert(#sslsocket{}) ->{ok, DerCert::binary()} | {error, reason()}.
+-spec peercert(SslSocket) -> {ok, Cert} | {error, reason()} when
+ SslSocket :: sslsocket(),
+ Cert :: binary().
%%
%% Description: Returns the peercert.
%%--------------------------------------------------------------------
@@ -770,7 +893,10 @@ peercert(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
{error, enotconn}.
%%--------------------------------------------------------------------
--spec negotiated_protocol(#sslsocket{}) -> {ok, binary()} | {error, reason()}.
+-spec negotiated_protocol(SslSocket) -> {ok, Protocol} | {error, Reason} when
+ SslSocket :: sslsocket(),
+ Protocol :: binary(),
+ Reason :: protocol_not_negotiated.
%%
%% Description: Returns the protocol that has been negotiated. If no
%% protocol has been negotiated will return {error, protocol_not_negotiated}
@@ -784,8 +910,9 @@ negotiated_protocol(#sslsocket{pid = [Pid|_]}) when is_pid(Pid) ->
cipher_suites() ->
cipher_suites(erlang).
%%--------------------------------------------------------------------
--spec cipher_suites(erlang | openssl | all) ->
- [old_cipher_suite() | string()].
+-spec cipher_suites(Type) -> [old_cipher_suite() | string()] when
+ Type :: erlang | openssl | all.
+
%% Description: Returns all supported cipher suites.
%%--------------------------------------------------------------------
cipher_suites(erlang) ->
@@ -799,9 +926,10 @@ cipher_suites(all) ->
[ssl_cipher_format:suite_legacy(Suite) || Suite <- available_suites(all)].
%%--------------------------------------------------------------------
--spec cipher_suites(default | all | anonymous, ssl_record:ssl_version() |
- tls_record:tls_atom_version() | dtls_record:dtls_atom_version()) ->
- [erl_cipher_suite()].
+-spec cipher_suites(Supported, Version) -> ciphers() when
+ Supported :: default | all | anonymous,
+ Version :: protocol_version().
+
%% Description: Returns all default and all supported cipher suites for a
%% TLS/DTLS version
%%--------------------------------------------------------------------
@@ -817,9 +945,10 @@ cipher_suites(Base, Version) ->
[ssl_cipher_format:suite_bin_to_map(Suite) || Suite <- supported_suites(Base, Version)].
%%--------------------------------------------------------------------
--spec filter_cipher_suites([erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()] ,
- [{key_exchange | cipher | mac | prf, fun()}] | []) ->
- [erl_cipher_suite()] | [ssl_cipher_format:cipher_suite()].
+-spec filter_cipher_suites(Suites, Filters) -> Ciphers when
+ Suites :: ciphers(),
+ Filters :: cipher_filters(),
+ Ciphers :: ciphers().
%% Description: Removes cipher suites if any of the filter functions returns false
%% for any part of the cipher suite. This function also calls default filter functions
@@ -837,10 +966,10 @@ filter_cipher_suites(Suites, Filters0) ->
prf_filters => add_filter(proplists:get_value(prf, Filters0), PrfF)},
ssl_cipher:filter_suites(Suites, Filters).
%%--------------------------------------------------------------------
--spec prepend_cipher_suites([erl_cipher_suite()] |
- [{key_exchange | cipher | mac | prf, fun()}],
- [erl_cipher_suite()]) ->
- [erl_cipher_suite()].
+-spec prepend_cipher_suites(Preferred, Suites) -> ciphers() when
+ Preferred :: ciphers() | cipher_filters(),
+ Suites :: ciphers().
+
%% Description: Make <Preferred> suites become the most prefered
%% suites that is put them at the head of the cipher suite list
%% and remove them from <Suites> if present. <Preferred> may be a
@@ -855,10 +984,10 @@ prepend_cipher_suites(Filters, Suites) ->
Preferred = filter_cipher_suites(Suites, Filters),
Preferred ++ (Suites -- Preferred).
%%--------------------------------------------------------------------
--spec append_cipher_suites(Deferred :: [erl_cipher_suite()] |
- [{key_exchange | cipher | mac | prf, fun()}],
- [erl_cipher_suite()]) ->
- [erl_cipher_suite()].
+-spec append_cipher_suites(Deferred, Suites) -> ciphers() when
+ Deferred :: ciphers() | cipher_filters(),
+ Suites :: ciphers().
+
%% Description: Make <Deferred> suites suites become the
%% least prefered suites that is put them at the end of the cipher suite list
%% and removed them from <Suites> if present.
@@ -872,7 +1001,9 @@ append_cipher_suites(Filters, Suites) ->
(Suites -- Deferred) ++ Deferred.
%%--------------------------------------------------------------------
--spec eccs() -> tls_v1:curves().
+-spec eccs() -> NamedCurves when
+ NamedCurves :: [named_curve()].
+
%% Description: returns all supported curves across all versions
%%--------------------------------------------------------------------
eccs() ->
@@ -880,27 +1011,24 @@ eccs() ->
eccs_filter_supported(Curves).
%%--------------------------------------------------------------------
--spec eccs(tls_record:tls_atom_version() |
- ssl_record:ssl_version() | dtls_record:dtls_atom_version()) ->
- tls_v1:curves().
+-spec eccs(Version) -> NamedCurves when
+ Version :: protocol_version(),
+ NamedCurves :: [named_curve()].
+
%% Description: returns the curves supported for a given version of
%% ssl/tls.
%%--------------------------------------------------------------------
-eccs({3,0}) ->
+eccs(sslv3) ->
[];
-eccs({3,_}) ->
- Curves = tls_v1:ecc_curves(all),
- eccs_filter_supported(Curves);
-eccs({254,_} = Version) ->
- eccs(dtls_v1:corresponding_tls_version(Version));
+eccs('dtlsv1') ->
+ eccs('tlsv1.1');
+eccs('dtlsv1.2') ->
+ eccs('tlsv1.2');
eccs(Version) when Version == 'tlsv1.2';
Version == 'tlsv1.1';
- Version == tlsv1;
- Version == sslv3 ->
- eccs(tls_record:protocol_version(Version));
-eccs(Version) when Version == 'dtlsv1.2';
- Version == 'dtlsv1'->
- eccs(dtls_v1:corresponding_tls_version(dtls_record:protocol_version(Version))).
+ Version == tlsv1 ->
+ Curves = tls_v1:ecc_curves(all),
+ eccs_filter_supported(Curves).
eccs_filter_supported(Curves) ->
CryptoCurves = crypto:ec_curves(),
@@ -908,22 +1036,24 @@ eccs_filter_supported(Curves) ->
Curves).
%%--------------------------------------------------------------------
--spec groups() -> tls_v1:supported_groups().
+-spec groups() -> [group()].
%% Description: returns all supported groups (TLS 1.3 and later)
%%--------------------------------------------------------------------
groups() ->
tls_v1:groups(4).
%%--------------------------------------------------------------------
--spec groups(default) -> tls_v1:supported_groups().
+-spec groups(default) -> [group()].
%% Description: returns the default groups (TLS 1.3 and later)
%%--------------------------------------------------------------------
groups(default) ->
tls_v1:default_groups(4).
%%--------------------------------------------------------------------
--spec getopts(#sslsocket{}, [gen_tcp:option_name()]) ->
- {ok, [gen_tcp:option()]} | {error, reason()}.
+-spec getopts(SslSocket, OptionNames) ->
+ {ok, [gen_tcp:option()]} | {error, reason()} when
+ SslSocket :: sslsocket(),
+ OptionNames :: [gen_tcp:option_name()].
%%
%% Description: Gets options
%%--------------------------------------------------------------------
@@ -954,7 +1084,9 @@ getopts(#sslsocket{}, OptionTags) ->
{error, {options, {socket_options, OptionTags}}}.
%%--------------------------------------------------------------------
--spec setopts(#sslsocket{}, [gen_tcp:option()]) -> ok | {error, reason()}.
+-spec setopts(SslSocket, Options) -> ok | {error, reason()} when
+ SslSocket :: sslsocket(),
+ Options :: [gen_tcp:option()].
%%
%% Description: Sets options
%%--------------------------------------------------------------------
@@ -1010,9 +1142,9 @@ setopts(#sslsocket{}, Options) ->
{error, {options,{not_a_proplist, Options}}}.
%%---------------------------------------------------------------
--spec getstat(Socket) ->
- {ok, OptionValues} | {error, inet:posix()} when
- Socket :: #sslsocket{},
+-spec getstat(SslSocket) ->
+ {ok, OptionValues} | {error, inet:posix()} when
+ SslSocket :: sslsocket(),
OptionValues :: [{inet:stat_option(), integer()}].
%%
%% Description: Get all statistic options for a socket.
@@ -1021,9 +1153,9 @@ getstat(Socket) ->
getstat(Socket, inet:stats()).
%%---------------------------------------------------------------
--spec getstat(Socket, Options) ->
- {ok, OptionValues} | {error, inet:posix()} when
- Socket :: #sslsocket{},
+-spec getstat(SslSocket, Options) ->
+ {ok, OptionValues} | {error, inet:posix()} when
+ SslSocket :: sslsocket(),
Options :: [inet:stat_option()],
OptionValues :: [{inet:stat_option(), integer()}].
%%
@@ -1036,7 +1168,9 @@ getstat(#sslsocket{pid = [Pid|_], fd = {Transport, Socket, _, _}}, Options) when
tls_socket:getstat(Transport, Socket, Options).
%%---------------------------------------------------------------
--spec shutdown(#sslsocket{}, read | write | read_write) -> ok | {error, reason()}.
+-spec shutdown(SslSocket, How) -> ok | {error, reason()} when
+ SslSocket :: sslsocket(),
+ How :: read | write | read_write.
%%
%% Description: Same as gen_tcp:shutdown/2
%%--------------------------------------------------------------------
@@ -1050,7 +1184,11 @@ shutdown(#sslsocket{pid = [Pid|_]}, How) when is_pid(Pid) ->
ssl_connection:shutdown(Pid, How).
%%--------------------------------------------------------------------
--spec sockname(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}.
+-spec sockname(SslSocket) ->
+ {ok, {Address, Port}} | {error, reason()} when
+ SslSocket :: sslsocket(),
+ Address :: inet:ip_address(),
+ Port :: inet:port_number().
%%
%% Description: Same as inet:sockname/1
%%--------------------------------------------------------------------
@@ -1064,10 +1202,10 @@ sockname(#sslsocket{pid = [Pid| _], fd = {Transport, Socket,_,_}}) when is_pid(P
tls_socket:sockname(Transport, Socket).
%%---------------------------------------------------------------
--spec versions() -> [{ssl_app, string()} | {supported, [tls_record:tls_atom_version()]} |
- {supported_dtls, [dtls_record:dtls_atom_version()]} |
- {available, [tls_record:tls_atom_version()]} |
- {available_dtls, [dtls_record:dtls_atom_version()]}].
+-spec versions() -> [VersionInfo] when
+ VersionInfo :: {ssl_app, string()} |
+ {supported | available, [tls_version()]} |
+ {supported_dtls | available_dtls, [dtls_version()]}.
%%
%% Description: Returns a list of relevant versions.
%%--------------------------------------------------------------------
@@ -1085,7 +1223,8 @@ versions() ->
%%---------------------------------------------------------------
--spec renegotiate(#sslsocket{}) -> ok | {error, reason()}.
+-spec renegotiate(SslSocket) -> ok | {error, reason()} when
+ SslSocket :: sslsocket().
%%
%% Description: Initiates a renegotiation.
%%--------------------------------------------------------------------
@@ -1105,9 +1244,13 @@ renegotiate(#sslsocket{pid = {Listen,_}}) when is_port(Listen) ->
{error, enotconn}.
%%--------------------------------------------------------------------
--spec prf(#sslsocket{}, binary() | 'master_secret', binary(),
- [binary() | prf_random()], non_neg_integer()) ->
- {ok, binary()} | {error, reason()}.
+-spec prf(SslSocket, Secret, Label, Seed, WantedLength) ->
+ {ok, binary()} | {error, reason()} when
+ SslSocket :: sslsocket(),
+ Secret :: binary() | 'master_secret',
+ Label::binary(),
+ Seed :: [binary() | prf_random()],
+ WantedLength :: non_neg_integer().
%%
%% Description: use a ssl sessions TLS PRF to generate key material
%%--------------------------------------------------------------------
@@ -1128,7 +1271,8 @@ clear_pem_cache() ->
ssl_pem_cache:clear().
%%---------------------------------------------------------------
--spec format_error({error, term()}) -> list().
+-spec format_error({error, Reason}) -> string() when
+ Reason :: any().
%%
%% Description: Creates error string.
%%--------------------------------------------------------------------
@@ -1167,7 +1311,14 @@ tls_version({254, _} = Version) ->
dtls_v1:corresponding_tls_version(Version).
%%--------------------------------------------------------------------
--spec suite_to_str(erl_cipher_suite()) -> string().
+-spec suite_to_str(CipherSuite) -> string() when
+ CipherSuite :: erl_cipher_suite();
+ (CipherSuite) -> string() when
+ %% For internal use!
+ CipherSuite :: #{key_exchange := null,
+ cipher := null,
+ mac := null,
+ prf := null}.
%%
%% Description: Return the string representation of a cipher suite.
%%--------------------------------------------------------------------
diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl
index 9c5e2f80a9..0fa5f66c49 100644
--- a/lib/ssl/src/ssl_cipher.hrl
+++ b/lib/ssl/src/ssl_cipher.hrl
@@ -690,9 +690,9 @@
-define(TLS_CHACHA20_POLY1305_SHA256, <<?BYTE(16#13),?BYTE(16#03)>>).
%% %% TLS_AES_128_CCM_SHA256 = {0x13,0x04}
-%% -define(TLS_AES_128_CCM_SHA256, <<?BYTE(16#13), ?BYTE(16#04)>>).
+-define(TLS_AES_128_CCM_SHA256, <<?BYTE(16#13), ?BYTE(16#04)>>).
%% %% TLS_AES_128_CCM_8_SHA256 = {0x13,0x05}
-%% -define(TLS_AES_128_CCM_8_SHA256, <<?BYTE(16#13),?BYTE(16#05)>>).
+-define(TLS_AES_128_CCM_8_SHA256, <<?BYTE(16#13),?BYTE(16#05)>>).
-endif. % -ifdef(ssl_cipher).
diff --git a/lib/ssl/src/ssl_cipher_format.erl b/lib/ssl/src/ssl_cipher_format.erl
index 887eb6c653..577156a4b5 100644
--- a/lib/ssl/src/ssl_cipher_format.erl
+++ b/lib/ssl/src/ssl_cipher_format.erl
@@ -955,12 +955,12 @@ suite_bin_to_map(?TLS_CHACHA20_POLY1305_SHA256) ->
#{key_exchange => any,
cipher => chacha20_poly1305,
mac => aead,
- prf => sha256}.
-%% suite_bin_to_map(?TLS_AES_128_CCM_SHA256) ->
-%% #{key_exchange => any,
-%% cipher => aes_128_ccm,
-%% mac => aead
-%% prf => sha256};
+ prf => sha256};
+suite_bin_to_map(?TLS_AES_128_CCM_SHA256) ->
+ #{key_exchange => any,
+ cipher => aes_128_ccm,
+ mac => aead,
+ prf => sha256}.
%% suite_bin_to_map(?TLS_AES_128_CCM_8_SHA256) ->
%% #{key_exchange => any,
%% cipher => aes_128_ccm_8,
@@ -1690,12 +1690,12 @@ suite_map_to_bin(#{key_exchange := any,
cipher := chacha20_poly1305,
mac := aead,
prf := sha256}) ->
- ?TLS_CHACHA20_POLY1305_SHA256.
-%% suite_map_to_bin(#{key_exchange := any,
-%% cipher := aes_128_ccm,
-%% mac := aead,
-%% prf := sha256}) ->
-%% ?TLS_AES_128_CCM_SHA256;
+ ?TLS_CHACHA20_POLY1305_SHA256;
+suite_map_to_bin(#{key_exchange := any,
+ cipher := aes_128_ccm,
+ mac := aead,
+ prf := sha256}) ->
+ ?TLS_AES_128_CCM_SHA256.
%% suite_map_to_bin(#{key_exchange := any,
%% cipher := aes_128_ccm_8,
%% mac := aead,
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 6e325b504c..a5f754d2e3 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -115,7 +115,7 @@ handshake(Connection, Port, Socket, Opts, User, CbInfo, Timeout) ->
%%--------------------------------------------------------------------
-spec handshake(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} |
- {ok, #sslsocket{}, map()}| {error, reason()}.
+ {ok, #sslsocket{}, map()}| {error, reason()}.
%%
%% Description: Starts ssl handshake.
%%--------------------------------------------------------------------
@@ -130,8 +130,8 @@ handshake(#sslsocket{pid = [Pid|_]} = Socket, Timeout) ->
end.
%%--------------------------------------------------------------------
--spec handshake(#sslsocket{}, {#ssl_options{},#socket_options{}},
- timeout()) -> {ok, #sslsocket{}} | {error, reason()}.
+-spec handshake(#sslsocket{}, {#ssl_options{},#socket_options{}}, timeout()) ->
+ {ok, #sslsocket{}} | {ok, #sslsocket{}, map()} | {error, reason()}.
%%
%% Description: Starts ssl handshake with some new options
%%--------------------------------------------------------------------
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index dc037bb1ea..7b34991f4f 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -98,7 +98,7 @@ hello_request() ->
#hello_request{}.
%%--------------------------------------------------------------------
--spec server_hello(#session{}, ssl_record:ssl_version(), ssl_record:connection_states(),
+-spec server_hello(binary(), ssl_record:ssl_version(), ssl_record:connection_states(),
Extension::map()) -> #server_hello{}.
%%
%% Description: Creates a server hello message.
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 4ee0230d88..06c3ccae45 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -28,12 +28,12 @@
-define(VSN, "8.2.6").
-define(SECRET_PRINTOUT, "***").
--type reason() :: term().
--type reply() :: term().
--type msg() :: term().
--type from() :: term().
+-type reason() :: any().
+-type reply() :: any().
+-type msg() :: any().
+-type from() :: any().
-type certdb_ref() :: reference().
--type db_handle() :: term().
+-type db_handle() :: any().
-type der_cert() :: binary().
-type issuer() :: tuple().
-type serialnumber() :: integer().
@@ -109,26 +109,26 @@
-define('24H_in_sec', 86400).
-record(ssl_options, {
- protocol :: tls | dtls,
- versions :: [ssl_record:ssl_version()], %% ssl_record:atom_version() in API
- verify :: verify_none | verify_peer,
+ protocol :: tls | dtls | 'undefined',
+ versions :: [ssl_record:ssl_version()] | 'undefined', %% ssl_record:atom_version() in API
+ verify :: verify_none | verify_peer | 'undefined',
verify_fun, %%:: fun(CertVerifyErrors::term()) -> boolean(),
- partial_chain :: fun(),
- fail_if_no_peer_cert :: boolean(),
- verify_client_once :: boolean(),
+ partial_chain :: fun() | 'undefined',
+ fail_if_no_peer_cert :: boolean() | 'undefined',
+ verify_client_once :: boolean() | 'undefined',
%% fun(Extensions, State, Verify, AccError) -> {Extensions, State, AccError}
validate_extensions_fun,
- depth :: integer(),
- certfile :: binary(),
+ depth :: integer() | 'undefined',
+ certfile :: binary() | 'undefined',
cert :: public_key:der_encoded() | secret_printout() | 'undefined',
- keyfile :: binary(),
- key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo',
+ keyfile :: binary() | 'undefined',
+ key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo' | 'undefined',
public_key:der_encoded()} | map() %%map() -> ssl:key() how to handle dialyzer?
| secret_printout() | 'undefined',
password :: string() | secret_printout() | 'undefined',
cacerts :: [public_key:der_encoded()] | secret_printout() | 'undefined',
- cacertfile :: binary(),
- dh :: public_key:der_encoded() | secret_printout(),
+ cacertfile :: binary() | 'undefined',
+ dh :: public_key:der_encoded() | secret_printout() | 'undefined',
dhfile :: binary() | secret_printout() | 'undefined',
user_lookup_fun, % server option, fun to lookup the user
psk_identity :: binary() | secret_printout() | 'undefined',
@@ -140,23 +140,23 @@
reuse_session :: fun() | binary() | undefined, %% Server side is a fun()
%% If false sessions will never be reused, if true they
%% will be reused if possible.
- reuse_sessions :: boolean() | save, %% Only client side can use value save
+ reuse_sessions :: boolean() | save | 'undefined', %% Only client side can use value save
renegotiate_at,
secure_renegotiate,
client_renegotiation,
%% undefined if not hibernating, or number of ms of
%% inactivity after which ssl_connection will go into
%% hibernation
- hibernate_after :: timeout(),
+ hibernate_after :: timeout() | 'undefined',
%% This option should only be set to true by inet_tls_dist
erl_dist = false :: boolean(),
- alpn_advertised_protocols = undefined :: [binary()] | undefined ,
+ alpn_advertised_protocols = undefined :: [binary()] | undefined,
alpn_preferred_protocols = undefined :: [binary()] | undefined,
next_protocols_advertised = undefined :: [binary()] | undefined,
next_protocol_selector = undefined, %% fun([binary()]) -> binary())
log_level = notice :: atom(),
server_name_indication = undefined,
- sni_hosts :: [{inet:hostname(), [tuple()]}],
+ sni_hosts :: [{inet:hostname(), [tuple()]}] | 'undefined',
sni_fun :: function() | undefined,
%% Should the server prefer its own cipher order over the one provided by
%% the client?
@@ -166,14 +166,14 @@
%%mitigation entirely?
beast_mitigation = one_n_minus_one :: one_n_minus_one | zero_n | disabled,
fallback = false :: boolean(),
- crl_check :: boolean() | peer | best_effort,
+ crl_check :: boolean() | peer | best_effort | 'undefined',
crl_cache,
signature_algs,
signature_algs_cert,
eccs,
supported_groups, %% RFC 8422, RFC 8446
- honor_ecc_order :: boolean(),
- max_handshake_size :: integer(),
+ honor_ecc_order :: boolean() | 'undefined',
+ max_handshake_size :: integer() | 'undefined',
handshake,
customize_hostname_check
%% ,
@@ -199,9 +199,9 @@
}).
-type state_name() :: hello | abbreviated | certify | cipher | connection.
--type gen_fsm_state_return() :: {next_state, state_name(), term()} |
- {next_state, state_name(), term(), timeout()} |
- {stop, term(), term()}.
+-type gen_fsm_state_return() :: {next_state, state_name(), any()} |
+ {next_state, state_name(), any(), timeout()} |
+ {stop, any(), any()}.
-type ssl_options() :: #ssl_options{}.
-endif. % -ifdef(ssl_internal).
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index 74ab0d5923..2480e05097 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -251,7 +251,7 @@ encode_handshake(Package, Version) ->
%%--------------------------------------------------------------------
-spec get_tls_handshake(tls_record:tls_version(), binary(), binary() | iolist(),
#ssl_options{}) ->
- {[tls_handshake()], binary()}.
+ {[{tls_handshake(), binary()}], binary()}.
%%
%% Description: Given buffered and new data from ssl_record, collects
%% and returns it as a list of handshake messages, also returns leftover
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index 9f0c588cb6..a5c550a429 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -602,16 +602,18 @@ encode_fragments(_Type, _Version, _Data, CS, _CompS, _CipherS, _Seq, _CipherFrag
%% 1/n-1 splitting countermeasure Rizzo/Duong-Beast, RC4 chiphers are
%% not vulnerable to this attack.
-split_iovec([<<FirstByte:8, Rest/binary>>|Data], Version, BCA, one_n_minus_one)
+split_iovec(Data, Version, BCA, one_n_minus_one)
when (BCA =/= ?RC4) andalso ({3, 1} == Version orelse
{3, 0} == Version) ->
- [[FirstByte]|split_iovec([Rest|Data])];
+ {Part, RestData} = split_iovec(Data, 1, []),
+ [Part|split_iovec(RestData)];
%% 0/n splitting countermeasure for clients that are incompatible with 1/n-1
%% splitting.
split_iovec(Data, Version, BCA, zero_n)
when (BCA =/= ?RC4) andalso ({3, 1} == Version orelse
{3, 0} == Version) ->
- [<<>>|split_iovec(Data)];
+ {Part, RestData} = split_iovec(Data, 0, []),
+ [Part|split_iovec(RestData)];
split_iovec(Data, _Version, _BCA, _BeatMitigation) ->
split_iovec(Data).
@@ -621,16 +623,16 @@ split_iovec(Data) ->
{Part,Rest} = split_iovec(Data, ?MAX_PLAIN_TEXT_LENGTH, []),
[Part|split_iovec(Rest)].
%%
-split_iovec([Bin|Data], SplitSize, Acc) ->
+split_iovec([Bin|Data] = Bin_Data, SplitSize, Acc) ->
BinSize = byte_size(Bin),
if
+ BinSize =< SplitSize ->
+ split_iovec(Data, SplitSize - BinSize, [Bin|Acc]);
+ SplitSize == 0 ->
+ {lists:reverse(Acc), Bin_Data};
SplitSize < BinSize ->
{Last, Rest} = erlang:split_binary(Bin, SplitSize),
- {lists:reverse(Acc, [Last]), [Rest|Data]};
- BinSize < SplitSize ->
- split_iovec(Data, SplitSize - BinSize, [Bin|Acc]);
- true -> % Perfect match
- {lists:reverse(Acc, [Bin]), Data}
+ {lists:reverse(Acc, [Last]), [Rest|Data]}
end;
split_iovec([], _SplitSize, Acc) ->
{lists:reverse(Acc),[]}.
diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl
index f103f3218b..27cd5765e5 100644
--- a/lib/ssl/src/tls_v1.erl
+++ b/lib/ssl/src/tls_v1.erl
@@ -501,18 +501,18 @@ suites(3) ->
suites(4) ->
[?TLS_AES_256_GCM_SHA384,
?TLS_AES_128_GCM_SHA256,
- ?TLS_CHACHA20_POLY1305_SHA256
+ ?TLS_CHACHA20_POLY1305_SHA256,
+ ?TLS_AES_128_CCM_SHA256
%% Not supported
- %% ?TLS_AES_128_CCM_SHA256,
%% ?TLS_AES_128_CCM_8_SHA256
] ++ suites(3);
suites('TLS_v1.3') ->
[?TLS_AES_256_GCM_SHA384,
?TLS_AES_128_GCM_SHA256,
- ?TLS_CHACHA20_POLY1305_SHA256
+ ?TLS_CHACHA20_POLY1305_SHA256,
+ ?TLS_AES_128_CCM_SHA256
%% Not supported
- %% ?TLS_AES_128_CCM_SHA256,
%% ?TLS_AES_128_CCM_8_SHA256
].
diff --git a/lib/ssl/test/ssl_ECC_SUITE.erl b/lib/ssl/test/ssl_ECC_SUITE.erl
index ca8d0ec70c..c64358960c 100644
--- a/lib/ssl/test/ssl_ECC_SUITE.erl
+++ b/lib/ssl/test/ssl_ECC_SUITE.erl
@@ -212,7 +212,7 @@ client_ecdsa_server_ecdsa_with_raw_key(Config) when is_list(Config) ->
ecc_default_order(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))),
+ DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))),
{COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_ecdsa, ecdhe_ecdsa,
@@ -227,7 +227,7 @@ ecc_default_order(Config) ->
ecc_default_order_custom_curves(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))),
+ DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))),
{COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_ecdsa, ecdhe_ecdsa,
@@ -242,7 +242,7 @@ ecc_default_order_custom_curves(Config) ->
ecc_client_order(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))),
+ DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))),
{COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_ecdsa, ecdhe_ecdsa,
@@ -257,7 +257,7 @@ ecc_client_order(Config) ->
ecc_client_order_custom_curves(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))),
+ DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))),
{COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_ecdsa, ecdhe_ecdsa,
@@ -282,7 +282,7 @@ ecc_unknown_curve(Config) ->
client_ecdh_rsa_server_ecdhe_ecdsa_server_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))),
+ DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))),
{COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdh_rsa, ecdhe_ecdsa, Config),
@@ -296,7 +296,7 @@ client_ecdh_rsa_server_ecdhe_ecdsa_server_custom(Config) ->
client_ecdh_rsa_server_ecdhe_rsa_server_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))),
+ DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))),
{COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdh_rsa, ecdhe_rsa, Config),
@@ -311,7 +311,7 @@ client_ecdh_rsa_server_ecdhe_rsa_server_custom(Config) ->
client_ecdhe_rsa_server_ecdhe_ecdsa_server_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))),
+ DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))),
{COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_rsa, ecdhe_ecdsa, Config),
@@ -325,7 +325,7 @@ client_ecdhe_rsa_server_ecdhe_ecdsa_server_custom(Config) ->
client_ecdhe_rsa_server_ecdhe_rsa_server_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))),
+ DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))),
{COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_rsa, ecdhe_rsa, Config),
@@ -339,7 +339,7 @@ client_ecdhe_rsa_server_ecdhe_rsa_server_custom(Config) ->
end.
client_ecdhe_rsa_server_ecdh_rsa_server_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))),
+ DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))),
Ext = x509_test:extensions([{key_usage, [keyEncipherment]}]),
{COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, [[], [], [{extensions, Ext}]]},
{client_chain, Default}],
@@ -357,7 +357,7 @@ client_ecdhe_rsa_server_ecdh_rsa_server_custom(Config) ->
client_ecdhe_ecdsa_server_ecdhe_ecdsa_server_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))),
+ DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))),
{COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_ecdsa, ecdhe_ecdsa, Config),
@@ -371,7 +371,7 @@ client_ecdhe_ecdsa_server_ecdhe_ecdsa_server_custom(Config) ->
client_ecdhe_ecdsa_server_ecdhe_rsa_server_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))),
+ DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))),
{COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_ecdsa, ecdhe_rsa, Config),
@@ -385,7 +385,7 @@ client_ecdhe_ecdsa_server_ecdhe_rsa_server_custom(Config) ->
client_ecdhe_ecdsa_server_ecdhe_ecdsa_client_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))),
+ DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))),
{COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_ecdsa, ecdhe_ecdsa, Config),
@@ -399,7 +399,7 @@ client_ecdhe_ecdsa_server_ecdhe_ecdsa_client_custom(Config) ->
client_ecdhe_rsa_server_ecdhe_ecdsa_client_custom(Config) ->
Default = ssl_test_lib:default_cert_chain_conf(),
- DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(0))),
+ DefaultCurve = pubkey_cert_records:namedCurves(hd(tls_v1:ecc_curves(1))),
{COpts0, SOpts0} = ssl_test_lib:make_ec_cert_chains([{server_chain, Default},
{client_chain, Default}],
ecdhe_rsa, ecdhe_ecdsa, Config),
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 7b98209b31..20d9f28512 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -549,11 +549,10 @@ alerts(Config) when is_list(Config) ->
Alerts = [?ALERT_REC(?WARNING, ?CLOSE_NOTIFY) |
[?ALERT_REC(?FATAL, Desc) || Desc <- Descriptions]],
lists:foreach(fun(Alert) ->
- case ssl_alert:alert_txt(Alert) of
- Txt when is_list(Txt) ->
- ok;
- Other ->
- ct:fail({unexpected, Other})
+ try ssl_alert:alert_txt(Alert)
+ catch
+ C:E:T ->
+ ct:fail({unexpected, {C, E, T}})
end
end, Alerts).
%%--------------------------------------------------------------------
@@ -1833,14 +1832,12 @@ eccs() ->
eccs(Config) when is_list(Config) ->
[_|_] = All = ssl:eccs(),
- [] = SSL3 = ssl:eccs({3,0}),
- [_|_] = Tls = ssl:eccs({3,1}),
- [_|_] = Tls1 = ssl:eccs({3,2}),
- [_|_] = Tls2 = ssl:eccs({3,3}),
[] = SSL3 = ssl:eccs(sslv3),
[_|_] = Tls = ssl:eccs(tlsv1),
[_|_] = Tls1 = ssl:eccs('tlsv1.1'),
[_|_] = Tls2 = ssl:eccs('tlsv1.2'),
+ [_|_] = Tls1 = ssl:eccs('dtlsv1'),
+ [_|_] = Tls2 = ssl:eccs('dtlsv1.2'),
%% ordering is currently unverified by the test
true = lists:sort(All) =:= lists:usort(SSL3 ++ Tls ++ Tls1 ++ Tls2),
ok.
@@ -3655,7 +3652,7 @@ listen_socket(Config) ->
{error, enotconn} = ssl:peername(ListenSocket),
{error, enotconn} = ssl:peercert(ListenSocket),
{error, enotconn} = ssl:renegotiate(ListenSocket),
- {error, enotconn} = ssl:prf(ListenSocket, 'master_secret', <<"Label">>, client_random, 256),
+ {error, enotconn} = ssl:prf(ListenSocket, 'master_secret', <<"Label">>, [client_random], 256),
{error, enotconn} = ssl:shutdown(ListenSocket, read_write),
ok = ssl:close(ListenSocket).
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
index 4f340af4f5..55dee9a48f 100644
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
@@ -448,7 +448,7 @@ server_require_peer_cert_partial_chain_fun_fail(Config) when is_list(Config) ->
[{_,_,_}, {_, IntermidiateCA, _} | _] = public_key:pem_decode(ServerCAs),
PartialChain = fun(_CertChain) ->
- ture = false %% crash on purpose
+ true = false %% crash on purpose
end,
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 798bdf0416..65b8998cc3 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -1873,6 +1873,14 @@ check_sane_openssl_version(Version) ->
case {Version, os:cmd("openssl version")} of
{'sslv3', "OpenSSL 1.0.2" ++ _} ->
false;
+ {'dtlsv1', "OpenSSL 0" ++ _} ->
+ false;
+ {'dtlsv1.2', "OpenSSL 0" ++ _} ->
+ false;
+ {'dtlsv1.2', "OpenSSL 1.0.2" ++ _} ->
+ false;
+ {'dtlsv1', "OpenSSL 1.0.0" ++ _} ->
+ false;
{'dtlsv1', _} ->
not is_fips(openssl);
{'dtlsv1.2', _} ->
@@ -1885,18 +1893,10 @@ check_sane_openssl_version(Version) ->
false;
{'tlsv1.1', "OpenSSL 1.0.0" ++ _} ->
false;
- {'dtlsv1.2', "OpenSSL 1.0.2" ++ _} ->
- false;
- {'dtlsv1', "OpenSSL 1.0.0" ++ _} ->
- false;
{'tlsv1.2', "OpenSSL 0" ++ _} ->
false;
{'tlsv1.1', "OpenSSL 0" ++ _} ->
false;
- {'dtlsv1', "OpenSSL 0" ++ _} ->
- false;
- {'dtlsv1.2', "OpenSSL 0" ++ _} ->
- false;
{_, _} ->
true
end;
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index c4bcc1560c..98070f794c 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 9.2.1
+SSL_VSN = 9.2.2
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 4640b2b228..dd49288417 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -4461,21 +4461,24 @@ add_loop(T, I) ->
test_table_counter_concurrency(WhatToTest) ->
+ IntStatePrevOn =
+ erts_debug:set_internal_state(available_internal_state, true),
ItemsToAdd = 1000000,
SizeLoopSize = 1000,
T = ets:new(k, [public, ordered_set, {write_concurrency, true}]),
+ erts_debug:set_internal_state(ets_debug_random_split_join, {T, false}),
0 = ets:info(T, size),
P = self(),
SpawnedSizeProcs =
- [spawn(fun() ->
- size_loop(T, SizeLoopSize, 0, WhatToTest),
- P ! done
- end)
+ [spawn_link(fun() ->
+ size_loop(T, SizeLoopSize, 0, WhatToTest),
+ P ! done
+ end)
|| _ <- lists:seq(1, 6)],
- spawn(fun() ->
- add_loop(T, ItemsToAdd),
- P ! done_add
- end),
+ spawn_link(fun() ->
+ add_loop(T, ItemsToAdd),
+ P ! done_add
+ end),
[receive
done -> ok;
done_add -> ok
@@ -4487,6 +4490,7 @@ test_table_counter_concurrency(WhatToTest) ->
_ ->
ok
end,
+ erts_debug:set_internal_state(available_internal_state, IntStatePrevOn),
ok.
test_table_size_concurrency(Config) when is_list(Config) ->
diff --git a/lib/tools/test/Makefile b/lib/tools/test/Makefile
index 2b7b17afb3..7a0a941ccc 100644
--- a/lib/tools/test/Makefile
+++ b/lib/tools/test/Makefile
@@ -32,6 +32,7 @@ MODULES = \
make_SUITE \
tools_SUITE \
xref_SUITE \
+ prof_bench_SUITE \
ignore_cores
ERL_FILES= $(MODULES:%=%.erl)
@@ -41,7 +42,7 @@ INSTALL_PROGS= $(TARGET_FILES)
EMAKEFILE=Emakefile
-SPEC_FILES= tools.spec
+SPEC_FILES= tools.spec tools_bench.spec
COVER_FILE = tools.cover
# ----------------------------------------------------
diff --git a/lib/tools/test/emacs_SUITE.erl b/lib/tools/test/emacs_SUITE.erl
index 8756a4e9b3..73270e6ed6 100644
--- a/lib/tools/test/emacs_SUITE.erl
+++ b/lib/tools/test/emacs_SUITE.erl
@@ -119,7 +119,7 @@ compile_and_load(_Config) ->
false -> " "
end,
emacs([Pedantic,
- " -f batch-byte-compile ",filename:join(Dir, File)]),
+ " -f batch-byte-compile ", dquote(filename:join(Dir, File))]),
true
end,
lists:foreach(Compile, Files),
@@ -144,6 +144,10 @@ tests_compiled(_Config) ->
ok
end.
+
+dquote(Str) ->
+ "\"" ++ Str ++ "\"".
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
indent(Config) ->
@@ -207,14 +211,14 @@ emacs_version_ok(AcceptVer) ->
emacs(EmacsCmds) when is_list(EmacsCmds) ->
Cmd = ["emacs ",
"--batch --quick ",
- "--directory ", emacs_dir(), " ",
+ "--directory ", dquote(emacs_dir()), " ",
"--eval \"(require 'erlang-start)\" "
| EmacsCmds],
Res0 = os:cmd(Cmd ++ " ; echo $?"),
Rows = string:lexemes(Res0, ["\r\n", $\n]),
Res = lists:last(Rows),
Output = string:join(lists:droplast(Rows), "\n"),
- io:format("Cmd ~s:~n => ~s ~ts~n", [Cmd, Res, Output]),
+ io:format("Cmd ~ts:~n => ~s ~ts~n", [Cmd, Res, Output]),
"0" = Res,
Output.
diff --git a/lib/tools/test/instrument_SUITE.erl b/lib/tools/test/instrument_SUITE.erl
index 33259df58f..f474669836 100644
--- a/lib/tools/test/instrument_SUITE.erl
+++ b/lib/tools/test/instrument_SUITE.erl
@@ -260,13 +260,18 @@ test_format(Options0, Gather, Verify) ->
test_abort(Gather) ->
%% There's no way for us to tell whether this actually aborted or ran to
%% completion, but it might catch a few segfaults.
+ %% This testcase is mostly useful when run in an debug emulator as it needs
+ %% the modified reduction count to trigger the odd trap scenarios
Runner = self(),
Ref = make_ref(),
spawn_opt(fun() ->
- [Gather({Type, SchedId, 1, 1, Ref}) ||
- Type <- erlang:system_info(alloc_util_allocators),
- SchedId <- lists:seq(0, erlang:system_info(schedulers))],
- Runner ! Ref
+ [begin
+ Ref2 = make_ref(),
+ [Gather({Type, SchedId, 1, 1, Ref2}) ||
+ Type <- erlang:system_info(alloc_util_allocators),
+ SchedId <- lists:seq(0, erlang:system_info(schedulers))]
+ end || _ <- lists:seq(1,100)],
+ Runner ! Ref
end, [{priority, max}]),
receive
Ref -> ok
diff --git a/lib/tools/test/prof_bench_SUITE.erl b/lib/tools/test/prof_bench_SUITE.erl
new file mode 100644
index 0000000000..50d0ba9cd9
--- /dev/null
+++ b/lib/tools/test/prof_bench_SUITE.erl
@@ -0,0 +1,126 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-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(prof_bench_SUITE).
+
+-include_lib("common_test/include/ct_event.hrl").
+
+%% Test server framework exports
+-export([all/0, suite/0, init_per_suite/1, end_per_suite/1]).
+
+-export([overhead/1]).
+
+%%%---------------------------------------------------------------------
+%%% Test suites
+%%%---------------------------------------------------------------------
+
+
+suite() ->
+ [{timetrap,{minutes,10}}].
+
+all() ->
+ [overhead].
+
+init_per_suite(Config) ->
+ case {test_server:is_native(fprof_SUITE) or
+ (lists:any(fun(M) -> test_server:is_native(M) end, modules())) or
+ (whereis(cover_server) =/= undefined),
+ erlang:system_info(wordsize)}
+ of
+ {true, _} -> {skip, "Native or cover code"};
+ {_, 4} -> {skip, "Can't run on 32-bit as files will be large"};
+ {false, 8} -> Config
+ end.
+
+end_per_suite(Config) ->
+ LogFile = filename:join(proplists:get_value(priv_dir, Config), "fprof.trace"),
+ file:delete(LogFile),
+ ok.
+
+%%%---------------------------------------------------------------------
+
+%% ct:run_test([{suite, prof_bench_SUITE}]).
+overhead(Config) ->
+ LogFile = filename:join(proplists:get_value(priv_dir, Config), "fprof.trace"),
+ SofsCopy = filename:join(proplists:get_value(data_dir, Config), "sofs_copy.erl"),
+ TC = fun() -> compile:file(SofsCopy, [binary]) end,
+ _Warmup = timer:tc(TC),
+
+ {NormTime,{ok, sofs_copy, _}} = timer:tc(TC),
+ {FProfTime,{ok,sofs_copy,_}} = fprof:apply(timer, tc, [TC], [{file, LogFile}]),
+ ct:pal("FProf: ~p Norm: ~p Ratio: ~p",[FProfTime, NormTime, NormTime / FProfTime * 100]),
+ {ok,{EProfTime,{ok,sofs_copy,_}}} = eprof:profile([], timer, tc, [TC]),
+ ct:pal("EProf: ~p Norm: ~p Ratio: ~p",[EProfTime, NormTime, NormTime / EProfTime * 100]),
+ {CProfTime,{ok,sofs_copy,_}} = cprof_apply(timer, tc, [TC]),
+ ct:pal("CProf: ~p Norm: ~p Ratio: ~p",[CProfTime, NormTime, NormTime / CProfTime * 100]),
+ {CoverTime,{ok,sofs_copy,_}} = cover_apply(timer, tc, [TC]),
+ ct:pal("Cover: ~p Norm: ~p Ratio: ~p",[CoverTime, NormTime, NormTime / CoverTime * 100]),
+
+ ct_event:notify(#event{name = benchmark_data,
+ data = [{name, fprof_overhead},
+ {value, NormTime / FProfTime * 100}]}),
+ ct_event:notify(#event{name = benchmark_data,
+ data = [{name, eprof_overhead},
+ {value, NormTime / EProfTime * 100}]}),
+ ct_event:notify(#event{name = benchmark_data,
+ data = [{name, cprof_overhead},
+ {value, NormTime / CProfTime * 100}]}),
+ ct_event:notify(#event{name = benchmark_data,
+ data = [{name, cover_overhead},
+ {value, NormTime / CoverTime * 100}]}).
+
+%% overhead(Config) ->
+%% LogFile = filename:join(proplists:get_value(priv_dir, Config), "fprof.trace"),
+%% SofsCopy = filename:join(proplists:get_value(data_dir, Config), "sofs_copy.erl"),
+%% TC = fun() -> compile:file(SofsCopy, [binary]) end,
+%% _Warmup = timer:tc(TC),
+
+%% [{ok,{EProfTime,{ok,sofs_copy,_}}} = eprof:profile([], timer, tc, [TC])
+%% || _ <- lists:seq(1,10)],
+%% %% [fprof:apply(timer, tc, [TC], [{file, LogFile}]) || _ <- lists:seq(1,10)],
+%% {FProfTime,{ok,sofs_copy,_}} = fprof:apply(timer, tc, [TC], [{file, LogFile}]),
+%% {NormTime,{ok, sofs_copy, _}} = timer:tc(TC),
+
+ %% ct:pal("FProf: ~p Norm: ~p Ratio: ~p",[FProfTime, NormTime, FProfTime / NormTime]).
+
+cprof_apply(M, F, A) ->
+ cprof:start(),
+ Res = apply(M, F, A),
+ cprof:stop(),
+ Res.
+
+cover_apply(M, F, A) ->
+ cover:start(),
+ catch cover:local_only(),
+ Modules = modules(),
+ [code:unstick_mod(Mod) || Mod <- Modules],
+ cover:compile_beam(Modules),
+ [code:stick_mod(Mod) || Mod <- Modules],
+ Res = apply(M, F, A),
+ cover:stop(),
+ Res.
+
+modules() ->
+ application:load(compiler),
+ {ok, CompilerModules} = application:get_key(compiler, modules),
+ %% Only cover compile a subset of the stdlib modules
+ StdlibModules = [erl_parse, erl_expand_records, erl_lint, gb_trees, gb_sets, sofs,
+ beam_lib, dict, epp, erl_anno, erl_bits,
+ orddict, ordsets, sets, string, unicode, unicode_util],
+ CompilerModules ++ StdlibModules.
diff --git a/lib/tools/test/prof_bench_SUITE_data/sofs_copy.erl b/lib/tools/test/prof_bench_SUITE_data/sofs_copy.erl
new file mode 100644
index 0000000000..2a9b19177e
--- /dev/null
+++ b/lib/tools/test/prof_bench_SUITE_data/sofs_copy.erl
@@ -0,0 +1,2809 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-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(sofs_copy).
+
+-export([from_term/1, from_term/2, from_external/2, empty_set/0,
+ is_type/1, set/1, set/2, from_sets/1, relation/1, relation/2,
+ a_function/1, a_function/2, family/1, family/2,
+ to_external/1, type/1, to_sets/1, no_elements/1,
+ specification/2, union/2, intersection/2, difference/2,
+ symdiff/2, symmetric_partition/2, product/1, product/2,
+ constant_function/2, is_equal/2, is_subset/2, is_sofs_set/1,
+ is_set/1, is_empty_set/1, is_disjoint/2]).
+
+-export([union/1, intersection/1, canonical_relation/1]).
+
+-export([relation_to_family/1, domain/1, range/1, field/1,
+ relative_product/1, relative_product/2, relative_product1/2,
+ converse/1, image/2, inverse_image/2, strict_relation/1,
+ weak_relation/1, extension/3, is_a_function/1]).
+
+-export([composite/2, inverse/1]).
+
+-export([restriction/2, restriction/3, drestriction/2, drestriction/3,
+ substitution/2, projection/2, partition/1, partition/2,
+ partition/3, multiple_relative_product/2, join/4]).
+
+-export([family_to_relation/1, family_specification/2,
+ union_of_family/1, intersection_of_family/1,
+ family_union/1, family_intersection/1,
+ family_domain/1, family_range/1, family_field/1,
+ family_union/2, family_intersection/2, family_difference/2,
+ partition_family/2, family_projection/2]).
+
+-export([family_to_digraph/1, family_to_digraph/2,
+ digraph_to_family/1, digraph_to_family/2]).
+
+%% Shorter names of some functions.
+-export([fam2rel/1, rel2fam/1]).
+
+-import(lists,
+ [any/2, append/1, flatten/1, foreach/2,
+ keysort/2, last/1, map/2, mapfoldl/3, member/2, merge/2,
+ reverse/1, reverse/2, sort/1, umerge/1, umerge/2, usort/1]).
+
+-compile({inline, [{family_to_relation,1}, {relation_to_family,1}]}).
+
+-compile({inline, [{rel,2},{a_func,2},{fam,2},{term2set,2}]}).
+
+-compile({inline, [{external_fun,1},{element_type,1}]}).
+
+-compile({inline,
+ [{unify_types,2}, {match_types,2},
+ {test_rel,3}, {symdiff,3},
+ {subst,3}]}).
+
+-compile({inline, [{fam_binop,3}]}).
+
+%% Nope, no is_member, del_member or add_member.
+%%
+%% See also "Naive Set Theory" by Paul R. Halmos.
+%%
+%% By convention, erlang:error/1 is called from exported functions.
+
+-define(TAG, 'Set').
+-define(ORDTAG, 'OrdSet').
+
+-record(?TAG, {data = [] :: list(), type = type :: term()}).
+-record(?ORDTAG, {orddata = {} :: tuple() | atom(),
+ ordtype = type :: term()}).
+
+-define(LIST(S), (S)#?TAG.data).
+-define(TYPE(S), (S)#?TAG.type).
+-define(SET(L, T), #?TAG{data = L, type = T}).
+-define(IS_SET(S), is_record(S, ?TAG)).
+-define(IS_UNTYPED_SET(S), ?TYPE(S) =:= ?ANYTYPE).
+
+%% Ordered sets and atoms:
+-define(ORDDATA(S), (S)#?ORDTAG.orddata).
+-define(ORDTYPE(S), (S)#?ORDTAG.ordtype).
+-define(ORDSET(L, T), #?ORDTAG{orddata = L, ordtype = T}).
+-define(IS_ORDSET(S), is_record(S, ?ORDTAG)).
+-define(ATOM_TYPE, atom).
+-define(IS_ATOM_TYPE(T), is_atom(T)). % true for ?ANYTYPE...
+
+%% When IS_SET is true:
+-define(ANYTYPE, '_').
+-define(BINREL(X, Y), {X, Y}).
+-define(IS_RELATION(R), is_tuple(R)).
+-define(REL_ARITY(R), tuple_size(R)).
+-define(REL_TYPE(I, R), element(I, R)).
+-define(SET_OF(X), [X]).
+-define(IS_SET_OF(X), is_list(X)).
+-define(FAMILY(X, Y), ?BINREL(X, ?SET_OF(Y))).
+
+-export_type([anyset/0, binary_relation/0, external_set/0, a_function/0,
+ family/0, relation/0, set_of_sets/0, set_fun/0, spec_fun/0,
+ type/0]).
+-export_type([ordset/0, a_set/0]).
+
+-type(anyset() :: ordset() | a_set()).
+-type(binary_relation() :: relation()).
+-type(external_set() :: term()).
+-type(a_function() :: relation()).
+-type(family() :: a_function()).
+-opaque(ordset() :: #?ORDTAG{}).
+-type(relation() :: a_set()).
+-opaque(a_set() :: #?TAG{}).
+-type(set_of_sets() :: a_set()).
+-type(set_fun() :: pos_integer()
+ | {external, fun((external_set()) -> external_set())}
+ | fun((anyset()) -> anyset())).
+-type(spec_fun() :: {external, fun((external_set()) -> boolean())}
+ | fun((anyset()) -> boolean())).
+-type(type() :: term()).
+
+-type(tuple_of(_T) :: tuple()).
+
+%%
+%% Exported functions
+%%
+
+%%%
+%%% Create sets
+%%%
+
+-spec(from_term(Term) -> AnySet when
+ AnySet :: anyset(),
+ Term :: term()).
+from_term(T) ->
+ Type = case T of
+ _ when is_list(T) -> [?ANYTYPE];
+ _ -> ?ANYTYPE
+ end,
+ try setify(T, Type)
+ catch _:_ -> erlang:error(badarg)
+ end.
+
+-spec(from_term(Term, Type) -> AnySet when
+ AnySet :: anyset(),
+ Term :: term(),
+ Type :: type()).
+from_term(L, T) ->
+ case is_type(T) of
+ true ->
+ try setify(L, T)
+ catch _:_ -> erlang:error(badarg)
+ end;
+ false ->
+ erlang:error(badarg)
+ end.
+
+-spec(from_external(ExternalSet, Type) -> AnySet when
+ ExternalSet :: external_set(),
+ AnySet :: anyset(),
+ Type :: type()).
+from_external(L, ?SET_OF(Type)) ->
+ ?SET(L, Type);
+from_external(T, Type) ->
+ ?ORDSET(T, Type).
+
+-spec(empty_set() -> Set when
+ Set :: a_set()).
+empty_set() ->
+ ?SET([], ?ANYTYPE).
+
+-spec(is_type(Term) -> Bool when
+ Bool :: boolean(),
+ Term :: term()).
+is_type(Atom) when ?IS_ATOM_TYPE(Atom), Atom =/= ?ANYTYPE ->
+ true;
+is_type(?SET_OF(T)) ->
+ is_element_type(T);
+is_type(T) when tuple_size(T) > 0 ->
+ is_types(tuple_size(T), T);
+is_type(_T) ->
+ false.
+
+-spec(set(Terms) -> Set when
+ Set :: a_set(),
+ Terms :: [term()]).
+set(L) ->
+ try usort(L) of
+ SL -> ?SET(SL, ?ATOM_TYPE)
+ catch _:_ -> erlang:error(badarg)
+ end.
+
+-spec(set(Terms, Type) -> Set when
+ Set :: a_set(),
+ Terms :: [term()],
+ Type :: type()).
+set(L, ?SET_OF(Type)) when ?IS_ATOM_TYPE(Type), Type =/= ?ANYTYPE ->
+ try usort(L) of
+ SL -> ?SET(SL, Type)
+ catch _:_ -> erlang:error(badarg)
+ end;
+set(L, ?SET_OF(_) = T) ->
+ try setify(L, T)
+ catch _:_ -> erlang:error(badarg)
+ end;
+set(_, _) ->
+ erlang:error(badarg).
+
+-spec(from_sets(ListOfSets) -> Set when
+ Set :: a_set(),
+ ListOfSets :: [anyset()];
+ (TupleOfSets) -> Ordset when
+ Ordset :: ordset(),
+ TupleOfSets :: tuple_of(anyset())).
+from_sets(Ss) when is_list(Ss) ->
+ case set_of_sets(Ss, [], ?ANYTYPE) of
+ {error, Error} ->
+ erlang:error(Error);
+ Set ->
+ Set
+ end;
+from_sets(Tuple) when is_tuple(Tuple) ->
+ case ordset_of_sets(tuple_to_list(Tuple), [], []) of
+ error ->
+ erlang:error(badarg);
+ Set ->
+ Set
+ end;
+from_sets(_) ->
+ erlang:error(badarg).
+
+-spec(relation(Tuples) -> Relation when
+ Relation :: relation(),
+ Tuples :: [tuple()]).
+relation([]) ->
+ ?SET([], ?BINREL(?ATOM_TYPE, ?ATOM_TYPE));
+relation(Ts = [T | _]) when is_tuple(T) ->
+ try rel(Ts, tuple_size(T))
+ catch _:_ -> erlang:error(badarg)
+ end;
+relation(_) ->
+ erlang:error(badarg).
+
+-spec(relation(Tuples, Type) -> Relation when
+ N :: integer(),
+ Type :: N | type(),
+ Relation :: relation(),
+ Tuples :: [tuple()]).
+relation(Ts, TS) ->
+ try rel(Ts, TS)
+ catch _:_ -> erlang:error(badarg)
+ end.
+
+-spec(a_function(Tuples) -> Function when
+ Function :: a_function(),
+ Tuples :: [tuple()]).
+a_function(Ts) ->
+ try func(Ts, ?BINREL(?ATOM_TYPE, ?ATOM_TYPE)) of
+ Bad when is_atom(Bad) ->
+ erlang:error(Bad);
+ Set ->
+ Set
+ catch _:_ -> erlang:error(badarg)
+ end.
+
+-spec(a_function(Tuples, Type) -> Function when
+ Function :: a_function(),
+ Tuples :: [tuple()],
+ Type :: type()).
+a_function(Ts, T) ->
+ try a_func(Ts, T) of
+ Bad when is_atom(Bad) ->
+ erlang:error(Bad);
+ Set ->
+ Set
+ catch _:_ -> erlang:error(badarg)
+ end.
+
+-spec(family(Tuples) -> Family when
+ Family :: family(),
+ Tuples :: [tuple()]).
+family(Ts) ->
+ try fam2(Ts, ?FAMILY(?ATOM_TYPE, ?ATOM_TYPE)) of
+ Bad when is_atom(Bad) ->
+ erlang:error(Bad);
+ Set ->
+ Set
+ catch _:_ -> erlang:error(badarg)
+ end.
+
+-spec(family(Tuples, Type) -> Family when
+ Family :: family(),
+ Tuples :: [tuple()],
+ Type :: type()).
+family(Ts, T) ->
+ try fam(Ts, T) of
+ Bad when is_atom(Bad) ->
+ erlang:error(Bad);
+ Set ->
+ Set
+ catch _:_ -> erlang:error(badarg)
+ end.
+
+%%%
+%%% Functions on sets.
+%%%
+
+-spec(to_external(AnySet) -> ExternalSet when
+ ExternalSet :: external_set(),
+ AnySet :: anyset()).
+to_external(S) when ?IS_SET(S) ->
+ ?LIST(S);
+to_external(S) when ?IS_ORDSET(S) ->
+ ?ORDDATA(S).
+
+-spec(type(AnySet) -> Type when
+ AnySet :: anyset(),
+ Type :: type()).
+type(S) when ?IS_SET(S) ->
+ ?SET_OF(?TYPE(S));
+type(S) when ?IS_ORDSET(S) ->
+ ?ORDTYPE(S).
+
+-spec(to_sets(ASet) -> Sets when
+ ASet :: a_set() | ordset(),
+ Sets :: tuple_of(AnySet) | [AnySet],
+ AnySet :: anyset()).
+to_sets(S) when ?IS_SET(S) ->
+ case ?TYPE(S) of
+ ?SET_OF(Type) -> list_of_sets(?LIST(S), Type, []);
+ Type -> list_of_ordsets(?LIST(S), Type, [])
+ end;
+to_sets(S) when ?IS_ORDSET(S), is_tuple(?ORDTYPE(S)) ->
+ tuple_of_sets(tuple_to_list(?ORDDATA(S)), tuple_to_list(?ORDTYPE(S)), []);
+to_sets(S) when ?IS_ORDSET(S) ->
+ erlang:error(badarg).
+
+-spec(no_elements(ASet) -> NoElements when
+ ASet :: a_set() | ordset(),
+ NoElements :: non_neg_integer()).
+no_elements(S) when ?IS_SET(S) ->
+ length(?LIST(S));
+no_elements(S) when ?IS_ORDSET(S), is_tuple(?ORDTYPE(S)) ->
+ tuple_size(?ORDDATA(S));
+no_elements(S) when ?IS_ORDSET(S) ->
+ erlang:error(badarg).
+
+-spec(specification(Fun, Set1) -> Set2 when
+ Fun :: spec_fun(),
+ Set1 :: a_set(),
+ Set2 :: a_set()).
+specification(Fun, S) when ?IS_SET(S) ->
+ Type = ?TYPE(S),
+ R = case external_fun(Fun) of
+ false ->
+ spec(?LIST(S), Fun, element_type(Type), []);
+ XFun ->
+ specification(?LIST(S), XFun, [])
+ end,
+ case R of
+ SL when is_list(SL) ->
+ ?SET(SL, Type);
+ Bad ->
+ erlang:error(Bad)
+ end.
+
+-spec(union(Set1, Set2) -> Set3 when
+ Set1 :: a_set(),
+ Set2 :: a_set(),
+ Set3 :: a_set()).
+union(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) ->
+ case unify_types(?TYPE(S1), ?TYPE(S2)) of
+ [] -> erlang:error(type_mismatch);
+ Type -> ?SET(umerge(?LIST(S1), ?LIST(S2)), Type)
+ end.
+
+-spec(intersection(Set1, Set2) -> Set3 when
+ Set1 :: a_set(),
+ Set2 :: a_set(),
+ Set3 :: a_set()).
+intersection(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) ->
+ case unify_types(?TYPE(S1), ?TYPE(S2)) of
+ [] -> erlang:error(type_mismatch);
+ Type -> ?SET(intersection(?LIST(S1), ?LIST(S2), []), Type)
+ end.
+
+-spec(difference(Set1, Set2) -> Set3 when
+ Set1 :: a_set(),
+ Set2 :: a_set(),
+ Set3 :: a_set()).
+difference(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) ->
+ case unify_types(?TYPE(S1), ?TYPE(S2)) of
+ [] -> erlang:error(type_mismatch);
+ Type -> ?SET(difference(?LIST(S1), ?LIST(S2), []), Type)
+ end.
+
+-spec(symdiff(Set1, Set2) -> Set3 when
+ Set1 :: a_set(),
+ Set2 :: a_set(),
+ Set3 :: a_set()).
+symdiff(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) ->
+ case unify_types(?TYPE(S1), ?TYPE(S2)) of
+ [] -> erlang:error(type_mismatch);
+ Type -> ?SET(symdiff(?LIST(S1), ?LIST(S2), []), Type)
+ end.
+
+-spec(symmetric_partition(Set1, Set2) -> {Set3, Set4, Set5} when
+ Set1 :: a_set(),
+ Set2 :: a_set(),
+ Set3 :: a_set(),
+ Set4 :: a_set(),
+ Set5 :: a_set()).
+symmetric_partition(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) ->
+ case unify_types(?TYPE(S1), ?TYPE(S2)) of
+ [] -> erlang:error(type_mismatch);
+ Type -> sympart(?LIST(S1), ?LIST(S2), [], [], [], Type)
+ end.
+
+-spec(product(Set1, Set2) -> BinRel when
+ BinRel :: binary_relation(),
+ Set1 :: a_set(),
+ Set2 :: a_set()).
+product(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) ->
+ if
+ ?TYPE(S1) =:= ?ANYTYPE -> S1;
+ ?TYPE(S2) =:= ?ANYTYPE -> S2;
+ true ->
+ F = fun(E) -> {0, E} end,
+ T = ?BINREL(?TYPE(S1), ?TYPE(S2)),
+ ?SET(relprod(map(F, ?LIST(S1)), map(F, ?LIST(S2))), T)
+ end.
+
+-spec(product(TupleOfSets) -> Relation when
+ Relation :: relation(),
+ TupleOfSets :: tuple_of(a_set())).
+product({S1, S2}) ->
+ product(S1, S2);
+product(T) when is_tuple(T) ->
+ Ss = tuple_to_list(T),
+ try sets_to_list(Ss) of
+ [] ->
+ erlang:error(badarg);
+ L ->
+ Type = types(Ss, []),
+ case member([], L) of
+ true ->
+ empty_set();
+ false ->
+ ?SET(reverse(prod(L, [], [])), Type)
+ end
+ catch _:_ -> erlang:error(badarg)
+ end.
+
+-spec(constant_function(Set, AnySet) -> Function when
+ AnySet :: anyset(),
+ Function :: a_function(),
+ Set :: a_set()).
+constant_function(S, E) when ?IS_SET(S) ->
+ case {?TYPE(S), is_sofs_set(E)} of
+ {?ANYTYPE, true} -> S;
+ {Type, true} ->
+ NType = ?BINREL(Type, type(E)),
+ ?SET(constant_function(?LIST(S), to_external(E), []), NType);
+ _ -> erlang:error(badarg)
+ end;
+constant_function(S, _) when ?IS_ORDSET(S) ->
+ erlang:error(badarg).
+
+-spec(is_equal(AnySet1, AnySet2) -> Bool when
+ AnySet1 :: anyset(),
+ AnySet2 :: anyset(),
+ Bool :: boolean()).
+is_equal(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) ->
+ case match_types(?TYPE(S1), ?TYPE(S2)) of
+ true -> ?LIST(S1) == ?LIST(S2);
+ false -> erlang:error(type_mismatch)
+ end;
+is_equal(S1, S2) when ?IS_ORDSET(S1), ?IS_ORDSET(S2) ->
+ case match_types(?ORDTYPE(S1), ?ORDTYPE(S2)) of
+ true -> ?ORDDATA(S1) == ?ORDDATA(S2);
+ false -> erlang:error(type_mismatch)
+ end;
+is_equal(S1, S2) when ?IS_SET(S1), ?IS_ORDSET(S2) ->
+ erlang:error(type_mismatch);
+is_equal(S1, S2) when ?IS_ORDSET(S1), ?IS_SET(S2) ->
+ erlang:error(type_mismatch).
+
+-spec(is_subset(Set1, Set2) -> Bool when
+ Bool :: boolean(),
+ Set1 :: a_set(),
+ Set2 :: a_set()).
+is_subset(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) ->
+ case match_types(?TYPE(S1), ?TYPE(S2)) of
+ true -> subset(?LIST(S1), ?LIST(S2));
+ false -> erlang:error(type_mismatch)
+ end.
+
+-spec(is_sofs_set(Term) -> Bool when
+ Bool :: boolean(),
+ Term :: term()).
+is_sofs_set(S) when ?IS_SET(S) ->
+ true;
+is_sofs_set(S) when ?IS_ORDSET(S) ->
+ true;
+is_sofs_set(_S) ->
+ false.
+
+-spec(is_set(AnySet) -> Bool when
+ AnySet :: anyset(),
+ Bool :: boolean()).
+is_set(S) when ?IS_SET(S) ->
+ true;
+is_set(S) when ?IS_ORDSET(S) ->
+ false.
+
+-spec(is_empty_set(AnySet) -> Bool when
+ AnySet :: anyset(),
+ Bool :: boolean()).
+is_empty_set(S) when ?IS_SET(S) ->
+ ?LIST(S) =:= [];
+is_empty_set(S) when ?IS_ORDSET(S) ->
+ false.
+
+-spec(is_disjoint(Set1, Set2) -> Bool when
+ Bool :: boolean(),
+ Set1 :: a_set(),
+ Set2 :: a_set()).
+is_disjoint(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) ->
+ case match_types(?TYPE(S1), ?TYPE(S2)) of
+ true ->
+ case ?LIST(S1) of
+ [] -> true;
+ [A | As] -> disjoint(?LIST(S2), A, As)
+ end;
+ false -> erlang:error(type_mismatch)
+ end.
+
+%%%
+%%% Functions on set-of-sets.
+%%%
+
+-spec(union(SetOfSets) -> Set when
+ Set :: a_set(),
+ SetOfSets :: set_of_sets()).
+union(Sets) when ?IS_SET(Sets) ->
+ case ?TYPE(Sets) of
+ ?SET_OF(Type) -> ?SET(lunion(?LIST(Sets)), Type);
+ ?ANYTYPE -> Sets;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(intersection(SetOfSets) -> Set when
+ Set :: a_set(),
+ SetOfSets :: set_of_sets()).
+intersection(Sets) when ?IS_SET(Sets) ->
+ case ?LIST(Sets) of
+ [] -> erlang:error(badarg);
+ [L | Ls] ->
+ case ?TYPE(Sets) of
+ ?SET_OF(Type) ->
+ ?SET(lintersection(Ls, L), Type);
+ _ -> erlang:error(badarg)
+ end
+ end.
+
+-spec(canonical_relation(SetOfSets) -> BinRel when
+ BinRel :: binary_relation(),
+ SetOfSets :: set_of_sets()).
+canonical_relation(Sets) when ?IS_SET(Sets) ->
+ ST = ?TYPE(Sets),
+ case ST of
+ ?SET_OF(?ANYTYPE) -> empty_set();
+ ?SET_OF(Type) ->
+ ?SET(can_rel(?LIST(Sets), []), ?BINREL(Type, ST));
+ ?ANYTYPE -> Sets;
+ _ -> erlang:error(badarg)
+ end.
+
+%%%
+%%% Functions on binary relations only.
+%%%
+
+-spec(rel2fam(BinRel) -> Family when
+ Family :: family(),
+ BinRel :: binary_relation()).
+rel2fam(R) ->
+ relation_to_family(R).
+
+-spec(relation_to_family(BinRel) -> Family when
+ Family :: family(),
+ BinRel :: binary_relation()).
+%% Inlined.
+relation_to_family(R) when ?IS_SET(R) ->
+ case ?TYPE(R) of
+ ?BINREL(DT, RT) ->
+ ?SET(rel2family(?LIST(R)), ?FAMILY(DT, RT));
+ ?ANYTYPE -> R;
+ _Else -> erlang:error(badarg)
+ end.
+
+-spec(domain(BinRel) -> Set when
+ BinRel :: binary_relation(),
+ Set :: a_set()).
+domain(R) when ?IS_SET(R) ->
+ case ?TYPE(R) of
+ ?BINREL(DT, _) -> ?SET(dom(?LIST(R)), DT);
+ ?ANYTYPE -> R;
+ _Else -> erlang:error(badarg)
+ end.
+
+-spec(range(BinRel) -> Set when
+ BinRel :: binary_relation(),
+ Set :: a_set()).
+range(R) when ?IS_SET(R) ->
+ case ?TYPE(R) of
+ ?BINREL(_, RT) -> ?SET(ran(?LIST(R), []), RT);
+ ?ANYTYPE -> R;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(field(BinRel) -> Set when
+ BinRel :: binary_relation(),
+ Set :: a_set()).
+%% In "Introduction to LOGIC", Suppes defines the field of a binary
+%% relation to be the union of the domain and the range (or
+%% counterdomain).
+field(R) ->
+ union(domain(R), range(R)).
+
+-spec(relative_product(ListOfBinRels) -> BinRel2 when
+ ListOfBinRels :: [BinRel, ...],
+ BinRel :: binary_relation(),
+ BinRel2 :: binary_relation()).
+%% The following clause is kept for backward compatibility.
+%% The list is due to Dialyzer's specs.
+relative_product(RT) when is_tuple(RT) ->
+ relative_product(tuple_to_list(RT));
+relative_product(RL) when is_list(RL) ->
+ case relprod_n(RL, foo, false, false) of
+ {error, Reason} ->
+ erlang:error(Reason);
+ Reply ->
+ Reply
+ end.
+
+-spec(relative_product(ListOfBinRels, BinRel1) -> BinRel2 when
+ ListOfBinRels :: [BinRel, ...],
+ BinRel :: binary_relation(),
+ BinRel1 :: binary_relation(),
+ BinRel2 :: binary_relation();
+ (BinRel1, BinRel2) -> BinRel3 when
+ BinRel1 :: binary_relation(),
+ BinRel2 :: binary_relation(),
+ BinRel3 :: binary_relation()).
+relative_product(R1, R2) when ?IS_SET(R1), ?IS_SET(R2) ->
+ relative_product1(converse(R1), R2);
+%% The following clause is kept for backward compatibility.
+%% The list is due to Dialyzer's specs.
+relative_product(RT, R) when is_tuple(RT), ?IS_SET(R) ->
+ relative_product(tuple_to_list(RT), R);
+relative_product(RL, R) when is_list(RL), ?IS_SET(R) ->
+ EmptyR = case ?TYPE(R) of
+ ?BINREL(_, _) -> ?LIST(R) =:= [];
+ ?ANYTYPE -> true;
+ _ -> erlang:error(badarg)
+ end,
+ case relprod_n(RL, R, EmptyR, true) of
+ {error, Reason} ->
+ erlang:error(Reason);
+ Reply ->
+ Reply
+ end.
+
+-spec(relative_product1(BinRel1, BinRel2) -> BinRel3 when
+ BinRel1 :: binary_relation(),
+ BinRel2 :: binary_relation(),
+ BinRel3 :: binary_relation()).
+relative_product1(R1, R2) when ?IS_SET(R1), ?IS_SET(R2) ->
+ {DTR1, RTR1} = case ?TYPE(R1) of
+ ?BINREL(_, _) = R1T -> R1T;
+ ?ANYTYPE -> {?ANYTYPE, ?ANYTYPE};
+ _ -> erlang:error(badarg)
+ end,
+ {DTR2, RTR2} = case ?TYPE(R2) of
+ ?BINREL(_, _) = R2T -> R2T;
+ ?ANYTYPE -> {?ANYTYPE, ?ANYTYPE};
+ _ -> erlang:error(badarg)
+ end,
+ case match_types(DTR1, DTR2) of
+ true when DTR1 =:= ?ANYTYPE -> R1;
+ true when DTR2 =:= ?ANYTYPE -> R2;
+ true -> ?SET(relprod(?LIST(R1), ?LIST(R2)), ?BINREL(RTR1, RTR2));
+ false -> erlang:error(type_mismatch)
+ end.
+
+-spec(converse(BinRel1) -> BinRel2 when
+ BinRel1 :: binary_relation(),
+ BinRel2 :: binary_relation()).
+converse(R) when ?IS_SET(R) ->
+ case ?TYPE(R) of
+ ?BINREL(DT, RT) -> ?SET(converse(?LIST(R), []), ?BINREL(RT, DT));
+ ?ANYTYPE -> R;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(image(BinRel, Set1) -> Set2 when
+ BinRel :: binary_relation(),
+ Set1 :: a_set(),
+ Set2 :: a_set()).
+image(R, S) when ?IS_SET(R), ?IS_SET(S) ->
+ case ?TYPE(R) of
+ ?BINREL(DT, RT) ->
+ case match_types(DT, ?TYPE(S)) of
+ true ->
+ ?SET(usort(restrict(?LIST(S), ?LIST(R))), RT);
+ false ->
+ erlang:error(type_mismatch)
+ end;
+ ?ANYTYPE -> R;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(inverse_image(BinRel, Set1) -> Set2 when
+ BinRel :: binary_relation(),
+ Set1 :: a_set(),
+ Set2 :: a_set()).
+inverse_image(R, S) when ?IS_SET(R), ?IS_SET(S) ->
+ case ?TYPE(R) of
+ ?BINREL(DT, RT) ->
+ case match_types(RT, ?TYPE(S)) of
+ true ->
+ NL = restrict(?LIST(S), converse(?LIST(R), [])),
+ ?SET(usort(NL), DT);
+ false ->
+ erlang:error(type_mismatch)
+ end;
+ ?ANYTYPE -> R;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(strict_relation(BinRel1) -> BinRel2 when
+ BinRel1 :: binary_relation(),
+ BinRel2 :: binary_relation()).
+strict_relation(R) when ?IS_SET(R) ->
+ case ?TYPE(R) of
+ Type = ?BINREL(_, _) ->
+ ?SET(strict(?LIST(R), []), Type);
+ ?ANYTYPE -> R;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(weak_relation(BinRel1) -> BinRel2 when
+ BinRel1 :: binary_relation(),
+ BinRel2 :: binary_relation()).
+weak_relation(R) when ?IS_SET(R) ->
+ case ?TYPE(R) of
+ ?BINREL(DT, RT) ->
+ case unify_types(DT, RT) of
+ [] ->
+ erlang:error(badarg);
+ Type ->
+ ?SET(weak(?LIST(R)), ?BINREL(Type, Type))
+ end;
+ ?ANYTYPE -> R;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(extension(BinRel1, Set, AnySet) -> BinRel2 when
+ AnySet :: anyset(),
+ BinRel1 :: binary_relation(),
+ BinRel2 :: binary_relation(),
+ Set :: a_set()).
+extension(R, S, E) when ?IS_SET(R), ?IS_SET(S) ->
+ case {?TYPE(R), ?TYPE(S), is_sofs_set(E)} of
+ {T=?BINREL(DT, RT), ST, true} ->
+ case match_types(DT, ST) and match_types(RT, type(E)) of
+ false ->
+ erlang:error(type_mismatch);
+ true ->
+ RL = ?LIST(R),
+ case extc([], ?LIST(S), to_external(E), RL) of
+ [] ->
+ R;
+ L ->
+ ?SET(merge(RL, reverse(L)), T)
+ end
+ end;
+ {?ANYTYPE, ?ANYTYPE, true} ->
+ R;
+ {?ANYTYPE, ST, true} ->
+ case type(E) of
+ ?SET_OF(?ANYTYPE) ->
+ R;
+ ET ->
+ ?SET([], ?BINREL(ST, ET))
+ end;
+ {_, _, true} ->
+ erlang:error(badarg)
+ end.
+
+-spec(is_a_function(BinRel) -> Bool when
+ Bool :: boolean(),
+ BinRel :: binary_relation()).
+is_a_function(R) when ?IS_SET(R) ->
+ case ?TYPE(R) of
+ ?BINREL(_, _) ->
+ case ?LIST(R) of
+ [] -> true;
+ [{V,_} | Es] -> is_a_func(Es, V)
+ end;
+ ?ANYTYPE -> true;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(restriction(BinRel1, Set) -> BinRel2 when
+ BinRel1 :: binary_relation(),
+ BinRel2 :: binary_relation(),
+ Set :: a_set()).
+restriction(Relation, Set) ->
+ restriction(1, Relation, Set).
+
+-spec(drestriction(BinRel1, Set) -> BinRel2 when
+ BinRel1 :: binary_relation(),
+ BinRel2 :: binary_relation(),
+ Set :: a_set()).
+drestriction(Relation, Set) ->
+ drestriction(1, Relation, Set).
+
+%%%
+%%% Functions on functions only.
+%%%
+
+-spec(composite(Function1, Function2) -> Function3 when
+ Function1 :: a_function(),
+ Function2 :: a_function(),
+ Function3 :: a_function()).
+composite(Fn1, Fn2) when ?IS_SET(Fn1), ?IS_SET(Fn2) ->
+ ?BINREL(DTF1, RTF1) = case ?TYPE(Fn1)of
+ ?BINREL(_, _) = F1T -> F1T;
+ ?ANYTYPE -> {?ANYTYPE, ?ANYTYPE};
+ _ -> erlang:error(badarg)
+ end,
+ ?BINREL(DTF2, RTF2) = case ?TYPE(Fn2) of
+ ?BINREL(_, _) = F2T -> F2T;
+ ?ANYTYPE -> {?ANYTYPE, ?ANYTYPE};
+ _ -> erlang:error(badarg)
+ end,
+ case match_types(RTF1, DTF2) of
+ true when DTF1 =:= ?ANYTYPE -> Fn1;
+ true when DTF2 =:= ?ANYTYPE -> Fn2;
+ true ->
+ case comp(?LIST(Fn1), ?LIST(Fn2)) of
+ SL when is_list(SL) ->
+ ?SET(sort(SL), ?BINREL(DTF1, RTF2));
+ Bad ->
+ erlang:error(Bad)
+ end;
+ false -> erlang:error(type_mismatch)
+ end.
+
+-spec(inverse(Function1) -> Function2 when
+ Function1 :: a_function(),
+ Function2 :: a_function()).
+inverse(Fn) when ?IS_SET(Fn) ->
+ case ?TYPE(Fn) of
+ ?BINREL(DT, RT) ->
+ case inverse1(?LIST(Fn)) of
+ SL when is_list(SL) ->
+ ?SET(SL, ?BINREL(RT, DT));
+ Bad ->
+ erlang:error(Bad)
+ end;
+ ?ANYTYPE -> Fn;
+ _ -> erlang:error(badarg)
+ end.
+
+%%%
+%%% Functions on relations (binary or other).
+%%%
+
+-spec(restriction(SetFun, Set1, Set2) -> Set3 when
+ SetFun :: set_fun(),
+ Set1 :: a_set(),
+ Set2 :: a_set(),
+ Set3 :: a_set()).
+%% Equivalent to range(restriction(inverse(substitution(Fun, S1)), S2)).
+restriction(I, R, S) when is_integer(I), ?IS_SET(R), ?IS_SET(S) ->
+ RT = ?TYPE(R),
+ ST = ?TYPE(S),
+ case check_for_sort(RT, I) of
+ empty ->
+ R;
+ error ->
+ erlang:error(badarg);
+ Sort ->
+ RL = ?LIST(R),
+ case {match_types(?REL_TYPE(I, RT), ST), ?LIST(S)} of
+ {true, _SL} when RL =:= [] ->
+ R;
+ {true, []} ->
+ ?SET([], RT);
+ {true, [E | Es]} when Sort =:= false -> % I =:= 1
+ ?SET(reverse(restrict_n(I, RL, E, Es, [])), RT);
+ {true, [E | Es]} ->
+ ?SET(sort(restrict_n(I, keysort(I, RL), E, Es, [])), RT);
+ {false, _SL} ->
+ erlang:error(type_mismatch)
+ end
+ end;
+restriction(SetFun, S1, S2) when ?IS_SET(S1), ?IS_SET(S2) ->
+ Type1 = ?TYPE(S1),
+ Type2 = ?TYPE(S2),
+ SL1 = ?LIST(S1),
+ case external_fun(SetFun) of
+ false when Type2 =:= ?ANYTYPE ->
+ S2;
+ false ->
+ case subst(SL1, SetFun, element_type(Type1)) of
+ {NSL, NewType} -> % NewType can be ?ANYTYPE
+ case match_types(NewType, Type2) of
+ true ->
+ NL = sort(restrict(?LIST(S2), converse(NSL, []))),
+ ?SET(NL, Type1);
+ false ->
+ erlang:error(type_mismatch)
+ end;
+ Bad ->
+ erlang:error(Bad)
+ end;
+ _ when Type1 =:= ?ANYTYPE ->
+ S1;
+ _XFun when ?IS_SET_OF(Type1) ->
+ erlang:error(badarg);
+ XFun ->
+ FunT = XFun(Type1),
+ try check_fun(Type1, XFun, FunT) of
+ Sort ->
+ case match_types(FunT, Type2) of
+ true ->
+ R1 = inverse_substitution(SL1, XFun, Sort),
+ ?SET(sort(Sort, restrict(?LIST(S2), R1)), Type1);
+ false ->
+ erlang:error(type_mismatch)
+ end
+ catch _:_ -> erlang:error(badarg)
+ end
+ end.
+
+-spec(drestriction(SetFun, Set1, Set2) -> Set3 when
+ SetFun :: set_fun(),
+ Set1 :: a_set(),
+ Set2 :: a_set(),
+ Set3 :: a_set()).
+drestriction(I, R, S) when is_integer(I), ?IS_SET(R), ?IS_SET(S) ->
+ RT = ?TYPE(R),
+ ST = ?TYPE(S),
+ case check_for_sort(RT, I) of
+ empty ->
+ R;
+ error ->
+ erlang:error(badarg);
+ Sort ->
+ RL = ?LIST(R),
+ case {match_types(?REL_TYPE(I, RT), ST), ?LIST(S)} of
+ {true, []} ->
+ R;
+ {true, _SL} when RL =:= [] ->
+ R;
+ {true, [E | Es]} when Sort =:= false -> % I =:= 1
+ ?SET(diff_restrict_n(I, RL, E, Es, []), RT);
+ {true, [E | Es]} ->
+ ?SET(diff_restrict_n(I, keysort(I, RL), E, Es, []), RT);
+ {false, _SL} ->
+ erlang:error(type_mismatch)
+ end
+ end;
+drestriction(SetFun, S1, S2) when ?IS_SET(S1), ?IS_SET(S2) ->
+ Type1 = ?TYPE(S1),
+ Type2 = ?TYPE(S2),
+ SL1 = ?LIST(S1),
+ case external_fun(SetFun) of
+ false when Type2 =:= ?ANYTYPE ->
+ S1;
+ false ->
+ case subst(SL1, SetFun, element_type(Type1)) of
+ {NSL, NewType} -> % NewType can be ?ANYTYPE
+ case match_types(NewType, Type2) of
+ true ->
+ SL2 = ?LIST(S2),
+ NL = sort(diff_restrict(SL2, converse(NSL, []))),
+ ?SET(NL, Type1);
+ false ->
+ erlang:error(type_mismatch)
+ end;
+ Bad ->
+ erlang:error(Bad)
+ end;
+ _ when Type1 =:= ?ANYTYPE ->
+ S1;
+ _XFun when ?IS_SET_OF(Type1) ->
+ erlang:error(badarg);
+ XFun ->
+ FunT = XFun(Type1),
+ try check_fun(Type1, XFun, FunT) of
+ Sort ->
+ case match_types(FunT, Type2) of
+ true ->
+ R1 = inverse_substitution(SL1, XFun, Sort),
+ SL2 = ?LIST(S2),
+ ?SET(sort(Sort, diff_restrict(SL2, R1)), Type1);
+ false ->
+ erlang:error(type_mismatch)
+ end
+ catch _:_ -> erlang:error(badarg)
+ end
+ end.
+
+-spec(projection(SetFun, Set1) -> Set2 when
+ SetFun :: set_fun(),
+ Set1 :: a_set(),
+ Set2 :: a_set()).
+projection(I, Set) when is_integer(I), ?IS_SET(Set) ->
+ Type = ?TYPE(Set),
+ case check_for_sort(Type, I) of
+ empty ->
+ Set;
+ error ->
+ erlang:error(badarg);
+ _ when I =:= 1 ->
+ ?SET(projection1(?LIST(Set)), ?REL_TYPE(I, Type));
+ _ ->
+ ?SET(projection_n(?LIST(Set), I, []), ?REL_TYPE(I, Type))
+ end;
+projection(Fun, Set) ->
+ range(substitution(Fun, Set)).
+
+-spec(substitution(SetFun, Set1) -> Set2 when
+ SetFun :: set_fun(),
+ Set1 :: a_set(),
+ Set2 :: a_set()).
+substitution(I, Set) when is_integer(I), ?IS_SET(Set) ->
+ Type = ?TYPE(Set),
+ case check_for_sort(Type, I) of
+ empty ->
+ Set;
+ error ->
+ erlang:error(badarg);
+ _Sort ->
+ NType = ?REL_TYPE(I, Type),
+ NSL = substitute_element(?LIST(Set), I, []),
+ ?SET(NSL, ?BINREL(Type, NType))
+ end;
+substitution(SetFun, Set) when ?IS_SET(Set) ->
+ Type = ?TYPE(Set),
+ L = ?LIST(Set),
+ case external_fun(SetFun) of
+ false when L =/= [] ->
+ case subst(L, SetFun, element_type(Type)) of
+ {SL, NewType} ->
+ ?SET(reverse(SL), ?BINREL(Type, NewType));
+ Bad ->
+ erlang:error(Bad)
+ end;
+ false ->
+ empty_set();
+ _ when Type =:= ?ANYTYPE ->
+ empty_set();
+ _XFun when ?IS_SET_OF(Type) ->
+ erlang:error(badarg);
+ XFun ->
+ FunT = XFun(Type),
+ try check_fun(Type, XFun, FunT) of
+ _Sort ->
+ SL = substitute(L, XFun, []),
+ ?SET(SL, ?BINREL(Type, FunT))
+ catch _:_ -> erlang:error(badarg)
+ end
+ end.
+
+-spec(partition(SetOfSets) -> Partition when
+ SetOfSets :: set_of_sets(),
+ Partition :: a_set()).
+partition(Sets) ->
+ F1 = relation_to_family(canonical_relation(Sets)),
+ F2 = relation_to_family(converse(F1)),
+ range(F2).
+
+-spec(partition(SetFun, Set) -> Partition when
+ SetFun :: set_fun(),
+ Partition :: a_set(),
+ Set :: a_set()).
+partition(I, Set) when is_integer(I), ?IS_SET(Set) ->
+ Type = ?TYPE(Set),
+ case check_for_sort(Type, I) of
+ empty ->
+ Set;
+ error ->
+ erlang:error(badarg);
+ false -> % I =:= 1
+ ?SET(partition_n(I, ?LIST(Set)), ?SET_OF(Type));
+ true ->
+ ?SET(partition_n(I, keysort(I, ?LIST(Set))), ?SET_OF(Type))
+ end;
+partition(Fun, Set) ->
+ range(partition_family(Fun, Set)).
+
+-spec(partition(SetFun, Set1, Set2) -> {Set3, Set4} when
+ SetFun :: set_fun(),
+ Set1 :: a_set(),
+ Set2 :: a_set(),
+ Set3 :: a_set(),
+ Set4 :: a_set()).
+partition(I, R, S) when is_integer(I), ?IS_SET(R), ?IS_SET(S) ->
+ RT = ?TYPE(R),
+ ST = ?TYPE(S),
+ case check_for_sort(RT, I) of
+ empty ->
+ {R, R};
+ error ->
+ erlang:error(badarg);
+ Sort ->
+ RL = ?LIST(R),
+ case {match_types(?REL_TYPE(I, RT), ST), ?LIST(S)} of
+ {true, _SL} when RL =:= [] ->
+ {R, R};
+ {true, []} ->
+ {?SET([], RT), R};
+ {true, [E | Es]} when Sort =:= false -> % I =:= 1
+ [L1 | L2] = partition3_n(I, RL, E, Es, [], []),
+ {?SET(L1, RT), ?SET(L2, RT)};
+ {true, [E | Es]} ->
+ [L1 | L2] = partition3_n(I, keysort(I,RL), E, Es, [], []),
+ {?SET(L1, RT), ?SET(L2, RT)};
+ {false, _SL} ->
+ erlang:error(type_mismatch)
+ end
+ end;
+partition(SetFun, S1, S2) when ?IS_SET(S1), ?IS_SET(S2) ->
+ Type1 = ?TYPE(S1),
+ Type2 = ?TYPE(S2),
+ SL1 = ?LIST(S1),
+ case external_fun(SetFun) of
+ false when Type2 =:= ?ANYTYPE ->
+ {S2, S1};
+ false ->
+ case subst(SL1, SetFun, element_type(Type1)) of
+ {NSL, NewType} -> % NewType can be ?ANYTYPE
+ case match_types(NewType, Type2) of
+ true ->
+ R1 = converse(NSL, []),
+ [L1 | L2] = partition3(?LIST(S2), R1),
+ {?SET(sort(L1), Type1), ?SET(sort(L2), Type1)};
+ false ->
+ erlang:error(type_mismatch)
+ end;
+ Bad ->
+ erlang:error(Bad)
+ end;
+ _ when Type1 =:= ?ANYTYPE ->
+ {S1, S1};
+ _XFun when ?IS_SET_OF(Type1) ->
+ erlang:error(badarg);
+ XFun ->
+ FunT = XFun(Type1),
+ try check_fun(Type1, XFun, FunT) of
+ Sort ->
+ case match_types(FunT, Type2) of
+ true ->
+ R1 = inverse_substitution(SL1, XFun, Sort),
+ [L1 | L2] = partition3(?LIST(S2), R1),
+ {?SET(sort(L1), Type1), ?SET(sort(L2), Type1)};
+ false ->
+ erlang:error(type_mismatch)
+ end
+ catch _:_ -> erlang:error(badarg)
+ end
+ end.
+
+-spec(multiple_relative_product(TupleOfBinRels, BinRel1) -> BinRel2 when
+ TupleOfBinRels :: tuple_of(BinRel),
+ BinRel :: binary_relation(),
+ BinRel1 :: binary_relation(),
+ BinRel2 :: binary_relation()).
+multiple_relative_product(T, R) when is_tuple(T), ?IS_SET(R) ->
+ case test_rel(R, tuple_size(T), eq) of
+ true when ?TYPE(R) =:= ?ANYTYPE ->
+ empty_set();
+ true ->
+ MProd = mul_relprod(tuple_to_list(T), 1, R),
+ relative_product(MProd);
+ false ->
+ erlang:error(badarg)
+ end.
+
+-spec(join(Relation1, I, Relation2, J) -> Relation3 when
+ Relation1 :: relation(),
+ Relation2 :: relation(),
+ Relation3 :: relation(),
+ I :: pos_integer(),
+ J :: pos_integer()).
+join(R1, I1, R2, I2)
+ when ?IS_SET(R1), ?IS_SET(R2), is_integer(I1), is_integer(I2) ->
+ case test_rel(R1, I1, lte) and test_rel(R2, I2, lte) of
+ false -> erlang:error(badarg);
+ true when ?TYPE(R1) =:= ?ANYTYPE -> R1;
+ true when ?TYPE(R2) =:= ?ANYTYPE -> R2;
+ true ->
+ L1 = ?LIST(raise_element(R1, I1)),
+ L2 = ?LIST(raise_element(R2, I2)),
+ T = relprod1(L1, L2),
+ F = case (I1 =:= 1) and (I2 =:= 1) of
+ true ->
+ fun({X,Y}) -> join_element(X, Y) end;
+ false ->
+ fun({X,Y}) ->
+ list_to_tuple(join_element(X, Y, I2))
+ end
+ end,
+ ?SET(replace(T, F, []), F({?TYPE(R1), ?TYPE(R2)}))
+ end.
+
+%% Inlined.
+test_rel(R, I, C) ->
+ case ?TYPE(R) of
+ Rel when ?IS_RELATION(Rel), C =:= eq, I =:= ?REL_ARITY(Rel) -> true;
+ Rel when ?IS_RELATION(Rel), C =:= lte, I>=1, I =< ?REL_ARITY(Rel) ->
+ true;
+ ?ANYTYPE -> true;
+ _ -> false
+ end.
+
+%%%
+%%% Family functions
+%%%
+
+-spec(fam2rel(Family) -> BinRel when
+ Family :: family(),
+ BinRel :: binary_relation()).
+fam2rel(F) ->
+ family_to_relation(F).
+
+-spec(family_to_relation(Family) -> BinRel when
+ Family :: family(),
+ BinRel :: binary_relation()).
+%% Inlined.
+family_to_relation(F) when ?IS_SET(F) ->
+ case ?TYPE(F) of
+ ?FAMILY(DT, RT) ->
+ ?SET(family2rel(?LIST(F), []), ?BINREL(DT, RT));
+ ?ANYTYPE -> F;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(family_specification(Fun, Family1) -> Family2 when
+ Fun :: spec_fun(),
+ Family1 :: family(),
+ Family2 :: family()).
+family_specification(Fun, F) when ?IS_SET(F) ->
+ case ?TYPE(F) of
+ ?FAMILY(_DT, Type) = FType ->
+ R = case external_fun(Fun) of
+ false ->
+ fam_spec(?LIST(F), Fun, Type, []);
+ XFun ->
+ fam_specification(?LIST(F), XFun, [])
+ end,
+ case R of
+ SL when is_list(SL) ->
+ ?SET(SL, FType);
+ Bad ->
+ erlang:error(Bad)
+ end;
+ ?ANYTYPE -> F;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(union_of_family(Family) -> Set when
+ Family :: family(),
+ Set :: a_set()).
+union_of_family(F) when ?IS_SET(F) ->
+ case ?TYPE(F) of
+ ?FAMILY(_DT, Type) ->
+ ?SET(un_of_fam(?LIST(F), []), Type);
+ ?ANYTYPE -> F;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(intersection_of_family(Family) -> Set when
+ Family :: family(),
+ Set :: a_set()).
+intersection_of_family(F) when ?IS_SET(F) ->
+ case ?TYPE(F) of
+ ?FAMILY(_DT, Type) ->
+ case int_of_fam(?LIST(F)) of
+ FU when is_list(FU) ->
+ ?SET(FU, Type);
+ Bad ->
+ erlang:error(Bad)
+ end;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(family_union(Family1) -> Family2 when
+ Family1 :: family(),
+ Family2 :: family()).
+family_union(F) when ?IS_SET(F) ->
+ case ?TYPE(F) of
+ ?FAMILY(DT, ?SET_OF(Type)) ->
+ ?SET(fam_un(?LIST(F), []), ?FAMILY(DT, Type));
+ ?ANYTYPE -> F;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(family_intersection(Family1) -> Family2 when
+ Family1 :: family(),
+ Family2 :: family()).
+family_intersection(F) when ?IS_SET(F) ->
+ case ?TYPE(F) of
+ ?FAMILY(DT, ?SET_OF(Type)) ->
+ case fam_int(?LIST(F), []) of
+ FU when is_list(FU) ->
+ ?SET(FU, ?FAMILY(DT, Type));
+ Bad ->
+ erlang:error(Bad)
+ end;
+ ?ANYTYPE -> F;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(family_domain(Family1) -> Family2 when
+ Family1 :: family(),
+ Family2 :: family()).
+family_domain(F) when ?IS_SET(F) ->
+ case ?TYPE(F) of
+ ?FAMILY(FDT, ?BINREL(DT, _)) ->
+ ?SET(fam_dom(?LIST(F), []), ?FAMILY(FDT, DT));
+ ?ANYTYPE -> F;
+ ?FAMILY(_, ?ANYTYPE) -> F;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(family_range(Family1) -> Family2 when
+ Family1 :: family(),
+ Family2 :: family()).
+family_range(F) when ?IS_SET(F) ->
+ case ?TYPE(F) of
+ ?FAMILY(DT, ?BINREL(_, RT)) ->
+ ?SET(fam_ran(?LIST(F), []), ?FAMILY(DT, RT));
+ ?ANYTYPE -> F;
+ ?FAMILY(_, ?ANYTYPE) -> F;
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(family_field(Family1) -> Family2 when
+ Family1 :: family(),
+ Family2 :: family()).
+family_field(F) ->
+ family_union(family_domain(F), family_range(F)).
+
+-spec(family_union(Family1, Family2) -> Family3 when
+ Family1 :: family(),
+ Family2 :: family(),
+ Family3 :: family()).
+family_union(F1, F2) ->
+ fam_binop(F1, F2, fun fam_union/3).
+
+-spec(family_intersection(Family1, Family2) -> Family3 when
+ Family1 :: family(),
+ Family2 :: family(),
+ Family3 :: family()).
+family_intersection(F1, F2) ->
+ fam_binop(F1, F2, fun fam_intersect/3).
+
+-spec(family_difference(Family1, Family2) -> Family3 when
+ Family1 :: family(),
+ Family2 :: family(),
+ Family3 :: family()).
+family_difference(F1, F2) ->
+ fam_binop(F1, F2, fun fam_difference/3).
+
+%% Inlined.
+fam_binop(F1, F2, FF) when ?IS_SET(F1), ?IS_SET(F2) ->
+ case unify_types(?TYPE(F1), ?TYPE(F2)) of
+ [] ->
+ erlang:error(type_mismatch);
+ ?ANYTYPE ->
+ F1;
+ Type = ?FAMILY(_, _) ->
+ ?SET(FF(?LIST(F1), ?LIST(F2), []), Type);
+ _ -> erlang:error(badarg)
+ end.
+
+-spec(partition_family(SetFun, Set) -> Family when
+ Family :: family(),
+ SetFun :: set_fun(),
+ Set :: a_set()).
+partition_family(I, Set) when is_integer(I), ?IS_SET(Set) ->
+ Type = ?TYPE(Set),
+ case check_for_sort(Type, I) of
+ empty ->
+ Set;
+ error ->
+ erlang:error(badarg);
+ false -> % when I =:= 1
+ ?SET(fam_partition_n(I, ?LIST(Set)),
+ ?BINREL(?REL_TYPE(I, Type), ?SET_OF(Type)));
+ true ->
+ ?SET(fam_partition_n(I, keysort(I, ?LIST(Set))),
+ ?BINREL(?REL_TYPE(I, Type), ?SET_OF(Type)))
+ end;
+partition_family(SetFun, Set) when ?IS_SET(Set) ->
+ Type = ?TYPE(Set),
+ SL = ?LIST(Set),
+ case external_fun(SetFun) of
+ false when SL =/= [] ->
+ case subst(SL, SetFun, element_type(Type)) of
+ {NSL, NewType} ->
+ P = fam_partition(converse(NSL, []), true),
+ ?SET(reverse(P), ?BINREL(NewType, ?SET_OF(Type)));
+ Bad ->
+ erlang:error(Bad)
+ end;
+ false ->
+ empty_set();
+ _ when Type =:= ?ANYTYPE ->
+ empty_set();
+ _XFun when ?IS_SET_OF(Type) ->
+ erlang:error(badarg);
+ XFun ->
+ DType = XFun(Type),
+ try check_fun(Type, XFun, DType) of
+ Sort ->
+ Ts = inverse_substitution(?LIST(Set), XFun, Sort),
+ P = fam_partition(Ts, Sort),
+ ?SET(reverse(P), ?BINREL(DType, ?SET_OF(Type)))
+ catch _:_ -> erlang:error(badarg)
+ end
+ end.
+
+-spec(family_projection(SetFun, Family1) -> Family2 when
+ SetFun :: set_fun(),
+ Family1 :: family(),
+ Family2 :: family()).
+family_projection(SetFun, F) when ?IS_SET(F) ->
+ case ?TYPE(F) of
+ ?FAMILY(_, _) when [] =:= ?LIST(F) ->
+ empty_set();
+ ?FAMILY(DT, Type) ->
+ case external_fun(SetFun) of
+ false ->
+ case fam_proj(?LIST(F), SetFun, Type, ?ANYTYPE, []) of
+ {SL, NewType} ->
+ ?SET(SL, ?BINREL(DT, NewType));
+ Bad ->
+ erlang:error(Bad)
+ end;
+ _ ->
+ erlang:error(badarg)
+ end;
+ ?ANYTYPE -> F;
+ _ -> erlang:error(badarg)
+ end.
+
+%%%
+%%% Digraph functions
+%%%
+
+-spec(family_to_digraph(Family) -> Graph when
+ Graph :: digraph:graph(),
+ Family :: family()).
+family_to_digraph(F) when ?IS_SET(F) ->
+ case ?TYPE(F) of
+ ?FAMILY(_, _) -> fam2digraph(F, digraph:new());
+ ?ANYTYPE -> digraph:new();
+ _Else -> erlang:error(badarg)
+ end.
+
+-spec(family_to_digraph(Family, GraphType) -> Graph when
+ Graph :: digraph:graph(),
+ Family :: family(),
+ GraphType :: [digraph:d_type()]).
+family_to_digraph(F, Type) when ?IS_SET(F) ->
+ case ?TYPE(F) of
+ ?FAMILY(_, _) -> ok;
+ ?ANYTYPE -> ok;
+ _Else -> erlang:error(badarg)
+ end,
+ try digraph:new(Type) of
+ G -> case catch fam2digraph(F, G) of
+ {error, Reason} ->
+ true = digraph:delete(G),
+ erlang:error(Reason);
+ _ ->
+ G
+ end
+ catch
+ error:badarg -> erlang:error(badarg)
+ end.
+
+-spec(digraph_to_family(Graph) -> Family when
+ Graph :: digraph:graph(),
+ Family :: family()).
+digraph_to_family(G) ->
+ try digraph_family(G) of
+ L -> ?SET(L, ?FAMILY(?ATOM_TYPE, ?ATOM_TYPE))
+ catch _:_ -> erlang:error(badarg)
+ end.
+
+-spec(digraph_to_family(Graph, Type) -> Family when
+ Graph :: digraph:graph(),
+ Family :: family(),
+ Type :: type()).
+digraph_to_family(G, T) ->
+ case {is_type(T), T} of
+ {true, ?SET_OF(?FAMILY(_,_) = Type)} ->
+ try digraph_family(G) of
+ L -> ?SET(L, Type)
+ catch _:_ -> erlang:error(badarg)
+ end;
+ _ ->
+ erlang:error(badarg)
+ end.
+
+%%
+%% Local functions
+%%
+
+%% Type = OrderedSetType
+%% | SetType
+%% | atom() except '_'
+%% OrderedSetType = {Type, ..., Type}
+%% SetType = [ElementType] % list of exactly one element
+%% ElementType = '_' % any type (implies empty set)
+%% | Type
+
+is_types(0, _T) ->
+ true;
+is_types(I, T) ->
+ case is_type(?REL_TYPE(I, T)) of
+ true -> is_types(I-1, T);
+ false -> false
+ end.
+
+is_element_type(?ANYTYPE) ->
+ true;
+is_element_type(T) ->
+ is_type(T).
+
+set_of_sets([S | Ss], L, T0) when ?IS_SET(S) ->
+ case unify_types([?TYPE(S)], T0) of
+ [] -> {error, type_mismatch};
+ Type -> set_of_sets(Ss, [?LIST(S) | L], Type)
+ end;
+set_of_sets([S | Ss], L, T0) when ?IS_ORDSET(S) ->
+ case unify_types(?ORDTYPE(S), T0) of
+ [] -> {error, type_mismatch};
+ Type -> set_of_sets(Ss, [?ORDDATA(S) | L], Type)
+ end;
+set_of_sets([], L, T) ->
+ ?SET(usort(L), T);
+set_of_sets(_, _L, _T) ->
+ {error, badarg}.
+
+ordset_of_sets([S | Ss], L, T) when ?IS_SET(S) ->
+ ordset_of_sets(Ss, [?LIST(S) | L], [[?TYPE(S)] | T]);
+ordset_of_sets([S | Ss], L, T) when ?IS_ORDSET(S) ->
+ ordset_of_sets(Ss, [?ORDDATA(S) | L], [?ORDTYPE(S) | T]);
+ordset_of_sets([], L, T) ->
+ ?ORDSET(list_to_tuple(reverse(L)), list_to_tuple(reverse(T)));
+ordset_of_sets(_, _L, _T) ->
+ error.
+
+%% Inlined.
+rel(Ts, [Type]) ->
+ case is_type(Type) and atoms_only(Type, 1) of
+ true ->
+ rel(Ts, tuple_size(Type), Type);
+ false ->
+ rel_type(Ts, [], Type)
+ end;
+rel(Ts, Sz) ->
+ rel(Ts, Sz, erlang:make_tuple(Sz, ?ATOM_TYPE)).
+
+atoms_only(Type, I) when ?IS_ATOM_TYPE(?REL_TYPE(I, Type)) ->
+ atoms_only(Type, I+1);
+atoms_only(Type, I) when I > tuple_size(Type), ?IS_RELATION(Type) ->
+ true;
+atoms_only(_Type, _I) ->
+ false.
+
+rel(Ts, Sz, Type) when Sz >= 1 ->
+ SL = usort(Ts),
+ rel(SL, SL, Sz, Type).
+
+rel([T | Ts], L, Sz, Type) when tuple_size(T) =:= Sz ->
+ rel(Ts, L, Sz, Type);
+rel([], L, _Sz, Type) ->
+ ?SET(L, Type).
+
+rel_type([E | Ts], L, Type) ->
+ {NType, NE} = make_element(E, Type, Type),
+ rel_type(Ts, [NE | L], NType);
+rel_type([], [], ?ANYTYPE) ->
+ empty_set();
+rel_type([], SL, Type) when ?IS_RELATION(Type) ->
+ ?SET(usort(SL), Type).
+
+%% Inlined.
+a_func(Ts, T) ->
+ case {T, is_type(T)} of
+ {[?BINREL(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT),
+ ?IS_ATOM_TYPE(RT) ->
+ func(Ts, Type);
+ {[Type], true} ->
+ func_type(Ts, [], Type, fun(?BINREL(_,_)) -> true end)
+ end.
+
+func(L0, Type) ->
+ L = usort(L0),
+ func(L, L, L, Type).
+
+func([{X,_} | Ts], X0, L, Type) when X /= X0 ->
+ func(Ts, X, L, Type);
+func([{X,_} | _Ts], X0, _L, _Type) when X == X0 ->
+ bad_function;
+func([], _X0, L, Type) ->
+ ?SET(L, Type).
+
+%% Inlined.
+fam(Ts, T) ->
+ case {T, is_type(T)} of
+ {[?FAMILY(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT),
+ ?IS_ATOM_TYPE(RT) ->
+ fam2(Ts, Type);
+ {[Type], true} ->
+ func_type(Ts, [], Type, fun(?FAMILY(_,_)) -> true end)
+ end.
+
+fam2([], Type) ->
+ ?SET([], Type);
+fam2(Ts, Type) ->
+ fam2(sort(Ts), Ts, [], Type).
+
+fam2([{I,L} | T], I0, SL, Type) when I /= I0 ->
+ fam2(T, I, [{I,usort(L)} | SL], Type);
+fam2([{I,L} | T], I0, SL, Type) when I == I0 ->
+ case {usort(L), SL} of
+ {NL, [{_I,NL1} | _]} when NL == NL1 ->
+ fam2(T, I0, SL, Type);
+ _ ->
+ bad_function
+ end;
+fam2([], _I0, SL, Type) ->
+ ?SET(reverse(SL), Type).
+
+func_type([E | T], SL, Type, F) ->
+ {NType, NE} = make_element(E, Type, Type),
+ func_type(T, [NE | SL], NType, F);
+func_type([], [], ?ANYTYPE, _F) ->
+ empty_set();
+func_type([], SL, Type, F) ->
+ true = F(Type),
+ NL = usort(SL),
+ check_function(NL, ?SET(NL, Type)).
+
+setify(L, ?SET_OF(Atom)) when ?IS_ATOM_TYPE(Atom), Atom =/= ?ANYTYPE ->
+ ?SET(usort(L), Atom);
+setify(L, ?SET_OF(Type0)) ->
+ try is_no_lists(Type0) of
+ N when is_integer(N) ->
+ rel(L, N, Type0);
+ Sizes ->
+ make_oset(L, Sizes, L, Type0)
+ catch
+ _:_ ->
+ {?SET_OF(Type), Set} = create(L, Type0, Type0, []),
+ ?SET(Set, Type)
+ end;
+setify(E, Type0) ->
+ {Type, OrdSet} = make_element(E, Type0, Type0),
+ ?ORDSET(OrdSet, Type).
+
+is_no_lists(T) when is_tuple(T) ->
+ Sz = tuple_size(T),
+ is_no_lists(T, Sz, Sz, []).
+
+is_no_lists(_T, 0, Sz, []) ->
+ Sz;
+is_no_lists(_T, 0, Sz, L) ->
+ {Sz, L};
+is_no_lists(T, I, Sz, L) when ?IS_ATOM_TYPE(?REL_TYPE(I, T)) ->
+ is_no_lists(T, I-1, Sz, L);
+is_no_lists(T, I, Sz, L) ->
+ is_no_lists(T, I-1, Sz, [{I,is_no_lists(?REL_TYPE(I, T))} | L]).
+
+create([E | Es], T, T0, L) ->
+ {NT, S} = make_element(E, T, T0),
+ create(Es, NT, T0, [S | L]);
+create([], T, _T0, L) ->
+ {?SET_OF(T), usort(L)}.
+
+make_element(C, ?ANYTYPE, _T0) ->
+ make_element(C);
+make_element(C, Atom, ?ANYTYPE) when ?IS_ATOM_TYPE(Atom),
+ not is_list(C), not is_tuple(C) ->
+ {Atom, C};
+make_element(C, Atom, Atom) when ?IS_ATOM_TYPE(Atom) ->
+ {Atom, C};
+make_element(T, TT, ?ANYTYPE) when tuple_size(T) =:= tuple_size(TT) ->
+ make_tuple(tuple_to_list(T), tuple_to_list(TT), [], [], ?ANYTYPE);
+make_element(T, TT, T0) when tuple_size(T) =:= tuple_size(TT) ->
+ make_tuple(tuple_to_list(T), tuple_to_list(TT), [], [], tuple_to_list(T0));
+make_element(L, [LT], ?ANYTYPE) when is_list(L) ->
+ create(L, LT, ?ANYTYPE, []);
+make_element(L, [LT], [T0]) when is_list(L) ->
+ create(L, LT, T0, []).
+
+make_tuple([E | Es], [T | Ts], NT, L, T0) when T0 =:= ?ANYTYPE ->
+ {ET, ES} = make_element(E, T, T0),
+ make_tuple(Es, Ts, [ET | NT], [ES | L], T0);
+make_tuple([E | Es], [T | Ts], NT, L, [T0 | T0s]) ->
+ {ET, ES} = make_element(E, T, T0),
+ make_tuple(Es, Ts, [ET | NT], [ES | L], T0s);
+make_tuple([], [], NT, L, _T0s) when NT =/= [] ->
+ {list_to_tuple(reverse(NT)), list_to_tuple(reverse(L))}.
+
+%% Derive type.
+make_element(C) when not is_list(C), not is_tuple(C) ->
+ {?ATOM_TYPE, C};
+make_element(T) when is_tuple(T) ->
+ make_tuple(tuple_to_list(T), [], []);
+make_element(L) when is_list(L) ->
+ create(L, ?ANYTYPE, ?ANYTYPE, []).
+
+make_tuple([E | Es], T, L) ->
+ {ET, ES} = make_element(E),
+ make_tuple(Es, [ET | T], [ES | L]);
+make_tuple([], T, L) when T =/= [] ->
+ {list_to_tuple(reverse(T)), list_to_tuple(reverse(L))}.
+
+make_oset([T | Ts], Szs, L, Type) ->
+ true = test_oset(Szs, T, T),
+ make_oset(Ts, Szs, L, Type);
+make_oset([], _Szs, L, Type) ->
+ ?SET(usort(L), Type).
+
+%% Optimization. Avoid re-building (nested) tuples.
+test_oset({Sz,Args}, T, T0) when tuple_size(T) =:= Sz ->
+ test_oset_args(Args, T, T0);
+test_oset(Sz, T, _T0) when tuple_size(T) =:= Sz ->
+ true.
+
+test_oset_args([{Arg,Szs} | Ss], T, T0) ->
+ true = test_oset(Szs, ?REL_TYPE(Arg, T), T0),
+ test_oset_args(Ss, T, T0);
+test_oset_args([], _T, _T0) ->
+ true.
+
+list_of_sets([S | Ss], Type, L) ->
+ list_of_sets(Ss, Type, [?SET(S, Type) | L]);
+list_of_sets([], _Type, L) ->
+ reverse(L).
+
+list_of_ordsets([S | Ss], Type, L) ->
+ list_of_ordsets(Ss, Type, [?ORDSET(S, Type) | L]);
+list_of_ordsets([], _Type, L) ->
+ reverse(L).
+
+tuple_of_sets([S | Ss], [?SET_OF(Type) | Types], L) ->
+ tuple_of_sets(Ss, Types, [?SET(S, Type) | L]);
+tuple_of_sets([S | Ss], [Type | Types], L) ->
+ tuple_of_sets(Ss, Types, [?ORDSET(S, Type) | L]);
+tuple_of_sets([], [], L) ->
+ list_to_tuple(reverse(L)).
+
+spec([E | Es], Fun, Type, L) ->
+ case Fun(term2set(E, Type)) of
+ true ->
+ spec(Es, Fun, Type, [E | L]);
+ false ->
+ spec(Es, Fun, Type, L);
+ _ ->
+ badarg
+ end;
+spec([], _Fun, _Type, L) ->
+ reverse(L).
+
+specification([E | Es], Fun, L) ->
+ case Fun(E) of
+ true ->
+ specification(Es, Fun, [E | L]);
+ false ->
+ specification(Es, Fun, L);
+ _ ->
+ badarg
+ end;
+specification([], _Fun, L) ->
+ reverse(L).
+
+%% Elements from the first list are kept.
+intersection([H1 | T1], [H2 | T2], L) when H1 < H2 ->
+ intersection1(T1, T2, L, H2);
+intersection([H1 | T1], [H2 | T2], L) when H1 == H2 ->
+ intersection(T1, T2, [H1 | L]);
+intersection([H1 | T1], [_H2 | T2], L) ->
+ intersection2(T1, T2, L, H1);
+intersection(_, _, L) ->
+ reverse(L).
+
+intersection1([H1 | T1], T2, L, H2) when H1 < H2 ->
+ intersection1(T1, T2, L, H2);
+intersection1([H1 | T1], T2, L, H2) when H1 == H2 ->
+ intersection(T1, T2, [H1 | L]);
+intersection1([H1 | T1], T2, L, _H2) ->
+ intersection2(T1, T2, L, H1);
+intersection1(_, _, L, _) ->
+ reverse(L).
+
+intersection2(T1, [H2 | T2], L, H1) when H1 > H2 ->
+ intersection2(T1, T2, L, H1);
+intersection2(T1, [H2 | T2], L, H1) when H1 == H2 ->
+ intersection(T1, T2, [H1 | L]);
+intersection2(T1, [H2 | T2], L, _H1) ->
+ intersection1(T1, T2, L, H2);
+intersection2(_, _, L, _) ->
+ reverse(L).
+
+difference([H1 | T1], [H2 | T2], L) when H1 < H2 ->
+ diff(T1, T2, [H1 | L], H2);
+difference([H1 | T1], [H2 | T2], L) when H1 == H2 ->
+ difference(T1, T2, L);
+difference([H1 | T1], [_H2 | T2], L) ->
+ diff2(T1, T2, L, H1);
+difference(L1, _, L) ->
+ reverse(L, L1).
+
+diff([H1 | T1], T2, L, H2) when H1 < H2 ->
+ diff(T1, T2, [H1 | L], H2);
+diff([H1 | T1], T2, L, H2) when H1 == H2 ->
+ difference(T1, T2, L);
+diff([H1 | T1], T2, L, _H2) ->
+ diff2(T1, T2, L, H1);
+diff(_, _, L, _) ->
+ reverse(L).
+
+diff2(T1, [H2 | T2], L, H1) when H1 > H2 ->
+ diff2(T1, T2, L, H1);
+diff2(T1, [H2 | T2], L, H1) when H1 == H2 ->
+ difference(T1, T2, L);
+diff2(T1, [H2 | T2], L, H1) ->
+ diff(T1, T2, [H1 | L], H2);
+diff2(T1, _, L, H1) ->
+ reverse(L, [H1 | T1]).
+
+symdiff([H1 | T1], T2, L) ->
+ symdiff2(T1, T2, L, H1);
+symdiff(_, T2, L) ->
+ reverse(L, T2).
+
+symdiff1([H1 | T1], T2, L, H2) when H1 < H2 ->
+ symdiff1(T1, T2, [H1 | L], H2);
+symdiff1([H1 | T1], T2, L, H2) when H1 == H2 ->
+ symdiff(T1, T2, L);
+symdiff1([H1 | T1], T2, L, H2) ->
+ symdiff2(T1, T2, [H2 | L], H1);
+symdiff1(_, T2, L, H2) ->
+ reverse(L, [H2 | T2]).
+
+symdiff2(T1, [H2 | T2], L, H1) when H1 > H2 ->
+ symdiff2(T1, T2, [H2 | L], H1);
+symdiff2(T1, [H2 | T2], L, H1) when H1 == H2 ->
+ symdiff(T1, T2, L);
+symdiff2(T1, [H2 | T2], L, H1) ->
+ symdiff1(T1, T2, [H1 | L], H2);
+symdiff2(T1, _, L, H1) ->
+ reverse(L, [H1 | T1]).
+
+sympart([H1 | T1], [H2 | T2], L1, L12, L2, T) when H1 < H2 ->
+ sympart1(T1, T2, [H1 | L1], L12, L2, T, H2);
+sympart([H1 | T1], [H2 | T2], L1, L12, L2, T) when H1 == H2 ->
+ sympart(T1, T2, L1, [H1 | L12], L2, T);
+sympart([H1 | T1], [H2 | T2], L1, L12, L2, T) ->
+ sympart2(T1, T2, L1, L12, [H2 | L2], T, H1);
+sympart(S1, [], L1, L12, L2, T) ->
+ {?SET(reverse(L1, S1), T),
+ ?SET(reverse(L12), T),
+ ?SET(reverse(L2), T)};
+sympart(_, S2, L1, L12, L2, T) ->
+ {?SET(reverse(L1), T),
+ ?SET(reverse(L12), T),
+ ?SET(reverse(L2, S2), T)}.
+
+sympart1([H1 | T1], T2, L1, L12, L2, T, H2) when H1 < H2 ->
+ sympart1(T1, T2, [H1 | L1], L12, L2, T, H2);
+sympart1([H1 | T1], T2, L1, L12, L2, T, H2) when H1 == H2 ->
+ sympart(T1, T2, L1, [H1 | L12], L2, T);
+sympart1([H1 | T1], T2, L1, L12, L2, T, H2) ->
+ sympart2(T1, T2, L1, L12, [H2 | L2], T, H1);
+sympart1(_, T2, L1, L12, L2, T, H2) ->
+ {?SET(reverse(L1), T),
+ ?SET(reverse(L12), T),
+ ?SET(reverse(L2, [H2 | T2]), T)}.
+
+sympart2(T1, [H2 | T2], L1, L12, L2, T, H1) when H1 > H2 ->
+ sympart2(T1, T2, L1, L12, [H2 | L2], T, H1);
+sympart2(T1, [H2 | T2], L1, L12, L2, T, H1) when H1 == H2 ->
+ sympart(T1, T2, L1, [H1 | L12], L2, T);
+sympart2(T1, [H2 | T2], L1, L12, L2, T, H1) ->
+ sympart1(T1, T2, [H1 | L1], L12, L2, T, H2);
+sympart2(T1, _, L1, L12, L2, T, H1) ->
+ {?SET(reverse(L1, [H1 | T1]), T),
+ ?SET(reverse(L12), T),
+ ?SET(reverse(L2), T)}.
+
+prod([[E | Es] | Xs], T, L) ->
+ prod(Es, Xs, T, prod(Xs, [E | T], L));
+prod([], T, L) ->
+ [list_to_tuple(reverse(T)) | L].
+
+prod([E | Es], Xs, T, L) ->
+ prod(Es, Xs, T, prod(Xs, [E | T], L));
+prod([], _Xs, _E, L) ->
+ L.
+
+constant_function([E | Es], X, L) ->
+ constant_function(Es, X, [{E,X} | L]);
+constant_function([], _X, L) ->
+ reverse(L).
+
+subset([H1 | T1], [H2 | T2]) when H1 > H2 ->
+ subset(T1, T2, H1);
+subset([H1 | T1], [H2 | T2]) when H1 == H2 ->
+ subset(T1, T2);
+subset(L1, _) ->
+ L1 =:= [].
+
+subset(T1, [H2 | T2], H1) when H1 > H2 ->
+ subset(T1, T2, H1);
+subset(T1, [H2 | T2], H1) when H1 == H2 ->
+ subset(T1, T2);
+subset(_, _, _) ->
+ false.
+
+disjoint([B | Bs], A, As) when A < B ->
+ disjoint(As, B, Bs);
+disjoint([B | _Bs], A, _As) when A == B ->
+ false;
+disjoint([_B | Bs], A, As) ->
+ disjoint(Bs, A, As);
+disjoint(_Bs, _A, _As) ->
+ true.
+
+%% Append sets that come in order, then "merge".
+lunion([[_] = S]) -> % optimization
+ S;
+lunion([[] | Ls]) ->
+ lunion(Ls);
+lunion([S | Ss]) ->
+ umerge(lunion(Ss, last(S), [S], []));
+lunion([]) ->
+ [].
+
+lunion([[E] = S | Ss], Last, SL, Ls) when E > Last -> % optimization
+ lunion(Ss, E, [S | SL], Ls);
+lunion([S | Ss], Last, SL, Ls) when hd(S) > Last ->
+ lunion(Ss, last(S), [S | SL], Ls);
+lunion([S | Ss], _Last, SL, Ls) ->
+ lunion(Ss, last(S), [S], [append(reverse(SL)) | Ls]);
+lunion([], _Last, SL, Ls) ->
+ [append(reverse(SL)) | Ls].
+
+%% The empty list is always the first list, if present.
+lintersection(_, []) ->
+ [];
+lintersection([S | Ss], S0) ->
+ lintersection(Ss, intersection(S, S0, []));
+lintersection([], S) ->
+ S.
+
+can_rel([S | Ss], L) ->
+ can_rel(Ss, L, S, S);
+can_rel([], L) ->
+ sort(L).
+
+can_rel(Ss, L, [E | Es], S) ->
+ can_rel(Ss, [{E, S} | L], Es, S);
+can_rel(Ss, L, _, _S) ->
+ can_rel(Ss, L).
+
+rel2family([{X,Y} | S]) ->
+ rel2fam(S, X, [Y], []);
+rel2family([]) ->
+ [].
+
+rel2fam([{X,Y} | S], X0, YL, L) when X0 == X ->
+ rel2fam(S, X0, [Y | YL], L);
+rel2fam([{X,Y} | S], X0, [A,B | YL], L) -> % optimization
+ rel2fam(S, X, [Y], [{X0,reverse(YL,[B,A])} | L]);
+rel2fam([{X,Y} | S], X0, YL, L) ->
+ rel2fam(S, X, [Y], [{X0,YL} | L]);
+rel2fam([], X, YL, L) ->
+ reverse([{X,reverse(YL)} | L]).
+
+dom([{X,_} | Es]) ->
+ dom([], X, Es);
+dom([] = L) ->
+ L.
+
+dom(L, X, [{X1,_} | Es]) when X == X1 ->
+ dom(L, X, Es);
+dom(L, X, [{Y,_} | Es]) ->
+ dom([X | L], Y, Es);
+dom(L, X, []) ->
+ reverse(L, [X]).
+
+ran([{_,Y} | Es], L) ->
+ ran(Es, [Y | L]);
+ran([], L) ->
+ usort(L).
+
+relprod(A, B) ->
+ usort(relprod1(A, B)).
+
+relprod1([{Ay,Ax} | A], B) ->
+ relprod1(B, Ay, Ax, A, []);
+relprod1(_A, _B) ->
+ [].
+
+relprod1([{Bx,_By} | B], Ay, Ax, A, L) when Ay > Bx ->
+ relprod1(B, Ay, Ax, A, L);
+relprod1([{Bx,By} | B], Ay, Ax, A, L) when Ay == Bx ->
+ relprod(B, Bx, By, A, [{Ax,By} | L], Ax, B, Ay);
+relprod1([{Bx,By} | B], _Ay, _Ax, A, L) ->
+ relprod2(B, Bx, By, A, L);
+relprod1(_B, _Ay, _Ax, _A, L) ->
+ L.
+
+relprod2(B, Bx, By, [{Ay, _Ax} | A], L) when Ay < Bx ->
+ relprod2(B, Bx, By, A, L);
+relprod2(B, Bx, By, [{Ay, Ax} | A], L) when Ay == Bx ->
+ relprod(B, Bx, By, A, [{Ax,By} | L], Ax, B, Ay);
+relprod2(B, _Bx, _By, [{Ay, Ax} | A], L) ->
+ relprod1(B, Ay, Ax, A, L);
+relprod2(_, _, _, _, L) ->
+ L.
+
+relprod(B0, Bx0, By0, A0, L, Ax, [{Bx,By} | B], Ay) when Ay == Bx ->
+ relprod(B0, Bx0, By0, A0, [{Ax,By} | L], Ax, B, Ay);
+relprod(B0, Bx0, By0, A0, L, _Ax, _B, _Ay) ->
+ relprod2(B0, Bx0, By0, A0, L).
+
+relprod_n([], _R, _EmptyG, _IsR) ->
+ {error, badarg};
+relprod_n(RL, R, EmptyR, IsR) ->
+ case domain_type(RL, ?ANYTYPE) of
+ Error = {error, _Reason} ->
+ Error;
+ DType ->
+ Empty = any(fun is_empty_set/1, RL) or EmptyR,
+ RType = range_type(RL, []),
+ Type = ?BINREL(DType, RType),
+ Prod =
+ case Empty of
+ true when DType =:= ?ANYTYPE; RType =:= ?ANYTYPE ->
+ empty_set();
+ true ->
+ ?SET([], Type);
+ false ->
+ TL = ?LIST((relprod_n(RL))),
+ Sz = length(RL),
+ Fun = fun({X,A}) -> {X, flat(Sz, A, [])} end,
+ ?SET(map(Fun, TL), Type)
+ end,
+ case IsR of
+ true -> relative_product(Prod, R);
+ false -> Prod
+ end
+ end.
+
+relprod_n([R | Rs]) ->
+ relprod_n(Rs, R).
+
+relprod_n([], R) ->
+ R;
+relprod_n([R | Rs], R0) ->
+ T = raise_element(R0, 1),
+ R1 = relative_product1(T, R),
+ NR = projection({external, fun({{X,A},AS}) -> {X,{A,AS}} end}, R1),
+ relprod_n(Rs, NR).
+
+flat(1, A, L) ->
+ list_to_tuple([A | L]);
+flat(N, {T,A}, L) ->
+ flat(N-1, T, [A | L]).
+
+domain_type([T | Ts], T0) when ?IS_SET(T) ->
+ case ?TYPE(T) of
+ ?BINREL(DT, _RT) ->
+ case unify_types(DT, T0) of
+ [] -> {error, type_mismatch};
+ T1 -> domain_type(Ts, T1)
+ end;
+ ?ANYTYPE ->
+ domain_type(Ts, T0);
+ _ -> {error, badarg}
+ end;
+domain_type([], T0) ->
+ T0.
+
+range_type([T | Ts], L) ->
+ case ?TYPE(T) of
+ ?BINREL(_DT, RT) ->
+ range_type(Ts, [RT | L]);
+ ?ANYTYPE ->
+ ?ANYTYPE
+ end;
+range_type([], L) ->
+ list_to_tuple(reverse(L)).
+
+converse([{A,B} | X], L) ->
+ converse(X, [{B,A} | L]);
+converse([], L) ->
+ sort(L).
+
+strict([{E1,E2} | Es], L) when E1 == E2 ->
+ strict(Es, L);
+strict([E | Es], L) ->
+ strict(Es, [E | L]);
+strict([], L) ->
+ reverse(L).
+
+weak(Es) ->
+ %% Not very efficient...
+ weak(Es, ran(Es, []), []).
+
+weak(Es=[{X,_} | _], [Y | Ys], L) when X > Y ->
+ weak(Es, Ys, [{Y,Y} | L]);
+weak(Es=[{X,_} | _], [Y | Ys], L) when X == Y ->
+ weak(Es, Ys, L);
+weak([E={X,Y} | Es], Ys, L) when X > Y ->
+ weak1(Es, Ys, [E | L], X);
+weak([E={X,Y} | Es], Ys, L) when X == Y ->
+ weak2(Es, Ys, [E | L], X);
+weak([E={X,_Y} | Es], Ys, L) -> % when X < _Y
+ weak2(Es, Ys, [E, {X,X} | L], X);
+weak([], [Y | Ys], L) ->
+ weak([], Ys, [{Y,Y} | L]);
+weak([], [], L) ->
+ reverse(L).
+
+weak1([E={X,Y} | Es], Ys, L, X0) when X > Y, X == X0 ->
+ weak1(Es, Ys, [E | L], X);
+weak1([E={X,Y} | Es], Ys, L, X0) when X == Y, X == X0 ->
+ weak2(Es, Ys, [E | L], X);
+weak1([E={X,_Y} | Es], Ys, L, X0) when X == X0 -> % when X < Y
+ weak2(Es, Ys, [E, {X,X} | L], X);
+weak1(Es, Ys, L, X) ->
+ weak(Es, Ys, [{X,X} | L]).
+
+weak2([E={X,_Y} | Es], Ys, L, X0) when X == X0 -> % when X < _Y
+ weak2(Es, Ys, [E | L], X);
+weak2(Es, Ys, L, _X) ->
+ weak(Es, Ys, L).
+
+extc(L, [D | Ds], C, Ts) ->
+ extc(L, Ds, C, Ts, D);
+extc(L, [], _C, _Ts) ->
+ L.
+
+extc(L, Ds, C, [{X,_Y} | Ts], D) when X < D ->
+ extc(L, Ds, C, Ts, D);
+extc(L, Ds, C, [{X,_Y} | Ts], D) when X == D ->
+ extc(L, Ds, C, Ts);
+extc(L, Ds, C, [{X,_Y} | Ts], D) ->
+ extc2([{D,C} | L], Ds, C, Ts, X);
+extc(L, Ds, C, [], D) ->
+ extc_tail([{D,C} | L], Ds, C).
+
+extc2(L, [D | Ds], C, Ts, X) when X > D ->
+ extc2([{D,C} | L], Ds, C, Ts, X);
+extc2(L, [D | Ds], C, Ts, X) when X == D ->
+ extc(L, Ds, C, Ts);
+extc2(L, [D | Ds], C, Ts, _X) ->
+ extc(L, Ds, C, Ts, D);
+extc2(L, [], _C, _Ts, _X) ->
+ L.
+
+extc_tail(L, [D | Ds], C) ->
+ extc_tail([{D,C} | L], Ds, C);
+extc_tail(L, [], _C) ->
+ L.
+
+is_a_func([{E,_} | Es], E0) when E /= E0 ->
+ is_a_func(Es, E);
+is_a_func(L, _E) ->
+ L =:= [].
+
+restrict_n(I, [T | Ts], Key, Keys, L) ->
+ case element(I, T) of
+ K when K < Key ->
+ restrict_n(I, Ts, Key, Keys, L);
+ K when K == Key ->
+ restrict_n(I, Ts, Key, Keys, [T | L]);
+ K ->
+ restrict_n(I, K, Ts, Keys, L, T)
+ end;
+restrict_n(_I, _Ts, _Key, _Keys, L) ->
+ L.
+
+restrict_n(I, K, Ts, [Key | Keys], L, E) when K > Key ->
+ restrict_n(I, K, Ts, Keys, L, E);
+restrict_n(I, K, Ts, [Key | Keys], L, E) when K == Key ->
+ restrict_n(I, Ts, Key, Keys, [E | L]);
+restrict_n(I, _K, Ts, [Key | Keys], L, _E) ->
+ restrict_n(I, Ts, Key, Keys, L);
+restrict_n(_I, _K, _Ts, _Keys, L, _E) ->
+ L.
+
+restrict([Key | Keys], Tuples) ->
+ restrict(Tuples, Key, Keys, []);
+restrict(_Keys, _Tuples) ->
+ [].
+
+restrict([{K,_E} | Ts], Key, Keys, L) when K < Key ->
+ restrict(Ts, Key, Keys, L);
+restrict([{K,E} | Ts], Key, Keys, L) when K == Key ->
+ restrict(Ts, Key, Keys, [E | L]);
+restrict([{K,E} | Ts], _Key, Keys, L) ->
+ restrict(Ts, K, Keys, L, E);
+restrict(_Ts, _Key, _Keys, L) ->
+ L.
+
+restrict(Ts, K, [Key | Keys], L, E) when K > Key ->
+ restrict(Ts, K, Keys, L, E);
+restrict(Ts, K, [Key | Keys], L, E) when K == Key ->
+ restrict(Ts, Key, Keys, [E | L]);
+restrict(Ts, _K, [Key | Keys], L, _E) ->
+ restrict(Ts, Key, Keys, L);
+restrict(_Ts, _K, _Keys, L, _E) ->
+ L.
+
+diff_restrict_n(I, [T | Ts], Key, Keys, L) ->
+ case element(I, T) of
+ K when K < Key ->
+ diff_restrict_n(I, Ts, Key, Keys, [T | L]);
+ K when K == Key ->
+ diff_restrict_n(I, Ts, Key, Keys, L);
+ K ->
+ diff_restrict_n(I, K, Ts, Keys, L, T)
+ end;
+diff_restrict_n(I, _Ts, _Key, _Keys, L) when I =:= 1 ->
+ reverse(L);
+diff_restrict_n(_I, _Ts, _Key, _Keys, L) ->
+ sort(L).
+
+diff_restrict_n(I, K, Ts, [Key | Keys], L, T) when K > Key ->
+ diff_restrict_n(I, K, Ts, Keys, L, T);
+diff_restrict_n(I, K, Ts, [Key | Keys], L, _T) when K == Key ->
+ diff_restrict_n(I, Ts, Key, Keys, L);
+diff_restrict_n(I, _K, Ts, [Key | Keys], L, T) ->
+ diff_restrict_n(I, Ts, Key, Keys, [T | L]);
+diff_restrict_n(I, _K, Ts, _Keys, L, T) when I =:= 1 ->
+ reverse(L, [T | Ts]);
+diff_restrict_n(_I, _K, Ts, _Keys, L, T) ->
+ sort([T | Ts ++ L]).
+
+diff_restrict([Key | Keys], Tuples) ->
+ diff_restrict(Tuples, Key, Keys, []);
+diff_restrict(_Keys, Tuples) ->
+ diff_restrict_tail(Tuples, []).
+
+diff_restrict([{K,E} | Ts], Key, Keys, L) when K < Key ->
+ diff_restrict(Ts, Key, Keys, [E | L]);
+diff_restrict([{K,_E} | Ts], Key, Keys, L) when K == Key ->
+ diff_restrict(Ts, Key, Keys, L);
+diff_restrict([{K,E} | Ts], _Key, Keys, L) ->
+ diff_restrict(Ts, K, Keys, L, E);
+diff_restrict(_Ts, _Key, _Keys, L) ->
+ L.
+
+diff_restrict(Ts, K, [Key | Keys], L, E) when K > Key ->
+ diff_restrict(Ts, K, Keys, L, E);
+diff_restrict(Ts, K, [Key | Keys], L, _E) when K == Key ->
+ diff_restrict(Ts, Key, Keys, L);
+diff_restrict(Ts, _K, [Key | Keys], L, E) ->
+ diff_restrict(Ts, Key, Keys, [E | L]);
+diff_restrict(Ts, _K, _Keys, L, E) ->
+ diff_restrict_tail(Ts, [E | L]).
+
+diff_restrict_tail([{_K,E} | Ts], L) ->
+ diff_restrict_tail(Ts, [E | L]);
+diff_restrict_tail(_Ts, L) ->
+ L.
+
+comp([], B) ->
+ check_function(B, []);
+comp(_A, []) ->
+ bad_function;
+comp(A0, [{Bx,By} | B]) ->
+ A = converse(A0, []),
+ check_function(A0, comp1(A, B, [], Bx, By)).
+
+comp1([{Ay,Ax} | A], B, L, Bx, By) when Ay == Bx ->
+ comp1(A, B, [{Ax,By} | L], Bx, By);
+comp1([{Ay,Ax} | A], B, L, Bx, _By) when Ay > Bx ->
+ comp2(A, B, L, Bx, Ay, Ax);
+comp1([{Ay,_Ax} | _A], _B, _L, Bx, _By) when Ay < Bx ->
+ bad_function;
+comp1([], B, L, Bx, _By) ->
+ check_function(Bx, B, L).
+
+comp2(A, [{Bx,_By} | B], L, Bx0, Ay, Ax) when Ay > Bx, Bx /= Bx0 ->
+ comp2(A, B, L, Bx, Ay, Ax);
+comp2(A, [{Bx,By} | B], L, _Bx0, Ay, Ax) when Ay == Bx ->
+ comp1(A, B, [{Ax,By} | L], Bx, By);
+comp2(_A, _B, _L, _Bx0, _Ay, _Ax) ->
+ bad_function.
+
+inverse1([{A,B} | X]) ->
+ inverse(X, A, [{B,A}]);
+inverse1([]) ->
+ [].
+
+inverse([{A,B} | X], A0, L) when A0 /= A ->
+ inverse(X, A, [{B,A} | L]);
+inverse([{A,_B} | _X], A0, _L) when A0 == A ->
+ bad_function;
+inverse([], _A0, L) ->
+ SL = [{V,_} | Es] = sort(L),
+ case is_a_func(Es, V) of
+ true -> SL;
+ false -> bad_function
+ end.
+
+%% Inlined.
+external_fun({external, Function}) when is_atom(Function) ->
+ false;
+external_fun({external, Fun}) ->
+ Fun;
+external_fun(_) ->
+ false.
+
+%% Inlined.
+element_type(?SET_OF(Type)) -> Type;
+element_type(Type) -> Type.
+
+subst(Ts, Fun, Type) ->
+ subst(Ts, Fun, Type, ?ANYTYPE, []).
+
+subst([T | Ts], Fun, Type, NType, L) ->
+ case setfun(T, Fun, Type, NType) of
+ {SD, ST} -> subst(Ts, Fun, Type, ST, [{T, SD} | L]);
+ Bad -> Bad
+ end;
+subst([], _Fun, _Type, NType, L) ->
+ {L, NType}.
+
+projection1([E | Es]) ->
+ projection1([], element(1, E), Es);
+projection1([] = L) ->
+ L.
+
+projection1(L, X, [E | Es]) ->
+ case element(1, E) of
+ X1 when X == X1 -> projection1(L, X, Es);
+ X1 -> projection1([X | L], X1, Es)
+ end;
+projection1(L, X, []) ->
+ reverse(L, [X]).
+
+projection_n([E | Es], I, L) ->
+ projection_n(Es, I, [element(I, E) | L]);
+projection_n([], _I, L) ->
+ usort(L).
+
+substitute_element([T | Ts], I, L) ->
+ substitute_element(Ts, I, [{T, element(I, T)} | L]);
+substitute_element(_, _I, L) ->
+ reverse(L).
+
+substitute([T | Ts], Fun, L) ->
+ substitute(Ts, Fun, [{T, Fun(T)} | L]);
+substitute(_, _Fun, L) ->
+ reverse(L).
+
+partition_n(I, [E | Ts]) ->
+ partition_n(I, Ts, element(I, E), [E], []);
+partition_n(_I, []) ->
+ [].
+
+partition_n(I, [E | Ts], K, Es, P) ->
+ case {element(I, E), Es} of
+ {K1, _} when K == K1 ->
+ partition_n(I, Ts, K, [E | Es], P);
+ {K1, [_]} -> % optimization
+ partition_n(I, Ts, K1, [E], [Es | P]);
+ {K1, _} ->
+ partition_n(I, Ts, K1, [E], [reverse(Es) | P])
+ end;
+partition_n(I, [], _K, Es, P) when I > 1 ->
+ sort([reverse(Es) | P]);
+partition_n(_I, [], _K, [_] = Es, P) -> % optimization
+ reverse(P, [Es]);
+partition_n(_I, [], _K, Es, P) ->
+ reverse(P, [reverse(Es)]).
+
+partition3_n(I, [T | Ts], Key, Keys, L1, L2) ->
+ case element(I, T) of
+ K when K < Key ->
+ partition3_n(I, Ts, Key, Keys, L1, [T | L2]);
+ K when K == Key ->
+ partition3_n(I, Ts, Key, Keys, [T | L1], L2);
+ K ->
+ partition3_n(I, K, Ts, Keys, L1, L2, T)
+ end;
+partition3_n(I, _Ts, _Key, _Keys, L1, L2) when I =:= 1 ->
+ [reverse(L1) | reverse(L2)];
+partition3_n(_I, _Ts, _Key, _Keys, L1, L2) ->
+ [sort(L1) | sort(L2)].
+
+partition3_n(I, K, Ts, [Key | Keys], L1, L2, T) when K > Key ->
+ partition3_n(I, K, Ts, Keys, L1, L2, T);
+partition3_n(I, K, Ts, [Key | Keys], L1, L2, T) when K == Key ->
+ partition3_n(I, Ts, Key, Keys, [T | L1], L2);
+partition3_n(I, _K, Ts, [Key | Keys], L1, L2, T) ->
+ partition3_n(I, Ts, Key, Keys, L1, [T | L2]);
+partition3_n(I, _K, Ts, _Keys, L1, L2, T) when I =:= 1 ->
+ [reverse(L1) | reverse(L2, [T | Ts])];
+partition3_n(_I, _K, Ts, _Keys, L1, L2, T) ->
+ [sort(L1) | sort([T | Ts ++ L2])].
+
+partition3([Key | Keys], Tuples) ->
+ partition3(Tuples, Key, Keys, [], []);
+partition3(_Keys, Tuples) ->
+ partition3_tail(Tuples, [], []).
+
+partition3([{K,E} | Ts], Key, Keys, L1, L2) when K < Key ->
+ partition3(Ts, Key, Keys, L1, [E | L2]);
+partition3([{K,E} | Ts], Key, Keys, L1, L2) when K == Key ->
+ partition3(Ts, Key, Keys, [E | L1], L2);
+partition3([{K,E} | Ts], _Key, Keys, L1, L2) ->
+ partition3(Ts, K, Keys, L1, L2, E);
+partition3(_Ts, _Key, _Keys, L1, L2) ->
+ [L1 | L2].
+
+partition3(Ts, K, [Key | Keys], L1, L2, E) when K > Key ->
+ partition3(Ts, K, Keys, L1, L2, E);
+partition3(Ts, K, [Key | Keys], L1, L2, E) when K == Key ->
+ partition3(Ts, Key, Keys, [E | L1], L2);
+partition3(Ts, _K, [Key | Keys], L1, L2, E) ->
+ partition3(Ts, Key, Keys, L1, [E | L2]);
+partition3(Ts, _K, _Keys, L1, L2, E) ->
+ partition3_tail(Ts, L1, [E | L2]).
+
+partition3_tail([{_K,E} | Ts], L1, L2) ->
+ partition3_tail(Ts, L1, [E | L2]);
+partition3_tail(_Ts, L1, L2) ->
+ [L1 | L2].
+
+replace([E | Es], F, L) ->
+ replace(Es, F, [F(E) | L]);
+replace(_, _F, L) ->
+ sort(L).
+
+mul_relprod([T | Ts], I, R) when ?IS_SET(T) ->
+ P = raise_element(R, I),
+ F = relative_product1(P, T),
+ [F | mul_relprod(Ts, I+1, R)];
+mul_relprod([], _I, _R) ->
+ [].
+
+raise_element(R, I) ->
+ L = sort(I =/= 1, rearr(?LIST(R), I, [])),
+ Type = ?TYPE(R),
+ ?SET(L, ?BINREL(?REL_TYPE(I, Type), Type)).
+
+rearr([E | Es], I, L) ->
+ rearr(Es, I, [{element(I, E), E} | L]);
+rearr([], _I, L) ->
+ L.
+
+join_element(E1, E2) ->
+ [_ | L2] = tuple_to_list(E2),
+ list_to_tuple(tuple_to_list(E1) ++ L2).
+
+join_element(E1, E2, I2) ->
+ tuple_to_list(E1) ++ join_element2(tuple_to_list(E2), 1, I2).
+
+join_element2([B | Bs], C, I2) when C =/= I2 ->
+ [B | join_element2(Bs, C+1, I2)];
+join_element2([_ | Bs], _C, _I2) ->
+ Bs.
+
+family2rel([{X,S} | F], L) ->
+ fam2rel(F, L, X, S);
+family2rel([], L) ->
+ reverse(L).
+
+fam2rel(F, L, X, [Y | Ys]) ->
+ fam2rel(F, [{X,Y} | L], X, Ys);
+fam2rel(F, L, _X, _) ->
+ family2rel(F, L).
+
+fam_spec([{_,S}=E | F], Fun, Type, L) ->
+ case Fun(?SET(S, Type)) of
+ true ->
+ fam_spec(F, Fun, Type, [E | L]);
+ false ->
+ fam_spec(F, Fun, Type, L);
+ _ ->
+ badarg
+ end;
+fam_spec([], _Fun, _Type, L) ->
+ reverse(L).
+
+fam_specification([{_,S}=E | F], Fun, L) ->
+ case Fun(S) of
+ true ->
+ fam_specification(F, Fun, [E | L]);
+ false ->
+ fam_specification(F, Fun, L);
+ _ ->
+ badarg
+ end;
+fam_specification([], _Fun, L) ->
+ reverse(L).
+
+un_of_fam([{_X,S} | F], L) ->
+ un_of_fam(F, [S | L]);
+un_of_fam([], L) ->
+ lunion(sort(L)).
+
+int_of_fam([{_,S} | F]) ->
+ int_of_fam(F, [S]);
+int_of_fam([]) ->
+ badarg.
+
+int_of_fam([{_,S} | F], L) ->
+ int_of_fam(F, [S | L]);
+int_of_fam([], [L | Ls]) ->
+ lintersection(Ls, L).
+
+fam_un([{X,S} | F], L) ->
+ fam_un(F, [{X, lunion(S)} | L]);
+fam_un([], L) ->
+ reverse(L).
+
+fam_int([{X, [S | Ss]} | F], L) ->
+ fam_int(F, [{X, lintersection(Ss, S)} | L]);
+fam_int([{_X,[]} | _F], _L) ->
+ badarg;
+fam_int([], L) ->
+ reverse(L).
+
+fam_dom([{X,S} | F], L) ->
+ fam_dom(F, [{X, dom(S)} | L]);
+fam_dom([], L) ->
+ reverse(L).
+
+fam_ran([{X,S} | F], L) ->
+ fam_ran(F, [{X, ran(S, [])} | L]);
+fam_ran([], L) ->
+ reverse(L).
+
+fam_union(F1 = [{A,_AS} | _AL], [B1={B,_BS} | BL], L) when A > B ->
+ fam_union(F1, BL, [B1 | L]);
+fam_union([{A,AS} | AL], [{B,BS} | BL], L) when A == B ->
+ fam_union(AL, BL, [{A, umerge(AS, BS)} | L]);
+fam_union([A1 | AL], F2, L) ->
+ fam_union(AL, F2, [A1 | L]);
+fam_union(_, F2, L) ->
+ reverse(L, F2).
+
+fam_intersect(F1 = [{A,_AS} | _AL], [{B,_BS} | BL], L) when A > B ->
+ fam_intersect(F1, BL, L);
+fam_intersect([{A,AS} | AL], [{B,BS} | BL], L) when A == B ->
+ fam_intersect(AL, BL, [{A, intersection(AS, BS, [])} | L]);
+fam_intersect([_A1 | AL], F2, L) ->
+ fam_intersect(AL, F2, L);
+fam_intersect(_, _, L) ->
+ reverse(L).
+
+fam_difference(F1 = [{A,_AS} | _AL], [{B,_BS} | BL], L) when A > B ->
+ fam_difference(F1, BL, L);
+fam_difference([{A,AS} | AL], [{B,BS} | BL], L) when A == B ->
+ fam_difference(AL, BL, [{A, difference(AS, BS, [])} | L]);
+fam_difference([A1 | AL], F2, L) ->
+ fam_difference(AL, F2, [A1 | L]);
+fam_difference(F1, _, L) ->
+ reverse(L, F1).
+
+check_function([{X,_} | XL], R) ->
+ check_function(X, XL, R);
+check_function([], R) ->
+ R.
+
+check_function(X0, [{X,_} | XL], R) when X0 /= X ->
+ check_function(X, XL, R);
+check_function(X0, [{X,_} | _XL], _R) when X0 == X ->
+ bad_function;
+check_function(_X0, [], R) ->
+ R.
+
+fam_partition_n(I, [E | Ts]) ->
+ fam_partition_n(I, Ts, element(I, E), [E], []);
+fam_partition_n(_I, []) ->
+ [].
+
+fam_partition_n(I, [E | Ts], K, Es, P) ->
+ case {element(I, E), Es} of
+ {K1, _} when K == K1 ->
+ fam_partition_n(I, Ts, K, [E | Es], P);
+ {K1, [_]} -> % optimization
+ fam_partition_n(I, Ts, K1, [E], [{K,Es} | P]);
+ {K1, _} ->
+ fam_partition_n(I, Ts, K1, [E], [{K,reverse(Es)} | P])
+ end;
+fam_partition_n(_I, [], K, [_] = Es, P) -> % optimization
+ reverse(P, [{K,Es}]);
+fam_partition_n(_I, [], K, Es, P) ->
+ reverse(P, [{K,reverse(Es)}]).
+
+fam_partition([{K,Vs} | Ts], Sort) ->
+ fam_partition(Ts, K, [Vs], [], Sort);
+fam_partition([], _Sort) ->
+ [].
+
+fam_partition([{K1,V} | Ts], K, Vs, P, S) when K1 == K ->
+ fam_partition(Ts, K, [V | Vs], P, S);
+fam_partition([{K1,V} | Ts], K, [_] = Vs, P, S) -> % optimization
+ fam_partition(Ts, K1, [V], [{K, Vs} | P], S);
+fam_partition([{K1,V} | Ts], K, Vs, P, S) ->
+ fam_partition(Ts, K1, [V], [{K, sort(S, Vs)} | P], S);
+fam_partition([], K, [_] = Vs, P, _S) -> % optimization
+ [{K, Vs} | P];
+fam_partition([], K, Vs, P, S) ->
+ [{K, sort(S, Vs)} | P].
+
+fam_proj([{X,S} | F], Fun, Type, NType, L) ->
+ case setfun(S, Fun, Type, NType) of
+ {SD, ST} -> fam_proj(F, Fun, Type, ST, [{X, SD} | L]);
+ Bad -> Bad
+ end;
+fam_proj([], _Fun, _Type, NType, L) ->
+ {reverse(L), NType}.
+
+setfun(T, Fun, Type, NType) ->
+ case Fun(term2set(T, Type)) of
+ NS when ?IS_SET(NS) ->
+ case unify_types(NType, ?SET_OF(?TYPE(NS))) of
+ [] -> type_mismatch;
+ NT -> {?LIST(NS), NT}
+ end;
+ NS when ?IS_ORDSET(NS) ->
+ case unify_types(NType, NT = ?ORDTYPE(NS)) of
+ [] -> type_mismatch;
+ NT -> {?ORDDATA(NS), NT}
+ end;
+ _ ->
+ badarg
+ end.
+
+%% Inlined.
+term2set(L, Type) when is_list(L) ->
+ ?SET(L, Type);
+term2set(T, Type) ->
+ ?ORDSET(T, Type).
+
+fam2digraph(F, G) ->
+ Fun = fun({From, ToL}) ->
+ digraph:add_vertex(G, From),
+ Fun2 = fun(To) ->
+ digraph:add_vertex(G, To),
+ case digraph:add_edge(G, From, To) of
+ {error, {bad_edge, _}} ->
+ throw({error, cyclic});
+ _ ->
+ true
+ end
+ end,
+ foreach(Fun2, ToL)
+ end,
+ foreach(Fun, to_external(F)),
+ G.
+
+digraph_family(G) ->
+ Vs = sort(digraph:vertices(G)),
+ digraph_fam(Vs, Vs, G, []).
+
+digraph_fam([V | Vs], V0, G, L) when V /= V0 ->
+ Ns = sort(digraph:out_neighbours(G, V)),
+ digraph_fam(Vs, V, G, [{V,Ns} | L]);
+digraph_fam([], _V0, _G, L) ->
+ reverse(L).
+
+%% -> boolean()
+check_fun(T, F, FunT) ->
+ true = is_type(FunT),
+ {NT, _MaxI} = number_tuples(T, 1),
+ L = flatten(tuple2list(F(NT))),
+ has_hole(L, 1).
+
+number_tuples(T, N) when is_tuple(T) ->
+ {L, NN} = mapfoldl(fun number_tuples/2, N, tuple_to_list(T)),
+ {list_to_tuple(L), NN};
+number_tuples(_, N) ->
+ {N, N+1}.
+
+tuple2list(T) when is_tuple(T) ->
+ map(fun tuple2list/1, tuple_to_list(T));
+tuple2list(C) ->
+ [C].
+
+has_hole([I | Is], I0) when I =< I0 -> has_hole(Is, erlang:max(I+1, I0));
+has_hole(Is, _I) -> Is =/= [].
+
+%% Optimization. Same as check_fun/3, but for integers.
+check_for_sort(T, _I) when T =:= ?ANYTYPE ->
+ empty;
+check_for_sort(T, I) when ?IS_RELATION(T), I =< ?REL_ARITY(T), I >= 1 ->
+ I > 1;
+check_for_sort(_T, _I) ->
+ error.
+
+inverse_substitution(L, Fun, Sort) ->
+ %% One easily sees that the inverse of the tuples created by
+ %% applying Fun need to be sorted iff the tuples created by Fun
+ %% need to be sorted.
+ sort(Sort, fun_rearr(L, Fun, [])).
+
+fun_rearr([E | Es], Fun, L) ->
+ fun_rearr(Es, Fun, [{Fun(E), E} | L]);
+fun_rearr([], _Fun, L) ->
+ L.
+
+sets_to_list(Ss) ->
+ map(fun(S) when ?IS_SET(S) -> ?LIST(S) end, Ss).
+
+types([], L) ->
+ list_to_tuple(reverse(L));
+types([S | _Ss], _L) when ?TYPE(S) =:= ?ANYTYPE ->
+ ?ANYTYPE;
+types([S | Ss], L) ->
+ types(Ss, [?TYPE(S) | L]).
+
+%% Inlined.
+unify_types(T, T) -> T;
+unify_types(Type1, Type2) ->
+ catch unify_types1(Type1, Type2).
+
+unify_types1(Atom, Atom) when ?IS_ATOM_TYPE(Atom) ->
+ Atom;
+unify_types1(?ANYTYPE, Type) ->
+ Type;
+unify_types1(Type, ?ANYTYPE) ->
+ Type;
+unify_types1(?SET_OF(Type1), ?SET_OF(Type2)) ->
+ [unify_types1(Type1, Type2)];
+unify_types1(T1, T2) when tuple_size(T1) =:= tuple_size(T2) ->
+ unify_typesl(tuple_size(T1), T1, T2, []);
+unify_types1(_T1, _T2) ->
+ throw([]).
+
+unify_typesl(0, _T1, _T2, L) ->
+ list_to_tuple(L);
+unify_typesl(N, T1, T2, L) ->
+ T = unify_types1(?REL_TYPE(N, T1), ?REL_TYPE(N, T2)),
+ unify_typesl(N-1, T1, T2, [T | L]).
+
+%% inlined.
+match_types(T, T) -> true;
+match_types(Type1, Type2) -> match_types1(Type1, Type2).
+
+match_types1(Atom, Atom) when ?IS_ATOM_TYPE(Atom) ->
+ true;
+match_types1(?ANYTYPE, _) ->
+ true;
+match_types1(_, ?ANYTYPE) ->
+ true;
+match_types1(?SET_OF(Type1), ?SET_OF(Type2)) ->
+ match_types1(Type1, Type2);
+match_types1(T1, T2) when tuple_size(T1) =:= tuple_size(T2) ->
+ match_typesl(tuple_size(T1), T1, T2);
+match_types1(_T1, _T2) ->
+ false.
+
+match_typesl(0, _T1, _T2) ->
+ true;
+match_typesl(N, T1, T2) ->
+ case match_types1(?REL_TYPE(N, T1), ?REL_TYPE(N, T2)) of
+ true -> match_typesl(N-1, T1, T2);
+ false -> false
+ end.
+
+sort(true, L) ->
+ sort(L);
+sort(false, L) ->
+ reverse(L).
diff --git a/lib/tools/test/tools_bench.spec b/lib/tools/test/tools_bench.spec
new file mode 100644
index 0000000000..ef08fd68a8
--- /dev/null
+++ b/lib/tools/test/tools_bench.spec
@@ -0,0 +1 @@
+{suites,"../tools_test",[prof_bench_SUITE]}.
diff --git a/make/otp_patch_solve_forward_merge_version b/make/otp_patch_solve_forward_merge_version
index 45a4fb75db..ec635144f6 100644
--- a/make/otp_patch_solve_forward_merge_version
+++ b/make/otp_patch_solve_forward_merge_version
@@ -1 +1 @@
-8
+9
diff --git a/otp_versions.table b/otp_versions.table
index 42c57a68fa..566aa3cb4c 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,5 @@
+OTP-21.3.7 : ssh-4.7.6 # asn1-5.0.8 common_test-1.17.1 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.6 erl_docgen-0.9 erl_interface-3.11.2 erts-10.3.4 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 kernel-6.3.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.5 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssl-9.2.2 stdlib-3.8.1 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1 wx-1.8.7 xmerl-1.3.20 :
+OTP-21.3.6 : ssl-9.2.2 # asn1-5.0.8 common_test-1.17.1 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.6 erl_docgen-0.9 erl_interface-3.11.2 erts-10.3.4 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 kernel-6.3.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.5 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.5 stdlib-3.8.1 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1 wx-1.8.7 xmerl-1.3.20 :
OTP-21.3.5 : diameter-2.2.1 erts-10.3.4 inets-7.0.7 # asn1-5.0.8 common_test-1.17.1 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 edoc-0.10 eldap-1.2.6 erl_docgen-0.9 erl_interface-3.11.2 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 jinterface-1.9.1 kernel-6.3.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.5 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.5 ssl-9.2.1 stdlib-3.8.1 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1 wx-1.8.7 xmerl-1.3.20 :
OTP-21.3.4 : common_test-1.17.1 crypto-4.4.2 erl_interface-3.11.2 erts-10.3.3 ssh-4.7.5 # asn1-5.0.8 compiler-7.3.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2 edoc-0.10 eldap-1.2.6 erl_docgen-0.9 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.6 jinterface-1.9.1 kernel-6.3.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.5 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssl-9.2.1 stdlib-3.8.1 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1 wx-1.8.7 xmerl-1.3.20 :
OTP-21.3.3 : erts-10.3.2 kernel-6.3.1 stdlib-3.8.1 # asn1-5.0.8 common_test-1.17 compiler-7.3.2 crypto-4.4.1 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2 edoc-0.10 eldap-1.2.6 erl_docgen-0.9 erl_interface-3.11.1 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.6 jinterface-1.9.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.5 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.4 ssl-9.2.1 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1 wx-1.8.7 xmerl-1.3.20 :
diff --git a/system/doc/system_principles/create_target.xmlsrc b/system/doc/system_principles/create_target.xmlsrc
index 47b84e5760..ea0d938936 100644
--- a/system/doc/system_principles/create_target.xmlsrc
+++ b/system/doc/system_principles/create_target.xmlsrc
@@ -352,7 +352,7 @@ fi
START_ERL_DATA=${1:-$RELDIR/start_erl.data}
$ROOTDIR/bin/run_erl -daemon /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR\
-$RELDIR $START_ERL_DATA -heart</code>
+$RELDIR $START_ERL_DATA -heart"</code>
<p>We use the simplest possible <c>sys.config</c>, which we
store in <c>releases/FIRST</c>:</p>
<code type="none">