<commit_before>use crate::logger::{self, Level};
use std::fs::{self, File, OpenOptions};
use std::io::{Error, Read, Write};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::path::{Path, PathBuf};
use std::process;

fn remove_lock(lock_path: &Path) {
    fs::remove_file(lock_path).ok();
    log!(
        Level::Debug,
        "FsLock: removed lockfile {}",
        lock_path.display()
    );
}

#[derive(Default)]
pub struct FsLock {
    lock_path: PathBuf,
    lock_file: Option<File>,
}

impl Drop for FsLock {
    fn drop(&mut self) {
        if self.lock_file.is_some() {
            remove_lock(&self.lock_path);
        }
    }
}

impl FsLock {
    pub fn try_lock(&mut self, new_lock_path: &Path, pid: &mut libc::pid_t) -> Result<(), String> {
        if self.lock_file.is_some() && self.lock_path == new_lock_path {
            return Ok(());
        }

        // pid == 0 indicates that something went majorly wrong during locking
        *pid = 0;

        log!(
            Level::Debug,
            "FsLock: trying to lock `{}'",
            new_lock_path.display()
        );

        // first we open (and possibly create) the lock file
        let mut options = OpenOptions::new();
        options.create(true).read(true).write(true).mode(0o600);
        let mut file = match options.open(&new_lock_path) {
            Ok(file) => file,
            Err(reason) => {
                return Err(format!(
                    "Failed to open lock file: '{}' ({})",
                    new_lock_path.display(),
                    reason
                ))
            }
        };

        // then we lock it (returns immediately if locking is not possible)
        if unsafe { libc::lockf(file.as_raw_fd(), libc::F_TLOCK, 0) } == 0 {
            log!(
                Level::Debug,
                "FsLock: locked `{}', writing PID...",
                new_lock_path.display()
            );
            let pid = process::id().to_string();
            if let Err(reason) = file
                .set_len(0)
                .and_then(|_| file.write_all(&pid.as_bytes()))
            {
                log!(Level::Debug, "FsLock: Failed to write PID");
                return Err(format!(
                    "Failed to write PID to lock file: '{}' ({})",
                    new_lock_path.display(),
                    reason
                ));
            }
            log!(Level::Debug, "FsLock: PID written successfully");
            if self.lock_file.take().is_some() {
                remove_lock(&self.lock_path);
            }
            self.lock_file = Some(file);
            self.lock_path = new_lock_path.to_owned();
            Ok(())
        } else {
            log!(
                Level::Error,
                "FsLock: something went wrong during locking: {}",
                Error::last_os_error()
            );

            // locking was not successful -> read PID of locking process from the file
            let mut buf = String::new();
            if file.read_to_string(&mut buf).is_ok() {
                *pid = buf.parse().unwrap_or(0);
            }
            log!(
                Level::Debug,
                "FsLock: locking failed, already locked by {}",
                pid
            );
            Err(format!(
                "Failed to lock '{}', already locked by process with PID {}",
                new_lock_path.display(),
                pid
            ))
        }
    }
}
<commit_msg>Rust FsLock: Support translation of error messages<commit_after>use crate::logger::{self, Level};
use gettextrs::gettext;
use std::fs::{self, File, OpenOptions};
use std::io::{Error, Read, Write};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::path::{Path, PathBuf};
use std::process;

use strprintf::fmt;

fn remove_lock(lock_path: &Path) {
    fs::remove_file(lock_path).ok();
    log!(
        Level::Debug,
        "FsLock: removed lockfile {}",
        lock_path.display()
    );
}

#[derive(Default)]
pub struct FsLock {
    lock_path: PathBuf,
    lock_file: Option<File>,
}

impl Drop for FsLock {
    fn drop(&mut self) {
        if self.lock_file.is_some() {
            remove_lock(&self.lock_path);
        }
    }
}

impl FsLock {
    pub fn try_lock(&mut self, new_lock_path: &Path, pid: &mut libc::pid_t) -> Result<(), String> {
        if self.lock_file.is_some() && self.lock_path == new_lock_path {
            return Ok(());
        }

        // pid == 0 indicates that something went majorly wrong during locking
        *pid = 0;

        log!(
            Level::Debug,
            "FsLock: trying to lock `{}'",
            new_lock_path.display()
        );

        // first we open (and possibly create) the lock file
        let mut options = OpenOptions::new();
        options.create(true).read(true).write(true).mode(0o600);
        let mut file = match options.open(&new_lock_path) {
            Ok(file) => file,
            Err(reason) => {
                return Err(fmt!(
                    &gettext("Failed to open lock file: '%s' (%s)"),
                    new_lock_path
                        .to_str()
                        .unwrap_or(&gettext("<filename containing invalid unicode character>")),
                    reason.to_string()
                ))
            }
        };

        // then we lock it (returns immediately if locking is not possible)
        if unsafe { libc::lockf(file.as_raw_fd(), libc::F_TLOCK, 0) } == 0 {
            log!(
                Level::Debug,
                "FsLock: locked `{}', writing PID...",
                new_lock_path.display()
            );
            let pid = process::id().to_string();
            if let Err(reason) = file
                .set_len(0)
                .and_then(|_| file.write_all(&pid.as_bytes()))
            {
                log!(Level::Debug, "FsLock: Failed to write PID");
                return Err(fmt!(
                    &gettext("Failed to write PID to lock file: '%s' (%s)"),
                    new_lock_path
                        .to_str()
                        .unwrap_or(&gettext("<filename containing invalid unicode character>")),
                    reason.to_string()
                ));
            }
            log!(Level::Debug, "FsLock: PID written successfully");
            if self.lock_file.take().is_some() {
                remove_lock(&self.lock_path);
            }
            self.lock_file = Some(file);
            self.lock_path = new_lock_path.to_owned();
            Ok(())
        } else {
            log!(
                Level::Error,
                "FsLock: something went wrong during locking: {}",
                Error::last_os_error()
            );

            // locking was not successful -> read PID of locking process from the file
            let mut buf = String::new();
            if file.read_to_string(&mut buf).is_ok() {
                *pid = buf.parse().unwrap_or(0);
            }
            log!(
                Level::Debug,
                "FsLock: locking failed, already locked by {}",
                pid
            );
            Err(fmt!(
                &gettext("Failed to lock '%s', already locked by process with PID %{}"),
                new_lock_path
                    .to_str()
                    .unwrap_or(&gettext("<filename containing invalid unicode character>")),
                PRIi64,
                *pid as i64
            ))
        }
    }
}
<|endoftext|>


<commit_before>use util::bits::Bits as _;

use crate::memory::{
    io::{Effect, IoRegisters},
    palette::Palette,
};

pub const OBJ: usize = 4;
pub const BACKDROP: usize = 5;

pub struct LineBuffer {
    // 240 pixels for each background (BG0-3 + OBJ)
    pixels: [[u16; 240]; 5],
    layer_attrs: [LayerAttrs; 5],
}

impl Default for LineBuffer {
    fn default() -> Self {
        LineBuffer {
            pixels: [[0; 240]; 5],
            layer_attrs: [LayerAttrs::default(); 5],
        }
    }
}

impl LineBuffer {
    pub(crate) fn put(&mut self, layer: usize, x: usize, pixel: u16) {
        self.pixels[layer][x] = pixel | 0x8000;
    }

    pub(crate) fn put_obj_4bpp(&mut self, attrs: PixelAttrs, x: usize, palette: u8, entry: u8) {
        self.pixels[OBJ][x] =
            (attrs.value as u16) | ((palette as u16) << 12) | ((entry as u16) << 8);
    }

    pub(crate) fn put_obj_8bpp(&mut self, attrs: PixelAttrs, x: usize, entry: u8) {
        self.pixels[OBJ][x] = attrs.value as u16 | ((entry as u16) << 8);
    }

    pub(crate) fn put_4bpp(&mut self, layer: usize, x: usize, palette: u8, entry: u8) {
        self.pixels[layer][x] = ((palette as u16) << 4) | (entry as u16);
    }

    pub(crate) fn put_8bpp(&mut self, layer: usize, x: usize, entry: u8) {
        self.pixels[layer][x] = entry as u16;
    }

    pub(crate) fn layer_attrs_mut(&mut self, layer: usize) -> &mut LayerAttrs {
        &mut self.layer_attrs[layer]
    }

    fn color_obj(&self, x: usize, priority: u16, palette: &Palette) -> Option<(u16, PixelAttrs)> {
        let entry = self.pixels[OBJ][x];
        let attrs = PixelAttrs { value: entry as u8 };

        if attrs.priority() != priority {
            return None;
        }

        let entry = (entry >> 8) as u8;

        if attrs.is_4bpp() {
            let color_entry = entry & 0xF;
            if color_entry == 0 {
                return None;
            }
            let palette_index = entry >> 4;
            Some((
                palette.get_obj16(palette_index as _, color_entry as _),
                attrs,
            ))
        } else if entry as u8 == 0 {
            None
        } else {
            Some((palette.get_obj256(entry), attrs))
        }
    }

    fn color_bg(&self, layer: usize, x: usize, palette: &Palette) -> Option<u16> {
        let attrs = self.layer_attrs[layer];
        let entry = self.pixels[layer][x];

        if attrs.is_bitmap() {
            return Some(entry);
        }

        if attrs.is_4bpp() {
            let color_entry = entry & 0xF;
            if color_entry == 0 {
                return None;
            }
            let palette_index = (entry >> 4) & 0xF;

            Some(palette.get_bg16(palette_index as _, color_entry as _))
        } else if entry == 0 {
            None
        } else {
            Some(palette.get_bg256(entry as _))
        }
    }

    pub fn render(&self, output: &mut [u16], ioregs: &IoRegisters, palette: &Palette) {
        let backdrop = palette.get_bg256(0) | 0x8000;

        (0..240)
            .map(|x| (x, self.render_pixel(x, ioregs, backdrop, palette)))
            .for_each(|(x, p)| output[x] = p);
    }

