1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
//! The shape of a type that crosses between mods: what the layout checks of
//! services, mod events and extension points hash for every field.
//!
//! A field's hash covers its name, place and size, and the [`Shape`] of its
//! type: for a struct every field inside it, for an enum every variant with
//! its discriminant, for a newtype the named values it lists, for `bool`
//! its two values. Two mods built against declarations whose meaning
//! differs anywhere down the tree, even at the same size, are refused with
//! the name of the outer field. [`#[derive(Shape)]`](macro@crate::Shape)
//! writes the implementation for a type of the mod's own.
use core::ffi::c_void;
use core::marker::PhantomData;
use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64};
use core::ptr::NonNull;
/// A type with a structural hash, so a field of it can be checked between
/// mods.
///
/// # Safety
///
/// - Implemented with [`#[derive(Shape)]`](macro@crate::Shape).
/// - [`SHAPE`](Shape::SHAPE) changes whenever the type's layout or the
/// meaning of its values changes: every field with its offset, every
/// variant with its discriminant, every named value.
/// - [`PADDING_FREE`](Shape::PADDING_FREE) is `true` only when every byte
/// of every value is initialised, with no padding anywhere inside it,
/// nested types included.
///
/// # Examples
///
/// ```
/// use enshwick::Shape;
///
/// #[derive(Clone, Copy, Debug, Shape)]
/// #[repr(u8)]
/// pub enum Coin {
/// Copper = 1,
/// Silver = 2,
/// }
///
/// assert_ne!(Coin::SHAPE, u8::SHAPE);
/// ```
pub unsafe trait Shape {
/// The type's structural hash.
const SHAPE: u64;
/// What a point schema calls the type: a primitive's name such as
/// `u32` or `bool`, `enshwick.<name>` for a type of this crate, or the
/// type's own name; empty for a type a schema cannot describe.
const SCHEMA_NAME: &'static str = "";
/// The `[[type]]` tables of a point schema for the type and every named
/// type inside it; empty for a primitive or a type of this crate.
const SCHEMA_TYPES: &'static str = "";
/// Whether every byte of a value is initialised, with no padding
/// anywhere inside it, so another language may read it as bytes; false
/// unless the type says so.
const PADDING_FREE: bool = false;
}
/// Builds a [`Shape`] hash, FNV-1a over the parts in order.
///
/// # Examples
///
/// ```
/// use enshwick::shape::ShapeHasher;
///
/// let a = ShapeHasher::new("enum").text("Coin").number(1).finish();
/// assert_ne!(a, ShapeHasher::new("enum").text("Coin").number(2).finish());
/// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ShapeHasher(u64);
impl ShapeHasher {
const PRIME: u64 = 0x0000_0100_0000_01b3;
/// A hash that starts with `kind`, such as `struct` or `enum`.
///
/// # Examples
///
/// See [`ShapeHasher`].
#[must_use]
pub const fn new(kind: &str) -> Self {
Self(0xcbf2_9ce4_8422_2325).text(kind)
}
/// Adds `text` and its length, so `ab`, `c` and `a`, `bc` differ.
///
/// # Examples
///
/// See [`ShapeHasher`].
#[must_use]
pub const fn text(self, text: &str) -> Self {
let mut hash = self.number(text.len() as u64).0;
let bytes = text.as_bytes();
let mut i = 0;
while i < bytes.len() {
hash ^= bytes[i] as u64;
hash = hash.wrapping_mul(Self::PRIME);
i += 1;
}
Self(hash)
}
/// Adds `number`, such as an offset, a discriminant or another shape.
///
/// # Examples
///
/// See [`ShapeHasher`].
#[must_use]
pub const fn number(self, number: u64) -> Self {
let mut hash = self.0;
let bytes = number.to_le_bytes();
let mut i = 0;
while i < bytes.len() {
hash ^= bytes[i] as u64;
hash = hash.wrapping_mul(Self::PRIME);
i += 1;
}
Self(hash)
}
/// Adds the size and alignment of `T`.
///
/// # Examples
///
/// ```
/// use enshwick::shape::ShapeHasher;
///
/// let a = ShapeHasher::new("struct").layout::<u32>().finish();
/// assert_ne!(a, ShapeHasher::new("struct").layout::<u64>().finish());
/// ```
#[must_use]
pub const fn layout<T>(self) -> Self {
self.number(size_of::<T>() as u64)
.number(align_of::<T>() as u64)
}
/// Adds a field's offset.
///
/// # Examples
///
/// ```
/// use enshwick::shape::ShapeHasher;
///
/// let a = ShapeHasher::new("struct").offset(0).finish();
/// assert_ne!(a, ShapeHasher::new("struct").offset(4).finish());
/// ```
#[must_use]
pub const fn offset(self, offset: usize) -> Self {
self.number(offset as u64)
}
/// Adds a discriminant or a named value, sign and all.
///
/// # Examples
///
/// ```
/// use enshwick::shape::ShapeHasher;
///
/// let a = ShapeHasher::new("enum").value(-1).finish();
/// assert_ne!(a, ShapeHasher::new("enum").value(1).finish());
/// ```
#[must_use]
pub const fn value(self, value: i128) -> Self {
let bits = value.to_le_bytes();
let mut low = [0u8; 8];
let mut high = [0u8; 8];
let mut i = 0;
while i < 8 {
low[i] = bits[i];
high[i] = bits[i + 8];
i += 1;
}
self.number(u64::from_le_bytes(low))
.number(u64::from_le_bytes(high))
}
/// The hash.
///
/// # Examples
///
/// See [`ShapeHasher`].
#[must_use]
pub const fn finish(self) -> u64 {
self.0
}
}
macro_rules! leaf {
($($ty:ty),* $(,)?) => {$(
// SAFETY: a primitive's shape is its name, which fixes its layout
// and values.
unsafe impl Shape for $ty {
const SHAPE: u64 = ShapeHasher::new(stringify!($ty)).finish();
}
)*};
}
macro_rules! number {
($($ty:ty),* $(,)?) => {$(
// SAFETY: a number's shape is its name, which fixes its layout and
// values.
unsafe impl Shape for $ty {
const SHAPE: u64 = ShapeHasher::new(stringify!($ty)).finish();
const SCHEMA_NAME: &'static str = stringify!($ty);
const PADDING_FREE: bool = true;
}
)*};
}
number!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
leaf!(
(),
u128,
usize,
i128,
isize,
char,
str,
c_void,
NonZeroU8,
NonZeroU16,
NonZeroU32,
NonZeroU64,
);
// SAFETY: `bool` has the values 0 and 1, both in the hash.
unsafe impl Shape for bool {
const SHAPE: u64 = ShapeHasher::new("bool")
.text("false")
.number(0)
.text("true")
.number(1)
.finish();
const SCHEMA_NAME: &'static str = "bool";
const PADDING_FREE: bool = true;
}
// SAFETY: an array is its element's shape repeated `N` times.
unsafe impl<T: Shape, const N: usize> Shape for [T; N] {
const SHAPE: u64 = ShapeHasher::new("array")
.number(T::SHAPE)
.number(N as u64)
.finish();
const PADDING_FREE: bool = T::PADDING_FREE;
}
// SAFETY: a slice is its element's shape.
unsafe impl<T: Shape> Shape for [T] {
const SHAPE: u64 = ShapeHasher::new("slice").number(T::SHAPE).finish();
}
// SAFETY: `Option` adds its tag or niche to the shape inside it.
unsafe impl<T: Shape> Shape for Option<T> {
const SHAPE: u64 = ShapeHasher::new("Option").number(T::SHAPE).finish();
}
// SAFETY: `PhantomData` holds nothing.
unsafe impl<T: ?Sized> Shape for PhantomData<T> {
const SHAPE: u64 = ShapeHasher::new("PhantomData").finish();
}
macro_rules! pointer {
($($kind:literal $ty:ty),* $(,)?) => {$(
// SAFETY: a pointer is the shape of what it points at.
unsafe impl<T: Shape + ?Sized> Shape for $ty {
const SHAPE: u64 = ShapeHasher::new($kind).number(T::SHAPE).finish();
}
)*};
}
pointer!(
"*const" *const T,
"*mut" *mut T,
"&" &T,
"&mut" &mut T,
"NonNull" NonNull<T>,
);
macro_rules! function {
($($arg:ident)*) => {
function!(@abi "Rust" fn($($arg),*) -> R; $($arg)*);
function!(@abi "C" extern "C" fn($($arg),*) -> R; $($arg)*);
function!(@abi "system" extern "system" fn($($arg),*) -> R; $($arg)*);
function!(@abi "unsafe Rust" unsafe fn($($arg),*) -> R; $($arg)*);
function!(@abi "unsafe C" unsafe extern "C" fn($($arg),*) -> R; $($arg)*);
function!(@abi "unsafe system" unsafe extern "system" fn($($arg),*) -> R; $($arg)*);
};
(@abi $abi:literal $ty:ty; $($arg:ident)*) => {
// SAFETY: a function pointer is its calling convention, its
// arguments' shapes in order and its return's shape.
unsafe impl<R: Shape, $($arg: Shape),*> Shape for $ty {
const SHAPE: u64 = ShapeHasher::new("fn")
.text($abi)
.number(R::SHAPE)
$(.number($arg::SHAPE))*
.finish();
}
};
}
function!();
function!(A);
function!(A B);
function!(A B C);
function!(A B C D);
function!(A B C D E);
function!(A B C D E F);
function!(A B C D E F G);
function!(A B C D E F G H);
#[cfg(test)]
mod tests {
use super::Shape;
#[test]
fn primitives_differ() {
let shapes = [
u8::SHAPE,
i8::SHAPE,
bool::SHAPE,
u32::SHAPE,
f32::SHAPE,
<[u8; 4]>::SHAPE,
<[u8; 2]>::SHAPE,
<*const u32>::SHAPE,
<*mut u32>::SHAPE,
<extern "system" fn(u32) -> u32>::SHAPE,
<extern "C" fn(u32) -> u32>::SHAPE,
<extern "system" fn(u64) -> u32>::SHAPE,
<extern "system" fn(u32, u32) -> u32>::SHAPE,
];
for (i, a) in shapes.iter().enumerate() {
for b in &shapes[i + 1..] {
assert_ne!(a, b);
}
}
}
}