<commit_before>import syntax::{visit, ast_util};
import syntax::ast::*;
import syntax::codemap::span;
import std::list::{is_not_empty, list, nil, cons, tail};
import core::unreachable;
import std::list;
import std::map::hashmap;

// Last use analysis pass.
//
// Finds the last read of each value stored in a local variable or
// callee-owned argument (arguments with by-move or by-copy passing
// style). This is a limited form of liveness analysis, peformed
// (perhaps foolishly) directly on the AST.
//
// The algorithm walks the AST, keeping a set of (def, last_use)
// pairs. When the function is exited, or the local is overwritten,
// the current set of last uses is marked with 'true' in a table.
// Other branches may later overwrite them with 'false' again, since
// they may find a use coming after them. (Marking an expression as a
// last use is only done if it has not already been marked with
// 'false'.)
//
// Some complexity is added to deal with joining control flow branches
// (by `break` or conditionals), and for handling loops.

// Marks expr_paths that are last uses.
enum is_last_use {
    is_last_use,
    has_last_use,
    closes_over([node_id]),
}
type last_uses = std::map::hashmap<node_id, is_last_use>;

enum seen { unset, seen(node_id), }
enum block_type { func, lp, }

enum use { var_use(node_id), close_over(node_id), }
type set = [{def: node_id, uses: list<use>}];
type bl = @{type: block_type, mutable second: bool, mutable exits: [set]};

enum use_id { path(node_id), close(node_id, node_id) }
fn hash_use_id(id: use_id) -> uint {
    (alt id { path(i) { i } close(i, j) { (i << 10) + j } }) as uint
}

type ctx = {last_uses: std::map::hashmap<use_id, bool>,
            def_map: resolve::def_map,
            ref_map: alias::ref_map,
            tcx: ty::ctxt,
            // The current set of local last uses
            mutable current: set,
            mutable blocks: list<bl>};

fn find_last_uses(c: @crate, def_map: resolve::def_map,
                  ref_map: alias::ref_map, tcx: ty::ctxt) -> last_uses {
    let v = visit::mk_vt(@{visit_expr: visit_expr,
                           visit_stmt: visit_stmt,
                           visit_fn: visit_fn
                           with *visit::default_visitor()});
    let cx = {last_uses: std::map::hashmap(hash_use_id, {|a, b| a == b}),
              def_map: def_map,
              ref_map: ref_map,
              tcx: tcx,
              mutable current: [],
              mutable blocks: nil};
    visit::visit_crate(*c, cx, v);
    let mini_table = std::map::int_hash();
    cx.last_uses.items {|key, val|
        if !val { ret; }
        alt key {
          path(id) {
            mini_table.insert(id, is_last_use);
            let def_node = ast_util::def_id_of_def(def_map.get(id)).node;
            mini_table.insert(def_node, has_last_use);
          }
          close(fn_id, local_id) {
            mini_table.insert(local_id, has_last_use);
            let known = alt check mini_table.find(fn_id) {
              some(closes_over(ids)) { ids }
              none { [] }
            };
            mini_table.insert(fn_id, closes_over(known + [local_id]));
          }
        }
    }
    ret mini_table;
}

fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
    alt ex.node {
      expr_ret(oexpr) {
        visit::visit_expr_opt(oexpr, cx, v);
        if !add_block_exit(cx, func) { leave_fn(cx); }
      }
      expr_fail(oexpr) {
        visit::visit_expr_opt(oexpr, cx, v);
        leave_fn(cx);
      }
      expr_break { add_block_exit(cx, lp); }
      expr_while(_, _) | expr_do_while(_, _) | expr_loop(_) {
        visit_block(lp, cx) {|| visit::visit_expr(ex, cx, v);}
      }
      expr_for(_, coll, blk) {
        v.visit_expr(coll, cx, v);
        visit_block(lp, cx) {|| visit::visit_block(blk, cx, v);}
      }
      expr_alt(input, arms, _) {
        v.visit_expr(input, cx, v);
        let before = cx.current, sets = [];
        for arm in arms {
            cx.current = before;
            v.visit_arm(arm, cx, v);
            sets += [cx.current];
        }
        cx.current = join_branches(sets);
      }
      expr_if(cond, then, els) {
        v.visit_expr(cond, cx, v);
        let cur = cx.current;
        visit::visit_block(then, cx, v);
        cx.current <-> cur;
        visit::visit_expr_opt(els, cx, v);
        cx.current = join_branches([cur, cx.current]);
      }
      expr_path(_) {
        let my_def = cx.def_map.get(ex.id);
        let my_def_id = ast_util::def_id_of_def(my_def).node;
        alt cx.ref_map.find(my_def_id) {
          option::some(root_id) {
            clear_in_current(cx, root_id, false);
          }
          _ {
            option::may(def_is_owned_local(cx, my_def)) {|nid|
                clear_in_current(cx, nid, false);
                cx.current += [{def: nid,
                                uses: cons(var_use(ex.id), @nil)}];
            }
          }
        }
      }
      expr_swap(lhs, rhs) {
        clear_if_path(cx, lhs, v, false);
        clear_if_path(cx, rhs, v, false);
      }
      expr_move(dest, src) | expr_assign(dest, src) {
        v.visit_expr(src, cx, v);
        clear_if_path(cx, dest, v, true);
      }
      expr_assign_op(_, dest, src) {
        v.visit_expr(src, cx, v);
        v.visit_expr(dest, cx, v);
        clear_if_path(cx, dest, v, true);
      }
      expr_fn(_, _, _, cap_clause) {
        // n.b.: safe to ignore copies, as if they are unused
        // then they are ignored, otherwise they will show up
        // as freevars in the body.
        vec::iter(cap_clause.moves) {|ci|
            clear_def_if_local(cx, cx.def_map.get(ci.id), false);
        }
        visit::visit_expr(ex, cx, v);
      }
      expr_call(f, args, _) {
        v.visit_expr(f, cx, v);
        let i = 0u, fns = [];
        let arg_ts = ty::ty_fn_args(ty::expr_ty(cx.tcx, f));
        for arg in args {
            alt arg.node {
              expr_fn(_, _, _, _) | expr_fn_block(_, _)
              if is_blockish(ty::ty_fn_proto(arg_ts[i].ty)) {
                fns += [arg];
              }
              _ {
                alt ty::arg_mode(cx.tcx, arg_ts[i]) {
                  by_mutbl_ref { clear_if_path(cx, arg, v, false); }
                  _ { v.visit_expr(arg, cx, v); }
                }
              }
            }
            i += 1u;
        }
        for f in fns { v.visit_expr(f, cx, v); }
      }
      _ { visit::visit_expr(ex, cx, v); }
    }
}

fn visit_stmt(s: @stmt, cx: ctx, v: visit::vt<ctx>) {
    alt s.node {
      stmt_decl(@{node: decl_local(ls), _}, _) {
        shadow_in_current(cx, {|id|
            for local in ls {
                let found = false;
                pat_util::pat_bindings(cx.tcx.def_map, local.node.pat,
                                       {|pid, _a, _b|
                    if pid == id { found = true; }
                });
                if found { ret true; }
            }
            false
        });
      }
      _ {}
    }
    visit::visit_stmt(s, cx, v);
}

fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
            sp: span, id: node_id,
            cx: ctx, v: visit::vt<ctx>) {
    let fty = ty::node_id_to_type(cx.tcx, id);
    let proto = ty::ty_fn_proto(fty);
    alt proto {
      proto_any | proto_block {
        visit_block(func, cx, {||
            shadow_in_current(cx, {|id|
                for arg in decl.inputs { if arg.id == id { ret true; } }
                false
            });
            visit::visit_fn(fk, decl, body, sp, id, cx, v);
        });
      }
      proto_box | proto_uniq | proto_bare {
        alt cx.tcx.freevars.find(id) {
          some(vars) {
            for v in *vars {
                option::may(def_is_owned_local(cx, v.def)) {|nid|
                    clear_in_current(cx, nid, false);
                    cx.current += [{def: nid,
                                    uses: cons(close_over(id), @nil)}];
                }
            }
          }
          _ {}
        }
        let old_cur = [], old_blocks = nil;
        cx.blocks <-> old_blocks;
        cx.current <-> old_cur;
        visit::visit_fn(fk, decl, body, sp, id, cx, v);
        cx.blocks <-> old_blocks;
        leave_fn(cx);
        cx.current <-> old_cur;
      }
    }
}

fn visit_block(tp: block_type, cx: ctx, visit: fn()) {
    let local = @{type: tp, mutable second: false, mutable exits: []};
    cx.blocks = cons(local, @cx.blocks);
    visit();
    local.second = true;
    local.exits = [];
    visit();
    let cx_blocks = cx.blocks;
    cx.blocks = tail(cx_blocks);
    local.exits += [cx.current];
    cx.current = join_branches(local.exits);
}

fn add_block_exit(cx: ctx, tp: block_type) -> bool {
    let cur = cx.blocks;
    while cur != nil {
        alt cur {
          cons(b, tail) {
            if (b.type == tp) {
                if !b.second { b.exits += [cx.current]; }
                ret true;
            }
            cur = *tail;
          }
          nil {
            // typestate can't use the while loop condition --
            // *sigh*
            unreachable();
          }
        }
    }
    ret false;
}

fn join_branches(branches: [set]) -> set {
    let found: set = [], i = 0u, l = vec::len(branches);
    for set in branches {
        i += 1u;
        for {def, uses} in set {
            if !vec::any(found, {|v| v.def == def}) {
                let j = i, nne = uses;
                while j < l {
                    for {def: d2, uses} in branches[j] {
                        if d2 == def {
                            list::iter(uses) {|e|
                                if !list::has(nne, e) { nne = cons(e, @nne); }
                            }
                        }
                    }
                    j += 1u;
                }
                found += [{def: def, uses: nne}];
            }
        }
    }
    ret found;
}

fn leave_fn(cx: ctx) {
    for {def, uses} in cx.current {
        list::iter(uses) {|use|
            let key = alt use {
              var_use(pth_id) { path(pth_id) }
              close_over(fn_id) { close(fn_id, def) }
            };
            if !cx.last_uses.contains_key(key) {
                cx.last_uses.insert(key, true);
            }
        }
    }
}

fn shadow_in_current(cx: ctx, p: fn(node_id) -> bool) {
    let out = [];
    cx.current <-> out;
    for e in out { if !p(e.def) { cx.current += [e]; } }
}

