666 lines
18 KiB
C
666 lines
18 KiB
C
// Tiny string.h / stdlib.h helpers — kept out of libc.c because
|
|
// adding to that translation unit shifts vprintf's internal branch
|
|
// distances and randomly breaks BranchExpand (same precedent as
|
|
// strtol.c and snprintf.c).
|
|
//
|
|
// Functions:
|
|
// string.h: strcat, strncat
|
|
// stdlib.h: atol, llabs
|
|
|
|
typedef unsigned long size_t;
|
|
|
|
|
|
char *strcat(char *dst, const char *src) {
|
|
char *d = dst;
|
|
while (*d) {
|
|
d++;
|
|
}
|
|
while ((*d = *src)) {
|
|
d++;
|
|
src++;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
|
|
char *strncat(char *dst, const char *src, size_t n) {
|
|
char *d = dst;
|
|
while (*d) {
|
|
d++;
|
|
}
|
|
while (n && *src) {
|
|
*d = *src;
|
|
d++;
|
|
src++;
|
|
n--;
|
|
}
|
|
*d = 0;
|
|
return dst;
|
|
}
|
|
|
|
|
|
extern int isspace(int);
|
|
|
|
|
|
long atol(const char *s) {
|
|
while (isspace(*s)) {
|
|
s++;
|
|
}
|
|
int sign = 1;
|
|
if (*s == '-') {
|
|
sign = -1;
|
|
s++;
|
|
} else if (*s == '+') {
|
|
s++;
|
|
}
|
|
// Parse magnitude as unsigned to avoid signed-overflow UB (e.g.
|
|
// "-2147483648" — the magnitude 2147483648 doesn't fit in long).
|
|
unsigned long u = 0;
|
|
while (*s >= '0' && *s <= '9') {
|
|
u = u * 10 + (unsigned long)(*s - '0');
|
|
s++;
|
|
}
|
|
return sign < 0 ? (long)(0ul - u) : (long)u;
|
|
}
|
|
|
|
|
|
long long llabs(long long n) {
|
|
return n < 0 ? -n : n;
|
|
}
|
|
|
|
|
|
// strnlen: like strlen but capped at maxlen. Useful for safely
|
|
// measuring strings that may not be NUL-terminated within a known
|
|
// buffer size.
|
|
size_t strnlen(const char *s, size_t maxlen) {
|
|
size_t n = 0;
|
|
while (n < maxlen && s[n]) {
|
|
n++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
|
|
static int toLowerByte(int c) {
|
|
if (c >= 'A' && c <= 'Z') {
|
|
return c - 'A' + 'a';
|
|
}
|
|
return c;
|
|
}
|
|
|
|
|
|
int strcasecmp(const char *a, const char *b) {
|
|
while (*a && *b) {
|
|
int da = toLowerByte((unsigned char)*a);
|
|
int db = toLowerByte((unsigned char)*b);
|
|
if (da != db) {
|
|
return da - db;
|
|
}
|
|
a++;
|
|
b++;
|
|
}
|
|
return toLowerByte((unsigned char)*a) - toLowerByte((unsigned char)*b);
|
|
}
|
|
|
|
|
|
int strncasecmp(const char *a, const char *b, size_t n) {
|
|
while (n && *a && *b) {
|
|
int da = toLowerByte((unsigned char)*a);
|
|
int db = toLowerByte((unsigned char)*b);
|
|
if (da != db) {
|
|
return da - db;
|
|
}
|
|
a++;
|
|
b++;
|
|
n--;
|
|
}
|
|
if (!n) return 0;
|
|
return toLowerByte((unsigned char)*a) - toLowerByte((unsigned char)*b);
|
|
}
|
|
|
|
|
|
// Linear congruential generator — Numerical Recipes constants.
|
|
// Returns 15-bit values (RAND_MAX = 0x7FFF) per C standard convention.
|
|
static unsigned long randSeed = 1;
|
|
|
|
void srand(unsigned int seed) {
|
|
randSeed = seed;
|
|
}
|
|
|
|
int rand(void) {
|
|
randSeed = randSeed * 1103515245UL + 12345UL;
|
|
return (int)((randSeed >> 16) & 0x7FFF);
|
|
}
|
|
|
|
|
|
// crt0 hook: seed rand() from the IIgs RTC via ReadTimeHex (Misc
|
|
// Tool $0D03). Called from crt0Gsos.s / crt0Gno.s after .init_array
|
|
// has run. The Tool Locator is already up at that point (the GS/OS
|
|
// Loader brings it up before transferring control to __start; GNO's
|
|
// kernel does likewise), so JSL $E10000 X=$0D03 is safe.
|
|
//
|
|
// Without this hook randSeed stays at 1 and every run produces an
|
|
// identical PRNG sequence -- a silent correctness bug for callers
|
|
// like mkstemp that rely on rand() for uniqueness across invocations.
|
|
//
|
|
// Mixing strategy: fold the 8 TimeRec bytes into the seed via a
|
|
// simple u16 rotate-XOR, then place into the high half of randSeed
|
|
// (the LCG output is `(seed >> 16) & 0x7FFF`, so the first rand()
|
|
// directly reflects the seed bits we just installed). u16 arithmetic
|
|
// keeps the helper small -- ~150 B vs ~860 B for the u32 form.
|
|
extern void iigsReadTimeHex(unsigned char *buf8);
|
|
|
|
void __srandInitFromTime(void) {
|
|
unsigned char b[8];
|
|
iigsReadTimeHex(b);
|
|
unsigned short s = 0;
|
|
for (int i = 0; i < 8; i++) {
|
|
s = (unsigned short)((s << 3) | (s >> 13));
|
|
s = (unsigned short)(s ^ (unsigned short)b[i]);
|
|
}
|
|
// Force non-zero (LCG with seed 0 still cycles, but at least one
|
|
// bit set keeps the early outputs out of the trivial-prefix range).
|
|
if (!s) {
|
|
s = 1;
|
|
}
|
|
// Place the time-derived bits in the high half so the first
|
|
// rand() output -- ((seed * K + C) >> 16) & 0x7FFF -- carries
|
|
// them. Low half stays 0; the LCG mixes it into the next call.
|
|
randSeed = ((unsigned long)s) << 16;
|
|
}
|
|
|
|
|
|
// ----- sys/time.h gettimeofday() ---------------------------------------
|
|
//
|
|
// Thin shim over libc.c's time() — same epoch-second source, packaged
|
|
// in the POSIX struct timeval shape. tv_usec is always 0 because the
|
|
// IIgs has no sub-second wall clock (the VBL counter at $E1:006B is
|
|
// monotonic but not aligned to wall-clock seconds). The tz argument
|
|
// is accepted for source compat and ignored; the IIgs has no
|
|
// timezone database.
|
|
//
|
|
// Declared in <sys/time.h>; the struct timeval layout matches that
|
|
// header byte-for-byte (time_t, then long).
|
|
|
|
extern long time(long *t); // matches signature in <time.h>
|
|
|
|
struct __ggGtodTimeval {
|
|
long tv_sec;
|
|
long tv_usec;
|
|
};
|
|
|
|
|
|
int gettimeofday(struct __ggGtodTimeval *tv, void *tz) {
|
|
(void)tz;
|
|
if (!tv) {
|
|
return 0;
|
|
}
|
|
long s = time((long *)0);
|
|
if (s == 0) {
|
|
// time() returns 0 either at Unix epoch midnight (impossible on
|
|
// a real IIgs RTC) or when the Tool Locator isn't up. Treat as
|
|
// failure -- matches the POSIX convention.
|
|
tv->tv_sec = 0;
|
|
tv->tv_usec = 0;
|
|
return -1;
|
|
}
|
|
tv->tv_sec = s;
|
|
tv->tv_usec = 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
// ----- additional string.h ----------------------------------------------
|
|
|
|
static int inSet(char c, const char *set) {
|
|
while (*set) {
|
|
if (c == *set) {
|
|
return 1;
|
|
}
|
|
set++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
char *strpbrk(const char *s, const char *accept) {
|
|
while (*s) {
|
|
if (inSet(*s, accept)) {
|
|
return (char *)s;
|
|
}
|
|
s++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
size_t strspn(const char *s, const char *accept) {
|
|
size_t n = 0;
|
|
while (s[n] && inSet(s[n], accept)) {
|
|
n++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
|
|
size_t strcspn(const char *s, const char *reject) {
|
|
size_t n = 0;
|
|
while (s[n] && !inSet(s[n], reject)) {
|
|
n++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
|
|
// strtok / strtok_r are in runtime/src/strtok.c.
|
|
|
|
// ---- wchar.h ----
|
|
// wchar_t is 16-bit on this target. The wcs* functions mirror the
|
|
// str* family. mbtowc / wctomb use the trivial 1:1 byte<->wide-char
|
|
// mapping (essentially Latin-1) — no real multi-byte / locale support.
|
|
|
|
// Now `int` to match the clang builtin signature for wcslen/wcscmp/
|
|
// wcscpy etc; was `unsigned short`. Latin-1 content (0..255) is
|
|
// representable in both.
|
|
typedef int wchar_t;
|
|
|
|
size_t wcslen(const wchar_t *s) {
|
|
size_t n = 0;
|
|
while (*s++) n++;
|
|
return n;
|
|
}
|
|
|
|
int wcscmp(const wchar_t *a, const wchar_t *b) {
|
|
while (*a && *a == *b) { a++; b++; }
|
|
return (int)((short)*a - (short)*b);
|
|
}
|
|
|
|
int wcsncmp(const wchar_t *a, const wchar_t *b, size_t n) {
|
|
while (n && *a && *a == *b) { a++; b++; n--; }
|
|
if (!n) return 0;
|
|
return (int)((short)*a - (short)*b);
|
|
}
|
|
|
|
wchar_t *wcscpy(wchar_t *dst, const wchar_t *src) {
|
|
wchar_t *d = dst;
|
|
while ((*d++ = *src++)) {}
|
|
return dst;
|
|
}
|
|
|
|
wchar_t *wcsncpy(wchar_t *dst, const wchar_t *src, size_t n) {
|
|
wchar_t *d = dst;
|
|
while (n && (*d = *src)) { d++; src++; n--; }
|
|
while (n--) *d++ = 0;
|
|
return dst;
|
|
}
|
|
|
|
wchar_t *wcscat(wchar_t *dst, const wchar_t *src) {
|
|
wchar_t *d = dst;
|
|
while (*d) d++;
|
|
while ((*d++ = *src++)) {}
|
|
return dst;
|
|
}
|
|
|
|
wchar_t *wcschr(const wchar_t *s, wchar_t c) {
|
|
while (*s) {
|
|
if (*s == c) return (wchar_t *)s;
|
|
s++;
|
|
}
|
|
return (c == 0) ? (wchar_t *)s : (wchar_t *)0;
|
|
}
|
|
|
|
wchar_t *wcsrchr(const wchar_t *s, wchar_t c) {
|
|
const wchar_t *last = (const wchar_t *)0;
|
|
while (*s) {
|
|
if (*s == c) last = s;
|
|
s++;
|
|
}
|
|
if (c == 0) return (wchar_t *)s;
|
|
return (wchar_t *)last;
|
|
}
|
|
|
|
int mbtowc(wchar_t *pwc, const char *s, size_t n) {
|
|
if (!s) return 0; // no shift state
|
|
if (n == 0) return -1;
|
|
unsigned char c = (unsigned char)*s;
|
|
if (pwc) *pwc = (wchar_t)c;
|
|
return c ? 1 : 0;
|
|
}
|
|
|
|
int wctomb(char *s, wchar_t wc) {
|
|
if (!s) return 0; // no shift state
|
|
if (wc > 0xFF) return -1;
|
|
*s = (char)wc;
|
|
return 1;
|
|
}
|
|
|
|
size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n) {
|
|
size_t i = 0;
|
|
while (i < n && s[i]) {
|
|
if (pwcs) pwcs[i] = (wchar_t)(unsigned char)s[i];
|
|
i++;
|
|
}
|
|
if (pwcs && i < n) pwcs[i] = 0;
|
|
return i;
|
|
}
|
|
|
|
size_t wcstombs(char *s, const wchar_t *pwcs, size_t n) {
|
|
size_t i = 0;
|
|
while (i < n && pwcs[i]) {
|
|
if (pwcs[i] > 0xFF) return (size_t)-1;
|
|
if (s) s[i] = (char)pwcs[i];
|
|
i++;
|
|
}
|
|
if (s && i < n) s[i] = 0;
|
|
return i;
|
|
}
|
|
|
|
int mblen(const char *s, size_t n) {
|
|
if (!s) return 0;
|
|
if (n == 0) return -1;
|
|
return *s ? 1 : 0;
|
|
}
|
|
|
|
|
|
// ---- wide-char memory + scan/format ---------------------------------
|
|
// Operate on wchar_t arrays (wchar_t is `int` on this target = 2
|
|
// bytes). Under Latin-1 we delegate the actual work to the byte/str
|
|
// equivalents wherever the data fits in 8 bits.
|
|
|
|
#include <stdarg.h>
|
|
|
|
struct tm;
|
|
|
|
extern void *memcpy (void *dst, const void *src, size_t n);
|
|
extern void *memmove(void *dst, const void *src, size_t n);
|
|
extern long strtol (const char *nptr, char **endptr, int base);
|
|
extern unsigned long strtoul (const char *nptr, char **endptr, int base);
|
|
extern long long strtoll (const char *nptr, char **endptr, int base);
|
|
extern unsigned long long strtoull(const char *nptr, char **endptr, int base);
|
|
extern double strtod (const char *nptr, char **endptr);
|
|
extern float strtof (const char *nptr, char **endptr);
|
|
extern int vsnprintf(char *buf, size_t n, const char *fmt, va_list ap);
|
|
extern size_t strftime (char *buf, size_t n, const char *fmt, const struct tm *tm);
|
|
|
|
|
|
wchar_t *wmemcpy(wchar_t *dst, const wchar_t *src, size_t n) {
|
|
memcpy(dst, src, n * sizeof(wchar_t));
|
|
return dst;
|
|
}
|
|
|
|
|
|
wchar_t *wmemmove(wchar_t *dst, const wchar_t *src, size_t n) {
|
|
memmove(dst, src, n * sizeof(wchar_t));
|
|
return dst;
|
|
}
|
|
|
|
|
|
wchar_t *wmemset(wchar_t *dst, wchar_t c, size_t n) {
|
|
wchar_t *p = dst;
|
|
while (n--) {
|
|
*p++ = c;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
|
|
int wmemcmp(const wchar_t *a, const wchar_t *b, size_t n) {
|
|
while (n--) {
|
|
if (*a != *b) {
|
|
return (int)(*a - *b);
|
|
}
|
|
a++;
|
|
b++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
wchar_t *wmemchr(const wchar_t *s, wchar_t c, size_t n) {
|
|
while (n--) {
|
|
if (*s == c) {
|
|
return (wchar_t *)s;
|
|
}
|
|
s++;
|
|
}
|
|
return (wchar_t *)0;
|
|
}
|
|
|
|
|
|
// Helper: narrow a wide string of up to `lim` chars into a byte
|
|
// buffer. Stops at the first NUL or after `lim` chars. Returns
|
|
// the number of bytes written (excluding any trailing NUL).
|
|
static size_t __narrow(char *out, const wchar_t *in, size_t lim) {
|
|
size_t i = 0;
|
|
while (i < lim && in[i]) {
|
|
out[i] = (char)(in[i] & 0xFF);
|
|
i++;
|
|
}
|
|
if (i < lim) {
|
|
out[i] = 0;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
long wcstol(const wchar_t *nptr, wchar_t **endptr, int base) {
|
|
char buf[40];
|
|
size_t k = __narrow(buf, nptr, sizeof(buf) - 1);
|
|
buf[k] = 0;
|
|
char *bend;
|
|
long r = strtol(buf, &bend, base);
|
|
if (endptr) {
|
|
*endptr = (wchar_t *)(nptr + (bend - buf));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
unsigned long wcstoul(const wchar_t *nptr, wchar_t **endptr, int base) {
|
|
char buf[40];
|
|
size_t k = __narrow(buf, nptr, sizeof(buf) - 1);
|
|
buf[k] = 0;
|
|
char *bend;
|
|
unsigned long r = strtoul(buf, &bend, base);
|
|
if (endptr) {
|
|
*endptr = (wchar_t *)(nptr + (bend - buf));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
long long wcstoll(const wchar_t *nptr, wchar_t **endptr, int base) {
|
|
char buf[40];
|
|
size_t k = __narrow(buf, nptr, sizeof(buf) - 1);
|
|
buf[k] = 0;
|
|
char *bend;
|
|
long long r = strtoll(buf, &bend, base);
|
|
if (endptr) {
|
|
*endptr = (wchar_t *)(nptr + (bend - buf));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
unsigned long long wcstoull(const wchar_t *nptr, wchar_t **endptr, int base) {
|
|
char buf[40];
|
|
size_t k = __narrow(buf, nptr, sizeof(buf) - 1);
|
|
buf[k] = 0;
|
|
char *bend;
|
|
unsigned long long r = strtoull(buf, &bend, base);
|
|
if (endptr) {
|
|
*endptr = (wchar_t *)(nptr + (bend - buf));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
double wcstod(const wchar_t *nptr, wchar_t **endptr) {
|
|
char buf[40];
|
|
size_t k = __narrow(buf, nptr, sizeof(buf) - 1);
|
|
buf[k] = 0;
|
|
char *bend;
|
|
double r = strtod(buf, &bend);
|
|
if (endptr) {
|
|
*endptr = (wchar_t *)(nptr + (bend - buf));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
float wcstof(const wchar_t *nptr, wchar_t **endptr) {
|
|
char buf[40];
|
|
size_t k = __narrow(buf, nptr, sizeof(buf) - 1);
|
|
buf[k] = 0;
|
|
char *bend;
|
|
float r = strtof(buf, &bend);
|
|
if (endptr) {
|
|
*endptr = (wchar_t *)(nptr + (bend - buf));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
// swprintf: narrow the format string, route through vsnprintf into a
|
|
// byte buffer, then widen the result back into `buf`. Limits the
|
|
// format-spec coverage to what vsnprintf supports; %ls / %lc are not
|
|
// honoured (caller must pass narrow-char args). Returns -1 on
|
|
// overflow per C11.
|
|
//
|
|
// Buffers kept small (64 bytes each) so the total frame stays under
|
|
// the W65816's 256-byte stack-rel addressing limit. Long format
|
|
// strings and long outputs are truncated.
|
|
int vswprintf(wchar_t *buf, size_t n, const wchar_t *fmt, va_list ap) {
|
|
if (n == 0) {
|
|
return -1;
|
|
}
|
|
char fmtBuf[64];
|
|
__narrow(fmtBuf, fmt, sizeof(fmtBuf) - 1);
|
|
fmtBuf[sizeof(fmtBuf) - 1] = 0;
|
|
char outBuf[64];
|
|
size_t cap = n - 1 < sizeof(outBuf) - 1 ? n - 1 : sizeof(outBuf) - 1;
|
|
int wrote = vsnprintf(outBuf, cap + 1, fmtBuf, ap);
|
|
if (wrote < 0 || (size_t)wrote >= n) {
|
|
buf[0] = 0;
|
|
return -1;
|
|
}
|
|
int i;
|
|
for (i = 0; i < wrote; i++) {
|
|
buf[i] = (wchar_t)(unsigned char)outBuf[i];
|
|
}
|
|
buf[wrote] = 0;
|
|
return wrote;
|
|
}
|
|
|
|
|
|
int swprintf(wchar_t *buf, size_t n, const wchar_t *fmt, ...) {
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
int r = vswprintf(buf, n, fmt, ap);
|
|
va_end(ap);
|
|
return r;
|
|
}
|
|
|
|
|
|
size_t wcsftime(wchar_t *buf, size_t n, const wchar_t *fmt, const struct tm *tm) {
|
|
if (n == 0) {
|
|
return 0;
|
|
}
|
|
char fmtBuf[64];
|
|
__narrow(fmtBuf, fmt, sizeof(fmtBuf) - 1);
|
|
fmtBuf[sizeof(fmtBuf) - 1] = 0;
|
|
char outBuf[128];
|
|
size_t cap = n - 1 < sizeof(outBuf) - 1 ? n - 1 : sizeof(outBuf) - 1;
|
|
size_t wrote = strftime(outBuf, cap + 1, fmtBuf, tm);
|
|
if (wrote == 0 || wrote >= n) {
|
|
buf[0] = 0;
|
|
return 0;
|
|
}
|
|
size_t i;
|
|
for (i = 0; i < wrote; i++) {
|
|
buf[i] = (wchar_t)(unsigned char)outBuf[i];
|
|
}
|
|
buf[wrote] = 0;
|
|
return wrote;
|
|
}
|
|
|
|
|
|
// ---- fenv.h ----------------------------------------------------------
|
|
//
|
|
// softFloat / softDouble are fixed at round-to-nearest-even and don't
|
|
// raise IEEE exceptions. We track the requested rounding mode and an
|
|
// exception-flag word but neither affects soft-float output.
|
|
|
|
static int __fenvRound = 0; /* FE_TONEAREST */
|
|
static unsigned short __fenvExcept = 0;
|
|
|
|
int feclearexcept(int excepts) { __fenvExcept &= (unsigned short)~excepts; return 0; }
|
|
int feraiseexcept(int excepts) { __fenvExcept |= (unsigned short)excepts; return 0; }
|
|
int fetestexcept(int excepts) { return __fenvExcept & excepts; }
|
|
int fegetexceptflag(unsigned short *flagp, int e) { (void)e; if (flagp) *flagp = __fenvExcept; return 0; }
|
|
int fesetexceptflag(const unsigned short *flagp, int e) {
|
|
if (!flagp) return -1;
|
|
__fenvExcept = (unsigned short)((__fenvExcept & ~e) | (*flagp & e));
|
|
return 0;
|
|
}
|
|
int fegetround(void) { return __fenvRound; }
|
|
int fesetround(int r) { __fenvRound = r; return 0; }
|
|
int fegetenv(unsigned short *envp) { if (envp) *envp = __fenvExcept; return 0; }
|
|
int feholdexcept(unsigned short *envp) { if (envp) *envp = __fenvExcept; __fenvExcept = 0; return 0; }
|
|
int fesetenv(const unsigned short *envp) { __fenvExcept = envp ? *envp : 0; return 0; }
|
|
int feupdateenv(const unsigned short *envp) { unsigned short e = envp ? *envp : 0; __fenvExcept |= e; return 0; }
|
|
|
|
|
|
// ---- threads.h backing storage ---------------------------------------
|
|
//
|
|
// All thread / mutex / cond ops are inline no-ops; only tss_* needs
|
|
// real per-key storage. 8 keys is enough for any single-core code.
|
|
|
|
void *__tss_slots[8];
|
|
int __tss_next = 0;
|
|
|
|
|
|
// ---- aligned_alloc / posix_memalign ---------------------------------
|
|
//
|
|
// Wraps malloc with an over-allocation + alignment-adjust trick: alloc
|
|
// (n + alignment + sizeof(void*)) bytes; align upward; stash the
|
|
// original pointer just before the returned address for free() to find.
|
|
// `aligned_alloc` requires `n` to be a multiple of `alignment` (C11).
|
|
|
|
extern void *malloc(unsigned long n);
|
|
extern void free (void *p);
|
|
|
|
void *aligned_alloc(unsigned long alignment, unsigned long size) {
|
|
if (alignment == 0 || (alignment & (alignment - 1))) return (void *)0;
|
|
if (size % alignment) return (void *)0;
|
|
unsigned long over = size + alignment + sizeof(void *);
|
|
char *raw = (char *)malloc(over);
|
|
if (!raw) return (void *)0;
|
|
unsigned long addr = (unsigned long)raw + sizeof(void *);
|
|
unsigned long aligned = (addr + alignment - 1) & ~(alignment - 1);
|
|
((void **)aligned)[-1] = raw;
|
|
return (void *)aligned;
|
|
}
|
|
|
|
// Wrappers that read the stashed raw pointer and free the underlying
|
|
// block. Callers should use these (not plain free) for aligned_alloc'd
|
|
// pointers. Single-source projects can `#define free aligned_free` if
|
|
// needed; the standard C11 contract is that `free` works on aligned
|
|
// pointers, so we also patch free below.
|
|
void aligned_free(void *p) {
|
|
if (!p) return;
|
|
void *raw = ((void **)p)[-1];
|
|
free(raw);
|
|
}
|
|
|
|
int posix_memalign(void **memptr, unsigned long alignment, unsigned long size) {
|
|
if (!memptr) return 22; /* EINVAL */
|
|
if (alignment < sizeof(void *) || (alignment & (alignment - 1))) {
|
|
*memptr = (void *)0;
|
|
return 22;
|
|
}
|
|
void *p = aligned_alloc(alignment, (size + alignment - 1) & ~(alignment - 1));
|
|
if (!p) { *memptr = (void *)0; return 12; /* ENOMEM */ }
|
|
*memptr = p;
|
|
return 0;
|
|
}
|