簡體   English   中英

將類 C 的打包數據結構映射到 Rust 結構

[英]Map C-like packed data structure to Rust struct

我是 Rust 的新手,大部分時間都在用 C/C++ 編寫代碼。 我有一個 Flask 網絡服務器,它以長度 + 空終止字符串的形式返回一個打包的數據結構:

test_data = "Hello there bob!" + "\x00"
test_data = test_data.encode("utf-8")
data = struct.pack("<I", len(test_data ))
data += test_data
return data

在我的easy_http_request代碼中,我使用的是easy_http_request crate 並且可以通過調用get_from_url_str成功獲取響應。 我想要做的是將返回的響應映射回Test數據結構(如果可能)。 我試圖使用align_to未能成功地將字符串數據映射到結構。

extern crate easy_http_request;
extern crate libc;

use easy_http_request::DefaultHttpRequest;
use libc::c_char;

#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
struct Test {
    a: u32,
    b: *const c_char  // TODO: What do I put here???
}

fn main() {
    let response = DefaultHttpRequest::get_from_url_str("http://localhost:5000/").unwrap().send().unwrap();
    let (head, body, _tail) = unsafe { response.body.align_to::<Test>() };
    let my_test: Test = body[0];
    println!("{}", my_test.a); // Correctly prints '17'
    println!("{:?}", my_test.b); // Fails
}

我不確定這在 Rust 中是否可行。 response.body我可以正確地看到以空字符結尾的字符串,所以我知道數據在那里。 只是不確定是否有辦法將它映射到Test結構中的字符串。 我沒有理由需要使用以空字符結尾的字符串。 最終,我只是嘗試將大小和字符串的數據結構映射到類似類型的 Rust 結構。

看起來您對pack的兩種不同含義感到困惑: * 在 Python 中, pack是一種將數據序列化為字節數組的協議。 * 在 Rust 中, pack是添加到結構中的指令,用於刪除成員之間的填充並禁用其他奇怪的東西。

雖然它們可以一起使用以使協議起作用,但事實並非如此,因為在您的包中您有一個可變長度的成員。 嘗試直接序列化/反序列化指針值是一個非常糟糕的主意。

您打包的燒瓶消息基本上是:

  • 4 個字節的小端值與字符串中的字節數。
  • 上面為字符串指示的這么多字節,以 utf-8 編碼。

為此,您不需要打包的結構。 最簡單的方法是手動逐個讀取字段。 像這樣(省略錯誤檢查):

use std::convert::TryInto;

let a = i32::from_le_bytes(response[0..4].try_into().unwrap());
let b = std::str::from_utf8(&response[4 .. 4 + a as usize]).unwrap();

不要使用原始指針,它們使用起來不安全,只有在有充分理由繞過 Rust 的安全保證時才推薦使用

至少符合您要求的結構類似於:

struct Test<'a> {
    value: &'a str
}

String擁有的值,以避免生命周期依賴。

&str的引用包含一個 len 和一個指針(它不是類似 C 的char *指針)。

順便說一下,困難的部分不是有線協議的解析,而是正確管理所有可能的解碼錯誤,並在出現錯誤或惡意客戶端的情況下避免意外的運行時故障。

為了不重新發明輪子,使用解析組合器nom的示例:

use nom::{
    number::complete::le_u32,
    bytes::complete::take,
    error::ErrorKind,
    IResult
};
use easy_http_request::DefaultHttpRequest;
use std::str::from_utf8;

#[derive(Debug, Clone)]
struct Test {
    value: String
}

fn decode_len_value(bytes: &[u8]) -> IResult<&[u8], Test> {
    let (buffer, len) = le_u32(bytes)?;

    // take len-1 bytes because null char (\0) is accounted into len
    let (remaining, val) = take(len-1)(buffer)?;
    match from_utf8(val) {
        Ok(strval) => Ok((remaining, Test {value: strval.to_owned()})),
        Err(_) => Err(nom::Err::Error((remaining, ErrorKind::Char)))
    }
}

fn main() {
    let response = DefaultHttpRequest::get_from_url_str("http://localhost:5000/").unwrap().send().unwrap();
    let result = decode_len_value(&response.body);
    println!("{:?}", result);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM