<commit_before>use alloc::arc::Arc;
use alloc::boxed::Box;
use collections::{BTreeMap, Vec, VecDeque};
use core::mem;
use spin::Mutex;

use context::arch;
use context::file::File;
use context::memory::{Grant, Memory, SharedMemory, Tls};
use device;
use scheme::{SchemeNamespace, FileHandle};
use syscall::data::{Event, SigAction};
use syscall::flag::SIG_DFL;
use sync::{WaitMap, WaitQueue};

/// Unique identifier for a context (i.e. `pid`).
use ::core::sync::atomic::AtomicUsize;
int_like!(ContextId, AtomicContextId, usize, AtomicUsize);

/// The status of a context - used for scheduling
/// See syscall::process::waitpid and the sync module for examples of usage
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Status {
    Runnable,
    Blocked,
    Exited(usize)
}

/// A context, which identifies either a process or a thread
#[derive(Debug)]
pub struct Context {
    /// The ID of this context
    pub id: ContextId,
    /// The group ID of this context
    pub pgid: ContextId,
    /// The ID of the parent context
    pub ppid: ContextId,
    /// The real user id
    pub ruid: u32,
    /// The real group id
    pub rgid: u32,
    /// The real namespace id
    pub rns: SchemeNamespace,
    /// The effective user id
    pub euid: u32,
    /// The effective group id
    pub egid: u32,
    /// The effective namespace id
    pub ens: SchemeNamespace,
    /// Status of context
    pub status: Status,
    /// Context running or not
    pub running: bool,
    /// CPU ID, if locked
    pub cpu_id: Option<usize>,
    /// Context is halting parent
    pub vfork: bool,
    /// Context is being waited on
    pub waitpid: Arc<WaitMap<ContextId, usize>>,
    /// Context should handle pending signals
    pub pending: VecDeque<u8>,
    /// Context should wake up at specified time
    pub wake: Option<(u64, u64)>,
    /// The architecture specific context
    pub arch: arch::Context,
    /// Kernel FX - used to store SIMD and FPU registers on context switch
    pub kfx: Option<Box<[u8]>>,
    /// Kernel stack
    pub kstack: Option<Box<[u8]>>,
    /// Kernel signal backup
    pub ksig: Option<(arch::Context, Option<Box<[u8]>>, Option<Box<[u8]>>)>,
    /// Restore ksig context on next switch
    pub ksig_restore: bool,
    /// Executable image
    pub image: Vec<SharedMemory>,
    /// User heap
    pub heap: Option<SharedMemory>,
    /// User stack
    pub stack: Option<Memory>,
    /// User signal stack
    pub sigstack: Option<Memory>,
    /// User Thread local storage
    pub tls: Option<Tls>,
    /// User grants
    pub grants: Arc<Mutex<Vec<Grant>>>,
    /// The name of the context
    pub name: Arc<Mutex<Vec<u8>>>,
    /// The current working directory
    pub cwd: Arc<Mutex<Vec<u8>>>,
    /// Kernel events
    pub events: Arc<WaitQueue<Event>>,
    /// The process environment
    pub env: Arc<Mutex<BTreeMap<Box<[u8]>, Arc<Mutex<Vec<u8>>>>>>,
    /// The open files in the scheme
    pub files: Arc<Mutex<Vec<Option<File>>>>,
    /// Singal actions
    pub actions: Arc<Mutex<Vec<(SigAction, usize)>>>,
}

impl Context {
    pub fn new(id: ContextId) -> Context {
        Context {
            id: id,
            pgid: id,
            ppid: ContextId::from(0),
            ruid: 0,
            rgid: 0,
            rns: SchemeNamespace::from(0),
            euid: 0,
            egid: 0,
            ens: SchemeNamespace::from(0),
            status: Status::Blocked,
            running: false,
            cpu_id: None,
            vfork: false,
            waitpid: Arc::new(WaitMap::new()),
            pending: VecDeque::new(),
            wake: None,
            arch: arch::Context::new(),
            kfx: None,
            kstack: None,
            ksig: None,
            ksig_restore: false,
            image: Vec::new(),
            heap: None,
            stack: None,
            sigstack: None,
            tls: None,
            grants: Arc::new(Mutex::new(Vec::new())),
            name: Arc::new(Mutex::new(Vec::new())),
            cwd: Arc::new(Mutex::new(Vec::new())),
            events: Arc::new(WaitQueue::new()),
            env: Arc::new(Mutex::new(BTreeMap::new())),
            files: Arc::new(Mutex::new(Vec::new())),
            actions: Arc::new(Mutex::new(vec![(
                SigAction {
                    sa_handler: unsafe { mem::transmute(SIG_DFL) },
                    sa_mask: [0; 2],
                    sa_flags: 0,
                },
                0
            ); 128])),
        }
    }

    /// Make a relative path absolute
    /// Given a cwd of "scheme:/path"
    /// This function will turn "foo" into "scheme:/path/foo"
    /// "/foo" will turn into "scheme:/foo"
    /// "bar:/foo" will be used directly, as it is already absolute
    pub fn canonicalize(&self, path: &[u8]) -> Vec<u8> {
        let mut canon = if path.iter().position(|&b| b == b':').is_none() {
            let cwd = self.cwd.lock();

            let mut canon = if !path.starts_with(b"/") {
                let mut c = cwd.clone();
                if ! c.ends_with(b"/") {
                    c.push(b'/');
                }
                c
            } else {
                cwd[..cwd.iter().position(|&b| b == b':').map_or(1, |i| i + 1)].to_vec()
            };

            canon.extend_from_slice(&path);
            canon
        } else {
            path.to_vec()
        };

        // NOTE: assumes the scheme does not include anything like "../" or "./"
        let mut result = {
            let parts = canon.split(|&c| c == b'/')
                .filter(|&part| part != b".")
                .rev()
                .scan(0, |nskip, part| {
                    if part == b"." {
                        Some(None)
                    } else if part == b".." {
                        *nskip += 1;
                        Some(None)
                    } else {
                        if *nskip > 0 {
                            *nskip -= 1;
                            Some(None)
                        } else {
                            Some(Some(part))
                        }
                    }
                })
                .filter_map(|x| x)
                .collect::<Vec<_>>();
            parts
                .iter()
                .rev()
                .fold(Vec::new(), |mut vec, &part| {
                    vec.extend_from_slice(part);
                    vec.push(b'/');
                    vec
                })
        };
        result.pop(); // remove extra '/'

        // replace with the root of the scheme if it's empty
        if result.len() == 0 {
            let pos = canon.iter()
                            .position(|&b| b == b':')
                            .map_or(canon.len(), |p| p + 1);
            canon.truncate(pos);
            canon
        } else {
            result
        }
    }

    /// Block the context, and return true if it was runnable before being blocked
    pub fn block(&mut self) -> bool {
        if self.status == Status::Runnable {
            self.status = Status::Blocked;
            true
        } else {
            false
        }
    }

    /// Unblock context, and return true if it was blocked before being marked runnable
    pub fn unblock(&mut self) -> bool {
        if self.status == Status::Blocked {
            self.status = Status::Runnable;
            if let Some(cpu_id) = self.cpu_id {
                if cpu_id != ::cpu_id() {
                    // Send IPI if not on current CPU
                    // TODO: Make this more architecture independent
                    unsafe { device::local_apic::LOCAL_APIC.ipi(cpu_id) };
                }
            }
            true
        } else {
            false
        }
    }

    /// Add a file to the lowest available slot.
    /// Return the file descriptor number or None if no slot was found
    pub fn add_file(&self, file: File) -> Option<FileHandle> {
        let mut files = self.files.lock();
        for (i, mut file_option) in files.iter_mut().enumerate() {
            if file_option.is_none() {
                *file_option = Some(file);
                return Some(FileHandle::from(i));
            }
        }
        let len = files.len();
        if len < super::CONTEXT_MAX_FILES {
            files.push(Some(file));
            Some(FileHandle::from(len))
        } else {
            None
        }
    }

    /// Get a file
    pub fn get_file(&self, i: FileHandle) -> Option<File> {
        let files = self.files.lock();
        if i.into() < files.len() {
            files[i.into()].clone()
        } else {
            None
        }
    }

    /// Insert a file with a specific handle number. This is used by dup2
    /// Return the file descriptor number or None if the slot was not empty, or i was invalid
    pub fn insert_file(&self, i: FileHandle, file: File) -> Option<FileHandle> {
        let mut files = self.files.lock();
        if i.into() < super::CONTEXT_MAX_FILES {
            while i.into() >= files.len() {
                files.push(None);
            }
            if files[i.into()].is_none() {
                files[i.into()] = Some(file);
                Some(i)
            } else {
                None
            }
        } else {
            None
        }
    }

    /// Remove a file
    // TODO: adjust files vector to smaller size if possible
    pub fn remove_file(&self, i: FileHandle) -> Option<File> {
        let mut files = self.files.lock();
        if i.into() < files.len() {
            files[i.into()].take()
        } else {
            None
        }
    }
}
<commit_msg>Strip extra slashes from path<commit_after>use alloc::arc::Arc;
use alloc::boxed::Box;
use collections::{BTreeMap, Vec, VecDeque};
use core::mem;
use spin::Mutex;

use context::arch;
use context::file::File;
use context::memory::{Grant, Memory, SharedMemory, Tls};
use device;
use scheme::{SchemeNamespace, FileHandle};
use syscall::data::{Event, SigAction};
use syscall::flag::SIG_DFL;
use sync::{WaitMap, WaitQueue};

/// Unique identifier for a context (i.e. `pid`).
use ::core::sync::atomic::AtomicUsize;
int_like!(ContextId, AtomicContextId, usize, AtomicUsize);

/// The status of a context - used for scheduling
/// See syscall::process::waitpid and the sync module for examples of usage
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Status {
    Runnable,
    Blocked,
    Exited(usize)
}

/// A context, which identifies either a process or a thread
#[derive(Debug)]
pub struct Context {
    /// The ID of this context
    pub id: ContextId,
    /// The group ID of this context
    pub pgid: ContextId,
    /// The ID of the parent context
    pub ppid: ContextId,
    /// The real user id
    pub ruid: u32,
    /// The real group id
    pub rgid: u32,
    /// The real namespace id
    pub rns: SchemeNamespace,
    /// The effective user id
    pub euid: u32,
    /// The effective group id
    pub egid: u32,
    /// The effective namespace id
    pub ens: SchemeNamespace,
    /// Status of context
    pub status: Status,
    /// Context running or not
    pub running: bool,
    /// CPU ID, if locked
    pub cpu_id: Option<usize>,
    /// Context is halting parent
    pub vfork: bool,
    /// Context is being waited on
    pub waitpid: Arc<WaitMap<ContextId, usize>>,
    /// Context should handle pending signals
    pub pending: VecDeque<u8>,
    /// Context should wake up at specified time
    pub wake: Option<(u64, u64)>,
    /// The architecture specific context
    pub arch: arch::Context,
    /// Kernel FX - used to store SIMD and FPU registers on context switch
    pub kfx: Option<Box<[u8]>>,
    /// Kernel stack
    pub kstack: Option<Box<[u8]>>,
    /// Kernel signal backup
    pub ksig: Option<(arch::Context, Option<Box<[u8]>>, Option<Box<[u8]>>)>,
    /// Restore ksig context on next switch
    pub ksig_restore: bool,
    /// Executable image
    pub image: Vec<SharedMemory>,
    /// User heap
    pub heap: Option<SharedMemory>,
    /// User stack
    pub stack: Option<Memory>,
    /// User signal stack
    pub sigstack: Option<Memory>,
    /// User Thread local storage
    pub tls: Option<Tls>,
    /// User grants
    pub grants: Arc<Mutex<Vec<Grant>>>,
    /// The name of the context
    pub name: Arc<Mutex<Vec<u8>>>,
    /// The current working directory
    pub cwd: Arc<Mutex<Vec<u8>>>,
    /// Kernel events
    pub events: Arc<WaitQueue<Event>>,
    /// The process environment
    pub env: Arc<Mutex<BTreeMap<Box<[u8]>, Arc<Mutex<Vec<u8>>>>>>,
    /// The open files in the scheme
    pub files: Arc<Mutex<Vec<Option<File>>>>,
    /// Singal actions
    pub actions: Arc<Mutex<Vec<(SigAction, usize)>>>,
}

