oct 0.37.0

Octonary utilities.
Documentation
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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
// Copyright 2024-2026 Gabriel Bjørnager Jensen.
//
// SPDX: MIT OR Apache-2.0

//! Utility functions.

mod test;

use core::mem::{ManuallyDrop, MaybeUninit};
use core::ptr;
use core::slice;
use oct::{
	FromOctets,
	Immutable,
	IntoOctets,
	Outlay,
	Zeroable,
};

#[cfg(feature = "alloc")]
use alloc::alloc::{alloc, handle_alloc_error};

#[cfg(feature = "alloc")]
use alloc::boxed::Box;

/// Allocates a boxed object with a custom
/// initialiser.
///
/// This function should rarely be used directly as
/// it requires very special care. It is provided as
/// a more simple interface for boxing with specific
/// representation.
///
/// # Panics
///
/// This function will panic if layout could not be
/// constructed for the provided metadata.
///
/// # Safety
///
/// The raw slice passed to the provided function
/// must be initialised to a valid instance of `T`
/// -- keeping the provided metadata in mind.
///
/// Failure to properly initialise the object will
/// construct an invalid instance of `T` and -- by
/// extension -- an invalid instance of `Box<T>`.
/// Behaviour is in that case undefined and may
/// unexpectetly close/open Suez and/or Hormoz.
#[cfg(feature = "alloc")]
pub unsafe fn boxed_from_fn_with_metadata<T: ?Sized + Outlay, F: FnOnce(&mut [MaybeUninit<u8>])>(f: F, metadata: T::Metadata) -> Box<T> {
	// Determine the layout for the metadata.
	let Ok(layout) = T::layout_for_metadata(metadata) else {
		panic!("cannot compute layout for dst with metadata `{metadata:?}`");
	};

	let raw = if layout.size() > 0 {
		// SAFETY: The above test guarantees that the size
		// is non-zero.
		let data = unsafe { alloc(layout) };

		if data.is_null() {
			handle_alloc_error(layout);
		};

		T::ptr_from_raw_parts_mut(data, metadata)
	} else {
		T::ptr_dangling_with_metadata_mut(metadata)
	};

	{
		let len  = layout.size();
		let data = raw.cast::<MaybeUninit<u8>>();

		// SAFETY:
		//
		// * The allocation is either successfully-
		//   allocated or dangling, and the pointer is in
		//   both cases guaranteed to be non-null and valid
		//   for writes (and reads) for `layout.size()`
		//   bytes. The allocation is always aligned for
		//   `[MaybeUninit<u8>]`, no matter the instance of
		//   `T`.
		// * `MaybeUninit<u8>` accepts any representation,
		//   so we do not need to care about whether the
		//   allocation is initialised or not.
		// * We do not use another pointer to the alloca-
		//   tion during the slice's life time, nor have
		//   we exposed the pointer, so we can rest assured
		//   that it remains exclusive (and it would not
		//   make sense for the allocator to leak it its-
		//   self.)
		// * `Layout` guarantees that the size is less than
		//   or equal to `isize::MAX`. The allocation is a
		//   Rust allocation, so it shouldn't (can't?)
		//   overflow the address space.
		let buf = unsafe { slice::from_raw_parts_mut(data, len) };

		f(buf)
	}

	// SAFETY:
	//
	// * The allocation has been allocated using the
	//   global allocator via a layout that is valid
	//   for `T`.
	// * The caller guarantees that `f` has correctly
	//   initialised the allocation to a valid instance
	//   of `T` (keeping the provided metadata in
	//   mind.)
	unsafe { Box::from_raw(raw) }
}

