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 #[cfg(feature = "contract")]
37 pub fn bytes_read(&self) -> usize {
38 unsafe { *(self.last_read as *mut u32) as usize }
39 }
40
41 #[cfg(feature = "contract")]
43 pub fn reset_pointers(&mut self) {
44 unsafe {
45 *(self.last_read as *mut u32) = 0;
46 *(self.last_write as *mut u32) = 0;
47 }
48 }
49
50 pub fn start(&self) -> *mut u8 {
52 self.start as _
53 }
54
55 pub fn last_read_ptr(&self) -> *mut u32 {
57 self.last_read as *mut u32
58 }
59
60 pub fn last_write_ptr(&self) -> *mut u32 {
62 self.last_write as *mut u32
63 }
64
65 pub unsafe fn update_buffer(&mut self, data: Vec<u8>) {
69 let read_ptr = Box::leak(Box::from_raw(self.last_read as *mut u32));
70 let write_ptr = Box::leak(Box::from_raw(self.last_write as *mut u32));
71
72 let prev = Vec::from_raw_parts(self.start as *mut u8, *write_ptr as usize, self.capacity());
74 std::mem::drop(prev);
75
76 let new_ptr = data.as_ptr();
78 self.start = new_ptr as i64;
79 self.capacity = data.capacity() as _;
80 *read_ptr = 0;
81 *write_ptr = data.len().saturating_sub(1) as _; std::mem::forget(data);
83 }
84
85 pub fn to_ptr(self) -> *mut BufferBuilder {
87 Box::into_raw(Box::new(self))
88 }
89}
90
91#[derive(thiserror::Error, Debug)]
93pub enum Error {
94 #[error("insufficient memory, needed {req} bytes but had {free} bytes")]
96 InsufficientMemory {
97 req: usize,
99 free: usize,
101 },
102}
103
104#[derive(Debug)]
106pub struct BufferMut<'instance> {
107 buffer: &'instance mut [u8],
108 read_ptr: &'instance u32,
110 write_ptr: &'instance mut u32,
112 builder_ptr: *mut BufferBuilder,
114 mem: WasmLinearMem,
116}
117
118impl<'instance> BufferMut<'instance> {
119 pub fn write<T>(&mut self, obj: T) -> Result<(), Error>
123 where
124 T: AsRef<[u8]>,
125 {
126 let obj = obj.as_ref();
127 if obj.len() > self.buffer.len() {
128 return Err(Error::InsufficientMemory {
129 req: obj.len(),
130 free: self.buffer.len(),
131 });
132 }
133 let mut last_write = (*self.write_ptr) as usize;
134 let free_right = self.buffer.len() - last_write;
135 if obj.len() <= free_right {
136 let copy_to = &mut self.buffer[last_write..last_write + obj.len()];
137 copy_to.copy_from_slice(obj);
138 last_write += obj.len();
139 *self.write_ptr = last_write as u32;
140 Ok(())
141 } else {
142 Err(Error::InsufficientMemory {
143 req: obj.len(),
144 free: free_right,
145 })
146 }
147 }
148
149 pub fn read_bytes(&self, len: usize) -> &[u8] {
153 let next_offset = *self.read_ptr as usize;
154 &self.buffer[next_offset..next_offset + len]
156 }
157
158 pub fn shared(self) -> Buffer<'instance> {
160 let BufferMut {
161 builder_ptr, mem, ..
162 } = self;
163 let BuilderInfo {
164 buffer,
165 read_ptr,
166 write_ptr,
167 ..
168 } = from_raw_builder(builder_ptr, mem);
169 Buffer {
170 buffer,
171 read_ptr,
172 write_ptr,
173 builder_ptr,
174 mem,
175 }
176 }
177
178 pub fn capacity(&self) -> usize {
180 unsafe {
181 let p = &*compute_ptr(self.builder_ptr, &self.mem);
182 p.capacity as _
183 }
184 }
185
186 pub unsafe fn from_ptr(
189 builder_ptr: *mut BufferBuilder,
190 linear_mem_space: WasmLinearMem,
191 ) -> Self {
192 let BuilderInfo {
193 buffer,
194 read_ptr,
195 write_ptr,
196 } = from_raw_builder(builder_ptr, linear_mem_space);
197 BufferMut {
198 buffer,
199 read_ptr,
200 write_ptr,
201 builder_ptr,
202 mem: linear_mem_space,
203 }
204 }
205
206 pub fn ptr(&self) -> *mut BufferBuilder {
208 self.builder_ptr
209 }
210}
211
212impl std::io::Write for BufferMut<'_> {
213 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
214 let last_write = (*self.write_ptr) as usize;
215 let free = self.buffer.len() - last_write;
216 let n = buf.len().min(free);
217 if n == 0 && !buf.is_empty() {
218 return Err(std::io::Error::new(
219 std::io::ErrorKind::WriteZero,
220 "buffer full",
221 ));
222 }
223 self.buffer[last_write..last_write + n].copy_from_slice(&buf[..n]);
224 *self.write_ptr = (last_write + n) as u32;
225 Ok(n)
226 }
227
228 fn flush(&mut self) -> std::io::Result<()> {
229 Ok(())
230 }
231}
232
233#[inline(always)]
234pub fn compute_ptr<T>(ptr: *mut T, linear_mem_space: &WasmLinearMem) -> *mut T {
235 let mem_start_ptr = linear_mem_space.start_ptr;
236 (mem_start_ptr as isize + ptr as isize) as _
237}
238
239struct BuilderInfo<'instance> {
240 buffer: &'instance mut [u8],
241 read_ptr: &'instance mut u32,
242 write_ptr: &'instance mut u32,
243}
244
245fn from_raw_builder<'a>(builder_ptr: *mut BufferBuilder, mem: WasmLinearMem) -> BuilderInfo<'a> {
246 unsafe {
247 #[cfg(feature = "trace")]
248 {
249 if !mem.start_ptr.is_null() && mem.size > 0 {
250 let contract_mem = std::slice::from_raw_parts(mem.start_ptr, mem.size as usize);
251 tracing::trace!(
252 "*mut BufferBuilder <- offset: {}; in mem: {:?}",
253 builder_ptr as usize,
254 &contract_mem[builder_ptr as usize
255 ..builder_ptr as usize + std::mem::size_of::<BufferBuilder>()]
256 );
257 }
258 }
262
263 let builder_ptr = compute_ptr(builder_ptr, &mem);
264 let buf_builder: &'static mut BufferBuilder = Box::leak(Box::from_raw(builder_ptr));
265 #[cfg(feature = "trace")]
266 {
267 tracing::trace!("buf builder from FFI: {buf_builder:?}");
268 }
269
270 let read_ptr = Box::leak(Box::from_raw(compute_ptr(
271 buf_builder.last_read as *mut u32,
272 &mem,
273 )));
274 let write_ptr = Box::leak(Box::from_raw(compute_ptr(
275 buf_builder.last_write as *mut u32,
276 &mem,
277 )));
278 let buffer_ptr = compute_ptr(buf_builder.start as *mut u8, &mem);
279 let buffer =
280 &mut *std::ptr::slice_from_raw_parts_mut(buffer_ptr, buf_builder.capacity as usize);
281 BuilderInfo {
282 buffer,
283 read_ptr,
284 write_ptr,
285 }
286 }
287}
288
289#[derive(Debug)]
290pub struct Buffer<'instance> {
292 buffer: &'instance mut [u8],
293 read_ptr: &'instance mut u32,
295 write_ptr: &'instance u32,
296 builder_ptr: *mut BufferBuilder,
297 mem: WasmLinearMem,
298}
299
300impl<'instance> Buffer<'instance> {
301 pub unsafe fn read<T: Sized>(&mut self) -> T {
306 let next_offset = *self.read_ptr as usize;
307 let bytes = &self.buffer[next_offset..next_offset + std::mem::size_of::<T>()];
308 let t = std::ptr::read(bytes.as_ptr() as *const T);
309 *self.read_ptr += std::mem::size_of::<T>() as u32;
310 t
311 }
312
313 pub fn read_bytes(&mut self, len: usize) -> &[u8] {
315 let next_offset = *self.read_ptr as usize;
316 *self.read_ptr += len as u32;
317 &self.buffer[next_offset..next_offset + len]
318 }
319
320 pub fn read_all(&mut self) -> &[u8] {
322 let next_offset = *self.read_ptr as usize;
323 *self.read_ptr += self.buffer.len() as u32;
324 &self.buffer[next_offset..=*self.write_ptr as usize]
325 }
326
327 #[doc(hidden)]
332 pub unsafe fn exclusive(self) -> BufferMut<'instance> {
333 let Buffer {
334 builder_ptr, mem, ..
335 } = self;
336 let BuilderInfo {
337 buffer,
338 read_ptr,
339 write_ptr,
340 } = from_raw_builder(builder_ptr, mem);
341 BufferMut {
342 buffer,
343 read_ptr,
344 write_ptr,
345 builder_ptr,
346 mem,
347 }
348 }
349}
350
351#[cfg(all(feature = "contract", target_family = "wasm"))]
362#[link(wasm_import_module = "freenet_contract_io")]
363extern "C" {
364 fn __frnt__fill_buffer(id: i64, buf_ptr: i64) -> u32;
365}
366
367#[cfg(all(feature = "contract", not(target_family = "wasm")))]
370unsafe extern "C" fn __frnt__fill_buffer(_id: i64, _buf_ptr: i64) -> u32 {
371 0
372}
373
374#[cfg(feature = "contract")]
380pub struct StreamingBuffer {
381 buf_ptr: *mut BufferBuilder,
382 total_remaining: usize,
384}
385
386#[cfg(feature = "contract")]
387impl StreamingBuffer {
388 pub fn total_remaining(&self) -> usize {
397 self.total_remaining
398 }
399
400 pub unsafe fn from_ptr(ptr: i64) -> Self {
401 let buf_ptr = ptr as *mut BufferBuilder;
402 let builder = &*buf_ptr;
403 let data_start = builder.start() as *const u8;
405 let total_len = u32::from_le_bytes([
406 *data_start,
407 *data_start.add(1),
408 *data_start.add(2),
409 *data_start.add(3),
410 ]) as usize;
411 let read_ptr = builder.last_read as *mut u32;
413 *read_ptr = 4;
414 StreamingBuffer {
415 buf_ptr,
416 total_remaining: total_len,
417 }
418 }
419}
420
421#[cfg(feature = "contract")]
422impl std::io::Read for StreamingBuffer {
423 fn read(&mut self, out: &mut [u8]) -> std::io::Result<usize> {
424 if self.total_remaining == 0 {
425 return Ok(0); }
427 let builder = unsafe { &*self.buf_ptr };
428 let mut available = builder.bytes_written().saturating_sub(builder.bytes_read());
429 if available == 0 {
430 let filled =
432 unsafe { __frnt__fill_buffer(crate::global::INSTANCE_ID, self.buf_ptr as i64) };
433 if filled == 0 {
434 return Ok(0); }
436 available = filled as usize;
437 }
438 let n = out.len().min(available).min(self.total_remaining);
439 let read_pos = builder.bytes_read();
441 unsafe {
442 let src = builder.start().add(read_pos);
443 std::ptr::copy_nonoverlapping(src, out.as_mut_ptr(), n);
444 *(builder.last_read as *mut u32) = (read_pos + n) as u32;
446 }
447 self.total_remaining -= n;
448 Ok(n)
449 }
450}
451
452#[doc(hidden)]
456#[allow(non_snake_case)]
457#[no_mangle]
458#[cfg(any(feature = "contract", test))]
459fn __frnt__initiate_buffer(capacity: u32) -> i64 {
460 let buf: Vec<u8> = Vec::with_capacity(capacity as usize);
461 let start = buf.as_ptr() as i64;
462
463 let last_read = Box::into_raw(Box::new(0u32));
464 let last_write = Box::into_raw(Box::new(0u32));
465 let buffer = Box::into_raw(Box::new(BufferBuilder {
466 start,
467 capacity,
468 last_read: last_read as _,
469 last_write: last_write as _,
470 }));
471 #[cfg(feature = "trace")]
472 {
473 tracing::trace!(
474 "new buffer ptr: {:p} -> {} as i64 w/ cap: {capacity}",
475 buf.as_ptr(),
476 start
477 );
478 tracing::trace!(
479 "last read ptr: {last_read:p} -> {} as i64",
480 last_read as i64
481 );
482 tracing::trace!(
483 "last write ptr: {last_write:p} -> {} as i64",
484 last_write as i64
485 );
486 tracing::trace!("buffer ptr: {buffer:p} -> {} as i64", buffer as i64);
487 }
488 std::mem::forget(buf);
489 buffer as i64
490}
491
492#[cfg(test)]
493mod test_io_write {
494 use super::*;
495 use std::io::Write;
496
497 unsafe fn host_buffer_mut(capacity: u32) -> BufferMut<'static> {
501 let builder_ptr = __frnt__initiate_buffer(capacity) as *mut BufferBuilder;
502 let linear_mem = WasmLinearMem {
503 start_ptr: std::ptr::null(),
504 size: 0,
505 };
506 BufferMut::from_ptr(builder_ptr, linear_mem)
507 }
508
509 fn io_write(buf: &mut BufferMut<'_>, data: &[u8]) -> std::io::Result<usize> {
511 Write::write(buf, data)
512 }
513
514 #[test]
515 fn write_trait_basic() {
516 let mut buf = unsafe { host_buffer_mut(32) };
517 let n = io_write(&mut buf, b"hello").unwrap();
518 assert_eq!(n, 5);
519 assert_eq!(buf.read_bytes(5), b"hello");
520 }
521
522 #[test]
523 fn write_trait_fills_exactly() {
524 let mut buf = unsafe { host_buffer_mut(4) };
525 let n = io_write(&mut buf, b"abcd").unwrap();
526 assert_eq!(n, 4);
527 assert_eq!(buf.read_bytes(4), b"abcd");
528 }
529
530 #[test]
531 fn write_trait_partial_when_near_full() {
532 let mut buf = unsafe { host_buffer_mut(4) };
533 io_write(&mut buf, b"ab").unwrap();
534 let n = io_write(&mut buf, b"xyz").unwrap();
536 assert_eq!(n, 2);
537 assert_eq!(buf.read_bytes(4), b"abxy");
538 }
539
540 #[test]
541 fn write_trait_error_when_full() {
542 let mut buf = unsafe { host_buffer_mut(2) };
543 io_write(&mut buf, b"ab").unwrap();
544 let err = io_write(&mut buf, b"c").unwrap_err();
545 assert_eq!(err.kind(), std::io::ErrorKind::WriteZero);
546 }
547
548 #[test]
549 fn write_trait_empty_slice_ok() {
550 let mut buf = unsafe { host_buffer_mut(4) };
551 let n = io_write(&mut buf, b"").unwrap();
552 assert_eq!(n, 0);
553 }
554
555 #[test]
556 fn write_all_trait() {
557 let mut buf = unsafe { host_buffer_mut(16) };
558 buf.write_all(b"hello world").unwrap();
559 assert_eq!(buf.read_bytes(11), b"hello world");
560 }
561
562 #[test]
563 fn write_all_insufficient_space() {
564 let mut buf = unsafe { host_buffer_mut(4) };
565 let err = buf.write_all(b"hello").unwrap_err();
566 assert_eq!(err.kind(), std::io::ErrorKind::WriteZero);
567 }
568
569 #[test]
570 fn bincode_serialize_into() {
571 let data: Vec<u32> = vec![1, 2, 3, 4, 5];
572 let size = bincode::serialized_size(&data).unwrap() as usize;
573 let mut buf = unsafe { host_buffer_mut(size as u32) };
574 bincode::serialize_into(&mut buf, &data).unwrap();
575 let result: Vec<u32> = bincode::deserialize(buf.read_bytes(size)).unwrap();
576 assert_eq!(result, data);
577 }
578}
579
580#[cfg(all(test, feature = "contract"))]
584mod test_streaming_read {
585 use super::*;
586 use std::io::Read;
587
588 unsafe fn host_streaming_buffer(data: &[u8]) -> StreamingBuffer {
590 let total_with_header = data.len() + 4;
591 let ptr = __frnt__initiate_buffer(total_with_header as u32);
592 let builder = &mut *(ptr as *mut BufferBuilder);
593
594 let header = (data.len() as u32).to_le_bytes();
596 let start = builder.start();
597 std::ptr::copy_nonoverlapping(header.as_ptr(), start, 4);
598 std::ptr::copy_nonoverlapping(data.as_ptr(), start.add(4), data.len());
599
600 *(builder.last_write as *mut u32) = total_with_header as u32;
602
603 StreamingBuffer::from_ptr(ptr)
604 }
605
606 #[test]
607 fn read_basic() {
608 let data = b"hello streaming";
609 let mut reader = unsafe { host_streaming_buffer(data) };
610 let mut out = vec![0u8; data.len()];
611 reader.read_exact(&mut out).unwrap();
612 assert_eq!(&out, data);
613 }
614
615 #[test]
616 fn read_to_end_collects_all() {
617 let data = b"the quick brown fox jumps over the lazy dog";
618 let mut reader = unsafe { host_streaming_buffer(data) };
619 let mut out = Vec::new();
620 reader.read_to_end(&mut out).unwrap();
621 assert_eq!(&out, data);
622 }
623
624 #[test]
625 fn read_empty_payload() {
626 let mut reader = unsafe { host_streaming_buffer(b"") };
627 let mut out = Vec::new();
628 let n = reader.read_to_end(&mut out).unwrap();
629 assert_eq!(n, 0);
630 assert!(out.is_empty());
631 }
632
633 #[test]
634 fn read_in_small_chunks() {
635 let data = b"abcdefghij";
636 let mut reader = unsafe { host_streaming_buffer(data) };
637 let mut result = Vec::new();
638 let mut buf = [0u8; 3];
639 loop {
640 let n = reader.read(&mut buf).unwrap();
641 if n == 0 {
642 break;
643 }
644 result.extend_from_slice(&buf[..n]);
645 }
646 assert_eq!(&result, data);
647 }
648
649 #[test]
650 fn total_remaining_decreases() {
651 let data = b"1234567890";
652 let mut reader = unsafe { host_streaming_buffer(data) };
653 assert_eq!(reader.total_remaining(), 10);
654 let mut buf = [0u8; 4];
655 reader.read(&mut buf).unwrap();
656 assert_eq!(reader.total_remaining(), 6);
657 }
658
659 #[test]
660 fn eof_after_all_read() {
661 let data = b"abc";
662 let mut reader = unsafe { host_streaming_buffer(data) };
663 let mut out = vec![0u8; 3];
664 reader.read_exact(&mut out).unwrap();
665 assert_eq!(reader.total_remaining(), 0);
666 let n = reader.read(&mut out).unwrap();
667 assert_eq!(n, 0);
668 }
669
670 #[test]
671 fn bincode_roundtrip_through_streaming() {
672 let original: Vec<u32> = vec![42, 99, 1337, 0, u32::MAX];
673 let serialized = bincode::serialize(&original).unwrap();
674 let mut reader = unsafe { host_streaming_buffer(&serialized) };
675 let mut bytes = Vec::with_capacity(reader.total_remaining());
676 reader.read_to_end(&mut bytes).unwrap();
677 let result: Vec<u32> = bincode::deserialize(&bytes).unwrap();
678 assert_eq!(result, original);
679 }
680}
681
682#[cfg(all(test, any(unix, windows), feature = "wasmer-tests"))]
683mod test {
684 use super::*;
685 use wasmer::{
686 imports, wat2wasm, AsStoreMut, Cranelift, Function, Instance, Module, Store, TypedFunction,
687 };
688
689 const TEST_MODULE: &str = r#"
690 (module
691 (func $initiate_buffer (import "freenet" "initiate_buffer") (param i32) (result i64))
692 (memory $locutus_mem (export "memory") 20)
693 (export "initiate_buffer" (func $initiate_buffer))
694 )"#;
695
696 fn build_test_mod() -> Result<(Store, Instance), Box<dyn std::error::Error>> {
697 let wasm_bytes = wat2wasm(TEST_MODULE.as_bytes())?;
698 let mut store = Store::new(Cranelift::new());
699 let module = Module::new(&store, wasm_bytes)?;
700
701 let init_buf_fn = Function::new_typed(&mut store, __frnt__initiate_buffer);
702 let imports = imports! {
703 "freenet" => { "initiate_buffer" => init_buf_fn }
704 };
705 let instance = Instance::new(&mut store, &module, &imports).unwrap();
706 Ok((store, instance))
707 }
708
709 fn init_buf(store: &mut impl AsStoreMut, instance: &Instance, size: u32) -> *mut BufferBuilder {
710 let initiate_buffer: TypedFunction<u32, i64> = instance
711 .exports
712 .get_typed_function(&store, "initiate_buffer")
713 .unwrap();
714 initiate_buffer.call(store, size).unwrap() as *mut BufferBuilder
715 }
716
717 #[test]
718 #[ignore]
719 fn read_and_write() -> Result<(), Box<dyn std::error::Error>> {
720 let (mut store, instance) = build_test_mod()?;
721 let mem = instance.exports.get_memory("memory")?.view(&store);
722 let linear_mem = WasmLinearMem {
723 start_ptr: mem.data_ptr() as *const _,
724 size: mem.data_size(),
725 };
726
727 let mut writer =
728 unsafe { BufferMut::from_ptr(init_buf(&mut store, &instance, 10), linear_mem) };
729 writer.write([1u8, 2])?;
730 let mut reader = writer.shared();
731 let r: [u8; 2] = unsafe { reader.read() };
732 assert_eq!(r, [1, 2]);
733
734 let mut writer = unsafe { reader.exclusive() };
735 writer.write([3u8, 4])?;
736 let mut reader = writer.shared();
737 let r: [u8; 2] = unsafe { reader.read() };
738 assert_eq!(r, [3, 4]);
739 Ok(())
740 }
741
742 #[test]
743 #[ignore]
744 fn read_and_write_bytes() -> Result<(), Box<dyn std::error::Error>> {
745 let (mut store, instance) = build_test_mod()?;
746 let mem = instance.exports.get_memory("memory")?.view(&store);
747 let linear_mem = WasmLinearMem {
748 start_ptr: mem.data_ptr() as *const _,
749 size: mem.data_size(),
750 };
751
752 let mut writer =
753 unsafe { BufferMut::from_ptr(init_buf(&mut store, &instance, 10), linear_mem) };
754 writer.write([1u8, 2])?;
755 let mut reader = writer.shared();
756 let r = reader.read_bytes(2);
757 assert_eq!(r, &[1, 2]);
758
759 let mut writer = unsafe { reader.exclusive() };
760 writer.write([3u8, 4])?;
761 let mut reader = writer.shared();
762 let r = reader.read_bytes(2);
763 assert_eq!(r, &[3, 4]);
764 Ok(())
765 }
766
767 #[test]
768 #[ignore]
769 fn update() -> Result<(), Box<dyn std::error::Error>> {
770 let (mut store, instance) = build_test_mod()?;
771 let mem = instance.exports.get_memory("memory")?.view(&store);
772 let linear_mem = WasmLinearMem {
773 start_ptr: mem.data_ptr() as *const _,
774 size: mem.data_size(),
775 };
776
777 let ptr = {
778 let mut writer =
779 unsafe { BufferMut::from_ptr(init_buf(&mut store, &instance, 10), linear_mem) };
780 writer.write([1u8, 2])?;
781 writer.ptr()
782 };
783
784 let writer = unsafe {
785 let builder = &mut *ptr;
786 builder.update_buffer(vec![3, 5, 7]);
787 BufferMut::from_ptr(ptr, linear_mem)
788 };
789 let mut reader = writer.shared();
790 assert_eq!(reader.read_all(), &[3, 5, 7]);
791
792 Ok(())
793 }
794}