blob: bf03db3003676d47d0f07eafef6a1b6ba817e84b [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 */
260static ssize_t
Lev Walkin8471cec2004-10-21 14:02:19 +0000261REAL__xer_body_decode(void *sptr, void *chunk_buf, size_t chunk_size) {
262 REAL_t *st = (REAL_t *)sptr;
Lev Walkin5f560912004-10-21 13:37:57 +0000263 double value;
264 char *xerdata = (char *)chunk_buf;
265 char *endptr = 0;
266 char *b;
267
268 if(!chunk_size) return -1;
269
270 /*
271 * Decode an XMLSpecialRealValue: <MINUS-INFINITY>, etc.
272 */
273 if(xerdata[0] == 0x3c /* '<' */) {
274 size_t i;
275 for(i = 0; i < sizeof(specialRealValue)
276 / sizeof(specialRealValue[0]); i++) {
277 struct specialRealValue_s *srv = &specialRealValue[i];
Lev Walkin1aea6982004-10-26 09:35:25 +0000278 double dv;
279
Lev Walkin5f560912004-10-21 13:37:57 +0000280 if(srv->length != chunk_size
281 || memcmp(srv->string, chunk_buf, chunk_size))
282 continue;
283
Lev Walkin1aea6982004-10-26 09:35:25 +0000284 /*
285 * It could've been done using
286 * (double)srv->dv / real_zero,
287 * but it summons fp exception on some platforms.
288 */
289 switch(srv->dv) {
290 case -1: dv = - INFINITY; break;
291 case 0: dv = NAN; break;
292 case 1: dv = INFINITY; break;
293 default: return -1;
294 }
295
296 if(asn_double2REAL(st, dv)) return -1;
Lev Walkin5f560912004-10-21 13:37:57 +0000297
298 return chunk_size;
299 }
300 ASN_DEBUG("Unknown XMLSpecialRealValue");
301 return -1;
302 }
303
304 /*
305 * Copy chunk into the nul-terminated string, and run strtod.
306 */
Lev Walkin8471cec2004-10-21 14:02:19 +0000307 b = (char *)MALLOC(chunk_size + 1);
Lev Walkin5f560912004-10-21 13:37:57 +0000308 if(!b) return -1;
309 memcpy(b, chunk_buf, chunk_size);
Lev Walkin8471cec2004-10-21 14:02:19 +0000310 b[chunk_size] = 0; /* nul-terminate */
Lev Walkin5f560912004-10-21 13:37:57 +0000311
312 value = strtod(b, &endptr);
313 free(b);
314 if(endptr == b) return -1;
315
316 if(asn_double2REAL(st, value))
317 return -1;
318
319 return endptr - b;
320}
321
322asn_dec_rval_t
323REAL_decode_xer(asn_codec_ctx_t *opt_codec_ctx,
324 asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname,
325 void *buf_ptr, size_t size) {
326
327 return xer_decode_primitive(opt_codec_ctx, td,
Lev Walkin8471cec2004-10-21 14:02:19 +0000328 sptr, sizeof(REAL_t), opt_mname,
Lev Walkin5f560912004-10-21 13:37:57 +0000329 buf_ptr, size, REAL__xer_body_decode);
330}
331
332
Lev Walkin41ba1f22004-09-14 12:46:35 +0000333int
Lev Walkin5e033762004-09-29 13:26:15 +0000334asn_REAL2double(const REAL_t *st, double *dbl_value) {
Lev Walkina9cc46e2004-09-22 16:06:28 +0000335 unsigned int octv;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000336
337 if(!st || !st->buf) {
338 errno = EINVAL;
339 return -1;
340 }
341
342 if(st->size == 0) {
343 *dbl_value = 0;
344 return 0;
345 }
346
347 octv = st->buf[0]; /* unsigned byte */
348
349 switch(octv & 0xC0) {
350 case 0x40: /* X.690: 8.5.8 */
351 /* "SpecialRealValue" */
352
353 /* Be liberal in what you accept...
354 if(st->size != 1) ...
355 */
356
357 switch(st->buf[0]) {
358 case 0x40: /* 01000000: PLUS-INFINITY */
Lev Walkin1aea6982004-10-26 09:35:25 +0000359 *dbl_value = INFINITY;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000360 return 0;
361 case 0x41: /* 01000001: MINUS-INFINITY */
Lev Walkin1aea6982004-10-26 09:35:25 +0000362 *dbl_value = - INFINITY;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000363 return 0;
364 /*
365 * The following cases are defined by
366 * X.690 Amendment 1 (10/03)
367 */
368 case 0x42: /* 01000010: NOT-A-NUMBER */
369 *dbl_value = NAN;
370 return 0;
371 case 0x43: /* 01000011: minus zero */
Lev Walkin2a789d92004-09-27 21:36:59 +0000372 *dbl_value = -0.0;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000373 return 0;
374 }
375
376 errno = EINVAL;
377 return -1;
378 case 0x00: { /* X.690: 8.5.6 */
379 /*
380 * Decimal. NR{1,2,3} format.
381 */
382 double d;
383
384 assert(st->buf[st->size - 1] == 0); /* Security, vashu mat' */
385
386 d = strtod((char *)st->buf, 0);
387 if(finite(d)) {
388 *dbl_value = d;
389 return 0;
390 } else {
391 errno = ERANGE;
392 return 0;
393 }
394 }
395 }
396
397 /*
398 * Binary representation.
399 */
400 {
401 double m;
402 int expval; /* exponent value */
403 unsigned int elen; /* exponent value length, in octets */
404 unsigned int scaleF;
405 unsigned int baseF;
406 uint8_t *ptr;
407 uint8_t *end;
408 int sign;
409
410 switch((octv & 0x30) >> 4) {
411 case 0x00: baseF = 1; break; /* base 2 */
412 case 0x01: baseF = 3; break; /* base 8 */
413 case 0x02: baseF = 4; break; /* base 16 */
414 default:
415 /* Reserved field, can't parse now. */
416 errno = EINVAL;
417 return -1;
418 }
419
420 sign = (octv & 0x40); /* bit 7 */
421 scaleF = (octv & 0x0C) >> 2; /* bits 4 to 3 */
422
Lev Walkina9cc46e2004-09-22 16:06:28 +0000423 if(st->size <= (int)(1 + (octv & 0x03))) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000424 errno = EINVAL;
425 return -1;
426 }
427
428 if((octv & 0x03) == 0x11) {
429 /* 8.5.6.4, case d) */
430 elen = st->buf[1]; /* unsigned binary number */
Lev Walkina9cc46e2004-09-22 16:06:28 +0000431 if(elen == 0 || st->size <= (int)(2 + elen)) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000432 errno = EINVAL;
433 return -1;
434 }
435 ptr = &st->buf[2];
436 } else {
437 elen = (octv & 0x03);
438 ptr = &st->buf[1];
439 }
440
441 /* Fetch the multibyte exponent */
442 expval = (int)(*(int8_t *)ptr);
443 end = ptr + elen + 1;
444 for(ptr++; ptr < end; ptr++)
445 expval = (expval * 256) + *ptr;
446
447 m = 0.0; /* Initial mantissa value */
448
449 /* Okay, the exponent is here. Now, what about mantissa? */
450 end = st->buf + st->size;
451 if(ptr < end) {
452 for(; ptr < end; ptr++)
453 m = scalbn(m, 8) + *ptr;
454 }
455
Lev Walkin5f560912004-10-21 13:37:57 +0000456 if(0)
Lev Walkin41ba1f22004-09-14 12:46:35 +0000457 ASN_DEBUG("m=%.10f, scF=%d, bF=%d, expval=%d, ldexp()=%f, scalbn()=%f",
458 m, scaleF, baseF, expval,
459 ldexp(m, expval * baseF + scaleF),
460 scalbn(m, scaleF) * pow(pow(2, baseF), expval)
461 );
462
463 /*
464 * (S * N * 2^F) * B^E
465 * Essentially:
466 m = scalbn(m, scaleF) * pow(pow(2, base), expval);
467 */
468 m = ldexp(m, expval * baseF + scaleF);
469 if(finite(m)) {
470 *dbl_value = sign ? -m : m;
471 } else {
472 errno = ERANGE;
473 return -1;
474 }
475
476 } /* if(binary_format) */
477
478 return 0;
479}
480
481/*
482 * Assume IEEE 754 floating point: standard 64 bit double.
483 * [1 bit sign] [11 bits exponent] [52 bits mantissa]
484 */
485int
Lev Walkin5e033762004-09-29 13:26:15 +0000486asn_double2REAL(REAL_t *st, double dbl_value) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000487#ifdef WORDS_BIGENDIAN /* Known to be big-endian */
488 int littleEndian = 0;
489#else /* need to test: have no explicit information */
490 unsigned int LE = 1;
491 int littleEndian = *(unsigned char *)&LE;
492#endif
493 uint8_t buf[16]; /* More than enough for 8-byte dbl_value */
494 uint8_t dscr[sizeof(dbl_value)]; /* double value scratch pad */
495 /* Assertion guards: won't even compile, if unexpected double size */
496 char assertion_buffer1[9 - sizeof(dbl_value)] __attribute__((unused));
497 char assertion_buffer2[sizeof(dbl_value) - 7] __attribute__((unused));
498 uint8_t *ptr = buf;
499 uint8_t *mstop; /* Last byte of mantissa */
500 unsigned int mval; /* Value of the last byte of mantissa */
501 unsigned int bmsign; /* binary mask with sign */
502 unsigned int buflen;
503 unsigned int accum;
504 int expval;
505
506 if(!st) {
507 errno = EINVAL;
508 return -1;
509 }
510
Lev Walkin057fb732004-09-14 13:58:10 +0000511 /*
512 * ilogb(+-0) returns -INT_MAX or INT_MIN (platform-dependent)
513 * ilogb(+-inf) returns INT_MAX
Lev Walkin2a789d92004-09-27 21:36:59 +0000514 * ilogb(NaN) returns INT_MIN or INT_MAX (platform-dependent)
Lev Walkin057fb732004-09-14 13:58:10 +0000515 */
Lev Walkin41ba1f22004-09-14 12:46:35 +0000516 expval = ilogb(dbl_value);
Lev Walkin2a789d92004-09-27 21:36:59 +0000517 if(expval <= -INT_MAX /* Also catches +-0 and maybe isnan() */
518 || expval == INT_MAX /* catches isfin() and maybe isnan() */
Lev Walkin41ba1f22004-09-14 12:46:35 +0000519 ) {
520 if(!st->buf || st->size < 2) {
Lev Walkin8e8078a2004-09-26 13:10:40 +0000521 ptr = (uint8_t *)MALLOC(2);
Lev Walkin41ba1f22004-09-14 12:46:35 +0000522 if(!ptr) return -1;
523 st->buf = ptr;
524 }
525 /* fpclassify(3) is not portable yet */
Lev Walkin2a789d92004-09-27 21:36:59 +0000526 if(isnan(dbl_value)) {
527 st->buf[0] = 0x42; /* NaN */
528 st->buf[1] = 0;
529 st->size = 1;
Lev Walkina460ba32004-10-20 15:40:04 +0000530 } else if(!finite(dbl_value)) {
Lev Walkin7e03db92004-09-14 13:50:21 +0000531 if(copysign(1.0, dbl_value) < 0.0) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000532 st->buf[0] = 0x41; /* MINUS-INFINITY */
533 } else {
534 st->buf[0] = 0x40; /* PLUS-INFINITY */
535 }
536 st->buf[1] = 0;
537 st->size = 1;
Lev Walkinc51e7d62004-09-27 22:16:18 +0000538 } else {
539 if(copysign(1.0, dbl_value) < 0.0) {
540 st->buf[0] = 0x80 | 0x40;
541 st->buf[1] = 0;
542 st->size = 2;
543 } else {
544 /* no content octets: positive zero */
545 st->buf[0] = 0; /* JIC */
546 st->size = 0;
547 }
Lev Walkin41ba1f22004-09-14 12:46:35 +0000548 }
549 return 0;
550 }
551
552 if(littleEndian) {
553 uint8_t *s = ((uint8_t *)&dbl_value) + sizeof(dbl_value) - 2;
Lev Walkin057fb732004-09-14 13:58:10 +0000554 uint8_t *start = ((uint8_t *)&dbl_value);
Lev Walkin41ba1f22004-09-14 12:46:35 +0000555 uint8_t *d;
556
557 bmsign = 0x80 | ((s[1] >> 1) & 0x40); /* binary mask & - */
Lev Walkin057fb732004-09-14 13:58:10 +0000558 for(mstop = d = dscr; s >= start; d++, s--) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000559 *d = *s;
560 if(*d) mstop = d;
561 }
562 } else {
563 uint8_t *s = ((uint8_t *)&dbl_value) + 1;
564 uint8_t *end = ((uint8_t *)&dbl_value) + sizeof(double);
565 uint8_t *d;
566
567 bmsign = 0x80 | ((s[-1] >> 1) & 0x40); /* binary mask & - */
568 for(mstop = d = dscr; s < end; d++, s++) {
569 *d = *s;
570 if(*d) mstop = d;
571 }
572 }
573
574 /* Remove parts of the exponent, leave mantissa and explicit 1. */
575 dscr[0] = 0x10 | (dscr[0] & 0x0f);
576
577 /* Adjust exponent in a very unobvious way */
578 expval -= 8 * ((mstop - dscr) + 1) - 4;
579
580 /* This loop ensures DER conformance by forcing mantissa odd: 11.3.1 */
581 mval = *mstop;
582 if(mval && !(mval & 1)) {
583 unsigned int shift_count = 1;
584 unsigned int ishift;
585 uint8_t *mptr;
586
587 /*
588 * Figure out what needs to be done to make mantissa odd.
589 */
590 if(!(mval & 0x0f)) /* Speed-up a little */
591 shift_count = 4;
592 while(((mval >> shift_count) & 1) == 0)
593 shift_count++;
594
595 ishift = 8 - shift_count;
596 accum = 0;
597
598 /* Go over the buffer, shifting it shift_count bits right. */
599 for(mptr = dscr; mptr <= mstop; mptr++) {
600 mval = *mptr;
601 *mptr = accum | (mval >> shift_count);
602 accum = mval << ishift;
603 }
604
605 /* Adjust mantissa appropriately. */
606 expval += shift_count;
607 }
608
609 if(expval < 0) {
610 if((expval >> 7) == -1) {
611 *ptr++ = bmsign | 0x00;
612 *ptr++ = expval;
613 } else if((expval >> 15) == -1) {
614 *ptr++ = bmsign | 0x01;
615 *ptr++ = expval >> 8;
616 *ptr++ = expval;
617 } else {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000618 *ptr++ = bmsign | 0x02;
619 *ptr++ = expval >> 16;
620 *ptr++ = expval >> 8;
621 *ptr++ = expval;
622 }
623 } else if(expval <= 0x7f) {
624 *ptr++ = bmsign | 0x00;
625 *ptr++ = expval;
626 } else if(expval <= 0x7fff) {
627 *ptr++ = bmsign | 0x01;
628 *ptr++ = expval >> 8;
629 *ptr++ = expval;
630 } else {
631 assert(expval <= 0x7fffff);
632 *ptr++ = bmsign | 0x02;
633 *ptr++ = expval >> 16;
634 *ptr++ = expval >> 8;
635 *ptr++ = expval;
636 }
637
638 buflen = (mstop - dscr) + 1;
639 memcpy(ptr, dscr, buflen);
640 ptr += buflen;
641 buflen = ptr - buf;
642
Lev Walkinc17d90f2005-01-17 14:32:45 +0000643 ptr = (uint8_t *)MALLOC(buflen + 1);
Lev Walkin41ba1f22004-09-14 12:46:35 +0000644 if(!ptr) return -1;
645
646 memcpy(ptr, buf, buflen);
647 buf[buflen] = 0; /* JIC */
648
649 if(st->buf) FREEMEM(st->buf);
650 st->buf = ptr;
651 st->size = buflen;
652
653 return 0;
654}