<commit_before>type LineNumber = usize;

struct Line {
    sector: disk::Sector,
    data: Box<[u8]>,
    dirty: bool,
    flush_dependencies: Vec<LineNumber>,
}

impl Line {
    fn reset(&mut self) {
        // The cache line starts out as clean...
        self.dirty = false;
        // ...and hence has no dependencies.
        self.flush_dependencies.clear();
    }
}

struct WriteQuery<'a> {
    sector: disk::Sector,
    data: &'a [u8],
}

struct Cached<D> {
    disk: D,
    cache_tracker: plru::DynamicCache,
    line_map: HashMap<disk::Sector, LineNumber>,
    lines: Vec<[Line]>,
}

impl<D: Disk> Cached<D> {
    fn alloc_line(&mut self) -> LineNumber {
        // Test if the cache is filled.
        if self.lines.len() < self.cache_tracker.len() {
            // The cache is not filled, so we don't need to replace any cache line, we can simply
            // push.
            self.lines.push(Line {
                sector: sector,
                data: vec![0; self.disk.sector_size()],
                dirty: false,
                flush_dependencies: Vec::new(),
            });

            self.lines.len() - 1
        } else {
            // Find a candidate for replacement.
            let line_number = self.cache_tracker.replace();

            // Flush it to the disk before throwing the data away.
            self.flush(line_number);

            // Remove the old sector from the cache line map.
            let line = &mut self.lines[line_number];
            self.line_map.remove(line.sector);

            // Reset the cache line.
            line.reset();

            line_number
        }
    }

    fn fetch_fresh(&mut self, sector: disk::Sector) -> Result<&mut Line, disk::Error> {
        // Allocate a new cache line.
        let line_number = self.alloc_line();
        let line = &mut self.lines[line_number];

        // Read the sector from the disk.
        self.disk.read(sector, &mut line.data)?;

        // Update the sector number.
        line.sector = sector;

        // Update the cache line map with the new line.
        self.line_map.insert(sector, line_number);
    }

    fn write(&mut self, write: WriteQuery) -> LineNumber {
        // Allocate a new cache line.
        let line_number = self.alloc_line();
        let line = &mut self.lines[line_number];

        // Copy the data into the freshly allocated cache line.
        line.data.copy_from_slice(data);

        // Update the sector number.
        line.sector = sector;

        // Update the cache line map with the new line.
        self.line_map.insert(sector, line_number);

        line
    }

    fn write_seq<I: Iterator<Item = WriteQuery>>(&mut self, writes: I) {
        // Execute the first query and store the number of the cache line in which it is written.
        let mut prev_line_number = self.write(writes.next().unwrap());
        // Execute the rest of the queries.
        for query in writes {<commit_msg>Make sure the cache line is marked dirty after writes.<commit_after>type LineNumber = usize;

struct Line {
    sector: disk::Sector,
    data: Box<[u8]>,
    dirty: bool,
    flush_dependencies: Vec<LineNumber>,
}

impl Line {
    fn reset(&mut self) {
        // The cache line starts out as clean...
        self.dirty = false;
        // ...and hence has no dependencies.
        self.flush_dependencies.clear();
    }
}

struct WriteQuery<'a> {
    sector: disk::Sector,
    data: &'a [u8],
}

struct Cached<D> {
    disk: D,
    cache_tracker: plru::DynamicCache,
    line_map: RwLock<HashMap<disk::Sector, LineNumber>>,
    lines: RwLock<Vec<[RwLock<Line>]>>,
}

impl<D: Disk> Cached<D> {
    fn alloc_line(&mut self) -> LineNumber {
        // Test if the cache is filled.
        if self.lines.len() < self.cache_tracker.len() {
            // The cache is not filled, so we don't need to replace any cache line, we can simply
            // push.
            self.lines.push(Line {
                sector: sector,
                data: vec![0; self.disk.sector_size()],
                dirty: false,
                flush_dependencies: Vec::new(),
            });

            self.lines.len() - 1
        } else {
            // Find a candidate for replacement.
            let line_number = self.cache_tracker.replace();

            // Flush it to the disk before throwing the data away.
            self.flush(line_number);

            // Remove the old sector from the cache line map.
            let line = &mut self.lines[line_number];
            self.line_map.remove(line.sector);

            // Reset the cache line.
            line.reset();

            line_number
        }
    }

    fn fetch_fresh(&mut self, sector: disk::Sector) -> Result<&mut Line, disk::Error> {
        // Allocate a new cache line.
        let line_number = self.alloc_line();
        let line = &mut self.lines[line_number];

        // Read the sector from the disk.
        self.disk.read(sector, &mut line.data)?;

        // Update the sector number.
        line.sector = sector;

        // Update the cache line map with the new line.
        self.line_map.insert(sector, line_number);
    }

    fn write(&mut self, write: WriteQuery) -> LineNumber {
        // Allocate a new cache line.
        let line_number = self.alloc_line();
        let line = &mut self.lines[line_number];

        // Copy the data into the freshly allocated cache line.
        line.data.copy_from_slice(data);

        // Update the sector number.
        line.sector = sector;
        // Mark dirty.
        line.dirty = true;

        // Update the cache line map with the new line.
        self.line_map.insert(sector, line_number);

        line
    }

    fn write_seq<I: Iterator<Item = WriteQuery>>(&mut self, writes: I) {
        // Execute the first query and store the number of the cache line in which it is written.
        let mut prev_line_number = self.write(writes.next().unwrap());
        // Execute the rest of the queries.
        for query in writes {
{'fraction_non_alphanumeric': 0.11133300049925113, 'fraction_numerical': 0.0013313363288400732, 'mean_word_length': 1.8963855421686746, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 34, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '179871', 'n_tokens_mistral': 1775, 'n_tokens_neox': 1714, 'n_words': 547}