Skip to content

Commit

Permalink
Replace rustler_bigint by a feature flag on rustler
Browse files Browse the repository at this point in the history
  • Loading branch information
filmor committed Mar 12, 2024
1 parent 981c51f commit 1cf142f
Show file tree
Hide file tree
Showing 14 changed files with 61 additions and 190 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
resolver = "2"
members = [
"rustler",
"rustler_bigint",
"rustler_codegen",
"rustler_sys",
"rustler_tests/native/binary_example",
Expand Down
10 changes: 10 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions prepare_release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions rustler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand All @@ -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]

Expand Down
4 changes: 4 additions & 0 deletions rustler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
130 changes: 31 additions & 99 deletions rustler_bigint/src/big_int.rs → rustler/src/types/big_int.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 {
Expand All @@ -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> {
Expand All @@ -59,44 +48,16 @@ rustler::atoms! {
/// }
/// ```
///
#[derive(Debug, PartialEq, PartialOrd, Eq)]
pub struct BigInt(num_bigint::BigInt);

impl std::convert::From<num_bigint::BigInt> for BigInt {
fn from(big_int: num_bigint::BigInt) -> BigInt {
BigInt(big_int)
}
}

impl std::convert::From<BigInt> 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<num_bigint::BigInt> {
fn decode_big_integer(input: &[u8]) -> NifResult<BigInt> {
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;
Expand All @@ -106,7 +67,7 @@ fn decode_big_integer(input: &[u8]) -> NifResult<num_bigint::BigInt> {
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 => {
Expand All @@ -117,14 +78,14 @@ fn decode_big_integer(input: &[u8]) -> NifResult<num_bigint::BigInt> {
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<u8> {
fn encode_big_integer(big_int: &BigInt) -> Vec<u8> {
if let Ok(integer) = i32::try_from(big_int) {
let mut out = vec![EXTERNAL_TERM_FORMAT_VERSION, INTEGER];
out.extend(integer.to_be_bytes());
Expand Down Expand Up @@ -154,56 +115,27 @@ fn encode_big_integer(big_int: &num_bigint::BigInt) -> Vec<u8> {

impl<'a> Decoder<'a> for BigInt {
fn decode(term: Term<'a>) -> NifResult<Self> {
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()),
}
}
}

#[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());
}
Expand All @@ -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());
}
Expand All @@ -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());
}
Expand All @@ -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());
}
Expand All @@ -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());
}
Expand All @@ -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());
}
Expand Down Expand Up @@ -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());
}
Expand All @@ -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));
}
Expand All @@ -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));
}
Expand All @@ -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));
}
Expand All @@ -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));
}
Expand All @@ -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));
}
Expand All @@ -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));
}
Expand Down Expand Up @@ -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));
}
Loading

0 comments on commit 1cf142f

Please sign in to comment.