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 always 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 *      It might be nice to add a flag to indicate no decimal digits in
  22 *      that case.  This could be either an additional parameter, or a
  23 *      special value of display_unit.  Such a flag would also be nice for du.
  24 *
  25 *      Some code to omit the decimal point and tenths digit is sketched out
  26 *      and "#if 0"'d below.
  27 *
  28 * Licensed under GPLv2, see file LICENSE in this tarball for details.
  29 */
  30
  31#include "libbb.h"
  32
  33const char* FAST_FUNC make_human_readable_str(unsigned long long val,
  34        unsigned long block_size, unsigned long display_unit)
  35{
  36        static const char unit_chars[] ALIGN1 = {
  37                '\0', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'
  38        };
  39
  40        static char *str;
  41
  42        unsigned frac; /* 0..9 - the fractional digit */
  43        const char *u;
  44        const char *fmt;
  45
  46        if (val == 0)
  47                return "0";
  48
  49        fmt = "%llu";
  50        if (block_size > 1)
  51                val *= block_size;
  52        frac = 0;
  53        u = unit_chars;
  54
  55        if (display_unit) {
  56                val += display_unit/2;  /* Deal with rounding */
  57                val /= display_unit;    /* Don't combine with the line above! */
  58                /* will just print it as ulonglong (below) */
  59        } else {
  60                while ((val >= 1024)
  61                 /* && (u < unit_chars + sizeof(unit_chars) - 1) - always true */
  62                ) {
  63                        fmt = "%llu.%u%c";
  64                        u++;
  65                        frac = (((unsigned)val % 1024) * 10 + 1024/2) / 1024;
  66                        val /= 1024;
  67                }
  68                if (frac >= 10) { /* we need to round up here */
  69                        ++val;
  70                        frac = 0;
  71                }
  72#if 1
  73                /* If block_size is 0, dont print fractional part */
  74                if (block_size == 0) {
  75                        if (frac >= 5) {
  76                                ++val;
  77                        }
  78                        fmt = "%llu%*c";
  79                        frac = 1;
  80                }
  81#endif
  82        }
  83
  84        if (!str) {
  85                /* sufficient for any width of val */
  86                str = xmalloc(sizeof(val)*3 + 2 + 3);
  87        }
  88        sprintf(str, fmt, val, frac, *u);
  89        return str;
  90}
  91
  92
  93/* vda's implementations of the similar idea */
  94
  95/* Convert unsigned long long value into compact 5-char representation.
  96 * String is not terminated (buf[5] is untouched) */
  97void FAST_FUNC smart_ulltoa5(unsigned long long ul, char buf[6], const char *scale)
  98{
  99        const char *fmt;
 100        char c;
 101        unsigned v, u, idx = 0;
 102
 103        if (ul > 99999) { // do not scale if 99999 or less
 104                ul *= 10;
 105                do {
 106                        ul /= 1024;
 107                        idx++;
 108                } while (ul >= 100000);
 109        }
 110        v = ul; // ullong divisions are expensive, avoid them
 111
 112        fmt = " 123456789";
 113        u = v / 10;
 114        v = v % 10;
 115        if (!idx) {
 116                // 99999 or less: use "12345" format
 117                // u is value/10, v is last digit
 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                c = buf[2] = fmt[u/10%10];
 123                if (c != ' ') fmt = "0123456789";
 124                buf[3] = fmt[u%10];
 125                buf[4] = "0123456789"[v];
 126        } else {
 127                // value has been scaled into 0..9999.9 range
 128                // u is value, v is 1/10ths (allows for 92.1M format)
 129                if (u >= 100) {
 130                        // value is >= 100: use "1234M', " 123M" formats
 131                        c = buf[0] = " 123456789"[u/1000];
 132                        if (c != ' ') fmt = "0123456789";
 133                        c = buf[1] = fmt[u/100%10];
 134                        if (c != ' ') fmt = "0123456789";
 135                        v = u % 10;
 136                        u = u / 10;
 137                        buf[2] = fmt[u%10];
 138                } else {
 139                        // value is < 100: use "92.1M" format
 140                        c = buf[0] = " 123456789"[u/10];
 141                        if (c != ' ') fmt = "0123456789";
 142                        buf[1] = fmt[u%10];
 143                        buf[2] = '.';
 144                }
 145                buf[3] = "0123456789"[v];
 146                buf[4] = scale[idx]; /* typically scale = " kmgt..." */
 147        }
 148}
 149
 150/* Convert unsigned long long value into compact 4-char
 151 * representation. Examples: "1234", "1.2k", " 27M", "123T"
 152 * String is not terminated (buf[4] is untouched) */
 153void FAST_FUNC smart_ulltoa4(unsigned long long ul, char buf[5], const char *scale)
 154{
 155        const char *fmt;
 156        char c;
 157        unsigned v, u, idx = 0;
 158
 159        if (ul > 9999) { // do not scale if 9999 or less
 160                ul *= 10;
 161                do {
 162                        ul /= 1024;
 163                        idx++;
 164                } while (ul >= 10000);
 165        }
 166        v = ul; // ullong divisions are expensive, avoid them
 167
 168        fmt = " 123456789";
 169        u = v / 10;
 170        v = v % 10;
 171        if (!idx) {
 172                // 9999 or less: use "1234" format
 173                // u is value/10, v is last digit
 174                c = buf[0] = " 123456789"[u/100];
 175                if (c != ' ') fmt = "0123456789";
 176                c = buf[1] = fmt[u/10%10];
 177                if (c != ' ') fmt = "0123456789";
 178                buf[2] = fmt[u%10];
 179                buf[3] = "0123456789"[v];
 180        } else {
 181                // u is value, v is 1/10ths (allows for 9.2M format)
 182                if (u >= 10) {
 183                        // value is >= 10: use "123M', " 12M" formats
 184                        c = buf[0] = " 123456789"[u/100];
 185                        if (c != ' ') fmt = "0123456789";
 186                        v = u % 10;
 187                        u = u / 10;
 188                        buf[1] = fmt[u%10];
 189                } else {
 190                        // value is < 10: use "9.2M" format
 191                        buf[0] = "0123456789"[u];
 192                        buf[1] = '.';
 193                }
 194                buf[2] = "0123456789"[v];
 195                buf[3] = scale[idx]; /* typically scale = " kmgt..." */
 196        }
 197}
 198