@@ -225,6 +225,11 @@ pub struct DstLayout {
225
225
_trailing_slice_elem_size : Option < usize > ,
226
226
}
227
227
228
+ enum _CastType {
229
+ _Prefix,
230
+ _Suffix,
231
+ }
232
+
228
233
impl DstLayout {
229
234
/// Constructs a `DstLayout` which describes `T`.
230
235
///
@@ -251,6 +256,124 @@ impl DstLayout {
251
256
_trailing_slice_elem_size : Some ( mem:: size_of :: < T > ( ) ) ,
252
257
}
253
258
}
259
+
260
+ /// TODO
261
+ ///
262
+ /// The caller is responsible for ensuring that `addr + bytes_len` does not
263
+ /// overflow `usize`.
264
+ ///
265
+ /// # Panics
266
+ ///
267
+ /// If `addr + bytes_len` overflows `usize`, `validate_cast` may panic, or
268
+ /// it may return incorrect results. No guarantees are made about when
269
+ /// `validate_cast` will panic. The caller should not rely on
270
+ /// `validate_cast` panicking in any particular condition, even if
271
+ /// `debug_assertions` are enabled.
272
+ const fn _validate_cast (
273
+ & self ,
274
+ addr : usize ,
275
+ bytes_len : usize ,
276
+ cast_type : _CastType ,
277
+ ) -> Option < ( usize , usize , usize ) > {
278
+ let base_size = self . _base_layout . size ( ) ;
279
+
280
+ // LEMMA 0: max_slice_bytes + base_size == bytes_len
281
+ //
282
+ // LEMMA 1: base_size <= bytes_len:
283
+ // - If `base_size > bytes_len`, `bytes_len.checked_sub(base_size)`
284
+ // returns `None`, and we return.
285
+ let Some ( max_slice_bytes) = bytes_len. checked_sub ( base_size) else {
286
+ return None ;
287
+ } ;
288
+
289
+ let ( elems, self_bytes) = if let Some ( elem_size) = self . _trailing_slice_elem_size {
290
+ let Some ( elem_size) = NonZeroUsize :: new ( elem_size) else {
291
+ panic ! ( "attempted to cast to slice type with zero-sized element" ) ;
292
+ } ;
293
+
294
+ // Guaranteed not to divide by 0 because `elem_size` is a
295
+ // `NonZeroUsize`.
296
+ //
297
+ // LEMMA 2: elems * elem_size <= max_slice_bytes
298
+ #[ allow( clippy:: arithmetic_side_effects) ]
299
+ let elems = max_slice_bytes / elem_size. get ( ) ;
300
+
301
+ // Guaranteed not to overflow:
302
+ // - elems * elem_size <= max_slice_bytes (lemma 2)
303
+ // - max_slice_bytes <= usize::MAX (max_slice_bytes: usize)
304
+ // - elems * elem_size <= usize::MAX (substitution)
305
+ //
306
+ // LEMMA 3: slice_bytes <= max_slice_bytes:
307
+ // - max_slice_bytes + base_size == bytes_len (lemma 0)
308
+ // - base_size == bytes_len - max_slice_bytes
309
+ // - elems * elem_size <= max_slice_bytes (lemma 2)
310
+ // - slice_bytes <= max_slice_bytes (substitution)
311
+ #[ allow( clippy:: arithmetic_side_effects) ]
312
+ let slice_bytes = elems * elem_size. get ( ) ;
313
+
314
+ // Guaranteed not to overflow:
315
+ // - max_slice_bytes + base_size == bytes_len (lemma 0)
316
+ // - slice_bytes <= max_slice_bytes (lemma 3)
317
+ // - slice_bytes + base_size <= bytes_len (substitution)
318
+ // - bytes_len <= usize::MAX (bytes_len: usize)
319
+ // - slice_bytes + base_size <= usize::MAX (substitution)
320
+ //
321
+ // LEMMA 3: self_bytes <= bytes_len: TODO
322
+ #[ allow( clippy:: arithmetic_side_effects) ]
323
+ let self_bytes = base_size + slice_bytes;
324
+
325
+ ( elems, self_bytes)
326
+ } else {
327
+ ( 0 , base_size)
328
+ } ;
329
+
330
+ // LEMMA 4: self_bytes <= bytes_len:
331
+ // - `if` branch returns `self_bytes`; lemma 3 guarantees `self_bytes <=
332
+ // bytes_len`
333
+ // - `else` branch returns `base_size`; lemma 1 guarantees `base_size <=
334
+ // bytes_len`
335
+
336
+ // `self_addr` indicates where in the given byte range the `Self` will
337
+ // start. If we're doing a prefix cast, it starts at the beginning. If
338
+ // we're doing a suffix cast, it starts after whatever bytes are
339
+ // remaining.
340
+ let ( self_addr, split_at) = match cast_type {
341
+ _CastType:: _Prefix => ( addr, self_bytes) ,
342
+ _CastType:: _Suffix => {
343
+ // Guaranteed not to underflow because `self_bytes <= bytes_len`
344
+ // (lemma 4).
345
+ //
346
+ // LEMMA 5: split_at = bytes_len - self_bytes
347
+ #[ allow( clippy:: arithmetic_side_effects) ]
348
+ let split_at = bytes_len - self_bytes;
349
+
350
+ // Guaranteed not to overflow:
351
+ // - addr + bytes_len <= usize::MAX (method precondition)
352
+ // - split_at == bytes_len - self_bytes (lemma 5)
353
+ // - addr + split_at == addr + bytes_len - self_bytes (substitution)
354
+ // - addr + split_at <= addr + bytes_len
355
+ // - addr + split_at <= usize::MAX (substitution)
356
+ #[ allow( clippy:: arithmetic_side_effects) ]
357
+ let self_addr = addr + split_at;
358
+
359
+ ( self_addr, split_at)
360
+ }
361
+ } ;
362
+
363
+ // Guaranteed not to divide by 0 because `.align()` guarantees that it
364
+ // returns a non-zero value.
365
+ #[ allow( clippy:: arithmetic_side_effects) ]
366
+ if self_addr % self . _base_layout . align ( ) != 0 {
367
+ return None ;
368
+ }
369
+
370
+ // Guaranteed not to underflow because `self_bytes <= bytes_len` (lemma
371
+ // 4).
372
+ #[ allow( clippy:: arithmetic_side_effects) ]
373
+ let prefix_suffix_bytes = bytes_len - self_bytes;
374
+
375
+ Some ( ( elems, split_at, prefix_suffix_bytes) )
376
+ }
254
377
}
255
378
256
379
/// A trait which carries information about a type's layout that is used by the
0 commit comments