1use super::WasmLinearMem;
4
5#[doc(hidden)]
6#[derive(Clone, Copy, Debug)]
7#[repr(C)]
8pub struct BufferBuilder {
9 start: i64,
10 capacity: u32,
11 last_read: i64,
12 last_write: i64,
13}
14
15impl BufferBuilder {
16 pub fn capacity(&self) -> usize {
18 self.capacity as _
19 }
20
21 #[cfg(not(feature = "contract"))]
23 pub fn bytes_written(&self, mem: &WasmLinearMem) -> usize {
24 unsafe {
25 let ptr = compute_ptr(self.last_write as *mut u32, mem);
26 *ptr as usize
27 }
28 }
29
30 #[cfg(feature = "contract")]
31 pub fn bytes_written(&self) -> usize {
32 unsafe { *(self.last_write as *mut u32) as usize }
33 }
34
35 pub fn start(&self) -> *mut u8 {
37 self.start as _
38 }
39
40 pub unsafe fn update_buffer(&mut self, data: Vec<u8>) {
44 let read_ptr = Box::leak(Box::from_raw(self.last_read as *mut u32));
45 let write_ptr = Box::leak(Box::from_raw(self.last_write as *mut u32));
46
47 let prev = Vec::from_raw_parts(self.start as *mut u8, *write_ptr as usize, self.capacity());
49 std::mem::drop(prev);
50
51 let new_ptr = data.as_ptr();
53 self.start = new_ptr as i64;
54 self.capacity = data.capacity() as _;
55 *read_ptr = 0;
56 *write_ptr = data.len().saturating_sub(1) as _; std::mem::forget(data);
58 }
59
60 pub fn to_ptr(self) -> *mut BufferBuilder {
62 Box::into_raw(Box::new(self))
63 }
64}
65
66#[derive(thiserror::Error, Debug)]
68pub enum Error {
69 #[error("insufficient memory, needed {req} bytes but had {free} bytes")]
71 InsufficientMemory {
72 req: usize,
74 free: usize,
76 },
77}
78
79#[derive(Debug)]
81pub struct BufferMut<'instance> {
82 buffer: &'instance mut [u8],
83 read_ptr: &'instance u32,
85 write_ptr: &'instance mut u32,
87 builder_ptr: *mut BufferBuilder,
89 mem: WasmLinearMem,
91}
92
93impl<'instance> BufferMut<'instance> {
94 pub fn write<T>(&mut self, obj: T) -> Result<(), Error>
98 where
99 T: AsRef<[u8]>,
100 {
101 let obj = obj.as_ref();
102 if obj.len() > self.buffer.len() {
103 return Err(Error::InsufficientMemory {
104 req: obj.len(),
105 free: self.buffer.len(),
106 });
107 }
108 let mut last_write = (*self.write_ptr) as usize;
109 let free_right = self.buffer.len() - last_write;
110 if obj.len() <= free_right {
111 let copy_to = &mut self.buffer[last_write..last_write + obj.len()];
112 copy_to.copy_from_slice(obj);
113 last_write += obj.len();
114 *self.write_ptr = last_write as u32;
115 Ok(())
116 } else {
117 Err(Error::InsufficientMemory {
118 req: obj.len(),
119 free: free_right,
120 })
121 }
122 }
123
124 pub fn read_bytes(&self, len: usize) -> &[u8] {
128 let next_offset = *self.read_ptr as usize;
129 &self.buffer[next_offset..next_offset + len]
131 }
132
133 pub fn shared(self) -> Buffer<'instance> {
135 let BufferMut {
136 builder_ptr, mem, ..
137 } = self;
138 let BuilderInfo {
139 buffer,
140 read_ptr,
141 write_ptr,
142 ..
143 } = from_raw_builder(builder_ptr, mem);
144 Buffer {
145 buffer,
146 read_ptr,
147 write_ptr,
148 builder_ptr,
149 mem,
150 }
151 }
152
153 pub fn capacity(&self) -> usize {
155 unsafe {
156 let p = &*compute_ptr(self.builder_ptr, &self.mem);
157 p.capacity as _
158 }
159 }
160
161 pub unsafe fn from_ptr(
164 builder_ptr: *mut BufferBuilder,
165 linear_mem_space: WasmLinearMem,
166 ) -> Self {
167 let BuilderInfo {
168 buffer,
169 read_ptr,
170 write_ptr,
171 } = from_raw_builder(builder_ptr, linear_mem_space);
172 BufferMut {
173 buffer,
174 read_ptr,
175 write_ptr,
176 builder_ptr,
177 mem: linear_mem_space,
178 }
179 }
180
181 pub fn ptr(&self) -> *mut BufferBuilder {
183 self.builder_ptr
184 }
185}
186
187#[inline(always)]
188pub(crate) fn compute_ptr<T>(ptr: *mut T, linear_mem_space: &WasmLinearMem) -> *mut T {
189 let mem_start_ptr = linear_mem_space.start_ptr;
190 (mem_start_ptr as isize + ptr as isize) as _
191}
192
193struct BuilderInfo<'instance> {
194 buffer: &'instance mut [u8],
195 read_ptr: &'instance mut u32,
196 write_ptr: &'instance mut u32,
197}
198
199fn from_raw_builder<'a>(builder_ptr: *mut BufferBuilder, mem: WasmLinearMem) -> BuilderInfo<'a> {
200 unsafe {
201 #[cfg(feature = "trace")]
202 {
203 let contract_mem = std::slice::from_raw_parts(mem.start_ptr, mem.size as usize);
204 tracing::trace!(
205 "*mut BufferBuilder <- offset: {}; in mem: {:?}",
206 builder_ptr as usize,
207 &contract_mem[builder_ptr as usize
208 ..builder_ptr as usize + std::mem::size_of::<BufferBuilder>()]
209 );
210 }
214
215 let builder_ptr = compute_ptr(builder_ptr, &mem);
216 let buf_builder: &'static mut BufferBuilder = Box::leak(Box::from_raw(builder_ptr));
217 #[cfg(feature = "trace")]
218 {
219 tracing::trace!("buf builder from FFI: {buf_builder:?}");
220 }
221
222 let read_ptr = Box::leak(Box::from_raw(compute_ptr(
223 buf_builder.last_read as *mut u32,
224 &mem,
225 )));
226 let write_ptr = Box::leak(Box::from_raw(compute_ptr(
227 buf_builder.last_write as *mut u32,
228 &mem,
229 )));
230 let buffer_ptr = compute_ptr(buf_builder.start as *mut u8, &mem);
231 let buffer =
232 &mut *std::ptr::slice_from_raw_parts_mut(buffer_ptr, buf_builder.capacity as usize);
233 BuilderInfo {
234 buffer,
235 read_ptr,
236 write_ptr,
237 }
238 }
239}
240
241#[derive(Debug)]
242pub struct Buffer<'instance> {
244 buffer: &'instance mut [u8],
245 read_ptr: &'instance mut u32,
247 write_ptr: &'instance u32,
248 builder_ptr: *mut BufferBuilder,
249 mem: WasmLinearMem,
250}
251
252impl<'instance> Buffer<'instance> {
253 pub unsafe fn read<T: Sized>(&mut self) -> T {
258 let next_offset = *self.read_ptr as usize;
259 let bytes = &self.buffer[next_offset..next_offset + std::mem::size_of::<T>()];
260 let t = std::ptr::read(bytes.as_ptr() as *const T);
261 *self.read_ptr += std::mem::size_of::<T>() as u32;
262 t
263 }
264
265 pub fn read_bytes(&mut self, len: usize) -> &[u8] {
267 let next_offset = *self.read_ptr as usize;
268 *self.read_ptr += len as u32;
269 &self.buffer[next_offset..next_offset + len]
270 }
271
272 pub fn read_all(&mut self) -> &[u8] {
274 let next_offset = *self.read_ptr as usize;
275 *self.read_ptr += self.buffer.len() as u32;
276 &self.buffer[next_offset..=*self.write_ptr as usize]
277 }
278
279 #[doc(hidden)]
284 pub unsafe fn exclusive(self) -> BufferMut<'instance> {
285 let Buffer {
286 builder_ptr, mem, ..
287 } = self;
288 let BuilderInfo {
289 buffer,
290 read_ptr,
291 write_ptr,
292 } = from_raw_builder(builder_ptr, mem);
293 BufferMut {
294 buffer,
295 read_ptr,
296 write_ptr,
297 builder_ptr,
298 mem,
299 }
300 }
301}
302
303#[doc(hidden)]
307#[allow(non_snake_case)]
308#[no_mangle]
309#[cfg(any(feature = "contract", test))]
310fn __frnt__initiate_buffer(capacity: u32) -> i64 {
311 let buf: Vec<u8> = Vec::with_capacity(capacity as usize);
312 let start = buf.as_ptr() as i64;
313
314 let last_read = Box::into_raw(Box::new(0u32));
315 let last_write = Box::into_raw(Box::new(0u32));
316 let buffer = Box::into_raw(Box::new(BufferBuilder {
317 start,
318 capacity,
319 last_read: last_read as _,
320 last_write: last_write as _,
321 }));
322 #[cfg(feature = "trace")]
323 {
324 tracing::trace!(
325 "new buffer ptr: {:p} -> {} as i64 w/ cap: {capacity}",
326 buf.as_ptr(),
327 start
328 );
329 tracing::trace!(
330 "last read ptr: {last_read:p} -> {} as i64",
331 last_read as i64
332 );
333 tracing::trace!(
334 "last write ptr: {last_write:p} -> {} as i64",
335 last_write as i64
336 );
337 tracing::trace!("buffer ptr: {buffer:p} -> {} as i64", buffer as i64);
338 }
339 std::mem::forget(buf);
340 buffer as i64
341}
342
343#[cfg(all(test, any(unix, windows)))]
344mod test {
345 use super::*;
346 use wasmer::{
347 imports, wat2wasm, AsStoreMut, Cranelift, Function, Instance, Module, Store, TypedFunction,
348 };
349
350 const TEST_MODULE: &str = r#"
351 (module
352 (func $initiate_buffer (import "freenet" "initiate_buffer") (param i32) (result i64))
353 (memory $locutus_mem (export "memory") 20)
354 (export "initiate_buffer" (func $initiate_buffer))
355 )"#;
356
357 fn build_test_mod() -> Result<(Store, Instance), Box<dyn std::error::Error>> {
358 let wasm_bytes = wat2wasm(TEST_MODULE.as_bytes())?;
359 let mut store = Store::new(Cranelift::new());
360 let module = Module::new(&store, wasm_bytes)?;
361
362 let init_buf_fn = Function::new_typed(&mut store, __frnt__initiate_buffer);
363 let imports = imports! {
364 "freenet" => { "initiate_buffer" => init_buf_fn }
365 };
366 let instance = Instance::new(&mut store, &module, &imports).unwrap();
367 Ok((store, instance))
368 }
369
370 fn init_buf(store: &mut impl AsStoreMut, instance: &Instance, size: u32) -> *mut BufferBuilder {
371 let initiate_buffer: TypedFunction<u32, i64> = instance
372 .exports
373 .get_typed_function(&store, "initiate_buffer")
374 .unwrap();
375 initiate_buffer.call(store, size).unwrap() as *mut BufferBuilder
376 }
377
378 #[test]
379 #[ignore]
380 fn read_and_write() -> Result<(), Box<dyn std::error::Error>> {
381 let (mut store, instance) = build_test_mod()?;
382 let mem = instance.exports.get_memory("memory")?.view(&store);
383 let linear_mem = WasmLinearMem {
384 start_ptr: mem.data_ptr() as *const _,
385 size: mem.data_size(),
386 };
387
388 let mut writer =
389 unsafe { BufferMut::from_ptr(init_buf(&mut store, &instance, 10), linear_mem) };
390 writer.write([1u8, 2])?;
391 let mut reader = writer.shared();
392 let r: [u8; 2] = unsafe { reader.read() };
393 assert_eq!(r, [1, 2]);
394
395 let mut writer = unsafe { reader.exclusive() };
396 writer.write([3u8, 4])?;
397 let mut reader = writer.shared();
398 let r: [u8; 2] = unsafe { reader.read() };
399 assert_eq!(r, [3, 4]);
400 Ok(())
401 }
402
403 #[test]
404 #[ignore]
405 fn read_and_write_bytes() -> Result<(), Box<dyn std::error::Error>> {
406 let (mut store, instance) = build_test_mod()?;
407 let mem = instance.exports.get_memory("memory")?.view(&store);
408 let linear_mem = WasmLinearMem {
409 start_ptr: mem.data_ptr() as *const _,
410 size: mem.data_size(),
411 };
412
413 let mut writer =
414 unsafe { BufferMut::from_ptr(init_buf(&mut store, &instance, 10), linear_mem) };
415 writer.write([1u8, 2])?;
416 let mut reader = writer.shared();
417 let r = reader.read_bytes(2);
418 assert_eq!(r, &[1, 2]);
419
420 let mut writer = unsafe { reader.exclusive() };
421 writer.write([3u8, 4])?;
422 let mut reader = writer.shared();
423 let r = reader.read_bytes(2);
424 assert_eq!(r, &[3, 4]);
425 Ok(())
426 }
427
428 #[test]
429 #[ignore]
430 fn update() -> Result<(), Box<dyn std::error::Error>> {
431 let (mut store, instance) = build_test_mod()?;
432 let mem = instance.exports.get_memory("memory")?.view(&store);
433 let linear_mem = WasmLinearMem {
434 start_ptr: mem.data_ptr() as *const _,
435 size: mem.data_size(),
436 };
437
438 let ptr = {
439 let mut writer =
440 unsafe { BufferMut::from_ptr(init_buf(&mut store, &instance, 10), linear_mem) };
441 writer.write([1u8, 2])?;
442 writer.ptr()
443 };
444
445 let writer = unsafe {
446 let builder = &mut *ptr;
447 builder.update_buffer(vec![3, 5, 7]);
448 BufferMut::from_ptr(ptr, linear_mem)
449 };
450 let mut reader = writer.shared();
451 assert_eq!(reader.read_all(), &[3, 5, 7]);
452
453 Ok(())
454 }
455}