Branch data Line data Source code
1 : : // Copyright 2019-2026 David Robillard <d@drobilla.net>
2 : : // SPDX-License-Identifier: ISC
3 : :
4 : : #include "year.h"
5 : : #include "int_math.h"
6 : : #include "number_utils.h"
7 : : #include "read_utils.h"
8 : : #include "result.h"
9 : : #include "timezone.h"
10 : : #include "write_utils.h"
11 : :
12 : : #include <exess/exess.h>
13 : :
14 : : #include <stdbool.h>
15 : : #include <stdint.h>
16 : : #include <stdlib.h>
17 : : #include <string.h>
18 : :
19 : : EXESS_NONBLOCKING ExessResult
20 : 422 : read_year_number(int16_t* const out, const char* const str)
21 : : {
22 : 422 : *out = 0;
23 : :
24 : : // Read leading sign if present
25 : 422 : size_t i = 0;
26 : 422 : int sign = 1;
27 [ + + ]: 422 : if (str[i] == '-') {
28 : 22 : sign = -1;
29 : 22 : ++i;
30 : : }
31 : :
32 : : // Read digits
33 : 422 : uint64_t magnitude = 0;
34 : 422 : const ExessResult r = read_digits(&magnitude, str + i);
35 : :
36 : 422 : i += r.count;
37 [ + + ]: 422 : if (r.status) {
38 : 11 : return RESULT(r.status, i + r.count);
39 : : }
40 : :
41 [ + + ]: 411 : if (sign > 0) {
42 [ + + ]: 389 : if (magnitude > (uint16_t)INT16_MAX) {
43 : 2 : return RESULT(EXESS_OUT_OF_RANGE, i);
44 : : }
45 : :
46 : 387 : *out = (int16_t)magnitude;
47 : : } else {
48 : 22 : const uint16_t min_magnitude = (uint16_t)(-(INT16_MIN + 1)) + 1;
49 [ + + ]: 22 : if (magnitude > min_magnitude) {
50 : 2 : return RESULT(EXESS_OUT_OF_RANGE, i);
51 : : }
52 : :
53 [ + + ]: 20 : if (magnitude == min_magnitude) {
54 : 5 : *out = INT16_MIN;
55 : : } else {
56 : 15 : *out = (int16_t)(-(int16_t)magnitude);
57 : : }
58 : : }
59 : :
60 [ + + ]: 407 : return RESULT(r.count >= 4 ? EXESS_SUCCESS : EXESS_EXPECTED_DIGIT, i);
61 : : }
62 : :
63 : : EXESS_NONBLOCKING ExessResult
64 : 184 : write_year_number(const int16_t value, const size_t buf_size, char* const buf)
65 : : {
66 : 184 : const uint32_t abs_year = (uint32_t)abs(value);
67 : 184 : const uint8_t n_digits = exess_num_digits(abs_year);
68 : 184 : const bool is_negative = value < 0;
69 : :
70 : : // Write sign
71 : 184 : size_t i = 0;
72 [ + + ]: 184 : if (is_negative) {
73 : 23 : i += write_char('-', buf_size, buf, i);
74 : : }
75 : :
76 : : // Write leading zeros to ensure we have at least 4 year digits
77 [ + + ]: 223 : for (size_t j = n_digits; j < 4; ++j) {
78 : 39 : i += write_char('0', buf_size, buf, i);
79 : : }
80 : :
81 : 184 : const ExessResult yr = write_digits(abs_year, buf_size, buf, i);
82 : 184 : return end_write(yr.status, buf_size, buf, i + yr.count);
83 : : }
84 : :
85 : : EXESS_NONBLOCKING ExessResult
86 : 20 : exess_read_year(const char* const str, ExessYear* const out)
87 : : {
88 : 20 : memset(out, 0, sizeof(*out));
89 : :
90 : : // Read YYYY number
91 : 20 : size_t i = skip_whitespace(str);
92 : 20 : ExessResult r = read_year_number(&out->year, str + i);
93 : :
94 : : // Read timezone if present
95 : 20 : r.count = i + r.count;
96 : 20 : return read_optional_timezone(&out->zone, str, r);
97 : : }
98 : :
99 : : EXESS_NONBLOCKING ExessResult
100 : 6 : exess_write_year(const ExessYear value, const size_t buf_size, char* const buf)
101 : : {
102 : 6 : const ExessResult r = write_year_number(value.year, buf_size, buf);
103 : :
104 : 6 : return write_timezone(value.zone, buf_size, buf, r);
105 : : }
|