sol_parser_sdk/logs/
utils.rs

1//! 日志解析通用工具函数
2//!
3//! 提供字节数据解析的基础工具,不使用 BorshDeserialize
4
5use solana_sdk::{pubkey::Pubkey, signature::Signature};
6use crate::core::events::EventMetadata;
7use base64::{Engine as _, engine::general_purpose};
8
9/// 从日志中提取程序数据(使用 SIMD 优化查找)
10#[inline]
11pub fn extract_program_data(log: &str) -> Option<Vec<u8>> {
12    use memchr::memmem;
13
14    let log_bytes = log.as_bytes();
15    let pos = memmem::find(log_bytes, b"Program data: ")?;
16
17    let data_part = &log[pos + 14..];
18    general_purpose::STANDARD.decode(data_part.trim()).ok()
19}
20
21/// 快速提取 discriminator(只解码前16字节,避免完整解码)
22#[inline]
23pub fn extract_discriminator_fast(log: &str) -> Option<[u8; 8]> {
24    use memchr::memmem;
25
26    let log_bytes = log.as_bytes();
27    let pos = memmem::find(log_bytes, b"Program data: ")?;
28
29    let data_part = log[pos + 14..].trim();
30
31    // Base64 编码:每4个字符解码为3个字节
32    // 要获取8字节,需要至少 ceil(8/3)*4 = 12 个 base64 字符
33    if data_part.len() < 12 {
34        return None;
35    }
36
37    // 取前16个字符(解码为12字节,包含8字节 discriminator)
38    let prefix = &data_part[..16];
39
40    let mut buf = [0u8; 12];
41    let decoded_len = general_purpose::STANDARD.decode_slice(prefix.as_bytes(), &mut buf).ok()?;
42
43    if decoded_len >= 8 {
44        Some(buf[0..8].try_into().unwrap())
45    } else {
46        None
47    }
48}
49
50/// 从字节数组中读取 u64(小端序)- SIMD 优化
51#[inline]
52pub fn read_u64_le(data: &[u8], offset: usize) -> Option<u64> {
53    data.get(offset..offset + 8)
54        .map(|slice| u64::from_le_bytes(slice.try_into().unwrap()))
55}
56
57/// 从字节数组中读取 u32(小端序)- SIMD 优化
58#[inline]
59pub fn read_u32_le(data: &[u8], offset: usize) -> Option<u32> {
60    data.get(offset..offset + 4)
61        .map(|slice| u32::from_le_bytes(slice.try_into().unwrap()))
62}
63
64/// 从字节数组中读取 i64(小端序)- SIMD 优化
65pub fn read_i64_le(data: &[u8], offset: usize) -> Option<i64> {
66    data.get(offset..offset + 8)
67        .map(|slice| i64::from_le_bytes(slice.try_into().unwrap()))
68}
69
70/// 从字节数组中读取 i32(小端序)- SIMD 优化
71pub fn read_i32_le(data: &[u8], offset: usize) -> Option<i32> {
72    data.get(offset..offset + 4)
73        .map(|slice| i32::from_le_bytes(slice.try_into().unwrap()))
74}
75
76/// 从字节数组中读取 u128(小端序)- SIMD 优化
77pub fn read_u128_le(data: &[u8], offset: usize) -> Option<u128> {
78    data.get(offset..offset + 16)
79        .map(|slice| u128::from_le_bytes(slice.try_into().unwrap()))
80}
81
82/// 从字节数组中读取 u16(小端序)- SIMD 优化
83pub fn read_u16_le(data: &[u8], offset: usize) -> Option<u16> {
84    data.get(offset..offset + 2)
85        .map(|slice| u16::from_le_bytes(slice.try_into().unwrap()))
86}
87
88/// 从字节数组中读取 u8
89pub fn read_u8(data: &[u8], offset: usize) -> Option<u8> {
90    data.get(offset).copied()
91}
92
93/// 从字节数组中读取 Pubkey(32字节)- SIMD 优化
94#[inline]
95pub fn read_pubkey(data: &[u8], offset: usize) -> Option<Pubkey> {
96    data.get(offset..offset + 32)
97        .and_then(|slice| {
98            let key_bytes: [u8; 32] = slice.try_into().ok()?;
99            Some(Pubkey::new_from_array(key_bytes))
100        })
101}
102
103/// 从字节数组中读取字符串
104pub fn read_string(data: &[u8], offset: usize) -> Option<(String, usize)> {
105    if data.len() < offset + 4 {
106        return None;
107    }
108
109    let len = read_u32_le(data, offset)? as usize;
110    if data.len() < offset + 4 + len {
111        return None;
112    }
113
114    let string_bytes = &data[offset + 4..offset + 4 + len];
115    // 使用 from_utf8_lossy 避免额外的错误处理和内存分配
116    let string = std::str::from_utf8(string_bytes).ok()?.to_string();
117    Some((string, 4 + len))
118}
119
120/// 读取布尔值
121pub fn read_bool(data: &[u8], offset: usize) -> Option<bool> {
122    if data.len() <= offset {
123        return None;
124    }
125    Some(data[offset] == 1)
126}
127
128/// 创建事件元数据的通用函数
129pub fn create_metadata_simple(
130    signature: Signature,
131    slot: u64,
132    tx_index: u64,
133    block_time: Option<i64>,
134    program_id: Pubkey,
135    grpc_recv_us: i64,
136) -> EventMetadata {
137    EventMetadata {
138        signature,
139        slot,
140        tx_index,
141        block_time_us: block_time.unwrap_or(0) * 1_000_000,
142        grpc_recv_us,
143    }
144}
145
146/// 创建默认事件元数据的通用函数(不需要程序ID)
147pub fn create_metadata_default(
148    signature: Signature,
149    slot: u64,
150    tx_index: u64,
151    block_time: Option<i64>,
152) -> EventMetadata {
153    // 优化:macOS 使用 CLOCK_REALTIME(Linux 可用 CLOCK_REALTIME_COARSE)
154    let current_time = unsafe {
155        let mut ts = libc::timespec {
156            tv_sec: 0,
157            tv_nsec: 0,
158        };
159        #[cfg(target_os = "linux")]
160        libc::clock_gettime(libc::CLOCK_REALTIME_COARSE, &mut ts);
161        #[cfg(not(target_os = "linux"))]
162        libc::clock_gettime(libc::CLOCK_REALTIME, &mut ts);
163        (ts.tv_sec as i64 * 1_000_000) + (ts.tv_nsec as i64 / 1_000)
164    };
165
166    EventMetadata {
167        signature,
168        slot,
169        tx_index,
170        block_time_us: block_time.unwrap_or(0) * 1_000_000,
171        grpc_recv_us: current_time,
172    }
173}
174
175/// 文本回退解析工具
176pub mod text_parser {
177
178    /// 从文本中提取数字
179    pub fn extract_number_from_text(text: &str, field: &str) -> Option<u64> {
180        if let Some(start) = text.find(&format!("{}:", field)) {
181            let after_colon = &text[start + field.len() + 1..];
182            if let Some(end) = after_colon.find(' ').or_else(|| after_colon.find(',')) {
183                after_colon[..end].trim().parse().ok()
184            } else {
185                after_colon.trim().parse().ok()
186            }
187        } else {
188            None
189        }
190    }
191
192    /// 从文本中提取字段值
193    pub fn extract_text_field(text: &str, field: &str) -> Option<String> {
194        if let Some(start) = text.find(&format!("{}:", field)) {
195            let after_colon = &text[start + field.len() + 1..];
196            if let Some(end) = after_colon.find(',').or_else(|| after_colon.find(' ')) {
197                Some(after_colon[..end].trim().to_string())
198            } else {
199                Some(after_colon.trim().to_string())
200            }
201        } else {
202            None
203        }
204    }
205
206    /// 检测交易类型
207    pub fn detect_trade_type(log: &str) -> Option<bool> {
208        if log.contains("buy") || log.contains("Buy") {
209            Some(true)
210        } else if log.contains("sell") || log.contains("Sell") {
211            Some(false)
212        } else {
213            None
214        }
215    }
216}