use crate::core::command::CommandId;
use std::cell::RefCell;
pub const MAX_COMMANDS: usize = 32 * 2048;
thread_local! {
static GLOBAL_COMMAND_SET: RefCell<CommandSet> = RefCell::new(CommandSet::with_all_enabled());
static COMMAND_SET_CHANGED: RefCell<bool> = RefCell::new(false);
}
pub fn command_enabled(command: CommandId) -> bool {
GLOBAL_COMMAND_SET.with(|cs| cs.borrow().has(command))
}
pub fn enable_command(command: CommandId) {
GLOBAL_COMMAND_SET.with(|cs| {
let mut set = cs.borrow_mut();
if !set.has(command) {
COMMAND_SET_CHANGED.with(|changed| *changed.borrow_mut() = true);
}
set.enable_command(command);
});
}
pub fn disable_command(command: CommandId) {
GLOBAL_COMMAND_SET.with(|cs| {
let mut set = cs.borrow_mut();
if set.has(command) {
COMMAND_SET_CHANGED.with(|changed| *changed.borrow_mut() = true);
}
set.disable_command(command);
});
}
pub fn command_set_changed() -> bool {
COMMAND_SET_CHANGED.with(|changed| *changed.borrow())
}
pub fn clear_command_set_changed() {
COMMAND_SET_CHANGED.with(|changed| *changed.borrow_mut() = false);
}
pub fn init_command_set() {
use crate::core::command::CM_CLOSE;
GLOBAL_COMMAND_SET.with(|cs| {
let mut set = cs.borrow_mut();
*set = CommandSet::with_all_enabled();
set.disable_command(CM_CLOSE);
});
COMMAND_SET_CHANGED.with(|changed| *changed.borrow_mut() = false);
}
const COMMANDS_COUNT: usize = MAX_COMMANDS / 32;
#[derive(Clone, PartialEq)]
pub struct CommandSet {
cmds: Box<[u32; COMMANDS_COUNT]>,
}
impl CommandSet {
pub fn new() -> Self {
Self {
cmds: Box::new([0; COMMANDS_COUNT]),
}
}
pub fn with_all_enabled() -> Self {
Self {
cmds: Box::new([0xFFFFFFFF; COMMANDS_COUNT]),
}
}
pub fn has(&self, command: CommandId) -> bool {
let cmd = command as usize;
if cmd >= MAX_COMMANDS {
return true;
}
let word_index = cmd / 32;
let bit_mask = 1u32 << (cmd & 0x1F);
(self.cmds[word_index] & bit_mask) != 0
}
pub fn enable_command(&mut self, command: CommandId) {
let cmd = command as usize;
if cmd >= MAX_COMMANDS {
return;
}
let word_index = cmd / 32;
let bit_mask = 1u32 << (cmd & 0x1F);
self.cmds[word_index] |= bit_mask;
}
pub fn disable_command(&mut self, command: CommandId) {
let cmd = command as usize;
if cmd >= MAX_COMMANDS {
return;
}
let word_index = cmd / 32;
let bit_mask = 1u32 << (cmd & 0x1F);
self.cmds[word_index] &= !bit_mask;
}
pub fn enable_range(&mut self, cmd_start: CommandId, cmd_end: CommandId) {
let start = cmd_start as usize;
let end = cmd_end as usize;
if end >= MAX_COMMANDS || end <= start {
return;
}
let word_start = start / 32;
let word_end = end / 32;
if word_start == word_end {
for bit in (start & 0x1F)..=(end & 0x1F) {
self.cmds[word_start] |= 1u32 << bit;
}
return;
}
for bit in (start & 0x1F)..32 {
self.cmds[word_start] |= 1u32 << bit;
}
for word in (word_start + 1)..word_end {
self.cmds[word] = 0xFFFFFFFF;
}
for bit in 0..=(end & 0x1F) {
self.cmds[word_end] |= 1u32 << bit;
}
}
pub fn disable_range(&mut self, cmd_start: CommandId, cmd_end: CommandId) {
let start = cmd_start as usize;
let end = cmd_end as usize;
if end >= MAX_COMMANDS || end <= start {
return;
}
let word_start = start / 32;
let word_end = end / 32;
if word_start == word_end {
for bit in (start & 0x1F)..=(end & 0x1F) {
self.cmds[word_start] &= !(1u32 << bit);
}
return;
}
for bit in (start & 0x1F)..32 {
self.cmds[word_start] &= !(1u32 << bit);
}
for word in (word_start + 1)..word_end {
self.cmds[word] = 0;
}
for bit in 0..=(end & 0x1F) {
self.cmds[word_end] &= !(1u32 << bit);
}
}
pub fn enable_set(&mut self, other: &CommandSet) {
for i in 0..COMMANDS_COUNT {
self.cmds[i] |= other.cmds[i];
}
}
pub fn disable_set(&mut self, other: &CommandSet) {
for i in 0..COMMANDS_COUNT {
self.cmds[i] &= !other.cmds[i];
}
}
pub fn enable_all(&mut self) {
self.cmds.fill(0xFFFFFFFF);
}
pub fn is_empty(&self) -> bool {
self.cmds.iter().all(|&word| word == 0)
}
pub fn intersect(&mut self, other: &CommandSet) {
for i in 0..COMMANDS_COUNT {
self.cmds[i] &= other.cmds[i];
}
}
pub fn union(&mut self, other: &CommandSet) {
for i in 0..COMMANDS_COUNT {
self.cmds[i] |= other.cmds[i];
}
}
}
impl Default for CommandSet {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_enable_disable_single() {
let mut cs = CommandSet::new();
assert!(!cs.has(10));
cs.enable_command(10);
assert!(cs.has(10));
cs.disable_command(10);
assert!(!cs.has(10));
}
#[test]
fn test_enable_range() {
let mut cs = CommandSet::new();
cs.enable_range(10, 20);
assert!(!cs.has(9));
assert!(cs.has(10));
assert!(cs.has(15));
assert!(cs.has(20));
assert!(!cs.has(21));
}
#[test]
fn test_enable_all() {
let mut cs = CommandSet::new();
cs.enable_all();
assert!(cs.has(0));
assert!(cs.has(100));
assert!(cs.has(65535));
}
#[test]
fn test_is_empty() {
let mut cs = CommandSet::new();
assert!(cs.is_empty());
cs.enable_command(50);
assert!(!cs.is_empty());
}
#[test]
fn test_commands_default_disabled() {
let cs = CommandSet::new();
assert!(!cs.has(0));
assert!(!cs.has(100));
assert!(!cs.has(1000));
assert!(!cs.has(60000));
assert!(!cs.has(65535)); }
}