khora_data/ecs/
bitset.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Implements a sparse bitset for tracking component presence in semantic domains.
16
17/// A simple bitset wrapped around a `Vec<u64>`.
18///
19/// This structure is used to track which entities have components in a specific
20/// `SemanticDomain`. It allows for extremely fast (bitwise) filtering during
21/// transversal queries.
22#[derive(Debug, Default, Clone)]
23pub struct DomainBitset {
24    pub(crate) bits: Vec<u64>,
25}
26
27impl DomainBitset {
28    /// Creates a new, empty bitset.
29    pub fn new() -> Self {
30        Self { bits: Vec::new() }
31    }
32
33    /// Sets the bit at the specified index to 1.
34    pub fn set(&mut self, index: u32) {
35        let word_idx = (index / 64) as usize;
36        let bit_idx = index % 64;
37
38        // Ensure the vector is large enough to hold the bit.
39        if word_idx >= self.bits.len() {
40            self.bits.resize(word_idx + 1, 0);
41        }
42
43        self.bits[word_idx] |= 1 << bit_idx;
44    }
45
46    /// Clears the bit at the specified index to 0.
47    pub fn clear(&mut self, index: u32) {
48        let word_idx = (index / 64) as usize;
49        let bit_idx = index % 64;
50
51        if word_idx < self.bits.len() {
52            self.bits[word_idx] &= !(1 << bit_idx);
53        }
54    }
55
56    /// Returns the 64-bit block at the specified word index.
57    /// Returns 0 if the index is out of bounds.
58    pub fn get_block(&self, word_idx: usize) -> u64 {
59        self.bits.get(word_idx).copied().unwrap_or(0)
60    }
61
62    /// Returns true if the bit at the specified index is set.
63    pub fn is_set(&self, index: u32) -> bool {
64        let word_idx = (index / 64) as usize;
65        let bit_idx = index % 64;
66
67        if let Some(word) = self.bits.get(word_idx) {
68            (word & (1 << bit_idx)) != 0
69        } else {
70            false
71        }
72    }
73
74    /// Performs a bitwise AND with another bitset.
75    pub fn intersect(&mut self, other: &DomainBitset) {
76        let len = self.bits.len().min(other.bits.len());
77        for i in 0..len {
78            self.bits[i] &= other.bits[i];
79        }
80        // If other is shorter, the rest of self bits must be cleared.
81        if self.bits.len() > len {
82            self.bits.truncate(len);
83        }
84    }
85}