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

Decode breaks when not calling write! #457

Closed
Dirbaio opened this issue Apr 22, 2021 · 6 comments
Closed

Decode breaks when not calling write! #457

Dirbaio opened this issue Apr 22, 2021 · 6 comments
Labels
breaking change fix / feature / improvement involves a breaking change and needs to wait until next minor version difficulty: medium Somewhat difficult to solve priority: high High priority for the Knurling team status: needs PR Issue just needs a Pull Request implementing the changes type: bug Something isn't working
Milestone

Comments

@Dirbaio
Copy link
Contributor

Dirbaio commented Apr 22, 2021

    struct Foo;

    impl defmt::Format for Foo {
        fn format(&self, fmt: Formatter) {}
    }

    defmt::info!("boom: {=?}", Foo);

produces

(HOST) ERROR failed to decode defmt data: [9, 0, 0, 0, 0, 0, 0, 0, 0, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0]

I guess this is somewhat related to #455

@jonas-schievink jonas-schievink added priority: high High priority for the Knurling team type: bug Something isn't working labels Apr 22, 2021
@jonas-schievink jonas-schievink added difficulty: easy Pretty easy to solve status: needs PR Issue just needs a Pull Request implementing the changes difficulty: medium Somewhat difficult to solve and removed difficulty: easy Pretty easy to solve labels May 5, 2021
@jonas-schievink
Copy link
Contributor

This should not be too hard to fix, by remembering whether a Formatter was written to or not, and panicking if a custom Format impl didn't do that

@Dirbaio
Copy link
Contributor Author

Dirbaio commented May 6, 2021

This would increase code size somewhat. Also, making Formatter zero-sized leads to big code size wins (#456), and this would prevent that.

Assuming we can break wire format, I think this can be fixed with a new trait without breaking API (also fixing #455 #458).

  • New trait RawFormat, allows writing the tag and data separately. Impls must guarantee the tag is a constant for a given type.
  • Primitives from the defmt crate impl RawFormat.
  • derive(Format) impls RawFormat since the generated code guarantees the tag is constant.
  • Formats wire format changes to be tag+data, tag+data, tag+data, .., terminator. This would allow Formats to do multiple write!() calls too!
  • [RawFormat]s wire format changes to tag+len+data+data+data... This can't break since RawFormat guarantees the tag is constant.
  • [RawFormat;N]s wire format changes to tag+data+data+data.., similarly.
  • Remove the needs_tag optimization
// This trait would be "public but not really", in defmt::export.
// Not intended to be impl'd by the end user, only by code in `defmt`, and the macros.
// This allows us to change it in the future again if needed.
trait RawFormat {
    fn format_tag() -> u16,
    fn format_data(&self);
}

// The current Format trait stays unchanged
trait Format {
    fn format(&self, fmt: Formatter<'_>);
}

impl<T: Format + ?Sized> RawFormat for T {
    fn format_tag() -> u16 {
        internp!("{=_format}") // some new special tag
    }
    fn format_data(&self) {
       self.format(Formatter::new());
       defmt::export::write_u8(0x00); // some terminator byte
    }
}


impl<T: RawFormat> RawFormat for [T] {
    fn format_tag() -> u16 {
        internp!("{=_slice}") // some new special tag
    }
    fn format_data(&self) {
       defmt::export::write_tag(T::format_tag());
       defmt::export::write_usize(self.len());
       for x in self {
           x.format_data();
       }
    }
}

impl<T: RawFormat, const N: usize> RawFormat for [T;N] { .. etc } 

@jonas-schievink
Copy link
Contributor

That sounds good, I guess we should really focus on allowing breaking changes to the wire format then

@Dirbaio
Copy link
Contributor Author

Dirbaio commented May 6, 2021

Actually that is technically breaking: primitives like u8 will no longer implement Format, only RawFormat. It could break user code that has where T: defmt::Format bounds, but I'm not sure how common that is.

@Dirbaio
Copy link
Contributor Author

Dirbaio commented May 6, 2021

Maybe RawFormat's existence should be part of the public API, but the method contents shouldn't be? Not sure if there's any precedent for that...

User code might want to do where T: defmt::RawFormat if they want to be generic over "anything printable".

@Urhengulas Urhengulas added the breaking change fix / feature / improvement involves a breaking change and needs to wait until next minor version label May 7, 2021
@Urhengulas Urhengulas added this to the v0.3.0 milestone Jun 18, 2021
@japaric
Copy link
Member

japaric commented Jul 7, 2021

this appears to already have been fixed in the main branch (possibly by #508)

@japaric japaric closed this as completed Jul 7, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change fix / feature / improvement involves a breaking change and needs to wait until next minor version difficulty: medium Somewhat difficult to solve priority: high High priority for the Knurling team status: needs PR Issue just needs a Pull Request implementing the changes type: bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants