summaryrefslogtreecommitdiffstats
path: root/openpgp/examples/statistics.rs
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2018-05-31 16:29:58 +0200
committerJustus Winter <justus@sequoia-pgp.org>2018-05-31 16:38:00 +0200
commitd9dba05d734d15abb23a6db530fc3f90ddaa710c (patch)
tree51e9f6910ab6c698ccf25c0157101374d3b56fb6 /openpgp/examples/statistics.rs
parent6ca5dcf8e52a282079dc8825f73c9b69f6d3e7e5 (diff)
openpgp: Add example computing statistics over packets.
Diffstat (limited to 'openpgp/examples/statistics.rs')
-rw-r--r--openpgp/examples/statistics.rs194
1 files changed, 194 insertions, 0 deletions
diff --git a/openpgp/examples/statistics.rs b/openpgp/examples/statistics.rs
new file mode 100644
index 00000000..798ee92d
--- /dev/null
+++ b/openpgp/examples/statistics.rs
@@ -0,0 +1,194 @@
+/// This program collects statistics about e.g. the SKS packet dump.
+///
+/// Note that to achieve reasonable performance, you need to compile
+/// Sequoia and this program with optimizations, either by copying
+/// this example to a new project and compile the release target, or
+/// by adding this to Sequoia's top-level Cargo.toml:
+///
+/// [profile.dev]
+/// opt-level = 3
+
+use std::env;
+use std::fs::File;
+extern crate buffered_reader;
+use buffered_reader::BufferedReaderGeneric;
+extern crate openpgp;
+use openpgp::{Packet, Tag};
+use openpgp::packet::BodyLength;
+use openpgp::parse::PacketParser;
+
+fn main() {
+ let args: Vec<String> = env::args().collect();
+ if args.len() != 2 {
+ panic!("Collects statistics about OpenPGP packet dumps.\n\n\
+ Usage: {} <packet-dump>\n", args[0]);
+ }
+
+ // Global stats.
+ let mut packet_count = 0;
+ let mut packet_size = 0 as usize;
+
+ // Per-tag statistics.
+ let mut tags_count = vec![0; 64];
+ let mut tags_unknown = vec![0; 64];
+ let mut tags_size_bytes = vec![0 as usize; 64];
+ let mut tags_size_count = vec![0; 64];
+ let mut tags_size_min = vec![::std::u32::MAX; 64];
+ let mut tags_size_max = vec![0; 64];
+
+ // Per-TPK statistics.
+ let mut tpk_count = 0;
+ let mut tpk = PerTPK::min();
+ let mut tpk_min = PerTPK::max();
+ let mut tpk_max = PerTPK::min();
+
+ // Create a parser.
+ let br = BufferedReaderGeneric::new(
+ File::open(&args[1]).expect("Failed to open file"),
+ Some(128 * 1024 * 1024) // Use a large buffer.
+ );
+ let mut ppo = PacketParser::from_reader(br)
+ .expect("Failed to create reader");
+
+ // Iterate over all packets.
+ while let Some(pp) = ppo {
+ // While the packet is in the parser, get some data for later.
+ let size = match pp.header.length {
+ BodyLength::Full(n) => Some(n),
+ _ => None,
+ };
+
+ // Get the packet and advance the parser.
+ let (packet, _, tmp, _) = pp.next()
+ .expect("Failed to get next packet");
+ ppo = tmp;
+
+ packet_count += 1;
+ if let Some(n) = size {
+ packet_size += n as usize;
+ }
+ let i = u8::from(packet.tag()) as usize;
+ tags_count[i] += 1;
+
+ // If a new TPK starts, update TPK statistics.
+ if let Packet::PublicKey(_) = packet {
+ if tpk_count > 0 {
+ tpk.update_min_max(&mut tpk_min, &mut tpk_max);
+ }
+ tpk_count += 1;
+ tpk = PerTPK::min();
+ }
+
+ if let Packet::Unknown(_) = packet {
+ tags_unknown[i] += 1;
+ } else {
+ // Only record size statistics of packets we successfully
+ // parsed.
+ if let Some(n) = size {
+ tags_size_bytes[i] += n as usize;
+ tags_size_count[i] += 1;
+ if n < tags_size_min[i] {
+ tags_size_min[i] = n;
+ }
+ if n > tags_size_max[i] {
+ tags_size_max[i] = n;
+ }
+
+ tpk.bytes += n as usize;
+ }
+
+ tpk.packets += 1;
+ tpk.tags[i] += 1;
+ }
+ }
+ tpk.update_min_max(&mut tpk_min, &mut tpk_max);
+
+ // Print statistics.
+ println!("# Packet statistics\n\n\
+ {:>15} {:>9} {:>9} {:>9} {:>9} {:>9}",
+ "Packet tag", "count", "unknown",
+ "min size", "mean size", "max size");
+ println!("-------------------------------------------------------\
+ ----------");
+ for t in 0..64 {
+ let count = tags_count[t];
+ if count > 0 {
+ println!("{:>15} {:>9} {:>9} {:>9} {:>9} {:>9}",
+ format!("{:?}", Tag::from(t as u8)),
+ count,
+ tags_unknown[t],
+ tags_size_min[t],
+ tags_size_bytes[t] / tags_size_count[t],
+ tags_size_max[t]);
+ }
+ }
+ println!();
+
+ println!("# TPK statistics\n\n\
+ {:>15} {:>9} {:>9} {:>9}",
+ "", "min", "mean", "max");
+ println!("---------------------------------------------");
+ println!("{:>15} {:>9} {:>9} {:>9}",
+ "Size (packets)",
+ tpk_min.packets, packet_count / tpk_count, tpk_max.packets);
+ println!("{:>15} {:>9} {:>9} {:>9}",
+ "Size (bytes)",
+ tpk_min.bytes, packet_size / tpk_count, tpk_max.bytes);
+ for t in 0..64 {
+ let max = tpk_max.tags[t];
+ if t as u8 != Tag::PublicKey.into() && max > 0 {
+ println!("{:>15} {:>9} {:>9} {:>9}",
+ format!("{:?}", Tag::from(t as u8)),
+ tpk_min.tags[t],
+ tags_count[t] / tpk_count,
+ max);
+ }
+ }
+}
+
+struct PerTPK {
+ packets: usize,
+ bytes: usize,
+ tags: Vec<u32>,
+}
+
+impl PerTPK {
+ fn min() -> Self {
+ PerTPK {
+ packets: 0,
+ bytes: 0,
+ tags: vec![0; 64],
+ }
+ }
+
+ fn max() -> Self {
+ PerTPK {
+ packets: ::std::usize::MAX,
+ bytes: ::std::usize::MAX,
+ tags: vec![::std::u32::MAX; 64],
+ }
+ }
+
+ fn update_min_max(&self, min: &mut PerTPK, max: &mut PerTPK) {
+ if self.packets < min.packets {
+ min.packets = self.packets;
+ }
+ if self.packets > max.packets {
+ max.packets = self.packets;
+ }
+ if self.bytes < min.bytes {
+ min.bytes = self.bytes;
+ }
+ if self.bytes > max.bytes {
+ max.bytes = self.bytes;
+ }
+ for i in 0..64 {
+ if self.tags[i] < min.tags[i] {
+ min.tags[i] = self.tags[i];
+ }
+ if self.tags[i] > max.tags[i] {
+ max.tags[i] = self.tags[i];
+ }
+ }
+ }
+}