/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "primpl.h"

#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>

#if defined(SOLARIS)

static size_t GetHighResClock(void* buf, size_t maxbytes) {
  hrtime_t t;
  t = gethrtime();
  if (t) {
    return _pr_CopyLowBits(buf, maxbytes, &t, sizeof(t));
  }
  return 0;
}

#elif defined(AIX)

static size_t GetHighResClock(void* buf, size_t maxbytes) { return 0; }

#elif (defined(LINUX) || defined(FREEBSD) || defined(__FreeBSD_kernel__) || \
       defined(NETBSD) || defined(__NetBSD_kernel__) || defined(OPENBSD) || \
       defined(__GNU__))
#  include <sys/types.h>
#  include <sys/stat.h>
#  include <fcntl.h>

static int fdDevURandom;
static PRCallOnceType coOpenDevURandom;

static PRStatus OpenDevURandom(void) {
  fdDevURandom = open("/dev/urandom", O_RDONLY);
  return ((-1 == fdDevURandom) ? PR_FAILURE : PR_SUCCESS);
} /* end OpenDevURandom() */

static size_t GetDevURandom(void* buf, size_t size) {
  int bytesIn;
  int rc;

  rc = PR_CallOnce(&coOpenDevURandom, OpenDevURandom);
  if (PR_FAILURE == rc) {
    _PR_MD_MAP_OPEN_ERROR(errno);
    return (0);
  }

  bytesIn = read(fdDevURandom, buf, size);
  if (-1 == bytesIn) {
    _PR_MD_MAP_READ_ERROR(errno);
    return (0);
  }

  return (bytesIn);
} /* end GetDevURandom() */

static size_t GetHighResClock(void* buf, size_t maxbytes) {
  return (GetDevURandom(buf, maxbytes));
}

#elif defined(NTO) || defined(QNX) || defined(DARWIN) || defined(RISCOS)
#  include <sys/times.h>

static size_t GetHighResClock(void* buf, size_t maxbytes) {
  int ticks;
  struct tms buffer;

  ticks = times(&buffer);
  return _pr_CopyLowBits(buf, maxbytes, &ticks, sizeof(ticks));
}
#else
#  error ! Platform undefined
#endif /* defined(SOLARIS) */

extern PRSize _PR_MD_GetRandomNoise(void* buf, PRSize size) {
  struct timeval tv;
  int n = 0;
  int s;

  n += GetHighResClock(buf, size);
  size -= n;

  GETTIMEOFDAY(&tv);

  if (size > 0) {
    s = _pr_CopyLowBits((char*)buf + n, size, &tv.tv_usec, sizeof(tv.tv_usec));
    size -= s;
    n += s;
  }
  if (size > 0) {
    s = _pr_CopyLowBits((char*)buf + n, size, &tv.tv_sec, sizeof(tv.tv_usec));
    size -= s;
    n += s;
  }

  return n;
} /* end _PR_MD_GetRandomNoise() */
