use crossbeam_queue::ArrayQueue;
use once_cell::sync::Lazy;
use solana_sdk::{
hash::Hash, instruction::Instruction, message::{v0, AddressLookupTableAccount, Message, VersionedMessage}, pubkey::Pubkey
};
use std::sync::Arc;
pub struct PreallocatedTxBuilder {
instructions: Vec<Instruction>,
lookup_tables: Vec<v0::MessageAddressTableLookup>,
}
impl PreallocatedTxBuilder {
fn new() -> Self {
Self {
instructions: Vec::with_capacity(32), lookup_tables: Vec::with_capacity(8), }
}
#[inline(always)]
fn reset(&mut self) {
self.instructions.clear();
self.lookup_tables.clear();
}
#[inline(always)]
pub fn build_zero_alloc(
&mut self,
payer: &Pubkey,
instructions: &[Instruction],
address_lookup_table_account: Option<AddressLookupTableAccount>,
recent_blockhash: Hash,
) -> VersionedMessage {
self.reset();
self.instructions.extend_from_slice(instructions);
if let Some(address_lookup_table_account) = address_lookup_table_account {
let message = v0::Message::try_compile(
payer,
&self.instructions,
&[address_lookup_table_account],
recent_blockhash,
).expect("v0 message compile failed");
VersionedMessage::V0(message)
} else {
let message = Message::new_with_blockhash(
&self.instructions,
Some(payer),
&recent_blockhash,
);
VersionedMessage::Legacy(message)
}
}
}
static TX_BUILDER_POOL: Lazy<Arc<ArrayQueue<PreallocatedTxBuilder>>> = Lazy::new(|| {
let pool = ArrayQueue::new(1000);
for _ in 0..100 {
let _ = pool.push(PreallocatedTxBuilder::new());
}
Arc::new(pool)
});
#[inline(always)]
pub fn acquire_builder() -> PreallocatedTxBuilder {
TX_BUILDER_POOL
.pop()
.unwrap_or_else(|| PreallocatedTxBuilder::new())
}
#[inline(always)]
pub fn release_builder(mut builder: PreallocatedTxBuilder) {
builder.reset();
let _ = TX_BUILDER_POOL.push(builder);
}
pub fn get_pool_stats() -> (usize, usize) {
(TX_BUILDER_POOL.len(), TX_BUILDER_POOL.capacity())
}
pub struct TxBuilderGuard {
builder: Option<PreallocatedTxBuilder>,
}
impl TxBuilderGuard {
pub fn new() -> Self {
Self {
builder: Some(acquire_builder()),
}
}
pub fn get_mut(&mut self) -> &mut PreallocatedTxBuilder {
self.builder.as_mut().unwrap()
}
}
impl Drop for TxBuilderGuard {
fn drop(&mut self) {
if let Some(builder) = self.builder.take() {
release_builder(builder);
}
}
}