///                MentOS, The Mentoring Operating system project
/// @file stdlib.c
/// @brief
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.

#include "system/syscall_types.h"
#include "stdlib.h"
#include "string.h"
#include "assert.h"

/// @brief Number which identifies a memory area allocated through a call to
/// malloc(), calloc() or realloc().
#define MALLOC_MAGIC_NUMBER 0x600DC0DE

static inline int __malloc_is_valid_ptr(void *ptr)
{
    return (ptr && (((size_t *)ptr)[-1] == MALLOC_MAGIC_NUMBER));
}

size_t malloc_usable_size(void *ptr)
{
    if (__malloc_is_valid_ptr(ptr))
        return ((size_t *)ptr)[-2];
    return 0;
}

void *malloc(unsigned int size)
{
    size_t *_res, _size = 2 * sizeof(size_t) + size;
    __inline_syscall1(_res, brk, _size);
    _res[0] = size;
    _res[1] = MALLOC_MAGIC_NUMBER;
    return &_res[2];
}

void *calloc(size_t num, size_t size)
{
    void *ptr = malloc(num * size);
    if (ptr) {
        memset(ptr, 0, num * size);
    }
    return ptr;
}

void *realloc(void *ptr, size_t size)
{
    // C standard implementation: When NULL is passed to realloc,
    // simply malloc the requested size and return a pointer to that.
    if (__builtin_expect(ptr == NULL, 0)) {
        return malloc(size);
    }
    // C standard implementation: For a size of zero, free the
    // pointer and return NULL, allocating no new memory.
    if (__builtin_expect(size == 0, 0)) {
        free(ptr);
        return NULL;
    }
    // Get the old size.
    size_t old_size = malloc_usable_size(ptr);
    // Create the new pointer.
    void *newp = malloc(size);
    memset(newp, 0, size);
    memcpy(newp, ptr, old_size);
    free(ptr);
    return newp;
}

void free(void *ptr)
{
    int _res;
    if (__malloc_is_valid_ptr(ptr)) {
        size_t *_ptr = (size_t *)ptr - 2;
        __inline_syscall1(_res, brk, _ptr);
    } else {
        __inline_syscall1(_res, brk, ptr);
    }
}

/// Seed used to generate random numbers.
static int rseed = 0;

void srand(int x)
{
    rseed = x;
}

int rand()
{
    return rseed = (rseed * 1103515245U + 12345U) & RAND_MAX;
}
