2016-08-31 14 views
6

Tôi đang viết các liên kết Rust với thư viện C có tùy chọn sử dụng bộ cấp phát bộ nhớ của bên thứ ba. giao diện của nó trông như thế này:Làm cách nào để sử dụng bộ cấp phát bộ nhớ Rust cho thư viện C có thể được cung cấp một bộ cấp phát?

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

Các Rust struct tương ứng là, tôi đoán, như sau:

#[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)>, 
} 

Làm thế nào tôi có thể thực hiện những chức năng hai extern rằng nên bắt chước cấp phát? Tôi không tìm thấy bất cứ điều gì thực sự trông giống như API cấp phát trong Rust (Tôi hiểu lý do tại sao tuy nhiên), vì vậy tôi tò mò nếu nó có thể.

+0

https://www.reddit.com/r/rust/comments/2eqdg2/allocate_a_vec_on_cs_heap/ Có một số suy nghĩ về một chủ đề tương tự, mặc dù việc quản lý thời gian phân bổ có thể khó khăn. – snuk182

+1

Như bạn chỉ ra, chủ đề đó là làm cho Rust sử dụng cùng một trình cấp phát như C. Có thể với [các trình phân bổ tùy chỉnh] (https://doc.rust-lang.org/stable/book/custom-allocators.html). – Shepmaster

Trả lời

6

Không dễ như bạn muốn.

Phương thức phân bổ được hiển thị trong heap module of the alloc crate.

Tạo một số phương pháp wrapper và populating struct là thẳng về phía trước, nhưng chúng tôi nhanh chóng chạy vào một vấn đề:

#![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), 
    }; 
} 

Các Rust cấp phát dự kiến ​​sẽ được thông báo kích thước của bất kỳ phân bổ trước cũng như mong muốn căn chỉnh. API bạn đang đối sánh không có bất kỳ cách nào để truyền dữ liệu đó.

Căn chỉnh nên (Tôi không phải là chuyên gia) được phép mã hóa ở một số giá trị, nói 16 byte. Kích thước là phức tạp hơn. Bạn có thể sẽ cần phải ăn cắp một số thủ thuật C cũ và phân bổ thêm một chút không gian để lưu trữ kích thước. Bạn có thể lưu trữ kích thước và trả về một con trỏ vừa qua.

Một hoàn toàn chưa được kiểm tra dụ:

#![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); 
} 

Không phải là [... using Vec as an allocator ...] giải pháp cấp cao hơn?

Điều đó chắc chắn có thể, nhưng tôi không hoàn toàn chắc chắn nó sẽ hoạt động như thế nào với phân bổ lại. Bạn cũng sẽ phải theo dõi kích thước và dung lượng của Vec để hoàn nguyên nó để tái phân bổ/thả nó.

+2

Bạn cũng có thể lưu trữ kích thước phân bổ trong một 'HashMap' hoặc tương tự. Tôi không chắc chắn bạn sẽ đạt được bất cứ điều gì trên tổng phân bổ và lưu trữ trong phân bổ, mặc dù (ngoại trừ một cơ hội tốt hơn phát hiện một xấu 'miễn phí' từ phía C). –

+2

@ChrisEmerson điểm tốt! Tuy nhiên, khi tôi đã thử những điều tương tự trong quá khứ, tôi đã trải qua hiệu suất kém bất thường. Mà không biết chính xác lý do tại sao, tôi nghĩ rằng nó đã làm với địa phương bộ nhớ cache nghèo. Nhiều chủ đề cũng đau hơn với một bộ sưu tập được chia sẻ. – Shepmaster

+1

Đã thử nghiệm cho môi trường luồng đơn, không có vấn đề gì. – snuk182

Các vấn đề liên quan