diff options
| author | Jonathan Bradley <jcb@pikum.xyz> | 2025-03-20 11:06:50 -0400 |
|---|---|---|
| committer | Jonathan Bradley <jcb@pikum.xyz> | 2025-03-21 11:04:43 -0400 |
| commit | 9b39b4c8eab360e087423f06ecffb694a3b98b23 (patch) | |
| tree | d35a32bc037bd9ed590d22022447fc11afb04e25 /src/pk.h | |
| parent | 95ee6f829b9ba379e883f4295d96dad5a0c738dd (diff) | |
pke: update pk.h to 0.4.2
Diffstat (limited to 'src/pk.h')
| -rw-r--r-- | src/pk.h | 330 |
1 files changed, 328 insertions, 2 deletions
@@ -1,7 +1,7 @@ #ifndef PK_SINGLE_HEADER_FILE_H #define PK_SINGLE_HEADER_FILE_H /******************************************************************************* -* PK Single-Header-Library V0.4.0 +* PK Single-Header-Library V0.4.2 * * Author: Jonathan Bradley * Copyright: © 2024-2025 Jonathan Bradley @@ -145,6 +145,7 @@ * * Initialize `stride`, `alignment`, and `bkt` (optional) members * *before* calling any `pk_arr_*` methods. +* Alternatively, if using c++, use the template ctor. * * Examples: * ``` c @@ -155,6 +156,11 @@ * pk_arr_reserve(&arr, 10); // optional * pk_arr_append(&arr, &obj); * ``` +* ``` c++ +* struct pk_arr<some_type> arr(bkt); +* pk_arr_reserve(&arr, 10); // optional +* pk_arr_append(&arr, &obj); +* ``` * ``` c * struct pk_arr arr = {0}; * arr.stride = sizeof(obj); // required @@ -164,6 +170,11 @@ * obj* d = (obj*)arr->data; * d[0] = ...; * ``` +* ``` c++ +* struct pk_arr_t<some_type> arr(); +* pk_arr_resize(&arr, 10); +* arr[0] = {}; +* ``` * ******************************************************************************** * pkstn.h: def PK_IMPL_STN before including pk.h to enable ad-hoc. @@ -185,9 +196,27 @@ * `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 "0.4.0" +#define PK_VERSION "0.4.2" #ifdef PK_IMPL_ALL # ifndef PK_IMPL_MEM_TYPES @@ -208,6 +237,9 @@ # ifndef PK_IMPL_STN # define PK_IMPL_STN # endif +# ifndef PK_IMPL_UUID +# define PK_IMPL_UUID +# endif #endif #ifndef PK_MACROS_H #define PK_MACROS_H @@ -1597,8 +1629,82 @@ void pk_arr_resize(struct pk_arr *arr, uint32_t count); void pk_arr_move_to_back(struct pk_arr *arr, uint32_t index); void pk_arr_append(struct pk_arr *arr, void *data); void pk_arr_remove_at(struct pk_arr *arr, uint32_t index); +void pk_arr_clone(struct pk_arr *lhs, struct pk_arr *rhs); +void pk_arr_swap(struct pk_arr *lhs, struct pk_arr *rhs); uint32_t pk_arr_find_first_index(struct pk_arr *arr, void *user_data, pk_arr_item_compare *fn); +#if defined(__cplusplus) +template<typename T> +struct pk_arr_t : public pk_arr { + pk_arr_t(); + pk_arr_t(struct pk_membucket *bkt); + pk_arr_t(const pk_arr_t<T> &other); + pk_arr_t(pk_arr_t<T> &&other); + pk_arr_t &operator=(const pk_arr_t<T> &other); + pk_arr_t &operator=(pk_arr_t<T> &&other); + ~pk_arr_t(); + T &operator[](size_t index); +}; +template<typename T> +pk_arr_t<T>::pk_arr_t() { + this->next = 0; + this->reserved = 0; + this->stride = sizeof(T); + this->alignment = alignof(T); + this->bkt = NULL; + this->data = NULL; +} +template<typename T> +pk_arr_t<T>::pk_arr_t(struct pk_membucket *bkt) : pk_arr_t<T>() { + this->bkt = bkt; +} +template<typename T> +pk_arr_t<T>::pk_arr_t(const pk_arr_t<T> &other) { + // copy ctor + pk_arr_clone(static_cast<struct pk_arr_t *>(&const_cast<pk_arr_t<T>&>(other)), this); +} +template<typename T> +pk_arr_t<T>::pk_arr_t(pk_arr_t<T> &&other) { + // move ctor + pk_arr_swap(this, &other); + other.data = NULL; +} +template<typename T> +pk_arr_t<T> & +pk_arr_t<T>::operator=(const pk_arr_t<T> &other) { + // copy assignment + if (this->data != NULL) { + pk_arr_reset(this); + } + pk_arr_clone(static_cast<struct pk_arr_t *>(&const_cast<pk_arr_t<T>&>(other)), this); + return *this; +} +template<typename T> +pk_arr_t<T> & +pk_arr_t<T>::operator=(pk_arr_t<T> &&other) { + // move assignment + if (this->data != NULL) { + pk_arr_reset(this); + } + pk_arr_swap(this, &other); + other.data = NULL; + return *this; +} +template<typename T> +pk_arr_t<T>::~pk_arr_t() { + if (this->data != NULL) pk_delete(this->data, this->stride * this->reserved, this->bkt); +} +template<typename T> +T &pk_arr_t<T>::operator[](size_t index) { + if(index >= this->next) throw "pk_arr_t<T>::operator[] out of range"; + return reinterpret_cast<T*>(this->data)[index]; +} +template<typename T> +void pk_arr_append_t(pk_arr_t<T> *arr, const T &item) { + pk_arr_append(arr, &const_cast<T&>(item)); +} +#endif + #endif /* PK_PKARR_H */ #ifdef PK_IMPL_ARR @@ -1768,6 +1874,24 @@ pk_arr_remove_at(struct pk_arr *arr, uint32_t index) arr->next -= 1; } +void +pk_arr_clone(struct pk_arr *lhs, struct pk_arr *rhs) { + size_t sz; + *rhs = *lhs; + if (lhs->data == NULL) return; + sz = lhs->stride * lhs->reserved; + rhs->data = pk_new(sz, lhs->alignment, lhs->bkt); + memcpy(rhs->data, lhs->data, sz); +} + +void +pk_arr_swap(struct pk_arr *lhs, struct pk_arr *rhs) +{ + struct pk_arr tmp = *lhs; + *lhs = *rhs; + *rhs = tmp; +} + uint32_t pk_arr_find_first_index(struct pk_arr *arr, void *user_data, pk_arr_item_compare *fn) { @@ -2083,4 +2207,206 @@ struct pk_tmr { #define pk_tmr_duration_dbl_scnd(tmr) ((tmr.e.tv_sec + 1e-9 * tmr.e.tv_nsec) - (tmr.b.tv_sec + 1e-9 * tmr.b.tv_nsec)) #endif /* PK_PKTMR_H */ +#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) +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 #endif /* PK_SINGLE_HEADER_FILE_H */ |