fn clear_in_current(cx: ctx, my_def: node_id, to: bool) {
    for {def, uses} in cx.current {
        if def == my_def {
            list::iter(uses) {|use|
                let key = alt use {
                  var_use(pth_id) { path(pth_id) }
                  close_over(fn_id) { close(fn_id, def) }
                };
                if !to || !cx.last_uses.contains_key(key) {
                    cx.last_uses.insert(key, to);
                }
            }
            cx.current = vec::filter(copy cx.current, {|x| x.def != my_def});
            break;
        }
    }
}

fn def_is_owned_local(cx: ctx, d: def) -> option<node_id> {
    alt d {
      def_local(id, _) { some(id) }
      def_arg(id, m) {
        alt ty::resolved_mode(cx.tcx, m) {
          by_copy | by_move { some(id) }
          by_ref | by_val | by_mutbl_ref { none }
        }
      }
      def_upvar(_, d, fn_id) {
        if is_blockish(ty::ty_fn_proto(ty::node_id_to_type(cx.tcx, fn_id))) {
            def_is_owned_local(cx, *d)
        } else { none }
      }
      _ { none }
    }
}

fn clear_def_if_local(cx: ctx, d: def, to: bool) {
    alt def_is_owned_local(cx, d) {
      some(nid) { clear_in_current(cx, nid, to); }
      _ {}
    }
}

fn clear_if_path(cx: ctx, ex: @expr, v: visit::vt<ctx>, to: bool) {
    alt ex.node {
      expr_path(_) { clear_def_if_local(cx, cx.def_map.get(ex.id), to); }
      _ { v.visit_expr(ex, cx, v); }
    }
}
<commit_msg>Make last-use finder treat referenced function arguments properly<commit_after>import syntax::{visit, ast_util};
import syntax::ast::*;
import syntax::codemap::span;
import std::list::{is_not_empty, list, nil, cons, tail};
import core::unreachable;
import std::list;
import std::map::hashmap;

// Last use analysis pass.
//
// Finds the last read of each value stored in a local variable or
// callee-owned argument (arguments with by-move or by-copy passing
// style). This is a limited form of liveness analysis, peformed
// (perhaps foolishly) directly on the AST.
//
// The algorithm walks the AST, keeping a set of (def, last_use)
// pairs. When the function is exited, or the local is overwritten,
// the current set of last uses is marked with 'true' in a table.
// Other branches may later overwrite them with 'false' again, since
// they may find a use coming after them. (Marking an expression as a
// last use is only done if it has not already been marked with
// 'false'.)
//
// Some complexity is added to deal with joining control flow branches
// (by `break` or conditionals), and for handling loops.

// Marks expr_paths that are last uses.
enum is_last_use {
    is_last_use,
    has_last_use,
    closes_over([node_id]),
}
type last_uses = std::map::hashmap<node_id, is_last_use>;

enum seen { unset, seen(node_id), }
enum block_type { func, lp, }

enum use { var_use(node_id), close_over(node_id), }
type set = [{def: node_id, uses: list<use>}];
type bl = @{type: block_type, mutable second: bool, mutable exits: [set]};

enum use_id { path(node_id), close(node_id, node_id) }
fn hash_use_id(id: use_id) -> uint {
    (alt id { path(i) { i } close(i, j) { (i << 10) + j } }) as uint
}

type ctx = {last_uses: std::map::hashmap<use_id, bool>,
            def_map: resolve::def_map,
            ref_map: alias::ref_map,
            tcx: ty::ctxt,
            // The current set of local last uses
            mutable current: set,
            mutable blocks: list<bl>};

fn find_last_uses(c: @crate, def_map: resolve::def_map,
                  ref_map: alias::ref_map, tcx: ty::ctxt) -> last_uses {
    let v = visit::mk_vt(@{visit_expr: visit_expr,
                           visit_stmt: visit_stmt,
                           visit_fn: visit_fn
                           with *visit::default_visitor()});
    let cx = {last_uses: std::map::hashmap(hash_use_id, {|a, b| a == b}),
              def_map: def_map,
              ref_map: ref_map,
              tcx: tcx,
              mutable current: [],
              mutable blocks: nil};
    visit::visit_crate(*c, cx, v);
    let mini_table = std::map::int_hash();
    cx.last_uses.items {|key, val|
        if !val { ret; }
        alt key {
          path(id) {
            mini_table.insert(id, is_last_use);
            let def_node = ast_util::def_id_of_def(def_map.get(id)).node;
            mini_table.insert(def_node, has_last_use);
          }
          close(fn_id, local_id) {
            mini_table.insert(local_id, has_last_use);
            let known = alt check mini_table.find(fn_id) {
              some(closes_over(ids)) { ids }
              none { [] }
            };
            mini_table.insert(fn_id, closes_over(known + [local_id]));
          }
        }
    }
    ret mini_table;
}

fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
    alt ex.node {
      expr_ret(oexpr) {
        visit::visit_expr_opt(oexpr, cx, v);
        if !add_block_exit(cx, func) { leave_fn(cx); }
      }
      expr_fail(oexpr) {
        visit::visit_expr_opt(oexpr, cx, v);
        leave_fn(cx);
      }
      expr_break { add_block_exit(cx, lp); }
      expr_while(_, _) | expr_do_while(_, _) | expr_loop(_) {
        visit_block(lp, cx) {|| visit::visit_expr(ex, cx, v);}
      }
      expr_for(_, coll, blk) {
        v.visit_expr(coll, cx, v);
        visit_block(lp, cx) {|| visit::visit_block(blk, cx, v);}
      }
      expr_alt(input, arms, _) {
        v.visit_expr(input, cx, v);
        let before = cx.current, sets = [];
        for arm in arms {
            cx.current = before;
            v.visit_arm(arm, cx, v);
            sets += [cx.current];
        }
        cx.current = join_branches(sets);
      }
      expr_if(cond, then, els) {
        v.visit_expr(cond, cx, v);
        let cur = cx.current;
        visit::visit_block(then, cx, v);
        cx.current <-> cur;
        visit::visit_expr_opt(els, cx, v);
        cx.current = join_branches([cur, cx.current]);
      }
      expr_path(_) {
        let my_def = cx.def_map.get(ex.id);
        let my_def_id = ast_util::def_id_of_def(my_def).node;
        alt cx.ref_map.find(my_def_id) {
          option::some(root_id) {
            clear_in_current(cx, root_id, false);
          }
          _ {
            option::may(def_is_owned_local(cx, my_def)) {|nid|
                clear_in_current(cx, nid, false);
                cx.current += [{def: nid,
                                uses: cons(var_use(ex.id), @nil)}];
            }
          }
        }
      }
      expr_swap(lhs, rhs) {
        clear_if_path(cx, lhs, v, false);
        clear_if_path(cx, rhs, v, false);
      }
      expr_move(dest, src) | expr_assign(dest, src) {
        v.visit_expr(src, cx, v);
        clear_if_path(cx, dest, v, true);
      }
      expr_assign_op(_, dest, src) {
        v.visit_expr(src, cx, v);
        v.visit_expr(dest, cx, v);
        clear_if_path(cx, dest, v, true);
      }
      expr_fn(_, _, _, cap_clause) {
        // n.b.: safe to ignore copies, as if they are unused
        // then they are ignored, otherwise they will show up
        // as freevars in the body.
        vec::iter(cap_clause.moves) {|ci|
            clear_def_if_local(cx, cx.def_map.get(ci.id), false);
        }
        visit::visit_expr(ex, cx, v);
      }
      expr_call(f, args, _) {
        v.visit_expr(f, cx, v);
        let fns = [];
        let arg_ts = ty::ty_fn_args(ty::expr_ty(cx.tcx, f));
        vec::iter2(args, arg_ts) {|arg, arg_t|
            alt arg.node {
              expr_fn(_, _, _, _) | expr_fn_block(_, _)
              if is_blockish(ty::ty_fn_proto(arg_t.ty)) {
                fns += [arg];
              }
              _ {
                alt ty::arg_mode(cx.tcx, arg_t) {
                  by_mutbl_ref { clear_if_path(cx, arg, v, false); }
                  _ { v.visit_expr(arg, cx, v); }
                }
              }
            }
        }
        for f in fns { v.visit_expr(f, cx, v); }
        vec::iter2(args, arg_ts) {|arg, arg_t|
            alt arg.node {
              expr_path(_) {
                alt ty::arg_mode(cx.tcx, arg_t) {
                  by_ref | by_val | by_mutbl_ref {
                    clear_if_path(cx, arg, v, false);
                  }
                  _ {}
                }
              }
              _ {}
            }
        }
      }
      _ { visit::visit_expr(ex, cx, v); }
    }
}

fn visit_stmt(s: @stmt, cx: ctx, v: visit::vt<ctx>) {
    alt s.node {
      stmt_decl(@{node: decl_local(ls), _}, _) {
        shadow_in_current(cx, {|id|
            for local in ls {
                let found = false;
                pat_util::pat_bindings(cx.tcx.def_map, local.node.pat,
                                       {|pid, _a, _b|
                    if pid == id { found = true; }
                });
                if found { ret true; }
            }
            false
        });
      }
      _ {}
    }
    visit::visit_stmt(s, cx, v);
}

fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
            sp: span, id: node_id,
            cx: ctx, v: visit::vt<ctx>) {
    let fty = ty::node_id_to_type(cx.tcx, id);
    let proto = ty::ty_fn_proto(fty);
    alt proto {
      proto_any | proto_block {
        visit_block(func, cx, {||
            shadow_in_current(cx, {|id|
                for arg in decl.inputs { if arg.id == id { ret true; } }
                false
            });
            visit::visit_fn(fk, decl, body, sp, id, cx, v);
        });
      }
      proto_box | proto_uniq | proto_bare {
        alt cx.tcx.freevars.find(id) {
          some(vars) {
            for v in *vars {
                option::may(def_is_owned_local(cx, v.def)) {|nid|
                    clear_in_current(cx, nid, false);
                    cx.current += [{def: nid,
                                    uses: cons(close_over(id), @nil)}];
                }
            }
          }
          _ {}
        }
        let old_cur = [], old_blocks = nil;
        cx.blocks <-> old_blocks;
        cx.current <-> old_cur;
        visit::visit_fn(fk, decl, body, sp, id, cx, v);
        cx.blocks <-> old_blocks;
        leave_fn(cx);
        cx.current <-> old_cur;
      }
    }
}

fn visit_block(tp: block_type, cx: ctx, visit: fn()) {
    let local = @{type: tp, mutable second: false, mutable exits: []};
    cx.blocks = cons(local, @cx.blocks);
    visit();
    local.second = true;
    local.exits = [];
    visit();
    let cx_blocks = cx.blocks;
    cx.blocks = tail(cx_blocks);
    local.exits += [cx.current];
    cx.current = join_branches(local.exits);
}

