REAL.c 17 KB
Newer Older
1
/*-
Lev Walkin's avatar
Lev Walkin committed
2
 * Copyright (c) 2004-2013 Lev Walkin <vlm@lionet.info>. All rights reserved.
3
4
 * Redistribution and modifications are permitted subject to BSD license.
 */
Lev Walkin's avatar
Lev Walkin committed
5
#define	_ISOC99_SOURCE		/* For ilogb() and quiet NAN */
6
#define	_BSD_SOURCE		/* To reintroduce finite(3) */
Lev Walkin's avatar
Lev Walkin committed
7
#if	defined(__alpha)
8
#include <sys/resource.h>	/* For INFINITY */
Lev Walkin's avatar
alpha64    
Lev Walkin committed
9
#endif
Lev Walkin's avatar
Lev Walkin committed
10
#include <asn_internal.h>
11
12
13
#include <stdlib.h>	/* for strtod(3) */
#include <math.h>
#include <errno.h>
Lev Walkin's avatar
alpha64    
Lev Walkin committed
14
#include <REAL.h>
Lev Walkin's avatar
Lev Walkin committed
15
#include <OCTET_STRING.h>
16
17
18
19

#undef	INT_MAX
#define	INT_MAX	((int)(((unsigned int)-1) >> 1))

20
#if	!(defined(NAN) || defined(INFINITY))
Lev Walkin's avatar
Lev Walkin committed
21
static volatile double real_zero GCC_NOTUSED = 0.0;
22
#endif
Lev Walkin's avatar
Lev Walkin committed
23
#ifndef	NAN
Lev Walkin's avatar
Lev Walkin committed
24
#define	NAN	(real_zero/real_zero)
Lev Walkin's avatar
Lev Walkin committed
25
#endif
26
27
28
#ifndef	INFINITY
#define	INFINITY	(1.0/real_zero)
#endif
Lev Walkin's avatar
Lev Walkin committed
29

30
31
32
/*
 * REAL basic type description.
 */
Lev Walkin's avatar
Lev Walkin committed
33
static ber_tlv_tag_t asn_DEF_REAL_tags[] = {
34
35
	(ASN_TAG_CLASS_UNIVERSAL | (9 << 2))
};
Lev Walkin's avatar
Lev Walkin committed
36
asn_TYPE_descriptor_t asn_DEF_REAL = {
Lev Walkin's avatar
Lev Walkin committed
37
	"REAL",
38
	"REAL",
39
	ASN__PRIMITIVE_TYPE_free,
Lev Walkin's avatar
Lev Walkin committed
40
	REAL_print,
41
	asn_generic_no_constraint,
42
43
	ber_decode_primitive,
	der_encode_primitive,
Lev Walkin's avatar
Lev Walkin committed
44
	REAL_decode_xer,
Lev Walkin's avatar
Lev Walkin committed
45
	REAL_encode_xer,
Lev Walkin's avatar
Lev Walkin committed
46
47
	REAL_decode_uper,
	REAL_encode_uper,
48
	0, /* Use generic outmost tag fetcher */
Lev Walkin's avatar
Lev Walkin committed
49
50
51
52
	asn_DEF_REAL_tags,
	sizeof(asn_DEF_REAL_tags) / sizeof(asn_DEF_REAL_tags[0]),
	asn_DEF_REAL_tags,	/* Same as above */
	sizeof(asn_DEF_REAL_tags) / sizeof(asn_DEF_REAL_tags[0]),
Lev Walkin's avatar
Lev Walkin committed
53
	0,	/* No PER visible constraints */
54
55
56
57
	0, 0,	/* No members */
	0	/* No specifics */
};

Lev Walkin's avatar
Lev Walkin committed
58
59
60
61
62
63
64
typedef enum specialRealValue {
	SRV__NOT_A_NUMBER,
	SRV__MINUS_INFINITY,
	SRV__PLUS_INFINITY
} specialRealValue_e;
static struct specialRealValue_s {
	char *string;
Lev Walkin's avatar
Lev Walkin committed
65
	size_t length;
66
	long dv;
Lev Walkin's avatar
Lev Walkin committed
67
68
} specialRealValue[] = {
#define	SRV_SET(foo, val)	{ foo, sizeof(foo) - 1, val }
69
70
71
	SRV_SET("<NOT-A-NUMBER/>", 0),
	SRV_SET("<MINUS-INFINITY/>", -1),
	SRV_SET("<PLUS-INFINITY/>", 1),
Lev Walkin's avatar
Lev Walkin committed
72
73
74
#undef	SRV_SET
};

