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 "decimal_to_double.h"
6 : : #include "double_to_decimal.h"
7 : : #include "macros.h"
8 : : #include "number_utils.h"
9 : : #include "result.h"
10 : :
11 : : #include <exess/exess.h>
12 : :
13 : : #include <assert.h>
14 : : #include <math.h>
15 : : #include <stdbool.h>
16 : : #include <stdint.h>
17 : : #include <string.h>
18 : :
19 : : /* Limits for the range of integers that can be exactly represented in floating
20 : : point types. Note that these limits are one less than the largest value,
21 : : since values larger than that may round to it which causes problems with
22 : : perfect round-tripping. For example, 16777217 when parsed as a float will
23 : : result in 1.6777216E7, which a "lossless" coercion would then convert to
24 : : 16777216. */
25 : :
26 : : #define MAX_FLOAT_INT 16777215
27 : : #define MAX_DOUBLE_INT 9007199254740991L
28 : :
29 : : #ifndef FLT_DECIMAL_DIG
30 : : # define FLT_DECIMAL_DIG 9U
31 : : #endif
32 : : #ifndef DBL_DECIMAL_DIG
33 : : # define DBL_DECIMAL_DIG 17U
34 : : #endif
35 : :
36 : : #define ENSURE(condition, return_status_otherwise) \
37 : : do { \
38 : : if (!(condition)) { \
39 : : return RESULT((return_status_otherwise), 0U); \
40 : : } \
41 : : } while (0)
42 : :
43 : : #define ENSURE_COERCION(coercions, coercion) \
44 : : ENSURE(((coercions) & (ExessCoercions)(coercion)), EXESS_LOSS)
45 : :
46 : : #ifdef __cplusplus
47 : : # define UCONVERSION(s, v) (UConversion{s, v})
48 : : # define SCONVERSION(s, v) (SConversion{s, v})
49 : : #else
50 : : # define UCONVERSION(s, v) ((UConversion){s, v})
51 : : # define SCONVERSION(s, v) ((SConversion){s, v})
52 : : #endif
53 : :
54 : : typedef struct {
55 : : ExessStatus status;
56 : : int64_t value;
57 : : } SConversion;
58 : :
59 : : typedef struct {
60 : : ExessStatus status;
61 : : uint64_t value;
62 : : } UConversion;
63 : :
64 : : static bool
65 : 209 : round_decimal_up(const BigDecimal* const d, const char* const digits)
66 : : {
67 [ + + ]: 209 : if (digits[d->expt] > '5') {
68 : 2 : return true;
69 : : }
70 : :
71 [ + + ]: 207 : if (digits[d->expt] < '5') {
72 : 39 : return false;
73 : : }
74 : :
75 [ - + ]: 168 : assert(d->n_digits > 0);
76 [ + + ]: 168 : const char ones = (char)((d->expt > 0) ? digits[d->expt - 1] : '0');
77 : 168 : return ((ones - '0') % 2);
78 : : }
79 : :
80 : : static UConversion
81 : 858 : convert_significand(const ExessCoercions coercions, const void* const in)
82 : : {
83 : 858 : BigDecimal d = *(const BigDecimal*)in;
84 : 858 : const char* const digits = (const char*)in + sizeof(d);
85 : :
86 : 858 : bool round_up = false;
87 [ + + ]: 858 : if (d.expt < (int)d.n_digits) {
88 [ + + ]: 763 : if (coercions & (ExessCoercions)EXESS_TRUNCATE) {
89 [ + + ]: 212 : d.n_digits = (uint8_t)MAX(0, d.expt);
90 [ + + ]: 551 : } else if (coercions & (ExessCoercions)EXESS_ROUND) {
91 [ + + ]: 224 : if (d.expt <= 0) {
92 : 48 : return UCONVERSION(EXESS_SUCCESS, 0U);
93 : : }
94 : :
95 : 176 : round_up = round_decimal_up(&d, digits);
96 : 176 : d.n_digits = (uint8_t)d.expt;
97 : : } else {
98 : 327 : return UCONVERSION(EXESS_LOSS, 0U);
99 : : }
100 : : }
101 : :
102 : 483 : uint64_t magnitude = 0U;
103 : 483 : size_t i = 0U;
104 [ + + ]: 2210 : for (; i < d.n_digits; ++i) {
105 : 1730 : const uint64_t next = (magnitude * 10U) + (uint64_t)(digits[i] - '0');
106 [ + + ]: 1730 : if (next < magnitude) {
107 : 3 : return UCONVERSION(EXESS_OUT_OF_RANGE, i);
108 : : }
109 : :
110 : 1727 : magnitude = next;
111 : : }
112 : :
113 : 480 : return UCONVERSION(EXESS_SUCCESS, magnitude + round_up);
114 : : }
115 : :
116 : : static SConversion
117 : 590 : convert_signed_decimal(const ExessCoercions coercions, const void* const in)
118 : : {
119 : 590 : BigDecimal d = *(const BigDecimal*)in;
120 : 590 : const UConversion c = convert_significand(coercions, in);
121 : 590 : const uint64_t magnitude = c.value;
122 : :
123 : 815 : return c.status ? SCONVERSION(c.status, 0U)
124 [ + + ]: 602 : : (d.expt == 19 &&
125 [ + + + + ]: 57 : (d.kind == EXESS_NEGATIVE && magnitude == 9223372036854775808ULL))
126 : 20 : ? SCONVERSION(c.status, INT64_MIN)
127 [ + + ]: 365 : : (d.expt > 19 ||
128 [ + + + + ]: 342 : (d.expt == 19 && (magnitude >= 9223372036854775808ULL)))
129 : : ? SCONVERSION(EXESS_OUT_OF_RANGE, 0)
130 [ + + ]: 345 : : (d.kind == EXESS_NEGATIVE)
131 : 152 : ? SCONVERSION(c.status, -(int64_t)magnitude)
132 [ + + ]: 333 : : SCONVERSION(c.status, (int64_t)magnitude);
133 : : }
134 : :
135 : : static SConversion
136 : 6680 : convert_signed(const ExessCoercions coercions,
137 : : const ExessDatatype in_datatype,
138 : : const void* const in)
139 : : {
140 : : #define FP_CONVERSION(Type, f, max, trunc_func, round_func) \
141 : : ((f) < (Type)(-(max)) || (f) > (Type)(max)) \
142 : : ? SCONVERSION(EXESS_OUT_OF_RANGE, 0) \
143 : : : ((f) == trunc_func(f)) ? SCONVERSION(EXESS_SUCCESS, (int64_t)(f)) \
144 : : : (coercions & (ExessCoercions)EXESS_ROUND) \
145 : : ? SCONVERSION(EXESS_SUCCESS, round_func(f)) \
146 : : : (coercions & (ExessCoercions)EXESS_TRUNCATE) \
147 : : ? SCONVERSION(EXESS_SUCCESS, (int64_t)(f)) \
148 : : : SCONVERSION(EXESS_LOSS, 0)
149 : :
150 [ + + + + : 6680 : switch (in_datatype) {
+ + + + +
+ + + + +
- ]
151 : 6 : case EXESS_NOTHING:
152 : 99 : break;
153 : :
154 : 20 : case EXESS_BOOLEAN:
155 : 6581 : return SCONVERSION(EXESS_SUCCESS, *(const bool*)in);
156 : :
157 : 817 : case EXESS_DOUBLE:
158 [ + + + + : 817 : return FP_CONVERSION(
+ + + + +
+ ]
159 : : double, *(const double*)in, MAX_DOUBLE_INT, trunc, llrint);
160 : 817 : case EXESS_FLOAT:
161 [ + + + + : 817 : return FP_CONVERSION(
+ + + + +
+ ]
162 : : float, *(const float*)in, MAX_FLOAT_INT, truncf, llrintf);
163 : :
164 : 4234 : case EXESS_LONG:
165 : 4234 : return SCONVERSION(EXESS_SUCCESS, *(const int64_t*)in);
166 : 11 : case EXESS_INT:
167 : 11 : return SCONVERSION(EXESS_SUCCESS, *(const int32_t*)in);
168 : 11 : case EXESS_SHORT:
169 : 11 : return SCONVERSION(EXESS_SUCCESS, *(const int16_t*)in);
170 : 11 : case EXESS_BYTE:
171 : 11 : return SCONVERSION(EXESS_SUCCESS, *(const int8_t*)in);
172 : :
173 : 46 : case EXESS_ULONG: {
174 : 46 : const uint64_t u = *(const uint64_t*)in;
175 : 46 : return (u > INT64_MAX) ? SCONVERSION(EXESS_OUT_OF_RANGE, 0)
176 [ + + ]: 46 : : SCONVERSION(EXESS_SUCCESS, (int64_t)u);
177 : : }
178 : :
179 : 8 : case EXESS_UINT:
180 : 8 : return SCONVERSION(EXESS_SUCCESS, *(const uint32_t*)in);
181 : 8 : case EXESS_USHORT:
182 : 8 : return SCONVERSION(EXESS_SUCCESS, *(const uint16_t*)in);
183 : 8 : case EXESS_UBYTE:
184 : 8 : return SCONVERSION(EXESS_SUCCESS, *(const uint8_t*)in);
185 : :
186 : 590 : case EXESS_DECIMAL:
187 : : case EXESS_INTEGER:
188 : : case EXESS_NON_POSITIVE_INTEGER:
189 : : case EXESS_NEGATIVE_INTEGER:
190 : : case EXESS_NON_NEGATIVE_INTEGER:
191 : : case EXESS_POSITIVE_INTEGER:
192 : 590 : return convert_signed_decimal(coercions, in);
193 : :
194 : 93 : case EXESS_DURATION:
195 : : case EXESS_YEAR_MONTH_DURATION:
196 : : case EXESS_DAY_TIME_DURATION:
197 : : case EXESS_DATE_TIME:
198 : : case EXESS_DATE_TIME_STAMP:
199 : : case EXESS_TIME:
200 : : case EXESS_DATE:
201 : : case EXESS_HEX:
202 : : case EXESS_BASE64:
203 : 93 : break;
204 : : }
205 : :
206 : 99 : return SCONVERSION(EXESS_UNSUPPORTED, 0);
207 : : }
208 : :
209 : : static UConversion
210 : 945 : convert_unsigned(const ExessCoercions coercions,
211 : : const ExessDatatype in_datatype,
212 : : const void* const in)
213 : : {
214 [ + + ]: 945 : if (in_datatype == EXESS_ULONG) {
215 : 21 : return UCONVERSION(EXESS_SUCCESS, *(const uint64_t*)in);
216 : : }
217 : :
218 [ + + + + ]: 924 : if (in_datatype >= EXESS_DECIMAL && in_datatype <= EXESS_POSITIVE_INTEGER) {
219 : 268 : BigDecimal d = *(const BigDecimal*)in;
220 : 268 : const UConversion c = convert_significand(coercions, in);
221 : :
222 : 373 : return c.status ? UCONVERSION(c.status, 0U)
223 [ + + ]: 431 : : (d.expt > 20) ? UCONVERSION(EXESS_OUT_OF_RANGE, 0U)
224 [ + + ]: 163 : : c;
225 : : }
226 : :
227 : 656 : const SConversion c = convert_signed(coercions, in_datatype, in);
228 : :
229 : 929 : return c.status ? UCONVERSION(c.status, 0U)
230 [ + + ]: 683 : : (c.value < 0) ? UCONVERSION(EXESS_OUT_OF_RANGE, 0U)
231 [ + + ]: 383 : : UCONVERSION(EXESS_SUCCESS, (uint64_t)c.value);
232 : : }
233 : :
234 : : static SConversion
235 : 5997 : convert_signed_range(const ExessCoercions coercions,
236 : : const ExessDatatype in_datatype,
237 : : const void* const in,
238 : : const int64_t min,
239 : : const int64_t max)
240 : : {
241 : 5997 : const SConversion c = convert_signed(coercions, in_datatype, in);
242 : :
243 [ + + + + ]: 5239 : return (!c.status && (c.value < min || c.value > max))
244 : : ? SCONVERSION(EXESS_OUT_OF_RANGE, 0U)
245 [ + + ]: 11236 : : c;
246 : : }
247 : :
248 : : static UConversion
249 : 945 : convert_unsigned_range(const ExessCoercions coercions,
250 : : const ExessDatatype in_datatype,
251 : : const void* const in,
252 : : const uint64_t max)
253 : : {
254 : 945 : const UConversion c = convert_unsigned(coercions, in_datatype, in);
255 : :
256 [ + + + + ]: 945 : return (!c.status && c.value > max) ? UCONVERSION(EXESS_OUT_OF_RANGE, 0U) : c;
257 : : }
258 : :
259 : : static ExessResult
260 : 27 : to_boolean(const ExessCoercions coercions,
261 : : const ExessDatatype in_datatype,
262 : : const void* const in,
263 : : bool* const out)
264 : : {
265 : 27 : const SConversion c = convert_signed(coercions, in_datatype, in);
266 [ + + ]: 27 : ENSURE(!c.status, c.status);
267 [ + + + + : 23 : ENSURE((coercions & (ExessCoercions)EXESS_TRUNCATE) || c.value == 0 ||
+ + ]
268 : : c.value == 1,
269 : : EXESS_LOSS);
270 : :
271 : 14 : *out = !!c.value;
272 : 14 : return RESULT(EXESS_SUCCESS, sizeof(bool));
273 : : }
274 : :
275 : : static ExessResult
276 : 8500 : to_decimal(const ExessCoercions coercions,
277 : : const ExessDatatype in_datatype,
278 : : const void* const in,
279 : : const size_t out_size,
280 : : void* const out)
281 : : {
282 : 8500 : BigDecimal* const head = (BigDecimal*)out;
283 : 8500 : const size_t digits_size = out_size - sizeof(*head);
284 : 8500 : char* const digits = (char*)(head + 1U);
285 : :
286 [ + + ]: 8500 : if (in_datatype == EXESS_DECIMAL) {
287 : 68 : const BigDecimal* const in_head = (const BigDecimal*)in;
288 : 68 : const size_t size = sizeof(BigDecimal) + in_head->n_digits + 1;
289 [ + + ]: 68 : if (out_size < size) {
290 : 18 : return RESULT(EXESS_NO_SPACE, size);
291 : : }
292 : :
293 : 50 : memcpy(out, in, size);
294 : 50 : return RESULT(EXESS_SUCCESS, size);
295 : : }
296 : :
297 [ + + + + ]: 8432 : if (in_datatype == EXESS_DOUBLE || in_datatype == EXESS_FLOAT) {
298 : 4257 : const double d = (in_datatype == EXESS_DOUBLE) ? *(const double*)in
299 [ + + ]: 4257 : : (double)*(const float*)in;
300 : :
301 : 4257 : *head = double_to_decimal(d, digits_size, digits);
302 [ + + ]: 4257 : if (head->n_digits == digits_size) { // No space for terminator
303 : 30 : return RESULT(EXESS_NO_SPACE, sizeof(*head) + head->n_digits + 1U);
304 : : }
305 : :
306 : 4227 : digits[head->n_digits] = '\0';
307 : 4227 : return RESULT(EXESS_SUCCESS, sizeof(*head) + head->n_digits + 1U);
308 : : }
309 : :
310 [ + + ]: 4175 : if (in_datatype == EXESS_ULONG) {
311 : 11 : const uint64_t u_in = *(const uint64_t*)in;
312 : 11 : const ExessResult r = write_digits(u_in, digits_size, digits, 0);
313 [ + + ]: 11 : head->kind = u_in ? EXESS_POSITIVE : EXESS_POSITIVE_ZERO;
314 : 11 : head->n_digits = (uint8_t)r.count;
315 : 11 : head->expt = (int16_t)r.count;
316 : 11 : digits[head->n_digits] = '\0';
317 : 11 : return RESULT(r.status, sizeof(*head) + r.count + 1U);
318 : : }
319 : :
320 : : const SConversion c =
321 : 4164 : convert_signed_range(coercions, in_datatype, in, INT64_MIN, INT64_MAX);
322 : :
323 [ + + ]: 4164 : ENSURE(!c.status, c.status);
324 [ + + ]: 4159 : if (!c.value) {
325 : 18 : head->kind = EXESS_POSITIVE_ZERO;
326 : 18 : head->n_digits = 0U;
327 : 18 : head->expt = 0;
328 : 18 : digits[0] = '\0';
329 : 18 : return RESULT(EXESS_SUCCESS, sizeof(*head) + 1U);
330 : : }
331 : :
332 [ + + ]: 4141 : if (c.value == INT64_MIN) {
333 [ + + ]: 7 : if (out_size < sizeof(BigDecimal) + 20U) {
334 : 3 : return RESULT(EXESS_NO_SPACE, sizeof(BigDecimal) + head->n_digits + 1);
335 : : }
336 : :
337 : 4 : head->kind = EXESS_NEGATIVE;
338 : 4 : head->n_digits = 19U;
339 : 4 : head->expt = 19;
340 : 4 : memcpy(digits, "9223372036854775808", 19U);
341 : 4 : return RESULT(EXESS_SUCCESS, sizeof(*head) + 20U);
342 : : }
343 : :
344 : 4134 : uint64_t abs_value = c.value < 0 ? (uint64_t)-c.value : (uint64_t)c.value;
345 : 4134 : int expt_offset = 0;
346 [ + + + + ]: 4380 : while (abs_value >= 10U && (abs_value % 10U) == 0U) {
347 : 246 : abs_value /= 10U;
348 : 246 : ++expt_offset;
349 : : }
350 : :
351 : 4134 : const ExessResult r = write_digits(abs_value, digits_size - 1U, digits, 0);
352 : :
353 [ + + ]: 4134 : head->kind = c.value < 0 ? EXESS_NEGATIVE : EXESS_POSITIVE;
354 : 4134 : head->n_digits = (uint8_t)r.count;
355 : 4134 : head->expt = (int16_t)(expt_offset + (int)r.count);
356 : 4134 : digits[head->n_digits] = '\0';
357 : :
358 : 4134 : return RESULT(r.status, sizeof(*head) + r.count + 1U);
359 : : }
360 : :
361 : : static ExessResult
362 : 4115 : to_double(const ExessCoercions coercions,
363 : : const ExessDatatype in_datatype,
364 : : const void* const in,
365 : : double* const out)
366 : : {
367 [ + + ]: 4115 : if (in_datatype == EXESS_DECIMAL) {
368 : 4103 : const BigDecimal d = *(const BigDecimal*)in;
369 : 4103 : const char* const digits = (const char*)in + sizeof(d);
370 [ + + + + ]: 4103 : ENSURE((coercions & (ExessCoercions)EXESS_APPROXIMATE) ||
371 : : d.n_digits <= DBL_DECIMAL_DIG,
372 : : EXESS_LOSS);
373 : :
374 : 4102 : *out = decimal_to_double(d, digits);
375 : 4102 : return RESULT(EXESS_SUCCESS, sizeof(double));
376 : : }
377 : :
378 [ + + ]: 12 : if (in_datatype == EXESS_FLOAT) {
379 : 1 : *out = (double)*(const float*)in;
380 : 1 : return RESULT(EXESS_SUCCESS, sizeof(double));
381 : : }
382 : :
383 : 11 : const SConversion c = convert_signed_range(
384 : : coercions, in_datatype, in, -MAX_DOUBLE_INT, MAX_DOUBLE_INT);
385 : :
386 [ + + ]: 11 : ENSURE(!c.status, c.status);
387 : 8 : *out = (double)c.value;
388 : 8 : return RESULT(EXESS_SUCCESS, sizeof(double));
389 : : }
390 : :
391 : : static ExessResult
392 : 19 : to_float(const ExessCoercions coercions,
393 : : const ExessDatatype in_datatype,
394 : : const void* const in,
395 : : float* const out)
396 : : {
397 [ + + ]: 19 : if (in_datatype == EXESS_DECIMAL) {
398 : 3 : const BigDecimal d = *(const BigDecimal*)in;
399 : 3 : const char* const digits = (const char*)in + sizeof(d);
400 [ + + + + ]: 3 : ENSURE((coercions & (ExessCoercions)EXESS_APPROXIMATE) ||
401 : : d.n_digits <= FLT_DECIMAL_DIG,
402 : : EXESS_LOSS);
403 : :
404 : 2 : *out = (float)decimal_to_double(d, digits);
405 : 2 : return RESULT(EXESS_SUCCESS, sizeof(float));
406 : : }
407 : :
408 [ + + ]: 16 : if (in_datatype == EXESS_DOUBLE) {
409 [ + + ]: 5 : ENSURE_COERCION(coercions, EXESS_APPROXIMATE);
410 : 2 : *out = (float)*(const double*)in;
411 : 2 : return RESULT(EXESS_SUCCESS, sizeof(float));
412 : : }
413 : :
414 : 11 : const SConversion c = convert_signed_range(
415 : : coercions, in_datatype, in, -MAX_FLOAT_INT, MAX_FLOAT_INT);
416 : :
417 [ + + ]: 11 : ENSURE(!c.status, c.status);
418 : 8 : *out = (float)c.value;
419 : 8 : return RESULT(EXESS_SUCCESS, sizeof(float));
420 : : }
421 : :
422 : : static ExessResult
423 : 4337 : to_integer(const ExessCoercions coercions,
424 : : const ExessDatatype in_datatype,
425 : : const void* const in,
426 : : const size_t out_size,
427 : : void* const out)
428 : : {
429 : 4337 : ExessResult r = to_decimal(coercions, in_datatype, in, out_size, out);
430 [ + + ]: 4337 : if (r.status) {
431 : 58 : return r;
432 : : }
433 : :
434 : 4279 : BigDecimal* const d = (BigDecimal*)out;
435 [ + + ]: 4279 : if (d->expt < (int)d->n_digits) {
436 : 121 : char* const digits = (char*)(d + 1);
437 [ + + ]: 121 : if (coercions & (ExessCoercions)EXESS_TRUNCATE) {
438 [ + + ]: 27 : d->n_digits = (uint8_t)MAX(0, d->expt);
439 : 27 : digits[d->n_digits] = '\0';
440 [ + + ]: 27 : if (!d->n_digits) {
441 : 7 : d->kind = EXESS_POSITIVE_ZERO;
442 : 7 : d->expt = 0;
443 : : }
444 [ + + ]: 94 : } else if (coercions & (ExessCoercions)EXESS_ROUND) {
445 : 33 : const bool up = round_decimal_up(d, digits);
446 [ + + ]: 33 : d->n_digits = (uint8_t)MAX(0, d->expt);
447 [ + + ]: 33 : if (up) {
448 : 14 : ++digits[d->n_digits - 1];
449 : : }
450 [ + + ]: 33 : if (!d->n_digits) {
451 : 7 : d->kind = EXESS_POSITIVE_ZERO;
452 : 7 : d->expt = 0;
453 : : }
454 : : } else {
455 : 61 : r.status = EXESS_LOSS;
456 : : }
457 : : }
458 : :
459 : 4279 : return r;
460 : : }
461 : :
462 : : static ExessResult
463 : 29 : to_constrained_integer(const ExessCoercions coercions,
464 : : const ExessDatatype in_datatype,
465 : : const void* const in,
466 : : const ExessDatatype out_datatype,
467 : : const size_t out_size,
468 : : void* const out)
469 : : {
470 : 29 : const ExessResult r = to_integer(coercions, in_datatype, in, out_size, out);
471 [ + + ]: 29 : ENSURE(!r.status, r.status);
472 : :
473 : 20 : const BigDecimalKind kind = ((BigDecimal*)out)->kind;
474 : :
475 : 20 : const bool bad_kind =
476 : : (out_datatype == EXESS_NON_POSITIVE_INTEGER) ? (kind == EXESS_POSITIVE)
477 [ + + + + : 20 : : (out_datatype == EXESS_NEGATIVE_INTEGER) ? (kind != EXESS_NEGATIVE)
+ + ]
478 : : : (out_datatype == EXESS_NON_NEGATIVE_INTEGER) ? (kind == EXESS_NEGATIVE)
479 : : : (kind != EXESS_POSITIVE);
480 : :
481 [ + + ]: 20 : return bad_kind ? RESULT(EXESS_OUT_OF_RANGE, 0U) : r;
482 : : }
483 : :
484 : : static ExessResult
485 : 17 : to_time(const ExessCoercions coercions,
486 : : const ExessDatatype in_datatype,
487 : : const void* const in,
488 : : ExessTime* const out)
489 : : {
490 [ + + ]: 17 : ENSURE(in_datatype == EXESS_DATE_TIME, EXESS_UNSUPPORTED);
491 [ + + ]: 7 : ENSURE_COERCION(coercions, EXESS_TRUNCATE);
492 : :
493 : 3 : const ExessDateTime datetime = *(const ExessDateTime*)in;
494 : :
495 : 3 : out->zone = datetime.zone;
496 : 3 : out->hour = datetime.hour;
497 : 3 : out->minute = datetime.minute;
498 : 3 : out->second = datetime.second;
499 : 3 : out->nanosecond = datetime.nanosecond;
500 : 3 : return RESULT(EXESS_SUCCESS, sizeof(ExessTime));
501 : : }
502 : :
503 : : static ExessResult
504 : 16 : to_date(const ExessCoercions coercions,
505 : : const ExessDatatype in_datatype,
506 : : const void* const in,
507 : : ExessDate* const out)
508 : : {
509 [ + + ]: 16 : ENSURE(in_datatype == EXESS_DATE_TIME, EXESS_UNSUPPORTED);
510 [ + + ]: 6 : ENSURE_COERCION(coercions, EXESS_TRUNCATE);
511 : :
512 : 3 : const ExessDateTime datetime = *(const ExessDateTime*)in;
513 : :
514 : 3 : out->year = datetime.year;
515 : 3 : out->month = datetime.month;
516 : 3 : out->day = datetime.day;
517 : 3 : out->zone = datetime.zone;
518 : 3 : return RESULT(EXESS_SUCCESS, sizeof(ExessDate));
519 : : }
520 : :
521 : : static uint8_t
522 : 13924 : min_value_size(const ExessDatatype datatype)
523 : : {
524 [ + + ]: 8593 : return (datatype >= EXESS_DECIMAL && datatype <= EXESS_POSITIVE_INTEGER)
525 : : ? 6U
526 [ + + ]: 22517 : : exess_value_sizes[datatype];
527 : : }
528 : :
529 : : EXESS_NONBLOCKING ExessResult
530 : 13925 : exess_coerce_value(const ExessCoercions coercions,
531 : : const ExessDatatype in_datatype,
532 : : const size_t in_size,
533 : : const void* const in,
534 : : const ExessDatatype out_datatype,
535 : : const size_t out_size,
536 : : void* const out)
537 : : {
538 : : // Ensure the input and output are large enough (for fixed types)
539 [ + + ]: 13925 : ENSURE(in_size >= exess_value_sizes[in_datatype], EXESS_BAD_VALUE);
540 [ + + ]: 13924 : ENSURE(out_size >= min_value_size(out_datatype), EXESS_NO_SPACE);
541 : :
542 : : // Copy value verbatim for trivial conversions
543 [ + + + + ]: 13516 : if ((out_datatype == in_datatype) ||
544 [ + + + + ]: 13513 : (out_datatype == EXESS_HEX && in_datatype == EXESS_BASE64) ||
545 [ + + ]: 5 : (out_datatype == EXESS_BASE64 && in_datatype == EXESS_HEX)) {
546 [ + + ]: 10 : ENSURE(out_size >= in_size, EXESS_NO_SPACE);
547 : 6 : memcpy(out, in, in_size);
548 : 6 : return RESULT(EXESS_SUCCESS, in_size);
549 : : }
550 : :
551 : : #define FIXED_CASE(Type, c) \
552 : : do { \
553 : : *(Type*)out = (c).status ? (Type)0 : (Type)(c).value; \
554 : : return RESULT((c).status, (c).status ? 0U : sizeof(Type)); \
555 : : } while (0)
556 : :
557 [ + + + + : 13506 : switch (out_datatype) {
+ + + + +
+ + + + +
+ + + + +
- ]
558 : 2 : case EXESS_NOTHING:
559 : 2 : break;
560 : :
561 : 27 : case EXESS_BOOLEAN:
562 : 27 : return to_boolean(coercions, in_datatype, in, (bool*)out);
563 : 4115 : case EXESS_DOUBLE:
564 : 4115 : return to_double(coercions, in_datatype, in, (double*)out);
565 : 19 : case EXESS_FLOAT:
566 : 19 : return to_float(coercions, in_datatype, in, (float*)out);
567 : :
568 : 169 : case EXESS_LONG:
569 [ + + + + ]: 169 : FIXED_CASE(
570 : : int64_t,
571 : : convert_signed_range(coercions, in_datatype, in, INT64_MIN, INT64_MAX));
572 : 116 : case EXESS_INT:
573 [ + + + + ]: 116 : FIXED_CASE(
574 : : int32_t,
575 : : convert_signed_range(coercions, in_datatype, in, INT32_MIN, INT32_MAX));
576 : 116 : case EXESS_SHORT:
577 [ + + + + ]: 116 : FIXED_CASE(
578 : : int16_t,
579 : : convert_signed_range(coercions, in_datatype, in, INT16_MIN, INT16_MAX));
580 : 116 : case EXESS_BYTE:
581 [ + + ]: 116 : FIXED_CASE(
582 : : int8_t,
583 : : convert_signed_range(coercions, in_datatype, in, INT8_MIN, INT8_MAX));
584 : :
585 : 103 : case EXESS_ULONG:
586 [ + + + + ]: 103 : FIXED_CASE(uint64_t,
587 : : convert_unsigned_range(coercions, in_datatype, in, UINT64_MAX));
588 : 56 : case EXESS_UINT:
589 [ + + + + ]: 56 : FIXED_CASE(uint32_t,
590 : : convert_unsigned_range(coercions, in_datatype, in, UINT32_MAX));
591 : 56 : case EXESS_USHORT:
592 [ + + + + ]: 56 : FIXED_CASE(uint16_t,
593 : : convert_unsigned_range(coercions, in_datatype, in, UINT16_MAX));
594 : 56 : case EXESS_UBYTE:
595 [ + + ]: 56 : FIXED_CASE(uint8_t,
596 : : convert_unsigned_range(coercions, in_datatype, in, UINT8_MAX));
597 : :
598 : 4163 : case EXESS_DECIMAL:
599 : 4163 : return to_decimal(coercions, in_datatype, in, out_size, out);
600 : 4308 : case EXESS_INTEGER:
601 : 4308 : return to_integer(coercions, in_datatype, in, out_size, out);
602 : 29 : case EXESS_NON_POSITIVE_INTEGER:
603 : : case EXESS_NEGATIVE_INTEGER:
604 : : case EXESS_NON_NEGATIVE_INTEGER:
605 : : case EXESS_POSITIVE_INTEGER:
606 : 29 : return to_constrained_integer(
607 : : coercions, in_datatype, in, out_datatype, out_size, out);
608 : :
609 : 20 : case EXESS_DURATION:
610 : : case EXESS_YEAR_MONTH_DURATION:
611 : : case EXESS_DAY_TIME_DURATION:
612 : : case EXESS_DATE_TIME:
613 : : case EXESS_DATE_TIME_STAMP:
614 : 20 : break;
615 : :
616 : 17 : case EXESS_TIME:
617 : 17 : return to_time(coercions, in_datatype, in, (ExessTime*)out);
618 : 16 : case EXESS_DATE:
619 : 16 : return to_date(coercions, in_datatype, in, (ExessDate*)out);
620 : :
621 : 2 : case EXESS_HEX:
622 : : case EXESS_BASE64:
623 : 2 : break;
624 : : }
625 : :
626 : : #undef FIXED_CASE
627 : :
628 : 24 : return RESULT(EXESS_UNSUPPORTED, 0U);
629 : : }
|