gmsol_store/states/market/revertible/
buffer.rs1use 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 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 let is_clocks_dirty = state.clocks.is_dirty(rev);
102 if is_clocks_dirty {
103 storage.clocks = state.clocks;
104 }
105
106 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);