busybox/libbb/human_readable.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * June 30, 2001                 Manuel Novoa III
   4 *
   5 * All-integer version (hey, not everyone has floating point) of
   6 * make_human_readable_str, modified from similar code I had written
   7 * for busybox several months ago.
   8 *
   9 * Notes:
  10 *   1) I'm using an unsigned long long to hold the product size * block_size,
  11 *      as df (which calls this routine) could request a representation of a
  12 *      partition size in bytes > max of unsigned long.  If long longs aren't
  13 *      available, it would be possible to do what's needed using polynomial
  14 *      representations (say, powers of 1024) and manipulating coefficients.
  15 *      The base ten "bytes" output could be handled similarly.
  16 *
  17 *   2) This routine outputs a decimal point and a tenths digit when
  18 *      display_unit == 0.  Hence, it isn't uncommon for the returned string
  19 *      to have a length of 5 or 6.
  20 *
  21 *      If block_size is also 0, no decimal digits are printed.
  22 *
  23 * Licensed under GPLv2, see file LICENSE in this source tree.
  24 */
  25#include "libbb.h"
  26
  27const char* FAST_FUNC make_human_readable_str(unsigned long long val,
  28        unsigned long block_size, unsigned long display_unit)
  29{
  30        static const char unit_chars[] ALIGN1 = {
  31                '\0', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'
  32        };
  33
  34        unsigned frac; /* 0..9 - the fractional digit */
  35        const char *u;
  36        const char *fmt;
  37
  38        if (val == 0)
  39                return "0";
  40
  41        fmt = "%llu";
  42        if (block_size > 1)
  43                val *= block_size;
  44        frac = 0;
  45        u = unit_chars;
  46
  47        if (display_unit) {
  48                val += display_unit/2;  /* Deal with rounding */
  49                val /= display_unit;    /* Don't combine with the line above! */
  50                /* will just print it as ulonglong (below) */
  51        } else {
  52                while ((val >= 1024)
  53                 /* && (u < unit_chars + sizeof(unit_chars) - 1) - always true */
  54                ) {
  55                        fmt = "%llu.%u%c";
  56                        u++;
  57                        frac = (((unsigned)val % 1024) * 10 + 1024/2) / 1024;
  58                        val /= 1024;
  59                }
  60                if (frac >= 10) { /* we need to round up here */
  61                        ++val;
  62                        frac = 0;
  63                }
  64#if 1
  65                /* If block_size is 0, dont print fractional part */
  66                if (block_size == 0) {
  67                        if (frac >= 5) {
  68                                ++val;
  69                        }
  70                        fmt = "%llu%*c";
  71                        frac = 1;
  72                }
  73#endif
  74        }
  75
  76        return auto_string(xasprintf(fmt, val, frac, *u));
  77}
  78
  79
  80/* vda's implementations of the similar idea */
  81
  82/* Convert unsigned long long value into compact 5-char representation.
  83 * String is not terminated (buf[5] is untouched) */
  84char* FAST_FUNC smart_ulltoa5(unsigned long long ul, char buf[5], const char *scale)
  85{
  86        const char *fmt;
  87        char c;
  88        unsigned v, u, idx = 0;
  89
  90        if (ul > 99999) { // do not scale if 99999 or less
  91                ul *= 10;
  92                do {
  93                        ul /= 1024;
  94                        idx++;
  95                } while (ul >= 100000);
  96        }
  97        v = ul; // ullong divisions are expensive, avoid them
  98
  99        fmt = " 123456789";
 100        u = v / 10;
 101        v = v % 10;
 102        if (!idx) {
 103                // 99999 or less: use "12345" format
 104                // u is value/10, v is last digit
 105                c = buf[0] = " 123456789"[u/1000];
 106                if (c != ' ') fmt = "0123456789";
 107                c = buf[1] = fmt[u/100%10];
 108                if (c != ' ') fmt = "0123456789";
 109                c = buf[2] = fmt[u/10%10];
 110                if (c != ' ') fmt = "0123456789";
 111                buf[3] = fmt[u%10];
 112                buf[4] = "0123456789"[v];
 113        } else {
 114                // value has been scaled into 0..9999.9 range
 115                // u is value, v is 1/10ths (allows for 92.1M format)
 116                if (u >= 100) {
 117                        // value is >= 100: use "1234M', " 123M" formats
 118                        c = buf[0] = " 123456789"[u/1000];
 119                        if (c != ' ') fmt = "0123456789";
 120                        c = buf[1] = fmt[u/100%10];
 121                        if (c != ' ') fmt = "0123456789";
 122                        v = u % 10;
 123                        u = u / 10;
 124                        buf[2] = fmt[u%10];
 125                } else {
 126                        // value is < 100: use "92.1M" format
 127                        c = buf[0] = " 123456789"[u/10];
 128                        if (c != ' ') fmt = "0123456789";
 129                        buf[1] = fmt[u%10];
 130                        buf[2] = '.';
 131                }
 132                buf[3] = "0123456789"[v];
 133                buf[4] = scale[idx]; /* typically scale = " kmgt..." */
 134        }
 135        return buf + 5;
 136}
 137
 138/* Convert unsigned long long value into compact 4-char
 139 * representation. Examples: "1234", "1.2k", " 27M", "123T"
 140 * String is not terminated (buf[4] is untouched) */
 141char* FAST_FUNC smart_ulltoa4(unsigned long long ul, char buf[4], const char *scale)
 142{
 143        const char *fmt;
 144        char c;
 145        unsigned v, u, idx = 0;
 146
 147        if (ul > 9999) { // do not scale if 9999 or less
 148                ul *= 10;
 149                do {
 150                        ul /= 1024;
 151                        idx++;
 152                } while (ul >= 10000);
 153        }
 154        v = ul; // ullong divisions are expensive, avoid them
 155
 156        fmt = " 123456789";
 157        u = v / 10;
 158        v = v % 10;
 159        if (!idx) {
 160                // 9999 or less: use "1234" format
 161                // u is value/10, v is last digit
 162                c = buf[0] = " 123456789"[u/100];
 163                if (c != ' ') fmt = "0123456789";
 164                c = buf[1] = fmt[u/10%10];
 165                if (c != ' ') fmt = "0123456789";
 166                buf[2] = fmt[u%10];
 167                buf[3] = "0123456789"[v];
 168        } else {
 169                // u is value, v is 1/10ths (allows for 9.2M format)
 170                if (u >= 10) {
 171                        // value is >= 10: use "123M', " 12M" formats
 172                        c = buf[0] = " 123456789"[u/100];
 173                        if (c != ' ') fmt = "0123456789";
 174                        v = u % 10;
 175                        u = u / 10;
 176                        buf[1] = fmt[u%10];
 177                } else {
 178                        // value is < 10: use "9.2M" format
 179                        buf[0] = "0123456789"[u];
 180                        buf[1] = '.';
 181                }
 182                buf[2] = "0123456789"[v];
 183                buf[3] = scale[idx]; /* typically scale = " kmgt..." */
 184        }
 185        return buf + 4;
 186}
 187