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//
359// Only linked as a WASM import on wasm32 targets. On native targets (tests,
360// host-side builds with "contract" feature), a stub returning 0 (EOF) is used.
361#[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// Stub for non-WASM builds (native tests, host-side compilation).
368// Returns 0 (EOF) since there is no WASM host to refill from.
369#[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/// Contract-side streaming reader for refill-pattern buffers.
375///
376/// The host writes a `[total_len: u32]` header followed by as much data as
377/// fits into a small buffer. The contract reads through this wrapper; when
378/// the buffer is exhausted, [`Read::read`] calls the host to refill it.
379#[cfg(feature = "contract")]
380pub struct StreamingBuffer {
381    buf_ptr: *mut BufferBuilder,
382    /// Total bytes remaining to be read (initialized from the header).
383    total_remaining: usize,
384}
385
386#[cfg(feature = "contract")]
387impl StreamingBuffer {
388    /// Create a streaming reader from a buffer pointer.
389    ///
390    /// Reads the `[total_len: u32]` header and prepares for streaming.
391    ///
392    /// # Safety
393    /// `ptr` must point to a valid `BufferBuilder` in WASM linear memory
394    /// whose first 4 bytes of data contain the total payload length as LE u32.
395    /// Returns the total number of payload bytes remaining to be read.
396    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        // Read the total_len header (first 4 bytes)
404        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        // Advance the read pointer past the header
412        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); // EOF — all expected data has been read
426        }
427        let builder = unsafe { &*self.buf_ptr };
428        let mut available = builder.bytes_written().saturating_sub(builder.bytes_read());
429        if available == 0 {
430            // Buffer exhausted — ask host to refill
431            let filled =
432                unsafe { __frnt__fill_buffer(crate::global::INSTANCE_ID, self.buf_ptr as i64) };
433            if filled == 0 {
434                return Ok(0); // Host says EOF
435            }
436            available = filled as usize;
437        }
438        let n = out.len().min(available).min(self.total_remaining);
439        // Copy from buffer at current read position
440        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            // Advance the read pointer
445            *(builder.last_read as *mut u32) = (read_pos + n) as u32;
446        }
447        self.total_remaining -= n;
448        Ok(n)
449    }
450}
451
452/// Returns the pointer to a new BufferBuilder.
453///
454/// This buffer leaks it's own memory and will only be freed by the runtime when a contract instance is dropped.
455#[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    /// Create a BufferMut backed by host memory (no WASM runtime needed).
498    /// Uses `__frnt__initiate_buffer` which allocates in host memory during tests,
499    /// and a null-base WasmLinearMem so compute_ptr is a no-op on absolute pointers.
500    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    /// Call std::io::Write::write (not BufferMut::write which has different signature)
510    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        // Only 2 bytes free, writing 3 should write 2
535        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/// Tests for StreamingBuffer (contract-side reader).
581/// These test the non-refill path only — when all data fits in the initial buffer.
582/// The refill path requires a WASM runtime and is tested via integration tests.
583#[cfg(all(test, feature = "contract"))]
584mod test_streaming_read {
585    use super::*;
586    use std::io::Read;
587
588    /// Create a host-memory buffer pre-loaded with `[total_len: u32 LE][data...]`.
589    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        // Write total_len header (LE u32)
595        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        // Set write pointer to total bytes written
601        *(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}