Skip to content
This repository has been archived by the owner on Jun 10, 2024. It is now read-only.

encode_unsigned_1 #676

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions native_implemented/otp/src/binary.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod encode_unsigned_1;
pub mod to_term;

use std::backtrace::Backtrace;
Expand All @@ -11,6 +12,14 @@ use liblumen_alloc::erts::exception::{self, ArcError, Exception, InternalExcepti
use liblumen_alloc::erts::term::prelude::*;
use liblumen_alloc::Process;

pub fn module() -> Atom {
Atom::from_str("binary")
}

pub fn module_id() -> usize {
module().id()
}

pub struct PartRange {
pub byte_offset: usize,
pub byte_len: usize,
Expand Down
47 changes: 47 additions & 0 deletions native_implemented/otp/src/binary/encode_unsigned_1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#[cfg(all(not(target_arch = "wasm32"), test))]
mod test;

use crate::runtime::context::{term_is_not_integer, term_is_not_non_negative_integer};
use anyhow::*;
use liblumen_alloc::erts::exception;
use liblumen_alloc::erts::process::Process;
use liblumen_alloc::erts::term::prelude::*;
use num_bigint::Sign;

/// Returns the smallest possible representation in a binary digit representation for the given big
/// endian unsigned integer.
#[native_implemented::function(binary:encode_unsigned/1)]
pub fn result(process: &Process, term: Term) -> exception::Result<Term> {
match term.decode().unwrap() {
TypedTerm::SmallInteger(small_integer) => {
let signed: isize = small_integer.into();
if signed < 0 {
return Err(TryIntoIntegerError::Type)
.context(term_is_not_non_negative_integer("encoded_unsigned", term))
.map_err(From::from);
}
let mut bytes: Vec<u8> = small_integer.to_le_bytes();
bytes.reverse();
Ok(process.binary_from_bytes(without_leading_zeros(&bytes)))
}
TypedTerm::BigInteger(big_integer) => {
if Sign::Minus == big_integer.sign() {
return Err(TryIntoIntegerError::Type)
.context(term_is_not_non_negative_integer("encoded_unsigned", term))
.map_err(From::from);
}

let bytes: Vec<u8> = big_integer.to_signed_bytes_be();
Ok(process.binary_from_bytes(without_leading_zeros(&bytes)))
}
_ => Err(TryIntoIntegerError::Type)
.context(term_is_not_integer("encoded_unsigned", term))
.map_err(From::from),
}
}

#[inline]
fn without_leading_zeros(bytes: &Vec<u8>) -> &[u8] {
let first_nonzero_index = bytes.iter().position(|&b| b != 0).unwrap_or(0);
&bytes[first_nonzero_index..]
}
111 changes: 111 additions & 0 deletions native_implemented/otp/src/binary/encode_unsigned_1/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use crate::binary::encode_unsigned_1::result;
use crate::test::with_process;
use crate::test::*;
use liblumen_alloc::erts::term::prelude::*;
use num_bigint::{BigInt, ToBigInt};
use proptest::strategy::Just;

// 1> binary:encode_unsigned(11111111).
// <<169,138,199>>
#[test]
fn otp_doctest() {
with_process(|process| {
assert_eq!(
result(process, process.integer(11111111)),
Ok(process.binary_from_bytes(&[169, 138, 199]))
)
});
}

#[test]
fn smallest_big_int() {
let largest_small_int_as_big_int: BigInt = SmallInteger::MAX_VALUE.into();
let smallest_big_int: BigInt = largest_small_int_as_big_int + 1;

// 1> binary:encode_unsigned(70368744177664).
// <<64,0,0,0,0,0>>

with_process(|process| {
assert_eq!(
result(process, process.integer(smallest_big_int)),
Ok(process.binary_from_bytes(&[64, 0, 0, 0, 0, 0]))
)
});
}

#[test]
fn big_int_with_middle_zeros() {
let largest_small_int_as_big_int: BigInt = SmallInteger::MAX_VALUE.into();
let big_int_with_middle_zeros: BigInt = largest_small_int_as_big_int + 2;

// 1> binary:encode_unsigned(70368744177665).
// <<64,0,0,0,0,1>>
with_process(|process| {
assert_eq!(
result(process, process.integer(big_int_with_middle_zeros)),
Ok(process.binary_from_bytes(&[64, 0, 0, 0, 0, 1]))
)
});
}

#[test]
fn small_int_with_middle_zeros() {
// 1> binary:encode_unsigned(11075783).
// <<169,0,199>>
let largest_small_int_as_big_int: BigInt = SmallInteger::MAX_VALUE.into();
assert!(11075783.to_bigint().unwrap() < largest_small_int_as_big_int);

with_process(|process| {
assert_eq!(
result(process, process.integer(11075783)),
Ok(process.binary_from_bytes(&[169, 0, 199]))
)
});
}

#[test]
fn small_int_with_trailing_zeros() {
// 1> binary:encode_unsigned(16777216).
// <<1,0,0,0>>
let largest_small_int_as_big_int: BigInt = SmallInteger::MAX_VALUE.into();
assert!(16777216.to_bigint().unwrap() < largest_small_int_as_big_int);

with_process(|process| {
assert_eq!(
result(process, process.integer(16777216)),
Ok(process.binary_from_bytes(&[1, 0, 0, 0]))
)
});
}

#[test]
fn negative_integer() {
run!(
|arc_process| {
(
Just(arc_process.clone()),
strategy::term::integer::negative(arc_process.clone()),
)
},
|(arc_process, non_int)| {
prop_assert_badarg!(result(&arc_process, non_int), "invalid integer conversion");
Ok(())
},
);
}

#[test]
fn not_integer() {
run!(
|arc_process| {
(
Just(arc_process.clone()),
strategy::term::is_not_integer(arc_process.clone()),
)
},
|(arc_process, non_int)| {
prop_assert_badarg!(result(&arc_process, non_int), "invalid integer conversion");
Ok(())
},
);
}
2 changes: 2 additions & 0 deletions native_implemented/otp/tests/internal/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#[path = "lib/binary.rs"]
pub mod binary;
#[path = "lib/erlang.rs"]
pub mod erlang;
#[path = "lib/maps.rs"]
Expand Down
2 changes: 2 additions & 0 deletions native_implemented/otp/tests/internal/lib/binary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#[path = "binary/encode_unsigned_1.rs"]
pub mod encode_unsigned_1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
test_stdout!(doctest, "<<169,138,199>>\n");
test_stdout!(with_smallest_big_int, "<<64,0,0,0,0,0>>\n");
test_stdout!(
with_non_integer,
"{caught, error, badarg}\n{caught, error, badarg}\n{caught, error, badarg}\n"
);
test_stdout!(
with_negative_integer,
"{caught, error, badarg}\n{caught, error, badarg}\n"
);
test_stdout!(
when_big_int_encoded_bytes_have_significant_trailing_zeros,
"<<64,0,0,0,0,0>>\n"
);
test_stdout!(
when_small_int_encoded_bytes_have_significant_trailing_zeros,
"<<1,0,0,0>>\n"
);
test_stdout!(
when_small_int_encoded_bytes_have_zeros_in_the_middle,
"<<169,0,199>>\n"
);
test_stdout!(
when_big_int_encoded_bytes_have_zeros_in_the_middle,
"<<64,0,0,0,0,1>>\n"
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-module(init).
-export([start/0]).
-import(erlang, [display/1]).

start() ->
display(binary:encode_unsigned(11111111)).
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-module(init).
-export([start/0]).
-import(erlang, [display/1]).

start() ->
display(binary:encode_unsigned(70368744177665)).
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-module(init).
-export([start/0]).
-import(erlang, [display/1]).

start() ->
display(binary:encode_unsigned(70368744177665)).
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-module(init).
-export([start/0]).
-import(erlang, [display/1]).

start() ->
display(binary:encode_unsigned(16777216)).
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-module(init).
-export([start/0]).
-import(erlang, [display/1]).

start() ->
display(binary:encode_unsigned(11075783)).
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-module(init).
-export([start/0]).
-import(erlang, [display/1]).

start() ->
test:caught(fun () ->
display(binary:encode_unsigned(-1))
end),
test:caught(fun () ->
display(binary:encode_unsigned(-70368744177664))
end).
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-module(init).
-export([start/0]).
-import(erlang, [display/1]).

start() ->
test:caught(fun () ->
display(binary:encode_unsigned(nil))
end),
test:caught(fun () ->
display(binary:encode_unsigned(foo))
end),
test:caught(fun () ->
display(binary:encode_unsigned("foo"))
end).
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-module(init).
-export([start/0]).
-import(erlang, [display/1]).

start() ->
display(binary:encode_unsigned(70368744177664)).