Skip to content

Vanderhell/loxbudget

Repository files navigation

loxbudget

Tiny no-heap C99 library for embedded firmware: pre-flight checks for embedded operations.

CI Release License Language Platform Single Header CMake No Heap No Floats Release Snapshot Security Coverage

Why loxbudget exists

Embedded firmware often fails because operations are allowed to run even when the system is already under resource pressure.

loxbudget provides a deterministic pre-flight gate before risky work: MQTT publish, OTA update, flash write, log burst, debug dump, parser run, queue allocation, or any operation that must be degraded, delayed, rejected, or allowed under pressure.

Features

  • Deterministic check/enter/leave decisions for operation profiles under pressure.
  • No heap, no floats, no global mutable state (all state is user-owned storage).
  • Optional audit ring buffer (LOXBUDGET_ENABLE_AUDIT_TRAIL) to retrieve recent decisions.
  • Optional diagnostic strings (LOXBUDGET_ENABLE_DIAGNOSTIC_STRINGS).
  • Optional rate windows + lifetime limits (LOXBUDGET_ENABLE_RATE_WINDOWS).
  • Optional calibration (LOXBUDGET_ENABLE_CALIBRATION).
  • API stability: the public API declared in include/loxbudget.h is intended to be stable starting with v1.0.0 (semver).

Typical use cases

  • Prevent MQTT storms from exhausting queue slots.
  • Block OTA when voltage or flash budget is unsafe.
  • Degrade logging under memory pressure.
  • Reject non-critical work during survival mode.
  • Enforce flash-write lifetime budgets.

What this is not

loxbudget is not a scheduler, allocator, watchdog, logger, profiler, or RTOS replacement. It is a deterministic admission-control layer for deciding whether an operation may run now.

Alternatives (and why not)

Evaluators almost always ask "why not X?". This table is the short, practical answer.

Alternative Good for Where it breaks down Why loxbudget exists anyway
Token bucket / leaky bucket Smooth-rate limiting a single flow (API calls, publishes) Hard to model multiple resources (RAM/queue slots/flash budget), pressure modes, and enter/leave lifetimes; typically doesn't explain why a decision happened loxbudget gates operations against multiple resource types + pressure state, with deterministic decisions and (optional) audit trail
Ad-hoc if (...) return; Tiny one-off guardrails Drifts into inconsistent rules, hidden coupling, and untestable edge cases; hard to enforce "no heap / no globals / bounded work" across a codebase Centralizes policy in one library + test suite + CI gates (banned symbols, footprint budgets, sanitizers)
FreeRTOS resource handling (queues/semaphores/mutexes) Concurrency control and backpressure within a subsystem It answers “can I take this resource now?”, not “should I start this expensive operation under global pressure?”; no cross-cutting policy across subsystems loxbudget is an admission controller you call before starting work, independent of the RTOS primitive you use underneath

Release status

Current development line: Unreleased

Historical release: v1.0.1

Historical release notes:

  • v1.0.1 is the current tagged release snapshot.
  • v1.0.0-rc1 is a historical release-candidate snapshot.
  • v1.0.0 is the historical first stable release snapshot.

The public API follows semver stability expectations starting with v1.0.0. The project does not claim production readiness or safety certification in the README.

Quick start

#include "loxbudget.h"

int main(void) {
  static uint32_t storage[(LOXBUDGET_REQUIRED_SIZE(2, 2, 0) + 3u) / 4u];
  loxbudget_t b;

  loxbudget_op_profile_t p = loxbudget_op_profile_default(0);
  loxbudget_decision_t d;

  if (loxbudget_init_simple(&b, storage, sizeof(storage), 2, 2) != LOXBUDGET_OK) return 2;
  (void)loxbudget_set_resource(&b, 0, 10, LOXBUDGET_RES_REUSABLE);
  (void)loxbudget_register_op(&b, &p);
  (void)loxbudget_op_set_need(&b, 0, 0, 5);
  (void)loxbudget_check(&b, 0, &d);
  return (d.action == LOXBUDGET_ALLOW_FULL) ? 0 : 1;
}