    fn render_pixel(
        &self,
        x: usize,
        ioregs: &IoRegisters,
        backdrop: u16,
        palette: &Palette,
    ) -> u16 {
        let mut attrs = (PixelAttrs::default(), PixelAttrs::default());
        let mut colors = (backdrop, 0);

        if ioregs.bldcnt.is_first_target(BACKDROP) {
            attrs.0.set_first_target();
        }
        if ioregs.bldcnt.is_second_target(BACKDROP) {
            attrs.0.set_second_target();
        }

        for priority in (0..4).rev() {
            for bg in (0usize..4).rev() {
                // FIXME do window checks here.
                if !ioregs.dispcnt.display_bg(bg as _) || ioregs.bgcnt[bg].priority() != priority {
                    continue;
                }

                if let Some(color) = self.color_bg(bg, x, palette) {
                    let mut new_attrs = PixelAttrs::default();
                    if ioregs.bldcnt.is_first_target(bg) {
                        new_attrs.set_first_target();
                    }
                    if ioregs.bldcnt.is_second_target(bg) {
                        new_attrs.set_second_target();
                    }

                    attrs.1 = attrs.0;
                    attrs.0 = new_attrs;

                    colors.1 = colors.0;
                    colors.0 = color;
                }
            }

            if ioregs.dispcnt.display_obj() {
                if let Some((color, new_attrs)) = self.color_obj(x, priority, palette) {
                    attrs.1 = attrs.0;
                    attrs.0 = new_attrs;

                    colors.1 = colors.0;
                    colors.0 = color;
                }
            }
        }

        let mut effect = ioregs.bldcnt.effect();

        // OBJs that are defined as 'Semi-Transparent' in OAM memory are always selected as 1st Target (regardless of BLDCNT Bit 4),
        // and are always using Alpha Blending mode (regardless of BLDCNT Bit 6-7).
        // The BLDCNT register may be used to perform Brightness effects on the OBJ (and/or other BG/BD layers).
        // However, if a semi-transparent OBJ pixel does overlap a 2nd target pixel, then semi-transparency becomes priority,
        // and the brightness effect will not take place (neither on 1st, nor 2nd target).
        if attrs.0.is_semi_transparent() {
            attrs.0.set_first_target();
            if attrs.1.is_second_target() {
                effect = Effect::AlphaBlending;
            }
        }

        if effect == Effect::None || !attrs.0.is_first_target() {
            return colors.0;
        }

        match effect {
            Effect::AlphaBlending => {
                // For this effect, the top-most non-transparent pixel must be selected as 1st Target,
                // and the next-lower non-transparent pixel must be selected as 2nd Target, if so - and only if so,
                // then color intensities of 1st and 2nd Target are mixed together by using the parameters in BLDALPHA register,
                // for each pixel each R, G, B intensities are calculated separately:
                //   I = MIN ( 31, I1st*EVA + I2nd*EVB )
                // Otherwise - for example, if only one target exists, or if a non-transparent non-2nd-target
                // pixel is moved between the two targets, or if 2nd target has higher display priority than 1st target -
                // then only the top-most pixel is displayed (at normal intensity, regardless of BLDALPHA).
                if attrs.1.is_second_target() {
                    let eva = ioregs.bldalpha.eva_coeff();
                    let evb = ioregs.bldalpha.evb_coeff();
                    alpha_blend(colors.0, colors.1, eva, evb)
                } else {
                    colors.0
                }
            }

            Effect::BrightnessIncrease => {
                //  For each pixel each R, G, B intensities are calculated separately:
                //   I = I1st + (31-I1st)*EVY   ;For Brightness Increase
                // The color intensities of any selected 1st target surface(s) are increased by using the parameter in BLDY register.
                let evy = ioregs.bldy.evy_coeff();
                brightness_increase(colors.0, evy)
            }

            Effect::BrightnessDecrease => {
                //  For each pixel each R, G, B intensities are calculated separately:
                //   I = I1st - (I1st)*EVY      ;For Brightness Decrease
                // The color intensities of any selected 1st target surface(s) are decreased by using the parameter in BLDY register.
                let evy = ioregs.bldy.evy_coeff();
                brightness_decrease(colors.0, evy)
            }

            Effect::None => unreachable!(),
        }
    }
}

fn alpha_blend(c1: u16, c2: u16, eva: u16, evb: u16) -> u16 {
    let (r1, g1, b1) = decompose(c1);
    let (r2, g2, b2) = decompose(c2);
    //   I = MIN ( 31, I1st*EVA + I2nd*EVB )
    let r = (r1 * eva + r2 * evb) / 16;
    let g = (g1 * eva + g2 * evb) / 16;
    let b = (b1 * eva + b2 * evb) / 16;
    recompose(r, g, b)
}

fn brightness_increase(c: u16, evy: u16) -> u16 {
    let (r, g, b) = decompose(c);
    //   I = I1st + (31-I1st)*EVY
    let r = r + (((31 - r) * evy) / 16);
    let g = g + (((31 - g) * evy) / 16);
    let b = b + (((31 - b) * evy) / 16);
    recompose(r, g, b)
}

fn brightness_decrease(c: u16, evy: u16) -> u16 {
    let (r, g, b) = decompose(c);
    //   I = I1st - (I1st)*EVY
    let r = r + ((r * evy) / 16);
    let g = g + ((g * evy) / 16);
    let b = b + ((b * evy) / 16);
    recompose(r, g, b)
}

fn decompose(c: u16) -> (u16, u16, u16) {
    let r = c.bits(0, 4);
    let g = c.bits(5, 9);
    let b = c.bits(10, 14);
    (r, g, b)
}

fn recompose(r: u16, g: u16, b: u16) -> u16 {
    r.min(31) | (g.min(31) << 5) | (b.min(31) << 10)
}

#[derive(Clone, Copy, Default)]
pub(crate) struct LayerAttrs {
    value: u8,
}

impl LayerAttrs {
    const BITMAP_16BPP: u8 = 0x1;
    const PALETTE_4BPP: u8 = 0x2;

    pub fn is_bitmap(&self) -> bool {
        (self.value & Self::BITMAP_16BPP) != 0
    }

    pub fn is_4bpp(&self) -> bool {
        (self.value & Self::PALETTE_4BPP) != 0
    }

    pub fn set_bitmap(&mut self) {
        self.value |= Self::BITMAP_16BPP;
    }

    pub fn set_4bpp(&mut self) {
        self.value |= Self::PALETTE_4BPP;
    }

    pub fn set_8bpp(&mut self) {
        /* NOP */
    }
}

#[derive(Clone, Copy, Default)]
pub(crate) struct PixelAttrs {
    value: u8,
}

impl PixelAttrs {
    // # NOTE Bits 6 and 7 are used for priority

    const FIRST_TARGET: u8 = 0x1; // bit 0
    const SECOND_TARGET: u8 = 0x2; // bit 1
    const PALETTE_4BPP: u8 = 0x4; // bit 2
    const SEMI_TRANSPARENT: u8 = 0x8; // bit 3

    pub fn is_first_target(&self) -> bool {
        (self.value & Self::FIRST_TARGET) != 0
    }

    pub fn is_second_target(&self) -> bool {
        (self.value & Self::SECOND_TARGET) != 0
    }

    pub fn set_first_target(&mut self) {
        self.value |= Self::FIRST_TARGET;
    }

    pub fn set_second_target(&mut self) {
        self.value |= Self::SECOND_TARGET;
    }

    /// Only used by OBJ layer pixels
    pub fn is_4bpp(&self) -> bool {
        (self.value & Self::PALETTE_4BPP) != 0
    }

    /// Only used by OBJ layer pixels
    pub fn set_4bpp(&mut self) {
        self.value |= Self::PALETTE_4BPP;
    }

    /// Only used by OBJ layer pixels
    pub fn set_8bpp(&mut self) {
        /* NOP */
    }

    pub fn set_semi_transparent(&mut self) {
        self.value |= Self::SEMI_TRANSPARENT;
    }

    pub fn is_semi_transparent(&self) -> bool {
        (self.value & Self::SEMI_TRANSPARENT) != 0
    }

    pub fn set_priority(&mut self, priority: u16) {
        self.value |= (priority as u8) << 6;
    }

    pub fn priority(&self) -> u16 {
        (self.value >> 6) as u16
    }
}
<commit_msg>Refactor pixel rendering to do a lot less work per pixel.<commit_after>use util::bits::Bits as _;

use crate::memory::{
    io::{Effect, IoRegisters},
    palette::Palette,
};

pub const OBJ: usize = 4;
pub const BACKDROP: usize = 5;

pub struct LineBuffer {
    // 240 pixels for each background (BG0-3 + OBJ)
    pixels: [[u16; 240]; 5],
    layer_attrs: [LayerAttrs; 5],
}

impl Default for LineBuffer {
    fn default() -> Self {
        LineBuffer {
            pixels: [[0; 240]; 5],
            layer_attrs: [LayerAttrs::default(); 5],
        }
    }
}

impl LineBuffer {
    pub(crate) fn put(&mut self, layer: usize, x: usize, pixel: u16) {
        self.pixels[layer][x] = pixel | 0x8000;
    }

    pub(crate) fn put_obj_4bpp(&mut self, attrs: PixelAttrs, x: usize, palette: u8, entry: u8) {
        self.pixels[OBJ][x] =
            (attrs.value as u16) | ((palette as u16) << 12) | ((entry as u16) << 8);
    }

    pub(crate) fn put_obj_8bpp(&mut self, attrs: PixelAttrs, x: usize, entry: u8) {
        self.pixels[OBJ][x] = attrs.value as u16 | ((entry as u16) << 8);
    }

    pub(crate) fn put_4bpp(&mut self, layer: usize, x: usize, palette: u8, entry: u8) {
        self.pixels[layer][x] = ((palette as u16) << 4) | (entry as u16);
    }

    pub(crate) fn put_8bpp(&mut self, layer: usize, x: usize, entry: u8) {
        self.pixels[layer][x] = entry as u16;
    }

    pub(crate) fn layer_attrs_mut(&mut self, layer: usize) -> &mut LayerAttrs {
        &mut self.layer_attrs[layer]
    }

    fn color_obj(&self, x: usize, priority: u16, palette: &Palette) -> Option<(u16, PixelAttrs)> {
        let entry = self.pixels[OBJ][x];
        let attrs = PixelAttrs { value: entry as u8 };

        if attrs.priority() != priority {
            return None;
        }

        let entry = (entry >> 8) as u8;

        if attrs.is_4bpp() {
            let color_entry = entry & 0xF;
            if color_entry == 0 {
                return None;
            }
            let palette_index = entry >> 4;
            Some((
                palette.get_obj16(palette_index as _, color_entry as _),
                attrs,
            ))
        } else if entry as u8 == 0 {
            None
        } else {
            Some((palette.get_obj256(entry), attrs))
        }
    }

    fn color_bg(&self, layer: usize, x: usize, palette: &Palette) -> Option<u16> {
        let attrs = self.layer_attrs[layer];
        let entry = self.pixels[layer][x];

        if attrs.is_bitmap() {
            return Some(entry);
        }

        if attrs.is_4bpp() {
            let color_entry = entry & 0xF;
            if color_entry == 0 {
                return None;
            }
            let palette_index = (entry >> 4) & 0xF;

            Some(palette.get_bg16(palette_index as _, color_entry as _))
        } else if entry == 0 {
            None
        } else {
            Some(palette.get_bg256(entry as _))
        }
    }

    pub fn render(&self, output: &mut [u16], ioregs: &IoRegisters, palette: &Palette) {
        let backdrop_color = palette.get_bg256(0) | 0x8000;
        let mut backdrop_attrs = PixelAttrs::default();
        if ioregs.bldcnt.is_first_target(BACKDROP) {
            backdrop_attrs.set_first_target();
        }
        if ioregs.bldcnt.is_second_target(BACKDROP) {
            backdrop_attrs.set_second_target();
        }
        let mut backdrop = Pixel::default();
        backdrop.push(backdrop_color, backdrop_attrs);
        let mut pixels = [backdrop; 240];

        for priority in (0..4).rev() {
            for bg in (0usize..4).rev() {
                // FIXME do window checks here.
                if !ioregs.dispcnt.display_bg(bg as _) || ioregs.bgcnt[bg].priority() != priority {
                    continue;
                }

                let mut attrs = PixelAttrs::default();
                if ioregs.bldcnt.is_first_target(bg) {
                    attrs.set_first_target();
                }
                if ioregs.bldcnt.is_second_target(bg) {
                    attrs.set_second_target();
                }

                (0..240).for_each(|x| {
                    if let Some(color) = self.color_bg(bg, x, palette) {
                        pixels[x].push(color, attrs);
                    }
                });
            }

            if ioregs.dispcnt.display_obj() {
                (0..240).for_each(|x| {
                    if let Some((color, attrs)) = self.color_obj(x, priority, palette) {
                        pixels[x].push(color, attrs);
                    }
                });
            }
        }

        (0..240)
            .map(|x| (x, self.render_pixel(pixels[x], ioregs)))
            .for_each(|(x, p)| output[x] = p);
    }

