// -*- C++ -*-
//
// OS_NS_time.inl,v 1.21 2006/01/12 20:29:23 jwillemsen Exp

#include "ace/OS_NS_string.h"
#include "ace/OS_NS_errno.h"
#include "ace/Time_Value.h"
#include "ace/OS_NS_unistd.h"
#include "ace/OS_NS_sys_time.h"

ACE_BEGIN_VERSIONED_NAMESPACE_DECL

ACE_INLINE char *
ACE_OS::asctime (const struct tm *t)
{
#if !defined (ACE_HAS_WINCE) && !defined(ACE_PSOS) || defined (ACE_PSOS_HAS_TIME)
  ACE_OS_TRACE ("ACE_OS::asctime");
  ACE_OSCALL_RETURN (ACE_STD_NAMESPACE::asctime (t), char *, 0);
#else
  // @@ WinCE doesn't have gmtime also.
  ACE_UNUSED_ARG (t);
  ACE_NOTSUP_RETURN (0);
#endif /*  !ACE_HAS_WINCE && !ACE_PSOS || ACE_PSOS_HAS_TIME */
}

ACE_INLINE char *
ACE_OS::asctime_r (const struct tm *t, char *buf, int buflen)
{
  ACE_OS_TRACE ("ACE_OS::asctime_r");
#if defined (ACE_HAS_REENTRANT_FUNCTIONS)
# if defined (ACE_HAS_2_PARAM_ASCTIME_R_AND_CTIME_R)
  char *result;
#   if defined (DIGITAL_UNIX)
  ACE_OSCALL (::_Pasctime_r (t, buf), char *, 0, result);
#   else
  ACE_OSCALL (::asctime_r (t, buf), char *, 0, result);
#   endif /* DIGITAL_UNIX */
  ACE_OS::strsncpy (buf, result, buflen);
  return buf;
# else
#   if defined (HPUX_10)
  return (::asctime_r(t, buf, buflen) == 0 ? buf : (char *)0);
#   elif defined (ACE_HAS_SIZET_PTR_ASCTIME_R_AND_CTIME_R)
  ACE_OSCALL_RETURN (::asctime_r (t, buf, reinterpret_cast<size_t*>(&buflen)), char *, 0);
#   else
  ACE_OSCALL_RETURN (::asctime_r (t, buf, buflen), char *, 0);
#   endif /* HPUX_10 */
# endif /* ACE_HAS_2_PARAM_ASCTIME_R_AND_CTIME_R */
#elif ! defined (ACE_HAS_WINCE) && !defined(ACE_PSOS) || defined (ACE_PSOS_HAS_TIME)
  char *result;
  ACE_OSCALL (ACE_STD_NAMESPACE::asctime (t), char *, 0, result);
  ACE_OS::strsncpy (buf, result, buflen);
  return buf;
#else
  // @@ Same as ACE_OS::asctime (), you need to implement it
  //    yourself.
  ACE_UNUSED_ARG (t);
  ACE_UNUSED_ARG (buf);
  ACE_UNUSED_ARG (buflen);
  ACE_NOTSUP_RETURN (0);
#endif /* ACE_HAS_REENTRANT_FUNCTIONS */
}

ACE_INLINE int
ACE_OS::clock_gettime (clockid_t clockid, struct timespec *ts)
{
  ACE_OS_TRACE ("ACE_OS::clock_gettime");
#if defined (ACE_HAS_CLOCK_GETTIME)
  ACE_OSCALL_RETURN (::clock_gettime (clockid, ts), int, -1);
# elif defined (ACE_PSOS) && ! defined (ACE_PSOS_DIAB_MIPS)
  ACE_UNUSED_ARG (clockid);
  ACE_PSOS_Time_t pt;
  int result = ACE_PSOS_Time_t::get_system_time (pt);
  *ts = static_cast<struct timespec> (pt);
  return result;
#else
  ACE_UNUSED_ARG (clockid);
  ACE_UNUSED_ARG (ts);
  ACE_NOTSUP_RETURN (-1);
#endif /* ACE_HAS_CLOCK_GETTIME */
}

