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
187impl std::io::Write for BufferMut<'_> {
188 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
189 let last_write = (*self.write_ptr) as usize;
190 let free = self.buffer.len() - last_write;
191 let n = buf.len().min(free);
192 if n == 0 && !buf.is_empty() {
193 return Err(std::io::Error::new(
194 std::io::ErrorKind::WriteZero,
195 "buffer full",
196 ));
197 }
198 self.buffer[last_write..last_write + n].copy_from_slice(&buf[..n]);
199 *self.write_ptr = (last_write + n) as u32;
200 Ok(n)
201 }
202
203 fn flush(&mut self) -> std::io::Result<()> {
204 Ok(())
205 }
206}
207
208#[inline(always)]
209pub(crate) fn compute_ptr<T>(ptr: *mut T, linear_mem_space: &WasmLinearMem) -> *mut T {
210 let mem_start_ptr = linear_mem_space.start_ptr;
211 (mem_start_ptr as isize + ptr as isize) as _
212}
213
214struct BuilderInfo<'instance> {
215 buffer: &'instance mut [u8],
216 read_ptr: &'instance mut u32,
217 write_ptr: &'instance mut u32,
218}
219
220fn from_raw_builder<'a>(builder_ptr: *mut BufferBuilder, mem: WasmLinearMem) -> BuilderInfo<'a> {
221 unsafe {
222 #[cfg(feature = "trace")]
223 {
224 if !mem.start_ptr.is_null() && mem.size > 0 {
225 let contract_mem = std::slice::from_raw_parts(mem.start_ptr, mem.size as usize);
226 tracing::trace!(
227 "*mut BufferBuilder <- offset: {}; in mem: {:?}",
228 builder_ptr as usize,
229 &contract_mem[builder_ptr as usize
230 ..builder_ptr as usize + std::mem::size_of::<BufferBuilder>()]
231 );
232 }
233 }
237
238 let builder_ptr = compute_ptr(builder_ptr, &mem);
239 let buf_builder: &'static mut BufferBuilder = Box::leak(Box::from_raw(builder_ptr));
240 #[cfg(feature = "trace")]
241 {
242 tracing::trace!("buf builder from FFI: {buf_builder:?}");
243 }
244
245 let read_ptr = Box::leak(Box::from_raw(compute_ptr(
246 buf_builder.last_read as *mut u32,
247 &mem,
248 )));
249 let write_ptr = Box::leak(Box::from_raw(compute_ptr(
250 buf_builder.last_write as *mut u32,
251 &mem,
252 )));
253 let buffer_ptr = compute_ptr(buf_builder.start as *mut u8, &mem);
254 let buffer =
255 &mut *std::ptr::slice_from_raw_parts_mut(buffer_ptr, buf_builder.capacity as usize);
256 BuilderInfo {
257 buffer,
258 read_ptr,
259 write_ptr,
260 }
261 }
262}
263
264#[derive(Debug)]
265pub struct Buffer<'instance> {
267 buffer: &'instance mut [u8],
268 read_ptr: &'instance mut u32,
270 write_ptr: &'instance u32,
271 builder_ptr: *mut BufferBuilder,
272 mem: WasmLinearMem,
273}
274
275impl<'instance> Buffer<'instance> {
276 pub unsafe fn read<T: Sized>(&mut self) -> T {
281 let next_offset = *self.read_ptr as usize;
282 let bytes = &self.buffer[next_offset..next_offset + std::mem::size_of::<T>()];
283 let t = std::ptr::read(bytes.as_ptr() as *const T);
284 *self.read_ptr += std::mem::size_of::<T>() as u32;
285 t
286 }
287
288 pub fn read_bytes(&mut self, len: usize) -> &[u8] {
290 let next_offset = *self.read_ptr as usize;
291 *self.read_ptr += len as u32;
292 &self.buffer[next_offset..next_offset + len]
293 }
294
295 pub fn read_all(&mut self) -> &[u8] {
297 let next_offset = *self.read_ptr as usize;
298 *self.read_ptr += self.buffer.len() as u32;
299 &self.buffer[next_offset..=*self.write_ptr as usize]
300 }
301
302 #[doc(hidden)]
307 pub unsafe fn exclusive(self) -> BufferMut<'instance> {
308 let Buffer {
309 builder_ptr, mem, ..
310 } = self;
311 let BuilderInfo {
312 buffer,
313 read_ptr,
314 write_ptr,
315 } = from_raw_builder(builder_ptr, mem);
316 BufferMut {
317 buffer,
318 read_ptr,
319 write_ptr,
320 builder_ptr,
321 mem,
322 }
323 }
324}
325
326#[doc(hidden)]
330#[allow(non_snake_case)]
331#[no_mangle]
332#[cfg(any(feature = "contract", test))]
333fn __frnt__initiate_buffer(capacity: u32) -> i64 {
334 let buf: Vec<u8> = Vec::with_capacity(capacity as usize);
335 let start = buf.as_ptr() as i64;
336
337 let last_read = Box::into_raw(Box::new(0u32));
338 let last_write = Box::into_raw(Box::new(0u32));
339 let buffer = Box::into_raw(Box::new(BufferBuilder {
340 start,
341 capacity,
342 last_read: last_read as _,
343 last_write: last_write as _,
344 }));
345 #[cfg(feature = "trace")]
346 {
347 tracing::trace!(
348 "new buffer ptr: {:p} -> {} as i64 w/ cap: {capacity}",
349 buf.as_ptr(),
350 start
351 );
352 tracing::trace!(
353 "last read ptr: {last_read:p} -> {} as i64",
354 last_read as i64
355 );
356 tracing::trace!(
357 "last write ptr: {last_write:p} -> {} as i64",
358 last_write as i64
359 );
360 tracing::trace!("buffer ptr: {buffer:p} -> {} as i64", buffer as i64);
361 }
362 std::mem::forget(buf);
363 buffer as i64
364}
365
366#[cfg(test)]
367mod test_io_write {
368 use super::*;
369 use std::io::Write;
370
371 unsafe fn host_buffer_mut(capacity: u32) -> BufferMut<'static> {
375 let builder_ptr = __frnt__initiate_buffer(capacity) as *mut BufferBuilder;
376 let linear_mem = WasmLinearMem {
377 start_ptr: std::ptr::null(),
378 size: 0,
379 };
380 BufferMut::from_ptr(builder_ptr, linear_mem)
381 }
382
383 fn io_write(buf: &mut BufferMut<'_>, data: &[u8]) -> std::io::Result<usize> {
385 Write::write(buf, data)
386 }
387
388 #[test]
389 fn write_trait_basic() {
390 let mut buf = unsafe { host_buffer_mut(32) };
391 let n = io_write(&mut buf, b"hello").unwrap();
392 assert_eq!(n, 5);
393 assert_eq!(buf.read_bytes(5), b"hello");
394 }
395
396 #[test]
397 fn write_trait_fills_exactly() {
398 let mut buf = unsafe { host_buffer_mut(4) };
399 let n = io_write(&mut buf, b"abcd").unwrap();
400 assert_eq!(n, 4);
401 assert_eq!(buf.read_bytes(4), b"abcd");
402 }
403
404 #[test]
405 fn write_trait_partial_when_near_full() {
406 let mut buf = unsafe { host_buffer_mut(4) };
407 io_write(&mut buf, b"ab").unwrap();
408 let n = io_write(&mut buf, b"xyz").unwrap();
410 assert_eq!(n, 2);
411 assert_eq!(buf.read_bytes(4), b"abxy");
412 }
413
414 #[test]
415 fn write_trait_error_when_full() {
416 let mut buf = unsafe { host_buffer_mut(2) };
417 io_write(&mut buf, b"ab").unwrap();
418 let err = io_write(&mut buf, b"c").unwrap_err();
419 assert_eq!(err.kind(), std::io::ErrorKind::WriteZero);
420 }
421
422 #[test]
423 fn write_trait_empty_slice_ok() {
424 let mut buf = unsafe { host_buffer_mut(4) };
425 let n = io_write(&mut buf, b"").unwrap();
426 assert_eq!(n, 0);
427 }
428
429 #[test]
430 fn write_all_trait() {
431 let mut buf = unsafe { host_buffer_mut(16) };
432 buf.write_all(b"hello world").unwrap();
433 assert_eq!(buf.read_bytes(11), b"hello world");
434 }
435
436 #[test]
437 fn write_all_insufficient_space() {
438 let mut buf = unsafe { host_buffer_mut(4) };
439 let err = buf.write_all(b"hello").unwrap_err();
440 assert_eq!(err.kind(), std::io::ErrorKind::WriteZero);
441 }
442
443 #[test]
444 fn bincode_serialize_into() {
445 let data: Vec<u32> = vec![1, 2, 3, 4, 5];
446 let size = bincode::serialized_size(&data).unwrap() as usize;
447 let mut buf = unsafe { host_buffer_mut(size as u32) };
448 bincode::serialize_into(&mut buf, &data).unwrap();
449 let result: Vec<u32> = bincode::deserialize(buf.read_bytes(size)).unwrap();
450 assert_eq!(result, data);
451 }
452}
453
454#[cfg(all(test, any(unix, windows), feature = "wasmer-tests"))]
455mod test {
456 use super::*;
457 use wasmer::{
458 imports, wat2wasm, AsStoreMut, Cranelift, Function, Instance, Module, Store, TypedFunction,
459 };
460
461 const TEST_MODULE: &str = r#"
462 (module
463 (func $initiate_buffer (import "freenet" "initiate_buffer") (param i32) (result i64))
464 (memory $locutus_mem (export "memory") 20)
465 (export "initiate_buffer" (func $initiate_buffer))
466 )"#;
467
468 fn build_test_mod() -> Result<(Store, Instance), Box<dyn std::error::Error>> {
469 let wasm_bytes = wat2wasm(TEST_MODULE.as_bytes())?;
470 let mut store = Store::new(Cranelift::new());
471 let module = Module::new(&store, wasm_bytes)?;
472
473 let init_buf_fn = Function::new_typed(&mut store, __frnt__initiate_buffer);
474 let imports = imports! {
475 "freenet" => { "initiate_buffer" => init_buf_fn }
476 };
477 let instance = Instance::new(&mut store, &module, &imports).unwrap();
478 Ok((store, instance))
479 }
480
481 fn init_buf(store: &mut impl AsStoreMut, instance: &Instance, size: u32) -> *mut BufferBuilder {
482 let initiate_buffer: TypedFunction<u32, i64> = instance
483 .exports
484 .get_typed_function(&store, "initiate_buffer")
485 .unwrap();
486 initiate_buffer.call(store, size).unwrap() as *mut BufferBuilder
487 }
488
489 #[test]
490 #[ignore]
491 fn read_and_write() -> Result<(), Box<dyn std::error::Error>> {
492 let (mut store, instance) = build_test_mod()?;
493 let mem = instance.exports.get_memory("memory")?.view(&store);
494 let linear_mem = WasmLinearMem {
495 start_ptr: mem.data_ptr() as *const _,
496 size: mem.data_size(),
497 };
498
499 let mut writer =
500 unsafe { BufferMut::from_ptr(init_buf(&mut store, &instance, 10), linear_mem) };
501 writer.write([1u8, 2])?;
502 let mut reader = writer.shared();
503 let r: [u8; 2] = unsafe { reader.read() };
504 assert_eq!(r, [1, 2]);
505
506 let mut writer = unsafe { reader.exclusive() };
507 writer.write([3u8, 4])?;
508 let mut reader = writer.shared();
509 let r: [u8; 2] = unsafe { reader.read() };
510 assert_eq!(r, [3, 4]);
511 Ok(())
512 }
513
514 #[test]
515 #[ignore]
516 fn read_and_write_bytes() -> Result<(), Box<dyn std::error::Error>> {
517 let (mut store, instance) = build_test_mod()?;
518 let mem = instance.exports.get_memory("memory")?.view(&store);
519 let linear_mem = WasmLinearMem {
520 start_ptr: mem.data_ptr() as *const _,
521 size: mem.data_size(),
522 };
523
524 let mut writer =
525 unsafe { BufferMut::from_ptr(init_buf(&mut store, &instance, 10), linear_mem) };
526 writer.write([1u8, 2])?;
527 let mut reader = writer.shared();
528 let r = reader.read_bytes(2);
529 assert_eq!(r, &[1, 2]);
530
531 let mut writer = unsafe { reader.exclusive() };
532 writer.write([3u8, 4])?;
533 let mut reader = writer.shared();
534 let r = reader.read_bytes(2);
535 assert_eq!(r, &[3, 4]);
536 Ok(())
537 }
538
539 #[test]
540 #[ignore]
541 fn update() -> Result<(), Box<dyn std::error::Error>> {
542 let (mut store, instance) = build_test_mod()?;
543 let mem = instance.exports.get_memory("memory")?.view(&store);
544 let linear_mem = WasmLinearMem {
545 start_ptr: mem.data_ptr() as *const _,
546 size: mem.data_size(),
547 };
548
549 let ptr = {
550 let mut writer =
551 unsafe { BufferMut::from_ptr(init_buf(&mut store, &instance, 10), linear_mem) };
552 writer.write([1u8, 2])?;
553 writer.ptr()
554 };
555
556 let writer = unsafe {
557 let builder = &mut *ptr;
558 builder.update_buffer(vec![3, 5, 7]);
559 BufferMut::from_ptr(ptr, linear_mem)
560 };
561 let mut reader = writer.shared();
562 assert_eq!(reader.read_all(), &[3, 5, 7]);
563
564 Ok(())
565 }
566}