Skip to content

Commit d1532cc

Browse files
authored
Strengthen short finish (#229)
* Add more tests * Fix two problems with the short_finish: * If on ARM where the xor happens first the same value cannot be used for both parameters. * XOR the two halfs of the output to avoid the possibility that two bit diffs can cancel * Fix test error * Add aarch64 nightly to github actions Signed-off-by: Tom Kaitchuck <Tom.Kaitchuck@gmail.com>
1 parent cd5000a commit d1532cc

File tree

4 files changed

+120
-15
lines changed

4 files changed

+120
-15
lines changed

.github/workflows/rust.yml

+20
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,26 @@ jobs:
8080
toolchain: 1.72.0
8181
targets: aarch64-apple-darwin
8282
- run: cargo +1.72.0 check --target aarch64-apple-darwin
83+
nightly:
84+
name: Aarch64 nightly
85+
runs-on: macos-14
86+
env:
87+
RUSTFLAGS: -C target-cpu=native
88+
steps:
89+
- uses: actions/checkout@v4
90+
- name: Install latest nightly
91+
uses: dtolnay/rust-toolchain@master
92+
with:
93+
toolchain: nightly
94+
components: clippy
95+
- name: check nightly
96+
run: cargo check -Z msrv-policy
97+
- name: test nightly
98+
run: cargo test
99+
- name: check serde
100+
run: cargo check --features serde
101+
- name: test serde
102+
run: cargo test --features serde
83103
# aarch64-debug:
84104
# name: Debug Apple
85105
# runs-on: macos-14

src/aes_hash.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ impl AHasher {
9797
#[inline]
9898
#[cfg(feature = "specialize")]
9999
fn short_finish(&self) -> u64 {
100-
let combined = aesenc(self.sum, self.enc);
101-
let result: [u64; 2] = aesdec(combined, combined).convert();
102-
result[0]
100+
let combined = aesdec(self.enc, self.sum);
101+
let result: [u64; 2] = aesdec(combined, self.key).convert();
102+
result[0] ^ result[1]
103103
}
104104

105105
#[inline]

src/hash_quality_test.rs

+95-9
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ fn test_keys_change_output<T: Hasher>(constructor: impl Fn(u128, u128) -> T) {
106106
}
107107

108108
fn test_input_affect_every_byte<T: Hasher>(constructor: impl Fn(u128, u128) -> T) {
109-
let base = hash_with(&0, constructor(0, 0));
109+
let base = hash_with(&0_u128, constructor(0, 0));
110110
for shift in 0..16 {
111111
let mut alternatives = vec![];
112112
for v in 0..256 {
@@ -257,7 +257,6 @@ fn test_single_bit_flip<T: Hasher>(hasher: impl Fn() -> T) {
257257
let compare_value = hash(&0u128, &hasher);
258258
for pos in 0..size {
259259
let test_value = hash(&(1u128 << pos), &hasher);
260-
dbg!(compare_value, test_value);
261260
assert_sufficiently_different(compare_value, test_value, 2);
262261
}
263262
}
@@ -330,10 +329,14 @@ fn test_padding_doesnot_collide<T: Hasher>(hasher: impl Fn() -> T) {
330329
fn test_length_extension<T: Hasher>(hasher: impl Fn(u128, u128) -> T) {
331330
for key in 0..256 {
332331
let h1 = hasher(key, key);
333-
let v1 = hash_with(&[0_u8, 0, 0, 0, 0, 0, 0, 0], h1);
334-
let h2 = hasher(key, key);
335-
let v2 = hash_with(&[1_u8, 0, 0, 0, 0, 0, 0, 0, 0], h2);
336-
assert_ne!(v1, v2);
332+
let v1 = vec![0_u8; 1];
333+
let o1 = hash_with(&v1, h1);
334+
for len in 2..256 {
335+
let h2 = hasher(key, key);
336+
let v2 = vec![0; len];
337+
let o2 = hash_with(&v2, h2);
338+
assert_ne!(o1, o2);
339+
}
337340
}
338341
}
339342

@@ -371,6 +374,8 @@ mod fallback_tests {
371374

372375
#[test]
373376
fn fallback_single_bit_flip() {
377+
#[cfg(feature = "specialize")]
378+
test_single_bit_flip(|| AHasherFixed(AHasher::new_with_keys(0, 0)));
374379
test_single_bit_flip(|| AHasher::new_with_keys(0, 0))
375380
}
376381

@@ -381,16 +386,22 @@ mod fallback_tests {
381386

382387
#[test]
383388
fn fallback_all_bytes_matter() {
389+
#[cfg(feature = "specialize")]
390+
test_all_bytes_matter(|| AHasherStr(AHasher::new_with_keys(0, 0)));
384391
test_all_bytes_matter(|| AHasher::new_with_keys(0, 0));
385392
}
386393

387394
#[test]
388395
fn fallback_test_no_pair_collisions() {
396+
#[cfg(feature = "specialize")]
397+
test_no_pair_collisions(|| AHasherFixed(AHasher::new_with_keys(0, 0)));
389398
test_no_pair_collisions(|| AHasher::new_with_keys(0, 0));
390399
}
391400

392401
#[test]
393402
fn fallback_test_no_full_collisions() {
403+
#[cfg(feature = "specialize")]
404+
test_no_full_collisions(|| AHasherStr(AHasher::new_with_keys(0, 0)));
394405
test_no_full_collisions(|| AHasher::new_with_keys(0, 0));
395406
}
396407

@@ -401,6 +412,8 @@ mod fallback_tests {
401412

402413
#[test]
403414
fn fallback_input_affect_every_byte() {
415+
#[cfg(feature = "specialize")]
416+
test_input_affect_every_byte(|a, b| AHasherFixed(AHasher::new_with_keys(a, b)));
404417
test_input_affect_every_byte(AHasher::new_with_keys);
405418
}
406419

@@ -415,6 +428,8 @@ mod fallback_tests {
415428

416429
#[test]
417430
fn fallback_finish_is_consistant() {
431+
#[cfg(feature = "specialize")]
432+
test_finish_is_consistent(|a, b| AHasherStr(AHasher::test_with_keys(a, b)));
418433
test_finish_is_consistent(AHasher::test_with_keys)
419434
}
420435

@@ -424,17 +439,31 @@ mod fallback_tests {
424439
test_padding_doesnot_collide(|| AHasher::new_with_keys(0, 2));
425440
test_padding_doesnot_collide(|| AHasher::new_with_keys(2, 0));
426441
test_padding_doesnot_collide(|| AHasher::new_with_keys(2, 2));
442+
#[cfg(feature = "specialize")]
443+
{
444+
test_padding_doesnot_collide(|| AHasherStr(AHasher::new_with_keys(0, 0)));
445+
test_padding_doesnot_collide(|| AHasherStr(AHasher::new_with_keys(0, 2)));
446+
test_padding_doesnot_collide(|| AHasherStr(AHasher::new_with_keys(2, 0)));
447+
test_padding_doesnot_collide(|| AHasherStr(AHasher::new_with_keys(2, 2)));
448+
}
427449
}
428450

429451
#[test]
430452
fn fallback_length_extension() {
453+
#[cfg(feature = "specialize")]
454+
test_length_extension(|a, b| AHasherStr(AHasher::new_with_keys(a, b)));
431455
test_length_extension(|a, b| AHasher::new_with_keys(a, b));
432456
}
433457

434458
#[test]
435459
fn test_no_sparse_collisions() {
436460
test_sparse(|| AHasher::new_with_keys(0, 0));
437461
test_sparse(|| AHasher::new_with_keys(1, 2));
462+
#[cfg(feature = "specialize")]
463+
{
464+
test_sparse(|| AHasherStr(AHasher::new_with_keys(0, 0)));
465+
test_sparse(|| AHasherStr(AHasher::new_with_keys(1, 2)));
466+
}
438467
}
439468
}
440469

@@ -449,7 +478,7 @@ mod aes_tests {
449478
use crate::aes_hash::*;
450479
use crate::hash_quality_test::*;
451480
use std::hash::{Hash, Hasher};
452-
481+
453482
//This encrypts to 0.
454483
const BAD_KEY2: u128 = 0x6363_6363_6363_6363_6363_6363_6363_6363;
455484
//This decrypts to 0.
@@ -466,40 +495,77 @@ mod aes_tests {
466495

467496
#[test]
468497
fn aes_single_bit_flip() {
498+
test_single_bit_flip(|| AHasher::test_with_keys(0, 0));
469499
test_single_bit_flip(|| AHasher::test_with_keys(BAD_KEY, BAD_KEY));
470500
test_single_bit_flip(|| AHasher::test_with_keys(BAD_KEY2, BAD_KEY2));
501+
#[cfg(feature = "specialize")]
502+
{
503+
test_single_bit_flip(|| AHasherFixed(AHasher::test_with_keys(0, 0)));
504+
test_single_bit_flip(|| AHasherFixed(AHasher::test_with_keys(BAD_KEY, BAD_KEY)));
505+
test_single_bit_flip(|| AHasherFixed(AHasher::test_with_keys(BAD_KEY2, BAD_KEY2)));
506+
}
471507
}
472508

473509
#[test]
474510
fn aes_single_key_bit_flip() {
475-
test_single_key_bit_flip(AHasher::test_with_keys)
511+
test_single_key_bit_flip(AHasher::test_with_keys);
512+
#[cfg(feature = "specialize")]
513+
{
514+
test_single_key_bit_flip(|a, b| AHasherStr(AHasher::test_with_keys(a, b)));
515+
}
476516
}
477517

478518
#[test]
479519
fn aes_all_bytes_matter() {
520+
test_all_bytes_matter(|| AHasher::test_with_keys(0, 0));
480521
test_all_bytes_matter(|| AHasher::test_with_keys(BAD_KEY, BAD_KEY));
481522
test_all_bytes_matter(|| AHasher::test_with_keys(BAD_KEY2, BAD_KEY2));
523+
#[cfg(feature = "specialize")]
524+
{
525+
test_all_bytes_matter(|| AHasherStr(AHasher::test_with_keys(0, 0)));
526+
test_all_bytes_matter(|| AHasherStr(AHasher::test_with_keys(BAD_KEY, BAD_KEY)));
527+
test_all_bytes_matter(|| AHasherStr(AHasher::test_with_keys(BAD_KEY2, BAD_KEY2)));
528+
}
482529
}
483530

484531
#[test]
485532
fn aes_test_no_pair_collisions() {
533+
test_no_pair_collisions(|| AHasher::test_with_keys(0, 0));
486534
test_no_pair_collisions(|| AHasher::test_with_keys(BAD_KEY, BAD_KEY));
487535
test_no_pair_collisions(|| AHasher::test_with_keys(BAD_KEY2, BAD_KEY2));
536+
#[cfg(feature = "specialize")]
537+
{
538+
test_no_pair_collisions(|| AHasherFixed(AHasher::test_with_keys(0, 0)));
539+
test_no_pair_collisions(|| AHasherFixed(AHasher::test_with_keys(BAD_KEY, BAD_KEY)));
540+
test_no_pair_collisions(|| AHasherFixed(AHasher::test_with_keys(BAD_KEY2, BAD_KEY2)));
541+
}
488542
}
489543

490544
#[test]
491545
fn ase_test_no_full_collisions() {
492546
test_no_full_collisions(|| AHasher::test_with_keys(12345, 67890));
547+
#[cfg(feature = "specialize")]
548+
{
549+
test_no_full_collisions(|| AHasherStr(AHasher::test_with_keys(12345, 67890)));
550+
}
493551
}
494552

495553
#[test]
496554
fn aes_keys_change_output() {
497555
test_keys_change_output(AHasher::test_with_keys);
556+
#[cfg(feature = "specialize")]
557+
{
558+
test_keys_change_output(|a, b| AHasherStr(AHasher::test_with_keys(a, b)));
559+
}
498560
}
499561

500562
#[test]
501563
fn aes_input_affect_every_byte() {
502564
test_input_affect_every_byte(AHasher::test_with_keys);
565+
#[cfg(feature = "specialize")]
566+
{
567+
test_input_affect_every_byte(|a, b| AHasherFixed(AHasher::test_with_keys(a, b)));
568+
}
503569
}
504570

505571
#[test]
@@ -512,23 +578,43 @@ mod aes_tests {
512578

513579
#[test]
514580
fn aes_finish_is_consistant() {
515-
test_finish_is_consistent(AHasher::test_with_keys)
581+
test_finish_is_consistent(AHasher::test_with_keys);
582+
#[cfg(feature = "specialize")]
583+
{
584+
test_finish_is_consistent(|a, b| AHasherStr(AHasher::test_with_keys(a, b)));
585+
}
516586
}
517587

518588
#[test]
519589
fn aes_padding_doesnot_collide() {
590+
test_padding_doesnot_collide(|| AHasher::test_with_keys(0, 0));
520591
test_padding_doesnot_collide(|| AHasher::test_with_keys(BAD_KEY, BAD_KEY));
521592
test_padding_doesnot_collide(|| AHasher::test_with_keys(BAD_KEY2, BAD_KEY2));
593+
#[cfg(feature = "specialize")]
594+
{
595+
test_padding_doesnot_collide(|| AHasherStr(AHasher::test_with_keys(0, 0)));
596+
test_padding_doesnot_collide(|| AHasherStr(AHasher::test_with_keys(BAD_KEY, BAD_KEY)));
597+
test_padding_doesnot_collide(|| AHasherStr(AHasher::test_with_keys(BAD_KEY2, BAD_KEY2)));
598+
}
522599
}
523600

524601
#[test]
525602
fn aes_length_extension() {
526603
test_length_extension(|a, b| AHasher::test_with_keys(a, b));
604+
#[cfg(feature = "specialize")]
605+
{
606+
test_length_extension(|a, b| AHasherStr(AHasher::test_with_keys(a, b)));
607+
}
527608
}
528609

529610
#[test]
530611
fn aes_no_sparse_collisions() {
531612
test_sparse(|| AHasher::test_with_keys(0, 0));
532613
test_sparse(|| AHasher::test_with_keys(1, 2));
614+
#[cfg(feature = "specialize")]
615+
{
616+
test_sparse(|| AHasherStr(AHasher::test_with_keys(0, 0)));
617+
test_sparse(|| AHasherStr(AHasher::test_with_keys(1, 2)));
618+
}
533619
}
534620
}

tests/map_tests.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -280,9 +280,8 @@ fn ahash_vec(b: &Vec<String>) -> u64 {
280280
let mut total: u64 = 0;
281281
let state = RandomState::<String>::with_seeds(0, 0, 0, 0);
282282
for item in b {
283-
let mut hasher = state.build_hasher();
284-
item.hash(&mut hasher);
285-
total = total.wrapping_add(hasher.finish());
283+
let value = state.hash_one(item);
284+
total = total.wrapping_add(value);
286285
}
287286
total
288287
}

0 commit comments

Comments
 (0)