Browse Source

Implement bfg_sprintf for use with custom BPRI* formatters

Timestamps:
- BPRItm: int timestamp_format, struct tm *, long usecs
- BPRIts: int timestamp_format, struct timeval *
- BPRItt: int timestamp_format, int64 time (cast from time_t)
- BPRIte: int timestamp_format, int elapsed_secs
Large numbers (eg, hashrates and data sizes):
- Integer precision: BPRInd: const char *measurement, enum h2bs_fmt, double hashrate, int unit
- Float   precision: BPRInf: ^
Percentage:
- BPRIpgo: double count, double other
- BPRIpgt: double count, double total
Misc:
- Temperature: BPRItp: double temperature
Luke Dashjr 12 years ago
parent
commit
faf8da56af
3 changed files with 241 additions and 12 deletions
  1. 10 11
      miner.c
  2. 207 1
      util.c
  3. 24 0
      util.h

+ 10 - 11
miner.c

@@ -2451,14 +2451,8 @@ void pick_unit(float hashrate, unsigned char *unit)
 }
 }
 #define hashrate_pick_unit(hashrate, unit)  pick_unit(hashrate, unit)
 #define hashrate_pick_unit(hashrate, unit)  pick_unit(hashrate, unit)
 
 
-enum h2bs_fmt {
-	H2B_NOUNIT,  // "xxx.x"
-	H2B_SHORT,   // "xxx.xMH/s"
-	H2B_SPACED,  // "xxx.x MH/s"
-};
 static const size_t h2bs_fmt_size[] = {6, 10, 11};
 static const size_t h2bs_fmt_size[] = {6, 10, 11};
 
 
