1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <pretty/sizes.h> 6 7#include <assert.h> 8#include <stdbool.h> 9#include <stdint.h> 10#include <stdio.h> 11 12#include <zircon/assert.h> 13 14char* format_size_fixed(char* str, size_t str_size, size_t bytes, char unit) { 15 static const char units[] = "BkMGTPE"; 16 static int num_units = sizeof(units) - 1; 17 18 if (str_size == 0) { 19 // Even if NULL. 20 return str; 21 } 22 ZX_DEBUG_ASSERT(str != NULL); 23 if (str_size == 1) { 24 str[0] = '\0'; 25 return str; 26 } 27 28 char* orig_str = str; 29 size_t orig_bytes = bytes; 30retry:; 31 int ui = 0; 32 uint16_t r = 0; 33 bool whole = true; 34 // If we have a fixed (non-zero) unit, divide until we hit it. 35 // 36 // Otherwise, divide until we reach a unit that can express the value 37 // with 4 or fewer whole digits. 38 // - If we can express the value without a fraction (it's a whole 39 // kibi/mebi/gibibyte), use the largest possible unit (e.g., favor 40 // "1M" over "1024k"). 41 // - Otherwise, favor more whole digits to retain precision (e.g., 42 // favor "1025k" or "1025.0k" over "1.0M"). 43 while (unit != 0 44 ? units[ui] != unit 45 : (bytes >= 10000 || (bytes != 0 && (bytes & 1023) == 0))) { 46 ui++; 47 if (ui >= num_units) { 48 // We probably got an unknown unit. Fall back to a natural unit, 49 // but leave a hint that something's wrong. 50 ZX_DEBUG_ASSERT(str_size > 1); 51 *str++ = '?'; 52 str_size--; 53 unit = 0; 54 bytes = orig_bytes; 55 goto retry; 56 } 57 if (bytes & 1023) { 58 whole = false; 59 } 60 r = bytes % 1024; 61 bytes /= 1024; 62 } 63 if (whole) { 64 snprintf(str, str_size, "%zu%c", bytes, units[ui]); 65 } else { 66 // r represents the remainder of the most recent division operation. 67 // Since we provide a single unit of precision, we can round it based 68 // on the second digit and increment bytes in the case that it pushes 69 // the final value back over into a whole number. 70 unsigned int round_up = ((r % 100) >= 50); 71 r = (r / 100) + round_up; 72 if (r == 10) { 73 bytes++; 74 r = 0; 75 } 76 snprintf(str, str_size, "%zu.%1u%c", bytes, r, units[ui]); 77 } 78 return orig_str; 79} 80 81char* format_size(char* str, size_t str_size, size_t bytes) { 82 return format_size_fixed(str, str_size, bytes, 0); 83} 84