Lev Walkin's avatar
Lev Walkin committed
75
76
ssize_t
REAL__dump(double d, int canonical, asn_app_consume_bytes_f *cb, void *app_key) {
Lev Walkin's avatar
Lev Walkin committed
77
	char local_buf[64];
Lev Walkin's avatar
Lev Walkin committed
78
79
	char *buf = local_buf;
	ssize_t buflen = sizeof(local_buf);
Lev Walkin's avatar
Lev Walkin committed
80
	const char *fmt = canonical?"%.15E":"%.15f";
Lev Walkin's avatar
Lev Walkin committed
81
82
	ssize_t ret;

83
84
85
	/*
	 * Check whether it is a special value.
	 */
Lev Walkin's avatar
Lev Walkin committed
86
87
	/* fpclassify(3) is not portable yet */
	if(isnan(d)) {
Lev Walkin's avatar
Lev Walkin committed
88
89
		buf = specialRealValue[SRV__NOT_A_NUMBER].string;
		buflen = specialRealValue[SRV__NOT_A_NUMBER].length;
Lev Walkin's avatar
Lev Walkin committed
90
		return (cb(buf, buflen, app_key) < 0) ? -1 : buflen;
91
	} else if(!finite(d)) {
Lev Walkin's avatar
Lev Walkin committed
92
		if(copysign(1.0, d) < 0.0) {
Lev Walkin's avatar
Lev Walkin committed
93
94
			buf = specialRealValue[SRV__MINUS_INFINITY].string;
			buflen = specialRealValue[SRV__MINUS_INFINITY].length;
Lev Walkin's avatar
Lev Walkin committed
95
		} else {
Lev Walkin's avatar
Lev Walkin committed
96
97
			buf = specialRealValue[SRV__PLUS_INFINITY].string;
			buflen = specialRealValue[SRV__PLUS_INFINITY].length;
Lev Walkin's avatar
Lev Walkin committed
98
99
100
101
102
103
104
105
106
		}
		return (cb(buf, buflen, app_key) < 0) ? -1 : buflen;
	} else if(ilogb(d) <= -INT_MAX) {
		if(copysign(1.0, d) < 0.0) {
			buf = "-0";
			buflen = 2;
		} else {
			buf = "0";
			buflen = 1;
107
108
109
110
111
112
113
		}
		return (cb(buf, buflen, app_key) < 0) ? -1 : buflen;
	}

	/*
	 * Use the libc's double printing, hopefully they got it right.
	 */
Lev Walkin's avatar
Lev Walkin committed
114
115
116
117
118
119
120
121
122
123
	do {
		ret = snprintf(buf, buflen, fmt, d);
		if(ret < 0) {
			buflen <<= 1;
		} else if(ret >= buflen) {
			buflen = ret + 1;
		} else {
			buflen = ret;
			break;
		}
Lev Walkin's avatar
clarity    
Lev Walkin committed
124
		if(buf != local_buf) FREEMEM(buf);
125
		buf = (char *)MALLOC(buflen);
Lev Walkin's avatar
Lev Walkin committed
126
127
128
129
		if(!buf) return -1;
	} while(1);

	if(canonical) {
Lev Walkin's avatar
Lev Walkin committed
130
131
		/*
		 * Transform the "[-]d.dddE+-dd" output into "[-]d.dddE[-]d"
Lev Walkin's avatar
Lev Walkin committed
132
		 * Check that snprintf() constructed the output correctly.
Lev Walkin's avatar
Lev Walkin committed
133
		 */
Lev Walkin's avatar
Lev Walkin committed
134
135
		char *dot, *E;
		char *end = buf + buflen;
Lev Walkin's avatar
Lev Walkin committed
136
		char *last_zero;
Lev Walkin's avatar
Lev Walkin committed
137

Lev Walkin's avatar
Lev Walkin committed
138
		dot = (buf[0] == 0x2d /* '-' */) ? (buf + 2) : (buf + 1);
Lev Walkin's avatar
Lev Walkin committed
139
		if(*dot >= 0x30) {
Lev Walkin's avatar
Lev Walkin committed
140
			if(buf != local_buf) FREEMEM(buf);
Lev Walkin's avatar
Lev Walkin committed
141
142
143
			errno = EINVAL;
			return -1;	/* Not a dot, really */
		}
Lev Walkin's avatar
Lev Walkin committed
144
		*dot = 0x2e;		/* Replace possible comma */
Lev Walkin's avatar
Lev Walkin committed
145

Lev Walkin's avatar
Lev Walkin committed
146
147
148
149
150
		for(last_zero = dot + 2, E = dot; dot < end; E++) {
			if(*E == 0x45) {
				char *expptr = ++E;
				char *s = expptr;
				int sign;
Lev Walkin's avatar
Lev Walkin committed
151
				if(*expptr == 0x2b /* '+' */) {
Lev Walkin's avatar
Lev Walkin committed
152
					/* Skip the "+" */
Lev Walkin's avatar
Lev Walkin committed
153
					buflen -= 1;
Lev Walkin's avatar
Lev Walkin committed
154
155
156
					sign = 0;
				} else {
					sign = 1;
Lev Walkin's avatar
Lev Walkin committed
157
158
					s++;
				}
Lev Walkin's avatar
Lev Walkin committed
159
160
				expptr++;
				if(expptr > end) {
Lev Walkin's avatar
Lev Walkin committed
161
					if(buf != local_buf) FREEMEM(buf);
Lev Walkin's avatar
Lev Walkin committed
162
163
164
					errno = EINVAL;
					return -1;
				}
Lev Walkin's avatar
Lev Walkin committed
165
166
167
168
169
170
				if(*expptr == 0x30) {
					buflen--;
					expptr++;
				}
				if(*last_zero == 0x30) {
					*last_zero = 0x45;	/* E */
Lev Walkin's avatar
Lev Walkin committed
171
					buflen -= s - (last_zero + 1);
Lev Walkin's avatar
Lev Walkin committed
172
					s = last_zero + 1;
Lev Walkin's avatar
Lev Walkin committed
173
174
175
176
					if(sign) {
						*s++ = 0x2d /* '-' */;
						buflen++;
					}
Lev Walkin's avatar
Lev Walkin committed
177
178
179
180
181
182
183
				}
				for(; expptr <= end; s++, expptr++)
					*s = *expptr;
				break;
			} else if(*E == 0x30) {
				if(*last_zero != 0x30)
					last_zero = E;
Lev Walkin's avatar
Lev Walkin committed
184
185
186
			}
		}
		if(E == end) {
Lev Walkin's avatar
Lev Walkin committed
187
			if(buf != local_buf) FREEMEM(buf);
Lev Walkin's avatar
Lev Walkin committed
188
189
190
			errno = EINVAL;
			return -1;		/* No promised E */
		}
Lev Walkin's avatar
Lev Walkin committed
191
192
193
194
195
196
	} else {
		/*
		 * Remove trailing zeros.
		 */
		char *end = buf + buflen;
		char *last_zero = end;
197
		int stoplooking = 0;
Lev Walkin's avatar
Lev Walkin committed
198
199
200
		char *z;
		for(z = end - 1; z > buf; z--) {
			switch(*z) {
201
202
203
204
			case 0x30:
				if(!stoplooking)
					last_zero = z;
				continue;
Lev Walkin's avatar
Lev Walkin committed
205
206
			case 0x31: case 0x32: case 0x33: case 0x34:
			case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
207
				stoplooking = 1;
Lev Walkin's avatar
Lev Walkin committed
208
209
				continue;
			default:	/* Catch dot and other separators */
210
211
212
213
214
				/*
				 * Replace possible comma (which may even
				 * be not a comma at all: locale-defined).
				 */
				*z = 0x2e;
Lev Walkin's avatar
Lev Walkin committed
215
216
217
218
219
220
221
222
223
				if(last_zero == z + 1) {	/* leave x.0 */
					last_zero++;
				}
				buflen = last_zero - buf;
				*last_zero = '\0';
				break;
			}
			break;
		}
Lev Walkin's avatar
Lev Walkin committed
224
225
226
	}

	ret = cb(buf, buflen, app_key);
Lev Walkin's avatar
clarity    
Lev Walkin committed
227
	if(buf != local_buf) FREEMEM(buf);
Lev Walkin's avatar
Lev Walkin committed
228
229
230
	return (ret < 0) ? -1 : buflen;
}

