<commit_before>/**
Katy Williamson kcw4sn
Ben Terner bt3ze
@virginia.edu

Final Project for CS4414 - Operating Systems
A parallel MST implementation on images in Rust

*/

/*
some documentation:
Pixel object is composed of r,g,b values, a color, and an x,y index that takes the place of its adjacency list
Point is just an (x,y) value to denote a pixel's location
Edge contains two points: source and dest, along with the edge cost between them

*/

extern mod extra;
use std::{os, num,hashmap} ;
use extra::{arc,priority_queue};

struct Pixel{
    r: int,
    b: int,
    g: int,
    color: int,
    x: int,
    y: int
}

impl Pixel {
    fn new(red: int, blue: int, green: int, x: int, y: int) -> Pixel {
        Pixel{
            r: red,
            b: blue,
            g: green,
            color: -1,
            x: x,
            y: y
        }
    }
}

impl Clone for Pixel{
    fn clone(&self) -> Pixel {
        Pixel{
            r: self.r,
            b: self.b,
            g: self.g,
            color: self.color,
            x: self.x,
            y: self.y
        }
    }
}

struct Point {
    x: int,
    y: int
}

impl Point{
    fn new(x: int, y: int)-> Point {
        Point {
            x: x,
            y: y
        }
    }
}

struct Edge {
    source: Point,
    dest: Point,
    cost: float
}

impl Edge {
    fn new(s: Point, d: Point, cost: float) -> Edge {
        Edge {
            source: s,
            dest: d,
            cost: cost
        }
    }
}

impl Ord for Edge {
    fn lt(&self, other: &Edge) -> bool {
        self.cost < other.cost
    }
}

fn edgeCost(a: &Pixel, b: &Pixel) -> float {
    num::sqrt( (
            a.r*a.r-b.r*b.r + a.g*a.g-b.g*b.g + a.b*a.b-b.b*b.b ) as float)
}

fn readImage( filename: &~str) -> ~ [ ~[Pixel]] {
    ~[~[]]
}

fn main(){
    let argv =  os::args();
    println(argv.to_str());

    let pixels: ~[ ~[Pixel] ] = readImage(&argv[1]);

    // width and height should be replaced by just reading them from the file
    let width = match from_str::<int>(argv[2]) {
        Some(x) => x,
        None() => 0
    };
    let height = match from_str::<int>(argv[3]) {
        Some(x) => x,
        None() => 0
    };

    let mut find_arcs: ~[ ~[arc::RWArc<Pixel>] ] =  ~[ ~[]];
    let mut heit = 0;
    let mut wid = 0;
    loop {
        loop {
            find_arcs[heit][wid] = arc::RWArc::new(pixels[heit][wid]);
                wid+=1;
            if(wid == width) {
                wid = 0;
                break;
            }
        }
        heit+=1;
        if(heit == height) {
            break;
        }
    }

    let arcs = find_arcs; // now we have an immutable array that can be shared

    let corners = [ (0,0),(0,width-1),(height-1,0),(height-1,width-1) ];

    for &corner in corners.iter() {
        let (a,b) = corner;
        let shared_arcs = arcs.clone();
        do spawn { // split into threads here

            let mut x=a;
            let mut y=b;
            /*
            weird compiler error:
            let corners = [ (0,0),(0,width-1),(height-1,0),(height-1,width-1) ];
            for &corner in corners.iter(){
            do spawn {
            let mut x = 0;
            let mut y = 0;
            (x,y) = corner
                 }
             }
             */

            let mut queue: priority_queue::PriorityQueue<Edge> =  priority_queue::PriorityQueue::new();
            let mut visited: hashmap::HashMap<(int,int),bool> = hashmap::HashMap::new();

            let mut newvisit: bool = true;

            loop { // iterate through items in the queue, which contains all of the edges/vertices
                if(newvisit){
                    let neighbors = [ (x,y-1),(x+1,y), (x,y+1), (x,y-1)];
                    for &coord in neighbors.iter() {
                        let (w,z) = coord;
                        if w >=0 && w < width && z >=0 && z < height { // bounds checking
                            // if neighbor at coord has not been or colored
                            let mut newvertex = false;
                            let mut uncolored = false;
                            do shared_arcs[w][z].read |dest| {
                                if dest.color < 0 {
                                    uncolored = true; }
                            }
                            if uncolored {
                                do shared_arcs[w][z].write |dest| {
                                    if dest.color < 0 {
                                        do shared_arcs[x][y].read |src| {
                                            dest.color = src.color;
                                        }
                                        newvertex = true;
                                    }
                                }
                            }
                            if newvertex { // then we have a new vertex
                                do shared_arcs[x][y].read |src| {
                                    do shared_arcs[w][z].read |dest| {
                                        queue.push( Edge::new(Point::new(x,y),Point::new(w,z),edgeCost(src,dest)));
                                    }
                                }
                            }
                        }
                    }
                }

                let edge = queue.maybe_pop();
                match edge {
                Some(e) => {
                        let mut col = 0;
                        do shared_arcs[e.dest.x][e.dest.y].read |dest| {
                        col = dest.color;
                        }
                        if col < 0 {
                            // not in any tree
                            let coord = (e.dest.x,e.dest.y);
                            if *visited.get(&coord) == false { // use visited hashmap to figure out if we're at a new vertex
                                visited.insert(coord,true);
                                x = e.dest.x;
                                y = e.dest.y;
                                newvisit = true;
                            } else {
                                newvisit = false;
                            }
                        }
                    }
                    None => { break; } // this should execute at the end when there are no more unvisited or uncolored nodes for a thread
                }
            }
        }
    }
}
<commit_msg>wrapping up.<commit_after>/**
Katy Williamson kcw4sn
Ben Terner bt3ze
@virginia.edu

Final Project for CS4414 - Operating Systems
A parallel MST implementation on images in Rust

*/

/*
some documentation:
Pixel object is composed of r,g,b values, a color, and an x,y index that takes the place of its adjacency list
Point is just an (x,y) value to denote a pixel's location
Edge contains two points: source and dest, along with the edge cost between them

*/

extern mod extra;
use std::{os, num,hashmap} ;
use extra::{arc,priority_queue};

struct Pixel{
    r: int,
    b: int,
    g: int,
    color: int,
    x: int,
    y: int
}

impl Pixel {
    fn new(red: int, blue: int, green: int, x: int, y: int) -> Pixel {
        Pixel{
            r: red,
            b: blue,
            g: green,
            color: -1,
            x: x,
            y: y
        }
    }
}

impl Clone for Pixel{
    fn clone(&self) -> Pixel {
        Pixel{
            r: self.r,
            b: self.b,
            g: self.g,
            color: self.color,
            x: self.x,
            y: self.y
        }
    }
}

struct Point {
    x: int,
    y: int
}

impl Point{
    fn new(x: int, y: int)-> Point {
        Point {
            x: x,
            y: y
        }
    }
}

struct Edge {
    source: Point,
    dest: Point,
    cost: float
}

impl Edge {
    fn new(s: Point, d: Point, cost: float) -> Edge {
        Edge {
            source: s,
            dest: d,
            cost: cost
        }
    }
}

impl Ord for Edge {
    fn lt(&self, other: &Edge) -> bool {
        self.cost < other.cost
    }
}

fn edgeCost(a: &Pixel, b: &Pixel) -> float {
    num::sqrt( (
            a.r*a.r-b.r*b.r + a.g*a.g-b.g*b.g + a.b*a.b-b.b*b.b ) as float)
}

fn readImage( filename: &~str) -> ~ [ ~[Pixel]] {
    ~[~[]]
}

