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_YEAR_MONTH:
202 : : case EXESS_YEAR:
203 : : case EXESS_MONTH_DAY:
204 : : case EXESS_DAY:
205 : : case EXESS_MONTH:
206 : : case EXESS_HEX:
207 : : case EXESS_BASE64:
208 : 93 : break;
209 : : }
210 : :
211 : 99 : return SCONVERSION(EXESS_UNSUPPORTED, 0);
212 : : }
213 : :
214 : : static UConversion
215 : 945 : convert_unsigned(const ExessCoercions coercions,
216 : : const ExessDatatype in_datatype,
217 : : const void* const in)
218 : : {
219 [ + + ]: 945 : if (in_datatype == EXESS_ULONG) {
220 : 21 : return UCONVERSION(EXESS_SUCCESS, *(const uint64_t*)in);
221 : : }
222 : :
223 [ + + + + ]: 924 : if (in_datatype >= EXESS_DECIMAL && in_datatype <= EXESS_POSITIVE_INTEGER) {
224 : 268 : BigDecimal d = *(const BigDecimal*)in;
225 : 268 : const UConversion c = convert_significand(coercions, in);
226 : :
227 : 373 : return c.status ? UCONVERSION(c.status, 0U)
228 [ + + ]: 431 : : (d.expt > 20) ? UCONVERSION(EXESS_OUT_OF_RANGE, 0U)
229 [ + + ]: 163 : : c;
230 : : }
231 : :
232 : 656 : const SConversion c = convert_signed(coercions, in_datatype, in);
233 : :
234 : 929 : return c.status ? UCONVERSION(c.status, 0U)
235 [ + + ]: 683 : : (c.value < 0) ? UCONVERSION(EXESS_OUT_OF_RANGE, 0U)
236 [ + + ]: 383 : : UCONVERSION(EXESS_SUCCESS, (uint64_t)c.value);
237 : : }
238 : :
239 : : static SConversion
240 : 5997 : convert_signed_range(const ExessCoercions coercions,
241 : : const ExessDatatype in_datatype,
242 : : const void* const in,
243 : : const int64_t min,
244 : : const int64_t max)
245 : : {
246 : 5997 : const SConversion c = convert_signed(coercions, in_datatype, in);
247 : :
248 [ + + + + ]: 5239 : return (!c.status && (c.value < min || c.value > max))
249 : : ? SCONVERSION(EXESS_OUT_OF_RANGE, 0U)
250 [ + + ]: 11236 : : c;
251 : : }
252 : :
253 : : static UConversion
254 : 945 : convert_unsigned_range(const ExessCoercions coercions,
255 : : const ExessDatatype in_datatype,
256 : : const void* const in,
257 : : const uint64_t max)
258 : : {
259 : 945 : const UConversion c = convert_unsigned(coercions, in_datatype, in);
260 : :
261 [ + + + + ]: 945 : return (!c.status && c.value > max) ? UCONVERSION(EXESS_OUT_OF_RANGE, 0U) : c;
262 : : }
263 : :
264 : : static ExessResult
265 : 27 : to_boolean(const ExessCoercions coercions,
266 : : const ExessDatatype in_datatype,
267 : : const void* const in,
268 : : bool* const out)
269 : : {
270 : 27 : const SConversion c = convert_signed(coercions, in_datatype, in);
271 [ + + ]: 27 : ENSURE(!c.status, c.status);
272 [ + + + + : 23 : ENSURE((coercions & (ExessCoercions)EXESS_TRUNCATE) || c.value == 0 ||
+ + ]
273 : : c.value == 1,
274 : : EXESS_LOSS);
275 : :
276 : 14 : *out = !!c.value;
277 : 14 : return RESULT(EXESS_SUCCESS, sizeof(bool));
278 : : }
279 : :
280 : : static ExessResult
281 : 8500 : to_decimal(const ExessCoercions coercions,
282 : : const ExessDatatype in_datatype,
283 : : const void* const in,
284 : : const size_t out_size,
285 : : void* const out)
286 : : {
287 : 8500 : BigDecimal* const head = (BigDecimal*)out;
288 : 8500 : const size_t digits_size = out_size - sizeof(*head);
289 : 8500 : char* const digits = (char*)(head + 1U);
290 : :
291 [ + + ]: 8500 : if (in_datatype == EXESS_DECIMAL) {
292 : 68 : const BigDecimal* const in_head = (const BigDecimal*)in;
293 : 68 : const size_t size = sizeof(BigDecimal) + in_head->n_digits + 1;
294 [ + + ]: 68 : if (out_size < size) {
295 : 18 : return RESULT(EXESS_NO_SPACE, size);
296 : : }
297 : :
298 : 50 : memcpy(out, in, size);
299 : 50 : return RESULT(EXESS_SUCCESS, size);
300 : : }
301 : :
302 [ + + + + ]: 8432 : if (in_datatype == EXESS_DOUBLE || in_datatype == EXESS_FLOAT) {
303 : 4257 : const double d = (in_datatype == EXESS_DOUBLE) ? *(const double*)in
304 [ + + ]: 4257 : : (double)*(const float*)in;
305 : :
306 : 4257 : *head = double_to_decimal(d, digits_size, digits);
307 [ + + ]: 4257 : if (head->n_digits == digits_size) { // No space for terminator
308 : 30 : return RESULT(EXESS_NO_SPACE, sizeof(*head) + head->n_digits + 1U);
309 : : }
310 : :
311 : 4227 : digits[head->n_digits] = '\0';
312 : 4227 : return RESULT(EXESS_SUCCESS, sizeof(*head) + head->n_digits + 1U);
313 : : }
314 : :
315 [ + + ]: 4175 : if (in_datatype == EXESS_ULONG) {
316 : 11 : const uint64_t u_in = *(const uint64_t*)in;
317 : 11 : const ExessResult r = write_digits(u_in, digits_size, digits, 0);
318 [ + + ]: 11 : head->kind = u_in ? EXESS_POSITIVE : EXESS_POSITIVE_ZERO;
319 : 11 : head->n_digits = (uint8_t)r.count;
320 : 11 : head->expt = (int16_t)r.count;
321 : 11 : digits[head->n_digits] = '\0';
322 : 11 : return RESULT(r.status, sizeof(*head) + r.count + 1U);
323 : : }
324 : :
325 : : const SConversion c =
326 : 4164 : convert_signed_range(coercions, in_datatype, in, INT64_MIN, INT64_MAX);
327 : :
328 [ + + ]: 4164 : ENSURE(!c.status, c.status);
329 [ + + ]: 4159 : if (!c.value) {
330 : 18 : head->kind = EXESS_POSITIVE_ZERO;
331 : 18 : head->n_digits = 0U;
332 : 18 : head->expt = 0;
333 : 18 : digits[0] = '\0';
334 : 18 : return RESULT(EXESS_SUCCESS, sizeof(*head) + 1U);
335 : : }
336 : :
337 [ + + ]: 4141 : if (c.value == INT64_MIN) {
338 [ + + ]: 7 : if (out_size < sizeof(BigDecimal) + 20U) {
339 : 3 : return RESULT(EXESS_NO_SPACE, sizeof(BigDecimal) + head->n_digits + 1);
340 : : }
341 : :
342 : 4 : head->kind = EXESS_NEGATIVE;
343 : 4 : head->n_digits = 19U;
344 : 4 : head->expt = 19;
345 : 4 : memcpy(digits, "9223372036854775808", 19U);
346 : 4 : return RESULT(EXESS_SUCCESS, sizeof(*head) + 20U);
347 : : }
348 : :
349 : 4134 : uint64_t abs_value = c.value < 0 ? (uint64_t)-c.value : (uint64_t)c.value;
350 : 4134 : int expt_offset = 0;
351 [ + + + + ]: 4394 : while (abs_value >= 10U && (abs_value % 10U) == 0U) {
352 : 260 : abs_value /= 10U;
353 : 260 : ++expt_offset;
354 : : }
355 : :
356 : 4134 : const ExessResult r = write_digits(abs_value, digits_size - 1U, digits, 0);
357 : :
358 [ + + ]: 4134 : head->kind = c.value < 0 ? EXESS_NEGATIVE : EXESS_POSITIVE;
359 : 4134 : head->n_digits = (uint8_t)r.count;
360 : 4134 : head->expt = (int16_t)(expt_offset + (int)r.count);
361 : 4134 : digits[head->n_digits] = '\0';
362 : :
363 : 4134 : return RESULT(r.status, sizeof(*head) + r.count + 1U);
364 : : }
365 : :
366 : : static ExessResult
367 : 4115 : to_double(const ExessCoercions coercions,
368 : : const ExessDatatype in_datatype,
369 : : const void* const in,
370 : : double* const out)
371 : : {
372 [ + + ]: 4115 : if (in_datatype == EXESS_DECIMAL) {
373 : 4103 : const BigDecimal d = *(const BigDecimal*)in;
374 : 4103 : const char* const digits = (const char*)in + sizeof(d);
375 [ + + + + ]: 4103 : ENSURE((coercions & (ExessCoercions)EXESS_APPROXIMATE) ||
376 : : d.n_digits <= DBL_DECIMAL_DIG,
377 : : EXESS_LOSS);
378 : :
379 : 4102 : *out = decimal_to_double(d, digits);
380 : 4102 : return RESULT(EXESS_SUCCESS, sizeof(double));
381 : : }
382 : :
383 [ + + ]: 12 : if (in_datatype == EXESS_FLOAT) {
384 : 1 : *out = (double)*(const float*)in;
385 : 1 : return RESULT(EXESS_SUCCESS, sizeof(double));
386 : : }
387 : :
388 : 11 : const SConversion c = convert_signed_range(
389 : : coercions, in_datatype, in, -MAX_DOUBLE_INT, MAX_DOUBLE_INT);
390 : :
391 [ + + ]: 11 : ENSURE(!c.status, c.status);
392 : 8 : *out = (double)c.value;
393 : 8 : return RESULT(EXESS_SUCCESS, sizeof(double));
394 : : }
395 : :
396 : : static ExessResult
397 : 19 : to_float(const ExessCoercions coercions,
398 : : const ExessDatatype in_datatype,
399 : : const void* const in,
400 : : float* const out)
401 : : {
402 [ + + ]: 19 : if (in_datatype == EXESS_DECIMAL) {
403 : 3 : const BigDecimal d = *(const BigDecimal*)in;
404 : 3 : const char* const digits = (const char*)in + sizeof(d);
405 [ + + + + ]: 3 : ENSURE((coercions & (ExessCoercions)EXESS_APPROXIMATE) ||
406 : : d.n_digits <= FLT_DECIMAL_DIG,
407 : : EXESS_LOSS);
408 : :
409 : 2 : *out = (float)decimal_to_double(d, digits);
410 : 2 : return RESULT(EXESS_SUCCESS, sizeof(float));
411 : : }
412 : :
413 [ + + ]: 16 : if (in_datatype == EXESS_DOUBLE) {
414 [ + + ]: 5 : ENSURE_COERCION(coercions, EXESS_APPROXIMATE);
415 : 2 : *out = (float)*(const double*)in;
416 : 2 : return RESULT(EXESS_SUCCESS, sizeof(float));
417 : : }
418 : :
419 : 11 : const SConversion c = convert_signed_range(
420 : : coercions, in_datatype, in, -MAX_FLOAT_INT, MAX_FLOAT_INT);
421 : :
422 [ + + ]: 11 : ENSURE(!c.status, c.status);
423 : 8 : *out = (float)c.value;
424 : 8 : return RESULT(EXESS_SUCCESS, sizeof(float));
425 : : }
426 : :
427 : : static ExessResult
428 : 4337 : to_integer(const ExessCoercions coercions,
429 : : const ExessDatatype in_datatype,
430 : : const void* const in,
431 : : const size_t out_size,
432 : : void* const out)
433 : : {
434 : 4337 : ExessResult r = to_decimal(coercions, in_datatype, in, out_size, out);
435 [ + + ]: 4337 : if (r.status) {
436 : 58 : return r;
437 : : }
438 : :
439 : 4279 : BigDecimal* const d = (BigDecimal*)out;
440 [ + + ]: 4279 : if (d->expt < (int)d->n_digits) {
441 : 121 : char* const digits = (char*)(d + 1);
442 [ + + ]: 121 : if (coercions & (ExessCoercions)EXESS_TRUNCATE) {
443 [ + + ]: 27 : d->n_digits = (uint8_t)MAX(0, d->expt);
444 : 27 : digits[d->n_digits] = '\0';
445 [ + + ]: 27 : if (!d->n_digits) {
446 : 7 : d->kind = EXESS_POSITIVE_ZERO;
447 : 7 : d->expt = 0;
448 : : }
449 [ + + ]: 94 : } else if (coercions & (ExessCoercions)EXESS_ROUND) {
450 : 33 : const bool up = round_decimal_up(d, digits);
451 [ + + ]: 33 : d->n_digits = (uint8_t)MAX(0, d->expt);
452 [ + + ]: 33 : if (up) {
453 : 14 : ++digits[d->n_digits - 1];
454 : : }
455 [ + + ]: 33 : if (!d->n_digits) {
456 : 7 : d->kind = EXESS_POSITIVE_ZERO;
457 : 7 : d->expt = 0;
458 : : }
459 : : } else {
460 : 61 : r.status = EXESS_LOSS;
461 : : }
462 : : }
463 : :
464 : 4279 : return r;
465 : : }
466 : :
467 : : static ExessResult
468 : 29 : to_constrained_integer(const ExessCoercions coercions,
469 : : const ExessDatatype in_datatype,
470 : : const void* const in,
471 : : const ExessDatatype out_datatype,
472 : : const size_t out_size,
473 : : void* const out)
474 : : {
475 : 29 : const ExessResult r = to_integer(coercions, in_datatype, in, out_size, out);
476 [ + + ]: 29 : ENSURE(!r.status, r.status);
477 : :
478 : 20 : const BigDecimalKind kind = ((BigDecimal*)out)->kind;
479 : :
480 : 20 : const bool bad_kind =
481 : : (out_datatype == EXESS_NON_POSITIVE_INTEGER) ? (kind == EXESS_POSITIVE)
482 [ + + + + : 20 : : (out_datatype == EXESS_NEGATIVE_INTEGER) ? (kind != EXESS_NEGATIVE)
+ + ]
483 : : : (out_datatype == EXESS_NON_NEGATIVE_INTEGER) ? (kind == EXESS_NEGATIVE)
484 : : : (kind != EXESS_POSITIVE);
485 : :
486 [ + + ]: 20 : return bad_kind ? RESULT(EXESS_OUT_OF_RANGE, 0U) : r;
487 : : }
488 : :
489 : : static ExessResult
490 : 17 : to_time(const ExessCoercions coercions,
491 : : const ExessDatatype in_datatype,
492 : : const void* const in,
493 : : ExessTime* const out)
494 : : {
495 [ + + ]: 17 : ENSURE(in_datatype == EXESS_DATE_TIME, EXESS_UNSUPPORTED);
496 [ + + ]: 7 : ENSURE_COERCION(coercions, EXESS_TRUNCATE);
497 : :
498 : 3 : const ExessDateTime datetime = *(const ExessDateTime*)in;
499 : :
500 : 3 : out->zone = datetime.zone;
501 : 3 : out->hour = datetime.hour;
502 : 3 : out->minute = datetime.minute;
503 : 3 : out->second = datetime.second;
504 : 3 : out->nanosecond = datetime.nanosecond;
505 : 3 : return RESULT(EXESS_SUCCESS, sizeof(ExessTime));
506 : : }
507 : :
508 : : static ExessResult
509 : 16 : to_date(const ExessCoercions coercions,
510 : : const ExessDatatype in_datatype,
511 : : const void* const in,
512 : : ExessDate* const out)
513 : : {
514 [ + + ]: 16 : ENSURE(in_datatype == EXESS_DATE_TIME, EXESS_UNSUPPORTED);
515 [ + + ]: 6 : ENSURE_COERCION(coercions, EXESS_TRUNCATE);
516 : :
517 : 3 : const ExessDateTime datetime = *(const ExessDateTime*)in;
518 : :
519 : 3 : out->year = datetime.year;
520 : 3 : out->month = datetime.month;
521 : 3 : out->day = datetime.day;
522 : 3 : out->zone = datetime.zone;
523 : 3 : return RESULT(EXESS_SUCCESS, sizeof(ExessDate));
524 : : }
525 : :
526 : : static uint8_t
527 : 13924 : min_value_size(const ExessDatatype datatype)
528 : : {
529 [ + + ]: 8593 : return (datatype >= EXESS_DECIMAL && datatype <= EXESS_POSITIVE_INTEGER)
530 : : ? 6U
531 [ + + ]: 22517 : : exess_value_sizes[datatype];
532 : : }
533 : :
534 : : EXESS_NONBLOCKING ExessResult
535 : 13925 : exess_coerce_value(const ExessCoercions coercions,
536 : : const ExessDatatype in_datatype,
537 : : const size_t in_size,
538 : : const void* const in,
539 : : const ExessDatatype out_datatype,
540 : : const size_t out_size,
541 : : void* const out)
542 : : {
543 : : // Ensure the input and output are large enough (for fixed types)
544 [ + + ]: 13925 : ENSURE(in_size >= exess_value_sizes[in_datatype], EXESS_BAD_VALUE);
545 [ + + ]: 13924 : ENSURE(out_size >= min_value_size(out_datatype), EXESS_NO_SPACE);
546 : :
547 : : // Copy value verbatim for trivial conversions
548 [ + + + + ]: 13516 : if ((out_datatype == in_datatype) ||
549 [ + + + + ]: 13513 : (out_datatype == EXESS_HEX && in_datatype == EXESS_BASE64) ||
550 [ + + ]: 5 : (out_datatype == EXESS_BASE64 && in_datatype == EXESS_HEX)) {
551 [ + + ]: 10 : ENSURE(out_size >= in_size, EXESS_NO_SPACE);
552 : 6 : memcpy(out, in, in_size);
553 : 6 : return RESULT(EXESS_SUCCESS, in_size);
554 : : }
555 : :
556 : : #define FIXED_CASE(Type, c) \
557 : : do { \
558 : : *(Type*)out = (c).status ? (Type)0 : (Type)(c).value; \
559 : : return RESULT((c).status, (c).status ? 0U : sizeof(Type)); \
560 : : } while (0)
561 : :
562 [ + + + + : 13506 : switch (out_datatype) {
+ + + + +
+ + + + +
+ + + + +
- ]
563 : 2 : case EXESS_NOTHING:
564 : 2 : break;
565 : :
566 : 27 : case EXESS_BOOLEAN:
567 : 27 : return to_boolean(coercions, in_datatype, in, (bool*)out);
568 : 4115 : case EXESS_DOUBLE:
569 : 4115 : return to_double(coercions, in_datatype, in, (double*)out);
570 : 19 : case EXESS_FLOAT:
571 : 19 : return to_float(coercions, in_datatype, in, (float*)out);
572 : :
573 : 169 : case EXESS_LONG:
574 [ + + + + ]: 169 : FIXED_CASE(
575 : : int64_t,
576 : : convert_signed_range(coercions, in_datatype, in, INT64_MIN, INT64_MAX));
577 : 116 : case EXESS_INT:
578 [ + + + + ]: 116 : FIXED_CASE(
579 : : int32_t,
580 : : convert_signed_range(coercions, in_datatype, in, INT32_MIN, INT32_MAX));
581 : 116 : case EXESS_SHORT:
582 [ + + + + ]: 116 : FIXED_CASE(
583 : : int16_t,
584 : : convert_signed_range(coercions, in_datatype, in, INT16_MIN, INT16_MAX));
585 : 116 : case EXESS_BYTE:
586 [ + + ]: 116 : FIXED_CASE(
587 : : int8_t,
588 : : convert_signed_range(coercions, in_datatype, in, INT8_MIN, INT8_MAX));
589 : :
590 : 103 : case EXESS_ULONG:
591 [ + + + + ]: 103 : FIXED_CASE(uint64_t,
592 : : convert_unsigned_range(coercions, in_datatype, in, UINT64_MAX));
593 : 56 : case EXESS_UINT:
594 [ + + + + ]: 56 : FIXED_CASE(uint32_t,
595 : : convert_unsigned_range(coercions, in_datatype, in, UINT32_MAX));
596 : 56 : case EXESS_USHORT:
597 [ + + + + ]: 56 : FIXED_CASE(uint16_t,
598 : : convert_unsigned_range(coercions, in_datatype, in, UINT16_MAX));
599 : 56 : case EXESS_UBYTE:
600 [ + + ]: 56 : FIXED_CASE(uint8_t,
601 : : convert_unsigned_range(coercions, in_datatype, in, UINT8_MAX));
602 : :
603 : 4163 : case EXESS_DECIMAL:
604 : 4163 : return to_decimal(coercions, in_datatype, in, out_size, out);
605 : 4308 : case EXESS_INTEGER:
606 : 4308 : return to_integer(coercions, in_datatype, in, out_size, out);
607 : 29 : case EXESS_NON_POSITIVE_INTEGER:
608 : : case EXESS_NEGATIVE_INTEGER:
609 : : case EXESS_NON_NEGATIVE_INTEGER:
610 : : case EXESS_POSITIVE_INTEGER:
611 : 29 : return to_constrained_integer(
612 : : coercions, in_datatype, in, out_datatype, out_size, out);
613 : :
614 : 20 : case EXESS_DURATION:
615 : : case EXESS_YEAR_MONTH_DURATION:
616 : : case EXESS_DAY_TIME_DURATION:
617 : : case EXESS_DATE_TIME:
618 : : case EXESS_DATE_TIME_STAMP:
619 : 20 : break;
620 : :
621 : 17 : case EXESS_TIME:
622 : 17 : return to_time(coercions, in_datatype, in, (ExessTime*)out);
623 : 16 : case EXESS_DATE:
624 : 16 : return to_date(coercions, in_datatype, in, (ExessDate*)out);
625 : :
626 : 2 : case EXESS_YEAR_MONTH:
627 : : case EXESS_YEAR:
628 : : case EXESS_MONTH_DAY:
629 : : case EXESS_DAY:
630 : : case EXESS_MONTH:
631 : : case EXESS_HEX:
632 : : case EXESS_BASE64:
633 : 2 : break;
634 : : }
635 : :
636 : : #undef FIXED_CASE
637 : :
638 : 24 : return RESULT(EXESS_UNSUPPORTED, 0U);
639 : : }
|