fn add_block_exit(cx: ctx, tp: block_type) -> bool {
    let cur = cx.blocks;
    while cur != nil {
        alt cur {
          cons(b, tail) {
            if (b.type == tp) {
                if !b.second { b.exits += [cx.current]; }
                ret true;
            }
            cur = *tail;
          }
          nil {
            // typestate can't use the while loop condition --
            // *sigh*
            unreachable();
          }
        }
    }
    ret false;
}

fn join_branches(branches: [set]) -> set {
    let found: set = [], i = 0u, l = vec::len(branches);
    for set in branches {
        i += 1u;
        for {def, uses} in set {
            if !vec::any(found, {|v| v.def == def}) {
                let j = i, nne = uses;
                while j < l {
                    for {def: d2, uses} in branches[j] {
                        if d2 == def {
                            list::iter(uses) {|e|
                                if !list::has(nne, e) { nne = cons(e, @nne); }
                            }
                        }
                    }
                    j += 1u;
                }
                found += [{def: def, uses: nne}];
            }
        }
    }
    ret found;
}

fn leave_fn(cx: ctx) {
    for {def, uses} in cx.current {
        list::iter(uses) {|use|
            let key = alt use {
              var_use(pth_id) { path(pth_id) }
              close_over(fn_id) { close(fn_id, def) }
            };
            if !cx.last_uses.contains_key(key) {
                cx.last_uses.insert(key, true);
            }
        }
    }
}

fn shadow_in_current(cx: ctx, p: fn(node_id) -> bool) {
    let out = [];
    cx.current <-> out;
    for e in out { if !p(e.def) { cx.current += [e]; } }
}

fn clear_in_current(cx: ctx, my_def: node_id, to: bool) {
    for {def, uses} in cx.current {
        if def == my_def {
            list::iter(uses) {|use|
                let key = alt use {
                  var_use(pth_id) { path(pth_id) }
                  close_over(fn_id) { close(fn_id, def) }
                };
                if !to || !cx.last_uses.contains_key(key) {
                    cx.last_uses.insert(key, to);
                }
            }
            cx.current = vec::filter(copy cx.current, {|x| x.def != my_def});
            break;
        }
    }
}

fn def_is_owned_local(cx: ctx, d: def) -> option<node_id> {
    alt d {
      def_local(id, _) { some(id) }
      def_arg(id, m) {
        alt ty::resolved_mode(cx.tcx, m) {
          by_copy | by_move { some(id) }
          by_ref | by_val | by_mutbl_ref { none }
        }
      }
      def_upvar(_, d, fn_id) {
        if is_blockish(ty::ty_fn_proto(ty::node_id_to_type(cx.tcx, fn_id))) {
            def_is_owned_local(cx, *d)
        } else { none }
      }
      _ { none }
    }
}

fn clear_def_if_local(cx: ctx, d: def, to: bool) {
    alt def_is_owned_local(cx, d) {
      some(nid) { clear_in_current(cx, nid, to); }
      _ {}
    }
}

fn clear_if_path(cx: ctx, ex: @expr, v: visit::vt<ctx>, to: bool) {
    alt ex.node {
      expr_path(_) { clear_def_if_local(cx, cx.def_map.get(ex.id), to); }
      _ { v.visit_expr(ex, cx, v); }
    }
}
<|endoftext|>


<commit_before>#[cfg(feature = "std")]
extern crate std;

use ParseError;

impl std::error::Error for ParseError {
    fn description(&self) -> &str {
        "UUID parse error"
    }
}
<commit_msg>refactor: remove unnecessary cfg<commit_after>extern crate std;

use ParseError;

impl std::error::Error for ParseError {
    fn description(&self) -> &str {
        "UUID parse error"
    }
}
<|endoftext|>


<commit_before>use std::collections::VecDeque;

use languageserver_types::TextDocumentContentChangeEvent;

use gluon::base::source;
use gluon::base::pos::Span;

use rpc::ServerError;
use range_to_byte_span;

struct VersionedChange {
    version: u64,
    content_changes: Vec<TextDocumentContentChangeEvent>,
}

/// Type which applies text changes in the order that the client sends them.
/// Out of order changes are stored until all earlier changes have been received after which they
/// are applied all at once.
pub struct TextChanges {
    changes: VecDeque<VersionedChange>,
}

impl TextChanges {
    pub fn new() -> Self {
        TextChanges {
            changes: VecDeque::new(),
        }
    }

    pub fn add(&mut self, version: u64, content_changes: Vec<TextDocumentContentChangeEvent>) {
        let i = self.changes
            .iter()
            .position(|change| change.version > version)
            .unwrap_or(self.changes.len());
        self.changes.insert(
            i,
            VersionedChange {
                version,
                content_changes,
            },
        );
    }

    pub fn apply_changes(
        &mut self,
        source: &mut String,
        mut version: u64,
    ) -> Result<u64, ServerError<()>> {
        while let Some(change) = self.changes.pop_front() {
            assert!(
                (change.version == version && change.content_changes.is_empty())
                    || change.version > version,
                "BUG: Attempt to apply old change on newer contents"
            );
            if version + 1 != change.version {
                self.changes.push_front(change);
                break;
            }
            version += 1;
            apply_changes(source, &change.content_changes)?
        }
        Ok(version)
    }
}

fn apply_changes(
    source: &mut String,
    content_changes: &[TextDocumentContentChangeEvent],
) -> Result<(), ServerError<()>> {
    for change in content_changes {
        let lines = source::Lines::new(source.as_bytes().iter().cloned());
        apply_change(source, &lines, change)?;
    }
    Ok(())
}

fn apply_change(
    source: &mut String,
    lines: &source::Lines,
    change: &TextDocumentContentChangeEvent,
) -> Result<(), ServerError<()>> {
    info!("Applying change: {:?}", change);
    let span = match (change.range, change.range_length) {
        (None, None) => Span::new(0.into(), source.len().into()),
        (Some(range), None) | (Some(range), Some(_)) => range_to_byte_span(lines, &range)?,
        (None, Some(_)) => return Err("Invalid change".into()),
    };
    source.drain(span.start.to_usize()..span.end.to_usize());
    source.insert_str(span.start.to_usize(), &change.text);
    Ok(())
}

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

    use languageserver_types::{Position, Range};

    #[test]
    fn apply_changes_test() {
        let mut source = String::new();
        apply_changes(
            &mut source,
            &[
                TextDocumentContentChangeEvent {
                    range: Some(Range {
                        start: Position {
                            line: 0,
                            character: 0,
                        },
                        end: Position {
                            line: 0,
                            character: 0,
                        },
                    }),
                    range_length: None,
                    text: "test".to_string(),
                },
            ],
        ).unwrap();

        assert_eq!(source, "test");

        apply_changes(
            &mut source,
            &[
                TextDocumentContentChangeEvent {
                    range: Some(Range {
                        start: Position {
                            line: 0,
                            character: 2,
                        },
                        end: Position {
                            line: 0,
                            character: 3,
                        },
                    }),
                    range_length: Some(1),
                    text: "".to_string(),
                },
            ],
        ).unwrap();

        assert_eq!(source, "tet");

        apply_changes(
            &mut source,
            &[
                TextDocumentContentChangeEvent {
                    range: Some(Range {
                        start: Position {
                            line: 0,
                            character: 2,
                        },
                        end: Position {
                            line: 0,
                            character: 3,
                        },
                    }),
                    range_length: Some(2),
                    text: "ab".to_string(),
                },
            ],
        ).unwrap();

        assert_eq!(source, "teab");
    }
}
<commit_msg>Handle empty changes better<commit_after>use std::collections::VecDeque;

use languageserver_types::TextDocumentContentChangeEvent;

use gluon::base::source;
use gluon::base::pos::Span;

use rpc::ServerError;
use range_to_byte_span;

struct VersionedChange {
    version: u64,
    content_changes: Vec<TextDocumentContentChangeEvent>,
}

/// Type which applies text changes in the order that the client sends them.
/// Out of order changes are stored until all earlier changes have been received after which they
/// are applied all at once.
pub struct TextChanges {
    changes: VecDeque<VersionedChange>,
}

impl TextChanges {
    pub fn new() -> Self {
        TextChanges {
            changes: VecDeque::new(),
        }
    }

    pub fn add(&mut self, version: u64, content_changes: Vec<TextDocumentContentChangeEvent>) {
        let i = self.changes
            .iter()
            .position(|change| change.version > version)
            .unwrap_or(self.changes.len());
        self.changes.insert(
            i,
            VersionedChange {
                version,
                content_changes,
            },
        );
    }

    pub fn apply_changes(
        &mut self,
        source: &mut String,
        mut version: u64,
    ) -> Result<u64, ServerError<()>> {
        while let Some(change) = self.changes.pop_front() {
            assert!(
                (change.version == version && change.content_changes.is_empty())
                    || change.version > version,
                "BUG: Attempt to apply old change on newer contents"
            );
            if change.content_changes.is_empty() {
                continue;
            }
            if version + 1 != change.version {
                self.changes.push_front(change);
                break;
            }
            version += 1;
            apply_changes(source, &change.content_changes)?
        }
        Ok(version)
    }
}

fn apply_changes(
    source: &mut String,
    content_changes: &[TextDocumentContentChangeEvent],
) -> Result<(), ServerError<()>> {
    for change in content_changes {
        let lines = source::Lines::new(source.as_bytes().iter().cloned());
        apply_change(source, &lines, change)?;
    }
    Ok(())
}