ACE_INLINE int
ACE_OS::clock_settime (clockid_t clockid, const struct timespec *ts)
{
#if defined (ACE_HAS_CLOCK_SETTIME)
#  if defined (ACE_HAS_NONCONST_CLOCK_SETTIME)
  ACE_OSCALL_RETURN (::clock_settime (clockid, const_cast<struct timespec *>(ts)), int, -1);
#  else
  ACE_OSCALL_RETURN (::clock_settime (clockid, ts), int, -1);
#  endif /* ACE_HAS_NONCONST_CLOCK_SETTIME */
#else
  ACE_UNUSED_ARG (clockid);
  ACE_UNUSED_ARG (ts);
  ACE_NOTSUP_RETURN (-1);
#endif /* ACE_HAS_CLOCK_SETTIME */
}

// Magic number declaration and definition for ctime and ctime_r ()
static const int ctime_buf_size = 26;

ACE_INLINE ACE_TCHAR *
ACE_OS::ctime (const time_t *t)
{
  ACE_OS_TRACE ("ACE_OS::ctime");
#if defined (ACE_HAS_BROKEN_CTIME)
  ACE_OSCALL_RETURN (::asctime (::localtime (t)), char *, 0);
#elif defined(ACE_PSOS) && ! defined (ACE_PSOS_HAS_TIME)
  return "ctime-return";
#elif defined (ACE_HAS_WINCE)
  static ACE_TCHAR buf [ctime_buf_size];
  return ACE_OS::ctime_r (t,
                          buf,
                          ctime_buf_size);
#elif defined (ACE_WIN32) && defined (ACE_USES_WCHAR)
  ACE_OSCALL_RETURN (::_wctime (t), wchar_t *, 0);
#else
#  if defined (ACE_USES_WCHAR)   /* Not Win32, else it would do the above */
  char *narrow_time;
  ACE_OSCALL (::ctime (t), char *, 0, narrow_time);
  if (narrow_time == 0)
    return 0;
  // ACE_Ascii_To_Wide::convert allocates (via new []) a wchar_t[]. If
  // we've done this before, free the previous one. Yes, this leaves a
  // small memory leak (26 characters) but there's no way around this
  // that I know of. (Steve Huston, 12-Feb-2003).
  static wchar_t *wide_time = 0;
  if (wide_time != 0)
    delete [] wide_time;
  wide_time = ACE_Ascii_To_Wide::convert (narrow_time);
  return wide_time;
#  else
  ACE_OSCALL_RETURN (::ctime (t), char *, 0);
#  endif /* ACE_USES_WCHAR */
# endif /* ACE_HAS_BROKEN_CTIME */
}

#if !defined (ACE_HAS_WINCE)  /* CE version in OS.cpp */
ACE_INLINE ACE_TCHAR *
ACE_OS::ctime_r (const time_t *t, ACE_TCHAR *buf, int buflen)
{
  ACE_OS_TRACE ("ACE_OS::ctime_r");

#if defined (ACE_HAS_REENTRANT_FUNCTIONS)

  char *bufp = 0;
#   if defined (ACE_USES_WCHAR)
  char narrow_buf[ctime_buf_size];
  bufp = narrow_buf;
#   else
  bufp = buf;
#   endif /* ACE_USES_WCHAR */

  if (buflen < ctime_buf_size)
    {
      errno = ERANGE;
      return 0;
    }
#   if defined (ACE_HAS_2_PARAM_ASCTIME_R_AND_CTIME_R)
#      if defined (DIGITAL_UNIX)
  ACE_OSCALL (::_Pctime_r (t, bufp), ACE_TCHAR *, 0, bufp);
#      else /* DIGITAL_UNIX */
  ACE_OSCALL (::ctime_r (t, bufp), char *, 0, bufp);
#      endif /* DIGITAL_UNIX */
#   else /* ACE_HAS_2_PARAM_ASCTIME_R_AND_CTIME_R */

#      if defined (ACE_CTIME_R_RETURNS_INT)
  bufp = ::ctime_r (t, bufp, buflen) == -1 ? 0 : bufp;
#      elif defined (ACE_HAS_SIZET_PTR_ASCTIME_R_AND_CTIME_R)
  bufp = ::ctime_r (t, bufp, reinterpret_cast<size_t*>(&buflen));
#      else /* ACE_CTIME_R_RETURNS_INT */
  bufp = ::ctime_r (t, bufp, buflen);
#      endif /* ACE_CTIME_R_RETURNS_INT */

#   endif /* ACE_HAS_2_PARAM_ASCTIME_R_AND_CTIME_R */

  if (bufp == 0)
    return 0;

#   if defined (ACE_USES_WCHAR)
  ACE_Ascii_To_Wide wide_buf (bufp);
  ACE_OS_String::strcpy (buf, wide_buf.wchar_rep ());
  return buf;
#   else
  return bufp;
#   endif /* ACE_USES_WCHAR */

#else /* ACE_HAS_REENTRANT_FUNCTIONS */
#   if defined(ACE_PSOS) && ! defined (ACE_PSOS_HAS_TIME)
  ACE_OS::strsncpy (buf, "ctime-return", buflen);
  return buf;
#   else /* ACE_PSOS && !ACE_PSOS_HAS_TIME */
  if (buflen < ctime_buf_size)
    {
      errno = ERANGE;
      return 0;
    }

  ACE_TCHAR *result;
#     if defined (ACE_USES_WCHAR)
  ACE_OSCALL (::_wctime (t), wchar_t *, 0, result);
#     else /* ACE_USES_WCHAR */
  ACE_OSCALL (::ctime (t), char *, 0, result);
#     endif /* ACE_USES_WCHAR */
  if (result != 0)
    ACE_OS::strsncpy (buf, result, buflen);
  return buf;
#   endif /* ACE_PSOS && !ACE_PSOS_HAS_TIME */
#endif /* ACE_HAS_REENTRANT_FUNCTIONS */
}
#endif /* !ACE_HAS_WINCE */

