use crate::error::SnowflakeError;
#[must_use]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct BitLayout {
pub(crate) timestamp_bits: u8,
pub(crate) machine_id_bits: u8,
pub(crate) node_id_bits: u8,
pub(crate) sequence_bits: u8,
pub(crate) node_id_shift: u8,
pub(crate) machine_id_shift: u8,
pub(crate) timestamp_shift: u8,
}
impl BitLayout {
pub fn new(
timestamp_bits: u8,
machine_id_bits: u8,
node_id_bits: u8,
sequence_bits: u8,
) -> Result<Self, SnowflakeError> {
let total = timestamp_bits
.checked_add(machine_id_bits)
.and_then(|s| s.checked_add(node_id_bits))
.and_then(|s| s.checked_add(sequence_bits))
.unwrap_or(u8::MAX);
if total != 63 {
return Err(SnowflakeError::InvalidBitLayout {
t: timestamp_bits,
m: machine_id_bits,
n: node_id_bits,
s: sequence_bits,
total,
});
}
let node_id_shift = sequence_bits;
let machine_id_shift = node_id_shift + node_id_bits;
let timestamp_shift = machine_id_shift + machine_id_bits;
Ok(Self {
timestamp_bits,
machine_id_bits,
node_id_bits,
sequence_bits,
node_id_shift,
machine_id_shift,
timestamp_shift,
})
}
#[inline]
pub const fn timestamp_bits(&self) -> u8 {
self.timestamp_bits
}
#[inline]
pub const fn machine_id_bits(&self) -> u8 {
self.machine_id_bits
}
#[inline]
pub const fn node_id_bits(&self) -> u8 {
self.node_id_bits
}
#[inline]
pub const fn sequence_bits(&self) -> u8 {
self.sequence_bits
}
#[inline]
pub const fn max_sequence(&self) -> u32 {
(1u32 << self.sequence_bits) - 1
}
#[inline]
pub const fn max_machine_id(&self) -> i64 {
(1i64 << self.machine_id_bits) - 1
}
#[inline]
pub const fn max_node_id(&self) -> i64 {
(1i64 << self.node_id_bits) - 1
}
#[inline]
pub const fn max_timestamp_millis(&self) -> i64 {
(1i64 << self.timestamp_bits) - 1
}
#[inline]
pub fn max_ids_per_second(&self) -> u64 {
(self.max_sequence() as u64 + 1)
* (self.max_machine_id() as u64 + 1)
* (self.max_node_id() as u64 + 1)
* 1_000
}
}
impl Default for BitLayout {
fn default() -> Self {
Self::new(41, 5, 5, 12).expect("default layout is always valid")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_layout_is_valid() {
let l = BitLayout::default();
assert_eq!(l.max_sequence(), 4095);
assert_eq!(l.max_machine_id(), 31);
assert_eq!(l.max_node_id(), 31);
}
#[test]
fn custom_layout_capacities() {
let l = BitLayout::new(38, 8, 7, 10).unwrap();
assert_eq!(l.max_sequence(), 1023);
assert_eq!(l.max_machine_id(), 255);
assert_eq!(l.max_node_id(), 127);
}
#[test]
fn invalid_bit_layout_rejected() {
assert!(BitLayout::new(40, 5, 5, 12).is_err());
}
#[test]
fn max_timestamp_millis() {
let l = BitLayout::default();
assert_eq!(l.max_timestamp_millis(), 2_199_023_255_551);
}
#[test]
fn max_ids_per_second() {
let l = BitLayout::default();
assert_eq!(l.max_ids_per_second(), 4_096 * 32 * 32 * 1_000);
}
#[test]
fn zero_width_fields() {
let l = BitLayout::new(51, 0, 0, 12).unwrap();
assert_eq!(l.max_machine_id(), 0);
assert_eq!(l.max_node_id(), 0);
assert_eq!(l.max_sequence(), 4095);
}
#[test]
fn max_sequence_bits_after_widening() {
let l = BitLayout::new(16, 8, 8, 31).unwrap();
assert_eq!(l.max_sequence(), (1u32 << 31) - 1);
}
}