<commit_before>
use byteorder::{ByteOrder, LittleEndian};

use std;

use gameboy;
use gameboy::{Interconnect, Interrupt, Irq, Memory};
use gameboy::frame::{Color, Frame};

const VRAM_SIZE: usize = 0x4000;
const SPRITE_DATA_SIZE: usize = 0xA0;

pub struct GpuStat {
    pub coincidence_interrupt_enabled: bool,
    pub OAM_interrupt_enabled: bool,
    pub VBlank_interrupt_enabled: bool,
    pub HBlank_interrupt_enabled: bool,
}

impl GpuStat {
    pub fn new() -> GpuStat {
        GpuStat {
            coincidence_interrupt_enabled: false,
            OAM_interrupt_enabled: false,
            VBlank_interrupt_enabled: false,
            HBlank_interrupt_enabled: false,
        }
    }

    pub fn from_u8(b: u8) -> GpuStat {
        GpuStat {
            coincidence_interrupt_enabled: b & 0x40 == 0x40,
            OAM_interrupt_enabled: b & 0x20 == 0x20,
            VBlank_interrupt_enabled: b & 0x10 == 0x10,
            HBlank_interrupt_enabled: b & 0x08 == 0x08,
        }
    }

    pub fn to_u8(&self, gpu: &Gpu) -> u8 {
        (if self.coincidence_interrupt_enabled {
            0x40
        } else {
            0
        }) | (if self.OAM_interrupt_enabled { 0x20 } else { 0 }) |
        (if self.VBlank_interrupt_enabled {
            0x10
        } else {
            0
        }) |
        (if self.HBlank_interrupt_enabled {
            0x08
        } else {
            0
        }) | (if gpu.ly == gpu.lyc { 0x04 } else { 0 }) | gpu.mode.to_u8()
    }
}

#[derive(Eq, PartialEq, Clone)]
pub enum GpuMode {
    HBlank,
    VBlank,
    SearchingRam,
    TransferringData,
}

impl GpuMode {
    fn to_u8(&self) -> u8 {
        match *self {
            GpuMode::HBlank => 0x00,
            GpuMode::VBlank => 0x01,
            GpuMode::SearchingRam => 0x02,
            GpuMode::TransferringData => 0x03,
        }
    }
}

pub enum SpriteShape {
    Square,
    Rectangle,
}

pub struct Gpu {
    pub enabled: bool,
    pub ram: Memory,
    pub sprite_data: Memory,
    control_register: u8,
    stat: GpuStat,
    scroll_y: u8,
    scroll_x: u8,
    window_y: u8,
    window_x: u8,
    pub ly: u8,
    lyc: u8,
    bg_palette: u8,
    palette0: u8,
    palette1: u8,

    cycles: isize,

    pub frame: Frame,
    pub backbuffer: Frame,
    mode: GpuMode,

    counter: u8,
    tile_base: usize,
    background_base: usize,
    sprite_shape: SpriteShape,
    sprites_enabled: bool,
}

impl Gpu {
    pub fn new() -> Gpu {
        Gpu {
            enabled: true,
            ram: Memory::new(VRAM_SIZE),
            sprite_data: Memory::new(SPRITE_DATA_SIZE),
            control_register: 0x00,
            stat: GpuStat::new(),
            scroll_y: 0x00,
            scroll_x: 0x00,
            window_y: 0x00,
            window_x: 0x00,
            ly: 0x00,
            lyc: 0x00,
            bg_palette: 0x00,
            palette0: 0x00,
            palette1: 0x00,
            cycles: 0x00,
            frame: Frame::new(),
            backbuffer: Frame::new(),
            mode: GpuMode::HBlank,
            counter: 0,
            tile_base: 0x00,
            background_base: 0xC00,
            sprite_shape: SpriteShape::Square,
            sprites_enabled: true,
        }
    }

    pub fn reset(&mut self) {
        *self = Gpu::new();
    }

    fn set_status(&mut self, irq: &mut Irq) {
        if !self.enabled {
            self.cycles = 0x1C8;
            self.ly = 0x00;
            self.stat = GpuStat::from_u8(0xFD);
        }

        let current_mode = self.mode.clone();

        if self.ly >= 0x90 {
            self.mode = GpuMode::VBlank;
        } else {
            if self.cycles >= 0x178 {
                self.mode = GpuMode::SearchingRam;
            } else if self.cycles >= 0xCC {
                self.mode = GpuMode::TransferringData;
            } else {
                self.mode = GpuMode::HBlank;
            }
        }

        if self.mode != current_mode {
            irq.request(Interrupt::Lcd);
        }

        self.check_coincidence(irq);
    }

    pub fn step(&mut self, irq: &mut Irq, cycles: usize) -> Result<(), String> {
        self.set_status(irq);

        let cycles = cycles as isize;
        if self.enabled {
            self.cycles -= cycles;
        } else {
            return Ok(());
        }

        if self.cycles <= 0 {
            self.ly += 0x01;

            self.cycles = 0x1C8;

            if self.ly == 0x90 {
                irq.request(Interrupt::Vblank);
            } else if self.ly > 0x99 {
                self.ly = 0x00;
            } else if self.ly < 0x90 {
                self.render_scanline();
            }
        }

        Ok(())
    }

    fn render_scanline(&mut self) {
        let line = self.ly.wrapping_add(self.scroll_y) as usize;
        // self.clear_scanline(line);
        self.render_background(line);
        self.render_sprites(line);
        self.frame = self.backbuffer.clone();
    }

    fn clear_scanline(&mut self, line: usize) {
        for i in 0..160 {
            self.backbuffer.pixels[line * 160 + i] = Color::new(0xFF, 0xFF, 0xFF, 0xFF);
        }
    }

    fn render_background(&mut self, line: usize) {
        let bg_base = self.background_base;
        let tile_base = self.tile_base;
        let bg_map_row = (line / 0x08) as usize;
        for i in 0..gameboy::SCREEN_WIDTH {
            let x = (i as u8).wrapping_add(self.scroll_x);
            let bg_map_col = (x / 8) as usize;
            let raw_tile_number = self.ram[bg_base + (bg_map_row * 0x20 + bg_map_col)] as usize;

            let line_offset = (line % 0x08) << 0x01;

            let tile_data_start = tile_base +
                                  (if tile_base == 0x00 {
                raw_tile_number
            } else {
                (raw_tile_number as i8 as i16 + 0x80) as usize
            }) * 0x10 + line_offset;

            let x_shift = (x % 8).wrapping_sub(0x07).wrapping_mul(0xFF);
            let tile_data1 = (self.ram[tile_data_start] >> x_shift) & 0x01;
            let tile_data2 = (self.ram[tile_data_start + 0x01] >> x_shift) & 0x01;
            let total_row_data = (tile_data2 << 1) | tile_data1;
            let color_value = total_row_data;
            let c = self.get_background_color_for_byte(color_value as u8);
            self.backbuffer.pixels[self.ly as usize * 160 + i as usize] = c;
        }
    }

    fn render_sprites(&mut self, line: usize) {
        guard!(self.sprites_enabled);

        for i in 0..40 {
            let sprite_table_entry_base = i * 0x04;
            let s_y = self.sprite_data[sprite_table_entry_base] as i16 - 0x10;
            let s_x = self.sprite_data[sprite_table_entry_base + 0x01] as i16 - 0x08;
            let sprite_height = if let SpriteShape::Rectangle = self.sprite_shape {
                0x10
            } else {
                0x08
            };

            if s_y <= line as i16 && (s_y + sprite_height as i16) > line as i16 {
                let tile_number = self.sprite_data[sprite_table_entry_base + 0x02] as i16;
                let attributes = self.sprite_data[sprite_table_entry_base + 0x03];
                let above_background = attributes & 0x80 == 0x00;
                let flip_y = attributes & 0x40 == 0x40;
                let flip_x = attributes & 0x20 == 0x20;

                let sprite_y = (line as i16 - s_y) << 0x01;
                let tile_data_start = (tile_number * 0x10) + sprite_y;
                for x in 0..8 {
                    if s_x + x < 0 || s_x + x >= 160 {
                        continue;
                    }
                    let shift = if flip_x { x } else { 0x07 - x };
                    let tile_data1 = (self.ram[tile_data_start as usize] >> shift) & 0x01;
                    let tile_data2 = (self.ram[tile_data_start as usize + 0x01] >> shift) & 0x01;
                    let total_row_data = (tile_data2 << 1) | tile_data1;
                    let color_value = total_row_data;
                    if color_value == 0x00 {
                        continue;
                    }
                    let c = self.get_sprite_color_for_byte(color_value as u8,
                                                           ((attributes & 0x10) >> 0x04) as u8);
                    self.backbuffer.pixels[line as usize * 160 + (s_x as usize + x as usize)] = c;
                }
            }
        }
    }

