Skip to content

Commit 9273f4a

Browse files
committed
handle more edge cases, use i128 as the maximum type
1 parent 8e5adfc commit 9273f4a

File tree

1 file changed

+144
-52
lines changed

1 file changed

+144
-52
lines changed

src/lib.rs

Lines changed: 144 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,21 @@
2727
#![deny(clippy::arithmetic_side_effects)]
2828

2929
use anyhow::{anyhow, ensure, Context, Result};
30-
use can_dbc::{Message, MultiplexIndicator, Signal, ValDescription, ValueDescription, DBC};
30+
use can_dbc::ValueType::Signed;
31+
use can_dbc::{
32+
Message, MultiplexIndicator, Signal, ValDescription, ValueDescription, ValueType, DBC,
33+
};
3134
use heck::{ToPascalCase, ToSnakeCase};
3235
use pad::PadAdapter;
36+
use std::cmp::{max, min};
3337
use std::{
3438
collections::{BTreeMap, BTreeSet},
3539
fmt::Display,
40+
i128, i64,
3641
io::{self, BufWriter, Write},
3742
};
38-
use std::cmp::max;
3943
use typed_builder::TypedBuilder;
4044

41-
4245
mod includes;
4346
mod keywords;
4447
mod pad;
@@ -1035,11 +1038,6 @@ fn write_enum(
10351038
///
10361039
/// NOTE: Factor and offset must be whole integers.
10371040
fn scaled_signal_to_rust_int(signal: &Signal) -> String {
1038-
let sign = match signal.value_type() {
1039-
can_dbc::ValueType::Signed => "i",
1040-
can_dbc::ValueType::Unsigned => "u",
1041-
};
1042-
10431041
assert!(
10441042
signal.factor.fract().abs() <= f64::EPSILON,
10451043
"Signal Factor ({}) should be an integer",
@@ -1052,49 +1050,120 @@ fn scaled_signal_to_rust_int(signal: &Signal) -> String {
10521050
);
10531051

10541052
// calculate the maximum possible signal value, accounting for factor and offset
1053+
signal_params_to_rust_int(
1054+
*signal.value_type(),
1055+
signal.signal_size as u32,
1056+
signal.factor as i64,
1057+
signal.offset as i64,
1058+
)
1059+
.expect(&format!(
1060+
"Signal {} could not be represented as a Rust integer",
1061+
&signal.name()
1062+
))
1063+
}
1064+
1065+
fn signal_params_to_rust_int(
1066+
sign: ValueType,
1067+
signal_size: u32,
1068+
factor: i64,
1069+
offset: i64,
1070+
) -> Option<String> {
1071+
if signal_size > 64 {
1072+
return None;
1073+
}
1074+
let range = get_range_of_values(sign, signal_size, factor, offset);
1075+
match range {
1076+
Some((low, high)) => Some(range_to_rust_int(low, high)),
1077+
_ => None,
1078+
}
1079+
}
1080+
1081+
/// Using the signal's parameters, find the range of values that it spans
1082+
fn get_range_of_values(
1083+
sign: ValueType,
1084+
signal_size: u32,
1085+
factor: i64,
1086+
offset: i64,
1087+
) -> Option<(i128, i128)> {
1088+
let range1;
1089+
let range2;
1090+
match sign {
1091+
Signed => {
1092+
range1 = 1i128
1093+
.checked_shl(signal_size - 1)
1094+
.and_then(|n| n.checked_mul(-1))
1095+
.and_then(|n| n.checked_mul(factor.into()))
1096+
.and_then(|n| n.checked_add(offset.into()));
1097+
range2 = 1i128
1098+
.checked_shl(signal_size - 1)
1099+
.and_then(|n| n.checked_sub(1))
1100+
.and_then(|n| n.checked_mul(factor.into()))
1101+
.and_then(|n| n.checked_add(offset.into()));
1102+
}
1103+
ValueType::Unsigned => {
1104+
range1 = Some(0);
1105+
range2 = 1i128
1106+
.checked_shl(signal_size)
1107+
.and_then(|n| n.checked_sub(1))
1108+
.and_then(|n| n.checked_mul(factor.into()))
1109+
.and_then(|n| n.checked_add(offset.into()));
1110+
}
1111+
}
1112+
match (range1, range2) {
1113+
(Some(a), Some(b)) => Some((min(a, b), max(a, b))),
1114+
_ => None,
1115+
}
1116+
}
1117+
1118+
fn apply_factor_and_offset(input: Option<i64>, factor: i64, offset: i64) -> Option<i64> {
1119+
input
1120+
.and_then(|n| n.checked_mul(factor))
1121+
.and_then(|n| n.checked_add(offset))
1122+
}
10551123

1056-
if signal.min >= 0.0 {
1057-
let factor = signal.factor as u64;
1058-
let offset = signal.offset as u64;
1059-
let max_value = 1u64
1060-
.checked_shl(*signal.signal_size() as u32)
1061-
.map(|n| n.saturating_sub(1))
1062-
.and_then(|n| n.checked_mul(factor))
1063-
.and_then(|n| n.checked_add(offset))
1064-
.unwrap_or(u64::MAX);
1065-
1066-
let size = match max_value {
1067-
n if n <= u8::MAX.into() => "8",
1068-
n if n <= u16::MAX.into() => "16",
1069-
n if n <= u32::MAX.into() => "32",
1070-
_ => "64",
1124+
/// Determine the smallest Rust integer type that can fit the range of values
1125+
/// Only values derived from 64 bit integers are supported, i.e. the range [-2^64-1, 2^64-1]
1126+
fn range_to_rust_int(low: i128, high: i128) -> String {
1127+
// Two cases:
1128+
// Min is negative, in which case lower and upper bounds are signed
1129+
// Min is positive so lower/upper bounds are unsigned
1130+
let lower_bound: u8;
1131+
let upper_bound: u8;
1132+
let sign: &str;
1133+
1134+
if low < 0 {
1135+
// signed case
1136+
sign = "i";
1137+
lower_bound = match low {
1138+
n if n >= i8::MIN.into() => 8,
1139+
n if n >= i16::MIN.into() => 16,
1140+
n if n >= i32::MIN.into() => 32,
1141+
n if n >= i64::MIN.into() => 64,
1142+
_ => 128,
1143+
};
1144+
upper_bound = match high {
1145+
n if n <= i8::MAX.into() => 8,
1146+
n if n <= i16::MAX.into() => 16,
1147+
n if n <= i32::MAX.into() => 32,
1148+
n if n <= i64::MAX.into() => 64,
1149+
_ => 128,
10711150
};
1072-
format!("{sign}{size}")
10731151
} else {
1074-
let factor = signal.factor as i64;
1075-
let offset = signal.offset as i64;
1076-
let max_value = 1i64
1077-
.checked_shl(*signal.signal_size() as u32)
1078-
.map(|n| n.saturating_sub(1))
1079-
.and_then(|n| n.checked_mul(factor))
1080-
.and_then(|n| n.checked_add(offset))
1081-
.unwrap_or(i64::MAX);
1082-
1083-
let size = match max_value {
1084-
n if n <= i8::MAX.into() => "8",
1085-
n if n <= i16::MAX.into() => "16",
1086-
n if n <= i32::MAX.into() => "32",
1087-
_ => "64",
1152+
sign = "u";
1153+
lower_bound = 8;
1154+
upper_bound = match high {
1155+
n if n <= u8::MAX.into() => 8,
1156+
n if n <= u16::MAX.into() => 16,
1157+
n if n <= u32::MAX.into() => 32,
1158+
n if n <= u64::MAX.into() => 64,
1159+
_ => 128,
10881160
};
1089-
format!("i{size}")
10901161
}
1091-
}
10921162

1093-
fn signal_params_to_rust_int(signal_size: u32, factor: i64, offset: i64) -> String {
1094-
String::from("u8")
1163+
let size = max(lower_bound, upper_bound);
1164+
format!("{sign}{size}")
10951165
}
10961166

1097-
10981167
/// Determine the smallest rust integer that can fit the raw signal values.
10991168
fn signal_to_rust_int(signal: &Signal) -> String {
11001169
let sign = match signal.value_type() {
@@ -1124,7 +1193,6 @@ fn signal_to_rust_uint(signal: &Signal) -> String {
11241193
format!("u{}", size)
11251194
}
11261195

1127-
11281196
#[allow(clippy::float_cmp)]
11291197
fn signal_is_float_in_rust(signal: &Signal) -> bool {
11301198
signal.offset.fract() != 0.0 || signal.factor.fract() != 0.0
@@ -1518,19 +1586,43 @@ impl FeatureConfig<'_> {
15181586

15191587
#[cfg(test)]
15201588
mod tests {
1521-
use crate::signal_params_to_rust_int;
1589+
use crate::{get_range_of_values, range_to_rust_int, signal_params_to_rust_int};
1590+
use can_dbc::ValueType::{Signed, Unsigned};
15221591

15231592
#[test]
1524-
fn test_something() {
1525-
// TODO drop
1526-
assert_eq!(1, 1);
1593+
fn test_range_of_values() {
1594+
assert_eq!(get_range_of_values(Unsigned, 4, 1, 0), Some((0, 15)));
1595+
assert_eq!(
1596+
get_range_of_values(Unsigned, 32, -1, 0),
1597+
Some((-(u32::MAX as i128), 0))
1598+
);
15271599
}
15281600

15291601
#[test]
1530-
fn test_convert_signal_params_to_rust_int(){
1531-
assert_eq!(signal_params_to_rust_int(8, 1, 0), "u8");
1532-
assert_eq!(signal_params_to_rust_int(8, 2, 0), "u16");
1533-
1602+
fn test_range_to_rust_int() {
1603+
assert_eq!(range_to_rust_int(0, 255), "u8");
1604+
assert_eq!(range_to_rust_int(-1, 127), "i8");
1605+
assert_eq!(range_to_rust_int(-1, 128), "i16");
1606+
assert_eq!(range_to_rust_int(-1, 255), "i16");
1607+
assert_eq!(range_to_rust_int(-65535, 0), "i32");
1608+
assert_eq!(range_to_rust_int(-129, -127), "i16");
1609+
assert_eq!(range_to_rust_int(0, 1i128 << 65), "u128");
1610+
assert_eq!(range_to_rust_int(-(1i128 << 65), 0), "i128");
15341611
}
15351612

1613+
#[test]
1614+
fn test_convert_signal_params_to_rust_int() {
1615+
assert_eq!(signal_params_to_rust_int(Signed, 8, 1, 0).unwrap(), "i8");
1616+
assert_eq!(signal_params_to_rust_int(Signed, 8, 2, 0).unwrap(), "i16");
1617+
assert_eq!(signal_params_to_rust_int(Signed, 63, 1, 0).unwrap(), "i64");
1618+
assert_eq!(
1619+
signal_params_to_rust_int(Unsigned, 64, -1, 0).unwrap(),
1620+
"i128"
1621+
);
1622+
assert_eq!(
1623+
signal_params_to_rust_int(Unsigned, 65, 1, 0),
1624+
None,
1625+
"This shouldn't be valid in a DBC, it's more than 64 bits"
1626+
);
1627+
}
15361628
}

0 commit comments

Comments
 (0)