xref: /netbsd-src/external/bsd/openldap/dist/libraries/liblutil/meter.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: meter.c,v 1.2 2021/08/14 16:14:58 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.2 2021/08/14 16:14:58 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
lutil_time_string(char * dest,int duration,int max_terms)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
lutil_get_now(double * now)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
lutil_meter_open(lutil_meter_t * meter,const lutil_meter_display_t * display,const lutil_meter_estimator_t * estimator,size_t goal_value)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
lutil_meter_update(lutil_meter_t * meter,size_t position,int force)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 || elapsed == 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
lutil_meter_close(lutil_meter_t * meter)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
text_open(void ** display_datap)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
text_update(void ** display_datap,double frac,time_t remaining_time,time_t elapsed,double byte_rate)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
text_close(void ** display_datap)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
null_open_close(void ** datap)346 null_open_close (void **datap)
347 {
348 	assert( datap );
349 	*datap = NULL;
350 	return 0;
351 }
352 
353 static int
linear_update(void ** estimator_datap,double start,double frac,time_t * remaining)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