    fn get_background_color_for_byte(&self, b: u8) -> Color {
        let palette_index = match b {
            0x00 => self.bg_palette & 0x0003,
            0x01 => (self.bg_palette & 0x000C) >> 0x02,
            0x02 => (self.bg_palette & 0x0030) >> 0x04,
            0x03 => (self.bg_palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from_dmg_byte(palette_index as u8)
    }

    fn get_sprite_color_for_byte(&self, b: u8, palette_entry: u8) -> Color {
        let palette = if palette_entry == 0x00 {
            self.palette0
        } else {
            self.palette1
        };

        let palette_index = match b {
            0x00 => palette & 0x0003,
            0x01 => (palette & 0x000C) >> 0x02,
            0x02 => (palette & 0x0030) >> 0x04,
            0x03 => (palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from_dmg_byte(palette_index as u8)
    }

    pub fn read_u8(&self, addr: u16) -> u8 {
        match addr {
            0x40 => self.control_register,
            0x41 => self.stat.to_u8(self),
            0x42 => self.scroll_y,
            0x43 => self.scroll_x,
            0x4A => self.window_y,
            0x4B => self.window_x,
            0x44 => self.ly,
            0x45 => self.lyc,
            0x47 => self.bg_palette,
            0x48 => self.palette0,
            0x49 => self.palette1,
            _ => panic!("tried to read GPU memory that is not mapped"),
        }
    }

    pub fn write_u8(&mut self, addr: u16, val: u8) {
        match addr {
            0x40 => {
                self.control_register = val;
                self.enabled = self.control_register & 0x80 == 0x80;
                self.tile_base = if self.control_register & 0x10 == 0x10 {
                    0x00
                } else {
                    0x800
                };
                self.background_base = if self.control_register & 0x08 == 0x08 {
                    0x1C00
                } else {
                    0x1800
                };
                self.sprite_shape = if self.control_register & 0x04 == 0x04 {
                    SpriteShape::Rectangle
                } else {
                    SpriteShape::Square
                };
                self.sprites_enabled = self.control_register & 0x02 == 0x02;
            }
            0x41 => self.stat = GpuStat::from_u8(val),
            0x42 => self.scroll_y = val,
            0x43 => self.scroll_x = val,
            0x44 => (),
            0x45 => {
                self.lyc = val;
            }
            0x47 => self.bg_palette = val,
            0x48 => self.palette0 = val,
            0x49 => self.palette1 = val,
            0x4A => self.window_y = val,
            0x4B => self.window_x = val,
            _ => panic!("tried to write GPU memory that is not mapped: {:04}", addr),
        }
    }

    pub fn write_u16(&mut self, addr: u16, val: u16) {
        let addr = addr as usize;
        LittleEndian::write_u16(&mut self.ram[addr..], val)
    }

    fn check_coincidence(&mut self, irq: &mut Irq) {
        // If LY == LYC then set the coincidence flag
        if self.lyc == self.ly && self.stat.coincidence_interrupt_enabled {
            irq.request(Interrupt::Lcd);
        }
    }
}
<commit_msg>Rename tile base variable to separate it for window tile base variable in future<commit_after>
use byteorder::{ByteOrder, LittleEndian};

use std;

use gameboy;
use gameboy::{Interconnect, Interrupt, Irq, Memory};
use gameboy::frame::{Color, Frame};

const VRAM_SIZE: usize = 0x4000;
const SPRITE_DATA_SIZE: usize = 0xA0;

pub struct GpuStat {
    pub coincidence_interrupt_enabled: bool,
    pub OAM_interrupt_enabled: bool,
    pub VBlank_interrupt_enabled: bool,
    pub HBlank_interrupt_enabled: bool,
}

impl GpuStat {
    pub fn new() -> GpuStat {
        GpuStat {
            coincidence_interrupt_enabled: false,
            OAM_interrupt_enabled: false,
            VBlank_interrupt_enabled: false,
            HBlank_interrupt_enabled: false,
        }
    }

    pub fn from_u8(b: u8) -> GpuStat {
        GpuStat {
            coincidence_interrupt_enabled: b & 0x40 == 0x40,
            OAM_interrupt_enabled: b & 0x20 == 0x20,
            VBlank_interrupt_enabled: b & 0x10 == 0x10,
            HBlank_interrupt_enabled: b & 0x08 == 0x08,
        }
    }

    pub fn to_u8(&self, gpu: &Gpu) -> u8 {
        (if self.coincidence_interrupt_enabled {
            0x40
        } else {
            0
        }) | (if self.OAM_interrupt_enabled { 0x20 } else { 0 }) |
        (if self.VBlank_interrupt_enabled {
            0x10
        } else {
            0
        }) |
        (if self.HBlank_interrupt_enabled {
            0x08
        } else {
            0
        }) | (if gpu.ly == gpu.lyc { 0x04 } else { 0 }) | gpu.mode.to_u8()
    }
}

#[derive(Eq, PartialEq, Clone)]
pub enum GpuMode {
    HBlank,
    VBlank,
    SearchingRam,
    TransferringData,
}

impl GpuMode {
    fn to_u8(&self) -> u8 {
        match *self {
            GpuMode::HBlank => 0x00,
            GpuMode::VBlank => 0x01,
            GpuMode::SearchingRam => 0x02,
            GpuMode::TransferringData => 0x03,
        }
    }
}

pub enum SpriteShape {
    Square,
    Rectangle,
}

pub struct Gpu {
    pub enabled: bool,
    pub ram: Memory,
    pub sprite_data: Memory,
    control_register: u8,
    stat: GpuStat,
    scroll_y: u8,
    scroll_x: u8,
    window_y: u8,
    window_x: u8,
    pub ly: u8,
    lyc: u8,
    bg_palette: u8,
    palette0: u8,
    palette1: u8,

    cycles: isize,

    pub frame: Frame,
    pub backbuffer: Frame,
    mode: GpuMode,

    counter: u8,
    bg_tile_base: usize,
    background_base: usize,
    sprite_shape: SpriteShape,
    sprites_enabled: bool,
}

impl Gpu {
    pub fn new() -> Gpu {
        Gpu {
            enabled: true,
            ram: Memory::new(VRAM_SIZE),
            sprite_data: Memory::new(SPRITE_DATA_SIZE),
            control_register: 0x00,
            stat: GpuStat::new(),
            scroll_y: 0x00,
            scroll_x: 0x00,
            window_y: 0x00,
            window_x: 0x00,
            ly: 0x00,
            lyc: 0x00,
            bg_palette: 0x00,
            palette0: 0x00,
            palette1: 0x00,
            cycles: 0x00,
            frame: Frame::new(),
            backbuffer: Frame::new(),
            mode: GpuMode::HBlank,
            counter: 0,
            bg_tile_base: 0x00,
            background_base: 0xC00,
            sprite_shape: SpriteShape::Square,
            sprites_enabled: true,
        }
    }

    pub fn reset(&mut self) {
        *self = Gpu::new();
    }

    fn set_status(&mut self, irq: &mut Irq) {
        if !self.enabled {
            self.cycles = 0x1C8;
            self.ly = 0x00;
            self.stat = GpuStat::from_u8(0xFD);
        }

        let current_mode = self.mode.clone();

        if self.ly >= 0x90 {
            self.mode = GpuMode::VBlank;
        } else {
            if self.cycles >= 0x178 {
                self.mode = GpuMode::SearchingRam;
            } else if self.cycles >= 0xCC {
                self.mode = GpuMode::TransferringData;
            } else {
                self.mode = GpuMode::HBlank;
            }
        }

        if self.mode != current_mode {
            irq.request(Interrupt::Lcd);
        }

        self.check_coincidence(irq);
    }

    pub fn step(&mut self, irq: &mut Irq, cycles: usize) -> Result<(), String> {
        self.set_status(irq);

        let cycles = cycles as isize;
        if self.enabled {
            self.cycles -= cycles;
        } else {
            return Ok(());
        }

        if self.cycles <= 0 {
            self.ly += 0x01;

            self.cycles = 0x1C8;

            if self.ly == 0x90 {
                irq.request(Interrupt::Vblank);
            } else if self.ly > 0x99 {
                self.ly = 0x00;
            } else if self.ly < 0x90 {
                self.render_scanline();
            }
        }

        Ok(())
    }

    fn render_scanline(&mut self) {
        let line = self.ly.wrapping_add(self.scroll_y) as usize;
        // self.clear_scanline(line);
        self.render_background(line);
        self.render_sprites(line);
        self.frame = self.backbuffer.clone();
    }

    fn clear_scanline(&mut self, line: usize) {
        for i in 0..160 {
            self.backbuffer.pixels[line * 160 + i] = Color::new(0xFF, 0xFF, 0xFF, 0xFF);
        }
    }

    fn render_background(&mut self, line: usize) {
        let bg_base = self.background_base;
        let tile_base = self.bg_tile_base;
        let bg_map_row = (line / 0x08) as usize;
        for i in 0..gameboy::SCREEN_WIDTH {
            let x = (i as u8).wrapping_add(self.scroll_x);
            let bg_map_col = (x / 8) as usize;
            let raw_tile_number = self.ram[bg_base + (bg_map_row * 0x20 + bg_map_col)] as usize;

            let line_offset = (line % 0x08) << 0x01;

            let tile_data_start = tile_base +
                                  (if tile_base == 0x00 {
                raw_tile_number
            } else {
                (raw_tile_number as i8 as i16 + 0x80) as usize
            }) * 0x10 + line_offset;

            let x_shift = (x % 8).wrapping_sub(0x07).wrapping_mul(0xFF);
            let tile_data1 = (self.ram[tile_data_start] >> x_shift) & 0x01;
            let tile_data2 = (self.ram[tile_data_start + 0x01] >> x_shift) & 0x01;
            let total_row_data = (tile_data2 << 1) | tile_data1;
            let color_value = total_row_data;
            let c = self.get_background_color_for_byte(color_value as u8);
            self.backbuffer.pixels[self.ly as usize * 160 + i as usize] = c;
        }
    }

    fn render_sprites(&mut self, line: usize) {
        guard!(self.sprites_enabled);

        for i in 0..40 {
            let sprite_table_entry_base = i * 0x04;
            let s_y = self.sprite_data[sprite_table_entry_base] as i16 - 0x10;
            let s_x = self.sprite_data[sprite_table_entry_base + 0x01] as i16 - 0x08;
            let sprite_height = if let SpriteShape::Rectangle = self.sprite_shape {
                0x10
            } else {
                0x08
            };

            if s_y <= line as i16 && (s_y + sprite_height as i16) > line as i16 {
                let tile_number = self.sprite_data[sprite_table_entry_base + 0x02] as i16;
                let attributes = self.sprite_data[sprite_table_entry_base + 0x03];
                let above_background = attributes & 0x80 == 0x00;
                let flip_y = attributes & 0x40 == 0x40;
                let flip_x = attributes & 0x20 == 0x20;

                let sprite_y = (line as i16 - s_y) << 0x01;
                let tile_data_start = (tile_number * 0x10) + sprite_y;
                for x in 0..8 {
                    if s_x + x < 0 || s_x + x >= 160 {
                        continue;
                    }
                    let shift = if flip_x { x } else { 0x07 - x };
                    let tile_data1 = (self.ram[tile_data_start as usize] >> shift) & 0x01;
                    let tile_data2 = (self.ram[tile_data_start as usize + 0x01] >> shift) & 0x01;
                    let total_row_data = (tile_data2 << 1) | tile_data1;
                    let color_value = total_row_data;
                    if color_value == 0x00 {
                        continue;
                    }
                    let c = self.get_sprite_color_for_byte(color_value as u8,
                                                           ((attributes & 0x10) >> 0x04) as u8);
                    self.backbuffer.pixels[line as usize * 160 + (s_x as usize + x as usize)] = c;
                }
            }
        }
    }

    fn get_background_color_for_byte(&self, b: u8) -> Color {
        let palette_index = match b {
            0x00 => self.bg_palette & 0x0003,
            0x01 => (self.bg_palette & 0x000C) >> 0x02,
            0x02 => (self.bg_palette & 0x0030) >> 0x04,
            0x03 => (self.bg_palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from_dmg_byte(palette_index as u8)
    }

    fn get_sprite_color_for_byte(&self, b: u8, palette_entry: u8) -> Color {
        let palette = if palette_entry == 0x00 {
            self.palette0
        } else {
            self.palette1
        };

        let palette_index = match b {
            0x00 => palette & 0x0003,
            0x01 => (palette & 0x000C) >> 0x02,
            0x02 => (palette & 0x0030) >> 0x04,
            0x03 => (palette & 0x00C0) >> 0x06,
            _ => panic!("err: invalid pixel color value found"),
        };

        Color::from_dmg_byte(palette_index as u8)
    }

    pub fn read_u8(&self, addr: u16) -> u8 {
        match addr {
            0x40 => self.control_register,
            0x41 => self.stat.to_u8(self),
            0x42 => self.scroll_y,
            0x43 => self.scroll_x,
            0x4A => self.window_y,
            0x4B => self.window_x,
            0x44 => self.ly,
            0x45 => self.lyc,
            0x47 => self.bg_palette,
            0x48 => self.palette0,
            0x49 => self.palette1,
            _ => panic!("tried to read GPU memory that is not mapped"),
        }
    }

    pub fn write_u8(&mut self, addr: u16, val: u8) {
        match addr {
            0x40 => {
                self.control_register = val;
                self.enabled = self.control_register & 0x80 == 0x80;
                self.bg_tile_base = if self.control_register & 0x10 == 0x10 {
                    0x00
                } else {
                    0x800
                };
                self.background_base = if self.control_register & 0x08 == 0x08 {
                    0x1C00
                } else {
                    0x1800
                };
                self.sprite_shape = if self.control_register & 0x04 == 0x04 {
                    SpriteShape::Rectangle
                } else {
                    SpriteShape::Square
                };
                self.sprites_enabled = self.control_register & 0x02 == 0x02;
            }
            0x41 => self.stat = GpuStat::from_u8(val),
            0x42 => self.scroll_y = val,
            0x43 => self.scroll_x = val,
            0x44 => (),
            0x45 => {
                self.lyc = val;
            }
            0x47 => self.bg_palette = val,
            0x48 => self.palette0 = val,
            0x49 => self.palette1 = val,
            0x4A => self.window_y = val,
            0x4B => self.window_x = val,
            _ => panic!("tried to write GPU memory that is not mapped: {:04}", addr),
        }
    }

    pub fn write_u16(&mut self, addr: u16, val: u16) {
        let addr = addr as usize;
        LittleEndian::write_u16(&mut self.ram[addr..], val)
    }

    fn check_coincidence(&mut self, irq: &mut Irq) {
        // If LY == LYC then set the coincidence flag
        if self.lyc == self.ly && self.stat.coincidence_interrupt_enabled {
            irq.request(Interrupt::Lcd);
        }
    }
}
<|endoftext|>


<commit_before>//! Reading GameData from data.win format

use std::io::prelude::*;
use std::io;
use byteorder::{self, ReadBytesExt, WriteBytesExt, LittleEndian};
use super::*;

quick_error! {
    #[derive(Debug)]
    pub enum StringReadError {
        Io(err: io::Error) {
            from()
        }
        ByteOrder(err: byteorder::Error) {
            from()
        }
        MissingNullTerminator {}
    }
}

quick_error! {
    #[derive(Debug)]
    pub enum ReadError {
        Io(err: io::Error) {
            from()
        }
        ByteOrder(err: byteorder::Error) {
            from()
        }
        String(err: StringReadError) {
            from()
        }
        InvalidChunkTypeId(what: [u8; 4]) { }
    }
}

const TYPE_ID_LEN: usize = 4;
const CHUNK_HEADER_LEN: i32 = TYPE_ID_LEN as i32 + 4;

pub fn read<R: Read>(reader: &mut R) -> Result<GameData, ReadError> {
    try!(get_chunk_header(reader, b"FORM"));
    let meta = try!(MetaData::read(reader));
    let optn = try!(Optn::read(reader));
    let extn = try!(Extn::read(reader));
    let sounds = try!(Sounds::read(reader));
    let agrp = try!(Agrp::read(reader));
    let sprites = try!(Sprites::read(reader));
    let backgrounds = try!(Backgrounds::read(reader));
    let paths = try!(Paths::read(reader));
    let scripts = try!(Scripts::read(reader));
    let shaders = try!(Shaders::read(reader));
    let fonts = try!(Fonts::read(reader));
    let timelines = try!(Timelines::read(reader));
    let objects = try!(Objects::read(reader));
    let rooms = try!(Rooms::read(reader));
    let dafl = try!(Dafl::read(reader));
    let tpag = try!(Tpag::read(reader));
    let code = try!(Code::read(reader));
    let vari = try!(Vari::read(reader));
    let functions = try!(Functions::read(reader));
    let (strings, _offsets) = try!(Strings::read(reader));
    let textures = try!(Textures::read(reader));
    let audio = try!(Audio::read(reader));
    Ok(GameData {
        metadata: meta,
        optn: optn,
        extn: extn,
        sounds: sounds,
        agrp: Some(agrp),
        sprites: sprites,
        backgrounds: backgrounds,
        paths: paths,
        scripts: scripts,
        shaders: shaders,
        fonts: fonts,
        timelines: timelines,
        objects: objects,
        rooms: rooms,
        dafl: dafl,
        tpag: tpag,
        code: code,
        vari: vari,
        functions: functions,
        strings: strings,
        textures: textures,
        audio: audio,
    })
}

fn form_content_len(data: &GameData) -> i32 {
    data.metadata.len() + CHUNK_HEADER_LEN + data.optn.len() + CHUNK_HEADER_LEN +
    data.extn.len() + CHUNK_HEADER_LEN + data.sounds.len() + CHUNK_HEADER_LEN +
    data.agrp.as_ref().map_or(0, |a| a.len()) + CHUNK_HEADER_LEN +
    data.sprites.len() + CHUNK_HEADER_LEN + data.backgrounds.len() + CHUNK_HEADER_LEN +
    data.paths.len() + CHUNK_HEADER_LEN + data.scripts.len() +
    CHUNK_HEADER_LEN + data.shaders.len() + CHUNK_HEADER_LEN + data.fonts.len() +
    CHUNK_HEADER_LEN +
    data.timelines.len() + CHUNK_HEADER_LEN + data.objects.len() + CHUNK_HEADER_LEN +
    data.rooms.len() + CHUNK_HEADER_LEN + data.dafl.len() +
    CHUNK_HEADER_LEN + data.tpag.len() + CHUNK_HEADER_LEN + data.code.len() + CHUNK_HEADER_LEN +
    data.vari.len() + CHUNK_HEADER_LEN + data.functions.len() +
    CHUNK_HEADER_LEN + data.strings.len() + CHUNK_HEADER_LEN + data.textures.len() +
    CHUNK_HEADER_LEN +
    data.audio.len() + CHUNK_HEADER_LEN
}

pub fn write<W: Write>(data: &GameData, writer: &mut W) -> Result<(), io::Error> {
    let mut offset = 0;
    try!(writer.write_all(b"FORM"));
    let len = form_content_len(data);
    assert_eq!(len, 56291302);
    try!(writer.write_i32::<LittleEndian>(len));
    offset += CHUNK_HEADER_LEN;
    try!(data.metadata.write(writer, offset));
    offset += data.metadata.len() + CHUNK_HEADER_LEN;
    try!(data.optn.write(writer, offset));
    offset += data.optn.len() + CHUNK_HEADER_LEN;
    try!(data.extn.write(writer, offset));
    offset += data.extn.len() + CHUNK_HEADER_LEN;
    try!(data.sounds.write(writer, offset));
    offset += data.sounds.len() + CHUNK_HEADER_LEN;
    if let Some(ref agrp) = data.agrp {
        try!(agrp.write(writer, offset));
        offset += agrp.len() + CHUNK_HEADER_LEN;
    }
    try!(data.sprites.write(writer, offset));
    offset += data.sprites.len() + CHUNK_HEADER_LEN;
    try!(data.backgrounds.write(writer, offset));
    offset += data.backgrounds.len() + CHUNK_HEADER_LEN;
    try!(data.paths.write(writer, offset));
    offset += data.paths.len() + CHUNK_HEADER_LEN;
    try!(data.scripts.write(writer, offset));
    offset += data.scripts.len() + CHUNK_HEADER_LEN;
    try!(data.shaders.write(writer, offset));
    offset += data.shaders.len() + CHUNK_HEADER_LEN;
    try!(data.fonts.write(writer, offset));
    offset += data.fonts.len() + CHUNK_HEADER_LEN;
    try!(data.timelines.write(writer, offset));
    offset += data.timelines.len() + CHUNK_HEADER_LEN;
    try!(data.objects.write(writer, offset));
    offset += data.objects.len() + CHUNK_HEADER_LEN;
    try!(data.rooms.write(writer, offset));
    offset += data.rooms.len() + CHUNK_HEADER_LEN;
    try!(data.dafl.write(writer, offset));
    offset += data.dafl.len() + CHUNK_HEADER_LEN;
    try!(data.tpag.write(writer, offset));
    offset += data.tpag.len() + CHUNK_HEADER_LEN;
    try!(data.code.write(writer, offset));
    offset += data.code.len() + CHUNK_HEADER_LEN;
    try!(data.vari.write(writer, offset));
    offset += data.vari.len() + CHUNK_HEADER_LEN;
    try!(data.functions.write(writer, offset));
    offset += data.functions.len() + CHUNK_HEADER_LEN;
    try!(data.strings.write(writer, offset));
    offset += data.strings.len() + CHUNK_HEADER_LEN;
    try!(data.textures.write(writer, offset));
    offset += data.textures.len() + CHUNK_HEADER_LEN;
    try!(data.audio.write(writer, offset));
    Ok(())
}

trait Chunk {
    const TYPE_ID: &'static [u8; 4];
    type ReadOutput = Self;
    fn read<R: Read>(reader: &mut R) -> Result<Self::ReadOutput, ReadError>;
    /// Returns how many bytes were written
    fn write<W: Write>(&self, writer: &mut W, offset: i32) -> Result<(), io::Error>;
    fn len(&self) -> i32;
}

macro_rules! unk_chunk {
    ($name:ident, $typeid:expr) => {
        impl Chunk for $name {
            const TYPE_ID: &'static [u8; 4] = $typeid;
            fn read<R: Read>(reader: &mut R) -> Result<Self::ReadOutput, ReadError> {
                let chunk_header = try!(get_chunk_header(reader, Self::TYPE_ID));
                Ok($name {
                    raw: try!(read_into_byte_vec(reader, chunk_header.size))
                })
            }
            fn write<W: Write>(&self, writer: &mut W, _offset: i32) -> Result<(), io::Error> {
                try!(writer.write_all(Self::TYPE_ID));
                try!(writer.write_i32::<LittleEndian>(self.len()));
                try!(writer.write_all(&self.raw));
                Ok(())
            }
            fn len(&self) -> i32 {
                self.raw.len() as i32
            }
        }
    }
}

unk_chunk!(MetaData, b"GEN8");
unk_chunk!(Optn, b"OPTN");
unk_chunk!(Extn, b"EXTN");
unk_chunk!(Sounds, b"SOND");
unk_chunk!(Agrp, b"AGRP");
unk_chunk!(Sprites, b"SPRT");
unk_chunk!(Backgrounds, b"BGND");
unk_chunk!(Paths, b"PATH");
unk_chunk!(Scripts, b"SCPT");
unk_chunk!(Shaders, b"SHDR");
unk_chunk!(Fonts, b"FONT");
unk_chunk!(Timelines, b"TMLN");
unk_chunk!(Objects, b"OBJT");
unk_chunk!(Rooms, b"ROOM");
unk_chunk!(Dafl, b"DAFL");
unk_chunk!(Tpag, b"TPAG");
unk_chunk!(Code, b"CODE");
unk_chunk!(Vari, b"VARI");
unk_chunk!(Functions, b"FUNC");
unk_chunk!(Textures, b"TXTR");
unk_chunk!(Audio, b"AUDO");

impl Chunk for Strings {
    const TYPE_ID: &'static [u8; 4] = b"STRG";
    type ReadOutput = (Self, Vec<u32>);
    fn read<R: Read>(reader: &mut R) -> Result<Self::ReadOutput, ReadError> {
        try!(get_chunk_header(reader, Self::TYPE_ID));
        let count = try!(reader.read_u32::<LittleEndian>());
        let mut offsets = Vec::with_capacity(count as usize);
        for _ in 0..count {
            let offset = try!(reader.read_u32::<LittleEndian>());
            offsets.push(offset);
        }
        let mut strings = Vec::with_capacity(count as usize);
        for _ in 0..count {
            let string = try!(read_string(reader));
            strings.push(string);
        }
        // TODO: Why do we need to consume additional 4 bytes?
        // Looks like 4 zero bytes.
        let mut buf = [0u8; 4];
        try!(reader.read_exact(&mut buf));
        Ok((Strings { strings: strings }, offsets))
    }
    fn write<W: Write>(&self, writer: &mut W, offset: i32) -> Result<(), io::Error> {
        try!(writer.write_all(Self::TYPE_ID));
        try!(writer.write_i32::<LittleEndian>(self.len()));
        try!(writer.write_u32::<LittleEndian>(self.strings.len() as u32));
        let mut string_offset = offset + CHUNK_HEADER_LEN + 4 + (self.strings.len() as i32 * 4);
        // Fucking up at first absolute offset
        // orig 10918688 vs new 10794188
        for string in &self.strings {
            try!(writer.write_u32::<LittleEndian>(string_offset as u32));
            string_offset += (string.len() + 1) as i32 + 4;
        }
        for string in &self.strings {
            try!(writer.write_u32::<LittleEndian>(string.len() as u32));
            try!(writer.write_all(string.as_bytes()));
            try!(writer.write_u8(0));
        }
        // Required padding
        try!(writer.write_all(&[0u8; 4]));
        Ok(())
    }
    fn len(&self) -> i32 {
        let mut lengths = 0;
        for s in &self.strings {
            // The length denominator before the string
            lengths += 4;
            // + 1 for null terminator
            lengths += s.len() + 1;
        }
        // +4 at end for zero padding
        (4 + (self.strings.len() * 4) + lengths + 4) as i32
    }
}

fn read_string<R: Read>(reader: &mut R) -> Result<String, StringReadError> {
    let len = try!(reader.read_u32::<LittleEndian>());
    let mut buf = Vec::with_capacity(len as usize);
    unsafe {
        buf.set_len(len as usize);
        try!(reader.read_exact(&mut buf));
    }
    let terminator = try!(reader.read_u8());
    if terminator == 0 {
        // We assume strings are valid UTF-8, if not, panic.
        Ok(String::from_utf8(buf).unwrap())
    } else {
        Err(StringReadError::MissingNullTerminator)
    }
}

struct ChunkHeader {
    type_id: [u8; TYPE_ID_LEN],
    size: usize,
}

fn read_chunk_header<R: Read>(reader: &mut R) -> Result<ChunkHeader, ReadError> {
    let mut type_id = [0u8; TYPE_ID_LEN];
    try!(reader.read_exact(&mut type_id));
    let size = try!(reader.read_i32::<LittleEndian>());
    Ok(ChunkHeader {
        type_id: type_id,
        size: size as usize,
    })
}

fn get_chunk_header<R: Read>(reader: &mut R, should_be: &[u8]) -> Result<ChunkHeader, ReadError> {
    let header = try!(read_chunk_header(reader));
    if &header.type_id == should_be {
        Ok(header)
    } else {
        Err(ReadError::InvalidChunkTypeId(header.type_id))
    }
}

fn read_into_byte_vec<R: Read>(reader: &mut R, len: usize) -> Result<Vec<u8>, io::Error> {
    let mut vec = Vec::with_capacity(len);
    unsafe {
        vec.set_len(len);
        try!(reader.read_exact(&mut vec));
    }
    Ok(vec)
}
<commit_msg>Remove lying comment<commit_after>//! Reading GameData from data.win format

use std::io::prelude::*;
use std::io;
use byteorder::{self, ReadBytesExt, WriteBytesExt, LittleEndian};
use super::*;

quick_error! {
    #[derive(Debug)]
    pub enum StringReadError {
        Io(err: io::Error) {
            from()
        }
        ByteOrder(err: byteorder::Error) {
            from()
        }
        MissingNullTerminator {}
    }
}

quick_error! {
    #[derive(Debug)]
    pub enum ReadError {
        Io(err: io::Error) {
            from()
        }
        ByteOrder(err: byteorder::Error) {
            from()
        }
        String(err: StringReadError) {
            from()
        }
        InvalidChunkTypeId(what: [u8; 4]) { }
    }
}

const TYPE_ID_LEN: usize = 4;
const CHUNK_HEADER_LEN: i32 = TYPE_ID_LEN as i32 + 4;

pub fn read<R: Read>(reader: &mut R) -> Result<GameData, ReadError> {
    try!(get_chunk_header(reader, b"FORM"));
    let meta = try!(MetaData::read(reader));
    let optn = try!(Optn::read(reader));
    let extn = try!(Extn::read(reader));
    let sounds = try!(Sounds::read(reader));
    let agrp = try!(Agrp::read(reader));
    let sprites = try!(Sprites::read(reader));
    let backgrounds = try!(Backgrounds::read(reader));
    let paths = try!(Paths::read(reader));
    let scripts = try!(Scripts::read(reader));
    let shaders = try!(Shaders::read(reader));
    let fonts = try!(Fonts::read(reader));
    let timelines = try!(Timelines::read(reader));
    let objects = try!(Objects::read(reader));
    let rooms = try!(Rooms::read(reader));
    let dafl = try!(Dafl::read(reader));
    let tpag = try!(Tpag::read(reader));
    let code = try!(Code::read(reader));
    let vari = try!(Vari::read(reader));
    let functions = try!(Functions::read(reader));
    let (strings, _offsets) = try!(Strings::read(reader));
    let textures = try!(Textures::read(reader));
    let audio = try!(Audio::read(reader));
    Ok(GameData {
        metadata: meta,
        optn: optn,
        extn: extn,
        sounds: sounds,
        agrp: Some(agrp),
        sprites: sprites,
        backgrounds: backgrounds,
        paths: paths,
        scripts: scripts,
        shaders: shaders,
        fonts: fonts,
        timelines: timelines,
        objects: objects,
        rooms: rooms,
        dafl: dafl,
        tpag: tpag,
        code: code,
        vari: vari,
        functions: functions,
        strings: strings,
        textures: textures,
        audio: audio,
    })
}

fn form_content_len(data: &GameData) -> i32 {
    data.metadata.len() + CHUNK_HEADER_LEN + data.optn.len() + CHUNK_HEADER_LEN +
    data.extn.len() + CHUNK_HEADER_LEN + data.sounds.len() + CHUNK_HEADER_LEN +
    data.agrp.as_ref().map_or(0, |a| a.len()) + CHUNK_HEADER_LEN +
    data.sprites.len() + CHUNK_HEADER_LEN + data.backgrounds.len() + CHUNK_HEADER_LEN +
    data.paths.len() + CHUNK_HEADER_LEN + data.scripts.len() +
    CHUNK_HEADER_LEN + data.shaders.len() + CHUNK_HEADER_LEN + data.fonts.len() +
    CHUNK_HEADER_LEN +
    data.timelines.len() + CHUNK_HEADER_LEN + data.objects.len() + CHUNK_HEADER_LEN +
    data.rooms.len() + CHUNK_HEADER_LEN + data.dafl.len() +
    CHUNK_HEADER_LEN + data.tpag.len() + CHUNK_HEADER_LEN + data.code.len() + CHUNK_HEADER_LEN +
    data.vari.len() + CHUNK_HEADER_LEN + data.functions.len() +
    CHUNK_HEADER_LEN + data.strings.len() + CHUNK_HEADER_LEN + data.textures.len() +
    CHUNK_HEADER_LEN +
    data.audio.len() + CHUNK_HEADER_LEN
}

pub fn write<W: Write>(data: &GameData, writer: &mut W) -> Result<(), io::Error> {
    let mut offset = 0;
    try!(writer.write_all(b"FORM"));
    let len = form_content_len(data);
    assert_eq!(len, 56291302);
    try!(writer.write_i32::<LittleEndian>(len));
    offset += CHUNK_HEADER_LEN;
    try!(data.metadata.write(writer, offset));
    offset += data.metadata.len() + CHUNK_HEADER_LEN;
    try!(data.optn.write(writer, offset));
    offset += data.optn.len() + CHUNK_HEADER_LEN;
    try!(data.extn.write(writer, offset));
    offset += data.extn.len() + CHUNK_HEADER_LEN;
    try!(data.sounds.write(writer, offset));
    offset += data.sounds.len() + CHUNK_HEADER_LEN;
    if let Some(ref agrp) = data.agrp {
        try!(agrp.write(writer, offset));
        offset += agrp.len() + CHUNK_HEADER_LEN;
    }
    try!(data.sprites.write(writer, offset));
    offset += data.sprites.len() + CHUNK_HEADER_LEN;
    try!(data.backgrounds.write(writer, offset));
    offset += data.backgrounds.len() + CHUNK_HEADER_LEN;
    try!(data.paths.write(writer, offset));
    offset += data.paths.len() + CHUNK_HEADER_LEN;
    try!(data.scripts.write(writer, offset));
    offset += data.scripts.len() + CHUNK_HEADER_LEN;
    try!(data.shaders.write(writer, offset));
    offset += data.shaders.len() + CHUNK_HEADER_LEN;
    try!(data.fonts.write(writer, offset));
    offset += data.fonts.len() + CHUNK_HEADER_LEN;
    try!(data.timelines.write(writer, offset));
    offset += data.timelines.len() + CHUNK_HEADER_LEN;
    try!(data.objects.write(writer, offset));
    offset += data.objects.len() + CHUNK_HEADER_LEN;
    try!(data.rooms.write(writer, offset));
    offset += data.rooms.len() + CHUNK_HEADER_LEN;
    try!(data.dafl.write(writer, offset));
    offset += data.dafl.len() + CHUNK_HEADER_LEN;
    try!(data.tpag.write(writer, offset));
    offset += data.tpag.len() + CHUNK_HEADER_LEN;
    try!(data.code.write(writer, offset));
    offset += data.code.len() + CHUNK_HEADER_LEN;
    try!(data.vari.write(writer, offset));
    offset += data.vari.len() + CHUNK_HEADER_LEN;
    try!(data.functions.write(writer, offset));
    offset += data.functions.len() + CHUNK_HEADER_LEN;
    try!(data.strings.write(writer, offset));
    offset += data.strings.len() + CHUNK_HEADER_LEN;
    try!(data.textures.write(writer, offset));
    offset += data.textures.len() + CHUNK_HEADER_LEN;
    try!(data.audio.write(writer, offset));
    Ok(())
}

trait Chunk {
    const TYPE_ID: &'static [u8; 4];
    type ReadOutput = Self;
    fn read<R: Read>(reader: &mut R) -> Result<Self::ReadOutput, ReadError>;
    fn write<W: Write>(&self, writer: &mut W, offset: i32) -> Result<(), io::Error>;
    fn len(&self) -> i32;
}

macro_rules! unk_chunk {
    ($name:ident, $typeid:expr) => {
        impl Chunk for $name {
            const TYPE_ID: &'static [u8; 4] = $typeid;
            fn read<R: Read>(reader: &mut R) -> Result<Self::ReadOutput, ReadError> {
                let chunk_header = try!(get_chunk_header(reader, Self::TYPE_ID));
                Ok($name {
                    raw: try!(read_into_byte_vec(reader, chunk_header.size))
                })
            }
            fn write<W: Write>(&self, writer: &mut W, _offset: i32) -> Result<(), io::Error> {
                try!(writer.write_all(Self::TYPE_ID));
                try!(writer.write_i32::<LittleEndian>(self.len()));
                try!(writer.write_all(&self.raw));
                Ok(())
            }
            fn len(&self) -> i32 {
                self.raw.len() as i32
            }
        }
    }
}

unk_chunk!(MetaData, b"GEN8");
unk_chunk!(Optn, b"OPTN");
unk_chunk!(Extn, b"EXTN");
unk_chunk!(Sounds, b"SOND");
unk_chunk!(Agrp, b"AGRP");
unk_chunk!(Sprites, b"SPRT");
unk_chunk!(Backgrounds, b"BGND");
unk_chunk!(Paths, b"PATH");
unk_chunk!(Scripts, b"SCPT");
unk_chunk!(Shaders, b"SHDR");
unk_chunk!(Fonts, b"FONT");
unk_chunk!(Timelines, b"TMLN");
unk_chunk!(Objects, b"OBJT");
unk_chunk!(Rooms, b"ROOM");
unk_chunk!(Dafl, b"DAFL");
unk_chunk!(Tpag, b"TPAG");
unk_chunk!(Code, b"CODE");
unk_chunk!(Vari, b"VARI");
unk_chunk!(Functions, b"FUNC");
unk_chunk!(Textures, b"TXTR");
unk_chunk!(Audio, b"AUDO");

impl Chunk for Strings {
    const TYPE_ID: &'static [u8; 4] = b"STRG";
    type ReadOutput = (Self, Vec<u32>);
    fn read<R: Read>(reader: &mut R) -> Result<Self::ReadOutput, ReadError> {
        try!(get_chunk_header(reader, Self::TYPE_ID));
        let count = try!(reader.read_u32::<LittleEndian>());
        let mut offsets = Vec::with_capacity(count as usize);
        for _ in 0..count {
            let offset = try!(reader.read_u32::<LittleEndian>());
            offsets.push(offset);
        }
        let mut strings = Vec::with_capacity(count as usize);
        for _ in 0..count {
            let string = try!(read_string(reader));
            strings.push(string);
        }
        // TODO: Why do we need to consume additional 4 bytes?
        // Looks like 4 zero bytes.
        let mut buf = [0u8; 4];
        try!(reader.read_exact(&mut buf));
        Ok((Strings { strings: strings }, offsets))
    }
    fn write<W: Write>(&self, writer: &mut W, offset: i32) -> Result<(), io::Error> {
        try!(writer.write_all(Self::TYPE_ID));
        try!(writer.write_i32::<LittleEndian>(self.len()));
        try!(writer.write_u32::<LittleEndian>(self.strings.len() as u32));
        let mut string_offset = offset + CHUNK_HEADER_LEN + 4 + (self.strings.len() as i32 * 4);
        // Fucking up at first absolute offset
        // orig 10918688 vs new 10794188
        for string in &self.strings {
            try!(writer.write_u32::<LittleEndian>(string_offset as u32));
            string_offset += (string.len() + 1) as i32 + 4;
        }
        for string in &self.strings {
            try!(writer.write_u32::<LittleEndian>(string.len() as u32));
            try!(writer.write_all(string.as_bytes()));
            try!(writer.write_u8(0));
        }
        // Required padding
        try!(writer.write_all(&[0u8; 4]));
        Ok(())
    }
    fn len(&self) -> i32 {
        let mut lengths = 0;
        for s in &self.strings {
            // The length denominator before the string
            lengths += 4;
            // + 1 for null terminator
            lengths += s.len() + 1;
        }
        // +4 at end for zero padding
        (4 + (self.strings.len() * 4) + lengths + 4) as i32
    }
}

fn read_string<R: Read>(reader: &mut R) -> Result<String, StringReadError> {
    let len = try!(reader.read_u32::<LittleEndian>());
    let mut buf = Vec::with_capacity(len as usize);
    unsafe {
        buf.set_len(len as usize);
        try!(reader.read_exact(&mut buf));
    }
    let terminator = try!(reader.read_u8());
    if terminator == 0 {
        // We assume strings are valid UTF-8, if not, panic.
        Ok(String::from_utf8(buf).unwrap())
    } else {
        Err(StringReadError::MissingNullTerminator)
    }
}

struct ChunkHeader {
    type_id: [u8; TYPE_ID_LEN],
    size: usize,
}

fn read_chunk_header<R: Read>(reader: &mut R) -> Result<ChunkHeader, ReadError> {
    let mut type_id = [0u8; TYPE_ID_LEN];
    try!(reader.read_exact(&mut type_id));
    let size = try!(reader.read_i32::<LittleEndian>());
    Ok(ChunkHeader {
        type_id: type_id,
        size: size as usize,
    })
}

fn get_chunk_header<R: Read>(reader: &mut R, should_be: &[u8]) -> Result<ChunkHeader, ReadError> {
    let header = try!(read_chunk_header(reader));
    if &header.type_id == should_be {
        Ok(header)
    } else {
        Err(ReadError::InvalidChunkTypeId(header.type_id))
    }
}

fn read_into_byte_vec<R: Read>(reader: &mut R, len: usize) -> Result<Vec<u8>, io::Error> {
    let mut vec = Vec::with_capacity(len);
    unsafe {
        vec.set_len(len);
        try!(reader.read_exact(&mut vec));
    }
    Ok(vec)
}
<|endoftext|>


<commit_before>use super::builder::Builder;
use ast::HeadingLevel;

///
/// HTML generation might need to be customized to invividual use.
/// A `Format` allows to customize output generation (e.g. custom tags, classes, ...)
/// TODO: I need some feedback on the name `Format`
/// TODO: API is only a draft
///
pub trait Format {
    fn heading(&self, builder: &mut Builder, level: HeadingLevel, content: String);
}

pub struct DefaultFormat;

impl Format for DefaultFormat {
    fn heading(&self, builder: &mut Builder, level: HeadingLevel, content: String) {
        let tag = match level {
            HeadingLevel::Level1 => "h1",
            HeadingLevel::Level2 => "h2",
            HeadingLevel::Level3 => "h3",
            HeadingLevel::__NonExhaustive => unreachable!(),
        };

        builder.text_tag(tag, content);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ast::HeadingLevel;

    #[test]
    fn default_heading() {
        let format = DefaultFormat;
        let mut builder = Builder::new();

        format.heading(
            &mut builder,
            HeadingLevel::Level1,
            "hello world".to_string(),
        );

        format.heading(&mut builder, HeadingLevel::Level2, "level 2".to_string());
        format.heading(&mut builder, HeadingLevel::Level3, "level 3".to_string());

        assert_eq!("
hello world

level 2

level 3
", format!("{}", builder.consume()));
    }
}<commit_msg>Run rustfmt<commit_after>use super::builder::Builder;
use ast::HeadingLevel;

///
/// HTML generation might need to be customized to invividual use.
/// A `Format` allows to customize output generation (e.g. custom tags, classes, ...)
/// TODO: I need some feedback on the name `Format`
/// TODO: API is only a draft
///
pub trait Format {
    fn heading(&self, builder: &mut Builder, level: HeadingLevel, content: String);
}

pub struct DefaultFormat;

impl Format for DefaultFormat {
    fn heading(&self, builder: &mut Builder, level: HeadingLevel, content: String) {
        let tag = match level {
            HeadingLevel::Level1 => "h1",
            HeadingLevel::Level2 => "h2",
            HeadingLevel::Level3 => "h3",
            HeadingLevel::__NonExhaustive => unreachable!(),
        };

        builder.text_tag(tag, content);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ast::HeadingLevel;

    #[test]
    fn default_heading() {
        let format = DefaultFormat;
        let mut builder = Builder::new();

        format.heading(
            &mut builder,
            HeadingLevel::Level1,
            "hello world".to_string(),
        );

        format.heading(&mut builder, HeadingLevel::Level2, "level 2".to_string());
        format.heading(&mut builder, HeadingLevel::Level3, "level 3".to_string());

        assert_eq!(
            "
hello world

level 2

level 3
",
            format!("{}", builder.consume())
        );
    }
}<|endoftext|>


<commit_before>/*!
 * Poorly categorised functions for reading things used in multiple places.
 *
 * (That is, typically, more than one of request-or-response reading-or-writing.)
 *
 * TODO: refactor all this to store things in more usefully categorised places.
 */
use std::num::{Zero, cast};
use std::io::{IoError, IoResult, OtherIoError};
#[cfg(test)]
use std::io::MemReader;

// XXX: IoError ain't a good representation of this.
fn bad_input() -> IoError {
    IoError {
        kind: OtherIoError,
        desc: "invalid number",
        detail: None,
    }
}

static ASCII_ZERO: u8 = b'0';
static ASCII_NINE: u8 = b'9';
static ASCII_LOWER_A: u8 = b'a';
static ASCII_LOWER_F: u8 = b'f';
static ASCII_UPPER_A: u8 = b'A';
static ASCII_UPPER_F: u8 = b'F';

/**
 * Read a positive decimal integer from the given reader.
 *
 * # Arguments
 *
 * - `reader` - the reader to read the decimal digits from (and whatever byte comes next)
 * - `expected_end` - this function is called with the byte that follows the last decimal digit
 *   and should return `true` if that byte is what is expected to follow the number.
 *
 * # Return value
 *
 * - `None`, if the number overflows;
 * - `None`, if there is a leading zero;
 * - `None`, if all the digits are read and the `expected_end` function is false
 *
 * Should everything work as designed (i.e. none of these conditions occur) a `Some` is returned.
 */
pub fn read_decimal<R: Reader, N: Unsigned + NumCast + PartialOrd + CheckedMul + CheckedAdd>
                   (reader: &mut R, expected_end: |u8| -> bool)
                   -> IoResult<N> {
    // Here and in `read_hexadecimal` there is the possibility of infinite sequence of zeroes. The
    // spec allows this, but it may not be a good thing to allow. It's not a particularly good
    // attack surface, though, because of the low return.
    let mut n: N = Zero::zero();
    let mut got_content = false;
    let ten: N = cast(10).unwrap();
    loop {
        n = match reader.read_byte() {
            Ok(b@ASCII_ZERO..ASCII_NINE) => {
                // Written sanely, this is: n * 10 + (b - '0'), but we avoid
                // (semantically unsound) overflow by using checked operations.
                // There is no need in the b - '0' part as it is safe.
                match n.checked_mul(&ten).and_then(
                        |n| n.checked_add(&cast(b - ASCII_ZERO).unwrap())) {
                    Some(new_n) => new_n,
                    None => return Err(bad_input()),  // overflow
                }
            },
            Ok(b) if got_content && expected_end(b) => return Ok(n),
            Ok(_) => return Err(bad_input()),  // not a valid number
            Err(err) => return Err(err),  // I/O error
        };
        got_content = true;
    }
}

/**
 * Read a positive hexadecimal integer from the given reader.
 *
 * # Arguments
 *
 * - `reader` - the reader to read the hexadecimal digits from (and whatever byte comes next)
 * - `expected_end` - this function is called with the byte that follows the last hexadecimal digit
 *   and should return `true` if that byte is what is expected to follow the number.
 *
 * # Return value
 *
 * - `None`, if the number overflows;
 * - `None`, if there is a leading zero;
 * - `None`, if all the digits are read and the `expected_end` function is false
 *
 * Should everything work as designed (i.e. none of these conditions occur) a `Some` is returned.
 */
pub fn read_hexadecimal<R: Reader, N: Unsigned + NumCast + PartialOrd + CheckedMul + CheckedAdd>
                       (reader: &mut R, expected_end: |u8| -> bool)
                       -> IoResult<N> {
    let mut n: N = Zero::zero();
    let mut got_content = false;
    let sixteen: N = cast(16).unwrap();
    loop {
        n = match reader.read_byte() {
            Ok(b@ASCII_ZERO..ASCII_NINE) => {
                match n.checked_mul(&sixteen).and_then(
                        |n| n.checked_add(&cast(b - ASCII_ZERO).unwrap())) {
                    Some(new_n) => new_n,
                    None => return Err(bad_input()),  // overflow
                }
            },
            Ok(b@ASCII_LOWER_A..ASCII_LOWER_F) => {
                match n.checked_mul(&sixteen).and_then(
                        |n| n.checked_add(&cast(b - ASCII_LOWER_A + 10).unwrap())) {
                    Some(new_n) => new_n,
                    None => return Err(bad_input()),  // overflow
                }
            },
            Ok(b@ASCII_UPPER_A..ASCII_UPPER_F) => {
                match n.checked_mul(&sixteen).and_then(
                        |n| n.checked_add(&cast(b - ASCII_UPPER_A + 10).unwrap())) {
                    Some(new_n) => new_n,
                    None => return Err(bad_input()),  // overflow
                }
            },
            Ok(b) if got_content && expected_end(b) => return Ok(n),
            Ok(_) => return Err(bad_input()),  // not a valid number
            Err(err) => return Err(err),  // I/O error
        };
        got_content = true;
    }
}

/**
 * Read an HTTP-Version (e.g. "HTTP/1.1") from a stream.
 *
 * # Arguments
 *
 * - `reader` - the reader to read the HTTP-Version from (and whatever byte comes next)
 * - `expected_end` - this function is called with the byte that follows the HTTP-Version
 *   and should return `true` if that byte is what is expected to follow the HTTP-Version.
 *
 * # Return value
 *
 * - `None`, if the HTTP-Version is malformed in any way;
 * - `None`, if the `expected_end` function returns false;
 * - A `Some`, if all goes well.
 */
#[inline]
pub fn read_http_version<R: Reader>
                        (reader: &mut R, expected_end: |u8| -> bool)
                        -> IoResult<(uint, uint)> {
    // I'd read into a [0u8, ..5], but that buffer is not guaranteed to be
    // filled, so I must read it byte by byte to guarantee correctness.
    // (Sure, no sane person/library would send the first packet with "HTT"
    // and leave the "P/1.1" to the next packet, but it's *possible*.)
    let b0 = try!(reader.read_byte());
    let b1 = try!(reader.read_byte());
    let b2 = try!(reader.read_byte());
    let b3 = try!(reader.read_byte());
    let b4 = try!(reader.read_byte());
    if (b0 != b'h' && b0 != b'H') ||
       (b1 != b't' && b1 != b'T') ||
       (b2 != b't' && b2 != b'T') ||
       (b3 != b'p' && b3 != b'P') ||
       b4 != b'/' {
        return Err(bad_input());
    }

    let major = try!(read_decimal(reader, |b| b == b'.'));
    let minor = try!(read_decimal(reader, expected_end));
    Ok((major, minor))
}

// I couldn't think what to call it. Ah well. It's just trivial syntax sugar, anyway.
macro_rules! test_reads {
    ($func:ident $($value:expr => $expected:expr),*) => {{
        $(
            assert_eq!(
                concat_idents!(read_, $func)(&mut MemReader::new($value.bytes().collect::<Vec<_>>()),
                                             |b| b == 0).ok(),
                $expected);
        )*
    }}
}

#[test]
fn test_read_http_version() {
    test_reads!(http_version
                "HTTP/25.17\0" => Some((25, 17)),
                "http/1.0\0" => Some((1, 0)),
                "http 1.0\0" => None,
                "HTTP/1.0.\0" => None,
                "HTTP/1.0.\0" => None
    );
}

#[test]
fn test_read_decimal() {
    test_reads!(decimal
                "0\0" => Some(0u),
                "0\0" => Some(0u8),
                "0\0" => Some(0u64),
                "9\0" => Some(9u8),
                "42\0" => Some(42u64),
                "0123456789\0" => Some(123456789u64),
                "00000000000000000000000031\0" => Some(31u64),

                // End of stream in middle of number
                "0" => None::<u64>,

                // No number
                "\0" => None::<u64>,

                // Invalid character
                "0a\0" => None::<u64>,

                // At overflow bounds
                "255\0" => Some(255u8),
                "256\0" => None::<u8>,
                "256\0" => Some(256u16)
    );
}

#[test]
fn test_read_hexadecimal() {
    test_reads!(hexadecimal
                "0\0" => Some(0u),
                "0\0" => Some(0u8),
                "0\0" => Some(0u64),
                "f\0" => Some(0xfu8),
                "42\0" => Some(0x42u64),
                "012345\0" => Some(0x12345u64),
                "00000000000000000000000031\0" => Some(0x31u64),
                "0123456789AbCdEf\0" => Some(0x123456789abcdefu64),

                // End of stream in middle of number
                "0" => None::<u64>,

                // No number
                "\0" => None::<u64>,

                // Invalid character
                "fg\0" => None::<u64>,

                // At overflow bounds
                "ff\0" => Some(0xffu8),
                "100\0" => None::<u8>,
                "100\0" => Some(0x100u16)
    );
}
<commit_msg>(fix) Defined casts explicitly.<commit_after>/*!
 * Poorly categorised functions for reading things used in multiple places.
 *
 * (That is, typically, more than one of request-or-response reading-or-writing.)
 *
 * TODO: refactor all this to store things in more usefully categorised places.
 */
use std::num::{Zero, cast};
use std::io::{IoError, IoResult, OtherIoError};
#[cfg(test)]
use std::io::MemReader;

// XXX: IoError ain't a good representation of this.
fn bad_input() -> IoError {
    IoError {
        kind: OtherIoError,
        desc: "invalid number",
        detail: None,
    }
}

static ASCII_ZERO: u8 = b'0';
static ASCII_NINE: u8 = b'9';
static ASCII_LOWER_A: u8 = b'a';
static ASCII_LOWER_F: u8 = b'f';
static ASCII_UPPER_A: u8 = b'A';
static ASCII_UPPER_F: u8 = b'F';

/**
 * Read a positive decimal integer from the given reader.
 *
 * # Arguments
 *
 * - `reader` - the reader to read the decimal digits from (and whatever byte comes next)
 * - `expected_end` - this function is called with the byte that follows the last decimal digit
 *   and should return `true` if that byte is what is expected to follow the number.
 *
 * # Return value
 *
 * - `None`, if the number overflows;
 * - `None`, if there is a leading zero;
 * - `None`, if all the digits are read and the `expected_end` function is false
 *
 * Should everything work as designed (i.e. none of these conditions occur) a `Some` is returned.
 */
pub fn read_decimal<R: Reader, N: Unsigned + NumCast + PartialOrd + CheckedMul + CheckedAdd>
                   (reader: &mut R, expected_end: |u8| -> bool)
                   -> IoResult<N> {
    // Here and in `read_hexadecimal` there is the possibility of infinite sequence of zeroes. The
    // spec allows this, but it may not be a good thing to allow. It's not a particularly good
    // attack surface, though, because of the low return.
    let mut n: N = Zero::zero();
    let mut got_content = false;
    let ten: N = cast(10u32).unwrap();
    loop {
        n = match reader.read_byte() {
            Ok(b@ASCII_ZERO..ASCII_NINE) => {
                // Written sanely, this is: n * 10 + (b - '0'), but we avoid
                // (semantically unsound) overflow by using checked operations.
                // There is no need in the b - '0' part as it is safe.
                match n.checked_mul(&ten).and_then(
                        |n| n.checked_add(&cast(b - ASCII_ZERO).unwrap())) {
                    Some(new_n) => new_n,
                    None => return Err(bad_input()),  // overflow
                }
            },
            Ok(b) if got_content && expected_end(b) => return Ok(n),
            Ok(_) => return Err(bad_input()),  // not a valid number
            Err(err) => return Err(err),  // I/O error
        };
        got_content = true;
    }
}

/**
 * Read a positive hexadecimal integer from the given reader.
 *
 * # Arguments
 *
 * - `reader` - the reader to read the hexadecimal digits from (and whatever byte comes next)
 * - `expected_end` - this function is called with the byte that follows the last hexadecimal digit
 *   and should return `true` if that byte is what is expected to follow the number.
 *
 * # Return value
 *
 * - `None`, if the number overflows;
 * - `None`, if there is a leading zero;
 * - `None`, if all the digits are read and the `expected_end` function is false
 *
 * Should everything work as designed (i.e. none of these conditions occur) a `Some` is returned.
 */
pub fn read_hexadecimal<R: Reader, N: Unsigned + NumCast + PartialOrd + CheckedMul + CheckedAdd>
                       (reader: &mut R, expected_end: |u8| -> bool)
                       -> IoResult<N> {
    let mut n: N = Zero::zero();
    let mut got_content = false;
    let sixteen: N = cast(16u32).unwrap();
    loop {
        n = match reader.read_byte() {
            Ok(b@ASCII_ZERO..ASCII_NINE) => {
                match n.checked_mul(&sixteen).and_then(
                        |n| n.checked_add(&cast(b - ASCII_ZERO).unwrap())) {
                    Some(new_n) => new_n,
                    None => return Err(bad_input()),  // overflow
                }
            },
            Ok(b@ASCII_LOWER_A..ASCII_LOWER_F) => {
                match n.checked_mul(&sixteen).and_then(
                        |n| n.checked_add(&cast(b - ASCII_LOWER_A + 10).unwrap())) {
                    Some(new_n) => new_n,
                    None => return Err(bad_input()),  // overflow
                }
            },
            Ok(b@ASCII_UPPER_A..ASCII_UPPER_F) => {
                match n.checked_mul(&sixteen).and_then(
                        |n| n.checked_add(&cast(b - ASCII_UPPER_A + 10).unwrap())) {
                    Some(new_n) => new_n,
                    None => return Err(bad_input()),  // overflow
                }
            },
            Ok(b) if got_content && expected_end(b) => return Ok(n),
            Ok(_) => return Err(bad_input()),  // not a valid number
            Err(err) => return Err(err),  // I/O error
        };
        got_content = true;
    }
}

/**
 * Read an HTTP-Version (e.g. "HTTP/1.1") from a stream.
 *
 * # Arguments
 *
 * - `reader` - the reader to read the HTTP-Version from (and whatever byte comes next)
 * - `expected_end` - this function is called with the byte that follows the HTTP-Version
 *   and should return `true` if that byte is what is expected to follow the HTTP-Version.
 *
 * # Return value
 *
 * - `None`, if the HTTP-Version is malformed in any way;
 * - `None`, if the `expected_end` function returns false;
 * - A `Some`, if all goes well.
 */
#[inline]
pub fn read_http_version<R: Reader>
                        (reader: &mut R, expected_end: |u8| -> bool)
                        -> IoResult<(uint, uint)> {
    // I'd read into a [0u8, ..5], but that buffer is not guaranteed to be
    // filled, so I must read it byte by byte to guarantee correctness.
    // (Sure, no sane person/library would send the first packet with "HTT"
    // and leave the "P/1.1" to the next packet, but it's *possible*.)
    let b0 = try!(reader.read_byte());
    let b1 = try!(reader.read_byte());
    let b2 = try!(reader.read_byte());
    let b3 = try!(reader.read_byte());
    let b4 = try!(reader.read_byte());
    if (b0 != b'h' && b0 != b'H') ||
       (b1 != b't' && b1 != b'T') ||
       (b2 != b't' && b2 != b'T') ||
       (b3 != b'p' && b3 != b'P') ||
       b4 != b'/' {
        return Err(bad_input());
    }

    let major = try!(read_decimal(reader, |b| b == b'.'));
    let minor = try!(read_decimal(reader, expected_end));
    Ok((major, minor))
}

// I couldn't think what to call it. Ah well. It's just trivial syntax sugar, anyway.
macro_rules! test_reads {
    ($func:ident $($value:expr => $expected:expr),*) => {{
        $(
            assert_eq!(
                concat_idents!(read_, $func)(&mut MemReader::new($value.bytes().collect::<Vec<_>>()),
                                             |b| b == 0).ok(),
                $expected);
        )*
    }}
}

#[test]
fn test_read_http_version() {
    test_reads!(http_version
                "HTTP/25.17\0" => Some((25, 17)),
                "http/1.0\0" => Some((1, 0)),
                "http 1.0\0" => None,
                "HTTP/1.0.\0" => None,
                "HTTP/1.0.\0" => None
    );
}

#[test]
fn test_read_decimal() {
    test_reads!(decimal
                "0\0" => Some(0u),
                "0\0" => Some(0u8),
                "0\0" => Some(0u64),
                "9\0" => Some(9u8),
                "42\0" => Some(42u64),
                "0123456789\0" => Some(123456789u64),
                "00000000000000000000000031\0" => Some(31u64),

                // End of stream in middle of number
                "0" => None::<u64>,

                // No number
                "\0" => None::<u64>,

                // Invalid character
                "0a\0" => None::<u64>,

                // At overflow bounds
                "255\0" => Some(255u8),
                "256\0" => None::<u8>,
                "256\0" => Some(256u16)
    );
}

#[test]
fn test_read_hexadecimal() {
    test_reads!(hexadecimal
                "0\0" => Some(0u),
                "0\0" => Some(0u8),
                "0\0" => Some(0u64),
                "f\0" => Some(0xfu8),
                "42\0" => Some(0x42u64),
                "012345\0" => Some(0x12345u64),
                "00000000000000000000000031\0" => Some(0x31u64),
                "0123456789AbCdEf\0" => Some(0x123456789abcdefu64),

                // End of stream in middle of number
                "0" => None::<u64>,

                // No number
                "\0" => None::<u64>,

                // Invalid character
                "fg\0" => None::<u64>,

                // At overflow bounds
                "ff\0" => Some(0xffu8),
                "100\0" => None::<u8>,
                "100\0" => Some(0x100u16)
    );
}
<|endoftext|>


<commit_before>
use std::io::SeekFrom;
use std::cmp;

use hyper::{Error, StatusCode};
use hyper::client::Response;
use hyper::{Request, Method};
use hyper::header::ByteRangeSpec;
use hyper::header::ByteRangeSpec::FromTo;
use hyper::header::Range::Bytes;
use hyper::header::{ContentLength, ContentRange, ContentRangeSpec};

use futures::{Future, Stream};
use hyper::Client;
use tokio_core::reactor::Core;
use reader::Reader;
use buffer::Buffer;

fn get_head(filename: &String) -> Result<Response, Error> {
  let mut core = Core::new()?;
  let client = Client::new(&core.handle());

  let uri = filename.parse()?;
  let req = Request::new(Method::Head, uri);

  let work = client.request(req).and_then(|r| {
    Ok(r)
  });
  core.run(work)
}

fn get_data(filename: &String, range: Vec<ByteRangeSpec>) -> Result<Response, Error> {
  let mut core = Core::new()?;
  let client = Client::new(&core.handle());

  let uri = filename.parse()?;
  let mut req = Request::new(Method::Get, uri);
  req.headers_mut().set(Bytes(range));

  let work = client.request(req).and_then(|res| {
    Ok(res)
  });
  core.run(work)
}

fn get_content_length(response: &Response) -> Option<u64> {
  match response.headers().get::<ContentLength>() {
    Some(length) => Some(**length as u64),
    None => None
  }
}

fn get_content_range(response: &Response) -> Result<Option<u64>, String> {
  match response.headers().get::<ContentRange>() {
    Some(content_range) => {
      match content_range.clone() {
        ContentRange(ContentRangeSpec::Bytes{
            range: _range,
            instance_length: length
          }) => {
          Ok(length)
        },
        ContentRange(ContentRangeSpec::Unregistered{unit: _unit, resp: _resp}) => {
          Err("Unregistered, actually unsupported".to_string())
        }
      }
    },
    None =>
      Err("Missing content_range".to_string()),
  }
}

#[derive(Debug)]
pub struct HttpReader {
  pub filename: String,
  pub file_size: Option<u64>,
  pub position: u64,
  pub buffer: Buffer
}

pub fn exists(filename: &String) -> bool {
  match get_head(filename) {
    Ok(resp) => {
      resp.status().is_success()
    },
    Err(_msg) => {
      false
    }
  }
}

fn get_data_range(position: u64, size: usize, max_end_position: Option<u64>) -> Vec<ByteRangeSpec> {
  let start = position;
  let end =
    match (position, size) {
      (0, 0) => 0,
      (_, _) => {
        match max_end_position {
          Some(max) => {
            let max_size = max - position;
            position + cmp::min((size - 1) as u64, max_size)
          },
          None => position + (size - 1) as u64,
        }
      },
    };

  vec![FromTo(start, end)]
}

fn load_data(reader: &mut HttpReader, size: usize) -> Result<Option<Vec<u8>>, String> {

  info!("make HTTP request with request {:?} bytes", size);

  let position =
    match reader.buffer.size {
      Some(_) => reader.buffer.position,
      None => reader.position,
    };

  match reader.file_size {
    Some(size) => {
      if position >= size {
        return Ok(None)
      }
    },
    None => (),
  };

  let range = get_data_range(position, size, reader.buffer.max_end_position);
  let response = get_data(&reader.filename, range).unwrap();

  if response.status() != StatusCode::Ok {
    return Err("Bad request".to_string());
  }

  let loaded_size =
    match get_content_length(&response) {
      Some(content_length) => {
        content_length
      },
      None => return Err("Missing content length".to_string()),
    };

  match get_content_range(&response) {
    Ok(length) => {
      reader.file_size = length
    },
    Err(msg) => return Err(msg),
  };

  match loaded_size {
    0 => Err("Bad request range".to_string()),
    _ => {
      let body_data =
        match response.body().concat2().wait() {
          Ok(body) => body.to_vec(),
          Err(_msg) => vec![],
        };

      let new_position = position + body_data.len() as u64;
      match reader.buffer.size {
        Some(_) => {
          reader.buffer.position = new_position;
        },
        None => {
          reader.position = new_position;
        }
      };
      Ok(Some(body_data))
    }
  }
}

impl Reader for HttpReader {
  fn open(filename: &String) -> HttpReader {
    match get_head(filename) {
      Err(msg) => panic!(msg.to_string()),
      Ok(response) => {
        let content_length = get_content_length(&response);

        HttpReader {
          filename: filename.to_string(),
          file_size: content_length,
          position: 0,
          buffer: Buffer {
            size: None,
            position: 0,
            max_end_position: None,
            buffer: vec![]
          }
        }
      }
    }
  }

  fn get_position(&mut self) -> Result<u64, String> {
    Ok(self.position)
  }

  fn get_cache_size(&self) -> Option<usize> {
    self.buffer.size
  }

  fn set_cache_size(&mut self, cache_size: Option<usize>) {
    self.buffer.size = cache_size;
  }

  fn get_max_end_position(&self) -> Option<u64> {
    self.buffer.max_end_position
  }

  fn set_max_end_position(&mut self, max_end_position: Option<u64>) {
    self.buffer.max_end_position = max_end_position;
  }

  fn get_size(&mut self) -> Result<u64, String> {
    match self.file_size {
        Some(length) => Ok(length),
        None => Err("No length detected".to_string()),
    }
  }

  fn read(&mut self, size: usize) -> Result<Vec<u8>, String> {
    if self.buffer.get_cached_size() >= size {
      self.position += size as u64;
      Ok(self.buffer.get_data(size))
    } else {
      match self.buffer.size {
        Some(buffer_size) => {
          match load_data(self, buffer_size) {
            Err(msg) => Err(msg),
            Ok(some_data) => {
              match some_data {
                Some(data) => {
                  self.buffer.append_data(&data.to_vec());
                  self.position += size as u64;
                  Ok(self.buffer.get_data(size))
                },
                None => Ok(Vec::new()),
              }
            }
          }
        },
        None => {
          match load_data(self, size) {
            Err(msg) => Err(msg),
            Ok(some_data) => {
              match some_data {
                Some(data) => {
                  // println!("{:?} vs {:?}", data.len(), size);
                  match data.len() >= size {
                    true => {
                      Ok(data)
                    },
                    false => Ok(Vec::new()),
                  }
                },
                None => Ok(Vec::new()),
              }
            }
          }
        }
      }
    }
  }

  fn seek(&mut self, seek: SeekFrom) -> Result<u64, String> {
    match seek {
      SeekFrom::Current(offset) => {
        self.position = self.position + offset as u64;
        if self.buffer.size.is_some() {
          if offset > 0 && self.buffer.get_cached_size() > offset as usize {
            let _skiped_data = self.buffer.get_data(offset as usize);
          } else {
            self.buffer.reset();
          }
        }
      },
      SeekFrom::Start(offset) => {
        self.buffer.reset();
        self.position = offset;
        if self.buffer.size.is_some() {
          self.buffer.position = self.position;
        }
      },
      SeekFrom::End(offset) => {
        self.buffer.reset();
        match self.file_size {
          Some(size) => {
            self.position = size - offset as u64;
            if self.buffer.size.is_some() {
              self.buffer.position = self.position;
            }
          },
          None => return Err("Missing file size".to_string())
        }
      }
    }
    Ok(self.position)
  }
}
<commit_msg>use core to get full body with chuncked content<commit_after>
use std::io::SeekFrom;
use std::cmp;

use hyper::{Error, StatusCode};
use hyper::client::Response;
use hyper::{Request, Method};
use hyper::header::ByteRangeSpec;
use hyper::header::ByteRangeSpec::FromTo;
use hyper::header::Range::Bytes;
use hyper::header::{ContentLength, ContentRange, ContentRangeSpec};

use futures::{Future, Stream};
use hyper::Client;
use tokio_core::reactor::Core;
use reader::Reader;
use buffer::Buffer;

fn get_head(filename: &String) -> Result<Response, Error> {
  let mut core = Core::new()?;
  let client = Client::new(&core.handle());

  let uri = filename.parse()?;
  let req = Request::new(Method::Head, uri);

  let work = client.request(req).and_then(|r| {
    Ok(r)
  });
  core.run(work)
}

fn get_data(filename: &String, range: Vec<ByteRangeSpec>) -> Result<Response, Error> {
  let mut core = Core::new()?;
  let client = Client::new(&core.handle());

  let uri = filename.parse()?;
  let mut req = Request::new(Method::Get, uri);
  req.headers_mut().set(Bytes(range));

  let work = client.request(req).and_then(|res| {
    Ok(res)
  });
  core.run(work)
}

fn get_content_length(response: &Response) -> Option<u64> {
  match response.headers().get::<ContentLength>() {
    Some(length) => Some(**length as u64),
    None => None
  }
}

fn get_content_range(response: &Response) -> Result<Option<u64>, String> {
  match response.headers().get::<ContentRange>() {
    Some(content_range) => {
      match content_range.clone() {
        ContentRange(ContentRangeSpec::Bytes{
            range: _range,
            instance_length: length
          }) => {
          Ok(length)
        },
        ContentRange(ContentRangeSpec::Unregistered{unit: _unit, resp: _resp}) => {
          Err("Unregistered, actually unsupported".to_string())
        }
      }
    },
    None =>
      Err("Missing content_range".to_string()),
  }
}

#[derive(Debug)]
pub struct HttpReader {
  pub filename: String,
  pub file_size: Option<u64>,
  pub position: u64,
  pub buffer: Buffer
}

pub fn exists(filename: &String) -> bool {
  match get_head(filename) {
    Ok(resp) => {
      resp.status().is_success()
    },
    Err(_msg) => {
      false
    }
  }
}

fn get_data_range(position: u64, size: usize, max_end_position: Option<u64>) -> Vec<ByteRangeSpec> {
  let start = position;
  let end =
    match (position, size) {
      (0, 0) => 0,
      (_, _) => {
        match max_end_position {
          Some(max) => {
            let max_size = max - position;
            position + cmp::min((size - 1) as u64, max_size)
          },
          None => position + (size - 1) as u64,
        }
      },
    };

  vec![FromTo(start, end)]
}

fn load_data(reader: &mut HttpReader, size: usize) -> Result<Option<Vec<u8>>, String> {

  info!("make HTTP request with request {:?} bytes", size);

  let position =
    match reader.buffer.size {
      Some(_) => reader.buffer.position,
      None => reader.position,
    };

  match reader.file_size {
    Some(size) => {
      if position >= size {
        return Ok(None)
      }
    },
    None => (),
  };

  let range = get_data_range(position, size, reader.buffer.max_end_position);
  let response = get_data(&reader.filename, range).unwrap();

  if response.status() != StatusCode::Ok {
    return Err("Bad request".to_string());
  }

  let loaded_size =
    match get_content_length(&response) {
      Some(content_length) => {
        content_length
      },
      None => return Err("Missing content length".to_string()),
    };

  match get_content_range(&response) {
    Ok(length) => {
      reader.file_size = length
    },
    Err(msg) => return Err(msg),
  };

  match loaded_size {
    0 => Err("Bad request range".to_string()),
    _ => {
      let mut core = Core::new().unwrap();
      let body_data =
        match core.run(response.body().concat2()) {
          Ok(body) => body.to_vec(),
          Err(_msg) => vec![],
        };

      let new_position = position + body_data.len() as u64;
      match reader.buffer.size {
        Some(_) => {
          reader.buffer.position = new_position;
        },
        None => {
          reader.position = new_position;
        }
      };
      Ok(Some(body_data))
    }
  }
}

impl Reader for HttpReader {
  fn open(filename: &String) -> HttpReader {
    match get_head(filename) {
      Err(msg) => panic!(msg.to_string()),
      Ok(response) => {
        let content_length = get_content_length(&response);

        HttpReader {
          filename: filename.to_string(),
          file_size: content_length,
          position: 0,
          buffer: Buffer {
            size: None,
            position: 0,
            max_end_position: None,
            buffer: vec![]
          }
        }
      }
    }
  }

  fn get_position(&mut self) -> Result<u64, String> {
    Ok(self.position)
  }

  fn get_cache_size(&self) -> Option<usize> {
    self.buffer.size
  }

  fn set_cache_size(&mut self, cache_size: Option<usize>) {
    self.buffer.size = cache_size;
  }

  fn get_max_end_position(&self) -> Option<u64> {
    self.buffer.max_end_position
  }

  fn set_max_end_position(&mut self, max_end_position: Option<u64>) {
    self.buffer.max_end_position = max_end_position;
  }

  fn get_size(&mut self) -> Result<u64, String> {
    match self.file_size {
        Some(length) => Ok(length),
        None => Err("No length detected".to_string()),
    }
  }

  fn read(&mut self, size: usize) -> Result<Vec<u8>, String> {
    if self.buffer.get_cached_size() >= size {
      self.position += size as u64;
      Ok(self.buffer.get_data(size))
    } else {
      match self.buffer.size {
        Some(buffer_size) => {
          match load_data(self, buffer_size) {
            Err(msg) => Err(msg),
            Ok(some_data) => {
              match some_data {
                Some(data) => {
                  self.buffer.append_data(&data.to_vec());
                  self.position += size as u64;
                  Ok(self.buffer.get_data(size))
                },
                None => Ok(Vec::new()),
              }
            }
          }
        },
        None => {
          match load_data(self, size) {
            Err(msg) => Err(msg),
            Ok(some_data) => {
              match some_data {
                Some(data) => {
                  // println!("{:?} vs {:?}", data.len(), size);
                  match data.len() >= size {
                    true => {
                      Ok(data)
                    },
                    false => Ok(Vec::new()),
                  }
                },
                None => Ok(Vec::new()),
              }
            }
          }
        }
      }
    }
  }

  fn seek(&mut self, seek: SeekFrom) -> Result<u64, String> {
    match seek {
      SeekFrom::Current(offset) => {
        self.position = self.position + offset as u64;
        if self.buffer.size.is_some() {
          if offset > 0 && self.buffer.get_cached_size() > offset as usize {
            let _skiped_data = self.buffer.get_data(offset as usize);
          } else {
            self.buffer.reset();
          }
        }
      },
      SeekFrom::Start(offset) => {
        self.buffer.reset();
        self.position = offset;
        if self.buffer.size.is_some() {
          self.buffer.position = self.position;
        }
      },
      SeekFrom::End(offset) => {
        self.buffer.reset();
        match self.file_size {
          Some(size) => {
            self.position = size - offset as u64;
            if self.buffer.size.is_some() {
              self.buffer.position = self.position;
            }
          },
          None => return Err("Missing file size".to_string())
        }
      }
    }
    Ok(self.position)
  }
}
<|endoftext|>


<commit_before>use cmp;
use super::super::{Iterator, DoubleEndedIterator, ExactSizeIterator, FusedIterator, TrustedLen};

/// An iterator that iterates two other iterators simultaneously.
///
/// This `struct` is created by the [`zip`] method on [`Iterator`]. See its
/// documentation for more.
///
/// [`zip`]: trait.Iterator.html#method.zip
/// [`Iterator`]: trait.Iterator.html
#[derive(Clone, Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Zip<A, B> {
    a: A,
    b: B,
    // index and len are only used by the specialized version of zip
    index: usize,
    len: usize,
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A, B> Iterator for Zip<A, B> where A: Iterator, B: Iterator
{
    type Item = (A::Item, B::Item);

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        ZipImpl::next(self)
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        ZipImpl::size_hint(self)
    }

    #[inline]
    fn nth(&mut self, n: usize) -> Option<Self::Item> {
        ZipImpl::nth(self, n)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A, B> DoubleEndedIterator for Zip<A, B> where
    A: DoubleEndedIterator + ExactSizeIterator,
    B: DoubleEndedIterator + ExactSizeIterator,
{
    #[inline]
    fn next_back(&mut self) -> Option<(A::Item, B::Item)> {
        ZipImpl::next_back(self)
    }
}

// Zip specialization trait
#[doc(hidden)]
pub(in super::super) trait ZipImpl<A, B> {
    type Item;
    fn new(a: A, b: B) -> Self;
    fn next(&mut self) -> Option<Self::Item>;
    fn size_hint(&self) -> (usize, Option<usize>);
    fn nth(&mut self, n: usize) -> Option<Self::Item>;
    fn super_nth(&mut self, mut n: usize) -> Option<Self::Item> {
        while let Some(x) = self.next() {
            if n == 0 { return Some(x) }
            n -= 1;
        }
        None
    }
    fn next_back(&mut self) -> Option<Self::Item>
        where A: DoubleEndedIterator + ExactSizeIterator,
              B: DoubleEndedIterator + ExactSizeIterator;
}

// General Zip impl
#[doc(hidden)]
impl<A, B> ZipImpl<A, B> for Zip<A, B>
    where A: Iterator, B: Iterator
{
    type Item = (A::Item, B::Item);
    default fn new(a: A, b: B) -> Self {
        Zip {
            a,
            b,
            index: 0, // unused
            len: 0, // unused
        }
    }

    #[inline]
    default fn next(&mut self) -> Option<(A::Item, B::Item)> {
        self.a.next().and_then(|x| {
            self.b.next().and_then(|y| {
                Some((x, y))
            })
        })
    }

    #[inline]
    default fn nth(&mut self, n: usize) -> Option<Self::Item> {
        self.super_nth(n)
    }

    #[inline]
    default fn next_back(&mut self) -> Option<(A::Item, B::Item)>
        where A: DoubleEndedIterator + ExactSizeIterator,
              B: DoubleEndedIterator + ExactSizeIterator
    {
        let a_sz = self.a.len();
        let b_sz = self.b.len();
        if a_sz != b_sz {
            // Adjust a, b to equal length
            if a_sz > b_sz {
                for _ in 0..a_sz - b_sz { self.a.next_back(); }
            } else {
                for _ in 0..b_sz - a_sz { self.b.next_back(); }
            }
        }
        match (self.a.next_back(), self.b.next_back()) {
            (Some(x), Some(y)) => Some((x, y)),
            (None, None) => None,
            _ => unreachable!(),
        }
    }

    #[inline]
    default fn size_hint(&self) -> (usize, Option<usize>) {
        let (a_lower, a_upper) = self.a.size_hint();
        let (b_lower, b_upper) = self.b.size_hint();

        let lower = cmp::min(a_lower, b_lower);

        let upper = match (a_upper, b_upper) {
            (Some(x), Some(y)) => Some(cmp::min(x,y)),
            (Some(x), None) => Some(x),
            (None, Some(y)) => Some(y),
            (None, None) => None
        };

        (lower, upper)
    }
}

#[doc(hidden)]
impl<A, B> ZipImpl<A, B> for Zip<A, B>
    where A: TrustedRandomAccess, B: TrustedRandomAccess
{
    fn new(a: A, b: B) -> Self {
        let len = cmp::min(a.len(), b.len());
        Zip {
            a,
            b,
            index: 0,
            len,
        }
    }

    #[inline]
    fn next(&mut self) -> Option<(A::Item, B::Item)> {
        if self.index < self.len {
            let i = self.index;
            self.index += 1;
            unsafe {
                Some((self.a.get_unchecked(i), self.b.get_unchecked(i)))
            }
        } else if A::may_have_side_effect() && self.index < self.a.len() {
            // match the base implementation's potential side effects
            unsafe {
                self.a.get_unchecked(self.index);
            }
            self.index += 1;
            None
        } else {
            None
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let len = self.len - self.index;
        (len, Some(len))
    }

    #[inline]
    fn nth(&mut self, n: usize) -> Option<Self::Item> {
        let delta = cmp::min(n, self.len - self.index);
        let end = self.index + delta;
        while self.index < end {
            let i = self.index;
            self.index += 1;
            if A::may_have_side_effect() {
                unsafe { self.a.get_unchecked(i); }
            }
            if B::may_have_side_effect() {
                unsafe { self.b.get_unchecked(i); }
            }
        }

        self.super_nth(n - delta)
    }

    #[inline]
    fn next_back(&mut self) -> Option<(A::Item, B::Item)>
        where A: DoubleEndedIterator + ExactSizeIterator,
              B: DoubleEndedIterator + ExactSizeIterator
    {
        // Adjust a, b to equal length
        if A::may_have_side_effect() {
            let sz = self.a.len();
            if sz > self.len {
                for _ in 0..sz - cmp::max(self.len, self.index) {
                    self.a.next_back();
                }
            }
        }
        if B::may_have_side_effect() {
            let sz = self.b.len();
            if sz > self.len {
                for _ in 0..sz - self.len {
                    self.b.next_back();
                }
            }
        }
        if self.index < self.len {
            self.len -= 1;
            let i = self.len;
            unsafe {
                Some((self.a.get_unchecked(i), self.b.get_unchecked(i)))
            }
        } else {
            None
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A, B> ExactSizeIterator for Zip<A, B>
    where A: ExactSizeIterator, B: ExactSizeIterator {}

#[doc(hidden)]
unsafe impl<A, B> TrustedRandomAccess for Zip<A, B>
    where A: TrustedRandomAccess,
          B: TrustedRandomAccess,
{
    unsafe fn get_unchecked(&mut self, i: usize) -> (A::Item, B::Item) {
        (self.a.get_unchecked(i), self.b.get_unchecked(i))
    }

    fn may_have_side_effect() -> bool {
        A::may_have_side_effect() || B::may_have_side_effect()
    }
}

#[stable(feature = "fused", since = "1.26.0")]
impl<A, B> FusedIterator for Zip<A, B>
    where A: FusedIterator, B: FusedIterator, {}

#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<A, B> TrustedLen for Zip<A, B>
    where A: TrustedLen, B: TrustedLen,
{}

/// An iterator whose items are random-accessible efficiently
///
/// # Safety
///
/// The iterator's .len() and size_hint() must be exact.
/// `.len()` must be cheap to call.
///
/// .get_unchecked() must return distinct mutable references for distinct
/// indices (if applicable), and must return a valid reference if index is in
/// 0..self.len().
pub(crate) unsafe trait TrustedRandomAccess : ExactSizeIterator {
    unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item;
    /// Returns `true` if getting an iterator element may have
    /// side effects. Remember to take inner iterators into account.
    fn may_have_side_effect() -> bool;
}
<commit_msg>Move super_nth out of ZipImpl<commit_after>use cmp;
use super::super::{Iterator, DoubleEndedIterator, ExactSizeIterator, FusedIterator, TrustedLen};

/// An iterator that iterates two other iterators simultaneously.
///
/// This `struct` is created by the [`zip`] method on [`Iterator`]. See its
/// documentation for more.
///
/// [`zip`]: trait.Iterator.html#method.zip
/// [`Iterator`]: trait.Iterator.html
#[derive(Clone, Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Zip<A, B> {
    a: A,
    b: B,
    // index and len are only used by the specialized version of zip
    index: usize,
    len: usize,
}
impl<A: Iterator, B: Iterator> Zip<A, B> {
    fn super_nth(&mut self, mut n: usize) -> Option<(A::Item, B::Item)> {
        while let Some(x) = Iterator::next(self) {
            if n == 0 { return Some(x) }
            n -= 1;
        }
        None
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A, B> Iterator for Zip<A, B> where A: Iterator, B: Iterator
{
    type Item = (A::Item, B::Item);

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        ZipImpl::next(self)
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        ZipImpl::size_hint(self)
    }

    #[inline]
    fn nth(&mut self, n: usize) -> Option<Self::Item> {
        ZipImpl::nth(self, n)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A, B> DoubleEndedIterator for Zip<A, B> where
    A: DoubleEndedIterator + ExactSizeIterator,
    B: DoubleEndedIterator + ExactSizeIterator,
{
    #[inline]
    fn next_back(&mut self) -> Option<(A::Item, B::Item)> {
        ZipImpl::next_back(self)
    }
}

// Zip specialization trait
#[doc(hidden)]
pub(in super::super) trait ZipImpl<A, B> {
    type Item;
    fn new(a: A, b: B) -> Self;
    fn next(&mut self) -> Option<Self::Item>;
    fn size_hint(&self) -> (usize, Option<usize>);
    fn nth(&mut self, n: usize) -> Option<Self::Item>;
    fn next_back(&mut self) -> Option<Self::Item>
        where A: DoubleEndedIterator + ExactSizeIterator,
              B: DoubleEndedIterator + ExactSizeIterator;
}

// General Zip impl
#[doc(hidden)]
impl<A, B> ZipImpl<A, B> for Zip<A, B>
    where A: Iterator, B: Iterator
{
    type Item = (A::Item, B::Item);
    default fn new(a: A, b: B) -> Self {
        Zip {
            a,
            b,
            index: 0, // unused
            len: 0, // unused
        }
    }

    #[inline]
    default fn next(&mut self) -> Option<(A::Item, B::Item)> {
        self.a.next().and_then(|x| {
            self.b.next().and_then(|y| {
                Some((x, y))
            })
        })
    }

    #[inline]
    default fn nth(&mut self, n: usize) -> Option<Self::Item> {
        self.super_nth(n)
    }

    #[inline]
    default fn next_back(&mut self) -> Option<(A::Item, B::Item)>
        where A: DoubleEndedIterator + ExactSizeIterator,
              B: DoubleEndedIterator + ExactSizeIterator
    {
        let a_sz = self.a.len();
        let b_sz = self.b.len();
        if a_sz != b_sz {
            // Adjust a, b to equal length
            if a_sz > b_sz {
                for _ in 0..a_sz - b_sz { self.a.next_back(); }
            } else {
                for _ in 0..b_sz - a_sz { self.b.next_back(); }
            }
        }
        match (self.a.next_back(), self.b.next_back()) {
            (Some(x), Some(y)) => Some((x, y)),
            (None, None) => None,
            _ => unreachable!(),
        }
    }

    #[inline]
    default fn size_hint(&self) -> (usize, Option<usize>) {
        let (a_lower, a_upper) = self.a.size_hint();
        let (b_lower, b_upper) = self.b.size_hint();

        let lower = cmp::min(a_lower, b_lower);

        let upper = match (a_upper, b_upper) {
            (Some(x), Some(y)) => Some(cmp::min(x,y)),
            (Some(x), None) => Some(x),
            (None, Some(y)) => Some(y),
            (None, None) => None
        };

        (lower, upper)
    }
}

#[doc(hidden)]
impl<A, B> ZipImpl<A, B> for Zip<A, B>
    where A: TrustedRandomAccess, B: TrustedRandomAccess
{
    fn new(a: A, b: B) -> Self {
        let len = cmp::min(a.len(), b.len());
        Zip {
            a,
            b,
            index: 0,
            len,
        }
    }

    #[inline]
    fn next(&mut self) -> Option<(A::Item, B::Item)> {
        if self.index < self.len {
            let i = self.index;
            self.index += 1;
            unsafe {
                Some((self.a.get_unchecked(i), self.b.get_unchecked(i)))
            }
        } else if A::may_have_side_effect() && self.index < self.a.len() {
            // match the base implementation's potential side effects
            unsafe {
                self.a.get_unchecked(self.index);
            }
            self.index += 1;
            None
        } else {
            None
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let len = self.len - self.index;
        (len, Some(len))
    }

    #[inline]
    fn nth(&mut self, n: usize) -> Option<Self::Item> {
        let delta = cmp::min(n, self.len - self.index);
        let end = self.index + delta;
        while self.index < end {
            let i = self.index;
            self.index += 1;
            if A::may_have_side_effect() {
                unsafe { self.a.get_unchecked(i); }
            }
            if B::may_have_side_effect() {
                unsafe { self.b.get_unchecked(i); }
            }
        }

        self.super_nth(n - delta)
    }

    #[inline]
    fn next_back(&mut self) -> Option<(A::Item, B::Item)>
        where A: DoubleEndedIterator + ExactSizeIterator,
              B: DoubleEndedIterator + ExactSizeIterator
    {
        // Adjust a, b to equal length
        if A::may_have_side_effect() {
            let sz = self.a.len();
            if sz > self.len {
                for _ in 0..sz - cmp::max(self.len, self.index) {
                    self.a.next_back();
                }
            }
        }
        if B::may_have_side_effect() {
            let sz = self.b.len();
            if sz > self.len {
                for _ in 0..sz - self.len {
                    self.b.next_back();
                }
            }
        }
        if self.index < self.len {
            self.len -= 1;
            let i = self.len;
            unsafe {
                Some((self.a.get_unchecked(i), self.b.get_unchecked(i)))
            }
        } else {
            None
        }
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<A, B> ExactSizeIterator for Zip<A, B>
    where A: ExactSizeIterator, B: ExactSizeIterator {}

#[doc(hidden)]
unsafe impl<A, B> TrustedRandomAccess for Zip<A, B>
    where A: TrustedRandomAccess,
          B: TrustedRandomAccess,
{
    unsafe fn get_unchecked(&mut self, i: usize) -> (A::Item, B::Item) {
        (self.a.get_unchecked(i), self.b.get_unchecked(i))
    }

    fn may_have_side_effect() -> bool {
        A::may_have_side_effect() || B::may_have_side_effect()
    }
}

#[stable(feature = "fused", since = "1.26.0")]
impl<A, B> FusedIterator for Zip<A, B>
    where A: FusedIterator, B: FusedIterator, {}

#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<A, B> TrustedLen for Zip<A, B>
    where A: TrustedLen, B: TrustedLen,
{}

/// An iterator whose items are random-accessible efficiently
///
/// # Safety
///
/// The iterator's .len() and size_hint() must be exact.
/// `.len()` must be cheap to call.
///
/// .get_unchecked() must return distinct mutable references for distinct
/// indices (if applicable), and must return a valid reference if index is in
/// 0..self.len().
pub(crate) unsafe trait TrustedRandomAccess : ExactSizeIterator {
    unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item;
    /// Returns `true` if getting an iterator element may have
    /// side effects. Remember to take inner iterators into account.
    fn may_have_side_effect() -> bool;
}
<|endoftext|>