<commit_before>extern crate pirate;
extern crate tomllib;
use std::fs::File;
use std::env;
use std::io::{Read, BufReader, Error};
use pirate::{Matches, Match, Vars, matches, usage, vars};
use tomllib::TOMLParser;
use tomllib::types::ParseResult;

fn main() {
  let options = vec![
    "Required arguments",
    "f/file#The path to the TOML document to parse and manipulate.:",
    "#File-preserving commands",
    "g/get-value#Given a key or comma separated list of keys, retrieve the values for those keys. If multiple keys are \
      specified and any one of them fails to retrieve a value, the whole command will fail with an error message.:",
    "/has-value#Given a key or comma separated list of keys, print \"true\" if the key has a value and \"false\" if \
      the key doesn't have child keys. Optionally use the --set-true and --set-false flags to change what values are \
      printed instead of \"true\" and \"false\".:",
    "c/get-children#Given a key or comma separated list of keys, retrieve each key's set of child keys. If multiple \
      keys are specified and any one of them fails to retrieve a child keys, the whole command will fail with an error \
      message.:",
    "/has-children#Given a key or comma separated list of keys, print \"true\" if the key has child keys and \"false\" \
      if the key doesn't have child keys. Optionally use the --set-true and --set-false flags to change what values \
      are printed instead of \"true\" and \"false\".:",
    "#File-modifying commands",
    "s/set-value#Given a comma separated list of key followed by value followed by type, set the key's value to the \
      specified value. For instance: \"foo.bar,hello,basic-string,baz.qux,82374,int\" will set the key \"foo.bar\" to \
      a basic string value of \"hello\" and the key \"baz.qux\" to an integer value of 82374. If multiple keys are \
      specified and any one of them fails to set a value, the whole command will fail with an error message.:",
    "#Pre-command Options.",
    "h/help#Show this screen.",
    "/set-true#For commands that print \"true\" or \"false\", this will change what value is printed for \"true\", \
      i.e. --set-true=1 will cause tomlkit to print \"1\" for \"true\" instead of \"true\".:",
    "/set-false#For commands that print \"true\" or \"false\", this will change what value is printed for \"false\", \
      i.e. --set-false=0 will cause tomlkit to print \"0\" for \"false\" instead of \"false\".:",
    "#Post-command Options.",
    "/print-doc#Print out the resultant TOML document after all requested changes have been made.",
  ];

  let mut vars: Vars = match vars("tomlkit", &options) {
    Ok(v) => v,
    Err(e) => panic!("There was an error parsing argument definitions: {}", e)
  };

  let args: Vec<String> = env::args().collect();
  let matches: Matches = match matches(&args, &mut vars) {
    Ok(m) => m,
    Err(e) => {
      println!("Error: {}", e);
      usage(&vars);
      return;
    }
  };

  if matches.has_match("help") {
    usage(&vars);
    return;
  }
  let mut true_val = &"true".to_string();
  let mut false_val = &"false".to_string();
  let mut output: String = "".to_string();

  // The file we're operating on
  let mut file = String::new();
  if matches.has_match("file") {
    if let Some(f) = matches.get("file") {
      match get_file(f, &mut file) {
        Err(err) => {
          println!("Error: Unable to read file: \"{}\". Reason: {}", f, err);
          return;
        },
        _ => ()
      }
    } else {
      println!("Error: A required argument is missing for f/file.");
      usage(&vars);
    }
  } else

  // Pre-command options
  if matches.has_match("set-true") {
    if let Some(t) = matches.get("set-true") {
      true_val = t;
    } else {
      println!("Error: A required argument is missing for set-true.");
      usage(&vars);
    }
  } else if matches.has_match("set-false") {
    if let Some(f) = matches.get("set-false") {
      false_val = f;
    } else {
      println!("Error: A required argument is missing for set-false.");
      usage(&vars);
    }
  }

  // Commands only one command allowed per invocation for this version
  if matches.has_match("get-value") {
    if let Some(k) = matches.get("get-value") {
      output = get_value(k);
    } else {
      println!("Error: A required argument is missing for g/get-value.");
      usage(&vars);
    }
  } else if matches.has_match("has-value") {
    if let Some(k) = matches.get("has-value") {
      output = has_value(k, true_val, false_val);
    } else {
      println!("Error: A required argument is missing for has-value.");
      usage(&vars);
    }
  } else if matches.has_match("get-children") {
    if let Some(v) = matches.get("get-children") {
      output = get_children(v);
    } else {
      println!("Error: A required argument is missing for c/get-children.");
      usage(&vars);
    }
  } else if matches.has_match("has-children") {
    if let Some(k) = matches.get("has-children") {
      output = has_children(k, true_val, false_val);
    } else {
      println!("Error: A required argument is missing for has-children.");
      usage(&vars);
    }
  } else if matches.has_match("set-value") {
    if let Some(kv) = matches.get("set-value") {
      output = set_value(kv);
    } else {
      println!("Error: A required argument is missing for s/set-value.");
      usage(&vars);
    }
  } else {
    // No command specified print usage
    usage(&vars);
    return;
  }

  // Post-command options
  if matches.has_match("print-doc") {
    print_doc(&output);
  }
}

fn get_file(file_path: &String, out_file: &mut String) -> Result<usize, Error> {
  match File::open(file_path) {
    Ok(file) => {
      let mut contents = BufReader::new(&file);
      match contents.read_to_string(out_file) {
        Ok(bytes) => return Ok(bytes),
        Err(err) => return Err(err),
      }
    },
    Err(err) => return Err(err),
  }
}

fn get_value(key: &String) -> String {
  unimplemented!();
}

fn has_value(key: &String, true_val: &String, false_val: &String) -> String {
  unimplemented!();
}

fn get_children(key: &String) -> String {
  unimplemented!();
}

fn has_children(key: &String, true_val: &String, false_val: &String) -> String {
  unimplemented!();
}

fn set_value(key: &String) -> String {
  unimplemented!();
}

fn print_doc(doc: &String) {
  unimplemented!();
}
<commit_msg>Implemented document parsing, parser error handling and the get_value function. Currently only gets up to one value.<commit_after>extern crate pirate;
extern crate tomllib;
use std::fs::File;
use std::env;
use std::io::{Read, BufReader, Error};
use pirate::{Matches, Match, Vars, matches, usage, vars};
use tomllib::TOMLParser;
use tomllib::types::ParseResult;

macro_rules! usage(
  ($tval:expr) => (
    usage($tval);
    return;
  );
  ($submac:ident!( $($args:tt)* ), $tval:expr) => (
    $submac!($($args)*);
    usage($tval);
    return;
  );
);

