diff options
author | Chris Hall <chris.hall@highwayman.com> | 2011-09-02 00:53:59 +0100 |
---|---|---|
committer | Chris Hall <chris.hall@highwayman.com> | 2011-09-02 00:53:59 +0100 |
commit | 3690074a486cfada568975e287d9cbb9e687501f (patch) | |
tree | f6394c1ab1ca8381e9a77acca84f03dc701d10b2 /lib/qfstring.c | |
parent | 3f515315d5b17e432453eef67d7ac9e27bc39461 (diff) | |
download | quagga-3690074a486cfada568975e287d9cbb9e687501f.tar.bz2 quagga-3690074a486cfada568975e287d9cbb9e687501f.tar.xz |
Merging of euro_ix branch into pipework.
Bring in fixes for bgp dumping with pthreaded BGP Engine.
Bring in new "show nexus xxx" command.
Fix removal of '~' prompt.
Diffstat (limited to 'lib/qfstring.c')
-rw-r--r-- | lib/qfstring.c | 458 |
1 files changed, 457 insertions, 1 deletions
diff --git a/lib/qfstring.c b/lib/qfstring.c index eda2aa9a..a4ddb803 100644 --- a/lib/qfstring.c +++ b/lib/qfstring.c @@ -222,7 +222,7 @@ qfs_append_n(qf_str qfs, const char* src, uint n) } ; /*------------------------------------------------------------------------------ - * Append upto 'n' copies of the given character to the qf_str. + * Append upto 'n' copies of the given character to the qf_str * * May append nothing at all ! * @@ -1374,3 +1374,459 @@ qfs_arg_float(qf_str qfs, va_list* p_va, const char* start, size_t flen, return (want >= 0) ? pfp_done : pfp_failed ; } ; + +/*============================================================================== + * Construction of scaled numbers. + * + * + * + * + */ + +enum { scale_max = 6 } ; + +static const char* scale_d_tags [] = +{ + [0] = " " , + [1] = "k", + [2] = "m", + [3] = "g", + [4] = "t", /* Tera 10^12 */ + [5] = "p", /* Peta 10^15 */ + [6] = "e", /* Exa 10^18 */ +} ; +CONFIRM((sizeof(scale_d_tags) / sizeof(char*)) == (scale_max + 1)) ; + +static const char* scale_b_tags [] = +{ + [0] = " " , + [1] = "KiB", + [2] = "MiB", + [3] = "GiB", + [4] = "TiB", + [5] = "PiB", + [6] = "EiB", +} ; +CONFIRM((sizeof(scale_b_tags) / sizeof(char*)) == (scale_max + 1)) ; + +static const ulong p10 [] = +{ + [ 0] = 1l, + [ 1] = 10l, + [ 2] = 100l, + [ 3] = 1000l, + [ 4] = 10000l, + [ 5] = 100000l, + [ 6] = 1000000l, + [ 7] = 10000000l, + [ 8] = 100000000l, + [ 9] = 1000000000l, + [10] = 10000000000l, + [11] = 100000000000l, + [12] = 1000000000000l, + [13] = 10000000000000l, + [14] = 100000000000000l, + [15] = 1000000000000000l, + [16] = 10000000000000000l, + [17] = 100000000000000000l, + [18] = 1000000000000000000l, + [19] = (ulong)LONG_MAX + 1, /* all signed values < this */ +} ; +CONFIRM((sizeof(p10) / sizeof(ulong)) == ((scale_max * 3) + 2)) ; +CONFIRM((LONG_MAX / 10) < 1000000000000000000l) ; + +static const long q10 [] = +{ + [ 0] = 1l / 2, + [ 1] = 10l / 2, + [ 2] = 100l / 2, + [ 3] = 1000l / 2, + [ 4] = 10000l / 2, + [ 5] = 100000l / 2, + [ 6] = 1000000l / 2, + [ 7] = 10000000l / 2, + [ 8] = 100000000l / 2, + [ 9] = 1000000000l / 2, + [10] = 10000000000l / 2, + [11] = 100000000000l / 2, + [12] = 1000000000000l / 2, + [13] = 10000000000000l / 2, + [14] = 100000000000000l / 2, + [15] = 1000000000000000l / 2, + [16] = 10000000000000000l / 2, + [17] = 100000000000000000l / 2, + [18] = 1000000000000000000l / 2, +} ; +CONFIRM((sizeof(q10) / sizeof(long)) == ((scale_max * 3) + 1)) ; + +static void qfs_form_scaled(qf_str qfs, long v, uint f, uint d, const char* tag, + enum pf_flags flags) ; + +/*------------------------------------------------------------------------------ + * Form value scaled to 4 significant digits, or as simple decimal. + * + * When scaling, scale by powers of 1,000, to produce: + * + * 0..999 as simple 1, 2 or 3 digits, followed by " " + * 1,000..9,999 as 4 digits with comma, followed by " " + * + * 10,000..99,994 as 99.99k -- rounded + * 99,995..999,949 as 999.9k -- rounded + * 999,950..9,999,499 as 9,999k -- rounded + * + * thereafter, as for 'k', but with 'm', 'g', etc. + * + * When not scaling, produce simple decimal with no trailing space. + * + * In any case, produce a leading sign if required. + * + * Accepts the following pf_xxx flags: + * + * pf_scale -- scale as above (if not, no scaling) + * pf_trailing -- include blank scale for units + * pf_commas -- format with commas -- implied if pf_scale + * pf_plus -- add '+' sign if not -ve + * pf_space -- add ' ' "sign" if not -ve + */ +extern qfs_num_str_t +qfs_dec_value(long val, enum pf_flags flags) +{ + qfs_num_str_t num ; + qf_str_t qfs ; + + qfs_init(qfs, num.str, sizeof(num.str)) ; + + flags &= (pf_commas | pf_plus | pf_space | pf_scale | pf_trailing) ; + + if ((flags & pf_scale) == 0) + { + qfs_signed(qfs, val, flags & ~pf_trailing, 0, 0) ; + } + else + { + int s ; + uint i, d ; + ldiv_t r ; + + i = 0 ; + d = 0 ; + + if (val >= 0) + s = +1 ; + else + { + s = -1 ; + val = -val ; + } ; + + /* Find the power of 1,000 which val is ... */ + + while (((ulong)val >= p10[i + 4]) && (i < ((scale_max - 1) * 3))) + i += 3 ; + + if (i == 0) + { + r.quot = val ; + r.rem = 0 ; + } + else + { + /* Maximum i == (scale_max - 1) * 3 -- and have p10 upto and + * including scale_max * 3. + */ + if ((ulong)val < p10[i + 1]) + d = 3 ; + else if ((ulong)val < p10[i + 2]) + d = 2 ; + else if ((ulong)val < p10[i + 3]) + d = 1 ; + else + d = 0 ; + + /* Scale down to required number of decimals and round. + * + * If is thousands, then i = 3, if value = 10,000 (smallest possible) + * then d == 2. So divide by 5 (q10[3 - 2]) to make ls bit the + * rounding bit, add one and shift off the rounding bit. + * + * The result should be 1000..9999, unless value is greater than our + * ability to scale, or has rounded up one decade. + */ + val = ((val / q10[i - d]) + 1) >> 1 ; + + qassert(val >= 1000) ; + + if (val > 9999) + { + if (d == 0) + { + if (i < (scale_max * 3)) + { + qassert(val == 10000) ; + + val = 1000 ; + d = 2 ; + i += 3 ; + } ; + } + else + { + qassert(val == 10000) ; + + val = 1000 ; + d -= 1 ; + } ; + } ; + + r = ldiv(val, p10[d]) ; + } ; + + qfs_form_scaled(qfs, r.quot * s, r.rem, d, scale_d_tags[i / 3], flags) ; + } ; + + qfs_term(qfs) ; + + return num ; +} ; + +/*------------------------------------------------------------------------------ + * Form value scaled to 4 significant digits, or as simple decimal. + * + * When scaling, scale by powers of 1,024, to produce: + * + * 0..999 as simple 1, 2 or 3 digits, followed by " " + * 1,000..9,999 as 4 digits with comma, followed by " " + * + * 10,000..99,994 as 99.99KiB -- rounded + * 99,995..999,949 as 999.9KiB-- rounded + * 999,950..9,999,499 as 9,999KiB-- rounded + * + * thereafter, as for 'KiB', but with 'MiB', 'GiB', etc. + * + * When not scaling, produce simple decimal with no trailing space. + * + * In any case, produce a leading sign if required. + * + * Accepts the following pf_xxx flags: + * + * pf_scale -- scale as above (if not, no scaling) + * pf_trailing -- include blank scale for units + * pf_commas -- format with commas -- implied if pf_scale + * pf_plus -- add '+' sign if not -ve + * pf_space -- add ' ' "sign" if not -ve + */ +extern qfs_num_str_t +qfs_bin_value(long val, enum pf_flags flags) +{ + qfs_num_str_t num ; + qf_str_t qfs ; + + qfs_init(qfs, num.str, sizeof(num.str)) ; + + flags &= (pf_commas | pf_plus | pf_space | pf_scale | pf_trailing) ; + + if ((flags & pf_scale) == 0) + { + qfs_signed(qfs, val, flags & ~pf_trailing, 0, 0) ; + } + else + { + int s ; + + ulong v ; + uint i, d, f ; + + i = 0 ; + d = 0 ; + f = 0 ; + + if (val >= 0) + s = +1 ; + else + { + s = -1 ; + val = -val ; + } ; + + v = val ; + while ((v >= 1024) && (i < scale_max)) + { + v >>= 10 ; /* find power of 1024 scale */ + i += 1 ; + } ; + + if (i > 0) + { + ulong e ; + int is ; + + if (v < 10) + d = 3 ; /* number of decimals expected */ + else if (v < 100) + d = 2 ; + else if (v < 1000) + d = 1 ; + else + d = 0 ; /* should be already */ + + /* Scale up to the required number of decimals, shift down so that + * only ms bit of fraction is left, round and shift off rounding bit. + * + * If d != 0, then will scale up by 10, 100 or 1000. If the value is + * greater than ULONG_MAX / 1024, then we do the bottom 10 bits + * separately, and scale the calculation down by 10 bits. + */ + v = val ; /* operate on unsigned v */ + + e = 0 ; /* assume no extra bits */ + is = i * 10 ; /* the shift down */ + + if ((d != 0) && (v > (ULONG_MAX >> 10))) + { + e = (v & 0x3FF) * p10[d] ; /* take bottom 10 bits */ + e >>= 10 ; /* discard 10 bits of extra part */ + v >>= 10 ; /* scale down value */ + is -= 10 ; /* reduce shift */ + } ; + + v = ((((v * p10[d]) + e) >> (is - 1)) + 1) >> 1 ; + +// qassert(v >= 1000) ; + + if (d == 0) + { + if ((v == 1024) && (i < scale_max)) + { + v = 1000 ; /* rounded up to next power of 1024 */ + d = 3 ; + i += 1 ; + } + } + else + { + if (v >= 10000) + { +// qassert(v == 10000) ; + + v = 1000 ; /* rounded up to one less decimals */ + d -= 1 ; + } ; + } ; + + val = v / p10[d] ; + f = v % p10[d] ; + } ; + + qfs_form_scaled(qfs, val * s, f, d, scale_d_tags[i / 3], flags) ; + } ; + + qfs_term(qfs) ; + + return num ; +} ; + +/*------------------------------------------------------------------------------ + * Form a time period value. + * + * +/-999d99h99m99h99.999s + * + * Accepts the following pf_xxx flags: + * + * pf_commas -- format with commas (very unlikely !) + * pf_plus -- add '+' sign if not -ve + * pf_space -- add ' ' "sign" if not -ve + */ +extern qfs_num_str_t +qfs_time_period(qtime_t val, enum pf_flags flags) +{ + qfs_num_str_t num ; + qf_str_t qfs ; + int s ; + int w ; + + qfs_init(qfs, num.str, sizeof(num.str)) ; + + flags &= (pf_commas | pf_plus | pf_space) ; + + if (val >= 0) + s = +1 ; + else + { + s = -1 ; + val = -val ; + } ; + + /* Round value to milli seconds + */ + val = (val + (QTIME_SECOND / 2000)) / (QTIME_SECOND / 1000) ; + + w = 0 ; + + if (val >= (2 * 24 * 60 * 60 * 1000)) + { + qfs_signed(qfs, (val / (24 * 60 * 60 * 1000)) * s, flags, w, w) ; + qfs_append_ch(qfs, 'd') ; + + val %= (24 * 60 * 60 * 1000) ; + s = 1 ; + flags = pf_zeros ; + w = 2 ; + } ; + + if ((val >= (2 * 60 * 60 * 1000)) || (w > 0)) + { + qfs_signed(qfs, (val / (60 * 60 * 1000)) * s, flags, w, w) ; + qfs_append_ch(qfs, 'h') ; + + val %= (60 * 60 * 1000) ; + s = 1 ; + flags = pf_zeros ; + w = 2 ; + } ; + + if ((val >= (2 * 60 * 1000)) || (w > 0)) + { + qfs_signed(qfs, (val / (60 * 1000)) * s, flags, w, w) ; + qfs_append_ch(qfs, 'm') ; + + val %= (60 * 1000) ; + s = 1 ; + flags = pf_zeros ; + w = 2 ; + } ; + + qfs_signed(qfs, (val / 1000) * s, flags, w, w) ; + qfs_append_ch(qfs, '.') ; + qfs_unsigned(qfs, val % 1000, pf_zeros, 3, 3) ; + qfs_append_ch(qfs, 's') ; + + qfs_term(qfs) ; + + return num ; +} ; + +/*------------------------------------------------------------------------------ + * Form string for number, with commas and "d" decimal digits, followed by + * the given tag -- where d = 0..4 + * + * So: val=1234567, d=2, tag="k" -> "12,345.67k". + * val=1234, d=0, tag="" -> "1,234" + */ +static void +qfs_form_scaled(qf_str qfs, long v, uint f, uint d, const char* tag, + enum pf_flags flags) +{ + if (d == 0) + qfs_signed(qfs, v, flags | pf_commas, 0, 0) ; + else + { + qfs_signed(qfs, v, flags, 0, 0) ; + qfs_append_ch(qfs, '.') ; + qfs_unsigned(qfs, f, pf_zeros, d, 0) ; + } ; + + if ((*tag != ' ') || ((flags & pf_trailing) != 0)) + qfs_append(qfs, tag) ; +} ; + + |