Skip to main content

rns_core/resource/
window.rs

1use crate::constants::*;
2
3/// Window adaptation state for resource transfers.
4///
5/// Tracks window size, min/max bounds, and rate detection for
6/// fast/very-slow link adaptation.
7#[derive(Debug, Clone)]
8pub struct WindowState {
9    pub window: usize,
10    pub window_max: usize,
11    pub window_min: usize,
12    pub window_flexibility: usize,
13    pub fast_rate_rounds: usize,
14    pub very_slow_rate_rounds: usize,
15}
16
17impl WindowState {
18    pub fn new() -> Self {
19        WindowState {
20            window: RESOURCE_WINDOW,
21            window_max: RESOURCE_WINDOW_MAX_SLOW,
22            window_min: RESOURCE_WINDOW_MIN,
23            window_flexibility: RESOURCE_WINDOW_FLEXIBILITY,
24            fast_rate_rounds: 0,
25            very_slow_rate_rounds: 0,
26        }
27    }
28
29    /// Restore window state from a previous transfer on the same link.
30    pub fn restore(&mut self, previous_window: usize) {
31        self.window = previous_window;
32    }
33
34    /// Called when all outstanding parts in the window are received.
35    /// Grows window and ratchets window_min.
36    pub fn on_window_complete(&mut self) {
37        if self.window < self.window_max {
38            self.window += 1;
39            if (self.window as isize - self.window_min as isize)
40                > (self.window_flexibility as isize - 1)
41            {
42                self.window_min += 1;
43            }
44        }
45    }
46
47    /// Called on timeout waiting for parts.
48    /// Shrinks window, shrinks window_max (can decrease by 2).
49    pub fn on_timeout(&mut self) {
50        if self.window > self.window_min {
51            self.window -= 1;
52            if self.window_max > self.window_min {
53                self.window_max -= 1;
54                if (self.window_max as isize - self.window as isize)
55                    > (self.window_flexibility as isize - 1)
56                {
57                    self.window_max -= 1;
58                }
59            }
60        }
61    }
62
63    /// Update rate tracking based on measured req_resp_rtt_rate.
64    /// Called after first part of a window is received.
65    pub fn update_req_resp_rate(&mut self, rate: f64) {
66        if rate > RESOURCE_RATE_FAST && self.fast_rate_rounds < RESOURCE_FAST_RATE_THRESHOLD {
67            self.fast_rate_rounds += 1;
68            if self.fast_rate_rounds == RESOURCE_FAST_RATE_THRESHOLD {
69                self.window_max = RESOURCE_WINDOW_MAX_FAST;
70            }
71        }
72    }
73
74    /// Update rate tracking based on measured data RTT rate.
75    /// Called after a full window of parts is received.
76    pub fn update_data_rate(&mut self, rate: f64) {
77        if rate > RESOURCE_RATE_FAST && self.fast_rate_rounds < RESOURCE_FAST_RATE_THRESHOLD {
78            self.fast_rate_rounds += 1;
79            if self.fast_rate_rounds == RESOURCE_FAST_RATE_THRESHOLD {
80                self.window_max = RESOURCE_WINDOW_MAX_FAST;
81            }
82        }
83
84        // Very slow detection only when fast_rate_rounds == 0
85        if self.fast_rate_rounds == 0
86            && rate < RESOURCE_RATE_VERY_SLOW
87            && self.very_slow_rate_rounds < RESOURCE_VERY_SLOW_RATE_THRESHOLD
88        {
89            self.very_slow_rate_rounds += 1;
90            if self.very_slow_rate_rounds == RESOURCE_VERY_SLOW_RATE_THRESHOLD {
91                self.window_max = RESOURCE_WINDOW_MAX_VERY_SLOW;
92            }
93        }
94    }
95}
96
97impl Default for WindowState {
98    fn default() -> Self {
99        Self::new()
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_initial_state() {
109        let ws = WindowState::new();
110        assert_eq!(ws.window, RESOURCE_WINDOW); // 4
111        assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_SLOW); // 10
112        assert_eq!(ws.window_min, RESOURCE_WINDOW_MIN); // 2
113        assert_eq!(ws.window_flexibility, RESOURCE_WINDOW_FLEXIBILITY); // 4
114        assert_eq!(ws.fast_rate_rounds, 0);
115        assert_eq!(ws.very_slow_rate_rounds, 0);
116    }
117
118    #[test]
119    fn test_window_increase_on_complete() {
120        let mut ws = WindowState::new();
121        // window=4, window_max=10, window_min=2, flexibility=4
122        ws.on_window_complete();
123        // window -> 5; (5-2)=3, not > (4-1)=3, so window_min stays 2
124        assert_eq!(ws.window, 5);
125        assert_eq!(ws.window_min, 2);
126
127        ws.on_window_complete();
128        // window -> 6; (6-2)=4 > 3, so window_min -> 3
129        assert_eq!(ws.window, 6);
130        assert_eq!(ws.window_min, 3);
131    }
132
133    #[test]
134    fn test_window_capped_at_max() {
135        let mut ws = WindowState::new();
136        ws.window = 10;
137        ws.window_max = 10;
138        ws.on_window_complete();
139        assert_eq!(ws.window, 10); // didn't increase
140    }
141
142    #[test]
143    fn test_window_decrease_on_timeout() {
144        let mut ws = WindowState::new();
145        // window=4, window_max=10, window_min=2
146        ws.on_timeout();
147        // window -> 3, window_max -> 9
148        // (9-3)=6 > (4-1)=3, so window_max -> 8
149        assert_eq!(ws.window, 3);
150        assert_eq!(ws.window_max, 8);
151    }
152
153    #[test]
154    fn test_window_min_floor() {
155        let mut ws = WindowState::new();
156        ws.window = RESOURCE_WINDOW_MIN;
157        ws.on_timeout();
158        assert_eq!(ws.window, RESOURCE_WINDOW_MIN); // can't go below min
159    }
160
161    #[test]
162    fn test_fast_rate_detection() {
163        let mut ws = WindowState::new();
164        // Need FAST_RATE_THRESHOLD(4) rounds of fast rate
165        for _ in 0..3 {
166            ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
167        }
168        assert_eq!(ws.fast_rate_rounds, 3);
169        assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_SLOW); // not yet
170
171        ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
172        assert_eq!(ws.fast_rate_rounds, 4);
173        assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_FAST); // now!
174    }
175
176    #[test]
177    fn test_fast_rate_rounds_never_reset() {
178        let mut ws = WindowState::new();
179        ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
180        assert_eq!(ws.fast_rate_rounds, 1);
181
182        // Slow rate doesn't reset fast_rate_rounds
183        ws.update_req_resp_rate(1.0);
184        assert_eq!(ws.fast_rate_rounds, 1);
185    }
186
187    #[test]
188    fn test_fast_rate_rounds_cap() {
189        let mut ws = WindowState::new();
190        for _ in 0..10 {
191            ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
192        }
193        // Capped at threshold
194        assert_eq!(ws.fast_rate_rounds, RESOURCE_FAST_RATE_THRESHOLD);
195    }
196
197    #[test]
198    fn test_very_slow_detection() {
199        let mut ws = WindowState::new();
200        // fast_rate_rounds must be 0
201        assert_eq!(ws.fast_rate_rounds, 0);
202
203        ws.update_data_rate(RESOURCE_RATE_VERY_SLOW - 1.0);
204        assert_eq!(ws.very_slow_rate_rounds, 1);
205        assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_SLOW); // not yet
206
207        ws.update_data_rate(RESOURCE_RATE_VERY_SLOW - 1.0);
208        assert_eq!(ws.very_slow_rate_rounds, 2);
209        assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_VERY_SLOW); // capped!
210    }
211
212    #[test]
213    fn test_very_slow_blocked_by_fast() {
214        let mut ws = WindowState::new();
215        // Set fast_rate_rounds > 0
216        ws.update_data_rate(RESOURCE_RATE_FAST + 1.0);
217        assert_eq!(ws.fast_rate_rounds, 1);
218
219        // Very slow should not trigger
220        ws.update_data_rate(RESOURCE_RATE_VERY_SLOW - 1.0);
221        assert_eq!(ws.very_slow_rate_rounds, 0);
222    }
223
224    #[test]
225    fn test_restore_window() {
226        let mut ws = WindowState::new();
227        ws.restore(8);
228        assert_eq!(ws.window, 8);
229    }
230
231    #[test]
232    fn test_window_never_below_min() {
233        let mut ws = WindowState::new();
234        ws.window = 2;
235        ws.window_min = 2;
236        ws.on_timeout();
237        assert_eq!(ws.window, 2);
238    }
239
240    #[test]
241    fn test_window_never_above_max() {
242        let mut ws = WindowState::new();
243        ws.window = 10;
244        ws.window_max = 10;
245        ws.on_window_complete();
246        assert_eq!(ws.window, 10);
247    }
248}