Skip to content

Commit 834b6d9

Browse files
committed
feat: simplify hex-display
1 parent 6576c83 commit 834b6d9

File tree

5 files changed

+51
-18
lines changed

5 files changed

+51
-18
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ jobs:
4343
- macos-latest
4444
- windows-latest
4545
rust:
46-
- "1.86"
47-
- "1.87"
4846
- "1.88"
4947
- "1.89"
5048
- "1.90"

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ readme = "README.md"
1515
keywords = ["git", "git-remote", "codecommit"]
1616
categories = ["command-line-utilities", "development-tools"]
1717
publish = true
18-
rust-version = "1.86"
18+
rust-version = "1.88"
1919

2020
[workspace.lints.rust]
2121
let_underscore_drop = "warn"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Actively used on Windows, macOS, and low-resource Linux environments.
3939

4040
### MSRV
4141

42-
The minimum supported Rust version is `1.86`.
42+
The minimum supported Rust version is `1.88`.
4343

4444
## Install
4545

clippy.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# See https://doc.rust-lang.org/clippy/lint_configuration.html
22
# for full configuration options.
33

4-
msrv = "1.86"
4+
msrv = "1.88"
Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,56 @@
1-
pub struct HexDisplay<T>(T);
1+
pub struct HexDisplay([u8; 32]);
22

3-
impl<T: AsRef<[u8]>> core::fmt::Display for HexDisplay<T> {
3+
impl core::fmt::Debug for HexDisplay {
44
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
5-
for b in self.0.as_ref() {
6-
write!(f, "{b:02x}")?;
7-
}
8-
Ok(())
5+
std::fmt::Display::fmt(self, f)
6+
}
7+
}
8+
9+
impl core::fmt::Display for HexDisplay {
10+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
11+
// this implementation avoids any heap allocations and is optimized by
12+
// the compiler to use vectorized instructions where available.
13+
//
14+
// with O3, the loop is unrolled and vectorized to use SIMD instructions
15+
//
16+
// https://rust.godbolt.org/z/seM19zEfv
17+
18+
let mut buf = [0u8; 64];
19+
// SAFETY: 64 is evenly divisible by 2
20+
unsafe { buf.as_chunks_unchecked_mut::<2>() }
21+
.iter_mut()
22+
.zip(self.0.as_ref())
23+
.for_each(|(slot, &byte)| {
24+
*slot = byte_to_hex(byte);
25+
});
26+
27+
// SAFETY: buf only contains valid ASCII hex characters
28+
let buf = unsafe { core::str::from_utf8_unchecked(&buf) };
29+
30+
f.pad(buf)
931
}
1032
}
1133

12-
pub trait HexDisplayExt: AsRef<[u8]> {
13-
fn hex_display(self) -> HexDisplay<Self>
14-
where
15-
Self: Sized,
16-
{
17-
HexDisplay(self)
34+
const fn byte_to_hex(byte: u8) -> [u8; 2] {
35+
const unsafe fn nibble_to_hex(nibble: u8) -> u8 {
36+
match nibble {
37+
0..=9 => b'0' + nibble,
38+
10..=15 => b'a' + (nibble - 10),
39+
// SAFETY: invariant held by caller that nibble is in 0..=15
40+
_ => unsafe { core::hint::unreachable_unchecked() },
41+
}
1842
}
43+
44+
// SAFETY: shifting and masking ensures nibble is in 0..=15 for both calls
45+
unsafe { [nibble_to_hex(byte >> 4), nibble_to_hex(byte & 0x0F)] }
1946
}
2047

21-
impl<T: AsRef<[u8]>> HexDisplayExt for T {}
48+
pub trait HexDisplayExt {
49+
fn hex_display(self) -> HexDisplay;
50+
}
51+
52+
impl<T: Into<[u8; 32]>> HexDisplayExt for T {
53+
fn hex_display(self) -> HexDisplay {
54+
HexDisplay(self.into())
55+
}
56+
}

0 commit comments

Comments
 (0)