<commit_before>use snake;
use game_state;
use player;

pub static NUMBER_OF_LAYERS: uint = 4;
pub static WATER_COLOR: [f32, ..4] = [0.0, 0.0, 0.45, 1.0];
pub static WATER_FRICTION: f64 = 0.2;
pub static RADIUS: f64 = 0.1;
pub static SURFACE_Y: f64 = 10.0;
pub static INITIAL_GAME_STATE: game_state::GameState = game_state::Play;
pub static INITIAL_CAMERA_POS: [f64, ..2] = [0.0, 0.0];
pub static CAMERA_FOLLOW_PERCENTAGE: f64 = 0.2;

pub static YOU_WIN_TEXT: &'static str = "you win";
pub static YOU_WIN_TEXT_COLOR: [f32, ..4] = GREEN;
pub static YOU_WIN_POS: [f64, ..2] = [-0.42, 0.1];

pub static YOU_LOOSE_TEXT: &'static str = "you loose";
pub static YOU_LOOSE_TEXT_COLOR: [f32, ..4] = RED;
pub static YOU_LOOSE_POS: [f64, ..2] = [-0.55, 0.1];

pub static AIR_BOTTLE_RADIUS: f64 = 0.1;
pub static AIR_BOTTLE_TEST_COLOR: [f32, ..4] = WHITE;
pub static AIR_BOTTLE_FILL_UP: f64 = 0.5;
pub static AIR_BOTTLE_POS: &'static [f64] = &[
    0.5, 0.3,
    0.3, 0.8,
    0.6, 1.4,
];

pub static PLAYER_LOOSE_AIR_SPEED: f64 = 0.1;
pub static PLAYER_COLOR: [f32, ..4] = YELLOW;
pub static PLAYER_BITTEN_COLOR: [f32, ..4] = RED;
pub static PLAYER_INITIAL_BLOOD: f64 = 1.0;
pub static PLAYER_INITIAL_AIR: f64 = 1.0;
pub static PLAYER_RADIUS: f64 = 0.1;
pub static PLAYER_ACCELERATION_RIGHT: f64 = 0.1;
pub static PLAYER_ACCELERATION_LEFT: f64 = 0.1;
pub static PLAYER_ACCELERATION_UP: f64 = 0.1;
pub static PLAYER_ACCELERATION_DOWN: f64 = 0.1;
pub static PLAYER_INITIAL_TWEEN_FACTOR: f64 = 0.0;
pub static PLAYER_TWEEN_SPEED: f64 = 1.0;
pub static PLAYER_BITTEN_FADE_OUT_SECONDS: f64 = 2.0;
pub static PLAYER_INITIAL_STATE: player::PlayerState = player::Normal;

pub struct SnakeSettings {
    pub acceleration_left: f64,
    pub acceleration_right: f64,
    pub acceleration_up: f64,
    pub acceleration_down: f64,
    pub initial_state: snake::SnakeState,
    pub bite_damage: f64,
    pub sensor_distance: f64,
    pub attack_distance: f64,
    pub wait_seconds_before_initial_attack: f64,
    pub wait_seconds_before_repeat_attack: f64,
    pub test_color: [f32, ..4],
    pub radius: f64,
}

pub static SNAKE_TAIL_COLOR: [f32, ..4] = [0.8, 0.6, 0.6, 0.4];

pub static SNAKE_TAIL_DISTANCE: f64 = 0.01;
pub static SNAKE_OVERALL_ACCELERATION: f64 = 2.0;
pub static SNAKE_SETTINGS: SnakeSettings = SnakeSettings {
    acceleration_left: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_right: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_up: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_down: 0.1 * SNAKE_OVERALL_ACCELERATION,
    initial_state: snake::Ignorant,
    bite_damage: 0.1,
    sensor_distance: 2.0,
    attack_distance: 2.0 * PLAYER_RADIUS,
    wait_seconds_before_initial_attack: 1.0,
    wait_seconds_before_repeat_attack: 1.0,
    test_color: BLACK,
    radius: 0.03,
};

pub static SNAKE_SPEED_RIGHT: f64 = 0.1;
pub static SNAKE_SPEED_LEFT: f64 = 0.1;
pub static SNAKE_SPEED_UP: f64 = 0.1;
pub static SNAKE_SPEED_DOWN: f64 = 0.1;

// snake 1.
pub static SNAKE_1_SETTINGS: SnakeSettings = SNAKE_SETTINGS;
pub static SNAKE_1_POS: [f64, ..2] = [-0.8, 0.8];
pub static SNAKE_1_ADD: bool = true;

// snake 2.
pub static SNAKE_2_SETTINGS: SnakeSettings = SNAKE_SETTINGS;
pub static SNAKE_2_POS: [f64, ..2] = [0.8, 0.8];
pub static SNAKE_2_ADD: bool = true;

pub static BAR_RECTANGLE: [f64, ..4] = [0.3, -0.06, 1.5, 0.05];
pub static BAR_MARGIN: f64 = 0.01;
pub static BAR_TEXT_COLOR: [f32, ..4] = WHITE;

pub static AIR_BAR_INITIAL_VALUE: f64 = 0.5;
pub static AIR_BAR_POS: [f64, ..2] = [-0.9, 0.95];
pub static AIR_BAR_TEXT_COLOR: [f32, ..4] = BAR_TEXT_COLOR;
pub static AIR_BAR_BACKGROUND_COLOR: [f32, ..4] = DARK_BLUE;
pub static AIR_BAR_BAR_COLOR: [f32, ..4] = LIGHT_BLUE;

pub static BLOOD_BAR_INITIAL_VALUE: f64 = 0.5;
pub static BLOOD_BAR_POS: [f64, ..2] = [-0.9, 0.85];
pub static BLOOD_BAR_TEXT_COLOR: [f32, ..4] = BAR_TEXT_COLOR;
pub static BLOOD_BAR_BACKGROUND_COLOR: [f32, ..4] = DARK_RED;
pub static BLOOD_BAR_BAR_COLOR: [f32, ..4] = LIGHT_RED;

pub static BAR_BACKGROUND_HEIGHT: f64 = 0.275;
pub static BAR_BACKGROUND_COLOR: [f32, ..4] = [0.1, 0.1, 0.1, 0.9];
pub static BAR_BACKGROUND_COLOR_2: [f32, ..4] = [0.4, 0.4, 0.4, 0.8];
pub static BAR_BACKGROUND_MARGIN: f64 = 0.01;
pub static BAR_BACKGROUND_MARGIN_2: f64 = 0.02;

pub static BLACK: [f32, ..4] = [0.0, 0.0, 0.0, 1.0];
pub static WHITE: [f32, ..4] = [1.0, 1.0, 1.0, 1.0];
pub static GRAY: [f32, ..4] = [0.5, 0.5, 0.5, 1.0];
pub static DARK_GRAY: [f32, ..4] = [0.2, 0.2, 0.2, 1.0];

pub static RED: [f32, ..4] = [1.0, 0.0, 0.0, 1.0];
pub static DARK_RED: [f32, ..4] = [0.5, 0.0, 0.0, 1.0];
pub static LIGHT_RED: [f32, ..4] = [1.0, 0.0, 0.0, 1.0];

pub static GREEN: [f32, ..4] = [0.0, 1.0, 0.0, 1.0];

pub static BLUE: [f32, ..4] = [0.0, 0.0, 1.0, 1.0];
pub static DARK_BLUE: [f32, ..4] = [0.0, 0.0, 0.5, 1.0];
pub static LIGHT_BLUE: [f32, ..4] = [0.5, 0.5, 1.0, 1.0];

pub static YELLOW: [f32, ..4] = [0.0, 1.0, 1.0, 1.0];

pub static ORIGIN: [f64, ..2] = [0.0, 0.0];

<commit_msg>Made camera follow player better<commit_after>use snake;
use game_state;
use player;

pub static NUMBER_OF_LAYERS: uint = 4;
pub static WATER_COLOR: [f32, ..4] = [0.0, 0.0, 0.45, 1.0];
pub static WATER_FRICTION: f64 = 0.2;
pub static RADIUS: f64 = 0.1;
pub static SURFACE_Y: f64 = 10.0;
pub static INITIAL_GAME_STATE: game_state::GameState = game_state::Play;
pub static INITIAL_CAMERA_POS: [f64, ..2] = [0.0, 0.0];
pub static CAMERA_FOLLOW_PERCENTAGE: f64 = 0.4;

pub static YOU_WIN_TEXT: &'static str = "you win";
pub static YOU_WIN_TEXT_COLOR: [f32, ..4] = GREEN;
pub static YOU_WIN_POS: [f64, ..2] = [-0.42, 0.1];

pub static YOU_LOOSE_TEXT: &'static str = "you loose";
pub static YOU_LOOSE_TEXT_COLOR: [f32, ..4] = RED;
pub static YOU_LOOSE_POS: [f64, ..2] = [-0.55, 0.1];

pub static AIR_BOTTLE_RADIUS: f64 = 0.1;
pub static AIR_BOTTLE_TEST_COLOR: [f32, ..4] = WHITE;
pub static AIR_BOTTLE_FILL_UP: f64 = 0.5;
pub static AIR_BOTTLE_POS: &'static [f64] = &[
    0.5, 0.3,
    0.3, 0.8,
    0.6, 1.4,
];