impl Context {
    pub fn new(id: ContextId) -> Context {
        Context {
            id: id,
            pgid: id,
            ppid: ContextId::from(0),
            ruid: 0,
            rgid: 0,
            rns: SchemeNamespace::from(0),
            euid: 0,
            egid: 0,
            ens: SchemeNamespace::from(0),
            status: Status::Blocked,
            running: false,
            cpu_id: None,
            vfork: false,
            waitpid: Arc::new(WaitMap::new()),
            pending: VecDeque::new(),
            wake: None,
            arch: arch::Context::new(),
            kfx: None,
            kstack: None,
            ksig: None,
            ksig_restore: false,
            image: Vec::new(),
            heap: None,
            stack: None,
            sigstack: None,
            tls: None,
            grants: Arc::new(Mutex::new(Vec::new())),
            name: Arc::new(Mutex::new(Vec::new())),
            cwd: Arc::new(Mutex::new(Vec::new())),
            events: Arc::new(WaitQueue::new()),
            env: Arc::new(Mutex::new(BTreeMap::new())),
            files: Arc::new(Mutex::new(Vec::new())),
            actions: Arc::new(Mutex::new(vec![(
                SigAction {
                    sa_handler: unsafe { mem::transmute(SIG_DFL) },
                    sa_mask: [0; 2],
                    sa_flags: 0,
                },
                0
            ); 128])),
        }
    }

    /// Make a relative path absolute
    /// Given a cwd of "scheme:/path"
    /// This function will turn "foo" into "scheme:/path/foo"
    /// "/foo" will turn into "scheme:/foo"
    /// "bar:/foo" will be used directly, as it is already absolute
    pub fn canonicalize(&self, path: &[u8]) -> Vec<u8> {
        let mut canon = if path.iter().position(|&b| b == b':').is_none() {
            let cwd = self.cwd.lock();

            let mut canon = if !path.starts_with(b"/") {
                let mut c = cwd.clone();
                if ! c.ends_with(b"/") {
                    c.push(b'/');
                }
                c
            } else {
                cwd[..cwd.iter().position(|&b| b == b':').map_or(1, |i| i + 1)].to_vec()
            };

            canon.extend_from_slice(&path);
            canon
        } else {
            path.to_vec()
        };

        // NOTE: assumes the scheme does not include anything like "../" or "./"
        let mut result = {
            let parts = canon.split(|&c| c == b'/')
                .filter(|&part| part != b".")
                .rev()
                .scan(0, |nskip, part| {
                    if part == b"." {
                        Some(None)
                    } else if part == b".." {
                        *nskip += 1;
                        Some(None)
                    } else {
                        if *nskip > 0 {
                            *nskip -= 1;
                            Some(None)
                        } else {
                            Some(Some(part))
                        }
                    }
                })
                .filter_map(|x| x)
                .filter(|x| !x.is_empty())
                .collect::<Vec<_>>();
            parts
                .iter()
                .rev()
                .fold(Vec::new(), |mut vec, &part| {
                    vec.extend_from_slice(part);
                    vec.push(b'/');
                    vec
                })
        };
        result.pop(); // remove extra '/'

        // replace with the root of the scheme if it's empty
        if result.len() == 0 {
            let pos = canon.iter()
                            .position(|&b| b == b':')
                            .map_or(canon.len(), |p| p + 1);
            canon.truncate(pos);
            canon
        } else {
            result
        }
    }

    /// Block the context, and return true if it was runnable before being blocked
    pub fn block(&mut self) -> bool {
        if self.status == Status::Runnable {
            self.status = Status::Blocked;
            true
        } else {
            false
        }
    }

    /// Unblock context, and return true if it was blocked before being marked runnable
    pub fn unblock(&mut self) -> bool {
        if self.status == Status::Blocked {
            self.status = Status::Runnable;
            if let Some(cpu_id) = self.cpu_id {
                if cpu_id != ::cpu_id() {
                    // Send IPI if not on current CPU
                    // TODO: Make this more architecture independent
                    unsafe { device::local_apic::LOCAL_APIC.ipi(cpu_id) };
                }
            }
            true
        } else {
            false
        }
    }

    /// Add a file to the lowest available slot.
    /// Return the file descriptor number or None if no slot was found
    pub fn add_file(&self, file: File) -> Option<FileHandle> {
        let mut files = self.files.lock();
        for (i, mut file_option) in files.iter_mut().enumerate() {
            if file_option.is_none() {
                *file_option = Some(file);
                return Some(FileHandle::from(i));
            }
        }
        let len = files.len();
        if len < super::CONTEXT_MAX_FILES {
            files.push(Some(file));
            Some(FileHandle::from(len))
        } else {
            None
        }
    }

    /// Get a file
    pub fn get_file(&self, i: FileHandle) -> Option<File> {
        let files = self.files.lock();
        if i.into() < files.len() {
            files[i.into()].clone()
        } else {
            None
        }
    }

    /// Insert a file with a specific handle number. This is used by dup2
    /// Return the file descriptor number or None if the slot was not empty, or i was invalid
    pub fn insert_file(&self, i: FileHandle, file: File) -> Option<FileHandle> {
        let mut files = self.files.lock();
        if i.into() < super::CONTEXT_MAX_FILES {
            while i.into() >= files.len() {
                files.push(None);
            }
            if files[i.into()].is_none() {
                files[i.into()] = Some(file);
                Some(i)
            } else {
                None
            }
        } else {
            None
        }
    }

    /// Remove a file
    // TODO: adjust files vector to smaller size if possible
    pub fn remove_file(&self, i: FileHandle) -> Option<File> {
        let mut files = self.files.lock();
        if i.into() < files.len() {
            files[i.into()].take()
        } else {
            None
        }
    }
}
<|endoftext|>


<commit_before>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/gui/kernel_log.rs
/// Kernel log output (and debug terminal)
//
// Manages a set of windows giving a view into the kernel
// - Kernel log (current) : Contains the most recent kernel log messages
// - Logo
// - TODO: Kernel log (history) : A searchable/filterable/scrollable kernel log
// - TODO: Console

use prelude::*;

use super::windows::{WindowGroupHandle,WindowHandle};
use super::{Colour,Dims,Pos,Rect};
use core::fmt;

// Bitmap font used by this module is in another file
include!("font_cp437_8x16.rs");

// Raw bitmap logo (already encoded with dims and as a rust array)
include!("../../../Graphics/logo.rs");

struct KernelLog
{
	_wgh: WindowGroupHandle,
	wh: WindowHandle,
	_logo_wh: WindowHandle,
	cur_line: u32,

	#[allow(dead_code)]
	buffer_handle: super::windows::BufHandle,
}

/// Character position
#[derive(Copy,Clone,Debug)]
struct CharPos(u32,u32);

struct LogWriter
{
	log: ::sync::mutex::HeldLazyMutex<'static,KernelLog>,
	pos: CharPos,
	colour: Colour,
}

/// Trait to provde 'is_combining', used by render code
trait UnicodeCombining
{
	fn is_combining(&self) -> bool;
}

const C_CELL_DIMS: Dims = Dims { w: 8, h: 16 };
static S_KERNEL_LOG: ::sync::mutex::LazyMutex<KernelLog> = lazymutex_init!();

#[doc(hidden)]
pub fn init()
{
	// Create window (and structure)
	S_KERNEL_LOG.init(|| KernelLog::new());

	//super::register_dims_update(|| S_KERNEL_LOG.lock().update_dims());
	//S_KERNEL_LOG.lock().register_input();

	{
		use core::fmt::Write;
		{
			let mut w = LogWriter::new();
			w.set_colour(Colour::def_green());
			write!(&mut w, "Tifflin Kernel v{} build {}", env!("TK_VERSION"), env!("TK_BUILD")).unwrap();
		}
		{
			let mut w = LogWriter::new();
			w.set_colour(Colour::def_yellow());
			write!(&mut w, "> Git state : {}, Built with {}", env!("TK_GITSPEC"), env!("RUST_VERSION")).unwrap();
		}
	}

	// Populate kernel logging window with accumulated logs
	// TODO:
	// Register to recieve logs
}

impl KernelLog
{
	fn new() -> KernelLog
	{
		// TODO: Get this limit from the video layer, and update it when required
		//let max_dims = super::get_display_dims( Pos::new(0,0) );
		let max_dims = Dims::new(1280, 1024);

		// Kernel's window group
		let mut wgh = WindowGroupHandle::alloc("Kernel");

		// - Log Window
		let mut wh = wgh.create_window("Kernel Log");
		//wh.set_pos(Pos::new(0,0));
		//wh.resize(max_dims);
		wh.maximise();
		let log_buf_handle = wh.get_buffer();

		// - Fancy logo window
		let dims = Dims::new(S_LOGO_DIMS.0,S_LOGO_DIMS.1);
		let mut logo_wh = wgh.create_window("Logo");
		logo_wh.set_pos(Pos::new(max_dims.w-dims.w, 0));
		logo_wh.resize(dims);
		logo_wh.blit_rect( Rect::new_pd(Pos::new(0,0),dims), &S_LOGO_DATA );

		// > Show windows in reverse render order
		wh.show();
		logo_wh.show();

		// Return struct populated with above handles
		KernelLog {
			_wgh: wgh,
			wh: wh,
			_logo_wh: logo_wh,
			cur_line: 0,
			buffer_handle: log_buf_handle,
		}
	}

	/// Scroll the display up a step, revealing a new line
	fn scroll_up(&mut self)
	{
		self.cur_line += 1;
	}

	/// Write a string to the log display (at the given character position)
	fn write_text(&mut self, mut pos: CharPos, colour: Colour, text: &str) -> CharPos
	{
		for c in text.chars()
		{
			if self.putc(pos, colour, c)
			{
				pos = pos.next();
			}
		}
		pos
	}
	/// Flush changes
	fn flush(&mut self)
	{
		// Poke the WM and tell it to reblit us
		self.wh.redraw();
	}

	/// Writes a single codepoint to the display
	///
	/// Returns true if the character caused a cell change (i.e. it wasn't a combining character)
	fn putc(&mut self, pos: CharPos, colour: Colour, c: char) -> bool
	{
		// If the character was a combining AND it's not at the start of a line,
		// render atop the previous cell
		if c.is_combining() && pos.col() > 0 {
			self.render_char(pos.prev(), colour, c);
			false
		}
		// Otherwise, wipe the cell and render into it
		else {
			self.clear_cell(pos);
			self.render_char(pos, colour, c);
			true
		}
	}