fn main() {
  let options = vec![
    "Required arguments",
    "f/file#The path to the TOML document to parse and manipulate.:",
    "#File-preserving commands",
    "g/get-value#Given a key or comma separated list of keys, retrieve the values for those keys. If multiple keys are \
      specified and any one of them fails to retrieve a value, the whole command will fail with an error message.:",
    "/has-value#Given a key or comma separated list of keys, print \"true\" if the key has a value and \"false\" if \
      the key doesn't have child keys. Optionally use the --set-true and --set-false flags to change what values are \
      printed instead of \"true\" and \"false\".:",
    "c/get-children#Given a key or comma separated list of keys, retrieve each key's set of child keys. If multiple \
      keys are specified and any one of them fails to retrieve a child keys, the whole command will fail with an error \
      message.:",
    "/has-children#Given a key or comma separated list of keys, print \"true\" if the key has child keys and \"false\" \
      if the key doesn't have child keys. Optionally use the --set-true and --set-false flags to change what values \
      are printed instead of \"true\" and \"false\".:",
    "#File-modifying commands",
    "s/set-value#Given a comma separated list of key followed by value followed by type, set the key's value to the \
      specified value. For instance: \"foo.bar,hello,basic-string,baz.qux,82374,int\" will set the key \"foo.bar\" to \
      a basic string value of \"hello\" and the key \"baz.qux\" to an integer value of 82374. If multiple keys are \
      specified and any one of them fails to set a value, the whole command will fail with an error message.:",
    "#Pre-command Options.",
    "h/help#Show this screen.",
    "/set-true#For commands that print \"true\" or \"false\", this will change what value is printed for \"true\", \
      i.e. --set-true=1 will cause tomlkit to print \"1\" for \"true\" instead of \"true\".:",
    "/set-false#For commands that print \"true\" or \"false\", this will change what value is printed for \"false\", \
      i.e. --set-false=0 will cause tomlkit to print \"0\" for \"false\" instead of \"false\".:",
    "#Post-command Options.",
    "/print-doc#Print out the resultant TOML document after all requested changes have been made.",
  ];

  let mut vars: Vars = match vars("tomlkit", &options) {
    Ok(v) => v,
    Err(e) => panic!("There was an error parsing argument definitions: {}", e)
  };

  let args: Vec<String> = env::args().collect();
  let matches: Matches = match matches(&args, &mut vars) {
    Ok(m) => m,
    Err(e) => {
      usage!(println!("Error: {}", e), &vars);
    }
  };

  if matches.has_match("help") {
    usage!(&vars);
  }
  let mut true_val = &"true".to_string();
  let mut false_val = &"false".to_string();
  let mut output = String::new();

  // The file we're operating on
  let mut file = String::new();
  if matches.has_match("file") {
    if let Some(f) = matches.get("file") {
      match get_file(f, &mut file) {
        Err(err) => {
          println!("Error: Unable to read file: \"{}\". Reason: {}", f, err);
          return;
        },
        _ => ()
      }
    } else {
      usage!(println!("Error: A required argument is missing for f/file."), &vars);
    }
  } else {
    println!("Error: -f/--file is a required argument.");
    return;
  }

  // Parse the document
  let parser: TOMLParser = TOMLParser::new();
  let (parser, result) = parser.parse(&file);
  match result {
    ParseResult::Partial(_,_,_) => {
      println!("Error: Document only partially parsed. Please correct any errors before trying again.");
      return;
    },
    ParseResult::PartialError(_,_,_,_) => {
      println!("Error: Document only partially parsed with errors. Please correct any errors before trying again.");
      return;
    },
    ParseResult::Failure(_,_) => {
      println!("Error: Completely failed to parse document. Please correct any error before trying again.");
      return;
    },
    _ => (), // If verbose output Full or FullError
  }

  // Pre-command options
  if matches.has_match("set-true") {
    if let Some(t) = matches.get("set-true") {
      true_val = t;
    } else {
      usage!(println!("Error: A required argument is missing for set-true."), &vars);
    }
  } else if matches.has_match("set-false") {
    if let Some(f) = matches.get("set-false") {
      false_val = f;
    } else {
      usage!(println!("Error: A required argument is missing for set-false."), &vars);
    }
  }

  // Commands only one command allowed per invocation for this version
  if matches.has_match("get-value") {
    if let Some(k) = matches.get("get-value") {
      if let Some(val) = get_value(k, &parser) {
        output = val;
      }
    } else {
      usage!(println!("Error: A required argument is missing for g/get-value."), &vars);
    }
  } else if matches.has_match("has-value") {
    if let Some(k) = matches.get("has-value") {
      if let Some(val) = has_value(k, &parser, true_val, false_val) {
        output = val;
      }
    } else {
      usage!(println!("Error: A required argument is missing for has-value."), &vars);
    }
  } else if matches.has_match("get-children") {
    if let Some(k) = matches.get("get-children") {
      if let Some(c) = get_children(k, &parser) {
        output = c;
      }
    } else {
      usage!(println!("Error: A required argument is missing for c/get-children."), &vars);
    }
  } else if matches.has_match("has-children") {
    if let Some(k) = matches.get("has-children") {
      if let Some(c) = has_children(k, &parser, true_val, false_val) {
        output = c;
      }
    } else {
      usage!(println!("Error: A required argument is missing for has-children."), &vars);
    }
  } else if matches.has_match("set-value") {
    if let Some(kv) = matches.get("set-value") {
      if let Some(out) = set_value(kv, &parser) {
        output = out;
      }
    } else {
      usage!(println!("Error: A required argument is missing for s/set-value."), &vars);
    }
  } else {
    // No command specified print usage
    usage!(&vars);
  }

  // ************** Print output here! *******************
  println!("{}", output);

  // Post-command options
  if matches.has_match("print-doc") {
    println!("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>DOCUMENT<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
    print_doc(&parser);
  }
}

fn get_file(file_path: &String, out_file: &mut String) -> Result<usize, Error> {
  match File::open(file_path) {
    Ok(file) => {
      let mut contents = BufReader::new(&file);
      match contents.read_to_string(out_file) {
        Ok(bytes) => return Ok(bytes),
        Err(err) => return Err(err),
      }
    },
    Err(err) => return Err(err),
  }
}

fn get_value(key: &str, doc: &TOMLParser) -> Option<String> {
  if let Some(value) = doc.get_value(key) {
    return Some(format!("{}", value));
  }
  None
}

fn has_value(key: &str, doc: &TOMLParser, true_val: &String, false_val: &String) -> Option<String> {
  unimplemented!();
}

fn get_children(key: &str, doc: &TOMLParser) -> Option<String> {
  unimplemented!();
}

fn has_children(key: &str, doc: &TOMLParser, true_val: &String, false_val: &String) -> Option<String> {
  unimplemented!();
}

fn set_value(key: &str, doc: &TOMLParser) -> Option<String> {
  unimplemented!();
}

fn print_doc(doc: &TOMLParser) {
  //unimplemented!();
  println!("{}", doc);
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

//! Support for fmt! expressions.
//!
//! The syntax is close to that of Posix format strings:
//!
//! ~~~~~~
//! Format := '%' Parameter? Flag* Width? Precision? Type
//! Parameter := [0-9]+ '$'
//! Flag := [ 0#+-]
//! Width := Parameter | [0-9]+
//! Precision := '.' [0-9]+
//! Type := [bcdfiostuxX?]
//! ~~~~~~
//!
//! * Parameter is the 1-based argument to apply the format to. Currently not
//! implemented.
//! * Flag 0 causes leading zeros to be used for padding when converting
//! numbers.
//! * Flag # causes the conversion to be done in an *alternative* manner.
//! Currently not implemented.
//! * Flag + causes signed numbers to always be prepended with a sign
//! character.
//! * Flag - left justifies the result
//! * Width specifies the minimum field width of the result. By default
//! leading spaces are added.
//! * Precision specifies the minimum number of digits for integral types
//! and the minimum number
//! of decimal places for float.
//!
//! The types currently supported are:
//!
//! * b - bool
//! * c - char
//! * d - int
//! * f - float
//! * i - int (same as d)
//! * o - uint as octal
//! * t - uint as binary
//! * u - uint
//! * x - uint as lower-case hexadecimal
//! * X - uint as upper-case hexadecimal
//! * s - str (any flavor)
//! * ? - arbitrary type (does not use the to_str trait)

/*
Syntax Extension: fmt

Format a string

The 'fmt' extension is modeled on the posix printf system.

A posix conversion ostensibly looks like this

> %~[parameter]~[flags]~[width]~[.precision]~[length]type

Given the different numeric type bestiary we have, we omit the 'length'
parameter and support slightly different conversions for 'type'

> %~[parameter]~[flags]~[width]~[.precision]type

we also only support translating-to-rust a tiny subset of the possible
combinations at the moment.

Example:

debug!("hello, %s!", "world");

*/

use prelude::*;

/*
 * We have a 'ct' (compile-time) module that parses format strings into a
 * sequence of conversions. From those conversions AST fragments are built
 * that call into properly-typed functions in the 'rt' (run-time) module.
 * Each of those run-time conversion functions accepts another conversion
 * description that specifies how to format its output.
 *
 * The building of the AST is currently done in a module inside the compiler,
 * but should migrate over here as the plugin interface is defined.
 */

// Functions used by the fmt extension at compile time
#[doc(hidden)]
pub mod ct {
    use char;
    use container::Container;
    use prelude::*;
    use str;

    #[deriving(Eq)]
    pub enum Signedness { Signed, Unsigned, }

    #[deriving(Eq)]
    pub enum Caseness { CaseUpper, CaseLower, }

    #[deriving(Eq)]
    pub enum Ty {
        TyBool,
        TyStr,
        TyChar,
        TyInt(Signedness),
        TyBits,
        TyHex(Caseness),
        TyOctal,
        TyFloat,
        TyPointer,
        TyPoly,
    }

    #[deriving(Eq)]
    pub enum Flag {
        FlagLeftJustify,
        FlagLeftZeroPad,
        FlagSpaceForSign,
        FlagSignAlways,
        FlagAlternate,
    }

    #[deriving(Eq)]
    pub enum Count {
        CountIs(uint),
        CountIsParam(uint),
        CountIsNextParam,
        CountImplied,
    }

    #[deriving(Eq)]
    struct Parsed<T> {
        val: T,
        next: uint
    }

    impl<T> Parsed<T> {
        pub fn new(val: T, next: uint) -> Parsed<T> {
            Parsed {val: val, next: next}
        }
    }

    // A formatted conversion from an expression to a string
    #[deriving(Eq)]
    pub struct Conv {
        param: Option<uint>,
        flags: ~[Flag],
        width: Count,
        precision: Count,
        ty: Ty
    }

    // A fragment of the output sequence
    #[deriving(Eq)]
    pub enum Piece {
        PieceString(~str),
        PieceConv(Conv),
    }

    pub type ErrorFn<'self> = &'self fn(&str) -> !;

    pub fn parse_fmt_string<'a>(s: &str, err: ErrorFn<'a>) -> ~[Piece] {
        fn push_slice(ps: &mut ~[Piece], s: &str, from: uint, to: uint) {
            if to > from {
                ps.push(PieceString(s.slice(from, to).to_owned()));
            }
        }

        let lim = s.len();
        let mut h = 0;
        let mut i = 0;
        let mut pieces = ~[];

        while i < lim {
            if s[i] == '%' as u8 {
                i += 1;

                if i >= lim {
                    err("unterminated conversion at end of string");
                } else if s[i] == '%' as u8 {
                    push_slice(&mut pieces, s, h, i);
                    i += 1;
                } else {
                    push_slice(&mut pieces, s, h, i - 1);
                    let Parsed {
                        val,
                        next
                    } = parse_conversion(s, i, lim, |s| err(s));
                    pieces.push(val);
                    i = next;
                }

                h = i;
            } else {
                i += str::utf8_char_width(s[i]);
            }
        }

        push_slice(&mut pieces, s, h, i);
        pieces
    }

    pub fn peek_num(s: &str, i: uint, lim: uint) -> Option<Parsed<uint>> {
        let mut i = i;
        let mut accum = 0;
        let mut found = false;

        while i < lim {
            match char::to_digit(s[i] as char, 10) {
                Some(x) => {
                    found = true;
                    accum *= 10;
                    accum += x;
                    i += 1;
                }
                None => break
            }
        }

        if found {
            Some(Parsed::new(accum, i))
        } else {
            None
        }
    }

    pub fn parse_conversion<'a>(s: &str, i: uint, lim: uint, err: ErrorFn<'a>)
                                -> Parsed<Piece> {
        let param = parse_parameter(s, i, lim);
        // avoid copying ~[Flag] by destructuring
        let Parsed {val: flags_val, next: flags_next} = parse_flags(s,
            param.next, lim);
        let width = parse_count(s, flags_next, lim);
        let prec = parse_precision(s, width.next, lim);
        let ty = parse_type(s, prec.next, lim, err);

        Parsed::new(PieceConv(Conv {
            param: param.val,
            flags: flags_val,
            width: width.val,
            precision: prec.val,
            ty: ty.val}), ty.next)
    }

    pub fn parse_parameter(s: &str, i: uint, lim: uint) ->
        Parsed<Option<uint>> {
        if i >= lim { return Parsed::new(None, i); }

        match peek_num(s, i, lim) {
            Some(num) if num.next < lim && s[num.next] == '$' as u8 =>
                Parsed::new(Some(num.val), num.next + 1),
            _ => Parsed::new(None, i)
        }
    }

    pub fn parse_flags(s: &str, i: uint, lim: uint) -> Parsed<~[Flag]> {
        let mut i = i;
        let mut flags = ~[];

        while i < lim {
            let f = match s[i] as char {
                '-' => FlagLeftJustify,
                '0' => FlagLeftZeroPad,
                ' ' => FlagSpaceForSign,
                '+' => FlagSignAlways,
                '#' => FlagAlternate,
                _ => break
            };

            flags.push(f);
            i += 1;
        }

        Parsed::new(flags, i)
    }

    pub fn parse_count(s: &str, i: uint, lim: uint) -> Parsed<Count> {
        if i >= lim {
            Parsed::new(CountImplied, i)
        } else if s[i] == '*' as u8 {
            let param = parse_parameter(s, i + 1, lim);
            let j = param.next;

            match param.val {
                None => Parsed::new(CountIsNextParam, j),
                Some(n) => Parsed::new(CountIsParam(n), j)
            }
        } else {
            match peek_num(s, i, lim) {
                None => Parsed::new(CountImplied, i),
                Some(num) => Parsed::new(CountIs(num.val), num.next)
            }
        }
    }

    pub fn parse_precision(s: &str, i: uint, lim: uint) -> Parsed<Count> {
        if i < lim && s[i] == '.' as u8 {
            let count = parse_count(s, i + 1, lim);

            // If there were no digits specified, i.e. the precision
            // was ".", then the precision is 0
            match count.val {
                CountImplied => Parsed::new(CountIs(0), count.next),
                _ => count
            }
        } else {
            Parsed::new(CountImplied, i)
        }
    }

    pub fn parse_type<'a>(s: &str, i: uint, lim: uint, err: ErrorFn<'a>)
                          -> Parsed<Ty> {
        if i >= lim { err("missing type in conversion"); }

        // FIXME (#2249): Do we really want two signed types here?
        // How important is it to be printf compatible?
        let t = match s[i] as char {
            'b' => TyBool,
            's' => TyStr,
            'c' => TyChar,
            'd' | 'i' => TyInt(Signed),
            'u' => TyInt(Unsigned),
            'x' => TyHex(CaseLower),
            'X' => TyHex(CaseUpper),
            't' => TyBits,
            'o' => TyOctal,
            'f' => TyFloat,
            'p' => TyPointer,
            '?' => TyPoly,
            _ => err(format!("unknown type in conversion: {}", s.char_at(i)))
        };

        Parsed::new(t, i + 1)
    }

    #[cfg(test)]
    fn die(s: &str) -> ! { fail2!(s.to_owned()) }

    #[test]
    fn test_parse_count() {
        fn test(s: &str, count: Count, next: uint) -> bool {
            parse_count(s, 0, s.len()) == Parsed::new(count, next)
        }

        assert!(test("", CountImplied, 0));
        assert!(test("*", CountIsNextParam, 1));
        assert!(test("*1", CountIsNextParam, 1));
        assert!(test("*1$", CountIsParam(1), 3));
        assert!(test("123", CountIs(123), 3));
    }

    #[test]
    fn test_parse_flags() {
        fn pack(fs: &[Flag]) -> uint {
            fs.iter().fold(0, |p, &f| p | (1 << f as uint))
        }

        fn test(s: &str, flags: &[Flag], next: uint) {
            let f = parse_flags(s, 0, s.len());
            assert_eq!(pack(f.val), pack(flags));
            assert_eq!(f.next, next);
        }

        test("", [], 0);
        test("!#-+ 0", [], 0);
        test("#-+", [FlagAlternate, FlagLeftJustify, FlagSignAlways], 3);
        test(" 0", [FlagSpaceForSign, FlagLeftZeroPad], 2);
    }

    #[test]
    fn test_parse_fmt_string() {
        assert!(parse_fmt_string("foo %s bar", die) == ~[
            PieceString(~"foo "),
            PieceConv(Conv {
                param: None,
                flags: ~[],
                width: CountImplied,
                precision: CountImplied,
                ty: TyStr,
            }),
            PieceString(~" bar")]);

        assert!(parse_fmt_string("%s", die) == ~[
            PieceConv(Conv {
                param: None,
                flags: ~[],
                width: CountImplied,
                precision: CountImplied,
                ty: TyStr,
            })]);

        assert!(parse_fmt_string("%%%%", die) == ~[
            PieceString(~"%"), PieceString(~"%")]);
    }

    #[test]
    fn test_parse_parameter() {
        fn test(s: &str, param: Option<uint>, next: uint) -> bool {
            parse_parameter(s, 0, s.len()) == Parsed::new(param, next)
        }

        assert!(test("", None, 0));
        assert!(test("foo", None, 0));
        assert!(test("123", None, 0));
        assert!(test("123$", Some(123), 4));
    }

    #[test]
    fn test_parse_precision() {
        fn test(s: &str, count: Count, next: uint) -> bool {
            parse_precision(s, 0, s.len()) == Parsed::new(count, next)
        }

        assert!(test("", CountImplied, 0));
        assert!(test(".", CountIs(0), 1));
        assert!(test(".*", CountIsNextParam, 2));
        assert!(test(".*1", CountIsNextParam, 2));
        assert!(test(".*1$", CountIsParam(1), 4));
        assert!(test(".123", CountIs(123), 4));
    }

    #[test]
    fn test_parse_type() {
        fn test(s: &str, ty: Ty) -> bool {
            parse_type(s, 0, s.len(), die) == Parsed::new(ty, 1)
        }

        assert!(test("b", TyBool));
        assert!(test("c", TyChar));
        assert!(test("d", TyInt(Signed)));
        assert!(test("f", TyFloat));
        assert!(test("i", TyInt(Signed)));
        assert!(test("o", TyOctal));
        assert!(test("s", TyStr));
        assert!(test("t", TyBits));
        assert!(test("x", TyHex(CaseLower)));
        assert!(test("X", TyHex(CaseUpper)));
        assert!(test("p", TyPointer));
        assert!(test("?", TyPoly));
    }

    #[test]
    #[should_fail]
    fn test_parse_type_missing() {
        parse_type("", 0, 0, die);
    }

    #[test]
    #[should_fail]
    fn test_parse_type_unknown() {
        parse_type("!", 0, 1, die);
    }

    #[test]
    fn test_peek_num() {
        let s1 = "";
        assert!(peek_num(s1, 0, s1.len()).is_none());

        let s2 = "foo";
        assert!(peek_num(s2, 0, s2.len()).is_none());

        let s3 = "123";
        assert_eq!(peek_num(s3, 0, s3.len()), Some(Parsed::new(123, 3)));

        let s4 = "123foo";
        assert_eq!(peek_num(s4, 0, s4.len()), Some(Parsed::new(123, 3)));
    }
}

