<commit_before>use nom::{
    branch::alt,
    bytes::complete::{escaped_transform, is_not, tag, take},
    character::complete::{space0, space1},
    combinator::{complete, eof, map, recognize, value},
    multi::{many0, many1, separated_list0, separated_list1},
    sequence::{delimited, preceded},
    IResult,
};

fn unquoted_token(input: &str) -> IResult<&str, String> {
    let mut parser = map(recognize(is_not(" ;")), String::from);

    parser(input)
}

fn quoted_token<'a>(input: &'a str) -> IResult<&'a str, String> {
    let parser = escaped_transform(is_not(r#""\"#), '\\', |control_char: &'a str| {
        alt((
            value(r#"""#, tag(r#"""#)),
            value(r#"\"#, tag(r#"\"#)),
            value("\r", tag("r")),
            value("\n", tag("n")),
            value("\t", tag("t")),
            take(1usize), // all other escaped characters are passed through, unmodified
        ))(control_char)
    });

    let double_quote = tag("\"");
    let mut parser = delimited(&double_quote, parser, alt((&double_quote, eof)));

    parser(input)
}

fn token(input: &str) -> IResult<&str, String> {
    let mut parser = alt((quoted_token, unquoted_token));
    parser(input)
}

fn operation_with_args(input: &str) -> IResult<&str, Vec<String>> {
    let mut parser = separated_list1(space1, token);
    parser(input)
}

fn semicolon(input: &str) -> IResult<&str, &str> {
    delimited(space0, tag(";"), space0)(input)
}

fn operation_sequence(input: &str) -> IResult<&str, Vec<Vec<String>>> {
    let parser = separated_list0(many1(semicolon), operation_with_args);
    let parser = delimited(many0(semicolon), parser, many0(semicolon));
    let parser = preceded(space0, parser);

    let mut parser = complete(parser);

    parser(input)
}

/// Split a semicolon-separated list of operations into a vector. Each operation is represented by
/// a non-empty sub-vector, where the first element is the name of the operation, and the rest of
/// the elements are operation's arguments.
///
/// Tokens can be double-quoted. Such tokens can contain spaces and C-like escaped sequences: `\n`
/// for newline, `\r` for carriage return, `\t` for tab, `\"` for double quote, `\\` for backslash.
/// Unsupported sequences are stripped of the escaping, e.g. `\e` turns into `e`.
///
/// This function assumes that the input string:
/// 1. doesn't contain a comment;
/// 2. doesn't contain backticks that need to be processed.
///
/// Returns `None` if the input could not be parsed.
pub fn tokenize_operation_sequence(input: &str) -> Option<Vec<Vec<String>>> {
    match operation_sequence(input) {
        Ok((_leftovers, tokens)) => Some(tokens),
        Err(_error) => None,
    }
}

#[cfg(test)]
mod tests {
    use super::tokenize_operation_sequence;

    #[test]
    fn t_tokenize_operation_sequence_works_for_all_cpp_inputs() {
        assert_eq!(
            tokenize_operation_sequence("").unwrap(),
            Vec::<Vec<String>>::new()
        );
        assert_eq!(
            tokenize_operation_sequence("open").unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence("open-all-unread-in-browser-and-mark-read").unwrap(),
            vec![vec!["open-all-unread-in-browser-and-mark-read"]]
        );
        assert_eq!(
            tokenize_operation_sequence("; ; ; ;").unwrap(),
            Vec::<Vec<String>>::new()
        );
        assert_eq!(
            tokenize_operation_sequence("open ; next").unwrap(),
            vec![vec!["open"], vec!["next"]]
        );
        assert_eq!(
            tokenize_operation_sequence("open ; next ; prev").unwrap(),
            vec![vec!["open"], vec!["next"], vec!["prev"]]
        );
        assert_eq!(
            tokenize_operation_sequence("open ; next ; prev ; quit").unwrap(),
            vec![vec!["open"], vec!["next"], vec!["prev"], vec!["quit"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"set "arg 1""#).unwrap(),
            vec![vec!["set", "arg 1"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"set "arg 1" ; set "arg 2" "arg 3""#).unwrap(),
            vec![vec!["set", "arg 1"], vec!["set", "arg 2", "arg 3"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"set browser "firefox"; open-in-browser"#).unwrap(),
            vec![vec!["set", "browser", "firefox"], vec!["open-in-browser"]]
        );
        assert_eq!(
            tokenize_operation_sequence("set browser firefox; open-in-browser").unwrap(),
            vec![vec!["set", "browser", "firefox"], vec!["open-in-browser"]]
        );
        assert_eq!(
            tokenize_operation_sequence("open-in-browser; quit").unwrap(),
            vec![vec!["open-in-browser"], vec!["quit"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"open; set browser "firefox --private-window"; quit"#)
                .unwrap(),
            vec![
                vec!["open"],
                vec!["set", "browser", "firefox --private-window"],
                vec!["quit"]
            ]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"open ;set browser "firefox --private-window" ;quit"#)
                .unwrap(),
            vec![
                vec!["open"],
                vec!["set", "browser", "firefox --private-window"],
                vec!["quit"]
            ]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"open;set browser "firefox --private-window";quit"#)
                .unwrap(),
            vec![
                vec!["open"],
                vec!["set", "browser", "firefox --private-window"],
                vec!["quit"]
            ]
        );
        assert_eq!(
            tokenize_operation_sequence("; ;; ; open",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence(";;; ;; ; open",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence(";;; ;; ; open ;",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence(";;; ;; ; open ;; ;",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence(";;; ;; ; open ; ;;;;",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence(";;; open ; ;;;;",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence("; open ;; ;; ;",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence("open ; ;;; ;;",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence(
                r#"set browser "sleep 3; do-something ; echo hi"; open-in-browser"#
            )
            .unwrap(),
            vec![
                vec!["set", "browser", "sleep 3; do-something ; echo hi"],
                vec!["open-in-browser"]
            ]
        );
    }

    #[test]
    fn t_tokenize_operation_sequence_ignores_escaped_sequences_outside_double_quotes() {
        assert_eq!(
            tokenize_operation_sequence(r#"\t"#).unwrap(),
            vec![vec![r#"\t"#]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"\r"#).unwrap(),
            vec![vec![r#"\r"#]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"\n"#).unwrap(),
            vec![vec![r#"\n"#]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"\v"#).unwrap(),
            vec![vec![r#"\v"#]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"\""#).unwrap(),
            vec![vec![r#"\""#]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"\\"#).unwrap(),
            vec![vec![r#"\\"#]]
        );
    }

    #[test]
    fn t_tokenize_operation_sequence_expands_escaped_sequences_inside_double_quotes() {
        assert_eq!(
            tokenize_operation_sequence(r#""\t""#).unwrap(),
            vec![vec!["\t"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\r""#).unwrap(),
            vec![vec!["\r"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\n""#).unwrap(),
            vec![vec!["\n"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\"""#).unwrap(),
            vec![vec!["\""]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\\""#).unwrap(),
            vec![vec!["\\"]]
        );
    }

    #[test]
    fn t_tokenize_operation_sequence_passes_through_unsupported_escaped_chars_inside_double_quotes()
    {
        assert_eq!(
            tokenize_operation_sequence(r#""\1""#).unwrap(),
            vec![vec!["1"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\W""#).unwrap(),
            vec![vec!["W"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\b""#).unwrap(),
            vec![vec!["b"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\d""#).unwrap(),
            vec![vec!["d"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\x""#).unwrap(),
            vec![vec!["x"]]
        );
    }

    #[test]
    fn t_tokenize_operation_sequence_implicitly_closes_double_quotes_at_end_of_input() {
        assert_eq!(
            tokenize_operation_sequence(r#"set "arg 1"#).unwrap(),
            vec![vec!["set", "arg 1"]]
        );
    }

    #[test]
    fn t_tokenize_operation_sequence_allows_single_character_unquoted() {
        assert_eq!(
            tokenize_operation_sequence(r#"set a b"#).unwrap(),
            vec![vec!["set", "a", "b"]]
        );
    }

    #[test]
    fn t_tokenize_operation_sequence_ignores_leading_and_trailing_spaces() {
        assert_eq!(
            tokenize_operation_sequence(r#"  set a b    "#).unwrap(),
            vec![vec!["set", "a", "b"]]
        );
    }
}
<commit_msg>Allow tabs as separation between macro arguments<commit_after>use nom::{
    branch::alt,
    bytes::complete::{escaped_transform, is_not, tag, take},
    character::complete::one_of,
    combinator::{complete, eof, map, recognize, value},
    multi::{many0, many1, separated_list0, separated_list1},
    sequence::{delimited, preceded},
    IResult,
};

fn unquoted_token(input: &str) -> IResult<&str, String> {
    let mut parser = map(recognize(is_not("\t ;")), String::from);

    parser(input)
}

fn quoted_token<'a>(input: &'a str) -> IResult<&'a str, String> {
    let parser = escaped_transform(is_not(r#""\"#), '\\', |control_char: &'a str| {
        alt((
            value(r#"""#, tag(r#"""#)),
            value(r#"\"#, tag(r#"\"#)),
            value("\r", tag("r")),
            value("\n", tag("n")),
            value("\t", tag("t")),
            take(1usize), // all other escaped characters are passed through, unmodified
        ))(control_char)
    });

    let double_quote = tag("\"");
    let mut parser = delimited(&double_quote, parser, alt((&double_quote, eof)));

    parser(input)
}

fn token(input: &str) -> IResult<&str, String> {
    let mut parser = alt((quoted_token, unquoted_token));
    parser(input)
}

fn operation_with_args(input: &str) -> IResult<&str, Vec<String>> {
    let mut parser = separated_list1(many1(one_of(" \t")), token);
    parser(input)
}

fn semicolon(input: &str) -> IResult<&str, &str> {
    delimited(many0(one_of(" \t")), tag(";"), many0(one_of(" \t")))(input)
}

fn operation_sequence(input: &str) -> IResult<&str, Vec<Vec<String>>> {
    let parser = separated_list0(many1(semicolon), operation_with_args);
    let parser = delimited(many0(semicolon), parser, many0(semicolon));
    let parser = preceded(many0(one_of(" \t")), parser);

    let mut parser = complete(parser);

    parser(input)
}

/// Split a semicolon-separated list of operations into a vector. Each operation is represented by
/// a non-empty sub-vector, where the first element is the name of the operation, and the rest of
/// the elements are operation's arguments.
///
/// Tokens can be double-quoted. Such tokens can contain spaces and C-like escaped sequences: `\n`
/// for newline, `\r` for carriage return, `\t` for tab, `\"` for double quote, `\\` for backslash.
/// Unsupported sequences are stripped of the escaping, e.g. `\e` turns into `e`.
///
/// This function assumes that the input string:
/// 1. doesn't contain a comment;
/// 2. doesn't contain backticks that need to be processed.
///
/// Returns `None` if the input could not be parsed.
pub fn tokenize_operation_sequence(input: &str) -> Option<Vec<Vec<String>>> {
    match operation_sequence(input) {
        Ok((_leftovers, tokens)) => Some(tokens),
        Err(_error) => None,
    }
}

#[cfg(test)]
mod tests {
    use super::tokenize_operation_sequence;

    #[test]
    fn t_tokenize_operation_sequence_works_for_all_cpp_inputs() {
        assert_eq!(
            tokenize_operation_sequence("").unwrap(),
            Vec::<Vec<String>>::new()
        );
        assert_eq!(
            tokenize_operation_sequence("open").unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence("open-all-unread-in-browser-and-mark-read").unwrap(),
            vec![vec!["open-all-unread-in-browser-and-mark-read"]]
        );
        assert_eq!(
            tokenize_operation_sequence("; ; ; ;").unwrap(),
            Vec::<Vec<String>>::new()
        );
        assert_eq!(
            tokenize_operation_sequence("open ; next").unwrap(),
            vec![vec!["open"], vec!["next"]]
        );
        assert_eq!(
            tokenize_operation_sequence("open ; next ; prev").unwrap(),
            vec![vec!["open"], vec!["next"], vec!["prev"]]
        );
        assert_eq!(
            tokenize_operation_sequence("open ; next ; prev ; quit").unwrap(),
            vec![vec!["open"], vec!["next"], vec!["prev"], vec!["quit"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"set "arg 1""#).unwrap(),
            vec![vec!["set", "arg 1"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"set "arg 1" ; set "arg 2" "arg 3""#).unwrap(),
            vec![vec!["set", "arg 1"], vec!["set", "arg 2", "arg 3"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"set browser "firefox"; open-in-browser"#).unwrap(),
            vec![vec!["set", "browser", "firefox"], vec!["open-in-browser"]]
        );
        assert_eq!(
            tokenize_operation_sequence("set browser firefox; open-in-browser").unwrap(),
            vec![vec!["set", "browser", "firefox"], vec!["open-in-browser"]]
        );
        assert_eq!(
            tokenize_operation_sequence("open-in-browser; quit").unwrap(),
            vec![vec!["open-in-browser"], vec!["quit"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"open; set browser "firefox --private-window"; quit"#)
                .unwrap(),
            vec![
                vec!["open"],
                vec!["set", "browser", "firefox --private-window"],
                vec!["quit"]
            ]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"open ;set browser "firefox --private-window" ;quit"#)
                .unwrap(),
            vec![
                vec!["open"],
                vec!["set", "browser", "firefox --private-window"],
                vec!["quit"]
            ]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"open;set browser "firefox --private-window";quit"#)
                .unwrap(),
            vec![
                vec!["open"],
                vec!["set", "browser", "firefox --private-window"],
                vec!["quit"]
            ]
        );
        assert_eq!(
            tokenize_operation_sequence("; ;; ; open",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence(";;; ;; ; open",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence(";;; ;; ; open ;",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence(";;; ;; ; open ;; ;",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence(";;; ;; ; open ; ;;;;",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence(";;; open ; ;;;;",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence("; open ;; ;; ;",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence("open ; ;;; ;;",).unwrap(),
            vec![vec!["open"]]
        );
        assert_eq!(
            tokenize_operation_sequence(
                r#"set browser "sleep 3; do-something ; echo hi"; open-in-browser"#
            )
            .unwrap(),
            vec![
                vec!["set", "browser", "sleep 3; do-something ; echo hi"],
                vec!["open-in-browser"]
            ]
        );
    }

    #[test]
    fn t_tokenize_operation_sequence_ignores_escaped_sequences_outside_double_quotes() {
        assert_eq!(
            tokenize_operation_sequence(r#"\t"#).unwrap(),
            vec![vec![r#"\t"#]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"\r"#).unwrap(),
            vec![vec![r#"\r"#]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"\n"#).unwrap(),
            vec![vec![r#"\n"#]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"\v"#).unwrap(),
            vec![vec![r#"\v"#]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"\""#).unwrap(),
            vec![vec![r#"\""#]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#"\\"#).unwrap(),
            vec![vec![r#"\\"#]]
        );
    }

    #[test]
    fn t_tokenize_operation_sequence_expands_escaped_sequences_inside_double_quotes() {
        assert_eq!(
            tokenize_operation_sequence(r#""\t""#).unwrap(),
            vec![vec!["\t"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\r""#).unwrap(),
            vec![vec!["\r"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\n""#).unwrap(),
            vec![vec!["\n"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\"""#).unwrap(),
            vec![vec!["\""]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\\""#).unwrap(),
            vec![vec!["\\"]]
        );
    }

    #[test]
    fn t_tokenize_operation_sequence_passes_through_unsupported_escaped_chars_inside_double_quotes()
    {
        assert_eq!(
            tokenize_operation_sequence(r#""\1""#).unwrap(),
            vec![vec!["1"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\W""#).unwrap(),
            vec![vec!["W"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\b""#).unwrap(),
            vec![vec!["b"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\d""#).unwrap(),
            vec![vec!["d"]]
        );
        assert_eq!(
            tokenize_operation_sequence(r#""\x""#).unwrap(),
            vec![vec!["x"]]
        );
    }

    #[test]
    fn t_tokenize_operation_sequence_implicitly_closes_double_quotes_at_end_of_input() {
        assert_eq!(
            tokenize_operation_sequence(r#"set "arg 1"#).unwrap(),
            vec![vec!["set", "arg 1"]]
        );
    }

    #[test]
    fn t_tokenize_operation_sequence_allows_single_character_unquoted() {
        assert_eq!(
            tokenize_operation_sequence(r#"set a b"#).unwrap(),
            vec![vec!["set", "a", "b"]]
        );
    }

    #[test]
    fn t_tokenize_operation_sequence_ignores_leading_and_trailing_whitespace() {
        assert_eq!(
            tokenize_operation_sequence(" \t set a b \t   ").unwrap(),
            vec![vec!["set", "a", "b"]]
        );
    }

    #[test]
    fn t_tokenize_operation_sequence_allows_tabs_between_arguments() {
        assert_eq!(
            tokenize_operation_sequence("\tset\ta\tb\t;\topen\t").unwrap(),
            vec![vec!["set", "a", "b"], vec!["open"]]
        );
    }
}
<|endoftext|>


<commit_before>use alloc::boxed::Box;

use arch::context::context_switch;
use arch::memory;

use core::{cmp, mem};

use common::time::{self, Duration};

use drivers::pci::config::PciConfig;
use drivers::io::{Io, Mmio, Pio, PhysAddr};

use fs::{KScheme, Resource, Url};

use syscall;

#[repr(packed)]
struct Bd {
    ptr: PhysAddr<Mmio<u32>>,
    samples: Mmio<u32>,
}

struct Ac97Resource {
    audio: usize,
    bus_master: usize,
    bdl: *mut Bd,
}

impl Resource for Ac97Resource {
    fn dup(&self) -> syscall::Result<Box<Resource>> {
        Ok(box Ac97Resource {
            audio: self.audio,
            bus_master: self.bus_master,
            bdl: self.bdl
        })
    }

    fn path(&self, buf: &mut [u8]) -> syscall::Result <usize> {
        let path = b"audio:";

        let mut i = 0;
        while i < buf.len() && i < path.len() {
            buf[i] = path[i];
            i += 1;
        }

        Ok(i)
    }

    fn write(&mut self, buf: &[u8]) -> syscall::Result<usize> {
        unsafe {
            let audio = self.audio as u16;

            let mut master_volume = Pio::<u16>::new(audio + 2);
            let mut pcm_volume = Pio::<u16>::new(audio + 0x18);

            debugln!("MASTER {:X} PCM {:X}", master_volume.read(), pcm_volume.read());

            master_volume.write(0);
            pcm_volume.write(0);

            debugln!("MASTER {:X} PCM {:X}", master_volume.read(), pcm_volume.read());

            let bus_master = self.bus_master as u16;

            let po_civ = Pio::<u8>::new(bus_master + 0x14);
            let mut po_lvi = Pio::<u8>::new(bus_master + 0x15);
            let mut po_cr = Pio::<u8>::new(bus_master + 0x1B);

            loop {
                if po_cr.read() & 1 == 0 {
                    break;
                }
                context_switch();
            }

            po_cr.write(0);

            for i in 0..32 {
                (*self.bdl.offset(i)).ptr.write(0);
                (*self.bdl.offset(i)).samples.write(0);
            }

            let mut wait = false;
            let mut position = 0;

            let mut lvi = po_lvi.read();

            let start_lvi;
            if lvi == 0 {
                start_lvi = 31;
            } else {
                start_lvi = lvi - 1;
            }

            lvi += 1;
            if lvi >= 32 {
                lvi = 0;
            }
            loop {
                while wait {
                    if po_civ.read() != lvi as u8 {
                        break;
                    }

                    {
                        let contexts = &mut *::env().contexts.get();
                        if let Ok(mut current) = contexts.current_mut() {
                            current.wake = Some(Duration::monotonic() + Duration::new(0, 10 * time::NANOS_PER_MILLI));
                            current.block("AC97 sleep 1");
                        }
                    }

                    context_switch();
                }

                debugln!("AC97 {} / {}: {} / {}",
                       po_civ.read(),
                       lvi as usize,
                       position,
                       buf.len());

                let bytes = cmp::min(65534 * 2, (buf.len() - position + 1));
                let samples = bytes / 2;

                (*self.bdl.offset(lvi as isize)).ptr.write(buf.as_ptr().offset(position as isize) as u32);
                (*self.bdl.offset(lvi as isize)).samples.write((samples & 0xFFFF) as u32);

                position += bytes;

                if position >= buf.len() {
                    break;
                }

                lvi += 1;

                if lvi >= 32 {
                    lvi = 0;
                }

                if lvi == start_lvi {
                    po_lvi.write(start_lvi);
                    po_cr.write(1);
                    wait = true;
                }
            }

            po_lvi.write(lvi);
            po_cr.write(1);

            loop {
                if po_civ.read() == lvi {
                    po_cr.write(0);
                    break;
                }

                {
                    let contexts = &mut *::env().contexts.get();
                    if let Ok(mut current) = contexts.current_mut() {
                        current.wake = Some(Duration::monotonic() + Duration::new(0, 10 * time::NANOS_PER_MILLI));
                        current.block("AC97 sleep 2");
                    }
                }

                context_switch();
            }

            debug!("AC97 Finished {} / {}\n", po_civ.read(), lvi);
        }

        Ok(buf.len())
    }
}

pub struct Ac97 {
    audio: usize,
    bus_master: usize,
    irq: u8,
    bdl: *mut Bd,
}

impl KScheme for Ac97 {
    fn scheme(&self) -> &str {
        "audio"
    }

    fn open(&mut self, _: Url, _: usize) -> syscall::Result<Box<Resource>> {
        Ok(box Ac97Resource {
            audio: self.audio,
            bus_master: self.bus_master,
            bdl: self.bdl
        })
    }

    fn on_irq(&mut self, irq: u8) {
        if irq == self.irq {
            // d("AC97 IRQ\n");
        }
    }
}

impl Ac97 {
    pub unsafe fn new(mut pci: PciConfig) -> Box<Ac97> {
        pci.flag(4, 4, true); // Bus mastering

        let module = box Ac97 {
            audio: pci.read(0x10) as usize & 0xFFFFFFF0,
            bus_master: pci.read(0x14) as usize & 0xFFFFFFF0,
            irq: pci.read(0x3C) as u8 & 0xF,
            bdl: memory::alloc(32 * mem::size_of::<Bd>()) as *mut Bd,
        };

        debug!(" + AC97 on: {:X}, {:X}, IRQ: {:X}\n", module.audio, module.bus_master, module.irq);

        let mut po_bdbar = PhysAddr::new(Pio::<u32>::new(module.bus_master as u16 + 0x10));
        po_bdbar.write(module.bdl as u32);

        module
    }
}
<commit_msg>Find physical address of userspace audio buffer<commit_after>use alloc::boxed::Box;

use arch::context::context_switch;
use arch::memory;

use core::{cmp, mem};

use common::time::{self, Duration};

use drivers::pci::config::PciConfig;
use drivers::io::{Io, Mmio, Pio, PhysAddr};

use fs::{KScheme, Resource, Url};

use syscall;

#[repr(packed)]
struct Bd {
    ptr: PhysAddr<Mmio<u32>>,
    samples: Mmio<u32>,
}

struct Ac97Resource {
    audio: usize,
    bus_master: usize,
    bdl: *mut Bd,
}

impl Resource for Ac97Resource {
    fn dup(&self) -> syscall::Result<Box<Resource>> {
        Ok(box Ac97Resource {
            audio: self.audio,
            bus_master: self.bus_master,
            bdl: self.bdl
        })
    }

    fn path(&self, buf: &mut [u8]) -> syscall::Result <usize> {
        let path = b"audio:";

        let mut i = 0;
        while i < buf.len() && i < path.len() {
            buf[i] = path[i];
            i += 1;
        }

        Ok(i)
    }

    fn write(&mut self, buf: &[u8]) -> syscall::Result<usize> {
        unsafe {
            let audio = self.audio as u16;

            let mut master_volume = Pio::<u16>::new(audio + 2);
            let mut pcm_volume = Pio::<u16>::new(audio + 0x18);

            debugln!("MASTER {:X} PCM {:X}", master_volume.read(), pcm_volume.read());

            master_volume.write(0);
            pcm_volume.write(0x808);

            debugln!("MASTER {:X} PCM {:X}", master_volume.read(), pcm_volume.read());

            let bus_master = self.bus_master as u16;

            let po_civ = Pio::<u8>::new(bus_master + 0x14);
            let mut po_lvi = Pio::<u8>::new(bus_master + 0x15);
            let mut po_cr = Pio::<u8>::new(bus_master + 0x1B);

            loop {
                if po_cr.read() & 1 == 0 {
                    break;
                }
                context_switch();
            }

            po_cr.write(0);

            for i in 0..32 {
                (*self.bdl.offset(i)).ptr.write(0);
                (*self.bdl.offset(i)).samples.write(0);
            }

            let mut wait = false;
            let mut position = 0;

            let mut lvi = po_lvi.read();

            let start_lvi;
            if lvi == 0 {
                start_lvi = 31;
            } else {
                start_lvi = lvi - 1;
            }

            lvi += 1;
            if lvi >= 32 {
                lvi = 0;
            }
            loop {
                while wait {
                    if po_civ.read() != lvi as u8 {
                        break;
                    }

                    {
                        let contexts = &mut *::env().contexts.get();
                        if let Ok(mut current) = contexts.current_mut() {
                            current.wake = Some(Duration::monotonic() + Duration::new(0, 10 * time::NANOS_PER_MILLI));
                            current.block("AC97 sleep 1");
                        }
                    }

                    context_switch();
                }

                debugln!("AC97 {} / {}: {} / {}",
                       po_civ.read(),
                       lvi as usize,
                       position,
                       buf.len());

                let bytes = cmp::min(65534 * 2, (buf.len() - position + 1));
                let samples = bytes / 2;

                let mut phys_buf = buf.as_ptr() as usize;
                {
                    let contexts = &mut *::env().contexts.get();
                    if let Ok(current) = contexts.current() {
                        if let Ok(phys) = current.translate(buf.as_ptr().offset(position as isize) as usize, bytes) {
                            debugln!("logical {:#X} -> physical {:#X}", &(buf.as_ptr() as usize), &phys);
                            phys_buf = phys;
                        }
                    }
                }

                (*self.bdl.offset(lvi as isize)).ptr.write(phys_buf as u32);
                (*self.bdl.offset(lvi as isize)).samples.write((samples & 0xFFFF) as u32);

                position += bytes;

                if position >= buf.len() {
                    break;
                }

                lvi += 1;

                if lvi >= 32 {
                    lvi = 0;
                }

                if lvi == start_lvi {
                    po_lvi.write(start_lvi);
                    po_cr.write(1);
                    wait = true;
                }
            }

            po_lvi.write(lvi);
            po_cr.write(1);

            loop {
                if po_civ.read() == lvi {
                    po_cr.write(0);
                    break;
                }

                {
                    let contexts = &mut *::env().contexts.get();
                    if let Ok(mut current) = contexts.current_mut() {
                        current.wake = Some(Duration::monotonic() + Duration::new(0, 10 * time::NANOS_PER_MILLI));
                        current.block("AC97 sleep 2");
                    }
                }

                context_switch();
            }

            debug!("AC97 Finished {} / {}\n", po_civ.read(), lvi);
        }

        Ok(buf.len())
    }
}

pub struct Ac97 {
    audio: usize,
    bus_master: usize,
    irq: u8,
    bdl: *mut Bd,
}

impl KScheme for Ac97 {
    fn scheme(&self) -> &str {
        "audio"
    }

    fn open(&mut self, _: Url, _: usize) -> syscall::Result<Box<Resource>> {
        Ok(box Ac97Resource {
            audio: self.audio,
            bus_master: self.bus_master,
            bdl: self.bdl
        })
    }

    fn on_irq(&mut self, irq: u8) {
        if irq == self.irq {
            // d("AC97 IRQ\n");
        }
    }
}

impl Ac97 {
    pub unsafe fn new(mut pci: PciConfig) -> Box<Ac97> {
        pci.flag(4, 4, true); // Bus mastering

        let module = box Ac97 {
            audio: pci.read(0x10) as usize & 0xFFFFFFF0,
            bus_master: pci.read(0x14) as usize & 0xFFFFFFF0,
            irq: pci.read(0x3C) as u8 & 0xF,
            bdl: memory::alloc(32 * mem::size_of::<Bd>()) as *mut Bd,
        };

        debug!(" + AC97 on: {:X}, {:X}, IRQ: {:X}\n", module.audio, module.bus_master, module.irq);

        let mut po_bdbar = PhysAddr::new(Pio::<u32>::new(module.bus_master as u16 + 0x10));
        po_bdbar.write(module.bdl as u32);

        module
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate log4rs;
extern crate iron;
extern crate logger;
extern crate router;
extern crate persistent;
extern crate r2d2;
extern crate r2d2_postgres;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate potboiler_common;
extern crate serde_json;
extern crate hyper;
extern crate hybrid_clocks;
mod tables;

use iron::prelude::*;
use iron::status;
use logger::Logger;
use persistent::Read as PRead;
use persistent::State;
use potboiler_common::{db, iron_str_error, server_id};
use potboiler_common::string_error::StringError;
use potboiler_common::types::{CRDT, LWW, Log};
use r2d2_postgres::PostgresConnectionManager;
use router::Router;
use std::env;
use std::io::Read;
use std::ops::Deref;

pub type PostgresConnection = r2d2::PooledConnection<PostgresConnectionManager>;

lazy_static! {
    static ref SERVER_URL: String = env::var("SERVER_URL").expect("Needed SERVER_URL");
}

include!(concat!(env!("OUT_DIR"), "/serde_types.rs"));

fn get_key(_: &mut Request) -> IronResult<Response> {
    Ok(Response::with((status::Ok, "get_key")))
}

fn update_key(req: &mut Request) -> IronResult<Response> {
    let body_string = {
        let mut body = String::new();
        req.body.read_to_string(&mut body).expect("could read from body");
        body
    };
    let mut json: serde_json::Value = match serde_json::de::from_str(&body_string) {
        Ok(val) => val,
        Err(err) => return Err(IronError::new(err, (status::BadRequest, "Bad JSON"))),
    };
    let map = json.as_object_mut().unwrap();
    map.insert("table".to_string(),
               serde_json::to_value(&potboiler_common::get_req_key(req, "table").unwrap()));
    map.insert("key".to_string(),
               serde_json::to_value(&potboiler_common::get_req_key(req, "key").unwrap()));

    let client = hyper::client::Client::new();
    let res = client.post(SERVER_URL.deref())
        .body(&serde_json::ser::to_string(map).unwrap())
        .send()
        .expect("sender ok");
    assert_eq!(res.status, hyper::status::StatusCode::Created);
    Ok(Response::with((status::Ok, "update_key")))
}

fn make_table(conn: &PostgresConnection, table_name: &str, kind: &CRDT) -> IronResult<()> {
    match kind {
        &CRDT::LWW => {
            conn.execute(&format!("CREATE TABLE IF NOT EXISTS {} (key VARCHAR(1024) PRIMARY KEY, value \
                                   JSONB, crdt JSONB)",
                                  &table_name),
                         &[])
                .map_err(iron_str_error)?;
        }
        &CRDT::ORSET => {
            conn.execute(&format!("CREATE TABLE IF NOT EXISTS {} (key VARCHAR(1024) PRIMARY KEY, crdt \
                                   JSONB)",
                                  &table_name),
                         &[])
                .map_err(iron_str_error)?;
            conn.execute(&format!("CREATE TABLE IF NOT EXISTS {}_items (\
                                   collection VARCHAR(1024),
                                   key VARCHAR(1024), \
                                   item VARCHAR(1024), PRIMARY KEY(collection, key, item))",
                                  &table_name),
                         &[])
                .map_err(iron_str_error)?;
        }
        &CRDT::GSET => {
            error!("No G-Set make table yet");
        }
    }
    return Ok(());
}

fn string_iron_error(error: &str) -> IronResult<Response> {
    Err(IronError::new(StringError(error.to_string()), (status::BadRequest, error)))
}

fn get_crdt(conn: &PostgresConnection,
            table: &String,
            key: &String)
            -> IronResult<Option<serde_json::Value>> {
    let stmt = conn.prepare(&format!("select crdt from {} where key=$1", table)).map_err(iron_str_error)?;
    let results = stmt.query(&[&key]).map_err(iron_str_error)?;
    if results.is_empty() {
        Ok(None)
    } else if results.len() == 1 {
        let row = results.get(0);
        let raw_crdt: serde_json::Value = row.get("crdt");
        Ok(Some(raw_crdt))
    } else {
        Err(iron_str_error(StringError::from(format!("{} entries for key {}", results.len(), key))))
    }
}

fn new_event(req: &mut Request) -> IronResult<Response> {
    let body_string = {
        let mut body = String::new();
        req.body.read_to_string(&mut body).expect("could read from body");
        body
    };
    let json: serde_json::Value = match serde_json::de::from_str(&body_string) {
        Ok(val) => val,
        Err(err) => return Err(IronError::new(err, (status::BadRequest, "Bad JSON"))),
    };
    info!("body: {:?}", json);
    let log = try!(serde_json::from_value::<Log>(json).map_err(iron_str_error));
    info!("log: {:?}", log);
    let change: Change = serde_json::from_value(log.data).unwrap();
    info!("change: {:?}", change);
    let tables = tables::get_tables(req);
    let table_type = match tables.get(&change.table) {
        None => return string_iron_error("Can't find table"),
        Some(&val) => val,
    };
    match table_type {
        CRDT::LWW => {
            match change.op {
                Operation::Set => {
                    let crdt_to_use: Option<CRDT> = if &change.table == tables::CONFIG_TABLE {
                        let crdt_type: &str = change.change
                            .find("crdt")
                            .ok_or(StringError::from("No CRDT key"))
                            .map_err(iron_str_error)?
                            .as_str()
                            .ok_or(StringError::from("CRDT value isn't string!"))
                            .map_err(iron_str_error)?;
                        // FIXME: format! bit is a hacky workaround for
                        // https://github.com/serde-rs/serde/issues/251
                        serde_json::from_str(&format!("\"{}\"", crdt_type)).map_err(iron_str_error)?
                    } else {
                        None
                    };
                    let conn = get_pg_connection!(&req);
                    let raw_crdt = get_crdt(&conn, &change.table, &change.key)?;
                    match raw_crdt {
                        None => {
                            let lww = LWW {
                                when: log.when,
                                data: change.change.clone(),
                            };
                            conn.execute(&format!("INSERT INTO {} (key, value, crdt) VALUES ($1, $2, $3)",
                                                  &change.table),
                                         &[&change.key, &change.change, &serde_json::to_value(&lww)])
                                .expect("insert worked");
                            if &change.table == tables::CONFIG_TABLE {
                                let crdt = crdt_to_use.unwrap();
                                make_table(&conn, &change.key, &crdt)?;
                                tables::add_table(req, &change.key, &crdt);
                            }
                        }
                        Some(raw_crdt) => {
                            let mut lww: LWW = serde_json::from_value(raw_crdt).expect("bad raw crdt");
                            if lww.when < log.when {
                                lww.when = log.when;
                                lww.data = change.change.clone();
                                conn.execute(&format!("UPDATE {} set value=$2, crdt=$3 where key=$1",
                                                      &change.table),
                                             &[&change.key, &change.change, &serde_json::to_value(&lww)])
                                    .expect("update worked");
                            } else {
                                info!("Earlier event, skipping");
                            }
                        }
                    }
                }
                _ => {
                    return string_iron_error("LWW only supports Set, not Add/Remove");
                }
            }
        }
        CRDT::ORSET => {
            let op: ORSetOp = serde_json::from_value(change.change).map_err(iron_str_error)?;
            let conn = get_pg_connection!(&req);
            let raw_crdt = get_crdt(&conn, &change.table, &change.key)?;
            let (mut crdt, existing) = match raw_crdt {
                Some(val) => (serde_json::from_value(val).map_err(iron_str_error)?, true),
                None => {
                    (ORSet {
                         adds: HashMap::new(),
                         removes: HashMap::new(),
                     },
                     false)
                }
            };
            let trans = conn.transaction().unwrap();
            match change.op {
                Operation::Add => {
                    if !crdt.removes.contains_key(&op.key) && !crdt.adds.contains_key(&op.key) {
                        trans.execute(&format!("INSERT INTO {}_items (collection, key, item) VALUES ($1, \
                                               $2, $3)",
                                              &change.table),
                                     &[&change.key, &op.key, &op.item])
                            .map_err(iron_str_error)?;
                        crdt.adds.insert(op.key, op.item);
                    }
                }
                Operation::Remove => {
                    trans.execute(&format!("DELETE FROM {}_items where collection=$1 and key=$2",
                                          &change.table),
                                 &[&change.key, &op.key])
                        .map_err(iron_str_error)?;
                    crdt.adds.remove(&op.key);
                    crdt.removes.insert(op.key, op.item);
                }
                _ => {
                    return string_iron_error("ORSET only supports Add/Remove");
                }
            }
            debug!("OR-Set for {}: {:?}", &change.table, &crdt);
            if existing {
                trans.execute(&format!("UPDATE {} set crdt=$2 where key=$1", &change.table),
                             &[&change.key, &serde_json::to_value(&crdt)])
                    .map_err(iron_str_error)?;
            } else {
                trans.execute(&format!("INSERT INTO {} (key, crdt) VALUES ($1, $2)", &change.table),
                             &[&change.key, &serde_json::to_value(&crdt)])
                    .map_err(iron_str_error)?;
            }
            let mut items: Vec<&str> = Vec::new();
            for key in crdt.adds.keys() {
                if crdt.removes.contains_key(key) {
                    continue;
                }
                items.push(crdt.adds.get(key).unwrap());
            }
            debug!("Items: {:?}", items);
            trans.commit().map_err(iron_str_error)?;
        }
        _ => {
            return string_iron_error("Only support LWW and OR-Set so far");
        }
    }
    Ok(Response::with(status::NoContent))
}

fn main() {
    log4rs::init_file("log.yaml", Default::default()).expect("log config ok");
    let client = hyper::client::Client::new();

    let mut map: serde_json::Map<String, String> = serde_json::Map::new();
    let host: &str = &env::var("HOST").unwrap_or("localhost".to_string());
    map.insert("url".to_string(),
               format!("http://{}:8001/kv/event", host).to_string());
    let res = client.post(&format!("{}/register", SERVER_URL.deref()))
        .body(&serde_json::ser::to_string(&map).unwrap())
        .send()
        .expect("Register ok");
    assert_eq!(res.status, hyper::status::StatusCode::NoContent);

    let db_url: &str = &env::var("DATABASE_URL").expect("Needed DATABASE_URL");
    let pool = db::get_pool(db_url);
    let conn = pool.get().unwrap();
    match make_table(&conn, "_config", &CRDT::LWW) {
        Ok(_) => {}
        Err(err) => {
            error!("Error while making config table: {}", err);
            return;
        }
    }
    let (logger_before, logger_after) = Logger::new(None);
    let mut router = Router::new();
    router.get("/kv/:table/:key", get_key);
    router.post("/kv/:table/:key", update_key);
    router.post("/kv/event", new_event);
    let mut chain = Chain::new(router);
    chain.link_before(logger_before);
    chain.link_after(logger_after);
    chain.link_before(PRead::<server_id::ServerId>::one(server_id::setup()));
    chain.link(PRead::<db::PostgresDB>::both(pool));
    let tables = tables::init_tables(&conn);
    chain.link(State::<tables::Tables>::both(tables));
    info!("Potboiler-kv booted");
    Iron::new(chain).http("0.0.0.0:8001").unwrap();
}
<commit_msg>Add key list to KV root<commit_after>#[macro_use]
extern crate log;
extern crate log4rs;
extern crate iron;
extern crate logger;
extern crate router;
extern crate persistent;
extern crate r2d2;
extern crate r2d2_postgres;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate potboiler_common;
extern crate serde_json;
extern crate hyper;
extern crate hybrid_clocks;
mod tables;

use iron::prelude::*;
use iron::status;
use logger::Logger;
use persistent::Read as PRead;
use persistent::State;
use potboiler_common::{db, iron_str_error, server_id};
use potboiler_common::string_error::StringError;
use potboiler_common::types::{CRDT, LWW, Log};
use r2d2_postgres::PostgresConnectionManager;
use router::Router;
use std::env;
use std::io::Read;
use std::ops::Deref;

pub type PostgresConnection = r2d2::PooledConnection<PostgresConnectionManager>;

lazy_static! {
    static ref SERVER_URL: String = env::var("SERVER_URL").expect("Needed SERVER_URL");
}

include!(concat!(env!("OUT_DIR"), "/serde_types.rs"));

fn get_key(_: &mut Request) -> IronResult<Response> {
    Ok(Response::with((status::Ok, "get_key")))
}

fn update_key(req: &mut Request) -> IronResult<Response> {
    let body_string = {
        let mut body = String::new();
        req.body.read_to_string(&mut body).expect("could read from body");
        body
    };
    let mut json: serde_json::Value = match serde_json::de::from_str(&body_string) {
        Ok(val) => val,
        Err(err) => return Err(IronError::new(err, (status::BadRequest, "Bad JSON"))),
    };
    let map = json.as_object_mut().unwrap();
    map.insert("table".to_string(),
               serde_json::to_value(&potboiler_common::get_req_key(req, "table").unwrap()));
    map.insert("key".to_string(),
               serde_json::to_value(&potboiler_common::get_req_key(req, "key").unwrap()));

    let client = hyper::client::Client::new();
    let res = client.post(SERVER_URL.deref())
        .body(&serde_json::ser::to_string(map).unwrap())
        .send()
        .expect("sender ok");
    assert_eq!(res.status, hyper::status::StatusCode::Created);
    Ok(Response::with((status::Ok, "update_key")))
}

fn make_table(conn: &PostgresConnection, table_name: &str, kind: &CRDT) -> IronResult<()> {
    match kind {
        &CRDT::LWW => {
            conn.execute(&format!("CREATE TABLE IF NOT EXISTS {} (key VARCHAR(1024) PRIMARY KEY, value \
                                   JSONB, crdt JSONB)",
                                  &table_name),
                         &[])
                .map_err(iron_str_error)?;
        }
        &CRDT::ORSET => {
            conn.execute(&format!("CREATE TABLE IF NOT EXISTS {} (key VARCHAR(1024) PRIMARY KEY, crdt \
                                   JSONB)",
                                  &table_name),
                         &[])
                .map_err(iron_str_error)?;
            conn.execute(&format!("CREATE TABLE IF NOT EXISTS {}_items (\
                                   collection VARCHAR(1024),
                                   key VARCHAR(1024), \
                                   item VARCHAR(1024), PRIMARY KEY(collection, key, item))",
                                  &table_name),
                         &[])
                .map_err(iron_str_error)?;
        }
        &CRDT::GSET => {
            error!("No G-Set make table yet");
        }
    }
    return Ok(());
}

fn string_iron_error(error: &str) -> IronResult<Response> {
    Err(IronError::new(StringError(error.to_string()), (status::BadRequest, error)))
}

fn get_crdt(conn: &PostgresConnection,
            table: &String,
            key: &String)
            -> IronResult<Option<serde_json::Value>> {
    let stmt = conn.prepare(&format!("select crdt from {} where key=$1", table)).map_err(iron_str_error)?;
    let results = stmt.query(&[&key]).map_err(iron_str_error)?;
    if results.is_empty() {
        Ok(None)
    } else if results.len() == 1 {
        let row = results.get(0);
        let raw_crdt: serde_json::Value = row.get("crdt");
        Ok(Some(raw_crdt))
    } else {
        Err(iron_str_error(StringError::from(format!("{} entries for key {}", results.len(), key))))
    }
}

fn new_event(req: &mut Request) -> IronResult<Response> {
    let body_string = {
        let mut body = String::new();
        req.body.read_to_string(&mut body).expect("could read from body");
        body
    };
    let json: serde_json::Value = match serde_json::de::from_str(&body_string) {
        Ok(val) => val,
        Err(err) => return Err(IronError::new(err, (status::BadRequest, "Bad JSON"))),
    };
    info!("body: {:?}", json);
    let log = try!(serde_json::from_value::<Log>(json).map_err(iron_str_error));
    info!("log: {:?}", log);
    let change: Change = serde_json::from_value(log.data).unwrap();
    info!("change: {:?}", change);
    let tables = tables::get_tables(req);
    let table_type = match tables.get(&change.table) {
        None => return string_iron_error("Can't find table"),
        Some(&val) => val,
    };
    match table_type {
        CRDT::LWW => {
            match change.op {
                Operation::Set => {
                    let crdt_to_use: Option<CRDT> = if &change.table == tables::CONFIG_TABLE {
                        let crdt_type: &str = change.change
                            .find("crdt")
                            .ok_or(StringError::from("No CRDT key"))
                            .map_err(iron_str_error)?
                            .as_str()
                            .ok_or(StringError::from("CRDT value isn't string!"))
                            .map_err(iron_str_error)?;
                        // FIXME: format! bit is a hacky workaround for
                        // https://github.com/serde-rs/serde/issues/251
                        serde_json::from_str(&format!("\"{}\"", crdt_type)).map_err(iron_str_error)?
                    } else {
                        None
                    };
                    let conn = get_pg_connection!(&req);
                    let raw_crdt = get_crdt(&conn, &change.table, &change.key)?;
                    match raw_crdt {
                        None => {
                            let lww = LWW {
                                when: log.when,
                                data: change.change.clone(),
                            };
                            conn.execute(&format!("INSERT INTO {} (key, value, crdt) VALUES ($1, $2, $3)",
                                                  &change.table),
                                         &[&change.key, &change.change, &serde_json::to_value(&lww)])
                                .expect("insert worked");
                            if &change.table == tables::CONFIG_TABLE {
                                let crdt = crdt_to_use.unwrap();
                                make_table(&conn, &change.key, &crdt)?;
                                tables::add_table(req, &change.key, &crdt);
                            }
                        }
                        Some(raw_crdt) => {
                            let mut lww: LWW = serde_json::from_value(raw_crdt).expect("bad raw crdt");
                            if lww.when < log.when {
                                lww.when = log.when;
                                lww.data = change.change.clone();
                                conn.execute(&format!("UPDATE {} set value=$2, crdt=$3 where key=$1",
                                                      &change.table),
                                             &[&change.key, &change.change, &serde_json::to_value(&lww)])
                                    .expect("update worked");
                            } else {
                                info!("Earlier event, skipping");
                            }
                        }
                    }
                }
                _ => {
                    return string_iron_error("LWW only supports Set, not Add/Remove");
                }
            }
        }
        CRDT::ORSET => {
            let op: ORSetOp = serde_json::from_value(change.change).map_err(iron_str_error)?;
            let conn = get_pg_connection!(&req);
            let raw_crdt = get_crdt(&conn, &change.table, &change.key)?;
            let (mut crdt, existing) = match raw_crdt {
                Some(val) => (serde_json::from_value(val).map_err(iron_str_error)?, true),
                None => {
                    (ORSet {
                         adds: HashMap::new(),
                         removes: HashMap::new(),
                     },
                     false)
                }
            };
            let trans = conn.transaction().unwrap();
            match change.op {
                Operation::Add => {
                    if !crdt.removes.contains_key(&op.key) && !crdt.adds.contains_key(&op.key) {
                        trans.execute(&format!("INSERT INTO {}_items (collection, key, item) VALUES ($1, \
                                               $2, $3)",
                                              &change.table),
                                     &[&change.key, &op.key, &op.item])
                            .map_err(iron_str_error)?;
                        crdt.adds.insert(op.key, op.item);
                    }
                }
                Operation::Remove => {
                    trans.execute(&format!("DELETE FROM {}_items where collection=$1 and key=$2",
                                          &change.table),
                                 &[&change.key, &op.key])
                        .map_err(iron_str_error)?;
                    crdt.adds.remove(&op.key);
                    crdt.removes.insert(op.key, op.item);
                }
                _ => {
                    return string_iron_error("ORSET only supports Add/Remove");
                }
            }
            debug!("OR-Set for {}: {:?}", &change.table, &crdt);
            if existing {
                trans.execute(&format!("UPDATE {} set crdt=$2 where key=$1", &change.table),
                             &[&change.key, &serde_json::to_value(&crdt)])
                    .map_err(iron_str_error)?;
            } else {
                trans.execute(&format!("INSERT INTO {} (key, crdt) VALUES ($1, $2)", &change.table),
                             &[&change.key, &serde_json::to_value(&crdt)])
                    .map_err(iron_str_error)?;
            }
            let mut items: Vec<&str> = Vec::new();
            for key in crdt.adds.keys() {
                if crdt.removes.contains_key(key) {
                    continue;
                }
                items.push(crdt.adds.get(key).unwrap());
            }
            debug!("Items: {:?}", items);
            trans.commit().map_err(iron_str_error)?;
        }
        _ => {
            return string_iron_error("Only support LWW and OR-Set so far");
        }
    }
    Ok(Response::with(status::NoContent))
}

fn list_tables(req: &mut Request) -> IronResult<Response> {
    let tables = tables::get_tables(req);
    let mut table_names = vec![];
    for t in tables.keys() {
        table_names.push(t);
    }
    Ok(Response::with((status::Ok, serde_json::to_string(&table_names).unwrap())))
}

fn main() {
    log4rs::init_file("log.yaml", Default::default()).expect("log config ok");
    let client = hyper::client::Client::new();

    let mut map: serde_json::Map<String, String> = serde_json::Map::new();
    let host: &str = &env::var("HOST").unwrap_or("localhost".to_string());
    map.insert("url".to_string(),
               format!("http://{}:8001/kv/event", host).to_string());
    let res = client.post(&format!("{}/register", SERVER_URL.deref()))
        .body(&serde_json::ser::to_string(&map).unwrap())
        .send()
        .expect("Register ok");
    assert_eq!(res.status, hyper::status::StatusCode::NoContent);

    let db_url: &str = &env::var("DATABASE_URL").expect("Needed DATABASE_URL");
    let pool = db::get_pool(db_url);
    let conn = pool.get().unwrap();
    match make_table(&conn, tables::CONFIG_TABLE, &CRDT::LWW) {
        Ok(_) => {}
        Err(err) => {
            error!("Error while making config table: {}", err);
            return;
        }
    }
    let (logger_before, logger_after) = Logger::new(None);
    let mut router = Router::new();
    router.get("/kv", list_tables);
    router.get("/kv/:table/:key", get_key);
    router.post("/kv/:table/:key", update_key);
    router.post("/kv/event", new_event);
    let mut chain = Chain::new(router);
    chain.link_before(logger_before);
    chain.link_after(logger_after);
    chain.link_before(PRead::<server_id::ServerId>::one(server_id::setup()));
    chain.link(PRead::<db::PostgresDB>::both(pool));
    let tables = tables::init_tables(&conn);
    chain.link(State::<tables::Tables>::both(tables));
    info!("Potboiler-kv booted");
    Iron::new(chain).http("0.0.0.0:8001").unwrap();
}
<|endoftext|>


<commit_before>use backend::Backend;
use provider::error;
use provider::Provider;
use provider::Output;
use libc::{c_char, uint32_t};
use std::ffi::CString;
use std::ffi::CStr;

pub struct File<'a> {
    name: &'static str,
    backend: &'a Backend,
    provider: &'a Provider,
}

impl<'a> File<'a> {
    pub fn new(n: &'static str, b: &'a Backend, p: &'a Provider) -> File<'a> {
        File {
            name: n,
            backend: b,
            provider: p,
        }
    }

    pub fn mode(&self) -> Result<u32, error::Error> {
        self.backend
            .handle(self.provider.file.mode(self.name))
            .and_then(Output::to_u32)
    }

    pub fn size(&self) -> Result<i64, error::Error> {
        self.backend
            .handle(self.provider.file.size(self.name))
            .and_then(Output::to_i64)
    }

    pub fn is_file(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_file(self.name))
            .and_then(Output::to_bool)
    }

    pub fn exist(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.exist(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_directory(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_directory(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_block_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_block_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_character_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_character_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_pipe(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_pipe(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_socket(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_socket(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_symlink(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_symlink(self.name))
            .and_then(Output::to_bool)
    }

    pub fn contents(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.contents(self.name))
            .and_then(Output::to_string)
    }

    pub fn owner(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.owner(self.name))
            .and_then(Output::to_string)
    }

    pub fn group(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.group(self.name))
            .and_then(Output::to_string)
    }

    pub fn linked_to(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.linked_to(self.name))
            .and_then(Output::to_string)
    }

    pub fn is_readable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_owner(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_group(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_group(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_others(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_others(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_user(&self, user: &'static str) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_user(self.name, user))
            .and_then(Output::to_bool)
    }

    pub fn is_writable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_owner(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_group(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_group(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_others(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_others(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_user(&self, user: &'static str) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_user(self.name, user))
            .and_then(Output::to_bool)
    }

    pub fn md5sum(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.md5sum(self.name))
            .and_then(Output::to_string)
    }

    pub fn sha256sum(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.sha256sum(self.name))
            .and_then(Output::to_string)
    }
}

// Wrapper functions for FFI

#[no_mangle]
pub extern "C" fn resource_file_free(ptr: *mut File) {
    if ptr.is_null() {
        return;
    }
    unsafe {
        Box::from_raw(ptr);
    }
}

#[no_mangle]
pub extern "C" fn resource_file_mode(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    f.mode().unwrap()
}

#[no_mangle]
pub extern "C" fn resource_file_is_file(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_file().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_exist(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.exist().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_directory(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_directory().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_block_device(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_block_device().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_character_device(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_character_device().unwrap() {
        1
    } else {
        0
    }
}

#[no_mangle]
pub extern "C" fn resource_file_is_pipe(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_pipe().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_socket(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_socket().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_symlink(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_symlink().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_contents(ptr: *const File) -> *mut c_char {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };
    let c = f.contents().unwrap();
    CString::new(c).unwrap().into_raw()
}

#[no_mangle]
pub extern "C" fn resource_file_owner(ptr: *const File) -> *mut c_char {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };
    let c = f.owner().unwrap();
    CString::new(c).unwrap().into_raw()
}

#[no_mangle]
pub extern "C" fn resource_file_group(ptr: *const File) -> *mut c_char {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };
    let c = f.group().unwrap();
    CString::new(c).unwrap().into_raw()
}

#[no_mangle]
pub extern "C" fn resource_file_is_readable(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_readable().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_readable_by_owner(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_readable_by_owner().unwrap() {
        1
    } else {
        0
    }
}

#[no_mangle]
pub extern "C" fn resource_file_is_readable_by_group(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_readable_by_group().unwrap() {
        1
    } else {
        0
    }
}

#[no_mangle]
pub extern "C" fn resource_file_is_readable_by_others(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_readable_by_others().unwrap() {
        1
    } else {
        0
    }
}

#[no_mangle]
pub extern "C" fn resource_file_is_readable_by_user(ptr: *const File,
                                                    u: *const c_char)
                                                    -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    let c_str = unsafe {
        assert!(!u.is_null());
        CStr::from_ptr(u)
    };

    let user = c_str.to_str().unwrap();
    if f.is_readable_by_user(user).unwrap() {
        1
    } else {
        0
    }
}

#[no_mangle]
pub extern "C" fn resource_file_is_writable(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_writable().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_writable_by_owner(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_writable_by_owner().unwrap() {
        1
    } else {
        0
    }
}
<commit_msg>Add wrapper function for file.is_writable_by_group()<commit_after>use backend::Backend;
use provider::error;
use provider::Provider;
use provider::Output;
use libc::{c_char, uint32_t};
use std::ffi::CString;
use std::ffi::CStr;

pub struct File<'a> {
    name: &'static str,
    backend: &'a Backend,
    provider: &'a Provider,
}

impl<'a> File<'a> {
    pub fn new(n: &'static str, b: &'a Backend, p: &'a Provider) -> File<'a> {
        File {
            name: n,
            backend: b,
            provider: p,
        }
    }

    pub fn mode(&self) -> Result<u32, error::Error> {
        self.backend
            .handle(self.provider.file.mode(self.name))
            .and_then(Output::to_u32)
    }

    pub fn size(&self) -> Result<i64, error::Error> {
        self.backend
            .handle(self.provider.file.size(self.name))
            .and_then(Output::to_i64)
    }

    pub fn is_file(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_file(self.name))
            .and_then(Output::to_bool)
    }

    pub fn exist(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.exist(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_directory(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_directory(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_block_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_block_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_character_device(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_character_device(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_pipe(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_pipe(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_socket(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_socket(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_symlink(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_symlink(self.name))
            .and_then(Output::to_bool)
    }

    pub fn contents(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.contents(self.name))
            .and_then(Output::to_string)
    }

    pub fn owner(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.owner(self.name))
            .and_then(Output::to_string)
    }

    pub fn group(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.group(self.name))
            .and_then(Output::to_string)
    }

    pub fn linked_to(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.linked_to(self.name))
            .and_then(Output::to_string)
    }

    pub fn is_readable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_owner(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_group(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_group(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_others(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_others(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_readable_by_user(&self, user: &'static str) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_readable_by_user(self.name, user))
            .and_then(Output::to_bool)
    }

    pub fn is_writable(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_owner(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_owner(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_group(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_group(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_others(&self) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_others(self.name))
            .and_then(Output::to_bool)
    }

    pub fn is_writable_by_user(&self, user: &'static str) -> Result<bool, error::Error> {
        self.backend
            .handle(self.provider.file.is_writable_by_user(self.name, user))
            .and_then(Output::to_bool)
    }

    pub fn md5sum(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.md5sum(self.name))
            .and_then(Output::to_string)
    }

    pub fn sha256sum(&self) -> Result<String, error::Error> {
        self.backend
            .handle(self.provider.file.sha256sum(self.name))
            .and_then(Output::to_string)
    }
}

// Wrapper functions for FFI

#[no_mangle]
pub extern "C" fn resource_file_free(ptr: *mut File) {
    if ptr.is_null() {
        return;
    }
    unsafe {
        Box::from_raw(ptr);
    }
}

#[no_mangle]
pub extern "C" fn resource_file_mode(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    f.mode().unwrap()
}

#[no_mangle]
pub extern "C" fn resource_file_is_file(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_file().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_exist(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.exist().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_directory(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_directory().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_block_device(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_block_device().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_character_device(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_character_device().unwrap() {
        1
    } else {
        0
    }
}

#[no_mangle]
pub extern "C" fn resource_file_is_pipe(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_pipe().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_socket(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_socket().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_symlink(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_symlink().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_contents(ptr: *const File) -> *mut c_char {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };
    let c = f.contents().unwrap();
    CString::new(c).unwrap().into_raw()
}

#[no_mangle]
pub extern "C" fn resource_file_owner(ptr: *const File) -> *mut c_char {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };
    let c = f.owner().unwrap();
    CString::new(c).unwrap().into_raw()
}

#[no_mangle]
pub extern "C" fn resource_file_group(ptr: *const File) -> *mut c_char {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };
    let c = f.group().unwrap();
    CString::new(c).unwrap().into_raw()
}

#[no_mangle]
pub extern "C" fn resource_file_is_readable(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_readable().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_readable_by_owner(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_readable_by_owner().unwrap() {
        1
    } else {
        0
    }
}

#[no_mangle]
pub extern "C" fn resource_file_is_readable_by_group(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_readable_by_group().unwrap() {
        1
    } else {
        0
    }
}

#[no_mangle]
pub extern "C" fn resource_file_is_readable_by_others(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_readable_by_others().unwrap() {
        1
    } else {
        0
    }
}

#[no_mangle]
pub extern "C" fn resource_file_is_readable_by_user(ptr: *const File,
                                                    u: *const c_char)
                                                    -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    let c_str = unsafe {
        assert!(!u.is_null());
        CStr::from_ptr(u)
    };

    let user = c_str.to_str().unwrap();
    if f.is_readable_by_user(user).unwrap() {
        1
    } else {
        0
    }
}

#[no_mangle]
pub extern "C" fn resource_file_is_writable(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_writable().unwrap() { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn resource_file_is_writable_by_owner(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_writable_by_owner().unwrap() {
        1
    } else {
        0
    }
}

#[no_mangle]
pub extern "C" fn resource_file_is_writable_by_group(ptr: *const File) -> uint32_t {
    let f = unsafe {
        assert!(!ptr.is_null());
        &*ptr
    };

    if f.is_writable_by_group().unwrap() {
        1
    } else {
        0
    }
}
<|endoftext|>


<commit_before>//! Author : Thibault Barbie
//!
//! A simple evolutionary algorithm written in Rust.

extern crate rand;

use rand::Rng;

fn main() {
    let target = "methinks it is like a weasel"; // lowercase for readability
    let copies = 100;
    let mutation_rate = 20; // 1/20 = 0.05 = 5%

    let mut rng = rand::weak_rng();

    // Generate first sentence, mutating each character
    let start = mutate(&mut rng, target, 1); // 1/1 = 1 = 100%

    println!("{}", target);
    println!("{}", start);

    evolve(&mut rng, target, start, copies, mutation_rate);
}

/// Evolution algorithm
///
/// Evolves `parent` to match `target`.  Returns the number of evolutions performed.
fn evolve<R: Rng>(
    rng: &mut R,
    target: &str,
    mut parent: String,
    copies: usize,
    mutation_rate: u32,
) -> usize {
    let mut counter = 0;
    let mut parent_fitness = target.len() + 1;

    loop {
        counter += 1;

        let (best_fitness, best_sentence) = (0..copies)
            .map(|_| {
                // Copy and mutate a new sentence.
                let sentence = mutate(rng, &parent, mutation_rate);
                // Find the fitness of the new mutation
                (fitness(target, &sentence), sentence)
            })
            .min_by_key(|&(f, _)| f) // find the closest mutation to the target
            .unwrap(); // fails if `copies == 0`

        // If the best mutation of this generation is better than `parent` then "the fittest
        // survives" and the next parent becomes the best of this generation.
        if best_fitness < parent_fitness {
            parent = best_sentence;
            parent_fitness = best_fitness;
            println!(
                "{} : generation {} with fitness {}",
                parent, counter, best_fitness
            );

            if best_fitness == 0 {
                return counter;
            }
        }
    }
}

/// Computes the fitness of a sentence against a target string, returning the number of
/// incorrect characters.
fn fitness(target: &str, sentence: &str) -> usize {
    let zip = sentence.chars().zip(target.chars());
    zip.filter(|&(c1, c2)| c1 != c2).count()
}

/// Mutation algorithm.
///
/// It mutates each character of a string, according to a `mutation_rate`.
fn mutate<R: Rng>(rng: &mut R, sentence: &str, mutation_rate: u32) -> String {
    let maybe_mutate = |c| {
        if rng.gen_weighted_bool(mutation_rate) {
            random_char(rng)
        } else {
            c
        }
    };
    sentence.chars().map(maybe_mutate).collect()
}

/// Generates a random lowercase letter or space.
fn random_char<R: Rng>(rng: &mut R) -> char {
    // Returns a value in the range [a, z] + an extra slot for the space character.  '|' is the
    // exclusive upper bound.  (The `u8` values could be cast to larger integers for a better
    // chance of the RNG hitting the proper range).
    match rng.gen_range(b'a', b'|') as char {
        '{' => ' ', // the `char` after 'z'
        c => c,
    }
}
<commit_msg>uppercase, syntax, random_char update<commit_after>//! Author : Thibault Barbie
//!
//! A simple evolutionary algorithm written in Rust.

extern crate rand;

use rand::Rng;

fn main() {
    let target = "METHINKS IT IS LIKE A WEASEL";
    let copies = 100;
    let mutation_rate = 20; // 1/20 = 0.05 = 5%

    let mut rng = rand::weak_rng();

    // Generate first sentence, mutating each character
    let start = mutate(&mut rng, target, 1); // 1/1 = 1 = 100%

    println!("{}", target);
    println!("{}", start);

    evolve(&mut rng, target, start, copies, mutation_rate);
}

/// Evolution algorithm
///
/// Evolves `parent` to match `target`.  Returns the number of evolutions performed.
fn evolve<R: Rng>(
    rng: &mut R,
    target: &str,
    mut parent: String,
    copies: usize,
    mutation_rate: u32,
) -> usize {
    let mut counter = 0;
    let mut parent_fitness = target.len() + 1;

    loop {
        counter += 1;

        let (best_fitness, best_sentence) = (0..copies)
            .map(|_| {
                // Copy and mutate a new sentence.
                let sentence = mutate(rng, &parent, mutation_rate);
                // Find the fitness of the new mutation
                (fitness(target, &sentence), sentence)
            })
            .min_by_key(|&(f, _)| f) // find the closest mutation to the target
            .unwrap(); // fails if `copies == 0`

        // If the best mutation of this generation is better than `parent` then "the fittest
        // survives" and the next parent becomes the best of this generation.
        if best_fitness < parent_fitness {
            parent = best_sentence;
            parent_fitness = best_fitness;
            println!(
                "{} : generation {} with fitness {}",
                parent, counter, best_fitness
            );

            if best_fitness == 0 {
                return counter;
            }
        }
    }
}

/// Computes the fitness of a sentence against a target string, returning the number of
/// incorrect characters.
fn fitness(target: &str, sentence: &str) -> usize {
    sentence
        .chars()
        .zip(target.chars())
        .filter(|&(c1, c2)| c1 != c2)
        .count()
}

/// Mutation algorithm.
///
/// It mutates each character of a string, according to a `mutation_rate`.
fn mutate<R: Rng>(rng: &mut R, sentence: &str, mutation_rate: u32) -> String {
    let maybe_mutate = |c| {
        if rng.gen_weighted_bool(mutation_rate) {
            random_char(rng)
        } else {
            c
        }
    };
    sentence.chars().map(maybe_mutate).collect()
}

/// Generates a random letter or space.
fn random_char<R: Rng>(rng: &mut R) -> char {
    // Returns a value in the range [A, Z] + an extra slot for the space character.  (The `u8`
    // values could be cast to larger integers for a better chance of the RNG hitting the proper
    // range).
    match rng.gen_range(b'A', b'Z' + 2) {
        c if c == b'Z' + 1 => ' ', // the `char` after 'Z'
        c => c as char,
    }
}
<|endoftext|>