oot_bitset

Crates.iooot_bitset
lib.rsoot_bitset
version0.1.1
created_at2025-07-04 19:42:26.880731+00
updated_at2025-07-04 19:44:28.657303+00
descriptionA no‑frills, zero‑overhead bit flag system
homepage
repositoryhttps://github.com/jb55/oot_bitset
max_upload_size
id1738408
size13,972
William Casarin (jb55)

documentation

README

OoT Bitset

A no‑frills, zero‑overhead flag system inspired by The Legend of Zelda: Ocarina of Time

Implemented in C / C++, and Rust

Need to pack hundreds (or thousands) of one‑bit flags—“talked to an NPC”, “opened a chest”, etc.—into a save file without wasting bytes? Ocarina of Time solved this by storing flags in an array of uint16_t words. oot_bitset offers the same trick in either language, with zero runtime overhead.


Why use it?

  • Space‑efficient – 1 × u16 word ≙ 16 flags. Scale from 1 to 4096 words (65 536 flags).
  • Zero‑cost abstractions – branch‑free bit‑twiddling; compiles to a handful of instructions.
  • Header‑only / single‑crate – drop a header (C) or add a tiny dependency (Rust). No heap, no alloc.
  • Infinitely scalable – need 10 flags or 10 000? Just resize the array.
  • Proven in‑game design – directly mirrors OoT’s save‑file format.

Installation

C / C++

  1. Copy oot_bitset.h somewhere in your include path.
  2. Compile with any C99 (or later) compiler—no extra flags required.
cc -std=c99 my_game.c -o my_game

Rust

Add the crate to your Cargo.toml:

[dependencies]
oot_bitset = "0.1"

Quick start

C example

#include "oot_bitset.h"
#include <stdio.h>

enum GameEvents {
    FLAG_MET_RUTO_FIRST_TIME        = 0x00, // word 0, bit 0
    FLAG_TALKED_TO_MALON_FIRST_TIME = 0x02, // word 0, bit 2
    FLAG_SAW_BOB   = 0x10,                  // word 1, bit 0
    FLAG_SAW_ALICE = 0x1A                   // word 1, bit 10
};

int main(void) {
    uint16_t flags[30] = {0};   // 30 words ⇒ 480 flags

    bitset_set(flags, FLAG_SAW_BOB);
    printf("Saw Bob? %s\n", bitset_get(flags, FLAG_SAW_BOB) ? "yes" : "no");
}

Compile:

cc -std=c99 demo.c -o demo

Rust example

use oot_bitset::{bitset_get, bitset_set};

#[repr(u16)]
enum GameEvents {
    MetRutoFirstTime        = 0x00, // word 0, bit 0
    TalkedToMalonFirstTime  = 0x02, // word 0, bit 2
    SawBob                  = 0x10, // word 1, bit 0
    SawAlice                = 0x1A, // word 1, bit 10
}

fn main() {
    let mut flags = [0u16; 30]; // 30 words ⇒ 480 flags

    bitset_set(&mut flags, GameEvents::SawBob as u16);
    println!("Saw Bob? {}", bitset_get(&flags, GameEvents::SawBob as u16));
}

Run:

cargo run --example basic

API reference

C functions & macros

#define bitset_word(set, flag)  ((set)[bitset_index(flag)])
static inline uint16_t bitset_index(uint16_t flag); // word (0–4095)
static inline uint16_t bitset_mask(uint16_t flag);  // bit  (0–15)
static inline bool     bitset_get (uint16_t *set, uint16_t flag);
static inline void     bitset_set (uint16_t *set, uint16_t flag);
static inline void     bitset_clear(uint16_t *set, uint16_t flag);

Rust equivalents

pub const fn bitset_index(flag: u16) -> usize;
pub const fn bitset_mask(flag: u16) -> u16;

pub fn bitset_get  (set: &[u16],   flag: u16) -> bool;
pub fn bitset_set  (set: &mut [u16], flag: u16);
pub fn bitset_clear(set: &mut [u16], flag: u16);
pub fn bitset_word_mut(set: &mut [u16], flag: u16) -> &mut u16;

All functions are #[inline(always)] and panic if the slice is too short.


Sizing the array

max_flags = words × 16
words     = ceil(max_flags / 16)

Use as few or as many words as your project needs. OoT used 30 words (480 flags), but nothing stops you from using 1 word (16 flags) or 4 096 words (65 536 flags).


Flag encoding

Bits 15…4 (12 bits) 3…0 (4 bits)
Use  word index bit index
Max  0–4095 words 0–15 bits

Because each hex digit is 4 bits, you can read a flag as “word:bit”. Example: 0x1AC → word 26, bit 12.


Example output

Words[0] = 0x0004   // FLAG_TALKED_TO_MALON_FIRST_TIME
Words[1] = 0x0401   // FLAG_SAW_BOB | FLAG_SAW_ALICE
Commit count: 0

cargo fmt