    fn render_pixel(&self, pixel: Pixel, ioregs: &IoRegisters) -> u16 {
        let Pixel { mut attrs, colors } = pixel;

        let mut effect = ioregs.bldcnt.effect();

        // OBJs that are defined as 'Semi-Transparent' in OAM memory are always selected as 1st Target (regardless of BLDCNT Bit 4),
        // and are always using Alpha Blending mode (regardless of BLDCNT Bit 6-7).
        // The BLDCNT register may be used to perform Brightness effects on the OBJ (and/or other BG/BD layers).
        // However, if a semi-transparent OBJ pixel does overlap a 2nd target pixel, then semi-transparency becomes priority,
        // and the brightness effect will not take place (neither on 1st, nor 2nd target).
        if attrs.0.is_semi_transparent() {
            attrs.0.set_first_target();
            if attrs.1.is_second_target() {
                effect = Effect::AlphaBlending;
            }
        }

        if effect == Effect::None || !attrs.0.is_first_target() {
            return colors.0;
        }

        match effect {
            Effect::AlphaBlending => {
                // For this effect, the top-most non-transparent pixel must be selected as 1st Target,
                // and the next-lower non-transparent pixel must be selected as 2nd Target, if so - and only if so,
                // then color intensities of 1st and 2nd Target are mixed together by using the parameters in BLDALPHA register,
                // for each pixel each R, G, B intensities are calculated separately:
                //   I = MIN ( 31, I1st*EVA + I2nd*EVB )
                // Otherwise - for example, if only one target exists, or if a non-transparent non-2nd-target
                // pixel is moved between the two targets, or if 2nd target has higher display priority than 1st target -
                // then only the top-most pixel is displayed (at normal intensity, regardless of BLDALPHA).
                if attrs.1.is_second_target() {
                    let eva = ioregs.bldalpha.eva_coeff();
                    let evb = ioregs.bldalpha.evb_coeff();
                    alpha_blend(colors.0, colors.1, eva, evb)
                } else {
                    colors.0
                }
            }

            Effect::BrightnessIncrease => {
                //  For each pixel each R, G, B intensities are calculated separately:
                //   I = I1st + (31-I1st)*EVY   ;For Brightness Increase
                // The color intensities of any selected 1st target surface(s) are increased by using the parameter in BLDY register.
                let evy = ioregs.bldy.evy_coeff();
                brightness_increase(colors.0, evy)
            }

            Effect::BrightnessDecrease => {
                //  For each pixel each R, G, B intensities are calculated separately:
                //   I = I1st - (I1st)*EVY      ;For Brightness Decrease
                // The color intensities of any selected 1st target surface(s) are decreased by using the parameter in BLDY register.
                let evy = ioregs.bldy.evy_coeff();
                brightness_decrease(colors.0, evy)
            }

            Effect::None => unreachable!(),
        }
    }
}

#[derive(Default, Copy, Clone)]
struct Pixel {
    attrs: (PixelAttrs, PixelAttrs),
    colors: (u16, u16),
}

impl Pixel {
    pub fn push(&mut self, color: u16, attrs: PixelAttrs) {
        self.colors.1 = self.colors.0;
        self.colors.0 = color;

        self.attrs.1 = self.attrs.0;
        self.attrs.0 = attrs;
    }
}

fn alpha_blend(c1: u16, c2: u16, eva: u16, evb: u16) -> u16 {
    let (r1, g1, b1) = decompose(c1);
    let (r2, g2, b2) = decompose(c2);
    //   I = MIN ( 31, I1st*EVA + I2nd*EVB )
    let r = (r1 * eva + r2 * evb) / 16;
    let g = (g1 * eva + g2 * evb) / 16;
    let b = (b1 * eva + b2 * evb) / 16;
    recompose(r, g, b)
}

fn brightness_increase(c: u16, evy: u16) -> u16 {
    let (r, g, b) = decompose(c);
    //   I = I1st + (31-I1st)*EVY
    let r = r + (((31 - r) * evy) / 16);
    let g = g + (((31 - g) * evy) / 16);
    let b = b + (((31 - b) * evy) / 16);
    recompose(r, g, b)
}

fn brightness_decrease(c: u16, evy: u16) -> u16 {
    let (r, g, b) = decompose(c);
    //   I = I1st - (I1st)*EVY
    let r = r + ((r * evy) / 16);
    let g = g + ((g * evy) / 16);
    let b = b + ((b * evy) / 16);
    recompose(r, g, b)
}

fn decompose(c: u16) -> (u16, u16, u16) {
    let r = c.bits(0, 4);
    let g = c.bits(5, 9);
    let b = c.bits(10, 14);
    (r, g, b)
}

fn recompose(r: u16, g: u16, b: u16) -> u16 {
    r.min(31) | (g.min(31) << 5) | (b.min(31) << 10)
}

#[derive(Clone, Copy, Default)]
pub(crate) struct LayerAttrs {
    value: u8,
}

impl LayerAttrs {
    const BITMAP_16BPP: u8 = 0x1;
    const PALETTE_4BPP: u8 = 0x2;

    pub fn is_bitmap(&self) -> bool {
        (self.value & Self::BITMAP_16BPP) != 0
    }

    pub fn is_4bpp(&self) -> bool {
        (self.value & Self::PALETTE_4BPP) != 0
    }

    pub fn set_bitmap(&mut self) {
        self.value |= Self::BITMAP_16BPP;
    }

    pub fn set_4bpp(&mut self) {
        self.value |= Self::PALETTE_4BPP;
    }

    pub fn set_8bpp(&mut self) {
        /* NOP */
    }
}

#[derive(Clone, Copy, Default)]
pub(crate) struct PixelAttrs {
    value: u8,
}

impl PixelAttrs {
    // # NOTE Bits 6 and 7 are used for priority

    const FIRST_TARGET: u8 = 0x1; // bit 0
    const SECOND_TARGET: u8 = 0x2; // bit 1
    const PALETTE_4BPP: u8 = 0x4; // bit 2
    const SEMI_TRANSPARENT: u8 = 0x8; // bit 3

    pub fn is_first_target(&self) -> bool {
        (self.value & Self::FIRST_TARGET) != 0
    }

    pub fn is_second_target(&self) -> bool {
        (self.value & Self::SECOND_TARGET) != 0
    }

    pub fn set_first_target(&mut self) {
        self.value |= Self::FIRST_TARGET;
    }

    pub fn set_second_target(&mut self) {
        self.value |= Self::SECOND_TARGET;
    }

    /// Only used by OBJ layer pixels
    pub fn is_4bpp(&self) -> bool {
        (self.value & Self::PALETTE_4BPP) != 0
    }

    /// Only used by OBJ layer pixels
    pub fn set_4bpp(&mut self) {
        self.value |= Self::PALETTE_4BPP;
    }

    /// Only used by OBJ layer pixels
    pub fn set_8bpp(&mut self) {
        /* NOP */
    }

    pub fn set_semi_transparent(&mut self) {
        self.value |= Self::SEMI_TRANSPARENT;
    }

    pub fn is_semi_transparent(&self) -> bool {
        (self.value & Self::SEMI_TRANSPARENT) != 0
    }

    pub fn set_priority(&mut self, priority: u16) {
        self.value |= (priority as u8) << 6;
    }

    pub fn priority(&self) -> u16 {
        (self.value >> 6) as u16
    }
}
<|endoftext|>


<commit_before>extern crate gtk;
use gtk::prelude::*;

use std::rc::Rc;
use std::cell::RefCell;

use ::media::{Context, Timestamp};

use super::{ImageSurface};

pub struct InfoController {
    drawingarea: gtk::DrawingArea,

    title_lbl: gtk::Label,
    artist_lbl: gtk::Label,
    container_lbl: gtk::Label,
    audio_codec_lbl: gtk::Label,
    video_codec_lbl: gtk::Label,
    duration_lbl: gtk::Label,
    position_lbl: gtk::Label,
    timeline_scale: gtk::Scale,

    chapter_treeview: gtk::TreeView,
    chapter_store: gtk::ListStore,
    chapter_starts: Vec<(u64, usize)>,
    current_chapter_id: usize,

    thumbnail: Rc<RefCell<Option<ImageSurface>>>,
}

impl InfoController {
    pub fn new(builder: &gtk::Builder) -> Self {
        // need a RefCell because the callbacks will use immutable versions of ac
        // when the UI controllers will get a mutable version from time to time
        let mut this = InfoController {
            drawingarea: builder.get_object("thumbnail-drawingarea").unwrap(),

            title_lbl: builder.get_object("title-lbl").unwrap(),
            artist_lbl: builder.get_object("artist-lbl").unwrap(),
            container_lbl: builder.get_object("container-lbl").unwrap(),
            audio_codec_lbl: builder.get_object("audio_codec-lbl").unwrap(),
            video_codec_lbl: builder.get_object("video_codec-lbl").unwrap(),
            duration_lbl: builder.get_object("duration-lbl").unwrap(),
            position_lbl: builder.get_object("position-lbl").unwrap(),
            timeline_scale: builder.get_object("timeline-scale").unwrap(),

            chapter_treeview: builder.get_object("chapter-treeview").unwrap(),
            // columns: Id, Title, Start, End
            chapter_store: gtk::ListStore::new(&[gtk::Type::I32, gtk::Type::String, gtk::Type::String, gtk::Type::String]),
            chapter_starts: Vec::<(u64, usize)>::new(),
            current_chapter_id: ::std::usize::MAX,

            thumbnail: Rc::new(RefCell::new(None)),
        };

        this.cleanup();

        this.chapter_treeview.set_model(Some(&this.chapter_store));
        this.add_chapter_column("Id", 0, false);
        this.add_chapter_column("Title", 1, true);
        this.add_chapter_column("Start", 2, false);
        this.add_chapter_column("End", 3, false);

        let thumbnail_weak = Rc::downgrade(&this.thumbnail);
        this.drawingarea.connect_draw(move |drawing_area, cairo_ctx| {
            if let Some(thumbnail_rc) = thumbnail_weak.upgrade() {
                let thumbnail_opt = thumbnail_rc.borrow();
                if let Some(ref thumbnail) = *thumbnail_opt {
                    let surface = &thumbnail.surface;

                    let allocation = drawing_area.get_allocation();
                    let alloc_ratio = allocation.width as f64 / allocation.height as f64;
                    let surface_ratio = surface.get_width() as f64 / surface.get_height() as f64;
                    let scale = if surface_ratio < alloc_ratio {
                        allocation.height as f64 / surface.get_height() as f64
                    }
                    else {
                        allocation.width as f64 / surface.get_width() as f64
                    };
                    let x = (allocation.width as f64 / scale - surface.get_width() as f64).abs() / 2f64;
                    let y = (allocation.height as f64 / scale - surface.get_height() as f64).abs() / 2f64;

                    cairo_ctx.scale(scale, scale);
                    cairo_ctx.set_source_surface(surface, x, y);
                    cairo_ctx.paint();
                }
            }

            Inhibit(true)
        });

        this
    }

