<commit_before>use std::error;
use std::fs;
use std::io;
use std::io::{Read,Write};
use std::mem;
use std::str;
use std::path::Path;

use fat::RootEntry;
use fat::BIOSParam;

// Always the same
const SECTORS_PER_ROOT: usize = 14;
const BYTES_PER_ROOT_ENTRY: usize = 32;

#[test]
fn test_root_entry_size() {
    assert_eq!(mem::size_of::<RootEntry>(), BYTES_PER_ROOT_ENTRY);
}

#[derive(Debug)]
pub struct Image {
    boot_sector: Vec<u8>,
    fat_1: Vec<u8>,
    fat_2: Vec<u8>,
    root_dir: Vec<u8>,
    data_area: Vec<u8>,
    bpb_data: BIOSParam,
}

#[allow(dead_code)]
impl Image {
    /// Create a new blank FAT Image from a defined BPB
    fn new(bpb: BIOSParam, length: usize) -> Image {
        let boot_sector_size = bpb.bytes_per_sector as usize * bpb.reserved_sectors as usize;
        let bytes_per_fat = (bpb.sectors_per_fat * bpb.bytes_per_sector as u32) as usize;
        let bytes_per_root = SECTORS_PER_ROOT * bpb.bytes_per_sector as usize;
        let data_offset = boot_sector_size as usize + (bytes_per_fat * 2) as usize + bytes_per_root as usize;
        let bytes_per_data_area = length - data_offset;
        Image {
            boot_sector: vec![0; boot_sector_size],
            fat_1: vec![0; bytes_per_fat],
            fat_2: vec![0; bytes_per_fat],
            root_dir: vec![0; bytes_per_root],
            data_area: vec![0; bytes_per_data_area],
            bpb_data: bpb,
        }
    }

    /// Create a new FAT Image from the specified file.
    pub fn from_file<P: AsRef<Path>>(p: P)
        -> Result<Image, Box<error::Error>>
    {
        let metadata = fs::metadata(p.as_ref())?;
        let bpb = BIOSParam::from_file(p.as_ref())?;

        let mut file = fs::File::open(p.as_ref())?;
        let mut image = Image::new(bpb, metadata.len() as usize);

        try!(file.read_exact(&mut image.boot_sector));
        try!(file.read_exact(&mut image.fat_1));
        try!(file.read_exact(&mut image.fat_2));
        try!(file.read_exact(&mut image.root_dir));
        try!(file.read_exact(&mut image.data_area));

        Ok(image)
    }

    /// Save the FAT filesystem image to the specified file.
    pub fn save(&self, image_fn: String)
        -> Result<(), io::Error>
    {
        let mut file = fs::File::create(image_fn)?;

        try!(file.write_all(&self.boot_sector));
        try!(file.write_all(&self.fat_1));
        try!(file.write_all(&self.fat_2));
        try!(file.write_all(&self.root_dir));
        try!(file.write_all(&self.data_area));

        Ok(())
    }

    /// Extract the BIOS Parameter Block (BPB) from the FAT filesystem.
    pub fn bios_parameter(&self) -> BIOSParam {
        self.bpb_data.clone()
    }

    /// FAT sector size in bytes.
    pub fn sector_size(&self) -> usize {
        self.bpb_data.bytes_per_sector as usize
    }

    pub fn volume_label(&self) -> Result<String, Box<error::Error>> {
        let entries = self.root_entries();
        for entry in entries {
            if !entry.is_volume_label() {
                continue;
            }
            let label = format!("{}{}",
                str::from_utf8(&entry.filename)?,
                str::from_utf8(&entry.extension)?);
            return Ok(label);
        }
        return Ok("has no label".to_string());
    }

    // TODO: Make this an iterator
    pub fn root_entries(&self) -> Vec<RootEntry> {
        self.root_dir
            .chunks(BYTES_PER_ROOT_ENTRY)
            .map(|chunk| {
                let mut entry_bytes = [0; BYTES_PER_ROOT_ENTRY];
                entry_bytes.clone_from_slice(chunk);

                let entry: RootEntry;
                unsafe { entry = mem::transmute(entry_bytes); }
                entry
            })
            .filter(|entry| { entry.filename[0] != 0xe5 })
            .take_while(|entry| { entry.filename[0] != 0 })
            .collect::<Vec<RootEntry>>()
    }