-static
 char *format_unit(char *buf, bool floatprec, const char *measurement, enum h2bs_fmt fmt, float hashrate, signed char unitin)
 char *format_unit(char *buf, bool floatprec, const char *measurement, enum h2bs_fmt fmt, float hashrate, signed char unitin)
 {
 {
 	unsigned char prec, i, unit;
 	unsigned char prec, i, unit;
@@ -2538,13 +2532,16 @@ char *_multi_format_unit(char **buflist, bool floatprec, const char *measurement
 #define multi_format_unit(buf, floatprec, measurement, fmt, delim, count, ...)  _multi_format_unit((char *[]){buf}, floatprec, measurement, fmt, delim, count, (float[]){ __VA_ARGS__ }, false)
 #define multi_format_unit(buf, floatprec, measurement, fmt, delim, count, ...)  _multi_format_unit((char *[]){buf}, floatprec, measurement, fmt, delim, count, (float[]){ __VA_ARGS__ }, false)
 #define multi_format_unit_array(buflist, floatprec, measurement, fmt, count, ...)  (void)_multi_format_unit(buflist, floatprec, measurement, fmt, NULL, count, (float[]){ __VA_ARGS__ }, true)
 #define multi_format_unit_array(buflist, floatprec, measurement, fmt, count, ...)  (void)_multi_format_unit(buflist, floatprec, measurement, fmt, NULL, count, (float[]){ __VA_ARGS__ }, true)
 
 
-static const char *
-percentf2(double p, double t, char *buf)
+void percentf3(char * const buf, double p, const double t)
 {
 {
 	if (!p)
 	if (!p)
-		return "none";
+		strcpy(buf, "none");
+	else
 	if (t <= p)
 	if (t <= p)
-		return "100%";
+		strcpy(buf, "100%");
+	else
+	{
+
 	p /= t;
 	p /= t;
 	if (p < 0.01)
 	if (p < 0.01)
 		sprintf(buf, ".%02.0f%%", p * 10000);  // ".01%"
 		sprintf(buf, ".%02.0f%%", p * 10000);  // ".01%"
@@ -2553,8 +2550,10 @@ percentf2(double p, double t, char *buf)
 		sprintf(buf, "%.1f%%", p * 100);  // "9.1%"
 		sprintf(buf, "%.1f%%", p * 100);  // "9.1%"
 	else
 	else
 		sprintf(buf, "%3.0f%%", p * 100);  // " 99%"
 		sprintf(buf, "%3.0f%%", p * 100);  // " 99%"
-	return buf;
+
+	}
 }
 }
+#define percentf2(p, t, buf)  (percentf3(buf, p, t), buf)
 #define percentf(p, t, buf)  percentf2(p, p + t, buf)
 #define percentf(p, t, buf)  percentf2(p, p + t, buf)
 
 
 #ifdef HAVE_CURSES
 #ifdef HAVE_CURSES

+ 207 - 1
util.c

@@ -13,10 +13,10 @@
 
 
 #include "config.h"
 #include "config.h"
 
 
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <ctype.h>
 #include <ctype.h>
-#include <stdarg.h>
 #include <string.h>
 #include <string.h>
 #include <pthread.h>
 #include <pthread.h>
 #include <jansson.h>
 #include <jansson.h>
@@ -1363,6 +1363,212 @@ int format_temperature_sz(const int numsz, const bool unicode)
 }
 }
 
 
 
 
+int bfg_vsprintf(char * const buf, const char *fmt, va_list ap)
+{
+	char *s = buf;
+	size_t L;
+	int arg_d, arg_d_2;
+	void *arg_p;
+	long arg_ld;
+	int64_t arg_d64;
+	double arg_f, arg_f_2;
+	
+	for (const char *p = fmt; ; ++p)
+	{
+		switch (p[0])
+		{
+			case '\0':
+			case '\b':
+			case '%':
+				if ( (L = (p - fmt)) )
+				{
+					char fmtcp[L+1];
+					memcpy(fmtcp, fmt, L);
+					fmtcp[L] = '\0';
+					s += vsprintf(s, fmtcp, ap);
+					fmt += L;
+				}
+				if (!fmt[0])
+					return (s - buf);
+				if (fmt[0] == '\b')
+				{
+// BFGMiner custom formatting
+switch (fmt[1])
+{
+	case 1:  // format_tm
+		arg_d = va_arg(ap, int);
+		arg_p = va_arg(ap, void*);
+		arg_ld = va_arg(ap, long);
+		s += format_tm(s, arg_d, arg_p, arg_ld);
+		L = sizeof(BPRItm);
+		break;
+	case 2:  // format_timestamp
+		arg_d = va_arg(ap, int);
+		arg_p = va_arg(ap, void*);
+		s += format_timestamp(s, arg_d, arg_p);
+		L = sizeof(BPRIts);
+		break;
+	case 3:  // format_time_t
+		arg_d = va_arg(ap, int);
+		arg_d64 = va_arg(ap, int64_t);
+		s += format_time_t(s, arg_d, arg_d64);
+		L = sizeof(BPRItt);
+		break;
+	case 4:  // format_elapsed
+		arg_d = va_arg(ap, int);
+		arg_d_2 = va_arg(ap, int);
+		s += format_elapsed(s, arg_d, arg_d_2);
+		L = sizeof(BPRIte);
+		break;
+	case 5:  // format_unit (int-precision)
+	case 6:  // format_unit (float-precision)
+		arg_p = va_arg(ap, void*);
+		arg_d = va_arg(ap, int);
+		arg_f = va_arg(ap, double);
+		arg_d_2 = va_arg(ap, int);
+		format_unit(s, (fmt[1] == 6), arg_p, arg_d, arg_f, arg_d_2);
+		s += strlen(s);
+		L = sizeof(BPRInd);
+		break;
+	case 7:  // temperature
+		arg_f = va_arg(ap, double);
+		s += format_temperature(s, 0, false, true, arg_f);
+		L = sizeof(BPRItp);
+		break;
+	case 8:  // percentage, 2nd arg is OTHERS
+	case 9:  // percentage, 2nd arg is TOTAL
+		arg_f = va_arg(ap, double);
+		arg_f_2 = va_arg(ap, double);
+		if (fmt[1] == 8)
+			arg_f_2 += arg_f;
+		percentf3(s, arg_f, arg_f_2);
+		s += 4;
+		L = sizeof(BPRIpgo);
+		break;
+}
+					fmt += (L - 1);
+					p = fmt - 1;
+					break;
+				}
+				
+				// Pass anything else through vsprintf above
+		}
+	}
+}
+
+int bfg_sprintf(char * const buf, const char * const fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	int rv = bfg_vsprintf(buf, fmt, ap);
+	va_end(ap);
+	return rv;
+}
+
+static
+void _test_bfg_sprintf(const char * const testname, const char * const fmt, const char * const expected, ...)
+{
+	char buf[0x100];
+	int len = strlen(expected), rv;
+	
+	va_list ap;
+	va_start(ap, expected);
+	rv = bfg_vsprintf(buf, fmt, ap);
+	va_end(ap);
+	
+	if (len != rv || memcmp(buf, expected, len))
+		applog(LOG_WARNING, "bfg_sprintf test %s failed: returned %d bytes \"%s\"", testname, rv, buf);
+}
+#define _test_bfg_sprintf(fmt, ...)  _test_bfg_sprintf(#fmt, fmt, __VA_ARGS__)
+
+void test_bfg_sprintf()
+{
+	struct tm tm = {
+		.tm_year = 115,
+		.tm_mon = 5,
+		.tm_mday = 3,
+		.tm_hour = 13,
+		.tm_min = 44,
+		.tm_sec = 22,
+	};
+	struct timeval tv;
+	time_t tt;
+	
+	_test_bfg_sprintf("abc", "abc");
+	
+	_test_bfg_sprintf("a%dbc", "a4bc", 4);
+	_test_bfg_sprintf("a%2dbc", "a 4bc", 4);
+	_test_bfg_sprintf("a%-2dbc", "a4 bc", 4);
+	_test_bfg_sprintf("a%02dbc1", "a04bc1", 4);
+	_test_bfg_sprintf("a%02dbc2", "a44bc2", 44);
+	
+	_test_bfg_sprintf("a%sbc", "axyzbc", "xyz");
+	
+	_test_bfg_sprintf("a%ubc", "a255bc", 255);
+	
+	_test_bfg_sprintf("a"BPRItm"bc1", "a[2015-06-03 13:44:22.000001]bc1", (BTF_BRACKETS | BTF_DATE | BTF_HRTIME), &tm, 1L);
+	_test_bfg_sprintf("a"BPRItm"bc2", "a[2015-06-03 13:44:22]bc2", (BTF_BRACKETS | BTF_DATE | BTF_TIME), &tm, 0L);
+	_test_bfg_sprintf("a"BPRItm"bc3", "a[2015-06-03 13:44]bc3", (BTF_BRACKETS | BTF_DATE | BTF_LRTIME), &tm, 0L);
+	_test_bfg_sprintf("a"BPRItm"bc4", "a[13:44:22.123456]bc4", (BTF_BRACKETS | BTF_HRTIME), &tm, 123456L);
+	_test_bfg_sprintf("a"BPRItm"bc5", "a13:44:22.999999bc5", (BTF_HRTIME), &tm, 999999L);
+	_test_bfg_sprintf("a"BPRItm"bc6", "a[2015-06-03]bc6", (BTF_BRACKETS | BTF_DATE), &tm, 0L);
+	_test_bfg_sprintf("a"BPRItm"bc7", "a2015-06-03bc7", (BTF_DATE), &tm, 0L);
+	
+	tt = mktime(&tm);
+	_test_bfg_sprintf("a"BPRItt"bc", "a[2015-06-03 13:44:22.000000]bc", (BTF_BRACKETS | BTF_DATE | BTF_HRTIME), (int64_t)tt);
+	tv = (struct timeval){
+		.tv_sec = tt,
+		.tv_usec = 987654,
+	};
+	_test_bfg_sprintf("a"BPRIts"bc", "a[2015-06-03 13:44:22.987654]bc", (BTF_BRACKETS | BTF_DATE | BTF_HRTIME), &tv);
+	
+	_test_bfg_sprintf("a"BPRIte"bc", "a[  1 day  05:04:03]bc", (BTF_BRACKETS | BTF_DATE | BTF_TIME), 104643);
+	_test_bfg_sprintf("a"BPRIte"bc", "a[  2 days 05:04:03]bc", (BTF_BRACKETS | BTF_DATE | BTF_TIME), 191043);
+	
+	_test_bfg_sprintf("a"BPRInd"bc1", "a  1bc1", "x/y", H2B_NOUNIT, 1., -1);
+	_test_bfg_sprintf("a"BPRInd"bc2", "a  1 x/ybc2", "x/y", H2B_SHORT, 1., -1);
+	_test_bfg_sprintf("a"BPRInd"bc3", "a  1  x/ybc3", "x/y", H2B_SPACED, 1., -1);
+	_test_bfg_sprintf("a"BPRInd"bc4", "a  1 x/ybc4", "x/y", H2B_SHORT, 1., 0);
+	_test_bfg_sprintf("a"BPRInd"bc5", "a  0kx/ybc5", "x/y", H2B_SHORT, 1., 1);
+	_test_bfg_sprintf("a"BPRInd"bc6", "a  0Mx/ybc6", "x/y", H2B_SHORT, 1., 2);
+	_test_bfg_sprintf("a"BPRInd"bc7", "a  0Gx/ybc7", "x/y", H2B_SHORT, 1., 3);
+	_test_bfg_sprintf("a"BPRInd"bc8", "a  0Tx/ybc8", "x/y", H2B_SHORT, 1., 4);
+	_test_bfg_sprintf("a"BPRInd"bc9", "a  1kx/ybc9", "x/y", H2B_SHORT, 1e3, -1);
+	_test_bfg_sprintf("a"BPRInd"bcA", "a  1Mx/ybcA", "x/y", H2B_SHORT, 1e6, -1);
+	_test_bfg_sprintf("a"BPRInd"bcB", "a  1Gx/ybcB", "x/y", H2B_SHORT, 1e9, -1);
+	_test_bfg_sprintf("a"BPRInd"bcC", "a  1Tx/ybcC", "x/y", H2B_SHORT, 1e12, -1);
+	
+	_test_bfg_sprintf("a"BPRInf"bc1", "a  1.0bc1", "x/y", H2B_NOUNIT, 1., -1);
+	_test_bfg_sprintf("a"BPRInf"bc2", "a  1.0 x/ybc2", "x/y", H2B_SHORT, 1., -1);
+	_test_bfg_sprintf("a"BPRInf"bc3", "a  1.0  x/ybc3", "x/y", H2B_SPACED, 1., -1);
+	_test_bfg_sprintf("a"BPRInf"bc4", "a  1.0 x/ybc4", "x/y", H2B_SHORT, 1., 0);
+	_test_bfg_sprintf("a"BPRInf"bc5", "a  0.0kx/ybc5", "x/y", H2B_SHORT, 1., 1);
+	_test_bfg_sprintf("a"BPRInf"bc6", "a 0.00Mx/ybc6", "x/y", H2B_SHORT, 1., 2);
+	_test_bfg_sprintf("a"BPRInf"bc7", "a 0.00Gx/ybc7", "x/y", H2B_SHORT, 1., 3);
+	_test_bfg_sprintf("a"BPRInf"bc8", "a 0.00Tx/ybc8", "x/y", H2B_SHORT, 1., 4);
+	_test_bfg_sprintf("a"BPRInf"bc9", "a  1.0kx/ybc9", "x/y", H2B_SHORT, 1e3, -1);
+	_test_bfg_sprintf("a"BPRInf"bcA", "a 1.00Mx/ybcA", "x/y", H2B_SHORT, 1e6, -1);
+	_test_bfg_sprintf("a"BPRInf"bcB", "a 1.00Gx/ybcB", "x/y", H2B_SHORT, 1e9, -1);
+	_test_bfg_sprintf("a"BPRInf"bcC", "a 1.00Tx/ybcC", "x/y", H2B_SHORT, 1e12, -1);
+	
+	_test_bfg_sprintf("a"BPRItp"bc", "a80\xb0""Cbc", 80.);
+	
+	_test_bfg_sprintf("a"BPRIpgo"bc1", "anonebc1",  0.,  40.);
+	_test_bfg_sprintf("a"BPRIpgo"bc2", "a100%bc2", 10.,   0.);
+	_test_bfg_sprintf("a"BPRIpgo"bc3", "a 20%bc3", 10.,  40.);
+	_test_bfg_sprintf("a"BPRIpgo"bc4", "a 80%bc4", 40.,  10.);
+	_test_bfg_sprintf("a"BPRIpgo"bc5", "a2.4%bc5",  1.,  40.);
+	_test_bfg_sprintf("a"BPRIpgo"bc6", "a.24%bc6",  1., 414.);
+	
+	_test_bfg_sprintf("a"BPRIpgt"bc1", "anonebc1",  0.,  40.);
+	_test_bfg_sprintf("a"BPRIpgt"bc2", "a100%bc2", 10.,  10.);
+	_test_bfg_sprintf("a"BPRIpgt"bc3", "a 25%bc3", 10.,  40.);
+	_test_bfg_sprintf("a"BPRIpgt"bc4", "a 40%bc4", 40., 100.);
+	_test_bfg_sprintf("a"BPRIpgt"bc5", "a2.5%bc5",  1.,  40.);
+	_test_bfg_sprintf("a"BPRIpgt"bc6", "a.24%bc6",  1., 414.);
+}
+
+
 bool extract_sockaddr(struct pool *pool, char *url)
 bool extract_sockaddr(struct pool *pool, char *url)
 {
 {
 	char *url_begin, *url_end, *ipv6_begin, *ipv6_end, *port_start = NULL;
 	char *url_begin, *url_end, *ipv6_begin, *ipv6_end, *port_start = NULL;

+ 24 - 0
util.h

@@ -340,9 +340,33 @@ struct timeval *select_timeout(struct timeval *tvp_timeout, struct timeval *tvp_
 }
 }
 
 
 
 
+enum h2bs_fmt {
+	H2B_NOUNIT,  // "xxx.x"
+	H2B_SHORT,   // "xxx.xMh/s"
+	H2B_SPACED,  // "xxx.x Mh/s"
+};
+
+extern char *format_unit(char *buf, bool floatprec, const char *measurement, enum h2bs_fmt fmt, float n, signed char unitin);
+extern void percentf3(char * const buf, double p, const double t);
 extern int format_temperature(char * const buf, const int pad, const bool highprecision, const bool unicode, const float temp);
 extern int format_temperature(char * const buf, const int pad, const bool highprecision, const bool unicode, const float temp);
 extern int format_temperature_sz(const int numsz, const bool unicode);
 extern int format_temperature_sz(const int numsz, const bool unicode);
 
 
+#define BPRItm "\b\x01%d%p%ld"
+#define BPRIts "\b\x02%d%p"
+#define BPRItt "\b\x03%d%"PRId64
+#define BPRIte "\b\x04%d%d"
+#define BPRInd "\b\x05%s%d%f%c"
+#define BPRInf "\b\x06%s%d%f%c"
+#define BPRItp "\b\x07%f"
+#define BPRIpgo "\b\x08%f%f"
+#define BPRIpgt "\b\x09%f%f"
+
+#ifdef va_arg
+extern int bfg_vsprintf(char * const buf, const char *fmt, va_list ap) FORMAT_SYNTAX_CHECK(printf, 2, 0);
+#endif
+extern int bfg_sprintf(char * const buf, const char * const fmt, ...) FORMAT_SYNTAX_CHECK(printf, 2, 3);
+extern void test_bfg_sprintf();
+
 
 
 #define RUNONCE(rv)  do {  \
 #define RUNONCE(rv)  do {  \
 	static bool _runonce = false;  \
 	static bool _runonce = false;  \