Skip to content

Commit 874f368

Browse files
committed
[project] Initial commit
gherrit-pr-id: I54eb863c96f056738f58727fc0fec4e1630bf97a
1 parent af67d9a commit 874f368

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,8 @@ pub mod layout;
333333
mod macros;
334334
#[doc(hidden)]
335335
pub mod pointer;
336+
#[doc(hidden)]
337+
pub mod project;
336338
mod r#ref;
337339
// TODO(#252): If we make this pub, come up with a better name.
338340
mod wrappers;

src/project.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// Copyright 2024 The Fuchsia Authors
2+
//
3+
// Licensed under the 2-Clause BSD License <LICENSE-BSD or
4+
// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0
5+
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
6+
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
7+
// This file may not be copied, modified, or distributed except according to
8+
// those terms.
9+
10+
// NOTE: `NAME_HASH` is not semver-stable - it just needs to be consistent
11+
// within a single compilation.
12+
//
13+
// TODO: `#[diagnostic::on_unimplemented]`
14+
#[doc(hidden)]
15+
pub unsafe trait Field<const NAME_HASH: u128> {
16+
type Type;
17+
const OFFSET: isize;
18+
}
19+
20+
pub unsafe trait Projectable {
21+
type Of<T>;
22+
type Inner;
23+
}
24+
25+
unsafe impl<T> Projectable for core::mem::ManuallyDrop<T> {
26+
type Of<U> = core::mem::ManuallyDrop<U>;
27+
type Inner = T;
28+
}
29+
30+
// TODO: Use `Ptr` to make this generic over `&`, `&mut`, `Box`, etc.
31+
#[doc(hidden)]
32+
#[allow(clippy::must_use_candidate)]
33+
#[inline(never)]
34+
pub const fn project<P, const NAME_HASH: u128>(
35+
ptr: *const P,
36+
) -> *const P::Of<<P::Inner as Field<{ NAME_HASH }>>::Type>
37+
where
38+
P: Projectable,
39+
P::Inner: Field<{ NAME_HASH }>,
40+
{
41+
let inner = ptr.cast::<P::Inner>();
42+
let field_inner = unsafe { inner.byte_offset(<P::Inner as Field<{ NAME_HASH }>>::OFFSET) };
43+
let field_inner = field_inner.cast::<<P::Inner as Field<{ NAME_HASH }>>::Type>();
44+
field_inner.cast()
45+
}
46+
47+
// TODO: Implement a stronger hash function so we can basically just ignore
48+
// collisions. If users encounter collisions in practice, we can just deal with
49+
// it then, publish a new version, and tell them to upgrade.
50+
#[doc(hidden)]
51+
#[must_use]
52+
#[inline(always)]
53+
#[allow(clippy::as_conversions, clippy::indexing_slicing, clippy::arithmetic_side_effects)]
54+
pub const fn hash_field_name(field_name: &str) -> u128 {
55+
let field_name = field_name.as_bytes();
56+
let mut hash = 0u64;
57+
let mut i = 0;
58+
while i < field_name.len() {
59+
const K: u64 = 0x517cc1b727220a95;
60+
hash = (hash.rotate_left(5) ^ (field_name[i] as u64)).wrapping_mul(K);
61+
i += 1;
62+
}
63+
hash as u128
64+
}
65+
66+
#[macro_export]
67+
macro_rules! projectable {
68+
// It's unlikely that anyone would ever call this macro on a field-less
69+
// struct, but might as well support it for completeness.
70+
($(#[$attr:meta])* $vis:vis struct $name:ident;) => {};
71+
($(#[$attr:meta])* $vis:vis struct $name:ident {
72+
$($field:ident: $field_ty:ty),* $(,)?
73+
}) => {
74+
$(#[$attr])*
75+
$vis struct $name {
76+
$($field: $field_ty,)*
77+
}
78+
79+
$(
80+
$crate::projectable!(@inner $name . $field: $field_ty);
81+
)*
82+
};
83+
($(#[$attr:meta])* $vis:vis struct $name:ident (
84+
$($field_ty:ty),* $(,)?
85+
);) => {
86+
$(#[$attr])*
87+
$vis struct $name (
88+
$($field_ty,)*
89+
);
90+
91+
// TODO: How to generate the idents `0`, `1`, etc in order to call
92+
// `projectable!(@inner ...)`?
93+
};
94+
(@inner $name:ident . $field:ident: $field_ty:ty) => {
95+
unsafe impl $crate::project::Field<{
96+
$crate::project::hash_field_name(stringify!($field))
97+
}> for $name {
98+
type Type = $field_ty;
99+
const OFFSET: isize = $crate::util::macro_util::core_reexport::mem::offset_of!($name, $field) as isize;
100+
}
101+
};
102+
}
103+
104+
#[macro_export]
105+
macro_rules! project {
106+
($outer:ident $(. $field:ident)*) => { $crate::project!(($outer) . $field) };
107+
(($outer:expr) . $field:ident) => {{
108+
let outer: &_ = $outer;
109+
let outer: *const _ = outer;
110+
let field = $crate::project::project::<_, {
111+
$crate::project::hash_field_name(stringify!($field))
112+
}>(outer);
113+
unsafe { &*field }
114+
}};
115+
(($outer:expr) $(. $field:ident)+) => {{
116+
let outer = $outer;
117+
$(
118+
let outer = project!((outer).$field);
119+
)+
120+
outer
121+
}};
122+
}
123+
124+
#[cfg(test)]
125+
mod tests {
126+
projectable! {
127+
struct Foo {
128+
a: u8,
129+
b: u16,
130+
c: u32,
131+
}
132+
}
133+
134+
#[test]
135+
fn test_project() {
136+
use core::mem::ManuallyDrop;
137+
138+
let f = ManuallyDrop::new(Foo { a: 0, b: 1, c: 2 });
139+
let p = project!((&f).c);
140+
assert_eq!(p, &ManuallyDrop::new(2));
141+
}
142+
}

0 commit comments

Comments
 (0)