[RELEASE] u22-v04
This version is submitted for U22 final presentation. (squashed 158 commits)
This commit is contained in:
9
util/statman/CMakeLists.txt
Normal file
9
util/statman/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
add_library(statman
|
||||
statman.c
|
||||
)
|
||||
|
||||
if (BUILD_TESTING)
|
||||
add_executable(statman-test test.c)
|
||||
target_link_libraries(statman-test statman)
|
||||
add_test(statman-test statman-test)
|
||||
endif()
|
4
util/statman/README.md
Normal file
4
util/statman/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
statman
|
||||
====
|
||||
|
||||
the simplest STATe MANagement system
|
89
util/statman/statman.c
Normal file
89
util/statman/statman.c
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "./statman.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define REDIRECT_MAX_ 100
|
||||
|
||||
static const statman_meta_t* statman_get_meta_by_state_(
|
||||
const statman_meta_t* meta, statman_state_t state) {
|
||||
assert(meta != NULL);
|
||||
|
||||
for (size_t i = 0; meta[i].name != NULL; ++i) {
|
||||
if (meta[i].state == state) return &meta[i];
|
||||
}
|
||||
fprintf(stderr, "statman: unknown state %"PRIu16"\n", state);
|
||||
abort();
|
||||
}
|
||||
|
||||
static void statman_initialize_state_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* state,
|
||||
size_t depth) {
|
||||
assert(meta != NULL);
|
||||
assert(state != NULL);
|
||||
assert(depth < REDIRECT_MAX_);
|
||||
|
||||
const statman_meta_t* m = statman_get_meta_by_state_(meta, *state);
|
||||
if (m->initialize == NULL) return;
|
||||
|
||||
const statman_state_t backup = *state;
|
||||
m->initialize(m, instance, state);
|
||||
|
||||
if (backup != *state) {
|
||||
statman_initialize_state_(meta, instance, state, depth+1);
|
||||
}
|
||||
}
|
||||
|
||||
static void statman_update_state_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* state,
|
||||
size_t depth) {
|
||||
assert(meta != NULL);
|
||||
assert(state != NULL);
|
||||
assert(depth < REDIRECT_MAX_);
|
||||
|
||||
const statman_meta_t* m = statman_get_meta_by_state_(meta, *state);
|
||||
if (m->update == NULL) return;
|
||||
|
||||
const statman_state_t backup = *state;
|
||||
m->update(m, instance, state);
|
||||
|
||||
if (backup != *state) {
|
||||
statman_initialize_state_(meta, instance, state, depth+1);
|
||||
statman_update_state_(meta, instance, state, depth+1);
|
||||
}
|
||||
}
|
||||
|
||||
void statman_update(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
statman_update_state_(meta, instance, state, 0);
|
||||
}
|
||||
|
||||
void statman_transition_to(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* state,
|
||||
statman_state_t next) {
|
||||
assert(meta != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
if (*state == next) return;
|
||||
|
||||
const statman_meta_t* m = statman_get_meta_by_state_(meta, *state);
|
||||
if (m->finalize != NULL) {
|
||||
m->finalize(m, instance, &next);
|
||||
}
|
||||
if (*state == next) return;
|
||||
|
||||
*state = next;
|
||||
statman_initialize_state_(meta, instance, state, 0);
|
||||
}
|
47
util/statman/statman.h
Normal file
47
util/statman/statman.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint16_t statman_state_t;
|
||||
|
||||
typedef struct statman_meta_t statman_meta_t;
|
||||
struct statman_meta_t {
|
||||
statman_state_t state;
|
||||
const char* name;
|
||||
const void* data;
|
||||
|
||||
void
|
||||
(*initialize)(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
void
|
||||
(*update)(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
void
|
||||
(*finalize)(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
};
|
||||
|
||||
void
|
||||
statman_update(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* state
|
||||
);
|
||||
|
||||
/* don't call in callback functions */
|
||||
void
|
||||
statman_transition_to(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* state,
|
||||
statman_state_t next
|
||||
);
|
160
util/statman/test.c
Normal file
160
util/statman/test.c
Normal file
@@ -0,0 +1,160 @@
|
||||
#undef NDEBUG
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "./statman.h"
|
||||
|
||||
typedef enum {
|
||||
CHECK_POINT_NONE,
|
||||
|
||||
CHECK_POINT_STATE0_INITIALIZE,
|
||||
CHECK_POINT_STATE1_INITIALIZE,
|
||||
CHECK_POINT_STATE2_INITIALIZE,
|
||||
CHECK_POINT_STATE3_INITIALIZE,
|
||||
|
||||
CHECK_POINT_STATE0_UPDATE,
|
||||
CHECK_POINT_STATE1_UPDATE,
|
||||
CHECK_POINT_STATE2_UPDATE,
|
||||
|
||||
CHECK_POINT_STATE0_FINALIZE,
|
||||
CHECK_POINT_STATE1_FINALIZE,
|
||||
CHECK_POINT_STATE2_FINALIZE,
|
||||
CHECK_POINT_STATE3_FINALIZE,
|
||||
} check_point_t;
|
||||
|
||||
static check_point_t actual_[256] = {0};
|
||||
static size_t actual_length_ = 0;
|
||||
|
||||
static inline void check_point_(check_point_t cp) {
|
||||
assert(actual_length_ < sizeof(actual_)/sizeof(actual_[0]));
|
||||
actual_[actual_length_++] = cp;
|
||||
}
|
||||
|
||||
static void state_initialize_(
|
||||
const statman_meta_t* meta, void* time, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(time != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
check_point_(CHECK_POINT_STATE0_INITIALIZE+meta->state);
|
||||
}
|
||||
|
||||
static void state3_initialize_(
|
||||
const statman_meta_t* meta, void* time, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(time != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
check_point_(CHECK_POINT_STATE3_INITIALIZE);
|
||||
*next = 0;
|
||||
}
|
||||
|
||||
static void state_update_(
|
||||
const statman_meta_t* meta, void* time, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(time != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
check_point_(CHECK_POINT_STATE0_UPDATE+meta->state);
|
||||
|
||||
size_t *t = (typeof(t)) time;
|
||||
if ((*t)++%2 == 0) *next = meta->state+1;
|
||||
}
|
||||
|
||||
static void state_finalize_(
|
||||
const statman_meta_t* meta, void* time, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(time != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
check_point_(CHECK_POINT_STATE0_FINALIZE+meta->state);
|
||||
*next = 0;
|
||||
}
|
||||
|
||||
static const statman_meta_t table_[] = {
|
||||
{
|
||||
.state = 0,
|
||||
.name = "0",
|
||||
.initialize = state_initialize_,
|
||||
.update = state_update_,
|
||||
.finalize = state_finalize_,
|
||||
},
|
||||
{
|
||||
.state = 1,
|
||||
.name = "1",
|
||||
.initialize = state_initialize_,
|
||||
.update = state_update_,
|
||||
.finalize = state_finalize_,
|
||||
},
|
||||
{
|
||||
.state = 2,
|
||||
.name = "2",
|
||||
.initialize = state_initialize_,
|
||||
.update = state_update_,
|
||||
.finalize = state_finalize_,
|
||||
},
|
||||
{
|
||||
.state = 3,
|
||||
.name = "3",
|
||||
.initialize = state3_initialize_,
|
||||
},
|
||||
};
|
||||
|
||||
int main(void) {
|
||||
statman_state_t state = 0;
|
||||
size_t time = 0;
|
||||
|
||||
/* state0 -> state1 */
|
||||
statman_update(table_, &time, &state);
|
||||
assert(state == 1);
|
||||
assert(time == 2);
|
||||
|
||||
/* state1 -> state2 */
|
||||
statman_update(table_, &time, &state);
|
||||
assert(state == 2);
|
||||
assert(time == 4);
|
||||
|
||||
/* state2 -> state0 */
|
||||
statman_update(table_, &time, &state);
|
||||
assert(state == 0);
|
||||
assert(time == 6);
|
||||
|
||||
/* state0 -> state1 */
|
||||
statman_update(table_, &time, &state);
|
||||
assert(state == 1);
|
||||
assert(time == 8);
|
||||
|
||||
/* stat1's finalizer forces to move to 0 */
|
||||
statman_transition_to(table_, &time, &state, 2);
|
||||
assert(state == 0);
|
||||
assert(time == 8);
|
||||
|
||||
static const check_point_t expects[] = {
|
||||
CHECK_POINT_STATE0_UPDATE,
|
||||
CHECK_POINT_STATE1_INITIALIZE,
|
||||
CHECK_POINT_STATE1_UPDATE,
|
||||
|
||||
CHECK_POINT_STATE1_UPDATE,
|
||||
CHECK_POINT_STATE2_INITIALIZE,
|
||||
CHECK_POINT_STATE2_UPDATE,
|
||||
|
||||
CHECK_POINT_STATE2_UPDATE,
|
||||
CHECK_POINT_STATE3_INITIALIZE,
|
||||
CHECK_POINT_STATE0_INITIALIZE,
|
||||
CHECK_POINT_STATE0_UPDATE,
|
||||
|
||||
CHECK_POINT_STATE0_UPDATE,
|
||||
CHECK_POINT_STATE1_INITIALIZE,
|
||||
CHECK_POINT_STATE1_UPDATE,
|
||||
|
||||
CHECK_POINT_STATE1_FINALIZE,
|
||||
CHECK_POINT_STATE0_INITIALIZE,
|
||||
};
|
||||
|
||||
assert(actual_length_ == sizeof(expects)/sizeof(expects[0]));
|
||||
for (size_t i = 0; i < actual_length_; ++i) {
|
||||
assert(expects[i] == actual_[i]);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user