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