fn apply_change(
    source: &mut String,
    lines: &source::Lines,
    change: &TextDocumentContentChangeEvent,
) -> Result<(), ServerError<()>> {
    info!("Applying change: {:?}", change);
    let span = match (change.range, change.range_length) {
        (None, None) => Span::new(0.into(), source.len().into()),
        (Some(range), None) | (Some(range), Some(_)) => range_to_byte_span(lines, &range)?,
        (None, Some(_)) => return Err("Invalid change".into()),
    };
    source.drain(span.start.to_usize()..span.end.to_usize());
    source.insert_str(span.start.to_usize(), &change.text);
    Ok(())
}

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

    use languageserver_types::{Position, Range};

    #[test]
    fn apply_changes_test() {
        let mut source = String::new();
        apply_changes(
            &mut source,
            &[
                TextDocumentContentChangeEvent {
                    range: Some(Range {
                        start: Position {
                            line: 0,
                            character: 0,
                        },
                        end: Position {
                            line: 0,
                            character: 0,
                        },
                    }),
                    range_length: None,
                    text: "test".to_string(),
                },
            ],
        ).unwrap();

        assert_eq!(source, "test");

        apply_changes(
            &mut source,
            &[
                TextDocumentContentChangeEvent {
                    range: Some(Range {
                        start: Position {
                            line: 0,
                            character: 2,
                        },
                        end: Position {
                            line: 0,
                            character: 3,
                        },
                    }),
                    range_length: Some(1),
                    text: "".to_string(),
                },
            ],
        ).unwrap();

        assert_eq!(source, "tet");

        apply_changes(
            &mut source,
            &[
                TextDocumentContentChangeEvent {
                    range: Some(Range {
                        start: Position {
                            line: 0,
                            character: 2,
                        },
                        end: Position {
                            line: 0,
                            character: 3,
                        },
                    }),
                    range_length: Some(2),
                    text: "ab".to_string(),
                },
            ],
        ).unwrap();

        assert_eq!(source, "teab");
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::iter::repeat;
use std::str::FromStr;

enum Hamming { Encode, Decode, EncodeBinary, DecodeBinary }

fn main() {
    let title = "RUSTYHAM: A HAMMING CODE GENERATOR IN RUST";
    let border: String = repeat('=').take(title.len()).collect::<>();
    println!("{}", border);
    println!("{}", title);
    println!("{}", border);

    loop {
        let mut linebuf = String::new();
        println!("(1) Encode ASCII");
        println!("(2) Encode binary");
        println!("(3) Decode to ASCII");
        println!("(4) Decode to binary");
        print!("Enter your choice: ");
        io::stdout().flush().unwrap();
        io::stdin().read_line(&mut linebuf).unwrap();

        let num = i32::from_str(linebuf.trim());
        match num.ok() {
            Some(n) => {
                let mut good = true;
                println!("{}", match n {
                    1 => hamming(Hamming::Encode, prompt("Enter string to encode: ")),
                    2 => hamming(Hamming::EncodeBinary, prompt("Enter binary string to encode: ")),
                    3 => hamming(Hamming::Decode, prompt("Enter Hamming code to decode: ")),
                    4 => hamming(Hamming::DecodeBinary, prompt("Enter Hamming code to decode to binary: ")),
                    _ => { good = false; "Invalid input.".to_string() }
                });
                if good { break; }
            }
            None => {
                println!("Invalid input.");
            }
        };
    }
}

fn hamming(action: Hamming, s: String) -> String {
    match action {
        Hamming::Encode | Hamming::EncodeBinary => {
            // get an iterator over the individual bits
            let mut bytes;
            let bytes_str = match action {
                Hamming::EncodeBinary => s,
                _ => {
                    // convert ASCII string to binary
                    bytes = s.into_bytes();  // takes ownership of s
                    bytes.iter().map(|&c| format!("{:0>1$b}", c, 7))
                        .collect::<Vec<String>>().concat()
                }
            };
            let mut bytes_iter = bytes_str.chars();
            // we should assume that ownership of s has already been
            // transferred away by this point

            // compute block and message length
            let mlen = bytes_str.len() as u32;
            let lenpow = (2..).find(|&r| 2u32.pow(r) - r - 1 >= mlen).unwrap();
            let len = 2us.pow(lenpow) - 1;

            // the thing we're storing the hamming code in
            let mut code: Vec<bool> = repeat(false).take(len).collect::<>();

            // set data bits
            for i in 1..len {
                if (i & (i - 1)) != 0 {  // if i is not a power of 2
                    code[i-1] = bytes_iter.next().unwrap_or('0') == '1';
                }
            }

            // set parity bits
            for i in 0..lenpow {
                code[2us.pow(i) - 1] = calc_parity(&code, i);
            }

            code.into_iter().map(|x| if x {"1"} else {"0"}).collect::<Vec<_>>().concat()
        },
        Hamming::Decode | Hamming::DecodeBinary => {
            // verify parity bits, fix 1-bit-flipped errors if any
            let len = s.len();
            let lenpow = ((len + 1) as f32).sqrt().round() as u32;
            let mut chars = s.chars().map(|x| x == '1').collect::<Vec<bool>>();
            let mut flipped_bit = -1i32;
            while (0..lenpow).any(|i| calc_parity(&chars, i)) {
                if flipped_bit != -1 {
                    chars[flipped_bit as usize] = !chars[flipped_bit as usize];
                }
                flipped_bit += 1;
                chars[flipped_bit as usize] = !chars[flipped_bit as usize];
            }

            // collect all bits at non-powers-of-2
            let data = &chars.iter().enumerate()
                .filter(|x| ((x.0 + 1) & x.0) != 0).map(|x| if *x.1 {'1'} else {'0'})
                .collect::<Vec<char>>()[..];
            let chars = data.chunks(7)
                .map(|x| u8::from_str_radix(&x.iter().cloned().collect::<String>()[..], 2).unwrap())
                .collect::<Vec<u8>>();

            String::from_utf8(chars).unwrap()
        }
    }
}

fn calc_parity(code: &Vec<bool>, i: u32) -> bool {
    let bi = 2us.pow(i) - 1;
    let (mut parity, mut ignore, mut counter) = (false, false, 0);
    for j in bi..code.len() {
        if !ignore && code[j] { parity = !parity }
        counter += 1;
        if counter >= 2u32.pow(i) {
            ignore = !ignore;
            counter = 0;
        }
    }
    parity
}

fn prompt(s: &str) -> String {
    let mut input = String::new();
    print!("{}", s);
    io::stdout().flush().unwrap();
    io::stdin().read_line(&mut input).unwrap();
    input.trim().to_string()
}
<commit_msg>add decode to binary support<commit_after>use std::io;
use std::io::prelude::*;
use std::iter::repeat;
use std::str::FromStr;

enum Hamming { Encode, Decode, EncodeBinary, DecodeBinary }

fn main() {
    let title = "RUSTYHAM: A HAMMING CODE GENERATOR IN RUST";
    let border: String = repeat('=').take(title.len()).collect::<>();
    println!("{}", border);
    println!("{}", title);
    println!("{}", border);

    loop {
        let mut linebuf = String::new();
        println!("(1) Encode ASCII");
        println!("(2) Encode binary");
        println!("(3) Decode to ASCII");
        println!("(4) Decode to binary");
        print!("Enter your choice: ");
        io::stdout().flush().unwrap();
        io::stdin().read_line(&mut linebuf).unwrap();

        let num = i32::from_str(linebuf.trim());
        match num.ok() {
            Some(n) => {
                let mut good = true;
                println!("{}", match n {
                    1 => hamming(Hamming::Encode, prompt("Enter string to encode: ")),
                    2 => hamming(Hamming::EncodeBinary, prompt("Enter binary string to encode: ")),
                    3 => hamming(Hamming::Decode, prompt("Enter Hamming code to decode: ")),
                    4 => hamming(Hamming::DecodeBinary, prompt("Enter Hamming code to decode to binary: ")),
                    _ => { good = false; "Invalid input.".to_string() }
                });
                if good { break; }
            }
            None => {
                println!("Invalid input.");
            }
        };
    }
}

fn hamming(action: Hamming, s: String) -> String {
    match action {
        Hamming::Encode | Hamming::EncodeBinary => {
            // get an iterator over the individual bits
            let mut bytes;
            let bytes_str = match action {
                Hamming::EncodeBinary => s,
                _ => {
                    // convert ASCII string to binary
                    bytes = s.into_bytes();  // takes ownership of s
                    bytes.iter().map(|&c| format!("{:0>1$b}", c, 7))
                        .collect::<Vec<String>>().concat()
                }
            };
            let mut bytes_iter = bytes_str.chars();
            // we should assume that ownership of s has already been
            // transferred away by this point

            // compute block and message length
            let mlen = bytes_str.len() as u32;
            let lenpow = (2..).find(|&r| 2u32.pow(r) - r - 1 >= mlen).unwrap();
            let len = 2us.pow(lenpow) - 1;

            // the thing we're storing the hamming code in
            let mut code: Vec<bool> = repeat(false).take(len).collect::<>();

            // set data bits
            for i in 1..len {
                if (i & (i - 1)) != 0 {  // if i is not a power of 2
                    code[i-1] = bytes_iter.next().unwrap_or('0') == '1';
                }
            }

            // set parity bits
            for i in 0..lenpow {
                code[2us.pow(i) - 1] = calc_parity(&code, i);
            }

            code.into_iter().map(|x| if x {"1"} else {"0"}).collect::<Vec<_>>().concat()
        },
        Hamming::Decode | Hamming::DecodeBinary => {
            // verify parity bits, fix 1-bit-flipped errors if any
            let len = s.len();
            let lenpow = ((len + 1) as f32).sqrt().round() as u32;
            let mut chars = s.chars().map(|x| x == '1').collect::<Vec<bool>>();
            let mut flipped_bit = -1i32;
            while (0..lenpow).any(|i| calc_parity(&chars, i)) {
                if flipped_bit != -1 {
                    chars[flipped_bit as usize] = !chars[flipped_bit as usize];
                }
                flipped_bit += 1;
                chars[flipped_bit as usize] = !chars[flipped_bit as usize];
            }

            // collect all bits at non-powers-of-2
            let data = chars.iter().enumerate()
                .filter(|x| ((x.0 + 1) & x.0) != 0).map(|x| if *x.1 {'1'} else {'0'});

            // return
            match action {
                Hamming::DecodeBinary => {
                    data.collect::<String>()
                },
                _ => {
                    let chars = (&data.collect::<Vec<char>>()[..]).chunks(7)
                        .map(|x| u8::from_str_radix(&x.iter().cloned().collect::<String>()[..], 2).unwrap())
                        .collect::<Vec<u8>>();
                    String::from_utf8(chars).unwrap()
                }
            }
        }
    }
}

fn calc_parity(code: &Vec<bool>, i: u32) -> bool {
    let bi = 2us.pow(i) - 1;
    let (mut parity, mut ignore, mut counter) = (false, false, 0);
    for j in bi..code.len() {
        if !ignore && code[j] { parity = !parity }
        counter += 1;
        if counter >= 2u32.pow(i) {
            ignore = !ignore;
            counter = 0;
        }
    }
    parity
}

fn prompt(s: &str) -> String {
    let mut input = String::new();
    print!("{}", s);
    io::stdout().flush().unwrap();
    io::stdin().read_line(&mut input).unwrap();
    input.trim().to_string()
}
<|endoftext|>


<commit_before>extern crate icecore;

use icecore::data::FileStore;
use icecore::data::DataStore;

fn main() {
  let foo = "bar";
  let store = FileStore::new(String::from_str("_documents"));
  store.write(foo.as_bytes());
}
<commit_msg>Split the insert with arguements<commit_after>extern crate icecore;

use icecore::data::{FileStore, DataStore};
use getopts::{optopt, getopts};
use std::os;

fn insert(data: &str) {
    let store = FileStore::new("./_documents");
    store.write(foo.as_bytes());
}

fn main() {
    let args: Vec<String> = os::args();

    match args[1].as_slice() {
        "insert" => {
            let opts = [
                optopt("d", "data", "Data to insert into the database", "DATA"),
            ];
            let matches = match getopts(args.tail(), opts) {
                Ok(m) => m,
                Err(e) => panic!(e.to_string()),
            };

            let data = if matches.opt_present("d") {
                matches.opt_str("d").unwrap()
            };

            insert(data);
        },
        _ => {
            println!("Command not found.");
        }
    }
}
<|endoftext|>


<commit_before>// error-pattern:yummy
#![feature(box_syntax)]
#![feature(rustc_private)]
#![feature(static_in_const)]

#![allow(unknown_lints, missing_docs_in_private_items)]

extern crate clippy_lints;
extern crate getopts;
extern crate rustc;
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_plugin;
extern crate syntax;

use rustc_driver::{driver, CompilerCalls, RustcDefaultCalls, Compilation};
use rustc::session::{config, Session};
use rustc::session::config::{Input, ErrorOutputType};
use std::path::PathBuf;
use std::process::{self, Command};
use syntax::ast;
use std::io::{self, Write};

use clippy_lints::utils::cargo;

struct ClippyCompilerCalls {
    default: RustcDefaultCalls,
    run_lints: bool,
}

impl ClippyCompilerCalls {
    fn new(run_lints: bool) -> Self {
        ClippyCompilerCalls {
            default: RustcDefaultCalls,
            run_lints: run_lints,
        }
    }
}

impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
    fn early_callback(&mut self,
                      matches: &getopts::Matches,
                      sopts: &config::Options,
                      cfg: &ast::CrateConfig,
                      descriptions: &rustc_errors::registry::Registry,
                      output: ErrorOutputType)
                      -> Compilation {
        self.default.early_callback(matches, sopts, cfg, descriptions, output)
    }
    fn no_input(&mut self,
                matches: &getopts::Matches,
                sopts: &config::Options,
                cfg: &ast::CrateConfig,
                odir: &Option<PathBuf>,
                ofile: &Option<PathBuf>,
                descriptions: &rustc_errors::registry::Registry)
                -> Option<(Input, Option<PathBuf>)> {
        self.default.no_input(matches, sopts, cfg, odir, ofile, descriptions)
    }
    fn late_callback(&mut self,
                     matches: &getopts::Matches,
                     sess: &Session,
                     input: &Input,
                     odir: &Option<PathBuf>,
                     ofile: &Option<PathBuf>)
                     -> Compilation {
        self.default.late_callback(matches, sess, input, odir, ofile)
    }
    fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> driver::CompileController<'a> {
        let mut control = self.default.build_controller(sess, matches);

        if self.run_lints {
            let old = std::mem::replace(&mut control.after_parse.callback, box |_| {});
            control.after_parse.callback = Box::new(move |state| {
                {
                    let mut registry = rustc_plugin::registry::Registry::new(state.session, state.krate.as_ref().expect("at this compilation stage the krate must be parsed").span);
                    registry.args_hidden = Some(Vec::new());
                    clippy_lints::register_plugins(&mut registry);

                    let rustc_plugin::registry::Registry { early_lint_passes,
                                                           late_lint_passes,
                                                           lint_groups,
                                                           llvm_passes,
                                                           attributes,
                                                           mir_passes,
                                                           .. } = registry;
                    let sess = &state.session;
                    let mut ls = sess.lint_store.borrow_mut();
                    for pass in early_lint_passes {
                        ls.register_early_pass(Some(sess), true, pass);
                    }
                    for pass in late_lint_passes {
                        ls.register_late_pass(Some(sess), true, pass);
                    }

                    for (name, to) in lint_groups {
                        ls.register_group(Some(sess), true, name, to);
                    }

                    sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
                    sess.mir_passes.borrow_mut().extend(mir_passes);
                    sess.plugin_attributes.borrow_mut().extend(attributes);
                }
                old(state);
            });
        }

        control
    }
}