pub static PLAYER_LOOSE_AIR_SPEED: f64 = 0.1;
pub static PLAYER_COLOR: [f32, ..4] = YELLOW;
pub static PLAYER_BITTEN_COLOR: [f32, ..4] = RED;
pub static PLAYER_INITIAL_BLOOD: f64 = 1.0;
pub static PLAYER_INITIAL_AIR: f64 = 1.0;
pub static PLAYER_RADIUS: f64 = 0.1;
pub static PLAYER_ACCELERATION_RIGHT: f64 = 0.1;
pub static PLAYER_ACCELERATION_LEFT: f64 = 0.1;
pub static PLAYER_ACCELERATION_UP: f64 = 0.1;
pub static PLAYER_ACCELERATION_DOWN: f64 = 0.1;
pub static PLAYER_INITIAL_TWEEN_FACTOR: f64 = 0.0;
pub static PLAYER_TWEEN_SPEED: f64 = 1.0;
pub static PLAYER_BITTEN_FADE_OUT_SECONDS: f64 = 2.0;
pub static PLAYER_INITIAL_STATE: player::PlayerState = player::Normal;

pub struct SnakeSettings {
    pub acceleration_left: f64,
    pub acceleration_right: f64,
    pub acceleration_up: f64,
    pub acceleration_down: f64,
    pub initial_state: snake::SnakeState,
    pub bite_damage: f64,
    pub sensor_distance: f64,
    pub attack_distance: f64,
    pub wait_seconds_before_initial_attack: f64,
    pub wait_seconds_before_repeat_attack: f64,
    pub test_color: [f32, ..4],
    pub radius: f64,
}

pub static SNAKE_TAIL_COLOR: [f32, ..4] = [0.8, 0.6, 0.6, 0.4];

pub static SNAKE_TAIL_DISTANCE: f64 = 0.01;
pub static SNAKE_OVERALL_ACCELERATION: f64 = 2.0;
pub static SNAKE_SETTINGS: SnakeSettings = SnakeSettings {
    acceleration_left: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_right: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_up: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_down: 0.1 * SNAKE_OVERALL_ACCELERATION,
    initial_state: snake::Ignorant,
    bite_damage: 0.1,
    sensor_distance: 2.0,
    attack_distance: 2.0 * PLAYER_RADIUS,
    wait_seconds_before_initial_attack: 1.0,
    wait_seconds_before_repeat_attack: 1.0,
    test_color: BLACK,
    radius: 0.03,
};

pub static SNAKE_SPEED_RIGHT: f64 = 0.1;
pub static SNAKE_SPEED_LEFT: f64 = 0.1;
pub static SNAKE_SPEED_UP: f64 = 0.1;
pub static SNAKE_SPEED_DOWN: f64 = 0.1;

// snake 1.
pub static SNAKE_1_SETTINGS: SnakeSettings = SNAKE_SETTINGS;
pub static SNAKE_1_POS: [f64, ..2] = [-0.8, 0.8];
pub static SNAKE_1_ADD: bool = true;

// snake 2.
pub static SNAKE_2_SETTINGS: SnakeSettings = SNAKE_SETTINGS;
pub static SNAKE_2_POS: [f64, ..2] = [0.8, 0.8];
pub static SNAKE_2_ADD: bool = true;

pub static BAR_RECTANGLE: [f64, ..4] = [0.3, -0.06, 1.5, 0.05];
pub static BAR_MARGIN: f64 = 0.01;
pub static BAR_TEXT_COLOR: [f32, ..4] = WHITE;

pub static AIR_BAR_INITIAL_VALUE: f64 = 0.5;
pub static AIR_BAR_POS: [f64, ..2] = [-0.9, 0.95];
pub static AIR_BAR_TEXT_COLOR: [f32, ..4] = BAR_TEXT_COLOR;
pub static AIR_BAR_BACKGROUND_COLOR: [f32, ..4] = DARK_BLUE;
pub static AIR_BAR_BAR_COLOR: [f32, ..4] = LIGHT_BLUE;

pub static BLOOD_BAR_INITIAL_VALUE: f64 = 0.5;
pub static BLOOD_BAR_POS: [f64, ..2] = [-0.9, 0.85];
pub static BLOOD_BAR_TEXT_COLOR: [f32, ..4] = BAR_TEXT_COLOR;
pub static BLOOD_BAR_BACKGROUND_COLOR: [f32, ..4] = DARK_RED;
pub static BLOOD_BAR_BAR_COLOR: [f32, ..4] = LIGHT_RED;

pub static BAR_BACKGROUND_HEIGHT: f64 = 0.275;
pub static BAR_BACKGROUND_COLOR: [f32, ..4] = [0.1, 0.1, 0.1, 0.9];
pub static BAR_BACKGROUND_COLOR_2: [f32, ..4] = [0.4, 0.4, 0.4, 0.8];
pub static BAR_BACKGROUND_MARGIN: f64 = 0.01;
pub static BAR_BACKGROUND_MARGIN_2: f64 = 0.02;

pub static BLACK: [f32, ..4] = [0.0, 0.0, 0.0, 1.0];
pub static WHITE: [f32, ..4] = [1.0, 1.0, 1.0, 1.0];
pub static GRAY: [f32, ..4] = [0.5, 0.5, 0.5, 1.0];
pub static DARK_GRAY: [f32, ..4] = [0.2, 0.2, 0.2, 1.0];

pub static RED: [f32, ..4] = [1.0, 0.0, 0.0, 1.0];
pub static DARK_RED: [f32, ..4] = [0.5, 0.0, 0.0, 1.0];
pub static LIGHT_RED: [f32, ..4] = [1.0, 0.0, 0.0, 1.0];

pub static GREEN: [f32, ..4] = [0.0, 1.0, 0.0, 1.0];

pub static BLUE: [f32, ..4] = [0.0, 0.0, 1.0, 1.0];
pub static DARK_BLUE: [f32, ..4] = [0.0, 0.0, 0.5, 1.0];
pub static LIGHT_BLUE: [f32, ..4] = [0.5, 0.5, 1.0, 1.0];

pub static YELLOW: [f32, ..4] = [0.0, 1.0, 1.0, 1.0];

pub static ORIGIN: [f64, ..2] = [0.0, 0.0];

<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fs::File;
use std::io::Read;

use crate::stacked_borrows::Tag;
use crate::*;

struct FileHandle {
    file: File,
    flag: i32,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        if flag != this.eval_libc_i32("O_RDONLY")? && flag != this.eval_libc_i32("O_CLOEXEC")? {
            throw_unsup_format!("Unsupported flag {:#x}", flag);
        }

        let path_bytes = this
            .memory()
            .read_c_str(this.read_scalar(path_op)?.not_undef()?)?;
        let path = std::str::from_utf8(path_bytes)
            .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", path_bytes))?
            .to_owned();
        let fd = File::open(&path).map(|file| {
            let mut fh = &mut this.machine.file_handler;
            fh.low += 1;
            fh.handles.insert(fh.low, FileHandle{ file, flag});
            fh.low
        });

        this.consume_result::<i32>(fd, -1)
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        arg_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;

        if cmd == this.eval_libc_i32("F_SETFD")? {
            // This does not affect the file itself. Certain flags might require changing the file
            // or the way it is accessed somehow.
            let flag = this.read_scalar(arg_op.unwrap())?.to_i32()?;
            // The only usage of this in stdlib at the moment is to enable the `FD_CLOEXEC` flag.
            let fd_cloexec = this.eval_libc_i32("FD_CLOEXEC")?;
            if let Some(FileHandle{ flag: old_flag, .. }) = this.machine.file_handler.handles.get_mut(&fd) {
                if flag ^ *old_flag == fd_cloexec {
                    *old_flag = flag;
                } else {
                    throw_unsup_format!("Unsupported arg {:#x} for `F_SETFD`", flag);
                }
            }
            Ok(0)
        } else if cmd == this.eval_libc_i32("F_GETFD")? {
            if let Some(handle) = this.machine.file_handler.handles.get(&fd) {
                Ok(handle.flag)
            } else {
                this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
                Ok(-1)
            }
        } else {
            throw_unsup_format!("Unsupported command {:#x}", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            this.consume_result::<i32>(handle.file.sync_all().map(|_| 0), -1)
        } else {
            this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
            Ok(-1)
        }
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let tcx = &{ this.tcx.tcx };

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.force_ptr(this.read_scalar(buf_op)?.not_undef()?)?;
        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;

        let mut bytes = vec![0; count as usize];

        let read_result = if let Some(FileHandle { file, ..}) = this.machine.file_handler.handles.get_mut(&fd) {
            file.read(&mut bytes).map(|bytes| bytes as i64)
        } else {
            this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
            return Ok(-1);
        };

        let read_bytes = this.consume_result::<i64>(read_result, -1)?;
        if read_bytes != -1 {
            bytes.truncate(read_bytes as usize);
            this.memory_mut()
                .get_mut(buf.alloc_id)?
                .write_bytes(tcx, buf, &bytes)?;
        }
        Ok(read_bytes)
    }

    fn consume_result<T>(&mut self, result: std::io::Result<T>, t: T) -> InterpResult<'tcx, T> {
        match result {
            Ok(ok) => Ok(ok),
            Err(e) => {
                self.eval_context_mut().machine.last_error = e.raw_os_error().unwrap() as u32;
                Ok(t)
            }
        }
    }
}
<commit_msg>Avoid buffer allocation to read files<commit_after>use std::collections::HashMap;
use std::fs::File;
use std::io::Read;

