gmsol_store/states/market/revertible/
buffer.rs

1use anchor_lang::prelude::*;
2use gmsol_model::PoolKind;
3use strum::IntoEnumIterator;
4
5use crate::{
6    events::{EventEmitter, MarketStateUpdatedRef},
7    states::{
8        market::{Clocks, Pool, State},
9        OtherState, PoolStorage,
10    },
11};
12
13use super::Revision;
14
15#[zero_copy]
16#[cfg_attr(feature = "debug", derive(Debug))]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub(crate) struct RevertibleBuffer {
19    rev: u64,
20    padding: [u8; 8],
21    state: State,
22}
23
24impl RevertibleBuffer {
25    pub(crate) fn init(&mut self) {
26        self.rev = 1;
27    }
28
29    pub(super) fn pool<'a>(&'a self, kind: PoolKind, storage: &'a State) -> Option<&'a Pool> {
30        let pool_storage = self.state.pools.get(kind)?;
31        Some(
32            pool_storage
33                .cache_get_with(self.rev, || storage.pools.get(kind).expect("must exist"))
34                .pool(),
35        )
36    }
37
38    pub(super) fn pool_mut(&mut self, kind: PoolKind, storage: &State) -> Option<&mut Pool> {
39        let pool_storage = self.state.pools.get_mut(kind)?;
40        Some(
41            pool_storage
42                .cache_get_mut_with(self.rev, || *storage.pools.get(kind).expect("must exist"))
43                .pool_mut(),
44        )
45    }
46
47    pub(super) fn clocks<'a>(&'a self, storage: &'a State) -> &'a Clocks {
48        self.state
49            .clocks
50            .cache_get_with(self.rev, || &storage.clocks)
51    }
52
53    pub(super) fn clocks_mut(&mut self, storage: &State) -> &mut Clocks {
54        self.state
55            .clocks
56            .cache_get_mut_with(self.rev, || storage.clocks)
57    }
58
59    pub(super) fn other<'a>(&'a self, storage: &'a State) -> &'a OtherState {
60        self.state.other.cache_get_with(self.rev, || &storage.other)
61    }
62
63    pub(super) fn other_mut(&mut self, storage: &State) -> &mut OtherState {
64        self.state
65            .other
66            .cache_get_mut_with(self.rev, || storage.other)
67    }
68
69    pub(super) fn rev(&self) -> u64 {
70        self.rev
71    }
72
73    pub(super) fn start_revertible_operation(&mut self) {
74        self.rev = self.rev.checked_add(1).expect("rev overflow");
75    }
76
77    pub(super) fn commit_to_storage(
78        &mut self,
79        storage: &mut State,
80        market_token: &Pubkey,
81        event_emitter: &EventEmitter,
82    ) {
83        let state = &self.state;
84        let rev = self.rev;
85
86        let mut updated_pool_kinds = Vec::new();
87
88        // Commit pools.
89        for kind in PoolKind::iter() {
90            let Some(pool) = state.pools.get(kind) else {
91                continue;
92            };
93            if pool.is_dirty(rev) {
94                let target = storage.pools.get_mut(kind).expect("must exist");
95                *target = *pool;
96                updated_pool_kinds.push(kind);
97            }
98        }
99
100        // Commit clocks.
101        let is_clocks_dirty = state.clocks.is_dirty(rev);
102        if is_clocks_dirty {
103            storage.clocks = state.clocks;
104        }
105
106        // Commit other state.
107        let is_other_dirty = state.other.is_dirty(rev);
108        if is_other_dirty {
109            storage.other = state.other;
110        }
111
112        let updated_pools = updated_pool_kinds
113            .iter()
114            .map(|kind| state.pools.get(*kind).expect("must exist").pool())
115            .collect();
116
117        let event = MarketStateUpdatedRef::new(
118            self.rev(),
119            *market_token,
120            updated_pool_kinds,
121            updated_pools,
122            is_clocks_dirty.then_some(&state.clocks),
123            is_other_dirty.then_some(&state.other),
124        );
125
126        event_emitter
127            .emit_cpi_with_space(&event, event.space())
128            .map_err(|err| {
129                msg!("{}", err);
130            })
131            .expect("emit event error");
132    }
133}
134
135pub(super) trait Cache: Revision {
136    fn is_dirty(&self, rev: u64) -> bool;
137
138    fn set_rev(&mut self, rev: u64) -> u64;
139
140    fn cache_get_with<'a>(&'a self, rev: u64, f: impl FnOnce() -> &'a Self) -> &'a Self {
141        if self.is_dirty(rev) {
142            self
143        } else {
144            f()
145        }
146    }
147
148    fn cache_get_mut_with(&mut self, rev: u64, f: impl FnOnce() -> Self) -> &mut Self
149    where
150        Self: Sized,
151    {
152        if !self.is_dirty(rev) {
153            *self = f();
154            self.set_rev(rev);
155        }
156        self
157    }
158}
159
160macro_rules! impl_cache {
161    ($cache:ty) => {
162        impl Revision for $cache {
163            fn rev(&self) -> u64 {
164                self.rev
165            }
166        }
167
168        impl Cache for $cache {
169            fn is_dirty(&self, rev: u64) -> bool {
170                self.rev() == rev
171            }
172
173            fn set_rev(&mut self, mut rev: u64) -> u64 {
174                std::mem::swap(&mut rev, &mut self.rev);
175                rev
176            }
177        }
178    };
179}
180
181impl_cache!(Clocks);
182impl_cache!(PoolStorage);
183impl_cache!(OtherState);