|
83 | 83 | //! let v5_parsed: Vec<NetflowPacket> = parsed.into_iter().filter(|p| p.is_v5()).collect(); |
84 | 84 | //! ``` |
85 | 85 | //! |
86 | | -//! ## Stream Processing (Iterator API) |
| 86 | +//! ## Iterator API |
87 | 87 | //! |
88 | 88 | //! For high-performance scenarios where you want to avoid allocating a `Vec`, you can use the iterator API to process packets one-by-one as they're parsed: |
89 | 89 | //! |
|
117 | 117 | //! } |
118 | 118 | //! ``` |
119 | 119 | //! |
| 120 | +//! The iterator provides access to unconsumed bytes for advanced use cases: |
| 121 | +//! |
| 122 | +//! ```rust |
| 123 | +//! use netflow_parser::NetflowParser; |
| 124 | +//! |
| 125 | +//! # let buffer = [0u8; 72]; |
| 126 | +//! let mut parser = NetflowParser::default(); |
| 127 | +//! let mut iter = parser.iter_packets(&buffer); |
| 128 | +//! |
| 129 | +//! while let Some(packet) = iter.next() { |
| 130 | +//! // Process packet |
| 131 | +//! # _ = packet; |
| 132 | +//! } |
| 133 | +//! |
| 134 | +//! // Check if all bytes were consumed |
| 135 | +//! if !iter.is_complete() { |
| 136 | +//! println!("Warning: {} bytes remain unconsumed", iter.remaining().len()); |
| 137 | +//! } |
| 138 | +//! ``` |
| 139 | +//! |
120 | 140 | //! ### Benefits of Iterator API |
121 | 141 | //! |
122 | 142 | //! - **Zero allocation**: Packets are yielded one-by-one without allocating a `Vec` |
123 | 143 | //! - **Memory efficient**: Ideal for processing large batches or continuous streams |
124 | 144 | //! - **Lazy evaluation**: Only parses packets as you consume them |
125 | 145 | //! - **Template caching preserved**: V9/IPFIX template state is maintained across iterations |
126 | 146 | //! - **Composable**: Works with standard Rust iterator methods (`.filter()`, `.map()`, `.take()`, etc.) |
| 147 | +//! - **Buffer inspection**: Access unconsumed bytes via `.remaining()` and check completion with `.is_complete()` |
127 | 148 | //! |
128 | 149 | //! ### Iterator Examples |
129 | 150 | //! |
|
139 | 160 | //! // Process only the first 10 packets |
140 | 161 | //! for packet in parser.iter_packets(&buffer).take(10) { |
141 | 162 | //! // Handle packet |
| 163 | +//! # _ = packet; |
142 | 164 | //! } |
143 | 165 | //! |
144 | 166 | //! // Collect only if needed (equivalent to parse_bytes()) |
145 | 167 | //! let packets: Vec<_> = parser.iter_packets(&buffer).collect(); |
| 168 | +//! |
| 169 | +//! // Check unconsumed bytes (useful for mixed protocol streams) |
| 170 | +//! let mut iter = parser.iter_packets(&buffer); |
| 171 | +//! for packet in &mut iter { |
| 172 | +//! // Process packet |
| 173 | +//! # _ = packet; |
| 174 | +//! } |
| 175 | +//! if !iter.is_complete() { |
| 176 | +//! let remaining = iter.remaining(); |
| 177 | +//! // Handle non-netflow data at end of buffer |
| 178 | +//! # _ = remaining; |
| 179 | +//! } |
146 | 180 | //! ``` |
147 | 181 | //! |
148 | 182 | //! ## Parsing Out Unneeded Versions |
@@ -520,6 +554,59 @@ pub struct NetflowPacketIterator<'a> { |
520 | 554 | errored: bool, |
521 | 555 | } |
522 | 556 |
|
| 557 | +impl<'a> NetflowPacketIterator<'a> { |
| 558 | + /// Returns the unconsumed bytes remaining in the buffer. |
| 559 | + /// |
| 560 | + /// This is useful for: |
| 561 | + /// - Debugging: See how much data was consumed |
| 562 | + /// - Mixed protocols: Process non-netflow data after netflow packets |
| 563 | + /// - Resumption: Know where parsing stopped |
| 564 | + /// |
| 565 | + /// # Examples |
| 566 | + /// |
| 567 | + /// ```rust |
| 568 | + /// use netflow_parser::NetflowParser; |
| 569 | + /// |
| 570 | + /// let v5_packet = [0, 5, 0, 1, 3, 0, 4, 0, 5, 0, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,]; |
| 571 | + /// let mut parser = NetflowParser::default(); |
| 572 | + /// let mut iter = parser.iter_packets(&v5_packet); |
| 573 | + /// |
| 574 | + /// while let Some(_packet) = iter.next() { |
| 575 | + /// // Process packet |
| 576 | + /// } |
| 577 | + /// |
| 578 | + /// // Check how many bytes remain unconsumed |
| 579 | + /// assert_eq!(iter.remaining().len(), 0); |
| 580 | + /// ``` |
| 581 | + pub fn remaining(&self) -> &'a [u8] { |
| 582 | + self.remaining |
| 583 | + } |
| 584 | + |
| 585 | + /// Returns true if all bytes have been consumed or an error occurred. |
| 586 | + /// |
| 587 | + /// This is useful for validation and ensuring complete buffer processing. |
| 588 | + /// |
| 589 | + /// # Examples |
| 590 | + /// |
| 591 | + /// ```rust |
| 592 | + /// use netflow_parser::NetflowParser; |
| 593 | + /// |
| 594 | + /// let v5_packet = [0, 5, 0, 1, 3, 0, 4, 0, 5, 0, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,]; |
| 595 | + /// let mut parser = NetflowParser::default(); |
| 596 | + /// let mut iter = parser.iter_packets(&v5_packet); |
| 597 | + /// |
| 598 | + /// // Consume all packets |
| 599 | + /// for _packet in &mut iter { |
| 600 | + /// // Process packet |
| 601 | + /// } |
| 602 | + /// |
| 603 | + /// assert!(iter.is_complete()); |
| 604 | + /// ``` |
| 605 | + pub fn is_complete(&self) -> bool { |
| 606 | + self.remaining.is_empty() || self.errored |
| 607 | + } |
| 608 | +} |
| 609 | + |
523 | 610 | impl<'a> Iterator for NetflowPacketIterator<'a> { |
524 | 611 | type Item = NetflowPacket; |
525 | 612 |
|
|
0 commit comments