Skip to main content

sol_parser_sdk/logs/
utils.rs

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