rivet_envoy_client/
utils.rs1use std::collections::HashMap;
2use std::future::Future;
3use std::pin::Pin;
4use std::time::Duration;
5
6use rand::Rng;
7#[cfg(target_arch = "wasm32")]
8use wasm_bindgen::{JsCast, JsValue};
9#[cfg(target_arch = "wasm32")]
10use wasm_bindgen_futures::JsFuture;
11
12pub fn id_to_str(id: &[u8]) -> String {
14 hex::encode(id)
15}
16
17pub fn stringify_error(error: &anyhow::Error) -> String {
19 format!("{error:#}")
20}
21
22#[derive(Debug)]
24pub struct EnvoyShutdownError;
25
26impl std::fmt::Display for EnvoyShutdownError {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 write!(f, "envoy shut down")
29 }
30}
31
32impl std::error::Error for EnvoyShutdownError {}
33
34#[derive(Debug)]
37pub struct RemoteSqliteIndeterminateResultError {
38 pub operation: &'static str,
39}
40
41impl std::fmt::Display for RemoteSqliteIndeterminateResultError {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 write!(
44 f,
45 "remote sqlite {} result is indeterminate after envoy disconnect",
46 self.operation
47 )
48 }
49}
50
51impl std::error::Error for RemoteSqliteIndeterminateResultError {}
52
53#[derive(Debug)]
56pub struct RemoteSqliteConnectionSessionLostError {
57 pub expected: u64,
58 pub current: Option<u64>,
59}
60
61impl std::fmt::Display for RemoteSqliteConnectionSessionLostError {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 write!(
64 f,
65 "remote sqlite transaction connection session {} is no longer active",
66 self.expected
67 )
68 }
69}
70
71impl std::error::Error for RemoteSqliteConnectionSessionLostError {}
72
73pub async fn inject_latency(ms: Option<u64>) {
75 if let Some(ms) = ms {
76 if ms > 0 {
77 sleep(Duration::from_millis(ms)).await;
78 }
79 }
80}
81
82#[cfg(not(target_arch = "wasm32"))]
83pub type SleepFuture = Pin<Box<dyn Future<Output = ()> + Send>>;
84#[cfg(target_arch = "wasm32")]
85pub type SleepFuture = Pin<Box<dyn Future<Output = ()>>>;
86
87pub fn boxed_sleep(duration: Duration) -> SleepFuture {
88 Box::pin(sleep(duration))
89}
90
91#[cfg(not(target_arch = "wasm32"))]
92pub async fn sleep(duration: Duration) {
93 tokio::time::sleep(duration).await;
94}
95
96#[cfg(target_arch = "wasm32")]
97pub async fn sleep(duration: Duration) {
98 let delay_ms = duration.as_millis().min(u32::MAX as u128) as f64;
99 let promise = js_sys::Promise::new(&mut |resolve, _reject| {
100 let global = js_sys::global();
101 let set_timeout = js_sys::Reflect::get(&global, &JsValue::from_str("setTimeout"))
102 .ok()
103 .and_then(|value| value.dyn_into::<js_sys::Function>().ok());
104
105 if let Some(set_timeout) = set_timeout {
106 let _ = set_timeout.call2(&global, &resolve, &JsValue::from_f64(delay_ms));
107 } else {
108 let _ = resolve.call0(&JsValue::UNDEFINED);
109 }
110 });
111
112 let _ = JsFuture::from(promise).await;
113}
114
115#[cfg(not(target_arch = "wasm32"))]
116pub fn spawn_detached<F>(future: F)
117where
118 F: Future<Output = ()> + Send + 'static,
119{
120 tokio::spawn(future);
121}
122
123#[cfg(target_arch = "wasm32")]
124pub fn spawn_detached<F>(future: F)
125where
126 F: Future<Output = ()> + 'static,
127{
128 tokio::task::spawn_local(future);
129}
130
131pub struct BackoffOptions {
132 pub initial_delay: u64,
133 pub max_delay: u64,
134 pub multiplier: f64,
135 pub jitter: bool,
136}
137
138impl Default for BackoffOptions {
139 fn default() -> Self {
140 Self {
141 initial_delay: 1000,
142 max_delay: 30000,
143 multiplier: 2.0,
144 jitter: true,
145 }
146 }
147}
148
149pub fn calculate_backoff(attempt: u32, options: &BackoffOptions) -> Duration {
150 let delay = (options.initial_delay as f64 * options.multiplier.powi(attempt as i32))
151 .min(options.max_delay as f64);
152
153 let delay = if options.jitter {
154 let jitter = rand::thread_rng().gen_range(0.0..0.25);
155 delay * (1.0 + jitter)
156 } else {
157 delay
158 };
159
160 Duration::from_millis(delay as u64)
161}
162
163pub struct ParsedCloseReason {
164 pub group: String,
165 pub error: String,
166 pub ray_id: Option<String>,
167}
168
169pub fn parse_ws_close_reason(reason: &str) -> Option<ParsedCloseReason> {
170 let (main_part, ray_id) = match reason.split_once('#') {
171 Some((main, ray)) => (main, Some(ray.to_string())),
172 None => (reason, None),
173 };
174
175 let (group, error) = main_part.split_once('.')?;
176
177 if group.is_empty() || error.is_empty() {
178 tracing::warn!(%reason, "failed to parse close reason");
179 return None;
180 }
181
182 Some(ParsedCloseReason {
183 group: group.to_string(),
184 error: error.to_string(),
185 ray_id,
186 })
187}
188
189const U16_MAX: u32 = 65535;
190
191pub fn wrapping_add_u16(a: u16, b: u16) -> u16 {
192 a.wrapping_add(b)
193}
194
195pub fn wrapping_sub_u16(a: u16, b: u16) -> u16 {
196 a.wrapping_sub(b)
197}
198
199pub fn wrapping_gt_u16(a: u16, b: u16) -> bool {
200 a != b && (a.wrapping_sub(b) as u32) < U16_MAX / 2
201}
202
203pub fn wrapping_lt_u16(a: u16, b: u16) -> bool {
204 a != b && (b.wrapping_sub(a) as u32) < U16_MAX / 2
205}
206
207pub fn wrapping_gte_u16(a: u16, b: u16) -> bool {
208 a == b || wrapping_gt_u16(a, b)
209}
210
211pub fn wrapping_lte_u16(a: u16, b: u16) -> bool {
212 a == b || wrapping_lt_u16(a, b)
213}
214
215pub struct BufferMap<T> {
217 inner: HashMap<String, T>,
218}
219
220impl<T> BufferMap<T> {
221 pub fn new() -> Self {
222 Self {
223 inner: HashMap::new(),
224 }
225 }
226
227 pub fn get(&self, buffers: &[&[u8]]) -> Option<&T> {
228 self.inner.get(&cyrb53(buffers))
229 }
230
231 pub fn get_mut(&mut self, buffers: &[&[u8]]) -> Option<&mut T> {
232 self.inner.get_mut(&cyrb53(buffers))
233 }
234
235 pub fn insert(&mut self, buffers: &[&[u8]], value: T) {
236 self.inner.insert(cyrb53(buffers), value);
237 }
238
239 pub fn remove(&mut self, buffers: &[&[u8]]) -> Option<T> {
240 self.inner.remove(&cyrb53(buffers))
241 }
242
243 pub fn contains_key(&self, buffers: &[&[u8]]) -> bool {
244 self.inner.contains_key(&cyrb53(buffers))
245 }
246
247 pub fn remove_where(&mut self, mut predicate: impl FnMut(&T) -> bool) -> Vec<T> {
248 let keys = self
249 .inner
250 .iter()
251 .filter_map(|(key, value)| predicate(value).then_some(key.clone()))
252 .collect::<Vec<_>>();
253 keys.into_iter()
254 .filter_map(|key| self.inner.remove(&key))
255 .collect()
256 }
257}
258
259impl<T> Default for BufferMap<T> {
260 fn default() -> Self {
261 Self::new()
262 }
263}
264
265fn cyrb53(buffers: &[&[u8]]) -> String {
266 let (mut h1, mut h2): (u32, u32) = (0xdeadbeef, 0x41c6ce57);
267 for buffer in buffers {
268 for &b in *buffer {
269 h1 = (h1 ^ b as u32).wrapping_mul(2654435761);
270 h2 = (h2 ^ b as u32).wrapping_mul(1597334677);
271 }
272 }
273 h1 = (h1 ^ (h1 >> 16)).wrapping_mul(2246822507) ^ (h2 ^ (h2 >> 13)).wrapping_mul(3266489909);
274 h2 = (h2 ^ (h2 >> 16)).wrapping_mul(2246822507) ^ (h1 ^ (h1 >> 13)).wrapping_mul(3266489909);
275 let result = (2097151 & h2 as u64) * 4294967296 + h1 as u64;
276 format!("{result:x}")
277}