blob: 7c230103e44a8fe7785fb3200bae35cc5b45622e [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 Walkina9cc46e2004-09-22 16:06:28 +00005#include <asn_internal.h>
Lev Walkin41ba1f22004-09-14 12:46:35 +00006#include <REAL.h>
7#include <INTEGER.h>
8#include <stdlib.h> /* for strtod(3) */
9#include <math.h>
10#include <errno.h>
11#include <assert.h>
12
13#undef INT_MAX
14#define INT_MAX ((int)(((unsigned int)-1) >> 1))
15
Lev Walkin40319a12004-09-14 13:40:42 +000016#ifndef INFINITY
17#define INFINITY HUGE_VAL
18#endif
19
20#ifndef NAN
21static const double nan0;
22#define NAN (nan0/nan0)
23#endif
24
Lev Walkin41ba1f22004-09-14 12:46:35 +000025/*
26 * REAL basic type description.
27 */
28static ber_tlv_tag_t asn1_DEF_REAL_tags[] = {
29 (ASN_TAG_CLASS_UNIVERSAL | (9 << 2))
30};
31asn1_TYPE_descriptor_t asn1_DEF_REAL = {
32 "REAL",
Lev Walkina9cc46e2004-09-22 16:06:28 +000033 INTEGER_free,
34 REAL_print,
Lev Walkin41ba1f22004-09-14 12:46:35 +000035 asn_generic_no_constraint,
36 INTEGER_decode_ber, /* Implemented in terms of INTEGER type */
37 INTEGER_encode_der,
Lev Walkina9cc46e2004-09-22 16:06:28 +000038 0, /* Not implemented yet */
39 REAL_encode_xer,
Lev Walkin41ba1f22004-09-14 12:46:35 +000040 0, /* Use generic outmost tag fetcher */
41 asn1_DEF_REAL_tags,
42 sizeof(asn1_DEF_REAL_tags) / sizeof(asn1_DEF_REAL_tags[0]),
43 asn1_DEF_REAL_tags, /* Same as above */
44 sizeof(asn1_DEF_REAL_tags) / sizeof(asn1_DEF_REAL_tags[0]),
45 0, /* Always in primitive form */
46 0, 0, /* No members */
47 0 /* No specifics */
48};
49
Lev Walkina9cc46e2004-09-22 16:06:28 +000050ssize_t
51REAL__dump(double d, int canonical, asn_app_consume_bytes_f *cb, void *app_key) {
52 char local_buf[32];
53 char *buf = local_buf;
54 ssize_t buflen = sizeof(local_buf);
55 const char *fmt = canonical?"%15E":"f";
56 ssize_t ret;
57
58 do {
59 ret = snprintf(buf, buflen, fmt, d);
60 if(ret < 0) {
61 buflen <<= 1;
62 } else if(ret >= buflen) {
63 buflen = ret + 1;
64 } else {
65 buflen = ret;
66 break;
67 }
68 if(buf != local_buf) free(buf);
69 (void *)buf = MALLOC(buflen);
70 if(!buf) return -1;
71 } while(1);
72
73 /*
74 * Transform the "[-]d.dddE+-dd" output into "[-]d.dddE[-]d"
75 */
76 if(canonical) {
77 char *dot, *E;
78 char *end = buf + buflen;
79
80 dot = (buf[0] == '-') ? (buf + 2) : (buf + 1);
81 if(*dot >= 0x30) {
82 errno = EINVAL;
83 return -1; /* Not a dot, really */
84 }
85 *dot = '.'; /* Replace possible comma */
86
87 for(E = dot; dot < end; E++) {
88 if(*E == 'E') {
89 char *s = ++E;
90 if(*E == '+') {
91 /* Skip the "+" too */
92 buflen -= 2;
93 } else {
94 buflen -= 1;
95 s++;
96 }
97 E += 2;
98 if(E[-1] != '0' || E > end) {
99 errno = EINVAL;
100 return -1;
101 }
102 for(; E <= end; s++, E++)
103 *s = *E;
104 }
105 }
106 if(E == end) {
107 errno = EINVAL;
108 return -1; /* No promised E */
109 }
110 }
111
112 ret = cb(buf, buflen, app_key);
113 if(buf != local_buf) free(buf);
114 return (ret < 0) ? -1 : buflen;
115}
116
Lev Walkin41ba1f22004-09-14 12:46:35 +0000117int
118REAL_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
119 asn_app_consume_bytes_f *cb, void *app_key) {
120 const REAL_t *st = (const REAL_t *)sptr;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000121 double d;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000122
123 (void)td; /* Unused argument */
124 (void)ilevel; /* Unused argument */
125
Lev Walkina9cc46e2004-09-22 16:06:28 +0000126 if(!st || !st->buf)
Lev Walkin41ba1f22004-09-14 12:46:35 +0000127 return cb("<absent>", 8, app_key);
128
129 if(asn1_REAL2double(st, &d))
130 return cb("<error>", 7, app_key);
131
Lev Walkina9cc46e2004-09-22 16:06:28 +0000132 return (REAL__dump(d, 0, cb, app_key) < 0) ? -1 : 0;
133}
Lev Walkin41ba1f22004-09-14 12:46:35 +0000134
Lev Walkina9cc46e2004-09-22 16:06:28 +0000135asn_enc_rval_t
136REAL_encode_xer(asn1_TYPE_descriptor_t *td, void *sptr,
137 int ilevel, enum xer_encoder_flags_e flags,
138 asn_app_consume_bytes_f *cb, void *app_key) {
139 REAL_t *st = (REAL_t *)sptr;
140 asn_enc_rval_t er;
141 double d;
142
143 (void)ilevel;
144
145 if(!st || !st->buf || asn1_REAL2double(st, &d))
146 _ASN_ENCODE_FAILED;
147
148 er.encoded = REAL__dump(d, flags & XER_F_CANONICAL, cb, app_key);
149 if(er.encoded < 0) _ASN_ENCODE_FAILED;
150
151 return er;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000152}
153
154int
155asn1_REAL2double(const REAL_t *st, double *dbl_value) {
Lev Walkina9cc46e2004-09-22 16:06:28 +0000156 unsigned int octv;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000157
158 if(!st || !st->buf) {
159 errno = EINVAL;
160 return -1;
161 }
162
163 if(st->size == 0) {
164 *dbl_value = 0;
165 return 0;
166 }
167
168 octv = st->buf[0]; /* unsigned byte */
169
170 switch(octv & 0xC0) {
171 case 0x40: /* X.690: 8.5.8 */
172 /* "SpecialRealValue" */
173
174 /* Be liberal in what you accept...
175 if(st->size != 1) ...
176 */
177
178 switch(st->buf[0]) {
179 case 0x40: /* 01000000: PLUS-INFINITY */
180 *dbl_value = INFINITY;
181 return 0;
182 case 0x41: /* 01000001: MINUS-INFINITY */
183 *dbl_value = -INFINITY;
184 return 0;
185 /*
186 * The following cases are defined by
187 * X.690 Amendment 1 (10/03)
188 */
189 case 0x42: /* 01000010: NOT-A-NUMBER */
190 *dbl_value = NAN;
191 return 0;
192 case 0x43: /* 01000011: minus zero */
193 *dbl_value = NAN;
194 return 0;
195 }
196
197 errno = EINVAL;
198 return -1;
199 case 0x00: { /* X.690: 8.5.6 */
200 /*
201 * Decimal. NR{1,2,3} format.
202 */
203 double d;
204
205 assert(st->buf[st->size - 1] == 0); /* Security, vashu mat' */
206
207 d = strtod((char *)st->buf, 0);
208 if(finite(d)) {
209 *dbl_value = d;
210 return 0;
211 } else {
212 errno = ERANGE;
213 return 0;
214 }
215 }
216 }
217
218 /*
219 * Binary representation.
220 */
221 {
222 double m;
223 int expval; /* exponent value */
224 unsigned int elen; /* exponent value length, in octets */
225 unsigned int scaleF;
226 unsigned int baseF;
227 uint8_t *ptr;
228 uint8_t *end;
229 int sign;
230
231 switch((octv & 0x30) >> 4) {
232 case 0x00: baseF = 1; break; /* base 2 */
233 case 0x01: baseF = 3; break; /* base 8 */
234 case 0x02: baseF = 4; break; /* base 16 */
235 default:
236 /* Reserved field, can't parse now. */
237 errno = EINVAL;
238 return -1;
239 }
240
241 sign = (octv & 0x40); /* bit 7 */
242 scaleF = (octv & 0x0C) >> 2; /* bits 4 to 3 */
243
Lev Walkina9cc46e2004-09-22 16:06:28 +0000244 if(st->size <= (int)(1 + (octv & 0x03))) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000245 errno = EINVAL;
246 return -1;
247 }
248
249 if((octv & 0x03) == 0x11) {
250 /* 8.5.6.4, case d) */
251 elen = st->buf[1]; /* unsigned binary number */
Lev Walkina9cc46e2004-09-22 16:06:28 +0000252 if(elen == 0 || st->size <= (int)(2 + elen)) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000253 errno = EINVAL;
254 return -1;
255 }
256 ptr = &st->buf[2];
257 } else {
258 elen = (octv & 0x03);
259 ptr = &st->buf[1];
260 }
261
262 /* Fetch the multibyte exponent */
263 expval = (int)(*(int8_t *)ptr);
264 end = ptr + elen + 1;
265 for(ptr++; ptr < end; ptr++)
266 expval = (expval * 256) + *ptr;
267
268 m = 0.0; /* Initial mantissa value */
269
270 /* Okay, the exponent is here. Now, what about mantissa? */
271 end = st->buf + st->size;
272 if(ptr < end) {
273 for(; ptr < end; ptr++)
274 m = scalbn(m, 8) + *ptr;
275 }
276
277 ASN_DEBUG("m=%.10f, scF=%d, bF=%d, expval=%d, ldexp()=%f, scalbn()=%f",
278 m, scaleF, baseF, expval,
279 ldexp(m, expval * baseF + scaleF),
280 scalbn(m, scaleF) * pow(pow(2, baseF), expval)
281 );
282
283 /*
284 * (S * N * 2^F) * B^E
285 * Essentially:
286 m = scalbn(m, scaleF) * pow(pow(2, base), expval);
287 */
288 m = ldexp(m, expval * baseF + scaleF);
289 if(finite(m)) {
290 *dbl_value = sign ? -m : m;
291 } else {
292 errno = ERANGE;
293 return -1;
294 }
295
296 } /* if(binary_format) */
297
298 return 0;
299}
300
301/*
302 * Assume IEEE 754 floating point: standard 64 bit double.
303 * [1 bit sign] [11 bits exponent] [52 bits mantissa]
304 */
305int
306asn1_double2REAL(REAL_t *st, double dbl_value) {
307#ifdef WORDS_BIGENDIAN /* Known to be big-endian */
308 int littleEndian = 0;
309#else /* need to test: have no explicit information */
310 unsigned int LE = 1;
311 int littleEndian = *(unsigned char *)&LE;
312#endif
313 uint8_t buf[16]; /* More than enough for 8-byte dbl_value */
314 uint8_t dscr[sizeof(dbl_value)]; /* double value scratch pad */
315 /* Assertion guards: won't even compile, if unexpected double size */
316 char assertion_buffer1[9 - sizeof(dbl_value)] __attribute__((unused));
317 char assertion_buffer2[sizeof(dbl_value) - 7] __attribute__((unused));
318 uint8_t *ptr = buf;
319 uint8_t *mstop; /* Last byte of mantissa */
320 unsigned int mval; /* Value of the last byte of mantissa */
321 unsigned int bmsign; /* binary mask with sign */
322 unsigned int buflen;
323 unsigned int accum;
324 int expval;
325
326 if(!st) {
327 errno = EINVAL;
328 return -1;
329 }
330
Lev Walkin057fb732004-09-14 13:58:10 +0000331 /*
332 * ilogb(+-0) returns -INT_MAX or INT_MIN (platform-dependent)
333 * ilogb(+-inf) returns INT_MAX
334 */
Lev Walkin41ba1f22004-09-14 12:46:35 +0000335 expval = ilogb(dbl_value);
336
Lev Walkin057fb732004-09-14 13:58:10 +0000337 if(expval <= -INT_MAX /* Also catches (dbl_value == 0) */
Lev Walkin41ba1f22004-09-14 12:46:35 +0000338 || expval == INT_MAX /* catches finite() which catches isnan() */
339 ) {
340 if(!st->buf || st->size < 2) {
341 (void *)ptr = MALLOC(2);
342 if(!ptr) return -1;
343 st->buf = ptr;
344 }
345 /* fpclassify(3) is not portable yet */
Lev Walkin057fb732004-09-14 13:58:10 +0000346 if(expval <= -INT_MAX) {
Lev Walkin7e03db92004-09-14 13:50:21 +0000347 if(copysign(1.0, dbl_value) < 0.0) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000348 st->buf[0] = 0x80 | 0x40;
349 st->buf[1] = 0;
350 st->size = 2;
351 } else {
352 st->buf[0] = 0; /* JIC */
353 st->size = 0;
354 }
355 } else if(isinf(dbl_value)) {
Lev Walkin7e03db92004-09-14 13:50:21 +0000356 if(copysign(1.0, dbl_value) < 0.0) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000357 st->buf[0] = 0x41; /* MINUS-INFINITY */
358 } else {
359 st->buf[0] = 0x40; /* PLUS-INFINITY */
360 }
361 st->buf[1] = 0;
362 st->size = 1;
363 } else {
364 st->buf[0] = 0x42; /* NaN */
365 st->buf[1] = 0;
366 st->size = 1;
367 }
368 return 0;
369 }
370
371 if(littleEndian) {
372 uint8_t *s = ((uint8_t *)&dbl_value) + sizeof(dbl_value) - 2;
Lev Walkin057fb732004-09-14 13:58:10 +0000373 uint8_t *start = ((uint8_t *)&dbl_value);
Lev Walkin41ba1f22004-09-14 12:46:35 +0000374 uint8_t *d;
375
376 bmsign = 0x80 | ((s[1] >> 1) & 0x40); /* binary mask & - */
Lev Walkin057fb732004-09-14 13:58:10 +0000377 for(mstop = d = dscr; s >= start; d++, s--) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000378 *d = *s;
379 if(*d) mstop = d;
380 }
381 } else {
382 uint8_t *s = ((uint8_t *)&dbl_value) + 1;
383 uint8_t *end = ((uint8_t *)&dbl_value) + sizeof(double);
384 uint8_t *d;
385
386 bmsign = 0x80 | ((s[-1] >> 1) & 0x40); /* binary mask & - */
387 for(mstop = d = dscr; s < end; d++, s++) {
388 *d = *s;
389 if(*d) mstop = d;
390 }
391 }
392
393 /* Remove parts of the exponent, leave mantissa and explicit 1. */
394 dscr[0] = 0x10 | (dscr[0] & 0x0f);
395
396 /* Adjust exponent in a very unobvious way */
397 expval -= 8 * ((mstop - dscr) + 1) - 4;
398
399 /* This loop ensures DER conformance by forcing mantissa odd: 11.3.1 */
400 mval = *mstop;
401 if(mval && !(mval & 1)) {
402 unsigned int shift_count = 1;
403 unsigned int ishift;
404 uint8_t *mptr;
405
406 /*
407 * Figure out what needs to be done to make mantissa odd.
408 */
409 if(!(mval & 0x0f)) /* Speed-up a little */
410 shift_count = 4;
411 while(((mval >> shift_count) & 1) == 0)
412 shift_count++;
413
414 ishift = 8 - shift_count;
415 accum = 0;
416
417 /* Go over the buffer, shifting it shift_count bits right. */
418 for(mptr = dscr; mptr <= mstop; mptr++) {
419 mval = *mptr;
420 *mptr = accum | (mval >> shift_count);
421 accum = mval << ishift;
422 }
423
424 /* Adjust mantissa appropriately. */
425 expval += shift_count;
426 }
427
428 if(expval < 0) {
429 if((expval >> 7) == -1) {
430 *ptr++ = bmsign | 0x00;
431 *ptr++ = expval;
432 } else if((expval >> 15) == -1) {
433 *ptr++ = bmsign | 0x01;
434 *ptr++ = expval >> 8;
435 *ptr++ = expval;
436 } else {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000437 *ptr++ = bmsign | 0x02;
438 *ptr++ = expval >> 16;
439 *ptr++ = expval >> 8;
440 *ptr++ = expval;
441 }
442 } else if(expval <= 0x7f) {
443 *ptr++ = bmsign | 0x00;
444 *ptr++ = expval;
445 } else if(expval <= 0x7fff) {
446 *ptr++ = bmsign | 0x01;
447 *ptr++ = expval >> 8;
448 *ptr++ = expval;
449 } else {
450 assert(expval <= 0x7fffff);
451 *ptr++ = bmsign | 0x02;
452 *ptr++ = expval >> 16;
453 *ptr++ = expval >> 8;
454 *ptr++ = expval;
455 }
456
457 buflen = (mstop - dscr) + 1;
458 memcpy(ptr, dscr, buflen);
459 ptr += buflen;
460 buflen = ptr - buf;
461
462 (void *)ptr = MALLOC(buflen + 1);
463 if(!ptr) return -1;
464
465 memcpy(ptr, buf, buflen);
466 buf[buflen] = 0; /* JIC */
467
468 if(st->buf) FREEMEM(st->buf);
469 st->buf = ptr;
470 st->size = buflen;
471
472 return 0;
473}