aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--spec/util_human_units_spec.lua15
-rw-r--r--util/human/units.lua58
2 files changed, 73 insertions, 0 deletions
diff --git a/spec/util_human_units_spec.lua b/spec/util_human_units_spec.lua
new file mode 100644
index 00000000..4326cdd4
--- /dev/null
+++ b/spec/util_human_units_spec.lua
@@ -0,0 +1,15 @@
+local units = require "util.human.units";
+
+describe("util.human.units", function ()
+ describe("format", function ()
+ it("formats numbers with SI units", function ()
+ assert.equal("1 km", units.format(1000, "m"));
+ assert.equal("1 GJ", units.format(1000000000, "J"));
+ assert.equal("1 ms", units.format(1/1000, "s"));
+ assert.equal("10 ms", units.format(10/1000, "s"));
+ assert.equal("1 ns", units.format(1/1000000000, "s"));
+ assert.equal("1 KiB", units.format(1024, "B", 'b'));
+ assert.equal("1 MiB", units.format(1024*1024, "B", 'b'));
+ end);
+ end);
+end);
diff --git a/util/human/units.lua b/util/human/units.lua
new file mode 100644
index 00000000..2c4662cd
--- /dev/null
+++ b/util/human/units.lua
@@ -0,0 +1,58 @@
+local large = {
+ "k", 1000,
+ "M", 1000000,
+ "G", 1000000000,
+ "T", 1000000000000,
+ "P", 1000000000000000,
+ "E", 1000000000000000000,
+ "Z", 1000000000000000000000,
+ "Y", 1000000000000000000000000,
+}
+local small = {
+ "m", 0.001,
+ "μ", 0.000001,
+ "n", 0.000000001,
+ "p", 0.000000000001,
+ "f", 0.000000000000001,
+ "a", 0.000000000000000001,
+ "z", 0.000000000000000000001,
+ "y", 0.000000000000000000000001,
+}
+
+local binary = {
+ "Ki", 2^10,
+ "Mi", 2^20,
+ "Gi", 2^30,
+ "Ti", 2^40,
+ "Pi", 2^50,
+ "Ei", 2^60,
+ "Zi", 2^70,
+ "Yi", 2^80,
+}
+
+-- n: number, the number to format
+-- unit: string, the base unit
+-- b: optional enum 'b', thousands base
+local function format(n, unit, b) --> string
+ local round = math.floor;
+ local prefixes = large;
+ local logbase = 1000;
+ local fmt = "%.3g %s%s";
+ if n == 0 then
+ return fmt:format(n, "", unit);
+ end
+ if b == 'b' then
+ prefixes = binary;
+ logbase = 1024;
+ elseif n < 1 then
+ prefixes = small;
+ round = math.ceil;
+ end
+ local m = math.max(0, math.min(8, round(math.abs(math.log(math.abs(n), logbase)))));
+ local prefix, multiplier = table.unpack(prefixes, m * 2-1, m*2);
+ return fmt:format(n / (multiplier or 1), prefix or "", unit);
+end
+
+return {
+ format = format;
+};