[英]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
是添加到結構中的指令,用於刪除成員之間的填充並禁用其他奇怪的東西。
雖然它們可以一起使用以使協議起作用,但事實並非如此,因為在您的包中您有一個可變長度的成員。 嘗試直接序列化/反序列化指針值是一個非常糟糕的主意。
您打包的燒瓶消息基本上是:
為此,您不需要打包的結構。 最簡單的方法是手動逐個讀取字段。 像這樣(省略錯誤檢查):
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.