diff --git a/src/header/map.rs b/src/header/map.rs index acb1dc9..b766ff5 100644 --- a/src/header/map.rs +++ b/src/header/map.rs @@ -1594,6 +1594,8 @@ impl HeaderMap { // Initialze the indices self.indices = vec![Pos::none(); cap]; + self.mask = cap.wrapping_sub(1) as Size; + // Rebuild the table self.rebuild(); } @@ -1604,32 +1606,37 @@ impl HeaderMap { debug_assert!(!self.is_scan()); // Loop over all entries and re-insert them into the map - for entry in &mut self.entries { + 'outer: + for (index, entry) in self.entries.iter_mut().enumerate() { let hash = hash_elem_using(&self.danger, &entry.key); let mut probe = desired_pos(self.mask, hash); let mut dist = 0; + // Update the entry's hash code + entry.hash = hash; + probe_loop!(probe < self.indices.len() as Size, { if let Some((_, entry_hash)) = self.indices[probe as usize].resolve() { // if existing element probed less than us, swap let their_dist = probe_distance(self.mask, entry_hash, probe); if their_dist < dist { + // Robinhood break; } } else { - break; + // Vaccant slot + self.indices[probe as usize] = Pos::new(index as Size, hash); + continue 'outer; } dist += 1; }); - entry.hash = hash; - do_insert_phase_two( &mut self.indices, probe as Size, - Pos::new(probe as Size, hash)); + Pos::new(index as Size, hash)); } } @@ -3015,7 +3022,7 @@ fn probe_distance(mask: Size, hash: HashValue, current: Size) -> Size { fn hash_elem_using(danger: &Danger, k: &K) -> HashValue where K: FastHash { - const MASK: u64 = MAX_SIZE as u64; + const MASK: u64 = (MAX_SIZE as u64) - 1; let hash = match *danger { // Safe hash diff --git a/tests/header_map.rs b/tests/header_map.rs index 72b9709..24dccd5 100644 --- a/tests/header_map.rs +++ b/tests/header_map.rs @@ -131,3 +131,129 @@ fn eq() { assert_ne!(a, b); } + +#[test] +fn insert_all_std_headers() { + let mut m = HeaderMap::new(); + + for (i, hdr) in STD.iter().enumerate() { + m.insert(hdr.clone(), hdr.as_str().to_string()); + + for j in 0..(i+1) { + assert_eq!(m[&STD[j]], STD[j].as_str()); + } + + if i != 0 { + for j in (i+1)..STD.len() { + assert!(m.get(&STD[j]).is_none(), "contained {}; j={}", STD[j].as_str(), j); + } + } + } +} + +#[test] +fn insert_79_custom_std_headers() { + let mut h = HeaderMap::new(); + let hdrs = custom_std(79); + + for (i, hdr) in hdrs.iter().enumerate() { + h.insert(hdr.clone(), hdr.as_str().to_string()); + + for j in 0..(i+1) { + assert_eq!(h[&hdrs[j]], hdrs[j].as_str()); + } + + for j in (i+1)..hdrs.len() { + assert!(h.get(&hdrs[j]).is_none()); + } + } +} + +fn custom_std(n: usize) -> Vec { + (0..n).map(|i| { + let s = format!("{}-{}", STD[i % STD.len()].as_str(), i); + s.parse().unwrap() + }).collect() +} + +const STD: [HeaderName; 79] = [ + ACCEPT, + ACCEPT_CHARSET, + ACCEPT_ENCODING, + ACCEPT_LANGUAGE, + ACCEPT_PATCH, + ACCEPT_RANGES, + ACCESS_CONTROL_ALLOW_CREDENTIALS, + ACCESS_CONTROL_ALLOW_HEADERS, + ACCESS_CONTROL_ALLOW_METHODS, + ACCESS_CONTROL_ALLOW_ORIGIN, + ACCESS_CONTROL_EXPOSE_HEADERS, + ACCESS_CONTROL_MAX_AGE, + ACCESS_CONTROL_REQUEST_HEADERS, + ACCESS_CONTROL_REQUEST_METHOD, + AGE, + ALLOW, + ALT_SVC, + AUTHORIZATION, + CACHE_CONTROL, + CONNECTION, + CONTENT_DISPOSITION, + CONTENT_ENCODING, + CONTENT_LANGUAGE, + CONTENT_LENGTH, + CONTENT_LOCATION, + CONTENT_MD5, + CONTENT_RANGE, + CONTENT_SECURITY_POLICY, + CONTENT_SECURITY_POLICY_REPORT_ONLY, + CONTENT_TYPE, + COOKIE, + DNT, + DATE, + ETAG, + EXPECT, + EXPIRES, + FORWARDED, + FROM, + HOST, + IF_MATCH, + IF_MODIFIED_SINCE, + IF_NONE_MATCH, + IF_RANGE, + IF_UNMODIFIED_SINCE, + LAST_MODIFIED, + KEEP_ALIVE, + LINK, + LOCATION, + MAX_FORWARDS, + ORIGIN, + PRAGMA, + PROXY_AUTHENTICATE, + PROXY_AUTHORIZATION, + PUBLIC_KEY_PINS, + PUBLIC_KEY_PINS_REPORT_ONLY, + RANGE, + REFERER, + REFERRER_POLICY, + REFRESH, + RETRY_AFTER, + SERVER, + SET_COOKIE, + STRICT_TRANSPORT_SECURITY, + TE, + TK, + TRAILER, + TRANSFER_ENCODING, + TSV, + USER_AGENT, + UPGRADE, + UPGRADE_INSECURE_REQUESTS, + VARY, + VIA, + WARNING, + WWW_AUTHENTICATE, + X_CONTENT_TYPE_OPTIONS, + X_DNS_PREFETCH_CONTROL, + X_FRAME_OPTIONS, + X_XSS_PROTECTION, +];