2016-08-31 6 views
6

サードパーティのメモリアロケータを使用するオプションがあるCライブラリにRustバインディングを書いています。そのインターフェイスは、次のようになります。アロケータを提供できるCライブラリにRustメモリアロケータを使用するにはどうすればよいですか?

struct allocator { 
    void*(*alloc)(void *old, uint); 
    void(*free)(void*); 
}; 

対応錆構造体は、私が推測する、以下の通りです:

#[repr(C)] 
#[derive(Copy, Clone, Debug, PartialEq)] 
pub struct Allocator { 
    alloc: Option<extern "C" fn(*mut c_void, c_uint) -> *mut c_void>, 
    free: Option<extern "C" fn(*mut c_void)>, 
} 

どのように私はアロケータを模倣する必要があり、これらの2つのexternの機能を実装することができますか?私は本当にRustのアロケータAPIのようなものは見つけられませんでした(私はなぜそれが分かりましたか)。それが可能であれば興味があります。

+0

https://www.reddit.com/r/rust/comments/2eqdg2/allocate_a_vec_o​​n_cs_heap/ 同様のトピックについていくつかの考えがありますが、割り当ての有効期間管理は難しいかもしれません。 – snuk182

+1

あなたが指摘しているように、そのトピックは、RustにCと同じアロケータを使用させることです。[custom allocators](https://doc.rust-lang.org/stable/book/custom-allocators.html)で可能です。 – Shepmaster

答えて

6

好きかもしれないほど簡単ではありません。

割り当て方法はheap module of the alloc crateで公開されています。

ストレートフォワードですが、私たちはすぐに問題が発生したいくつかのラッパー・メソッドを作成し、構造体を移入:

#![feature(heap_api)] 

extern crate libc; 
extern crate alloc; 

use libc::{c_void, c_uint}; 
use alloc::heap; 

#[repr(C)] 
#[derive(Copy, Clone, Debug, PartialEq)] 
pub struct Allocator { 
    alloc: Option<extern "C" fn(*mut c_void, c_uint) -> *mut c_void>, 
    free: Option<extern "C" fn(*mut c_void)>, 
} 


extern "C" fn alloc_ext(old: *mut c_void, size: c_uint) -> *mut c_void { 
    if old.is_null() { 
     heap::allocate(size as usize, align) as *mut c_void 
    } else { 
     heap::reallocate(old as *mut u8, old_size, size as usize, align) as *mut c_void 
    } 
} 

extern "C" fn free_ext(old: *mut c_void) { 
    heap::deallocate(old as *mut u8, old_size, align); 
} 

fn main() { 
    Allocator { 
     alloc: Some(alloc_ext), 
     free: Some(free_ext), 
    }; 
} 

錆アロケータは、以前の割り当てのサイズを告げただけでなく、希望されることを想定していアラインメント。あなたが照合しているAPIにはそれを渡す方法がありません。

アライメント(私は専門家ではありません)ハードコードを16バイトと指定しても問題ありません。サイズはよりトリッキーです。おそらく古いCトリックを盗み出し、サイズを格納するために少し余分なスペースを割り当てる必要があります。次に、サイズを保存してその直後にポインタを返すことができます。

完全にテストされていない例:

#![feature(alloc, heap_api)] 

extern crate libc; 
extern crate alloc; 

use libc::{c_void, c_uint}; 
use alloc::heap; 
use std::{mem, ptr}; 

#[repr(C)] 
#[derive(Copy, Clone, Debug, PartialEq)] 
pub struct Allocator { 
    alloc: Option<extern "C" fn(*mut c_void, c_uint) -> *mut c_void>, 
    free: Option<extern "C" fn(*mut c_void)>, 
} 

const ALIGNMENT: usize = 16; 

extern "C" fn alloc_ext(old: *mut c_void, size: c_uint) -> *mut c_void { 
    unsafe { 
     // Should check for integer overflow 
     let size_size = mem::size_of::<usize>(); 
     let size = size as usize + size_size; 

     let memory = if old.is_null() { 
      heap::allocate(size, ALIGNMENT) 
     } else { 
      let old = old as *mut u8; 
      let old = old.offset(-(size_size as isize)); 
      let old_size = *(old as *const usize); 
      heap::reallocate(old, old_size, size, ALIGNMENT) 
     }; 

     *(memory as *mut usize) = size; 
     memory.offset(size_size as isize) as *mut c_void 
    } 
} 

extern "C" fn free_ext(old: *mut c_void) { 
    if old.is_null() { return } 

    unsafe { 
     let size_size = mem::size_of::<usize>(); 

     let old = old as *mut u8; 
     let old = old.offset(-(size_size as isize)); 
     let old_size = *(old as *const usize); 

     heap::deallocate(old as *mut u8, old_size, ALIGNMENT); 
    } 
} 

fn main() { 
    Allocator { 
     alloc: Some(alloc_ext), 
     free: Some(free_ext), 
    }; 

    let pointer = alloc_ext(ptr::null_mut(), 54); 
    let pointer = alloc_ext(pointer, 105); 
    free_ext(pointer); 
} 

ないです[... using Vec as an allocator ...]より高レベルのソリューション?

確かに可能ですが、再割り当てでどのように動作するかは完全にはわかりません。 Vecのサイズと容量を把握して、それを再割り当てして削除する必要があります。

+2

原則として、割り当てサイズを 'HashMap'などに格納することもできます。私はあなたが割振りの全体的な割り当てと保存に何かを得ることは確かではありませんが(ただし、C側から悪い 'free'を検出する良い機会を除いて)。 –

+2

@ChrisEmerson良い点!しかし、私が過去に同様のことを試みたとき、私は異常にパフォーマンスが悪かった。理由を正確に知らなくても、キャッシュのローカリティが悪いことが関係していると思います。複数のスレッドは、共有コレクションを使用するとさらに苦労します。 – Shepmaster

+1

シングルスレッド環境でテストされ、問題はありません。 – snuk182

関連する問題