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