@@ -30,6 +30,7 @@ use anyhow::{anyhow, ensure, Context, Result};
30
30
use can_dbc:: { Message , MultiplexIndicator , Signal , ValDescription , ValueDescription , DBC } ;
31
31
use heck:: { ToPascalCase , ToSnakeCase } ;
32
32
use pad:: PadAdapter ;
33
+ use std:: cmp:: { max, min} ;
33
34
use std:: {
34
35
collections:: { BTreeMap , BTreeSet } ,
35
36
fmt:: Display ,
@@ -261,7 +262,7 @@ fn render_message(mut w: impl Write, config: &Config<'_>, msg: &Message, dbc: &D
261
262
let mut w = PadAdapter :: wrap ( & mut w) ;
262
263
config
263
264
. impl_serde
264
- . fmt_attr ( & mut w, "serde(with = \" serde_bytes\" )" ) ;
265
+ . fmt_attr ( & mut w, "serde(with = \" serde_bytes\" )" ) ? ;
265
266
writeln ! ( w, "raw: [u8; {}]," , msg. message_size( ) ) ?;
266
267
}
267
268
writeln ! ( w, "}}" ) ?;
@@ -1033,59 +1034,127 @@ fn write_enum(
1033
1034
///
1034
1035
/// NOTE: Factor and offset must be whole integers.
1035
1036
fn scaled_signal_to_rust_int ( signal : & Signal ) -> String {
1036
- let sign = match signal. value_type ( ) {
1037
- can_dbc:: ValueType :: Signed => "i" ,
1038
- can_dbc:: ValueType :: Unsigned => "u" ,
1039
- } ;
1040
-
1041
1037
assert ! (
1042
1038
signal. factor. fract( ) . abs( ) <= f64 :: EPSILON ,
1043
1039
"Signal Factor ({}) should be an integer" ,
1044
1040
signal. factor,
1045
1041
) ;
1046
1042
assert ! (
1047
1043
signal. offset. fract( ) . abs( ) <= f64 :: EPSILON ,
1048
- "Signal Factor ({}) should be an integer" ,
1044
+ "Signal Offset ({}) should be an integer" ,
1049
1045
signal. offset,
1050
1046
) ;
1051
1047
1052
- // calculate the maximum possible signal value, accounting for factor and offset
1053
-
1054
- if signal. min >= 0.0 {
1055
- let factor = signal. factor as u64 ;
1056
- let offset = signal. offset as u64 ;
1057
- let max_value = 1u64
1058
- . checked_shl ( * signal. signal_size ( ) as u32 )
1059
- . map ( |n| n. saturating_sub ( 1 ) )
1060
- . and_then ( |n| n. checked_mul ( factor) )
1061
- . and_then ( |n| n. checked_add ( offset) )
1062
- . unwrap_or ( u64:: MAX ) ;
1063
-
1064
- let size = match max_value {
1065
- n if n <= u8:: MAX . into ( ) => "8" ,
1066
- n if n <= u16:: MAX . into ( ) => "16" ,
1067
- n if n <= u32:: MAX . into ( ) => "32" ,
1068
- _ => "64" ,
1048
+ let err = format ! (
1049
+ "Signal {} could not be represented as a Rust integer" ,
1050
+ & signal. name( )
1051
+ ) ;
1052
+ signal_params_to_rust_int (
1053
+ * signal. value_type ( ) ,
1054
+ signal. signal_size as u32 ,
1055
+ signal. factor as i64 ,
1056
+ signal. offset as i64 ,
1057
+ )
1058
+ . expect ( & err)
1059
+ }
1060
+
1061
+ /// Convert the relevant parameters of a `can_dbc::Signal` into a Rust type.
1062
+ fn signal_params_to_rust_int (
1063
+ sign : can_dbc:: ValueType ,
1064
+ signal_size : u32 ,
1065
+ factor : i64 ,
1066
+ offset : i64 ,
1067
+ ) -> Option < String > {
1068
+ if signal_size > 64 {
1069
+ return None ;
1070
+ }
1071
+ let range = get_range_of_values ( sign, signal_size, factor, offset) ;
1072
+ match range {
1073
+ Some ( ( low, high) ) => Some ( range_to_rust_int ( low, high) ) ,
1074
+ _ => None ,
1075
+ }
1076
+ }
1077
+
1078
+ /// Using the signal's parameters, find the range of values that it spans.
1079
+ fn get_range_of_values (
1080
+ sign : can_dbc:: ValueType ,
1081
+ signal_size : u32 ,
1082
+ factor : i64 ,
1083
+ offset : i64 ,
1084
+ ) -> Option < ( i128 , i128 ) > {
1085
+ if signal_size == 0 {
1086
+ return None ;
1087
+ }
1088
+ let low;
1089
+ let high;
1090
+ match sign {
1091
+ can_dbc:: ValueType :: Signed => {
1092
+ low = 1i128
1093
+ . checked_shl ( signal_size. saturating_sub ( 1 ) )
1094
+ . and_then ( |n| n. checked_mul ( -1 ) ) ;
1095
+ high = 1i128
1096
+ . checked_shl ( signal_size. saturating_sub ( 1 ) )
1097
+ . and_then ( |n| n. checked_sub ( 1 ) ) ;
1098
+ }
1099
+ can_dbc:: ValueType :: Unsigned => {
1100
+ low = Some ( 0 ) ;
1101
+ high = 1i128
1102
+ . checked_shl ( signal_size)
1103
+ . and_then ( |n| n. checked_sub ( 1 ) ) ;
1104
+ }
1105
+ }
1106
+ let range1 = apply_factor_and_offset ( low, factor, offset) ;
1107
+ let range2 = apply_factor_and_offset ( high, factor, offset) ;
1108
+ match ( range1, range2) {
1109
+ ( Some ( a) , Some ( b) ) => Some ( ( min ( a, b) , max ( a, b) ) ) ,
1110
+ _ => None ,
1111
+ }
1112
+ }
1113
+
1114
+ fn apply_factor_and_offset ( input : Option < i128 > , factor : i64 , offset : i64 ) -> Option < i128 > {
1115
+ input
1116
+ . and_then ( |n| n. checked_mul ( factor. into ( ) ) )
1117
+ . and_then ( |n| n. checked_add ( offset. into ( ) ) )
1118
+ }
1119
+
1120
+ /// Determine the smallest Rust integer type that can fit the range of values
1121
+ /// Only values derived from 64 bit integers are supported, i.e. the range [-2^64-1, 2^64-1]
1122
+ fn range_to_rust_int ( low : i128 , high : i128 ) -> String {
1123
+ let lower_bound: u8 ;
1124
+ let upper_bound: u8 ;
1125
+ let sign: & str ;
1126
+
1127
+ if low < 0 {
1128
+ // signed case
1129
+ sign = "i" ;
1130
+ lower_bound = match low {
1131
+ n if n >= i8:: MIN . into ( ) => 8 ,
1132
+ n if n >= i16:: MIN . into ( ) => 16 ,
1133
+ n if n >= i32:: MIN . into ( ) => 32 ,
1134
+ n if n >= i64:: MIN . into ( ) => 64 ,
1135
+ _ => 128 ,
1136
+ } ;
1137
+ upper_bound = match high {
1138
+ n if n <= i8:: MAX . into ( ) => 8 ,
1139
+ n if n <= i16:: MAX . into ( ) => 16 ,
1140
+ n if n <= i32:: MAX . into ( ) => 32 ,
1141
+ n if n <= i64:: MAX . into ( ) => 64 ,
1142
+ _ => 128 ,
1069
1143
} ;
1070
- format ! ( "{sign}{size}" )
1071
1144
} else {
1072
- let factor = signal. factor as i64 ;
1073
- let offset = signal. offset as i64 ;
1074
- let max_value = 1i64
1075
- . checked_shl ( * signal. signal_size ( ) as u32 )
1076
- . map ( |n| n. saturating_sub ( 1 ) )
1077
- . and_then ( |n| n. checked_mul ( factor) )
1078
- . and_then ( |n| n. checked_add ( offset) )
1079
- . unwrap_or ( i64:: MAX ) ;
1080
-
1081
- let size = match max_value {
1082
- n if n <= i8:: MAX . into ( ) => "8" ,
1083
- n if n <= i16:: MAX . into ( ) => "16" ,
1084
- n if n <= i32:: MAX . into ( ) => "32" ,
1085
- _ => "64" ,
1145
+ sign = "u" ;
1146
+ lower_bound = 8 ;
1147
+ upper_bound = match high {
1148
+ n if n <= u8:: MAX . into ( ) => 8 ,
1149
+ n if n <= u16:: MAX . into ( ) => 16 ,
1150
+ n if n <= u32:: MAX . into ( ) => 32 ,
1151
+ n if n <= u64:: MAX . into ( ) => 64 ,
1152
+ _ => 128 ,
1086
1153
} ;
1087
- format ! ( "i{size}" )
1088
1154
}
1155
+
1156
+ let size = max ( lower_bound, upper_bound) ;
1157
+ format ! ( "{sign}{size}" )
1089
1158
}
1090
1159
1091
1160
/// Determine the smallest rust integer that can fit the raw signal values.
@@ -1507,3 +1576,59 @@ impl FeatureConfig<'_> {
1507
1576
f ( & mut w)
1508
1577
}
1509
1578
}
1579
+
1580
+ #[ cfg( test) ]
1581
+ mod tests {
1582
+ use crate :: { get_range_of_values, range_to_rust_int, signal_params_to_rust_int} ;
1583
+ use can_dbc:: ValueType :: { Signed , Unsigned } ;
1584
+
1585
+ #[ test]
1586
+ fn test_range_of_values ( ) {
1587
+ assert_eq ! ( get_range_of_values( Unsigned , 4 , 1 , 0 ) , Some ( ( 0 , 15 ) ) ) ;
1588
+ assert_eq ! (
1589
+ get_range_of_values( Unsigned , 32 , -1 , 0 ) ,
1590
+ Some ( ( -( u32 :: MAX as i128 ) , 0 ) )
1591
+ ) ;
1592
+ assert_eq ! (
1593
+ get_range_of_values( Unsigned , 12 , 1 , -1000 ) ,
1594
+ Some ( ( -1000 , 3095 ) )
1595
+ ) ;
1596
+ }
1597
+
1598
+ #[ test]
1599
+ fn test_range_0_signal_size ( ) {
1600
+ assert_eq ! (
1601
+ get_range_of_values( Signed , 0 , 1 , 0 ) ,
1602
+ None ,
1603
+ "0 bit signal should be invalid"
1604
+ ) ;
1605
+ }
1606
+
1607
+ #[ test]
1608
+ fn test_range_to_rust_int ( ) {
1609
+ assert_eq ! ( range_to_rust_int( 0 , 255 ) , "u8" ) ;
1610
+ assert_eq ! ( range_to_rust_int( -1 , 127 ) , "i8" ) ;
1611
+ assert_eq ! ( range_to_rust_int( -1 , 128 ) , "i16" ) ;
1612
+ assert_eq ! ( range_to_rust_int( -1 , 255 ) , "i16" ) ;
1613
+ assert_eq ! ( range_to_rust_int( -65535 , 0 ) , "i32" ) ;
1614
+ assert_eq ! ( range_to_rust_int( -129 , -127 ) , "i16" ) ;
1615
+ assert_eq ! ( range_to_rust_int( 0 , 1i128 << 65 ) , "u128" ) ;
1616
+ assert_eq ! ( range_to_rust_int( -( 1i128 << 65 ) , 0 ) , "i128" ) ;
1617
+ }
1618
+
1619
+ #[ test]
1620
+ fn test_convert_signal_params_to_rust_int ( ) {
1621
+ assert_eq ! ( signal_params_to_rust_int( Signed , 8 , 1 , 0 ) . unwrap( ) , "i8" ) ;
1622
+ assert_eq ! ( signal_params_to_rust_int( Signed , 8 , 2 , 0 ) . unwrap( ) , "i16" ) ;
1623
+ assert_eq ! ( signal_params_to_rust_int( Signed , 63 , 1 , 0 ) . unwrap( ) , "i64" ) ;
1624
+ assert_eq ! (
1625
+ signal_params_to_rust_int( Unsigned , 64 , -1 , 0 ) . unwrap( ) ,
1626
+ "i128"
1627
+ ) ;
1628
+ assert_eq ! (
1629
+ signal_params_to_rust_int( Unsigned , 65 , 1 , 0 ) ,
1630
+ None ,
1631
+ "This shouldn't be valid in a DBC, it's more than 64 bits"
1632
+ ) ;
1633
+ }
1634
+ }
0 commit comments