#![no_std]
#![feature(core_intrinsics)]
#[cfg(feature = "print-backtrace")]
extern crate backtrace;
extern crate libc;
extern crate spin;
use core::fmt::{Arguments, Result as FmtResult, Write};
use core::sync::atomic::{AtomicBool};
use core::sync::atomic::Ordering::SeqCst;
#[doc(hidden)]
pub use core::mem::drop;
#[doc(hidden)]
pub use core::fmt::write;
#[doc(hidden)]
pub static STDERR_MTX: spin::Mutex<()> = spin::Mutex::new(());
#[doc(hidden)]
pub static STDOUT_MTX: spin::Mutex<()> = spin::Mutex::new(());
#[doc(hidden)]
pub static STDOUT: libc::c_int = 1;
#[doc(hidden)]
pub static STDERR: libc::c_int = 2;
#[doc(hidden)]
pub struct FDWriter(pub libc::c_int);
impl Write for FDWriter {
#[inline]
fn write_str(&mut self, s: &str) -> FmtResult {
let mut buf = s.as_bytes();
while !buf.is_empty() {
unsafe {
#[cfg(not(windows))]
let written = libc::write(self.0, buf.as_ptr() as *const _, buf.len());
#[cfg(windows)]
let written =
libc::write(self.0, buf.as_ptr() as *const _, buf.len() as libc::c_uint);
if written < 1 {
core::intrinsics::abort();
}
buf = &buf[written as usize..];
}
}
Ok(())
}
}
#[doc(hidden)]
pub unsafe fn abort() -> ! {
core::intrinsics::abort();
}
#[doc(hidden)]
#[macro_export]
macro_rules! print_internal {
($file:expr, $mtx:expr, $fmt:expr) => {
#[allow(unused_unsafe)]
unsafe {
let guard = $mtx.lock();
let mut fd = $crate::FDWriter($file);
let _ = $crate::write(&mut fd, $fmt).map_err(|_| {
$crate::abort();
});
$crate::drop(guard);
}
}
}
#[macro_export]
macro_rules! alloc_print {
($($arg:tt)*) => (print_internal!($crate::STDOUT, $crate::STDOUT_MTX, format_args!($($arg)*)))
}
#[macro_export]
macro_rules! alloc_eprint {
($($arg:tt)*) => (print_internal!($crate::STDERR, $crate::STDERR_MTX, format_args!($($arg)*)))
}
#[macro_export]
macro_rules! alloc_println {
() => (alloc_print!("\n"));
($fmt:expr) => (alloc_print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (alloc_print!(concat!($fmt, "\n"), $($arg)*));
}
#[macro_export]
macro_rules! alloc_eprintln {
() => (alloc_eprint!("\n"));
($fmt:expr) => (alloc_eprint!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (alloc_eprint!(concat!($fmt, "\n"), $($arg)*));
}
#[doc(hidden)]
pub static IS_PANICKING: AtomicBool = AtomicBool::new(false);
#[macro_export]
macro_rules! alloc_panic {
() => (alloc_panic!("explicit panic"));
($msg:expr) => ({
$crate::panic(&(format_args!($msg), file!(), line!(), column!()))
});
($fmt:expr, $($arg:tt)*) => ({
$crate::panic(&(format_args!($fmt, $($arg)*), file!(), line!(), column!()))
})
}
#[doc(hidden)]
#[inline(never)]
#[cold]
pub fn panic(fmt_file_line_col: &(Arguments, &'static str, u32, u32)) -> ! {
let (fmt, file, line, col) = *fmt_file_line_col;
alloc_eprint!("thread panicked at '");
print_internal!(STDERR, STDERR_MTX, fmt);
alloc_eprintln!("', {}:{}:{}", file, line, col);
unsafe {
if IS_PANICKING.compare_and_swap(false, true, SeqCst) {
alloc_eprintln!("thread panicked while panicking");
core::intrinsics::abort();
}
print_backtrace_and_abort()
}
}
#[macro_export]
macro_rules! alloc_assert {
($pred:expr) => ({
alloc_assert!($pred, "{}", stringify!($pred));
});
($pred:expr, $msg:expr) => ({
if !($pred) {
alloc_panic!("assertion failed: {}", $msg);
}
});
($pred:expr, $fmt:expr, $($arg:tt)*) => ({
if !($pred) {
alloc_panic!(concat!("assertion failed: ", $fmt), $($arg)*);
}
})
}
#[macro_export]
macro_rules! alloc_debug_assert {
($($arg:tt)*) => {
if cfg!(debug_assertions) {
alloc_assert!($($arg)*);
}
}
}
#[macro_export]
macro_rules! alloc_assert_eq {
($a:expr, $b:expr) => {
{
let a = $a;
let b = $b;
let s = stringify!($a == $b);
alloc_assert!(a == b, "{} (evaluated to {:?} == {:?})", s, a, b);
}
};
($a:expr, $b:expr, $fmt:expr) => {
{
let a = $a;
let b = $b;
let s = stringify!($a == $b);
alloc_assert!(a == b, concat!("{} (evaluated to {:?} == {:?}): ", $fmt), s, a, b);
}
};
($a:expr, $b:expr, $fmt:expr, $($arg:tt)*) => {
{
let a = $a;
let b = $b;
let s = stringify!($a == $b);
alloc_assert!(a == b, concat!("{} (evaluated to {:?} == {:?}): ", $fmt), s, a, b, $($arg)*);
}
}
}
#[macro_export]
macro_rules! alloc_debug_assert_eq {
($($arg:tt)*) => {
if cfg!(debug_assertions) {
alloc_assert_eq!($($arg)*);
}
}
}
#[macro_export]
macro_rules! alloc_assert_ne {
($a:expr, $b:expr) => {
{
let a = $a;
let b = $b;
let s = stringify!($a != $b);
alloc_assert!(a != b, "{} (evaluated to {:?} != {:?})", s, a, b);
}
};
($a:expr, $b:expr, $fmt:expr) => {
{
let a = $a;
let b = $b;
let s = stringify!($a != $b);
alloc_assert!(a != b, concat!("{} (evaluated to {:?} != {:?}): ", $fmt), s, a, b);
}
};
($a:expr, $b:expr, $fmt:expr, $($arg:tt)*) => {
{
let a = $a;
let b = $b;
let s = stringify!($a != $b);
alloc_assert!(a != b, concat!("{} (evaluated to {:?} != {:?}): ", $fmt), s, a, b, $($arg)*);
}
}
}
#[macro_export]
macro_rules! alloc_debug_assert_ne {
($($arg:tt)*) => {
if cfg!(debug_assertions) {
alloc_assert_ne!($($arg)*);
}
}
}
pub trait AllocUnwrap {
type Item;
fn alloc_unwrap(self) -> Self::Item;
fn alloc_expect(self, msg: &str) -> Self::Item;
}
impl<T> AllocUnwrap for Option<T> {
type Item = T;
#[inline]
fn alloc_unwrap(self) -> T {
match self {
Some(val) => val,
None => alloc_panic!("called `Option::alloc_unwrap()` on a `None` value"),
}
}
#[inline]
fn alloc_expect(self, msg: &str) -> T {
#[inline(never)]
#[cold]
fn failed(msg: &str) -> ! {
alloc_panic!("{}", msg);
}
match self {
Some(val) => val,
None => failed(msg),
}
}
}
impl<T, E: ::core::fmt::Debug> AllocUnwrap for Result<T, E> {
type Item = T;
#[inline]
fn alloc_unwrap(self) -> T {
match self {
Ok(val) => val,
Err(err) => {
result_unwrap_failed("called `Result::alloc_unwrap()` on an `Err` value", err)
}
}
}
#[inline]
fn alloc_expect(self, msg: &str) -> T {
match self {
Ok(val) => val,
Err(err) => result_unwrap_failed(msg, err),
}
}
}
#[inline(never)]
#[cold]
fn result_unwrap_failed<E: ::core::fmt::Debug>(msg: &str, err: E) -> ! {
alloc_panic!("{}: {:?}", msg, err)
}
#[doc(hidden)]
pub unsafe fn print_backtrace_and_abort() -> ! {
#[cfg(feature = "print-backtrace")]
{
backtrace::trace(|frame| {
let ip = frame.ip();
backtrace::resolve(ip, |symbol| {
if let Some(name) = symbol.name() {
alloc_eprintln!("{}", name);
} else {
alloc_eprintln!("<unknown function>");
}
if let Some(path) = symbol.filename() {
if let Some(s) = path.to_str() {
alloc_eprint!("\t{}", s);
} else {
alloc_eprint!("\t<unknown file>");
}
} else {
alloc_eprint!("\t<unknown file>");
}
if let Some(line) = symbol.lineno() {
alloc_eprintln!(":{}", line);
} else {
alloc_eprintln!();
}
});
true
});
}
core::intrinsics::abort();
}
#[allow(unused)]
#[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
fn never_called() {
alloc_print!("foo");
alloc_println!("foo");
alloc_eprint!("foo");
alloc_eprintln!("foo");
alloc_assert!(false && true);
alloc_assert!(false && true, "foo");
alloc_assert!(false && true, "foo: {}", "bar");
alloc_debug_assert!(false && true);
alloc_debug_assert!(false && true, "foo");
alloc_debug_assert!(false && true, "foo: {}", "bar");
alloc_assert_eq!(1 + 2, 1);
alloc_assert_eq!(1 + 2, 1, "foo");
alloc_assert_eq!(1 + 2, 1, "foo: {}", "bar");
alloc_debug_assert_eq!(1 + 2, 1);
alloc_debug_assert_eq!(1 + 2, 1, "foo");
alloc_debug_assert_eq!(1 + 2, 1, "foo: {}", "bar");
alloc_assert_eq!(1 + 2, 3);
alloc_assert_eq!(1 + 2, 3, "foo");
alloc_assert_eq!(1 + 2, 3, "foo: {}", "bar");
alloc_debug_assert_eq!(1 + 2, 3);
alloc_debug_assert_eq!(1 + 2, 3, "foo");
alloc_debug_assert_eq!(1 + 2, 3, "foo: {}", "bar");
Some(0).alloc_unwrap();
Some(0).alloc_expect("None");
let _: usize = None.alloc_unwrap();
let _: usize = None.alloc_expect("None");
(Ok(0) as Result<_, &'static str>).alloc_unwrap();
(Ok(0) as Result<_, &'static str>).alloc_expect("None");
(Err("") as Result<usize, _>).alloc_unwrap();
(Err("") as Result<usize, _>).alloc_expect("None");
}