fn main(){
    let argv =  os::args();
    println(argv.to_str());

    let pixels: ~[ ~[Pixel] ] = readImage(&argv[1]);

    // width and height should be replaced by just reading them from the file
    let width = match from_str::<int>(argv[2]) {
        Some(x) => x,
        None() => 0
    };
    let height = match from_str::<int>(argv[3]) {
        Some(x) => x,
        None() => 0
    };

    let mut find_arcs: ~[ ~[arc::RWArc<Pixel>] ] =  ~[ ~[]];
    let mut heit = 0;
    let mut wid = 0;
    loop {
        loop {
            find_arcs[heit][wid] = arc::RWArc::new(pixels[heit][wid]);
                wid+=1;
            if(wid == width) {
                wid = 0;
                break;
            }
        }
        heit+=1;
        if(heit == height) {
            break;
        }
    }

    let arcs = find_arcs; // now we have an immutable array that can be shared

    let corners = [ (0,0),(0,width-1),(height-1,0),(height-1,width-1) ];

    for &corner in corners.iter() {
        let (a,b) = corner;
        let shared_arcs = arcs.clone();
        do spawn { // split into threads here

            let mut x=a;
            let mut y=b;
            /*
            weird compiler error:
            let corners = [ (0,0),(0,width-1),(height-1,0),(height-1,width-1) ];
            for &corner in corners.iter(){
            do spawn {
            let mut x = 0;
            let mut y = 0;
            (x,y) = corner
                 }
             }
             */

            let mut queue: priority_queue::PriorityQueue<Edge> =  priority_queue::PriorityQueue::new();
            let mut visited: hashmap::HashMap<(int,int),bool> = hashmap::HashMap::new();

            let mut newvisit: bool = true;

            loop { // iterate through items in the queue, which contains all of the edges/vertices
                if(newvisit){
                    let neighbors = [ (x,y-1),(x+1,y), (x,y+1), (x,y-1)];
                    for &coord in neighbors.iter() {
                        let (w,z) = coord;
                        if w >=0 && w < width && z >=0 && z < height { // bounds checking
                            // if neighbor at coord has not been or colored
                            let mut newvertex = false;
                            let mut uncolored = false;
                            do shared_arcs[w][z].read |dest| {
                                if dest.color < 0 {
                                    uncolored = true; }
                            }
                            if uncolored {
                                do shared_arcs[w][z].write |dest| {
                                    if dest.color < 0 {
                                        do shared_arcs[x][y].read |src| {
                                            dest.color = src.color;
                                        }
                                        newvertex = true;
                                    }
                                }
                            }
                            if newvertex { // then we have a new vertex
                                do shared_arcs[x][y].read |src| {
                                    do shared_arcs[w][z].read |dest| {
                                        queue.push( Edge::new(Point::new(x,y),Point::new(w,z),edgeCost(src,dest)));
                                    }
                                }
                            }
                        }
                    }
                }

                let edge = queue.maybe_pop();
                match edge {
                    Some(e) => {
                        // not in any tree
                        let coord = (e.dest.x,e.dest.y);
                        if *visited.get(&coord) == false { // use visited hashmap to figure out if we're at a new vertex. returns false if it can't return a vertex?
                            visited.insert(coord,true);
                            x = e.dest.x;
                            y = e.dest.y;
                            newvisit = true;
                        } else {
                            newvisit = false;
                        }
                        None => { break; } // this should execute at the end when there are no more unvisited or uncolored nodes for a thread
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>
/// Implemented by contexts who can add color.
pub trait AddColor<'a, T> {
    /// Add color with alpha channel.
    fn rgba(&'a self, r: f32, g: f32, b: f32, a: f32) -> T;

    /// Adds color with alpha channel set to 1.0.
    #[inline(always)]
    fn rgb(&'a self, r: f32, g: f32, b: f32) -> T {
        self.rgba(r, g, b, 1.0)
    }
}

<commit_msg>Added `colour` to `AddColor`<commit_after>
/// Implemented by contexts who can add color.
pub trait AddColor<'a, T> {
    /// Add color with alpha channel.
    fn rgba(&'a self, r: f32, g: f32, b: f32, a: f32) -> T;

    /// Adds color with alpha channel set to 1.0.
    #[inline(always)]
    fn rgb(&'a self, r: f32, g: f32, b: f32) -> T {
        self.rgba(r, g, b, 1.0)
    }

    /// Add color [r, g, b, a].
    #[inline(always)]
    fn color(&'a self, color: [f32, ..4]) -> T {
        self.rgba(color[0], color[1], color[2], color[3])
    }
}

<|endoftext|>


<commit_before>#![allow(unstable)]
#![feature(plugin)]
#[plugin] #[no_link]
extern crate regex_macros;
extern crate regex;

use std::io::fs::PathExtensions;
use std::io;
use std::os;

const TOP:&'static str = "
#![crate_type = \"dylib\"]
extern crate libc;

use libc::*;
use std::io::stdio::*;
use std::collections::HashMap;

const NOOP_FUNC:&'static WashFunc = &(noop as WashFunc);

pub type WashFunc = fn(&Vec<String>, &mut WashEnv) -> Vec<String>;
pub type VarTable = HashMap<String, String>;
pub type FuncTable = HashMap<String, WashFunc>;
pub type ScriptTable = HashMap<Path, WashScript>;

pub struct WashEnv {
    variables: VarTable,
    functions: FuncTable,
    scripts: ScriptTable,
    controls: Controls
}

pub struct WashScript {
    path: Path,
    hash: String,
    controls: Controls,
    handle: *const c_void,
    run_ptr: *const c_void,
    load_ptr: *const c_void,
    loaded: bool
}

pub struct Controls {
    stdin: StdinReader,
    stdout: StdWriter,
    stderr: StdWriter
}

impl WashEnv {
    pub fn hasv(&self, name:String) -> bool {
        self.variables.contains_key(&name)
    }

    pub fn hasf(&self, name:String) -> bool {
        self.functions.contains_key(&name)
    }

    pub fn insv(&mut self, name:String, val:String) {
        self.variables.insert(name, val);
    }

    pub fn insf(&mut self, name:String, func:WashFunc) {
        self.functions.insert(name, func);
    }

    pub fn getv(u_env:*const WashEnv, name:String) -> String {
        let env = unsafe{u_env.as_ref()}.unwrap();
        return match env.variables.get(&name) {
            None => \"\".to_string(),
            Some(val) => val.clone()
        };
    }

    pub fn getf<'a>(u_env:*const WashEnv, name:String) -> &'a WashFunc {
        let env = unsafe{u_env.as_ref()}.unwrap();
        return match env.functions.get(&name) {
            None => NOOP_FUNC,
            Some(ptr) => ptr
        }
    }
}

fn noop(args:&Vec<String>, env:&mut WashEnv) -> Vec<String> {
    return vec![];
}

fn sgetenv(name:&str) -> String {
    return std::os::getenv(name).unwrap_or(\"\".to_string()).to_string();
}";

const RUN_TOP:&'static str = "
#[no_mangle]
pub extern fn wash_run(u_args:*const Vec<String>, u_env:*mut WashEnv) {
    let args = unsafe{u_args.as_ref()}.unwrap();
    let mut env = unsafe{u_env.as_mut()}.unwrap();
";

const RUN_BOTTOM:&'static str = "
}";

fn process_line(line:String) -> String {
    if regex!("=").is_match(line.as_slice()) {
        let parts = regex!("=").splitn(line.as_slice(), 2).collect::<Vec<&str>>();
        return process_lvalue(parts[0].to_string()).connect(process_rvalue(parts[1].to_string()).as_slice());
    } else {
        return process_rvalue(line);
    }
}

fn process_lvalue(val:String) -> Vec<String> {
    if regex!("\\$(\\S+):(\\S+)").is_match(val.as_slice()) {
        let caps = regex!("\\$(\\S+):(\\S+)").captures(val.as_slice()).unwrap();
        match caps.at(1).unwrap() {
            "env" => {
                let val = caps.at(2).unwrap();
                let pre = format!("std::os::setenv(\"{}\", ", val).to_string();
                return vec![pre, ".as_slice());".to_string()];
            },
            _ => panic!("Unknown variable identifier")
        }
    } else if regex!("\\$(\\S+)").is_match(val.as_slice()) {
        let val = regex!("\\$(\\S+)").captures(val.as_slice()).unwrap().at(1).unwrap();
        let pre = format!("env.insv(\"{}\".to_string(), ", val).to_string();
        return vec![pre, ");".to_string()];
    } else {
        return vec![];
    }
}

fn process_rvalue(val:String) -> String {
    if regex!("^\\s*\"[^\"]*\"\\s*$").is_match(val.as_slice()) {
        let val = regex!("\"[^\"]*\"").captures(val.as_slice()).unwrap().at(0).unwrap();
        return format!("{}.to_string()", val.to_string());
    } if regex!("^\\s*\\$(\\S+):(\\S+)\\s*$").is_match(val.as_slice()) {
        let caps = regex!("\\$(\\S+):(\\S+)").captures(val.as_slice()).unwrap();
        match caps.at(1).unwrap() {
            "env" => {
                let val = caps.at(2).unwrap();
                return format!("sgetenv(\"{}\")", val).to_string();
            },
            _ => panic!("Unknown variable identifier")
        }
    } else if regex!("^\\s*\\$(\\S+)\\s*$").is_match(val.as_slice()) {
        let val = regex!("\\$(\\S+)").captures(val.as_slice()).unwrap().at(1).unwrap();
        return format!("WashEnv::getv(env, \"{}\".to_string())", val).to_string();
    } else if regex!("^\\s*(\\S+)\\((.*)\\)\\s*$").is_match(val.as_slice()) {
        let caps = regex!("(\\S+)\\((.*)\\)").captures(val.as_slice()).unwrap();
        let name = caps.at(1).unwrap();
        let re = regex!(",");
        let args_cap = caps.at(2).unwrap();
        let args_str;
        if re.is_match(args_cap) {
            let mut cap_args = re.split(caps.at(2).unwrap());
            let mut args = Vec::<String>::new();
            for arg in cap_args {
                args.push(process_rvalue(arg.to_string()));
            }
            args_str = args.connect(", ");
        } else {
            args_str = process_rvalue(args_cap.to_string());
        }
        return format!("WashEnv::getf(env, \"{}\".to_string())(&vec![{}], env);", name, args_str).to_string()
    } else {
        return "".to_string();
    }
}

pub fn main() {
    let args = os::args();
    if args.len() == 1 {
        panic!("No arguments given");
    }
    let inp = Path::new(args[1].to_string());
    if !inp.exists() {
        panic!("File does not exist");
    }
    let mut stdout = io::stdout();
    let mut inf = io::BufferedReader::new(io::File::open(&inp));
    stdout.write_str(TOP).unwrap();
    stdout.write_str(RUN_TOP).unwrap();
    loop {
        match inf.read_line() {
            Err(_) => break,
            Ok(line) => {
                stdout.write_str(process_line(line).as_slice()).unwrap();
                stdout.write_str("\n").unwrap();
            }
        }
    }
    stdout.write_str(RUN_BOTTOM).unwrap();
    stdout.write_str("\n").unwrap();
}
<commit_msg>Improve translation regex<commit_after>#![allow(unstable)]
#![feature(plugin)]
#[plugin] #[no_link]
extern crate regex_macros;
extern crate regex;

use std::io::fs::PathExtensions;
use std::io;
use std::os;

const TOP:&'static str = "
#![crate_type = \"dylib\"]
extern crate libc;

use libc::*;
use std::io::stdio::*;
use std::collections::HashMap;

const NOOP_FUNC:&'static WashFunc = &(noop as WashFunc);

pub type WashFunc = fn(&Vec<String>, &mut WashEnv) -> Vec<String>;
pub type VarTable = HashMap<String, String>;
pub type FuncTable = HashMap<String, WashFunc>;
pub type ScriptTable = HashMap<Path, WashScript>;

pub struct WashEnv {
    variables: VarTable,
    functions: FuncTable,
    scripts: ScriptTable,
    controls: Controls
}

pub struct WashScript {
    path: Path,
    hash: String,
    controls: Controls,
    handle: *const c_void,
    run_ptr: *const c_void,
    load_ptr: *const c_void,
    loaded: bool
}

pub struct Controls {
    stdin: StdinReader,
    stdout: StdWriter,
    stderr: StdWriter
}

impl WashEnv {
    pub fn hasv(&self, name:String) -> bool {
        self.variables.contains_key(&name)
    }

    pub fn hasf(&self, name:String) -> bool {
        self.functions.contains_key(&name)
    }

    pub fn insv(&mut self, name:String, val:String) {
        self.variables.insert(name, val);
    }

    pub fn insf(&mut self, name:String, func:WashFunc) {
        self.functions.insert(name, func);
    }

    pub fn getv(u_env:*const WashEnv, name:String) -> String {
        let env = unsafe{u_env.as_ref()}.unwrap();
        return match env.variables.get(&name) {
            None => \"\".to_string(),
            Some(val) => val.clone()
        };
    }

    pub fn getf<'a>(u_env:*const WashEnv, name:String) -> &'a WashFunc {
        let env = unsafe{u_env.as_ref()}.unwrap();
        return match env.functions.get(&name) {
            None => NOOP_FUNC,
            Some(ptr) => ptr
        }
    }
}

fn noop(args:&Vec<String>, env:&mut WashEnv) -> Vec<String> {
    return vec![];
}

fn sgetenv(name:&str) -> String {
    return std::os::getenv(name).unwrap_or(\"\".to_string()).to_string();
}";

const RUN_TOP:&'static str = "
#[no_mangle]
pub extern fn wash_run(u_args:*const Vec<String>, u_env:*mut WashEnv) {
    let args = unsafe{u_args.as_ref()}.unwrap();
    let mut env = unsafe{u_env.as_mut()}.unwrap();
";

const RUN_BOTTOM:&'static str = "
}";

fn process_line(line:String) -> String {
    if regex!("=").is_match(line.as_slice()) {
        let parts = regex!("=").splitn(line.as_slice(), 2).collect::<Vec<&str>>();
        return process_lvalue(parts[0].to_string()).connect(process_rvalue(parts[1].to_string()).as_slice());
    } else {
        return process_rvalue(line);
    }
}

fn process_lvalue(val:String) -> Vec<String> {
    if regex!("\\$([^ \t\n\r,\"()]+):([^ \t\n\r,\"()]+)").is_match(val.as_slice()) {
        let caps = regex!("\\$([^ \t\n\r,\"()]+):([^ \t\n\r,\"()]+)").captures(val.as_slice()).unwrap();
        match caps.at(1).unwrap() {
            "env" => {
                let val = caps.at(2).unwrap();
                let pre = format!("std::os::setenv(\"{}\", ", val).to_string();
                return vec![pre, ".as_slice());".to_string()];
            },
            _ => panic!("Unknown variable identifier")
        }
    } else if regex!("\\$([^ \t\n\r,\"()]+)").is_match(val.as_slice()) {
        let val = regex!("\\$([^ \t\n\r,\"()]+)").captures(val.as_slice()).unwrap().at(1).unwrap();
        let pre = format!("env.insv(\"{}\".to_string(), ", val).to_string();
        return vec![pre, ");".to_string()];
    } else {
        return vec![];
    }
}

fn process_rvalue(val:String) -> String {
    if regex!("^\\s*\"[^\"]*\"\\s*$").is_match(val.as_slice()) {
        let val = regex!("\"[^\"]*\"").captures(val.as_slice()).unwrap().at(0).unwrap();
        return format!("{}.to_string()", val.to_string());
    } if regex!("^\\s*\\$([^ \t\n\r,\"()]+):([^ \t\n\r,\"()]+)\\s*$").is_match(val.as_slice()) {
        let caps = regex!("\\$([^ \t\n\r,\"()]+):([^ \t\n\r,\"()]+)").captures(val.as_slice()).unwrap();
        match caps.at(1).unwrap() {
            "env" => {
                let val = caps.at(2).unwrap();
                return format!("sgetenv(\"{}\")", val).to_string();
            },
            _ => panic!("Unknown variable identifier")
        }
    } else if regex!("^\\s*\\$([^ \t\n\r,\"()]+)\\s*$").is_match(val.as_slice()) {
        let val = regex!("\\$([^ \t\n\r,\"()]+)").captures(val.as_slice()).unwrap().at(1).unwrap();
        return format!("WashEnv::getv(env, \"{}\".to_string())", val).to_string();
    } else if regex!("^\\s*([^ \t\n\r,\"()]+)\\((.*)\\)\\s*$").is_match(val.as_slice()) {
        let caps = regex!("([^ \t\n\r,\"()]+)\\((.*)\\)").captures(val.as_slice()).unwrap();
        let name = caps.at(1).unwrap();
        let re = regex!("(\".+\"|[^ \t\n\r,\"()]+)");
        let args_cap = caps.at(2).unwrap();
        let mut args = Vec::<String>::new();
        for cap in re.captures_iter(args_cap) {
            args.push(process_rvalue(cap.at(1).unwrap().to_string()));
        }
        return format!("WashEnv::getf(env, \"{}\".to_string())(&vec![{}], env);", name, args.connect(", ")).to_string()
    } else {
        return "".to_string();
    }
}

pub fn main() {
    let args = os::args();
    if args.len() == 1 {
        panic!("No arguments given");
    }
    let inp = Path::new(args[1].to_string());
    if !inp.exists() {
        panic!("File does not exist");
    }
    let mut stdout = io::stdout();
    let mut inf = io::BufferedReader::new(io::File::open(&inp));
    stdout.write_str(TOP).unwrap();
    stdout.write_str(RUN_TOP).unwrap();
    loop {
        match inf.read_line() {
            Err(_) => break,
            Ok(line) => {
                stdout.write_str(process_line(line).as_slice()).unwrap();
                stdout.write_str("\n").unwrap();
            }
        }
    }
    stdout.write_str(RUN_BOTTOM).unwrap();
    stdout.write_str("\n").unwrap();
}
<|endoftext|>


<commit_before>use proc_macro2::TokenStream;

use syn::DeriveInput;
use syn::spanned::Spanned;
use syn::parse::Error;
use quote::quote;

use default_attr::{DefaultAttr, ConversionStrategy};
use util::find_only;

pub fn impl_my_derive(input: &DeriveInput) -> Result<TokenStream, Error> {
    let name = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let (default_expr, doc) = match input.data {
        syn::Data::Struct(ref body) => {
            let (body_assignment, doc) = default_body_tt(&body.fields)?;
            (quote! {
                #name #body_assignment
            }, format!("Return `{}{}`", name, doc))
        }
        syn::Data::Enum(ref body) => {
            let default_variant = find_only(body.variants.iter(), |variant| {
                if let Some(meta) = DefaultAttr::find_in_attributes(&variant.attrs)? {
                    if meta.code.is_none() {
                        Ok(true)
                    } else {
                        Err(Error::new(meta.code.span(), "Attribute #[default] on variants should have no value"))
                    }
                } else {
                    Ok(false)
                }
            })?.ok_or_else(|| Error::new(input.span(), "No default variant"))?;
            let default_variant_name = &default_variant.ident;
            let (body_assignment, doc) = default_body_tt(&default_variant.fields)?;
            (quote! {
                #name :: #default_variant_name #body_assignment
            }, format!("Return `{}::{}{}`", name, default_variant_name, doc))
        }
        syn::Data::Union(_) => {
            panic!()
        }
    };
    Ok(quote! {
        impl #impl_generics Default for #name #ty_generics #where_clause {
            #[doc = #doc]
            fn default() -> Self {
                #default_expr
            }
        }
    })
}

/// Return a token-tree for the default "body" - the part after the name that contains the values.
/// That is, the `{ ... }` part for structs, the `(...)` part for tuples, and nothing for units.
fn default_body_tt(body: &syn::Fields) -> Result<(TokenStream, String), Error> {
    let mut doc = String::new();
    use std::fmt::Write;
    let body_tt = match body {
        &syn::Fields::Named(ref fields) => {
            doc.push_str(" {");
            let result = {
                let field_assignments = fields.named.iter().map(|field| {
                    let field_name = field.ident.as_ref();
                    let (default_value, default_doc) = field_default_expr_and_doc(field)?;
                    write!(&mut doc, "\n    {}: {},", field_name.expect("field value in struct is empty"), default_doc).unwrap();
                    // let default_value = default_value.into_token_stream();
                    Ok(quote! { #field_name : #default_value })
                }).collect::<Result<Vec<_>, Error>>()?;
                quote!{
                    {
                        #( #field_assignments ),*
                    }
                }
            };
            if (&mut doc).ends_with(",") {
                doc.pop();
                doc.push('\n');
            };
            doc.push('}');
            result
        }
        &syn::Fields::Unnamed(ref fields) => {
            doc.push('(');
            let result = {
                let field_assignments = fields.unnamed.iter().map(|field| {
                    let (default_value, default_doc) = field_default_expr_and_doc(field)?;
                    write!(&mut doc, "{}, ", default_doc).unwrap();
                    Ok(default_value)
                }).collect::<Result<Vec<TokenStream>, Error>>()?;
                quote! {
                    (
                        #( #field_assignments ),*
                    )
                }
            };
            if (&mut doc).ends_with(", ") {
                doc.pop();
                doc.pop();
            };
            doc.push(')');
            result
        }
        &syn::Fields::Unit => quote!{},
    };
    Ok((body_tt, doc))
}

/// Return a default expression for a field based on it's `#[default = "..."]` attribute. Panic
/// if there is more than one, of if there is a `#[default]` attribute without value.
fn field_default_expr_and_doc(field: &syn::Field) -> Result<(TokenStream, String), Error> {
    if let Some(default_attr) = DefaultAttr::find_in_attributes(&field.attrs)? {
        let conversion_strategy = default_attr.conversion_strategy();
        let field_value = default_attr.code.ok_or_else(|| {
            Error::new(field.span(), "Expected #[default = ...] or #[default(...)]")})?;

        let field_value = match conversion_strategy {
            ConversionStrategy::NoConversion => field_value,
            ConversionStrategy::Into => quote!((#field_value).into()),
        };

        let field_doc = format!("{}", field_value);
        Ok((field_value, field_doc))
    } else {
        Ok((quote! {
            Default::default()
        }, "Default::default()".to_owned()))
    }
}
<commit_msg>Fix 'default_trait_access' Clippy lint complains<commit_after>use proc_macro2::TokenStream;

use syn::DeriveInput;
use syn::spanned::Spanned;
use syn::parse::Error;
use quote::{quote, ToTokens as _};

use default_attr::{DefaultAttr, ConversionStrategy};
use util::find_only;

pub fn impl_my_derive(input: &DeriveInput) -> Result<TokenStream, Error> {
    let name = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let (default_expr, doc) = match input.data {
        syn::Data::Struct(ref body) => {
            let (body_assignment, doc) = default_body_tt(&body.fields)?;
            (quote! {
                #name #body_assignment
            }, format!("Return `{}{}`", name, doc))
        }
        syn::Data::Enum(ref body) => {
            let default_variant = find_only(body.variants.iter(), |variant| {
                if let Some(meta) = DefaultAttr::find_in_attributes(&variant.attrs)? {
                    if meta.code.is_none() {
                        Ok(true)
                    } else {
                        Err(Error::new(meta.code.span(), "Attribute #[default] on variants should have no value"))
                    }
                } else {
                    Ok(false)
                }
            })?.ok_or_else(|| Error::new(input.span(), "No default variant"))?;
            let default_variant_name = &default_variant.ident;
            let (body_assignment, doc) = default_body_tt(&default_variant.fields)?;
            (quote! {
                #name :: #default_variant_name #body_assignment
            }, format!("Return `{}::{}{}`", name, default_variant_name, doc))
        }
        syn::Data::Union(_) => {
            panic!()
        }
    };
    Ok(quote! {
        impl #impl_generics Default for #name #ty_generics #where_clause {
            #[doc = #doc]
            fn default() -> Self {
                #default_expr
            }
        }
    })
}

/// Return a token-tree for the default "body" - the part after the name that contains the values.
/// That is, the `{ ... }` part for structs, the `(...)` part for tuples, and nothing for units.
fn default_body_tt(body: &syn::Fields) -> Result<(TokenStream, String), Error> {
    let mut doc = String::new();
    use std::fmt::Write;
    let body_tt = match body {
        &syn::Fields::Named(ref fields) => {
            doc.push_str(" {");
            let result = {
                let field_assignments = fields.named.iter().map(|field| {
                    let field_name = field.ident.as_ref();
                    let (default_value, default_doc) = field_default_expr_and_doc(field)?;
                    write!(&mut doc, "\n    {}: {},", field_name.expect("field value in struct is empty"), default_doc).unwrap();
                    // let default_value = default_value.into_token_stream();
                    Ok(quote! { #field_name : #default_value })
                }).collect::<Result<Vec<_>, Error>>()?;
                quote!{
                    {
                        #( #field_assignments ),*
                    }
                }
            };
            if (&mut doc).ends_with(",") {
                doc.pop();
                doc.push('\n');
            };
            doc.push('}');
            result
        }
        &syn::Fields::Unnamed(ref fields) => {
            doc.push('(');
            let result = {
                let field_assignments = fields.unnamed.iter().map(|field| {
                    let (default_value, default_doc) = field_default_expr_and_doc(field)?;
                    write!(&mut doc, "{}, ", default_doc).unwrap();
                    Ok(default_value)
                }).collect::<Result<Vec<TokenStream>, Error>>()?;
                quote! {
                    (
                        #( #field_assignments ),*
                    )
                }
            };
            if (&mut doc).ends_with(", ") {
                doc.pop();
                doc.pop();
            };
            doc.push(')');
            result
        }
        &syn::Fields::Unit => quote!{},
    };
    Ok((body_tt, doc))
}

/// Return a default expression for a field based on it's `#[default = "..."]` attribute. Panic
/// if there is more than one, of if there is a `#[default]` attribute without value.
fn field_default_expr_and_doc(field: &syn::Field) -> Result<(TokenStream, String), Error> {
    if let Some(default_attr) = DefaultAttr::find_in_attributes(&field.attrs)? {
        let conversion_strategy = default_attr.conversion_strategy();
        let field_value = default_attr.code.ok_or_else(|| {
            Error::new(field.span(), "Expected #[default = ...] or #[default(...)]")})?;

        let field_value = match conversion_strategy {
            ConversionStrategy::NoConversion => field_value,
            ConversionStrategy::Into => quote!((#field_value).into()),
        };

        let field_doc = format!("{}", field_value);
        Ok((field_value, field_doc))
    } else {
        let field_type = field.ty.clone().into_token_stream();
        Ok((quote! {
            <#field_type as core::default::Default>::default()
        }, "Default::default()".to_owned()))
    }
}
<|endoftext|>


<commit_before>use nom::{alphanumeric, digit, multispace};
use std::str;

use common::{binary_comparison_operator, binary_logical_operator, column_identifier,
             unary_negation_operator, Operator};
use parser::{ConditionBase, ConditionExpression, ConditionTree};

fn fold_cond_exprs(initial: ConditionExpression,
                   remainder: Vec<(Operator, ConditionExpression)>)
                   -> ConditionExpression {
    remainder.into_iter().fold(initial, |acc, pair| {
        let (oper, expr) = pair;
        match oper {
            Operator::Equal | Operator::Less | Operator::Greater | Operator::LessOrEqual |
            Operator::GreaterOrEqual | Operator::NotEqual => {
                ConditionExpression::ComparisonOp(ConditionTree {
                    operator: oper.clone(),
                    left: Some(Box::new(acc)),
                    right: Some(Box::new(expr)),
                })
            }
            Operator::And | Operator::Or => {
                ConditionExpression::LogicalOp(ConditionTree {
                    operator: oper.clone(),
                    left: Some(Box::new(acc)),
                    right: Some(Box::new(expr)),
                })
            }
            o => {
                println!("unsupported op {:?}", o);
                unimplemented!()
            }
        }
    })
}

/// Parse a conditional expression into a condition tree structure
named!(pub condition_expr<&[u8], ConditionExpression>,
    chain!(
        neg_op: opt!(unary_negation_operator) ~
        initial: boolean_primary ~
        remainder: many0!(
            complete!(
                chain!(
                    log_op: delimited!(opt!(multispace),
                                       binary_logical_operator,
                                       opt!(multispace)) ~
                    right_expr: condition_expr,
                    || {
                        (log_op, right_expr)
                    }
                )
            )
        ),
        || {
            if let Some(Operator::Not) = neg_op {
                ConditionExpression::LogicalOp(
                    ConditionTree {
                        operator: Operator::Not,
                        left: Some(Box::new(fold_cond_exprs(initial, remainder))),
                        right: None,
                    }
                )
            } else {
                fold_cond_exprs(initial, remainder)
            }
        }
    )
);

named!(boolean_primary<&[u8], ConditionExpression>,
    chain!(
        initial: predicate ~
        remainder:
        many0!(
            complete!(
                chain!(
                    op: delimited!(opt!(multispace),
                                   binary_comparison_operator,
                                   opt!(multispace)) ~
                    right_expr: boolean_primary,
                    || {
                        (op, right_expr)
                    }
                )
            )
        ),
        || { fold_cond_exprs(initial, remainder) }
    )
);

named!(predicate<&[u8], ConditionExpression>,
    alt_complete!(
            chain!(
                delimited!(opt!(multispace), tag!("?"), opt!(multispace)),
                || {
                    ConditionExpression::Base(
                        ConditionBase::Placeholder
                    )
                }
            )
        |   chain!(
                field: delimited!(opt!(multispace), digit, opt!(multispace)),
                || {
                    ConditionExpression::Base(
                        ConditionBase::Literal(String::from(str::from_utf8(field).unwrap()))
                    )
                }
            )
        |   chain!(
                field: delimited!(opt!(multispace),
                                  alt_complete!(
                                        delimited!(tag!("\""), alphanumeric, tag!("\""))
                                      | delimited!(tag!("'"), alphanumeric, tag!("'"))
                                  ),
                                  opt!(multispace)),
                || {
                    ConditionExpression::Base(
                        ConditionBase::Literal(String::from(str::from_utf8(field).unwrap()))
                    )
                }
            )
        |   chain!(
                field: delimited!(opt!(multispace), column_identifier, opt!(multispace)),
                || {
                    ConditionExpression::Base(
                        ConditionBase::Field(field)
                    )
                }
            )
    )
);

#[cfg(test)]
mod tests {
    use super::*;
    use common::Operator;
    use parser::{Column, ConditionBase, ConditionExpression, ConditionTree};

    fn flat_condition_tree(op: Operator,
                           l: ConditionBase,
                           r: ConditionBase)
                           -> ConditionExpression {
        ConditionExpression::ComparisonOp(ConditionTree {
            operator: op,
            left: Some(Box::new(ConditionExpression::Base(l))),
            right: Some(Box::new(ConditionExpression::Base(r))),
        })
    }

    #[test]
    fn equality_placeholder() {
        let cond = "foo = ?";

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1,
                   flat_condition_tree(Operator::Equal,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Placeholder)
                  );
    }

    #[test]
    fn equality_literals() {
        let cond1 = "foo = 42";
        let cond2 = "foo = \"hello\"";

        let res1 = condition_expr(cond1.as_bytes());
        assert_eq!(res1.unwrap().1,
                   flat_condition_tree(Operator::Equal,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Literal(String::from("42"))
                                      )
                   );

        let res2 = condition_expr(cond2.as_bytes());
        assert_eq!(res2.unwrap().1,
                   flat_condition_tree(Operator::Equal,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Literal(String::from("hello"))
                                      )
                   );
    }

    #[test]
    fn inequality_literals() {
        let cond1 = "foo >= 42";
        let cond2 = "foo <= 5";

        let res1 = condition_expr(cond1.as_bytes());
        assert_eq!(res1.unwrap().1,
                   flat_condition_tree(Operator::GreaterOrEqual,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Literal(String::from("42"))
                                      )
                   );

        let res2 = condition_expr(cond2.as_bytes());
        assert_eq!(res2.unwrap().1,
                   flat_condition_tree(Operator::LessOrEqual,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Literal(String::from("5"))
                                      )
                   );
    }
}
<commit_msg>Support LIKE operator<commit_after>use nom::{alphanumeric, digit, multispace};
use std::str;

use common::{binary_comparison_operator, binary_logical_operator, column_identifier,
             unary_negation_operator, Operator};
use parser::{ConditionBase, ConditionExpression, ConditionTree};

fn fold_cond_exprs(initial: ConditionExpression,
                   remainder: Vec<(Operator, ConditionExpression)>)
                   -> ConditionExpression {
    remainder.into_iter().fold(initial, |acc, pair| {
        let (oper, expr) = pair;
        match oper {
            Operator::Equal | Operator::Less | Operator::Greater | Operator::LessOrEqual |
            Operator::Like | Operator::GreaterOrEqual | Operator::NotEqual => {
                ConditionExpression::ComparisonOp(ConditionTree {
                    operator: oper.clone(),
                    left: Some(Box::new(acc)),
                    right: Some(Box::new(expr)),
                })
            }
            Operator::And | Operator::Or => {
                ConditionExpression::LogicalOp(ConditionTree {
                    operator: oper.clone(),
                    left: Some(Box::new(acc)),
                    right: Some(Box::new(expr)),
                })
            }
            o => {
                println!("unsupported op {:?}", o);
                unimplemented!()
            }
        }
    })
}

/// Parse a conditional expression into a condition tree structure
named!(pub condition_expr<&[u8], ConditionExpression>,
    chain!(
        neg_op: opt!(unary_negation_operator) ~
        initial: boolean_primary ~
        remainder: many0!(
            complete!(
                chain!(
                    log_op: delimited!(opt!(multispace),
                                       binary_logical_operator,
                                       opt!(multispace)) ~
                    right_expr: condition_expr,
                    || {
                        (log_op, right_expr)
                    }
                )
            )
        ),
        || {
            if let Some(Operator::Not) = neg_op {
                ConditionExpression::LogicalOp(
                    ConditionTree {
                        operator: Operator::Not,
                        left: Some(Box::new(fold_cond_exprs(initial, remainder))),
                        right: None,
                    }
                )
            } else {
                fold_cond_exprs(initial, remainder)
            }
        }
    )
);

named!(boolean_primary<&[u8], ConditionExpression>,
    chain!(
        initial: predicate ~
        remainder:
        many0!(
            complete!(
                chain!(
                    op: delimited!(opt!(multispace),
                                   binary_comparison_operator,
                                   opt!(multispace)) ~
                    right_expr: boolean_primary,
                    || {
                        (op, right_expr)
                    }
                )
            )
        ),
        || { fold_cond_exprs(initial, remainder) }
    )
);

named!(predicate<&[u8], ConditionExpression>,
    alt_complete!(
            chain!(
                delimited!(opt!(multispace), tag!("?"), opt!(multispace)),
                || {
                    ConditionExpression::Base(
                        ConditionBase::Placeholder
                    )
                }
            )
        |   chain!(
                field: delimited!(opt!(multispace), digit, opt!(multispace)),
                || {
                    ConditionExpression::Base(
                        ConditionBase::Literal(String::from(str::from_utf8(field).unwrap()))
                    )
                }
            )
        |   chain!(
                field: delimited!(opt!(multispace),
                                  alt_complete!(
                                        delimited!(tag!("\""), alphanumeric, tag!("\""))
                                      | delimited!(tag!("'"), alphanumeric, tag!("'"))
                                  ),
                                  opt!(multispace)),
                || {
                    ConditionExpression::Base(
                        ConditionBase::Literal(String::from(str::from_utf8(field).unwrap()))
                    )
                }
            )
        |   chain!(
                field: delimited!(opt!(multispace), column_identifier, opt!(multispace)),
                || {
                    ConditionExpression::Base(
                        ConditionBase::Field(field)
                    )
                }
            )
    )
);

#[cfg(test)]
mod tests {
    use super::*;
    use common::Operator;
    use parser::{Column, ConditionBase, ConditionExpression, ConditionTree};

    fn flat_condition_tree(op: Operator,
                           l: ConditionBase,
                           r: ConditionBase)
                           -> ConditionExpression {
        ConditionExpression::ComparisonOp(ConditionTree {
            operator: op,
            left: Some(Box::new(ConditionExpression::Base(l))),
            right: Some(Box::new(ConditionExpression::Base(r))),
        })
    }

    #[test]
    fn equality_placeholder() {
        let cond = "foo = ?";

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1,
                   flat_condition_tree(Operator::Equal,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Placeholder)
                  );
    }

    #[test]
    fn equality_literals() {
        let cond1 = "foo = 42";
        let cond2 = "foo = \"hello\"";

        let res1 = condition_expr(cond1.as_bytes());
        assert_eq!(res1.unwrap().1,
                   flat_condition_tree(Operator::Equal,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Literal(String::from("42"))
                                      )
                   );

        let res2 = condition_expr(cond2.as_bytes());
        assert_eq!(res2.unwrap().1,
                   flat_condition_tree(Operator::Equal,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Literal(String::from("hello"))
                                      )
                   );
    }

    #[test]
    fn inequality_literals() {
        let cond1 = "foo >= 42";
        let cond2 = "foo <= 5";

        let res1 = condition_expr(cond1.as_bytes());
        assert_eq!(res1.unwrap().1,
                   flat_condition_tree(Operator::GreaterOrEqual,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Literal(String::from("42"))
                                      )
                   );

        let res2 = condition_expr(cond2.as_bytes());
        assert_eq!(res2.unwrap().1,
                   flat_condition_tree(Operator::LessOrEqual,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Literal(String::from("5"))
                                      )
                   );
    }
}
<|endoftext|>


<commit_before>use commons::CHasher;
use commons::HASH_SIZE;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::BufReader;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write;
use std::path::Path;

pub const CHUNK_SIZE: usize = 60000;

pub struct DiskFile
{
    file: File,
    file_path: String,
    read_only: bool,
    global_hash: [u8; HASH_SIZE],
    chunk_hash: Vec<[u8; HASH_SIZE]>,
    file_size: u64,
    file_name: String,
    name_hash: [u8; HASH_SIZE],
}

impl DiskFile
{
    pub fn new(path: &Path, set_size: Option<u64>) -> DiskFile
    {
        let ro = set_size.is_none();
        let mut diskfile = DiskFile {
            file: match OpenOptions::new().write(!ro).read(true).create(!ro).open(path)
            {
                Err(why) => panic!("Cannot open {}: {}", path.to_str().unwrap_or("NULL"), why),
                Ok(result) => result,
            },
            read_only: ro,
            global_hash: [0; HASH_SIZE],
            name_hash: [0; HASH_SIZE],
            chunk_hash: Vec::new(),
            file_path: path.to_str().unwrap_or("NULL").to_string(),
            file_size: 0,
            file_name: String::from(path.file_name().unwrap().to_str().unwrap_or("")),
        };
        if diskfile.file_name == ""
        {
            panic!("Cannot get filename for {}", path.to_str().unwrap_or("NULL"));
        }
        let mut name_sha3 = CHasher::new();
        name_sha3.update(diskfile.file_name.as_bytes());
        name_sha3.finalize(&mut diskfile.name_hash);
        if !diskfile.read_only
        {
            let _ = diskfile.file.set_len(set_size.unwrap());
        }
        let _ = diskfile.file.sync_all();
        let mut buff_file = BufReader::new(match OpenOptions::new().read(true).open(path)
        {
            Err(why) => panic!("Cannot open {}: {}", path.to_str().unwrap_or("NULL"), why),
            Ok(result) => result,
        });
        let mut file_sha3 = CHasher::new();
        loop
        {
            let mut chunk_sha3 = CHasher::new();
            let mut buffer: [u8; CHUNK_SIZE] = [0; CHUNK_SIZE];
            let mut buffer_index: usize = 0;
            loop
            {
                match buff_file.read(&mut buffer[buffer_index..CHUNK_SIZE])
                {
                    Err(why) => panic!("Error while reading {}: {}", diskfile.file_path, why),
                    Ok(0) => break,
                    Ok(n) => buffer_index += n,
                };
            }
            if buffer_index != 0
            {
                chunk_sha3.update(&buffer[0..buffer_index]);
                file_sha3.update(&buffer[0..buffer_index]);
            }
            let mut tmp_chunk_sha3: [u8; HASH_SIZE] = [0; HASH_SIZE];
            chunk_sha3.finalize(&mut tmp_chunk_sha3);
            diskfile.chunk_hash.push(tmp_chunk_sha3);
            diskfile.file_size += buffer_index as u64;
            if buffer_index != CHUNK_SIZE
            {
                break;
            }
        }
        file_sha3.finalize(&mut diskfile.global_hash);
        return diskfile;
    }

    pub fn get_name_hash(&self) -> [u8; HASH_SIZE]
    {
        let mut v: [u8; HASH_SIZE] = [0; HASH_SIZE];
        v.clone_from_slice(&self.name_hash);
        return v;
    }

    pub fn get_chunk_hash(&self, chunk_number: usize) -> [u8; HASH_SIZE]
    {
        let mut v: [u8; HASH_SIZE] = [0; HASH_SIZE];
        v.clone_from_slice(&self.chunk_hash[chunk_number]);
        return v;
    }

    pub fn get_file_hash(&self) -> [u8; HASH_SIZE]
    {
        let mut v: [u8; HASH_SIZE] = [0; HASH_SIZE];
        v.clone_from_slice(&self.global_hash);
        return v;
    }

    pub fn get_file_name(&self) -> String
    {
        return self.file_name.clone();
    }

    pub fn get_num_chunks(&self) -> usize
    {
        return self.chunk_hash.len();
    }

    pub fn get_chunk_size(&self, chunk_number: usize) -> usize
    {
        if chunk_number != self.chunk_hash.len() - 1
        {
            return CHUNK_SIZE;
        }
        if self.file_size % (CHUNK_SIZE as u64) == 0
        {
            return CHUNK_SIZE;
        }
        return (self.file_size % (CHUNK_SIZE as u64)) as usize;
    }

    pub fn read_chunk(&mut self, chunk_number: usize) -> Vec<u8>
    {
        if chunk_number >= self.chunk_hash.len()
        {
            panic!("{} -> cannot read chunk {}: doesn't exist", self.file_path, chunk_number);
        }
        let chunk_size = self.get_chunk_size(chunk_number);
        let mut v: Vec<u8> = vec![0; chunk_size];
        let _ = match self.file.seek(SeekFrom::Start((CHUNK_SIZE as u64) * (chunk_number as u64)))
        {
            Err(why) => panic!("Seek error on {}: {}", self.file_path, why),
            Ok(n) => n,
        };
        let _ = match self.file.read_exact(v.as_mut_slice())
        {
            Err(why) => panic!("Read error on {}: {}", self.file_path, why),
            _ => 0,
        };
        return v;
    }

    pub fn write_chunk(&mut self, chunk_number: usize, data: &Vec<u8>)
    {
        if self.read_only
        {
            panic!("Trying to write {} which is read-only!", self.file_path);
        }
        let chunk_size = self.get_chunk_size(chunk_number);
        if chunk_size != data.len()
        {
            panic!("Error while writing chunk for {}: chunk size mismatch", self.file_path);
        }
        let _ = match self.file.seek(SeekFrom::Start((CHUNK_SIZE as u64) * (chunk_number as u64)))
        {
            Err(why) => panic!("Seek error on {}: {}", self.file_path, why),
            Ok(n) => n,
        };
        let _ = match self.file.write_all(data.as_slice())
        {
            Err(why) => panic!("Write error on {}: {}", self.file_path, why),
            _ => 0,
        };
    }
}

impl Drop for DiskFile
{
    fn drop(&mut self)
    {
        if !self.read_only
        {
            match self.file.sync_all()
            {
                Err(why) => panic!("Cannot sync {} to disk: {}", self.file_path, why),
                Ok(_) => println!("{} sync'd to disk!", self.file_path),
            }
        };
    }
}
<commit_msg>Fix for non truncable files<commit_after>use commons::CHasher;
use commons::HASH_SIZE;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::BufReader;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write;
use std::path::Path;
use std::time::Instant;

pub const CHUNK_SIZE: usize = 60000;

pub struct DiskFile
{
    file: File,
    file_path: String,
    read_only: bool,
    global_hash: [u8; HASH_SIZE],
    chunk_hash: Vec<[u8; HASH_SIZE]>,
    file_size: u64,
    file_name: String,
    name_hash: [u8; HASH_SIZE],
}

impl DiskFile
{
    pub fn new(path: &Path, set_size: Option<u64>) -> DiskFile
    {
        let ro = set_size.is_none();
        let mut diskfile = DiskFile {
            file: match OpenOptions::new().write(!ro).read(true).create(!ro).open(path)
            {
                Err(why) => panic!("Cannot open {}: {}", path.to_str().unwrap_or("NULL"), why),
                Ok(result) => result,
            },
            read_only: ro,
            global_hash: [0; HASH_SIZE],
            name_hash: [0; HASH_SIZE],
            chunk_hash: Vec::new(),
            file_path: path.to_str().unwrap_or("NULL").to_string(),
            file_size: 0,
            file_name: String::from(path.file_name().unwrap().to_str().unwrap_or("")),
        };
        if diskfile.file_name == ""
        {
            panic!("Cannot get filename for {}", path.to_str().unwrap_or("NULL"));
        }
        println!("Calculating hash for {}", diskfile.file_name);
        let mut name_sha3 = CHasher::new();
        name_sha3.update(diskfile.file_name.as_bytes());
        name_sha3.finalize(&mut diskfile.name_hash);
        if !diskfile.read_only
        {
            let _ = diskfile.file.set_len(set_size.unwrap());
        }
        let _ = diskfile.file.sync_all();
        let mut buff_file = BufReader::new(match OpenOptions::new().read(true).open(path)
        {
            Err(why) => panic!("Cannot open {}: {}", path.to_str().unwrap_or("NULL"), why),
            Ok(result) => result,
        });
        let mut file_sha3 = CHasher::new();
        let mut byte_count: u64 = 0;
        let mut started = Instant::now();
        loop
        {
            if started.elapsed().as_secs() > 1
            {
                started = Instant::now();
                println!("Hashed {} kibibytes", byte_count / 1024);
            }
            let mut chunk_sha3 = CHasher::new();
            let mut buffer: [u8; CHUNK_SIZE] = [0; CHUNK_SIZE];
            let mut buffer_index: usize = 0;
            loop
            {
                match buff_file.read(&mut buffer[buffer_index..CHUNK_SIZE])
                {
                    Err(why) => panic!("Error while reading {}: {}", diskfile.file_path, why),
                    Ok(0) => break,
                    Ok(n) => buffer_index += n,
                };
            }
            byte_count += buffer_index as u64;
            if set_size.is_some() && byte_count > set_size.unwrap()
            {
                buffer_index -= (byte_count - set_size.unwrap() as u64) as usize;
            }
            if buffer_index != 0
            {
                chunk_sha3.update(&buffer[0..buffer_index]);
                file_sha3.update(&buffer[0..buffer_index]);
            }
            let mut tmp_chunk_sha3: [u8; HASH_SIZE] = [0; HASH_SIZE];
            chunk_sha3.finalize(&mut tmp_chunk_sha3);
            diskfile.chunk_hash.push(tmp_chunk_sha3);
            diskfile.file_size += buffer_index as u64;
            if buffer_index != CHUNK_SIZE
            {
                break;
            }
        }
        file_sha3.finalize(&mut diskfile.global_hash);
        return diskfile;
    }

    pub fn get_name_hash(&self) -> [u8; HASH_SIZE]
    {
        let mut v: [u8; HASH_SIZE] = [0; HASH_SIZE];
        v.clone_from_slice(&self.name_hash);
        return v;
    }

    pub fn get_chunk_hash(&self, chunk_number: usize) -> [u8; HASH_SIZE]
    {
        let mut v: [u8; HASH_SIZE] = [0; HASH_SIZE];
        v.clone_from_slice(&self.chunk_hash[chunk_number]);
        return v;
    }

    pub fn get_file_hash(&self) -> [u8; HASH_SIZE]
    {
        let mut v: [u8; HASH_SIZE] = [0; HASH_SIZE];
        v.clone_from_slice(&self.global_hash);
        return v;
    }

    pub fn get_file_name(&self) -> String
    {
        return self.file_name.clone();
    }

    pub fn get_num_chunks(&self) -> usize
    {
        return self.chunk_hash.len();
    }

    pub fn get_chunk_size(&self, chunk_number: usize) -> usize
    {
        if chunk_number != self.chunk_hash.len() - 1
        {
            return CHUNK_SIZE;
        }
        if self.file_size % (CHUNK_SIZE as u64) == 0
        {
            return CHUNK_SIZE;
        }
        return (self.file_size % (CHUNK_SIZE as u64)) as usize;
    }

    pub fn read_chunk(&mut self, chunk_number: usize) -> Vec<u8>
    {
        if chunk_number >= self.chunk_hash.len()
        {
            panic!("{} -> cannot read chunk {}: doesn't exist", self.file_path, chunk_number);
        }
        let chunk_size = self.get_chunk_size(chunk_number);
        let mut v: Vec<u8> = vec![0; chunk_size];
        let _ = match self.file.seek(SeekFrom::Start((CHUNK_SIZE as u64) * (chunk_number as u64)))
        {
            Err(why) => panic!("Seek error on {}: {}", self.file_path, why),
            Ok(n) => n,
        };
        let _ = match self.file.read_exact(v.as_mut_slice())
        {
            Err(why) => panic!("Read error on {}: {}", self.file_path, why),
            _ => 0,
        };
        return v;
    }

    pub fn write_chunk(&mut self, chunk_number: usize, data: &Vec<u8>)
    {
        if self.read_only
        {
            panic!("Trying to write {} which is read-only!", self.file_path);
        }
        let chunk_size = self.get_chunk_size(chunk_number);
        if chunk_size != data.len()
        {
            panic!("Error while writing chunk for {}: chunk size mismatch", self.file_path);
        }
        let _ = match self.file.seek(SeekFrom::Start((CHUNK_SIZE as u64) * (chunk_number as u64)))
        {
            Err(why) => panic!("Seek error on {}: {}", self.file_path, why),
            Ok(n) => n,
        };
        let _ = match self.file.write_all(data.as_slice())
        {
            Err(why) => panic!("Write error on {}: {}", self.file_path, why),
            _ => 0,
        };
    }
}

impl Drop for DiskFile
{
    fn drop(&mut self)
    {
        if !self.read_only
        {
            match self.file.sync_all()
            {
                Err(why) => panic!("Cannot sync {} to disk: {}", self.file_path, why),
                Ok(_) => println!("{} sync'd to disk!", self.file_path),
            }
        };
    }
}
<|endoftext|>


<commit_before>//! Lints concerned with the grouping of digits with underscores in integral or
//! floating-point literal expressions.

use rustc::lint::*;
use syntax::ast::*;
use syntax_pos;
use utils::{span_help_and_lint, snippet_opt, in_external_macro};

/// **What it does:** Warns if a long integral or floating-point constant does
/// not contain underscores.
///
/// **Why is this bad?** Reading long numbers is difficult without separators.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 61864918973511
/// ```
declare_lint! {
    pub UNREADABLE_LITERAL,
    Warn,
    "long integer literal without underscores"
}

/// **What it does:** Warns if an integral or floating-point constant is
/// grouped inconsistently with underscores.
///
/// **Why is this bad?** Readers may incorrectly interpret inconsistently
/// grouped digits.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 618_64_9189_73_511
/// ```
declare_lint! {
    pub INCONSISTENT_DIGIT_GROUPING,
    Warn,
    "integer literals with digits grouped inconsistently"
}

/// **What it does:** Warns if the digits of an integral or floating-point
/// constant are grouped into groups that
/// are too large.
///
/// **Why is this bad?** Negatively impacts readability.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 6186491_8973511
/// ```
declare_lint! {
    pub LARGE_DIGIT_GROUPS,
    Warn,
    "grouping digits into groups that are too large"
}

#[derive(Debug)]
enum Radix {
    Binary,
    Octal,
    Decimal,
    Hexadecimal,
}

impl Radix {
    /// Return a reasonable digit group size for this radix.
    pub fn suggest_grouping(&self) -> usize {
        match *self {
            Radix::Binary | Radix::Hexadecimal => 4,
            Radix::Octal | Radix::Decimal => 3,
        }
    }
}

#[derive(Debug)]
struct DigitInfo<'a> {
    /// Characters of a literal between the radix prefix and type suffix.
    pub digits: &'a str,
    /// Which radix the literal was represented in.
    pub radix: Radix,
    /// The radix prefix, if present.
    pub prefix: Option<&'a str>,
    /// The type suffix, including preceding underscore if present.
    pub suffix: Option<&'a str>,
    /// True for floating-point literals.
    pub float: bool,
}

impl<'a> DigitInfo<'a> {
    pub fn new(lit: &str, float: bool) -> DigitInfo {
        // Determine delimiter for radix prefix, if present, and radix.
        let radix = if lit.starts_with("0x") {
            Radix::Hexadecimal
        } else if lit.starts_with("0b") {
            Radix::Binary
        } else if lit.starts_with("0o") {
            Radix::Octal
        } else {
            Radix::Decimal
        };

        // Grab part of the literal after prefix, if present.
        let (prefix, sans_prefix) = if let Radix::Decimal = radix {
            (None, lit)
        } else {
            let (p, s) = lit.split_at(2);
            (Some(p), s)
        };

        let mut last_d = '\0';
        for (d_idx, d) in sans_prefix.char_indices() {
            if !float && (d == 'i' || d == 'u') || float && d == 'f' {
                let suffix_start = if last_d == '_' { d_idx - 1 } else { d_idx };
                let (digits, suffix) = sans_prefix.split_at(suffix_start);
                return DigitInfo {
                    digits: digits,
                    radix: radix,
                    prefix: prefix,
                    suffix: Some(suffix),
                    float: float,
                };
            }
            last_d = d
        }

        // No suffix found
        DigitInfo {
            digits: sans_prefix,
            radix: radix,
            prefix: prefix,
            suffix: None,
            float: float,
        }
    }

    /// Returns digits grouped in a sensible way.
    fn grouping_hint(&self) -> String {
        let group_size = self.radix.suggest_grouping();
        if self.digits.contains('.') {
            let mut parts = self.digits.split('.');
            let int_part_hint = parts.next()
                .unwrap()
                .chars()
                .rev()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().rev().collect())
                .rev()
                .collect::<Vec<String>>()
                .join("_");
            let frac_part_hint = parts.next()
                .unwrap()
                .chars()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().collect())
                .collect::<Vec<String>>()
                .join("_");
            format!("{}.{}{}", int_part_hint, frac_part_hint, self.suffix.unwrap_or(""))
        } else {
            let hint = self.digits
                .chars()
                .rev()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().rev().collect())
                .rev()
                .collect::<Vec<String>>()
                .join("_");
            format!("{}{}{}", self.prefix.unwrap_or(""), hint, self.suffix.unwrap_or(""))
        }
    }
}

enum WarningType {
    UnreadableLiteral,
    InconsistentDigitGrouping,
    LargeDigitGroups,
}

impl WarningType {
    pub fn display(&self, grouping_hint: &str, cx: &EarlyContext, span: &syntax_pos::Span) {
        match *self {
            WarningType::UnreadableLiteral => {
                span_help_and_lint(cx,
                                   UNREADABLE_LITERAL,
                                   *span,
                                   "long literal lacking separators",
                                   &format!("consider: {}", grouping_hint))
            },
            WarningType::LargeDigitGroups => {
                span_help_and_lint(cx,
                                   LARGE_DIGIT_GROUPS,
                                   *span,
                                   "digit groups should be smaller",
                                   &format!("consider: {}", grouping_hint))
            },
            WarningType::InconsistentDigitGrouping => {
                span_help_and_lint(cx,
                                   INCONSISTENT_DIGIT_GROUPING,
                                   *span,
                                   "digits grouped inconsistently by underscores",
                                   &format!("consider: {}", grouping_hint))
            },
        };
    }
}

#[derive(Copy, Clone)]
pub struct LiteralDigitGrouping;

impl LintPass for LiteralDigitGrouping {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNREADABLE_LITERAL, INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS)
    }
}

impl EarlyLintPass for LiteralDigitGrouping {
    fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
        if in_external_macro(cx, expr.span) {
            return;
        }

        if let ExprKind::Lit(ref lit) = expr.node {
            self.check_lit(cx, lit)
        }
    }
}

impl LiteralDigitGrouping {
    fn check_lit(&self, cx: &EarlyContext, lit: &Lit) {
        // Lint integral literals.
        if_let_chain! {[
            let LitKind::Int(..) = lit.node,
            let Some(src) = snippet_opt(cx, lit.span),
            let Some(firstch) = src.chars().next(),
            char::to_digit(firstch, 10).is_some()
        ], {
            let digit_info = DigitInfo::new(&src, false);
            let _ = LiteralDigitGrouping::do_lint(digit_info.digits).map_err(|warning_type| {
                warning_type.display(&digit_info.grouping_hint(), cx, &lit.span)
            });
        }}

        // Lint floating-point literals.
        if_let_chain! {[
            let LitKind::Float(..) = lit.node,
            let Some(src) = snippet_opt(cx, lit.span),
            let Some(firstch) = src.chars().next(),
            char::to_digit(firstch, 10).is_some()
        ], {
            let digit_info = DigitInfo::new(&src, true);
            // Separate digits into integral and fractional parts.
            let parts: Vec<&str> = digit_info
                .digits
                .split_terminator('.')
                .collect();

            // Lint integral and fractional parts separately, and then check consistency of digit
            // groups if both pass.
            let _ = LiteralDigitGrouping::do_lint(parts[0])
                .map(|integral_group_size| {
                    if parts.len() > 1 {
                        // Lint the fractional part of literal just like integral part, but reversed.
                        let fractional_part = &parts[1].chars().rev().collect::<String>();
                        let _ = LiteralDigitGrouping::do_lint(fractional_part)
                            .map(|fractional_group_size| {
                                let consistent = LiteralDigitGrouping::parts_consistent(integral_group_size, fractional_group_size, parts[0].len(), parts[1].len());
                                if !consistent {
                                    WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(), cx, &lit.span);
                                }
                            })
                            .map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(), cx, &lit.span));
                    }
                })
                .map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(), cx, &lit.span));
        }}
    }

    /// Given the sizes of the digit groups of both integral and fractional
    /// parts, and the length
    /// of both parts, determine if the digits have been grouped consistently.
    fn parts_consistent(int_group_size: usize, frac_group_size: usize, int_size: usize, frac_size: usize) -> bool {
        match (int_group_size, frac_group_size) {
            // No groups on either side of decimal point - trivially consistent.
            (0, 0) => true,
            // Integral part has grouped digits, fractional part does not.
            (_, 0) => frac_size <= int_group_size,
            // Fractional part has grouped digits, integral part does not.
            (0, _) => int_size <= frac_group_size,
            // Both parts have grouped digits. Groups should be the same size.
            (_, _) => int_group_size == frac_group_size,
        }
    }

    /// Performs lint on `digits` (no decimal point) and returns the group
    /// size on success or `WarningType` when emitting a warning.
    fn do_lint(digits: &str) -> Result<usize, WarningType> {
        // Grab underscore indices with respect to the units digit.
        let underscore_positions: Vec<usize> = digits.chars()
            .rev()
            .enumerate()
            .filter_map(|(idx, digit)| if digit == '_' { Some(idx) } else { None })
            .collect();

        if underscore_positions.is_empty() {
            // Check if literal needs underscores.
            if digits.len() > 4 {
                Err(WarningType::UnreadableLiteral)
            } else {
                Ok(0)
            }
        } else {
            // Check consistency and the sizes of the groups.
            let group_size = underscore_positions[0];
            let consistent = underscore_positions
                .windows(2)
                .all(|ps| ps[1] - ps[0] == group_size + 1)
                // number of digits to the left of the last group cannot be bigger than group size.
                && (digits.len() - underscore_positions.last().unwrap() <= group_size + 1);

            if !consistent {
                return Err(WarningType::InconsistentDigitGrouping);
            } else if group_size > 4 {
                return Err(WarningType::LargeDigitGroups);
            }
            Ok(group_size)
        }
    }
}
<commit_msg>Fix some more dogfood issues<commit_after>//! Lints concerned with the grouping of digits with underscores in integral or
//! floating-point literal expressions.

