rust-embedded / rust-i2cdev

Rust library for interfacing with i2c devices under Linux
Apache License 2.0
207 stars 54 forks source link

Make smbus_read_i2c_block_data() write into &mut Vec/[u8] rather than returning new Vec? #82

Open nyanpasu64 opened 1 year ago

nyanpasu64 commented 1 year ago

It may be worthwhile for smbus_read_i2c_block_data() and similar functions to write data into a preallocated buffer (of fixed size &mut [u8], or allowing the function to resize a &mut [u8]), rather than returning a new Vec every time. This would save allocations, but would probably be an insignificant micro-optimization (unless you were running in a no-alloc environment or similar).

JaredDyreson commented 1 year ago

I took a look on how to implement this for the i2c_smbus_read_i2c_block_data function in src/ffi.rs and I came up with this:

pub fn i2c_smbus_read_i2c_block_data_inplace(
    fd: RawFd,
    register: u8,
    block: &mut [u8],
) -> Result<usize, I2CError> {
    let mut data = i2c_smbus_data::empty();
    unsafe {
        i2c_smbus_access(
            fd,
            I2CSMBusReadWrite::I2C_SMBUS_READ,
            register,
            I2CSMBusSize::I2C_SMBUS_BLOCK_DATA,
            &mut data,
        )?;
    }

    let bytes_available = data.block[0] as usize;

    let bytes_written = if bytes_available >= block.len() {
        // since this is a &mut [u8], we cannot resize it
        // and we just override the entire container
        block.len()
    } else {
        // otherwise, we write the amount of bytes fetched from the line
        bytes_available
    };

    // zip allows us to be bounded by the shortest iterator, 
    // therefore we will never overrun the buffer
    data.block[0..bytes_available]
        .iter()
        .zip(block.into_iter())
        .for_each(|(source, destination)| {
            *destination = *source;
        });

    // similar to the `std::io::Write::read` method
    Ok(bytes_written)
}

If the caller provides a &mut [u8], then it should be assumed that the starting position of writing is at the beginning and no resizing can be done.

This would still allow for a no-alloc environment, as both the i2c_smbus_data and block would have a known size at compile time.