Snap for 10453563 from dab5a943946be1805fedc9c609c1a6647f156f9e to mainline-art-release

Change-Id: I15b255b903709facd2605f14f24d9f51cb260ed0
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index a7e226d..f1fdee4 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "8d8bdb2adaf4572b3c4e8502e7498838b2cf7ccb"
+    "sha1": "1b162e8dd2283a62380c30b04cf8444c9c194d5c"
   },
   "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..8acd5fc 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,24 +3,37 @@
 on:
   push:
   pull_request:
+  workflow_dispatch:
   schedule: [cron: "40 1 * * *"]
 
+permissions:
+  contents: read
+
 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
       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}}
+      - 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
@@ -28,9 +41,12 @@
 
   node:
     name: Node
+    needs: pre_ci
+    if: needs.pre_ci.outputs.continue
     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 +57,51 @@
     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
+    needs: pre_ci
+    if: needs.pre_ci.outputs.continue
     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
+    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
-        with:
-          components: miri
-      - run: cargo miri test
+      - uses: dtolnay/install@cargo-fuzz
+      - run: cargo fuzz check
 
   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..f148121 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 @@
     name: "libsemver",
     crate_name: "semver",
     cargo_env_compat: true,
-    cargo_pkg_version: "1.0.6",
+    cargo_pkg_version: "1.0.17",
     srcs: ["src/lib.rs"],
     edition: "2018",
     features: [
@@ -58,4 +56,6 @@
         "//apex_available:platform",
         "com.android.virt",
     ],
+    product_available: true,
+    vendor_available: true,
 }
diff --git a/Cargo.toml b/Cargo.toml
index e250936..e7fcfb7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,20 +13,28 @@
 edition = "2018"
 rust-version = "1.31"
 name = "semver"
-version = "1.0.6"
+version = "1.0.17"
 authors = ["David Tolnay <dtolnay@gmail.com>"]
 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"
 
 [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
 
 [dependencies.serde]
 version = "1.0"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index ef65cdc..30ee98e 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,14 +1,15 @@
 [package]
 name = "semver"
-version = "1.0.6"
+version = "1.0.17"
 authors = ["David Tolnay <dtolnay@gmail.com>"]
-edition = "2018"
-rust-version = "1.31"
-license = "MIT OR Apache-2.0"
+categories = ["data-structures", "no-std"]
 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"
+edition = "2018"
+keywords = ["cargo"]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/dtolnay/semver"
+rust-version = "1.31"
 
 [features]
 default = ["std"]
@@ -17,6 +18,9 @@
 [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/LICENSE-APACHE b/LICENSE-APACHE
index 16fe87b..1b5ec8b 100644
--- a/LICENSE-APACHE
+++ b/LICENSE-APACHE
@@ -174,28 +174,3 @@
    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 98ff1b7..642e29a 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 @@
   }
   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.17.crate"
   }
-  version: "1.0.6"
+  version: "1.0.17"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2022
-    month: 3
-    day: 1
+    year: 2023
+    month: 4
+    day: 3
   }
 }
diff --git a/README.md b/README.md
index 9de11d7..a9a1cb8 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,8 @@
 
 [<img alt="github" src="https://img.shields.io/badge/github-dtolnay/semver-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/semver)
 [<img alt="crates.io" src="https://img.shields.io/crates/v/semver.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/semver)
-[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-semver-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/semver)
-[<img alt="build status" src="https://img.shields.io/github/workflow/status/dtolnay/semver/CI/master?style=for-the-badge" height="20">](https://github.com/dtolnay/semver/actions?query=branch%3Amaster)
+[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-semver-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/semver)
+[<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/dtolnay/semver/ci.yml?branch=master&style=for-the-badge" height="20">](https://github.com/dtolnay/semver/actions?query=branch%3Amaster)
 
 A parser and evaluator for Cargo's flavor of Semantic Versioning.
 
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 061ff86..5366bfb 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,13 +1,11 @@
 // Generated by update_crate_tests.py for tests that depend on this crate.
 {
-  "presubmit": [
+  "imports": [
     {
-      "name": "virtualizationservice_device_test"
-    }
-  ],
-  "presubmit-rust": [
+      "path": "packages/modules/Virtualization/virtualizationmanager"
+    },
     {
-      "name": "virtualizationservice_device_test"
+      "path": "packages/modules/Virtualization/vm"
     }
   ]
 }
diff --git a/build.rs b/build.rs
index b39468d..81ad970 100644
--- a/build.rs
+++ b/build.rs
@@ -3,17 +3,13 @@
 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 @@
 
     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 @@
     }
 }
 
-#[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..93b05ee 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -2,6 +2,7 @@
 use core::fmt::{self, Debug, Display};
 
 pub(crate) enum ErrorKind {
+    Empty,
     UnexpectedEnd(Position),
     UnexpectedChar(Position, char),
     UnexpectedCharAfter(Position, char),
@@ -31,21 +32,33 @@
 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)
             }
             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 +109,18 @@
         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::<Version>() == size_of::<Option<Version>>().
 
-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::<NonNull<u8>>();
+
+// 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<u8>,
+    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<u8> = 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::<Identifier>()];
                 // 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>()], 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::<usize>() < 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 @@
                 // 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 @@
             ""
         } 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 @@
         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 @@
     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<u8> {
     // `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<u8>) -> *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<u8>) -> *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 @@
 
 // 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 @@
 
 // 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<u8>) -> &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..d6a8fe3 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
 //!
 //! <br>
 //!
@@ -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.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 bf70739..e92d87a 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -26,6 +26,10 @@
     type Err = Error;
 
     fn from_str(text: &str) -> Result<Self, Self::Err> {
+        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)?;
@@ -262,23 +266,23 @@
 
 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 Comparator {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.collect_str(self)
+    }
+}
+
 impl<'de> Deserialize<'de> for Version {
     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     where
@@ -72,3 +81,29 @@
         deserializer.deserialize_str(VersionReqVisitor)
     }
 }
+
+impl<'de> Deserialize<'de> for Comparator {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    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<E>(self, string: &str) -> Result<Self::Value, E>
+            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..5d16689
--- /dev/null
+++ b/tests/test_autotrait.rs
@@ -0,0 +1,14 @@
+#![allow(clippy::extra_unused_type_parameters)]
+
+fn assert_send_sync<T: Send + Sync>() {}
+
+#[test]
+fn test() {
+    assert_send_sync::<semver::BuildMetadata>();
+    assert_send_sync::<semver::Comparator>();
+    assert_send_sync::<semver::Error>();
+    assert_send_sync::<semver::Prerelease>();
+    assert_send_sync::<semver::Version>();
+    assert_send_sync::<semver::VersionReq>();
+    assert_send_sync::<semver::Op>();
+}
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 @@
 #[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(
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 @@
     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");