]> git.sur5r.net Git - openldap/blob - libraries/liblutil/meter.c
0bbbdc03078b4fcc605cfe838ea42c0d7b7e16cf
[openldap] / libraries / liblutil / meter.c
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
27 int
28 lutil_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
77 int
78 lutil_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
97 int
98 lutil_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
132 int
133 lutil_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
187 int
188 lutil_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 */
197 typedef struct {
198         int buffer_length;
199         char * buffer;
200         int need_eol;
201         int phase;
202         FILE *output;
203 } text_display_state_t;
204
205 static int
206 text_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
222 static int
223 text_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
321 static int
322 text_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
340 static int
341 null_open_close (void **datap)
342 {
343         assert( datap );
344         *datap = NULL;
345         return 0;
346 }
347
348 static int
349 linear_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
380 const lutil_meter_display_t lutil_meter_text_display = {
381         text_open, text_update, text_close
382 };
383
384 const lutil_meter_estimator_t lutil_meter_linear_estimator = {
385         null_open_close, linear_update, null_open_close
386 };