#if !defined (ACE_LACKS_DIFFTIME)
ACE_INLINE double
ACE_OS::difftime (time_t t1, time_t t0)
{
  return ::ace_difftime (t1, t0);
}
#endif /* ! ACE_LACKS_DIFFTIME */

#if defined (ghs) && defined (ACE_HAS_PENTIUM) && !defined (ACE_WIN32)
  extern "C" ACE_hrtime_t ACE_GETHRTIME_NAME ();
#endif /* ghs && ACE_HAS_PENTIUM */

ACE_INLINE ACE_hrtime_t
ACE_OS::gethrtime (const ACE_HRTimer_Op op)
{
  ACE_OS_TRACE ("ACE_OS::gethrtime");
#if defined (ACE_HAS_HI_RES_TIMER)
  ACE_UNUSED_ARG (op);
  return ::gethrtime ();
#elif defined (ACE_HAS_AIX_HI_RES_TIMER)
  ACE_UNUSED_ARG (op);
  timebasestruct_t tb;

  ::read_real_time(&tb, TIMEBASE_SZ);
  ::time_base_to_time(&tb, TIMEBASE_SZ);

  return ACE_hrtime_t(tb.tb_high) * ACE_ONE_SECOND_IN_NSECS + tb.tb_low;
#elif defined (ghs) && defined (ACE_HAS_PENTIUM) && !defined (ACE_WIN32)
  ACE_UNUSED_ARG (op);
  // Use .obj/gethrtime.o, which was compiled with g++.
  return ACE_GETHRTIME_NAME ();
#elif (defined(__KCC) || defined (__GNUG__) || defined (__INTEL_COMPILER)) && !defined (ACE_WIN32) && !defined(ACE_VXWORKS) && defined (ACE_HAS_PENTIUM)
  ACE_UNUSED_ARG (op);
# if defined (ACE_LACKS_LONGLONG_T)
  double now;
# else  /* ! ACE_LACKS_LONGLONG_T */
  ACE_hrtime_t now;
# endif /* ! ACE_LACKS_LONGLONG_T */

  // See comments about the RDTSC Pentium instruction for the ACE_WIN32
  // version of ACE_OS::gethrtime (), below.
  //
  // Read the high-res tick counter directly into memory variable "now".
  // The A constraint signifies a 64-bit int.
  asm volatile ("rdtsc" : "=A" (now) : : "memory");

# if defined (ACE_LACKS_LONGLONG_T)
  ACE_UINT32 least, most;
  ACE_OS::memcpy (&least, &now, sizeof (ACE_UINT32));
  ACE_OS::memcpy (&most, (u_char *) &now + sizeof (ACE_UINT32),
                  sizeof (ACE_UINT32));

  ACE_hrtime_t ret (least, most);
  return ret;
# else  /* ! ACE_LACKS_LONGLONG_T */
  return now;
# endif /* ! ACE_LACKS_LONGLONG_T */
#elif defined (linux) && defined (ACE_HAS_ALPHA_TIMER)
  // NOTE:  alphas only have a 32 bit tick (cycle) counter.  The rpcc
  // instruction actually reads 64 bits, but the high 32 bits are
  // implementation-specific.  Linux and Digital Unix, for example,
  // use them for virtual tick counts, i.e., taking into account only
  // the time that the process was running.  This information is from
  // David Mosberger's article, see comment below.
  ACE_UINT32 now;

  // The following statement is based on code published by:
  // Mosberger, David, "How to Make Your Applications Fly, Part 1",
  // Linux Journal Issue 42, October 1997, page 50.  It reads the
  // high-res tick counter directly into the memory variable.
  asm volatile ("rpcc %0" : "=r" (now) : : "memory");

  return now;
#elif defined (ACE_WIN32)
  ACE_UNUSED_ARG(op);
  LARGE_INTEGER freq;

  ::QueryPerformanceCounter (&freq);

#  if defined (ACE_LACKS_LONGLONG_T)
  ACE_UINT64 uint64_freq (freq.u.LowPart,
                          static_cast<unsigned int> (freq.u.HighPart));
  return uint64_freq;
#  else
  return freq.QuadPart;
#  endif //ACE_LACKS_LONGLONG_T

#elif defined (CHORUS)
  if (op == ACE_OS::ACE_HRTIMER_GETTIME)
    {
      struct timespec ts;

      ACE_OS::clock_gettime (CLOCK_REALTIME, &ts);

      // Carefully create the return value to avoid arithmetic overflow
      // if ACE_hrtime_t is ACE_U_LongLong.
      ACE_hrtime_t now = ts.tv_sec;
      now *= ACE_U_ONE_SECOND_IN_NSECS;
      now += ts.tv_nsec;

      return now;
    }
  else
    {
      // Use the sysBench timer on Chorus.  On MVME177, at least, it only
      // has 32 bits.  Be careful, because using it disables interrupts!
      ACE_UINT32 now;
      if (::sysBench (op, (int *) &now) == K_OK)
        {
          now *= 1000u /* nanoseconds/microsecond */;
          return (ACE_hrtime_t) now;
        }
      else
        {
          // Something went wrong.  Just return 0.
          return (ACE_hrtime_t) 0;
        }
    }

#elif defined (ACE_HAS_POWERPC_TIMER) && (defined (ghs) || defined (__GNUG__))
  // PowerPC w/ GreenHills or g++.

  ACE_UNUSED_ARG (op);
  u_long most;
  u_long least;

#if defined (ghs)
  ACE_OS::readPPCTimeBase (most, least);
#else
  u_long scratch;

  do {
    asm volatile ("mftbu %0\n"
          "mftb  %1\n"
          "mftbu %2"
          : "=r" (most), "=r" (least), "=r" (scratch));
  } while (most != scratch);
#endif

#if defined (ACE_LACKS_LONGLONG_T)
  return ACE_U_LongLong (least, most);
#else  /* ! ACE_LACKS_LONGLONG_T */
  return 0x100000000llu * most  +  least;
#endif /* ! ACE_LACKS_LONGLONG_T */

#elif defined (ACE_HAS_CLOCK_GETTIME) || defined (ACE_PSOS)
  // e.g., VxWorks (besides POWERPC && GreenHills) . . .
  ACE_UNUSED_ARG (op);
  struct timespec ts;

  ACE_OS::clock_gettime (
#if defined (ACE_HAS_CLOCK_GETTIME_MONOTONIC)
         CLOCK_MONOTONIC,
#endif /* !ACE_HAS_CLOCK_GETTIME_MONOTONIC */
         CLOCK_REALTIME,
         &ts);

  // Carefully create the return value to avoid arithmetic overflow
  // if ACE_hrtime_t is ACE_U_LongLong.
  return static_cast<ACE_hrtime_t> (ts.tv_sec) *
    ACE_U_ONE_SECOND_IN_NSECS  +  static_cast<ACE_hrtime_t> (ts.tv_nsec);
#else
  ACE_UNUSED_ARG (op);
  const ACE_Time_Value now = ACE_OS::gettimeofday ();

  // Carefully create the return value to avoid arithmetic overflow
  // if ACE_hrtime_t is ACE_U_LongLong.
  return (static_cast<ACE_hrtime_t> (now.sec ()) * (ACE_UINT32) 1000000  +
          static_cast<ACE_hrtime_t> (now.usec ())) * (ACE_UINT32) 1000;
#endif /* ACE_HAS_HI_RES_TIMER */
}

