ISSUE ID:- RICAPP-216
[ric-app/bouncer.git] / Bouncer / e2sm_kpm / lib / REAL.c
1 /*-
2  * Copyright (c) 2004-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
3  * Redistribution and modifications are permitted subject to BSD license.
4  */
5 #define _ISOC99_SOURCE          /* For ilogb() and quiet NAN */
6 #ifndef _BSD_SOURCE
7 #define _BSD_SOURCE             /* To reintroduce finite(3) */
8 #endif
9 #include <asn_internal.h>
10 #if     defined(__alpha)
11 #include <sys/resource.h>       /* For INFINITY */
12 #endif
13 #include <stdlib.h>     /* for strtod(3) */
14 #include <math.h>
15 #include <float.h>
16 #include <errno.h>
17 #include <REAL.h>
18 #include <OCTET_STRING.h>
19
20 #undef  INT_MAX
21 #define INT_MAX ((int)(((unsigned int)-1) >> 1))
22
23 #if     !(defined(NAN) || defined(INFINITY))
24 static volatile double real_zero CC_NOTUSED = 0.0;
25 #endif
26 #ifndef NAN
27 #define NAN     (0.0/0.0)
28 #endif
29 #ifndef INFINITY
30 #define INFINITY        (1.0/0.0)
31 #endif
32
33 #if defined(__clang__)
34 /*
35  * isnan() is defined using generic selections and won't compile in
36  * strict C89 mode because of too fancy system's standard library.
37  * However, prior to C11 the math had a perfectly working isnan()
38  * in the math library.
39  * Disable generic selection warning so we can test C89 mode with newer libc.
40  */
41 #pragma clang diagnostic push
42 #pragma clang diagnostic ignored "-Wc11-extensions"
43 static int asn_isnan(double d) {
44     return isnan(d);
45 }
46 static int asn_isfinite(double d) {
47 #ifdef isfinite
48     return isfinite(d);  /* ISO C99 */
49 #else
50     return finite(d);    /* Deprecated on Mac OS X 10.9 */
51 #endif
52 }
53 #pragma clang diagnostic pop
54 #else   /* !clang */
55 #define asn_isnan(v)    isnan(v)
56 #ifdef isfinite
57 #define asn_isfinite(d)   isfinite(d)  /* ISO C99 */
58 #else
59 #define asn_isfinite(d)   finite(d)    /* Deprecated on Mac OS X 10.9 */
60 #endif
61 #endif  /* clang */
62
63 /*
64  * REAL basic type description.
65  */
66 static const ber_tlv_tag_t asn_DEF_REAL_tags[] = {
67         (ASN_TAG_CLASS_UNIVERSAL | (9 << 2))
68 };
69 asn_TYPE_operation_t asn_OP_REAL = {
70         ASN__PRIMITIVE_TYPE_free,
71         REAL_print,
72         REAL_compare,
73         ber_decode_primitive,
74         der_encode_primitive,
75         REAL_decode_xer,
76         REAL_encode_xer,
77 #ifdef  ASN_DISABLE_OER_SUPPORT
78         0,
79         0,
80 #else
81         REAL_decode_oer,
82         REAL_encode_oer,
83 #endif  /* ASN_DISABLE_OER_SUPPORT */
84 #ifdef  ASN_DISABLE_PER_SUPPORT
85         0,
86         0,
87         0,
88         0,
89 #else
90         REAL_decode_uper,
91         REAL_encode_uper,
92         REAL_decode_aper,
93         REAL_encode_aper,
94 #endif  /* ASN_DISABLE_PER_SUPPORT */
95         REAL_random_fill,
96         0       /* Use generic outmost tag fetcher */
97 };
98 asn_TYPE_descriptor_t asn_DEF_REAL = {
99         "REAL",
100         "REAL",
101         &asn_OP_REAL,
102         asn_DEF_REAL_tags,
103         sizeof(asn_DEF_REAL_tags) / sizeof(asn_DEF_REAL_tags[0]),
104         asn_DEF_REAL_tags, /* Same as above */
105         sizeof(asn_DEF_REAL_tags) / sizeof(asn_DEF_REAL_tags[0]),
106         { 0, 0, asn_generic_no_constraint },
107         0,
108         0,      /* No members */
109         0       /* No specifics */
110 };
111
112 typedef enum specialRealValue {
113         SRV__NOT_A_NUMBER,
114         SRV__MINUS_INFINITY,
115         SRV__PLUS_INFINITY
116 } specialRealValue_e;
117 static struct specialRealValue_s {
118         char *string;
119         size_t length;
120         long dv;
121 } specialRealValue[] = {
122 #define SRV_SET(foo, val)       { foo, sizeof(foo) - 1, val }
123         SRV_SET("<NOT-A-NUMBER/>", 0),
124         SRV_SET("<MINUS-INFINITY/>", -1),
125         SRV_SET("<PLUS-INFINITY/>", 1),
126 #undef  SRV_SET
127 };
128
129 ssize_t
130 REAL__dump(double d, int canonical, asn_app_consume_bytes_f *cb, void *app_key) {
131         char local_buf[64];
132         char *buf = local_buf;
133         ssize_t buflen = sizeof(local_buf);
134         const char *fmt = canonical ? "%.17E" /* Precise */ : "%.15f" /* Pleasant*/;
135         ssize_t ret;
136
137         /*
138          * Check whether it is a special value.
139          */
140         /* fpclassify(3) is not portable yet */
141         if(asn_isnan(d)) {
142                 buf = specialRealValue[SRV__NOT_A_NUMBER].string;
143                 buflen = specialRealValue[SRV__NOT_A_NUMBER].length;
144                 return (cb(buf, buflen, app_key) < 0) ? -1 : buflen;
145         } else if(!asn_isfinite(d)) {
146                 if(copysign(1.0, d) < 0.0) {
147                         buf = specialRealValue[SRV__MINUS_INFINITY].string;
148                         buflen = specialRealValue[SRV__MINUS_INFINITY].length;
149                 } else {
150                         buf = specialRealValue[SRV__PLUS_INFINITY].string;
151                         buflen = specialRealValue[SRV__PLUS_INFINITY].length;
152                 }
153                 return (cb(buf, buflen, app_key) < 0) ? -1 : buflen;
154         } else if(ilogb(d) <= -INT_MAX) {
155                 if(copysign(1.0, d) < 0.0) {
156                         buf = "-0";
157                         buflen = 2;
158                 } else {
159                         buf = "0";
160                         buflen = 1;
161                 }
162                 return (cb(buf, buflen, app_key) < 0) ? -1 : buflen;
163         }
164
165         /*
166          * Use the libc's double printing, hopefully they got it right.
167          */
168         do {
169                 ret = snprintf(buf, buflen, fmt, d);
170                 if(ret < 0) {
171                         /* There are some old broken APIs. */
172                         buflen <<= 1;
173                         if(buflen > 4096) {
174                                 /* Should be plenty. */
175                                 if(buf != local_buf) FREEMEM(buf);
176                                 return -1;
177                         }
178                 } else if(ret >= buflen) {
179                         buflen = ret + 1;
180                 } else {
181                         buflen = ret;
182                         break;
183                 }
184                 if(buf != local_buf) FREEMEM(buf);
185                 buf = (char *)MALLOC(buflen);
186                 if(!buf) return -1;
187         } while(1);
188
189         if(canonical) {
190                 /*
191                  * Transform the "[-]d.dddE+-dd" output into "[-]d.dddE[-]d"
192                  * Check that snprintf() constructed the output correctly.
193                  */
194                 char *dot;
195                 char *end = buf + buflen;
196                 char *last_zero;
197                 char *first_zero_in_run;
198         char *s;
199
200         enum {
201             LZSTATE_NOTHING,
202             LZSTATE_ZEROES
203         } lz_state = LZSTATE_NOTHING;
204
205                 dot = (buf[0] == 0x2d /* '-' */) ? (buf + 2) : (buf + 1);
206                 if(*dot >= 0x30) {
207                         if(buf != local_buf) FREEMEM(buf);
208                         errno = EINVAL;
209                         return -1;      /* Not a dot, really */
210                 }
211                 *dot = 0x2e;            /* Replace possible comma */
212
213         for(first_zero_in_run = last_zero = s = dot + 2; s < end; s++) {
214             switch(*s) {
215             case 0x45: /* 'E' */
216                 if(lz_state == LZSTATE_ZEROES) last_zero = first_zero_in_run;
217                 break;
218             case 0x30: /* '0' */
219                 if(lz_state == LZSTATE_NOTHING) first_zero_in_run = s;
220                 lz_state = LZSTATE_ZEROES;
221                 continue;
222             default:
223                 lz_state = LZSTATE_NOTHING;
224                 continue;
225             }
226             break;
227         }
228
229                 if(s == end) {
230                         if(buf != local_buf) FREEMEM(buf);
231                         errno = EINVAL;
232                         return -1;              /* No promised E */
233                 }
234
235         assert(*s == 0x45);
236         {
237             char *E = s;
238             char *expptr = ++E;
239             char *s = expptr;
240             int sign;
241
242             if(*expptr == 0x2b /* '+' */) {
243                 /* Skip the "+" */
244                 buflen -= 1;
245                 sign = 0;
246             } else {
247                 sign = 1;
248                 s++;
249             }
250             expptr++;
251             if(expptr > end) {
252                 if(buf != local_buf) FREEMEM(buf);
253                 errno = EINVAL;
254                 return -1;
255             }
256             if(*expptr == 0x30) {
257                 buflen--;
258                 expptr++;
259             }
260             if(lz_state == LZSTATE_ZEROES) {
261                 *last_zero = 0x45;      /* E */
262                 buflen -= s - (last_zero + 1);
263                 s = last_zero + 1;
264                 if(sign) {
265                     *s++ = 0x2d /* '-' */;
266                     buflen++;
267                 }
268             }
269             for(; expptr <= end; s++, expptr++)
270                 *s = *expptr;
271         }
272         } else {
273                 /*
274                  * Remove trailing zeros.
275                  */
276                 char *end = buf + buflen;
277                 char *last_zero = end;
278                 int stoplooking = 0;
279                 char *z;
280                 for(z = end - 1; z > buf; z--) {
281                         switch(*z) {
282                         case 0x30:
283                                 if(!stoplooking)
284                                         last_zero = z;
285                                 continue;
286                         case 0x31: case 0x32: case 0x33: case 0x34:
287                         case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
288                                 stoplooking = 1;
289                                 continue;
290                         default:        /* Catch dot and other separators */
291                                 /*
292                                  * Replace possible comma (which may even
293                                  * be not a comma at all: locale-defined).
294                                  */
295                                 *z = 0x2e;
296                                 if(last_zero == z + 1) {        /* leave x.0 */
297                                         last_zero++;
298                                 }
299                                 buflen = last_zero - buf;
300                                 *last_zero = '\0';
301                                 break;
302                         }
303                         break;
304                 }
305         }
306
307         ret = cb(buf, buflen, app_key);
308         if(buf != local_buf) FREEMEM(buf);
309         return (ret < 0) ? -1 : buflen;
310 }
311
312 int
313 REAL_print(const asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
314            asn_app_consume_bytes_f *cb, void *app_key) {
315     const REAL_t *st = (const REAL_t *)sptr;
316         ssize_t ret;
317         double d;
318
319         (void)td;       /* Unused argument */
320         (void)ilevel;   /* Unused argument */
321
322         if(!st || !st->buf)
323                 ret = cb("<absent>", 8, app_key);
324         else if(asn_REAL2double(st, &d))
325                 ret = cb("<error>", 7, app_key);
326         else
327                 ret = REAL__dump(d, 0, cb, app_key);
328
329         return (ret < 0) ? -1 : 0;
330 }
331
332 int
333 REAL_compare(const asn_TYPE_descriptor_t *td, const void *aptr,
334              const void *bptr) {
335     const REAL_t *a = aptr;
336     const REAL_t *b = bptr;
337
338     (void)td;
339
340     if(a && b) {
341         double adbl, bdbl;
342         int ra, rb;
343         ra = asn_REAL2double(a, &adbl);
344         rb = asn_REAL2double(b, &bdbl);
345         if(ra == 0 && rb == 0) {
346             if(asn_isnan(adbl)) {
347                 if(asn_isnan(bdbl)) {
348                     return 0;
349                 } else {
350                     return -1;
351                 }
352             } else if(asn_isnan(bdbl)) {
353                 return 1;
354             }
355             /* Value comparison. */
356             if(adbl < bdbl) {
357                 return -1;
358             } else if(adbl > bdbl) {
359                 return 1;
360             } else {
361                 return 0;
362             }
363         } else if(ra) {
364             return -1;
365         } else {
366             return 1;
367         }
368     } else if(!a) {
369         return -1;
370     } else {
371         return 1;
372     }
373 }
374
375 asn_enc_rval_t
376 REAL_encode_xer(const asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
377                 enum xer_encoder_flags_e flags, asn_app_consume_bytes_f *cb,
378                 void *app_key) {
379         const REAL_t *st = (const REAL_t *)sptr;
380         asn_enc_rval_t er = {0,0,0};
381         double d;
382
383         (void)ilevel;
384
385         if(!st || !st->buf || asn_REAL2double(st, &d))
386                 ASN__ENCODE_FAILED;
387
388         er.encoded = REAL__dump(d, flags & XER_F_CANONICAL, cb, app_key);
389         if(er.encoded < 0) ASN__ENCODE_FAILED;
390
391         ASN__ENCODED_OK(er);
392 }
393
394
395 /*
396  * Decode the chunk of XML text encoding REAL.
397  */
398 static enum xer_pbd_rval
399 REAL__xer_body_decode(const asn_TYPE_descriptor_t *td, void *sptr,
400                       const void *chunk_buf, size_t chunk_size) {
401     REAL_t *st = (REAL_t *)sptr;
402         double value;
403         const char *xerdata = (const char *)chunk_buf;
404         char *endptr = 0;
405         char *b;
406
407         (void)td;
408
409         if(!chunk_size) return XPBD_BROKEN_ENCODING;
410
411         /*
412          * Decode an XMLSpecialRealValue: <MINUS-INFINITY>, etc.
413          */
414         if(xerdata[0] == 0x3c /* '<' */) {
415                 size_t i;
416                 for(i = 0; i < sizeof(specialRealValue)
417                                 / sizeof(specialRealValue[0]); i++) {
418                         struct specialRealValue_s *srv = &specialRealValue[i];
419                         double dv;
420
421                         if(srv->length != chunk_size
422                         || memcmp(srv->string, chunk_buf, chunk_size))
423                                 continue;
424
425                         /*
426                          * It could've been done using
427                          * (double)srv->dv / real_zero,
428                          * but it summons fp exception on some platforms.
429                          */
430                         switch(srv->dv) {
431                         case -1: dv = - INFINITY; break;
432                         case 0: dv = NAN;       break;
433                         case 1: dv = INFINITY;  break;
434                         default: return XPBD_SYSTEM_FAILURE;
435                         }
436
437                         if(asn_double2REAL(st, dv))
438                                 return XPBD_SYSTEM_FAILURE;
439
440                         return XPBD_BODY_CONSUMED;
441                 }
442                 ASN_DEBUG("Unknown XMLSpecialRealValue");
443                 return XPBD_BROKEN_ENCODING;
444         }
445
446         /*
447          * Copy chunk into the nul-terminated string, and run strtod.
448          */
449         b = (char *)MALLOC(chunk_size + 1);
450         if(!b) return XPBD_SYSTEM_FAILURE;
451         memcpy(b, chunk_buf, chunk_size);
452         b[chunk_size] = 0;      /* nul-terminate */
453
454         value = strtod(b, &endptr);
455         FREEMEM(b);
456         if(endptr == b) return XPBD_BROKEN_ENCODING;
457
458         if(asn_double2REAL(st, value))
459                 return XPBD_SYSTEM_FAILURE;
460
461         return XPBD_BODY_CONSUMED;
462 }
463
464 asn_dec_rval_t
465 REAL_decode_xer(const asn_codec_ctx_t *opt_codec_ctx,
466                 const asn_TYPE_descriptor_t *td, void **sptr,
467                 const char *opt_mname, const void *buf_ptr, size_t size) {
468     return xer_decode_primitive(opt_codec_ctx, td,
469                 sptr, sizeof(REAL_t), opt_mname,
470                 buf_ptr, size, REAL__xer_body_decode);
471 }
472
473 int
474 asn_REAL2double(const REAL_t *st, double *dbl_value) {
475         unsigned int octv;
476
477         if(!st || !st->buf) {
478                 errno = EINVAL;
479                 return -1;
480         }
481
482         if(st->size == 0) {
483                 *dbl_value = 0;
484                 return 0;
485         }
486
487         octv = st->buf[0];      /* unsigned byte */
488
489         switch(octv & 0xC0) {
490         case 0x40:      /* X.690: 8.5.6 a) => 8.5.9 */
491                 /* "SpecialRealValue" */
492
493                 /* Be liberal in what you accept...
494                  * http://en.wikipedia.org/wiki/Robustness_principle
495                 if(st->size != 1) ...
496                 */
497
498                 switch(st->buf[0]) {
499                 case 0x40:      /* 01000000: PLUS-INFINITY */
500                         *dbl_value = INFINITY;
501                         return 0;
502                 case 0x41:      /* 01000001: MINUS-INFINITY */
503                         *dbl_value = - INFINITY;
504                         return 0;
505                 case 0x42:      /* 01000010: NOT-A-NUMBER */
506                         *dbl_value = NAN;
507                         return 0;
508                 case 0x43:      /* 01000011: minus zero */
509                         *dbl_value = -0.0;
510                         return 0;
511                 }
512
513                 errno = EINVAL;
514                 return -1;
515         case 0x00: {    /* X.690: 8.5.7 */
516                 /*
517                  * Decimal. NR{1,2,3} format from ISO 6093.
518                  * NR1: [ ]*[+-]?[0-9]+
519                  * NR2: [ ]*[+-]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)
520                  * NR3: [ ]*[+-]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)[Ee][+-]?[0-9]+
521                  */
522                 double d;
523                 char *source = 0;
524                 char *endptr;
525                 int used_malloc = 0;
526
527                 if(octv == 0 || (octv & 0x3C)) {
528                         /* Remaining values of bits 6 to 1 are Reserved. */
529                         errno = EINVAL;
530                         return -1;
531                 }
532
533                 /* 1. By contract, an input buffer should be '\0'-terminated.
534                  * OCTET STRING decoder ensures that, as is asn_double2REAL().
535                  * 2. ISO 6093 specifies COMMA as a possible decimal separator.
536                  * However, strtod() can't always deal with COMMA.
537                  * So her we fix both by reallocating, copying and fixing.
538                  */
539                 if(st->buf[st->size] != '\0' || memchr(st->buf, ',', st->size)) {
540                         const uint8_t *p, *end;
541                         char *b;
542
543             b = source = (char *)MALLOC(st->size + 1);
544             if(!source) return -1;
545             used_malloc = 1;
546
547                         /* Copy without the first byte and with 0-termination */
548                         for(p = st->buf + 1, end = st->buf + st->size;
549                                         p < end; b++, p++)
550                                 *b = (*p == ',') ? '.' : *p;
551                         *b = '\0';
552                 } else {
553                         source = (char *)&st->buf[1];
554                 }
555
556                 endptr = source;
557                 d = strtod(source, &endptr);
558                 if(*endptr != '\0') {
559                         /* Format is not consistent with ISO 6093 */
560                         if(used_malloc) FREEMEM(source);
561                         errno = EINVAL;
562                         return -1;
563                 }
564                 if(used_malloc) FREEMEM(source);
565                 if(asn_isfinite(d)) {
566                         *dbl_value = d;
567                         return 0;
568                 } else {
569                         errno = ERANGE;
570                         return -1;
571                 }
572           }
573         }
574
575         /*
576          * Binary representation.
577          */
578     {
579         double m;
580         int32_t expval;         /* exponent value */
581         unsigned int elen;      /* exponent value length, in octets */
582         int scaleF;
583         int baseF;
584         uint8_t *ptr;
585         uint8_t *end;
586         int sign;
587
588         switch((octv & 0x30) >> 4) {
589         case 0x00: baseF = 1; break;    /* base 2 */
590         case 0x01: baseF = 3; break;    /* base 8 */
591         case 0x02: baseF = 4; break;    /* base 16 */
592         default:
593                 /* Reserved field, can't parse now. */
594                 errno = EINVAL;
595                 return -1;
596         }
597
598         sign = (octv & 0x40);   /* bit 7 */
599         scaleF = (octv & 0x0C) >> 2;    /* bits 4 to 3 */
600
601         if(st->size <= 1 + (octv & 0x03)) {
602                 errno = EINVAL;
603                 return -1;
604         }
605
606         elen = (octv & 0x03);   /* bits 2 to 1; 8.5.6.4 */
607         if(elen == 0x03) {      /* bits 2 to 1 = 11; 8.5.6.4, case d) */
608                 elen = st->buf[1];      /* unsigned binary number */
609                 if(elen == 0 || st->size <= (2 + elen)) {
610                         errno = EINVAL;
611                         return -1;
612                 }
613                 /* FIXME: verify constraints of case d) */
614                 ptr = &st->buf[2];
615         } else {
616                 ptr = &st->buf[1];
617         }
618
619         /* Fetch the multibyte exponent */
620         expval = (int)(*(int8_t *)ptr);
621         if(elen >= sizeof(expval)-1) {
622                 errno = ERANGE;
623                 return -1;
624         }
625         end = ptr + elen + 1;
626         for(ptr++; ptr < end; ptr++)
627                 expval = (expval * 256) + *ptr;
628
629         m = 0.0;        /* Initial mantissa value */
630
631         /* Okay, the exponent is here. Now, what about mantissa? */
632         end = st->buf + st->size;
633         for(; ptr < end; ptr++)
634                 m = ldexp(m, 8) + *ptr;
635
636         if(0)
637         ASN_DEBUG("m=%.10f, scF=%d, bF=%d, expval=%d, ldexp()=%f, ldexp()=%f\n",
638                 m, scaleF, baseF, expval,
639                 ldexp(m, expval * baseF + scaleF),
640                 ldexp(m, scaleF) * pow(pow(2, baseF), expval)
641         );
642
643         /*
644          * (S * N * 2^F) * B^E
645          * Essentially:
646         m = ldexp(m, scaleF) * pow(pow(2, baseF), expval);
647          */
648         m = ldexp(m, expval * baseF + scaleF);
649         if(asn_isfinite(m)) {
650                 *dbl_value = sign ? -m : m;
651         } else {
652                 errno = ERANGE;
653                 return -1;
654         }
655
656     } /* if(binary_format) */
657
658         return 0;
659 }
660
661 /*
662  * Assume IEEE 754 floating point: standard 64 bit double.
663  * [1 bit sign]  [11 bits exponent]  [52 bits mantissa]
664  */
665 int
666 asn_double2REAL(REAL_t *st, double dbl_value) {
667     double test = -0.0;
668     int float_big_endian = *(const char *)&test != 0;
669         uint8_t buf[16];        /* More than enough for 8-byte dbl_value */
670         uint8_t dscr[sizeof(dbl_value)];        /* double value scratch pad */
671         /* Assertion guards: won't even compile, if unexpected double size */
672         char assertion_buffer1[9 - sizeof(dbl_value)] CC_NOTUSED;
673         char assertion_buffer2[sizeof(dbl_value) - 7] CC_NOTUSED;
674         uint8_t *ptr = buf;
675         uint8_t *mstop;         /* Last byte of mantissa */
676         unsigned int mval;      /* Value of the last byte of mantissa */
677         unsigned int bmsign;    /* binary mask with sign */
678         unsigned int buflen;
679         unsigned int accum;
680         int expval;
681
682         if(!st) {
683                 errno = EINVAL;
684                 return -1;
685         }
686
687         /*
688          * ilogb(+-0) returns -INT_MAX or INT_MIN (platform-dependent)
689          * ilogb(+-inf) returns INT_MAX, logb(+-inf) returns +inf
690          * ilogb(NaN) returns INT_MIN or INT_MAX (platform-dependent)
691          */
692         expval = ilogb(dbl_value);
693         if(expval <= -INT_MAX   /* Also catches +-0 and maybe isnan() */
694         || expval == INT_MAX    /* catches isfin() and maybe isnan() */
695         ) {
696                 if(!st->buf || st->size < 2) {
697                         ptr = (uint8_t *)MALLOC(2);
698                         if(!ptr) return -1;
699                         if(st->buf) FREEMEM(st->buf);
700                         st->buf = ptr;
701                 }
702                 /* fpclassify(3) is not portable yet */
703                 if(asn_isnan(dbl_value)) {
704                         st->buf[0] = 0x42;      /* NaN */
705                         st->buf[1] = 0;
706                         st->size = 1;
707                 } else if(!asn_isfinite(dbl_value)) {
708                         if(copysign(1.0, dbl_value) < 0.0) {
709                                 st->buf[0] = 0x41;      /* MINUS-INFINITY */
710                         } else {
711                                 st->buf[0] = 0x40;      /* PLUS-INFINITY */
712                         }
713                         st->buf[1] = 0;
714                         st->size = 1;
715                 } else {
716                         if(copysign(1.0, dbl_value) >= 0.0) {
717                                 /* no content octets: positive zero */
718                                 st->buf[0] = 0; /* JIC */
719                                 st->size = 0;
720                         } else {
721                                 /* Negative zero. #8.5.3, 8.5.9 */
722                                 st->buf[0] = 0x43;
723                                 st->buf[1] = 0;
724                                 st->size = 1;
725                         }
726                 }
727                 return 0;
728         }
729
730         if(float_big_endian) {
731                 uint8_t *s = ((uint8_t *)&dbl_value) + 1;
732                 uint8_t *end = ((uint8_t *)&dbl_value) + sizeof(double);
733                 uint8_t *d;
734
735                 bmsign = 0x80 | ((s[-1] >> 1) & 0x40);  /* binary mask & - */
736                 for(mstop = d = dscr; s < end; d++, s++) {
737                         *d = *s;
738                         if(*d) mstop = d;
739                 }
740     } else {
741                 uint8_t *s = ((uint8_t *)&dbl_value) + sizeof(dbl_value) - 2;
742                 uint8_t *start = ((uint8_t *)&dbl_value);
743                 uint8_t *d;
744
745                 bmsign = 0x80 | ((s[1] >> 1) & 0x40);   /* binary mask & - */
746                 for(mstop = d = dscr; s >= start; d++, s--) {
747                         *d = *s;
748                         if(*d) mstop = d;
749                 }
750     }
751
752         /* Remove parts of the exponent, leave mantissa and explicit 1. */
753         dscr[0] = 0x10 | (dscr[0] & 0x0f);
754
755         /* Adjust exponent in a very unobvious way */
756         expval -= 8 * ((mstop - dscr) + 1) - 4;
757
758         /* This loop ensures DER conformance by forcing mantissa odd: 11.3.1 */
759         mval = *mstop;
760         if(mval && !(mval & 1)) {
761                 int shift_count = 1;
762                 int ishift;
763                 uint8_t *mptr;
764
765                 /*
766                  * Figure out what needs to be done to make mantissa odd.
767                  */
768                 if(!(mval & 0x0f))      /* Speed-up a little */
769                         shift_count = 4;
770                 while(((mval >> shift_count) & 1) == 0)
771                         shift_count++;
772
773                 ishift = 8 - shift_count;
774                 accum = 0;
775
776                 /* Go over the buffer, shifting it shift_count bits right. */
777                 for(mptr = dscr; mptr <= mstop; mptr++) {
778                         mval = *mptr;
779                         *mptr = accum | (mval >> shift_count);
780                         accum = mval << ishift;
781                 }
782
783                 /* Adjust exponent appropriately. */
784                 expval += shift_count;
785         }
786
787         if(expval < 0) {
788                 if((expval >> 7) == -1) {
789                         *ptr++ = bmsign | 0x00;
790                         *ptr++ = expval;
791                 } else if((expval >> 15) == -1) {
792                         *ptr++ = bmsign | 0x01;
793                         *ptr++ = expval >> 8;
794                         *ptr++ = expval;
795                 } else {
796                         *ptr++ = bmsign | 0x02;
797                         *ptr++ = expval >> 16;
798                         *ptr++ = expval >> 8;
799                         *ptr++ = expval;
800                 }
801         } else if(expval <= 0x7f) {
802                 *ptr++ = bmsign | 0x00;
803                 *ptr++ = expval;
804         } else if(expval <= 0x7fff) {
805                 *ptr++ = bmsign | 0x01;
806                 *ptr++ = expval >> 8;
807                 *ptr++ = expval;
808         } else {
809                 assert(expval <= 0x7fffff);
810                 *ptr++ = bmsign | 0x02;
811                 *ptr++ = expval >> 16;
812                 *ptr++ = expval >> 8;
813                 *ptr++ = expval;
814         }
815
816         buflen = (mstop - dscr) + 1;
817         memcpy(ptr, dscr, buflen);
818         ptr += buflen;
819         buflen = ptr - buf;
820
821         ptr = (uint8_t *)MALLOC(buflen + 1);
822         if(!ptr) return -1;
823
824         memcpy(ptr, buf, buflen);
825         buf[buflen] = 0;        /* JIC */
826
827         if(st->buf) FREEMEM(st->buf);
828         st->buf = ptr;
829         st->size = buflen;
830
831         return 0;
832 }
833
834 int CC_ATTR_NO_SANITIZE("float-cast-overflow")
835 asn_double2float(double d, float *outcome) {
836     float f = d;
837
838     *outcome = f;
839
840     if(asn_isfinite(d) == asn_isfinite(f)) {
841         return 0;
842     } else {
843         return -1;
844     }
845 }
846
847 #ifndef ASN_DISABLE_OER_SUPPORT
848
849 /*
850  * Encode as Canonical OER
851  */
852 asn_enc_rval_t
853 REAL_encode_oer(const asn_TYPE_descriptor_t *td,
854                 const asn_oer_constraints_t *constraints, const void *sptr,
855                 asn_app_consume_bytes_f *cb, void *app_key) {
856     const REAL_t *st = sptr;
857     asn_enc_rval_t er = {0,0,0};
858     ssize_t len_len;
859
860     if(!st || !st->buf || !td)
861         ASN__ENCODE_FAILED;
862
863     if(!constraints) constraints = td->encoding_constraints.oer_constraints;
864     if(constraints && constraints->value.width != 0) {
865         /* If we're constrained to a narrow float/double representation, we
866          * shouldn't have ended up using REAL. Expecting NativeReal. */
867         ASN__ENCODE_FAILED;
868     }
869
870     /* Encode a fake REAL */
871     len_len = oer_serialize_length(st->size, cb, app_key);
872     if(len_len < 0 || cb(st->buf, st->size, app_key) < 0) {
873         ASN__ENCODE_FAILED;
874     } else {
875         er.encoded = len_len + st->size;
876         ASN__ENCODED_OK(er);
877     }
878 }
879
880 asn_dec_rval_t
881 REAL_decode_oer(const asn_codec_ctx_t *opt_codec_ctx,
882                 const asn_TYPE_descriptor_t *td,
883                 const asn_oer_constraints_t *constraints, void **sptr,
884                 const void *ptr, size_t size) {
885     asn_dec_rval_t ok = {RC_OK, 0};
886     REAL_t *st;
887     uint8_t *buf;
888     ssize_t len_len;
889     size_t real_body_len;
890
891     (void)opt_codec_ctx;
892
893     if(!constraints) constraints = td->encoding_constraints.oer_constraints;
894     if(constraints && constraints->value.width != 0) {
895         /* If we're constrained to a narrow float/double representation, we
896          * shouldn't have ended up using REAL. Expecting NativeReal. */
897         ASN__DECODE_FAILED;
898     }
899
900     len_len = oer_fetch_length(ptr, size, &real_body_len);
901     if(len_len < 0) ASN__DECODE_FAILED;
902     if(len_len == 0) ASN__DECODE_STARVED;
903
904     ptr = (const char *)ptr + len_len;
905     size -= len_len;
906
907     if(real_body_len > size) ASN__DECODE_STARVED;
908
909     buf = CALLOC(1, real_body_len + 1);
910     if(!buf) ASN__DECODE_FAILED;
911
912     if(!(st = *sptr)) {
913         st = (*sptr = CALLOC(1, sizeof(REAL_t)));
914         if(!st) {
915             FREEMEM(buf);
916             ASN__DECODE_FAILED;
917         }
918     } else {
919         FREEMEM(st->buf);
920     }
921
922     memcpy(buf, ptr, real_body_len);
923     buf[real_body_len] = '\0';
924
925     st->buf = buf;
926     st->size = real_body_len;
927
928     ok.consumed = len_len + real_body_len;
929     return ok;
930 }
931
932 #endif  /* ASN_DISABLE_OER_SUPPORT */
933
934 #ifndef ASN_DISABLE_PER_SUPPORT
935
936 asn_dec_rval_t
937 REAL_decode_uper(const asn_codec_ctx_t *opt_codec_ctx,
938                  const asn_TYPE_descriptor_t *td,
939                  const asn_per_constraints_t *constraints, void **sptr,
940                  asn_per_data_t *pd) {
941     (void)constraints;  /* No PER visible constraints */
942         return OCTET_STRING_decode_uper(opt_codec_ctx, td, 0, sptr, pd);
943 }
944
945 asn_enc_rval_t
946 REAL_encode_uper(const asn_TYPE_descriptor_t *td,
947                  const asn_per_constraints_t *constraints, const void *sptr,
948                  asn_per_outp_t *po) {
949     (void)constraints;  /* No PER visible constraints */
950         return OCTET_STRING_encode_uper(td, 0, sptr, po);
951 }
952
953 asn_dec_rval_t
954 REAL_decode_aper(const asn_codec_ctx_t *opt_codec_ctx,
955                  const asn_TYPE_descriptor_t *td,
956                  const asn_per_constraints_t *constraints,
957                  void **sptr, asn_per_data_t *pd) {
958         (void)constraints;      /* No PER visible constraints */
959         return OCTET_STRING_decode_aper(opt_codec_ctx, td, 0, sptr, pd);
960 }
961
962 asn_enc_rval_t
963 REAL_encode_aper(const asn_TYPE_descriptor_t *td,
964                  const asn_per_constraints_t *constraints,
965                  const void *sptr, asn_per_outp_t *po) {
966         (void)constraints;      /* No PER visible constraints */
967         return OCTET_STRING_encode_aper(td, 0, sptr, po);
968 }
969
970 #endif  /* ASN_DISABLE_PER_SUPPORT */
971
972 asn_random_fill_result_t
973 REAL_random_fill(const asn_TYPE_descriptor_t *td, void **sptr,
974                        const asn_encoding_constraints_t *constraints,
975                        size_t max_length) {
976     asn_random_fill_result_t result_ok = {ARFILL_OK, 1};
977     asn_random_fill_result_t result_failed = {ARFILL_FAILED, 0};
978     asn_random_fill_result_t result_skipped = {ARFILL_SKIPPED, 0};
979     static const double values[] = {
980         0, -0.0, -1, 1, -M_E, M_E, -3.14, 3.14, -M_PI, M_PI, -255, 255,
981         /* 2^51 */
982         -2251799813685248.0, 2251799813685248.0,
983         /* 2^52 */
984         -4503599627370496.0, 4503599627370496.0,
985         /* 2^100 */
986         -1267650600228229401496703205376.0, 1267650600228229401496703205376.0,
987         -FLT_MIN, FLT_MIN,
988         -FLT_MAX, FLT_MAX,
989         -DBL_MIN, DBL_MIN,
990         -DBL_MAX, DBL_MAX,
991 #ifdef  FLT_TRUE_MIN
992         -FLT_TRUE_MIN, FLT_TRUE_MIN,
993 #endif
994 #ifdef  DBL_TRUE_MIN
995         -DBL_TRUE_MIN, DBL_TRUE_MIN,
996 #endif
997         INFINITY, -INFINITY, NAN};
998     REAL_t *st;
999     double d;
1000
1001     (void)constraints;
1002
1003     if(max_length == 0) return result_skipped;
1004
1005     d = values[asn_random_between(0, sizeof(values) / sizeof(values[0]) - 1)];
1006
1007     if(*sptr) {
1008         st = *sptr;
1009     } else {
1010         st = (REAL_t*)(*sptr = CALLOC(1, sizeof(REAL_t)));
1011         if(!st) {
1012             return result_failed;
1013         }
1014     }
1015
1016     if(asn_double2REAL(st, d)) {
1017         if(st == *sptr) {
1018             ASN_STRUCT_RESET(*td, st);
1019         } else {
1020             ASN_STRUCT_FREE(*td, st);
1021         }
1022         return result_failed;
1023     }
1024
1025     result_ok.length = st->size;
1026     return result_ok;
1027 }
1028