*/
#include "nest/bird.h"
-#include "string.h"
+#include "lib/macro.h"
+#include "lib/string.h"
#include <errno.h>
va_end(args);
return out;
}
+
+static const u64 decadic_multiplier[] = {
+ 1, 10, 100, 1000,
+ 10000, 100000, 1000000, 10000000,
+#if 0
+ 100000000, 1000000000, 10000000000, 100000000000,
+ 1000000000000, 10000000000000, 100000000000000, 1000000000000000,
+ 10000000000000000, 100000000000000000, 1000000000000000000,
+#endif
+};
+
+static const u64 decmul_limit[] = {
+ ~0ULL / 1, ~0ULL / 10,
+ ~0ULL / 100, ~0ULL / 1000,
+ ~0ULL / 10000, ~0ULL / 100000,
+ ~0ULL / 1000000, ~0ULL / 10000000,
+#if 0
+ ~0ULL / 100000000, ~0ULL / 1000000000,
+ ~0ULL / 10000000000, ~0ULL / 100000000000,
+ ~0ULL / 1000000000000, ~0ULL / 10000000000000,
+ ~0ULL / 100000000000000, ~0ULL / 1000000000000000,
+ ~0ULL / 10000000000000000, ~0ULL / 100000000000000000,
+ ~0ULL / 1000000000000000000,
+#endif
+};
+
+STATIC_ASSERT(sizeof decadic_multiplier == sizeof decmul_limit);
+
+char *fmt_order(u64 value, uint decimals, u64 kb_threshold)
+{
+ bool too_big = (value + 512 < 512ULL);
+
+ u64 mv = value;
+ uint magnitude = 0;
+ while (mv > kb_threshold)
+ {
+ magnitude++;
+ mv = (mv + (too_big ? 0 : 512)) / 1024;
+ }
+
+ uint shift = magnitude * 10;
+
+ /* The trivial case */
+ if (magnitude == 0)
+ return tmp_sprintf("%lu ", value);
+
+ /* Now we can find the suffix and the main divisor */
+ ASSERT_DIE(magnitude < 7);
+ char suffix = " kMGTPE"[magnitude];
+
+ /* The value before the dot is available just by dividing */
+ u64 before_dot = value >> shift;
+
+ /* Remainder is more tricky. First we need to know it. */
+ u64 remainder = value - (before_dot << shift);
+
+ /* We would like to compute (remainder * decmul) / divisor
+ * in integers but it's tricky because of u64 limits. */
+ ASSERT_DIE(decimals < ARRAY_SIZE(decadic_multiplier));
+ u64 decmul = decadic_multiplier[decimals];
+ u64 product;
+
+ if (remainder < decmul_limit[decimals])
+ {
+ /* The easier version: Everything fits into u64 */
+ product = remainder * decmul;
+ product >>= shift - 1;
+ product++;
+ product >>= 1;
+ }
+ else
+ {
+ /* Harder version: We have to multiply by parts.
+ * Fortunately, decmul always fits into 32 bits. */
+ /* After this, product = lower + (upper << 32). */
+ u64 lower = (remainder & ((1ULL << 32) - 1)) * decmul;
+ u64 upper = (remainder >> 32) * decmul;
+
+ if (shift < 33)
+ {
+ /* Divide lower */
+ lower >>= shift - 1;
+
+ /* Add the full upper part, not shifted enough to lose any bits */
+ lower += upper << (33 - shift);
+ }
+ else
+ {
+ /* First move the shifted-out bits from upper to lower */
+ lower += (upper & ((1ULL << (shift - 32)) - 1)) << 32;
+
+ /* Then we can divide */
+ lower >>= shift - 1;
+
+ /* And add the shifted upper part */
+ lower += upper >> (shift - 33);
+ }
+
+ /* Now we finish the division by rounding */
+ product = (lower + 1) >> 1;
+ }
+
+ if (product == decmul)
+ {
+ product = 0;
+ before_dot++;
+ }
+
+ ASSERT_DIE(product < decmul);
+
+ /* And now we finally have all the numbers to print! */
+ if (decimals)
+ return tmp_sprintf("%lu.%0*lu %c",
+ before_dot, decimals, product, suffix
+ );
+ else
+ return tmp_sprintf("%lu %c", before_dot, suffix);
+}
return 1;
}
+static int
+t_fmt_order(void)
+{
+ struct fmt_order_tv {
+ u64 value;
+ int decimals;
+ u64 threshold;
+ const char *expect;
+ } test_vectors [] = {
+ { 9999, 1, 10000, "9999 " },
+ { 10001, 1, 10000, "9.8 k" },
+ { 10001, 2, 10000, "9.77 k" },
+ { 10001, 3, 10000, "9.767 k" },
+ { 1048575, 0, 10000, "1024 k" },
+ { 1048575, 1, 10000, "1024.0 k" },
+ { 1048575, 2, 10000, "1024.00 k" },
+ { 1048575, 3, 10000, "1023.999 k" },
+ { 1048575, 4, 10000, "1023.9990 k" },
+ { 1048575, 5, 10000, "1023.99902 k" },
+ { 1048575, 6, 10000, "1023.999023 k" },
+ { 1048575, 0, 1000, "1 M" },
+ { 1048575, 1, 1000, "1.0 M" },
+ { 1048575, 2, 1000, "1.00 M" },
+ { 1048575, 3, 1000, "1.000 M" },
+ { 1048575, 4, 1000, "1.0000 M" },
+ { 1048575, 5, 1000, "1.00000 M" },
+ { 1048575, 6, 1000, "0.999999 M" },
+ { 1048577, 6, 10000, "1024.000977 k" },
+ { 1048577, 6, 1000, "1.000001 M" },
+ { 1048577, 6, 100, "1.000001 M" },
+ { 1048577, 6, 10, "1.000001 M" },
+ { 1048577, 6, 1, "1.000001 M" },
+ { 10000000000000, 6, 10000, "9313.225746 G" },
+ { 10000000000000, 6, 1000, "9.094947 T" },
+ { 123456789123456789, 0, 1000, "110 P" },
+ { 123456789123456789, 4, 1000, "109.6517 P" },
+ { 123456789123456789, 7, 1000, "109.6516559 P" },
+ { 1234567890123456789, 0, 1000, "1 E" },
+ { 1234567890123456789, 1, 1000, "1.1 E" },
+ { 1234567890123456789, 2, 1000, "1.07 E" },
+ { 1234567890123456789, 3, 1000, "1.071 E" },
+ { 1234567890123456789, 4, 1000, "1.0708 E" },
+ { 1234567890123456789, 5, 1000, "1.07082 E" },
+ { 1234567890123456789, 6, 1000, "1.070817 E" },
+ { 1234567890123456789, 7, 1000, "1.0708170 E" },
+ { 9444732965739290427U, 3, 1000, "8.192 E" },
+ { 9444732965739290427U, 6, 1000, "8.192000 E" },
+ { 18446744073709551615U, 2, 1000, "16.00 E" },
+ };
+
+ for (int i = 0; i < (int)ARRAY_SIZE(test_vectors); i++)
+ {
+ const char *result = fmt_order(test_vectors[i].value, test_vectors[i].decimals, test_vectors[i].threshold);
+ const char *expect = test_vectors[i].expect;
+
+ bt_assert_msg(strncmp(result, expect, strlen(expect)) == 0,
+ "case %d, result \"%s\", expect \"%s\"", i, result, expect);
+ }
+
+ return 1;
+}
+
int
main(int argc, char *argv[])
{
bt_test_suite(t_router_id, "print router id");
bt_test_suite(t_time, "print time");
bt_test_suite(t_bstrcmp, "bstrcmp");
+ bt_test_suite(t_fmt_order, "fmt_order");
return bt_exit_value();
}