	// Low-level rendering
	/// Clear a character cell
	fn clear_cell(&mut self, pos: CharPos)
	{
		self.wh.fill_rect( Rect{ pos : pos.to_pixels(), dims: C_CELL_DIMS }, Colour::def_black());
	}
	/// Actually does the rendering
	fn render_char(&mut self, pos: CharPos, colour: Colour, cp: char)
	{
		let idx = match cp as u32
			{
			32 ... 0x7E => cp as u8,
			_ => b'?',
			};
		//log_trace!("KernelLog::render_char({:?}, {:?}, '{}') idx={}", pos, colour, cp, idx);

		let bitmap = &S_FONTDATA[idx as usize];

		// Actual render!
		let Pos { x: bx, y: by } = pos.to_pixels();
		for row in (0 .. 16)
		{
			let byte = &bitmap[row as usize];
			let r = self.buffer_handle.scanline_rgn_mut(by as usize + row, bx as usize, 8);
			for col in (0usize .. 8)
			{
				if (byte >> 7-col) & 1 != 0 {
					r[col] = colour.as_argb32();
				}
			}
		}
	}
}

impl CharPos
{
	fn col(&self) -> u32 { self.1 }
	fn next(self) -> CharPos { CharPos(self.0, self.1+1) }
	fn prev(self) -> CharPos { CharPos(self.0, self.1-1) }
	fn to_pixels(self) -> Pos {
		Pos::new( (self.1 * C_CELL_DIMS.w) as u32, (self.0 * C_CELL_DIMS.h) as u32 )
	}
}

impl LogWriter
{
	pub fn new() -> LogWriter
	{
		let mut log = S_KERNEL_LOG.lock();
		log.scroll_up();
		LogWriter {
			pos: CharPos(log.cur_line-1,0),
			colour: Colour::def_white(),
			log: log,
		}
	}

	pub fn set_colour(&mut self, colour: Colour)
	{
		self.colour = colour;
	}
}
impl fmt::Write for LogWriter
{
	fn write_str(&mut self, s: &str) -> fmt::Result
	{
		self.pos = self.log.write_text(self.pos, self.colour, s);
		Ok( () )
	}
}
impl ::core::ops::Drop for LogWriter
{
	fn drop(&mut self)
	{
		self.log.flush();
	}
}

impl UnicodeCombining for char
{
	fn is_combining(&self) -> bool
	{
		match *self as u32
		{
		// Ranges from wikipedia:Combining_Character
		0x0300 ... 0x036F => true,
		0x1AB0 ... 0x1AFF => true,
		0x1DC0 ... 0x1DFF => true,
		0x20D0 ... 0x20FF => true,
		0xFE20 ... 0xFE2F => true,
		_ => false,
		}
	}
}

<commit_msg>Kernel/Core - Get screen dimensions in kernel log<commit_after>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/gui/kernel_log.rs
/// Kernel log output (and debug terminal)
//
// Manages a set of windows giving a view into the kernel
// - Kernel log (current) : Contains the most recent kernel log messages
// - Logo
// - TODO: Kernel log (history) : A searchable/filterable/scrollable kernel log
// - TODO: Console

use prelude::*;

use super::windows::{WindowGroupHandle,WindowHandle};
use super::{Colour,Dims,Pos,Rect};
use core::fmt;

// Bitmap font used by this module is in another file
include!("font_cp437_8x16.rs");

// Raw bitmap logo (already encoded with dims and as a rust array)
include!("../../../Graphics/logo.rs");

struct KernelLog
{
	_wgh: WindowGroupHandle,
	wh: WindowHandle,
	_logo_wh: WindowHandle,
	cur_line: u32,

	#[allow(dead_code)]
	buffer_handle: super::windows::BufHandle,
}

/// Character position
#[derive(Copy,Clone,Debug)]
struct CharPos(u32,u32);

struct LogWriter
{
	log: ::sync::mutex::HeldLazyMutex<'static,KernelLog>,
	pos: CharPos,
	colour: Colour,
}

/// Trait to provde 'is_combining', used by render code
trait UnicodeCombining
{
	fn is_combining(&self) -> bool;
}

const C_CELL_DIMS: Dims = Dims { w: 8, h: 16 };
static S_KERNEL_LOG: ::sync::mutex::LazyMutex<KernelLog> = lazymutex_init!();

#[doc(hidden)]
pub fn init()
{
	// Create window (and structure)
	S_KERNEL_LOG.init(|| KernelLog::new());

	//super::register_dims_update(|| S_KERNEL_LOG.lock().update_dims());
	//S_KERNEL_LOG.lock().register_input();

	{
		use core::fmt::Write;
		{
			let mut w = LogWriter::new();
			w.set_colour(Colour::def_green());
			write!(&mut w, "Tifflin Kernel v{} build {}", env!("TK_VERSION"), env!("TK_BUILD")).unwrap();
		}
		{
			let mut w = LogWriter::new();
			w.set_colour(Colour::def_yellow());
			write!(&mut w, "> Git state : {}, Built with {}", env!("TK_GITSPEC"), env!("RUST_VERSION")).unwrap();
		}
	}

	// Populate kernel logging window with accumulated logs
	// TODO:
	// Register to recieve logs
}

impl KernelLog
{
	fn new() -> KernelLog
	{
		// TODO: Register to somehow be informed when dimensions change
        // - Is this particular call bad for bypassing the GUI? Or is this acceptable
		let max_dims = ::metadevs::video::get_display_for_pos( Pos::new(0,0) ).expect("No display at (0,0)").dims();

		// Kernel's window group
		let mut wgh = WindowGroupHandle::alloc("Kernel");

		// - Log Window
		let mut wh = wgh.create_window("Kernel Log");
		//wh.set_pos(Pos::new(0,0));
		//wh.resize(max_dims);
		wh.maximise();
		let log_buf_handle = wh.get_buffer();

		// - Fancy logo window
		let dims = Dims::new(S_LOGO_DIMS.0,S_LOGO_DIMS.1);
		let mut logo_wh = wgh.create_window("Logo");
		logo_wh.set_pos(Pos::new(max_dims.w-dims.w, 0));
		logo_wh.resize(dims);
		logo_wh.blit_rect( Rect::new_pd(Pos::new(0,0),dims), &S_LOGO_DATA );

		// > Show windows in reverse render order
		wh.show();
		logo_wh.show();

		// Return struct populated with above handles
		KernelLog {
			_wgh: wgh,
			wh: wh,
			_logo_wh: logo_wh,
			cur_line: 0,
			buffer_handle: log_buf_handle,
		}
	}

	/// Scroll the display up a step, revealing a new line
	fn scroll_up(&mut self)
	{
		self.cur_line += 1;
	}

	/// Write a string to the log display (at the given character position)
	fn write_text(&mut self, mut pos: CharPos, colour: Colour, text: &str) -> CharPos
	{
		for c in text.chars()
		{
			if self.putc(pos, colour, c)
			{
				pos = pos.next();
			}
		}
		pos
	}
	/// Flush changes
	fn flush(&mut self)
	{
		// Poke the WM and tell it to reblit us
		self.wh.redraw();
	}

	/// Writes a single codepoint to the display
	///
	/// Returns true if the character caused a cell change (i.e. it wasn't a combining character)
	fn putc(&mut self, pos: CharPos, colour: Colour, c: char) -> bool
	{
		// If the character was a combining AND it's not at the start of a line,
		// render atop the previous cell
		if c.is_combining() && pos.col() > 0 {
			self.render_char(pos.prev(), colour, c);
			false
		}
		// Otherwise, wipe the cell and render into it
		else {
			self.clear_cell(pos);
			self.render_char(pos, colour, c);
			true
		}
	}

	// Low-level rendering
	/// Clear a character cell
	fn clear_cell(&mut self, pos: CharPos)
	{
		self.wh.fill_rect( Rect{ pos : pos.to_pixels(), dims: C_CELL_DIMS }, Colour::def_black());
	}
	/// Actually does the rendering
	fn render_char(&mut self, pos: CharPos, colour: Colour, cp: char)
	{
		let idx = match cp as u32
			{
			32 ... 0x7E => cp as u8,
			_ => b'?',
			};
		//log_trace!("KernelLog::render_char({:?}, {:?}, '{}') idx={}", pos, colour, cp, idx);

		let bitmap = &S_FONTDATA[idx as usize];

		// Actual render!
		let Pos { x: bx, y: by } = pos.to_pixels();
		for row in (0 .. 16)
		{
			let byte = &bitmap[row as usize];
			let r = self.buffer_handle.scanline_rgn_mut(by as usize + row, bx as usize, 8);
			for col in (0usize .. 8)
			{
				if (byte >> 7-col) & 1 != 0 {
					r[col] = colour.as_argb32();
				}
			}
		}
	}
}

impl CharPos
{
	fn col(&self) -> u32 { self.1 }
	fn next(self) -> CharPos { CharPos(self.0, self.1+1) }
	fn prev(self) -> CharPos { CharPos(self.0, self.1-1) }
	fn to_pixels(self) -> Pos {
		Pos::new( (self.1 * C_CELL_DIMS.w) as u32, (self.0 * C_CELL_DIMS.h) as u32 )
	}
}

impl LogWriter
{
	pub fn new() -> LogWriter
	{
		let mut log = S_KERNEL_LOG.lock();
		log.scroll_up();
		LogWriter {
			pos: CharPos(log.cur_line-1,0),
			colour: Colour::def_white(),
			log: log,
		}
	}

	pub fn set_colour(&mut self, colour: Colour)
	{
		self.colour = colour;
	}
}
impl fmt::Write for LogWriter
{
	fn write_str(&mut self, s: &str) -> fmt::Result
	{
		self.pos = self.log.write_text(self.pos, self.colour, s);
		Ok( () )
	}
}
impl ::core::ops::Drop for LogWriter
{
	fn drop(&mut self)
	{
		self.log.flush();
	}
}

impl UnicodeCombining for char
{
	fn is_combining(&self) -> bool
	{
		match *self as u32
		{
		// Ranges from wikipedia:Combining_Character
		0x0300 ... 0x036F => true,
		0x1AB0 ... 0x1AFF => true,
		0x1DC0 ... 0x1DFF => true,
		0x20D0 ... 0x20FF => true,
		0xFE20 ... 0xFE2F => true,
		_ => false,
		}
	}
}

<|endoftext|>


<commit_before>use std::fmt;
use std::io::{Result, Write};

use super::nodes::*;

pub struct Printer<O: Write> {
    output: O,
    indent: u8,
}

macro_rules! writeln {
    ($self: ident, $($arg:tt)*) => (
        $self.writeln(format_args!($($arg)*))
    );
}

impl<O: Write> Printer<O> {
    pub fn new(output: O) -> Self {
        Self { output, indent: 0 }
    }

    fn indented<F: FnOnce(&mut Self) -> Result<()>>(&mut self, inner: F) -> Result<()> {
        self.indent += 2;
        let result = inner(self);
        self.indent -= 2;
        result
    }

    pub fn print_module(&mut self, root: Module) -> Result<()> {
        if let Some(bindings) = root.get_bindings() {
            writeln!(self, "bindings [")?;
            self.indented(|printer| {
                for capture in bindings.iter() {
                    writeln!(printer, "{}", capture)?;
                }
                Ok(())
            })?;
            writeln!(self, "]")?;
        }
        if let Some(parent_bindings) = root.get_parent_bindings() {
            writeln!(self, "parent_bindings [")?;
            self.indented(|printer| {
                for capture in parent_bindings.iter() {
                    writeln!(printer, "{}", capture)?;
                }
                Ok(())
            })?;
            writeln!(self, "]")?;
        }
        for node in root.nodes {
            self.print_node(node)?
        }
        Ok(())
    }

