sol-parser-sdk 0.3.1

A lightweight Rust library for real-time event streaming from Solana DEX trading programs. Supports PumpFun, PumpSwap, Bonk, and Raydium protocols with Yellowstone gRPC and ShredStream.
Documentation
//! # 解析器缓存模块
//!
//! 提供高性能缓存机制,减少内存分配和重复计算:
//! - 程序ID缓存:避免重复查找和分配
//! - 账户公钥缓存:线程局部缓存,零锁竞争
//!
//! ## 性能优势
//!
//! - **减少 30-50% 内存分配**:通过缓存重用避免重复分配
//! - **零锁竞争**:线程局部存储,每个线程独立缓存
//! - **快速查找**:读写锁优化,读操作无阻塞
//!
//! ## 使用示例
//!
//! ```rust
//! use sol_parser_sdk::core::cache::build_account_pubkeys_with_cache;
//! use solana_sdk::pubkey::Pubkey;
//!
//! let instruction_accounts = vec![0u8, 1, 2];
//! let all_accounts = vec![Pubkey::default(); 10];
//!
//! // 使用线程局部缓存,避免重复分配
//! let account_pubkeys = build_account_pubkeys_with_cache(&instruction_accounts, &all_accounts);
//! ```

use solana_sdk::pubkey::Pubkey;
use std::cell::RefCell;

// ============================================================================
// 账户公钥缓存工具(Account Pubkey Cache)
// ============================================================================

/// 高性能账户公钥缓存
///
/// 通过重用内存避免重复Vec分配,提升性能
#[derive(Debug)]
pub struct AccountPubkeyCache {
    /// 预分配的账户公钥向量
    cache: Vec<Pubkey>,
}

impl AccountPubkeyCache {
    /// 创建新的账户公钥缓存
    ///
    /// 预分配32个位置,覆盖大多数交易场景
    pub fn new() -> Self {
        Self { cache: Vec::with_capacity(32) }
    }

    /// 从指令账户索引构建账户公钥向量
    ///
    /// # 参数
    /// - `instruction_accounts`: 指令账户索引列表
    /// - `all_accounts`: 所有账户公钥列表
    ///
    /// # 返回
    /// 账户公钥切片引用
    ///
    /// # 性能优化
    /// - 重用内部缓存,避免重新分配
    /// - 仅在必要时扩容
    #[inline]
    pub fn build_account_pubkeys(
        &mut self,
        instruction_accounts: &[u8],
        all_accounts: &[Pubkey],
    ) -> &[Pubkey] {
        self.cache.clear();

        // 确保容量足够,避免动态扩容
        if self.cache.capacity() < instruction_accounts.len() {
            self.cache.reserve(instruction_accounts.len() - self.cache.capacity());
        }

        // 快速填充账户公钥(带边界检查)
        for &idx in instruction_accounts.iter() {
            if (idx as usize) < all_accounts.len() {
                self.cache.push(all_accounts[idx as usize]);
            }
        }

        &self.cache
    }
}

impl Default for AccountPubkeyCache {
    fn default() -> Self {
        Self::new()
    }
}

thread_local! {
    static THREAD_LOCAL_ACCOUNT_CACHE: RefCell<AccountPubkeyCache> =
        RefCell::new(AccountPubkeyCache::new());
}

/// 从线程局部缓存构建账户公钥列表
///
/// # 参数
/// - `instruction_accounts`: 指令账户索引列表
/// - `all_accounts`: 所有账户公钥列表
///
/// # 返回
/// 账户公钥向量
///
/// # 线程安全
/// 使用线程局部存储,每个线程独立缓存
///
/// # 性能
/// - 首次调用:分配缓存(约 1μs)
/// - 后续调用:重用缓存(约 100ns)
///
/// # 示例
/// ```rust
/// use sol_parser_sdk::core::cache::build_account_pubkeys_with_cache;
/// use solana_sdk::pubkey::Pubkey;
///
/// let instruction_accounts = vec![0u8, 1, 2];
/// let all_accounts = vec![Pubkey::default(); 10];
///
/// let account_pubkeys = build_account_pubkeys_with_cache(&instruction_accounts, &all_accounts);
/// assert_eq!(account_pubkeys.len(), 3);
/// ```
#[inline]
pub fn build_account_pubkeys_with_cache(
    instruction_accounts: &[u8],
    all_accounts: &[Pubkey],
) -> Vec<Pubkey> {
    THREAD_LOCAL_ACCOUNT_CACHE.with(|cache| {
        let mut cache = cache.borrow_mut();
        cache.build_account_pubkeys(instruction_accounts, all_accounts).to_vec()
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_account_pubkey_cache_basic() {
        let mut cache = AccountPubkeyCache::new();
        let all_accounts = vec![Pubkey::new_unique(), Pubkey::new_unique(), Pubkey::new_unique()];
        let instruction_accounts = vec![0u8, 2];

        let result = cache.build_account_pubkeys(&instruction_accounts, &all_accounts);
        assert_eq!(result.len(), 2);
        assert_eq!(result[0], all_accounts[0]);
        assert_eq!(result[1], all_accounts[2]);
    }

    #[test]
    fn test_account_pubkey_cache_reuse() {
        let mut cache = AccountPubkeyCache::new();
        let all_accounts = vec![Pubkey::new_unique(); 10];

        // 第一次调用
        let instruction_accounts = vec![0u8, 1, 2];
        let result1 = cache.build_account_pubkeys(&instruction_accounts, &all_accounts);
        assert_eq!(result1.len(), 3);

        // 第二次调用 - 应该重用缓存
        let instruction_accounts = vec![5u8, 6];
        let result2 = cache.build_account_pubkeys(&instruction_accounts, &all_accounts);
        assert_eq!(result2.len(), 2);
        assert_eq!(result2[0], all_accounts[5]);
        assert_eq!(result2[1], all_accounts[6]);
    }

    #[test]
    fn test_account_pubkey_cache_out_of_bounds() {
        let mut cache = AccountPubkeyCache::new();
        let all_accounts = vec![Pubkey::new_unique(); 3];
        let instruction_accounts = vec![0u8, 1, 10]; // 10 超出范围

        let result = cache.build_account_pubkeys(&instruction_accounts, &all_accounts);
        assert_eq!(result.len(), 2); // 只有前两个有效
    }

    #[test]
    fn test_thread_local_cache() {
        let all_accounts = vec![Pubkey::new_unique(); 5];
        let instruction_accounts = vec![0u8, 1, 2];

        let result = build_account_pubkeys_with_cache(&instruction_accounts, &all_accounts);
        assert_eq!(result.len(), 3);
        assert_eq!(result[0], all_accounts[0]);
        assert_eq!(result[1], all_accounts[1]);
        assert_eq!(result[2], all_accounts[2]);
    }

    #[test]
    fn test_thread_local_cache_multiple_calls() {
        let all_accounts = vec![Pubkey::new_unique(); 10];

        // 第一次调用
        let result1 = build_account_pubkeys_with_cache(&[0u8, 1], &all_accounts);
        assert_eq!(result1.len(), 2);

        // 第二次调用 - 应该重用线程局部缓存
        let result2 = build_account_pubkeys_with_cache(&[5u8, 6, 7], &all_accounts);
        assert_eq!(result2.len(), 3);
    }
}