Skip to main content

ps_uuid/implementations/ops/
shift.rs

1//! Shift operator implementations for UUID.
2//!
3//! This module provides implementations for:
4//!
5//! | Operator | Trait | Semantics |
6//! |----------|-------|-----------|
7//! | `<<` | [`Shl`] | Wrapping left shift |
8//! | `>>` | [`Shr`] | Wrapping right shift |
9//!
10//! # Wrapping Semantics
11//!
12//! Shift operations use **wrapping semantics**: the shift amount is masked to
13//! the valid range (0..128), matching the behavior of `u128::wrapping_shl/shr`.
14//! This means:
15//!
16//! - `uuid << 128` is equivalent to `uuid << 0` (shift amount is masked)
17//! - `uuid << 256` is equivalent to `uuid << 0`
18//! - Negative shift amounts are converted to their unsigned equivalent
19//!
20//! # Shift Amount Types
21//!
22//! Shift operations accept any integer type as the shift amount:
23//!
24//! - Unsigned: `u8`, `u16`, `u32`, `u64`, `u128`, `usize`
25//! - Signed: `i8`, `i16`, `i32`, `i64`, `i128`, `isize`
26//! - References to any of the above
27//!
28//! # Supported Operand Combinations
29//!
30//! For each shift amount type `T`:
31//!
32//! - `UUID << T` and `UUID >> T`
33//! - `&UUID << T` and `&UUID >> T`
34//! - `UUID <<= T` and `UUID >>= T`
35//!
36//! # Examples
37//!
38//! ```
39//! use ps_uuid::UUID;
40//!
41//! // Left shift - multiply by powers of 2
42//! let uuid = UUID::from(1u128);
43//! assert_eq!(u128::from(uuid << 4u32), 16);  // 1 * 2^4 = 16
44//! assert_eq!(u128::from(uuid << 8u32), 256); // 1 * 2^8 = 256
45//!
46//! // Right shift - divide by powers of 2
47//! let uuid = UUID::from(256u128);
48//! assert_eq!(u128::from(uuid >> 4u32), 16);  // 256 / 2^4 = 16
49//! assert_eq!(u128::from(uuid >> 8u32), 1);   // 256 / 2^8 = 1
50//!
51//! // Different shift amount types
52//! let uuid = UUID::from(1u128);
53//! let _ = uuid << 4u8;
54//! let _ = uuid << 4u16;
55//! let _ = uuid << 4u32;
56//! let _ = uuid << 4i32;
57//! let _ = uuid << 4usize;
58//!
59//! // Compound assignment
60//! let mut uuid = UUID::from(1u128);
61//! uuid <<= 4u32;
62//! assert_eq!(u128::from(uuid), 16);
63//! ```
64
65use core::ops::{Shl, ShlAssign, Shr, ShrAssign};
66
67use crate::UUID;
68
69use super::shift_amount::ShiftAmount;
70
71// ============================================================================
72// Left Shift
73// ============================================================================
74
75impl<T: ShiftAmount> Shl<T> for UUID {
76    type Output = Self;
77
78    #[inline]
79    fn shl(self, rhs: T) -> Self::Output {
80        Self::from_u128(self.to_u128().wrapping_shl(rhs.as_u32()))
81    }
82}
83
84impl<T: ShiftAmount> Shl<T> for &UUID {
85    type Output = UUID;
86
87    #[inline]
88    fn shl(self, rhs: T) -> Self::Output {
89        UUID::from_u128(self.to_u128().wrapping_shl(rhs.as_u32()))
90    }
91}
92
93impl<T: ShiftAmount> ShlAssign<T> for UUID {
94    #[inline]
95    fn shl_assign(&mut self, rhs: T) {
96        *self = *self << rhs;
97    }
98}
99
100// ============================================================================
101// Right Shift
102// ============================================================================
103
104impl<T: ShiftAmount> Shr<T> for UUID {
105    type Output = Self;
106
107    #[inline]
108    fn shr(self, rhs: T) -> Self::Output {
109        Self::from_u128(self.to_u128().wrapping_shr(rhs.as_u32()))
110    }
111}
112
113impl<T: ShiftAmount> Shr<T> for &UUID {
114    type Output = UUID;
115
116    #[inline]
117    fn shr(self, rhs: T) -> Self::Output {
118        UUID::from_u128(self.to_u128().wrapping_shr(rhs.as_u32()))
119    }
120}
121
122impl<T: ShiftAmount> ShrAssign<T> for UUID {
123    #[inline]
124    fn shr_assign(&mut self, rhs: T) {
125        *self = *self >> rhs;
126    }
127}
128
129// ============================================================================
130// Tests
131// ============================================================================
132
133#[cfg(test)]
134mod tests {
135    #![allow(clippy::op_ref)]
136
137    use super::*;
138
139    // -------------------------------------------------------------------------
140    // Left shift basic tests
141    // -------------------------------------------------------------------------
142
143    #[test]
144    fn shl_by_zero() {
145        let uuid = UUID::from(0b1010_1010u128);
146        assert_eq!(uuid << 0u32, uuid);
147    }
148
149    #[test]
150    fn shl_by_one() {
151        let uuid = UUID::from(1u128);
152        assert_eq!(u128::from(uuid << 1u32), 2);
153    }
154
155    #[test]
156    fn shl_by_four() {
157        let uuid = UUID::from(1u128);
158        assert_eq!(u128::from(uuid << 4u32), 16);
159    }
160
161    #[test]
162    fn shl_by_eight() {
163        let uuid = UUID::from(1u128);
164        assert_eq!(u128::from(uuid << 8u32), 256);
165    }
166
167    #[test]
168    fn shl_pattern() {
169        let uuid = UUID::from(0b1010_1010u128);
170        assert_eq!(u128::from(uuid << 4u32), 0b1010_1010_0000);
171    }
172
173    // -------------------------------------------------------------------------
174    // Right shift basic tests
175    // -------------------------------------------------------------------------
176
177    #[test]
178    fn shr_by_zero() {
179        let uuid = UUID::from(0b1010_1010u128);
180        assert_eq!(uuid >> 0u32, uuid);
181    }
182
183    #[test]
184    fn shr_by_one() {
185        let uuid = UUID::from(2u128);
186        assert_eq!(u128::from(uuid >> 1u32), 1);
187    }
188
189    #[test]
190    fn shr_by_four() {
191        let uuid = UUID::from(256u128);
192        assert_eq!(u128::from(uuid >> 4u32), 16);
193    }
194
195    #[test]
196    fn shr_by_eight() {
197        let uuid = UUID::from(256u128);
198        assert_eq!(u128::from(uuid >> 8u32), 1);
199    }
200
201    #[test]
202    fn shr_pattern() {
203        let uuid = UUID::from(0b1010_1010_0000u128);
204        assert_eq!(u128::from(uuid >> 4u32), 0b1010_1010);
205    }
206
207    // -------------------------------------------------------------------------
208    // Shift amount type tests
209    // -------------------------------------------------------------------------
210
211    #[test]
212    fn shl_with_u8() {
213        let uuid = UUID::from(1u128);
214        assert_eq!(u128::from(uuid << 4u8), 16);
215    }
216
217    #[test]
218    fn shl_with_u16() {
219        let uuid = UUID::from(1u128);
220        assert_eq!(u128::from(uuid << 4u16), 16);
221    }
222
223    #[test]
224    fn shl_with_u32() {
225        let uuid = UUID::from(1u128);
226        assert_eq!(u128::from(uuid << 4u32), 16);
227    }
228
229    #[test]
230    fn shl_with_u64() {
231        let uuid = UUID::from(1u128);
232        assert_eq!(u128::from(uuid << 4u64), 16);
233    }
234
235    #[test]
236    fn shl_with_u128() {
237        let uuid = UUID::from(1u128);
238        assert_eq!(u128::from(uuid << 4u128), 16);
239    }
240
241    #[test]
242    fn shl_with_usize() {
243        let uuid = UUID::from(1u128);
244        assert_eq!(u128::from(uuid << 4usize), 16);
245    }
246
247    #[test]
248    fn shl_with_i8() {
249        let uuid = UUID::from(1u128);
250        assert_eq!(u128::from(uuid << 4i8), 16);
251    }
252
253    #[test]
254    fn shl_with_i16() {
255        let uuid = UUID::from(1u128);
256        assert_eq!(u128::from(uuid << 4i16), 16);
257    }
258
259    #[test]
260    fn shl_with_i32() {
261        let uuid = UUID::from(1u128);
262        assert_eq!(u128::from(uuid << 4i32), 16);
263    }
264
265    #[test]
266    fn shl_with_i64() {
267        let uuid = UUID::from(1u128);
268        assert_eq!(u128::from(uuid << 4i64), 16);
269    }
270
271    #[test]
272    fn shl_with_i128() {
273        let uuid = UUID::from(1u128);
274        assert_eq!(u128::from(uuid << 4i128), 16);
275    }
276
277    #[test]
278    fn shl_with_isize() {
279        let uuid = UUID::from(1u128);
280        assert_eq!(u128::from(uuid << 4isize), 16);
281    }
282
283    // -------------------------------------------------------------------------
284    // Reference variant tests
285    // -------------------------------------------------------------------------
286
287    #[test]
288    fn shl_ref_uuid() {
289        let uuid = UUID::from(1u128);
290        assert_eq!(u128::from(&uuid << 4u32), 16);
291    }
292
293    #[test]
294    fn shr_ref_uuid() {
295        let uuid = UUID::from(16u128);
296        assert_eq!(u128::from(&uuid >> 4u32), 1);
297    }
298
299    #[test]
300    fn shl_with_ref_amount() {
301        let uuid = UUID::from(1u128);
302        let shift = 4u32;
303        assert_eq!(u128::from(uuid << &shift), 16);
304    }
305
306    #[test]
307    fn shl_ref_uuid_ref_amount() {
308        let uuid = UUID::from(1u128);
309        let shift = 4u32;
310        assert_eq!(u128::from(&uuid << &shift), 16);
311    }
312
313    // -------------------------------------------------------------------------
314    // Compound assignment tests
315    // -------------------------------------------------------------------------
316
317    #[test]
318    fn shl_assign() {
319        let mut uuid = UUID::from(1u128);
320        uuid <<= 4u32;
321        assert_eq!(u128::from(uuid), 16);
322    }
323
324    #[test]
325    fn shl_assign_ref_amount() {
326        let mut uuid = UUID::from(1u128);
327        let shift = 4u32;
328        uuid <<= &shift;
329        assert_eq!(u128::from(uuid), 16);
330    }
331
332    #[test]
333    fn shr_assign() {
334        let mut uuid = UUID::from(16u128);
335        uuid >>= 4u32;
336        assert_eq!(u128::from(uuid), 1);
337    }
338
339    #[test]
340    fn shr_assign_ref_amount() {
341        let mut uuid = UUID::from(16u128);
342        let shift = 4u32;
343        uuid >>= &shift;
344        assert_eq!(u128::from(uuid), 1);
345    }
346
347    // -------------------------------------------------------------------------
348    // Wrapping semantics tests
349    // -------------------------------------------------------------------------
350
351    #[test]
352    fn shl_by_128_wraps() {
353        let uuid = UUID::from(0b1010_1010u128);
354        // Shift by 128 is masked to 0, so result is unchanged
355        assert_eq!(uuid << 128u32, uuid);
356    }
357
358    #[test]
359    fn shl_by_127() {
360        let uuid = UUID::from(1u128);
361        // 1 << 127 = 0x8000...0000 (high bit set)
362        assert_eq!(u128::from(uuid << 127u32), 1u128 << 127);
363    }
364
365    #[test]
366    fn shr_by_128_wraps() {
367        let uuid = UUID::from(0b1010_1010u128);
368        // Shift by 128 is masked to 0, so result is unchanged
369        assert_eq!(uuid >> 128u32, uuid);
370    }
371
372    #[test]
373    fn shr_by_127() {
374        let uuid = UUID::from(1u128 << 127);
375        assert_eq!(u128::from(uuid >> 127u32), 1);
376    }
377
378    // -------------------------------------------------------------------------
379    // Shift out / shift in tests
380    // -------------------------------------------------------------------------
381
382    #[test]
383    fn shl_shifts_out_high_bits() {
384        let uuid = UUID::max();
385        // Shifting left loses high bits
386        assert_eq!(u128::from(uuid << 1u32), u128::MAX << 1);
387    }
388
389    #[test]
390    fn shr_shifts_in_zeros() {
391        let uuid = UUID::max();
392        // Shifting right brings in zeros
393        assert_eq!(u128::from(uuid >> 1u32), u128::MAX >> 1);
394    }
395
396    #[test]
397    fn shl_shr_roundtrip_when_no_loss() {
398        let uuid = UUID::from(0b1010_1010u128);
399        // Shift left then right by same amount (no bits lost)
400        assert_eq!((uuid << 4u32) >> 4u32, uuid);
401    }
402
403    #[test]
404    fn shr_shl_not_roundtrip_when_bits_lost() {
405        let uuid = UUID::from(0b1010_1010u128);
406        // Shift right loses low bits, shift left doesn't restore them
407        assert_eq!((uuid >> 4u32) << 4u32, UUID::from(0b1010_0000u128));
408    }
409
410    // -------------------------------------------------------------------------
411    // Boundary tests
412    // -------------------------------------------------------------------------
413
414    #[test]
415    fn shl_nil() {
416        // Shifting zero produces zero
417        assert_eq!(UUID::nil() << 64u32, UUID::nil());
418    }
419
420    #[test]
421    fn shr_nil() {
422        // Shifting zero produces zero
423        assert_eq!(UUID::nil() >> 64u32, UUID::nil());
424    }
425
426    #[test]
427    fn shl_max_by_any_nonzero() {
428        // Shifting max left by any amount produces a different value
429        // (unless masked to 0)
430        let uuid = UUID::max();
431        assert_ne!(uuid << 1u32, uuid);
432    }
433
434    #[test]
435    fn shr_max_by_any_nonzero() {
436        // Shifting max right by any amount produces a different value
437        // (unless masked to 0)
438        let uuid = UUID::max();
439        assert_ne!(uuid >> 1u32, uuid);
440    }
441
442    // -------------------------------------------------------------------------
443    // Negative shift amount tests (wrapping behavior)
444    // -------------------------------------------------------------------------
445
446    #[test]
447    fn shl_negative_amount() {
448        // Negative shift amounts wrap due to the as_u32 conversion
449        // -1i32 as u32 = u32::MAX, which gets masked by wrapping_shl
450        let uuid = UUID::from(1u128);
451        // The exact result depends on how wrapping_shl masks large values
452        let result = uuid << (-1i32);
453        // Verify it doesn't panic and produces some result
454        let _ = u128::from(result);
455    }
456}