stormdl-segment 0.1.2

Segment manager with adaptive splitting algorithm
Documentation
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{Duration, Instant};

pub struct AdaptiveController {
    current_segments: AtomicUsize,
    max_segments: usize,
    min_segment_size: u64,
    file_size: u64,
    last_adjustment: parking_lot::Mutex<Instant>,
    adjustment_interval: Duration,
}

impl AdaptiveController {
    pub fn new(file_size: u64, initial_segments: usize) -> Self {
        Self {
            current_segments: AtomicUsize::new(initial_segments),
            max_segments: 32,
            min_segment_size: 256 * 1024,
            file_size,
            last_adjustment: parking_lot::Mutex::new(Instant::now()),
            adjustment_interval: Duration::from_millis(500),
        }
    }

    pub fn with_config(
        file_size: u64,
        initial_segments: usize,
        max_segments: usize,
        min_segment_size: u64,
    ) -> Self {
        Self {
            current_segments: AtomicUsize::new(initial_segments),
            max_segments,
            min_segment_size,
            file_size,
            last_adjustment: parking_lot::Mutex::new(Instant::now()),
            adjustment_interval: Duration::from_millis(500),
        }
    }

    pub fn current_segments(&self) -> usize {
        self.current_segments.load(Ordering::Relaxed)
    }

    pub fn evaluate(&self, bdp: Option<u64>, _current_speed: f64) -> Option<SegmentAdjustment> {
        let mut last = self.last_adjustment.lock();
        if last.elapsed() < self.adjustment_interval {
            return None;
        }

        let current = self.current_segments.load(Ordering::Relaxed);
        let bdp = bdp?;

        let tcp_window = 65536u64;
        let optimal = ((bdp as f64) / (tcp_window as f64)).ceil() as usize;
        let optimal = optimal.clamp(1, self.max_segments);

        if optimal <= current {
            return None;
        }

        let remaining_segments = optimal - current;
        let segments_to_add = remaining_segments.min(4);

        let avg_segment_size = self.file_size / (current + segments_to_add) as u64;
        if avg_segment_size < self.min_segment_size {
            return None;
        }

        *last = Instant::now();
        self.current_segments
            .store(current + segments_to_add, Ordering::Relaxed);

        Some(SegmentAdjustment::Split {
            count: segments_to_add,
            reason: AdjustmentReason::BdpIncrease { bdp, optimal },
        })
    }

    pub fn should_split_slow_segment(
        &self,
        segment_speed: f64,
        avg_speed: f64,
        remaining_bytes: u64,
    ) -> bool {
        if remaining_bytes < self.min_segment_size * 2 {
            return false;
        }

        let current = self.current_segments.load(Ordering::Relaxed);
        if current >= self.max_segments {
            return false;
        }

        let threshold = avg_speed * 0.3;
        segment_speed > 0.0 && segment_speed < threshold
    }

    pub fn record_split(&self) {
        self.current_segments.fetch_add(1, Ordering::Relaxed);
    }
}

#[derive(Debug, Clone)]
pub enum SegmentAdjustment {
    Split {
        count: usize,
        reason: AdjustmentReason,
    },
    #[allow(dead_code)]
    Merge {
        count: usize,
        reason: AdjustmentReason,
    },
}

#[derive(Debug, Clone)]
pub enum AdjustmentReason {
    BdpIncrease {
        bdp: u64,
        optimal: usize,
    },
    #[allow(dead_code)]
    SlowSegment {
        speed: f64,
        threshold: f64,
    },
    #[allow(dead_code)]
    ConnectionLimit,
}