blob: e42a0613c759295b6fc2b507f640b9ee03ea2011 [file] [log] [blame]
vlm785435b2004-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 */
5#include <REAL.h>
6#include <INTEGER.h>
7#include <stdlib.h> /* for strtod(3) */
8#include <math.h>
9#include <errno.h>
10#include <assert.h>
11
12#undef INT_MAX
13#define INT_MAX ((int)(((unsigned int)-1) >> 1))
14
vlm1d3ed282004-09-14 13:40:42 +000015#ifndef INFINITY
16#define INFINITY HUGE_VAL
17#endif
18
19#ifndef NAN
20static const double nan0;
21#define NAN (nan0/nan0)
22#endif
23
vlm785435b2004-09-14 12:46:35 +000024/*
25 * REAL basic type description.
26 */
27static ber_tlv_tag_t asn1_DEF_REAL_tags[] = {
28 (ASN_TAG_CLASS_UNIVERSAL | (9 << 2))
29};
30asn1_TYPE_descriptor_t asn1_DEF_REAL = {
31 "REAL",
32 asn_generic_no_constraint,
33 INTEGER_decode_ber, /* Implemented in terms of INTEGER type */
34 INTEGER_encode_der,
35 REAL_print,
36 INTEGER_free,
37 0, /* Use generic outmost tag fetcher */
38 asn1_DEF_REAL_tags,
39 sizeof(asn1_DEF_REAL_tags) / sizeof(asn1_DEF_REAL_tags[0]),
40 asn1_DEF_REAL_tags, /* Same as above */
41 sizeof(asn1_DEF_REAL_tags) / sizeof(asn1_DEF_REAL_tags[0]),
42 0, /* Always in primitive form */
43 0, 0, /* No members */
44 0 /* No specifics */
45};
46
47int
48REAL_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
49 asn_app_consume_bytes_f *cb, void *app_key) {
50 const REAL_t *st = (const REAL_t *)sptr;
51 char buf[128];
52 double d;
53 int ret;
54
55 (void)td; /* Unused argument */
56 (void)ilevel; /* Unused argument */
57
58 if(!st)
59 return cb("<absent>", 8, app_key);
60
61 if(asn1_REAL2double(st, &d))
62 return cb("<error>", 7, app_key);
63
64 ret = snprintf(buf, sizeof(buf), "%f", d);
65 if(ret < 0 || ret >= sizeof(buf))
66 return cb("<error>", 7, app_key);
67
68 return cb(buf, ret, app_key);
69}
70
71int
72asn1_REAL2double(const REAL_t *st, double *dbl_value) {
73 unsigned long octv;
74
75 if(!st || !st->buf) {
76 errno = EINVAL;
77 return -1;
78 }
79
80 if(st->size == 0) {
81 *dbl_value = 0;
82 return 0;
83 }
84
85 octv = st->buf[0]; /* unsigned byte */
86
87 switch(octv & 0xC0) {
88 case 0x40: /* X.690: 8.5.8 */
89 /* "SpecialRealValue" */
90
91 /* Be liberal in what you accept...
92 if(st->size != 1) ...
93 */
94
95 switch(st->buf[0]) {
96 case 0x40: /* 01000000: PLUS-INFINITY */
97 *dbl_value = INFINITY;
98 return 0;
99 case 0x41: /* 01000001: MINUS-INFINITY */
100 *dbl_value = -INFINITY;
101 return 0;
102 /*
103 * The following cases are defined by
104 * X.690 Amendment 1 (10/03)
105 */
106 case 0x42: /* 01000010: NOT-A-NUMBER */
107 *dbl_value = NAN;
108 return 0;
109 case 0x43: /* 01000011: minus zero */
110 *dbl_value = NAN;
111 return 0;
112 }
113
114 errno = EINVAL;
115 return -1;
116 case 0x00: { /* X.690: 8.5.6 */
117 /*
118 * Decimal. NR{1,2,3} format.
119 */
120 double d;
121
122 assert(st->buf[st->size - 1] == 0); /* Security, vashu mat' */
123
124 d = strtod((char *)st->buf, 0);
125 if(finite(d)) {
126 *dbl_value = d;
127 return 0;
128 } else {
129 errno = ERANGE;
130 return 0;
131 }
132 }
133 }
134
135 /*
136 * Binary representation.
137 */
138 {
139 double m;
140 int expval; /* exponent value */
141 unsigned int elen; /* exponent value length, in octets */
142 unsigned int scaleF;
143 unsigned int baseF;
144 uint8_t *ptr;
145 uint8_t *end;
146 int sign;
147
148 switch((octv & 0x30) >> 4) {
149 case 0x00: baseF = 1; break; /* base 2 */
150 case 0x01: baseF = 3; break; /* base 8 */
151 case 0x02: baseF = 4; break; /* base 16 */
152 default:
153 /* Reserved field, can't parse now. */
154 errno = EINVAL;
155 return -1;
156 }
157
158 sign = (octv & 0x40); /* bit 7 */
159 scaleF = (octv & 0x0C) >> 2; /* bits 4 to 3 */
160
161 if(st->size <= (1 + (octv & 0x03))) {
162 errno = EINVAL;
163 return -1;
164 }
165
166 if((octv & 0x03) == 0x11) {
167 /* 8.5.6.4, case d) */
168 elen = st->buf[1]; /* unsigned binary number */
169 if(elen == 0 || st->size <= (2 + elen)) {
170 errno = EINVAL;
171 return -1;
172 }
173 ptr = &st->buf[2];
174 } else {
175 elen = (octv & 0x03);
176 ptr = &st->buf[1];
177 }
178
179 /* Fetch the multibyte exponent */
180 expval = (int)(*(int8_t *)ptr);
181 end = ptr + elen + 1;
182 for(ptr++; ptr < end; ptr++)
183 expval = (expval * 256) + *ptr;
184
185 m = 0.0; /* Initial mantissa value */
186
187 /* Okay, the exponent is here. Now, what about mantissa? */
188 end = st->buf + st->size;
189 if(ptr < end) {
190 for(; ptr < end; ptr++)
191 m = scalbn(m, 8) + *ptr;
192 }
193
194 ASN_DEBUG("m=%.10f, scF=%d, bF=%d, expval=%d, ldexp()=%f, scalbn()=%f",
195 m, scaleF, baseF, expval,
196 ldexp(m, expval * baseF + scaleF),
197 scalbn(m, scaleF) * pow(pow(2, baseF), expval)
198 );
199
200 /*
201 * (S * N * 2^F) * B^E
202 * Essentially:
203 m = scalbn(m, scaleF) * pow(pow(2, base), expval);
204 */
205 m = ldexp(m, expval * baseF + scaleF);
206 if(finite(m)) {
207 *dbl_value = sign ? -m : m;
208 } else {
209 errno = ERANGE;
210 return -1;
211 }
212
213 } /* if(binary_format) */
214
215 return 0;
216}
217
218/*
219 * Assume IEEE 754 floating point: standard 64 bit double.
220 * [1 bit sign] [11 bits exponent] [52 bits mantissa]
221 */
222int
223asn1_double2REAL(REAL_t *st, double dbl_value) {
224#ifdef WORDS_BIGENDIAN /* Known to be big-endian */
225 int littleEndian = 0;
226#else /* need to test: have no explicit information */
227 unsigned int LE = 1;
228 int littleEndian = *(unsigned char *)&LE;
229#endif
230 uint8_t buf[16]; /* More than enough for 8-byte dbl_value */
231 uint8_t dscr[sizeof(dbl_value)]; /* double value scratch pad */
232 /* Assertion guards: won't even compile, if unexpected double size */
233 char assertion_buffer1[9 - sizeof(dbl_value)] __attribute__((unused));
234 char assertion_buffer2[sizeof(dbl_value) - 7] __attribute__((unused));
235 uint8_t *ptr = buf;
236 uint8_t *mstop; /* Last byte of mantissa */
237 unsigned int mval; /* Value of the last byte of mantissa */
238 unsigned int bmsign; /* binary mask with sign */
239 unsigned int buflen;
240 unsigned int accum;
241 int expval;
242
243 if(!st) {
244 errno = EINVAL;
245 return -1;
246 }
247
vlm46361982004-09-14 13:58:10 +0000248 /*
249 * ilogb(+-0) returns -INT_MAX or INT_MIN (platform-dependent)
250 * ilogb(+-inf) returns INT_MAX
251 */
vlm785435b2004-09-14 12:46:35 +0000252 expval = ilogb(dbl_value);
253
vlm46361982004-09-14 13:58:10 +0000254 if(expval <= -INT_MAX /* Also catches (dbl_value == 0) */
vlm785435b2004-09-14 12:46:35 +0000255 || expval == INT_MAX /* catches finite() which catches isnan() */
256 ) {
257 if(!st->buf || st->size < 2) {
258 (void *)ptr = MALLOC(2);
259 if(!ptr) return -1;
260 st->buf = ptr;
261 }
262 /* fpclassify(3) is not portable yet */
vlm46361982004-09-14 13:58:10 +0000263 if(expval <= -INT_MAX) {
vlmb02ccb22004-09-14 13:50:21 +0000264 if(copysign(1.0, dbl_value) < 0.0) {
vlm785435b2004-09-14 12:46:35 +0000265 st->buf[0] = 0x80 | 0x40;
266 st->buf[1] = 0;
267 st->size = 2;
268 } else {
269 st->buf[0] = 0; /* JIC */
270 st->size = 0;
271 }
272 } else if(isinf(dbl_value)) {
vlmb02ccb22004-09-14 13:50:21 +0000273 if(copysign(1.0, dbl_value) < 0.0) {
vlm785435b2004-09-14 12:46:35 +0000274 st->buf[0] = 0x41; /* MINUS-INFINITY */
275 } else {
276 st->buf[0] = 0x40; /* PLUS-INFINITY */
277 }
278 st->buf[1] = 0;
279 st->size = 1;
280 } else {
281 st->buf[0] = 0x42; /* NaN */
282 st->buf[1] = 0;
283 st->size = 1;
284 }
285 return 0;
286 }
287
288 if(littleEndian) {
289 uint8_t *s = ((uint8_t *)&dbl_value) + sizeof(dbl_value) - 2;
vlm46361982004-09-14 13:58:10 +0000290 uint8_t *start = ((uint8_t *)&dbl_value);
vlm785435b2004-09-14 12:46:35 +0000291 uint8_t *d;
292
293 bmsign = 0x80 | ((s[1] >> 1) & 0x40); /* binary mask & - */
vlm46361982004-09-14 13:58:10 +0000294 for(mstop = d = dscr; s >= start; d++, s--) {
vlm785435b2004-09-14 12:46:35 +0000295 *d = *s;
296 if(*d) mstop = d;
297 }
298 } else {
299 uint8_t *s = ((uint8_t *)&dbl_value) + 1;
300 uint8_t *end = ((uint8_t *)&dbl_value) + sizeof(double);
301 uint8_t *d;
302
303 bmsign = 0x80 | ((s[-1] >> 1) & 0x40); /* binary mask & - */
304 for(mstop = d = dscr; s < end; d++, s++) {
305 *d = *s;
306 if(*d) mstop = d;
307 }
308 }
309
310 /* Remove parts of the exponent, leave mantissa and explicit 1. */
311 dscr[0] = 0x10 | (dscr[0] & 0x0f);
312
313 /* Adjust exponent in a very unobvious way */
314 expval -= 8 * ((mstop - dscr) + 1) - 4;
315
316 /* This loop ensures DER conformance by forcing mantissa odd: 11.3.1 */
317 mval = *mstop;
318 if(mval && !(mval & 1)) {
319 unsigned int shift_count = 1;
320 unsigned int ishift;
321 uint8_t *mptr;
322
323 /*
324 * Figure out what needs to be done to make mantissa odd.
325 */
326 if(!(mval & 0x0f)) /* Speed-up a little */
327 shift_count = 4;
328 while(((mval >> shift_count) & 1) == 0)
329 shift_count++;
330
331 ishift = 8 - shift_count;
332 accum = 0;
333
334 /* Go over the buffer, shifting it shift_count bits right. */
335 for(mptr = dscr; mptr <= mstop; mptr++) {
336 mval = *mptr;
337 *mptr = accum | (mval >> shift_count);
338 accum = mval << ishift;
339 }
340
341 /* Adjust mantissa appropriately. */
342 expval += shift_count;
343 }
344
345 if(expval < 0) {
346 if((expval >> 7) == -1) {
347 *ptr++ = bmsign | 0x00;
348 *ptr++ = expval;
349 } else if((expval >> 15) == -1) {
350 *ptr++ = bmsign | 0x01;
351 *ptr++ = expval >> 8;
352 *ptr++ = expval;
353 } else {
vlm785435b2004-09-14 12:46:35 +0000354 *ptr++ = bmsign | 0x02;
355 *ptr++ = expval >> 16;
356 *ptr++ = expval >> 8;
357 *ptr++ = expval;
358 }
359 } else if(expval <= 0x7f) {
360 *ptr++ = bmsign | 0x00;
361 *ptr++ = expval;
362 } else if(expval <= 0x7fff) {
363 *ptr++ = bmsign | 0x01;
364 *ptr++ = expval >> 8;
365 *ptr++ = expval;
366 } else {
367 assert(expval <= 0x7fffff);
368 *ptr++ = bmsign | 0x02;
369 *ptr++ = expval >> 16;
370 *ptr++ = expval >> 8;
371 *ptr++ = expval;
372 }
373
374 buflen = (mstop - dscr) + 1;
375 memcpy(ptr, dscr, buflen);
376 ptr += buflen;
377 buflen = ptr - buf;
378
379 (void *)ptr = MALLOC(buflen + 1);
380 if(!ptr) return -1;
381
382 memcpy(ptr, buf, buflen);
383 buf[buflen] = 0; /* JIC */
384
385 if(st->buf) FREEMEM(st->buf);
386 st->buf = ptr;
387 st->size = buflen;
388
389 return 0;
390}