    fn print_node(&mut self, node: Node) -> Result<()> {
        match node {
            Node::Assignment(assignment) => self.print_assignment(assignment),
            Node::Block(block) => self.print_block(block),
            Node::Function(function) => self.print_function(function),
            Node::Identifier(identifier) => self.print_identifier(identifier),
            Node::Integer(integer) => self.print_integer(integer),
            Node::PostfixCall(postfix_call) => self.print_postfix_call(postfix_call),
            Node::Return(ret) => self.print_return(ret),
            Node::Var(var) => self.print_var(var),
            _ => writeln!(self, "{:?}", node),
        }
    }

    fn print_assignment(&mut self, assignment: Assignment) -> Result<()> {
        writeln!(self, "Assignment(")?;
        self.indented(|printer| {
            printer.print_node(*assignment.lhs)?;
            printer.print_node(*assignment.rhs)
        })?;
        writeln!(self, ")")
    }

    fn print_block(&mut self, block: Block) -> Result<()> {
        writeln!(self, "Block(")?;
        self.indented(|printer| {
            for node in block.nodes {
                printer.print_node(node)?;
            }
            Ok(())
        })?;
        writeln!(self, ")")
    }

    fn print_function(&mut self, function: Function) -> Result<()> {
        writeln!(
            self,
            "Function({}",
            function.name.clone().unwrap_or("".to_string()),
        )?;
        if let Some(bindings) = function.get_bindings() {
            self.indented(|printer| {
                writeln!(printer, "bindings [")?;
                printer.indented(|bindings_printer| {
                    for capture in bindings.iter() {
                        writeln!(bindings_printer, "{}", capture)?;
                    }
                    Ok(())
                })?;
                writeln!(printer, "]")
            })?;
        }
        if let Some(parent_bindings) = function.get_parent_bindings() {
            self.indented(|printer| {
                writeln!(printer, "parent_bindings [")?;
                printer.indented(|parent_bindings_printer| {
                    for capture in parent_bindings.iter() {
                        writeln!(parent_bindings_printer, "{}", capture)?;
                    }
                    Ok(())
                })?;
                writeln!(printer, "]")
            })?;
        }
        self.indented(|printer| printer.print_node(*function.body))?;
        writeln!(self, ")")
    }

    fn print_identifier(&mut self, identifier: Identifier) -> Result<()> {
        writeln!(self, "Identifier({})", identifier.value)
    }

    fn print_integer(&mut self, integer: Integer) -> Result<()> {
        writeln!(self, "Integer({})", integer.value)
    }

    fn print_postfix_call(&mut self, postfix_call: PostfixCall) -> Result<()> {
        writeln!(self, "PostfixCall(")?;
        self.indented(|printer| {
            printer.print_node(*postfix_call.clone().target)?;
            if postfix_call.arguments.is_empty() {
                return writeln!(printer, "[]");
            }
            writeln!(printer, "[")?;
            printer.indented(|arguments_printer| {
                for argument in postfix_call.arguments {
                    arguments_printer.print_node(argument)?;
                }
                Ok(())
            })?;
            writeln!(printer, "]")
        })?;
        writeln!(self, ")")
    }

    fn print_return(&mut self, ret: Return) -> Result<()> {
        if let Some(rhs) = ret.rhs {
            writeln!(self, "Return(")?;
            self.indented(|printer| printer.print_node(*rhs))?;
            writeln!(self, ")")
        } else {
            writeln!(self, "Return()")
        }
    }

    fn print_var(&mut self, var: Var) -> Result<()> {
        if let Some(rhs) = var.rhs {
            writeln!(self, "Var({}", var.lhs.value)?;
            self.indented(|printer| printer.print_node(*rhs))?;
            writeln!(self, ")")
        } else {
            writeln!(self, "Var({})", var.lhs.value)
        }
    }

    fn writeln(&mut self, args: fmt::Arguments) -> Result<()> {
        let indented = " ".repeat(self.indent as usize);
        self.output
            .write(indented.as_bytes())
            .and_then(|_| self.output.write_fmt(args))
            .and_then(|_| self.output.write("\n".as_bytes()))
            .map(|_| ())
    }
}
<commit_msg>Remove more old stuff<commit_after>use std::fmt;
use std::io::{Result, Write};

use super::nodes::*;

pub struct Printer<O: Write> {
    output: O,
    indent: u8,
}

macro_rules! writeln {
    ($self: ident, $($arg:tt)*) => (
        $self.writeln(format_args!($($arg)*))
    );
}

impl<O: Write> Printer<O> {
    pub fn new(output: O) -> Self {
        Self { output, indent: 0 }
    }

    fn indented<F: FnOnce(&mut Self) -> Result<()>>(&mut self, inner: F) -> Result<()> {
        self.indent += 2;
        let result = inner(self);
        self.indent -= 2;
        result
    }

    pub fn print_module(&mut self, module: Module) -> Result<()> {
        for node in module.nodes {
            self.print_node(node)?
        }
        Ok(())
    }

    fn print_node(&mut self, node: Node) -> Result<()> {
        match node {
            Node::Assignment(assignment) => self.print_assignment(assignment),
            Node::Block(block) => self.print_block(block),
            Node::Function(function) => self.print_function(function),
            Node::Identifier(identifier) => self.print_identifier(identifier),
            Node::Integer(integer) => self.print_integer(integer),
            Node::PostfixCall(postfix_call) => self.print_postfix_call(postfix_call),
            Node::Return(ret) => self.print_return(ret),
            Node::Var(var) => self.print_var(var),
            _ => writeln!(self, "{:?}", node),
        }
    }

    fn print_assignment(&mut self, assignment: Assignment) -> Result<()> {
        writeln!(self, "Assignment(")?;
        self.indented(|printer| {
            printer.print_node(*assignment.lhs)?;
            printer.print_node(*assignment.rhs)
        })?;
        writeln!(self, ")")
    }

    fn print_block(&mut self, block: Block) -> Result<()> {
        writeln!(self, "Block(")?;
        self.indented(|printer| {
            for node in block.nodes {
                printer.print_node(node)?;
            }
            Ok(())
        })?;
        writeln!(self, ")")
    }

    fn print_function(&mut self, function: Function) -> Result<()> {
        writeln!(self, "Function({}", function.name)?;
        self.indented(|printer| printer.print_block(function.block))?;
        writeln!(self, ")")
    }

    fn print_identifier(&mut self, identifier: Identifier) -> Result<()> {
        writeln!(self, "Identifier({})", identifier.value)
    }

    fn print_integer(&mut self, integer: Integer) -> Result<()> {
        writeln!(self, "Integer({})", integer.value)
    }

    fn print_postfix_call(&mut self, postfix_call: PostfixCall) -> Result<()> {
        writeln!(self, "PostfixCall(")?;
        self.indented(|printer| {
            printer.print_node(*postfix_call.clone().target)?;
            if postfix_call.arguments.is_empty() {
                return writeln!(printer, "[]");
            }
            writeln!(printer, "[")?;
            printer.indented(|arguments_printer| {
                for argument in postfix_call.arguments {
                    arguments_printer.print_node(argument)?;
                }
                Ok(())
            })?;
            writeln!(printer, "]")
        })?;
        writeln!(self, ")")
    }

    fn print_return(&mut self, ret: Return) -> Result<()> {
        if let Some(rhs) = ret.rhs {
            writeln!(self, "Return(")?;
            self.indented(|printer| printer.print_node(*rhs))?;
            writeln!(self, ")")
        } else {
            writeln!(self, "Return()")
        }
    }

    fn print_var(&mut self, var: Var) -> Result<()> {
        if let Some(rhs) = var.rhs {
            writeln!(self, "Var({}", var.lhs.value)?;
            self.indented(|printer| printer.print_node(*rhs))?;
            writeln!(self, ")")
        } else {
            writeln!(self, "Var({})", var.lhs.value)
        }
    }

    fn writeln(&mut self, args: fmt::Arguments) -> Result<()> {
        let indented = " ".repeat(self.indent as usize);
        self.output
            .write(indented.as_bytes())
            .and_then(|_| self.output.write_fmt(args))
            .and_then(|_| self.output.write("\n".as_bytes()))
            .map(|_| ())
    }
}
<|endoftext|>


<commit_before>use hyper::Client;
use hubcaps::{Github, ReleaseOptions, Credentials};
use error::Error;
use super::USERAGENT;
use config::Config;

pub fn can_release(config: &Config) -> bool {
    let repo = &config.repository;
    match repo.find_remote("origin") {
        Ok(remote) => {
            let url = match remote.url() {
                Some(u) => u,
                None => return false
            };
            is_github_url(url)
        },
        Err(_) => false
    }
}

pub fn is_github_url(url: &str) -> bool {
    url.contains("github.com")
}

pub fn release(config: &Config, tag_name: &str, tag_message: &str) -> Result<(), Error> {
    let user      = &config.user.as_ref().unwrap()[..];
    let repo_name = &config.repository_name.as_ref().unwrap()[..];
    let branch    = &config.branch[..];
    let token     = config.gh_token.as_ref().unwrap();

    let client = Client::new();
    let credentials = Credentials::Token(token.to_owned());
    let github = Github::new(USERAGENT, client, credentials);

    let opts = ReleaseOptions::builder(tag_name)
        .name(tag_name)
        .body(tag_message)
        .commitish(branch)
        .draft(false)
        .prerelease(false)
        .build();

    let repo = github.repo(user, repo_name);
    let release = repo.releases();

    release
        .create(&opts)
        .map(|_| ())
        .map_err(Error::from)
}
<commit_msg>Use module from different path<commit_after>use hyper::Client;
use hubcaps::{Github, Credentials};
use hubcaps::releases::ReleaseOptions;
use error::Error;
use super::USERAGENT;
use config::Config;

pub fn can_release(config: &Config) -> bool {
    let repo = &config.repository;
    match repo.find_remote("origin") {
        Ok(remote) => {
            let url = match remote.url() {
                Some(u) => u,
                None => return false
            };
            is_github_url(url)
        },
        Err(_) => false
    }
}

pub fn is_github_url(url: &str) -> bool {
    url.contains("github.com")
}

pub fn release(config: &Config, tag_name: &str, tag_message: &str) -> Result<(), Error> {
    let user      = &config.user.as_ref().unwrap()[..];
    let repo_name = &config.repository_name.as_ref().unwrap()[..];
    let branch    = &config.branch[..];
    let token     = config.gh_token.as_ref().unwrap();

    let client = Client::new();
    let credentials = Credentials::Token(token.to_owned());
    let github = Github::new(USERAGENT, client, credentials);

    let opts = ReleaseOptions::builder(tag_name)
        .name(tag_name)
        .body(tag_message)
        .commitish(branch)
        .draft(false)
        .prerelease(false)
        .build();

    let repo = github.repo(user, repo_name);
    let release = repo.releases();

    release
        .create(&opts)
        .map(|_| ())
        .map_err(Error::from)
}
<|endoftext|>


<commit_before>use config;
use config::action::message::MessageActionBuilder;
use conditions::ConditionsBuilder;
use Correlator;
use message::MessageBuilder;

