Skip to content

Commit

Permalink
Merge branch 'main' into overflowing_shr
Browse files Browse the repository at this point in the history
  • Loading branch information
prestwich authored Feb 27, 2024
2 parents ee8ad40 + 4f2b31d commit c9f9fa4
Show file tree
Hide file tree
Showing 17 changed files with 176 additions and 97 deletions.
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[package]
name = "ruint"
description = "Unsigned integer type with const-generic bit length"
version = "1.11.1"
version = "1.12.0"
keywords = ["uint"]
categories = ["mathematics"]
exclude = ["benches/", "proptest-regressions/", "tests/"]
include = ["src/**/*.rs", "README.md", "ruint-macro/README.md"]
readme = "README.md"

edition.workspace = true
Expand Down Expand Up @@ -37,7 +37,7 @@ path = "benches/bench.rs"
required-features = ["std"]

[dependencies]
ruint-macro = { version = "1.1.0", path = "ruint-macro" }
ruint-macro = { version = "1.2.0", path = "ruint-macro" }

thiserror = { version = "1.0", optional = true }

Expand Down Expand Up @@ -108,7 +108,7 @@ std = [
"rlp?/std",
"serde?/std",
"valuable?/std",
"zeroize?/std"
"zeroize?/std",
]
ssz = ["std", "dep:ethereum_ssz"]
alloc = ["proptest?/alloc", "rand?/alloc", "serde?/alloc", "valuable?/alloc", "zeroize?/alloc"]
Expand Down
4 changes: 2 additions & 2 deletions ruint-macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "ruint-macro"
description = "The `uint!` macro for `Uint` literals"
version = "1.1.0"
description = "The `uint!` macro for `Uint` and `Bits` literals"
version = "1.2.0"
keywords = ["uint", "macro"]
categories = ["mathematics"]
readme = "README.md"
Expand Down
1 change: 0 additions & 1 deletion ruint-macro/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ error: Value too large for Uint<8>: 300
| ^^^^^^
```


## References

* Rust [integer literals syntax](https://doc.rust-lang.org/stable/reference/tokens.html#integer-literals).
183 changes: 111 additions & 72 deletions ruint-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,36 @@
#![doc = include_str!("../README.md")]
#![warn(clippy::all, clippy::pedantic, clippy::cargo, clippy::nursery)]

use core::{
fmt::{Display, Formatter, Write},
str::FromStr,
};
use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
use std::fmt::{self, Write};

// Repeat the crate doc.
#[doc = include_str!("../README.md")]
#[proc_macro]
pub fn uint(stream: TokenStream) -> TokenStream {
Transformer::new(None).transform_stream(stream)
}

/// Same as [`uint`], but with the first token always being a
/// [group](proc_macro::Group) containing the `ruint` crate path.
///
/// This allows the macro to be used in a crates that don't on `ruint` through a
/// wrapper `macro_rules!` that passes `$crate` as the path.
///
/// This is an implementation detail and should not be used directly.
#[proc_macro]
#[doc(hidden)]
pub fn uint_with_path(stream: TokenStream) -> TokenStream {
let mut stream_iter = stream.into_iter();
let Some(TokenTree::Group(group)) = stream_iter.next() else {
return error(
Span::call_site(),
"Expected a group containing the `ruint` crate path",
)
.into();
};
Transformer::new(Some(group.stream())).transform_stream(stream_iter.collect())
}

#[derive(Copy, Clone, PartialEq, Debug)]
enum LiteralBaseType {
Expand All @@ -17,16 +42,16 @@ impl LiteralBaseType {
const PATTERN: &'static [char] = &['U', 'B'];
}

impl Display for LiteralBaseType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
impl fmt::Display for LiteralBaseType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Uint => f.write_str("Uint"),
Self::Bits => f.write_str("Bits"),
}
}
}

impl FromStr for LiteralBaseType {
impl std::str::FromStr for LiteralBaseType {
type Err = ();

fn from_str(s: &str) -> Result<Self, Self::Err> {
Expand All @@ -38,18 +63,6 @@ impl FromStr for LiteralBaseType {
}
}

/// Construct a `<{base_type}><{bits}>` literal from `limbs`.
fn construct(base_type: LiteralBaseType, bits: usize, limbs: &[u64]) -> TokenStream {
let mut limbs_str = String::new();
for limb in limbs {
write!(&mut limbs_str, "0x{limb:016x}_u64, ").unwrap();
}
let limbs_str = limbs_str.trim_end_matches(", ");
let limbs = (bits + 63) / 64;
let source = format!("::ruint::{base_type}::<{bits}, {limbs}>::from_limbs([{limbs_str}])");
TokenStream::from_str(&source).unwrap()
}

/// Construct a compiler error message.
// FEATURE: (BLOCKED) Replace with Diagnostic API when stable.
// See <https://doc.rust-lang.org/stable/proc_macro/struct.Diagnostic.html>
Expand Down Expand Up @@ -162,66 +175,92 @@ fn parse_suffix(source: &str) -> Option<(LiteralBaseType, usize, &str)> {
Some((base_type, bits, value))
}

/// Transforms a [`Literal`] and returns the substitute [`TokenStream`].
fn transform_literal(source: &str) -> Result<Option<TokenStream>, String> {
// Check if literal has a suffix we accept
let Some((base_type, bits, value)) = parse_suffix(source) else {
return Ok(None);
};

// Parse `value` into limbs.
// At this point we are confident the literal was for us, so we throw errors.
let limbs = parse_digits(value)?;

// Pad limbs to the correct length.
let Some(limbs) = pad_limbs(bits, limbs) else {
let value = value.trim_end_matches('_');
return Err(format!("Value too large for {base_type}<{bits}>: {value}"));
};

Ok(Some(construct(base_type, bits, &limbs)))
struct Transformer {
/// The `ruint` crate path.
/// Note that this stream's span must be used in order for the `$crate` to
/// work.
ruint_crate: TokenStream,
}

/// Recurse down tree and transform all literals.
fn transform_tree(tree: TokenTree) -> TokenTree {
match tree {
TokenTree::Group(group) => {
let delimiter = group.delimiter();
let span = group.span();
let stream = transform_stream(group.stream());
let mut transformed = Group::new(delimiter, stream);
transformed.set_span(span);
TokenTree::Group(transformed)
impl Transformer {
fn new(ruint_crate: Option<TokenStream>) -> Self {
Self {
ruint_crate: ruint_crate.unwrap_or_else(|| "::ruint".parse().unwrap()),
}
TokenTree::Literal(a) => {
let span = a.span();
let source = a.to_string();
let mut tree = match transform_literal(&source) {
Ok(Some(stream)) => TokenTree::Group({
let mut group = Group::new(Delimiter::None, stream);
group.set_span(span);
group
}),
Ok(None) => TokenTree::Literal(a),
Err(message) => error(span, &message),
};
tree.set_span(span);
tree
}

/// Construct a `<{base_type}><{bits}>` literal from `limbs`.
fn construct(&self, base_type: LiteralBaseType, bits: usize, limbs: &[u64]) -> TokenStream {
let mut limbs_str = String::new();
for limb in limbs {
write!(&mut limbs_str, "0x{limb:016x}_u64, ").unwrap();
}
tree => tree,
let limbs_str = limbs_str.trim_end_matches(", ");
let limbs = (bits + 63) / 64;
let source = format!("::{base_type}::<{bits}, {limbs}>::from_limbs([{limbs_str}])");

let mut tokens = self.ruint_crate.clone();
tokens.extend(source.parse::<TokenStream>().unwrap());
tokens
}
}

/// Iterate over a [`TokenStream`] and transform all [`TokenTree`]s.
fn transform_stream(stream: TokenStream) -> TokenStream {
stream.into_iter().map(transform_tree).collect()
}
/// Transforms a [`Literal`] and returns the substitute [`TokenStream`].
fn transform_literal(&self, source: &str) -> Result<Option<TokenStream>, String> {
// Check if literal has a suffix we accept.
let Some((base_type, bits, value)) = parse_suffix(source) else {
return Ok(None);
};

// Repeat the crate doc
#[doc = include_str!("../README.md")]
#[proc_macro]
pub fn uint(stream: TokenStream) -> TokenStream {
transform_stream(stream)
// Parse `value` into limbs.
// At this point we are confident the literal was for us, so we throw errors.
let limbs = parse_digits(value)?;

// Pad limbs to the correct length.
let Some(limbs) = pad_limbs(bits, limbs) else {
let value = value.trim_end_matches('_');
return Err(format!("Value too large for {base_type}<{bits}>: {value}"));
};

Ok(Some(self.construct(base_type, bits, &limbs)))
}

/// Recurse down tree and transform all literals.
fn transform_tree(&self, tree: TokenTree) -> TokenTree {
match tree {
TokenTree::Group(group) => {
let delimiter = group.delimiter();
let span = group.span();
let stream = self.transform_stream(group.stream());
let mut transformed = Group::new(delimiter, stream);
transformed.set_span(span);
TokenTree::Group(transformed)
}
TokenTree::Literal(a) => {
let span = a.span();
let source = a.to_string();
let mut tree = match self.transform_literal(&source) {
Ok(Some(stream)) => TokenTree::Group({
let mut group = Group::new(Delimiter::None, stream);
group.set_span(span);
group
}),
Ok(None) => TokenTree::Literal(a),
Err(message) => error(span, &message),
};
tree.set_span(span);
tree
}
tree => tree,
}
}

/// Iterate over a [`TokenStream`] and transform all [`TokenTree`]s.
fn transform_stream(&self, stream: TokenStream) -> TokenStream {
stream
.into_iter()
.map(|tree| self.transform_tree(tree))
.collect()
}
}

#[cfg(test)]
Expand Down
4 changes: 3 additions & 1 deletion src/algorithms/div/knuth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,15 @@ pub fn div_nxm(numerator: &mut [u64], divisor: &mut [u64]) {
mod tests {
use super::*;
use crate::algorithms::{addmul, cmp, sbb_n};
use alloc::vec::Vec;
use core::cmp::Ordering;
use proptest::{
collection, num, proptest,
strategy::{Just, Strategy},
};

#[allow(unused_imports)]
use alloc::vec::Vec;

// Basic test without exceptional paths
#[test]
fn test_divrem_8by4() {
Expand Down
1 change: 1 addition & 0 deletions src/bit_arr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use core::{
};

#[cfg(feature = "alloc")]
#[allow(unused_imports)]
use alloc::{borrow::Cow, vec::Vec};

/// A newtype wrapper around [`Uint`] that restricts operations to those
Expand Down
1 change: 1 addition & 0 deletions src/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::Uint;
use core::slice;

#[cfg(feature = "alloc")]
#[allow(unused_imports)]
use alloc::{borrow::Cow, vec::Vec};

// OPT: *_to_smallvec to avoid allocation.
Expand Down
2 changes: 1 addition & 1 deletion src/cmp.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::Uint;
use core::cmp::{Ord, Ordering, PartialOrd};
use core::cmp::Ordering;

impl<const BITS: usize, const LIMBS: usize> Ord for Uint<BITS, LIMBS> {
#[inline]
Expand Down
14 changes: 7 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,13 @@
// Unstable features
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
#![cfg_attr(feature = "nightly", allow(internal_features))]
#![cfg_attr(
feature = "generic_const_exprs",
feature(generic_const_exprs),
allow(incomplete_features)
)]

// Workaround for proc-macro `uint!` in this crate.
// See <https://github.com/rust-lang/rust/pull/55275>
extern crate self as ruint;

#[cfg(feature = "alloc")]
#[macro_use]
extern crate alloc;
Expand Down Expand Up @@ -76,9 +73,6 @@ pub use self::{
string::ParseError,
};

#[doc(inline)]
pub use ruint_macro::uint;

#[cfg(feature = "generic_const_exprs")]
pub mod nightly {
//! Extra features that are nightly only.
Expand Down Expand Up @@ -331,6 +325,12 @@ pub const fn mask(bits: usize) -> u64 {
}
}

// Not public API.
#[doc(hidden)]
pub mod __private {
pub use ruint_macro;
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
26 changes: 26 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
#[allow(rustdoc::broken_intra_doc_links)]
#[doc = include_str!("../ruint-macro/README.md")]
#[macro_export]
macro_rules! uint {
($($t:tt)*) => {
$crate::__private::ruint_macro::uint_with_path!([$crate] $($t)*)
}
}

macro_rules! impl_bin_op {
($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident, $fdel:ident) => {
impl<const BITS: usize, const LIMBS: usize> $trait_assign<Uint<BITS, LIMBS>>
Expand Down Expand Up @@ -64,3 +73,20 @@ macro_rules! impl_bin_op {
}
};
}

#[cfg(test)]
mod tests {
#[test]
fn test_uint_macro_with_paths() {
extern crate self as aaa;
use crate as ruint;
use crate as __ruint;
let value = crate::aliases::U256::from(0x10);
assert_eq!(value, uint!(0x10U256));
assert_eq!(value, ruint_macro::uint_with_path!([crate] 0x10U256));
assert_eq!(value, ruint_macro::uint_with_path!([aaa] 0x10U256));
assert_eq!(value, ruint_macro::uint_with_path!([aaa] 0x10U256));
assert_eq!(value, ruint_macro::uint_with_path!([ruint] 0x10U256));
assert_eq!(value, ruint_macro::uint_with_path!([__ruint] 0x10U256));
}
}
2 changes: 1 addition & 1 deletion src/mul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ impl_bin_op!(Mul, mul, MulAssign, mul_assign, wrapping_mul);
#[cfg(test)]
mod tests {
use super::*;
use crate::{const_for, nlimbs};
use crate::const_for;
use proptest::proptest;

#[test]
Expand Down
Loading

0 comments on commit c9f9fa4

Please sign in to comment.