<commit_before>extern crate debugserver_types;
extern crate env_logger;
extern crate futures;
extern crate jsonrpc_core;
extern crate serde;
extern crate serde_json;

extern crate gluon_language_server;
extern crate gluon;

use std::error::Error as StdError;
use std::fs::File;
use std::io::{BufReader, Read};
use std::net::{TcpListener, TcpStream};
use std::sync::{Arc, Condvar, Mutex};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::thread::spawn;
use std::collections::{HashMap, HashSet};

use futures::{Async, BoxFuture, Future, IntoFuture};

use jsonrpc_core::IoHandler;

use serde_json::Value;

use debugserver_types::*;

use gluon::base::pos::Line;
use gluon::vm::api::{OpaqueValue, Hole};
use gluon::vm::Error as VmError;
use gluon::vm::thread::{RootedThread, ThreadInternal, LINE_FLAG};
use gluon::{Compiler, filename_to_module};

use gluon_language_server::rpc::{LanguageServerCommand, ServerCommand, ServerError, main_loop,
                                 write_message};

pub struct InitializeHandler {
    stream: Arc<TcpStream>,
    seq: Arc<AtomicUsize>,
}

impl LanguageServerCommand<InitializeRequestArguments> for InitializeHandler {
    type Output = Option<Capabilities>;
    type Error = ();
    fn execute(&self,
               _args: InitializeRequestArguments)
               -> BoxFuture<Option<Capabilities>, ServerError<Self::Error>> {

        let initialized = InitializedEvent {
            body: None,
            event: "initialized".into(),
            seq: self.seq.fetch_add(1, Ordering::SeqCst) as i64,
            type_: "event".into(),
        };
        write_message(&*self.stream, &initialized)
            .map_err(|err| {
                ServerError {
                    message: format!("Unable to write result to initialize: {}", err),
                    data: None,
                }
            })
            .map(|_| {
                Some(Capabilities {
                    supports_configuration_done_request: Some(true),
                    ..Capabilities::default()
                })
            })
            .into_future()
            .boxed()
    }
}

pub struct LaunchHandler {
    debugger: Arc<Debugger>,
    thread: RootedThread,
    stream: Arc<TcpStream>,
    seq: Arc<AtomicUsize>,
}

impl LanguageServerCommand<Value> for LaunchHandler {
    type Output = Option<Value>;
    type Error = ();
    fn execute(&self, args: Value) -> BoxFuture<Option<Value>, ServerError<()>> {
        self.execute_launch(args).into_future().boxed()
    }
}

impl LaunchHandler {
    fn execute_launch(&self, args: Value) -> Result<Option<Value>, ServerError<()>> {
        let program = args.get("program")
            .and_then(|s| s.as_str())
            .ok_or_else(|| {
                ServerError {
                    message: "No program argument found".into(),
                    data: None,
                }
            })?;
        let module = filename_to_module(program);
        let expr = {
            let mut file = File::open(program).map_err(|_| {
                    ServerError {
                        message: format!("Program does not exit: `{}`", program),
                        data: None,
                    }
                })?;
            let mut expr = String::new();
            file.read_to_string(&mut expr).expect("Could not read from file");
            expr
        };

        let debugger = self.debugger.clone();
        self.thread.context().set_hook(Some(Box::new(move |_, debug_info| {
            let stack_info = debug_info.stack_info(0).unwrap();
            if let Some(line) = stack_info.line() {
                if debugger.should_break(stack_info.source_name(), line) {
                    let mut continue_exection = debugger.continue_mutex.lock().unwrap();
                    *continue_exection = false;
                    write_message(&*debugger.stream,
                                  &StoppedEvent {
                                      body: StoppedEventBody {
                                          all_threads_stopped: Some(true),
                                          reason: "breakpoint".into(),
                                          text: None,
                                          thread_id: Some(1),
                                      },
                                      event: "stopped".to_string(),
                                      seq: debugger.seq(),
                                      type_: "event".to_string(),
                                  }).map_err(|err| VmError::Message(format!("{}", err)))?;
                    while !*continue_exection {
                        continue_exection = debugger.continue_var.wait(continue_exection).unwrap();
                    }
                }
            }
            Ok(Async::Ready(()))
        })));
        self.thread.context().set_hook_mask(LINE_FLAG);

        let thread = self.thread.clone();
        let seq = self.seq.clone();
        let stream = self.stream.clone();
        spawn(move || {
            let _result = Compiler::new()
                .run_expr::<OpaqueValue<RootedThread, Hole>>(&thread, &module, &expr);
            let seq = seq.fetch_add(1, Ordering::SeqCst) as i64;
            let terminated = TerminatedEvent {
                body: None,
                event: "terminated".into(),
                seq: seq,
                type_: "event".into(),
            };
            write_message(&*stream, &terminated).unwrap();
        });
        Ok(None)
    }
}

pub struct DisconnectHandler {
    exit_token: Arc<AtomicBool>,
}

impl LanguageServerCommand<DisconnectArguments> for DisconnectHandler {
    type Output = Option<Value>;
    type Error = ();
    fn execute(&self, _args: Value) -> BoxFuture<Option<Value>, ServerError<()>> {
        self.exit_token.store(true, Ordering::SeqCst);
        Ok(None).into_future().boxed()
    }
}

// Translate debug server messages into jsonrpc-2.0
fn translate_request(message: String) -> Result<String, Box<StdError>> {
    use serde_json::Value;
    let mut data: Value = try!(serde_json::from_str(&message));
    if data.is_object() {
        let data = data.as_object_mut().unwrap();
        let command = data.get("command").and_then(|c| c.as_str()).expect("command").to_string();
        let seq = data.get("seq")
            .and_then(|c| {
                c.as_str()
                    .map(|s| Value::String(s.to_string()))
                    .or_else(|| c.as_i64().map(Value::from))
            })
            .expect("seq");
        let arguments = data.remove("arguments").unwrap_or(Value::Object(Default::default()));
        let map = vec![("method".to_string(), Value::String(command)),
                       ("id".to_string(), seq),
                       ("params".to_string(), arguments),
                       ("jsonrpc".to_owned(), Value::String("2.0".to_string()))]
            .into_iter()
            .collect();
        return Ok(try!(serde_json::to_string(&Value::Object(map))));
    }
    Ok(message)
}

fn translate_response(debugger: &Debugger, message: String) -> Result<String, Box<StdError>> {
    use serde_json::Value;
    let mut data: Value = try!(serde_json::from_str(&message));
    if data.is_object() {
        let data = data.as_object_mut().unwrap();
        let seq = data.get("id")
            .and_then(|c| {
                c.as_str()
                    .map(|s| Value::String(s.to_string()))
                    .or_else(|| c.as_i64().map(Value::from))
            })
            .expect("id");
        let result = data.remove("result");
        let error = data.remove("error");

        let mut map = vec![("success".to_string(), Value::Bool(result.is_some())),
                           ("request_seq".to_string(), seq),
                           ("seq".to_string(), Value::from(debugger.seq())),
                           ("type".to_owned(), Value::String("response".to_string()))];

        if let Some(result) = result {
            map.push(("body".to_string(), result));
        }
        if let Some(mut error) = error {
            map.push(("message".to_string(),
                      error.as_object_mut().unwrap().remove("message").expect("error message")));
        }

        let map = map.into_iter().collect();
        return Ok(try!(serde_json::to_string(&Value::Object(map))));
    }
    Ok(message)
}

pub struct Debugger {
    stream: Arc<TcpStream>,
    breakpoints: Mutex<HashMap<String, HashSet<Line>>>,
    continue_mutex: Mutex<bool>,
    continue_var: Condvar,
    seq: Arc<AtomicUsize>,
}

impl Debugger {
    fn seq(&self) -> i64 {
        self.seq.fetch_add(1, Ordering::SeqCst) as i64
    }

    fn should_break(&self, source_name: &str, line: Line) -> bool {
        let breakpoints = self.breakpoints.lock().expect("breakpoints mutex is poisoned");
        breakpoints.get(source_name)
            .map_or(false, |lines| lines.contains(&line))
    }
}

pub fn main() {
    env_logger::init().unwrap();

    let listener = TcpListener::bind("127.0.0.1:4711").unwrap();
    for stream in listener.incoming() {
        let stream = Arc::new(stream.unwrap());

        let exit_token = Arc::new(AtomicBool::new(false));
        let seq = Arc::new(AtomicUsize::new(1));

        let thread = gluon::new_vm();

        let debugger = Arc::new(Debugger {
            stream: stream.clone(),
            breakpoints: Mutex::new(HashMap::new()),
            continue_mutex: Mutex::new(false),
            continue_var: Condvar::new(),
            seq: seq.clone(),
        });

        let mut io = IoHandler::new();
        io.add_async_method("initialize",
                            ServerCommand::new(InitializeHandler {
                                stream: stream.clone(),
                                seq: seq.clone(),
                            }));

        let configuration_done =
            move |_: ConfigurationDoneArguments| -> BoxFuture<(), ServerError<()>> {
                Ok(()).into_future().boxed()
            };
        io.add_async_method("configurationDone", ServerCommand::new(configuration_done));

        io.add_async_method("launch",
                            ServerCommand::new(LaunchHandler {
                                debugger: debugger.clone(),
                                thread: thread,
                                stream: stream.clone(),
                                seq: seq.clone(),
                            }));
        io.add_async_method("disconnect",
                            ServerCommand::new(DisconnectHandler {
                                exit_token: exit_token.clone(),
                            }));
        {
            let debugger = debugger.clone();
            let set_break = move |args: SetBreakpointsArguments| -> BoxFuture<_, ServerError<()>> {
                let mut breakpoint_map = debugger.breakpoints.lock().unwrap();
                let breakpoints =
                    args.breakpoints
                        .iter()
                        .flat_map(|bs| {
                            bs.iter().map(|breakpoint| Line::from(breakpoint.line as usize))
                        })
                        .collect();
                if let Some(path) = args.source.path {
                    breakpoint_map.insert(filename_to_module(path.trim_right_matches(".glu")),
                                          breakpoints);
                }
                Ok(SetBreakpointsResponseBody {
                        breakpoints: args.breakpoints
                            .into_iter()
                            .flat_map(|bs| bs)
                            .map(|breakpoint| {
                                Breakpoint {
                                    column: None,
                                    end_column: None,
                                    end_line: None,
                                    id: None,
                                    line: Some(breakpoint.line),
                                    message: None,
                                    source: None,
                                    verified: true,
                                }
                            })
                            .collect(),
                    })
                    .into_future()
                    .boxed()
            };

            io.add_async_method("setBreakpoints", ServerCommand::new(set_break));
        }

        let threads = move |_: Value| -> BoxFuture<ThreadsResponseBody, ServerError<()>> {
            Ok(ThreadsResponseBody {
                    threads: vec![Thread {
                                      id: 1,
                                      name: "main".to_string(),
                                  }],
                })
                .into_future()
                .boxed()
        };

        io.add_async_method("threads", ServerCommand::new(threads));

        let stack_trace = move |_: StackTraceArguments| -> BoxFuture<_, ServerError<()>> {
            Ok(StackTraceResponseBody {
                    stack_frames: vec![StackFrame {
                                           column: 0,
                                           end_column: None,
                                           end_line: None,
                                           id: 0,
                                           line: 0,
                                           module_id: None,
                                           name: "main".to_string(),
                                           source: None,
                                       }],
                    total_frames: Some(1),
                })
                .into_future()
                .boxed()
        };
        io.add_async_method("stackTrace", ServerCommand::new(stack_trace));

        let scopes = move |_: ScopesArguments| -> BoxFuture<_, ServerError<()>> {
            Ok(ScopesResponseBody {
                    scopes: vec![Scope {
                                     column: None,
                                     end_column: None,
                                     end_line: None,
                                     expensive: false,
                                     indexed_variables: None,
                                     line: None,
                                     name: "Locals".to_string(),
                                     named_variables: None,
                                     source: Some(Source {
                                         name: Some("main".to_string()),
                                         ..Source::default()
                                     }),
                                     variables_reference: 0,
                                 }],
                })
                .into_future()
                .boxed()
        };
        io.add_async_method("scopes", ServerCommand::new(scopes));

        {
            let debugger = debugger.clone();
            let cont = move |_: ContinueArguments| -> BoxFuture<Option<Value>, ServerError<()>> {
                *debugger.continue_mutex.lock().unwrap() = true;
                debugger.continue_var.notify_one();
                Ok(None).into_future().boxed()
            };
            io.add_async_method("continue", ServerCommand::new(cont));
        }

        spawn(move || {
            let read = BufReader::new(&*stream);
            main_loop(read,
                      &*stream,
                      &io,
                      exit_token,
                      translate_request,
                      |body| translate_response(&debugger, body))
                .unwrap()
        });
    }
}
<commit_msg>fix: Handle 1 based lines<commit_after>extern crate debugserver_types;
extern crate env_logger;
extern crate futures;
extern crate jsonrpc_core;
extern crate serde;
extern crate serde_json;

