Skip to content

Commit 3c47d82

Browse files
authored
(Backport) Fix OptionVarULE in zerovec to also use C, packed (#5144)
Backport of #5143
1 parent 6336fc8 commit 3c47d82

File tree

9 files changed

+20
-19
lines changed

9 files changed

+20
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- `zerovec`
1717
- (0.10.3) Fix size regression by making `twox-hash` dep `no_std` (https://github.com/unicode-org/icu4x/pull/5007)
1818
- (0.10.3) Enforce C,packed, not just packed, on ULE types, fixing for incoming changes to `repr(Rust)` (https://github.com/unicode-org/icu4x/pull/5049)
19+
- (0.10.4) Enforce C,packed on OptionVarULE (https://github.com/unicode-org/icu4x/pull/5143)
1920
- `zerovec_derive`
2021
- (0.10.3) Enforce C,packed, not just packed, on ULE types, fixing for incoming changes to `repr(Rust)` (https://github.com/unicode-org/icu4x/pull/5049)
2122
## icu4x 1.5 (May 28, 2024)

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

utils/zerovec/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[package]
66
name = "zerovec"
77
description = "Zero-copy vector backed by a byte array"
8-
version = "0.10.3"
8+
version = "0.10.4"
99
categories = ["rust-patterns", "memory-management", "caching", "no-std", "data-structures"]
1010
keywords = ["zerocopy", "serialization", "zero-copy", "serde"]
1111

utils/zerovec/derive/src/ule.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ pub fn derive_impl(input: &DeriveInput) -> TokenStream2 {
4848

4949
// Safety (based on the safety checklist on the ULE trait):
5050
// 1. #name does not include any uninitialized or padding bytes.
51-
// (achieved by enforcing #[repr(transparent)] or #[repr(packed)] on a struct of only ULE types)
51+
// (achieved by enforcing #[repr(transparent)] or #[repr(C, packed)] on a struct of only ULE types)
5252
// 2. #name is aligned to 1 byte.
53-
// (achieved by enforcing #[repr(transparent)] or #[repr(packed)] on a struct of only ULE types)
53+
// (achieved by enforcing #[repr(transparent)] or #[repr(C, packed)] on a struct of only ULE types)
5454
// 3. The impl of validate_byte_slice() returns an error if any byte is not valid.
5555
// 4. The impl of validate_byte_slice() returns an error if there are extra bytes.
5656
// 5. The other ULE methods use the default impl.

utils/zerovec/derive/src/varule.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ pub fn derive_impl(
8888

8989
// Safety (based on the safety checklist on the ULE trait):
9090
// 1. #name does not include any uninitialized or padding bytes
91-
// (achieved by enforcing #[repr(transparent)] or #[repr(packed)] on a struct of only ULE types)
91+
// (achieved by enforcing #[repr(transparent)] or #[repr(C, packed)] on a struct of only ULE types)
9292
// 2. #name is aligned to 1 byte.
93-
// (achieved by enforcing #[repr(transparent)] or #[repr(packed)] on a struct of only ULE types)
93+
// (achieved by enforcing #[repr(transparent)] or #[repr(C, packed)] on a struct of only ULE types)
9494
// 3. The impl of `validate_byte_slice()` returns an error if any byte is not valid.
9595
// 4. The impl of `validate_byte_slice()` returns an error if the slice cannot be used in its entirety
9696
// 5. The impl of `from_byte_slice_unchecked()` returns a reference to the same data.

utils/zerovec/design_doc.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ where
182182

183183
The trait is `unsafe` to implement since `ZeroVec` will rely on invariants promised by the trait. The main feature here is that this trait lets `ZeroVec` take a bytestream it is decoding and certify that it contains valid `Self` types. It allows `ZeroVec<T>` to turn `&[u8]` into `&[T::ULE]` during parsing or deserialization.
184184

185-
As `ULE` requires types to not have any alignment restrictions, most `ULE` types will be `#[repr(transparent)]` or `#[repr(packed)]` wrappers around other ULE types (or in general, types known to have no alignment requirements). Note that `#[repr(Rust)]` isn't defined or stable, so ULE types _must_ have _some_ `#[repr(..)]` tag for them to be able to stably uphold the invariants.
185+
As `ULE` requires types to not have any alignment restrictions, most `ULE` types will be `#[repr(transparent)]` or `#[repr(C, packed)]` wrappers around other ULE types (or in general, types known to have no alignment requirements). Note that `#[repr(Rust)]` isn't defined or stable, so ULE types _must_ have _some_ `#[repr(..)]` tag for them to be able to stably uphold the invariants.
186186

187187
If you wish to make a custom ULE type, it will likely wrap [`RawBytesULE`] with added invariants (and `#[repr(transparent)]`, or do something like the following:
188188

@@ -195,7 +195,7 @@ struct Foo {
195195
}
196196

197197
// Implements ULE
198-
#[repr(packed)]
198+
#[repr(C, packed)]
199199
struct FooULE {
200200
field1: u32::ULE,
201201
field2: char::ULE,
@@ -223,7 +223,7 @@ pub unsafe trait VarULE: 'static {
223223

224224
Similarly to [`ULE`], `VarULE` is an `unsafe` trait which mainly requires the user to specify whether a `&[u8]` slice contains a valid bit pattern for a _single_ `Self` instance. Since pointer metadata can vary between unsized types, `from_byte_slice_unchecked()` must also be specified by the implementor so that `VarZeroVec` can materialize `&Self` instances out of known-valid bit patterns after validation.
225225

226-
`VarULE` types must also accept any alignment, so most custom `VarULE` types will be `#[repr(packed)]` wrappers around structs containing `ULE` and `VarULE` types (like `str`, `[u8]`, [`VarZeroSlice`], [`ZeroSlice`]).
226+
`VarULE` types must also accept any alignment, so most custom `VarULE` types will be `#[repr(C, packed)]` wrappers around structs containing `ULE` and `VarULE` types (like `str`, `[u8]`, [`VarZeroSlice`], [`ZeroSlice`]).
227227

228228

229229
### `EncodeAsVarULE`
@@ -315,7 +315,7 @@ These are basic derives that can be applied to types to _just_ generate ULE and
315315

316316
These can only be applied to structs where all fields are ULE types (for `#[derive(VarULE)]`, the last field must be an unsized `VarULE` type). These derives will do the following things:
317317

318-
- Apply `#[repr(packed)]` to the type (or perhaps `#[repr(C)]` if we can determine that that will always work)
318+
- Apply `#[repr(C, packed)]` to the type (or perhaps `#[repr(C)]` if we can determine that that will always work)
319319
- Generate the appropriate `ZeroMapKV` impl (an opt-out can be provided)
320320
- Generate a `ULE` or `VarULE` implementation that applies offsetted `validate_byte_slice()` for each field to implement the final `validate_byte_slice()`
321321
- Generate `Copy`/`Clone` impls as necessary (`#[derive()]` does not work with packed types)
@@ -382,7 +382,7 @@ For an initial pass, we may only support single-heap-field structs for `#[make_v
382382

383383
Ideally, enums can have ULE impls autogenerated for them, but handling the discriminants gets tricky.
384384

385-
One way to handle this is to generate a private internal struct for each variant and do the usual ULE or VarULE generation for each. Then, manaully construct a `#[repr(packed)]` tagged union with a `u8` tag and a `union` of all of the internal structs. This is somewhat complicated but actually relatively simple to implement since it can use `#[make_ule]` and `#[make_varule]`.
385+
One way to handle this is to generate a private internal struct for each variant and do the usual ULE or VarULE generation for each. Then, manaully construct a `#[repr(C, packed)]` tagged union with a `u8` tag and a `union` of all of the internal structs. This is somewhat complicated but actually relatively simple to implement since it can use `#[make_ule]` and `#[make_varule]`.
386386

387387

388388
We may also do this completely manually, which gives us the opportunity for bitpacking the discriminant further, if combined with the bitpacking scheme discussed below.

utils/zerovec/src/ule/custom.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,19 @@
4747
//! # field3: ZeroVec<'a, u32>
4848
//! # }
4949
//!
50-
//! // Must be repr(packed) for safety of VarULE!
50+
//! // Must be repr(C, packed) for safety of VarULE!
5151
//! // Must also only contain ULE types
52-
//! #[repr(packed)]
52+
//! #[repr(C, packed)]
5353
//! struct FooULE {
5454
//! field1: <char as AsULE>::ULE,
5555
//! field2: <u32 as AsULE>::ULE,
5656
//! field3: ZeroSlice<u32>,
5757
//! }
5858
//!
5959
//! // Safety (based on the safety checklist on the VarULE trait):
60-
//! // 1. FooULE does not include any uninitialized or padding bytes. (achieved by `#[repr(packed)]` on
60+
//! // 1. FooULE does not include any uninitialized or padding bytes. (achieved by `#[repr(C, packed)]` on
6161
//! // a struct with only ULE fields)
62-
//! // 2. FooULE is aligned to 1 byte. (achieved by `#[repr(packed)]` on
62+
//! // 2. FooULE is aligned to 1 byte. (achieved by `#[repr(C, packed)]` on
6363
//! // a struct with only ULE fields)
6464
//! // 3. The impl of `validate_byte_slice()` returns an error if any byte is not valid.
6565
//! // 4. The impl of `validate_byte_slice()` returns an error if the slice cannot be used in its entirety

utils/zerovec/src/ule/niche.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ impl<U: NicheBytes<N> + ULE + Eq, const N: usize> Eq for NichedOptionULE<U, N> {
108108
/// containing only ULE fields.
109109
/// NichedOptionULE either contains NICHE_BIT_PATTERN or valid U byte sequences.
110110
/// In both cases the data is initialized.
111-
/// 2. NichedOptionULE is aligned to 1 byte due to `#[repr(packed)]` on a struct containing only
111+
/// 2. NichedOptionULE is aligned to 1 byte due to `#[repr(C, packed)]` on a struct containing only
112112
/// ULE fields.
113113
/// 3. validate_byte_slice impl returns an error if invalid bytes are encountered.
114114
/// 4. validate_byte_slice impl returns an error there are extra bytes.

utils/zerovec/src/ule/option.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ impl<U: Copy + Eq> Eq for OptionULE<U> {}
141141
/// ```
142142
// The slice field is empty when None (bool = false),
143143
// and is a valid T when Some (bool = true)
144-
#[repr(packed)]
144+
#[repr(C, packed)]
145145
pub struct OptionVarULE<U: VarULE + ?Sized>(PhantomData<U>, bool, [u8]);
146146

147147
impl<U: VarULE + ?Sized> OptionVarULE<U> {
@@ -166,8 +166,8 @@ impl<U: VarULE + ?Sized + core::fmt::Debug> core::fmt::Debug for OptionVarULE<U>
166166

167167
// Safety (based on the safety checklist on the VarULE trait):
168168
// 1. OptionVarULE<T> does not include any uninitialized or padding bytes
169-
// (achieved by being repr(packed) on ULE types)
170-
// 2. OptionVarULE<T> is aligned to 1 byte (achieved by being repr(packed) on ULE types)
169+
// (achieved by being repr(C, packed) on ULE types)
170+
// 2. OptionVarULE<T> is aligned to 1 byte (achieved by being repr(C, packed) on ULE types)
171171
// 3. The impl of `validate_byte_slice()` returns an error if any byte is not valid.
172172
// 4. The impl of `validate_byte_slice()` returns an error if the slice cannot be used in its entirety
173173
// 5. The impl of `from_byte_slice_unchecked()` returns a reference to the same data.

0 commit comments

Comments
 (0)