use std::path::Path;

const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.

Usage:
    cargo clippy [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
    --features               Features to compile for the package
    -V, --version            Print version info and exit

Other options are the same as `cargo rustc`.

To allow or deny a lint from the command line you can use `cargo clippy --`
with:

    -W --warn OPT       Set lint warnings
    -A --allow OPT      Set lint allowed
    -D --deny OPT       Set lint denied
    -F --forbid OPT     Set lint forbidden

The feature `cargo-clippy` is automatically defined for convenience. You can use
it to allow or deny lints from the code, eg.:

    #[cfg_attr(feature = "cargo-clippy", allow(needless_lifetimes))]
"#;

#[allow(print_stdout)]
fn show_help() {
    println!("{}", CARGO_CLIPPY_HELP);
}

#[allow(print_stdout)]
fn show_version() {
    println!("{}", env!("CARGO_PKG_VERSION"));
}

#[cfg_attr(feature = "cargo-clippy", allow(print_stdout))]
pub fn main() {
    use std::env;

    if env::var("CLIPPY_DOGFOOD").map(|_| true).unwrap_or(false) {
        panic!("yummy");
    }

    // Check for version and help flags even when invoked as 'cargo-clippy'
    if std::env::args().any(|a| a == "--help" || a == "-h") {
        show_help();
        return;
    }
    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    let dep_path = env::current_dir().expect("current dir is not readable").join("target").join("debug").join("deps");

    if let Some("clippy") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        // this arm is executed on the initial call to `cargo clippy`

        let manifest_path_arg = std::env::args().skip(2).find(|val| val.starts_with("--manifest-path="));

        let mut metadata = if let Ok(metadata) = cargo::metadata(manifest_path_arg.as_ref().map(AsRef::as_ref)) {
            metadata
        } else {
            let _ = io::stderr().write_fmt(format_args!("error: Could not obtain cargo metadata."));
            process::exit(101);
        };

        assert_eq!(metadata.version, 1);

        let manifest_path = manifest_path_arg.map(|arg| PathBuf::from(Path::new(&arg["--manifest-path=".len()..])));

        let current_dir = std::env::current_dir();

        let package_index = metadata.packages.iter()
            .position(|package| {
                let package_manifest_path = Path::new(&package.manifest_path);
                if let Some(ref manifest_path) = manifest_path {
                    package_manifest_path == manifest_path
                } else {
                    let current_dir = current_dir.as_ref().expect("could not read current directory");
                    let package_manifest_directory = package_manifest_path.parent().expect("could not find parent directory of package manifest");
                    package_manifest_directory == current_dir
                }
            })
            .expect("could not find matching package");
        let package = metadata.packages.remove(package_index);
        for target in package.targets {
            let args = std::env::args().skip(2);
            if let Some(first) = target.kind.get(0) {
                if target.kind.len() > 1 || first.ends_with("lib") {
                    if let Err(code) = process(std::iter::once("--lib".to_owned()).chain(args), &dep_path) {
                        std::process::exit(code);
                    }
                } else if ["bin", "example", "test", "bench"].contains(&&**first) {
                    if let Err(code) = process(vec![format!("--{}", first), target.name].into_iter().chain(args), &dep_path) {
                        std::process::exit(code);
                    }
                }
            } else {
                panic!("badly formatted cargo metadata: target::kind is an empty array");
            }
        }
    } else {
        // this arm is executed when cargo-clippy runs `cargo rustc` with the `RUSTC` env var set to itself

        let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
        let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
        let sys_root = if let (Some(home), Some(toolchain)) = (home, toolchain) {
            format!("{}/toolchains/{}", home, toolchain)
        } else {
            option_env!("SYSROOT")
                .map(|s| s.to_owned())
                .or(Command::new("rustc")
                    .arg("--print")
                    .arg("sysroot")
                    .output()
                    .ok()
                    .and_then(|out| String::from_utf8(out.stdout).ok())
                    .map(|s| s.trim().to_owned()))
                .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust")
        };

        // this conditional check for the --sysroot flag is there so users can call `cargo-clippy` directly
        // without having to pass --sysroot or anything
        let mut args: Vec<String> = if env::args().any(|s| s == "--sysroot") {
            env::args().collect()
        } else {
            env::args().chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect()
        };

        // this check ensures that dependencies are built but not linted and the final crate is
        // linted but not built
        args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]);

        let mut ccc = ClippyCompilerCalls::new(env::args().any(|s| s == "-Zno-trans"));
        let (result, _) = rustc_driver::run_compiler(&args, &mut ccc, None, None);

        if let Err(err_count) = result {
            if err_count > 0 {
                std::process::exit(1);
            }
        }
    }
}