ACE_INLINE struct tm *
ACE_OS::gmtime (const time_t *t)
{
#if !defined (ACE_HAS_WINCE) && !defined (ACE_PSOS) || defined (ACE_PSOS_HAS_TIME)
  ACE_OS_TRACE ("ACE_OS::gmtime");
  ACE_OSCALL_RETURN (::gmtime (t), struct tm *, 0);
#else
  // @@ WinCE doesn't have gmtime also.
  ACE_UNUSED_ARG (t);
  ACE_NOTSUP_RETURN (0);
#endif /* ACE_HAS_WINCE && !ACE_PSOS || ACE_PSOS_HAS_TIME */
}

ACE_INLINE struct tm *
ACE_OS::gmtime_r (const time_t *t, struct tm *res)
{
  ACE_OS_TRACE ("ACE_OS::gmtime_r");
#if defined (ACE_HAS_REENTRANT_FUNCTIONS)
# if defined (DIGITAL_UNIX)
  ACE_OSCALL_RETURN (::_Pgmtime_r (t, res), struct tm *, 0);
# elif defined (HPUX_10)
  return (::gmtime_r (t, res) == 0 ? res : (struct tm *) 0);
# else
  ACE_OSCALL_RETURN (::gmtime_r (t, res), struct tm *, 0);
# endif /* DIGITAL_UNIX */
#elif !defined (ACE_HAS_WINCE) && !defined(ACE_PSOS) || defined (ACE_PSOS_HAS_TIME)
  struct tm *result;
  ACE_OSCALL (::gmtime (t), struct tm *, 0, result) ;
  if (result != 0)
    *res = *result;
  return res;
#else
  // @@ Same as ACE_OS::gmtime (), you need to implement it
  //    yourself.
  ACE_UNUSED_ARG (t);
  ACE_UNUSED_ARG (res);
  ACE_NOTSUP_RETURN (0);
#endif /* ACE_HAS_REENTRANT_FUNCTIONS */
}

