use sea_query::Value;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ShardRoute {
Global,
Key(u64),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ShardCodec {
shard_bits: u32,
}
impl ShardCodec {
pub const DEFAULT: ShardCodec = ShardCodec::new(12);
pub const fn new(shard_bits: u32) -> Self {
Self { shard_bits }
}
pub const fn seq_bits(&self) -> u32 {
64 - self.shard_bits
}
pub const fn shard_of(&self, id: u64) -> u32 {
(id >> self.seq_bits()) as u32
}
pub const fn local_of(&self, id: u64) -> u64 {
id & ((1u64 << self.seq_bits()) - 1)
}
pub const fn compose(&self, shard: u32, local: u64) -> u64 {
((shard as u64) << self.seq_bits()) | (local & ((1u64 << self.seq_bits()) - 1))
}
}
pub fn stable_hash(bytes: &[u8]) -> u64 {
let mut h = 0xcbf2_9ce4_8422_2325u64;
for &b in bytes {
h ^= b as u64;
h = h.wrapping_mul(0x0000_0100_0000_01b3);
}
h
}
pub(crate) fn routing_key(v: &Value) -> Option<u64> {
use sea_query::Value as V;
Some(match v {
V::TinyInt(Some(n)) => *n as u64,
V::SmallInt(Some(n)) => *n as u64,
V::Int(Some(n)) => *n as u64,
V::BigInt(Some(n)) => *n as u64,
V::TinyUnsigned(Some(n)) => *n as u64,
V::SmallUnsigned(Some(n)) => *n as u64,
V::Unsigned(Some(n)) => *n as u64,
V::BigUnsigned(Some(n)) => *n,
V::String(Some(s)) => stable_hash(s.as_bytes()),
V::Char(Some(c)) => stable_hash(c.encode_utf8(&mut [0u8; 4]).as_bytes()),
_ => return None,
})
}
pub(crate) fn route_for<E: crate::Entity>(column: &str, value: &Value) -> ShardRoute {
if E::shard_columns().contains(&column) {
if let Some(k) = routing_key(value) {
return ShardRoute::Key(k);
}
}
ShardRoute::Global
}