@@ -30,6 +30,7 @@ use anyhow::{anyhow, ensure, Context, Result};
3030use can_dbc:: { Message , MultiplexIndicator , Signal , ValDescription , ValueDescription , DBC } ;
3131use heck:: { ToPascalCase , ToSnakeCase } ;
3232use pad:: PadAdapter ;
33+ use std:: cmp:: { max, min} ;
3334use std:: {
3435 collections:: { BTreeMap , BTreeSet } ,
3536 fmt:: Display ,
@@ -261,7 +262,7 @@ fn render_message(mut w: impl Write, config: &Config<'_>, msg: &Message, dbc: &D
261262 let mut w = PadAdapter :: wrap ( & mut w) ;
262263 config
263264 . impl_serde
264- . fmt_attr ( & mut w, "serde(with = \" serde_bytes\" )" ) ;
265+ . fmt_attr ( & mut w, "serde(with = \" serde_bytes\" )" ) ? ;
265266 writeln ! ( w, "raw: [u8; {}]," , msg. message_size( ) ) ?;
266267 }
267268 writeln ! ( w, "}}" ) ?;
@@ -1033,59 +1034,127 @@ fn write_enum(
10331034///
10341035/// NOTE: Factor and offset must be whole integers.
10351036fn 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-
10411037 assert ! (
10421038 signal. factor. fract( ) . abs( ) <= f64 :: EPSILON ,
10431039 "Signal Factor ({}) should be an integer" ,
10441040 signal. factor,
10451041 ) ;
10461042 assert ! (
10471043 signal. offset. fract( ) . abs( ) <= f64 :: EPSILON ,
1048- "Signal Factor ({}) should be an integer" ,
1044+ "Signal Offset ({}) should be an integer" ,
10491045 signal. offset,
10501046 ) ;
10511047
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 ,
10691143 } ;
1070- format ! ( "{sign}{size}" )
10711144 } 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 ,
10861153 } ;
1087- format ! ( "i{size}" )
10881154 }
1155+
1156+ let size = max ( lower_bound, upper_bound) ;
1157+ format ! ( "{sign}{size}" )
10891158}
10901159
10911160/// Determine the smallest rust integer that can fit the raw signal values.
@@ -1507,3 +1576,59 @@ impl FeatureConfig<'_> {
15071576 f ( & mut w)
15081577 }
15091578}
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