use rustc::lint::*;
use syntax::ast::*;
use syntax_pos;
use utils::{span_help_and_lint, snippet_opt, in_external_macro};

/// **What it does:** Warns if a long integral or floating-point constant does
/// not contain underscores.
///
/// **Why is this bad?** Reading long numbers is difficult without separators.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 61864918973511
/// ```
declare_lint! {
    pub UNREADABLE_LITERAL,
    Warn,
    "long integer literal without underscores"
}

/// **What it does:** Warns if an integral or floating-point constant is
/// grouped inconsistently with underscores.
///
/// **Why is this bad?** Readers may incorrectly interpret inconsistently
/// grouped digits.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 618_64_9189_73_511
/// ```
declare_lint! {
    pub INCONSISTENT_DIGIT_GROUPING,
    Warn,
    "integer literals with digits grouped inconsistently"
}

/// **What it does:** Warns if the digits of an integral or floating-point
/// constant are grouped into groups that
/// are too large.
///
/// **Why is this bad?** Negatively impacts readability.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 6186491_8973511
/// ```
declare_lint! {
    pub LARGE_DIGIT_GROUPS,
    Warn,
    "grouping digits into groups that are too large"
}

#[derive(Debug)]
enum Radix {
    Binary,
    Octal,
    Decimal,
    Hexadecimal,
}

