<commit_before>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// arch/amd64/boot.rs
///! Kernel logging framework
///!
///! All kernel logging goes through this module, using the `log_*` macros, each corresponding
///! to a one of the logging levels in `Levels`.
// TODO: Support registerable log sinks, where all >Debug logs go (why not all?)
// > Cache/buffer sink
// > Display sink
// > Serial sink
use prelude::*;
use core::fmt::{self,Write};
use arch::sync::Spinlock;

/// Log level, ranging from a kernel panic down to tracing
#[derive(PartialEq,PartialOrd,Copy,Clone)]
pub enum Level
{
	/// Everything broke
	LevelPanic,
	/// Something broke
	LevelError,
	/// Recoverable
	LevelWarning,
 	/// Odd
	LevelNotice,
   	/// Interesting (least important for the user)
	LevelInfo,
    	/// General (highest developer-only level)
	LevelLog,
   	/// What
	LevelDebug,
  	/// Where
	LevelTrace,
}

#[doc(hidden)]
pub enum Colour
{
	Default,
	Red,
	Yellow,
	Green,
	Blue,
	Purple,
	Grey,
}

#[doc(hidden)]
pub struct LoggingFormatter<'a>
{
	lock_handle: ::arch::sync::HeldSpinlock<'a,Sinks>,

	// NOTE: Must be second, forcing interrupts to be reenabled after the lock is released
	_irq_handle: ::arch::sync::HeldInterrupts,
}

/// Wrapper around a &-ptr that prints a hexdump of the passed data.
pub struct HexDump<'a,T:'a>(pub &'a T);

/// Wrapper around a `&[u8]` to print it as an escaped byte string
pub struct RawString<'a>(pub &'a [u8]);

static S_LOGGING_LOCK: Spinlock<Sinks> = Spinlock::new( Sinks { serial: serial::Sink, memory: None, video: None } );

trait Sink
{
	/// Start a new log entry
	fn start(&mut self, timestamp: ::time::TickCount, level: Level, source: &'static str);
	/// Append data to the current log entry
	fn write(&mut self, data: &str);
	/// End a log entry
	fn end(&mut self);
}
struct Sinks
{
	serial: serial::Sink,
	memory: Option<memory::Sink>,
	video: Option<video::Sink>,
}

mod serial
{
	use prelude::*;
	use super::{Level,Colour};
	use core::fmt;

	pub struct Sink;
	impl super::Sink for Sink
	{
		fn start(&mut self, timestamp: ::time::TickCount, level: Level, source: &'static str) {
			use core::fmt::Write;
			self.set_colour(level.to_colour());
			write!(self, "{:6}{} {}[{}] - ", timestamp, level, ::threads::get_thread_id(), source).unwrap();
		}
		fn write(&mut self, s: &str) {
			::arch::puts(s);
		}
		fn end(&mut self) {
			::arch::puts("\x1b[0m\n");
		}
	}
	impl fmt::Write for Sink
	{
		fn write_str(&mut self, s: &str) -> fmt::Result {
			::arch::puts(s);
			Ok( () )
		}
	}
	impl Sink
	{
		/// Set the output colour of the formatter
		pub fn set_colour(&self, colour: Colour) {
			match colour
			{
			Colour::Default => ::arch::puts("\x1b[0m"),
			Colour::Red     => ::arch::puts("\x1b[31m"),
			Colour::Green   => ::arch::puts("\x1b[32m"),
			Colour::Yellow  => ::arch::puts("\x1b[33m"),
			Colour::Blue    => ::arch::puts("\x1b[34m"),
			Colour::Purple  => ::arch::puts("\x1b[35m"),
			Colour::Grey    => ::arch::puts("\x1b[1;30m"),
			}
		}
	}
}

mod memory
{
	use prelude::*;
	use super::Level;

	pub struct Sink
	{
		lines: ::lib::ring_buffer::RingBuf<LogMessage>,
	}
	// Buffer for log data
	// Temp hack until type-level ints are avaliable
	struct LogDataBuf([u8;160]);
	impl LogDataBuf {
		fn new() -> LogDataBuf { LogDataBuf(unsafe{::core::mem::zeroed()}) }
	}
	impl ::core::convert::AsRef<[u8]> for LogDataBuf {
		fn as_ref(&self) -> &[u8] { &self.0 }
	}
	impl ::core::convert::AsMut<[u8]> for LogDataBuf {
		fn as_mut(&mut self) -> &mut [u8] { &mut self.0 }
	}
	#[allow(dead_code)]	// Allow unread fields
	struct LogMessage
	{
		time: ::time::TickCount,
		level: Level,
		source: &'static str,
		data: ::lib::string::FixedString<LogDataBuf>,
	}
	impl Sink
	{
		pub fn new() -> Sink {
			Sink {
				lines: ::lib::ring_buffer::RingBuf::new(256),	// 256 log of scrollback
			}
		}
	}
	impl super::Sink for Sink
	{
		fn start(&mut self, timestamp: ::time::TickCount, level: Level, source: &'static str) {
			let new_line = LogMessage {
				time: timestamp, level: level, source: source,
				data: ::lib::string::FixedString::new(LogDataBuf::new())
				};
			match self.lines.push_back( new_line )
			{
			Ok(_) => {},
			Err(_new_line) => {
				todo!("Handle rollover of kernel log");
				},
			}
		}
		fn write(&mut self, s: &str) {
			self.lines.back_mut().unwrap().data.push_str(s);
		}
		fn end(&mut self) {
			// No action required
		}
	}
}

mod video
{
	//use prelude::*;
	use super::Level;

	pub struct Sink;

	impl super::Sink for Sink
	{
		fn start(&mut self, timestamp: ::time::TickCount, level: Level, source: &'static str) {
			// Acquire a writer from the GUI
			// - TODO: requires acquiring the lock on the kernel log, which is a Mutex, and may already be held.
			// Write header
			todo!("VideoSink - {} {} {}", timestamp, level, source);
		}
		fn write(&mut self, s: &str) {
			// Pass through
			todo!("video::Sink::write - '{}'", s);
		}
		fn end(&mut self) {
			// Drop writer (replace with None)
			unimplemented!();
		}
	}
}

impl Level
{
	fn to_flag(&self) -> char
	{
		match *self
		{
		Level::LevelPanic   => 'k',
		Level::LevelError   => 'e',
		Level::LevelWarning => 'w',
		Level::LevelNotice  => 'n',
		Level::LevelInfo    => 'i',
		Level::LevelLog     => 'l',
		Level::LevelDebug   => 'd',
		Level::LevelTrace   => 't',
		}
	}
	fn to_colour(&self) -> Colour
	{
		match *self
		{
		Level::LevelPanic   => Colour::Purple,
		Level::LevelError   => Colour::Red,
		Level::LevelWarning => Colour::Yellow,
		Level::LevelNotice  => Colour::Green,
		Level::LevelLog     => Colour::Blue,
		Level::LevelTrace   => Colour::Grey,
		_ => Colour::Default,
		}
	}
}

impl fmt::Display for Level
{
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "{}", self.to_flag())
	}
}

impl Sinks
{
	pub fn foreach_mut<Fcn: FnMut(&mut Sink)>(&mut self, mut f: Fcn)
	{
		f(&mut self.serial);
		self.memory.as_mut().map(|x| f(x));
		self.video .as_mut().map(|x| f(x));
	}
}

impl<'a> LoggingFormatter<'a>
{
	/// Create a new logging formatter
	pub fn new(level: Level, modname: &'static str) -> LoggingFormatter<'static>
	{
		let mut rv = LoggingFormatter {
				_irq_handle: ::arch::sync::hold_interrupts(),
				lock_handle: S_LOGGING_LOCK.lock()
			};
		let ts = ::time::ticks();
		rv.lock_handle.foreach_mut(|x| x.start(ts, level, modname));
		rv
	}
}

impl<'a> fmt::Write for LoggingFormatter<'a>
{
	fn write_str(&mut self, s: &str) -> fmt::Result
	{
		self.lock_handle.foreach_mut(|x| x.write(s));
		Ok( () )
	}
}
impl<'a> ::core::ops::Drop for LoggingFormatter<'a>
{
	fn drop(&mut self)
	{
		self.lock_handle.foreach_mut(|x| x.end());
	}
}

impl<'a,T:'a> HexDump<'a,T>
{
	/// Return the wrapped type as a &[u8]
	fn byteslice(&self) -> &[u8]
	{
		let size = ::core::mem::size_of::<T>();
		// SAFE: Memory is valid, and cast is allowed
		unsafe { ::core::slice::from_raw_parts( self.0 as *const T as *const u8, size ) }
	}
}

impl<'a,T:'a> fmt::Debug for HexDump<'a,T>
{
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
	{
		let slice = self.byteslice();
		try!(write!(f, "{} bytes: ", slice.len()));
		for (idx,v) in slice.iter().enumerate()
		{
			try!(write!(f, "{:02x} ", *v));
			if idx % 16 == 15 {
				try!(write!(f, "| "));
			}
		}
		Ok( () )
	}
}

impl<'a> fmt::Debug for RawString<'a>
{
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		try!(write!(f, "b\""));
		for &b in self.0 {
			match b
			{
			b'\t' => try!(write!(f, "\\t")),
			b'\n' => try!(write!(f, "\\n")),
			b'\\' => try!(write!(f, "\\\\")),
			b'"' => try!(write!(f, "\\\"")),
			32 ... 0x7E => try!(write!(f, "{}", b as char)),
			_ => try!(write!(f, "\\x{:02x}", b)),
			}
		}
		try!(write!(f, "\""));
		Ok( () )
	}
}

struct HexDumpBlk<'a>(&'a [u8]);
impl<'a> fmt::Display for HexDumpBlk<'a>
{
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
	{
		assert!(self.0.len() <= 16);
		for i in (0 .. 16)
		{
			if i == 8 {
				try!(write!(f, " "));
			}
			if i < self.0.len() {
				try!(write!(f, "{:02x} ", self.0[i]));
			}
			else {
				try!(write!(f, "   "));
			}

		}
		try!(write!(f, "|"));
		for i in (0 .. 16)
		{
			if i < self.0.len() {
				try!(write!(f, "{}",
					match self.0[i]
					{
					v @ 32 ... 0x7E => v as char,
					_ => '.',
					}));
			}
			else {
				try!(write!(f, " "));
			}
		}
		try!(write!(f, "|"));
		Ok( () )
	}
}
pub fn hex_dump_t<T>(label: &str, data: &T) {
	// SAFE: Casting to an immutable byte representation is valid
	let slice = unsafe { ::core::slice::from_raw_parts(data as *const T as *const u8, ::core::mem::size_of::<T>()) };
	hex_dump(label, slice);
}
pub fn hex_dump(label: &str, data: &[u8])
{
	for block in data.chunks(16)
	{
		log_debug!("{} {:p}: {}", label, &block[0], HexDumpBlk(block));
	}
}

