1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use std::ffi::OsStr;
use std::fs::File;
use std::io::Read;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use crate::libapi::{Class, ClassDef, FuncDef, Module};
use crate::str::Buf;
use crate::sym::Symbol;
use crate::val::Val;
const IO_FILE: FuncDef = func!(
/// Load the contents of a file into a string
resynth fn file(
/// Path to the file to load
filename: Str,
=>
=>
Str
) -> Str
|mut args| {
let arg: Buf = args.next().into();
let path = Path::new(OsStr::from_bytes(arg.as_ref()));
let mut f = File::open(path)?;
let mut buf = Vec::new();
f.read_to_end(&mut buf)?;
Ok(Val::str(buf))
}
);
#[derive(Debug, PartialEq, Eq, Default)]
struct BufIo {
buf: Vec<u8>,
taken: usize,
}
impl BufIo {
pub fn read(&mut self, bytes: usize) -> &[u8] {
let remaining = self.buf.len() - self.taken;
let take = std::cmp::min(remaining, bytes);
let ret = &self.buf[self.taken..self.taken + take];
self.taken += take;
ret
}
pub fn read_all(&mut self) -> &[u8] {
let remaining = self.buf.len() - self.taken;
let ret = &self.buf[self.taken..self.taken + remaining];
self.taken += remaining;
ret
}
}
impl<T> From<T> for BufIo
where
T: AsRef<[u8]>,
{
fn from(s: T) -> Self {
Self {
buf: Vec::from(s.as_ref()),
taken: 0,
}
}
}
const BUFIO_READ: FuncDef = func!(
/// Read the next `bytes` bytes from the buffer, advancing the read position.
/// If fewer than `bytes` bytes remain, returns what is left.
resynth fn read(
/// Number of bytes to read from the buffer
bytes: U64,
=>
=>
Void
) -> Str
|mut args| {
let bytes: u64 = args.next().into();
let obj = args.take_this();
let mut r = obj.borrow_mut();
let this: &mut BufIo = r.as_mut_any().downcast_mut().unwrap();
Ok(Val::Str(this.read(bytes as usize).into()))
}
);
const BUFIO_READ_ALL: FuncDef = func!(
/// Read all remaining bytes from the buffer, advancing the read position to the end.
resynth fn read_all(
=>
=>
Void
) -> Str
|mut args| {
let obj = args.take_this();
let mut r = obj.borrow_mut();
let this: &mut BufIo = r.as_mut_any().downcast_mut().unwrap();
Ok(Val::Str(this.read_all().into()))
}
);
static BUFIO_CLASS: ClassDef = class!(
/// # Buffered I/O
///
/// A stateful byte buffer that can be read in sequential chunks. Useful for
/// splitting a pre-built payload (such as a TLS record or protocol message)
/// across multiple packets without duplicating the content.
///
/// Create with `io::bufio(...)`, then call `read(n)` to consume `n` bytes at
/// a time, or `read_all()` to consume the remainder.
resynth class BufIO {
read => Symbol::Func(&BUFIO_READ),
read_all => Symbol::Func(&BUFIO_READ_ALL),
}
);
impl Class for BufIo {
fn def(&self) -> &'static ClassDef {
&BUFIO_CLASS
}
}
const BUFIO: FuncDef = func!(
/// Create a `BufIO` buffer from one or more byte strings, from which bytes
/// can be consumed in sequential chunks using `read(n)` and `read_all()`.
/// This is useful for splitting a pre-assembled payload across multiple
/// packets — for example, sending the first 15 bytes of a TLS record in one
/// TCP segment and the rest in another.
resynth fn bufio(
=>
=>
Str
) -> Class(&BUFIO_CLASS)
|mut args| {
let bytes: Buf = args.join_extra(b"").into();
Ok(Val::from(BufIo::from(bytes.as_ref())))
}
);
pub const MODULE: Module = module!(
/// # Buffers and File I/O
///
/// Buffered I/O — read from and write to byte buffers and files.
resynth mod io {
BufIO => Symbol::Class(&BUFIO_CLASS),
file => Symbol::Func(&IO_FILE),
bufio => Symbol::Func(&BUFIO),
}
);