Skip to main content

carbon_core/
deserialize.rs

1//! Borsh helpers shared by Codama-generated decoder crates.
2//!
3//! # Components
4//!
5//! - [`CarbonDeserialize`] — discriminator-prefixed borsh deserialization
6//!   contract used by every generated instruction/account decoder.
7//! - [`extract_discriminator`] — splits raw bytes into `(discriminator,
8//!   payload)`.
9//! - [`ArrangeAccounts`] — turns the positional `&[AccountMeta]` of an
10//!   instruction into a typed accounts struct.
11//! - [`PrefixString`] / [`U64PrefixString`] — newtypes for `String`s serialized
12//!   with a length prefix wider than borsh's default.
13
14use std::{
15    io::{Error, ErrorKind, Read, Result},
16    ops::Deref,
17};
18/// Discriminator-prefixed borsh deserialization contract.
19///
20/// Implementors define a static `DISCRIMINATOR` byte slice (typically
21/// 8 bytes for Anchor-style programs) and `deserialize` is expected to
22/// peel that prefix off before delegating to `BorshDeserialize`.
23pub trait CarbonDeserialize
24where
25    Self: Sized + crate::borsh::BorshDeserialize,
26{
27    const DISCRIMINATOR: &'static [u8];
28
29    fn deserialize(data: &[u8]) -> Option<Self>;
30}
31
32pub fn extract_discriminator(length: usize, data: &[u8]) -> Option<(&[u8], &[u8])> {
33    if data.len() < length {
34        return None;
35    }
36
37    Some((&data[..length], &data[length..]))
38}
39
40/// Turns the positional `&[AccountMeta]` of an instruction into a typed
41/// accounts struct. Generated alongside instruction decoders; rarely
42/// implemented by hand.
43pub trait ArrangeAccounts {
44    type ArrangedAccounts: Clone + Send + Sync + std::fmt::Debug;
45
46    fn arrange_accounts(
47        accounts: &[solana_instruction::AccountMeta],
48    ) -> Option<Self::ArrangedAccounts>;
49}
50
51/// `String` newtype with a 32-bit borsh length prefix (matches the
52/// default `String` layout but exposed as a distinct type so generated
53/// decoders can opt in explicitly).
54#[derive(serde::Serialize, serde::Deserialize, Default, PartialEq, Eq, Clone)]
55pub struct PrefixString(pub String);
56
57impl Deref for PrefixString {
58    type Target = String;
59
60    fn deref(&self) -> &Self::Target {
61        &self.0
62    }
63}
64
65impl From<PrefixString> for String {
66    fn from(val: PrefixString) -> Self {
67        val.0
68    }
69}
70
71impl std::fmt::Debug for PrefixString {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        f.write_fmt(format_args!("{:?}", self.0))
74    }
75}
76
77impl crate::borsh::BorshDeserialize for PrefixString {
78    #[inline]
79    fn deserialize_reader<R: Read>(reader: &mut R) -> Result<Self> {
80        // read the length of the String
81        let mut buffer = vec![0u8; 4];
82        reader.read_exact(&mut buffer)?;
83        let length = u32::deserialize(&mut buffer.as_slice())?;
84        let mut buffer = vec![0u8; length as usize];
85        reader.read_exact(&mut buffer)?;
86
87        Ok(Self(String::from_utf8(buffer).map_err(|_| {
88            Error::new(ErrorKind::InvalidData, "invalid utf8")
89        })?))
90    }
91}
92
93/// `String` newtype with a 64-bit borsh length prefix. Used by programs
94/// that serialise long strings outside `String`'s 4-byte default.
95#[derive(serde::Serialize, Default, serde::Deserialize, PartialEq, Eq, Clone, Hash)]
96pub struct U64PrefixString(pub String);
97
98impl Deref for U64PrefixString {
99    type Target = String;
100
101    fn deref(&self) -> &Self::Target {
102        &self.0
103    }
104}
105
106impl From<U64PrefixString> for String {
107    fn from(val: U64PrefixString) -> Self {
108        val.0
109    }
110}
111
112impl std::fmt::Debug for U64PrefixString {
113    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114        f.write_fmt(format_args!("{:?}", self.0))
115    }
116}
117
118impl crate::borsh::BorshDeserialize for U64PrefixString {
119    #[inline]
120    fn deserialize_reader<R: Read>(reader: &mut R) -> Result<Self> {
121        // read the length of the String
122        let mut buffer = vec![0u8; 8];
123        reader.read_exact(&mut buffer)?;
124        let length = u64::deserialize(&mut buffer.as_slice())?;
125        let mut buffer = vec![0u8; length as usize];
126        reader.read_exact(&mut buffer)?;
127
128        Ok(Self(String::from_utf8(buffer).map_err(|_| {
129            Error::new(ErrorKind::InvalidData, "invalid utf8")
130        })?))
131    }
132}