view interps/cfunge/cfunge-src/src/fingerprints/HRTI/HRTI.c @ 12518:2d8fe55c6e65 draft default tip

<int-e> learn The password of the month is release incident pilot.
author HackEso <hackeso@esolangs.org>
date Sun, 03 Nov 2024 00:31:02 +0000
parents 859f9b4339e6
children
line wrap: on
line source

/* -*- mode: C; coding: utf-8; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
 *
 * cfunge - A standard-conforming Befunge93/98/109 interpreter in C.
 * Copyright (C) 2008-2009 Arvid Norlander <anmaster AT tele2 DOT se>
 *
 * 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 3 of the License, or
 * (at the proxy's option) any later version. Arvid Norlander is a
 * proxy who can decide which future versions of the GNU General Public
 * License can be used.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 */

#include "HRTI.h"
#include "../../stack.h"

#include <stdint.h>

// This code tries to select the best timer:
//  1. clock_gettime() with CLOCK_MONOTONIC
//  2. clock_gettime() with CLOCK_REALTIME
//  3. gettimeofday()

#ifdef HAVE_clock_gettime
#  include <unistd.h> /* _POSIX_MONOTONIC_CLOCK */
#  include <time.h>
typedef struct timespec timetype;
typedef long res_type;

// Select monotonic or realtime:
#  if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK > 0)
#    define TIMER_TYPE CLOCK_MONOTONIC
#  else
#    define TIMER_TYPE CLOCK_REALTIME
#  endif

#  define TIMERFUNC(x) clock_gettime(TIMER_TYPE, (x))

#  define SECONDS_MUTIPLIER 1000000000
#  define MICRO_TO_SMALL 1000

#  define MSEC(x) ((x) / 1000)
#  define MSEC_P(x) ((x)->tv_nsec / 1000)

#  define SMALL_P(x) ((x)->tv_nsec)

#  define ZERO_TIMETYPE(x) \
	{ \
		(x)->tv_sec = 0; \
		(x)->tv_nsec = 0; \
	}

#else
// Fallback on gettimeofday.
#  include <sys/time.h>
typedef struct timeval timetype;
typedef suseconds_t res_type;

#  define TIMERFUNC(x) gettimeofday((x), NULL)

#  define SECONDS_MUTIPLIER 1000000
#  define MICRO_TO_SMALL 1

#  define MSEC(x) (x)
#  define MSEC_P(x) ((x)->tv_usec)

#  define SMALL_P(x) ((x)->tv_usec)

#  define ZERO_TIMETYPE(x) \
	{ \
		(x)->tv_sec = 0; \
		(x)->tv_usec = 0; \
	}

#endif


/// The resolution.
static res_type resolution = 0;

FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL FUNGE_ATTR_PURE FUNGE_ATTR_WARN_UNUSED
static inline funge_cell get_difference(const timetype * restrict before,
                                        const timetype * restrict after)
{
	return 1000000 * ((funge_cell)after->tv_sec - (funge_cell)before->tv_sec)
	       + MSEC((funge_cell)SMALL_P(after) - (funge_cell)SMALL_P(before));
}

/// This function checks that the IP have a non-null HRTI data pointer.
FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL FUNGE_ATTR_WARN_UNUSED
static inline bool check_ip_have_HRTI(instructionPointer * ip)
{
	if (!ip->fingerHRTItimestamp) {
		ip->fingerHRTItimestamp = cf_malloc_noptr(sizeof(timetype));
		if (FUNGE_UNLIKELY(!ip->fingerHRTItimestamp))
			return false;
		ZERO_TIMETYPE((timetype*)ip->fingerHRTItimestamp);
	}
	return true;
}

/// E - Erase Mark
static void finger_HRTI_erase_mark(instructionPointer * ip)
{
	if (!check_ip_have_HRTI(ip)) {
		ip_reverse(ip);
		return;
	}

	ZERO_TIMETYPE((timetype*)ip->fingerHRTItimestamp);
}

/// G - Granularity
static void finger_HRTI_granularity(instructionPointer * ip)
{
	stack_push(ip->stack, (funge_cell)resolution);
}

/// M - Mark
static void finger_HRTI_mark(instructionPointer * ip)
{
	if (!check_ip_have_HRTI(ip)) {
		ip_reverse(ip);
		return;
	}

	TIMERFUNC(ip->fingerHRTItimestamp);
}

/// T - Timer
static void finger_HRTI_timer(instructionPointer * ip)
{
	if (!ip->fingerHRTItimestamp || (((timetype*)ip->fingerHRTItimestamp)->tv_sec == 0)) {
		ip_reverse(ip);
	} else {
		timetype curTime;
		TIMERFUNC(&curTime);
		stack_push(ip->stack, get_difference(ip->fingerHRTItimestamp, &curTime));
	}
}

/// S - Second
static void finger_HRTI_second(instructionPointer * ip)
{
	timetype curTime;
	TIMERFUNC(&curTime);
	stack_push(ip->stack, (funge_cell)MSEC_P(&curTime));
}

FUNGE_ATTR_FAST static inline bool setup_HRTI(instructionPointer * ip)
{
	// This bit is global
	if (resolution == 0) {
		timetype before;
		timetype after;

		TIMERFUNC(&before);
		do {
			TIMERFUNC(&after);
			resolution = SECONDS_MUTIPLIER * (after.tv_sec - before.tv_sec) + SMALL_P(&after) - SMALL_P(&before);
		} while (resolution < MICRO_TO_SMALL);
	}
	resolution = MSEC(resolution);
	// Per IP, set up the data
	return check_ip_have_HRTI(ip);
}

bool finger_HRTI_load(instructionPointer * ip)
{
	if (FUNGE_UNLIKELY(!setup_HRTI(ip)))
		return false;
	manager_add_opcode(HRTI, 'E', erase_mark)
	manager_add_opcode(HRTI, 'G', granularity)
	manager_add_opcode(HRTI, 'M', mark)
	manager_add_opcode(HRTI, 'T', timer)
	manager_add_opcode(HRTI, 'S', second)

	return true;
}