#include "eval.h"
#include <string.h>
#include "bounded.h"
#include "limitations.h"
#include "simplicity_alloc.h"
#include "simplicity_assert.h"
static void forward(frameItem* frame, size_t n) {
frame->offset += n;
}
static void backward(frameItem* frame, size_t n) {
rustsimplicity_0_6_debug_assert(n <= frame->offset);
frame->offset -= n;
}
static void skip(frameItem* frame, size_t n) {
rustsimplicity_0_6_debug_assert(n <= frame->offset);
frame->offset -= n;
}
static void writeValue(frameItem* dst, const bitstring* compactValue, size_t typeIx, type* type_dag) {
size_t cur = typeSkip(typeIx, type_dag);
size_t offset = 0;
bool calling = true;
setTypeBack(cur, type_dag, 0);
while (cur) {
if (SUM == type_dag[cur].kind) {
rustsimplicity_0_6_debug_assert(calling);
bool bit = getBit(compactValue, offset);
offset++;
writeBit(dst, bit);
skip(dst, pad(bit, type_dag[type_dag[cur].typeArg[0]].bitSize, type_dag[type_dag[cur].typeArg[1]].bitSize));
size_t next = typeSkip(type_dag[cur].typeArg[bit], type_dag);
if (next) {
setTypeBack(next, type_dag, type_dag[cur].back);
cur = next;
} else {
cur = type_dag[cur].back;
calling = false;
}
} else {
rustsimplicity_0_6_debug_assert(PRODUCT == type_dag[cur].kind);
size_t next;
if (calling) {
next = typeSkip(type_dag[cur].typeArg[0], type_dag);
if (next) {
setTypeBack(next, type_dag, cur);
cur = next;
continue;
}
}
next = typeSkip(type_dag[cur].typeArg[1], type_dag);
if (next) {
setTypeBack(next, type_dag, type_dag[cur].back);
cur = next;
calling = true;
} else {
cur = type_dag[cur].back;
calling = false;
}
}
}
}
typedef struct evalState {
frameItem* activeReadFrame;
frameItem* activeWriteFrame;
} evalState;
typedef struct call {
size_t return_to;
flags_type flags;
} call;
#define FLAG_TCO 0x01
#define FLAG_LAST_CASE 0x02
#define FLAG_EXEC 0x10
#define FLAG_CASE_LEFT 0x20
#define FLAG_CASE_RIGHT 0x40
static inline bool get_tco_flag(const call *stack) {
return FLAG_TCO == (stack->flags & FLAG_TCO);
}
static inline void set_tco_flag(call *stack, bool flag) {
if (flag) {
stack->flags |= FLAG_TCO;
} else {
stack->flags &= (flags_type)(~FLAG_TCO);
}
}
static inline bool get_case_last_flag(const call *stack) {
return FLAG_LAST_CASE == (stack->flags & FLAG_LAST_CASE);
}
static inline void set_case_last_flag(call *stack, bool flag) {
if (flag) {
stack->flags |= FLAG_LAST_CASE;
} else {
stack->flags &= (flags_type)(~FLAG_LAST_CASE);
}
}
static simplicity_err runTCO(evalState state, call* stack, const dag_node* dag, type* type_dag, size_t len, const txEnv* env) {
size_t pc = len - 1;
stack[pc].return_to = len;
set_tco_flag(&stack[pc], false);
bool calling = true;
while(pc < len) {
stack[pc].flags |= FLAG_EXEC;
tag_t tag = dag[pc].tag;
rustsimplicity_0_6_debug_assert(state.activeReadFrame < state.activeWriteFrame);
rustsimplicity_0_6_debug_assert(state.activeReadFrame->edge <= state.activeWriteFrame->edge);
if (dag[pc].jet) {
if(!dag[pc].jet(state.activeWriteFrame, *state.activeReadFrame, env)) return SIMPLICITY_ERR_EXEC_JET;
tag = UNIT;
}
switch (tag) {
case COMP:
if (calling) {
*(state.activeWriteFrame - 1) = initWriteFrame(type_dag[COMP_B(dag, type_dag, pc)].bitSize, state.activeWriteFrame->edge);
state.activeWriteFrame--;
stack[dag[pc].child[0]].return_to = pc;
set_tco_flag(&stack[dag[pc].child[0]], get_tco_flag(&stack[pc]));
pc = dag[pc].child[0];
} else {
rustsimplicity_0_6_debug_assert(0 == state.activeWriteFrame->offset);
memmove( state.activeReadFrame->edge, state.activeWriteFrame->edge
, (size_t)((state.activeWriteFrame + 1)->edge - state.activeWriteFrame->edge) * sizeof(UWORD)
);
*(state.activeReadFrame + 1) = initReadFrame(type_dag[COMP_B(dag, type_dag, pc)].bitSize, state.activeReadFrame->edge);
state.activeWriteFrame++; state.activeReadFrame++;
calling = true;
stack[dag[pc].child[1]].return_to = stack[pc].return_to;
set_tco_flag(&stack[dag[pc].child[1]], true);
pc = dag[pc].child[1];
}
break;
case ASSERTL:
case ASSERTR:
case CASE:
if (calling) {
bool bit = peekBit(state.activeReadFrame);
if (bit) {
stack[pc].flags |= FLAG_CASE_RIGHT;
} else {
stack[pc].flags |= FLAG_CASE_LEFT;
}
forward(state.activeReadFrame, 1 + pad( bit
, type_dag[CASE_A(dag, type_dag, pc)].bitSize
, type_dag[CASE_B(dag, type_dag, pc)].bitSize));
stack[dag[pc].child[bit]].return_to = get_tco_flag(&stack[pc]) ? stack[pc].return_to : pc;
set_tco_flag(&stack[dag[pc].child[bit]], get_tco_flag(&stack[pc]));
set_case_last_flag(&stack[pc], bit);
pc = dag[pc].child[bit];
} else {
backward(state.activeReadFrame, 1 + pad( get_case_last_flag(&stack[pc])
, type_dag[CASE_A(dag, type_dag, pc)].bitSize
, type_dag[CASE_B(dag, type_dag, pc)].bitSize));
pc = stack[pc].return_to;
}
break;
case PAIR:
if (calling) {
stack[dag[pc].child[0]].return_to = pc;
set_tco_flag(&stack[dag[pc].child[0]], false);
pc = dag[pc].child[0];
} else {
calling = true;
stack[dag[pc].child[1]].return_to = stack[pc].return_to;
set_tco_flag(&stack[dag[pc].child[1]], get_tco_flag(&stack[pc]));
pc = dag[pc].child[1];
}
break;
case DISCONNECT:
if (calling) {
*(state.activeWriteFrame - 1) = initWriteFrame(type_dag[DISCONNECT_W256A(dag, type_dag, pc)].bitSize,
state.activeWriteFrame->edge);
state.activeWriteFrame--;
write32s(state.activeWriteFrame, dag[dag[pc].child[1]].cmr.s, 8);
rustsimplicity_0_6_copyBits(state.activeWriteFrame, state.activeReadFrame, type_dag[DISCONNECT_A(dag, type_dag, pc)].bitSize);
if (get_tco_flag(&stack[pc])) {
state.activeReadFrame--;
}
rustsimplicity_0_6_debug_assert(0 == state.activeWriteFrame->offset);
memmove( state.activeReadFrame->edge, state.activeWriteFrame->edge
, (size_t)((state.activeWriteFrame + 1)->edge - state.activeWriteFrame->edge) * sizeof(UWORD)
);
*(state.activeReadFrame + 1) = initReadFrame(type_dag[DISCONNECT_W256A(dag, type_dag, pc)].bitSize,
state.activeReadFrame->edge);
state.activeWriteFrame++; state.activeReadFrame++;
*(state.activeWriteFrame - 1) = initWriteFrame(type_dag[DISCONNECT_BC(dag, type_dag, pc)].bitSize,
state.activeWriteFrame->edge);
state.activeWriteFrame--;
stack[dag[pc].child[0]].return_to = pc;
set_tco_flag(&stack[dag[pc].child[0]], true);
pc = dag[pc].child[0];
} else {
rustsimplicity_0_6_debug_assert(0 == state.activeWriteFrame->offset);
memmove( state.activeReadFrame->edge, state.activeWriteFrame->edge
, (size_t)((state.activeWriteFrame + 1)->edge - state.activeWriteFrame->edge) * sizeof(UWORD)
);
*(state.activeReadFrame + 1) = initReadFrame(type_dag[DISCONNECT_BC(dag, type_dag, pc)].bitSize,
state.activeReadFrame->edge);
state.activeWriteFrame++; state.activeReadFrame++;
rustsimplicity_0_6_copyBits(state.activeWriteFrame, state.activeReadFrame, type_dag[DISCONNECT_B(dag, type_dag, pc)].bitSize);
forward(state.activeReadFrame, type_dag[DISCONNECT_B(dag, type_dag, pc)].bitSize);
calling = true;
stack[dag[pc].child[1]].return_to = stack[pc].return_to;
set_tco_flag(&stack[dag[pc].child[1]], true);
pc = dag[pc].child[1];
}
break;
case INJL:
case INJR:
writeBit(state.activeWriteFrame, INJR == dag[pc].tag);
skip(state.activeWriteFrame, pad( INJR == dag[pc].tag
, type_dag[INJ_B(dag, type_dag, pc)].bitSize
, type_dag[INJ_C(dag, type_dag, pc)].bitSize));
case TAKE:
rustsimplicity_0_6_debug_assert(calling);
stack[dag[pc].child[0]].return_to = stack[pc].return_to;
set_tco_flag(&stack[dag[pc].child[0]], get_tco_flag(&stack[pc]));
pc = dag[pc].child[0];
break;
case DROP:
if (calling) {
forward(state.activeReadFrame, type_dag[PROJ_A(dag, type_dag, pc)].bitSize);
stack[dag[pc].child[0]].return_to = get_tco_flag(&stack[pc]) ? stack[pc].return_to : pc;
set_tco_flag(&stack[dag[pc].child[0]], get_tco_flag(&stack[pc]));
pc = dag[pc].child[0];
} else {
backward(state.activeReadFrame, type_dag[PROJ_A(dag, type_dag, pc)].bitSize);
pc = stack[pc].return_to;
}
break;
case IDEN:
case WORD:
case WITNESS:
if (IDEN == tag) {
rustsimplicity_0_6_copyBits(state.activeWriteFrame, state.activeReadFrame, type_dag[IDEN_A(dag, type_dag, pc)].bitSize);
} else {
writeValue(state.activeWriteFrame, &dag[pc].compactValue, dag[pc].targetType, type_dag);
}
case UNIT:
rustsimplicity_0_6_debug_assert(calling);
if (get_tco_flag(&stack[pc])) {
state.activeReadFrame--;
}
calling = false;
pc = stack[pc].return_to;
break;
case HIDDEN: return SIMPLICITY_ERR_EXEC_ASSERT;
case JET:
SIMPLICITY_UNREACHABLE;
}
}
rustsimplicity_0_6_assert(pc == len);
return SIMPLICITY_NO_ERROR;
}
static simplicity_err antiDos(flags_type checks, const call* stack, const dag_node* dag, size_t len) {
static_assert(CHECK_EXEC == FLAG_EXEC, "CHECK_EXEC does not match FLAG_EXEC");
static_assert(CHECK_CASE == (FLAG_CASE_LEFT | FLAG_CASE_RIGHT), "CHECK_CASE does not match FLAG_CASE");
rustsimplicity_0_6_assert(CHECK_CASE == (checks & CHECK_CASE) || 0 == (checks & CHECK_CASE));
if (!checks) return SIMPLICITY_NO_ERROR;
for(size_t i = 0; i < len; ++i) {
flags_type test_flags = (HIDDEN != dag[i].tag ? CHECK_EXEC : 0)
| (CASE == dag[i].tag ? CHECK_CASE : 0);
test_flags &= checks;
if (test_flags != (test_flags & stack[i].flags)) {
return SIMPLICITY_ERR_ANTIDOS;
}
}
return SIMPLICITY_NO_ERROR;
}
typedef struct boundsAnalysis {
ubounded extraCellsBound[2];
ubounded extraUWORDBound[2];
ubounded extraFrameBound[2];
ubounded cost;
} boundsAnalysis;
simplicity_err rustsimplicity_0_6_analyseBounds( ubounded *cellsBound, ubounded *UWORDBound, ubounded *frameBound, ubounded *costBound
, ubounded maxCells, ubounded minCost, ubounded maxCost, const dag_node* dag, const type* type_dag, const size_t len) {
static_assert(DAG_LEN_MAX <= SIZE_MAX / sizeof(boundsAnalysis), "bound array too large.");
static_assert(1 <= DAG_LEN_MAX, "DAG_LEN_MAX is zero.");
static_assert(DAG_LEN_MAX - 1 <= UINT32_MAX, "bound array index does not fit in uint32_t.");
rustsimplicity_0_6_assert(1 <= len);
rustsimplicity_0_6_assert(len <= DAG_LEN_MAX);
boundsAnalysis* bound = rustsimplicity_0_6_malloc(len * sizeof(boundsAnalysis));
if (!bound) return SIMPLICITY_ERR_MALLOC;
for (size_t i = 0; i < len; ++i) {
switch (dag[i].tag) {
case ASSERTL:
case ASSERTR:
case CASE:
bound[i].extraCellsBound[0] = bounded_max( bound[dag[i].child[0]].extraCellsBound[0]
, bound[dag[i].child[1]].extraCellsBound[0] );
bound[i].extraCellsBound[1] = bounded_max( bound[dag[i].child[0]].extraCellsBound[1]
, bound[dag[i].child[1]].extraCellsBound[1] );
bound[i].extraUWORDBound[0] = bounded_max( bound[dag[i].child[0]].extraUWORDBound[0]
, bound[dag[i].child[1]].extraUWORDBound[0] );
bound[i].extraUWORDBound[1] = bounded_max( bound[dag[i].child[0]].extraUWORDBound[1]
, bound[dag[i].child[1]].extraUWORDBound[1] );
bound[i].extraFrameBound[0] = bounded_max( bound[dag[i].child[0]].extraFrameBound[0]
, bound[dag[i].child[1]].extraFrameBound[0] );
bound[i].extraFrameBound[1] = bounded_max( bound[dag[i].child[0]].extraFrameBound[1]
, bound[dag[i].child[1]].extraFrameBound[1] );
bound[i].cost = bounded_add(overhead, bounded_max( bound[dag[i].child[0]].cost
, bound[dag[i].child[1]].cost ));
break;
case DISCONNECT:
bound[i].extraCellsBound[1] = type_dag[DISCONNECT_W256A(dag, type_dag, i)].bitSize;
bound[i].extraCellsBound[0] = bounded_max(
bounded_add( type_dag[DISCONNECT_BC(dag, type_dag, i)].bitSize
, bounded_max( bounded_add(bound[i].extraCellsBound[1], bound[dag[i].child[0]].extraCellsBound[1])
, bounded_max(bound[dag[i].child[0]].extraCellsBound[0], bound[dag[i].child[1]].extraCellsBound[1]))),
bound[dag[i].child[1]].extraCellsBound[0]);
bound[i].extraUWORDBound[1] = (ubounded)ROUND_UWORD(type_dag[DISCONNECT_W256A(dag, type_dag, i)].bitSize);
bound[i].extraUWORDBound[0] = bounded_max(
(ubounded)ROUND_UWORD(type_dag[DISCONNECT_BC(dag, type_dag, i)].bitSize) +
bounded_max( bound[i].extraUWORDBound[1] + bound[dag[i].child[0]].extraUWORDBound[1]
, bounded_max(bound[dag[i].child[0]].extraUWORDBound[0], bound[dag[i].child[1]].extraUWORDBound[1])),
bound[dag[i].child[1]].extraUWORDBound[0]);
bound[i].extraFrameBound[1] = bounded_max( bound[dag[i].child[0]].extraFrameBound[1] + 1
, bound[dag[i].child[1]].extraFrameBound[1]);
bound[i].extraFrameBound[0] = bound[i].extraFrameBound[1] + 1;
bound[i].cost = bounded_add(overhead
, bounded_add(type_dag[DISCONNECT_W256A(dag, type_dag, i)].bitSize
, bounded_add(type_dag[DISCONNECT_W256A(dag, type_dag, i)].bitSize
, bounded_add(type_dag[DISCONNECT_BC(dag, type_dag, i)].bitSize
, bounded_add(type_dag[DISCONNECT_B(dag, type_dag, i)].bitSize
, bounded_add(bound[dag[i].child[0]].cost, bound[dag[i].child[1]].cost))))));
break;
case COMP:
bound[i].extraCellsBound[0] = bounded_max( bounded_add( type_dag[COMP_B(dag, type_dag, i)].bitSize
, bounded_max( bound[dag[i].child[0]].extraCellsBound[0]
, bound[dag[i].child[1]].extraCellsBound[1] ))
, bound[dag[i].child[1]].extraCellsBound[0] );
bound[i].extraCellsBound[1] = bounded_add( type_dag[COMP_B(dag, type_dag, i)].bitSize
, bound[dag[i].child[0]].extraCellsBound[1] );
bound[i].extraUWORDBound[0] = bounded_max( (ubounded)ROUND_UWORD(type_dag[COMP_B(dag, type_dag, i)].bitSize) +
bounded_max( bound[dag[i].child[0]].extraUWORDBound[0]
, bound[dag[i].child[1]].extraUWORDBound[1] )
, bound[dag[i].child[1]].extraUWORDBound[0] );
bound[i].extraUWORDBound[1] = (ubounded)ROUND_UWORD(type_dag[COMP_B(dag, type_dag, i)].bitSize)
+ bound[dag[i].child[0]].extraUWORDBound[1];
bound[i].extraFrameBound[0] = bounded_max( bound[dag[i].child[0]].extraFrameBound[0]
, bound[dag[i].child[1]].extraFrameBound[1] )
+ 1;
bound[i].extraFrameBound[1] = bounded_max( bound[dag[i].child[0]].extraFrameBound[1] + 1
, bound[dag[i].child[1]].extraFrameBound[1] );
bound[i].cost = bounded_add(overhead
, bounded_add(type_dag[COMP_B(dag, type_dag, i)].bitSize
, bounded_add(bound[dag[i].child[0]].cost, bound[dag[i].child[1]].cost)));
break;
case PAIR:
bound[i].extraCellsBound[0] = bound[dag[i].child[1]].extraCellsBound[0];
bound[i].extraCellsBound[1] = bounded_max( bound[dag[i].child[0]].extraCellsBound[0]
, bounded_max( bound[dag[i].child[0]].extraCellsBound[1]
, bound[dag[i].child[1]].extraCellsBound[1] ));
bound[i].extraUWORDBound[0] = bound[dag[i].child[1]].extraUWORDBound[0];
bound[i].extraUWORDBound[1] = bounded_max( bound[dag[i].child[0]].extraUWORDBound[0]
, bounded_max( bound[dag[i].child[0]].extraUWORDBound[1]
, bound[dag[i].child[1]].extraUWORDBound[1] ));
bound[i].extraFrameBound[0] = bounded_max( bound[dag[i].child[0]].extraFrameBound[0]
, bound[dag[i].child[1]].extraFrameBound[0] );
bound[i].extraFrameBound[1] = bounded_max( bound[dag[i].child[0]].extraFrameBound[0]
, bound[dag[i].child[1]].extraFrameBound[1] );
bound[i].cost = bounded_add(overhead, bounded_add( bound[dag[i].child[0]].cost
, bound[dag[i].child[1]].cost ));
break;
case INJL:
case INJR:
case TAKE:
case DROP:
bound[i].extraCellsBound[0] = bound[dag[i].child[0]].extraCellsBound[0];
bound[i].extraCellsBound[1] = bound[dag[i].child[0]].extraCellsBound[1];
bound[i].extraUWORDBound[0] = bound[dag[i].child[0]].extraUWORDBound[0];
bound[i].extraUWORDBound[1] = bound[dag[i].child[0]].extraUWORDBound[1];
bound[i].extraFrameBound[0] = bound[dag[i].child[0]].extraFrameBound[0];
bound[i].extraFrameBound[1] = bound[dag[i].child[0]].extraFrameBound[1];
bound[i].cost = bounded_add(overhead, bound[dag[i].child[0]].cost);
break;
case IDEN:
case UNIT:
case HIDDEN:
case WITNESS:
case JET:
case WORD:
bound[i].extraCellsBound[0] = bound[i].extraCellsBound[1] = 0;
bound[i].extraUWORDBound[0] = bound[i].extraUWORDBound[1] = 0;
bound[i].extraFrameBound[0] = bound[i].extraFrameBound[1] = 0;
bound[i].cost = IDEN == dag[i].tag ? bounded_add(overhead, type_dag[IDEN_A(dag, type_dag, i)].bitSize)
: WITNESS == dag[i].tag || WORD == dag[i].tag ? bounded_add(overhead, type_dag[dag[i].targetType].bitSize)
: JET == dag[i].tag ? bounded_add(overhead, dag[i].cost)
: HIDDEN == dag[i].tag ? 0
: overhead;
}
}
{
const ubounded inputSize = type_dag[dag[len-1].sourceType].bitSize;
const ubounded outputSize = type_dag[dag[len-1].targetType].bitSize;
*cellsBound = bounded_add( bounded_add(inputSize, outputSize)
, bounded_max(bound[len-1].extraCellsBound[0], bound[len-1].extraCellsBound[1])
);
*UWORDBound = (ubounded)ROUND_UWORD(inputSize) + (ubounded)ROUND_UWORD(outputSize)
+ bounded_max(bound[len-1].extraUWORDBound[0], bound[len-1].extraUWORDBound[1]);
*frameBound = bound[len-1].extraFrameBound[0] + 2;
*costBound = bound[len-1].cost;
}
rustsimplicity_0_6_free(bound);
return (maxCells < *cellsBound) ? SIMPLICITY_ERR_EXEC_MEMORY
: (maxCost < *costBound) ? SIMPLICITY_ERR_EXEC_BUDGET
: (*costBound <= minCost) ? SIMPLICITY_ERR_OVERWEIGHT
: SIMPLICITY_NO_ERROR;
}
simplicity_err rustsimplicity_0_6_evalTCOExpression( flags_type anti_dos_checks, UWORD* output, const UWORD* input
, const dag_node* dag, type* type_dag, size_t len, ubounded minCost, const ubounded* budget, const txEnv* env
) {
rustsimplicity_0_6_assert(1 <= len);
rustsimplicity_0_6_assert(len <= DAG_LEN_MAX);
if (budget) {
rustsimplicity_0_6_assert(*budget <= BUDGET_MAX);
rustsimplicity_0_6_assert(minCost <= *budget);
}
rustsimplicity_0_6_assert(minCost <= BUDGET_MAX);
static_assert(1 <= UBOUNDED_MAX, "UBOUNDED_MAX is zero.");
static_assert(BUDGET_MAX <= (UBOUNDED_MAX - 1) / 1000, "BUDGET_MAX is too large.");
static_assert(CELLS_MAX < UBOUNDED_MAX, "CELLS_MAX is too large.");
ubounded cellsBound, UWORDBound, frameBound, costBound;
simplicity_err result = rustsimplicity_0_6_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, CELLS_MAX, minCost*1000, budget ? *budget*1000 : UBOUNDED_MAX, dag, type_dag, len);
if (!IS_OK(result)) return result;
static_assert(DAG_LEN_MAX <= UBOUNDED_MAX / 2, "2*DAG_LEN_MAX does not fit in size_t.");
rustsimplicity_0_6_assert(frameBound <= 2*len);
static_assert(1 <= UWORD_BIT, "UWORD_BIT is zero.");
static_assert(2*DAG_LEN_MAX <= (SIZE_MAX - CELLS_MAX) / (UWORD_BIT - 1), "cellsBound + frameBound*(UWORD_BIT - 1) doesn't fit in size_t.");
rustsimplicity_0_6_assert(UWORDBound <= (cellsBound + frameBound*(UWORD_BIT - 1)) / UWORD_BIT);
rustsimplicity_0_6_assert(UWORDBound <= cellsBound);
static_assert(CELLS_MAX - 1 <= UINT32_MAX, "cells array index does not fit in uint32_t.");
UWORD* cells = rustsimplicity_0_6_calloc(UWORDBound ? UWORDBound : 1, sizeof(UWORD));
static_assert(2*DAG_LEN_MAX <= SIZE_MAX / sizeof(frameItem), "frames array does not fit in size_t.");
static_assert(1 <= DAG_LEN_MAX, "DAG_LEN_MAX is zero.");
static_assert(2*DAG_LEN_MAX - 1 <= UINT32_MAX, "frames array index does not fit in uint32_t.");
frameItem* frames = rustsimplicity_0_6_malloc(frameBound * sizeof(frameItem));
call* stack = rustsimplicity_0_6_calloc(len, sizeof(call));
result = cells && frames && stack ? SIMPLICITY_NO_ERROR : SIMPLICITY_ERR_MALLOC;
if (IS_OK(result)) {
const ubounded inputSize = type_dag[dag[len-1].sourceType].bitSize;
const ubounded outputSize = type_dag[dag[len-1].targetType].bitSize;
rustsimplicity_0_6_assert(NULL != input || 0 == inputSize);
if (inputSize) memcpy(cells, input, ROUND_UWORD(inputSize) * sizeof(UWORD));
evalState state =
{ .activeReadFrame = frames
, .activeWriteFrame = frames + (frameBound - 1)
};
*(state.activeReadFrame) = initReadFrame(inputSize, cells);
*(state.activeWriteFrame) = initWriteFrame(outputSize, cells + UWORDBound);
result = runTCO(state, stack, dag, type_dag, len, env);
if (IS_OK(result)) {
rustsimplicity_0_6_assert(NULL != output || 0 == outputSize);
if (outputSize) memcpy(output, state.activeWriteFrame->edge, ROUND_UWORD(outputSize) * sizeof(UWORD));
result = antiDos(anti_dos_checks, stack, dag, len);
}
}
rustsimplicity_0_6_free(stack);
rustsimplicity_0_6_free(frames);
rustsimplicity_0_6_free(cells);
return result;
}