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