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