Mercurial > repo
view interps/cfunge/cfunge-src/src/stack.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 "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)); } } }