summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2025-03-19 18:15:52 -0400
committerJonathan Bradley <jcb@pikum.xyz>2025-03-20 12:01:56 -0400
commit9cd55867de91013bdbfe0d73112df504eb7963ba (patch)
tree35c7d25805c10a61dd65bd80febedb81e82c50bf
parentbc7e78b1766e8d9c9af1d47c207d084e161db98d (diff)
pkuuid.h: first-pass + bump version to 0.4.1
-rw-r--r--Makefile17
-rw-r--r--config.mk2
-rw-r--r--pk.h.in21
-rw-r--r--pkuuid.h203
-rw-r--r--test/pkuuid.c165
-rw-r--r--test/pkuuid.cpp169
6 files changed, 576 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index fe5107d..0acc65a 100644
--- a/Makefile
+++ b/Makefile
@@ -13,6 +13,7 @@ SRC = \
pkev.h \
pkarr.h \
pkstn.h \
+ pkuuid.h \
test/pkmacros.c \
test/pkmacros.cpp \
test/pkmem-types.c \
@@ -29,6 +30,8 @@ SRC = \
test/pkstn.cpp \
test/pktmr.c\
test/pktmr.cpp \
+ test/pkuuid.c\
+ test/pkuuid.cpp \
OBJ = $(SRC:%.c=.o)
PPOBJ = $(SRC:%.cpp=.so)
@@ -44,6 +47,7 @@ all: options .WAIT clean .WAIT \
pkarr \
pkstn \
pktmr \
+ pkuuid \
test/test-pkmem-types test/test-pkmem-types-cpp \
test/test-pkmem test/test-pkmem-cpp \
test/test-pkmacros test/test-pkmacros-cpp \
@@ -52,6 +56,7 @@ all: options .WAIT clean .WAIT \
test/test-pkarr test/test-pkarr-cpp \
test/test-pkstn test/test-pkstn-cpp \
test/test-pktmr test/test-pktmr-cpp \
+ test/test-pkuuid test/test-pkuuid-cpp \
options:
@echo at-suite build options:
@@ -91,6 +96,8 @@ pkstn: pkstn.gch pkstn.gchpp
pktmr: pktmr.gch pktmr.gchpp
+pkuuid: pkuuid.gch pkuuid.gchpp
+
build: pkmacros
build: pkmem-types
build: pkmem
@@ -109,6 +116,7 @@ build: pktmr
pkarr.h \
pkstn.h \
pktmr.h \
+ pkuuid.h \
>> pk.h
echo "#endif /* PK_SINGLE_HEADER_FILE_H */" >> pk.h
sed -i -r \
@@ -167,6 +175,12 @@ test/test-pktmr: test/pktmr.o
test/test-pktmr-cpp: test/pktmr.so
$(CXX) -g -O3 -std=c++23 $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
+test/test-pkuuid: test/pkuuid.o
+ $(CC) -g -O3 -std=c2x $(CFLAGS) -o $@ $^ $(LDFLAGS)
+
+test/test-pkuuid-cpp: test/pkuuid.so
+ $(CXX) -g -O3 -std=c++23 $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
+
test: pkmacros pkmem-types pkmem pkstr pkev pkarr
test: test/test-pkmacros test/test-pkmacros-cpp
test: test/test-pkmem-types test/test-pkmem-types-cpp
@@ -176,6 +190,7 @@ test: test/test-pkev test/test-pkev-cpp
test: test/test-pkarr test/test-pkarr-cpp
test: test/test-pkstn test/test-pkstn-cpp
test: test/test-pktmr test/test-pktmr-cpp
+test: test/test-pkuuid test/test-pkuuid-cpp
test:
@echo ""
./test/test-pkmacros ; echo Result: $$? "\n"
@@ -194,6 +209,8 @@ test:
./test/test-pkstn-cpp ; echo Result: $$? "\n"
./test/test-pktmr ; echo Result: $$? "\n"
./test/test-pktmr-cpp ; echo Result: $$? "\n"
+ ./test/test-pkuuid ; echo Result: $$? "\n"
+ ./test/test-pkuuid-cpp ; echo Result: $$? "\n"
clean:
rm -f *.plist *.gch *.gchpp *.o *.so test/*.o test/*.so test/test-*
diff --git a/config.mk b/config.mk
index ccca469..d2fb017 100644
--- a/config.mk
+++ b/config.mk
@@ -1,5 +1,5 @@
# pk.h version
-VERSION = 0.4.0
+VERSION = 0.4.1
# paths
PREFIX = /usr/local
diff --git a/pk.h.in b/pk.h.in
index db5a720..94129ab 100644
--- a/pk.h.in
+++ b/pk.h.in
@@ -184,6 +184,24 @@
* `pk_tmr.e` end the end time.
* You could then call the `pk_tmr_duration...` convenience macros as needed.
*
+********************************************************************************
+* pkuuid.h: define PK_IMPL_UUID before including pk.h to enable ad-hoc.
+*
+* Provides a 16-byte unsigned char array struct for uuids.
+*
+* The following definitions (shown with defaults) can be overridden:
+* PK_UUID_CLOCK CLOCK_TAI (preferred, if available)
+* PK_UUID_CLOCK CLOCK_REALTIME (fallback)
+*
+* The `PK_UUID_CLOCK` macro has minimal built-in fallback logic.
+* The uuidv7 specification states that the timestamp portion of the uuid must be
+* a unix epoch, leap seconds EXCLUDED. Only `CLOCK_TAI` meets this requirement
+* on Linux.
+*
+* Note that this currectly calls `srand()` once at startup, and calls `rand()`
+* 2 times for each uuidv7 to fill 74 bits with random data (with an XOR for the
+* remaining 10 bits).
+*
*******************************************************************************/
#define PK_VERSION "@@PK_VERSION@@"
@@ -207,4 +225,7 @@
# ifndef PK_IMPL_STN
# define PK_IMPL_STN
# endif
+# ifndef PK_IMPL_UUID
+# define PK_IMPL_UUID
+# endif
#endif
diff --git a/pkuuid.h b/pkuuid.h
new file mode 100644
index 0000000..7426f79
--- /dev/null
+++ b/pkuuid.h
@@ -0,0 +1,203 @@
+#ifndef PK_UUID_H
+#define PK_UUID_H
+
+#include "stddef.h"
+#include <time.h>
+
+struct pk_uuid {
+ alignas(max_align_t) unsigned char uuid[16];
+};
+
+const struct pk_uuid pk_uuid_zed = { .uuid = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
+const struct pk_uuid pk_uuid_max = { .uuid = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } };
+
+#define pk_uuid_printf_format PK_Q(%.2x%.2x%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x)
+#define pk_uuid_printf_var(id) id.uuid[0], id.uuid[1], id.uuid[2], id.uuid[3], id.uuid[4], id.uuid[5], id.uuid[6], id.uuid[7], id.uuid[8], id.uuid[9], id.uuid[10], id.uuid[11], id.uuid[12], id.uuid[13], id.uuid[14], id.uuid[15]
+
+void pk_uuid_init(time_t srand_seed);
+void pk_uuid_teardown();
+
+struct pk_uuid pk_uuid_new_v7();
+
+#if defined(__cplusplus)
+#include <ostream>
+#include <iomanip>
+std::ostream& operator<<(std::ostream &o, const struct pk_uuid& uuid);
+std::istream& operator>>(std::istream &i, struct pk_uuid& uuid);
+#endif
+
+#endif /* PK_UUID_H */
+
+#ifdef PK_IMPL_UUID
+
+#include <stdatomic.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+// TODO JCB - 2025-03-19
+// This should have platform-specific defines
+#ifndef PK_UUID_CLOCK
+ #ifdef CLOCK_TAI
+ #define PK_UUID_CLOCK CLOCK_TAI
+ #else
+ #define PK_UUID_CLOCK CLOCK_REALTIME
+ #endif
+#endif
+
+void
+pk_uuid_init(time_t srand_seed)
+{
+ // TODO 2025-03-19 - JCB
+ // pk.h should NOT be setting srand.
+ // Replace dependency on rand/srand with a sufficient rand() implementation.
+ // I would prefer if generating a UUID did not advance a global random.
+ // Consider creating a pkrand.h to resolve this.
+ srand(srand_seed);
+}
+
+void
+pk_uuid_teardown()
+{
+}
+
+struct pk_uuid
+pk_uuid_new_v7()
+{
+ const int n = 1;
+ uint32_t r;
+ // https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-7
+ struct pk_uuid ret;
+ struct timespec t;
+ clock_gettime(PK_UUID_CLOCK, &t);
+ uint32_t sec = (uint32_t)t.tv_sec;
+ uint32_t nsec = (uint32_t)t.tv_nsec;
+ // [000-047] (6 bytes) big-endian unix epoch
+ // TODO test this on a big-endian machine, I don't think this is correct.
+ // This `if` determines if we are big or little endian.
+ // A return value of 1 says we are little endian, so swap the bits.
+ if (*(char *)&n == 1) {
+ ret.uuid[0] = (uint8_t)((sec & 0xFF000000) >> 24);
+ ret.uuid[1] = (uint8_t)((sec & 0x00FF0000) >> 16);
+ ret.uuid[2] = (uint8_t)((sec & 0x0000FF00) >> 8);
+ ret.uuid[3] = (uint8_t)((sec & 0x000000FF) >> 0);
+ ret.uuid[4] = (uint8_t)((nsec & 0x0000FF00) >> 8);
+ ret.uuid[5] = (uint8_t)((nsec & 0x000000FF) >> 0);
+ } else {
+ ret.uuid[0] = (uint8_t)((sec & 0xFF000000) >> 0);
+ ret.uuid[1] = (uint8_t)((sec & 0x00FF0000) >> 8);
+ ret.uuid[2] = (uint8_t)((sec & 0x0000FF00) >> 16);
+ ret.uuid[3] = (uint8_t)((sec & 0x000000FF) >> 24);
+ ret.uuid[4] = (uint8_t)((nsec & 0xFF000000) >> 0);
+ ret.uuid[5] = (uint8_t)((nsec & 0x00FF0000) >> 8);
+ }
+ // [052-127] random
+ r = (uint32_t)rand();
+ if (*(char *)&n == 1) {
+ ret.uuid[8] = (uint8_t)((r & 0xFF000000) >> 24);
+ ret.uuid[9] = (uint8_t)((r & 0x00FF0000) >> 16);
+ ret.uuid[10] = (uint8_t)((r & 0x0000FF00) >> 8);
+ ret.uuid[11] = (uint8_t)((r & 0x000000FF) >> 0);
+ } else {
+ ret.uuid[8] = (uint8_t)((r & 0xFF000000) >> 0);
+ ret.uuid[9] = (uint8_t)((r & 0x00FF0000) >> 8);
+ ret.uuid[10] = (uint8_t)((r & 0x0000FF00) >> 16);
+ ret.uuid[11] = (uint8_t)((r & 0x000000FF) >> 24);
+ }
+ r = rand();
+ if (*(char *)&n == 1) {
+ ret.uuid[12] = (uint8_t)((r & 0xFF000000) >> 24);
+ ret.uuid[13] = (uint8_t)((r & 0x00FF0000) >> 16);
+ ret.uuid[14] = (uint8_t)((r & 0x0000FF00) >> 8);
+ ret.uuid[15] = (uint8_t)((r & 0x000000FF) >> 0);
+ } else {
+ ret.uuid[12] = (uint8_t)((r & 0xFF000000) >> 0);
+ ret.uuid[13] = (uint8_t)((r & 0x00FF0000) >> 8);
+ ret.uuid[14] = (uint8_t)((r & 0x0000FF00) >> 16);
+ ret.uuid[15] = (uint8_t)((r & 0x000000FF) >> 24);
+ }
+ ret.uuid[6] = ret.uuid[9] ^ ret.uuid[12];
+ ret.uuid[7] = ret.uuid[10] ^ ret.uuid[15];
+
+ // [048-051] v7 nibble
+ // version must be 0x7_
+ // 0x70 is 0b01110000
+ // 0x7F is 0b01111111
+ ret.uuid[6] |= 0x70;
+ ret.uuid[6] &= 0x7F;
+
+ // [064-065] 2-bit variant field
+ // variant must be 0b10
+ // 0x80 is 0b10000000
+ // 0xBF is 0b10111111
+ ret.uuid[8] |= 0x80;
+ ret.uuid[8] &= 0xBF;
+
+ return ret;
+}
+
+#if defined(__cplusplus)
+#include "./pkstn.h" /* deleteme */
+std::ostream&
+operator<<(std::ostream &o, const struct pk_uuid& uuid)
+{
+ int i;
+ o << std::hex;
+ for (i = 0; i < 4; ++i) {
+ o << std::setw(2) << std::setfill('0');
+ o << (uint16_t)uuid.uuid[i];
+ }
+ o << "-";
+ for (i = 4; i < 6; ++i) {
+ o << std::setw(2) << std::setfill('0');
+ o << (uint16_t)uuid.uuid[i];
+ }
+ o << "-";
+ for (i = 6; i < 8; ++i) {
+ o << std::setw(2) << std::setfill('0');
+ o << (uint16_t)uuid.uuid[i];
+ }
+ o << "-";
+ for (i = 8; i < 10; ++i) {
+ o << std::setw(2) << std::setfill('0');
+ o << (uint16_t)uuid.uuid[i];
+ }
+ o << "-";
+ for (i = 10; i < 16; ++i) {
+ o << std::setw(2) << std::setfill('0');
+ o << (uint16_t)uuid.uuid[i];
+ }
+ return o;
+}
+
+std::istream&
+operator>>(std::istream &i, struct pk_uuid& uuid)
+{
+ char c[3];
+ char k = 0;
+ char offset = 0;
+ c[2] = '\0';
+ for (k = 0; k < 20; ++k) {
+ if (k == 4 || k == 7 || k == 10 || k == 13) {
+ offset += 1;
+ c[0] = i.peek();
+ if (c[0] != '-') {
+ goto err_out;
+ }
+ i.get(); // burn
+ continue;
+ }
+ i.get(c[0]);
+ i.get(c[1]);
+ if (pk_stn_uint8_t(&uuid.uuid[k - offset], c, 16) != PK_STN_RES_SUCCESS) {
+ goto err_out;
+ }
+ }
+ return i;
+err_out:
+ i.seekg(-(((k + 1) * 2) + offset), std::ios_base::cur);
+ uuid = pk_uuid_zed;
+ return i;
+}
+#endif
+
+#endif
diff --git a/test/pkuuid.c b/test/pkuuid.c
new file mode 100644
index 0000000..ee550cf
--- /dev/null
+++ b/test/pkuuid.c
@@ -0,0 +1,165 @@
+
+#define PK_IMPL_UUID
+#define PK_IMPL_TMR
+// #define PK_UUID_CLOCK CLOCK_REALTIME_ALARM
+
+#include "../pkmacros.h"
+#include "../pkuuid.h"
+
+#include "../pktmr.h"
+
+#include <stdio.h>
+
+void
+print_uuid_v7_breakdown(FILE *f, struct pk_uuid id)
+{
+ fprintf(f, "-------------------------------------------\n");
+ fprintf(f, "field bits value\n");
+ fprintf(f, "-------------------------------------------\n");
+ fprintf(f, "unix_ts_ms 48 0x");
+ fprintf(f, "%.2x", id.uuid[0]);
+ fprintf(f, "%.2x", id.uuid[1]);
+ fprintf(f, "%.2x", id.uuid[2]);
+ fprintf(f, "%.2x", id.uuid[3]);
+ fprintf(f, "%.2x", id.uuid[4]);
+ fprintf(f, "%.2x", id.uuid[5]);
+ fprintf(f, "\n");
+
+ fprintf(f, "ver 4 0x");
+ fprintf(f, "%.x", (id.uuid[6] & 0xF0) >> 4);
+ fprintf(f, "\n");
+
+ fprintf(f, "rand_a 12 0x");
+ fprintf(f, "%.1x", (id.uuid[6] & 0x0F));
+ fprintf(f, "%.2x", id.uuid[7]);
+ fprintf(f, "\n");
+
+ fprintf(f, "var 2 0b");
+ fprintf(f, "%.c", (id.uuid[8] & 0x80) ? '1' : '0');
+ fprintf(f, "%.c", (id.uuid[8] & 0x40) ? '1' : '0');
+ fprintf(f, "\n");
+
+ fprintf(f, "rand_b 62 0b");
+ fprintf(f, "%.c", (id.uuid[8] & 0x20) ? '1' : '0');
+ fprintf(f, "%.c", (id.uuid[8] & 0x10) ? '1' : '0');
+ fprintf(f, ", 0x");
+ fprintf(f, "%.x", (id.uuid[8] & 0x0F));
+ fprintf(f, "%.2x", id.uuid[9]);
+ fprintf(f, "%.2x", id.uuid[10]);
+ fprintf(f, "%.2x", id.uuid[11]);
+ fprintf(f, "%.2x", id.uuid[12]);
+ fprintf(f, "%.2x", id.uuid[13]);
+ fprintf(f, "%.2x", id.uuid[14]);
+ fprintf(f, "%.2x", id.uuid[15]);
+ fprintf(f, "\n");
+
+ fprintf(f, "-------------------------------------------\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv;
+ pk_uuid_init(time(NULL));
+
+ fprintf(stdout, "\n");
+ {
+ struct pk_uuid id;
+ id = pk_uuid_zed;
+ fprintf(stdout, "[%s] zed: " pk_uuid_printf_format "\n", __FILE__, pk_uuid_printf_var(id));
+ }
+
+ {
+ struct pk_uuid id;
+ id = pk_uuid_max;
+ fprintf(stdout, "[%s] one: " pk_uuid_printf_format "\n", __FILE__, pk_uuid_printf_var(id));
+ }
+
+ {
+ const int count = 4;
+ struct pk_uuid ids[count];
+
+ for (int i = 0; i < count; ++i) {
+ ids[i] = pk_uuid_new_v7();
+ }
+ for (int i = 0; i < count; ++i) {
+ fprintf(stdout, "[%s] new: " pk_uuid_printf_format "\n", __FILE__, pk_uuid_printf_var(ids[i]));
+ }
+
+ for (int i = 0; i < count; ++i) {
+ fprintf(stdout, "\n");
+ print_uuid_v7_breakdown(stdout, ids[i]);
+ fprintf(stdout, "[%s] new: " pk_uuid_printf_format "\n", __FILE__, pk_uuid_printf_var(ids[i]));
+ }
+ fprintf(stdout, "\n");
+ }
+
+ {
+ double ms;
+ const int count = 1000;
+ struct pk_uuid ids[count];
+ struct pk_tmr tmr;
+ pk_tmr_start(tmr);
+ for (int i = 0; i < count; ++i) {
+ ids[i] = pk_uuid_new_v7();
+ }
+ pk_tmr_stop(tmr);
+ fprintf(stdout, "generated %i ids...\n", count);
+ ms = pk_tmr_duration_dbl_mili(tmr);
+ fprintf(stdout, "elapsed time : %f ms\n", ms);
+ fprintf(stdout, "elapsed time (avg): %f ms\n", ms / (double)count);
+ fprintf(stdout, pk_uuid_printf_format "\n", pk_uuid_printf_var(ids[0]));
+ fprintf(stdout, pk_uuid_printf_format "\n", pk_uuid_printf_var(ids[99]));
+ }
+
+ // not really a test, just checking time
+ /* 2025-03-19 JCB - Disabled because we added a macro to define the clock
+ * instead of passing it as a parameter.
+ {
+ double ms;
+ const int count = 1000;
+ struct pk_uuid ids[count];
+ struct pk_tmr tmr;
+ pk_tmr_start(tmr);
+ for (int i = 0; i < count; ++i) {
+ ids[i] = pk_uuid_new_v7(CLOCK_REALTIME);
+ }
+ pk_tmr_stop(tmr);
+ fprintf(stdout, "generated %i ids using CLOCK_REALTIME...\n", count);
+ ms = pk_tmr_duration_dbl_mili(tmr);
+ fprintf(stdout, "elapsed time : %f ms\n", ms);
+ fprintf(stdout, "elapsed time (avg): %f ms\n", ms / (double)count);
+ fprintf(stdout, pk_uuid_printf_format "\n", pk_uuid_printf_var(ids[0]));
+ fprintf(stdout, pk_uuid_printf_format "\n", pk_uuid_printf_var(ids[99]));
+
+ pk_tmr_start(tmr);
+ for (int i = 0; i < count; ++i) {
+ ids[i] = pk_uuid_new_v7(CLOCK_REALTIME_COARSE);
+ }
+ pk_tmr_stop(tmr);
+ fprintf(stdout, "generated %i ids using CLOCK_REALTIME_COARSE...\n", count);
+ ms = pk_tmr_duration_dbl_mili(tmr);
+ fprintf(stdout, "elapsed time : %f ms\n", ms);
+ fprintf(stdout, "elapsed time (avg): %f ms\n", ms / (double)count);
+ fprintf(stdout, pk_uuid_printf_format "\n", pk_uuid_printf_var(ids[0]));
+ fprintf(stdout, pk_uuid_printf_format "\n", pk_uuid_printf_var(ids[99]));
+
+ pk_tmr_start(tmr);
+ for (int i = 0; i < count; ++i) {
+ ids[i] = pk_uuid_new_v7(CLOCK_TAI);
+ }
+ pk_tmr_stop(tmr);
+ fprintf(stdout, "generated %i ids using CLOCK_TAI...\n", count);
+ ms = pk_tmr_duration_dbl_mili(tmr);
+ fprintf(stdout, "elapsed time : %f ms\n", ms);
+ fprintf(stdout, "elapsed time (avg): %f ms\n", ms / (double)count);
+ fprintf(stdout, pk_uuid_printf_format "\n", pk_uuid_printf_var(ids[0]));
+ fprintf(stdout, pk_uuid_printf_format "\n", pk_uuid_printf_var(ids[99]));
+ }
+ */
+
+ pk_uuid_teardown();
+
+ return 0;
+}
diff --git a/test/pkuuid.cpp b/test/pkuuid.cpp
new file mode 100644
index 0000000..278cd02
--- /dev/null
+++ b/test/pkuuid.cpp
@@ -0,0 +1,169 @@
+
+#define PK_IMPL_UUID
+#define PK_IMPL_TMR
+// #define PK_UUID_CLOCK CLOCK_REALTIME_ALARM
+
+#include "../pkmacros.h"
+#include "../pkuuid.h"
+
+#include "../pktmr.h"
+
+#include <iostream>
+#include <sstream>
+#include <stdio.h>
+
+void
+print_uuid_v7_breakdown(FILE *f, struct pk_uuid id)
+{
+ fprintf(f, "-------------------------------------------\n");
+ fprintf(f, "field bits value\n");
+ fprintf(f, "-------------------------------------------\n");
+ fprintf(f, "unix_ts_ms 48 0x");
+ fprintf(f, "%.2x", id.uuid[0]);
+ fprintf(f, "%.2x", id.uuid[1]);
+ fprintf(f, "%.2x", id.uuid[2]);
+ fprintf(f, "%.2x", id.uuid[3]);
+ fprintf(f, "%.2x", id.uuid[4]);
+ fprintf(f, "%.2x", id.uuid[5]);
+ fprintf(f, "\n");
+
+ fprintf(f, "ver 4 0x");
+ fprintf(f, "%.x", (id.uuid[6] & 0xF0) >> 4);
+ fprintf(f, "\n");
+
+ fprintf(f, "rand_a 12 0x");
+ fprintf(f, "%.1x", (id.uuid[6] & 0x0F));
+ fprintf(f, "%.2x", id.uuid[7]);
+ fprintf(f, "\n");
+
+ fprintf(f, "var 2 0b");
+ fprintf(f, "%.c", (id.uuid[8] & 0x80) ? '1' : '0');
+ fprintf(f, "%.c", (id.uuid[8] & 0x40) ? '1' : '0');
+ fprintf(f, "\n");
+
+ fprintf(f, "rand_b 62 0b");
+ fprintf(f, "%.c", (id.uuid[8] & 0x20) ? '1' : '0');
+ fprintf(f, "%.c", (id.uuid[8] & 0x10) ? '1' : '0');
+ fprintf(f, ", 0x");
+ fprintf(f, "%.x", (id.uuid[8] & 0x0F));
+ fprintf(f, "%.2x", id.uuid[9]);
+ fprintf(f, "%.2x", id.uuid[10]);
+ fprintf(f, "%.2x", id.uuid[11]);
+ fprintf(f, "%.2x", id.uuid[12]);
+ fprintf(f, "%.2x", id.uuid[13]);
+ fprintf(f, "%.2x", id.uuid[14]);
+ fprintf(f, "%.2x", id.uuid[15]);
+ fprintf(f, "\n");
+
+ fprintf(f, "-------------------------------------------\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv;
+ pk_uuid_init(time(NULL));
+ const struct pk_uuid example_id = { .uuid = { 0x01, 0x7F, 0x22, 0xE2, 0x79, 0xB0, 0x7C, 0xC3, 0x98, 0xC4, 0xDC, 0x0C, 0x0C, 0x07, 0x39, 0x8F } };
+
+ fprintf(stdout, "\n");
+ {
+ struct pk_uuid id;
+ id = pk_uuid_zed;
+ fprintf(stdout, "[%s] zed: " pk_uuid_printf_format "\n", __FILE__, pk_uuid_printf_var(id));
+ }
+
+ {
+ struct pk_uuid id;
+ id = pk_uuid_max;
+ fprintf(stdout, "[%s] one: " pk_uuid_printf_format "\n", __FILE__, pk_uuid_printf_var(id));
+ }
+
+ {
+ const int count = 4;
+ struct pk_uuid ids[count];
+
+ for (int i = 0; i < count; ++i) {
+ ids[i] = pk_uuid_new_v7();
+ }
+ for (int i = 0; i < count; ++i) {
+ fprintf(stdout, "[%s] new: " pk_uuid_printf_format "\n", __FILE__, pk_uuid_printf_var(ids[i]));
+ }
+
+ for (int i = 0; i < count; ++i) {
+ fprintf(stdout, "\n");
+ print_uuid_v7_breakdown(stdout, ids[i]);
+ fprintf(stdout, "[%s] new: " pk_uuid_printf_format "\n", __FILE__, pk_uuid_printf_var(ids[i]));
+ }
+ fprintf(stdout, "\n");
+ }
+
+ {
+ double ms;
+ const int count = 1000;
+ struct pk_uuid ids[count];
+ struct pk_tmr tmr;
+ pk_tmr_start(tmr);
+ for (int i = 0; i < count; ++i) {
+ ids[i] = pk_uuid_new_v7();
+ }
+ pk_tmr_stop(tmr);
+ fprintf(stdout, "generated %i ids...\n", count);
+ ms = pk_tmr_duration_dbl_mili(tmr);
+ fprintf(stdout, "elapsed time : %f ms\n", ms);
+ fprintf(stdout, "elapsed time (avg): %f ms\n", ms / (double)count);
+ fprintf(stdout, pk_uuid_printf_format "\n", pk_uuid_printf_var(ids[0]));
+ fprintf(stdout, pk_uuid_printf_format "\n", pk_uuid_printf_var(ids[99]));
+ }
+
+ // <<
+ {
+ std::stringstream ss;
+ ss << example_id;
+
+ fprintf(stdout, "\n");
+ fprintf(stdout, "[%s] expected: 017f22e2-79b0-7cc3-98c4-dc0c0c07398f\n", __FILE__);
+ fprintf(stdout, "[%s] result : %s\n", __FILE__, ss.str().c_str());
+ if (ss.str() != "017f22e2-79b0-7cc3-98c4-dc0c0c07398f") return 1;
+ }
+
+ // >>
+ {
+ struct pk_uuid id;
+ std::istringstream ssi("017f22e2-79b0-7cc3-98c4-dc0c0c07398f");
+
+ ssi >> id;
+ ssi >> std::cout.rdbuf();
+
+ fprintf(stdout, "\n");
+ fprintf(stdout, "[%s] excpected: 017f22e2-79b0-7cc3-98c4-dc0c0c07398f\n", __FILE__);
+ fprintf(stdout, "[%s] result : " pk_uuid_printf_format "\n", __FILE__, pk_uuid_printf_var(id));
+
+ for (int i = 0; i < 16; ++i) {
+ if (example_id.uuid[i] != id.uuid[i]) return 1;
+ }
+ }
+
+ // >> err
+ {
+ struct pk_uuid id;
+ std::istringstream ssi("gnud");
+
+ ssi >> id;
+
+ fprintf(stdout, "\n");
+ fprintf(stdout, "[%s] leftovers: ", __FILE__);
+ ssi >> std::cout.rdbuf();
+ std::cout << std::endl;
+ fprintf(stdout, "[%s] excpected: 00000000-0000-0000-0000-000000000000\n", __FILE__);
+ fprintf(stdout, "[%s] result : " pk_uuid_printf_format "\n", __FILE__, pk_uuid_printf_var(id));
+
+ for (int i = 0; i < 16; ++i) {
+ if (pk_uuid_zed.uuid[i] != id.uuid[i]) return 1;
+ }
+ }
+
+ pk_uuid_teardown();
+
+ return 0;
+}