pub fn print_iter<I: Iterator>(i: I) -> PrintIter<I> { PrintIter(::core::cell::RefCell::new(Some(i))) }
pub struct PrintIter<I: Iterator>(::core::cell::RefCell<Option<I>>);
macro_rules! print_iter_def {
	($($t:ident),+) => {$(
		impl<I: Iterator> ::core::fmt::$t for PrintIter<I>
		where
			<I as Iterator>::Item: ::core::fmt::$t
		{
			fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
				let mut i = self.0.borrow_mut().take().unwrap();
				try!(write!(f, "["));
				if let Some(v) = i.next()
				{
					try!(v.fmt(f));
					for v in i {
						try!(write!(f, ","));
						try!(v.fmt(f));
					}
				}
				try!(write!(f, "]"));
				Ok( () )
			}
		})*
	};
}
print_iter_def! { LowerHex }

pub fn start_memory_sink() {
	let sink = memory::Sink::new();

	let _irq = ::arch::sync::hold_interrupts();
	let mut lh = S_LOGGING_LOCK.lock();

	if lh.memory.is_none()
	{
		lh.memory = Some( sink );
	}
}

#[doc(hidden)]
/// Returns true if the passed combination of module and level is enabled
pub fn enabled(level: Level, modname: &str) -> bool
{
	match modname
	{
	"kernel::memory::heap" => (level < Level::LevelDebug),	// Heap only prints higher than debug
	"kernel::memory::phys" => (level < Level::LevelTrace),	// PMM only prints >Trace
	"storage_ata::io" => (level < Level::LevelDebug),
	"kernel::async" => (level < Level::LevelDebug),
	_ => true,
	}
}

#[doc(hidden)]
/// Returns a logging formatter
pub fn getstream(level: Level, modname: &'static str) -> LoggingFormatter
{
	assert!( enabled(level, modname) );
	LoggingFormatter::new(level, modname)
}

// vim: ft=rust

<commit_msg>Kernel/logging - Suppress disk IO messages<commit_after>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// arch/amd64/boot.rs
///! Kernel logging framework
///!
///! All kernel logging goes through this module, using the `log_*` macros, each corresponding
///! to a one of the logging levels in `Levels`.
// TODO: Support registerable log sinks, where all >Debug logs go (why not all?)
// > Cache/buffer sink
// > Display sink
// > Serial sink
use prelude::*;
use core::fmt::{self,Write};
use arch::sync::Spinlock;

/// Log level, ranging from a kernel panic down to tracing
#[derive(PartialEq,PartialOrd,Copy,Clone)]
pub enum Level
{
	/// Everything broke
	LevelPanic,
	/// Something broke
	LevelError,
	/// Recoverable
	LevelWarning,
 	/// Odd
	LevelNotice,
   	/// Interesting (least important for the user)
	LevelInfo,
    	/// General (highest developer-only level)
	LevelLog,
   	/// What
	LevelDebug,
  	/// Where
	LevelTrace,
}

#[doc(hidden)]
pub enum Colour
{
	Default,
	Red,
	Yellow,
	Green,
	Blue,
	Purple,
	Grey,
}

#[doc(hidden)]
pub struct LoggingFormatter<'a>
{
	lock_handle: ::arch::sync::HeldSpinlock<'a,Sinks>,

	// NOTE: Must be second, forcing interrupts to be reenabled after the lock is released
	_irq_handle: ::arch::sync::HeldInterrupts,
}

/// Wrapper around a &-ptr that prints a hexdump of the passed data.
pub struct HexDump<'a,T:'a>(pub &'a T);

/// Wrapper around a `&[u8]` to print it as an escaped byte string
pub struct RawString<'a>(pub &'a [u8]);

static S_LOGGING_LOCK: Spinlock<Sinks> = Spinlock::new( Sinks { serial: serial::Sink, memory: None, video: None } );

trait Sink
{
	/// Start a new log entry
	fn start(&mut self, timestamp: ::time::TickCount, level: Level, source: &'static str);
	/// Append data to the current log entry
	fn write(&mut self, data: &str);
	/// End a log entry
	fn end(&mut self);
}
struct Sinks
{
	serial: serial::Sink,
	memory: Option<memory::Sink>,
	video: Option<video::Sink>,
}

mod serial
{
	use prelude::*;
	use super::{Level,Colour};
	use core::fmt;

	pub struct Sink;
	impl super::Sink for Sink
	{
		fn start(&mut self, timestamp: ::time::TickCount, level: Level, source: &'static str) {
			use core::fmt::Write;
			self.set_colour(level.to_colour());
			write!(self, "{:6}{} {}[{}] - ", timestamp, level, ::threads::get_thread_id(), source).unwrap();
		}
		fn write(&mut self, s: &str) {
			::arch::puts(s);
		}
		fn end(&mut self) {
			::arch::puts("\x1b[0m\n");
		}
	}
	impl fmt::Write for Sink
	{
		fn write_str(&mut self, s: &str) -> fmt::Result {
			::arch::puts(s);
			Ok( () )
		}
	}
	impl Sink
	{
		/// Set the output colour of the formatter
		pub fn set_colour(&self, colour: Colour) {
			match colour
			{
			Colour::Default => ::arch::puts("\x1b[0m"),
			Colour::Red     => ::arch::puts("\x1b[31m"),
			Colour::Green   => ::arch::puts("\x1b[32m"),
			Colour::Yellow  => ::arch::puts("\x1b[33m"),
			Colour::Blue    => ::arch::puts("\x1b[34m"),
			Colour::Purple  => ::arch::puts("\x1b[35m"),
			Colour::Grey    => ::arch::puts("\x1b[1;30m"),
			}
		}
	}
}

mod memory
{
	use prelude::*;
	use super::Level;

	pub struct Sink
	{
		lines: ::lib::ring_buffer::RingBuf<LogMessage>,
	}
	// Buffer for log data
	// Temp hack until type-level ints are avaliable
	struct LogDataBuf([u8;160]);
	impl LogDataBuf {
		fn new() -> LogDataBuf { LogDataBuf(unsafe{::core::mem::zeroed()}) }
	}
	impl ::core::convert::AsRef<[u8]> for LogDataBuf {
		fn as_ref(&self) -> &[u8] { &self.0 }
	}
	impl ::core::convert::AsMut<[u8]> for LogDataBuf {
		fn as_mut(&mut self) -> &mut [u8] { &mut self.0 }
	}
	#[allow(dead_code)]	// Allow unread fields
	struct LogMessage
	{
		time: ::time::TickCount,
		level: Level,
		source: &'static str,
		data: ::lib::string::FixedString<LogDataBuf>,
	}
	impl Sink
	{
		pub fn new() -> Sink {
			Sink {
				lines: ::lib::ring_buffer::RingBuf::new(256),	// 256 log of scrollback
			}
		}
	}
	impl super::Sink for Sink
	{
		fn start(&mut self, timestamp: ::time::TickCount, level: Level, source: &'static str) {
			let new_line = LogMessage {
				time: timestamp, level: level, source: source,
				data: ::lib::string::FixedString::new(LogDataBuf::new())
				};
			match self.lines.push_back( new_line )
			{
			Ok(_) => {},
			Err(_new_line) => {
				todo!("Handle rollover of kernel log");
				},
			}
		}
		fn write(&mut self, s: &str) {
			self.lines.back_mut().unwrap().data.push_str(s);
		}
		fn end(&mut self) {
			// No action required
		}
	}
}

mod video
{
	//use prelude::*;
	use super::Level;

	pub struct Sink;

	impl super::Sink for Sink
	{
		fn start(&mut self, timestamp: ::time::TickCount, level: Level, source: &'static str) {
			// Acquire a writer from the GUI
			// - TODO: requires acquiring the lock on the kernel log, which is a Mutex, and may already be held.
			// Write header
			todo!("VideoSink - {} {} {}", timestamp, level, source);
		}
		fn write(&mut self, s: &str) {
			// Pass through
			todo!("video::Sink::write - '{}'", s);
		}
		fn end(&mut self) {
			// Drop writer (replace with None)
			unimplemented!();
		}
	}
}

impl Level
{
	fn to_flag(&self) -> char
	{
		match *self
		{
		Level::LevelPanic   => 'k',
		Level::LevelError   => 'e',
		Level::LevelWarning => 'w',
		Level::LevelNotice  => 'n',
		Level::LevelInfo    => 'i',
		Level::LevelLog     => 'l',
		Level::LevelDebug   => 'd',
		Level::LevelTrace   => 't',
		}
	}
	fn to_colour(&self) -> Colour
	{
		match *self
		{
		Level::LevelPanic   => Colour::Purple,
		Level::LevelError   => Colour::Red,
		Level::LevelWarning => Colour::Yellow,
		Level::LevelNotice  => Colour::Green,
		Level::LevelLog     => Colour::Blue,
		Level::LevelTrace   => Colour::Grey,
		_ => Colour::Default,
		}
	}
}

impl fmt::Display for Level
{
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "{}", self.to_flag())
	}
}

impl Sinks
{
	pub fn foreach_mut<Fcn: FnMut(&mut Sink)>(&mut self, mut f: Fcn)
	{
		f(&mut self.serial);
		self.memory.as_mut().map(|x| f(x));
		self.video .as_mut().map(|x| f(x));
	}
}

impl<'a> LoggingFormatter<'a>
{
	/// Create a new logging formatter
	pub fn new(level: Level, modname: &'static str) -> LoggingFormatter<'static>
	{
		let mut rv = LoggingFormatter {
				_irq_handle: ::arch::sync::hold_interrupts(),
				lock_handle: S_LOGGING_LOCK.lock()
			};
		let ts = ::time::ticks();
		rv.lock_handle.foreach_mut(|x| x.start(ts, level, modname));
		rv
	}
}

impl<'a> fmt::Write for LoggingFormatter<'a>
{
	fn write_str(&mut self, s: &str) -> fmt::Result
	{
		self.lock_handle.foreach_mut(|x| x.write(s));
		Ok( () )
	}
}
impl<'a> ::core::ops::Drop for LoggingFormatter<'a>
{
	fn drop(&mut self)
	{
		self.lock_handle.foreach_mut(|x| x.end());
	}
}

impl<'a,T:'a> HexDump<'a,T>
{
	/// Return the wrapped type as a &[u8]
	fn byteslice(&self) -> &[u8]
	{
		let size = ::core::mem::size_of::<T>();
		// SAFE: Memory is valid, and cast is allowed
		unsafe { ::core::slice::from_raw_parts( self.0 as *const T as *const u8, size ) }
	}
}

impl<'a,T:'a> fmt::Debug for HexDump<'a,T>
{
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
	{
		let slice = self.byteslice();
		try!(write!(f, "{} bytes: ", slice.len()));
		for (idx,v) in slice.iter().enumerate()
		{
			try!(write!(f, "{:02x} ", *v));
			if idx % 16 == 15 {
				try!(write!(f, "| "));
			}
		}
		Ok( () )
	}
}

impl<'a> fmt::Debug for RawString<'a>
{
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		try!(write!(f, "b\""));
		for &b in self.0 {
			match b
			{
			b'\t' => try!(write!(f, "\\t")),
			b'\n' => try!(write!(f, "\\n")),
			b'\\' => try!(write!(f, "\\\\")),
			b'"' => try!(write!(f, "\\\"")),
			32 ... 0x7E => try!(write!(f, "{}", b as char)),
			_ => try!(write!(f, "\\x{:02x}", b)),
			}
		}
		try!(write!(f, "\""));
		Ok( () )
	}
}

