#[cfg(feature = "v2")]
mod imp {
use compactly::v2::{decode, encode, Encode, EntropyCoder, EntropyDecoder};
use std::net::{Ipv4Addr, Ipv6Addr};
use std::time::Instant;
fn report<T: Encode + PartialEq>(label: &str, values: &Vec<T>, raw_bytes: usize) {
let t0 = Instant::now();
let encoded = encode(values);
let encode_ms = t0.elapsed().as_secs_f64() * 1000.0;
let t1 = Instant::now();
let decoded = decode::<Vec<T>>(&encoded).expect("decode failed");
let decode_ms = t1.elapsed().as_secs_f64() * 1000.0;
assert!(decoded == *values, "roundtrip mismatch");
println!(
"{label}: raw={raw_bytes}B encoded={}B ratio={:.2}x encode={encode_ms:.1}ms decode={decode_ms:.1}ms",
encoded.len(),
raw_bytes as f64 / encoded.len() as f64,
);
}
#[derive(Default, Clone)]
struct OctetCtx {
is_zero: <bool as Encode>::Context,
nonzero: <u8 as Encode>::Context,
}
impl OctetCtx {
fn enc<E: EntropyCoder>(&mut self, byte: u8, w: &mut E) {
let z = byte == 0;
z.encode(w, &mut self.is_zero);
if !z {
byte.encode(w, &mut self.nonzero);
}
}
fn dec<D: EntropyDecoder>(&mut self, r: &mut D) -> Result<u8, std::io::Error> {
if bool::decode(r, &mut self.is_zero)? {
Ok(0)
} else {
u8::decode(r, &mut self.nonzero)
}
}
}
fn encode_zsi<E: EntropyCoder>(byte: u8, w: &mut E, zctx: &mut <bool as Encode>::Context) {
let z = byte == 0;
z.encode(w, zctx);
if !z {
w.encode_incompressible_bytes(&[byte]);
}
}
fn decode_zsi<D: EntropyDecoder>(
r: &mut D,
zctx: &mut <bool as Encode>::Context,
) -> Result<u8, std::io::Error> {
if bool::decode(r, zctx)? {
Ok(0)
} else {
let mut b = [0u8; 1];
r.decode_incompressible_bytes(&mut b)?;
Ok(b[0])
}
}
#[derive(Default, Clone)]
struct Ipv6Primary2Ctx {
oct_0: <u8 as Encode>::Context,
oct_1_6: [OctetCtx; 6],
oct_7_10_zsi: [<bool as Encode>::Context; 4],
oct_11_12: [OctetCtx; 2],
oct_13_14_zsi: [<bool as Encode>::Context; 2],
}
struct Ipv6Primary2([u8; 16]);
impl PartialEq for Ipv6Primary2 {
fn eq(&self, o: &Self) -> bool {
self.0 == o.0
}
}
impl Encode for Ipv6Primary2 {
type Context = Ipv6Primary2Ctx;
fn encode<E: EntropyCoder>(&self, w: &mut E, c: &mut Self::Context) {
let o = &self.0;
o[0].encode(w, &mut c.oct_0);
for (i, ctx) in c.oct_1_6.iter_mut().enumerate() {
ctx.enc(o[1 + i], w);
}
for (i, ctx) in c.oct_7_10_zsi.iter_mut().enumerate() {
encode_zsi(o[7 + i], w, ctx);
}
for (i, ctx) in c.oct_11_12.iter_mut().enumerate() {
ctx.enc(o[11 + i], w);
}
for (i, ctx) in c.oct_13_14_zsi.iter_mut().enumerate() {
encode_zsi(o[13 + i], w, ctx);
}
w.encode_incompressible_bytes(&[o[15]]);
}
fn decode<D: EntropyDecoder>(
r: &mut D,
c: &mut Self::Context,
) -> Result<Self, std::io::Error> {
let mut o = [0u8; 16];
o[0] = u8::decode(r, &mut c.oct_0)?;
for (i, ctx) in c.oct_1_6.iter_mut().enumerate() {
o[1 + i] = ctx.dec(r)?;
}
for (i, ctx) in c.oct_7_10_zsi.iter_mut().enumerate() {
o[7 + i] = decode_zsi(r, ctx)?;
}
for (i, ctx) in c.oct_11_12.iter_mut().enumerate() {
o[11 + i] = ctx.dec(r)?;
}
for (i, ctx) in c.oct_13_14_zsi.iter_mut().enumerate() {
o[13 + i] = decode_zsi(r, ctx)?;
}
r.decode_incompressible_bytes(&mut o[15..16])?;
Ok(Ipv6Primary2(o))
}
}
#[derive(Default, Clone)]
struct Ipv6BatchCtx {
zero: [<bool as Encode>::Context; 14], nz: [<u8 as Encode>::Context; 9], }
struct Ipv6Batched([u8; 16]);
impl PartialEq for Ipv6Batched {
fn eq(&self, o: &Self) -> bool {
self.0 == o.0
}
}
impl Encode for Ipv6Batched {
type Context = Ipv6BatchCtx;
fn encode<E: EntropyCoder>(&self, w: &mut E, c: &mut Self::Context) {
let o = self.0;
let z: [bool; 14] = std::array::from_fn(|i| o[i + 1] == 0);
for (zf, ctx) in z.iter().zip(c.zero.iter_mut()) {
zf.encode(w, ctx);
}
o[0].encode(w, &mut c.nz[0]);
for i in 0..6 {
if !z[i] {
o[1 + i].encode(w, &mut c.nz[1 + i]);
}
}
for i in 0..2 {
if !z[10 + i] {
o[11 + i].encode(w, &mut c.nz[7 + i]);
}
}
let mut buf = [0u8; 7];
let mut n = 0;
for i in 0..4 {
if !z[6 + i] {
buf[n] = o[7 + i];
n += 1;
}
}
for i in 0..2 {
if !z[12 + i] {
buf[n] = o[13 + i];
n += 1;
}
}
buf[n] = o[15];
n += 1;
w.encode_incompressible_bytes(&buf[..n]);
}
fn decode<D: EntropyDecoder>(
r: &mut D,
c: &mut Self::Context,
) -> Result<Self, std::io::Error> {
let mut z = [false; 14];
for (zf, ctx) in z.iter_mut().zip(c.zero.iter_mut()) {
*zf = bool::decode(r, ctx)?;
}
let mut o = [0u8; 16];
o[0] = u8::decode(r, &mut c.nz[0])?;
for i in 0..6 {
if !z[i] {
o[1 + i] = u8::decode(r, &mut c.nz[1 + i])?;
}
}
for i in 0..2 {
if !z[10 + i] {
o[11 + i] = u8::decode(r, &mut c.nz[7 + i])?;
}
}
let n = z[6..10].iter().filter(|&&z| !z).count()
+ z[12..14].iter().filter(|&&z| !z).count()
+ 1;
let mut buf = [0u8; 7];
r.decode_incompressible_bytes(&mut buf[..n])?;
let mut idx = 0;
for i in 0..4 {
if !z[6 + i] {
o[7 + i] = buf[idx];
idx += 1;
}
}
for i in 0..2 {
if !z[12 + i] {
o[13 + i] = buf[idx];
idx += 1;
}
}
o[15] = buf[idx];
Ok(Ipv6Batched(o))
}
}
pub(crate) fn run() {
let ipv4s: Vec<Ipv4Addr> = std::fs::read_to_string("ipv4.txt")
.expect("ipv4.txt")
.lines()
.filter(|l| !l.is_empty())
.map(|l| l.trim().parse().expect("valid IPv4"))
.collect();
let ipv6s: Vec<Ipv6Addr> = std::fs::read_to_string("ipv6.txt")
.expect("ipv6.txt")
.lines()
.filter(|l| !l.is_empty())
.map(|l| l.trim().parse().expect("valid IPv6"))
.collect();
let raw4 = ipv4s.len() * 4;
let raw6 = ipv6s.len() * 16;
println!("── IPv4 ({} addresses) ──", ipv4s.len());
report(" library Ipv4Addr ", &ipv4s, raw4);
println!("── IPv6 ({} addresses) ──", ipv6s.len());
let v6_p2: Vec<Ipv6Primary2> = ipv6s.iter().map(|a| Ipv6Primary2(a.octets())).collect();
report(" Primary2 (interleaved) ", &v6_p2, raw6);
let v6_b: Vec<Ipv6Batched> = ipv6s.iter().map(|a| Ipv6Batched(a.octets())).collect();
report(" Batched (flags+incomp) ", &v6_b, raw6);
}
}
#[cfg(feature = "v2")]
fn main() {
imp::run()
}
#[cfg(not(feature = "v2"))]
fn main() {
eprintln!("bench-net requires the v2 feature");
}