/*
 * @author  xamidev <xamidev@riseup.net>
 * @brief   Common memory utilities
 * @license GPL-3.0-only
 */

#include <stddef.h>
#include <stdint.h>
#include <limine.h>
#include <kernel.h>
#include <string/string.h>

// We won't be linked to standard library, but still need the basic mem* functions
// so everything goes allright with the compiler

// We use the "restrict" keyword on pointers so that the compiler knows it can
// do more optimization on them (and as it's a much used function, it's good to
// be able to do that)

/*
 * memcpy - Copy memory from one place to another
 * @dest: pointer to the destination region
 * @src:  pointer to the source region
 * @n:    amount of bytes to copy
 *
 * This function copies n bytes of memory from
 * src to dest.
 *
 * Return:
 * <dest> - Pointer to destination region
 */
void* memcpy(void* restrict dest, const void* restrict src, size_t n)
{
	uint8_t* restrict pdest = (uint8_t* restrict)dest;
	const uint8_t* restrict psrc = (const uint8_t* restrict)src;

	for (size_t i=0; i<n; i++) {
		pdest[i] = psrc[i];
	}

	return dest;
}

/*
 * memset - Sets a memory region to given byte
 * @s:    pointer to memory region
 * @c:    byte to be written
 * @n:    amount of bytes to write
 *
 * This function writes n times the byte c
 * to the memory region pointed to by s.
 *
 * Return:
 * <s> - Pointer to memory region
 */
void* memset(void* s, int c, size_t n)
{
	uint8_t* p = (uint8_t*)s;

	for (size_t i=0; i<n; i++) {
		p[i] = (uint8_t)c;
	}

	return s;
}

/*
 * memmove - Move memory from one place to another
 * @dest: pointer to the destination region
 * @src:  pointer to the source region
 * @n:    amount of bytes to move
 *
 * This function moves n bytes of memory from
 * src to dest.
 *
 * Return:
 * <dest> - Pointer to destination region
 */
void* memmove(void *dest, const void* src, size_t n)
{
	uint8_t* pdest = (uint8_t*)dest;
	const uint8_t* psrc = (uint8_t*)src;

	if (src > dest) {
		for (size_t i=0; i<n; i++) {
			pdest[i] = psrc[i];
		}
	} else if (src < dest) {
		for (size_t i=n; i>0; i--) {
			pdest[i-1] = psrc[i-1];
		}
	}
	return dest;	
}

/*
 * memcmp - Compare two memory regions
 * @s1:   pointer to the first region
 * @s2:   pointer to the second region
 * @n:    amount of bytes to compare
 *
 * This function compares n bytes of memory
 * bewteen regions pointed to by s1 and s2.
 *
 * Return:
 * %0     - if s1 and s2 are equal 
 * %-1    - if s1 is smaller than s2
 * %1     - if s1 is greater than s2
 */
int memcmp(const void* s1, const void* s2, size_t n)
{
	const uint8_t* p1 = (const uint8_t*)s1;
	const uint8_t* p2 = (const uint8_t*)s2;

	for (size_t i=0; i<n; i++) {
		if (p1[i] != p2[i]) {
			return p1[i] < p2[i] ? -1 : 1;
		}
	}

	return 0;
}