Path: blob/main/cddl/contrib/opensolaris/lib/libcmdutils/common/nicenum.c
39535 views
/*1* CDDL HEADER START2*3* The contents of this file are subject to the terms of the4* Common Development and Distribution License (the "License").5* You may not use this file except in compliance with the License.6*7* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE8* or http://www.opensolaris.org/os/licensing.9* See the License for the specific language governing permissions10* and limitations under the License.11*12* When distributing Covered Code, include this CDDL HEADER in each13* file and include the License file at usr/src/OPENSOLARIS.LICENSE.14* If applicable, add the following below this CDDL HEADER, with the15* fields enclosed by brackets "[]" replaced with your own identifying16* information: Portions Copyright [yyyy] [name of copyright owner]17*18* CDDL HEADER END19*/2021/*22* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.23* Copyright 2017 Jason king24*/2526#include <stdio.h>27#include <string.h>28#include <sys/types.h>29#include <sys/debug.h>30#include "libcmdutils.h"3132/* The largest suffix that can fit, aka an exabyte (2^60 / 10^18) */33#define INDEX_MAX (6)3435/* Verify INDEX_MAX fits */36CTASSERT(INDEX_MAX * 10 < sizeof (uint64_t) * 8);3738void39nicenum_scale(uint64_t n, size_t units, char *buf, size_t buflen,40uint32_t flags)41{42uint64_t divamt = 1024;43uint64_t divisor = 1;44int index = 0;45int rc = 0;46char u;4748if (units == 0)49units = 1;5051if (n > 0) {52n *= units;53if (n < units)54goto overflow;55}5657if (flags & NN_DIVISOR_1000)58divamt = 1000;5960/*61* This tries to find the suffix S(n) such that62* S(n) <= n < S(n+1), where S(n) = 2^(n*10) | 10^(3*n)63* (i.e. 1024/1000, 1,048,576/1,000,000, etc). Stop once S(n)64* is the largest prefix supported (i.e. don't bother computing65* and checking S(n+1). Since INDEX_MAX should be the largest66* suffix that fits (currently an exabyte), S(INDEX_MAX + 1) is67* never checked as it would overflow.68*/69while (index < INDEX_MAX) {70uint64_t newdiv = divisor * divamt;7172/* CTASSERT() guarantee these never trip */73VERIFY3U(newdiv, >=, divamt);74VERIFY3U(newdiv, >=, divisor);7576if (n < newdiv)77break;7879divisor = newdiv;80index++;81}8283u = " KMGTPE"[index];8485if (index == 0) {86rc = snprintf(buf, buflen, "%llu", n);87} else if (n % divisor == 0) {88/*89* If this is an even multiple of the base, always display90* without any decimal precision.91*/92rc = snprintf(buf, buflen, "%llu%c", n / divisor, u);93} else {94/*95* We want to choose a precision that reflects the best choice96* for fitting in 5 characters. This can get rather tricky97* when we have numbers that are very close to an order of98* magnitude. For example, when displaying 10239 (which is99* really 9.999K), we want only a single place of precision100* for 10.0K. We could develop some complex heuristics for101* this, but it's much easier just to try each combination102* in turn.103*/104int i;105for (i = 2; i >= 0; i--) {106if ((rc = snprintf(buf, buflen, "%.*f%c", i,107(double)n / divisor, u)) <= 5)108break;109}110}111112if (rc + 1 > buflen || rc < 0)113goto overflow;114115return;116117overflow:118/* prefer a more verbose message if possible */119if (buflen > 10)120(void) strlcpy(buf, "<overflow>", buflen);121else122(void) strlcpy(buf, "??", buflen);123}124125void126nicenum(uint64_t num, char *buf, size_t buflen)127{128nicenum_scale(num, 1, buf, buflen, 0);129}130131132