/// Retrieves a slice over object octets.
///
/// The returned slice covers the entire
/// representation of the referenced object.
///
/// Contrary to [`transmute_ref`], this function
/// will never panic. It is also possible to use
/// this function in `const` expressions.
///
/// *See also [`octets_of_mut`].*
///
/// [`octets_of_mut`]: octets_of_mut
#[inline]
#[must_use]
pub const fn octets_of<T: ?Sized + IntoOctets + Immutable>(value: &T) -> &[u8] {
	let len = size_of_val(value);

	let data = ptr::from_ref(value).cast::<u8>();

	// SAFETY:
	//
	// * The original reference guarantees that the ad-
	//   dress is non-null and valid for reads.
	// * `size_of_val` guarantees the exact size of the
	//   object, guaranteeing that the length will not
	//   overflow outside the allocation.
	// * `u8` (and `[u8]`) are unaligned in the sense
	//   that their alignment requirements are `1`
	//   byte. All addresses in Rust are byte-aligned,
	//   so we can consider all objects to be aligned
	//   for this purpose.
	// * The `T: IntoOctets` bound guarantees that all
	//   octets of the destination object are always
	//   valid instances of `u8`, i.e. are always ini-
	//   tialised.
	// * The `T: Immutable` bound guarantees that no
	//   mutations will occur on the destination object
	//   (including during the lifetime of the returned
	//   slice reference.)
	// * The destination object, already being a single
	//   Rust object, is guaranteed to never have a
	//   size that is greater than `isize::MAX` or one
	//   that would overflow when added to the origin
	//   address.
	unsafe { slice::from_raw_parts(data, len) }
}

/// Retrieves a mutable slice over object octets.
///
/// The returned slice covers the entire
/// representation of the referenced object.
///
/// Contrary to [`transmute_mut`], this function
/// will never panic. It is also possible to use
/// this function in `const` expressions.
///
/// *See also [`octets_of`].*
///
/// [`octets_of`]: octets_of
#[inline]
#[must_use]
pub const fn octets_of_mut<T: ?Sized + IntoOctets + FromOctets>(value: &mut T) -> &mut [u8] {
	let len = size_of_val(value);

	let data = ptr::from_mut(value).cast::<u8>();

	// SAFETY:
	//
	// * The original reference guarantees that the ad-
	//   dress is non-null and valid for reads.
	// * `size_of_val` guarantees the exact size of the
	//   object, guaranteeing that the length will not
	//   overflow outside the allocation.
	// * `u8` (and `[u8]`) are unaligned in the sense
	//   that their alignment requirements are `1`
	//   byte. All addresses in Rust are byte-aligned,
	//   so we can consider all objects to be aligned
	//   for this purpose.
	// * The `T: IntoOctets` bound guarantees that all
	//   octets of the destination object are always
	//   valid instances of `u8`, i.e. are always ini-
	//   tialised. The `T: FromOctets` bound guarantees
	//   in return that any write to the returned slice
	//   will not invalidate the representation of the
	//   destination as a `T` object.
	// * The original - mutable - reference guarantees
	//   that the destination object is already exclu-
	//   sive. We do not leak any pointers to this ob-
	//   ject, so we can assume (thanks to provenance)
	//   that this exclusivity is kept intact.s
	// * The destination object, already being a single
	//   Rust object, is guaranteed to never have a
	//   size that is greater than `isize::MAX` or one
	//   that would overflow when added to the origin
	//   address.
	unsafe { slice::from_raw_parts_mut(data, len) }
}

/// Overwrites an object with a specific octet
/// value.
///
/// The provided value is repeated over the entirety
/// of the referenced object. The object is fully
/// initialised after a successful call to this
/// function.
#[inline(always)]
pub const fn fill<T: ?Sized + FromOctets>(dst: &mut T, value: u8) {
	// SAFETY: The `T: FromOctets` bound guarantees
	// that any initialised representation is support-
	// ed.
	unsafe { fill_unchecked(dst, value) };
}

/// Unsafely overwrites an object with a specific
/// octet value.
///
/// The provided value is repeated over the entirety
/// of the referenced object. The object is fully
/// initialised after a successful call to this
/// function.
///
/// # Safety
///
/// The destination object must support having the
/// provided octet value repeated over it.
#[inline]
#[cfg_attr(miri, track_caller)]
pub const unsafe fn fill_unchecked<T: ?Sized>(dst: &mut T, value: u8) {
	let len = size_of_val(dst);
	let dst = ptr::from_mut(dst).cast::<u8>();

	// SAFETY:
	// * `size_of_val` returns the exact size of the
	//   object.
	// * Any pointer is considered aligned for `u8`.
	// * The object is initialised entirely. Caller
	//   guarantees that a `T` object with `value` re-
	//   peated is supported.
	unsafe { dst.write_bytes(value, len) };
}

