/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*  ZenTimer
 *  Copyright (C) 2001-2007 Jeffrey Stedfast
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */


#ifndef __ZENTINER_H__
#define __ZENTIMER_H__

#ifdef ENABLE_ZENTIMER

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>

#ifdef HAVE_STDINT_H
#include <inttypes.h>
#elif HAVE_INTTYPES_H
#include <inttypes.h>
#else
typedef unsigned char uint8_t;
typedef unsigned long int uint32_t;
typedef unsigned long long uint64_t;
#endif

#ifdef __cplusplus
extern "C" {
#pragma }
#endif /* __cplusplus */

/* G_STMT_START and G_STMT_END stolen from glib.h */
/* Provide simple macro statement wrappers (adapted from Perl):
 *  G_STMT_START { statements; } G_STMT_END;
 *  can be used as a single statement, as in
 *  if (x) G_STMT_START { ... } G_STMT_END; else ...
 *
 *  For gcc we will wrap the statements within `({' and `})' braces.
 *  For SunOS they will be wrapped within `if (1)' and `else (void) 0',
 *  and otherwise within `do' and `while (0)'.
 */
#if !(defined (G_STMT_START) && defined (G_STMT_END))
#  if defined (__GNUC__) && !defined (__STRICT_ANSI__) && !defined (__cplusplus)
#    define G_STMT_START	(void)(
#    define G_STMT_END		)
#  else
#    if (defined (sun) || defined (__sun__))
#      define G_STMT_START	if (1)
#      define G_STMT_END	else (void)0
#    else
#      define G_STMT_START	do
#      define G_STMT_END	while (0)
#    endif
#  endif
#endif

#define ZTIME_USEC_PER_SEC 1000000

typedef struct {
	uint32_t sec;
	uint32_t usec;
} ztime_t;

#ifdef G_OS_WIN32
#include <windows.h>

static void
ztime (ztime_t *ztimep)
{
	uint64_t systime;
	
	/* systime represents the number of 100-nanoseconds since Jan 1, 1601 */
	GetSystemTimeAsFileTime ((FILETIME *) &systime);
	
	/* convert to microseconds */
	systime /= 10;
	
	ztimep->sec = systime / ZTIME_USEC_PER_SEC;
	ztimep->usec = systime % ZTIME_USEC_PER_SEC;
}

#else /* POSIX OS */
#include <sys/time.h>

static void
ztime (ztime_t *ztimep)
{
	struct timeval tv;
	
	gettimeofday (&tv, NULL);
	ztimep->sec = tv.tv_sec;
	ztimep->usec = tv.tv_usec;
	
	if (ztimep->usec >= ZTIME_USEC_PER_SEC) {
		/* this is probably unneccessary */
		ztimep->sec += ztimep->usec / ZTIME_USEC_PER_SEC;
		ztimep->usec = ztimep->usec % ZTIME_USEC_PER_SEC;
	}
}

#endif /* OS */

static void
ztime_add (ztime_t *ztime, ztime_t *adj)
{
	ztime->sec += adj->sec;
	ztime->usec += adj->usec;
	ztime->sec += ztime->usec / ZTIME_USEC_PER_SEC;
	ztime->usec = ztime->usec % ZTIME_USEC_PER_SEC;
}

static void
ztime_delta (ztime_t *start, ztime_t *stop, ztime_t *delta)
{
	delta->sec = stop->sec - start->sec;
	if (stop->usec < start->usec) {
		delta->usec = (stop->usec + ZTIME_USEC_PER_SEC) - start->usec;
		delta->sec--;
	} else {
		delta->usec = stop->usec - start->usec;
	}
}

enum {
	ZTIMER_INACTIVE = 0,
	ZTIMER_ACTIVE   = (1 << 0),
	ZTIMER_PAUSED   = (1 << 1),
};

typedef uint8_t zstate_t;

typedef struct {
	zstate_t state;
	ztime_t start;
	ztime_t stop;
} ztimer_t;

#define ZTIMER_INITIALIZER { ZTIMER_INACTIVE, { 0, 0 }, { 0, 0 } }

/* default timer */
static ztimer_t __ztimer = ZTIMER_INITIALIZER;

static void
ZenTimerStart (ztimer_t *ztimer)
{
	ztimer = ztimer ? ztimer : &__ztimer;
	
	ztimer->state = ZTIMER_ACTIVE;
	ztime (&ztimer->start);
}

static void
ZenTimerStop (ztimer_t *ztimer)
{
	ztimer = ztimer ? ztimer : &__ztimer;
	
	ztime (&ztimer->stop);
	ztimer->state = ZTIMER_INACTIVE;
}

static void
ZenTimerPause (ztimer_t *ztimer)
{
	ztimer = ztimer ? ztimer : &__ztimer;
	
	ztime (&ztimer->stop);
	ztimer->state |= ZTIMER_PAUSED;
}

static void
ZenTimerResume (ztimer_t *ztimer)
{
	ztime_t now, delta;
	
	ztimer = ztimer ? ztimer : &__ztimer;
	
	/* unpause */
	ztimer->state &= ~ZTIMER_PAUSED;
	
	/* calculate time since paused */
	ztime (&now);
	ztime_delta (&ztimer->stop, &now, &delta);
	
	/* adjust start time to account for time elapsed since paused */
	ztime_add (&ztimer->start, &delta);
}

static void
ZenTimerReport (ztimer_t *ztimer, const char *oper)
{
	ztime_t delta;
	int paused;
	
	ztimer = ztimer ? ztimer : &__ztimer;
	
	if (ztimer->state == ZTIMER_ACTIVE) {
		ZenTimerPause (ztimer);
		paused = 1;
	} else {
		paused = 0;
	}
	
	ztime_delta (&ztimer->start, &ztimer->stop, &delta);
	
	fprintf (stderr, "ZenTimer: %s took %u.%06u seconds\n", oper,
		 (unsigned int) delta.sec, (unsigned int) delta.usec);
	
	if (paused)
		ZenTimerResume (ztimer);
}

#ifdef __cplusplus
}
#endif /* __cplusplus */

#else /* ! ENABLE_ZENTIMER */

#define ZenTimerStart(ztimerp)
#define ZenTimerStop(ztimerp)
#define ZenTimerReport(ztimerp, oper)

#endif /* ENABLE_ZENTIMER */

#endif /* __ZENTIMER_H__ */


syntax highlighted by Code2HTML, v. 0.9.1