/*******************************************************************************
* Copyright 2003-2020 Intel Corporation.
*
* This software and the related documents are Intel copyrighted  materials,  and
* your use of  them is  governed by the  express license  under which  they were
* provided to you (License).  Unless the License provides otherwise, you may not
* use, modify, copy, publish, distribute,  disclose or transmit this software or
* the related documents without Intel's prior written permission.
*
* This software and the related documents  are provided as  is,  with no express
* or implied  warranties,  other  than those  that are  expressly stated  in the
* License.
*******************************************************************************/

#include "vm_base.h"

/*
// Memory operations
*/
#if defined _WIN32
    #include <direct.h>
#else
    #if defined __APPLE__
        #include <sys/sysctl.h>
    #else
        #include <malloc.h>
    #endif
    #include <stdlib.h>
    #include <sys/stat.h>
    #include "safestring/safe_lib.h"
#endif

#include <errno.h>

void* vm_malloc(size_t iSize, size_t iAlign)
{
#if defined _WIN32
    return _aligned_malloc(iSize, iAlign);
#elif defined __APPLE__
    if(iAlign <= 1)
        return malloc(iSize);
    else
    {
        void *pBuffer  = malloc(iSize + (iAlign - 1) + sizeof(void*));
        char *pABuffer = ((char*)pBuffer) + sizeof(void*);

        pABuffer += (iAlign - (((size_t)pABuffer) & (iAlign - 1)));

        ((void**)pABuffer)[-1] = pBuffer;
        return pABuffer;
    }
#else
    return memalign(iAlign, iSize);
#endif
}

void vm_free(void* pBuffer)
{
#if defined _WIN32
    _aligned_free(pBuffer);
#elif defined __APPLE__
    free(((void**)pBuffer)[-1]);
#else
    free(pBuffer);
#endif
}

void* vm_memcpy(void* pDst, const void* pSrc, size_t size)
{
#if defined _WIN32
    memcpy_s(pDst, size, pSrc, size);
    return pDst;
#else
    memcpy_s(pDst, size, pSrc, size);
    return pDst;
#endif
}

/*
// Strings operations
*/
vm_char* vm_string_strncpy(vm_char *destination, const vm_char *source, size_t num)
{
#if defined _WIN32
    _tcscpy_s(destination, num, source);
    return destination;
#else
    strncpy_s(destination, num, source, num);
    return destination;
#endif
}

int vm_string_snprintf(vm_char *buffer, size_t n, const vm_char *format, ...)
{
    va_list argptr;
    int count;
    va_start(argptr, format);
    count = vm_string_vsnprintf(buffer, n, format, argptr);
    va_end(argptr);
    return count;
}

int vm_string_vsnprintf(vm_char *buffer, size_t n, const vm_char *format, va_list arg)
{
    int possible = _vsctprintf(format, arg);
    int status;

    if(!buffer || !n)
        return possible;

#if defined _WIN32
    status = _vsntprintf_s(buffer, n, _TRUNCATE, format, arg);
#else
    status = vsnprintf_s(buffer, n, _TRUNCATE, format, arg);
#endif

    if(status >= 0)
        return status;
    else if(!errno)
        return possible;
    else
        return status;
}

size_t vm_string_strlen(const vm_char *str, size_t bufLen)
{
#if defined  _WIN32 && !defined(__TARGET_ARCH_MIC)
#if defined _MBCS
    return _mbstrnlen(str, bufLen);
#elif defined _UNICODE
    return wcsnlen_s(str, bufLen);
#else
    return strnlen_s(str, bufLen);
#endif
#else
    size_t len = strlen(str);
    return (len > bufLen)?bufLen:len;
#endif
}

/*
// Files operations
*/
FILE* vm_file_fopen(const vm_char* filename, const vm_char* mode)
{
#if defined _WIN32
    FILE *pFile = NULL;
    errno_t err = _tfopen_s(&pFile, filename, mode);
    if(err == 0)
        return pFile;
    else
    {
        *_errno() = err;
        return NULL;
    }
#elif defined __APPLE__ || defined (__x86_64__) || defined(__USE_LARGEFILE64)
    return fopen(filename, mode);
#else
    return fopen64(filename, mode);
#endif
}

#if defined __unix__ || defined __APPLE__
unsigned long long vm_file_fseek(FILE *fd, long long position, int mode)
{
#if defined __ANDROID__
    return fseek(fd, (size_t)position, mode);
#elif defined __APPLE__ || defined __x86_64__
    return fseeko(fd, (off_t)position, mode);
#else
    return fseeko64(fd, (__off64_t)position, mode);
#endif
}