// Functions used by the fmt extension at runtime. For now there are a lot of
// decisions made a runtime. If it proves worthwhile then some of these
// conditions can be evaluated at compile-time. For now though it's cleaner to
// implement it this way, I think.
#[doc(hidden)]
#[allow(non_uppercase_statics)]
pub mod rt {
    use f64;
    use str;
    use sys;
    use num;
    use vec;
    use option::{Some, None, Option};

    pub static flag_none : u32 = 0u32;
    pub static flag_left_justify   : u32 = 0b00000000000001u32;
    pub static flag_left_zero_pad  : u32 = 0b00000000000010u32;
    pub static flag_space_for_sign : u32 = 0b00000000000100u32;
    pub static flag_sign_always    : u32 = 0b00000000001000u32;
    pub static flag_alternate      : u32 = 0b00000000010000u32;

    pub enum Count { CountIs(uint), CountImplied, }

    pub enum Ty { TyDefault, TyBits, TyHexUpper, TyHexLower, TyOctal, }

    pub struct Conv {
        flags: u32,
        width: Count,
        precision: Count,
        ty: Ty,
    }

    pub fn conv_int(cv: Conv, i: int, buf: &mut ~str) {
        let radix = 10;
        let prec = get_int_precision(cv);
        let s : ~str = uint_to_str_prec(num::abs(i) as uint, radix, prec);

        let head = if i >= 0 {
            if have_flag(cv.flags, flag_sign_always) {
                Some('+')
            } else if have_flag(cv.flags, flag_space_for_sign) {
                Some(' ')
            } else {
                None
            }
        } else { Some('-') };
        pad(cv, s, head, PadSigned, buf);
    }
    pub fn conv_uint(cv: Conv, u: uint, buf: &mut ~str) {
        let prec = get_int_precision(cv);
        let rs =
            match cv.ty {
              TyDefault => uint_to_str_prec(u, 10, prec),
              TyHexLower => uint_to_str_prec(u, 16, prec),

              // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
              // to_ascii_move and to_str_move to not do a unnecessary copy.
              TyHexUpper => {
                let s = uint_to_str_prec(u, 16, prec);
                s.to_ascii().to_upper().to_str_ascii()
              }
              TyBits => uint_to_str_prec(u, 2, prec),
              TyOctal => uint_to_str_prec(u, 8, prec)
            };
        pad(cv, rs, None, PadUnsigned, buf);
    }
    pub fn conv_bool(cv: Conv, b: bool, buf: &mut ~str) {
        let s = if b { "true" } else { "false" };
        // run the boolean conversion through the string conversion logic,
        // giving it the same rules for precision, etc.
        conv_str(cv, s, buf);
    }
    pub fn conv_char(cv: Conv, c: char, buf: &mut ~str) {
        pad(cv, "", Some(c), PadNozero, buf);
    }
    pub fn conv_str(cv: Conv, s: &str, buf: &mut ~str) {
        // For strings, precision is the maximum characters
        // displayed
        let unpadded = match cv.precision {
            CountImplied => s,
            CountIs(max) => {
                if (max as uint) < s.char_len() {
                    s.slice(0, max as uint)
                } else {
                    s
                }
            }
        };
        pad(cv, unpadded, None, PadNozero, buf);
    }
    pub fn conv_float(cv: Conv, f: f64, buf: &mut ~str) {
        let (to_str, digits) = match cv.precision {
              CountIs(c) => (f64::to_str_exact, c as uint),
              CountImplied => (f64::to_str_digits, 6u)
        };
        let s = to_str(f, digits);
        let head = if 0.0 <= f {
            if have_flag(cv.flags, flag_sign_always) {
                Some('+')
            } else if have_flag(cv.flags, flag_space_for_sign) {
                Some(' ')
            } else {
                None
            }
        } else { None };
        pad(cv, s, head, PadFloat, buf);
    }
    pub fn conv_pointer<T>(cv: Conv, ptr: *T, buf: &mut ~str) {
        let s = ~"0x" + uint_to_str_prec(ptr as uint, 16, 1u);
        pad(cv, s, None, PadNozero, buf);
    }
    pub fn conv_poly<T>(cv: Conv, v: &T, buf: &mut ~str) {
        let s = sys::log_str(v);
        conv_str(cv, s, buf);
    }

    // Convert a uint to string with a minimum number of digits.  If precision
    // is 0 and num is 0 then the result is the empty string. Could move this
    // to uint: but it doesn't seem all that useful.
    pub fn uint_to_str_prec(num: uint, radix: uint, prec: uint) -> ~str {
        return if prec == 0u && num == 0u {
                ~""
            } else {
                let s = num.to_str_radix(radix);
                let len = s.char_len();
                if len < prec {
                    let diff = prec - len;
                    let pad = str::from_chars(vec::from_elem(diff, '0'));
                    pad + s
                } else { s }
            };
    }
    pub fn get_int_precision(cv: Conv) -> uint {
        return match cv.precision {
              CountIs(c) => c as uint,
              CountImplied => 1u
            };
    }

    #[deriving(Eq)]
    pub enum PadMode { PadSigned, PadUnsigned, PadNozero, PadFloat }

    pub fn pad(cv: Conv, s: &str, head: Option<char>, mode: PadMode,
               buf: &mut ~str) {
        let headsize = match head { Some(_) => 1, _ => 0 };
        let uwidth : uint = match cv.width {
            CountImplied => {
                for &c in head.iter() {
                    buf.push_char(c);
                }
                return buf.push_str(s);
            }
            CountIs(width) => { width as uint }
        };
        let strlen = s.char_len() + headsize;
        if uwidth <= strlen {
            for &c in head.iter() {
                buf.push_char(c);
            }
            return buf.push_str(s);
        }
        let mut padchar = ' ';
        let diff = uwidth - strlen;
        if have_flag(cv.flags, flag_left_justify) {
            for &c in head.iter() {
                buf.push_char(c);
            }
            buf.push_str(s);
            do diff.times {
                buf.push_char(padchar);
            }
            return;
        }
        let (might_zero_pad, signed) = match mode {
          PadNozero   => (false, true),
          PadSigned   => (true, true),
          PadFloat    => (true, true),
          PadUnsigned => (true, false)
        };
        fn have_precision(cv: Conv) -> bool {
            return match cv.precision { CountImplied => false, _ => true };
        }
        let zero_padding = {
            if might_zero_pad && have_flag(cv.flags, flag_left_zero_pad) &&
                (!have_precision(cv) || mode == PadFloat) {
                padchar = '0';
                true
            } else {
                false
            }
        };
        let padstr = str::from_chars(vec::from_elem(diff, padchar));
        // This is completely heinous. If we have a signed value then
        // potentially rip apart the intermediate result and insert some
        // zeros. It may make sense to convert zero padding to a precision
        // instead.

        if signed && zero_padding {
            for &head in head.iter() {
                if head == '+' || head == '-' || head == ' ' {
                    buf.push_char(head);
                    buf.push_str(padstr);
                    buf.push_str(s);
                    return;
                }
            }
        }
        buf.push_str(padstr);
        for &c in head.iter() {
            buf.push_char(c);
        }
        buf.push_str(s);
    }
    #[inline]
    pub fn have_flag(flags: u32, f: u32) -> bool {
        flags & f != 0
    }
}

// Bulk of the tests are in src/test/run-pass/syntax-extension-fmt.rs
#[cfg(test)]
mod test {
    #[test]
    fn fmt_slice() {
        let s = "abc";
        let _s = format!("{}", s);
    }
}
<commit_msg>Removed the file src/libstd/unstable/extfmt.r<commit_after><|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::process;
use std::error::Error;

struct Config {
    query: String,
    filename: String,
}

impl Config {
    fn new(args: &[String]) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("not enough arguments");
        }

        let query = args[1].clone();
        let filename = args[2].clone();

        Ok(Config {
            query: query,
            filename: filename,
        })
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();

    let config = Config::new(&args).unwrap_or_else(|err| {
        println!("Problem parsing arguments: {}", err);
        process::exit(1);
    });

    if let Err(e) = run(config) {
        println!("Application error: {}", e);

        process::exit(1);
    }
}

fn run(config: Config) -> Result<(), Box<Error>> {
    let mut f = File::open(config.filename.clone())?;

    println!("Searching for {}", config.query);
    println!("In file {}\n", config.filename);

    let mut contents = String::new();
    f.read_to_string(&mut contents)?;

    println!("With text:\n{}", contents);

    Ok(())
}<commit_msg>split main.rs && lib.rs<commit_after>extern crate greprs;

