/*
 * string.c
 *
 * Copyright (C) 2017 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <string.h>
#include <ctype.h>

char *strcpy(char *destination, const char *source)
{
    while (*source != 0) *(destination++) = *(source++);
    *destination = 0;
    return destination;
}

char *strncpy(char *destination, const char *source, uint32_t num)
{
    char *ptr = destination;
    while ((num--) && (*source)) *(ptr++) = *(source++);
    *ptr = 0;
    return destination;
}

int strcmp(const char *str1, const char *str2)
{
    while (*str1 || *str2)
    {
        if (*str1 != *str2) break;
        str1++;
        str2++;
    }

    if (*str1 < *str2) return -1;
    else if (*str1 > *str2) return 1;
    else return 0;
}

int stricmp(const char *str1, const char *str2)
{
    while (*str1 || *str2)
    {
        if (tolower(*str1) != tolower(*str2)) break;
        str1++;
        str2++;
    }
    if (*str1 < *str2) return -1;
    else if (*str1 > *str2) return 1;
    else return 0;
}

int strncmp(const char *str1, const char *str2, size_t n)
{
    int i;

    for (i = 0; i < n; i++)
    {
        if (str1[i] < str2[i]) return -1;
        else if (str1[i] > str2[i]) return 1;
        if (str1[i] == 0 || str2[i] == 0) break;
    }

    return 0;
}

char *strstr(const char *haystack, const char *needle)
{
    while (*haystack)
    {
        const char *s1 = haystack;
        const char *s2 = needle;
        int found = 1;

        while (*s2)
        {
            if (*s1++ != *s2++)
            {
                found = 0;
                break;
            }
        }

        if (found) return (char*)haystack;
        haystack++;
    }

    return NULL;
}

char *strchr(const char *str, char c)
{
    while ((*str != 0) && (*str != c)) str++;
    if (*str == c) return (char*)str;
    else return NULL;
}

char *strrchr(const char *str, char c)
{
    char *ret = NULL;

    while (*str != 0)
    {
        if (*str == c) ret = (char*)str;
        str++;
    }

    return ret;
}

char *strcat(char *destination, const char *source)
{
    strcpy(strchr(destination, '\0'), source);
    return destination;
}

char *strncat(char *destination, const char *source, size_t n)
{
    char *ptr = strchr(destination, 0);
    while ((n--) && (*source)) *(ptr++) = *(source++);
    *ptr = 0;
    return destination;
}

char *strdup(const char *source)
{
    char *destination = (char*)malloc(strlen(source) + 1);
    if (destination) strcpy(destination, source);
    return destination;
}

char *strtok_r(char *str, const char *delimiters, char **endptr)
{
    if (str == NULL) str = *endptr;
    char *start = str;

    while (*str != 0)
    {
        if (strchr(delimiters, *str) != NULL)
        {
            *str = 0;
            *endptr = str + 1;
            return start;
        }

        str++;
    }

    *endptr = str;
    return start != str ? start : NULL;
}

char *strtok(char *str, const char *delimiters)
{
    static char *endptr;
    return strtok_r(str, delimiters, &endptr);
}

size_t strlen(const char *str)
{
    int ret = 0;
    while (str[ret] != 0) ret++;
    return ret;
}

void strrev(char *str)
{
    size_t i, j = strlen(str) - 1;

    while (i < j)
    {
        int t = str[i];
        str[i] = str[j];
        str[j] = t;

        i++;
        j--;
    }
}

void memset(void *ptr, int value, size_t n)
{
    uint8_t *ptr_byte = (uint8_t*)ptr;
    value &= 0xFF;
    uint32_t tile = value | (value << 8) | (value << 16) | (value << 24);

    if (n >> 2)
    {
        asm volatile ("cld\n"
                      "rep stosl\n"
                      : "=D"(ptr_byte)
                      : "D"(ptr_byte), "a"(tile), "c"(n >> 2)
                      : "cc");
    }

    switch (n & 3)
    {
    case 3:
        *ptr_byte++ = value;
    case 2:
        *ptr_byte++ = value;
    case 1:
        *ptr_byte++ = value;
    }
}

void memcpy(void *destination, const void *source, size_t n)
{
    uint8_t *src_byte = (uint8_t*)source;
    uint8_t *dest_byte = (uint8_t*)destination;
    if (!n) return;

    if (n >> 2)
    {
        asm volatile ("cld\n"
                      "rep movsl\n"
                      : "=D"(dest_byte), "=S"(src_byte)
                      : "D"(destination), "S"(source), "c"(n >> 2)
                      : "cc", "memory");
    }

    switch (n & 3)
    {
    case 3:
        *dest_byte++ = *src_byte++;
    case 2:
        *dest_byte++ = *src_byte++;
    case 1:
        *dest_byte++ = *src_byte++;
    }
}

void memmove(void *destination, const void *source, size_t n)
{
    if (!n) return;

    if (destination < source)
    {
        uint8_t *src_byte = (uint8_t*)source;
        uint8_t *dest_byte = (uint8_t*)destination;

        if (n >> 2)
        {
            asm volatile ("cld\n"
                          "rep movsl\n"
                          : "=D"(dest_byte), "=S"(src_byte)
                          : "D"(destination), "S"(source), "c"(n >> 2)
                          : "cc", "memory");
        }

        switch (n & 3)
        {
        case 3:
            *dest_byte++ = *src_byte++;
        case 2:
            *dest_byte++ = *src_byte++;
        case 1:
            *dest_byte++ = *src_byte++;
        }
    }
    else if (destination > source)
    {
        uint8_t *src_byte = (uint8_t*)source + n;
        uint8_t *dest_byte = (uint8_t*)destination + n;

        switch (n & 3)
        {
        case 3:
            *--dest_byte = *--src_byte;
        case 2:
            *--dest_byte = *--src_byte;
        case 1:
            *--dest_byte = *--src_byte;
        }

        if (n >> 2)
        {
            asm volatile ("std\n"
                          "rep movsl\n"
                          :
                          : "D"(dest_byte - 4), "S"(src_byte - 4), "c"(n >> 2)
                          : "cc", "memory");
        }
    }
}

int memcmp(const void *mem1, const void *mem2, size_t n)
{
    uint32_t flags;

    asm volatile ("cld\n"
                  "repz cmpsb\n"
                  "pushfl\n"
                  "popl %0\n"
                  : "=r"(flags)
                  : "S"(mem1), "D"(mem2), "c"(n)
                  : "cc");

    if (flags & 0x40) return 0;
    else return (flags & 1) ? 1 : -1;
}
