Skip to content

[WIP] ctutils: automatically use optimized 1-byte CtEq#1369

Closed
tarcieri wants to merge 1 commit into
masterfrom
ctutils/auto-optimized-1-byte-ct-eq
Closed

[WIP] ctutils: automatically use optimized 1-byte CtEq#1369
tarcieri wants to merge 1 commit into
masterfrom
ctutils/auto-optimized-1-byte-ct-eq

Conversation

@tarcieri

Copy link
Copy Markdown
Member

As an alternative to the BytesCtEq approach to using the optimized impl of CmovEq for [u8], this implementation automatically uses it for all 1-byte types when the CtEq impl for [T] is invoked (or anything that calls it, like the impls on [T; N], Box<[T]>, and Vec<T>.

To ensure we're not casting from a slice of a type containing uninitialized memory to [u8], this bounds all such CtEq impls on a newly introduced unsafe trait NoUninit, which is currently not exposed in the public API except through the bounds. The trait has been impl'd for all of the types we impl other traits for in this crate.

@tarcieri tarcieri force-pushed the ctutils/auto-optimized-1-byte-ct-eq branch from c4536aa to 62bb328 Compare January 17, 2026 02:22
As an alternative to the `BytesCtEq` approach to using the optimized
impl of `CmovEq` for `[u8]`, this implementation automatically uses it
for all 1-byte types when the `CtEq` impl for `[T]` is invoked
(or anything that calls it, like the impls on `[T; N]`, `Box<[T]>`, and
`Vec<T>`.

To ensure we're not casting from a slice of a type containing
uninitialized memory to `[u8]`, this bounds all such `CtEq` impls on a
newly introduced `unsafe trait NoUninit`, which is currently not exposed
in the public API except through the bounds. The trait has been impl'd
for all of the types we impl other traits for in this crate.
@tarcieri tarcieri force-pushed the ctutils/auto-optimized-1-byte-ct-eq branch from 62bb328 to bcc517f Compare January 17, 2026 02:36
#[cfg(feature = "alloc")]
unsafe impl<T: NoUninit> NoUninit for Box<T> {}
#[cfg(feature = "alloc")]
unsafe impl<T: NoUninit> NoUninit for Vec<T> {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This impl is not sound, vec does not have layout guarantees and there could be padding under -Zrandomize-layout for example

unsafe impl<T: NoUninit, const N: usize> NoUninit for [T; N] {}

#[cfg(feature = "alloc")]
unsafe impl<T: NoUninit> NoUninit for Box<T> {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

For Box it should be fine since I think it's guaranteed that it's just a pointer, but you don't even need the T: NoUninit bound here :). the implicit T: Sized matters though since fat pointer layout isn't guaranteed

@Noratrieb

Copy link
Copy Markdown

I would probably recommend copying the impls from bytemuck where I'm pretty sure everything is correct.

@tarcieri

tarcieri commented Jan 17, 2026

Copy link
Copy Markdown
Member Author

I'm going to go a completely different direction with this.

cmov now has optimized impls not just for 1-byte sized types, but for pretty much all of the small core integer types (e.g. i16/u16, and can coalesce u32 on 64-bit targets)

Since this approach restricts the types that can be used with the impl anyway, we can just delegate to cmov a type-at-a-type with no generic/blanket impl whatsoever and therefore no typecasting, which should both provide better performance for more types and leave things open for downstream impls to add theirs where they don't conflict.

@tarcieri tarcieri closed this Jan 17, 2026
@tarcieri tarcieri deleted the ctutils/auto-optimized-1-byte-ct-eq branch January 17, 2026 14:09
@tarcieri

Copy link
Copy Markdown
Member Author

In case anyone is curious, here's the alternative approach which avoids (any additional) unsafe code entirely: #1376

Instead of having a generic implementation that tries to use unsafe casting to specialize internally, it removes the generic implementation and just has a bunch of optimized implementations for concrete types.

Perhaps in a future with specialization, we could eventually add the generic impls back, but in the meantime they have been retained as generic free functions users can use to write their own trait impls for concrete types.

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.

2 participants