From 48ac8e2767d1fd977c0a200b53d75fd088a01ade Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Thu, 26 May 2022 12:20:09 -0700 Subject: Update TEST_MAPPING Test: None Bug: 233924440 Change-Id: I2dd06d2bcca9490f4a058a5b7412d079eacf1ce7 --- TEST_MAPPING | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TEST_MAPPING b/TEST_MAPPING index 061ff86..6495c8a 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,5 +1,10 @@ // Generated by update_crate_tests.py for tests that depend on this crate. { + "postsubmit": [ + { + "name": "virtualizationservice_device_test" + } + ], "presubmit": [ { "name": "virtualizationservice_device_test" -- cgit v1.2.3 From 237e16b5b7db537c6c86481ccc755c7e91980333 Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Tue, 14 Jun 2022 17:04:28 -0700 Subject: Update TEST_MAPPING Test: None Bug: 236006683 Change-Id: Ic718affaa100c3fc9ebc4b2073b9332ec20ff973 --- TEST_MAPPING | 5 ----- 1 file changed, 5 deletions(-) diff --git a/TEST_MAPPING b/TEST_MAPPING index 6495c8a..061ff86 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,10 +1,5 @@ // Generated by update_crate_tests.py for tests that depend on this crate. { - "postsubmit": [ - { - "name": "virtualizationservice_device_test" - } - ], "presubmit": [ { "name": "virtualizationservice_device_test" -- cgit v1.2.3 From 4b5bfa8596fa3d17eb8e17834da46c2f1998bafa Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep Date: Mon, 19 Dec 2022 09:34:04 +0100 Subject: Upgrade semver to 1.0.16 This project was upgraded with external_updater. Usage: tools/external_updater/updater.sh update rust/crates/semver For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md Test: TreeHugger Change-Id: I046a5f21e9979517cdea35442c7854717ca5cc72 --- .cargo_vcs_info.json | 2 +- .github/FUNDING.yml | 1 + .github/workflows/ci.yml | 46 +++++++++++--- Android.bp | 4 +- Cargo.toml | 10 ++- Cargo.toml.orig | 14 +++-- METADATA | 12 ++-- README.md | 4 +- build.rs | 10 +-- src/backport.rs | 42 +------------ src/error.rs | 34 ++++++++-- src/identifier.rs | 154 +++++++++++++++++++++++++++++++++------------- src/lib.rs | 4 +- src/parse.rs | 10 +-- src/serde.rs | 37 ++++++++++- tests/test_autotrait.rs | 12 ++++ tests/test_version_req.rs | 2 +- 17 files changed, 267 insertions(+), 131 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100644 tests/test_autotrait.rs diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index a7e226d..54870e9 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "8d8bdb2adaf4572b3c4e8502e7498838b2cf7ccb" + "sha1": "dda4b834a9c896d7e1c8d17d5e326386bb346b50" }, "path_in_vcs": "" } \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..7507077 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: dtolnay diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd7b48a..0bfe3a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,9 @@ on: pull_request: schedule: [cron: "40 1 * * *"] +permissions: + contents: read + env: RUSTFLAGS: -Dwarnings @@ -15,9 +18,10 @@ jobs: strategy: fail-fast: false matrix: - rust: [nightly, beta, stable, 1.52.0, 1.46.0, 1.45.0, 1.40.0, 1.39.0, 1.36.0, 1.33.0, 1.32.0, 1.31.0] + rust: [nightly, beta, stable, 1.52.0, 1.46.0, 1.40.0, 1.39.0, 1.36.0, 1.33.0, 1.32.0, 1.31.0] + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} @@ -29,8 +33,9 @@ jobs: node: name: Node runs-on: ubuntu-latest + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - run: npm install semver - run: cargo test @@ -41,26 +46,47 @@ jobs: name: Clippy runs-on: ubuntu-latest if: github.event_name != 'pull_request' + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@clippy - run: cargo clippy --tests --benches -- -Dclippy::all -Dclippy::pedantic miri: name: Miri runs-on: ubuntu-latest + env: + MIRIFLAGS: -Zmiri-strict-provenance + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@miri + - name: Run cargo miri test (64-bit little endian) + run: cargo miri test --target x86_64-unknown-linux-gnu + - name: Run cargo miri test (64-bit big endian) + run: cargo miri test --target powerpc64-unknown-linux-gnu + - name: Run cargo miri test (32-bit little endian) + run: cargo miri test --target i686-unknown-linux-gnu + - name: Run cargo miri test (32-bit big endian) + run: cargo miri test --target mips-unknown-linux-gnu + + fuzz: + name: Fuzz + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly - with: - components: miri - - run: cargo miri test + - uses: dtolnay/install@cargo-fuzz + - run: cargo fuzz build -O outdated: name: Outdated runs-on: ubuntu-latest if: github.event_name != 'pull_request' + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/install@cargo-outdated - - run: cargo outdated --exit-code 1 + - run: cargo outdated --workspace --exit-code 1 + - run: cargo outdated --manifest-path fuzz/Cargo.toml --exit-code 1 diff --git a/Android.bp b/Android.bp index 402c65c..8a8a1eb 100644 --- a/Android.bp +++ b/Android.bp @@ -1,8 +1,6 @@ // This file is generated by cargo2android.py --config cargo2android.json. // Do not modify this file as changes will be overridden on upgrade. - - package { default_applicable_licenses: ["external_rust_crates_semver_license"], } @@ -43,7 +41,7 @@ rust_library { name: "libsemver", crate_name: "semver", cargo_env_compat: true, - cargo_pkg_version: "1.0.6", + cargo_pkg_version: "1.0.16", srcs: ["src/lib.rs"], edition: "2018", features: [ diff --git a/Cargo.toml b/Cargo.toml index e250936..e0bfea2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,11 +13,16 @@ edition = "2018" rust-version = "1.31" name = "semver" -version = "1.0.6" +version = "1.0.16" authors = ["David Tolnay "] description = "Parser and evaluator for Cargo's flavor of Semantic Versioning" documentation = "https://docs.rs/semver" readme = "README.md" +keywords = ["cargo"] +categories = [ + "data-structures", + "no-std", +] license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/semver" @@ -28,6 +33,9 @@ rustdoc-args = [ "doc_cfg", ] +[lib] +doc-scrape-examples = false + [dependencies.serde] version = "1.0" optional = true diff --git a/Cargo.toml.orig b/Cargo.toml.orig index ef65cdc..307c361 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,14 +1,15 @@ [package] name = "semver" -version = "1.0.6" +version = "1.0.16" authors = ["David Tolnay "] +categories = ["data-structures", "no-std"] +description = "Parser and evaluator for Cargo's flavor of Semantic Versioning" +documentation = "https://docs.rs/semver" edition = "2018" -rust-version = "1.31" +keywords = ["cargo"] license = "MIT OR Apache-2.0" -description = "Parser and evaluator for Cargo's flavor of Semantic Versioning" repository = "https://github.com/dtolnay/semver" -documentation = "https://docs.rs/semver" -readme = "README.md" +rust-version = "1.31" [features] default = ["std"] @@ -17,6 +18,9 @@ std = [] [dependencies] serde = { version = "1.0", optional = true, default-features = false } +[lib] +doc-scrape-examples = false + [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = ["--cfg", "doc_cfg"] diff --git a/METADATA b/METADATA index 98ff1b7..886c058 100644 --- a/METADATA +++ b/METADATA @@ -1,3 +1,7 @@ +# This project was upgraded with external_updater. +# Usage: tools/external_updater/updater.sh update rust/crates/semver +# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md + name: "semver" description: "Parser and evaluator for Cargo\'s flavor of Semantic Versioning" third_party { @@ -7,13 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/semver/semver-1.0.6.crate" + value: "https://static.crates.io/crates/semver/semver-1.0.16.crate" } - version: "1.0.6" + version: "1.0.16" license_type: NOTICE last_upgrade_date { year: 2022 - month: 3 - day: 1 + month: 12 + day: 19 } } diff --git a/README.md b/README.md index 9de11d7..a9a1cb8 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ semver [github](https://github.com/dtolnay/semver) [crates.io](https://crates.io/crates/semver) -[docs.rs](https://docs.rs/semver) -[build status](https://github.com/dtolnay/semver/actions?query=branch%3Amaster) +[docs.rs](https://docs.rs/semver) +[build status](https://github.com/dtolnay/semver/actions?query=branch%3Amaster) A parser and evaluator for Cargo's flavor of Semantic Versioning. diff --git a/build.rs b/build.rs index b39468d..81ad970 100644 --- a/build.rs +++ b/build.rs @@ -3,17 +3,13 @@ use std::process::Command; use std::str; fn main() { + println!("cargo:rerun-if-changed=build.rs"); + let compiler = match rustc_minor_version() { Some(compiler) => compiler, None => return, }; - if compiler < 32 { - // u64::from_ne_bytes. - // https://doc.rust-lang.org/std/primitive.u64.html#method.from_ne_bytes - println!("cargo:rustc-cfg=no_from_ne_bytes"); - } - if compiler < 33 { // Exhaustive integer patterns. On older compilers, a final `_` arm is // required even if every possible integer value is otherwise covered. @@ -41,7 +37,7 @@ fn main() { if compiler < 45 { // String::strip_prefix. - // https://doc.rust-lang.org/std/primitive.str.html#method.repeat + // https://doc.rust-lang.org/std/primitive.str.html#method.strip_prefix println!("cargo:rustc-cfg=no_str_strip_prefix"); } diff --git a/src/backport.rs b/src/backport.rs index f3a69c9..b5e1d02 100644 --- a/src/backport.rs +++ b/src/backport.rs @@ -14,50 +14,10 @@ impl StripPrefixExt for str { } } -#[cfg(no_from_ne_bytes)] // rustc <1.32 -pub(crate) trait FromNeBytes { - fn from_ne_bytes(bytes: [u8; 8]) -> Self; -} - -#[cfg(no_from_ne_bytes)] -impl FromNeBytes for u64 { - fn from_ne_bytes(bytes: [u8; 8]) -> Self { - unsafe { std::mem::transmute(bytes) } - } -} - pub(crate) use crate::alloc::vec::Vec; #[cfg(no_alloc_crate)] // rustc <1.36 pub(crate) mod alloc { + pub use std::alloc; pub use std::vec; - - pub mod alloc { - use std::mem; - - pub struct Layout { - size: usize, - } - - impl Layout { - pub unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self { - assert_eq!(align, 2); - Layout { size } - } - } - - pub unsafe fn alloc(layout: Layout) -> *mut u8 { - let len_u16 = (layout.size + 1) / 2; - let mut vec = Vec::new(); - vec.reserve_exact(len_u16); - let ptr: *mut u16 = vec.as_mut_ptr(); - mem::forget(vec); - ptr as *mut u8 - } - - pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { - let len_u16 = (layout.size + 1) / 2; - unsafe { Vec::from_raw_parts(ptr as *mut u16, 0, len_u16) }; - } - } } diff --git a/src/error.rs b/src/error.rs index 72749c2..a514e3f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -37,15 +37,26 @@ impl Display for Error { ErrorKind::UnexpectedChar(pos, ch) => { write!( formatter, - "unexpected character {:?} while parsing {}", - ch, pos, + "unexpected character {} while parsing {}", + QuotedChar(*ch), + pos, ) } ErrorKind::UnexpectedCharAfter(pos, ch) => { - write!(formatter, "unexpected character {:?} after {}", ch, pos) + write!( + formatter, + "unexpected character {} after {}", + QuotedChar(*ch), + pos, + ) } ErrorKind::ExpectedCommaFound(pos, ch) => { - write!(formatter, "expected comma after {}, found {:?}", pos, ch) + write!( + formatter, + "expected comma after {}, found {}", + pos, + QuotedChar(*ch), + ) } ErrorKind::LeadingZero(pos) => { write!(formatter, "invalid leading zero in {}", pos) @@ -96,3 +107,18 @@ impl Debug for Error { Ok(()) } } + +struct QuotedChar(char); + +impl Display for QuotedChar { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + // Standard library versions prior to https://github.com/rust-lang/rust/pull/95345 + // print character 0 as '\u{0}'. We prefer '\0' to keep error messages + // the same across all supported Rust versions. + if self.0 == '\0' { + formatter.write_str("'\\0'") + } else { + write!(formatter, "{:?}", self.0) + } + } +} diff --git a/src/identifier.rs b/src/identifier.rs index 500239e..0273ae6 100644 --- a/src/identifier.rs +++ b/src/identifier.rs @@ -66,51 +66,82 @@ // repr, leaving it available as a niche for downstream code. For example this // allows size_of::() == size_of::>(). -use crate::alloc::alloc::{alloc, dealloc, Layout}; -#[cfg(no_from_ne_bytes)] -use crate::backport::FromNeBytes; +use crate::alloc::alloc::{alloc, dealloc, handle_alloc_error, Layout}; +use core::isize; use core::mem; use core::num::{NonZeroU64, NonZeroUsize}; -use core::ptr; +use core::ptr::{self, NonNull}; use core::slice; use core::str; +use core::usize; -#[repr(transparent)] +const PTR_BYTES: usize = mem::size_of::>(); + +// If pointers are already 8 bytes or bigger, then 0. If pointers are smaller +// than 8 bytes, then Identifier will contain a byte array to raise its size up +// to 8 bytes total. +const TAIL_BYTES: usize = 8 * (PTR_BYTES < 8) as usize - PTR_BYTES * (PTR_BYTES < 8) as usize; + +#[repr(C, align(8))] pub(crate) struct Identifier { - repr: NonZeroU64, + head: NonNull, + tail: [u8; TAIL_BYTES], } impl Identifier { - const EMPTY: NonZeroU64 = unsafe { NonZeroU64::new_unchecked(!0) }; - pub(crate) const fn empty() -> Self { + // This is a separate constant because unsafe function calls are not + // allowed in a const fn body, only in a const, until later rustc than + // what we support. + const HEAD: NonNull = unsafe { NonNull::new_unchecked(!0 as *mut u8) }; + // `mov rax, -1` - Identifier { repr: Self::EMPTY } + Identifier { + head: HEAD, + tail: [!0; TAIL_BYTES], + } } // SAFETY: string must be ASCII and not contain \0 bytes. pub(crate) unsafe fn new_unchecked(string: &str) -> Self { let len = string.len(); - let repr = match len as u64 { - 0 => Self::EMPTY, + debug_assert!(len <= isize::MAX as usize); + match len as u64 { + 0 => Self::empty(), 1..=8 => { - let mut bytes = [0u8; 8]; + let mut bytes = [0u8; mem::size_of::()]; // SAFETY: string is big enough to read len bytes, bytes is big // enough to write len bytes, and they do not overlap. unsafe { ptr::copy_nonoverlapping(string.as_ptr(), bytes.as_mut_ptr(), len) }; - // SAFETY: it's nonzero because the input string was at least 1 - // byte of ASCII and did not contain \0. - unsafe { NonZeroU64::new_unchecked(u64::from_ne_bytes(bytes)) } + // SAFETY: the head field is nonzero because the input string + // was at least 1 byte of ASCII and did not contain \0. + unsafe { mem::transmute::<[u8; mem::size_of::()], Identifier>(bytes) } } 9..=0xff_ffff_ffff_ffff => { // SAFETY: len is in a range that does not contain 0. let size = bytes_for_varint(unsafe { NonZeroUsize::new_unchecked(len) }) + len; let align = 2; + // On 32-bit and 16-bit architecture, check for size overflowing + // isize::MAX. Making an allocation request bigger than this to + // the allocator is considered UB. All allocations (including + // static ones) are limited to isize::MAX so we're guaranteed + // len <= isize::MAX, and we know bytes_for_varint(len) <= 5 + // because 128**5 > isize::MAX, which means the only problem + // that can arise is when isize::MAX - 5 <= len <= isize::MAX. + // This is pretty much guaranteed to be malicious input so we + // don't need to care about returning a good error message. + if mem::size_of::() < 8 { + let max_alloc = usize::MAX / 2 - align; + assert!(size <= max_alloc); + } // SAFETY: align is not zero, align is a power of two, and - // rounding size up to align does not overflow usize::MAX. + // rounding size up to align does not overflow isize::MAX. let layout = unsafe { Layout::from_size_align_unchecked(size, align) }; // SAFETY: layout's size is nonzero. let ptr = unsafe { alloc(layout) }; + if ptr.is_null() { + handle_alloc_error(layout); + } let mut write = ptr; let mut varint_remaining = len; while varint_remaining > 0 { @@ -124,25 +155,33 @@ impl Identifier { // SAFETY: size is bytes_for_varint(len) bytes + len bytes. This // is writing to the last len bytes. unsafe { ptr::copy_nonoverlapping(string.as_ptr(), write, len) }; - ptr_to_repr(ptr) + Identifier { + head: ptr_to_repr(ptr), + tail: [0; TAIL_BYTES], + } } 0x100_0000_0000_0000..=0xffff_ffff_ffff_ffff => { unreachable!("please refrain from storing >64 petabytes of text in semver version"); } #[cfg(no_exhaustive_int_match)] // rustc <1.33 _ => unreachable!(), - }; - Identifier { repr } + } } pub(crate) fn is_empty(&self) -> bool { // `cmp rdi, -1` -- basically: `repr as i64 == -1` - self.repr == Self::EMPTY + let empty = Self::empty(); + let is_empty = self.head == empty.head && self.tail == empty.tail; + // The empty representation does nothing on Drop. We can't let this one + // drop normally because `impl Drop for Identifier` calls is_empty; that + // would be an infinite recursion. + mem::forget(empty); + is_empty } fn is_inline(&self) -> bool { // `test rdi, rdi` -- basically: `repr as i64 >= 0` - self.repr.get() >> 63 == 0 + self.head.as_ptr() as usize >> (PTR_BYTES * 8 - 1) == 0 } fn is_empty_or_inline(&self) -> bool { @@ -155,38 +194,46 @@ impl Identifier { "" } else if self.is_inline() { // SAFETY: repr is in the inline representation. - unsafe { inline_as_str(&self.repr) } + unsafe { inline_as_str(self) } } else { // SAFETY: repr is in the heap allocated representation. - unsafe { ptr_as_str(&self.repr) } + unsafe { ptr_as_str(&self.head) } } } } impl Clone for Identifier { fn clone(&self) -> Self { - let repr = if self.is_empty_or_inline() { - self.repr + if self.is_empty_or_inline() { + Identifier { + head: self.head, + tail: self.tail, + } } else { - let ptr = repr_to_ptr(self.repr); + let ptr = repr_to_ptr(self.head); // SAFETY: ptr is one of our own heap allocations. let len = unsafe { decode_len(ptr) }; let size = bytes_for_varint(len) + len.get(); let align = 2; // SAFETY: align is not zero, align is a power of two, and rounding - // size up to align does not overflow usize::MAX. This is just + // size up to align does not overflow isize::MAX. This is just // duplicating a previous allocation where all of these guarantees // were already made. let layout = unsafe { Layout::from_size_align_unchecked(size, align) }; // SAFETY: layout's size is nonzero. let clone = unsafe { alloc(layout) }; + if clone.is_null() { + handle_alloc_error(layout); + } // SAFETY: new allocation cannot overlap the previous one (this was // not a realloc). The argument ptrs are readable/writeable // respectively for size bytes. unsafe { ptr::copy_nonoverlapping(ptr, clone, size) } - ptr_to_repr(clone) - }; - Identifier { repr } + Identifier { + head: ptr_to_repr(clone), + tail: [0; TAIL_BYTES], + } + } } } @@ -195,7 +242,7 @@ impl Drop for Identifier { if self.is_empty_or_inline() { return; } - let ptr = repr_to_ptr_mut(self.repr); + let ptr = repr_to_ptr_mut(self.head); // SAFETY: ptr is one of our own heap allocations. let len = unsafe { decode_len(ptr) }; let size = bytes_for_varint(len) + len.get(); @@ -214,46 +261,65 @@ impl PartialEq for Identifier { fn eq(&self, rhs: &Self) -> bool { if self.is_empty_or_inline() { // Fast path (most common) - self.repr == rhs.repr + self.head == rhs.head && self.tail == rhs.tail } else if rhs.is_empty_or_inline() { false } else { // SAFETY: both reprs are in the heap allocated representation. - unsafe { ptr_as_str(&self.repr) == ptr_as_str(&rhs.repr) } + unsafe { ptr_as_str(&self.head) == ptr_as_str(&rhs.head) } } } } +unsafe impl Send for Identifier {} +unsafe impl Sync for Identifier {} + // We use heap pointers that are 2-byte aligned, meaning they have an // insignificant 0 in the least significant bit. We take advantage of that // unneeded bit to rotate a 1 into the most significant bit to make the repr // distinguishable from ASCII bytes. -fn ptr_to_repr(ptr: *mut u8) -> NonZeroU64 { +fn ptr_to_repr(original: *mut u8) -> NonNull { // `mov eax, 1` // `shld rax, rdi, 63` - let repr = (ptr as u64 | 1).rotate_right(1); + let modified = (original as usize | 1).rotate_right(1); + + // `original + (modified - original)`, but being mindful of provenance. + let diff = modified.wrapping_sub(original as usize); + let modified = original.wrapping_add(diff); // SAFETY: the most significant bit of repr is known to be set, so the value // is not zero. - unsafe { NonZeroU64::new_unchecked(repr) } + unsafe { NonNull::new_unchecked(modified) } } // Shift out the 1 previously placed into the most significant bit of the least // significant byte. Shift in a low 0 bit to reconstruct the original 2-byte // aligned pointer. -fn repr_to_ptr(repr: NonZeroU64) -> *const u8 { +fn repr_to_ptr(modified: NonNull) -> *const u8 { // `lea rax, [rdi + rdi]` - (repr.get() << 1) as *const u8 + let modified = modified.as_ptr(); + let original = (modified as usize) << 1; + + // `modified + (original - modified)`, but being mindful of provenance. + let diff = original.wrapping_sub(modified as usize); + modified.wrapping_add(diff) } -fn repr_to_ptr_mut(repr: NonZeroU64) -> *mut u8 { +fn repr_to_ptr_mut(repr: NonNull) -> *mut u8 { repr_to_ptr(repr) as *mut u8 } // Compute the length of the inline string, assuming the argument is in short // string representation. Short strings are stored as 1 to 8 nonzero ASCII // bytes, followed by \0 padding for the remaining bytes. -fn inline_len(repr: NonZeroU64) -> NonZeroUsize { +// +// SAFETY: the identifier must indeed be in the inline representation. +unsafe fn inline_len(repr: &Identifier) -> NonZeroUsize { + // SAFETY: Identifier's layout is align(8) and at least size 8. We're doing + // an aligned read of the first 8 bytes from it. The bytes are not all zero + // because inline strings are at least 1 byte long and cannot contain \0. + let repr = unsafe { ptr::read(repr as *const Identifier as *const NonZeroU64) }; + // Rustc >=1.53 has intrinsics for counting zeros on a non-zeroable integer. // On many architectures these are more efficient than counting on ordinary // zeroable integers (bsf vs cttz). On rustc <1.53 without those intrinsics, @@ -275,9 +341,9 @@ fn inline_len(repr: NonZeroU64) -> NonZeroUsize { // SAFETY: repr must be in the inline representation, i.e. at least 1 and at // most 8 nonzero ASCII bytes padded on the end with \0 bytes. -unsafe fn inline_as_str(repr: &NonZeroU64) -> &str { - let ptr = repr as *const NonZeroU64 as *const u8; - let len = inline_len(*repr).get(); +unsafe fn inline_as_str(repr: &Identifier) -> &str { + let ptr = repr as *const Identifier as *const u8; + let len = unsafe { inline_len(repr) }.get(); // SAFETY: we are viewing the nonzero ASCII prefix of the inline repr's // contents as a slice of bytes. Input/output lifetimes are correctly // associated. @@ -335,7 +401,7 @@ unsafe fn decode_len(ptr: *const u8) -> NonZeroUsize { // SAFETY: repr must be in the heap allocated representation, with varint header // and string contents already written. -unsafe fn ptr_as_str(repr: &NonZeroU64) -> &str { +unsafe fn ptr_as_str(repr: &NonNull) -> &str { let ptr = repr_to_ptr(*repr); let len = unsafe { decode_len(ptr) }; let header = bytes_for_varint(len); diff --git a/src/lib.rs b/src/lib.rs index 5887ea2..32ed96d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ //! //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust -//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K +//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs //! //!
//! @@ -60,7 +60,7 @@ //! //! [Specifying Dependencies]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html -#![doc(html_root_url = "https://docs.rs/semver/1.0.6")] +#![doc(html_root_url = "https://docs.rs/semver/1.0.16")] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![cfg_attr(all(not(feature = "std"), not(no_alloc_crate)), no_std)] #![cfg_attr(not(no_unsafe_op_in_unsafe_fn_lint), deny(unsafe_op_in_unsafe_fn))] diff --git a/src/parse.rs b/src/parse.rs index bf70739..6a8f6ff 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -262,23 +262,23 @@ fn identifier(input: &str, pos: Position) -> Result<(&str, &str), Error> { fn op(input: &str) -> (Op, &str) { let bytes = input.as_bytes(); - if bytes.get(0) == Some(&b'=') { + if bytes.first() == Some(&b'=') { (Op::Exact, &input[1..]) - } else if bytes.get(0) == Some(&b'>') { + } else if bytes.first() == Some(&b'>') { if bytes.get(1) == Some(&b'=') { (Op::GreaterEq, &input[2..]) } else { (Op::Greater, &input[1..]) } - } else if bytes.get(0) == Some(&b'<') { + } else if bytes.first() == Some(&b'<') { if bytes.get(1) == Some(&b'=') { (Op::LessEq, &input[2..]) } else { (Op::Less, &input[1..]) } - } else if bytes.get(0) == Some(&b'~') { + } else if bytes.first() == Some(&b'~') { (Op::Tilde, &input[1..]) - } else if bytes.get(0) == Some(&b'^') { + } else if bytes.first() == Some(&b'^') { (Op::Caret, &input[1..]) } else { (Op::DEFAULT, input) diff --git a/src/serde.rs b/src/serde.rs index 06eceb6..1fcc7d8 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,4 +1,4 @@ -use crate::{Version, VersionReq}; +use crate::{Comparator, Version, VersionReq}; use core::fmt; use serde::de::{Deserialize, Deserializer, Error, Visitor}; use serde::ser::{Serialize, Serializer}; @@ -21,6 +21,15 @@ impl Serialize for VersionReq { } } +impl Serialize for Comparator { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_str(self) + } +} + impl<'de> Deserialize<'de> for Version { fn deserialize(deserializer: D) -> Result where @@ -72,3 +81,29 @@ impl<'de> Deserialize<'de> for VersionReq { deserializer.deserialize_str(VersionReqVisitor) } } + +impl<'de> Deserialize<'de> for Comparator { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct ComparatorVisitor; + + impl<'de> Visitor<'de> for ComparatorVisitor { + type Value = Comparator; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("semver comparator") + } + + fn visit_str(self, string: &str) -> Result + where + E: Error, + { + string.parse().map_err(Error::custom) + } + } + + deserializer.deserialize_str(ComparatorVisitor) + } +} diff --git a/tests/test_autotrait.rs b/tests/test_autotrait.rs new file mode 100644 index 0000000..af8534b --- /dev/null +++ b/tests/test_autotrait.rs @@ -0,0 +1,12 @@ +fn assert_send_sync() {} + +#[test] +fn test() { + assert_send_sync::(); + assert_send_sync::(); + assert_send_sync::(); + assert_send_sync::(); + assert_send_sync::(); + assert_send_sync::(); + assert_send_sync::(); +} diff --git a/tests/test_version_req.rs b/tests/test_version_req.rs index 2ea33ab..98a03ac 100644 --- a/tests/test_version_req.rs +++ b/tests/test_version_req.rs @@ -336,7 +336,7 @@ pub fn test_parse_errors() { let err = req_err("\0"); assert_to_string( err, - "unexpected character '\\u{0}' while parsing major version number", + "unexpected character '\\0' while parsing major version number", ); let err = req_err(">= >= 0.0.2"); -- cgit v1.2.3 From 0e51d1d1f3484298a72cdde8d9fc9090b2d618d1 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Mon, 23 Jan 2023 14:02:50 +0000 Subject: Update TEST_MAPPING Test: None Change-Id: I3f85dcc3e38e796165c0f075c59b8abf366d0af2 --- TEST_MAPPING | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/TEST_MAPPING b/TEST_MAPPING index 061ff86..a6711a8 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -2,12 +2,18 @@ { "presubmit": [ { - "name": "virtualizationservice_device_test" + "name": "virtualizationmanager_device_test" + }, + { + "name": "vm.test" } ], "presubmit-rust": [ { - "name": "virtualizationservice_device_test" + "name": "virtualizationmanager_device_test" + }, + { + "name": "vm.test" } ] } -- cgit v1.2.3 From 304d73376c013e6d8e2bc5b4c2fa88fc25fba249 Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep Date: Mon, 30 Jan 2023 13:43:02 +0100 Subject: Update TEST_MAPPING Test: atest Change-Id: I04abb74286730320b1a9623ffec23c4d5aca8d31 --- TEST_MAPPING | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/TEST_MAPPING b/TEST_MAPPING index a6711a8..5366bfb 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,19 +1,11 @@ // Generated by update_crate_tests.py for tests that depend on this crate. { - "presubmit": [ + "imports": [ { - "name": "virtualizationmanager_device_test" + "path": "packages/modules/Virtualization/virtualizationmanager" }, { - "name": "vm.test" - } - ], - "presubmit-rust": [ - { - "name": "virtualizationmanager_device_test" - }, - { - "name": "vm.test" + "path": "packages/modules/Virtualization/vm" } ] } -- cgit v1.2.3 From 4a4be7df2851bb810de25a0a0154699cb1b09b6b Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Tue, 7 Mar 2023 17:24:47 -0800 Subject: Make semver available to product and vendor Bug: 270690570 Test: mma in external/rust/crates Change-Id: I5f28aa3d5ec96f701030b5899b726c570f2eba8a --- Android.bp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Android.bp b/Android.bp index 8a8a1eb..500f53f 100644 --- a/Android.bp +++ b/Android.bp @@ -56,4 +56,6 @@ rust_library { "//apex_available:platform", "com.android.virt", ], + product_available: true, + vendor_available: true, } -- cgit v1.2.3 From 55c2df20e645d70323c8255a065aa2a711045cce Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep Date: Mon, 3 Apr 2023 09:08:56 +0200 Subject: Upgrade semver to 1.0.17 This project was upgraded with external_updater. Usage: tools/external_updater/updater.sh update rust/crates/semver For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md Test: TreeHugger Change-Id: Ib884cb11b2dcc1b06ce521fa64a31eac633a5d22 --- .cargo_vcs_info.json | 2 +- .github/workflows/ci.yml | 17 ++++++++++++++++- Android.bp | 2 +- Cargo.toml | 4 ++-- Cargo.toml.orig | 2 +- LICENSE-APACHE | 25 ------------------------- METADATA | 10 +++++----- src/error.rs | 2 ++ src/lib.rs | 2 +- src/parse.rs | 4 ++++ tests/test_autotrait.rs | 2 ++ tests/test_version.rs | 5 +---- 12 files changed, 36 insertions(+), 41 deletions(-) diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 54870e9..f1fdee4 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "dda4b834a9c896d7e1c8d17d5e326386bb346b50" + "sha1": "1b162e8dd2283a62380c30b04cf8444c9c194d5c" }, "path_in_vcs": "" } \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0bfe3a7..8acd5fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,7 @@ name: CI on: push: pull_request: + workflow_dispatch: schedule: [cron: "40 1 * * *"] permissions: @@ -12,8 +13,13 @@ env: RUSTFLAGS: -Dwarnings jobs: + pre_ci: + uses: dtolnay/.github/.github/workflows/pre_ci.yml@master + test: name: Rust ${{matrix.rust}} + needs: pre_ci + if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest strategy: fail-fast: false @@ -25,6 +31,9 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} + - name: Enable type layout randomization + run: echo RUSTFLAGS=${RUSTFLAGS}\ -Zrandomize-layout >> $GITHUB_ENV + if: matrix.rust == 'nightly' - run: cargo test - run: cargo check --no-default-features - run: cargo check --features serde @@ -32,6 +41,8 @@ jobs: node: name: Node + needs: pre_ci + if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest timeout-minutes: 45 steps: @@ -54,6 +65,8 @@ jobs: miri: name: Miri + needs: pre_ci + if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest env: MIRIFLAGS: -Zmiri-strict-provenance @@ -72,13 +85,15 @@ jobs: fuzz: name: Fuzz + needs: pre_ci + if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/install@cargo-fuzz - - run: cargo fuzz build -O + - run: cargo fuzz check outdated: name: Outdated diff --git a/Android.bp b/Android.bp index 500f53f..f148121 100644 --- a/Android.bp +++ b/Android.bp @@ -41,7 +41,7 @@ rust_library { name: "libsemver", crate_name: "semver", cargo_env_compat: true, - cargo_pkg_version: "1.0.16", + cargo_pkg_version: "1.0.17", srcs: ["src/lib.rs"], edition: "2018", features: [ diff --git a/Cargo.toml b/Cargo.toml index e0bfea2..e7fcfb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ edition = "2018" rust-version = "1.31" name = "semver" -version = "1.0.16" +version = "1.0.17" authors = ["David Tolnay "] description = "Parser and evaluator for Cargo's flavor of Semantic Versioning" documentation = "https://docs.rs/semver" @@ -27,11 +27,11 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/semver" [package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = [ "--cfg", "doc_cfg", ] +targets = ["x86_64-unknown-linux-gnu"] [lib] doc-scrape-examples = false diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 307c361..30ee98e 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,6 +1,6 @@ [package] name = "semver" -version = "1.0.16" +version = "1.0.17" authors = ["David Tolnay "] categories = ["data-structures", "no-std"] description = "Parser and evaluator for Cargo's flavor of Semantic Versioning" diff --git a/LICENSE-APACHE b/LICENSE-APACHE index 16fe87b..1b5ec8b 100644 --- a/LICENSE-APACHE +++ b/LICENSE-APACHE @@ -174,28 +174,3 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/METADATA b/METADATA index 886c058..642e29a 100644 --- a/METADATA +++ b/METADATA @@ -11,13 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/semver/semver-1.0.16.crate" + value: "https://static.crates.io/crates/semver/semver-1.0.17.crate" } - version: "1.0.16" + version: "1.0.17" license_type: NOTICE last_upgrade_date { - year: 2022 - month: 12 - day: 19 + year: 2023 + month: 4 + day: 3 } } diff --git a/src/error.rs b/src/error.rs index a514e3f..93b05ee 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,6 +2,7 @@ use crate::parse::Error; use core::fmt::{self, Debug, Display}; pub(crate) enum ErrorKind { + Empty, UnexpectedEnd(Position), UnexpectedChar(Position, char), UnexpectedCharAfter(Position, char), @@ -31,6 +32,7 @@ impl std::error::Error for Error {} impl Display for Error { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match &self.kind { + ErrorKind::Empty => formatter.write_str("empty string, expected a semver version"), ErrorKind::UnexpectedEnd(pos) => { write!(formatter, "unexpected end of input while parsing {}", pos) } diff --git a/src/lib.rs b/src/lib.rs index 32ed96d..d6a8fe3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,7 +60,7 @@ //! //! [Specifying Dependencies]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html -#![doc(html_root_url = "https://docs.rs/semver/1.0.16")] +#![doc(html_root_url = "https://docs.rs/semver/1.0.17")] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![cfg_attr(all(not(feature = "std"), not(no_alloc_crate)), no_std)] #![cfg_attr(not(no_unsafe_op_in_unsafe_fn_lint), deny(unsafe_op_in_unsafe_fn))] diff --git a/src/parse.rs b/src/parse.rs index 6a8f6ff..e92d87a 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -26,6 +26,10 @@ impl FromStr for Version { type Err = Error; fn from_str(text: &str) -> Result { + if text.is_empty() { + return Err(Error::new(ErrorKind::Empty)); + } + let mut pos = Position::Major; let (major, text) = numeric_identifier(text, pos)?; let text = dot(text, pos)?; diff --git a/tests/test_autotrait.rs b/tests/test_autotrait.rs index af8534b..5d16689 100644 --- a/tests/test_autotrait.rs +++ b/tests/test_autotrait.rs @@ -1,3 +1,5 @@ +#![allow(clippy::extra_unused_type_parameters)] + fn assert_send_sync() {} #[test] diff --git a/tests/test_version.rs b/tests/test_version.rs index 93a528c..de3628f 100644 --- a/tests/test_version.rs +++ b/tests/test_version.rs @@ -12,10 +12,7 @@ use semver::{BuildMetadata, Prerelease, Version}; #[test] fn test_parse() { let err = version_err(""); - assert_to_string( - err, - "unexpected end of input while parsing major version number", - ); + assert_to_string(err, "empty string, expected a semver version"); let err = version_err(" "); assert_to_string( -- cgit v1.2.3