extern crate gluon_language_server;
extern crate gluon;

use std::error::Error as StdError;
use std::fs::File;
use std::io::{BufReader, Read};
use std::net::{TcpListener, TcpStream};
use std::sync::{Arc, Condvar, Mutex};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::thread::spawn;
use std::collections::{HashMap, HashSet};

use futures::{Async, BoxFuture, Future, IntoFuture};

use jsonrpc_core::IoHandler;

use serde_json::Value;

use debugserver_types::*;

use gluon::base::pos::Line;
use gluon::vm::api::{OpaqueValue, Hole};
use gluon::vm::Error as VmError;
use gluon::vm::thread::{RootedThread, ThreadInternal, LINE_FLAG};
use gluon::{Compiler, filename_to_module};

use gluon_language_server::rpc::{LanguageServerCommand, ServerCommand, ServerError, main_loop,
                                 write_message};

pub struct InitializeHandler {
    debugger: Arc<Debugger>,
}

impl LanguageServerCommand<InitializeRequestArguments> for InitializeHandler {
    type Output = Option<Capabilities>;
    type Error = ();
    fn execute(&self,
               args: InitializeRequestArguments)
               -> BoxFuture<Option<Capabilities>, ServerError<Self::Error>> {
        self.debugger
            .lines_start_at_1
            .store(args.lines_start_at_1.unwrap_or(true), Ordering::Release);
        let initialized = InitializedEvent {
            body: None,
            event: "initialized".into(),
            seq: self.debugger.seq(),
            type_: "event".into(),
        };
        write_message(&*self.debugger.stream, &initialized)
            .map_err(|err| {
                ServerError {
                    message: format!("Unable to write result to initialize: {}", err),
                    data: None,
                }
            })
            .map(|_| {
                Some(Capabilities {
                    supports_configuration_done_request: Some(true),
                    ..Capabilities::default()
                })
            })
            .into_future()
            .boxed()
    }
}

pub struct LaunchHandler {
    debugger: Arc<Debugger>,
    thread: RootedThread,
    stream: Arc<TcpStream>,
    seq: Arc<AtomicUsize>,
}

impl LanguageServerCommand<Value> for LaunchHandler {
    type Output = Option<Value>;
    type Error = ();
    fn execute(&self, args: Value) -> BoxFuture<Option<Value>, ServerError<()>> {
        self.execute_launch(args).into_future().boxed()
    }
}

impl LaunchHandler {
    fn execute_launch(&self, args: Value) -> Result<Option<Value>, ServerError<()>> {
        let program = args.get("program")
            .and_then(|s| s.as_str())
            .ok_or_else(|| {
                ServerError {
                    message: "No program argument found".into(),
                    data: None,
                }
            })?;
        let module = filename_to_module(program);
        let expr = {
            let mut file = File::open(program).map_err(|_| {
                    ServerError {
                        message: format!("Program does not exit: `{}`", program),
                        data: None,
                    }
                })?;
            let mut expr = String::new();
            file.read_to_string(&mut expr).expect("Could not read from file");
            expr
        };

        let debugger = self.debugger.clone();
        self.thread.context().set_hook(Some(Box::new(move |_, debug_info| {
            let stack_info = debug_info.stack_info(0).unwrap();
            if let Some(line) = stack_info.line() {
                if debugger.should_break(stack_info.source_name(), line) {
                    let mut continue_exection = debugger.continue_mutex.lock().unwrap();
                    *continue_exection = false;
                    write_message(&*debugger.stream,
                                  &StoppedEvent {
                                      body: StoppedEventBody {
                                          all_threads_stopped: Some(true),
                                          reason: "breakpoint".into(),
                                          text: None,
                                          thread_id: Some(1),
                                      },
                                      event: "stopped".to_string(),
                                      seq: debugger.seq(),
                                      type_: "event".to_string(),
                                  }).map_err(|err| VmError::Message(format!("{}", err)))?;
                    while !*continue_exection {
                        continue_exection = debugger.continue_var.wait(continue_exection).unwrap();
                    }
                }
            }
            Ok(Async::Ready(()))
        })));
        self.thread.context().set_hook_mask(LINE_FLAG);

        let thread = self.thread.clone();
        let seq = self.seq.clone();
        let stream = self.stream.clone();
        spawn(move || {
            let _result = Compiler::new()
                .run_expr::<OpaqueValue<RootedThread, Hole>>(&thread, &module, &expr);
            let seq = seq.fetch_add(1, Ordering::SeqCst) as i64;
            let terminated = TerminatedEvent {
                body: None,
                event: "terminated".into(),
                seq: seq,
                type_: "event".into(),
            };
            write_message(&*stream, &terminated).unwrap();
        });
        Ok(None)
    }
}

pub struct DisconnectHandler {
    exit_token: Arc<AtomicBool>,
}

impl LanguageServerCommand<DisconnectArguments> for DisconnectHandler {
    type Output = Option<Value>;
    type Error = ();
    fn execute(&self, _args: Value) -> BoxFuture<Option<Value>, ServerError<()>> {
        self.exit_token.store(true, Ordering::SeqCst);
        Ok(None).into_future().boxed()
    }
}

// Translate debug server messages into jsonrpc-2.0
fn translate_request(message: String) -> Result<String, Box<StdError>> {
    use serde_json::Value;
    let mut data: Value = try!(serde_json::from_str(&message));
    if data.is_object() {
        let data = data.as_object_mut().unwrap();
        let command = data.get("command").and_then(|c| c.as_str()).expect("command").to_string();
        let seq = data.get("seq")
            .and_then(|c| {
                c.as_str()
                    .map(|s| Value::String(s.to_string()))
                    .or_else(|| c.as_i64().map(Value::from))
            })
            .expect("seq");
        let arguments = data.remove("arguments").unwrap_or(Value::Object(Default::default()));
        let map = vec![("method".to_string(), Value::String(command)),
                       ("id".to_string(), seq),
                       ("params".to_string(), arguments),
                       ("jsonrpc".to_owned(), Value::String("2.0".to_string()))]
            .into_iter()
            .collect();
        return Ok(try!(serde_json::to_string(&Value::Object(map))));
    }
    Ok(message)
}

fn translate_response(debugger: &Debugger, message: String) -> Result<String, Box<StdError>> {
    use serde_json::Value;
    let mut data: Value = try!(serde_json::from_str(&message));
    if data.is_object() {
        let data = data.as_object_mut().unwrap();
        let seq = data.get("id")
            .and_then(|c| {
                c.as_str()
                    .map(|s| Value::String(s.to_string()))
                    .or_else(|| c.as_i64().map(Value::from))
            })
            .expect("id");
        let result = data.remove("result");
        let error = data.remove("error");

        let mut map = vec![("success".to_string(), Value::Bool(result.is_some())),
                           ("request_seq".to_string(), seq),
                           ("seq".to_string(), Value::from(debugger.seq())),
                           ("type".to_owned(), Value::String("response".to_string()))];

        if let Some(result) = result {
            map.push(("body".to_string(), result));
        }
        if let Some(mut error) = error {
            map.push(("message".to_string(),
                      error.as_object_mut().unwrap().remove("message").expect("error message")));
        }

        let map = map.into_iter().collect();
        return Ok(try!(serde_json::to_string(&Value::Object(map))));
    }
    Ok(message)
}

pub struct Debugger {
    stream: Arc<TcpStream>,
    breakpoints: Mutex<HashMap<String, HashSet<Line>>>,
    continue_mutex: Mutex<bool>,
    continue_var: Condvar,
    seq: Arc<AtomicUsize>,
    lines_start_at_1: AtomicBool,
}

impl Debugger {
    fn seq(&self) -> i64 {
        self.seq.fetch_add(1, Ordering::SeqCst) as i64
    }

    fn should_break(&self, source_name: &str, line: Line) -> bool {
        let breakpoints = self.breakpoints.lock().expect("breakpoints mutex is poisoned");
        breakpoints.get(source_name)
            .map_or(false, |lines| lines.contains(&line))
    }

    fn line(&self, line: i64) -> Line {
        Line::from((line as usize)
            .saturating_sub(self.lines_start_at_1.load(Ordering::Acquire) as usize))
    }
}

