#ifndef EXITEST_STATE_H_INCLUDED #define EXITEST_STATE_H_INCLUDED #include #include struct et_state; typedef union { uintptr_t i; void *p; } et_state_init_data; extern const et_state_init_data et_init_data_null; struct et_next_state { const struct et_state *state; et_state_init_data init_data; }; /* Sets up the state. The second argument (void *) should point to at least state->private_size bytes * of data. The state assumes responsibility for clearing/initializing this buffer before use. * * Returns < 0 for error, >= 0 for success. * This function conceptually takes ownership of init_data. The implications of this depend * on what init_data actually contains -- sometimes it's globally shared data. * * State authors: Free any data filled into init_data regardless of error states. * The caller will not touch init_data again. */ typedef int (et_state_init_f)(const struct et_state *, void *, et_state_init_data); /* Ticks the state. * * Returns < 0 for error (still call cleanup or exit(...)), 0 for "continue", >0 for "advance state". * If a state requests to advance to the next state, the info will be in *next_state (which should be non-NULL). * Note that next_state->init_data may have malloc()'d data which must be handed off by passing to the init function * of the appropriate state. */ typedef int (et_state_tick_f)(const struct et_state *, void *, struct et_next_state *); typedef void (et_state_cleanup_f)(const struct et_state *, void *); struct et_state { et_state_init_f *init_f; et_state_tick_f *tick_f; et_state_cleanup_f *cleanup_f; size_t private_size; }; /* convenience macros because these existed as functions at one point * (note: state expression is evaluated twice. make sure it doesn't have side effects) */ #define et_state_init(_st, _data, _init_data) ((_st)->init_f(_st, _data, _init_data)) #define et_state_tick(_st, _data, _next) ((_st)->tick_f(_st, _data, _next)) #define et_state_cleanup(_st, _data) ((_st)->cleanup_f(_st, _data)) extern const struct et_state *et_state_setup; #endif /* include guard */