fn process<P, I>(old_args: I, dep_path: P) -> Result<(), i32>
    where P: AsRef<Path>,
          I: Iterator<Item = String>
{

    let mut args = vec!["rustc".to_owned()];

    let mut found_dashes = false;
    for arg in old_args {
        found_dashes |= arg == "--";
        args.push(arg);
    }
    if !found_dashes {
        args.push("--".to_owned());
    }
    args.push("-L".to_owned());
    args.push(dep_path.as_ref().to_string_lossy().into_owned());
    args.push("-Zno-trans".to_owned());
    args.push("--cfg".to_owned());
    args.push(r#"feature="cargo-clippy""#.to_owned());

    let path = std::env::current_exe().expect("current executable path invalid");
    let exit_status = std::process::Command::new("cargo")
        .args(&args)
        .env("RUSTC", path)
        .spawn().expect("could not run cargo")
        .wait().expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}
<commit_msg>Remove unnecessary #[allow]<commit_after>// error-pattern:yummy
#![feature(box_syntax)]
#![feature(rustc_private)]
#![feature(static_in_const)]

#![allow(unknown_lints, missing_docs_in_private_items)]

extern crate clippy_lints;
extern crate getopts;
extern crate rustc;
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_plugin;
extern crate syntax;

use rustc_driver::{driver, CompilerCalls, RustcDefaultCalls, Compilation};
use rustc::session::{config, Session};
use rustc::session::config::{Input, ErrorOutputType};
use std::path::PathBuf;
use std::process::{self, Command};
use syntax::ast;
use std::io::{self, Write};

use clippy_lints::utils::cargo;

struct ClippyCompilerCalls {
    default: RustcDefaultCalls,
    run_lints: bool,
}

impl ClippyCompilerCalls {
    fn new(run_lints: bool) -> Self {
        ClippyCompilerCalls {
            default: RustcDefaultCalls,
            run_lints: run_lints,
        }
    }
}

impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
    fn early_callback(&mut self,
                      matches: &getopts::Matches,
                      sopts: &config::Options,
                      cfg: &ast::CrateConfig,
                      descriptions: &rustc_errors::registry::Registry,
                      output: ErrorOutputType)
                      -> Compilation {
        self.default.early_callback(matches, sopts, cfg, descriptions, output)
    }
    fn no_input(&mut self,
                matches: &getopts::Matches,
                sopts: &config::Options,
                cfg: &ast::CrateConfig,
                odir: &Option<PathBuf>,
                ofile: &Option<PathBuf>,
                descriptions: &rustc_errors::registry::Registry)
                -> Option<(Input, Option<PathBuf>)> {
        self.default.no_input(matches, sopts, cfg, odir, ofile, descriptions)
    }
    fn late_callback(&mut self,
                     matches: &getopts::Matches,
                     sess: &Session,
                     input: &Input,
                     odir: &Option<PathBuf>,
                     ofile: &Option<PathBuf>)
                     -> Compilation {
        self.default.late_callback(matches, sess, input, odir, ofile)
    }
    fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> driver::CompileController<'a> {
        let mut control = self.default.build_controller(sess, matches);

        if self.run_lints {
            let old = std::mem::replace(&mut control.after_parse.callback, box |_| {});
            control.after_parse.callback = Box::new(move |state| {
                {
                    let mut registry = rustc_plugin::registry::Registry::new(state.session, state.krate.as_ref().expect("at this compilation stage the krate must be parsed").span);
                    registry.args_hidden = Some(Vec::new());
                    clippy_lints::register_plugins(&mut registry);

                    let rustc_plugin::registry::Registry { early_lint_passes,
                                                           late_lint_passes,
                                                           lint_groups,
                                                           llvm_passes,
                                                           attributes,
                                                           mir_passes,
                                                           .. } = registry;
                    let sess = &state.session;
                    let mut ls = sess.lint_store.borrow_mut();
                    for pass in early_lint_passes {
                        ls.register_early_pass(Some(sess), true, pass);
                    }
                    for pass in late_lint_passes {
                        ls.register_late_pass(Some(sess), true, pass);
                    }

                    for (name, to) in lint_groups {
                        ls.register_group(Some(sess), true, name, to);
                    }

                    sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
                    sess.mir_passes.borrow_mut().extend(mir_passes);
                    sess.plugin_attributes.borrow_mut().extend(attributes);
                }
                old(state);
            });
        }

        control
    }
}

use std::path::Path;

const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.

Usage:
    cargo clippy [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
    --features               Features to compile for the package
    -V, --version            Print version info and exit

Other options are the same as `cargo rustc`.

To allow or deny a lint from the command line you can use `cargo clippy --`
with:

    -W --warn OPT       Set lint warnings
    -A --allow OPT      Set lint allowed
    -D --deny OPT       Set lint denied
    -F --forbid OPT     Set lint forbidden

The feature `cargo-clippy` is automatically defined for convenience. You can use
it to allow or deny lints from the code, eg.:

    #[cfg_attr(feature = "cargo-clippy", allow(needless_lifetimes))]
"#;

#[allow(print_stdout)]
fn show_help() {
    println!("{}", CARGO_CLIPPY_HELP);
}

#[allow(print_stdout)]
fn show_version() {
    println!("{}", env!("CARGO_PKG_VERSION"));
}

pub fn main() {
    use std::env;

    if env::var("CLIPPY_DOGFOOD").map(|_| true).unwrap_or(false) {
        panic!("yummy");
    }

    // Check for version and help flags even when invoked as 'cargo-clippy'
    if std::env::args().any(|a| a == "--help" || a == "-h") {
        show_help();
        return;
    }
    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    let dep_path = env::current_dir().expect("current dir is not readable").join("target").join("debug").join("deps");

    if let Some("clippy") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        // this arm is executed on the initial call to `cargo clippy`

        let manifest_path_arg = std::env::args().skip(2).find(|val| val.starts_with("--manifest-path="));

        let mut metadata = if let Ok(metadata) = cargo::metadata(manifest_path_arg.as_ref().map(AsRef::as_ref)) {
            metadata
        } else {
            let _ = io::stderr().write_fmt(format_args!("error: Could not obtain cargo metadata."));
            process::exit(101);
        };

        assert_eq!(metadata.version, 1);

        let manifest_path = manifest_path_arg.map(|arg| PathBuf::from(Path::new(&arg["--manifest-path=".len()..])));

        let current_dir = std::env::current_dir();

        let package_index = metadata.packages.iter()
            .position(|package| {
                let package_manifest_path = Path::new(&package.manifest_path);
                if let Some(ref manifest_path) = manifest_path {
                    package_manifest_path == manifest_path
                } else {
                    let current_dir = current_dir.as_ref().expect("could not read current directory");
                    let package_manifest_directory = package_manifest_path.parent().expect("could not find parent directory of package manifest");
                    package_manifest_directory == current_dir
                }
            })
            .expect("could not find matching package");
        let package = metadata.packages.remove(package_index);
        for target in package.targets {
            let args = std::env::args().skip(2);
            if let Some(first) = target.kind.get(0) {
                if target.kind.len() > 1 || first.ends_with("lib") {
                    if let Err(code) = process(std::iter::once("--lib".to_owned()).chain(args), &dep_path) {
                        std::process::exit(code);
                    }
                } else if ["bin", "example", "test", "bench"].contains(&&**first) {
                    if let Err(code) = process(vec![format!("--{}", first), target.name].into_iter().chain(args), &dep_path) {
                        std::process::exit(code);
                    }
                }
            } else {
                panic!("badly formatted cargo metadata: target::kind is an empty array");
            }
        }
    } else {
        // this arm is executed when cargo-clippy runs `cargo rustc` with the `RUSTC` env var set to itself

        let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
        let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
        let sys_root = if let (Some(home), Some(toolchain)) = (home, toolchain) {
            format!("{}/toolchains/{}", home, toolchain)
        } else {
            option_env!("SYSROOT")
                .map(|s| s.to_owned())
                .or(Command::new("rustc")
                    .arg("--print")
                    .arg("sysroot")
                    .output()
                    .ok()
                    .and_then(|out| String::from_utf8(out.stdout).ok())
                    .map(|s| s.trim().to_owned()))
                .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust")
        };

        // this conditional check for the --sysroot flag is there so users can call `cargo-clippy` directly
        // without having to pass --sysroot or anything
        let mut args: Vec<String> = if env::args().any(|s| s == "--sysroot") {
            env::args().collect()
        } else {
            env::args().chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect()
        };

        // this check ensures that dependencies are built but not linted and the final crate is
        // linted but not built
        args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]);

        let mut ccc = ClippyCompilerCalls::new(env::args().any(|s| s == "-Zno-trans"));
        let (result, _) = rustc_driver::run_compiler(&args, &mut ccc, None, None);

        if let Err(err_count) = result {
            if err_count > 0 {
                std::process::exit(1);
            }
        }
    }
}

