Files
aria2-rust-pro/crates/aria2-rust-pro-protocol/src/torrent/utils.rs
T

70 lines
2.3 KiB
Rust

use super::bencode::BencodeValue;
/// Decodes a 40-character hexadecimal string into a fixed-width 20-byte array.
pub(super) fn decode_hex_20_array(input: &str) -> Result<[u8; 20], String> {
if input.len() != 40 {
return Err(format!(
"expected 40 hex characters for 20-byte info-hash, got {}",
input.len()
));
}
let mut out = [0_u8; 20];
for (index, chunk) in input.as_bytes().chunks_exact(2).enumerate() {
let hi = decode_hex_nibble(chunk[0])
.ok_or_else(|| "info-hash contains non-hex characters".to_owned())?;
let lo = decode_hex_nibble(chunk[1])
.ok_or_else(|| "info-hash contains non-hex characters".to_owned())?;
out[index] = (hi << 4) | lo;
}
Ok(out)
}
/// Decodes one ASCII hexadecimal nibble.
const fn decode_hex_nibble(byte: u8) -> Option<u8> {
match byte {
b'0'..=b'9' => Some(byte - b'0'),
b'a'..=b'f' => Some(byte - b'a' + 10),
b'A'..=b'F' => Some(byte - b'A' + 10),
_ => None,
}
}
/// Decodes torrent byte strings into owned lossy UTF-8 text.
pub(super) fn bytes_to_string(bytes: &[u8]) -> String {
String::from_utf8_lossy(bytes).into_owned()
}
/// Converts one torrent bencode value into human-readable string form.
pub(super) fn value_to_string(value: &BencodeValue) -> String {
match value {
BencodeValue::Bytes(bytes) => bytes_to_string(bytes),
BencodeValue::Int(value) => value.to_string(),
BencodeValue::List(values) => values
.iter()
.map(value_to_string)
.collect::<Vec<_>>()
.join(","),
BencodeValue::Dict(map) => map
.iter()
.map(|(key, value)| format!("{key}={}", value_to_string(value)))
.collect::<Vec<_>>()
.join("&"),
}
}
/// Hex-encodes raw torrent bytes using lowercase hexadecimal.
pub(super) fn hex_encode(bytes: &[u8]) -> String {
const HEX: &[u8; 16] = b"0123456789abcdef";
let mut out = String::with_capacity(bytes.len() * 2);
for &byte in bytes {
out.push(char::from(HEX[usize::from(byte >> 4)]));
out.push(char::from(HEX[usize::from(byte & 0x0f)]));
}
out
}
/// Saturates an integer-like torrent length field into `u64`.
pub(super) fn i64_to_u64(value: i64) -> u64 {
u64::try_from(value.max(0)).unwrap_or(u64::MAX)
}