/*
 *   BSD LICENSE
 *
 *   Copyright(c) 2016-2020 Intel Corporation. All rights reserved.
 *   All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h> /**< gettimeofday() */

#include "common.h"
#include "rdt.h"

struct rdtset g_cfg;

int
str_to_cpuset(const char *cpustr, const unsigned cpustr_len, cpu_set_t *cpuset)
{
        unsigned idx, min, max;
        char *buff = malloc(cpustr_len + 1);
        char *end = NULL;
        const char *str = buff;
        int ret = 0;

        if (NULL == buff || NULL == cpustr || NULL == cpuset || 0 == cpustr_len)
                goto err;

        memcpy(buff, cpustr, cpustr_len);
        buff[cpustr_len] = 0;
        CPU_ZERO(cpuset);

        while (isblank(*str))
                str++;

        /* only digit is qualify for start point */
        if (!isdigit(*str) || *str == '\0')
                goto err;

        min = CPU_SETSIZE;
        do {
                /* go ahead to the first digit */
                while (isblank(*str))
                        str++;

                if (!isdigit(*str))
                        goto err;

                /* get the digit value */
                errno = 0;
                idx = strtoul(str, &end, 10);
                if (errno != 0 || end == NULL || end == str ||
                    idx >= CPU_SETSIZE)
                        goto err;

                /* go ahead to separator '-',',' */
                while (isblank(*end))
                        end++;

                if (*end == '-') {
                        if (min == CPU_SETSIZE)
                                min = idx;
                        else /* avoid continuous '-' */
                                goto err;
                } else if (*end == ',' || *end == 0) {
                        max = idx;

                        if (min == CPU_SETSIZE)
                                min = idx;

                        for (idx = MIN(min, max); idx <= MAX(min, max); idx++)
                                CPU_SET(idx, cpuset);

                        min = CPU_SETSIZE;
                } else
                        goto err;

                str = end + 1;
        } while (*end != '\0');

        ret = end - buff;

        free(buff);
        return ret;

err:
        if (buff != NULL)
                free(buff);
        return -EINVAL;
}

void
cpuset_to_str(char *cpustr, const unsigned cpustr_len, const cpu_set_t *cpuset)
{
        unsigned len = 0, j = 0;

        memset(cpustr, 0, cpustr_len);

        /* Generate CPU list */
        for (j = 0; j < CPU_SETSIZE; j++) {
                if (CPU_ISSET(j, cpuset) != 1)
                        continue;

                len += snprintf(cpustr + len, cpustr_len - len - 1, "%u,", j);

                if (len >= cpustr_len - 1) {
                        len = cpustr_len;
                        memcpy(cpustr + cpustr_len - 4, "...", 3);
                        break;
                }
        }

        /* Remove trailing separator */
        cpustr[len - 1] = 0;
}

/**
 * @brief Function to check if a value is already contained in a table
 *
 * @param tab table of values to check
 * @param size size of the table
 * @param val value to search for
 *
 * @return If the value is already in the table
 * @retval 1 if value if found
 * @retval 0 if value is not found
 */
static int
isdup(const uint64_t *tab, const unsigned size, const uint64_t val)
{
        unsigned i;

        for (i = 0; i < size; i++)
                if (tab[i] == val)
                        return 1;
        return 0;
}

/**
 * @brief Converts string into 64-bit unsigned number.
 *
 * Numbers can be in decimal or hexadecimal format.
 * On error, this functions causes process to exit with FAILURE code.
 *
 * @param s string to be converted into 64-bit unsigned number
 *
 * @return Numeric value of the string representing the number
 */
static uint64_t
strtouint64(const char *s)
{
        const char *str = s;
        int base = 10;
        uint64_t n = 0;
        char *endptr = NULL;

        if (s == NULL)
                exit(EXIT_FAILURE);

        if (strncasecmp(s, "0x", 2) == 0) {
                base = 16;
                s += 2;
        }

        n = strtoull(s, &endptr, base);

        if (!(*s != '\0' && *endptr == '\0')) {
                printf("Error converting '%s' to unsigned number!\n", str);
                exit(EXIT_FAILURE);
        }

        return n;
}

unsigned
strlisttotab(char *s, uint64_t *tab, const unsigned max)
{
        unsigned index = 0;
        char *saveptr = NULL;

        if (s == NULL || tab == NULL || max == 0)
                return index;

        for (;;) {
                char *p = NULL;
                char *token = NULL;

                token = strtok_r(s, ",", &saveptr);
                if (token == NULL)
                        break;

                s = NULL;

                /* get rid of leading spaces & skip empty tokens */
                while (isspace(*token))
                        token++;
                if (*token == '\0')
                        continue;

                p = strchr(token, '-');
                if (p != NULL) {
                        /**
                         * range of numbers provided
                         * example: 1-5 or 12-9
                         */
                        uint64_t n, start, end;
                        *p = '\0';
                        start = strtouint64(token);
                        end = strtouint64(p + 1);
                        if (start > end) {
                                /**
                                 * no big deal just swap start with end
                                 */
                                n = start;
                                start = end;
                                end = n;
                        }
                        for (n = start; n <= end; n++) {
                                if (!(isdup(tab, index, n))) {
                                        tab[index] = n;
                                        index++;
                                }
                                if (index >= max)
                                        return index;
                        }
                } else {
                        /**
                         * single number provided here
                         * remove duplicates if necessary
                         */
                        uint64_t val = strtouint64(token);

                        if (!(isdup(tab, index, val))) {
                                tab[index] = val;
                                index++;
                        }
                        if (index >= max)
                                return index;
                }
        }

        return index;
}

uint64_t
get_time_usec(void)
{
        struct timeval tv;

        gettimeofday(&tv, NULL);

        return ((uint64_t)tv.tv_usec) + ((uint64_t)tv.tv_sec * 1000000L);
}