use rustc::ty::layout::Size;

use crate::stacked_borrows::Tag;
use crate::*;

struct FileHandle {
    file: File,
    flag: i32,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        if flag != this.eval_libc_i32("O_RDONLY")? && flag != this.eval_libc_i32("O_CLOEXEC")? {
            throw_unsup_format!("Unsupported flag {:#x}", flag);
        }

        let path_bytes = this
            .memory()
            .read_c_str(this.read_scalar(path_op)?.not_undef()?)?;
        let path = std::str::from_utf8(path_bytes)
            .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", path_bytes))?
            .to_owned();
        let fd = File::open(&path).map(|file| {
            let mut fh = &mut this.machine.file_handler;
            fh.low += 1;
            fh.handles.insert(fh.low, FileHandle { file, flag });
            fh.low
        });

        this.consume_result::<i32>(fd, -1)
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        arg_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;

        if cmd == this.eval_libc_i32("F_SETFD")? {
            // This does not affect the file itself. Certain flags might require changing the file
            // or the way it is accessed somehow.
            let flag = this.read_scalar(arg_op.unwrap())?.to_i32()?;
            // The only usage of this in stdlib at the moment is to enable the `FD_CLOEXEC` flag.
            let fd_cloexec = this.eval_libc_i32("FD_CLOEXEC")?;
            if let Some(FileHandle { flag: old_flag, .. }) =
                this.machine.file_handler.handles.get_mut(&fd)
            {
                if flag ^ *old_flag == fd_cloexec {
                    *old_flag = flag;
                } else {
                    throw_unsup_format!("Unsupported arg {:#x} for `F_SETFD`", flag);
                }
            }
            Ok(0)
        } else if cmd == this.eval_libc_i32("F_GETFD")? {
            if let Some(handle) = this.machine.file_handler.handles.get(&fd) {
                Ok(handle.flag)
            } else {
                this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
                Ok(-1)
            }
        } else {
            throw_unsup_format!("Unsupported command {:#x}", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            this.consume_result::<i32>(handle.file.sync_all().map(|_| 0), -1)
        } else {
            this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
            Ok(-1)
        }
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        if !this.machine.communicate {
            throw_unsup_format!("`open` not available when isolation is enabled")
        }

        let tcx = &{ this.tcx.tcx };

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.force_ptr(this.read_scalar(buf_op)?.not_undef()?)?;
        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;

        // Remove the file handle to avoid borrowing issues
        if let Some(mut handle) = this.machine.file_handler.handles.remove(&fd) {
            let bytes = handle
                .file
                .read(this.memory_mut().get_mut(buf.alloc_id)?.get_bytes_mut(
                    tcx,
                    buf,
                    Size::from_bytes(count),
                )?)
                .map(|bytes| bytes as i64);
            // Reinsert the file handle
            this.machine.file_handler.handles.insert(fd, handle);
            this.consume_result::<i64>(bytes, -1)
        } else {
            this.machine.last_error = this.eval_libc_i32("EBADF")? as u32;
            Ok(-1)
        }
    }

    fn consume_result<T>(&mut self, result: std::io::Result<T>, t: T) -> InterpResult<'tcx, T> {
        match result {
            Ok(ok) => Ok(ok),
            Err(e) => {
                self.eval_context_mut().machine.last_error = e.raw_os_error().unwrap() as u32;
                Ok(t)
            }
        }
    }
}
<|endoftext|>


<commit_before>// SureTree

use Result;

use flate2::{self, Compression, FlateReadExt};
use std::collections::BTreeMap;
use std::os::unix::ffi::OsStringExt;
use std::ffi::OsString;
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, BufReader, BufWriter};
use std::path::{Path, PathBuf};

use super::escape::*;

pub type AttMap = BTreeMap<String, String>;

/// Represents a single directory entity.  The `String` values (name, or
/// att properties) are `Escape` encoded, when they represent a name in the
/// filesystem.
#[derive(Debug)]
pub struct SureTree {
    pub name: String,
    pub atts: AttMap,
    pub children: Vec<SureTree>,
    pub files: Vec<SureFile>,
}

#[derive(Debug)]
pub struct SureFile {
    pub name: String,
    pub atts: AttMap,
}

impl SureTree {
    /// Load a sure tree from a standard gzip compressed surefile.
    pub fn load<P: AsRef<Path>>(name: P) -> Result<SureTree> {
        let rd = File::open(name)?;
        let rd = rd.gz_decode()?;
        Self::load_from(rd)
    }

    /// Load a sure tree from the given reader.
    pub fn load_from<R: Read>(rd: R) -> Result<SureTree> {
        let rd = BufReader::new(rd);
        let mut lines = rd.split('\n' as u8);

        fixed(&mut lines, b"asure-2.0")?;
        fixed(&mut lines, b"-----")?;

        let first = Self::get_line(&mut lines)?;
        Self::subload(first, &mut lines)
    }

    fn subload<B: BufRead>(first: Vec<u8>, mut inp: &mut io::Split<B>) -> Result<SureTree> {
        let (name, atts) = decode_entity(&first[1..]);
        let mut children = vec![];

        let mut line = Self::get_line(inp)?;
        loop {
            if line[0] != 'd' as u8 {
                break;
            }
            let tree = Self::subload(line, &mut inp)?;
            children.push(tree);
            line = Self::get_line(&mut inp)?;
        }

        if line != &['-' as u8] {
            return Err(From::from("surefile missing '-' marker'"));
        }

        let mut files = vec![];
        line = Self::get_line(inp)?;
        loop {
            if line[0] != 'f' as u8 {
                break;
            }
            let (fname, fatts) = decode_entity(&line[1..]);
            files.push(SureFile {
                name: fname,
                atts: fatts,
            });
            line = Self::get_line(inp)?;
        }

        if line != &['u' as u8] {
            return Err(From::from("surefile missing 'u' marker'"));
        }

        Ok(SureTree {
            name: name,
            atts: atts,
            children: children,
            files: files,
        })
    }

    fn get_line<B: BufRead>(mut inp: &mut io::Split<B>) -> Result<Vec<u8>> {
        match inp.next() {
            None => return Err(From::from("surefile is truncated")),
            Some(l) => Ok(l?),
        }
    }

    pub fn count_nodes(&self) -> usize {
        self.children.iter().fold(
            0,
            |acc, item| acc + item.count_nodes(),
        ) + self.files.len()
    }

    /// Write a sure tree to a standard gzipped file of the given name.
    pub fn save<P: AsRef<Path>>(&self, name: P) -> Result<()> {
        let wr = File::create(name)?;
        let wr = flate2::write::GzEncoder::new(wr, Compression::Default);
        self.save_to(wr)
    }

    /// Write a sure tree to the given writer.
    pub fn save_to<W: Write>(&self, wr: W) -> Result<()> {
        let mut wr = BufWriter::new(wr); // Benchmark with and without, gz might buffer.

        writeln!(&mut wr, "asure-2.0")?;
        writeln!(&mut wr, "-----")?;

        self.walk(&mut wr)
    }

    fn walk<W: Write>(&self, out: &mut W) -> Result<()> {
        self.header(out, 'd', &self.name, &self.atts)?;
        for child in &self.children {
            child.walk(out)?;
        }
        writeln!(out, "-")?;
        for child in &self.files {
            self.header(out, 'f', &child.name, &child.atts)?;
        }
        writeln!(out, "u")?;
        Ok(())
    }

    fn header<W: Write>(&self, out: &mut W, kind: char, name: &str, atts: &AttMap) -> Result<()> {
        write!(out, "{}{} [", kind, name)?;

        // BTrees are sorted.
        for (k, v) in atts {
            write!(out, "{} {} ", k, v)?;
        }
        writeln!(out, "]")?;
        Ok(())
    }
}

// TODO: These should return Result to handle errors.
fn decode_entity(text: &[u8]) -> (String, AttMap) {
    let (name, mut text) = get_delim(text, ' ');
    trace!(
        "name = '{:?}' ('{:?}')",
        name,
        String::from_utf8_lossy(&text)
    );
    assert!(text[0] == '[' as u8);
    text = &text[1..];

    let mut atts = AttMap::new();
    while text[0] != ']' as u8 {
        let (key, t2) = get_delim(text, ' ');
        let (value, t2) = get_delim(t2, ' ');
        trace!("  {} = {}", key, value);
        text = t2;

        atts.insert(key, value);
    }

    (name, atts)
}

fn get_delim(text: &[u8], delim: char) -> (String, &[u8]) {
    let mut it = text.iter();
    let space = it.position(|&s| s == delim as u8).unwrap();
    (
        String::from_utf8(text[..space].to_owned()).unwrap(),
        &text[space + 1..],
    )
}

fn fixed<I>(inp: &mut I, exp: &[u8]) -> Result<()>
where
    I: Iterator<Item = io::Result<Vec<u8>>>,
{
    match inp.next() {
        Some(Ok(ref text)) if &text[..] == exp => Ok(()),
        Some(Ok(ref text)) => Err(From::from(format!(
            "Unexpected line: '{}', expect '{}'",
            String::from_utf8_lossy(text),
            String::from_utf8_lossy(exp)
        ))),
        Some(Err(e)) => Err(From::from(format!("Error reading surefile: {}", e))),
        None => Err(From::from(format!("Unexpected eof on surefile"))),
    }
}

