diff --git a/CHANGELOG.md b/CHANGELOG.md index 86f362e1..77ad7252 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ versions. (#598), thus allowing being iterated in reverse using `.rev()` - `Env::is_process_alive` and `LocalPid::is_alive` (#599) - Encoding and decoding of 128 bit integers (#600) +- Optional codec for `num_bigint::BigInt` using the `big_integer` feature (#601) ### Fixed @@ -27,6 +28,9 @@ versions. ### Removed +- `rustler_bigint` is replaced by a feature flag and the wrapper is not + necessary anymore (#601) + ## [0.31.0] - 2024-02-13 ### Added diff --git a/Cargo.toml b/Cargo.toml index 33211ea7..e888541c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ resolver = "2" members = [ "rustler", - "rustler_bigint", "rustler_codegen", "rustler_sys", "rustler_tests/native/binary_example", diff --git a/UPGRADE.md b/UPGRADE.md index 8c4f5546..a10f6586 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -2,6 +2,16 @@ This document is intended to simplify upgrading to newer versions by extending the changelog. +## 0.31 -> 0.32 + +1. The functionality of `rustler_bigint` has moved into `rustler`. The library + will still work, but it can now also be replaced by activating the new + `big_integer` feature on `rustler`. The new `rustler::BigInt` is a re-export + of `num_bigint::BigInt` in contrast to `rustler_bigint::BigInt`, which was a + wrapper. For most codebases, it will be enough to activate the feature and + replace all `rustler_bigint::BigInt` usages by `rustler::BigInt` (or + `num_bigint::BigInt`). + ## 0.29 -> 0.30 1. `rustler_crates` configuration is deprecated in favor of explicitly passing diff --git a/prepare_release.sh b/prepare_release.sh index c25b5e52..f481a702 100755 --- a/prepare_release.sh +++ b/prepare_release.sh @@ -38,14 +38,13 @@ echo "Bumping versions.." sed -i "s/^version = \"[^\"]*\" # rustler version$/version = \"$VERSION\" # rustler version/" rustler/Cargo.toml sed -i "s/^rustler_codegen.*$/rustler_codegen = { path = \"..\/rustler_codegen\", version = \"$VERSION\", optional = true}/" rustler/Cargo.toml sed -i "s/^version = \"[^\"]*\" # rustler_codegen version$/version = \"$VERSION\" # rustler_codegen version/" rustler_codegen/Cargo.toml -sed -i "s/^rustler.*$/rustler = {path = \"..\/rustler\", version = \"$VERSION\"}/" rustler_bigint/Cargo.toml sed -i "s/def rustler_version, do: \"[^\"]*\"$/def rustler_version, do: \"$VERSION\"/" rustler_mix/mix.exs rustler_mix/lib/rustler.ex sed -i "s/@version .*$/@version \"$VERSION\"/" rustler_mix/mix.exs sed -i "s/{:rustler, \".*\"/{:rustler, \"~> $VERSION\"/" rustler_mix/README.md echo "Committing version.." git commit -m "(release) $VERSION" \ - rustler/Cargo.toml rustler_codegen/Cargo.toml rustler_mix/mix.exs rustler_mix/lib/rustler.ex rustler_mix/README.md rustler_bigint/Cargo.toml + rustler/Cargo.toml rustler_codegen/Cargo.toml rustler_mix/mix.exs rustler_mix/lib/rustler.ex rustler_mix/README.md echo "Tagging version.." git tag "$TAG" diff --git a/rustler/Cargo.toml b/rustler/Cargo.toml index 3c9f154d..6291761b 100644 --- a/rustler/Cargo.toml +++ b/rustler/Cargo.toml @@ -9,6 +9,7 @@ readme = "../README.md" edition = "2021" [features] +big_integer = ["dep:num-bigint"] default = ["derive", "nif_version_2_15"] derive = ["rustler_codegen"] alternative_nif_init_name = [] @@ -21,6 +22,7 @@ nif_version_2_17 = ["nif_version_2_16", "rustler_sys/nif_version_2_17"] lazy_static = "1.4" rustler_codegen = { path = "../rustler_codegen", version = "0.31.0", optional = true} rustler_sys = { path = "../rustler_sys", version = "~2.4.0" } +num-bigint = { version = "0.4", optional = true } [package.metadata.release] diff --git a/rustler/src/lib.rs b/rustler/src/lib.rs index ce700ef6..40030f12 100644 --- a/rustler/src/lib.rs +++ b/rustler/src/lib.rs @@ -41,6 +41,10 @@ pub use crate::types::{ Atom, Binary, Decoder, Encoder, ErlOption, ListIterator, LocalPid, MapIterator, NewBinary, OwnedBinary, }; + +#[cfg(feature = "big_integer")] +pub use crate::types::BigInt; + pub mod resource; pub use crate::resource::ResourceArc; diff --git a/rustler_bigint/src/big_int.rs b/rustler/src/types/big_int.rs similarity index 78% rename from rustler_bigint/src/big_int.rs rename to rustler/src/types/big_int.rs index 9173ae6b..53aa9f33 100644 --- a/rustler_bigint/src/big_int.rs +++ b/rustler/src/types/big_int.rs @@ -1,6 +1,6 @@ -use rustler::{Decoder, Encoder, Env, Error, NifResult, Term}; +use crate::{Decoder, Encoder, Env, Error, NifResult, Term}; -use num_bigint::Sign; +use num_bigint::{BigInt, Sign}; // From https://www.erlang.org/doc/apps/erts/erl_ext_dist.html const EXTERNAL_TERM_FORMAT_VERSION: u8 = 131; @@ -9,26 +9,17 @@ const INTEGER: u8 = 98; const SMALL_BIG_EXT: u8 = 110; const LARGE_BIG_EXT: u8 = 111; -rustler::atoms! { +crate::atoms! { big_int_encoder_invalid_bytes } -/// Wrapper around num-bigint that implements [Decoder](rustler::Decoder) and [Encoder](rustler::Encoder) traits -/// -/// ```rust -/// use rustler_bigint::{BigInt, num_bigint}; -/// -/// // convert from and to the bigint wrapper -/// let number = num_bigint::BigInt::from(12); -/// let implements_decode_encode = BigInt::from(number.clone()); -/// let bigint = num_bigint::BigInt::from(implements_decode_encode); -/// assert_eq!(number, bigint); -/// ``` +/// Implementation of [Decoder](rustler::Decoder) and [Encoder](rustler::Encoder) traits for +/// num-bigint. /// /// ## Examples /// /// ```rust -/// use rustler_bigint::BigInt; +/// use rustler::BigInt; // or num_bigint::BigInt; /// /// #[rustler::nif] /// pub fn pow(base: BigInt, exponent: u32) -> BigInt { @@ -37,18 +28,16 @@ rustler::atoms! { /// ``` /// /// ```rust -/// use rustler::Binary; -/// use rustler_bigint::{BigInt, num_bigint}; +/// use rustler::{Binary, BigInt}; /// /// #[rustler::nif] /// pub fn binary_to_integer(binary: Binary) -> BigInt { -/// num_bigint::BigInt::from_signed_bytes_be(binary.as_slice()).into() +/// BigInt::from_signed_bytes_be(binary.as_slice()).into() /// } /// ``` /// /// ```rust -/// use rustler::{Binary, Env, NewBinary}; -/// use rustler_bigint::BigInt; +/// use rustler::{Binary, BigInt, Env, NewBinary}; /// /// #[rustler::nif] /// pub fn integer_to_binary<'a>(env: Env<'a>, integer: BigInt) -> Binary<'a> { @@ -59,44 +48,16 @@ rustler::atoms! { /// } /// ``` /// -#[derive(Debug, PartialEq, PartialOrd, Eq)] -pub struct BigInt(num_bigint::BigInt); - -impl std::convert::From for BigInt { - fn from(big_int: num_bigint::BigInt) -> BigInt { - BigInt(big_int) - } -} - -impl std::convert::From for num_bigint::BigInt { - fn from(big_int: BigInt) -> num_bigint::BigInt { - big_int.0 - } -} - -impl std::ops::Deref for BigInt { - type Target = num_bigint::BigInt; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl std::ops::DerefMut for BigInt { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} -fn decode_big_integer(input: &[u8]) -> NifResult { +fn decode_big_integer(input: &[u8]) -> NifResult { if Some(&EXTERNAL_TERM_FORMAT_VERSION) != input.first() { return Err(Error::BadArg); } match input[1] { - SMALL_INTEGER => Ok(num_bigint::BigInt::from(input[2])), + SMALL_INTEGER => Ok(BigInt::from(input[2])), - INTEGER => Ok(num_bigint::BigInt::from_signed_bytes_be(&input[2..6])), + INTEGER => Ok(BigInt::from_signed_bytes_be(&input[2..6])), SMALL_BIG_EXT => { let n = input[2] as usize; @@ -106,7 +67,7 @@ fn decode_big_integer(input: &[u8]) -> NifResult { Sign::Minus }; - Ok(num_bigint::BigInt::from_bytes_le(sign, &input[4..n + 4])) + Ok(BigInt::from_bytes_le(sign, &input[4..n + 4])) } LARGE_BIG_EXT => { @@ -117,14 +78,14 @@ fn decode_big_integer(input: &[u8]) -> NifResult { Sign::Minus }; - Ok(num_bigint::BigInt::from_bytes_le(sign, &input[7..n + 7])) + Ok(BigInt::from_bytes_le(sign, &input[7..n + 7])) } _ => Err(Error::BadArg), } } -fn encode_big_integer(big_int: &num_bigint::BigInt) -> Vec { +fn encode_big_integer(big_int: &BigInt) -> Vec { if let Ok(integer) = i32::try_from(big_int) { let mut out = vec![EXTERNAL_TERM_FORMAT_VERSION, INTEGER]; out.extend(integer.to_be_bytes()); @@ -154,14 +115,14 @@ fn encode_big_integer(big_int: &num_bigint::BigInt) -> Vec { impl<'a> Decoder<'a> for BigInt { fn decode(term: Term<'a>) -> NifResult { - decode_big_integer(term.to_binary().as_slice()).map(BigInt) + decode_big_integer(term.to_binary().as_slice()) } } impl Encoder for BigInt { fn encode<'c>(&self, env: Env<'c>) -> Term<'c> { // Returns error tuple if the encode_big_integer returns invalid ETF bytes - let binary = encode_big_integer(&self.0); + let binary = encode_big_integer(self); match env.binary_to_term(&binary) { Some((term, _)) => term, None => env.error_tuple(big_int_encoder_invalid_bytes()), @@ -169,41 +130,12 @@ impl Encoder for BigInt { } } -#[test] -fn from_test() { - let right = BigInt::from(num_bigint::BigInt::from(123)); - let left = BigInt(num_bigint::BigInt::from(123)); - assert_eq!(left, right) -} - -#[test] -fn into_test() { - let right = BigInt(num_bigint::BigInt::from(123)); - let left = num_bigint::BigInt::from(123); - assert_eq!(left, right.into()) -} - -#[test] -fn deref_test() { - let input = BigInt(num_bigint::BigInt::from(123)); - assert_eq!(vec![123], input.to_signed_bytes_be()) -} - -#[test] -fn deref_mut_test() { - let expected = BigInt(num_bigint::BigInt::from(127)); - let mut input = BigInt(num_bigint::BigInt::from(123)); - input.set_bit(2, true); - - assert_eq!(expected, input) -} - #[test] fn decode_small_int() { // :erlang.term_to_binary(3) let data = [131, 97, 3]; - let expected = num_bigint::BigInt::from(3); + let expected = BigInt::from(3); assert_eq!(expected, decode_big_integer(&data).unwrap()); } @@ -213,7 +145,7 @@ fn decode_small_negative_int() { // :erlang.term_to_binary(-5) let data = [131, 98, 255, 255, 255, 251]; - let expected = num_bigint::BigInt::from(-5); + let expected = BigInt::from(-5); assert_eq!(expected, decode_big_integer(&data).unwrap()); } @@ -223,7 +155,7 @@ fn decode_normal_int() { // :erlang.term_to_binary(12345) let data = [131, 98, 0, 0, 48, 57]; - let expected = num_bigint::BigInt::from(12345); + let expected = BigInt::from(12345); assert_eq!(expected, decode_big_integer(&data).unwrap()); } @@ -237,7 +169,7 @@ fn decode_small_big_int() { 240, 96, 176, 91, 142, 219, 66, 30, 177, 137, 199, 21, 191, 153, 182, 169, 73, 73, ]; - let expected = num_bigint::BigInt::from(24).pow(120); + let expected = BigInt::from(24).pow(120); assert_eq!(expected, decode_big_integer(&data).unwrap()); } @@ -252,7 +184,7 @@ fn decode_negative_small_big_int() { 186, 211, 12, 168, 222, 95, ]; - let expected = num_bigint::BigInt::from(-17).pow(121); + let expected = BigInt::from(-17).pow(121); assert_eq!(expected, decode_big_integer(&data).unwrap()); } @@ -277,7 +209,7 @@ fn decode_large_big_int() { 133, 185, 168, 30, 64, 0, 151, 20, 186, 82, 147, 226, 1, 2, 141, ]; - let expected = num_bigint::BigInt::from(5).pow(923); + let expected = BigInt::from(5).pow(923); assert_eq!(expected, decode_big_integer(&data).unwrap()); } @@ -305,7 +237,7 @@ fn decode_negative_large_big_int() { 193, 165, 1, 16, 3, ]; - let expected = num_bigint::BigInt::from(-17).pow(613); + let expected = BigInt::from(-17).pow(613); assert_eq!(expected, decode_big_integer(&data).unwrap()); } @@ -314,7 +246,7 @@ fn decode_negative_large_big_int() { fn encode_positive_int_as_integer() { let expected = vec![131, 98, 0, 0, 0, 12]; - let input = num_bigint::BigInt::from(12); + let input = BigInt::from(12); assert_eq!(expected, encode_big_integer(&input)); } @@ -323,7 +255,7 @@ fn encode_positive_int_as_integer() { fn encode_negative_int_as_integer() { let expected = vec![131, 98, 255, 255, 254, 254]; - let input = num_bigint::BigInt::from(-258); + let input = BigInt::from(-258); assert_eq!(expected, encode_big_integer(&input)); } @@ -332,7 +264,7 @@ fn encode_negative_int_as_integer() { fn encode_negative_int_just_outside_32_bites_as_small_big_int() { let expected = vec![131, 110, 4, 1, 1, 0, 0, 128]; - let input = num_bigint::BigInt::from(i32::MIN as i64 - 1); + let input = BigInt::from(i32::MIN as i64 - 1); assert_eq!(expected, encode_big_integer(&input)); } @@ -346,7 +278,7 @@ fn encode_small_big_int() { 240, 96, 176, 91, 142, 219, 66, 30, 177, 137, 199, 21, 191, 153, 182, 169, 73, 73, ]; - let input = num_bigint::BigInt::from(24).pow(120); + let input = BigInt::from(24).pow(120); assert_eq!(expected, encode_big_integer(&input)); } @@ -361,7 +293,7 @@ fn encode_negative_small_big_int() { 186, 211, 12, 168, 222, 95, ]; - let input = num_bigint::BigInt::from(-17).pow(121); + let input = BigInt::from(-17).pow(121); assert_eq!(expected, encode_big_integer(&input)); } @@ -386,7 +318,7 @@ fn encode_large_big_int() { 133, 185, 168, 30, 64, 0, 151, 20, 186, 82, 147, 226, 1, 2, 141, ]; - let input = num_bigint::BigInt::from(5).pow(923); + let input = BigInt::from(5).pow(923); assert_eq!(expected, encode_big_integer(&input)); } @@ -414,7 +346,7 @@ fn encode_negative_large_big_int() { 193, 165, 1, 16, 3, ]; - let input = num_bigint::BigInt::from(-17).pow(613); + let input = BigInt::from(-17).pow(613); assert_eq!(expected, encode_big_integer(&input)); } diff --git a/rustler/src/types/mod.rs b/rustler/src/types/mod.rs index b6205dc2..9a5c78a7 100644 --- a/rustler/src/types/mod.rs +++ b/rustler/src/types/mod.rs @@ -8,6 +8,11 @@ pub use crate::types::atom::Atom; pub mod binary; pub use crate::types::binary::{Binary, NewBinary, OwnedBinary}; +#[cfg(feature = "big_integer")] +pub mod big_int; +#[cfg(feature = "big_integer")] +pub use num_bigint::BigInt; + #[doc(hidden)] pub mod list; pub use crate::types::list::ListIterator; diff --git a/rustler_bigint/Cargo.toml b/rustler_bigint/Cargo.toml deleted file mode 100644 index fff56948..00000000 --- a/rustler_bigint/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "rustler_bigint" -description = "Handle Erlang's arbitrarily-sized integers" -repository = "https://github.com/rusterlium/rustler" -authors = ["Thomas Timmer"] -license = "MIT/Apache-2.0" -version = "0.2.0-rc.1" -edition = "2021" -readme = "README.md" -keywords = ["bigint", "Erlang", "Elixir"] - -[dependencies] -num-bigint = {version = "0.4"} -rustler = {path = "../rustler", version = "0.31.0"} diff --git a/rustler_bigint/README.md b/rustler_bigint/README.md deleted file mode 100644 index 0462e950..00000000 --- a/rustler_bigint/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Rustler BigInt - -[Documentation](https://docs.rs/rustler_bigint/latest/rustler_bigint) -[![Build Status](https://github.com/rusterlium/rustler/workflows/CI/badge.svg?branch=master)](https://github.com/rusterlium/rustler/actions/workflows/main.yml) -[![Crates.io package version](https://img.shields.io/crates/v/rustler_bigint.svg)](https://crates.io/crates/rustler_bigint) - -`rustler_bigint` provides support for Erlang's arbitrarily-sized integers. - -## Installation - -Add this to `Cargo.toml`: - -```toml -[dependencies] -rustler_bigint = { version = "0.1" } -``` - -## Example - -Lets assume that we need to handle integers of variable size. Some might fit -into Rust's `i64`, but others might not. For example: - -```elixir -large = Bitwise.bsl(2, 65) # This does not fit into i64, it is an Erlang big integer -``` - -In Rust, we can use `rustler_bigint::BigInt` to pass integer values of -different sizes into a NIF. The type `rustler_bigint::BigInt` is a newtype -wrapping `num_bigint::BigInt` and implements `std::ops::Deref`, so functions -from `num_bigint::BigInt` can be called directly. - -```rust -/// Simply echo `large` back to the caller. -#[rustler::nif] -pub fn handle_large(large: rustler_bigint::BigInt) -> NifResult { - Ok(large) -} -``` diff --git a/rustler_bigint/src/lib.rs b/rustler_bigint/src/lib.rs deleted file mode 100644 index f6303448..00000000 --- a/rustler_bigint/src/lib.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! [Github](https://github.com/rusterlium/rustler) -//! -//! [BigInt](crate::BigInt) is a helper type to support using large integers in Rust nifs -//! -//! BigInt type is a wrapper around [num-bigint](num_bigint::BigInt) -//! this crate is also exposed via `rustler_bigint::num_bigint` -//! -//! ## Example -//! -//! ```rust -//! use rustler::{NifResult, Error}; -//! use rustler_bigint::BigInt; -//! -//! #[rustler::nif] -//! pub fn add(left: BigInt, right: BigInt) -> NifResult { -//! // BigInt implements deref so we can just use num-bigint methods directly -//! if let Some(result) = left.checked_add(&right) { -//! Ok(result.into()) -//! } else { -//! Err(Error::RaiseAtom("number_overflow")) -//! } -//! } -//! ``` - -pub mod big_int; - -pub use big_int::BigInt; -pub use num_bigint; diff --git a/rustler_tests/native/dynamic_load/Cargo.toml b/rustler_tests/native/dynamic_load/Cargo.toml index 469596ea..4cc7c5d1 100644 --- a/rustler_tests/native/dynamic_load/Cargo.toml +++ b/rustler_tests/native/dynamic_load/Cargo.toml @@ -9,5 +9,4 @@ path = "src/lib.rs" crate-type = ["cdylib"] [dependencies] -rustler_bigint = { path = "../../../rustler_bigint" } -rustler = { path = "../../../rustler" } \ No newline at end of file +rustler = { path = "../../../rustler", features = ["big_integer"] } diff --git a/rustler_tests/native/rustler_bigint_test/Cargo.toml b/rustler_tests/native/rustler_bigint_test/Cargo.toml index 79960355..9f64a529 100644 --- a/rustler_tests/native/rustler_bigint_test/Cargo.toml +++ b/rustler_tests/native/rustler_bigint_test/Cargo.toml @@ -9,5 +9,4 @@ path = "src/lib.rs" crate-type = ["cdylib"] [dependencies] -rustler_bigint = { path = "../../../rustler_bigint" } -rustler = { path = "../../../rustler" } +rustler = { path = "../../../rustler", features = ["big_integer"] } diff --git a/rustler_tests/native/rustler_bigint_test/src/lib.rs b/rustler_tests/native/rustler_bigint_test/src/lib.rs index 62bc4103..99e29f4e 100644 --- a/rustler_tests/native/rustler_bigint_test/src/lib.rs +++ b/rustler_tests/native/rustler_bigint_test/src/lib.rs @@ -1,6 +1,4 @@ -use rustler::NifResult; -use rustler_bigint::num_bigint; -use rustler_bigint::BigInt; +use rustler::{BigInt, NifResult}; #[rustler::nif] pub fn echo(input: BigInt) -> NifResult { @@ -9,15 +7,12 @@ pub fn echo(input: BigInt) -> NifResult { #[rustler::nif] pub fn add_one(input: BigInt) -> NifResult { - Ok(input - .checked_add(&num_bigint::BigInt::from(1)) - .unwrap() - .into()) + Ok(input.checked_add(&BigInt::from(1)).unwrap()) } #[rustler::nif] pub fn add(a: BigInt, b: BigInt) -> NifResult { - Ok(a.checked_add(&b).unwrap().into()) + Ok(a.checked_add(&b).unwrap()) } rustler::init!("Elixir.RustlerBigintTest", [echo, add_one, add]);