From 20602d1244b27c8d2e50dfb2a6d57905e020e43a Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Tue, 15 Oct 2019 10:40:01 +0200 Subject: [PATCH 1/2] Unconditionally use MaybeUninit This raises the minimum Rust version to 1.36.0, so this is a breaking change. --- .travis.yml | 2 +- Cargo.toml | 3 +-- build.rs | 3 --- src/macros.rs | 45 ++++++++++++--------------------------------- 4 files changed, 14 insertions(+), 39 deletions(-) diff --git a/.travis.yml b/.travis.yml index c4048bfa..8da4a509 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ rust: - nightly - beta - stable - - 1.31.0 + - 1.36.0 script: - cargo build --verbose diff --git a/Cargo.toml b/Cargo.toml index c10853b1..9f4c20bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cssparser" -version = "0.25.9" +version = "0.26.0" authors = [ "Simon Sapin " ] description = "Rust implementation of CSS Syntax Level 3" @@ -30,7 +30,6 @@ serde = {version = "1.0", optional = true} smallvec = "0.6" [build-dependencies] -autocfg = "0.1.4" syn = { version = "1", features = ["extra-traits", "fold", "full"] } quote = "1" proc-macro2 = "1" diff --git a/build.rs b/build.rs index 2cef2756..7a73ab18 100644 --- a/build.rs +++ b/build.rs @@ -2,7 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -extern crate autocfg; #[macro_use] extern crate quote; #[macro_use] @@ -50,7 +49,5 @@ fn main() { println!("cargo:rustc-cfg=rustc_has_pr45225") } - autocfg::new().emit_has_path("std::mem::MaybeUninit"); - codegen::main(); } diff --git a/src/macros.rs b/src/macros.rs index f40e95e1..3df74d30 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -2,6 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use std::mem::MaybeUninit; + // See docs of the `procedural-masquerade` crate. define_invoke_proc_macro!(cssparser_internal__invoke_proc_macro); @@ -110,42 +112,15 @@ macro_rules! ascii_case_insensitive_phf_map { #[doc(hidden)] macro_rules! cssparser_internal__to_lowercase { ($input: expr, $BUFFER_SIZE: expr => $output: ident) => { - let mut buffer; - // Safety: `buffer` is only used in `_internal__to_lowercase`, - // which initializes with `copy_from_slice` the part of the buffer it uses, - // before it uses it. #[allow(unsafe_code)] - let buffer = unsafe { cssparser_internal__uninit!(buffer, $BUFFER_SIZE) }; + let mut buffer = unsafe { + ::std::mem::MaybeUninit::<[::std::mem::MaybeUninit; $BUFFER_SIZE]>::uninit().assume_init() + }; let input: &str = $input; - let $output = $crate::_internal__to_lowercase(buffer, input); + let $output = $crate::_internal__to_lowercase(&mut buffer, input); }; } -#[cfg(has_std__mem__MaybeUninit)] -#[macro_export] -#[doc(hidden)] -macro_rules! cssparser_internal__uninit { - ($buffer: ident, $BUFFER_SIZE: expr) => { - { - $buffer = ::std::mem::MaybeUninit::<[u8; $BUFFER_SIZE]>::uninit(); - &mut *($buffer.as_mut_ptr()) - } - } -} - -// FIXME: remove this when we require Rust 1.36 -#[cfg(not(has_std__mem__MaybeUninit))] -#[macro_export] -#[doc(hidden)] -macro_rules! cssparser_internal__uninit { - ($buffer: ident, $BUFFER_SIZE: expr) => { - { - $buffer = ::std::mem::uninitialized::<[u8; $BUFFER_SIZE]>(); - &mut $buffer - } - } -} - /// Implementation detail of match_ignore_ascii_case! and ascii_case_insensitive_phf_map! macros. /// /// **This function is not part of the public API. It can change or be removed between any verisons.** @@ -154,10 +129,14 @@ macro_rules! cssparser_internal__uninit { /// Otherwise, return `input` ASCII-lowercased, using `buffer` as temporary space if necessary. #[doc(hidden)] #[allow(non_snake_case)] -pub fn _internal__to_lowercase<'a>(buffer: &'a mut [u8], input: &'a str) -> Option<&'a str> { +pub fn _internal__to_lowercase<'a>(buffer: &'a mut [MaybeUninit], input: &'a str) -> Option<&'a str> { if let Some(buffer) = buffer.get_mut(..input.len()) { if let Some(first_uppercase) = input.bytes().position(|byte| matches!(byte, b'A'..=b'Z')) { - buffer.copy_from_slice(input.as_bytes()); + let buffer = unsafe { + let ptr = buffer.as_mut_ptr() as *mut u8; + std::ptr::copy_nonoverlapping(input.as_bytes().as_ptr(), ptr, input.len()); + &mut *(buffer as *mut [MaybeUninit] as *mut [u8]) + }; buffer[first_uppercase..].make_ascii_lowercase(); // `buffer` was initialized to a copy of `input` (which is &str so well-formed UTF-8) // then lowercased (which preserves UTF-8 well-formedness) From 4721b6975759fee40971c1510c264b149eab690f Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 21 Oct 2019 13:01:48 +0200 Subject: [PATCH 2/2] Use [T]::copy_from_slice instead of ptr::copy_nonoverlapping --- src/macros.rs | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 3df74d30..8741a68c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -114,7 +114,8 @@ macro_rules! cssparser_internal__to_lowercase { ($input: expr, $BUFFER_SIZE: expr => $output: ident) => { #[allow(unsafe_code)] let mut buffer = unsafe { - ::std::mem::MaybeUninit::<[::std::mem::MaybeUninit; $BUFFER_SIZE]>::uninit().assume_init() + ::std::mem::MaybeUninit::<[::std::mem::MaybeUninit; $BUFFER_SIZE]>::uninit() + .assume_init() }; let input: &str = $input; let $output = $crate::_internal__to_lowercase(&mut buffer, input); @@ -129,18 +130,28 @@ macro_rules! cssparser_internal__to_lowercase { /// Otherwise, return `input` ASCII-lowercased, using `buffer` as temporary space if necessary. #[doc(hidden)] #[allow(non_snake_case)] -pub fn _internal__to_lowercase<'a>(buffer: &'a mut [MaybeUninit], input: &'a str) -> Option<&'a str> { +pub fn _internal__to_lowercase<'a>( + buffer: &'a mut [MaybeUninit], + input: &'a str, +) -> Option<&'a str> { if let Some(buffer) = buffer.get_mut(..input.len()) { if let Some(first_uppercase) = input.bytes().position(|byte| matches!(byte, b'A'..=b'Z')) { - let buffer = unsafe { - let ptr = buffer.as_mut_ptr() as *mut u8; - std::ptr::copy_nonoverlapping(input.as_bytes().as_ptr(), ptr, input.len()); - &mut *(buffer as *mut [MaybeUninit] as *mut [u8]) - }; - buffer[first_uppercase..].make_ascii_lowercase(); - // `buffer` was initialized to a copy of `input` (which is &str so well-formed UTF-8) - // then lowercased (which preserves UTF-8 well-formedness) - unsafe { Some(::std::str::from_utf8_unchecked(buffer)) } + unsafe { + // This cast doesn’t change the pointer’s validity + // since `u8` has the same layout as `MaybeUninit`: + let input_bytes = &*(input.as_bytes() as *const [u8] as *const [MaybeUninit]); + + buffer.copy_from_slice(&*input_bytes); + + // Same as above re layout, plus these bytes have been initialized: + let buffer = &mut *(buffer as *mut [MaybeUninit] as *mut [u8]); + + buffer[first_uppercase..].make_ascii_lowercase(); + // `buffer` was initialized to a copy of `input` + // (which is `&str` so well-formed UTF-8) + // then ASCII-lowercased (which preserves UTF-8 well-formedness): + Some(::std::str::from_utf8_unchecked(buffer)) + } } else { // Input is already lower-case Some(input)