pub fn main() {
    env_logger::init().unwrap();

    let listener = TcpListener::bind("127.0.0.1:4711").unwrap();
    for stream in listener.incoming() {
        let stream = Arc::new(stream.unwrap());

        let exit_token = Arc::new(AtomicBool::new(false));
        let seq = Arc::new(AtomicUsize::new(1));

        let thread = gluon::new_vm();

        let debugger = Arc::new(Debugger {
            stream: stream.clone(),
            breakpoints: Mutex::new(HashMap::new()),
            continue_mutex: Mutex::new(false),
            continue_var: Condvar::new(),
            seq: seq.clone(),
            lines_start_at_1: AtomicBool::new(true),
        });

        let mut io = IoHandler::new();
        io.add_async_method("initialize",
                            ServerCommand::new(InitializeHandler { debugger: debugger.clone() }));

        let configuration_done =
            move |_: ConfigurationDoneArguments| -> BoxFuture<(), ServerError<()>> {
                Ok(()).into_future().boxed()
            };
        io.add_async_method("configurationDone", ServerCommand::new(configuration_done));

        io.add_async_method("launch",
                            ServerCommand::new(LaunchHandler {
                                debugger: debugger.clone(),
                                thread: thread,
                                stream: stream.clone(),
                                seq: seq.clone(),
                            }));
        io.add_async_method("disconnect",
                            ServerCommand::new(DisconnectHandler {
                                exit_token: exit_token.clone(),
                            }));
        {
            let debugger = debugger.clone();
            let set_break = move |args: SetBreakpointsArguments| -> BoxFuture<_, ServerError<()>> {
                let mut breakpoint_map = debugger.breakpoints.lock().unwrap();
                let breakpoints = args.breakpoints
                    .iter()
                    .flat_map(|bs| bs.iter().map(|breakpoint| debugger.line(breakpoint.line)))
                    .collect();
                if let Some(path) = args.source.path {
                    breakpoint_map.insert(filename_to_module(path.trim_right_matches(".glu")),
                                          breakpoints);
                }
                Ok(SetBreakpointsResponseBody {
                        breakpoints: args.breakpoints
                            .into_iter()
                            .flat_map(|bs| bs)
                            .map(|breakpoint| {
                                Breakpoint {
                                    column: None,
                                    end_column: None,
                                    end_line: None,
                                    id: None,
                                    line: Some(breakpoint.line),
                                    message: None,
                                    source: None,
                                    verified: true,
                                }
                            })
                            .collect(),
                    })
                    .into_future()
                    .boxed()
            };

            io.add_async_method("setBreakpoints", ServerCommand::new(set_break));
        }

        let threads = move |_: Value| -> BoxFuture<ThreadsResponseBody, ServerError<()>> {
            Ok(ThreadsResponseBody {
                    threads: vec![Thread {
                                      id: 1,
                                      name: "main".to_string(),
                                  }],
                })
                .into_future()
                .boxed()
        };

        io.add_async_method("threads", ServerCommand::new(threads));

        let stack_trace = move |_: StackTraceArguments| -> BoxFuture<_, ServerError<()>> {
            Ok(StackTraceResponseBody {
                    stack_frames: vec![StackFrame {
                                           column: 0,
                                           end_column: None,
                                           end_line: None,
                                           id: 0,
                                           line: 0,
                                           module_id: None,
                                           name: "main".to_string(),
                                           source: None,
                                       }],
                    total_frames: Some(1),
                })
                .into_future()
                .boxed()
        };
        io.add_async_method("stackTrace", ServerCommand::new(stack_trace));

        let scopes = move |_: ScopesArguments| -> BoxFuture<_, ServerError<()>> {
            Ok(ScopesResponseBody {
                    scopes: vec![Scope {
                                     column: None,
                                     end_column: None,
                                     end_line: None,
                                     expensive: false,
                                     indexed_variables: None,
                                     line: None,
                                     name: "Locals".to_string(),
                                     named_variables: None,
                                     source: Some(Source {
                                         name: Some("main".to_string()),
                                         ..Source::default()
                                     }),
                                     variables_reference: 0,
                                 }],
                })
                .into_future()
                .boxed()
        };
        io.add_async_method("scopes", ServerCommand::new(scopes));

        {
            let debugger = debugger.clone();
            let cont = move |_: ContinueArguments| -> BoxFuture<Option<Value>, ServerError<()>> {
                *debugger.continue_mutex.lock().unwrap() = true;
                debugger.continue_var.notify_one();
                Ok(None).into_future().boxed()
            };
            io.add_async_method("continue", ServerCommand::new(cont));
        }

        spawn(move || {
            let read = BufReader::new(&*stream);
            main_loop(read,
                      &*stream,
                      &io,
                      exit_token,
                      translate_request,
                      |body| translate_response(&debugger, body))
                .unwrap()
        });
    }
}
<|endoftext|>


<commit_before>use {Bytes, Chunk, SChunks};
use client::GetResponse;
use hyper::client::Client;
use hyper::error::Error;
use hyper::header::{ByteRangeSpec, Headers, Range};
use pbr::{MultiBar, Pipe, ProgressBar};
use response::CheckResponseStatus;
use std::cmp::min;
use std::io::Read;
use std::thread;

/// Represents a range between two Bytes types
#[derive(Debug, PartialEq)]
struct RangeBytes(Bytes, Bytes);

/// Function to get the current chunk length, based on the chunk index.
fn get_chunk_length(chunk_index: u64,
                    content_length: Bytes,
                    global_chunk_length: Bytes)
                    -> Option<RangeBytes> {

    if content_length == 0 || global_chunk_length == 0 {
        return None;
    }

    let b_range: Bytes = chunk_index * global_chunk_length;

    if b_range >= (content_length - 1) {
        return None;
    }

    let e_range: Bytes = min(content_length - 1,
                             ((chunk_index + 1) * global_chunk_length) - 1);

    Some(RangeBytes(b_range, e_range))

}

/// Function to get the HTTP header to send to the file server, for a chunk (specified by its index)
fn get_header_from_index(chunk_index: u64,
                         content_length: Bytes,
                         global_chunk_length: Bytes)
                         -> Option<(Headers, Bytes)> {

    match get_chunk_length(chunk_index, content_length, global_chunk_length) {
        Some(range) => {
            let mut header = Headers::new();
            header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(range.0, range.1)]));
            Some((header, range.1 - range.0))
        }
        None => None,
    }

}

/// Function to get from the server the content of a chunk.
/// This function returns a Result type - Bytes if the content of the header is accessible, an Error type otherwise.
fn download_a_chunk(http_client: &Client,
                    http_header: Headers,
                    chunk_vector: &mut Chunk,
                    url: &str,
                    mpb: &mut ProgressBar<Pipe>)
                    -> Result<Bytes, Error> {

    let mut body = http_client.get_http_response_using_headers(url, http_header).unwrap();
    if !body.check_partialcontent_status() {
        return Err(Error::Status);
    }
    let mut bytes_buffer = [0; 2048];
    let mut sum_bytes = 0;
    while let Ok(n) = body.read(&mut bytes_buffer) {
        if n == 0 {
            return Ok(sum_bytes);
        }
        chunk_vector.extend_from_slice(&bytes_buffer[..n]);
        sum_bytes += n as u64;
        mpb.add(n as u64);
    }
    return Ok(0u64);
}

/// Function to download each chunk of a remote content (given by its URL).
/// This function takes as parameters:
/// * the remote content length,
/// * a mutable reference to share between threads, which contains each chunk,
/// * the number of chunks that contains the remote content,
/// * the URL of the remote content server.
pub fn download_chunks(content_length: u64,
                       downloaded_chunks: &SChunks,
                       nb_chunks: u64,
                       url: &str) {

    // let mut downloaded_chunks: Arc<Mutex<Chunks>> =
    //     Arc::new(Mutex::new(Chunks::with_capacity(nb_chunks as usize)));
    let global_chunk_length: u64 = (content_length / nb_chunks) + 1;
    let mut jobs = vec![];

    let mut mpb = MultiBar::new();
    mpb.println(&format!("Downloading {} chunks: ", nb_chunks));

    for chunk_index in 0..nb_chunks {

        let (http_header, chunk_length) =
            get_header_from_index(chunk_index, content_length, global_chunk_length).unwrap();
        let mut chunk_content = Chunk::with_capacity(chunk_length as usize);
        let hyper_client = Client::new();
        let url_clone = String::from(url);
        let clone_chunks = downloaded_chunks.clone();

        // Progress bar customization
        let mut mp = mpb.create_bar(chunk_length);
        mp.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏");
        mp.format("|#--|");
        mp.show_tick = true;
        mp.show_speed = false;
        mp.show_percent = true;
        mp.show_counter = false;
        mp.show_time_left = true;
        mp.message(&format!("Chunk {} ", chunk_index));

        jobs.push(thread::spawn(move || match download_a_chunk(&hyper_client,
                                                                    http_header,
                                                                    &mut chunk_content,
                                                                    &url_clone,
                                                                    &mut mp) {
            Ok(bytes_written) => {
                if bytes_written > 0 {
                    let mut shared_clone_chunks = clone_chunks.lock().unwrap();
                    shared_clone_chunks.insert(chunk_index as usize, chunk_content);
                    mp.finish();
                } else {
                    panic!("The downloaded chunk {} is empty", chunk_index);
                }
            }
            Err(error) => {
                panic!("Cannot download the chunk {}, due to error {}",
                       chunk_index,
                       error);
            }
        }));
    }

    mpb.listen();

    for child in jobs {
        let _ = child.join();
    }

}

#[cfg(test)]
mod test_chunk_length {

    use super::get_chunk_length;
    use super::RangeBytes;

    #[test]
    fn wrong_content_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 15, 0));
    }

    #[test]
    fn wrong_global_chunk_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 15));
    }

    #[test]
    fn wrong_length_parameters_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 0));
    }

    #[test]
    fn get_the_first_range_in_chunk() {
        assert_eq!(Some(RangeBytes(0, 249)), get_chunk_length(0, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_chunk() {
        assert_eq!(Some(RangeBytes(750, 999)), get_chunk_length(3, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_shorten_chunk() {
        assert_eq!(Some(RangeBytes(750, 997)), get_chunk_length(3, 998, 250));
    }

    #[test]
    fn wrong_index_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(4, 1000, 250));
    }

}

#[cfg(test)]
mod test_header {

    use super::get_header_from_index;
    use hyper::header::{ByteRangeSpec, Headers, Range};

    #[test]
    fn wrong_chunk_length_should_return_none() {
        assert_eq!(None, get_header_from_index(0, 0, 0));
    }

    #[test]
    fn good_chunk_length_should_return_a_good_header() {
        let mut test_header = Headers::new();
        test_header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(750, 997)]));
        assert_eq!(Some((test_header, 247)), get_header_from_index(3, 998, 250));
    }

}
<commit_msg>Update progress only once in 500ms to fix 100% CPU<commit_after>use {Bytes, Chunk, SChunks};
use client::GetResponse;
use hyper::client::Client;
use hyper::error::Error;
use hyper::header::{ByteRangeSpec, Headers, Range};
use pbr::{MultiBar, Pipe, ProgressBar, Units};
use response::CheckResponseStatus;
use std::cmp::min;
use std::io::Read;
use std::thread;
use std::time::{Instant, Duration};

/// Represents a range between two Bytes types
#[derive(Debug, PartialEq)]
struct RangeBytes(Bytes, Bytes);

/// Function to get the current chunk length, based on the chunk index.
fn get_chunk_length(chunk_index: u64,
                    content_length: Bytes,
                    global_chunk_length: Bytes)
                    -> Option<RangeBytes> {

    if content_length == 0 || global_chunk_length == 0 {
        return None;
    }

    let b_range: Bytes = chunk_index * global_chunk_length;

    if b_range >= (content_length - 1) {
        return None;
    }

    let e_range: Bytes = min(content_length - 1,
                             ((chunk_index + 1) * global_chunk_length) - 1);

    Some(RangeBytes(b_range, e_range))

}

/// Function to get the HTTP header to send to the file server, for a chunk (specified by its index)
fn get_header_from_index(chunk_index: u64,
                         content_length: Bytes,
                         global_chunk_length: Bytes)
                         -> Option<(Headers, Bytes)> {

    match get_chunk_length(chunk_index, content_length, global_chunk_length) {
        Some(range) => {
            let mut header = Headers::new();
            header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(range.0, range.1)]));
            Some((header, range.1 - range.0))
        }
        None => None,
    }

}