231
int
Lev Walkin's avatar
Lev Walkin committed
232
REAL_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
233
234
	asn_app_consume_bytes_f *cb, void *app_key) {
	const REAL_t *st = (const REAL_t *)sptr;
235
	ssize_t ret;
236
237
238
239
240
	double d;

	(void)td;	/* Unused argument */
	(void)ilevel;	/* Unused argument */

Lev Walkin's avatar
Lev Walkin committed
241
	if(!st || !st->buf)
242
		ret = cb("<absent>", 8, app_key);
Lev Walkin's avatar
Lev Walkin committed
243
	else if(asn_REAL2double(st, &d))
244
245
246
		ret = cb("<error>", 7, app_key);
	else
		ret = REAL__dump(d, 0, cb, app_key);
247

248
	return (ret < 0) ? -1 : 0;
Lev Walkin's avatar
Lev Walkin committed
249
250
251
}

asn_enc_rval_t
Lev Walkin's avatar
Lev Walkin committed
252
REAL_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
Lev Walkin's avatar
Lev Walkin committed
253
254
255
256
257
258
259
260
	int ilevel, enum xer_encoder_flags_e flags,
		asn_app_consume_bytes_f *cb, void *app_key) {
	REAL_t *st = (REAL_t *)sptr;
	asn_enc_rval_t er;
	double d;

	(void)ilevel;

Lev Walkin's avatar
Lev Walkin committed
261
	if(!st || !st->buf || asn_REAL2double(st, &d))
Lev Walkin's avatar
Lev Walkin committed
262
263
264
265
		_ASN_ENCODE_FAILED;

	er.encoded = REAL__dump(d, flags & XER_F_CANONICAL, cb, app_key);
	if(er.encoded < 0) _ASN_ENCODE_FAILED;
266

Lev Walkin's avatar
Lev Walkin committed
267
	_ASN_ENCODED_OK(er);
268
269
}

