<commit_before>#![macro_escape]
use vm::{VM, Value, Int, Float, Function, Userdata, StackFrame, Data};
use typecheck::{TcType, Typed, FunctionType, unit_type_tc, bool_type_tc, int_type_tc, float_type_tc};
use compiler::CallGlobal;
use std::any::{Any, AnyRefExt};
use std::boxed::BoxAny;

pub trait VMType {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType;
    fn make_type(&self, vm: &VM) -> TcType {
        self.vm_type(vm).clone()
    }
}
pub trait VMValue : VMType {
    fn push(self, stack: &mut StackFrame);
    fn from_value(value: Value) -> Option<Self>;
}
impl VMType for () {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &unit_type_tc
    }
}
impl VMValue for () {
    fn push(self, _: &mut StackFrame) {
    }
    fn from_value(_: Value) -> Option<()> {
        Some(())
    }
}

impl VMType for int {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &int_type_tc
    }
}
impl VMValue for int {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Int(self));
    }
    fn from_value(value: Value) -> Option<int> {
        match value {
            Int(i) => Some(i),
            _ => None
        }
    }
}
impl VMType for f64 {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &float_type_tc
    }
}
impl VMValue for f64 {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Float(self));
    }
    fn from_value(value: Value) -> Option<f64> {
        match value {
            Float(f) => Some(f),
            _ => None
        }
    }
}
impl VMType for bool {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &bool_type_tc
    }
}
impl VMValue for bool {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Int(if self { 1 } else { 0 }));
    }
    fn from_value(value: Value) -> Option<bool> {
        match value {
            Int(1) => Some(true),
            Int(0) => Some(false),
            _ => None
        }
    }
}
impl <T: 'static + BoxAny + Clone> VMType for Box<T> {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<T>()
    }
}
impl <T: 'static + BoxAny + Clone> VMValue for Box<T> {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Userdata(Data::new(self as Box<Any>)));
    }
    fn from_value(value: Value) -> Option<Box<T>> {
        match value {
            Userdata(v) => v.data.borrow().downcast_ref::<T>().map(|x| box x.clone()),
            _ => None
        }
    }
}
impl <T: 'static> VMType for *mut T {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<T>()
    }
}
impl <T: 'static> VMValue for *mut T {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Userdata(Data::new(box self as Box<Any>)));
    }
    fn from_value(value: Value) -> Option<*mut T> {
        match value {
            Userdata(v) => v.data.borrow().downcast_ref::<*mut T>().map(|x| *x),
            _ => None
        }
    }
}

fn vm_type<'a, T: VMValue>(vm: &'a VM) -> &'a TcType {
    let t: T = unsafe { ::std::mem::uninitialized() };
    let typ = t.vm_type(vm);
    unsafe { ::std::mem::forget(t) }
    typ
}

fn make_type<'a, T: VMValue>(vm: &'a VM) -> TcType {
    let t: T = unsafe { ::std::mem::uninitialized() };
    let typ = t.make_type(vm);
    unsafe { ::std::mem::forget(t) }
    typ
}

trait Get<'a> {
    fn get_function(vm: &'a mut VM, name: &str) -> Option<Self>;
}
macro_rules! make_get(
    ($($args:ident),* -> $result:ident) => (
impl <'a, $($args : VMValue),*, $result: VMValue> Get<'a> for Callable<'a, ($($args),*), $result> {
    fn get_function(vm: &'a mut VM, name: &str) -> Option<Callable<'a, ($($args),*), $result>> {
        let value = match vm.get_global(name) {
            Some((function_ref, global)) => {
                match global.type_of() {
                    &FunctionType(ref args, ref return_type) => {
                        let mut arg_iter = args.iter();
                        let ok = $({
                            arg_iter.next().unwrap() == vm_type::<$args>(vm)
                            })&&*;
                        if arg_iter.next().is_none() && ok && **return_type == *vm_type::<$result>(vm) {
                            Some(FunctionRef { value: function_ref })
                        }
                        else {
                            None
                        }
                    }
                    _ => None
                }
            }
            None => None
        };
        match value {
            Some(value) => Some(Callable { vm: vm, value: value }),
            None => None
        }
    }
}
))

make_get!(A, B -> R)

impl <'a, T: VMValue, R: VMValue> Get<'a> for Callable<'a, (T,), R> {
    fn get_function(vm: &'a mut VM, name: &str) -> Option<Callable<'a, (T,), R>> {
        let value = match vm.get_global(name) {
            Some((function_ref, global)) => {
                match global.type_of() {
                    &FunctionType(ref args, ref return_type) => {
                        if args.len() == 1 {
                            let ok = args[0] == *vm_type::<T>(vm);
                            if ok && **return_type == *vm_type::<R>(vm) {
                                Some(FunctionRef { value: function_ref })
                            }
                            else {
                                None
                            }
                        }
                        else {
                            None
                        }
                    }
                    _ => None
                }
            }
            None => None
        };
        match value {
            Some(value) => Some(Callable { vm: vm, value: value }),
            None => None
        }
    }
}

pub struct Callable<'a, Args, R> {
    vm: &'a mut VM,
    value: FunctionRef<Args, R>
}
struct FunctionRef<Args, R> {
    value: uint
}

impl <Args, R> VMType for FunctionRef<Args, R> {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<|Args|:'static -> R>()
    }
}

impl <Args, R> VMValue for FunctionRef<Args, R> {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Function(self.value));
    }
    fn from_value(value: Value) -> Option<FunctionRef<Args, R>> {
        match value {
            Function(i) => Some(FunctionRef { value: i }),//TODO not type safe
            _ => None
        }
    }
}

impl <'a, A: VMValue, R: VMValue> Callable<'a, (A,), R> {
    pub fn call(&mut self, a: A) -> R {
        let mut vec = Vec::new();
        {
            let mut stack = StackFrame::new(&mut vec, 0, None);
            self.value.push(&mut stack);
            a.push(&mut stack);
            self.vm.execute(stack, &[CallGlobal(1)]);
        }
        vec.pop().and_then(|value| VMValue::from_value(value))
            .expect("Wrong type")
    }
}
impl <'a, A: VMValue, B: VMValue, R: VMValue> Callable<'a, (A, B), R> {
    pub fn call2(&mut self, a: A, b: B) -> R {
        let mut vec = Vec::new();
        {
            let mut stack = StackFrame::new(&mut vec, 0, None);
            self.value.push(&mut stack);
            a.push(&mut stack);
            b.push(&mut stack);
            self.vm.execute(stack, &[CallGlobal(2)]);
        }
        vec.pop().and_then(|value| VMValue::from_value(value))
            .expect("Wrong type")
    }
}

pub fn get_function<'a, T: Get<'a>>(vm: &'a mut VM, name: &str) -> Option<T> {
    Get::get_function(vm, name)
}

trait VMFunction {
    fn unpack_and_call(mut stack: StackFrame, f: Self);
}

macro_rules! make_vm_function(
    ($($args:ident),*) => (
impl <$($args: VMValue,)* R: VMValue> VMType for fn ($($args),*) -> R {
    #[allow(non_snake_case)]
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<fn ($($args),*) -> R>()
    }
    #[allow(non_snake_case)]
    fn make_type(&self, vm: &VM) -> TcType {
        FunctionType(vec![$(make_type::<$args>(vm)),*], box make_type::<R>(vm))
    }
}

impl <$($args : VMValue,)* R: VMValue> VMFunction for fn ($($args),*) -> R {
    #[allow(non_snake_case)]
    fn unpack_and_call(mut stack: StackFrame, f: fn ($($args),*) -> R) {
        let mut i = 0u;
        $(let $args = {
            let x = VMValue::from_value(stack[i].clone()).unwrap();
            i += 1;
            x
        });*;
        drop(i);//Avoid unused warnings
        let r = f($($args),*);
        r.push(&mut stack);
    }
}
    )
)
pub fn unpack_and_call<F: VMFunction>(stack: StackFrame, f: F) {
    VMFunction::unpack_and_call(stack, f)
}

make_vm_function!()
make_vm_function!(A)
make_vm_function!(A, B)
make_vm_function!(A, B, C)
make_vm_function!(A, B, C, D)
make_vm_function!(A, B, C, D, E)
make_vm_function!(A, B, C, D, E, F)
make_vm_function!(A, B, C, D, E, F, G)

#[macro_export]
macro_rules! vm_function(
    ($func: expr) => ({
        fn wrapper(_: &VM, stack: StackFrame) {
            unpack_and_call(stack, $func)
        }
        wrapper
    })
)

#[macro_export]
macro_rules! define_function(
    ($vm: expr, $name: expr, $func: expr) => ({
        let vm = $vm;
        let (args, ret) = match $func.make_type(vm) {
            FunctionType(ref args, ref return_type) => (args.clone(), (**return_type).clone()),
            _ => fail!()
        };
        vm.extern_function($name, args, ret, vm_function!($func))
    })
)

#[cfg(test)]
mod tests {
    use super::{Get, Callable, VMType, VMFunction};

    use typecheck::FunctionType;
    use vm::{VM, load_script, StackFrame};
    use std::io::BufReader;

    #[test]
    fn call_function() {
        let s =
r"
fn add10(x: int) -> int {
    x + 10
}
fn mul(x: float, y: float) -> float {
    x * y
}
";
        let mut vm = VM::new();
        let mut buffer = BufReader::new(s.as_bytes());
        load_script(&mut vm, &mut buffer)
            .unwrap_or_else(|err| fail!("{}", err));
        {
            let mut f: Callable<(int,), int> = Get::get_function(&mut vm, "add10")
                .expect("No function");
            let result = f.call(2);
            assert_eq!(result, 12);
        }
        let mut f: Callable<(f64, f64), f64> = Get::get_function(&mut vm, "mul")
            .expect("No function");
        let result = f.call2(4., 5.);
        assert_eq!(result, 20.);
    }
    #[test]
    fn pass_userdata() {
        let s =
r"
fn id(x: Test) -> Test {
    x
}
";
        let mut vm = VM::new();
        fn test(test: *mut Test) -> bool {
            let test = unsafe { &mut *test };
            let x = test.x == 123;
            test.x *= 2;
            x
        }
        struct Test {
            x: int
        }
        vm.register_type::<Test>("Test")
            .unwrap_or_else(|_| fail!("Could not add type"));
        define_function!(&mut vm, "test", test);
        let mut buffer = BufReader::new(s.as_bytes());
        load_script(&mut vm, &mut buffer)
            .unwrap_or_else(|err| fail!("{}", err));

        let mut test = Test { x: 123 };
        {
            let mut f: Callable<(*mut Test,), *mut Test> = Get::get_function(&mut vm, "id")
                .expect("No function id");
            let result = f.call(&mut test);
            assert!(result == &mut test as *mut Test);
        }
        let mut f: Callable<(*mut Test,), bool> = Get::get_function(&mut vm, "test")
            .expect("No function test");
        let result = f.call(&mut test);
        assert!(result);
        assert_eq!(test.x, 123 * 2);
    }
}
<commit_msg>Simplified the make_get function<commit_after>#![macro_escape]
use vm::{VM, Value, Int, Float, Function, Userdata, StackFrame, Data};
use typecheck::{TcType, Typed, FunctionType, unit_type_tc, bool_type_tc, int_type_tc, float_type_tc};
use compiler::CallGlobal;
use std::any::{Any, AnyRefExt};
use std::boxed::BoxAny;

