[pacman-dev] [PATCH 2/6] Add utility functions to convert sizes to strings

Dan McGee dpmcgee at gmail.com
Fri Feb 25 09:51:03 EST 2011


On Mon, Feb 21, 2011 at 1:02 PM, Jakob Gruber <jakob.gruber at gmail.com> wrote:
> char *size_to_human_string(off_t bytes);
> char *size_to_human_string_short(off_t bytes);
>
> Scale to the first unit with which the value will be less or equal to
> 2048. size_to_human_string_short uses short unit labels ("K","M",...)
> instead of the default ("KB", "MB"), uses only one decimal digit and has
> no space between the value and the label.
>
> For example, size_to_human_string: "1.31 MB", short: "1.3M".
>
> char *size_to_human_string_mb(off_t bytes);
> char *size_to_human_string_kb(off_t bytes);
>
> Convert to MB and KB respectively and format identically to
> size_to_human_string.
>
> Signed-off-by: Jakob Gruber <jakob.gruber at gmail.com>
> ---
>  src/pacman/callback.c |   46 ++++++------------------
>  src/pacman/package.c  |   20 ++++++-----
>  src/pacman/query.c    |    7 ++--
>  src/pacman/sync.c     |    7 ++--
>  src/pacman/util.c     |   94 +++++++++++++++++++++++++++++++++++++++++--------
>  src/pacman/util.h     |    4 ++
>  6 files changed, 111 insertions(+), 67 deletions(-)
>
> diff --git a/src/pacman/callback.c b/src/pacman/callback.c
> index f1e71bb..8ec4c3e 100644
> --- a/src/pacman/callback.c
> +++ b/src/pacman/callback.c
> @@ -484,17 +484,16 @@ void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total)
>  {
>        int infolen;
>        int filenamelen;
> -       char *fname, *p;
> +       char *fname, *p, *ratestr, *xferedstr;
>        /* used for wide character width determination and printing */
>        int len, wclen, wcwid, padwid;
>        wchar_t *wcfname;
>
>        int totaldownload = 0;
>        off_t xfered, total;
> -       double rate = 0.0, timediff = 0.0, f_xfered = 0.0;
> +       double rate = 0.0, timediff = 0.0;
>        unsigned int eta_h = 0, eta_m = 0, eta_s = 0;
>        int file_percent = 0, total_percent = 0;
> -       char rate_size = 'K', xfered_size = 'K';
>
>        if(config->noprogressbar || file_total == -1) {
>                if(file_xfered == 0) {
> @@ -556,7 +555,7 @@ void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total)
>                diff_sec = current_time.tv_sec - initial_time.tv_sec;
>                diff_usec = current_time.tv_usec - initial_time.tv_usec;
>                timediff = diff_sec + (diff_usec / 1000000.0);
> -               rate = xfered / (timediff * 1024.0);
> +               rate = xfered / timediff;
>
>                /* round elapsed time to the nearest second */
>                eta_s = (int)(timediff + 0.5);
> @@ -568,10 +567,10 @@ void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total)
>                        /* return if the calling interval was too short */
>                        return;
>                }
> -               rate = (xfered - xfered_last) / (timediff * 1024.0);
> +               rate = (xfered - xfered_last) / timediff;
>                /* average rate to reduce jumpiness */
>                rate = (rate + 2 * rate_last) / 3;
> -               eta_s = (total - xfered) / (rate * 1024.0);
> +               eta_s = (total - xfered) / (rate * 1024.0 * 1024.0);
>                rate_last = rate;
>                xfered_last = xfered;
>        }
> @@ -625,38 +624,15 @@ void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total)
>
>        }
>
> -       /* Awesome formatting for progress bar.  We need a mess of Kb->Mb->Gb stuff
> -        * here. We'll use limit of 2048 for each until we get some empirical */
> -       /* rate_size = 'K'; was set above */
> -       if(rate > 2048.0) {
> -               rate /= 1024.0;
> -               rate_size = 'M';
> -               if(rate > 2048.0) {
> -                       rate /= 1024.0;
> -                       rate_size = 'G';
> -                       /* we should not go higher than this for a few years (9999.9 Gb/s?)*/
> -               }
> -       }
> -
> -       f_xfered = xfered / 1024.0; /* convert to K by default */
> -       /* xfered_size = 'K'; was set above */
> -       if(f_xfered > 2048.0) {
> -               f_xfered /= 1024.0;
> -               xfered_size = 'M';
> -               if(f_xfered > 2048.0) {
> -                       f_xfered /= 1024.0;
> -                       xfered_size = 'G';
> -                       /* I should seriously hope that archlinux packages never break
> -                        * the 9999.9GB mark... we'd have more serious problems than the progress
> -                        * bar in pacman */
> -               }
> -       }
> +       ratestr = size_to_human_string_short((off_t)rate);
> +       xferedstr = size_to_human_string_short(xfered);
>
>        /* 1 space + filenamelen + 1 space + 7 for size + 1 + 7 for rate + 2 for /s + 1 space + 8 for eta */
> -       printf(" %ls%-*s %6.1f%c %#6.1f%c/s %02u:%02u:%02u", wcfname,
> -                       padwid, "", f_xfered, xfered_size,
> -                       rate, rate_size, eta_h, eta_m, eta_s);
> +       printf(" %ls%-*s %7s %7s/s %02u:%02u:%02u", wcfname,
> +                       padwid, "", xferedstr, ratestr, eta_h, eta_m, eta_s);
>
> +       free(ratestr);
> +       free(xferedstr);
>        free(fname);
>        free(wcfname);
>
> diff --git a/src/pacman/package.c b/src/pacman/package.c
> index 77a5ee7..156d257 100644
> --- a/src/pacman/package.c
> +++ b/src/pacman/package.c
> @@ -50,7 +50,7 @@ void dump_pkg_full(pmpkg_t *pkg, int level)
>  {
>        const char *reason;
>        time_t bdate, idate;
> -       char bdatestr[50] = "", idatestr[50] = "";
> +       char bdatestr[50] = "", idatestr[50] = "", *size;
I'd prefer these defined on different lines; yes they are the "same"
type but it can be confusing.
>        const alpm_list_t *i;
>        alpm_list_t *requiredby = NULL, *depstrings = NULL;
>
> @@ -105,17 +105,19 @@ void dump_pkg_full(pmpkg_t *pkg, int level)
>        }
>        list_display(_("Conflicts With :"), alpm_pkg_get_conflicts(pkg));
>        list_display(_("Replaces       :"), alpm_pkg_get_replaces(pkg));
> +
> +       size = size_to_human_string_kb(alpm_pkg_get_size(pkg));
>        if(level < 0) {
> -               printf(_("Download Size  : %6.2f K\n"),
> -                       (double)alpm_pkg_get_size(pkg) / 1024.0);
> -       }
> -       if(level == 0) {
> -               printf(_("Compressed Size: %6.2f K\n"),
> -                       (double)alpm_pkg_get_size(pkg) / 1024.0);
> +               printf(_("Download Size  : %s\n"), size);
> +       } else if(level == 0) {
> +               printf(_("Compressed Size: %s\n"), size);
>        }
> +       free(size);
> +
> +       size = size_to_human_string_kb(alpm_pkg_get_isize(pkg));
> +       printf(_("Installed Size : %s\n"), size);
> +       free(size);
>
> -       printf(_("Installed Size : %6.2f K\n"),
> -                       (double)alpm_pkg_get_isize(pkg) / 1024.0);
>        string_display(_("Packager       :"), alpm_pkg_get_packager(pkg));
>        string_display(_("Architecture   :"), alpm_pkg_get_arch(pkg));
>        string_display(_("Build Date     :"), bdatestr);
> diff --git a/src/pacman/query.c b/src/pacman/query.c
> index 734875b..8a66927 100644
> --- a/src/pacman/query.c
> +++ b/src/pacman/query.c
> @@ -247,10 +247,9 @@ static int query_search(alpm_list_t *targets)
>
>                /* print the package size with the output if ShowSize option set */
>                if(!config->quiet && config->showsize) {
> -                       /* Convert byte size to MB */
> -                       double mbsize = (double)alpm_pkg_get_size(pkg) / (1024.0 * 1024.0);
> -
> -                       printf(" [%.2f MB]", mbsize);
> +                       char *size = size_to_human_string_mb(alpm_pkg_get_size(pkg));
> +                       printf(" [%s]", size);
> +                       free(size);
>                }
>
>
> diff --git a/src/pacman/sync.c b/src/pacman/sync.c
> index 7af1667..21fe4ba 100644
> --- a/src/pacman/sync.c
> +++ b/src/pacman/sync.c
> @@ -349,10 +349,9 @@ static int sync_search(alpm_list_t *syncs, alpm_list_t *targets)
>
>                        /* print the package size with the output if ShowSize option set */
>                        if(!config->quiet && config->showsize) {
> -                               /* Convert byte size to MB */
> -                               double mbsize = (double)alpm_pkg_get_size(pkg) / (1024.0 * 1024.0);
> -
> -                               printf(" [%.2f MB]", mbsize);
> +                               char *size = size_to_human_string_mb(alpm_pkg_get_size(pkg));
> +                               printf(" [%s]", size);
> +                               free(size);
>                        }
>
>                        if (!config->quiet) {
> diff --git a/src/pacman/util.c b/src/pacman/util.c
> index c08ebb1..b651478 100644
> --- a/src/pacman/util.c
> +++ b/src/pacman/util.c
> @@ -501,10 +501,9 @@ void list_display_linebreak(const char *title, const alpm_list_t *list)
>  /* prepare a list of pkgs to display */
>  void display_targets(const alpm_list_t *pkgs, int install)
>  {
> -       char *str;
> +       char *str, *size;
>        const alpm_list_t *i;
>        off_t isize = 0, dlsize = 0;
> -       double mbisize = 0.0, mbdlsize = 0.0;
>        alpm_list_t *targets = NULL;
>
>        if(!pkgs) {
> @@ -522,10 +521,10 @@ void display_targets(const alpm_list_t *pkgs, int install)
>
>                /* print the package size with the output if ShowSize option set */
>                if(config->showsize) {
> -                       double mbsize = (double)alpm_pkg_get_size(pkg) / (1024.0 * 1024.0);
> -
> -                       pm_asprintf(&str, "%s-%s [%.2f MB]", alpm_pkg_get_name(pkg),
> -                                       alpm_pkg_get_version(pkg), mbsize);
> +                       size = size_to_human_string_mb(alpm_pkg_get_size(pkg));
> +                       pm_asprintf(&str, "%s-%s [%s]", alpm_pkg_get_name(pkg),
> +                                       alpm_pkg_get_version(pkg), size);
> +                       free(size);
>                } else {
>                        pm_asprintf(&str, "%s-%s", alpm_pkg_get_name(pkg),
>                                        alpm_pkg_get_version(pkg));
> @@ -533,19 +532,19 @@ void display_targets(const alpm_list_t *pkgs, int install)
>                targets = alpm_list_add(targets, str);
>        }
>
> -       /* Convert byte sizes to MB */
> -       mbdlsize = (double)dlsize / (1024.0 * 1024.0);
> -       mbisize = (double)isize / (1024.0 * 1024.0);
> -
>        if(install) {
>                pm_asprintf(&str, _("Targets (%d):"), alpm_list_count(targets));
>                list_display(str, targets);
>                free(str);
>                printf("\n");
>
> -               printf(_("Total Download Size:    %.2f MB\n"), mbdlsize);
> +               size = size_to_human_string_mb(dlsize);
> +               printf(_("Total Download Size:    %s\n"), size);
> +               free(size);
>                if(!(config->flags & PM_TRANS_FLAG_DOWNLOADONLY)) {
> -                       printf(_("Total Installed Size:   %.2f MB\n"), mbisize);
> +                       size = size_to_human_string_mb(isize);
> +                       printf(_("Total Installed Size:   %s\n"), size);
> +                       free(size);
>                }
>        } else {
>                pm_asprintf(&str, _("Remove (%d):"), alpm_list_count(targets));
> @@ -553,7 +552,9 @@ void display_targets(const alpm_list_t *pkgs, int install)
>                free(str);
>                printf("\n");
>
> -               printf(_("Total Removed Size:   %.2f MB\n"), mbisize);
> +               size = size_to_human_string_mb(isize);
> +               printf(_("Total Removed Size:   %s\n"), size);
> +               free(size);
I see no reason we should lock any of these to MB units- just use the
general formatter here.

>        }
>
>        FREELIST(targets);
> @@ -594,6 +595,70 @@ static char *pkg_get_location(pmpkg_t *pkg)
>        }
>  }
>
> +/** Converts sizes in bytes into strings.
> + *
> + * @param bytes the size in bytes
> + * @param format the format string to use. The first argument is of type
> + *                             float and represents the size. The second argument is of type
> + *                             char* and represents the unit label. for example, "%.2f %s"
> + *                             could result in "1.23 MB"
> + * @param unit the target unit. can be either one of the short unit
> + *                             labels ("B", "K", ...); or NULL, in which case the first
> + *                             unit which will bring the value to below a threshold of 2048
> + *                             will be chosen.
> + * @param long_units whether to use short ("K") or long ("KB") unit labels
> + *
> + * @return the size string
> + */
> +static char *size_to_human_string_format(off_t bytes, const char *format,
> +                                                                                                                                       const char *unit, int long_labels)
> +{
> +       char *ret;
> +       static const char *shortlabels[] = {"B", "K", "M", "G", "T", "P"};
> +       static const char *longlabels[] = {"B", "KB", "MB", "GB", "TB", "PB"};
> +       const char **labels = long_labels ? longlabels : shortlabels;
> +       const int unitcount = sizeof(shortlabels) / sizeof(shortlabels[0]);
> +       int index;
> +       float val = (float)bytes;
> +
> +       /* stop conditions:
> +        * 1) a target unit is specified and we are at target unit,
> +        * 2) target unit is NOT specified and value is leq 2048,
> +        * and 3) we are already at the largest unit.s
> +        * note that if an invalid target is specified in 1),
> +        * we convert to the largest unit */
> +       for(index = 0; index < unitcount - 1; index++) {
> +               if(unit != NULL && strcmp(shortlabels[index], unit) == 0) break;
I'd make the function param a char (not a "string") to ensure people
aren't doing stupid things like passing a long label in by mistake.
Then you can just use == here.

> +               else if(unit == NULL && val <= 2048.0) break;
> +
> +               val /= 1024.0;
> +       }
> +
> +       pm_asprintf(&ret, format, val, labels[index]);
> +
> +       return ret;
> +}
> +
> +char *size_to_human_string(off_t bytes)
> +{
> +       return size_to_human_string_format(bytes, "%.2f %s", NULL, 1);
> +}
> +
> +char *size_to_human_string_mb(off_t bytes)
> +{
> +       return size_to_human_string_format(bytes, "%.2f %s", "M", 1);
> +}
> +
> +char *size_to_human_string_kb(off_t bytes)
> +{
> +       return size_to_human_string_format(bytes, "%.2f %s", "K", 1);
> +}
> +
> +char *size_to_human_string_short(off_t bytes)
> +{
> +       return size_to_human_string_format(bytes, "%.1f%s", NULL, 0);
> +}
> +

I think this API has been over-engineered with all these one-liners.
We aren't exposing this as a library function or anything, so people
can deal with a little complexity in the call themselves. I'd have one
function for all of this, and we can then even skip the malloc/free
cycle:
    double value humanize_size(off_t bytes, char max_unit, int
long_labels, const char **label)

Logic stays as is in your format function, expect we don't actually
print the string- the value is returned and label, if provided, gets a
pointer to the correct string. Does this make sense? I'm open for
feedback of course, but I see this as 1) less boilerplate methods in
util.c 2) no free() necessary by caller.