/// Files and trees both have names.  These names are escaped.
pub trait Named {
    // Return the escaped name of this entity.
    fn get_name(&self) -> &str;
}

impl Named for SureTree {
    fn get_name(&self) -> &str {
        &self.name
    }
}

impl Named for SureFile {
    fn get_name(&self) -> &str {
        &self.name
    }
}

/// Tree and file nodes can add themselves to a path.
pub trait PathAdd {
    /// Given an existing path, add the component of this entity to that
    /// path, and return the resulting PathBuf.
    fn join(&self, path: &Path) -> PathBuf;
}

impl<T: Named> PathAdd for T {
    fn join(&self, path: &Path) -> PathBuf {
        let s: OsString = OsStringExt::from_vec(self.get_name().unescape().unwrap());
        path.join(&s)
    }
}

// Provide for strings as well, assuming they are also escaped.
impl PathAdd for str {
    fn join(&self, path: &Path) -> PathBuf {
        let s: OsString = OsStringExt::from_vec(self.unescape().unwrap());
        path.join(&s)
    }
}
<commit_msg>Remove unneeded 'mut'<commit_after>// SureTree

use Result;

use flate2::{self, Compression, FlateReadExt};
use std::collections::BTreeMap;
use std::os::unix::ffi::OsStringExt;
use std::ffi::OsString;
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, BufReader, BufWriter};
use std::path::{Path, PathBuf};

use super::escape::*;

pub type AttMap = BTreeMap<String, String>;

/// Represents a single directory entity.  The `String` values (name, or
/// att properties) are `Escape` encoded, when they represent a name in the
/// filesystem.
#[derive(Debug)]
pub struct SureTree {
    pub name: String,
    pub atts: AttMap,
    pub children: Vec<SureTree>,
    pub files: Vec<SureFile>,
}

#[derive(Debug)]
pub struct SureFile {
    pub name: String,
    pub atts: AttMap,
}

impl SureTree {
    /// Load a sure tree from a standard gzip compressed surefile.
    pub fn load<P: AsRef<Path>>(name: P) -> Result<SureTree> {
        let rd = File::open(name)?;
        let rd = rd.gz_decode()?;
        Self::load_from(rd)
    }

    /// Load a sure tree from the given reader.
    pub fn load_from<R: Read>(rd: R) -> Result<SureTree> {
        let rd = BufReader::new(rd);
        let mut lines = rd.split('\n' as u8);

        fixed(&mut lines, b"asure-2.0")?;
        fixed(&mut lines, b"-----")?;

        let first = Self::get_line(&mut lines)?;
        Self::subload(first, &mut lines)
    }

    fn subload<B: BufRead>(first: Vec<u8>, mut inp: &mut io::Split<B>) -> Result<SureTree> {
        let (name, atts) = decode_entity(&first[1..]);
        let mut children = vec![];

        let mut line = Self::get_line(inp)?;
        loop {
            if line[0] != 'd' as u8 {
                break;
            }
            let tree = Self::subload(line, &mut inp)?;
            children.push(tree);
            line = Self::get_line(&mut inp)?;
        }

        if line != &['-' as u8] {
            return Err(From::from("surefile missing '-' marker'"));
        }

        let mut files = vec![];
        line = Self::get_line(inp)?;
        loop {
            if line[0] != 'f' as u8 {
                break;
            }
            let (fname, fatts) = decode_entity(&line[1..]);
            files.push(SureFile {
                name: fname,
                atts: fatts,
            });
            line = Self::get_line(inp)?;
        }

        if line != &['u' as u8] {
            return Err(From::from("surefile missing 'u' marker'"));
        }

        Ok(SureTree {
            name: name,
            atts: atts,
            children: children,
            files: files,
        })
    }

    fn get_line<B: BufRead>(inp: &mut io::Split<B>) -> Result<Vec<u8>> {
        match inp.next() {
            None => return Err(From::from("surefile is truncated")),
            Some(l) => Ok(l?),
        }
    }

    pub fn count_nodes(&self) -> usize {
        self.children.iter().fold(
            0,
            |acc, item| acc + item.count_nodes(),
        ) + self.files.len()
    }

    /// Write a sure tree to a standard gzipped file of the given name.
    pub fn save<P: AsRef<Path>>(&self, name: P) -> Result<()> {
        let wr = File::create(name)?;
        let wr = flate2::write::GzEncoder::new(wr, Compression::Default);
        self.save_to(wr)
    }

    /// Write a sure tree to the given writer.
    pub fn save_to<W: Write>(&self, wr: W) -> Result<()> {
        let mut wr = BufWriter::new(wr); // Benchmark with and without, gz might buffer.

        writeln!(&mut wr, "asure-2.0")?;
        writeln!(&mut wr, "-----")?;

        self.walk(&mut wr)
    }

    fn walk<W: Write>(&self, out: &mut W) -> Result<()> {
        self.header(out, 'd', &self.name, &self.atts)?;
        for child in &self.children {
            child.walk(out)?;
        }
        writeln!(out, "-")?;
        for child in &self.files {
            self.header(out, 'f', &child.name, &child.atts)?;
        }
        writeln!(out, "u")?;
        Ok(())
    }

    fn header<W: Write>(&self, out: &mut W, kind: char, name: &str, atts: &AttMap) -> Result<()> {
        write!(out, "{}{} [", kind, name)?;

        // BTrees are sorted.
        for (k, v) in atts {
            write!(out, "{} {} ", k, v)?;
        }
        writeln!(out, "]")?;
        Ok(())
    }
}

// TODO: These should return Result to handle errors.
fn decode_entity(text: &[u8]) -> (String, AttMap) {
    let (name, mut text) = get_delim(text, ' ');
    trace!(
        "name = '{:?}' ('{:?}')",
        name,
        String::from_utf8_lossy(&text)
    );
    assert!(text[0] == '[' as u8);
    text = &text[1..];

    let mut atts = AttMap::new();
    while text[0] != ']' as u8 {
        let (key, t2) = get_delim(text, ' ');
        let (value, t2) = get_delim(t2, ' ');
        trace!("  {} = {}", key, value);
        text = t2;

        atts.insert(key, value);
    }

    (name, atts)
}

fn get_delim(text: &[u8], delim: char) -> (String, &[u8]) {
    let mut it = text.iter();
    let space = it.position(|&s| s == delim as u8).unwrap();
    (
        String::from_utf8(text[..space].to_owned()).unwrap(),
        &text[space + 1..],
    )
}

fn fixed<I>(inp: &mut I, exp: &[u8]) -> Result<()>
where
    I: Iterator<Item = io::Result<Vec<u8>>>,
{
    match inp.next() {
        Some(Ok(ref text)) if &text[..] == exp => Ok(()),
        Some(Ok(ref text)) => Err(From::from(format!(
            "Unexpected line: '{}', expect '{}'",
            String::from_utf8_lossy(text),
            String::from_utf8_lossy(exp)
        ))),
        Some(Err(e)) => Err(From::from(format!("Error reading surefile: {}", e))),
        None => Err(From::from(format!("Unexpected eof on surefile"))),
    }
}

/// Files and trees both have names.  These names are escaped.
pub trait Named {
    // Return the escaped name of this entity.
    fn get_name(&self) -> &str;
}

impl Named for SureTree {
    fn get_name(&self) -> &str {
        &self.name
    }
}

impl Named for SureFile {
    fn get_name(&self) -> &str {
        &self.name
    }
}

/// Tree and file nodes can add themselves to a path.
pub trait PathAdd {
    /// Given an existing path, add the component of this entity to that
    /// path, and return the resulting PathBuf.
    fn join(&self, path: &Path) -> PathBuf;
}

impl<T: Named> PathAdd for T {
    fn join(&self, path: &Path) -> PathBuf {
        let s: OsString = OsStringExt::from_vec(self.get_name().unescape().unwrap());
        path.join(&s)
    }
}

// Provide for strings as well, assuming they are also escaped.
impl PathAdd for str {
    fn join(&self, path: &Path) -> PathBuf {
        let s: OsString = OsStringExt::from_vec(self.unescape().unwrap());
        path.join(&s)
    }
}
<|endoftext|>


<commit_before>use {Error, Result};
#[cfg(not(target_os = "android"))]
use NixPath;
use errno::Errno;
#[cfg(not(target_os = "android"))]
use fcntl::OFlag;
use libc::{self, c_int, c_void, size_t, off_t};
#[cfg(not(target_os = "android"))]
use sys::stat::Mode;
use std::os::unix::io::RawFd;

libc_bitflags!{
    /// Desired memory protection of a memory mapping.
    pub struct ProtFlags: c_int {
        /// Pages cannot be accessed.
        PROT_NONE;
        /// Pages can be read.
        PROT_READ;
        /// Pages can be written.
        PROT_WRITE;
        /// Pages can be executed
        PROT_EXEC;
        /// Apply protection up to the end of a mapping that grows upwards.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        PROT_GROWSDOWN;
        /// Apply protection down to the beginning of a mapping that grows downwards.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        PROT_GROWSUP;
    }
}