pub trait VMType {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType;
    fn make_type(&self, vm: &VM) -> TcType {
        self.vm_type(vm).clone()
    }
}
pub trait VMValue : VMType {
    fn push(self, stack: &mut StackFrame);
    fn from_value(value: Value) -> Option<Self>;
}
impl VMType for () {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &unit_type_tc
    }
}
impl VMValue for () {
    fn push(self, _: &mut StackFrame) {
    }
    fn from_value(_: Value) -> Option<()> {
        Some(())
    }
}

impl VMType for int {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &int_type_tc
    }
}
impl VMValue for int {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Int(self));
    }
    fn from_value(value: Value) -> Option<int> {
        match value {
            Int(i) => Some(i),
            _ => None
        }
    }
}
impl VMType for f64 {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &float_type_tc
    }
}
impl VMValue for f64 {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Float(self));
    }
    fn from_value(value: Value) -> Option<f64> {
        match value {
            Float(f) => Some(f),
            _ => None
        }
    }
}
impl VMType for bool {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &bool_type_tc
    }
}
impl VMValue for bool {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Int(if self { 1 } else { 0 }));
    }
    fn from_value(value: Value) -> Option<bool> {
        match value {
            Int(1) => Some(true),
            Int(0) => Some(false),
            _ => None
        }
    }
}
impl <T: 'static + BoxAny + Clone> VMType for Box<T> {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<T>()
    }
}
impl <T: 'static + BoxAny + Clone> VMValue for Box<T> {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Userdata(Data::new(self as Box<Any>)));
    }
    fn from_value(value: Value) -> Option<Box<T>> {
        match value {
            Userdata(v) => v.data.borrow().downcast_ref::<T>().map(|x| box x.clone()),
            _ => None
        }
    }
}
impl <T: 'static> VMType for *mut T {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<T>()
    }
}
impl <T: 'static> VMValue for *mut T {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Userdata(Data::new(box self as Box<Any>)));
    }
    fn from_value(value: Value) -> Option<*mut T> {
        match value {
            Userdata(v) => v.data.borrow().downcast_ref::<*mut T>().map(|x| *x),
            _ => None
        }
    }
}

fn vm_type<'a, T: VMValue>(vm: &'a VM) -> &'a TcType {
    let t: T = unsafe { ::std::mem::uninitialized() };
    let typ = t.vm_type(vm);
    unsafe { ::std::mem::forget(t) }
    typ
}

fn make_type<'a, T: VMValue>(vm: &'a VM) -> TcType {
    let t: T = unsafe { ::std::mem::uninitialized() };
    let typ = t.make_type(vm);
    unsafe { ::std::mem::forget(t) }
    typ
}

trait Get<'a> {
    fn get_function(vm: &'a mut VM, name: &str) -> Option<Self>;
}
macro_rules! make_get(
    ($($args:ident),*) => (
impl <'a, $($args : VMValue,)* R: VMValue> Get<'a> for Callable<'a, ($($args,)*), R> {
    fn get_function(vm: &'a mut VM, name: &str) -> Option<Callable<'a, ($($args,)*), R>> {
        let value = match vm.get_global(name) {
            Some((function_ref, global)) => {
                match global.type_of() {
                    &FunctionType(ref args, ref return_type) => {
                        let mut arg_iter = args.iter();
                        let ok = $({
                            arg_iter.next().unwrap() == vm_type::<$args>(vm)
                            } &&)* true;
                        if arg_iter.next().is_none() && ok && **return_type == *vm_type::<R>(vm) {
                            Some(FunctionRef { value: function_ref })
                        }
                        else {
                            None
                        }
                    }
                    _ => None
                }
            }
            None => None
        };
        match value {
            Some(value) => Some(Callable { vm: vm, value: value }),
            None => None
        }
    }
}
))

make_get!()
make_get!(A)
make_get!(A, B)
make_get!(A, B, C)
make_get!(A, B, C, D)
make_get!(A, B, C, D, E)
make_get!(A, B, C, D, E, F, G)

pub struct Callable<'a, Args, R> {
    vm: &'a mut VM,
    value: FunctionRef<Args, R>
}
struct FunctionRef<Args, R> {
    value: uint
}

impl <Args, R> VMType for FunctionRef<Args, R> {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<|Args|:'static -> R>()
    }
}

impl <Args, R> VMValue for FunctionRef<Args, R> {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Function(self.value));
    }
    fn from_value(value: Value) -> Option<FunctionRef<Args, R>> {
        match value {
            Function(i) => Some(FunctionRef { value: i }),//TODO not type safe
            _ => None
        }
    }
}

impl <'a, A: VMValue, R: VMValue> Callable<'a, (A,), R> {
    pub fn call(&mut self, a: A) -> R {
        let mut vec = Vec::new();
        {
            let mut stack = StackFrame::new(&mut vec, 0, None);
            self.value.push(&mut stack);
            a.push(&mut stack);
            self.vm.execute(stack, &[CallGlobal(1)]);
        }
        vec.pop().and_then(|value| VMValue::from_value(value))
            .expect("Wrong type")
    }
}
impl <'a, A: VMValue, B: VMValue, R: VMValue> Callable<'a, (A, B), R> {
    pub fn call2(&mut self, a: A, b: B) -> R {
        let mut vec = Vec::new();
        {
            let mut stack = StackFrame::new(&mut vec, 0, None);
            self.value.push(&mut stack);
            a.push(&mut stack);
            b.push(&mut stack);
            self.vm.execute(stack, &[CallGlobal(2)]);
        }
        vec.pop().and_then(|value| VMValue::from_value(value))
            .expect("Wrong type")
    }
}

pub fn get_function<'a, T: Get<'a>>(vm: &'a mut VM, name: &str) -> Option<T> {
    Get::get_function(vm, name)
}

trait VMFunction {
    fn unpack_and_call(mut stack: StackFrame, f: Self);
}

macro_rules! make_vm_function(
    ($($args:ident),*) => (
impl <$($args: VMValue,)* R: VMValue> VMType for fn ($($args),*) -> R {
    #[allow(non_snake_case)]
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<fn ($($args),*) -> R>()
    }
    #[allow(non_snake_case)]
    fn make_type(&self, vm: &VM) -> TcType {
        FunctionType(vec![$(make_type::<$args>(vm)),*], box make_type::<R>(vm))
    }
}

impl <$($args : VMValue,)* R: VMValue> VMFunction for fn ($($args),*) -> R {
    #[allow(non_snake_case, unused_mut, dead_assignment, unused_variable)]
    fn unpack_and_call(mut stack: StackFrame, f: fn ($($args),*) -> R) {
        let mut i = 0u;
        $(let $args = {
            let x = VMValue::from_value(stack[i].clone()).unwrap();
            i += 1;
            x
        });*;
        let r = f($($args),*);
        r.push(&mut stack);
    }
}
    )
)
pub fn unpack_and_call<F: VMFunction>(stack: StackFrame, f: F) {
    VMFunction::unpack_and_call(stack, f)
}

make_vm_function!()
make_vm_function!(A)
make_vm_function!(A, B)
make_vm_function!(A, B, C)
make_vm_function!(A, B, C, D)
make_vm_function!(A, B, C, D, E)
make_vm_function!(A, B, C, D, E, F)
make_vm_function!(A, B, C, D, E, F, G)

#[macro_export]
macro_rules! vm_function(
    ($func: expr) => ({
        fn wrapper(_: &VM, stack: StackFrame) {
            unpack_and_call(stack, $func)
        }
        wrapper
    })
)

#[macro_export]
macro_rules! define_function(
    ($vm: expr, $name: expr, $func: expr) => ({
        let vm = $vm;
        let (args, ret) = match $func.make_type(vm) {
            FunctionType(ref args, ref return_type) => (args.clone(), (**return_type).clone()),
            _ => fail!()
        };
        vm.extern_function($name, args, ret, vm_function!($func))
    })
)

#[cfg(test)]
mod tests {
    use super::{Get, Callable, VMType, unpack_and_call};

    use typecheck::FunctionType;
    use vm::{VM, load_script, StackFrame};
    use std::io::BufReader;

    #[test]
    fn call_function() {
        let s =
r"
fn add10(x: int) -> int {
    x + 10
}
fn mul(x: float, y: float) -> float {
    x * y
}
";
        let mut vm = VM::new();
        let mut buffer = BufReader::new(s.as_bytes());
        load_script(&mut vm, &mut buffer)
            .unwrap_or_else(|err| fail!("{}", err));
        {
            let mut f: Callable<(int,), int> = Get::get_function(&mut vm, "add10")
                .expect("No function");
            let result = f.call(2);
            assert_eq!(result, 12);
        }
        let mut f: Callable<(f64, f64), f64> = Get::get_function(&mut vm, "mul")
            .expect("No function");
        let result = f.call2(4., 5.);
        assert_eq!(result, 20.);
    }
    #[test]
    fn pass_userdata() {
        let s =
r"
fn id(x: Test) -> Test {
    x
}
";
        let mut vm = VM::new();
        fn test(test: *mut Test) -> bool {
            let test = unsafe { &mut *test };
            let x = test.x == 123;
            test.x *= 2;
            x
        }
        struct Test {
            x: int
        }
        vm.register_type::<Test>("Test")
            .unwrap_or_else(|_| fail!("Could not add type"));
        define_function!(&mut vm, "test", test);
        let mut buffer = BufReader::new(s.as_bytes());
        load_script(&mut vm, &mut buffer)
            .unwrap_or_else(|err| fail!("{}", err));

        let mut test = Test { x: 123 };
        {
            let mut f: Callable<(*mut Test,), *mut Test> = Get::get_function(&mut vm, "id")
                .expect("No function id");
            let result = f.call(&mut test);
            assert!(result == &mut test as *mut Test);
        }
        let mut f: Callable<(*mut Test,), bool> = Get::get_function(&mut vm, "test")
            .expect("No function test");
        let result = f.call(&mut test);
        assert!(result);
        assert_eq!(test.x, 123 * 2);
    }
}
<|endoftext|>


<commit_before>use core::intrinsics;

