Skip to content
Snippets Groups Projects
macros.rs 9.84 KiB
Newer Older
Jeremy Soller's avatar
Jeremy Soller committed
#[macro_export]
macro_rules! c_str {
    ($lit:expr) => {
Jeremy Soller's avatar
Jeremy Soller committed
        #[allow(unused_unsafe)]
Jeremy Soller's avatar
Jeremy Soller committed
        unsafe {
Jeremy Soller's avatar
Jeremy Soller committed
            $crate::c_str::CStr::from_bytes_with_nul_unchecked(concat!($lit, "\0").as_bytes())
Jeremy Soller's avatar
Jeremy Soller committed
        }
Jeremy Soller's avatar
Jeremy Soller committed
    };
Jeremy Soller's avatar
Jeremy Soller committed
}

/// Print to stdout
#[macro_export]
macro_rules! print {
    ($($arg:tt)*) => ({
        use core::fmt::Write;
        let _ = write!($crate::platform::FileWriter(1), $($arg)*);
    });
}

/// Print with new line to stdout
#[macro_export]
macro_rules! println {
    () => (print!("\n"));
    ($fmt:expr) => (print!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}

/// Print to stderr
#[macro_export]
macro_rules! eprint {
    ($($arg:tt)*) => ({
        use core::fmt::Write;
        let _ = write!($crate::platform::FileWriter(2), $($arg)*);
    });
}

/// Print with new line to stderr
#[macro_export]
macro_rules! eprintln {
    () => (eprint!("\n"));
    ($fmt:expr) => (eprint!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => (eprint!(concat!($fmt, "\n"), $($arg)*));
}

/// Lifted from libstd
#[macro_export]
macro_rules! dbg {
    () => {
        eprintln!("[{}:{}]", file!(), line!());
    };
    ($val:expr) => {
        // Use of `match` here is intentional because it affects the lifetimes
        // of temporaries - https://stackoverflow.com/a/48732525/1063961
        match $val {
            tmp => {
Jeremy Soller's avatar
Jeremy Soller committed
                eprintln!(
                    "[{}:{}] {} = {:#?}",
                    file!(),
                    line!(),
                    stringify!($val),
                    &tmp
                );
Jeremy Soller's avatar
Jeremy Soller committed
    };
#[macro_export]
#[cfg(not(feature = "trace"))]
macro_rules! trace {
    ($($arg:tt)*) => {};
}

#[macro_export]
#[cfg(feature = "trace")]
macro_rules! trace {
Jeremy Soller's avatar
Jeremy Soller committed
    ($($arg:tt)*) => ({
        use $crate::{Pal, Sys};
        eprintln!($($arg)*);
    });
}

#[macro_export]
#[cfg(not(feature = "trace"))]
macro_rules! trace_expr {
    ($expr:expr, $($arg:tt)*) => {
        $expr
    };
}

#[macro_export]
#[cfg(feature = "trace")]
macro_rules! trace_expr {
    ($expr:expr, $($arg:tt)*) => ({
Jeremy Soller's avatar
Jeremy Soller committed
        use $crate::header::errno::STR_ERROR;
        use $crate::platform;

jD91mZM2's avatar
jD91mZM2 committed
        trace!("{}", format_args!($($arg)*));
Jeremy Soller's avatar
Jeremy Soller committed
        #[allow(unused_unsafe)]
        let trace_old_errno = unsafe { platform::errno };
Jeremy Soller's avatar
Jeremy Soller committed
        #[allow(unused_unsafe)]
Jeremy Soller's avatar
Jeremy Soller committed
        unsafe { platform::errno = 0; }
        let ret = $expr;
Jeremy Soller's avatar
Jeremy Soller committed
        #[allow(unused_unsafe)]
        let trace_errno = unsafe { platform::errno } as isize;
        if trace_errno == 0 {
Jeremy Soller's avatar
Jeremy Soller committed
            #[allow(unused_unsafe)]
            unsafe { platform::errno = trace_old_errno; }
        let trace_strerror = if trace_errno >= 0 && trace_errno < STR_ERROR.len() as isize {
            STR_ERROR[trace_errno as usize]
        trace!("{} = {} ({}, {})", format_args!($($arg)*), ret, trace_errno, trace_strerror);
Jeremy Soller's avatar
Jeremy Soller committed
#[macro_export]
macro_rules! strto_impl {
    (
        $rettype:ty, $signed:expr, $maxval:expr, $minval:expr, $s:ident, $endptr:ident, $base:ident
    ) => {{
        // ensure these are constants
        const CHECK_SIGN: bool = $signed;
        const MAX_VAL: $rettype = $maxval;
        const MIN_VAL: $rettype = $minval;

        let set_endptr = |idx: isize| {
            if !$endptr.is_null() {
                // This is stupid, but apparently strto* functions want
                // const input but mut output, yet the man page says
                // "stores the address of the first invalid character in *endptr"
                // so obviously it doesn't want us to clone it.
                *$endptr = $s.offset(idx) as *mut _;
            }
        };

        let invalid_input = || {
            platform::errno = EINVAL;
            set_endptr(0);
        };

        // only valid bases are 2 through 36
        if $base != 0 && ($base < 2 || $base > 36) {
            invalid_input();
            return 0;
        }

        let mut idx = 0;

        // skip any whitespace at the beginning of the string
        while ctype::isspace(*$s.offset(idx) as c_int) != 0 {
            idx += 1;
        }

        // check for +/-
        let positive = match is_positive(*$s.offset(idx)) {
            Some((pos, i)) => {
                idx += i;
                pos
            }
            None => {
                invalid_input();
                return 0;
            }
        };

        // convert the string to a number
        let num_str = $s.offset(idx);
        let res = match $base {
            0 => detect_base(num_str)
                .and_then(|($base, i)| convert_integer(num_str.offset(i), $base)),
            8 => convert_octal(num_str),
            16 => convert_hex(num_str),
            _ => convert_integer(num_str, $base),
        };

        // check for error parsing octal/hex prefix
        // also check to ensure a number was indeed parsed
        let (num, i, overflow) = match res {
            Some(res) => res,
            None => {
                invalid_input();
                return 0;
            }
        };
        idx += i;

        let overflow = if CHECK_SIGN {
            overflow || (num as c_long).is_negative()
        } else {
            overflow
        };
        // account for the sign
        let num = num as $rettype;
        let num = if overflow {
            platform::errno = ERANGE;
            if CHECK_SIGN {
                if positive {
                    MAX_VAL
                } else {
                    MIN_VAL
                }
            } else {
                MAX_VAL
            }
        } else {
            if positive {
                num
            } else {
                // not using -num to keep the compiler happy
                num.overflowing_neg().0
            }
        };

        set_endptr(idx);

        num
    }};
}
jD91mZM2's avatar
jD91mZM2 committed
#[macro_export]
macro_rules! strto_float_impl {
    ($type:ident, $s:expr, $endptr:expr) => {{
        let mut s = $s;
        let endptr = $endptr;

        while ctype::isspace(*s as c_int) != 0 {
            s = s.offset(1);
        }

        let mut result: $type = 0.0;
        let mut exponent: Option<$type> = None;
jD91mZM2's avatar
jD91mZM2 committed
        let mut radix = 10;

        let result_sign = match *s as u8 {
jD91mZM2's avatar
jD91mZM2 committed
            b'-' => {
                s = s.offset(1);
jD91mZM2's avatar
jD91mZM2 committed
            }
            b'+' => {
                s = s.offset(1);
jD91mZM2's avatar
jD91mZM2 committed
            }
jD91mZM2's avatar
jD91mZM2 committed
        };

        let rust_s = CStr::from_ptr(s).to_string_lossy();

        // detect NaN, Inf
        if rust_s.to_lowercase().starts_with("inf") {
            result = $type::INFINITY;
            s = s.offset(3);
        } else if rust_s.to_lowercase().starts_with("nan") {
            // we cannot signal negative NaN in LLVM backed languages
            // https://github.com/rust-lang/rust/issues/73328 , https://github.com/rust-lang/rust/issues/81261
            result = $type::NAN;
            s = s.offset(3);
        } else {
            if *s as u8 == b'0' && *s.offset(1) as u8 == b'x' {
                s = s.offset(2);
                radix = 16;
            }
jD91mZM2's avatar
jD91mZM2 committed

            while let Some(digit) = (*s as u8 as char).to_digit(radix) {
                result *= radix as $type;
                result += digit as $type;
jD91mZM2's avatar
jD91mZM2 committed
                s = s.offset(1);
            }
            if *s as u8 == b'.' {
                let mut i = 1.0;
                while let Some(digit) = (*s as u8 as char).to_digit(radix) {
                    i *= radix as $type;
                    result += digit as $type / i;
                    s = s.offset(1);
                }
            }
            let s_before_exponent = s;

            exponent = match (*s as u8, radix) {
                (b'e' | b'E', 10) | (b'p' | b'P', 16) => {
                    s = s.offset(1);

                    let is_exponent_positive = match *s as u8 {
                        b'-' => {
                            s = s.offset(1);
                            false
                        }
                        b'+' => {
                            s = s.offset(1);
                            true
                        }
                        _ => true,
                    // Exponent digits are always in base 10.
                    if (*s as u8 as char).is_digit(10) {
                        let mut exponent_value = 0;

                        while let Some(digit) = (*s as u8 as char).to_digit(10) {
                            exponent_value *= 10;
                            exponent_value += digit;
                            s = s.offset(1);
                        }

                        let exponent_base = match radix {
                            10 => 10u128,
                            16 => 2u128,
                            _ => unreachable!(),
                        };

                        if is_exponent_positive {
                            Some(exponent_base.pow(exponent_value) as $type)
                        } else {
                            Some(1.0 / (exponent_base.pow(exponent_value) as $type))
                        }
                        // Exponent had no valid digits after 'e'/'p' and '+'/'-', rollback
                        s = s_before_exponent;
                        None
jD91mZM2's avatar
jD91mZM2 committed
        if !endptr.is_null() {
            // This is stupid, but apparently strto* functions want
            // const input but mut output, yet the man page says
            // "stores the address of the first invalid character in *endptr"
            // so obviously it doesn't want us to clone it.
            *endptr = s as *mut _;
        }

        if let Some(exponent) = exponent {
            result_sign * result * exponent
jD91mZM2's avatar
jD91mZM2 committed
        } else {
            result_sign * result
jD91mZM2's avatar
jD91mZM2 committed
        }
Jeremy Soller's avatar
Jeremy Soller committed
    }};
jD91mZM2's avatar
jD91mZM2 committed
}