Skip to content

Commit

Permalink
[byteorder] Make some methods const
Browse files Browse the repository at this point in the history
For methods which have generic types with trait bounds (other than
`Sized`), conditionally compile as `const` only on Rust 1.61.0 or later.
  • Loading branch information
joshlf committed Feb 8, 2024
1 parent 8fd4a7e commit 84a8f32
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 21 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
matrix:
# See `INTERNAL.md` for an explanation of these pinned toolchain
# versions.
toolchain: [ "msrv", "stable", "nightly", "zerocopy-aarch64-simd" ]
toolchain: [ "msrv", "stable", "nightly", "zerocopy-aarch64-simd", "zerocopy-generic-bounds-in-const-fn" ]
target: [
"i686-unknown-linux-gnu",
"x86_64-unknown-linux-gnu",
Expand All @@ -61,6 +61,8 @@ jobs:
features: "--all-features"
- toolchain: "zerocopy-aarch64-simd"
features: "--all-features"
- toolchain: "zerocopy-generic-bounds-in-const-fn"
features: "--all-features"
# Exclude any combination for the zerocopy-derive crate which
# uses zerocopy features.
- crate: "zerocopy-derive"
Expand All @@ -75,6 +77,8 @@ jobs:
# zerocopy-derive doesn't behave different on these toolchains.
- crate: "zerocopy-derive"
toolchain: "zerocopy-aarch64-simd"
- crate: "zerocopy-derive"
toolchain: "zerocopy-generic-bounds-in-const-fn"

name: Build & Test (crate:${{ matrix.crate }}, toolchain:${{ matrix.toolchain }}, target:${{ matrix.target }}, features:${{ matrix.features }})

Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ exclude = [".*"]
# versions, these types require the "simd-nightly" feature.
zerocopy-aarch64-simd = "1.59.0"

# From 1.61.0, Rust supports generic types with trait bounds in `const fn`.
zerocopy-generic-bounds-in-const-fn = "1.61.0"

[package.metadata.ci]
# The versions of the stable and nightly compiler toolchains to use in CI.
pinned-stable = "1.75.0"
Expand Down
3 changes: 2 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,14 @@ fn main() {
}
}

#[derive(Ord, PartialEq, PartialOrd, Eq)]
#[derive(Debug, Ord, PartialEq, PartialOrd, Eq)]
struct Version {
major: usize,
minor: usize,
patch: usize,
}

#[derive(Debug)]
struct VersionCfg {
version: Version,
cfg_name: String,
Expand Down
55 changes: 36 additions & 19 deletions src/byteorder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,40 +401,45 @@ example of how it can be used for parsing UDP packets.
/// Constructs a new value from bytes which are already in `O` byte
/// order.
#[inline(always)]
pub fn from_bytes(bytes: [u8; $bytes]) -> $name<O> {
pub const fn from_bytes(bytes: [u8; $bytes]) -> $name<O> {
$name(bytes, PhantomData)
}

/// Extracts the bytes of `self` without swapping the byte order.
///
/// The returned bytes will be in `O` byte order.
#[inline(always)]
pub fn to_bytes(self) -> [u8; $bytes] {
pub const fn to_bytes(self) -> [u8; $bytes] {
self.0
}
}

impl<O: ByteOrder> $name<O> {
/// Constructs a new value, possibly performing an endianness swap
/// to guarantee that the returned value has endianness `O`.
#[inline(always)]
pub fn new(n: $native) -> $name<O> {
let bytes = match O::ORDER {
Order::BigEndian => $to_be_fn(n),
Order::LittleEndian => $to_le_fn(n),
};
maybe_const_trait_bounded_fn! {
/// Constructs a new value, possibly performing an endianness
/// swap to guarantee that the returned value has endianness
/// `O`.
#[inline(always)]
pub const fn new(n: $native) -> $name<O> {
let bytes = match O::ORDER {
Order::BigEndian => $to_be_fn(n),
Order::LittleEndian => $to_le_fn(n),
};

$name(bytes, PhantomData)
$name(bytes, PhantomData)
}
}

/// Returns the value as a primitive type, possibly performing an
/// endianness swap to guarantee that the return value has the
/// endianness of the native platform.
#[inline(always)]
pub fn get(self) -> $native {
match O::ORDER {
Order::BigEndian => $from_be_fn(self.0),
Order::LittleEndian => $from_le_fn(self.0),
maybe_const_trait_bounded_fn! {
/// Returns the value as a primitive type, possibly performing
/// an endianness swap to guarantee that the return value has
/// the endianness of the native platform.
#[inline(always)]
pub const fn get(self) -> $native {
match O::ORDER {
Order::BigEndian => $from_be_fn(self.0),
Order::LittleEndian => $from_le_fn(self.0),
}
}
}

Expand Down Expand Up @@ -1123,6 +1128,18 @@ mod tests {
1024
};

#[test]
fn test_const_methods() {
use big_endian::*;

#[rustversion::since(1.61.0)]
const _U: U16 = U16::new(0);
#[rustversion::since(1.61.0)]
const _NATIVE: u16 = _U.get();
const _FROM_BYTES: U16 = U16::from_bytes([0, 1]);
const _BYTES: [u8; 2] = _FROM_BYTES.to_bytes();
}

#[cfg_attr(test, test)]
#[cfg_attr(kani, kani::proof)]
fn test_zero() {
Expand Down
16 changes: 16 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,3 +416,19 @@ macro_rules! assert_unaligned {
$(assert_unaligned!($ty);)*
};
}

/// Emits a function definition as either `const fn` or `fn` depending on
/// whether the current toolchain version supports `const fn` with generic trait
/// bounds.
macro_rules! maybe_const_trait_bounded_fn {
// This case handles both `self` methods (where `self` is by value) and
// non-method functions. Each `$args` may optionally be followed by `:
// $arg_tys:ty`, which can be omitted for `self`.
($(#[$attr:meta])* $vis:vis const fn $name:ident($($args:ident $(: $arg_tys:ty)?),* $(,)?) $(-> $ret_ty:ty)? $body:block) => {
#[cfg(zerocopy_generic_bounds_in_const_fn)]
$(#[$attr])* $vis const fn $name($($args $(: $arg_tys)?),*) $(-> $ret_ty)? $body

#[cfg(not(zerocopy_generic_bounds_in_const_fn))]
$(#[$attr])* $vis fn $name($($args $(: $arg_tys)?),*) $(-> $ret_ty)? $body
};
}

0 comments on commit 84a8f32

Please sign in to comment.