Last active
July 9, 2023 00:58
-
-
Save imaami/a6f1a7bd61a8d5dc61c7e759a4f5a1e7 to your computer and use it in GitHub Desktop.
An SSO thing in C, WIP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <errno.h> | |
#include <limits.h> | |
#include <stdbool.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#define stringify_(x) #x | |
#define stringify(x) stringify_(x) | |
#if defined __GNUC__ && !defined __clang__ | |
# define ligma_gcc(...) _Pragma(stringify_(GCC __VA_ARGS__)) | |
#else | |
# define ligma_gcc(...) | |
#endif | |
#define force_inline __attribute__((always_inline)) static inline | |
static void | |
hexdump (unsigned char const *data, | |
size_t size, | |
char *buf, | |
size_t off); | |
#define hexdump_(data, size) hexdump((data), (size), (char[72u]){""}, 0) | |
/** | |
* @brief dstr stands for dumb string. | |
*/ | |
typedef struct dstr | |
{ | |
_Alignas(char *) union | |
{ | |
struct __attribute__((packed)) | |
{ | |
union { | |
char *ptr; //!< String pointer. | |
char const *view; //!< String view. | |
}; | |
unsigned int size; //!< Total heap allocation size. | |
}; | |
char arr[sizeof(char *) + sizeof(unsigned int)]; //!< Array. | |
}; | |
unsigned int len; //!< String length excluding the null terminator. | |
} dstr; | |
#define dstr_get(s_) (__typeof__((s_)->arr[0]) *) \ | |
{ !dstr_is_pointer(s_) ? (s_)->arr : (s_)->ptr } | |
#define is_char_array(x) \ | |
_Generic((__typeof__(x) *){0}, \ | |
char (*)[sizeof(x)]: 1, \ | |
char const (*)[sizeof(x)]: 1, \ | |
default: 0) | |
#define is_string_literal(x) \ | |
__builtin_choose_expr(is_char_array(x), __builtin_constant_p(x), 0) | |
#define safe_string_literal(lit, what) \ | |
__builtin_choose_expr( \ | |
is_string_literal(lit) && sizeof(lit) <= sizeof(what), \ | |
lit, "") | |
#define make_dstr_view_from_literal(src) \ | |
__builtin_choose_expr( \ | |
sizeof(src) <= sizeof(((dstr *)0)->arr), \ | |
(dstr){.arr = {safe_string_literal(src, ((dstr*)0)->arr)}, \ | |
.len = sizeof(src) - 1u}, \ | |
(dstr){.view = src, .size = 0u, .len = sizeof(src) - 1u}) | |
#define make_dstr_view_from_array(src) \ | |
__builtin_choose_expr( \ | |
sizeof(src) <= sizeof(((dstr *)0)->arr), \ | |
make_dstr_from_small_string(src, sizeof(src) - 1u), \ | |
(dstr){.view = src, .size = 0u, .len = sizeof(src) - 1u}) | |
#define make_dstr_view(src) \ | |
__builtin_choose_expr( \ | |
is_string_literal(src), \ | |
make_dstr_view_from_literal(src), \ | |
__builtin_choose_expr( \ | |
is_char_array(src), \ | |
make_dstr_view_from_array(src), \ | |
make_dstr_view_from_decay(src, strlen(src)))) | |
force_inline dstr | |
make_dstr_from_small_string (char const *const src, | |
size_t len) | |
{ | |
dstr d = {.arr = {0}, .len = (unsigned)len}; | |
__builtin_memcpy(&d.arr[0], src, len); | |
return d; | |
} | |
force_inline dstr | |
make_dstr_view_from_decay (char const *const src, | |
size_t len) | |
{ | |
ligma_gcc(diagnostic push) | |
ligma_gcc(diagnostic ignored "-Wstringop-overflow") | |
return !len || len > UINT_MAX - 1u | |
? (dstr){0} | |
: len < sizeof(((dstr *)0)->arr) | |
? make_dstr_from_small_string(src, len) | |
: (dstr){.view = src, .size = 0u, .len = (unsigned)len}; | |
ligma_gcc(diagnostic pop) | |
} | |
force_inline bool | |
dstr_is_empty (dstr const *s) | |
{ | |
return !s->len; | |
} | |
force_inline bool | |
dstr_is_array (dstr const *s) | |
{ | |
return s->len && s->len < sizeof s->arr; | |
} | |
force_inline bool | |
dstr_is_pointer (dstr const *s) | |
{ | |
return s->len >= sizeof s->arr; | |
} | |
force_inline bool | |
dstr_owns_memory (dstr const *s) | |
{ | |
return dstr_is_pointer(s) && s->size; | |
} | |
force_inline void | |
dstr_init (dstr *s) | |
{ | |
s->ptr = NULL; | |
s->size = 0u; | |
s->len = 0u; | |
} | |
force_inline void | |
dstr_fini (dstr *s) | |
{ | |
if (dstr_owns_memory(s)) | |
free(s->ptr); | |
dstr_init(s); | |
} | |
#define Y(b) &"-\0yes"[b<<1] | |
#define TEST_DSTR_VIEW(x) \ | |
do { \ | |
dstr s = make_dstr_view(x); \ | |
printf(" %-15s\t %-15s\t%-11s %-11s %-11s \t", \ | |
stringify(x), dstr_get(&s), Y(dstr_is_array(&s)), \ | |
Y(dstr_is_pointer(&s)), Y(dstr_owns_memory(&s))); \ | |
hexdump_((unsigned char const *)&s, sizeof s); \ | |
dstr_fini(&s); \ | |
} while (0) | |
int | |
main (void) | |
{ | |
#define SMALL_STRING "Sup world" | |
#define LARGE_STRING "How do you do" | |
const char small_array[] = SMALL_STRING; | |
const char large_array[] = LARGE_STRING; | |
printf(" %-15s\t %-15s\t%-11s %-11s %-11s \t %s\n" | |
" %-15s\t %-15s\t%-11s %-11s %-11s \t %s\n", | |
"Initializer", "String value", "Is array", | |
"Is ptr", "Owns mem", "Hexdump", | |
"-----------", "------------", "--------", | |
"------", "--------", "-------"); | |
TEST_DSTR_VIEW(SMALL_STRING); | |
TEST_DSTR_VIEW(&SMALL_STRING[0]); | |
TEST_DSTR_VIEW(small_array); | |
TEST_DSTR_VIEW(&small_array[0]); | |
TEST_DSTR_VIEW(LARGE_STRING); | |
TEST_DSTR_VIEW(&LARGE_STRING[0]); | |
TEST_DSTR_VIEW(large_array); | |
TEST_DSTR_VIEW(&large_array[0]); | |
return 0; | |
} | |
#undef TEST_DSTR_VIEW | |
#undef SMALL_STRING | |
#undef LARGE_STRING | |
#undef Y | |
force_inline void | |
ascdump (unsigned char **pp, | |
unsigned char const *bs, | |
size_t sz) | |
{ | |
*(*pp)++ = ' '; | |
*(*pp)++ = '|'; | |
for (size_t i = 0; i < sz; ++i) | |
*(*pp)++ = bs[i] > 0x1fu && bs[i] < 0x7fu | |
? bs[i] : (unsigned char)'.'; | |
*(*pp)++ = '|'; | |
} | |
force_inline void | |
hexchar (unsigned char **pp, | |
unsigned char ch) | |
{ | |
static const unsigned char hex[16] = "0123456789abcdef"; | |
*(*pp)++ = ' '; | |
*(*pp)++ = hex[ch >> 4u]; | |
*(*pp)++ = hex[ch & 15u]; | |
} | |
static void | |
hexdump (unsigned char const *data, | |
size_t size, | |
char *buf, | |
size_t off) | |
{ | |
unsigned char *s = (unsigned char *)&buf[off]; | |
unsigned char *p = s; | |
size_t i = 0u, n = size >> 4u; | |
for (size_t r = 0; r < n; ++r, i = 0u, p = s, data += 16) { | |
for (; i < 8u; ++i) | |
hexchar(&p, data[i]); | |
*p++ = (unsigned char)' '; | |
for (; i < 16u; ++i) | |
hexchar(&p, data[i]); | |
*p++ = (unsigned char)' '; | |
ascdump(&p, data, 16u); | |
*p++ = (unsigned char)'\n'; | |
*p = (unsigned char)'\0'; | |
fputs(buf, stdout); | |
} | |
if ((n = size & 15u)) { | |
for (; i < n; ++i) { | |
hexchar(&p, data[i]); | |
if ((i & 7u) == 7u) | |
*p++ = (unsigned char)' '; | |
} | |
for (; i < 16u; ++i) { | |
*p++ = (unsigned char)' '; | |
*p++ = (unsigned char)' '; | |
*p++ = (unsigned char)' '; | |
if ((i & 7u) == 7u) | |
*p++ = (unsigned char)' '; | |
} | |
ascdump(&p, data, n); | |
*p++ = (unsigned char)'\n'; | |
*p = (unsigned char)'\0'; | |
fputs(buf, stdout); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment