Skip to content

Commit b2a1386

Browse files
committed
[core] add uuidv7 support
1 parent 5cb7479 commit b2a1386

File tree

11 files changed

+328
-7
lines changed

11 files changed

+328
-7
lines changed

Makefile.am

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ library_include_HEADERS = \
300300
libs/libteletone/src/libteletone_detect.h \
301301
libs/libteletone/src/libteletone_generate.h \
302302
libs/libteletone/src/libteletone.h \
303+
src/include/switch_uuidv7.h \
303304
src/include/switch_limit.h \
304305
src/include/switch_odbc.h \
305306
src/include/switch_hashtable.h \
@@ -394,7 +395,8 @@ libfreeswitch_la_SOURCES = \
394395
libs/miniupnpc/minissdpc.c \
395396
libs/miniupnpc/upnperrors.c \
396397
libs/libnatpmp/natpmp.c \
397-
libs/libnatpmp/getgateway.c
398+
libs/libnatpmp/getgateway.c \
399+
src/switch_uuidv7.c
398400

399401
if ENABLE_CPP
400402
libfreeswitch_la_SOURCES += src/switch_cpp.cpp
@@ -814,4 +816,3 @@ support:
814816
@cp support-d/.screenrc ~
815817
@cp support-d/.bashrc ~
816818
@test -f ~/.cc-mode-installed || sh support-d/install-cc-mode.sh && touch ~/.cc-mode-installed
817-

conf/vanilla/autoload_configs/switch.conf.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
<!-- Default Global Log Level - value is one of debug,info,notice,warning,err,crit,alert -->
6565
<param name="loglevel" value="debug"/>
6666

67+
<!-- UUID version to use, 4 or 7 -->
68+
<!-- <param name="uuid-version" value="7"/> -->
69+
6770
<!-- Set the core DEBUG level (0-10) -->
6871
<!-- <param name="debug-level" value="10"/> -->
6972

@@ -206,4 +209,3 @@
206209
</settings>
207210

208211
</configuration>
209-

src/include/private/switch_core_pvt.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ struct switch_runtime {
287287
char *event_channel_key_separator;
288288
uint32_t max_audio_channels;
289289
switch_call_cause_t shutdown_cause;
290+
uint32_t uuid_version;
290291
};
291292

292293
extern struct switch_runtime runtime;

src/include/switch_types.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2310,7 +2310,8 @@ typedef enum {
23102310
SCSC_SESSIONS_PEAK,
23112311
SCSC_SESSIONS_PEAK_FIVEMIN,
23122312
SCSC_MDNS_RESOLVE,
2313-
SCSC_SHUTDOWN_CAUSE
2313+
SCSC_SHUTDOWN_CAUSE,
2314+
SCSC_UUID_VERSION
23142315
} switch_session_ctl_t;
23152316

