diff interps/cfunge/cfunge-src/src/stack.c @ 996:859f9b4339e6

<Gregor> tar xf egobot.tar.xz
author HackBot
date Sun, 09 Dec 2012 19:30:08 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/interps/cfunge/cfunge-src/src/stack.c	Sun Dec 09 19:30:08 2012 +0000
@@ -0,0 +1,564 @@
+/* -*- 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 "global.h"
+#include "stack.h"
+#include "vector.h"
+#include "ip.h"
+#include "settings.h"
+#include "diagnostic.h"
+
+#include <assert.h>
+#include <string.h> /* memcpy, memset */
+
+/// How many new items to allocate in one go?
+#define ALLOCCHUNKSIZE 4096
+
+/******************************
+ * Constructor and destructor *
+ ******************************/
+
+FUNGE_ATTR_FAST funge_stack * stack_create(void)
+{
+	funge_stack * tmp = (funge_stack*)cf_malloc(sizeof(funge_stack));
+	if (FUNGE_UNLIKELY(!tmp))
+		return NULL;
+	tmp->entries = (funge_cell*)cf_malloc_noptr(ALLOCCHUNKSIZE * sizeof(funge_cell));
+	if (FUNGE_UNLIKELY(!tmp->entries)) {
+		cf_free(tmp);
+		return NULL;
+	}
+	tmp->size = ALLOCCHUNKSIZE;
+	tmp->top = 0;
+	return tmp;
+}
+
+FUNGE_ATTR_FAST void stack_free(funge_stack * stack)
+{
+	if (FUNGE_UNLIKELY(!stack))
+		return;
+	if (FUNGE_LIKELY(stack->entries != NULL)) {
+		cf_free(stack->entries);
+		stack->entries = NULL;
+	}
+	cf_free(stack);
+}
+
+#ifdef CONCURRENT_FUNGE
+// Used for concurrency
+FUNGE_ATTR_FAST FUNGE_ATTR_MALLOC FUNGE_ATTR_NONNULL FUNGE_ATTR_WARN_UNUSED
+static inline funge_stack * stack_duplicate(const funge_stack * old)
+{
+	funge_stack * tmp = (funge_stack*)cf_malloc(sizeof(funge_stack));
+	if (FUNGE_UNLIKELY(!tmp))
+		return NULL;
+	tmp->entries = (funge_cell*)cf_malloc_noptr((old->top + 1) * sizeof(funge_cell));
+	if (FUNGE_UNLIKELY(!tmp->entries)) {
+		cf_free(tmp);
+		return NULL;
+	}
+	tmp->size = old->top + 1;
+	tmp->top = old->top;
+	// Not sure if memcpy() on 0 is well defined, so lets be careful.
+	if (tmp->top != 0)
+		memcpy(tmp->entries, old->entries, sizeof(funge_cell) * tmp->top);
+	return tmp;
+}
+#endif
+
+FUNGE_ATTR_FAST FUNGE_ATTR_COLD FUNGE_ATTR_NORET
+static void stack_oom(void)
+{
+	DIAG_OOM("Failed to allocate enough memory for new stack items");
+}
+
+/*************************************
+ * Basic push/pop/peeks and prealloc *
+ *************************************/
+
+FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
+static inline void stack_prealloc_space(funge_stack * restrict stack, size_t minfree)
+{
+	if ((stack->top + minfree) >= stack->size) {
+		size_t newsize = stack->size + minfree;
+		// Round upwards to whole ALLOCCHUNKSIZEed blocks.
+		newsize += ALLOCCHUNKSIZE - (newsize % ALLOCCHUNKSIZE);
+		stack->entries = (funge_cell*)cf_realloc(stack->entries, newsize * sizeof(funge_cell));
+		if (FUNGE_UNLIKELY(!stack->entries)) {
+			stack_oom();
+		}
+		stack->size = newsize;
+	}
+}
+
+FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
+static inline void stack_push_no_check(funge_stack * restrict stack, funge_cell value)
+{
+	// This should only be used if that is true...
+	assert(stack->top < stack->size);
+	stack->entries[stack->top] = value;
+	stack->top++;
+}
+
+FUNGE_ATTR_FAST void stack_push(funge_stack * restrict stack, funge_cell value)
+{
+	assert(stack != NULL);
+	assert(stack->top <= stack->size);
+
+	// Do we need to realloc?
+	if (FUNGE_UNLIKELY(stack->top == stack->size)) {
+		stack->entries = (funge_cell*)cf_realloc(stack->entries, (stack->size + ALLOCCHUNKSIZE) * sizeof(funge_cell));
+		if (FUNGE_UNLIKELY(!stack->entries)) {
+			stack_oom();
+		}
+		stack->size += ALLOCCHUNKSIZE;
+	}
+	stack->entries[stack->top] = value;
+	stack->top++;
+}
+
+FUNGE_ATTR_FAST inline funge_cell stack_pop(funge_stack * restrict stack)
+{
+	assert(stack != NULL);
+
+	if (stack->top == 0)
+		return 0;
+
+	return stack->entries[--stack->top];
+}
+
+FUNGE_ATTR_FAST void stack_discard(funge_stack * restrict stack, size_t n)
+{
+	assert(stack != NULL);
+
+	if (stack->top == 0) {
+		return;
+	} else if (stack->top > n) {
+		stack->top -= n;
+	} else {
+		stack->top = 0;
+	}
+}
+
+
+FUNGE_ATTR_FAST inline funge_cell stack_peek(const funge_stack * restrict stack)
+{
+	assert(stack != NULL);
+
+	if (stack->top == 0) {
+		return 0;
+	} else {
+		return stack->entries[stack->top - 1];
+	}
+}
+
+
+FUNGE_ATTR_FAST inline funge_cell stack_get_index(const funge_stack * restrict stack, size_t index)
+{
+	assert(stack != NULL);
+
+	if (stack->top == 0) {
+		return 0;
+	} else if (stack->top <= index) {
+		return 0;
+	} else {
+		return stack->entries[index - 1];
+	}
+}
+
+FUNGE_ATTR_FAST inline size_t stack_strlen(const funge_stack * restrict stack)
+{
+	// TODO: Maybe scan two cells at once if we are using 32-bit cells on a
+	// 64-bit system?
+	for (size_t i = stack->top; i > 0; i--) {
+		if (stack->entries[i - 1] == 0)
+			return stack->top - i;
+	}
+	return stack->top;
+}
+
+
+/********************************
+ * Push and pop for data types. *
+ ********************************/
+
+FUNGE_ATTR_FAST void stack_push_vector(funge_stack * restrict stack, const funge_vector * restrict value)
+{
+	// TODO: Optimise
+	stack_push(stack, value->x);
+	stack_push(stack, value->y);
+}
+
+FUNGE_ATTR_FAST funge_vector stack_pop_vector(funge_stack * restrict stack)
+{
+	// TODO: Optimise
+	funge_cell x, y;
+	y = stack_pop(stack);
+	x = stack_pop(stack);
+	return (funge_vector) { .x = x, .y = y };
+}
+
+FUNGE_ATTR_FAST void stack_push_string(funge_stack * restrict stack, const unsigned char * restrict str, size_t len)
+{
+	assert(str != NULL);
+	assert(stack != NULL);
+	// Increment it once or it won't work
+	stack_prealloc_space(stack, len + 1);
+	{
+		const size_t top = stack->top + len;
+		for (ssize_t i = (ssize_t)len; i >= 0; i--)
+			stack->entries[top - (size_t)i] = str[i];
+		stack->top += len + 1;
+	}
+}
+
+FUNGE_ATTR_FAST unsigned char *stack_pop_string(funge_stack * restrict stack, size_t * restrict len)
+{
+	funge_cell c;
+	size_t index = 0;
+	// FIXME: This may very likely be more than is needed.
+	unsigned char * buf = (unsigned char*)cf_malloc_noptr((stack->top + 1) * sizeof(char));
+	if (FUNGE_UNLIKELY(!buf)) {
+		*len = 0;
+		return NULL;
+	}
+
+	while ((c = stack_pop(stack)) != '\0') {
+		buf[index++] = (unsigned char)c;
+	}
+	buf[index] = '\0';
+	if (len)
+		*len = index;
+	return buf;
+}
+
+#ifdef UNUSED
+FUNGE_ATTR_FAST unsigned char *stack_pop_sized_string(funge_stack * restrict stack, size_t len)
+{
+	unsigned char * restrict x = (unsigned char*)cf_malloc_noptr((len + 1) * sizeof(char));
+	if (FUNGE_UNLIKELY(!x))
+		return NULL;
+
+	for (size_t i = 0; i < len; i++) {
+		x[i] = (unsigned char)stack_pop(stack);
+	}
+	x[len + 1] = '\0';
+	return x;
+}
+#endif
+
+
+/***************
+ * Other stuff *
+ ***************/
+FUNGE_ATTR_FAST void stack_dup_top(funge_stack * restrict stack)
+{
+	// TODO: Optimise instead of doing it this way
+	funge_cell tmp = stack_peek(stack);
+	stack_push(stack, tmp);
+	// If it was empty, push a second zero.
+	if (stack->top == 1)
+		stack_push(stack, 0);
+}
+
+FUNGE_ATTR_FAST void stack_swap_top(funge_stack * restrict stack)
+{
+	// TODO: Optimise instead of doing it this way
+	funge_cell a, b;
+	// Well this have to work logically...
+	a = stack_pop(stack);
+	b = stack_pop(stack);
+	stack_prealloc_space(stack, 2);
+	stack_push_no_check(stack, a);
+	stack_push_no_check(stack, b);
+}
+
+
+#ifndef NDEBUG
+/*************
+ * Debugging *
+ *************/
+
+
+// For use with call in gdb
+void stack_dump(const funge_stack * stack) FUNGE_ATTR_UNUSED FUNGE_ATTR_COLD;
+
+void stack_dump(const funge_stack * stack)
+{
+	if (!stack)
+		return;
+	fprintf(stderr, "%zu elements:\n", stack->top);
+	for (size_t i = 0; i < stack->top; i++)
+		fprintf(stderr, "%" FUNGECELLPRI " ", stack->entries[i]);
+	fputs("\n", stderr);
+}
+
+#endif
+
+#ifndef DISABLE_TRACE
+// This is for tracing
+FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
+void stack_print_top(const funge_stack * stack)
+{
+	assert(stack != NULL);
+	if (stack->top == 0) {
+		fputs("\tStack is empty.\n", stderr);
+	} else {
+		fprintf(stderr, "\tStack has %zu elements, top 5 (or less) elements:\n\t\t", stack->top);
+		for (ssize_t i = (ssize_t)stack->top; (i > 0) && (i > ((ssize_t)stack->top - 5)); i--)
+			fprintf(stderr, "%" FUNGECELLPRI " ", stack->entries[i-1]);
+		fputs("\n", stderr);
+	}
+}
+#endif
+
+/****************
+ * Stack-stacks *
+ ****************/
+
+FUNGE_ATTR_FAST funge_stackstack * stackstack_create(void)
+{
+	funge_stackstack * stackStack;
+	funge_stack      * stack;
+
+	stackStack = (funge_stackstack*)cf_malloc(sizeof(funge_stackstack) + sizeof(funge_stack*));
+	if (FUNGE_UNLIKELY(!stackStack))
+		return NULL;
+
+	stack = stack_create();
+	if (FUNGE_UNLIKELY(!stack)) {
+		cf_free(stackStack);
+		return NULL;
+	}
+
+	stackStack->size = 1;
+	stackStack->current = 0;
+	stackStack->stacks[0] = stack;
+	return stackStack;
+}
+
+FUNGE_ATTR_FAST void stackstack_free(funge_stackstack * me)
+{
+	if (FUNGE_UNLIKELY(!me))
+		return;
+
+	for (size_t i = 0; i < me->size; i++)
+		stack_free(me->stacks[i]);
+
+	cf_free(me);
+}
+
+#ifdef CONCURRENT_FUNGE
+FUNGE_ATTR_FAST funge_stackstack * stackstack_duplicate(const funge_stackstack * restrict old)
+{
+	funge_stackstack * stackStack;
+
+	assert(old != NULL);
+
+	stackStack = (funge_stackstack*)cf_malloc(sizeof(funge_stackstack) + old->size * sizeof(funge_stack*));
+	if (FUNGE_UNLIKELY(!stackStack))
+		return NULL;
+
+	for (size_t i = 0; i <= old->current; i++) {
+		stackStack->stacks[i] = stack_duplicate(old->stacks[i]);
+		if (FUNGE_UNLIKELY(!stackStack->stacks[i]))
+			return NULL;
+	}
+
+	stackStack->size = old->size;
+	stackStack->current = old->current;
+	return stackStack;
+}
+#endif
+
+
+FUNGE_ATTR_FAST static void oom_stackstack(const instructionPointer * restrict ip)
+{
+	diag_warn_format("Out of memory in stack-stack routine at x=%"
+	                 FUNGECELLPRI " y=%" FUNGECELLPRI ". Reflecting.",
+	                 ip->position.x, ip->position.y);
+	// Lets hope.
+#ifdef CFUN_USE_GC
+	gc_collect_full();
+#endif
+}
+
+
+/**
+ * Like stack_prealloc_space() above, except it returns false instead of exiting
+ * on OOM. Needed for stack stack begin/end.
+ */
+FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
+static inline bool stack_prealloc_space_non_fatal(funge_stack * restrict stack, size_t minfree)
+{
+	if ((stack->top + minfree) >= stack->size) {
+		size_t newsize = stack->size + minfree;
+		funge_cell* newentries;
+		// Round upwards to whole ALLOCCHUNKSIZEed blocks.
+		newsize += ALLOCCHUNKSIZE - (newsize % ALLOCCHUNKSIZE);
+		newentries = (funge_cell*)cf_realloc(stack->entries, newsize * sizeof(funge_cell));
+		if (FUNGE_UNLIKELY(!newentries)) {
+			return false;
+		}
+		stack->entries = newentries;
+		stack->size = newsize;
+	}
+	return true;
+}
+
+FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
+static inline void stack_zero_fill(funge_stack * restrict stack, size_t count)
+{
+	stack_prealloc_space(stack, count);
+	memset(&stack->entries[stack->top], 0, count * sizeof(funge_cell));
+	stack->top += count;
+}
+
+// This does an in-order bulk copy of count elements between two stacks.
+FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
+static inline void stack_bulk_copy(funge_stack * restrict dest, const funge_stack * restrict src, size_t count)
+{
+	stack_prealloc_space(dest, count);
+
+	// Figure out if we were asked to copy more items than actually exists:
+	if (count > src->top) {
+		// Push some initial zeros then.
+		size_t zero_count = count - src->top;
+		count -= zero_count;
+		stack_zero_fill(dest, zero_count);
+	}
+
+	// memcpy the rest.
+	memcpy(&dest->entries[dest->top], &src->entries[src->top - count], count*sizeof(funge_cell));
+	dest->top += count;
+}
+
+FUNGE_ATTR_FAST
+bool stackstack_begin(instructionPointer * restrict ip, funge_cell count, const funge_vector * restrict storageOffset)
+{
+	funge_stackstack *stackStack;
+	funge_stack      *TOSS, *SOSS;
+
+	assert(ip != NULL);
+	assert(storageOffset != NULL);
+
+	// Set up variables
+	stackStack = ip->stackstack;
+
+	TOSS = stack_create();
+	if (FUNGE_UNLIKELY(!TOSS)) {
+		oom_stackstack(ip);
+		return false;
+	}
+	// Allocate enough space on the TOSS and reflect if not.
+	// This is count + 2 (storage offset)
+	if (FUNGE_UNLIKELY(!stack_prealloc_space_non_fatal(TOSS, ABS(count) + 2))) {
+		stack_free(TOSS);
+		oom_stackstack(ip);
+		return false;
+	}
+
+	// Extend by one
+	stackStack = cf_realloc(stackStack, sizeof(funge_stackstack) + (stackStack->size + 1) * sizeof(funge_stack*));
+	if (FUNGE_UNLIKELY(!stackStack)) {
+		stack_free(TOSS);
+		oom_stackstack(ip);
+		return false;
+	}
+	SOSS = stackStack->stacks[stackStack->current];
+
+	stackStack->size++;
+	stackStack->current++;
+	stackStack->stacks[stackStack->current] = TOSS;
+
+	if (count > 0) {
+		stack_bulk_copy(TOSS, SOSS, (size_t)count);
+		// Make it into a move.
+		if ((size_t)count > SOSS->top)
+			SOSS->top = 0;
+		else
+			SOSS->top -= (size_t)count;
+	} else if (count < 0) {
+		stack_zero_fill(SOSS, (size_t)(-count));
+	}
+	stack_push_vector(SOSS, &ip->storageOffset);
+	ip->storageOffset.x = storageOffset->x;
+	ip->storageOffset.y = storageOffset->y;
+	ip->stack = TOSS;
+	ip->stackstack = stackStack;
+	return true;
+}
+
+
+FUNGE_ATTR_FAST bool stackstack_end(instructionPointer * restrict ip, funge_cell count)
+{
+	funge_stack      *TOSS, *SOSS;
+	funge_stackstack *stackStack;
+	funge_vector      storageOffset;
+
+	assert(ip != NULL);
+
+	// Set up variables
+	stackStack = ip->stackstack;
+	TOSS = stackStack->stacks[stackStack->current];
+	SOSS = stackStack->stacks[stackStack->current - 1];
+	storageOffset = stack_pop_vector(SOSS);
+	if (count > 0) {
+		// Since TOSS is discarded there is no need to update it's top pointer.
+		stack_bulk_copy(SOSS, TOSS, (size_t)count);
+	} else if (count < 0) {
+		stack_discard(SOSS, (size_t)(-count));
+	}
+	ip->storageOffset.x = storageOffset.x;
+	ip->storageOffset.y = storageOffset.y;
+
+	ip->stack = SOSS;
+	// FIXME: Maybe we shouldn't realloc here to reduce overhead.
+	// Make it one smaller
+	stackStack = (funge_stackstack*)cf_realloc(stackStack, sizeof(funge_stackstack) + (stackStack->size - 1) * sizeof(funge_stack*));
+	if (FUNGE_UNLIKELY(!stackStack)) {
+		oom_stackstack(ip);
+		return false;
+	}
+	stackStack->size--;
+	stackStack->current--;
+	ip->stackstack = stackStack;
+	stack_free(TOSS);
+	return true;
+}
+
+
+FUNGE_ATTR_FAST void stackstack_transfer(funge_cell count, funge_stack * restrict TOSS, funge_stack * restrict SOSS)
+{
+	assert(TOSS != NULL);
+	assert(SOSS != NULL);
+	assert(TOSS != SOSS);
+
+	if (count > 0) {
+		while (count--) {
+			stack_push(TOSS, stack_pop(SOSS));
+		}
+	} else if (count < 0) {
+		while (count++) {
+			stack_push(SOSS, stack_pop(TOSS));
+		}
+	}
+}