use std::env;
use std::process;

use greprs::Config;

fn main() {
    let args: Vec<String> = env::args().collect();

    let config = Config::new(&args).unwrap_or_else(|err| {
        println!("Problem parsing arguments: {}", err);
        process::exit(1);
    });

    println!("Searching for {}", config.query);
    println!("In file {}", config.filename);

    if let Err(e) = greprs::run(config) {
        println!("Application error: {}", e);

        process::exit(1);
    }
}<|endoftext|>


<commit_before>#[repr(C)]
pub struct MBuf {
    buf_addr: *mut u8,
    phys_addr: usize,
    buf_len: u16,
    data_off: u16,
    refcnt: u16,
    nb_segs: u8,
    port: u8,
    ol_flags: u8,
    packet_type: u32,
    pkt_len: u32,
    data_len: u16,
    vlan_tci: u16,
    hash: u64,
    seqn: u32,
    vlan_tci_outer: u32,
    userdata: u64,
    pool: u64,
    next: *mut MBuf,
    tx_offload: u64,
    priv_size: u16,
    timesync: u16,
}

// FIXME: Remove this once we start using these functions correctly
#[allow(dead_code)]
impl MBuf {
    #[inline]
    pub fn data_address(&self, offset: usize) -> *mut u8 {
        unsafe { self.buf_addr.offset(self.data_off as isize).offset(offset as isize) }
    }

    /// Returns the total allocated size of this mbuf segment.
    /// This is a constant.
    #[inline]
    pub fn buf_len(&self) -> usize {
        self.buf_len as usize
    }

    /// Returns the length of data in this mbuf segment.
    #[inline]
    pub fn data_len(&self) -> usize {
        self.data_len as usize
    }

    /// Returns the size of the packet (across multiple mbuf segment).
    #[inline]
    pub fn pkt_len(&self) -> usize {
        self.pkt_len as usize
    }

    /// Change the length of data in this packet.
    ///
    /// FIXME: This does not consider segments
    #[inline]
    pub fn change_data_len(&mut self, len: usize) {
        self.pkt_len = len as u32;
        self.data_len = len as u16;
    }

    /// Add data to this packet.
    pub fn append_data(&mut self, len: usize) {
        self.pkt_len = self.pkt_len + (len as u32);
        self.data_len = self.data_len + (len as u16);
    }
}
<commit_msg>Add functions to add or remove data from packet<commit_after>#[repr(C)]
pub struct MBuf {
    buf_addr: *mut u8,
    phys_addr: usize,
    buf_len: u16,
    data_off: u16,
    refcnt: u16,
    nb_segs: u8,
    port: u8,
    ol_flags: u8,
    packet_type: u32,
    pkt_len: u32,
    data_len: u16,
    vlan_tci: u16,
    hash: u64,
    seqn: u32,
    vlan_tci_outer: u32,
    userdata: u64,
    pool: u64,
    next: *mut MBuf,
    tx_offload: u64,
    priv_size: u16,
    timesync: u16,
}

// FIXME: Remove this once we start using these functions correctly
#[allow(dead_code)]
impl MBuf {
    #[inline]
    pub fn data_address(&self, offset: usize) -> *mut u8 {
        unsafe { self.buf_addr.offset(self.data_off as isize).offset(offset as isize) }
    }

    /// Returns the total allocated size of this mbuf segment.
    /// This is a constant.
    #[inline]
    pub fn buf_len(&self) -> usize {
        self.buf_len as usize
    }

    /// Returns the length of data in this mbuf segment.
    #[inline]
    pub fn data_len(&self) -> usize {
        self.data_len as usize
    }

    /// Returns the size of the packet (across multiple mbuf segment).
    #[inline]
    pub fn pkt_len(&self) -> usize {
        self.pkt_len as usize
    }

    #[inline]
    fn pkt_headroom(&self) -> usize {
        self.data_off as usize
    }

    #[inline]
    fn pkt_tailroom(&self) -> usize {
        self.buf_len() - self.data_off as usize - self.data_len()
    }

    /// Add data to the beginning of the packet. This might fail (i.e., return 0) when no more headroom is left.
    #[inline]
    pub fn add_data_beginning(&mut self, len: usize) -> usize {
        // If only we could add a likely here.
        if len > self.pkt_headroom() {
            0
        } else {
            self.data_off -= len as u16;
            self.data_len += len as u16;
            self.pkt_len += len as u32;
            len
        }
    }

    /// Add data to the end of a packet buffer. This might fail (i.e., return 0) when no more tailroom is left. We do
    /// not currently deal with packet with multiple segments.
    #[inline]
    pub fn add_data_end(&mut self, len: usize) -> usize {
        if len > self.pkt_tailroom() {
            0
        } else {
            self.data_len += len as u16;
            self.pkt_len += len as u32;
            len
        }
    }

    #[inline]
    pub fn remove_data_beginning(&mut self, len: usize) -> usize {
        if len > self.data_len() {
            0
        } else {
            self.data_off += len as u16;
            self.data_len -= len as u16;
            self.pkt_len -= len as u32;
            len
        }
    }

    #[inline]
    pub fn remove_data_end(&mut self, len: usize) -> usize {
        if len > self.data_len() {
            0
        } else {
            self.data_len -= len as u16;
            self.pkt_len -= len as u32;
            len
        }
    }
}
<|endoftext|>


<commit_before>// Bloom
//
// HTTP REST API caching middleware
// Copyright: 2017, Valerian Saliou <valerian@valeriansaliou.name>
// License: Mozilla Public License v2.0 (MPL v2.0)

use std::cmp;
use std::time::Duration;
use r2d2::Pool;
use r2d2::config::Config;
use r2d2_redis::{RedisConnectionManager, Error};
use redis::{self, Value, Connection, Commands, PipelineCommands};
use futures::future::Future;
use futures_cpupool::CpuPool;

use super::route::ROUTE_PREFIX;
use APP_CONF;

static KEY_BODY: &'static str = "b";
static KEY_FINGERPRINT: &'static str = "f";
static KEY_TAGS: &'static str = "t";
static KEY_TAGS_SEPARATOR: &'static str = ",";

lazy_static! {
    pub static ref EXECUTOR_POOL: CpuPool = CpuPool::new(APP_CONF.cache.executor_pool as usize);
}

pub struct CacheStoreBuilder;

pub struct CacheStore {
    pool: Pool<RedisConnectionManager>,
}

#[derive(Debug)]
pub enum CacheStoreError {
    Disconnected,
    Failed,
    Invalid,
    Corrupted,
    TooLarge,
}

#[derive(Debug)]
pub enum CachePurgeVariant {
    Bucket,
    Auth,
}

type CacheReadResultFuture = Box<Future<Item = Option<String>, Error = CacheStoreError>>;
type CacheWriteResult = Result<(String), (CacheStoreError, String)>;
type CacheWriteResultFuture = Box<Future<Item = CacheWriteResult, Error = ()>>;
type CachePurgeResult = Result<(), CacheStoreError>;

impl CacheStoreBuilder {
    pub fn new() -> CacheStore {
        info!(
            "binding to store backend at {}:{}",
            APP_CONF.redis.host,
            APP_CONF.redis.port
        );

        let addr_auth = match APP_CONF.redis.password {
            Some(ref password) => format!(":{}@", password),
            None => "".to_string(),
        };

        let tcp_addr_raw =
            format!(
            "redis://{}{}:{}/{}",
            &addr_auth,
            APP_CONF.redis.host,
            APP_CONF.redis.port,
            APP_CONF.redis.database,
        );

        debug!("will connect to redis at: {}", tcp_addr_raw);

        match RedisConnectionManager::new(tcp_addr_raw.as_ref()) {
            Ok(manager) => {
                let config = Config::<Connection, Error>::builder()
                    .initialization_fail_fast(false)
                    .test_on_check_out(false)
                    .pool_size(APP_CONF.redis.pool_size)
                    .max_lifetime(Some(
                        Duration::from_secs(APP_CONF.redis.max_lifetime_seconds),
                    ))
                    .idle_timeout(Some(
                        Duration::from_secs(APP_CONF.redis.idle_timeout_seconds),
                    ))
                    .connection_timeout(Duration::from_secs(
                        APP_CONF.redis.connection_timeout_seconds,
                    ))
                    .build();

                match Pool::new(config, manager) {
                    Ok(pool) => {
                        info!("bound to store backend");

                        CacheStore { pool: pool }
                    }
                    Err(_) => panic!("could not spawn redis pool"),
                }
            }
            Err(_) => panic!("could not create redis connection manager"),
        }
    }
}

impl CacheStore {
    pub fn get_meta(&self, shard: u8, key: String) -> CacheReadResultFuture {
        let pool = self.pool.to_owned();

        Box::new(EXECUTOR_POOL.spawn_fn(move || {
            get_cache_store_client!(pool, CacheStoreError::Disconnected, client {
                    match (*client).hget::<_, _, (Value, Value)>(key, (KEY_FINGERPRINT, KEY_TAGS)) {
                        Ok(value) => {
                            match value {
                                (Value::Data(fingerprint_bytes), tags_bytes) => {
                                    // Parse tags and bump their last access time
                                    if let Value::Data(tags_bytes_data) = tags_bytes {
                                        if let Ok(tags_data) = String::from_utf8(
                                            tags_bytes_data) {
                                            if tags_data.is_empty() == false {
                                                let tags = tags_data.split(KEY_TAGS_SEPARATOR)
                                                    .map(|tag| {
                                                        format!(
                                                            "{}:{}:{}", ROUTE_PREFIX, shard, tag
                                                        )
                                                    })
                                                    .collect::<Vec<String>>();

                                                // Proceed a soft bump of last access time of \
                                                //   associated tag keys. This prevents a \
                                                //   frequently accessed cache namespace to \
                                                //   become 'orphan' (ie. one or more tag keys \
                                                //   are LRU-expired), and thus cache namespace \
                                                //   not to be properly removed on purge of an \
                                                //   associated tag.
                                                // Also, count bumped keys. It may happen that \
                                                //   some tag keys are incorrectly removed by \
                                                //   Redis LRU system, as it is probabilistic \
                                                //   and thus might sample some keys incorrectly.
                                                // The conditions explained above only happens on \
                                                //   Redis instances with used memory going over \
                                                //   the threshold of the max memory policy.
                                                let tags_count = tags.len();

                                                match redis::cmd("TOUCH").arg(tags)
                                                    .query::<usize>(&*client) {
                                                    Ok(bump_count) => {
                                                        // Partial bump count? Consider cache as \
                                                        //   non-existing
                                                        if bump_count < tags_count {
                                                            return Ok(None);
                                                        }
                                                    },
                                                    Err(err) => {
                                                        error!(
                                                            "error bumping access time of tags: {}",
                                                            err
                                                        );
                                                    }
                                                }
                                            }
                                        }
                                    }

                                    // Decode raw bytes to string
                                    if let Ok(fingerprint) = String::from_utf8(fingerprint_bytes) {
                                        Ok(Some(fingerprint))
                                    } else {
                                        Err(CacheStoreError::Corrupted)
                                    }
                                },
                                (Value::Nil, _) | (_, Value::Nil) => Ok(None),
                                _ => Err(CacheStoreError::Invalid),
                            }
                        },
                        _ => Err(CacheStoreError::Failed),
                    }
                })
        }))
    }