ACE_INLINE struct tm *
ACE_OS::localtime (const time_t *t)
{
#if !defined (ACE_HAS_WINCE) && !defined (ACE_PSOS) || defined (ACE_PSOS_HAS_TIME)
  ACE_OS_TRACE ("ACE_OS::localtime");
  ACE_OSCALL_RETURN (::localtime (t), struct tm *, 0);
#else
  // @@ Don't you start wondering what kind of functions
  //    does WinCE really support?
  ACE_UNUSED_ARG (t);
  ACE_NOTSUP_RETURN (0);
#endif /* ACE_HAS_WINCE && !ACE_PSOS || ACE_PSOS_HAS_TIME */
}

ACE_INLINE int
ACE_OS::nanosleep (const struct timespec *requested,
                   struct timespec *remaining)
{
  ACE_OS_TRACE ("ACE_OS::nanosleep");
#if defined (ACE_HAS_CLOCK_GETTIME)
  // ::nanosleep () is POSIX 1003.1b.  So is ::clock_gettime ().  So,
  // if ACE_HAS_CLOCK_GETTIME is defined, then ::nanosleep () should
  // be available on the platform.  On Solaris 2.x, both functions
  // require linking with -lposix4.
  return ::nanosleep ((ACE_TIMESPEC_PTR) requested, remaining);
#elif defined (ACE_PSOS)
#  if ! defined (ACE_PSOS_DIAB_MIPS)
  double ticks =
    KC_TICKS2SEC * requested->tv_sec +
    (static_cast<double> (requested->tv_nsec) *
     static_cast<double> (KC_TICKS2SEC) ) /
    static_cast<double> (ACE_ONE_SECOND_IN_NSECS);

