[PATCH RFC 1/3] rust: add useful ops for u64
Alexandre Courbot
acourbot at nvidia.com
Tue Feb 18 13:16:53 UTC 2025
Hi Daniel!
On Tue Feb 18, 2025 at 6:10 AM JST, Daniel Almeida wrote:
> Hi Alex,
>
>> On 17 Feb 2025, at 11:04, Alexandre Courbot <acourbot at nvidia.com> wrote:
>>
>> It is common to build a u64 from its high and low parts obtained from
>> two 32-bit registers. Conversely, it is also common to split a u64 into
>> two u32s to write them into registers. Add an extension trait for u64
>> that implement these methods in a new `num` module.
>
> Thank you for working on that. I find myself doing this manually extremely often indeed.
Are you aware of existing upstream code that could benefit from this?
This would allow me to split that patch out of this series.
>
>
>>
>> It is expected that this trait will be extended with other useful
>> operations, and similar extension traits implemented for other types.
>>
>> Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
>> ---
>> rust/kernel/lib.rs | 1 +
>> rust/kernel/num.rs | 32 ++++++++++++++++++++++++++++++++
>> 2 files changed, 33 insertions(+)
>>
>> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
>> index 496ed32b0911a9fdbce5d26738b9cf7ef910b269..8c0c7c20a16aa96e3d3e444be3e03878650ddf77 100644
>> --- a/rust/kernel/lib.rs
>> +++ b/rust/kernel/lib.rs
>> @@ -59,6 +59,7 @@
>> pub mod miscdevice;
>> #[cfg(CONFIG_NET)]
>> pub mod net;
>> +pub mod num;
>> pub mod of;
>> pub mod page;
>> #[cfg(CONFIG_PCI)]
>> diff --git a/rust/kernel/num.rs b/rust/kernel/num.rs
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..5e714cbda4575b8d74f50660580dc4c5683f8c2b
>> --- /dev/null
>> +++ b/rust/kernel/num.rs
>> @@ -0,0 +1,32 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +//! Numerical and binary utilities for primitive types.
>> +
>> +/// Useful operations for `u64`.
>> +pub trait U64Ext {
>> + /// Build a `u64` by combining its `high` and `low` parts.
>> + ///
>> + /// ```
>> + /// use kernel::num::U64Ext;
>> + /// assert_eq!(u64::from_u32s(0x01234567, 0x89abcdef), 0x01234567_89abcdef);
>> + /// ```
>> + fn from_u32s(high: u32, low: u32) -> Self;
>> +
>> + /// Returns the `(high, low)` u32s that constitute `self`.
>> + ///
>> + /// ```
>> + /// use kernel::num::U64Ext;
>> + /// assert_eq!(u64::into_u32s(0x01234567_89abcdef), (0x1234567, 0x89abcdef));
>> + /// ```
>> + fn into_u32s(self) -> (u32, u32);
>> +}
>> +
>> +impl U64Ext for u64 {
>> + fn from_u32s(high: u32, low: u32) -> Self {
>> + ((high as u64) << u32::BITS) | low as u64
>> + }
>> +
>> + fn into_u32s(self) -> (u32, u32) {
>
> I wonder if a struct would make more sense here.
>
> Just recently I had to debug an issue where I forgot the
> right order for code I had just written. Something like:
>
> let (pgcount, pgsize) = foo(); where the function actually
> returned (pgsize, pgcount).
>
> A proper struct with `high` and `low` might be more verbose, but
> it rules out this issue.
Mmm indeed, so we would have client code looking like:
let SplitU64 { high, low } = some_u64.into_u32();
instead of
let (high, low) = some_u64.into_u32();
which is correct, and
let (low, high) = some_u64.into_u32();
which is incorrect, but is likely to not be caught.
Since the point of these methods is to avoid potential errors in what
otherwise appears to be trivial code, I agree it would be better to
avoid introducing a new trap because the elements of the returned tuple
are not clearly named. Not sure which is more idiomatic here.
More information about the Nouveau
mailing list