Lev Walkin's avatar
Lev Walkin committed
270
271
272
273

/*
 * Decode the chunk of XML text encoding REAL.
 */
274
275
static enum xer_pbd_rval
REAL__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) {
Lev Walkin's avatar
Lev Walkin committed
276
	REAL_t *st = (REAL_t *)sptr;
Lev Walkin's avatar
Lev Walkin committed
277
	double value;
278
	const char *xerdata = (const char *)chunk_buf;
Lev Walkin's avatar
Lev Walkin committed
279
280
281
	char *endptr = 0;
	char *b;

Lev Walkin's avatar
Lev Walkin committed
282
283
	(void)td;

284
	if(!chunk_size) return XPBD_BROKEN_ENCODING;
Lev Walkin's avatar
Lev Walkin committed
285
286
287
288
289
290
291
292
293

	/*
	 * Decode an XMLSpecialRealValue: <MINUS-INFINITY>, etc.
	 */
	if(xerdata[0] == 0x3c /* '<' */) {
		size_t i;
		for(i = 0; i < sizeof(specialRealValue)
				/ sizeof(specialRealValue[0]); i++) {
			struct specialRealValue_s *srv = &specialRealValue[i];
294
295
			double dv;

Lev Walkin's avatar
Lev Walkin committed
296
297
298
299
			if(srv->length != chunk_size
			|| memcmp(srv->string, chunk_buf, chunk_size))
				continue;

300
301
302
303
304
305
306
307
308
			/*
			 * It could've been done using
			 * (double)srv->dv / real_zero,
			 * but it summons fp exception on some platforms.
			 */
			switch(srv->dv) {
			case -1: dv = - INFINITY; break;
			case 0: dv = NAN;	break;
			case 1: dv = INFINITY;	break;
309
			default: return XPBD_SYSTEM_FAILURE;
310
311
			}

312
313
			if(asn_double2REAL(st, dv))
				return XPBD_SYSTEM_FAILURE;
Lev Walkin's avatar
Lev Walkin committed
314

315
			return XPBD_BODY_CONSUMED;
Lev Walkin's avatar
Lev Walkin committed
316
317
		}
		ASN_DEBUG("Unknown XMLSpecialRealValue");
318
		return XPBD_BROKEN_ENCODING;
Lev Walkin's avatar
Lev Walkin committed
319
320
321
322
323
	}

	/*
	 * Copy chunk into the nul-terminated string, and run strtod.
	 */
Lev Walkin's avatar
Lev Walkin committed
324
	b = (char *)MALLOC(chunk_size + 1);
325
	if(!b) return XPBD_SYSTEM_FAILURE;
Lev Walkin's avatar
Lev Walkin committed
326
	memcpy(b, chunk_buf, chunk_size);
Lev Walkin's avatar
Lev Walkin committed
327
	b[chunk_size] = 0;	/* nul-terminate */
Lev Walkin's avatar
Lev Walkin committed
328
329

	value = strtod(b, &endptr);
Lev Walkin's avatar
clarity    
Lev Walkin committed
330
	FREEMEM(b);
331
	if(endptr == b) return XPBD_BROKEN_ENCODING;
Lev Walkin's avatar
Lev Walkin committed
332
333

	if(asn_double2REAL(st, value))
334
		return XPBD_SYSTEM_FAILURE;
Lev Walkin's avatar
Lev Walkin committed
335

336
	return XPBD_BODY_CONSUMED;
Lev Walkin's avatar
Lev Walkin committed
337
338
339
340
341
}