    pub fn get_body(&self, key: String) -> CacheReadResultFuture {
        let pool = self.pool.to_owned();

        Box::new(EXECUTOR_POOL.spawn_fn(move || {
            get_cache_store_client!(pool, CacheStoreError::Disconnected, client {
                    match (*client).hget::<_, _, Value>(key, KEY_BODY) {
                        Ok(value) => {
                            match value {
                                Value::Data(body_bytes) => {
                                    // Decode raw bytes to string
                                    if let Ok(body) = String::from_utf8(body_bytes) {
                                        Ok(Some(body))
                                    } else {
                                        Err(CacheStoreError::Corrupted)
                                    }
                                },
                                Value::Nil => Ok(None),
                                _ => Err(CacheStoreError::Invalid),
                            }
                        },
                        _ => Err(CacheStoreError::Failed),
                    }
                })
        }))
    }

    pub fn set(
        &self,
        key: String,
        key_mask: String,
        value: String,
        fingerprint: String,
        ttl: usize,
        key_tags: Vec<(String, String)>,
    ) -> CacheWriteResultFuture {
        let pool = self.pool.to_owned();

        Box::new(EXECUTOR_POOL.spawn_fn(move || {
            Ok(get_cache_store_client!(
                    pool,
                    (CacheStoreError::Disconnected, fingerprint),

                    client {
                        // Cap TTL to 'max_key_expiration'
                        let ttl_cap = cmp::min(ttl, APP_CONF.redis.max_key_expiration);

                        // Ensure value is not larger than 'max_key_size'
                        if value.len() > APP_CONF.redis.max_key_size {
                            Err((CacheStoreError::TooLarge, fingerprint))
                        } else {
                            let mut pipeline = redis::pipe();

                            // Append storage command
                            {
                                let key_tag_masks = key_tags.iter()
                                    .map(|key_tag| key_tag.1.as_ref())
                                    .collect::<Vec<&str>>();

                                pipeline.hset_multiple(
                                    &key, &[
                                        (KEY_FINGERPRINT, &fingerprint),
                                        (KEY_TAGS, &key_tag_masks.join(KEY_TAGS_SEPARATOR)),
                                        (KEY_BODY, &value)
                                    ]
                                ).ignore();
                            }

                            pipeline.expire(&key, ttl_cap).ignore();

                            for key_tag in key_tags {
                                pipeline.sadd(&key_tag.0, &key_mask).ignore();
                                pipeline.expire(&key_tag.0, APP_CONF.redis.max_key_expiration);
                            }

                            // Bucket (MULTI operation for main data + bucket marker)
                            match pipeline.query::<()>(&*client) {
                                Ok(_) => Ok(fingerprint),
                                Err(err) => {
                                    error!("got store error: {}", err);

                                    Err((CacheStoreError::Failed, fingerprint))
                                }
                            }
                        }
                    }
                ))
        }))
    }

    pub fn purge_tag(
        &self,
        variant: &CachePurgeVariant,
        shard: u8,
        key_tag: &str,
    ) -> CachePurgeResult {
        get_cache_store_client!(self.pool, CacheStoreError::Disconnected, client {
            // Invoke keyspace cleanup script for key tag
            let result = redis::Script::new(variant.get_script())
                .arg(ROUTE_PREFIX)
                .arg(shard)
                .arg(key_tag)
                .invoke::<()>(&*client);

            result
                .and(Ok(()))
                .or(Err(CacheStoreError::Failed))
        })
    }
}

impl CachePurgeVariant {
    fn get_script(&self) -> &'static str {
        match *self {
            CachePurgeVariant::Bucket |
            CachePurgeVariant::Auth => {
                r#"
                    local targets = {}

                    for _, tag in pairs(redis.call('SMEMBERS', ARGV[3])) do
                        table.insert(targets, ARGV[1] .. ":" .. ARGV[2] .. ":c:" .. tag)
                    end

                    table.insert(targets, ARGV[3])

                    redis.call('DEL', unpack(targets))
                "#
            }
        }
    }
}
<commit_msg>Fix<commit_after>// Bloom
//
// HTTP REST API caching middleware
// Copyright: 2017, Valerian Saliou <valerian@valeriansaliou.name>
// License: Mozilla Public License v2.0 (MPL v2.0)

use std::cmp;
use std::time::Duration;
use r2d2::Pool;
use r2d2::config::Config;
use r2d2_redis::{RedisConnectionManager, Error};
use redis::{self, Value, Connection, Commands, PipelineCommands};
use futures::future::Future;
use futures_cpupool::CpuPool;

use super::route::ROUTE_PREFIX;
use APP_CONF;

static KEY_BODY: &'static str = "b";
static KEY_FINGERPRINT: &'static str = "f";
static KEY_TAGS: &'static str = "t";
static KEY_TAGS_SEPARATOR: &'static str = ",";

lazy_static! {
    pub static ref EXECUTOR_POOL: CpuPool = CpuPool::new(APP_CONF.cache.executor_pool as usize);
}

pub struct CacheStoreBuilder;

pub struct CacheStore {
    pool: Pool<RedisConnectionManager>,
}

#[derive(Debug)]
pub enum CacheStoreError {
    Disconnected,
    Failed,
    Invalid,
    Corrupted,
    TooLarge,
}

#[derive(Debug)]
pub enum CachePurgeVariant {
    Bucket,
    Auth,
}

type CacheReadResultFuture = Box<Future<Item = Option<String>, Error = CacheStoreError>>;
type CacheWriteResult = Result<(String), (CacheStoreError, String)>;
type CacheWriteResultFuture = Box<Future<Item = CacheWriteResult, Error = ()>>;
type CachePurgeResult = Result<(), CacheStoreError>;

impl CacheStoreBuilder {
    pub fn new() -> CacheStore {
        info!(
            "binding to store backend at {}:{}",
            APP_CONF.redis.host,
            APP_CONF.redis.port
        );

        let addr_auth = match APP_CONF.redis.password {
            Some(ref password) => format!(":{}@", password),
            None => "".to_string(),
        };

        let tcp_addr_raw =
            format!(
            "redis://{}{}:{}/{}",
            &addr_auth,
            APP_CONF.redis.host,
            APP_CONF.redis.port,
            APP_CONF.redis.database,
        );

        debug!("will connect to redis at: {}", tcp_addr_raw);

        match RedisConnectionManager::new(tcp_addr_raw.as_ref()) {
            Ok(manager) => {
                let config = Config::<Connection, Error>::builder()
                    .initialization_fail_fast(false)
                    .test_on_check_out(false)
                    .pool_size(APP_CONF.redis.pool_size)
                    .max_lifetime(Some(
                        Duration::from_secs(APP_CONF.redis.max_lifetime_seconds),
                    ))
                    .idle_timeout(Some(
                        Duration::from_secs(APP_CONF.redis.idle_timeout_seconds),
                    ))
                    .connection_timeout(Duration::from_secs(
                        APP_CONF.redis.connection_timeout_seconds,
                    ))
                    .build();

                match Pool::new(config, manager) {
                    Ok(pool) => {
                        info!("bound to store backend");

                        CacheStore { pool: pool }
                    }
                    Err(_) => panic!("could not spawn redis pool"),
                }
            }
            Err(_) => panic!("could not create redis connection manager"),
        }
    }
}

impl CacheStore {
    pub fn get_meta(&self, shard: u8, key: String) -> CacheReadResultFuture {
        let pool = self.pool.to_owned();

        Box::new(EXECUTOR_POOL.spawn_fn(move || {
            get_cache_store_client!(pool, CacheStoreError::Disconnected, client {
                    match (*client).hget::<_, _, (Value, Value)>(key, (KEY_FINGERPRINT, KEY_TAGS)) {
                        Ok(value) => {
                            match value {
                                (Value::Data(fingerprint_bytes), tags_bytes) => {
                                    // Parse tags and bump their last access time
                                    if let Value::Data(tags_bytes_data) = tags_bytes {
                                        if let Ok(tags_data) = String::from_utf8(
                                            tags_bytes_data) {
                                            if tags_data.is_empty() == false {
                                                let tags = tags_data.split(KEY_TAGS_SEPARATOR)
                                                    .map(|tag| {
                                                        format!(
                                                            "{}:{}:{}", ROUTE_PREFIX, shard, tag
                                                        )
                                                    })
                                                    .collect::<Vec<String>>();

                                                // Proceed a soft bump of last access time of \
                                                //   associated tag keys. This prevents a \
                                                //   frequently accessed cache namespace to \
                                                //   become 'orphan' (ie. one or more tag keys \
                                                //   are LRU-expired), and thus cache namespace \
                                                //   not to be properly removed on purge of an \
                                                //   associated tag.
                                                // Also, count bumped keys. It may happen that \
                                                //   some tag keys are incorrectly removed by \
                                                //   Redis LRU system, as it is probabilistic \
                                                //   and thus might sample some keys incorrectly.
                                                // The conditions explained above only happens on \
                                                //   Redis instances with used memory going over \
                                                //   the threshold of the max memory policy.
                                                let tags_count = tags.len();

                                                match redis::cmd("TOUCH").arg(tags)
                                                    .query::<usize>(&*client) {
                                                    Ok(bump_count) => {
                                                        // Partial bump count? Consider cache as \
                                                        //   non-existing
                                                        if bump_count < tags_count {
                                                            return Ok(None);
                                                        }
                                                    },
                                                    Err(err) => {
                                                        error!(
                                                            "error bumping access time of tags: {}",
                                                            err
                                                        );
                                                    }
                                                }
                                            }
                                        }
                                    }

                                    // Decode raw bytes to string
                                    if let Ok(fingerprint) = String::from_utf8(fingerprint_bytes) {
                                        Ok(Some(fingerprint))
                                    } else {
                                        Err(CacheStoreError::Corrupted)
                                    }
                                },
                                (Value::Nil, _) => Ok(None),
                                _ => Err(CacheStoreError::Invalid),
                            }
                        },
                        _ => Err(CacheStoreError::Failed),
                    }
                })
        }))
    }

    pub fn get_body(&self, key: String) -> CacheReadResultFuture {
        let pool = self.pool.to_owned();

        Box::new(EXECUTOR_POOL.spawn_fn(move || {
            get_cache_store_client!(pool, CacheStoreError::Disconnected, client {
                    match (*client).hget::<_, _, Value>(key, KEY_BODY) {
                        Ok(value) => {
                            match value {
                                Value::Data(body_bytes) => {
                                    // Decode raw bytes to string
                                    if let Ok(body) = String::from_utf8(body_bytes) {
                                        Ok(Some(body))
                                    } else {
                                        Err(CacheStoreError::Corrupted)
                                    }
                                },
                                Value::Nil => Ok(None),
                                _ => Err(CacheStoreError::Invalid),
                            }
                        },
                        _ => Err(CacheStoreError::Failed),
                    }
                })
        }))
    }

    pub fn set(
        &self,
        key: String,
        key_mask: String,
        value: String,
        fingerprint: String,
        ttl: usize,
        key_tags: Vec<(String, String)>,
    ) -> CacheWriteResultFuture {
        let pool = self.pool.to_owned();

        Box::new(EXECUTOR_POOL.spawn_fn(move || {
            Ok(get_cache_store_client!(
                    pool,
                    (CacheStoreError::Disconnected, fingerprint),

                    client {
                        // Cap TTL to 'max_key_expiration'
                        let ttl_cap = cmp::min(ttl, APP_CONF.redis.max_key_expiration);

                        // Ensure value is not larger than 'max_key_size'
                        if value.len() > APP_CONF.redis.max_key_size {
                            Err((CacheStoreError::TooLarge, fingerprint))
                        } else {
                            let mut pipeline = redis::pipe();

                            // Append storage command
                            {
                                let key_tag_masks = key_tags.iter()
                                    .map(|key_tag| key_tag.1.as_ref())
                                    .collect::<Vec<&str>>();

                                pipeline.hset_multiple(
                                    &key, &[
                                        (KEY_FINGERPRINT, &fingerprint),
                                        (KEY_TAGS, &key_tag_masks.join(KEY_TAGS_SEPARATOR)),
                                        (KEY_BODY, &value)
                                    ]
                                ).ignore();
                            }

                            pipeline.expire(&key, ttl_cap).ignore();

                            for key_tag in key_tags {
                                pipeline.sadd(&key_tag.0, &key_mask).ignore();
                                pipeline.expire(&key_tag.0, APP_CONF.redis.max_key_expiration);
                            }

                            // Bucket (MULTI operation for main data + bucket marker)
                            match pipeline.query::<()>(&*client) {
                                Ok(_) => Ok(fingerprint),
                                Err(err) => {
                                    error!("got store error: {}", err);

                                    Err((CacheStoreError::Failed, fingerprint))
                                }
                            }
                        }
                    }
                ))
        }))
    }

