otter/types/terms/
binary.rs1use core::marker::PhantomData;
6use std::str::Utf8Error;
7
8use crate::types::sealed::Sealed;
9use crate::types::{AnyTerm, Env, Invariant, RawTerm, Term};
10
11#[derive(Clone, Copy)]
17pub struct Binary<'id> {
18 raw_term: RawTerm,
19 _id: Invariant<'id>,
20}
21
22#[derive(Clone, Copy)]
28pub struct Bitstring<'id> {
29 raw_term: RawTerm,
30 _id: Invariant<'id>,
31}
32
33impl<'id> Bitstring<'id> {
34 #[crate::raw]
35 pub(crate) fn from_raw(raw_term: RawTerm) -> Self {
36 Self { raw_term, _id: PhantomData }
37 }
38
39 pub fn is_binary(self, env: impl Env<'id>) -> bool {
41 unsafe { enif_ffi::is_binary(env.raw_env(), self.raw_term) != 0 }
42 }
43
44 pub fn to_binary(self, env: impl Env<'id>) -> Option<Binary<'id>> {
46 self.is_binary(env)
47 .then_some(Binary { raw_term: self.raw_term, _id: PhantomData })
48 }
49}
50
51impl<'id> Binary<'id> {
52 #[crate::raw]
53 pub(crate) fn from_raw(raw_term: RawTerm) -> Self {
54 Self { raw_term, _id: PhantomData }
55 }
56
57 pub fn as_bytes(self, env: impl Env<'id>) -> &'id [u8] {
62 let mut bin: enif_ffi::Binary = unsafe { std::mem::zeroed() };
63 let ok = unsafe { enif_ffi::inspect_binary(env.raw_env(), self.raw_term, &mut bin) };
64 assert!(ok != 0, "inspect_binary failed on a validated Binary");
65 unsafe { std::slice::from_raw_parts(bin.data, bin.size) }
66 }
67
68 pub fn len(self, env: impl Env<'id>) -> usize {
70 self.as_bytes(env).len()
71 }
72
73 pub fn is_empty(self, env: impl Env<'id>) -> bool {
75 self.len(env) == 0
76 }
77
78 pub fn try_str(self, env: impl Env<'id>) -> Result<&'id str, Utf8Error> {
81 std::str::from_utf8(self.as_bytes(env))
82 }
83
84 pub fn sub(self, env: impl Env<'id>, pos: usize, len: usize) -> Binary<'id> {
87 let total = self.len(env);
88 assert!(
89 pos.checked_add(len).is_some_and(|end| end <= total),
90 "sub-binary out of bounds: pos({pos}) + len({len}) > {total}"
91 );
92 let raw_term = unsafe { enif_ffi::make_sub_binary(env.raw_env(), self.raw_term, pos, len) };
93 Binary { raw_term, _id: PhantomData }
94 }
95
96 pub fn from_bytes(env: impl Env<'id>, data: &[u8]) -> Binary<'id> {
99 let mut raw_term: RawTerm = 0;
100 unsafe {
101 let ptr = enif_ffi::make_new_binary(env.raw_env(), data.len(), &mut raw_term);
102 assert!(!ptr.is_null(), "enif_make_new_binary returned null");
108 std::ptr::copy_nonoverlapping(data.as_ptr(), ptr, data.len());
109 }
110 Binary { raw_term, _id: PhantomData }
111 }
112
113 pub fn is_binary(env: impl Env<'id>, term: impl Term<'id>) -> bool {
116 unsafe { enif_ffi::is_binary(env.raw_env(), term.raw_term()) != 0 }
117 }
118
119 pub fn deserialize(self, env: impl Env<'id>, safe: bool) -> Option<AnyTerm<'id>> {
123 let bytes = self.as_bytes(env);
124 let opts = if safe { enif_ffi::BIN2TERM_SAFE } else { 0 };
125 let mut term: RawTerm = 0;
126 let consumed = unsafe {
127 enif_ffi::binary_to_term(env.raw_env(), bytes.as_ptr(), bytes.len(), &mut term, opts)
128 };
129 (consumed != 0).then(|| AnyTerm::wrap(term, env))
130 }
131}
132
133impl std::fmt::Debug for Binary<'_> {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 write!(f, "Binary")
136 }
137}
138
139impl PartialEq for Binary<'_> {
140 fn eq(&self, other: &Self) -> bool {
141 unsafe { enif_ffi::is_identical(self.raw_term, other.raw_term) != 0 }
142 }
143}
144impl Eq for Binary<'_> {}
145impl PartialOrd for Binary<'_> {
146 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
147 Some(self.cmp(other))
148 }
149}
150impl Ord for Binary<'_> {
151 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
152 unsafe { enif_ffi::compare(self.raw_term, other.raw_term) }.cmp(&0)
153 }
154}
155
156impl std::fmt::Debug for Bitstring<'_> {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 write!(f, "Bitstring")
159 }
160}
161impl PartialEq for Bitstring<'_> {
162 fn eq(&self, other: &Self) -> bool {
163 unsafe { enif_ffi::is_identical(self.raw_term, other.raw_term) != 0 }
164 }
165}
166impl Eq for Bitstring<'_> {}
167impl PartialOrd for Bitstring<'_> {
168 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
169 Some(self.cmp(other))
170 }
171}
172impl Ord for Bitstring<'_> {
173 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
174 unsafe { enif_ffi::compare(self.raw_term, other.raw_term) }.cmp(&0)
175 }
176}
177
178impl<'id> Sealed for Binary<'id> {}
179impl<'id> Term<'id> for Binary<'id> {
180 fn raw_term(self) -> RawTerm {
181 self.raw_term
182 }
183}
184
185impl<'id> Sealed for Bitstring<'id> {}
186impl<'id> Term<'id> for Bitstring<'id> {
187 fn raw_term(self) -> RawTerm {
188 self.raw_term
189 }
190}