/// Function to get from the server the content of a chunk.
/// This function returns a Result type - Bytes if the content of the header is accessible, an Error type otherwise.
fn download_a_chunk(http_client: &Client,
                    http_header: Headers,
                    chunk_vector: &mut Chunk,
                    url: &str,
                    mpb: &mut ProgressBar<Pipe>)
                    -> Result<Bytes, Error> {

    let mut body = http_client.get_http_response_using_headers(url, http_header).unwrap();
    if !body.check_partialcontent_status() {
        return Err(Error::Status);
    }
    let mut bytes_buffer = [0; 2048];
    let mut sum_bytes = 0;

    let progress_update_interval = Duration::from_millis(500);
    let mut last_progress_bytes = 0;
    let mut last_progress_time = Instant::now() - progress_update_interval;

    while let Ok(n) = body.read(&mut bytes_buffer) {
        if n == 0 {
            return Ok(sum_bytes);
        }

        chunk_vector.extend_from_slice(&bytes_buffer[..n]);
        sum_bytes += n as u64;

        if Instant::now().duration_since(last_progress_time) > progress_update_interval {
            last_progress_time = Instant::now();
            let progress_bytes_delta = sum_bytes-last_progress_bytes;
            last_progress_bytes = sum_bytes;
            mpb.add(progress_bytes_delta);
        }
    }
    mpb.add(sum_bytes-last_progress_bytes);
    return Ok(0u64);
}

/// Function to download each chunk of a remote content (given by its URL).
/// This function takes as parameters:
/// * the remote content length,
/// * a mutable reference to share between threads, which contains each chunk,
/// * the number of chunks that contains the remote content,
/// * the URL of the remote content server.
pub fn download_chunks(content_length: u64,
                       downloaded_chunks: &SChunks,
                       nb_chunks: u64,
                       url: &str) {

    // let mut downloaded_chunks: Arc<Mutex<Chunks>> =
    //     Arc::new(Mutex::new(Chunks::with_capacity(nb_chunks as usize)));
    let global_chunk_length: u64 = (content_length / nb_chunks) + 1;
    let mut jobs = vec![];

    let mut mpb = MultiBar::new();
    mpb.println(&format!("Downloading {} chunks: ", nb_chunks));

    for chunk_index in 0..nb_chunks {

        let (http_header, chunk_length) =
            get_header_from_index(chunk_index, content_length, global_chunk_length).unwrap();
        let mut chunk_content = Chunk::with_capacity(chunk_length as usize);
        let hyper_client = Client::new();
        let url_clone = String::from(url);
        let clone_chunks = downloaded_chunks.clone();

        // Progress bar customization
        let mut mp = mpb.create_bar(chunk_length);
        mp.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏");
        mp.format("|#--|");
        mp.show_tick = true;
        mp.show_speed = false;
        mp.show_percent = true;
        mp.show_counter = false;
        mp.show_time_left = true;
        mp.message(&format!("Chunk {} ", chunk_index));

        jobs.push(thread::spawn(move || match download_a_chunk(&hyper_client,
                                                                    http_header,
                                                                    &mut chunk_content,
                                                                    &url_clone,
                                                                    &mut mp) {
            Ok(bytes_written) => {
                if bytes_written > 0 {
                    let mut shared_clone_chunks = clone_chunks.lock().unwrap();
                    shared_clone_chunks.insert(chunk_index as usize, chunk_content);
                    mp.finish();
                } else {
                    panic!("The downloaded chunk {} is empty", chunk_index);
                }
            }
            Err(error) => {
                panic!("Cannot download the chunk {}, due to error {}",
                       chunk_index,
                       error);
            }
        }));
    }

    mpb.listen();

    for child in jobs {
        let _ = child.join();
    }

}

#[cfg(test)]
mod test_chunk_length {

    use super::get_chunk_length;
    use super::RangeBytes;

    #[test]
    fn wrong_content_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 15, 0));
    }

    #[test]
    fn wrong_global_chunk_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 15));
    }

    #[test]
    fn wrong_length_parameters_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 0));
    }

    #[test]
    fn get_the_first_range_in_chunk() {
        assert_eq!(Some(RangeBytes(0, 249)), get_chunk_length(0, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_chunk() {
        assert_eq!(Some(RangeBytes(750, 999)), get_chunk_length(3, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_shorten_chunk() {
        assert_eq!(Some(RangeBytes(750, 997)), get_chunk_length(3, 998, 250));
    }

    #[test]
    fn wrong_index_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(4, 1000, 250));
    }

}

#[cfg(test)]
mod test_header {

    use super::get_header_from_index;
    use hyper::header::{ByteRangeSpec, Headers, Range};

    #[test]
    fn wrong_chunk_length_should_return_none() {
        assert_eq!(None, get_header_from_index(0, 0, 0));
    }

    #[test]
    fn good_chunk_length_should_return_a_good_header() {
        let mut test_header = Headers::new();
        test_header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(750, 997)]));
        assert_eq!(Some((test_header, 247)), get_header_from_index(3, 998, 250));
    }

}
<|endoftext|>


<commit_before>//! escaping provides functions for the Server Query escaping.
//!
//! # Example
//! ```
//! use sqlib::escaping::{escape, unescape};
//!
//! let unescaped = "hello world/|\\".to_string();
//! let escaped = "hello\\sworld\\/\\p\\\\".to_string();
//!
//! let escaped_test = escape(&unescaped);
//! let unescaped_test = unescape(&escaped);
//!
//! assert_eq!(escaped_test, escaped);
//! assert_eq!(unescaped_test, unescaped);
//! ```

const ESCAPE_CHARS: [(char, &'static str); 11] = [('\\', "\\\\"),
                                                  (' ', "\\s"),
                                                  ('/', "\\/"),
                                                  ('|', "\\p"),
                                                  (7 as char, "\\a"),
                                                  (8 as char, "\\b"),
                                                  (9 as char, "\\t"),
                                                  (10 as char, "\\n"),
                                                  (11 as char, "\\v"),
                                                  (12 as char, "\\f"),
                                                  (13 as char, "\\r")];

/// escapes all chars described in the server query manual
///
/// # Example
/// ```
/// use sqlib::escaping::escape;
///
/// let unescaped = "hello world/|\\".to_string();
/// let escaped = "hello\\sworld\\/\\p\\\\".to_string();
///
/// let s = escape(&unescaped);
///
/// assert_eq!(s, escaped);
/// ```
pub fn escape(s: &str) -> String {
    let mut new_string = s.to_string();
    for &(from, to) in ESCAPE_CHARS.iter() {
        new_string = new_string.replace(from, to);
    }
    new_string
}

/// unescapes all chars described in the server query manual
///
/// # Example
/// ```
/// use sqlib::escaping::unescape;
///
/// let unescaped = "hello world/|\\".to_string();
/// let escaped = "hello\\sworld\\/\\p\\\\".to_string();
///
/// let s = unescape(&escaped);
///
/// assert_eq!(s, unescaped);
/// ```
pub fn unescape(s: &str) -> String {
    let mut new_string = s.to_string();
    for &(to, from) in ESCAPE_CHARS.iter() {
        new_string = new_string.replace(from, &to.to_string());
    }
    new_string
}
<commit_msg>changing the strings in ESCAPE_CHARS to raw strings<commit_after>//! escaping provides functions for the Server Query escaping.
//!
//! # Example
//! ```
//! use sqlib::escaping::{escape, unescape};
//!
//! let unescaped = "hello world/|\\".to_string();
//! let escaped = "hello\\sworld\\/\\p\\\\".to_string();
//!
//! let escaped_test = escape(&unescaped);
//! let unescaped_test = unescape(&escaped);
//!
//! assert_eq!(escaped_test, escaped);
//! assert_eq!(unescaped_test, unescaped);
//! ```

const ESCAPE_CHARS: [(char, &'static str); 11] = [('\\', r"\\"),
                                                  (' ', r"\s"),
                                                  ('/', r"\/"),
                                                  ('|', r"\p"),
                                                  (7 as char, r"\a"),
                                                  (8 as char, r"\b"),
                                                  (9 as char, r"\t"),
                                                  (10 as char, r"\n"),
                                                  (11 as char, r"\v"),
                                                  (12 as char, r"\f"),
                                                  (13 as char, r"\r")];

/// escapes all chars described in the server query manual
///
/// # Example
/// ```
/// use sqlib::escaping::escape;
///
/// let unescaped = "hello world/|\\".to_string();
/// let escaped = "hello\\sworld\\/\\p\\\\".to_string();
///
/// let s = escape(&unescaped);
///
/// assert_eq!(s, escaped);
/// ```
pub fn escape(s: &str) -> String {
    let mut new_string = s.to_string();
    for &(from, to) in ESCAPE_CHARS.iter() {
        new_string = new_string.replace(from, to);
    }
    new_string
}

/// unescapes all chars described in the server query manual
///
/// # Example
/// ```
/// use sqlib::escaping::unescape;
///
/// let unescaped = "hello world/|\\".to_string();
/// let escaped = "hello\\sworld\\/\\p\\\\".to_string();
///
/// let s = unescape(&escaped);
///
/// assert_eq!(s, unescaped);
/// ```
pub fn unescape(s: &str) -> String {
    let mut new_string = s.to_string();
    for &(to, from) in ESCAPE_CHARS.iter() {
        new_string = new_string.replace(from, &to.to_string());
    }
    new_string
}
<|endoftext|>


<commit_before>use crate::Error;
use crossbeam_channel::{Receiver, Sender};
use parking_lot::Mutex;
use std::{
    sync::Arc,
    thread::{Builder as ThreadBuilder, JoinHandle},
};

pub trait Executor: std::fmt::Debug + Send + Sync {
    fn execute(&self, f: Box<dyn FnOnce() + Send>) -> Result<(), Error>;
}

#[derive(Clone, Debug)]
pub struct DefaultExecutor {
    sender: Sender<Box<dyn FnOnce() + Send>>,
    receiver: Receiver<Box<dyn FnOnce() + Send>>,
    threads: Arc<Mutex<Vec<JoinHandle<()>>>>,
    max_threads: usize,
}

impl DefaultExecutor {
    pub fn new(max_threads: usize) -> Arc<Self> {
        let (sender, receiver) = crossbeam_channel::unbounded();
        Arc::new(Self {
            sender,
            receiver,
            threads: Default::default(),
            max_threads,
        })
    }

    pub(crate) fn default() -> Arc<Self> {
        Self::new(1)
    }
}

impl DefaultExecutor {
    pub(crate) fn maybe_spawn_thread(&self) -> Result<(), Error> {
        let mut threads = self.threads.lock();
        let id = threads.len() + 1;
        if id <= self.max_threads {
            let receiver = self.receiver.clone();
            threads.push(
                ThreadBuilder::new()
                    .name(format!("executor {}", id))
                    .spawn(move || loop {
                        if let Ok(f) = receiver.recv() {
                            f();
                        } else {
                            return;
                        }
                    })
                    .map_err(Error::IOError)?,
            );
        }
        Ok(())
    }
}

impl Executor for DefaultExecutor {
    fn execute(&self, f: Box<dyn FnOnce() + Send>) -> Result<(), Error> {
        self.maybe_spawn_thread()?;
        self.sender.send(f).expect("executor failed");
        Ok(())
    }
}
<commit_msg>simplify executor<commit_after>use crate::Error;
use crossbeam_channel::{Receiver, Sender};
use parking_lot::Mutex;
use std::{
    sync::Arc,
    thread::{Builder as ThreadBuilder, JoinHandle},
};

pub trait Executor: std::fmt::Debug + Send + Sync {
    fn execute(&self, f: Box<dyn FnOnce() + Send>) -> Result<(), Error>;
}

#[derive(Clone, Debug)]
pub struct DefaultExecutor {
    sender: Sender<Box<dyn FnOnce() + Send>>,
    receiver: Receiver<Box<dyn FnOnce() + Send>>,
    threads: Arc<Mutex<Vec<JoinHandle<()>>>>,
    max_threads: usize,
}

impl DefaultExecutor {
    pub fn new(max_threads: usize) -> Arc<Self> {
        let (sender, receiver) = crossbeam_channel::unbounded();
        Arc::new(Self {
            sender,
            receiver,
            threads: Default::default(),
            max_threads,
        })
    }

    pub(crate) fn default() -> Arc<Self> {
        Self::new(1)
    }
}

impl DefaultExecutor {
    pub(crate) fn maybe_spawn_thread(&self) -> Result<(), Error> {
        let mut threads = self.threads.lock();
        let id = threads.len() + 1;
        if id <= self.max_threads {
            let receiver = self.receiver.clone();
            threads.push(
                ThreadBuilder::new()
                    .name(format!("executor {}", id))
                    .spawn(move || for f in receiver {
                        f();
                    })
                    .map_err(Error::IOError)?,
            );
        }
        Ok(())
    }
}

impl Executor for DefaultExecutor {
    fn execute(&self, f: Box<dyn FnOnce() + Send>) -> Result<(), Error> {
        self.maybe_spawn_thread()?;
        self.sender.send(f).expect("executor failed");
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::fs::File;
use std::io;
use std::fs::OpenOptions;
use std::os::unix::io::{RawFd, AsRawFd};
use std::fmt;
use std::error::Error as ErrorTrait;

use fd;
pub use util::{Mode, Kind, ParseError};

#[derive(Debug)]
pub enum Error {
    LockError(fd::Error),
    IoError(PathBuf, io::Error),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Error::IoError(ref path, ref err)
                => write!(f, "Couldn't open lock file at '{}': {}", path.display(), err),
            Error::LockError(ref err)
                => err.fmt(f),
        }
    }
}

impl ErrorTrait for Error {
    fn description(&self) -> &str {
        match *self {
            Error::IoError(_, _)
                => "Could not open or create the file to lock",
            Error::LockError(_)
                => "Failed to obtain a file lock"
        }
    }
}

unsafe impl Send for Error {}

impl From<fd::Error> for Error {
    fn from(err: fd::Error) -> Self {
        Error::LockError(err)
    }
}

/// A type creating a lock file on demand.
///
/// It supports multiple reader, single writer semantics and encodes
/// whether read or write access is required in an interface similar
/// to the one of the [`RwLock`](http://doc.rust-lang.org/std/sync/struct.RwLock.html)
///
/// It will remove the lock file it possibly created in case a lock could be obtained.
#[derive(Debug)]
pub struct Lock {
    path: PathBuf,
    file: Option<File>,
    mode: Mode
}

impl Lock {
    pub fn new(path: PathBuf, mode: Mode) -> Lock {
        Lock {
            path: path,
            file: None,
            mode: mode,
        }
    }

    fn opened_file_fd(&mut self) -> Result<RawFd, io::Error> {
        if let Some(ref file) = self.file {
            return Ok(file.as_raw_fd())
        }

        let (raw_fd, file) = match OpenOptions::new()
                                   .create(true)
                                   .read(self.mode == Mode::Read)
                                   .write(self.mode == Mode::Write)
                                   .open(&self.path) {
            Err(io_err) => return Err(io_err),
            Ok(file) => (file.as_raw_fd(), Some(file))
        };

        self.file = file;
        Ok(raw_fd)
    }

    pub fn any_lock(&mut self, kind: Kind) -> Result<(), Error> {
        let fd = match self.opened_file_fd() {
            Ok(fd) => fd,
            Err(io_err) => return Err(Error::IoError(self.path.clone(), io_err))
        };

        Ok(try!(fd::lock(fd, kind, self.mode.clone())))
    }

    pub fn lock(&mut self) -> Result<(), Error> {
        self.any_lock(Kind::Blocking)
    }

    pub fn try_lock(&mut self) -> Result<(), Error> {
        self.any_lock(Kind::NonBlocking)
    }

    pub fn unlock(&mut self) -> Result<(), Error> {
        match self.file {
            Some(ref file) => Ok(try!(fd::unlock(file.as_raw_fd()))),
            None => Err(Error::IoError(self.path.clone(),
                                       io::Error::new(io::ErrorKind::NotFound,
                                       "unlock() called before lock() or try_lock()").into()))
        }
    }

    pub fn path(&self) -> &PathBuf {
        &self.path
    }

    pub fn file(&mut self) -> Option<&mut File> {
        self.file.as_mut()
    }
}

impl Drop for Lock {
    fn drop(&mut self) {
        self.unlock().ok();
    }
}<commit_msg>imp(filename): provide path info on LockError<commit_after>use std::path::PathBuf;
use std::fs::File;
use std::io;
use std::fs::OpenOptions;
use std::os::unix::io::{RawFd, AsRawFd};
use std::fmt;
use std::error::Error as ErrorTrait;

use fd;
pub use util::{Mode, Kind, ParseError};

#[derive(Debug)]
pub enum Error {
    LockError(PathBuf, fd::Error),
    IoError(PathBuf, io::Error),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Error::IoError(ref path, ref err)
                => write!(f, "Couldn't open lock file at '{}': {}", path.display(), err),
            Error::LockError(ref path, ref err)
                => write!(f, "{} (at file '{}')", err, path.display()),
        }
    }
}