asn_dec_rval_t
REAL_decode_xer(asn_codec_ctx_t *opt_codec_ctx,
	asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname,
Lev Walkin's avatar
Lev Walkin committed
342
		const void *buf_ptr, size_t size) {
Lev Walkin's avatar
Lev Walkin committed
343
344

	return xer_decode_primitive(opt_codec_ctx, td,
Lev Walkin's avatar
Lev Walkin committed
345
		sptr, sizeof(REAL_t), opt_mname,
Lev Walkin's avatar
Lev Walkin committed
346
347
348
		buf_ptr, size, REAL__xer_body_decode);
}

Lev Walkin's avatar
Lev Walkin committed
349
350
351
352
353
354
355
356
357
358
359
360
361
362
asn_dec_rval_t
REAL_decode_uper(asn_codec_ctx_t *opt_codec_ctx,
	asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints,
	void **sptr, asn_per_data_t *pd) {
	(void)constraints;	/* No PER visible constraints */
	return OCTET_STRING_decode_uper(opt_codec_ctx, td, 0, sptr, pd);
}

asn_enc_rval_t
REAL_encode_uper(asn_TYPE_descriptor_t *td,
	asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
	(void)constraints;	/* No PER visible constraints */
	return OCTET_STRING_encode_uper(td, 0, sptr, po);
}
Lev Walkin's avatar
Lev Walkin committed
363