libc_bitflags!{
    /// Additional parameters for `mmap()`.
    pub struct MapFlags: c_int {
        /// Compatibility flag. Ignored.
        MAP_FILE;
        /// Share this mapping. Mutually exclusive with `MAP_PRIVATE`.
        MAP_SHARED;
        /// Create a private copy-on-write mapping. Mutually exclusive with `MAP_SHARED`.
        MAP_PRIVATE;
        /// Place the mapping at exactly the address specified in `addr`.
        MAP_FIXED;
        /// Synonym for `MAP_ANONYMOUS`.
        MAP_ANON;
        /// The mapping is not backed by any file.
        #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
        MAP_ANONYMOUS;
        /// Put the mapping into the first 2GB of the process address space.
        #[cfg(any(all(any(target_os = "android", target_os = "linux"),
                      any(target_arch = "x86", target_arch = "x86_64")),
                  all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_pointer_width = "64")),
                  all(target_os = "freebsd", target_pointer_width = "64")))]
        MAP_32BIT;
        /// Used for stacks; indicates to the kernel that the mapping should extend downward in memory.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MAP_GROWSDOWN;
        /// Compatibility flag. Ignored.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MAP_DENYWRITE;
        /// Compatibility flag. Ignored.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MAP_EXECUTABLE;
        /// Mark the mmaped region to be locked in the same way as `mlock(2)`.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MAP_LOCKED;
        /// Do not reserve swap space for this mapping.
        ///
        /// This was removed in FreeBSD 11.
        #[cfg(not(target_os = "freebsd"))]
        MAP_NORESERVE;
        /// Populate page tables for a mapping.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MAP_POPULATE;
        /// Only meaningful when used with `MAP_POPULATE`. Don't perform read-ahead.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MAP_NONBLOCK;
        /// Allocate the mapping using "huge pages."
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MAP_HUGETLB;
        /// Lock the mapped region into memory as with `mlock(2)`.
        #[cfg(target_os = "netbsd")]
        MAP_WIRED;
        /// Causes dirtied data in the specified range to be flushed to disk only when necessary.
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
        MAP_NOSYNC;
        /// Rename private pages to a file.
        ///
        /// This was removed in FreeBSD 11.
        #[cfg(any(target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))]
        MAP_RENAME;
        /// Region may contain semaphores.
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
        MAP_HASSEMAPHORE;
        /// Region grows down, like a stack.
        #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
        MAP_STACK;
        /// Pages in this mapping are not retained in the kernel's memory cache.
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MAP_NOCACHE;
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MAP_JIT;
    }
}

libc_enum!{
    /// Usage information for a range of memory to allow for performance optimizations by the kernel.
    ///
    /// Used by [`madvise`](./fn.madvise.html).
    #[repr(i32)]
    pub enum MmapAdvise {
        /// No further special treatment. This is the default.
        MADV_NORMAL,
        /// Expect random page references.
        MADV_RANDOM,
        /// Expect sequential page references.
        MADV_SEQUENTIAL,
        /// Expect access in the near future.
        MADV_WILLNEED,
        /// Do not expect access in the near future.
        MADV_DONTNEED,
        /// Free up a given range of pages and its associated backing store.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_REMOVE,
        /// Do not make pages in this range available to the child after a `fork(2)`.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_DONTFORK,
        /// Undo the effect of `MADV_DONTFORK`.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_DOFORK,
        /// Poison the given pages.
        ///
        /// Subsequent references to those pages are treated like hardware memory corruption.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_HWPOISON,
        /// Enable Kernel Samepage Merging (KSM) for the given pages.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_MERGEABLE,
        /// Undo the effect of `MADV_MERGEABLE`
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_UNMERGEABLE,
        /// Preserve the memory of each page but offline the original page.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_SOFT_OFFLINE,
        /// Enable Transparent Huge Pages (THP) for pages in the given range.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_HUGEPAGE,
        /// Undo the effect of `MADV_HUGEPAGE`.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_NOHUGEPAGE,
        /// Exclude the given range from a core dump.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_DONTDUMP,
        /// Undo the effect of an earlier `MADV_DONTDUMP`.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_DODUMP,
        /// Specify that the application no longer needs the pages in the given range.
        MADV_FREE,
        /// Request that the system not flush the current range to disk unless it needs to.
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
        MADV_NOSYNC,
        /// Undoes the effects of `MADV_NOSYNC` for any future pages dirtied within the given range.
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
        MADV_AUTOSYNC,
        /// Region is not included in a core file.
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
        MADV_NOCORE,
        /// Include region in a core file
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
        MADV_CORE,
        #[cfg(any(target_os = "freebsd"))]
        MADV_PROTECT,
        /// Invalidate the hardware page table for the given region.
        #[cfg(target_os = "dragonfly")]
        MADV_INVAL,
        /// Set the offset of the page directory page to `value` for the virtual page table.
        #[cfg(target_os = "dragonfly")]
        MADV_SETMAP,
        /// Indicates that the application will not need the data in the given range.
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MADV_ZERO_WIRED_PAGES,
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MADV_FREE_REUSABLE,
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MADV_FREE_REUSE,
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MADV_CAN_REUSE,
    }
}

libc_bitflags!{
    /// Configuration flags for `msync`.
    pub struct MsFlags: c_int {
        /// Schedule an update but return immediately.
        MS_ASYNC;
        /// Invalidate all cached data.
        MS_INVALIDATE;
        /// Invalidate pages, but leave them mapped.
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MS_KILLPAGES;
        /// Deactivate pages, but leave them mapped.
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MS_DEACTIVATE;
        /// Perform an update and wait for it to complete.
        MS_SYNC;
    }
}

libc_bitflags!{
    /// Flags for `mlockall`.
    pub struct MlockAllFlags: c_int {
        /// Lock pages that are currently mapped into the address space of the process.
        MCL_CURRENT;
        /// Lock pages which will become mapped into the address space of the process in the future.
        MCL_FUTURE;
    }
}

/// Locks all memory pages that contain part of the address range with `length` bytes starting at
/// `addr`. Locked pages never move to the swap area.
pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> {
    Errno::result(libc::mlock(addr, length)).map(drop)
}

/// Unlocks all memory pages that contain part of the address range with `length` bytes starting at
/// `addr`.
pub unsafe fn munlock(addr: *const c_void, length: size_t) -> Result<()> {
    Errno::result(libc::munlock(addr, length)).map(drop)
}

/// Locks all memory pages mapped into this process' address space. Locked pages never move to the
/// swap area.
pub fn mlockall(flags: MlockAllFlags) -> Result<()> {
    unsafe { Errno::result(libc::mlockall(flags.bits())) }.map(drop)
}

/// Unlocks all memory pages mapped into this process' address space.
pub fn munlockall() -> Result<()> {
    unsafe { Errno::result(libc::munlockall()) }.map(drop)
}

/// Calls to mmap are inherently unsafe, so they must be made in an unsafe block. Typically
/// a higher-level abstraction will hide the unsafe interactions with the mmap'd region.
pub unsafe fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: MapFlags, fd: RawFd, offset: off_t) -> Result<*mut c_void> {
    let ret = libc::mmap(addr, length, prot.bits(), flags.bits(), fd, offset);

    if ret == libc::MAP_FAILED {
        Err(Error::Sys(Errno::last()))
    } else {
        Ok(ret)
    }
}

pub unsafe fn munmap(addr: *mut c_void, len: size_t) -> Result<()> {
    Errno::result(libc::munmap(addr, len)).map(drop)
}

pub unsafe fn madvise(addr: *mut c_void, length: size_t, advise: MmapAdvise) -> Result<()> {
    Errno::result(libc::madvise(addr, length, advise as i32)).map(drop)
}

pub unsafe fn msync(addr: *mut c_void, length: size_t, flags: MsFlags) -> Result<()> {
    Errno::result(libc::msync(addr, length, flags.bits())).map(drop)
}

#[cfg(not(target_os = "android"))]
pub fn shm_open<P: ?Sized + NixPath>(name: &P, flag: OFlag, mode: Mode) -> Result<RawFd> {
    let ret = try!(name.with_nix_path(|cstr| {
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        unsafe {
            libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::c_uint)
        }
        #[cfg(not(any(target_os = "macos", target_os = "ios")))]
        unsafe {
            libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::mode_t)
        }
    }));

    Errno::result(ret)
}

#[cfg(not(target_os = "android"))]
pub fn shm_unlink<P: ?Sized + NixPath>(name: &P) -> Result<()> {
    let ret = try!(name.with_nix_path(|cstr| {
        unsafe { libc::shm_unlink(cstr.as_ptr()) }
    }));

    Errno::result(ret).map(drop)
}
<commit_msg>Fix compilation on aarch64-unknown-linux-musl<commit_after>use {Error, Result};
#[cfg(not(target_os = "android"))]
use NixPath;
use errno::Errno;
#[cfg(not(target_os = "android"))]
use fcntl::OFlag;
use libc::{self, c_int, c_void, size_t, off_t};
#[cfg(not(target_os = "android"))]
use sys::stat::Mode;
use std::os::unix::io::RawFd;

