Branch data Line data Source code
1 : : // Copyright 2019-2026 David Robillard <d@drobilla.net>
2 : : // SPDX-License-Identifier: ISC
3 : :
4 : : #include "big_decimal.h"
5 : : #include "char_utils.h"
6 : : #include "number_utils.h"
7 : : #include "read_utils.h"
8 : : #include "result.h"
9 : : #include "write_utils.h"
10 : :
11 : : #include <exess/exess.h>
12 : :
13 : : #include <assert.h>
14 : : #include <stddef.h>
15 : : #include <stdint.h>
16 : :
17 : : // Value has a BigDecimal header, char digits, and a null byte
18 : : static const size_t min_value_size = sizeof(BigDecimal) + 1U;
19 : :
20 : : EXESS_NONBLOCKING ExessVariableResult
21 : 4176 : exess_read_integer(const char* const str,
22 : : const size_t out_size,
23 : : void* const out)
24 : : {
25 [ + + ]: 4176 : if (out_size < min_value_size) {
26 : 1 : return VRESULT(EXESS_NO_SPACE, 0U, min_value_size);
27 : : }
28 : :
29 : 4175 : BigDecimal* const d = (BigDecimal*)out;
30 : 4175 : char* const digits = (char*)out + sizeof(*d);
31 : 4175 : const size_t digits_size = out_size - sizeof(*d) - 1U;
32 : :
33 : 4175 : d->kind = EXESS_NAN;
34 : 4175 : d->n_digits = 0U;
35 : 4175 : d->expt = 0;
36 : :
37 : : // Skip leading whitespace and read sign if present
38 : 4175 : size_t i = skip_whitespace(str);
39 : 4175 : int sign = 1;
40 : 4175 : i += read_sign(&sign, &str[i]);
41 : :
42 : : // Ensure the first character is a digit
43 [ + + ]: 4175 : if (!is_digit(str[i])) {
44 : 13 : return VRESULT(EXESS_EXPECTED_DIGIT, i, 0U);
45 : : }
46 : :
47 : : // Skip leading zeros
48 [ + + ]: 4183 : while (str[i] == '0') {
49 : 21 : ++i;
50 : : }
51 : :
52 : : // Read digits
53 : 4162 : unsigned n_trailing_zeros = 0U;
54 [ + + ]: 81616 : for (; is_digit(str[i]); ++i) {
55 [ + + ]: 77455 : if (d->n_digits == digits_size) {
56 : 1 : return VRESULT(EXESS_OUT_OF_RANGE, i, 0U);
57 : : }
58 : :
59 : 77454 : ++d->expt;
60 : 77454 : digits[d->n_digits++] = str[i];
61 [ + + ]: 77454 : n_trailing_zeros = (str[i] == '0') ? (n_trailing_zeros + 1U) : 0U;
62 : : }
63 : :
64 : : // Trim trailing zeros
65 [ - + ]: 4161 : assert(n_trailing_zeros <= d->n_digits);
66 : 4161 : d->n_digits = (uint8_t)(d->n_digits - n_trailing_zeros);
67 : :
68 : 4161 : digits[d->n_digits] = '\0';
69 : :
70 [ + + ]: 8305 : d->kind = !d->n_digits ? EXESS_POSITIVE_ZERO
71 [ + + ]: 4144 : : (sign > 0) ? EXESS_POSITIVE
72 : : : EXESS_NEGATIVE;
73 : :
74 : 4161 : return VRESULT(EXESS_SUCCESS, i, sizeof(*d) + d->n_digits + 1U);
75 : : }
76 : :
77 : : EXESS_NONBLOCKING ExessResult
78 : 4251 : exess_write_integer(const size_t value_size,
79 : : const void* const value,
80 : : const size_t buf_size,
81 : : char* const buf)
82 : : {
83 : 4251 : const BigDecimal decimal = *(const BigDecimal*)value;
84 : 4251 : const char* const digits = (const char*)value + sizeof(decimal);
85 : :
86 [ + + ]: 4251 : if (decimal.kind < EXESS_NEGATIVE_ZERO ||
87 [ + + ]: 4245 : value_size < sizeof(decimal) + decimal.n_digits ||
88 [ + + ]: 4244 : decimal.expt < (int)decimal.n_digits) {
89 : 11 : return end_write(EXESS_BAD_VALUE, buf_size, buf, 0);
90 : : }
91 : :
92 : 4240 : size_t i = 0U;
93 [ + + ]: 4240 : if (decimal.kind == EXESS_NEGATIVE) {
94 : 1034 : i += write_char('-', buf_size, buf, i);
95 : : }
96 : :
97 : 4240 : const unsigned n_extra_zeros =
98 : 4240 : ((unsigned)decimal.expt - decimal.n_digits) + !decimal.n_digits;
99 : :
100 : 4240 : const size_t n_total = i + decimal.n_digits + n_extra_zeros;
101 [ + + ]: 4240 : if (n_total >= buf_size) {
102 : 54 : return end_write(EXESS_NO_SPACE, buf_size, buf, n_total);
103 : : }
104 : :
105 : 4186 : i += write_string(decimal.n_digits, digits, buf_size, buf, i);
106 : 4186 : i += write_zeros(n_extra_zeros, buf_size, buf, i);
107 : :
108 : 4186 : return end_write(EXESS_SUCCESS, buf_size, buf, i);
109 : : }
|