Viewing: stdlib.c
📄 stdlib.c (Read Only) ⬅ To go back
#include "stdlib.h"
#include "unistd.h"
#include "string.h"

/* Global environment pointer — set by crt0 from execve stack layout */
char** __environ = 0;

/*
 * Free-list allocator using brk() syscall.
 *
 * Each allocated block has a header: [size | in_use_flag]
 * Free blocks are linked in an explicit free list.
 * Coalescing is done on free() (merge with adjacent free blocks).
 *
 * Layout: [header 8 bytes] [user data ...]
 *   header.size = total block size including header (aligned to 8)
 *   header.next = pointer to next free block (only valid when free)
 */

#define ALLOC_ALIGN 8
#define ALLOC_HDR_SIZE 8  /* must == sizeof(struct block_hdr) */
#define ALLOC_USED_BIT 1U

struct block_hdr {
    uint32_t size;       /* total size including header; bit 0 = used flag */
    uint32_t next_free;  /* pointer to next free block (as uintptr_t), 0 = end */
};

static struct block_hdr* free_list = 0;
static void* heap_base = 0;
static void* heap_end = 0;

static inline uint32_t blk_size(struct block_hdr* b) { return b->size & ~ALLOC_USED_BIT; }
static inline int      blk_used(struct block_hdr* b) { return (int)(b->size & ALLOC_USED_BIT); }

static void* sbrk_grow(size_t inc) {
    if (!heap_base) {
        heap_base = brk(0);
        heap_end = heap_base;
    }
    void* old_end = heap_end;
    void* new_end = (void*)((char*)heap_end + inc);
    void* result = brk(new_end);
    if ((uintptr_t)result < (uintptr_t)new_end)
        return (void*)0;
    heap_end = new_end;
    return old_end;
}

void* malloc(size_t size) {
    if (size == 0) return (void*)0;

    /* Align to 8 bytes, add header */
    size = (size + ALLOC_ALIGN - 1) & ~(size_t)(ALLOC_ALIGN - 1);
    uint32_t total = (uint32_t)size + ALLOC_HDR_SIZE;

    /* First-fit search in free list */
    struct block_hdr** prev = &free_list;
    struct block_hdr* cur = free_list;
    while (cur) {
        uint32_t bsz = blk_size(cur);
        if (bsz >= total) {
            /* Split if remainder is large enough for another block */
            if (bsz >= total + ALLOC_HDR_SIZE + ALLOC_ALIGN) {
                struct block_hdr* split = (struct block_hdr*)((char*)cur + total);
                split->size = bsz - total;
                split->next_free = cur->next_free;
                *prev = split;
                cur->size = total | ALLOC_USED_BIT;
            } else {
                /* Use entire block */
                *prev = (struct block_hdr*)(uintptr_t)cur->next_free;
                cur->size = bsz | ALLOC_USED_BIT;
            }
            return (void*)((char*)cur + ALLOC_HDR_SIZE);
        }
        prev = (struct block_hdr**)&cur->next_free;
        cur = (struct block_hdr*)(uintptr_t)cur->next_free;
    }

    /* No free block found — grow heap */
    void* p = sbrk_grow(total);
    if (!p) return (void*)0;
    struct block_hdr* b = (struct block_hdr*)p;
    b->size = total | ALLOC_USED_BIT;
    b->next_free = 0;
    return (void*)((char*)b + ALLOC_HDR_SIZE);
}

void free(void* ptr) {
    if (!ptr) return;
    struct block_hdr* b = (struct block_hdr*)((char*)ptr - ALLOC_HDR_SIZE);
    b->size &= ~ALLOC_USED_BIT;  /* mark free */

    /* Insert into free list (address-ordered for coalescing) */
    struct block_hdr** prev = &free_list;
    struct block_hdr* cur = free_list;
    while (cur && (uintptr_t)cur < (uintptr_t)b) {
        prev = (struct block_hdr**)&cur->next_free;
        cur = (struct block_hdr*)(uintptr_t)cur->next_free;
    }

    b->next_free = (uint32_t)(uintptr_t)cur;
    *prev = b;

    /* Coalesce with next block if adjacent */
    if (cur && (char*)b + blk_size(b) == (char*)cur) {
        b->size += blk_size(cur);
        b->next_free = cur->next_free;
    }

    /* Coalesce with previous block if adjacent */
    struct block_hdr* p_prev = free_list;
    if (p_prev != b) {
        while (p_prev && (struct block_hdr*)(uintptr_t)p_prev->next_free != b)
            p_prev = (struct block_hdr*)(uintptr_t)p_prev->next_free;
        if (p_prev && (char*)p_prev + blk_size(p_prev) == (char*)b) {
            p_prev->size += blk_size(b);
            p_prev->next_free = b->next_free;
        }
    }
}

