summaryrefslogtreecommitdiffstats
path: root/source/state.h
blob: 2f705279fc134b9c861b2189641e4e8b45dc915c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#ifndef EXITEST_STATE_H_INCLUDED
#define EXITEST_STATE_H_INCLUDED

#include <stdint.h>
#include <stddef.h>

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 */