#[derive(Default, Display, Add, AddAssign, Into, From, Clone, Copy)]
pub struct Bytes(usize);
impl std::iter::Sum for Bytes {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = Bytes>,
{
iter.fold(Default::default(), std::ops::Add::add)
}
}
#[derive(
Default,
Display,
Serialize,
Deserialize,
From,
Into,
Add,
AddAssign,
Clone,
Copy,
PartialOrd,
Ord,
PartialEq,
Eq,
)]
pub struct Instances(usize);
impl Instances {
pub fn is_zero(&self) -> bool {
self.0 == 0
}
}
impl std::iter::Sum for Instances {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = Instances>,
{
iter.fold(Default::default(), std::ops::Add::add)
}
}
pub struct BytesAndInstances {
bytes: Bytes,
instances: Instances,
}
impl BytesAndInstances {
pub fn new(bytes: Bytes, instances: Instances) -> Self {
BytesAndInstances { bytes, instances }
}
}
#[macro_export]
macro_rules! for_field_in_user_extensible {
( $cb: ident ) => {
$cb!(
(floats, "floats", b"floats"),
(unsigned_longs, "unsigned_longs", b"unsigned_longs"),
(property_keys, "property_keys", b"property_keys"),
(identifier_names, "identifier_names", b"identifier_names"),
(string_literals, "string_literals", b"string_literals"),
(list_lengths, "list_lengths", b"list_lengths")
)
};
}
#[derive(Debug, Default, Add, Clone, AddAssign)]
pub struct PerUserExtensibleKind<T> {
pub floats: T,
pub unsigned_longs: T,
pub property_keys: T,
pub identifier_names: T,
pub string_literals: T,
pub list_lengths: T,
}
impl<T> PerUserExtensibleKind<T> {
pub fn with<F>(f: F) -> Self
where
F: Fn(&str) -> T,
{
macro_rules! with_field { ($(($ident: ident, $name: expr, $bname: expr )),*) => {
PerUserExtensibleKind {
$(
$ident: f($name),
)*
}
} };
for_field_in_user_extensible!(with_field)
}
pub fn into_with<F, U>(self, f: F) -> PerUserExtensibleKind<U>
where
F: Fn(&str, T) -> U,
{
macro_rules! with_field { ($(($ident: ident, $name: expr, $bname: expr )),*) => {
PerUserExtensibleKind {
$(
$ident: f($name, self.$ident),
)*
}
} };
for_field_in_user_extensible!(with_field)
}
pub fn iter(&self) -> impl Iterator<Item = (&'static str, &T)> {
macro_rules! with_field { ($(($ident: ident, $name: expr, $bname: expr )),*) => {
vec![
$(
($name, &self.$ident),
)*
]
} };
for_field_in_user_extensible!(with_field).into_iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&'static str, &mut T)> {
macro_rules! with_field { ($(($ident: ident, $name: expr, $bname: expr )),*) => {
vec![
$(
($name, &mut self.$ident),
)*
]
} };
for_field_in_user_extensible!(with_field).into_iter()
}
pub fn into_iter(self) -> impl Iterator<Item = (&'static str, T)> {
macro_rules! with_field { ($(($ident: ident, $name: expr, $bname: expr )),*) => {
vec![
$(
($name, self.$ident),
)*
]
} };
for_field_in_user_extensible!(with_field).into_iter()
}
pub fn get(&self, field_name: &str) -> Option<&T> {
self.get_b(field_name.as_bytes())
}
pub fn get_mut(&mut self, field_name: &str) -> Option<&mut T> {
self.get_mut_b(field_name.as_bytes())
}
pub fn get_mut_b(&mut self, field_name: &[u8]) -> Option<&mut T> {
macro_rules! with_field { ($(($ident: ident, $name: expr, $bname: expr )),*) => {
match field_name {
$(
$bname => Some(&mut self.$ident),
)*
_ => None,
}
} };
for_field_in_user_extensible!(with_field)
}
pub fn get_b(&self, field_name: &[u8]) -> Option<&T> {
macro_rules! with_field { ($(($ident: ident, $name: expr, $bname: expr )),*) => {
match field_name {
$(
$bname => Some(&self.$ident),
)*
_ => None,
}
} };
for_field_in_user_extensible!(with_field)
}
}
#[macro_export]
macro_rules! for_field_in_per_static {
( $cb: ident ) => {
$cb!(
(bools, "bools", "bools", b"bools"),
(interface_names, "interfaces", "interfaces", b"interfaces"),
(
string_enums,
"string_enums",
"string enums",
b"string_enums"
)
)
};
}
#[derive(Debug, Default)]
pub struct PerStaticKind<T> {
pub bools: T,
pub interface_names: T,
pub string_enums: T,
}
const DEFAULT_HISTOGRAM_BUCKETS: usize = 32;
pub struct Rational<T> {
pub num: T,
pub den: T,
}
pub struct ProbabilityHistogram {
buckets: Vec<usize>,
}
impl ProbabilityHistogram {
pub fn with_capacity(len: usize) -> Self {
assert!(len > 1);
let mut buckets = Vec::with_capacity(len);
buckets.resize(len, 0);
Self { buckets }
}
pub fn new() -> Self {
Self::with_capacity(DEFAULT_HISTOGRAM_BUCKETS)
}
pub fn add_probability(&mut self, probability: Rational<usize>) {
assert!(probability.num <= probability.den);
let len = self.buckets.len();
let index = (probability.num * (len - 1)) / probability.den;
self.buckets[index] += 1;
}
}
impl Default for ProbabilityHistogram {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for ProbabilityHistogram {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let total: usize = self.buckets.iter().sum();
write!(formatter, " bucket ")?;
for i in 0..32 {
write!(formatter, " | {:3}", i)?;
}
write!(formatter, "\n --------")?;
for _ in 0..32 {
write!(formatter, "-|----")?;
}
write!(formatter, "\n % ")?;
for bucket in &self.buckets {
write!(formatter, " | {:3.0}", 100. * *bucket as f64 / total as f64)?;
}
write!(formatter, "\n")?;
Ok(())
}
}
impl std::fmt::Display for PerStaticKind<ProbabilityHistogram> {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(formatter, "Static:\n")?;
macro_rules! with_field { ($(($ident: ident, $name: expr, $user_readable: expr, $bname: expr )),*) => {
$(
write!(formatter, " {name}:\n", name = $user_readable)?;
write!(formatter, "{content}", content = self.$ident)?;
)*
} };
for_field_in_per_static!(with_field);
Ok(())
}
}
pub trait DisplayWith<T> {
fn fmt(&self, formatter: &mut std::fmt::Formatter, data: &T) -> Result<(), std::fmt::Error>;
}
impl DisplayWith< BytesAndInstances> for BytesAndInstances {
fn fmt(
&self,
formatter: &mut std::fmt::Formatter,
total: &BytesAndInstances,
) -> Result<(), std::fmt::Error> {
let bytes = Into::<usize>::into(self.bytes);
let symbols = Into::<usize>::into(self.instances);
let total_bytes = Into::<usize>::into(total.bytes);
let total_symbols = Into::<usize>::into(total.instances);
write!(formatter, "symbols {symbols} = {symbols_percent:.2}, bytes {bytes} = {bytes_percent:.2} ({bits_per_symbol:.2} bits/symbol)",
symbols = symbols,
bytes = bytes,
symbols_percent = 100.* symbols as f64 / total_symbols as f64,
bytes_percent = 100.* bytes as f64 / total_bytes as f64,
bits_per_symbol = 8. * bytes as f64 / symbols as f64,
)
}
}
impl std::fmt::Display for PerUserExtensibleKind<BytesAndInstances> {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let total = BytesAndInstances {
bytes: self.iter().map(|(_, data)| data.bytes.clone()).sum(),
instances: self.iter().map(|(_, data)| data.instances.clone()).sum(),
};
write!(formatter, " User-extensible:\n")?;
for (field, name) in &[
(&self.identifier_names, "identifier names"),
(&self.string_literals, "string literals"),
(&self.floats, "floats"),
(&self.unsigned_longs, "unsigned longs"),
(&self.list_lengths, "list lengths"),
] {
write!(formatter, " {name}: ", name = name)?;
field.fmt(formatter, &total)?;
write!(formatter, "\n")?;
}
write!(
formatter,
"Total: {} bytes, {} symbols",
total.bytes, total.instances
)?;
Ok(())
}
}