// NOTE This function and the ones below are implemented using assembly because they using a custom
// calling convention which can't be implemented using a normal Rust function.
// NOTE The only difference between the iOS and non-iOS versions of those functions is that the iOS
// versions use 3 leading underscores in the names of called functions instead of 2.
#[cfg(not(any(target_os = "ios", target_env = "msvc")))]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_uidivmod() {
    asm!("push {lr}
          sub sp, sp, #4
          mov r2, sp
          bl __udivmodsi4
          ldr r1, [sp]
          add sp, sp, #4
          pop {pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

#[cfg(target_os = "ios")]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_uidivmod() {
    asm!("push {lr}
          sub sp, sp, #4
          mov r2, sp
          bl ___udivmodsi4
          ldr r1, [sp]
          add sp, sp, #4
          pop {pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

#[cfg(not(target_os = "ios"))]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_uldivmod() {
    asm!("push {r4, lr}
          sub sp, sp, #16
          add r4, sp, #8
          str r4, [sp]
          bl __udivmoddi4
          ldr r2, [sp, #8]
          ldr r3, [sp, #12]
          add sp, sp, #16
          pop {r4, pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

#[cfg(target_os = "ios")]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_uldivmod() {
    asm!("push {r4, lr}
          sub sp, sp, #16
          add r4, sp, #8
          str r4, [sp]
          bl ___udivmoddi4
          ldr r2, [sp, #8]
          ldr r3, [sp, #12]
          add sp, sp, #16
          pop {r4, pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

#[cfg(not(target_os = "ios"))]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_idivmod() {
    asm!("push {r0, r1, r4, lr}
          bl __aeabi_idiv
          pop {r1, r2}
          muls r2, r2, r0
          subs r1, r1, r2
          pop {r4, pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

#[cfg(target_os = "ios")]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_idivmod() {
    asm!("push {r0, r1, r4, lr}
          bl ___aeabi_idiv
          pop {r1, r2}
          muls r2, r2, r0
          subs r1, r1, r2
          pop {r4, pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

#[cfg(not(target_os = "ios"))]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_ldivmod() {
    asm!("push {r4, lr}
          sub sp, sp, #16
          add r4, sp, #8
          str r4, [sp]
          bl __divmoddi4
          ldr r2, [sp, #8]
          ldr r3, [sp, #12]
          add sp, sp, #16
          pop {r4, pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

#[cfg(target_os = "ios")]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_ldivmod() {
    asm!("push {r4, lr}
          sub sp, sp, #16
          add r4, sp, #8
          str r4, [sp]
          bl ___divmoddi4
          ldr r2, [sp, #8]
          ldr r3, [sp, #12]
          add sp, sp, #16
          pop {r4, pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

// FIXME: The `*4` and `*8` variants should be defined as aliases.

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) {
    ::mem::memcpy(dest, src, n);
}

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, mut n: usize) {
    use core::ptr;

    let mut dest = dest as *mut u32;
    let mut src = src as *mut u32;

    while n >= 4 {
        ptr::write(dest, ptr::read(src));
        dest = dest.offset(1);
        src = src.offset(1);
        n -= 4;
    }

    __aeabi_memcpy(dest as *mut u8, src as *const u8, n);
}

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memcpy8(dest: *mut u8, src: *const u8, n: usize) {
    __aeabi_memcpy4(dest, src, n);
}

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memmove(dest: *mut u8, src: *const u8, n: usize) {
    ::mem::memmove(dest, src, n);
}

#[cfg(not(any(target_os = "ios", target_env = "msvc")))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memmove4(dest: *mut u8, src: *const u8, n: usize) {
    __aeabi_memmove(dest, src, n);
}

#[cfg(not(any(target_os = "ios", target_env = "msvc")))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memmove8(dest: *mut u8, src: *const u8, n: usize) {
    __aeabi_memmove(dest, src, n);
}

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) {
    // Note the different argument order
    ::mem::memset(dest, c, n);
}

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memset4(dest: *mut u8, mut n: usize, c: i32) {
    use core::ptr;

    let mut dest = dest as *mut u32;

    let byte = (c as u32) & 0xff;
    let c = (byte << 24) | (byte << 16) | (byte << 8) | byte;

    while n >= 4 {
        ptr::write(dest, c);
        dest = dest.offset(1);
        n -= 4;
    }

    __aeabi_memset(dest as *mut u8, n, byte as i32);
}

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memset8(dest: *mut u8, n: usize, c: i32) {
    __aeabi_memset4(dest, n, c);
}

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memclr(dest: *mut u8, n: usize) {
    __aeabi_memset(dest, n, 0);
}

#[cfg(not(any(target_os = "ios", target_env = "msvc")))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memclr4(dest: *mut u8, n: usize) {
    __aeabi_memset4(dest, n, 0);
}

#[cfg(not(any(target_os = "ios", target_env = "msvc")))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memclr8(dest: *mut u8, n: usize) {
    __aeabi_memset4(dest, n, 0);
}
<commit_msg>avoid ptr::write which might panic in debug mode<commit_after>use core::intrinsics;

// NOTE This function and the ones below are implemented using assembly because they using a custom
// calling convention which can't be implemented using a normal Rust function.
// NOTE The only difference between the iOS and non-iOS versions of those functions is that the iOS
// versions use 3 leading underscores in the names of called functions instead of 2.
#[cfg(not(any(target_os = "ios", target_env = "msvc")))]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_uidivmod() {
    asm!("push {lr}
          sub sp, sp, #4
          mov r2, sp
          bl __udivmodsi4
          ldr r1, [sp]
          add sp, sp, #4
          pop {pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

#[cfg(target_os = "ios")]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_uidivmod() {
    asm!("push {lr}
          sub sp, sp, #4
          mov r2, sp
          bl ___udivmodsi4
          ldr r1, [sp]
          add sp, sp, #4
          pop {pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

#[cfg(not(target_os = "ios"))]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_uldivmod() {
    asm!("push {r4, lr}
          sub sp, sp, #16
          add r4, sp, #8
          str r4, [sp]
          bl __udivmoddi4
          ldr r2, [sp, #8]
          ldr r3, [sp, #12]
          add sp, sp, #16
          pop {r4, pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

#[cfg(target_os = "ios")]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_uldivmod() {
    asm!("push {r4, lr}
          sub sp, sp, #16
          add r4, sp, #8
          str r4, [sp]
          bl ___udivmoddi4
          ldr r2, [sp, #8]
          ldr r3, [sp, #12]
          add sp, sp, #16
          pop {r4, pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

#[cfg(not(target_os = "ios"))]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_idivmod() {
    asm!("push {r0, r1, r4, lr}
          bl __aeabi_idiv
          pop {r1, r2}
          muls r2, r2, r0
          subs r1, r1, r2
          pop {r4, pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

#[cfg(target_os = "ios")]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_idivmod() {
    asm!("push {r0, r1, r4, lr}
          bl ___aeabi_idiv
          pop {r1, r2}
          muls r2, r2, r0
          subs r1, r1, r2
          pop {r4, pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

#[cfg(not(target_os = "ios"))]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_ldivmod() {
    asm!("push {r4, lr}
          sub sp, sp, #16
          add r4, sp, #8
          str r4, [sp]
          bl __divmoddi4
          ldr r2, [sp, #8]
          ldr r3, [sp, #12]
          add sp, sp, #16
          pop {r4, pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

#[cfg(target_os = "ios")]
#[naked]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
pub unsafe fn __aeabi_ldivmod() {
    asm!("push {r4, lr}
          sub sp, sp, #16
          add r4, sp, #8
          str r4, [sp]
          bl ___divmoddi4
          ldr r2, [sp, #8]
          ldr r3, [sp, #12]
          add sp, sp, #16
          pop {r4, pc}" ::: "memory" : "volatile");
    intrinsics::unreachable();
}

// FIXME: The `*4` and `*8` variants should be defined as aliases.

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) {
    ::mem::memcpy(dest, src, n);
}

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, mut n: usize) {
    use core::ptr;

    // We are guaranteed 4-alignment, so accessing at u32 is okay.
    let mut dest = dest as *mut u32;
    let mut src = src as *mut u32;

    while n >= 4 {
        *dest = *src;
        dest = dest.offset(1);
        src = src.offset(1);
        n -= 4;
    }

    __aeabi_memcpy(dest as *mut u8, src as *const u8, n);
}

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memcpy8(dest: *mut u8, src: *const u8, n: usize) {
    __aeabi_memcpy4(dest, src, n);
}

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memmove(dest: *mut u8, src: *const u8, n: usize) {
    ::mem::memmove(dest, src, n);
}

#[cfg(not(any(target_os = "ios", target_env = "msvc")))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memmove4(dest: *mut u8, src: *const u8, n: usize) {
    __aeabi_memmove(dest, src, n);
}

#[cfg(not(any(target_os = "ios", target_env = "msvc")))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memmove8(dest: *mut u8, src: *const u8, n: usize) {
    __aeabi_memmove(dest, src, n);
}

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) {
    // Note the different argument order
    ::mem::memset(dest, c, n);
}

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memset4(dest: *mut u8, mut n: usize, c: i32) {
    use core::ptr;

    let mut dest = dest as *mut u32;

    let byte = (c as u32) & 0xff;
    let c = (byte << 24) | (byte << 16) | (byte << 8) | byte;

    while n >= 4 {
        *dest = c;
        dest = dest.offset(1);
        n -= 4;
    }

    __aeabi_memset(dest as *mut u8, n, byte as i32);
}

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memset8(dest: *mut u8, n: usize, c: i32) {
    __aeabi_memset4(dest, n, c);
}

#[cfg(not(target_os = "ios"))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memclr(dest: *mut u8, n: usize) {
    __aeabi_memset(dest, n, 0);
}

#[cfg(not(any(target_os = "ios", target_env = "msvc")))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memclr4(dest: *mut u8, n: usize) {
    __aeabi_memset4(dest, n, 0);
}

#[cfg(not(any(target_os = "ios", target_env = "msvc")))]
#[cfg_attr(not(feature = "mangled-names"), no_mangle)]
#[cfg_attr(thumb, linkage = "weak")]
pub unsafe extern "aapcs" fn __aeabi_memclr8(dest: *mut u8, n: usize) {
    __aeabi_memset4(dest, n, 0);
}
<|endoftext|>


<commit_before>use crate::config::{Config, Solver};
use crate::heuristic;
use oppai_field::field::{self, Field};
use oppai_field::player::Player;
use oppai_field::zobrist::Zobrist;
use oppai_minimax::minimax::Minimax;
use oppai_patterns::patterns::Patterns;
use oppai_uct::uct::UctRoot;
use rand::seq::SliceRandom;
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;
use std::{cmp, sync::Arc, time::Duration};

const MIN_COMPLEXITY: u32 = 0;

const MAX_COMPLEXITY: u32 = 100;

const MIN_UCT_ITERATIONS: usize = 0;

const MAX_UCT_ITERATIONS: usize = 500_000;

const MIN_MINIMAX_DEPTH: u32 = 0;

const MAX_MINIMAX_DEPTH: u32 = 12;

pub struct Bot {
  rng: XorShiftRng,
  patterns: Arc<Patterns>,
  field: Field,
  uct: UctRoot,
  minimax: Minimax,
  config: Config,
}

impl Bot {
  pub fn new(width: u32, height: u32, seed: u64, patterns: Arc<Patterns>, config: Config) -> Bot {
    info!(
      "Initialization with width {0}, height {1}, seed {2}.",
      width, height, seed
    );
    let length = field::length(width, height);
    let seed_array = [
      2,
      (seed & 0xff) as u8,
      3,
      ((seed >> 8) & 0xff) as u8,
      5,
      ((seed >> 16) & 0xff) as u8,
      7,
      ((seed >> 24) & 0xff) as u8,
      11,
      ((seed >> 32) & 0xff) as u8,
      13,
      ((seed >> 40) & 0xff) as u8,
      17,
      ((seed >> 48) & 0xff) as u8,
      19,
      ((seed >> 56) & 0xff) as u8,
    ];
    let mut rng = XorShiftRng::from_seed(seed_array);
    let zobrist = Arc::new(Zobrist::new(length * 2, &mut rng));
    let field_zobrist = Arc::clone(&zobrist);
    Bot {
      rng,
      patterns,
      field: Field::new(width, height, field_zobrist),
      uct: UctRoot::new(config.uct.clone(), length),
      minimax: Minimax::new(config.minimax.clone()),
      config,
    }
  }

  pub fn initial_move(&self) -> Option<(u32, u32)> {
    match self.field.moves_count() {
      0 => Some((self.field.width() / 2, self.field.height() / 2)),
      1 => {
        let width = self.field.width();
        let height = self.field.height();
        let pos = self.field.points_seq()[0];
        let x = self.field.to_x(pos);
        let y = self.field.to_y(pos);
        if x == 0 || x == width - 1 || y == 0 || y == height - 1 {
          Some((width / 2, height / 2))
        } else if cmp::min(x, width - x - 1) < cmp::min(y, height - y - 1) {
          if x < width - x - 1 {
            Some((x + 1, y))
          } else {
            Some((x - 1, y))
          }
        } else if cmp::min(x, width - x - 1) > cmp::min(y, height - y - 1) {
          if y < height - y - 1 {
            Some((x, y + 1))
          } else {
            Some((x, y - 1))
          }
        } else {
          let dx = x as i32 - (width / 2) as i32;
          let dy = y as i32 - (height / 2) as i32;
          if dx.abs() > dy.abs() {
            if dx < 0 {
              Some((x + 1, y))
            } else {
              Some((x - 1, y))
            }
          } else if dy < 0 {
            Some((x, y + 1))
          } else {
            Some((x, y - 1))
          }
        }
      }
      _ => None,
    }
  }

  fn is_field_occupied(field: &Field) -> bool {
    for pos in field.min_pos()..=field.max_pos() {
      if field.cell(pos).is_putting_allowed() {
        return false;
      }
    }
    true
  }

  pub fn best_move(&mut self, player: Player) -> Option<(u32, u32)> {
    self.best_move_with_complexity(player, (MAX_COMPLEXITY - MIN_COMPLEXITY) / 2 + MIN_COMPLEXITY)
  }

  pub fn best_move_with_time(&mut self, player: Player, time: u32) -> Option<(u32, u32)> {
    if self.field.width() < 3 || self.field.height() < 3 || Bot::is_field_occupied(&self.field) {
      return None;
    }
    if let Some(m) = self.initial_move() {
      return Some(m);
    }
    if let Some(&pos) = self.patterns.find(&self.field, player, false).choose(&mut self.rng) {
      return Some((self.field.to_x(pos), self.field.to_y(pos)));
    }
    match self.config.bot.solver {
      Solver::Uct => self
        .uct
        .best_move_with_time(
          &self.field,
          player,
          &mut self.rng,
          Duration::from_millis(u64::from(time - self.config.bot.time_gap)),
        )
        .or_else(|| heuristic::heuristic(&self.field, player))
        .map(|pos| (self.field.to_x(pos.get()), self.field.to_y(pos.get()))),
      Solver::Minimax => self
        .minimax
        .minimax_with_time(
          &mut self.field,
          player,
          Duration::from_millis(u64::from(time - self.config.bot.time_gap)),
        )
        .or_else(|| heuristic::heuristic(&self.field, player))
        .map(|pos| (self.field.to_x(pos.get()), self.field.to_y(pos.get()))),
      Solver::Heuristic => {
        heuristic::heuristic(&self.field, player).map(|pos| (self.field.to_x(pos.get()), self.field.to_y(pos.get())))
      }
    }
  }

  pub fn best_move_with_full_time(
    &mut self,
    player: Player,
    remaining_time: u32,
    time_per_move: u32,
  ) -> Option<(u32, u32)> {
    self.best_move_with_time(player, time_per_move + remaining_time / 25)
  }

  pub fn best_move_with_complexity(&mut self, player: Player, complexity: u32) -> Option<(u32, u32)> {
    if self.field.width() < 3 || self.field.height() < 3 || Bot::is_field_occupied(&self.field) {
      return None;
    }
    if let Some(m) = self.initial_move() {
      return Some(m);
    }
    if let Some(&pos) = self.patterns.find(&self.field, player, false).choose(&mut self.rng) {
      return Some((self.field.to_x(pos), self.field.to_y(pos)));
    }
    match self.config.bot.solver {
      Solver::Uct => {
        let iterations_count = (complexity - MIN_COMPLEXITY) as usize * (MAX_UCT_ITERATIONS - MIN_UCT_ITERATIONS)
          / (MAX_COMPLEXITY - MIN_COMPLEXITY) as usize
          + MIN_UCT_ITERATIONS;
        self
          .uct
          .best_move_with_iterations_count(&self.field, player, &mut self.rng, iterations_count)
          .or_else(|| heuristic::heuristic(&self.field, player))
          .map(|pos| (self.field.to_x(pos.get()), self.field.to_y(pos.get())))
      }
      Solver::Minimax => {
        let depth = (complexity - MIN_COMPLEXITY) * (MAX_MINIMAX_DEPTH - MIN_MINIMAX_DEPTH)
          / (MAX_COMPLEXITY - MIN_COMPLEXITY)
          + MIN_MINIMAX_DEPTH;
        self
          .minimax
          .minimax(&mut self.field, player, depth)
          .or_else(|| heuristic::heuristic(&self.field, player))
          .map(|pos| (self.field.to_x(pos.get()), self.field.to_y(pos.get())))
      }
      Solver::Heuristic => {
        heuristic::heuristic(&self.field, player).map(|pos| (self.field.to_x(pos.get()), self.field.to_y(pos.get())))
      }
    }
  }

  pub fn put_point(&mut self, x: u32, y: u32, player: Player) -> bool {
    let pos = self.field.to_pos(x, y);
    self.field.put_point(pos, player)
  }

  pub fn undo(&mut self) -> bool {
    self.field.undo()
  }
}
<commit_msg>Use seed_from_u64.<commit_after>use crate::config::{Config, Solver};
use crate::heuristic;
use oppai_field::field::{self, Field};
use oppai_field::player::Player;
use oppai_field::zobrist::Zobrist;
use oppai_minimax::minimax::Minimax;
use oppai_patterns::patterns::Patterns;
use oppai_uct::uct::UctRoot;
use rand::seq::SliceRandom;
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;
use std::{cmp, sync::Arc, time::Duration};

const MIN_COMPLEXITY: u32 = 0;

const MAX_COMPLEXITY: u32 = 100;

const MIN_UCT_ITERATIONS: usize = 0;

const MAX_UCT_ITERATIONS: usize = 500_000;

const MIN_MINIMAX_DEPTH: u32 = 0;

const MAX_MINIMAX_DEPTH: u32 = 12;

pub struct Bot {
  rng: XorShiftRng,
  patterns: Arc<Patterns>,
  field: Field,
  uct: UctRoot,
  minimax: Minimax,
  config: Config,
}

impl Bot {
  pub fn new(width: u32, height: u32, seed: u64, patterns: Arc<Patterns>, config: Config) -> Bot {
    info!(
      "Initialization with width {0}, height {1}, seed {2}.",
      width, height, seed
    );
    let length = field::length(width, height);
    let mut rng = XorShiftRng::seed_from_u64(seed);
    let zobrist = Arc::new(Zobrist::new(length * 2, &mut rng));
    let field_zobrist = Arc::clone(&zobrist);
    Bot {
      rng,
      patterns,
      field: Field::new(width, height, field_zobrist),
      uct: UctRoot::new(config.uct.clone(), length),
      minimax: Minimax::new(config.minimax.clone()),
      config,
    }
  }

  pub fn initial_move(&self) -> Option<(u32, u32)> {
    match self.field.moves_count() {
      0 => Some((self.field.width() / 2, self.field.height() / 2)),
      1 => {
        let width = self.field.width();
        let height = self.field.height();
        let pos = self.field.points_seq()[0];
        let x = self.field.to_x(pos);
        let y = self.field.to_y(pos);
        if x == 0 || x == width - 1 || y == 0 || y == height - 1 {
          Some((width / 2, height / 2))
        } else if cmp::min(x, width - x - 1) < cmp::min(y, height - y - 1) {
          if x < width - x - 1 {
            Some((x + 1, y))
          } else {
            Some((x - 1, y))
          }
        } else if cmp::min(x, width - x - 1) > cmp::min(y, height - y - 1) {
          if y < height - y - 1 {
            Some((x, y + 1))
          } else {
            Some((x, y - 1))
          }
        } else {
          let dx = x as i32 - (width / 2) as i32;
          let dy = y as i32 - (height / 2) as i32;
          if dx.abs() > dy.abs() {
            if dx < 0 {
              Some((x + 1, y))
            } else {
              Some((x - 1, y))
            }
          } else if dy < 0 {
            Some((x, y + 1))
          } else {
            Some((x, y - 1))
          }
        }
      }
      _ => None,
    }
  }

  fn is_field_occupied(field: &Field) -> bool {
    for pos in field.min_pos()..=field.max_pos() {
      if field.cell(pos).is_putting_allowed() {
        return false;
      }
    }
    true
  }

  pub fn best_move(&mut self, player: Player) -> Option<(u32, u32)> {
    self.best_move_with_complexity(player, (MAX_COMPLEXITY - MIN_COMPLEXITY) / 2 + MIN_COMPLEXITY)
  }

  pub fn best_move_with_time(&mut self, player: Player, time: u32) -> Option<(u32, u32)> {
    if self.field.width() < 3 || self.field.height() < 3 || Bot::is_field_occupied(&self.field) {
      return None;
    }
    if let Some(m) = self.initial_move() {
      return Some(m);
    }
    if let Some(&pos) = self.patterns.find(&self.field, player, false).choose(&mut self.rng) {
      return Some((self.field.to_x(pos), self.field.to_y(pos)));
    }
    match self.config.bot.solver {
      Solver::Uct => self
        .uct
        .best_move_with_time(
          &self.field,
          player,
          &mut self.rng,
          Duration::from_millis(u64::from(time - self.config.bot.time_gap)),
        )
        .or_else(|| heuristic::heuristic(&self.field, player))
        .map(|pos| (self.field.to_x(pos.get()), self.field.to_y(pos.get()))),
      Solver::Minimax => self
        .minimax
        .minimax_with_time(
          &mut self.field,
          player,
          Duration::from_millis(u64::from(time - self.config.bot.time_gap)),
        )
        .or_else(|| heuristic::heuristic(&self.field, player))
        .map(|pos| (self.field.to_x(pos.get()), self.field.to_y(pos.get()))),
      Solver::Heuristic => {
        heuristic::heuristic(&self.field, player).map(|pos| (self.field.to_x(pos.get()), self.field.to_y(pos.get())))
      }
    }
  }

  pub fn best_move_with_full_time(
    &mut self,
    player: Player,
    remaining_time: u32,
    time_per_move: u32,
  ) -> Option<(u32, u32)> {
    self.best_move_with_time(player, time_per_move + remaining_time / 25)
  }

  pub fn best_move_with_complexity(&mut self, player: Player, complexity: u32) -> Option<(u32, u32)> {
    if self.field.width() < 3 || self.field.height() < 3 || Bot::is_field_occupied(&self.field) {
      return None;
    }
    if let Some(m) = self.initial_move() {
      return Some(m);
    }
    if let Some(&pos) = self.patterns.find(&self.field, player, false).choose(&mut self.rng) {
      return Some((self.field.to_x(pos), self.field.to_y(pos)));
    }
    match self.config.bot.solver {
      Solver::Uct => {
        let iterations_count = (complexity - MIN_COMPLEXITY) as usize * (MAX_UCT_ITERATIONS - MIN_UCT_ITERATIONS)
          / (MAX_COMPLEXITY - MIN_COMPLEXITY) as usize
          + MIN_UCT_ITERATIONS;
        self
          .uct
          .best_move_with_iterations_count(&self.field, player, &mut self.rng, iterations_count)
          .or_else(|| heuristic::heuristic(&self.field, player))
          .map(|pos| (self.field.to_x(pos.get()), self.field.to_y(pos.get())))
      }
      Solver::Minimax => {
        let depth = (complexity - MIN_COMPLEXITY) * (MAX_MINIMAX_DEPTH - MIN_MINIMAX_DEPTH)
          / (MAX_COMPLEXITY - MIN_COMPLEXITY)
          + MIN_MINIMAX_DEPTH;
        self
          .minimax
          .minimax(&mut self.field, player, depth)
          .or_else(|| heuristic::heuristic(&self.field, player))
          .map(|pos| (self.field.to_x(pos.get()), self.field.to_y(pos.get())))
      }
      Solver::Heuristic => {
        heuristic::heuristic(&self.field, player).map(|pos| (self.field.to_x(pos.get()), self.field.to_y(pos.get())))
      }
    }
  }

  pub fn put_point(&mut self, x: u32, y: u32, player: Player) -> bool {
    let pos = self.field.to_pos(x, y);
    self.field.put_point(pos, player)
  }

  pub fn undo(&mut self) -> bool {
    self.field.undo()
  }
}
<|endoftext|>


<commit_before>//! Similar to https://github.com/retep998/wio-rs/blob/44093f7db8/src/com.rs , but can be null

use std::ops::Deref;
use std::ptr;
use winapi;
use winapi::Interface;
use winapi::ctypes::c_void;
use winapi::shared::winerror::HRESULT;
use winapi::um::unknwnbase::IUnknown;

pub trait ToResult: Sized {
    fn to_result(self) -> Result<(), Self>;
}

impl ToResult for HRESULT {
    fn to_result(self) -> Result<(), Self> {
        if winapi::shared::winerror::SUCCEEDED(self) {
            Ok(())
        } else {
            Err(self)
        }
    }
}

/// Nullable pointer to an owned COM object
pub struct Com<T> where T: AsIUnknown {
    ptr: *mut T,
}

impl<T> Com<T> where T: AsIUnknown {
    pub fn null() -> Self {
        Com { ptr: ptr::null_mut() }
    }

    pub fn as_ref(&self) -> Option<&T> {
        unsafe {
            self.ptr.as_ref()
        }
    }

    pub unsafe fn as_ptr_ptr(&mut self) -> *mut *mut T {
        &mut self.ptr
    }

    pub unsafe fn as_void_ptr_ptr(&mut self) -> *mut *mut c_void {
        self.as_ptr_ptr() as _
    }

    pub fn query_interface<U>(&self) -> Result<Com<U>, HRESULT> where U: AsIUnknown {
        let mut obj = Com::<U>::null();
        unsafe {
            self.as_iunknown().QueryInterface(&U::uuidof(), obj.as_void_ptr_ptr()).to_result()?
        };
        Ok(obj)
    }
}

impl<T> Deref for Com<T> where T: AsIUnknown {
    type Target = T;

    fn deref(&self) -> &T {
        self.as_ref().expect("dereferencing a null COM pointer")
    }
}

impl<T> Drop for Com<T> where T: AsIUnknown {
    fn drop(&mut self) {
        unsafe {
            if let Some(r) = self.ptr.as_ref() {
                r.as_iunknown().Release();
            }
        }
    }
}

pub trait AsIUnknown: Interface {
    fn as_iunknown(&self) -> &IUnknown;
}

macro_rules! impl_as_iunknown {
    ($( $ty: path, )+) => {
        $(
            impl AsIUnknown for $ty {
                fn as_iunknown(&self) -> &IUnknown { self }
            }
        )+
    }
}

impl_as_iunknown! {
    winapi::shared::dxgi::IDXGIDevice,
    winapi::um::d3d11::ID3D11Device,
    winapi::um::dcomp::IDCompositionDevice,
    winapi::um::dcomp::IDCompositionTarget,
}
<commit_msg>*mut *mut _ alreay requires unsafe to break invariants<commit_after>//! Similar to https://github.com/retep998/wio-rs/blob/44093f7db8/src/com.rs , but can be null

use std::ops::Deref;
use std::ptr;
use winapi;
use winapi::Interface;
use winapi::ctypes::c_void;
use winapi::shared::winerror::HRESULT;
use winapi::um::unknwnbase::IUnknown;

pub trait ToResult: Sized {
    fn to_result(self) -> Result<(), Self>;
}

impl ToResult for HRESULT {
    fn to_result(self) -> Result<(), Self> {
        if winapi::shared::winerror::SUCCEEDED(self) {
            Ok(())
        } else {
            Err(self)
        }
    }
}

/// Nullable pointer to an owned COM object
pub struct Com<T> where T: AsIUnknown {
    ptr: *mut T,
}

impl<T> Com<T> where T: AsIUnknown {
    pub fn null() -> Self {
        Com { ptr: ptr::null_mut() }
    }

    pub fn as_ref(&self) -> Option<&T> {
        unsafe {
            self.ptr.as_ref()
        }
    }

    pub fn as_ptr_ptr(&mut self) -> *mut *mut T {
        &mut self.ptr
    }

    pub fn as_void_ptr_ptr(&mut self) -> *mut *mut c_void {
        self.as_ptr_ptr() as _
    }

    pub fn query_interface<U>(&self) -> Result<Com<U>, HRESULT> where U: AsIUnknown {
        let mut obj = Com::<U>::null();
        unsafe {
            self.as_iunknown().QueryInterface(&U::uuidof(), obj.as_void_ptr_ptr()).to_result()?
        };
        Ok(obj)
    }
}

impl<T> Deref for Com<T> where T: AsIUnknown {
    type Target = T;

    fn deref(&self) -> &T {
        self.as_ref().expect("dereferencing a null COM pointer")
    }
}

impl<T> Drop for Com<T> where T: AsIUnknown {
    fn drop(&mut self) {
        unsafe {
            if let Some(r) = self.ptr.as_ref() {
                r.as_iunknown().Release();
            }
        }
    }
}

/// FIXME: https://github.com/retep998/winapi-rs/issues/571 or some other way to express this generically.
pub trait AsIUnknown: Interface {
    fn as_iunknown(&self) -> &IUnknown;
}

macro_rules! impl_as_iunknown {
    ($( $ty: path, )+) => {
        $(
            impl AsIUnknown for $ty {
                fn as_iunknown(&self) -> &IUnknown { self }
            }
        )+
    }
}

impl_as_iunknown! {
    winapi::shared::dxgi::IDXGIDevice,
    winapi::um::d3d11::ID3D11Device,
    winapi::um::dcomp::IDCompositionDevice,
    winapi::um::dcomp::IDCompositionTarget,
}
<|endoftext|>


<commit_before>/// Calculates the 32bit crc from a given set of data. It does not appear to
/// be the typical crc calculation and may be specific to Rocket League and/or
/// Unreal Engine. Implementation is close, but not the same as the crc create
pub fn calc_crc(data: &[u8]) -> u32 {
    !data.iter().fold(!0xefcb_f201, |acc, &x| {
        (acc << 8) ^ (TABLE[((u32::from(x)) ^ (acc >> 24)) as usize])
    })
}

/// Precomputed crc table. It is extremely close to IEEE housed in the
/// crc crate, but they are not interchangeable. Still need to determine
/// the differences. Copied from the Octane project.
const TABLE: [u32; 256] =
    [0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2,
     0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3,
     0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
     0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
     0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e,
     0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
     0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90,
     0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
     0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a,
     0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c,
     0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13,
     0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
     0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
     0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20,
     0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f,
     0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
     0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055,
     0xfef34de2, 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
     0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632,
     0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
     0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0,
     0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91,
     0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
     0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
     0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604,
     0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615,
     0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a,
     0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
     0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f,
     0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
     0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 0x3793a651,
     0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
     0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
     0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa,
     0xf9278673, 0xfde69bc4, 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5,
     0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
     0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4];

#[cfg(test)]
mod tests {
    use crc::calc_crc;

    #[test]
    fn crc_rumble_test() {
        let data = include_bytes!("../assets/rumble.replay");
        assert_eq!(calc_crc(&data[..]), 2034487435);
    }
}
<commit_msg>Add single crc byte test for completeness<commit_after>/// Calculates the 32bit crc from a given set of data. It does not appear to
/// be the typical crc calculation and may be specific to Rocket League and/or
/// Unreal Engine. Implementation is close, but not the same as the crc create
pub fn calc_crc(data: &[u8]) -> u32 {
    !data.iter().fold(!0xefcb_f201, |acc, &x| {
        (acc << 8) ^ (TABLE[((u32::from(x)) ^ (acc >> 24)) as usize])
    })
}

/// Precomputed crc table. It is extremely close to IEEE housed in the
/// crc crate, but they are not interchangeable. Still need to determine
/// the differences. Copied from the Octane project.
const TABLE: [u32; 256] =
    [0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2,
     0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3,
     0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
     0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
     0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e,
     0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
     0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90,
     0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
     0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a,
     0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c,
     0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13,
     0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
     0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
     0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20,
     0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f,
     0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
     0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055,
     0xfef34de2, 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
     0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632,
     0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
     0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0,
     0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91,
     0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
     0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
     0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604,
     0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615,
     0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a,
     0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
     0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f,
     0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
     0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 0x3793a651,
     0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
     0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
     0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa,
     0xf9278673, 0xfde69bc4, 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5,
     0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
     0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4];

#[cfg(test)]
mod tests {
    use crc::calc_crc;

    #[test]
    fn crc_rumble_test() {
        let data = include_bytes!("../assets/rumble.replay");
        assert_eq!(calc_crc(&data[..]), 2034487435);
    }

    #[test]
    fn single_byte_test() {
        assert_eq!(calc_crc(&[0xa0]), 0x76cc8c81);
    }
}
<|endoftext|>


<commit_before>//! Simple graphviz dot file format output.

use std::fmt::{self, Display, Write};

use visit::{GraphRef};

/// `Dot` implements output to graphviz .dot format for a graph.
///
/// Formatting and options are rather simple, this is mostly intended
/// for debugging. Exact output may change.
///
/// # Examples
///
/// ```
/// use petgraph::Graph;
/// use petgraph::dot::{Dot, Config};
///
/// let mut graph = Graph::<_, ()>::new();
/// graph.add_node("A");
/// graph.add_node("B");
/// graph.add_node("C");
/// graph.add_node("D");
/// graph.extend_with_edges(&[
///     (0, 1), (0, 2), (0, 3),
///     (1, 2), (1, 3),
///     (2, 3),
/// ]);
///
/// println!("{:?}", Dot::with_config(&graph, &[Config::EdgeNoLabel]));
///
/// // In this case the output looks like this:
/// //
/// // digraph {
/// //     0 [label="\"A\""]
/// //     1 [label="\"B\""]
/// //     2 [label="\"C\""]
/// //     3 [label="\"D\""]
/// //     0 -> 1
/// //     0 -> 2
/// //     0 -> 3
/// //     1 -> 2
/// //     1 -> 3
/// //     2 -> 3
/// // }
///
/// // If you need multiple config options, just list them all in the slice.
/// ```
pub struct Dot<'a, G> {
    graph: G,
    config: &'a [Config],
}

static TYPE: [&'static str; 2] = ["graph", "digraph"];
static EDGE: [&'static str; 2] = ["--", "->"];
static INDENT: &'static str = "    ";

impl<'a, G> Dot<'a, G> where G: GraphRef {
    /// Create a `Dot` formatting wrapper with default configuration.
    pub fn new(graph: G) -> Self {
        Self::with_config(graph, &[])
    }

    /// Create a `Dot` formatting wrapper with custom configuration.
    pub fn with_config(graph: G, config: &'a [Config]) -> Self {
        Dot {
            graph: graph,
            config: config,
        }
    }
}

/// `Dot` configuration.
///
/// This enum does not have an exhaustive definition (will be expanded)
#[derive(Debug, PartialEq, Eq)]
pub enum Config {
    /// Use indices for node labels.
    NodeIndexLabel,
    /// Use indices for edge labels.
    EdgeIndexLabel,
    /// Use no edge labels.
    EdgeNoLabel,
    #[doc(hidden)]
    _Incomplete(()),
}

use visit::{ IntoNodeReferences, NodeIndexable, IntoEdgeReferences, EdgeRef};
use visit::{ Data, NodeRef, GraphProp, };

impl<'a, G> Dot<'a, G>
{
    fn graph_fmt<NF, EF, NW, EW>(&self, g: G, f: &mut fmt::Formatter,
                    mut node_fmt: NF, mut edge_fmt: EF) -> fmt::Result
        where G: NodeIndexable + IntoNodeReferences + IntoEdgeReferences,
              G: GraphProp,
              G: Data<NodeWeight=NW, EdgeWeight=EW>,
              NF: FnMut(&NW, &mut FnMut(&Display) -> fmt::Result) -> fmt::Result,
              EF: FnMut(&EW, &mut FnMut(&Display) -> fmt::Result) -> fmt::Result,
    {
        try!(writeln!(f, "{} {{", TYPE[g.is_directed() as usize]));

        // output all labels
        for node in g.node_references() {
            try!(write!(f, "{}{}", INDENT, g.to_index(node.id())));
            if self.config.contains(&Config::NodeIndexLabel) {
                try!(writeln!(f, ""));
            } else {
                try!(write!(f, " [label=\""));
                try!(node_fmt(node.weight(), &mut |d| Escaped(d).fmt(f)));
                try!(writeln!(f, "\"]"));
            }

        }
        // output all edges
        for (i, edge) in g.edge_references().enumerate() {
            try!(write!(f, "{}{} {} {}",
                        INDENT,
                        g.to_index(edge.source()),
                        EDGE[g.is_directed() as usize],
                        g.to_index(edge.target())));
            if self.config.contains(&Config::EdgeNoLabel) {
                try!(writeln!(f, ""));
            } else if self.config.contains(&Config::EdgeIndexLabel) {
                try!(writeln!(f, " [label=\"{}\"]", i));
            } else {
                try!(write!(f, " [label=\""));
                try!(edge_fmt(edge.weight(), &mut |d| Escaped(d).fmt(f)));
                try!(writeln!(f, "\"]"));
            }
        }

        try!(writeln!(f, "}}"));
        Ok(())
    }
}

impl<'a, G> fmt::Display for Dot<'a, G>
    where G: IntoEdgeReferences + IntoNodeReferences + NodeIndexable + GraphProp,
          G::EdgeWeight: fmt::Display,
          G::NodeWeight: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.graph_fmt(self.graph, f, |n, cb| cb(n), |e, cb| cb(e))
    }
}

impl<'a, G> fmt::Debug for Dot<'a, G>
    where G: IntoEdgeReferences + IntoNodeReferences + NodeIndexable + GraphProp,
          G::EdgeWeight: fmt::Debug,
          G::NodeWeight: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.graph_fmt(self.graph, f,
                       |n, cb| cb(&DebugFmt(n)),
                       |e, cb| cb(&DebugFmt(e)))
    }
}

/// Escape for Graphviz
struct Escaper<W>(W);

impl<W> fmt::Write for Escaper<W>
    where W: fmt::Write
{
    fn write_str(&mut self, s: &str) -> fmt::Result {
        for c in s.chars() {
            try!(self.write_char(c));
        }
        Ok(())
    }

    fn write_char(&mut self, c: char) -> fmt::Result {
        match c {
            '"' | '\\' => try!(self.0.write_char('\\')),
            // \l is for left justified linebreak
            '\n' => return self.0.write_str("\\l"),
            _ => {}
        }
        self.0.write_char(c)
    }
}

/// Pass Display formatting through a simple escaping filter
struct Escaped<T>(T);

impl<T> fmt::Display for Escaped<T>
    where T: fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if f.alternate() {
            write!(&mut Escaper(f), "{:#}\n", &self.0)
        } else {
            write!(&mut Escaper(f), "{}", &self.0)
        }
    }
}

/// Pass Debug formatting to Display
struct DebugFmt<T>(T);

impl<T> fmt::Display for DebugFmt<T>
    where T: fmt::Debug
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

#[test]
fn test_escape() {
    let mut buff = String::new();
    {
        let mut e = Escaper(&mut buff);
        let _ = e.write_str("\" \\ \n");
    }
    assert_eq!(buff, "\\\" \\\\ \\l");
}
<commit_msg>Add GraphContentOnly config for dot<commit_after>//! Simple graphviz dot file format output.

use std::fmt::{self, Display, Write};

use visit::{GraphRef};

/// `Dot` implements output to graphviz .dot format for a graph.
///
/// Formatting and options are rather simple, this is mostly intended
/// for debugging. Exact output may change.
///
/// # Examples
///
/// ```
/// use petgraph::Graph;
/// use petgraph::dot::{Dot, Config};
///
/// let mut graph = Graph::<_, ()>::new();
/// graph.add_node("A");
/// graph.add_node("B");
/// graph.add_node("C");
/// graph.add_node("D");
/// graph.extend_with_edges(&[
///     (0, 1), (0, 2), (0, 3),
///     (1, 2), (1, 3),
///     (2, 3),
/// ]);
///
/// println!("{:?}", Dot::with_config(&graph, &[Config::EdgeNoLabel]));
///
/// // In this case the output looks like this:
/// //
/// // digraph {
/// //     0 [label="\"A\""]
/// //     1 [label="\"B\""]
/// //     2 [label="\"C\""]
/// //     3 [label="\"D\""]
/// //     0 -> 1
/// //     0 -> 2
/// //     0 -> 3
/// //     1 -> 2
/// //     1 -> 3
/// //     2 -> 3
/// // }
///
/// // If you need multiple config options, just list them all in the slice.
/// ```
pub struct Dot<'a, G> {
    graph: G,
    config: &'a [Config],
}

static TYPE: [&'static str; 2] = ["graph", "digraph"];
static EDGE: [&'static str; 2] = ["--", "->"];
static INDENT: &'static str = "    ";

impl<'a, G> Dot<'a, G> where G: GraphRef {
    /// Create a `Dot` formatting wrapper with default configuration.
    pub fn new(graph: G) -> Self {
        Self::with_config(graph, &[])
    }

    /// Create a `Dot` formatting wrapper with custom configuration.
    pub fn with_config(graph: G, config: &'a [Config]) -> Self {
        Dot {
            graph: graph,
            config: config,
        }
    }
}

/// `Dot` configuration.
///
/// This enum does not have an exhaustive definition (will be expanded)
#[derive(Debug, PartialEq, Eq)]
pub enum Config {
    /// Use indices for node labels.
    NodeIndexLabel,
    /// Use indices for edge labels.
    EdgeIndexLabel,
    /// Use no edge labels.
    EdgeNoLabel,
    /// Do not print the graph/digraph string.
    GraphContentOnly,
    #[doc(hidden)]
    _Incomplete(()),
}

use visit::{ IntoNodeReferences, NodeIndexable, IntoEdgeReferences, EdgeRef};
use visit::{ Data, NodeRef, GraphProp, };

impl<'a, G> Dot<'a, G>
{
    fn graph_fmt<NF, EF, NW, EW>(&self, g: G, f: &mut fmt::Formatter,
                    mut node_fmt: NF, mut edge_fmt: EF) -> fmt::Result
        where G: NodeIndexable + IntoNodeReferences + IntoEdgeReferences,
              G: GraphProp,
              G: Data<NodeWeight=NW, EdgeWeight=EW>,
              NF: FnMut(&NW, &mut FnMut(&Display) -> fmt::Result) -> fmt::Result,
              EF: FnMut(&EW, &mut FnMut(&Display) -> fmt::Result) -> fmt::Result,
    {
        if !self.config.contains(&Config::GraphContentOnly) {
            try!(writeln!(f, "{} {{", TYPE[g.is_directed() as usize]));
        }

        // output all labels
        for node in g.node_references() {
            try!(write!(f, "{}{}", INDENT, g.to_index(node.id())));
            if self.config.contains(&Config::NodeIndexLabel) {
                try!(writeln!(f, ""));
            } else {
                try!(write!(f, " [label=\""));
                try!(node_fmt(node.weight(), &mut |d| Escaped(d).fmt(f)));
                try!(writeln!(f, "\"]"));
            }

        }
        // output all edges
        for (i, edge) in g.edge_references().enumerate() {
            try!(write!(f, "{}{} {} {}",
                        INDENT,
                        g.to_index(edge.source()),
                        EDGE[g.is_directed() as usize],
                        g.to_index(edge.target())));
            if self.config.contains(&Config::EdgeNoLabel) {
                try!(writeln!(f, ""));
            } else if self.config.contains(&Config::EdgeIndexLabel) {
                try!(writeln!(f, " [label=\"{}\"]", i));
            } else {
                try!(write!(f, " [label=\""));
                try!(edge_fmt(edge.weight(), &mut |d| Escaped(d).fmt(f)));
                try!(writeln!(f, "\"]"));
            }
        }

        if !self.config.contains(&Config::GraphContentOnly) {
            try!(writeln!(f, "}}"));
        }
        Ok(())
    }
}

impl<'a, G> fmt::Display for Dot<'a, G>
    where G: IntoEdgeReferences + IntoNodeReferences + NodeIndexable + GraphProp,
          G::EdgeWeight: fmt::Display,
          G::NodeWeight: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.graph_fmt(self.graph, f, |n, cb| cb(n), |e, cb| cb(e))
    }
}

impl<'a, G> fmt::Debug for Dot<'a, G>
    where G: IntoEdgeReferences + IntoNodeReferences + NodeIndexable + GraphProp,
          G::EdgeWeight: fmt::Debug,
          G::NodeWeight: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.graph_fmt(self.graph, f,
                       |n, cb| cb(&DebugFmt(n)),
                       |e, cb| cb(&DebugFmt(e)))
    }
}

/// Escape for Graphviz
struct Escaper<W>(W);

impl<W> fmt::Write for Escaper<W>
    where W: fmt::Write
{
    fn write_str(&mut self, s: &str) -> fmt::Result {
        for c in s.chars() {
            try!(self.write_char(c));
        }
        Ok(())
    }

    fn write_char(&mut self, c: char) -> fmt::Result {
        match c {
            '"' | '\\' => try!(self.0.write_char('\\')),
            // \l is for left justified linebreak
            '\n' => return self.0.write_str("\\l"),
            _ => {}
        }
        self.0.write_char(c)
    }
}

/// Pass Display formatting through a simple escaping filter
struct Escaped<T>(T);

impl<T> fmt::Display for Escaped<T>
    where T: fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if f.alternate() {
            write!(&mut Escaper(f), "{:#}\n", &self.0)
        } else {
            write!(&mut Escaper(f), "{}", &self.0)
        }
    }
}

/// Pass Debug formatting to Display
struct DebugFmt<T>(T);

impl<T> fmt::Display for DebugFmt<T>
    where T: fmt::Debug
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

#[test]
fn test_escape() {
    let mut buff = String::new();
    {
        let mut e = Escaper(&mut buff);
        let _ = e.write_str("\" \\ \n");
    }
    assert_eq!(buff, "\\\" \\\\ \\l");
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::path::PathBuf;
use std::io::{self, Read, BufReader};
use std::io::prelude::*;
use toml;
use xdg::BaseDirectories;

use configuration::Configuration;
use execution_timer::ExecutionTimer;
use file;
use mru_list::MRUList;

/// Represents the persistent runtime data of the system. This is things like MRU lists
/// that we expect to get written to disk and be available the next time we start.
/// Therefore, it excludes things like buffer collections. Things that are virtually
/// always used are non-lazy, while things that are used less frequently are wrapped
/// by a Lazy<T>.

/// TODO: PersistentData or SessionData.
struct DataItem<T> {
    filename: &'static str,
    data: T
}

pub struct RuntimeData {
    mru: MRUList,
    mru2: DataItem<MRUList>
}

impl RuntimeData {
    /// Constructs a new RuntimeData object based on the default configuration.
    pub fn new(config: &Configuration) -> RuntimeData {
        RuntimeData {
            mru: MRUList::new(config.max_mru_items()),
            mru2: DataItem { filename: "mru.toml", data: MRUList::new(config.max_mru_items()) }
        }
    }

    fn place_file(xdg: &BaseDirectories, filename: &str) -> Option<PathBuf> {
        let path = xdg.place_data_file(filename);
        if path.is_err() {
            warn!("Could not locate runtime data file {}  in xdg directory structure", filename);
            return None;
        }

        let path = path.unwrap();

        if path.exists() && !path.is_file() {
            warn!("The runtime data file {:?} is not a file", path);
            return None;
        }

        Some(path)
    }

    pub fn load_runtime_data(config: &Configuration, xdg: &BaseDirectories) -> RuntimeData {
        let _timer = ExecutionTimer::with_start_message("load_runtime_data");

        let mut rd = RuntimeData::new(&config);

        let dir = xdg.get_data_home();
        if !dir.exists() {
            warn!("The data_directory does not exist, no runtime data will be loaded, data_directory: {:?}", dir);
            return rd;
        }

        if !dir.is_dir() {
            warn!("The data_directory is a file, not a directory, no runtime data will be loaded, data_directory: {:?}", dir);
            return rd;
        }

        info!("Loading runtime data from data_directory {:?}", dir);

        let path = RuntimeData::place_file(&xdg, MRU_FILE);
        if let Some(filename) = path {
            if let Some(mru) = load_mru(config.max_mru_items(), &filename) {
                rd.mru = mru;
            }
        }

        rd
    }

    /// Writes the runtime data to disk.
    pub fn save(&mut self, config: &Configuration, xdg: &BaseDirectories) {
        let _timer = ExecutionTimer::with_start_message("save_runtime_data");

        if self.mru.is_changed() {

            let path = RuntimeData::place_file(&xdg, MRU_FILE);
            info!("MRU is changed, path = {:?}", path);

            if let Some(filename) = path {
                if let Ok(num_bytes) = save_mru(&filename, &self.mru) {
                    self.mru.clear_is_changed();
                    info!("Wrote {} bytes to {:?}", num_bytes, filename);
                }
            }
        }
    }

    pub fn mru(&mut self) -> &mut MRUList {
        &mut self.mru
    }
}

const MRU_FILE : &'static str = "mru.toml";

fn load_mru(max_mru_items: usize, filename: &PathBuf) -> Option<MRUList> {
    let list = file::load_to_vector(filename);

    // TODO: if num lines loaded > max_mru_items then the mru list needs to be marked as changed.

    match list {
        Ok(list) => {
            info!("Loaded {} lines from {:?}", list.len(), filename);
            let mru = MRUList::from_slice(max_mru_items, &list);
            dump(&mru);
            Some(mru)
        },
        Err(e) => None
    }
}

fn save_mru(filename: &PathBuf, mru: &MRUList) -> Result<usize, String> {
    let v = mru.iter().map(|f| f.clone()).collect();
    file::save_from_vector(filename, v)
}

pub fn dump(mru: &MRUList) {
    for (i, file) in mru.iter().enumerate() {
        info!("{} = {:?}", i, file);
    }
}
<commit_msg>Refactor saving of MRULists in the RuntimeData.<commit_after>use std::fs::File;
use std::path::PathBuf;
use std::io::{self, Read, BufReader};
use std::io::prelude::*;
use toml;
use xdg::BaseDirectories;

use configuration::Configuration;
use execution_timer::ExecutionTimer;
use file;
use mru_list::MRUList;

/// Represents the persistent runtime data of the system. This is things like MRU lists
/// that we expect to get written to disk and be available the next time we start.
/// Therefore, it excludes things like buffer collections. Things that are virtually
/// always used are non-lazy, while things that are used less frequently are wrapped
/// by a Lazy<T>.

/// TODO: PersistentData or SessionData.
struct DataItem<T> {
    filename: &'static str,
    data: T
}

pub struct RuntimeData {
    mru: MRUList,
    mru2: DataItem<MRUList>
}

impl RuntimeData {
    /// Constructs a new RuntimeData object based on the default configuration.
    pub fn new(config: &Configuration) -> RuntimeData {
        RuntimeData {
            mru: MRUList::new(config.max_mru_items()),
            mru2: DataItem { filename: "mru.toml", data: MRUList::new(config.max_mru_items()) }
        }
    }

    fn place_file(xdg: &BaseDirectories, filename: &str) -> Option<PathBuf> {
        let path = xdg.place_data_file(filename);
        if path.is_err() {
            warn!("Could not locate runtime data file {}  in xdg directory structure", filename);
            return None;
        }

        let path = path.unwrap();

        if path.exists() && !path.is_file() {
            warn!("The runtime data file {:?} is not a file", path);
            return None;
        }

        Some(path)
    }

    pub fn load_runtime_data(config: &Configuration, xdg: &BaseDirectories) -> RuntimeData {
        let _timer = ExecutionTimer::with_start_message("load_runtime_data");

        let mut rd = RuntimeData::new(&config);

        let dir = xdg.get_data_home();
        if !dir.exists() {
            warn!("The data_directory does not exist, no runtime data will be loaded, data_directory: {:?}", dir);
            return rd;
        }

        if !dir.is_dir() {
            warn!("The data_directory is a file, not a directory, no runtime data will be loaded, data_directory: {:?}", dir);
            return rd;
        }

        info!("Loading runtime data from data_directory {:?}", dir);

        let path = RuntimeData::place_file(&xdg, MRU_FILE);
        if let Some(filename) = path {
            if let Some(mru) = load_mru(config.max_mru_items(), &filename) {
                rd.mru = mru;
            }
        }

        rd
    }

    /// Writes the runtime data to disk.
    pub fn save(&mut self, config: &Configuration, xdg: &BaseDirectories) {
        let _timer = ExecutionTimer::with_start_message("save_runtime_data");

        if self.mru.is_changed() {
            let path = RuntimeData::place_file(&xdg, MRU_FILE);
            info!("MRU is changed, path = {:?}", path);

            if let Some(filename) = path {
                match self.mru.save(filename.as_path()) {
                    Ok(num_bytes) => { info!("Wrote {} bytes to {:?}", num_bytes, filename); },
                    Err(e) => { warn!("Could not save MRU List to {:?}", filename); }
                }
            }
        }
    }

    pub fn mru(&mut self) -> &mut MRUList {
        &mut self.mru
    }
}

const MRU_FILE : &'static str = "mru.toml";

fn load_mru(max_mru_items: usize, filename: &PathBuf) -> Option<MRUList> {
    let list = file::load_to_vector(filename);

    // TODO: if num lines loaded > max_mru_items then the mru list needs to be marked as changed.

    match list {
        Ok(list) => {
            info!("Loaded {} lines from {:?}", list.len(), filename);
            let mru = MRUList::from_slice(max_mru_items, &list);
            dump(&mru);
            Some(mru)
        },
        Err(e) => None
    }
}

pub fn dump(mru: &MRUList) {
    for (i, file) in mru.iter().enumerate() {
        info!("{} = {:?}", i, file);
    }
}
<|endoftext|>


<commit_before>use std::fs;

use kite_rocksdb::RocksDBIndexStore;

use analysis::AnalyzerSpec;
use analysis::tokenizers::TokenizerSpec;
use analysis::filters::FilterSpec;
use analysis::ngram_generator::Edge;
use index::Index;

use api::persistent;
use api::iron::prelude::*;
use api::iron::status;
use api::router::Router;
use api::utils::json_response;

pub fn view_get_index(req: &mut Request) -> IronResult<Response> {
    let ref system = get_system!(req);
    let ref index_name = read_path_parameter!(req, "index").unwrap_or("");

    // Lock index array
    let indices = system.indices.read().unwrap();

    // Check index exists
    get_index_or_404!(indices, *index_name);

    // TODO
    return Ok(json_response(status::Ok, "{}"));
}

pub fn view_put_index(req: &mut Request) -> IronResult<Response> {
    let ref system = get_system!(req);
    let ref index_name = read_path_parameter!(req, "index").unwrap_or("");

    // Lock index array
    let mut indices = system.indices.write().unwrap();

    // Load data from body
    // let data = json_from_request_body!(req);

    // Find index
    let index_ref = indices.names.find_canonical(&index_name);

    match index_ref {
        Some(_) => {
            // Update existing index
            // TODO

            system.log.info("[api] updated index", b!("index" => *index_name));
        }
        None => {
            // Create index
            let mut indices_dir = system.get_indices_dir();
            indices_dir.push(index_name);
            indices_dir.set_extension("rsi");
            let mut index = Index::new(index_name.clone().to_owned(), RocksDBIndexStore::create(indices_dir).unwrap());

            // Insert standard and edgengram analyzers
            // TODO: Load these from index settings
            index.analyzers.insert("standard".to_string(), AnalyzerSpec {
                tokenizer: TokenizerSpec::Standard,
                filters: vec![
                    FilterSpec::Lowercase,
                    FilterSpec::ASCIIFolding,
                ]
            });
            index.analyzers.insert("edgengram_analyzer".to_string(), AnalyzerSpec {
                tokenizer: TokenizerSpec::Standard,
                filters: vec![
                    FilterSpec::Lowercase,
                    FilterSpec::ASCIIFolding,
                    FilterSpec::NGram {
                        min_size: 2,
                        max_size: 15,
                        edge: Edge::Left
                    },
                ]
            });

            // TODO: load settings

            let index_ref = indices.insert(index);
            indices.names.insert_canonical(index_name.clone().to_owned(), index_ref).unwrap();

            system.log.info("[api] created index", b!("index" => *index_name));
        }
    }

    return Ok(json_response(status::Ok, "{\"acknowledged\": true}"));
}

pub fn view_delete_index(req: &mut Request) -> IronResult<Response> {
    let ref system = get_system!(req);
    let ref index_selector = read_path_parameter!(req, "index").unwrap_or("");

    // Lock index array
    let mut indices = system.indices.write().unwrap();

    // Make sure the index exists
    get_index_or_404!(indices, *index_selector);

    // Remove indices
    for index_ref in indices.names.find(*index_selector) {
        // Get the index name
        let index_name = {
            if let Some(index) = indices.get(&index_ref) {
                index.name().to_string()
            } else {
                // Index doesn't exist
                continue;
            }
        };

        // Remove index from array
        indices.remove(&index_ref);

        // Delete canonical name
        indices.names.delete_canonical(&index_name, index_ref).unwrap();

        // Delete file
        let mut indices_dir = system.get_indices_dir();
        indices_dir.push(&index_name);
        indices_dir.set_extension("rsi");
        match fs::remove_dir_all(&indices_dir) {
            Ok(()) => {},
            Err(e) => {
                system.log.warn("[api] failed to delete index data", b!("index" => format!("{}", index_name), "error" => format!("{}", e)));
            }
        }

        system.log.info("[api] deleted index", b!("index" => format!("{}", index_name)));

        // Delete aliases
        let alias_names = indices.names.iter_index_aliases(index_ref).map(|n| n.to_string()).collect::<Vec<String>>();
        for alias_name in alias_names {
            println!("DELETING {}", alias_name);
            let alias_deleted = indices.names.delete_alias(&alias_name, index_ref).unwrap();

            // If this was the only index being referenced by the alias, the alias would be deleted
            if alias_deleted {
                 system.log.info("[api] deleted alias", b!("alias" => format!("{}", alias_name)));
            }
        }
    }

    return Ok(json_response(status::Ok, "{\"acknowledged\": true}"));
}

pub fn view_post_refresh_index(_req: &mut Request) -> IronResult<Response> {
    // let ref system = get_system!(req);
    // let ref index_name = read_path_parameter!(req, "index").unwrap_or("");

    // Lock index array
    // TODO
    // let mut indices = system.indices.write().unwrap();

    // TODO: {"_shards":{"total":10,"successful":5,"failed":0}}
    return Ok(json_response(status::Ok, "{\"acknowledged\": true}"));
}
<commit_msg>Remove another stray print<commit_after>use std::fs;

use kite_rocksdb::RocksDBIndexStore;

use analysis::AnalyzerSpec;
use analysis::tokenizers::TokenizerSpec;
use analysis::filters::FilterSpec;
use analysis::ngram_generator::Edge;
use index::Index;

use api::persistent;
use api::iron::prelude::*;
use api::iron::status;
use api::router::Router;
use api::utils::json_response;

pub fn view_get_index(req: &mut Request) -> IronResult<Response> {
    let ref system = get_system!(req);
    let ref index_name = read_path_parameter!(req, "index").unwrap_or("");

    // Lock index array
    let indices = system.indices.read().unwrap();

    // Check index exists
    get_index_or_404!(indices, *index_name);

    // TODO
    return Ok(json_response(status::Ok, "{}"));
}

pub fn view_put_index(req: &mut Request) -> IronResult<Response> {
    let ref system = get_system!(req);
    let ref index_name = read_path_parameter!(req, "index").unwrap_or("");

    // Lock index array
    let mut indices = system.indices.write().unwrap();

    // Load data from body
    // let data = json_from_request_body!(req);

    // Find index
    let index_ref = indices.names.find_canonical(&index_name);

    match index_ref {
        Some(_) => {
            // Update existing index
            // TODO

            system.log.info("[api] updated index", b!("index" => *index_name));
        }
        None => {
            // Create index
            let mut indices_dir = system.get_indices_dir();
            indices_dir.push(index_name);
            indices_dir.set_extension("rsi");
            let mut index = Index::new(index_name.clone().to_owned(), RocksDBIndexStore::create(indices_dir).unwrap());

            // Insert standard and edgengram analyzers
            // TODO: Load these from index settings
            index.analyzers.insert("standard".to_string(), AnalyzerSpec {
                tokenizer: TokenizerSpec::Standard,
                filters: vec![
                    FilterSpec::Lowercase,
                    FilterSpec::ASCIIFolding,
                ]
            });
            index.analyzers.insert("edgengram_analyzer".to_string(), AnalyzerSpec {
                tokenizer: TokenizerSpec::Standard,
                filters: vec![
                    FilterSpec::Lowercase,
                    FilterSpec::ASCIIFolding,
                    FilterSpec::NGram {
                        min_size: 2,
                        max_size: 15,
                        edge: Edge::Left
                    },
                ]
            });

            // TODO: load settings

            let index_ref = indices.insert(index);
            indices.names.insert_canonical(index_name.clone().to_owned(), index_ref).unwrap();

            system.log.info("[api] created index", b!("index" => *index_name));
        }
    }

    return Ok(json_response(status::Ok, "{\"acknowledged\": true}"));
}

pub fn view_delete_index(req: &mut Request) -> IronResult<Response> {
    let ref system = get_system!(req);
    let ref index_selector = read_path_parameter!(req, "index").unwrap_or("");

    // Lock index array
    let mut indices = system.indices.write().unwrap();

    // Make sure the index exists
    get_index_or_404!(indices, *index_selector);

    // Remove indices
    for index_ref in indices.names.find(*index_selector) {
        // Get the index name
        let index_name = {
            if let Some(index) = indices.get(&index_ref) {
                index.name().to_string()
            } else {
                // Index doesn't exist
                continue;
            }
        };

        // Remove index from array
        indices.remove(&index_ref);

        // Delete canonical name
        indices.names.delete_canonical(&index_name, index_ref).unwrap();

        // Delete file
        let mut indices_dir = system.get_indices_dir();
        indices_dir.push(&index_name);
        indices_dir.set_extension("rsi");
        match fs::remove_dir_all(&indices_dir) {
            Ok(()) => {},
            Err(e) => {
                system.log.warn("[api] failed to delete index data", b!("index" => format!("{}", index_name), "error" => format!("{}", e)));
            }
        }

        system.log.info("[api] deleted index", b!("index" => format!("{}", index_name)));

        // Delete aliases
        let alias_names = indices.names.iter_index_aliases(index_ref).map(|n| n.to_string()).collect::<Vec<String>>();
        for alias_name in alias_names {
            let alias_deleted = indices.names.delete_alias(&alias_name, index_ref).unwrap();

            // If this was the only index being referenced by the alias, the alias would be deleted
            if alias_deleted {
                 system.log.info("[api] deleted alias", b!("alias" => format!("{}", alias_name)));
            }
        }
    }

    return Ok(json_response(status::Ok, "{\"acknowledged\": true}"));
}

pub fn view_post_refresh_index(_req: &mut Request) -> IronResult<Response> {
    // let ref system = get_system!(req);
    // let ref index_name = read_path_parameter!(req, "index").unwrap_or("");

    // Lock index array
    // TODO
    // let mut indices = system.indices.write().unwrap();

    // TODO: {"_shards":{"total":10,"successful":5,"failed":0}}
    return Ok(json_response(status::Ok, "{\"acknowledged\": true}"));
}
<|endoftext|>