Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
nyanpasu64 opened this issue Oct 5, 2023 · 1 comment

Comments

@nyanpasu64
Copy link

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
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants