Skip to main content

tancore/
buffer.rs

1// #todo Abbreviate to `buf`.
2// #todo consider other names: `Buf`, `Byte-Buffer`, etc.
3
4use std::sync::{Arc, RwLock};
5
6use tan::{context::Context, error::Error, expr::Expr, util::module_util::require_module};
7
8// #insight
9// size -> in bytes (maybe size-in-bytes ?)
10// length/count -> in items/elements (maybe size ?)
11
12// #todo add support for u32, u64 and maybe even other types.
13// #todo add support for little-endian/big-endian.
14
15// #todo use array instead of vec? can we have dynamic array, probably a slice.
16
17// #todo make buffer Iterable/Iterate
18
19pub fn buffer_new(args: &[Expr]) -> Result<Expr, Error> {
20    // #todo also support a default-element/fill option.
21
22    let [length] = args else {
23        return Err(Error::invalid_arguments("requires `length` argument", None));
24    };
25
26    // #todo create a helper.
27    let Some(length) = length.as_int() else {
28        return Err(Error::invalid_arguments(
29            &format!("length=`{length}` is not Int"),
30            length.range(),
31        ));
32    };
33
34    let length = length as usize;
35    // #todo allow for custom initial value.
36    let buf: Vec<u8> = vec![0; length];
37
38    Ok(Expr::Buffer(length, Arc::new(RwLock::new(buf))))
39}
40
41// (put buf index value)
42pub fn buffer_put(args: &[Expr]) -> Result<Expr, Error> {
43    // #todo enforce bounds!!
44
45    let [buffer, index, value] = args else {
46        return Err(Error::invalid_arguments(
47            "requires `index` and `value` arguments",
48            None,
49        ));
50    };
51
52    let Some((length, mut buffer)) = buffer.as_buffer_mut() else {
53        return Err(Error::invalid_arguments(
54            &format!("buffer=`{buffer}` is not a Buffer"),
55            buffer.range(),
56        ));
57    };
58
59    let Some(i) = index.as_int() else {
60        return Err(Error::invalid_arguments(
61            &format!("index=`{index}` is not Int"),
62            index.range(),
63        ));
64    };
65
66    if i < 0 {
67        // #todo use specialized error variant? e.g. invalid_argument_out_of_bounds?
68        return Err(Error::invalid_arguments(
69            &format!("buffer index=`{i}` cannot be negative"),
70            index.range(),
71        ));
72    }
73
74    let i = i as usize;
75
76    if i >= length {
77        return Err(Error::invalid_arguments(
78            &format!("buffer index=`{i}` must be less than the buffer length=`{length}`"),
79            index.range(),
80        ));
81    }
82
83    let Some(value) = value.as_u8() else {
84        return Err(Error::invalid_arguments(
85            &format!("value=`{value}` is not U8"),
86            value.range(),
87        ));
88    };
89
90    buffer[i] = value;
91
92    // #todo what should we return?
93    Ok(Expr::None)
94}
95
96pub fn setup_lib_buffer(context: &mut Context) {
97    // #todo put in 'buffer' path, and import selected functionality to prelude.
98    let module = require_module("prelude", context);
99
100    // #todo consider `Buf`.
101    module.insert_invocable("Buffer", Expr::foreign_func(&buffer_new));
102
103    // #todo also provide a put$$Int
104
105    module.insert_invocable("put$$Buffer$$Int$$U8", Expr::foreign_func(&buffer_put));
106}
107
108// #todo Push with Int, reuse push with U8
109// #todo Support 5u8.
110// #todo Implement Eq.