as_ffi_bindings/
any_ptr.rs

1use super::{Env, Memory, Read, Write};
2use crate::{BufferPtr, StringPtr};
3use std::convert::{TryFrom, TryInto};
4use wasmer::{Array, FromToNativeWasmType, Value, WasmPtr};
5
6use crate::tools::export_asr;
7
8// todo: should I implement Any ?
9#[derive(Clone, Copy)]
10pub struct AnyPtr(WasmPtr<u8, Array>);
11pub struct AnyPtrExported {
12    pub id: u32,
13    pub content: Vec<u8>,
14}
15
16impl AnyPtrExported {
17    pub fn serialize(self) -> Vec<u8> {
18        let ret = self.id.to_be_bytes().to_vec();
19        [ret, self.content].concat()
20    }
21
22    pub fn deserialize(b: &[u8]) -> anyhow::Result<Self> {
23        if b.len() < 4 {
24            anyhow::bail!("any pointer to small")
25        }
26        Ok(Self {
27            id: u32::from_be_bytes(b[..4].try_into()?),
28            content: b[4..].to_vec(),
29        })
30    }
31}
32
33pub enum Type {
34    String(Box<StringPtr>),
35    Buffer(Box<BufferPtr>),
36    Any(Box<AnyPtr>),
37}
38
39impl Type {
40    pub fn offset(&self) -> u32 {
41        match self {
42            Type::String(ptr) => ptr.offset(),
43            Type::Buffer(ptr) => ptr.offset(),
44            Type::Any(ptr) => ptr.offset(),
45        }
46    }
47}
48
49impl AnyPtr {
50    pub fn new(offset: u32) -> Self {
51        Self(WasmPtr::new(offset))
52    }
53    pub fn to_type(self, memory: &Memory) -> anyhow::Result<Type> {
54        let t = ptr_id(self.offset(), memory)?;
55        if t == 0 {
56            Ok(Type::Buffer(Box::new(BufferPtr::new(self.offset()))))
57        } else if t == 1 {
58            Ok(Type::String(Box::new(StringPtr::new(self.offset()))))
59        } else {
60            Ok(Type::Any(Box::new(self)))
61        }
62    }
63    /// Get ptr stored offset
64    pub fn offset(&self) -> u32 {
65        self.0.offset()
66    }
67    pub fn export(&self, memory: &Memory) -> anyhow::Result<AnyPtrExported> {
68        let content = self.read(memory)?;
69        let id = ptr_id(self.offset(), memory)?;
70        Ok(AnyPtrExported { content, id })
71    }
72    /// Create a new pointer with an allocation and write the pointer that
73    /// has been writen. Return a pointer type.
74    pub fn import(ptr_exported: &AnyPtrExported, env: &Env) -> anyhow::Result<Type> {
75        if ptr_exported.id == 0 {
76            Ok(Type::Buffer(BufferPtr::alloc(&ptr_exported.content, env)?))
77        } else if ptr_exported.id == 1 {
78            let utf16_vec = unsafe {
79                let len = ptr_exported.content.len();
80                if len % 2 != 0 {
81                    anyhow::bail!("Cannot cast u8 slice into u16")
82                }
83                let c = ptr_exported.content.as_ptr().cast::<u16>();
84                let a = std::slice::from_raw_parts(c, len / 2);
85                a.to_vec()
86            };
87            Ok(Type::String(StringPtr::alloc(
88                &String::from_utf16_lossy(&utf16_vec),
89                env,
90            )?))
91        } else {
92            // todo write type anyway
93            let ptr = AnyPtr::alloc(&ptr_exported.content, env)?;
94            set_id(ptr.offset(), ptr_exported.id, env)?;
95            Ok(Type::Any(ptr))
96        }
97    }
98}
99
100unsafe impl FromToNativeWasmType for AnyPtr {
101    type Native = i32;
102    fn to_native(self) -> Self::Native {
103        self.offset() as i32
104    }
105    fn from_native(n: Self::Native) -> Self {
106        Self::new(n as u32)
107    }
108}
109
110impl Read<Vec<u8>> for AnyPtr {
111    fn read(&self, memory: &Memory) -> anyhow::Result<Vec<u8>> {
112        let size = self.size(memory)?;
113        if let Some(buf) = self.0.deref(memory, 0, size * 2) {
114            Ok(buf.iter().map(|b| b.get()).collect())
115        } else {
116            anyhow::bail!("Wrong offset: can't read any object")
117        }
118    }
119
120    fn size(&self, memory: &Memory) -> anyhow::Result<u32> {
121        size(self.0.offset(), memory)
122    }
123}
124
125impl Write<Vec<u8>> for AnyPtr {
126    fn alloc(value: &Vec<u8>, env: &Env) -> anyhow::Result<Box<AnyPtr>> {
127        let new = export_asr!(fn_new, env);
128        let size = i32::try_from(value.len())?;
129        let offset = u32::try_from(
130            if let Some(value) = new.call(&[Value::I32(size), Value::I32(0)])?.get(0) {
131                match value.i32() {
132                    Some(offset) => offset,
133                    _ => anyhow::bail!("Unable to allocate value"),
134                }
135            } else {
136                anyhow::bail!("Unable to allocate value")
137            },
138        )?;
139        write_buffer(offset, value, env)?;
140        Ok(Box::new(AnyPtr::new(offset)))
141    }
142
143    fn write(&mut self, value: &Vec<u8>, env: &Env) -> anyhow::Result<Box<Self>> {
144        let memory = match env.memory.get_ref() {
145            Some(mem) => mem,
146            _ => anyhow::bail!("Cannot get memory"),
147        };
148        let prev_size = size(self.offset(), memory)?;
149        let new_size = u32::try_from(value.len())?;
150        if prev_size == new_size {
151            write_buffer(self.offset(), value, env)?;
152            Ok(Box::new(*self))
153        } else {
154            // unpin old ptr
155            let unpin = export_asr!(fn_pin, env);
156            unpin.call(&[Value::I32(self.offset().try_into()?)])?;
157
158            // collect
159            let collect = export_asr!(fn_collect, env);
160            collect.call(&[])?;
161
162            // alloc with new size
163            AnyPtr::alloc(value, env)
164        }
165    }
166
167    fn free(self, _env: &Env) -> anyhow::Result<()> {
168        todo!("Release the memory from this string")
169    }
170}
171
172fn write_buffer(offset: u32, value: &[u8], env: &Env) -> anyhow::Result<()> {
173    let view = match env.memory.get_ref() {
174        Some(mem) => mem.view::<u8>(),
175        _ => anyhow::bail!("Uninitialized memory"),
176    };
177    // We count in 32 so we have to devide by 2
178    let from = usize::try_from(offset)?;
179    for (bytes, cell) in value.iter().zip(view[from..from + value.len()].iter()) {
180        cell.set(*bytes);
181    }
182    Ok(())
183}
184
185fn size(offset: u32, memory: &Memory) -> anyhow::Result<u32> {
186    if offset < 8 {
187        anyhow::bail!("Wrong offset: less than 8")
188    }
189    // read -4 offset
190    // https://www.assemblyscript.org/memory.html#internals
191    if let Some(cell) = memory.view::<u32>().get(offset as usize / (32 / 8) - 1) {
192        Ok(cell.get() / 2)
193    } else {
194        anyhow::bail!("Wrong offset: can't read size")
195    }
196}
197
198fn ptr_id(offset: u32, memory: &Memory) -> anyhow::Result<u32> {
199    if offset < 8 {
200        anyhow::bail!("Wrong offset: less than 8")
201    }
202    // read -8 offset
203    // https://www.assemblyscript.org/memory.html#internals
204    if let Some(cell) = memory.view::<u32>().get(offset as usize / (32 / 8) - 2) {
205        Ok(cell.get())
206    } else {
207        anyhow::bail!("Wrong offset: can't read type")
208    }
209}
210
211fn set_id(offset: u32, id: u32, env: &Env) -> anyhow::Result<()> {
212    if offset < 8 {
213        anyhow::bail!("Wrong offset: less than 8")
214    }
215    let memory = match env.memory.get_ref() {
216        Some(mem) => mem,
217        _ => anyhow::bail!("Uninitialized memory"),
218    };
219    // read -8 offset
220    // https://www.assemblyscript.org/memory.html#internals
221    if let Some(cell) = memory.view::<u32>().get((offset as usize / (32 / 8)) - 2) {
222        cell.set(id);
223    } else {
224        anyhow::bail!("Wrong offset: can't read type")
225    }
226
227    Ok(())
228}