blob: 4b73f4b766c26ecdcc67c697044257bcb9c58d8e [file] [log] [blame]
Lev Walkin41ba1f22004-09-14 12:46:35 +00001/*-
2 * Copyright (c) 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
3 * Redistribution and modifications are permitted subject to BSD license.
4 */
Lev Walkin33700162004-10-26 09:03:31 +00005#if defined(__alpha)
Lev Walkin1aea6982004-10-26 09:35:25 +00006#define _ISOC99_SOURCE /* For quiet NAN, through bits/nan.h */
7#define _BSD_SOURCE /* To reintroduce finite(3) */
8#include <sys/resource.h> /* For INFINITY */
Lev Walkin33700162004-10-26 09:03:31 +00009#endif
Lev Walkina9cc46e2004-09-22 16:06:28 +000010#include <asn_internal.h>
Lev Walkin41ba1f22004-09-14 12:46:35 +000011#include <stdlib.h> /* for strtod(3) */
12#include <math.h>
13#include <errno.h>
14#include <assert.h>
Lev Walkin33700162004-10-26 09:03:31 +000015#include <REAL.h>
Lev Walkin41ba1f22004-09-14 12:46:35 +000016
17#undef INT_MAX
18#define INT_MAX ((int)(((unsigned int)-1) >> 1))
19
Lev Walkin1aea6982004-10-26 09:35:25 +000020#if !(defined(NAN) || defined(INFINITY))
21static volatile double real_zero __attribute__ ((unused)) = 0.0;
22#endif
Lev Walkinae1bce92004-09-27 22:18:34 +000023#ifndef NAN
Lev Walkinc51e7d62004-09-27 22:16:18 +000024#define NAN (real_zero/real_zero)
Lev Walkin40319a12004-09-14 13:40:42 +000025#endif
Lev Walkin1aea6982004-10-26 09:35:25 +000026#ifndef INFINITY
27#define INFINITY (1.0/real_zero)
28#endif
Lev Walkin40319a12004-09-14 13:40:42 +000029
Lev Walkin41ba1f22004-09-14 12:46:35 +000030/*
31 * REAL basic type description.
32 */
Lev Walkin5e033762004-09-29 13:26:15 +000033static ber_tlv_tag_t asn_DEF_REAL_tags[] = {
Lev Walkin41ba1f22004-09-14 12:46:35 +000034 (ASN_TAG_CLASS_UNIVERSAL | (9 << 2))
35};
Lev Walkin5e033762004-09-29 13:26:15 +000036asn_TYPE_descriptor_t asn_DEF_REAL = {
Lev Walkin41ba1f22004-09-14 12:46:35 +000037 "REAL",
Lev Walkindc06f6b2004-10-20 15:50:55 +000038 "REAL",
Lev Walkin8e8078a2004-09-26 13:10:40 +000039 ASN__PRIMITIVE_TYPE_free,
Lev Walkina9cc46e2004-09-22 16:06:28 +000040 REAL_print,
Lev Walkin41ba1f22004-09-14 12:46:35 +000041 asn_generic_no_constraint,
Lev Walkin8e8078a2004-09-26 13:10:40 +000042 ber_decode_primitive,
43 der_encode_primitive,
Lev Walkin5f560912004-10-21 13:37:57 +000044 REAL_decode_xer,
Lev Walkina9cc46e2004-09-22 16:06:28 +000045 REAL_encode_xer,
Lev Walkin41ba1f22004-09-14 12:46:35 +000046 0, /* Use generic outmost tag fetcher */
Lev Walkin5e033762004-09-29 13:26:15 +000047 asn_DEF_REAL_tags,
48 sizeof(asn_DEF_REAL_tags) / sizeof(asn_DEF_REAL_tags[0]),
49 asn_DEF_REAL_tags, /* Same as above */
50 sizeof(asn_DEF_REAL_tags) / sizeof(asn_DEF_REAL_tags[0]),
Lev Walkin41ba1f22004-09-14 12:46:35 +000051 0, 0, /* No members */
52 0 /* No specifics */
53};
54
Lev Walkin5f560912004-10-21 13:37:57 +000055typedef enum specialRealValue {
56 SRV__NOT_A_NUMBER,
57 SRV__MINUS_INFINITY,
58 SRV__PLUS_INFINITY
59} specialRealValue_e;
60static struct specialRealValue_s {
61 char *string;
Lev Walkin8471cec2004-10-21 14:02:19 +000062 size_t length;
Lev Walkin1aea6982004-10-26 09:35:25 +000063 long dv;
Lev Walkin5f560912004-10-21 13:37:57 +000064} specialRealValue[] = {
65#define SRV_SET(foo, val) { foo, sizeof(foo) - 1, val }
Lev Walkin1aea6982004-10-26 09:35:25 +000066 SRV_SET("<NOT-A-NUMBER/>", 0),
67 SRV_SET("<MINUS-INFINITY/>", -1),
68 SRV_SET("<PLUS-INFINITY/>", 1),
Lev Walkin5f560912004-10-21 13:37:57 +000069#undef SRV_SET
70};
71
Lev Walkina9cc46e2004-09-22 16:06:28 +000072ssize_t
73REAL__dump(double d, int canonical, asn_app_consume_bytes_f *cb, void *app_key) {
Lev Walkin92b35d22004-09-27 20:52:18 +000074 char local_buf[64];
Lev Walkina9cc46e2004-09-22 16:06:28 +000075 char *buf = local_buf;
76 ssize_t buflen = sizeof(local_buf);
Lev Walkin92b35d22004-09-27 20:52:18 +000077 const char *fmt = canonical?"%.15E":"%.15f";
Lev Walkina9cc46e2004-09-22 16:06:28 +000078 ssize_t ret;
79
Lev Walkin8e8078a2004-09-26 13:10:40 +000080 /*
81 * Check whether it is a special value.
82 */
Lev Walkinc51e7d62004-09-27 22:16:18 +000083 /* fpclassify(3) is not portable yet */
84 if(isnan(d)) {
Lev Walkin5f560912004-10-21 13:37:57 +000085 buf = specialRealValue[SRV__NOT_A_NUMBER].string;
86 buflen = specialRealValue[SRV__NOT_A_NUMBER].length;
Lev Walkinc51e7d62004-09-27 22:16:18 +000087 return (cb(buf, buflen, app_key) < 0) ? -1 : buflen;
Lev Walkina460ba32004-10-20 15:40:04 +000088 } else if(!finite(d)) {
Lev Walkinc51e7d62004-09-27 22:16:18 +000089 if(copysign(1.0, d) < 0.0) {
Lev Walkin5f560912004-10-21 13:37:57 +000090 buf = specialRealValue[SRV__MINUS_INFINITY].string;
91 buflen = specialRealValue[SRV__MINUS_INFINITY].length;
Lev Walkinc51e7d62004-09-27 22:16:18 +000092 } else {
Lev Walkin5f560912004-10-21 13:37:57 +000093 buf = specialRealValue[SRV__PLUS_INFINITY].string;
94 buflen = specialRealValue[SRV__PLUS_INFINITY].length;
Lev Walkinc51e7d62004-09-27 22:16:18 +000095 }
96 return (cb(buf, buflen, app_key) < 0) ? -1 : buflen;
97 } else if(ilogb(d) <= -INT_MAX) {
98 if(copysign(1.0, d) < 0.0) {
99 buf = "-0";
100 buflen = 2;
101 } else {
102 buf = "0";
103 buflen = 1;
Lev Walkin8e8078a2004-09-26 13:10:40 +0000104 }
105 return (cb(buf, buflen, app_key) < 0) ? -1 : buflen;
106 }
107
108 /*
109 * Use the libc's double printing, hopefully they got it right.
110 */
Lev Walkina9cc46e2004-09-22 16:06:28 +0000111 do {
112 ret = snprintf(buf, buflen, fmt, d);
113 if(ret < 0) {
114 buflen <<= 1;
115 } else if(ret >= buflen) {
116 buflen = ret + 1;
117 } else {
118 buflen = ret;
119 break;
120 }
121 if(buf != local_buf) free(buf);
Lev Walkin8e8078a2004-09-26 13:10:40 +0000122 buf = (char *)MALLOC(buflen);
Lev Walkina9cc46e2004-09-22 16:06:28 +0000123 if(!buf) return -1;
124 } while(1);
125
Lev Walkina9cc46e2004-09-22 16:06:28 +0000126 if(canonical) {
Lev Walkin92b35d22004-09-27 20:52:18 +0000127 /*
128 * Transform the "[-]d.dddE+-dd" output into "[-]d.dddE[-]d"
Lev Walkin5f560912004-10-21 13:37:57 +0000129 * Check that snprintf() constructed the output correctly.
Lev Walkin92b35d22004-09-27 20:52:18 +0000130 */
Lev Walkina9cc46e2004-09-22 16:06:28 +0000131 char *dot, *E;
132 char *end = buf + buflen;
Lev Walkin92b35d22004-09-27 20:52:18 +0000133 char *last_zero;
Lev Walkina9cc46e2004-09-22 16:06:28 +0000134
Lev Walkin5f560912004-10-21 13:37:57 +0000135 dot = (buf[0] == 0x2d /* '-' */) ? (buf + 2) : (buf + 1);
Lev Walkina9cc46e2004-09-22 16:06:28 +0000136 if(*dot >= 0x30) {
137 errno = EINVAL;
138 return -1; /* Not a dot, really */
139 }
Lev Walkin92b35d22004-09-27 20:52:18 +0000140 *dot = 0x2e; /* Replace possible comma */
Lev Walkina9cc46e2004-09-22 16:06:28 +0000141
Lev Walkin92b35d22004-09-27 20:52:18 +0000142 for(last_zero = dot + 2, E = dot; dot < end; E++) {
143 if(*E == 0x45) {
144 char *expptr = ++E;
145 char *s = expptr;
146 int sign;
Lev Walkin5f560912004-10-21 13:37:57 +0000147 if(*expptr == 0x2b /* '+' */) {
Lev Walkin92b35d22004-09-27 20:52:18 +0000148 /* Skip the "+" */
Lev Walkina9cc46e2004-09-22 16:06:28 +0000149 buflen -= 1;
Lev Walkin92b35d22004-09-27 20:52:18 +0000150 sign = 0;
151 } else {
152 sign = 1;
Lev Walkina9cc46e2004-09-22 16:06:28 +0000153 s++;
154 }
Lev Walkin92b35d22004-09-27 20:52:18 +0000155 expptr++;
156 if(expptr > end) {
Lev Walkina9cc46e2004-09-22 16:06:28 +0000157 errno = EINVAL;
158 return -1;
159 }
Lev Walkin92b35d22004-09-27 20:52:18 +0000160 if(*expptr == 0x30) {
161 buflen--;
162 expptr++;
163 }
164 if(*last_zero == 0x30) {
165 *last_zero = 0x45; /* E */
Lev Walkin5f560912004-10-21 13:37:57 +0000166 buflen -= s - (last_zero + 1);
Lev Walkin92b35d22004-09-27 20:52:18 +0000167 s = last_zero + 1;
Lev Walkin5f560912004-10-21 13:37:57 +0000168 if(sign) {
169 *s++ = 0x2d /* '-' */;
170 buflen++;
171 }
Lev Walkin92b35d22004-09-27 20:52:18 +0000172 }
173 for(; expptr <= end; s++, expptr++)
174 *s = *expptr;
175 break;
176 } else if(*E == 0x30) {
177 if(*last_zero != 0x30)
178 last_zero = E;
Lev Walkina9cc46e2004-09-22 16:06:28 +0000179 }
180 }
181 if(E == end) {
182 errno = EINVAL;
183 return -1; /* No promised E */
184 }
Lev Walkin92b35d22004-09-27 20:52:18 +0000185 } else {
186 /*
187 * Remove trailing zeros.
188 */
189 char *end = buf + buflen;
190 char *last_zero = end;
191 char *z;
192 for(z = end - 1; z > buf; z--) {
193 switch(*z) {
194 case 0x030:
195 last_zero = z;
196 case 0x31: case 0x32: case 0x33: case 0x34:
197 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
198 continue;
199 default: /* Catch dot and other separators */
200 *z = 0x2e; /* Replace possible comma */
201 if(last_zero == z + 1) { /* leave x.0 */
202 last_zero++;
203 }
204 buflen = last_zero - buf;
205 *last_zero = '\0';
206 break;
207 }
208 break;
209 }
Lev Walkina9cc46e2004-09-22 16:06:28 +0000210 }
211
212 ret = cb(buf, buflen, app_key);
213 if(buf != local_buf) free(buf);
214 return (ret < 0) ? -1 : buflen;
215}
216
Lev Walkin41ba1f22004-09-14 12:46:35 +0000217int
Lev Walkin5e033762004-09-29 13:26:15 +0000218REAL_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
Lev Walkin41ba1f22004-09-14 12:46:35 +0000219 asn_app_consume_bytes_f *cb, void *app_key) {
220 const REAL_t *st = (const REAL_t *)sptr;
Lev Walkin8e8078a2004-09-26 13:10:40 +0000221 ssize_t ret;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000222 double d;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000223
224 (void)td; /* Unused argument */
225 (void)ilevel; /* Unused argument */
226
Lev Walkina9cc46e2004-09-22 16:06:28 +0000227 if(!st || !st->buf)
Lev Walkin8e8078a2004-09-26 13:10:40 +0000228 ret = cb("<absent>", 8, app_key);
Lev Walkin5e033762004-09-29 13:26:15 +0000229 else if(asn_REAL2double(st, &d))
Lev Walkin8e8078a2004-09-26 13:10:40 +0000230 ret = cb("<error>", 7, app_key);
231 else
232 ret = REAL__dump(d, 0, cb, app_key);
Lev Walkin41ba1f22004-09-14 12:46:35 +0000233
Lev Walkin8e8078a2004-09-26 13:10:40 +0000234 return (ret < 0) ? -1 : 0;
Lev Walkina9cc46e2004-09-22 16:06:28 +0000235}
Lev Walkin41ba1f22004-09-14 12:46:35 +0000236
Lev Walkina9cc46e2004-09-22 16:06:28 +0000237asn_enc_rval_t
Lev Walkin5e033762004-09-29 13:26:15 +0000238REAL_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
Lev Walkina9cc46e2004-09-22 16:06:28 +0000239 int ilevel, enum xer_encoder_flags_e flags,
240 asn_app_consume_bytes_f *cb, void *app_key) {
241 REAL_t *st = (REAL_t *)sptr;
242 asn_enc_rval_t er;
243 double d;
244
245 (void)ilevel;
246
Lev Walkin5e033762004-09-29 13:26:15 +0000247 if(!st || !st->buf || asn_REAL2double(st, &d))
Lev Walkina9cc46e2004-09-22 16:06:28 +0000248 _ASN_ENCODE_FAILED;
249
250 er.encoded = REAL__dump(d, flags & XER_F_CANONICAL, cb, app_key);
251 if(er.encoded < 0) _ASN_ENCODE_FAILED;
252
253 return er;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000254}
255
Lev Walkin5f560912004-10-21 13:37:57 +0000256
257/*
258 * Decode the chunk of XML text encoding REAL.
259 */
Lev Walkin0fab1a62005-03-09 22:19:25 +0000260static enum xer_pbd_rval
261REAL__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) {
Lev Walkin8471cec2004-10-21 14:02:19 +0000262 REAL_t *st = (REAL_t *)sptr;
Lev Walkin5f560912004-10-21 13:37:57 +0000263 double value;
Lev Walkin0fab1a62005-03-09 22:19:25 +0000264 const char *xerdata = (const char *)chunk_buf;
Lev Walkin5f560912004-10-21 13:37:57 +0000265 char *endptr = 0;
266 char *b;
267
Lev Walkine0b56e02005-02-25 12:10:27 +0000268 (void)td;
269
Lev Walkin0fab1a62005-03-09 22:19:25 +0000270 if(!chunk_size) return XPBD_BROKEN_ENCODING;
Lev Walkin5f560912004-10-21 13:37:57 +0000271
272 /*
273 * Decode an XMLSpecialRealValue: <MINUS-INFINITY>, etc.
274 */
275 if(xerdata[0] == 0x3c /* '<' */) {
276 size_t i;
277 for(i = 0; i < sizeof(specialRealValue)
278 / sizeof(specialRealValue[0]); i++) {
279 struct specialRealValue_s *srv = &specialRealValue[i];
Lev Walkin1aea6982004-10-26 09:35:25 +0000280 double dv;
281
Lev Walkin5f560912004-10-21 13:37:57 +0000282 if(srv->length != chunk_size
283 || memcmp(srv->string, chunk_buf, chunk_size))
284 continue;
285
Lev Walkin1aea6982004-10-26 09:35:25 +0000286 /*
287 * It could've been done using
288 * (double)srv->dv / real_zero,
289 * but it summons fp exception on some platforms.
290 */
291 switch(srv->dv) {
292 case -1: dv = - INFINITY; break;
293 case 0: dv = NAN; break;
294 case 1: dv = INFINITY; break;
Lev Walkin0fab1a62005-03-09 22:19:25 +0000295 default: return XPBD_SYSTEM_FAILURE;
Lev Walkin1aea6982004-10-26 09:35:25 +0000296 }
297
Lev Walkin0fab1a62005-03-09 22:19:25 +0000298 if(asn_double2REAL(st, dv))
299 return XPBD_SYSTEM_FAILURE;
Lev Walkin5f560912004-10-21 13:37:57 +0000300
Lev Walkin0fab1a62005-03-09 22:19:25 +0000301 return XPBD_BODY_CONSUMED;
Lev Walkin5f560912004-10-21 13:37:57 +0000302 }
303 ASN_DEBUG("Unknown XMLSpecialRealValue");
Lev Walkin0fab1a62005-03-09 22:19:25 +0000304 return XPBD_BROKEN_ENCODING;
Lev Walkin5f560912004-10-21 13:37:57 +0000305 }
306
307 /*
308 * Copy chunk into the nul-terminated string, and run strtod.
309 */
Lev Walkin8471cec2004-10-21 14:02:19 +0000310 b = (char *)MALLOC(chunk_size + 1);
Lev Walkin0fab1a62005-03-09 22:19:25 +0000311 if(!b) return XPBD_SYSTEM_FAILURE;
Lev Walkin5f560912004-10-21 13:37:57 +0000312 memcpy(b, chunk_buf, chunk_size);
Lev Walkin8471cec2004-10-21 14:02:19 +0000313 b[chunk_size] = 0; /* nul-terminate */
Lev Walkin5f560912004-10-21 13:37:57 +0000314
315 value = strtod(b, &endptr);
316 free(b);
Lev Walkin0fab1a62005-03-09 22:19:25 +0000317 if(endptr == b) return XPBD_BROKEN_ENCODING;
Lev Walkin5f560912004-10-21 13:37:57 +0000318
319 if(asn_double2REAL(st, value))
Lev Walkin0fab1a62005-03-09 22:19:25 +0000320 return XPBD_SYSTEM_FAILURE;
Lev Walkin5f560912004-10-21 13:37:57 +0000321
Lev Walkin0fab1a62005-03-09 22:19:25 +0000322 return XPBD_BODY_CONSUMED;
Lev Walkin5f560912004-10-21 13:37:57 +0000323}
324
325asn_dec_rval_t
326REAL_decode_xer(asn_codec_ctx_t *opt_codec_ctx,
327 asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname,
328 void *buf_ptr, size_t size) {
329
330 return xer_decode_primitive(opt_codec_ctx, td,
Lev Walkin8471cec2004-10-21 14:02:19 +0000331 sptr, sizeof(REAL_t), opt_mname,
Lev Walkin5f560912004-10-21 13:37:57 +0000332 buf_ptr, size, REAL__xer_body_decode);
333}
334
335
Lev Walkin41ba1f22004-09-14 12:46:35 +0000336int
Lev Walkin5e033762004-09-29 13:26:15 +0000337asn_REAL2double(const REAL_t *st, double *dbl_value) {
Lev Walkina9cc46e2004-09-22 16:06:28 +0000338 unsigned int octv;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000339
340 if(!st || !st->buf) {
341 errno = EINVAL;
342 return -1;
343 }
344
345 if(st->size == 0) {
346 *dbl_value = 0;
347 return 0;
348 }
349
350 octv = st->buf[0]; /* unsigned byte */
351
352 switch(octv & 0xC0) {
353 case 0x40: /* X.690: 8.5.8 */
354 /* "SpecialRealValue" */
355
356 /* Be liberal in what you accept...
357 if(st->size != 1) ...
358 */
359
360 switch(st->buf[0]) {
361 case 0x40: /* 01000000: PLUS-INFINITY */
Lev Walkin1aea6982004-10-26 09:35:25 +0000362 *dbl_value = INFINITY;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000363 return 0;
364 case 0x41: /* 01000001: MINUS-INFINITY */
Lev Walkin1aea6982004-10-26 09:35:25 +0000365 *dbl_value = - INFINITY;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000366 return 0;
367 /*
368 * The following cases are defined by
369 * X.690 Amendment 1 (10/03)
370 */
371 case 0x42: /* 01000010: NOT-A-NUMBER */
372 *dbl_value = NAN;
373 return 0;
374 case 0x43: /* 01000011: minus zero */
Lev Walkin2a789d92004-09-27 21:36:59 +0000375 *dbl_value = -0.0;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000376 return 0;
377 }
378
379 errno = EINVAL;
380 return -1;
381 case 0x00: { /* X.690: 8.5.6 */
382 /*
383 * Decimal. NR{1,2,3} format.
384 */
385 double d;
386
387 assert(st->buf[st->size - 1] == 0); /* Security, vashu mat' */
388
389 d = strtod((char *)st->buf, 0);
390 if(finite(d)) {
391 *dbl_value = d;
392 return 0;
393 } else {
394 errno = ERANGE;
395 return 0;
396 }
397 }
398 }
399
400 /*
401 * Binary representation.
402 */
403 {
404 double m;
405 int expval; /* exponent value */
406 unsigned int elen; /* exponent value length, in octets */
407 unsigned int scaleF;
408 unsigned int baseF;
409 uint8_t *ptr;
410 uint8_t *end;
411 int sign;
412
413 switch((octv & 0x30) >> 4) {
414 case 0x00: baseF = 1; break; /* base 2 */
415 case 0x01: baseF = 3; break; /* base 8 */
416 case 0x02: baseF = 4; break; /* base 16 */
417 default:
418 /* Reserved field, can't parse now. */
419 errno = EINVAL;
420 return -1;
421 }
422
423 sign = (octv & 0x40); /* bit 7 */
424 scaleF = (octv & 0x0C) >> 2; /* bits 4 to 3 */
425
Lev Walkina9cc46e2004-09-22 16:06:28 +0000426 if(st->size <= (int)(1 + (octv & 0x03))) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000427 errno = EINVAL;
428 return -1;
429 }
430
431 if((octv & 0x03) == 0x11) {
432 /* 8.5.6.4, case d) */
433 elen = st->buf[1]; /* unsigned binary number */
Lev Walkina9cc46e2004-09-22 16:06:28 +0000434 if(elen == 0 || st->size <= (int)(2 + elen)) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000435 errno = EINVAL;
436 return -1;
437 }
438 ptr = &st->buf[2];
439 } else {
440 elen = (octv & 0x03);
441 ptr = &st->buf[1];
442 }
443
444 /* Fetch the multibyte exponent */
445 expval = (int)(*(int8_t *)ptr);
446 end = ptr + elen + 1;
447 for(ptr++; ptr < end; ptr++)
448 expval = (expval * 256) + *ptr;
449
450 m = 0.0; /* Initial mantissa value */
451
452 /* Okay, the exponent is here. Now, what about mantissa? */
453 end = st->buf + st->size;
454 if(ptr < end) {
455 for(; ptr < end; ptr++)
456 m = scalbn(m, 8) + *ptr;
457 }
458
Lev Walkin5f560912004-10-21 13:37:57 +0000459 if(0)
Lev Walkin41ba1f22004-09-14 12:46:35 +0000460 ASN_DEBUG("m=%.10f, scF=%d, bF=%d, expval=%d, ldexp()=%f, scalbn()=%f",
461 m, scaleF, baseF, expval,
462 ldexp(m, expval * baseF + scaleF),
463 scalbn(m, scaleF) * pow(pow(2, baseF), expval)
464 );
465
466 /*
467 * (S * N * 2^F) * B^E
468 * Essentially:
469 m = scalbn(m, scaleF) * pow(pow(2, base), expval);
470 */
471 m = ldexp(m, expval * baseF + scaleF);
472 if(finite(m)) {
473 *dbl_value = sign ? -m : m;
474 } else {
475 errno = ERANGE;
476 return -1;
477 }
478
479 } /* if(binary_format) */
480
481 return 0;
482}
483
484/*
485 * Assume IEEE 754 floating point: standard 64 bit double.
486 * [1 bit sign] [11 bits exponent] [52 bits mantissa]
487 */
488int
Lev Walkin5e033762004-09-29 13:26:15 +0000489asn_double2REAL(REAL_t *st, double dbl_value) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000490#ifdef WORDS_BIGENDIAN /* Known to be big-endian */
491 int littleEndian = 0;
492#else /* need to test: have no explicit information */
493 unsigned int LE = 1;
494 int littleEndian = *(unsigned char *)&LE;
495#endif
496 uint8_t buf[16]; /* More than enough for 8-byte dbl_value */
497 uint8_t dscr[sizeof(dbl_value)]; /* double value scratch pad */
498 /* Assertion guards: won't even compile, if unexpected double size */
499 char assertion_buffer1[9 - sizeof(dbl_value)] __attribute__((unused));
500 char assertion_buffer2[sizeof(dbl_value) - 7] __attribute__((unused));
501 uint8_t *ptr = buf;
502 uint8_t *mstop; /* Last byte of mantissa */
503 unsigned int mval; /* Value of the last byte of mantissa */
504 unsigned int bmsign; /* binary mask with sign */
505 unsigned int buflen;
506 unsigned int accum;
507 int expval;
508
509 if(!st) {
510 errno = EINVAL;
511 return -1;
512 }
513
Lev Walkin057fb732004-09-14 13:58:10 +0000514 /*
515 * ilogb(+-0) returns -INT_MAX or INT_MIN (platform-dependent)
516 * ilogb(+-inf) returns INT_MAX
Lev Walkin2a789d92004-09-27 21:36:59 +0000517 * ilogb(NaN) returns INT_MIN or INT_MAX (platform-dependent)
Lev Walkin057fb732004-09-14 13:58:10 +0000518 */
Lev Walkin41ba1f22004-09-14 12:46:35 +0000519 expval = ilogb(dbl_value);
Lev Walkin2a789d92004-09-27 21:36:59 +0000520 if(expval <= -INT_MAX /* Also catches +-0 and maybe isnan() */
521 || expval == INT_MAX /* catches isfin() and maybe isnan() */
Lev Walkin41ba1f22004-09-14 12:46:35 +0000522 ) {
523 if(!st->buf || st->size < 2) {
Lev Walkin8e8078a2004-09-26 13:10:40 +0000524 ptr = (uint8_t *)MALLOC(2);
Lev Walkin41ba1f22004-09-14 12:46:35 +0000525 if(!ptr) return -1;
526 st->buf = ptr;
527 }
528 /* fpclassify(3) is not portable yet */
Lev Walkin2a789d92004-09-27 21:36:59 +0000529 if(isnan(dbl_value)) {
530 st->buf[0] = 0x42; /* NaN */
531 st->buf[1] = 0;
532 st->size = 1;
Lev Walkina460ba32004-10-20 15:40:04 +0000533 } else if(!finite(dbl_value)) {
Lev Walkin7e03db92004-09-14 13:50:21 +0000534 if(copysign(1.0, dbl_value) < 0.0) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000535 st->buf[0] = 0x41; /* MINUS-INFINITY */
536 } else {
537 st->buf[0] = 0x40; /* PLUS-INFINITY */
538 }
539 st->buf[1] = 0;
540 st->size = 1;
Lev Walkinc51e7d62004-09-27 22:16:18 +0000541 } else {
542 if(copysign(1.0, dbl_value) < 0.0) {
543 st->buf[0] = 0x80 | 0x40;
544 st->buf[1] = 0;
545 st->size = 2;
546 } else {
547 /* no content octets: positive zero */
548 st->buf[0] = 0; /* JIC */
549 st->size = 0;
550 }
Lev Walkin41ba1f22004-09-14 12:46:35 +0000551 }
552 return 0;
553 }
554
555 if(littleEndian) {
556 uint8_t *s = ((uint8_t *)&dbl_value) + sizeof(dbl_value) - 2;
Lev Walkin057fb732004-09-14 13:58:10 +0000557 uint8_t *start = ((uint8_t *)&dbl_value);
Lev Walkin41ba1f22004-09-14 12:46:35 +0000558 uint8_t *d;
559
560 bmsign = 0x80 | ((s[1] >> 1) & 0x40); /* binary mask & - */
Lev Walkin057fb732004-09-14 13:58:10 +0000561 for(mstop = d = dscr; s >= start; d++, s--) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000562 *d = *s;
563 if(*d) mstop = d;
564 }
565 } else {
566 uint8_t *s = ((uint8_t *)&dbl_value) + 1;
567 uint8_t *end = ((uint8_t *)&dbl_value) + sizeof(double);
568 uint8_t *d;
569
570 bmsign = 0x80 | ((s[-1] >> 1) & 0x40); /* binary mask & - */
571 for(mstop = d = dscr; s < end; d++, s++) {
572 *d = *s;
573 if(*d) mstop = d;
574 }
575 }
576
577 /* Remove parts of the exponent, leave mantissa and explicit 1. */
578 dscr[0] = 0x10 | (dscr[0] & 0x0f);
579
580 /* Adjust exponent in a very unobvious way */
581 expval -= 8 * ((mstop - dscr) + 1) - 4;
582
583 /* This loop ensures DER conformance by forcing mantissa odd: 11.3.1 */
584 mval = *mstop;
585 if(mval && !(mval & 1)) {
586 unsigned int shift_count = 1;
587 unsigned int ishift;
588 uint8_t *mptr;
589
590 /*
591 * Figure out what needs to be done to make mantissa odd.
592 */
593 if(!(mval & 0x0f)) /* Speed-up a little */
594 shift_count = 4;
595 while(((mval >> shift_count) & 1) == 0)
596 shift_count++;
597
598 ishift = 8 - shift_count;
599 accum = 0;
600
601 /* Go over the buffer, shifting it shift_count bits right. */
602 for(mptr = dscr; mptr <= mstop; mptr++) {
603 mval = *mptr;
604 *mptr = accum | (mval >> shift_count);
605 accum = mval << ishift;
606 }
607
608 /* Adjust mantissa appropriately. */
609 expval += shift_count;
610 }
611
612 if(expval < 0) {
613 if((expval >> 7) == -1) {
614 *ptr++ = bmsign | 0x00;
615 *ptr++ = expval;
616 } else if((expval >> 15) == -1) {
617 *ptr++ = bmsign | 0x01;
618 *ptr++ = expval >> 8;
619 *ptr++ = expval;
620 } else {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000621 *ptr++ = bmsign | 0x02;
622 *ptr++ = expval >> 16;
623 *ptr++ = expval >> 8;
624 *ptr++ = expval;
625 }
626 } else if(expval <= 0x7f) {
627 *ptr++ = bmsign | 0x00;
628 *ptr++ = expval;
629 } else if(expval <= 0x7fff) {
630 *ptr++ = bmsign | 0x01;
631 *ptr++ = expval >> 8;
632 *ptr++ = expval;
633 } else {
634 assert(expval <= 0x7fffff);
635 *ptr++ = bmsign | 0x02;
636 *ptr++ = expval >> 16;
637 *ptr++ = expval >> 8;
638 *ptr++ = expval;
639 }
640
641 buflen = (mstop - dscr) + 1;
642 memcpy(ptr, dscr, buflen);
643 ptr += buflen;
644 buflen = ptr - buf;
645
Lev Walkinc17d90f2005-01-17 14:32:45 +0000646 ptr = (uint8_t *)MALLOC(buflen + 1);
Lev Walkin41ba1f22004-09-14 12:46:35 +0000647 if(!ptr) return -1;
648
649 memcpy(ptr, buf, buflen);
650 buf[buflen] = 0; /* JIC */
651
652 if(st->buf) FREEMEM(st->buf);
653 st->buf = ptr;
654 st->size = buflen;
655
656 return 0;
657}