/// Overwrites an object with all-zeros.
///
/// The provided object is considered initialised
/// after a successful call to this method.
#[inline(always)]
pub const fn fill_zeros<T: ?Sized + Zeroable>(dst: &mut T) {
	// SAFETY: The `T: Zeroable` bound guarantees that
	// an all-zeros representation is supported.
	unsafe { fill_unchecked(dst, 0); };
}

/// Unsafely constructs an object from a repeated
/// octet value.
///
/// The returned value is fully initialised.
#[inline(always)]
pub const fn filled<T: FromOctets>(value: u8) -> T {
	let mut buf = MaybeUninit::<T>::uninit();

	fill(&mut buf, value);

	// SAFETY: The `T: FromOctets` bound guarantees
	// that any `value` repeated over any `T` object is
	// valid.
	unsafe { buf.assume_init() }
}

/// Constructs a zeroed object.
///
/// The returned object is fully initialised.
///
/// This function is similar to
/// [`core::mem::zeroed`] but -- in contrast -- is
/// always safe to use.
#[inline(always)]
#[must_use]
pub const fn zeroed<T: Zeroable>() -> T {
	// NOTE: We use the standard `zeroed` function in
	// case it somehow yields better code gen.
	//
	// SAFETY: The `T: Zeroable` bound guarantees all-
	// zeros isn't an invariant.
	unsafe { core::mem::zeroed() }
}

/// Allocates a zeroed, boxed object via metadata.
///
/// This function is guaranteed to not
/// heap-allocate the zeroed object before boxing
/// it.
///
/// # Panics
///
/// This constructor will panic if layout could not
/// be constructed for the provided metadata.
#[cfg(feature = "alloc")]
#[must_use]
pub fn zeroed_boxed_with_metadata<T: ?Sized + Zeroable + Outlay>(metadata: T::Metadata) -> Box<T> {
	let f = |buf: &mut [MaybeUninit<u8>]| {
		// SAFETY: The `T: Zeroable` bound guarantees that
		// an all-zero representation is supported (no
		// matter the metadata provided.)
		buf.fill(MaybeUninit::new(0));
	};

	// SAFETY: The forwarded function correctly zero-
	// initialises the underlying object.
	unsafe { boxed_from_fn_with_metadata(f, metadata) }
}

/// Transmutes an object to another type.
///
/// The source value is reinterpreted as-is as an
/// object of the destination type. Padding bytes
/// are not preserved, but the initialisation states
/// of [`MaybeUninit`] fields are.
///
/// [`MaybeUninit`]: core::mem::MaybeUninit
///
/// # Panics
///
/// This function will panic if `T` and `U` aren't
/// of the same size.
#[inline]
#[must_use]
pub const fn transmute<T: IntoOctets, U: FromOctets>(value: T) -> U {
	// Bound notes:
	// * `T: Init` guarantees that the returned object
	//   doesn't assume uninitialised data being ini-
	//   tialised.

	// Test that the two types are of the same size.
	assert!(
		size_of::<U>() == size_of::<T>(),
		"cannot transmute types of different sizes",
	);

	// Reinterpret the object.

	// SAFETY: We have asserted that both types are of
	// equal size. `U` bounds guarantee lack of type
	// invariants.
	unsafe { transmute_unchecked(value) }
}

