pub struct Funifier { /* private fields */ }Expand description
All-vCPU fun-mode key + petname dictionary handle. Cheap to
clone (everything inside is Copy or 'static); typically
constructed once per CLI invocation and reused for every
identifier in the dump.
Implementations§
Source§impl Funifier
impl Funifier
Sourcepub fn ephemeral() -> Self
pub fn ephemeral() -> Self
Construct a Funifier with a process-fresh random key. Two
invocations in the same process give DIFFERENT mappings —
callers who need cross-invocation determinism use
Self::with_seed instead. Used by callers that just want
“produce a fun version of this output” without any need to
reproduce the mapping later.
Derives the key from SHA-256 over the process pid and a nanosecond timestamp; no rand/getrandom dependency (see body comment). Two instances in one process differ only via the ns timestamp.
Sourcepub fn with_seed(seed: &str) -> Self
pub fn with_seed(seed: &str) -> Self
Construct a Funifier whose mapping is fully determined by
seed. Two invocations with the same seed (in the same
binary build) produce identical fun names for the same
inputs. Different seeds give independent mappings.
Uses SHA-256 over the fixed FUN_PEPPER || seed bytes,
truncated to 128 bits for SipHash — enough for a stable,
low-collision fun mapping.
Sourcepub fn petname_for(&self, category: &str, payload: &str) -> String
pub fn petname_for(&self, category: &str, payload: &str) -> String
Replace a string identifier with a deterministic
adjective-animal pair. The 65 536 (adjective, animal)
pairs the dictionary supports give a comfortable margin for
dumps with hundreds of distinct identifiers per category —
the birthday-paradox collision probability for 100 names
drawn from 65k buckets is ~7%, for 50 names ~2%. A future
extension could append a 4-digit suffix on collision; for
v1 we accept the rare collision.
Examples (with a fixed seed):
let f = Funifier::with_seed("demo");
// Each call yields an adjective-animal pair; the exact
// pair is seed-dependent.
f.petname_for("comm", "ktstr_test_main");
f.petname_for("comm", "scx_simple");Sourcepub fn numeric_id(&self, category: &str, n: u64) -> u64
pub fn numeric_id(&self, category: &str, n: u64) -> u64
Replace a u64 identifier with another u64. The mapping is a deterministic permutation per (seed, category): the keyed hash mixes (category, n.to_le_bytes()), and we take the low 64 bits as the new identifier.
The permutation just needs to be deterministic and to rarely collide; fun mode keeps the numbers stable and distinct, not meaningful.
Two distinct (category, n) inputs collide on the same
output u64 with probability ~2^-64. Within a single
category, n=0 always maps to 0 is NOT guaranteed; consumers
that need a sentinel zero should call Self::is_sentinel_u64
or carry the original value out-of-band.
Sourcepub fn numeric_id_i64(&self, category: &str, n: i64) -> i64
pub fn numeric_id_i64(&self, category: &str, n: i64) -> i64
Replace an i64 identifier (e.g. a kernel pid_t which is
signed). Same contract as Self::numeric_id but
preserves the i64 zero (since dumps frequently use 0 or
-1 as sentinels). Negative values are funified by their
absolute value; the sign survives.
Sourcepub fn numeric_id_i32(&self, category: &str, n: i32) -> i32
pub fn numeric_id_i32(&self, category: &str, n: i32) -> i32
Replace an i32 identifier (e.g. a kernel pid_t / signed
uid_t / any 32-bit-wide signed field) with another i32.
Same contract as Self::numeric_id_i64 but the output
is masked so it fits in 31 bits of magnitude (so a
downstream as i32 cast of the funified value can never
wrap a high-bit hash output back into the legal i32
range). Sentinels (0, i32::MIN, i32::MAX) round-
trip unchanged so failure-dump renderers see the same
“kthread / no value” markers in the funified output.
Sourcepub fn is_sentinel_i32(n: i32) -> bool
pub fn is_sentinel_i32(n: i32) -> bool
32-bit-wide analog of Self::is_sentinel_u64 for signed
32-bit identifiers. Schemas commonly use 0 for
“kernel/unset”, i32::MIN for “no value” / error sentinels,
and i32::MAX for “max” markers. Kept distinct from
Self::is_sentinel_u32 because the negative sentinel
(i32::MIN) has no u32 analog.
Sourcepub fn numeric_id_u32(&self, category: &str, n: u32) -> u32
pub fn numeric_id_u32(&self, category: &str, n: u32) -> u32
Replace a u32 identifier (e.g. a host CPU number, uid, gid,
nlink, or any other 32-bit-wide field) with another u32.
Same contract as Self::numeric_id but the output is
masked to fit in 32 bits so a downstream consumer that
as u32-casts the funified value cannot wrap a high-bit
hash output back into the legal 0..=u32::MAX range. Mirror
of Self::numeric_id_i64 for the unsigned narrow case.
Sentinel preservation differs from numeric_id: this
method preserves both 0 and u32::MAX exactly, since
32-bit identifier schemas frequently use those as
sentinels (CPU 0, “no value” 0xFFFFFFFF). Consumers that
want the universal u64 sentinel-check semantics call
Self::is_sentinel_u64 on the up-cast value, which is
equivalent because the u32 sentinels round-trip through
the u64 check.
Sourcepub fn is_sentinel_u64(n: u64) -> bool
pub fn is_sentinel_u64(n: u64) -> bool
True when the given identifier is “obvious sentinel” — 0 or “max” — and should be passed through unchanged. Lets downstream renderers preserve the failure-dump’s “kthread” vs “pid 0” semantics without leaking real pids.
Sourcepub fn is_sentinel_u32(n: u32) -> bool
pub fn is_sentinel_u32(n: u32) -> bool
32-bit-wide analog of Self::is_sentinel_u64 for the
narrow u32 paths in Self::numeric_id_u32. Schemas
frequently use u32::MAX as the “no value” marker for
32-bit fields and 0 as “kernel / unset”, same shape as
the u64 check — kept distinct so downstream callers using
numeric_id_u32 don’t have to up-cast just to check.
Sourcepub fn is_u32_category(key: &str) -> bool
pub fn is_u32_category(key: &str) -> bool
Categories whose JSON value is u32-width in the originating
schema and must be funified through
Self::numeric_id_u32 (32-bit-masked output) instead of
the default Self::numeric_id (full u64 output).
Why this matters: serde_json’s Value::Number only carries
is_u64/is_i64/is_f64, not the original Rust width.
When a struct field is typed u32 but serialized through a
generic serde_json::Value, the funify walker can’t see
the narrowing. A full-u64 funified output then overflows
when a downstream consumer (CLI parser, as u32 cast,
JSON round-trip into a u32-field struct) narrows it back.
Naming the u32-width identifier categories explicitly
is the only mechanism available without schema metadata.
The allowlist is conservative: only includes keys whose originating Rust field is documented or named-matchable as u32-wide. New u32 fields added to ktstr’s schemas must be declared here or they fall through to the u64 path and the overflow returns.
Sourcepub fn is_metric_passthrough(key: &str) -> bool
pub fn is_metric_passthrough(key: &str) -> bool
Allowlist gate for the funify walker: returns true when
the JSON-object key holds a value that is a METRIC (count,
rate, ratio, byte/duration unit, structural enum) and
should pass through funification unchanged. Returns false
for everything else — those values get funified.
Inverted polarity vs. v1: previously a deny-list of known identifier keys (pid/cpu/cgroup/…) selected the funify path. The deny-list missed every novel identifier-shaped field as the schema grew. The allowlist makes the safe default “funify it” — any new or unrecognised field is hidden by default, only metrics whose values are numeric/categorical truth (and therefore safe to retain) pass through.
Match strategy:
- lowercased-key whole-match against a fixed structural vocabulary (schema/version/type/kind/status/…);
- suffix-match against unit/quantity vocabulary (_count/_total/_per_sec/_ns/_bytes/_ratio/_pct/…);
- everything else returns false.
Returns true when key names a metric value.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Funifier
impl RefUnwindSafe for Funifier
impl Send for Funifier
impl Sync for Funifier
impl Unpin for Funifier
impl UnwindSafe for Funifier
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more