struct HexDumpBlk<'a>(&'a [u8]);
impl<'a> fmt::Display for HexDumpBlk<'a>
{
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
	{
		assert!(self.0.len() <= 16);
		for i in (0 .. 16)
		{
			if i == 8 {
				try!(write!(f, " "));
			}
			if i < self.0.len() {
				try!(write!(f, "{:02x} ", self.0[i]));
			}
			else {
				try!(write!(f, "   "));
			}

		}
		try!(write!(f, "|"));
		for i in (0 .. 16)
		{
			if i < self.0.len() {
				try!(write!(f, "{}",
					match self.0[i]
					{
					v @ 32 ... 0x7E => v as char,
					_ => '.',
					}));
			}
			else {
				try!(write!(f, " "));
			}
		}
		try!(write!(f, "|"));
		Ok( () )
	}
}
pub fn hex_dump_t<T>(label: &str, data: &T) {
	// SAFE: Casting to an immutable byte representation is valid
	let slice = unsafe { ::core::slice::from_raw_parts(data as *const T as *const u8, ::core::mem::size_of::<T>()) };
	hex_dump(label, slice);
}
pub fn hex_dump(label: &str, data: &[u8])
{
	for block in data.chunks(16)
	{
		log_debug!("{} {:p}: {}", label, &block[0], HexDumpBlk(block));
	}
}

pub fn print_iter<I: Iterator>(i: I) -> PrintIter<I> { PrintIter(::core::cell::RefCell::new(Some(i))) }
pub struct PrintIter<I: Iterator>(::core::cell::RefCell<Option<I>>);
macro_rules! print_iter_def {
	($($t:ident),+) => {$(
		impl<I: Iterator> ::core::fmt::$t for PrintIter<I>
		where
			<I as Iterator>::Item: ::core::fmt::$t
		{
			fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
				let mut i = self.0.borrow_mut().take().unwrap();
				try!(write!(f, "["));
				if let Some(v) = i.next()
				{
					try!(v.fmt(f));
					for v in i {
						try!(write!(f, ","));
						try!(v.fmt(f));
					}
				}
				try!(write!(f, "]"));
				Ok( () )
			}
		})*
	};
}
print_iter_def! { LowerHex }

pub fn start_memory_sink() {
	let sink = memory::Sink::new();

	let _irq = ::arch::sync::hold_interrupts();
	let mut lh = S_LOGGING_LOCK.lock();

	if lh.memory.is_none()
	{
		lh.memory = Some( sink );
	}
}

#[doc(hidden)]
/// Returns true if the passed combination of module and level is enabled
pub fn enabled(level: Level, modname: &str) -> bool
{
	match modname
	{
	"kernel::memory::heap" => (level < Level::LevelDebug),	// Heap only prints higher than debug
	"kernel::memory::phys" => (level < Level::LevelTrace),	// PMM only prints >Trace
	"kernel::metadevs::storage" => (level < Level::LevelTrace),
	"storage_ata::io" => (level < Level::LevelDebug),
	"kernel::async" => (level < Level::LevelDebug),
	_ => true,
	}
}

#[doc(hidden)]
/// Returns a logging formatter
pub fn getstream(level: Level, modname: &'static str) -> LoggingFormatter
{
	assert!( enabled(level, modname) );
	LoggingFormatter::new(level, modname)
}

// vim: ft=rust

<|endoftext|>


<commit_before>// Build script.
//
// This program is run by Cargo when building lib/codegen. It is used to generate Rust code from
// the language definitions in the lib/codegen/meta directory.
//
// Environment:
//
// OUT_DIR
//     Directory where generated files should be placed.
//
// TARGET
//     Target triple provided by Cargo.
//
// CRETONNE_TARGETS (Optional)
//     A setting for conditional compilation of isa targets. Possible values can be "native" or
//     known isa targets separated by ','.
//
// The build script expects to be run from the directory where this build.rs file lives. The
// current directory is used to find the sources.

use std::env;
use std::process;

fn main() {
    let out_dir = env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set");
    let target_triple = env::var("TARGET").expect("The TARGET environment variable must be set");
    let cretonne_targets = env::var("CRETONNE_TARGETS").ok();
    let cretonne_targets = cretonne_targets.as_ref().map(|s| s.as_ref());

    // Configure isa targets cfg.
    match isa_targets(cretonne_targets, &target_triple) {
        Ok(isa_targets) => {
            for isa in &isa_targets {
                println!("cargo:rustc-cfg=build_{}", isa.name());
            }
        }
        Err(err) => {
            eprintln!("Error: {}", err);
            process::exit(1);
        }
    }

    println!("Build script generating files in {}", out_dir);

    let cur_dir = env::current_dir().expect("Can't access current working directory");
    let crate_dir = cur_dir.as_path();

    // Make sure we rebuild if this build script changes.
    // I guess that won't happen if you have non-UTF8 bytes in your path names.
    // The `build.py` script prints out its own dependencies.
    println!(
        "cargo:rerun-if-changed={}",
        crate_dir.join("build.rs").to_string_lossy()
    );

    // Scripts are in `$crate_dir/meta`.
    let meta_dir = crate_dir.join("meta");
    let build_script = meta_dir.join("build.py");

    // Launch build script with Python. We'll just find python in the path.
    // Use -B to disable .pyc files, because they cause trouble for vendoring
    // scripts, and this is a build step that isn't run very often anyway.
    let status = process::Command::new("python")
        .current_dir(crate_dir)
        .arg("-B")
        .arg(build_script)
        .arg("--out-dir")
        .arg(out_dir)
        .status()
        .expect("Failed to launch second-level build script");
    if !status.success() {
        process::exit(status.code().unwrap());
    }
}

/// Represents known ISA target.
#[derive(Copy, Clone)]
enum Isa {
    Riscv,
    X86,
    Arm32,
    Arm64,
}

impl Isa {
    /// Creates isa target using name.
    fn new(name: &str) -> Option<Self> {
        Isa::all()
            .iter()
            .cloned()
            .filter(|isa| isa.name() == name)
            .next()
    }

    /// Creates isa target from arch.
    fn from_arch(arch: &str) -> Option<Isa> {
        Isa::all()
            .iter()
            .cloned()
            .filter(|isa| isa.is_arch_applicable(arch))
            .next()
    }

    /// Returns all supported isa targets.
    fn all() -> [Isa; 4] {
        [Isa::Riscv, Isa::X86, Isa::Arm32, Isa::Arm64]
    }

    /// Returns name of the isa target.
    fn name(&self) -> &'static str {
        match *self {
            Isa::Riscv => "riscv",
            Isa::X86 => "x86",
            Isa::Arm32 => "arm32",
            Isa::Arm64 => "arm64",
        }
    }

    /// Checks if arch is applicable for the isa target.
    fn is_arch_applicable(&self, arch: &str) -> bool {
        match *self {
            Isa::Riscv => arch == "riscv",
            Isa::X86 => ["x86_64", "i386", "i586", "i686"].contains(&arch),
            Isa::Arm32 => arch.starts_with("arm") || arch.starts_with("thumb"),
            Isa::Arm64 => arch == "aarch64",
        }
    }
}

/// Returns isa targets to configure conditional compilation.
fn isa_targets(cretonne_targets: Option<&str>, target_triple: &str) -> Result<Vec<Isa>, String> {
    match cretonne_targets {
        Some("native") => {
            Isa::from_arch(target_triple.split('-').next().unwrap())
                .map(|isa| vec![isa])
                .ok_or_else(|| {
                    format!(
                        "no supported isa found for target triple `{}`",
                        target_triple
                    )
                })
        }
        Some(targets) => {
            let unknown_isa_targets = targets
                .split(',')
                .filter(|target| Isa::new(target).is_none())
                .collect::<Vec<_>>();
            let isa_targets = targets.split(',').flat_map(Isa::new).collect::<Vec<_>>();
            match (unknown_isa_targets.is_empty(), isa_targets.is_empty()) {
                (true, true) => Ok(Isa::all().to_vec()),
                (true, _) => Ok(isa_targets),
                (_, _) => Err(format!(
                    "unknown isa targets: `{}`",
                    unknown_isa_targets.join(", ")
                )),
            }
        }
        None => Ok(Isa::all().to_vec()),
    }
}
<commit_msg>Improve the error message when python isn't installed.<commit_after>// Build script.
//
// This program is run by Cargo when building lib/codegen. It is used to generate Rust code from
// the language definitions in the lib/codegen/meta directory.
//
// Environment:
//
// OUT_DIR
//     Directory where generated files should be placed.
//
// TARGET
//     Target triple provided by Cargo.
//
// CRETONNE_TARGETS (Optional)
//     A setting for conditional compilation of isa targets. Possible values can be "native" or
//     known isa targets separated by ','.
//
// The build script expects to be run from the directory where this build.rs file lives. The
// current directory is used to find the sources.

use std::env;
use std::process;

fn main() {
    let out_dir = env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set");
    let target_triple = env::var("TARGET").expect("The TARGET environment variable must be set");
    let cretonne_targets = env::var("CRETONNE_TARGETS").ok();
    let cretonne_targets = cretonne_targets.as_ref().map(|s| s.as_ref());

    // Configure isa targets cfg.
    match isa_targets(cretonne_targets, &target_triple) {
        Ok(isa_targets) => {
            for isa in &isa_targets {
                println!("cargo:rustc-cfg=build_{}", isa.name());
            }
        }
        Err(err) => {
            eprintln!("Error: {}", err);
            process::exit(1);
        }
    }

    println!("Build script generating files in {}", out_dir);

    let cur_dir = env::current_dir().expect("Can't access current working directory");
    let crate_dir = cur_dir.as_path();

    // Make sure we rebuild if this build script changes.
    // I guess that won't happen if you have non-UTF8 bytes in your path names.
    // The `build.py` script prints out its own dependencies.
    println!(
        "cargo:rerun-if-changed={}",
        crate_dir.join("build.rs").to_string_lossy()
    );

    // Scripts are in `$crate_dir/meta`.
    let meta_dir = crate_dir.join("meta");
    let build_script = meta_dir.join("build.py");

    // Launch build script with Python. We'll just find python in the path.
    // Use -B to disable .pyc files, because they cause trouble for vendoring
    // scripts, and this is a build step that isn't run very often anyway.
    let status = process::Command::new("python")
        .current_dir(crate_dir)
        .arg("-B")
        .arg(build_script)
        .arg("--out-dir")
        .arg(out_dir)
        .status()
        .expect(
            "Failed to launch second-level build script; is python installed?",
        );
    if !status.success() {
        process::exit(status.code().unwrap());
    }
}