/// Transmutes an object to another type, copying
/// the representation.
///
/// The source value is reinterpreted as-is as an
/// object of the destination type. Padding bytes
/// are not preserved, but the initialisation states
/// of [`MaybeUninit`] fields are.
///
/// [`MaybeUninit`]: core::mem::MaybeUninit
///
/// # Panics
///
/// This function will panic if the provided object
/// has a size that is less than that of `U`.
#[inline]
#[must_use]
pub const fn transmute_copy<T: ?Sized + IntoOctets, U: FromOctets>(value: &T) -> U {
	assert!(
		size_of_val(value) >= size_of::<U>(),
		"cannot copy transmutation of smaller type",
	);

	let p = ptr::from_ref(value).cast::<U>();

	// SAFETY:
	//
	// * `p` inherits read validity from `value`.
	// * The `T: IntoOctets` guarantees that any `T`
	//   object may be reinterpreted as an octet array.
	//   The `U: FromOctets` guarantees that we may re-
	//   interpret any such array as an `U` object.
	//   Here, we skip the middle transmutation and di-
	//   rectly reinterpret the `T` object as being `U`
	//   (which is identical as the elided conversion
	//   doesn't change the representation.)
	unsafe { p.read_unaligned() }
}

/// Unsafely transmutes an object to another type.
///
/// The source value is reinterpreted as-is as an
/// object of the destination type. Padding bytes
/// are not preserved, but the initialisation states
/// of [`MaybeUninit`] fields are.
///
/// [`MaybeUninit`]: core::mem::MaybeUninit
///
/// This function is massively unsafe but also
/// allows for much more (sound) usage than
/// [`transmute`] and [`core::mem::transmute`].
///
/// # Safety
///
/// The following guarantees must be upheld when
/// transmuting objects:
///
/// * `U` and `T` must be of the exact same size.
/// * Any uninitialised octet in `value` must also
///   be permitted as uninitialised by `U`.
/// * Any initialised octet in `value` must have
///   value that is also permitted by `U`.
/// * `value` -- if a pointer -- was initially
///   transm from an integer.
///
/// A violation of any of these requirements results
/// in undefined behaviour. See also the
/// documentation for [`core::mem::transmute`].
#[inline(always)]
#[must_use]
#[cfg_attr(miri, track_caller)]
pub const unsafe fn transmute_unchecked<T, U>(value: T) -> U {
	#[repr(C)]
	union Transmute<Src, Dst> {
		src: ManuallyDrop<Src>,
		dst: ManuallyDrop<Dst>,
	}

	// Wrap the object in the union.

	let transmute = Transmute { src: ManuallyDrop::new(value) };

	// Reread the object as the destination type.

	// SAFETY: Caller guarantees correct representa-
	// tion.
	unsafe { ManuallyDrop::into_inner(transmute.dst) }
}

/// Transmutes a reference to another type.
///
/// See also [`transmute_ref_with_metadata`].
///
/// # Panics
///
/// This function will panic if any of the following
/// prerequisites are broken:
///
/// * The size of the object referenced by `r` is
///   not a valid size for `U` objects, or metadata
///   cannot indisputely be selected for the size.
/// * The object referenced by `r` is not aligned to
///   the requirements of `U`.
///
/// # Examples
///
/// Transmuting an object to a slice:
///
/// ```rust
/// use oct::transmute_ref;
///
/// let src = i64::MAX.to_le();
///
/// let dst: &[u32] = transmute_ref(&src);
///
/// assert_eq!(
///     dst,
///     &[
///         0xFFFF_FFFFu32.to_le(),
///         0x7FFF_FFFFu32.to_le(),
///     ],
/// );
/// ```
#[inline]
#[must_use]
#[track_caller]
pub fn transmute_ref<T, U>(r: &T) -> &U
where
	T: ?Sized + IntoOctets + Immutable,
	U: ?Sized + FromOctets + Immutable + Outlay,
{
	let size = size_of_val(r);

	let metadata = U::classify_size(size)
		.expect("cannot transmute reference with incompatible object size");

	transmute_ref_with_metadata(r, metadata)
}