    pub fn purge_tag(
        &self,
        variant: &CachePurgeVariant,
        shard: u8,
        key_tag: &str,
    ) -> CachePurgeResult {
        get_cache_store_client!(self.pool, CacheStoreError::Disconnected, client {
            // Invoke keyspace cleanup script for key tag
            let result = redis::Script::new(variant.get_script())
                .arg(ROUTE_PREFIX)
                .arg(shard)
                .arg(key_tag)
                .invoke::<()>(&*client);

            result
                .and(Ok(()))
                .or(Err(CacheStoreError::Failed))
        })
    }
}

impl CachePurgeVariant {
    fn get_script(&self) -> &'static str {
        match *self {
            CachePurgeVariant::Bucket |
            CachePurgeVariant::Auth => {
                r#"
                    local targets = {}

                    for _, tag in pairs(redis.call('SMEMBERS', ARGV[3])) do
                        table.insert(targets, ARGV[1] .. ":" .. ARGV[2] .. ":c:" .. tag)
                    end

                    table.insert(targets, ARGV[3])

                    redis.call('DEL', unpack(targets))
                "#
            }
        }
    }
}
<|endoftext|>


<commit_before>use dora_asm::arm64::Cond;
use lazy_static::lazy_static;

use crate::masm::CondCode;

#[cfg(target_os = "macos")]
pub fn flush_icache(start: *const u8, len: usize) {
    extern "C" {
        fn sys_icache_invalidate(data: *const u8, len: usize);
    }

    unsafe {
        sys_icache_invalidate(start, len);
    }
}

