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
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn test_initial_state() {
103        let ws = WindowState::new();
104        assert_eq!(ws.window, RESOURCE_WINDOW); // 4
105        assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_SLOW); // 10
106        assert_eq!(ws.window_min, RESOURCE_WINDOW_MIN); // 2
107        assert_eq!(ws.window_flexibility, RESOURCE_WINDOW_FLEXIBILITY); // 4
108        assert_eq!(ws.fast_rate_rounds, 0);
109        assert_eq!(ws.very_slow_rate_rounds, 0);
110    }
111
112    #[test]
113    fn test_window_increase_on_complete() {
114        let mut ws = WindowState::new();
115        // window=4, window_max=10, window_min=2, flexibility=4
116        ws.on_window_complete();
117        // window -> 5; (5-2)=3, not > (4-1)=3, so window_min stays 2
118        assert_eq!(ws.window, 5);
119        assert_eq!(ws.window_min, 2);
120
121        ws.on_window_complete();
122        // window -> 6; (6-2)=4 > 3, so window_min -> 3
123        assert_eq!(ws.window, 6);
124        assert_eq!(ws.window_min, 3);
125    }
126
127    #[test]
128    fn test_window_capped_at_max() {
129        let mut ws = WindowState::new();
130        ws.window = 10;
131        ws.window_max = 10;
132        ws.on_window_complete();
133        assert_eq!(ws.window, 10); // didn't increase
134    }
135
136    #[test]
137    fn test_window_decrease_on_timeout() {
138        let mut ws = WindowState::new();
139        // window=4, window_max=10, window_min=2
140        ws.on_timeout();
141        // window -> 3, window_max -> 9
142        // (9-3)=6 > (4-1)=3, so window_max -> 8
143        assert_eq!(ws.window, 3);
144        assert_eq!(ws.window_max, 8);
145    }
146
147    #[test]
148    fn test_window_min_floor() {
149        let mut ws = WindowState::new();
150        ws.window = RESOURCE_WINDOW_MIN;
151        ws.on_timeout();
152        assert_eq!(ws.window, RESOURCE_WINDOW_MIN); // can't go below min
153    }
154
155    #[test]
156    fn test_fast_rate_detection() {
157        let mut ws = WindowState::new();
158        // Need FAST_RATE_THRESHOLD(4) rounds of fast rate
159        for _ in 0..3 {
160            ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
161        }
162        assert_eq!(ws.fast_rate_rounds, 3);
163        assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_SLOW); // not yet
164
165        ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
166        assert_eq!(ws.fast_rate_rounds, 4);
167        assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_FAST); // now!
168    }
169
170    #[test]
171    fn test_fast_rate_rounds_never_reset() {
172        let mut ws = WindowState::new();
173        ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
174        assert_eq!(ws.fast_rate_rounds, 1);
175
176        // Slow rate doesn't reset fast_rate_rounds
177        ws.update_req_resp_rate(1.0);
178        assert_eq!(ws.fast_rate_rounds, 1);
179    }
180
181    #[test]
182    fn test_fast_rate_rounds_cap() {
183        let mut ws = WindowState::new();
184        for _ in 0..10 {
185            ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
186        }
187        // Capped at threshold
188        assert_eq!(ws.fast_rate_rounds, RESOURCE_FAST_RATE_THRESHOLD);
189    }
190
191    #[test]
192    fn test_very_slow_detection() {
193        let mut ws = WindowState::new();
194        // fast_rate_rounds must be 0
195        assert_eq!(ws.fast_rate_rounds, 0);
196
197        ws.update_data_rate(RESOURCE_RATE_VERY_SLOW - 1.0);
198        assert_eq!(ws.very_slow_rate_rounds, 1);
199        assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_SLOW); // not yet
200
201        ws.update_data_rate(RESOURCE_RATE_VERY_SLOW - 1.0);
202        assert_eq!(ws.very_slow_rate_rounds, 2);
203        assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_VERY_SLOW); // capped!
204    }
205
206    #[test]
207    fn test_very_slow_blocked_by_fast() {
208        let mut ws = WindowState::new();
209        // Set fast_rate_rounds > 0
210        ws.update_data_rate(RESOURCE_RATE_FAST + 1.0);
211        assert_eq!(ws.fast_rate_rounds, 1);
212
213        // Very slow should not trigger
214        ws.update_data_rate(RESOURCE_RATE_VERY_SLOW - 1.0);
215        assert_eq!(ws.very_slow_rate_rounds, 0);
216    }
217
218    #[test]
219    fn test_restore_window() {
220        let mut ws = WindowState::new();
221        ws.restore(8);
222        assert_eq!(ws.window, 8);
223    }
224
225    #[test]
226    fn test_window_never_below_min() {
227        let mut ws = WindowState::new();
228        ws.window = 2;
229        ws.window_min = 2;
230        ws.on_timeout();
231        assert_eq!(ws.window, 2);
232    }
233
234    #[test]
235    fn test_window_never_above_max() {
236        let mut ws = WindowState::new();
237        ws.window = 10;
238        ws.window_max = 10;
239        ws.on_window_complete();
240        assert_eq!(ws.window, 10);
241    }
242}