Skip to content

zdimension/embed-c

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
src
 
 
 
 
 
 
 
 
 
 
 
 
 
 

embed-c

embed-c is a crate that allows you to embed C code inside Rust code files. The C code is translated into Rust code at compile time using C2Rust, which means that it is fully interoperable with Rust. C code can call Rust code, and vice-versa.

Basic usage

#![feature(rustc_private)] 

use embed_c::embed_c;

embed_c! {
    int add(int x, int y) {
        return x + y;
    }
}

fn main() {
    let x = unsafe { add(1, 2) };
    println!("{}", x);
}

Install

The library is not yet on crates.io. Clone the repository somewhere and set it up:

git clone https://github.com/zdimension/embed-c.git
cd embed-c
git submodule update --init c2rust
cp Cargo.lock ..
cd ..

and add this to your Cargo.toml:

[dependencies]
embed-c = { path = "./embed-c", version = "0.1" }

[patch.crates-io]
c2rust-transpile = { path = "./embed-c/c2rust/c2rust-transpile" }

NOTE: this crate is designed to work for the nightly-2019-12-05 version of Rust, so put this in your rust-toolchain.toml:

[toolchain]
channel = "nightly-2019-12-05"

And change the package.edition setting in your Cargo.toml to be "2018":

[package]
edition = "2018"

If you get errors about the matches! macro, or from the half or rustc_demangle crates, copy the Cargo.lock file to your project root again.

Usage details

The example at the top is translated into:

#[no_mangle]
pub unsafe extern "C" fn add(mut x: libc::c_int, mut y: libc::c_int)
 -> libc::c_int {
    return x + y;
}

fn main() {
    let x = unsafe { add(1, 2) };
    println!("{}", x);
}

The #![feature(rustc_private)] bit is required since the crate uses internal features while not being loaded from crates.io.

See more examples in src/lib.rs.

embed_c! {
    void send(to, from, count)
        register short *to, *from;
        register count;
    {
        register n = (count + 7) / 8;
        switch (count % 8) {
        case 0: do { *to++ = *from++;
        case 7:      *to++ = *from++;
        case 6:      *to++ = *from++;
        case 5:      *to++ = *from++;
        case 4:      *to++ = *from++;
        case 3:      *to++ = *from++;
        case 2:      *to++ = *from++;
        case 1:      *to++ = *from++;
                } while (--n > 0);
        }
    }
}

fn main() {
    let mut source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    let mut dest = [0; 10];
    unsafe { send(dest.as_mut_ptr(), source.as_mut_ptr(), 10); };
    assert_eq!(source, dest);
}

Here, the send function becomes:

#[no_mangle]
pub unsafe extern "C" fn send(mut to: *mut libc::c_short,
                              mut from: *mut libc::c_short,
                              mut count: libc::c_int) {
    let mut n: libc::c_int = (count + 7 as libc::c_int) / 8 as libc::c_int;
    let mut current_block_7: u64;
    match count % 8 as libc::c_int {
        0 => { current_block_7 = 8258075665625361029; }
        7 => { current_block_7 = 8412423308551259261; }
        6 => { current_block_7 = 15077176690991321518; }
        ...
        1 => { current_block_7 = 14053040055230693409; }
        _ => { current_block_7 = 13586036798005543211; }
    }
    loop  {
        match current_block_7 {
            13586036798005543211 => { return; }
            8258075665625361029 => {
                let fresh0 = from;
                from = from.offset(1);
                let fresh1 = to;
                to = to.offset(1);
                *fresh1 = *fresh0;
                current_block_7 = 8412423308551259261;
            }
            8412423308551259261 => ...
            ...
            _ => {
                let fresh14 = from;
                from = from.offset(1);
                let fresh15 = to;
                to = to.offset(1);
                *fresh15 = *fresh14;
                n -= 1;
                if n > 0 as libc::c_int {
                    current_block_7 = 8258075665625361029;
                } else { current_block_7 = 13586036798005543211; }
            }
        }
    };
}

As you can see, the interweaved switch-do construct is transpiled into a very idiomatic Rust function through the use of pattern matching. You may not like it, but this is what peak functional programming looks like.

Limitations

Many

Motivation

N/A

License

This project is licensed under either of

About

Embed C code at compile time inside Rust using C2Rust

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Languages