    fn add_chapter_column(&self, title: &str, col_id: i32, can_expand: bool) {
        let col = gtk::TreeViewColumn::new();
        col.set_title(title);
        let renderer = gtk::CellRendererText::new();
        col.pack_start(&renderer, true);
        col.add_attribute(&renderer, "text", col_id);
        if can_expand {
            col.set_min_width(70);
            col.set_expand(can_expand);
        }
        self.chapter_treeview.append_column(&col);
    }

    pub fn new_media(&mut self, context: &Context) {
        let duration = context.get_duration();
        self.timeline_scale.set_range(0f64, duration as f64);
        self.duration_lbl.set_label(
            &format!("{}", Timestamp::format(duration))
        );

        self.chapter_store.clear();

        let mut has_image = false;
        {
            let mut info = context.info.lock()
                .expect("Failed to lock media info in InfoController");

            if let Some(thumbnail) = info.thumbnail.take() {
                if let Ok(image) = ImageSurface::from_aligned_image(thumbnail) {
                    let mut thumbnail_ref = self.thumbnail.borrow_mut();
                    *thumbnail_ref = Some(image);
                    has_image = true;
                }
            };

            self.title_lbl.set_label(&info.title);
            self.artist_lbl.set_label(&info.artist);
            // Fix container for mp3 audio files TODO: move this to MediaInfo
            let container = if info.video_codec.is_empty()
                && info.audio_codec.to_lowercase().find("mp3").is_some()
            {
                "MP3"
            }
            else {
                &info.container
            };
            self.container_lbl.set_label(container);
            self.audio_codec_lbl.set_label(
                if !info.audio_codec.is_empty() { &info.audio_codec } else { "-" }
            );
            self.video_codec_lbl.set_label(
                if !info.video_codec.is_empty() { &info.video_codec } else { "-" }
            );

            self.timeline_scale.clear_marks();
            self.chapter_starts.clear();

            // FIX for sample.mkv video: generate ids (TODO: remove)
            for (id, chapter) in info.chapters.iter().enumerate() {
                self.timeline_scale.add_mark(
                    chapter.start.nano_total as f64,
                    gtk::PositionType::Top,
                    None
                );
                self.chapter_store.insert_with_values(
                    None, &[0, 1, 2, 3],
                    &[&((id+1) as u32), &chapter.title(), &format!("{}", &chapter.start), &format!("{}", chapter.end)],
                );
                self.chapter_starts.push((chapter.start.nano_total, id));
            }

            // reverse chapters in order to ease seaching
            self.chapter_starts.reverse();
            self.current_chapter_id = ::std::usize::MAX;
        }

        if has_image {
            self.drawingarea.show();
            self.drawingarea.queue_draw();
        }
        else {
            self.drawingarea.hide();
        }
    }

    pub fn cleanup(&mut self) {
        self.title_lbl.set_text("");
        self.artist_lbl.set_text("");
        self.container_lbl.set_text("");
        self.audio_codec_lbl.set_text("");
        self.video_codec_lbl.set_text("");
        self.duration_lbl.set_text("00:00.000");
        self.chapter_store.clear();
        self.timeline_scale.clear_marks();
        self.timeline_scale.set_value(0f64);
        self.position_lbl.set_text("00:00.000");
    }

    pub fn tic(&mut self, position: u64) {
        self.timeline_scale.set_value(position as f64);
        self.position_lbl.set_text(&Timestamp::format(position));

        // reminder: chapters order is reversed
        for &(start, id) in self.chapter_starts.iter() {
            if start <= position {
                // found current chapter
                if self.current_chapter_id != id {
                    if let Some(store_iter) =
                        self.chapter_store.get_iter(
                            &gtk::TreePath::new_from_indicesv(&[id as i32])
                        )
                    {
                        self.chapter_treeview.get_selection()
                            .select_iter(&store_iter);
                    };

                    self.current_chapter_id = id;
                }

                break;
            }
        }
    }
}
<commit_msg>Rework chapters follow up (#14) This will also ease position retrieval for seek from the chapters' list<commit_after>extern crate gtk;
use gtk::prelude::*;

use std::rc::Rc;
use std::cell::RefCell;

use ::media::{Context, Timestamp};

use super::{ImageSurface};

pub struct InfoController {
    drawingarea: gtk::DrawingArea,

    title_lbl: gtk::Label,
    artist_lbl: gtk::Label,
    container_lbl: gtk::Label,
    audio_codec_lbl: gtk::Label,
    video_codec_lbl: gtk::Label,
    duration_lbl: gtk::Label,
    position_lbl: gtk::Label,
    timeline_scale: gtk::Scale,

    chapter_treeview: gtk::TreeView,
    chapter_store: gtk::ListStore,
    chapter_iter: Option<gtk::TreeIter>,

    thumbnail: Rc<RefCell<Option<ImageSurface>>>,
}

impl InfoController {
    pub fn new(builder: &gtk::Builder) -> Self {
        // need a RefCell because the callbacks will use immutable versions of ac
        // when the UI controllers will get a mutable version from time to time
        let mut this = InfoController {
            drawingarea: builder.get_object("thumbnail-drawingarea").unwrap(),

            title_lbl: builder.get_object("title-lbl").unwrap(),
            artist_lbl: builder.get_object("artist-lbl").unwrap(),
            container_lbl: builder.get_object("container-lbl").unwrap(),
            audio_codec_lbl: builder.get_object("audio_codec-lbl").unwrap(),
            video_codec_lbl: builder.get_object("video_codec-lbl").unwrap(),
            duration_lbl: builder.get_object("duration-lbl").unwrap(),
            position_lbl: builder.get_object("position-lbl").unwrap(),
            timeline_scale: builder.get_object("timeline-scale").unwrap(),

            chapter_treeview: builder.get_object("chapter-treeview").unwrap(),
            // columns: Id, Start, End, Title, StartStr, EndStr
            chapter_store: gtk::ListStore::new(&[
                gtk::Type::I32,
                gtk::Type::U64,
                gtk::Type::U64,
                gtk::Type::String,
                gtk::Type::String,
                gtk::Type::String
            ]),
            chapter_iter: None,

            thumbnail: Rc::new(RefCell::new(None)),
        };

        this.cleanup();

        this.chapter_treeview.set_model(Some(&this.chapter_store));
        this.add_chapter_column("Id", 0, false);
        this.add_chapter_column("Title", 3, true);
        this.add_chapter_column("Start", 4, false);
        this.add_chapter_column("End", 5, false);

        let thumbnail_weak = Rc::downgrade(&this.thumbnail);
        this.drawingarea.connect_draw(move |drawing_area, cairo_ctx| {
            if let Some(thumbnail_rc) = thumbnail_weak.upgrade() {
                let thumbnail_opt = thumbnail_rc.borrow();
                if let Some(ref thumbnail) = *thumbnail_opt {
                    let surface = &thumbnail.surface;

                    let allocation = drawing_area.get_allocation();
                    let alloc_ratio = allocation.width as f64 / allocation.height as f64;
                    let surface_ratio = surface.get_width() as f64 / surface.get_height() as f64;
                    let scale = if surface_ratio < alloc_ratio {
                        allocation.height as f64 / surface.get_height() as f64
                    }
                    else {
                        allocation.width as f64 / surface.get_width() as f64
                    };
                    let x = (allocation.width as f64 / scale - surface.get_width() as f64).abs() / 2f64;
                    let y = (allocation.height as f64 / scale - surface.get_height() as f64).abs() / 2f64;

                    cairo_ctx.scale(scale, scale);
                    cairo_ctx.set_source_surface(surface, x, y);
                    cairo_ctx.paint();
                }
            }

            Inhibit(true)
        });

        this
    }

    fn add_chapter_column(&self, title: &str, col_id: i32, can_expand: bool) {
        let col = gtk::TreeViewColumn::new();
        col.set_title(title);
        let renderer = gtk::CellRendererText::new();
        col.pack_start(&renderer, true);
        col.add_attribute(&renderer, "text", col_id);
        if can_expand {
            col.set_min_width(70);
            col.set_expand(can_expand);
        }
        self.chapter_treeview.append_column(&col);
    }

    pub fn new_media(&mut self, context: &Context) {
        let duration = context.get_duration();
        self.timeline_scale.set_range(0f64, duration as f64);
        self.duration_lbl.set_label(
            &format!("{}", Timestamp::format(duration))
        );

        self.chapter_store.clear();

        let mut has_image = false;
        {
            let mut info = context.info.lock()
                .expect("Failed to lock media info in InfoController");

            if let Some(thumbnail) = info.thumbnail.take() {
                if let Ok(image) = ImageSurface::from_aligned_image(thumbnail) {
                    let mut thumbnail_ref = self.thumbnail.borrow_mut();
                    *thumbnail_ref = Some(image);
                    has_image = true;
                }
            };

            self.title_lbl.set_label(&info.title);
            self.artist_lbl.set_label(&info.artist);
            // Fix container for mp3 audio files TODO: move this to MediaInfo
            let container = if info.video_codec.is_empty()
                && info.audio_codec.to_lowercase().find("mp3").is_some()
            {
                "MP3"
            }
            else {
                &info.container
            };
            self.container_lbl.set_label(container);
            self.audio_codec_lbl.set_label(
                if !info.audio_codec.is_empty() { &info.audio_codec } else { "-" }
            );
            self.video_codec_lbl.set_label(
                if !info.video_codec.is_empty() { &info.video_codec } else { "-" }
            );

            self.timeline_scale.clear_marks();
            self.chapter_iter = None;

            // FIX for sample.mkv video: generate ids (TODO: remove)
            for (id, chapter) in info.chapters.iter().enumerate() {
                self.timeline_scale.add_mark(
                    chapter.start.nano_total as f64,
                    gtk::PositionType::Top,
                    None
                );
                self.chapter_store.insert_with_values(
                    None, &[0, 1, 2, 3, 4, 5],
                    &[
                        &((id+1) as u32),
                        &chapter.start.nano_total,
                        &chapter.end.nano_total,
                        &chapter.title(),
                        &format!("{}", &chapter.start),
                        &format!("{}", chapter.end)
                    ],
                );
            }

            self.chapter_iter = self.chapter_store.get_iter_first();
        }

        if has_image {
            self.drawingarea.show();
            self.drawingarea.queue_draw();
        }
        else {
            self.drawingarea.hide();
        }
    }

    pub fn cleanup(&mut self) {
        self.title_lbl.set_text("");
        self.artist_lbl.set_text("");
        self.container_lbl.set_text("");
        self.audio_codec_lbl.set_text("");
        self.video_codec_lbl.set_text("");
        self.duration_lbl.set_text("00:00.000");
        *self.thumbnail.borrow_mut() = None;
        self.chapter_iter = None;
        self.chapter_store.clear();
        self.timeline_scale.clear_marks();
        self.timeline_scale.set_value(0f64);
        self.position_lbl.set_text("00:00.000");
    }

    pub fn tic(&mut self, position: u64) {
        self.timeline_scale.set_value(position as f64);
        self.position_lbl.set_text(&Timestamp::format(position));

        let mut done_with_chapters = false;

        if let Some(current_iter) = self.chapter_iter.as_mut() {
            if position >= self.chapter_store.get_value(&current_iter, 2)
                    .get::<u64>().unwrap() {
                // passed the end of current chapter
                // unselect current chapter
                self.chapter_treeview.get_selection()
                    .unselect_iter(&current_iter);

                if !self.chapter_store.iter_next(&current_iter) {
                    // no more chapters
                    done_with_chapters = true;
                }
            }

            if !done_with_chapters
            && position >= self.chapter_store.get_value(&current_iter, 1)
                    .get::<u64>().unwrap() // after current start
            && position < self.chapter_store.get_value(&current_iter, 2)
                    .get::<u64>().unwrap() { // before current end
                self.chapter_treeview.get_selection()
                    .select_iter(&current_iter);
            }
        }

        if done_with_chapters {
            self.chapter_iter = None;
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use cloudabi as abi;
use core::{
    cell::Cell,
    mem,
    sync::atomic::{AtomicU32, Ordering},
};
use std::{convert::TryFrom, thread, time::Instant};

extern "C" {
    #[thread_local]
    static __pthread_thread_id: abi::tid;
}

struct Lock {
    lock: AtomicU32,
}

impl Lock {
    #[inline]
    pub fn new() -> Self {
        Lock {
            lock: AtomicU32::new(abi::LOCK_UNLOCKED.0),
        }
    }

    #[inline]
    fn try_lock(&self) -> Option<LockGuard<'_>> {
        // Attempt to acquire the lock.
        if let Err(old) = self.lock.compare_exchange(
            abi::LOCK_UNLOCKED.0,
            unsafe { __pthread_thread_id.0 } | abi::LOCK_WRLOCKED.0,
            Ordering::Acquire,
            Ordering::Relaxed,
        ) {
            // Failure. Crash upon recursive acquisition.
            debug_assert_ne!(
                old & !abi::LOCK_KERNEL_MANAGED.0,
                unsafe { __pthread_thread_id.0 } | abi::LOCK_WRLOCKED.0,
                "Attempted to recursive write-lock a lock",
            );
            None
        } else {
            Some(LockGuard { inner: &self })
        }
    }

    #[inline]
    pub fn lock(&self) -> LockGuard<'_> {
        self.try_lock().unwrap_or_else(|| {
            // Call into the kernel to acquire a write lock.
            unsafe {
                let subscription = abi::subscription {
                    type_: abi::eventtype::LOCK_WRLOCK,
                    union: abi::subscription_union {
                        lock: abi::subscription_lock {
                            lock: self.ptr(),
                            lock_scope: abi::scope::PRIVATE,
                        },
                    },
                    ..mem::zeroed()
                };
                let mut event: abi::event = mem::uninitialized();
                let mut nevents: usize = mem::uninitialized();
                let ret = abi::poll(&subscription, &mut event, 1, &mut nevents);
                debug_assert_eq!(ret, abi::errno::SUCCESS);
                debug_assert_eq!(event.error, abi::errno::SUCCESS);
            }
            LockGuard { inner: &self }
        })
    }

    #[inline]
    fn ptr(&self) -> *mut abi::lock {
        &self.lock as *const AtomicU32 as *mut abi::lock
    }
}

struct LockGuard<'a> {
    inner: &'a Lock,
}

impl LockGuard<'_> {
    #[inline]
    fn ptr(&self) -> *mut abi::lock {
        &self.inner.lock as *const AtomicU32 as *mut abi::lock
    }
}

