aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/configure.in2
-rw-r--r--erts/emulator/beam/atom.c2
-rw-r--r--erts/emulator/beam/erl_bif_info.c5
-rw-r--r--erts/emulator/beam/erl_lock_check.c2
-rw-r--r--erts/emulator/beam/erl_time.h7
-rw-r--r--erts/emulator/beam/erl_time_sup.c147
-rw-r--r--erts/emulator/beam/sys.h4
-rw-r--r--erts/emulator/pcre/local_config.h2
-rw-r--r--erts/emulator/pcre/pcre-8.40.tar.bz2bin1560119 -> 0 bytes
-rw-r--r--erts/emulator/pcre/pcre-8.41.tar.bz2bin0 -> 1561874 bytes
-rw-r--r--erts/emulator/pcre/pcre.h4
-rw-r--r--erts/emulator/pcre/pcre_compile.c16
-rw-r--r--erts/emulator/pcre/pcre_dfa_exec.c4
-rw-r--r--erts/emulator/pcre/pcre_exec.c2
-rw-r--r--erts/emulator/pcre/pcre_internal.h11
-rw-r--r--erts/emulator/pcre/pcre_jit_compile.c958
-rw-r--r--erts/emulator/pcre/pcre_tables.c4
-rw-r--r--erts/emulator/pcre/pcre_ucd.c14
-rw-r--r--erts/emulator/sys/unix/erl_unix_sys.h4
-rw-r--r--erts/emulator/test/bif_SUITE.erl2
-rw-r--r--erts/emulator/test/binary_SUITE.erl3
-rw-r--r--erts/emulator/test/port_SUITE.erl6
-rw-r--r--erts/emulator/test/statistics_SUITE.erl34
-rw-r--r--erts/test/system_smoke.spec1
-rw-r--r--lib/compiler/src/beam_validator.erl14
-rw-r--r--lib/diameter/doc/src/diameter.xml221
-rw-r--r--lib/diameter/doc/src/diameter_app.xml9
-rw-r--r--lib/diameter/doc/src/diameter_codec.xml18
-rw-r--r--lib/diameter/doc/src/diameter_sctp.xml3
-rw-r--r--lib/diameter/doc/src/diameter_tcp.xml13
-rw-r--r--lib/diameter/doc/src/seealso.ent5
-rw-r--r--lib/diameter/doc/standard/rfc7683.txt2355
-rw-r--r--lib/diameter/examples/code/client.erl10
-rw-r--r--lib/diameter/examples/code/client_cb.erl29
-rw-r--r--lib/diameter/src/Makefile6
-rw-r--r--lib/diameter/src/base/diameter.erl56
-rw-r--r--lib/diameter/src/base/diameter_callback.erl6
-rw-r--r--lib/diameter/src/base/diameter_codec.erl6
-rw-r--r--lib/diameter/src/base/diameter_config.erl299
-rw-r--r--lib/diameter/src/base/diameter_gen.erl342
-rw-r--r--lib/diameter/src/base/diameter_lib.erl2
-rw-r--r--lib/diameter/src/base/diameter_peer.erl6
-rw-r--r--lib/diameter/src/base/diameter_peer_fsm.erl66
-rw-r--r--lib/diameter/src/base/diameter_service.erl71
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl90
-rw-r--r--lib/diameter/src/base/diameter_watchdog.erl39
-rw-r--r--lib/diameter/src/compiler/diameter_dict_util.erl4
-rw-r--r--lib/diameter/src/dict/doic_rfc7683.dia50
-rw-r--r--lib/diameter/src/modules.mk1
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl92
-rw-r--r--lib/diameter/src/transport/diameter_tcp.erl94
-rw-r--r--lib/diameter/test/diameter_codec_SUITE.erl2
-rw-r--r--lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl2
-rw-r--r--lib/diameter/test/diameter_codec_test.erl5
-rw-r--r--lib/diameter/test/diameter_event_SUITE.erl11
-rw-r--r--lib/diameter/test/diameter_examples_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl248
-rw-r--r--lib/diameter/test/diameter_transport_SUITE.erl39
-rw-r--r--lib/kernel/src/user_drv.erl31
-rw-r--r--lib/ssl/src/ssl_alert.erl24
-rw-r--r--lib/ssl/src/ssl_connection.erl23
-rw-r--r--lib/ssl/src/ssl_manager.erl2
-rw-r--r--lib/stdlib/doc/src/ets.xml4
-rw-r--r--lib/stdlib/test/re_SUITE_data/testoutput14
-rw-r--r--lib/stdlib/test/re_SUITE_data/testoutput84
-rw-r--r--lib/tools/doc/src/lcnt.xml4
-rwxr-xr-xscripts/run-dialyzer11
67 files changed, 4267 insertions, 1292 deletions
diff --git a/erts/configure.in b/erts/configure.in
index 85351ab7c7..913315e402 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -1784,6 +1784,8 @@ AC_CHECK_HEADER(sys/resource.h,
[#include <sys/resource.h>])],
[],[])
+AC_CHECK_FUNCS([getrusage])
+
dnl Check if we have kernel poll support
have_kernel_poll=no
AC_CHECK_HEADER(sys/event.h, have_kernel_poll=kqueue)
diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c
index c2d78aaccb..38e02c386f 100644
--- a/erts/emulator/beam/atom.c
+++ b/erts/emulator/beam/atom.c
@@ -138,7 +138,7 @@ atom_hash(Atom* obj)
while(len--) {
v = *p++;
/* latin1 clutch for r16 */
- if ((v & 0xFE) == 0xC2 && (*p & 0xC0) == 0x80) {
+ if (len && (v & 0xFE) == 0xC2 && (*p & 0xC0) == 0x80) {
v = (v << 6) | (*p & 0x3F);
p++; len--;
}
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index e5d7efcc72..0547b4d75c 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -48,6 +48,7 @@
#include "erl_map.h"
#define ERTS_PTAB_WANT_DEBUG_FUNCS__
#include "erl_ptab.h"
+#include "erl_time.h"
#ifdef HIPE
#include "hipe_arch.h"
#endif
@@ -3547,7 +3548,7 @@ BIF_RETTYPE statistics_1(BIF_ALIST_1)
ErtsMonotonicTime u1, u2;
Eterm b1, b2;
Uint hsz;
- elapsed_time_both(&u1, NULL, &u2, NULL);
+ erts_runtime_elapsed_both(&u1, NULL, &u2, NULL);
hsz = 3; /* 2-tuple */
(void) erts_bld_monotonic_time(NULL, &hsz, u1);
(void) erts_bld_monotonic_time(NULL, &hsz, u2);
@@ -3563,7 +3564,7 @@ BIF_RETTYPE statistics_1(BIF_ALIST_1)
ErtsMonotonicTime w1, w2;
Eterm b1, b2;
Uint hsz;
- wall_clock_elapsed_time_both(&w1, &w2);
+ erts_wall_clock_elapsed_both(&w1, &w2);
hsz = 3; /* 2-tuple */
(void) erts_bld_monotonic_time(NULL, &hsz, w1);
(void) erts_bld_monotonic_time(NULL, &hsz, w2);
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index cf091ee43f..189c88ac4a 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -171,9 +171,9 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "xports_list_pre_alloc_lock", "address" },
{ "inet_buffer_stack_lock", NULL },
{ "system_block", NULL },
- { "timeofday", NULL },
{ "get_time", NULL },
{ "get_corrected_time", NULL },
+ { "runtime", NULL },
{ "breakpoints", NULL },
{ "pix_lock", "address" },
{ "run_queues_lists", NULL },
diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h
index ccc5526664..27164d50a0 100644
--- a/erts/emulator/beam/erl_time.h
+++ b/erts/emulator/beam/erl_time.h
@@ -130,6 +130,13 @@ Eterm erts_get_monotonic_end_time(struct process *c_p);
Eterm erts_monotonic_time_source(struct process*c_p);
Eterm erts_system_time_source(struct process*c_p);
+void erts_runtime_elapsed_both(ErtsMonotonicTime *ms_user,
+ ErtsMonotonicTime *ms_sys,
+ ErtsMonotonicTime *ms_user_diff,
+ ErtsMonotonicTime *ms_sys_diff);
+void erts_wall_clock_elapsed_both(ErtsMonotonicTime *total,
+ ErtsMonotonicTime *diff);
+
#ifdef SYS_CLOCK_RESOLUTION
#define ERTS_CLKTCK_RESOLUTION ((ErtsMonotonicTime) (SYS_CLOCK_RESOLUTION*1000))
#else
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index f6bb52dde1..979c03fd43 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -36,12 +36,29 @@
#include "erl_driver.h"
#include "erl_nif.h"
-static erts_smp_mtx_t erts_timeofday_mtx;
static erts_smp_mtx_t erts_get_time_mtx;
-static SysTimes t_start; /* Used in elapsed_time_both */
-static ErtsMonotonicTime prev_wall_clock_elapsed; /* Used in wall_clock_elapsed_time_both */
-static ErtsMonotonicTime previous_now; /* Used in get_now */
+ /* used by erts_runtime_elapsed_both */
+typedef struct {
+ erts_smp_mtx_t mtx;
+ ErtsMonotonicTime user;
+ ErtsMonotonicTime sys;
+} ErtsRunTimePrevData;
+
+static union {
+ ErtsRunTimePrevData data;
+ char align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsRunTimePrevData))];
+} runtime_prev erts_align_attribute(ERTS_CACHE_LINE_SIZE);
+
+static union {
+ erts_smp_atomic64_t time;
+ char align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(erts_smp_atomic64_t))];
+} wall_clock_prev erts_align_attribute(ERTS_CACHE_LINE_SIZE);
+
+static union {
+ erts_smp_atomic64_t time;
+ char align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(erts_smp_atomic64_t))];
+} now_prev erts_align_attribute(ERTS_CACHE_LINE_SIZE);
static ErtsMonitor *time_offset_monitors = NULL;
static Uint no_time_offset_monitors = 0;
@@ -954,10 +971,12 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
ASSERT(ERTS_MONOTONIC_TIME_MIN < ERTS_MONOTONIC_TIME_MAX);
- erts_smp_mtx_init(&erts_timeofday_mtx, "timeofday", NIL,
- ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
erts_smp_mtx_init(&erts_get_time_mtx, "get_time", NIL,
ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
+ erts_smp_mtx_init(&runtime_prev.data.mtx, "runtime", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
+ runtime_prev.data.user = 0;
+ runtime_prev.data.sys = 0;
time_sup.r.o.correction = time_correction;
time_sup.r.o.warp_mode = time_warp_mode;
@@ -1157,9 +1176,13 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
time_sup.f.c.last_not_corrected_time = 0;
}
- prev_wall_clock_elapsed = 0;
+ erts_smp_atomic64_init_nob(&wall_clock_prev.time,
+ (erts_aint64_t) 0);
+
+ erts_smp_atomic64_init_nob(
+ &now_prev.time,
+ (erts_aint64_t) ERTS_MONOTONIC_TO_USEC(get_time_offset()));
- previous_now = ERTS_MONOTONIC_TO_USEC(get_time_offset());
#ifdef DEBUG
time_sup_initialized = 1;
@@ -1289,36 +1312,65 @@ erts_finalize_time_offset(void)
/* info functions */
void
-elapsed_time_both(ErtsMonotonicTime *ms_user, ErtsMonotonicTime *ms_sys,
- ErtsMonotonicTime *ms_user_diff, ErtsMonotonicTime *ms_sys_diff)
+erts_runtime_elapsed_both(ErtsMonotonicTime *ms_user, ErtsMonotonicTime *ms_sys,
+ ErtsMonotonicTime *ms_user_diff, ErtsMonotonicTime *ms_sys_diff)
{
- ErtsMonotonicTime prev_total_user, prev_total_sys;
- ErtsMonotonicTime total_user, total_sys;
+ ErtsMonotonicTime prev_user, prev_sys, user, sys;
+
+#ifdef HAVE_GETRUSAGE
+
+ struct rusage now;
+
+ if (getrusage(RUSAGE_SELF, &now) != 0) {
+ erts_exit(ERTS_ABORT_EXIT, "getrusage(RUSAGE_SELF, _) failed: %d\n", errno);
+ return;
+ }
+
+ user = (ErtsMonotonicTime) now.ru_utime.tv_sec;
+ user *= (ErtsMonotonicTime) 1000000;
+ user += (ErtsMonotonicTime) now.ru_utime.tv_usec;
+ user /= (ErtsMonotonicTime) 1000;
+
+ sys = (ErtsMonotonicTime) now.ru_stime.tv_sec;
+ sys *= (ErtsMonotonicTime) 1000000;
+ sys += (ErtsMonotonicTime) now.ru_stime.tv_usec;
+ sys /= (ErtsMonotonicTime) 1000;
+
+#else
+
SysTimes now;
sys_times(&now);
- total_user = (ErtsMonotonicTime) ((now.tms_utime * 1000) / SYS_CLK_TCK);
- total_sys = (ErtsMonotonicTime) ((now.tms_stime * 1000) / SYS_CLK_TCK);
+ user = (ErtsMonotonicTime) now.tms_utime;
+ user *= (ErtsMonotonicTime) 1000;
+ user /= (ErtsMonotonicTime) SYS_CLK_TCK;
- if (ms_user != NULL)
- *ms_user = total_user;
- if (ms_sys != NULL)
- *ms_sys = total_sys;
+ sys = (ErtsMonotonicTime) now.tms_stime;
+ sys *= (ErtsMonotonicTime) 1000;
+ sys /= (ErtsMonotonicTime) SYS_CLK_TCK;
+
+#endif
+
+ if (ms_user)
+ *ms_user = user;
+ if (ms_sys)
+ *ms_sys = sys;
if (ms_user_diff || ms_sys_diff) {
- erts_smp_mtx_lock(&erts_timeofday_mtx);
-
- prev_total_user = (ErtsMonotonicTime) ((t_start.tms_utime * 1000) / SYS_CLK_TCK);
- prev_total_sys = (ErtsMonotonicTime) ((t_start.tms_stime * 1000) / SYS_CLK_TCK);
- t_start = now;
-
- erts_smp_mtx_unlock(&erts_timeofday_mtx);
+
+ erts_smp_mtx_lock(&runtime_prev.data.mtx);
- if (ms_user_diff != NULL)
- *ms_user_diff = total_user - prev_total_user;
-
- if (ms_sys_diff != NULL)
- *ms_sys_diff = total_sys - prev_total_sys;
+ prev_user = runtime_prev.data.user;
+ prev_sys = runtime_prev.data.sys;
+ runtime_prev.data.user = user;
+ runtime_prev.data.sys = sys;
+
+ erts_smp_mtx_unlock(&runtime_prev.data.mtx);
+
+ if (ms_user_diff)
+ *ms_user_diff = user - prev_user;
+ if (ms_sys_diff)
+ *ms_sys_diff = sys - prev_sys;
}
}
@@ -1326,7 +1378,7 @@ elapsed_time_both(ErtsMonotonicTime *ms_user, ErtsMonotonicTime *ms_sys,
/* wall clock routines */
void
-wall_clock_elapsed_time_both(ErtsMonotonicTime *ms_total, ErtsMonotonicTime *ms_diff)
+erts_wall_clock_elapsed_both(ErtsMonotonicTime *ms_total, ErtsMonotonicTime *ms_diff)
{
ErtsMonotonicTime now, elapsed;
@@ -1334,16 +1386,18 @@ wall_clock_elapsed_time_both(ErtsMonotonicTime *ms_total, ErtsMonotonicTime *ms_
update_last_mtime(NULL, now);
elapsed = ERTS_MONOTONIC_TO_MSEC(now);
+ elapsed -= ERTS_MONOTONIC_TO_MSEC(ERTS_MONOTONIC_BEGIN);
*ms_total = elapsed;
if (ms_diff) {
- erts_smp_mtx_lock(&erts_timeofday_mtx);
+ ErtsMonotonicTime prev;
- *ms_diff = elapsed - prev_wall_clock_elapsed;
- prev_wall_clock_elapsed = elapsed;
+ prev = ((ErtsMonotonicTime)
+ erts_smp_atomic64_xchg_mb(&wall_clock_prev.time,
+ (erts_aint64_t) elapsed));
- erts_smp_mtx_unlock(&erts_timeofday_mtx);
+ *ms_diff = elapsed - prev;
}
}
@@ -1722,22 +1776,27 @@ univ_to_local(Sint *year, Sint *month, Sint *day,
void
get_now(Uint* megasec, Uint* sec, Uint* microsec)
{
- ErtsMonotonicTime now_megasec, now_sec, now, mtime, time_offset;
+ ErtsMonotonicTime now_megasec, now_sec, now, prev, mtime, time_offset;
mtime = time_sup.r.o.get_time();
time_offset = get_time_offset();
update_last_mtime(NULL, mtime);
now = ERTS_MONOTONIC_TO_USEC(mtime + time_offset);
- erts_smp_mtx_lock(&erts_timeofday_mtx);
-
/* Make sure now time is later than last time */
- if (now <= previous_now)
- now = previous_now + 1;
-
- previous_now = now;
-
- erts_smp_mtx_unlock(&erts_timeofday_mtx);
+ prev = erts_smp_atomic64_read_nob(&now_prev.time);
+ while (1) {
+ ErtsMonotonicTime act;
+ if (now <= prev)
+ now = prev + 1;
+ act = ((ErtsMonotonicTime)
+ erts_smp_atomic64_cmpxchg_mb(&now_prev.time,
+ (erts_aint64_t) now,
+ (erts_aint64_t) prev));
+ if (act == prev)
+ break;
+ prev = act;
+ }
now_megasec = now / ERTS_MONOTONIC_TIME_TERA;
now_sec = now / ERTS_MONOTONIC_TIME_MEGA;
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index b6c77794d2..6662685f54 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -784,10 +784,6 @@ Preload* sys_preloaded(void);
unsigned char* sys_preload_begin(Preload*);
void sys_preload_end(Preload*);
int sys_get_key(int);
-void elapsed_time_both(ErtsMonotonicTime *ms_user, ErtsMonotonicTime *ms_sys,
- ErtsMonotonicTime *ms_user_diff, ErtsMonotonicTime *ms_sys_diff);
-void wall_clock_elapsed_time_both(ErtsMonotonicTime *ms_total,
- ErtsMonotonicTime *ms_diff);
void get_time(int *hour, int *minute, int *second);
void get_date(int *year, int *month, int *day);
void get_localtime(int *year, int *month, int *day,
diff --git a/erts/emulator/pcre/local_config.h b/erts/emulator/pcre/local_config.h
index e90f4dcada..c6af423d72 100644
--- a/erts/emulator/pcre/local_config.h
+++ b/erts/emulator/pcre/local_config.h
@@ -86,4 +86,4 @@
#define SUPPORT_UTF
/* Version number of package */
-#define VERSION "8.40"
+#define VERSION "8.41"
diff --git a/erts/emulator/pcre/pcre-8.40.tar.bz2 b/erts/emulator/pcre/pcre-8.40.tar.bz2
deleted file mode 100644
index 6147917f4e..0000000000
--- a/erts/emulator/pcre/pcre-8.40.tar.bz2
+++ /dev/null
Binary files differ
diff --git a/erts/emulator/pcre/pcre-8.41.tar.bz2 b/erts/emulator/pcre/pcre-8.41.tar.bz2
new file mode 100644
index 0000000000..1798432dc9
--- /dev/null
+++ b/erts/emulator/pcre/pcre-8.41.tar.bz2
Binary files differ
diff --git a/erts/emulator/pcre/pcre.h b/erts/emulator/pcre/pcre.h
index 9cbd9c0293..ab8f40cfc1 100644
--- a/erts/emulator/pcre/pcre.h
+++ b/erts/emulator/pcre/pcre.h
@@ -43,9 +43,9 @@ POSSIBILITY OF SUCH DAMAGE.
/* The current PCRE version information. */
#define PCRE_MAJOR 8
-#define PCRE_MINOR 40
+#define PCRE_MINOR 41
#define PCRE_PRERELEASE
-#define PCRE_DATE 2017-01-11
+#define PCRE_DATE 2017-07-05
/* When an application links to a PCRE DLL in Windows, the symbols that are
imported have to be identified as such. When building PCRE, the appropriate
diff --git a/erts/emulator/pcre/pcre_compile.c b/erts/emulator/pcre/pcre_compile.c
index 6e841c9cf8..e79284ab79 100644
--- a/erts/emulator/pcre/pcre_compile.c
+++ b/erts/emulator/pcre/pcre_compile.c
@@ -5740,6 +5740,21 @@ for (;; ptr++)
ptr = p - 1; /* Character before the next significant one. */
}
+ /* We also need to skip over (?# comments, which are not dependent on
+ extended mode. */
+
+ if (ptr[1] == CHAR_LEFT_PARENTHESIS && ptr[2] == CHAR_QUESTION_MARK &&
+ ptr[3] == CHAR_NUMBER_SIGN)
+ {
+ ptr += 4;
+ while (*ptr != CHAR_NULL && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++;
+ if (*ptr == CHAR_NULL)
+ {
+ *errorcodeptr = ERR18;
+ goto FAILED;
+ }
+ }
+
/* If the next character is '+', we have a possessive quantifier. This
implies greediness, whatever the setting of the PCRE_UNGREEDY option.
If the next character is '?' this is a minimizing repeat, by default,
@@ -8211,7 +8226,6 @@ for (;; ptr++)
if (mclength == 1 || req_caseopt == 0)
{
- firstchar = mcbuffer[0] | req_caseopt;
firstchar = mcbuffer[0];
firstcharflags = req_caseopt;
diff --git a/erts/emulator/pcre/pcre_dfa_exec.c b/erts/emulator/pcre/pcre_dfa_exec.c
index 529f40685b..c859d67fc7 100644
--- a/erts/emulator/pcre/pcre_dfa_exec.c
+++ b/erts/emulator/pcre/pcre_dfa_exec.c
@@ -7,7 +7,7 @@ and semantics are as close as possible to those of the Perl 5 language (but see
below for why this module is different).
Written by Philip Hazel
- Copyright (c) 1997-2014 University of Cambridge
+ Copyright (c) 1997-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -2626,7 +2626,7 @@ for (;;)
if (isinclass)
{
int max = (int)GET2(ecode, 1 + IMM2_SIZE);
- if (*ecode == OP_CRPOSRANGE)
+ if (*ecode == OP_CRPOSRANGE && count >= (int)GET2(ecode, 1))
{
active_count--; /* Remove non-match possibility */
next_active_state--;
diff --git a/erts/emulator/pcre/pcre_exec.c b/erts/emulator/pcre/pcre_exec.c
index 0f682d3daf..6708ba92a6 100644
--- a/erts/emulator/pcre/pcre_exec.c
+++ b/erts/emulator/pcre/pcre_exec.c
@@ -755,7 +755,7 @@ if (ecode == NULL)
return match((PCRE_PUCHAR)&rdepth, NULL, NULL, 0, NULL, NULL, 1);
else
{
- int len = (char *)&rdepth - (char *)eptr;
+ int len = (int)((char *)&rdepth - (char *)eptr);
return (len > 0)? -len : len;
}
}
diff --git a/erts/emulator/pcre/pcre_internal.h b/erts/emulator/pcre/pcre_internal.h
index cc4f171438..c84dcb5a38 100644
--- a/erts/emulator/pcre/pcre_internal.h
+++ b/erts/emulator/pcre/pcre_internal.h
@@ -2791,6 +2791,9 @@ extern const pcre_uint8 PRIV(ucd_stage1)[];
extern const pcre_uint16 PRIV(ucd_stage2)[];
extern const pcre_uint32 PRIV(ucp_gentype)[];
extern const pcre_uint32 PRIV(ucp_gbtable)[];
+#ifdef COMPILE_PCRE32
+extern const ucd_record PRIV(dummy_ucd_record)[];
+#endif
#ifdef SUPPORT_JIT
extern const int PRIV(ucp_typerange)[];
#endif
@@ -2799,10 +2802,16 @@ extern const int PRIV(ucp_typerange)[];
/* UCD access macros */
#define UCD_BLOCK_SIZE 128
-#define GET_UCD(ch) (PRIV(ucd_records) + \
+#define REAL_GET_UCD(ch) (PRIV(ucd_records) + \
PRIV(ucd_stage2)[PRIV(ucd_stage1)[(int)(ch) / UCD_BLOCK_SIZE] * \
UCD_BLOCK_SIZE + (int)(ch) % UCD_BLOCK_SIZE])
+#ifdef COMPILE_PCRE32
+#define GET_UCD(ch) ((ch > 0x10ffff)? PRIV(dummy_ucd_record) : REAL_GET_UCD(ch))
+#else
+#define GET_UCD(ch) REAL_GET_UCD(ch)
+#endif
+
#define UCD_CHARTYPE(ch) GET_UCD(ch)->chartype
#define UCD_SCRIPT(ch) GET_UCD(ch)->script
#define UCD_CATEGORY(ch) PRIV(ucp_gentype)[UCD_CHARTYPE(ch)]
diff --git a/erts/emulator/pcre/pcre_jit_compile.c b/erts/emulator/pcre/pcre_jit_compile.c
index 89400498f0..932ca2c389 100644
--- a/erts/emulator/pcre/pcre_jit_compile.c
+++ b/erts/emulator/pcre/pcre_jit_compile.c
@@ -487,7 +487,7 @@ typedef struct compare_context {
#undef CMP
/* Used for accessing the elements of the stack. */
-#define STACK(i) ((-(i) - 1) * (int)sizeof(sljit_sw))
+#define STACK(i) ((i) * (int)sizeof(sljit_sw))
#define TMP1 SLJIT_R0
#define TMP2 SLJIT_R2
@@ -552,13 +552,15 @@ the start pointers when the end of the capturing group has not yet reached. */
sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w))
#define CMPTO(type, src1, src1w, src2, src2w, label) \
sljit_set_label(sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w)), (label))
-#define OP_FLAGS(op, dst, dstw, src, srcw, type) \
- sljit_emit_op_flags(compiler, (op), (dst), (dstw), (src), (srcw), (type))
+#define OP_FLAGS(op, dst, dstw, type) \
+ sljit_emit_op_flags(compiler, (op), (dst), (dstw), (type))
#define GET_LOCAL_BASE(dst, dstw, offset) \
sljit_get_local_base(compiler, (dst), (dstw), (offset))
#define READ_CHAR_MAX 0x7fffffff
+#define INVALID_UTF_CHAR 888
+
static pcre_uchar *bracketend(pcre_uchar *cc)
{
SLJIT_ASSERT((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NOT) || (*cc >= OP_ONCE && *cc <= OP_SCOND));
@@ -784,7 +786,7 @@ switch(*cc)
default:
/* All opcodes are supported now! */
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return NULL;
}
}
@@ -1660,9 +1662,9 @@ while (cc < ccend)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0));
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
setsom_found = TRUE;
}
cc += 1;
@@ -1676,9 +1678,9 @@ while (cc < ccend)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
setmark_found = TRUE;
}
cc += 1 + 2 + cc[1];
@@ -1689,27 +1691,27 @@ while (cc < ccend)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0));
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
setsom_found = TRUE;
}
if (common->mark_ptr != 0 && !setmark_found)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
setmark_found = TRUE;
}
if (common->capture_last_ptr != 0 && !capture_last_found)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
capture_last_found = TRUE;
}
cc += 1 + LINK_SIZE;
@@ -1723,20 +1725,20 @@ while (cc < ccend)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
capture_last_found = TRUE;
}
offset = (GET2(cc, 1 + LINK_SIZE)) << 1;
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, OVECTOR(offset));
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP2, 0);
- stackpos += (int)sizeof(sljit_sw);
+ stackpos -= (int)sizeof(sljit_sw);
cc += 1 + LINK_SIZE + IMM2_SIZE;
break;
@@ -1887,18 +1889,17 @@ BOOL tmp1empty = TRUE;
BOOL tmp2empty = TRUE;
pcre_uchar *alternative;
enum {
- start,
loop,
end
} status;
-status = save ? start : loop;
-stackptr = STACK(stackptr - 2);
+status = loop;
+stackptr = STACK(stackptr);
stacktop = STACK(stacktop - 1);
if (!save)
{
- stackptr += (needs_control_head ? 2 : 1) * sizeof(sljit_sw);
+ stacktop -= (needs_control_head ? 2 : 1) * sizeof(sljit_sw);
if (stackptr < stacktop)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), stackptr);
@@ -1914,196 +1915,186 @@ if (!save)
/* The tmp1next must be TRUE in either way. */
}
+SLJIT_ASSERT(common->recursive_head_ptr != 0);
+
do
{
count = 0;
- switch(status)
+ if (cc >= ccend)
{
- case start:
- SLJIT_ASSERT(save && common->recursive_head_ptr != 0);
+ if (!save)
+ break;
+
count = 1;
srcw[0] = common->recursive_head_ptr;
if (needs_control_head)
{
SLJIT_ASSERT(common->control_head_ptr != 0);
count = 2;
- srcw[1] = common->control_head_ptr;
+ srcw[0] = common->control_head_ptr;
+ srcw[1] = common->recursive_head_ptr;
+ }
+ status = end;
+ }
+ else switch(*cc)
+ {
+ case OP_KET:
+ if (PRIVATE_DATA(cc) != 0)
+ {
+ count = 1;
+ srcw[0] = PRIVATE_DATA(cc);
+ SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0);
+ cc += PRIVATE_DATA(cc + 1);
}
- status = loop;
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ONCE:
+ case OP_ONCE_NC:
+ case OP_BRAPOS:
+ case OP_SBRA:
+ case OP_SBRAPOS:
+ case OP_SCOND:
+ count = 1;
+ srcw[0] = PRIVATE_DATA(cc);
+ SLJIT_ASSERT(srcw[0] != 0);
+ cc += 1 + LINK_SIZE;
break;
- case loop:
- if (cc >= ccend)
+ case OP_CBRA:
+ case OP_SCBRA:
+ if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0)
{
- status = end;
- break;
+ count = 1;
+ srcw[0] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE));
}
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
- switch(*cc)
- {
- case OP_KET:
- if (PRIVATE_DATA(cc) != 0)
- {
- count = 1;
- srcw[0] = PRIVATE_DATA(cc);
- SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0);
- cc += PRIVATE_DATA(cc + 1);
- }
- cc += 1 + LINK_SIZE;
- break;
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ count = 2;
+ srcw[0] = PRIVATE_DATA(cc);
+ srcw[1] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE));
+ SLJIT_ASSERT(srcw[0] != 0 && srcw[1] != 0);
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
- case OP_ASSERT:
- case OP_ASSERT_NOT:
- case OP_ASSERTBACK:
- case OP_ASSERTBACK_NOT:
- case OP_ONCE:
- case OP_ONCE_NC:
- case OP_BRAPOS:
- case OP_SBRA:
- case OP_SBRAPOS:
- case OP_SCOND:
+ case OP_COND:
+ /* Might be a hidden SCOND. */
+ alternative = cc + GET(cc, 1);
+ if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN)
+ {
count = 1;
srcw[0] = PRIVATE_DATA(cc);
SLJIT_ASSERT(srcw[0] != 0);
- cc += 1 + LINK_SIZE;
- break;
-
- case OP_CBRA:
- case OP_SCBRA:
- if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0)
- {
- count = 1;
- srcw[0] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE));
- }
- cc += 1 + LINK_SIZE + IMM2_SIZE;
- break;
+ }
+ cc += 1 + LINK_SIZE;
+ break;
- case OP_CBRAPOS:
- case OP_SCBRAPOS:
- count = 2;
+ CASE_ITERATOR_PRIVATE_DATA_1
+ if (PRIVATE_DATA(cc))
+ {
+ count = 1;
srcw[0] = PRIVATE_DATA(cc);
- srcw[1] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE));
- SLJIT_ASSERT(srcw[0] != 0 && srcw[1] != 0);
- cc += 1 + LINK_SIZE + IMM2_SIZE;
- break;
-
- case OP_COND:
- /* Might be a hidden SCOND. */
- alternative = cc + GET(cc, 1);
- if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN)
- {
- count = 1;
- srcw[0] = PRIVATE_DATA(cc);
- SLJIT_ASSERT(srcw[0] != 0);
- }
- cc += 1 + LINK_SIZE;
- break;
-
- CASE_ITERATOR_PRIVATE_DATA_1
- if (PRIVATE_DATA(cc))
- {
- count = 1;
- srcw[0] = PRIVATE_DATA(cc);
- }
- cc += 2;
+ }
+ cc += 2;
#ifdef SUPPORT_UTF
- if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
#endif
- break;
+ break;
- CASE_ITERATOR_PRIVATE_DATA_2A
- if (PRIVATE_DATA(cc))
- {
- count = 2;
- srcw[0] = PRIVATE_DATA(cc);
- srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw);
- }
- cc += 2;
+ CASE_ITERATOR_PRIVATE_DATA_2A
+ if (PRIVATE_DATA(cc))
+ {
+ count = 2;
+ srcw[0] = PRIVATE_DATA(cc);
+ srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw);
+ }
+ cc += 2;
#ifdef SUPPORT_UTF
- if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
#endif
- break;
+ break;
- CASE_ITERATOR_PRIVATE_DATA_2B
- if (PRIVATE_DATA(cc))
- {
- count = 2;
- srcw[0] = PRIVATE_DATA(cc);
- srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw);
- }
- cc += 2 + IMM2_SIZE;
+ CASE_ITERATOR_PRIVATE_DATA_2B
+ if (PRIVATE_DATA(cc))
+ {
+ count = 2;
+ srcw[0] = PRIVATE_DATA(cc);
+ srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw);
+ }
+ cc += 2 + IMM2_SIZE;
#ifdef SUPPORT_UTF
- if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
#endif
- break;
+ break;
- CASE_ITERATOR_TYPE_PRIVATE_DATA_1
- if (PRIVATE_DATA(cc))
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_1
+ if (PRIVATE_DATA(cc))
+ {
+ count = 1;
+ srcw[0] = PRIVATE_DATA(cc);
+ }
+ cc += 1;
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_2A
+ if (PRIVATE_DATA(cc))
+ {
+ count = 2;
+ srcw[0] = PRIVATE_DATA(cc);
+ srcw[1] = srcw[0] + sizeof(sljit_sw);
+ }
+ cc += 1;
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_2B
+ if (PRIVATE_DATA(cc))
+ {
+ count = 2;
+ srcw[0] = PRIVATE_DATA(cc);
+ srcw[1] = srcw[0] + sizeof(sljit_sw);
+ }
+ cc += 1 + IMM2_SIZE;
+ break;
+
+ case OP_CLASS:
+ case OP_NCLASS:
+#if defined SUPPORT_UTF || !defined COMPILE_PCRE8
+ case OP_XCLASS:
+ size = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(pcre_uchar);
+#else
+ size = 1 + 32 / (int)sizeof(pcre_uchar);
+#endif
+ if (PRIVATE_DATA(cc))
+ switch(get_class_iterator_size(cc + size))
{
+ case 1:
count = 1;
srcw[0] = PRIVATE_DATA(cc);
- }
- cc += 1;
- break;
+ break;
- CASE_ITERATOR_TYPE_PRIVATE_DATA_2A
- if (PRIVATE_DATA(cc))
- {
+ case 2:
count = 2;
srcw[0] = PRIVATE_DATA(cc);
srcw[1] = srcw[0] + sizeof(sljit_sw);
- }
- cc += 1;
- break;
+ break;
- CASE_ITERATOR_TYPE_PRIVATE_DATA_2B
- if (PRIVATE_DATA(cc))
- {
- count = 2;
- srcw[0] = PRIVATE_DATA(cc);
- srcw[1] = srcw[0] + sizeof(sljit_sw);
+ default:
+ SLJIT_UNREACHABLE();
+ break;
}
- cc += 1 + IMM2_SIZE;
- break;
-
- case OP_CLASS:
- case OP_NCLASS:
-#if defined SUPPORT_UTF || !defined COMPILE_PCRE8
- case OP_XCLASS:
- size = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(pcre_uchar);
-#else
- size = 1 + 32 / (int)sizeof(pcre_uchar);
-#endif
- if (PRIVATE_DATA(cc))
- switch(get_class_iterator_size(cc + size))
- {
- case 1:
- count = 1;
- srcw[0] = PRIVATE_DATA(cc);
- break;
-
- case 2:
- count = 2;
- srcw[0] = PRIVATE_DATA(cc);
- srcw[1] = srcw[0] + sizeof(sljit_sw);
- break;
-
- default:
- SLJIT_ASSERT_STOP();
- break;
- }
- cc += size;
- break;
-
- default:
- cc = next_opcode(common, cc);
- SLJIT_ASSERT(cc != NULL);
- break;
- }
+ cc += size;
break;
- case end:
- SLJIT_ASSERT_STOP();
+ default:
+ cc = next_opcode(common, cc);
+ SLJIT_ASSERT(cc != NULL);
break;
}
@@ -2312,7 +2303,7 @@ static SLJIT_INLINE void count_match(compiler_common *common)
{
DEFINE_COMPILER;
-OP2(SLJIT_SUB | SLJIT_SET_E, COUNT_MATCH, 0, COUNT_MATCH, 0, SLJIT_IMM, 1);
+OP2(SLJIT_SUB | SLJIT_SET_Z, COUNT_MATCH, 0, COUNT_MATCH, 0, SLJIT_IMM, 1);
add_jump(compiler, &common->calllimit, JUMP(SLJIT_ZERO));
}
@@ -2322,7 +2313,7 @@ static SLJIT_INLINE void allocate_stack(compiler_common *common, int size)
DEFINE_COMPILER;
SLJIT_ASSERT(size > 0);
-OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw));
+OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw));
#ifdef DESTROY_REGISTERS
OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 12345);
OP1(SLJIT_MOV, TMP3, 0, TMP1, 0);
@@ -2330,7 +2321,7 @@ OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, TMP1, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP1, 0);
#endif
-add_stub(common, CMP(SLJIT_GREATER, STACK_TOP, 0, STACK_LIMIT, 0));
+add_stub(common, CMP(SLJIT_LESS, STACK_TOP, 0, STACK_LIMIT, 0));
}
static SLJIT_INLINE void free_stack(compiler_common *common, int size)
@@ -2338,7 +2329,7 @@ static SLJIT_INLINE void free_stack(compiler_common *common, int size)
DEFINE_COMPILER;
SLJIT_ASSERT(size > 0);
-OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw));
+OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw));
}
static sljit_uw * allocate_read_only_data(compiler_common *common, sljit_uw size)
@@ -2396,7 +2387,7 @@ else
OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1);
loop = LABEL();
OP1(SLJIT_MOVU, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw), SLJIT_R0, 0);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, loop);
}
}
@@ -2434,7 +2425,7 @@ else
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2);
loop = LABEL();
OP1(SLJIT_MOVU, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0);
- OP2(SLJIT_SUB | SLJIT_SET_E, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, loop);
}
@@ -2452,22 +2443,22 @@ static sljit_sw SLJIT_CALL do_search_mark(sljit_sw *current, const pcre_uchar *s
{
while (current != NULL)
{
- switch (current[-2])
+ switch (current[1])
{
case type_then_trap:
break;
case type_mark:
- if (STRCMP_UC_UC(skip_arg, (pcre_uchar *)current[-3]) == 0)
- return current[-4];
+ if (STRCMP_UC_UC(skip_arg, (pcre_uchar *)current[2]) == 0)
+ return current[3];
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
- SLJIT_ASSERT(current > (sljit_sw*)current[-1]);
- current = (sljit_sw*)current[-1];
+ SLJIT_ASSERT(current[0] == 0 || current < (sljit_sw*)current[0]);
+ current = (sljit_sw*)current[0];
}
return -1;
}
@@ -2501,7 +2492,7 @@ OP2(SLJIT_ADD, SLJIT_S0, 0, SLJIT_S0, 0, SLJIT_IMM, sizeof(sljit_sw));
OP2(SLJIT_ASHR, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_IMM, UCHAR_SHIFT);
#endif
OP1(SLJIT_MOVU_S32, SLJIT_MEM1(SLJIT_R2), sizeof(int), SLJIT_S1, 0);
-OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1);
+OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, loop);
JUMPHERE(early_quit);
@@ -3106,8 +3097,8 @@ if (common->utf)
OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
/* Skip low surrogate if necessary. */
OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xdc00);
- OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xdc00);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
return;
@@ -3126,6 +3117,7 @@ struct sljit_jump *jump;
if (nltype == NLTYPE_ANY)
{
add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
add_jump(compiler, backtracks, JUMP(jumpifmatch ? SLJIT_NOT_ZERO : SLJIT_ZERO));
}
else if (nltype == NLTYPE_ANYCRLF)
@@ -3167,7 +3159,7 @@ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
/* Searching for the first zero. */
-OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800);
+OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800);
jump = JUMP(SLJIT_NOT_ZERO);
/* Two byte sequence. */
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
@@ -3181,7 +3173,7 @@ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
-OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x10000);
+OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x10000);
jump = JUMP(SLJIT_NOT_ZERO);
/* Three byte sequence. */
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
@@ -3215,15 +3207,15 @@ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
/* Searching for the first zero. */
-OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800);
+OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800);
jump = JUMP(SLJIT_NOT_ZERO);
/* Two byte sequence. */
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
JUMPHERE(jump);
-OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x400);
-OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_NOT_ZERO);
+OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x400);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_NOT_ZERO);
/* This code runs only in 8 bit mode. No need to shift the value. */
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
@@ -3246,7 +3238,7 @@ struct sljit_jump *compare;
sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
-OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x20);
+OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x20);
jump = JUMP(SLJIT_NOT_ZERO);
/* Two byte sequence. */
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
@@ -3287,10 +3279,30 @@ static void do_getucd(compiler_common *common)
/* Search the UCD record for the character comes in TMP1.
Returns chartype in TMP1 and UCD offset in TMP2. */
DEFINE_COMPILER;
+#ifdef COMPILE_PCRE32
+struct sljit_jump *jump;
+#endif
+
+#if defined SLJIT_DEBUG && SLJIT_DEBUG
+/* dummy_ucd_record */
+const ucd_record *record = GET_UCD(INVALID_UTF_CHAR);
+SLJIT_ASSERT(record->script == ucp_Common && record->chartype == ucp_Cn && record->gbprop == ucp_gbOther);
+SLJIT_ASSERT(record->caseset == 0 && record->other_case == 0);
+#endif
SLJIT_ASSERT(UCD_BLOCK_SIZE == 128 && sizeof(ucd_record) == 8);
sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+#ifdef COMPILE_PCRE32
+if (!common->utf)
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x10ffff + 1);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+ JUMPHERE(jump);
+ }
+#endif
+
OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1));
OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK);
@@ -3365,8 +3377,8 @@ if (newlinecheck)
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
end = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, common->newline & 0xff);
- OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, common->newline & 0xff);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32
OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
#endif
@@ -3403,8 +3415,8 @@ if (common->utf)
{
singlechar = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800);
OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800);
- OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
JUMPHERE(singlechar);
@@ -3853,7 +3865,7 @@ while (TRUE)
}
}
-#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) && !(defined SUPPORT_VALGRIND)
static sljit_s32 character_to_int32(pcre_uchar chr)
{
@@ -4019,6 +4031,7 @@ instruction[0] = 0x0f;
instruction[1] = 0xbc;
instruction[2] = 0xc0 | (tmp1_ind << 3) | tmp1_ind;
sljit_emit_op_custom(compiler, instruction, 3);
+sljit_set_current_flags(compiler, SLJIT_SET_Z);
nomatch = JUMP(SLJIT_ZERO);
@@ -4119,6 +4132,7 @@ instruction[0] = 0x0f;
instruction[1] = 0xbc;
instruction[2] = 0xc0 | (tmp1_ind << 3) | tmp1_ind;
sljit_emit_op_custom(compiler, instruction, 3);
+sljit_set_current_flags(compiler, SLJIT_SET_Z);
JUMPTO(SLJIT_ZERO, start);
@@ -4155,18 +4169,8 @@ if (has_match_end)
OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
OP2(SLJIT_ADD, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, SLJIT_IMM, IN_UCHARS(offset + 1));
-#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
- if (sljit_x86_is_cmov_available())
- {
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_END, 0, TMP3, 0);
- sljit_x86_emit_cmov(compiler, SLJIT_GREATER, STR_END, TMP3, 0);
- }
-#endif
- {
- quit = CMP(SLJIT_LESS_EQUAL, STR_END, 0, TMP3, 0);
- OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
- JUMPHERE(quit);
- }
+ OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_END, 0, TMP3, 0);
+ sljit_emit_cmov(compiler, SLJIT_GREATER, STR_END, TMP3, 0);
}
#if defined SUPPORT_UTF && !defined COMPILE_PCRE32
@@ -4174,11 +4178,11 @@ if (common->utf && offset > 0)
utf_start = LABEL();
#endif
-#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) && !(defined SUPPORT_VALGRIND)
/* SSE2 accelerated first character search. */
-if (sljit_x86_is_sse2_available())
+if (sljit_has_cpu_feature(SLJIT_HAS_SSE2))
{
fast_forward_first_char2_sse2(common, char1, char2);
@@ -4213,16 +4217,16 @@ if (sljit_x86_is_sse2_available())
if (offset > 0)
OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset));
}
- else if (sljit_x86_is_cmov_available())
- {
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_PTR, 0, STR_END, 0);
- sljit_x86_emit_cmov(compiler, SLJIT_GREATER_EQUAL, STR_PTR, has_match_end ? SLJIT_MEM1(SLJIT_SP) : STR_END, has_match_end ? common->match_end_ptr : 0);
- }
else
{
- quit = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0);
- OP1(SLJIT_MOV, STR_PTR, 0, has_match_end ? SLJIT_MEM1(SLJIT_SP) : STR_END, has_match_end ? common->match_end_ptr : 0);
- JUMPHERE(quit);
+ OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, STR_PTR, 0, STR_END, 0);
+ if (has_match_end)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ sljit_emit_cmov(compiler, SLJIT_GREATER_EQUAL, STR_PTR, TMP1, 0);
+ }
+ else
+ sljit_emit_cmov(compiler, SLJIT_GREATER_EQUAL, STR_PTR, STR_END, 0);
}
if (has_match_end)
@@ -4249,10 +4253,10 @@ else
}
else
{
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char1);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char2);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char1);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char2);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
found = JUMP(SLJIT_NOT_ZERO);
}
}
@@ -4571,8 +4575,8 @@ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
firstchar = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0);
OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(2));
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_PTR, 0, TMP1, 0);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_GREATER_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, STR_PTR, 0, TMP1, 0);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER_EQUAL);
#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32
OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCHAR_SHIFT);
#endif
@@ -4616,8 +4620,8 @@ if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF)
JUMPHERE(foundcr);
notfoundnl = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_NL);
- OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_NL);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
#if defined COMPILE_PCRE16 || defined COMPILE_PCRE32
OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
#endif
@@ -4670,7 +4674,7 @@ if (!check_class_ranges(common, start_bits, (start_bits[31] & 0x80) != 0, TRUE,
OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)start_bits);
OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
found = JUMP(SLJIT_NOT_ZERO);
}
@@ -4692,8 +4696,8 @@ if (common->utf)
{
CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800, start);
OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800);
- OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
}
@@ -4780,31 +4784,31 @@ struct sljit_jump *jump;
struct sljit_label *mainloop;
sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
-OP1(SLJIT_MOV, TMP1, 0, STACK_TOP, 0);
-GET_LOCAL_BASE(TMP3, 0, 0);
+OP1(SLJIT_MOV, TMP3, 0, STACK_TOP, 0);
+GET_LOCAL_BASE(TMP1, 0, 0);
/* Drop frames until we reach STACK_TOP. */
mainloop = LABEL();
-OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), 0);
-OP2(SLJIT_SUB | SLJIT_SET_S, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0);
-jump = JUMP(SLJIT_SIG_LESS_EQUAL);
-
-OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP3, 0);
-OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(TMP1), sizeof(sljit_sw));
-OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), SLJIT_MEM1(TMP1), 2 * sizeof(sljit_sw));
-OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 3 * sizeof(sljit_sw));
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), -sizeof(sljit_sw));
+jump = CMP(SLJIT_SIG_LESS_EQUAL, TMP2, 0, SLJIT_IMM, 0);
+
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -2 * sizeof(sljit_sw));
+OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), SLJIT_MEM1(STACK_TOP), -3 * sizeof(sljit_sw));
+OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * sizeof(sljit_sw));
JUMPTO(SLJIT_JUMP, mainloop);
JUMPHERE(jump);
-jump = JUMP(SLJIT_SIG_LESS);
-/* End of dropping frames. */
+jump = CMP(SLJIT_NOT_ZERO /* SIG_LESS */, TMP2, 0, SLJIT_IMM, 0);
+/* End of reverting values. */
+OP1(SLJIT_MOV, STACK_TOP, 0, TMP3, 0);
sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
JUMPHERE(jump);
OP1(SLJIT_NEG, TMP2, 0, TMP2, 0);
-OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP3, 0);
-OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(TMP1), sizeof(sljit_sw));
-OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 2 * sizeof(sljit_sw));
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -2 * sizeof(sljit_sw));
+OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * sizeof(sljit_sw));
JUMPTO(SLJIT_JUMP, mainloop);
}
@@ -4837,11 +4841,11 @@ if (common->use_ucp)
jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE);
add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL));
OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
JUMPHERE(jump);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP2, 0);
}
@@ -4881,11 +4885,11 @@ if (common->use_ucp)
jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE);
add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL));
OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
JUMPHERE(jump);
}
else
@@ -4913,7 +4917,7 @@ else
}
set_jumps(skipread_list, LABEL());
-OP2(SLJIT_XOR | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
+OP2(SLJIT_XOR | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
sljit_emit_fast_return(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0);
}
@@ -5064,7 +5068,7 @@ switch(length)
return TRUE;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return FALSE;
}
}
@@ -5077,22 +5081,22 @@ DEFINE_COMPILER;
sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a);
-OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a);
-OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
-OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a);
+OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a);
#if defined SUPPORT_UTF || defined COMPILE_PCRE16 || defined COMPILE_PCRE32
#ifdef COMPILE_PCRE8
if (common->utf)
{
#endif
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a);
#ifdef COMPILE_PCRE8
}
#endif
#endif /* SUPPORT_UTF || COMPILE_PCRE16 || COMPILE_PCRE32 */
-OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
}
@@ -5103,34 +5107,34 @@ DEFINE_COMPILER;
sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
-OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x09);
-OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
-OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x20);
-OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
-OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xa0);
+OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x09);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x20);
+OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xa0);
#if defined SUPPORT_UTF || defined COMPILE_PCRE16 || defined COMPILE_PCRE32
#ifdef COMPILE_PCRE8
if (common->utf)
{
#endif
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x1680);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x1680);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x2000);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x200A - 0x2000);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x202f - 0x2000);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x205f - 0x2000);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x3000 - 0x2000);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x200A - 0x2000);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x202f - 0x2000);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x205f - 0x2000);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x3000 - 0x2000);
#ifdef COMPILE_PCRE8
}
#endif
#endif /* SUPPORT_UTF || COMPILE_PCRE16 || COMPILE_PCRE32 */
-OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
}
@@ -5143,22 +5147,22 @@ DEFINE_COMPILER;
sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a);
-OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a);
-OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
-OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a);
+OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a);
#if defined SUPPORT_UTF || defined COMPILE_PCRE16 || defined COMPILE_PCRE32
#ifdef COMPILE_PCRE8
if (common->utf)
{
#endif
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a);
#ifdef COMPILE_PCRE8
}
#endif
#endif /* SUPPORT_UTF || COMPILE_PCRE16 || COMPILE_PCRE32 */
-OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
sljit_emit_fast_return(compiler, RETURN_ADDR, 0);
}
@@ -5183,7 +5187,7 @@ label = LABEL();
OP1(MOVU_UCHAR, CHAR1, 0, SLJIT_MEM1(TMP1), IN_UCHARS(1));
OP1(MOVU_UCHAR, CHAR2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
jump = CMP(SLJIT_NOT_EQUAL, CHAR1, 0, CHAR2, 0);
-OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
JUMPTO(SLJIT_NOT_ZERO, label);
JUMPHERE(jump);
@@ -5227,7 +5231,7 @@ OP1(SLJIT_MOV_U8, CHAR2, 0, SLJIT_MEM2(LCC_TABLE, CHAR2), 0);
JUMPHERE(jump);
#endif
jump = CMP(SLJIT_NOT_EQUAL, CHAR1, 0, CHAR2, 0);
-OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
JUMPTO(SLJIT_NOT_ZERO, label);
JUMPHERE(jump);
@@ -5394,7 +5398,7 @@ do
#endif
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
context->ucharptr = 0;
@@ -5568,7 +5572,7 @@ while (*cc != XCL_END)
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
cc += 2;
@@ -5592,7 +5596,7 @@ if ((cc[-1] & XCL_HASPROP) == 0)
OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
add_jump(compiler, &found, JUMP(SLJIT_NOT_ZERO));
}
@@ -5625,7 +5629,7 @@ else if ((cc[-1] & XCL_MAP) != 0)
OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
add_jump(compiler, list, JUMP(SLJIT_NOT_ZERO));
#ifdef COMPILE_PCRE8
@@ -5644,6 +5648,15 @@ if (needstype || needsscript)
if (needschar && !charsaved)
OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0);
+#ifdef COMPILE_PCRE32
+ if (!common->utf)
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x10ffff + 1);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+ JUMPHERE(jump);
+ }
+#endif
+
OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1));
OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK);
@@ -5735,14 +5748,14 @@ while (*cc != XCL_END)
if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE))
{
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
- OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, numberofcmps == 0 ? SLJIT_UNUSED : TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
numberofcmps++;
}
else if (numberofcmps > 0)
{
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
numberofcmps = 0;
}
@@ -5761,14 +5774,14 @@ while (*cc != XCL_END)
if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE))
{
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
- OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, numberofcmps == 0 ? SLJIT_UNUSED : TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
numberofcmps++;
}
else if (numberofcmps > 0)
{
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
numberofcmps = 0;
}
@@ -5793,12 +5806,12 @@ while (*cc != XCL_END)
break;
case PT_LAMP:
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - typeoffset);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ll - typeoffset);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lt - typeoffset);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - typeoffset);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ll - typeoffset);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lt - typeoffset);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
break;
@@ -5820,33 +5833,33 @@ while (*cc != XCL_END)
case PT_SPACE:
case PT_PXSPACE:
SET_CHAR_OFFSET(9);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd - 0x9);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd - 0x9);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x9);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x9);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x9);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x9);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
SET_TYPE_OFFSET(ucp_Zl);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Zl);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Zl);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
break;
case PT_WORD:
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_UNDERSCORE - charoffset));
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_UNDERSCORE - charoffset));
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
/* Fall through. */
case PT_ALNUM:
SET_TYPE_OFFSET(ucp_Ll);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
- OP_FLAGS((*cc == PT_ALNUM) ? SLJIT_MOV : SLJIT_OR, TMP2, 0, (*cc == PT_ALNUM) ? SLJIT_UNUSED : TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
+ OP_FLAGS((*cc == PT_ALNUM) ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
SET_TYPE_OFFSET(ucp_Nd);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_No - ucp_Nd);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_No - ucp_Nd);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
break;
@@ -5868,8 +5881,8 @@ while (*cc != XCL_END)
OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset);
OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]);
}
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[1]);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[1]);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
other_cases += 2;
}
else if (is_powerof2(other_cases[2] ^ other_cases[1]))
@@ -5881,63 +5894,63 @@ while (*cc != XCL_END)
OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset);
OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]);
}
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[2]);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[2]);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(other_cases[0] - charoffset));
- OP_FLAGS(SLJIT_OR | ((other_cases[3] == NOTACHAR) ? SLJIT_SET_E : 0), TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(other_cases[0] - charoffset));
+ OP_FLAGS(SLJIT_OR | ((other_cases[3] == NOTACHAR) ? SLJIT_SET_Z : 0), TMP2, 0, SLJIT_EQUAL);
other_cases += 3;
}
else
{
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset));
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset));
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
}
while (*other_cases != NOTACHAR)
{
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset));
- OP_FLAGS(SLJIT_OR | ((*other_cases == NOTACHAR) ? SLJIT_SET_E : 0), TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset));
+ OP_FLAGS(SLJIT_OR | ((*other_cases == NOTACHAR) ? SLJIT_SET_Z : 0), TMP2, 0, SLJIT_EQUAL);
}
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
break;
case PT_UCNC:
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_DOLLAR_SIGN - charoffset));
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_COMMERCIAL_AT - charoffset));
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_GRAVE_ACCENT - charoffset));
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_DOLLAR_SIGN - charoffset));
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_COMMERCIAL_AT - charoffset));
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_GRAVE_ACCENT - charoffset));
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
SET_CHAR_OFFSET(0xa0);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(0xd7ff - charoffset));
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(0xd7ff - charoffset));
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
SET_CHAR_OFFSET(0);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xe000 - 0);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_GREATER_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xe000 - 0);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_GREATER_EQUAL);
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
break;
case PT_PXGRAPH:
/* C and Z groups are the farthest two groups. */
SET_TYPE_OFFSET(ucp_Ll);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_GREATER);
+ OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER);
jump = CMP(SLJIT_NOT_EQUAL, typereg, 0, SLJIT_IMM, ucp_Cf - ucp_Ll);
/* In case of ucp_Cf, we overwrite the result. */
SET_CHAR_OFFSET(0x2066);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x2066);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x2066);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
JUMPHERE(jump);
jump = CMP(SLJIT_ZERO ^ invertcmp, TMP2, 0, SLJIT_IMM, 0);
@@ -5946,21 +5959,21 @@ while (*cc != XCL_END)
case PT_PXPRINT:
/* C and Z groups are the farthest two groups. */
SET_TYPE_OFFSET(ucp_Ll);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_GREATER);
+ OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Ll);
- OP_FLAGS(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_NOT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Ll);
+ OP_FLAGS(SLJIT_AND, TMP2, 0, SLJIT_NOT_EQUAL);
jump = CMP(SLJIT_NOT_EQUAL, typereg, 0, SLJIT_IMM, ucp_Cf - ucp_Ll);
/* In case of ucp_Cf, we overwrite the result. */
SET_CHAR_OFFSET(0x2066);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066);
- OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
JUMPHERE(jump);
jump = CMP(SLJIT_ZERO ^ invertcmp, TMP2, 0, SLJIT_IMM, 0);
@@ -5968,21 +5981,21 @@ while (*cc != XCL_END)
case PT_PXPUNCT:
SET_TYPE_OFFSET(ucp_Sc);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Sc);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Sc);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
SET_CHAR_OFFSET(0);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x7f);
- OP_FLAGS(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x7f);
+ OP_FLAGS(SLJIT_AND, TMP2, 0, SLJIT_LESS_EQUAL);
SET_TYPE_OFFSET(ucp_Pc);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ps - ucp_Pc);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ps - ucp_Pc);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
cc += 2;
@@ -6028,6 +6041,7 @@ switch(type)
case OP_NOT_WORD_BOUNDARY:
case OP_WORD_BOUNDARY:
add_jump(compiler, &common->wordboundary, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
add_jump(compiler, backtracks, JUMP(type == OP_NOT_WORD_BOUNDARY ? SLJIT_NOT_ZERO : SLJIT_ZERO));
return cc;
@@ -6043,10 +6057,10 @@ switch(type)
else
{
jump[1] = CMP(SLJIT_EQUAL, TMP2, 0, STR_END, 0);
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0);
- OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff);
- OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_NOT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_NOT_EQUAL);
add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL));
check_partial(common, TRUE);
add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
@@ -6068,9 +6082,9 @@ switch(type)
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
jump[1] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR);
OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
- OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0);
jump[2] = JUMP(SLJIT_GREATER);
- add_jump(compiler, backtracks, JUMP(SLJIT_LESS));
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL) /* LESS */);
/* Equal. */
OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
jump[3] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL);
@@ -6089,6 +6103,7 @@ switch(type)
read_char_range(common, common->nlmin, common->nlmax, TRUE);
add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0));
add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
add_jump(compiler, backtracks, JUMP(SLJIT_ZERO));
OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
}
@@ -6204,7 +6219,7 @@ switch(type)
label = LABEL();
add_jump(compiler, backtracks, CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP3, 0));
skip_char_back(common);
- OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
}
else
@@ -6217,7 +6232,7 @@ switch(type)
check_start_used_ptr(common);
return cc + LINK_SIZE;
}
-SLJIT_ASSERT_STOP();
+SLJIT_UNREACHABLE();
return cc;
}
@@ -6250,7 +6265,7 @@ switch(type)
#endif
read_char8_type(common, type == OP_NOT_DIGIT);
/* Flip the starting bit in the negative case. */
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_digit);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_digit);
add_jump(compiler, backtracks, JUMP(type == OP_DIGIT ? SLJIT_ZERO : SLJIT_NOT_ZERO));
return cc;
@@ -6264,7 +6279,7 @@ switch(type)
else
#endif
read_char8_type(common, type == OP_NOT_WHITESPACE);
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_space);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_space);
add_jump(compiler, backtracks, JUMP(type == OP_WHITESPACE ? SLJIT_ZERO : SLJIT_NOT_ZERO));
return cc;
@@ -6278,7 +6293,7 @@ switch(type)
else
#endif
read_char8_type(common, type == OP_NOT_WORDCHAR);
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_word);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_word);
add_jump(compiler, backtracks, JUMP(type == OP_WORDCHAR ? SLJIT_ZERO : SLJIT_NOT_ZERO));
return cc;
@@ -6320,8 +6335,8 @@ switch(type)
#elif defined COMPILE_PCRE16
jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800);
OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800);
- OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
#endif
@@ -6383,6 +6398,7 @@ switch(type)
detect_partial_match(common, backtracks);
read_char_range(common, 0x9, 0x3000, type == OP_NOT_HSPACE);
add_jump(compiler, &common->hspace, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
add_jump(compiler, backtracks, JUMP(type == OP_NOT_HSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO));
return cc;
@@ -6392,6 +6408,7 @@ switch(type)
detect_partial_match(common, backtracks);
read_char_range(common, 0xa, 0x2029, type == OP_NOT_VSPACE);
add_jump(compiler, &common->vspace, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
add_jump(compiler, backtracks, JUMP(type == OP_NOT_VSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO));
return cc;
@@ -6418,7 +6435,7 @@ switch(type)
OP1(SLJIT_MOV_U32, TMP1, 0, SLJIT_MEM1(STACK_TOP), (sljit_sw)PRIV(ucp_gbtable));
OP1(SLJIT_MOV, STACK_TOP, 0, TMP2, 0);
OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
JUMPTO(SLJIT_NOT_ZERO, label);
OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
@@ -6587,7 +6604,7 @@ switch(type)
OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
- OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
+ OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0);
add_jump(compiler, backtracks, JUMP(SLJIT_ZERO));
#if defined SUPPORT_UTF || !defined COMPILE_PCRE8
@@ -6604,7 +6621,7 @@ switch(type)
return cc + GET(cc, 0) - 1;
#endif
}
-SLJIT_ASSERT_STOP();
+SLJIT_UNREACHABLE();
return cc;
}
@@ -6790,9 +6807,9 @@ else
#endif /* SUPPORT_UTF && SUPPORT_UCP */
{
if (ref)
- OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP1, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP1, 0);
else
- OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0);
if (withchecks)
jump = JUMP(SLJIT_ZERO);
@@ -6883,7 +6900,7 @@ switch(type)
cc += 1 + IMM2_SIZE + 1 + 2 * IMM2_SIZE;
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
@@ -6897,7 +6914,7 @@ if (!minimize)
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0);
/* Temporary release of STR_PTR. */
- OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
/* Handles both invalid and empty cases. Since the minimum repeat,
is zero the invalid case is basically the same as an empty case. */
if (ref)
@@ -6910,7 +6927,7 @@ if (!minimize)
zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
}
/* Restore if not zero length. */
- OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
}
else
{
@@ -7157,7 +7174,7 @@ return (*PUBL(callout))(callout_block);
(((int)sizeof(PUBL(callout_block)) + 7) & ~7)
#define CALLOUT_ARG_OFFSET(arg) \
- (-CALLOUT_ARG_SIZE + SLJIT_OFFSETOF(PUBL(callout_block), arg))
+ SLJIT_OFFSETOF(PUBL(callout_block), arg)
static SLJIT_INLINE pcre_uchar *compile_callout_matchingpath(compiler_common *common, pcre_uchar *cc, backtrack_common *parent)
{
@@ -7187,7 +7204,8 @@ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(mark), (common->mark_pt
/* Needed to save important temporary registers. */
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STACK_TOP, 0);
-OP2(SLJIT_SUB, SLJIT_R1, 0, STACK_TOP, 0, SLJIT_IMM, CALLOUT_ARG_SIZE);
+/* SLJIT_R0 = arguments */
+OP1(SLJIT_MOV, SLJIT_R1, 0, STACK_TOP, 0);
GET_LOCAL_BASE(SLJIT_R2, 0, OVECTOR_START);
sljit_emit_ijump(compiler, SLJIT_CALL3, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_callout));
OP1(SLJIT_MOV_S32, SLJIT_RETURN_REG, 0, SLJIT_RETURN_REG, 0);
@@ -7195,12 +7213,12 @@ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
free_stack(common, CALLOUT_ARG_SIZE / sizeof(sljit_sw));
/* Check return value. */
-OP2(SLJIT_SUB | SLJIT_SET_S, SLJIT_UNUSED, 0, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0);
+OP2(SLJIT_SUB | SLJIT_SET_Z | SLJIT_SET_SIG_GREATER, SLJIT_UNUSED, 0, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0);
add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_SIG_GREATER));
if (common->forced_quit_label == NULL)
- add_jump(compiler, &common->forced_quit, JUMP(SLJIT_SIG_LESS));
+ add_jump(compiler, &common->forced_quit, JUMP(SLJIT_NOT_EQUAL) /* SIG_LESS */);
else
- JUMPTO(SLJIT_SIG_LESS, common->forced_quit_label);
+ JUMPTO(SLJIT_NOT_EQUAL /* SIG_LESS */, common->forced_quit_label);
return cc + 2 + 2 * LINK_SIZE;
}
@@ -7321,7 +7339,7 @@ else
allocate_stack(common, framesize + extrasize);
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
- OP2(SLJIT_SUB, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw));
+ OP2(SLJIT_ADD, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw));
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0);
if (needs_control_head)
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
@@ -7392,22 +7410,22 @@ while (1)
free_stack(common, extrasize);
if (needs_control_head)
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1));
}
else
{
if ((opcode != OP_ASSERT_NOT && opcode != OP_ASSERTBACK_NOT) || conditional)
{
/* We don't need to keep the STR_PTR, only the previous private_data_ptr. */
- OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw));
if (needs_control_head)
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1));
}
else
{
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
if (needs_control_head)
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), (framesize + 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 2));
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
}
}
@@ -7418,25 +7436,25 @@ while (1)
if (conditional)
{
if (extrasize > 0)
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), needs_control_head ? sizeof(sljit_sw) : 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), needs_control_head ? STACK(-2) : STACK(-1));
}
else if (bra == OP_BRAZERO)
{
if (framesize < 0)
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize));
else
{
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_sw));
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (framesize + extrasize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 1));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-framesize - extrasize));
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0);
}
- OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
}
else if (framesize >= 0)
{
/* For OP_BRA and OP_BRAMINZERO. */
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 1));
}
}
add_jump(compiler, found, JUMP(SLJIT_JUMP));
@@ -7480,12 +7498,12 @@ if (common->positive_assert_quit != NULL)
set_jumps(common->positive_assert_quit, LABEL());
SLJIT_ASSERT(framesize != no_stack);
if (framesize < 0)
- OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, extrasize * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, extrasize * sizeof(sljit_sw));
else
{
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
- OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw));
}
JUMPHERE(jump);
}
@@ -7534,18 +7552,18 @@ if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK)
{
/* We know that STR_PTR was stored on the top of the stack. */
if (extrasize > 0)
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize));
/* Keep the STR_PTR on the top of the stack. */
if (bra == OP_BRAZERO)
{
- OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
if (extrasize == 2)
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
}
else if (bra == OP_BRAMINZERO)
{
- OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
}
}
@@ -7554,13 +7572,13 @@ if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK)
if (bra == OP_BRA)
{
/* We don't need to keep the STR_PTR, only the previous private_data_ptr. */
- OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw));
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 2) * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize + 1));
}
else
{
/* We don't need to keep the STR_PTR, only the previous private_data_ptr. */
- OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 2) * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 2) * sizeof(sljit_sw));
if (extrasize == 2)
{
OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
@@ -7588,7 +7606,7 @@ if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK)
{
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 1));
}
set_jumps(backtrack->common.topbacktracks, LABEL());
}
@@ -7675,23 +7693,23 @@ if (framesize < 0)
}
if (needs_control_head)
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), (ket != OP_KET || has_alternatives) ? sizeof(sljit_sw) : 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), (ket != OP_KET || has_alternatives) ? STACK(-2) : STACK(-1));
/* TMP2 which is set here used by OP_KETRMAX below. */
if (ket == OP_KETRMAX)
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(-1));
else if (ket == OP_KETRMIN)
{
/* Move the STR_PTR to the private_data_ptr. */
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1));
}
}
else
{
stacksize = (ket != OP_KET || has_alternatives) ? 2 : 1;
- OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + stacksize) * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + stacksize) * sizeof(sljit_sw));
if (needs_control_head)
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-1));
if (ket == OP_KETRMAX)
{
@@ -7927,7 +7945,7 @@ if (bra == OP_BRAMINZERO)
{
/* Except when the whole stack frame must be saved. */
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
- braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), (BACKTRACK_AS(bracket_backtrack)->u.framesize + 1) * sizeof(sljit_sw));
+ braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), STACK(-BACKTRACK_AS(bracket_backtrack)->u.framesize - 2));
}
JUMPHERE(skip);
}
@@ -8000,7 +8018,7 @@ if (opcode == OP_ONCE)
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0);
if (BACKTRACK_AS(bracket_backtrack)->u.framesize == no_frame)
- OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, needs_control_head ? (2 * sizeof(sljit_sw)) : sizeof(sljit_sw));
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, needs_control_head ? (2 * sizeof(sljit_sw)) : sizeof(sljit_sw));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize + 1), TMP2, 0);
}
else if (ket == OP_KETRMAX || has_alternatives)
@@ -8018,7 +8036,7 @@ if (opcode == OP_ONCE)
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
- OP2(SLJIT_SUB, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
+ OP2(SLJIT_ADD, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
stacksize = needs_control_head ? 1 : 0;
if (ket != OP_KET || has_alternatives)
@@ -8090,13 +8108,13 @@ if (opcode == OP_COND || opcode == OP_SCOND)
slot = common->name_table + GET2(matchingpath, 1) * common->name_entry_size;
OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0);
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1));
- OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0);
slot += common->name_entry_size;
i--;
while (i-- > 0)
{
OP2(SLJIT_SUB, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0);
- OP2(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, STR_PTR, 0);
+ OP2(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, TMP2, 0, STR_PTR, 0);
slot += common->name_entry_size;
}
OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
@@ -8111,7 +8129,7 @@ if (opcode == OP_COND || opcode == OP_SCOND)
if (*matchingpath == OP_FAIL)
stacksize = 0;
- if (*matchingpath == OP_RREF)
+ else if (*matchingpath == OP_RREF)
{
stacksize = GET2(matchingpath, 1);
if (common->currententry == NULL)
@@ -8244,7 +8262,7 @@ if (ket == OP_KETRMAX)
{
if (has_alternatives)
BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL();
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, rmax_label);
/* Drop STR_PTR for greedy plus quantifier. */
if (opcode != OP_ONCE)
@@ -8274,7 +8292,7 @@ if (ket == OP_KETRMAX)
if (repeat_type == OP_EXACT)
{
count_match(common);
- OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, rmax_label);
}
else if (repeat_type == OP_UPTO)
@@ -8374,7 +8392,7 @@ switch(opcode)
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
@@ -8452,7 +8470,7 @@ else
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
if (needs_control_head)
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
- OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, -STACK(stacksize - 1));
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
stack = 0;
if (!zero)
@@ -8524,7 +8542,7 @@ while (*cc != OP_KETRPOS)
{
if (offset != 0)
{
- OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, stacksize * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, stacksize * sizeof(sljit_sw));
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), cbraprivptr, STR_PTR, 0);
@@ -8535,10 +8553,10 @@ while (*cc != OP_KETRPOS)
else
{
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
- OP2(SLJIT_ADD, STACK_TOP, 0, TMP2, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, TMP2, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
if (opcode == OP_SBRAPOS)
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw));
- OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw), STR_PTR, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), STACK(-framesize - 2));
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), STACK(-framesize - 2), STR_PTR, 0);
}
/* Even if the match is empty, we need to reset the control head. */
@@ -8584,7 +8602,7 @@ while (*cc != OP_KETRPOS)
else
{
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
- OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP2), STACK(-framesize - 2));
}
}
@@ -8601,7 +8619,7 @@ if (!zero)
if (framesize < 0)
add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0));
else /* TMP2 is set to [private_data_ptr] above. */
- add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(TMP2), (stacksize - 1) * sizeof(sljit_sw), SLJIT_IMM, 0));
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(TMP2), STACK(-stacksize), SLJIT_IMM, 0));
}
/* None of them matched. */
@@ -8824,7 +8842,7 @@ if (exact > 1)
OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact);
label = LABEL();
compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE);
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
}
else
@@ -8832,7 +8850,7 @@ if (exact > 1)
OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact);
label = LABEL();
compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, TRUE);
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
}
}
@@ -8862,7 +8880,7 @@ switch(opcode)
if (opcode == OP_UPTO)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0);
- OP2(SLJIT_SUB | SLJIT_SET_E, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
jump = JUMP(SLJIT_ZERO);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, TMP1, 0);
}
@@ -8924,7 +8942,7 @@ switch(opcode)
label = LABEL();
if (opcode == OP_UPTO)
{
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_ZERO));
}
compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE);
@@ -8944,7 +8962,7 @@ switch(opcode)
OP1(SLJIT_MOV, base, offset1, STR_PTR, 0);
if (opcode == OP_UPTO)
{
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
add_jump(compiler, &no_match, JUMP(SLJIT_ZERO));
}
@@ -8971,7 +8989,7 @@ switch(opcode)
if (opcode == OP_UPTO)
{
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
}
else
@@ -9000,7 +9018,7 @@ switch(opcode)
if (opcode == OP_UPTO)
{
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
}
else
@@ -9026,7 +9044,7 @@ switch(opcode)
compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE);
if (opcode == OP_UPTO)
{
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
}
@@ -9113,7 +9131,7 @@ switch(opcode)
label = LABEL();
compile_char1_matchingpath(common, type, cc, &no_match, TRUE);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, STR_PTR, 0);
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
set_jumps(no_match, LABEL());
OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1);
@@ -9124,7 +9142,7 @@ switch(opcode)
label = LABEL();
detect_partial_match(common, &no_match);
compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE);
- OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
JUMPTO(SLJIT_NOT_ZERO, label);
OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
set_jumps(no_char1_match, LABEL());
@@ -9142,7 +9160,7 @@ switch(opcode)
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
@@ -9264,7 +9282,7 @@ size = 3 + (size < 0 ? 0 : size);
OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
allocate_stack(common, size);
if (size > 3)
- OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0, SLJIT_IMM, (size - 3) * sizeof(sljit_sw));
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0, SLJIT_IMM, (size - 3) * sizeof(sljit_sw));
else
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 1), SLJIT_IMM, BACKTRACK_AS(then_trap_backtrack)->start);
@@ -9569,7 +9587,7 @@ while (cc < ccend)
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
return;
}
if (cc == NULL)
@@ -9677,7 +9695,7 @@ switch(opcode)
case OP_MINUPTO:
OP1(SLJIT_MOV, TMP1, 0, base, offset1);
OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
- OP2(SLJIT_SUB | SLJIT_SET_E, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
add_jump(compiler, &jumplist, JUMP(SLJIT_ZERO));
OP1(SLJIT_MOV, base, offset1, TMP1, 0);
@@ -9723,7 +9741,7 @@ switch(opcode)
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
@@ -9831,7 +9849,7 @@ if (*cc == OP_ASSERT || *cc == OP_ASSERTBACK)
{
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr);
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(assert_backtrack)->framesize * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-CURRENT_AS(assert_backtrack)->framesize - 1));
set_jumps(current->topbacktracks, LABEL());
}
@@ -9841,7 +9859,7 @@ else
if (bra == OP_BRAZERO)
{
/* We know there is enough place on the stack. */
- OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
JUMPTO(SLJIT_JUMP, CURRENT_AS(assert_backtrack)->matchingpath);
JUMPHERE(brajump);
@@ -9954,7 +9972,7 @@ else if (ket == OP_KETRMIN)
else
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
- CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), (CURRENT_AS(bracket_backtrack)->u.framesize + 1) * sizeof(sljit_sw), CURRENT_AS(bracket_backtrack)->recursive_matchingpath);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), STACK(-CURRENT_AS(bracket_backtrack)->u.framesize - 2), CURRENT_AS(bracket_backtrack)->recursive_matchingpath);
}
/* Drop STR_PTR for non-greedy plus quantifier. */
if (opcode != OP_ONCE)
@@ -10060,7 +10078,7 @@ if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND))
{
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr);
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, SLJIT_MEM1(STACK_TOP), assert->framesize * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-assert->framesize - 1));
}
cond = JUMP(SLJIT_JUMP);
set_jumps(CURRENT_AS(bracket_backtrack)->u.assert->condfailed, LABEL());
@@ -10201,7 +10219,7 @@ if (has_alternatives)
{
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr);
add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, SLJIT_MEM1(STACK_TOP), assert->framesize * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-assert->framesize - 1));
}
JUMPHERE(cond);
}
@@ -10256,7 +10274,7 @@ else if (opcode == OP_ONCE)
JUMPHERE(once);
/* Restore previous private_data_ptr */
if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0)
- OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(bracket_backtrack)->u.framesize * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-CURRENT_AS(bracket_backtrack)->u.framesize - 1));
else if (ket == OP_KETRMIN)
{
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
@@ -10346,7 +10364,7 @@ if (current->topbacktracks)
free_stack(common, CURRENT_AS(bracketpos_backtrack)->stacksize);
JUMPHERE(jump);
}
-OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(bracketpos_backtrack)->framesize * sizeof(sljit_sw));
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-CURRENT_AS(bracketpos_backtrack)->framesize - 1));
}
static SLJIT_INLINE void compile_braminzero_backtrackingpath(compiler_common *common, struct backtrack_common *current)
@@ -10392,10 +10410,10 @@ if (opcode == OP_THEN || opcode == OP_THEN_ARG)
jump = JUMP(SLJIT_JUMP);
loop = LABEL();
- OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), -(int)sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
JUMPHERE(jump);
- CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), -(int)(2 * sizeof(sljit_sw)), TMP1, 0, loop);
- CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), -(int)(3 * sizeof(sljit_sw)), TMP2, 0, loop);
+ CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0, loop);
+ CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(2), TMP2, 0, loop);
add_jump(compiler, &common->then_trap->quit, JUMP(SLJIT_JUMP));
return;
}
@@ -10645,7 +10663,7 @@ while (current)
break;
default:
- SLJIT_ASSERT_STOP();
+ SLJIT_UNREACHABLE();
break;
}
current = current->prev;
@@ -10684,7 +10702,7 @@ sljit_emit_fast_enter(compiler, TMP2, 0);
count_match(common);
allocate_stack(common, private_data_size + framesize + alternativesize);
OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(private_data_size + framesize + alternativesize - 1), TMP2, 0);
-copy_private_data(common, ccbegin, ccend, TRUE, private_data_size + framesize + alternativesize, framesize + alternativesize, needs_control_head);
+copy_private_data(common, ccbegin, ccend, TRUE, framesize + alternativesize, private_data_size + framesize + alternativesize, needs_control_head);
if (needs_control_head)
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, STACK_TOP, 0);
@@ -10737,9 +10755,9 @@ if (common->quit != NULL)
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr);
if (needs_frame)
{
- OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw));
- add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw));
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw));
}
OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0);
common->quit = NULL;
@@ -10750,32 +10768,32 @@ set_jumps(common->accept, LABEL());
OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr);
if (needs_frame)
{
- OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw));
- add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw));
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw));
}
OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 1);
JUMPHERE(jump);
if (common->quit != NULL)
set_jumps(common->quit, LABEL());
-copy_private_data(common, ccbegin, ccend, FALSE, private_data_size + framesize + alternativesize, framesize + alternativesize, needs_control_head);
+copy_private_data(common, ccbegin, ccend, FALSE, framesize + alternativesize, private_data_size + framesize + alternativesize, needs_control_head);
free_stack(common, private_data_size + framesize + alternativesize);
if (needs_control_head)
{
- OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), 2 * sizeof(sljit_sw));
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), sizeof(sljit_sw));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-3));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(-2));
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, TMP1, 0);
OP1(SLJIT_MOV, TMP1, 0, TMP3, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, TMP2, 0);
}
else
{
- OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), sizeof(sljit_sw));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(-2));
OP1(SLJIT_MOV, TMP1, 0, TMP3, 0);
OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, TMP2, 0);
}
-sljit_emit_fast_return(compiler, SLJIT_MEM1(STACK_TOP), 0);
+sljit_emit_fast_return(compiler, SLJIT_MEM1(STACK_TOP), STACK(-1));
}
#undef COMPILE_BACKTRACKINGPATH
@@ -11237,7 +11255,7 @@ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP2, 0);
OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack));
OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, top), STACK_TOP, 0);
-OP2(SLJIT_ADD, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, limit), SLJIT_IMM, STACK_GROWTH_RATE);
+OP2(SLJIT_SUB, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, limit), SLJIT_IMM, STACK_GROWTH_RATE);
sljit_emit_ijump(compiler, SLJIT_CALL2, SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_stack_resize));
jump = CMP(SLJIT_NOT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0);
@@ -11391,10 +11409,10 @@ union {
sljit_u8 local_space[MACHINE_STACK_SIZE];
struct sljit_stack local_stack;
-local_stack.top = (sljit_sw)&local_space;
-local_stack.base = local_stack.top;
-local_stack.limit = local_stack.base + MACHINE_STACK_SIZE;
-local_stack.max_limit = local_stack.limit;
+local_stack.max_limit = local_space;
+local_stack.limit = local_space;
+local_stack.base = local_space + MACHINE_STACK_SIZE;
+local_stack.top = local_space + MACHINE_STACK_SIZE;
arguments->stack = &local_stack;
convert_executable_func.executable_func = executable_func;
return convert_executable_func.call_executable_func(arguments);
diff --git a/erts/emulator/pcre/pcre_tables.c b/erts/emulator/pcre/pcre_tables.c
index 2f6302e2e1..08e31f1460 100644
--- a/erts/emulator/pcre/pcre_tables.c
+++ b/erts/emulator/pcre/pcre_tables.c
@@ -6,7 +6,7 @@
and semantics are as close as possible to those of the Perl 5 language.
Written by Philip Hazel
- Copyright (c) 1997-2012 University of Cambridge
+ Copyright (c) 1997-2017 University of Cambridge
-----------------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
@@ -162,7 +162,7 @@ const pcre_uint32 PRIV(ucp_gbtable[]) = {
(1<<ucp_gbExtend)|(1<<ucp_gbSpacingMark), /* 5 SpacingMark */
(1<<ucp_gbExtend)|(1<<ucp_gbSpacingMark)|(1<<ucp_gbL)| /* 6 L */
- (1<<ucp_gbL)|(1<<ucp_gbV)|(1<<ucp_gbLV)|(1<<ucp_gbLVT),
+ (1<<ucp_gbV)|(1<<ucp_gbLV)|(1<<ucp_gbLVT),
(1<<ucp_gbExtend)|(1<<ucp_gbSpacingMark)|(1<<ucp_gbV)| /* 7 V */
(1<<ucp_gbT),
diff --git a/erts/emulator/pcre/pcre_ucd.c b/erts/emulator/pcre/pcre_ucd.c
index 9b700c0785..2dd4b05751 100644
--- a/erts/emulator/pcre/pcre_ucd.c
+++ b/erts/emulator/pcre/pcre_ucd.c
@@ -38,6 +38,20 @@ const pcre_uint16 PRIV(ucd_stage2)[] = {0};
const pcre_uint32 PRIV(ucd_caseless_sets)[] = {0};
#else
+/* If the 32-bit library is run in non-32-bit mode, character values
+greater than 0x10ffff may be encountered. For these we set up a
+special record. */
+
+#ifdef COMPILE_PCRE32
+const ucd_record PRIV(dummy_ucd_record)[] = {{
+ ucp_Common, /* script */
+ ucp_Cn, /* type unassigned */
+ ucp_gbOther, /* grapheme break property */
+ 0, /* case set */
+ 0, /* other case */
+ }};
+#endif
+
/* When recompiling tables with a new Unicode version, please check the
types in this structure definition from pcre_internal.h (the actual
field names will be different):
diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h
index 22059d21d5..b83837a7d2 100644
--- a/erts/emulator/sys/unix/erl_unix_sys.h
+++ b/erts/emulator/sys/unix/erl_unix_sys.h
@@ -86,6 +86,10 @@
#include <sys/times.h>
+#ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+#endif
+
#ifdef HAVE_IEEEFP_H
#include <ieeefp.h>
#endif
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl
index 2320870a0e..04b7f2de15 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -525,6 +525,8 @@ binary_to_atom(Config) when is_list(Config) ->
?BADARG(binary_to_atom(id(<<255>>), utf8)),
?BADARG(binary_to_atom(id(<<255,0>>), utf8)),
?BADARG(binary_to_atom(id(<<16#C0,16#80>>), utf8)), %Overlong 0.
+ <<B:1/binary, _/binary>> = id(<<194, 163>>), %Truncated character ERL-474
+ ?BADARG(binary_to_atom(B, utf8)),
%% system_limit failures.
?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255>>), utf8)),
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index 4d17276e5c..61536bacd7 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -599,6 +599,9 @@ bad_binary_to_term(Config) when is_list(Config) ->
%% Bad float.
bad_bin_to_term(<<131,70,-1:64>>),
+
+ %% Truncated UTF8 character (ERL-474)
+ bad_bin_to_term(<<131,119,1,194,163>>),
ok.
bad_bin_to_term(BadBin) ->
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index ab0b1a82bd..730a17d7e8 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -159,7 +159,7 @@ suite() ->
all() ->
[otp_6224, {group, stream}, basic_ping, slow_writes,
bad_packet, bad_port_messages, {group, options},
- {group, multiple_packets}, parallell, dying_port,
+ {group, multiple_packets}, parallell, dying_port, dropped_commands,
port_program_with_path, open_input_file_port,
open_output_file_port, name1, env, huge_env, bad_env, cd,
cd_relative, pipe_limit_env, bad_args,
@@ -569,12 +569,14 @@ dropped_commands(Config, Outputv, Cmd) ->
[dropped_commands_test(Cmd) || _ <- lists:seq(1, 100)],
timer:sleep(100),
erl_ddll:unload_driver("echo_drv"),
+ os:unsetenv("ECHO_DRV_USE_OUTPUTV"),
ok.
dropped_commands_test(Cmd) ->
- Port = erlang:open_port({spawn_driver, "echo_drv"}, [{parallelism, true}]),
spawn_monitor(
fun() ->
+ Port = erlang:open_port({spawn_driver, "echo_drv"},
+ [{parallelism, true}]),
[spawn_link(fun() -> spin(Port, Cmd) end) || _ <- lists:seq(1,8)],
timer:sleep(5),
port_close(Port),
diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl
index 7690557fda..40cc940a94 100644
--- a/erts/emulator/test/statistics_SUITE.erl
+++ b/erts/emulator/test/statistics_SUITE.erl
@@ -23,8 +23,10 @@
%% Tests the statistics/1 bif.
-export([all/0, suite/0, groups/0,
+ wall_clock_sanity/1,
wall_clock_zero_diff/1, wall_clock_update/1,
- runtime_zero_diff/1,
+ runtime_sanity/1,
+ runtime_zero_diff/1,
runtime_update/1, runtime_diff/1,
run_queue_one/1,
scheduler_wall_time/1,
@@ -54,11 +56,23 @@ all() ->
groups() ->
[{wall_clock, [],
- [wall_clock_zero_diff, wall_clock_update]},
+ [wall_clock_sanity, wall_clock_zero_diff, wall_clock_update]},
{runtime, [],
- [runtime_zero_diff, runtime_update, runtime_diff]},
+ [runtime_sanity, runtime_zero_diff, runtime_update, runtime_diff]},
{run_queue, [], [run_queue_one]}].
+wall_clock_sanity(Config) when is_list(Config) ->
+ erlang:yield(),
+ {WallClock, _} = statistics(wall_clock),
+ MT = erlang:monotonic_time(),
+ Time = erlang:convert_time_unit(MT - erlang:system_info(start_time),
+ native, millisecond),
+ io:format("Time=~p WallClock=~p~n",
+ [Time, WallClock]),
+ true = WallClock =< Time,
+ true = Time - 100 =< WallClock,
+ ok.
+
%%% Testing statistics(wall_clock).
%% Tests that the 'Wall clock since last call' element of the result
@@ -102,6 +116,20 @@ wall_clock_update1(0) ->
%%% Test statistics(runtime).
+runtime_sanity(Config) when is_list(Config) ->
+ case erlang:system_info(logical_processors_available) of
+ unknown ->
+ {skipped, "Don't know available logical processors"};
+ LP when is_integer(LP) ->
+ erlang:yield(),
+ {RunTime, _} = statistics(runtime),
+ MT = erlang:monotonic_time(),
+ Time = erlang:convert_time_unit(MT - erlang:system_info(start_time),
+ native, millisecond),
+ io:format("Time=~p RunTime=~p~n",
+ [Time, RunTime]),
+ true = RunTime =< Time*LP
+ end.
%% Tests that the difference between the times returned from two consectuitive
%% calls to statistics(runtime) is zero.
diff --git a/erts/test/system_smoke.spec b/erts/test/system_smoke.spec
index 99092c1dab..bcb727b6d9 100644
--- a/erts/test/system_smoke.spec
+++ b/erts/test/system_smoke.spec
@@ -1,4 +1,3 @@
-{suites,"../system_test",[ethread_SUITE]}.
{cases,"../system_test",otp_SUITE,
[undefined_functions,
deprecated_not_in_obsolete,
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 622e00bb2b..00901077d3 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -1430,13 +1430,13 @@ merge_types(bool, {atom,A}) ->
merge_bool(A);
merge_types({atom,A}, bool) ->
merge_bool(A);
-merge_types(#ms{id=Id,valid=B0,slots=Slots}=M,
- #ms{id=Id,valid=B1,slots=Slots}) ->
- M#ms{valid=B0 bor B1,slots=Slots};
-merge_types(#ms{}=M, _) ->
- M;
-merge_types(_, #ms{}=M) ->
- M;
+merge_types(#ms{id=Id1,valid=B0,slots=Slots},
+ #ms{id=Id2,valid=B1,slots=Slots}) ->
+ Id = if
+ Id1 =:= Id2 -> Id1;
+ true -> make_ref()
+ end,
+ #ms{id=Id,valid=B0 band B1,slots=Slots};
merge_types(T1, T2) when T1 =/= T2 ->
%% Too different. All we know is that the type is a 'term'.
term.
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index 3ad24257a5..6b84b22eb5 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -397,10 +397,10 @@ from the peer offers it.</p>
Note that each tuple communicates one or more AVP values.
It is an error to specify duplicate tuples.</p>
-<marker id="evaluable"/>
+<marker id="eval"/>
</item>
-<tag><c>evaluable() = {M,F,A} | fun() | [evaluable() | A]</c></tag>
+<tag><c>eval() = {M,F,A} | fun() | [eval() | A]</c></tag>
<item>
<p>
An expression that can be evaluated as a function in the following
@@ -418,7 +418,7 @@ eval(F) ->
</pre>
<p>
-Applying an <c>&evaluable;</c>
+Applying an <c>&eval;</c>
<c>E</c> to an argument list <c>A</c>
is meant in the sense of <c>eval([E|A])</c>.</p>
@@ -484,11 +484,11 @@ Matches only those peers whose Origin-Realm has the
specified value, or all peers if the atom <c>any</c>.</p>
</item>
-<tag><c>{eval, &evaluable;}</c></tag>
+<tag><c>{eval, &eval;}</c></tag>
<item>
<p>
Matches only those peers for which the specified
-<c>&evaluable;</c> returns
+<c>&eval;</c> returns
<c>true</c> when applied to the connection's <c>diameter_caps</c>
record.
Any other return value or exception is equivalent to <c>false</c>.</p>
@@ -650,7 +650,7 @@ Result = ResultCode | {capabilities_cb, CB, ResultCode|discard}
Caps = #diameter_caps{}
Pkt = #diameter_packet{}
ResultCode = integer()
-CB = &evaluable;
+CB = &eval;
</pre>
<p>
@@ -799,7 +799,7 @@ be matched by corresponding &capability; configuration, of
<tag>
<marker id="decode_format"/>
-<c>{decode_format, record | list | map | false}</c></tag>
+<c>{decode_format, record | list | map | none}</c></tag>
<item>
<p>
The format of decoded messages and grouped AVPs in the <c>msg</c> field
@@ -808,10 +808,10 @@ records respectively.
If <c>record</c> then a record whose definition is generated from the
dictionary file in question.
If <c>list</c> or <c>map</c> then a <c>[Name | Avps]</c> pair where
-<c>Avps</c> is either a list of AVP name/values pairs or a map keyed on
+<c>Avps</c> is a list of AVP name/values pairs or a map keyed on
AVP names respectively.
-If <c>false</c> then the representation is omitted and <c>msg</c> and
-<c>value</c> fields are set to <c>false</c>.
+If <c>none</c> then the atom-value message name, or <c>undefined</c>
+for a Grouped AVP.
See also &codec_message;.</p>
<p>
@@ -826,24 +826,11 @@ field of diameter_packet records independently of
</item>
-<tag>
-<marker id="incoming_maxlen"/><c>{incoming_maxlen, 0..16777215}</c></tag>
-<item>
-<p>
-Bound on the expected size of incoming Diameter messages.
-Messages larger than the specified number of bytes are discarded.</p>
-
-<p>
-Defaults to <c>16777215</c>, the maximum value of the 24-bit Message
-Length field in a Diameter Header.</p>
-
-</item>
-
<tag><c>{restrict_connections, false
| node
| nodes
| [node()]
- | evaluable()}</c></tag>
+ | eval()}</c></tag>
<item>
<p>
The degree to which the service allows multiple transport
@@ -854,7 +841,7 @@ at capabilities exchange.</p>
If <c>[node()]</c> then a connection is rejected if another already
exists on any of the specified nodes.
Types <c>false</c>, <c>node</c>, <c>nodes</c> and
-&evaluable; are equivalent to
+&eval; are equivalent to
<c>[]</c>, <c>[node()]</c>, <c>[node()|nodes()]</c> and the
evaluated value respectively, evaluation of each expression taking
place whenever a new connection is to be established.
@@ -869,7 +856,7 @@ by their own peer and watchdog state machines.</p>
Defaults to <c>nodes</c>.</p>
</item>
-<tag><c>{sequence, {H,N} | &evaluable;}</c></tag>
+<tag><c>{sequence, {H,N} | &eval;}</c></tag>
<item>
<p>
A constant value <c>H</c> for the topmost <c>32-N</c> bits of
@@ -904,7 +891,7 @@ outgoing requests.</p>
</warning>
</item>
-<tag><c>{share_peers, boolean() | [node()] | evaluable()}</c></tag>
+<tag><c>{share_peers, boolean() | [node()] | eval()}</c></tag>
<item>
<p>
Nodes to which peer connections established on the local
@@ -917,7 +904,7 @@ configured to use them: see <c>use_shared_peers</c> below.</p>
If <c>false</c> then peers are not shared.
If <c>[node()]</c> then peers are shared with the specified list of
nodes.
-If <c>evaluable()</c> then peers are shared with the nodes returned
+If <c>eval()</c> then peers are shared with the nodes returned
by the specified function, evaluated whenever a peer connection
becomes available or a remote service requests information about local
connections.
@@ -943,18 +930,6 @@ of a single Diameter node across multiple Erlang nodes.</p>
</note>
</item>
-<tag><c>{spawn_opt, [term()]}</c></tag>
-<item>
-<p>
-Options list passed to &spawn_opt; when spawning a process for an
-incoming Diameter request, unless the transport in question
-specifies another value.
-Options <c>monitor</c> and <c>link</c> are ignored.</p>
-
-<p>
-Defaults to the empty list.</p>
-</item>
-
<tag>
<marker id="strict_arities"/><c>{strict_arities, boolean()
| encode
@@ -962,7 +937,8 @@ Defaults to the empty list.</p>
<item>
<p>
Whether or not to require that the number of AVPs in a message or
-grouped AVP agree with those specified in the dictionary in question.
+grouped AVP agree with those specified in the dictionary in question
+when passing messages to &man_app; callbacks.
If <c>true</c> then mismatches in an outgoing messages cause message
encoding to fail, while mismatches in an incoming message are reported
as 5005/5009 errors in the errors field of the diameter_packet record
@@ -987,49 +963,6 @@ of arity 1 as bare values, not wrapped in a list.</p>
</item>
<tag>
-<marker id="strict_mbit"/><c>{strict_mbit, boolean()}</c></tag>
-<item>
-<p>
-Whether or not to regard an AVP setting the M-bit as erroneous when
-the command grammar in question does not explicitly allow the AVP.
-If <c>true</c> then such AVPs are regarded as 5001 errors,
-DIAMETER_AVP_UNSUPPORTED.
-If <c>false</c> then the M-bit is ignored and policing
-it becomes the receiver's responsibility.</p>
-
-<p>
-Defaults to <c>true</c>.</p>
-
-<warning>
-<p>
-RFC 6733 is unclear about the semantics of the M-bit.
-One the one hand, the CCF specification in section 3.2 documents AVP
-in a command grammar as meaning <em>any</em> arbitrary AVP; on the
-other hand, 1.3.4 states that AVPs setting the M-bit cannot be added
-to an existing command: the modified command must instead be
-placed in a new Diameter application.</p>
-<p>
-The reason for the latter is presumably interoperability:
-allowing arbitrary AVPs setting the M-bit in a command makes its
-interpretation implementation-dependent, since there's no
-guarantee that all implementations will understand the same set of
-arbitrary AVPs in the context of a given command.
-However, interpreting <c>AVP</c> in a command grammar as any
-AVP, regardless of M-bit, renders 1.3.4 meaningless, since the receiver
-can simply ignore any AVP it thinks isn't relevant, regardless of the
-sender's intent.</p>
-<p>
-Beware of confusing mandatory in the sense of the M-bit with mandatory
-in the sense of the command grammar.
-The former is a semantic requirement: that the receiver understand the
-semantics of the AVP in the context in question.
-The latter is a syntactic requirement: whether or not the AVP must
-occur in the message in question.</p>
-</warning>
-
-</item>
-
-<tag>
<marker id="string_decode"/><c>{string_decode, boolean()}</c></tag>
<item>
<p>
@@ -1073,7 +1006,7 @@ omitted counters are not returned by &service_info;.</p>
</item>
-<tag><c>{use_shared_peers, boolean() | [node()] | evaluable()}</c></tag>
+<tag><c>{use_shared_peers, boolean() | [node()] | eval()}</c></tag>
<item>
<p>
Nodes from which communicated peers are made available in
@@ -1083,7 +1016,7 @@ the remote candidates list of &app_pick_peer; callbacks.</p>
If <c>false</c> then remote peers are not used.
If <c>[node()]</c> then only peers from the specified list of nodes
are used.
-If <c>evaluable()</c> then only peers returned by the specified
+If <c>eval()</c> then only peers returned by the specified
function are used, evaluated whenever a remote service communicates
information about an available peer connection.
The value <c>true</c> is equivalent to <c>fun &nodes;</c>.
@@ -1108,6 +1041,15 @@ each node from which requests are sent.</p>
</warning>
</item>
+<tag><c>&transport_opt;</c></tag>
+<item>
+<p>
+Any transport option except <c>applications</c> or
+<c>capabilities</c>.
+Used as defaults for transport configuration, values passed to
+&add_transport; overriding values configured on the service.</p>
+</item>
+
</taglist>
<marker id="transport_opt"/>
@@ -1141,6 +1083,37 @@ implies having to set matching *-Application-Id AVPs in a
</item>
<tag>
+<marker id="avp_dictionaries"/><c>{avp_dictionaries, [module()]}</c></tag>
+<item>
+<p>
+A list of alternate dictionary modules with which to encode/decode
+AVPs that are not defined by the dictionary of the application in
+question.
+At decode, such AVPs are represented as diameter_avp records in the
+<c>'AVP'</c> field of a decoded message or Grouped AVP, the first
+alternate that succeeds in decoding the AVP setting the record's value
+field.
+At encode, values in an <c>'AVP'</c> list can be passed as AVP
+name/value 2-tuples, and it is an encode error for no alternate to
+define the AVP of such a tuple.</p>
+
+<p>
+Defaults to the empty list.</p>
+
+<note>
+<p>
+The motivation for alternate dictionaries is RFC 7683, Diameter
+Overload Indication Conveyance (DOIC), which defines AVPs to
+be piggybacked onto existing application messages rather than defining
+an application of its own.
+The DOIC dictionary is provided by the diameter application, as module
+<c>diameter_gen_doic_rfc7683</c>, but alternate dictionaries can be
+used to encode/decode any set of AVPs not known to an application
+dictionary.</p>
+</note>
+</item>
+
+<tag>
<marker id="capabilities"/><c>{capabilities, [&capability;]}</c></tag>
<item>
<p>
@@ -1155,7 +1128,7 @@ TLS is desired over TCP as implemented by &man_tcp;.</p>
</item>
<tag>
-<marker id="capabilities_cb"/><c>{capabilities_cb, &evaluable;}</c></tag>
+<marker id="capabilities_cb"/><c>{capabilities_cb, &eval;}</c></tag>
<item>
<p>
Callback invoked upon reception of CER/CEA during capabilities
@@ -1249,7 +1222,7 @@ transport.</p>
</item>
<tag>
-<marker id="disconnect_cb"/><c>{disconnect_cb, &evaluable;}</c></tag>
+<marker id="disconnect_cb"/><c>{disconnect_cb, &eval;}</c></tag>
<item>
<p>
Callback invoked prior to terminating the transport process of a
@@ -1349,6 +1322,19 @@ Defaults to 5000.</p>
</item>
<tag>
+<marker id="incoming_maxlen"/><c>{incoming_maxlen, 0..16777215}</c></tag>
+<item>
+<p>
+Bound on the expected size of incoming Diameter messages.
+Messages larger than the specified number of bytes are discarded.</p>
+
+<p>
+Defaults to <c>16777215</c>, the maximum value of the 24-bit Message
+Length field in a Diameter Header.</p>
+
+</item>
+
+<tag>
<marker id="length_errors"/><c>{length_errors, exit|handle|discard}</c></tag>
<item>
<p>
@@ -1406,7 +1392,64 @@ incoming Diameter request.
Options <c>monitor</c> and <c>link</c> are ignored.</p>
<p>
-Defaults to the list configured on the service if not specified.</p>
+Defaults to the empty list.</p>
+</item>
+
+<tag>
+<marker id="strict_capx"/><c>{strict_capx, boolean()]}</c></tag>
+<item>
+<p>
+Whether or not to enforce the RFC 6733 requirement that any message
+before capabilities exchange should close the peer connection.
+If false then unexpected messages are discarded.</p>
+
+<p>
+Defaults to true.
+Changing this results in non-standard behaviour, but can be useful in
+case peers are known to be behave badly.</p>
+</item>
+
+<tag>
+<marker id="strict_mbit"/><c>{strict_mbit, boolean()}</c></tag>
+<item>
+<p>
+Whether or not to regard an AVP setting the M-bit as erroneous when
+the command grammar in question does not explicitly allow the AVP.
+If <c>true</c> then such AVPs are regarded as 5001 errors,
+DIAMETER_AVP_UNSUPPORTED.
+If <c>false</c> then the M-bit is ignored and policing
+it becomes the receiver's responsibility.</p>
+
+<p>
+Defaults to <c>true</c>.</p>
+
+<warning>
+<p>
+RFC 6733 is unclear about the semantics of the M-bit.
+One the one hand, the CCF specification in section 3.2 documents AVP
+in a command grammar as meaning <em>any</em> arbitrary AVP; on the
+other hand, 1.3.4 states that AVPs setting the M-bit cannot be added
+to an existing command: the modified command must instead be
+placed in a new Diameter application.</p>
+<p>
+The reason for the latter is presumably interoperability:
+allowing arbitrary AVPs setting the M-bit in a command makes its
+interpretation implementation-dependent, since there's no
+guarantee that all implementations will understand the same set of
+arbitrary AVPs in the context of a given command.
+However, interpreting <c>AVP</c> in a command grammar as any
+AVP, regardless of M-bit, renders 1.3.4 meaningless, since the receiver
+can simply ignore any AVP it thinks isn't relevant, regardless of the
+sender's intent.</p>
+<p>
+Beware of confusing mandatory in the sense of the M-bit with mandatory
+in the sense of the command grammar.
+The former is a semantic requirement: that the receiver understand the
+semantics of the AVP in the context in question.
+The latter is a syntactic requirement: whether or not the AVP must
+occur in the message in question.</p>
+</warning>
+
</item>
<tag>
diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml
index dfcd00975b..aa334beb21 100644
--- a/lib/diameter/doc/src/diameter_app.xml
+++ b/lib/diameter/doc/src/diameter_app.xml
@@ -13,7 +13,8 @@
<header>
<copyright>
-<year>2011</year><year>2016</year>
+<year>2011</year>
+<year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -319,7 +320,7 @@ or &peer_down; callback.</p>
<v>Action = Send | Discard | {eval_packet, Action, PostF}</v>
<v>Send = {send, &packet; | &message;}</v>
<v>Discard = {discard, Reason} | discard</v>
-<v>PostF = &mod_evaluable;}</v>
+<v>PostF = &mod_eval;}</v>
</type>
<desc>
<p>
@@ -371,7 +372,7 @@ discarded}</c>.</p>
<v>Action = Send | Discard | {eval_packet, Action, PostF}</v>
<v>Send = {send, &packet; | &message;}</v>
<v>Discard = {discard, Reason} | discard</v>
-<v>PostF = &mod_evaluable;}</v>
+<v>PostF = &mod_eval;}</v>
</type>
<desc>
<p>
@@ -478,7 +479,7 @@ not selected.</p>
| {answer_message, 3000..3999|5000..5999}
| {protocol_error, 3000..3999}</v>
<v>Opt = &mod_call_opt;</v>
-<v>PostF = &mod_evaluable;</v>
+<v>PostF = &mod_eval;</v>
</type>
<desc>
<p>
diff --git a/lib/diameter/doc/src/diameter_codec.xml b/lib/diameter/doc/src/diameter_codec.xml
index 0846334d23..5124b49484 100644
--- a/lib/diameter/doc/src/diameter_codec.xml
+++ b/lib/diameter/doc/src/diameter_codec.xml
@@ -4,7 +4,10 @@
'<seealso marker="diameter_dict#MESSAGE_RECORDS">diameter_dict(4)</seealso>'>
<!ENTITY types
'<seealso marker="diameter_dict#DATA_TYPES">diameter_dict(4)</seealso>'>
- <!ENTITY % also SYSTEM "seealso.ent" >
+ <!ENTITY decode_format
+ '<seealso marker="diameter#decode_format">decode format</seealso>'>
+
+<!ENTITY % also SYSTEM "seealso.ent" >
<!ENTITY % here SYSTEM "seehere.ent" >
%also;
%here;
@@ -145,7 +148,8 @@ question.</p>
<p>
The decoded value of an AVP.
Will be <c>undefined</c> on decode if the data bytes could
-not be decoded or the AVP is unknown.
+not be decoded, the AVP is unknown, or if the &decode_format; is
+<c>none</c>.
The type of a decoded value is as document in &types;.</p>
</item>
@@ -243,8 +247,7 @@ Equivalently, a message can also be encoded as a list whose head is
the atom-valued message name (as specified in the relevant dictionary
file) and whose tail is either a list of AVP name/values
pairs or a map with values keyed on AVP names.
-The format at decode is determined by &mod_service_opt;
-<c>decode_format</c>.
+The format at decode is determined by &mod_decode_format;.
Any of the formats is accepted at encode.</p>
<p>
@@ -288,15 +291,16 @@ value other than <c>undefined</c>.</p>
<item>
<p>
The incoming/outgoing message.
-For an incoming message, a record if the message can be
-decoded in a non-relay application, <c>undefined</c> otherwise.
+For an incoming message, a term corresponding to the configured
+&decode_format; if the message can be decoded in a non-relay
+application, <c>undefined</c> otherwise.
For an outgoing message, setting a <c>[&header; | &avp;]</c> list is
equivalent to setting the <c>header</c> and <c>avps</c> fields to the
corresponding values.</p>
<warning>
<p>
-A record-valued <c>msg</c> field does <em>not</em> imply an absence of
+A value in the <c>msg</c> field does <em>not</em> imply an absence of
decode errors.
The <c>errors</c> field should also be examined.</p>
</warning>
diff --git a/lib/diameter/doc/src/diameter_sctp.xml b/lib/diameter/doc/src/diameter_sctp.xml
index 9b6d629f79..c9b74a9ec5 100644
--- a/lib/diameter/doc/src/diameter_sctp.xml
+++ b/lib/diameter/doc/src/diameter_sctp.xml
@@ -16,7 +16,7 @@
<header>
<copyright>
<year>2011</year>
-<year>2016</year>
+<year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -116,7 +116,6 @@ and port respectively.</p>
Multiple <c>ip</c> options can be specified for a multihomed peer.
If none are specified then the values of <c>Host-IP-Address</c>
in the <c>diameter_service</c> record are used.
-(In particular, one of these must be specified.)
Option <c>port</c> defaults to 3868 for a listening transport and 0 for a
connecting transport.</p>
diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml
index 6ca280c52b..1d65d14257 100644
--- a/lib/diameter/doc/src/diameter_tcp.xml
+++ b/lib/diameter/doc/src/diameter_tcp.xml
@@ -170,14 +170,11 @@ that will not be forthcoming, which will eventually cause the RFC 3539
watchdog to take down the connection.</p>
<p>
-If an <c>ip</c> option is not specified then the first element of a
-non-empty <c>Host-IP-Address</c> list in <c>Svc</c> provides the local
-IP address.
-If neither is specified then the default address selected by &gen_tcp;
-is used.
-In all cases, the selected address is either returned from
-&start; or passed in a <c>connected</c> message over the transport
-interface.</p>
+The first element of a non-empty <c>Host-IP-Address</c> list in
+<c>Svc</c> provides the local IP address if an <c>ip</c> option is not
+specified.
+The local address is either returned from&start; or passed in a
+<c>connected</c> message over the transport interface.</p>
</desc>
</func>
diff --git a/lib/diameter/doc/src/seealso.ent b/lib/diameter/doc/src/seealso.ent
index e5c284c6e8..c5a53670d0 100644
--- a/lib/diameter/doc/src/seealso.ent
+++ b/lib/diameter/doc/src/seealso.ent
@@ -4,7 +4,7 @@
%CopyrightBegin%
-Copyright Ericsson AB 2012-2015. All Rights Reserved.
+Copyright Ericsson AB 2012-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.
@@ -53,7 +53,7 @@ significant.
<!ENTITY mod_application_opt '<seealso marker="diameter#application_opt">diameter:application_opt()</seealso>'>
<!ENTITY mod_call_opt '<seealso marker="diameter#call_opt">diameter:call_opt()</seealso>'>
<!ENTITY mod_capability '<seealso marker="diameter#capability">diameter:capability()</seealso>'>
-<!ENTITY mod_evaluable '<seealso marker="diameter#evaluable">diameter:evaluable()</seealso>'>
+<!ENTITY mod_eval '<seealso marker="diameter#eval">diameter:eval()</seealso>'>
<!ENTITY mod_peer_filter '<seealso marker="diameter#peer_filter">diameter:peer_filter()</seealso>'>
<!ENTITY mod_service_event '<seealso marker="diameter#service_event">diameter:service_event()</seealso>'>
<!ENTITY mod_service_event_info '<seealso marker="diameter#service_event_info">diameter:service_event_info()</seealso>'>
@@ -72,6 +72,7 @@ significant.
<!ENTITY watchdog_timer '<seealso marker="#watchdog_timer">watchdog_timer</seealso>'>
<!ENTITY mod_string_decode '<seealso marker="diameter#service_opt">diameter:service_opt()</seealso> <seealso marker="diameter#string_decode">string_decode</seealso>'>
+<!ENTITY mod_decode_format '<seealso marker="diameter#service_opt">diameter:service_opt()</seealso> <seealso marker="diameter#decode_format">decode_format</seealso>'>
<!-- diameter_app -->
diff --git a/lib/diameter/doc/standard/rfc7683.txt b/lib/diameter/doc/standard/rfc7683.txt
new file mode 100644
index 0000000000..ab2392c6c0
--- /dev/null
+++ b/lib/diameter/doc/standard/rfc7683.txt
@@ -0,0 +1,2355 @@
+
+
+
+
+
+
+Internet Engineering Task Force (IETF) J. Korhonen, Ed.
+Request for Comments: 7683 Broadcom Corporation
+Category: Standards Track S. Donovan, Ed.
+ISSN: 2070-1721 B. Campbell
+ Oracle
+ L. Morand
+ Orange Labs
+ October 2015
+
+
+ Diameter Overload Indication Conveyance
+
+Abstract
+
+ This specification defines a base solution for Diameter overload
+ control, referred to as Diameter Overload Indication Conveyance
+ (DOIC).
+
+Status of This Memo
+
+ This is an Internet Standards Track document.
+
+ This document is a product of the Internet Engineering Task Force
+ (IETF). It represents the consensus of the IETF community. It has
+ received public review and has been approved for publication by the
+ Internet Engineering Steering Group (IESG). Further information on
+ Internet Standards is available in Section 2 of RFC 5741.
+
+ Information about the current status of this document, any errata,
+ and how to provide feedback on it may be obtained at
+ http://www.rfc-editor.org/info/rfc7683.
+
+Copyright Notice
+
+ Copyright (c) 2015 IETF Trust and the persons identified as the
+ document authors. All rights reserved.
+
+ This document is subject to BCP 78 and the IETF Trust's Legal
+ Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info) in effect on the date of
+ publication of this document. Please review these documents
+ carefully, as they describe your rights and restrictions with respect
+ to this document. Code Components extracted from this document must
+ include Simplified BSD License text as described in Section 4.e of
+ the Trust Legal Provisions and are provided without warranty as
+ described in the Simplified BSD License.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 1]
+
+RFC 7683 DOIC October 2015
+
+
+Table of Contents
+
+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
+ 2. Terminology and Abbreviations . . . . . . . . . . . . . . . . 3
+ 3. Conventions Used in This Document . . . . . . . . . . . . . . 5
+ 4. Solution Overview . . . . . . . . . . . . . . . . . . . . . . 5
+ 4.1. Piggybacking . . . . . . . . . . . . . . . . . . . . . . 6
+ 4.2. DOIC Capability Announcement . . . . . . . . . . . . . . 7
+ 4.3. DOIC Overload Condition Reporting . . . . . . . . . . . . 9
+ 4.4. DOIC Extensibility . . . . . . . . . . . . . . . . . . . 11
+ 4.5. Simplified Example Architecture . . . . . . . . . . . . . 12
+ 5. Solution Procedures . . . . . . . . . . . . . . . . . . . . . 12
+ 5.1. Capability Announcement . . . . . . . . . . . . . . . . . 12
+ 5.1.1. Reacting Node Behavior . . . . . . . . . . . . . . . 13
+ 5.1.2. Reporting Node Behavior . . . . . . . . . . . . . . . 13
+ 5.1.3. Agent Behavior . . . . . . . . . . . . . . . . . . . 14
+ 5.2. Overload Report Processing . . . . . . . . . . . . . . . 15
+ 5.2.1. Overload Control State . . . . . . . . . . . . . . . 15
+ 5.2.2. Reacting Node Behavior . . . . . . . . . . . . . . . 19
+ 5.2.3. Reporting Node Behavior . . . . . . . . . . . . . . . 20
+ 5.3. Protocol Extensibility . . . . . . . . . . . . . . . . . 22
+ 6. Loss Algorithm . . . . . . . . . . . . . . . . . . . . . . . 23
+ 6.1. Overview . . . . . . . . . . . . . . . . . . . . . . . . 23
+ 6.2. Reporting Node Behavior . . . . . . . . . . . . . . . . . 24
+ 6.3. Reacting Node Behavior . . . . . . . . . . . . . . . . . 24
+ 7. Attribute Value Pairs . . . . . . . . . . . . . . . . . . . . 25
+ 7.1. OC-Supported-Features AVP . . . . . . . . . . . . . . . . 25
+ 7.2. OC-Feature-Vector AVP . . . . . . . . . . . . . . . . . . 25
+ 7.3. OC-OLR AVP . . . . . . . . . . . . . . . . . . . . . . . 26
+ 7.4. OC-Sequence-Number AVP . . . . . . . . . . . . . . . . . 26
+ 7.5. OC-Validity-Duration AVP . . . . . . . . . . . . . . . . 26
+ 7.6. OC-Report-Type AVP . . . . . . . . . . . . . . . . . . . 27
+ 7.7. OC-Reduction-Percentage AVP . . . . . . . . . . . . . . . 27
+ 7.8. AVP Flag Rules . . . . . . . . . . . . . . . . . . . . . 28
+ 8. Error Response Codes . . . . . . . . . . . . . . . . . . . . 28
+ 9. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 29
+ 9.1. AVP Codes . . . . . . . . . . . . . . . . . . . . . . . . 29
+ 9.2. New Registries . . . . . . . . . . . . . . . . . . . . . 29
+ 10. Security Considerations . . . . . . . . . . . . . . . . . . . 30
+ 10.1. Potential Threat Modes . . . . . . . . . . . . . . . . . 30
+ 10.2. Denial-of-Service Attacks . . . . . . . . . . . . . . . 31
+ 10.3. Noncompliant Nodes . . . . . . . . . . . . . . . . . . . 32
+ 10.4. End-to-End Security Issues . . . . . . . . . . . . . . . 32
+ 11. References . . . . . . . . . . . . . . . . . . . . . . . . . 34
+ 11.1. Normative References . . . . . . . . . . . . . . . . . . 34
+ 11.2. Informative References . . . . . . . . . . . . . . . . . 34
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 2]
+
+RFC 7683 DOIC October 2015
+
+
+ Appendix A. Issues Left for Future Specifications . . . . . . . 35
+ A.1. Additional Traffic Abatement Algorithms . . . . . . . . . 35
+ A.2. Agent Overload . . . . . . . . . . . . . . . . . . . . . 35
+ A.3. New Error Diagnostic AVP . . . . . . . . . . . . . . . . 35
+ Appendix B. Deployment Considerations . . . . . . . . . . . . . 35
+ Appendix C. Considerations for Applications Integrating the DOIC
+ Solution . . . . . . . . . . . . . . . . . . . . . . 36
+ C.1. Application Classification . . . . . . . . . . . . . . . 36
+ C.2. Implications of Application Type Overload . . . . . . . . 37
+ C.3. Request Transaction Classification . . . . . . . . . . . 38
+ C.4. Request Type Overload Implications . . . . . . . . . . . 39
+ Contributors . . . . . . . . . . . . . . . . . . . . . . . . . . 41
+ Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 42
+
+1. Introduction
+
+ This specification defines a base solution for Diameter overload
+ control, referred to as Diameter Overload Indication Conveyance
+ (DOIC), based on the requirements identified in [RFC7068].
+
+ This specification addresses Diameter overload control between
+ Diameter nodes that support the DOIC solution. The solution, which
+ is designed to apply to existing and future Diameter applications,
+ requires no changes to the Diameter base protocol [RFC6733] and is
+ deployable in environments where some Diameter nodes do not implement
+ the Diameter overload control solution defined in this specification.
+
+ A new application specification can incorporate the overload control
+ mechanism specified in this document by making it mandatory to
+ implement for the application and referencing this specification
+ normatively. It is the responsibility of the Diameter application
+ designers to define how overload control mechanisms work on that
+ application.
+
+ Note that the overload control solution defined in this specification
+ does not address all the requirements listed in [RFC7068]. A number
+ of features related to overload control are left for future
+ specifications. See Appendix A for a list of extensions that are
+ currently being considered.
+
+2. Terminology and Abbreviations
+
+ Abatement
+
+ Reaction to receipt of an overload report resulting in a reduction
+ in traffic sent to the reporting node. Abatement actions include
+ diversion and throttling.
+
+
+
+
+Korhonen, et al. Standards Track [Page 3]
+
+RFC 7683 DOIC October 2015
+
+
+ Abatement Algorithm
+
+ An extensible method requested by reporting nodes and used by
+ reacting nodes to reduce the amount of traffic sent during an
+ occurrence of overload control.
+
+ Diversion
+
+ An overload abatement treatment where the reacting node selects
+ alternate destinations or paths for requests.
+
+ Host-Routed Requests
+
+ Requests that a reacting node knows will be served by a particular
+ host, either due to the presence of a Destination-Host Attribute
+ Value Pair (AVP) or by some other local knowledge on the part of
+ the reacting node.
+
+ Overload Control State (OCS)
+
+ Internal state maintained by a reporting or reacting node
+ describing occurrences of overload control.
+
+ Overload Report (OLR)
+
+ Overload control information for a particular overload occurrence
+ sent by a reporting node.
+
+ Reacting Node
+
+ A Diameter node that acts upon an overload report.
+
+ Realm-Routed Requests
+
+ Requests sent by a reacting node where the reacting node does not
+ know to which host the request will be routed.
+
+ Reporting Node
+
+ A Diameter node that generates an overload report. (This may or
+ may not be the overloaded node.)
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 4]
+
+RFC 7683 DOIC October 2015
+
+
+ Throttling
+
+ An abatement treatment that limits the number of requests sent by
+ the reacting node. Throttling can include a Diameter Client
+ choosing to not send requests, or a Diameter Agent or Server
+ rejecting requests with appropriate error responses. In both
+ cases, the result of the throttling is a permanent rejection of
+ the transaction.
+
+3. Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [RFC2119].
+
+ The interpretation from RFC 2119 [RFC2119] does not apply for the
+ above listed words when they are not used in all caps.
+
+4. Solution Overview
+
+ The Diameter Overload Information Conveyance (DOIC) solution allows
+ Diameter nodes to request that other Diameter nodes perform overload
+ abatement actions, that is, actions to reduce the load offered to the
+ overloaded node or realm.
+
+ A Diameter node that supports DOIC is known as a "DOIC node". Any
+ Diameter node can act as a DOIC node, including Diameter Clients,
+ Diameter Servers, and Diameter Agents. DOIC nodes are further
+ divided into "Reporting Nodes" and "Reacting Nodes." A reporting
+ node requests overload abatement by sending Overload Reports (OLRs).
+
+ A reacting node acts upon OLRs and performs whatever actions are
+ needed to fulfill the abatement requests included in the OLRs. A
+ reporting node may report overload on its own behalf or on behalf of
+ other nodes. Likewise, a reacting node may perform overload
+ abatement on its own behalf or on behalf of other nodes.
+
+ A Diameter node's role as a DOIC node is independent of its Diameter
+ role. For example, Diameter Agents may act as DOIC nodes, even
+ though they are not endpoints in the Diameter sense. Since Diameter
+ enables bidirectional applications, where Diameter Servers can send
+ requests towards Diameter Clients, a given Diameter node can
+ simultaneously act as both a reporting node and a reacting node.
+
+ Likewise, a Diameter Agent may act as a reacting node from the
+ perspective of upstream nodes, and a reporting node from the
+ perspective of downstream nodes.
+
+
+
+
+Korhonen, et al. Standards Track [Page 5]
+
+RFC 7683 DOIC October 2015
+
+
+ DOIC nodes do not generate new messages to carry DOIC-related
+ information. Rather, they "piggyback" DOIC information over existing
+ Diameter messages by inserting new AVPs into existing Diameter
+ requests and responses. Nodes indicate support for DOIC, and any
+ needed DOIC parameters, by inserting an OC-Supported-Features AVP
+ (Section 7.1) into existing requests and responses. Reporting nodes
+ send OLRs by inserting OC-OLR AVPs (Section 7.3).
+
+ A given OLR applies to the Diameter realm and application of the
+ Diameter message that carries it. If a reporting node supports more
+ than one realm and/or application, it reports independently for each
+ combination of realm and application. Similarly, the OC-Supported-
+ Features AVP applies to the realm and application of the enclosing
+ message. This implies that a node may support DOIC for one
+ application and/or realm, but not another, and may indicate different
+ DOIC parameters for each application and realm for which it supports
+ DOIC.
+
+ Reacting nodes perform overload abatement according to an agreed-upon
+ abatement algorithm. An abatement algorithm defines the meaning of
+ some of the parameters of an OLR and the procedures required for
+ overload abatement. An overload abatement algorithm separates
+ Diameter requests into two sets. The first set contains the requests
+ that are to undergo overload abatement treatment of either throttling
+ or diversion. The second set contains the requests that are to be
+ given normal routing treatment. This document specifies a single
+ "must-support" algorithm, namely, the "loss" algorithm (Section 6).
+ Future specifications may introduce new algorithms.
+
+ Overload conditions may vary in scope. For example, a single
+ Diameter node may be overloaded, in which case, reacting nodes may
+ attempt to send requests to other destinations. On the other hand,
+ an entire Diameter realm may be overloaded, in which case, such
+ attempts would do harm. DOIC OLRs have a concept of "report type"
+ (Section 7.6), where the type defines such behaviors. Report types
+ are extensible. This document defines report types for overload of a
+ specific host and for overload of an entire realm.
+
+ DOIC works through non-supporting Diameter Agents that properly pass
+ unknown AVPs unchanged.
+
+4.1. Piggybacking
+
+ There is no new Diameter application defined to carry overload-
+ related AVPs. The overload control AVPs defined in this
+ specification have been designed to be piggybacked on top of existing
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 6]
+
+RFC 7683 DOIC October 2015
+
+
+ application messages. This is made possible by adding the optional
+ overload control AVPs OC-OLR and OC-Supported-Features into existing
+ commands.
+
+ Reacting nodes indicate support for DOIC by including the
+ OC-Supported-Features AVP in all request messages originated or
+ relayed by the reacting node.
+
+ Reporting nodes indicate support for DOIC by including the
+ OC-Supported-Features AVP in all answer messages that are originated
+ or relayed by the reporting node and that are in response to a
+ request that contained the OC-Supported-Features AVP. Reporting
+ nodes may include overload reports using the OC-OLR AVP in answer
+ messages.
+
+ Note that the overload control solution does not have fixed server
+ and client roles. The DOIC node role is determined based on the
+ message type: whether the message is a request (i.e., sent by a
+ "reacting node") or an answer (i.e., sent by a "reporting node").
+ Therefore, in a typical client-server deployment, the Diameter Client
+ may report its overload condition to the Diameter Server for any
+ Diameter-Server-initiated message exchange. An example of such is
+ the Diameter Server requesting a re-authentication from a Diameter
+ Client.
+
+4.2. DOIC Capability Announcement
+
+ The DOIC solution supports the ability for Diameter nodes to
+ determine if other nodes in the path of a request support the
+ solution. This capability is referred to as DOIC Capability
+ Announcement (DCA) and is separate from the Diameter Capability
+ Exchange.
+
+ The DCA mechanism uses the OC-Supported-Features AVPs to indicate the
+ Diameter overload features supported.
+
+ The first node in the path of a Diameter request that supports the
+ DOIC solution inserts the OC-Supported-Features AVP in the request
+ message.
+
+ The individual features supported by the DOIC nodes are indicated in
+ the OC-Feature-Vector AVP. Any semantics associated with the
+ features will be defined in extension specifications that introduce
+ the features.
+
+ Note: As discussed elsewhere in the document, agents in the path
+ of the request can modify the OC-Supported-Features AVP.
+
+
+
+
+Korhonen, et al. Standards Track [Page 7]
+
+RFC 7683 DOIC October 2015
+
+
+ Note: The DOIC solution must support deployments where Diameter
+ Clients and/or Diameter Servers do not support the DOIC solution.
+ In this scenario, Diameter Agents that support the DOIC solution
+ may handle overload abatement for the non-supporting Diameter
+ nodes. In this case, the DOIC agent will insert the OC-Supported-
+ Features AVP in requests that do not already contain one, telling
+ the reporting node that there is a DOIC node that will handle
+ overload abatement. For transactions where there was an
+ OC-Supporting-Features AVP in the request, the agent will insert
+ the OC-Supported-Features AVP in answers, telling the reacting
+ node that there is a reporting node.
+
+ The OC-Feature-Vector AVP will always contain an indication of
+ support for the loss overload abatement algorithm defined in this
+ specification (see Section 6). This ensures that a reporting node
+ always supports at least one of the advertised abatement algorithms
+ received in a request messages.
+
+ The reporting node inserts the OC-Supported-Features AVP in all
+ answer messages to requests that contained the OC-Supported-Features
+ AVP. The contents of the reporting node's OC-Supported-Features AVP
+ indicate the set of Diameter overload features supported by the
+ reporting node. This specification defines one exception -- the
+ reporting node only includes an indication of support for one
+ overload abatement algorithm, independent of the number of overload
+ abatement algorithms actually supported by the reacting node. The
+ overload abatement algorithm indicated is the algorithm that the
+ reporting node intends to use should it enter an overload condition.
+ Reacting nodes can use the indicated overload abatement algorithm to
+ prepare for possible overload reports and must use the indicated
+ overload abatement algorithm if traffic reduction is actually
+ requested.
+
+ Note that the loss algorithm defined in this document is a
+ stateless abatement algorithm. As a result, it does not require
+ any actions by reacting nodes prior to the receipt of an overload
+ report. Stateful abatement algorithms that base the abatement
+ logic on a history of request messages sent might require reacting
+ nodes to maintain state in advance of receiving an overload report
+ to ensure that the overload reports can be properly handled.
+
+ While it should only be done in exceptional circumstances and not
+ during an active occurrence of overload, a reacting node that wishes
+ to transition to a different abatement algorithm can stop advertising
+ support for the algorithm indicated by the reporting node, as long as
+ support for the loss algorithm is always advertised.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 8]
+
+RFC 7683 DOIC October 2015
+
+
+ The DCA mechanism must also allow the scenario where the set of
+ features supported by the sender of a request and by agents in the
+ path of a request differ. In this case, the agent can update the
+ OC-Supported-Features AVP to reflect the mixture of the two sets of
+ supported features.
+
+ Note: The logic to determine if the content of the OC-Supported-
+ Features AVP should be changed is out of scope for this document,
+ as is the logic to determine the content of a modified
+ OC-Supported-Features AVP. These are left to implementation
+ decisions. Care must be taken not to introduce interoperability
+ issues for downstream or upstream DOIC nodes. As such, the agent
+ must act as a fully compliant reporting node to the downstream
+ reacting node and as a fully compliant reacting node to the
+ upstream reporting node.
+
+4.3. DOIC Overload Condition Reporting
+
+ As with DOIC capability announcement, overload condition reporting
+ uses new AVPs (Section 7.3) to indicate an overload condition.
+
+ The OC-OLR AVP is referred to as an overload report. The OC-OLR AVP
+ includes the type of report, a sequence number, the length of time
+ that the report is valid, and AVPs specific to the abatement
+ algorithm.
+
+ Two types of overload reports are defined in this document: host
+ reports and realm reports.
+
+ A report of type "HOST_REPORT" is sent to indicate the overload of a
+ specific host, identified by the Origin-Host AVP of the message
+ containing the OLR, for the Application-ID indicated in the
+ transaction. When receiving an OLR of type "HOST_REPORT", a reacting
+ node applies overload abatement treatment to the host-routed requests
+ identified by the overload abatement algorithm (as defined in
+ Section 2) sent for this application to the overloaded host.
+
+ A report of type "REALM_REPORT" is sent to indicate the overload of a
+ realm for the Application-ID indicated in the transaction. The
+ overloaded realm is identified by the Destination-Realm AVP of the
+ message containing the OLR. When receiving an OLR of type
+ "REALM_REPORT", a reacting node applies overload abatement treatment
+ to realm-routed requests identified by the overload abatement
+ algorithm (as defined in Section 2) sent for this application to the
+ overloaded realm.
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 9]
+
+RFC 7683 DOIC October 2015
+
+
+ This document assumes that there is a single source for realm reports
+ for a given realm, or that if multiple nodes can send realm reports,
+ that each such node has full knowledge of the overload state of the
+ entire realm. A reacting node cannot distinguish between receiving
+ realm reports from a single node or from multiple nodes.
+
+ Note: Known issues exist if there are multiple sources for
+ overload reports that apply to the same Diameter entity. Reacting
+ nodes have no way of determining the source and, as such, will
+ treat them as coming from a single source. Variance in sequence
+ numbers between the two sources can then cause incorrect overload
+ abatement treatment to be applied for indeterminate periods of
+ time.
+
+ Reporting nodes are responsible for determining the need for a
+ reduction of traffic. The method for making this determination is
+ implementation specific and depends on the type of overload report
+ being generated. A host report might be generated by tracking use of
+ resources required by the host to handle transactions for the
+ Diameter application. A realm report generally impacts the traffic
+ sent to multiple hosts and, as such, requires tracking the capacity
+ of all servers able to handle realm-routed requests for the
+ application and realm.
+
+ Once a reporting node determines the need for a reduction in traffic,
+ it uses the DOIC-defined AVPs to report on the condition. These AVPs
+ are included in answer messages sent or relayed by the reporting
+ node. The reporting node indicates the overload abatement algorithm
+ that is to be used to handle the traffic reduction in the
+ OC-Supported-Features AVP. The OC-OLR AVP is used to communicate
+ information about the requested reduction.
+
+ Reacting nodes, upon receipt of an overload report, apply the
+ overload abatement algorithm to traffic impacted by the overload
+ report. The method used to determine the requests that are to
+ receive overload abatement treatment is dependent on the abatement
+ algorithm. The loss abatement algorithm is defined in this document
+ (Section 6). Other abatement algorithms can be defined in extensions
+ to the DOIC solution.
+
+ Two types of overload abatement treatment are defined, diversion and
+ throttling. Reacting nodes are responsible for determining which
+ treatment is appropriate for individual requests.
+
+ As the conditions that lead to the generation of the overload report
+ change, the reporting node can send new overload reports requesting
+ greater reduction if the condition gets worse or less reduction if
+ the condition improves. The reporting node sends an overload report
+
+
+
+Korhonen, et al. Standards Track [Page 10]
+
+RFC 7683 DOIC October 2015
+
+
+ with a duration of zero to indicate that the overload condition has
+ ended and abatement is no longer needed.
+
+ The reacting node also determines when the overload report expires
+ based on the OC-Validity-Duration AVP in the overload report and
+ stops applying the abatement algorithm when the report expires.
+
+ Note that erroneous overload reports can be used for DoS attacks.
+ This includes the ability to indicate that a significant reduction in
+ traffic, up to and including a request for no traffic, should be sent
+ to a reporting node. As such, care should be taken to verify the
+ sender of overload reports.
+
+4.4. DOIC Extensibility
+
+ The DOIC solution is designed to be extensible. This extensibility
+ is based on existing Diameter-based extensibility mechanisms, along
+ with the DOIC capability announcement mechanism.
+
+ There are multiple categories of extensions that are expected. This
+ includes the definition of new overload abatement algorithms, the
+ definition of new report types, and the definition of new scopes of
+ messages impacted by an overload report.
+
+ A DOIC node communicates supported features by including them in the
+ OC-Feature-Vector AVP, as a sub-AVP of OC-Supported-Features. Any
+ non-backwards-compatible DOIC extensions define new values for the
+ OC-Feature-Vector AVP. DOIC extensions also have the ability to add
+ new AVPs to the OC-Supported-Features AVP, if additional information
+ about the new feature is required.
+
+ Overload reports can also be extended by adding new sub-AVPs to the
+ OC-OLR AVP, allowing reporting nodes to communicate additional
+ information about handling an overload condition.
+
+ If necessary, new extensions can also define new AVPs that are not
+ part of the OC-Supported-Features and OC-OLR group AVPs. It is,
+ however, recommended that DOIC extensions use the OC-Supported-
+ Features AVP and OC-OLR AVP to carry all DOIC-related AVPs.
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 11]
+
+RFC 7683 DOIC October 2015
+
+
+4.5. Simplified Example Architecture
+
+ Figure 1 illustrates the simplified architecture for Diameter
+ overload information conveyance.
+
+ Realm X Same or other Realms
+ <--------------------------------------> <---------------------->
+
+
+ +--------+ : (optional) :
+ |Diameter| : :
+ |Server A|--+ .--. : +--------+ : .--.
+ +--------+ | _( `. : |Diameter| : _( `. +--------+
+ +--( )--:-| Agent |-:--( )--|Diameter|
+ +--------+ | ( ` . ) ) : +--------+ : ( ` . ) ) | Client |
+ |Diameter|--+ `--(___.-' : : `--(___.-' +--------+
+ |Server B| : :
+ +--------+ : :
+
+ End-to-end Overload Indication
+ 1) <----------------------------------------------->
+ Diameter Application Y
+
+ Overload Indication A Overload Indication A'
+ 2) <----------------------> <---------------------->
+ Diameter Application Y Diameter Application Y
+
+ Figure 1: Simplified Architecture Choices for Overload Indication
+ Delivery
+
+ In Figure 1, the Diameter overload indication can be conveyed (1)
+ end-to-end between servers and clients or (2) between servers and the
+ Diameter Agent inside the realm and then between the Diameter Agent
+ and the clients.
+
+5. Solution Procedures
+
+ This section outlines the normative behavior for the DOIC solution.
+
+5.1. Capability Announcement
+
+ This section defines DOIC Capability Announcement (DCA) behavior.
+
+ Note: This specification assumes that changes in DOIC node
+ capabilities are relatively rare events that occur as a result of
+ administrative action. Reacting nodes ought to minimize changes
+ that force the reporting node to change the features being used,
+ especially during active overload conditions. But even if
+
+
+
+Korhonen, et al. Standards Track [Page 12]
+
+RFC 7683 DOIC October 2015
+
+
+ reacting nodes avoid such changes, reporting nodes still have to
+ be prepared for them to occur. For example, differing
+ capabilities between multiple reacting nodes may still force a
+ reporting node to select different features on a per-transaction
+ basis.
+
+5.1.1. Reacting Node Behavior
+
+ A reacting node MUST include the OC-Supported-Features AVP in all
+ requests. It MAY include the OC-Feature-Vector AVP, as a sub-AVP of
+ OC-Supported-Features. If it does so, it MUST indicate support for
+ the "loss" algorithm. If the reacting node is configured to support
+ features (including other algorithms) in addition to the loss
+ algorithm, it MUST indicate such support in an OC-Feature-Vector AVP.
+
+ An OC-Supported-Features AVP in answer messages indicates there is a
+ reporting node for the transaction. The reacting node MAY take
+ action, for example, creating state for some stateful abatement
+ algorithm, based on the features indicated in the OC-Feature-Vector
+ AVP.
+
+ Note: The loss abatement algorithm does not require stateful
+ behavior when there is no active overload report.
+
+ Reacting nodes need to be prepared for the reporting node to change
+ selected algorithms. This can happen at any time, including when the
+ reporting node has sent an active overload report. The reacting node
+ can minimize the potential for changes by modifying the advertised
+ abatement algorithms sent to an overloaded reporting node to the
+ currently selected algorithm and loss (or just loss if it is the
+ currently selected algorithm). This has the effect of limiting the
+ potential change in abatement algorithm from the currently selected
+ algorithm to loss, avoiding changes to more complex abatement
+ algorithms that require state to operate properly.
+
+5.1.2. Reporting Node Behavior
+
+ Upon receipt of a request message, a reporting node determines if
+ there is a reacting node for the transaction based on the presence of
+ the OC-Supported-Features AVP in the request message.
+
+ If the request message contains an OC-Supported-Features AVP, then a
+ reporting node MUST include the OC-Supported-Features AVP in the
+ answer message for that transaction.
+
+ Note: Capability announcement is done on a per-transaction basis.
+ The reporting node cannot assume that the capabilities announced
+ by a reacting node will be the same between transactions.
+
+
+
+Korhonen, et al. Standards Track [Page 13]
+
+RFC 7683 DOIC October 2015
+
+
+ A reporting node MUST NOT include the OC-Supported-Features AVP,
+ OC-OLR AVP, or any other overload control AVPs defined in extension
+ documents in response messages for transactions where the request
+ message does not include the OC-Supported-Features AVP. Lack of the
+ OC-Supported-Features AVP in the request message indicates that there
+ is no reacting node for the transaction.
+
+ A reporting node knows what overload control functionality is
+ supported by the reacting node based on the content or absence of the
+ OC-Feature-Vector AVP within the OC-Supported-Features AVP in the
+ request message.
+
+ A reporting node MUST select a single abatement algorithm in the
+ OC-Feature-Vector AVP. The abatement algorithm selected MUST
+ indicate the abatement algorithm the reporting node wants the
+ reacting node to use when the reporting node enters an overload
+ condition.
+
+ The abatement algorithm selected MUST be from the set of abatement
+ algorithms contained in the request message's OC-Feature-Vector AVP.
+
+ A reporting node that selects the loss algorithm may do so by
+ including the OC-Feature-Vector AVP with an explicit indication of
+ the loss algorithm, or it MAY omit the OC-Feature-Vector AVP. If it
+ selects a different algorithm, it MUST include the OC-Feature-Vector
+ AVP with an explicit indication of the selected algorithm.
+
+ The reporting node SHOULD indicate support for other DOIC features
+ defined in extension documents that it supports and that apply to the
+ transaction. It does so using the OC-Feature-Vector AVP.
+
+ Note: Not all DOIC features will apply to all Diameter
+ applications or deployment scenarios. The features included in
+ the OC-Feature-Vector AVP are based on local policy of the
+ reporting node.
+
+5.1.3. Agent Behavior
+
+ Diameter Agents that support DOIC can ensure that all messages
+ relayed by the agent contain the OC-Supported-Features AVP.
+
+ A Diameter Agent MAY take on reacting node behavior for Diameter
+ endpoints that do not support the DOIC solution. A Diameter Agent
+ detects that a Diameter endpoint does not support DOIC reacting node
+ behavior when there is no OC-Supported-Features AVP in a request
+ message.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 14]
+
+RFC 7683 DOIC October 2015
+
+
+ For a Diameter Agent to be a reacting node for a non-supporting
+ Diameter endpoint, the Diameter Agent MUST include the OC-Supported-
+ Features AVP in request messages it relays that do not contain the
+ OC-Supported-Features AVP.
+
+ A Diameter Agent MAY take on reporting node behavior for Diameter
+ endpoints that do not support the DOIC solution. The Diameter Agent
+ MUST have visibility to all traffic destined for the non-supporting
+ host in order to become the reporting node for the Diameter endpoint.
+ A Diameter Agent detects that a Diameter endpoint does not support
+ DOIC reporting node behavior when there is no OC-Supported-Features
+ AVP in an answer message for a transaction that contained the
+ OC-Supported-Features AVP in the request message.
+
+ If a request already has the OC-Supported-Features AVP, a Diameter
+ Agent MAY modify it to reflect the features appropriate for the
+ transaction. Otherwise, the agent relays the OC-Supported-Features
+ AVP without change.
+
+ Example: If the agent supports a superset of the features reported
+ by the reacting node, then the agent might choose, based on local
+ policy, to advertise that superset of features to the reporting
+ node.
+
+ If the Diameter Agent changes the OC-Supported-Features AVP in a
+ request message, then it is likely it will also need to modify the
+ OC-Supported-Features AVP in the answer message for the transaction.
+ A Diameter Agent MAY modify the OC-Supported-Features AVP carried in
+ answer messages.
+
+ When making changes to the OC-Supported-Features or OC-OLR AVPs, the
+ Diameter Agent needs to ensure consistency in its behavior with both
+ upstream and downstream DOIC nodes.
+
+5.2. Overload Report Processing
+
+5.2.1. Overload Control State
+
+ Both reacting and reporting nodes maintain Overload Control State
+ (OCS) for active overload conditions. The following sections define
+ behavior associated with that OCS.
+
+ The contents of the OCS in the reporting node and in the reacting
+ node represent logical constructs. The actual internal physical
+ structure of the state included in the OCS is an implementation
+ decision.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 15]
+
+RFC 7683 DOIC October 2015
+
+
+5.2.1.1. Overload Control State for Reacting Nodes
+
+ A reacting node maintains the following OCS per supported Diameter
+ application:
+
+ o a host-type OCS entry for each Destination-Host to which it sends
+ host-type requests and
+
+ o a realm-type OCS entry for each Destination-Realm to which it
+ sends realm-type requests.
+
+ A host-type OCS entry is identified by the pair of Application-ID and
+ the node's DiameterIdentity.
+
+ A realm-type OCS entry is identified by the pair of Application-ID
+ and realm.
+
+ The host-type and realm-type OCS entries include the following
+ information (the actual information stored is an implementation
+ decision):
+
+ o Sequence number (as received in OC-OLR; see Section 7.3)
+
+ o Time of expiry (derived from OC-Validity-Duration AVP received in
+ the OC-OLR AVP and time of reception of the message carrying
+ OC-OLR AVP)
+
+ o Selected abatement algorithm (as received in the OC-Supported-
+ Features AVP)
+
+ o Input data that is abatement algorithm specific (as received in
+ the OC-OLR AVP -- for example, OC-Reduction-Percentage for the
+ loss abatement algorithm)
+
+5.2.1.2. Overload Control State for Reporting Nodes
+
+ A reporting node maintains OCS entries per supported Diameter
+ application, per supported (and eventually selected) abatement
+ algorithm, and per report type.
+
+ An OCS entry is identified by the tuple of Application-ID, report
+ type, and abatement algorithm, and it includes the following
+ information (the actual information stored is an implementation
+ decision):
+
+ o Sequence number
+
+ o Validity duration
+
+
+
+Korhonen, et al. Standards Track [Page 16]
+
+RFC 7683 DOIC October 2015
+
+
+ o Expiration time
+
+ o Input data that is algorithm specific (for example, the reduction
+ percentage for the loss abatement algorithm)
+
+5.2.1.3. Reacting Node's Maintenance of Overload Control State
+
+ When a reacting node receives an OC-OLR AVP, it MUST determine if it
+ is for an existing or new overload condition.
+
+ Note: For the remainder of this section, the term "OLR" refers to
+ the combination of the contents of the received OC-OLR AVP and the
+ abatement algorithm indicated in the received OC-Supported-
+ Features AVP.
+
+ When receiving an answer message with multiple OLRs of different
+ supported report types, a reacting node MUST process each received
+ OLR.
+
+ The OLR is for an existing overload condition if a reacting node has
+ an OCS that matches the received OLR.
+
+ For a host report, this means it matches the Application-ID and the
+ host's DiameterIdentity in an existing host OCS entry.
+
+ For a realm report, this means it matches the Application-ID and the
+ realm in an existing realm OCS entry.
+
+ If the OLR is for an existing overload condition, then a reacting
+ node MUST determine if the OLR is a retransmission or an update to
+ the existing OLR.
+
+ If the sequence number for the received OLR is greater than the
+ sequence number stored in the matching OCS entry, then a reacting
+ node MUST update the matching OCS entry.
+
+ If the sequence number for the received OLR is less than or equal to
+ the sequence number in the matching OCS entry, then a reacting node
+ MUST silently ignore the received OLR. The matching OCS MUST NOT be
+ updated in this case.
+
+ If the reacting node determines that the sequence number has rolled
+ over, then the reacting node MUST update the matching OCS entry.
+ This can be determined by recognizing that the number has changed
+ from a value within 1% of the maximum value in the OC-Sequence-Number
+ AVP to a value within 1% of the minimum value in the OC-Sequence-
+ Number AVP.
+
+
+
+
+Korhonen, et al. Standards Track [Page 17]
+
+RFC 7683 DOIC October 2015
+
+
+ If the received OLR is for a new overload condition, then a reacting
+ node MUST generate a new OCS entry for the overload condition.
+
+ For a host report, this means a reacting node creates an OCS entry
+ with the Application-ID in the received message and DiameterIdentity
+ of the Origin-Host in the received message.
+
+ Note: This solution assumes that the Origin-Host AVP in the answer
+ message included by the reporting node is not changed along the
+ path to the reacting node.
+
+ For a realm report, this means a reacting node creates an OCS entry
+ with the Application-ID in the received message and realm of the
+ Origin-Realm in the received message.
+
+ If the received OLR contains a validity duration of zero ("0"), then
+ a reacting node MUST update the OCS entry as being expired.
+
+ Note: It is not necessarily appropriate to delete the OCS entry,
+ as the recommended behavior is that the reacting node slowly
+ returns to full traffic when ending an overload abatement period.
+
+ The reacting node does not delete an OCS when receiving an answer
+ message that does not contain an OC-OLR AVP (i.e., absence of OLR
+ means "no change").
+
+5.2.1.4. Reporting Node's Maintenance of Overload Control State
+
+ A reporting node SHOULD create a new OCS entry when entering an
+ overload condition.
+
+ Note: If a reporting node knows through absence of the
+ OC-Supported-Features AVP in received messages that there are no
+ reacting nodes supporting DOIC, then the reporting node can choose
+ to not create OCS entries.
+
+ When generating a new OCS entry, the sequence number SHOULD be set to
+ zero ("0").
+
+ When generating sequence numbers for new overload conditions, the new
+ sequence number MUST be greater than any sequence number in an active
+ (unexpired) overload report for the same application and report type
+ previously sent by the reporting node. This property MUST hold over
+ a reboot of the reporting node.
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 18]
+
+RFC 7683 DOIC October 2015
+
+
+ Note: One way of addressing this over a reboot of a reporting node
+ is to use a timestamp for the first overload condition that occurs
+ after the report and to start using sequences beginning with zero
+ for subsequent overload conditions.
+
+ A reporting node MUST update an OCS entry when it needs to adjust the
+ validity duration of the overload condition at reacting nodes.
+
+ Example: If a reporting node wishes to instruct reacting nodes to
+ continue overload abatement for a longer period of time than
+ originally communicated. This also applies if the reporting node
+ wishes to shorten the period of time that overload abatement is to
+ continue.
+
+ A reporting node MUST update an OCS entry when it wishes to adjust
+ any parameters specific to the abatement algorithm, including, for
+ example, the reduction percentage used for the loss abatement
+ algorithm.
+
+ Example: If a reporting node wishes to change the reduction
+ percentage either higher (if the overload condition has worsened)
+ or lower (if the overload condition has improved), then the
+ reporting node would update the appropriate OCS entry.
+
+ A reporting node MUST increment the sequence number associated with
+ the OCS entry anytime the contents of the OCS entry are changed.
+ This will result in a new sequence number being sent to reacting
+ nodes, instructing them to process the OC-OLR AVP.
+
+ A reporting node SHOULD update an OCS entry with a validity duration
+ of zero ("0") when the overload condition ends.
+
+ Note: If a reporting node knows that the OCS entries in the
+ reacting nodes are near expiration, then the reporting node might
+ decide not to send an OLR with a validity duration of zero.
+
+ A reporting node MUST keep an OCS entry with a validity duration of
+ zero ("0") for a period of time long enough to ensure that any
+ unexpired reacting node's OCS entry created as a result of the
+ overload condition in the reporting node is deleted.
+
+5.2.2. Reacting Node Behavior
+
+ When a reacting node sends a request, it MUST determine if that
+ request matches an active OCS.
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 19]
+
+RFC 7683 DOIC October 2015
+
+
+ If the request matches an active OCS, then the reacting node MUST use
+ the overload abatement algorithm indicated in the OCS to determine if
+ the request is to receive overload abatement treatment.
+
+ For the loss abatement algorithm defined in this specification, see
+ Section 6 for the overload abatement algorithm logic applied.
+
+ If the overload abatement algorithm selects the request for overload
+ abatement treatment, then the reacting node MUST apply overload
+ abatement treatment on the request. The abatement treatment applied
+ depends on the context of the request.
+
+ If diversion abatement treatment is possible (i.e., a different path
+ for the request can be selected where the overloaded node is not part
+ of the different path), then the reacting node SHOULD apply diversion
+ abatement treatment to the request. The reacting node MUST apply
+ throttling abatement treatment to requests identified for abatement
+ treatment when diversion treatment is not possible or was not
+ applied.
+
+ Note: This only addresses the case where there are two defined
+ abatement treatments, diversion and throttling. Any extension
+ that defines a new abatement treatment must also define its
+ interaction with existing treatments.
+
+ If the overload abatement treatment results in throttling of the
+ request and if the reacting node is an agent, then the agent MUST
+ send an appropriate error as defined in Section 8.
+
+ Diameter endpoints that throttle requests need to do so according to
+ the rules of the client application. Those rules will vary by
+ application and are beyond the scope of this document.
+
+ In the case that the OCS entry indicated no traffic was to be sent to
+ the overloaded entity and the validity duration expires, then
+ overload abatement associated with the overload report MUST be ended
+ in a controlled fashion.
+
+5.2.3. Reporting Node Behavior
+
+ If there is an active OCS entry, then a reporting node SHOULD include
+ the OC-OLR AVP in all answers to requests that contain the
+ OC-Supported-Features AVP and that match the active OCS entry.
+
+ Note: A request matches 1) if the Application-ID in the request
+ matches the Application-ID in any active OCS entry and 2) if the
+ report type in the OCS entry matches a report type supported by
+ the reporting node as indicated in the OC-Supported-Features AVP.
+
+
+
+Korhonen, et al. Standards Track [Page 20]
+
+RFC 7683 DOIC October 2015
+
+
+ The contents of the OC-OLR AVP depend on the selected algorithm.
+
+ A reporting node MAY choose to not resend an overload report to a
+ reacting node if it can guarantee that this overload report is
+ already active in the reacting node.
+
+ Note: In some cases (e.g., when there are one or more agents in
+ the path between reporting and reacting nodes, or when overload
+ reports are discarded by reacting nodes), a reporting node may not
+ be able to guarantee that the reacting node has received the
+ report.
+
+ A reporting node MUST NOT send overload reports of a type that has
+ not been advertised as supported by the reacting node.
+
+ Note: A reacting node implicitly advertises support for the host
+ and realm report types by including the OC-Supported-Features AVP
+ in the request. Support for other report types will be explicitly
+ indicated by new feature bits in the OC-Feature-Vector AVP.
+
+ A reporting node SHOULD explicitly indicate the end of an overload
+ occurrence by sending a new OLR with OC-Validity-Duration set to a
+ value of zero ("0"). The reporting node SHOULD ensure that all
+ reacting nodes receive the updated overload report.
+
+ A reporting node MAY rely on the OC-Validity-Duration AVP values for
+ the implicit cleanup of overload control state on the reacting node.
+
+ Note: All OLRs sent have an expiration time calculated by adding
+ the validity duration contained in the OLR to the time the message
+ was sent. Transit time for the OLR can be safely ignored. The
+ reporting node can ensure that all reacting nodes have received
+ the OLR by continuing to send it in answer messages until the
+ expiration time for all OLRs sent for that overload condition have
+ expired.
+
+ When a reporting node sends an OLR, it effectively delegates any
+ necessary throttling to downstream nodes. If the reporting node also
+ locally throttles the same set of messages, the overall number of
+ throttled requests may be higher than intended. Therefore, before
+ applying local message throttling, a reporting node needs to check if
+ these messages match existing OCS entries, indicating that these
+ messages have survived throttling applied by downstream nodes that
+ have received the related OLR.
+
+ However, even if the set of messages match existing OCS entries, the
+ reporting node can still apply other abatement methods such as
+ diversion. The reporting node might also need to throttle requests
+
+
+
+Korhonen, et al. Standards Track [Page 21]
+
+RFC 7683 DOIC October 2015
+
+
+ for reasons other than overload. For example, an agent or server
+ might have a configured rate limit for each client and might throttle
+ requests that exceed that limit, even if such requests had already
+ been candidates for throttling by downstream nodes. The reporting
+ node also has the option to send new OLRs requesting greater
+ reductions in traffic, reducing the need for local throttling.
+
+ A reporting node SHOULD decrease requested overload abatement
+ treatment in a controlled fashion to avoid oscillations in traffic.
+
+ Example: A reporting node might wait some period of time after
+ overload ends before terminating the OLR, or it might send a
+ series of OLRs indicating progressively less overload severity.
+
+5.3. Protocol Extensibility
+
+ The DOIC solution can be extended. Types of potential extensions
+ include new traffic abatement algorithms, new report types, or other
+ new functionality.
+
+ When defining a new extension that requires new normative behavior,
+ the specification must define a new feature for the OC-Feature-Vector
+ AVP. This feature bit is used to communicate support for the new
+ feature.
+
+ The extension may define new AVPs for use in the DOIC Capability
+ Announcement and for use in DOIC overload reporting. These new AVPs
+ SHOULD be defined to be extensions to the OC-Supported-Features or
+ OC-OLR AVPs defined in this document.
+
+ The Grouped AVP extension mechanisms defined in [RFC6733] apply.
+ This allows, for example, defining a new feature that is mandatory to
+ be understood even when piggybacked on an existing application.
+
+ When defining new report type values, the corresponding specification
+ must define the semantics of the new report types and how they affect
+ the OC-OLR AVP handling.
+
+ The OC-Supported-Feature and OC-OLR AVPs can be expanded with
+ optional sub-AVPs only if a legacy DOIC implementation can safely
+ ignore them without breaking backward compatibility for the given
+ OC-Report-Type AVP value. Any new sub-AVPs must not require that the
+ M-bit be set.
+
+ Documents that introduce new report types must describe any
+ limitations on their use across non-supporting agents.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 22]
+
+RFC 7683 DOIC October 2015
+
+
+ As with any Diameter specification, RFC 6733 requires all new AVPs to
+ be registered with IANA. See Section 9 for the required procedures.
+ New features (feature bits in the OC-Feature-Vector AVP) and report
+ types (in the OC-Report-Type AVP) MUST be registered with IANA.
+
+6. Loss Algorithm
+
+ This section documents the Diameter overload loss abatement
+ algorithm.
+
+6.1. Overview
+
+ The DOIC specification supports the ability for multiple overload
+ abatement algorithms to be specified. The abatement algorithm used
+ for any instance of overload is determined by the DOIC Capability
+ Announcement process documented in Section 5.1.
+
+ The loss algorithm described in this section is the default algorithm
+ that must be supported by all Diameter nodes that support DOIC.
+
+ The loss algorithm is designed to be a straightforward and stateless
+ overload abatement algorithm. It is used by reporting nodes to
+ request a percentage reduction in the amount of traffic sent. The
+ traffic impacted by the requested reduction depends on the type of
+ overload report.
+
+ Reporting nodes request the stateless reduction of the number of
+ requests by an indicated percentage. This percentage reduction is in
+ comparison to the number of messages the node otherwise would send,
+ regardless of how many requests the node might have sent in the past.
+
+ From a conceptual level, the logic at the reacting node could be
+ outlined as follows.
+
+ 1. An overload report is received, and the associated OCS is either
+ saved or updated (if required) by the reacting node.
+
+ 2. A new Diameter request is generated by the application running on
+ the reacting node.
+
+ 3. The reacting node determines that an active overload report
+ applies to the request, as indicated by the corresponding OCS
+ entry.
+
+ 4. The reacting node determines if overload abatement treatment
+ should be applied to the request. One approach that could be
+ taken for each request is to select a uniformly selected random
+ number between 1 and 100. If the random number is less than or
+
+
+
+Korhonen, et al. Standards Track [Page 23]
+
+RFC 7683 DOIC October 2015
+
+
+ equal to the indicated reduction percentage, then the request is
+ given abatement treatment; otherwise, the request is given normal
+ routing treatment.
+
+6.2. Reporting Node Behavior
+
+ The method a reporting node uses to determine the amount of traffic
+ reduction required to address an overload condition is an
+ implementation decision.
+
+ When a reporting node that has selected the loss abatement algorithm
+ determines the need to request a reduction in traffic, it includes an
+ OC-OLR AVP in answer messages as described in Section 5.2.3.
+
+ When sending the OC-OLR AVP, the reporting node MUST indicate a
+ percentage reduction in the OC-Reduction-Percentage AVP.
+
+ The reporting node MAY change the reduction percentage in subsequent
+ overload reports. When doing so, the reporting node must conform to
+ overload report handling specified in Section 5.2.3.
+
+6.3. Reacting Node Behavior
+
+ The method a reacting node uses to determine which request messages
+ are given abatement treatment is an implementation decision.
+
+ When receiving an OC-OLR in an answer message where the algorithm
+ indicated in the OC-Supported-Features AVP is the loss algorithm, the
+ reacting node MUST apply abatement treatment to the requested
+ percentage of request messages sent.
+
+ Note: The loss algorithm is a stateless algorithm. As a result,
+ the reacting node does not guarantee that there will be an
+ absolute reduction in traffic sent. Rather, it guarantees that
+ the requested percentage of new requests will be given abatement
+ treatment.
+
+ If the reacting node comes out of the 100% traffic reduction
+ (meaning, it has received an OLR indicating that no traffic should be
+ sent, as a result of the overload report timing out), the reacting
+ node sending the traffic SHOULD be conservative and, for example,
+ first send "probe" messages to learn the overload condition of the
+ overloaded node before converging to any traffic amount/rate decided
+ by the sender. Similar concerns apply in all cases when the overload
+ report times out, unless the previous overload report stated 0%
+ reduction.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 24]
+
+RFC 7683 DOIC October 2015
+
+
+ Note: The goal of this behavior is to reduce the probability of
+ overload condition thrashing where an immediate transition from
+ 100% reduction to 0% reduction results in the reporting node
+ moving quickly back into an overload condition.
+
+7. Attribute Value Pairs
+
+ This section describes the encoding and semantics of the Diameter
+ Overload Indication Attribute Value Pairs (AVPs) defined in this
+ document.
+
+ Refer to Section 4 of [RFC6733] for more information on AVPs and AVP
+ data types.
+
+7.1. OC-Supported-Features AVP
+
+ The OC-Supported-Features AVP (AVP Code 621) is of type Grouped and
+ serves two purposes. First, it announces a node's support for the
+ DOIC solution in general. Second, it contains the description of the
+ supported DOIC features of the sending node. The OC-Supported-
+ Features AVP MUST be included in every Diameter request message a
+ DOIC supporting node sends.
+
+ OC-Supported-Features ::= < AVP Header: 621 >
+ [ OC-Feature-Vector ]
+ * [ AVP ]
+
+7.2. OC-Feature-Vector AVP
+
+ The OC-Feature-Vector AVP (AVP Code 622) is of type Unsigned64 and
+ contains a 64-bit flags field of announced capabilities of a DOIC
+ node. The value of zero (0) is reserved.
+
+ The OC-Feature-Vector sub-AVP is used to announce the DOIC features
+ supported by the DOIC node, in the form of a flag-bits field in which
+ each bit announces one feature or capability supported by the node.
+ The absence of the OC-Feature-Vector AVP in request messages
+ indicates that only the default traffic abatement algorithm described
+ in this specification is supported. The absence of the OC-Feature-
+ Vector AVP in answer messages indicates that the default traffic
+ abatement algorithm described in this specification is selected
+ (while other traffic abatement algorithms may be supported), and no
+ features other than abatement algorithms are supported.
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 25]
+
+RFC 7683 DOIC October 2015
+
+
+ The following capability is defined in this document:
+
+ OLR_DEFAULT_ALGO (0x0000000000000001)
+
+ When this flag is set by the a DOIC reacting node, it means that
+ the default traffic abatement (loss) algorithm is supported. When
+ this flag is set by a DOIC reporting node, it means that the loss
+ algorithm will be used for requested overload abatement.
+
+7.3. OC-OLR AVP
+
+ The OC-OLR AVP (AVP Code 623) is of type Grouped and contains the
+ information necessary to convey an overload report on an overload
+ condition at the reporting node. The application the OC-OLR AVP
+ applies to is identified by the Application-ID found in the Diameter
+ message header. The host or realm the OC-OLR AVP concerns is
+ determined from the Origin-Host AVP and/or Origin-Realm AVP found in
+ the encapsulating Diameter command. The OC-OLR AVP is intended to be
+ sent only by a reporting node.
+
+ OC-OLR ::= < AVP Header: 623 >
+ < OC-Sequence-Number >
+ < OC-Report-Type >
+ [ OC-Reduction-Percentage ]
+ [ OC-Validity-Duration ]
+ * [ AVP ]
+
+7.4. OC-Sequence-Number AVP
+
+ The OC-Sequence-Number AVP (AVP Code 624) is of type Unsigned64. Its
+ usage in the context of overload control is described in Section 5.2.
+
+ From the functionality point of view, the OC-Sequence-Number AVP is
+ used as a nonvolatile increasing counter for a sequence of overload
+ reports between two DOIC nodes for the same overload occurrence.
+ Sequence numbers are treated in a unidirectional manner, i.e., two
+ sequence numbers in each direction between two DOIC nodes are not
+ related or correlated.
+
+7.5. OC-Validity-Duration AVP
+
+ The OC-Validity-Duration AVP (AVP Code 625) is of type Unsigned32 and
+ indicates in seconds the validity time of the overload report. The
+ number of seconds is measured after reception of the first OC-OLR AVP
+ with a given value of OC-Sequence-Number AVP. The default value for
+ the OC-Validity-Duration AVP is 30 seconds. When the OC-Validity-
+ Duration AVP is not present in the OC-OLR AVP, the default value
+ applies. The maximum value for the OC-Validity-Duration AVP is
+
+
+
+Korhonen, et al. Standards Track [Page 26]
+
+RFC 7683 DOIC October 2015
+
+
+ 86,400 seconds (24 hours). If the value received in the OC-Validity-
+ Duration is greater than the maximum value, then the default value
+ applies.
+
+7.6. OC-Report-Type AVP
+
+ The OC-Report-Type AVP (AVP Code 626) is of type Enumerated. The
+ value of the AVP describes what the overload report concerns. The
+ following values are initially defined:
+
+ HOST_REPORT 0
+ The overload report is for a host. Overload abatement treatment
+ applies to host-routed requests.
+
+ REALM_REPORT 1
+ The overload report is for a realm. Overload abatement treatment
+ applies to realm-routed requests.
+
+ The values 2-4294967295 are unassigned.
+
+7.7. OC-Reduction-Percentage AVP
+
+ The OC-Reduction-Percentage AVP (AVP Code 627) is of type Unsigned32
+ and describes the percentage of the traffic that the sender is
+ requested to reduce, compared to what it otherwise would send. The
+ OC-Reduction-Percentage AVP applies to the default (loss) algorithm
+ specified in this specification. However, the AVP can be reused for
+ future abatement algorithms, if its semantics fit into the new
+ algorithm.
+
+ The value of the Reduction-Percentage AVP is between zero (0) and one
+ hundred (100). Values greater than 100 are ignored. The value of
+ 100 means that all traffic is to be throttled, i.e., the reporting
+ node is under a severe load and ceases to process any new messages.
+ The value of 0 means that the reporting node is in a stable state and
+ has no need for the reacting node to apply any traffic abatement.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 27]
+
+RFC 7683 DOIC October 2015
+
+
+7.8. AVP Flag Rules
+
+ +---------+
+ |AVP flag |
+ |rules |
+ +----+----+
+ AVP Section | |MUST|
+ Attribute Name Code Defined Value Type |MUST| NOT|
+ +--------------------------------------------------+----+----+
+ |OC-Supported-Features 621 7.1 Grouped | | V |
+ +--------------------------------------------------+----+----+
+ |OC-Feature-Vector 622 7.2 Unsigned64 | | V |
+ +--------------------------------------------------+----+----+
+ |OC-OLR 623 7.3 Grouped | | V |
+ +--------------------------------------------------+----+----+
+ |OC-Sequence-Number 624 7.4 Unsigned64 | | V |
+ +--------------------------------------------------+----+----+
+ |OC-Validity-Duration 625 7.5 Unsigned32 | | V |
+ +--------------------------------------------------+----+----+
+ |OC-Report-Type 626 7.6 Enumerated | | V |
+ +--------------------------------------------------+----+----+
+ |OC-Reduction | | |
+ | -Percentage 627 7.7 Unsigned32 | | V |
+ +--------------------------------------------------+----+----+
+
+ As described in the Diameter base protocol [RFC6733], the M-bit usage
+ for a given AVP in a given command may be defined by the application.
+
+8. Error Response Codes
+
+ When a DOIC node rejects a Diameter request due to overload, the DOIC
+ node MUST select an appropriate error response code. This
+ determination is made based on the probability of the request
+ succeeding if retried on a different path.
+
+ Note: This only applies for DOIC nodes that are not the originator
+ of the request.
+
+ A reporting node rejecting a Diameter request due to an overload
+ condition SHOULD send a DIAMETER_TOO_BUSY error response, if it can
+ assume that the same request may succeed on a different path.
+
+ If a reporting node knows or assumes that the same request will not
+ succeed on a different path, the DIAMETER_UNABLE_TO_COMPLY error
+ response SHOULD be used. Retrying would consume valuable resources
+ during an occurrence of overload.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 28]
+
+RFC 7683 DOIC October 2015
+
+
+ For instance, if the request arrived at the reporting node without
+ a Destination-Host AVP, then the reporting node might determine
+ that there is an alternative Diameter node that could successfully
+ process the request and that retrying the transaction would not
+ negatively impact the reporting node. DIAMETER_TOO_BUSY would be
+ sent in this case.
+
+ If the request arrived at the reporting node with a Destination-
+ Host AVP populated with its own Diameter identity, then the
+ reporting node can assume that retrying the request would result
+ in it coming to the same reporting node.
+ DIAMETER_UNABLE_TO_COMPLY would be sent in this case.
+
+ A second example is when an agent that supports the DOIC solution
+ is performing the role of a reacting node for a non-supporting
+ client. Requests that are rejected as a result of DOIC throttling
+ by the agent in this scenario would generally be rejected with a
+ DIAMETER_UNABLE_TO_COMPLY response code.
+
+9. IANA Considerations
+
+9.1. AVP Codes
+
+ New AVPs defined by this specification are listed in Section 7. All
+ AVP codes are allocated from the "AVP Codes" sub-registry under the
+ "Authentication, Authorization, and Accounting (AAA) Parameters"
+ registry.
+
+9.2. New Registries
+
+ Two new registries have been created in the "AVP Specific Values"
+ sub-registry under the "Authentication, Authorization, and Accounting
+ (AAA) Parameters" registry.
+
+ A new "OC-Feature-Vector AVP Values (code 622)" registry has been
+ created. This registry contains the following:
+
+ Feature Vector Value Name
+
+ Feature Vector Value
+
+ Specification defining the new value
+
+ See Section 7.2 for the initial Feature Vector Value in the registry.
+ This specification defines the value. New values can be added to the
+ registry using the Specification Required policy [RFC5226].
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 29]
+
+RFC 7683 DOIC October 2015
+
+
+ A new "OC-Report-Type AVP Values (code 626)" registry has been
+ created. This registry contains the following:
+
+ Report Type Value Name
+
+ Report Type Value
+
+ Specification defining the new value
+
+ See Section 7.6 for the initial assignment in the registry. New
+ types can be added using the Specification Required policy [RFC5226].
+
+10. Security Considerations
+
+ DOIC gives Diameter nodes the ability to request that downstream
+ nodes send fewer Diameter requests. Nodes do this by exchanging
+ overload reports that directly effect this reduction. This exchange
+ is potentially subject to multiple methods of attack and has the
+ potential to be used as a denial-of-service (DoS) attack vector. For
+ instance, a series of injected realm OLRs with a requested reduction
+ percentage of 100% could be used to completely eliminate any traffic
+ from being sent to that realm.
+
+ Overload reports may contain information about the topology and
+ current status of a Diameter network. This information is
+ potentially sensitive. Network operators may wish to control
+ disclosure of overload reports to unauthorized parties to avoid their
+ use for competitive intelligence or to target attacks.
+
+ Diameter does not include features to provide end-to-end
+ authentication, integrity protection, or confidentiality. This may
+ cause complications when sending overload reports between non-
+ adjacent nodes.
+
+10.1. Potential Threat Modes
+
+ The Diameter protocol involves transactions in the form of requests
+ and answers exchanged between clients and servers. These clients and
+ servers may be peers, that is, they may share a direct transport
+ (e.g., TCP or SCTP) connection, or the messages may traverse one or
+ more intermediaries, known as Diameter Agents. Diameter nodes use
+ TLS, DTLS, or IPsec to authenticate peers and to provide
+ confidentiality and integrity protection of traffic between peers.
+ Nodes can make authorization decisions based on the peer identities
+ authenticated at the transport layer.
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 30]
+
+RFC 7683 DOIC October 2015
+
+
+ When agents are involved, this presents an effectively transitive
+ trust model. That is, a Diameter client or server can authorize an
+ agent for certain actions, but it must trust that agent to make
+ appropriate authorization decisions about its peers, and so on.
+ Since confidentiality and integrity protection occur at the transport
+ layer, agents can read, and perhaps modify, any part of a Diameter
+ message, including an overload report.
+
+ There are several ways an attacker might attempt to exploit the
+ overload control mechanism. An unauthorized third party might inject
+ an overload report into the network. If this third party is upstream
+ of an agent, and that agent fails to apply proper authorization
+ policies, downstream nodes may mistakenly trust the report. This
+ attack is at least partially mitigated by the assumption that nodes
+ include overload reports in Diameter answers but not in requests.
+ This requires an attacker to have knowledge of the original request
+ in order to construct an answer. Such an answer would also need to
+ arrive at a Diameter node via a protected transport connection.
+ Therefore, implementations MUST validate that an answer containing an
+ overload report is a properly constructed response to a pending
+ request prior to acting on the overload report, and that the answer
+ was received via an appropriate transport connection.
+
+ A similar attack involves a compromised but otherwise authorized node
+ that sends an inappropriate overload report. For example, a server
+ for the realm "example.com" might send an overload report indicating
+ that a competitor's realm "example.net" is overloaded. If other
+ nodes act on the report, they may falsely believe that "example.net"
+ is overloaded, effectively reducing that realm's capacity.
+ Therefore, it's critical that nodes validate that an overload report
+ received from a peer actually falls within that peer's responsibility
+ before acting on the report or forwarding the report to other peers.
+ For example, an overload report from a peer that applies to a realm
+ not handled by that peer is suspect. This may require out-of-band,
+ non-Diameter agreements and/or mechanisms.
+
+ This attack is partially mitigated by the fact that the
+ application, as well as host and realm, for a given OLR is
+ determined implicitly by respective AVPs in the enclosing answer.
+ If a reporting node modifies any of those AVPs, the enclosing
+ transaction will also be affected.
+
+10.2. Denial-of-Service Attacks
+
+ Diameter overload reports, especially realm reports, can cause a node
+ to cease sending some or all Diameter requests for an extended
+ period. This makes them a tempting vector for DoS attacks.
+ Furthermore, since Diameter is almost always used in support of other
+
+
+
+Korhonen, et al. Standards Track [Page 31]
+
+RFC 7683 DOIC October 2015
+
+
+ protocols, a DoS attack on Diameter is likely to impact those
+ protocols as well. In the worst case, where the Diameter application
+ is being used for access control into an IP network, a coordinated
+ DoS attack could result in the blockage of all traffic into that
+ network. Therefore, Diameter nodes MUST NOT honor or forward OLRs
+ received from peers that are not trusted to send them.
+
+ An attacker might use the information in an OLR to assist in DoS
+ attacks. For example, an attacker could use information about
+ current overload conditions to time an attack for maximum effect, or
+ use subsequent overload reports as a feedback mechanism to learn the
+ results of a previous or ongoing attack. Operators need the ability
+ to ensure that OLRs are not leaked to untrusted parties.
+
+10.3. Noncompliant Nodes
+
+ In the absence of an overload control mechanism, Diameter nodes need
+ to implement strategies to protect themselves from floods of
+ requests, and to make sure that a disproportionate load from one
+ source does not prevent other sources from receiving service. For
+ example, a Diameter server might throttle a certain percentage of
+ requests from sources that exceed certain limits. Overload control
+ can be thought of as an optimization for such strategies, where
+ downstream nodes never send the excess requests in the first place.
+ However, the presence of an overload control mechanism does not
+ remove the need for these other protection strategies.
+
+ When a Diameter node sends an overload report, it cannot assume that
+ all nodes will comply, even if they indicate support for DOIC. A
+ noncompliant node might continue to send requests with no reduction
+ in load. Such noncompliance could be done accidentally or
+ maliciously to gain an unfair advantage over compliant nodes.
+ Requirement 28 in [RFC7068] indicates that the overload control
+ solution cannot assume that all Diameter nodes in a network are
+ trusted. It also requires that malicious nodes not be allowed to
+ take advantage of the overload control mechanism to get more than
+ their fair share of service.
+
+10.4. End-to-End Security Issues
+
+ The lack of end-to-end integrity features makes it difficult to
+ establish trust in overload reports received from non-adjacent nodes.
+ Any agents in the message path may insert or modify overload reports.
+ Nodes must trust that their adjacent peers perform proper checks on
+ overload reports from their peers, and so on, creating a transitive-
+ trust requirement extending for potentially long chains of nodes.
+ Network operators must determine if this transitive trust requirement
+ is acceptable for their deployments. Nodes supporting Diameter
+
+
+
+Korhonen, et al. Standards Track [Page 32]
+
+RFC 7683 DOIC October 2015
+
+
+ overload control MUST give operators the ability to select which
+ peers are trusted to deliver overload reports and whether they are
+ trusted to forward overload reports from non-adjacent nodes. DOIC
+ nodes MUST strip DOIC AVPs from messages received from peers that are
+ not trusted for DOIC purposes.
+
+ The lack of end-to-end confidentiality protection means that any
+ Diameter Agent in the path of an overload report can view the
+ contents of that report. In addition to the requirement to select
+ which peers are trusted to send overload reports, operators MUST be
+ able to select which peers are authorized to receive reports. A node
+ MUST NOT send an overload report to a peer not authorized to receive
+ it. Furthermore, an agent MUST remove any overload reports that
+ might have been inserted by other nodes before forwarding a Diameter
+ message to a peer that is not authorized to receive overload reports.
+
+ A DOIC node cannot always automatically detect that a peer also
+ supports DOIC. For example, a node might have a peer that is a
+ non-supporting agent. If nodes on the other side of that agent
+ send OC-Supported-Features AVPs, the agent is likely to forward
+ them as unknown AVPs. Messages received across the non-supporting
+ agent may be indistinguishable from messages received across a
+ DOIC supporting agent, giving the false impression that the non-
+ supporting agent actually supports DOIC. This complicates the
+ transitive-trust nature of DOIC. Operators need to be careful to
+ avoid situations where a non-supporting agent is mistakenly
+ trusted to enforce DOIC-related authorization policies.
+
+ It is expected that work on end-to-end Diameter security might make
+ it easier to establish trust in non-adjacent nodes for overload
+ control purposes. Readers should be reminded, however, that the
+ overload control mechanism allows Diameter Agents to modify AVPs in,
+ or insert additional AVPs into, existing messages that are originated
+ by other nodes. If end-to-end security is enabled, there is a risk
+ that such modification could violate integrity protection. The
+ details of using any future Diameter end-to-end security mechanism
+ with overload control will require careful consideration, and are
+ beyond the scope of this document.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 33]
+
+RFC 7683 DOIC October 2015
+
+
+11. References
+
+11.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119,
+ DOI 10.17487/RFC2119, March 1997,
+ <http://www.rfc-editor.org/info/rfc2119>.
+
+ [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an
+ IANA Considerations Section in RFCs", BCP 26, RFC 5226,
+ DOI 10.17487/RFC5226, May 2008,
+ <http://www.rfc-editor.org/info/rfc5226>.
+
+ [RFC6733] Fajardo, V., Ed., Arkko, J., Loughney, J., and G. Zorn,
+ Ed., "Diameter Base Protocol", RFC 6733,
+ DOI 10.17487/RFC6733, October 2012,
+ <http://www.rfc-editor.org/info/rfc6733>.
+
+11.2. Informative References
+
+ [Cx] 3GPP, "Cx and Dx interfaces based on the Diameter
+ protocol; Protocol details", 3GPP TS 29.229 12.7.0,
+ September 2015.
+
+ [PCC] 3GPP, "Policy and charging control architecture", 3GPP
+ TS 23.203 12.10.0, September 2015.
+
+ [RFC4006] Hakala, H., Mattila, L., Koskinen, J-P., Stura, M., and J.
+ Loughney, "Diameter Credit-Control Application", RFC 4006,
+ DOI 10.17487/RFC4006, August 2005,
+ <http://www.rfc-editor.org/info/rfc4006>.
+
+ [RFC7068] McMurry, E. and B. Campbell, "Diameter Overload Control
+ Requirements", RFC 7068, DOI 10.17487/RFC7068, November
+ 2013, <http://www.rfc-editor.org/info/rfc7068>.
+
+ [S13] 3GPP, "Evolved Packet System (EPS); Mobility Management
+ Entity (MME) and Serving GPRS Support Node (SGSN) related
+ interfaces based on Diameter protocol", 3GPP TS 29.272
+ 12.8.0, September 2015.
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 34]
+
+RFC 7683 DOIC October 2015
+
+
+Appendix A. Issues Left for Future Specifications
+
+ The base solution for overload control does not cover all possible
+ use cases. A number of solution aspects were intentionally left for
+ future specification and protocol work. The following subsections
+ define some of the potential extensions to the DOIC solution.
+
+A.1. Additional Traffic Abatement Algorithms
+
+ This specification describes only means for a simple loss-based
+ algorithm. Future algorithms can be added using the designed
+ solution extension mechanism. The new algorithms need to be
+ registered with IANA. See Sections 7.2 and 9 for the required IANA
+ steps.
+
+A.2. Agent Overload
+
+ This specification focuses on Diameter endpoint (server or client)
+ overload. A separate extension will be required to outline the
+ handling of the case of agent overload.
+
+A.3. New Error Diagnostic AVP
+
+ This specification indicates the use of existing error messages when
+ nodes reject requests due to overload. There is an expectation that
+ additional error codes or AVPs will be defined in a separate
+ specification to indicate that overload was the reason for the
+ rejection of the message.
+
+Appendix B. Deployment Considerations
+
+ Non-supporting Agents
+
+ Due to the way that realm-routed requests are handled in Diameter
+ networks with the server selection for the request done by an
+ agent, network operators should enable DOIC at agents that perform
+ server selection first.
+
+ Topology-Hiding Interactions
+
+ There exist proxies that implement what is referred to as Topology
+ Hiding. This can include cases where the agent modifies the
+ Origin-Host in answer messages. The behavior of the DOIC solution
+ is not well understood when this happens. As such, the DOIC
+ solution does not address this scenario.
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 35]
+
+RFC 7683 DOIC October 2015
+
+
+ Inter-Realm/Administrative Domain Considerations
+
+ There are likely to be special considerations for handling DOIC
+ signaling across administrative boundaries. This includes
+ considerations for whether or not information included in the DOIC
+ signaling should be sent across those boundaries. In addition,
+ consideration should be taken as to whether or not a reacting node
+ in one realm can be trusted to implement the requested overload
+ abatement handling for overload reports received from a separately
+ administered realm.
+
+Appendix C. Considerations for Applications Integrating the DOIC
+ Solution
+
+ This section outlines considerations to be taken into account when
+ integrating the DOIC solution into Diameter applications.
+
+C.1. Application Classification
+
+ The following is a classification of Diameter applications and
+ request types. This discussion is meant to document factors that
+ play into decisions made by the Diameter entity responsible for
+ handling overload reports.
+
+ Section 8.1 of [RFC6733] defines two state machines that imply two
+ types of applications, session-less and session-based applications.
+ The primary difference between these types of applications is the
+ lifetime of Session-Ids.
+
+ For session-based applications, the Session-Id is used to tie
+ multiple requests into a single session.
+
+ The Credit-Control application defined in [RFC4006] is an example of
+ a Diameter session-based application.
+
+ In session-less applications, the lifetime of the Session-Id is a
+ single Diameter transaction, i.e., the session is implicitly
+ terminated after a single Diameter transaction and a new Session-Id
+ is generated for each Diameter request.
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 36]
+
+RFC 7683 DOIC October 2015
+
+
+ For the purposes of this discussion, session-less applications are
+ further divided into two types of applications:
+
+ Stateless Applications:
+
+ Requests within a stateless application have no relationship to
+ each other. The 3GPP-defined S13 application is an example of a
+ stateless application [S13], where only a Diameter command is
+ defined between a client and a server and no state is maintained
+ between two consecutive transactions.
+
+ Pseudo-Session Applications:
+
+ Applications that do not rely on the Session-Id AVP for
+ correlation of application messages related to the same session
+ but use other session-related information in the Diameter requests
+ for this purpose. The 3GPP-defined Cx application [Cx] is an
+ example of a pseudo-session application.
+
+ The handling of overload reports must take the type of application
+ into consideration, as discussed in Appendix C.2.
+
+C.2. Implications of Application Type Overload
+
+ This section discusses considerations for mitigating overload
+ reported by a Diameter entity. This discussion focuses on the type
+ of application. Appendix C.3 discusses considerations for handling
+ various request types when the target server is known to be in an
+ overloaded state.
+
+ These discussions assume that the strategy for mitigating the
+ reported overload is to reduce the overall workload sent to the
+ overloaded entity. The concept of applying overload treatment to
+ requests targeted for an overloaded Diameter entity is inherent to
+ this discussion. The method used to reduce offered load is not
+ specified here, but it could include routing requests to another
+ Diameter entity known to be able to handle them, or it could mean
+ rejecting certain requests. For a Diameter Agent, rejecting requests
+ will usually mean generating appropriate Diameter error responses.
+ For a Diameter client, rejecting requests will depend upon the
+ application. For example, it could mean giving an indication to the
+ entity requesting the Diameter service that the network is busy and
+ to try again later.
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 37]
+
+RFC 7683 DOIC October 2015
+
+
+ Stateless Applications:
+
+ By definition, there is no relationship between individual
+ requests in a stateless application. As a result, when a request
+ is sent or relayed to an overloaded Diameter entity -- either a
+ Diameter Server or a Diameter Agent -- the sending or relaying
+ entity can choose to apply the overload treatment to any request
+ targeted for the overloaded entity.
+
+ Pseudo-session Applications:
+
+ For pseudo-session applications, there is an implied ordering of
+ requests. As a result, decisions about which requests towards an
+ overloaded entity to reject could take the command code of the
+ request into consideration. This generally means that
+ transactions later in the sequence of transactions should be given
+ more favorable treatment than messages earlier in the sequence.
+ This is because more work has already been done by the Diameter
+ network for those transactions that occur later in the sequence.
+ Rejecting them could result in increasing the load on the network
+ as the transactions earlier in the sequence might also need to be
+ repeated.
+
+ Session-Based Applications:
+
+ Overload handling for session-based applications must take into
+ consideration the work load associated with setting up and
+ maintaining a session. As such, the entity sending requests
+ towards an overloaded Diameter entity for a session-based
+ application might tend to reject new session requests prior to
+ rejecting intra-session requests. In addition, session-ending
+ requests might be given a lower probability of being rejected, as
+ rejecting session-ending requests could result in session status
+ being out of sync between the Diameter clients and servers.
+ Application designers that would decide to reject mid-session
+ requests will need to consider whether the rejection invalidates
+ the session and any resulting session cleanup procedures.
+
+C.3. Request Transaction Classification
+
+ Independent Request:
+
+ An independent request is not correlated to any other requests,
+ and, as such, the lifetime of the Session-Id is constrained to an
+ individual transaction.
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 38]
+
+RFC 7683 DOIC October 2015
+
+
+ Session-Initiating Request:
+
+ A session-initiating request is the initial message that
+ establishes a Diameter session. The ACR message defined in
+ [RFC6733] is an example of a session-initiating request.
+
+ Correlated Session-Initiating Request:
+
+ There are cases when multiple session-initiated requests must be
+ correlated and managed by the same Diameter server. It is notably
+ the case in the 3GPP Policy and Charging Control (PCC)
+ architecture [PCC], where multiple apparently independent Diameter
+ application sessions are actually correlated and must be handled
+ by the same Diameter server.
+
+ Intra-session Request:
+
+ An intra-session request is a request that uses the same Session-
+ Id as the one used in a previous request. An intra-session
+ request generally needs to be delivered to the server that handled
+ the session-creating request for the session. The STR message
+ defined in [RFC6733] is an example of an intra-session request.
+
+ Pseudo-session Requests:
+
+ Pseudo-session requests are independent requests and do not use
+ the same Session-Id but are correlated by other session-related
+ information contained in the request. There exist Diameter
+ applications that define an expected ordering of transactions.
+ This sequencing of independent transactions results in a pseudo-
+ session. The AIR, MAR, and SAR requests in the 3GPP-defined Cx
+ [Cx] application are examples of pseudo-session requests.
+
+C.4. Request Type Overload Implications
+
+ The request classes identified in Appendix C.3 have implications on
+ decisions about which requests should be throttled first. The
+ following list of request treatments regarding throttling is provided
+ as guidelines for application designers when implementing the
+ Diameter overload control mechanism described in this document. The
+ exact behavior regarding throttling is a matter of local policy,
+ unless specifically defined for the application.
+
+ Independent Requests:
+
+ Independent requests can generally be given equal treatment when
+ making throttling decisions, unless otherwise indicated by
+ application requirements or local policy.
+
+
+
+Korhonen, et al. Standards Track [Page 39]
+
+RFC 7683 DOIC October 2015
+
+
+ Session-Initiating Requests:
+
+ Session-initiating requests often represent more work than
+ independent or intra-session requests. Moreover, session-
+ initiating requests are typically followed by other session-
+ related requests. Since the main objective of overload control is
+ to reduce the total number of requests sent to the overloaded
+ entity, throttling decisions might favor allowing intra-session
+ requests over session-initiating requests. In the absence of
+ local policies or application-specific requirements to the
+ contrary, individual session-initiating requests can be given
+ equal treatment when making throttling decisions.
+
+ Correlated Session-Initiating Requests:
+
+ A request that results in a new binding; where the binding is used
+ for routing of subsequent session-initiating requests to the same
+ server, it represents more work load than other requests. As
+ such, these requests might be throttled more frequently than other
+ request types.
+
+ Pseudo-session Requests:
+
+ Throttling decisions for pseudo-session requests can take into
+ consideration where individual requests fit into the overall
+ sequence of requests within the pseudo-session. Requests that are
+ earlier in the sequence might be throttled more aggressively than
+ requests that occur later in the sequence.
+
+ Intra-session Requests:
+
+ There are two types of intra-sessions requests, requests that
+ terminate a session and the remainder of intra-session requests.
+ Implementers and operators may choose to throttle session-
+ terminating requests less aggressively in order to gracefully
+ terminate sessions, allow cleanup of the related resources (e.g.,
+ session state), and avoid the need for additional intra-session
+ requests. Favoring session termination requests may reduce the
+ session management impact on the overloaded entity. The default
+ handling of other intra-session requests might be to treat them
+ equally when making throttling decisions. There might also be
+ application-level considerations whether some request types are
+ favored over others.
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 40]
+
+RFC 7683 DOIC October 2015
+
+
+Contributors
+
+ The following people contributed substantial ideas, feedback, and
+ discussion to this document:
+
+ o Eric McMurry
+
+ o Hannes Tschofenig
+
+ o Ulrich Wiehe
+
+ o Jean-Jacques Trottin
+
+ o Maria Cruz Bartolome
+
+ o Martin Dolly
+
+ o Nirav Salot
+
+ o Susan Shishufeng
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 41]
+
+RFC 7683 DOIC October 2015
+
+
+Authors' Addresses
+
+ Jouni Korhonen (editor)
+ Broadcom Corporation
+ 3151 Zanker Road
+ San Jose, CA 95134
+ United States
+
+
+
+ Steve Donovan (editor)
+ Oracle
+ 7460 Warren Parkway
+ Frisco, Texas 75034
+ United States
+
+
+
+ Ben Campbell
+ Oracle
+ 7460 Warren Parkway
+ Frisco, Texas 75034
+ United States
+
+
+
+ Lionel Morand
+ Orange Labs
+ 38/40 rue du General Leclerc
+ Issy-Les-Moulineaux Cedex 9 92794
+ France
+
+ Phone: +33145296257
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 42]
+
diff --git a/lib/diameter/examples/code/client.erl b/lib/diameter/examples/code/client.erl
index 6fb90b1c09..0864919cdd 100644
--- a/lib/diameter/examples/code/client.erl
+++ b/lib/diameter/examples/code/client.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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.
@@ -39,7 +39,6 @@
-module(client).
-include_lib("diameter/include/diameter.hrl").
--include_lib("diameter/include/diameter_gen_base_rfc6733.hrl").
-export([start/1, %% start a service
start/2, %%
@@ -71,6 +70,7 @@
{'Product-Name', "Client"},
{'Auth-Application-Id', [0]},
{string_decode, false},
+ {decode_format, map},
{application, [{alias, common},
{dictionary, diameter_gen_base_rfc6733},
{module, client_cb}]}]).
@@ -108,9 +108,9 @@ connect(T) ->
call(Name) ->
SId = diameter:session_id(?L(Name)),
- RAR = #diameter_base_RAR{'Session-Id' = SId,
- 'Auth-Application-Id' = 0,
- 'Re-Auth-Request-Type' = 0},
+ RAR = ['RAR' | #{'Session-Id' => SId,
+ 'Auth-Application-Id' => 0,
+ 'Re-Auth-Request-Type' => 0}],
diameter:call(Name, common, RAR, []).
call() ->
diff --git a/lib/diameter/examples/code/client_cb.erl b/lib/diameter/examples/code/client_cb.erl
index ed1d3b9b7b..af2d4d6da7 100644
--- a/lib/diameter/examples/code/client_cb.erl
+++ b/lib/diameter/examples/code/client_cb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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.
@@ -55,21 +55,18 @@ prepare_request(#diameter_packet{msg = ['RAR' = T | Avps]}, _, {_, Caps}) ->
origin_realm = {OR, DR}}
= Caps,
- {send, [T, {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Destination-Host', DH},
- {'Destination-Realm', DR}
- | Avps]};
-
-prepare_request(#diameter_packet{msg = Rec}, _, {_, Caps}) ->
- #diameter_caps{origin_host = {OH, DH},
- origin_realm = {OR, DR}}
- = Caps,
-
- {send, Rec#diameter_base_RAR{'Origin-Host' = OH,
- 'Origin-Realm' = OR,
- 'Destination-Host' = DH,
- 'Destination-Realm' = DR}}.
+ {send, [T | if is_map(Avps) ->
+ Avps#{'Origin-Host' => OH,
+ 'Origin-Realm' => OR,
+ 'Destination-Host' => DH,
+ 'Destination-Realm' => DR};
+ is_list(Avps) ->
+ [{'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Destination-Host', DH},
+ {'Destination-Realm', DR}
+ | Avps]
+ end]}.
%% prepare_retransmit/3
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 6bf748a727..3af856f63e 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2016. All Rights Reserved.
+# Copyright Ericsson AB 2010-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.
@@ -274,9 +274,7 @@ gen/diameter_gen_base_accounting.erl gen/diameter_gen_base_accounting.hrl: \
gen/diameter_gen_acct_rfc6733.erl gen/diameter_gen_acct_rfc6733.hrl: \
$(EBIN)/diameter_gen_base_rfc6733.$(EMULATOR)
-gen/diameter_gen_relay.erl gen/diameter_gen_relay.hrl \
-gen/diameter_gen_base_rfc3588.erl gen/diameter_gen_base_rfc3588.hrl \
-gen/diameter_gen_base_rfc6733.erl gen/diameter_gen_base_rfc6733.hrl: \
+$(DICT_ERLS) $(DICT_HRLS): \
$(COMPILER_MODULES:%=$(EBIN)/%.$(EMULATOR))
$(DICT_MODULES:gen/%=$(EBIN)/%.$(EMULATOR)): \
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl
index 2e18a1d903..b90b794611 100644
--- a/lib/diameter/src/base/diameter.erl
+++ b/lib/diameter/src/base/diameter.erl
@@ -46,7 +46,8 @@
-export([start/0,
stop/0]).
--export_type([evaluable/0,
+-export_type([eval/0,
+ evaluable/0, %% deprecated
decode_format/0,
strict_arities/0,
restriction/0,
@@ -301,7 +302,7 @@ call(SvcName, App, Message) ->
| realm
| {host, any|'DiameterIdentity'()}
| {realm, any|'DiameterIdentity'()}
- | {eval, evaluable()}
+ | {eval, eval()}
| {neg, peer_filter()}
| {all, [peer_filter()]}
| {any, [peer_filter()]}.
@@ -309,10 +310,13 @@ call(SvcName, App, Message) ->
-opaque peer_ref()
:: pid().
--type evaluable()
+-type eval()
:: {module(), atom(), list()}
| fun()
- | maybe_improper_list(evaluable(), list()).
+ | maybe_improper_list(eval(), list()).
+
+-type evaluable()
+ :: eval().
-type sequence()
:: {'Unsigned32'(), 0..32}.
@@ -322,12 +326,12 @@ call(SvcName, App, Message) ->
| node
| nodes
| [node()]
- | evaluable().
+ | eval().
-type remotes()
:: boolean()
| [node()]
- | evaluable().
+ | eval().
-type message_length()
:: 0..16#FFFFFF.
@@ -336,7 +340,7 @@ call(SvcName, App, Message) ->
:: record
| list
| map
- | false
+ | none
| record_from_map.
-type strict_arities()
@@ -344,22 +348,39 @@ call(SvcName, App, Message) ->
| encode
| decode.
+%% Options common to both start_service/2 and add_transport/2.
+
+-type common_opt()
+ :: {pool_size, pos_integer()}
+ | {capabilities_cb, eval()}
+ | {capx_timeout, 'Unsigned32'()}
+ | {strict_capx, boolean()}
+ | {strict_mbit, boolean()}
+ | {avp_dictionaries, [module()]}
+ | {disconnect_cb, eval()}
+ | {dpr_timeout, 'Unsigned32'()}
+ | {dpa_timeout, 'Unsigned32'()}
+ | {incoming_maxlen, message_length()}
+ | {length_errors, exit | handle | discard}
+ | {connect_timer, 'Unsigned32'()}
+ | {watchdog_timer, 'Unsigned32'() | {module(), atom(), list()}}
+ | {watchdog_config, [{okay|suspect, non_neg_integer()}]}
+ | {spawn_opt, list()}.
+
%% Options passed to start_service/2
-type service_opt()
:: capability()
| {application, [application_opt()]}
| {restrict_connections, restriction()}
- | {sequence, sequence() | evaluable()}
+ | {sequence, sequence() | eval()}
| {share_peers, remotes()}
| {decode_format, decode_format()}
| {traffic_counters, boolean()}
| {string_decode, boolean()}
| {strict_arities, true | strict_arities()}
- | {strict_mbit, boolean()}
- | {incoming_maxlen, message_length()}
| {use_shared_peers, remotes()}
- | {spawn_opt, list()}.
+ | common_opt().
-type application_opt()
:: {alias, app_alias()}
@@ -389,20 +410,9 @@ call(SvcName, App, Message) ->
:: {transport_module, atom()}
| {transport_config, any()}
| {transport_config, any(), 'Unsigned32'() | infinity}
- | {pool_size, pos_integer()}
| {applications, [app_alias()]}
| {capabilities, [capability()]}
- | {capabilities_cb, evaluable()}
- | {capx_timeout, 'Unsigned32'()}
- | {capx_strictness, boolean()}
- | {disconnect_cb, evaluable()}
- | {dpr_timeout, 'Unsigned32'()}
- | {dpa_timeout, 'Unsigned32'()}
- | {length_errors, exit | handle | discard}
- | {connect_timer, 'Unsigned32'()}
- | {watchdog_timer, 'Unsigned32'() | {module(), atom(), list()}}
- | {watchdog_config, [{okay|suspect, non_neg_integer()}]}
- | {spawn_opt, list()}
+ | common_opt()
| {private, any()}.
%% Predicate passed to remove_transport/2
diff --git a/lib/diameter/src/base/diameter_callback.erl b/lib/diameter/src/base/diameter_callback.erl
index f9cdc66c70..d04a416bef 100644
--- a/lib/diameter/src/base/diameter_callback.erl
+++ b/lib/diameter/src/base/diameter_callback.erl
@@ -26,16 +26,16 @@
%% as the Diameter application callback in question. The record has
%% one field for each callback function as well as 'default' and
%% 'extra' fields. A function-specific field can be set to a
-%% diameter:evaluable() in order to redirect the callback
+%% diameter:eval() in order to redirect the callback
%% corresponding to that field, or to 'false' to request the default
%% callback implemented in this module. If neither of these fields are
%% set then the 'default' field determines the form of the callback: a
%% module name results in the usual callback as if the module had been
-%% configured directly as the callback module, a diameter_evaluable()
+%% configured directly as the callback module, a diameter_eval()
%% in a callback applied to the atom-valued callback name and argument
%% list. For all callbacks not to this module, the 'extra' field is a
%% list of additional arguments, following arguments supplied by
-%% diameter but preceding those of the diameter:evaluable() being
+%% diameter but preceding those of the diameter:eval() being
%% applied.
%%
%% For example, the following config to diameter:start_service/2, in
diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl
index 63e39b12d1..2dd2c906a2 100644
--- a/lib/diameter/src/base/diameter_codec.erl
+++ b/lib/diameter/src/base/diameter_codec.erl
@@ -324,7 +324,7 @@ decode_avps(MsgName, Mod, AppMod, Opts, #diameter_packet{bin = Bin} = Pkt) ->
{_, Avps} = split_binary(Bin, 20),
{Rec, As, Errors} = Mod:decode_avps(MsgName,
Avps,
- Opts#{dictionary => AppMod,
+ Opts#{app_dictionary => AppMod,
failed_avp => false}),
?LOGC([] /= Errors, decode_errors, Pkt#diameter_packet.header),
Pkt#diameter_packet{msg = reformat(MsgName, Rec, Opts),
@@ -614,8 +614,8 @@ pack_avp(#diameter_avp{data = {T, {Type, Value}}}, Opts) ->
pack_avp(#diameter_avp{data = {T, Data}}, _) ->
pack_data(T, Data);
-pack_avp(#diameter_avp{data = {Dict, Name, Data}}, Opts) ->
- pack_data(Dict:avp_header(Name), Dict:avp(encode, Data, Name, Opts));
+pack_avp(#diameter_avp{data = {Dict, Name, Value}}, Opts) ->
+ pack_data(Dict:avp_header(Name), Dict:avp(encode, Value, Name, Opts));
%% ... with a truncated header ...
pack_avp(#diameter_avp{code = undefined, data = B}, _)
diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl
index f1b6e56782..90a9282349 100644
--- a/lib/diameter/src/base/diameter_config.erl
+++ b/lib/diameter/src/base/diameter_config.erl
@@ -102,9 +102,6 @@
-record(monitor, {mref = make_ref() :: reference(),
service}). %% name
-%% The default sequence mask.
--define(NOMASK, {0,32}).
-
%% Time to lay low before restarting a dead service.
-define(RESTART_SLEEP, 2000).
@@ -560,87 +557,186 @@ add(SvcName, Type, Opts0) ->
end.
transport_opts(Opts) ->
- lists:map(fun topt/1, Opts).
+ [setopt(transport, T) || T <- Opts].
+
+%% setopt/2
-topt(T) ->
- case opt(T) of
+setopt(K, T) ->
+ case opt(K, T) of
{value, X} ->
X;
true ->
T;
false ->
- ?THROW({invalid, T})
+ ?THROW({invalid, T});
+ {error, Reason} ->
+ ?THROW({invalid, T, Reason})
end.
-opt({transport_module, M}) ->
+%% opt/2
+
+opt(_, {incoming_maxlen, N}) ->
+ is_integer(N) andalso 0 =< N andalso N < 1 bsl 24;
+
+opt(service, {K, B})
+ when K == string_decode;
+ K == traffic_counters ->
+ is_boolean(B);
+
+opt(service, {K, false})
+ when K == share_peers;
+ K == use_shared_peers;
+ K == monitor;
+ K == restrict_connections;
+ K == strict_arities ->
+ true;
+
+opt(service, {K, true})
+ when K == share_peers;
+ K == use_shared_peers;
+ K == strict_arities ->
+ true;
+
+opt(service, {decode_format, T})
+ when T == record;
+ T == list;
+ T == map;
+ T == none;
+ T == record_from_map ->
+ true;
+
+opt(service, {strict_arities, T})
+ when T == encode;
+ T == decode ->
+ true;
+
+opt(service, {restrict_connections, T})
+ when T == node;
+ T == nodes ->
+ true;
+
+opt(service, {K, T})
+ when (K == share_peers
+ orelse K == use_shared_peers
+ orelse K == restrict_connections), ([] == T
+ orelse is_atom(hd(T))) ->
+ true;
+
+opt(service, {monitor, P}) ->
+ is_pid(P);
+
+opt(service, {K, F})
+ when K == restrict_connections;
+ K == share_peers;
+ K == use_shared_peers ->
+ try diameter_lib:eval(F) of %% but no guarantee that it won't fail later
+ Nodes ->
+ is_list(Nodes) orelse {error, Nodes}
+ catch
+ E:R ->
+ {error, {E, R, ?STACK}}
+ end;
+
+opt(service, {sequence, {H,N}}) ->
+ 0 =< N andalso N =< 32
+ andalso is_integer(H)
+ andalso 0 =< H
+ andalso 0 == H bsr (32-N);
+
+opt(service = S, {sequence = K, F}) ->
+ try diameter_lib:eval(F) of
+ {_,_} = T ->
+ KT = {K,T},
+ opt(S, KT) andalso {value, KT};
+ V ->
+ {error, V}
+ catch
+ E:R ->
+ {error, {E, R, ?STACK}}
+ end;
+
+opt(transport, {transport_module, M}) ->
is_atom(M);
-opt({transport_config, _, Tmo}) ->
+opt(transport, {transport_config, _, Tmo}) ->
?IS_UINT32(Tmo) orelse Tmo == infinity;
-opt({applications, As}) ->
+opt(transport, {applications, As}) ->
is_list(As);
-opt({capabilities, Os}) ->
- is_list(Os) andalso ok == encode_CER(Os);
+opt(transport, {capabilities, Os}) ->
+ is_list(Os) andalso try ok = encode_CER(Os), true
+ catch ?FAILURE(No) -> {error, No}
+ end;
-opt({K, Tmo})
+opt(_, {K, Tmo})
when K == capx_timeout;
K == dpr_timeout;
K == dpa_timeout ->
?IS_UINT32(Tmo);
-opt({capx_strictness, B}) ->
+opt(_, {capx_strictness, B}) ->
+ is_boolean(B) andalso {value, {strict_capx, B}};
+opt(_, {K, B})
+ when K == strict_capx;
+ K == strict_mbit ->
is_boolean(B);
-opt({length_errors, T}) ->
+opt(_, {avp_dictionaries, Mods}) ->
+ is_list(Mods) andalso lists:all(fun erlang:is_atom/1, Mods);
+
+opt(_, {length_errors, T}) ->
lists:member(T, [exit, handle, discard]);
-opt({K, Tmo})
- when K == reconnect_timer; %% deprecated
- K == connect_timer ->
+opt(transport, {reconnect_timer, Tmo}) -> %% deprecated
+ ?IS_UINT32(Tmo) andalso {value, {connect_timer, Tmo}};
+opt(_, {connect_timer, Tmo}) ->
?IS_UINT32(Tmo);
-opt({watchdog_timer, {M,F,A}})
+opt(_, {watchdog_timer, {M,F,A}})
when is_atom(M), is_atom(F), is_list(A) ->
true;
-opt({watchdog_timer, Tmo}) ->
+opt(_, {watchdog_timer, Tmo}) ->
?IS_UINT32(Tmo);
-opt({watchdog_config, L}) ->
- is_list(L) andalso lists:all(fun wdopt/1, L);
+opt(_, {watchdog_config, L}) ->
+ is_list(L) andalso lists:all(fun wd/1, L);
-opt({spawn_opt, {M,F,A}})
+opt(_, {spawn_opt, {M,F,A}})
when is_atom(M), is_atom(F), is_list(A) ->
true;
-opt({spawn_opt = K, Opts}) ->
+opt(_, {spawn_opt = K, Opts}) ->
if is_list(Opts) ->
{value, {K, spawn_opts(Opts)}};
true ->
false
end;
-opt({pool_size, N}) ->
+opt(_, {pool_size, N}) ->
is_integer(N) andalso 0 < N;
-%% Options that we can't validate.
-opt({K, _})
+%% Options we can't validate.
+opt(_, {K, _})
+ when K == disconnect_cb;
+ K == capabilities_cb ->
+ true;
+opt(transport, {K, _})
when K == transport_config;
- K == capabilities_cb;
- K == disconnect_cb;
K == private ->
true;
-%% Anything else, which is ignored by us. This makes options sensitive
-%% to spelling mistakes but arbitrary options are passed by some users
-%% as a way to identify transports. (That is, can't just do away with
-%% it.)
-opt(_) ->
- true.
+%% Anything else, which is ignored in transport config. This makes
+%% options sensitive to spelling mistakes, but arbitrary options are
+%% passed by some users as a way to identify transports so can't just
+%% do away with it.
+opt(K, _) ->
+ K == transport.
+
+%% wd/1
-wdopt({K,N}) ->
+wd({K,N}) ->
(K == okay orelse K == suspect) andalso is_integer(N) andalso 0 =< N;
-wdopt(_) ->
+wd(_) ->
false.
%% start_transport/2
@@ -705,19 +801,7 @@ make_config(SvcName, Opts) ->
ok = encode_CER(CapOpts),
- SvcOpts = make_opts((Opts -- AppOpts) -- CapOpts,
- [{false, share_peers},
- {false, use_shared_peers},
- {false, monitor},
- {?NOMASK, sequence},
- {nodes, restrict_connections},
- {16#FFFFFF, incoming_maxlen},
- {true, strict_arities},
- {true, strict_mbit},
- {record, decode_format},
- {true, traffic_counters},
- {true, string_decode},
- {[], spawn_opt}]),
+ SvcOpts = service_opts((Opts -- AppOpts) -- CapOpts),
D = proplists:get_value(string_decode, SvcOpts, true),
@@ -731,115 +815,22 @@ binary_caps(Caps, true) ->
binary_caps(Caps, false) ->
diameter_capx:binary_caps(Caps).
-%% make_opts/2
+%% service_opts/1
-make_opts(Opts, Defs) ->
- Known = [{K, get_opt(K, Opts, D)} || {D,K} <- Defs],
- Unknown = Opts -- Known,
-
- [] == Unknown orelse ?THROW({invalid, hd(Unknown)}),
-
- [{K, opt(K,V)} || {K,V} <- Known].
-
-opt(incoming_maxlen, N)
- when 0 =< N, N < 1 bsl 24 ->
- N;
-
-opt(spawn_opt, {M,F,A} = T)
- when is_atom(M), is_atom(F), is_list(A) ->
- T;
-
-opt(spawn_opt, L)
- when is_list(L) ->
- spawn_opts(L);
-
-opt(K, false = B)
- when K == share_peers;
- K == use_shared_peers;
- K == monitor;
- K == restrict_connections;
- K == strict_arities;
- K == strict_mbit;
- K == decode_format;
- K == traffic_counters;
- K == string_decode ->
- B;
-
-opt(K, true = B)
- when K == share_peers;
- K == use_shared_peers;
- K == strict_arities;
- K == strict_mbit;
- K == traffic_counters;
- K == string_decode ->
- B;
-
-opt(decode_format, T)
- when T == record;
- T == list;
- T == map;
- T == record_from_map ->
- T;
-
-opt(strict_arities, T)
- when T == encode;
- T == decode ->
- T;
-
-opt(restrict_connections, T)
- when T == node;
- T == nodes ->
- T;
-
-opt(K, T)
- when (K == share_peers
- orelse K == use_shared_peers
- orelse K == restrict_connections), ([] == T
- orelse is_atom(hd(T))) ->
- T;
-
-opt(monitor, P)
- when is_pid(P) ->
- P;
-
-opt(K, F)
- when K == restrict_connections;
- K == share_peers;
- K == use_shared_peers ->
- try diameter_lib:eval(F) of %% but no guarantee that it won't fail later
- Nodes when is_list(Nodes) ->
- F;
- V ->
- ?THROW({value, {K,V}})
- catch
- E:R ->
- ?THROW({value, {K, E, R, ?STACK}})
- end;
-
-opt(sequence, {_,_} = T) ->
- sequence(T);
-
-opt(sequence = K, F) ->
- try diameter_lib:eval(F) of
- T -> sequence(T)
- catch
- E:R ->
- ?THROW({value, {K, E, R, ?STACK}})
- end;
-
-opt(K, _) ->
- ?THROW({value, K}).
+service_opts(Opts) ->
+ Res = [setopt(service, T) || T <- Opts],
+ Keys = sets:to_list(sets:from_list([K || {K,_} <- Res])), %% unique
+ Dups = lists:foldl(fun(K,A) -> lists:keydelete(K, 1, A) end, Res, Keys),
+ [] == Dups orelse ?THROW({duplicate, Dups}),
+ Res.
+%% Reject duplicates on a service, but not on a transport. There's no
+%% particular reason for the inconsistency, but the historic behaviour
+%% ignores all but the first of a transport_opt(), and there's no real
+%% reason to change it.
spawn_opts(L) ->
[T || T <- L, T /= link, T /= monitor].
-sequence({H,N} = T)
- when 0 =< N, N =< 32, 0 =< H, 0 == H bsr (32-N) ->
- T;
-
-sequence(_) ->
- ?THROW({value, sequence}).
-
make_caps(Caps, Opts) ->
case diameter_capx:make_caps(Caps, Opts) of
{ok, T} ->
diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl
index 7a1a46ec52..6add06ea38 100644
--- a/lib/diameter/src/base/diameter_gen.erl
+++ b/lib/diameter/src/base/diameter_gen.erl
@@ -45,7 +45,7 @@
-define(THROW(T), throw({?MODULE, T})).
-type parent_name() :: atom(). %% parent = Message or AVP
--type parent_record() :: tuple(). %%
+-type parent_record() :: tuple() | avp_values() | map().
-type avp_name() :: atom().
-type avp_record() :: tuple().
-type avp_values() :: [{avp_name(), term()}].
@@ -61,9 +61,7 @@
%% # encode_avps/3
%% ---------------------------------------------------------------------------
--spec encode_avps(parent_name(),
- parent_record() | avp_values() | map(),
- map())
+-spec encode_avps(parent_name(), parent_record(), map())
-> iolist()
| no_return().
@@ -102,73 +100,73 @@ encode(Name, Vals, Opts, Strict, Mod)
encode(Name, Map, Opts, Strict, Mod)
when is_map(Map) ->
- [enc(Name, F, A, V, Opts, Strict, Mod) || {F,A} <- Mod:avp_arity(Name),
- V <- [mget(F, Map, undefined)]];
+ [enc(F, A, V, Opts, Strict, Mod) || {F,A} <- Mod:avp_arity(Name),
+ V <- [mget(F, Map, undefined)]];
encode(Name, Rec, Opts, Strict, Mod) ->
[encode(Name, F, V, Opts, Strict, Mod) || {F,V} <- Mod:'#get-'(Rec)].
%% encode/6
-encode(Name, AvpName, Values, Opts, Strict, Mod)
+encode(_, AvpName, Values, Opts, Strict, Mod)
when Strict /= encode ->
- enc(Name, AvpName, ?ANY, Values, Opts, Strict, Mod);
+ enc(AvpName, ?ANY, Values, Opts, Strict, Mod);
encode(Name, AvpName, Values, Opts, Strict, Mod) ->
Arity = Mod:avp_arity(Name, AvpName),
- enc(Name, AvpName, Arity, Values, Opts, Strict, Mod).
+ enc(AvpName, Arity, Values, Opts, Strict, Mod).
-%% enc/7
+%% enc/6
-enc(Name, AvpName, Arity, Values, Opts, Strict, Mod)
+enc(AvpName, Arity, Values, Opts, Strict, Mod)
when Strict /= encode, Arity /= ?ANY ->
- enc(Name, AvpName, ?ANY, Values, Opts, Strict, Mod);
+ enc(AvpName, ?ANY, Values, Opts, Strict, Mod);
-enc(_, AvpName, 1, undefined, _, _, _) ->
+enc(AvpName, 1, undefined, _, _, _) ->
?THROW([mandatory_avp_missing, AvpName]);
-enc(Name, AvpName, 1, Value, Opts, _, Mod) ->
+enc(AvpName, 1, Value, Opts, _, Mod) ->
H = avp_header(AvpName, Mod),
- enc1(Name, AvpName, H, Value, Opts, Mod);
+ enc(AvpName, H, Value, Opts, Mod);
-enc(_, _, {0,_}, [], _, _, _) ->
+enc(_, {0,_}, [], _, _, _) ->
[];
-enc(_, _, _, undefined, _, _, _) ->
+enc(_, _, undefined, _, _, _) ->
[];
%% Be forgiving when a list of values is expected. If the value itself
%% is a list then the user has to wrap it to avoid each member from
%% being interpreted as an individual AVP value.
-enc(Name, AvpName, Arity, V, Opts, Strict, Mod)
+enc(AvpName, Arity, V, Opts, Strict, Mod)
when not is_list(V) ->
- enc(Name, AvpName, Arity, [V], Opts, Strict, Mod);
+ enc(AvpName, Arity, [V], Opts, Strict, Mod);
-enc(Name, AvpName, {Min, Max}, Values, Opts, Strict, Mod) ->
+enc(AvpName, {Min, Max}, Values, Opts, Strict, Mod) ->
H = avp_header(AvpName, Mod),
- enc(Name, AvpName, H, Min, 0, Max, Values, Opts, Strict, Mod).
+ enc(AvpName, H, Min, 0, Max, Values, Opts, Strict, Mod).
-%% enc/10
+%% enc/9
-enc(Name, AvpName, H, Min, N, Max, Vs, Opts, Strict, Mod)
+enc(AvpName, H, Min, N, Max, Vs, Opts, Strict, Mod)
when Strict /= encode;
Max == '*', Min =< N ->
- [enc1(Name, AvpName, H, V, Opts, Mod) || V <- Vs];
+ [enc(AvpName, H, V, Opts, Mod) || V <- Vs];
-enc(_, AvpName, _, Min, N, _, [], _, _, _)
+enc(AvpName, _, Min, N, _, [], _, _, _)
when N < Min ->
?THROW([repeated_avp_insufficient_arity, AvpName, Min, N]);
-enc(_, _, _, _, _, _, [], _, _, _) ->
+enc(_, _, _, _, _, [], _, _, _) ->
[];
-enc(_, AvpName, _, _, N, Max, _, _, _, _)
+enc(AvpName, _, _, N, Max, _, _, _, _)
when Max =< N ->
?THROW([repeated_avp_excessive_arity, AvpName, Max]);
-enc(Name, AvpName, H, Min, N, Max, [V|Vs], Opts, Strict, Mod) ->
- [enc1(Name, AvpName, H, V, Opts, Mod)
- | enc(Name, AvpName, H, Min, N+1, Max, Vs, Opts, Strict, Mod)].
+enc(AvpName, H, Min, N, Max, [V|Vs], Opts, Strict, Mod) ->
+ [enc(AvpName, H, V, Opts, Mod)
+ | enc(AvpName, H, Min, N+1, Max, Vs, Opts, Strict, Mod)].
%% avp_header/2
@@ -178,12 +176,12 @@ avp_header('AVP', _) ->
avp_header(AvpName, Mod) ->
{_,_,_} = Mod:avp_header(AvpName).
-%% enc1/6
+%% enc/5
-enc1(Name, 'AVP', false, Value, Opts, Mod) ->
- enc_AVP(Name, Value, Opts, Mod);
+enc('AVP', false, Value, Opts, Mod) ->
+ enc_AVP(Value, Opts, Mod);
-enc1(_, AvpName, Hdr, Value, Opts, Mod) ->
+enc(AvpName, Hdr, Value, Opts, Mod) ->
enc1(AvpName, Hdr, Value, Opts, Mod).
%% enc1/5
@@ -191,54 +189,72 @@ enc1(_, AvpName, Hdr, Value, Opts, Mod) ->
enc1(AvpName, {_,_,_} = Hdr, Value, Opts, Mod) ->
diameter_codec:pack_data(Hdr, Mod:avp(encode, Value, AvpName, Opts)).
-%% enc_AVP/4
+%% enc1/6
+
+enc1(AvpName, {_,_,_} = Hdr, Value, Opts, Mod, Dict) ->
+ diameter_codec:pack_data(Hdr, avp(encode, Value, AvpName, Opts, Mod, Dict)).
+
+%% enc_AVP/3
%% No value: assume AVP data is already encoded. The normal case will
%% be when this is passed back from #diameter_packet.errors as a
%% consequence of a failed decode. Any AVP can be encoded this way
%% however, which side-steps any arity checks for known AVP's and
%% could potentially encode something unfortunate.
-enc_AVP(_, #diameter_avp{value = undefined} = A, Opts, _) ->
+enc_AVP(#diameter_avp{value = undefined} = A, Opts, _) ->
diameter_codec:pack_avp(A, Opts);
-%% Missing name for value encode.
-enc_AVP(_, #diameter_avp{name = N, value = V}, _, _)
- when N == undefined;
- N == 'AVP' ->
- ?THROW([value_with_nameless_avp, N, V]);
+%% Encode a name/value pair using an alternate dictionary if need be ...
+enc_AVP(#diameter_avp{name = AvpName, value = Value}, Opts, Mod) ->
+ enc_AVP(AvpName, Value, Opts, Mod);
+enc_AVP({AvpName, Value}, Opts, Mod) ->
+ enc_AVP(AvpName, Value, Opts, Mod);
-%% Or not. Ensure that 'AVP' is the appropriate field. Note that if we
-%% don't know this AVP at all then the encode will fail.
-enc_AVP(Name, #diameter_avp{name = AvpName, value = Data}, Opts, Mod) ->
- 0 == Mod:avp_arity(Name, AvpName)
- orelse ?THROW([known_avp_as_AVP, Name, AvpName, Data]),
- enc(AvpName, Data, Opts, Mod);
+%% ... or with a specified dictionary.
+enc_AVP({Dict, AvpName, Value}, Opts, Mod) ->
+ enc1(AvpName, Dict:avp_header(AvpName), Value, Opts, Mod, Dict).
-%% The backdoor ...
-enc_AVP(_, {AvpName, Value}, Opts, Mod) ->
- enc(AvpName, Value, Opts, Mod);
+%% Don't guard against anything being sent as a generic 'AVP', which
+%% allows arity restrictions to be abused.
-%% ... and the side door.
-enc_AVP(_Name, {_Dict, _AvpName, _Data} = T, Opts, _) ->
- diameter_codec:pack_avp(#diameter_avp{data = T}, Opts).
+%% enc_AVP/4
-%% enc/4
+enc_AVP(AvpName, Value, Opts, Mod) ->
+ try Mod:avp_header(AvpName) of
+ H ->
+ enc1(AvpName, H, Value, Opts, Mod)
+ catch
+ error: _ ->
+ Dicts = mget(avp_dictionaries, Opts, []),
+ enc_AVP(Dicts, AvpName, Value, Opts, Mod)
+ end.
+
+%% enc_AVP/5
+
+enc_AVP([Dict | Rest], AvpName, Value, Opts, Mod) ->
+ try Dict:avp_header(AvpName) of
+ H ->
+ enc1(AvpName, H, Value, Opts, Mod, Dict)
+ catch
+ error: _ ->
+ enc_AVP(Rest, AvpName, Value, Opts, Mod)
+ end;
-enc(AvpName, Value, Opts, Mod) ->
- enc1(AvpName, Mod:avp_header(AvpName), Value, Opts, Mod).
+enc_AVP([], AvpName, _, _, _) ->
+ ?THROW([no_dictionary, AvpName]).
%% ---------------------------------------------------------------------------
%% # decode_avps/3
%% ---------------------------------------------------------------------------
-spec decode_avps(parent_name(), binary(), map())
- -> {parent_record(), [avp()], Failed}
+ -> {parent_record() | parent_name(), [avp()], Failed}
when Failed :: [{5000..5999, #diameter_avp{}}].
decode_avps(Name, Bin, #{module := Mod, decode_format := Fmt} = Opts) ->
Strict = mget(strict_arities, Opts, decode),
[AM, Avps, Failed | Rec]
- = decode(Bin, Name, Mod, Fmt, Strict, Opts, #{}),
+ = decode(Bin, Name, Mod, Fmt, Strict, Opts, 0, #{}),
%% AM counts the number of top-level AVPs, which missing/5 then
%% uses when appending 5005 errors.
{reformat(Name, Rec, Strict, Mod, Fmt),
@@ -249,7 +265,7 @@ decode_avps(Name, Bin, #{module := Mod, decode_format := Fmt} = Opts) ->
%% encountered. Failed-AVP should typically contain the first
%% error encountered.
-%% decode/7
+%% decode/8
decode(<<Code:32, V:1, M:1, P:1, _:5, Len:24, I:V/unit:32, Rest/binary>>,
Name,
@@ -257,6 +273,7 @@ decode(<<Code:32, V:1, M:1, P:1, _:5, Len:24, I:V/unit:32, Rest/binary>>,
Fmt,
Strict,
Opts,
+ Idx,
AM) ->
decode(Rest,
Code,
@@ -270,21 +287,23 @@ decode(<<Code:32, V:1, M:1, P:1, _:5, Len:24, I:V/unit:32, Rest/binary>>,
Fmt,
Strict,
Opts,
+ Idx,
AM);
-decode(<<>>, Name, Mod, Fmt, Strict, _, AM) ->
+decode(<<>>, Name, Mod, Fmt, Strict, _, _, AM) ->
[AM, [], [] | newrec(Fmt, Mod, Name, Strict)];
-decode(Bin, Name, Mod, Fmt, Strict, _, AM) ->
- Avp = #diameter_avp{data = Bin},
+decode(Bin, Name, Mod, Fmt, Strict, _, Idx, AM) ->
+ Avp = #diameter_avp{data = Bin, index = Idx},
[AM, [Avp], [{5014, Avp}] | newrec(Fmt, Mod, Name, Strict)].
-%% decode/13
+%% decode/14
-decode(Bin, Code, Vid, DataLen, Pad, M, P, Name, Mod, Fmt, Strict, Opts0, AM0) ->
+decode(Bin, Code, Vid, DataLen, Pad, M, P, Name, Mod, Fmt, Strict, Opts0,
+ Idx, AM0) ->
case Bin of
<<Data:DataLen/binary, _:Pad/binary, T/binary>> ->
- {NameT, AvpName, Arity, {Idx, AM}}
+ {NameT, Field, Arity, {I, AM}}
= incr(Name, Code, Vid, M, Mod, Strict, Opts0, AM0),
Opts = setopts(NameT, Name, M, Opts0),
@@ -300,11 +319,11 @@ decode(Bin, Code, Vid, DataLen, Pad, M, P, Name, Mod, Fmt, Strict, Opts0, AM0) -
type = type(NameT),
index = Idx},
- Dec = decode(Data, Name, NameT, Mod, Opts, Avp), %% decode
- Acc = decode(T, Name, Mod, Fmt, Strict, Opts, AM), %% recurse
- acc(Acc, Dec, Name, AvpName, Arity, Strict, Mod, Opts);
+ Dec = dec(Data, Name, NameT, Mod, Fmt, Opts, Avp),
+ Acc = decode(T, Name, Mod, Fmt, Strict, Opts, Idx+1, AM),%% recurse
+ acc(Acc, Dec, I, Field, Arity, Strict, Mod, Opts);
_ ->
- {NameT, _AvpName, _Arity, {Idx, AM}}
+ {NameT, _Field, _Arity, {_, AM}}
= incr(Name, Code, Vid, M, Mod, Strict, Opts0, AM0),
Avp = #diameter_avp{code = Code,
@@ -323,9 +342,14 @@ decode(Bin, Code, Vid, DataLen, Pad, M, P, Name, Mod, Fmt, Strict, Opts0, AM0) -
incr(Name, Code, Vid, M, Mod, Strict, Opts, AM0) ->
NameT = Mod:avp_name(Code, Vid), %% {AvpName, Type} | 'AVP'
- AvpName = field(NameT),
- Arity = avp_arity(Name, AvpName, Mod, Opts, M),
- {NameT, AvpName, Arity, incr(AvpName, Arity, Strict, AM0)}.
+ Field = field(NameT), %% AvpName | 'AVP'
+ Arity = avp_arity(Name, Field, Mod, Opts, M),
+ if 0 == Arity, 'AVP' /= Field ->
+ A = pack_arity(Name, Field, Opts, Mod, M),
+ {NameT, 'AVP', A, incr('AVP', A, Strict, AM0)};
+ true ->
+ {NameT, Field, Arity, incr(Field, Arity, Strict, AM0)}
+ end.
%% Data is a truncated header if command_code = undefined, otherwise
%% payload bytes. The former is padded to the length of a header if
@@ -342,9 +366,8 @@ setopts({_, Type}, Name, M, Opts) ->
%% incr/4
-incr(F, A, SA, AM)
- when F == 'AVP';
- A == ?ANY;
+incr(_, A, SA, AM)
+ when A == ?ANY;
A == 0;
SA /= decode ->
{undefined, AM};
@@ -444,12 +467,16 @@ field({AvpName, _}) ->
field(_) ->
'AVP'.
-%% decode/6
+%% dec/7
-%% AVP not in dictionary.
-decode(_Data, _Name, 'AVP', _Mod, _Opts, Avp) ->
+%% AVP not in dictionary: try an alternate.
+
+dec(_, _, 'AVP', _Mod, none, _, Avp) -> %% none decode is no-op
Avp;
+dec(Data, Name, 'AVP', Mod, Fmt, Opts, Avp) ->
+ dec_AVP(dicts(Mod, Opts), Data, Name, Mod, Fmt, Opts, Avp);
+
%% 6733, 4.4:
%%
%% Receivers of a Grouped AVP that does not have the 'M' (mandatory)
@@ -497,40 +524,100 @@ decode(_Data, _Name, 'AVP', _Mod, _Opts, Avp) ->
%% defined the RFC's "unrecognized", which is slightly stronger than
%% "not defined".)
-decode(Data, Name, {AvpName, Type}, Mod, Opts, Avp) ->
- #{dictionary := AppMod, failed_avp := Failed}
+dec(Data, Name, {AvpName, Type}, Mod, Fmt, Opts, Avp) ->
+ #{app_dictionary := AppMod, failed_avp := Failed}
= Opts,
%% Reset the dictionary for best-effort decode of Failed-AVP.
- DecMod = if Failed -> AppMod;
- true -> Mod
- end,
-
- %% A Grouped AVP is represented as a #diameter_avp{} list with AVP
- %% as head and component AVPs as tail. On encode, data can be a
- %% list of component AVPs.
-
- try avp_decode(Data, AvpName, Opts, DecMod, Mod) of
- {Rec, As} when Type == 'Grouped' ->
- A = Avp#diameter_avp{value = Rec},
- [A | As];
- V when Type /= 'Grouped' ->
- Avp#diameter_avp{value = V}
+ Dict = if Failed -> AppMod;
+ true -> Mod
+ end,
+
+ dec(Data, Name, AvpName, Type, Mod, Dict, Fmt, Failed, Opts, Avp).
+
+%% dicts/2
+
+dicts(Mod, #{app_dictionary := Mod, avp_dictionaries := Dicts}) ->
+ Dicts;
+
+dicts(_, #{app_dictionary := Dict, avp_dictionaries := Dicts}) ->
+ [Dict | Dicts];
+
+dicts(Mod, #{app_dictionary := Mod}) ->
+ [];
+
+dicts(_, #{app_dictionary := Dict}) ->
+ [Dict].
+
+%% dec/10
+
+dec(Data, Name, AvpName, Type, Mod, Dict, Fmt, Failed, Opts, Avp) ->
+ try avp(decode, Data, AvpName, Opts, Mod, Dict) of
+ V ->
+ set(Type, Fmt, Avp, V)
catch
throw: {?MODULE, T} ->
- decode_error(Failed, T, Avp);
+ decode_error(Failed, Fmt, T, Avp);
error: Reason ->
decode_error(Failed, Reason, Name, Mod, Opts, Avp)
end.
-%% decode_error/3
+%% dec_AVP/7
+
+dec_AVP([], _, _, _, _, _, Avp) ->
+ Avp;
+
+dec_AVP(Dicts, Data, Name, Mod, Fmt, Opts, #diameter_avp{code = Code,
+ vendor_id = Vid}
+ = Avp) ->
+ dec_AVP(Dicts, Data, Name, Mod, Fmt, Opts, Code, Vid, Avp).
+
+%% dec_AVP/9
+%%
+%% Try to decode an AVP in the first alternate dictionary that defines
+%% it.
+
+dec_AVP([Dict | Rest], Data, Name, Mod, Fmt, Opts, Code, Vid, Avp) ->
+ case Dict:avp_name(Code, Vid) of
+ {AvpName, Type} ->
+ A = Avp#diameter_avp{name = AvpName,
+ type = Type},
+ #{failed_avp := Failed} = Opts,
+ dec(Data, Name, AvpName, Type, Mod, Dict, Fmt, Failed, Opts, A);
+ _ ->
+ dec_AVP(Rest, Data, Name, Mod, Fmt, Opts, Code, Vid, Avp)
+ end;
+
+dec_AVP([], _, _, _, _, _, _, _, Avp) ->
+ Avp.
+
+%% set/4
+%%
+%% A Grouped AVP is represented as a #diameter_avp{} list with AVP
+%% as head and component AVPs as tail.
+
+set('Grouped', none, Avp, V) ->
+ {_Rec, As} = V,
+ [Avp | As];
+
+set('Grouped', _, Avp, V) ->
+ {Rec, As} = V,
+ [Avp#diameter_avp{value = Rec} | As];
+
+set(_, _, Avp, V) ->
+ Avp#diameter_avp{value = V}.
+
+%% decode_error/4
%%
%% Error when decoding a grouped AVP.
-decode_error(true, {Rec, _, _}, Avp) ->
+decode_error(true, none, _, Avp) ->
+ Avp;
+
+decode_error(true, _, {Rec, _, _}, Avp) ->
Avp#diameter_avp{value = Rec};
-decode_error(false, {_, ComponentAvps, [{RC,A} | _]}, Avp) ->
+decode_error(false, _, {_, ComponentAvps, [{RC,A} | _]}, Avp) ->
{RC, [Avp | ComponentAvps], Avp#diameter_avp{data = [A]}}.
%% decode_error/6
@@ -548,13 +635,13 @@ decode_error(false, Reason, Name, Mod, Opts, Avp) ->
{Reason, Name, Avp#diameter_avp.name, Mod, Stack}),
rc(Reason, Avp, Opts, Mod).
-%% avp_decode/5
+%% avp/6
-avp_decode(Data, AvpName, Opts, Mod, Mod) ->
- Mod:avp(decode, Data, AvpName, Opts);
+avp(T, Data, AvpName, Opts, Mod, Mod) ->
+ Mod:avp(T, Data, AvpName, Opts);
-avp_decode(Data, AvpName, Opts, Mod, _) ->
- Mod:avp(decode, Data, AvpName, Opts, Mod).
+avp(T, Data, AvpName, Opts, _, Mod) ->
+ Mod:avp(T, Data, AvpName, Opts#{module := Mod}).
%% set_strict/3
%%
@@ -579,8 +666,8 @@ set_failed(_, Opts) ->
%% acc/8
-acc([AM | Acc], As, Name, AvpName, Arity, Strict, Mod, Opts) ->
- [AM | acc1(Acc, As, Name, AvpName, Arity, Strict, Mod, Opts)].
+acc([AM | Acc], As, I, Field, Arity, Strict, Mod, Opts) ->
+ [AM | acc1(Acc, As, I, Field, Arity, Strict, Mod, Opts)].
%% acc1/8
@@ -595,12 +682,20 @@ acc1(Acc, {RC, As, Avp}, _, _, _, _, _, _) ->
[[As | Avps], [{RC, Avp} | Failed] | Rec];
%% Grouped AVP ...
-acc1([Avps | Acc], [Avp|_] = As, Name, AvpName, Arity, Strict, Mod, Opts) ->
- [[As|Avps] | acc2(Acc, Avp, Name, AvpName, Arity, Strict, Mod, Opts)];
+acc1([Avps | Acc], [Avp|_] = As, I, Field, Arity, Strict, Mod, Opts) ->
+ [[As|Avps] | acc2(Acc, Avp, I, Field, Arity, Strict, Mod, Opts)];
%% ... or not.
-acc1([Avps | Acc], Avp, Name, AvpName, Arity, Strict, Mod, Opts) ->
- [[Avp|Avps] | acc2(Acc, Avp, Name, AvpName, Arity, Strict, Mod, Opts)].
+acc1([Avps | Acc], Avp, I, Field, Arity, Strict, Mod, Opts) ->
+ [[Avp|Avps] | acc2(Acc, Avp, I, Field, Arity, Strict, Mod, Opts)].
+
+%% The component list of a Grouped AVP is discarded when packing into
+%% the record (or equivalent): the values in an 'AVP' field are
+%% diameter_avp records, not a list of records in the Grouped case,
+%% and the decode into the value field is best-effort. The reason is
+%% history more than logic: it would probably have made more sense to
+%% retain the same structure as in diameter_packet.avps, but an 'AVP'
+%% list has always been flat.
%% acc2/8
@@ -609,29 +704,23 @@ acc2(Acc, Avp, _, 'AVP', 0, _, _, _) ->
[Failed | Rec] = Acc,
[[{rc(Avp), Avp} | Failed] | Rec];
-%% No AVP of this name: try to pack as 'AVP'.
-acc2(Acc, Avp, Name, AvpName, 0, Strict, Mod, Opts) ->
- M = Avp#diameter_avp.is_mandatory,
- Arity = pack_arity(Name, AvpName, Opts, Mod, M),
- acc2(Acc, Avp, Name, 'AVP', Arity, Strict, Mod, Opts);
-
%% Relaxed arities.
-acc2(Acc, Avp, _, AvpName, Arity, Strict, Mod, _)
+acc2(Acc, Avp, _, Field, Arity, Strict, Mod, _)
when Strict /= decode ->
- pack(Arity, AvpName, Avp, Mod, Acc);
+ pack(Arity, Field, Avp, Mod, Acc);
%% No maximum arity.
-acc2(Acc, Avp, _, AvpName, {_,'*'} = Arity, _, Mod, _) ->
- pack(Arity, AvpName, Avp, Mod, Acc);
+acc2(Acc, Avp, _, Field, {_,'*'} = Arity, _, Mod, _) ->
+ pack(Arity, Field, Avp, Mod, Acc);
%% Or check.
-acc2(Acc, Avp, _, AvpName, Arity, _, Mod, _) ->
+acc2(Acc, Avp, I, Field, Arity, _, Mod, _) ->
Mx = max_arity(Arity),
- if Mx =< Avp#diameter_avp.index ->
+ if Mx =< I ->
[Failed | Rec] = Acc,
[[{5009, Avp} | Failed] | Rec];
true ->
- pack(Arity, AvpName, Avp, Mod, Acc)
+ pack(Arity, Field, Avp, Mod, Acc)
end.
%% 3588/6733:
@@ -723,8 +812,9 @@ pack(Arity, F, Avp, Mod, [Failed | Rec]) ->
%% set/5
-set(_, _, _, _, false = No) ->
- No;
+set(_, _, _, _, None)
+ when is_atom(None) ->
+ None;
set(1, F, Value, _, Map)
when is_map(Map) ->
@@ -818,8 +908,8 @@ empty(Name, #{module := Mod} = Opts) ->
%% newrec/4
-newrec(false = No, _, _, _) ->
- No;
+newrec(none, _, Name, _) ->
+ Name;
newrec(record, Mod, Name, T)
when T /= decode ->
diff --git a/lib/diameter/src/base/diameter_lib.erl b/lib/diameter/src/base/diameter_lib.erl
index 8792e97621..1c1ea42cb5 100644
--- a/lib/diameter/src/base/diameter_lib.erl
+++ b/lib/diameter/src/base/diameter_lib.erl
@@ -283,7 +283,7 @@ ip(T)
%% Or not: convert from '.'/':'-separated decimal/hex.
ip(Addr) ->
- {ok, A} = inet_parse:address(Addr), %% documented in inet(3)
+ {ok, A} = inet:parse_address(Addr),
A.
%% ---------------------------------------------------------------------------
diff --git a/lib/diameter/src/base/diameter_peer.erl b/lib/diameter/src/base/diameter_peer.erl
index 2759f17e64..4cb5a57a54 100644
--- a/lib/diameter/src/base/diameter_peer.erl
+++ b/lib/diameter/src/base/diameter_peer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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.
@@ -202,10 +202,10 @@ match1(Addr, Match) ->
match(Addr, {ok, A}, _) ->
Addr == A;
match(Addr, {error, _}, RE) ->
- match == re:run(inet_parse:ntoa(Addr), RE, [{capture, none}]).
+ match == re:run(inet:ntoa(Addr), RE, [{capture, none}, caseless]).
addr([_|_] = A) ->
- inet_parse:address(A);
+ inet:parse_address(A);
addr(A) ->
{ok, A}.
diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl
index 9115630eb5..d99f11a697 100644
--- a/lib/diameter/src/base/diameter_peer_fsm.erl
+++ b/lib/diameter/src/base/diameter_peer_fsm.erl
@@ -128,9 +128,8 @@
%% outgoing DPR; boolean says whether or not
%% the request was sent explicitly with
%% diameter:call/4.
- codec :: #{decode_format := record,
+ codec :: #{decode_format := diameter:decode_format(),
string_decode := boolean(),
- strict_arities => diameter:strict_arities(),
strict_mbit := boolean(),
rfc := 3588 | 6733,
ordered_encode := false},
@@ -239,7 +238,7 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) ->
proplists:get_value(dpa_timeout, Opts, ?DPA_TIMEOUT)}),
Tmo = proplists:get_value(capx_timeout, Opts, ?CAPX_TIMEOUT),
- Strictness = proplists:get_value(capx_strictness, Opts, true),
+ Strict = proplists:get_value(strict_capx, Opts, true),
LengthErr = proplists:get_value(length_errors, Opts, exit),
{TPid, Addrs} = start_transport(T, Rest, Svc),
@@ -253,15 +252,14 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) ->
mode = M,
service = svc(Svc, Addrs),
length_errors = LengthErr,
- strict = Strictness,
+ strict = Strict,
incoming_maxlen = Maxlen,
codec = maps:with([decode_format,
string_decode,
strict_mbit,
rfc,
ordered_encode],
- SvcOpts#{ordered_encode => false,
- decode_format => record})}.
+ SvcOpts#{ordered_encode => false})}.
%% The transport returns its local ip addresses so that different
%% transports on the same service can use different local addresses.
%% The local addresses are put into Host-IP-Address avps here when
@@ -818,7 +816,8 @@ handle('DPA' = N,
%% service: explicit DPR is counted in the same way
%% as other explicitly sent requests.
incr(recv, H, Dict0),
- incr_rc(recv, diameter_codec:decode(Dict0, Opts, Pkt), Dict0)
+ {_, RecPkt} = decode(Dict0, Opts, Pkt),
+ incr_rc(recv, RecPkt, Dict0)
end,
diameter_peer:close(TPid),
{stop, N};
@@ -922,21 +921,30 @@ handle_request(Name,
= S) ->
?LOG(recv, Name),
incr(recv, H, Dict0),
- send_answer(Name, diameter_codec:decode(Dict0, Opts, Pkt), S).
+ send_answer(Name, decode(Dict0, Opts, Pkt), S).
+
+%% decode/3
+%%
+%% Decode the message as record for diameter_capx, and in the
+%% configured format for events.
+
+decode(Dict0, Opts, Pkt) ->
+ {diameter_codec:decode(Dict0, Opts, Pkt),
+ diameter_codec:decode(Dict0, Opts#{decode_format := record}, Pkt)}.
%% send_answer/3
-send_answer(Type, ReqPkt, #state{transport = TPid,
- dictionary = Dict,
- codec = Opts}
- = S) ->
- incr_error(recv, ReqPkt, Dict),
+send_answer(Type, {DecPkt, RecPkt}, #state{transport = TPid,
+ dictionary = Dict,
+ codec = Opts}
+ = S) ->
+ incr_error(recv, RecPkt, Dict),
#diameter_packet{header = H,
transport_data = TD}
- = ReqPkt,
+ = RecPkt,
- {Msg, PostF} = build_answer(Type, ReqPkt, S),
+ {Msg, PostF} = build_answer(Type, DecPkt, RecPkt, S),
%% An answer message clears the R and T flags and retains the P
%% flag. The E flag is set at encode.
@@ -964,15 +972,15 @@ eval([F|A], S) ->
eval(T, _) ->
close(T).
-%% build_answer/3
+%% build_answer/4
build_answer('CER',
+ DecPkt,
#diameter_packet{msg = CER,
header = #diameter_header{version
= ?DIAMETER_VERSION,
is_error = false},
- errors = []}
- = Pkt,
+ errors = []},
#state{dictionary = Dict0}
= S) ->
{SupportedApps, RCaps, CEA} = recv_CER(CER, S),
@@ -990,25 +998,25 @@ build_answer('CER',
orelse ?THROW(4003), %% DIAMETER_ELECTION_LOST
caps_cb(Caps)
of
- N -> {cea(CEA, N, Dict0), [fun open/5, Pkt,
+ N -> {cea(CEA, N, Dict0), [fun open/5, DecPkt,
SupportedApps,
Caps,
{accept, inband_security(IS)}]}
catch
?FAILURE(Reason) ->
- rejected(Reason, {'CER', Reason, Caps, Pkt}, S)
+ rejected(Reason, {'CER', Reason, Caps, DecPkt}, S)
end;
%% The error checks below are similar to those in diameter_traffic for
%% other messages. Should factor out the commonality.
build_answer(Type,
+ DecPkt,
#diameter_packet{header = H,
- errors = Es}
- = Pkt,
+ errors = Es},
S) ->
{RC, FailedAVP} = result_code(Type, H, Es),
- {answer(Type, RC, FailedAVP, S), post(Type, RC, Pkt, S)}.
+ {answer(Type, RC, FailedAVP, S), post(Type, RC, DecPkt, S)}.
inband_security([]) ->
?NO_INBAND_SECURITY;
@@ -1180,12 +1188,10 @@ handle_CEA(#diameter_packet{header = H}
= S) ->
incr(recv, H, Dict0),
- #diameter_packet{}
- = DPkt
- = diameter_codec:decode(Dict0, Opts, Pkt),
+ {DecPkt, RecPkt} = decode(Dict0, Opts, Pkt),
- RC = result_code(incr_rc(recv, DPkt, Dict0)),
- {SApps, IS, RCaps} = recv_CEA(DPkt, S),
+ RC = result_code(incr_rc(recv, RecPkt, Dict0)),
+ {SApps, IS, RCaps} = recv_CEA(RecPkt, S),
#diameter_caps{origin_host = {OH, DH}}
= Caps
@@ -1208,9 +1214,9 @@ handle_CEA(#diameter_packet{header = H}
orelse ?THROW(election_lost),
caps_cb(Caps)
of
- _ -> open(DPkt, SApps, Caps, {connect, hd([_] = IS)}, S)
+ _ -> open(DecPkt, SApps, Caps, {connect, hd([_] = IS)}, S)
catch
- ?FAILURE(Reason) -> close({'CEA', Reason, Caps, DPkt})
+ ?FAILURE(Reason) -> close({'CEA', Reason, Caps, DecPkt})
end.
%% Check more than the result code since the peer could send success
%% regardless. If not 2001 then a peer_up callback could do anything
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index 208e2e8cb8..31dd92f878 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -115,8 +115,21 @@
strict_arities => diameter:strict_arities(),
strict_mbit := boolean(),
decode_format := diameter:decode_format(),
+ avp_dictionaries => nonempty_list(module()),
traffic_counters := boolean(),
string_decode := boolean(),
+ capabilities_cb => diameter:evaluable(),
+ pool_size => pos_integer(),
+ capx_timeout => diameter:'Unsigned32'(),
+ strict_capx => boolean(),
+ disconnect_cb => diameter:evaluable(),
+ dpr_timeout => diameter:'Unsigned32'(),
+ dpa_timeout => diameter:'Unsigned32'(),
+ length_errors => exit | handle | discard,
+ connect_timer => diameter:'Unsigned32'(),
+ watchdog_timer => diameter:'Unsigned32'()
+ | {module(), atom(), list()},
+ watchdog_config => [{okay|suspect, non_neg_integer()}],
spawn_opt := list() | {module(), atom(), list()}}}).
%% Record representing an RFC 3539 watchdog process implemented by
@@ -689,12 +702,15 @@ i(SvcName) ->
cfg_acc({SvcName, #diameter_service{applications = Apps} = Rec, Opts},
{false, Acc}) ->
lists:foreach(fun init_mod/1, Apps),
+ #{monitor := M}
+ = SvcOpts
+ = service_opts(Opts),
S = #state{service_name = SvcName,
service = Rec#diameter_service{pid = self()},
local = init_peers(),
remote = init_peers(),
- monitor = mref(get_value(monitor, Opts)),
- options = service_options(lists:keydelete(monitor, 1, Opts))},
+ monitor = mref(M),
+ options = maps:remove(monitor, SvcOpts)},
{S, Acc};
cfg_acc({_Ref, Type, _Opts} = T, {S, Acc})
@@ -709,8 +725,29 @@ init_peers() ->
%% Alias,
%% TPid}
-service_options(Opts) ->
- maps:from_list(lists:delete({strict_arities, true}, Opts)).
+service_opts(Opts) ->
+ remove([{strict_arities, true},
+ {avp_dictionaries, []}],
+ maps:merge(maps:from_list([{monitor, false} | def_opts()]),
+ maps:from_list(Opts))).
+
+remove(List, Map) ->
+ maps:filter(fun(K,V) -> not lists:member({K,V}, List) end,
+ Map).
+
+def_opts() -> %% defaults on the service map
+ [{share_peers, false},
+ {use_shared_peers, false},
+ {sequence, {0,32}},
+ {restrict_connections, nodes},
+ {incoming_maxlen, 16#FFFFFF},
+ {strict_arities, true},
+ {strict_mbit, true},
+ {decode_format, record},
+ {avp_dictionaries, []},
+ {traffic_counters, true},
+ {string_decode, true},
+ {spawn_opt, []}].
mref(false = No) ->
No;
@@ -730,10 +767,6 @@ init_mod(#diameter_app{alias = Alias,
start_fsm({Ref, Type, Opts}, S) ->
start(Ref, {Type, Opts}, S).
-get_value(Key, Vs) ->
- {_, V} = lists:keyfind(Key, 1, Vs),
- V.
-
notify(Share, SvcName, T) ->
Nodes = remotes(Share),
[] /= Nodes andalso diameter_peer:notify(Nodes, SvcName, T).
@@ -823,7 +856,7 @@ start(Ref, Type, Opts, State) ->
start(Ref, Type, Opts, N, #state{watchdogT = WatchdogT,
local = {PeerT, _, _},
options = #{string_decode := SD}
- = SvcOpts0,
+ = SvcOpts,
service_name = SvcName,
service = Svc0})
when Type == connect;
@@ -832,12 +865,12 @@ start(Ref, Type, Opts, N, #state{watchdogT = WatchdogT,
= Svc1
= merge_service(Opts, Svc0),
Svc = binary_caps(Svc1, SD),
- SvcOpts = merge_options(Opts, SvcOpts0),
- RecvData = diameter_traffic:make_recvdata([SvcName, PeerT, Apps, SvcOpts]),
- T = {Opts, SvcOpts, RecvData, Svc},
+ {SOpts, TOpts} = merge_opts(SvcOpts, Opts),
+ RecvData = diameter_traffic:make_recvdata([SvcName, PeerT, Apps, SOpts]),
+ T = {TOpts, SOpts, RecvData, Svc},
Rec = #watchdog{type = Type,
ref = Ref,
- options = Opts},
+ options = TOpts},
diameter_lib:fold_n(fun(_,A) ->
[wd(Type, Ref, T, WatchdogT, Rec) | A]
@@ -845,10 +878,14 @@ start(Ref, Type, Opts, N, #state{watchdogT = WatchdogT,
[],
N).
-merge_options(Opts, SvcOpts) ->
- Keys = maps:keys(SvcOpts),
- Map = maps:from_list([KV || {K,_} = KV <- Opts, lists:member(K, Keys)]),
- maps:merge(SvcOpts, Map).
+merge_opts(SvcOpts, Opts) ->
+ Keys = [K || {K,_} <- def_opts()],
+ SO = [T || {K,_} = T <- Opts, lists:member(K, Keys)],
+ TO = Opts -- SO,
+ {maps:merge(maps:with(Keys, SvcOpts), maps:from_list(SO)),
+ TO ++ [T || {K,_} = T <- maps:to_list(SvcOpts),
+ not lists:member(K, Keys),
+ not lists:keymember(K, 1, Opts)]}.
binary_caps(Svc, true) ->
Svc;
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index 27a41d6eb0..f510f40a17 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -78,6 +78,7 @@
sequence :: diameter:sequence(),
counters :: boolean(),
codec :: #{decode_format := diameter:decode_format(),
+ avp_dictionaries => nonempty_list(module()),
string_decode := boolean(),
strict_arities => diameter:strict_arities(),
strict_mbit := boolean(),
@@ -92,6 +93,7 @@
caller :: pid() | undefined, %% calling process
handler :: pid(), %% request process
peer :: undefined | {pid(), #diameter_caps{}},
+ caps :: undefined, %% no longer used
packet :: #diameter_packet{} | undefined}). %% of request
%% ---------------------------------------------------------------------------
@@ -107,6 +109,7 @@ make_recvdata([SvcName, PeerT, Apps, SvcOpts | _]) ->
sequence = Mask,
counters = B,
codec = maps:with([decode_format,
+ avp_dictionaries,
string_decode,
strict_arities,
strict_mbit,
@@ -351,6 +354,8 @@ recv_request(Ack,
No
end.
+%% decode/4
+
decode(Id, Dict, #recvdata{codec = Opts}, Pkt) ->
errors(Id, diameter_codec:decode(Id, Dict, Opts, Pkt)).
@@ -1031,15 +1036,15 @@ answer_message(RC,
origin_realm = {OR,_}},
#diameter_packet{avps = Avps,
errors = Es}) ->
- {Code, _, Vid} = Dict0:avp_header('Session-Id'),
['answer-message', {'Origin-Host', OH},
{'Origin-Realm', OR},
- {'Result-Code', RC}]
- ++ session_id(Code, Vid, Avps)
- ++ failed_avp(RC, Es).
+ {'Result-Code', RC}
+ | session_id(Dict0, Avps)
+ ++ failed_avp(RC, Es)
+ ++ proxy_info(Dict0, Avps)].
-session_id(Code, Vid, Avps)
- when is_list(Avps) ->
+session_id(Dict0, Avps) ->
+ {Code, _, Vid} = Dict0:avp_header('Session-Id'),
try
#diameter_avp{data = Bin} = find_avp(Code, Vid, Avps),
[{'Session-Id', [Bin]}]
@@ -1057,6 +1062,14 @@ failed_avp(RC, [_ | Es]) ->
failed_avp(_, [] = No) ->
No.
+proxy_info(Dict0, Avps) ->
+ {Code, _, Vid} = Dict0:avp_header('Proxy-Info'),
+ [{'AVP', [A#diameter_avp{value = undefined}
+ || [#diameter_avp{code = C, vendor_id = I} = A | _]
+ <- Avps,
+ C == Code,
+ I == Vid]}].
+
%% find_avp/3
%% Grouped ...
@@ -1891,16 +1904,12 @@ str(T) ->
%% get_avp/3
%%
-%% Find an AVP in a message of one of three forms:
-%%
-%% - a message record (as generated from a .dia spec) or
-%% - a list of an atom message name followed by 2-tuple, avp name/value pairs.
-%% - a list of a #diameter_header{} followed by #diameter_avp{} records,
-%%
-%% In the first two forms a dictionary module is used at encode to
-%% identify the type of the AVP and its arity in the message in
-%% question. The third form allows messages to be sent as is, without
-%% a dictionary, which is needed in the case of relay agents, for one.
+%% Find an AVP in a message in one of the decoded formats, or as a
+%% header/avps list. There are only four AVPs that are extracted here:
+%% Result-Code and Experimental-Result in order when constructing
+%% counter keys, and Destination-Host/Realm when selecting a next-hop
+%% peer. Experimental-Result is the only of type Grouped, and is given
+%% special treatment in order to return the value as a record.
%% Messages will be header/avps list as a relay and the only AVP's we
%% look for are in the common dictionary. This is required since the
@@ -1909,12 +1918,12 @@ str(T) ->
get_avp(?RELAY, Name, Msg) ->
get_avp(?BASE, Name, Msg);
-%% Message is a header/avps list.
+%% Message as header/avps list.
get_avp(Dict, Name, [#diameter_header{} | Avps]) ->
try
- {Code, _, VId} = Dict:avp_header(Name),
- A = find_avp(Code, VId, Avps),
- (avp_decode(Dict, Name, ungroup(A)))#diameter_avp{name = Name}
+ {Code, _, Vid} = Dict:avp_header(Name),
+ A = find_avp(Code, Vid, Avps),
+ avp_decode(Dict, Name, ungroup(A))
catch
error: _ ->
undefined
@@ -1924,20 +1933,33 @@ get_avp(Dict, Name, [#diameter_header{} | Avps]) ->
get_avp(_, Name, [_MsgName | Avps]) ->
case find(Name, Avps) of
{_, V} ->
- #diameter_avp{name = Name, value = V};
+ #diameter_avp{name = Name, value = value(Name, V)};
_ ->
undefined
end;
-%% ... or record (but not necessarily).
+%% ... or record.
get_avp(Dict, Name, Rec) ->
- try
- #diameter_avp{name = Name, value = Dict:'#get-'(Name, Rec)}
+ try Dict:'#get-'(Name, Rec) of
+ V ->
+ #diameter_avp{name = Name, value = value(Name, V)}
catch
error:_ ->
undefined
end.
+value('Experimental-Result' = N, #{'Vendor-Id' := Vid,
+ 'Experimental-Result-Code' := RC}) ->
+ {N, Vid, RC};
+value('Experimental-Result' = N, [{'Experimental-Result-Code', RC},
+ {'Vendor-Id', Vid}]) ->
+ {N, Vid, RC};
+value('Experimental-Result' = N, [{'Vendor-Id', Vid},
+ {'Experimental-Result-Code', RC}]) ->
+ {N, Vid, RC};
+value(_, V) ->
+ V.
+
%% find/2
find(Key, Map)
@@ -1967,14 +1989,25 @@ ungroup(Avp) ->
%% avp_decode/3
+%% Ensure Experimental-Result is decoded as record, since this format
+%% is used for counter keys.
+avp_decode(Dict, 'Experimental-Result' = N, #diameter_avp{data = Bin}
+ = Avp)
+ when is_binary(Bin) ->
+ {V,_} = Dict:avp(decode, Bin, N, decode_opts(Dict)),
+ Avp#diameter_avp{name = N, value = V};
+
avp_decode(Dict, Name, #diameter_avp{value = undefined,
data = Bin}
= Avp)
when is_binary(Bin) ->
V = Dict:avp(decode, Bin, Name, decode_opts(Dict)),
- Avp#diameter_avp{value = V};
-avp_decode(_, _, #diameter_avp{} = Avp) ->
- Avp.
+ Avp#diameter_avp{name = Name, value = V};
+
+avp_decode(_, Name, #diameter_avp{} = Avp) ->
+ Avp#diameter_avp{name = Name}.
+
+%% cb/3
cb(#diameter_app{module = [_|_] = M}, F, A) ->
eval(M, F, A).
@@ -1991,4 +2024,5 @@ decode_opts(Dict) ->
string_decode => false,
strict_mbit => false,
failed_avp => false,
- dictionary => Dict}.
+ module => Dict,
+ app_dictionary => Dict}.
diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl
index b2172356ee..c08e2da672 100644
--- a/lib/diameter/src/base/diameter_watchdog.erl
+++ b/lib/diameter/src/base/diameter_watchdog.erl
@@ -72,14 +72,12 @@
restrict := boolean(),
suspect := non_neg_integer(), %% OKAY -> SUSPECT
okay := non_neg_integer()}, %% REOPEN -> OKAY
- codec :: #{decode_format := false,
+ codec :: #{decode_format := none,
string_decode := false,
strict_arities => diameter:strict_arities(),
strict_mbit := boolean(),
- failed_avp := false,
rfc := 3588 | 6733,
- ordered_encode := false,
- incoming_maxlen := diameter:message_length()},
+ ordered_encode := false},
shutdown = false :: boolean()}).
%% ---------------------------------------------------------------------------
@@ -137,15 +135,6 @@ i({Ack, T, Pid, {Opts,
putr(restart, {T, Opts, Svc, SvcOpts}), %% save seeing it in trace
putr(dwr, dwr(Caps)), %%
Nodes = restrict_nodes(Restrict),
- CodecKeys = [decode_format,
- string_decode,
- strict_arities,
- strict_mbit,
- incoming_maxlen,
- spawn_opt,
- rfc,
- ordered_encode],
-
#watchdog{parent = Pid,
transport = start(T, Opts, SvcOpts, Nodes, Dict0, Svc),
tw = proplists:get_value(watchdog_timer,
@@ -153,14 +142,22 @@ i({Ack, T, Pid, {Opts,
?DEFAULT_TW_INIT),
receive_data = RecvData,
dictionary = Dict0,
- config =
- maps:without([traffic_counters | CodecKeys],
- config(SvcOpts#{restrict => restrict(Nodes),
- suspect => 1,
- okay => 3},
- Opts)),
- codec = maps:with(CodecKeys -- [strict_arities],
- SvcOpts#{decode_format := false,
+ config = maps:with([sequence,
+ restrict_connections,
+ restrict,
+ suspect,
+ okay],
+ config(SvcOpts#{restrict => restrict(Nodes),
+ suspect => 1,
+ okay => 3},
+ Opts)),
+ codec = maps:with([decode_format,
+ strict_arities,
+ strict_mbit,
+ string_decode,
+ rfc,
+ ordered_encode],
+ SvcOpts#{decode_format := none,
string_decode := false,
ordered_encode => false})}.
diff --git a/lib/diameter/src/compiler/diameter_dict_util.erl b/lib/diameter/src/compiler/diameter_dict_util.erl
index f9f2b02e94..7b53e51cb6 100644
--- a/lib/diameter/src/compiler/diameter_dict_util.erl
+++ b/lib/diameter/src/compiler/diameter_dict_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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.
@@ -923,7 +923,7 @@ xa([D|_] = Ds, [[Qual, D, {_, Line, AvpName}] | Avps], Dict, Key, Name) ->
store_new({Key, {Name, AvpName}},
[Line, Qual, D],
Dict,
- [Name, Line],
+ [AvpName, Line],
avp_already_referenced),
Key,
Name);
diff --git a/lib/diameter/src/dict/doic_rfc7683.dia b/lib/diameter/src/dict/doic_rfc7683.dia
new file mode 100644
index 0000000000..2b7804115e
--- /dev/null
+++ b/lib/diameter/src/dict/doic_rfc7683.dia
@@ -0,0 +1,50 @@
+;;
+;; %CopyrightBegin%
+;;
+;; Copyright Ericsson AB 2017. All Rights Reserved.
+;;
+;; Licensed under the Apache License, Version 2.0 (the "License");
+;; you may not use this file except in compliance with the License.
+;; You may obtain a copy of the License at
+;;
+;; http://www.apache.org/licenses/LICENSE-2.0
+;;
+;; Unless required by applicable law or agreed to in writing, software
+;; distributed under the License is distributed on an "AS IS" BASIS,
+;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+;; See the License for the specific language governing permissions and
+;; limitations under the License.
+;;
+;; %CopyrightEnd%
+;;
+
+@name diameter_gen_doic_rfc7683
+@prefix diameter_doic
+
+@avp_types
+
+ OC-Supported-Features 621 Grouped -
+ OC-Feature-Vector 622 Unsigned64 -
+ OC-OLR 623 Grouped -
+ OC-Sequence-Number 624 Unsigned64 -
+ OC-Validity-Duration 625 Unsigned32 -
+ OC-Report-Type 626 Enumerated -
+ OC-Reduction-Percentage 627 Unsigned32 -
+
+@enum OC-Report-Type
+
+ HOST_REPORT 0
+ REALM_REPORT 1
+
+@grouped
+
+ OC-Supported-Features ::= < AVP Header: 621 >
+ [ OC-Feature-Vector ]
+ * [ AVP ]
+
+ OC-OLR ::= < AVP Header: 623 >
+ < OC-Sequence-Number >
+ < OC-Report-Type >
+ [ OC-Reduction-Percentage ]
+ [ OC-Validity-Duration ]
+ * [ AVP ]
diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk
index bb3b234d20..bb86de016a 100644
--- a/lib/diameter/src/modules.mk
+++ b/lib/diameter/src/modules.mk
@@ -24,6 +24,7 @@ DICTS = \
base_rfc6733 \
base_accounting \
acct_rfc6733 \
+ doic_rfc7683 \
relay
# The yecc grammar for the dictionary parser.
diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index a0104fac6e..4eb3379d59 100644
--- a/lib/diameter/src/transport/diameter_sctp.erl
+++ b/lib/diameter/src/transport/diameter_sctp.erl
@@ -79,7 +79,7 @@
-type option() :: {sender, boolean()}
| sender
| {packet, boolean() | raw}
- | {message_cb, false | diameter:evaluable()}.
+ | {message_cb, false | diameter:eval()}.
-type uint() :: non_neg_integer().
@@ -102,9 +102,10 @@
streams :: {uint(), uint()} %% {InStream, OutStream} counts
| undefined,
os = 0 :: uint(), %% next output stream
+ rotate = 1 :: boolean() | 0 | 1, %% rotate os?
packet = true :: boolean() %% legacy transport_data?
| raw,
- message_cb = false :: false | diameter:evaluable(),
+ message_cb = false :: false | diameter:eval(),
send = false :: pid() | boolean()}). %% sending process
%% Monitor process state.
@@ -120,7 +121,7 @@
socket :: gen_sctp:sctp_socket(),
service :: pid(), %% service process
pending = {0, queue:new()},
- opts :: [[match()] | boolean() | diameter:evaluable()]}).
+ opts :: [[match()] | boolean() | diameter:eval()]}).
%% Field pending implements two queues: the first of transport-to-be
%% processes to which an association has been assigned but for which
%% diameter hasn't yet spawned a transport process, a short-lived
@@ -156,12 +157,7 @@ start(T, Svc, Opts)
= Svc,
diameter_sctp_sup:start(), %% start supervisors on demand
Addrs = Caps#diameter_caps.host_ip_address,
- s(T, Addrs, Pid, lists:map(fun ip/1, Opts)).
-
-ip({ifaddr, A}) ->
- {ip, A};
-ip(T) ->
- T.
+ s(T, Addrs, Pid, Opts).
%% A listener spawns transports either as a consequence of this call
%% when there is not yet an association to assign it, or at comm_up on
@@ -354,23 +350,35 @@ l([], Ref, T) ->
%% open/3
open(Addrs, Opts, PortNr) ->
- {LAs, Os} = addrs(Addrs, Opts),
- {LAs, case gen_sctp:open(gen_opts(portnr(Os, PortNr))) of
- {ok, Sock} ->
- Sock;
- {error, Reason} ->
- x({open, Reason})
- end}.
+ case gen_sctp:open(gen_opts(portnr(addrs(Addrs, Opts), PortNr))) of
+ {ok, Sock} ->
+ {addrs(Sock), Sock};
+ {error, Reason} ->
+ x({open, Reason})
+ end.
addrs(Addrs, Opts) ->
- case proplists:split(Opts, [ip]) of
- {[[]], _} ->
- {Addrs, Opts ++ [{ip, A} || A <- Addrs]};
- {[As], Os} ->
- LAs = [diameter_lib:ipaddr(A) || {ip, A} <- As],
- {LAs, Os ++ [{ip, A} || A <- LAs]}
+ case lists:mapfoldl(fun ipaddr/2, false, Opts) of
+ {Os, true} ->
+ Os;
+ {_, false} ->
+ Opts ++ [{ip, A} || A <- Addrs]
end.
+ipaddr({K,A}, _)
+ when K == ifaddr;
+ K == ip ->
+ {{ip, ipaddr(A)}, true};
+ipaddr(T, B) ->
+ {T, B}.
+
+ipaddr(A)
+ when A == loopback;
+ A == any ->
+ A;
+ipaddr(A) ->
+ diameter_lib:ipaddr(A).
+
portnr(Opts, PortNr) ->
case proplists:get_value(port, Opts) of
undefined ->
@@ -379,6 +387,14 @@ portnr(Opts, PortNr) ->
Opts
end.
+addrs(Sock) ->
+ case inet:socknames(Sock) of
+ {ok, As} ->
+ [A || {A,_} <- As];
+ {error, Reason} ->
+ x({socknames, Reason})
+ end.
+
%% x/1
x(Reason) ->
@@ -677,11 +693,16 @@ send(#diameter_packet{transport_data = {outstream, SId}}
= S) ->
send(SId rem OS, Msg, S);
-%% ... or not: rotate through all streams.
-send(Msg, #transport{streams = {_, OS},
+%% ... or not: rotate when sending on multiple streams ...
+send(Msg, #transport{rotate = true,
+ streams = {_, OS},
os = N}
= S) ->
- send(N, Msg, S#transport{os = (N + 1) rem OS}).
+ send(N, Msg, S#transport{os = (N + 1) rem OS});
+
+%% ... or send on the only stream available.
+send(Msg, S) ->
+ send(0, Msg, S).
%% send/3
@@ -749,7 +770,7 @@ recv({[#sctp_sndrcvinfo{assoc_id = Id}], _Bin}
%% Inbound Diameter message.
recv({[#sctp_sndrcvinfo{}], Bin} = Msg, S)
when is_binary(Bin) ->
- message(recv, Msg, S);
+ message(recv, Msg, recv(S));
recv({_, #sctp_shutdown_event{}}, _) ->
stop;
@@ -769,6 +790,25 @@ recv({_, #sctp_paddr_change{}}, _) ->
recv({_, #sctp_pdapi_event{}}, _) ->
ok.
+%% recv/1
+%%
+%% Start sending unordered after the second reception, so that an
+%% outgoing CER/CEA will arrive at the peer before another request.
+
+recv(#transport{rotate = B} = S)
+ when is_boolean(B) ->
+ S;
+
+recv(#transport{rotate = 0, streams = {_,N}, socket = Sock} = S) ->
+ ok = inet:setopts(Sock, [{sctp_default_send_param,
+ #sctp_sndrcvinfo{flags = [unordered]}}]),
+ S#transport{rotate = 1 < N};
+
+recv(#transport{rotate = N} = S) ->
+ S#transport{rotate = N-1}.
+
+%% publish/4
+
publish(T, Ref, Id, Sock) ->
true = diameter_reg:add_new({?MODULE, T, {Ref, {Id, Sock}}}),
putr(?INFO_KEY, {gen_sctp, Sock}). %% for info/1
diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl
index 5d7bca059a..a8639baa11 100644
--- a/lib/diameter/src/transport/diameter_tcp.erl
+++ b/lib/diameter/src/transport/diameter_tcp.erl
@@ -110,7 +110,7 @@
-type option() :: {port, non_neg_integer()}
| {sender, boolean()}
| sender
- | {message_cb, false | diameter:evaluable()}
+ | {message_cb, false | diameter:eval()}
| {fragment_timer, 0..16#FFFFFFFF}.
%% Accepting/connecting transport process state.
@@ -125,7 +125,7 @@
timeout :: infinity | 0..16#FFFFFFFF, %% fragment timeout
tref = false :: false | reference(), %% fragment timer reference
flush = false :: boolean(), %% flush fragment at timeout?
- message_cb :: false | diameter:evaluable(),
+ message_cb :: false | diameter:eval(),
send :: pid() | false}). %% sending process
%% The usual transport using gen_tcp can be replaced by anything
@@ -142,8 +142,7 @@
-> {ok, pid(), [inet:ip_address()]}
when Ref :: diameter:transport_ref();
({connect, Ref}, #diameter_service{}, [connect_option()])
- -> {ok, pid(), [inet:ip_address()]}
- | {ok, pid()}
+ -> {ok, pid()}
when Ref :: diameter:transport_ref().
start({T, Ref}, Svc, Opts) ->
@@ -258,22 +257,14 @@ i(#monitor{parent = Pid, transport = TPid} = S) ->
i({listen, Ref, {Mod, Opts, Addrs}}) ->
[_] = diameter_config:subscribe(Ref, transport), %% assert existence
- {[LA, LP], Rest} = proplists:split(Opts, [ip, port]),
- LAddrOpt = get_addr(LA, Addrs),
- LPort = get_port(LP),
- {ok, LSock} = Mod:listen(LPort, gen_opts(LAddrOpt, Rest)),
- LAddr = laddr(LAddrOpt, Mod, LSock),
+ {[LP], Rest} = proplists:split(Opts, [port]),
+ {ok, LSock} = Mod:listen(get_port(LP), gen_opts(Addrs, Rest)),
+ {ok, {LAddr, _}} = sockname(Mod, LSock),
true = diameter_reg:add_new({?MODULE, listener, {Ref, {LAddr, LSock}}}),
proc_lib:init_ack({ok, self(), {LAddr, LSock}}),
#listener{socket = LSock,
module = Mod}.
-laddr([], Mod, Sock) ->
- {ok, {Addr, _Port}} = sockname(Mod, Sock),
- Addr;
-laddr([{ip, Addr}], _, _) ->
- Addr.
-
ssl_opts([]) ->
false;
ssl_opts([{ssl_options, true}]) ->
@@ -308,24 +299,16 @@ init(accept = T, Ref, Mod, Pid, Opts, Addrs, SvcPid) ->
Sock;
init(connect = T, Ref, Mod, Pid, Opts, Addrs, _SvcPid) ->
- {[LA, RA, RP], Rest} = proplists:split(Opts, [ip, raddr, rport]),
- LAddrOpt = get_addr(LA, Addrs),
+ {[RA, RP], Rest} = proplists:split(Opts, [raddr, rport]),
RAddr = get_addr(RA),
RPort = get_port(RP),
- proc_lib:init_ack(init_rc(LAddrOpt)),
- Sock = ok(connect(Mod, RAddr, RPort, gen_opts(LAddrOpt, Rest))),
+ proc_lib:init_ack({ok, self()}),
+ Sock = ok(connect(Mod, RAddr, RPort, gen_opts(Addrs, Rest))),
publish(Mod, T, Ref, Sock),
- up(Pid, {RAddr, RPort}, LAddrOpt, Mod, Sock),
+ up(Pid, {RAddr, RPort}, Mod, Sock),
Sock.
-init_rc([{ip, Addr}]) ->
- {ok, self(), [Addr]};
-init_rc([]) ->
- {ok, self()}.
-
-up(Pid, Remote, [{ip, _Addr}], _, _) ->
- diameter_peer:up(Pid, Remote);
-up(Pid, Remote, [], Mod, Sock) ->
+up(Pid, Remote, Mod, Sock) ->
{Addr, _Port} = ok(sockname(Mod, Sock)),
diameter_peer:up(Pid, Remote, [Addr]).
@@ -382,25 +365,41 @@ l([{{?MODULE, listener, {_, AS}}, LPid}], _, _) ->
l([], Ref, T) ->
diameter_tcp_sup:start_child({listen, Ref, T}).
-%% get_addr/1
+%% addrs/2
+%%
+%% Take the first address from the service if several are specified
+%% and not address is configured.
+
+addrs(Addrs, Opts) ->
+ case lists:mapfoldr(fun ipaddr/2, [], Opts) of
+ {Os, [_]} ->
+ Os;
+ {_, []} ->
+ Opts ++ [{ip, A} || [A|_] <- [Addrs]];
+ {_, As} ->
+ ?ERROR({invalid_addrs, As, Addrs})
+ end.
-get_addr(As) ->
- diameter_lib:ipaddr(addr(As, [])).
+ipaddr({K,A}, As)
+ when K == ifaddr;
+ K == ip ->
+ {{ip, ipaddr(A)}, [A | As]};
+ipaddr(T, B) ->
+ {T, B}.
-%% get_addr/2
+ipaddr(A)
+ when A == loopback;
+ A == any ->
+ A;
+ipaddr(A) ->
+ diameter_lib:ipaddr(A).
-get_addr([], []) ->
- [];
-get_addr(As, Def) ->
- [{ip, diameter_lib:ipaddr(addr(As, Def))}].
+%% get_addr/1
-%% Take the first address from the service if several are unspecified.
-addr([], [Addr | _]) ->
- Addr;
-addr([{_, Addr}], _) ->
- Addr;
-addr(As, Addrs) ->
- ?ERROR({invalid_addrs, As, Addrs}).
+get_addr([{_, Addr}]) ->
+ diameter_lib:ipaddr(Addr);
+get_addr(Addrs) ->
+ ?ERROR({invalid_addrs, Addrs}).
%% get_port/1
@@ -413,10 +412,15 @@ get_port(Ps) ->
%% gen_opts/2
-gen_opts(LAddrOpt, Opts) ->
+gen_opts(Addrs, Opts) ->
+ gen_opts(addrs(Addrs, Opts)).
+
+%% gen_opts/1
+
+gen_opts(Opts) ->
{L,_} = proplists:split(Opts, [binary, packet, active]),
[[],[],[]] == L orelse ?ERROR({reserved_options, Opts}),
- [binary, {packet, 0}, {active, false}] ++ LAddrOpt ++ Opts.
+ [binary, {packet, 0}, {active, false} | Opts].
%% ---------------------------------------------------------------------------
%% # ports/1
diff --git a/lib/diameter/test/diameter_codec_SUITE.erl b/lib/diameter/test/diameter_codec_SUITE.erl
index c79b642c09..17112794e4 100644
--- a/lib/diameter/test/diameter_codec_SUITE.erl
+++ b/lib/diameter/test/diameter_codec_SUITE.erl
@@ -291,7 +291,7 @@ recode(Msg, Dict) ->
recode(#diameter_packet{msg = Msg}, Dict).
opts(Mod) ->
- #{dictionary => Mod,
+ #{app_dictionary => Mod,
decode_format => record,
string_decode => false,
strict_mbit => true,
diff --git a/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl b/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl
index 735339ebb9..c6bba75f09 100644
--- a/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl
+++ b/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl
@@ -77,7 +77,7 @@ dec('BR', #diameter_packet
ok.
opts(Mod) ->
- #{dictionary => Mod,
+ #{app_dictionary => Mod,
decode_format => record,
string_decode => true,
strict_mbit => true,
diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl
index 22fb0550ea..70e910ffa6 100644
--- a/lib/diameter/test/diameter_codec_test.erl
+++ b/lib/diameter/test/diameter_codec_test.erl
@@ -44,7 +44,8 @@ base() ->
[] = run([[fun base/1, T] || T <- [zero, decode]]).
gen(Mod) ->
- Fs = [{Mod, F, []} || F <- [name, id, vendor_id, vendor_name]],
+ Fs = [{Mod, F, []} || Mod /= diameter_gen_doic_rfc7683,
+ F <- [name, id, vendor_id, vendor_name]],
[] = run(Fs ++ [[fun gen/2, Mod, T] || T <- [messages,
command_codes,
avp_types,
@@ -216,7 +217,7 @@ avp(Mod, encode = X, V, Name, _) ->
opts(Mod) ->
(opts())#{module => Mod,
- dictionary => Mod}.
+ app_dictionary => Mod}.
opts() ->
#{decode_format => record,
diff --git a/lib/diameter/test/diameter_event_SUITE.erl b/lib/diameter/test/diameter_event_SUITE.erl
index 57d3427037..a291dde6be 100644
--- a/lib/diameter/test/diameter_event_SUITE.erl
+++ b/lib/diameter/test/diameter_event_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2013-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.
@@ -63,7 +63,8 @@
{'Host-IP-Address', [?ADDR]},
{'Vendor-Id', 12345},
{'Product-Name', "OTP/diameter"},
- {'Acct-Application-Id', [D:id() || D <- Dicts]}
+ {'Acct-Application-Id', [D:id() || D <- Dicts]},
+ {decode_format, map}
| [{application, [{dictionary, D},
{module, #diameter_callback{}}]}
|| D <- Dicts]]).
@@ -111,7 +112,8 @@ up(Config) ->
{Svc, Ref} = connect(Config, [{connect_timer, 5000},
{watchdog_timer, 15000}]),
start = event(Svc),
- {up, Ref, {TPid, Caps}, Cfg, #diameter_packet{}} = event(Svc),
+ {up, Ref, {TPid, Caps}, Cfg, #diameter_packet{msg = M}} = event(Svc),
+ ['CEA' | #{}] = M, %% assert
{watchdog, Ref, _, {initial, okay}, _} = event(Svc),
%% Kill the transport process and see that the connection is
%% reestablished after a watchdog timeout, not after connect_timer
@@ -131,8 +133,9 @@ down(Config) ->
{connect_timer, 5000},
{watchdog_timer, 20000}]),
start = event(Svc),
- {closed, Ref, {'CEA', ?NO_COMMON_APP, _, #diameter_packet{}}, _}
+ {closed, Ref, {'CEA', ?NO_COMMON_APP, _, #diameter_packet{msg = M}}, _}
= event(Svc),
+ ['CEA' | #{}] = M, %% assert
{reconnect, Ref, _} = event(Svc, 4000, 10000).
%% Connect with matching capabilities but have the server delay its
diff --git a/lib/diameter/test/diameter_examples_SUITE.erl b/lib/diameter/test/diameter_examples_SUITE.erl
index eb99f10fe6..ee44ed8dc9 100644
--- a/lib/diameter/test/diameter_examples_SUITE.erl
+++ b/lib/diameter/test/diameter_examples_SUITE.erl
@@ -344,7 +344,7 @@ top(Dir, LibDir) ->
start({server, Prot}) ->
ok = diameter:start(),
ok = server:start(),
- {ok, Ref} = server:listen(Prot),
+ {ok, Ref} = server:listen({Prot, any, 3868}),
[_] = ?util:lport(Prot, Ref),
ok;
@@ -352,7 +352,7 @@ start({client = Svc, Prot}) ->
ok = diameter:start(),
true = diameter:subscribe(Svc),
ok = client:start(),
- {ok, Ref} = client:connect(Prot),
+ {ok, Ref} = client:connect({Prot, loopback, loopback, 3868}),
receive #diameter_event{info = {up, Ref, _, _, _}} -> ok end;
start(Config) ->
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index 8b0ce9710a..ffb4a508cd 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -20,6 +20,7 @@
%%
%% Tests of traffic between two Diameter nodes, one client, one server.
+%% The traffic isn't meant to be sensible, just to exercise code.
%%
-module(diameter_traffic_SUITE).
@@ -49,6 +50,7 @@
send_protocol_error/1,
send_experimental_result/1,
send_arbitrary/1,
+ send_proxy_info/1,
send_unknown/1,
send_unknown_short/1,
send_unknown_mandatory/1,
@@ -114,6 +116,8 @@
%% diameter_{tcp,sctp} callbacks
-export([message/3]).
+-include_lib("kernel/include/inet_sctp.hrl").
+
-include("diameter.hrl").
-include("diameter_gen_base_rfc3588.hrl").
-include("diameter_gen_base_accounting.hrl").
@@ -135,6 +139,7 @@
-define(A, list_to_atom).
-define(L, atom_to_list).
+-define(B, iolist_to_binary).
%% Don't use is_record/2 since dictionary hrl's aren't included.
%% (Since they define conflicting records with the same names.)
@@ -154,7 +159,7 @@
-define(ENCODINGS, [list, record, map]).
%% How to decode incoming messages.
--define(DECODINGS, [record, false, map, list, record_from_map]).
+-define(DECODINGS, [record, none, map, list, record_from_map]).
%% Which dictionary to use in the clients.
-define(RFCS, [rfc3588, rfc6733, rfc4005]).
@@ -213,6 +218,7 @@
{'Acct-Application-Id', [3]}, %% base accounting
{restrict_connections, false},
{string_decode, Grp#group.strings},
+ {avp_dictionaries, [diameter_gen_doic_rfc7683]},
{incoming_maxlen, 1 bsl 21}
| [{application, [{dictionary, D},
{module, [?MODULE, Grp]},
@@ -387,6 +393,16 @@ select(T) ->
%% --------------------
+%% Work around common_test accumulating Config improperly, causing
+%% testcases to get Config from groups and suites they're not in.
+init_per_testcase(N, Config)
+ when N == rfc4005;
+ N == start;
+ N == result_codes;
+ N == empty;
+ N == stop ->
+ Config;
+
%% Skip testcases that can reasonably fail under SCTP.
init_per_testcase(Name, Config) ->
TCs = proplists:get_value(runlist, Config, []),
@@ -409,6 +425,9 @@ end_per_testcase(_, _) ->
ok.
%% replace/2
+%%
+%% Work around common_test running init functions inappropriately, and
+%% this accumulating more config than expected.
replace(Pairs, Config)
when is_list(Pairs) ->
@@ -429,6 +448,7 @@ tc() ->
send_protocol_error,
send_experimental_result,
send_arbitrary,
+ send_proxy_info,
send_unknown,
send_unknown_short,
send_unknown_mandatory,
@@ -494,6 +514,7 @@ start_services(Config) ->
| ?SERVICE(SN, Grp)]),
ok = diameter:start_service(CN, [{traffic_counters, bool()},
{sequence, ?CLIENT_MASK},
+ {decode_format, map},
{strict_arities, decode}
| ?SERVICE(CN, Grp)]).
@@ -520,7 +541,7 @@ add_transports(Config) ->
| server_apps()]
++ [{spawn_opt, {erlang, spawn, []}} || CS]),
Cs = [?util:connect(CN,
- [T, {sender, CS}],
+ [T, {sender, CS} | client_opts(T)],
LRef,
[{id, Id}
| client_apps(R, [{'Origin-State-Id', origin(Id)}])])
@@ -530,6 +551,14 @@ add_transports(Config) ->
Id <- [{D,E}]],
?util:write_priv(Config, "transport", [LRef | Cs]).
+client_opts(tcp) ->
+ [];
+client_opts(sctp) ->
+ [{sctp_initmsg, #sctp_initmsg{num_ostreams = N,
+ max_instreams = 5}}
+ || N <- [rand:uniform(8)],
+ N =< 6].
+
server_apps() ->
B = have_nas(),
[{applications, [diameter_gen_base_rfc3588,
@@ -611,7 +640,6 @@ result_codes(_Config) ->
send_ok(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 1}],
-
['ACA' | #{'Result-Code' := ?SUCCESS,
'Session-Id' := _}]
= call(Config, Req).
@@ -643,13 +671,80 @@ send_bad_answer(Config) ->
= call(Config, Req).
%% Send an ACR that the server callback answers explicitly with a
-%% protocol error.
+%% protocol error and some AVPs to check the decoding of.
send_protocol_error(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 4}],
- ?answer_message(?TOO_BUSY)
- = call(Config, Req).
+ ['answer-message' | #{'Result-Code' := ?TOO_BUSY,
+ 'AVP' := [OLR | _]} = Avps]
+ = call(Config, Req),
+
+ #diameter_avp{name = 'OC-OLR',
+ value = #{'OC-Sequence-Number' := 1,
+ 'OC-Report-Type' := 0, %% HOST_REPORT
+ 'OC-Reduction-Percentage' := [25],
+ 'OC-Validity-Duration' := [60],
+ 'AVP' := [OSF]}}
+ = OLR,
+ #diameter_avp{name = 'OC-Supported-Features',
+ value = #{} = Fs}
+ = OSF,
+ 0 = maps:size(Fs),
+
+ #group{client_dict = D} = group(Config),
+
+ if D == nas4005 ->
+ error = maps:find('Failed-AVP', Avps),
+ #{'AVP' := [_,Failed]}
+ = Avps,
+ #diameter_avp{name = 'Failed-AVP',
+ value = #{'AVP' := [NP,FR,AP]}}
+ = Failed,
+ #diameter_avp{name = 'NAS-Port',
+ value = 44}
+ = NP,
+ #diameter_avp{name = 'Firmware-Revision',
+ value = 12}
+ = FR,
+ #diameter_avp{name = 'Auth-Grace-Period',
+ value = 13}
+ = AP;
+
+ D == diameter_gen_base_rfc3588;
+ D == diameter_gen_basr_accounting ->
+ error = maps:find('Failed-AVP', Avps),
+ #{'AVP' := [_,Failed]}
+ = Avps,
+
+ #diameter_avp{name = 'Failed-AVP',
+ value = #{'AVP' := [NP,FR,AP]}}
+ = Failed,
+ #diameter_avp{name = undefined,
+ value = undefined}
+ = NP,
+ #diameter_avp{name = 'Firmware-Revision',
+ value = 12}
+ = FR,
+ #diameter_avp{name = 'Auth-Grace-Period',
+ value = 13}
+ = AP;
+
+ D == diameter_gen_base_rfc6733;
+ D == diameter_gen_acct_rfc6733 ->
+ #{'Failed-AVP' := [#{'AVP' := [NP,FR,AP]}],
+ 'AVP' := [_]}
+ = Avps,
+ #diameter_avp{name = undefined,
+ value = undefined}
+ = NP,
+ #diameter_avp{name = 'Firmware-Revision',
+ value = 12}
+ = FR,
+ #diameter_avp{name = 'Auth-Grace-Period',
+ value = 13}
+ = AP
+ end.
%% Send a 3xxx Experimental-Result in an answer not setting the E-bit
%% and missing a Result-Code.
@@ -671,6 +766,19 @@ send_arbitrary(Config) ->
= call(Config, Req),
"XXX" = string(V, Config).
+%% Send Proxy-Info in an ASR that the peer answers with 3xxx, and
+%% ensure that the AVP is returned.
+send_proxy_info(Config) ->
+ H0 = ?B(?util:unique_string()),
+ S0 = ?B(?util:unique_string()),
+ Req = ['ASR', {'Proxy-Info', #{'Proxy-Host' => H0,
+ 'Proxy-State' => S0}}],
+ ['answer-message' | #{'Result-Code' := 3999,
+ 'Proxy-Info' := [#{'Proxy-Host' := H,
+ 'Proxy-State' := S}]}]
+ = call(Config, Req),
+ [H0, S0] = [?B(X) || X <- [H,S]].
+
%% Send an unknown AVP (to some client) and check that it comes back.
send_unknown(Config) ->
Req = ['ASR', {'AVP', [#diameter_avp{code = 999,
@@ -694,12 +802,12 @@ send_unknown_short(Config, M, RC) ->
data = <<17>>}]}],
['ASA' | #{'Session-Id' := _,
'Result-Code' := RC,
- 'Failed-AVP' := Avps}]
+ 'Failed-AVP' := [#{'AVP' := [Avp]}]}]
= call(Config, Req),
- [[#diameter_avp{code = 999,
- is_mandatory = M,
- data = <<17, _/binary>>}]] %% extra bits from padding
- = failed_avps(Avps, Config).
+ #diameter_avp{code = 999,
+ is_mandatory = M,
+ data = <<17, _/binary>>} %% extra bits from padding
+ = Avp.
%% Ditto but set the M flag.
send_unknown_mandatory(Config) ->
@@ -708,12 +816,12 @@ send_unknown_mandatory(Config) ->
data = <<17>>}]}],
['ASA' | #{'Session-Id' := _,
'Result-Code' := ?AVP_UNSUPPORTED,
- 'Failed-AVP' := Avps}]
+ 'Failed-AVP' := [#{'AVP' := [Avp]}]}]
= call(Config, Req),
- [[#diameter_avp{code = 999,
- is_mandatory = true,
- data = <<17>>}]]
- = failed_avps(Avps, Config).
+ #diameter_avp{code = 999,
+ is_mandatory = true,
+ data = <<17>>}
+ = Avp.
%% Ditto, and point the AVP length past the end of the message. Expect
%% 5014 instead of 5001.
@@ -728,13 +836,13 @@ send_unexpected_mandatory_decode(Config) ->
data = <<12:32>>}]}],
['ASA' | #{'Session-Id' := _,
'Result-Code' := ?AVP_UNSUPPORTED,
- 'Failed-AVP' := Avps}]
+ 'Failed-AVP' := [#{'AVP' := [Avp]}]}]
= call(Config, Req),
- [[#diameter_avp{code = 27,
- is_mandatory = true,
- value = 12,
- data = <<12:32>>}]]
- = failed_avps(Avps, Config).
+ #diameter_avp{code = 27,
+ is_mandatory = true,
+ value = 12,
+ data = <<12:32>>}
+ = Avp.
%% Try to two Auth-Application-Id in ASR expect 5009.
send_too_many(Config) ->
@@ -742,11 +850,11 @@ send_too_many(Config) ->
['ASA' | #{'Session-Id' := _,
'Result-Code' := ?TOO_MANY,
- 'Failed-AVP' := Avps}]
+ 'Failed-AVP' := [#{'AVP' := [Avp]}]}]
= call(Config, Req),
- [[#diameter_avp{name = 'Auth-Application-Id',
- value = 44}]]
- = failed_avps(Avps, Config).
+ #diameter_avp{name = 'Auth-Application-Id',
+ value = 44}
+ = Avp.
%% Send an containing a faulty Grouped AVP (empty Proxy-Host in
%% Proxy-Info) and expect that only the faulty AVP is sent in
@@ -758,12 +866,11 @@ send_grouped_error(Config) ->
{'Proxy-State', ""}]]}],
['ASA' | #{'Session-Id' := _,
'Result-Code' := ?INVALID_AVP_LENGTH,
- 'Failed-AVP' := Avps}]
+ 'Failed-AVP' := [#{'AVP' := [Avp]}]}]
= call(Config, Req),
- [[#diameter_avp{name = 'Proxy-Info', value = V}]]
- = failed_avps(Avps, Config),
- {Empty, undefined, []} = proxy_info(V, Config),
- <<0>> = iolist_to_binary(Empty).
+ #diameter_avp{name = 'Proxy-Info', value = #{'Proxy-Host' := H}}
+ = Avp,
+ <<0>> = ?B(H).
%% Send an STR that the server ignores.
send_noreply(Config) ->
@@ -816,9 +923,8 @@ send_invalid_avp_length(Config) ->
'Result-Code' := ?INVALID_AVP_LENGTH,
'Origin-Host' := _,
'Origin-Realm' := _,
- 'Failed-AVP' := Avps}]
- = call(Config, Req),
- [[_]] = failed_avps(Avps, Config).
+ 'Failed-AVP' := [#{'AVP' := [_]}]}]
+ = call(Config, Req).
%% Send a request containing 5xxx errors that the server rejects with
%% 3xxx.
@@ -1029,29 +1135,6 @@ send_anything(Config) ->
%% ===========================================================================
-failed_avps(Avps, Config) ->
- #group{client_dict = D} = proplists:get_value(group, Config),
- [failed_avp(D, T) || T <- Avps].
-
-failed_avp(nas4005, {'nas_Failed-AVP', As}) ->
- As;
-failed_avp(_, #'diameter_base_Failed-AVP'{'AVP' = As}) ->
- As.
-
-proxy_info(Rec, Config) ->
- #group{client_dict = D} = proplists:get_value(group, Config),
- if D == nas4005 ->
- {'nas_Proxy-Info', H, S, As}
- = Rec,
- {H,S,As};
- true ->
- #'diameter_base_Proxy-Info'{'Proxy-Host' = H,
- 'Proxy-State' = S,
- 'AVP' = As}
- = Rec,
- {H,S,As}
- end.
-
group(Config) ->
#group{} = proplists:get_value(group, Config).
@@ -1092,12 +1175,12 @@ origin(N) ->
decode(record) -> 0;
decode(list) -> 1;
decode(map) -> 2;
-decode(false) -> 3;
+decode(none) -> 3;
decode(record_from_map) -> 4;
decode(0) -> record;
decode(1) -> list;
decode(2) -> map;
-decode(3) -> false;
+decode(3) -> none;
decode(4) -> record_from_map.
encode(record) -> 0;
@@ -1144,16 +1227,18 @@ to_map(#diameter_packet{header = H, msg = Rec},
%% No record decode: do it ourselves.
to_map(#diameter_packet{header = H,
- msg = false,
+ msg = Name,
bin = Bin},
- #group{server_decoding = false,
+ #group{server_decoding = none,
strings = B}) ->
Opts = #{decode_format => map,
string_decode => B,
+ avp_dictionaries => [diameter_gen_doic_rfc7683],
strict_mbit => true,
rfc => 6733},
- #diameter_packet{msg = [_MsgName | _Map] = Msg}
+ #diameter_packet{msg = [MsgName | _Map] = Msg}
= diameter_codec:decode(dict(H), Opts, Bin),
+ {MsgName, _} = {Name, Msg}, %% assert
Msg.
dict(#diameter_header{application_id = Id,
@@ -1521,24 +1606,23 @@ answer(Pkt, Req, _Peer, Name, #group{client_dict = Dict0}) ->
#diameter_packet{header = H, msg = Ans, errors = Es} = Pkt,
ApplId = app(Req, Name, Dict0),
#diameter_header{application_id = ApplId} = H, %% assert
- Dict = dict(Ans, Dict0),
- rec_to_map(answer(Ans, Es, Name), Dict).
+ answer(Ans, Es, Name).
%% Missing Result-Code and inappropriate Experimental-Result-Code.
-answer(Rec, Es, send_experimental_result) ->
+answer(Ans, Es, send_experimental_result) ->
[{5004, #diameter_avp{name = 'Experimental-Result'}},
{5005, #diameter_avp{name = 'Result-Code'}}]
= Es,
- Rec;
+ Ans;
%% An inappropriate E-bit results in a decode error ...
-answer(Rec, Es, send_bad_answer) ->
+answer(Ans, Es, send_bad_answer) ->
[{5004, #diameter_avp{name = 'Result-Code'}} | _] = Es,
- Rec;
+ Ans;
%% ... while other errors are reflected in Failed-AVP.
-answer(Rec, [], _) ->
- Rec.
+answer(Ans, [], _) ->
+ Ans.
app(_, send_unsupported_app, _) ->
?BAD_APP;
@@ -1715,11 +1799,33 @@ request(['ACR' | #{'Session-Id' := SId,
request(['ACR' | #{'Accounting-Record-Number' := 4}],
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
+ %% Include a DOIC AVP that will be encoded/decoded because of
+ %% avp_dictionaries config.
+ OLR = #{'OC-Sequence-Number' => 1,
+ 'OC-Report-Type' => 0, %% HOST_REPORT
+ 'OC-Reduction-Percentage' => [25],
+ 'OC-Validity-Duration' => [60],
+ 'AVP' => [{'OC-Supported-Features', []}]},
+ %% Include a NAS Failed-AVP AVP that will only be decoded under
+ %% that application. Encode as 'AVP' since RFC 3588 doesn't list
+ %% Failed-AVP in the answer-message grammar while RFC 6733 does.
+ NP = #diameter_avp{data = {nas4005, 'NAS-Port', 44}},
+ FR = #diameter_avp{name = 'Firmware-Revision', value = 12}, %% M=0
+ AP = #diameter_avp{name = 'Auth-Grace-Period', value = 13}, %% M=1
+ Failed = #diameter_avp{data = {diameter_gen_base_rfc3588,
+ 'Failed-AVP',
+ [{'AVP', [NP,FR,AP]}]}},
Ans = ['answer-message', {'Result-Code', ?TOO_BUSY},
{'Origin-Host', OH},
- {'Origin-Realm', OR}],
+ {'Origin-Realm', OR},
+ {'AVP', [{'OC-OLR', OLR}, Failed]}],
{reply, Ans};
+%% send_proxy_info
+request(['ASR' | #{'Proxy-Info' := _}],
+ _) ->
+ {protocol_error, 3999};
+
request(['ASR' | #{'Session-Id' := SId} = Avps],
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
diff --git a/lib/diameter/test/diameter_transport_SUITE.erl b/lib/diameter/test/diameter_transport_SUITE.erl
index 9d981d0a2b..284d2b9566 100644
--- a/lib/diameter/test/diameter_transport_SUITE.erl
+++ b/lib/diameter/test/diameter_transport_SUITE.erl
@@ -349,35 +349,40 @@ rand_bytes(N) ->
%% start_connect/3
start_connect(Prot, PortNr, Ref) ->
- {ok, TPid, [?ADDR]} = start_connect(Prot,
- {connect, Ref},
- ?SVC([]),
- [{raddr, ?ADDR},
- {rport, PortNr},
- {ip, ?ADDR},
- {port, 0}]),
- ?RECV(?TMSG({TPid, connected, _})),
+ {ok, TPid} = start_connect(Prot,
+ {connect, Ref},
+ ?SVC([]),
+ [{raddr, ?ADDR},
+ {rport, PortNr},
+ {ip, ?ADDR},
+ {port, 0}]),
+ connected(Prot, TPid),
TPid.
+connected(sctp, TPid) ->
+ ?RECV(?TMSG({TPid, connected, _}));
+connected(tcp, TPid) ->
+ ?RECV(?TMSG({TPid, connected, _, [?ADDR]})).
+
start_connect(sctp, T, Svc, Opts) ->
- diameter_sctp:start(T, Svc, [{sctp_initmsg, ?SCTP_INIT} | Opts]);
+ {ok, TPid, [?ADDR]}
+ = diameter_sctp:start(T, Svc, [{sctp_initmsg, ?SCTP_INIT} | Opts]),
+ {ok, TPid};
start_connect(tcp, T, Svc, Opts) ->
diameter_tcp:start(T, Svc, Opts).
%% start_accept/2
start_accept(Prot, Ref) ->
- {Mod, Opts} = tmod(Prot),
- {ok, TPid, [?ADDR]} = Mod:start({accept, Ref},
- ?SVC([?ADDR]),
- [{port, 0} | Opts]),
+ {ok, TPid, [?ADDR]}
+ = start_accept(Prot, {accept, Ref}, ?SVC([?ADDR]), [{port, 0}]),
?RECV(?TMSG({TPid, connected})),
TPid.
-tmod(sctp) ->
- {diameter_sctp, [{sctp_initmsg, ?SCTP_INIT}]};
-tmod(tcp) ->
- {diameter_tcp, []}.
+start_accept(sctp, T, Svc, Opts) ->
+ diameter_sctp:start(T, Svc, [{sctp_initmsg, ?SCTP_INIT} | Opts]);
+start_accept(tcp, T, Svc, Opts) ->
+ diameter_tcp:start(T, Svc, Opts).
%% ===========================================================================
diff --git a/lib/kernel/src/user_drv.erl b/lib/kernel/src/user_drv.erl
index b794d4f45e..99ea4210bd 100644
--- a/lib/kernel/src/user_drv.erl
+++ b/lib/kernel/src/user_drv.erl
@@ -175,6 +175,18 @@ server_loop(Iport, Oport, Curr, User, Gr, {Resp, IOQ} = IOQueue) ->
{Iport,eof} ->
Curr ! {self(),eof},
server_loop(Iport, Oport, Curr, User, Gr, IOQueue);
+
+ %% We always handle geometry and unicode requests
+ {Requester,tty_geometry} ->
+ Requester ! {self(),tty_geometry,get_tty_geometry(Iport)},
+ server_loop(Iport, Oport, Curr, User, Gr, IOQueue);
+ {Requester,get_unicode_state} ->
+ Requester ! {self(),get_unicode_state,get_unicode_state(Iport)},
+ server_loop(Iport, Oport, Curr, User, Gr, IOQueue);
+ {Requester,set_unicode_state, Bool} ->
+ Requester ! {self(),set_unicode_state,set_unicode_state(Iport,Bool)},
+ server_loop(Iport, Oport, Curr, User, Gr, IOQueue);
+
Req when element(1,Req) =:= User orelse element(1,Req) =:= Curr,
tuple_size(Req) =:= 2 orelse tuple_size(Req) =:= 3 ->
%% We match {User|Curr,_}|{User|Curr,_,_}
@@ -224,21 +236,16 @@ server_loop(Iport, Oport, Curr, User, Gr, {Resp, IOQ} = IOQueue) ->
_ -> % not current, just remove it
server_loop(Iport, Oport, Curr, User, gr_del_pid(Gr, Pid), IOQueue)
end;
+ {Requester, {put_chars_sync, _, _, Reply}} ->
+ %% We need to ack the Req otherwise originating process will hang forever
+ %% Do discard the output to non visible shells (as was done previously)
+ Requester ! {reply, Reply},
+ server_loop(Iport, Oport, Curr, User, Gr, IOQueue);
_X ->
- %% Ignore unknown messages.
- server_loop(Iport, Oport, Curr, User, Gr, IOQueue)
+ %% Ignore unknown messages.
+ server_loop(Iport, Oport, Curr, User, Gr, IOQueue)
end.
-%% We always handle geometry and unicode requests
-handle_req({Curr,tty_geometry},Iport,_Oport,IOQueue) ->
- Curr ! {self(),tty_geometry,get_tty_geometry(Iport)},
- IOQueue;
-handle_req({Curr,get_unicode_state},Iport,_Oport,IOQueue) ->
- Curr ! {self(),get_unicode_state,get_unicode_state(Iport)},
- IOQueue;
-handle_req({Curr,set_unicode_state, Bool},Iport,_Oport,IOQueue) ->
- Curr ! {self(),set_unicode_state,set_unicode_state(Iport,Bool)},
- IOQueue;
handle_req(next,Iport,Oport,{false,IOQ}=IOQueue) ->
case queue:out(IOQ) of
{empty,_} ->
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index 2749feb1eb..db415a3666 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -32,7 +32,7 @@
-include("ssl_record.hrl").
-include("ssl_internal.hrl").
--export([decode/1, alert_txt/1, reason_code/2]).
+-export([decode/1, own_alert_txt/1, alert_txt/1, reason_code/2]).
%%====================================================================
%% Internal application API
@@ -60,12 +60,28 @@ reason_code(#alert{description = Description}, _) ->
{tls_alert, string:to_lower(description_txt(Description))}.
%%--------------------------------------------------------------------
+-spec own_alert_txt(#alert{}) -> string().
+%%
+%% Description: Returns the error string for given alert generated
+%% by the erlang implementation.
+%%--------------------------------------------------------------------
+own_alert_txt(#alert{level = Level, description = Description, where = {Mod,Line}, reason = undefined, role = Role}) ->
+ "at " ++ Mod ++ ":" ++ integer_to_list(Line) ++ " generated " ++ string:to_upper(atom_to_list(Role)) ++ " ALERT: " ++
+ level_txt(Level) ++ description_txt(Description);
+own_alert_txt(#alert{reason = Reason} = Alert) ->
+ BaseTxt = own_alert_txt(Alert#alert{reason = undefined}),
+ FormatDepth = 9, % Some limit on printed representation of an error
+ ReasonTxt = lists:flatten(io_lib:format("~P", [Reason, FormatDepth])),
+ BaseTxt ++ " - " ++ ReasonTxt.
+
+%%--------------------------------------------------------------------
-spec alert_txt(#alert{}) -> string().
%%
-%% Description: Returns the error string for given alert.
+%% Description: Returns the error string for given alert received from
+%% the peer.
%%--------------------------------------------------------------------
-alert_txt(#alert{level = Level, description = Description, where = {Mod,Line}, reason = undefined, role = Role}) ->
- "at " ++ Mod ++ ":" ++ integer_to_list(Line) ++ " " ++ string:to_upper(atom_to_list(Role)) ++ " ALERT: " ++
+alert_txt(#alert{level = Level, description = Description, reason = undefined, role = Role}) ->
+ "received " ++ string:to_upper(atom_to_list(Role)) ++ " ALERT: " ++
level_txt(Level) ++ description_txt(Description);
alert_txt(#alert{reason = Reason} = Alert) ->
BaseTxt = alert_txt(Alert#alert{reason = undefined}),
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 5cd66387ae..b031d3d47b 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1144,7 +1144,8 @@ handle_alert(#alert{level = ?FATAL} = Alert, StateName,
port = Port, session = Session, user_application = {_Mon, Pid},
role = Role, socket_options = Opts, tracker = Tracker}) ->
invalidate_session(Role, Host, Port, Session),
- log_alert(SslOpts#ssl_options.log_alert, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
+ log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(),
+ StateName, Alert#alert{role = opposite_role(Role)}),
alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, Connection),
{stop, normal};
@@ -1155,7 +1156,8 @@ handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
#state{role = Role, ssl_options = SslOpts, protocol_cb = Connection, renegotiation = {true, internal}} = State) ->
- log_alert(SslOpts#ssl_options.log_alert, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
+ log_alert(SslOpts#ssl_options.log_alert, Role,
+ Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
handle_normal_shutdown(Alert, StateName, State),
{stop, {shutdown, peer_close}};
@@ -1163,7 +1165,8 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert,
#state{role = Role,
ssl_options = SslOpts, renegotiation = {true, From},
protocol_cb = Connection} = State0) ->
- log_alert(SslOpts#ssl_options.log_alert, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
+ log_alert(SslOpts#ssl_options.log_alert, Role,
+ Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
gen_statem:reply(From, {error, renegotiation_rejected}),
{Record, State} = Connection:next_record(State0),
%% Go back to connection!
@@ -1172,7 +1175,8 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert,
%% Gracefully log and ignore all other warning alerts
handle_alert(#alert{level = ?WARNING} = Alert, StateName,
#state{ssl_options = SslOpts, protocol_cb = Connection, role = Role} = State0) ->
- log_alert(SslOpts#ssl_options.log_alert, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
+ log_alert(SslOpts#ssl_options.log_alert, Role,
+ Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
{Record, State} = Connection:next_record(State0),
Connection:next_event(StateName, Record, State).
@@ -2372,10 +2376,13 @@ alert_user(Transport, Tracker, Socket, Active, Pid, From, Alert, Role, Connectio
Transport, Socket, Connection, Tracker), ReasonCode})
end.
-log_alert(true, ProtocolName, StateName, Alert) ->
+log_alert(true, Role, ProtocolName, StateName, #alert{role = Role} = Alert) ->
+ Txt = ssl_alert:own_alert_txt(Alert),
+ error_logger:info_report(io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]));
+log_alert(true, Role, ProtocolName, StateName, Alert) ->
Txt = ssl_alert:alert_txt(Alert),
- error_logger:format("~s: In state ~p ~s\n", [ProtocolName, StateName, Txt]);
-log_alert(false, _, _, _) ->
+ error_logger:info_report(io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]));
+log_alert(false, _, _, _, _) ->
ok.
handle_own_alert(Alert, Version, StateName,
@@ -2393,7 +2400,7 @@ handle_own_alert(Alert, Version, StateName,
ignore
end,
try %% Try to tell the local user
- log_alert(SslOpts#ssl_options.log_alert, Connection:protocol_name(), StateName, Alert#alert{role = Role}),
+ log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(), StateName, Alert#alert{role = Role}),
handle_normal_shutdown(Alert,StateName, State)
catch _:_ ->
ok
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index ca9aaf4660..ef7c3de7af 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -563,7 +563,7 @@ server_register_session(Port, Session, #state{session_cache_server_max = Max,
do_register_session(Key, Session, Max, Pid, Cache, CacheCb) ->
try CacheCb:size(Cache) of
- Max ->
+ Size when Size >= Max ->
invalidate_session_cache(Pid, CacheCb, Cache);
_ ->
CacheCb:update(Cache, Key, Session),
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index f6f3d18d6a..95af2b77a5 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -408,9 +408,9 @@
calls cannot be in the guard or body of the fun. Calls to built-in
match specification functions is of course allowed:</p>
<pre>
-4> <input>ets:fun2ms(fun({M,N}) when N > X, is_atomm(M) -> M end).</input>
+4> <input>ets:fun2ms(fun({M,N}) when N > X, my_fun(M) -> M end).</input>
Error: fun containing local Erlang function calls
-('is_atomm' called in guard) cannot be translated into match_spec
+('my_fun' called in guard) cannot be translated into match_spec
{error,transform_error}
5> <input>ets:fun2ms(fun({M,N}) when N > X, is_atom(M) -> M end).</input>
[{{'$1','$2'},[{'>','$2',{const,3}},{is_atom,'$1'}],['$1']}]</pre>
diff --git a/lib/stdlib/test/re_SUITE_data/testoutput1 b/lib/stdlib/test/re_SUITE_data/testoutput1
index a2b3cffe9d..eff8ecc948 100644
--- a/lib/stdlib/test/re_SUITE_data/testoutput1
+++ b/lib/stdlib/test/re_SUITE_data/testoutput1
@@ -9442,4 +9442,8 @@ No match
\ X
0: X
+/X+(?#comment)?/
+ >XXX<
+ 0: X
+
/-- End of testinput1 --/
diff --git a/lib/stdlib/test/re_SUITE_data/testoutput8 b/lib/stdlib/test/re_SUITE_data/testoutput8
index 17b667a980..4984376d3c 100644
--- a/lib/stdlib/test/re_SUITE_data/testoutput8
+++ b/lib/stdlib/test/re_SUITE_data/testoutput8
@@ -7801,4 +7801,8 @@ No match
** Show all captures ignored after DFA matching
0: a
+/(02-)?[0-9]{3}-[0-9]{3}/
+ 02-123-123
+ 0: 02-123-123
+
/-- End of testinput8 --/
diff --git a/lib/tools/doc/src/lcnt.xml b/lib/tools/doc/src/lcnt.xml
index 922c2ac0f4..5bdfc60448 100644
--- a/lib/tools/doc/src/lcnt.xml
+++ b/lib/tools/doc/src/lcnt.xml
@@ -471,7 +471,7 @@
<c>lcnt:rt_opt/2</c>.
</p>
<p>Valid categories are:</p>
- <taglist>
+ <list>
<item><c>allocator</c></item>
<item><c>db</c> (ETS tables)</item>
<item><c>debug</c></item>
@@ -480,7 +480,7 @@
<item><c>io</c></item>
<item><c>process</c></item>
<item><c>scheduler</c></item>
- </taglist>
+ </list>
<p>
This list is subject to change at any time, as is the category any given lock
may belong to.
diff --git a/scripts/run-dialyzer b/scripts/run-dialyzer
index 383ae2301d..05c1fd63c0 100755
--- a/scripts/run-dialyzer
+++ b/scripts/run-dialyzer
@@ -4,4 +4,13 @@ set -e
$ERL_TOP/bin/dialyzer --build_plt --apps asn1 compiler crypto dialyzer edoc erts et hipe inets kernel mnesia observer public_key runtime_tools snmp ssh ssl stdlib syntax_tools wx xmerl --statistics
$ERL_TOP/bin/dialyzer -n -Wunknown -Wunmatched_returns --apps compiler erts kernel stdlib asn1 crypto dialyzer hipe parsetools public_key runtime_tools sasl tools --statistics
-$ERL_TOP/bin/dialyzer -n --apps common_test debugger edoc inets mnesia observer ssh ssl syntax_tools tools wx xmerl --statistics
+$ERL_TOP/bin/dialyzer -n --apps common_test debugger edoc inets mnesia observer ssh ssl syntax_tools wx xmerl --statistics
+
+# In travis we don't dialyze everything as it takes too much time
+if [ "X$DIALYZE_ALL_APPLICATIONS" = "Xtrue" ]; then
+ $ERL_TOP/bin/dialyzer -n -Wunknown -Wunmatched_returns --apps eldap erl_docgen et odbc --statistics
+ $ERL_TOP/bin/dialyzer -n --apps eunit reltool os_mon --statistics
+
+ # These application are not run always as the currently have dialyzer warnings
+ # $ERL_TOP/bin/dialyzer -n --apps cosEvent cosEventDomain cosFileTransfer cosNotification cosProperty cosTime cosTransactions diameter megaco orber snmp --statistics
+fi