Skip to main content

freenet_stdlib/memory/
buf.rs

1//! Memory buffers to interact with the WASM contracts.
2
3use 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    /// Return the buffer capacity.
17    pub fn capacity(&self) -> usize {
18        self.capacity as _
19    }
20
21    /// Returns the number of bytes written to the buffer.
22    #[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    /// Returns the number of bytes read from the buffer.
36    #[cfg(feature = "contract")]
37    pub fn bytes_read(&self) -> usize {
38        unsafe { *(self.last_read as *mut u32) as usize }
39    }
40
41    /// Resets the read and write pointers to 0 (contract-side).
42    #[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    /// Returns the first byte of buffer.
51    pub fn start(&self) -> *mut u8 {
52        self.start as _
53    }
54
55    /// Returns the raw pointer to the read position tracker (for host-side reset).
56    pub fn last_read_ptr(&self) -> *mut u32 {
57        self.last_read as *mut u32
58    }
59
60    /// Returns the raw pointer to the write position tracker (for host-side reset).
61    pub fn last_write_ptr(&self) -> *mut u32 {
62        self.last_write as *mut u32
63    }
64
65    /// # Safety
66    /// Requires that there are no living references to the current
67    /// underlying buffer or will trigger UB
68    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        // drop previous buffer
73        let prev = Vec::from_raw_parts(self.start as *mut u8, *write_ptr as usize, self.capacity());
74        std::mem::drop(prev);
75
76        // write the new buffer information
77        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 _; // []
82        std::mem::forget(data);
83    }
84
85    /// Returns a wrapped raw pointer to the buffer builder.
86    pub fn to_ptr(self) -> *mut BufferBuilder {
87        Box::into_raw(Box::new(self))
88    }
89}
90
91/// Type of buffer errors.
92#[derive(thiserror::Error, Debug)]
93pub enum Error {
94    /// Insufficient memory while trying to write to the buffer.
95    #[error("insufficient memory, needed {req} bytes but had {free} bytes")]
96    InsufficientMemory {
97        /// Required memory available
98        req: usize,
99        /// Available memory.
100        free: usize,
101    },
102}
103
104/// A live mutable buffer in the WASM linear memory.
105#[derive(Debug)]
106pub struct BufferMut<'instance> {
107    buffer: &'instance mut [u8],
108    /// stores the last read in the buffer
109    read_ptr: &'instance u32,
110    /// stores the last write in the buffer
111    write_ptr: &'instance mut u32,
112    /// A pointer to the underlying builder
113    builder_ptr: *mut BufferBuilder,
114    /// Linear memory pointer and size in bytes
115    mem: WasmLinearMem,
116}
117
118impl<'instance> BufferMut<'instance> {
119    /// Tries to write data into the buffer, after any unread bytes.
120    ///
121    /// Will return an error if there is insufficient space.
122    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    /// Read bytes specified number of bytes from the buffer.
150    ///
151    /// Always reads from the beginning.
152    pub fn read_bytes(&self, len: usize) -> &[u8] {
153        let next_offset = *self.read_ptr as usize;
154        // don't update the read ptr
155        &self.buffer[next_offset..next_offset + len]
156    }
157
158    /// Give ownership of the buffer back to the guest.
159    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    /// Return the buffer capacity.
179    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    /// # Safety
187    /// The pointer passed come from a previous call to `initiate_buffer` exported function from the contract.
188    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    /// A pointer to the linear memory address.
207    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            // use std::{fs::File, io::Write};
259            // let mut f = File::create(std::env::temp_dir().join("dump.mem")).unwrap();
260            // f.write_all(contract_mem).unwrap();
261        }
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)]
290/// A live buffer in the WASM linear memory.
291pub struct Buffer<'instance> {
292    buffer: &'instance mut [u8],
293    /// stores the last read in the buffer
294    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    /// # Safety
302    /// In order for this to be a safe T must be properly aligned and cannot re-use the buffer
303    /// trying to read the same memory region again (that would create more than one copy to
304    /// the same underlying data and break aliasing rules).
305    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    /// Read the specified number of bytes from the buffer.
314    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    /// Reads all the bytes from the buffer.
321    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    /// Give ownership of the buffer back to the guest.
328    ///
329    /// # Safety
330    /// Must guarantee that there are not underlying alive shared references.
331    #[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// ---------------------------------------------------------------------------
352// Streaming refill buffer (contract-side only)
353// ---------------------------------------------------------------------------
354
355// Host import for refilling a buffer. Called by the contract when it has
356// exhausted the current buffer contents and needs more data from the host.
357// Returns the number of bytes the host wrote into the buffer, or 0 for EOF.
358#[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// Test stub: returns 0 (EOF) since tests don't have a WASM host.
365// This means tests can only exercise the non-refill path.
366#[cfg(all(feature = "contract", test))]
367unsafe extern "C" fn __frnt__fill_buffer(_id: i64, _buf_ptr: i64) -> u32 {
368    0
369}
370
371/// Contract-side streaming reader for refill-pattern buffers.
372///
373/// The host writes a `[total_len: u32]` header followed by as much data as
374/// fits into a small buffer. The contract reads through this wrapper; when
375/// the buffer is exhausted, [`Read::read`] calls the host to refill it.
376#[cfg(feature = "contract")]
377pub struct StreamingBuffer {
378    buf_ptr: *mut BufferBuilder,
379    /// Total bytes remaining to be read (initialized from the header).
380    total_remaining: usize,
381}
382
383#[cfg(feature = "contract")]
384impl StreamingBuffer {
385    /// Create a streaming reader from a buffer pointer.
386    ///
387    /// Reads the `[total_len: u32]` header and prepares for streaming.
388    ///
389    /// # Safety
390    /// `ptr` must point to a valid `BufferBuilder` in WASM linear memory
391    /// whose first 4 bytes of data contain the total payload length as LE u32.
392    /// Returns the total number of payload bytes remaining to be read.
393    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        // Read the total_len header (first 4 bytes)
401        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        // Advance the read pointer past the header
409        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); // EOF — all expected data has been read
423        }
424        let builder = unsafe { &*self.buf_ptr };
425        let mut available = builder.bytes_written().saturating_sub(builder.bytes_read());
426        if available == 0 {
427            // Buffer exhausted — ask host to refill
428            let filled =
429                unsafe { __frnt__fill_buffer(crate::global::INSTANCE_ID, self.buf_ptr as i64) };
430            if filled == 0 {
431                return Ok(0); // Host says EOF
432            }
433            available = filled as usize;
434        }
435        let n = out.len().min(available).min(self.total_remaining);
436        // Copy from buffer at current read position
437        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            // Advance the read pointer
442            *(builder.last_read as *mut u32) = (read_pos + n) as u32;
443        }
444        self.total_remaining -= n;
445        Ok(n)
446    }
447}
448
449/// Returns the pointer to a new BufferBuilder.
450///
451/// This buffer leaks it's own memory and will only be freed by the runtime when a contract instance is dropped.
452#[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    /// Create a BufferMut backed by host memory (no WASM runtime needed).
495    /// Uses `__frnt__initiate_buffer` which allocates in host memory during tests,
496    /// and a null-base WasmLinearMem so compute_ptr is a no-op on absolute pointers.
497    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    /// Call std::io::Write::write (not BufferMut::write which has different signature)
507    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        // Only 2 bytes free, writing 3 should write 2
532        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/// Tests for StreamingBuffer (contract-side reader).
578/// These test the non-refill path only — when all data fits in the initial buffer.
579/// The refill path requires a WASM runtime and is tested via integration tests.
580#[cfg(all(test, feature = "contract"))]
581mod test_streaming_read {
582    use super::*;
583    use std::io::Read;
584
585    /// Create a host-memory buffer pre-loaded with `[total_len: u32 LE][data...]`.
586    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        // Write total_len header (LE u32)
592        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        // Set write pointer to total bytes written
598        *(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}