impl Radix {
    /// Return a reasonable digit group size for this radix.
    pub fn suggest_grouping(&self) -> usize {
        match *self {
            Radix::Binary | Radix::Hexadecimal => 4,
            Radix::Octal | Radix::Decimal => 3,
        }
    }
}

#[derive(Debug)]
struct DigitInfo<'a> {
    /// Characters of a literal between the radix prefix and type suffix.
    pub digits: &'a str,
    /// Which radix the literal was represented in.
    pub radix: Radix,
    /// The radix prefix, if present.
    pub prefix: Option<&'a str>,
    /// The type suffix, including preceding underscore if present.
    pub suffix: Option<&'a str>,
    /// True for floating-point literals.
    pub float: bool,
}

impl<'a> DigitInfo<'a> {
    pub fn new(lit: &str, float: bool) -> DigitInfo {
        // Determine delimiter for radix prefix, if present, and radix.
        let radix = if lit.starts_with("0x") {
            Radix::Hexadecimal
        } else if lit.starts_with("0b") {
            Radix::Binary
        } else if lit.starts_with("0o") {
            Radix::Octal
        } else {
            Radix::Decimal
        };

        // Grab part of the literal after prefix, if present.
        let (prefix, sans_prefix) = if let Radix::Decimal = radix {
            (None, lit)
        } else {
            let (p, s) = lit.split_at(2);
            (Some(p), s)
        };

        let mut last_d = '\0';
        for (d_idx, d) in sans_prefix.char_indices() {
            if !float && (d == 'i' || d == 'u') || float && d == 'f' {
                let suffix_start = if last_d == '_' { d_idx - 1 } else { d_idx };
                let (digits, suffix) = sans_prefix.split_at(suffix_start);
                return DigitInfo {
                    digits: digits,
                    radix: radix,
                    prefix: prefix,
                    suffix: Some(suffix),
                    float: float,
                };
            }
            last_d = d
        }

        // No suffix found
        DigitInfo {
            digits: sans_prefix,
            radix: radix,
            prefix: prefix,
            suffix: None,
            float: float,
        }
    }