fn process<P, I>(old_args: I, dep_path: P) -> Result<(), i32>
    where P: AsRef<Path>,
          I: Iterator<Item = String>
{

    let mut args = vec!["rustc".to_owned()];

    let mut found_dashes = false;
    for arg in old_args {
        found_dashes |= arg == "--";
        args.push(arg);
    }
    if !found_dashes {
        args.push("--".to_owned());
    }
    args.push("-L".to_owned());
    args.push(dep_path.as_ref().to_string_lossy().into_owned());
    args.push("-Zno-trans".to_owned());
    args.push("--cfg".to_owned());
    args.push(r#"feature="cargo-clippy""#.to_owned());

    let path = std::env::current_exe().expect("current executable path invalid");
    let exit_status = std::process::Command::new("cargo")
        .args(&args)
        .env("RUSTC", path)
        .spawn().expect("could not run cargo")
        .wait().expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}
<|endoftext|>


<commit_before>extern crate rustwlc;
#[macro_use]
extern crate wayland_client;

extern crate tempfile;

extern crate byteorder;
extern crate image;

use std::env;
use std::process::exit;
use std::io::{BufRead, BufReader};
use image::{load, ImageFormat};
//use gdk_sys;

fn main() {
    let args: Vec<_> = env::args().collect();
    if args.len() < 2 {
        println!("Please supply either a file path or a color (written in hex)");
        exit(1);
    }
    let input = &args[1];
    // TODO Don't hard code this, get it from the server...
    let output = unsafe {rustwlc::WlcOutput::dummy(0)};
    if let Ok(color) = input.parse::<u32>() {
        let color = Color::from_u32(color);
        generate_solid_background(color, output);
    } else {
        generate_image_background(input.clone(), output);
    }
}

use std::mem::transmute;
use std::os::unix::io::AsRawFd;
use std::io::{Read, Write};
use std::fs::File;
use std::path::PathBuf;

//use gdk_pixbuf::{Pixbuf, InterpType};

use wayland_client::wayland::get_display;
use wayland_client::wayland::compositor::{WlCompositor, WlSurface};
use wayland_client::wayland::shell::{WlShellSurface, WlShell};
use wayland_client::wayland::shm::{WlBuffer, WlShm, WlShmFormat};
use wayland_client::wayland::seat::{WlSeat, WlPointerEvent};
use wayland_client::cursor::load_theme;
use wayland_client::{EventIterator, Proxy};

use rustwlc::WlcOutput;

use byteorder::{NativeEndian, WriteBytesExt};

wayland_env!(WaylandEnv,
             compositor: WlCompositor,
             shell: WlShell,
             shm: WlShm,
             seat: WlSeat
);

#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
/// Holds the bytes to represent a colored background.
/// To be written into a wayland surface.
pub struct Color(pub [u8; 4]);

impl Color {
    /// Generate a new color out of a u32.
    /// E.G: 0xFFFFFF
    pub fn from_u32(color: u32) -> Self {
        unsafe { Color(transmute(color)) }
    }

    pub fn as_u32(&self) -> u32 {
        unsafe { transmute(self.0)}
    }
}

/// Given a solid color, writes bytes associated with that color to
/// a special Wayland surface which is then rendered as a background for Way Cooler.
pub fn generate_solid_background(color: Color, _output: WlcOutput) {
    // Get shortcuts to the globals.
    let (display, iter) = get_display()
        .expect("Unable to connect to a wayland compositor");
    let (env, evt_iter) = WaylandEnv::init(display, iter);
    let compositor = env.compositor.as_ref().map(|o| &o.0).unwrap();
    let shell = env.shell.as_ref().map(|o| &o.0).unwrap();
    let shm = env.shm.as_ref().map(|o| &o.0).unwrap();
    let seat = env.seat.as_ref().map(|o| &o.0).unwrap();

    // Create the surface we are going to write into
    let surface = compositor.create_surface();
    let shell_surface = shell.get_shell_surface(&surface);
    let mut tmp = tempfile::tempfile().ok().expect("Unable to create a tempfile.");

    // Calculate how big the buffer needs to be from the output resolution
    // TODO Get the output size from way cooler somehow...
    //let resolution = output.get_resolution()
    //    .expect("Couldn't get output resolution");
    //let (width, height) = (resolution.w as i32, resolution.h as i32);
    let width = 800; let height = 600;
    let size = (width * height) as i32;

    // Write in the color coding to the surface
    for _ in 0..size {
        unsafe {
            tmp.write_u32::<NativeEndian>(transmute(color.0))
                .expect("Could not write to file")
        }
    }
    tmp.flush()
        .expect("Could not flush buffer");

    // Create the buffer that is mem-mapped to the temp file descriptor
    let pool = shm.create_pool(tmp.as_raw_fd(), size);
    let buffer = pool.create_buffer(0, width, height, width, WlShmFormat::Argb8888);
    // Tell Way Cooler not to set put this in the tree, treat as background
    shell_surface.set_class("Background".into());
    shell_surface.set_title(format!("0x{:x}", color.as_u32()));

    // Attach the buffer to the surface
    surface.attach(Some(&buffer), 0, 0);

    main_background_loop(compositor, shell, shm, seat, surface,
                         shell_surface, buffer, evt_iter);
}

fn weird_math(num: u8, third_num: u32) -> u8 {
    let big_num = num as u32;
    ((big_num * third_num) / 255) as u8
}

#[test]
fn test_weird_math() {
    assert_eq!(weird_math(10, 254), 9);
    assert_eq!(weird_math(2, 255), 2);
    assert_eq!(weird_math(255, 500), 500);
}

/// Given the data from an image, writes it to a special Wayland surface
/// which is then rendered as a background for Way Cooler.
pub fn generate_image_background(path: String, _output: WlcOutput) {
    let image_file = File::open(path.clone())
        .unwrap_or_else(|_| {
            println!("Could not open \"{:?}\"", path);
            panic!("Could not open image file");
        });
    let image_reader = BufReader::new(image_file);
    let image = load(image_reader, ImageFormat::PNG)
        .expect("Image was not a png file!");
    let mut image = image.to_rgba();
    let width = image.width();
    let height = image.height();
    // TODO Split this into its own function
    let mut out_file = File::create("out.bin").unwrap();
    {
        let pixels = image.enumerate_pixels_mut();
        for (_x, _y, pixel) in pixels {
            let alpha = pixel[3] as u32;
            pixel[0] = weird_math(pixel[0], alpha);
            pixel[1] = weird_math(pixel[1], alpha);
            pixel[2] = weird_math(pixel[2], alpha);

            let mut pixels: [u8; 4] = [0; 4];
            pixels[2] = pixel[0];
            pixels[1] = pixel[1];
            pixels[0] = pixel[2];
            pixels[3] = pixel[3];
            unsafe {
                out_file.write_u32::<NativeEndian>(transmute(pixels))
                    .unwrap();
            }
        }
    }
    //let tmp = tempfile::NamedTempFile::new().expect("Unable to create a tempfile.");
    //tmp.set_len(size as u64).expect("Could not truncate length of file");

/*    //let pix_buf = Pixbuf::new_from_file(path.as_str())
    //    .expect("Could not read the file");
    //let image_file = File::open(path.clone())
    let image_file = ::std::fs::OpenOptions::new()
        .read(true)
        .write(true)
        .create_new(false)
        .open(path.clone())
        .unwrap_or_else(|_| {
            println!("Could not open \"{:?}\"", path);
            panic!("Could not open image file");
        });
    let raw_fd = image_file.as_raw_fd();
    let map = Mmap::open(&image_file, memmap::Protection::ReadWrite)
        .expect("Could not memory map the temp file");
    let background_surface = ImageSurface::create_from_png(image_file)
        .expect("could not create surface from png");
    // Get shortcuts to the globals.
    let (display, iter) = get_display()
        .expect("Unable to connect to a wayland compositor");
    let (env, evt_iter) = WaylandEnv::init(display, iter);
    let compositor = env.compositor.as_ref().map(|o| &o.0).unwrap();
    let shell = env.shell.as_ref().map(|o| &o.0).unwrap();
    let shm = env.shm.as_ref().map(|o| &o.0).unwrap();
    let seat = env.seat.as_ref().map(|o| &o.0).unwrap();

    // Create the surface we are going to write into
    let surface = compositor.create_surface();
    let shell_surface = shell.get_shell_surface(&surface);
    // Calculate how big the buffer needs to be from the output resolution
    // TODO Get the output size from way cooler somehow...

    let width = background_surface.get_width();
    let height = background_surface.get_height();
    let stride = background_surface.get_stride();
    let size: i32 = stride * height;
    println!("Size: {}, mmap len: {}", size, map.len());
    //let size = map.len() as i32;
    //let tmp = tempfile::NamedTempFile::new().expect("Unable to create a tempfile.");
    //tmp.set_len(size as u64).expect("Could not truncate length of file");
    // Create the buffer that is mem-mapped to the temp file descriptor
    let pool = shm.create_pool(raw_fd, size);
    let buffer = pool.create_buffer(0, width, height, stride, WlShmFormat::Argb8888);
    // Tell Way Cooler not to put this in the tree, treat as background
    // TODO Make this less hacky by actually giving way cooler access to this thing...
    shell_surface.set_class("Background".into());
    // TODO Actually give it the path or something idk
    shell_surface.set_title(format!("Image background yay"));

    // Attach the buffer to the surface
    surface.attach(Some(&buffer), 0, 0);
    surface.commit();
    surface.set_buffer_scale(1);
    surface.damage(0, 0, width, height);

    println!("{:?}", unsafe{map.as_slice()});
    main_background_loop(compositor, shell, shm, seat, surface,
                         shell_surface, buffer, evt_iter);
    */
}

#[allow(dead_code)]
fn read_image_data(path: PathBuf) -> Vec<u32> {
    let image_file = File::open(path.clone())
        .unwrap_or_else(|_| {
            println!("Could not open \"{:?}\"", path);
            panic!("Could not open image file");
        });
    // Most common screen size
    let mut buffer: Vec<u32> = Vec::with_capacity(1366 * 768);
    // TODO, while clever looking, is Ok(byte) here correct?
    let mut bytes = image_file.bytes();
    'outer: loop {
        let result: u32;
        let mut data_chunk: [u8; 4] = [0; 4];
        for i in 0..3 {
            if let Some(Ok(next_chunk)) = bytes.next() {
                data_chunk[i] = next_chunk;
            } else {
                break 'outer;
            }
        }
        /*let data_chunk: Vec<u8> = next_chunk
            .flat_map(|maybe_bytes| maybe_bytes.into_iter())
            .collect();*/
        result = unsafe {::std::mem::transmute(data_chunk)};
        println!("{:X}", result);
        buffer.push(result);
    }
    buffer
}

/// Main loop for rendering backgrounds.
/// Need to keep the surface alive, and update it if the
/// user wants to change the background.
#[allow(unused_variables)]
fn main_background_loop(compositor: &WlCompositor, shell: &WlShell, shm: &WlShm,
                        seat: &WlSeat,
                        surface: WlSurface, shell_surface: WlShellSurface,
                        buffer: WlBuffer, mut event_iter: EventIterator) {
    use wayland_client::wayland::WaylandProtocolEvent;
    use wayland_client::Event;
    println!("Entering main loop");
    // For now just loop and do nothing
    // Eventually need to query the background state and update
    let cursor_surface = compositor.create_surface();
    let mut pointer = seat.get_pointer();
    let cursor_theme = load_theme(None, 16, shm);
    let maybe_cursor = cursor_theme.get_cursor("default");
    // TODO Uncomment
    /*if maybe_cursor.is_none() {
        println!("Could not load cursor theme properly, cannot load background");
        println!("Please consult the developers about this issue with your distro version, this is a known issue");
        return;
    }
    let cursor = maybe_cursor.unwrap();
    let cursor_buffer = cursor.frame_buffer(0).expect("Couldn't get frame_buffer");
    cursor_surface.attach(Some(&*cursor_buffer), 0, 0);
     */
    pointer.set_event_iterator(&event_iter);
    pointer.set_cursor(0, Some(&cursor_surface), 0, 0);
    surface.commit();
    event_iter.sync_roundtrip().unwrap();
    loop {
        for event in &mut event_iter {
            match event {
                Event::Wayland(wayland_event) => {
                    match wayland_event {
                        WaylandProtocolEvent::WlPointer(id, pointer_event) => {
                            match pointer_event {
                                WlPointerEvent::Enter(serial, surface, surface_x, surface_y) => {
                                    pointer.set_cursor(0, Some(&cursor_surface), 0, 0);
                                },
                                _ => {
                                }
                            }
                        },
                        _ => {/* unhandled events */}
                    }
                }
                _ => { /* unhandled events */ }
            }
        }
        event_iter.dispatch().expect("Connection with the compositor was lost.");
    }
}
<commit_msg>Improved I/O speed<commit_after>extern crate rustwlc;
#[macro_use]
extern crate wayland_client;

extern crate tempfile;

extern crate byteorder;
extern crate image;

use std::env;
use std::process::exit;
use std::io::{BufReader};
use image::{load, ImageFormat};
//use gdk_sys;

fn main() {
    let args: Vec<_> = env::args().collect();
    if args.len() < 2 {
        println!("Please supply either a file path or a color (written in hex)");
        exit(1);
    }
    let input = &args[1];
    // TODO Don't hard code this, get it from the server...
    let output = unsafe {rustwlc::WlcOutput::dummy(0)};
    if let Ok(color) = input.parse::<u32>() {
        let color = Color::from_u32(color);
        generate_solid_background(color, output);
    } else {
        generate_image_background(input.clone(), output);
    }
}

use std::mem::transmute;
use std::os::unix::io::AsRawFd;
use std::io::{Read, Write};
use std::fs::File;
use std::path::PathBuf;

//use gdk_pixbuf::{Pixbuf, InterpType};

use wayland_client::wayland::get_display;
use wayland_client::wayland::compositor::{WlCompositor, WlSurface};
use wayland_client::wayland::shell::{WlShellSurface, WlShell};
use wayland_client::wayland::shm::{WlBuffer, WlShm, WlShmFormat};
use wayland_client::wayland::seat::{WlSeat, WlPointerEvent};
use wayland_client::cursor::load_theme;
use wayland_client::{EventIterator, Proxy};

