Notcurses 3.0.13
a blingful library for TUIs and character graphics
Loading...
Searching...
No Matches
metric.c
Go to the documentation of this file.
1#include <fenv.h>
2#include <string.h>
3#include <locale.h>
4#include <pthread.h>
5#include <inttypes.h>
7#include "internal.h"
8
9static const wchar_t UTF8_SUBPREFIX[] = L"mµnpfazy"; // 10^24-1
10static const wchar_t ASCII_SUBPREFIX[] = L"munpfazy"; // 10^24-1
11
12// we want to use UTF8_SUBPREFIX if we have utf8 available to us. we could
13// pull this out of const struct notcurses*, except these ncnmetric() doesn't
14// take one, and we don't want to break the API. instead, we call this from
15// notcurses_init() when we create a utf8 context. a gross hack =\.
16static pthread_once_t utf8_verdict = PTHREAD_ONCE_INIT;
17static const wchar_t* SUBPREFIXES = ASCII_SUBPREFIX;
18
19static void
20ncmetric_use_utf8_internal(void){
21 SUBPREFIXES = UTF8_SUBPREFIX;
22}
23
25 pthread_once(&utf8_verdict, ncmetric_use_utf8_internal);
26}
27
28const char* ncnmetric(uintmax_t val, size_t s, uintmax_t decimal,
29 char* buf, int omitdec, uintmax_t mult,
30 int uprefix){
31 fesetround(FE_TONEAREST); // FIXME global to the process...ick :/
32 // these two must have the same number of elements
33 const wchar_t* subprefixes = SUBPREFIXES;
34 const wchar_t prefixes[] = L"KMGTPEZY"; // 10^21-1 encompasses 2^64-1
35 if(decimal == 0 || mult == 0){
36 return NULL;
37 }
38 if(decimal > UINTMAX_MAX / 10){
39 return NULL;
40 }
41 unsigned consumed = 0;
42 uintmax_t dv = mult;
43 if(decimal <= val || val == 0){
44 // FIXME verify that input < 2^89
45 while((val / decimal) >= dv && consumed < sizeof(prefixes) / sizeof(*prefixes)){
46 dv *= mult;
47 ++consumed;
48 if(UINTMAX_MAX / dv < mult){ // near overflow--can't scale dv again
49 break;
50 }
51 }
52 }else{
53 while(val < decimal && consumed < sizeof(prefixes) / sizeof(*prefixes)){
54 val *= mult;
55 ++consumed;
56 if(UINTMAX_MAX / dv < mult){ // near overflow--can't scale dv again
57 break;
58 }
59 }
60 }
61 int sprintfed;
62 if(dv != mult){ // if consumed == 0, dv must equal mult
63 if((val / decimal) / dv > 0){
64 ++consumed;
65 }else{
66 dv /= mult;
67 }
68 val /= decimal;
69 // Remainder is val % dv, but we want a percentage as scaled integer.
70 // Ideally we would multiply by 100 and divide the result by dv, for
71 // maximum accuracy (dv might not be a multiple of 10--it is not for
72 // 1,024). That can overflow with large 64-bit values, but we can first
73 // divide both sides by mult, and then scale by 100.
74 if(omitdec && (val % dv) == 0){
75 sprintfed = snprintf(buf, s, "%" PRIu64 "%lc", (uint64_t)(val / dv),
76 (wint_t)prefixes[consumed - 1]);
77 }else{
78 sprintfed = snprintf(buf, s, "%.2f%lc", (double)val / dv,
79 (wint_t)prefixes[consumed - 1]);
80 }
81 if(sprintfed < 0){
82 return NULL;
83 }
84 if(uprefix){
85 if((size_t)sprintfed < s){
86 buf[sprintfed] = uprefix;
87 buf[++sprintfed] = '\0';
88 }
89 }
90 return buf;
91 }
92 // unscaled output, consumed == 0, dv == mult
93 // val / decimal < dv (or we ran out of prefixes)
94 if(omitdec && val % decimal == 0){
95 if(consumed){
96 sprintfed = snprintf(buf, s, "%" PRIu64 "%lc", (uint64_t)(val / decimal),
97 (wint_t)subprefixes[consumed - 1]);
98 }else{
99 sprintfed = snprintf(buf, s, "%" PRIu64, (uint64_t)(val / decimal));
100 }
101 }else{
102 if(consumed){
103 sprintfed = snprintf(buf, s, "%.2f%lc", (double)val / decimal,
104 (wint_t)subprefixes[consumed - 1]);
105 }else{
106 sprintfed = snprintf(buf, s, "%.2f", (double)val / decimal);
107 }
108 }
109 if(sprintfed < 0){
110 return NULL;
111 }
112 if(consumed && uprefix){
113 if((size_t)sprintfed < s){
114 buf[sprintfed] = uprefix;
115 buf[++sprintfed] = '\0';
116 }
117 }
118 return buf;
119}
void ncmetric_use_utf8(void)
Definition metric.c:24
const char * ncnmetric(uintmax_t val, size_t s, uintmax_t decimal, char *buf, int omitdec, uintmax_t mult, int uprefix)
Definition metric.c:28
return NULL
Definition termdesc.h:229