impl ErrorTrait for Error {
    fn description(&self) -> &str {
        match *self {
            Error::IoError(_, _)
                => "Could not open or create the file to lock",
            Error::LockError(_, _)
                => "Failed to obtain a file lock"
        }
    }
}

unsafe impl Send for Error {}

/// A type creating a lock file on demand.
///
/// It supports multiple reader, single writer semantics and encodes
/// whether read or write access is required in an interface similar
/// to the one of the [`RwLock`](http://doc.rust-lang.org/std/sync/struct.RwLock.html)
///
/// It will remove the lock file it possibly created in case a lock could be obtained.
#[derive(Debug)]
pub struct Lock {
    path: PathBuf,
    file: Option<File>,
    mode: Mode
}

impl Lock {
    pub fn new(path: PathBuf, mode: Mode) -> Lock {
        Lock {
            path: path,
            file: None,
            mode: mode,
        }
    }

    fn opened_file_fd(&mut self) -> Result<RawFd, io::Error> {
        if let Some(ref file) = self.file {
            return Ok(file.as_raw_fd())
        }

        let (raw_fd, file) = match OpenOptions::new()
                                   .create(true)
                                   .read(self.mode == Mode::Read)
                                   .write(self.mode == Mode::Write)
                                   .open(&self.path) {
            Err(io_err) => return Err(io_err),
            Ok(file) => (file.as_raw_fd(), Some(file))
        };

        self.file = file;
        Ok(raw_fd)
    }

    pub fn any_lock(&mut self, kind: Kind) -> Result<(), Error> {
        let fd = match self.opened_file_fd() {
            Ok(fd) => fd,
            Err(io_err) => return Err(Error::IoError(self.path.clone(), io_err))
        };

        match fd::lock(fd, kind, self.mode.clone()) {
            Ok(res) => Ok(res),
            Err(lock_err) => Err(Error::LockError(self.path.clone(), lock_err)),
        }
    }

    pub fn lock(&mut self) -> Result<(), Error> {
        self.any_lock(Kind::Blocking)
    }

    pub fn try_lock(&mut self) -> Result<(), Error> {
        self.any_lock(Kind::NonBlocking)
    }

    pub fn unlock(&mut self) -> Result<(), Error> {
        match self.file {
            Some(ref file) => match fd::unlock(file.as_raw_fd()) {
                Ok(res) => Ok(res),
                Err(lock_err) => Err(Error::LockError(self.path.clone(), lock_err)),
            },
            None => Err(Error::IoError(self.path.clone(),
                                       io::Error::new(io::ErrorKind::NotFound,
                                       "unlock() called before lock() or try_lock()").into()))
        }
    }

    pub fn path(&self) -> &PathBuf {
        &self.path
    }

    pub fn file(&mut self) -> Option<&mut File> {
        self.file.as_mut()
    }
}

impl Drop for Lock {
    fn drop(&mut self) {
        self.unlock().ok();
    }
}<|endoftext|>


<commit_before>pub mod socket;
pub mod latency_recorder;

use std::ops::Not;
use std::sync::{Arc, Mutex};
use std::sync::mpsc;
use std::thread;

use rustc_serialize::json;
use rustc_serialize::Decoder;
use rustc_serialize::Decodable;
use uuid::Uuid;

use time;

use lila;
use lila::Session;

pub use game::latency_recorder::LatencyRecorder;

pub struct ConnectedPov {
    pub pov: Arc<Mutex<Pov>>,
    pub latency: Arc<Mutex<LatencyRecorder>>,
    send_tx: mpsc::Sender<String>,
}

#[derive(RustcDecodable)]
pub struct Pov {
    pub game: Game,
    pub clock: Option<Clock>,
    pub correspondence: Option<CorrespondenceClock>,
    pub url: GameUrl,
    pub player: Player,
    pub opponent: Player,
    pub tv: Option<Tv>,
    pub orientation: Option<Color>,
}

#[allow(non_camel_case_types)]
#[derive(PartialEq, Eq, Copy, Clone)]
#[derive(RustcDecodable)]
pub enum Color {
    white,
    black,
}

#[derive(RustcDecodable)]
pub struct Tv {
    pub channel: String,
    pub flip: bool,
}

#[derive(RustcDecodable)]
pub struct Clock {
    pub white: f64,
    pub black: f64,
    last_update: Time,
}

// TODO: ehm, maybe not like this
// wrap to implement trait on foreign type in this version of rust
pub struct Time(time::Tm);
impl Decodable for Time {
    fn decode<D: Decoder>(_d: &mut D) -> Result<Time, D::Error> {
        Ok(Time(time::now_utc()))
    }
}

#[derive(RustcDecodable)]
pub struct CorrespondenceClock {
    _todo: Option<String>,
}

#[derive(RustcDecodable)]
pub struct GameUrl {
    pub socket: String,
    pub round: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable)]
pub struct Game {
    pub id: String,
    pub variant: Variant,
    pub speed: String,
    pub perf: String,
    pub rated: bool,
    pub initialFen: String,
    pub fen: String,
    pub player: Color,
    pub turns: u64,
    pub startedAtTurn: i64,
    pub lastMove: Option<String>,
    pub threefold: bool,
    pub source: String,
    pub status: Status,
}

#[derive(RustcDecodable)]
pub struct Variant {
    pub key: String,
    pub name: String,
    pub short: String,
    pub title: String,
}

#[derive(RustcDecodable)]
pub struct Status {
    pub id: i64,
    pub name: String,
}

#[derive(RustcDecodable)]
pub struct Player {
    pub color: Color,
    pub version: Option<i64>,
    pub spectator: Option<bool>,
    pub user: Option<User>,
    pub rating: Option<i64>,
}

#[derive(RustcDecodable)]
pub struct User {
    pub id: String,
    pub username: String,
}