void* calloc(size_t nmemb, size_t size) {
    size_t total = nmemb * size;
    if (nmemb != 0 && total / nmemb != size) return (void*)0;
    void* p = malloc(total);
    if (p) memset(p, 0, total);
    return p;
}

void* realloc(void* ptr, size_t size) {
    if (!ptr) return malloc(size);
    if (size == 0) { free(ptr); return (void*)0; }

    struct block_hdr* b = (struct block_hdr*)((char*)ptr - ALLOC_HDR_SIZE);
    uint32_t old_usable = blk_size(b) - ALLOC_HDR_SIZE;

    if (old_usable >= size) return ptr;  /* already large enough */

    void* new_ptr = malloc(size);
    if (new_ptr) {
        memcpy(new_ptr, ptr, old_usable);
        free(ptr);
    }
    return new_ptr;
}

int atoi(const char* s) {
    int n = 0;
    int neg = 0;
    while (*s == ' ' || *s == '\t') s++;
    if (*s == '-') { neg = 1; s++; }
    else if (*s == '+') { s++; }
    while (*s >= '0' && *s <= '9') {
        n = n * 10 + (*s - '0');
        s++;
    }
    return neg ? -n : n;
}

char* realpath(const char* path, char* resolved) {
    if (!path || !resolved) return (void*)0;

    char tmp[256];
    int tpos = 0;

    if (path[0] != '/') {
        if (getcwd(tmp, sizeof(tmp)) < 0) return (void*)0;
        tpos = (int)strlen(tmp);
        if (tpos > 0 && tmp[tpos - 1] != '/') tmp[tpos++] = '/';
    }

    while (*path) {
        if (*path == '/') { path++; continue; }
        if (path[0] == '.' && (path[1] == '/' || path[1] == '\0')) {
            path += 1; continue;
        }
        if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || path[2] == '\0')) {
            path += 2;
            if (tpos > 1) {
                tpos--;
                while (tpos > 0 && tmp[tpos - 1] != '/') tpos--;
            }
            continue;
        }
        if (tpos > 0 && tmp[tpos - 1] != '/') tmp[tpos++] = '/';
        while (*path && *path != '/' && tpos < 254) tmp[tpos++] = *path++;
    }

    if (tpos == 0) tmp[tpos++] = '/';
    tmp[tpos] = '\0';

    memcpy(resolved, tmp, (size_t)(tpos + 1));
    return resolved;
}

double atof(const char* s) {
    /* Minimal: parse integer part only (no FP hardware in AdrOS) */
    int neg = 0;
    while (*s == ' ' || *s == '\t') s++;
    if (*s == '-') { neg = 1; s++; }
    else if (*s == '+') { s++; }
    double val = 0.0;
    while (*s >= '0' && *s <= '9') { val = val * 10.0 + (*s - '0'); s++; }
    if (*s == '.') {
        s++;
        double frac = 0.1;
        while (*s >= '0' && *s <= '9') { val += (*s - '0') * frac; frac *= 0.1; s++; }
    }
    return neg ? -val : val;
}

long strtol(const char* nptr, char** endptr, int base) {
    const char* s = nptr;
    long result = 0;
    int neg = 0;

    while (*s == ' ' || *s == '\t') s++;
    if (*s == '-') { neg = 1; s++; }
    else if (*s == '+') { s++; }

    if (base == 0) {
        if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { base = 16; s += 2; }
        else if (s[0] == '0') { base = 8; s++; }
        else { base = 10; }
    } else if (base == 16 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
        s += 2;
    }

    while (*s) {
        int digit;
        if (*s >= '0' && *s <= '9') digit = *s - '0';
        else if (*s >= 'a' && *s <= 'z') digit = *s - 'a' + 10;
        else if (*s >= 'A' && *s <= 'Z') digit = *s - 'A' + 10;
        else break;
        if (digit >= base) break;
        result = result * base + digit;
        s++;
    }

    if (endptr) *endptr = (char*)s;
    return neg ? -result : result;
}

