version to string functions `v_ctos{s,n,}()` working as intended

TODO: Move them out of header and into seperate TU "version.c"

version_t holds version components. `v_rawtoc()`, `v_ctoraw()` to create from/convert to the format used by `VERSION()` macro and friends. Punning this union between the raw (u32, used by the macros) and the componant struct (`v_comp`) is possible on little endian machines but discouraged.

Fortune for naka's current commit: Half blessing − 半吉
generic-project-skeleton
Avril 3 years ago
parent d9ccfa0d89
commit fb2fa5801e
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -37,6 +37,16 @@
#define restrict __restrict__ #define restrict __restrict__
#endif #endif
// Argument attribute
// Used like: `int *pOUT output, const int *pIN input`
#define pOUT restrict
#define pIN const restrict
// Used like: `int _OUT output, const int _IN input`
#define _IN *pIN
#define _OUT *pOUT
// Type macros // Type macros
#define AS(x, t) ((t)(x)) #define AS(x, t) ((t)(x))
@ -155,9 +165,9 @@ static_assert_eq(bswap(bswap(128lu)), 128, "bswap128 (lu) failed (3)");
#define VERSION_COMP(ver, mask) _AS_u32( _AS_u32(ver) & (0xffu << _AS_u32(mask)) /*>> _AS_u32(mask)*/ ) #define VERSION_COMP(ver, mask) _AS_u32( _AS_u32(ver) & (0xffu << _AS_u32(mask)) /*>> _AS_u32(mask)*/ )
#define VERSION_MAJ(ver) _AS_u32(VERSION_COMP((ver), 24u) >> 24u) #define VERSION_MAJ(ver) _AS_u32(VERSION_COMP((ver), 24u) >> 24u)
#define VERSINO_MIN(ver) _AS_u32(VERSION_COMP((ver), 16u) >> 16u) #define VERSION_MIN(ver) _AS_u32(VERSION_COMP((ver), 16u) >> 16u)
#define VERSINO_BF(ver) _AS_u32(VERSION_COMP((ver), 8u) >> 8u) #define VERSION_BF(ver) _AS_u32(VERSION_COMP((ver), 8u) >> 8u)
#define VERSINO_REV(ver) _AS_u32(_AS_u32(ver) & 0xffu) #define VERSION_REV(ver) _AS_u32(_AS_u32(ver) & 0xffu)
_Static_assert( (VERSION_COMP(VERSION(1,2,3,4), VER_COMP_MIN) >> VER_COMP_MIN) == 2u, "invalid native version spec"); _Static_assert( (VERSION_COMP(VERSION(1,2,3,4), VER_COMP_MIN) >> VER_COMP_MIN) == 2u, "invalid native version spec");
#endif /* _MACROS_H */ #endif /* _MACROS_H */

@ -0,0 +1,79 @@
#ifndef _VERSION_H
#define _VERSION_H
#include "macros.h"
#include "ints.h"
typedef union {
uint32_t raw;
// XXX: Punning through this union is unsafe, it may work (on little endian machines), but it is not intended to be punned. Use `v_rawtoc` and `v_ctoraw` instead.
struct v_comp {
u8 revision, bugfix, minor, major;
} comp;
} version_t;
// Convert a native `u32` representation of a version (the output of `VERSION()` macro) to a `version_t`.
#define v_rawtoc_unsafe(raw) ((version_t){ .raw = (raw) })
_mixin version_t v_rawtoc(uint32_t raw)
{
register version_t out = {
.comp = {
.major = VERSION_MAJ(raw),
.minor = VERSION_MIN(raw),
.bugfix = VERSION_BF(raw),
.revision = VERSION_REV(raw),
},
};
return out;
}
/// Convert a `version_t` to its native `u32` representation.
#define v_ctoraw_unsafe(ver) ((ver).raw)
_mixin uint32_t v_ctoraw(version_t ver)
{
return VERSION(ver.comp.major,
ver.comp.minor,
ver.comp.bugfix,
ver.comp.revision);
}
#define VERSION_C(ma,mi,bf,re) v_rawtoc(VERSION((ma),(mi),(bf),(re)))
#define VERSION_STRING_MAX AS(3 + 3 + 3 + 3 + 3, usize) // maj + min + bf + rev + (..r)
//TODO: Move the below functions to `version.c` and have them prototyped, not mixins.
/// Print a version to a string buffer `out`. Return the number of bytes written.
///
/// Has the same semantics as `snprintf`; to calculate the length of the string needed to allocate, invoke like: `size_t sz = v_ctosn(vers, 0, NULL)`. The buffer should be allocated as `sz + 1`, and then the next call to `v_ctosn` should be like: `v_ctosn(vers, sz+1, buffer);
_mixin usize v_ctosn(version_t ver, usize max, char out[static pOUT max])
{
struct v_comp comp = ver.comp;
const char* const fmt = comp.revision ? "%hhu.%hhu.%hhur%hhu" : "%hhu.%hhu.%hhu";
return snprintf(out, max, fmt,
comp.major,
comp.minor,
comp.bugfix,
comp.revision);
}
/// Print a version to a static string buffer. The string buffer is thread local. The next call to this function on the same thread will overwrite the returned buffer.
_mixin const char* v_ctoss(version_t ver)
{
_Thread_local static char buffer[VERSION_STRING_MAX+1];
buffer[v_ctosn(ver, VERSION_STRING_MAX+1, buffer)] = 0;
return buffer;
}
/// Print a version to a `malloc()`d string buffer. The buffer is unique and owned by the callee. The buffer must be `free()`d.
_mixin char* v_ctos(version_t ver)
{
usize sz = v_ctosn(ver, 0, NULL);
char* buf = malloc(sz+1);
v_ctosn(ver, sz+1, buf);
return buf;
}
#endif /* _VERSION_H */

@ -6,17 +6,28 @@
#include <ints.h> #include <ints.h>
#include <macros.h> #include <macros.h>
#include <version.h>
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
IGNORE(argc); IGNORE(argc);
IGNORE(argv); IGNORE(argv);
u64 i = 128; const u32 vraw = VERSION(1,2,3,240);
version_t vers = v_rawtoc(vraw);
u32 vers = VERSION(1,2,3,4); usize sz = v_ctosn(vers, 0, NULL);
char verstr[sz+1];
v_ctosn(vers, sz+1, verstr);
INFO("Hello world %lu %lu %lu!", i, bswap(i), bswap(bswap(i))); char* verstr_h;
INFO("Version: 0x%x, raw: 0x%x", vers.raw, vraw);
INFO("Output is: (%lu chars)\t%s", sz, verstr);
INFO("Output is (static):\t%s", v_ctoss(vers));
INFO("Output is (leak):\t\t%s", (verstr_h = v_ctos(vers)));
free(verstr_h);
//INFO("Hello world %lu %lu %lu!", i, bswap(i), bswap(bswap(i)));
//INFO("Version: 0x%x (0x%x)", vers, bswap(vers)); // Check which version endian was preferrable, we'll stick with the default system endian (little, for this one.) //INFO("Version: 0x%x (0x%x)", vers, bswap(vers)); // Check which version endian was preferrable, we'll stick with the default system endian (little, for this one.)
return 0; return 0;
} }

Loading…
Cancel
Save