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}