/// Represents known ISA target.
#[derive(Copy, Clone)]
enum Isa {
    Riscv,
    X86,
    Arm32,
    Arm64,
}

impl Isa {
    /// Creates isa target using name.
    fn new(name: &str) -> Option<Self> {
        Isa::all()
            .iter()
            .cloned()
            .filter(|isa| isa.name() == name)
            .next()
    }

    /// Creates isa target from arch.
    fn from_arch(arch: &str) -> Option<Isa> {
        Isa::all()
            .iter()
            .cloned()
            .filter(|isa| isa.is_arch_applicable(arch))
            .next()
    }

    /// Returns all supported isa targets.
    fn all() -> [Isa; 4] {
        [Isa::Riscv, Isa::X86, Isa::Arm32, Isa::Arm64]
    }

    /// Returns name of the isa target.
    fn name(&self) -> &'static str {
        match *self {
            Isa::Riscv => "riscv",
            Isa::X86 => "x86",
            Isa::Arm32 => "arm32",
            Isa::Arm64 => "arm64",
        }
    }

    /// Checks if arch is applicable for the isa target.
    fn is_arch_applicable(&self, arch: &str) -> bool {
        match *self {
            Isa::Riscv => arch == "riscv",
            Isa::X86 => ["x86_64", "i386", "i586", "i686"].contains(&arch),
            Isa::Arm32 => arch.starts_with("arm") || arch.starts_with("thumb"),
            Isa::Arm64 => arch == "aarch64",
        }
    }
}

/// Returns isa targets to configure conditional compilation.
fn isa_targets(cretonne_targets: Option<&str>, target_triple: &str) -> Result<Vec<Isa>, String> {
    match cretonne_targets {
        Some("native") => {
            Isa::from_arch(target_triple.split('-').next().unwrap())
                .map(|isa| vec![isa])
                .ok_or_else(|| {
                    format!(
                        "no supported isa found for target triple `{}`",
                        target_triple
                    )
                })
        }
        Some(targets) => {
            let unknown_isa_targets = targets
                .split(',')
                .filter(|target| Isa::new(target).is_none())
                .collect::<Vec<_>>();
            let isa_targets = targets.split(',').flat_map(Isa::new).collect::<Vec<_>>();
            match (unknown_isa_targets.is_empty(), isa_targets.is_empty()) {
                (true, true) => Ok(Isa::all().to_vec()),
                (true, _) => Ok(isa_targets),
                (_, _) => Err(format!(
                    "unknown isa targets: `{}`",
                    unknown_isa_targets.join(", ")
                )),
            }
        }
        None => Ok(Isa::all().to_vec()),
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use super::bits::U16Field;
use super::bits::U32Field;
use super::packet::Block;
use super::packet::Packet;
use super::packet::Piece;
use super::domain_name::DomainName;

const TYPE:U16Field = U16Field{index:0};
const CLASS:U16Field = U16Field{index:2};
const TTL:U32Field = U32Field{index:4};
const LENGTH:U16Field = U16Field{index:8};
const SIZE:usize = 4;

struct ResourceData<'d> {
    data: Piece<'d>,
}

pub struct Resource<'d> {
    name: DomainName<'d>,
    footer: ResourceData<'d>,
    data: Piece<'d>,
}

impl<'d> ResourceData<'d> {

    fn rtype(&self) -> Option<u16> { TYPE.get(self.data.data()) }
    fn rclass(&self) -> Option<u16> { CLASS.get(self.data.data()) }
    fn ttl(&self) -> Option<u32> { TTL.get(self.data.data()) }
    fn data_length(&self) -> Option<u16> { LENGTH.get(self.data.data()) }
}

impl<'d> Resource<'d> {
    pub fn name<'a>(&'a self) -> Option<&'a DomainName<'d>> { Some(&self.name) }
}

impl<'d> Block<'d, Resource<'d>> for Resource<'d> {
    fn at<'p>(src: &'p mut Packet<'d>, at:usize) -> Resource<'d> {
        let name = src.next::<DomainName<'d>>();
        let footer = src.next::<ResourceData<'d>>();
        let payload = src.next_slice(footer.data_length().unwrap_or(0) as usize);
        Resource { name: name, footer: footer, data:payload }
    }
}

impl<'d> Block<'d, ResourceData<'d>> for ResourceData<'d> {
    fn at<'p>(src: &'p mut Packet<'d>, at:usize) -> ResourceData<'d> {
        ResourceData { data: src.next_slice(SIZE) }
    }
}

impl<'d> fmt::Debug for Resource<'d> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Resource")
            .field("name", &self.name())
            .field("rtype", &self.footer.rtype())
            .field("rclass", &self.footer.rclass())
            .field("ttl", &self.footer.ttl())
            .field("rdata length", &self.footer.data_length())
            .field("rdata", &self.data)
            .finish()
    }
}

#[cfg(test)]
mod test {
    use protocol::packet::Packet;
    use super::Resource;

    #[test]
    fn resource() {
        let data = &[0];
        let mut p = Packet::new(data);
        let q = p.next::<Resource>();
    }

}
<commit_msg>Port resource to new bits.<commit_after>use std::fmt;
use super::bits::BitData;
use super::bits::BEU16Field;
use super::bits::BEU32Field;
use super::domain_name::DomainName;
use std::ops::Range;

const TYPE:BEU16Field = BEU16Field{index:0};
const CLASS:BEU16Field = BEU16Field{index:2};
const TTL:BEU32Field = BEU32Field{index:4};
const LENGTH:BEU16Field = BEU16Field{index:8};
const SIZE:usize = 10;

pub struct Resource<'d> {
    name: DomainName<'d>,
    footer: &'d [u8],
}

impl<'d> Resource<'d> {
    pub fn name<'a>(&'a self) -> Option<&'a DomainName<'d>> { Some(&self.name) }
    pub fn payload_range(&self) -> Option<Range<usize>> {
        if let Some(len) = self.data_length() {
            return Some(Range {
                start:self.name.end_offset() + SIZE,
                end: self.name.end_offset() + SIZE + len as usize
            });
        }
        None
    }

    pub fn payload<D:?Sized + BitData>(&self, message: &'d D) -> Option<&'d <D as BitData>::Slice> {
        if let Some(range) = self.payload_range() {
            return message.get_range(range);
        }
        None
    }

    pub fn rtype(&self) -> Option<u16> { TYPE.get(self.footer) }
    pub fn rclass(&self) -> Option<u16> { CLASS.get(self.footer) }
    pub fn ttl(&self) -> Option<u32> { TTL.get(self.footer) }
    pub fn data_length(&self) -> Option<u16> { LENGTH.get(self.footer) }
}

impl<'d> Resource<'d> {
    fn from_message<D:'d + ?Sized + BitData<Slice=[u8]>>(message: &'d D, at:usize) -> Option<Resource<'d>> {
        if let Some(name) = DomainName::from_message(message, at) {
            if let Some(footer) = message.get_range(Range{start:name.end_offset(), end:name.end_offset() + SIZE}) {
                return Some(Resource { name: name, footer: footer })
            }
        }
        None
    }
}

impl<'d> fmt::Debug for Resource<'d> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Resource")
            .field("name", &self.name())
            .field("rtype", &self.rtype())
            .field("rclass", &self.rclass())
            .field("ttl", &self.ttl())
            .field("rdata length", &self.data_length())
            .finish()
    }
}

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

    #[test]
    fn payload() {
        let data = &[0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 2, 0xaa, 0xab][..];
        let r = Resource::from_message(data, 0).unwrap();
        assert_eq!(Some(1), r.rtype());
        assert_eq!(Some(2), r.rclass());
        assert_eq!(Some(3), r.ttl());
        assert_eq!(Some(2), r.data_length());

        // Truncated packet:
        assert_eq!(2, r.payload(data).unwrap().len());
        assert_eq!(0xaa, r.payload(data).unwrap()[0]);
        assert_eq!(0xab, r.payload(data).unwrap()[1]);
    }

    #[test]
    fn truncated_resource() {
        let data = &[0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 4][..];
        let r = Resource::from_message(data, 0).unwrap();
        assert_eq!(Some(1), r.rtype());
        assert_eq!(Some(2), r.rclass());
        assert_eq!(Some(3), r.ttl());
        assert_eq!(Some(4), r.data_length());

        // Truncated packet:
        assert!(r.payload(data).is_none());
    }

}
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2015 Y. T. Chung <zonyitoo@gmail.com>

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

//! Processing unit of a thread

use std::cell::UnsafeCell;
use std::io;
#[cfg(target_os = "linux")]
use std::os::unix::io::AsRawFd;
#[cfg(target_os = "linux")]
use std::convert::From;
// use std::sync::Arc;
use std::thread;
use std::mem;
use std::sync::mpsc::{self, Sender, Receiver};
use std::boxed::FnBox;

use mio::{EventLoop, Evented, Handler, Token, EventSet, PollOpt};
use mio::util::Slab;
#[cfg(target_os = "linux")]
use mio::Io;

// use mio::util::BoundedQueue;

use deque::{BufferPool, Stolen, Worker, Stealer};

use rand;

use scheduler::Scheduler;
use coroutine::{self, Coroutine, State, Handle, SendableCoroutinePtr};
use options::Options;

const MAX_TOKEN_NUM: usize = 102400;

thread_local!(static PROCESSOR: UnsafeCell<Processor> = UnsafeCell::new(Processor::new()));

/// Processing unit of a thread
pub struct Processor {
    event_loop: EventLoop<Processor>,

    main_coro: Handle,
    cur_running: Option<*mut Coroutine>,
    last_result: Option<coroutine::Result<State>>,

    queue_worker: Worker<SendableCoroutinePtr>,
    queue_stealer: Stealer<SendableCoroutinePtr>,
    neighbor_stealers: Vec<Stealer<SendableCoroutinePtr>>,

    chan_sender: Sender<ProcMessage>,
    chan_receiver: Receiver<ProcMessage>,

    is_scheduling: bool,
    has_ready_tasks: bool,

    #[cfg(any(target_os = "linux",
              target_os = "android"))]
    io_slabs: Slab<(*mut Coroutine, Io)>,
    #[cfg(any(target_os = "macos",
              target_os = "freebsd",
              target_os = "dragonfly",
              target_os = "ios",
              target_os = "bitrig",
              target_os = "openbsd"))]
    io_slabs: Slab<*mut Coroutine>,
    #[cfg(windows)]
    io_slabs: Slab<*mut Coroutine>,

    timer_slabs: Slab<*mut Coroutine>,
}

impl Processor {
    fn new() -> Processor {
        Processor::new_with_neighbors(Vec::new())
    }

    fn new_with_neighbors(neigh: Vec<Stealer<SendableCoroutinePtr>>) -> Processor {
        let main_coro = unsafe {
            Coroutine::empty()
        };

        let (worker, stealer) = BufferPool::new().deque();
        let (tx, rx) = mpsc::channel();

        Processor {
            event_loop: EventLoop::new().expect("Unable to create the eventloop"),

            main_coro: main_coro,
            cur_running: None,
            last_result: None,

            queue_worker: worker,
            queue_stealer: stealer,
            neighbor_stealers: neigh,

            chan_sender: tx,
            chan_receiver: rx,

            is_scheduling: false,
            has_ready_tasks: false,

            io_slabs: Slab::new(MAX_TOKEN_NUM),

            timer_slabs: Slab::new(MAX_TOKEN_NUM),
        }
    }