364
int
Lev Walkin's avatar
Lev Walkin committed
365
asn_REAL2double(const REAL_t *st, double *dbl_value) {
Lev Walkin's avatar
Lev Walkin committed
366
	unsigned int octv;
367
368
369
370
371
372
373
374
375
376
377
378
379
380

	if(!st || !st->buf) {
		errno = EINVAL;
		return -1;
	}

	if(st->size == 0) {
		*dbl_value = 0;
		return 0;
	}

	octv = st->buf[0];	/* unsigned byte */

	switch(octv & 0xC0) {
381
	case 0x40:	/* X.690: 8.5.6 a) => 8.5.9 */
382
383
384
		/* "SpecialRealValue" */

		/* Be liberal in what you accept...
385
		 * http://en.wikipedia.org/wiki/Robustness_principle
386
387
388
389
390
		if(st->size != 1) ...
		*/

		switch(st->buf[0]) {
		case 0x40:	/* 01000000: PLUS-INFINITY */
391
			*dbl_value = INFINITY;
392
393
			return 0;
		case 0x41:	/* 01000001: MINUS-INFINITY */
394
			*dbl_value = - INFINITY;
395
396
397
398
399
			return 0;
		case 0x42:	/* 01000010: NOT-A-NUMBER */
			*dbl_value = NAN;
			return 0;
		case 0x43:	/* 01000011: minus zero */
Lev Walkin's avatar
Lev Walkin committed
400
			*dbl_value = -0.0;
401
402
403
404
405
			return 0;
		}

		errno = EINVAL;
		return -1;
406
	case 0x00: {	/* X.690: 8.5.7 */
407
		/*
408
409
410
411
		 * Decimal. NR{1,2,3} format from ISO 6093.
		 * NR1: [ ]*[+-]?[0-9]+
		 * NR2: [ ]*[+-]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)
		 * NR3: [ ]*[+-]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)[Ee][+-]?[0-9]+
412
413
		 */
		double d;
414
415
416
		char *buf;
		char *endptr;
		int used_malloc = 0;
417

418
		if(octv == 0 || (octv & 0x3C)) {
419
420
421
422
423
			/* Remaining values of bits 6 to 1 are Reserved. */
			errno = EINVAL;
			return -1;
		}

424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456

		/* 1. By contract, an input buffer should be null-terminated.
		 * OCTET STRING decoder ensures that, as is asn_double2REAL().
		 * 2. ISO 6093 specifies COMMA as a possible decimal separator.
		 * However, strtod() can't always deal with COMMA.
		 * So her we fix both by reallocating, copying and fixing.
		 */
		if(st->buf[st->size] || memchr(st->buf, ',', st->size)) {
			uint8_t *p, *end;
			char *b;
			if(st->size > 100) {
				/* Avoid malicious stack overflow in alloca() */
				buf = (char *)MALLOC(st->size);
				if(!buf) return -1;
				used_malloc = 1;
			} else {
				buf = alloca(st->size);
			}
			b = buf;
			/* Copy without the first byte and with 0-termination */
			for(p = st->buf + 1, end = st->buf + st->size;
					p < end; b++, p++)
				*b = (*p == ',') ? '.' : *p;
			*b = '\0';
		} else {
			buf = (char *)&st->buf[1];
		}

		endptr = buf;
		d = strtod(buf, &endptr);
		if(*endptr != '\0') {
			/* Format is not consistent with ISO 6093 */
			if(used_malloc) FREEMEM(buf);
457
458
459
			errno = EINVAL;
			return -1;
		}
460
		if(used_malloc) FREEMEM(buf);
461
462
463
464
465
		if(finite(d)) {
			*dbl_value = d;
			return 0;
		} else {
			errno = ERANGE;
466
			return -1;
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
		}
	  }
	}

	/*
	 * Binary representation.
	 */
    {
	double m;
	int expval;		/* exponent value */
	unsigned int elen;	/* exponent value length, in octets */
	unsigned int scaleF;
	unsigned int baseF;
	uint8_t *ptr;
	uint8_t *end;
	int sign;

	switch((octv & 0x30) >> 4) {
	case 0x00: baseF = 1; break;	/* base 2 */
	case 0x01: baseF = 3; break;	/* base 8 */
	case 0x02: baseF = 4; break;	/* base 16 */
	default:
		/* Reserved field, can't parse now. */
		errno = EINVAL;
		return -1;
	}

	sign = (octv & 0x40);	/* bit 7 */
	scaleF = (octv & 0x0C) >> 2;	/* bits 4 to 3 */

Lev Walkin's avatar
Lev Walkin committed
497
	if(st->size <= (int)(1 + (octv & 0x03))) {
498
499
500
501
		errno = EINVAL;
		return -1;
	}

502
503
	elen = (octv & 0x03);	/* bits 2 to 1; 8.5.6.4 */
	if(elen == 0x03) {	/* bits 2 to 1 = 11; 8.5.6.4, case d) */
504
		elen = st->buf[1];	/* unsigned binary number */
Lev Walkin's avatar
Lev Walkin committed
505
		if(elen == 0 || st->size <= (int)(2 + elen)) {
506
507
508
			errno = EINVAL;
			return -1;
		}
509
		/* FIXME: verify constraints of case d) */
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
		ptr = &st->buf[2];
	} else {
		ptr = &st->buf[1];
	}

	/* Fetch the multibyte exponent */
	expval = (int)(*(int8_t *)ptr);
	end = ptr + elen + 1;
	for(ptr++; ptr < end; ptr++)
		expval = (expval * 256) + *ptr;

	m = 0.0;	/* Initial mantissa value */

	/* Okay, the exponent is here. Now, what about mantissa? */
	end = st->buf + st->size;
525
526
	for(; ptr < end; ptr++)
		m = ldexp(m, 8) + *ptr;
527

Lev Walkin's avatar
Lev Walkin committed
528
	if(0)
529
	ASN_DEBUG("m=%.10f, scF=%d, bF=%d, expval=%d, ldexp()=%f, ldexp()=%f\n",
530
531
		m, scaleF, baseF, expval,
		ldexp(m, expval * baseF + scaleF),
532
		ldexp(m, scaleF) * pow(pow(2, baseF), expval)
533
534
535
536
537
	);

	/*
	 * (S * N * 2^F) * B^E
	 * Essentially:
538
	m = ldexp(m, scaleF) * pow(pow(2, base), expval);
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
	 */
	m = ldexp(m, expval * baseF + scaleF);
	if(finite(m)) {
		*dbl_value = sign ? -m : m;
	} else {
		errno = ERANGE;
		return -1;
	}

    } /* if(binary_format) */

	return 0;
}

