Skip to main content

aria2_core/engine/
concurrent_segment_manager.rs

1use std::sync::Arc;
2use std::sync::atomic::{AtomicU32, Ordering};
3use tracing::debug;
4
5use crate::constants;
6use crate::selector::server_stat_man::ServerStatMan;
7use crate::selector::uri_selector::UriSelector;
8
9#[derive(Debug, Clone, PartialEq)]
10pub enum SegmentStatus {
11    Pending,
12    Downloading,
13    Done,
14    Failed,
15}
16
17#[derive(Debug)]
18pub struct Segment {
19    pub index: u32,
20    pub offset: u64,
21    pub length: u64,
22    pub status: SegmentStatus,
23    pub data: Option<bytes::Bytes>, // Zero-copy storage
24    pub assigned_mirror: Option<usize>,
25    pub retry_count: u32,
26}
27
28impl Segment {
29    fn new(index: u32, offset: u64, length: u64) -> Self {
30        Self {
31            index,
32            offset,
33            length,
34            status: SegmentStatus::Pending,
35            data: None,
36            assigned_mirror: None,
37            retry_count: 0,
38        }
39    }
40}
41
42#[derive(Debug)]
43pub struct MirrorState {
44    pub url: String,
45    pub speed: f64,
46    pub active_segments: usize,
47    pub max_connections: usize,
48    pub consecutive_failures: usize,
49    pub disabled: bool,
50}
51
52impl MirrorState {
53    fn new(url: String) -> Self {
54        Self {
55            url,
56            speed: 0.0,
57            active_segments: 0,
58            max_connections: constants::DEFAULT_MAX_CONNECTIONS_PER_MIRROR,
59            consecutive_failures: 0,
60            disabled: false,
61        }
62    }
63
64    pub fn is_available(&self) -> bool {
65        !self.disabled && self.active_segments < self.max_connections
66    }
67
68    pub fn can_accept_more(&self) -> bool {
69        !self.disabled && self.active_segments < self.max_connections
70    }
71}
72
73pub struct ConcurrentSegmentManager {
74    total_size: u64,
75    segments: Vec<Segment>,
76    mirrors: Vec<MirrorState>,
77    /// URLs for mirror selection (used with UriSelector)
78    mirror_urls: Vec<String>,
79    completed_bytes: u64,
80    max_retries_per_segment: u32,
81    max_mirror_failures: usize,
82    /// Optional server statistics manager for intelligent mirror selection
83    stat_man: Option<Arc<ServerStatMan>>,
84    /// Optional URI selector for intelligent mirror selection
85    uri_selector: Option<Box<dyn UriSelector>>,
86    /// Atomic hint pointing at the next candidate segment index for allocation.
87    ///
88    /// This enables O(1) amortized segment allocation: scans start from the last
89    /// assigned position instead of re-scanning already-assigned segments from
90    /// the beginning. Updated with `Ordering::Relaxed` since it is only a hint;
91    /// the linear scan with wraparound always preserves correctness even if the
92    /// hint races or points at an already-assigned segment.
93    next_segment_idx: AtomicU32,
94}
95
96impl ConcurrentSegmentManager {
97    /// Create a new segment manager with basic round-robin mirror selection.
98    ///
99    /// This is the basic constructor that uses the built-in `MirrorState`
100    /// for mirror tracking. For intelligent mirror selection, use
101    /// `new_with_selector()` instead.
102    pub fn new(total_size: u64, urls: Vec<String>, segment_size: Option<u64>) -> Self {
103        let seg_size = segment_size.unwrap_or(constants::DEFAULT_SEGMENT_SIZE as u64);
104        let num_segments = if total_size == 0 {
105            0
106        } else {
107            total_size.div_ceil(seg_size) as usize
108        };
109
110        let mut segments = Vec::with_capacity(num_segments);
111        for i in 0..num_segments {
112            let offset = (i as u64) * seg_size;
113            let remaining = total_size.saturating_sub(offset);
114            let length = seg_size.min(remaining);
115            segments.push(Segment::new(i as u32, offset, length));
116        }
117
118        let mirrors = urls.iter().cloned().map(MirrorState::new).collect();
119
120        Self {
121            total_size,
122            segments,
123            mirrors,
124            mirror_urls: urls,
125            completed_bytes: 0,
126            max_retries_per_segment: constants::MAX_RETRIES_PER_SEGMENT,
127            max_mirror_failures: constants::MAX_MIRROR_FAILURES as usize,
128            stat_man: None,
129            uri_selector: None,
130            next_segment_idx: AtomicU32::new(0),
131        }
132    }
133
134    /// Create a new segment manager with intelligent mirror selection.
135    ///
136    /// This constructor integrates with `ServerStatMan` and `UriSelector`
137    /// for intelligent mirror selection based on historical performance data.
138    ///
139    /// # Arguments
140    ///
141    /// * `total_size` - Total file size in bytes
142    /// * `urls` - List of mirror URLs
143    /// * `segment_size` - Optional segment size (default: 1 MB)
144    /// * `stat_man` - Shared server statistics manager
145    /// * `uri_selector` - URI selector for intelligent mirror selection
146    ///
147    /// # Example
148    ///
149    /// ```ignore
150    /// use std::sync::Arc;
151    /// use aria2_core::selector::server_stat_man::ServerStatMan;
152    /// use aria2_core::selector::adaptive_uri_selector::AdaptiveUriSelector;
153    /// use aria2_core::engine::concurrent_segment_manager::ConcurrentSegmentManager;
154    ///
155    /// let stat_man = Arc::new(ServerStatMan::new());
156    /// let urls = vec!["http://mirror1.com/file".to_string()];
157    /// let selector = Box::new(AdaptiveUriSelector::new_with_uris(Arc::clone(&stat_man), urls.clone()));
158    ///
159    /// let mgr = ConcurrentSegmentManager::new_with_selector(
160    ///     10_000_000,
161    ///     urls,
162    ///     Some(1_000_000),
163    ///     stat_man,
164    ///     selector,
165    /// );
166    /// ```
167    pub fn new_with_selector(
168        total_size: u64,
169        urls: Vec<String>,
170        segment_size: Option<u64>,
171        stat_man: Arc<ServerStatMan>,
172        uri_selector: Box<dyn UriSelector>,
173    ) -> Self {
174        let seg_size = segment_size.unwrap_or(constants::DEFAULT_SEGMENT_SIZE as u64);
175        let num_segments = if total_size == 0 {
176            0
177        } else {
178            total_size.div_ceil(seg_size) as usize
179        };
180
181        let mut segments = Vec::with_capacity(num_segments);
182        for i in 0..num_segments {
183            let offset = (i as u64) * seg_size;
184            let remaining = total_size.saturating_sub(offset);
185            let length = seg_size.min(remaining);
186            segments.push(Segment::new(i as u32, offset, length));
187        }
188
189        let mirrors = urls.iter().cloned().map(MirrorState::new).collect();
190
191        Self {
192            total_size,
193            segments,
194            mirrors,
195            mirror_urls: urls,
196            completed_bytes: 0,
197            max_retries_per_segment: constants::MAX_RETRIES_PER_SEGMENT,
198            max_mirror_failures: constants::MAX_MIRROR_FAILURES as usize,
199            stat_man: Some(stat_man),
200            uri_selector: Some(uri_selector),
201            next_segment_idx: AtomicU32::new(0),
202        }
203    }
204
205    pub fn allocate_segments(&mut self) {
206        for mirror_idx in 0..self.mirrors.len() {
207            while self.mirrors[mirror_idx].can_accept_more() {
208                if let Some(seg) = self.find_pending_segment() {
209                    seg.status = SegmentStatus::Downloading;
210                    seg.assigned_mirror = Some(mirror_idx);
211                    self.mirrors[mirror_idx].active_segments += 1;
212                } else {
213                    break;
214                }
215            }
216        }
217    }
218
219    fn find_pending_segment(&mut self) -> Option<&mut Segment> {
220        let len = self.segments.len();
221        if len == 0 {
222            return None;
223        }
224        // Start scanning from the atomic hint to skip already-assigned segments.
225        // The wraparound scan guarantees correctness even if the hint is stale.
226        let start = (self.next_segment_idx.load(Ordering::Relaxed) as usize) % len;
227        for i in 0..len {
228            let idx = (start + i) % len;
229            if self.segments[idx].status == SegmentStatus::Pending {
230                // Advance the hint past this segment; the caller marks it
231                // Downloading immediately so it won't be selected again.
232                self.next_segment_idx
233                    .store((idx + 1) as u32, Ordering::Relaxed);
234                return Some(&mut self.segments[idx]);
235            }
236        }
237        None
238    }
239
240    pub fn next_pending_segment_for_mirror(
241        &mut self,
242        mirror_idx: usize,
243    ) -> Option<(u32, u64, u64)> {
244        if !self
245            .mirrors
246            .get(mirror_idx)
247            .is_some_and(|m| m.can_accept_more())
248        {
249            return None;
250        }
251
252        let len = self.segments.len();
253        if len == 0 {
254            return None;
255        }
256        // Start scanning from the atomic hint to skip already-assigned segments.
257        // This yields O(1) amortized allocation: each segment is visited at most
258        // once before the hint catches up to it.
259        let start = (self.next_segment_idx.load(Ordering::Relaxed) as usize) % len;
260        for i in 0..len {
261            let idx = (start + i) % len;
262            let seg = &mut self.segments[idx];
263            if seg.status == SegmentStatus::Pending {
264                seg.status = SegmentStatus::Downloading;
265                seg.assigned_mirror = Some(mirror_idx);
266                self.next_segment_idx
267                    .store((idx + 1) as u32, Ordering::Relaxed);
268                if let Some(m) = self.mirrors.get_mut(mirror_idx) {
269                    m.active_segments += 1;
270                }
271                return Some((seg.index, seg.offset, seg.length));
272            }
273        }
274        None
275    }
276
277    pub fn next_pending_segment(&mut self) -> Option<(u32, u64, u64)> {
278        self.next_pending_segment_for_mirror(0)
279    }
280
281    /// Atomically allocate the next segment index for lock-free assignment.
282    ///
283    /// Returns the index of the next segment to claim, or `None` if all segments
284    /// have been allocated. This uses `fetch_add` for O(1) lock-free allocation.
285    /// The caller is responsible for checking the segment status and claiming it.
286    ///
287    /// Unlike [`next_pending_segment_for_mirror`](Self::next_pending_segment_for_mirror),
288    /// this method takes only `&self` and is therefore safe to call concurrently
289    /// from multiple threads through an `Arc<ConcurrentSegmentManager>`.
290    ///
291    /// # Concurrency
292    ///
293    /// Each successful call returns a distinct index. Once all segments have been
294    /// issued, subsequent calls return `None` (the counter keeps growing but is
295    /// bounded by the number of callers, so it cannot overflow in practice).
296    pub fn allocate_next_index(&self) -> Option<u32> {
297        let idx = self.next_segment_idx.fetch_add(1, Ordering::Relaxed);
298        if (idx as usize) < self.segments.len() {
299            Some(idx)
300        } else {
301            None
302        }
303    }
304
305    /// Reset the allocation index to 0.
306    ///
307    /// This is useful for retry scenarios where segments that previously failed
308    /// need to be reconsidered for assignment from the beginning of the vector.
309    ///
310    /// # Safety of use
311    ///
312    /// This must not be called concurrently with [`allocate_next_index`](Self::allocate_next_index)
313    /// if unique indices are required; it is intended for reset points where the
314    /// caller has exclusive access (e.g. between download attempts).
315    pub fn reset_allocation_index(&self) {
316        self.next_segment_idx.store(0, Ordering::Relaxed);
317    }
318
319    pub fn complete_segment(&mut self, index: u32, data: bytes::Bytes) -> bool {
320        if let Some(seg) = self.segments.get_mut(index as usize) {
321            seg.status = SegmentStatus::Done;
322            seg.data = Some(data);
323
324            if let Some(mi) = seg.assigned_mirror
325                && let Some(m) = self.mirrors.get_mut(mi)
326            {
327                m.active_segments = m.active_segments.saturating_sub(1);
328                m.consecutive_failures = 0;
329            }
330
331            self.completed_bytes += seg.length;
332            true
333        } else {
334            false
335        }
336    }
337
338    pub fn fail_segment(&mut self, index: u32) -> Option<usize> {
339        let (prev_mirror, new_retry) = {
340            let seg = self.segments.get(index as usize)?;
341            (seg.assigned_mirror, seg.retry_count + 1)
342        };
343
344        if let Some(mi) = prev_mirror
345            && let Some(m) = self.mirrors.get_mut(mi)
346        {
347            m.active_segments = m.active_segments.saturating_sub(1);
348            m.consecutive_failures += 1;
349            if m.consecutive_failures >= self.max_mirror_failures {
350                m.disabled = true;
351            }
352        }
353
354        if new_retry >= self.max_retries_per_segment {
355            if let Some(seg) = self.segments.get_mut(index as usize) {
356                seg.status = SegmentStatus::Failed;
357                seg.retry_count = new_retry;
358            }
359            None
360        } else {
361            let reassign = self.find_available_mirror_for_reassignment(prev_mirror.unwrap_or(0));
362            if let Some(seg) = self.segments.get_mut(index as usize) {
363                seg.status = SegmentStatus::Pending;
364                seg.assigned_mirror = reassign;
365                seg.retry_count = new_retry;
366            }
367            reassign
368        }
369    }
370
371    fn find_available_mirror_for_reassignment(&self, exclude: usize) -> Option<usize> {
372        self.mirrors
373            .iter()
374            .enumerate()
375            .filter(|(i, m)| *i != exclude && m.is_available())
376            .map(|(i, _)| i)
377            .next()
378    }
379
380    pub fn is_complete(&self) -> bool {
381        self.segments
382            .iter()
383            .all(|s| s.status == SegmentStatus::Done)
384    }
385
386    pub fn has_failed_segments(&self) -> bool {
387        self.segments
388            .iter()
389            .any(|s| s.status == SegmentStatus::Failed)
390    }
391
392    pub fn has_pending_segments(&self) -> bool {
393        self.segments
394            .iter()
395            .any(|s| s.status == SegmentStatus::Pending)
396    }
397
398    pub fn assemble(&self) -> Option<Vec<u8>> {
399        if !self.is_complete() || self.total_size == 0 {
400            return None;
401        }
402
403        let mut result = Vec::with_capacity(self.total_size as usize);
404        for seg in &self.segments {
405            let data = seg.data.as_ref()?;
406            result.extend_from_slice(data);
407        }
408        Some(result)
409    }
410
411    pub fn progress(&self) -> f64 {
412        if self.total_size == 0 {
413            return 100.0;
414        }
415        let done = self
416            .segments
417            .iter()
418            .filter(|s| s.status == SegmentStatus::Done)
419            .count();
420        done as f64 / self.segments.len() as f64 * 100.0
421    }
422
423    pub fn num_segments(&self) -> usize {
424        self.segments.len()
425    }
426    pub fn segment_status(&self, index: usize) -> Option<SegmentStatus> {
427        self.segments.get(index).map(|s| s.status.clone())
428    }
429    pub fn num_mirrors(&self) -> usize {
430        self.mirrors.len()
431    }
432    pub fn total_size(&self) -> u64 {
433        self.total_size
434    }
435    pub fn completed_bytes(&self) -> u64 {
436        self.completed_bytes
437    }
438
439    pub fn mirror_url(&self, index: usize) -> Option<&str> {
440        self.mirrors.get(index).map(|m| m.url.as_str())
441    }
442
443    pub fn available_mirrors(&self) -> Vec<usize> {
444        self.mirrors
445            .iter()
446            .enumerate()
447            .filter(|(_, m)| m.is_available())
448            .map(|(i, _)| i)
449            .collect()
450    }
451
452    pub fn any_mirror_available(&self) -> bool {
453        self.mirrors.iter().any(|m| m.is_available())
454    }
455
456    pub fn set_max_connections_per_mirror(&mut self, max: usize) {
457        for m in &mut self.mirrors {
458            m.max_connections = max;
459        }
460    }
461
462    /// Set the maximum connections for a specific mirror.
463    ///
464    /// This is used for dynamic connection rebalancing based on mirror performance.
465    ///
466    /// # Arguments
467    ///
468    /// * `mirror_idx` - Index of the mirror
469    /// * `max` - New maximum connections for this mirror
470    pub fn set_mirror_max_connections(&mut self, mirror_idx: usize, max: usize) {
471        if let Some(mirror) = self.mirrors.get_mut(mirror_idx) {
472            mirror.max_connections = max;
473        }
474    }
475
476    /// Get the current maximum connections for a specific mirror.
477    ///
478    /// # Arguments
479    ///
480    /// * `mirror_idx` - Index of the mirror
481    pub fn get_mirror_max_connections(&self, mirror_idx: usize) -> Option<usize> {
482        self.mirrors.get(mirror_idx).map(|m| m.max_connections)
483    }
484
485    pub fn set_max_retries(&mut self, retries: u32) {
486        self.max_retries_per_segment = retries;
487    }
488
489    pub fn segment_retry_count(&self, seg_idx: u32) -> u32 {
490        self.segments
491            .iter()
492            .find(|s| s.index == seg_idx)
493            .map(|s| s.retry_count)
494            .unwrap_or(0)
495    }
496
497    pub fn has_permanently_failed_segments(&self) -> bool {
498        self.segments.iter().any(|s| {
499            s.status == SegmentStatus::Failed && s.retry_count >= self.max_retries_per_segment
500        })
501    }
502
503    pub fn mark_completed_up_to(&mut self, offset: u64, length: u64) {
504        let end_offset = offset + length;
505        for segment in &mut self.segments {
506            if segment.offset + segment.length <= offset {
507                if segment.status != SegmentStatus::Done {
508                    segment.status = SegmentStatus::Done;
509                    self.completed_bytes += segment.length;
510                }
511            } else if segment.offset < end_offset {
512                let overlap_start = std::cmp::max(segment.offset, offset);
513                let overlap_end = std::cmp::min(segment.offset + segment.length, end_offset);
514                if overlap_end > overlap_start {
515                    debug!(
516                        "段 {} 部分已完成: {}/{} bytes",
517                        segment.index,
518                        overlap_end - segment.offset,
519                        segment.length
520                    );
521                }
522            }
523        }
524    }
525
526    pub fn segment_info(&self, index: usize) -> Option<(u64, u64, &SegmentStatus)> {
527        self.segments
528            .get(index)
529            .map(|s| (s.offset, s.length, &s.status))
530    }
531
532    // ==================== Intelligent Mirror Selection Methods ====================
533
534    /// Select a mirror for the next pending segment using UriSelector.
535    ///
536    /// If a `UriSelector` is configured, this method uses it to intelligently
537    /// select the best mirror based on historical performance data. Otherwise,
538    /// it falls back to the first available mirror.
539    ///
540    /// # Returns
541    ///
542    /// * `Some((mirror_idx, segment_info))` - Mirror index and segment info (index, offset, length)
543    /// * `None` - No pending segments or no available mirrors
544    pub fn select_mirror_for_next_segment(&mut self) -> Option<(usize, (u32, u64, u64))> {
545        // Find a pending segment first
546        let pending_seg = self
547            .segments
548            .iter()
549            .find(|s| s.status == SegmentStatus::Pending)?;
550
551        let seg_index = pending_seg.index;
552
553        // Use UriSelector if available
554        if let Some(ref selector) = self.uri_selector {
555            // Build list of currently used hosts
556            let used_hosts: Vec<(usize, String)> = self
557                .segments
558                .iter()
559                .filter(|s| s.status == SegmentStatus::Downloading)
560                .filter_map(|s| {
561                    s.assigned_mirror.and_then(|idx| {
562                        self.mirror_urls.get(idx).map(|url| {
563                            // Extract host from URL
564                            let host = extract_host_from_url(url);
565                            (idx, host)
566                        })
567                    })
568                })
569                .collect();
570
571            // Select mirror using UriSelector
572            if let Some(mirror_idx) = selector.select(&self.mirror_urls, &used_hosts) {
573                // Check if mirror can accept more
574                if self
575                    .mirrors
576                    .get(mirror_idx)
577                    .is_some_and(|m| m.can_accept_more())
578                {
579                    // Assign segment to this mirror
580                    if let Some(seg) = self.segments.get_mut(seg_index as usize) {
581                        seg.status = SegmentStatus::Downloading;
582                        seg.assigned_mirror = Some(mirror_idx);
583                        if let Some(m) = self.mirrors.get_mut(mirror_idx) {
584                            m.active_segments += 1;
585                        }
586                        return Some((mirror_idx, (seg.index, seg.offset, seg.length)));
587                    }
588                }
589            }
590        }
591
592        // Fallback: find first available mirror
593        for mirror_idx in 0..self.mirrors.len() {
594            if self.mirrors[mirror_idx].can_accept_more()
595                && let Some(seg) = self.segments.get_mut(seg_index as usize)
596            {
597                seg.status = SegmentStatus::Downloading;
598                seg.assigned_mirror = Some(mirror_idx);
599                self.mirrors[mirror_idx].active_segments += 1;
600                return Some((mirror_idx, (seg.index, seg.offset, seg.length)));
601            }
602        }
603
604        None
605    }
606
607    /// Report a segment download completion with speed feedback.
608    ///
609    /// This method should be called after a segment is successfully downloaded.
610    /// It updates the server statistics with the measured download speed,
611    /// which affects future mirror selection decisions.
612    ///
613    /// # Arguments
614    ///
615    /// * `seg_idx` - Index of the completed segment
616    /// * `data` - Downloaded data
617    /// * `bytes_per_sec` - Measured download speed in bytes per second
618    /// * `is_multi_connection` - Whether this was a multi-connection download
619    ///
620    /// # Returns
621    ///
622    /// `true` if the segment was successfully marked as complete
623    pub fn report_segment_complete(
624        &mut self,
625        seg_idx: u32,
626        data: bytes::Bytes,
627        bytes_per_sec: u64,
628        is_multi_connection: bool,
629    ) -> bool {
630        // Get the mirror index before completing
631        let mirror_idx = self
632            .segments
633            .get(seg_idx as usize)
634            .and_then(|s| s.assigned_mirror);
635
636        // Complete the segment
637        let success = self.complete_segment(seg_idx, data);
638
639        // Update server stats if available
640        if success {
641            if let (Some(idx), Some(stat_man)) = (mirror_idx, &self.stat_man)
642                && let Some(url) = self.mirror_urls.get(idx)
643            {
644                let host = extract_host_from_url(url);
645                stat_man.update(&host, bytes_per_sec, is_multi_connection);
646
647                // Reset failure count on success
648                if let Some(stat) = stat_man.find_stat(&host) {
649                    stat.reset_status();
650                }
651            }
652
653            // Tune the selector with the new speed
654            if let Some(ref selector) = self.uri_selector {
655                selector.tune_command(&self.mirror_urls, bytes_per_sec);
656            }
657        }
658
659        success
660    }
661
662    /// Report a segment download failure.
663    ///
664    /// This method should be called when a segment download fails.
665    /// It updates the server statistics and may disable the mirror
666    /// if it has too many consecutive failures.
667    ///
668    /// # Arguments
669    ///
670    /// * `seg_idx` - Index of the failed segment
671    /// * `error_code` - HTTP error code (e.g., 500, 503) or 0 for network errors
672    ///
673    /// # Returns
674    ///
675    /// * `Some(new_mirror_idx)` - Segment reassigned to a new mirror
676    /// * `None` - Segment permanently failed or no alternative mirror
677    pub fn report_segment_failed(&mut self, seg_idx: u32, error_code: u16) -> Option<usize> {
678        // Get the mirror index before failing
679        let mirror_idx = self
680            .segments
681            .get(seg_idx as usize)
682            .and_then(|s| s.assigned_mirror);
683
684        // Fail the segment (this handles reassignment logic)
685        let reassign = self.fail_segment(seg_idx);
686
687        // Update server stats if available
688        if let (Some(idx), Some(stat_man)) = (mirror_idx, &self.stat_man)
689            && let Some(url) = self.mirror_urls.get(idx)
690        {
691            let host = extract_host_from_url(url);
692            // Ensure stat exists before marking failure
693            stat_man.get_or_create(&host);
694            stat_man.mark_failure(&host, error_code);
695
696            // Check if mirror should be disabled
697            if let Some(stat) = stat_man.find_stat(&host)
698                && !stat.is_available()
699            {
700                // Mirror is in cooldown, disable it temporarily
701                if let Some(m) = self.mirrors.get_mut(idx) {
702                    m.disabled = true;
703                }
704            }
705        }
706
707        reassign
708    }
709
710    /// Get the URL for a specific mirror index.
711    pub fn get_mirror_url(&self, mirror_idx: usize) -> Option<&str> {
712        self.mirror_urls.get(mirror_idx).map(|s| s.as_str())
713    }
714
715    /// Get the number of active segments for a specific mirror.
716    pub fn mirror_active_segments(&self, mirror_idx: usize) -> usize {
717        self.mirrors
718            .get(mirror_idx)
719            .map(|m| m.active_segments)
720            .unwrap_or(0)
721    }
722
723    /// Check if the manager is using intelligent mirror selection.
724    pub fn has_intelligent_selection(&self) -> bool {
725        self.uri_selector.is_some() && self.stat_man.is_some()
726    }
727}
728
729/// Extract host from URL (helper function).
730fn extract_host_from_url(url: &str) -> String {
731    let url = url.trim();
732    if !url.contains("://") {
733        return url.to_string();
734    }
735    let after_scheme = &url[url.find("://").unwrap() + 3..];
736    let host_part = if let Some(slash_idx) = after_scheme.find('/') {
737        &after_scheme[..slash_idx]
738    } else {
739        after_scheme
740    };
741    host_part.to_string()
742}
743
744#[cfg(test)]
745mod tests {
746    use super::*;
747
748    #[test]
749    fn test_manager_creation_small_file() {
750        let mgr = ConcurrentSegmentManager::new(1024, vec!["http://a.com/f".to_string()], None);
751        assert_eq!(mgr.num_segments(), 1);
752        assert_eq!(mgr.num_mirrors(), 1);
753        assert_eq!(mgr.total_size(), 1024);
754        assert!(!mgr.is_complete());
755        assert!(mgr.has_pending_segments());
756    }
757
758    #[test]
759    fn test_manager_large_file_multi_segment() {
760        let mgr = ConcurrentSegmentManager::new(
761            3_000_000,
762            vec!["http://a.com/f".to_string(), "http://b.com/f".to_string()],
763            Some(1_000_000),
764        );
765        assert_eq!(mgr.num_segments(), 3);
766        assert_eq!(mgr.num_mirrors(), 2);
767    }
768
769    #[test]
770    fn test_allocate_segments_round_robin() {
771        let mut mgr = ConcurrentSegmentManager::new(
772            3_000_000,
773            vec!["http://a.com/f".to_string(), "http://b.com/f".to_string()],
774            Some(1_000_000),
775        );
776
777        mgr.allocate_segments();
778
779        let assigned_a: Vec<_> = mgr
780            .segments
781            .iter()
782            .filter(|s| s.assigned_mirror == Some(0))
783            .map(|s| s.index)
784            .collect();
785        let assigned_b: Vec<_> = mgr
786            .segments
787            .iter()
788            .filter(|s| s.assigned_mirror == Some(1))
789            .map(|s| s.index)
790            .collect();
791
792        assert!(!assigned_a.is_empty());
793        assert!(!assigned_b.is_empty());
794        assert_eq!(assigned_a.len() + assigned_b.len(), 3);
795    }
796
797    #[test]
798    fn test_complete_and_assemble() {
799        let mut mgr =
800            ConcurrentSegmentManager::new(200, vec!["http://x.com/f".to_string()], Some(100));
801
802        mgr.allocate_segments();
803        assert_eq!(mgr.progress(), 0.0);
804
805        mgr.complete_segment(0, bytes::Bytes::from(vec![0xAB; 100]));
806        assert!(!mgr.is_complete());
807        assert!((mgr.progress() - 50.0).abs() < 0.01);
808
809        mgr.complete_segment(1, bytes::Bytes::from(vec![0xCD; 100]));
810        assert!(mgr.is_complete());
811        assert!((mgr.progress() - 100.0).abs() < 0.01);
812
813        let assembled = mgr.assemble().unwrap();
814        assert_eq!(assembled.len(), 200);
815        assert_eq!(&assembled[..100], &[0xAB; 100][..]);
816        assert_eq!(&assembled[100..], &[0xCD; 100][..]);
817    }
818
819    #[test]
820    fn test_fail_and_reassign() {
821        let mut mgr = ConcurrentSegmentManager::new(
822            200,
823            vec!["http://a.com/f".to_string(), "http://b.com/f".to_string()],
824            Some(100),
825        );
826
827        mgr.allocate_segments();
828
829        let reassign = mgr.fail_segment(0);
830        assert!(reassign.is_some());
831
832        let seg = &mgr.segments[0];
833        assert_eq!(seg.status, SegmentStatus::Pending);
834        assert_eq!(seg.assigned_mirror, reassign);
835        assert_eq!(seg.retry_count, 1);
836    }
837
838    #[test]
839    fn test_max_retries_exhausted() {
840        let mut mgr =
841            ConcurrentSegmentManager::new(100, vec!["http://a.com/f".to_string()], Some(100));
842        mgr.set_max_retries(2);
843
844        mgr.fail_segment(0);
845        assert!(mgr.has_pending_segments());
846
847        mgr.fail_segment(0);
848        assert!(mgr.has_failed_segments());
849        assert!(!mgr.has_pending_segments());
850    }
851
852    #[test]
853    fn test_empty_file() {
854        let mgr = ConcurrentSegmentManager::new(0, vec!["http://x.com/f".to_string()], None);
855        assert_eq!(mgr.num_segments(), 0);
856        assert!(mgr.is_complete());
857        assert!(mgr.assemble().is_none());
858    }
859
860    #[test]
861    fn test_next_pending_for_specific_mirror() {
862        let mut mgr = ConcurrentSegmentManager::new(
863            300,
864            vec!["http://a.com/f".to_string(), "http://b.com/f".to_string()],
865            Some(100),
866        );
867
868        let r = mgr.next_pending_segment_for_mirror(0);
869        assert!(r.is_some());
870        let (idx, off, len) = r.unwrap();
871        assert_eq!(idx, 0);
872        assert_eq!(off, 0);
873        assert_eq!(len, 100);
874
875        let r2 = mgr.next_pending_segment_for_mirror(1);
876        assert!(r2.is_some());
877        let (idx2, _, _) = r2.unwrap();
878        assert_eq!(idx2, 1);
879    }
880
881    // ======================================================================
882    // Tests for Intelligent Mirror Selection
883    // ======================================================================
884
885    #[test]
886    fn test_new_with_selector() {
887        use crate::selector::adaptive_uri_selector::AdaptiveUriSelector;
888
889        let stat_man = Arc::new(ServerStatMan::new());
890        let urls = vec![
891            "http://mirror1.com/file".to_string(),
892            "http://mirror2.com/file".to_string(),
893        ];
894        let selector = Box::new(AdaptiveUriSelector::new_with_uris(
895            Arc::clone(&stat_man),
896            urls.clone(),
897        ));
898
899        let mgr = ConcurrentSegmentManager::new_with_selector(
900            1_000_000,
901            urls,
902            Some(500_000),
903            stat_man,
904            selector,
905        );
906
907        assert_eq!(mgr.num_segments(), 2);
908        assert_eq!(mgr.num_mirrors(), 2);
909        assert!(mgr.has_intelligent_selection());
910    }
911
912    #[test]
913    fn test_select_mirror_for_next_segment_without_selector() {
914        let mut mgr = ConcurrentSegmentManager::new(
915            300,
916            vec!["http://a.com/f".to_string(), "http://b.com/f".to_string()],
917            Some(100),
918        );
919
920        // Without UriSelector, should use fallback
921        let result = mgr.select_mirror_for_next_segment();
922        assert!(result.is_some());
923
924        let (mirror_idx, (seg_idx, offset, len)) = result.unwrap();
925        assert_eq!(seg_idx, 0);
926        assert_eq!(offset, 0);
927        assert_eq!(len, 100);
928        assert!(mirror_idx < 2);
929    }
930
931    #[test]
932    fn test_select_mirror_for_next_segment_with_selector() {
933        use crate::selector::adaptive_uri_selector::AdaptiveUriSelector;
934
935        let stat_man = Arc::new(ServerStatMan::new());
936        let urls = vec![
937            "http://fast.com/f".to_string(),
938            "http://slow.com/f".to_string(),
939        ];
940
941        // Make fast.com have better stats
942        stat_man.update("fast.com", 1_000_000, false);
943        stat_man.update("slow.com", 1000, false);
944        let fast_stat = stat_man.find_stat("fast.com").unwrap();
945        fast_stat.increment_counter();
946        let slow_stat = stat_man.find_stat("slow.com").unwrap();
947        slow_stat.increment_counter();
948
949        let selector = Box::new(AdaptiveUriSelector::new_with_uris(
950            Arc::clone(&stat_man),
951            urls.clone(),
952        ));
953
954        let mut mgr =
955            ConcurrentSegmentManager::new_with_selector(300, urls, Some(100), stat_man, selector);
956
957        let result = mgr.select_mirror_for_next_segment();
958        assert!(result.is_some());
959
960        let (mirror_idx, _) = result.unwrap();
961        // Fast mirror (index 0) should be selected
962        assert_eq!(mirror_idx, 0);
963    }
964
965    #[test]
966    fn test_report_segment_complete_updates_stats() {
967        use crate::selector::adaptive_uri_selector::AdaptiveUriSelector;
968
969        let stat_man = Arc::new(ServerStatMan::new());
970        let urls = vec!["http://test.mirror.com/f".to_string()];
971
972        let selector = Box::new(AdaptiveUriSelector::new_with_uris(
973            Arc::clone(&stat_man),
974            urls.clone(),
975        ));
976
977        let mut mgr = ConcurrentSegmentManager::new_with_selector(
978            100,
979            urls,
980            Some(100),
981            stat_man.clone(),
982            selector,
983        );
984
985        mgr.allocate_segments();
986
987        // Report completion with 1 MB/s speed
988        let success =
989            mgr.report_segment_complete(0, bytes::Bytes::from(vec![0xAB; 100]), 1_000_000, false);
990        assert!(success);
991
992        // Check that stats were updated
993        let stat = stat_man.find_stat("test.mirror.com").unwrap();
994        assert!(stat.get_download_speed() > 0);
995    }
996
997    #[test]
998    fn test_report_segment_failed_updates_stats() {
999        use crate::selector::adaptive_uri_selector::AdaptiveUriSelector;
1000
1001        let stat_man = Arc::new(ServerStatMan::new());
1002        let urls = vec![
1003            "http://failing.mirror.com/f".to_string(),
1004            "http://backup.mirror.com/f".to_string(),
1005        ];
1006
1007        let selector = Box::new(AdaptiveUriSelector::new_with_uris(
1008            Arc::clone(&stat_man),
1009            urls.clone(),
1010        ));
1011
1012        let mut mgr = ConcurrentSegmentManager::new_with_selector(
1013            100,
1014            urls,
1015            Some(100),
1016            stat_man.clone(),
1017            selector,
1018        );
1019
1020        mgr.allocate_segments();
1021
1022        // Report failure
1023        let reassign = mgr.report_segment_failed(0, 503);
1024        assert!(reassign.is_some());
1025
1026        // Check that stats were updated
1027        let stat = stat_man.find_stat("failing.mirror.com").unwrap();
1028        assert_eq!(stat.get_consecutive_failures(), 1);
1029        assert_eq!(stat.get_last_error_code(), 503);
1030    }
1031
1032    #[test]
1033    fn test_extract_host_from_url() {
1034        assert_eq!(
1035            extract_host_from_url("http://example.com/path"),
1036            "example.com"
1037        );
1038        assert_eq!(
1039            extract_host_from_url("https://host:8080/file?q=1"),
1040            "host:8080"
1041        );
1042        assert_eq!(extract_host_from_url("ftp://server.com"), "server.com");
1043        assert_eq!(extract_host_from_url("not-a-url"), "not-a-url");
1044    }
1045
1046    #[test]
1047    fn test_get_mirror_url() {
1048        let mgr = ConcurrentSegmentManager::new(
1049            100,
1050            vec!["http://a.com/f".to_string(), "http://b.com/f".to_string()],
1051            Some(100),
1052        );
1053
1054        assert_eq!(mgr.get_mirror_url(0), Some("http://a.com/f"));
1055        assert_eq!(mgr.get_mirror_url(1), Some("http://b.com/f"));
1056        assert_eq!(mgr.get_mirror_url(999), None);
1057    }
1058
1059    #[test]
1060    fn test_mirror_active_segments() {
1061        let mut mgr =
1062            ConcurrentSegmentManager::new(300, vec!["http://a.com/f".to_string()], Some(100));
1063
1064        assert_eq!(mgr.mirror_active_segments(0), 0);
1065        assert_eq!(mgr.num_segments(), 3);
1066
1067        // Set max connections to allow all 3 segments
1068        mgr.set_max_connections_per_mirror(3);
1069
1070        mgr.allocate_segments();
1071        // After allocation, all 3 segments should be assigned to the single mirror
1072        assert_eq!(mgr.mirror_active_segments(0), 3);
1073    }
1074
1075    #[test]
1076    fn test_no_intelligent_selection_by_default() {
1077        let mgr = ConcurrentSegmentManager::new(100, vec!["http://a.com/f".to_string()], Some(100));
1078
1079        assert!(!mgr.has_intelligent_selection());
1080    }
1081
1082    // ======================================================================
1083    // Tests for atomic / lock-free segment allocation (Phase E1)
1084    // ======================================================================
1085
1086    /// Verify that `allocate_next_index` is lock-free and never issues a
1087    /// duplicate or missing index when hammered from many threads.
1088    ///
1089    /// 16 threads each call `allocate_next_index` 1000 times against a shared
1090    /// `Arc<ConcurrentSegmentManager>` (16000 segments of 1 byte each). Because
1091    /// `allocate_next_index` takes only `&self` and uses `fetch_add`, every call
1092    /// must receive a distinct index in `0..16000` with no duplicates and no gaps.
1093    #[tokio::test(flavor = "multi_thread", worker_threads = 16)]
1094    async fn test_segment_allocation_is_lock_free() {
1095        use std::collections::HashSet;
1096
1097        // 16000 segments of size 1 byte = 16000 segments.
1098        let manager = Arc::new(ConcurrentSegmentManager::new(
1099            16000,
1100            vec!["http://test".into()],
1101            Some(1),
1102        ));
1103        assert_eq!(manager.num_segments(), 16000);
1104
1105        let collected: Arc<tokio::sync::Mutex<Vec<u32>>> =
1106            Arc::new(tokio::sync::Mutex::new(Vec::new()));
1107
1108        let mut handles = Vec::with_capacity(16);
1109        for _ in 0..16 {
1110            let m = manager.clone();
1111            let c = collected.clone();
1112            handles.push(tokio::spawn(async move {
1113                // Collect locally first to minimize lock contention on `collected`.
1114                let mut local = Vec::with_capacity(1000);
1115                for _ in 0..1000 {
1116                    if let Some(idx) = m.allocate_next_index() {
1117                        local.push(idx);
1118                    }
1119                }
1120                c.lock().await.extend(local);
1121            }));
1122        }
1123
1124        for h in handles {
1125            h.await.unwrap();
1126        }
1127
1128        let indices = collected.lock().await.clone();
1129        assert_eq!(
1130            indices.len(),
1131            16000,
1132            "should have allocated all 16000 segments"
1133        );
1134
1135        // Verify no duplicates.
1136        let set: HashSet<u32> = indices.iter().copied().collect();
1137        assert_eq!(
1138            set.len(),
1139            16000,
1140            "all indices must be unique (no duplicates)"
1141        );
1142
1143        // Verify all indices 0..16000 are present.
1144        for i in 0..16000u32 {
1145            assert!(set.contains(&i), "missing index {}", i);
1146        }
1147    }
1148
1149    /// Verify the allocation hint advances indices in order and that
1150    /// `reset_allocation_index` rewinds the scan start position.
1151    ///
1152    /// The hint optimization must (a) return segments in ascending index order
1153    /// without re-scanning from 0 each call, (b) use wraparound to find a
1154    /// Pending segment that lies behind the current hint, and (c) be rewound
1155    /// to 0 by `reset_allocation_index`.
1156    #[test]
1157    fn test_allocation_hint_advances_and_resets() {
1158        let mut mgr =
1159            ConcurrentSegmentManager::new(500, vec!["http://a.com/f".to_string()], Some(100));
1160        // 5 segments; let the single mirror accept all of them.
1161        mgr.set_max_connections_per_mirror(10);
1162        assert_eq!(mgr.num_segments(), 5);
1163
1164        // Claim segments one at a time. The hint advances so each allocation
1165        // starts scanning right after the last claim, yielding indices 0..5
1166        // in order without re-scanning already-assigned segments.
1167        let mut claimed = Vec::new();
1168        while let Some((idx, _, _)) = mgr.next_pending_segment_for_mirror(0) {
1169            claimed.push(idx);
1170        }
1171        assert_eq!(claimed, vec![0, 1, 2, 3, 4]);
1172
1173        // All segments are Downloading; no pending segment remains.
1174        assert!(mgr.next_pending_segment_for_mirror(0).is_none());
1175
1176        // Simulate a retry: re-mark segment 1 as Pending. The hint currently
1177        // points past the end (5 -> wraps to 0), so the wraparound scan must
1178        // visit index 1 to find it.
1179        mgr.segments[1].status = SegmentStatus::Pending;
1180        let next = mgr.next_pending_segment_for_mirror(0);
1181        assert!(next.is_some());
1182        assert_eq!(next.unwrap().0, 1);
1183
1184        // reset_allocation_index rewinds the hint to 0 without touching statuses.
1185        mgr.reset_allocation_index();
1186        // Mark segment 3 as Pending; with the hint rewound to 0 the scan walks
1187        // forward from index 0 and finds segment 3 (the only Pending one).
1188        mgr.segments[3].status = SegmentStatus::Pending;
1189        let next = mgr.next_pending_segment_for_mirror(0);
1190        assert!(next.is_some());
1191        assert_eq!(next.unwrap().0, 3);
1192    }
1193}