diff --git a/Makefile.am b/Makefile.am
index 840d114c65..7caf7d51e8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -300,6 +300,7 @@ library_include_HEADERS = \
libs/libteletone/src/libteletone_detect.h \
libs/libteletone/src/libteletone_generate.h \
libs/libteletone/src/libteletone.h \
+ src/include/switch_uuidv7.h \
src/include/switch_limit.h \
src/include/switch_odbc.h \
src/include/switch_hashtable.h \
@@ -394,7 +395,8 @@ libfreeswitch_la_SOURCES = \
libs/miniupnpc/minissdpc.c \
libs/miniupnpc/upnperrors.c \
libs/libnatpmp/natpmp.c \
- libs/libnatpmp/getgateway.c
+ libs/libnatpmp/getgateway.c \
+ src/switch_uuidv7.c
if ENABLE_CPP
libfreeswitch_la_SOURCES += src/switch_cpp.cpp
@@ -814,4 +816,3 @@ support:
@cp support-d/.screenrc ~
@cp support-d/.bashrc ~
@test -f ~/.cc-mode-installed || sh support-d/install-cc-mode.sh && touch ~/.cc-mode-installed
-
diff --git a/conf/vanilla/autoload_configs/switch.conf.xml b/conf/vanilla/autoload_configs/switch.conf.xml
index 8117d8ed9c..29ac39976f 100644
--- a/conf/vanilla/autoload_configs/switch.conf.xml
+++ b/conf/vanilla/autoload_configs/switch.conf.xml
@@ -64,6 +64,9 @@
+
+
+
@@ -206,4 +209,3 @@
-
diff --git a/src/include/private/switch_core_pvt.h b/src/include/private/switch_core_pvt.h
index fafaae3cba..7507f5dc33 100644
--- a/src/include/private/switch_core_pvt.h
+++ b/src/include/private/switch_core_pvt.h
@@ -287,6 +287,7 @@ struct switch_runtime {
char *event_channel_key_separator;
uint32_t max_audio_channels;
switch_call_cause_t shutdown_cause;
+ uint32_t uuid_version;
};
extern struct switch_runtime runtime;
diff --git a/src/include/switch_types.h b/src/include/switch_types.h
index e71570c21d..294c64ee3f 100644
--- a/src/include/switch_types.h
+++ b/src/include/switch_types.h
@@ -2310,7 +2310,8 @@ typedef enum {
SCSC_SESSIONS_PEAK,
SCSC_SESSIONS_PEAK_FIVEMIN,
SCSC_MDNS_RESOLVE,
- SCSC_SHUTDOWN_CAUSE
+ SCSC_SHUTDOWN_CAUSE,
+ SCSC_UUID_VERSION
} switch_session_ctl_t;
typedef enum {
diff --git a/src/include/switch_uuidv7.h b/src/include/switch_uuidv7.h
new file mode 100644
index 0000000000..432e9635f9
--- /dev/null
+++ b/src/include/switch_uuidv7.h
@@ -0,0 +1,196 @@
+/*
+ * switch_uuidv7.h uuidv7
+*/
+#include
+
+#undef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 199309L
+
+#include
+#include
+
+
+/**
+ * Indicates that the `unix_ts_ms` passed was used because no preceding UUID was
+ * specified.
+ */
+#define UUIDV7_STATUS_UNPRECEDENTED (0)
+
+/**
+ * Indicates that the `unix_ts_ms` passed was used because it was greater than
+ * the previous one.
+ */
+#define UUIDV7_STATUS_NEW_TIMESTAMP (1)
+
+/**
+ * Indicates that the counter was incremented because the `unix_ts_ms` passed
+ * was no greater than the previous one.
+ */
+#define UUIDV7_STATUS_COUNTER_INC (2)
+
+/**
+ * Indicates that the previous `unix_ts_ms` was incremented because the counter
+ * reached its maximum value.
+ */
+#define UUIDV7_STATUS_TIMESTAMP_INC (3)
+
+/**
+ * Indicates that the monotonic order of generated UUIDs was broken because the
+ * `unix_ts_ms` passed was less than the previous one by more than ten seconds.
+ */
+#define UUIDV7_STATUS_CLOCK_ROLLBACK (4)
+
+/** Indicates that an invalid `unix_ts_ms` is passed. */
+#define UUIDV7_STATUS_ERR_TIMESTAMP (-1)
+
+/**
+ * Indicates that the attempt to increment the previous `unix_ts_ms` failed
+ * because it had reached its maximum value.
+ */
+#define UUIDV7_STATUS_ERR_TIMESTAMP_OVERFLOW (-2)
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * Generates a new UUIDv7 from the given Unix time, random bytes, and previous
+ * UUID.
+ *
+ * @param uuid_out 16-byte byte array where the generated UUID is stored.
+ * @param unix_ts_ms Current Unix time in milliseconds.
+ * @param rand_bytes At least 10-byte byte array filled with random bytes. This
+ * function consumes the leading 4 bytes or the whole 10
+ * bytes per call depending on the conditions.
+ * `uuidv7_status_n_rand_consumed()` maps the return value of
+ * this function to the number of random bytes consumed.
+ * @param uuid_prev 16-byte byte array representing the immediately preceding
+ * UUID, from which the previous timestamp and counter are
+ * extracted. This may be NULL if the caller does not care
+ * the ascending order of UUIDs within the same timestamp.
+ * This may point to the same location as `uuid_out`; this
+ * function reads the value before writing.
+ * @return One of the `UUIDV7_STATUS_*` codes that describe the
+ * characteristics of generated UUIDs. Callers can usually
+ * ignore the status unless they need to guarantee the
+ * monotonic order of UUIDs or fine-tune the generation
+ * process.
+ */
+
+static inline int8_t uuidv7_generate(uint8_t *uuid_out, uint64_t unix_ts_ms,const uint8_t *rand_bytes,const uint8_t *uuid_prev) {
+ int8_t status;
+ uint64_t timestamp = 0;
+ static const uint64_t MAX_TIMESTAMP = ((uint64_t)1 << 48) - 1;
+ static const uint64_t MAX_COUNTER = ((uint64_t)1 << 42) - 1;
+
+ if (unix_ts_ms > MAX_TIMESTAMP) {
+ return UUIDV7_STATUS_ERR_TIMESTAMP;
+ }
+
+
+ if (uuid_prev == NULL) {
+ status = UUIDV7_STATUS_UNPRECEDENTED;
+ timestamp = unix_ts_ms;
+ } else {
+ for (int i = 0; i < 6; i++) {
+ timestamp = (timestamp << 8) | uuid_prev[i];
+ }
+
+ if (unix_ts_ms > timestamp) {
+ status = UUIDV7_STATUS_NEW_TIMESTAMP;
+ timestamp = unix_ts_ms;
+ } else if (unix_ts_ms + 10000 < timestamp) {
+ // ignore prev if clock moves back by more than ten seconds
+ status = UUIDV7_STATUS_CLOCK_ROLLBACK;
+ timestamp = unix_ts_ms;
+ } else {
+ // increment prev counter
+ uint64_t counter = uuid_prev[6] & 0x0f; // skip ver
+ counter = (counter << 8) | uuid_prev[7];
+ counter = (counter << 6) | (uuid_prev[8] & 0x3f); // skip var
+ counter = (counter << 8) | uuid_prev[9];
+ counter = (counter << 8) | uuid_prev[10];
+ counter = (counter << 8) | uuid_prev[11];
+
+ if (counter++ < MAX_COUNTER) {
+ status = UUIDV7_STATUS_COUNTER_INC;
+ uuid_out[6] = counter >> 38; // ver + bits 0-3
+ uuid_out[7] = counter >> 30; // bits 4-11
+ uuid_out[8] = counter >> 24; // var + bits 12-17
+ uuid_out[9] = counter >> 16; // bits 18-25
+ uuid_out[10] = counter >> 8; // bits 26-33
+ uuid_out[11] = counter; // bits 34-41
+ } else {
+ // increment prev timestamp at counter overflow
+ status = UUIDV7_STATUS_TIMESTAMP_INC;
+ timestamp++;
+ if (timestamp > MAX_TIMESTAMP) {
+ return UUIDV7_STATUS_ERR_TIMESTAMP_OVERFLOW;
+ }
+ }
+ }
+ }
+
+ uuid_out[0] = timestamp >> 40;
+ uuid_out[1] = timestamp >> 32;
+ uuid_out[2] = timestamp >> 24;
+ uuid_out[3] = timestamp >> 16;
+ uuid_out[4] = timestamp >> 8;
+ uuid_out[5] = timestamp;
+
+ for (int i = (status == UUIDV7_STATUS_COUNTER_INC) ? 12 : 6; i < 16; i++) {
+ uuid_out[i] = *rand_bytes++;
+ }
+
+ uuid_out[6] = 0x70 | (uuid_out[6] & 0x0f); // set ver
+ uuid_out[8] = 0x80 | (uuid_out[8] & 0x3f); // set var
+
+ return status;
+}
+
+/**
+ * Determines the number of random bytes consumsed by `uuidv7_generate()` from
+ * the `UUIDV7_STATUS_*` code returned.
+ *
+ * @param status `UUIDV7_STATUS_*` code returned by `uuidv7_generate()`.
+ * @return `4` if `status` is `UUIDV7_STATUS_COUNTER_INC` or `10`
+ * otherwise.
+ */
+static inline int uuidv7_status_n_rand_consumed(int8_t status) {
+ return status == UUIDV7_STATUS_COUNTER_INC ? 4 : 10;
+}
+
+
+/**
+ * @name High-level APIs that require platform integration
+ */
+
+/**
+ * Generates a new UUIDv7 with the current Unix time.
+ *
+ * This declaration defines the interface to generate a new UUIDv7 with the
+ * current time, default random number generator, and global shared state
+ * holding the previously generated UUID. Since this single-file library does
+ * not provide platform-specific implementations, users need to prepare a
+ * concrete implementation (if necessary) by integrating a real-time clock,
+ * cryptographically strong random number generator, and shared state storage
+ * available in the target platform.
+ *
+ * @param uuid_out 16-byte byte array where the generated UUID is stored.
+ * @return One of the `UUIDV7_STATUS_*` codes that describe the
+ * characteristics of generated UUIDs or an
+ * implementation-dependent code. Callers can usually ignore
+ * the `UUIDV7_STATUS_*` code unless they need to guarantee the
+ * monotonic order of UUIDs or fine-tune the generation
+ * process. The implementation-dependent code must be out of
+ * the range of `int8_t` and negative if it reports an error.
+ */
+
+SWITCH_DECLARE(int) uuidv7_new(uint8_t *uuid_out);
+
+
+#ifdef __cplusplus
+} /* extern "C" { */
+#endif
diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c
index 5b9620a781..075f29aa11 100644
--- a/src/mod/applications/mod_commands/mod_commands.c
+++ b/src/mod/applications/mod_commands/mod_commands.c
@@ -2417,7 +2417,7 @@ SWITCH_STANDARD_API(uptime_function)
return SWITCH_STATUS_SUCCESS;
}
-#define CTL_SYNTAX "[api_expansion [on|off]|recover|send_sighup|hupall|pause [inbound|outbound]|resume [inbound|outbound]|shutdown [cancel|elegant|asap|now|restart]|sps|sps_peak_reset|sync_clock|sync_clock_when_idle|reclaim_mem|max_sessions|min_dtmf_duration [num]|max_dtmf_duration [num]|default_dtmf_duration [num]|min_idle_cpu|loglevel [level]|debug_level [level]|mdns_resolve [enable|disable]]"
+#define CTL_SYNTAX "[api_expansion [on|off]|recover|send_sighup|hupall|pause [inbound|outbound]|resume [inbound|outbound]|shutdown [cancel|elegant|asap|now|restart]|uuid_version [4|7]|sps|sps_peak_reset|sync_clock|sync_clock_when_idle|reclaim_mem|max_sessions|min_dtmf_duration [num]|max_dtmf_duration [num]|default_dtmf_duration [num]|min_idle_cpu|loglevel [level]|debug_level [level]|mdns_resolve [enable|disable]]"
SWITCH_STANDARD_API(ctl_function)
{
int argc;
@@ -2661,6 +2661,14 @@ SWITCH_STANDARD_API(ctl_function)
}
switch_core_session_ctl(SCSC_SPS, &arg);
stream->write_function(stream, "+OK sessions per second: %d\n", arg);
+ } else if (!strcasecmp(argv[0], "uuid_version")) {
+ if (argc > 1) {
+ arg = atoi(argv[1]);
+ } else {
+ arg = 0;
+ }
+ switch_core_session_ctl(SCSC_UUID_VERSION, &arg);
+ stream->write_function(stream, "+OK set uuid version: %d\n", arg);
} else if (!strcasecmp(argv[0], "sync_clock")) {
arg = 0;
switch_core_session_ctl(SCSC_SYNC_CLOCK, &arg);
@@ -7808,6 +7816,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
switch_console_set_complete("add fsctl send_sighup");
switch_console_set_complete("add fsctl mdns_resolve disable");
switch_console_set_complete("add fsctl mdns_resolve enable");
+ switch_console_set_complete("add fsctl uuid_version");
+ switch_console_set_complete("add fsctl uuid_version 4");
+ switch_console_set_complete("add fsctl uuid_version 7");
switch_console_set_complete("add interface_ip auto ::console::list_interfaces");
switch_console_set_complete("add interface_ip ipv4 ::console::list_interfaces");
switch_console_set_complete("add interface_ip ipv6 ::console::list_interfaces");
diff --git a/src/switch_apr.c b/src/switch_apr.c
index bd6cfdec56..f20bcb4bc5 100644
--- a/src/switch_apr.c
+++ b/src/switch_apr.c
@@ -89,6 +89,7 @@
#ifndef WIN32
#include
#endif
+#include
/* apr stubs */
@@ -1153,7 +1154,11 @@ SWITCH_DECLARE(void) switch_uuid_get(switch_uuid_t *uuid)
{
switch_mutex_lock(runtime.uuid_mutex);
#ifndef WIN32
- uuid_generate(uuid->data);
+ if (runtime.uuid_version == 7) {
+ uuidv7_new(uuid->data);
+ } else {
+ uuid_generate(uuid->data);
+ }
#else
UuidCreate((UUID *) uuid);
#endif
diff --git a/src/switch_core.c b/src/switch_core.c
index 7ec10d8885..cf4abf731c 100644
--- a/src/switch_core.c
+++ b/src/switch_core.c
@@ -1904,6 +1904,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_init(switch_core_flag_t flags, switc
load_mime_types();
runtime.flags |= flags;
runtime.sps_total = 30;
+ runtime.uuid_version = 4;
*err = NULL;
@@ -2212,6 +2213,8 @@ static void switch_load_core_config(const char *file)
switch_time_set_use_system_time(switch_true(val));
} else if (!strcasecmp(var, "enable-monotonic-timing")) {
switch_time_set_monotonic(switch_true(val));
+ } else if (!strcasecmp(var, "uuid-version") && !zstr(val)) {
+ runtime.uuid_version = atoi(val);
} else if (!strcasecmp(var, "enable-softtimer-timerfd")) {
int ival = 0;
if (val) {
@@ -2963,7 +2966,12 @@ SWITCH_DECLARE(int32_t) switch_core_session_ctl(switch_session_ctl_t cmd, void *
newintval = runtime.sps_total;
switch_mutex_unlock(runtime.throttle_mutex);
break;
-
+ case SCSC_UUID_VERSION:
+ if(oldintval > 0){
+ runtime.uuid_version = oldintval;
+ }
+ newintval = runtime.uuid_version;
+ break;
case SCSC_RECLAIM:
switch_core_memory_reclaim_all();
newintval = 0;
diff --git a/src/switch_uuidv7.c b/src/switch_uuidv7.c
new file mode 100644
index 0000000000..cf855dc099
--- /dev/null
+++ b/src/switch_uuidv7.c
@@ -0,0 +1,40 @@
+
+
+#include "switch_uuidv7.h"
+
+// #include
+// #include
+// #include
+// #include
+// #include
+#ifdef __APPLE__
+#include // for macOS getentropy()
+#endif
+
+SWITCH_DECLARE(int) uuidv7_new(uint8_t *uuid_out)
+{
+ int8_t status;
+ // struct timespec tp;
+ static uint8_t uuid_prev[16] = {0};
+ static uint8_t rand_bytes[256] = {0};
+ static size_t n_rand_consumed = sizeof(rand_bytes);
+
+ uint64_t unix_ts_ms ;
+ // clock_gettime(CLOCK_REALTIME, &tp);
+ // unix_ts_ms = (uint64_t)tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
+ unix_ts_ms = switch_time_now() / 1000;
+
+ if (n_rand_consumed > sizeof(rand_bytes) - 10)
+ {
+ getentropy(rand_bytes, n_rand_consumed);
+ n_rand_consumed = 0;
+ }
+
+ status = uuidv7_generate(uuid_prev, unix_ts_ms,
+ &rand_bytes[n_rand_consumed], uuid_prev);
+ n_rand_consumed += uuidv7_status_n_rand_consumed(status);
+
+ memcpy(uuid_out, uuid_prev, 16);
+ return status;
+
+}
diff --git a/tests/unit/.gitignore b/tests/unit/.gitignore
index 470c5ded12..de3e635bf2 100644
--- a/tests/unit/.gitignore
+++ b/tests/unit/.gitignore
@@ -43,6 +43,10 @@ switch_xml
switch_estimators
switch_jitter_buffer
test_sofia
+switch_core_asr
+switch_core_media
+switch_sip
+switch_rtp_pcap
.deps/
Makefile
conf/*/
diff --git a/tests/unit/switch_core.c b/tests/unit/switch_core.c
index 295e4e0ff1..0f7dbb1f78 100644
--- a/tests/unit/switch_core.c
+++ b/tests/unit/switch_core.c
@@ -32,6 +32,11 @@
#include
#include
+#include
+// #include
+#include
+#include
+
#if defined(HAVE_OPENSSL)
#include
#endif
@@ -568,6 +573,52 @@ FST_CORE_BEGIN("./conf")
fst_check(switch_channel_get_variable_buf(channel, "test_var_does_not_exist", buf, sizeof(buf)) == SWITCH_STATUS_FALSE);
}
FST_SESSION_END()
+
+ FST_TEST_BEGIN(test_create_uuid)
+ {
+ switch_uuid_t uuid;
+ switch_uuid_t uuid2;
+ char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 };
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "test_create_uuid:\n");
+ uuidv7_new(uuid.data);
+ switch_uuid_format(uuid_str, &uuid);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "uuidv7: %s\n", uuid_str);
+ uuidv7_new(uuid.data);
+ switch_uuid_format(uuid_str, &uuid);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "uuidv7: %s\n", uuid_str);
+ fst_check(0 != memcmp(uuid.data, uuid2.data, sizeof(uuid.data)));
+ uuid_generate(uuid.data);
+ switch_uuid_format(uuid_str, &uuid);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "uuidv4: %s\n", uuid_str);
+ }
+ FST_TEST_END()
+
+ FST_TEST_BEGIN(test_create_uuid_speed)
+ {
+ int n;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "test_create_uuid_speed:\n");
+ for (n = 1; n < 4; n++) {
+ clock_t start = 0, end = 0;
+ switch_uuid_t uuid;
+ double cpu_time_used = 0;
+ start = clock();
+ for (int i = 0; i < 1000 * pow(10, n); i++) {
+ uuidv7_new(uuid.data);
+ }
+ end = clock();
+ cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%d uuidv7_new running time: %f seconds\n", 1000 * (int)pow(10, n), cpu_time_used);
+
+ start = clock();
+ for (long long int i = 0; i < 1000 * pow(10, n); i++) {
+ uuid_generate(uuid.data);
+ }
+ end = clock();
+ cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%d uuid_generate running time: %f seconds\n", 1000 * (int)pow(10, n), cpu_time_used);
+ }
+ }
+ FST_TEST_END()
}
FST_SUITE_END()
}