use std::sync::atomic::{AtomicI32, Ordering};
use super::ids::{OrderId, RequestId, REQUEST_ID_CEILING, REQUEST_ID_FLOOR};
use crate::Error;
#[derive(Debug)]
pub(crate) struct IdGenerator {
next_id: AtomicI32,
}
impl IdGenerator {
pub(crate) fn new(start: i32) -> Self {
Self {
next_id: AtomicI32::new(start),
}
}
pub(crate) fn new_request_id_generator() -> Self {
Self::new(REQUEST_ID_FLOOR)
}
pub(crate) fn new_order_id_generator(start: i32) -> Self {
Self::new(start)
}
pub(crate) fn next(&self) -> i32 {
self.next_id.fetch_add(1, Ordering::Relaxed)
}
pub(crate) fn next_up_to(&self, max: i32) -> Option<i32> {
self.next_id
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |id| (id <= max).then(|| id + 1))
.ok()
}
#[allow(dead_code)]
pub(crate) fn current(&self) -> i32 {
self.next_id.load(Ordering::Relaxed)
}
pub(crate) fn raise(&self, value: i32) {
self.next_id.fetch_max(value, Ordering::Relaxed);
}
}
impl Default for IdGenerator {
fn default() -> Self {
Self::new(0)
}
}
#[derive(Debug)]
pub(crate) struct ClientIdManager {
request_ids: IdGenerator,
order_ids: IdGenerator,
}
impl ClientIdManager {
pub(crate) fn new(initial_order_id: i32) -> Result<Self, Error> {
if OrderId::from(initial_order_id).checked().is_err() {
return Err(Error::ConnectionRejected(format!(
"server's next valid order id {initial_order_id} is at or above {REQUEST_ID_FLOOR}, which is reserved for request ids"
)));
}
Ok(Self {
request_ids: IdGenerator::new_request_id_generator(),
order_ids: IdGenerator::new_order_id_generator(initial_order_id),
})
}
pub(crate) fn next_request_id(&self) -> RequestId {
self.request_ids
.next_up_to(REQUEST_ID_CEILING)
.and_then(RequestId::from_raw)
.unwrap_or_else(|| {
log::error!("request ids exhausted at {REQUEST_ID_CEILING}");
panic!("request ids exhausted at {REQUEST_ID_CEILING}")
})
}
pub(crate) fn next_order_id(&self) -> OrderId {
OrderId::from(self.order_ids.next())
}
pub(crate) fn raise_order_id(&self, order_id: OrderId) {
self.order_ids.raise(order_id.value());
}
pub(crate) fn raise_order_id_from_server(&self, next_valid_id: i32) {
let order_id = OrderId::from(next_valid_id);
if order_id.checked().is_err() {
log::error!("server's next valid order id {next_valid_id} is at or above {REQUEST_ID_FLOOR}, which is reserved for request ids; orders will be rejected");
}
self.raise_order_id(order_id);
}
#[allow(dead_code)]
pub(crate) fn current_order_id(&self) -> i32 {
self.order_ids.current()
}
#[allow(dead_code)]
pub(crate) fn current_request_id(&self) -> i32 {
self.request_ids.current()
}
}
#[cfg(test)]
#[path = "id_generator_tests.rs"]
mod tests;