libc_bitflags!{
    /// Desired memory protection of a memory mapping.
    pub struct ProtFlags: c_int {
        /// Pages cannot be accessed.
        PROT_NONE;
        /// Pages can be read.
        PROT_READ;
        /// Pages can be written.
        PROT_WRITE;
        /// Pages can be executed
        PROT_EXEC;
        /// Apply protection up to the end of a mapping that grows upwards.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        PROT_GROWSDOWN;
        /// Apply protection down to the beginning of a mapping that grows downwards.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        PROT_GROWSUP;
    }
}

libc_bitflags!{
    /// Additional parameters for `mmap()`.
    pub struct MapFlags: c_int {
        /// Compatibility flag. Ignored.
        MAP_FILE;
        /// Share this mapping. Mutually exclusive with `MAP_PRIVATE`.
        MAP_SHARED;
        /// Create a private copy-on-write mapping. Mutually exclusive with `MAP_SHARED`.
        MAP_PRIVATE;
        /// Place the mapping at exactly the address specified in `addr`.
        MAP_FIXED;
        /// Synonym for `MAP_ANONYMOUS`.
        MAP_ANON;
        /// The mapping is not backed by any file.
        #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
        MAP_ANONYMOUS;
        /// Put the mapping into the first 2GB of the process address space.
        #[cfg(any(all(any(target_os = "android", target_os = "linux"),
                      any(target_arch = "x86", target_arch = "x86_64")),
                  all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_arch = "x86_64")),
                  all(target_os = "freebsd", target_pointer_width = "64")))]
        MAP_32BIT;
        /// Used for stacks; indicates to the kernel that the mapping should extend downward in memory.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MAP_GROWSDOWN;
        /// Compatibility flag. Ignored.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MAP_DENYWRITE;
        /// Compatibility flag. Ignored.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MAP_EXECUTABLE;
        /// Mark the mmaped region to be locked in the same way as `mlock(2)`.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MAP_LOCKED;
        /// Do not reserve swap space for this mapping.
        ///
        /// This was removed in FreeBSD 11.
        #[cfg(not(target_os = "freebsd"))]
        MAP_NORESERVE;
        /// Populate page tables for a mapping.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MAP_POPULATE;
        /// Only meaningful when used with `MAP_POPULATE`. Don't perform read-ahead.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MAP_NONBLOCK;
        /// Allocate the mapping using "huge pages."
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MAP_HUGETLB;
        /// Lock the mapped region into memory as with `mlock(2)`.
        #[cfg(target_os = "netbsd")]
        MAP_WIRED;
        /// Causes dirtied data in the specified range to be flushed to disk only when necessary.
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
        MAP_NOSYNC;
        /// Rename private pages to a file.
        ///
        /// This was removed in FreeBSD 11.
        #[cfg(any(target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))]
        MAP_RENAME;
        /// Region may contain semaphores.
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
        MAP_HASSEMAPHORE;
        /// Region grows down, like a stack.
        #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
        MAP_STACK;
        /// Pages in this mapping are not retained in the kernel's memory cache.
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MAP_NOCACHE;
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MAP_JIT;
    }
}

libc_enum!{
    /// Usage information for a range of memory to allow for performance optimizations by the kernel.
    ///
    /// Used by [`madvise`](./fn.madvise.html).
    #[repr(i32)]
    pub enum MmapAdvise {
        /// No further special treatment. This is the default.
        MADV_NORMAL,
        /// Expect random page references.
        MADV_RANDOM,
        /// Expect sequential page references.
        MADV_SEQUENTIAL,
        /// Expect access in the near future.
        MADV_WILLNEED,
        /// Do not expect access in the near future.
        MADV_DONTNEED,
        /// Free up a given range of pages and its associated backing store.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_REMOVE,
        /// Do not make pages in this range available to the child after a `fork(2)`.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_DONTFORK,
        /// Undo the effect of `MADV_DONTFORK`.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_DOFORK,
        /// Poison the given pages.
        ///
        /// Subsequent references to those pages are treated like hardware memory corruption.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_HWPOISON,
        /// Enable Kernel Samepage Merging (KSM) for the given pages.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_MERGEABLE,
        /// Undo the effect of `MADV_MERGEABLE`
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_UNMERGEABLE,
        /// Preserve the memory of each page but offline the original page.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_SOFT_OFFLINE,
        /// Enable Transparent Huge Pages (THP) for pages in the given range.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_HUGEPAGE,
        /// Undo the effect of `MADV_HUGEPAGE`.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_NOHUGEPAGE,
        /// Exclude the given range from a core dump.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_DONTDUMP,
        /// Undo the effect of an earlier `MADV_DONTDUMP`.
        #[cfg(any(target_os = "android", target_os = "linux"))]
        MADV_DODUMP,
        /// Specify that the application no longer needs the pages in the given range.
        MADV_FREE,
        /// Request that the system not flush the current range to disk unless it needs to.
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
        MADV_NOSYNC,
        /// Undoes the effects of `MADV_NOSYNC` for any future pages dirtied within the given range.
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
        MADV_AUTOSYNC,
        /// Region is not included in a core file.
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
        MADV_NOCORE,
        /// Include region in a core file
        #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
        MADV_CORE,
        #[cfg(any(target_os = "freebsd"))]
        MADV_PROTECT,
        /// Invalidate the hardware page table for the given region.
        #[cfg(target_os = "dragonfly")]
        MADV_INVAL,
        /// Set the offset of the page directory page to `value` for the virtual page table.
        #[cfg(target_os = "dragonfly")]
        MADV_SETMAP,
        /// Indicates that the application will not need the data in the given range.
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MADV_ZERO_WIRED_PAGES,
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MADV_FREE_REUSABLE,
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MADV_FREE_REUSE,
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MADV_CAN_REUSE,
    }
}

libc_bitflags!{
    /// Configuration flags for `msync`.
    pub struct MsFlags: c_int {
        /// Schedule an update but return immediately.
        MS_ASYNC;
        /// Invalidate all cached data.
        MS_INVALIDATE;
        /// Invalidate pages, but leave them mapped.
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MS_KILLPAGES;
        /// Deactivate pages, but leave them mapped.
        #[cfg(any(target_os = "ios", target_os = "macos"))]
        MS_DEACTIVATE;
        /// Perform an update and wait for it to complete.
        MS_SYNC;
    }
}

libc_bitflags!{
    /// Flags for `mlockall`.
    pub struct MlockAllFlags: c_int {
        /// Lock pages that are currently mapped into the address space of the process.
        MCL_CURRENT;
        /// Lock pages which will become mapped into the address space of the process in the future.
        MCL_FUTURE;
    }
}

/// Locks all memory pages that contain part of the address range with `length` bytes starting at
/// `addr`. Locked pages never move to the swap area.
pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> {
    Errno::result(libc::mlock(addr, length)).map(drop)
}

/// Unlocks all memory pages that contain part of the address range with `length` bytes starting at
/// `addr`.
pub unsafe fn munlock(addr: *const c_void, length: size_t) -> Result<()> {
    Errno::result(libc::munlock(addr, length)).map(drop)
}

/// Locks all memory pages mapped into this process' address space. Locked pages never move to the
/// swap area.
pub fn mlockall(flags: MlockAllFlags) -> Result<()> {
    unsafe { Errno::result(libc::mlockall(flags.bits())) }.map(drop)
}

/// Unlocks all memory pages mapped into this process' address space.
pub fn munlockall() -> Result<()> {
    unsafe { Errno::result(libc::munlockall()) }.map(drop)
}

/// Calls to mmap are inherently unsafe, so they must be made in an unsafe block. Typically
/// a higher-level abstraction will hide the unsafe interactions with the mmap'd region.
pub unsafe fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: MapFlags, fd: RawFd, offset: off_t) -> Result<*mut c_void> {
    let ret = libc::mmap(addr, length, prot.bits(), flags.bits(), fd, offset);

    if ret == libc::MAP_FAILED {
        Err(Error::Sys(Errno::last()))
    } else {
        Ok(ret)
    }
}

pub unsafe fn munmap(addr: *mut c_void, len: size_t) -> Result<()> {
    Errno::result(libc::munmap(addr, len)).map(drop)
}

pub unsafe fn madvise(addr: *mut c_void, length: size_t, advise: MmapAdvise) -> Result<()> {
    Errno::result(libc::madvise(addr, length, advise as i32)).map(drop)
}

pub unsafe fn msync(addr: *mut c_void, length: size_t, flags: MsFlags) -> Result<()> {
    Errno::result(libc::msync(addr, length, flags.bits())).map(drop)
}

#[cfg(not(target_os = "android"))]
pub fn shm_open<P: ?Sized + NixPath>(name: &P, flag: OFlag, mode: Mode) -> Result<RawFd> {
    let ret = try!(name.with_nix_path(|cstr| {
        #[cfg(any(target_os = "macos", target_os = "ios"))]
        unsafe {
            libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::c_uint)
        }
        #[cfg(not(any(target_os = "macos", target_os = "ios")))]
        unsafe {
            libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::mode_t)
        }
    }));

    Errno::result(ret)
}

#[cfg(not(target_os = "android"))]
pub fn shm_unlink<P: ?Sized + NixPath>(name: &P) -> Result<()> {
    let ret = try!(name.with_nix_path(|cstr| {
        unsafe { libc::shm_unlink(cstr.as_ptr()) }
    }));

    Errno::result(ret).map(drop)
}
<|endoftext|>


