1 | #include "jemalloc/internal/jemalloc_preamble.h" |
2 | #include "jemalloc/internal/jemalloc_internal_includes.h" |
3 | |
4 | #include "jemalloc/internal/fxp.h" |
5 | |
6 | static bool |
7 | fxp_isdigit(char c) { |
8 | return '0' <= c && c <= '9'; |
9 | } |
10 | |
11 | bool |
12 | fxp_parse(fxp_t *result, const char *str, char **end) { |
13 | /* |
14 | * Using malloc_strtoumax in this method isn't as handy as you might |
15 | * expect (I tried). In the fractional part, significant leading zeros |
16 | * mean that you still need to do your own parsing, now with trickier |
17 | * math. In the integer part, the casting (uintmax_t to uint32_t) |
18 | * forces more reasoning about bounds than just checking for overflow as |
19 | * we parse. |
20 | */ |
21 | uint32_t integer_part = 0; |
22 | |
23 | const char *cur = str; |
24 | |
25 | /* The string must start with a digit or a decimal point. */ |
26 | if (*cur != '.' && !fxp_isdigit(*cur)) { |
27 | return true; |
28 | } |
29 | |
30 | while ('0' <= *cur && *cur <= '9') { |
31 | integer_part *= 10; |
32 | integer_part += *cur - '0'; |
33 | if (integer_part >= (1U << 16)) { |
34 | return true; |
35 | } |
36 | cur++; |
37 | } |
38 | |
39 | /* |
40 | * We've parsed all digits at the beginning of the string, without |
41 | * overflow. Either we're done, or there's a fractional part. |
42 | */ |
43 | if (*cur != '.') { |
44 | *result = (integer_part << 16); |
45 | if (end != NULL) { |
46 | *end = (char *)cur; |
47 | } |
48 | return false; |
49 | } |
50 | |
51 | /* There's a fractional part. */ |
52 | cur++; |
53 | if (!fxp_isdigit(*cur)) { |
54 | /* Shouldn't end on the decimal point. */ |
55 | return true; |
56 | } |
57 | |
58 | /* |
59 | * We use a lot of precision for the fractional part, even though we'll |
60 | * discard most of it; this lets us get exact values for the important |
61 | * special case where the denominator is a small power of 2 (for |
62 | * instance, 1/512 == 0.001953125 is exactly representable even with |
63 | * only 16 bits of fractional precision). We need to left-shift by 16 |
64 | * before dividing so we pick the number of digits to be |
65 | * floor(log(2**48)) = 14. |
66 | */ |
67 | uint64_t fractional_part = 0; |
68 | uint64_t frac_div = 1; |
69 | for (int i = 0; i < FXP_FRACTIONAL_PART_DIGITS; i++) { |
70 | fractional_part *= 10; |
71 | frac_div *= 10; |
72 | if (fxp_isdigit(*cur)) { |
73 | fractional_part += *cur - '0'; |
74 | cur++; |
75 | } |
76 | } |
77 | /* |
78 | * We only parse the first maxdigits characters, but we can still ignore |
79 | * any digits after that. |
80 | */ |
81 | while (fxp_isdigit(*cur)) { |
82 | cur++; |
83 | } |
84 | |
85 | assert(fractional_part < frac_div); |
86 | uint32_t fractional_repr = (uint32_t)( |
87 | (fractional_part << 16) / frac_div); |
88 | |
89 | /* Success! */ |
90 | *result = (integer_part << 16) + fractional_repr; |
91 | if (end != NULL) { |
92 | *end = (char *)cur; |
93 | } |
94 | return false; |
95 | } |
96 | |
97 | void |
98 | fxp_print(fxp_t a, char buf[FXP_BUF_SIZE]) { |
99 | uint32_t integer_part = fxp_round_down(a); |
100 | uint32_t fractional_part = (a & ((1U << 16) - 1)); |
101 | |
102 | int leading_fraction_zeros = 0; |
103 | uint64_t fraction_digits = fractional_part; |
104 | for (int i = 0; i < FXP_FRACTIONAL_PART_DIGITS; i++) { |
105 | if (fraction_digits < (1U << 16) |
106 | && fraction_digits * 10 >= (1U << 16)) { |
107 | leading_fraction_zeros = i; |
108 | } |
109 | fraction_digits *= 10; |
110 | } |
111 | fraction_digits >>= 16; |
112 | while (fraction_digits > 0 && fraction_digits % 10 == 0) { |
113 | fraction_digits /= 10; |
114 | } |
115 | |
116 | size_t printed = malloc_snprintf(buf, FXP_BUF_SIZE, "%" FMTu32"." , |
117 | integer_part); |
118 | for (int i = 0; i < leading_fraction_zeros; i++) { |
119 | buf[printed] = '0'; |
120 | printed++; |
121 | } |
122 | malloc_snprintf(&buf[printed], FXP_BUF_SIZE - printed, "%" FMTu64, |
123 | fraction_digits); |
124 | } |
125 | |