add nix_hash

This commit is contained in:
Artemis Tosini 2024-07-13 21:36:33 +00:00
parent af8b7b2384
commit e4826c62df
Signed by: artemist
GPG key ID: EE5227935FE3FF18
3 changed files with 87 additions and 0 deletions

16
rust/nix_hash/Cargo.lock generated Normal file
View file

@ -0,0 +1,16 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "data-encoding"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
[[package]]
name = "nix_hash"
version = "0.1.0"
dependencies = [
"data-encoding",
]

7
rust/nix_hash/Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[package]
name = "nix_hash"
version = "0.1.0"
edition = "2021"
[dependencies]
data-encoding = "2.6.0"

64
rust/nix_hash/src/main.rs Normal file
View file

@ -0,0 +1,64 @@
use data_encoding::BitOrder;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut binary = data_encoding::HEXLOWER
.decode(b"3a99595f4daedd5f52d646e413ea1c989abb910cd2ea18fd98ccddeab68f0883")?;
let mut spec = data_encoding::Specification::new();
spec.symbols.push_str("0123456789abcdfghijklmnpqrsvwxyz");
spec.bit_order = BitOrder::LeastSignificantFirst;
println!(
"Found: {}",
spec.encoding()?
.encode(&binary)
.chars()
.rev()
.collect::<String>()
);
println!("Expected: 10q8iyvfmpfck3yiisnj1j8vp6lq3km17r26sr95zpdf9mgmk69s");
println!("Found: {}", encode_nix32(&binary));
Ok(())
}
static NIX32_CHARS: &[u8; 32] = b"0123456789abcdfghijklmnpqrsvwxyz";
fn encode_nix32(input: &[u8]) -> String {
let length = if input.len() == 0 {
0
} else {
// ceil(input.len() * 8 / 5)
(input.len() * 8 - 1) / 5 + 1
};
let mut output = String::with_capacity(length);
// nix32 hashes feel like they're a bug that stuck
// The output is backwards and bits are grouped
// from the least significant bit in each byte
// instead of the most significant bit.
// e.g. encoding "Meow" gives us:
// Char: M (0x4d) e (0x65) o (0x6f) w (0x77)
// Value: 0 1 0 0 1 1 0 1 | 0 1 1 0 0 1 0 1 | 0 1 1 0 1 1 1 1 | 0 1 1 1 0 1 1 1
// Out No.: 5 5 5 6 6 6 6 6 | 3 4 4 4 4 4 5 5 | 2 2 2 2 3 3 3 3 | 0 0 1 1 1 1 1 2
// Out Bit: 2 1 0 4 3 2 1 0 | 0 5 4 3 2 1 4 3 | 3 2 1 0 4 3 2 1 | 1 0 4 3 2 1 0 4
//
// where "Out No." is the index of the output charater responsible for a given bit,
// and 2**"Out Bit" is the value of a given bit for its output character.
//
// In this example, characters 0 to 6 have values
// 0x01, 0x1b, 0x16, 0x1e, 0x19, 0xa, 0xd.
// Indexing into the alphabet gives us "1vnyrad"
for char_no in 0..length {
let bit_no = (length - char_no - 1) * 5;
let byte_no = bit_no / 8;
let offset = bit_no % 8;
let next_byte = input.get(byte_no + 1).unwrap_or(&0);
let value = (input[byte_no] as u16 >> offset) | ((*next_byte as u16) << (8 - offset));
output.push(NIX32_CHARS[(value & 0x1f) as usize] as char);
}
output
}