Skip to content

Make can::Id usable as key in BTreeMap and HashMap #384

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Added
- Implement `PartialOrd`, `Ord`, `Hash` for `can::StandardId`, `can::ExtendedId` and `can::Id` according to CAN bus arbitration rules

## [v1.0.0-alpha.8] - 2022-04-15

*** This is (also) an alpha release with breaking changes (sorry) ***
Expand Down
55 changes: 52 additions & 3 deletions src/can/id.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! CAN Identifiers.

/// Standard 11-bit CAN Identifier (`0..=0x7FF`).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct StandardId(u16);

impl StandardId {
Expand Down Expand Up @@ -40,7 +40,7 @@ impl StandardId {
}

/// Extended 29-bit CAN Identifier (`0..=1FFF_FFFF`).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct ExtendedId(u32);

impl ExtendedId {
Expand Down Expand Up @@ -85,7 +85,7 @@ impl ExtendedId {
}

/// A CAN Identifier (standard or extended).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Id {
/// Standard 11-bit Identifier (`0..=0x7FF`).
Standard(StandardId),
Expand All @@ -94,6 +94,44 @@ pub enum Id {
Extended(ExtendedId),
}

/// Implement `Ord` according to the CAN arbitration rules
///
/// When performing arbitration, frames are looked at bit for bit starting
/// from the beginning. A bit with the value 0 is dominant and a bit with
/// value of 1 is recessive.
///
/// When two devices are sending frames at the same time, as soon as the first
/// bit is found which differs, the frame with the corresponding dominant
/// 0 bit will win and get to send the rest of the frame.
///
/// This implementation of `Ord` for `Id` will take this into consideration
/// and when comparing two different instances of `Id` the "smallest" will
/// always be the ID which would form the most dominant frame, all other
/// things being equal.
impl Ord for Id {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
let split_id = |id: &Id| {
let (standard_id_part, ide_bit, extended_id_part) = match id {
Id::Standard(StandardId(x)) => (*x, 0, 0),
Id::Extended(x) => (
x.standard_id().0,
1,
x.0 & ((1 << 18) - 1), // Bit ID-17 to ID-0
),
};
(standard_id_part, ide_bit, extended_id_part)
};

split_id(self).cmp(&split_id(other))
}
}

impl PartialOrd for Id {
fn partial_cmp(&self, other: &Id) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}

impl From<StandardId> for Id {
#[inline]
fn from(id: StandardId) -> Self {
Expand Down Expand Up @@ -157,4 +195,15 @@ mod tests {
StandardId::new((ExtendedId::MAX.0 >> 18) as u16)
);
}

#[test]
fn cmp_id() {
assert!(StandardId::ZERO < StandardId::MAX);
assert!(ExtendedId::ZERO < ExtendedId::MAX);

assert!(Id::Standard(StandardId::ZERO) < Id::Extended(ExtendedId::ZERO));
assert!(Id::Extended(ExtendedId::ZERO) < Id::Extended(ExtendedId::MAX));
assert!(Id::Extended(ExtendedId((1 << 11) - 1)) < Id::Standard(StandardId(1)));
assert!(Id::Standard(StandardId(1)) < Id::Extended(ExtendedId::MAX));
}
}