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