Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] add uuidv7 support #2746

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

4 changes: 3 additions & 1 deletion conf/vanilla/autoload_configs/switch.conf.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
<!-- Default Global Log Level - value is one of debug,info,notice,warning,err,crit,alert -->
<param name="loglevel" value="debug"/>

<!-- UUID version to use, 4 or 7 -->
<!-- <param name="uuid-version" value="7"/> -->

<!-- Set the core DEBUG level (0-10) -->
<!-- <param name="debug-level" value="10"/> -->

Expand Down Expand Up @@ -206,4 +209,3 @@
</settings>

</configuration>

1 change: 1 addition & 0 deletions src/include/private/switch_core_pvt.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/include/switch_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
196 changes: 196 additions & 0 deletions src/include/switch_uuidv7.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
* switch_uuidv7.h uuidv7
*/
#include <switch.h>

#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L

#include <stddef.h>
#include <stdint.h>


/**
* 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
13 changes: 12 additions & 1 deletion src/mod/applications/mod_commands/mod_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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");
Expand Down
7 changes: 6 additions & 1 deletion src/switch_apr.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
#ifndef WIN32
#include <uuid/uuid.h>
#endif
#include <switch_uuidv7.h>

/* apr stubs */

Expand Down Expand Up @@ -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
Expand Down
10 changes: 9 additions & 1 deletion src/switch_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
40 changes: 40 additions & 0 deletions src/switch_uuidv7.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@


#include "switch_uuidv7.h"

// #include <assert.h>
// #include <stdio.h>
// #include <string.h>
// #include <time.h>
// #include <unistd.h>
#ifdef __APPLE__
#include <sys/random.h> // 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;

}
4 changes: 4 additions & 0 deletions tests/unit/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/*/
Expand Down
Loading
Loading