>  void print_packages(const alpm_list_t *packages)
>  {
>        const alpm_list_t *i;
> @@ -638,8 +703,7 @@ void print_packages(const alpm_list_t *packages)
>                /* %s : size */
>                if(strstr(temp,"%s")) {
>                        char *size;
> -                       double mbsize = (double)pkg_get_size(pkg) / (1024.0 * 1024.0);
> -                       pm_asprintf(&size, "%.2f", mbsize);
> +                       size = size_to_human_string_format(pkg_get_size(pkg), "%.2f", "M", 0);
>                        string = strreplace(temp, "%s", size);
>                        free(size);
>                        free(temp);
> diff --git a/src/pacman/util.h b/src/pacman/util.h
> index 234a631..ed03c13 100644
> --- a/src/pacman/util.h
> +++ b/src/pacman/util.h
> @@ -51,6 +51,10 @@ char *strtoupper(char *str);
>  char *strtrim(char *str);
>  char *strreplace(const char *str, const char *needle, const char *replace);
>  alpm_list_t *strsplit(const char *str, const char splitchar);
> +char *size_to_human_string(off_t bytes);
> +char *size_to_human_string_mb(off_t bytes);
> +char *size_to_human_string_kb(off_t bytes);
> +char *size_to_human_string_short(off_t bytes);
>  void string_display(const char *title, const char *string);
>  void list_display(const char *title, const alpm_list_t *list);
>  void list_display_linebreak(const char *title, const alpm_list_t *list);
> --
> 1.7.4.1
>
>
>


More information about the pacman-dev mailing list