    /// Returns digits grouped in a sensible way.
    fn grouping_hint(&self) -> String {
        let group_size = self.radix.suggest_grouping();
        if self.digits.contains('.') {
            let mut parts = self.digits.split('.');
            let int_part_hint = parts.next()
                .expect("split always returns at least one element")
                .chars()
                .rev()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().rev().collect())
                .rev()
                .collect::<Vec<String>>()
                .join("_");
            let frac_part_hint = parts.next()
                .expect("already checked that there is a `.`")
                .chars()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().collect())
                .collect::<Vec<String>>()
                .join("_");
            format!("{}.{}{}", int_part_hint, frac_part_hint, self.suffix.unwrap_or(""))
        } else {
            let hint = self.digits
                .chars()
                .rev()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().rev().collect())
                .rev()
                .collect::<Vec<String>>()
                .join("_");
            format!("{}{}{}", self.prefix.unwrap_or(""), hint, self.suffix.unwrap_or(""))
        }
    }
}

enum WarningType {
    UnreadableLiteral,
    InconsistentDigitGrouping,
    LargeDigitGroups,
}

impl WarningType {
    pub fn display(&self, grouping_hint: &str, cx: &EarlyContext, span: &syntax_pos::Span) {
        match *self {
            WarningType::UnreadableLiteral => {
                span_help_and_lint(cx,
                                   UNREADABLE_LITERAL,
                                   *span,
                                   "long literal lacking separators",
                                   &format!("consider: {}", grouping_hint))
            },
            WarningType::LargeDigitGroups => {
                span_help_and_lint(cx,
                                   LARGE_DIGIT_GROUPS,
                                   *span,
                                   "digit groups should be smaller",
                                   &format!("consider: {}", grouping_hint))
            },
            WarningType::InconsistentDigitGrouping => {
                span_help_and_lint(cx,
                                   INCONSISTENT_DIGIT_GROUPING,
                                   *span,
                                   "digits grouped inconsistently by underscores",
                                   &format!("consider: {}", grouping_hint))
            },
        };
    }
}

