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", not(test)))]
359#[link(wasm_import_module = "freenet_contract_io")]
360extern "C" {
361 fn __frnt__fill_buffer(id: i64, buf_ptr: i64) -> u32;
362}
363
364#[cfg(all(feature = "contract", test))]
367unsafe extern "C" fn __frnt__fill_buffer(_id: i64, _buf_ptr: i64) -> u32 {
368 0
369}
370
371#[cfg(feature = "contract")]
377pub struct StreamingBuffer {
378 buf_ptr: *mut BufferBuilder,
379 total_remaining: usize,
381}
382
383#[cfg(feature = "contract")]
384impl StreamingBuffer {
385 pub fn total_remaining(&self) -> usize {
394 self.total_remaining
395 }
396
397 pub unsafe fn from_ptr(ptr: i64) -> Self {
398 let buf_ptr = ptr as *mut BufferBuilder;
399 let builder = &*buf_ptr;
400 let data_start = builder.start() as *const u8;
402 let total_len = u32::from_le_bytes([
403 *data_start,
404 *data_start.add(1),
405 *data_start.add(2),
406 *data_start.add(3),
407 ]) as usize;
408 let read_ptr = builder.last_read as *mut u32;
410 *read_ptr = 4;
411 StreamingBuffer {
412 buf_ptr,
413 total_remaining: total_len,
414 }
415 }
416}
417
418#[cfg(feature = "contract")]
419impl std::io::Read for StreamingBuffer {
420 fn read(&mut self, out: &mut [u8]) -> std::io::Result<usize> {
421 if self.total_remaining == 0 {
422 return Ok(0); }
424 let builder = unsafe { &*self.buf_ptr };
425 let mut available = builder.bytes_written().saturating_sub(builder.bytes_read());
426 if available == 0 {
427 let filled =
429 unsafe { __frnt__fill_buffer(crate::global::INSTANCE_ID, self.buf_ptr as i64) };
430 if filled == 0 {
431 return Ok(0); }
433 available = filled as usize;
434 }
435 let n = out.len().min(available).min(self.total_remaining);
436 let read_pos = builder.bytes_read();
438 unsafe {
439 let src = builder.start().add(read_pos);
440 std::ptr::copy_nonoverlapping(src, out.as_mut_ptr(), n);
441 *(builder.last_read as *mut u32) = (read_pos + n) as u32;
443 }
444 self.total_remaining -= n;
445 Ok(n)
446 }
447}
448
449#[doc(hidden)]
453#[allow(non_snake_case)]
454#[no_mangle]
455#[cfg(any(feature = "contract", test))]
456fn __frnt__initiate_buffer(capacity: u32) -> i64 {
457 let buf: Vec<u8> = Vec::with_capacity(capacity as usize);
458 let start = buf.as_ptr() as i64;
459
460 let last_read = Box::into_raw(Box::new(0u32));
461 let last_write = Box::into_raw(Box::new(0u32));
462 let buffer = Box::into_raw(Box::new(BufferBuilder {
463 start,
464 capacity,
465 last_read: last_read as _,
466 last_write: last_write as _,
467 }));
468 #[cfg(feature = "trace")]
469 {
470 tracing::trace!(
471 "new buffer ptr: {:p} -> {} as i64 w/ cap: {capacity}",
472 buf.as_ptr(),
473 start
474 );
475 tracing::trace!(
476 "last read ptr: {last_read:p} -> {} as i64",
477 last_read as i64
478 );
479 tracing::trace!(
480 "last write ptr: {last_write:p} -> {} as i64",
481 last_write as i64
482 );
483 tracing::trace!("buffer ptr: {buffer:p} -> {} as i64", buffer as i64);
484 }
485 std::mem::forget(buf);
486 buffer as i64
487}
488
489#[cfg(test)]
490mod test_io_write {
491 use super::*;
492 use std::io::Write;
493
494 unsafe fn host_buffer_mut(capacity: u32) -> BufferMut<'static> {
498 let builder_ptr = __frnt__initiate_buffer(capacity) as *mut BufferBuilder;
499 let linear_mem = WasmLinearMem {
500 start_ptr: std::ptr::null(),
501 size: 0,
502 };
503 BufferMut::from_ptr(builder_ptr, linear_mem)
504 }
505
506 fn io_write(buf: &mut BufferMut<'_>, data: &[u8]) -> std::io::Result<usize> {
508 Write::write(buf, data)
509 }
510
511 #[test]
512 fn write_trait_basic() {
513 let mut buf = unsafe { host_buffer_mut(32) };
514 let n = io_write(&mut buf, b"hello").unwrap();
515 assert_eq!(n, 5);
516 assert_eq!(buf.read_bytes(5), b"hello");
517 }
518
519 #[test]
520 fn write_trait_fills_exactly() {
521 let mut buf = unsafe { host_buffer_mut(4) };
522 let n = io_write(&mut buf, b"abcd").unwrap();
523 assert_eq!(n, 4);
524 assert_eq!(buf.read_bytes(4), b"abcd");
525 }
526
527 #[test]
528 fn write_trait_partial_when_near_full() {
529 let mut buf = unsafe { host_buffer_mut(4) };
530 io_write(&mut buf, b"ab").unwrap();
531 let n = io_write(&mut buf, b"xyz").unwrap();
533 assert_eq!(n, 2);
534 assert_eq!(buf.read_bytes(4), b"abxy");
535 }
536
537 #[test]
538 fn write_trait_error_when_full() {
539 let mut buf = unsafe { host_buffer_mut(2) };
540 io_write(&mut buf, b"ab").unwrap();
541 let err = io_write(&mut buf, b"c").unwrap_err();
542 assert_eq!(err.kind(), std::io::ErrorKind::WriteZero);
543 }
544
545 #[test]
546 fn write_trait_empty_slice_ok() {
547 let mut buf = unsafe { host_buffer_mut(4) };
548 let n = io_write(&mut buf, b"").unwrap();
549 assert_eq!(n, 0);
550 }
551
552 #[test]
553 fn write_all_trait() {
554 let mut buf = unsafe { host_buffer_mut(16) };
555 buf.write_all(b"hello world").unwrap();
556 assert_eq!(buf.read_bytes(11), b"hello world");
557 }
558
559 #[test]
560 fn write_all_insufficient_space() {
561 let mut buf = unsafe { host_buffer_mut(4) };
562 let err = buf.write_all(b"hello").unwrap_err();
563 assert_eq!(err.kind(), std::io::ErrorKind::WriteZero);
564 }
565
566 #[test]
567 fn bincode_serialize_into() {
568 let data: Vec<u32> = vec![1, 2, 3, 4, 5];
569 let size = bincode::serialized_size(&data).unwrap() as usize;
570 let mut buf = unsafe { host_buffer_mut(size as u32) };
571 bincode::serialize_into(&mut buf, &data).unwrap();
572 let result: Vec<u32> = bincode::deserialize(buf.read_bytes(size)).unwrap();
573 assert_eq!(result, data);
574 }
575}
576
577#[cfg(all(test, feature = "contract"))]
581mod test_streaming_read {
582 use super::*;
583 use std::io::Read;
584
585 unsafe fn host_streaming_buffer(data: &[u8]) -> StreamingBuffer {
587 let total_with_header = data.len() + 4;
588 let ptr = __frnt__initiate_buffer(total_with_header as u32);
589 let builder = &mut *(ptr as *mut BufferBuilder);
590
591 let header = (data.len() as u32).to_le_bytes();
593 let start = builder.start();
594 std::ptr::copy_nonoverlapping(header.as_ptr(), start, 4);
595 std::ptr::copy_nonoverlapping(data.as_ptr(), start.add(4), data.len());
596
597 *(builder.last_write as *mut u32) = total_with_header as u32;
599
600 StreamingBuffer::from_ptr(ptr)
601 }
602
603 #[test]
604 fn read_basic() {
605 let data = b"hello streaming";
606 let mut reader = unsafe { host_streaming_buffer(data) };
607 let mut out = vec![0u8; data.len()];
608 reader.read_exact(&mut out).unwrap();
609 assert_eq!(&out, data);
610 }
611
612 #[test]
613 fn read_to_end_collects_all() {
614 let data = b"the quick brown fox jumps over the lazy dog";
615 let mut reader = unsafe { host_streaming_buffer(data) };
616 let mut out = Vec::new();
617 reader.read_to_end(&mut out).unwrap();
618 assert_eq!(&out, data);
619 }
620
621 #[test]
622 fn read_empty_payload() {
623 let mut reader = unsafe { host_streaming_buffer(b"") };
624 let mut out = Vec::new();
625 let n = reader.read_to_end(&mut out).unwrap();
626 assert_eq!(n, 0);
627 assert!(out.is_empty());
628 }
629
630 #[test]
631 fn read_in_small_chunks() {
632 let data = b"abcdefghij";
633 let mut reader = unsafe { host_streaming_buffer(data) };
634 let mut result = Vec::new();
635 let mut buf = [0u8; 3];
636 loop {
637 let n = reader.read(&mut buf).unwrap();
638 if n == 0 {
639 break;
640 }
641 result.extend_from_slice(&buf[..n]);
642 }
643 assert_eq!(&result, data);
644 }
645
646 #[test]
647 fn total_remaining_decreases() {
648 let data = b"1234567890";
649 let mut reader = unsafe { host_streaming_buffer(data) };
650 assert_eq!(reader.total_remaining(), 10);
651 let mut buf = [0u8; 4];
652 reader.read(&mut buf).unwrap();
653 assert_eq!(reader.total_remaining(), 6);
654 }
655
656 #[test]
657 fn eof_after_all_read() {
658 let data = b"abc";
659 let mut reader = unsafe { host_streaming_buffer(data) };
660 let mut out = vec![0u8; 3];
661 reader.read_exact(&mut out).unwrap();
662 assert_eq!(reader.total_remaining(), 0);
663 let n = reader.read(&mut out).unwrap();
664 assert_eq!(n, 0);
665 }
666
667 #[test]
668 fn bincode_roundtrip_through_streaming() {
669 let original: Vec<u32> = vec![42, 99, 1337, 0, u32::MAX];
670 let serialized = bincode::serialize(&original).unwrap();
671 let mut reader = unsafe { host_streaming_buffer(&serialized) };
672 let mut bytes = Vec::with_capacity(reader.total_remaining());
673 reader.read_to_end(&mut bytes).unwrap();
674 let result: Vec<u32> = bincode::deserialize(&bytes).unwrap();
675 assert_eq!(result, original);
676 }
677}
678
679#[cfg(all(test, any(unix, windows), feature = "wasmer-tests"))]
680mod test {
681 use super::*;
682 use wasmer::{
683 imports, wat2wasm, AsStoreMut, Cranelift, Function, Instance, Module, Store, TypedFunction,
684 };
685
686 const TEST_MODULE: &str = r#"
687 (module
688 (func $initiate_buffer (import "freenet" "initiate_buffer") (param i32) (result i64))
689 (memory $locutus_mem (export "memory") 20)
690 (export "initiate_buffer" (func $initiate_buffer))
691 )"#;
692
693 fn build_test_mod() -> Result<(Store, Instance), Box<dyn std::error::Error>> {
694 let wasm_bytes = wat2wasm(TEST_MODULE.as_bytes())?;
695 let mut store = Store::new(Cranelift::new());
696 let module = Module::new(&store, wasm_bytes)?;
697
698 let init_buf_fn = Function::new_typed(&mut store, __frnt__initiate_buffer);
699 let imports = imports! {
700 "freenet" => { "initiate_buffer" => init_buf_fn }
701 };
702 let instance = Instance::new(&mut store, &module, &imports).unwrap();
703 Ok((store, instance))
704 }
705
706 fn init_buf(store: &mut impl AsStoreMut, instance: &Instance, size: u32) -> *mut BufferBuilder {
707 let initiate_buffer: TypedFunction<u32, i64> = instance
708 .exports
709 .get_typed_function(&store, "initiate_buffer")
710 .unwrap();
711 initiate_buffer.call(store, size).unwrap() as *mut BufferBuilder
712 }
713
714 #[test]
715 #[ignore]
716 fn read_and_write() -> Result<(), Box<dyn std::error::Error>> {
717 let (mut store, instance) = build_test_mod()?;
718 let mem = instance.exports.get_memory("memory")?.view(&store);
719 let linear_mem = WasmLinearMem {
720 start_ptr: mem.data_ptr() as *const _,
721 size: mem.data_size(),
722 };
723
724 let mut writer =
725 unsafe { BufferMut::from_ptr(init_buf(&mut store, &instance, 10), linear_mem) };
726 writer.write([1u8, 2])?;
727 let mut reader = writer.shared();
728 let r: [u8; 2] = unsafe { reader.read() };
729 assert_eq!(r, [1, 2]);
730
731 let mut writer = unsafe { reader.exclusive() };
732 writer.write([3u8, 4])?;
733 let mut reader = writer.shared();
734 let r: [u8; 2] = unsafe { reader.read() };
735 assert_eq!(r, [3, 4]);
736 Ok(())
737 }
738
739 #[test]
740 #[ignore]
741 fn read_and_write_bytes() -> Result<(), Box<dyn std::error::Error>> {
742 let (mut store, instance) = build_test_mod()?;
743 let mem = instance.exports.get_memory("memory")?.view(&store);
744 let linear_mem = WasmLinearMem {
745 start_ptr: mem.data_ptr() as *const _,
746 size: mem.data_size(),
747 };
748
749 let mut writer =
750 unsafe { BufferMut::from_ptr(init_buf(&mut store, &instance, 10), linear_mem) };
751 writer.write([1u8, 2])?;
752 let mut reader = writer.shared();
753 let r = reader.read_bytes(2);
754 assert_eq!(r, &[1, 2]);
755
756 let mut writer = unsafe { reader.exclusive() };
757 writer.write([3u8, 4])?;
758 let mut reader = writer.shared();
759 let r = reader.read_bytes(2);
760 assert_eq!(r, &[3, 4]);
761 Ok(())
762 }
763
764 #[test]
765 #[ignore]
766 fn update() -> Result<(), Box<dyn std::error::Error>> {
767 let (mut store, instance) = build_test_mod()?;
768 let mem = instance.exports.get_memory("memory")?.view(&store);
769 let linear_mem = WasmLinearMem {
770 start_ptr: mem.data_ptr() as *const _,
771 size: mem.data_size(),
772 };
773
774 let ptr = {
775 let mut writer =
776 unsafe { BufferMut::from_ptr(init_buf(&mut store, &instance, 10), linear_mem) };
777 writer.write([1u8, 2])?;
778 writer.ptr()
779 };
780
781 let writer = unsafe {
782 let builder = &mut *ptr;
783 builder.update_buffer(vec![3, 5, 7]);
784 BufferMut::from_ptr(ptr, linear_mem)
785 };
786 let mut reader = writer.shared();
787 assert_eq!(reader.read_all(), &[3, 5, 7]);
788
789 Ok(())
790 }
791}