Skip to main content

gmsol_sdk/utils/
zero_copy.rs

1use std::sync::Arc;
2
3use anchor_lang::{err, Discriminator};
4use gmsol_programs::{
5    anchor_lang,
6    bytemuck::{self, PodCastError},
7};
8
9/// Check discriminator.
10pub fn check_discriminator<T: Discriminator>(data: &[u8]) -> anchor_lang::prelude::Result<()> {
11    use anchor_lang::error::ErrorCode;
12
13    let disc = T::DISCRIMINATOR;
14    if data.len() < disc.len() {
15        return err!(ErrorCode::AccountDiscriminatorNotFound);
16    }
17    let given_disc = &data[..8];
18    if disc != given_disc {
19        return err!(ErrorCode::AccountDiscriminatorMismatch);
20    }
21    Ok(())
22}
23
24/// A workaround to deserialize "zero-copy" account data.
25///
26/// See [anchort#2689](https://github.com/coral-xyz/anchor/issues/2689) for more information.
27pub fn try_deserialize<T>(data: &[u8]) -> anchor_lang::prelude::Result<T>
28where
29    T: anchor_lang::ZeroCopy,
30{
31    check_discriminator::<T>(data)?;
32    try_deserialize_unchecked(data)
33}
34
35/// A workaround to deserialize "zero-copy" account data.
36///
37/// See [anchort#2689](https://github.com/coral-xyz/anchor/issues/2689) for more information.
38pub fn try_deserialize_unchecked<T>(data: &[u8]) -> anchor_lang::prelude::Result<T>
39where
40    T: anchor_lang::ZeroCopy,
41{
42    use anchor_lang::{error, error::ErrorCode};
43    let end = std::mem::size_of::<T>() + 8;
44    if data.len() < end {
45        return err!(ErrorCode::AccountDidNotDeserialize);
46    }
47    let data_without_discriminator = &data[8..end];
48
49    match bytemuck::try_from_bytes(data_without_discriminator) {
50        Ok(data) => Ok(*data),
51        Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) => {
52            bytemuck::try_pod_read_unaligned(data_without_discriminator)
53                .map_err(|_| error!(ErrorCode::AccountDidNotDeserialize))
54        }
55        Err(_) => Err(error!(ErrorCode::AccountDidNotDeserialize)),
56    }
57}
58
59/// Workaround for deserializing zero-copy accounts.
60#[derive(Debug, Clone, Copy)]
61pub struct ZeroCopy<T>(pub T);
62
63impl<T> ZeroCopy<T> {
64    /// Conver into inner value.
65    pub fn into_inner(self) -> T {
66        self.0
67    }
68}
69
70impl<T> anchor_lang::AccountDeserialize for ZeroCopy<T>
71where
72    T: anchor_lang::ZeroCopy,
73{
74    fn try_deserialize(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
75        let account = try_deserialize(buf)?;
76        Ok(Self(account))
77    }
78
79    fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
80        let account = try_deserialize_unchecked(buf)?;
81        Ok(Self(account))
82    }
83}
84
85impl<T> Discriminator for ZeroCopy<T>
86where
87    T: Discriminator,
88{
89    const DISCRIMINATOR: &'static [u8] = T::DISCRIMINATOR;
90}
91
92impl<T> AsRef<T> for ZeroCopy<T> {
93    fn as_ref(&self) -> &T {
94        &self.0
95    }
96}
97
98/// Deserialize a [`ZeroCopy`](anchor_lang::ZeroCopy) structure.
99pub fn try_deserialize_zero_copy_with_options<T: anchor_lang::ZeroCopy>(
100    mut data: &[u8],
101    no_discriminator: bool,
102) -> crate::Result<ZeroCopy<T>> {
103    use anchor_lang::AccountDeserialize;
104    if no_discriminator {
105        let data = [T::DISCRIMINATOR, data].concat();
106        Ok(ZeroCopy::<T>::try_deserialize(&mut data.as_slice())?)
107    } else {
108        Ok(ZeroCopy::<T>::try_deserialize(&mut data)?)
109    }
110}
111
112/// Deserialize a [`ZeroCopy`](anchor_lang::ZeroCopy) structure.
113pub fn try_deserialize_zero_copy<T: anchor_lang::ZeroCopy>(
114    data: &[u8],
115) -> crate::Result<ZeroCopy<T>> {
116    try_deserialize_zero_copy_with_options(data, false)
117}
118
119/// Deserialize a [`ZeroCopy`](anchor_lang::ZeroCopy) structure from base64.
120pub fn try_deserialize_zero_copy_from_base64_with_options<T: anchor_lang::ZeroCopy>(
121    data: &str,
122    no_discriminator: bool,
123) -> crate::Result<ZeroCopy<T>> {
124    let mut data = crate::utils::base64::decode_base64(data)?;
125    if no_discriminator {
126        data = [T::DISCRIMINATOR, &data].concat();
127    }
128    try_deserialize_zero_copy(&data)
129}
130
131/// Deserialize a [`ZeroCopy`](anchor_lang::ZeroCopy) structure from base64.
132pub fn try_deserialize_zero_copy_from_base64<T: anchor_lang::ZeroCopy>(
133    data: &str,
134) -> crate::Result<ZeroCopy<T>> {
135    try_deserialize_zero_copy_from_base64_with_options(data, false)
136}
137
138/// Workaround for deserializing zero-copy accounts and wrapping the result into Arc.
139pub struct SharedZeroCopy<T>(pub Arc<T>);
140
141impl<T> Clone for SharedZeroCopy<T> {
142    fn clone(&self) -> Self {
143        Self(self.0.clone())
144    }
145}
146
147impl<T> SharedZeroCopy<T> {
148    /// Conver into inner value.
149    pub fn into_inner(self) -> Arc<T> {
150        self.0
151    }
152}
153
154impl<T> anchor_lang::AccountDeserialize for SharedZeroCopy<T>
155where
156    T: anchor_lang::ZeroCopy,
157{
158    fn try_deserialize(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
159        let account = try_deserialize(buf)?;
160        Ok(Self(Arc::new(account)))
161    }
162
163    fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
164        let account = try_deserialize_unchecked(buf)?;
165        Ok(Self(Arc::new(account)))
166    }
167}
168
169impl<T> Discriminator for SharedZeroCopy<T>
170where
171    T: Discriminator,
172{
173    const DISCRIMINATOR: &'static [u8] = T::DISCRIMINATOR;
174}
175
176impl<T> AsRef<T> for SharedZeroCopy<T> {
177    fn as_ref(&self) -> &T {
178        &self.0
179    }
180}
181
182/// Wrapper for deserializing account into arced type.
183pub struct Shared<T>(pub Arc<T>);
184
185impl<T> Clone for Shared<T> {
186    fn clone(&self) -> Self {
187        Self(self.0.clone())
188    }
189}
190
191impl<T> Shared<T> {
192    /// Conver into inner value.
193    pub fn into_inner(self) -> Arc<T> {
194        self.0
195    }
196}
197
198impl<T> anchor_lang::AccountDeserialize for Shared<T>
199where
200    T: anchor_lang::AccountDeserialize,
201{
202    fn try_deserialize(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
203        let account = T::try_deserialize(buf)?;
204        Ok(Self(Arc::new(account)))
205    }
206
207    fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
208        let account = T::try_deserialize_unchecked(buf)?;
209        Ok(Self(Arc::new(account)))
210    }
211}
212
213impl<T> Discriminator for Shared<T>
214where
215    T: Discriminator,
216{
217    const DISCRIMINATOR: &'static [u8] = T::DISCRIMINATOR;
218}
219
220impl<T> AsRef<T> for Shared<T> {
221    fn as_ref(&self) -> &T {
222        &self.0
223    }
224}