blob: 315908112c2f5a86809e59171e44d5e54293fe2d [file]
// Copyright 2026 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
use attribution_bindings as bindings;
use core::mem::MaybeUninit;
pub use bindings::vm_AttributionCounts as AttributionCounts;
/// Structure to store fractional counts of bytes in fixed point with 63 bits of precision. The max
/// fractional value is thus `2-Fraction::Epsilon()`. Counts are strictly unsigned.
///
/// This structure supports accumulation with other fractional counts and the code will handle the
/// internal bookkeeping around moving whole bytes from the fractional sum to the integral sum.
///
/// The structure also supports addition, subtraction, and division with whole integers. If
/// overflow occurs in either direction, an assert is fired. Multiplication is not supported.
///
/// We always check and guarantee that any fractional sums >=1 have the excess byte stripped off
/// and rolled over to the integral value. Thus, since accumulation always operates on fractions
/// `<=1-Fraction::Epsilon()`, it always generates results `<= 2-Fraction::Epsilon()` and there is
/// never overflow in the fractional fields.
pub use bindings::vm_FractionalBytes as FractionalBytes;
/// Returns an `AttributionCounts` initialized to zero.
pub fn zero() -> AttributionCounts {
let mut counts = MaybeUninit::uninit();
// SAFETY: `counts.as_mut_ptr()` is valid for writing `AttributionCounts`.
unsafe {
bindings::cpp_attribution_counts_zero(counts.as_mut_ptr());
}
// SAFETY: `cpp_attribution_counts_zero` certainly populated `counts`.
unsafe { counts.assume_init() }
}
/// Returns the sum of uncompressed and compressed bytes.
pub fn total_bytes(counts: &AttributionCounts) -> u64 {
(counts.uncompressed_bytes + counts.compressed_bytes) as u64
}
/// Returns the sum of private uncompressed and private compressed bytes.
pub fn total_private_bytes(counts: &AttributionCounts) -> u64 {
(counts.private_uncompressed_bytes + counts.private_compressed_bytes) as u64
}
/// Returns the sum of scaled uncompressed and scaled compressed bytes.
pub fn total_scaled_bytes(counts: &AttributionCounts) -> FractionalBytes {
fractional_bytes_add(counts.scaled_uncompressed_bytes, counts.scaled_compressed_bytes)
}
/// Creates a `FractionalBytes` representing the given number of whole bytes.
pub fn fractional_bytes_from_whole(whole_bytes: u64) -> FractionalBytes {
let mut out = MaybeUninit::uninit();
// SAFETY: `out.as_mut_ptr()` is valid for writing `FractionalBytes`.
unsafe {
bindings::cpp_fractional_bytes_from_whole(whole_bytes, out.as_mut_ptr());
}
// SAFETY: `cpp_fractional_bytes_from_whole` certainly populated `counts`.
unsafe { out.assume_init() }
}
/// Creates a `FractionalBytes` representing a fractional value from `numerator` and `denominator`.
pub fn fractional_bytes_from_fraction(numerator: u64, denominator: u64) -> FractionalBytes {
let mut out = MaybeUninit::uninit();
// SAFETY: `out.as_mut_ptr()` is valid for writing `FractionalBytes`.
unsafe {
bindings::cpp_fractional_bytes_from_fraction(numerator, denominator, out.as_mut_ptr());
}
// SAFETY: `cpp_fractional_bytes_from_fraction` certainly populated `out`.
unsafe { out.assume_init() }
}
/// Adds two `FractionalBytes` values.
pub fn fractional_bytes_add(a: FractionalBytes, b: FractionalBytes) -> FractionalBytes {
let mut out = MaybeUninit::uninit();
// SAFETY: `&a` and `&b` are valid `FractionalBytes`, and `out.as_mut_ptr()` is valid for
// writing.
unsafe {
bindings::cpp_fractional_bytes_add(&a, &b, out.as_mut_ptr());
}
// SAFETY: `cpp_fractional_bytes_add` certainly populated `out`.
unsafe { out.assume_init() }
}