impl Drop for LockGuard<'_> {
    fn drop(&mut self) {
        debug_assert_eq!(
            self.inner.lock.load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
            unsafe { __pthread_thread_id.0 } | abi::LOCK_WRLOCKED.0,
            "This lock is not write-locked by this thread"
        );

        if !self
            .inner
            .lock
            .compare_exchange(
                unsafe { __pthread_thread_id.0 } | abi::LOCK_WRLOCKED.0,
                abi::LOCK_UNLOCKED.0,
                Ordering::Release,
                Ordering::Relaxed,
            )
            .is_ok()
        {
            // Lock is managed by kernelspace. Call into the kernel
            // to unblock waiting threads.
            let ret = unsafe { abi::lock_unlock(self.ptr(), abi::scope::PRIVATE) };
            debug_assert_eq!(ret, abi::errno::SUCCESS);
        }
    }
}

struct Condvar {
    condvar: AtomicU32,
}

impl Condvar {
    #[inline]
    pub fn new() -> Self {
        Condvar {
            condvar: AtomicU32::new(abi::CONDVAR_HAS_NO_WAITERS.0),
        }
    }

    #[inline]
    pub fn wait(&self, lock: &LockGuard<'_>) {
        unsafe {
            let subscription = abi::subscription {
                type_: abi::eventtype::CONDVAR,
                union: abi::subscription_union {
                    condvar: abi::subscription_condvar {
                        condvar: self.ptr(),
                        condvar_scope: abi::scope::PRIVATE,
                        lock: lock.ptr(),
                        lock_scope: abi::scope::PRIVATE,
                    },
                },
                ..mem::zeroed()
            };
            let mut event: abi::event = mem::uninitialized();
            let mut nevents: usize = mem::uninitialized();

            let ret = abi::poll(&subscription, &mut event, 1, &mut nevents);
            debug_assert_eq!(ret, abi::errno::SUCCESS);
            debug_assert_eq!(event.error, abi::errno::SUCCESS);
        }
    }

    /// Waits for a signal on the condvar.
    /// Returns false if it times out before anyone notified us.
    #[inline]
    pub fn wait_timeout(&self, lock: &LockGuard<'_>, timeout: abi::timestamp) -> bool {
        unsafe {
            let subscriptions = [
                abi::subscription {
                    type_: abi::eventtype::CONDVAR,
                    union: abi::subscription_union {
                        condvar: abi::subscription_condvar {
                            condvar: self.ptr(),
                            condvar_scope: abi::scope::PRIVATE,
                            lock: lock.ptr(),
                            lock_scope: abi::scope::PRIVATE,
                        },
                    },
                    ..mem::zeroed()
                },
                abi::subscription {
                    type_: abi::eventtype::CLOCK,
                    union: abi::subscription_union {
                        clock: abi::subscription_clock {
                            clock_id: abi::clockid::MONOTONIC,
                            timeout,
                            ..mem::zeroed()
                        },
                    },
                    ..mem::zeroed()
                },
            ];
            let mut events: [abi::event; 2] = mem::uninitialized();
            let mut nevents: usize = mem::uninitialized();

            let ret = abi::poll(subscriptions.as_ptr(), events.as_mut_ptr(), 2, &mut nevents);
            debug_assert_eq!(ret, abi::errno::SUCCESS);
            for i in 0..nevents {
                debug_assert_eq!(events[i].error, abi::errno::SUCCESS);
                if events[i].type_ == abi::eventtype::CONDVAR {
                    return true;
                }
            }
        }
        false
    }

    #[inline]
    pub fn notify(&self) {
        let ret = unsafe { abi::condvar_signal(self.ptr(), abi::scope::PRIVATE, 1) };
        debug_assert_eq!(ret, abi::errno::SUCCESS);
    }

    #[inline]
    fn ptr(&self) -> *mut abi::condvar {
        &self.condvar as *const AtomicU32 as *mut abi::condvar
    }
}

// Helper type for putting a thread to sleep until some other thread wakes it up
pub struct ThreadParker {
    should_park: Cell<bool>,
    lock: Lock,
    condvar: Condvar,
}

impl super::ThreadParkerT for ThreadParker {
    type UnparkHandle = UnparkHandle<'_>;

    const IS_CHEAP_TO_CONSTRUCT: bool = true;

    #[inline]
    fn new() -> ThreadParker {
        ThreadParker {
            should_park: Cell::new(false),
            lock: Lock::new(),
            condvar: Condvar::new(),
        }
    }

    #[inline]
    fn prepare_park(&self) {
        self.should_park.set(true);
    }

    #[inline]
    fn timed_out(&self) -> bool {
        // We need to grab the lock here because another thread may be
        // concurrently executing UnparkHandle::unpark, which is done without
        // holding the queue lock.
        let _guard = self.lock.lock();
        self.should_park.get()
    }

    #[inline]
    fn park(&self) {
        let guard = self.lock.lock();
        while self.should_park.get() {
            self.condvar.wait(&guard);
        }
    }

    #[inline]
    fn park_until(&self, timeout: Instant) -> bool {
        let guard = self.lock.lock();
        while self.should_park.get() {
            if let Some(duration_left) = timeout.checked_duration_since(Instant::now()) {
                if let Ok(nanos_left) = abi::timestamp::try_from(duration_left.as_nanos()) {
                    self.condvar.wait_timeout(&guard, nanos_left);
                } else {
                    // remaining timeout overflows an abi::timestamp. Sleep indefinitely
                    self.condvar.wait(&guard);
                }
            } else {
                // We timed out
                return false;
            }
        }
        true
    }

    #[inline]
    fn unpark_lock(&self) -> UnparkHandle<'_> {
        let _lock_guard = self.lock.lock();

        UnparkHandle {
            thread_parker: self,
            _lock_guard,
        }
    }
}

pub struct UnparkHandle<'a> {
    thread_parker: *const ThreadParker,
    _lock_guard: LockGuard<'a>,
}

impl super::UnparkHandleT for UnparkHandle<'_> {
    #[inline]
    fn unpark(self) {
        unsafe {
            (*self.thread_parker).should_park.set(false);

            // We notify while holding the lock here to avoid races with the target
            // thread. In particular, the thread could exit after we unlock the
            // mutex, which would make the condvar access invalid memory.
            (*self.thread_parker).condvar.notify();
        }
    }
}

#[inline]
pub fn thread_yield() {
    thread::yield_now();
}
<commit_msg>Fix CloudABI ThreadParker<commit_after>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use cloudabi as abi;
use core::{
    cell::Cell,
    mem,
    sync::atomic::{AtomicU32, Ordering},
};
use std::{convert::TryFrom, thread, time::Instant};

extern "C" {
    #[thread_local]
    static __pthread_thread_id: abi::tid;
}

struct Lock {
    lock: AtomicU32,
}

impl Lock {
    pub fn new() -> Self {
        Lock {
            lock: AtomicU32::new(abi::LOCK_UNLOCKED.0),
        }
    }

    /// # Safety
    ///
    /// See `Lock::lock`.
    unsafe fn try_lock(&self) -> Option<LockGuard> {
        // Attempt to acquire the lock.
        if let Err(old) = self.lock.compare_exchange(
            abi::LOCK_UNLOCKED.0,
            __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
            Ordering::Acquire,
            Ordering::Relaxed,
        ) {
            // Failure. Crash upon recursive acquisition.
            debug_assert_ne!(
                old & !abi::LOCK_KERNEL_MANAGED.0,
                __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
                "Attempted to recursive write-lock a lock",
            );
            None
        } else {
            Some(LockGuard { lock: &self.lock })
        }
    }