impl ConnectedPov {
    pub fn new(session: &lila::Session, path: &str) -> ConnectedPov {
        let mut body = String::new();
        session.get(path, &mut body);
        debug!("GET response: {}", body);
        let pov: Pov = json::decode(&body).unwrap();
        let version = match pov.player.version {
            Some(v) => v as u64,
            None => 0
        };
        let socket_path = pov.url.socket.clone();
        let pov_1 = Arc::new(Mutex::new(pov));
        let (game_tx, game_rx) = mpsc::channel();
        let (send_tx, send_rx) = mpsc::channel();
        let send_rx = Arc::new(Mutex::new(send_rx));

        let pov_2 = pov_1.clone();
        let c = session.cookie();
        let sri = Uuid::new_v4();
        debug!("SRI set to {}", sri);
        let url = Session::socket_url(&format!("{}?sri={}&version={}", socket_path, sri, version));
        thread::spawn(move || {
            socket::Client::connect(&c, url.clone(), version, game_tx.clone(), send_rx);
        });

        let latency_1 = Arc::new(Mutex::new(LatencyRecorder::new()));
        let latency_2 = latency_1.clone();
        thread::spawn(move || loop {
            let obj = game_rx.recv().unwrap();
            let mut pov = pov_2.lock().unwrap();
            match LilaMessage::decode(&obj) {
                Some(LilaMessage::Pong(p)) => {
                    latency_2.lock().unwrap().add(p.latency);
                },
                Some(LilaMessage::Move(m)) => {
                    pov.game.fen = m.fen;
                    pov.game.turns = m.ply;
                    pov.game.player = if m.ply % 2 == 0 { Color::white } else { Color::black };
                    if let Some(c) = m.clock {
                        pov.clock = Some(c);
                    };
                },
                Some(LilaMessage::Clock(c)) => {
                    pov.clock = Some(c);
                },
                //LilaMessage::End => tx_1.send(Message::close()).unwrap(),
                _ => ()
            };
        });

        ConnectedPov {
            pov: pov_1,
            latency: latency_1,
            send_tx: send_tx,
        }
    }

    pub fn send_move(&mut self, from: String, to: String) {
        let move_packet = MovePacket {
            t: "move".into(),
            l: None, // TODO
            d: Dest {
                from: from,
                to: to,
                promotion: None,
            },
        };
        let message = json::encode(&move_packet).unwrap();
        self.send_tx.send(message).unwrap();
    }
}

impl Pov {
    pub fn orientation(&self) -> Color {
        match self.orientation {
            Some(o) => o,
            None => {
                self.player.color
            }
        }
    }

    pub fn tick(&mut self) {
        // FUTURE: `let` is only needed bc rust borrow checker is lazy
        let color = self.game.player;
        self.clock.as_mut().map(|c| c.tick(color));
    }
}

impl Not for Color {
    type Output = Color;
    fn not(self) -> Color {
        match self {
            Color::white => Color::black,
            Color::black => Color::white,
        }
    }
}

impl Clock {
    pub fn from(&self, color: Color) -> f64 {
        match color {
            Color::white => self.white,
            Color::black => self.black,
        }
    }

    pub fn tick(&mut self, color: Color) {
        let now = time::now_utc();
        let Time(updated) = self.last_update;
        let passed = ((now - updated).num_milliseconds() as f64) / 1000.0;
        self.last_update = Time(now);
        match color {
            // TODO: only tick when gameIsRunning instead of using max
            Color::white => self.white = (self.white - passed).max(0.0),
            Color::black => self.black = (self.black - passed).max(0.0),
        };
    }
}

enum LilaMessage {
    Pong(Pong),
    Move(Move),
    Clock(Clock),
}

impl LilaMessage {
    fn decode(obj: &json::Object) -> Option<LilaMessage> {
        fn decode<T: Decodable>(data: &json::Json) -> Option<T> {
            let mut decoder = json::Decoder::new(data.to_owned());
            Decodable::decode(&mut decoder)
                .map_err(|e| error!("could not decode: {}", e)).ok()
        }
        let data = obj.get("d");
        match (obj.get("t").and_then(|t| t.as_string()), data) {
            // TODO: gone, crowd, end, tvSelect, challenges, drop,
            // following_enters, following_leaves, following_onlines,
            // following_playing, following_stopped_plaing,
            // message, analysisProgress, reload, and more
            (Some("n"), Some(data)) => decode(data).map(|d| LilaMessage::Pong(d)),
            (Some("move"), Some(data)) => decode(data).map(|d| LilaMessage::Move(d)),
            (Some("clock"), Some(data)) => decode(data).map(|d| LilaMessage::Clock(d)),
            (Some(ref t), d) => {
                warn!("unhandled: {}, {:?}", t, d);
                None
            },
            _ => {
                warn!("unhandled: Missing type");
                None
            }
        }
    }

}

#[derive(RustcDecodable)]
struct Pong {
    latency: i64,
}

#[allow(dead_code)]
#[derive(RustcDecodable)]
struct Move {
    clock: Option<Clock>,
    // "dests": Object({"a6": String("a5"), "c7": String("b8d8b6a5d6e5f4g3h2"), "d5": String("c6e6"), "f5": String("f4")}),
    dests: String,
    fen: String,
    san: String, // Bc7
    uci: String, // e5c7
    ply: u64,
}

#[derive(RustcEncodable)]
pub struct MovePacket {
    t: String,
    d: Dest,
    l: Option<i64>,
}

#[derive(RustcEncodable)]
pub struct Dest {
    pub from: String,
    pub to: String,
    pub promotion: Option<String>,
}

<commit_msg>fix broken move messages (could't decode)<commit_after>pub mod socket;
pub mod latency_recorder;

use std::ops::Not;
use std::sync::{Arc, Mutex};
use std::sync::mpsc;
use std::thread;

use rustc_serialize::json;
use rustc_serialize::Decoder;
use rustc_serialize::Decodable;
use uuid::Uuid;

use time;

use lila;
use lila::Session;

pub use game::latency_recorder::LatencyRecorder;

pub struct ConnectedPov {
    pub pov: Arc<Mutex<Pov>>,
    pub latency: Arc<Mutex<LatencyRecorder>>,
    send_tx: mpsc::Sender<String>,
}

#[derive(RustcDecodable)]
pub struct Pov {
    pub game: Game,
    pub clock: Option<Clock>,
    pub correspondence: Option<CorrespondenceClock>,
    pub url: GameUrl,
    pub player: Player,
    pub opponent: Player,
    pub tv: Option<Tv>,
    pub orientation: Option<Color>,
}

#[allow(non_camel_case_types)]
#[derive(PartialEq, Eq, Copy, Clone)]
#[derive(RustcDecodable)]
pub enum Color {
    white,
    black,
}

#[derive(RustcDecodable)]
pub struct Tv {
    pub channel: String,
    pub flip: bool,
}

#[derive(RustcDecodable)]
pub struct Clock {
    pub white: f64,
    pub black: f64,
    last_update: Time,
}

// TODO: ehm, maybe not like this
// wrap to implement trait on foreign type in this version of rust
pub struct Time(time::Tm);
impl Decodable for Time {
    fn decode<D: Decoder>(_d: &mut D) -> Result<Time, D::Error> {
        Ok(Time(time::now_utc()))
    }
}

#[derive(RustcDecodable)]
pub struct CorrespondenceClock {
    _todo: Option<String>,
}

#[derive(RustcDecodable)]
pub struct GameUrl {
    pub socket: String,
    pub round: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable)]
pub struct Game {
    pub id: String,
    pub variant: Variant,
    pub speed: String,
    pub perf: String,
    pub rated: bool,
    pub initialFen: String,
    pub fen: String,
    pub player: Color,
    pub turns: u64,
    pub startedAtTurn: i64,
    pub lastMove: Option<String>,
    pub threefold: bool,
    pub source: String,
    pub status: Status,
}

#[derive(RustcDecodable)]
pub struct Variant {
    pub key: String,
    pub name: String,
    pub short: String,
    pub title: String,
}

#[derive(RustcDecodable)]
pub struct Status {
    pub id: i64,
    pub name: String,
}

#[derive(RustcDecodable)]
pub struct Player {
    pub color: Color,
    pub version: Option<i64>,
    pub spectator: Option<bool>,
    pub user: Option<User>,
    pub rating: Option<i64>,
}

#[derive(RustcDecodable)]
pub struct User {
    pub id: String,
    pub username: String,
}

impl ConnectedPov {
    pub fn new(session: &lila::Session, path: &str) -> ConnectedPov {
        let mut body = String::new();
        session.get(path, &mut body);
        debug!("GET response: {}", body);
        let pov: Pov = json::decode(&body).unwrap();
        let version = match pov.player.version {
            Some(v) => v as u64,
            None => 0
        };
        let socket_path = pov.url.socket.clone();
        let pov_1 = Arc::new(Mutex::new(pov));
        let (game_tx, game_rx) = mpsc::channel();
        let (send_tx, send_rx) = mpsc::channel();
        let send_rx = Arc::new(Mutex::new(send_rx));

        let pov_2 = pov_1.clone();
        let c = session.cookie();
        let sri = Uuid::new_v4();
        debug!("SRI set to {}", sri);
        let url = Session::socket_url(&format!("{}?sri={}&version={}", socket_path, sri, version));
        thread::spawn(move || {
            socket::Client::connect(&c, url.clone(), version, game_tx.clone(), send_rx);
        });

        let latency_1 = Arc::new(Mutex::new(LatencyRecorder::new()));
        let latency_2 = latency_1.clone();
        thread::spawn(move || loop {
            let obj = game_rx.recv().unwrap();
            let mut pov = pov_2.lock().unwrap();
            match LilaMessage::decode(&obj) {
                Some(LilaMessage::Pong(p)) => {
                    latency_2.lock().unwrap().add(p.latency);
                },
                Some(LilaMessage::Move(m)) => {
                    pov.game.fen = m.fen;
                    pov.game.turns = m.ply;
                    pov.game.player = if m.ply % 2 == 0 { Color::white } else { Color::black };
                    if let Some(c) = m.clock {
                        pov.clock = Some(c);
                    };
                },
                Some(LilaMessage::Clock(c)) => {
                    pov.clock = Some(c);
                },
                //LilaMessage::End => tx_1.send(Message::close()).unwrap(),
                _ => ()
            };
        });

        ConnectedPov {
            pov: pov_1,
            latency: latency_1,
            send_tx: send_tx,
        }
    }

    pub fn send_move(&mut self, from: String, to: String) {
        let move_packet = MovePacket {
            t: "move".into(),
            l: None, // TODO
            d: Dest {
                from: from,
                to: to,
                promotion: None,
            },
        };
        let message = json::encode(&move_packet).unwrap();
        self.send_tx.send(message).unwrap();
    }
}

impl Pov {
    pub fn orientation(&self) -> Color {
        match self.orientation {
            Some(o) => o,
            None => {
                self.player.color
            }
        }
    }

    pub fn tick(&mut self) {
        // FUTURE: `let` is only needed bc rust borrow checker is lazy
        let color = self.game.player;
        self.clock.as_mut().map(|c| c.tick(color));
    }
}

impl Not for Color {
    type Output = Color;
    fn not(self) -> Color {
        match self {
            Color::white => Color::black,
            Color::black => Color::white,
        }
    }
}

impl Clock {
    pub fn from(&self, color: Color) -> f64 {
        match color {
            Color::white => self.white,
            Color::black => self.black,
        }
    }

