kcp_sys/
ffi_safe.rs

1use crate::{error::Error, ffi::*};
2use std::time::Instant;
3
4use bytes::{Bytes, BytesMut};
5
6#[derive(Debug, Clone, Copy)]
7pub struct KcpConfig {
8    pub conv: IUINT32,
9    pub mtu: Option<i32>,
10
11    pub sndwnd: Option<i32>,
12    pub rcvwnd: Option<i32>,
13
14    pub nodelay: Option<i32>,
15    pub interval: Option<i32>,
16    pub resend: Option<i32>,
17    pub nc: Option<i32>,
18}
19
20impl KcpConfig {
21    pub fn new(conv: IUINT32) -> Self {
22        Self {
23            conv,
24            mtu: None,
25            sndwnd: None,
26            rcvwnd: None,
27            nodelay: None,
28            interval: None,
29            resend: None,
30            nc: None,
31        }
32    }
33
34    pub fn new_turbo(conv: IUINT32) -> Self {
35        Self {
36            conv,
37            mtu: Some(1200),
38            sndwnd: Some(1024),
39            rcvwnd: Some(1024),
40            nodelay: Some(1),
41            interval: Some(10),
42            resend: Some(2),
43            nc: Some(1),
44        }
45    }
46}
47
48pub type OutputCb = Box<dyn Fn(u32, BytesMut) -> Result<(), Error>>;
49
50pub struct Kcp {
51    kcp: *mut ikcpcb,
52    config: KcpConfig,
53    now: Instant,
54    output_cb: Option<Box<dyn Fn(u32, BytesMut) -> Result<(), Error>>>,
55
56    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
57}
58
59unsafe impl Send for Kcp {}
60
61unsafe extern "C" fn ikcp_output(
62    buf: *const ::std::os::raw::c_char,
63    len: ::std::os::raw::c_int,
64    kcp: *mut ikcpcb,
65    this: *mut ::std::os::raw::c_void,
66) -> i32 {
67    // convert this to KcpConnection
68    let kcp_connection = &mut *(this as *mut Kcp);
69    assert_eq!(kcp_connection.kcp, kcp);
70
71    let buf = BytesMut::from(std::slice::from_raw_parts(buf as *const u8, len as usize));
72
73    // TODO: handle output error
74    let _ = kcp_connection.handle_output_callback(buf);
75
76    // kcp doesn't care about the return value
77    0
78}
79
80impl Kcp {
81    pub fn new(config: KcpConfig) -> Result<Box<Self>, Error> {
82        unsafe {
83            let conv = config.conv;
84            let mut ret = Box::new(Self {
85                kcp: std::ptr::null_mut(),
86                config,
87                now: Instant::now(),
88                output_cb: None,
89                _marker: core::marker::PhantomData,
90            });
91
92            let kcp = ikcp_create(conv, &mut *ret as *mut Kcp as *mut ::std::os::raw::c_void);
93            if kcp.is_null() {
94                return Err(Error::CreateConnectionFailed);
95            }
96
97            (*kcp).stream = 1;
98
99            ret.kcp = kcp;
100
101            ikcp_setoutput(kcp, Some(ikcp_output));
102
103            ret.apply_config()?;
104
105            return Ok(ret);
106        }
107    }
108
109    pub fn set_output_cb(&mut self, output_cb: OutputCb) {
110        self.output_cb = Some(output_cb);
111    }
112
113    pub fn handle_input(&mut self, data: &[u8]) -> Result<(), Error> {
114        let ret = unsafe { ikcp_input(self.kcp, data.as_ptr() as *const _, data.len() as _) };
115        if ret < 0 {
116            return Err(anyhow::anyhow!("input failed, return: {}", ret).into());
117        } else {
118            return Ok(());
119        }
120    }
121
122    pub fn update(&mut self) {
123        unsafe {
124            ikcp_update(self.kcp, self.now.elapsed().as_millis() as IUINT32);
125        }
126    }
127
128    pub fn next_update_delay_ms(&mut self) -> IUINT32 {
129        let current = self.now.elapsed().as_millis() as IUINT32;
130        let next = unsafe { ikcp_check(self.kcp, current) };
131        next - current
132    }
133
134    pub fn send(&mut self, data: Bytes) -> Result<usize, Error> {
135        let ret = unsafe { ikcp_send(self.kcp, data.as_ptr() as *const _, data.len() as _) };
136        if ret < 0 {
137            return Err(anyhow::anyhow!("send failed, return: {}", ret).into());
138        } else {
139            return Ok(ret as usize);
140        }
141    }
142
143    pub fn flush(&mut self) {
144        unsafe {
145            ikcp_flush(self.kcp);
146        }
147    }
148
149    pub fn peeksize(&self) -> i32 {
150        unsafe { ikcp_peeksize(self.kcp) }
151    }
152
153    pub fn recv(&mut self, buf: &mut BytesMut) -> Result<(), Error> {
154        let ret = unsafe { ikcp_recv(self.kcp, buf.as_mut_ptr() as *mut _, buf.capacity() as _) };
155        if ret < 0 {
156            return Err(anyhow::anyhow!("recv failed, return: {}", ret).into());
157        } else {
158            unsafe {
159                buf.set_len(ret as usize);
160            }
161            return Ok(());
162        }
163    }
164
165    pub fn waitsnd(&self) -> i32 {
166        unsafe { ikcp_waitsnd(self.kcp) }
167    }
168
169    pub fn sendwnd(&self) -> i32 {
170        // see IKCP_WND_SND
171        self.config.sndwnd.unwrap_or(32)
172    }
173
174    fn handle_output_callback(&self, buf: BytesMut) -> Result<(), Error> {
175        (self.output_cb.as_ref().unwrap())(self.config.conv, buf)
176    }
177
178    fn apply_config(&mut self) -> Result<(), Error> {
179        unsafe {
180            let ret = ikcp_setmtu(self.kcp, self.config.mtu.unwrap_or(1200));
181            if ret < 0 {
182                return Err(anyhow::anyhow!("setmtu failed, return: {}", ret).into());
183            }
184
185            let ret = ikcp_wndsize(
186                self.kcp,
187                self.config.sndwnd.unwrap_or(-1),
188                self.config.rcvwnd.unwrap_or(-1),
189            );
190            if ret < 0 {
191                return Err(anyhow::anyhow!("wndsize failed, return: {}", ret).into());
192            }
193
194            let ret = ikcp_nodelay(
195                self.kcp,
196                self.config.nodelay.unwrap_or(-1),
197                self.config.interval.unwrap_or(-1),
198                self.config.resend.unwrap_or(-1),
199                self.config.nc.unwrap_or(-1),
200            );
201            if ret < 0 {
202                return Err(anyhow::anyhow!("nodelay failed, return: {}", ret).into());
203            }
204
205            if let Some(interval) = self.config.interval {
206                if interval > 0 {
207                    (*self.kcp).interval = interval as _;
208                }
209            }
210        }
211
212        return Ok(());
213    }
214}
215
216impl Drop for Kcp {
217    fn drop(&mut self) {
218        unsafe {
219            ikcp_release(self.kcp);
220        }
221    }
222}