Skip to main content

sol_parser_sdk/logs/
zero_copy_parser.rs

1//! 零拷贝解析器 - 极致性能优化
2
3use solana_sdk::{pubkey::Pubkey, signature::Signature};
4use crate::core::events::*;
5use memchr::memmem;
6use base64::{Engine as _, engine::general_purpose};
7use super::perf_hints::prefetch_read;
8
9/// 零分配 PumpFun Trade 事件解析(栈缓冲区)
10#[inline(always)]
11pub fn parse_pumpfun_trade(
12    log: &str,
13    signature: Signature,
14    slot: u64,
15    tx_index: u64,
16    block_time_us: Option<i64>,
17    grpc_recv_us: i64,
18    is_created_buy: bool,
19) -> Option<DexEvent> {
20    // 使用栈缓冲区,避免堆分配(需要足够大以容纳完整的事件数据)
21    // PumpFun Trade 事件最大约 350 base64 字符 = 262字节,留出余量用 512 字节
22    const MAX_DECODE_SIZE: usize = 512;
23    let mut decode_buf: [u8; MAX_DECODE_SIZE] = [0u8; MAX_DECODE_SIZE];
24
25    // SIMD 快速查找 "Program data: "
26    let log_bytes = log.as_bytes();
27    let pos = memmem::find(log_bytes, b"Program data: ")?;
28    let data_part = log[pos + 14..].trim();
29
30    // 快速检查 discriminator(需要至少12个base64字符才能解码出8字节)
31    // base64: 每4个字符 = 3个字节,所以12个字符 = 9个字节
32    if data_part.len() < 12 {
33        return None;
34    }
35
36    // 解码 discriminator 到栈缓冲区(12个字符解码为9字节,包含完整8字节discriminator)
37    let disc_decoded_len = general_purpose::STANDARD
38        .decode_slice(&data_part.as_bytes()[..12], &mut decode_buf[..9])
39        .ok()?;
40
41    if disc_decoded_len < 8 {
42        return None;
43    }
44
45    // 检查是否为 Trade 事件 discriminator
46    const TRADE_DISCRIMINATOR: [u8; 8] = [189, 219, 127, 211, 78, 230, 97, 238];
47
48    if decode_buf[..8] != TRADE_DISCRIMINATOR {
49        return None;
50    }
51
52    // 完整解码事件数据到栈缓冲区
53    let decoded_len = general_purpose::STANDARD
54        .decode_slice(data_part.as_bytes(), &mut decode_buf)
55        .ok()?;
56
57    if decoded_len < 96 {
58        return None;
59    }
60
61    let data = &decode_buf[8..decoded_len];
62    let mut offset = 0;
63
64    // 预取后续数据到 CPU 缓存
65    unsafe {
66        if data.len() >= 64 {
67            prefetch_read(data.as_ptr().add(32));
68        }
69    }
70
71    // 快速解析字段(内联读取,避免函数调用开销)
72    let mint = read_pubkey_inline(data, offset)?;
73    offset += 32;
74
75    let sol_amount = read_u64_le_inline(data, offset)?;
76    offset += 8;
77
78    let token_amount = read_u64_le_inline(data, offset)?;
79    offset += 8;
80
81    let is_buy = read_u8_inline(data, offset)?;
82    offset += 1;
83
84    let user = read_pubkey_inline(data, offset)?;
85    offset += 32;
86
87    let timestamp = read_i64_le_inline(data, offset)?;
88    offset += 8;
89
90    let virtual_sol_reserves = read_u64_le_inline(data, offset)?;
91    offset += 8;
92
93    let virtual_token_reserves = read_u64_le_inline(data, offset)?;
94    offset += 8;
95
96    let real_sol_reserves = read_u64_le_inline(data, offset).unwrap_or(0);
97    offset += 8;
98
99    let real_token_reserves = read_u64_le_inline(data, offset).unwrap_or(0);
100    offset += 8;
101
102    let fee_recipient = read_pubkey_inline(data, offset).unwrap_or_default();
103    offset += 32;
104
105    let fee_basis_points = read_u64_le_inline(data, offset).unwrap_or(0);
106    offset += 8;
107
108    let fee = read_u64_le_inline(data, offset).unwrap_or(0);
109    offset += 8;
110
111    let creator = read_pubkey_inline(data, offset).unwrap_or_default();
112    offset += 32;
113
114    let creator_fee_basis_points = read_u64_le_inline(data, offset).unwrap_or(0);
115    offset += 8;
116
117    let creator_fee = read_u64_le_inline(data, offset).unwrap_or(0);
118    offset += 8;
119
120    let track_volume = read_u8_inline(data, offset).unwrap_or(0) != 0;
121    offset += 1;
122
123    let total_unclaimed_tokens = read_u64_le_inline(data, offset).unwrap_or(0);
124    offset += 8;
125
126    let total_claimed_tokens = read_u64_le_inline(data, offset).unwrap_or(0);
127    offset += 8;
128
129    let current_sol_volume = read_u64_le_inline(data, offset).unwrap_or(0);
130
131    let metadata = EventMetadata {
132        signature,
133        slot,
134        tx_index,
135        block_time_us: block_time_us.unwrap_or(0),
136        grpc_recv_us,
137        recent_blockhash: None,
138    };
139
140    Some(DexEvent::PumpFunTrade(PumpFunTradeEvent {
141        metadata,
142        mint,
143        sol_amount,
144        token_amount,
145        is_buy: is_buy != 0,
146        is_created_buy,
147        user,
148        timestamp,
149        virtual_sol_reserves,
150        virtual_token_reserves,
151        real_sol_reserves,
152        real_token_reserves,
153        fee_recipient,
154        fee_basis_points,
155        fee,
156        creator,
157        creator_fee_basis_points,
158        creator_fee,
159        track_volume,
160        total_unclaimed_tokens,
161        total_claimed_tokens,
162        current_sol_volume,
163        ..Default::default()
164    }))
165}
166
167/// 内联读取 Pubkey(避免函数调用)
168#[inline(always)]
169fn read_pubkey_inline(data: &[u8], offset: usize) -> Option<Pubkey> {
170    if offset + 32 <= data.len() {
171        let mut bytes = [0u8; 32];
172        bytes.copy_from_slice(&data[offset..offset + 32]);
173        Some(Pubkey::new_from_array(bytes))
174    } else {
175        None
176    }
177}
178
179/// 内联读取 u64(避免函数调用)
180#[inline(always)]
181fn read_u64_le_inline(data: &[u8], offset: usize) -> Option<u64> {
182    if offset + 8 <= data.len() {
183        let mut bytes = [0u8; 8];
184        bytes.copy_from_slice(&data[offset..offset + 8]);
185        Some(u64::from_le_bytes(bytes))
186    } else {
187        None
188    }
189}
190
191/// 内联读取 i64(避免函数调用)
192#[inline(always)]
193fn read_i64_le_inline(data: &[u8], offset: usize) -> Option<i64> {
194    if offset + 8 <= data.len() {
195        let mut bytes = [0u8; 8];
196        bytes.copy_from_slice(&data[offset..offset + 8]);
197        Some(i64::from_le_bytes(bytes))
198    } else {
199        None
200    }
201}
202
203/// 内联读取 u8(避免函数调用)
204#[inline(always)]
205fn read_u8_inline(data: &[u8], offset: usize) -> Option<u8> {
206    data.get(offset).copied()
207}