1 /* $NetBSD: meter.c,v 1.1.1.4 2018/02/06 01:53:08 christos Exp $ */ 2 3 /* meter.c - lutil_meter meters */ 4 /* $OpenLDAP$ */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright (c) 2009 by Emily Backes, Symas Corp. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted only as authorized by the OpenLDAP 12 * Public License. 13 * 14 * A copy of this license is available in the file LICENSE in the 15 * top-level directory of the distribution or, alternatively, at 16 * <http://www.OpenLDAP.org/license.html>. 17 */ 18 /* ACKNOWLEDGEMENTS: 19 * This work was initially developed by Emily Backes for inclusion 20 * in OpenLDAP software. 21 */ 22 23 #include <sys/cdefs.h> 24 __RCSID("$NetBSD: meter.c,v 1.1.1.4 2018/02/06 01:53:08 christos Exp $"); 25 26 #include "portable.h" 27 #include "lutil_meter.h" 28 29 #include <ac/assert.h> 30 #include <ac/string.h> 31 32 int 33 lutil_time_string ( 34 char *dest, 35 int duration, 36 int max_terms) 37 { 38 static const int time_div[] = {31556952, 39 604800, 40 86400, 41 3600, 42 60, 43 1, 44 0}; 45 const int * time_divp = time_div; 46 static const char * time_name_ch = "ywdhms"; 47 const char * time_name_chp = time_name_ch; 48 int term_count = 0; 49 char *buf = dest; 50 int time_quot; 51 52 assert ( max_terms >= 2 ); /* room for "none" message */ 53 54 if ( duration < 0 ) { 55 *dest = '\0'; 56 return 1; 57 } 58 if ( duration == 0 ) { 59 strcpy( dest, "none" ); 60 return 0; 61 } 62 while ( term_count < max_terms && duration > 0 ) { 63 if (duration > *time_divp) { 64 time_quot = duration / *time_divp; 65 duration %= *time_divp; 66 if (time_quot > 99) { 67 return 1; 68 } else { 69 *(buf++) = time_quot / 10 + '0'; 70 *(buf++) = time_quot % 10 + '0'; 71 *(buf++) = *time_name_chp; 72 ++term_count; 73 } 74 } 75 if ( *(++time_divp) == 0) duration = 0; 76 ++time_name_chp; 77 } 78 *buf = '\0'; 79 return 0; 80 } 81 82 int 83 lutil_get_now (double *now) 84 { 85 #ifdef HAVE_GETTIMEOFDAY 86 struct timeval tv; 87 88 assert( now ); 89 gettimeofday( &tv, NULL ); 90 *now = ((double) tv.tv_sec) + (((double) tv.tv_usec) / 1000000.0); 91 return 0; 92 #else 93 time_t tm; 94 95 assert( now ); 96 time( &tm ); 97 *now = (double) tm; 98 return 0; 99 #endif 100 } 101 102 int 103 lutil_meter_open ( 104 lutil_meter_t *meter, 105 const lutil_meter_display_t *display, 106 const lutil_meter_estimator_t *estimator, 107 size_t goal_value) 108 { 109 int rc; 110 111 assert( meter != NULL ); 112 assert( display != NULL ); 113 assert( estimator != NULL ); 114 115 if (goal_value < 1) return -1; 116 117 memset( (void*) meter, 0, sizeof( lutil_meter_t )); 118 meter->display = display; 119 meter->estimator = estimator; 120 lutil_get_now( &meter->start_time ); 121 meter->last_update = meter->start_time; 122 meter->goal_value = goal_value; 123 meter->last_position = 0; 124 125 rc = meter->display->display_open( &meter->display_data ); 126 if( rc != 0 ) return rc; 127 128 rc = meter->estimator->estimator_open( &meter->estimator_data ); 129 if( rc != 0 ) { 130 meter->display->display_close( &meter->display_data ); 131 return rc; 132 } 133 134 return 0; 135 } 136 137 int 138 lutil_meter_update ( 139 lutil_meter_t *meter, 140 size_t position, 141 int force) 142 { 143 static const double display_rate = 0.5; 144 double frac, cycle_length, speed, now; 145 time_t remaining_time, elapsed; 146 int rc; 147 148 assert( meter != NULL ); 149 150 lutil_get_now( &now ); 151 152 if ( !force && now - meter->last_update < display_rate ) return 0; 153 154 frac = ((double)position) / ((double) meter->goal_value); 155 elapsed = now - meter->start_time; 156 if (frac <= 0.0) return 0; 157 if (frac >= 1.0) { 158 rc = meter->display->display_update( 159 &meter->display_data, 160 1.0, 161 0, 162 (time_t) elapsed, 163 ((double)position) / elapsed); 164 } else { 165 rc = meter->estimator->estimator_update( 166 &meter->estimator_data, 167 meter->start_time, 168 frac, 169 &remaining_time ); 170 if ( rc == 0 ) { 171 cycle_length = now - meter->last_update; 172 speed = cycle_length > 0.0 ? 173 ((double)(position - meter->last_position)) 174 / cycle_length : 175 0.0; 176 rc = meter->display->display_update( 177 &meter->display_data, 178 frac, 179 remaining_time, 180 (time_t) elapsed, 181 speed); 182 if ( rc == 0 ) { 183 meter->last_update = now; 184 meter->last_position = position; 185 } 186 } 187 } 188 189 return rc; 190 } 191 192 int 193 lutil_meter_close (lutil_meter_t *meter) 194 { 195 meter->estimator->estimator_close( &meter->estimator_data ); 196 meter->display->display_close( &meter->display_data ); 197 198 return 0; 199 } 200 201 /* Default display and estimator */ 202 typedef struct { 203 int buffer_length; 204 char * buffer; 205 int need_eol; 206 int phase; 207 FILE *output; 208 } text_display_state_t; 209 210 static int 211 text_open (void ** display_datap) 212 { 213 static const int default_buffer_length = 81; 214 text_display_state_t *data; 215 216 assert( display_datap != NULL ); 217 data = calloc( 1, sizeof( text_display_state_t )); 218 assert( data != NULL ); 219 data->buffer_length = default_buffer_length; 220 data->buffer = calloc( 1, default_buffer_length ); 221 assert( data->buffer != NULL ); 222 data->output = stderr; 223 *display_datap = data; 224 return 0; 225 } 226 227 static int 228 text_update ( 229 void **display_datap, 230 double frac, 231 time_t remaining_time, 232 time_t elapsed, 233 double byte_rate) 234 { 235 text_display_state_t *data; 236 char *buf, *buf_end; 237 238 assert( display_datap != NULL ); 239 assert( *display_datap != NULL ); 240 data = (text_display_state_t*) *display_datap; 241 242 if ( data->output == NULL ) return 1; 243 244 buf = data->buffer; 245 buf_end = buf + data->buffer_length - 1; 246 247 /* |#################### 100.00% eta 1d19h elapsed 23w 7d23h15m12s spd nnnn.n M/s */ 248 249 { 250 /* spinner */ 251 static const int phase_mod = 8; 252 static const char phase_char[] = "_.-*\"*-."; 253 *buf++ = phase_char[data->phase % phase_mod]; 254 data->phase++; 255 } 256 257 { 258 /* bar */ 259 static const int bar_length = 20; 260 static const double bar_lengthd = 20.0; 261 static const char fill_char = '#'; 262 static const char blank_char = ' '; 263 char *bar_end = buf + bar_length; 264 char *bar_pos = frac < 0.0 ? 265 buf : 266 frac < 1.0 ? 267 buf + (int) (bar_lengthd * frac) : 268 bar_end; 269 270 assert( (buf_end - buf) > bar_length ); 271 while ( buf < bar_end ) { 272 *buf = buf < bar_pos ? 273 fill_char : blank_char; 274 ++buf; 275 } 276 } 277 278 { 279 /* percent */ 280 (void) snprintf( buf, buf_end-buf, "%7.2f%%", 100.0*frac ); 281 buf += 8; 282 } 283 284 { 285 /* eta and elapsed */ 286 char time_buffer[19]; 287 int rc; 288 rc = lutil_time_string( time_buffer, remaining_time, 2); 289 if (rc == 0) 290 snprintf( buf, buf_end-buf, " eta %6s", time_buffer ); 291 buf += 5+6; 292 rc = lutil_time_string( time_buffer, elapsed, 5); 293 if (rc == 0) 294 snprintf( buf, buf_end-buf, " elapsed %15s", 295 time_buffer ); 296 buf += 9+15; 297 } 298 299 { 300 /* speed */ 301 static const char prefixes[] = " kMGTPEZY"; 302 const char *prefix_chp = prefixes; 303 304 while (*prefix_chp && byte_rate >= 1024.0) { 305 byte_rate /= 1024.0; 306 ++prefix_chp; 307 } 308 if ( byte_rate >= 1024.0 ) { 309 snprintf( buf, buf_end-buf, " fast!" ); 310 buf += 6; 311 } else { 312 snprintf( buf, buf_end-buf, " spd %5.1f %c/s", 313 byte_rate, 314 *prefix_chp); 315 buf += 5+6+4; 316 } 317 } 318 319 (void) fprintf( data->output, 320 "\r%-79s", 321 data->buffer ); 322 data->need_eol = 1; 323 return 0; 324 } 325 326 static int 327 text_close (void ** display_datap) 328 { 329 text_display_state_t *data; 330 331 if (display_datap) { 332 if (*display_datap) { 333 data = (text_display_state_t*) *display_datap; 334 if (data->output && data->need_eol) 335 fputs ("\n", data->output); 336 if (data->buffer) 337 free( data->buffer ); 338 free( data ); 339 } 340 *display_datap = NULL; 341 } 342 return 0; 343 } 344 345 static int 346 null_open_close (void **datap) 347 { 348 assert( datap ); 349 *datap = NULL; 350 return 0; 351 } 352 353 static int 354 linear_update ( 355 void **estimator_datap, 356 double start, 357 double frac, 358 time_t *remaining) 359 { 360 double now; 361 double elapsed; 362 363 assert( estimator_datap != NULL ); 364 assert( *estimator_datap == NULL ); 365 assert( start > 0.0 ); 366 assert( frac >= 0.0 ); 367 assert( frac <= 1.0 ); 368 assert( remaining != NULL ); 369 lutil_get_now( &now ); 370 371 elapsed = now-start; 372 assert( elapsed >= 0.0 ); 373 374 if ( frac == 0.0 ) { 375 return 1; 376 } else if ( frac >= 1.0 ) { 377 *remaining = 0; 378 return 0; 379 } else { 380 *remaining = (time_t) (elapsed/frac-elapsed+0.5); 381 return 0; 382 } 383 } 384 385 const lutil_meter_display_t lutil_meter_text_display = { 386 text_open, text_update, text_close 387 }; 388 389 const lutil_meter_estimator_t lutil_meter_linear_estimator = { 390 null_open_close, linear_update, null_open_close 391 }; 392