    /// # Safety
    ///
    /// This method is unsafe because the `LockGuard` has a raw pointer into this `Lock`
    /// that it will access on drop to unlock the lock. So make sure the `LockGuard` goes
    /// out of scope before the `Lock` it came from moves or goes out of scope.
    pub unsafe fn lock(&self) -> LockGuard {
        self.try_lock().unwrap_or_else(|| {
            // Call into the kernel to acquire a write lock.
            let subscription = abi::subscription {
                type_: abi::eventtype::LOCK_WRLOCK,
                union: abi::subscription_union {
                    lock: abi::subscription_lock {
                        lock: self.ptr(),
                        lock_scope: abi::scope::PRIVATE,
                    },
                },
                ..mem::zeroed()
            };
            let mut event: abi::event = mem::uninitialized();
            let mut nevents: usize = mem::uninitialized();
            let ret = abi::poll(&subscription, &mut event, 1, &mut nevents);
            debug_assert_eq!(ret, abi::errno::SUCCESS);
            debug_assert_eq!(event.error, abi::errno::SUCCESS);

            LockGuard { lock: &self.lock }
        })
    }

    fn ptr(&self) -> *mut abi::lock {
        &self.lock as *const AtomicU32 as *mut abi::lock
    }
}

struct LockGuard {
    lock: *const AtomicU32,
}

impl LockGuard {
    fn ptr(&self) -> *mut abi::lock {
        self.lock as *mut abi::lock
    }
}

impl Drop for LockGuard {
    fn drop(&mut self) {
        let lock = unsafe { &*self.lock };
        debug_assert_eq!(
            lock.load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
            unsafe { __pthread_thread_id.0 } | abi::LOCK_WRLOCKED.0,
            "This lock is not write-locked by this thread"
        );

        if !lock
            .compare_exchange(
                unsafe { __pthread_thread_id.0 } | abi::LOCK_WRLOCKED.0,
                abi::LOCK_UNLOCKED.0,
                Ordering::Release,
                Ordering::Relaxed,
            )
            .is_ok()
        {
            // Lock is managed by kernelspace. Call into the kernel
            // to unblock waiting threads.
            let ret = unsafe { abi::lock_unlock(self.lock  as *mut abi::lock, abi::scope::PRIVATE) };
            debug_assert_eq!(ret, abi::errno::SUCCESS);
        }
    }
}

struct Condvar {
    condvar: AtomicU32,
}

impl Condvar {
    pub fn new() -> Self {
        Condvar {
            condvar: AtomicU32::new(abi::CONDVAR_HAS_NO_WAITERS.0),
        }
    }

    pub fn wait(&self, lock: &LockGuard) {
        unsafe {
            let subscription = abi::subscription {
                type_: abi::eventtype::CONDVAR,
                union: abi::subscription_union {
                    condvar: abi::subscription_condvar {
                        condvar: self.ptr(),
                        condvar_scope: abi::scope::PRIVATE,
                        lock: lock.ptr(),
                        lock_scope: abi::scope::PRIVATE,
                    },
                },
                ..mem::zeroed()
            };
            let mut event: abi::event = mem::uninitialized();
            let mut nevents: usize = mem::uninitialized();

            let ret = abi::poll(&subscription, &mut event, 1, &mut nevents);
            debug_assert_eq!(ret, abi::errno::SUCCESS);
            debug_assert_eq!(event.error, abi::errno::SUCCESS);
        }
    }

    /// Waits for a signal on the condvar.
    /// Returns false if it times out before anyone notified us.
    pub fn wait_timeout(&self, lock: &LockGuard, timeout: abi::timestamp) -> bool {
        unsafe {
            let subscriptions = [
                abi::subscription {
                    type_: abi::eventtype::CONDVAR,
                    union: abi::subscription_union {
                        condvar: abi::subscription_condvar {
                            condvar: self.ptr(),
                            condvar_scope: abi::scope::PRIVATE,
                            lock: lock.ptr(),
                            lock_scope: abi::scope::PRIVATE,
                        },
                    },
                    ..mem::zeroed()
                },
                abi::subscription {
                    type_: abi::eventtype::CLOCK,
                    union: abi::subscription_union {
                        clock: abi::subscription_clock {
                            clock_id: abi::clockid::MONOTONIC,
                            timeout,
                            ..mem::zeroed()
                        },
                    },
                    ..mem::zeroed()
                },
            ];
            let mut events: [abi::event; 2] = mem::uninitialized();
            let mut nevents: usize = mem::uninitialized();

            let ret = abi::poll(subscriptions.as_ptr(), events.as_mut_ptr(), 2, &mut nevents);
            debug_assert_eq!(ret, abi::errno::SUCCESS);
            for i in 0..nevents {
                debug_assert_eq!(events[i].error, abi::errno::SUCCESS);
                if events[i].type_ == abi::eventtype::CONDVAR {
                    return true;
                }
            }
        }
        false
    }

    pub fn notify(&self) {
        let ret = unsafe { abi::condvar_signal(self.ptr(), abi::scope::PRIVATE, 1) };
        debug_assert_eq!(ret, abi::errno::SUCCESS);
    }

    fn ptr(&self) -> *mut abi::condvar {
        &self.condvar as *const AtomicU32 as *mut abi::condvar
    }
}

// Helper type for putting a thread to sleep until some other thread wakes it up
pub struct ThreadParker {
    should_park: Cell<bool>,
    lock: Lock,
    condvar: Condvar,
}

impl super::ThreadParkerT for ThreadParker {
    type UnparkHandle = UnparkHandle;

    const IS_CHEAP_TO_CONSTRUCT: bool = true;

    fn new() -> ThreadParker {
        ThreadParker {
            should_park: Cell::new(false),
            lock: Lock::new(),
            condvar: Condvar::new(),
        }
    }

    unsafe fn prepare_park(&self) {
        self.should_park.set(true);
    }

    unsafe fn timed_out(&self) -> bool {
        // We need to grab the lock here because another thread may be
        // concurrently executing UnparkHandle::unpark, which is done without
        // holding the queue lock.
        let _guard = self.lock.lock();
        self.should_park.get()
    }

    unsafe fn park(&self) {
        let guard = self.lock.lock();
        while self.should_park.get() {
            self.condvar.wait(&guard);
        }
    }

    unsafe fn park_until(&self, timeout: Instant) -> bool {
        let guard = self.lock.lock();
        while self.should_park.get() {
            if let Some(duration_left) = timeout.checked_duration_since(Instant::now()) {
                if let Ok(nanos_left) = abi::timestamp::try_from(duration_left.as_nanos()) {
                    self.condvar.wait_timeout(&guard, nanos_left);
                } else {
                    // remaining timeout overflows an abi::timestamp. Sleep indefinitely
                    self.condvar.wait(&guard);
                }
            } else {
                // We timed out
                return false;
            }
        }
        true
    }

    unsafe fn unpark_lock(&self) -> UnparkHandle {
        let _lock_guard = self.lock.lock();

        UnparkHandle {
            thread_parker: self,
            _lock_guard,
        }
    }
}

pub struct UnparkHandle {
    thread_parker: *const ThreadParker,
    _lock_guard: LockGuard,
}

impl super::UnparkHandleT for UnparkHandle {
    unsafe fn unpark(self) {
        (*self.thread_parker).should_park.set(false);

        // We notify while holding the lock here to avoid races with the target
        // thread. In particular, the thread could exit after we unlock the
        // mutex, which would make the condvar access invalid memory.
        (*self.thread_parker).condvar.notify();
    }
}

#[inline]
pub fn thread_yield() {
    thread::yield_now();
}
<|endoftext|>


<commit_before>// xfail-stage0
// xfail-stage1
// xfail-stage2
// xfail-stage3
// -*- rust -*-

// error-pattern: may not return

fn god_exists(int a) -> bool {
  be god_exists(a);
}

fn f(int a) -> int {
  if (god_exists(a)) {
    ret 5;
  }
}

fn main() {
  f(12);
}
<commit_msg>un-XFAIL forgot-ret and update error pattern<commit_after>// -*- rust -*-

// error-pattern: not all control paths return

fn god_exists(int a) -> bool {
  be god_exists(a);
}

fn f(int a) -> int {
  if (god_exists(a)) {
    ret 5;
  }
}

