Skip to content

Commit 58ef878

Browse files
authored
Merge pull request #20 from doublegate/copilot/sub-pr-18
Migrate from bincode to rkyv v0.8.14 for memory-mapped serialization
2 parents f8e4134 + f166517 commit 58ef878

File tree

9 files changed

+215
-31
lines changed

9 files changed

+215
-31
lines changed

Cargo.lock

Lines changed: 119 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ rusqlite = { version = "0.31", features = ["bundled"] }
4343

4444
# Memory-mapped I/O (Sprint 6.6)
4545
memmap2 = "0.9"
46-
bincode = "1.3"
46+
rkyv = "0.8.14"
4747

4848
# CSV export (promote to workspace)
4949
csv = "1.3"

crates/prtip-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ rand = { workspace = true }
3030
sysinfo = { workspace = true }
3131
flate2 = "1.0"
3232
dirs = "5.0"
33+
rkyv = { workspace = true }
3334

3435
[dev-dependencies]
3536
tokio = { workspace = true }

crates/prtip-core/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,4 @@ pub use resource_monitor::{
136136
pub use retry::{retry_with_backoff, RetryConfig};
137137
pub use service_db::{ServiceMatch, ServiceProbe, ServiceProbeDb};
138138
pub use templates::{ScanTemplate, TemplateManager};
139-
pub use types::{PortRange, PortState, Protocol, ScanResult, ScanTarget, ScanType, TimingTemplate};
139+
pub use types::{PortRange, PortState, Protocol, ScanResult, ScanResultRkyv, ScanTarget, ScanType, TimingTemplate};

crates/prtip-core/src/types.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use crate::error::{Error, Result};
44
use chrono::{DateTime, Utc};
55
use ipnetwork::IpNetwork;
6+
use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
67
use serde::{Deserialize, Serialize};
78
use std::fmt;
89
use std::net::IpAddr;
@@ -315,7 +316,8 @@ impl Iterator for PortRangeIterator {
315316
}
316317

317318
/// State of a scanned port
318-
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
319+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Archive, RkyvSerialize, RkyvDeserialize)]
320+
#[rkyv(derive(Debug))]
319321
pub enum PortState {
320322
/// Port is open and accepting connections
321323
Open,
@@ -475,6 +477,53 @@ impl fmt::Display for TimingTemplate {
475477
}
476478
}
477479

480+
/// Serializable representation of ScanResult for rkyv
481+
#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize)]
482+
#[rkyv(derive(Debug))]
483+
pub struct ScanResultRkyv {
484+
target_ip: IpAddr,
485+
port: u16,
486+
state: PortState,
487+
response_time_nanos: u128,
488+
timestamp_nanos: i64,
489+
banner: Option<String>,
490+
service: Option<String>,
491+
version: Option<String>,
492+
raw_response: Option<Vec<u8>>,
493+
}
494+
495+
impl From<&ScanResult> for ScanResultRkyv {
496+
fn from(result: &ScanResult) -> Self {
497+
Self {
498+
target_ip: result.target_ip,
499+
port: result.port,
500+
state: result.state,
501+
response_time_nanos: result.response_time.as_nanos(),
502+
timestamp_nanos: result.timestamp.timestamp_nanos_opt().unwrap_or(0),
503+
banner: result.banner.clone(),
504+
service: result.service.clone(),
505+
version: result.version.clone(),
506+
raw_response: result.raw_response.clone(),
507+
}
508+
}
509+
}
510+
511+
impl From<ScanResultRkyv> for ScanResult {
512+
fn from(rkyv: ScanResultRkyv) -> Self {
513+
Self {
514+
target_ip: rkyv.target_ip,
515+
port: rkyv.port,
516+
state: rkyv.state,
517+
response_time: Duration::from_nanos(rkyv.response_time_nanos as u64),
518+
timestamp: DateTime::from_timestamp_nanos(rkyv.timestamp_nanos),
519+
banner: rkyv.banner,
520+
service: rkyv.service,
521+
version: rkyv.version,
522+
raw_response: rkyv.raw_response,
523+
}
524+
}
525+
}
526+
478527
/// Result of scanning a single port
479528
#[derive(Debug, Clone, Serialize, Deserialize)]
480529
pub struct ScanResult {

crates/prtip-scanner/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ pcap-file = "2.0"
8787

8888
# Memory-mapped I/O
8989
memmap2 = { workspace = true }
90-
bincode = { workspace = true }
90+
rkyv = { workspace = true, features = ["alloc"] }
9191

9292
[dev-dependencies]
9393
tokio = { workspace = true }

crates/prtip-scanner/src/output/mmap_reader.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
//! Memory-mapped result reader for zero-copy access to scan results
22
33
use memmap2::Mmap;
4-
use prtip_core::ScanResult;
4+
use prtip_core::{ScanResult, ScanResultRkyv};
55
use std::fs::File;
66
use std::io;
77
use std::path::Path;
88

99
const HEADER_SIZE: usize = 64;
1010
const ENTRY_SIZE: usize = 512;
11+
const LENGTH_PREFIX_SIZE: usize = 8; // 8 bytes for length to maintain alignment
1112

1213
/// Memory-mapped result reader
1314
pub struct MmapResultReader {
@@ -75,10 +76,24 @@ impl MmapResultReader {
7576
}
7677

7778
let offset = HEADER_SIZE + (index * self.entry_size);
78-
let entry_bytes = &self.mmap[offset..offset + self.entry_size];
79+
80+
// Read length prefix (8 bytes)
81+
let len_bytes: [u8; 8] = self.mmap[offset..offset + LENGTH_PREFIX_SIZE].try_into().ok()?;
82+
let len = u64::from_le_bytes(len_bytes) as usize;
83+
84+
if len == 0 || len + LENGTH_PREFIX_SIZE > self.entry_size {
85+
return None;
86+
}
7987

80-
// Deserialize the entry (bincode handles trailing zeros)
81-
bincode::deserialize(entry_bytes).ok()
88+
// Copy data to an aligned buffer (rkyv requires alignment)
89+
let entry_bytes = &self.mmap[offset + LENGTH_PREFIX_SIZE..offset + LENGTH_PREFIX_SIZE + len];
90+
let aligned_data: Vec<u8> = entry_bytes.to_vec();
91+
92+
// Deserialize using rkyv
93+
match rkyv::from_bytes::<ScanResultRkyv, rkyv::rancor::Error>(&aligned_data) {
94+
Ok(rkyv_result) => Some(rkyv_result.into()),
95+
Err(_) => None,
96+
}
8297
}
8398

8499
/// Create an iterator over all entries

0 commit comments

Comments
 (0)