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