Skip to content

Commit

Permalink
Merge branch 'faster-hex'
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Aug 16, 2023
2 parents 9f4dfe0 + 145125a commit 4a4fa0f
Show file tree
Hide file tree
Showing 14 changed files with 80 additions and 58 deletions.
16 changes: 12 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion gix-hash/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ serde= ["dep:serde"]

[dependencies]
thiserror = "1.0.33"
hex = "0.4.2"
faster-hex = "0.8.0"
serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] }

document-features = { version = "0.2.0", optional = true }
Expand Down
25 changes: 14 additions & 11 deletions gix-hash/src/object_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ pub mod decode {
pub enum Error {
#[error("A hash sized {0} hexadecimal characters is invalid")]
InvalidHexEncodingLength(usize),
#[error("Invalid character {c} at position {index}")]
Invalid { c: char, index: usize },
#[error("Invalid character encountered")]
Invalid,
}

/// Hash decoding
Expand All @@ -50,16 +50,19 @@ pub mod decode {
///
/// Such a buffer can be obtained using [`oid::write_hex_to(buffer)`][super::oid::write_hex_to()]
pub fn from_hex(buffer: &[u8]) -> Result<ObjectId, Error> {
use hex::FromHex;
match buffer.len() {
40 => Ok(ObjectId::Sha1(<[u8; 20]>::from_hex(buffer).map_err(
|err| match err {
hex::FromHexError::InvalidHexCharacter { c, index } => Error::Invalid { c, index },
hex::FromHexError::OddLength | hex::FromHexError::InvalidStringLength => {
unreachable!("BUG: This is already checked")
}
},
)?)),
40 => Ok({
ObjectId::Sha1({
let mut buf = [0; 20];
faster_hex::hex_decode(buffer, &mut buf).map_err(|err| match err {
faster_hex::Error::InvalidChar => Error::Invalid,
faster_hex::Error::InvalidLength(_) => {
unreachable!("BUG: This is already checked")
}
})?;
buf
})
}),
len => Err(Error::InvalidHexEncodingLength(len)),
}
}
Expand Down
2 changes: 1 addition & 1 deletion gix-hash/src/oid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ impl oid {
#[must_use]
pub fn hex_to_buf(&self, buf: &mut [u8]) -> usize {
let num_hex_bytes = self.bytes.len() * 2;
hex::encode_to_slice(&self.bytes, &mut buf[..num_hex_bytes]).expect("to count correctly");
faster_hex::hex_encode(&self.bytes, &mut buf[..num_hex_bytes]).expect("to count correctly");
num_hex_bytes
}

Expand Down
17 changes: 10 additions & 7 deletions gix-hash/src/prefix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ pub mod from_hex {
TooShort { hex_len: usize },
#[error("An id cannot be larger than {} chars in hex, but {hex_len} was requested", crate::Kind::longest().len_in_hex())]
TooLong { hex_len: usize },
#[error("Invalid character {c} at position {index}")]
Invalid { c: char, index: usize },
#[error("Invalid hex character")]
Invalid,
}
}

Expand Down Expand Up @@ -95,7 +95,6 @@ impl Prefix {

/// Create an instance from the given hexadecimal prefix `value`, e.g. `35e77c16` would yield a `Prefix` with `hex_len()` = 8.
pub fn from_hex(value: &str) -> Result<Self, from_hex::Error> {
use hex::FromHex;
let hex_len = value.len();

if hex_len > crate::Kind::longest().len_in_hex() {
Expand All @@ -105,16 +104,20 @@ impl Prefix {
};

let src = if value.len() % 2 == 0 {
Vec::from_hex(value)
let mut out = Vec::from_iter(std::iter::repeat(0).take(value.len() / 2));
faster_hex::hex_decode(value.as_bytes(), &mut out).map(move |_| out)
} else {
// TODO(perf): do without heap allocation here.
let mut buf = [0u8; crate::Kind::longest().len_in_hex()];
buf[..value.len()].copy_from_slice(value.as_bytes());
buf[value.len()] = b'0';
Vec::from_hex(&buf[..=value.len()])
let src = &buf[..=value.len()];
let mut out = Vec::from_iter(std::iter::repeat(0).take(src.len() / 2));
faster_hex::hex_decode(src, &mut out).map(move |_| out)
}
.map_err(|e| match e {
hex::FromHexError::InvalidHexCharacter { c, index } => from_hex::Error::Invalid { c, index },
hex::FromHexError::OddLength | hex::FromHexError::InvalidStringLength => panic!("This is already checked"),
faster_hex::Error::InvalidChar => from_hex::Error::Invalid,
faster_hex::Error::InvalidLength(_) => panic!("This is already checked"),
})?;

let mut bytes = ObjectId::null(crate::Kind::from_hex_len(value.len()).expect("hex-len is already checked"));
Expand Down
2 changes: 1 addition & 1 deletion gix-hash/tests/object_id/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mod from_hex {
fn non_hex_characters() {
assert!(matches!(
ObjectId::from_hex(b"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz").unwrap_err(),
decode::Error::Invalid { index: 0, c: 'z' }
decode::Error::Invalid
));
}

Expand Down
2 changes: 1 addition & 1 deletion gix-hash/tests/prefix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ mod try_from {
#[test]
fn invalid_chars() {
let input = "abcdfOsd";
let expected = Error::Invalid { c: 'O', index: 5 };
let expected = Error::Invalid;
let actual = Prefix::try_from(input).unwrap_err();
assert_eq!(actual, expected);
}
Expand Down
1 change: 0 additions & 1 deletion gix-object/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ gix-date = { version = "^0.7.2", path = "../gix-date" }
btoi = "0.4.2"
itoa = "1.0.1"
thiserror = "1.0.34"
hex = "0.4.2"
bstr = { version = "1.3.0", default-features = false, features = ["std", "unicode"] }
nom = { version = "7", default-features = false, features = ["std"]}
smallvec = { version = "1.4.0", features = ["write"] }
Expand Down
2 changes: 1 addition & 1 deletion gix-packetline-blocking/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ serde = ["dep:serde", "bstr/serde"]
[dependencies]
serde = { version = "1.0.114", optional = true, default-features = false, features = ["std", "derive"]}
thiserror = "1.0.34"
hex = "0.4.2"
faster-hex = "0.8.0"
bstr = { version = "1.3.0", default-features = false, features = ["std"] }

document-features = { version = "0.2.0", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion gix-packetline/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ required-features = ["blocking-io", "maybe-async/is_sync"]
[dependencies]
serde = { version = "1.0.114", optional = true, default-features = false, features = ["std", "derive"]}
thiserror = "1.0.34"
hex = "0.4.2"
faster-hex = "0.8.0"
bstr = { version = "1.3.0", default-features = false, features = ["std"] }
# async support
futures-io = { version = "0.3.16", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion gix-packetline/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ pub fn hex_prefix(four_bytes: &[u8]) -> Result<PacketLineOrWantedSize<'_>, Error
}

let mut buf = [0u8; U16_HEX_BYTES / 2];
hex::decode_to_slice(four_bytes, &mut buf).map_err(|err| Error::HexDecode { err: err.to_string() })?;
faster_hex::hex_decode(four_bytes, &mut buf).map_err(|err| Error::HexDecode { err: err.to_string() })?;
let wanted_bytes = u16::from_be_bytes(buf);

if wanted_bytes == 3 {
Expand Down
2 changes: 1 addition & 1 deletion gix-packetline/src/encode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ pub use blocking_io::*;

pub(crate) fn u16_to_hex(value: u16) -> [u8; 4] {
let mut buf = [0u8; 4];
hex::encode_to_slice(value.to_be_bytes(), &mut buf).expect("two bytes to 4 hex chars never fails");
faster_hex::hex_encode(&value.to_be_bytes(), &mut buf).expect("two bytes to 4 hex chars never fails");
buf
}
2 changes: 1 addition & 1 deletion gix-packetline/tests/decode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ mod streaming {
fn error_on_invalid_hex() {
assert_err_display(
streaming(b"fooo"),
"Failed to decode the first four hex bytes indicating the line length: Invalid character 'o' at position 1",
"Failed to decode the first four hex bytes indicating the line length: Invalid character",
);
}

Expand Down
61 changes: 35 additions & 26 deletions gix-revision/fuzz/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4a4fa0f

Please sign in to comment.