/*
 * Assume IEEE 754 floating point: standard 64 bit double.
 * [1 bit sign]  [11 bits exponent]  [52 bits mantissa]
 */
int
Lev Walkin's avatar
Lev Walkin committed
558
asn_double2REAL(REAL_t *st, double dbl_value) {
559
560
561
562
563
564
565
566
567
#ifdef	WORDS_BIGENDIAN		/* Known to be big-endian */
	int littleEndian = 0;
#else				/* need to test: have no explicit information */
	unsigned int LE = 1;
	int littleEndian = *(unsigned char *)&LE;
#endif
	uint8_t buf[16];	/* More than enough for 8-byte dbl_value */
	uint8_t dscr[sizeof(dbl_value)];	/* double value scratch pad */
	/* Assertion guards: won't even compile, if unexpected double size */
Lev Walkin's avatar
Lev Walkin committed
568
569
	char assertion_buffer1[9 - sizeof(dbl_value)] GCC_NOTUSED;
	char assertion_buffer2[sizeof(dbl_value) - 7] GCC_NOTUSED;
570
571
572
573
574
575
576
577
578
579
580
581
582
	uint8_t *ptr = buf;
	uint8_t *mstop;		/* Last byte of mantissa */
	unsigned int mval;	/* Value of the last byte of mantissa */
	unsigned int bmsign;	/* binary mask with sign */
	unsigned int buflen;
	unsigned int accum;
	int expval;

	if(!st) {
		errno = EINVAL;
		return -1;
	}

583
584
	/*
	 * ilogb(+-0) returns -INT_MAX or INT_MIN (platform-dependent)
585
	 * ilogb(+-inf) returns INT_MAX, logb(+-inf) returns +inf
Lev Walkin's avatar
Lev Walkin committed
586
	 * ilogb(NaN) returns INT_MIN or INT_MAX (platform-dependent)
587
	 */
588
	expval = ilogb(dbl_value);
Lev Walkin's avatar
Lev Walkin committed
589
590
	if(expval <= -INT_MAX	/* Also catches +-0 and maybe isnan() */
	|| expval == INT_MAX	/* catches isfin() and maybe isnan() */
591
592
	) {
		if(!st->buf || st->size < 2) {
593
			ptr = (uint8_t *)MALLOC(2);
594
595
596
597
			if(!ptr) return -1;
			st->buf = ptr;
		}
		/* fpclassify(3) is not portable yet */
Lev Walkin's avatar
Lev Walkin committed
598
599
600
601
		if(isnan(dbl_value)) {
			st->buf[0] = 0x42;	/* NaN */
			st->buf[1] = 0;
			st->size = 1;
602
		} else if(!finite(dbl_value)) {
Lev Walkin's avatar
Lev Walkin committed
603
604
605
606
607
608
609
610
			if(copysign(1.0, dbl_value) < 0.0) {
				st->buf[0] = 0x41;	/* MINUS-INFINITY */
			} else {
				st->buf[0] = 0x40;	/* PLUS-INFINITY */
			}
			st->buf[1] = 0;
			st->size = 1;
		} else {
Lev Walkin's avatar
Lev Walkin committed
611
			if(copysign(1.0, dbl_value) >= 0.0) {
Lev Walkin's avatar
Lev Walkin committed
612
				/* no content octets: positive zero */
613
614
				st->buf[0] = 0;	/* JIC */
				st->size = 0;
Lev Walkin's avatar
Lev Walkin committed
615
616
617
618
			} else {
				/* Negative zero. #8.5.3, 8.5.9 */
				st->buf[0] = 0x43;
				st->size = 1;
619
620
621
622
623
624
625
			}
		}
		return 0;
	}

	if(littleEndian) {
		uint8_t *s = ((uint8_t *)&dbl_value) + sizeof(dbl_value) - 2;
626
		uint8_t *start = ((uint8_t *)&dbl_value);
627
628
629
		uint8_t *d;

		bmsign = 0x80 | ((s[1] >> 1) & 0x40);	/* binary mask & - */
630
		for(mstop = d = dscr; s >= start; d++, s--) {
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
			*d = *s;
			if(*d) mstop = d;
		}
	} else {
		uint8_t *s = ((uint8_t *)&dbl_value) + 1;
		uint8_t *end = ((uint8_t *)&dbl_value) + sizeof(double);
		uint8_t *d;

		bmsign = 0x80 | ((s[-1] >> 1) & 0x40);	/* binary mask & - */
		for(mstop = d = dscr; s < end; d++, s++) {
			*d = *s;
			if(*d) mstop = d;
		}
	}

	/* Remove parts of the exponent, leave mantissa and explicit 1. */
	dscr[0] = 0x10 | (dscr[0] & 0x0f);

	/* Adjust exponent in a very unobvious way */
	expval -= 8 * ((mstop - dscr) + 1) - 4;

	/* This loop ensures DER conformance by forcing mantissa odd: 11.3.1 */
	mval = *mstop;
	if(mval && !(mval & 1)) {
		unsigned int shift_count = 1;
		unsigned int ishift;
		uint8_t *mptr;

		/*
		 * Figure out what needs to be done to make mantissa odd.
		 */
		if(!(mval & 0x0f))	/* Speed-up a little */
			shift_count = 4;
		while(((mval >> shift_count) & 1) == 0)
			shift_count++;

		ishift = 8 - shift_count;
		accum = 0;

		/* Go over the buffer, shifting it shift_count bits right. */
		for(mptr = dscr; mptr <= mstop; mptr++) {
			mval = *mptr;
			*mptr = accum | (mval >> shift_count);
			accum = mval << ishift;
		}

Lev Walkin's avatar
Lev Walkin committed
677
		/* Adjust exponent appropriately. */
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
		expval += shift_count;
	}

	if(expval < 0) {
		if((expval >> 7) == -1) {
			*ptr++ = bmsign | 0x00;
			*ptr++ = expval;
		} else if((expval >> 15) == -1) {
			*ptr++ = bmsign | 0x01;
			*ptr++ = expval >> 8;
			*ptr++ = expval;
		} else {
			*ptr++ = bmsign | 0x02;
			*ptr++ = expval >> 16;
			*ptr++ = expval >> 8;
			*ptr++ = expval;
		}
	} else if(expval <= 0x7f) {
		*ptr++ = bmsign | 0x00;
		*ptr++ = expval;
	} else if(expval <= 0x7fff) {
		*ptr++ = bmsign | 0x01;
		*ptr++ = expval >> 8;
		*ptr++ = expval;
	} else {
		assert(expval <= 0x7fffff);
		*ptr++ = bmsign | 0x02;
		*ptr++ = expval >> 16;
		*ptr++ = expval >> 8;
		*ptr++ = expval;
	}

	buflen = (mstop - dscr) + 1;
	memcpy(ptr, dscr, buflen);
	ptr += buflen;
	buflen = ptr - buf;

Lev Walkin's avatar
Lev Walkin committed
715
	ptr = (uint8_t *)MALLOC(buflen + 1);
716
717
718
719
720
721
722
723
724
725
726
	if(!ptr) return -1;

	memcpy(ptr, buf, buflen);
	buf[buflen] = 0;	/* JIC */

	if(st->buf) FREEMEM(st->buf);
	st->buf = ptr;
	st->size = buflen;

	return 0;
}