diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/debugid | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/debugid')
-rw-r--r-- | third_party/rust/debugid/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/debugid/CHANGELOG.md | 80 | ||||
-rw-r--r-- | third_party/rust/debugid/Cargo.toml | 38 | ||||
-rw-r--r-- | third_party/rust/debugid/LICENSE | 201 | ||||
-rw-r--r-- | third_party/rust/debugid/Makefile | 32 | ||||
-rw-r--r-- | third_party/rust/debugid/README.md | 13 | ||||
-rw-r--r-- | third_party/rust/debugid/clippy.toml | 1 | ||||
-rwxr-xr-x | third_party/rust/debugid/scripts/bump-version.sh | 17 | ||||
-rw-r--r-- | third_party/rust/debugid/src/lib.rs | 543 | ||||
-rw-r--r-- | third_party/rust/debugid/tests/test_codeid.rs | 27 | ||||
-rw-r--r-- | third_party/rust/debugid/tests/test_debugid.rs | 388 | ||||
-rw-r--r-- | third_party/rust/debugid/tests/test_serde.rs | 44 |
12 files changed, 1385 insertions, 0 deletions
diff --git a/third_party/rust/debugid/.cargo-checksum.json b/third_party/rust/debugid/.cargo-checksum.json new file mode 100644 index 0000000000..f925c468b0 --- /dev/null +++ b/third_party/rust/debugid/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"2cf828df6f9e9544eb35466de3646b03262f9d03938da9f9c6ef2c2e68e7c8d4","Cargo.toml":"e866dbd4e8fcfae5766ad977779cda62cfc25eb1324487f16571f5ba6f1fb9d2","LICENSE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","Makefile":"acc4e8dc69b0fae078515c01c3f4353afe18f54a616ec7e6e20ddc893cd97e59","README.md":"2381579ffc25b9eddd07c4a470b1a695876701ee8004174c8f657d3831e4f00d","clippy.toml":"444ea54130c23cdd2b5a5d4976c00e63175cfd67501d14888158a1ea841b48d4","scripts/bump-version.sh":"81aad8c6d661a75c064d351c1722a0fc19d9f00883a4832a11a7ace936b7858a","src/lib.rs":"d39adde21fef7b00210b90c20d94320fc8fad94af7f073022c36b6c3635eb961","tests/test_codeid.rs":"e90ef3d209ecb79b2f3d5568b62645a3341565168a7ba4a81770eea7b93a9d71","tests/test_debugid.rs":"98d93057ea1bfed142797589e24f72f58741e7e037f5423b927a5d1c5b98b4e2","tests/test_serde.rs":"37ca5066022e9771e464184dba94adbad2a223d80d15513c6662d8ffca8536ee"},"package":"bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"}
\ No newline at end of file diff --git a/third_party/rust/debugid/CHANGELOG.md b/third_party/rust/debugid/CHANGELOG.md new file mode 100644 index 0000000000..4efc7090a9 --- /dev/null +++ b/third_party/rust/debugid/CHANGELOG.md @@ -0,0 +1,80 @@ +# Changelog + +## 0.8.0 + +- Bump Minimal Supported Rust Version to 1.46 due to dependencies. +- Update `uuid` dependency to `1.0`. + +## 0.7.3 + +- Bump Minimal Supported Rust Version to 1.36 due to dependencies. +- Add support for PDB 2.0 format. + +## 0.7.2 + +- Implement stricter and more consistent validation in `FromStr for DebugId`. +- `DebugId::from_breakpad` now properly validates the identifier. +- Remove internal dependencies on `regex` and `lazy_static`. + +## 0.7.1 + +- Remove deprecated implementation of `Error::description` on errors. + +## 0.7.0 + +- Update `uuid` to `0.8.1`. + +## 0.6.0 + +_yanked_ + +## 0.5.3 + +- Only allow ASCII hex charactes in code identifiers. +- Implement `AsRef<str>` for `CodeId`. + +## 0.5.2 + +- Implement conversion traits for `CodeId`. +- Always coerce code identifiers to lower case. + +## 0.6.0 (yanked) + +- Change `CodeId` to be a binary buffer instead of formatted string. + +## 0.5.1 + +- Implement `Display` and `std::error::Error` for `ParseCodeIdError`. + +## 0.5.0 + +- Add `CodeId`, an identifier for code files. +- Add `DebugId::nil` to create an empty id. This is the default. +- Add `DebugId::is_nil` to check whether a debug ID is empty. +- **Breaking Change:** The serde feature is now only called `"serde"`. + +## 0.4.0 + +- **Breaking Change**: Require Rust 1.31.0 (Edition 2018) or newer +- Make parsing from string future-proof + +## 0.3.1 + +Add a conversion from Microsoft GUIDs. + +## 0.3.0 + +Update to `uuid:0.7.0`. + +## 0.2.0 + +Renamed `DebugIdParseError` to `ParseDebugIdError`, according to Rust naming +guidelines. + +## 0.1.1 + +Improved implementation of the `serde` traits. + +## 0.1.0 + +Initial release. diff --git a/third_party/rust/debugid/Cargo.toml b/third_party/rust/debugid/Cargo.toml new file mode 100644 index 0000000000..b523afc32a --- /dev/null +++ b/third_party/rust/debugid/Cargo.toml @@ -0,0 +1,38 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "debugid" +version = "0.8.0" +authors = ["Sentry <hello@sentry.io>"] +description = "Common reusable types for implementing the sentry.io protocol." +homepage = "https://sentry.io/" +documentation = "https://docs.rs/debugid" +readme = "README.md" +keywords = [ + "sentry", + "debugid", + "breakpad", + "crashpad", +] +license = "Apache-2.0" +repository = "https://github.com/getsentry/rust-debugid" + +[dependencies.serde] +version = "1.0.85" +optional = true + +[dependencies.uuid] +version = "1.0.0" + +[dev-dependencies.serde_json] +version = "1.0.37" diff --git a/third_party/rust/debugid/LICENSE b/third_party/rust/debugid/LICENSE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/third_party/rust/debugid/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + 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/third_party/rust/debugid/Makefile b/third_party/rust/debugid/Makefile new file mode 100644 index 0000000000..f0670799d8 --- /dev/null +++ b/third_party/rust/debugid/Makefile @@ -0,0 +1,32 @@ +all: style lint test +.PHONY: all + +check: style lint +.PHONY: check + +build: + @cargo build +.PHONY: build + +doc: + @cargo doc +.PHONY: doc + +test: + @cargo test +.PHONY: test + +style: + @rustup component add rustfmt --toolchain stable 2> /dev/null + cargo +stable fmt -- --check +.PHONY: style + +format: + @rustup component add rustfmt --toolchain stable 2> /dev/null + @cargo +stable fmt +.PHONY: format + +lint: + @rustup component add clippy --toolchain stable 2> /dev/null + @cargo +stable clippy --tests -- -D clippy::all +.PHONY: lint diff --git a/third_party/rust/debugid/README.md b/third_party/rust/debugid/README.md new file mode 100644 index 0000000000..a390688022 --- /dev/null +++ b/third_party/rust/debugid/README.md @@ -0,0 +1,13 @@ +# Rust Debug ID + +<a href="https://travis-ci.org/getsentry/rust-debugid"><img src="https://travis-ci.org/getsentry/rust-debugid.svg?branch=master" alt=""></a> +<a href="https://crates.io/crates/debugid"><img src="https://img.shields.io/crates/v/debugid.svg" alt=""></a> + +This crate implements a type that is a thin wrapper around uuids that +can hold a "debug id". This is a concept that originally comes from +breakpad and is also used by Sentry to identify a debug information +file. + +## License + +Symbolic is licensed under the Apache 2 license. diff --git a/third_party/rust/debugid/clippy.toml b/third_party/rust/debugid/clippy.toml new file mode 100644 index 0000000000..eb66960ac8 --- /dev/null +++ b/third_party/rust/debugid/clippy.toml @@ -0,0 +1 @@ +msrv = "1.46" diff --git a/third_party/rust/debugid/scripts/bump-version.sh b/third_party/rust/debugid/scripts/bump-version.sh new file mode 100755 index 0000000000..41e67772d9 --- /dev/null +++ b/third_party/rust/debugid/scripts/bump-version.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -euo pipefail + +if [ "$(uname -s)" != "Linux" ]; then + echo "Please use the GitHub Action." + exit 1 +fi + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd $SCRIPT_DIR/.. + +OLD_VERSION="${1}" +NEW_VERSION="${2}" + +echo "Bumping version: ${NEW_VERSION}" + +find . -name Cargo.toml -type f -exec sed -i '' -e "s/^version.*/version = \"$NEW_VERSION\"/" {} \; diff --git a/third_party/rust/debugid/src/lib.rs b/third_party/rust/debugid/src/lib.rs new file mode 100644 index 0000000000..7022051a41 --- /dev/null +++ b/third_party/rust/debugid/src/lib.rs @@ -0,0 +1,543 @@ +//! This crate provides types for identifiers of object files, such as executables, dynamic +//! libraries or debug companion files. The concept originates in Google Breakpad and defines two +//! types: +//! +//! - [`CodeId`]: Identifies the file containing source code, i.e. the actual library or +//! executable. The identifier is platform dependent and implementation defined. Thus, there is +//! no canonical representation. +//! - [`DebugId`]: Identifies a debug information file, which may or may not use information from +//! the Code ID. The contents are also implementation defined, but as opposed to `CodeId`, the +//! structure is streamlined across platforms. It is also guaranteed to be 32 bytes in size. +//! +//! [`CodeId`]: struct.CodeId.html +//! [`DebugId`]: struct.DebugId.html + +#![warn(missing_docs)] + +use std::error; +use std::fmt; +use std::fmt::Write; +use std::str; + +use uuid::{Bytes, Uuid}; + +/// Indicates an error parsing a [`DebugId`](struct.DebugId.html). +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct ParseDebugIdError; + +impl error::Error for ParseDebugIdError {} + +impl fmt::Display for ParseDebugIdError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "invalid debug identifier") + } +} + +#[derive(Clone, Copy, Debug)] +struct ParseOptions { + allow_hyphens: bool, + require_appendix: bool, + allow_tail: bool, +} + +/// Unique identifier for debug information files and their debug information. +/// +/// This type is analogous to [`CodeId`], except that it identifies a debug file instead of the +/// actual library or executable. One some platforms, a `DebugId` is an alias for a `CodeId` but the +/// exact rules around this are complex. On Windows, the identifiers are completely different and +/// refer to separate files. +/// +/// The string representation must be between 33 and 40 characters long and consist of: +/// +/// 1. 36 character hyphenated hex representation of the UUID field +/// 2. 1-16 character lowercase hex representation of the u32 appendix +/// +/// The debug identifier is compatible to Google Breakpad. Use [`DebugId::breakpad`] to get a +/// breakpad string representation of this debug identifier. +/// +/// There is one exception to this: for the old PDB 2.0 format the debug identifier consists +/// of only a 32-bit integer + age resulting in a string representation of between 9 and 16 +/// hex characters. +/// +/// # Example +/// +/// ``` +/// # extern crate debugid; +/// use std::str::FromStr; +/// use debugid::DebugId; +/// +/// # fn foo() -> Result<(), ::debugid::ParseDebugIdError> { +/// let id = DebugId::from_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75-a")?; +/// assert_eq!("dfb8e43a-f242-3d73-a453-aeb6a777ef75-a".to_string(), id.to_string()); +/// # Ok(()) +/// # } +/// +/// # fn main() { foo().unwrap() } +/// ``` +/// +/// # In-memory representation +/// +/// The in-memory representation takes up 32 bytes and can be directly written to storage +/// and mapped back into an object reference. +/// +/// ``` +/// use std::str::FromStr; +/// use debugid::DebugId; +/// +/// let debug_id = DebugId::from_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75-a").unwrap(); +/// +/// let slice = &[debug_id]; +/// let ptr = slice.as_ptr() as *const u8; +/// let len = std::mem::size_of_val(slice); +/// let buf: &[u8] = unsafe { std::slice::from_raw_parts(ptr, len) }; +/// +/// let mut new_buf: Vec<u8> = Vec::new(); +/// std::io::copy(&mut std::io::Cursor::new(buf), &mut new_buf).unwrap(); +/// +/// let ptr = new_buf.as_ptr() as *const DebugId; +/// let new_debug_id = unsafe { &*ptr }; +/// +/// assert_eq!(*new_debug_id, debug_id); +/// ``` +/// +/// As long the bytes were written using the same major version of this crate you will be +/// able to read it again like this. +/// +/// [`CodeId`]: struct.CodeId.html +/// [`DebugId::breakpad`]: struct.DebugId.html#method.breakpad +// This needs to be backwards compatible also in its exact in-memory byte-layout since this +// struct is directly mapped from disk in e.g. Symbolic SymCache formats. The first version +// of this struct was defined as: +// +// ```rust +// struct DebugId { +// uuid: Uuid, +// appendix: u32, +// _padding: [u8; 12], +// } +// ``` +// +// For this reason the current `typ` byte represents the type of `DebugId` stored in the +// `Bytes`: +// +// - `0u8`: The `bytes` field contains a UUID. +// - `1u8`: The first 4 bytes of the `bytes` field contain a big-endian u32, the remaining +// bytes are 0. +#[repr(C, packed)] +#[derive(Default, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)] +pub struct DebugId { + bytes: Bytes, + appendix: u32, + _padding: [u8; 11], + typ: u8, +} + +impl DebugId { + /// Constructs an empty debug identifier, containing only zeros. + pub fn nil() -> Self { + Self::default() + } + + /// Constructs a `DebugId` from its `uuid`. + pub fn from_uuid(uuid: Uuid) -> Self { + Self::from_parts(uuid, 0) + } + + /// Constructs a `DebugId` from its `uuid` and `appendix` parts. + pub fn from_parts(uuid: Uuid, appendix: u32) -> Self { + DebugId { + bytes: *uuid.as_bytes(), + appendix, + typ: 0, + _padding: [0; 11], + } + } + + /// Constructs a `DebugId` from a Microsoft little-endian GUID and age. + pub fn from_guid_age(guid: &[u8], age: u32) -> Result<Self, ParseDebugIdError> { + if guid.len() != 16 { + return Err(ParseDebugIdError); + } + + let uuid = Uuid::from_bytes([ + guid[3], guid[2], guid[1], guid[0], guid[5], guid[4], guid[7], guid[6], guid[8], + guid[9], guid[10], guid[11], guid[12], guid[13], guid[14], guid[15], + ]); + + Ok(DebugId::from_parts(uuid, age)) + } + + /// Constructs a `DebugId` from a PDB 2.0 timestamp and age. + pub fn from_pdb20(timestamp: u32, age: u32) -> Self { + // The big-endian byte-order here has to match the one used to read this number in + // the DebugId::timestamp method. + DebugId { + bytes: [ + (timestamp >> 24) as u8, + (timestamp >> 16) as u8, + (timestamp >> 8) as u8, + timestamp as u8, + 0u8, + 0u8, + 0u8, + 0u8, + 0u8, + 0u8, + 0u8, + 0u8, + 0u8, + 0u8, + 0u8, + 0u8, + ], + appendix: age, + _padding: [0u8; 11], + typ: 1u8, + } + } + + /// Parses a breakpad identifier from a string. + pub fn from_breakpad(string: &str) -> Result<Self, ParseDebugIdError> { + let options = ParseOptions { + allow_hyphens: false, + require_appendix: true, + allow_tail: false, + }; + Self::parse_str(string, options).ok_or(ParseDebugIdError) + } + + /// Returns the UUID part of the code module's debug_identifier. + /// + /// If this is a debug identifier for the PDB 2.0 format an invalid UUID is returned + /// where only the first 4 bytes are filled in and the remainder of the bytes are 0. + /// This means the UUID has variant [`uuid::Variant::NCS`] and an unknown version, + /// [`Uuid::get_version`] will return `None`, which is not a valid UUID. + /// + /// This may seem odd however does seem reasonable: + /// + /// - Every [`DebugId`] can be represented as [`Uuid`] and will still mostly look + /// reasonable e.g. in comparisons etc. + /// - The PDB 2.0 format is very old and very unlikely to appear practically. + pub fn uuid(&self) -> Uuid { + Uuid::from_bytes(self.bytes) + } + + /// Returns the appendix part of the code module's debug identifier. + /// + /// On Windows, this is an incrementing counter to identify the build. + /// On all other platforms, this value will always be zero. + pub fn appendix(&self) -> u32 { + self.appendix + } + + /// Returns whether this identifier is nil, i.e. it consists only of zeros. + pub fn is_nil(&self) -> bool { + self.bytes == [0u8; 16] && self.appendix == 0 + } + + /// Returns whether this identifier is from the PDB 2.0 format. + pub fn is_pdb20(&self) -> bool { + self.typ == 1 + } + + /// Returns a wrapper which when formatted via `fmt::Display` will format a + /// a breakpad identifier. + pub fn breakpad(&self) -> BreakpadFormat<'_> { + BreakpadFormat { inner: self } + } + + fn parse_str(string: &str, options: ParseOptions) -> Option<Self> { + let is_hyphenated = string.get(8..9) == Some("-"); + if is_hyphenated && !options.allow_hyphens || !string.is_ascii() { + return None; + } + + // Can the PDB 2.0 format match? This can never be true for a valid UUID. + let min_len = if is_hyphenated { 10 } else { 9 }; + let max_len = if is_hyphenated { 17 } else { 16 }; + if min_len <= string.len() && string.len() <= max_len { + let timestamp_str = string.get(..8)?; + let timestamp = u32::from_str_radix(timestamp_str, 16).ok()?; + let appendix_str = match is_hyphenated { + true => string.get(9..)?, + false => string.get(8..)?, + }; + let appendix = u32::from_str_radix(appendix_str, 16).ok()?; + return Some(Self::from_pdb20(timestamp, appendix)); + } + + let uuid_len = if is_hyphenated { 36 } else { 32 }; + let uuid = string.get(..uuid_len)?.parse().ok()?; + if !options.require_appendix && string.len() == uuid_len { + return Some(Self::from_parts(uuid, 0)); + } + + let mut appendix_str = &string[uuid_len..]; + if is_hyphenated ^ appendix_str.starts_with('-') { + return None; // Require a hyphen if and only if we're hyphenated. + } else if is_hyphenated { + appendix_str = &appendix_str[1..]; // Skip the hyphen for parsing. + } + + if options.allow_tail && appendix_str.len() > 8 { + appendix_str = &appendix_str[..8]; + } + + // Parse the appendix, which fails on empty strings. + let appendix = u32::from_str_radix(appendix_str, 16).ok()?; + Some(Self::from_parts(uuid, appendix)) + } + + /// Returns the PDB 2.0 timestamp. + /// + /// Only valid if you know this is a PDB 2.0 debug identifier. + fn timestamp(&self) -> u32 { + u32::from_be_bytes([self.bytes[0], self.bytes[1], self.bytes[2], self.bytes[3]]) + } +} + +impl fmt::Debug for DebugId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let uuid = self.uuid(); + f.debug_struct("DebugId") + .field("uuid", &uuid.hyphenated().to_string()) + .field("appendix", &self.appendix()) + .finish() + } +} + +impl fmt::Display for DebugId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.is_pdb20() { + true => { + let timestamp = self.timestamp(); + write!(f, "{:08X}", timestamp)?; + } + false => { + let uuid = self.uuid(); + uuid.fmt(f)?; + } + } + if self.appendix > 0 { + write!(f, "-{:x}", { self.appendix })?; + } + Ok(()) + } +} + +impl str::FromStr for DebugId { + type Err = ParseDebugIdError; + + fn from_str(string: &str) -> Result<Self, ParseDebugIdError> { + let options = ParseOptions { + allow_hyphens: true, + require_appendix: false, + allow_tail: true, + }; + Self::parse_str(string, options).ok_or(ParseDebugIdError) + } +} + +impl From<Uuid> for DebugId { + fn from(uuid: Uuid) -> Self { + DebugId::from_uuid(uuid) + } +} + +impl From<(Uuid, u32)> for DebugId { + fn from(tuple: (Uuid, u32)) -> Self { + let (uuid, appendix) = tuple; + DebugId::from_parts(uuid, appendix) + } +} + +/// Wrapper around [`DebugId`] for Breakpad formatting. +/// +/// **Example:** +/// +/// ``` +/// # extern crate debugid; +/// use std::str::FromStr; +/// use debugid::DebugId; +/// +/// # fn foo() -> Result<(), debugid::ParseDebugIdError> { +/// let id = DebugId::from_breakpad("DFB8E43AF2423D73A453AEB6A777EF75a")?; +/// assert_eq!("DFB8E43AF2423D73A453AEB6A777EF75a".to_string(), id.breakpad().to_string()); +/// # Ok(()) +/// # } +/// +/// # fn main() { foo().unwrap() } +/// ``` +/// +/// [`DebugId`]: struct.DebugId.html +#[derive(Debug)] +pub struct BreakpadFormat<'a> { + inner: &'a DebugId, +} + +impl<'a> fmt::Display for BreakpadFormat<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.inner.is_pdb20() { + true => { + let timestamp = self.inner.timestamp(); + write!(f, "{:08X}{:x}", timestamp, self.inner.appendix()) + } + false => { + let uuid = self.inner.uuid(); + write!(f, "{:X}{:x}", uuid.simple(), self.inner.appendix()) + } + } + } +} + +/// Indicates an error parsing a [`CodeId`](struct.CodeId.html). +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct ParseCodeIdError; + +impl error::Error for ParseCodeIdError {} + +impl fmt::Display for ParseCodeIdError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "invalid code identifier") + } +} + +/// Unique platform-dependent identifier of code files. +/// +/// This identifier assumes a string representation that depends on the platform and compiler used. +/// The representation only retains hex characters and canonically stores lower case. +/// +/// There are the following known formats: +/// +/// - **MachO UUID**: The unique identifier of a Mach binary, specified in the `LC_UUID` load +/// command header. +/// - **GNU Build ID**: Contents of the `.gnu.build-id` note or section contents formatted as +/// lowercase hex string. +/// - **PE Timestamp**: Timestamp and size of image values from a Windows PE header. The size of +/// image value is truncated, so the length of the `CodeId` might not be a multiple of 2. +#[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct CodeId { + inner: String, +} + +impl CodeId { + /// Constructs an empty code identifier. + pub fn nil() -> Self { + Self::default() + } + + /// Constructs a `CodeId` from its string representation. + pub fn new(mut string: String) -> Self { + string.retain(|c| c.is_ascii_hexdigit()); + string.make_ascii_lowercase(); + CodeId { inner: string } + } + + /// Constructs a `CodeId` from a binary slice. + pub fn from_binary(slice: &[u8]) -> Self { + let mut string = String::with_capacity(slice.len() * 2); + + for byte in slice { + write!(&mut string, "{:02x}", byte).expect(""); + } + + Self::new(string) + } + + /// Returns whether this identifier is nil, i.e. it is empty. + pub fn is_nil(&self) -> bool { + self.inner.is_empty() + } + + /// Returns the string representation of this code identifier. + pub fn as_str(&self) -> &str { + self.inner.as_str() + } +} + +impl fmt::Display for CodeId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.inner) + } +} + +impl fmt::Debug for CodeId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "CodeId({})", self) + } +} + +impl From<String> for CodeId { + fn from(string: String) -> Self { + Self::new(string) + } +} + +impl From<&'_ str> for CodeId { + fn from(string: &str) -> Self { + Self::new(string.into()) + } +} + +impl AsRef<str> for CodeId { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl str::FromStr for CodeId { + type Err = ParseCodeIdError; + + fn from_str(string: &str) -> Result<Self, ParseCodeIdError> { + Ok(Self::new(string.into())) + } +} + +#[cfg(feature = "serde")] +mod serde_support { + use serde::de::{self, Deserialize, Deserializer, Unexpected, Visitor}; + use serde::ser::{Serialize, Serializer}; + + use super::*; + + impl Serialize for CodeId { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + serializer.serialize_str(self.as_str()) + } + } + + impl<'de> Deserialize<'de> for CodeId { + fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { + let string = String::deserialize(deserializer)?; + Ok(CodeId::new(string)) + } + } + + impl<'de> Deserialize<'de> for DebugId { + fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { + struct V; + + impl<'de> Visitor<'de> for V { + type Value = DebugId; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("DebugId") + } + + fn visit_str<E: de::Error>(self, value: &str) -> Result<DebugId, E> { + value + .parse() + .map_err(|_| de::Error::invalid_value(Unexpected::Str(value), &self)) + } + } + + deserializer.deserialize_str(V) + } + } + + impl Serialize for DebugId { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + serializer.serialize_str(&self.to_string()) + } + } +} diff --git a/third_party/rust/debugid/tests/test_codeid.rs b/third_party/rust/debugid/tests/test_codeid.rs new file mode 100644 index 0000000000..62cf8d02d9 --- /dev/null +++ b/third_party/rust/debugid/tests/test_codeid.rs @@ -0,0 +1,27 @@ +use debugid::CodeId; + +#[test] +fn test_new() { + let id = CodeId::new("dfb8e43af2423d73a453aeb6a777ef75".into()); + assert_eq!(id.as_str(), "dfb8e43af2423d73a453aeb6a777ef75"); +} + +#[test] +fn test_pe() { + // Code identifiers of PE files might have an odd length and contain upper case characters. + let id = CodeId::new("5CCC38584b08000".into()); + assert_eq!(id.as_str(), "5ccc38584b08000"); +} + +#[test] +fn test_from_binary() { + let binary = b"\xdf\xb8\xe4\x3a\xf2\x42\x3d\x73\xa4\x53\xae\xb6\xa7\x77\xef\x75"; + let id = CodeId::from_binary(&binary[..]); + assert_eq!(id.as_str(), "dfb8e43af2423d73a453aeb6a777ef75"); +} + +#[test] +fn test_is_nil() { + let id = CodeId::nil(); + assert!(id.is_nil()); +} diff --git a/third_party/rust/debugid/tests/test_debugid.rs b/third_party/rust/debugid/tests/test_debugid.rs new file mode 100644 index 0000000000..1944e59754 --- /dev/null +++ b/third_party/rust/debugid/tests/test_debugid.rs @@ -0,0 +1,388 @@ +use std::mem::{align_of, size_of}; +use std::str::FromStr; + +use debugid::DebugId; +use uuid::Uuid; + +#[test] +fn test_is_nil() { + assert!(DebugId::default().is_nil()); +} + +#[test] +fn test_parse_zero() { + assert_eq!( + DebugId::from_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75").unwrap(), + DebugId::from_parts( + Uuid::parse_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75").unwrap(), + 0, + ) + ); +} + +#[test] +fn test_parse_short() { + assert_eq!( + DebugId::from_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75-a").unwrap(), + DebugId::from_parts( + Uuid::parse_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75").unwrap(), + 0xa, + ) + ); +} + +#[test] +fn test_parse_long() { + assert_eq!( + DebugId::from_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75-feedface").unwrap(), + DebugId::from_parts( + Uuid::parse_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75").unwrap(), + 0xfeed_face, + ) + ); +} + +#[test] +fn test_parse_compact() { + assert_eq!( + DebugId::from_str("dfb8e43af2423d73a453aeb6a777ef75feedface").unwrap(), + DebugId::from_parts( + Uuid::parse_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75").unwrap(), + 0xfeed_face, + ) + ); +} + +#[test] +fn test_parse_upper() { + assert_eq!( + DebugId::from_str("DFB8E43A-F242-3D73-A453-AEB6A777EF75-FEEDFACE").unwrap(), + DebugId::from_parts( + Uuid::parse_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75").unwrap(), + 0xfeed_face, + ) + ); +} + +#[test] +fn test_parse_ignores_tail() { + assert_eq!( + DebugId::from_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75-feedface-1-2-3").unwrap(), + DebugId::from_parts( + Uuid::parse_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75").unwrap(), + 0xfeed_face, + ) + ); +} + +#[test] +fn test_to_string_zero() { + let id = DebugId::from_parts( + Uuid::parse_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75").unwrap(), + 0, + ); + + assert_eq!(id.to_string(), "dfb8e43a-f242-3d73-a453-aeb6a777ef75"); +} + +#[test] +fn test_to_string_short() { + let id = DebugId::from_parts( + Uuid::parse_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75").unwrap(), + 10, + ); + + assert_eq!(id.to_string(), "dfb8e43a-f242-3d73-a453-aeb6a777ef75-a"); +} + +#[test] +fn test_to_string_long() { + let id = DebugId::from_parts( + Uuid::parse_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75").unwrap(), + 0xfeed_face, + ); + + assert_eq!( + id.to_string(), + "dfb8e43a-f242-3d73-a453-aeb6a777ef75-feedface" + ); +} + +#[test] +fn test_parse_error_short() { + assert!(DebugId::from_str("dfb8e43a-f242-3d73-a453-aeb6a777ef7").is_err()); +} + +#[test] +fn test_parse_error_trailing_dash() { + assert!(DebugId::from_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75-").is_err()); +} + +#[test] +fn test_parse_error_unicode() { + assert!(DebugId::from_str("아이쿱 조합원 앱카드").is_err()); +} + +#[test] +fn test_from_guid_age() { + let guid = [ + 0x98, 0xd1, 0xef, 0xe8, 0x6e, 0xf8, 0xfe, 0x45, 0x9d, 0xdb, 0xe1, 0x13, 0x82, 0xb5, 0xd1, + 0xc9, + ]; + + assert_eq!( + DebugId::from_guid_age(&guid[..], 1).unwrap(), + DebugId::from_str("e8efd198-f86e-45fe-9ddb-e11382b5d1c9-1").unwrap() + ) +} + +#[test] +fn test_parse_breakpad_zero() { + assert_eq!( + DebugId::from_breakpad("DFB8E43AF2423D73A453AEB6A777EF750").unwrap(), + DebugId::from_parts( + Uuid::parse_str("DFB8E43AF2423D73A453AEB6A777EF75").unwrap(), + 0, + ) + ); +} + +#[test] +fn test_parse_breakpad_short() { + assert_eq!( + DebugId::from_breakpad("DFB8E43AF2423D73A453AEB6A777EF75a").unwrap(), + DebugId::from_parts( + Uuid::parse_str("DFB8E43AF2423D73A453AEB6A777EF75").unwrap(), + 10, + ) + ); +} + +#[test] +fn test_parse_breakpad_long() { + assert_eq!( + DebugId::from_breakpad("DFB8E43AF2423D73A453AEB6A777EF75feedface").unwrap(), + DebugId::from_parts( + Uuid::parse_str("DFB8E43AF2423D73A453AEB6A777EF75").unwrap(), + 0xfeed_face, + ) + ); +} + +#[test] +fn test_parse_breakpad_error_tail() { + assert!(DebugId::from_breakpad("DFB8E43AF2423D73A453AEB6A777EF75feedface123").is_err()); +} + +#[test] +fn test_parse_breakpad_error_missing_age() { + assert!(DebugId::from_breakpad("DFB8E43AF2423D73A453AEB6A777EF75").is_err()); +} + +#[test] +fn test_parse_breakpad_error_short() { + assert!(DebugId::from_breakpad("DFB8E43AF2423D73A453AEB6A777EF7").is_err()); +} + +#[test] +fn test_parse_breakpad_error_dashes() { + assert!(DebugId::from_breakpad("e8efd198-f86e-45fe-9ddb-e11382b5d1c9-1").is_err()); +} + +#[test] +fn test_to_string_breakpad_zero() { + let id = DebugId::from_parts( + Uuid::parse_str("DFB8E43AF2423D73A453AEB6A777EF75").unwrap(), + 0, + ); + + assert_eq!( + id.breakpad().to_string(), + "DFB8E43AF2423D73A453AEB6A777EF750" + ); +} + +#[test] +fn test_to_string_breakpad_short() { + let id = DebugId::from_parts( + Uuid::parse_str("DFB8E43AF2423D73A453AEB6A777EF75").unwrap(), + 10, + ); + + assert_eq!( + id.breakpad().to_string(), + "DFB8E43AF2423D73A453AEB6A777EF75a" + ); +} + +#[test] +fn test_to_string_breakpad_long() { + let id = DebugId::from_parts( + Uuid::parse_str("DFB8E43AF2423D73A453AEB6A777EF75").unwrap(), + 0xfeed_face, + ); + + assert_eq!( + id.breakpad().to_string(), + "DFB8E43AF2423D73A453AEB6A777EF75feedface" + ); +} + +#[test] +fn test_debug_id_debug() { + let id = DebugId::from_parts( + Uuid::parse_str("DFB8E43AF2423D73A453AEB6A777EF75").unwrap(), + 10, + ); + + assert_eq!( + format!("{:?}", id), + "DebugId { uuid: \"dfb8e43a-f242-3d73-a453-aeb6a777ef75\", appendix: 10 }" + ); +} + +#[test] +#[cfg(feature = "with_serde")] +fn test_serde_serialize() { + let id = DebugId::from_parts( + Uuid::parse_str("DFB8E43AF2423D73A453AEB6A777EF75").unwrap(), + 10, + ); + + assert_eq!( + serde_json::to_string(&id).expect("could not serialize"), + "\"dfb8e43a-f242-3d73-a453-aeb6a777ef75-a\"" + ) +} + +#[test] +#[cfg(feature = "with_serde")] +fn test_serde_deserialize() { + let id: DebugId = serde_json::from_str("\"dfb8e43a-f242-3d73-a453-aeb6a777ef75-a\"") + .expect("could not deserialize"); + + assert_eq!( + id, + DebugId::from_parts( + Uuid::parse_str("DFB8E43AF2423D73A453AEB6A777EF75").unwrap(), + 10, + ) + ); +} + +#[test] +fn test_pdb20() { + let timestamp: u32 = 0x418e89c3; + let age: u32 = 1; + let debug_id = DebugId::from_pdb20(timestamp, age); + + assert!(debug_id.is_pdb20()); + assert_eq!( + debug_id.uuid(), + Uuid::parse_str("418e89c3-0000-0000-0000-000000000000").unwrap() + ); +} + +#[test] +fn test_pdb20_format() { + let timestamp: u32 = 0x418e89c3; + let age: u32 = 1; + let debug_id = DebugId::from_pdb20(timestamp, age); + + assert_eq!(debug_id.to_string(), "418E89C3-1".to_string()); + assert_eq!(debug_id.breakpad().to_string(), "418E89C31"); +} + +#[test] +fn test_pdb20_parse() { + let timestamp: u32 = 0x418e89c3; + let age: u32 = 1; + let debug_id = DebugId::from_pdb20(timestamp, age); + + let s = "418E89C3-1"; + let parsed = DebugId::from_str(s).unwrap(); + assert_eq!(parsed, debug_id); + + let s = "418E89C31"; + let parsed = DebugId::from_str(s).unwrap(); + assert_eq!(parsed, debug_id); + + let s = "418E89C31"; + let parsed = DebugId::from_breakpad(s).unwrap(); + assert_eq!(parsed, debug_id); + + let s = "418E89C3-1"; + assert!(DebugId::from_breakpad(s).is_err()); +} + +/// The version of `DebugId` up to 0.7.2. +#[repr(C, packed)] +struct OldDebugId { + uuid: Uuid, + appendix: u32, + _padding: [u8; 12], +} + +#[test] +fn test_mem() { + // The size of this struct needs to be exactly aligned and can not change for backwards + // compatibility. + assert_eq!(size_of::<OldDebugId>(), 32); + assert_eq!(size_of::<OldDebugId>(), size_of::<DebugId>()); + assert_eq!(align_of::<DebugId>(), 1); +} + +#[test] +fn test_to_from_raw() { + let debug_id = DebugId::from_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75-a").unwrap(); + + // Create &[u8] from DebugId. + let slice = &[debug_id]; + let ptr = slice.as_ptr() as *const u8; + let len = std::mem::size_of_val(slice); + let buf: &[u8] = unsafe { std::slice::from_raw_parts(ptr, len) }; + + // Copy bytes to new location. + let mut new_buf: Vec<u8> = Vec::new(); + std::io::copy(&mut std::io::Cursor::new(buf), &mut new_buf).unwrap(); + + // Create DebugId from &[u8]. + let ptr = new_buf.as_ptr() as *const DebugId; + let new_debug_id = unsafe { &*ptr }; + + assert_eq!(*new_debug_id, debug_id); +} + +#[test] +fn test_from_old_raw() { + // Ensures we can still read previous in-memory representations into the new format. + let debug_id = DebugId::from_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75-a").unwrap(); + let old_debug_id = OldDebugId { + uuid: debug_id.uuid(), + appendix: debug_id.appendix(), + _padding: [0; 12], + }; + + // Create &[u8] from OldDebugId. + let slice = &[old_debug_id]; + let ptr = slice.as_ptr() as *const u8; + let len = std::mem::size_of_val(slice); + let buf: &[u8] = unsafe { std::slice::from_raw_parts(ptr, len) }; + + // Copy bytes to new location. + let mut new_buf: Vec<u8> = Vec::new(); + std::io::copy(&mut std::io::Cursor::new(buf), &mut new_buf).unwrap(); + + // Create DebugId from &[u8]. + let ptr = new_buf.as_ptr() as *const DebugId; + let new_debug_id = unsafe { &*ptr }; + + assert_eq!(*new_debug_id, debug_id); +} + +#[test] +fn test_default() { + let debug_id = DebugId::default(); + assert_eq!(debug_id.uuid(), Uuid::nil()); + assert_eq!(debug_id.appendix(), 0); +} diff --git a/third_party/rust/debugid/tests/test_serde.rs b/third_party/rust/debugid/tests/test_serde.rs new file mode 100644 index 0000000000..bda92232f9 --- /dev/null +++ b/third_party/rust/debugid/tests/test_serde.rs @@ -0,0 +1,44 @@ +#![cfg(feature = "serde")] + +use debugid::{CodeId, DebugId}; +use uuid::Uuid; + +#[test] +fn test_deserialize_debugid() { + assert_eq!( + DebugId::from_parts( + Uuid::parse_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75").unwrap(), + 0, + ), + serde_json::from_str("\"dfb8e43a-f242-3d73-a453-aeb6a777ef75\"").unwrap(), + ); +} + +#[test] +fn test_serialize_debugid() { + let id = DebugId::from_parts( + Uuid::parse_str("dfb8e43a-f242-3d73-a453-aeb6a777ef75").unwrap(), + 0, + ); + + assert_eq!( + "\"dfb8e43a-f242-3d73-a453-aeb6a777ef75\"", + serde_json::to_string(&id).unwrap(), + ); +} + +#[test] +fn test_deserialize_codeid() { + assert_eq!( + CodeId::new("dfb8e43af2423d73a453aeb6a777ef75".into()), + serde_json::from_str("\"dfb8e43af2423d73a453aeb6a777ef75\"").unwrap(), + ); +} + +#[test] +fn test_serialize_codeid() { + assert_eq!( + "\"dfb8e43af2423d73a453aeb6a777ef75\"", + serde_json::to_string(&CodeId::new("dfb8e43af2423d73a453aeb6a777ef75".into())).unwrap(), + ); +} |