{"id":50737694,"url":"https://github.com/royratcliffe/infinite_state_machine","last_synced_at":"2026-06-10T15:02:11.127Z","repository":{"id":312367744,"uuid":"1047074085","full_name":"royratcliffe/infinite_state_machine","owner":"royratcliffe","description":"Infinite State Machine Library","archived":false,"fork":false,"pushed_at":"2026-01-01T11:27:13.000Z","size":282,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-06T04:52:34.731Z","etag":null,"topics":["c","cpp","embedded","state-machine"],"latest_commit_sha":null,"homepage":"https://royratcliffe.github.io/infinite_state_machine/","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/royratcliffe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-29T17:40:16.000Z","updated_at":"2026-01-01T15:19:01.000Z","dependencies_parsed_at":"2025-08-30T06:11:54.050Z","dependency_job_id":"80d6477e-e42c-45d5-8349-89ea9abad7b0","html_url":"https://github.com/royratcliffe/infinite_state_machine","commit_stats":null,"previous_names":["royratcliffe/infinite_state_machine"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/royratcliffe/infinite_state_machine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/royratcliffe%2Finfinite_state_machine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/royratcliffe%2Finfinite_state_machine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/royratcliffe%2Finfinite_state_machine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/royratcliffe%2Finfinite_state_machine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/royratcliffe","download_url":"https://codeload.github.com/royratcliffe/infinite_state_machine/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/royratcliffe%2Finfinite_state_machine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34157453,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-10T02:00:07.152Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","cpp","embedded","state-machine"],"created_at":"2026-06-10T15:02:05.614Z","updated_at":"2026-06-10T15:02:06.153Z","avatar_url":"https://github.com/royratcliffe.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"See also original blog [post](https://roy.ratcliffe.me/2025/08/31/infinite-state-machine/).\n\n# Infinite State Machine\n\nThe following presents a hierarchical (nested) state machine\nimplementation in C.\n\n\u003cdiv id=\"fig-infinite-state-machine\"\u003e\n\n\u003cimg src=\"infinite-state-machine.svg\" /\u003e\n\nFigure 1: The infinite state machine concept in Unified Modelling\nLanguage\n\n\u003c/div\u003e\n\nThe concept applies a separation of concerns. The ‘machine’ and the\n‘topology’ of its states occupy distinct yet interrelated aspects. The\nmachine is a run-time entity that manages state transitions, while the\ntopology defines the structural relationships between states. One is\ndynamic, the other is ordinarily static and pre-defined. Transitions and\nactions applied to the machine serve as operational mechanisms that\nmanipulate the machine by walking the topology, triggering actions along\nthe way.\n\n## Introduction\n\nWhy infinite? “Infinite” refers to the absence of intrinsic compile-time\nnesting limits aside from memory or configured maximum depth in the C\nvariant. It also implies that the system can handle an unbounded number\nof states and transitions, making it highly flexible and adaptable. The\ndesign also supports unbounded state topologies. Such can be flat or\nhierarchical, allowing for more complex state relationships.\nAdditionally, the state machine can manage an unbounded number of active\nstates simultaneously, allowing for concurrent state management with\noverlapping topologies as needed.\n\n“Topology” here refers to the arrangement and organisation of states\nwithin the state machine. This can include hierarchical relationships,\nwhere states are nested within one another, for more complex\narrangements that offer greater flexibility and adaptability—states\nwithin states *ad aeternum*.\n\nFinally, the term “infinite” acts as a play on words, suggesting\nlimitless possibilities versus finite constraints.\n\n## C Implementation for Deeply-Embedded Systems\n\nThink of C as the lingua Franca of computing. If you can express it in\nC, you can express it anywhere.\n\nUse simple language elements: integers, structures, arrays and pointers.\nThis makes it suitable for deeply embedded systems with limited\nresources, and it is also helpful in designing a portable framework. C\nconstructs typically have their analogues in other languages, making it\neasier to translate ideas across different platforms.\n\n### Data structures\n\nTranslating the design to C gives the following data structures:\n\n``` c\nstruct infinite_state {\n    struct infinite_state *super; // parent (NULL for root)\n    void (*enter)(struct infinite_state *, struct infinite_state_machine *); // optional\n    void (*exit)(struct infinite_state *, struct infinite_state_machine *);  // optional\n};\n\nstruct infinite_state_machine {\n    struct infinite_state *states[INFINITE_STATE_MACHINE_MAX_DEPTH];\n    int depth; // 0..INFINITE_STATE_MACHINE_MAX_DEPTH\n};\n```\n\nThe maximum depth defaults to 7, unless it has already been defined\nbefore the inclusion point of the header. Why 7? There is a method to\nthe choice of a maximum depth of 7. Pointers and integers are 32 bits\nwide on a 32-bit machine. Seven pointers and one integer occupy eight\nwords, or 32 bytes on a typical microcontroller. If a single machine\nrequires more than seven nesting levels, it is generally better to\nrefactor the design to reduce complexity.\n\n### Lifecycle and core operations\n\n| Function | Description |\n|----|----|\n| `void infinite_state_machine_init(machine)` | Clear machine (depth=0, state slots NULL) |\n| `void infinite_state_machine_goto(machine, state)` | LCA-optimised transition to `state` (may be NULL: no-op) |\n| `void infinite_state_machine_jump(machine, state)` | Rebuild the stack from scratch, from root to `state,` ignoring callbacks |\n| `int infinite_state_machine_in(machine, state)` | 1 if active; 0 if not; negative `-EINVAL` on invalid arguments |\n| `struct infinite_state *infinite_state_machine_top(machine)` | Current innermost state or NULL |\n| `struct infinite_state **infinite_state_topology(state, depth, vec)` | Helper producing forward topology (outer to inner) |\n\nLCA stands for “least common ancestor.” It is an optimisation technique\nused to improve the efficiency of state transitions within the state\nmachine. By identifying the least common ancestor of the current state\nand the target state, the state machine can skip unnecessary\nintermediate states, resulting in faster transitions and reduced\noverhead.\n\nLCA improves:\n\n- Transitioning from a nested state to a sibling state without going\n  through the parent state.\n- Skipping intermediate states when transitioning back to a previously\n  active state.\n- Optimising transitions between states with complex hierarchical\n  relationships.\n\nThe core “goto” operation applies the optimisation, as follows.\n\n``` c\n/*!\n * \\brief Goes to a state in the infinite state machine.\n * \\param machine The infinite state machine.\n * \\param state The state to enter.\n *\n * Going to a state in the infinite state machine transitions the machine to the\n * new state. This will push the current state onto the stack and transition to\n * the new state. If the new state is the same as the current state, no action\n * is taken; likewise, if the new state is \\c{NULL}, no action is taken.\n * Otherwise, all the exit actions for the current state are run, and the new\n * state is entered by running all enter actions.\n *\n * \\note O(n) time complexity applies, where n is the depth of the state machine.\n */\nvoid infinite_state_machine_goto(struct infinite_state_machine *machine, struct infinite_state *state)\n{\n    if (state == infinite_state_machine_top(machine))\n    {\n        return;\n    }\n    struct infinite_state_machine jump;\n    infinite_state_machine_jump(\u0026jump, state);\n    int depth = 0;\n    while (depth \u003c machine-\u003edepth \u0026\u0026 depth \u003c jump.depth \u0026\u0026 machine-\u003estates[depth] == jump.states[depth])\n    {\n        depth++;\n    }\n    while (machine-\u003edepth \u003e depth)\n    {\n        infinite_state_machine_exit(machine);\n    }\n    while (jump.depth \u003e depth)\n    {\n        infinite_state_machine_enter(machine, jump.states[depth++]);\n    }\n}\n```\n\nThe implementation uses a little trick. By leveraging the concept of a\n“jump” state machine, the “go” operator can effectively manage state\ntransitions without the overhead of maintaining a whole stack of states.\nThis enables more efficient transitions, particularly in scenarios with\ncomplex state hierarchies. The jump state machine serves as a\nlightweight alternative, allowing for quick adjustments to the state\nwithout the need for extensive bookkeeping.\n\n## Usage\n\nThe following example models a simple engine with the states: stopped,\nstarting, igniting, cranking, and running. It demonstrates the use of\nthe infinite state machine to manage the engine’s state transitions.\n\nThe engine starts in the stopped state. When the `start` function is\ncalled, it transitions to the starting state, which then enters the\nigniting state. The engine cycles through the igniting and cranking\nstates before reaching the running state. The `stop` function can be\ncalled at any time to transition back to the stopped state.\n\nIgniting and cranking exist as sub-states of “starting.” The engine\ntakes time to transition between these states, simulating the various\nphases of starting an engine: switch on the ignition for a moment, then\ncrank the engine until it starts.\n\n\u003cdiv id=\"fig-engine\"\u003e\n\n\u003cimg src=\"engine.svg\" /\u003e\n\nFigure 2: Engine in Unified Modelling Language\n\n\u003c/div\u003e\n\nImplemented in C:\n\n``` c\n#include \"infinite_state_machine.h\"\n\n#include \u003cstdbool.h\u003e\n#include \u003cassert.h\u003e\n\nstatic void engine_cycle(void);\nstatic void igniting_cycle(void);\nstatic void cranking_cycle(void);\n\nstatic void starting_enter(struct infinite_state *state, struct infinite_state_machine *machine);\nstatic void igniting_enter(struct infinite_state *state, struct infinite_state_machine *machine);\nstatic void cranking_enter(struct infinite_state *state, struct infinite_state_machine *machine);\n\nstruct engine\n{\n    struct infinite_state state;\n    void (*cycle)(void);\n};\n\nstruct starting\n{\n    struct engine engine;\n    int cycling;\n};\n\n/*\n * Initialise the engine state topology.\n */\nstatic struct engine stopped = {.cycle = engine_cycle};\nstatic struct engine starting = {.state.enter = starting_enter, .cycle = engine_cycle};\nstatic struct starting igniting = {.engine =\n    {.state.super = \u0026starting.state, .state.enter = igniting_enter, .cycle = igniting_cycle}};\nstatic struct starting cranking = {.engine =\n    {.state.super = \u0026starting.state, .state.enter = cranking_enter, .cycle = cranking_cycle}};\nstatic struct engine running = {.cycle = engine_cycle};\n\nstatic struct infinite_state_machine engine;\n\nstatic void go(struct engine *to)\n{\n    infinite_state_machine_goto(\u0026engine, \u0026to-\u003estate);\n}\n\n/*\n * Check if the engine is in a specific state or super-state.\n */\nstatic bool in(struct engine *in)\n{\n    return infinite_state_machine_in(\u0026engine, \u0026in-\u003estate) == 1;\n}\n\n/*\n * Start the engine. This is an external event.\n * Transition from the stopped state to the starting state.\n */\nvoid start(void)\n{\n    if (in(\u0026stopped))\n    {\n        go(\u0026starting);\n    }\n}\n\n/*\n * Stop the engine, an external event.\n * Transition from the any state to the stopped state.\n */\nvoid stop(void)\n{\n    go(\u0026stopped);\n}\n\n/*\n * Cycle the engine's top state.\n * This takes advantage of the fact that the engine's states are all of the same\n * type. It assumes that the top state is always an engine structure and that\n * the structure's first member is the infinite_state base structure. It also\n * assumes that the cycle function is implemented for each engine state.\n */\nstatic void cycle(void)\n{\n    ((struct engine *)infinite_state_machine_top(\u0026engine))-\u003ecycle();\n}\n\n/*\n * Test the infinite state machine.\n */\nint test_engine()\n{\n    /*\n     * Strictly speaking, the machine does not require initialisation.\n     * It lives in \"blank static storage.\" The C run-time automatically initialises\n     * all static and global variables to zero. In other words, C has already initialised\n     * the machine for us.\n     */\n    infinite_state_machine_init(\u0026engine);\n\n    /*\n     * Set up the engine by applying the initial transition.\n     */\n    go(\u0026stopped);\n\n    assert(in(\u0026stopped));\n    start();\n    assert(in(\u0026starting));\n    assert(in(\u0026igniting.engine));\n    cycle();\n    assert(in(\u0026starting));\n    assert(in(\u0026cranking.engine));\n    cycle();\n    assert(in(\u0026starting));\n    assert(in(\u0026cranking.engine));\n    cycle();\n    assert(in(\u0026running));\n    stop();\n    assert(in(\u0026stopped));\n\n    return 0;\n}\n\nstatic void engine_cycle(void) {}\n\nstatic void igniting_cycle(void)\n{\n    if (--igniting.cycling == 0)\n    {\n        go(\u0026cranking.engine);\n    }\n}\n\nstatic void cranking_cycle(void)\n{\n    if (--cranking.cycling == 0)\n    {\n        go(\u0026running);\n    }\n}\n\nstatic void starting_enter(struct infinite_state *state, struct infinite_state_machine *machine)\n{\n    go(\u0026igniting.engine);\n}\n\nstatic void igniting_enter(struct infinite_state *state, struct infinite_state_machine *machine)\n{\n    igniting.cycling = 1;\n}\n\nstatic void cranking_enter(struct infinite_state *state, struct infinite_state_machine *machine)\n{\n    cranking.cycling = 2;\n}\n```\n\nThe example flow proceeds as follows:\n\n1.  Enter “starting” and immediately go to “igniting”.\n2.  Enter “igniting” and set cycling to 1.\n3.  Each cycle, decrement cycling. When zero, go to “cranking”.\n4.  Enter “cranking” and set cycling to 2.\n5.  Each cycle, decrement cycling. When zero, go to “running”.\n\n## Conclusions\n\nThe concept of a state machine is ubiquitous in computer science and\nengineering. Its principles can be applied across various domains, from\nembedded systems to high-level application design. The infinite state\nmachine model presented here offers a lightweight but flexible and\nrobust framework for managing complex state interactions, enabling\ndevelopers to create more adaptable and maintainable stateful systems.\n\nThe C implementation is particularly well-suited for deeply embedded\nsystems, where resources are limited, and efficiency is paramount. By\nleveraging simple data structures and core operations, this design can\nbe easily adapted to a wide range of applications, making it a versatile\ntool in the developer’s arsenal. The risk for the developer lies in the\npotential complexity of managing state transitions and ensuring the\ncorrect behaviour of the state machine. As the system evolves,\nmaintaining clarity and simplicity in the state management logic becomes\ncrucial to avoid introducing bugs and inefficiencies.\n\nNotably, the state machine apparatus itself has a state; call it an\nauxiliary state. But in this case, the additional state is elementary:\njust a single topology vector representing a ‘cursor’ within the state\nhierarchy. The topology of the ‘machine’ and the machine itself exist\nseparately, allowing for a clean separation of concerns and easier\nmanagement of state transitions. It also means that the machine\nabstraction can be modelled as a private property, or properties in the\ncase of multiple parallel machines, all belonging to the enclosing\nconcept as a whole and *not* as part of an inheritance framework. The\nenclosing design can thus *fully* encapsulate the state machine’s\nbehaviour and interactions, providing a more robust framework for\nbuilding complex systems.\n\n\u003e “Divide et impera, ad aeternum regnat.”\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froyratcliffe%2Finfinite_state_machine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Froyratcliffe%2Finfinite_state_machine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froyratcliffe%2Finfinite_state_machine/lists"}