From cf74d227b352e7c666c1d4e529b9655405eddbc0 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 27 Jul 2023 18:05:13 +0200 Subject: [PATCH] feat: no_std --- CHANGELOG.md | 6 +++ Cargo.toml | 72 ++++++++++++++++++++----------- benches/benches/algorithms/gcd.rs | 2 +- benches/benches/log.rs | 4 ++ benches/benches/root.rs | 4 ++ ruint-macro/src/lib.rs | 4 +- src/algorithms/div/knuth.rs | 3 +- src/algorithms/div/reciprocal.rs | 2 +- src/algorithms/gcd/matrix.rs | 2 +- src/algorithms/gcd/mod.rs | 2 +- src/base_convert.rs | 26 ++++++++--- src/bit_arr.rs | 11 +++-- src/bytes.rs | 2 +- src/from.rs | 66 +++++++++++++++++++++++----- src/lib.rs | 5 +++ src/log.rs | 2 + src/pow.rs | 3 +- src/root.rs | 2 + src/string.rs | 41 ++++++++++++++---- src/support/arbitrary.rs | 3 +- src/support/pyo3.rs | 2 +- src/support/rlp.rs | 2 +- src/support/serde.rs | 6 ++- src/utils.rs | 2 + 24 files changed, 207 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eaa5ca..4795938 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Support for `no_std` environments (#274) + +[#274]: https://github.com/recmo/uint/pulls/274 + ## [1.9.0] - 2023-07-25 ### Added diff --git a/Cargo.toml b/Cargo.toml index 3bf3e3f..f4b7886 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,26 +42,29 @@ path = "benches/bench.rs" [dependencies] ruint-macro.workspace = true -thiserror = "1.0" +thiserror = { version = "1.0", optional = true } # support -alloy-rlp = { version = "0.3", optional = true } -arbitrary = { version = "1", optional = true } -ark-ff-04 = { version = "0.4.0", package = "ark-ff", optional = true } -ark-ff-03 = { version = "0.3.0", package = "ark-ff", optional = true } -bn-rs = { version = "0.2", optional = true } -fastrlp = { version = "0.3", optional = true } -num-bigint = { version = "0.4", optional = true } -parity-scale-codec = { version = "3", optional = true, features = ["derive", "max-encoded-len"] } -primitive-types = { version = "0.12", optional = true } -proptest = { version = "1", optional = true } -pyo3 = { version = "0.19", optional = true } -quickcheck = { version = "1", optional = true } -rand = { version = "0.8", optional = true } -rlp = { version = "0.5", optional = true } -serde = { version = "1", optional = true } -valuable = { version = "0.1", optional = true } -zeroize = { version = "1.6", optional = true } +alloy-rlp = { version = "0.3", optional = true, default-features = false } +arbitrary = { version = "1", optional = true, default-features = false } +ark-ff-03 = { version = "0.3.0", package = "ark-ff", optional = true, default-features = false } +ark-ff-04 = { version = "0.4.0", package = "ark-ff", optional = true, default-features = false } +bn-rs = { version = "0.2", optional = true, default-features = true } +fastrlp = { version = "0.3", optional = true, default-features = false } +num-bigint = { version = "0.4", optional = true, default-features = false } +parity-scale-codec = { version = "3", optional = true, features = [ + "derive", + "max-encoded-len", +], default-features = false } +primitive-types = { version = "0.12", optional = true, default-features = false } +proptest = { version = "1", optional = true, default-features = false } +pyo3 = { version = "0.19", optional = true, default-features = false } +quickcheck = { version = "1", optional = true, default-features = false } +rand = { version = "0.8", optional = true, default-features = false } +rlp = { version = "0.5", optional = true, default-features = false } +serde = { version = "1", optional = true, default-features = false } +valuable = { version = "0.1", optional = true, default-features = false } +zeroize = { version = "1.6", optional = true, default-features = false } # postgres bytes = { version = "1.4", optional = true } @@ -73,8 +76,8 @@ sqlx-core = { version = "0.7", optional = true } [dev-dependencies] ruint = { workspace = true, features = ["arbitrary", "proptest"] } -ark-bn254-04 = { version = "0.4.0", package = "ark-bn254" } ark-bn254-03 = { version = "0.3.0", package = "ark-bn254" } +ark-bn254-04 = { version = "0.4.0", package = "ark-bn254" } criterion = "0.5" rand = "0.8" @@ -89,28 +92,47 @@ proptest = "1.2" serde_json = "1.0" [features] +default = ["std"] +std = [ + "thiserror", + "alloy-rlp?/std", + "ark-ff-03?/std", + "ark-ff-04?/std", + "bytes?/std", + "fastrlp?/std", + "num-bigint?/std", + "parity-scale-codec?/std", + "primitive-types?/std", + "proptest?/std", + "rand?/std", + "rlp?/std", + "serde?/std", + "valuable?/std", + "zeroize?/std", +] + # nightly-only features nightly = [] generic_const_exprs = ["nightly"] # support alloy-rlp = ["dep:alloy-rlp"] -arbitrary = ["dep:arbitrary"] +arbitrary = ["dep:arbitrary", "std"] ark-ff = ["dep:ark-ff-03"] ark-ff-04 = ["dep:ark-ff-04"] -bn-rs = ["dep:bn-rs"] +bn-rs = ["dep:bn-rs", "std"] fastrlp = ["dep:fastrlp"] num-bigint = ["dep:num-bigint"] parity-scale-codec = ["dep:parity-scale-codec"] primitive-types = ["dep:primitive-types"] proptest = ["dep:proptest"] -pyo3 = ["dep:pyo3"] -quickcheck = ["dep:quickcheck"] +pyo3 = ["dep:pyo3", "std"] +quickcheck = ["dep:quickcheck", "std"] rand = ["dep:rand"] rlp = ["dep:rlp"] serde = ["dep:serde"] valuable = ["dep:valuable"] zeroize = ["dep:zeroize"] -postgres = ["dep:postgres-types", "dep:bytes"] -sqlx = ["dep:sqlx-core"] +postgres = ["dep:postgres-types", "dep:bytes", "std"] +sqlx = ["dep:sqlx-core", "std"] diff --git a/benches/benches/algorithms/gcd.rs b/benches/benches/algorithms/gcd.rs index 7477831..19004dd 100644 --- a/benches/benches/algorithms/gcd.rs +++ b/benches/benches/algorithms/gcd.rs @@ -1,6 +1,6 @@ use crate::prelude::*; +use core::cmp::{max, min}; use ruint::algorithms::LehmerMatrix as Matrix; -use std::cmp::{max, min}; pub fn group(criterion: &mut Criterion) { bench_from_u64(criterion); diff --git a/benches/benches/log.rs b/benches/benches/log.rs index 5434cc8..c3bb92c 100644 --- a/benches/benches/log.rs +++ b/benches/benches/log.rs @@ -1,12 +1,16 @@ use crate::prelude::*; pub fn group(criterion: &mut Criterion) { + #[cfg(not(feature = "std"))] + let _ = criterion; + #[cfg(feature = "std")] const_for!(BITS in BENCH { const LIMBS: usize = nlimbs(BITS); bench_log::(criterion); }); } +#[cfg(feature = "std")] fn bench_log(criterion: &mut Criterion) { if BITS < 7 { return; diff --git a/benches/benches/root.rs b/benches/benches/root.rs index 7453b38..9e46c3d 100644 --- a/benches/benches/root.rs +++ b/benches/benches/root.rs @@ -1,6 +1,9 @@ use crate::prelude::*; pub fn group(criterion: &mut Criterion) { + #[cfg(not(feature = "std"))] + let _ = criterion; + #[cfg(feature = "std")] const_for!(BITS in BENCH { const LIMBS: usize = nlimbs(BITS); bench_root::(criterion, 2); @@ -10,6 +13,7 @@ pub fn group(criterion: &mut Criterion) { }); } +#[cfg(feature = "std")] fn bench_root(criterion: &mut Criterion, degree: usize) { let input = Uint::::arbitrary(); let mut runner = TestRunner::deterministic(); diff --git a/ruint-macro/src/lib.rs b/ruint-macro/src/lib.rs index f061449..e54eb16 100644 --- a/ruint-macro/src/lib.rs +++ b/ruint-macro/src/lib.rs @@ -1,11 +1,11 @@ #![doc = include_str!("../README.md")] #![warn(clippy::all, clippy::pedantic, clippy::cargo, clippy::nursery)] -use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; -use std::{ +use core::{ fmt::{Display, Formatter, Write}, str::FromStr, }; +use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; #[derive(Copy, Clone, PartialEq, Debug)] enum LiteralBaseType { diff --git a/src/algorithms/div/knuth.rs b/src/algorithms/div/knuth.rs index c7f8399..ee35ab8 100644 --- a/src/algorithms/div/knuth.rs +++ b/src/algorithms/div/knuth.rs @@ -194,11 +194,12 @@ mod tests { add::{cmp, sbb_n}, addmul, }; + use alloc::vec::Vec; + use core::cmp::Ordering; use proptest::{ collection, num, proptest, strategy::{Just, Strategy}, }; - use std::cmp::Ordering; // Basic test without exceptional paths #[test] diff --git a/src/algorithms/div/reciprocal.rs b/src/algorithms/div/reciprocal.rs index 3a1cd25..117e70d 100644 --- a/src/algorithms/div/reciprocal.rs +++ b/src/algorithms/div/reciprocal.rs @@ -6,7 +6,7 @@ //! [new]: https://gmplib.org/list-archives/gmp-devel/2019-October/005590.html #![allow(dead_code, clippy::cast_possible_truncation, clippy::cast_lossless)] -use std::num::Wrapping; +use core::num::Wrapping; pub use self::{reciprocal_2_mg10 as reciprocal_2, reciprocal_mg10 as reciprocal}; diff --git a/src/algorithms/gcd/matrix.rs b/src/algorithms/gcd/matrix.rs index 7996ec8..a5af40b 100644 --- a/src/algorithms/gcd/matrix.rs +++ b/src/algorithms/gcd/matrix.rs @@ -336,9 +336,9 @@ mod tests { use core::{ cmp::{max, min}, mem::swap, + str::FromStr, }; use proptest::{proptest, test_runner::Config}; - use std::str::FromStr; fn gcd(mut a: u128, mut b: u128) -> u128 { while b != 0 { diff --git a/src/algorithms/gcd/mod.rs b/src/algorithms/gcd/mod.rs index d4edf1c..d8773b6 100644 --- a/src/algorithms/gcd/mod.rs +++ b/src/algorithms/gcd/mod.rs @@ -193,7 +193,7 @@ mod tests { #[test] fn test_gcd_one() { - use std::str::FromStr; + use core::str::FromStr; const BITS: usize = 129; const LIMBS: usize = nlimbs(BITS); type U = Uint; diff --git a/src/base_convert.rs b/src/base_convert.rs index d86958a..4519d6b 100644 --- a/src/base_convert.rs +++ b/src/base_convert.rs @@ -1,24 +1,39 @@ use crate::Uint; -use thiserror::Error; +use alloc::vec::Vec; +use core::fmt; /// Error for [`from_base_le`][Uint::from_base_le] and /// [`from_base_be`][Uint::from_base_be]. #[allow(clippy::module_name_repetitions)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Error)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum BaseConvertError { /// The value is too large to fit the target type. - #[error("The value is too large to fit the target type")] Overflow, /// The requested number base `.0` is less than two. - #[error("The requested number base {0} is less than two")] InvalidBase(u64), /// The provided digit `.0` is out of range for requested base `.1`. - #[error("digit {0} is out of range for base {1}")] InvalidDigit(u64, u64), } +#[cfg(feature = "std")] +impl std::error::Error for BaseConvertError {} + +impl fmt::Display for BaseConvertError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Overflow => write!(f, "The value is too large to fit the target type"), + Self::InvalidBase(base) => { + write!(f, "The requested number base {base} is less than two") + } + Self::InvalidDigit(digit, base) => { + write!(f, "digit {digit} is out of range for base {base}") + } + } + } +} + impl Uint { /// Returns an iterator over the base `base` digits of the number in /// little-endian order. @@ -68,6 +83,7 @@ impl Uint { base: u64, digits: I, ) -> Result { + // TODO: Do not allocate. let mut digits: Vec<_> = digits.into_iter().collect(); digits.reverse(); Self::from_base_be(base, digits) diff --git a/src/bit_arr.rs b/src/bit_arr.rs index b4a5b85..2c8e441 100644 --- a/src/bit_arr.rs +++ b/src/bit_arr.rs @@ -1,9 +1,12 @@ use crate::{ParseError, Uint}; -use core::ops::{ - BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Index, Not, Shl, ShlAssign, - Shr, ShrAssign, +use alloc::{borrow::Cow, vec::Vec}; +use core::{ + ops::{ + BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Index, Not, Shl, ShlAssign, + Shr, ShrAssign, + }, + str::FromStr, }; -use std::{borrow::Cow, str::FromStr}; /// A newtype wrapper around [`Uint`] that restricts operations to those /// relevant for bit arrays. diff --git a/src/bytes.rs b/src/bytes.rs index 0fa182e..3d2103e 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -5,11 +5,11 @@ use crate::{ utils::{trim_end_slice, trim_end_vec}, Uint, }; +use alloc::{borrow::Cow, vec::Vec}; use core::{ ptr::{addr_of, addr_of_mut}, slice, }; -use std::borrow::Cow; // OPT: *_to_smallvec to avoid allocation. impl Uint { diff --git a/src/from.rs b/src/from.rs index 9bfa0e1..3fc037c 100644 --- a/src/from.rs +++ b/src/from.rs @@ -32,51 +32,90 @@ // } use crate::Uint; -use core::{any::type_name, fmt::Debug}; -use thiserror::Error; +use core::{fmt, fmt::Debug}; /// Error for [`TryFrom`][TryFrom] for [`Uint`]. -#[derive(Clone, Copy, Debug, Error, Eq, PartialEq, Hash)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum ToUintError { /// Value is too large to fit the Uint. /// /// `.0` is `BITS` and `.1` is the wrapped value. - #[error("Value is too large for Uint<{0}>")] ValueTooLarge(usize, T), /// Negative values can not be represented as Uint. /// /// `.0` is `BITS` and `.1` is the wrapped value. - #[error("Negative values can not be represented as Uint<{0}>")] ValueNegative(usize, T), /// 'Not a number' (NaN) can not be represented as Uint - #[error("'Not a number' (NaN) not be represented as Uint<{0}>")] NotANumber(usize), } +#[cfg(feature = "std")] +impl std::error::Error for ToUintError {} + +impl fmt::Display for ToUintError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ValueTooLarge(bits, _) => write!(f, "Value is too large for Uint<{bits}>"), + Self::ValueNegative(bits, _) => { + write!(f, "Negative values can not be represented as Uint<{bits}>") + } + Self::NotANumber(bits) => { + write!(f, "'Not a number' (NaN) not be represented as Uint<{bits}>") + } + } + } +} + /// Error for [`TryFrom`][TryFrom]. #[allow(clippy::derive_partial_eq_without_eq)] // False positive -#[derive(Clone, Copy, Debug, Error, PartialEq, Eq, Hash)] #[allow(clippy::module_name_repetitions)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum FromUintError { /// The Uint value is too large for the target type. /// /// `.0` number of `BITS` in the Uint, `.1` is the wrapped value and /// `.2` is the maximum representable value in the target type. - #[error("Uint<{0}> value is too large for {}", type_name::())] Overflow(usize, T, T), } +#[cfg(feature = "std")] +impl std::error::Error for FromUintError {} + +impl fmt::Display for FromUintError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Overflow(bits, ..) => write!( + f, + "Uint<{bits}> value is too large for {}", + core::any::type_name::() + ), + } + } +} + /// Error for [`TryFrom`][TryFrom] for [`ark_ff`](https://docs.rs/ark-ff) and others. #[allow(dead_code)] // This is used by some support features. -#[derive(Debug, Clone, Copy, Error)] +#[derive(Debug, Clone, Copy)] pub enum ToFieldError { /// Number is equal or larger than the target field modulus. - #[error("Number is equal or larger than the target field modulus.")] NotInField, } +#[cfg(feature = "std")] +impl std::error::Error for ToFieldError {} + +impl fmt::Display for ToFieldError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::NotInField => { + f.write_str("Number is equal or larger than the target field modulus.") + } + } + } +} + impl Uint { /// Construct a new [`Uint`] from the value. /// @@ -418,6 +457,7 @@ impl_from_signed_int!(i64, u64); impl_from_signed_int!(i128, u128); impl_from_signed_int!(isize, usize); +#[cfg(feature = "std")] impl TryFrom for Uint { type Error = ToUintError; @@ -486,6 +526,7 @@ impl TryFrom for Uint { } } +#[cfg(feature = "std")] impl TryFrom for Uint { type Error = ToUintError; @@ -605,12 +646,14 @@ impl TryFrom<&Uint> for u128 // Convert Uint to floating point +#[cfg(feature = "std")] impl From> for f32 { fn from(value: Uint) -> Self { Self::from(&value) } } +#[cfg(feature = "std")] impl From<&Uint> for f32 { /// Approximate single precision float. /// @@ -622,12 +665,14 @@ impl From<&Uint> for f32 { } } +#[cfg(feature = "std")] impl From> for f64 { fn from(value: Uint) -> Self { Self::from(&value) } } +#[cfg(feature = "std")] impl From<&Uint> for f64 { /// Approximate double precision float. /// @@ -659,6 +704,7 @@ mod test { } #[test] + #[cfg(feature = "std")] fn test_f64() { assert_eq!(Uint::<0, 0>::try_from(0.0_f64), Ok(Uint::ZERO)); const_for!(BITS in NON_ZERO { diff --git a/src/lib.rs b/src/lib.rs index 02ad01b..7364c2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ any(test, feature = "bench"), allow(clippy::wildcard_imports, clippy::cognitive_complexity) )] +#![cfg_attr(not(feature = "std"), no_std)] // Unstable features #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![cfg_attr(feature = "nightly", feature(no_coverage, core_intrinsics))] @@ -26,6 +27,10 @@ // See extern crate self as ruint; +// TODO: alloc feature flag +#[macro_use] +extern crate alloc; + #[macro_use] mod macros; diff --git a/src/log.rs b/src/log.rs index 784ab01..06c9cd4 100644 --- a/src/log.rs +++ b/src/log.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "std")] + use crate::Uint; impl Uint { diff --git a/src/pow.rs b/src/pow.rs index 62cbb56..0706b5f 100644 --- a/src/pow.rs +++ b/src/pow.rs @@ -118,6 +118,7 @@ impl Uint { /// assert_eq!(U64::approx_pow2(10.385), Some(1337_U64)); /// # } /// ``` + #[cfg(feature = "std")] #[must_use] pub fn approx_pow2(exp: f64) -> Option { const LN2_1P5: f64 = 0.584_962_500_721_156_2_f64; @@ -166,8 +167,8 @@ impl Uint { mod tests { use super::*; use crate::{const_for, nlimbs}; + use core::iter::repeat; use proptest::proptest; - use std::iter::repeat; #[test] fn test_pow2_shl() { diff --git a/src/root.rs b/src/root.rs index 5c0006e..1d49ead 100644 --- a/src/root.rs +++ b/src/root.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "std")] + use crate::Uint; use core::cmp::{min, Ordering}; diff --git a/src/string.rs b/src/string.rs index 67952c5..f946782 100644 --- a/src/string.rs +++ b/src/string.rs @@ -1,9 +1,8 @@ use crate::{base_convert::BaseConvertError, utils::rem_up, Uint}; -use core::fmt::{ - Binary, Debug, Display, Formatter, LowerHex, Octal, Result as FmtResult, UpperHex, +use core::{ + fmt::{Binary, Debug, Display, Formatter, LowerHex, Octal, Result as FmtResult, UpperHex}, + str::FromStr, }; -use std::str::FromStr; -use thiserror::Error; // FEATURE: Respect width parameter in formatters. @@ -93,19 +92,42 @@ impl Octal for Uint { } /// Error for [`from_str_radix`](Uint::from_str_radix). -#[derive(Debug, Copy, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ParseError { /// Invalid digit in string. - #[error("invalid digit")] InvalidDigit(char), /// Invalid radix, up to base 64 is supported. - #[error("invalid radix, up to 64 is supported")] InvalidRadix(u64), /// Error from [`Uint::from_base_be`]. - #[error(transparent)] - BaseConvertError(#[from] BaseConvertError), + BaseConvertError(BaseConvertError), +} + +#[cfg(feature = "std")] +impl std::error::Error for ParseError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::BaseConvertError(e) => Some(e), + _ => None, + } + } +} + +impl From for ParseError { + fn from(value: BaseConvertError) -> Self { + Self::BaseConvertError(value) + } +} + +impl Display for ParseError { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + Self::BaseConvertError(e) => Display::fmt(e, f), + Self::InvalidDigit(c) => write!(f, "Invalid digit: {c}"), + Self::InvalidRadix(r) => write!(f, "Invalid radix {r}, up to 64 is supported"), + } + } } impl Uint { @@ -186,6 +208,7 @@ impl FromStr for Uint { #[cfg(test)] mod tests { use super::*; + use alloc::string::ToString; use proptest::proptest; #[allow(clippy::unreadable_literal)] diff --git a/src/support/arbitrary.rs b/src/support/arbitrary.rs index dc52b82..cd7e475 100644 --- a/src/support/arbitrary.rs +++ b/src/support/arbitrary.rs @@ -34,7 +34,8 @@ impl<'a, const BITS: usize, const LIMBS: usize> Arbitrary<'a> for Uint ToPyObject for Uint { fn to_object(&self, py: Python<'_>) -> PyObject { diff --git a/src/support/rlp.rs b/src/support/rlp.rs index 772bbba..4f7f11c 100644 --- a/src/support/rlp.rs +++ b/src/support/rlp.rs @@ -4,8 +4,8 @@ #![cfg_attr(docsrs, doc(cfg(feature = "rlp")))] use crate::{Bits, Uint}; +use core::cmp::Ordering; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; -use std::cmp::Ordering; /// Allows a [`Uint`] to be serialized as RLP. /// diff --git a/src/support/serde.rs b/src/support/serde.rs index d9a2751..a278c46 100644 --- a/src/support/serde.rs +++ b/src/support/serde.rs @@ -4,12 +4,14 @@ #![cfg_attr(docsrs, doc(cfg(feature = "serde")))] use crate::{nbytes, Bits, Uint}; -use core::fmt::{Formatter, Result as FmtResult}; +use core::{ + fmt::{Formatter, Result as FmtResult, Write}, + str, +}; use serde::{ de::{Error, Unexpected, Visitor}, Deserialize, Deserializer, Serialize, Serializer, }; -use std::{fmt::Write, str}; /// Canonical serialization for all human-readable instances of `Uint<0, 0>`, /// and minimal human-readable `Uint::ZERO` for any bit size. diff --git a/src/utils.rs b/src/utils.rs index 91c66b0..a07653b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,5 @@ +use alloc::vec::Vec; + /// Like `a % b` but returns `b` instead of `0`. #[must_use] pub(crate) const fn rem_up(a: usize, b: usize) -> usize {