use handlebars::Template;
use uuid::Uuid;
use serde_json::from_str;
use std::rc::Rc;
use std::cell::RefCell;
use std::thread;

use test_utils::correlator::MessageEventHandler;

const JSON_CONFIG: &'static str = r#"
      [
        {
          "name": "CONTEXT_NAME_1",
          "uuid": "185e96da-c00e-454b-b4fe-9d0a14a86335",
          "conditions": {
            "timeout": 100,
            "patterns": [
              "p1",
              "p2",
              "p3"
            ],
            "first_opens": true
          },
          "actions": [
            {
              "message": {
                  "uuid": "uuid1",
                  "message": "message_1"
              }
            }
          ]
        },
        {
          "name": "CONTEXT_NAME_2",
          "uuid": "285e96da-c00e-454b-b4fe-9d0a14a86335",
          "conditions": {
            "timeout": 10000,
            "max_size": 5
          },
          "actions": [
            {
              "message": {
                  "uuid": "uuid1",
                  "message": "message_2"
              }
            },
            {
              "message": {
                  "uuid": "uuid2",
                  "message": "message_2"
              }
            }
          ]
        },
        {
          "name": "CONTEXT_NAME_3",
          "uuid": "385e96da-c00e-454b-b4fe-9d0a14a86335",
          "conditions": {
            "timeout": 100,
            "patterns": [
              "p1"
            ]
          },
          "actions": [
            {
              "message": {
                  "uuid": "uuid2",
                  "message": "message_3"
              }
            }
          ]
        }
      ]
    "#;

#[test]
fn test_given_manually_built_correlator_when_it_closes_a_context_then_the_actions_are_executed() {
    let uuid1 = "1b47ba91-d867-4a8c-9553-a5dfd6ea1274".to_string();
    let uuid2 = "2b47ba91-d867-4a8c-9553-a5dfd6ea1274".to_string();
    let uuid3 = "3b47ba91-d867-4a8c-9553-a5dfd6ea1274".to_string();
    let patterns = vec![
        uuid1.clone(),
        uuid2.clone(),
        uuid3.clone(),
    ];
    let condition = ConditionsBuilder::new(100)
                        .patterns(patterns)
                        .first_opens(true)
                        .last_closes(true)
                        .build();
    let message = Template::compile("message".to_string())
                      .ok()
                      .expect("Failed to compile a handlebars template");
    let actions = vec![MessageActionBuilder::new("uuid", message).build().into()];
    let contexts = vec![
        config::ContextBuilder::new(Uuid::new_v4(), condition.clone()).actions(actions.clone()).build(),
        config::ContextBuilder::new(Uuid::new_v4(), condition.clone()).actions(actions.clone()).build(),
        config::ContextBuilder::new(Uuid::new_v4(), condition.clone()).actions(actions.clone()).build(),
    ];
    let responses = Rc::new(RefCell::new(Vec::new()));
    let message_event_handler = Box::new(MessageEventHandler { responses: responses.clone() });
    let mut correlator = Correlator::new(contexts);
    correlator.register_handler(message_event_handler);
    let _ = correlator.push_message(MessageBuilder::new(&uuid1, "message").build());
    thread::sleep_ms(20);
    let _ = correlator.push_message(MessageBuilder::new(&uuid2, "message").build());
    thread::sleep_ms(80);
    let _ = correlator.push_message(MessageBuilder::new(&uuid3, "message").build());
    let _ = correlator.stop();
    assert_eq!(3, responses.borrow().len());
}

#[test]
fn test_given_correlator_when_it_is_built_from_json_then_we_get_the_expected_correlator() {
    let result = from_str::<Vec<config::Context>>(JSON_CONFIG);
    let expected_name = "CONTEXT_NAME_1".to_string();
    let expected_uuid = "185e96da-c00e-454b-b4fe-9d0a14a86335".to_string();
    println!("{:?}", &result);
    let mut contexts = result.ok().expect("Failed to deserialize a config::Context from JSON");
    for i in &contexts {
        assert_eq!(true, i.name.is_some());
    }
    let context = contexts.remove(0);
    assert_eq!(Some(&expected_name), context.name.as_ref());
    assert_eq!(&expected_uuid, &context.uuid.to_hyphenated_string());
}

#[test]
fn test_given_correlator_when_it_is_built_from_json_then_it_produces_the_expected_number_of_messages
    () {
    let uuid1 = "1b47ba91-d867-4a8c-9553-a5dfd6ea1274".to_string();
    let uuid2 = "2b47ba91-d867-4a8c-9553-a5dfd6ea1274".to_string();
    let uuid3 = "3b47ba91-d867-4a8c-9553-a5dfd6ea1274".to_string();
    let result = from_str::<Vec<config::Context>>(JSON_CONFIG);
    println!("{:?}", &result);
    let contexts = result.ok().expect("Failed to deserialize a config::Context from JSON");
    let responses = Rc::new(RefCell::new(Vec::new()));
    let message_event_handler = Box::new(MessageEventHandler { responses: responses.clone() });
    let mut correlator = Correlator::new(contexts);
    correlator.register_handler(message_event_handler);
    let _ = correlator.push_message(MessageBuilder::new(&uuid1, "message")
                                        .name(Some("p1"))
                                        .build());
    thread::sleep_ms(20);
    let _ = correlator.push_message(MessageBuilder::new(&uuid2, "message")
                                        .name(Some("p2"))
                                        .build());
    let _ = correlator.push_message(MessageBuilder::new(&uuid3, "message")
                                        .name(Some("p3"))
                                        .build());
    let _ = correlator.push_message(MessageBuilder::new(&uuid3, "message")
                                        .name(Some("p3"))
                                        .build());
    let _ = correlator.push_message(MessageBuilder::new(&uuid3, "message")
                                        .name(Some("p3"))
                                        .build());
    let _ = correlator.push_message(MessageBuilder::new(&uuid3, "message")
                                        .name(Some("p3"))
                                        .build());
    let _ = correlator.push_message(MessageBuilder::new(&uuid3, "message")
                                        .name(Some("p3"))
                                        .build());
    thread::sleep_ms(200);
    let _ = correlator.stop();
    println!("{:?}", &responses.borrow());
    assert_eq!(4, responses.borrow().len());
}
<commit_msg>correlator: test the new 'when' configuration option<commit_after>use config;
use config::action::message::MessageActionBuilder;
use conditions::ConditionsBuilder;
use Correlator;
use message::MessageBuilder;

use handlebars::Template;
use uuid::Uuid;
use serde_json::from_str;
use std::rc::Rc;
use std::cell::RefCell;
use std::thread;

use test_utils::correlator::MessageEventHandler;

const JSON_CONFIG: &'static str = r#"
      [
        {
          "name": "CONTEXT_NAME_1",
          "uuid": "185e96da-c00e-454b-b4fe-9d0a14a86335",
          "conditions": {
            "timeout": 100,
            "patterns": [
              "p1",
              "p2",
              "p3"
            ],
            "first_opens": true
          },
          "actions": [
            {
              "message": {
                  "uuid": "uuid1",
                  "when": {
                    "on_opened": "true",
                    "on_closed": "true"
                  },
                  "message": "message_1"
              }
            }
          ]
        },
        {
          "name": "CONTEXT_NAME_2",
          "uuid": "285e96da-c00e-454b-b4fe-9d0a14a86335",
          "conditions": {
            "timeout": 10000,
            "max_size": 5
          },
          "actions": [
            {
              "message": {
                  "uuid": "uuid1",
                  "message": "message_2"
              }
            },
            {
              "message": {
                  "uuid": "uuid2",
                  "message": "message_2"
              }
            }
          ]
        },
        {
          "name": "CONTEXT_NAME_3",
          "uuid": "385e96da-c00e-454b-b4fe-9d0a14a86335",
          "conditions": {
            "timeout": 100,
            "patterns": [
              "p1"
            ]
          },
          "actions": [
            {
              "message": {
                  "uuid": "uuid2",
                  "message": "message_3"
              }
            }
          ]
        }
      ]
    "#;

#[test]
fn test_given_manually_built_correlator_when_it_closes_a_context_then_the_actions_are_executed() {
    let uuid1 = "1b47ba91-d867-4a8c-9553-a5dfd6ea1274".to_string();
    let uuid2 = "2b47ba91-d867-4a8c-9553-a5dfd6ea1274".to_string();
    let uuid3 = "3b47ba91-d867-4a8c-9553-a5dfd6ea1274".to_string();
    let patterns = vec![
        uuid1.clone(),
        uuid2.clone(),
        uuid3.clone(),
    ];
    let condition = ConditionsBuilder::new(100)
                        .patterns(patterns)
                        .first_opens(true)
                        .last_closes(true)
                        .build();
    let message = Template::compile("message".to_string())
                      .ok()
                      .expect("Failed to compile a handlebars template");
    let actions = vec![MessageActionBuilder::new("uuid", message).build().into()];
    let contexts = vec![
        config::ContextBuilder::new(Uuid::new_v4(), condition.clone()).actions(actions.clone()).build(),
        config::ContextBuilder::new(Uuid::new_v4(), condition.clone()).actions(actions.clone()).build(),
        config::ContextBuilder::new(Uuid::new_v4(), condition.clone()).actions(actions.clone()).build(),
    ];
    let responses = Rc::new(RefCell::new(Vec::new()));
    let message_event_handler = Box::new(MessageEventHandler { responses: responses.clone() });
    let mut correlator = Correlator::new(contexts);
    correlator.register_handler(message_event_handler);
    let _ = correlator.push_message(MessageBuilder::new(&uuid1, "message").build());
    thread::sleep_ms(20);
    let _ = correlator.push_message(MessageBuilder::new(&uuid2, "message").build());
    thread::sleep_ms(80);
    let _ = correlator.push_message(MessageBuilder::new(&uuid3, "message").build());
    let _ = correlator.stop();
    assert_eq!(3, responses.borrow().len());
}

#[test]
fn test_given_correlator_when_it_is_built_from_json_then_we_get_the_expected_correlator() {
    let result = from_str::<Vec<config::Context>>(JSON_CONFIG);
    let expected_name = "CONTEXT_NAME_1".to_string();
    let expected_uuid = "185e96da-c00e-454b-b4fe-9d0a14a86335".to_string();
    println!("{:?}", &result);
    let mut contexts = result.ok().expect("Failed to deserialize a config::Context from JSON");
    for i in &contexts {
        assert_eq!(true, i.name.is_some());
    }
    let context = contexts.remove(0);
    assert_eq!(Some(&expected_name), context.name.as_ref());
    assert_eq!(&expected_uuid, &context.uuid.to_hyphenated_string());
}

