1use crate::core::events::*;
13use memchr::memmem;
14use once_cell::sync::Lazy;
15use solana_sdk::{pubkey::Pubkey, signature::Signature};
16
17#[cfg(feature = "perf-stats")]
18use std::sync::atomic::{AtomicUsize, Ordering};
19
20#[cfg(feature = "perf-stats")]
25pub static PARSE_COUNT: AtomicUsize = AtomicUsize::new(0);
26#[cfg(feature = "perf-stats")]
27pub static PARSE_TIME_NS: AtomicUsize = AtomicUsize::new(0);
28
29pub mod discriminators {
35 pub const CREATE_EVENT: u64 = u64::from_le_bytes([27, 114, 169, 77, 222, 235, 99, 118]);
36 pub const TRADE_EVENT: u64 = u64::from_le_bytes([189, 219, 127, 211, 78, 230, 97, 238]);
37 pub const MIGRATE_EVENT: u64 = u64::from_le_bytes([189, 233, 93, 185, 92, 148, 234, 148]);
38}
39
40static BASE64_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"Program data: "));
42
43#[inline(always)]
52fn extract_program_data_zero_copy<'a>(log: &'a str, buf: &'a mut [u8; 2048]) -> Option<&'a [u8]> {
53 let log_bytes = log.as_bytes();
54 let pos = BASE64_FINDER.find(log_bytes)?;
55
56 let data_part = &log[pos + 14..];
57 let trimmed = data_part.trim();
58
59 if trimmed.len() > 2700 {
62 return None;
63 }
64
65 use base64_simd::AsOut;
67 let decoded_slice = base64_simd::STANDARD
68 .decode(trimmed.as_bytes(), buf.as_mut().as_out())
69 .ok()?;
70
71 Some(decoded_slice)
72}
73
74#[inline(always)]
76fn extract_discriminator_simd(log: &str) -> Option<u64> {
77 let log_bytes = log.as_bytes();
78 let pos = BASE64_FINDER.find(log_bytes)?;
79
80 let data_part = &log[pos + 14..];
81 let trimmed = data_part.trim();
82
83 if trimmed.len() < 12 {
84 return None;
85 }
86
87 use base64_simd::AsOut;
89 let mut buf = [0u8; 12];
90 base64_simd::STANDARD
91 .decode(&trimmed.as_bytes()[..16], buf.as_mut().as_out())
92 .ok()?;
93
94 unsafe {
96 let ptr = buf.as_ptr() as *const u64;
97 Some(ptr.read_unaligned())
98 }
99}
100
101#[inline(always)]
107unsafe fn read_u64_unchecked(data: &[u8], offset: usize) -> u64 {
108 let ptr = data.as_ptr().add(offset) as *const u64;
109 u64::from_le(ptr.read_unaligned())
110}
111
112#[inline(always)]
114unsafe fn read_i64_unchecked(data: &[u8], offset: usize) -> i64 {
115 let ptr = data.as_ptr().add(offset) as *const i64;
116 i64::from_le(ptr.read_unaligned())
117}
118
119#[inline(always)]
121unsafe fn read_bool_unchecked(data: &[u8], offset: usize) -> bool {
122 *data.get_unchecked(offset) == 1
123}
124
125#[inline(always)]
129unsafe fn read_pubkey_unchecked(data: &[u8], offset: usize) -> Pubkey {
130 #[cfg(target_arch = "x86_64")]
133 {
134 use std::arch::x86_64::_mm_prefetch;
135 use std::arch::x86_64::_MM_HINT_T0;
136 if offset + 64 < data.len() {
137 _mm_prefetch((data.as_ptr().add(offset + 32)) as *const i8, _MM_HINT_T0);
138 }
139 }
140
141 let ptr = data.as_ptr().add(offset);
142 let mut bytes = [0u8; 32];
143 std::ptr::copy_nonoverlapping(ptr, bytes.as_mut_ptr(), 32);
144 Pubkey::new_from_array(bytes)
145}
146
147#[inline(always)]
151unsafe fn read_str_unchecked(data: &[u8], offset: usize) -> Option<(&str, usize)> {
152 if data.len() < offset + 4 {
153 return None;
154 }
155
156 let len = read_u32_unchecked(data, offset) as usize;
157 if data.len() < offset + 4 + len {
158 return None;
159 }
160
161 let string_bytes = &data[offset + 4..offset + 4 + len];
162 let s = std::str::from_utf8_unchecked(string_bytes);
163 Some((s, 4 + len))
164}
165
166#[inline(always)]
168unsafe fn read_u32_unchecked(data: &[u8], offset: usize) -> u32 {
169 let ptr = data.as_ptr().add(offset) as *const u32;
170 u32::from_le(ptr.read_unaligned())
171}
172
173#[inline(always)]
181pub fn parse_log(
182 log: &str,
183 signature: Signature,
184 slot: u64,
185 tx_index: u64,
186 block_time_us: Option<i64>,
187 grpc_recv_us: i64,
188 is_created_buy: bool,
189) -> Option<DexEvent> {
190 #[cfg(feature = "perf-stats")]
191 let start = std::time::Instant::now();
192
193 let mut buf = [0u8; 2048];
195 let program_data = extract_program_data_zero_copy(log, &mut buf)?;
196
197 if program_data.len() < 8 {
198 return None;
199 }
200
201 let discriminator = unsafe { read_u64_unchecked(program_data, 0) };
203 let data = &program_data[8..];
204
205 let result = match discriminator {
206 discriminators::CREATE_EVENT => {
207 parse_create_event_optimized(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
208 }
209 discriminators::TRADE_EVENT => {
210 parse_trade_event_optimized(data, signature, slot, tx_index, block_time_us, grpc_recv_us, is_created_buy)
211 }
212 discriminators::MIGRATE_EVENT => {
213 parse_migrate_event_optimized(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
214 }
215 _ => None,
216 };
217
218 #[cfg(feature = "perf-stats")]
219 {
220 PARSE_COUNT.fetch_add(1, Ordering::Relaxed);
221 PARSE_TIME_NS.fetch_add(start.elapsed().as_nanos() as usize, Ordering::Relaxed);
222 }
223
224 result
225}
226
227#[inline(always)]
234fn parse_create_event_optimized(
235 data: &[u8],
236 signature: Signature,
237 slot: u64,
238 tx_index: u64,
239 block_time_us: Option<i64>,
240 grpc_recv_us: i64,
241) -> Option<DexEvent> {
242 unsafe {
243 let mut offset = 0;
244
245 let (name, name_len) = read_str_unchecked(data, offset)?;
247 offset += name_len;
248
249 let (symbol, symbol_len) = read_str_unchecked(data, offset)?;
250 offset += symbol_len;
251
252 let (uri, uri_len) = read_str_unchecked(data, offset)?;
253 offset += uri_len;
254
255 if data.len() < offset + 32 + 32 + 32 + 32 + 8 + 8 + 8 + 8 + 8 + 32 + 1 {
257 return None;
258 }
259
260 let mint = read_pubkey_unchecked(data, offset);
262 offset += 32;
263
264 let bonding_curve = read_pubkey_unchecked(data, offset);
265 offset += 32;
266
267 let user = read_pubkey_unchecked(data, offset);
268 offset += 32;
269
270 let creator = read_pubkey_unchecked(data, offset);
271 offset += 32;
272
273 let timestamp = read_i64_unchecked(data, offset);
275 offset += 8;
276
277 let virtual_token_reserves = read_u64_unchecked(data, offset);
278 offset += 8;
279
280 let virtual_sol_reserves = read_u64_unchecked(data, offset);
281 offset += 8;
282
283 let real_token_reserves = read_u64_unchecked(data, offset);
284 offset += 8;
285
286 let token_total_supply = read_u64_unchecked(data, offset);
287 offset += 8;
288
289 let token_program = if offset + 32 <= data.len() {
290 read_pubkey_unchecked(data, offset)
291 } else {
292 Pubkey::default()
293 };
294 offset += 32;
295
296 let is_mayhem_mode = if offset < data.len() {
297 read_bool_unchecked(data, offset)
298 } else {
299 false
300 };
301
302 let metadata = EventMetadata {
303 signature,
304 slot,
305 tx_index,
306 block_time_us: block_time_us.unwrap_or(0),
307 grpc_recv_us,
308 };
309
310 Some(DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
313 metadata,
314 name: name.to_string(),
315 symbol: symbol.to_string(),
316 uri: uri.to_string(),
317 mint,
318 bonding_curve,
319 user,
320 creator,
321 timestamp,
322 virtual_token_reserves,
323 virtual_sol_reserves,
324 real_token_reserves,
325 token_total_supply,
326 token_program,
327 is_mayhem_mode,
328 }))
329 }
330}
331
332#[inline(always)]
340fn parse_trade_event_optimized(
341 data: &[u8],
342 signature: Signature,
343 slot: u64,
344 tx_index: u64,
345 block_time_us: Option<i64>,
346 grpc_recv_us: i64,
347 is_created_buy: bool,
348) -> Option<DexEvent> {
349 unsafe {
350 if data.len() < 32 + 8 + 8 + 1 + 32 + 8 + 8 + 8 + 8 + 8 + 32 + 8 + 8 + 32 + 8 + 8 {
352 return None;
353 }
354
355 let mut offset = 0;
356
357 let mint = read_pubkey_unchecked(data, offset);
358 offset += 32;
359
360 let sol_amount = read_u64_unchecked(data, offset);
361 offset += 8;
362
363 let token_amount = read_u64_unchecked(data, offset);
364 offset += 8;
365
366 let is_buy = read_bool_unchecked(data, offset);
367 offset += 1;
368
369 let user = read_pubkey_unchecked(data, offset);
370 offset += 32;
371
372 let timestamp = read_i64_unchecked(data, offset);
373 offset += 8;
374
375 let virtual_sol_reserves = read_u64_unchecked(data, offset);
376 offset += 8;
377
378 let virtual_token_reserves = read_u64_unchecked(data, offset);
379 offset += 8;
380
381 let real_sol_reserves = read_u64_unchecked(data, offset);
382 offset += 8;
383
384 let real_token_reserves = read_u64_unchecked(data, offset);
385 offset += 8;
386
387 let fee_recipient = read_pubkey_unchecked(data, offset);
388 offset += 32;
389
390 let fee_basis_points = read_u64_unchecked(data, offset);
391 offset += 8;
392
393 let fee = read_u64_unchecked(data, offset);
394 offset += 8;
395
396 let creator = read_pubkey_unchecked(data, offset);
397 offset += 32;
398
399 let creator_fee_basis_points = read_u64_unchecked(data, offset);
400 offset += 8;
401
402 let creator_fee = read_u64_unchecked(data, offset);
403 offset += 8;
404
405 let track_volume = if offset < data.len() {
407 read_bool_unchecked(data, offset)
408 } else {
409 false
410 };
411 offset += 1;
412
413 let total_unclaimed_tokens = if offset + 8 <= data.len() {
414 read_u64_unchecked(data, offset)
415 } else {
416 0
417 };
418 offset += 8;
419
420 let total_claimed_tokens = if offset + 8 <= data.len() {
421 read_u64_unchecked(data, offset)
422 } else {
423 0
424 };
425 offset += 8;
426
427 let current_sol_volume = if offset + 8 <= data.len() {
428 read_u64_unchecked(data, offset)
429 } else {
430 0
431 };
432 offset += 8;
433
434 let last_update_timestamp = if offset + 8 <= data.len() {
435 read_i64_unchecked(data, offset)
436 } else {
437 0
438 };
439 offset += 8;
440
441 let ix_name = if offset + 4 <= data.len() {
444 if let Some((s, len)) = read_str_unchecked(data, offset) {
445 offset += len;
446 s.to_string()
447 } else {
448 String::new()
449 }
450 } else {
451 String::new()
452 };
453
454 let mayhem_mode = if offset < data.len() {
456 read_bool_unchecked(data, offset)
457 } else {
458 false
459 };
460
461 let metadata = EventMetadata {
462 signature,
463 slot,
464 tx_index,
465 block_time_us: block_time_us.unwrap_or(0),
466 grpc_recv_us,
467 };
468
469 let trade_event = PumpFunTradeEvent {
470 metadata,
471 mint,
472 sol_amount,
473 token_amount,
474 is_buy,
475 is_created_buy,
476 user,
477 timestamp,
478 virtual_sol_reserves,
479 virtual_token_reserves,
480 real_sol_reserves,
481 real_token_reserves,
482 fee_recipient,
483 fee_basis_points,
484 fee,
485 creator,
486 creator_fee_basis_points,
487 creator_fee,
488 track_volume,
489 total_unclaimed_tokens,
490 total_claimed_tokens,
491 current_sol_volume,
492 last_update_timestamp,
493 ix_name: ix_name.clone(),
494 mayhem_mode,
495 bonding_curve: Pubkey::default(),
496 associated_bonding_curve: Pubkey::default(),
497 creator_vault: Pubkey::default(),
498 token_program: Pubkey::default(),
499 };
500
501 match ix_name.as_str() {
503 "buy" => Some(DexEvent::PumpFunBuy(trade_event)),
504 "sell" => Some(DexEvent::PumpFunSell(trade_event)),
505 "buy_exact_sol_in" => Some(DexEvent::PumpFunBuyExactSolIn(trade_event)),
506 _ => Some(DexEvent::PumpFunTrade(trade_event)), }
508 }
509}
510
511#[inline(always)]
513fn parse_migrate_event_optimized(
514 data: &[u8],
515 signature: Signature,
516 slot: u64,
517 tx_index: u64,
518 block_time_us: Option<i64>,
519 grpc_recv_us: i64,
520) -> Option<DexEvent> {
521 unsafe {
522 if data.len() < 32 + 32 + 8 + 8 + 8 + 32 + 8 + 32 {
524 return None;
525 }
526
527 let mut offset = 0;
528
529 let user = read_pubkey_unchecked(data, offset);
530 offset += 32;
531
532 let mint = read_pubkey_unchecked(data, offset);
533 offset += 32;
534
535 let mint_amount = read_u64_unchecked(data, offset);
536 offset += 8;
537
538 let sol_amount = read_u64_unchecked(data, offset);
539 offset += 8;
540
541 let pool_migration_fee = read_u64_unchecked(data, offset);
542 offset += 8;
543
544 let bonding_curve = read_pubkey_unchecked(data, offset);
545 offset += 32;
546
547 let timestamp = read_i64_unchecked(data, offset);
548 offset += 8;
549
550 let pool = read_pubkey_unchecked(data, offset);
551
552 let metadata = EventMetadata {
553 signature,
554 slot,
555 tx_index,
556 block_time_us: block_time_us.unwrap_or(0),
557 grpc_recv_us,
558 };
559
560 Some(DexEvent::PumpFunMigrate(PumpFunMigrateEvent {
561 metadata,
562 user,
563 mint,
564 mint_amount,
565 sol_amount,
566 pool_migration_fee,
567 bonding_curve,
568 timestamp,
569 pool,
570 }))
571 }
572}
573
574#[inline(always)]
582pub fn get_event_type_fast(log: &str) -> Option<u64> {
583 extract_discriminator_simd(log)
584}
585
586#[inline(always)]
588pub fn is_event_type(log: &str, discriminator: u64) -> bool {
589 extract_discriminator_simd(log) == Some(discriminator)
590}
591
592#[inline(always)]
607pub fn parse_trade_from_data(data: &[u8], metadata: EventMetadata, is_created_buy: bool) -> Option<DexEvent> {
608 unsafe {
609 if data.len() < 32 + 8 + 8 + 1 + 32 + 8 + 8 + 8 + 8 + 8 + 32 + 8 + 8 + 32 + 8 + 8 {
611 return None;
612 }
613
614 let mut offset = 0;
615
616 let mint = read_pubkey_unchecked(data, offset);
617 offset += 32;
618
619 let sol_amount = read_u64_unchecked(data, offset);
620 offset += 8;
621
622 let token_amount = read_u64_unchecked(data, offset);
623 offset += 8;
624
625 let is_buy = read_bool_unchecked(data, offset);
626 offset += 1;
627
628 let user = read_pubkey_unchecked(data, offset);
629 offset += 32;
630
631 let timestamp = read_i64_unchecked(data, offset);
632 offset += 8;
633
634 let virtual_sol_reserves = read_u64_unchecked(data, offset);
635 offset += 8;
636
637 let virtual_token_reserves = read_u64_unchecked(data, offset);
638 offset += 8;
639
640 let real_sol_reserves = read_u64_unchecked(data, offset);
641 offset += 8;
642
643 let real_token_reserves = read_u64_unchecked(data, offset);
644 offset += 8;
645
646 let fee_recipient = read_pubkey_unchecked(data, offset);
647 offset += 32;
648
649 let fee_basis_points = read_u64_unchecked(data, offset);
650 offset += 8;
651
652 let fee = read_u64_unchecked(data, offset);
653 offset += 8;
654
655 let creator = read_pubkey_unchecked(data, offset);
656 offset += 32;
657
658 let creator_fee_basis_points = read_u64_unchecked(data, offset);
659 offset += 8;
660
661 let creator_fee = read_u64_unchecked(data, offset);
662 offset += 8;
663
664 let track_volume = if offset < data.len() {
666 read_bool_unchecked(data, offset)
667 } else {
668 false
669 };
670 offset += 1;
671
672 let total_unclaimed_tokens = if offset + 8 <= data.len() {
673 read_u64_unchecked(data, offset)
674 } else {
675 0
676 };
677 offset += 8;
678
679 let total_claimed_tokens = if offset + 8 <= data.len() {
680 read_u64_unchecked(data, offset)
681 } else {
682 0
683 };
684 offset += 8;
685
686 let current_sol_volume = if offset + 8 <= data.len() {
687 read_u64_unchecked(data, offset)
688 } else {
689 0
690 };
691 offset += 8;
692
693 let last_update_timestamp = if offset + 8 <= data.len() {
694 read_i64_unchecked(data, offset)
695 } else {
696 0
697 };
698 offset += 8;
699
700 let ix_name = if offset + 4 <= data.len() {
701 if let Some((s, len)) = read_str_unchecked(data, offset) {
702 offset += len;
703 s.to_string()
704 } else {
705 String::new()
706 }
707 } else {
708 String::new()
709 };
710
711 let mayhem_mode = if offset < data.len() {
713 read_bool_unchecked(data, offset)
714 } else {
715 false
716 };
717
718 let trade_event = PumpFunTradeEvent {
719 metadata,
720 mint,
721 sol_amount,
722 token_amount,
723 is_buy,
724 is_created_buy,
725 user,
726 timestamp,
727 virtual_sol_reserves,
728 virtual_token_reserves,
729 real_sol_reserves,
730 real_token_reserves,
731 fee_recipient,
732 fee_basis_points,
733 fee,
734 creator,
735 creator_fee_basis_points,
736 creator_fee,
737 track_volume,
738 total_unclaimed_tokens,
739 total_claimed_tokens,
740 current_sol_volume,
741 last_update_timestamp,
742 ix_name: ix_name.clone(),
743 mayhem_mode,
744 bonding_curve: Pubkey::default(),
745 associated_bonding_curve: Pubkey::default(),
746 creator_vault: Pubkey::default(),
747 token_program: Pubkey::default(),
748 };
749
750 match ix_name.as_str() {
752 "buy" => Some(DexEvent::PumpFunBuy(trade_event)),
753 "sell" => Some(DexEvent::PumpFunSell(trade_event)),
754 "buy_exact_sol_in" => Some(DexEvent::PumpFunBuyExactSolIn(trade_event)),
755 _ => Some(DexEvent::PumpFunTrade(trade_event)),
756 }
757 }
758}
759
760#[inline(always)]
764pub fn parse_buy_from_data(data: &[u8], metadata: EventMetadata, is_created_buy: bool) -> Option<DexEvent> {
765 let event = parse_trade_from_data(data, metadata, is_created_buy)?;
766 match &event {
767 DexEvent::PumpFunBuy(_) => Some(event),
768 _ => None,
769 }
770}
771
772#[inline(always)]
776pub fn parse_sell_from_data(data: &[u8], metadata: EventMetadata, is_created_buy: bool) -> Option<DexEvent> {
777 let event = parse_trade_from_data(data, metadata, is_created_buy)?;
778 match &event {
779 DexEvent::PumpFunSell(_) => Some(event),
780 _ => None,
781 }
782}
783
784#[inline(always)]
788pub fn parse_buy_exact_sol_in_from_data(data: &[u8], metadata: EventMetadata, is_created_buy: bool) -> Option<DexEvent> {
789 let event = parse_trade_from_data(data, metadata, is_created_buy)?;
790 match &event {
791 DexEvent::PumpFunBuyExactSolIn(_) => Some(event),
792 _ => None,
793 }
794}
795
796#[inline(always)]
798pub fn parse_create_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
799 unsafe {
800 let mut offset = 0;
801
802 let (name, name_len) = read_str_unchecked(data, offset)?;
803 offset += name_len;
804
805 let (symbol, symbol_len) = read_str_unchecked(data, offset)?;
806 offset += symbol_len;
807
808 let (uri, uri_len) = read_str_unchecked(data, offset)?;
809 offset += uri_len;
810
811 if data.len() < offset + 32 + 32 + 32 + 32 + 8 + 8 + 8 + 8 + 8 + 32 + 1 {
812 return None;
813 }
814
815 let mint = read_pubkey_unchecked(data, offset);
816 offset += 32;
817
818 let bonding_curve = read_pubkey_unchecked(data, offset);
819 offset += 32;
820
821 let user = read_pubkey_unchecked(data, offset);
822 offset += 32;
823
824 let creator = read_pubkey_unchecked(data, offset);
825 offset += 32;
826
827 let timestamp = read_i64_unchecked(data, offset);
828 offset += 8;
829
830 let virtual_token_reserves = read_u64_unchecked(data, offset);
831 offset += 8;
832
833 let virtual_sol_reserves = read_u64_unchecked(data, offset);
834 offset += 8;
835
836 let real_token_reserves = read_u64_unchecked(data, offset);
837 offset += 8;
838
839 let token_total_supply = read_u64_unchecked(data, offset);
840 offset += 8;
841
842 let token_program = if offset + 32 <= data.len() {
843 read_pubkey_unchecked(data, offset)
844 } else {
845 Pubkey::default()
846 };
847 offset += 32;
848
849 let is_mayhem_mode = if offset < data.len() {
850 read_bool_unchecked(data, offset)
851 } else {
852 false
853 };
854
855 Some(DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
856 metadata,
857 name: name.to_string(),
858 symbol: symbol.to_string(),
859 uri: uri.to_string(),
860 mint,
861 bonding_curve,
862 user,
863 creator,
864 timestamp,
865 virtual_token_reserves,
866 virtual_sol_reserves,
867 real_token_reserves,
868 token_total_supply,
869 token_program,
870 is_mayhem_mode,
871 }))
872 }
873}
874
875#[inline(always)]
877pub fn parse_migrate_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
878 unsafe {
879 if data.len() < 32 + 32 + 8 + 8 + 8 + 32 + 8 + 32 {
880 return None;
881 }
882
883 let mut offset = 0;
884
885 let user = read_pubkey_unchecked(data, offset);
886 offset += 32;
887
888 let mint = read_pubkey_unchecked(data, offset);
889 offset += 32;
890
891 let mint_amount = read_u64_unchecked(data, offset);
892 offset += 8;
893
894 let sol_amount = read_u64_unchecked(data, offset);
895 offset += 8;
896
897 let pool_migration_fee = read_u64_unchecked(data, offset);
898 offset += 8;
899
900 let bonding_curve = read_pubkey_unchecked(data, offset);
901 offset += 32;
902
903 let timestamp = read_i64_unchecked(data, offset);
904 offset += 8;
905
906 let pool = read_pubkey_unchecked(data, offset);
907
908 Some(DexEvent::PumpFunMigrate(PumpFunMigrateEvent {
909 metadata,
910 user,
911 mint,
912 mint_amount,
913 sol_amount,
914 pool_migration_fee,
915 bonding_curve,
916 timestamp,
917 pool,
918 }))
919 }
920}
921
922#[cfg(feature = "perf-stats")]
927pub fn get_perf_stats() -> (usize, usize) {
928 let count = PARSE_COUNT.load(Ordering::Relaxed);
929 let total_ns = PARSE_TIME_NS.load(Ordering::Relaxed);
930 (count, total_ns)
931}
932
933#[cfg(feature = "perf-stats")]
934pub fn reset_perf_stats() {
935 PARSE_COUNT.store(0, Ordering::Relaxed);
936 PARSE_TIME_NS.store(0, Ordering::Relaxed);
937}
938
939#[cfg(test)]
940mod tests {
941 use super::*;
942
943 #[test]
944 fn test_discriminator_simd() {
945 let log = "Program data: G3Kp5Dfe605nAAAAAAAAAAA=";
947 let disc = extract_discriminator_simd(log);
948 assert!(disc.is_some());
949 }
950
951 #[test]
952 fn test_parse_performance() {
953 let log = "Program data: G3Kp5Dfe605nAAAAAAAAAAA=";
955 let sig = Signature::default();
956
957 let start = std::time::Instant::now();
958 for _ in 0..1000 {
959 let _ = parse_log(log, sig, 0, 0, Some(0), 0, false);
960 }
961 let elapsed = start.elapsed();
962
963 println!("Average parse time: {} ns", elapsed.as_nanos() / 1000);
964 }
965}