fix: Replace HashSet with VecDeque for recent blocks cache

Problem:
- HashSet doesn't maintain insertion order
- iter().next() returns arbitrary element, not the oldest
- Cache eviction was unpredictable

Solution:
- Use VecDeque for FIFO ordering
- push_back() adds new block
- pop_front() removes oldest block when cache exceeds 10 elements
- contains() is O(n) but performant for 10 elements

Changes:
- RadioParadiseStreamSourceLogic: recent_blocks now VecDeque<EventId>
- mark_block_downloaded(): simplified with guaranteed FIFO eviction
- Documentation updated with VecDeque usage and advantages
This commit is contained in:
Claude
2025-11-05 05:31:43 +00:00
parent 1d8bdfa30c
commit c2b78fa040
2 changed files with 17 additions and 17 deletions

View File

@@ -20,7 +20,7 @@ RadioParadiseStreamSource (wrapper)
Responsabilités :
- **File d'attente** : `VecDeque<EventId>` pour les blocks à télécharger
- **Cache anti-redondance** : `HashSet<EventId>` pour 10 blocs récents
- **Cache anti-redondance** : `VecDeque<EventId>` pour 10 blocs récents (FIFO)
- **Téléchargement** : Fetch bloc FLAC (bitrate=4 uniquement)
- **Décodage** : Stream FLAC via `pmoflac::decode_audio_stream`
- **Timing** : Calcul précis pour insertion TrackBoundary
@@ -36,7 +36,7 @@ Responsabilités :
┌─────────────────────────────────────────────────────────┐
│ 2. Vérification cache │
│ └─> HashSet::contains(&event_id)
│ └─> VecDeque::contains(&event_id) │
└─────────────────────────────────────────────────────────┘
@@ -118,17 +118,20 @@ AudioSegment::new_audio(42, AudioChunk::I16(...))
const RECENT_BLOCKS_CACHE_SIZE: usize = 10;
fn mark_block_downloaded(&mut self, event_id: EventId) {
self.recent_blocks.insert(event_id);
self.recent_blocks.push_back(event_id);
// Limiter la taille du cache : retirer le plus ancien
if self.recent_blocks.len() > RECENT_BLOCKS_CACHE_SIZE {
// Retirer un élément (ordre non garanti avec HashSet)
if let Some(&first) = self.recent_blocks.iter().next() {
self.recent_blocks.remove(&first);
}
self.recent_blocks.pop_front();
}
}
```
**Avantages VecDeque** :
- ✅ Ordre FIFO garanti (le plus ancien est toujours retiré)
- ✅ Simple et prévisible
- ✅ Pour 10 éléments, `contains()` en O(n) reste très performant
## Support FLAC
### Formats supportés

View File

@@ -17,7 +17,7 @@ use pmoaudio::{
use pmoflac::decode_audio_stream;
use pmometadata::{MemoryTrackMetadata, TrackMetadata};
use std::{
collections::{HashSet, VecDeque},
collections::VecDeque,
sync::Arc,
time::Duration,
};
@@ -38,7 +38,7 @@ const RECENT_BLOCKS_CACHE_SIZE: usize = 10;
pub struct RadioParadiseStreamSourceLogic {
client: RadioParadiseClient,
chunk_frames: usize,
recent_blocks: HashSet<EventId>,
recent_blocks: VecDeque<EventId>,
block_queue: VecDeque<EventId>,
}
@@ -50,7 +50,7 @@ impl RadioParadiseStreamSourceLogic {
Self {
client,
chunk_frames,
recent_blocks: HashSet::with_capacity(RECENT_BLOCKS_CACHE_SIZE),
recent_blocks: VecDeque::with_capacity(RECENT_BLOCKS_CACHE_SIZE),
block_queue: VecDeque::new(),
}
}
@@ -65,16 +65,13 @@ impl RadioParadiseStreamSourceLogic {
self.recent_blocks.contains(&event_id)
}
/// Marque un bloc comme récemment téléchargé
/// Marque un bloc comme récemment téléchargé (FIFO)
fn mark_block_downloaded(&mut self, event_id: EventId) {
self.recent_blocks.insert(event_id);
self.recent_blocks.push_back(event_id);
// Limiter la taille du cache
// Limiter la taille du cache : retirer le plus ancien
if self.recent_blocks.len() > RECENT_BLOCKS_CACHE_SIZE {
// Retirer un élément (HashSet n'a pas d'ordre, donc on retire le premier)
if let Some(&first) = self.recent_blocks.iter().next() {
self.recent_blocks.remove(&first);
}
self.recent_blocks.pop_front();
}
}