diff options
Diffstat (limited to 'libbb/progress.c')
| -rw-r--r-- | libbb/progress.c | 95 |
1 files changed, 65 insertions, 30 deletions
diff --git a/libbb/progress.c b/libbb/progress.c index ced04ac32..a490b8390 100644 --- a/libbb/progress.c +++ b/libbb/progress.c | |||
| @@ -60,9 +60,16 @@ void FAST_FUNC bb_progress_init(bb_progress_t *p) | |||
| 60 | p->inited = 1; | 60 | p->inited = 1; |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | /* File already had beg_size bytes. | ||
| 64 | * Then we started downloading. | ||
| 65 | * We downloaded "transferred" bytes so far. | ||
| 66 | * Download is expected to stop when total size (beg_size + transferred) | ||
| 67 | * will be "totalsize" bytes. | ||
| 68 | * If totalsize == 0, then it is unknown. | ||
| 69 | */ | ||
| 63 | void FAST_FUNC bb_progress_update(bb_progress_t *p, | 70 | void FAST_FUNC bb_progress_update(bb_progress_t *p, |
| 64 | const char *curfile, | 71 | const char *curfile, |
| 65 | uoff_t beg_range, | 72 | uoff_t beg_size, |
| 66 | uoff_t transferred, | 73 | uoff_t transferred, |
| 67 | uoff_t totalsize) | 74 | uoff_t totalsize) |
| 68 | { | 75 | { |
| @@ -72,32 +79,53 @@ void FAST_FUNC bb_progress_update(bb_progress_t *p, | |||
| 72 | int barlength; | 79 | int barlength; |
| 73 | int kiloscale; | 80 | int kiloscale; |
| 74 | 81 | ||
| 75 | /* totalsize == 0 if it is unknown */ | 82 | beg_and_transferred = beg_size + transferred; |
| 76 | |||
| 77 | beg_and_transferred = beg_range + transferred; | ||
| 78 | 83 | ||
| 79 | elapsed = monotonic_sec(); | 84 | elapsed = monotonic_sec(); |
| 80 | since_last_update = elapsed - p->lastupdate_sec; | 85 | since_last_update = elapsed - p->lastupdate_sec; |
| 81 | /* Do not update on every call | 86 | /* |
| 82 | * (we can be called on every network read!) */ | 87 | * Do not update on every call |
| 88 | * (we can be called on every network read!) | ||
| 89 | */ | ||
| 83 | if (since_last_update == 0 && beg_and_transferred < totalsize) | 90 | if (since_last_update == 0 && beg_and_transferred < totalsize) |
| 84 | return; | 91 | return; |
| 85 | 92 | ||
| 86 | /* Scale sizes down if they are close to overflowing. | ||
| 87 | * If off_t is only 32 bits, this allows calculations | ||
| 88 | * like (100 * transferred / totalsize) without risking overflow. | ||
| 89 | * Introduced error is < 0.1% | ||
| 90 | */ | ||
| 91 | kiloscale = 0; | 93 | kiloscale = 0; |
| 92 | if (totalsize >= (1 << 20)) { | 94 | /* |
| 93 | totalsize >>= 10; | 95 | * Scale sizes down if they are close to overflowing. |
| 94 | beg_range >>= 10; | 96 | * This allows calculations like (100 * transferred / totalsize) |
| 95 | transferred >>= 10; | 97 | * without risking overflow: we guarantee 10 highest bits to be 0. |
| 96 | beg_and_transferred >>= 10; | 98 | * Introduced error is less than 1 / 2^12 ~= 0.025% |
| 97 | kiloscale++; | 99 | */ |
| 100 | if (ULONG_MAX > 0xffffffff || sizeof(off_t) == 4 || sizeof(off_t) != 8) { | ||
| 101 | /* | ||
| 102 | * 64-bit CPU || small off_t: in either case, | ||
| 103 | * >> is cheap, single-word operation. | ||
| 104 | * ... || strange off_t: also use this code (it is safe, | ||
| 105 | * even if suboptimal), because 32/64 optimized one | ||
| 106 | * works only for 64-bit off_t. | ||
| 107 | */ | ||
| 108 | if (totalsize >= (1 << 22)) { | ||
| 109 | totalsize >>= 10; | ||
| 110 | beg_size >>= 10; | ||
| 111 | transferred >>= 10; | ||
| 112 | beg_and_transferred >>= 10; | ||
| 113 | kiloscale = 1; | ||
| 114 | } | ||
| 115 | } else { | ||
| 116 | /* 32-bit CPU and 64-bit off_t. | ||
| 117 | * Pick a shift (40 bits) which is easier to do on 32-bit CPU. | ||
| 118 | */ | ||
| 119 | if (totalsize >= (uoff_t)(1ULL << 54)) { | ||
| 120 | totalsize = (uint32_t)(totalsize >> 32) >> 8; | ||
| 121 | beg_size = (uint32_t)(beg_size >> 32) >> 8; | ||
| 122 | transferred = (uint32_t)(transferred >> 32) >> 8; | ||
| 123 | beg_and_transferred = (uint32_t)(beg_and_transferred >> 32) >> 8; | ||
| 124 | kiloscale = 4; | ||
| 125 | } | ||
| 98 | } | 126 | } |
| 99 | 127 | ||
| 100 | if (beg_and_transferred >= totalsize) | 128 | if (beg_and_transferred > totalsize) |
| 101 | beg_and_transferred = totalsize; | 129 | beg_and_transferred = totalsize; |
| 102 | 130 | ||
| 103 | ratio = 100 * beg_and_transferred / totalsize; | 131 | ratio = 100 * beg_and_transferred / totalsize; |
| @@ -124,14 +152,14 @@ void FAST_FUNC bb_progress_update(bb_progress_t *p, | |||
| 124 | } | 152 | } |
| 125 | 153 | ||
| 126 | while (beg_and_transferred >= 100000) { | 154 | while (beg_and_transferred >= 100000) { |
| 127 | kiloscale++; | ||
| 128 | beg_and_transferred >>= 10; | 155 | beg_and_transferred >>= 10; |
| 156 | kiloscale++; | ||
| 129 | } | 157 | } |
| 130 | /* see http://en.wikipedia.org/wiki/Tera */ | 158 | /* see http://en.wikipedia.org/wiki/Tera */ |
| 131 | fprintf(stderr, "%6u%c ", (unsigned)beg_and_transferred, " kMGTPEZY"[kiloscale]); | 159 | fprintf(stderr, "%6u%c ", (unsigned)beg_and_transferred, " kMGTPEZY"[kiloscale]); |
| 132 | #define beg_and_transferred dont_use_beg_and_transferred_below() | 160 | #define beg_and_transferred dont_use_beg_and_transferred_below() |
| 133 | 161 | ||
| 134 | if (transferred > p->lastsize) { | 162 | if (transferred != p->lastsize) { |
| 135 | p->lastupdate_sec = elapsed; | 163 | p->lastupdate_sec = elapsed; |
| 136 | p->lastsize = transferred; | 164 | p->lastsize = transferred; |
| 137 | if (since_last_update >= STALLTIME) { | 165 | if (since_last_update >= STALLTIME) { |
| @@ -141,20 +169,27 @@ void FAST_FUNC bb_progress_update(bb_progress_t *p, | |||
| 141 | } | 169 | } |
| 142 | since_last_update = 0; /* we are un-stalled now */ | 170 | since_last_update = 0; /* we are un-stalled now */ |
| 143 | } | 171 | } |
| 172 | |||
| 144 | elapsed -= p->start_sec; /* now it's "elapsed since start" */ | 173 | elapsed -= p->start_sec; /* now it's "elapsed since start" */ |
| 145 | 174 | ||
| 146 | if (since_last_update >= STALLTIME) { | 175 | if (since_last_update >= STALLTIME) { |
| 147 | fprintf(stderr, " - stalled -"); | 176 | fprintf(stderr, " - stalled -"); |
| 177 | } else if (!totalsize || !transferred || (int)elapsed <= 0) { | ||
| 178 | fprintf(stderr, "--:--:-- ETA"); | ||
| 148 | } else { | 179 | } else { |
| 149 | uoff_t to_download = totalsize - beg_range; | 180 | unsigned eta, secs, hours; |
| 150 | if (!totalsize || (int)elapsed <= 0 || transferred > to_download) { | 181 | |
| 151 | fprintf(stderr, "--:--:-- ETA"); | 182 | totalsize -= beg_size; /* now it's "total to upload" */ |
| 152 | } else { | 183 | |
| 153 | /* to_download / (transferred/elapsed) - elapsed: */ | 184 | /* Estimated remaining time = |
| 154 | unsigned eta = to_download * elapsed / transferred - elapsed; | 185 | * estimated_sec_to_dl_totalsize_bytes - elapsed_sec = |
| 155 | unsigned secs = eta % 3600; | 186 | * totalsize / average_bytes_sec_so_far - elapsed = |
| 156 | unsigned hours = eta / 3600; | 187 | * totalsize / (transferred/elapsed) - elapsed = |
| 157 | fprintf(stderr, "%02u:%02u:%02u ETA", hours, secs / 60, secs % 60); | 188 | * totalsize * elapsed / transferred - elapsed |
| 158 | } | 189 | */ |
| 190 | eta = totalsize * elapsed / transferred - elapsed; | ||
| 191 | secs = eta % 3600; | ||
| 192 | hours = eta / 3600; | ||
| 193 | fprintf(stderr, "%02u:%02u:%02u ETA", hours, secs / 60, secs % 60); | ||
| 159 | } | 194 | } |
| 160 | } | 195 | } |
