1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
use std::collections::{HashMap, VecDeque};
use std::{cmp, ptr};
use syscall::error::Result;

use disk::Disk;
use BLOCK_SIZE;

fn copy_memory(src: &[u8], dest: &mut [u8]) -> usize {
    let len = cmp::min(src.len(), dest.len());
    unsafe { ptr::copy(src.as_ptr(), dest.as_mut_ptr(), len) };
    len
}

pub struct DiskCache<T> {
    inner: T,
    cache: HashMap<u64, [u8; BLOCK_SIZE as usize]>,
    order: VecDeque<u64>,
    size: usize,
}

impl<T: Disk> DiskCache<T> {
    pub fn new(inner: T) -> Self {
        DiskCache {
            inner: inner,
            cache: HashMap::new(),
            order: VecDeque::new(),
            size: 65536, // 256 MB cache
        }
    }

    fn insert(&mut self, i: u64, data: [u8; BLOCK_SIZE as usize]) {
        while self.order.len() >= self.size {
            let removed = self.order.pop_front().unwrap();
            self.cache.remove(&removed);
        }

        self.cache.insert(i, data);
        self.order.push_back(i);
    }
}

impl<T: Disk> Disk for DiskCache<T> {
    fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
        // println!("Cache read at {}", block);

        let mut read = 0;
        let mut failed = false;
        for i in 0..(buffer.len() + BLOCK_SIZE as usize - 1) / (BLOCK_SIZE as usize) {
            let block_i = block + i as u64;

            let buffer_i = i * BLOCK_SIZE as usize;
            let buffer_j = cmp::min(buffer_i + BLOCK_SIZE as usize, buffer.len());
            let buffer_slice = &mut buffer[buffer_i..buffer_j];

            if let Some(cache_buf) = self.cache.get_mut(&block_i) {
                read += copy_memory(cache_buf, buffer_slice);
            } else {
                failed = true;
                break;
            }
        }

        if failed {
            self.inner.read_at(block, buffer)?;

            read = 0;
            for i in 0..(buffer.len() + BLOCK_SIZE as usize - 1) / (BLOCK_SIZE as usize) {
                let block_i = block + i as u64;

                let buffer_i = i * BLOCK_SIZE as usize;
                let buffer_j = cmp::min(buffer_i + BLOCK_SIZE as usize, buffer.len());
                let buffer_slice = &buffer[buffer_i..buffer_j];

                let mut cache_buf = [0; BLOCK_SIZE as usize];
                read += copy_memory(buffer_slice, &mut cache_buf);
                self.insert(block_i, cache_buf);
            }
        }

        Ok(read)
    }

    fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
        //TODO: Write only blocks that have changed
        // println!("Cache write at {}", block);

        self.inner.write_at(block, buffer)?;

        let mut written = 0;
        for i in 0..(buffer.len() + BLOCK_SIZE as usize - 1) / (BLOCK_SIZE as usize) {
            let block_i = block + i as u64;

            let buffer_i = i * BLOCK_SIZE as usize;
            let buffer_j = cmp::min(buffer_i + BLOCK_SIZE as usize, buffer.len());
            let buffer_slice = &buffer[buffer_i..buffer_j];

            let mut cache_buf = [0; BLOCK_SIZE as usize];
            written += copy_memory(buffer_slice, &mut cache_buf);
            self.insert(block_i, cache_buf);
        }

        Ok(written)
    }

    fn size(&mut self) -> Result<u64> {
        self.inner.size()
    }
}