<commit_before>use std::convert::TryInto;
use std::io::Read;
use std::vec;
use byteorder::{BigEndian, ReadBytesExt};

use super::error::{Error, ResultE};

/// auto-implemented trait to parse OSC data from a Read object.
pub trait OscReader: Read {
    /// Read a null-terminated sequence of bytes & verify padding
    fn read_0term_bytes(&mut self) -> ResultE<Vec<u8>> {
        let mut data = Vec::new();
        // Because of the 4-byte required padding, we can process 4 characters at a time
        let mut buf: [u8; 4] = [0, 0, 0, 0];
        let mut num_zeros = 0;
        while num_zeros == 0 {
            self.read_exact(&mut buf)?;
            // Copy the NON-NULL characters to the buffer.
            num_zeros = buf.iter().filter(|c| **c == 0).count();
            if buf[4-num_zeros..4].iter().any(|c| *c != 0) {
                // We had data after the null terminator.
                return Err(Error::BadPadding);
            }
            data.extend_from_slice(&buf[0..4-num_zeros]);
        }
        Ok(data)
    }
    /// Read a null-terminated UTF-8 string & verify padding
    fn parse_str(&mut self) -> ResultE<String> {
        // Note: although OSC specifies ascii only, we may have data >= 128 in the vector.
        // We can safely assume a UTF-8 encoding, because no byte of any multibyte UTF-8
        // contains a zero; the only zero possible in a UTF-8 string is the ASCII zero.
        // See the UTF-8 table here: https://en.wikipedia.org/wiki/UTF-8#History
        let bytes = self.read_0term_bytes()?;
        Ok(String::from_utf8(bytes)?)
    }
    fn parse_i32(&mut self) -> ResultE<i32> {
       Ok( self.read_i32::<BigEndian>()?)
    }
    fn parse_f32(&mut self) -> ResultE<f32> {
        Ok(self.read_f32::<BigEndian>()?)
    }
    /// "Time tags are represented by a 64 bit fixed point number.
    ///  The first 32 bits specify the number of seconds since midnight on January 1, 1900,
    ///  and the last 32 bits specify fractional parts of a second to a precision of about 200 picoseconds.
    ///  This is the representation used by Internet NTP timestamps."
    fn parse_timetag(&mut self) -> ResultE<(u32, u32)> {
       let sec = self.read_u32::<BigEndian>()?;
       let frac = self.read_u32::<BigEndian>()?;
       Ok((sec, frac))
    }
    /// Read an OSC blob & verify padding.
    /// A blob consists of a length + u8 array.
    fn parse_blob(&mut self) -> ResultE<Vec<u8>> {
        let size: usize = self.parse_i32()?.try_into()?;
        // Blobs are padded to a 4-byte boundary
        let padded_size = (size + 3) & !0x3;
        // Read EXACTLY this much data:
        let mut data = vec![0; padded_size];
        self.read_exact(&mut data)?;
        // Ensure these extra bytes where NULL (sanity check)
        if data.drain(size..padded_size).all(|c| c == 0) {
            Ok(data)
        } else {
            Err(Error::BadPadding)
        }
    }
}

/// Provide OSC reading functions to all types implementing Read
impl<R: Read + ?Sized> OscReader for R {}

<commit_msg>Remove unused import<commit_after>use std::convert::TryInto;
use std::io::Read;
use byteorder::{BigEndian, ReadBytesExt};

use super::error::{Error, ResultE};

/// auto-implemented trait to parse OSC data from a Read object.
pub trait OscReader: Read {
    /// Read a null-terminated sequence of bytes & verify padding
    fn read_0term_bytes(&mut self) -> ResultE<Vec<u8>> {
        let mut data = Vec::new();
        // Because of the 4-byte required padding, we can process 4 characters at a time
        let mut buf: [u8; 4] = [0, 0, 0, 0];
        let mut num_zeros = 0;
        while num_zeros == 0 {
            self.read_exact(&mut buf)?;
            // Copy the NON-NULL characters to the buffer.
            num_zeros = buf.iter().filter(|c| **c == 0).count();
            if buf[4-num_zeros..4].iter().any(|c| *c != 0) {
                // We had data after the null terminator.
                return Err(Error::BadPadding);
            }
            data.extend_from_slice(&buf[0..4-num_zeros]);
        }
        Ok(data)
    }
    /// Read a null-terminated UTF-8 string & verify padding
    fn parse_str(&mut self) -> ResultE<String> {
        // Note: although OSC specifies ascii only, we may have data >= 128 in the vector.
        // We can safely assume a UTF-8 encoding, because no byte of any multibyte UTF-8
        // contains a zero; the only zero possible in a UTF-8 string is the ASCII zero.
        // See the UTF-8 table here: https://en.wikipedia.org/wiki/UTF-8#History
        let bytes = self.read_0term_bytes()?;
        Ok(String::from_utf8(bytes)?)
    }
    fn parse_i32(&mut self) -> ResultE<i32> {
       Ok( self.read_i32::<BigEndian>()?)
    }
    fn parse_f32(&mut self) -> ResultE<f32> {
        Ok(self.read_f32::<BigEndian>()?)
    }
    /// "Time tags are represented by a 64 bit fixed point number.
    ///  The first 32 bits specify the number of seconds since midnight on January 1, 1900,
    ///  and the last 32 bits specify fractional parts of a second to a precision of about 200 picoseconds.
    ///  This is the representation used by Internet NTP timestamps."
    fn parse_timetag(&mut self) -> ResultE<(u32, u32)> {
       let sec = self.read_u32::<BigEndian>()?;
       let frac = self.read_u32::<BigEndian>()?;
       Ok((sec, frac))
    }
    /// Read an OSC blob & verify padding.
    /// A blob consists of a length + u8 array.
    fn parse_blob(&mut self) -> ResultE<Vec<u8>> {
        let size: usize = self.parse_i32()?.try_into()?;
        // Blobs are padded to a 4-byte boundary
        let padded_size = (size + 3) & !0x3;
        // Read EXACTLY this much data:
        let mut data = vec![0; padded_size];
        self.read_exact(&mut data)?;
        // Ensure these extra bytes where NULL (sanity check)
        if data.drain(size..padded_size).all(|c| c == 0) {
            Ok(data)
        } else {
            Err(Error::BadPadding)
        }
    }
}

/// Provide OSC reading functions to all types implementing Read
impl<R: Read + ?Sized> OscReader for R {}

<|endoftext|>


<commit_before>use libc;

use std::mem;
use std::slice;
use std::str;

use markup5ever::{namespace_url, ns, LocalName, Namespace, Prefix, QualName};

use crate::util::{opt_utf8_cstr, utf8_cstr};

pub struct PropertyBag<'a>(Vec<(QualName, &'a str)>);

pub struct PropertyBagIter<'a>(slice::Iter<'a, (QualName, &'a str)>);

impl<'a> PropertyBag<'a> {
    /// Creates an iterable `PropertyBag` from a C array of borrowed C strings.
    ///
    /// With libxml2's SAX parser, the caller's startElementNsSAX2Func
    /// callback gets passed a `xmlChar **` for attributes, which
    /// comes in groups of /// (localname/prefix/URI/value_start/value_end).
    /// In those, localname/prefix/URI are NUL-terminated strings;
    /// value_start and value_end point to the start-inclusive and
    /// end-exclusive bytes in the attribute's value.
    ///
    /// This function is unsafe because the caller must guarantee the following:
    ///
    /// * `attrs` is a valid pointer, with (n_attributes * 5) elements.
    ///
    /// * All strings are valid UTF-8.
    ///
    /// The lifetime of the `PropertyBag` should be considered the same as the lifetime of the
    /// `attrs` array, as the property bag does not copy the strings - it directly stores pointers
    /// into that array's strings.
    pub unsafe fn new_from_xml2_attributes(
        element_ns: &Namespace,
        n_attributes: usize,
        attrs: *const *const libc::c_char,
    ) -> PropertyBag<'a> {
        let mut array = Vec::new();

        if n_attributes > 0 && !attrs.is_null() {
            let attrs = slice::from_raw_parts(attrs, n_attributes * 5);

            let mut i = 0;
            while i < n_attributes * 5 {
                let localname = attrs[i];
                let prefix = attrs[i + 1];
                let uri = attrs[i + 2];
                let value_start = attrs[i + 3];
                let value_end = attrs[i + 4];

                assert!(!localname.is_null());

                let localname = utf8_cstr(localname);

                let qual_name = if localname == "id" {
                    // https://www.w3.org/TR/xml-names11/ section "7 Conformance of Documents"
                    // "No attributes with a declared type of ID [...] contain any colons."
                    //
                    // I'm interpreting this to mean that the id attribute has no
                    // namespace.

                    QualName::new(None, ns!(), LocalName::from(localname))
                } else {
                    let prefix = opt_utf8_cstr(prefix);
                    let uri = opt_utf8_cstr(uri);

                    // Use the namespace URI from the attribute, or if it is missing,
                    // use the element's namespace, per section "6.2 Namespace Defaulting"
                    // of https://www.w3.org/TR/xml-names11/
                    QualName::new(
                        prefix.map(Prefix::from),
                        uri.map(Namespace::from).unwrap_or_else(|| element_ns.clone()),
                        LocalName::from(localname)
                    )
                };

                if !value_start.is_null() && !value_end.is_null() {
                    assert!(value_end >= value_start);

                    // FIXME: ptr::offset_from() is nightly-only.
                    // We'll do the computation of the length by hand.
                    let start: usize = mem::transmute(value_start);
                    let end: usize = mem::transmute(value_end);
                    let len = end - start;

                    let value_slice = slice::from_raw_parts(value_start as *const u8, len);
                    let value_str = str::from_utf8_unchecked(value_slice);

                    array.push((qual_name, value_str));
                }

                i += 5;
            }
        }

        PropertyBag(array)
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }

    pub fn iter(&self) -> PropertyBagIter<'_> {
        PropertyBagIter(self.0.iter())
    }
}