use rustwlc::WlcOutput;

use byteorder::{NativeEndian, WriteBytesExt};

wayland_env!(WaylandEnv,
             compositor: WlCompositor,
             shell: WlShell,
             shm: WlShm,
             seat: WlSeat
);

#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
/// Holds the bytes to represent a colored background.
/// To be written into a wayland surface.
pub struct Color(pub [u8; 4]);

impl Color {
    /// Generate a new color out of a u32.
    /// E.G: 0xFFFFFF
    pub fn from_u32(color: u32) -> Self {
        unsafe { Color(transmute(color)) }
    }

    pub fn as_u32(&self) -> u32 {
        unsafe { transmute(self.0)}
    }
}

/// Given a solid color, writes bytes associated with that color to
/// a special Wayland surface which is then rendered as a background for Way Cooler.
pub fn generate_solid_background(color: Color, _output: WlcOutput) {
    // Get shortcuts to the globals.
    let (display, iter) = get_display()
        .expect("Unable to connect to a wayland compositor");
    let (env, evt_iter) = WaylandEnv::init(display, iter);
    let compositor = env.compositor.as_ref().map(|o| &o.0).unwrap();
    let shell = env.shell.as_ref().map(|o| &o.0).unwrap();
    let shm = env.shm.as_ref().map(|o| &o.0).unwrap();
    let seat = env.seat.as_ref().map(|o| &o.0).unwrap();

    // Create the surface we are going to write into
    let surface = compositor.create_surface();
    let shell_surface = shell.get_shell_surface(&surface);
    let mut tmp = tempfile::tempfile().ok().expect("Unable to create a tempfile.");

    // Calculate how big the buffer needs to be from the output resolution
    // TODO Get the output size from way cooler somehow...
    //let resolution = output.get_resolution()
    //    .expect("Couldn't get output resolution");
    //let (width, height) = (resolution.w as i32, resolution.h as i32);
    let width = 800; let height = 600;
    let size = (width * height) as i32;

    // Write in the color coding to the surface
    for _ in 0..size {
        unsafe {
            tmp.write_u32::<NativeEndian>(transmute(color.0))
                .expect("Could not write to file")
        }
    }
    tmp.flush()
        .expect("Could not flush buffer");

    // Create the buffer that is mem-mapped to the temp file descriptor
    let pool = shm.create_pool(tmp.as_raw_fd(), size);
    let buffer = pool.create_buffer(0, width, height, width, WlShmFormat::Argb8888);
    // Tell Way Cooler not to set put this in the tree, treat as background
    shell_surface.set_class("Background".into());
    shell_surface.set_title(format!("0x{:x}", color.as_u32()));

    // Attach the buffer to the surface
    surface.attach(Some(&buffer), 0, 0);

    main_background_loop(compositor, shell, shm, seat, surface,
                         shell_surface, buffer, evt_iter);
}

fn weird_math(num: u8, third_num: u32) -> u8 {
    let big_num = num as u32;
    ((big_num * third_num) / 255) as u8
}

#[test]
fn test_weird_math() {
    assert_eq!(weird_math(10, 254), 9);
    assert_eq!(weird_math(2, 255), 2);
    assert_eq!(weird_math(255, 500), 500);
}

/// Given the data from an image, writes it to a special Wayland surface
/// which is then rendered as a background for Way Cooler.
pub fn generate_image_background(path: String, _output: WlcOutput) {
    let image_file = File::open(path.clone())
        .unwrap_or_else(|_| {
            println!("Could not open \"{:?}\"", path);
            panic!("Could not open image file");
        });
    let image_reader = BufReader::new(image_file);
    let image = load(image_reader, ImageFormat::PNG)
        .expect("Image was not a png file!");
    let mut image = image.to_rgba();
    let width = image.width();
    let height = image.height();
    let size = width * height;
    // TODO Split this into its own function
    let mut out_file = File::create("out.bin").unwrap();
    {
        let pixels = image.enumerate_pixels_mut();
        for (_x, _y, pixel) in pixels {
            let alpha = pixel[3] as u32;
            pixel[0] = weird_math(pixel[0], alpha);
            pixel[1] = weird_math(pixel[1], alpha);
            pixel[2] = weird_math(pixel[2], alpha);

            let tmp = pixel[2];
            pixel[2] = pixel[0];
            pixel[0] = tmp;
        }
    }
    let vec = image.into_vec();
    out_file.write_all(&*vec).unwrap();
    let tmp = tempfile::NamedTempFile::new().expect("Unable to create a tempfile.");
    tmp.set_len(size as u64).expect("Could not truncate length of file");

/*    //let pix_buf = Pixbuf::new_from_file(path.as_str())
    //    .expect("Could not read the file");
    //let image_file = File::open(path.clone())
    let image_file = ::std::fs::OpenOptions::new()
        .read(true)
        .write(true)
        .create_new(false)
        .open(path.clone())
        .unwrap_or_else(|_| {
            println!("Could not open \"{:?}\"", path);
            panic!("Could not open image file");
        });
    let raw_fd = image_file.as_raw_fd();
    let map = Mmap::open(&image_file, memmap::Protection::ReadWrite)
        .expect("Could not memory map the temp file");
    let background_surface = ImageSurface::create_from_png(image_file)
        .expect("could not create surface from png");
    // Get shortcuts to the globals.
    let (display, iter) = get_display()
        .expect("Unable to connect to a wayland compositor");
    let (env, evt_iter) = WaylandEnv::init(display, iter);
    let compositor = env.compositor.as_ref().map(|o| &o.0).unwrap();
    let shell = env.shell.as_ref().map(|o| &o.0).unwrap();
    let shm = env.shm.as_ref().map(|o| &o.0).unwrap();
    let seat = env.seat.as_ref().map(|o| &o.0).unwrap();

    // Create the surface we are going to write into
    let surface = compositor.create_surface();
    let shell_surface = shell.get_shell_surface(&surface);
    // Calculate how big the buffer needs to be from the output resolution
    // TODO Get the output size from way cooler somehow...

    let width = background_surface.get_width();
    let height = background_surface.get_height();
    let stride = background_surface.get_stride();
    let size: i32 = stride * height;
    println!("Size: {}, mmap len: {}", size, map.len());
    //let size = map.len() as i32;
    //let tmp = tempfile::NamedTempFile::new().expect("Unable to create a tempfile.");
    //tmp.set_len(size as u64).expect("Could not truncate length of file");
    // Create the buffer that is mem-mapped to the temp file descriptor
    let pool = shm.create_pool(raw_fd, size);
    let buffer = pool.create_buffer(0, width, height, stride, WlShmFormat::Argb8888);
    // Tell Way Cooler not to put this in the tree, treat as background
    // TODO Make this less hacky by actually giving way cooler access to this thing...
    shell_surface.set_class("Background".into());
    // TODO Actually give it the path or something idk
    shell_surface.set_title(format!("Image background yay"));

    // Attach the buffer to the surface
    surface.attach(Some(&buffer), 0, 0);
    surface.commit();
    surface.set_buffer_scale(1);
    surface.damage(0, 0, width, height);

    println!("{:?}", unsafe{map.as_slice()});
    main_background_loop(compositor, shell, shm, seat, surface,
                         shell_surface, buffer, evt_iter);
    */
}

#[allow(dead_code)]
fn read_image_data(path: PathBuf) -> Vec<u32> {
    let image_file = File::open(path.clone())
        .unwrap_or_else(|_| {
            println!("Could not open \"{:?}\"", path);
            panic!("Could not open image file");
        });
    // Most common screen size
    let mut buffer: Vec<u32> = Vec::with_capacity(1366 * 768);
    // TODO, while clever looking, is Ok(byte) here correct?
    let mut bytes = image_file.bytes();
    'outer: loop {
        let result: u32;
        let mut data_chunk: [u8; 4] = [0; 4];
        for i in 0..3 {
            if let Some(Ok(next_chunk)) = bytes.next() {
                data_chunk[i] = next_chunk;
            } else {
                break 'outer;
            }
        }
        /*let data_chunk: Vec<u8> = next_chunk
            .flat_map(|maybe_bytes| maybe_bytes.into_iter())
            .collect();*/
        result = unsafe {::std::mem::transmute(data_chunk)};
        println!("{:X}", result);
        buffer.push(result);
    }
    buffer
}

/// Main loop for rendering backgrounds.
/// Need to keep the surface alive, and update it if the
/// user wants to change the background.
#[allow(unused_variables)]
fn main_background_loop(compositor: &WlCompositor, shell: &WlShell, shm: &WlShm,
                        seat: &WlSeat,
                        surface: WlSurface, shell_surface: WlShellSurface,
                        buffer: WlBuffer, mut event_iter: EventIterator) {
    use wayland_client::wayland::WaylandProtocolEvent;
    use wayland_client::Event;
    println!("Entering main loop");
    // For now just loop and do nothing
    // Eventually need to query the background state and update
    let cursor_surface = compositor.create_surface();
    let mut pointer = seat.get_pointer();
    let cursor_theme = load_theme(None, 16, shm);
    let maybe_cursor = cursor_theme.get_cursor("default");
    // TODO Uncomment
    /*if maybe_cursor.is_none() {
        println!("Could not load cursor theme properly, cannot load background");
        println!("Please consult the developers about this issue with your distro version, this is a known issue");
        return;
    }
    let cursor = maybe_cursor.unwrap();
    let cursor_buffer = cursor.frame_buffer(0).expect("Couldn't get frame_buffer");
    cursor_surface.attach(Some(&*cursor_buffer), 0, 0);
     */
    pointer.set_event_iterator(&event_iter);
    pointer.set_cursor(0, Some(&cursor_surface), 0, 0);
    surface.commit();
    event_iter.sync_roundtrip().unwrap();
    loop {
        for event in &mut event_iter {
            match event {
                Event::Wayland(wayland_event) => {
                    match wayland_event {
                        WaylandProtocolEvent::WlPointer(id, pointer_event) => {
                            match pointer_event {
                                WlPointerEvent::Enter(serial, surface, surface_x, surface_y) => {
                                    pointer.set_cursor(0, Some(&cursor_surface), 0, 0);
                                },
                                _ => {
                                }
                            }
                        },
                        _ => {/* unhandled events */}
                    }
                }
                _ => { /* unhandled events */ }
            }
        }
        event_iter.dispatch().expect("Connection with the compositor was lost.");
    }
}
<|endoftext|>