/*
 * Copyright (c) 2011-2020 Mellanox Technologies Ltd.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. Neither the name of the Mellanox Technologies Ltd 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 AUTHOR ``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 AUTHOR 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.
 */

/**
 * @file ticks_os.h
 *
 *
 * @details: Base functions taken from ticks.h in favor of Windows compatibility,
 * using QueryPerformanceFrequency() and QueryPerformanceCounter() for max accuracy.
 *
 *
 * @author  Meny Yossefi <menyy@mellanox.com>
 *
 **/
#ifndef _TICKS_OS_H_
#define _TICKS_OS_H_

#include <time.h> /* clock_gettime()*/
#include <exception>
#include <stdint.h> // for int64_t
#include <stdlib.h> // for qsort
#include "clock.h"

typedef int64_t ticks_t;

#ifdef WIN32

#include <WinSock2.h>
#include <Winbase.h>

#pragma warning(disable : 4290) // exception specification ignored except to indicate a function is
                                // not __declspec(nothrow)
#pragma warning(disable                                                                            \
                : 4996) // The compiler encountered a function that was marked with deprecated.

#define usleep(x) Sleep(x / 1000)
#define snprintf _snprintf

struct timespec {
    time_t tv_sec;    // Seconds.
    long int tv_nsec; // Nanoseconds.
};

#endif

/**
 * RDTSC extensions
 */
#define TSCVAL_INITIALIZER (0)

static const int64_t NSEC_IN_SEC = 1000 * 1000 * 1000;
// Utility function
inline ticks_t timespec2nsec(const struct timespec &_val) {
    return NSEC_IN_SEC * _val.tv_sec + _val.tv_nsec;
}

inline ticks_t os_gettimeoftsc() {
#ifdef WIN32
    LARGE_INTEGER lp;
    double PCFreq = 0.0;
    QueryPerformanceFrequency(&lp);
    PCFreq = (double(lp.QuadPart)) / (double)NSEC_IN_SEC; // NanoSec
    QueryPerformanceCounter(&lp);
    lp.QuadPart = (LONGLONG)((lp.QuadPart) / (PCFreq));
    return (ticks_t)(lp.QuadPart);
#else
    register uint32_t upper_32, lower_32;

#if defined(__powerpc64__)
    unsigned long long ret;
    asm volatile("mftb %0" : "=r"(ret) :);
    return (ticks_t)ret;
#elif defined(__s390__)
    unsigned long long ret;
    asm volatile("stck %0" : "=Q"(ret) : : "cc");
    return (ticks_t)ret;
#elif defined(__arm__) || defined(__aarch64__)
    // so the compiler will not complain. for
    // arm compile, this inline is not used
    // since no rdtsc supported on most arm processors
    upper_32 = lower_32 = 0;
/*throw("rdtsc not supported in arm");*/
#else
    // ReaD Time Stamp Counter (RDTCS)
    __asm__ __volatile__("rdtsc" : "=a"(lower_32), "=d"(upper_32));
#endif
    // Return to user
    return (((ticks_t)upper_32) << 32) | lower_32;
#endif
}

inline void os_ts_gettimeofclock(struct timespec *pts) {
#ifdef WIN32
    ticks_t val = os_gettimeoftsc(); // probably just NSEC_IN_SEC
    pts->tv_sec = val / NSEC_IN_SEC;
    pts->tv_nsec = val % NSEC_IN_SEC;
#else

    if (clock_gettime(CLOCK_MONOTONIC, pts)) {
        throw("clock_gettime failed");
    }
#endif
}

inline ticks_t os_gettimeofclock() {
    struct timespec ts;
    os_ts_gettimeofclock(&ts);
    return timespec2nsec(ts);
}

#endif /*_TICKS_OS_H_*/