impl<'a> Iterator for PropertyBagIter<'a> {
    type Item = (QualName, &'a str);

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|(a, v)| (a.clone(), v.clone()))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use markup5ever::{local_name, namespace_url, ns, LocalName};
    use std::ffi::CString;
    use std::ptr;

    #[test]
    fn empty_property_bag() {
        let map = unsafe { PropertyBag::new_from_xml2_attributes(&ns!(svg), 0, ptr::null()) };
        assert_eq!(map.len(), 0);
    }

    #[test]
    fn property_bag_with_namespaces() {
        let attrs = [
            (CString::new("rx").unwrap(),
             CString::new("svg").unwrap(),
             CString::new("http://www.w3.org/2000/svg").unwrap(),
             CString::new("1").unwrap()),

            (CString::new("ry").unwrap(),
             CString::new("svg").unwrap(),
             CString::new("http://www.w3.org/2000/svg").unwrap(),
             CString::new("2").unwrap()),

            (CString::new("empty").unwrap(),
             CString::new("svg").unwrap(),
             CString::new("http://www.w3.org/2000/svg").unwrap(),
             CString::new("").unwrap()),
        ];

        let mut v: Vec<*const libc::c_char> = Vec::new();

        for (localname, prefix, uri, val) in &attrs {
            v.push(localname.as_ptr() as *const libc::c_char);
            v.push(prefix.as_ptr() as *const libc::c_char);
            v.push(uri.as_ptr() as *const libc::c_char);

            let val_start = val.as_ptr() as *const libc::c_char;
            let val_end = unsafe { val_start.offset(val.as_bytes().len() as isize) };
            v.push(val_start); // value_start
            v.push(val_end); // value_end
        }

        let pbag = unsafe { PropertyBag::new_from_xml2_attributes(&ns!(svg), 3, v.as_ptr()) };

        let mut had_rx: bool = false;
        let mut had_ry: bool = false;
        let mut had_empty: bool = false;

        for (a, v) in pbag.iter() {
            assert_eq!(a.prefix.as_ref().unwrap(), "svg");
            assert_eq!(a.ns, ns!(svg));

            match a.local {
                local_name!("rx") => {
                    assert!(v == "1");
                    had_rx = true;
                }
                local_name!("ry") => {
                    assert!(v == "2");
                    had_ry = true;
                }
                ref n if *n == LocalName::from("empty") => {
                    assert!(v == "");
                    had_empty = true;
                }
                _ => unreachable!(),
            }
        }

        assert!(had_rx);
        assert!(had_ry);
        assert!(had_empty);
    }
}
<commit_msg>Use chunks_exact instead of manual iteration<commit_after>use libc;

use std::mem;
use std::slice;
use std::str;

use markup5ever::{namespace_url, ns, LocalName, Namespace, Prefix, QualName};

use crate::util::{opt_utf8_cstr, utf8_cstr};

pub struct PropertyBag<'a>(Vec<(QualName, &'a str)>);

pub struct PropertyBagIter<'a>(slice::Iter<'a, (QualName, &'a str)>);

impl<'a> PropertyBag<'a> {
    /// Creates an iterable `PropertyBag` from a C array of borrowed C strings.
    ///
    /// With libxml2's SAX parser, the caller's startElementNsSAX2Func
    /// callback gets passed a `xmlChar **` for attributes, which
    /// comes in groups of /// (localname/prefix/URI/value_start/value_end).
    /// In those, localname/prefix/URI are NUL-terminated strings;
    /// value_start and value_end point to the start-inclusive and
    /// end-exclusive bytes in the attribute's value.
    ///
    /// This function is unsafe because the caller must guarantee the following:
    ///
    /// * `attrs` is a valid pointer, with (n_attributes * 5) elements.
    ///
    /// * All strings are valid UTF-8.
    ///
    /// The lifetime of the `PropertyBag` should be considered the same as the lifetime of the
    /// `attrs` array, as the property bag does not copy the strings - it directly stores pointers
    /// into that array's strings.
    pub unsafe fn new_from_xml2_attributes(
        element_ns: &Namespace,
        n_attributes: usize,
        attrs: *const *const libc::c_char,
    ) -> PropertyBag<'a> {
        let mut array = Vec::new();

        if n_attributes > 0 && !attrs.is_null() {
            for attr in slice::from_raw_parts(attrs, n_attributes * 5).chunks_exact(5) {
                let localname = attr[0];
                let prefix = attr[1];
                let uri = attr[2];
                let value_start = attr[3];
                let value_end = attr[4];

                assert!(!localname.is_null());

                let localname = utf8_cstr(localname);

                let qual_name = if localname == "id" {
                    // https://www.w3.org/TR/xml-names11/ section "7 Conformance of Documents"
                    // "No attributes with a declared type of ID [...] contain any colons."
                    //
                    // I'm interpreting this to mean that the id attribute has no
                    // namespace.

                    QualName::new(None, ns!(), LocalName::from(localname))
                } else {
                    let prefix = opt_utf8_cstr(prefix);
                    let uri = opt_utf8_cstr(uri);

                    // Use the namespace URI from the attribute, or if it is missing,
                    // use the element's namespace, per section "6.2 Namespace Defaulting"
                    // of https://www.w3.org/TR/xml-names11/
                    QualName::new(
                        prefix.map(Prefix::from),
                        uri.map(Namespace::from).unwrap_or_else(|| element_ns.clone()),
                        LocalName::from(localname)
                    )
                };

                if !value_start.is_null() && !value_end.is_null() {
                    assert!(value_end >= value_start);

                    // FIXME: ptr::offset_from() is nightly-only.
                    // We'll do the computation of the length by hand.
                    let start: usize = mem::transmute(value_start);
                    let end: usize = mem::transmute(value_end);
                    let len = end - start;

                    let value_slice = slice::from_raw_parts(value_start as *const u8, len);
                    let value_str = str::from_utf8_unchecked(value_slice);

                    array.push((qual_name, value_str));
                }
            }
        }

        PropertyBag(array)
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }

    pub fn iter(&self) -> PropertyBagIter<'_> {
        PropertyBagIter(self.0.iter())
    }
}

impl<'a> Iterator for PropertyBagIter<'a> {
    type Item = (QualName, &'a str);

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|(a, v)| (a.clone(), v.clone()))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use markup5ever::{local_name, namespace_url, ns, LocalName};
    use std::ffi::CString;
    use std::ptr;

    #[test]
    fn empty_property_bag() {
        let map = unsafe { PropertyBag::new_from_xml2_attributes(&ns!(svg), 0, ptr::null()) };
        assert_eq!(map.len(), 0);
    }

    #[test]
    fn property_bag_with_namespaces() {
        let attrs = [
            (CString::new("rx").unwrap(),
             CString::new("svg").unwrap(),
             CString::new("http://www.w3.org/2000/svg").unwrap(),
             CString::new("1").unwrap()),

            (CString::new("ry").unwrap(),
             CString::new("svg").unwrap(),
             CString::new("http://www.w3.org/2000/svg").unwrap(),
             CString::new("2").unwrap()),

            (CString::new("empty").unwrap(),
             CString::new("svg").unwrap(),
             CString::new("http://www.w3.org/2000/svg").unwrap(),
             CString::new("").unwrap()),
        ];

        let mut v: Vec<*const libc::c_char> = Vec::new();

        for (localname, prefix, uri, val) in &attrs {
            v.push(localname.as_ptr() as *const libc::c_char);
            v.push(prefix.as_ptr() as *const libc::c_char);
            v.push(uri.as_ptr() as *const libc::c_char);

            let val_start = val.as_ptr() as *const libc::c_char;
            let val_end = unsafe { val_start.offset(val.as_bytes().len() as isize) };
            v.push(val_start); // value_start
            v.push(val_end); // value_end
        }

        let pbag = unsafe { PropertyBag::new_from_xml2_attributes(&ns!(svg), 3, v.as_ptr()) };

        let mut had_rx: bool = false;
        let mut had_ry: bool = false;
        let mut had_empty: bool = false;

        for (a, v) in pbag.iter() {
            assert_eq!(a.prefix.as_ref().unwrap(), "svg");
            assert_eq!(a.ns, ns!(svg));

            match a.local {
                local_name!("rx") => {
                    assert!(v == "1");
                    had_rx = true;
                }
                local_name!("ry") => {
                    assert!(v == "2");
                    had_ry = true;
                }
                ref n if *n == LocalName::from("empty") => {
                    assert!(v == "");
                    had_empty = true;
                }
                _ => unreachable!(),
            }
        }

        assert!(had_rx);
        assert!(had_ry);
        assert!(had_empty);
    }
}
<|endoftext|>