/// Transmutes a mutable reference to another type.
///
/// # Panics
///
/// This function will panic if the size of the
/// provided object is not a valid size for `U`
/// objects, or if the object is unaligned to `U`.
#[inline]
#[must_use]
#[track_caller]
pub fn transmute_mut<T, U>(r: &mut T) -> &mut U
where
	T: ?Sized + IntoOctets + FromOctets,
	U: ?Sized + IntoOctets + FromOctets + Outlay,
{
	let size = size_of_val(r);

	let metadata = U::classify_size(size)
		.expect("cannot transmute reference with incompatible object size");

	transmute_mut_with_metadata(r, metadata)
}

/// Transmutes a mutable reference to another type,
/// using predefined metadata.
///
/// # Panics
///
/// This function will panic if the provied object
/// is not sufficiently large to read a `U` object
/// with the specified metadata.
#[inline]
#[must_use]
pub fn transmute_ref_with_metadata<T, U>(r: &T, metadata: U::Metadata) -> &U
where
	T: ?Sized + IntoOctets + Immutable,
	U: ?Sized + FromOctets + Immutable + Outlay,
{
	// Construct a layout with respect to the provided
	// metadata.
	let Ok(layout) = U::layout_for_metadata(metadata) else {
		panic!("cannot construct layout for dst with metadata `{metadata:?}`");
	};

	// Test that the buffer is sufficiently large.
	assert!(
		size_of_val(r) >= layout.size(),
		"cannot construct reference from undersized octets",
	);

	let data = ptr::from_ref(r).cast::<u8>();

	let p = U::ptr_from_raw_parts(data, metadata);

	// Test that the buffer is sufficiently aligned.
	assert_eq!(
		p.addr() % layout.align(),
		0,
		"cannot construct reference from unaligned octets",
	);

	// SAFETY:
	//
	// * We have tested that original object is aligned
	//   to the requirements of `U`.
	// * The pointer derives from a reference and can
	//   therefore not be null.
	// * We have tested that the size of the object is
	//   contained within the provided octets.
	// * The reference inherits the exclusivity of the
	//   original reference.
	// * The returned reference inherits the lifetime
	//   of the original reference.
	// * The `T: Init` bound requires that the destina-
	//   tion object is already in an initialised
	//   state, with the `U: FromOctets` bound requir-
	//   ing that any such state is valid for `U`.
	//
	// Furthermore, writes cannot be made to the desti-
	// nation at all thanks to the `T: Immutable` and
	// `U: Immutable` bounds.
	unsafe { &*p }
}

/// Transmutes a mutable reference to another type,
/// using predefined metadata.
///
/// # Panics
///
/// This function will panic if the provied object
/// is not sufficiently large to read a `U` object
/// with the specified metadata.
#[inline]
#[must_use]
#[track_caller]
pub fn transmute_mut_with_metadata<T, U>(r: &mut T, metadata: U::Metadata) -> &mut U
where
	T: ?Sized + IntoOctets + FromOctets,
	U: ?Sized + IntoOctets + FromOctets + Outlay,
{
	// Construct a layout with respect to the provided
	// metadata.
	let Ok(layout) = U::layout_for_metadata(metadata) else {
		panic!("cannot construct layout for dst with metadata `{metadata:?}`");
	};

	// Test that the buffer is sufficiently large.
	assert!(
		size_of_val(r) >= layout.size(),
		"cannot transmute reference from undersized object",
	);

	let data = ptr::from_mut(r).cast::<u8>();

	let p = U::ptr_from_raw_parts_mut(data, metadata);

	// Test that the buffer is sufficiently aligned.
	assert_eq!(
		p.addr() % layout.align(),
		0,
		"cannot transmute reference with increasing alignment",
	);

	// SAFETY:
	//
	// * We have tested that original object is aligned
	//   to the requirements of `U`.
	// * The pointer derives from a reference and can
	//   therefore not be null.
	// * We have tested that the size of the object is
	//   contained within the provided octets.
	// * The reference inherits the exclusivity of the
	//   original reference.
	// * The returned reference inherits the lifetime
	//   of the original reference.
	// * The `T: Init` bound requires that the destina-
	//   tion object is already in an initialised
	//   state, with the `U: FromOctets` bound requir-
	//   ing that any such state is valid for `U`.
	unsafe { &mut *p }
}