Significantly extended stdlib

This commit is contained in:
2026-01-27 20:53:45 +01:00
parent 66e5a71032
commit 534f08b45c
42 changed files with 635 additions and 211 deletions

View File

@@ -0,0 +1,25 @@
use std::borrow::Cow;
use std::pin::Pin;
use std::rc::Rc;
use futures::AsyncWrite;
use orchid_api_traits::Encode;
use orchid_extension::atom::Atomic;
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
#[derive(Clone)]
pub struct BlobAtom(pub(crate) Rc<Vec<u8>>);
impl Atomic for BlobAtom {
type Variant = OwnedVariant;
type Data = ();
}
impl OwnedAtom for BlobAtom {
type Refs = ();
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
async fn serialize(&self, write: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs {
self.0.encode(write).await.unwrap()
}
async fn deserialize(mut dctx: impl DeserializeCtx, _: Self::Refs) -> Self {
Self(dctx.read::<Rc<Vec<u8>>>().await)
}
}

View File

@@ -0,0 +1,146 @@
use std::rc::Rc;
use orchid_base::error::{OrcErrv, mk_errv};
use orchid_base::interner::is;
use orchid_extension::atom::TAtom;
use orchid_extension::atom_owned::own;
use orchid_extension::func_atom::get_arg_posv;
use orchid_extension::gen_expr::new_atom;
use orchid_extension::tree::{GenMember, comments, fun, prefix};
use crate::std::binary::binary_atom::BlobAtom;
use crate::std::boolean::Bool;
use crate::{Int, OrcOpt, Tpl};
async fn bounds_error(
expected: String,
blob: &BlobAtom,
args: impl IntoIterator<Item = usize>,
) -> OrcErrv {
mk_errv(
is("Index out of bounds").await,
format!("Selected {expected} from blob of len {}", blob.0.len()),
get_arg_posv(args).await,
)
}
pub fn gen_binary_lib() -> Vec<GenMember> {
prefix("std", [comments(
["A Blob is a sequence of bytes stored and processed efficiently."],
prefix("binary", [
comments(
["Appends a binary blob to another", "|type: Blob -> Blob -> Blob|"],
fun(true, "concat", async |a: TAtom<BlobAtom>, b: TAtom<BlobAtom>| {
new_atom(BlobAtom(Rc::new(
own(&a).await.0.iter().chain(&own(&b).await.0[..]).copied().collect(),
)))
}),
),
comments(
[
"Copies out a subsection of the binary into a new blob \
specified by starting point and length",
"|type: Blob -> Int -> Int -> Blob|",
],
fun(true, "slice", async |a: TAtom<BlobAtom>, Int(start): Int, Int(len): Int| {
let blob = own(&a).await;
if start + len > blob.0.len() as i64 {
return Err(bounds_error(format!("{start}+{len}"), &blob, 0..3).await);
}
let sub = blob.0[start as usize..(start + len) as usize].to_vec();
Ok(new_atom(BlobAtom(Rc::new(sub))))
}),
),
comments(
[
"Return the index where the second binary appears as a subsection of the first",
"|type: Blob -> Blob -> std::option Int|",
],
fun(true, "find", async |haystack: TAtom<BlobAtom>, needle: TAtom<BlobAtom>| {
let haystack_vec = own(&haystack).await;
let needle_vec = own(&needle).await;
for i in 0..haystack_vec.0.len() - needle_vec.0.len() {
if haystack_vec.0[i..].starts_with(&needle_vec.0) {
return OrcOpt(Some(Int(i as i64)));
}
}
OrcOpt(None)
}),
),
comments(
[
"Splits the binary into two halves at the given byte index",
"|type: Blob -> Int -> std::tuple Blob Blob|",
],
fun(true, "split", async |a: TAtom<BlobAtom>, i: Int| {
let v = own(&a).await;
if v.0.len() < i.0 as usize {
return Err(bounds_error(i.0.to_string(), &v, 1..2).await);
}
let (l, r) = v.0.split_at(i.0 as usize);
Ok(Tpl((
new_atom(BlobAtom(Rc::new(l.to_vec()))),
new_atom(BlobAtom(Rc::new(r.to_vec()))),
)))
}),
),
comments(
[
"Takes a binary, a starting point, a length no greater than 8, and a boolean flag \
which is true if the number is little endian. Reads a usize from \
the specified location in the binary.",
"|type: Blob -> Int -> Int -> Bool -> Int|",
],
fun(
true,
"get_int",
async |bin: TAtom<BlobAtom>, Int(start): Int, Int(len): Int, Bool(le): Bool| {
let vec = own(&bin).await;
if start + len > vec.0.len() as i64 {
return Err(bounds_error(format!("{start}+{len}"), &vec, 1..3).await);
}
if 8 < len {
return Err(mk_errv(
is("Too many bytes for int conversion").await,
format!("At most 8 bytes fit into an Int, requested {len}"),
get_arg_posv(3..4).await,
));
}
let slice = &vec.0[start as usize..(start + len) as usize];
let mut data = [0u8; 8];
Ok(Int(if le {
data[..len as usize].copy_from_slice(slice);
i64::from_le_bytes(data)
} else {
data[(8 - len as usize)..].copy_from_slice(slice);
i64::from_be_bytes(data)
}))
},
),
),
comments(
[
"Takes a length no greater than int_bytes, a little endian flag and a number to encode. \
Turns the least significant bytes of the given int into a binary.",
"|type: Int -> Bool -> Int -> Blob|",
],
fun(true, "from_num", async |len: Int, le: Bool, val: Int| {
if 8 < len.0 {
return Err(mk_errv(
is("Too many bytes for int conversion").await,
format!("Ints are 8 bytes, attempted to write {} byte buffer", len.0),
get_arg_posv(0..1).await,
));
}
let data = if le.0 { val.0.to_le_bytes() } else { val.0.to_be_bytes() };
let data = if le.0 { &data[..len.0 as usize] } else { &data[(8 - len.0 as usize)..] };
Ok(new_atom(BlobAtom(Rc::new(data.to_vec()))))
}),
),
comments(
["Returns the number of bytes in a binary", "|type: Blob -> Int|"],
fun(true, "size", async |blob: TAtom<BlobAtom>| Int(own(&blob).await.0.len() as i64)),
),
]),
)])
}

View File

@@ -0,0 +1,2 @@
pub mod binary_atom;
pub mod binary_lib;