#[test]
fn test_given_correlator_when_it_is_built_from_json_then_it_produces_the_expected_number_of_messages
    () {
    let uuid1 = "1b47ba91-d867-4a8c-9553-a5dfd6ea1274".to_string();
    let uuid2 = "2b47ba91-d867-4a8c-9553-a5dfd6ea1274".to_string();
    let uuid3 = "3b47ba91-d867-4a8c-9553-a5dfd6ea1274".to_string();
    let result = from_str::<Vec<config::Context>>(JSON_CONFIG);
    println!("{:?}", &result);
    let contexts = result.ok().expect("Failed to deserialize a config::Context from JSON");
    let responses = Rc::new(RefCell::new(Vec::new()));
    let message_event_handler = Box::new(MessageEventHandler { responses: responses.clone() });
    let mut correlator = Correlator::new(contexts);
    correlator.register_handler(message_event_handler);
    let _ = correlator.push_message(MessageBuilder::new(&uuid1, "message")
                                        .name(Some("p1"))
                                        .build());
    thread::sleep_ms(20);
    let _ = correlator.push_message(MessageBuilder::new(&uuid2, "message")
                                        .name(Some("p2"))
                                        .build());
    let _ = correlator.push_message(MessageBuilder::new(&uuid3, "message")
                                        .name(Some("p3"))
                                        .build());
    let _ = correlator.push_message(MessageBuilder::new(&uuid3, "message")
                                        .name(Some("p3"))
                                        .build());
    let _ = correlator.push_message(MessageBuilder::new(&uuid3, "message")
                                        .name(Some("p3"))
                                        .build());
    let _ = correlator.push_message(MessageBuilder::new(&uuid3, "message")
                                        .name(Some("p3"))
                                        .build());
    let _ = correlator.push_message(MessageBuilder::new(&uuid3, "message")
                                        .name(Some("p3"))
                                        .build());
    thread::sleep_ms(200);
    let _ = correlator.stop();
    println!("{:?}", &responses.borrow());
    assert_eq!(5, responses.borrow().len());
}
<|endoftext|>


<commit_before>use cpu::CPU;
use fnv::FnvHashMap;
use fnv::FnvHashSet;
use memory::MemSegment;

pub struct Analyst<'a> {
    entry_point: u16,
    pc: u16,
    current_instruction: u16,
    cpu: &'a mut CPU,
    furthest_branch: u16,
    found_exit_point: bool,

    instructions: FnvHashMap<u16, InstructionAnalysis>,
}

pub struct InstructionAnalysis {
    pub is_branch_target: bool,
}

impl Default for InstructionAnalysis {
    fn default() -> InstructionAnalysis {
        InstructionAnalysis {
            is_branch_target: false,
        }
    }
}

pub struct BlockAnalysis {
    pub entry_point: u16,
    pub exit_point: u16,

    pub instructions: FnvHashMap<u16, InstructionAnalysis>,
}

impl<'a> Analyst<'a> {
    pub fn new(cpu: &'a mut CPU) -> Analyst<'a> {
        Analyst {
            entry_point: 0,
            pc: 0,
            current_instruction: 0,
            cpu: cpu,
            furthest_branch: 0,
            found_exit_point: false,

            instructions: FnvHashMap::default(),
        }
    }

    #[cfg(feature = "debug_features")]
    pub fn find_exit_point(self, entry_point: u16) -> u16 {
        self.analyze(entry_point).exit_point
    }

    pub fn analyze(mut self, entry_point: u16) -> BlockAnalysis {
        self.entry_point = entry_point;
        self.pc = entry_point;

        let mut valid_instr_addrs: FnvHashSet<u16> = FnvHashSet::default();

        while !self.found_exit_point {
            // Ensure that every instruction has an entry
            let temp_pc = self.pc;
            self.current_instruction = temp_pc;
            valid_instr_addrs.insert(temp_pc);
            self.get_instr_analysis(temp_pc);

            let opcode = self.read_incr_pc();
            decode_opcode!(opcode, self);
        }

        self.remove_invalid_instructions(valid_instr_addrs);

        BlockAnalysis {
            entry_point: entry_point,
            exit_point: self.pc - 1,
            instructions: self.instructions,
        }
    }

    fn remove_invalid_instructions(&mut self, valid_instr_addrs: FnvHashSet<u16>) {
        let mut invalid_instrs: FnvHashSet<u16> = FnvHashSet::default();

        {
            for addr in self.instructions.keys().cloned() {
                if !valid_instr_addrs.contains(&addr) {
                    invalid_instrs.insert(addr);
                }
            }
        }

        for addr in &invalid_instrs {
            self.instructions.remove(addr);
        }
    }

    // Addressing modes
    fn immediate(&mut self) -> u8 {
        self.read_incr_pc();
        0
    }
    fn absolute(&mut self) -> u8 {
        self.read_w_incr_pc();
        0
    }
    fn absolute_x(&mut self) -> u8 {
        self.read_w_incr_pc();
        0
    }
    fn absolute_y(&mut self) -> u8 {
        self.read_w_incr_pc();
        0
    }
    fn zero_page(&mut self) -> u8 {
        self.read_incr_pc();
        0
    }
    fn zero_page_x(&mut self) -> u8 {
        self.read_incr_pc();
        0
    }
    fn zero_page_y(&mut self) -> u8 {
        self.read_incr_pc();
        0
    }
    fn indirect_x(&mut self) -> u8 {
        self.read_incr_pc();
        0
    }
    fn indirect_y(&mut self) -> u8 {
        self.read_incr_pc();
        0
    }
    fn accumulator(&mut self) -> u8 {
        0
    }

    // Instructions
    // Stores
    fn stx(&mut self, _: u8) {}
    fn sty(&mut self, _: u8) {}
    fn sta(&mut self, _: u8) {}

    // Loads
    fn ldx(&mut self, _: u8) {}
    fn lda(&mut self, _: u8) {}
    fn ldy(&mut self, _: u8) {}

    // Logic/Math Ops
    fn bit(&mut self, _: u8) {}
    fn and(&mut self, _: u8) {}
    fn ora(&mut self, _: u8) {}
    fn eor(&mut self, _: u8) {}
    fn adc(&mut self, _: u8) {}
    fn sbc(&mut self, _: u8) {}
    fn cmp(&mut self, _: u8) {}
    fn cpx(&mut self, _: u8) {}
    fn cpy(&mut self, _: u8) {}
    fn inc(&mut self, _: u8) {}
    fn iny(&mut self) {}
    fn inx(&mut self) {}
    fn dec(&mut self, _: u8) {}
    fn dey(&mut self) {}
    fn dex(&mut self) {}
    fn lsr(&mut self, _: u8) {}
    fn asl(&mut self, _: u8) {}
    fn ror(&mut self, _: u8) {}
    fn rol(&mut self, _: u8) {}

    // Jumps
    fn jmp(&mut self) {
        self.read_w_incr_pc();
        self.end_function();
    }
    fn jmpi(&mut self) {
        self.read_w_incr_pc();
        self.end_function();
    }
    fn jsr(&mut self) {
        self.read_w_incr_pc();
        self.end_function();
    }
    fn rts(&mut self) {
        self.end_function();
    }
    fn rti(&mut self) {
        self.end_function();
    }
    fn brk(&mut self) {
        self.end_function();
    }

    fn unofficial(&self) {}

    fn end_function(&mut self) {
        if self.pc > self.furthest_branch {
            self.found_exit_point = true;
        }
    }

    // Branches
    fn bcs(&mut self) {
        self.branch()
    }
    fn bcc(&mut self) {
        self.branch()
    }
    fn beq(&mut self) {
        self.branch()
    }
    fn bne(&mut self) {
        self.branch()
    }
    fn bvs(&mut self) {
        self.branch()
    }
    fn bvc(&mut self) {
        self.branch()
    }
    fn bmi(&mut self) {
        self.branch()
    }
    fn bpl(&mut self) {
        self.branch()
    }

    fn branch(&mut self) {
        let arg = self.read_incr_pc();
        let target = self.relative_addr(arg);
        self.get_instr_analysis(target).is_branch_target = true;
        if target > self.furthest_branch {
            self.furthest_branch = target;
        }
    }

    // Stack
    fn plp(&mut self) {}
    fn php(&mut self) {}
    fn pla(&mut self) {}
    fn pha(&mut self) {}

    // Misc
    fn nop(&mut self) {}
    fn sec(&mut self) {}
    fn clc(&mut self) {}
    fn sei(&mut self) {}
    fn sed(&mut self) {}
    fn cld(&mut self) {}
    fn clv(&mut self) {}
    fn tax(&mut self) {}
    fn tay(&mut self) {}
    fn tsx(&mut self) {}
    fn txa(&mut self) {}
    fn txs(&mut self) {}
    fn tya(&mut self) {}
    fn cli(&mut self) {}

    // Unofficial instructions
    fn u_nop(&mut self, _: u8) {}
    fn lax(&mut self, _: u8) {}
    fn sax(&mut self, _: u8) {}
    fn dcp(&mut self, _: u8) {}
    fn isc(&mut self, _: u8) {}
    fn slo(&mut self, _: u8) {}
    fn rla(&mut self, _: u8) {}
    fn sre(&mut self, _: u8) {}
    fn rra(&mut self, _: u8) {}
    fn kil(&mut self) {
        self.end_function();
    }
    fn unsupported(&mut self, _: u8) {
        self.end_function();
    }

    fn relative_addr(&self, disp: u8) -> u16 {
        let disp = (disp as i8) as i16; // We want to sign-extend here.
        let pc = self.pc as i16;
        pc.wrapping_add(disp) as u16
    }

    fn read_incr_pc(&mut self) -> u8 {
        let pc = self.pc;
        let val: u8 = self.read_safe(pc);
        self.pc = self.pc.wrapping_add(1);
        val
    }

    fn get_instr_analysis(&mut self, addr: u16) -> &mut InstructionAnalysis {
        let default: InstructionAnalysis = Default::default();
        self.instructions.entry(addr).or_insert(default)
    }

    fn read_w_incr_pc(&mut self) -> u16 {
        self.read_incr_pc() as u16 | ((self.read_incr_pc() as u16) << 8)
    }

    fn read_safe(&mut self, idx: u16) -> u8 {
        match idx {
            0x2000...0x3FFF => 0xFF,
            0x4000...0x401F => 0xFF,
            _ => self.cpu.read(idx),
        }
    }
}
<commit_msg>Track which instructions set/use which flags.<commit_after>use cpu::CPU;
use fnv::FnvHashMap;
use fnv::FnvHashSet;
use memory::MemSegment;

pub struct Analyst<'a> {
    entry_point: u16,
    pc: u16,
    current_instruction: u16,
    cpu: &'a mut CPU,
    furthest_branch: u16,
    found_exit_point: bool,

    pub last_sign_flag_set: u16,
    pub last_overflow_flag_set: u16,
    pub last_zero_flag_set: u16,
    pub last_carry_flag_set: u16,

    instructions: FnvHashMap<u16, InstructionAnalysis>,
}

#[derive(Debug, Clone)]
pub struct InstructionAnalysis {
    pub is_branch_target: bool,

    pub sign_flag_used: bool,
    pub overflow_flag_used: bool,
    pub zero_flag_used: bool,
    pub carry_flag_used: bool,
}

impl Default for InstructionAnalysis {
    fn default() -> InstructionAnalysis {
        InstructionAnalysis {
            is_branch_target: false,

            sign_flag_used: false,
            overflow_flag_used: false,
            zero_flag_used: false,
            carry_flag_used: false,
        }
    }
}

#[derive(Debug)]
pub struct BlockAnalysis {
    pub entry_point: u16,
    pub exit_point: u16,

    pub instructions: FnvHashMap<u16, InstructionAnalysis>,
}