char* getenv(const char* name) {
    extern char** __environ;
    if (!name || !__environ) return (char*)0;
    size_t len = strlen(name);
    for (char** e = __environ; *e; e++) {
        if (strncmp(*e, name, len) == 0 && (*e)[len] == '=')
            return *e + len + 1;
    }
    return (char*)0;
}

int abs(int x) {
    return x < 0 ? -x : x;
}

long labs(long x) {
    return x < 0 ? -x : x;
}

void qsort(void* base, size_t nmemb, size_t size,
           int (*compar)(const void*, const void*)) {
    if (nmemb < 2 || !base || !compar) return;
    char* b = (char*)base;
    char tmp[256];
    for (size_t i = 1; i < nmemb; i++) {
        size_t j = i;
        while (j > 0 && compar(b + (j - 1) * size, b + j * size) > 0) {
            /* swap elements — use stack buffer for small, byte-swap for large */
            char* a1 = b + (j - 1) * size;
            char* a2 = b + j * size;
            if (size <= sizeof(tmp)) {
                memcpy(tmp, a1, size);
                memcpy(a1, a2, size);
                memcpy(a2, tmp, size);
            } else {
                for (size_t k = 0; k < size; k++) {
                    char t = a1[k]; a1[k] = a2[k]; a2[k] = t;
                }
            }
            j--;
        }
    }
}

int system(const char* cmd) {
    (void)cmd;
    return -1;
}

void* bsearch(const void* key, const void* base, size_t nmemb, size_t size,
              int (*compar)(const void*, const void*)) {
    const char* b = (const char*)base;
    size_t lo = 0, hi = nmemb;
    while (lo < hi) {
        size_t mid = lo + (hi - lo) / 2;
        int cmp = compar(key, b + mid * size);
        if (cmp == 0) return (void*)(b + mid * size);
        if (cmp < 0) hi = mid;
        else lo = mid + 1;
    }
    return (void*)0;
}

div_t div(int numer, int denom) {
    div_t r;
    r.quot = numer / denom;
    r.rem  = numer % denom;
    return r;
}

ldiv_t ldiv(long numer, long denom) {
    ldiv_t r;
    r.quot = numer / denom;
    r.rem  = numer % denom;
    return r;
}

int mkstemp(char* tmpl) {
    size_t len = strlen(tmpl);
    if (len < 6) return -1;
    char* suffix = tmpl + len - 6;
    /* Check template ends with XXXXXX */
    for (int i = 0; i < 6; i++)
        if (suffix[i] != 'X') return -1;
    /* Generate unique name using pid + counter */
    extern int getpid(void);
    static int counter = 0;
    int id = getpid() * 1000 + (counter++ % 1000);
    for (int i = 5; i >= 0; i--) {
        suffix[i] = (char)('0' + id % 10);
        id /= 10;
    }
    int fd = open(tmpl, 1 | 0x40 | 0x80 /* O_WRONLY|O_CREAT|O_EXCL */);
    return fd;
}

double strtod(const char* nptr, char** endptr) {
    const char* s = nptr;
    int neg = 0;
    while (*s == ' ' || *s == '\t') s++;
    if (*s == '-') { neg = 1; s++; }
    else if (*s == '+') { s++; }
    double val = 0.0;
    while (*s >= '0' && *s <= '9') { val = val * 10.0 + (*s - '0'); s++; }
    if (*s == '.') {
        s++;
        double frac = 0.1;
        while (*s >= '0' && *s <= '9') { val += (*s - '0') * frac; frac *= 0.1; s++; }
    }
    if (*s == 'e' || *s == 'E') {
        s++;
        int eneg = 0;
        if (*s == '-') { eneg = 1; s++; }
        else if (*s == '+') { s++; }
        int exp = 0;
        while (*s >= '0' && *s <= '9') { exp = exp * 10 + (*s - '0'); s++; }
        double mult = 1.0;
        for (int i = 0; i < exp; i++) mult *= 10.0;
        if (eneg) val /= mult; else val *= mult;
    }
    if (endptr) *endptr = (char*)s;
    return neg ? -val : val;
}

float strtof(const char* nptr, char** endptr) {
    return (float)strtod(nptr, endptr);
}