use crate::term::{
Term,
boxed::{BoxedHeader, BoxedTag},
};
pub const SUB_BINARY_PAYLOAD_WORDS: usize = 4;
pub const SUB_BINARY_WORDS: usize = 1 + SUB_BINARY_PAYLOAD_WORDS;
const SUB_BINARY_FLAGS: u64 = 0;
pub fn write_sub_binary(
heap: &mut [u64],
parent_term: Term,
offset: usize,
length: usize,
) -> Option<Term> {
if heap.len() < SUB_BINARY_WORDS {
return None;
}
heap[0] = BoxedHeader::new(BoxedTag::SubBinary, SUB_BINARY_PAYLOAD_WORDS);
heap[1] = parent_term.raw();
heap[2] = offset as u64;
heap[3] = length as u64;
heap[4] = SUB_BINARY_FLAGS;
Some(Term::boxed_ptr(heap.as_ptr()))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::term::binary::write_binary;
use crate::term::boxed::{ProcBin, SubBinary};
use crate::term::shared_binary::{SharedBinary, write_proc_bin};
#[test]
fn sub_binary_views_proc_bin_with_fixed_five_word_layout() {
let bytes: Vec<u8> = (0_u8..32).collect();
let shared = SharedBinary::new(bytes);
let mut proc_heap = [0_u64; 3];
let parent = write_proc_bin(&mut proc_heap, &shared).expect("proc bin fits");
assert!(ProcBin::new(parent).is_some());
let mut heap = [0_u64; SUB_BINARY_WORDS];
let term = write_sub_binary(&mut heap, parent, 10, 10).expect("sub binary fits");
assert!(term.is_boxed());
assert_eq!(BoxedHeader::tag(heap[0]), Some(BoxedTag::SubBinary));
assert_eq!(BoxedHeader::size(heap[0]), SUB_BINARY_PAYLOAD_WORDS);
assert_eq!(heap[1], parent.raw());
assert_eq!(heap[2], 10);
assert_eq!(heap[3], 10);
assert_eq!(heap[4], SUB_BINARY_FLAGS);
assert_eq!(heap.len(), 5);
let sub_binary = SubBinary::new(term).expect("sub binary accessor");
assert_eq!(sub_binary.parent(), parent);
assert_eq!(sub_binary.len(), 10);
assert_eq!(sub_binary.as_bytes(), &shared.as_bytes()[10..20]);
assert_eq!(shared.ref_count(), 2);
}
#[test]
fn sub_binary_views_inline_binary_parent() {
let mut parent_heap = [0_u64; 4];
let parent =
write_binary(&mut parent_heap, b"0123456789abcdef").expect("inline binary fits");
let mut heap = [0_u64; SUB_BINARY_WORDS];
let term = write_sub_binary(&mut heap, parent, 4, 6).expect("sub binary fits");
let sub_binary = SubBinary::new(term).expect("sub binary accessor");
assert_eq!(sub_binary.parent(), parent);
assert_eq!(sub_binary.len(), 6);
assert_eq!(sub_binary.as_bytes(), b"456789");
}
#[test]
fn sub_binary_accessor_rejects_non_binary_parent_and_out_of_bounds_view() {
let mut heap = [0_u64; SUB_BINARY_WORDS];
let non_binary = write_sub_binary(&mut heap, Term::NIL, 0, 0).expect("sub binary fits");
assert!(SubBinary::new(non_binary).is_none());
let mut parent_heap = [0_u64; 3];
let parent = write_binary(&mut parent_heap, b"abc").expect("inline binary fits");
let out_of_bounds = write_sub_binary(&mut heap, parent, 2, 2).expect("sub binary fits");
assert!(SubBinary::new(out_of_bounds).is_none());
}
#[test]
fn sub_binary_writer_rejects_too_small_heap_slice() {
let mut heap = [0_u64; SUB_BINARY_WORDS - 1];
assert_eq!(write_sub_binary(&mut heap, Term::NIL, 0, 0), None);
}
}