fn main() {
  f(12);
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![crate_name = "foo"]

// ignore-tidy-linelength

// @has foo/fn.bar.html '//*[@class="tooltip compile_fail"]/span' "This code doesn't compile so be extra careful!"
// @has foo/fn.bar.html '//*[@class="tooltip ignore"]/span' "Be careful when using this code, it's not being tested!"

/// foo
///
/// ```compile_fail
/// foo();
/// ```
///
/// ```ignore (tidy)
/// goo();
/// ```
///
/// ```
/// let x = 0;
/// ```
pub fn bar() -> usize { 2 }
<commit_msg>update codeblock-title test with new notice text<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![crate_name = "foo"]

// ignore-tidy-linelength

// @has foo/fn.bar.html '//*[@class="tooltip compile_fail"]/span' "This example deliberately fails to compile"
// @has foo/fn.bar.html '//*[@class="tooltip ignore"]/span' "This example is not tested"

/// foo
///
/// ```compile_fail
/// foo();
/// ```
///
/// ```ignore (tidy)
/// goo();
/// ```
///
/// ```
/// let x = 0;
/// ```
pub fn bar() -> usize { 2 }
<|endoftext|>


<commit_before>use inflector::Inflector;

use botocore::{Member, Operation, Service, Shape, ShapeType};
use super::{generate_field_name, mutate_type_name};

pub fn generate_struct_attributes(deserialized: bool) -> String {
    let mut derived = vec!["Default"];

    if deserialized {
        derived.push("Debug, Clone")
    }

    format!("#[derive({})]", derived.join(","))
}

pub fn generate_deserializer(name: &str, shape: &Shape, service: &Service) -> String {
    format!("struct {name}Deserializer;
            impl {name}Deserializer {{
                #[allow(unused_variables)]
                fn deserialize<'a, T: Peek + Next>(tag_name: &str, stack: &mut T)
                -> Result<{name}, XmlParseError> {{
                    {deserializer_body}
                }}
            }}",
            name = name,
            deserializer_body = generate_deserializer_body(name, shape, service))
}

pub fn generate_response_parser(service: &Service, operation: &Operation, mutable_result: bool) -> String {
    if operation.output.is_none() {
        return "let result = ();".to_string();
    }

    let shape_name = &operation.output.as_ref().unwrap().shape;
    let result_wrapper = &operation.output.as_ref().unwrap().result_wrapper;
    let output_shape = &service.shapes[shape_name];

    // if the 'payload' field on the output shape is a blob or string, it indicates that
    // the entire payload is set as one of the struct members, and not parsed
    match output_shape.payload {
        None => xml_body_parser(shape_name, result_wrapper, mutable_result),
        Some(ref payload_member) => {
            let payload_shape = &service.shapes[payload_member];
            match payload_shape.shape_type {
                payload_type if payload_type == ShapeType::Blob ||
                                payload_type == ShapeType::String => {
                    payload_body_parser(payload_type, shape_name, payload_member)
                }
                _ => xml_body_parser(shape_name, result_wrapper, mutable_result),
            }
        }
    }
}

fn payload_body_parser(payload_type: ShapeType,
                       output_shape: &str,
                       payload_member: &str)
                       -> String {
    let response_body = match payload_type {
        ShapeType::Blob => "Some(response.body.as_bytes().to_vec())",
        _ => "Some(response.body.to_owned())",
    };

    format!("
        let mut result = {output_shape}::default();
        result.{payload_member} = {response_body};
        ",
            output_shape = output_shape,
            payload_member = payload_member.to_snake_case(),
            response_body = response_body)
}

fn xml_body_parser(output_shape: &str, result_wrapper: &Option<String>, mutable_result: bool) -> String {
    let let_result = if mutable_result {
        "let mut result;"
    } else {
        "let result;"
    };

    let deserialize = match result_wrapper {
        &Some(ref tag_name) => {
            format!("try!(start_element(&actual_tag_name, &mut stack));
                     result = try!({output_shape}Deserializer::deserialize(\"{tag_name}\", &mut stack));
                     skip_tree(&mut stack);
                     try!(end_element(&actual_tag_name, &mut stack));",
                     output_shape = output_shape,
                     tag_name = tag_name)
        },
        &None => {
            format!("result = try!({output_shape}Deserializer::deserialize(&actual_tag_name, &mut stack));",
                     output_shape = output_shape)
        }
    };

    format!("
        {let_result}

        if response.body.is_empty() {{
            result = {output_shape}::default();
        }} else {{
            let reader = EventReader::new_with_config(
                response.body.as_bytes(),
                ParserConfig::new().trim_whitespace(true)
            );
            let mut stack = XmlResponse::new(reader.into_iter().peekable());
            let _start_document = stack.next();
            let actual_tag_name = try!(peek_at_name(&mut stack));
            {deserialize}
        }}",
            let_result = let_result,
            output_shape = output_shape,
            deserialize = deserialize)
}

fn generate_deserializer_body(name: &str, shape: &Shape, service: &Service) -> String {
    match shape.shape_type {
        ShapeType::List => generate_list_deserializer(shape),
        ShapeType::Map => generate_map_deserializer(shape),
        ShapeType::Structure => generate_struct_deserializer(name, service, shape),
        _ => generate_primitive_deserializer(shape),
    }
}

fn generate_list_deserializer(shape: &Shape) -> String {
    // flattened lists are just the list elements repeated without
    // an enclosing <FooList></FooList> tag
    if let Some(true) = shape.flattened {
        return generate_flat_list_deserializer(shape);
    }

    let location_name = shape.member
        .as_ref()
        .and_then(|m| m.location_name.to_owned())
        .unwrap_or_else(|| "member".to_owned());

    format!("
        let mut obj = vec![];
        try!(start_element(tag_name, stack));

        loop {{
            let next_event = match stack.peek() {{
                Some(&Ok(XmlEvent::EndElement {{ .. }})) => DeserializerNext::Close,
                Some(&Ok(XmlEvent::StartElement {{ ref name, .. }})) => DeserializerNext::Element(name.local_name.to_owned()),
                _ => DeserializerNext::Skip,
            }};

            match next_event {{
                DeserializerNext::Element(name) => {{
                    if name == \"{location_name}\" {{
                        obj.push(try!({member_name}Deserializer::deserialize(\"{location_name}\", stack)));
                    }} else {{
                        skip_tree(stack);
                    }}
                }},
                DeserializerNext::Close => {{
                    try!(end_element(tag_name, stack));
                    break;
                }}
                DeserializerNext::Skip => {{ stack.next(); }},
            }}
        }}

        Ok(obj)
        ",
            location_name = location_name,
            member_name = mutate_type_name(&shape.member_type()[..]))
}

fn generate_flat_list_deserializer(shape: &Shape) -> String {
    format!("
        let mut obj = vec![];

        loop {{

            let consume_next_tag = match stack.peek() {{
                Some(&Ok(XmlEvent::StartElement {{ ref name, .. }})) => name.local_name == tag_name,
                _ => false
            }};

            if consume_next_tag {{
                obj.push(try!({member_name}Deserializer::deserialize(tag_name, stack)));
            }} else {{
                break
            }}

        }}

        Ok(obj)
        ",
            member_name = mutate_type_name(shape.member_type()))
}

fn generate_map_deserializer(shape: &Shape) -> String {
    let key = shape.key.as_ref().unwrap();
    let value = shape.value.as_ref().unwrap();

    let entry_location = shape.location_name.as_ref()
                                            .map(String::as_ref)
                                            .unwrap_or_else(|| "entry");

    // the core of the map parser is the same whether or not it's flattened
    let entries_parser = format!(
        "
        let mut obj = ::std::collections::HashMap::new();

        while try!(peek_at_name(stack)) == \"{entry_location}\" {{
            try!(start_element(\"{entry_location}\", stack));
            let key = try!({key_type_name}Deserializer::deserialize(\"{key_tag_name}\", stack));
            let value = try!({value_type_name}Deserializer::deserialize(\"{value_tag_name}\", stack));
            obj.insert(key, value);
            try!(end_element(\"{entry_location}\", stack));
        }}
        ",
        key_tag_name = key.tag_name(),
        key_type_name = key.shape,
        value_tag_name = value.tag_name(),
        value_type_name = value.shape,
        entry_location = entry_location
    );

    // if the map is flattened, just return the entries parser
    // otherwise parse a start and end tag around the whole map
    match shape.flattened {
        Some(true) => format!("{entries_parser}
                               Ok(obj)",
                              entries_parser = entries_parser),
        _ => format!("try!(start_element(tag_name, stack));
                    {entries_parser}
                    try!(end_element(tag_name, stack));
                    Ok(obj)
                    ",
                    entries_parser = entries_parser)
    }
}

fn generate_primitive_deserializer(shape: &Shape) -> String {
    let statement = match shape.shape_type {
        ShapeType::String | ShapeType::Timestamp => "try!(characters(stack))",
        ShapeType::Integer => "i32::from_str(try!(characters(stack)).as_ref()).unwrap()",
        ShapeType::Long => "i64::from_str(try!(characters(stack)).as_ref()).unwrap()",
        ShapeType::Double => "f64::from_str(try!(characters(stack)).as_ref()).unwrap()",
        ShapeType::Float => "f32::from_str(try!(characters(stack)).as_ref()).unwrap()",
        ShapeType::Blob => "try!(characters(stack)).into_bytes()",
        ShapeType::Boolean => "bool::from_str(try!(characters(stack)).as_ref()).unwrap()",
        _ => panic!("Unknown primitive shape type"),
    };

    format!(
        "try!(start_element(tag_name, stack));
        let obj = {statement};
        try!(end_element(tag_name, stack));

        Ok(obj)
        ",
        statement = statement,
    )
}

fn generate_struct_deserializer(name: &str, service: &Service, shape: &Shape) -> String {
    let mut needs_xml_deserializer = false;

    // don't generate an xml deserializer if we don't need to
    for (_, member) in shape.members.as_ref().unwrap().iter() {
        match member.location.as_ref().map(String::as_ref) {
            Some("header") | Some("headers") => {}
            _ => needs_xml_deserializer = true,
        }
    }

    if !needs_xml_deserializer || shape.members.as_ref().unwrap().is_empty() {
        return format!(
            "try!(start_element(tag_name, stack));

            stack.next();

            let obj = {name}::default();

            try!(end_element(tag_name, stack));
            stack.next();

            Ok(obj)
            ",
            name = name,
        );
    }

    format!(
        "try!(start_element(tag_name, stack));

        let mut obj = {name}::default();

        loop {{
            let next_event = match stack.peek() {{
                Some(&Ok(XmlEvent::EndElement {{ ref name, .. }})) => DeserializerNext::Close,
                Some(&Ok(XmlEvent::StartElement {{ ref name, .. }})) => DeserializerNext::Element(name.local_name.to_owned()),
                _ => DeserializerNext::Skip,
            }};

            match next_event {{
                DeserializerNext::Element(name) => {{
                    match &name[..] {{
                        {struct_field_deserializers}
                        _ => skip_tree(stack),
                    }}
                }},
                DeserializerNext::Close => break,
                DeserializerNext::Skip => {{ stack.next(); }},
            }}
        }}

        try!(end_element(tag_name, stack));

        Ok(obj)
        ",
        name = name,
        struct_field_deserializers = generate_struct_field_deserializers(service, shape),
    )
}

fn generate_struct_field_deserializers(service: &Service, shape: &Shape) -> String {
    shape.members
        .as_ref()
        .unwrap()
        .iter()
        .filter_map(|(member_name, member)| {
            // look up member.shape in all_shapes.  use that shape.member.location_name
            let mut location_name = member.location_name.as_ref().unwrap_or(member_name);

            // skip deprecated and non-XML fields
            if member.deprecated() ||
               member.location == Some("header".to_owned()) ||
               member.location == Some("headers".to_owned()) {
                return None;
            }

            let member_shape = &service.shapes[&member.shape];

            // flattened lists have no surrounding tag, so match on the list member's tag
            if member_shape.shape_type == ShapeType::List && member_shape.flattened == Some(true) {
                let list_member = member_shape.member.as_ref().expect("list member undefined");
                if let Some(ref list_member_location) = list_member.location_name {
                    location_name = list_member_location;
                }
            }

            let parse_expression = generate_struct_field_parse_expression(shape,
                                                                          member_name,
                                                                          member,
                                                                          location_name);
            Some(format!(
            "\"{location_name}\" => {{
                obj.{field_name} = {parse_expression};
            }}",
            field_name = generate_field_name(member_name),
            parse_expression = parse_expression,
            location_name = location_name,
        ))

        })
        .collect::<Vec<String>>()
        .join("\n")
}

fn generate_struct_field_parse_expression(shape: &Shape,
                                          member_name: &str,
                                          member: &Member,
                                          location_name: &String)
                                          -> String {
    let expression = format!(
        "try!({name}Deserializer::deserialize(\"{location_name}\", stack))",
        name = member.shape,
        location_name = location_name,
    );

    if shape.required(member_name) {
        expression
    } else {
        format!("Some({})", expression)
    }
}
<commit_msg>raw_body must be used in parsing blob type<commit_after>use inflector::Inflector;

use botocore::{Member, Operation, Service, Shape, ShapeType};
use super::{generate_field_name, mutate_type_name};

pub fn generate_struct_attributes(deserialized: bool) -> String {
    let mut derived = vec!["Default"];

    if deserialized {
        derived.push("Debug, Clone")
    }

    format!("#[derive({})]", derived.join(","))
}

pub fn generate_deserializer(name: &str, shape: &Shape, service: &Service) -> String {
    format!("struct {name}Deserializer;
            impl {name}Deserializer {{
                #[allow(unused_variables)]
                fn deserialize<'a, T: Peek + Next>(tag_name: &str, stack: &mut T)
                -> Result<{name}, XmlParseError> {{
                    {deserializer_body}
                }}
            }}",
            name = name,
            deserializer_body = generate_deserializer_body(name, shape, service))
}

pub fn generate_response_parser(service: &Service, operation: &Operation, mutable_result: bool) -> String {
    if operation.output.is_none() {
        return "let result = ();".to_string();
    }

    let shape_name = &operation.output.as_ref().unwrap().shape;
    let result_wrapper = &operation.output.as_ref().unwrap().result_wrapper;
    let output_shape = &service.shapes[shape_name];

    // if the 'payload' field on the output shape is a blob or string, it indicates that
    // the entire payload is set as one of the struct members, and not parsed
    match output_shape.payload {
        None => xml_body_parser(shape_name, result_wrapper, mutable_result),
        Some(ref payload_member) => {
            let payload_shape = &service.shapes[payload_member];
            match payload_shape.shape_type {
                payload_type if payload_type == ShapeType::Blob ||
                                payload_type == ShapeType::String => {
                    payload_body_parser(payload_type, shape_name, payload_member)
                }
                _ => xml_body_parser(shape_name, result_wrapper, mutable_result),
            }
        }
    }
}

fn payload_body_parser(payload_type: ShapeType,
                       output_shape: &str,
                       payload_member: &str)
                       -> String {
    let response_body = match payload_type {
        ShapeType::Blob => "Some(response.raw_body.to_vec())",
        _ => "Some(response.body.to_owned())",
    };

    format!("
        let mut result = {output_shape}::default();
        result.{payload_member} = {response_body};
        ",
            output_shape = output_shape,
            payload_member = payload_member.to_snake_case(),
            response_body = response_body)
}

fn xml_body_parser(output_shape: &str, result_wrapper: &Option<String>, mutable_result: bool) -> String {
    let let_result = if mutable_result {
        "let mut result;"
    } else {
        "let result;"
    };

    let deserialize = match result_wrapper {
        &Some(ref tag_name) => {
            format!("try!(start_element(&actual_tag_name, &mut stack));
                     result = try!({output_shape}Deserializer::deserialize(\"{tag_name}\", &mut stack));
                     skip_tree(&mut stack);
                     try!(end_element(&actual_tag_name, &mut stack));",
                     output_shape = output_shape,
                     tag_name = tag_name)
        },
        &None => {
            format!("result = try!({output_shape}Deserializer::deserialize(&actual_tag_name, &mut stack));",
                     output_shape = output_shape)
        }
    };

    format!("
        {let_result}

        if response.body.is_empty() {{
            result = {output_shape}::default();
        }} else {{
            let reader = EventReader::new_with_config(
                response.body.as_bytes(),
                ParserConfig::new().trim_whitespace(true)
            );
            let mut stack = XmlResponse::new(reader.into_iter().peekable());
            let _start_document = stack.next();
            let actual_tag_name = try!(peek_at_name(&mut stack));
            {deserialize}
        }}",
            let_result = let_result,
            output_shape = output_shape,
            deserialize = deserialize)
}

fn generate_deserializer_body(name: &str, shape: &Shape, service: &Service) -> String {
    match shape.shape_type {
        ShapeType::List => generate_list_deserializer(shape),
        ShapeType::Map => generate_map_deserializer(shape),
        ShapeType::Structure => generate_struct_deserializer(name, service, shape),
        _ => generate_primitive_deserializer(shape),
    }
}

fn generate_list_deserializer(shape: &Shape) -> String {
    // flattened lists are just the list elements repeated without
    // an enclosing <FooList></FooList> tag
    if let Some(true) = shape.flattened {
        return generate_flat_list_deserializer(shape);
    }

    let location_name = shape.member
        .as_ref()
        .and_then(|m| m.location_name.to_owned())
        .unwrap_or_else(|| "member".to_owned());

    format!("
        let mut obj = vec![];
        try!(start_element(tag_name, stack));

        loop {{
            let next_event = match stack.peek() {{
                Some(&Ok(XmlEvent::EndElement {{ .. }})) => DeserializerNext::Close,
                Some(&Ok(XmlEvent::StartElement {{ ref name, .. }})) => DeserializerNext::Element(name.local_name.to_owned()),
                _ => DeserializerNext::Skip,
            }};

            match next_event {{
                DeserializerNext::Element(name) => {{
                    if name == \"{location_name}\" {{
                        obj.push(try!({member_name}Deserializer::deserialize(\"{location_name}\", stack)));
                    }} else {{
                        skip_tree(stack);
                    }}
                }},
                DeserializerNext::Close => {{
                    try!(end_element(tag_name, stack));
                    break;
                }}
                DeserializerNext::Skip => {{ stack.next(); }},
            }}
        }}

        Ok(obj)
        ",
            location_name = location_name,
            member_name = mutate_type_name(&shape.member_type()[..]))
}

fn generate_flat_list_deserializer(shape: &Shape) -> String {
    format!("
        let mut obj = vec![];

        loop {{

            let consume_next_tag = match stack.peek() {{
                Some(&Ok(XmlEvent::StartElement {{ ref name, .. }})) => name.local_name == tag_name,
                _ => false
            }};

            if consume_next_tag {{
                obj.push(try!({member_name}Deserializer::deserialize(tag_name, stack)));
            }} else {{
                break
            }}

        }}

        Ok(obj)
        ",
            member_name = mutate_type_name(shape.member_type()))
}

fn generate_map_deserializer(shape: &Shape) -> String {
    let key = shape.key.as_ref().unwrap();
    let value = shape.value.as_ref().unwrap();

    let entry_location = shape.location_name.as_ref()
                                            .map(String::as_ref)
                                            .unwrap_or_else(|| "entry");

    // the core of the map parser is the same whether or not it's flattened
    let entries_parser = format!(
        "
        let mut obj = ::std::collections::HashMap::new();

        while try!(peek_at_name(stack)) == \"{entry_location}\" {{
            try!(start_element(\"{entry_location}\", stack));
            let key = try!({key_type_name}Deserializer::deserialize(\"{key_tag_name}\", stack));
            let value = try!({value_type_name}Deserializer::deserialize(\"{value_tag_name}\", stack));
            obj.insert(key, value);
            try!(end_element(\"{entry_location}\", stack));
        }}
        ",
        key_tag_name = key.tag_name(),
        key_type_name = key.shape,
        value_tag_name = value.tag_name(),
        value_type_name = value.shape,
        entry_location = entry_location
    );

    // if the map is flattened, just return the entries parser
    // otherwise parse a start and end tag around the whole map
    match shape.flattened {
        Some(true) => format!("{entries_parser}
                               Ok(obj)",
                              entries_parser = entries_parser),
        _ => format!("try!(start_element(tag_name, stack));
                    {entries_parser}
                    try!(end_element(tag_name, stack));
                    Ok(obj)
                    ",
                    entries_parser = entries_parser)
    }
}

fn generate_primitive_deserializer(shape: &Shape) -> String {
    let statement = match shape.shape_type {
        ShapeType::String | ShapeType::Timestamp => "try!(characters(stack))",
        ShapeType::Integer => "i32::from_str(try!(characters(stack)).as_ref()).unwrap()",
        ShapeType::Long => "i64::from_str(try!(characters(stack)).as_ref()).unwrap()",
        ShapeType::Double => "f64::from_str(try!(characters(stack)).as_ref()).unwrap()",
        ShapeType::Float => "f32::from_str(try!(characters(stack)).as_ref()).unwrap()",
        ShapeType::Blob => "try!(characters(stack)).into_bytes()",
        ShapeType::Boolean => "bool::from_str(try!(characters(stack)).as_ref()).unwrap()",
        _ => panic!("Unknown primitive shape type"),
    };

    format!(
        "try!(start_element(tag_name, stack));
        let obj = {statement};
        try!(end_element(tag_name, stack));

        Ok(obj)
        ",
        statement = statement,
    )
}

fn generate_struct_deserializer(name: &str, service: &Service, shape: &Shape) -> String {
    let mut needs_xml_deserializer = false;

    // don't generate an xml deserializer if we don't need to
    for (_, member) in shape.members.as_ref().unwrap().iter() {
        match member.location.as_ref().map(String::as_ref) {
            Some("header") | Some("headers") => {}
            _ => needs_xml_deserializer = true,
        }
    }

    if !needs_xml_deserializer || shape.members.as_ref().unwrap().is_empty() {
        return format!(
            "try!(start_element(tag_name, stack));

            stack.next();

            let obj = {name}::default();

            try!(end_element(tag_name, stack));
            stack.next();

            Ok(obj)
            ",
            name = name,
        );
    }

    format!(
        "try!(start_element(tag_name, stack));

        let mut obj = {name}::default();

        loop {{
            let next_event = match stack.peek() {{
                Some(&Ok(XmlEvent::EndElement {{ ref name, .. }})) => DeserializerNext::Close,
                Some(&Ok(XmlEvent::StartElement {{ ref name, .. }})) => DeserializerNext::Element(name.local_name.to_owned()),
                _ => DeserializerNext::Skip,
            }};

            match next_event {{
                DeserializerNext::Element(name) => {{
                    match &name[..] {{
                        {struct_field_deserializers}
                        _ => skip_tree(stack),
                    }}
                }},
                DeserializerNext::Close => break,
                DeserializerNext::Skip => {{ stack.next(); }},
            }}
        }}

        try!(end_element(tag_name, stack));

        Ok(obj)
        ",
        name = name,
        struct_field_deserializers = generate_struct_field_deserializers(service, shape),
    )
}

fn generate_struct_field_deserializers(service: &Service, shape: &Shape) -> String {
    shape.members
        .as_ref()
        .unwrap()
        .iter()
        .filter_map(|(member_name, member)| {
            // look up member.shape in all_shapes.  use that shape.member.location_name
            let mut location_name = member.location_name.as_ref().unwrap_or(member_name);

            // skip deprecated and non-XML fields
            if member.deprecated() ||
               member.location == Some("header".to_owned()) ||
               member.location == Some("headers".to_owned()) {
                return None;
            }

            let member_shape = &service.shapes[&member.shape];

            // flattened lists have no surrounding tag, so match on the list member's tag
            if member_shape.shape_type == ShapeType::List && member_shape.flattened == Some(true) {
                let list_member = member_shape.member.as_ref().expect("list member undefined");
                if let Some(ref list_member_location) = list_member.location_name {
                    location_name = list_member_location;
                }
            }

            let parse_expression = generate_struct_field_parse_expression(shape,
                                                                          member_name,
                                                                          member,
                                                                          location_name);
            Some(format!(
            "\"{location_name}\" => {{
                obj.{field_name} = {parse_expression};
            }}",
            field_name = generate_field_name(member_name),
            parse_expression = parse_expression,
            location_name = location_name,
        ))

        })
        .collect::<Vec<String>>()
        .join("\n")
}

fn generate_struct_field_parse_expression(shape: &Shape,
                                          member_name: &str,
                                          member: &Member,
                                          location_name: &String)
                                          -> String {
    let expression = format!(
        "try!({name}Deserializer::deserialize(\"{location_name}\", stack))",
        name = member.shape,
        location_name = location_name,
    );

    if shape.required(member_name) {
        expression
    } else {
        format!("Some({})", expression)
    }
}
<|endoftext|>