23162317
typedef enum {

src/include/switch_uuidv7.h

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/*
2+
* switch_uuidv7.h uuidv7
3+
*/
4+
#include <switch.h>
5+
6+
#undef _POSIX_C_SOURCE
7+
#define _POSIX_C_SOURCE 199309L
8+
9+
#include <stddef.h>
10+
#include <stdint.h>
11+
12+
13+
/**
14+
* Indicates that the `unix_ts_ms` passed was used because no preceding UUID was
15+
* specified.
16+
*/
17+
#define UUIDV7_STATUS_UNPRECEDENTED (0)
18+
19+
/**
20+
* Indicates that the `unix_ts_ms` passed was used because it was greater than
21+
* the previous one.
22+
*/
23+
#define UUIDV7_STATUS_NEW_TIMESTAMP (1)
24+
25+
/**
26+
* Indicates that the counter was incremented because the `unix_ts_ms` passed
27+
* was no greater than the previous one.
28+
*/
29+
#define UUIDV7_STATUS_COUNTER_INC (2)
30+
31+
/**
32+
* Indicates that the previous `unix_ts_ms` was incremented because the counter
33+
* reached its maximum value.
34+
*/
35+
#define UUIDV7_STATUS_TIMESTAMP_INC (3)
36+
37+
/**
38+
* Indicates that the monotonic order of generated UUIDs was broken because the
39+
* `unix_ts_ms` passed was less than the previous one by more than ten seconds.
40+
*/
41+
#define UUIDV7_STATUS_CLOCK_ROLLBACK (4)
42+
43+
/** Indicates that an invalid `unix_ts_ms` is passed. */
44+
#define UUIDV7_STATUS_ERR_TIMESTAMP (-1)
45+
46+
/**
47+
* Indicates that the attempt to increment the previous `unix_ts_ms` failed
48+
* because it had reached its maximum value.
49+
*/
50+
#define UUIDV7_STATUS_ERR_TIMESTAMP_OVERFLOW (-2)
51+
52+
53+
#ifdef __cplusplus
54+
extern "C" {
55+
#endif
56+
57+
58+
/**
59+
* Generates a new UUIDv7 from the given Unix time, random bytes, and previous
60+
* UUID.
61+
*
62+
* @param uuid_out 16-byte byte array where the generated UUID is stored.
63+
* @param unix_ts_ms Current Unix time in milliseconds.
64+
* @param rand_bytes At least 10-byte byte array filled with random bytes. This
65+
* function consumes the leading 4 bytes or the whole 10
66+
* bytes per call depending on the conditions.
67+
* `uuidv7_status_n_rand_consumed()` maps the return value of
68+
* this function to the number of random bytes consumed.
69+
* @param uuid_prev 16-byte byte array representing the immediately preceding
70+
* UUID, from which the previous timestamp and counter are
71+
* extracted. This may be NULL if the caller does not care
72+
* the ascending order of UUIDs within the same timestamp.
73+
* This may point to the same location as `uuid_out`; this
74+
* function reads the value before writing.
75+
* @return One of the `UUIDV7_STATUS_*` codes that describe the
76+
* characteristics of generated UUIDs. Callers can usually
77+
* ignore the status unless they need to guarantee the
78+
* monotonic order of UUIDs or fine-tune the generation
79+
* process.
80+
*/
81+
82+
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) {
83+
int8_t status;
84+
uint64_t timestamp = 0;
85+
static const uint64_t MAX_TIMESTAMP = ((uint64_t)1 << 48) - 1;
86+
static const uint64_t MAX_COUNTER = ((uint64_t)1 << 42) - 1;
87+
88+
if (unix_ts_ms > MAX_TIMESTAMP) {
89+
return UUIDV7_STATUS_ERR_TIMESTAMP;
90+
}
91+
92+
93+
if (uuid_prev == NULL) {
94+
status = UUIDV7_STATUS_UNPRECEDENTED;
95+
timestamp = unix_ts_ms;
96+
} else {
97+
for (int i = 0; i < 6; i++) {
98+
timestamp = (timestamp << 8) | uuid_prev[i];
99+
}
100+
101+
if (unix_ts_ms > timestamp) {
102+
status = UUIDV7_STATUS_NEW_TIMESTAMP;
103+
timestamp = unix_ts_ms;
104+
} else if (unix_ts_ms + 10000 < timestamp) {
105+
// ignore prev if clock moves back by more than ten seconds
106+
status = UUIDV7_STATUS_CLOCK_ROLLBACK;
107+
timestamp = unix_ts_ms;
108+
} else {
109+
// increment prev counter
110+
uint64_t counter = uuid_prev[6] & 0x0f; // skip ver
111+
counter = (counter << 8) | uuid_prev[7];
112+
counter = (counter << 6) | (uuid_prev[8] & 0x3f); // skip var
113+
counter = (counter << 8) | uuid_prev[9];
114+
counter = (counter << 8) | uuid_prev[10];
115+
counter = (counter << 8) | uuid_prev[11];
116+
117+
if (counter++ < MAX_COUNTER) {
118+
status = UUIDV7_STATUS_COUNTER_INC;
119+
uuid_out[6] = counter >> 38; // ver + bits 0-3
120+
uuid_out[7] = counter >> 30; // bits 4-11
121+
uuid_out[8] = counter >> 24; // var + bits 12-17
122+
uuid_out[9] = counter >> 16; // bits 18-25
123+
uuid_out[10] = counter >> 8; // bits 26-33
124+
uuid_out[11] = counter; // bits 34-41
125+
} else {
126+
// increment prev timestamp at counter overflow
127+
status = UUIDV7_STATUS_TIMESTAMP_INC;
128+
timestamp++;
129+
if (timestamp > MAX_TIMESTAMP) {
130+
return UUIDV7_STATUS_ERR_TIMESTAMP_OVERFLOW;
131+
}
132+
}
133+
}
134+
}
135+
136+
uuid_out[0] = timestamp >> 40;
137+
uuid_out[1] = timestamp >> 32;
138+
uuid_out[2] = timestamp >> 24;
139+
uuid_out[3] = timestamp >> 16;
140+
uuid_out[4] = timestamp >> 8;
141+
uuid_out[5] = timestamp;
142+
143+
for (int i = (status == UUIDV7_STATUS_COUNTER_INC) ? 12 : 6; i < 16; i++) {
144+
uuid_out[i] = *rand_bytes++;
145+
}
146+
147+
uuid_out[6] = 0x70 | (uuid_out[6] & 0x0f); // set ver
148+
uuid_out[8] = 0x80 | (uuid_out[8] & 0x3f); // set var
149+
150+
return status;
151+
}
152+
153+
/**
154+
* Determines the number of random bytes consumsed by `uuidv7_generate()` from
155+
* the `UUIDV7_STATUS_*` code returned.
156+
*
157+
* @param status `UUIDV7_STATUS_*` code returned by `uuidv7_generate()`.
158+
* @return `4` if `status` is `UUIDV7_STATUS_COUNTER_INC` or `10`
159+
* otherwise.
160+
*/
161+
static inline int uuidv7_status_n_rand_consumed(int8_t status) {
162+
return status == UUIDV7_STATUS_COUNTER_INC ? 4 : 10;
163+
}
164+
165+
166+
/**
167+
* @name High-level APIs that require platform integration
168+
*/
169+
170+
/**
171+
* Generates a new UUIDv7 with the current Unix time.
172+
*
173+
* This declaration defines the interface to generate a new UUIDv7 with the
174+
* current time, default random number generator, and global shared state
175+
* holding the previously generated UUID. Since this single-file library does
176+
* not provide platform-specific implementations, users need to prepare a
177+
* concrete implementation (if necessary) by integrating a real-time clock,
178+
* cryptographically strong random number generator, and shared state storage
179+
* available in the target platform.
180+
*
181+
* @param uuid_out 16-byte byte array where the generated UUID is stored.
182+
* @return One of the `UUIDV7_STATUS_*` codes that describe the
183+
* characteristics of generated UUIDs or an
184+
* implementation-dependent code. Callers can usually ignore
185+
* the `UUIDV7_STATUS_*` code unless they need to guarantee the
186+
* monotonic order of UUIDs or fine-tune the generation
187+
* process. The implementation-dependent code must be out of
188+
* the range of `int8_t` and negative if it reports an error.
189+
*/
190+
191+
SWITCH_DECLARE(int) uuidv7_new(uint8_t *uuid_out);
192+
193+
194+
#ifdef __cplusplus
195+
} /* extern "C" { */
196+
#endif

