Skip to content

Commit 7b5808d

Browse files
authored
fix(hash): fix #780 (#784)
1 parent a4e7b98 commit 7b5808d

20 files changed

Lines changed: 192 additions & 37 deletions

File tree

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ percent-encoding = "2.3"
9090
ipnet = "2.11.0"
9191
schnellru = { version = "0.2.4", default-features = false }
9292
ahash = { version = "0.8.11", default-features = false }
93-
hashmemo = "0.2.0"
9493

9594
## boring-tls
9695
boring2 = { version = "5.0.0-alpha.4", features = ["pq-experimental"] }
Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pub use http::{Request as HttpRequest, Response as HttpResponse};
1+
use http::{Request as HttpRequest, Response as HttpResponse};
22
use tower::{
33
retry::Retry,
44
util::{BoxCloneSyncService, BoxCloneSyncServiceLayer, MapErr},
@@ -17,26 +17,26 @@ use crate::{
1717
};
1818

1919
#[cfg(not(feature = "cookies"))]
20-
type MaybeCookieLayer<T> = T;
20+
type CookieLayer<T> = T;
2121

2222
#[cfg(feature = "cookies")]
23-
type MaybeCookieLayer<T> = crate::client::middleware::cookie::CookieManager<T>;
23+
type CookieLayer<T> = crate::client::middleware::cookie::CookieManager<T>;
2424

2525
#[cfg(not(any(
2626
feature = "gzip",
2727
feature = "zstd",
2828
feature = "brotli",
2929
feature = "deflate"
3030
)))]
31-
type MaybeDecompression<T> = T;
31+
type Decompression<T> = T;
3232

3333
#[cfg(any(
3434
feature = "gzip",
3535
feature = "zstd",
3636
feature = "brotli",
3737
feature = "deflate"
3838
))]
39-
type MaybeDecompression<T> = crate::client::middleware::decoder::Decompression<T>;
39+
type Decompression<T> = crate::client::middleware::decoder::Decompression<T>;
4040

4141
#[cfg(any(
4242
feature = "gzip",
@@ -54,10 +54,8 @@ pub type ResponseBody = TimeoutBody<tower_http::decompression::DecompressionBody
5454
)))]
5555
pub type ResponseBody = TimeoutBody<Incoming>;
5656

57-
type RedirectLayer = FollowRedirect<
58-
MaybeCookieLayer<ResponseBodyTimeout<MaybeDecompression<ClientService>>>,
59-
RedirectPolicy,
60-
>;
57+
pub type RedirectLayer =
58+
FollowRedirect<CookieLayer<ResponseBodyTimeout<Decompression<ClientService>>>, RedirectPolicy>;
6159

6260
pub type CoreResponseFuture = crate::core::client::ResponseFuture;
6361

src/client/client/future.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ use std::{
33
task::{Context, Poll},
44
};
55

6-
use http::Response as HttpResponse;
6+
use http::{Request as HttpRequest, Response as HttpResponse};
77
use pin_project_lite::pin_project;
88
use tower::util::Oneshot;
99
use url::Url;
1010

