背景
typedef enum {SNAPPY_OK = 0,SNAPPY_INVALID_INPUT = 1,SNAPPY_BUFFER_TOO_SMALL = 2} snappy_status;snappy_status snappy_compress(const char* input,size_t input_length,char* compressed,size_t* compressed_length);snappy_status snappy_uncompress(const char* compressed,size_t compressed_length,char* uncompressed,size_t* uncompressed_length);size_t snappy_max_compressed_length(size_t source_length);snappy_status snappy_uncompressed_length(const char* compressed,size_t compressed_length,size_t* result);snappy_status snappy_validate_compressed_buffer(const char* compressed,size_t compressed_length)
cargo new --lib snappy-rscd snappy-rs编辑Cargo.toml,在[dependencies]部分加入libc
[dependencies]libc = "0.2"编辑src/lib.rs,加入如下代码:
use libc::{c_int, size_t};#[link(name = "snappy")]extern {fn snappy_compress(input: *const u8,input_length: size_t,compressed: *mut u8,compressed_length: *mut size_t) -> c_int;fn snappy_uncompress(compressed: *const u8,compressed_length: size_t,uncompressed: *mut u8,uncompressed_length: *mut size_t) -> c_int;fn snappy_max_compressed_length(source_length: size_t) -> size_t;fn snappy_uncompressed_length(compressed: *const u8,compressed_length: size_t,result: *mut size_t) -> c_int;fn snappy_validate_compressed_buffer(compressed: *const u8,compressed_length: size_t) -> c_int;}
use libc::{c_int, size_t};
[link(name = "snappy")]
snappy 这个库(比如,在 Linux 下就是对应 libsnappy.so 这个文件)。extern {}
extern "C" {}
fn snappy_compress(input: *const u8,input_length: size_t,compressed: *mut u8,: *mut size_t) -> c_int;
而对应的 C 代码为:
snappy_status snappy_compress(const char* input,size_t input_length,char* compressed,size_t* compressed_length);
函数名相同,不表。
c_int,C 代码,返回 snappy_status 类型, 它是个数字枚举类型,可取值为 0, 1, 2 中的一个。因此,两者是基本一样的。只是 Rust 这个封装为了简化,直接用一个 c_int 代替了数字枚举。Rust 中这个返回值的取值范围会大一些,理解上没那么清晰。input: *const u8,C 代码为 const char* input。*const 是指向常量的指针(通过这个指针,不能修改目标对象的值),对应的 *mut
是指向变量的指针(通过这个指针,可以修改目标对象的值)。然后,后面是 u8。这是因为,在 C
中,一个字符串实际是一个字符数组,而这个字符数组通常用指向这个数组开始地址的一个字符指针 char* 来表示(在前面加
const,表示这个字符串不能被这个指针修改)。C 中的字符,其实就是一个字节,即 u8。故这两种写法,描述的是同一个东西。input_length: size_t,C 代码为 size_t input_length。compressed: *mut u8,C 代码为 char* compressed。*mut 是 Rust 中的一种指针,指向一个变量,并且可通过这个指针来修改这个变量。这里这个变量就是 compressed。同样,类型为 u8,意思就是指向一个连续内存空间的指针。这个连续内存空间,可用来存放 C 字符串。compressed_length: *mut size_t, C 代码为 size_t* compressed_length。fn main() {let x = unsafe { snappy_max_compressed_length(100) };println!("max compressed length of a 100 byte buffer: {}", x);}
这个函数的作用,就是输入一个整数,然后计算一个整数输出。它本身的意义是根据给定的缓冲长度,计算压缩后的字符串的最大长度。
unsafe { } 中调用。这里,体现了 Rust 的一个极其重要的设计哲学:所有外来,皆不可信。unsafe,也不是办法啊,这不是 Rust 的风格。Rust 中应该尽量使用非 unsafe 代码。snappy_max_compressed_length 和 snappy_uncompressed_length 都是辅助函数,我们并不需要真正对用户导出。下面我们封装其它三个接口为 Rust 品味的函数。validate_compressed_buffer 检查缓冲区数据是不是正确的。pub fn validate_compressed_buffer(src: &[u8]) -> bool {unsafe {snappy_validate_compressed_buffer(src.as_ptr(), src.len() as size_t) == 0}}
src.as_ptr() 将 slice 转换成 *const T。它的定义在 std 文档中可以查到:pub const fn as_ptr(&self) -> *const T
pub fn compress(src: &[u8]) -> Vec<u8> {unsafe {let srclen = src.len() as size_t;let psrc = src.as_ptr();let mut dstlen = snappy_max_compressed_length(srclen);let mut dst = Vec::with_capacity(dstlen as usize);let pdst = dst.as_mut_ptr();snappy_compress(psrc, srclen, pdst, &mut dstlen);dst.set_len(dstlen as usize);dst}}
as_mut_ptr() 把一个 Vec 转换成 *mut T。函数原型为:pub fn as_mut_ptr(&mut self) -> *mut T
snappy_compress() 中做的,最后返回出人见人爱的 Vec<u8>,happy。unsafe 括起来。uncompress,用于解压缩。pub fn uncompress(src: &[u8]) -> Option<Vec<u8>> {unsafe {let srclen = src.len() as size_t;let psrc = src.as_ptr();let mut dstlen: size_t = 0;snappy_uncompressed_length(psrc, srclen, &mut dstlen);let mut dst = Vec::with_capacity(dstlen as usize);let pdst = dst.as_mut_ptr();if snappy_uncompress(psrc, srclen, pdst, &mut dstlen) == 0 {dst.set_len(dstlen as usize);Some(dst)} else {None // SNAPPY_INVALID_INPUT}}}
#[cfg(test)]mod tests {use super::*;#[test]fn valid() {let d = vec![0xde, 0xad, 0xd0, 0x0d];let c: &[u8] = &compress(&d);assert!(validate_compressed_buffer(c));assert!(uncompress(c) == Some(d));}#[test]fn invalid() {let d = vec![0, 0, 0, 0];assert!(!validate_compressed_buffer(&d));assert!(uncompress(&d).is_none());}#[test]fn empty() {let d = vec![];assert!(!validate_compressed_buffer(&d));assert!(uncompress(&d).is_none());let c = compress(&d);assert!(validate_compressed_buffer(&c));assert!(uncompress(&c) == Some(d));}}
原文作者:Rust语言中文社区;如有侵权,请联系管理员删除。