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
/*! Memory element descriptions.

This module describes memory integers and processor registers used to hold and
manipulate [`bitvec`] data buffers.

The [`BitMemory`] trait adds descriptive information to the unsigned integers
available in the language.

The [`BitRegister`] trait marks the unsigned integers that correspond to
processor registers, and can therefore be used for buffer control. The integers
that are `BitMemory` but not `BitRegister` can be composed out of register
values, but are unable to be used in buffer type parameters.

[`BitMemory`]: crate::mem::BitMemory
[`BitRegister`]: crate::mem::BitRegister
[`bitvec`]: crate
!*/

use core::mem;

use funty::IsUnsigned;
use radium::marker::BitOps;

/** Description of an integer memory element.

This trait provides information used to describe integer-typed regions of memory
and enables other parts of the project to adequately describe the memory bus.
This trait has **no** bearing on the processor instructions or registers used to
interact with memory. It solely describes integers that can exist on a system.

This trait cannot be implemented outside this crate.
**/
pub trait BitMemory: IsUnsigned + seal::Sealed {
	/// The number of bits required to store an index in the range `0 .. BITS`.
	const INDX: u8 = Self::BITS.trailing_zeros() as u8;

	/// A mask over all bits that can be used as an index within the element.
	/// This is the value with the least significant `INDX`-many bits set high.
	const MASK: u8 = Self::BITS as u8 - 1;
}

/** Description of a processor register.

This trait provides information used to describe processor registers. It only
needs to contain constant values for `1` and `!0`; the rest of its information
is contained in the presence or absence of its implementation on particular
integers.
**/
pub trait BitRegister: BitMemory + BitOps {
	/// The literal `1`.
	const ONE: Self;
	/// The literal `!0`.
	const ALL: Self;
}

macro_rules! memory {
	($($t:ident),+ $(,)?) => { $(
		impl BitMemory for $t {}
		impl seal::Sealed for $t {}
	)+ };
}

memory!(u8, u16, u32, u64, u128, usize);

macro_rules! register {
	($($t:ident),+ $(,)?) => { $(
		impl BitRegister for $t {
			const ONE: Self = 1;
			const ALL: Self = !0;
		}
	)+ };
}

register!(u8, u16, u32);

/** `u64` can only be used as a register on processors whose word size is at
least 64 bits.

This implementation is not present on targets with 32-bit processor words.
**/
#[cfg(target_pointer_width = "64")]
impl BitRegister for u64 {
	const ALL: Self = !0;
	const ONE: Self = 1;
}

register!(usize);

/** Computes the number of elements required to store some number of bits.

# Parameters

- `bits`: The number of bits to store in a `[T]` array.

# Returns

The number of elements `T` required to store `bits`.

As this is a const function, when `bits` is a constant expression, this can be
used to compute the size of an array type `[T; elts(bits)]`.
**/
#[doc(hidden)]
pub const fn elts<T>(bits: usize) -> usize {
	let width = mem::size_of::<T>() * 8;
	bits / width + (bits % width != 0) as usize
}

/** Tests that a type is aligned to at least its size.

This property is not necessarily true for all integers; for instance, `u64` on
32-bit x86 is permitted to be 4-byte-aligned. `bitvec` requires this property to
hold for the pointer representation to correctly function.

# Type Parameters

- `T`: A type whose alignment and size are to be compared

# Returns

`0` if the alignment is at least the size; `1` if the alignment is less.
**/
#[doc(hidden)]
pub(crate) const fn aligned_to_size<T>() -> usize {
	(mem::align_of::<T>() < mem::size_of::<T>()) as usize
}

/** Tests whether two types have compatible layouts.

# Type Parameters

- `A`
- `B`

# Returns

Zero if `A` and `B` have equal alignments and sizes, non-zero if they do not.

# Uses

This function is designed to be used in the expression
`const CHECK: [(): 0] = [(); cmp_layout::<A, B>()];`. It will cause a compiler
error if the conditions do not hold.
**/
#[doc(hidden)]
pub(crate) const fn cmp_layout<A, B>() -> usize {
	(mem::align_of::<A>() != mem::align_of::<B>()) as usize
		+ (mem::size_of::<A>() != mem::size_of::<B>()) as usize
}

#[doc(hidden)]
mod seal {
	#[doc(hidden)]
	pub trait Sealed {}
}

#[cfg(test)]
mod tests {
	use super::*;
	use crate::access::*;

	#[test]
	fn integer_properties() {
		assert_eq!(aligned_to_size::<u8>(), 0);
		assert_eq!(aligned_to_size::<BitSafeU8>(), 0);
		assert_eq!(cmp_layout::<u8, BitSafeU8>(), 0);

		assert_eq!(aligned_to_size::<u16>(), 0);
		assert_eq!(aligned_to_size::<BitSafeU16>(), 0);
		assert_eq!(cmp_layout::<u16, BitSafeU16>(), 0);

		assert_eq!(aligned_to_size::<u32>(), 0);
		assert_eq!(aligned_to_size::<BitSafeU32>(), 0);
		assert_eq!(cmp_layout::<u32, BitSafeU32>(), 0);

		assert_eq!(aligned_to_size::<usize>(), 0);
		assert_eq!(aligned_to_size::<BitSafeUsize>(), 0);
		assert_eq!(cmp_layout::<usize, BitSafeUsize>(), 0);

		#[cfg(target_pointer_width = "64")]
		{
			assert_eq!(aligned_to_size::<u64>(), 0);
			assert_eq!(aligned_to_size::<BitSafeU64>(), 0);
			assert_eq!(cmp_layout::<u64, BitSafeU64>(), 0);
		}
	}
}