impl<'a> Analyst<'a> {
    pub fn new(cpu: &'a mut CPU) -> Analyst<'a> {
        Analyst {
            entry_point: 0,
            pc: 0,
            current_instruction: 0,
            cpu: cpu,
            furthest_branch: 0,
            found_exit_point: false,

            last_sign_flag_set: 0,
            last_carry_flag_set: 0,
            last_zero_flag_set: 0,
            last_overflow_flag_set: 0,

            instructions: FnvHashMap::default(),
        }
    }

    #[cfg(feature = "debug_features")]
    pub fn find_exit_point(self, entry_point: u16) -> u16 {
        self.analyze(entry_point).exit_point
    }

    pub fn analyze(mut self, entry_point: u16) -> BlockAnalysis {
        self.entry_point = entry_point;
        self.pc = entry_point;

        let mut valid_instr_addrs: FnvHashSet<u16> = FnvHashSet::default();

        while !self.found_exit_point {
            // Ensure that every instruction has an entry
            let temp_pc = self.pc;
            self.current_instruction = temp_pc;
            valid_instr_addrs.insert(temp_pc);
            self.get_instr_analysis(temp_pc);

            let opcode = self.read_incr_pc();
            decode_opcode!(opcode, self);
        }

        self.remove_invalid_instructions(valid_instr_addrs);

        BlockAnalysis {
            entry_point: entry_point,
            exit_point: self.pc - 1,
            instructions: self.instructions,
        }
    }

    fn remove_invalid_instructions(&mut self, valid_instr_addrs: FnvHashSet<u16>) {
        let mut invalid_instrs: FnvHashSet<u16> = FnvHashSet::default();

        {
            for addr in self.instructions.keys().cloned() {
                if !valid_instr_addrs.contains(&addr) {
                    invalid_instrs.insert(addr);
                }
            }
        }

        for addr in &invalid_instrs {
            self.instructions.remove(addr);
        }
    }

    // Addressing modes
    fn immediate(&mut self) -> u8 {
        self.read_incr_pc();
        0
    }
    fn absolute(&mut self) -> u8 {
        self.read_w_incr_pc();
        0
    }
    fn absolute_x(&mut self) -> u8 {
        self.read_w_incr_pc();
        0
    }
    fn absolute_y(&mut self) -> u8 {
        self.read_w_incr_pc();
        0
    }
    fn zero_page(&mut self) -> u8 {
        self.read_incr_pc();
        0
    }
    fn zero_page_x(&mut self) -> u8 {
        self.read_incr_pc();
        0
    }
    fn zero_page_y(&mut self) -> u8 {
        self.read_incr_pc();
        0
    }
    fn indirect_x(&mut self) -> u8 {
        self.read_incr_pc();
        0
    }
    fn indirect_y(&mut self) -> u8 {
        self.read_incr_pc();
        0
    }
    fn accumulator(&mut self) -> u8 {
        0
    }

    // Instructions
    // Stores
    fn stx(&mut self, _: u8) {}
    fn sty(&mut self, _: u8) {}
    fn sta(&mut self, _: u8) {}

    // Loads
    fn ldx(&mut self, _: u8) {
        self.sign_set();
        self.zero_set();
    }
    fn lda(&mut self, _: u8) {
        self.sign_set();
        self.zero_set();
    }
    fn ldy(&mut self, _: u8) {
        self.sign_set();
        self.zero_set();
    }

    // Logic/Math Ops
    fn bit(&mut self, _: u8) {
        self.sign_set();
        self.carry_set();
        self.zero_set();
    }
    fn and(&mut self, _: u8) {
        self.sign_set();
        self.zero_set();
    }
    fn ora(&mut self, _: u8) {
        self.sign_set();
        self.zero_set();
    }
    fn eor(&mut self, _: u8) {
        self.sign_set();
        self.zero_set();
    }
    fn adc(&mut self, _: u8) {
        self.carry_used();
        self.carry_set();
        self.sign_set();
        self.zero_set();
        self.overflow_set();
    }
    fn sbc(&mut self, _: u8) {
        self.carry_used();
        self.carry_set();
        self.sign_set();
        self.zero_set();
        self.overflow_set();
    }
    fn cmp(&mut self, _: u8) {
        self.zero_set();
        self.sign_set();
        self.carry_set();
    }
    fn cpx(&mut self, _: u8) {
        self.zero_set();
        self.sign_set();
        self.carry_set();
    }
    fn cpy(&mut self, _: u8) {
        self.zero_set();
        self.sign_set();
        self.carry_set();
    }
    fn inc(&mut self, _: u8) {
        self.zero_set();
        self.sign_set();
    }
    fn iny(&mut self) {
        self.zero_set();
        self.sign_set();
    }
    fn inx(&mut self) {
        self.zero_set();
        self.sign_set();
    }
    fn dec(&mut self, _: u8) {
        self.zero_set();
        self.sign_set();
    }
    fn dey(&mut self) {
        self.zero_set();
        self.sign_set();
    }
    fn dex(&mut self) {
        self.zero_set();
        self.sign_set();
    }
    fn lsr(&mut self, _: u8) {
        self.zero_set();
        self.sign_set();
        self.carry_set();
    }
    fn asl(&mut self, _: u8) {
        self.zero_set();
        self.sign_set();
        self.carry_set();
    }
    fn ror(&mut self, _: u8) {
        self.carry_used();
        self.carry_set();
        self.sign_set();
        self.zero_set();
    }
    fn rol(&mut self, _: u8) {
        self.carry_used();
        self.carry_set();
        self.sign_set();
        self.zero_set();
    }

    // Jumps
    fn jmp(&mut self) {
        self.all_used();
        self.read_w_incr_pc();
        self.end_function();
    }
    fn jmpi(&mut self) {
        self.all_used();
        self.read_w_incr_pc();
        self.end_function();
    }
    fn jsr(&mut self) {
        self.all_used();
        self.read_w_incr_pc();
        self.end_function();
    }
    fn rts(&mut self) {
        self.all_used();
        self.end_function();
    }
    fn rti(&mut self) {
        self.all_used();
        self.end_function();
    }
    fn brk(&mut self) {
        self.all_used();
        self.end_function();
    }

    fn unofficial(&self) {}

    fn end_function(&mut self) {
        if self.pc > self.furthest_branch {
            self.found_exit_point = true;
        }
    }

    // Branches
    fn bcs(&mut self) {
        self.all_used();
        self.branch()
    }
    fn bcc(&mut self) {
        self.all_used();
        self.branch()
    }
    fn beq(&mut self) {
        self.all_used();
        self.branch()
    }
    fn bne(&mut self) {
        self.all_used();
        self.branch()
    }
    fn bvs(&mut self) {
        self.all_used();
        self.branch()
    }
    fn bvc(&mut self) {
        self.all_used();
        self.branch()
    }
    fn bmi(&mut self) {
        self.all_used();
        self.branch()
    }
    fn bpl(&mut self) {
        self.all_used();
        self.branch()
    }

    fn branch(&mut self) {
        let arg = self.read_incr_pc();
        let target = self.relative_addr(arg);
        self.get_instr_analysis(target).is_branch_target = true;
        if target > self.furthest_branch {
            self.furthest_branch = target;
        }
    }

    // Stack
    fn plp(&mut self) {
        self.carry_set();
        self.sign_set();
        self.zero_set();
        self.overflow_set();
    }
    fn php(&mut self) {
        self.all_used()
    }
    fn pla(&mut self) {
        self.sign_set();
        self.zero_set();
    }
    fn pha(&mut self) {}

    // Misc
    fn nop(&mut self) {}
    fn sec(&mut self) {
        self.carry_set();
    }
    fn clc(&mut self) {
        self.carry_set();
    }
    fn sei(&mut self) {}
    fn sed(&mut self) {}
    fn cld(&mut self) {}
    fn clv(&mut self) {
        self.overflow_set();
    }
    fn tax(&mut self) {
        self.sign_set();
        self.zero_set();
    }
    fn tay(&mut self) {
        self.sign_set();
        self.zero_set();
    }
    fn tsx(&mut self) {
        self.sign_set();
        self.zero_set();
    }
    fn txa(&mut self) {
        self.sign_set();
        self.zero_set();
    }
    fn txs(&mut self) {}
    fn tya(&mut self) {
        self.sign_set();
        self.zero_set();
    }
    fn cli(&mut self) {}

    // Unofficial instructions
    fn u_nop(&mut self, _: u8) {}
    fn lax(&mut self, _: u8) {
        self.lda(0);
        self.ldx(0);
    }
    fn sax(&mut self, _: u8) {}
    fn dcp(&mut self, _: u8) {
        self.dec(0);
        self.cmp(0);
    }
    fn isc(&mut self, _: u8) {
        self.sbc(0);
        self.inc(0);
    }
    fn slo(&mut self, _: u8) {
        self.asl(0);
        self.ora(0);
    }
    fn rla(&mut self, _: u8) {
        self.rol(0);
        self.and(0);
    }
    fn sre(&mut self, _: u8) {
        self.lsr(0);
        self.eor(0);
    }
    fn rra(&mut self, _: u8) {
        self.adc(0);
        self.ror(0);
    }
    fn kil(&mut self) {
        self.end_function();
    }
    fn unsupported(&mut self, _: u8) {
        self.end_function();
    }

    fn relative_addr(&self, disp: u8) -> u16 {
        let disp = (disp as i8) as i16; // We want to sign-extend here.
        let pc = self.pc as i16;
        pc.wrapping_add(disp) as u16
    }

    fn read_incr_pc(&mut self) -> u8 {
        let pc = self.pc;
        let val: u8 = self.read_safe(pc);
        self.pc = self.pc.wrapping_add(1);
        val
    }

    fn get_instr_analysis(&mut self, addr: u16) -> &mut InstructionAnalysis {
        let default: InstructionAnalysis = Default::default();
        self.instructions.entry(addr).or_insert(default)
    }

    fn read_w_incr_pc(&mut self) -> u16 {
        self.read_incr_pc() as u16 | ((self.read_incr_pc() as u16) << 8)
    }

    fn read_safe(&mut self, idx: u16) -> u8 {
        match idx {
            0x2000...0x3FFF => 0xFF,
            0x4000...0x401F => 0xFF,
            _ => self.cpu.read(idx),
        }
    }

    fn sign_set(&mut self) {
        self.last_sign_flag_set = self.current_instruction;
    }

    fn overflow_set(&mut self) {
        self.last_overflow_flag_set = self.current_instruction;
    }

    fn zero_set(&mut self) {
        self.last_zero_flag_set = self.current_instruction;
    }

    fn carry_set(&mut self) {
        self.last_carry_flag_set = self.current_instruction;
    }

    fn all_used(&mut self) {
        self.sign_used();
        self.overflow_used();
        self.zero_used();
        self.carry_used();
    }

    fn sign_used(&mut self) {
        if self.last_sign_flag_set == 0 {
            return;
        }
        self.instructions
            .get_mut(&self.last_sign_flag_set)
            .unwrap()
            .sign_flag_used = true;
    }

    fn overflow_used(&mut self) {
        if self.last_overflow_flag_set == 0 {
            return;
        }
        self.instructions
            .get_mut(&self.last_overflow_flag_set)
            .unwrap()
            .overflow_flag_used = true;
    }

    fn zero_used(&mut self) {
        if self.last_zero_flag_set == 0 {
            return;
        }
        self.instructions
            .get_mut(&self.last_zero_flag_set)
            .unwrap()
            .zero_flag_used = true;
    }

    fn carry_used(&mut self) {
        if self.last_carry_flag_set == 0 {
            return;
        }
        self.instructions
            .get_mut(&self.last_carry_flag_set)
            .unwrap()
            .carry_flag_used = true;
    }
}
<|endoftext|>


<commit_before>use super::book::Deal;
use std::ops;
use std::fmt;

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TradeStats {
  pub sum_sell: f64,
  pub sum_buy: f64,
  pub sum_sell_dest: f64,
  pub sum_buy_dest: f64,
  pub num_sell: u16,
  pub num_buy: u16,
}

// TradeStats operations

impl Default for TradeStats {
  fn default() -> TradeStats {
    TradeStats { sum_sell: 0.0, sum_sell_dest: 0.0, sum_buy: 0.0, sum_buy_dest: 0.0, num_sell: 0, num_buy: 0 }
  }
}

impl TradeStats {
  pub fn new(deals: Vec<&Deal>) -> TradeStats {
    deals.into_iter().fold(TradeStats::default(), |acc, deal| acc + deal)
  }
}

impl<'a> ops::Add<&'a TradeStats> for TradeStats {
  type Output = TradeStats;
  fn add(self, other: &TradeStats) -> TradeStats {
    TradeStats {
      sum_sell: self.sum_sell + other.sum_sell,
      sum_sell_dest: self.sum_sell_dest + other.sum_sell_dest,
      sum_buy: self.sum_buy + other.sum_buy,
      sum_buy_dest: self.sum_buy_dest + other.sum_buy_dest,
      num_sell: self.num_sell + other.num_sell,
      num_buy: self.num_buy + other.num_buy,
    }
  }
}

impl<'a> ops::Add<&'a Deal> for TradeStats {
  type Output = TradeStats;
  fn add(self, other: &Deal) -> TradeStats {
    let (buy, num_buy, buy_dest, sell, num_sell, sell_dest) =
      if other.amount > 0.0 { (other.amount, 1, other.amount*other.rate, 0.0, 0, 0.0) }
      else                  { (0.0, 0, 0.0, -other.amount, 1, -other.amount*other.rate) };
    TradeStats {
      sum_sell: self.sum_sell + sell,
      sum_sell_dest: self.sum_sell_dest + sell_dest,
      sum_buy: self.sum_buy + buy,
      sum_buy_dest: self.sum_buy_dest + buy_dest,
      num_sell: self.num_sell + num_sell,
      num_buy: self.num_buy + num_buy,
    }
  }
}

impl<'a> ops::Sub<&'a TradeStats> for TradeStats {
  type Output = TradeStats;
  fn sub(self, other: &TradeStats) -> TradeStats {
    TradeStats {
      sum_sell: self.sum_sell - other.sum_sell,
      sum_sell_dest: self.sum_sell_dest - other.sum_sell_dest,
      sum_buy: self.sum_buy - other.sum_buy,
      sum_buy_dest: self.sum_buy_dest - other.sum_buy_dest,
      num_sell: self.num_sell - other.num_sell,
      num_buy: self.num_buy - other.num_buy,
    }
  }
}

impl fmt::Display for TradeStats {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "SELL {} to {} rate {} num {} | BUY {} to {} rate {} num {}",
      self.sum_sell, self.sum_sell_dest, self.sum_sell_dest/self.sum_sell, self.num_sell,
      self.sum_buy, self.sum_buy_dest, self.sum_buy_dest/self.sum_buy, self.num_buy)
  }
}

