#include #include #include #include #include #include #define UUID_T_LENGTH (16) #define UNIX_TS_LENGTH (6) #define RAND_A_LENGTH (2) #define RAND_B_LENGTH (8) typedef uint8_t uuid_t[UUID_T_LENGTH]; static struct { uint64_t unix_ts; uint8_t rand_a[RAND_A_LENGTH]; uint8_t rand_b[RAND_B_LENGTH]; } state; uint64_t get_milliseconds(void) { struct timespec tp; clock_gettime(CLOCK_REALTIME, &tp); return ((tp.tv_sec * 1000) + (tp.tv_nsec / 1000000)); } void get_random_bytes(uint8_t buffer[], size_t len) { getentropy(buffer, len); if (errno != EXIT_SUCCESS) { exit(EXIT_FAILURE); } } void create_uuid7_stateless(uuid_t uuid) { static const int rand_ab_length = RAND_A_LENGTH + RAND_B_LENGTH; // get the timestamp bytes uint64_t unix_ts = get_milliseconds(); for(int i = 0; i < UNIX_TS_LENGTH; i++) { uuid[UNIX_TS_LENGTH - 1 - i] = (uint8_t) (unix_ts >> (8 * i)); } // get the random bytes uint8_t rand_ab[rand_ab_length]; get_random_bytes(rand_ab, rand_ab_length); for(int i = 0; i < rand_ab_length; i++) { uuid[UNIX_TS_LENGTH + i] = rand_ab[i]; } // set version and variant uuid[6] = 0x70 | (uuid[6] & 0x0f); // version 7 uuid[8] = 0x80 | (uuid[8] & 0x3f); // variant 2 } void create_uuid7_stateful_method1_type1(uuid_t uuid) { // get the timestamp bytes uint64_t unix_ts = get_milliseconds(); for(int i = 0; i < UNIX_TS_LENGTH; i++) { uuid[UNIX_TS_LENGTH - 1 - i] = (uint8_t) (unix_ts >> (8 * i)); } // get the rand_a bytes if (unix_ts > state.unix_ts) { get_random_bytes(state.rand_a, RAND_A_LENGTH); } else { // increment rand_a bytes from right to left for (int i = RAND_A_LENGTH - 1; i >= 0; i--) { if (++state.rand_a[i] != 0x00) { break; } } } for(int i = 0; i < RAND_A_LENGTH; i++) { uuid[UNIX_TS_LENGTH + i] = state.rand_a[i]; } // get the rand_b bytes get_random_bytes(state.rand_b, RAND_B_LENGTH); for(int i = 0; i < RAND_B_LENGTH; i++) { uuid[UNIX_TS_LENGTH + RAND_A_LENGTH + i] = state.rand_b[i]; } // save the last timestamp state.unix_ts = unix_ts; // set version and variant uuid[6] = 0x70 | (uuid[6] & 0x0f); // version 7 uuid[8] = 0x80 | (uuid[8] & 0x3f); // variant 2 } void create_uuid7_stateful_method1_type2(uuid_t uuid) { // get the timestamp bytes uint64_t unix_ts = get_milliseconds(); for(int i = 0; i < UNIX_TS_LENGTH; i++) { uuid[UNIX_TS_LENGTH - 1 - i] = (uint8_t) (unix_ts >> (8 * i)); } // get the rand_a bytes if (unix_ts > state.unix_ts) { get_random_bytes(state.rand_a, RAND_A_LENGTH); } else { // get random byte uint8_t increment[1]; get_random_bytes(increment, 1); // set increment between 1 and 255 const uint8_t increment_max = 0xff; increment[0] = increment[0] % increment_max + 1; // increment rand_a bytes from right to left if ((uint32_t) state.rand_a[1] + (uint32_t) increment[0] <= increment_max) { state.rand_a[1] += increment[0]; } else { state.rand_a[1] += increment[0]; state.rand_a[0] += 1; } } for(int i = 0; i < RAND_A_LENGTH; i++) { uuid[UNIX_TS_LENGTH + i] = state.rand_a[i]; } // get the rand_b bytes get_random_bytes(state.rand_b, RAND_B_LENGTH); for(int i = 0; i < RAND_B_LENGTH; i++) { uuid[UNIX_TS_LENGTH + RAND_A_LENGTH + i] = state.rand_b[i]; } // save the last timestamp state.unix_ts = unix_ts; // set version and variant uuid[6] = 0x70 | (uuid[6] & 0x0f); // version 7 uuid[8] = 0x80 | (uuid[8] & 0x3f); // variant 2 } void create_uuid7_stateful_method2_type1(uuid_t uuid) { // get the timestamp bytes uint64_t unix_ts = get_milliseconds(); for(int i = 0; i < UNIX_TS_LENGTH; i++) { uuid[UNIX_TS_LENGTH - 1 - i] = (uint8_t) (unix_ts >> (8 * i)); } // get the random bytes if (unix_ts > state.unix_ts) { get_random_bytes(state.rand_a, RAND_A_LENGTH); get_random_bytes(state.rand_b, RAND_B_LENGTH); } else { // increment rand_b bytes from right to left for (int i = RAND_B_LENGTH - 1; i >= 0; i--) { if (++state.rand_b[i] != 0x00) { break; } } } for(int i = 0; i < RAND_A_LENGTH; i++) { uuid[UNIX_TS_LENGTH + i] = state.rand_a[i]; } for(int i = 0; i < RAND_B_LENGTH; i++) { uuid[UNIX_TS_LENGTH + RAND_A_LENGTH + i] = state.rand_b[i]; } // save the last timestamp state.unix_ts = unix_ts; // set version and variant uuid[6] = 0x70 | (uuid[6] & 0x0f); // version 7 uuid[8] = 0x80 | (uuid[8] & 0x3f); // variant 2 } void create_uuid7_stateful_method2_type2(uuid_t uuid) { // get the timestamp bytes uint64_t unix_ts = get_milliseconds(); for(int i = 0; i < UNIX_TS_LENGTH; i++) { uuid[UNIX_TS_LENGTH - 1 - i] = (uint8_t) (unix_ts >> (8 * i)); } // get the random bytes if (unix_ts > state.unix_ts) { get_random_bytes(state.rand_a, RAND_A_LENGTH); get_random_bytes(state.rand_b, RAND_B_LENGTH); } else { // get random byte uint8_t increment[1]; get_random_bytes(increment, 1); // set increment between 1 and 255 const uint8_t increment_max = 0xff; increment[0] = increment[0] % increment_max + 1; // increment rand_b bytes from right to left if ((uint32_t) state.rand_b[7] + (uint32_t) increment[0] <= increment_max) { state.rand_b[7] += increment[0]; } else { state.rand_b[7] += increment[0]; for (int i = RAND_B_LENGTH - 2; i >= 0; i--) { if (++state.rand_b[i] != 0x00) { break; } } } } for(int i = 0; i < RAND_A_LENGTH; i++) { uuid[UNIX_TS_LENGTH + i] = state.rand_a[i]; } for(int i = 0; i < RAND_B_LENGTH; i++) { uuid[UNIX_TS_LENGTH + RAND_A_LENGTH + i] = state.rand_b[i]; } // save the last timestamp state.unix_ts = unix_ts; // set version and variant uuid[6] = 0x70 | (uuid[6] & 0x0f); // version 7 uuid[8] = 0x80 | (uuid[8] & 0x3f); // variant 2 } void print_uuid(uuid_t uuid) { static const char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; int s = 0; char str[36 + 1]; for(int i = 0; i < UUID_T_LENGTH; i++) { if(i == 4 || i == 6 || i == 8 || i == 10) { str[s++] = '-'; } str[s++] = hex[uuid[i] >> 4]; str[s++] = hex[uuid[i] & 0x0f]; } str[s++] ='\0'; printf("%s\n", str); } int main() { printf("\n"); printf("Stateless UUID v7:\n\n"); for(int i = 0; i < 10; i++) { uuid_t uuid; create_uuid7_stateless(uuid); print_uuid(uuid); } printf(" ^^^^ ^^^^ ^^^^^^^^^^^^ <- always random\n\n"); printf("Stateful UUID v7 using Method 1 and Type A:\n\n"); for(int i = 0; i < 10; i++) { uuid_t uuid; create_uuid7_stateful_method1_type1(uuid); print_uuid(uuid); } printf(" ^^^^ ^^^^^^^^^^^^ <- always random\n"); printf(" ^^^^ <- plus 1\n\n"); printf("Stateful UUID v7 using Method 1 and Type B:\n\n"); for(int i = 0; i < 10; i++) { uuid_t uuid; create_uuid7_stateful_method1_type2(uuid); print_uuid(uuid); } printf(" ^^^^ ^^^^^^^^^^^^ <- always random\n"); printf(" ^^^^ <- plus n, where 1 <= n <= 255\n\n"); printf("Stateful UUID v7 using Method 2 and Type A:\n\n"); for(int i = 0; i < 10; i++) { uuid_t uuid; create_uuid7_stateful_method2_type1(uuid); print_uuid(uuid); } printf(" ^^^^ ^^^^ ^^^^^^^^^^^^ <- plus 1\n\n"); printf("Stateful UUID v7 using Method 2 and Type B:\n\n"); for(int i = 0; i < 10; i++) { uuid_t uuid; create_uuid7_stateful_method2_type2(uuid); print_uuid(uuid); } printf(" ^^^^ ^^^^ ^^^^^^^^^^^^ <- plus n, where 1 <= n <= 255\n\n"); } /* ## OUTPUT: Stateless UUID v7: 017fb8c8-040e-747d-b7b0-4484f54bdcf3 017fb8c8-040e-767f-9e95-260c79ad415d 017fb8c8-040e-7a67-a735-b2a6c1a226e3 017fb8c8-040e-7d89-b4da-de1d91276d32 017fb8c8-040e-7337-9317-4b8acf8113a6 017fb8c8-040e-72f7-bc9b-bbcfbf73782b 017fb8c8-040e-7e00-b5ae-3518d2bcb3fa 017fb8c8-040e-7c7a-8cd2-04264d191231 017fb8c8-040e-72d7-9117-e155c3fe2ed1 017fb8c8-040e-7b47-beff-3ac66dbfe08a ^^^^ ^^^^ ^^^^^^^^^^^^ <- always random Stateful UUID v7 using Method 1 and Type A: 017fb8c8-040e-7635-b82d-6c1a912cc929 017fb8c8-040e-7636-81bf-2beb1b820814 017fb8c8-040e-7637-965f-a62f7582fcb5 017fb8c8-040e-7638-ad73-b13aec618618 017fb8c8-040e-7639-9406-e0a03537c37a 017fb8c8-040e-763a-a2e3-5413d99b84d4 017fb8c8-040e-763b-b362-2fe9d7946abe 017fb8c8-040e-763c-9f77-efd19f534e6e 017fb8c8-040f-7a47-b67c-2f449248fbf3 017fb8c8-040f-7a48-a8db-8aa3594b1edc ^^^^ ^^^^^^^^^^^^ <- always random ^^^^ <- plus 1 Stateful UUID v7 using Method 1 and Type B: 017fb8c8-040f-7aa8-b5e3-5dc7dc6e4c2a 017fb8c8-040f-7b30-96f8-f5c225eef8ba 017fb8c8-040f-7b93-8743-940cbf19a388 017fb8c8-040f-7c5e-b21d-f0d079cbc05d 017fb8c8-040f-7ca1-bc08-7c8d4e43e8b4 017fb8c8-040f-7cb0-974d-49932443f329 017fb8c8-040f-7cc9-865f-9d2262b4eee3 017fb8c8-040f-7dae-b084-b5e5a03289a5 017fb8c8-040f-7e77-a839-a3f4c3802a07 017fb8c8-040f-7f4d-913c-8ce5c548298b ^^^^ ^^^^^^^^^^^^ <- always random ^^^^ <- plus n, where 1 <= n <= 255 Stateful UUID v7 using Method 2 and Type A: 017fb8c8-040f-7f4d-913c-8ce5c548298c 017fb8c8-040f-7f4d-913c-8ce5c548298d 017fb8c8-040f-7f4d-913c-8ce5c548298e 017fb8c8-040f-7f4d-913c-8ce5c548298f 017fb8c8-040f-7f4d-913c-8ce5c5482990 017fb8c8-040f-7f4d-913c-8ce5c5482991 017fb8c8-040f-7f4d-913c-8ce5c5482992 017fb8c8-040f-7f4d-913c-8ce5c5482993 017fb8c8-040f-7f4d-913c-8ce5c5482994 017fb8c8-040f-7f4d-913c-8ce5c5482995 ^^^^ ^^^^ ^^^^^^^^^^^^ <- plus 1 Stateful UUID v7 using Method 2 and Type B: 017fb8c8-040f-7f4d-913c-8ce5c54829e9 017fb8c8-040f-7f4d-913c-8ce5c5482a03 017fb8c8-040f-7f4d-913c-8ce5c5482a71 017fb8c8-040f-7f4d-913c-8ce5c5482ac2 017fb8c8-040f-7f4d-913c-8ce5c5482b7b 017fb8c8-040f-7f4d-913c-8ce5c5482bbf 017fb8c8-040f-7f4d-913c-8ce5c5482bd6 017fb8c8-040f-7f4d-913c-8ce5c5482c0d 017fb8c8-040f-7f4d-913c-8ce5c5482c32 017fb8c8-040f-7f4d-913c-8ce5c5482cdd ^^^^ ^^^^ ^^^^^^^^^^^^ <- plus n, where 1 <= n <= 255 */