#[cfg(not(target_os = "macos"))]
pub fn flush_icache(start: *const u8, len: usize) {
    let start = start as usize;
    let end = start + len;

    let (icacheline_size, dcacheline_size) = cacheline_sizes();

    let istart = start & !(icacheline_size - 1);
    let dstart = start & !(dcacheline_size - 1);

    let mut ptr = dstart;

    while ptr < end {
        unsafe {
            llvm_asm!("dc civac, $0":: "r"(ptr) : "memory" : "volatile");
        }

        ptr += dcacheline_size;
    }

    unsafe {
        llvm_asm!("dsb ish" ::: "memory" : "volatile");
    }

    ptr = istart;

    while ptr < end {
        unsafe {
            llvm_asm!("ic ivau, $0":: "r"(ptr) : "memory" : "volatile");
        }

        ptr += icacheline_size;
    }

    unsafe {
        llvm_asm!("dsb ish
              isb" ::: "memory" : "volatile");
    }
}

pub fn has_lse_atomics() -> bool {
    *HAS_LSE_ATOMICS
}

lazy_static! {
    static ref HAS_LSE_ATOMICS: bool = check_support_for_lse_atomics();
}

#[cfg(target_os = "macos")]
fn check_support_for_lse_atomics() -> bool {
    use std::ffi::CString;

    unsafe {
        let mut oldbuffer: usize = 0;
        let mut oldlenp: libc::size_t = 4;
        let name = CString::new("hw.optional.armv8_1_atomics").unwrap();
        let result = libc::sysctlbyname(
            name.as_ptr(),
            &mut oldbuffer as *mut usize as *mut libc::c_void,
            &mut oldlenp,
            std::ptr::null_mut(),
            0,
        );
        assert_eq!(result, 0);
        oldbuffer != 0
    }
}

#[cfg(not(target_os = "macos"))]
fn check_support_for_lse_atomics() -> bool {
    let value: usize;
    unsafe {
        llvm_asm!("mrs $0, ID_AA64DFR1_EL1": "=r"(value)::: "volatile");
    }
    (value >> 20) & 0xF == 0b0010
}

#[cfg(not(target_os = "macos"))]
pub fn cacheline_sizes() -> (usize, usize) {
    let value: usize;

    unsafe {
        llvm_asm!("mrs $0, ctr_el0": "=r"(value)::: "volatile");
    }

    let insn = 4 << (value & 0xF);
    let data = 4 << ((value >> 16) & 0xF);

    (insn, data)
}

pub fn has_round() -> bool {
    true
}

pub fn has_popcnt() -> bool {
    true
}

pub fn has_lzcnt() -> bool {
    true
}

pub fn has_tzcnt() -> bool {
    true
}

impl From<CondCode> for Cond {
    fn from(c: CondCode) -> Cond {
        match c {
            CondCode::Zero => Cond::EQ,
            CondCode::NonZero => Cond::NE,
            CondCode::Equal => Cond::EQ,
            CondCode::NotEqual => Cond::NE,
            CondCode::Greater => Cond::GT,
            CondCode::GreaterEq => Cond::GE,
            CondCode::Less => Cond::LT,
            CondCode::LessEq => Cond::LE,
            CondCode::UnsignedGreater => Cond::HI,
            CondCode::UnsignedGreaterEq => Cond::HS,
            CondCode::UnsignedLess => Cond::LO,
            CondCode::UnsignedLessEq => Cond::LS,
        }
    }
}

use crate::cpu::{FReg, Reg};
use crate::language::ty::SourceType;
use dora_asm::arm64 as asm;
use dora_asm::arm64::Register;

pub static REG_PARAMS: [Reg; 8] = [R0, R1, R2, R3, R4, R5, R6, R7];
pub static FREG_PARAMS: [FReg; 8] = [F0, F1, F2, F3, F4, F5, F6, F7];

pub static CCALL_REG_PARAMS: [Reg; 8] = [R0, R1, R2, R3, R4, R5, R6, R7];
pub static CCALL_FREG_PARAMS: [FReg; 8] = [F0, F1, F2, F3, F4, F5, F6, F7];

pub static SCRATCH: [Reg; 5] = [R9, R12, R13, R14, R15];

pub const REG_RESULT: Reg = R0;
pub const REG_TMP1: Reg = R10;
pub const REG_TMP2: Reg = R11;
pub const REG_FP: Reg = R29;
pub const REG_LR: Reg = R30;
pub const REG_THREAD: Reg = R28;

pub const REG_SP: Reg = Reg(32);
pub const REG_ZERO: Reg = Reg(33);

pub const FREG_RESULT: FReg = F0;

// shall not overlap with param registers
pub const FREG_TMP1: FReg = F16;

pub const STACK_FRAME_ALIGNMENT: usize = 16;

pub const R0: Reg = Reg(0);
pub const R1: Reg = Reg(1);
pub const R2: Reg = Reg(2);
pub const R3: Reg = Reg(3);
pub const R4: Reg = Reg(4);
pub const R5: Reg = Reg(5);
pub const R6: Reg = Reg(6);
pub const R7: Reg = Reg(7);
pub const R8: Reg = Reg(8);
pub const R9: Reg = Reg(9);
pub const R10: Reg = Reg(10);
pub const R11: Reg = Reg(11);
pub const R12: Reg = Reg(12);
pub const R13: Reg = Reg(13);
pub const R14: Reg = Reg(14);
pub const R15: Reg = Reg(15);
pub const R16: Reg = Reg(16);
pub const R17: Reg = Reg(17);
pub const R18: Reg = Reg(18);
pub const R19: Reg = Reg(19);
pub const R20: Reg = Reg(20);
pub const R21: Reg = Reg(21);
pub const R22: Reg = Reg(22);
pub const R23: Reg = Reg(23);
pub const R24: Reg = Reg(24);
pub const R25: Reg = Reg(25);
pub const R26: Reg = Reg(26);
pub const R27: Reg = Reg(27);
pub const R28: Reg = Reg(28);
pub const R29: Reg = Reg(29);
pub const R30: Reg = Reg(30);

pub const F0: FReg = FReg(0);
pub const F1: FReg = FReg(1);
pub const F2: FReg = FReg(2);
pub const F3: FReg = FReg(3);
pub const F4: FReg = FReg(4);
pub const F5: FReg = FReg(5);
pub const F6: FReg = FReg(6);
pub const F7: FReg = FReg(7);
pub const F8: FReg = FReg(8);
pub const F9: FReg = FReg(9);
pub const F10: FReg = FReg(10);
pub const F11: FReg = FReg(11);
pub const F12: FReg = FReg(12);
pub const F13: FReg = FReg(13);
pub const F14: FReg = FReg(14);
pub const F15: FReg = FReg(15);
pub const F16: FReg = FReg(16);
pub const F17: FReg = FReg(17);
pub const F18: FReg = FReg(18);
pub const F19: FReg = FReg(19);
pub const F20: FReg = FReg(20);
pub const F21: FReg = FReg(21);
pub const F22: FReg = FReg(22);
pub const F23: FReg = FReg(23);
pub const F24: FReg = FReg(24);
pub const F25: FReg = FReg(25);
pub const F26: FReg = FReg(26);
pub const F27: FReg = FReg(27);
pub const F28: FReg = FReg(28);
pub const F29: FReg = FReg(29);
pub const F30: FReg = FReg(30);
pub const F31: FReg = FReg(31);

impl Reg {
    pub fn asm(self) -> u32 {
        match self {
            REG_SP => 31,
            REG_ZERO => 31,
            _ => self.0 as u32,
        }
    }

    pub fn is_gpr(self) -> bool {
        self.0 <= 30
    }

    pub fn is_gpr_or_zero(self) -> bool {
        self.is_gpr() || self == REG_ZERO
    }

    pub fn is_gpr_or_sp(self) -> bool {
        self.is_gpr() || self == REG_SP
    }
}

impl From<Reg> for Register {
    fn from(reg: Reg) -> Register {
        if reg.0 < 31 {
            Register::new(reg.0)
        } else if reg == REG_ZERO {
            asm::REG_ZERO
        } else {
            assert_eq!(reg, REG_SP);
            asm::REG_SP
        }
    }
}

impl FReg {
    pub fn asm(self) -> u32 {
        assert!(self.0 < 32);
        self.0 as u32
    }
}

pub static PARAM_OFFSET: i32 = 16;

pub fn next_param_offset(param_offset: i32, _: SourceType) -> i32 {
    param_offset + 8
}
<commit_msg>arm64: change to asm! macro<commit_after>use dora_asm::arm64::Cond;
use lazy_static::lazy_static;

use crate::masm::CondCode;

#[cfg(target_os = "macos")]
pub fn flush_icache(start: *const u8, len: usize) {
    extern "C" {
        fn sys_icache_invalidate(data: *const u8, len: usize);
    }

    unsafe {
        sys_icache_invalidate(start, len);
    }
}

#[cfg(not(target_os = "macos"))]
pub fn flush_icache(start: *const u8, len: usize) {
    let start = start as usize;
    let end = start + len;

    let (icacheline_size, dcacheline_size) = cacheline_sizes();

    let istart = start & !(icacheline_size - 1);
    let dstart = start & !(dcacheline_size - 1);

    let mut ptr = dstart;

    while ptr < end {
        unsafe {
            asm!("dc civac, $0":: "r"(ptr) : "memory" : "volatile");
        }

        ptr += dcacheline_size;
    }

    unsafe {
        asm!("dsb ish" ::: "memory" : "volatile");
    }

    ptr = istart;

    while ptr < end {
        unsafe {
            asm!("ic ivau, $0":: "r"(ptr) : "memory" : "volatile");
        }

        ptr += icacheline_size;
    }

    unsafe {
        asm!("dsb ish
              isb" ::: "memory" : "volatile");
    }
}

pub fn has_lse_atomics() -> bool {
    *HAS_LSE_ATOMICS
}

lazy_static! {
    static ref HAS_LSE_ATOMICS: bool = check_support_for_lse_atomics();
}

#[cfg(target_os = "macos")]
fn check_support_for_lse_atomics() -> bool {
    use std::ffi::CString;

    unsafe {
        let mut oldbuffer: usize = 0;
        let mut oldlenp: libc::size_t = 4;
        let name = CString::new("hw.optional.armv8_1_atomics").unwrap();
        let result = libc::sysctlbyname(
            name.as_ptr(),
            &mut oldbuffer as *mut usize as *mut libc::c_void,
            &mut oldlenp,
            std::ptr::null_mut(),
            0,
        );
        assert_eq!(result, 0);
        oldbuffer != 0
    }
}

#[cfg(not(target_os = "macos"))]
fn check_support_for_lse_atomics() -> bool {
    let value: usize;
    unsafe {
        asm!("mrs $0, ID_AA64DFR1_EL1": "=r"(value)::: "volatile");
    }
    (value >> 20) & 0xF == 0b0010
}

#[cfg(not(target_os = "macos"))]
pub fn cacheline_sizes() -> (usize, usize) {
    let value: usize;

    unsafe {
        asm!("mrs $0, ctr_el0": "=r"(value)::: "volatile");
    }

    let insn = 4 << (value & 0xF);
    let data = 4 << ((value >> 16) & 0xF);

    (insn, data)
}

pub fn has_round() -> bool {
    true
}

pub fn has_popcnt() -> bool {
    true
}

pub fn has_lzcnt() -> bool {
    true
}

pub fn has_tzcnt() -> bool {
    true
}

impl From<CondCode> for Cond {
    fn from(c: CondCode) -> Cond {
        match c {
            CondCode::Zero => Cond::EQ,
            CondCode::NonZero => Cond::NE,
            CondCode::Equal => Cond::EQ,
            CondCode::NotEqual => Cond::NE,
            CondCode::Greater => Cond::GT,
            CondCode::GreaterEq => Cond::GE,
            CondCode::Less => Cond::LT,
            CondCode::LessEq => Cond::LE,
            CondCode::UnsignedGreater => Cond::HI,
            CondCode::UnsignedGreaterEq => Cond::HS,
            CondCode::UnsignedLess => Cond::LO,
            CondCode::UnsignedLessEq => Cond::LS,
        }
    }
}

use crate::cpu::{FReg, Reg};
use crate::language::ty::SourceType;
use dora_asm::arm64 as asm;
use dora_asm::arm64::Register;

pub static REG_PARAMS: [Reg; 8] = [R0, R1, R2, R3, R4, R5, R6, R7];
pub static FREG_PARAMS: [FReg; 8] = [F0, F1, F2, F3, F4, F5, F6, F7];

pub static CCALL_REG_PARAMS: [Reg; 8] = [R0, R1, R2, R3, R4, R5, R6, R7];
pub static CCALL_FREG_PARAMS: [FReg; 8] = [F0, F1, F2, F3, F4, F5, F6, F7];

pub static SCRATCH: [Reg; 5] = [R9, R12, R13, R14, R15];

pub const REG_RESULT: Reg = R0;
pub const REG_TMP1: Reg = R10;
pub const REG_TMP2: Reg = R11;
pub const REG_FP: Reg = R29;
pub const REG_LR: Reg = R30;
pub const REG_THREAD: Reg = R28;

pub const REG_SP: Reg = Reg(32);
pub const REG_ZERO: Reg = Reg(33);

pub const FREG_RESULT: FReg = F0;

// shall not overlap with param registers
pub const FREG_TMP1: FReg = F16;

pub const STACK_FRAME_ALIGNMENT: usize = 16;

pub const R0: Reg = Reg(0);
pub const R1: Reg = Reg(1);
pub const R2: Reg = Reg(2);
pub const R3: Reg = Reg(3);
pub const R4: Reg = Reg(4);
pub const R5: Reg = Reg(5);
pub const R6: Reg = Reg(6);
pub const R7: Reg = Reg(7);
pub const R8: Reg = Reg(8);
pub const R9: Reg = Reg(9);
pub const R10: Reg = Reg(10);
pub const R11: Reg = Reg(11);
pub const R12: Reg = Reg(12);
pub const R13: Reg = Reg(13);
pub const R14: Reg = Reg(14);
pub const R15: Reg = Reg(15);
pub const R16: Reg = Reg(16);
pub const R17: Reg = Reg(17);
pub const R18: Reg = Reg(18);
pub const R19: Reg = Reg(19);
pub const R20: Reg = Reg(20);
pub const R21: Reg = Reg(21);
pub const R22: Reg = Reg(22);
pub const R23: Reg = Reg(23);
pub const R24: Reg = Reg(24);
pub const R25: Reg = Reg(25);
pub const R26: Reg = Reg(26);
pub const R27: Reg = Reg(27);
pub const R28: Reg = Reg(28);
pub const R29: Reg = Reg(29);
pub const R30: Reg = Reg(30);

pub const F0: FReg = FReg(0);
pub const F1: FReg = FReg(1);
pub const F2: FReg = FReg(2);
pub const F3: FReg = FReg(3);
pub const F4: FReg = FReg(4);
pub const F5: FReg = FReg(5);
pub const F6: FReg = FReg(6);
pub const F7: FReg = FReg(7);
pub const F8: FReg = FReg(8);
pub const F9: FReg = FReg(9);
pub const F10: FReg = FReg(10);
pub const F11: FReg = FReg(11);
pub const F12: FReg = FReg(12);
pub const F13: FReg = FReg(13);
pub const F14: FReg = FReg(14);
pub const F15: FReg = FReg(15);
pub const F16: FReg = FReg(16);
pub const F17: FReg = FReg(17);
pub const F18: FReg = FReg(18);
pub const F19: FReg = FReg(19);
pub const F20: FReg = FReg(20);
pub const F21: FReg = FReg(21);
pub const F22: FReg = FReg(22);
pub const F23: FReg = FReg(23);
pub const F24: FReg = FReg(24);
pub const F25: FReg = FReg(25);
pub const F26: FReg = FReg(26);
pub const F27: FReg = FReg(27);
pub const F28: FReg = FReg(28);
pub const F29: FReg = FReg(29);
pub const F30: FReg = FReg(30);
pub const F31: FReg = FReg(31);

impl Reg {
    pub fn asm(self) -> u32 {
        match self {
            REG_SP => 31,
            REG_ZERO => 31,
            _ => self.0 as u32,
        }
    }

    pub fn is_gpr(self) -> bool {
        self.0 <= 30
    }

    pub fn is_gpr_or_zero(self) -> bool {
        self.is_gpr() || self == REG_ZERO
    }

    pub fn is_gpr_or_sp(self) -> bool {
        self.is_gpr() || self == REG_SP
    }
}

impl From<Reg> for Register {
    fn from(reg: Reg) -> Register {
        if reg.0 < 31 {
            Register::new(reg.0)
        } else if reg == REG_ZERO {
            asm::REG_ZERO
        } else {
            assert_eq!(reg, REG_SP);
            asm::REG_SP
        }
    }
}

impl FReg {
    pub fn asm(self) -> u32 {
        assert!(self.0 < 32);
        self.0 as u32
    }
}

pub static PARAM_OFFSET: i32 = 16;

pub fn next_param_offset(param_offset: i32, _: SourceType) -> i32 {
    param_offset + 8
}
<|endoftext|>


<commit_before>use super::*;
use core::iter::*;

#[test]
fn test_iterator_flatten() {
    let xs = [0, 3, 6];
    let ys = [0, 1, 2, 3, 4, 5, 6, 7, 8];
    let it = xs.iter().map(|&x| (x..).step_by(1).take(3)).flatten();
    let mut i = 0;
    for x in it {
        assert_eq!(x, ys[i]);
        i += 1;
    }
    assert_eq!(i, ys.len());
}

/// Tests `Flatten::fold` with items already picked off the front and back,
/// to make sure all parts of the `Flatten` are folded correctly.
#[test]
fn test_iterator_flatten_fold() {
    let xs = [0, 3, 6];
    let ys = [1, 2, 3, 4, 5, 6, 7];
    let mut it = xs.iter().map(|&x| x..x + 3).flatten();
    assert_eq!(it.next(), Some(0));
    assert_eq!(it.next_back(), Some(8));
    let i = it.fold(0, |i, x| {
        assert_eq!(x, ys[i]);
        i + 1
    });
    assert_eq!(i, ys.len());

    let mut it = xs.iter().map(|&x| x..x + 3).flatten();
    assert_eq!(it.next(), Some(0));
    assert_eq!(it.next_back(), Some(8));
    let i = it.rfold(ys.len(), |i, x| {
        assert_eq!(x, ys[i - 1]);
        i - 1
    });
    assert_eq!(i, 0);
}

#[test]
fn test_flatten_try_folds() {
    let f = &|acc, x| i32::checked_add(acc * 2 / 3, x);
    let mr = &|x| (5 * x)..(5 * x + 5);
    assert_eq!((0..10).map(mr).flatten().try_fold(7, f), (0..50).try_fold(7, f));
    assert_eq!((0..10).map(mr).flatten().try_rfold(7, f), (0..50).try_rfold(7, f));
    let mut iter = (0..10).map(mr).flatten();
    iter.next();
    iter.next_back(); // have front and back iters in progress
    assert_eq!(iter.try_rfold(7, f), (1..49).try_rfold(7, f));

    let mut iter = (0..10).map(|x| (4 * x)..(4 * x + 4)).flatten();
    assert_eq!(iter.try_fold(0, i8::checked_add), None);
    assert_eq!(iter.next(), Some(17));
    assert_eq!(iter.try_rfold(0, i8::checked_add), None);
    assert_eq!(iter.next_back(), Some(35));
}

#[test]
fn test_flatten_non_fused_outer() {
    let mut iter = NonFused::new(once(0..2)).flatten();

    assert_eq!(iter.next_back(), Some(1));
    assert_eq!(iter.next(), Some(0));
    assert_eq!(iter.next(), None);
}

#[test]
fn test_flatten_non_fused_inner() {
    let mut iter = once(0..1).chain(once(1..3)).flat_map(NonFused::new);

    assert_eq!(iter.next_back(), Some(2));
    assert_eq!(iter.next(), Some(0));
    assert_eq!(iter.next(), Some(1));
    assert_eq!(iter.next(), None);
}

#[test]
fn test_double_ended_flatten() {
    let u = [0, 1];
    let v = [5, 6, 7, 8];
    let mut it = u.iter().map(|x| &v[*x..v.len()]).flatten();
    assert_eq!(it.next_back().unwrap(), &8);
    assert_eq!(it.next().unwrap(), &5);
    assert_eq!(it.next_back().unwrap(), &7);
    assert_eq!(it.next_back().unwrap(), &6);
    assert_eq!(it.next_back().unwrap(), &8);
    assert_eq!(it.next().unwrap(), &6);
    assert_eq!(it.next_back().unwrap(), &7);
    assert_eq!(it.next_back(), None);
    assert_eq!(it.next(), None);
    assert_eq!(it.next_back(), None);
}
<commit_msg>Improve flatten-fuse tests<commit_after>use super::*;
use core::iter::*;

#[test]
fn test_iterator_flatten() {
    let xs = [0, 3, 6];
    let ys = [0, 1, 2, 3, 4, 5, 6, 7, 8];
    let it = xs.iter().map(|&x| (x..).step_by(1).take(3)).flatten();
    let mut i = 0;
    for x in it {
        assert_eq!(x, ys[i]);
        i += 1;
    }
    assert_eq!(i, ys.len());
}

/// Tests `Flatten::fold` with items already picked off the front and back,
/// to make sure all parts of the `Flatten` are folded correctly.
#[test]
fn test_iterator_flatten_fold() {
    let xs = [0, 3, 6];
    let ys = [1, 2, 3, 4, 5, 6, 7];
    let mut it = xs.iter().map(|&x| x..x + 3).flatten();
    assert_eq!(it.next(), Some(0));
    assert_eq!(it.next_back(), Some(8));
    let i = it.fold(0, |i, x| {
        assert_eq!(x, ys[i]);
        i + 1
    });
    assert_eq!(i, ys.len());

    let mut it = xs.iter().map(|&x| x..x + 3).flatten();
    assert_eq!(it.next(), Some(0));
    assert_eq!(it.next_back(), Some(8));
    let i = it.rfold(ys.len(), |i, x| {
        assert_eq!(x, ys[i - 1]);
        i - 1
    });
    assert_eq!(i, 0);
}

#[test]
fn test_flatten_try_folds() {
    let f = &|acc, x| i32::checked_add(acc * 2 / 3, x);
    let mr = &|x| (5 * x)..(5 * x + 5);
    assert_eq!((0..10).map(mr).flatten().try_fold(7, f), (0..50).try_fold(7, f));
    assert_eq!((0..10).map(mr).flatten().try_rfold(7, f), (0..50).try_rfold(7, f));
    let mut iter = (0..10).map(mr).flatten();
    iter.next();
    iter.next_back(); // have front and back iters in progress
    assert_eq!(iter.try_rfold(7, f), (1..49).try_rfold(7, f));

    let mut iter = (0..10).map(|x| (4 * x)..(4 * x + 4)).flatten();
    assert_eq!(iter.try_fold(0, i8::checked_add), None);
    assert_eq!(iter.next(), Some(17));
    assert_eq!(iter.try_rfold(0, i8::checked_add), None);
    assert_eq!(iter.next_back(), Some(35));
}

#[test]
fn test_flatten_non_fused_outer() {
    let mut iter = NonFused::new(once(0..2)).flatten();

    assert_eq!(iter.next_back(), Some(1));
    assert_eq!(iter.next(), Some(0));
    assert_eq!(iter.next(), None);
    assert_eq!(iter.next(), None);

    let mut iter = NonFused::new(once(0..2)).flatten();

    assert_eq!(iter.next(), Some(0));
    assert_eq!(iter.next_back(), Some(1));
    assert_eq!(iter.next_back(), None);
    assert_eq!(iter.next_back(), None);
}

#[test]
fn test_flatten_non_fused_inner() {
    let mut iter = once(0..1).chain(once(1..3)).flat_map(NonFused::new);

    assert_eq!(iter.next_back(), Some(2));
    assert_eq!(iter.next(), Some(0));
    assert_eq!(iter.next(), Some(1));
    assert_eq!(iter.next(), None);
    assert_eq!(iter.next(), None);

    let mut iter = once(0..1).chain(once(1..3)).flat_map(NonFused::new);

    assert_eq!(iter.next(), Some(0));
    assert_eq!(iter.next_back(), Some(2));
    assert_eq!(iter.next_back(), Some(1));
    assert_eq!(iter.next_back(), None);
    assert_eq!(iter.next_back(), None);
}

#[test]
fn test_double_ended_flatten() {
    let u = [0, 1];
    let v = [5, 6, 7, 8];
    let mut it = u.iter().map(|x| &v[*x..v.len()]).flatten();
    assert_eq!(it.next_back().unwrap(), &8);
    assert_eq!(it.next().unwrap(), &5);
    assert_eq!(it.next_back().unwrap(), &7);
    assert_eq!(it.next_back().unwrap(), &6);
    assert_eq!(it.next_back().unwrap(), &8);
    assert_eq!(it.next().unwrap(), &6);
    assert_eq!(it.next_back().unwrap(), &7);
    assert_eq!(it.next_back(), None);
    assert_eq!(it.next(), None);
    assert_eq!(it.next_back(), None);
}
<|endoftext|>


<commit_before>extern crate cairo;
extern crate gio;
extern crate glib;
extern crate librsvg;

use gio::MemoryInputStreamExt;
use glib::Cast;

use librsvg::{
    CairoRenderer,
    DefsLookupErrorKind,
    HrefError,
    IntrinsicDimensions,
    Length,
    LengthUnit,
    Loader,
    RenderingError,
    SvgHandle,
};

fn load_svg(input: &'static [u8]) -> SvgHandle {
    let stream = gio::MemoryInputStream::new();
    stream.add_bytes(&glib::Bytes::from_static(input));

    Loader::new()
        .read_stream(&stream.upcast(), None, None)
        .unwrap()
}

#[test]
fn no_intrinsic_dimensions() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"/>
"#,
    );

    assert_eq!(
        CairoRenderer::new(&svg).intrinsic_dimensions(),
        IntrinsicDimensions {
            width: None,
            height: None,
            vbox: None,
        }
    );
}

