Data Unit Converter — Zig source
Convert between digital data units - B, KB/KiB, MB/MiB, GB/GiB, TB/TiB. Switch between decimal (1000) and binary (1024) bases, runs entirely in your browser.
This is the Zig implementation — the same logic the interactive tool runs, in a shareable, citable form.
// =============================================================================
// data-unit-converter.zig — CosmoDev polyglot showcase port of the
// `data-unit-converter` tool
// -----------------------------------------------------------------------------
// Language : Zig (0.13, standard library only)
// Source: ported from src/lib/dataUnits.ts (the canonical, live TypeScript
// lib); mirrors src/tool-sources/data-unit-converter/{python.py,rust.rs}
// License : display source — part of CosmoDev's polyglot tool pages
// (dev.cosmolabs.org). Shown verbatim alongside the JS/TS/Go/Rust/
// Python ports and the other language ports.
// -----------------------------------------------------------------------------
// Pure digital-data unit conversion. Convert between B, KB/KiB, MB/MiB,
// GB/GiB, TB/TiB: the decimal base 1000 pairs with KB/MB/GB/TB (SI) and the
// binary base 1024 with KiB/MiB/GiB/TiB (IEC). Deterministic and
// side-effect free; convertData returns NaN for non-finite input, unknown
// units, or an invalid base.
// =============================================================================
const std = @import("std");
/// Supported conversion radix: decimal (`.decimal = 1000`, SI units) or binary
/// (`.binary = 1024`, IEC units).
pub const Base = enum(u32) {
decimal = 1000,
binary = 1024,
/// Numeric radix as an f64, ready for `std.math.pow`.
pub fn radix(self: Base) f64 {
return @floatFromInt(@intFromEnum(self));
}
};
/// Recognized digital-data units. `kilo/mega/giga/tera` are decimal (1000);
/// `kibi/mebi/gibi/tebi` are binary (1024); `byte` is shared by both.
pub const Unit = enum {
byte,
kilobyte,
megabyte,
gigabyte,
terabyte,
kibibyte,
mebibyte,
gibibyte,
tebibyte,
/// Symbol string used in the UI and persisted in shareable URLs.
pub fn symbol(self: Unit) []const u8 {
return switch (self) {
.byte => "B",
.kilobyte => "KB",
.megabyte => "MB",
.gigabyte => "GB",
.terabyte => "TB",
.kibibyte => "KiB",
.mebibyte => "MiB",
.gibibyte => "GiB",
.tebibyte => "TiB",
};
}
/// Power-of-base rank: `byte` is rank 0 and each prefix step adds 1. KB
/// and KiB are both rank 1 — the unit picks the rank, the base decides
/// whether a rank means ×1000 or ×1024.
pub fn exponent(self: Unit) i32 {
return switch (self) {
.byte => 0,
.kilobyte, .kibibyte => 1,
.megabyte, .mebibyte => 2,
.gigabyte, .gibibyte => 3,
.terabyte, .tebibyte => 4,
};
}
/// Parse a symbol back into a Unit, mirroring the TS dictionary lookup.
/// Returns null for unknown symbols (the TS side returns NaN downstream).
pub fn fromSymbol(s: []const u8) ?Unit {
if (std.mem.eql(u8, s, "B")) return .byte;
if (std.mem.eql(u8, s, "KB")) return .kilobyte;
if (std.mem.eql(u8, s, "MB")) return .megabyte;
if (std.mem.eql(u8, s, "GB")) return .gigabyte;
if (std.mem.eql(u8, s, "TB")) return .terabyte;
if (std.mem.eql(u8, s, "KiB")) return .kibibyte;
if (std.mem.eql(u8, s, "MiB")) return .mebibyte;
if (std.mem.eql(u8, s, "GiB")) return .gibibyte;
if (std.mem.eql(u8, s, "TiB")) return .tebibyte;
return null;
}
};
/// Units available under each base, ordered smallest (byte) to largest. The UI
/// uses these to populate the per-base unit pickers.
pub fn unitsFor(base: Base) *const [5]Unit {
return switch (base) {
.decimal => &.{ .byte, .kilobyte, .megabyte, .gigabyte, .terabyte },
.binary => &.{ .byte, .kibibyte, .mebibyte, .gibibyte, .tebibyte },
};
}
/// Convert a value between two digital-data units under the given base.
///
/// Strategy: reduce to bytes via `value × base^fromExp`, then divide by
/// `base^toExp`. Non-finite values (Inf/NaN) return NaN, mirroring the
/// TypeScript contract. `Base` and `Unit` are already constrained by their
/// enum types; the symbol-based entry point below preserves the original
/// "unknown unit → NaN" behavior for string inputs.
pub fn convertData(value: f64, from: Unit, to: Unit, base: Base) f64 {
// Reject Inf/NaN early so they don't propagate through the multiplication.
if (!std.math.isFinite(value)) return std.math.nan(f64);
const radix = base.radix();
return (value * std.math.pow(f64, radix, @floatFromInt(from.exponent()))) /
std.math.pow(f64, radix, @floatFromInt(to.exponent()));
}
/// String-in / NaN-out entry point that mirrors the TypeScript signature
/// (`convertData(value, fromUnit, toUnit, base)`) for unknown-symbol handling.
/// Returns NaN for non-finite input, unrecognized symbols, or a non-canonical
/// base value.
pub fn convertDataSymbols(value: f64, from: []const u8, to: []const u8, base: i64) f64 {
if (!std.math.isFinite(value)) return std.math.nan(f64);
// Only the two canonical bases are valid; anything else is a caller bug.
const radix: Base = switch (base) {
1000 => .decimal,
1024 => .binary,
else => return std.math.nan(f64),
};
// Unknown unit symbols short-circuit to NaN, like the TS dictionary miss.
const from_unit = Unit.fromSymbol(from) orelse return std.math.nan(f64);
const to_unit = Unit.fromSymbol(to) orelse return std.math.nan(f64);
return convertData(value, from_unit, to_unit, radix);
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →