Skip to content

Instantly share code, notes, and snippets.

@courtc
Last active June 30, 2016 22:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save courtc/f4c2c35b59eff514733c to your computer and use it in GitHub Desktop.
Save courtc/f4c2c35b59eff514733c to your computer and use it in GitHub Desktop.
HSM, simplified
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hsm.h"
#define container_of(x, y, z) (y *)((char *)(x) - ((unsigned long)&((y *)0)->z))
enum {
STATE_FILL,
STATE_WRITE,
STATE_COMPLAIN,
};
struct sm {
struct hsm hsm;
char *str;
};
struct sm_data {
struct hsm_data data;
const char *str;
};
static int sm_enter(struct hsm *hsm)
{
struct sm *p = container_of(hsm, struct sm, hsm);
p->str = malloc(256);
return -(p->str == NULL);
}
static int sm_leave(struct hsm *hsm)
{
struct sm *p = container_of(hsm, struct sm, hsm);
free(p->str);
return 0;
}
static int sm_write_prepare(struct hsm *hsm)
{
struct sm *p = container_of(hsm, struct sm, hsm);
p->str[0] = 0;
return 0;
}
static int sm_write(struct hsm *hsm, struct hsm_data *data)
{
struct sm *p = container_of(hsm, struct sm, hsm);
struct sm_data *d = container_of(data, struct sm_data, data);
strcpy(p->str, d->str);
printf("\"%s\"\n", p->str);
hsm_xit(hsm, STATE_COMPLAIN);
return 0;
}
static int sm_complain(struct hsm *hsm)
{
printf("gosh\n");
return 0;
}
static const struct hsm_state sm_states[] = {
[STATE_FILL] = {
.name = "filling",
.parent = HSM_STATE_NULL,
.enter = sm_enter,
.leave = sm_leave,
},
[STATE_WRITE] = {
.name = "writing",
.parent = STATE_FILL,
.enter = sm_write_prepare,
.trigger = sm_write,
},
[STATE_COMPLAIN] = {
.name = "complain",
.parent = HSM_STATE_NULL,
.enter = sm_complain,
},
};
int main(int argc, char **argv)
{
struct sm sm;
struct sm_data data;
data.str = argv[0];
hsm_init(&sm.hsm, sm_states);
hsm_xit(&sm.hsm, STATE_WRITE);
hsm_fire(&sm.hsm, &data.data);
hsm_fini(&sm.hsm);
return 0;
}
#include <errno.h>
#include "hsm.h"
#define HSM_STATE_XITING (-2)
void hsm_init(struct hsm *hsm, persistent const struct hsm_state *states)
{
hsm->states = states;
hsm->state = HSM_STATE_NULL;
}
void hsm_fini(struct hsm *hsm)
{
hsm_xit(hsm, HSM_STATE_NULL);
}
#if DEBUG
#include <stdio.h>
#define LOGD(x, ...) fprintf(stderr, x "\n", ##__VA_ARGS__)
#else
#define LOGD(...)
#endif
static enum hsm_state_enum hsm_next(const struct hsm_state *states,
enum hsm_state_enum from, enum hsm_state_enum to)
{
/* does the target have an ancestor which is us? */
for (; to != HSM_STATE_NULL; to = states[to].parent) {
if (states[to].parent == from)
return to;
}
/* the target is not our (grand)* child, the direction to go is up */
return states[from].parent;
}
static int hsm_xit_function(struct hsm *hsm,
enum hsm_state_enum from, enum hsm_state_enum to)
{
int (* fn)(struct hsm *);
if (to == HSM_STATE_NULL) {
LOGD("-: %s -> NULL", hsm->states[from].name);
fn = hsm->states[from].leave;
} else if (from == HSM_STATE_NULL) {
LOGD("+: NULL -> %s", hsm->states[to].name);
fn = hsm->states[to].enter;
} else if (hsm->states[from].parent == to) { /* exiting */
LOGD("-: %s -> %s", hsm->states[from].name, hsm->states[to].name);
fn = hsm->states[from].leave;
} else {
LOGD("+: %s -> %s", hsm->states[from].name, hsm->states[to].name);
fn = hsm->states[to].enter;
}
return fn ? (* fn)(hsm) : 0;
}
int hsm_xit_one(struct hsm *hsm, enum hsm_state_enum *reqa, unsigned int nreq)
{
enum hsm_state_enum current;
enum hsm_state_enum next;
unsigned int i;
int rc = 0;
if (hsm->state == HSM_STATE_XITING) {
/* The reasoning here is that there's not a defined correct
* way to behave, so it's best to not have any behavior.
*
* What do we do if the requested event is already along the
* path to our desired destination. Do we call 'enter' again,
* if called from an enter function of the target? What
* happens when upon 'enter' a state calls 'xit' twice? Is
* the first path canceled? On that note, should this
* cancel paths at all?
*
* Ergo we error.
*/
return -EINVAL;
}
current = hsm->state;
hsm->state = HSM_STATE_XITING;
for (i = 0; i < nreq; ++i) {
enum hsm_state_enum req = reqa[i];
while (current != req) {
next = hsm_next(hsm->states, current, req);
rc = hsm_xit_function(hsm, current, next);
if (rc)
break;
current = next;
}
if (current == req)
break;
}
hsm->state = current;
return rc;
}
int hsm_xit(struct hsm *hsm, enum hsm_state_enum req)
{
return hsm_xit_one(hsm, &req, 1);
}
int hsm_fire(struct hsm *hsm, struct hsm_data *data)
{
enum hsm_state_enum current;
int rc = -EAGAIN;
if (hsm->state == HSM_STATE_XITING)
return -EINVAL;
current = hsm->state;
while (current != HSM_STATE_NULL && rc == -EAGAIN) {
const struct hsm_state *state = &hsm->states[current];
rc = state->trigger(hsm, data);
current = state->parent;
}
return rc;
}
#ifndef _HSM_H_
#define _HSM_H_
#define persistent
/** HSM state index.
* Custom state indices should start at 0.
*/
enum hsm_state_enum {
HSM_STATE_NULL = -1 /**< Parent null state. */
};
/** HSM data structure.
*/
struct hsm_data {
unsigned int id;
};
struct hsm;
/** HSM state definition. */
struct hsm_state {
const char *name; /**< State name. */
enum hsm_state_enum parent; /**< Parent/Super state. */
/** Event trigger callback.
* @param hsm HSM structure.
* @param data Event type.
* @return 0 on successful transition, !0 on failure.
*
* If -EAGAIN is returned from the handler, the event is deferred to
* the parent state, if one exists.
*/
int (* trigger)(struct hsm *hsm, struct hsm_data *data);
/** State enter callback.
* @param hsm HSM structure.
* @return 0 on success; !0 to cancel.
*/
int (* enter)(struct hsm *hsm);
/** State exit callback.
* @param hsm HSM structure.
* @return 0 on success; !0 to cancel.
*/
int (* leave)(struct hsm *hsm);
};
/** HSM structure */
struct hsm {
const struct hsm_state *states; /**< State table. */
enum hsm_state_enum state; /**< Current state. */
};
/** Initialize a HSM.
* @param hsm HSM structure.
* @param states Array of HSM states to be indexed by state.
*/
void hsm_init(struct hsm *hsm, persistent const struct hsm_state *states);
/** Finalize a HSM.
* @param hsm HSM structure.
*/
void hsm_fini(struct hsm *hsm);
/** Transition to the first successful requested state.
* @param hsm HSM structure.
* @param reqa Array of requested states.
* @param nreq Number of items in the requested state array.
* @return 0 on successful transition; return value from handler on failure.
*
* This will not revert to the original state on failure. If desired, this
* can be done by caching the original state.
*/
int hsm_xit_one(struct hsm *hsm, enum hsm_state_enum *reqa, unsigned int nreq);
/** Transition to requested state.
* @param HSM structure.
* @param req Requested state.
* @return 0 on success; return value from handler on failure.
*
* As this function may make several sub-transitions in the process
* of reaching the desired state, the resulting state on failure may be
* somewhere between the initial state and the requested state. No attempt
* to unwind the transition is made on failure.
*/
int hsm_xit(struct hsm *hsm, enum hsm_state_enum req);
/** Send an event to the currently active state.
* @param hsm HSM structure.
* @param data Userdata
* @return 0 on success, !0 on failure.
*
* The return value from the handler is returned on failure.
*/
int hsm_fire(struct hsm *hsm, struct hsm_data *data);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment