Hello!
I'm looking for a better (more idiomatic) way to implement a method that converts raw bytes into a typed value. This conversion depends on known type information (and data type size) and endianness. In my implementation I have a struct that holds the endianness, type information and has access to the raw bytes, for example something like this:
struct MyData {
pub typename: CTypes,
pub endianness: Endianness,
pub bytes: [u8; 8], // for illustration purposes. real implementation reads bytes from a buffer
}
// supporting enums
enum CTypes { UInt8, UInt16, UInt32, Int8, Int16, Int32, Float32, Float64 };
enum Endianness { Big, Little, Native };
I have an enum to hold the typed value:
enum CValue{
UInt8(u8),
UInt16(u16),
Uint32(u32),
Uint64(u64),
Int8(i8),
Int16(i16),
int32(i32),
int64(i64),
Float32(f32),
Float64(f64),
}
The get()
method below does the conversion of a &[u8]
slice into the appropriate typed value. My current implementation is defined something like the following. IMO this implementation feels clunky and ugly(?). You can see that the same basic code is written three time, one for each of the Endianness
options, then in each of those 3 basic "copies", there's the conversion of bytes to a typed value. I wonder if there's a more idiomatic / concise way of expressing the same functionality. Any ideas or thoughts?
pub fn get(&self) -> Option<CValue> {
let size = self.typename.type_size(); // returns number of bytes in the given type
let buf = &self.bytes[0..size];
let val = match self.endianness {
Endianness::Big => match self.typename {
CTypes::UInt8 => CValue::UInt8(u8::from_be_bytes(buf.try_into().unwrap())),
CTypes::UInt16 => CValue::UInt16(u16::from_be_bytes(buf.try_into().unwrap())),
CTypes::UInt32 => CValue::UInt32(u32::from_be_bytes(buf.try_into().unwrap())),
CTypes::Int8 => CValue::Int8(i8::from_be_bytes(buf.try_into().unwrap())),
CTypes::Int16 => CValue::Int16(i16::from_be_bytes(buf.try_into().unwrap())),
CTypes::Int32 => CValue::Int32(i32::from_be_bytes(buf.try_into().unwrap())),
// ...and so on
}
Endianness:Little => match self.typename {
CTypes::UInt8 => CValue::UInt8(u8::from_le_bytes(buf.try_into().unwrap())),
CTypes::UInt16 => CValue::UInt16(u16::from_le_bytes(buf.try_into().unwrap())),
CTypes::UInt32 => CValue::UInt32(u32::from_le_bytes(buf.try_into().unwrap())),
CTypes::Int8 => CValue::Int8(i8::from_le_bytes(buf.try_into().unwrap())),
CTypes::Int16 => CValue::Int16(i16::from_le_bytes(buf.try_into().unwrap())),
CTypes::Int32 => CValue::Int32(i32::from_le_bytes(buf.try_into().unwrap())),
// ...and so on
},
// for Native endianness, use `u8::from_ne_bytes()`, `u16::from_ne_bytes()`, etc
};
Some(val)
}
Thanks!
Edit: code indentation