wasm_bindgen/
safe_integer.rs1use alloc::boxed::Box;
2use core::convert::TryFrom;
3use core::fmt;
4use core::mem;
5use core::ops::{
6 Add, AddAssign, Deref, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
7};
8
9use crate::__rt::marker::ErasableGeneric;
10use crate::convert::{
11 FromWasmAbi, IntoWasmAbi, LongRefFromWasmAbi, MaybeFromWasmAbi, MaybeIntoWasmAbi,
12 NullableFromWasmAbi, NullableIntoWasmAbi, RefFromWasmAbi, TryFromJsValue, UpcastFrom,
13 VectorFromWasmAbi, VectorIntoWasmAbi, WasmSlice,
14};
15use crate::describe::{inform, WasmDescribe, WasmDescribeVector, F64, VECTOR};
16use crate::sys::{JsNullable, JsOption, Promising};
17use crate::{JsValue, Nullable};
18
19const MAX_SAFE: f64 = 9007199254740991.0;
21
22mod sealed {
23 pub trait Sealed: Copy + Default + Eq + Ord + core::hash::Hash + 'static {
24 const SIGNED: bool;
25 const MAX: Self;
26 const MIN: Self;
27 fn to_f64(self) -> f64;
28 fn from_f64(n: f64) -> Self;
29 }
30
31 impl Sealed for u64 {
32 const SIGNED: bool = false;
33 const MAX: u64 = (1 << 53) - 1;
34 const MIN: u64 = 0;
35 #[inline]
36 fn to_f64(self) -> f64 {
37 self as f64
38 }
39 #[inline]
40 fn from_f64(n: f64) -> u64 {
41 n as u64
42 }
43 }
44
45 impl Sealed for i64 {
46 const SIGNED: bool = true;
47 const MAX: i64 = (1 << 53) - 1;
48 const MIN: i64 = -((1 << 53) - 1);
49 #[inline]
50 fn to_f64(self) -> f64 {
51 self as f64
52 }
53 #[inline]
54 fn from_f64(n: f64) -> i64 {
55 n as i64
56 }
57 }
58}
59
60pub trait SafeIntegerPrimitive: sealed::Sealed {}
64impl SafeIntegerPrimitive for u64 {}
65impl SafeIntegerPrimitive for i64 {}
66
67#[repr(transparent)]
110#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
111pub struct SafeInteger<T: SafeIntegerPrimitive>(T);
112
113pub type SafeU64 = SafeInteger<u64>;
115
116pub type SafeI64 = SafeInteger<i64>;
118
119#[inline]
120fn is_safe<T: SafeIntegerPrimitive>(n: f64) -> bool {
121 let min = if T::SIGNED { -MAX_SAFE } else { 0.0 };
122 n >= min && n <= MAX_SAFE && (n as i64) as f64 == n
125}
126
127impl<T: SafeIntegerPrimitive> SafeInteger<T> {
128 pub const MAX: Self = SafeInteger(T::MAX);
130
131 pub const MIN: Self = SafeInteger(T::MIN);
134
135 #[inline]
137 pub fn new(value: T) -> Option<Self> {
138 if value >= T::MIN && value <= T::MAX {
139 Some(SafeInteger(value))
140 } else {
141 None
142 }
143 }
144
145 #[inline]
146 fn checked(value: T) -> Self {
147 debug_assert!(
148 value >= T::MIN && value <= T::MAX,
149 "SafeInteger out of the safe integer range"
150 );
151 SafeInteger(value)
152 }
153
154 #[inline]
155 fn to_f64(self) -> f64 {
156 self.0.to_f64()
157 }
158
159 #[inline]
160 fn from_f64(n: f64) -> Self {
161 debug_assert!(is_safe::<T>(n), "{n} is not a safe integer");
162 SafeInteger(T::from_f64(n))
163 }
164}
165
166impl<T: SafeIntegerPrimitive> Deref for SafeInteger<T> {
167 type Target = T;
168
169 #[inline]
170 fn deref(&self) -> &T {
171 &self.0
172 }
173}
174
175impl<T: SafeIntegerPrimitive> From<T> for SafeInteger<T> {
176 #[inline]
177 fn from(value: T) -> Self {
178 Self::checked(value)
179 }
180}
181
182impl<T: SafeIntegerPrimitive> PartialEq<T> for SafeInteger<T> {
183 #[inline]
184 fn eq(&self, other: &T) -> bool {
185 self.0 == *other
186 }
187}
188
189impl<T: SafeIntegerPrimitive> PartialOrd<T> for SafeInteger<T> {
190 #[inline]
191 fn partial_cmp(&self, other: &T) -> Option<core::cmp::Ordering> {
192 Some(self.0.cmp(other))
193 }
194}
195
196impl<T: SafeIntegerPrimitive + fmt::Debug> fmt::Debug for SafeInteger<T> {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 fmt::Debug::fmt(&self.0, f)
199 }
200}
201
202impl<T: SafeIntegerPrimitive + fmt::Display> fmt::Display for SafeInteger<T> {
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 fmt::Display::fmt(&self.0, f)
205 }
206}
207
208macro_rules! binops {
209 ($($imp:ident $method:ident $assign_imp:ident $assign_method:ident,)*) => ($(
210 impl<T: SafeIntegerPrimitive + $imp<Output = T>> $imp for SafeInteger<T> {
211 type Output = Self;
212
213 #[inline]
214 fn $method(self, rhs: Self) -> Self {
215 Self::checked($imp::$method(self.0, rhs.0))
216 }
217 }
218
219 impl<T: SafeIntegerPrimitive + $imp<Output = T>> $imp<T> for SafeInteger<T> {
220 type Output = Self;
221
222 #[inline]
223 fn $method(self, rhs: T) -> Self {
224 Self::checked($imp::$method(self.0, rhs))
225 }
226 }
227
228 impl<T: SafeIntegerPrimitive + $imp<Output = T>> $assign_imp for SafeInteger<T> {
229 #[inline]
230 fn $assign_method(&mut self, rhs: Self) {
231 *self = $imp::$method(*self, rhs);
232 }
233 }
234
235 impl<T: SafeIntegerPrimitive + $imp<Output = T>> $assign_imp<T> for SafeInteger<T> {
236 #[inline]
237 fn $assign_method(&mut self, rhs: T) {
238 *self = $imp::$method(*self, rhs);
239 }
240 }
241 )*)
242}
243
244binops! {
245 Add add AddAssign add_assign,
246 Sub sub SubAssign sub_assign,
247 Mul mul MulAssign mul_assign,
248 Div div DivAssign div_assign,
249 Rem rem RemAssign rem_assign,
250}
251
252impl Neg for SafeInteger<i64> {
253 type Output = Self;
254
255 #[inline]
256 fn neg(self) -> Self {
257 SafeInteger(-self.0)
258 }
259}
260
261macro_rules! concrete {
262 ($($t:ident)*) => ($(
263 impl From<SafeInteger<$t>> for $t {
264 #[inline]
265 fn from(value: SafeInteger<$t>) -> $t {
266 value.0
267 }
268 }
269
270 impl PartialEq<SafeInteger<$t>> for $t {
271 #[inline]
272 fn eq(&self, other: &SafeInteger<$t>) -> bool {
273 *self == other.0
274 }
275 }
276
277 impl PartialOrd<SafeInteger<$t>> for $t {
278 #[inline]
279 fn partial_cmp(&self, other: &SafeInteger<$t>) -> Option<core::cmp::Ordering> {
280 Some(self.cmp(&other.0))
281 }
282 }
283 )*)
284}
285
286concrete!(u64 i64);
287
288impl<T: SafeIntegerPrimitive> WasmDescribe for SafeInteger<T> {
289 #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
290 fn describe() {
291 inform(F64)
292 }
293}
294
295impl<T: SafeIntegerPrimitive> IntoWasmAbi for SafeInteger<T> {
296 type Abi = f64;
297
298 #[inline]
299 fn into_abi(self) -> f64 {
300 self.to_f64()
301 }
302}
303
304impl<T: SafeIntegerPrimitive> FromWasmAbi for SafeInteger<T> {
305 type Abi = f64;
306
307 #[inline]
308 unsafe fn from_abi(js: f64) -> Self {
309 Self::from_f64(js)
310 }
311}
312
313impl<T: SafeIntegerPrimitive> MaybeIntoWasmAbi for SafeInteger<T> {
316 type MaybeAbi = <f64 as MaybeIntoWasmAbi>::MaybeAbi;
317
318 #[inline]
319 fn into_maybe_abi(self) -> Self::MaybeAbi {
320 self.to_f64().into_maybe_abi()
321 }
322
323 #[inline]
324 fn none() -> Self::MaybeAbi {
325 <f64 as MaybeIntoWasmAbi>::none()
326 }
327}
328
329impl<T: SafeIntegerPrimitive> NullableIntoWasmAbi for SafeInteger<T> {
330 #[inline]
331 fn null() -> Self::MaybeAbi {
332 <f64 as NullableIntoWasmAbi>::null()
333 }
334}
335
336impl<T: SafeIntegerPrimitive> MaybeFromWasmAbi for SafeInteger<T> {
337 type MaybeAbi = <f64 as MaybeFromWasmAbi>::MaybeAbi;
338
339 #[inline]
340 unsafe fn from_maybe_abi(js: Self::MaybeAbi) -> Self {
341 Self::from_f64(f64::from_maybe_abi(js))
342 }
343
344 #[inline]
345 fn is_none(js: &Self::MaybeAbi) -> bool {
346 <f64 as MaybeFromWasmAbi>::is_none(js)
347 }
348}
349
350impl<T: SafeIntegerPrimitive> NullableFromWasmAbi for SafeInteger<T> {
351 #[inline]
352 fn is_null(js: &Self::MaybeAbi) -> bool {
353 <f64 as NullableFromWasmAbi>::is_null(js)
354 }
355}
356
357impl<T: SafeIntegerPrimitive> IntoWasmAbi for &SafeInteger<T> {
359 type Abi = f64;
360
361 #[inline]
362 fn into_abi(self) -> f64 {
363 (*self).into_abi()
364 }
365}
366
367impl<T: SafeIntegerPrimitive> MaybeIntoWasmAbi for &SafeInteger<T> {
368 type MaybeAbi = <f64 as MaybeIntoWasmAbi>::MaybeAbi;
369
370 #[inline]
371 fn into_maybe_abi(self) -> Self::MaybeAbi {
372 (*self).into_maybe_abi()
373 }
374
375 #[inline]
376 fn none() -> Self::MaybeAbi {
377 <f64 as MaybeIntoWasmAbi>::none()
378 }
379}
380
381impl<T: SafeIntegerPrimitive> NullableIntoWasmAbi for &SafeInteger<T> {
382 #[inline]
383 fn null() -> Self::MaybeAbi {
384 <f64 as NullableIntoWasmAbi>::null()
385 }
386}
387
388const _: () = assert!(
392 mem::size_of::<SafeInteger<u64>>() == mem::size_of::<f64>()
393 && mem::align_of::<SafeInteger<u64>>() == mem::align_of::<f64>()
394 && mem::size_of::<SafeInteger<i64>>() == mem::size_of::<f64>()
395 && mem::align_of::<SafeInteger<i64>>() == mem::align_of::<f64>()
396);
397
398impl<T: SafeIntegerPrimitive> WasmDescribeVector for SafeInteger<T> {
399 #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
400 fn describe_vector() {
401 inform(VECTOR);
402 inform(F64);
403 }
404}
405
406impl<T: SafeIntegerPrimitive> VectorIntoWasmAbi for SafeInteger<T> {
407 type Abi = WasmSlice;
408
409 fn vector_into_abi(vector: Box<[Self]>) -> WasmSlice {
410 let len = vector.len();
411 let ptr = Box::into_raw(vector) as *mut Self;
412 for i in 0..len {
413 unsafe {
414 let elem = ptr.add(i);
415 let n = (*elem).to_f64();
416 elem.cast::<f64>().write(n);
417 }
418 }
419 let floats =
420 unsafe { Box::from_raw(core::ptr::slice_from_raw_parts_mut(ptr.cast::<f64>(), len)) };
421 f64::vector_into_abi(floats)
422 }
423}
424
425impl<T: SafeIntegerPrimitive> VectorFromWasmAbi for SafeInteger<T> {
426 type Abi = WasmSlice;
427
428 unsafe fn vector_from_abi(js: WasmSlice) -> Box<[Self]> {
429 let floats = f64::vector_from_abi(js);
430 let len = floats.len();
431 let ptr = Box::into_raw(floats) as *mut f64;
432 for i in 0..len {
433 let elem = ptr.add(i);
434 let n = Self::from_f64(*elem);
435 elem.cast::<Self>().write(n);
436 }
437 Box::from_raw(core::ptr::slice_from_raw_parts_mut(ptr.cast::<Self>(), len))
438 }
439}
440
441impl<T: SafeIntegerPrimitive> RefFromWasmAbi for [SafeInteger<T>] {
442 type Abi = WasmSlice;
443 type Anchor = Box<[SafeInteger<T>]>;
444
445 #[inline]
446 unsafe fn ref_from_abi(js: WasmSlice) -> Self::Anchor {
447 <Box<[SafeInteger<T>]>>::from_abi(js)
448 }
449}
450
451impl<T: SafeIntegerPrimitive> LongRefFromWasmAbi for [SafeInteger<T>] {
452 type Abi = WasmSlice;
453 type Anchor = Box<[SafeInteger<T>]>;
454
455 #[inline]
456 unsafe fn long_ref_from_abi(js: WasmSlice) -> Self::Anchor {
457 <Box<[SafeInteger<T>]>>::from_abi(js)
458 }
459}
460
461unsafe impl<T: SafeIntegerPrimitive> ErasableGeneric for SafeInteger<T> {
462 type Repr = Self;
463}
464
465impl<T: SafeIntegerPrimitive> Promising for SafeInteger<T> {
466 type Resolution = Self;
467}
468
469impl<T: SafeIntegerPrimitive> UpcastFrom<SafeInteger<T>> for SafeInteger<T> {}
470impl<T: SafeIntegerPrimitive> UpcastFrom<SafeInteger<T>> for f64 {}
471impl<T: SafeIntegerPrimitive> UpcastFrom<SafeInteger<T>> for JsOption<f64> {}
472impl<T: SafeIntegerPrimitive> UpcastFrom<SafeInteger<T>> for JsNullable<f64> {}
473impl<T: SafeIntegerPrimitive> UpcastFrom<SafeInteger<T>> for JsValue {}
474impl<T: SafeIntegerPrimitive> UpcastFrom<SafeInteger<T>> for JsOption<JsValue> {}
475impl<T: SafeIntegerPrimitive> UpcastFrom<SafeInteger<T>> for JsNullable<JsValue> {}
476impl<T: SafeIntegerPrimitive, Target: UpcastFrom<SafeInteger<T>>> UpcastFrom<SafeInteger<T>>
477 for Option<Target>
478{
479}
480impl<T: SafeIntegerPrimitive, Target: UpcastFrom<SafeInteger<T>>> UpcastFrom<SafeInteger<T>>
481 for Nullable<Target>
482{
483}
484
485impl<T: SafeIntegerPrimitive> From<SafeInteger<T>> for JsValue {
486 #[inline]
487 fn from(n: SafeInteger<T>) -> JsValue {
488 JsValue::from_f64(n.to_f64())
489 }
490}
491
492impl<T: SafeIntegerPrimitive> PartialEq<SafeInteger<T>> for JsValue {
493 #[inline]
494 fn eq(&self, other: &SafeInteger<T>) -> bool {
495 self.as_f64() == Some(other.to_f64())
496 }
497}
498
499impl<T: SafeIntegerPrimitive> TryFromJsValue for SafeInteger<T> {
502 #[inline]
503 fn try_from_js_value_ref(value: &JsValue) -> Option<Self> {
504 let n = value.as_f64()?;
505 if is_safe::<T>(n) {
506 Some(SafeInteger(T::from_f64(n)))
507 } else {
508 None
509 }
510 }
511}
512
513impl<T: SafeIntegerPrimitive> TryFrom<JsValue> for SafeInteger<T> {
514 type Error = JsValue;
515
516 #[inline]
517 fn try_from(value: JsValue) -> Result<Self, JsValue> {
518 Self::try_from_js_value(value)
519 }
520}
521
522impl<T: SafeIntegerPrimitive> TryFrom<&JsValue> for SafeInteger<T> {
523 type Error = JsValue;
524
525 #[inline]
526 fn try_from(value: &JsValue) -> Result<Self, JsValue> {
527 Self::try_from_js_value_ref(value).ok_or_else(|| value.clone())
528 }
529}