    #[inline]
    // Get the current running coroutine
    pub unsafe fn running(&mut self) -> Option<*mut Coroutine> {
        self.cur_running
    }

    #[inline]
    pub fn set_neighbors(&mut self, neigh: Vec<Stealer<SendableCoroutinePtr>>) {
        self.neighbor_stealers = neigh;
    }

    /// Get the thread local processor
    #[inline]
    pub fn current() -> &'static mut Processor {
        PROCESSOR.with(|p| unsafe { &mut *p.get() })
    }

    #[inline]
    pub fn stealer(&self) -> Stealer<SendableCoroutinePtr> {
        self.queue_stealer.clone()
    }

    #[inline]
    pub fn handle(&self) -> Sender<ProcMessage> {
        self.chan_sender.clone()
    }

    #[inline]
    // Call by scheduler
    pub unsafe fn ready(&mut self, coro_ptr: *mut Coroutine) {
        self.has_ready_tasks = true;
        self.queue_worker.push(SendableCoroutinePtr(coro_ptr));
    }

    #[inline]
    pub fn spawn_opts<F>(&mut self, f: Box<F>, opts: Options)
        where F: FnBox() + 'static
    {
        let coro = Coroutine::spawn_opts(f, opts);

        unsafe {
            self.ready(mem::transmute(coro));
        }
        self.sched();
    }

    #[inline]
    fn set_last_result(&mut self, r: coroutine::Result<State>) {
        self.last_result = Some(r);
    }

    #[inline]
    unsafe fn run_with_all_local_tasks(&mut self, coro_ptr: *mut Coroutine) {
        let mut hdl = coro_ptr;
        loop {
            let is_suspended = match self.resume(hdl) {
                Ok(State::Suspended) => {
                    true
                },
                Ok(State::Finished) | Err(..) => {
                    Scheduler::finished(hdl);
                    false
                },
                Ok(..) => {
                    false
                }
            };

            // Try to fetch one task from the local queue
            let next_hdl = self.queue_worker.pop();
            if is_suspended {
                // If the current task has to be suspended, then
                // push it back to the local work queue
                self.ready(hdl);
            }

            match next_hdl {
                Some(h) => hdl = h.0,
                None => break
            }
        }
    }

    /// Run the processor
    pub fn schedule(&mut self) -> io::Result<()> {
        self.is_scheduling = true;

        'outerloop:
        loop {
            // 1. Run all tasks in local queue
            if let Some(hdl) = self.queue_worker.pop() {
                unsafe {
                    self.run_with_all_local_tasks(hdl.0);
                }
            } else {
                self.has_ready_tasks = false;
            }

            // 2. Get work from timer
            //    In favor of mio's timer implementation

            // 3. Well, check if there are any works could be waked up
            if self.io_slabs.count() != 0 || self.timer_slabs.count() != 0 {
                // Make the borrow checker happy.
                let proc_ptr: *mut Processor = self;
                if let Err(err) = self.event_loop.run_once(unsafe { &mut *proc_ptr }) {
                    self.is_scheduling = false;
                    error!("EventLoop failed with {:?}", err);
                    return Err(err);
                }

                if self.has_ready_tasks {
                    continue 'outerloop;
                }
            } else if Scheduler::instance().work_count() == 0 {
                break;
            } else {
                // We don't have active tasks in the local queue
                // And we don't have any activated tasks from event loop
            }

            // 4. Randomly steal from neighbors
            let rand_idx = rand::random::<usize>();
            let total_stealers = self.neighbor_stealers.len();
            for idx in (0..self.neighbor_stealers.len()).map(|x| (x + rand_idx) % total_stealers) {
                if let Stolen::Data(hdl) = self.neighbor_stealers[idx].steal() {
                    unsafe {
                        self.run_with_all_local_tasks(hdl.0);
                    }
                    continue 'outerloop;
                }
            }

            // 5. Check the mailbox
            while let Ok(msg) = self.chan_receiver.try_recv() {
                match msg {
                    ProcMessage::NewNeighbor(nei) => self.neighbor_stealers.push(nei),
                }
            }

            thread::sleep_ms(100);
        }

        self.is_scheduling = false;

        Ok(())
    }

    #[inline]
    pub fn resume(&mut self, coro_ptr: *mut Coroutine) -> coroutine::Result<State> {
        self.cur_running = Some(coro_ptr);
        unsafe {
            self.main_coro.yield_to(&mut *coro_ptr);
        }

        match self.last_result.take() {
            None => Ok(State::Suspended),
            Some(r) => r,
        }
    }

    /// Block the current coroutine until the specific time
    #[inline]
    pub fn sleep_ms(&mut self, delay: u64) {
        if let Some(coro_ptr) = unsafe { self.running() } {
            // Set into the timer
            loop {
                match self.timer_slabs.insert(coro_ptr) {
                    Ok(token) => {
                        debug!("Going to block with delay {} and {:?}", delay, token);
                        self.event_loop.timeout_ms(token, delay).unwrap();
                        break;
                    },
                    Err(..) => {}
                }
            }

            // Just block it, the handle has already been sent to
            // the timer.
            self.block();
        } else {
            warn!("Called `wait_until` without running coroutine");
        }
    }

    /// Suspended the current running coroutine, equivalent to `Scheduler::sched`
    #[inline]
    pub fn sched(&mut self) {
        match self.cur_running.take() {
            None => {},
            Some(coro_ptr) => unsafe {
                self.set_last_result(Ok(State::Suspended));
                (&mut *coro_ptr).yield_to(&*self.main_coro)
            }
        }
    }

    /// Block the current running coroutine, equivalent to `Scheduler::block`
    #[inline]
    pub fn block(&mut self) {
        match self.cur_running.take() {
            None => {},
            Some(coro_ptr) => unsafe {
                self.set_last_result(Ok(State::Blocked));
                (&mut *coro_ptr).yield_to(&*self.main_coro);
            }
        }
    }

    /// Yield the current running coroutine with specified result
    #[inline]
    pub fn yield_with(&mut self, r: coroutine::Result<State>) {
        match self.cur_running.take() {
            None => {},
            Some(coro_ptr) => unsafe {
                self.set_last_result(r);
                (&mut *coro_ptr).yield_to(&*self.main_coro)
            }
        }
    }
}

impl Processor {
    /// Register and wait I/O
    #[cfg(any(target_os = "linux",
              target_os = "android"))]
    pub fn wait_event<E: Evented + AsRawFd>(&mut self, fd: &E, interest: EventSet) -> io::Result<()> {
        let token = self.io_slabs.insert((unsafe { Processor::current().running().unwrap() },
                                               From::from(fd.as_raw_fd()))).unwrap();
        try!(self.event_loop.register_opt(fd, token, interest,
                                          PollOpt::edge()|PollOpt::oneshot()));

        debug!("wait_event: Blocked current Coroutine ...; token={:?}", token);
        self.block();
        debug!("wait_event: Waked up; token={:?}", token);

        Ok(())
    }

    #[cfg(any(target_os = "macos",
              target_os = "freebsd",
              target_os = "dragonfly",
              target_os = "ios",
              target_os = "bitrig",
              target_os = "openbsd"))]
    pub fn wait_event<E: Evented>(&mut self, fd: &E, interest: EventSet) -> io::Result<()> {
        let token = self.io_slabs.insert(unsafe { Processor::current().running().unwrap() }).unwrap();
        try!(self.event_loop.register_opt(fd, token, interest,
                                          PollOpt::edge()|PollOpt::oneshot()));

        debug!("wait_event: Blocked current Coroutine ...; token={:?}", token);
        self.block();
        debug!("wait_event: Waked up; token={:?}", token);

        Ok(())
    }

    #[cfg(windows)]
    pub fn wait_event<E: Evented>(&mut self, fd: &E, interest: EventSet) -> io::Result<()> {
        let token = self.io_slabs.insert(unsafe { Processor::current().running().unwrap() }).unwrap();
        try!(self.event_loop.register_opt(fd, token, interest,
                                          PollOpt::edge()|PollOpt::oneshot()));

        debug!("wait_event: Blocked current Coroutine ...; token={:?}", token);
        self.block();
        debug!("wait_event: Waked up; token={:?}", token);

        Ok(())
    }
}

impl Handler for Processor {
    type Timeout = Token;
    type Message = ();

    #[cfg(any(target_os = "linux",
              target_os = "android"))]
    fn ready(&mut self, event_loop: &mut EventLoop<Self>, token: Token, events: EventSet) {
        debug!("Got {:?} for {:?}", events, token);

        match self.io_slabs.remove(token) {
            Some((hdl, fd)) => {
                // Linux EPoll needs to explicit EPOLL_CTL_DEL the fd
                event_loop.deregister(&fd).unwrap();
                mem::forget(fd);
                unsafe {
                    self.ready(hdl);
                }
            },
            None => {
                warn!("No coroutine is waiting on token {:?}", token);
            }
        }
    }

    #[cfg(any(target_os = "macos",
              target_os = "freebsd",
              target_os = "dragonfly",
              target_os = "ios",
              target_os = "bitrig",
              target_os = "openbsd"))]
    fn ready(&mut self, _: &mut EventLoop<Self>, token: Token, events: EventSet) {
        debug!("Got {:?} for {:?}", events, token);

        match self.io_slabs.remove(token) {
            Some(hdl) => {
                unsafe {
                    self.ready(hdl);
                }
            },
            None => {
                warn!("No coroutine is waiting on token {:?}", token);
            }
        }
    }

    #[cfg(windows)]
    fn ready(&mut self, _: &mut EventLoop<Self>, token: Token, events: EventSet) {
        debug!("Got {:?} for {:?}", events, token);

        match self.io_slabs.remove(token) {
            Some(hdl) => {
                unsafe {
                    self.ready(hdl);
                }
            },
            None => {
                warn!("No coroutine is waiting on token {:?}", token);
            }
        }
    }

    fn timeout(&mut self, _: &mut EventLoop<Self>, token: Token) {
        debug!("Timer waked up {:?}", token);
        match self.timer_slabs.remove(token) {
            Some(coro_ptr) => unsafe {
                self.ready(coro_ptr);
            },
            None => {
                warn!("Timer token {:?} was awaited without waiting coroutines", token);
            }
        }
    }
}

pub enum ProcMessage {
    NewNeighbor(Stealer<SendableCoroutinePtr>),
}
<commit_msg>Returned back old style.<commit_after>// The MIT License (MIT)

// Copyright (c) 2015 Y. T. Chung <zonyitoo@gmail.com>

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

//! Processing unit of a thread

