提问人:Sebastian 提问时间:11/10/2023 最后编辑:Sebastian 更新时间:11/10/2023 访问量:66
其中 Rust 中 Python 中 msgpack Timestamp 类的等价物
Where is the equivalent in Rust of the msgpack Timestamp class in Python
问:
在 Python 中,当解码包含时间戳的消息包消息时,msgpack 库(Timestamp 类)中的代码非常简单,因为它只获取为字段找到的字节,并将它们转换为从下面的库复制的字节:
def from_bytes(b):
"""Unpack bytes into a `Timestamp` object.
Used for pure-Python msgpack unpacking.
:param b: Payload from msgpack ext message with code -1
:type b: bytes
:returns: Timestamp object unpacked from msgpack ext payload
:rtype: Timestamp
"""
if len(b) == 4:
seconds = struct.unpack("!L", b)[0]
nanoseconds = 0
elif len(b) == 8:
data64 = struct.unpack("!Q", b)[0]
seconds = data64 & 0x00000003FFFFFFFF
nanoseconds = data64 >> 34
elif len(b) == 12:
nanoseconds, seconds = struct.unpack("!Iq", b)
else:
raise ValueError(
"Timestamp type can only be created from 32, 64, or 96-bit byte objects"
)
return Timestamp(seconds, nanoseconds)
我正在努力在 Rust 中实现相同的行为。为了简单起见,这是一个简单的结构:
#[derive(Debug, PartialEq, Deserialize)]
struct ReceivedMessage {
a_string: String,
a_value: f64,
a_timestamp: ????? // <--- what to put here?
}
当使用二进制消息包消息调用时,我得到rmp_serde::from_slice(&'a [u8])
调用值:Syntax(“invalid type: newtype struct,预期的字节数组“)
Result::unwrap()
Err
我放什么类型并不重要.我试过了,,和等等。然后,我尝试使用并编写一个带有All的自定义转换器,但无济于事,并且总是相同的错误消息。网上似乎没有任何关于此的综合教程。(正如我在调试时发现的那样,结构中的其他字段反序列化得很好)。
如果重要的话,二进制消息以映射的形式出现,即带有字段名称而不是数组。a_timestamp
SystemTime
i64
f64
serde_as
TimestampSeconds
Vec<u8>
serde_bytes
serde::conv!
在我看来,这应该很容易,因为 Python 将所有这些功能内置到 msgpack 库中。那么如何在 Rust 中解码时间戳呢?
我正在使用最新的板条箱版本
rmp-serde = "1.1.2"
serde = "1"
serde_derive = "1"
serde_with = "3.4"
serde_bytes = "0.11"
答:
2赞
Stargateur
11/10/2023
#1
rmp-serde
没有实现此功能,请参阅 #298。
该问题中的一些代码似乎遵循 Msgpack 标准:
#[derive(Debug, Serialize, Deserialize)]
struct SomeStruct {
#[serde(
serialize_with = "serialize_mp_date_secs",
deserialize_with = "deserialize_mp_date_secs"
)]
date: u64,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename = "_ExtStruct")]
pub struct ExtStruct((i8, ByteBuf));
fn u32_from_bytebuf(buf: &ByteBuf) -> Result<u32, TryFromSliceError> {
let bytes = buf.as_slice().try_into()?;
Ok(u32::from_be_bytes(bytes))
}
fn u64_from_bytebuf(buf: &ByteBuf) -> Result<u64, TryFromSliceError> {
let bytes = buf.as_slice().try_into()?;
Ok(u64::from_be_bytes(bytes))
}
pub fn serialize_mp_date_secs<S>(secs: &u64, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let data64 = *secs;
let ext = if (secs >> 34) == 0 {
if (data64 & 0xffffffff00000000) == 0 {
// timestamp 32
let data32 = data64 as u32;
ExtStruct((-1, ByteBuf::from(data32.to_be_bytes())))
} else {
// timestamp 64
ExtStruct((-1, ByteBuf::from(data64.to_be_bytes())))
}
} else {
// timestamp 96
let mut bytes = 0u32.to_be_bytes().to_vec();
let mut secs = (data64 as i64).to_be_bytes().to_vec();
bytes.append(&mut secs);
ExtStruct((-1, ByteBuf::from(bytes)))
};
ext.serialize(serializer)
}
pub fn deserialize_mp_date_secs<'de, D>(deserializer: D) -> Result<u64, D::Error>
where
D: Deserializer<'de>,
{
let ExtStruct((ext_type, buf)) = Deserialize::deserialize(deserializer)?;
if ext_type != -1 {
return Err(de::Error::custom("Invalid extension type (!=-1)"));
}
let sec = match buf.len() {
4 => {
let sec = u32_from_bytebuf(&buf).map_err(|e| {
de::Error::custom(format!("Failed to get u32 from bytebuf ({})", e))
})?;
sec as u64
}
8 => {
let data64 = u64_from_bytebuf(&buf).map_err(|e| {
de::Error::custom(format!("Failed to get u64 from bytebuf ({})", e))
})?;
data64 & 0x00000003ffffffff
}
12 => {
u64_from_bytebuf(&buf)
.map_err(|e| de::Error::custom(format!("Failed to get u64 from bytebuf ({})", e)))?
+ 4
}
n => {
return Err(de::Error::custom(format!(
"Invalid data len {n} (valid sizes are 4, 8 and 12)"
)))
}
};
Ok(sec)
}
然而,这似乎只关心精确到秒。
评论
0赞
Sebastian
11/11/2023
非常感谢您找到这个。我以为我到处找过......但我必须诚实地说,已经达到版本 1 的库/crate 应该是功能完整的。它甚至没有在任何地方被提及。我的 Rust 冒险开始相当坎坷。
0赞
Stargateur
11/11/2023
@Sebastian rmp-serde 没有太多支持,我相信时间戳是一个扩展,也许您可以尝试 lib.rs/crates/msgpacker(看起来更好但没有 serde)。同样在 Rust 中,我们通常使用 bincode,这就是为什么对 messagepack 的支持不多的原因
0赞
Sebastian
11/11/2023
我必须使用消息包,因为我无法控制源。msgpacker 抱怨它不知道如何处理 Vec<String>即使它应该是开箱即用的,所以我忽略了它。此外,msgpack_simple有一个非常好的语法,但似乎已经过时了,因为它产生的字节与rmp_serde不同。所以我还是rmp_serde一起去,用你的答案让它工作!
1赞
Sebastian
11/13/2023
我为 GitHub 问题添加了一个答案,该问题也以纳秒为单位:github.com/3Hren/msgpack-rust/issues/......
评论