Skip to content

Conversation

francisbr
Copy link

@francisbr francisbr commented Mar 27, 2025

Following the latest major release of Bincode, the crate now relies on its own traits (Encode/Decode) it switched away from serde's De/Serialize. This PR is to add a feature flag here to optionally implement these traits on the Uuid structs.

The 2.0 bincode release: https://github.com/bincode-org/bincode/releases/tag/v2.0.0

@francisbr francisbr force-pushed the bincode branch 2 times, most recently from e619423 to 2d40b0f Compare March 27, 2025 18:48
@francisbr francisbr force-pushed the bincode branch 2 times, most recently from 168e645 to de93dbe Compare March 28, 2025 01:15
Copy link
Member

@KodrAus KodrAus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this @francisbr! As much as I'd love to fix the redundant length bytes, we need to maintain compatibility with the old serde and bincode 1.x format. If the new bincode derive macros have a similar concept to serde's #[with] for customizing serialization then we could add support for the more compact format that way.

Cargo.toml Outdated
readme = "README.md"
repository = "https://github.com/uuid-rs/uuid"
version = "1.16.0" # remember to update html_root_url in lib.rs
version = "1.16.1" # remember to update html_root_url in lib.rs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a script that updates this so we can leave it as 1.16.0 here. I’ll end up releasing it as 1.17.0

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, reverted!


impl Encode for Uuid {
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
Encode::encode(self.as_bytes(), encoder)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, in order to maintain the original serde 1.x + bincode 1.x encoding, we'll need to include the redundant length bytes here too. Here's an example original format UUID we can use to test equivalence:

[16, 0, 0, 0, 0, 0, 0, 0, 232, 18, 203, 187, 159, 207, 72, 46, 151, 197, 95, 187, 59, 106, 126, 5]

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not 100% sure I understand how that was working. The example you gave me is 24 bytes (more than the 16 or 17 with the length byte).

I added a check in the encode/decode function to check if the config passed is legacy. If so I simply prepend the '16' byte before the UUID. Is that what is needed?
You can see the test_legacy() test I added, when passing config::legacy() to the functions, 17 bytes are encoded/decoded.

Sorry if that is not what is needed and I missunderstood.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I thought our original encoding gave us a 1 byte length, but running bincode::serialize on 1.x gave me what appeared to be a usize length prefix

KodrAus
KodrAus previously approved these changes Apr 9, 2025
Copy link
Member

@KodrAus KodrAus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @francisbr! This looks good to me.

@KodrAus
Copy link
Member

KodrAus commented Apr 11, 2025

Looks like we've got a few build failures here:

failures:

---- external::bincode_support::bincode_tests::test_decode_failure stdout ----

thread 'external::bincode_support::bincode_tests::test_decode_failure' panicked at src/external/bincode_support.rs:221:18:
Unexpected error
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- external::bincode_support::bincode_tests::test_decode_non_nil_uuid stdout ----

thread 'external::bincode_support::bincode_tests::test_decode_non_nil_uuid' panicked at src/external/bincode_support.rs:239:9:
assertion `left == right` failed
  left: 16
 right: 17

Comment on lines +41 to +69
impl Encode for NonNilUuid {
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
self.get().encode(encoder)
}
}

impl Encode for Hyphenated {
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
self.as_uuid().encode(encoder)
}
}

impl Encode for Simple {
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
self.as_uuid().encode(encoder)
}
}

impl Encode for Urn {
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
self.as_uuid().encode(encoder)
}
}

impl Encode for Braced {
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
self.as_uuid().encode(encoder)
}
}
Copy link

@loarca loarca May 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't these differ from the serde impl?

serde would output strings for these, therefore I believe bincode v1 encodes a string rather than the bytes.

So, if we care that the Uuid serde impl is the same as the bincode one, these should encode the string representations

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah well spotted! We definitely should maintain compatibility on these adapter types too

Comment on lines +29 to +37
if encoder.config().int_encoding() == IntEncoding::Fixed {
usize::encode(&16, encoder)?;
} else {
u8::encode(&16, encoder)?;
}

<&[u8; 16]>::encode(&self.as_bytes(), encoder)?;

Ok(())
Copy link

@loarca loarca May 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is sufficient and delegates the int encoding implementation to bincode itself

Suggested change
if encoder.config().int_encoding() == IntEncoding::Fixed {
usize::encode(&16, encoder)?;
} else {
u8::encode(&16, encoder)?;
}
<&[u8; 16]>::encode(&self.as_bytes(), encoder)?;
Ok(())
<[u8]>::encode(self.as_bytes().as_slice(), encoder)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaning on the impl provided by bincode itself seems like a good idea. That way we should naturally respect any new options it introduces that might affect encoding that end users may want.

}

#[cfg(test)]
mod bincode_tests {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides testing the roundtrip of encode->decode, we should probably also test the output binary representation of encode. Just to ensure we know what to expect from a Uuid and its variants (Simple, etc)

@KodrAus KodrAus dismissed their stale review May 14, 2025 21:06

Found some incompatibilities that we need to fix up

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

Successfully merging this pull request may close these issues.

3 participants