其中 Rust 中 Python 中 msgpack Timestamp 类的等价物

Where is the equivalent in Rust of the msgpack Timestamp class in Python

提问人:Sebastian 提问时间:11/10/2023 最后编辑:Sebastian 更新时间:11/10/2023 访问量:66

问:

在 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_timestampSystemTimei64f64serde_asTimestampSecondsVec<u8>serde_bytesserde::conv!

在我看来,这应该很容易,因为 Python 将所有这些功能内置到 msgpack 库中。那么如何在 Rust 中解码时间戳呢?

我正在使用最新的板条箱版本

rmp-serde = "1.1.2"
serde = "1"
serde_derive = "1"
serde_with = "3.4"
serde_bytes = "0.11"
Rust 反序列化 serde msgpack

评论


答:

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/......