unsigned long long vm_file_ftell(FILE *fd)
{
#if defined __ANDROID__
    return (unsigned long long) ftell(fd);
#elif defined __APPLE__ || defined __x86_64__
    return (unsigned long long)ftello(fd);
#else
    return (unsigned long long)ftello64(fd);
#endif
}
#endif

vm_status vm_file_mkdir(const char* path)
{
    int status;
    int error;
#if defined _WIN32
    status = _mkdir(path);
    error  = *_errno();
#else
    status = mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
    error  = errno;
#endif
    if(status != 0)
    {
        if(error == EEXIST)
            return VM_FILE_ALREADY_EXISTS;
        return VM_OPERATION_FAILED;
    }
    return VM_OK;
}

/*
// Sysinfo operations
*/
#if defined __APPLE__
#include <sys/types.h>
#include <sys/sysctl.h>

/*
 * retrieve information about integer parameter
 * defined as CTL_... (ctl_class) . ENTRY (ctl_entry)
 * return value in res.
 *  status : 1  - OK
 *           0 - operation failed
 */
static unsigned int osx_sysctl_entry_32u( int ctl_class, int ctl_entry, unsigned int *res )
{
    int dcb[2];
    size_t i;
    dcb[0] = ctl_class;  dcb[1] = ctl_entry;
    i = sizeof(res[0]);
    return (sysctl(dcb, 2, res, &i, NULL, 0) != -1) ? 1 : 0;
}
#endif

unsigned int vm_sys_info_get_cpu_speed(void)
{
#if defined _WIN32
    return 0;
#elif defined __APPLE__
    unsigned int freq;
    return (osx_sysctl_entry_32u(CTL_HW, HW_CPU_FREQ, &freq)) ? (unsigned int)(freq/1000000) : 1000;
#else
    double ret = 0;
    FILE *pFile = NULL;
    vm_char buf[PATH_MAX];

    pFile = vm_file_fopen(VM_STRING("/proc/cpuinfo"), "r" );
    if (!pFile)
        return 1000;

    while ((vm_file_fgets(buf, PATH_MAX, pFile)))
    {
        if (!vm_string_strncmp(buf, VM_STRING("cpu MHz"), 7))
        {
            ret = vm_string_atol((vm_char *)(buf + 10));
            break;
        }
    }
    fclose(pFile);
    return ((unsigned int)ret);
#endif
}

unsigned int vm_sys_info_get_avail_cpu_num(void)
{
#if defined _WIN32
    unsigned int rtval = 0, i;
    SYSTEM_INFO siSysInfo;

    ZeroMemory(&siSysInfo, sizeof(SYSTEM_INFO));
    GetSystemInfo(&siSysInfo);
    for(i = 0; i < 32; ++i )
    {
        if (siSysInfo.dwActiveProcessorMask & 1)
            ++rtval;
        siSysInfo.dwActiveProcessorMask >>= 1;
    }
    return rtval;
#elif defined __APPLE__
    unsigned int cpu_num;
    return (unsigned int)((osx_sysctl_entry_32u(CTL_HW, HW_AVAILCPU, &cpu_num)) ? cpu_num : 1);
#else
    return (unsigned int)sysconf(_SC_NPROCESSORS_ONLN);
#endif
}

/*
// Time operations
*/
#ifndef _WIN32
#include <time.h>
#include <sys/time.h>
#include <sched.h>
#endif

void vm_time_sleep(unsigned int msec)
{
#if defined _WIN32
    if (msec)
        Sleep(msec);
    else
        SwitchToThread();
#else
    if (msec)
        usleep(1000 * msec);
    else
        sched_yield();
#endif
}

vm_tick vm_time_get_tick(void)
{
#if defined _WIN32
    LARGE_INTEGER t1;

    QueryPerformanceCounter(&t1);
    return t1.QuadPart;
#elif defined __APPLE__
    struct timeval time;

    gettimeofday(&time, NULL);
    return time.tv_sec*1000000+time.tv_usec;
#else
    struct timespec time;

    if(clock_gettime(CLOCK_MONOTONIC, &time) != 0)
        return 0;
    return time.tv_sec*1000000000L+time.tv_nsec;
#endif
}

vm_tick vm_time_get_frequency(void)
{
#if defined _WIN32
    LARGE_INTEGER t1;

    QueryPerformanceFrequency(&t1);
    return t1.QuadPart;
#elif defined __APPLE__
    return 1000000;
#else
    return 1000000000L;
#endif
}