1111
use super::{
1212
Response,
13-
types::{BoxedClientService, CoreResponseFuture, GenericClientService, HttpRequest},
13+
aliases::{BoxedClientService, CoreResponseFuture, GenericClientService},
1414
};
1515
use crate::{
1616
Body, Error,

src/client/client/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#[macro_use]
22
mod macros;
3+
mod aliases;
34
mod future;
45
mod service;
5-
mod types;
66

77
use std::{
88
collections::HashMap,
@@ -14,6 +14,7 @@ use std::{
1414
time::Duration,
1515
};
1616

17+
use aliases::{BoxedClientService, BoxedClientServiceLayer, GenericClientService, ResponseBody};
1718
pub use future::Pending;
1819
use http::{
1920
Request as HttpRequest, Response as HttpResponse,
@@ -25,7 +26,6 @@ use tower::{
2526
retry::RetryLayer,
2627
util::{BoxCloneSyncService, BoxCloneSyncServiceLayer},
2728
};
28-
use types::{BoxedClientService, BoxedClientServiceLayer, GenericClientService, ResponseBody};
2929
#[cfg(feature = "cookies")]
3030
use {super::middleware::cookie::CookieManagerLayer, crate::cookie};
3131

src/core/client/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ use std::{
1717
future::Future,
1818
num::NonZeroU32,
1919
pin::Pin,
20+
sync::Arc,
2021
task::{self, Poll},
2122
time::Duration,
2223
};
2324

2425
use futures_util::future::{self, Either, FutureExt, TryFutureExt};
25-
use hashmemo::HashMemo;
2626
use http::{
2727
HeaderValue, Method, Request, Response, Uri, Version,
2828
header::HOST,
@@ -40,13 +40,13 @@ use crate::{
4040
conn::TrySendError as ConnTrySendError,
4141
connect::{Alpn, Connect, Connected, Connection, TcpConnectOptions},
4242
},
43+
collections::{RANDOM_STATE, memo::HashMemo},
4344
common::{Exec, Lazy, lazy, timer},
4445
error::BoxError,
4546
ext::{
4647
RequestConfig, RequestEnforcedHttpVersion, RequestProxyMatcher,
4748
RequestTcpConnectOptions, RequestTransportConfig,
4849
},
49-
map::RANDOM_STATE,
5050
rt::{Executor, Timer},
5151
},
5252
proxy::Matcher as ProxyMacher,
@@ -111,7 +111,7 @@ impl ConnExtra {
111111
/// This type implements `Hash`, `Eq`, and `Clone` to support use
112112
/// in maps, caches, or deduplicated pools.
113113
#[derive(Clone, Hash, Debug, Eq, PartialEq)]
114-
pub(crate) struct ConnKey(Box<HashMemo<ConnExtra, ahash::RandomState>>);
114+
pub(crate) struct ConnKey(Arc<HashMemo<ConnExtra>>);
115115

116116
/// Describes all the parameters needed to initiate a client connection.
117117
///
@@ -124,7 +124,7 @@ pub(crate) struct ConnKey(Box<HashMemo<ConnExtra, ahash::RandomState>>);
124124
#[derive(Debug, Clone)]
125125
pub struct ConnRequest {
126126
uri: Uri,
127-
extra: Box<HashMemo<ConnExtra, ahash::RandomState>>,
127+
extra: Arc<HashMemo<ConnExtra>>,
128128
}
129129

130130
impl ConnRequest {
@@ -358,7 +358,7 @@ where
358358
}
359359

360360
let conn_req = ConnRequest {
361-
extra: Box::new(HashMemo::with_hasher(
361+
extra: Arc::new(HashMemo::with_hasher(
362362
ConnExtra {
363363
scheme: uri.scheme().cloned(),
364364
authority: uri.authority().cloned(),

src/core/client/pool.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ use tokio::sync::oneshot;
1818

1919
use crate::{
2020
core::{
21+
collections::{HashMap, HashSet, LruMap, RANDOM_STATE},
2122
common::{
2223
exec::{self, Exec},
2324
timer::Timer,
2425
},
25-
map::{HashMap, HashSet, LruMap, RANDOM_STATE},
2626
rt::{Sleep, Timer as _},
2727
},
2828
sync::Mutex,

src/core/collections/memo.rs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
//! Hash Value Memoization
2+
3+
use std::{
4+
borrow::Borrow,
5+
hash::{BuildHasher, BuildHasherDefault, Hash, Hasher},
6+
num::NonZeroU64,
7+
sync::atomic::{AtomicU64, Ordering},
8+
};
9+
10+
use ahash::RandomState;
11+
12+
/// A wrapper that memoizes the hash value of its contained data.
13+
#[derive(Debug)]
14+
pub struct HashMemo<T, H: BuildHasher = RandomState>
15+
where
16+
T: Eq + PartialEq + Hash,
17+
{
18+
value: T,
19+
hash: AtomicU64,
20+
hasher: H,
21+
}
22+
23+
impl<T, H> PartialOrd for HashMemo<T, H>
24+
where
25+
T: Eq + Hash + PartialOrd,
26+
H: BuildHasher,
27+
{
28+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
29+
self.value.partial_cmp(&other.value)
30+
}
31+
}
32+
33+
impl<T, H> Ord for HashMemo<T, H>
34+
where
35+
T: Eq + Hash + Ord,
36+
H: BuildHasher,
37+
{
38+
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
39+
self.value.cmp(&other.value)
40+
}
41+
}
42+
43+
impl<T, H> HashMemo<T, H>
44+
where
45+
T: Eq + Hash,
46+
H: BuildHasher,
47+
{
48+
/// Creates a new `HashMemo` with a custom hasher.
49+
///
50+
/// This allows you to specify a custom `BuildHasher` implementation for
51+
/// controlling how hash values are computed.
52+
pub const fn with_hasher(value: T, hasher: H) -> Self {
53+
Self {
54+
value,
55+
hash: AtomicU64::new(u64::MIN),
56+
hasher,
57+
}
58+
}
59+
}
60+
61+
impl<T, H> PartialEq for HashMemo<T, H>
62+
where
63+
T: Eq + Hash,
64+
H: BuildHasher,
65+
{
66+
fn eq(&self, other: &Self) -> bool {
67+
self.value == other.value
68+
}
69+
}
70+
71+
impl<T, H> Eq for HashMemo<T, H>
72+
where
73+
T: Eq + Hash,
74+
H: BuildHasher,
75+
{
76+
}
77+
78+
impl<T, H> Hash for HashMemo<T, H>
79+
where
80+
T: Eq + Hash,
81+
H: BuildHasher,
82+
{
83+
fn hash<H2: Hasher>(&self, state: &mut H2) {
84+
let hash = self.hash.load(Ordering::Relaxed);
85+
if hash != 0 {
86+
state.write_u64(hash);
87+
return;
88+
}
89+
90+
let computed_hash = NonZeroU64::new(self.hasher.hash_one(&self.value))
91+
.map(NonZeroU64::get)
92+
.unwrap_or(u64::MIN | 1);
93+
94+
let _ = self.hash.compare_exchange(
95+
u64::MIN,
96+
computed_hash,
97+
Ordering::Relaxed,
98+
Ordering::Relaxed,
99+
);
100+
state.write_u64(computed_hash);
101+
}
102+
}
103+
104+
impl<T, H> AsRef<T> for HashMemo<T, H>
105+
where
106+
T: Eq + Hash,
107+
H: BuildHasher,
108+
{
109+
fn as_ref(&self) -> &T {
110+
&self.value
111+
}
112+
}
113+
114+
impl<T, H> Borrow<T> for HashMemo<T, H>
115+
where
116+
T: Eq + Hash,
117+
H: BuildHasher,
118+
{
119+
fn borrow(&self) -> &T {
120+
&self.value
121+
}
122+
}
123+
124+
impl<T, H> From<T> for HashMemo<T, BuildHasherDefault<H>>
125+
where
126+
T: Eq + Hash,
127+
H: Hasher + Default,
128+
{
129+
fn from(value: T) -> Self {
130+
Self::with_hasher(value, BuildHasherDefault::<H>::default())
131+
}
132+
}
133+
134+
impl<T, H> Clone for HashMemo<T, H>
135+
where
136+
T: Eq + Hash + Clone,
137+
H: BuildHasher + Clone,
138+
{
139+
fn clone(&self) -> Self {
140+
Self {
141+
value: self.value.clone(),
142+
hash: AtomicU64::new(self.hash.load(Ordering::Relaxed)),
143+
hasher: self.hasher.clone(),
144+
}
145+
}
146+
}

src/core/collections/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pub mod aliases;
2+
pub mod memo;
3+
4+
pub use aliases::{HashMap, HashSet, LruMap, RANDOM_STATE};

src/core/ext/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,5 +159,5 @@ impl RequestConfigValue for RequestProxyMatcher {
159159
pub(crate) struct RequestOriginalHeaders;
160160

161161
impl RequestConfigValue for RequestOriginalHeaders {
162-
type Value = crate::core::header::OriginalHeaders;
162+
type Value = crate::core::ext::OriginalHeaders;
163163
}

0 commit comments

Comments
 (0)