xref: /netbsd-src/external/bsd/openldap/dist/libraries/liblutil/meter.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1*549b59edSchristos /*	$NetBSD: meter.c,v 1.2 2021/08/14 16:14:58 christos Exp $	*/
24e6df137Slukem 
34e6df137Slukem /* meter.c - lutil_meter meters */
4d11b170bStron /* $OpenLDAP$ */
54e6df137Slukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
64e6df137Slukem  *
7d11b170bStron  * Copyright (c) 2009 by Emily Backes, Symas Corp.
84e6df137Slukem  * All rights reserved.
94e6df137Slukem  *
104e6df137Slukem  * Redistribution and use in source and binary forms, with or without
114e6df137Slukem  * modification, are permitted only as authorized by the OpenLDAP
124e6df137Slukem  * Public License.
134e6df137Slukem  *
144e6df137Slukem  * A copy of this license is available in the file LICENSE in the
154e6df137Slukem  * top-level directory of the distribution or, alternatively, at
164e6df137Slukem  * <http://www.OpenLDAP.org/license.html>.
174e6df137Slukem  */
184e6df137Slukem /* ACKNOWLEDGEMENTS:
19d11b170bStron  * This work was initially developed by Emily Backes for inclusion
204e6df137Slukem  * in OpenLDAP software.
214e6df137Slukem  */
224e6df137Slukem 
23376af7d7Schristos #include <sys/cdefs.h>
24*549b59edSchristos __RCSID("$NetBSD: meter.c,v 1.2 2021/08/14 16:14:58 christos Exp $");
25376af7d7Schristos 
264e6df137Slukem #include "portable.h"
274e6df137Slukem #include "lutil_meter.h"
284e6df137Slukem 
294e6df137Slukem #include <ac/assert.h>
304e6df137Slukem #include <ac/string.h>
314e6df137Slukem 
324e6df137Slukem int
lutil_time_string(char * dest,int duration,int max_terms)334e6df137Slukem lutil_time_string (
344e6df137Slukem 	char *dest,
354e6df137Slukem 	int duration,
364e6df137Slukem 	int max_terms)
374e6df137Slukem {
384e6df137Slukem 	static const int time_div[] = {31556952,
394e6df137Slukem 				       604800,
404e6df137Slukem 				       86400,
414e6df137Slukem 				       3600,
424e6df137Slukem 				       60,
434e6df137Slukem 				       1,
444e6df137Slukem 				       0};
454e6df137Slukem 	const int * time_divp = time_div;
464e6df137Slukem 	static const char * time_name_ch = "ywdhms";
474e6df137Slukem 	const char * time_name_chp = time_name_ch;
484e6df137Slukem 	int term_count = 0;
494e6df137Slukem 	char *buf = dest;
504e6df137Slukem 	int time_quot;
514e6df137Slukem 
524e6df137Slukem 	assert ( max_terms >= 2 ); /* room for "none" message */
534e6df137Slukem 
544e6df137Slukem 	if ( duration < 0 ) {
554e6df137Slukem 		*dest = '\0';
564e6df137Slukem 		return 1;
574e6df137Slukem 	}
584e6df137Slukem 	if ( duration == 0 ) {
594e6df137Slukem 		strcpy( dest, "none" );
604e6df137Slukem 		return 0;
614e6df137Slukem 	}
624e6df137Slukem 	while ( term_count < max_terms && duration > 0 ) {
634e6df137Slukem 		if (duration > *time_divp) {
644e6df137Slukem 			time_quot = duration / *time_divp;
654e6df137Slukem 			duration %= *time_divp;
664e6df137Slukem 			if (time_quot > 99) {
674e6df137Slukem 				return 1;
684e6df137Slukem 			} else {
694e6df137Slukem 				*(buf++) = time_quot / 10 + '0';
704e6df137Slukem 				*(buf++) = time_quot % 10 + '0';
714e6df137Slukem 				*(buf++) = *time_name_chp;
724e6df137Slukem 				++term_count;
734e6df137Slukem 			}
744e6df137Slukem 		}
754e6df137Slukem 		if ( *(++time_divp) == 0) duration = 0;
764e6df137Slukem 		++time_name_chp;
774e6df137Slukem 	}
784e6df137Slukem 	*buf = '\0';
794e6df137Slukem 	return 0;
804e6df137Slukem }
814e6df137Slukem 
824e6df137Slukem int
lutil_get_now(double * now)834e6df137Slukem lutil_get_now (double *now)
844e6df137Slukem {
854e6df137Slukem #ifdef HAVE_GETTIMEOFDAY
864e6df137Slukem 	struct timeval tv;
874e6df137Slukem 
884e6df137Slukem 	assert( now );
894e6df137Slukem 	gettimeofday( &tv, NULL );
904e6df137Slukem 	*now = ((double) tv.tv_sec) + (((double) tv.tv_usec) / 1000000.0);
914e6df137Slukem 	return 0;
924e6df137Slukem #else
934e6df137Slukem 	time_t tm;
944e6df137Slukem 
954e6df137Slukem 	assert( now );
964e6df137Slukem 	time( &tm );
97d11b170bStron 	*now = (double) tm;
984e6df137Slukem 	return 0;
994e6df137Slukem #endif
1004e6df137Slukem }
1014e6df137Slukem 
1024e6df137Slukem int
lutil_meter_open(lutil_meter_t * meter,const lutil_meter_display_t * display,const lutil_meter_estimator_t * estimator,size_t goal_value)1034e6df137Slukem lutil_meter_open (
1044e6df137Slukem 	lutil_meter_t *meter,
1054e6df137Slukem 	const lutil_meter_display_t *display,
1064e6df137Slukem 	const lutil_meter_estimator_t *estimator,
107376af7d7Schristos 	size_t goal_value)
1084e6df137Slukem {
1094e6df137Slukem 	int rc;
1104e6df137Slukem 
1114e6df137Slukem 	assert( meter != NULL );
1124e6df137Slukem 	assert( display != NULL );
1134e6df137Slukem 	assert( estimator != NULL );
1144e6df137Slukem 
1154e6df137Slukem 	if (goal_value < 1) return -1;
1164e6df137Slukem 
1174e6df137Slukem 	memset( (void*) meter, 0, sizeof( lutil_meter_t ));
1184e6df137Slukem 	meter->display = display;
1194e6df137Slukem 	meter->estimator = estimator;
1204e6df137Slukem 	lutil_get_now( &meter->start_time );
1214e6df137Slukem 	meter->last_update = meter->start_time;
1224e6df137Slukem 	meter->goal_value = goal_value;
1234e6df137Slukem 	meter->last_position = 0;
1244e6df137Slukem 
1254e6df137Slukem 	rc = meter->display->display_open( &meter->display_data );
1264e6df137Slukem 	if( rc != 0 ) return rc;
1274e6df137Slukem 
1284e6df137Slukem 	rc = meter->estimator->estimator_open( &meter->estimator_data );
1294e6df137Slukem 	if( rc != 0 ) {
1304e6df137Slukem 		meter->display->display_close( &meter->display_data );
1314e6df137Slukem 		return rc;
1324e6df137Slukem 	}
1334e6df137Slukem 
1344e6df137Slukem 	return 0;
1354e6df137Slukem }
1364e6df137Slukem 
1374e6df137Slukem int
lutil_meter_update(lutil_meter_t * meter,size_t position,int force)1384e6df137Slukem lutil_meter_update (
1394e6df137Slukem 	lutil_meter_t *meter,
140376af7d7Schristos 	size_t position,
1414e6df137Slukem 	int force)
1424e6df137Slukem {
1434e6df137Slukem 	static const double display_rate = 0.5;
1444e6df137Slukem 	double frac, cycle_length, speed, now;
1454e6df137Slukem 	time_t remaining_time, elapsed;
1464e6df137Slukem 	int rc;
1474e6df137Slukem 
1484e6df137Slukem 	assert( meter != NULL );
1494e6df137Slukem 
1504e6df137Slukem 	lutil_get_now( &now );
1514e6df137Slukem 
1524e6df137Slukem 	if ( !force && now - meter->last_update < display_rate ) return 0;
1534e6df137Slukem 
1544e6df137Slukem 	frac = ((double)position) / ((double) meter->goal_value);
1554e6df137Slukem 	elapsed = now - meter->start_time;
156e670fd5cSchristos 	if (frac <= 0.0 || elapsed == 0) return 0;
1574e6df137Slukem 	if (frac >= 1.0) {
1584e6df137Slukem 		rc = meter->display->display_update(
1594e6df137Slukem 			&meter->display_data,
1604e6df137Slukem 			1.0,
1614e6df137Slukem 			0,
1624e6df137Slukem 			(time_t) elapsed,
1634e6df137Slukem 			((double)position) / elapsed);
1644e6df137Slukem 	} else {
1654e6df137Slukem 		rc = meter->estimator->estimator_update(
1664e6df137Slukem 			&meter->estimator_data,
1674e6df137Slukem 			meter->start_time,
1684e6df137Slukem 			frac,
1694e6df137Slukem 			&remaining_time );
1704e6df137Slukem 		if ( rc == 0 ) {
1714e6df137Slukem 			cycle_length = now - meter->last_update;
1724e6df137Slukem 			speed = cycle_length > 0.0 ?
1734e6df137Slukem 				((double)(position - meter->last_position))
1744e6df137Slukem 				/ cycle_length :
1754e6df137Slukem 				0.0;
1764e6df137Slukem 			rc = meter->display->display_update(
1774e6df137Slukem 				&meter->display_data,
1784e6df137Slukem 				frac,
1794e6df137Slukem 				remaining_time,
1804e6df137Slukem 				(time_t) elapsed,
1814e6df137Slukem 				speed);
1824e6df137Slukem 			if ( rc == 0 ) {
1834e6df137Slukem 				meter->last_update = now;
1844e6df137Slukem 				meter->last_position = position;
1854e6df137Slukem 			}
1864e6df137Slukem 		}
1874e6df137Slukem 	}
1884e6df137Slukem 
1894e6df137Slukem 	return rc;
1904e6df137Slukem }
1914e6df137Slukem 
1924e6df137Slukem int
lutil_meter_close(lutil_meter_t * meter)1934e6df137Slukem lutil_meter_close (lutil_meter_t *meter)
1944e6df137Slukem {
1954e6df137Slukem 	meter->estimator->estimator_close( &meter->estimator_data );
1964e6df137Slukem 	meter->display->display_close( &meter->display_data );
1974e6df137Slukem 
1984e6df137Slukem 	return 0;
1994e6df137Slukem }
2004e6df137Slukem 
2014e6df137Slukem /* Default display and estimator */
2024e6df137Slukem typedef struct {
2034e6df137Slukem 	int buffer_length;
2044e6df137Slukem 	char * buffer;
2054e6df137Slukem 	int need_eol;
2064e6df137Slukem 	int phase;
2074e6df137Slukem 	FILE *output;
2084e6df137Slukem } text_display_state_t;
2094e6df137Slukem 
2104e6df137Slukem static int
text_open(void ** display_datap)2114e6df137Slukem text_open (void ** display_datap)
2124e6df137Slukem {
2134e6df137Slukem 	static const int default_buffer_length = 81;
2144e6df137Slukem 	text_display_state_t *data;
2154e6df137Slukem 
2164e6df137Slukem 	assert( display_datap != NULL );
2174e6df137Slukem 	data = calloc( 1, sizeof( text_display_state_t ));
2184e6df137Slukem 	assert( data != NULL );
2194e6df137Slukem 	data->buffer_length = default_buffer_length;
2204e6df137Slukem 	data->buffer = calloc( 1, default_buffer_length );
2214e6df137Slukem 	assert( data->buffer != NULL );
2224e6df137Slukem 	data->output = stderr;
2234e6df137Slukem 	*display_datap = data;
2244e6df137Slukem 	return 0;
2254e6df137Slukem }
2264e6df137Slukem 
2274e6df137Slukem static int
text_update(void ** display_datap,double frac,time_t remaining_time,time_t elapsed,double byte_rate)2284e6df137Slukem text_update (
2294e6df137Slukem 	void **display_datap,
2304e6df137Slukem 	double frac,
2314e6df137Slukem 	time_t remaining_time,
2324e6df137Slukem 	time_t elapsed,
2334e6df137Slukem 	double byte_rate)
2344e6df137Slukem {
2354e6df137Slukem 	text_display_state_t *data;
2364e6df137Slukem 	char *buf, *buf_end;
2374e6df137Slukem 
2384e6df137Slukem 	assert( display_datap != NULL );
2394e6df137Slukem 	assert( *display_datap != NULL );
2404e6df137Slukem 	data = (text_display_state_t*) *display_datap;
2414e6df137Slukem 
2424e6df137Slukem 	if ( data->output == NULL ) return 1;
2434e6df137Slukem 
2444e6df137Slukem 	buf = data->buffer;
2454e6df137Slukem 	buf_end = buf + data->buffer_length - 1;
2464e6df137Slukem 
2474e6df137Slukem /* |#################### 100.00% eta  1d19h elapsed 23w 7d23h15m12s spd nnnn.n M/s */
2484e6df137Slukem 
2494e6df137Slukem 	{
2504e6df137Slukem 		/* spinner */
2514e6df137Slukem 		static const int phase_mod = 8;
2524e6df137Slukem 		static const char phase_char[] = "_.-*\"*-.";
2534e6df137Slukem 		*buf++ = phase_char[data->phase % phase_mod];
2544e6df137Slukem 		data->phase++;
2554e6df137Slukem 	}
2564e6df137Slukem 
2574e6df137Slukem 	{
2584e6df137Slukem 		/* bar */
2594e6df137Slukem 		static const int bar_length = 20;
2604e6df137Slukem 		static const double bar_lengthd = 20.0;
2614e6df137Slukem 		static const char fill_char = '#';
2624e6df137Slukem 		static const char blank_char = ' ';
2634e6df137Slukem 		char *bar_end = buf + bar_length;
2644e6df137Slukem 		char *bar_pos = frac < 0.0 ?
2654e6df137Slukem 			buf :
2664e6df137Slukem 			frac < 1.0 ?
2674e6df137Slukem 			buf + (int) (bar_lengthd * frac) :
2684e6df137Slukem 			bar_end;
2694e6df137Slukem 
2704e6df137Slukem 		assert( (buf_end - buf) > bar_length );
2714e6df137Slukem 		while ( buf < bar_end ) {
2724e6df137Slukem 			*buf = buf < bar_pos ?
2734e6df137Slukem 				fill_char : blank_char;
2744e6df137Slukem 			++buf;
2754e6df137Slukem 		}
2764e6df137Slukem 	}
2774e6df137Slukem 
2784e6df137Slukem 	{
2794e6df137Slukem 		/* percent */
2804e6df137Slukem 		(void) snprintf( buf, buf_end-buf, "%7.2f%%", 100.0*frac );
2814e6df137Slukem 		buf += 8;
2824e6df137Slukem 	}
2834e6df137Slukem 
2844e6df137Slukem 	{
2854e6df137Slukem 		/* eta and elapsed */
2864e6df137Slukem 		char time_buffer[19];
2874e6df137Slukem 		int rc;
2884e6df137Slukem 		rc = lutil_time_string( time_buffer, remaining_time, 2);
2894e6df137Slukem 		if (rc == 0)
2904e6df137Slukem 			snprintf( buf, buf_end-buf, " eta %6s", time_buffer );
2914e6df137Slukem 		buf += 5+6;
2924e6df137Slukem 		rc = lutil_time_string( time_buffer, elapsed, 5);
2934e6df137Slukem 		if (rc == 0)
2944e6df137Slukem 			snprintf( buf, buf_end-buf, " elapsed %15s",
2954e6df137Slukem 				  time_buffer );
2964e6df137Slukem 		buf += 9+15;
2974e6df137Slukem 	}
2984e6df137Slukem 
2994e6df137Slukem 	{
3004e6df137Slukem 		/* speed */
3014e6df137Slukem 		static const char prefixes[] = " kMGTPEZY";
3024e6df137Slukem 		const char *prefix_chp = prefixes;
3034e6df137Slukem 
3044e6df137Slukem 		while (*prefix_chp && byte_rate >= 1024.0) {
3054e6df137Slukem 			byte_rate /= 1024.0;
3064e6df137Slukem 			++prefix_chp;
3074e6df137Slukem 		}
3084e6df137Slukem 		if ( byte_rate >= 1024.0 ) {
3094e6df137Slukem 			snprintf( buf, buf_end-buf, " fast!" );
3104e6df137Slukem 			buf += 6;
3114e6df137Slukem 		} else {
3124e6df137Slukem 			snprintf( buf, buf_end-buf, " spd %5.1f %c/s",
3134e6df137Slukem 				  byte_rate,
3144e6df137Slukem 				  *prefix_chp);
3154e6df137Slukem 			buf += 5+6+4;
3164e6df137Slukem 		}
3174e6df137Slukem 	}
3184e6df137Slukem 
3194e6df137Slukem 	(void) fprintf( data->output,
3204e6df137Slukem 			"\r%-79s",
3214e6df137Slukem 			data->buffer );
3224e6df137Slukem 	data->need_eol = 1;
3234e6df137Slukem 	return 0;
3244e6df137Slukem }
3254e6df137Slukem 
3264e6df137Slukem static int
text_close(void ** display_datap)3274e6df137Slukem text_close (void ** display_datap)
3284e6df137Slukem {
3294e6df137Slukem 	text_display_state_t *data;
3304e6df137Slukem 
3314e6df137Slukem 	if (display_datap) {
3324e6df137Slukem 		if (*display_datap) {
3334e6df137Slukem 			data = (text_display_state_t*) *display_datap;
3344e6df137Slukem 			if (data->output && data->need_eol)
3354e6df137Slukem 				fputs ("\n", data->output);
3364e6df137Slukem 			if (data->buffer)
3374e6df137Slukem 				free( data->buffer );
3384e6df137Slukem 			free( data );
3394e6df137Slukem 		}
3404e6df137Slukem 		*display_datap = NULL;
3414e6df137Slukem 	}
3424e6df137Slukem 	return 0;
3434e6df137Slukem }
3444e6df137Slukem 
3454e6df137Slukem static int
null_open_close(void ** datap)3464e6df137Slukem null_open_close (void **datap)
3474e6df137Slukem {
3484e6df137Slukem 	assert( datap );
3494e6df137Slukem 	*datap = NULL;
3504e6df137Slukem 	return 0;
3514e6df137Slukem }
3524e6df137Slukem 
3534e6df137Slukem static int
linear_update(void ** estimator_datap,double start,double frac,time_t * remaining)3544e6df137Slukem linear_update (
3554e6df137Slukem 	void **estimator_datap,
3564e6df137Slukem 	double start,
3574e6df137Slukem 	double frac,
3584e6df137Slukem 	time_t *remaining)
3594e6df137Slukem {
3604e6df137Slukem 	double now;
3614e6df137Slukem 	double elapsed;
3624e6df137Slukem 
3634e6df137Slukem 	assert( estimator_datap != NULL );
3644e6df137Slukem 	assert( *estimator_datap == NULL );
3654e6df137Slukem 	assert( start > 0.0 );
3664e6df137Slukem 	assert( frac >= 0.0 );
3674e6df137Slukem 	assert( frac <= 1.0 );
3684e6df137Slukem 	assert( remaining != NULL );
3694e6df137Slukem 	lutil_get_now( &now );
3704e6df137Slukem 
3714e6df137Slukem 	elapsed = now-start;
3724e6df137Slukem 	assert( elapsed >= 0.0 );
3734e6df137Slukem 
3744e6df137Slukem 	if ( frac == 0.0 ) {
3754e6df137Slukem 		return 1;
3764e6df137Slukem 	} else if ( frac >= 1.0 ) {
3774e6df137Slukem 		*remaining = 0;
3784e6df137Slukem 		return 0;
3794e6df137Slukem 	} else {
3804e6df137Slukem 		*remaining = (time_t) (elapsed/frac-elapsed+0.5);
3814e6df137Slukem 		return 0;
3824e6df137Slukem 	}
3834e6df137Slukem }
3844e6df137Slukem 
3854e6df137Slukem const lutil_meter_display_t lutil_meter_text_display = {
3864e6df137Slukem 	text_open, text_update, text_close
3874e6df137Slukem };
3884e6df137Slukem 
3894e6df137Slukem const lutil_meter_estimator_t lutil_meter_linear_estimator = {
3904e6df137Slukem 	null_open_close, linear_update, null_open_close
3914e6df137Slukem };
392