    pub fn tick(&mut self, color: Color) {
        let now = time::now_utc();
        let Time(updated) = self.last_update;
        let passed = ((now - updated).num_milliseconds() as f64) / 1000.0;
        self.last_update = Time(now);
        match color {
            // TODO: only tick when gameIsRunning instead of using max
            Color::white => self.white = (self.white - passed).max(0.0),
            Color::black => self.black = (self.black - passed).max(0.0),
        };
    }
}

enum LilaMessage {
    Pong(Pong),
    Move(Move),
    Clock(Clock),
}

impl LilaMessage {
    fn decode(obj: &json::Object) -> Option<LilaMessage> {
        fn decode<T: Decodable>(data: &json::Json) -> Option<T> {
            let mut decoder = json::Decoder::new(data.to_owned());
            Decodable::decode(&mut decoder)
                .map_err(|e| error!("could not decode: {}", e)).ok()
        }
        let data = obj.get("d");
        match (obj.get("t").and_then(|t| t.as_string()), data) {
            // TODO: gone, crowd, end, tvSelect, challenges, drop,
            // following_enters, following_leaves, following_onlines,
            // following_playing, following_stopped_plaing,
            // message, analysisProgress, reload, and more
            (Some("n"), Some(data)) => decode(data).map(|d| LilaMessage::Pong(d)),
            (Some("move"), Some(data)) => decode(data).map(|d| LilaMessage::Move(d)),
            (Some("clock"), Some(data)) => decode(data).map(|d| LilaMessage::Clock(d)),
            (Some(ref t), d) => {
                warn!("unhandled: {}, {:?}", t, d);
                None
            },
            _ => {
                warn!("unhandled: Missing type");
                None
            }
        }
    }

}

#[derive(RustcDecodable)]
struct Pong {
    latency: i64,
}

#[allow(dead_code)]
#[derive(RustcDecodable)]
struct Move {
    clock: Option<Clock>,
    // "dests": Object({"a6": String("a5"), "c7": String("b8d8b6a5d6e5f4g3h2"), "d5": String("c6e6"), "f5": String("f4")}),
    //dests: String,
    fen: String,
    san: String, // Bc7
    uci: String, // e5c7
    ply: u64,
}

#[derive(RustcEncodable)]
pub struct MovePacket {
    t: String,
    d: Dest,
    l: Option<i64>,
}

#[derive(RustcEncodable)]
pub struct Dest {
    pub from: String,
    pub to: String,
    pub promotion: Option<String>,
}

<|endoftext|>


<commit_before>use std::ptr;

use gc::Address;
use mem;
use os::page_size;

pub fn reserve(size: usize) -> Address {
    debug_assert!(mem::is_page_aligned(size));

    let ptr = unsafe {
        libc::mmap(
            ptr::null_mut(),
            size,
            libc::PROT_NONE,
            libc::MAP_PRIVATE | libc::MAP_ANON | libc::MAP_NORESERVE,
            -1,
            0,
        ) as *mut libc::c_void
    };

    if ptr == libc::MAP_FAILED {
        panic!("reserving memory with mmap() failed");
    }

    Address::from_ptr(ptr)
}

pub fn reserve_align(size: usize, align: usize) -> Address {
    debug_assert!(mem::is_page_aligned(size));
    debug_assert!(mem::is_page_aligned(align));

    let align_minus_page = align - page_size() as usize;

    let unaligned = reserve(size + align_minus_page);
    let aligned: Address = mem::align_usize(unaligned.to_usize(), align).into();

    let gap_start = aligned.offset_from(unaligned);
    let gap_end = align_minus_page - gap_start;

    if gap_start > 0 {
        uncommit(unaligned, gap_start);
    }

    if gap_end > 0 {
        uncommit(aligned.offset(size), gap_end);
    }

    aligned
}

pub fn commit(ptr: Address, size: usize, executable: bool) {
    debug_assert!(ptr.is_page_aligned());
    debug_assert!(mem::is_page_aligned(size));

    let mut prot = libc::PROT_READ | libc::PROT_WRITE;

    if executable {
        prot |= libc::PROT_EXEC;
    }

    let val = unsafe {
        libc::mmap(
            ptr.to_mut_ptr(),
            size,
            prot,
            libc::MAP_PRIVATE | libc::MAP_ANON | libc::MAP_FIXED,
            -1,
            0,
        )
    };

    if val == libc::MAP_FAILED {
        panic!("committing memory with mmap() failed");
    }
}

pub fn uncommit(ptr: Address, size: usize) {
    debug_assert!(ptr.is_page_aligned());
    debug_assert!(mem::is_page_aligned(size));

    let val = unsafe {
        libc::mmap(
            ptr.to_mut_ptr(),
            size,
            libc::PROT_NONE,
            libc::MAP_PRIVATE | libc::MAP_ANON | libc::MAP_NORESERVE,
            -1,
            0,
        )
    };

    if val == libc::MAP_FAILED {
        panic!("uncommitting memory with mmap() failed");
    }
}

pub fn forget(ptr: Address, size: usize) {
    debug_assert!(ptr.is_page_aligned());
    debug_assert!(mem::is_page_aligned(size));

    let res = unsafe { libc::madvise(ptr.to_mut_ptr(), size, libc::MADV_DONTNEED) };

    if res != 0 {
        panic!("forgetting memory with madvise() failed");
    }
}
<commit_msg>swiper: invoke mprotect when forgetting memory<commit_after>use std::ptr;

use gc::Address;
use mem;
use os::page_size;

pub fn reserve(size: usize) -> Address {
    debug_assert!(mem::is_page_aligned(size));

    let ptr = unsafe {
        libc::mmap(
            ptr::null_mut(),
            size,
            libc::PROT_NONE,
            libc::MAP_PRIVATE | libc::MAP_ANON | libc::MAP_NORESERVE,
            -1,
            0,
        ) as *mut libc::c_void
    };

    if ptr == libc::MAP_FAILED {
        panic!("reserving memory with mmap() failed");
    }

    Address::from_ptr(ptr)
}

pub fn reserve_align(size: usize, align: usize) -> Address {
    debug_assert!(mem::is_page_aligned(size));
    debug_assert!(mem::is_page_aligned(align));

    let align_minus_page = align - page_size() as usize;

    let unaligned = reserve(size + align_minus_page);
    let aligned: Address = mem::align_usize(unaligned.to_usize(), align).into();

    let gap_start = aligned.offset_from(unaligned);
    let gap_end = align_minus_page - gap_start;

    if gap_start > 0 {
        uncommit(unaligned, gap_start);
    }

    if gap_end > 0 {
        uncommit(aligned.offset(size), gap_end);
    }

    aligned
}

pub fn commit(ptr: Address, size: usize, executable: bool) {
    debug_assert!(ptr.is_page_aligned());
    debug_assert!(mem::is_page_aligned(size));

    let mut prot = libc::PROT_READ | libc::PROT_WRITE;

    if executable {
        prot |= libc::PROT_EXEC;
    }

    let val = unsafe {
        libc::mmap(
            ptr.to_mut_ptr(),
            size,
            prot,
            libc::MAP_PRIVATE | libc::MAP_ANON | libc::MAP_FIXED,
            -1,
            0,
        )
    };

    if val == libc::MAP_FAILED {
        panic!("committing memory with mmap() failed");
    }
}

pub fn uncommit(ptr: Address, size: usize) {
    debug_assert!(ptr.is_page_aligned());
    debug_assert!(mem::is_page_aligned(size));

    let val = unsafe {
        libc::mmap(
            ptr.to_mut_ptr(),
            size,
            libc::PROT_NONE,
            libc::MAP_PRIVATE | libc::MAP_ANON | libc::MAP_NORESERVE,
            -1,
            0,
        )
    };

    if val == libc::MAP_FAILED {
        panic!("uncommitting memory with mmap() failed");
    }
}

pub fn forget(ptr: Address, size: usize) {
    debug_assert!(ptr.is_page_aligned());
    debug_assert!(mem::is_page_aligned(size));

    let res = unsafe { libc::madvise(ptr.to_mut_ptr(), size, libc::MADV_DONTNEED) };

    if res != 0 {
        panic!("forgetting memory with madvise() failed");
    }

    let res = unsafe { libc::mprotect(ptr.to_mut_ptr(), size, libc::PROT_NONE) };

    if res != 0 {
        panic!("forgetting memory with mprotect() failed");
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Drew Short <drew@sothr.com>.
//
// Licensed under the MIT license<LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms.

extern crate dft;
extern crate image;

use std::f64;
use std::fmt;
use std::path::Path;

use cache::Cache;

use self::image::FilterType;

mod ahash;
mod dhash;
mod phash;

// Constants //

// Used to get ranges for the precision of rounding floats
// Can round to 1 significant factor of precision
const FLOAT_PRECISION_MAX_1: f64 = f64::MAX / 10_f64;
const FLOAT_PRECISION_MIN_1: f64 = f64::MIN / 10_f64;
// Can round to 2 significant factors of precision
const FLOAT_PRECISION_MAX_2: f64 = f64::MAX / 100_f64;
const FLOAT_PRECISION_MIN_2: f64 = f64::MIN / 100_f64;
// Can round to 3 significant factors of precision
const FLOAT_PRECISION_MAX_3: f64 = f64::MAX / 1000_f64;
const FLOAT_PRECISION_MIN_3: f64 = f64::MIN / 1000_f64;
// Can round to 4 significant factors of precision
const FLOAT_PRECISION_MAX_4: f64 = f64::MAX / 10000_f64;
const FLOAT_PRECISION_MIN_4: f64 = f64::MIN / 10000_f64;
// Can round to 5 significant factors of precision
const FLOAT_PRECISION_MAX_5: f64 = f64::MAX / 100000_f64;
const FLOAT_PRECISION_MIN_5: f64 = f64::MIN / 100000_f64;
// Hamming Distance Similarity Limit //
const HAMMING_DISTANCE_SIMILARITY_LIMIT: u64 = 5u64;

// Structs/Enums //

/**
 * Prepared image that can be used to generate hashes
 */
pub struct PreparedImage {
    orig_path: String,
    image: Option<image::DynamicImage>,
}

/**
 * Wraps the various perceptual hashes
 */
pub struct PerceptualHashes {
    pub orig_path: String,
    pub ahash: u64,
    pub dhash: u64,
    pub phash: u64,
}

impl PerceptualHashes {
    pub fn similar(&self, other: &PerceptualHashes) -> bool {
        if self.orig_path != other.orig_path
            && calculate_hamming_distance(self.ahash, other.ahash)
                <= HAMMING_DISTANCE_SIMILARITY_LIMIT
            && calculate_hamming_distance(self.dhash, other.dhash)
                <= HAMMING_DISTANCE_SIMILARITY_LIMIT
            && calculate_hamming_distance(self.phash, other.phash)
                <= HAMMING_DISTANCE_SIMILARITY_LIMIT
        {
            true
        } else {
            false
        }
    }
}

/**
 * All the supported precision types
 *
 * Low aims for 32 bit precision
 * Medium aims for 64 bit precision
 * High aims for 128 bit precision
 */
#[allow(dead_code)]
#[derive(Copy, Clone)]
pub enum Precision {
    Low,
    Medium,
    High,
}

// Get the size of the required image
impl Precision {
    fn get_size(&self) -> u32 {
        match *self {
            Precision::Low => 4,
            Precision::Medium => 8,
            Precision::High => 16,
        }
    }
}

/**
 * Types of hashes supported
 */
#[derive(Copy, Clone)]
pub enum HashType {
    AHash,
    DHash,
    PHash,
}

impl fmt::Display for HashType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            HashType::AHash => write!(f, "AHash"),
            HashType::DHash => write!(f, "DHash"),
            HashType::PHash => write!(f, "PHash"),
        }
    }
}