use std::cell::UnsafeCell;
use std::io;
#[cfg(target_os = "linux")]
use std::os::unix::io::AsRawFd;
#[cfg(target_os = "linux")]
use std::convert::From;
// use std::sync::Arc;
use std::thread;
use std::mem;
use std::sync::mpsc::{self, Sender, Receiver};
use std::boxed::FnBox;

use mio::{EventLoop, Evented, Handler, Token, EventSet, PollOpt};
use mio::util::Slab;
#[cfg(target_os = "linux")]
use mio::Io;

// use mio::util::BoundedQueue;

use deque::{BufferPool, Stolen, Worker, Stealer};

use rand;

use scheduler::Scheduler;
use coroutine::{self, Coroutine, State, Handle, SendableCoroutinePtr};
use options::Options;

const MAX_TOKEN_NUM: usize = 102400;

thread_local!(static PROCESSOR: UnsafeCell<Processor> = UnsafeCell::new(Processor::new()));

/// Processing unit of a thread
pub struct Processor {
    event_loop: EventLoop<Processor>,

    main_coro: Handle,
    cur_running: Option<*mut Coroutine>,
    last_result: Option<coroutine::Result<State>>,

    queue_worker: Worker<SendableCoroutinePtr>,
    queue_stealer: Stealer<SendableCoroutinePtr>,
    neighbor_stealers: Vec<Stealer<SendableCoroutinePtr>>,

    chan_sender: Sender<ProcMessage>,
    chan_receiver: Receiver<ProcMessage>,

    is_scheduling: bool,
    has_ready_tasks: bool,

    #[cfg(any(target_os = "linux",
              target_os = "android"))]
    io_slabs: Slab<(*mut Coroutine, Io)>,
    #[cfg(any(target_os = "macos",
              target_os = "freebsd",
              target_os = "dragonfly",
              target_os = "ios",
              target_os = "bitrig",
              target_os = "openbsd"))]
    io_slabs: Slab<*mut Coroutine>,
    #[cfg(windows)]
    io_slabs: Slab<*mut Coroutine>,

    timer_slabs: Slab<*mut Coroutine>,
}

impl Processor {
    fn new() -> Processor {
        Processor::new_with_neighbors(Vec::new())
    }

    fn new_with_neighbors(neigh: Vec<Stealer<SendableCoroutinePtr>>) -> Processor {
        let main_coro = unsafe {
            Coroutine::empty()
        };

        let (worker, stealer) = BufferPool::new().deque();
        let (tx, rx) = mpsc::channel();

        Processor {
            event_loop: EventLoop::new().expect("Unable to create the eventloop"),

            main_coro: main_coro,
            cur_running: None,
            last_result: None,

            queue_worker: worker,
            queue_stealer: stealer,
            neighbor_stealers: neigh,

            chan_sender: tx,
            chan_receiver: rx,

            is_scheduling: false,
            has_ready_tasks: false,

            io_slabs: Slab::new(MAX_TOKEN_NUM),

            timer_slabs: Slab::new(MAX_TOKEN_NUM),
        }
    }

    #[inline]
    // Get the current running coroutine
    pub unsafe fn running(&mut self) -> Option<*mut Coroutine> {
        self.cur_running
    }

    #[inline]
    pub fn set_neighbors(&mut self, neigh: Vec<Stealer<SendableCoroutinePtr>>) {
        self.neighbor_stealers = neigh;
    }

    /// Get the thread local processor
    #[inline]
    pub fn current() -> &'static mut Processor {
        PROCESSOR.with(|p| unsafe { &mut *p.get() })
    }

    #[inline]
    pub fn stealer(&self) -> Stealer<SendableCoroutinePtr> {
        self.queue_stealer.clone()
    }

    #[inline]
    pub fn handle(&self) -> Sender<ProcMessage> {
        self.chan_sender.clone()
    }

    #[inline]
    // Call by scheduler
    pub unsafe fn ready(&mut self, coro_ptr: *mut Coroutine) {
        self.has_ready_tasks = true;
        self.queue_worker.push(SendableCoroutinePtr(coro_ptr));
    }

    #[inline]
    pub fn spawn_opts<F>(&mut self, f: Box<F>, opts: Options)
        where F: FnBox() + 'static
    {
        let coro = Coroutine::spawn_opts(f, opts);

        unsafe {
            self.ready(mem::transmute(coro));
        }
        self.sched();
    }

    #[inline]
    fn set_last_result(&mut self, r: coroutine::Result<State>) {
        self.last_result = Some(r);
    }

    #[inline]
    unsafe fn run_with_all_local_tasks(&mut self, coro_ptr: *mut Coroutine) {
        let mut hdl = coro_ptr;
        loop {
            let is_suspended = match self.resume(hdl) {
                Ok(State::Suspended) => {
                    true
                },
                Ok(State::Finished) | Err(..) => {
                    Scheduler::finished(hdl);
                    false
                },
                Ok(..) => {
                    false
                }
            };

            // Try to fetch one task from the local queue
            let next_hdl = self.queue_worker.pop();
            if is_suspended {
                // If the current task has to be suspended, then
                // push it back to the local work queue
                self.ready(hdl);
            }

            match next_hdl {
                Some(h) => hdl = h.0,
                None => break
            }
        }
    }

    /// Run the processor
    pub fn schedule(&mut self) -> io::Result<()> {
        self.is_scheduling = true;

        'outerloop:
        loop {
            // 1. Run all tasks in local queue
            if let Some(hdl) = self.queue_worker.pop() {
                unsafe {
                    self.run_with_all_local_tasks(hdl.0);
                }
            } else {
                self.has_ready_tasks = false;
            }

            // 2. Get work from timer
            //    In favor of mio's timer implementation

            // 3. Well, check if there are any works could be waked up
            if self.io_slabs.count() != 0 || self.timer_slabs.count() != 0 {
                // Make the borrow checker happy.
                let proc_ptr: *mut Processor = self;
                if let Err(err) = self.event_loop.run_once(unsafe { &mut *proc_ptr }) {
                    self.is_scheduling = false;
                    error!("EventLoop failed with {:?}", err);
                    return Err(err);
                }

                if self.has_ready_tasks {
                    continue 'outerloop;
                }
            } else if Scheduler::instance().work_count() == 0 {
                break;
            } else {
                // We don't have active tasks in the local queue
                // And we don't have any activated tasks from event loop
            }

            // 4. Randomly steal from neighbors
            let rand_idx = rand::random::<usize>();
            let total_stealers = self.neighbor_stealers.len();
            for idx in (0..self.neighbor_stealers.len()).map(|x| (x + rand_idx) % total_stealers) {
                if let Stolen::Data(hdl) = self.neighbor_stealers[idx].steal() {
                    unsafe {
                        self.run_with_all_local_tasks(hdl.0);
                    }
                    continue 'outerloop;
                }
            }

            // 5. Check the mailbox
            while let Ok(msg) = self.chan_receiver.try_recv() {
                match msg {
                    ProcMessage::NewNeighbor(nei) => self.neighbor_stealers.push(nei),
                }
            }

            thread::sleep_ms(100);
        }

        self.is_scheduling = false;

        Ok(())
    }

    #[inline]
    pub fn resume(&mut self, coro_ptr: *mut Coroutine) -> coroutine::Result<State> {
        self.cur_running = Some(coro_ptr);
        unsafe {
            self.main_coro.yield_to(&mut *coro_ptr);
        }

        match self.last_result.take() {
            None => Ok(State::Suspended),
            Some(r) => r,
        }
    }

    /// Block the current coroutine until the specific time
    #[inline]
    pub fn sleep_ms(&mut self, delay: u64) {
        if let Some(coro_ptr) = unsafe { self.running() } {
            // Set into the timer
            loop {
                match self.timer_slabs.insert(coro_ptr) {
                    Ok(token) => {
                        debug!("Going to block with delay {} and {:?}", delay, token);
                        self.event_loop.timeout_ms(token, delay).unwrap();
                        break;
                    },
                    Err(..) => {}
                }
            }

            // Just block it, the handle has already been sent to
            // the timer.
            self.block();
        } else {
            warn!("Called `wait_until` without running coroutine");
        }
    }

    /// Suspended the current running coroutine, equivalent to `Scheduler::sched`
    #[inline]
    pub fn sched(&mut self) {
        match self.cur_running.take() {
            None => {},
            Some(coro_ptr) => unsafe {
                self.set_last_result(Ok(State::Suspended));
                (&mut *coro_ptr).yield_to(&*self.main_coro)
            }
        }
    }

    /// Block the current running coroutine, equivalent to `Scheduler::block`
    #[inline]
    pub fn block(&mut self) {
        match self.cur_running.take() {
            None => {},
            Some(coro_ptr) => unsafe {
                self.set_last_result(Ok(State::Blocked));
                (&mut *coro_ptr).yield_to(&*self.main_coro)
            }
        }
    }

    /// Yield the current running coroutine with specified result
    #[inline]
    pub fn yield_with(&mut self, r: coroutine::Result<State>) {
        match self.cur_running.take() {
            None => {},
            Some(coro_ptr) => unsafe {
                self.set_last_result(r);
                (&mut *coro_ptr).yield_to(&*self.main_coro)
            }
        }
    }
}

impl Processor {
    /// Register and wait I/O
    #[cfg(any(target_os = "linux",
              target_os = "android"))]
    pub fn wait_event<E: Evented + AsRawFd>(&mut self, fd: &E, interest: EventSet) -> io::Result<()> {
        let token = self.io_slabs.insert((unsafe { Processor::current().running().unwrap() },
                                               From::from(fd.as_raw_fd()))).unwrap();
        try!(self.event_loop.register_opt(fd, token, interest,
                                          PollOpt::edge()|PollOpt::oneshot()));

        debug!("wait_event: Blocked current Coroutine ...; token={:?}", token);
        self.block();
        debug!("wait_event: Waked up; token={:?}", token);

        Ok(())
    }

    #[cfg(any(target_os = "macos",
              target_os = "freebsd",
              target_os = "dragonfly",
              target_os = "ios",
              target_os = "bitrig",
              target_os = "openbsd"))]
    pub fn wait_event<E: Evented>(&mut self, fd: &E, interest: EventSet) -> io::Result<()> {
        let token = self.io_slabs.insert(unsafe { Processor::current().running().unwrap() }).unwrap();
        try!(self.event_loop.register_opt(fd, token, interest,
                                          PollOpt::edge()|PollOpt::oneshot()));

        debug!("wait_event: Blocked current Coroutine ...; token={:?}", token);
        self.block();
        debug!("wait_event: Waked up; token={:?}", token);

        Ok(())
    }

    #[cfg(windows)]
    pub fn wait_event<E: Evented>(&mut self, fd: &E, interest: EventSet) -> io::Result<()> {
        let token = self.io_slabs.insert(unsafe { Processor::current().running().unwrap() }).unwrap();
        try!(self.event_loop.register_opt(fd, token, interest,
                                          PollOpt::edge()|PollOpt::oneshot()));

        debug!("wait_event: Blocked current Coroutine ...; token={:?}", token);
        self.block();
        debug!("wait_event: Waked up; token={:?}", token);

        Ok(())
    }
}

impl Handler for Processor {
    type Timeout = Token;
    type Message = ();

    #[cfg(any(target_os = "linux",
              target_os = "android"))]
    fn ready(&mut self, event_loop: &mut EventLoop<Self>, token: Token, events: EventSet) {
        debug!("Got {:?} for {:?}", events, token);

        match self.io_slabs.remove(token) {
            Some((hdl, fd)) => {
                // Linux EPoll needs to explicit EPOLL_CTL_DEL the fd
                event_loop.deregister(&fd).unwrap();
                mem::forget(fd);
                unsafe {
                    self.ready(hdl);
                }
            },
            None => {
                warn!("No coroutine is waiting on token {:?}", token);
            }
        }
    }

    #[cfg(any(target_os = "macos",
              target_os = "freebsd",
              target_os = "dragonfly",
              target_os = "ios",
              target_os = "bitrig",
              target_os = "openbsd"))]
    fn ready(&mut self, _: &mut EventLoop<Self>, token: Token, events: EventSet) {
        debug!("Got {:?} for {:?}", events, token);

        match self.io_slabs.remove(token) {
            Some(hdl) => {
                unsafe {
                    self.ready(hdl);
                }
            },
            None => {
                warn!("No coroutine is waiting on token {:?}", token);
            }
        }
    }

    #[cfg(windows)]
    fn ready(&mut self, _: &mut EventLoop<Self>, token: Token, events: EventSet) {
        debug!("Got {:?} for {:?}", events, token);

        match self.io_slabs.remove(token) {
            Some(hdl) => {
                unsafe {
                    self.ready(hdl);
                }
            },
            None => {
                warn!("No coroutine is waiting on token {:?}", token);
            }
        }
    }

    fn timeout(&mut self, _: &mut EventLoop<Self>, token: Token) {
        debug!("Timer waked up {:?}", token);
        match self.timer_slabs.remove(token) {
            Some(coro_ptr) => unsafe {
                self.ready(coro_ptr);
            },
            None => {
                warn!("Timer token {:?} was awaited without waiting coroutines", token);
            }
        }
    }
}

pub enum ProcMessage {
    NewNeighbor(Stealer<SendableCoroutinePtr>),
}
<|endoftext|>


<commit_before>#[doc = "Generic pass for performing an operation on all descriptions"];

export mk_pass;

fn mk_pass(op: fn~(str) -> str) -> pass {
    fn~(srv: astsrv::srv, doc: doc::cratedoc) -> doc::cratedoc {
        run(srv, doc, op)
    }
}

type op = fn~(str) -> str;

fn run(
    _srv: astsrv::srv,
    doc: doc::cratedoc,
    op: op
) -> doc::cratedoc {
    let fold = fold::fold({
        fold_mod: fold_mod,
        fold_const: fold_const,
        fold_fn: fold_fn,
        fold_enum: fold_enum,
        fold_res: fold_res,
        fold_iface: fold_iface,
        fold_impl: fold_impl,
        fold_type: fold_type
        with *fold::default_seq_fold(op)
    });
    fold.fold_crate(fold, doc)
}

fn maybe_apply_op(op: op, s: option<str>) -> option<str> {
    option::map(s) {|s| op(s) }
}

fn fold_mod(fold: fold::fold<op>, doc: doc::moddoc) -> doc::moddoc {
    let doc = fold::default_seq_fold_mod(fold, doc);

    {
        item: {
            brief: maybe_apply_op(fold.ctxt, doc.brief()),
            desc: maybe_apply_op(fold.ctxt, doc.desc())
            with doc.item
        }
        with doc
    }
}

fn fold_const(fold: fold::fold<op>, doc: doc::constdoc) -> doc::constdoc {
    let doc = fold::default_seq_fold_const(fold, doc);

    {
        item: {
            brief: maybe_apply_op(fold.ctxt, doc.brief()),
            desc: maybe_apply_op(fold.ctxt, doc.desc())
            with doc.item
        }
        with doc
    }
}

fn fold_fn(fold: fold::fold<op>, doc: doc::fndoc) -> doc::fndoc {
    let doc = fold::default_seq_fold_fn(fold, doc);

    {
        item: {
            brief: maybe_apply_op(fold.ctxt, doc.brief()),
            desc: maybe_apply_op(fold.ctxt, doc.desc())
            with doc.item
        },
        args: vec::map(doc.args) {|doc|
            {
                desc: maybe_apply_op(fold.ctxt, doc.desc)
                with doc
            }
        },
        return: {
            desc: maybe_apply_op(fold.ctxt, doc.return.desc)
            with doc.return
        },
        failure: maybe_apply_op(fold.ctxt, doc.failure)
        with doc
    }
}

fn fold_enum(fold: fold::fold<op>, doc: doc::enumdoc) -> doc::enumdoc {
    {
        item: {
            brief: maybe_apply_op(fold.ctxt, doc.brief()),
            desc: maybe_apply_op(fold.ctxt, doc.desc())
            with doc.item
        },
        variants: vec::map(doc.variants) {|variant|
            {
                desc: maybe_apply_op(fold.ctxt, variant.desc)
                with variant
            }
        }
        with doc
    }
}

fn fold_res(fold: fold::fold<op>, doc: doc::resdoc) -> doc::resdoc {
    {
        item: {
            brief: maybe_apply_op(fold.ctxt, doc.brief()),
            desc: maybe_apply_op(fold.ctxt, doc.desc())
            with doc.item
        },
        args: vec::map(doc.args) {|arg|
            {
                desc: maybe_apply_op(fold.ctxt, arg.desc)
                with arg
            }
        }
        with doc
    }
}

fn fold_iface(fold: fold::fold<op>, doc: doc::ifacedoc) -> doc::ifacedoc {
    {
        item: {
            brief: maybe_apply_op(fold.ctxt, doc.brief()),
            desc: maybe_apply_op(fold.ctxt, doc.desc())
            with doc.item
        },
        methods: apply_to_methods(fold.ctxt, doc.methods)
        with doc
    }
}

fn apply_to_methods(op: op, docs: [doc::methoddoc]) -> [doc::methoddoc] {
    vec::map(docs) {|doc|
        {
            brief: maybe_apply_op(op, doc.brief),
            desc: maybe_apply_op(op, doc.desc),
            args: vec::map(doc.args) {|doc|
                {
                    desc: maybe_apply_op(op, doc.desc)
                    with doc
                }
            },
            return: {
                desc: maybe_apply_op(op, doc.return.desc)
                with doc.return
            },
            failure: maybe_apply_op(op, doc.failure)
            with doc
        }
    }
}

fn fold_impl(fold: fold::fold<op>, doc: doc::impldoc) -> doc::impldoc {
    {
        item: {
            brief: maybe_apply_op(fold.ctxt, doc.brief()),
            desc: maybe_apply_op(fold.ctxt, doc.desc())
            with doc.item
        },
        methods: apply_to_methods(fold.ctxt, doc.methods)
        with doc
    }
}

fn fold_type(fold: fold::fold<op>, doc: doc::tydoc) -> doc::tydoc {
    {
        item: {
            brief: maybe_apply_op(fold.ctxt, doc.brief()),
            desc: maybe_apply_op(fold.ctxt, doc.desc())
            with doc.item
        }
        with doc
    }
}

#[test]
fn should_execute_op_on_enum_brief() {
    let doc = test::mk_doc("#[doc(brief = \" a \")] enum a { b }");
    assert doc.topmod.enums()[0].brief() == some("a");
}

#[test]
fn should_execute_op_on_enum_desc() {
    let doc = test::mk_doc("#[doc(desc = \" a \")] enum a { b }");
    assert doc.topmod.enums()[0].desc() == some("a");
}

#[test]
fn should_execute_op_on_variant_desc() {
    let doc = test::mk_doc("enum a { #[doc = \" a \"] b }");
    assert doc.topmod.enums()[0].variants[0].desc == some("a");
}

#[test]
fn should_execute_op_on_resource_brief() {
    let doc = test::mk_doc("#[doc(brief = \" a \")] resource r(a: bool) { }");
    assert doc.topmod.resources()[0].brief() == some("a");
}

#[test]
fn should_execute_op_on_resource_desc() {
    let doc = test::mk_doc("#[doc(desc = \" a \")] resource r(a: bool) { }");
    assert doc.topmod.resources()[0].desc() == some("a");
}

#[test]
fn should_execute_op_on_resource_args() {
    let doc = test::mk_doc(
        "#[doc(args(a = \" a \"))] resource r(a: bool) { }");
    assert doc.topmod.resources()[0].args[0].desc == some("a");
}

#[test]
fn should_execute_op_on_iface_brief() {
    let doc = test::mk_doc(
        "#[doc(brief = \" a \")] iface i { fn a(); }");
    assert doc.topmod.ifaces()[0].brief() == some("a");
}

#[test]
fn should_execute_op_on_iface_desc() {
    let doc = test::mk_doc(
        "#[doc(desc = \" a \")] iface i { fn a(); }");
    assert doc.topmod.ifaces()[0].desc() == some("a");
}

#[test]
fn should_execute_op_on_iface_method_brief() {
    let doc = test::mk_doc(
        "iface i { #[doc(brief = \" a \")] fn a(); }");
    assert doc.topmod.ifaces()[0].methods[0].brief == some("a");
}

#[test]
fn should_execute_op_on_iface_method_desc() {
    let doc = test::mk_doc(
        "iface i { #[doc(desc = \" a \")] fn a(); }");
    assert doc.topmod.ifaces()[0].methods[0].desc == some("a");
}

#[test]
fn should_execute_op_on_iface_method_args() {
    let doc = test::mk_doc(
        "iface i { #[doc(args(a = \" a \"))] fn a(a: bool); }");
    assert doc.topmod.ifaces()[0].methods[0].args[0].desc == some("a");
}

#[test]
fn should_execute_op_on_iface_method_return() {
    let doc = test::mk_doc(
        "iface i { #[doc(return = \" a \")] fn a() -> int; }");
    assert doc.topmod.ifaces()[0].methods[0].return.desc == some("a");
}

#[test]
fn should_execute_op_on_iface_method_failure_condition() {
    let doc = test::mk_doc("iface i { #[doc(failure = \" a \")] fn a(); }");
    assert doc.topmod.ifaces()[0].methods[0].failure == some("a");
}

#[test]
fn should_execute_op_on_impl_brief() {
    let doc = test::mk_doc(
        "#[doc(brief = \" a \")] impl i for int { fn a() { } }");
    assert doc.topmod.impls()[0].brief() == some("a");
}

#[test]
fn should_execute_op_on_impl_desc() {
    let doc = test::mk_doc(
        "#[doc(desc = \" a \")] impl i for int { fn a() { } }");
    assert doc.topmod.impls()[0].desc() == some("a");
}

#[test]
fn should_execute_op_on_impl_method_brief() {
    let doc = test::mk_doc(
        "impl i for int { #[doc(brief = \" a \")] fn a() { } }");
    assert doc.topmod.impls()[0].methods[0].brief == some("a");
}