#[test]
fn has_intrinsic_dimensions() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="10cm" height="20" viewBox="0 0 100 200"/>
"#,
    );

    assert_eq!(
        CairoRenderer::new(&svg).intrinsic_dimensions(),
        IntrinsicDimensions {
            width: Some(Length::new(10.0, LengthUnit::Cm)),
            height: Some(Length::new(20.0, LengthUnit::Px)),
            vbox: Some(cairo::Rectangle {
                x: 0.0,
                y: 0.0,
                width: 100.0,
                height: 200.0,
            }),
        }
    );
}

#[test]
fn root_geometry_with_percent_viewport() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
  <rect x="10" y="20" width="30" height="40"/>
</svg>
"#,
    );

    let renderer = CairoRenderer::new(&svg);
    let (ink_r, logical_r) = renderer.geometry_for_element(None).unwrap();

    let rect = cairo::Rectangle {
        x: 10.0,
        y: 20.0,
        width: 30.0,
        height: 40.0,
    };

    assert_eq!((ink_r, logical_r), (rect, rect));
}

#[test]
fn element_geometry_with_percent_viewport() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
  <rect id="foo" x="10" y="20" width="30" height="40"/>
</svg>
"#,
    );

    let renderer = CairoRenderer::new(&svg);
    let (ink_r, logical_r) = renderer.geometry_for_element(Some("#foo")).unwrap();

    let rect = cairo::Rectangle {
        x: 10.0,
        y: 20.0,
        width: 30.0,
        height: 40.0,
    };

    assert_eq!((ink_r, logical_r), (rect, rect));
}

#[test]
fn element_geometry_for_nonexistent_element() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"/>
"#,
    );

    let renderer = CairoRenderer::new(&svg);
    match renderer.geometry_for_element(Some("#foo")) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::NotFound)) => (),
        _ => panic!(),
    }
}

#[test]
fn element_geometry_for_invalid_id() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"/>
"#,
    );

    let renderer = CairoRenderer::new(&svg);
    match renderer.geometry_for_element(Some("foo")) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::CannotLookupExternalReferences)) => (),
        _ => panic!(),
    }

    match renderer.geometry_for_element(Some("foo.svg#foo")) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::CannotLookupExternalReferences)) => (),
        _ => panic!(),
    }

    match renderer.geometry_for_element(Some("")) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::HrefError(HrefError::ParseError))) => (),
        _ => panic!(),
    }
}
<commit_msg>Add test for extracting dimensions of an element when the SVG is fully specified<commit_after>extern crate cairo;
extern crate gio;
extern crate glib;
extern crate librsvg;

use gio::MemoryInputStreamExt;
use glib::Cast;

use librsvg::{
    CairoRenderer,
    DefsLookupErrorKind,
    HrefError,
    IntrinsicDimensions,
    Length,
    LengthUnit,
    Loader,
    RenderingError,
    SvgHandle,
};

fn load_svg(input: &'static [u8]) -> SvgHandle {
    let stream = gio::MemoryInputStream::new();
    stream.add_bytes(&glib::Bytes::from_static(input));

    Loader::new()
        .read_stream(&stream.upcast(), None, None)
        .unwrap()
}

#[test]
fn no_intrinsic_dimensions() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"/>
"#,
    );

    assert_eq!(
        CairoRenderer::new(&svg).intrinsic_dimensions(),
        IntrinsicDimensions {
            width: None,
            height: None,
            vbox: None,
        }
    );
}

#[test]
fn has_intrinsic_dimensions() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="10cm" height="20" viewBox="0 0 100 200"/>
"#,
    );

    assert_eq!(
        CairoRenderer::new(&svg).intrinsic_dimensions(),
        IntrinsicDimensions {
            width: Some(Length::new(10.0, LengthUnit::Cm)),
            height: Some(Length::new(20.0, LengthUnit::Px)),
            vbox: Some(cairo::Rectangle {
                x: 0.0,
                y: 0.0,
                width: 100.0,
                height: 200.0,
            }),
        }
    );
}

#[test]
fn root_geometry_with_percent_viewport() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
  <rect x="10" y="20" width="30" height="40"/>
</svg>
"#,
    );

    let renderer = CairoRenderer::new(&svg);
    let (ink_r, logical_r) = renderer.geometry_for_element(None).unwrap();

    let rect = cairo::Rectangle {
        x: 10.0,
        y: 20.0,
        width: 30.0,
        height: 40.0,
    };

    assert_eq!((ink_r, logical_r), (rect, rect));
}

#[test]
fn element_geometry_with_percent_viewport() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
  <rect id="foo" x="10" y="20" width="30" height="40"/>
</svg>
"#,
    );

    let renderer = CairoRenderer::new(&svg);
    let (ink_r, logical_r) = renderer.geometry_for_element(Some("#foo")).unwrap();

    let rect = cairo::Rectangle {
        x: 10.0,
        y: 20.0,
        width: 30.0,
        height: 40.0,
    };

    assert_eq!((ink_r, logical_r), (rect, rect));
}

#[test]
fn element_geometry_viewport_viewbox() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="400" viewBox="0 0 100 400">
  <rect id="one" x="0" y="0" width="100" height="200" fill="rgb(0,255,0)"/>
  <rect id="two" x="0" y="200" width="100" height="200" fill="rgb(0,0,255)"/>
</svg>
"#,
    );

    let renderer = CairoRenderer::new(&svg);
    let (ink_r, logical_r) = renderer.geometry_for_element(Some("#two")).unwrap();

    let rect = cairo::Rectangle {
        x: 0.0,
        y: 200.0,
        width: 100.0,
        height: 200.0,
    };

    assert_eq!((ink_r, logical_r), (rect, rect));
}

#[test]
fn element_geometry_for_nonexistent_element() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"/>
"#,
    );

    let renderer = CairoRenderer::new(&svg);
    match renderer.geometry_for_element(Some("#foo")) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::NotFound)) => (),
        _ => panic!(),
    }
}

#[test]
fn element_geometry_for_invalid_id() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"/>
"#,
    );

    let renderer = CairoRenderer::new(&svg);
    match renderer.geometry_for_element(Some("foo")) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::CannotLookupExternalReferences)) => (),
        _ => panic!(),
    }

    match renderer.geometry_for_element(Some("foo.svg#foo")) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::CannotLookupExternalReferences)) => (),
        _ => panic!(),
    }

    match renderer.geometry_for_element(Some("")) {
        Err(RenderingError::InvalidId(DefsLookupErrorKind::HrefError(HrefError::ParseError))) => (),
        _ => panic!(),
    }
}
<|endoftext|>