I’ve been working on a new project named depict, which helps draw pictures of complex systems.
Depict is implemented in Rust and runs both on desktops and on the web.
Making depict run well on the web has been an exciting challenge because of some normally obscure technical details which I’d like better document.
Today’s version of depict relies on a wonderful piece of software called osqp.
Depict uses osqp to figure out, for a given layout or placement of objects to be drawn, how big the objects should be and where they should be positioned.
OSQP is written in (relatively) library-free C.
Now, normally, when you want to combine programs written in multiple compiled languages like Rust and C, you do so either via networking or linking.
Linking, here, means that the compilers used for each separate unit of code to be linked need to agree on how to communicate, which is called an “ABI”, or “application binary interface”.
As it turns out, ABIs are still in flux for WASM – specifically for wasm32.
As of this writing, there are three wasm32 ABIs in common use and one more under active development:
(Thanks specifically to lopopolo for
pointing out this issue to
me, and to the Rust lang-team for documenting the ABI
mismatch issue so clearly. More reading: WebAssembly/tool-conventions
Since I am already targeting
turned out that the most expedient way to make everything play nicely
together was quite unusual: to mechanically translate osqp from C to a
drop-in rust replacement that I’m calling osqp-rust using c2rust.
A few more details were required to make all this work.
while osqp is relatively library free, in “non-embedded” mode, it still needs to know whether
to use 64-bit or 32-bit integers (cmake’s
it still needs memory allocation as well as OS-specific timing functionality.
I addressed this by translating osqp twice, once in 32-bit mode and once in 64-bit mode, and then adding by using conditional compilation to select which translation to use at (Rust crate) build time.
memory allocation is supported by rust on wasm32 via the dlmalloc crate, which it turns out is itself a c-to-rust translation of the dlmalloc allocator. Consequently, I was able to (somewhat hackishly) reimplement the necessary APIs as wrappers around rust’s System allocator.