From 5b8fe3e45a540e398094938697d5a1d46239f394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Cassiers?= Date: Tue, 24 Jan 2017 15:14:13 +0100 Subject: [PATCH 01/15] Move platform-specific implementation of libstd/path.rs This removes one exception in the tidy lint 'pal'. --- src/libstd/path.rs | 4 ++-- src/libstd/sys/redox/path.rs | 2 ++ src/libstd/sys/unix/path.rs | 1 + src/libstd/sys/windows/path.rs | 1 + src/tools/tidy/src/pal.rs | 1 - 5 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libstd/path.rs b/src/libstd/path.rs index eb0a6cd74d7bc..228c0fd773803 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -127,7 +127,7 @@ use ops::{self, Deref}; use ffi::{OsStr, OsString}; -use sys::path::{is_sep_byte, is_verbatim_sep, MAIN_SEP_STR, parse_prefix}; +use sys::path::{ABSOLUTE_NEEDS_PREFIX, is_sep_byte, is_verbatim_sep, MAIN_SEP_STR, parse_prefix}; //////////////////////////////////////////////////////////////////////////////// // GENERAL NOTES @@ -1509,7 +1509,7 @@ impl Path { #[allow(deprecated)] pub fn is_absolute(&self) -> bool { // FIXME: Remove target_os = "redox" and allow Redox prefixes - self.has_root() && (cfg!(unix) || cfg!(target_os = "redox") || self.prefix().is_some()) + self.has_root() && (!ABSOLUTE_NEEDS_PREFIX || self.prefix().is_some()) } /// A path is *relative* if it is not absolute. diff --git a/src/libstd/sys/redox/path.rs b/src/libstd/sys/redox/path.rs index e6a267dd5d913..8863937794bbe 100644 --- a/src/libstd/sys/redox/path.rs +++ b/src/libstd/sys/redox/path.rs @@ -37,3 +37,5 @@ pub fn parse_prefix(path: &OsStr) -> Option { pub const MAIN_SEP_STR: &'static str = "/"; pub const MAIN_SEP: char = '/'; +// FIXME: Change to 'true' and allow Redox prefixes +pub const ABSOLUTE_NEEDS_PREFIX: bool = false; diff --git a/src/libstd/sys/unix/path.rs b/src/libstd/sys/unix/path.rs index bf9af7a4353a8..5a06058534632 100644 --- a/src/libstd/sys/unix/path.rs +++ b/src/libstd/sys/unix/path.rs @@ -27,3 +27,4 @@ pub fn parse_prefix(_: &OsStr) -> Option { pub const MAIN_SEP_STR: &'static str = "/"; pub const MAIN_SEP: char = '/'; +pub const ABSOLUTE_NEEDS_PREFIX: bool = false; diff --git a/src/libstd/sys/windows/path.rs b/src/libstd/sys/windows/path.rs index 2b47808451bc2..900946438d5d2 100644 --- a/src/libstd/sys/windows/path.rs +++ b/src/libstd/sys/windows/path.rs @@ -106,3 +106,4 @@ pub fn parse_prefix<'a>(path: &'a OsStr) -> Option { pub const MAIN_SEP_STR: &'static str = "\\"; pub const MAIN_SEP: char = '\\'; +pub const ABSOLUTE_NEEDS_PREFIX: bool = true; diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index 3808c05c6b939..0a65de7468708 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -66,7 +66,6 @@ const EXCEPTION_PATHS: &'static [&'static str] = &[ // temporary exceptions "src/libstd/rtdeps.rs", // Until rustbuild replaces make - "src/libstd/path.rs", "src/libstd/f32.rs", "src/libstd/f64.rs", "src/libstd/sys_common/mod.rs", From 662fef63603a9600ff2e4a712373dea220d2e517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Cassiers?= Date: Thu, 26 Jan 2017 21:15:58 +0100 Subject: [PATCH 02/15] Move platform-specific implementation of libstd/f{32,64}.rs This removes exceptions in the tidy lint 'pal'. --- src/libstd/sys/redox/f32.rs | 78 ++++++++++++++ src/libstd/sys/redox/f64.rs | 33 ++++++ src/libstd/sys/unix/f32.rs | 81 +++++++++++++++ src/libstd/sys/unix/f64.rs | 66 ++++++++++++ src/libstd/sys/windows/f32.rs | 190 ++++++++++++++++++++++++++++++++++ src/libstd/sys/windows/f64.rs | 36 +++++++ 6 files changed, 484 insertions(+) create mode 100644 src/libstd/sys/redox/f32.rs create mode 100644 src/libstd/sys/redox/f64.rs create mode 100644 src/libstd/sys/unix/f32.rs create mode 100644 src/libstd/sys/unix/f64.rs create mode 100644 src/libstd/sys/windows/f32.rs create mode 100644 src/libstd/sys/windows/f64.rs diff --git a/src/libstd/sys/redox/f32.rs b/src/libstd/sys/redox/f32.rs new file mode 100644 index 0000000000000..6fd9ce3cc6bdb --- /dev/null +++ b/src/libstd/sys/redox/f32.rs @@ -0,0 +1,78 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +use intrinsics; + +pub mod cmath { + use libc::{c_float, c_int}; + + #[link_name = "m"] + extern { + pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float; + pub fn hypotf(x: c_float, y: c_float) -> c_float; + pub fn acosf(n: c_float) -> c_float; + pub fn asinf(n: c_float) -> c_float; + pub fn atan2f(a: c_float, b: c_float) -> c_float; + pub fn atanf(n: c_float) -> c_float; + pub fn coshf(n: c_float) -> c_float; + pub fn frexpf(n: c_float, value: &mut c_int) -> c_float; + pub fn ldexpf(x: c_float, n: c_int) -> c_float; + pub fn sinhf(n: c_float) -> c_float; + pub fn tanf(n: c_float) -> c_float; + pub fn tanhf(n: c_float) -> c_float; + } +} + +#[inline] +pub fn floor(x: f32) -> f32 { + unsafe { intrinsics::floorf32(x) } +} + +#[inline] +pub fn ceil(x: f32) -> f32 { + unsafe { intrinsics::ceilf32(x) } +} + +#[inline] +pub fn powf(x: f32, n: f32) -> f32 { + unsafe { intrinsics::powf32(x, n) } +} + +#[inline] +pub fn exp(x: f32) -> f32 { + unsafe { intrinsics::expf32(x) } +} + +#[inline] +pub fn ln(x: f32) -> f32 { + unsafe { intrinsics::logf32(x) } +} + +#[inline] +pub fn log2(x: f32) -> f32 { + unsafe { intrinsics::log2f32(x) } +} + +#[inline] +pub fn log10(x: f32) -> f32 { + unsafe { intrinsics::log10f32(x) } +} + +#[inline] +pub fn sin(x: f32) -> f32 { + unsafe { intrinsics::sinf32(x) } +} + +#[inline] +pub fn cos(x: f32) -> f32 { + unsafe { intrinsics::cosf32(x) } +} diff --git a/src/libstd/sys/redox/f64.rs b/src/libstd/sys/redox/f64.rs new file mode 100644 index 0000000000000..4935a813f240e --- /dev/null +++ b/src/libstd/sys/redox/f64.rs @@ -0,0 +1,33 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +pub mod cmath { + use libc::{c_double, c_int}; + + #[link_name = "m"] + extern { + pub fn lgamma_r(n: c_double, sign: &mut c_int) -> c_double; + pub fn hypot(x: c_double, y: c_double) -> c_double; + } +} + +pub fn ln(x: f64) -> f64 { + unsafe { ::intrinsics::logf64(x) } +} + +pub fn log2(x: f64) -> f64 { + unsafe { ::intrinsics::log2f64(x) } +} + +pub fn log10(x: f64) -> f64 { + unsafe { ::intrinsics::log10f64(x) } +} diff --git a/src/libstd/sys/unix/f32.rs b/src/libstd/sys/unix/f32.rs new file mode 100644 index 0000000000000..23b73a84b4192 --- /dev/null +++ b/src/libstd/sys/unix/f32.rs @@ -0,0 +1,81 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +use intrinsics; + +pub mod cmath { + use libc::{c_float, c_int}; + + #[link_name = "m"] + extern { + pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float; + pub fn hypotf(x: c_float, y: c_float) -> c_float; + pub fn acosf(n: c_float) -> c_float; + pub fn asinf(n: c_float) -> c_float; + pub fn atan2f(a: c_float, b: c_float) -> c_float; + pub fn atanf(n: c_float) -> c_float; + pub fn coshf(n: c_float) -> c_float; + pub fn frexpf(n: c_float, value: &mut c_int) -> c_float; + pub fn ldexpf(x: c_float, n: c_int) -> c_float; + pub fn sinhf(n: c_float) -> c_float; + pub fn tanf(n: c_float) -> c_float; + pub fn tanhf(n: c_float) -> c_float; + } +} + +#[inline] +pub fn floor(x: f32) -> f32 { + unsafe { intrinsics::floorf32(x) } +} + +#[inline] +pub fn ceil(x: f32) -> f32 { + unsafe { intrinsics::ceilf32(x) } +} + +#[inline] +pub fn powf(x: f32, n: f32) -> f32 { + unsafe { intrinsics::powf32(x, n) } +} + +#[inline] +pub fn exp(x: f32) -> f32 { + unsafe { intrinsics::expf32(x) } +} + +#[inline] +pub fn ln(x: f32) -> f32 { + unsafe { intrinsics::logf32(x) } +} + +#[inline] +pub fn log2(x: f32) -> f32 { + #[cfg(target_os = "android")] + return ::sys::android::log2f32(x); + #[cfg(not(target_os = "android"))] + return unsafe { intrinsics::log2f32(x) }; +} + +#[inline] +pub fn log10(x: f32) -> f32 { + unsafe { intrinsics::log10f32(x) } +} + +#[inline] +pub fn sin(x: f32) -> f32 { + unsafe { intrinsics::sinf32(x) } +} + +#[inline] +pub fn cos(x: f32) -> f32 { + unsafe { intrinsics::cosf32(x) } +} diff --git a/src/libstd/sys/unix/f64.rs b/src/libstd/sys/unix/f64.rs new file mode 100644 index 0000000000000..cce9d7a619f0e --- /dev/null +++ b/src/libstd/sys/unix/f64.rs @@ -0,0 +1,66 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +use core::f64::{NAN, NEG_INFINITY}; + +pub mod cmath { + use libc::{c_double, c_int}; + + #[link_name = "m"] + extern { + pub fn lgamma_r(n: c_double, sign: &mut c_int) -> c_double; + pub fn hypot(x: c_double, y: c_double) -> c_double; + } +} + +pub fn ln(x: f64) -> f64 { + log_wrapper(x, |n| { unsafe { ::intrinsics::logf64(n) } }) +} + +pub fn log2(x: f64) -> f64 { + log_wrapper(x, + |n| { + #[cfg(target_os = "android")] + return ::sys::android::log2f64(n); + #[cfg(not(target_os = "android"))] + return unsafe { ::intrinsics::log2f64(n) }; + }) +} + +pub fn log10(x: f64) -> f64 { + log_wrapper(x, |n| { unsafe { ::intrinsics::log10f64(n) } }) +} + +// Solaris/Illumos requires a wrapper around log, log2, and log10 functions +// because of their non-standard behavior (e.g. log(-n) returns -Inf instead +// of expected NaN). +fn log_wrapper f64>(x: f64, log_fn: F) -> f64 { + if !cfg!(target_os = "solaris") { + log_fn(x) + } else { + if x.is_finite() { + if x > 0.0 { + log_fn(x) + } else if x == 0.0 { + NEG_INFINITY // log(0) = -Inf + } else { + NAN // log(-n) = NaN + } + } else if x.is_nan() { + x // log(NaN) = NaN + } else if x > 0.0 { + x // log(Inf) = Inf + } else { + NAN // log(-Inf) = NaN + } + } +} diff --git a/src/libstd/sys/windows/f32.rs b/src/libstd/sys/windows/f32.rs new file mode 100644 index 0000000000000..b57aaa4ecd25e --- /dev/null +++ b/src/libstd/sys/windows/f32.rs @@ -0,0 +1,190 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +use intrinsics; + +pub mod cmath { + use libc::{c_float, c_int}; + + extern { + #[cfg_attr(target_env = "msvc", link_name = "__lgamma_r")] + pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float; + + #[cfg_attr(target_env = "msvc", link_name = "_hypot")] + pub fn hypotf(x: c_float, y: c_float) -> c_float; + } + + // See the comments in the `floor` function for why MSVC is special + // here. + #[cfg(not(target_env = "msvc"))] + extern { + pub fn acosf(n: c_float) -> c_float; + pub fn asinf(n: c_float) -> c_float; + pub fn atan2f(a: c_float, b: c_float) -> c_float; + pub fn atanf(n: c_float) -> c_float; + pub fn coshf(n: c_float) -> c_float; + pub fn frexpf(n: c_float, value: &mut c_int) -> c_float; + pub fn ldexpf(x: c_float, n: c_int) -> c_float; + pub fn sinhf(n: c_float) -> c_float; + pub fn tanf(n: c_float) -> c_float; + pub fn tanhf(n: c_float) -> c_float; + } + + #[cfg(target_env = "msvc")] + pub use self::shims::*; + #[cfg(target_env = "msvc")] + mod shims { + use libc::{c_float, c_int}; + + #[inline] + pub unsafe fn acosf(n: c_float) -> c_float { + f64::acos(n as f64) as c_float + } + + #[inline] + pub unsafe fn asinf(n: c_float) -> c_float { + f64::asin(n as f64) as c_float + } + + #[inline] + pub unsafe fn atan2f(n: c_float, b: c_float) -> c_float { + f64::atan2(n as f64, b as f64) as c_float + } + + #[inline] + pub unsafe fn atanf(n: c_float) -> c_float { + f64::atan(n as f64) as c_float + } + + #[inline] + pub unsafe fn coshf(n: c_float) -> c_float { + f64::cosh(n as f64) as c_float + } + + #[inline] + #[allow(deprecated)] + pub unsafe fn frexpf(x: c_float, value: &mut c_int) -> c_float { + let (a, b) = f64::frexp(x as f64); + *value = b as c_int; + a as c_float + } + + #[inline] + #[allow(deprecated)] + pub unsafe fn ldexpf(x: c_float, n: c_int) -> c_float { + f64::ldexp(x as f64, n as isize) as c_float + } + + #[inline] + pub unsafe fn sinhf(n: c_float) -> c_float { + f64::sinh(n as f64) as c_float + } + + #[inline] + pub unsafe fn tanf(n: c_float) -> c_float { + f64::tan(n as f64) as c_float + } + + #[inline] + pub unsafe fn tanhf(n: c_float) -> c_float { + f64::tanh(n as f64) as c_float + } + } +} + +pub fn floor(x: f32) -> f32 { + // On MSVC LLVM will lower many math intrinsics to a call to the + // corresponding function. On MSVC, however, many of these functions + // aren't actually available as symbols to call, but rather they are all + // `static inline` functions in header files. This means that from a C + // perspective it's "compatible", but not so much from an ABI + // perspective (which we're worried about). + // + // The inline header functions always just cast to a f64 and do their + // operation, so we do that here as well, but only for MSVC targets. + // + // Note that there are many MSVC-specific float operations which + // redirect to this comment, so `floorf` is just one case of a missing + // function on MSVC, but there are many others elsewhere. + #[cfg(target_env = "msvc")] + return (x as f64).floor() as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::floorf32(x) }; +} + +#[inline] +pub fn ceil(x: f32) -> f32 { + // see notes above in `floor` + #[cfg(target_env = "msvc")] + return (x as f64).ceil() as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::ceilf32(x) }; +} + +#[inline] +pub fn powf(x: f32, n: f32) -> f32 { + // see notes above in `floor` + #[cfg(target_env = "msvc")] + return (x as f64).powf(n as f64) as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::powf32(x, n) }; +} + +#[inline] +pub fn exp(x: f32) -> f32 { + // see notes above in `floor` + #[cfg(target_env = "msvc")] + return (x as f64).exp() as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::expf32(x) }; +} + +#[inline] +pub fn ln(x: f32) -> f32 { + // see notes above in `floor` + #[cfg(target_env = "msvc")] + return (x as f64).ln() as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::logf32(x) }; +} + +#[inline] +pub fn log2(x: f32) -> f32 { + unsafe { intrinsics::log2f32(x) } +} + +#[inline] +pub fn log10(x: f32) -> f32 { + // see notes above in `floor` + #[cfg(target_env = "msvc")] + return (x as f64).log10() as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::log10f32(x) }; +} + +#[inline] +pub fn sin(x: f32) -> f32 { + // see notes in `core::f32::Float::floor` + #[cfg(target_env = "msvc")] + return (x as f64).sin() as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::sinf32(x) }; +} + +#[inline] +pub fn cos(x: f32) -> f32 { + // see notes in `core::f32::Float::floor` + #[cfg(target_env = "msvc")] + return (x as f64).cos() as f32; + #[cfg(not(target_env = "msvc"))] + return unsafe { intrinsics::cosf32(x) }; +} diff --git a/src/libstd/sys/windows/f64.rs b/src/libstd/sys/windows/f64.rs new file mode 100644 index 0000000000000..3e8504f86dc95 --- /dev/null +++ b/src/libstd/sys/windows/f64.rs @@ -0,0 +1,36 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +pub mod cmath { + use libc::{c_double, c_int}; + + #[link_name = "m"] + extern { + #[cfg_attr(target_env = "msvc", link_name = "__lgamma_r")] + pub fn lgamma_r(n: c_double, sign: &mut c_int) -> c_double; + + #[cfg_attr(target_env = "msvc", link_name = "_hypot")] + pub fn hypot(x: c_double, y: c_double) -> c_double; + } +} + +pub fn ln(x: f64) -> f64 { + unsafe { ::intrinsics::logf64(x) } +} + +pub fn log2(x: f64) -> f64 { + unsafe { ::intrinsics::log2f64(x) } +} + +pub fn log10(x: f64) -> f64 { + unsafe { ::intrinsics::log10f64(x) } +} From badd513e8c21f1c9d64931dab84eaa544720df88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Cassiers?= Date: Thu, 26 Jan 2017 23:35:49 +0100 Subject: [PATCH 03/15] Use platform-specific implementation for floats Also remove exceptions in tidy lint 'pal'. --- src/libstd/f32.rs | 132 ++++------------------------------ src/libstd/f64.rs | 47 +++--------- src/libstd/sys/redox/mod.rs | 2 + src/libstd/sys/unix/mod.rs | 2 + src/libstd/sys/windows/mod.rs | 2 + src/tools/tidy/src/pal.rs | 2 - 6 files changed, 28 insertions(+), 159 deletions(-) diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 7a676c041ad89..9ae30a6d71768 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -15,6 +15,9 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] +#[allow(unused_imports)] +use sys; + #[cfg(not(test))] use core::num; #[cfg(not(test))] @@ -40,6 +43,8 @@ pub use core::f32::consts; mod cmath { use libc::{c_float, c_int}; + pub use sys::f32::cmath::*; + extern { pub fn cbrtf(n: c_float) -> c_float; pub fn erff(n: c_float) -> c_float; @@ -55,88 +60,6 @@ mod cmath { pub fn modff(n: c_float, iptr: &mut c_float) -> c_float; pub fn nextafterf(x: c_float, y: c_float) -> c_float; pub fn tgammaf(n: c_float) -> c_float; - - #[cfg_attr(all(windows, target_env = "msvc"), link_name = "__lgammaf_r")] - pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float; - #[cfg_attr(all(windows, target_env = "msvc"), link_name = "_hypotf")] - pub fn hypotf(x: c_float, y: c_float) -> c_float; - } - - // See the comments in the `floor` function for why MSVC is special - // here. - #[cfg(not(target_env = "msvc"))] - extern { - pub fn acosf(n: c_float) -> c_float; - pub fn asinf(n: c_float) -> c_float; - pub fn atan2f(a: c_float, b: c_float) -> c_float; - pub fn atanf(n: c_float) -> c_float; - pub fn coshf(n: c_float) -> c_float; - pub fn frexpf(n: c_float, value: &mut c_int) -> c_float; - pub fn ldexpf(x: c_float, n: c_int) -> c_float; - pub fn sinhf(n: c_float) -> c_float; - pub fn tanf(n: c_float) -> c_float; - pub fn tanhf(n: c_float) -> c_float; - } - - #[cfg(target_env = "msvc")] - pub use self::shims::*; - #[cfg(target_env = "msvc")] - mod shims { - use libc::{c_float, c_int}; - - #[inline] - pub unsafe fn acosf(n: c_float) -> c_float { - f64::acos(n as f64) as c_float - } - - #[inline] - pub unsafe fn asinf(n: c_float) -> c_float { - f64::asin(n as f64) as c_float - } - - #[inline] - pub unsafe fn atan2f(n: c_float, b: c_float) -> c_float { - f64::atan2(n as f64, b as f64) as c_float - } - - #[inline] - pub unsafe fn atanf(n: c_float) -> c_float { - f64::atan(n as f64) as c_float - } - - #[inline] - pub unsafe fn coshf(n: c_float) -> c_float { - f64::cosh(n as f64) as c_float - } - - #[inline] - #[allow(deprecated)] - pub unsafe fn frexpf(x: c_float, value: &mut c_int) -> c_float { - let (a, b) = f64::frexp(x as f64); - *value = b as c_int; - a as c_float - } - - #[inline] - #[allow(deprecated)] - pub unsafe fn ldexpf(x: c_float, n: c_int) -> c_float { - f64::ldexp(x as f64, n as isize) as c_float - } - - #[inline] - pub unsafe fn sinhf(n: c_float) -> c_float { - f64::sinh(n as f64) as c_float - } - - #[inline] - pub unsafe fn tanf(n: c_float) -> c_float { - f64::tan(n as f64) as c_float - } - - #[inline] - pub unsafe fn tanhf(n: c_float) -> c_float { - f64::tanh(n as f64) as c_float - } } } @@ -301,10 +224,7 @@ impl f32 { // Note that there are many MSVC-specific float operations which // redirect to this comment, so `floorf` is just one case of a missing // function on MSVC, but there are many others elsewhere. - #[cfg(target_env = "msvc")] - return (self as f64).floor() as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::floorf32(self) }; + sys::f32::floor(self) } /// Returns the smallest integer greater than or equal to a number. @@ -320,10 +240,7 @@ impl f32 { #[inline] pub fn ceil(self) -> f32 { // see notes above in `floor` - #[cfg(target_env = "msvc")] - return (self as f64).ceil() as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::ceilf32(self) }; + sys::f32::ceil(self) } /// Returns the nearest integer to a number. Round half-way cases away from @@ -519,10 +436,7 @@ impl f32 { #[inline] pub fn powf(self, n: f32) -> f32 { // see notes above in `floor` - #[cfg(target_env = "msvc")] - return (self as f64).powf(n as f64) as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::powf32(self, n) }; + sys::f32::powf(self, n) } /// Takes the square root of a number. @@ -568,10 +482,7 @@ impl f32 { #[inline] pub fn exp(self) -> f32 { // see notes above in `floor` - #[cfg(target_env = "msvc")] - return (self as f64).exp() as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::expf32(self) }; + sys::f32::exp(self) } /// Returns `2^(self)`. @@ -610,10 +521,7 @@ impl f32 { #[inline] pub fn ln(self) -> f32 { // see notes above in `floor` - #[cfg(target_env = "msvc")] - return (self as f64).ln() as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::logf32(self) }; + sys::f32::ln(self) } /// Returns the logarithm of the number with respect to an arbitrary base. @@ -652,10 +560,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log2(self) -> f32 { - #[cfg(target_os = "android")] - return ::sys::android::log2f32(self); - #[cfg(not(target_os = "android"))] - return unsafe { intrinsics::log2f32(self) }; + sys::f32::log2(self) } /// Returns the base 10 logarithm of the number. @@ -674,10 +579,7 @@ impl f32 { #[inline] pub fn log10(self) -> f32 { // see notes above in `floor` - #[cfg(target_env = "msvc")] - return (self as f64).log10() as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::log10f32(self) }; + sys::f32::log10(self) } /// Converts radians to degrees. @@ -908,10 +810,7 @@ impl f32 { #[inline] pub fn sin(self) -> f32 { // see notes in `core::f32::Float::floor` - #[cfg(target_env = "msvc")] - return (self as f64).sin() as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::sinf32(self) }; + sys::f32::sin(self) } /// Computes the cosine of a number (in radians). @@ -929,10 +828,7 @@ impl f32 { #[inline] pub fn cos(self) -> f32 { // see notes in `core::f32::Float::floor` - #[cfg(target_env = "msvc")] - return (self as f64).cos() as f32; - #[cfg(not(target_env = "msvc"))] - return unsafe { intrinsics::cosf32(self) }; + sys::f32::cos(self) } /// Computes the tangent of a number (in radians). diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index 67a1c302483d2..0b40358526224 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -35,10 +35,15 @@ pub use core::f64::{MIN, MIN_POSITIVE, MAX}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::f64::consts; +#[allow(unused_imports)] +use sys; + #[allow(dead_code)] mod cmath { use libc::{c_double, c_int}; + pub use sys::f64::cmath::*; + #[link_name = "m"] extern { pub fn acos(n: c_double) -> c_double; @@ -75,12 +80,6 @@ mod cmath { pub fn y0(n: c_double) -> c_double; pub fn y1(n: c_double) -> c_double; pub fn yn(i: c_int, n: c_double) -> c_double; - - #[cfg_attr(all(windows, target_env = "msvc"), link_name = "__lgamma_r")] - pub fn lgamma_r(n: c_double, sign: &mut c_int) -> c_double; - - #[cfg_attr(all(windows, target_env = "msvc"), link_name = "_hypot")] - pub fn hypot(x: c_double, y: c_double) -> c_double; } } @@ -515,7 +514,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn ln(self) -> f64 { - self.log_wrapper(|n| { unsafe { intrinsics::logf64(n) } }) + sys::f64::ln(self) } /// Returns the logarithm of the number with respect to an arbitrary base. @@ -550,12 +549,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log2(self) -> f64 { - self.log_wrapper(|n| { - #[cfg(target_os = "android")] - return ::sys::android::log2f64(n); - #[cfg(not(target_os = "android"))] - return unsafe { intrinsics::log2f64(n) }; - }) + sys::f64::log2(self) } /// Returns the base 10 logarithm of the number. @@ -571,7 +565,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log10(self) -> f64 { - self.log_wrapper(|n| { unsafe { intrinsics::log10f64(n) } }) + sys::f64::log10(self) } /// Converts radians to degrees. @@ -1091,31 +1085,6 @@ impl f64 { pub fn atanh(self) -> f64 { 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() } - - // Solaris/Illumos requires a wrapper around log, log2, and log10 functions - // because of their non-standard behavior (e.g. log(-n) returns -Inf instead - // of expected NaN). - fn log_wrapper f64>(self, log_fn: F) -> f64 { - if !cfg!(target_os = "solaris") { - log_fn(self) - } else { - if self.is_finite() { - if self > 0.0 { - log_fn(self) - } else if self == 0.0 { - NEG_INFINITY // log(0) = -Inf - } else { - NAN // log(-n) = NaN - } - } else if self.is_nan() { - self // log(NaN) = NaN - } else if self > 0.0 { - self // log(Inf) = Inf - } else { - NAN // log(-Inf) = NaN - } - } - } } #[cfg(test)] diff --git a/src/libstd/sys/redox/mod.rs b/src/libstd/sys/redox/mod.rs index 5982bdd6549ca..09880b74adfb5 100644 --- a/src/libstd/sys/redox/mod.rs +++ b/src/libstd/sys/redox/mod.rs @@ -18,6 +18,8 @@ pub mod backtrace; pub mod condvar; pub mod env; pub mod ext; +pub mod f32; +pub mod f64; pub mod fast_thread_local; pub mod fd; pub mod fs; diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index fd7dc17cccd8c..99a5b41048e66 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -38,6 +38,8 @@ pub mod backtrace; pub mod condvar; pub mod env; pub mod ext; +pub mod f32; +pub mod f64; pub mod fast_thread_local; pub mod fd; pub mod fs; diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index defc41c5f46a3..3d22d282bef5c 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -25,6 +25,8 @@ pub mod condvar; pub mod dynamic_lib; pub mod env; pub mod ext; +pub mod f32; +pub mod f64; pub mod fs; pub mod handle; pub mod memchr; diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index 0a65de7468708..82fd68652b2d6 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -66,8 +66,6 @@ const EXCEPTION_PATHS: &'static [&'static str] = &[ // temporary exceptions "src/libstd/rtdeps.rs", // Until rustbuild replaces make - "src/libstd/f32.rs", - "src/libstd/f64.rs", "src/libstd/sys_common/mod.rs", "src/libstd/sys_common/net.rs", "src/libterm", // Not sure how to make this crate portable, but test needs it From d90a7b3b006be912493ddf15d0dc5895a6929b38 Mon Sep 17 00:00:00 2001 From: f001 Date: Sun, 5 Feb 2017 17:39:52 +0800 Subject: [PATCH 04/15] std: Add retain method for HashMap and HashSet Fix #36648 --- src/libstd/collections/hash/map.rs | 114 ++++++++++++++++++--------- src/libstd/collections/hash/set.rs | 33 ++++++++ src/libstd/collections/hash/table.rs | 81 +++++++++++++++++-- 3 files changed, 182 insertions(+), 46 deletions(-) diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 8058972e75093..f689589dfa25f 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -416,22 +416,26 @@ fn search_hashed(table: M, hash: SafeHash, mut is_match: F) -> Inter } } -fn pop_internal(starting_bucket: FullBucketMut) -> (K, V) { +fn pop_internal(starting_bucket: FullBucketMut) + -> (K, V, &mut RawTable) +{ let (empty, retkey, retval) = starting_bucket.take(); let mut gap = match empty.gap_peek() { - Some(b) => b, - None => return (retkey, retval), + Ok(b) => b, + Err(b) => return (retkey, retval, b.into_table()), }; while gap.full().displacement() != 0 { gap = match gap.shift() { - Some(b) => b, - None => break, + Ok(b) => b, + Err(b) => { + return (retkey, retval, b.into_table()); + }, }; } // Now we've done all our shifting. Return the value we grabbed earlier. - (retkey, retval) + (retkey, retval, gap.into_bucket().into_table()) } /// Perform robin hood bucket stealing at the given `bucket`. You must @@ -721,38 +725,7 @@ impl HashMap return; } - // Grow the table. - // Specialization of the other branch. - let mut bucket = Bucket::first(&mut old_table); - - // "So a few of the first shall be last: for many be called, - // but few chosen." - // - // We'll most likely encounter a few buckets at the beginning that - // have their initial buckets near the end of the table. They were - // placed at the beginning as the probe wrapped around the table - // during insertion. We must skip forward to a bucket that won't - // get reinserted too early and won't unfairly steal others spot. - // This eliminates the need for robin hood. - loop { - bucket = match bucket.peek() { - Full(full) => { - if full.displacement() == 0 { - // This bucket occupies its ideal spot. - // It indicates the start of another "cluster". - bucket = full.into_bucket(); - break; - } - // Leaving this bucket in the last cluster for later. - full.into_bucket() - } - Empty(b) => { - // Encountered a hole between clusters. - b.into_bucket() - } - }; - bucket.next(); - } + let mut bucket = Bucket::head_bucket(&mut old_table); // This is how the buckets might be laid out in memory: // ($ marks an initialized bucket) @@ -1208,6 +1181,57 @@ impl HashMap self.search_mut(k).into_occupied_bucket().map(|bucket| pop_internal(bucket).1) } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all pairs `(k, v)` such that `f(&k,&mut v)` returns `false`. + /// + /// # Examples + /// + /// ``` + /// #![feature(retain_hash_collection)] + /// use std::collections::HashMap; + /// + /// let mut map: HashMap = (0..8).map(|x|(x, x*10)).collect(); + /// map.retain(|&k, _| k % 2 == 0); + /// assert_eq!(map.len(), 4); + /// ``` + #[unstable(feature = "retain_hash_collection", issue = "36648")] + pub fn retain(&mut self, mut f: F) + where F: FnMut(&K, &mut V) -> bool + { + if self.table.capacity() == 0 || self.table.size() == 0 { + return; + } + let mut bucket = Bucket::head_bucket(&mut self.table); + bucket.prev(); + let tail = bucket.index(); + loop { + bucket = match bucket.peek() { + Full(mut full) => { + let should_remove = { + let (k, v) = full.read_mut(); + !f(k, v) + }; + if should_remove { + let prev_idx = full.index(); + let prev_raw = full.raw(); + let (_, _, t) = pop_internal(full); + Bucket::new_from(prev_raw, prev_idx, t) + } else { + full.into_bucket() + } + }, + Empty(b) => { + b.into_bucket() + } + }; + bucket.prev(); // reverse iteration + if bucket.index() == tail { + break; + } + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1862,7 +1886,8 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// ``` #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] pub fn remove_entry(self) -> (K, V) { - pop_internal(self.elem) + let (k, v, _) = pop_internal(self.elem); + (k, v) } /// Gets a reference to the value in the entry. @@ -3156,4 +3181,15 @@ mod test_map { assert_eq!(a.len(), 1); assert_eq!(a[key], value); } + + #[test] + fn test_retain() { + let mut map: HashMap = (0..100).map(|x|(x, x*10)).collect(); + + map.retain(|&k, _| k % 2 == 0); + assert_eq!(map.len(), 50); + assert_eq!(map[&2], 20); + assert_eq!(map[&4], 40); + assert_eq!(map[&6], 60); + } } diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index 341b050862f5c..8de742db46110 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -624,6 +624,28 @@ impl HashSet { Recover::take(&mut self.map, value) } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` such that `f(&e)` returns `false`. + /// + /// # Examples + /// + /// ``` + /// #![feature(retain_hash_collection)] + /// use std::collections::HashSet; + /// + /// let xs = [1,2,3,4,5,6]; + /// let mut set: HashSet = xs.iter().cloned().collect(); + /// set.retain(|&k| k % 2 == 0); + /// assert_eq!(set.len(), 3); + /// ``` + #[unstable(feature = "retain_hash_collection", issue = "36648")] + pub fn retain(&mut self, mut f: F) + where F: FnMut(&T) -> bool + { + self.map.retain(|k, _| f(k)); + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1605,4 +1627,15 @@ mod test_set { assert!(a.contains(&5)); assert!(a.contains(&6)); } + + #[test] + fn test_retain() { + let xs = [1,2,3,4,5,6]; + let mut set: HashSet = xs.iter().cloned().collect(); + set.retain(|&k| k % 2 == 0); + assert_eq!(set.len(), 3); + assert!(set.contains(&2)); + assert!(set.contains(&4)); + assert!(set.contains(&6)); + } } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 1ab62130cd3dd..9e92b4750145e 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -85,7 +85,7 @@ pub struct RawTable { unsafe impl Send for RawTable {} unsafe impl Sync for RawTable {} -struct RawBucket { +pub struct RawBucket { hash: *mut HashUint, // We use *const to ensure covariance with respect to K and V pair: *const (K, V), @@ -216,6 +216,10 @@ impl FullBucket { pub fn index(&self) -> usize { self.idx } + /// Get the raw bucket. + pub fn raw(&self) -> RawBucket { + self.raw + } } impl EmptyBucket { @@ -230,6 +234,10 @@ impl Bucket { pub fn index(&self) -> usize { self.idx } + /// get the table. + pub fn into_table(self) -> M { + self.table + } } impl Deref for FullBucket @@ -275,6 +283,16 @@ impl>> Bucket { Bucket::at_index(table, hash.inspect() as usize) } + pub fn new_from(r: RawBucket, i: usize, t: M) + -> Bucket + { + Bucket { + raw: r, + idx: i, + table: t, + } + } + pub fn at_index(table: M, ib_index: usize) -> Bucket { // if capacity is 0, then the RawBucket will be populated with bogus pointers. // This is an uncommon case though, so avoid it in release builds. @@ -296,6 +314,40 @@ impl>> Bucket { } } + // "So a few of the first shall be last: for many be called, + // but few chosen." + // + // We'll most likely encounter a few buckets at the beginning that + // have their initial buckets near the end of the table. They were + // placed at the beginning as the probe wrapped around the table + // during insertion. We must skip forward to a bucket that won't + // get reinserted too early and won't unfairly steal others spot. + // This eliminates the need for robin hood. + pub fn head_bucket(table: M) -> Bucket { + let mut bucket = Bucket::first(table); + + loop { + bucket = match bucket.peek() { + Full(full) => { + if full.displacement() == 0 { + // This bucket occupies its ideal spot. + // It indicates the start of another "cluster". + bucket = full.into_bucket(); + break; + } + // Leaving this bucket in the last cluster for later. + full.into_bucket() + } + Empty(b) => { + // Encountered a hole between clusters. + b.into_bucket() + } + }; + bucket.next(); + } + bucket + } + /// Reads a bucket at a given index, returning an enum indicating whether /// it's initialized or not. You need to match on this enum to get /// the appropriate types to call most of the other functions in @@ -333,6 +385,17 @@ impl>> Bucket { self.raw = self.raw.offset(dist); } } + + /// Modifies the bucket pointer in place to make it point to the previous slot. + pub fn prev(&mut self) { + let range = self.table.capacity(); + let new_idx = self.idx.wrapping_sub(1) & (range - 1); + let dist = (new_idx as isize).wrapping_sub(self.idx as isize); + self.idx = new_idx; + unsafe { + self.raw = self.raw.offset(dist); + } + } } impl>> EmptyBucket { @@ -352,7 +415,7 @@ impl>> EmptyBucket { } } - pub fn gap_peek(self) -> Option> { + pub fn gap_peek(self) -> Result, Bucket> { let gap = EmptyBucket { raw: self.raw, idx: self.idx, @@ -361,12 +424,12 @@ impl>> EmptyBucket { match self.next().peek() { Full(bucket) => { - Some(GapThenFull { + Ok(GapThenFull { gap: gap, full: bucket, }) } - Empty(..) => None, + Empty(e) => Err(e.into_bucket()), } } } @@ -529,7 +592,11 @@ impl GapThenFull &self.full } - pub fn shift(mut self) -> Option> { + pub fn into_bucket(self) -> Bucket { + self.full.into_bucket() + } + + pub fn shift(mut self) -> Result, Bucket> { unsafe { *self.gap.raw.hash = mem::replace(&mut *self.full.raw.hash, EMPTY_BUCKET); ptr::copy_nonoverlapping(self.full.raw.pair, self.gap.raw.pair as *mut (K, V), 1); @@ -544,9 +611,9 @@ impl GapThenFull self.full = bucket; - Some(self) + Ok(self) } - Empty(..) => None, + Empty(b) => Err(b.into_bucket()), } } } From 4c3448f356dcb04150db20dcef92da688d49a4d5 Mon Sep 17 00:00:00 2001 From: Zack Weinberg Date: Wed, 8 Feb 2017 17:43:48 -0500 Subject: [PATCH 05/15] Add equivalents of C's functions to AsciiExt. * `is_ascii_alphabetic` * `is_ascii_uppercase` * `is_ascii_lowercase` * `is_ascii_alphanumeric` * `is_ascii_digit` * `is_ascii_hexdigit` * `is_ascii_punctuation` * `is_ascii_graphic` * `is_ascii_whitespace` * `is_ascii_control` This addresses issue #39658. --- src/libstd/ascii.rs | 838 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 838 insertions(+) diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs index 35c388ba076ce..cf965291fa2a6 100644 --- a/src/libstd/ascii.rs +++ b/src/libstd/ascii.rs @@ -184,6 +184,348 @@ pub trait AsciiExt { /// [`to_ascii_lowercase`]: #tymethod.to_ascii_lowercase #[stable(feature = "ascii", since = "1.9.0")] fn make_ascii_lowercase(&mut self); + + /// Checks if the value is an ASCII alphabetic character: + /// U+0041 'A' ... U+005A 'Z' or U+0061 'a' ... U+007A 'z'. + /// For strings, true if all characters in the string are + /// ASCII alphabetic. + /// + /// # Examples + /// + /// ``` + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(A.is_ascii_alphabetic()); + /// assert!(G.is_ascii_alphabetic()); + /// assert!(a.is_ascii_alphabetic()); + /// assert!(g.is_ascii_alphabetic()); + /// assert!(!zero.is_ascii_alphabetic()); + /// assert!(!percent.is_ascii_alphabetic()); + /// assert!(!space.is_ascii_alphabetic()); + /// assert!(!lf.is_ascii_alphabetic()); + /// assert!(!esc.is_ascii_alphabetic()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_alphabetic(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII uppercase character: + /// U+0041 'A' ... U+005A 'Z'. + /// For strings, true if all characters in the string are + /// ASCII uppercase. + /// + /// # Examples + /// + /// ``` + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(A.is_ascii_uppercase()); + /// assert!(G.is_ascii_uppercase()); + /// assert!(!a.is_ascii_uppercase()); + /// assert!(!g.is_ascii_uppercase()); + /// assert!(!zero.is_ascii_uppercase()); + /// assert!(!percent.is_ascii_uppercase()); + /// assert!(!space.is_ascii_uppercase()); + /// assert!(!lf.is_ascii_uppercase()); + /// assert!(!esc.is_ascii_uppercase()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_uppercase(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII lowercase character: + /// U+0061 'a' ... U+007A 'z'. + /// For strings, true if all characters in the string are + /// ASCII lowercase. + /// + /// # Examples + /// + /// ``` + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(!A.is_ascii_lowercase()); + /// assert!(!G.is_ascii_lowercase()); + /// assert!(a.is_ascii_lowercase()); + /// assert!(g.is_ascii_lowercase()); + /// assert!(!zero.is_ascii_lowercase()); + /// assert!(!percent.is_ascii_lowercase()); + /// assert!(!space.is_ascii_lowercase()); + /// assert!(!lf.is_ascii_lowercase()); + /// assert!(!esc.is_ascii_lowercase()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_lowercase(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII alphanumeric character: + /// U+0041 'A' ... U+005A 'Z', U+0061 'a' ... U+007A 'z', or + /// U+0030 '0' ... U+0039 '9'. + /// For strings, true if all characters in the string are + /// ASCII alphanumeric. + /// + /// # Examples + /// + /// ``` + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(A.is_ascii_alphanumeric()); + /// assert!(G.is_ascii_alphanumeric()); + /// assert!(a.is_ascii_alphanumeric()); + /// assert!(g.is_ascii_alphanumeric()); + /// assert!(zero.is_ascii_alphanumeric()); + /// assert!(!percent.is_ascii_alphanumeric()); + /// assert!(!space.is_ascii_alphanumeric()); + /// assert!(!lf.is_ascii_alphanumeric()); + /// assert!(!esc.is_ascii_alphanumeric()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_alphanumeric(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII decimal digit: + /// U+0030 '0' ... U+0039 '9'. + /// For strings, true if all characters in the string are + /// ASCII digits. + /// + /// # Examples + /// + /// ``` + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(!A.is_ascii_digit()); + /// assert!(!G.is_ascii_digit()); + /// assert!(!a.is_ascii_digit()); + /// assert!(!g.is_ascii_digit()); + /// assert!(zero.is_ascii_digit()); + /// assert!(!percent.is_ascii_digit()); + /// assert!(!space.is_ascii_digit()); + /// assert!(!lf.is_ascii_digit()); + /// assert!(!esc.is_ascii_digit()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_digit(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII hexadecimal digit: + /// U+0030 '0' ... U+0039 '9', U+0041 'A' ... U+0046 'F', or + /// U+0061 'a' ... U+0066 'f'. + /// For strings, true if all characters in the string are + /// ASCII hex digits. + /// + /// # Examples + /// + /// ``` + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(A.is_ascii_hexdigit()); + /// assert!(!G.is_ascii_hexdigit()); + /// assert!(a.is_ascii_hexdigit()); + /// assert!(!g.is_ascii_hexdigit()); + /// assert!(zero.is_ascii_hexdigit()); + /// assert!(!percent.is_ascii_hexdigit()); + /// assert!(!space.is_ascii_hexdigit()); + /// assert!(!lf.is_ascii_hexdigit()); + /// assert!(!esc.is_ascii_hexdigit()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_hexdigit(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII punctuation character: + /// U+0021 ... U+002F `! " # $ % & ' ( ) * + , - . /` + /// U+003A ... U+0040 `: ; < = > ? @` + /// U+005B ... U+0060 `[ \\ ] ^ _ \`` + /// U+007B ... U+007E `{ | } ~` + /// For strings, true if all characters in the string are + /// ASCII punctuation. + /// + /// # Examples + /// + /// ``` + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(!A.is_ascii_punctuation()); + /// assert!(!G.is_ascii_punctuation()); + /// assert!(!a.is_ascii_punctuation()); + /// assert!(!g.is_ascii_punctuation()); + /// assert!(!zero.is_ascii_punctuation()); + /// assert!(percent.is_ascii_punctuation()); + /// assert!(!space.is_ascii_punctuation()); + /// assert!(!lf.is_ascii_punctuation()); + /// assert!(!esc.is_ascii_punctuation()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_punctuation(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII graphic character: + /// U+0021 '@' ... U+007E '~'. + /// For strings, true if all characters in the string are + /// ASCII punctuation. + /// + /// # Examples + /// + /// ``` + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(A.is_ascii_graphic()); + /// assert!(G.is_ascii_graphic()); + /// assert!(a.is_ascii_graphic()); + /// assert!(g.is_ascii_graphic()); + /// assert!(zero.is_ascii_graphic()); + /// assert!(percent.is_ascii_graphic()); + /// assert!(!space.is_ascii_graphic()); + /// assert!(!lf.is_ascii_graphic()); + /// assert!(!esc.is_ascii_graphic()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_graphic(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII whitespace character: + /// U+0020 SPACE, U+0009 HORIZONTAL TAB, U+000A LINE FEED, + /// U+000C FORM FEED, or U+000D CARRIAGE RETURN. + /// For strings, true if all characters in the string are + /// ASCII whitespace. + /// + /// Rust uses the WhatWG Infra Standard's [definition of ASCII + /// whitespace][infra-aw]. There are several other definitions in + /// wide use. For instance, [the POSIX locale][posix-ctype] + /// includes U+000B VERTICAL TAB as well as all the above + /// characters, but—from the very same specification—[the default + /// rule for "field splitting" in the Bourne shell][field-splitting] + /// considers *only* SPACE, HORIZONTAL TAB, and LINE FEED as whitespace. + /// + /// If you are writing a program that will process an existing + /// file format, check what that format's definition of whitespace is + /// before using this function. + /// + /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace + /// [posix-ctype]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 + /// [field-splitting]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 + /// + /// # Examples + /// + /// ``` + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(!A.is_ascii_whitespace()); + /// assert!(!G.is_ascii_whitespace()); + /// assert!(!a.is_ascii_whitespace()); + /// assert!(!g.is_ascii_whitespace()); + /// assert!(!zero.is_ascii_whitespace()); + /// assert!(!percent.is_ascii_whitespace()); + /// assert!(space.is_ascii_whitespace()); + /// assert!(lf.is_ascii_whitespace()); + /// assert!(!esc.is_ascii_whitespace()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_whitespace(&self) -> bool { unimplemented!(); } + + /// Checks if the value is an ASCII control character: + /// U+0000 NUL ... U+001F UNIT SEPARATOR, or U+007F DELETE. + /// Note that most ASCII whitespace characters are control + /// characters, but SPACE is not. + /// + /// # Examples + /// + /// ``` + /// use std::ascii::AsciiExt; + /// let A = 'A'; + /// let G = 'G'; + /// let a = 'a'; + /// let g = 'g'; + /// let zero = '0'; + /// let percent = '%'; + /// let space = ' '; + /// let lf = '\n'; + /// let esc = '\u{001b}'; + /// + /// assert!(!A.is_ascii_control()); + /// assert!(!G.is_ascii_control()); + /// assert!(!a.is_ascii_control()); + /// assert!(!g.is_ascii_control()); + /// assert!(!zero.is_ascii_control()); + /// assert!(!percent.is_ascii_control()); + /// assert!(!space.is_ascii_control()); + /// assert!(lf.is_ascii_control()); + /// assert!(esc.is_ascii_control()); + /// ``` + #[unstable(feature = "ascii_ctype", issue = "39658")] + fn is_ascii_control(&self) -> bool { unimplemented!(); } } #[stable(feature = "rust1", since = "1.0.0")] @@ -225,6 +567,56 @@ impl AsciiExt for str { let me: &mut [u8] = unsafe { mem::transmute(self) }; me.make_ascii_lowercase() } + + #[inline] + fn is_ascii_alphabetic(&self) -> bool { + self.bytes().all(|b| b.is_ascii_alphabetic()) + } + + #[inline] + fn is_ascii_uppercase(&self) -> bool { + self.bytes().all(|b| b.is_ascii_uppercase()) + } + + #[inline] + fn is_ascii_lowercase(&self) -> bool { + self.bytes().all(|b| b.is_ascii_lowercase()) + } + + #[inline] + fn is_ascii_alphanumeric(&self) -> bool { + self.bytes().all(|b| b.is_ascii_alphanumeric()) + } + + #[inline] + fn is_ascii_digit(&self) -> bool { + self.bytes().all(|b| b.is_ascii_digit()) + } + + #[inline] + fn is_ascii_hexdigit(&self) -> bool { + self.bytes().all(|b| b.is_ascii_hexdigit()) + } + + #[inline] + fn is_ascii_punctuation(&self) -> bool { + self.bytes().all(|b| b.is_ascii_punctuation()) + } + + #[inline] + fn is_ascii_graphic(&self) -> bool { + self.bytes().all(|b| b.is_ascii_graphic()) + } + + #[inline] + fn is_ascii_whitespace(&self) -> bool { + self.bytes().all(|b| b.is_ascii_whitespace()) + } + + #[inline] + fn is_ascii_control(&self) -> bool { + self.bytes().all(|b| b.is_ascii_control()) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -268,6 +660,56 @@ impl AsciiExt for [u8] { byte.make_ascii_lowercase(); } } + + #[inline] + fn is_ascii_alphabetic(&self) -> bool { + self.iter().all(|b| b.is_ascii_alphabetic()) + } + + #[inline] + fn is_ascii_uppercase(&self) -> bool { + self.iter().all(|b| b.is_ascii_uppercase()) + } + + #[inline] + fn is_ascii_lowercase(&self) -> bool { + self.iter().all(|b| b.is_ascii_lowercase()) + } + + #[inline] + fn is_ascii_alphanumeric(&self) -> bool { + self.iter().all(|b| b.is_ascii_alphanumeric()) + } + + #[inline] + fn is_ascii_digit(&self) -> bool { + self.iter().all(|b| b.is_ascii_digit()) + } + + #[inline] + fn is_ascii_hexdigit(&self) -> bool { + self.iter().all(|b| b.is_ascii_hexdigit()) + } + + #[inline] + fn is_ascii_punctuation(&self) -> bool { + self.iter().all(|b| b.is_ascii_punctuation()) + } + + #[inline] + fn is_ascii_graphic(&self) -> bool { + self.iter().all(|b| b.is_ascii_graphic()) + } + + #[inline] + fn is_ascii_whitespace(&self) -> bool { + self.iter().all(|b| b.is_ascii_whitespace()) + } + + #[inline] + fn is_ascii_control(&self) -> bool { + self.iter().all(|b| b.is_ascii_control()) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -287,6 +729,96 @@ impl AsciiExt for u8 { fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase(); } #[inline] fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); } + + #[inline] + fn is_ascii_alphabetic(&self) -> bool { + if *self >= 0x80 { return false; } + match ASCII_CHARACTER_CLASS[*self as usize] { + L|Lx|U|Ux => true, + _ => false + } + } + + #[inline] + fn is_ascii_uppercase(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + U|Ux => true, + _ => false + } + } + + #[inline] + fn is_ascii_lowercase(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + L|Lx => true, + _ => false + } + } + + #[inline] + fn is_ascii_alphanumeric(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + D|L|Lx|U|Ux => true, + _ => false + } + } + + #[inline] + fn is_ascii_digit(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + D => true, + _ => false + } + } + + #[inline] + fn is_ascii_hexdigit(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + D|Lx|Ux => true, + _ => false + } + } + + #[inline] + fn is_ascii_punctuation(&self) -> bool { + if *self >= 0x80 { return false } + match ASCII_CHARACTER_CLASS[*self as usize] { + P => true, + _ => false + } + } + + #[inline] + fn is_ascii_graphic(&self) -> bool { + if *self >= 0x80 { return false; } + match ASCII_CHARACTER_CLASS[*self as usize] { + Ux|U|Lx|L|D|P => true, + _ => false + } + } + + #[inline] + fn is_ascii_whitespace(&self) -> bool { + if *self >= 0x80 { return false; } + match ASCII_CHARACTER_CLASS[*self as usize] { + Cw|W => true, + _ => false + } + } + + #[inline] + fn is_ascii_control(&self) -> bool { + if *self >= 0x80 { return false; } + match ASCII_CHARACTER_CLASS[*self as usize] { + C|Cw => true, + _ => false + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -324,6 +856,56 @@ impl AsciiExt for char { fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase(); } #[inline] fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); } + + #[inline] + fn is_ascii_alphabetic(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_alphabetic() + } + + #[inline] + fn is_ascii_uppercase(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_uppercase() + } + + #[inline] + fn is_ascii_lowercase(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_lowercase() + } + + #[inline] + fn is_ascii_alphanumeric(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_alphanumeric() + } + + #[inline] + fn is_ascii_digit(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_digit() + } + + #[inline] + fn is_ascii_hexdigit(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_hexdigit() + } + + #[inline] + fn is_ascii_punctuation(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_punctuation() + } + + #[inline] + fn is_ascii_graphic(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_graphic() + } + + #[inline] + fn is_ascii_whitespace(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_whitespace() + } + + #[inline] + fn is_ascii_control(&self) -> bool { + (*self as u32 <= 0x7f) && (*self as u8).is_ascii_control() + } } /// An iterator over the escaped version of a byte, constructed via @@ -485,6 +1067,30 @@ static ASCII_UPPERCASE_MAP: [u8; 256] = [ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, ]; +enum AsciiCharacterClass { + C, // control + Cw, // control whitespace + W, // whitespace + D, // digit + L, // lowercase + Lx, // lowercase hex digit + U, // uppercase + Ux, // uppercase hex digit + P, // punctuation +} +use self::AsciiCharacterClass::*; + +static ASCII_CHARACTER_CLASS: [AsciiCharacterClass; 128] = [ +// _0 _1 _2 _3 _4 _5 _6 _7 _8 _9 _a _b _c _d _e _f + C, C, C, C, C, C, C, C, C, Cw,Cw,C, Cw,Cw,C, C, // 0_ + C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, // 1_ + W, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, // 2_ + D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, P, // 3_ + P, Ux,Ux,Ux,Ux,Ux,Ux,U, U, U, U, U, U, U, U, U, // 4_ + U, U, U, U, U, U, U, U, U, U, U, P, P, P, P, P, // 5_ + P, Lx,Lx,Lx,Lx,Lx,Lx,L, L, L, L, L, L, L, L, L, // 6_ + L, L, L, L, L, L, L, L, L, L, L, P, P, P, P, C, // 7_ +]; #[cfg(test)] mod tests { @@ -606,4 +1212,236 @@ mod tests { let x = "a".to_string(); x.eq_ignore_ascii_case("A"); } + + // Shorthands used by the is_ascii_* tests. + macro_rules! assert_all { + ($what:ident, $($str:tt),+) => {{ + $( + for b in $str.chars() { + if !b.$what() { + panic!("expected {}({}) but it isn't", + stringify!($what), b); + } + } + for b in $str.as_bytes().iter() { + if !b.$what() { + panic!("expected {}(0x{:02x})) but it isn't", + stringify!($what), b); + } + } + assert!($str.$what()); + assert!($str.as_bytes().$what()); + )+ + }}; + ($what:ident, $($str:tt),+,) => (assert_all!($what,$($str),+)) + } + macro_rules! assert_none { + ($what:ident, $($str:tt),+) => {{ + $( + for b in $str.chars() { + if b.$what() { + panic!("expected not-{}({}) but it is", + stringify!($what), b); + } + } + for b in $str.as_bytes().iter() { + if b.$what() { + panic!("expected not-{}(0x{:02x})) but it is", + stringify!($what), b); + } + } + )* + }}; + ($what:ident, $($str:tt),+,) => (assert_none!($what,$($str),+)) + } + + #[test] + fn test_is_ascii_alphabetic() { + assert_all!(is_ascii_alphabetic, + "", + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + ); + assert_none!(is_ascii_alphabetic, + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_uppercase() { + assert_all!(is_ascii_uppercase, + "", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + ); + assert_none!(is_ascii_uppercase, + "abcdefghijklmnopqrstuvwxyz", + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_lowercase() { + assert_all!(is_ascii_lowercase, + "abcdefghijklmnopqrstuvwxyz", + ); + assert_none!(is_ascii_lowercase, + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_alphanumeric() { + assert_all!(is_ascii_alphanumeric, + "", + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + ); + assert_none!(is_ascii_alphanumeric, + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_digit() { + assert_all!(is_ascii_digit, + "", + "0123456789", + ); + assert_none!(is_ascii_digit, + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_hexdigit() { + assert_all!(is_ascii_hexdigit, + "", + "0123456789", + "abcdefABCDEF", + ); + assert_none!(is_ascii_hexdigit, + "ghijklmnopqrstuvwxyz", + "GHIJKLMNOQPRSTUVWXYZ", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_punctuation() { + assert_all!(is_ascii_punctuation, + "", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + ); + assert_none!(is_ascii_punctuation, + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_graphic() { + assert_all!(is_ascii_graphic, + "", + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + ); + assert_none!(is_ascii_graphic, + " \t\n\x0c\r", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_whitespace() { + assert_all!(is_ascii_whitespace, + "", + " \t\n\x0c\r", + ); + assert_none!(is_ascii_whitespace, + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x0b\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + } + + #[test] + fn test_is_ascii_control() { + assert_all!(is_ascii_control, + "", + "\x00\x01\x02\x03\x04\x05\x06\x07", + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17", + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x7f", + ); + assert_none!(is_ascii_control, + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOQPRSTUVWXYZ", + "0123456789", + "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + " ", + ); + } } From fb9104768c0991b935e4b5cbc67180e49d425f2c Mon Sep 17 00:00:00 2001 From: Brian Vincent Date: Thu, 2 Feb 2017 00:58:18 -0600 Subject: [PATCH 06/15] Dont segfault if btree range is not in order --- src/libcollections/btree/map.rs | 204 +++++++++++++--------------- src/libcollections/btree/search.rs | 3 +- src/libcollectionstest/btree/map.rs | 42 ++++++ 3 files changed, 134 insertions(+), 115 deletions(-) diff --git a/src/libcollections/btree/map.rs b/src/libcollections/btree/map.rs index e1fabe2cc496b..7218d15ded5f8 100644 --- a/src/libcollections/btree/map.rs +++ b/src/libcollections/btree/map.rs @@ -714,6 +714,11 @@ impl BTreeMap { /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive /// range from 4 to 10. /// + /// # Panics + /// + /// Panics if range `start > end`. + /// Panics if range `start == end` and both bounds are `Excluded`. + /// /// # Examples /// /// Basic usage: @@ -739,64 +744,11 @@ impl BTreeMap { pub fn range(&self, range: R) -> Range where T: Ord, K: Borrow, R: RangeArgument { - let min = range.start(); - let max = range.end(); - let front = match min { - Included(key) => { - match search::search_tree(self.root.as_ref(), key) { - Found(kv_handle) => { - match kv_handle.left_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => last_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Excluded(key) => { - match search::search_tree(self.root.as_ref(), key) { - Found(kv_handle) => { - match kv_handle.right_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => first_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Unbounded => first_leaf_edge(self.root.as_ref()), - }; + let root1 = self.root.as_ref(); + let root2 = self.root.as_ref(); + let (f, b) = range_search(root1, root2, range); - let back = match max { - Included(key) => { - match search::search_tree(self.root.as_ref(), key) { - Found(kv_handle) => { - match kv_handle.right_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => first_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Excluded(key) => { - match search::search_tree(self.root.as_ref(), key) { - Found(kv_handle) => { - match kv_handle.left_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => last_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Unbounded => last_leaf_edge(self.root.as_ref()), - }; - - Range { - front: front, - back: back, - } + Range { front: f, back: b} } /// Constructs a mutable double-ended iterator over a sub-range of elements in the map. @@ -806,6 +758,11 @@ impl BTreeMap { /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive /// range from 4 to 10. /// + /// # Panics + /// + /// Panics if range `start > end`. + /// Panics if range `start == end` and both bounds are `Excluded`. + /// /// # Examples /// /// Basic usage: @@ -831,66 +788,13 @@ impl BTreeMap { pub fn range_mut(&mut self, range: R) -> RangeMut where T: Ord, K: Borrow, R: RangeArgument { - let min = range.start(); - let max = range.end(); let root1 = self.root.as_mut(); let root2 = unsafe { ptr::read(&root1) }; - - let front = match min { - Included(key) => { - match search::search_tree(root1, key) { - Found(kv_handle) => { - match kv_handle.left_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => last_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Excluded(key) => { - match search::search_tree(root1, key) { - Found(kv_handle) => { - match kv_handle.right_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => first_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Unbounded => first_leaf_edge(root1), - }; - - let back = match max { - Included(key) => { - match search::search_tree(root2, key) { - Found(kv_handle) => { - match kv_handle.right_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => first_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Excluded(key) => { - match search::search_tree(root2, key) { - Found(kv_handle) => { - match kv_handle.left_edge().force() { - Leaf(bottom) => bottom, - Internal(internal) => last_leaf_edge(internal.descend()), - } - } - GoDown(bottom) => bottom, - } - } - Unbounded => last_leaf_edge(root2), - }; + let (f, b) = range_search(root1, root2, range); RangeMut { - front: front, - back: back, + front: f, + back: b, _marker: PhantomData, } } @@ -1827,6 +1731,80 @@ fn last_leaf_edge } } +fn range_search>( + root1: NodeRef, + root2: NodeRef, + range: R +)-> (Handle, marker::Edge>, + Handle, marker::Edge>) + where Q: Ord, K: Borrow +{ + match (range.start(), range.end()) { + (Excluded(s), Excluded(e)) if s==e => + panic!("range start and end are equal and excluded in BTreeMap"), + (Included(s), Included(e)) | + (Included(s), Excluded(e)) | + (Excluded(s), Included(e)) | + (Excluded(s), Excluded(e)) if s>e => + panic!("range start is greater than range end in BTreeMap"), + _ => {}, + }; + + let mut min_node = root1; + let mut max_node = root2; + let mut min_found = false; + let mut max_found = false; + let mut diverged = false; + + loop { + let min_edge = match (min_found, range.start()) { + (false, Included(key)) => match search::search_linear(&min_node, key) { + (i, true) => { min_found = true; i }, + (i, false) => i, + }, + (false, Excluded(key)) => match search::search_linear(&min_node, key) { + (i, true) => { min_found = true; i+1 }, + (i, false) => i, + }, + (_, Unbounded) => 0, + (true, Included(_)) => min_node.keys().len(), + (true, Excluded(_)) => 0, + }; + + let max_edge = match (max_found, range.end()) { + (false, Included(key)) => match search::search_linear(&max_node, key) { + (i, true) => { max_found = true; i+1 }, + (i, false) => i, + }, + (false, Excluded(key)) => match search::search_linear(&max_node, key) { + (i, true) => { max_found = true; i }, + (i, false) => i, + }, + (_, Unbounded) => max_node.keys().len(), + (true, Included(_)) => 0, + (true, Excluded(_)) => max_node.keys().len(), + }; + + if !diverged { + if max_edge < min_edge { panic!("Ord is ill-defined in BTreeMap range") } + if min_edge != max_edge { diverged = true; } + } + + let front = Handle::new_edge(min_node, min_edge); + let back = Handle::new_edge(max_node, max_edge); + match (front.force(), back.force()) { + (Leaf(f), Leaf(b)) => { + return (f, b); + }, + (Internal(min_int), Internal(max_int)) => { + min_node = min_int.descend(); + max_node = max_int.descend(); + }, + _ => unreachable!("BTreeMap has different depths"), + }; + } +} + #[inline(always)] unsafe fn unwrap_unchecked(val: Option) -> T { val.unwrap_or_else(|| { diff --git a/src/libcollections/btree/search.rs b/src/libcollections/btree/search.rs index c94b570bfed8b..bc1272fbc786e 100644 --- a/src/libcollections/btree/search.rs +++ b/src/libcollections/btree/search.rs @@ -58,7 +58,7 @@ pub fn search_node( } } -fn search_linear( +pub fn search_linear( node: &NodeRef, key: &Q ) -> (usize, bool) @@ -73,4 +73,3 @@ fn search_linear( } (node.keys().len(), false) } - diff --git a/src/libcollectionstest/btree/map.rs b/src/libcollectionstest/btree/map.rs index 11be13426e49c..f33923f996319 100644 --- a/src/libcollectionstest/btree/map.rs +++ b/src/libcollectionstest/btree/map.rs @@ -178,6 +178,48 @@ fn test_range_small() { assert_eq!(j, size - 2); } +#[test] +fn test_range_equal_empty_cases() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + assert_eq!(map.range((Included(2), Excluded(2))).next(), None); + assert_eq!(map.range((Excluded(2), Included(2))).next(), None); +} + +#[test] +#[should_panic] +fn test_range_equal_excluded() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + map.range((Excluded(2), Excluded(2))); +} + +#[test] +#[should_panic] +fn test_range_backwards_1() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + map.range((Included(3), Included(2))); +} + +#[test] +#[should_panic] +fn test_range_backwards_2() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + map.range((Included(3), Excluded(2))); +} + +#[test] +#[should_panic] +fn test_range_backwards_3() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + map.range((Excluded(3), Included(2))); +} + +#[test] +#[should_panic] +fn test_range_backwards_4() { + let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect(); + map.range((Excluded(3), Excluded(2))); +} + #[test] fn test_range_1000() { let size = 1000; From b3d73995dac028f287604dbe763b236be154e787 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Fri, 10 Feb 2017 23:23:11 +0000 Subject: [PATCH 07/15] Fix ICE on certain sequence repetitions. --- src/libsyntax/parse/parser.rs | 19 ++++++++++++++----- src/test/compile-fail/issue-39709.rs | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 src/test/compile-fail/issue-39709.rs diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index b051928ff9d3c..2c4fa8e15edf2 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -302,11 +302,20 @@ impl<'a> Parser<'a> { if i + 1 < tts.len() { self.tts.push((tts, i + 1)); } - if let TokenTree::Token(sp, tok) = tt { - TokenAndSpan { tok: tok, sp: sp } - } else { - self.tts.push((tt, 0)); - continue + // FIXME(jseyfried): remove after fixing #39390 in #39419. + if self.quote_depth > 0 { + if let TokenTree::Sequence(sp, _) = tt { + self.span_err(sp, "attempted to repeat an expression containing no \ + syntax variables matched as repeating at this depth"); + } + } + match tt { + TokenTree::Token(sp, tok) => TokenAndSpan { tok: tok, sp: sp }, + _ if tt.len() > 0 => { + self.tts.push((tt, 0)); + continue + } + _ => continue, } } else { TokenAndSpan { tok: token::Eof, sp: self.span } diff --git a/src/test/compile-fail/issue-39709.rs b/src/test/compile-fail/issue-39709.rs new file mode 100644 index 0000000000000..0f66fe8439336 --- /dev/null +++ b/src/test/compile-fail/issue-39709.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + println!("{}", { macro_rules! x { ($()*) => {} } 33 }); + //~^ ERROR no syntax variables matched as repeating at this depth +} + From 0340ddeb3bcfcd0cfe6a0c4745293ecf2b733dac Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 11 Feb 2017 17:28:29 -0800 Subject: [PATCH 08/15] travis: Add builders without assertions This commit adds three new builders, one OSX, one Linux, and one MSVC, which will produce "nightlies" with LLVM assertions disabled. Currently all nightly releases have LLVM assertions enabled to catch bugs before they reach the beta/stable channels. The beta/stable channels, however, do not have LLVM assertions enabled. Unfortunately though projects like Servo are stuck on nightlies for the near future at least and are also suffering very long compile times. The purpose of this commit is to provide artifacts to these projects which are not distributed through normal channels (e.g. rustup) but are provided for developers to use locally if need be. Logistically these builds will all be uploaded to `rustc-builds-alt` instead of the `rustc-builds` folder of the `rust-lang-ci` bucket. These builds will stay there forever (until cleaned out if necessary) and there are no plans to integrate this with rustup and/or the official release process. --- .travis.yml | 30 ++++++++++++++++++++++++++++++ appveyor.yml | 22 ++++++++++++++++++++++ src/ci/docker/run.sh | 1 + src/ci/run.sh | 4 +++- 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a776f1b1e1041..537849964ab41 100644 --- a/.travis.yml +++ b/.travis.yml @@ -65,6 +65,20 @@ matrix: osx_image: xcode8.2 install: *osx_install_sccache + # "alternate" deployments, these are "nightlies" but don't have assertions + # turned on, they're deployed to a different location primarily for projects + # which are stuck on nightly and don't want llvm assertions in the artifacts + # that they use. + - env: IMAGE=dist-x86-linux DEPLOY_ALT=1 + - env: > + RUST_CHECK_TARGET=dist + RUST_CONFIGURE_ARGS="--enable-extended" + SRC=. + DEPLOY_ALT=1 + os: osx + osx_image: xcode8.2 + install: *osx_install_sccache + env: global: - SCCACHE_BUCKET=rust-lang-ci-sccache @@ -125,3 +139,19 @@ deploy: on: branch: auto condition: $DEPLOY = 1 + + # this is the same as the above deployment provider except that it uploads to + # a slightly different directory and has a different trigger + - provider: s3 + bucket: rust-lang-ci + skip_cleanup: true + local_dir: deploy + upload_dir: rustc-builds-alt + acl: public_read + region: us-east-1 + access_key_id: AKIAIPQVNYF2T3DTYIWQ + secret_access_key: + secure: "FBqDqOTeIPMu6v/WYPf4CFSlh9rLRZGKVtpLa5KkyuOhXRTrnEzBduEtS8/FMIxdQImvurhSvxWvqRybMOi4qoVfjMqqpHAI7uBbidbrvAcJoHNsx6BgUNVCIoH6a0UsAjTUtm6/YPIpzbHoLZXPL0GrHPMk6Mu04qVSmcYNWn4=" + on: + branch: auto + condition: $DEPLOY_ALT = 1 diff --git a/appveyor.yml b/appveyor.yml index 2183d8da95f89..38781d281c8ec 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -61,6 +61,12 @@ environment: MINGW_DIR: mingw64 DEPLOY: 1 + # "alternate" deployment, see .travis.yml for more info + - MSYS_BITS: 64 + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended + SCRIPT: python x.py dist + DEPLOY_ALT: 1 + matrix: fast_finish: true @@ -146,6 +152,22 @@ deploy: branch: auto DEPLOY: 1 + # This provider is the same as the one above except that it has a slightly + # different upload directory and a slightly different trigger + - provider: S3 + skip_cleanup: true + access_key_id: AKIAIPQVNYF2T3DTYIWQ + secret_access_key: + secure: +11jsUNFTQ9dq5Ad1i2+PeUJaXluFJ0zIJAXESE1dFT3Kdjku4/eDdgyjgsB6GnV + bucket: rust-lang-ci + set_public: true + region: us-east-1 + artifact: /.*/ + folder: rustc-builds-alt + on: + branch: auto + DEPLOY_ALT: 1 + # init: # - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) # on_finish: diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index 0ddab8c4160f4..892c5baa5c64b 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -49,6 +49,7 @@ exec docker \ $args \ --env CARGO_HOME=/cargo \ --env DEPLOY=$DEPLOY \ + --env DEPLOY_ALT=$DEPLOY_ALT \ --env LOCAL_USER_ID=`id -u` \ --volume "$HOME/.cargo:/cargo" \ --rm \ diff --git a/src/ci/run.sh b/src/ci/run.sh index 960acc4de7d87..41230aedbfa01 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -30,12 +30,14 @@ RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-manage-submodules" # # FIXME: need a scheme for changing this `nightly` value to `beta` and `stable` # either automatically or manually. -if [ "$DEPLOY" != "" ]; then +if [ "$DEPLOY$DEPLOY_ALT" != "" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --release-channel=nightly" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-static-stdcpp" if [ "$NO_LLVM_ASSERTIONS" = "1" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions" + elif [ "$DEPLOY_ALT" != "" ]; then + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions" fi else RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-debug-assertions" From 729853539e94066ce9953f11db47ef7bae247c93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Cassiers?= Date: Sun, 12 Feb 2017 23:35:50 +0100 Subject: [PATCH 09/15] Fix wrong link names on windows --- src/libstd/sys/windows/f32.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstd/sys/windows/f32.rs b/src/libstd/sys/windows/f32.rs index b57aaa4ecd25e..4721858ab7bf5 100644 --- a/src/libstd/sys/windows/f32.rs +++ b/src/libstd/sys/windows/f32.rs @@ -16,10 +16,10 @@ pub mod cmath { use libc::{c_float, c_int}; extern { - #[cfg_attr(target_env = "msvc", link_name = "__lgamma_r")] + #[cfg_attr(target_env = "msvc", link_name = "__lgammaf_r")] pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float; - #[cfg_attr(target_env = "msvc", link_name = "_hypot")] + #[cfg_attr(target_env = "msvc", link_name = "_hypotf")] pub fn hypotf(x: c_float, y: c_float) -> c_float; } From 30abe7bd583d172414d82357acf62ceabf896730 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 13 Feb 2017 06:44:06 -0800 Subject: [PATCH 10/15] test: Remove sanitizer-thread test Unfortunately it appears to spuriously fail so we can't gate on it --- src/test/run-make/sanitizer-thread/Makefile | 10 ---------- src/test/run-make/sanitizer-thread/racy.rs | 21 --------------------- 2 files changed, 31 deletions(-) delete mode 100644 src/test/run-make/sanitizer-thread/Makefile delete mode 100644 src/test/run-make/sanitizer-thread/racy.rs diff --git a/src/test/run-make/sanitizer-thread/Makefile b/src/test/run-make/sanitizer-thread/Makefile deleted file mode 100644 index 8bb89a241cb05..0000000000000 --- a/src/test/run-make/sanitizer-thread/Makefile +++ /dev/null @@ -1,10 +0,0 @@ --include ../tools.mk - -ifdef SANITIZER_SUPPORT -all: - $(RUSTC) -g -Z sanitizer=thread -Z print-link-args racy.rs | grep -q librustc_tsan - $(TMPDIR)/racy 2>&1 | grep -q 'data race' -else -all: - -endif diff --git a/src/test/run-make/sanitizer-thread/racy.rs b/src/test/run-make/sanitizer-thread/racy.rs deleted file mode 100644 index dc929e004a479..0000000000000 --- a/src/test/run-make/sanitizer-thread/racy.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::thread; - -static mut ANSWER: i32 = 0; - -fn main() { - let t1 = thread::spawn(|| unsafe { ANSWER = 42 }); - unsafe { - ANSWER = 24; - } - t1.join().ok(); -} From c2566f638aa065f14f296aaad0666492ae00a636 Mon Sep 17 00:00:00 2001 From: Zack Weinberg Date: Mon, 13 Feb 2017 11:46:29 -0500 Subject: [PATCH 11/15] Squeeze URL lines under 100 chars wide to make tidy happy. --- src/libstd/ascii.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs index cf965291fa2a6..2a22e5e7a1136 100644 --- a/src/libstd/ascii.rs +++ b/src/libstd/ascii.rs @@ -454,19 +454,19 @@ pub trait AsciiExt { /// /// Rust uses the WhatWG Infra Standard's [definition of ASCII /// whitespace][infra-aw]. There are several other definitions in - /// wide use. For instance, [the POSIX locale][posix-ctype] - /// includes U+000B VERTICAL TAB as well as all the above - /// characters, but—from the very same specification—[the default - /// rule for "field splitting" in the Bourne shell][field-splitting] - /// considers *only* SPACE, HORIZONTAL TAB, and LINE FEED as whitespace. + /// wide use. For instance, [the POSIX locale][pct] includes + /// U+000B VERTICAL TAB as well as all the above characters, + /// but—from the very same specification—[the default rule for + /// "field splitting" in the Bourne shell][bfs] considers *only* + /// SPACE, HORIZONTAL TAB, and LINE FEED as whitespace. /// /// If you are writing a program that will process an existing /// file format, check what that format's definition of whitespace is /// before using this function. /// /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace - /// [posix-ctype]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 - /// [field-splitting]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 + /// [pct]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 + /// [bfs]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 /// /// # Examples /// From cc8d4558956e48ff65d5e0d1af13f3f6e8466b84 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 13 Feb 2017 18:11:20 +0100 Subject: [PATCH 12/15] Add filename when running rustdoc --test on a markdown file --- src/librustdoc/markdown.rs | 3 ++- src/librustdoc/test.rs | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 70ef7c597e4d7..c67e2fdc2b027 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -155,7 +155,8 @@ pub fn test(input: &str, cfgs: Vec, libs: SearchPaths, externs: Externs, let mut opts = TestOptions::default(); opts.no_crate_inject = true; let mut collector = Collector::new(input.to_string(), cfgs, libs, externs, - true, opts, maybe_sysroot, None); + true, opts, maybe_sysroot, None, + Some(input.to_owned())); find_testable_code(&input_str, &mut collector, DUMMY_SP); test_args.insert(0, "rustdoctest".to_string()); testing::test_main(&test_args, collector.tests); diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 349bddc87405c..1c37067d7f69d 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -104,7 +104,8 @@ pub fn run(input: &str, false, opts, maybe_sysroot, - Some(codemap)); + Some(codemap), + None); { let dep_graph = DepGraph::new(false); @@ -391,12 +392,13 @@ pub struct Collector { maybe_sysroot: Option, position: Span, codemap: Option>, + filename: Option, } impl Collector { pub fn new(cratename: String, cfgs: Vec, libs: SearchPaths, externs: Externs, use_headers: bool, opts: TestOptions, maybe_sysroot: Option, - codemap: Option>) -> Collector { + codemap: Option>, filename: Option) -> Collector { Collector { tests: Vec::new(), names: Vec::new(), @@ -411,6 +413,7 @@ impl Collector { maybe_sysroot: maybe_sysroot, position: DUMMY_SP, codemap: codemap, + filename: filename, } } @@ -483,6 +486,8 @@ impl Collector { pub fn get_filename(&self) -> String { if let Some(ref codemap) = self.codemap { codemap.span_to_filename(self.position) + } else if let Some(ref filename) = self.filename { + filename.clone() } else { "".to_owned() } From 5817351048b7c817720f696dd6a0f7005bd1a7a4 Mon Sep 17 00:00:00 2001 From: Zack Weinberg Date: Mon, 13 Feb 2017 12:33:35 -0500 Subject: [PATCH 13/15] tidy: exempt URLs from the line length restriction The length of a URL is usually not under our control, and Markdown provides no way to split a URL in the middle. Therefore, comment lines consisting _solely_ of a URL (possibly with a Markdown link label in front) should be exempt from the line-length restriction. Inline hyperlink destinations ( `[foo](http://...)` notation ) are _not_ exempt, because it is my arrogant opinion that long lines of that type make the source text illegible. The patch adds dependencies on the `regex` and `lazy_static` crates to the tidy utility. This _appears_ to Just Work, but if you would rather not have that dependency I am willing to provide a hand-written parser instead. --- src/tools/tidy/Cargo.toml | 2 ++ src/tools/tidy/src/main.rs | 3 +++ src/tools/tidy/src/style.rs | 33 +++++++++++++++++++++++++++++++-- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml index e900bd47fb7bd..39986d592899b 100644 --- a/src/tools/tidy/Cargo.toml +++ b/src/tools/tidy/Cargo.toml @@ -4,3 +4,5 @@ version = "0.1.0" authors = ["Alex Crichton "] [dependencies] +regex = "*" +lazy_static = "*" diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 9962c6ec9af12..bbd6c8e87c215 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -14,6 +14,9 @@ //! etc. This is run by default on `make check` and as part of the auto //! builders. +extern crate regex; +#[macro_use] extern crate lazy_static; + use std::fs; use std::path::{PathBuf, Path}; use std::env; diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index c722eb690b8c3..91c5edfd75abc 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -26,6 +26,8 @@ use std::fs::File; use std::io::prelude::*; use std::path::Path; +use regex::Regex; + const COLS: usize = 100; const LICENSE: &'static str = "\ Copyright The Rust Project Developers. See the COPYRIGHT @@ -38,6 +40,32 @@ http://www.apache.org/licenses/LICENSE-2.0> or the MIT license option. This file may not be copied, modified, or distributed except according to those terms."; +/// True if LINE is allowed to be longer than the normal limit. +/// +/// Currently there is only one exception: if the line is within a +/// comment, and its entire text is one URL (possibly with a Markdown +/// link label in front), then it's allowed to be overlength. This is +/// because Markdown offers no way to split a line in the middle of a +/// URL, and the length of URLs for external references is beyond our +/// control. +fn long_line_is_ok(line: &str) -> bool { + lazy_static! { + static ref URL_RE: Regex = Regex::new( + // This regexp uses the CommonMark definition of link + // label. It thinks any sequence of nonwhitespace + // characters beginning with "http://" or "https://" is a + // URL. Add more schemas as necessary. + r"^\s*//[!/]?\s+(?:\[(?:[^\]\\]|\\.){1,999}\]:\s+)?https?://\S+$" + ).unwrap(); + } + + if URL_RE.is_match(line) { + return true; + } + + false +} + pub fn check(path: &Path, bad: &mut bool) { let mut contents = String::new(); super::walk(path, &mut super::filter_dirs, &mut |file| { @@ -61,8 +89,9 @@ pub fn check(path: &Path, bad: &mut bool) { println!("{}:{}: {}", file.display(), i + 1, msg); *bad = true; }; - if line.chars().count() > COLS && !skip_length { - err(&format!("line longer than {} chars", COLS)); + if !skip_length && line.chars().count() > COLS + && !long_line_is_ok(line) { + err(&format!("line longer than {} chars", COLS)); } if line.contains("\t") && !skip_tab { err("tab character"); From ff4758c2a0dffef264fe73b90668bd04b1b2fa89 Mon Sep 17 00:00:00 2001 From: Zack Weinberg Date: Mon, 13 Feb 2017 15:44:51 -0500 Subject: [PATCH 14/15] Replace regex-based parser for URL lines with open-coded one. --- src/tools/tidy/Cargo.toml | 2 -- src/tools/tidy/src/main.rs | 3 -- src/tools/tidy/src/style.rs | 66 ++++++++++++++++++++++++++----------- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml index 39986d592899b..e900bd47fb7bd 100644 --- a/src/tools/tidy/Cargo.toml +++ b/src/tools/tidy/Cargo.toml @@ -4,5 +4,3 @@ version = "0.1.0" authors = ["Alex Crichton "] [dependencies] -regex = "*" -lazy_static = "*" diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index bbd6c8e87c215..9962c6ec9af12 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -14,9 +14,6 @@ //! etc. This is run by default on `make check` and as part of the auto //! builders. -extern crate regex; -#[macro_use] extern crate lazy_static; - use std::fs; use std::path::{PathBuf, Path}; use std::env; diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index 91c5edfd75abc..2233f8c352974 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -26,8 +26,6 @@ use std::fs::File; use std::io::prelude::*; use std::path::Path; -use regex::Regex; - const COLS: usize = 100; const LICENSE: &'static str = "\ Copyright The Rust Project Developers. See the COPYRIGHT @@ -40,26 +38,54 @@ http://www.apache.org/licenses/LICENSE-2.0> or the MIT license option. This file may not be copied, modified, or distributed except according to those terms."; -/// True if LINE is allowed to be longer than the normal limit. -/// -/// Currently there is only one exception: if the line is within a -/// comment, and its entire text is one URL (possibly with a Markdown -/// link label in front), then it's allowed to be overlength. This is -/// because Markdown offers no way to split a line in the middle of a -/// URL, and the length of URLs for external references is beyond our -/// control. -fn long_line_is_ok(line: &str) -> bool { - lazy_static! { - static ref URL_RE: Regex = Regex::new( - // This regexp uses the CommonMark definition of link - // label. It thinks any sequence of nonwhitespace - // characters beginning with "http://" or "https://" is a - // URL. Add more schemas as necessary. - r"^\s*//[!/]?\s+(?:\[(?:[^\]\\]|\\.){1,999}\]:\s+)?https?://\S+$" - ).unwrap(); +/// Parser states for line_is_url. +#[derive(PartialEq)] +#[allow(non_camel_case_types)] +enum LIUState { EXP_COMMENT_START, + EXP_LINK_LABEL_OR_URL, + EXP_URL, + EXP_END } + +/// True if LINE appears to be a line comment containing an URL, +/// possibly with a Markdown link label in front, and nothing else. +/// The Markdown link label, if present, may not contain whitespace. +/// Lines of this form are allowed to be overlength, because Markdown +/// offers no way to split a line in the middle of a URL, and the lengths +/// of URLs to external references are beyond our control. +fn line_is_url(line: &str) -> bool { + use self::LIUState::*; + let mut state: LIUState = EXP_COMMENT_START; + + for tok in line.split_whitespace() { + match (state, tok) { + (EXP_COMMENT_START, "//") => state = EXP_LINK_LABEL_OR_URL, + (EXP_COMMENT_START, "///") => state = EXP_LINK_LABEL_OR_URL, + (EXP_COMMENT_START, "//!") => state = EXP_LINK_LABEL_OR_URL, + + (EXP_LINK_LABEL_OR_URL, w) + if w.len() >= 4 && w.starts_with("[") && w.ends_with("]:") + => state = EXP_URL, + + (EXP_LINK_LABEL_OR_URL, w) + if w.starts_with("http://") || w.starts_with("https://") + => state = EXP_END, + + (EXP_URL, w) + if w.starts_with("http://") || w.starts_with("https://") + => state = EXP_END, + + (_, _) => return false, + } } - if URL_RE.is_match(line) { + state == EXP_END +} + +/// True if LINE is allowed to be longer than the normal limit. +/// Currently there is only one exception, for long URLs, but more +/// may be added in the future. +fn long_line_is_ok(line: &str) -> bool { + if line_is_url(line) { return true; } From 162240c744fa415602dcd56f08895b9583037717 Mon Sep 17 00:00:00 2001 From: Zack Weinberg Date: Mon, 13 Feb 2017 18:44:43 -0500 Subject: [PATCH 15/15] Add feature annotations to the doctests for ascii_ctype. --- src/libstd/ascii.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs index 2a22e5e7a1136..af21d6d906eb5 100644 --- a/src/libstd/ascii.rs +++ b/src/libstd/ascii.rs @@ -193,6 +193,8 @@ pub trait AsciiExt { /// # Examples /// /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] /// use std::ascii::AsciiExt; /// let A = 'A'; /// let G = 'G'; @@ -225,6 +227,8 @@ pub trait AsciiExt { /// # Examples /// /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] /// use std::ascii::AsciiExt; /// let A = 'A'; /// let G = 'G'; @@ -257,6 +261,8 @@ pub trait AsciiExt { /// # Examples /// /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] /// use std::ascii::AsciiExt; /// let A = 'A'; /// let G = 'G'; @@ -290,6 +296,8 @@ pub trait AsciiExt { /// # Examples /// /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] /// use std::ascii::AsciiExt; /// let A = 'A'; /// let G = 'G'; @@ -322,6 +330,8 @@ pub trait AsciiExt { /// # Examples /// /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] /// use std::ascii::AsciiExt; /// let A = 'A'; /// let G = 'G'; @@ -355,6 +365,8 @@ pub trait AsciiExt { /// # Examples /// /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] /// use std::ascii::AsciiExt; /// let A = 'A'; /// let G = 'G'; @@ -390,6 +402,8 @@ pub trait AsciiExt { /// # Examples /// /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] /// use std::ascii::AsciiExt; /// let A = 'A'; /// let G = 'G'; @@ -422,6 +436,8 @@ pub trait AsciiExt { /// # Examples /// /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] /// use std::ascii::AsciiExt; /// let A = 'A'; /// let G = 'G'; @@ -471,6 +487,8 @@ pub trait AsciiExt { /// # Examples /// /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] /// use std::ascii::AsciiExt; /// let A = 'A'; /// let G = 'G'; @@ -503,6 +521,8 @@ pub trait AsciiExt { /// # Examples /// /// ``` + /// #![feature(ascii_ctype)] + /// # #![allow(non_snake_case)] /// use std::ascii::AsciiExt; /// let A = 'A'; /// let G = 'G';