    /// Get the RootEntry for the specified file within the Image.
    pub fn get_file_entry(&self, filename: String)
        -> Result<RootEntry, Box<error::Error>>
    {
        let entries = self.root_entries();
        for entry in entries {
            let entry_name: String;
            match entry.filename() {
                Ok(name) => entry_name = name,
                Err(_) => continue,
            }

            if entry_name.to_lowercase() == filename.to_lowercase() {
                return Ok(entry);
            }
        }

        Err(From::from(format!("file {} not found", filename)))
    }

    /// Create a new RootEntry within the Image with the specified filename.
    pub fn create_file_entry(&self, filename: String)
        -> Result<(RootEntry, u16), Box<error::Error>>
    {
        for (index, e) in self.root_entries().iter().enumerate() {
            if !e.is_free() { continue; }

            let mut entry = RootEntry::new();
            try!(entry.set_filename(filename));
            return Ok((entry.clone(), index as u16));
        }

        Err(From::from("no free entries"))
    }

    pub fn save_file_entry(&mut self, entry: RootEntry, index: u16)
        -> Result<(), Box<error::Error>>
    {<commit_msg>image: Fix no free root entries on add<commit_after>use std::error;
use std::fs;
use std::io;
use std::io::{Read,Write};
use std::mem;
use std::str;
use std::path::Path;

use fat::RootEntry;
use fat::BIOSParam;

// Always the same
const SECTORS_PER_ROOT: usize = 14;
const BYTES_PER_ROOT_ENTRY: usize = 32;

#[test]
fn test_root_entry_size() {
    assert_eq!(mem::size_of::<RootEntry>(), BYTES_PER_ROOT_ENTRY);
}

#[derive(Debug)]
pub struct Image {
    boot_sector: Vec<u8>,
    fat_1: Vec<u8>,
    fat_2: Vec<u8>,
    root_dir: Vec<u8>,
    data_area: Vec<u8>,
    bpb_data: BIOSParam,
}

#[allow(dead_code)]
impl Image {
    /// Create a new blank FAT Image from a defined BPB
    fn new(bpb: BIOSParam, length: usize) -> Image {
        let boot_sector_size = bpb.bytes_per_sector as usize * bpb.reserved_sectors as usize;
        let bytes_per_fat = (bpb.sectors_per_fat * bpb.bytes_per_sector as u32) as usize;
        let bytes_per_root = SECTORS_PER_ROOT * bpb.bytes_per_sector as usize;
        let data_offset = boot_sector_size as usize + (bytes_per_fat * 2) as usize + bytes_per_root as usize;
        let bytes_per_data_area = length - data_offset;
        Image {
            boot_sector: vec![0; boot_sector_size],
            fat_1: vec![0; bytes_per_fat],
            fat_2: vec![0; bytes_per_fat],
            root_dir: vec![0; bytes_per_root],
            data_area: vec![0; bytes_per_data_area],
            bpb_data: bpb,
        }
    }

    /// Create a new FAT Image from the specified file.
    pub fn from_file<P: AsRef<Path>>(p: P)
        -> Result<Image, Box<error::Error>>
    {
        let metadata = fs::metadata(p.as_ref())?;
        let bpb = BIOSParam::from_file(p.as_ref())?;

        let mut file = fs::File::open(p.as_ref())?;
        let mut image = Image::new(bpb, metadata.len() as usize);

        try!(file.read_exact(&mut image.boot_sector));
        try!(file.read_exact(&mut image.fat_1));
        try!(file.read_exact(&mut image.fat_2));
        try!(file.read_exact(&mut image.root_dir));
        try!(file.read_exact(&mut image.data_area));

        Ok(image)
    }

    /// Save the FAT filesystem image to the specified file.
    pub fn save(&self, image_fn: String)
        -> Result<(), io::Error>
    {
        let mut file = fs::File::create(image_fn)?;

        try!(file.write_all(&self.boot_sector));
        try!(file.write_all(&self.fat_1));
        try!(file.write_all(&self.fat_2));
        try!(file.write_all(&self.root_dir));
        try!(file.write_all(&self.data_area));

        Ok(())
    }

    /// Extract the BIOS Parameter Block (BPB) from the FAT filesystem.
    pub fn bios_parameter(&self) -> BIOSParam {
        self.bpb_data.clone()
    }

    /// FAT sector size in bytes.
    pub fn sector_size(&self) -> usize {
        self.bpb_data.bytes_per_sector as usize
    }

    /// FAT volume label
    pub fn volume_label(&self) -> Result<String, Box<error::Error>> {
        let entries = self.root_entries();
        for entry in entries {
            if !entry.is_volume_label() {
                continue;
            }
            let label = format!("{}{}",
                str::from_utf8(&entry.filename)?,
                str::from_utf8(&entry.extension)?);
            return Ok(label);
        }
        return Ok("has no label".to_string());
    }

    // TODO: Make iterator once "impl Trait" is stable.
    /// Return all FAT root entries (including unused)
    pub fn root_entries_all(&self) -> Vec<RootEntry> {
        self.root_dir
            .chunks(BYTES_PER_ROOT_ENTRY)
            .map(|chunk| {
                let mut entry_bytes = [0; BYTES_PER_ROOT_ENTRY];
                entry_bytes.clone_from_slice(chunk);

                let entry: RootEntry;
                unsafe { entry = mem::transmute(entry_bytes); }
                entry
            })
            .collect::<Vec<RootEntry>>()
    }

    // TODO: Make iterator once "impl Trait" is stable.
    /// Return used FAT root entries
    pub fn root_entries(&self) -> Vec<RootEntry> {
        self.root_dir
            .chunks(BYTES_PER_ROOT_ENTRY)
            .map(|chunk| {
                let mut entry_bytes = [0; BYTES_PER_ROOT_ENTRY];
                entry_bytes.clone_from_slice(chunk);

                let entry: RootEntry;
                unsafe { entry = mem::transmute(entry_bytes); }
                entry
            })
            .filter(|entry| { entry.filename[0] != 0xe5 })
            .take_while(|entry| { entry.filename[0] != 0 })
            .collect::<Vec<RootEntry>>()
    }

    /// Get the RootEntry for the specified file within the Image.
    pub fn get_file_entry(&self, filename: String)
        -> Result<RootEntry, Box<error::Error>>
    {
        let entries = self.root_entries();
        for entry in entries {
            let entry_name: String;
            match entry.filename() {
                Ok(name) => entry_name = name,
                Err(_) => continue,
            }

            if entry_name.to_lowercase() == filename.to_lowercase() {
                return Ok(entry);
            }
        }

        Err(From::from(format!("file {} not found", filename)))
    }

    /// Create a new RootEntry within the Image with the specified filename.
    pub fn create_file_entry(&self, filename: String)
        -> Result<(RootEntry, u16), Box<error::Error>>
    {
        for (index, e) in self.root_entries_all().iter().enumerate() {
            if !e.is_free() { continue; }

            let mut entry = RootEntry::new();
            try!(entry.set_filename(filename));
            return Ok((entry.clone(), index as u16));
        }

        Err(From::from("no free entries"))
    }

    pub fn save_file_entry(&mut self, entry: RootEntry, index: u16)
        -> Result<(), Box<error::Error>>
    {
{'fraction_non_alphanumeric': 0.13399798774352878, 'fraction_numerical': 0.007225830055794384, 'mean_word_length': 2.226320448509885, 'pattern_counts': {'":': 0, '<': 50, '<?xml version=': 0, '>': 75, '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': '758777', 'n_tokens_mistral': 3580, 'n_tokens_neox': 3462, 'n_words': 800}