  if (ticks > static_cast<double> (ACE_PSOS_Time_t::max_ticks))
    {
      ticks -= static_cast<double> (ACE_PSOS_Time_t::max_ticks);
      remaining->tv_sec =
        static_cast<time_t> ((ticks / static_cast<double> (KC_TICKS2SEC)));
      ticks -= static_cast<double> (remaining->tv_sec) *
        static_cast<double> (KC_TICKS2SEC);

      remaining->tv_nsec =
        static_cast<long> ((ticks
                            * static_cast<double> (ACE_ONE_SECOND_IN_NSECS)) /
                           static_cast<double> (KC_TICKS2SEC));

      ::tm_wkafter (ACE_PSOS_Time_t::max_ticks);
    }
  else
    {
      remaining->tv_sec = 0;
      remaining->tv_nsec = 0;
      ::tm_wkafter (static_cast<u_long> (ticks));
    }

  // tm_wkafter always returns 0
#  endif /* ACE_PSOS_DIAB_MIPS */
  return 0;
#else
  ACE_UNUSED_ARG (remaining);

  // Convert into seconds and microseconds.
  ACE_Time_Value tv (requested->tv_sec,
                     requested->tv_nsec / 1000);
  return ACE_OS::sleep (tv);
#endif /* ACE_HAS_CLOCK_GETTIME */
}

ACE_INLINE size_t
ACE_OS::strftime (char *s, size_t maxsize, const char *format,
                  const struct tm *timeptr)
{
#if !defined (ACE_HAS_WINCE) && !defined(ACE_PSOS) || defined (ACE_PSOS_HAS_TIME)
  return ACE_STD_NAMESPACE::strftime (s, maxsize, format, timeptr);
#else
  ACE_UNUSED_ARG (s);
  ACE_UNUSED_ARG (maxsize);
  ACE_UNUSED_ARG (format);
  ACE_UNUSED_ARG (timeptr);
  ACE_NOTSUP_RETURN (0);
#endif /* !ACE_HAS_WINCE && !ACE_PSOS || ACE_PSOS_HAS_TIME */
}

ACE_INLINE time_t
ACE_OS::time (time_t *tloc)
{
#if !defined (ACE_HAS_WINCE)
  ACE_OS_TRACE ("ACE_OS::time");
#  if defined (ACE_PSOS) && ! defined (ACE_PSOS_HAS_TIME)
        unsigned long d_date, d_time, d_tick;
        tm_get(&d_date, &d_time, &d_tick); // get current time
        if (tloc)
                *tloc = d_time; // set time as time_t
        return d_time;
#  else
  ACE_OSCALL_RETURN (::time (tloc), time_t, (time_t) -1);
#  endif /* ACE_PSOS && ! ACE_PSOS_HAS_TIME */
#else
  time_t retv = ACE_OS::gettimeofday ().sec ();
  if (tloc)
    *tloc = retv;
  return retv;
#endif /* ACE_HAS_WINCE */
}

// Linux won't compile unless we explicitly use a namespace here.
#if defined (__GNUG__)
namespace ACE_OS {
  ACE_INLINE long
  timezone (void)
  {
    return ::ace_timezone ();
  }
} /* namespace ACE_OS */
#else
ACE_INLINE long
ACE_OS::timezone (void)
{
  return ::ace_timezone ();
}
#endif /* linux */

ACE_INLINE void
ACE_OS::tzset (void)
{
#if !defined (ACE_HAS_WINCE) && !defined (VXWORKS) && !defined (ACE_PSOS) && !defined(__rtems__) && !defined (ACE_HAS_DINKUM_STL)
#   if defined (ACE_WIN32)
  ::_tzset ();  // For Win32.
#   else
  ::tzset ();   // For UNIX platforms.
#   endif /* ACE_WIN32 */
# else
  errno = ENOTSUP;
# endif /* ACE_HAS_WINCE && !VXWORKS && !ACE_PSOS && !__rtems__ && !ACE_HAS_DINKUM_STL */
}

ACE_END_VERSIONED_NAMESPACE_DECL
