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

Impl of FixedGattValue for &str seems wrong #272

Open
jscatena88 opened this issue Jan 26, 2025 · 1 comment
Open

Impl of FixedGattValue for &str seems wrong #272

jscatena88 opened this issue Jan 26, 2025 · 1 comment

Comments

@jscatena88
Copy link

I was attempting to send a string as a characteristic with the following code:

#[gatt_service(uuid = DEVICE_INFO_SERVICE_UUID)]
struct DeviceInfoService {
    #[characteristic(uuid = "00002a25-0000-1000-8000-00805f9b34fb", value = "1337", read)]
    serial_number: &'static str,
}

I could see the characteristic but it had an odd nonsense value that was not valid utf8

Looking at the code for the impl<T: Primitive> FixedGattValue for T it seems that SIZE is set as mem::size_of::<Self>(); and in the case of T = &str this wont actually be the size of the str slice, just the size of the reference.

Having realized this I switched to trying to use heapless::String (as shown below) and that worked as expected.

#[gatt_service(uuid = DEVICE_INFO_SERVICE_UUID)]
struct DeviceInfoService {
    #[characteristic(uuid = "00002a25-0000-1000-8000-00805f9b34fb", value = heapless::String::from_str("1337").unwrap(), read)]
    serial_number: heapless::String<8>,
}
@jscatena88
Copy link
Author

For completeness here is the current code that seems to be flawed for &str

trait Primitive: Copy {}
impl Primitive for u8 {}
impl Primitive for u16 {}
impl Primitive for u32 {}
impl Primitive for u64 {}
impl Primitive for i8 {}
impl Primitive for i16 {}
impl Primitive for i32 {}
impl Primitive for i64 {}
impl Primitive for f32 {}
impl Primitive for f64 {}
impl Primitive for BluetoothUuid16 {} // ok as this is just a NewType(u16)
impl Primitive for &'_ str {}

impl<T: Primitive> FixedGattValue for T {
    const SIZE: usize = mem::size_of::<Self>();

    fn from_gatt(data: &[u8]) -> Result<Self, FromGattError> {
        if data.len() != Self::SIZE {
            Err(FromGattError::InvalidLength)
        } else {
            // SAFETY
            // - Pointer is considered "valid" as per the rules outlined for validity in std::ptr v1.82.0
            // - Pointer was generated from a slice of bytes matching the size of the type implementing Primitive, and all types implementing Primitive are valid for all possible configurations of bits
            // - Primitive trait is constrained to require Copy
            unsafe { Ok((data.as_ptr() as *const Self).read_unaligned()) }
        }
    }

    fn to_gatt(&self) -> &[u8] {
        // SAFETY
        // - Slice is of type u8 so data is guaranteed valid for reads of any length
        // - Data and len are tied to the address and size of the type
        unsafe { slice::from_raw_parts(self as *const Self as *const u8, Self::SIZE) }
    }
}

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

1 participant