This initializes a budget instance into caller-provided storage, declares one reusable resource, registers an operation profile, and asks the library for a deterministic decision before running the operation.

Use loxbudget_enter() and loxbudget_leave() when you need an atomic lease around the work itself.

Docs

  • Main index: docs/index.md
  • Getting started: docs/getting_started.md
  • MISRA notes: docs/misra.md
  • Handoff/release notes: docs/handoff.md
  • Release notes: releases/v1.0.1.md, releases/v1.0.0.md, releases/v1.0.0-rc1.md
  • Roadmap: ROADMAP.md

Build And Verify

make test
cmake -S . -B build && cmake --build build
ctest --test-dir build --output-on-failure

Install And Consume With CMake

Install locally:

cmake -S . -B build
cmake --build build
cmake --install build --prefix ./dist

Consume from another CMake project:

find_package(loxbudget CONFIG REQUIRED)
target_link_libraries(your_target PRIVATE loxbudget::loxbudget)

Verification status

Area Status
GCC host build covered in CI
Clang host build covered in CI
ARM Cortex-M0 cross-build covered in CI
clang-format enforced
clang-tidy enforced
ASan/UBSan covered in CI
fuzz smoke covered in CI
single-header build released artifact
CMake install/export supported
footprint budget checked

Benchmarks (measured numbers)

Embedded claims like "deterministic" need numbers. The library keeps .bss = 0 (no global mutable state) by design; user storage lives in your storage[] buffer.

At-a-glance table (MCU)

Platform Toolchain .text .bss loxbudget_check() WCET Stack peak (check())
Cortex-M0 arm-none-eabi-gcc -Os 4192 B (standard, causality off) 0 B TBD (needs on-target timer; M0 has no DWT->CYCCNT) TBD
Cortex-M4 arm-none-eabi-gcc -Os TBD 0 B TBD TBD

Footprint (Cortex-M0, -Os, freestanding)

Measured with arm-none-eabi-size on a relocatable linked object (arm-none-eabi-ld -r). See benchmarks/v1.1_footprint.md for the exact methodology and toolchain versions.

Build .text .data .bss Notes
Standard profile (LOXBUDGET_ENABLE_CAUSALITY=0) 4192 B 0 B 0 B build/loxbudget_arm_standard.o
Standard profile (LOXBUDGET_ENABLE_CAUSALITY=1) 5340 B 0 B 0 B +1148 B .text vs baseline

CPU time (host microbenchmark)

Host microbenchmarks are not MCU cycle counts, but they are good for catching accidental complexity regressions. See benchmarks/v1.1_cycles.md.

Scenario Result
loxbudget_check() baseline (no causality) 260.7 ns/check
loxbudget_check() with causality (2 edges) 338.8 ns/check
Overhead ~1.30×

MCU cycle counts + stack usage

Target-cycle and stack-peak measurement:

  • ESP32 (real hardware): examples/esp32_arduino_loxbudget/ prints min/max/avg latency in a tight-loop stress pass.
  • Cortex-M (real hardware): use a cycle counter (DWT->CYCCNT on M3/M4/M7) or a hardware timer on M0/M0+. See benchmarks/cortexm_wcet_stack/.

Repository Layout

  • include/ public API
  • src/ library implementation
  • adapters/ optional integrations
  • tests/ host and integration coverage
  • examples/ minimal and scenario-oriented demos
  • tools/ utility scripts for amalgamation, calibration and checks

Contributing

See CONTRIBUTING.md and CODE_OF_CONDUCT.md.

About

loxbudget is a small, deterministic, heap-free C99 library for embedded systems. It helps you decide whether an operation should run (and at what level) based on configurable resource budgets, rate windows, and optional calibration/auditing.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages