Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #362 from mlwilkerson/iolist-bifs
Implement IOList BIFs
- Loading branch information
Showing
31 changed files
with
989 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
lumen_runtime/proptest-regressions/otp/erlang/binary_to_float_1/test.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
cc 120136f010840952852eb52f87bfba1a91a73bdb62c7d2bdd97dd3f4ce94a709 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
pub const IOLIST: &str = | ||
"a maybe improper list with byte, binary, or iolist elements and binary or empty list tail"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
use std::convert::TryInto; | ||
|
||
use anyhow::*; | ||
|
||
use liblumen_alloc::erts::exception; | ||
use liblumen_alloc::erts::process::Process; | ||
use liblumen_alloc::erts::term::prelude::*; | ||
|
||
use crate::context::{r#type, term_is_not_type}; | ||
|
||
pub fn element_not_a_binary_context(iolist_or_binary: Term, element: Term) -> String { | ||
format!( | ||
"iolist_or_binary ({}) element ({}) is a bitstring, but not a binary", | ||
iolist_or_binary, element | ||
) | ||
} | ||
|
||
pub fn element_type_context(iolist_or_binary: Term, element: Term) -> String { | ||
format!( | ||
"iolist_or_binary ({}) element ({}) is not a byte, binary, or nested iolist ({})", | ||
iolist_or_binary, | ||
element, | ||
r#type::IOLIST | ||
) | ||
} | ||
|
||
pub fn native( | ||
process: &Process, | ||
iolist_or_binary: Term, | ||
try_into: fn(&Process, Term) -> exception::Result<Term>, | ||
) -> exception::Result<Term> { | ||
match iolist_or_binary.decode()? { | ||
TypedTerm::Nil | ||
| TypedTerm::List(_) | ||
| TypedTerm::BinaryLiteral(_) | ||
| TypedTerm::HeapBinary(_) | ||
| TypedTerm::MatchContext(_) | ||
| TypedTerm::ProcBin(_) | ||
| TypedTerm::SubBinary(_) => try_into(process, iolist_or_binary), | ||
_ => Err(TypeError) | ||
.context(term_is_not_type( | ||
"iolist_or_binary", | ||
iolist_or_binary, | ||
&format!("an iolist ({}) or binary", r#type::IOLIST), | ||
)) | ||
.map_err(From::from), | ||
} | ||
} | ||
|
||
pub fn to_binary(process: &Process, name: &'static str, value: Term) -> exception::Result<Term> { | ||
let mut byte_vec: Vec<u8> = Vec::new(); | ||
let mut stack: Vec<Term> = vec![value]; | ||
|
||
while let Some(top) = stack.pop() { | ||
match top.decode()? { | ||
TypedTerm::SmallInteger(small_integer) => { | ||
let top_byte = small_integer | ||
.try_into() | ||
.with_context(|| element_context(name, value, top))?; | ||
|
||
byte_vec.push(top_byte); | ||
} | ||
TypedTerm::Nil => (), | ||
TypedTerm::List(boxed_cons) => { | ||
// @type iolist :: maybe_improper_list(byte() | binary() | iolist(), | ||
// binary() | []) means that `byte()` isn't allowed | ||
// for `tail`s unlike `head`. | ||
|
||
let tail = boxed_cons.tail; | ||
let result_u8: Result<u8, _> = tail.try_into(); | ||
|
||
match result_u8 { | ||
Ok(_) => { | ||
return Err(TypeError) | ||
.context(format!( | ||
"{} ({}) tail ({}) cannot be a byte", | ||
name, value, tail | ||
)) | ||
.map_err(From::from) | ||
} | ||
Err(_) => stack.push(tail), | ||
}; | ||
|
||
stack.push(boxed_cons.head); | ||
} | ||
TypedTerm::HeapBinary(heap_binary) => { | ||
byte_vec.extend_from_slice(heap_binary.as_bytes()); | ||
} | ||
TypedTerm::SubBinary(subbinary) => { | ||
if subbinary.is_binary() { | ||
if subbinary.is_aligned() { | ||
byte_vec.extend(unsafe { subbinary.as_bytes_unchecked() }); | ||
} else { | ||
byte_vec.extend(subbinary.full_byte_iter()); | ||
} | ||
} else { | ||
return Err(NotABinary) | ||
.context(element_context(name, value, top)) | ||
.map_err(From::from); | ||
} | ||
} | ||
TypedTerm::ProcBin(procbin) => { | ||
byte_vec.extend_from_slice(procbin.as_bytes()); | ||
} | ||
_ => { | ||
return Err(TypeError) | ||
.context(element_context(name, value, top)) | ||
.map_err(From::from) | ||
} | ||
} | ||
} | ||
|
||
Ok(process.binary_from_bytes(byte_vec.as_slice()).unwrap()) | ||
} | ||
|
||
fn element_context(name: &'static str, value: Term, element: Term) -> String { | ||
format!( | ||
"{} ({}) element ({}) is not a byte, binary, or nested iolist", | ||
name, value, element | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// wasm32 proptest cannot be compiled at the same time as non-wasm32 proptest, so disable tests that | ||
// use proptest completely for wasm32 | ||
// | ||
// See https://github.com/rust-lang/cargo/issues/4866 | ||
#[cfg(all(not(target_arch = "wasm32"), test))] | ||
mod test; | ||
|
||
use std::convert::TryInto; | ||
|
||
use anyhow::*; | ||
|
||
use liblumen_alloc::erts::exception; | ||
use liblumen_alloc::erts::process::Process; | ||
use liblumen_alloc::erts::term::prelude::*; | ||
|
||
use lumen_runtime_macros::native_implemented_function; | ||
|
||
use crate::otp::erlang::iolist_or_binary::{self, *}; | ||
|
||
/// Returns the size, in bytes, of the binary that would be result from iolist_to_binary/1 | ||
#[native_implemented_function(iolist_size/1)] | ||
pub fn native(process: &Process, iolist_or_binary: Term) -> exception::Result<Term> { | ||
iolist_or_binary::native(process, iolist_or_binary, iolist_or_binary_size) | ||
} | ||
|
||
fn iolist_or_binary_size(process: &Process, iolist_or_binary: Term) -> exception::Result<Term> { | ||
let mut size: usize = 0; | ||
let mut stack: Vec<Term> = vec![iolist_or_binary]; | ||
|
||
while let Some(top) = stack.pop() { | ||
match top.decode()? { | ||
TypedTerm::SmallInteger(small_integer) => { | ||
let _: u8 = small_integer | ||
.try_into() | ||
.with_context(|| element_type_context(iolist_or_binary, top))?; | ||
|
||
size += 1; | ||
} | ||
TypedTerm::Nil => (), | ||
TypedTerm::List(boxed_cons) => { | ||
// @type iolist :: maybe_improper_list(byte() | binary() | iolist(), | ||
// binary() | []) means that `byte()` isn't allowed | ||
// for `tail`s unlike `head`. | ||
|
||
let tail = boxed_cons.tail; | ||
let result_u8: Result<u8, _> = tail.try_into(); | ||
|
||
match result_u8 { | ||
Ok(_) => { | ||
return Err(TypeError) | ||
.context(format!( | ||
"iolist_or_binary ({}) tail ({}) cannot be a byte", | ||
iolist_or_binary, tail | ||
)) | ||
.map_err(From::from) | ||
} | ||
Err(_) => stack.push(tail), | ||
}; | ||
|
||
stack.push(boxed_cons.head); | ||
} | ||
TypedTerm::HeapBinary(heap_binary) => size += heap_binary.full_byte_len(), | ||
TypedTerm::MatchContext(match_context) => { | ||
if match_context.is_binary() { | ||
size += match_context.full_byte_len(); | ||
} else { | ||
return Err(NotABinary) | ||
.context(element_not_a_binary_context(iolist_or_binary, top)) | ||
.map_err(From::from); | ||
} | ||
} | ||
TypedTerm::ProcBin(proc_bin) => size += proc_bin.total_byte_len(), | ||
TypedTerm::SubBinary(subbinary) => { | ||
if subbinary.is_binary() { | ||
size += subbinary.full_byte_len(); | ||
} else { | ||
return Err(NotABinary) | ||
.context(element_not_a_binary_context(iolist_or_binary, top)) | ||
.map_err(From::from); | ||
} | ||
} | ||
_ => { | ||
return Err(TypeError) | ||
.context(element_type_context(iolist_or_binary, top)) | ||
.map_err(From::from); | ||
} | ||
} | ||
} | ||
|
||
process.integer(size).map_err(From::from) | ||
} |
Oops, something went wrong.