Skip to main content

futu_codec/
codec.rs

1use bytes::{Buf, BytesMut};
2use tokio_util::codec::{Decoder, Encoder};
3
4use crate::frame::FutuFrame;
5use crate::header::{FutuHeader, HEADER_SIZE};
6
7/// API frame package limit.
8///
9/// C++ FTLogin channel rejects packages larger than `kMaxPackageSize`
10/// (`FTLogin/Src/ftlogin/channel/impl/channel_impl.h:91`, `decoder.cpp:54-61`),
11/// where package size includes the protocol header. Apply the same defensive
12/// upper bound before reserving memory for local FTAPI frames.
13pub const MAX_FRAME_PACKAGE_SIZE: usize = 12 * 1024 * 1024;
14
15/// **Stable API** — FutuOpenD 协议帧编解码器。
16///
17/// 实现 `tokio_util` 的 `Decoder` / `Encoder` trait,用于在 TCP 流上进行帧
18/// 边界识别和编解码。零 state unit struct,可以自由多实例。
19pub struct FutuCodec;
20
21impl Decoder for FutuCodec {
22    type Item = FutuFrame;
23    type Error = futu_core::error::FutuError;
24
25    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
26        // 尝试解析帧头(不消费)
27        let header = match FutuHeader::peek(src)? {
28            Some(h) => h,
29            None => return Ok(None),
30        };
31
32        let body_len = header.body_len as usize;
33        let total_len = HEADER_SIZE.checked_add(body_len).ok_or_else(|| {
34            futu_core::error::FutuError::Codec("Futu frame length overflow".into())
35        })?;
36        if total_len > MAX_FRAME_PACKAGE_SIZE {
37            return Err(futu_core::error::FutuError::Codec(format!(
38                "Futu frame package too large: actual={total_len} max={MAX_FRAME_PACKAGE_SIZE}"
39            )));
40        }
41
42        // 检查是否有足够的数据
43        if src.len() < total_len {
44            src.reserve(total_len - src.len());
45            return Ok(None);
46        }
47
48        // 消费帧头
49        src.advance(HEADER_SIZE);
50
51        // 提取 body
52        let body = src.split_to(body_len).freeze();
53
54        let frame = FutuFrame { header, body };
55
56        Ok(Some(frame))
57    }
58}
59
60impl Encoder<FutuFrame> for FutuCodec {
61    type Error = futu_core::error::FutuError;
62
63    fn encode(&mut self, item: FutuFrame, dst: &mut BytesMut) -> Result<(), Self::Error> {
64        item.header.encode(dst);
65        dst.extend_from_slice(&item.body);
66        Ok(())
67    }
68}
69
70#[cfg(test)]
71mod tests;