// Traits //

pub trait PerceptualHash {
    fn get_hash(&self, cache: &Option<Cache>) -> u64;
}

// Functions //

/**
 * Resonsible for parsing a path, converting an image and package it to be
 * hashed.
 *
 * # Arguments
 *
 * * 'path' - The path to the image requested to be hashed
 * * 'size' - The size that the image should be resize to, in the form of size x size
 *
 * # Returns
 *
 * A PreparedImage struct with the required information for performing hashing
 *
 */
pub fn prepare_image(
    path: &Path,
    hash_type: &HashType,
    precision: &Precision,
    cache: &Option<Cache>,
) -> PreparedImage {
    let image_path = path.to_str().unwrap();
    let size: u32 = match *hash_type {
        HashType::PHash => precision.get_size() * 4,
        _ => precision.get_size(),
    };
    // Check if we have the already converted image in a cache and use that if possible.
    match *cache {
        Some(ref cache) => {
            match cache.get_image_from_cache(&path, size) {
                Some(image) => PreparedImage {
                    orig_path: String::from(&*image_path),
                    image: Some(image),
                },
                None => {
                    let processed_image = process_image(&image_path, size);
                    // Oh, and save it in a cache
                    match processed_image.image {
                        Some(ref image) => {
                            match cache.put_image_in_cache(&path, size, &image) {
                                Ok(_) => {}
                                Err(e) => println!("Unable to store image in cache. {}", e),
                            };
                        }
                        None => {}
                    };
                    processed_image
                }
            }
        }
        None => process_image(&image_path, size),
    }
}

/**
 * Turn the image into something we can work with
 */
fn process_image(image_path: &str, size: u32) -> PreparedImage {
    // Otherwise let's do that work now and store it.
    // println!("Path: {}", image_path);
    let image = match image::open(Path::new(image_path)) {
        Ok(image) => {
            let small_image = image.resize_exact(size, size, FilterType::Lanczos3);
            Some(small_image.grayscale())
        }
        Err(e) => {
            println!("Error Processing Image [{}]: {} ", image_path, e);
            None
        }
    };
    PreparedImage {
        orig_path: String::from(&*image_path),
        image,
    }
}

/**
 * Get a specific HashType hash
 */
pub fn get_perceptual_hash(
    path: &Path,
    precision: &Precision,
    hash_type: &HashType,
    cache: &Option<Cache>,
) -> u64 {
    match *hash_type {
        HashType::AHash => ahash::AHash::new(&path, &precision, &cache).get_hash(&cache),
        HashType::DHash => dhash::DHash::new(&path, &precision, &cache).get_hash(&cache),
        HashType::PHash => phash::PHash::new(&path, &precision, &cache).get_hash(&cache),
    }
}

/**
 * Get all perceptual hashes for an image
 */
pub fn get_perceptual_hashes(
    path: &Path,
    precision: &Precision,
    cache: &Option<Cache>,
) -> PerceptualHashes {
    let image_path = path.to_str().unwrap();
    let ahash = ahash::AHash::new(&path, &precision, &cache).get_hash(&cache);
    let dhash = dhash::DHash::new(&path, &precision, &cache).get_hash(&cache);
    let phash = phash::PHash::new(&path, &precision, &cache).get_hash(&cache);
    PerceptualHashes {
        orig_path: String::from(&*image_path),
        ahash: ahash,
        dhash: dhash,
        phash: phash,
    }
}

/**
 * Calculate the number of bits different between two hashes
 * Add to the PerceptualHashTrait
 */
pub fn calculate_hamming_distance(hash1: u64, hash2: u64) -> u64 {
    // The binary xor of the two hashes should give us a number representing
    // the differences between the two hashes. All that's left is to count
    // the number of 1's in the difference to determine the hamming distance
    (hash1 ^ hash2).count_ones() as u64
}
<commit_msg>Remove old directive<commit_after>// Copyright 2016 Drew Short <drew@sothr.com>.
//
// Licensed under the MIT license<LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms.

extern crate dft;
extern crate image;

use std::f64;
use std::fmt;
use std::path::Path;

use cache::Cache;

use self::image::FilterType;

mod ahash;
mod dhash;
mod phash;

// Constants //

// Used to get ranges for the precision of rounding floats
// Can round to 1 significant factor of precision
const FLOAT_PRECISION_MAX_1: f64 = f64::MAX / 10_f64;
const FLOAT_PRECISION_MIN_1: f64 = f64::MIN / 10_f64;
// Can round to 2 significant factors of precision
const FLOAT_PRECISION_MAX_2: f64 = f64::MAX / 100_f64;
const FLOAT_PRECISION_MIN_2: f64 = f64::MIN / 100_f64;
// Can round to 3 significant factors of precision
const FLOAT_PRECISION_MAX_3: f64 = f64::MAX / 1000_f64;
const FLOAT_PRECISION_MIN_3: f64 = f64::MIN / 1000_f64;
// Can round to 4 significant factors of precision
const FLOAT_PRECISION_MAX_4: f64 = f64::MAX / 10000_f64;
const FLOAT_PRECISION_MIN_4: f64 = f64::MIN / 10000_f64;
// Can round to 5 significant factors of precision
const FLOAT_PRECISION_MAX_5: f64 = f64::MAX / 100000_f64;
const FLOAT_PRECISION_MIN_5: f64 = f64::MIN / 100000_f64;
// Hamming Distance Similarity Limit //
const HAMMING_DISTANCE_SIMILARITY_LIMIT: u64 = 5u64;

// Structs/Enums //

/**
 * Prepared image that can be used to generate hashes
 */
pub struct PreparedImage {
    orig_path: String,
    image: Option<image::DynamicImage>,
}

/**
 * Wraps the various perceptual hashes
 */
pub struct PerceptualHashes {
    pub orig_path: String,
    pub ahash: u64,
    pub dhash: u64,
    pub phash: u64,
}

impl PerceptualHashes {
    pub fn similar(&self, other: &PerceptualHashes) -> bool {
        if self.orig_path != other.orig_path
            && calculate_hamming_distance(self.ahash, other.ahash)
                <= HAMMING_DISTANCE_SIMILARITY_LIMIT
            && calculate_hamming_distance(self.dhash, other.dhash)
                <= HAMMING_DISTANCE_SIMILARITY_LIMIT
            && calculate_hamming_distance(self.phash, other.phash)
                <= HAMMING_DISTANCE_SIMILARITY_LIMIT
        {
            true
        } else {
            false
        }
    }
}

/**
 * All the supported precision types
 *
 * Low aims for 32 bit precision
 * Medium aims for 64 bit precision
 * High aims for 128 bit precision
 */
#[derive(Copy, Clone)]
pub enum Precision {
    Low,
    Medium,
    High,
}

// Get the size of the required image
impl Precision {
    fn get_size(&self) -> u32 {
        match *self {
            Precision::Low => 4,
            Precision::Medium => 8,
            Precision::High => 16,
        }
    }
}

/**
 * Types of hashes supported
 */
#[derive(Copy, Clone)]
pub enum HashType {
    AHash,
    DHash,
    PHash,
}

impl fmt::Display for HashType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            HashType::AHash => write!(f, "AHash"),
            HashType::DHash => write!(f, "DHash"),
            HashType::PHash => write!(f, "PHash"),
        }
    }
}

// Traits //

pub trait PerceptualHash {
    fn get_hash(&self, cache: &Option<Cache>) -> u64;
}

// Functions //

/**
 * Resonsible for parsing a path, converting an image and package it to be
 * hashed.
 *
 * # Arguments
 *
 * * 'path' - The path to the image requested to be hashed
 * * 'size' - The size that the image should be resize to, in the form of size x size
 *
 * # Returns
 *
 * A PreparedImage struct with the required information for performing hashing
 *
 */
pub fn prepare_image(
    path: &Path,
    hash_type: &HashType,
    precision: &Precision,
    cache: &Option<Cache>,
) -> PreparedImage {
    let image_path = path.to_str().unwrap();
    let size: u32 = match *hash_type {
        HashType::PHash => precision.get_size() * 4,
        _ => precision.get_size(),
    };
    // Check if we have the already converted image in a cache and use that if possible.
    match *cache {
        Some(ref cache) => {
            match cache.get_image_from_cache(&path, size) {
                Some(image) => PreparedImage {
                    orig_path: String::from(&*image_path),
                    image: Some(image),
                },
                None => {
                    let processed_image = process_image(&image_path, size);
                    // Oh, and save it in a cache
                    match processed_image.image {
                        Some(ref image) => {
                            match cache.put_image_in_cache(&path, size, &image) {
                                Ok(_) => {}
                                Err(e) => println!("Unable to store image in cache. {}", e),
                            };
                        }
                        None => {}
                    };
                    processed_image
                }
            }
        }
        None => process_image(&image_path, size),
    }
}

/**
 * Turn the image into something we can work with
 */
fn process_image(image_path: &str, size: u32) -> PreparedImage {
    // Otherwise let's do that work now and store it.
    // println!("Path: {}", image_path);
    let image = match image::open(Path::new(image_path)) {
        Ok(image) => {
            let small_image = image.resize_exact(size, size, FilterType::Lanczos3);
            Some(small_image.grayscale())
        }
        Err(e) => {
            println!("Error Processing Image [{}]: {} ", image_path, e);
            None
        }
    };
    PreparedImage {
        orig_path: String::from(&*image_path),
        image,
    }
}

/**
 * Get a specific HashType hash
 */
pub fn get_perceptual_hash(
    path: &Path,
    precision: &Precision,
    hash_type: &HashType,
    cache: &Option<Cache>,
) -> u64 {
    match *hash_type {
        HashType::AHash => ahash::AHash::new(&path, &precision, &cache).get_hash(&cache),
        HashType::DHash => dhash::DHash::new(&path, &precision, &cache).get_hash(&cache),
        HashType::PHash => phash::PHash::new(&path, &precision, &cache).get_hash(&cache),
    }
}

/**
 * Get all perceptual hashes for an image
 */
pub fn get_perceptual_hashes(
    path: &Path,
    precision: &Precision,
    cache: &Option<Cache>,
) -> PerceptualHashes {
    let image_path = path.to_str().unwrap();
    let ahash = ahash::AHash::new(&path, &precision, &cache).get_hash(&cache);
    let dhash = dhash::DHash::new(&path, &precision, &cache).get_hash(&cache);
    let phash = phash::PHash::new(&path, &precision, &cache).get_hash(&cache);
    PerceptualHashes {
        orig_path: String::from(&*image_path),
        ahash: ahash,
        dhash: dhash,
        phash: phash,
    }
}

/**
 * Calculate the number of bits different between two hashes
 * Add to the PerceptualHashTrait
 */
pub fn calculate_hamming_distance(hash1: u64, hash2: u64) -> u64 {
    // The binary xor of the two hashes should give us a number representing
    // the differences between the two hashes. All that's left is to count
    // the number of 1's in the difference to determine the hamming distance
    (hash1 ^ hash2).count_ones() as u64
}
<|endoftext|>