Functional Weave
Code in TypeScript

net.latency-summary@1.0.0

impl/typescript.ts

2,107 bytes · the TypeScript implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import { roundDiv } from "./math_round_div.ts";  ← from math.round-div ^1.0.0 · built alongside by fune
import { percentile } from "./stats_percentile.ts";  ← from stats.percentile ^2.0.0 · built alongside by fune
import { type LatencySummary } from "./net_latency_summary_types.ts";

/**
 * Summarise a run of probes the way ping does, in whole microseconds.
 *
 * Integer microseconds rather than float milliseconds, so every language
 * gives the same answer and a vector can compare exactly. A lost probe counts
 * towards loss and is skipped (not a break) when measuring jitter.
 */
export function summariseLatency(samplesUs: readonly (number | null)[]): LatencySummary {
  if (!Array.isArray(samplesUs)) throw new TypeError("samplesUs must be a list");
  if (samplesUs.length === 0) throw new RangeError("samplesUs must not be empty");
  const got: number[] = [];
  for (const s of samplesUs) {
    if (s === null || s === undefined) continue;
    if (typeof s !== "number" || !Number.isInteger(s)) {
      throw new TypeError(`each sample must be a whole number of microseconds or null, received ${s}`);
    }
    if (s < 0) throw new RangeError(`samples must not be negative, received ${s}`);
    got.push(s);
  }
  const sent = samplesUs.length;
  const received = got.length;
  const lost = sent - received;
  const lossBasisPoints = roundDiv(lost * 10000, sent, "half-up");
  if (received === 0) {
    return { sent, received, lost, lossBasisPoints, minUs: null, avgUs: null, maxUs: null, p95Us: null, jitterUs: null };
  }
  let min = got[0];
  let max = got[0];
  let sum = 0;
  for (const v of got) {
    if (v < min) min = v;
    if (v > max) max = v;
    sum += v;
  }
  let jitterUs: number | null = null;
  if (received >= 2) {
    let diffs = 0;
    for (let i = 1; i < received; i++) diffs += Math.abs(got[i] - got[i - 1]);
    jitterUs = roundDiv(diffs, received - 1, "half-up");
  }
  // Nearest-rank returns a sample that was really seen, so it is a whole number.
  const p95Us = percentile(got, 95, "nearest-rank", 0);
  return {
    sent,
    received,
    lost,
    lossBasisPoints,
    minUs: min,
    avgUs: roundDiv(sum, received, "half-up"),
    maxUs: max,
    p95Us,
    jitterUs,
  };
}