Skip to main content

grain_id/
core.rs

1use core::{fmt::Display, str::FromStr};
2
3use crate::{
4    Error,
5    alphabet::{BASE, u5_to_char_lossy},
6};
7
8/// Grain id struct
9///
10/// # Examples
11#[cfg_attr(feature = "default", doc = "```rust")]
12#[cfg_attr(not(feature = "default"), doc = "```ignore")]
13/// # use grain_id::*;
14/// # fn main() -> Result<(), Error> {
15/// // Generate random value.
16/// let random = GrainId::random();
17///
18/// assert_ne!(random, GrainId::NIL);
19///
20/// // Parse from string.
21/// let from_str: GrainId = "012abcd".parse()?;
22///
23/// // Parse from integer.
24/// let from_int: GrainId = 35990925u64.try_into()?;
25///
26/// assert_eq!(from_str, from_int);
27/// # Ok(())
28/// # }
29/// ```
30#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
31pub struct GrainId(u64);
32
33impl GrainId {
34    /// The size of the integer type in bits.
35    ///
36    /// This is not equal actually stored size.
37    pub const BITS: u32 = 35;
38
39    /// The capacity value of the grain id
40    #[deprecated(since = "0.8.1")]
41    pub const CAPACITY: u64 = (BASE as u64).pow(7);
42
43    const MAX_VALUE_PLUS_ONE: u64 = (BASE as u64).pow(7);
44    const MAX_VALUE: u64 = Self::MAX_VALUE_PLUS_ONE - 1;
45
46    const fn from_u64_unchecked(value: u64) -> Self {
47        Self(value)
48    }
49
50    /// The smallest value that can be represented by [`GrainId`].
51    ///
52    /// # Examples
53    ///
54    /// ```
55    /// # use grain_id::*;
56    /// # fn main() -> Result<(), Error> {
57    /// assert_eq!(GrainId::NIL, "0000000".parse::<GrainId>()?);
58    /// assert_eq!(GrainId::NIL, GrainId::try_from(0)?);
59    /// # Ok(())
60    /// # }
61    /// ```
62    pub const NIL: Self = Self::from_u64_unchecked(0);
63
64    /// The largest value that can be represented by [`GrainId`].
65    ///
66    /// # Examples
67    ///
68    /// ```
69    /// # use grain_id::*;
70    /// # fn main() -> Result<(), Error> {
71    /// assert_eq!(GrainId::MAX, "zzzzzzz".parse::<GrainId>()?);
72    /// assert_eq!(GrainId::MAX, GrainId::try_from(GrainId::CAPACITY - 1)?);
73    /// # Ok(())
74    /// # }
75    /// ```
76    pub const MAX: Self = Self::from_u64_unchecked(Self::MAX_VALUE);
77
78    /// Test if the [`GrainId`] is nil.
79    ///
80    /// # Examples
81    ///
82    /// ```
83    /// # use grain_id::*;
84    /// # fn main() -> Result<(), Error> {
85    /// assert!("0000000".parse::<GrainId>()?.is_nil());
86    /// # Ok(())
87    /// # }
88    /// ```
89    pub fn is_nil(&self) -> bool {
90        self == &Self::NIL
91    }
92
93    /// Test if the [`GrainId`] is max.
94    ///
95    /// # Examples
96    ///
97    /// ```
98    /// # use grain_id::*;
99    /// # fn main() -> Result<(), Error> {
100    /// assert!("zzzzzzz".parse::<GrainId>()?.is_max());
101    /// # Ok(())
102    /// # }
103    /// ```
104    pub fn is_max(&self) -> bool {
105        self == &Self::MAX
106    }
107
108    /// "Converts an unsigned integer to [`GrainId`] by truncating bits that exceed the valid range.")]
109    ///
110    /// This is a lossy conversion that masks the input value to fit within the ID's bit limit.
111    /// If you need to detect out-of-range values, use [`TryFrom`] instead.
112    ///
113    /// # Examples
114    ///
115    /// ```
116    /// # use grain_id::*;
117    /// // Values within range are preserved
118    /// let valid_int = 123;
119    /// let id = GrainId::from_u64_lossy(valid_int);
120    /// assert_eq!(u64::from(id), valid_int);
121    ///
122    /// // values exceeding 35 bits are truncated (MSB(s) dropped
123    /// let oversized_int = valid_int + GrainId::CAPACITY;
124    /// let overflowed_id = GrainId::from_u64_lossy(oversized_int);
125    /// assert_ne!(u64::from(overflowed_id), oversized_int);
126    /// // Only lower 35 bits retained
127    /// assert_eq!(u64::from(overflowed_id), valid_int)
128    /// ```
129    pub const fn from_u64_lossy(int: u64) -> Self {
130        Self::from_u64_unchecked(int & Self::MAX_VALUE)
131    }
132
133    /// Attempts to convert a [`u64`]  to [`GrainId`].
134    ///
135    ///
136    /// Return error if the value is larger than [`GrainId::MAX`].
137    /// If you don't need to detect out-of-range values, use [`from_uint_lossy`](Self::from_uint_lossy).
138    ///
139    /// # Examples
140    ///
141    /// ```
142    /// # use grain_id::*;
143    /// # fn main() -> Result<(), Error> {
144    /// assert_eq!(GrainId::from_u64(0x7FFFFFFFF)?, GrainId::MAX);
145    /// assert!(GrainId::from_u64(0x800000000).is_err());
146    /// # Ok(())
147    /// # }
148    /// ```
149    pub const fn from_u64(value: u64) -> Result<Self, crate::Error> {
150        if value < Self::MAX_VALUE_PLUS_ONE {
151            Ok(Self::from_u64_unchecked(value))
152        } else {
153            Err(Error::ValueOutOfRange(value))
154        }
155    }
156
157    /// Returns a reference of internal 64bit integer.
158    ///
159    /// # Examples
160    ///
161    /// ```
162    /// # use grain_id::*;
163    /// # fn main() -> Result<(), Error> {
164    /// let id = GrainId::from_u64(0x123456789)?;
165    /// assert_eq!(id.as_u64(), &0x123456789);
166    /// # Ok(())
167    /// # }
168    /// ```
169    pub const fn as_u64(&self) -> &u64 {
170        &self.0
171    }
172    /// Returns an internal 64bit integer value.
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// # use grain_id::*;
178    /// # fn main() -> Result<(), Error> {
179    /// let id = GrainId::from_u64(0x123456789)?;
180    /// assert_eq!(id.to_u64(), 0x123456789);
181    /// # Ok(())
182    /// # }
183    /// ```
184    pub const fn to_u64(self) -> u64 {
185        self.0
186    }
187
188    fn from_chars(value: [char; 7]) -> Result<Self, Error> {
189        Ok(Self::from_u64_unchecked(u5s_to_u35_unchecked(
190            chars_to_u5s(value)?,
191        )))
192    }
193
194    pub(crate) fn to_chars(self) -> [char; 7] {
195        [
196            u5_to_char_lossy((self.0 >> 30) as u8),
197            u5_to_char_lossy((self.0 >> 25) as u8),
198            u5_to_char_lossy((self.0 >> 20) as u8),
199            u5_to_char_lossy((self.0 >> 15) as u8),
200            u5_to_char_lossy((self.0 >> 10) as u8),
201            u5_to_char_lossy((self.0 >> 5) as u8),
202            u5_to_char_lossy((self.0) as u8),
203        ]
204    }
205
206    /// Creates a `GrainId` from a [`Duration`](std::time::Duration) based on decisecond precision.
207    ///
208    /// This method converts the given duration into deciseconds (0.1 second units)
209    /// and generates a `GrainId` from it. The resulting 35-bit identifier can
210    /// represent approximately 109 years worth of deciseconds.
211    ///
212    /// # Timestamp Format
213    ///
214    /// The entire 35 bits are used to represent the timestamp in deciseconds,
215    /// allowing for chronological sorting while maintaining a compact 7-character
216    /// BASE32 representation.
217    ///
218    /// # Overflow Behavior
219    ///
220    /// If the duration exceeds the maximum representable value (2^35 deciseconds,
221    /// approximately 109 years), the timestamp will wrap around. This is generally
222    /// not a concern for personal applications within a human lifespan.
223    ///
224    /// # Examples
225    ///
226    /// ```
227    /// # use grain_id::*;
228    /// use std::time::Duration;
229    ///
230    /// # fn main() -> Result<(), Error> {
231    /// let duration = Duration::new(5, 730_023_852);
232    /// let id = GrainId::from_duration(duration);
233    /// assert_eq!(id, GrainId::from_u64(57)?);
234    /// # Ok(())
235    /// # }
236    /// ```
237    pub fn from_duration(duration: core::time::Duration) -> Self {
238        let decisecs = duration.as_millis() / 100;
239        Self::from_u64_lossy(decisecs as u64)
240    }
241
242    #[deprecated(since = "0.15.0", note = "Use `to_u64().to_be_bytes()` instead.")]
243    #[doc = crate::macros::doc_to_bytes!("big endian")]
244    ///
245    /// # Examples
246    ///
247    /// ```
248    /// # use grain_id::*;
249    /// # fn main() -> Result<(), Error> {
250    /// let bytes = GrainId::from_u64(0x123456789)?.to_be_bytes();
251    /// assert_eq!(bytes, [0, 0, 0, 0x01, 0x23, 0x45, 0x67, 0x89]);
252    /// # Ok(())
253    /// # }
254    /// ```
255    pub const fn to_be_bytes(self) -> [u8; 8] {
256        self.0.to_be_bytes()
257    }
258
259    #[deprecated(since = "0.15.0", note = "Use `to_u64().to_le_bytes()` instead.")]
260    #[doc = crate::macros::doc_to_bytes!("little endian")]
261    ///
262    /// # Examples
263    ///
264    /// ```
265    /// # use grain_id::*;
266    /// # fn main() -> Result<(), Error> {
267    /// let bytes = GrainId::from_u64(0x123456789)?.to_le_bytes();
268    /// assert_eq!(bytes, [0x89, 0x67, 0x45, 0x23, 0x01, 0, 0, 0]);
269    /// # Ok(())
270    /// # }
271    /// ```
272    pub const fn to_le_bytes(self) -> [u8; 8] {
273        self.0.to_le_bytes()
274    }
275
276    #[deprecated(
277        since = "0.15.0",
278        note = "Use `GrainId::from_u64(u64::from_be_bytes(bytes))?` instead."
279    )]
280    #[doc = crate::macros::doc_from_bytes!("big endian", Self::from_be_bytes_lossy)]
281    ///
282    /// # Examples
283    ///
284    /// ```
285    /// # use grain_id::*;
286    /// # fn main() -> Result<(), Error> {
287    /// let value = [0, 0, 0, 0x01, 0x23, 0x45, 0x67, 0x89];
288    /// assert_eq!(
289    ///     GrainId::from_be_bytes(value)?,
290    ///     GrainId::from_u64(0x123456789)?
291    /// );
292    ///
293    /// // Oversized value returns error
294    /// let oversized_value = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
295    /// assert!(GrainId::from_be_bytes(oversized_value).is_err());
296    /// # Ok(())
297    /// # }
298    /// ```
299    pub fn from_be_bytes(bytes: [u8; 8]) -> Result<Self, Error> {
300        Self::from_u64(u64::from_be_bytes(bytes))
301    }
302
303    #[deprecated(
304        since = "0.15.0",
305        note = "Use `GrainId::from_u64(u64::from_le_bytes(bytes))?` instead."
306    )]
307    #[doc = crate::macros::doc_from_bytes!("little endian", Self::from_le_bytes_lossy)]
308    ///
309    /// # Examples
310    ///
311    /// ```
312    /// # use grain_id::*;
313    /// # fn main() -> Result<(), Error> {
314    /// let value = [0x89, 0x67, 0x45, 0x23, 0x01, 0, 0, 0];
315    /// assert_eq!(
316    ///     GrainId::from_le_bytes(value)?,
317    ///     GrainId::from_u64(0x123456789)?
318    /// );
319    ///
320    /// // Oversized value returns error
321    /// let oversized_value = [0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01];
322    /// assert!(GrainId::from_le_bytes(oversized_value).is_err());
323    /// # Ok(())
324    /// # }
325    /// ```
326    pub fn from_le_bytes(bytes: [u8; 8]) -> Result<Self, Error> {
327        Self::from_u64(u64::from_le_bytes(bytes))
328    }
329
330    #[deprecated(
331        since = "0.15.0",
332        note = "Use `GrainId::from_u64_lossy(u64::from_be_bytes(bytes))` instead."
333    )]
334    #[doc = crate::macros::doc_from_bytes_lossy!("big endian")]
335    ///
336    /// # Examples
337    ///
338    /// ```
339    /// # use grain_id::*;
340    /// # fn main() -> Result<(), Error> {
341    ///
342    /// let value = [0, 0, 0, 0x01, 0x23, 0x45, 0x67, 0x89];
343    /// assert_eq!(
344    ///     GrainId::from_be_bytes_lossy(value),
345    ///     GrainId::from_u64(0x123456789)?
346    /// );
347    ///
348    /// // Oversized value will be truncated.
349    /// let oversized_value = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
350    /// assert_eq!(
351    ///     GrainId::from_be_bytes_lossy(oversized_value),
352    ///     GrainId::from_u64(0x789abcdef)?
353    /// );
354    /// # Ok(())
355    /// # }
356    /// ```
357    pub fn from_be_bytes_lossy(bytes: [u8; 8]) -> Self {
358        Self::from_u64_lossy(u64::from_be_bytes(bytes))
359    }
360    #[deprecated(
361        since = "0.15.0",
362        note = "Use `GrainId::from_u64_lossy(u64::from_le_bytes(bytes))` instead."
363    )]
364    #[doc = crate::macros::doc_from_bytes_lossy!("little endian")]
365    ///
366    /// # Examples
367    ///
368    /// ```
369    /// # use grain_id::*;
370    /// # fn main() -> Result<(), Error> {
371    /// let value = [0x89, 0x67, 0x45, 0x23, 0x01, 0, 0, 0];
372    /// assert_eq!(
373    ///     GrainId::from_le_bytes_lossy(value),
374    ///     GrainId::from_u64(0x123456789)?
375    /// );
376    ///
377    /// // Oversized value will be truncated.
378    /// let oversized_value = [0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01];
379    /// assert_eq!(
380    ///     GrainId::from_le_bytes_lossy(oversized_value),
381    ///     GrainId::from_u64(0x789abcdef)?
382    /// );
383    /// # Ok(())
384    /// # }
385    /// ```
386    pub fn from_le_bytes_lossy(bytes: [u8; 8]) -> Self {
387        Self::from_u64_lossy(u64::from_le_bytes(bytes))
388    }
389
390    #[deprecated(since = "0.15.0", note = "Use `to_byte_suffix()` instead.")]
391    #[doc = crate::macros::doc_to_bytes!("big endian", compact)]
392    ///
393    /// # Examples
394    ///
395    /// ```
396    /// # use grain_id::*;
397    /// # fn main() -> Result<(), Error> {
398    /// let bytes = GrainId::from_u64(0x123456789)?.to_be_bytes_compact();
399    /// assert_eq!(bytes, [0x01, 0x23, 0x45, 0x67, 0x89]);
400    /// # Ok(())
401    /// # }
402    /// ```
403    pub const fn to_be_bytes_compact(self) -> [u8; 5] {
404        let bytes = self.0.to_be_bytes();
405        [bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]]
406    }
407
408    #[deprecated(
409        since = "0.15.0",
410        note = "Use `to_u64().to_le_bytes()` for an 8-byte little-endian representation."
411    )]
412    #[doc = crate::macros::doc_to_bytes!("little endian", compact)]
413    ///
414    /// # Examples
415    ///
416    /// ```
417    /// # use grain_id::*;
418    /// # fn main() -> Result<(), Error> {
419    /// let bytes = GrainId::from_u64(0x123456789)?.to_le_bytes_compact();
420    /// assert_eq!(bytes, [0x89, 0x67, 0x45, 0x23, 0x01]);
421    /// # Ok(())
422    /// # }
423    /// ```
424    pub const fn to_le_bytes_compact(self) -> [u8; 5] {
425        let bytes = self.0.to_le_bytes();
426        [bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]]
427    }
428
429    #[deprecated(
430        since = "0.15.0",
431        note = "Use `from_byte_suffix()` for lossy conversion, or `GrainId::from_u64(u64::from_be_bytes([0, 0, 0, bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]]))?` for checked conversion."
432    )]
433    #[doc = crate::macros::doc_from_bytes!("big endian", compact)]
434    ///
435    /// # Examples
436    ///
437    /// ```
438    /// # use grain_id::*;
439    /// # fn main() -> Result<(), Error> {
440    /// let value = [0x01, 0x23, 0x45, 0x67, 0x89];
441    /// assert_eq!(
442    ///     GrainId::from_be_bytes_compact(value)?,
443    ///     GrainId::from_u64(0x123456789)?
444    /// );
445    ///
446    /// // Overflowed value returns error.
447    /// let oversized_value = [0x12, 0x34, 0x56, 0x78, 0x90];
448    /// assert!(GrainId::from_be_bytes_compact(oversized_value).is_err());
449    /// # Ok(())
450    /// # }
451    /// ```
452    pub fn from_be_bytes_compact(bytes: [u8; 5]) -> Result<Self, Error> {
453        Self::from_u64(u64::from_be_bytes([
454            0, 0, 0, bytes[0], bytes[1], bytes[2], bytes[3], bytes[4],
455        ]))
456    }
457
458    #[deprecated(
459        since = "0.15.0",
460        note = "Use `GrainId::from_u64(u64::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], 0, 0, 0]))?` instead."
461    )]
462    #[doc = crate::macros::doc_from_bytes!("little endian", compact)]
463    ///
464    /// # Examples
465    ///
466    /// ```
467    /// # use grain_id::*;
468    /// # fn main() -> Result<(), Error> {
469    /// let value = [0x89, 0x67, 0x45, 0x23, 0x01];
470    /// assert_eq!(
471    ///     GrainId::from_le_bytes_compact(value)?,
472    ///     GrainId::from_u64(0x123456789)?
473    /// );
474    ///
475    /// // Oversized value returns error.
476    /// let oversized_value = [0x90, 0x78, 0x56, 0x34, 0x12];
477    /// assert!(GrainId::from_le_bytes_compact(oversized_value).is_err());
478    /// # Ok(())
479    /// # }
480    /// ```
481    pub fn from_le_bytes_compact(bytes: [u8; 5]) -> Result<Self, Error> {
482        Self::from_u64(u64::from_le_bytes([
483            bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], 0, 0, 0,
484        ]))
485    }
486
487    #[deprecated(since = "0.15.0", note = "Use `from_byte_suffix()` instead.")]
488    #[doc = crate::macros::doc_from_bytes_lossy!("big endian", compact)]
489    ///
490    /// # Examples
491    ///
492    /// ```
493    /// # use grain_id::*;
494    /// # fn main() -> Result<(), Error> {
495    /// let value = [0x01, 0x23, 0x45, 0x67, 0x89];
496    /// assert_eq!(
497    ///     GrainId::from_be_bytes_compact_lossy(value),
498    ///     GrainId::from_u64(0x123456789)?
499    /// );
500    ///
501    /// // Oversized value will be truncated.
502    /// let oversized_value = [0x12, 0x34, 0x56, 0x78, 0x90];
503    /// assert_eq!(
504    ///     GrainId::from_be_bytes_compact_lossy(oversized_value),
505    ///     GrainId::from_u64(0x234567890)?
506    /// );
507    /// # Ok(())
508    /// # }
509    /// ```
510    pub fn from_be_bytes_compact_lossy(bytes: [u8; 5]) -> Self {
511        Self::from_u64_lossy(u64::from_be_bytes([
512            0, 0, 0, bytes[0], bytes[1], bytes[2], bytes[3], bytes[4],
513        ]))
514    }
515
516    #[deprecated(
517        since = "0.15.0",
518        note = "Use `GrainId::from_u64_lossy(u64::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], 0, 0, 0]))` instead."
519    )]
520    #[doc = crate::macros::doc_from_bytes_lossy!("little endian", compact)]
521    ///
522    /// # Examples
523    ///
524    /// ```
525    /// # use grain_id::*;
526    /// # fn main() -> Result<(), Error> {
527    /// let value = [0x89, 0x67, 0x45, 0x23, 0x01];
528    /// assert_eq!(
529    ///     GrainId::from_le_bytes_compact_lossy(value),
530    ///     GrainId::from_u64(0x123456789)?
531    /// );
532    ///
533    /// // Oversized value will be truncated
534    /// let oversized_value = [0x90, 0x78, 0x56, 0x34, 0x12];
535    /// assert_eq!(
536    ///     GrainId::from_le_bytes_compact_lossy(oversized_value),
537    ///     GrainId::from_u64(0x234567890)?
538    /// );
539    /// # Ok(())
540    /// # }
541    /// ```
542    pub fn from_le_bytes_compact_lossy(bytes: [u8; 5]) -> Self {
543        Self::from_u64_lossy(u64::from_le_bytes([
544            bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], 0, 0, 0,
545        ]))
546    }
547
548    /// Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around at the boundary of the type.
549    ///
550    /// # Examples
551    /// ```
552    /// # use grain_id::*;
553    /// # fn main() -> Result<(), Error> {
554    /// assert_eq!(
555    ///     GrainId::from_u64(100)?.wrapping_sub(GrainId::from_u64(100)?),
556    ///     GrainId::NIL
557    /// );
558    /// assert_eq!(
559    ///     GrainId::from_u64(100)?.wrapping_sub(GrainId::from_u64(101)?),
560    ///     GrainId::MAX
561    /// );
562    /// # Ok(())
563    /// # }
564    /// ```
565    pub const fn wrapping_sub(self, rhs: Self) -> Self {
566        GrainId::from_u64_lossy(self.to_u64().wrapping_sub(rhs.to_u64()))
567    }
568
569    /// Creates a `GrainId` from the leading (most significant) 35 bits of a 5-byte array.
570    ///
571    /// This is useful for generating a compact identifier from a pre-computed hash,
572    /// public key, or any arbitrary byte sequence.
573    ///
574    /// # Examples
575    ///
576    /// ```
577    /// # use grain_id::*;
578    /// // Extract leading 35 bits from a byte array
579    /// let bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
580    /// let id = GrainId::from_byte_prefix(&bytes);
581    /// assert_eq!(id, GrainId::MAX);
582    ///
583    /// // For longer data, slice the first 5 bytes
584    /// let hash: [u8; 32] = [0; 32];
585    /// let id = GrainId::from_byte_prefix(hash[..5].try_into().unwrap());
586    /// assert_eq!(id, GrainId::NIL);
587    /// ```
588    pub const fn from_byte_prefix(bytes: &[u8; 5]) -> Self {
589        let value = (bytes[0] as u64) << 27
590            | (bytes[1] as u64) << 19
591            | (bytes[2] as u64) << 11
592            | (bytes[3] as u64) << 3
593            | (bytes[4] as u64) >> 5;
594        Self::from_u64_lossy(value)
595    }
596
597    /// Creates a `GrainId` from the trailing (least significant) 35 bits of a 5-byte array.
598    ///
599    /// This is the counterpart to [`from_byte_prefix`](Self::from_byte_prefix).
600    /// The upper 5 bits of the first byte are ignored.
601    ///
602    /// # Examples
603    ///
604    /// ```
605    /// # use grain_id::*;
606    /// // Extract trailing 35 bits from a byte array
607    /// let bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
608    /// let id = GrainId::from_byte_suffix(&bytes);
609    /// assert_eq!(id, GrainId::MAX);
610    ///
611    /// // Upper 5 bits of the first byte are ignored
612    /// let bytes = [0xF8, 0x00, 0x00, 0x00, 0x00];
613    /// let id = GrainId::from_byte_suffix(&bytes);
614    /// assert_eq!(id, GrainId::NIL);
615    /// ```
616    pub const fn from_byte_suffix(bytes: &[u8; 5]) -> Self {
617        let value = ((bytes[0] & 0x07) as u64) << 32
618            | (bytes[1] as u64) << 24
619            | (bytes[2] as u64) << 16
620            | (bytes[3] as u64) << 8
621            | (bytes[4] as u64);
622        Self::from_u64_unchecked(value)
623    }
624
625    /// Returns a 5-byte array with the `GrainId` value stored in the leading (most significant) 35 bits.
626    ///
627    /// This is the counterpart to [`from_byte_prefix`](Self::from_byte_prefix).
628    /// The lower 5 bits of the last byte are always zero.
629    ///
630    /// # Examples
631    ///
632    /// ```
633    /// # use grain_id::*;
634    /// # fn main() -> Result<(), Error> {
635    /// let bytes = GrainId::from_u64(0x123456789)?.to_byte_prefix();
636    /// assert_eq!(bytes, [0x24, 0x68, 0xAC, 0xF1, 0x20]);
637    ///
638    /// // Round-trip
639    /// let id = GrainId::from_u64(0x123456789)?;
640    /// assert_eq!(GrainId::from_byte_prefix(&id.to_byte_prefix()), id);
641    /// # Ok(())
642    /// # }
643    /// ```
644    pub const fn to_byte_prefix(self) -> [u8; 5] {
645        let v = self.0;
646        [
647            (v >> 27) as u8,
648            (v >> 19) as u8,
649            (v >> 11) as u8,
650            (v >> 3) as u8,
651            ((v & 0x07) << 5) as u8,
652        ]
653    }
654
655    /// Returns a 5-byte array with the `GrainId` value stored in the trailing (least significant) 35 bits.
656    ///
657    /// This is the counterpart to [`from_byte_suffix`](Self::from_byte_suffix).
658    /// The upper 5 bits of the first byte are always zero.
659    ///
660    /// # Examples
661    ///
662    /// ```
663    /// # use grain_id::*;
664    /// # fn main() -> Result<(), Error> {
665    /// let bytes = GrainId::from_u64(0x123456789)?.to_byte_suffix();
666    /// assert_eq!(bytes, [0x01, 0x23, 0x45, 0x67, 0x89]);
667    ///
668    /// // Round-trip
669    /// let id = GrainId::from_u64(0x123456789)?;
670    /// assert_eq!(GrainId::from_byte_suffix(&id.to_byte_suffix()), id);
671    /// # Ok(())
672    /// # }
673    /// ```
674    pub const fn to_byte_suffix(self) -> [u8; 5] {
675        let v = self.0;
676        [
677            (v >> 32) as u8,
678            (v >> 24) as u8,
679            (v >> 16) as u8,
680            (v >> 8) as u8,
681            v as u8,
682        ]
683    }
684
685    /// Increments the value by 1, wrapping around to [`GrainId::NIL`] on overflow.
686    ///
687    /// # Examples
688    /// ```
689    /// # use grain_id::*;
690    /// # fn main() -> Result<(), Error> {
691    /// assert_eq!(GrainId::from_u64(100)?.increment(), GrainId::from_u64(101)?);
692    /// assert_eq!(GrainId::MAX.increment(), GrainId::NIL);
693    /// # Ok(())
694    /// # }
695    /// ```
696    pub const fn increment(self) -> Self {
697        GrainId::from_u64_lossy(self.to_u64() + 1)
698    }
699}
700
701fn chars_to_u5s(value: [char; 7]) -> Result<[u8; 7], Error> {
702    let mut result = [0; 7];
703    for i in 0..7 {
704        result[i] = crate::alphabet::char_to_u5(value[i]).ok_or(Error::InvalidCharacter {
705            character: value[i],
706            index: i,
707        })?;
708    }
709    Ok(result)
710}
711
712fn u5s_to_u35_unchecked(value: [u8; 7]) -> u64 {
713    ((value[0] as u64) << 30)
714        | ((value[1] as u64) << 25)
715        | ((value[2] as u64) << 20)
716        | ((value[3] as u64) << 15)
717        | ((value[4] as u64) << 10)
718        | ((value[5] as u64) << 5)
719        | (value[6] as u64)
720}
721
722impl AsRef<u64> for GrainId {
723    fn as_ref(&self) -> &u64 {
724        self.as_u64()
725    }
726}
727
728impl TryFrom<u64> for GrainId {
729    type Error = Error;
730    /// Attempts to convert a [`u64`]  to [`GrainId`].
731    ///
732    /// # Error
733    /// Return error if the value is larger than [`GrainId::MAX`].
734    /// If you don't need to detect out-of-range values, use [`from_u64_lossy`](GrainId::from_u64_lossy).
735    ///
736    /// # Examples
737    ///
738    /// ```
739    /// # use grain_id::*;
740    /// assert!(GrainId::try_from(GrainId::CAPACITY - 1).is_ok());
741    /// assert!(GrainId::try_from(GrainId::CAPACITY).is_err());
742    /// ```
743    fn try_from(value: u64) -> Result<Self, Self::Error> {
744        Self::from_u64(value)
745    }
746}
747
748impl From<GrainId> for u64 {
749    fn from(value: GrainId) -> Self {
750        value.0
751    }
752}
753
754impl Display for GrainId {
755    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
756        let chars = self.to_chars();
757        write!(
758            f,
759            "{}{}{}{}{}{}{}",
760            chars[0], chars[1], chars[2], chars[3], chars[4], chars[5], chars[6]
761        )
762    }
763}
764
765impl FromStr for GrainId {
766    type Err = Error;
767
768    fn from_str(s: &str) -> Result<Self, Self::Err> {
769        let len = s.len();
770        if len == 7 {
771            let mut chars = s.chars();
772            Self::from_chars([
773                chars.next().unwrap(),
774                chars.next().unwrap(),
775                chars.next().unwrap(),
776                chars.next().unwrap(),
777                chars.next().unwrap(),
778                chars.next().unwrap(),
779                chars.next().unwrap(),
780            ])
781        } else {
782            Err(Error::InvalidLength(len))
783        }
784    }
785}