mango-v4/programs/mango-v4/src/state/orderbook/nodes.rs

270 lines
7.1 KiB
Rust

use std::mem::size_of;
use anchor_lang::prelude::*;
use bytemuck::{cast_mut, cast_ref};
use mango_macro::Pod;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use static_assertions::const_assert_eq;
use super::order_type::OrderType;
pub type NodeHandle = u32;
const NODE_SIZE: usize = 88;
#[derive(IntoPrimitive, TryFromPrimitive)]
#[repr(u32)]
pub enum NodeTag {
Uninitialized = 0,
InnerNode = 1,
LeafNode = 2,
FreeNode = 3,
LastFreeNode = 4,
}
/// InnerNodes and LeafNodes compose the binary tree of orders.
///
/// Each InnerNode has exactly two children, which are either InnerNodes themselves,
/// or LeafNodes. The children share the top `prefix_len` bits of `key`. The left
/// child has a 0 in the next bit, and the right a 1.
#[derive(Copy, Clone, Pod)]
#[repr(C)]
pub struct InnerNode {
pub tag: u32,
/// number of highest `key` bits that all children share
/// e.g. if it's 2, the two highest bits of `key` will be the same on all children
pub prefix_len: u32,
/// only the top `prefix_len` bits of `key` are relevant
pub key: i128,
/// indexes into `BookSide::nodes`
pub children: [NodeHandle; 2],
/// The earliest expiry timestamp for the left and right subtrees.
///
/// Needed to be able to find and remove expired orders without having to
/// iterate through the whole bookside.
pub child_earliest_expiry: [u64; 2],
pub reserve: [u8; NODE_SIZE - 48],
}
impl InnerNode {
pub fn new(prefix_len: u32, key: i128) -> Self {
Self {
tag: NodeTag::InnerNode.into(),
prefix_len,
key,
children: [0; 2],
child_earliest_expiry: [u64::MAX; 2],
reserve: [0; NODE_SIZE - 48],
}
}
/// Returns the handle of the child that may contain the search key
/// and 0 or 1 depending on which child it was.
pub(crate) fn walk_down(&self, search_key: i128) -> (NodeHandle, bool) {
let crit_bit_mask = 1i128 << (127 - self.prefix_len);
let crit_bit = (search_key & crit_bit_mask) != 0;
(self.children[crit_bit as usize], crit_bit)
}
/// The lowest timestamp at which one of the contained LeafNodes expires.
#[inline(always)]
pub fn earliest_expiry(&self) -> u64 {
std::cmp::min(self.child_earliest_expiry[0], self.child_earliest_expiry[1])
}
}
/// LeafNodes represent an order in the binary tree
#[derive(Debug, Copy, Clone, PartialEq, Eq, Pod)]
#[repr(C)]
pub struct LeafNode {
pub tag: u32,
pub owner_slot: u8,
pub order_type: OrderType, // this was added for TradingView move order
/// Time in seconds after `timestamp` at which the order expires.
/// A value of 0 means no expiry.
pub time_in_force: u8,
/// The binary tree key
pub key: i128,
pub owner: Pubkey,
pub quantity: i64,
pub client_order_id: u64,
// The time the order was placed
pub timestamp: u64,
pub reserve: [u8; NODE_SIZE - 81],
}
#[inline(always)]
fn key_to_price(key: i128) -> i64 {
(key >> 64) as i64
}
impl LeafNode {
#[allow(clippy::too_many_arguments)]
pub fn new(
owner_slot: u8,
key: i128,
owner: Pubkey,
quantity: i64,
client_order_id: u64,
timestamp: u64,
order_type: OrderType,
time_in_force: u8,
) -> Self {
Self {
tag: NodeTag::LeafNode.into(),
owner_slot,
order_type,
time_in_force,
key,
owner,
quantity,
client_order_id,
timestamp,
reserve: [0; NODE_SIZE - 81],
}
}
#[inline(always)]
pub fn price(&self) -> i64 {
key_to_price(self.key)
}
/// Time at which this order will expire, u64::MAX if never
#[inline(always)]
pub fn expiry(&self) -> u64 {
if self.time_in_force == 0 {
u64::MAX
} else {
self.timestamp + self.time_in_force as u64
}
}
#[inline(always)]
pub fn is_valid(&self, now_ts: u64) -> bool {
self.time_in_force == 0 || now_ts < self.timestamp + self.time_in_force as u64
}
}
#[derive(Copy, Clone, Pod)]
#[repr(C)]
pub struct FreeNode {
pub(crate) tag: u32,
pub(crate) next: NodeHandle,
pub(crate) reserve: [u8; NODE_SIZE - 8],
}
#[zero_copy]
#[derive(Pod)]
pub struct AnyNode {
pub tag: u32,
pub data: [u8; 84], // note: anchor can't parse the struct for IDL when it includes non numbers, NODE_SIZE == 88, 84 == 88 - 4
}
const_assert_eq!(size_of::<AnyNode>(), NODE_SIZE);
const_assert_eq!(size_of::<AnyNode>(), size_of::<InnerNode>());
const_assert_eq!(size_of::<AnyNode>(), size_of::<LeafNode>());
const_assert_eq!(size_of::<AnyNode>(), size_of::<FreeNode>());
pub enum NodeRef<'a> {
Inner(&'a InnerNode),
Leaf(&'a LeafNode),
}
pub enum NodeRefMut<'a> {
Inner(&'a mut InnerNode),
Leaf(&'a mut LeafNode),
}
impl AnyNode {
pub fn key(&self) -> Option<i128> {
match self.case()? {
NodeRef::Inner(inner) => Some(inner.key),
NodeRef::Leaf(leaf) => Some(leaf.key),
}
}
pub(crate) fn children(&self) -> Option<[NodeHandle; 2]> {
match self.case().unwrap() {
NodeRef::Inner(&InnerNode { children, .. }) => Some(children),
NodeRef::Leaf(_) => None,
}
}
pub(crate) fn case(&self) -> Option<NodeRef> {
match NodeTag::try_from(self.tag) {
Ok(NodeTag::InnerNode) => Some(NodeRef::Inner(cast_ref(self))),
Ok(NodeTag::LeafNode) => Some(NodeRef::Leaf(cast_ref(self))),
_ => None,
}
}
fn case_mut(&mut self) -> Option<NodeRefMut> {
match NodeTag::try_from(self.tag) {
Ok(NodeTag::InnerNode) => Some(NodeRefMut::Inner(cast_mut(self))),
Ok(NodeTag::LeafNode) => Some(NodeRefMut::Leaf(cast_mut(self))),
_ => None,
}
}
#[inline]
pub fn as_leaf(&self) -> Option<&LeafNode> {
match self.case() {
Some(NodeRef::Leaf(leaf_ref)) => Some(leaf_ref),
_ => None,
}
}
#[inline]
pub fn as_leaf_mut(&mut self) -> Option<&mut LeafNode> {
match self.case_mut() {
Some(NodeRefMut::Leaf(leaf_ref)) => Some(leaf_ref),
_ => None,
}
}
#[inline]
pub fn as_inner(&self) -> Option<&InnerNode> {
match self.case() {
Some(NodeRef::Inner(inner_ref)) => Some(inner_ref),
_ => None,
}
}
#[inline]
pub fn as_inner_mut(&mut self) -> Option<&mut InnerNode> {
match self.case_mut() {
Some(NodeRefMut::Inner(inner_ref)) => Some(inner_ref),
_ => None,
}
}
#[inline]
pub fn earliest_expiry(&self) -> u64 {
match self.case().unwrap() {
NodeRef::Inner(inner) => inner.earliest_expiry(),
NodeRef::Leaf(leaf) => leaf.expiry(),
}
}
}
impl AsRef<AnyNode> for InnerNode {
fn as_ref(&self) -> &AnyNode {
cast_ref(self)
}
}
impl AsRef<AnyNode> for LeafNode {
#[inline]
fn as_ref(&self) -> &AnyNode {
cast_ref(self)
}
}