src/mod/applications/mod_commands/mod_commands.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2417,7 +2417,7 @@ SWITCH_STANDARD_API(uptime_function)
24172417
return SWITCH_STATUS_SUCCESS;
24182418
}
24192419

2420-
#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]]"
2420+
#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]]"
24212421
SWITCH_STANDARD_API(ctl_function)
24222422
{
24232423
int argc;
@@ -2661,6 +2661,14 @@ SWITCH_STANDARD_API(ctl_function)
26612661
}
26622662
switch_core_session_ctl(SCSC_SPS, &arg);
26632663
stream->write_function(stream, "+OK sessions per second: %d\n", arg);
2664+
} else if (!strcasecmp(argv[0], "uuid_version")) {
2665+
if (argc > 1) {
2666+
arg = atoi(argv[1]);
2667+
} else {
2668+
arg = 0;
2669+
}
2670+
switch_core_session_ctl(SCSC_UUID_VERSION, &arg);
2671+
stream->write_function(stream, "+OK set uuid version: %d\n", arg);
26642672
} else if (!strcasecmp(argv[0], "sync_clock")) {
26652673
arg = 0;
26662674
switch_core_session_ctl(SCSC_SYNC_CLOCK, &arg);
@@ -7808,6 +7816,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
78087816
switch_console_set_complete("add fsctl send_sighup");
78097817
switch_console_set_complete("add fsctl mdns_resolve disable");
78107818
switch_console_set_complete("add fsctl mdns_resolve enable");
7819+
switch_console_set_complete("add fsctl uuid_version");
7820+
switch_console_set_complete("add fsctl uuid_version 4");
7821+
switch_console_set_complete("add fsctl uuid_version 7");
78117822
switch_console_set_complete("add interface_ip auto ::console::list_interfaces");
78127823
switch_console_set_complete("add interface_ip ipv4 ::console::list_interfaces");
78137824
switch_console_set_complete("add interface_ip ipv6 ::console::list_interfaces");

src/switch_apr.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
#ifndef WIN32
9090
#include <uuid/uuid.h>
9191
#endif
92+
#include <switch_uuidv7.h>
9293

9394
/* apr stubs */
9495

@@ -1153,7 +1154,11 @@ SWITCH_DECLARE(void) switch_uuid_get(switch_uuid_t *uuid)
11531154
{
11541155
switch_mutex_lock(runtime.uuid_mutex);
11551156
#ifndef WIN32
1156-
uuid_generate(uuid->data);
1157+
if (runtime.uuid_version == 7) {
1158+
uuidv7_new(uuid->data);
1159+
} else {
1160+
uuid_generate(uuid->data);
1161+
}
11571162
#else
11581163
UuidCreate((UUID *) uuid);
11591164
#endif

src/switch_core.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1904,6 +1904,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_init(switch_core_flag_t flags, switc
19041904
load_mime_types();
19051905
runtime.flags |= flags;
19061906
runtime.sps_total = 30;
1907+
runtime.uuid_version = 4;
19071908

19081909
*err = NULL;
19091910

@@ -2212,6 +2213,8 @@ static void switch_load_core_config(const char *file)
22122213
switch_time_set_use_system_time(switch_true(val));
22132214
} else if (!strcasecmp(var, "enable-monotonic-timing")) {
22142215
switch_time_set_monotonic(switch_true(val));
2216+
} else if (!strcasecmp(var, "uuid-version") && !zstr(val)) {
2217+
runtime.uuid_version = atoi(val);
22152218
} else if (!strcasecmp(var, "enable-softtimer-timerfd")) {
22162219
int ival = 0;
22172220
if (val) {
@@ -2963,7 +2966,12 @@ SWITCH_DECLARE(int32_t) switch_core_session_ctl(switch_session_ctl_t cmd, void *
29632966
newintval = runtime.sps_total;
29642967
switch_mutex_unlock(runtime.throttle_mutex);
29652968
break;
2966-
2969+
case SCSC_UUID_VERSION:
2970+
if(oldintval > 0){
2971+
runtime.uuid_version = oldintval;
2972+
}
2973+
newintval = runtime.uuid_version;
2974+
break;
29672975
case SCSC_RECLAIM:
29682976
switch_core_memory_reclaim_all();
29692977
newintval = 0;

src/switch_uuidv7.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
3+
#include "switch_uuidv7.h"
4+
5+
// #include <assert.h>
6+
// #include <stdio.h>
7+
// #include <string.h>
8+
// #include <time.h>
9+
// #include <unistd.h>
10+
#ifdef __APPLE__
11+
#include <sys/random.h> // for macOS getentropy()
12+
#endif
13+
14+
SWITCH_DECLARE(int) uuidv7_new(uint8_t *uuid_out)
15+
{
16+
int8_t status;
17+
// struct timespec tp;
18+
static uint8_t uuid_prev[16] = {0};
19+
static uint8_t rand_bytes[256] = {0};
20+
static size_t n_rand_consumed = sizeof(rand_bytes);
21+
22+
uint64_t unix_ts_ms ;
23+
// clock_gettime(CLOCK_REALTIME, &tp);
24+
// unix_ts_ms = (uint64_t)tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
25+
unix_ts_ms = switch_time_now() / 1000;
26+
27+
if (n_rand_consumed > sizeof(rand_bytes) - 10)
28+
{
29+
getentropy(rand_bytes, n_rand_consumed);
30+
n_rand_consumed = 0;
31+
}
32+
33+
status = uuidv7_generate(uuid_prev, unix_ts_ms,
34+
&rand_bytes[n_rand_consumed], uuid_prev);
35+
n_rand_consumed += uuidv7_status_n_rand_consumed(status);
36+
37+
memcpy(uuid_out, uuid_prev, 16);
38+
return status;
39+
40+
}

tests/unit/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ switch_xml
4343
switch_estimators
4444
switch_jitter_buffer
4545
test_sofia
46+
switch_core_asr
47+
switch_core_media
48+
switch_sip
49+
switch_rtp_pcap
4650
.deps/
4751
Makefile
4852
conf/*/

0 commit comments

Comments
 (0)