#[derive(Copy, Clone)]
pub struct LiteralDigitGrouping;

impl LintPass for LiteralDigitGrouping {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNREADABLE_LITERAL, INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS)
    }
}

impl EarlyLintPass for LiteralDigitGrouping {
    fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
        if in_external_macro(cx, expr.span) {
            return;
        }

        if let ExprKind::Lit(ref lit) = expr.node {
            self.check_lit(cx, lit)
        }
    }
}

impl LiteralDigitGrouping {
    fn check_lit(&self, cx: &EarlyContext, lit: &Lit) {
        // Lint integral literals.
        if_let_chain! {[
            let LitKind::Int(..) = lit.node,
            let Some(src) = snippet_opt(cx, lit.span),
            let Some(firstch) = src.chars().next(),
            char::to_digit(firstch, 10).is_some()
        ], {
            let digit_info = DigitInfo::new(&src, false);
            let _ = LiteralDigitGrouping::do_lint(digit_info.digits).map_err(|warning_type| {
                warning_type.display(&digit_info.grouping_hint(), cx, &lit.span)
            });
        }}

        // Lint floating-point literals.
        if_let_chain! {[
            let LitKind::Float(..) = lit.node,
            let Some(src) = snippet_opt(cx, lit.span),
            let Some(firstch) = src.chars().next(),
            char::to_digit(firstch, 10).is_some()
        ], {
            let digit_info = DigitInfo::new(&src, true);
            // Separate digits into integral and fractional parts.
            let parts: Vec<&str> = digit_info
                .digits
                .split_terminator('.')
                .collect();

            // Lint integral and fractional parts separately, and then check consistency of digit
            // groups if both pass.
            let _ = LiteralDigitGrouping::do_lint(parts[0])
                .map(|integral_group_size| {
                    if parts.len() > 1 {
                        // Lint the fractional part of literal just like integral part, but reversed.
                        let fractional_part = &parts[1].chars().rev().collect::<String>();
                        let _ = LiteralDigitGrouping::do_lint(fractional_part)
                            .map(|fractional_group_size| {
                                let consistent = LiteralDigitGrouping::parts_consistent(integral_group_size, fractional_group_size, parts[0].len(), parts[1].len());
                                if !consistent {
                                    WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(), cx, &lit.span);
                                }
                            })
                            .map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(), cx, &lit.span));
                    }
                })
                .map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(), cx, &lit.span));
        }}
    }

    /// Given the sizes of the digit groups of both integral and fractional
    /// parts, and the length
    /// of both parts, determine if the digits have been grouped consistently.
    fn parts_consistent(int_group_size: usize, frac_group_size: usize, int_size: usize, frac_size: usize) -> bool {
        match (int_group_size, frac_group_size) {
            // No groups on either side of decimal point - trivially consistent.
            (0, 0) => true,
            // Integral part has grouped digits, fractional part does not.
            (_, 0) => frac_size <= int_group_size,
            // Fractional part has grouped digits, integral part does not.
            (0, _) => int_size <= frac_group_size,
            // Both parts have grouped digits. Groups should be the same size.
            (_, _) => int_group_size == frac_group_size,
        }
    }

    /// Performs lint on `digits` (no decimal point) and returns the group
    /// size on success or `WarningType` when emitting a warning.
    fn do_lint(digits: &str) -> Result<usize, WarningType> {
        // Grab underscore indices with respect to the units digit.
        let underscore_positions: Vec<usize> = digits.chars()
            .rev()
            .enumerate()
            .filter_map(|(idx, digit)| if digit == '_' { Some(idx) } else { None })
            .collect();

        if underscore_positions.is_empty() {
            // Check if literal needs underscores.
            if digits.len() > 4 {
                Err(WarningType::UnreadableLiteral)
            } else {
                Ok(0)
            }
        } else {
            // Check consistency and the sizes of the groups.
            let group_size = underscore_positions[0];
            let consistent = underscore_positions
                .windows(2)
                .all(|ps| ps[1] - ps[0] == group_size + 1)
                // number of digits to the left of the last group cannot be bigger than group size.
                && (digits.len() - underscore_positions.last().expect("there's at least one element") <= group_size + 1);

            if !consistent {
                return Err(WarningType::InconsistentDigitGrouping);
            } else if group_size > 4 {
                return Err(WarningType::LargeDigitGroups);
            }
            Ok(group_size)
        }
    }
}
<|endoftext|>