pub trait TimeStats {
  fn update_stats_1s(&mut self);
}

/**
 ** TESTS TESTS TESTS
 **/

#[cfg(test)]
mod tests {
  use super::TradeStats;
  use ::data::book::Deal;

  #[test]
  fn stats_default() {
    let default = TradeStats::default();
    assert_eq!(default.sum_buy, 0.0);
  }

  #[test]
  fn stats_new() {
    let deal = Deal { id: 1, rate: 0.1, amount: 10.0 };
    let deals = vec![&deal];
    let stats = TradeStats::new(deals);
    assert_eq!(stats.sum_buy, 10.0);
  }

  #[test]
  fn stats_add_deal() {
    let deal = Deal { id: 1, rate: 0.1, amount: 10.0 };
    let stats = TradeStats::default() + &deal;
    assert_eq!(stats.sum_buy, 10.0);
  }

  #[test]
  fn stats_deals() {
    let deal1 = Deal { id: 1, rate: 0.1, amount: 10.0 };
    let deal2 = Deal { id: 2, rate: 0.1, amount: -10.0 };
    let stats = TradeStats::new(vec![&deal1, &deal2]);
    assert_eq!((stats.sum_buy, stats.num_buy, stats.sum_buy_dest), (10.0, 1, 1.0));
    assert_eq!((stats.sum_sell, stats.num_sell, stats.sum_sell_dest), (10.0, 1, 1.0));
  }

  #[test]
  fn stats_sub() {
    let deal1 = Deal { id: 1, rate: 0.1, amount: 10.0 };
    let deal2 = Deal { id: 2, rate: 0.1, amount: -10.0 };
    let stats1 = TradeStats::new(vec![&deal1, &deal2]);
    let stats2 = TradeStats::new(vec![&deal2]);
    let stats = stats1 - &stats2;
    assert_eq!((stats.sum_buy, stats.num_buy), (10.0, 1));
    assert_eq!((stats.sum_sell, stats.num_sell), (0.0, 0));
  }

  #[test]
  fn stats_sub_exact() {
    let deal1 = Deal { id: 1, rate: 0.1, amount: 10.0 };
    let deal2 = Deal { id: 2, rate: 0.1, amount: -10.0 };
    let stats1 = TradeStats::new(vec![&deal1, &deal2]);
    let stats2 = stats1.clone();
    let stats = stats1 - &stats2;
    assert_eq!(stats, TradeStats::default());
  }
}<commit_msg>account for float precission in stats<commit_after>use super::book::Deal;
use std::ops;
use std::fmt;

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TradeStats {
  pub sum_sell: f64,
  pub sum_buy: f64,
  pub sum_sell_dest: f64,
  pub sum_buy_dest: f64,
  pub num_sell: u16,
  pub num_buy: u16,
}

// TradeStats operations

impl Default for TradeStats {
  fn default() -> TradeStats {
    TradeStats { sum_sell: 0.0, sum_sell_dest: 0.0, sum_buy: 0.0, sum_buy_dest: 0.0, num_sell: 0, num_buy: 0 }
  }
}

impl TradeStats {
  pub fn new(deals: Vec<&Deal>) -> TradeStats {
    deals.into_iter().fold(TradeStats::default(), |acc, deal| acc + deal)
  }
}

impl<'a> ops::Add<&'a TradeStats> for TradeStats {
  type Output = TradeStats;
  fn add(self, other: &TradeStats) -> TradeStats {
    TradeStats {
      sum_sell: self.sum_sell + other.sum_sell,
      sum_sell_dest: self.sum_sell_dest + other.sum_sell_dest,
      sum_buy: self.sum_buy + other.sum_buy,
      sum_buy_dest: self.sum_buy_dest + other.sum_buy_dest,
      num_sell: self.num_sell + other.num_sell,
      num_buy: self.num_buy + other.num_buy,
    }
  }
}

impl<'a> ops::Add<&'a Deal> for TradeStats {
  type Output = TradeStats;
  fn add(self, other: &Deal) -> TradeStats {
    let (buy, num_buy, buy_dest, sell, num_sell, sell_dest) =
      if other.amount > 0.0 { (other.amount, 1, other.amount*other.rate, 0.0, 0, 0.0) }
      else                  { (0.0, 0, 0.0, -other.amount, 1, -other.amount*other.rate) };
    TradeStats {
      sum_sell: self.sum_sell + sell,
      sum_sell_dest: self.sum_sell_dest + sell_dest,
      sum_buy: self.sum_buy + buy,
      sum_buy_dest: self.sum_buy_dest + buy_dest,
      num_sell: self.num_sell + num_sell,
      num_buy: self.num_buy + num_buy,
    }
  }
}

impl<'a> ops::Sub<&'a TradeStats> for TradeStats {
  type Output = TradeStats;
  fn sub(self, other: &TradeStats) -> TradeStats {
    let (mut sum_sell, mut sum_sell_dest, mut sum_buy, mut sum_buy_dest) = (0.0, 0.0, 0.0, 0.0);
    let num_sell = self.num_sell - other.num_sell;
    let num_buy = self.num_buy - other.num_buy;
    if num_sell > 0 {
      sum_sell = self.sum_sell - other.sum_sell;
      sum_sell_dest = self.sum_sell_dest - other.sum_sell_dest;
    };
    if num_buy > 0 {
      sum_buy = self.sum_buy - other.sum_buy;
      sum_buy_dest = self.sum_buy_dest - other.sum_buy_dest;
    };
    TradeStats { sum_sell, sum_sell_dest, sum_buy, sum_buy_dest, num_sell, num_buy }
  }
}

impl fmt::Display for TradeStats {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "SELL {} to {} rate {} num {} | BUY {} to {} rate {} num {}",
      self.sum_sell, self.sum_sell_dest, self.sum_sell_dest/self.sum_sell, self.num_sell,
      self.sum_buy, self.sum_buy_dest, self.sum_buy_dest/self.sum_buy, self.num_buy)
  }
}

pub trait TimeStats {
  fn update_stats_1s(&mut self);
}

/**
 ** TESTS TESTS TESTS
 **/

#[cfg(test)]
mod tests {
  use super::TradeStats;
  use ::data::book::Deal;

  #[test]
  fn stats_default() {
    let default = TradeStats::default();
    assert_eq!(default.sum_buy, 0.0);
  }

  #[test]
  fn stats_new() {
    let deal = Deal { id: 1, rate: 0.1, amount: 10.0 };
    let deals = vec![&deal];
    let stats = TradeStats::new(deals);
    assert_eq!(stats.sum_buy, 10.0);
  }

  #[test]
  fn stats_add_deal() {
    let deal = Deal { id: 1, rate: 0.1, amount: 10.0 };
    let stats = TradeStats::default() + &deal;
    assert_eq!(stats.sum_buy, 10.0);
  }

  #[test]
  fn stats_deals() {
    let deal1 = Deal { id: 1, rate: 0.1, amount: 10.0 };
    let deal2 = Deal { id: 2, rate: 0.1, amount: -10.0 };
    let stats = TradeStats::new(vec![&deal1, &deal2]);
    assert_eq!((stats.sum_buy, stats.num_buy, stats.sum_buy_dest), (10.0, 1, 1.0));
    assert_eq!((stats.sum_sell, stats.num_sell, stats.sum_sell_dest), (10.0, 1, 1.0));
  }

  #[test]
  fn stats_sub() {
    let deal1 = Deal { id: 1, rate: 0.1, amount: 10.0 };
    let deal2 = Deal { id: 2, rate: 0.1, amount: -10.0 };
    let stats1 = TradeStats::new(vec![&deal1, &deal2]);
    let stats2 = TradeStats::new(vec![&deal2]);
    let stats = stats1 - &stats2;
    assert_eq!((stats.sum_buy, stats.num_buy), (10.0, 1));
    assert_eq!((stats.sum_sell, stats.num_sell), (0.0, 0));
  }

  #[test]
  fn stats_sub_exact() {
    let deal1 = Deal { id: 1, rate: 0.1, amount: 10.0 };
    let deal2 = Deal { id: 2, rate: 0.1, amount: -10.0 };
    let stats1 = TradeStats::new(vec![&deal1, &deal2]);
    let stats2 = stats1.clone();
    let stats = stats1 - &stats2;
    assert_eq!(stats, TradeStats::default());
  }
}<|endoftext|>