14

Exploit custom codecs to write inline C in Python

 4 years ago
source link: https://github.com/georgek42/inlinec
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Inline C

Effortlessly write inline C functions in Python source code

# coding: inlinec
from inlinec import inlinec

@inlinec
def Q_rsqrt(number):
    float Q_rsqrt( float number )
    {
        long i;
        float x2, y;
        const float threehalfs = 1.5F;

        x2 = number * 0.5F;
        y  = number;
        i  = * ( long * ) &y;                       // evil floating point bit level hacking
        i  = 0x5f3759df - ( i >> 1 );               // what the fuck? 
        y  = * ( float * ) &i;
        y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration

        return y;
    }

print(Q_rsqrt(1.234))

Inlinec supports gnu-specific c extensions, so you're likely to have reasonable success #includeing glibc headers.

Inspired by Pyxl

How does this work?

Python has a mechanism for creating custom codecs, which given an input token stream, produce an output token stream. Inlinec consumes the entire token stream, runs a fault-tolerant parser on it ( parso ), finds which function nodes are annotated with an @inlinec decorator, creates a ctypes wrapper for the content of the function, and replaces the function body with a call to the ctypes wrapper. The import for the wrapper is lifted to the top of the file. Once this transformation has been made, the source code is re-tokenized and the Python interpreter only sees the transformed source. So a function like this:

@inlinec
def test():
    #include<stdio.h>
    void test() {
        printf("Hello, world");
    }

Gets turned into:

from test_8281231239129310 import lib as test_8281231239129310_lib, ffii as test_8281231239129310_ffi

@inlinec
def test():
    return test_8281231239129310_lib.test()

In theory, this allows inline c functions to be called with a one-time compilation overhead and the same performance characteristics as ctypes -- the underlying FFI library.

Limitations

Note: This is just a proof of concept

gcc -E

Installation

Inlinec requires a C compiler to be installed on the system (tested with GCC and Clang), as well as the python development libraries to be installed (python3-dev). To play around with it in a container you can use the provided Dockerfile, just run docker build, exec into a shell in the container, and you have a working installation of inlinec.

> docker build -t inlinec . && docker run -it inlinec bash

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK