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