sol-shred-sdk 4.0.0

Solana raw shred and ShredStream decoding SDK with multi-DEX event parsing.
//! # 解析器缓存模块
//!
//! 提供高性能缓存机制,减少内存分配和重复计算:
//! - 程序ID缓存:避免重复查找和分配
//! - 账户公钥缓存:由调用方持有并跨调用复用,零锁竞争
//!
//! ## 性能优势
//!
//! - **减少 30-50% 内存分配**:通过缓存重用避免重复分配
//! - **零锁竞争**:缓存实例不包含共享状态
//! - **快速查找**:读写锁优化,读操作无阻塞
//!
//! ## 使用示例
//!
//! ```rust
//! use sol_shred_sdk::{core::cache::build_account_pubkeys_with_cache, 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;

// ============================================================================
// 账户公钥缓存工具(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()
    }
}

/// 构建拥有所有权的账户公钥列表
///
/// # 参数
/// - `instruction_accounts`: 指令账户索引列表
/// - `all_accounts`: 所有账户公钥列表
///
/// # 返回
/// 账户公钥向量
///
/// # 性能
/// 返回值拥有数据,因此需要一次精确容量分配。需要跨调用复用内存时,
/// 请直接持有 [`AccountPubkeyCache`] 并调用
/// [`AccountPubkeyCache::build_account_pubkeys`]。
///
/// # 示例
/// ```rust
/// use sol_shred_sdk::{core::cache::build_account_pubkeys_with_cache, 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> {
    let mut account_pubkeys = Vec::with_capacity(instruction_accounts.len());
    account_pubkeys.extend(
        instruction_accounts
            .iter()
            .filter_map(|&index| all_accounts.get(index as usize).copied()),
    );
    account_pubkeys
}

#[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_owned_account_list_builder() {
        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_owned_account_list_builder_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);
    }
}