Skip to content

Commit b28a872

Browse files
authored
Merge pull request #1974 from rust-osdev/bool-fix
uefi-raw: enhance Boolean type and make it more type safe
2 parents c889cb7 + ddfe46e commit b28a872

2 files changed

Lines changed: 106 additions & 2 deletions

File tree

uefi-raw/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
- Corrected the type of the `driver_image` parameter in
99
`BootServices::connect_controller` from `Handle` to `*const Handle`.
1010
- Corrected signature of `BootServices::exit` from `!` to `Status`.
11+
- We made changes to the `Boolean` type, especially switching to manual
12+
implementations of `PartialEq`, `Eq`, `PartialOrd`, `Ord`, and `Hash` to match
13+
the type's logical properties. If you rely on the specific bit pattern,
14+
please use `.0` to access the underlying value.
1115

1216

1317
# uefi-raw - v0.14.0 (2026-03-22)

uefi-raw/src/lib.rs

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,13 @@ pub mod time;
3333
mod net;
3434
mod status;
3535

36+
use core::cmp;
3637
pub use net::*;
3738
pub use status::Status;
3839
pub use uguid::{Guid, guid};
3940

4041
use core::ffi::c_void;
42+
use core::hash::{Hash, Hasher};
4143

4244
/// Handle to an event structure.
4345
pub type Event = *mut c_void;
@@ -73,19 +75,34 @@ pub type VirtualAddress = u64;
7375
/// This is similar to a `bool`, but allows values other than 0 or 1 to be
7476
/// stored without it being undefined behavior.
7577
///
76-
/// Any non-zero value is treated as logically `true`.
77-
#[derive(Copy, Clone, Debug, Default, PartialEq, Ord, PartialOrd, Eq, Hash)]
78+
/// Any non-zero value is treated as logical `true`. The comparison, ordering,
79+
/// and hashing implementations follow that logical interpretation, so all
80+
/// non-zero values compare equal and hash the same way. The original byte is
81+
/// still preserved in the public field; compare the `.0` values directly when
82+
/// the exact raw bit pattern matters.
83+
#[derive(Copy, Clone, Debug, Default)]
7884
#[repr(transparent)]
7985
pub struct Boolean(pub u8);
8086

8187
impl Boolean {
8288
/// [`Boolean`] representing `true`.
89+
///
90+
/// # Caution
91+
///
92+
/// This is only one possible true bit pattern. In UEFI, every non-zero
93+
/// bit pattern is treated as logical `true`.
8394
pub const TRUE: Self = Self(1);
8495

8596
/// [`Boolean`] representing `false`.
8697
pub const FALSE: Self = Self(0);
8798
}
8899

100+
impl From<u8> for Boolean {
101+
fn from(value: u8) -> Self {
102+
Self(value)
103+
}
104+
}
105+
89106
impl From<bool> for Boolean {
90107
fn from(value: bool) -> Self {
91108
match value {
@@ -106,6 +123,45 @@ impl From<Boolean> for bool {
106123
}
107124
}
108125

126+
impl PartialEq for Boolean {
127+
fn eq(&self, other: &Self) -> bool {
128+
match (self.0, other.0) {
129+
(0, 0) => true,
130+
(0, _) => false,
131+
(_, 0) => false,
132+
// We handle it as in C: Any bit-pattern != 0 equals true
133+
(_, _) => true,
134+
}
135+
}
136+
}
137+
138+
impl Eq for Boolean {}
139+
140+
impl PartialOrd for Boolean {
141+
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
142+
Some(self.cmp(other))
143+
}
144+
}
145+
146+
impl Ord for Boolean {
147+
fn cmp(&self, other: &Self) -> cmp::Ordering {
148+
match (self.0, other.0) {
149+
(0, 0) => cmp::Ordering::Equal,
150+
(0, _) => cmp::Ordering::Less,
151+
(_, 0) => cmp::Ordering::Greater,
152+
// We handle it as in C: Any bit-pattern != 0 equals true
153+
(_, _) => cmp::Ordering::Equal,
154+
}
155+
}
156+
}
157+
158+
impl Hash for Boolean {
159+
fn hash<H: Hasher>(&self, state: &mut H) {
160+
let seed = if self.0 == 0 { 0 } else { 1 };
161+
state.write_u8(seed);
162+
}
163+
}
164+
109165
#[cfg(test)]
110166
mod tests {
111167
use super::*;
@@ -127,4 +183,48 @@ mod tests {
127183
assert!(bool::from(Boolean(0b11111110)));
128184
assert!(bool::from(Boolean(0b11111111)));
129185
}
186+
187+
#[test]
188+
fn test_order() {
189+
assert!(Boolean::FALSE < Boolean::TRUE);
190+
assert!(Boolean::TRUE > Boolean::FALSE);
191+
}
192+
193+
#[test]
194+
fn test_equality() {
195+
assert_eq!(Boolean(0), Boolean(0));
196+
assert_ne!(Boolean(0), Boolean(3));
197+
assert_ne!(Boolean(7), Boolean(0));
198+
assert_eq!(Boolean(1), Boolean(1));
199+
assert_eq!(Boolean(13), Boolean(7));
200+
}
201+
202+
// Tests that hash impl matches equal impl
203+
#[test]
204+
fn test_hash() {
205+
#[derive(Default)]
206+
struct TestHasher(u64);
207+
208+
impl Hasher for TestHasher {
209+
fn finish(&self) -> u64 {
210+
self.0
211+
}
212+
213+
fn write(&mut self, bytes: &[u8]) {
214+
for byte in bytes {
215+
self.0 = self.0.wrapping_mul(257).wrapping_add(u64::from(*byte));
216+
}
217+
}
218+
}
219+
220+
fn calculate_hash(value: Boolean) -> u64 {
221+
let mut hasher = TestHasher::default();
222+
value.hash(&mut hasher);
223+
hasher.finish()
224+
}
225+
226+
assert_eq!(calculate_hash(Boolean(1)), calculate_hash(Boolean(13)));
227+
assert_eq!(calculate_hash(Boolean(13)), calculate_hash(Boolean(255)));
228+
assert_ne!(calculate_hash(Boolean(0)), calculate_hash(Boolean(13)));
229+
}
130230
}

0 commit comments

Comments
 (0)