#[test]
fn should_execute_op_on_impl_method_desc() {
    let doc = test::mk_doc(
        "impl i for int { #[doc(desc = \" a \")] fn a() { } }");
    assert doc.topmod.impls()[0].methods[0].desc == some("a");
}

#[test]
fn should_execute_op_on_impl_method_args() {
    let doc = test::mk_doc(
        "impl i for int { #[doc(args(a = \" a \"))] fn a(a: bool) { } }");
    assert doc.topmod.impls()[0].methods[0].args[0].desc == some("a");
}

#[test]
fn should_execute_op_on_impl_method_return() {
    let doc = test::mk_doc(
        "impl i for int { #[doc(return = \" a \")] fn a() -> int { fail } }");
    assert doc.topmod.impls()[0].methods[0].return.desc == some("a");
}

#[test]
fn should_execute_op_on_impl_method_failure_condition() {
    let doc = test::mk_doc(
        "impl i for int { #[doc(failure = \" a \")] fn a() { } }");
    assert doc.topmod.impls()[0].methods[0].failure == some("a");
}

#[test]
fn should_execute_op_on_type_brief() {
    let doc = test::mk_doc(
        "#[doc(brief = \" a \")] type t = int;");
    assert doc.topmod.types()[0].brief() == some("a");
}

#[test]
fn should_execute_op_on_type_desc() {
    let doc = test::mk_doc(
        "#[doc(desc = \" a \")] type t = int;");
    assert doc.topmod.types()[0].desc() == some("a");
}

#[cfg(test)]
mod test {
    fn mk_doc(source: str) -> doc::cratedoc {
        let srv = astsrv::mk_srv_from_str(source);
        let doc = extract::from_srv(srv, "");
        let doc = attr_pass::mk_pass()(srv, doc);
        mk_pass(str::trim)(srv, doc)
    }
}<commit_msg>rustdoc: Simplify desc pass<commit_after>#[doc = "Generic pass for performing an operation on all descriptions"];

export mk_pass;

fn mk_pass(op: fn~(str) -> str) -> pass {
    fn~(srv: astsrv::srv, doc: doc::cratedoc) -> doc::cratedoc {
        run(srv, doc, op)
    }
}

type op = fn~(str) -> str;

fn run(
    _srv: astsrv::srv,
    doc: doc::cratedoc,
    op: op
) -> doc::cratedoc {
    let fold = fold::fold({
        fold_item: fold_item,
        fold_fn: fold_fn,
        fold_enum: fold_enum,
        fold_res: fold_res,
        fold_iface: fold_iface,
        fold_impl: fold_impl
        with *fold::default_seq_fold(op)
    });
    fold.fold_crate(fold, doc)
}

fn maybe_apply_op(op: op, s: option<str>) -> option<str> {
    option::map(s) {|s| op(s) }
}

fn fold_item(fold: fold::fold<op>, doc: doc::itemdoc) -> doc::itemdoc {
    let doc = fold::default_seq_fold_item(fold, doc);

    {
        brief: maybe_apply_op(fold.ctxt, doc.brief),
        desc: maybe_apply_op(fold.ctxt, doc.desc)
        with doc
    }
}

fn fold_fn(fold: fold::fold<op>, doc: doc::fndoc) -> doc::fndoc {
    let doc = fold::default_seq_fold_fn(fold, doc);

    {
        args: vec::map(doc.args) {|doc|
            {
                desc: maybe_apply_op(fold.ctxt, doc.desc)
                with doc
            }
        },
        return: {
            desc: maybe_apply_op(fold.ctxt, doc.return.desc)
            with doc.return
        },
        failure: maybe_apply_op(fold.ctxt, doc.failure)
        with doc
    }
}

fn fold_enum(fold: fold::fold<op>, doc: doc::enumdoc) -> doc::enumdoc {
    let doc = fold::default_seq_fold_enum(fold, doc);

    {
        variants: vec::map(doc.variants) {|variant|
            {
                desc: maybe_apply_op(fold.ctxt, variant.desc)
                with variant
            }
        }
        with doc
    }
}

fn fold_res(fold: fold::fold<op>, doc: doc::resdoc) -> doc::resdoc {
    let doc = fold::default_seq_fold_res(fold, doc);

    {
        args: vec::map(doc.args) {|arg|
            {
                desc: maybe_apply_op(fold.ctxt, arg.desc)
                with arg
            }
        }
        with doc
    }
}

fn fold_iface(fold: fold::fold<op>, doc: doc::ifacedoc) -> doc::ifacedoc {
    let doc = fold::default_seq_fold_iface(fold, doc);

    {
        methods: apply_to_methods(fold.ctxt, doc.methods)
        with doc
    }
}

fn apply_to_methods(op: op, docs: [doc::methoddoc]) -> [doc::methoddoc] {
    vec::map(docs) {|doc|
        {
            brief: maybe_apply_op(op, doc.brief),
            desc: maybe_apply_op(op, doc.desc),
            args: vec::map(doc.args) {|doc|
                {
                    desc: maybe_apply_op(op, doc.desc)
                    with doc
                }
            },
            return: {
                desc: maybe_apply_op(op, doc.return.desc)
                with doc.return
            },
            failure: maybe_apply_op(op, doc.failure)
            with doc
        }
    }
}

fn fold_impl(fold: fold::fold<op>, doc: doc::impldoc) -> doc::impldoc {
    let doc = fold::default_seq_fold_impl(fold, doc);

    {
        methods: apply_to_methods(fold.ctxt, doc.methods)
        with doc
    }
}

#[test]
fn should_execute_op_on_enum_brief() {
    let doc = test::mk_doc("#[doc(brief = \" a \")] enum a { b }");
    assert doc.topmod.enums()[0].brief() == some("a");
}

#[test]
fn should_execute_op_on_enum_desc() {
    let doc = test::mk_doc("#[doc(desc = \" a \")] enum a { b }");
    assert doc.topmod.enums()[0].desc() == some("a");
}

#[test]
fn should_execute_op_on_variant_desc() {
    let doc = test::mk_doc("enum a { #[doc = \" a \"] b }");
    assert doc.topmod.enums()[0].variants[0].desc == some("a");
}

#[test]
fn should_execute_op_on_resource_brief() {
    let doc = test::mk_doc("#[doc(brief = \" a \")] resource r(a: bool) { }");
    assert doc.topmod.resources()[0].brief() == some("a");
}

#[test]
fn should_execute_op_on_resource_desc() {
    let doc = test::mk_doc("#[doc(desc = \" a \")] resource r(a: bool) { }");
    assert doc.topmod.resources()[0].desc() == some("a");
}

#[test]
fn should_execute_op_on_resource_args() {
    let doc = test::mk_doc(
        "#[doc(args(a = \" a \"))] resource r(a: bool) { }");
    assert doc.topmod.resources()[0].args[0].desc == some("a");
}

#[test]
fn should_execute_op_on_iface_brief() {
    let doc = test::mk_doc(
        "#[doc(brief = \" a \")] iface i { fn a(); }");
    assert doc.topmod.ifaces()[0].brief() == some("a");
}

#[test]
fn should_execute_op_on_iface_desc() {
    let doc = test::mk_doc(
        "#[doc(desc = \" a \")] iface i { fn a(); }");
    assert doc.topmod.ifaces()[0].desc() == some("a");
}

#[test]
fn should_execute_op_on_iface_method_brief() {
    let doc = test::mk_doc(
        "iface i { #[doc(brief = \" a \")] fn a(); }");
    assert doc.topmod.ifaces()[0].methods[0].brief == some("a");
}

#[test]
fn should_execute_op_on_iface_method_desc() {
    let doc = test::mk_doc(
        "iface i { #[doc(desc = \" a \")] fn a(); }");
    assert doc.topmod.ifaces()[0].methods[0].desc == some("a");
}

#[test]
fn should_execute_op_on_iface_method_args() {
    let doc = test::mk_doc(
        "iface i { #[doc(args(a = \" a \"))] fn a(a: bool); }");
    assert doc.topmod.ifaces()[0].methods[0].args[0].desc == some("a");
}

#[test]
fn should_execute_op_on_iface_method_return() {
    let doc = test::mk_doc(
        "iface i { #[doc(return = \" a \")] fn a() -> int; }");
    assert doc.topmod.ifaces()[0].methods[0].return.desc == some("a");
}

#[test]
fn should_execute_op_on_iface_method_failure_condition() {
    let doc = test::mk_doc("iface i { #[doc(failure = \" a \")] fn a(); }");
    assert doc.topmod.ifaces()[0].methods[0].failure == some("a");
}

#[test]
fn should_execute_op_on_impl_brief() {
    let doc = test::mk_doc(
        "#[doc(brief = \" a \")] impl i for int { fn a() { } }");
    assert doc.topmod.impls()[0].brief() == some("a");
}

#[test]
fn should_execute_op_on_impl_desc() {
    let doc = test::mk_doc(
        "#[doc(desc = \" a \")] impl i for int { fn a() { } }");
    assert doc.topmod.impls()[0].desc() == some("a");
}

#[test]
fn should_execute_op_on_impl_method_brief() {
    let doc = test::mk_doc(
        "impl i for int { #[doc(brief = \" a \")] fn a() { } }");
    assert doc.topmod.impls()[0].methods[0].brief == some("a");
}

#[test]
fn should_execute_op_on_impl_method_desc() {
    let doc = test::mk_doc(
        "impl i for int { #[doc(desc = \" a \")] fn a() { } }");
    assert doc.topmod.impls()[0].methods[0].desc == some("a");
}

#[test]
fn should_execute_op_on_impl_method_args() {
    let doc = test::mk_doc(
        "impl i for int { #[doc(args(a = \" a \"))] fn a(a: bool) { } }");
    assert doc.topmod.impls()[0].methods[0].args[0].desc == some("a");
}

#[test]
fn should_execute_op_on_impl_method_return() {
    let doc = test::mk_doc(
        "impl i for int { #[doc(return = \" a \")] fn a() -> int { fail } }");
    assert doc.topmod.impls()[0].methods[0].return.desc == some("a");
}

#[test]
fn should_execute_op_on_impl_method_failure_condition() {
    let doc = test::mk_doc(
        "impl i for int { #[doc(failure = \" a \")] fn a() { } }");
    assert doc.topmod.impls()[0].methods[0].failure == some("a");
}

#[test]
fn should_execute_op_on_type_brief() {
    let doc = test::mk_doc(
        "#[doc(brief = \" a \")] type t = int;");
    assert doc.topmod.types()[0].brief() == some("a");
}

#[test]
fn should_execute_op_on_type_desc() {
    let doc = test::mk_doc(
        "#[doc(desc = \" a \")] type t = int;");
    assert doc.topmod.types()[0].desc() == some("a");
}

#[cfg(test)]
mod test {
    fn mk_doc(source: str) -> doc::cratedoc {
        let srv = astsrv::mk_srv_from_str(source);
        let doc = extract::from_srv(srv, "");
        let doc = attr_pass::mk_pass()(srv, doc);
        mk_pass(str::trim)(srv, doc)
    }
}<|endoftext|>