]>
Commit | Line | Data |
---|---|---|
bac41a7b A |
1 | /* |
2 | * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. | |
3 | * | |
4 | * The contents of this file constitute Original Code as defined in and are | |
5 | * subject to the Apple Public Source License Version 1.2 (the 'License'). | |
6 | * You may not use this file except in compliance with the License. Please obtain | |
7 | * a copy of the License at http://www.apple.com/publicsource and read it before | |
8 | * using this file. | |
9 | * | |
10 | * This Original Code and all software distributed under the License are | |
11 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS | |
12 | * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT | |
13 | * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | |
14 | * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the | |
15 | * specific language governing rights and limitations under the License. | |
16 | */ | |
17 | ||
18 | ||
19 | /* | |
20 | * asn_real.c - BER encode, decode, print and free routines for ASN.1 REAL type. | |
21 | * | |
22 | * MS 92 | |
23 | * Copyright (C) 1992 Michael Sample and the University of British Columbia | |
24 | * | |
25 | * This library is free software; you can redistribute it and/or | |
26 | * modify it provided that this copyright/license information is retained | |
27 | * in original form. | |
28 | * | |
29 | * If you modify this file, you must clearly indicate your changes. | |
30 | * | |
31 | * This source code is distributed in the hope that it will be | |
32 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
33 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
34 | * | |
5a719ac8 | 35 | * $Header: /cvs/Darwin/src/live/Security/SecuritySNACCRuntime/c-lib/src/asn-real.c,v 1.1.1.1 2001/05/18 23:14:08 mb Exp $ |
bac41a7b A |
36 | * $Log: asn-real.c,v $ |
37 | * Revision 1.1.1.1 2001/05/18 23:14:08 mb | |
38 | * Move from private repository to open source repository | |
39 | * | |
40 | * Revision 1.2 2001/05/05 00:59:25 rmurphy | |
41 | * Adding darwin license headers | |
42 | * | |
43 | * Revision 1.1.1.1 1999/03/16 18:06:31 aram | |
44 | * Originals from SMIME Free Library. | |
45 | * | |
46 | * Revision 1.5 1997/02/28 13:39:50 wan | |
47 | * Modifications collected for new version 1.3: Bug fixes, tk4.2. | |
48 | * | |
49 | * Revision 1.4 1995/07/24 21:04:54 rj | |
50 | * changed `_' to `-' in file names. | |
51 | * | |
52 | * Revision 1.3 1995/02/18 16:25:13 rj | |
53 | * added support for CPU/compiler combination presenting 64 bit little endian long integers | |
54 | * (in addition to the aforesupported 32 bit big endian long ints). | |
55 | * | |
56 | * Revision 1.2 1994/09/01 00:06:28 rj | |
57 | * reduce the risk of unwanted surprises with macro expansion by properly separating the C tokens. | |
58 | * | |
59 | * Revision 1.1 1994/08/28 09:46:00 rj | |
60 | * first check-in. for a list of changes to the snacc-1.1 distribution please refer to the ChangeLog. | |
61 | * | |
62 | */ | |
63 | ||
64 | #include "asn-config.h" | |
65 | #include "asn-len.h" | |
66 | #include "asn-tag.h" | |
67 | #include "asn-real.h" | |
68 | ||
69 | double pow PROTO ((double base, double exp)); | |
70 | ||
71 | #ifdef IEEE_REAL_LIB | |
72 | /* ieee functions (in case not in math.h)*/ | |
73 | extern int iszero (double); | |
74 | extern int isinf (double); | |
75 | extern int signbit (double); | |
76 | extern int ilogb (double); | |
77 | extern double scalbn (double, int); | |
78 | #endif | |
79 | ||
80 | ||
81 | /* | |
82 | * You must call InitAsnInfinity() to initailize these values | |
83 | * (necessary if you deal with REAL values.) | |
84 | */ | |
85 | AsnReal PLUS_INFINITY; | |
86 | AsnReal MINUS_INFINITY; | |
87 | ||
88 | ||
89 | #define ENC_PLUS_INFINITY 0x40 | |
90 | #define ENC_MINUS_INFINITY 0x41 | |
91 | ||
92 | #define REAL_BINARY 0x80 | |
93 | #define REAL_SIGN 0x40 | |
94 | #define REAL_EXPLEN_MASK 0x03 | |
95 | #define REAL_EXPLEN_1 0x00 | |
96 | #define REAL_EXPLEN_2 0x01 | |
97 | #define REAL_EXPLEN_3 0x02 | |
98 | #define REAL_EXPLEN_LONG 0x03 | |
99 | #define REAL_FACTOR_MASK 0x0c | |
100 | #define REAL_BASE_MASK 0x30 | |
101 | #define REAL_BASE_2 0x00 | |
102 | #define REAL_BASE_8 0x10 | |
103 | #define REAL_BASE_16 0x20 | |
104 | ||
105 | ||
106 | /* | |
107 | * Returns the smallest octet length needed to | |
108 | * hold the given long int value | |
109 | */ | |
110 | unsigned int | |
111 | SignedIntOctetLen PARAMS ((val), | |
112 | long int val) | |
113 | { | |
114 | unsigned long int mask = (0x7f80L << ((sizeof (long int) - 2) * 8)); | |
115 | unsigned int retVal = sizeof (long int); | |
116 | ||
117 | if (val < 0) | |
118 | val = val ^ (~0L); /* XOR val with all 1's */ | |
119 | ||
120 | while ((retVal > 1) && ((val & mask) == 0)) | |
121 | { | |
122 | mask >>= 8; | |
123 | retVal--; | |
124 | } | |
125 | ||
126 | return retVal; | |
127 | ||
128 | } /* SignedIntOctetLen */ | |
129 | ||
130 | ||
131 | /* | |
132 | * encodes universal TAG LENGTH and Contents of and ASN.1 REAL | |
133 | */ | |
134 | AsnLen | |
135 | BEncAsnReal PARAMS ((b, data), | |
136 | BUF_TYPE b _AND_ | |
137 | AsnReal *data) | |
138 | { | |
139 | AsnLen len; | |
140 | ||
141 | len = BEncAsnRealContent (b, data); | |
142 | len += BEncDefLen (b, len); | |
143 | len += BEncTag1 (b, UNIV, PRIM, REAL_TAG_CODE); | |
144 | return len; | |
145 | } /* BEncAsnReal */ | |
146 | ||
147 | ||
148 | /* | |
149 | * decodes universal TAG LENGTH and Contents of and ASN.1 REAL | |
150 | */ | |
151 | void | |
152 | BDecAsnReal PARAMS ((b, result, bytesDecoded, env), | |
153 | BUF_TYPE b _AND_ | |
154 | AsnReal *result _AND_ | |
155 | AsnLen *bytesDecoded _AND_ | |
156 | jmp_buf env) | |
157 | { | |
158 | AsnTag tag; | |
159 | AsnLen elmtLen; | |
160 | ||
161 | if ((tag = BDecTag (b, bytesDecoded, env)) != MAKE_TAG_ID (UNIV, PRIM, REAL_TAG_CODE)) | |
162 | { | |
163 | Asn1Error ("BDecAsnReal: ERROR wrong tag on REAL.\n"); | |
164 | longjmp (env, -40); | |
165 | } | |
166 | ||
167 | elmtLen = BDecLen (b, bytesDecoded, env); | |
168 | BDecAsnRealContent (b, tag, elmtLen, result, bytesDecoded, env); | |
169 | ||
170 | } /* BDecAsnReal */ | |
171 | ||
172 | ||
173 | ||
174 | #ifdef IEEE_REAL_FMT | |
175 | ||
176 | /* | |
177 | * Inits the PLUS_INFINITY and MINUS_INFINITY globals assuming | |
178 | * that the double is an IEEE DOUBLE | |
179 | * The bits for MINUS_INFINITY are 0xfff0000000000000 | |
180 | * The bits for PLUS_INFINITY are 0x7ff0000000000000 | |
181 | */ | |
182 | void | |
183 | InitAsnInfinity() | |
184 | { | |
185 | unsigned char *c = (unsigned char *)&PLUS_INFINITY; | |
186 | int i; | |
187 | ||
188 | if (sizeof (double) != 8) | |
189 | Asn1Error ("InitAsnInfinity: ERROR expected sizeof (AsnReal) to be 8"); | |
190 | ||
191 | #if WORDS_BIGENDIAN /* Big endian */ | |
192 | c[0] = 0x7f; | |
193 | c[1] = 0xf0; | |
194 | for (i = 2; i < sizeof (double); i++) | |
195 | c[i] = 0; | |
196 | #else /* Little endian */ | |
197 | c[7] = 0x7f; | |
198 | c[6] = 0xf0; | |
199 | for (i = 0; i < 6; i++) | |
200 | c[i] = 0; | |
201 | #endif | |
202 | ||
203 | MINUS_INFINITY = -PLUS_INFINITY; | |
204 | } /* InitAsnInfinity */ | |
205 | ||
206 | /* | |
207 | * Encodes the content of an ASN.1 REAL value to the given buffer. | |
208 | * This version of the routine ASSUMES that the C rep. of a double | |
209 | * is the same as the IEEE std. | |
210 | */ | |
211 | AsnLen | |
212 | BEncAsnRealContent PARAMS ((b, value), | |
213 | BUF_TYPE b _AND_ | |
214 | AsnReal *value) | |
215 | { | |
216 | int exponent; | |
217 | int isNeg; | |
218 | #if SIZEOF_LONG == 4 | |
219 | unsigned char *dbl; | |
220 | unsigned long int *first4; | |
221 | unsigned long int *second4; | |
222 | #else | |
223 | #if SIZEOF_LONG == 8 | |
224 | unsigned long mantissa, val, *p; | |
225 | int i; | |
226 | #endif | |
227 | #endif | |
228 | ||
229 | /* no contents for 0.0 reals */ | |
230 | if (*value == 0.0) /* all bits zero, disregarding top/sign bit */ | |
231 | return 0; | |
232 | ||
233 | #if SIZEOF_LONG == 4 | |
234 | #if !WORDS_BIGENDIAN | |
235 | #error sorry! this 32 bit code requires big endianess. | |
236 | #endif | |
237 | ||
238 | /* this code is designed to work were longs are 32 bit wide and big endian */ | |
239 | ||
240 | dbl = (unsigned char *) value; | |
241 | ||
242 | first4 = (unsigned long int *) dbl; | |
243 | second4 = (unsigned long int *) (dbl + sizeof (long int)); | |
244 | ||
245 | isNeg = dbl[0] & 0x80; | |
246 | ||
247 | /* special real values for +/- oo */ | |
248 | if (((*first4 & 0x7fffffff) == 0x7ff00000) && (*second4 == 0)) | |
249 | { | |
250 | if (isNeg) | |
251 | { | |
252 | BufPutByteRvs (b, ENC_MINUS_INFINITY); | |
253 | } | |
254 | else | |
255 | { | |
256 | BufPutByteRvs (b, ENC_PLUS_INFINITY); | |
257 | } | |
258 | return 1; | |
259 | } | |
260 | else /* encode a binary real value */ | |
261 | { | |
262 | exponent = (((*first4) >> 20) & 0x07ff); | |
263 | ||
264 | /* write the mantissa (N value) */ | |
265 | BufPutSegRvs (b, (char *)(dbl+2), sizeof (double)-2); | |
266 | ||
267 | /* | |
268 | * The rightmost 4 bits of a double 2nd octet are the | |
269 | * most sig bits of the mantissa. | |
270 | * write the most signficant byte of the asn1 real mantissa, | |
271 | * adding implicit bit to 'left of decimal' if not de-normalized | |
272 | * (de normalized if exponent == 0) | |
273 | * | |
274 | * if the double is not in de-normalized form subtract 1023 | |
275 | * from the exponent to get proper signed exponent. | |
276 | * | |
277 | * for both the normalized and de-norm forms | |
278 | * correct the exponent by subtracting 52 since: | |
279 | * 1. mantissa is 52 bits in the double (56 in ASN.1 REAL form) | |
280 | * 2. implicit decimal at the beginning of double's mantissa | |
281 | * 3. ASN.1 REAL's implicit decimal is after its mantissa | |
282 | * so converting the double mantissa to the ASN.1 form has the | |
283 | * effect of multiplying it by 2^52. Subtracting 52 from the | |
284 | * exponent corrects this. | |
285 | */ | |
286 | if (exponent == 0) /* de-normalized - no implicit 1 to left of dec.*/ | |
287 | { | |
288 | BufPutByteRvs (b, dbl[1] & 0x0f); | |
289 | exponent -= 52; | |
290 | } | |
291 | else | |
292 | { | |
293 | BufPutByteRvs (b, (dbl[1] & 0x0f) | 0x10); /* 0x10 adds implicit bit */ | |
294 | exponent -= (1023 + 52); | |
295 | } | |
296 | ||
297 | #else | |
298 | #if SIZEOF_LONG == 8 | |
299 | #if WORDS_BIGENDIAN | |
300 | #error sorry! this 64 bit code requires little endianess. | |
301 | #endif | |
302 | ||
303 | /* this code is designed to work on Alpha under OSF/1 (64 bit longs, little endian) */ | |
304 | ||
305 | p = (unsigned long *) value; | |
306 | val = *p; | |
307 | ||
308 | isNeg = (val >> 63) & 1; | |
309 | /* special real values for +/- oo */ | |
310 | if (!finite (*value)) | |
311 | { | |
312 | if (isNeg) | |
313 | { | |
314 | BufPutByteRvs (b, ENC_MINUS_INFINITY); | |
315 | } | |
316 | else | |
317 | { | |
318 | BufPutByteRvs (b, ENC_PLUS_INFINITY); | |
319 | } | |
320 | return 1; | |
321 | } | |
322 | else /* encode a binary real value */ | |
323 | { | |
324 | exponent = (val >> 52) & 0x7ff; | |
325 | mantissa = (val & 0xfffffffffffffL) | 0x10000000000000L; | |
326 | ||
327 | for (i = 0; i < 7; i++) | |
328 | { | |
329 | BufPutByteRvs (b, mantissa & 0xff); | |
330 | mantissa >>= 8; | |
331 | } | |
332 | exponent -= (1023 + 52); | |
333 | ||
334 | #else | |
335 | #error long neither 8 nor 4 bytes in size? | |
336 | #endif | |
337 | #endif | |
338 | ||
339 | /* write the exponent */ | |
340 | BufPutByteRvs (b, exponent & 0xff); | |
341 | BufPutByteRvs (b, exponent >> 8); | |
342 | ||
343 | /* write format octet */ | |
344 | /* bb is 00 since base is 2 so do nothing */ | |
345 | /* ff is 00 since no other shifting is nec */ | |
346 | if (isNeg) | |
347 | { | |
348 | BufPutByteRvs (b, REAL_BINARY | REAL_EXPLEN_2 | REAL_SIGN); | |
349 | } | |
350 | else | |
351 | { | |
352 | BufPutByteRvs (b, REAL_BINARY | REAL_EXPLEN_2); | |
353 | } | |
354 | ||
355 | return sizeof (double) + 2; | |
356 | } | |
357 | ||
358 | /* not reached */ | |
359 | ||
360 | } /* BEncAsnRealContent */ | |
361 | ||
362 | #else /* IEEE_REAL_FMT not def */ | |
363 | ||
364 | #ifdef IEEE_REAL_LIB | |
365 | ||
366 | ||
367 | /* | |
368 | * Inits the PLUS_INFINITY and MINUS_INFINITY globals assuming | |
369 | * that the ieee_values library is present | |
370 | */ | |
371 | void | |
372 | InitAsnInfinity() | |
373 | { | |
374 | PLUS_INFINITY = infinity(); | |
375 | MINUS_INFINITY = -PLUS_INFINITY; | |
376 | } /* InitAsnInfinity */ | |
377 | ||
378 | /* | |
379 | * Encodes the content of an ASN.1 REAL value to the given buffer. | |
380 | * This version of the routine does not assume an IEEE double rep. | |
381 | * ieee library conversion routine are used instead. | |
382 | */ | |
383 | AsnLen | |
384 | BEncAsnRealContent PARAMS ((b, value), | |
385 | BUF_TYPE b _AND_ | |
386 | AsnReal *value) | |
387 | { | |
388 | unsigned long int encLen; | |
389 | double mantissa; | |
390 | double tmpMantissa; | |
391 | unsigned int truncatedMantissa; | |
392 | int exponent; | |
393 | unsigned int expLen; | |
394 | int sign; | |
395 | unsigned char buf[sizeof (double)]; | |
396 | int i, mantissaLen; | |
397 | unsigned char firstOctet; | |
398 | ||
399 | /* no contents for 0.0 reals */ | |
400 | if (iszero (*value)) | |
401 | return 0; | |
402 | ||
403 | /* special real values for +/- oo */ | |
404 | if (isinf (*value)) | |
405 | { | |
406 | if (signbit (*value)) /* neg */ | |
407 | BufPutByteRvs (b, ENC_MINUS_INFINITY); | |
408 | else | |
409 | BufPutByteRvs (b, ENC_PLUS_INFINITY); | |
410 | encLen = 1; | |
411 | } | |
412 | else /* encode a binary real value */ | |
413 | { | |
414 | if (signbit (*value)) | |
415 | sign = -1; | |
416 | else | |
417 | sign = 1; | |
418 | ||
419 | exponent = ilogb (*value); | |
420 | ||
421 | /* get the absolute value of the mantissa (subtract 1 to make < 1) */ | |
422 | mantissa = scalbn (fabs (*value), -exponent-1); | |
423 | ||
424 | ||
425 | tmpMantissa = mantissa; | |
426 | ||
427 | /* convert mantissa into an unsigned integer */ | |
428 | for (i = 0; i < sizeof (double); i++) | |
429 | { | |
430 | /* normalizied so shift 8 bits worth to the left of the decimal */ | |
431 | tmpMantissa *= (1<<8); | |
432 | ||
433 | /* grab only (octet sized) the integer part */ | |
434 | truncatedMantissa = (unsigned int) tmpMantissa; | |
435 | ||
436 | /* remove part to left of decimal now for next iteration */ | |
437 | tmpMantissa -= truncatedMantissa; | |
438 | ||
439 | /* write into tmp buffer */ | |
440 | buf[i] = truncatedMantissa; | |
441 | ||
442 | /* keep track of last non zero octet so can zap trailing zeros */ | |
443 | if (truncatedMantissa) | |
444 | mantissaLen = i+1; | |
445 | } | |
446 | ||
447 | /* | |
448 | * write format octet (first octet of content) | |
449 | * field 1 S bb ff ee | |
450 | * bit# 8 7 65 43 21 | |
451 | * | |
452 | * 1 in bit#1 means binary rep | |
453 | * 1 in bit#2 means the mantissa is neg, 0 pos | |
454 | * bb is the base: 65 base | |
455 | * 00 2 | |
456 | * 01 8 | |
457 | * 10 16 | |
458 | * 11 future ext. | |
459 | * | |
460 | * ff is the Value of F where Mantissa = sign x N x 2^F | |
461 | * FF can be one of 0 to 3 inclusive. (used to save re-alignment) | |
462 | * | |
463 | * ee is the length of the exponent: 21 length | |
464 | * 00 1 | |
465 | * 01 2 | |
466 | * 10 3 | |
467 | * 11 long form | |
468 | * | |
469 | * | |
470 | * encoded binary real value looks like | |
471 | * | |
472 | * fmt oct | |
473 | * -------------------------------------------------------- | |
474 | * |1Sbbffee| exponent (2's comp) | N (unsigned int) | | |
475 | * -------------------------------------------------------- | |
476 | * 87654321 | |
477 | */ | |
478 | firstOctet = REAL_BINARY; | |
479 | if (signbit (*value)) | |
480 | firstOctet |= REAL_SIGN; | |
481 | ||
482 | /* bb is 00 since base is 2 so do nothing */ | |
483 | /* ff is 00 since no other shifting is nec */ | |
484 | ||
485 | /* | |
486 | * get exponent calculate its encoded length | |
487 | * Note that the process of converting the mantissa | |
488 | * double to an int shifted the decimal mantissaLen * 8 | |
489 | * to the right - so correct that here | |
490 | */ | |
491 | exponent++; /* compensate for trick to put mantissa < 1 */ | |
492 | exponent -= (mantissaLen * 8); | |
493 | expLen = SignedIntOctetLen (exponent); | |
494 | ||
495 | switch (expLen) | |
496 | { | |
497 | case 1: | |
498 | firstOctet |= REAL_EXPLEN_1; | |
499 | break; | |
500 | case 2: | |
501 | firstOctet |= REAL_EXPLEN_2; | |
502 | break; | |
503 | case 3: | |
504 | firstOctet |= REAL_EXPLEN_3; | |
505 | break; | |
506 | default: | |
507 | firstOctet |= REAL_EXPLEN_LONG; | |
508 | break; | |
509 | } | |
510 | ||
511 | encLen = mantissaLen + expLen + 1; | |
512 | ||
513 | /* write the mantissa (N value) */ | |
514 | BufPutSegRvs (b, (char*)buf, mantissaLen); | |
515 | ||
516 | /* write the exponent */ | |
517 | for (i = expLen; i > 0; i--) | |
518 | { | |
519 | BufPutByteRvs (b, exponent); | |
520 | exponent >>= 8; | |
521 | } | |
522 | ||
523 | /* write the exponents length if nec */ | |
524 | if (expLen > 3) | |
525 | { | |
526 | encLen++; | |
527 | BufPutByteRvs (b, expLen); | |
528 | } | |
529 | ||
530 | /* write the format octet */ | |
531 | BufPutByteRvs (b, firstOctet); | |
532 | ||
533 | } | |
534 | return encLen; | |
535 | ||
536 | } /* BEncAsnRealContent */ | |
537 | ||
538 | #else /* neither IEEE_REAL_FMT or IEEE_REAL_LIB are def */ | |
539 | ||
540 | /* | |
541 | * Inits the PLUS_INFINITY and MINUS_INFINITY globals assuming | |
542 | * that the double is an IEEE DOUBLE. This should be changed | |
543 | * for the target architecture (if it is not IEEE) | |
544 | */ | |
545 | void | |
546 | InitAsnInfinity() | |
547 | { | |
548 | unsigned char *c; | |
549 | int i; | |
550 | ||
551 | if (sizeof (double) != 8) | |
552 | Asn1Error ("InitAsnInfinity: ERROR expected sizeof (AsnReal) to be 8"); | |
553 | ||
554 | c = (unsigned char*)&PLUS_INFINITY; | |
555 | c[0] = 0x7f; | |
556 | c[1] = 0xf0; | |
557 | for (i = 2; i < sizeof (double); i++) | |
558 | c[i] = 0; | |
559 | ||
560 | MINUS_INFINITY = -PLUS_INFINITY; | |
561 | } /* InitAsnInfinity */ | |
562 | ||
563 | /* | |
564 | * Encodes the content of an ASN.1 REAL value to the given buffer. | |
565 | * This version of the routine does not assume an IEEE double rep. | |
566 | * or the existence of the IEEE library routines. Uses old style | |
567 | * UNIX frexp etc. | |
568 | */ | |
569 | AsnLen | |
570 | BEncAsnRealContent PARAMS ((b, value), | |
571 | BUF_TYPE b _AND_ | |
572 | AsnReal *value) | |
573 | { | |
574 | unsigned long int encLen; | |
575 | double mantissa; | |
576 | double tmpMantissa; | |
577 | unsigned int truncatedMantissa; | |
578 | int exponent; | |
579 | unsigned int expLen; | |
580 | int sign; | |
581 | unsigned char buf[sizeof (double)]; | |
582 | int i, mantissaLen; | |
583 | unsigned char firstOctet; | |
584 | ||
585 | /* no contents for 0.0 reals */ | |
586 | if (*value == 0.0) | |
587 | return 0; | |
588 | ||
589 | /* special real values for +/- oo */ | |
590 | if (*value == MINUS_INFINITY) | |
591 | { | |
592 | BufPutByteRvs (b, ENC_MINUS_INFINITY); | |
593 | encLen = 1; | |
594 | } | |
595 | else if (*value == PLUS_INFINITY) | |
596 | { | |
597 | BufPutByteRvs (b, ENC_PLUS_INFINITY); | |
598 | encLen = 1; | |
599 | } | |
600 | else /* encode a binary real value */ | |
601 | { | |
602 | /* | |
603 | * this is what frexp gets from *value | |
604 | * *value == mantissa * 2^exponent | |
605 | * where 0.5 <= |manitissa| < 1.0 | |
606 | */ | |
607 | mantissa = frexp (*value, &exponent); | |
608 | ||
609 | /* set sign and make mantissa = | mantissa | */ | |
610 | if (mantissa < 0.0) | |
611 | { | |
612 | sign = -1; | |
613 | mantissa *= -1; | |
614 | } | |
615 | else | |
616 | sign = 1; | |
617 | ||
618 | ||
619 | tmpMantissa = mantissa; | |
620 | ||
621 | /* convert mantissa into an unsigned integer */ | |
622 | for (i = 0; i < sizeof (double); i++) | |
623 | { | |
624 | /* normalizied so shift 8 bits worth to the left of the decimal */ | |
625 | tmpMantissa *= (1<<8); | |
626 | ||
627 | /* grab only (octet sized) the integer part */ | |
628 | truncatedMantissa = (unsigned int) tmpMantissa; | |
629 | ||
630 | /* remove part to left of decimal now for next iteration */ | |
631 | tmpMantissa -= truncatedMantissa; | |
632 | ||
633 | /* write into tmp buffer */ | |
634 | buf[i] = truncatedMantissa; | |
635 | ||
636 | /* keep track of last non zero octet so can zap trailing zeros */ | |
637 | if (truncatedMantissa) | |
638 | mantissaLen = i+1; | |
639 | } | |
640 | ||
641 | /* | |
642 | * write format octet (first octet of content) | |
643 | * field 1 S bb ff ee | |
644 | * bit# 8 7 65 43 21 | |
645 | * | |
646 | * 1 in bit#1 means binary rep | |
647 | * 1 in bit#2 means the mantissa is neg, 0 pos | |
648 | * bb is the base: 65 base | |
649 | * 00 2 | |
650 | * 01 8 | |
651 | * 10 16 | |
652 | * 11 future ext. | |
653 | * | |
654 | * ff is the Value of F where Mantissa = sign x N x 2^F | |
655 | * FF can be one of 0 to 3 inclusive. (used to save re-alignment) | |
656 | * | |
657 | * ee is the length of the exponent: 21 length | |
658 | * 00 1 | |
659 | * 01 2 | |
660 | * 10 3 | |
661 | * 11 long form | |
662 | * | |
663 | * | |
664 | * encoded binary real value looks like | |
665 | * | |
666 | * fmt oct | |
667 | * -------------------------------------------------------- | |
668 | * |1Sbbffee| exponent (2's comp) | N (unsigned int) | | |
669 | * -------------------------------------------------------- | |
670 | * 87654321 | |
671 | */ | |
672 | firstOctet = REAL_BINARY; | |
673 | if (sign == -1) | |
674 | firstOctet |= REAL_SIGN; | |
675 | ||
676 | /* bb is 00 since base is 2 so do nothing */ | |
677 | /* ff is 00 since no other shifting is nec */ | |
678 | ||
679 | /* | |
680 | * get exponent calculate its encoded length | |
681 | * Note that the process of converting the mantissa | |
682 | * double to an int shifted the decimal mantissaLen * 8 | |
683 | * to the right - so correct that here | |
684 | */ | |
685 | exponent -= (mantissaLen * 8); | |
686 | expLen = SignedIntOctetLen (exponent); | |
687 | ||
688 | switch (expLen) | |
689 | { | |
690 | case 1: | |
691 | firstOctet |= REAL_EXPLEN_1; | |
692 | break; | |
693 | case 2: | |
694 | firstOctet |= REAL_EXPLEN_2; | |
695 | break; | |
696 | case 3: | |
697 | firstOctet |= REAL_EXPLEN_3; | |
698 | break; | |
699 | default: | |
700 | firstOctet |= REAL_EXPLEN_LONG; | |
701 | break; | |
702 | } | |
703 | ||
704 | encLen = mantissaLen + expLen + 1; | |
705 | ||
706 | /* write the mantissa (N value) */ | |
707 | BufPutSegRvs (b, (char*)buf, mantissaLen); | |
708 | ||
709 | /* write the exponent */ | |
710 | for (i = expLen; i > 0; i--) | |
711 | { | |
712 | BufPutByteRvs (b, exponent); | |
713 | exponent >>= 8; | |
714 | } | |
715 | ||
716 | /* write the exponents length if nec */ | |
717 | if (expLen > 3) | |
718 | { | |
719 | encLen++; | |
720 | BufPutByteRvs (b, expLen); | |
721 | } | |
722 | ||
723 | /* write the format octet */ | |
724 | BufPutByteRvs (b, firstOctet); | |
725 | ||
726 | } | |
727 | return encLen; | |
728 | ||
729 | } /* BEncAsnRealContent */ | |
730 | ||
731 | #endif /* IEEE_REAL_LIB */ | |
732 | #endif /* IEEE_REAL_FMT */ | |
733 | ||
734 | ||
735 | ||
736 | /* | |
737 | * Decodes the content of a BER REAL value. | |
738 | * This only supports the binary REAL encoding. The decimal encoding | |
739 | * is left as an exercise to the reader. | |
740 | */ | |
741 | void | |
742 | BDecAsnRealContent PARAMS ((b, tagId, len, result, bytesDecoded, env), | |
743 | BUF_TYPE b _AND_ | |
744 | AsnTag tagId _AND_ | |
745 | AsnLen len _AND_ | |
746 | AsnReal *result _AND_ | |
747 | AsnLen *bytesDecoded _AND_ | |
748 | jmp_buf env) | |
749 | { | |
750 | unsigned char firstOctet; | |
751 | unsigned char firstExpOctet; | |
752 | int i; | |
753 | unsigned int expLen; | |
754 | double mantissa; | |
755 | unsigned short base; | |
756 | long int exponent = 0; | |
757 | double tmpBase; | |
758 | double tmpExp; | |
759 | ||
760 | if (len == 0) | |
761 | { | |
762 | *result = 0.0; | |
763 | return; | |
764 | } | |
765 | ||
766 | firstOctet = BufGetByte (b); | |
767 | if (len == 1) | |
768 | { | |
769 | (*bytesDecoded) += 1; | |
770 | if (firstOctet == ENC_PLUS_INFINITY) | |
771 | *result = PLUS_INFINITY; | |
772 | else if (firstOctet == ENC_MINUS_INFINITY) | |
773 | *result = MINUS_INFINITY; | |
774 | else | |
775 | { | |
776 | Asn1Error ("BDecAsnRealContent: ERROR - unrecognized real number of length 1 octet.\n"); | |
777 | longjmp (env, -22); | |
778 | } | |
779 | } | |
780 | else | |
781 | { | |
782 | if (firstOctet & REAL_BINARY) | |
783 | { | |
784 | firstExpOctet = BufGetByte (b); | |
785 | if (firstExpOctet & 0x80) | |
786 | exponent = -1; | |
787 | switch (firstOctet & REAL_EXPLEN_MASK) | |
788 | { | |
789 | case REAL_EXPLEN_1: | |
790 | expLen = 1; | |
791 | exponent = (exponent << 8)| firstExpOctet; | |
792 | break; | |
793 | ||
794 | case REAL_EXPLEN_2: | |
795 | expLen = 2; | |
796 | exponent = (exponent << 16) | | |
797 | (((unsigned long int) firstExpOctet) << 8) | | |
798 | BufGetByte (b); | |
799 | break; | |
800 | ||
801 | case REAL_EXPLEN_3: | |
802 | expLen = 3; | |
803 | exponent = (exponent << 16) | | |
804 | (((unsigned long int) firstExpOctet) << 8) | | |
805 | BufGetByte (b); | |
806 | exponent = (exponent << 8) | BufGetByte (b); | |
807 | break; | |
808 | ||
809 | default: /* long form */ | |
810 | expLen = firstExpOctet +1; | |
811 | i = firstExpOctet-1; | |
812 | firstExpOctet = BufGetByte (b); | |
813 | if (firstExpOctet & 0x80) | |
814 | exponent = (-1 <<8) | firstExpOctet; | |
815 | else | |
816 | exponent = firstExpOctet; | |
817 | for (;i > 0; firstExpOctet--) | |
818 | exponent = (exponent << 8) | BufGetByte (b); | |
819 | break; | |
820 | } | |
821 | ||
822 | mantissa = 0.0; | |
823 | for (i = 1 + expLen; i < len; i++) | |
824 | { | |
825 | mantissa *= (1<<8); | |
826 | mantissa += BufGetByte (b); | |
827 | } | |
828 | ||
829 | /* adjust N by scaling factor */ | |
830 | mantissa *= (1<<((firstOctet & REAL_FACTOR_MASK) >> 2)); | |
831 | ||
832 | switch (firstOctet & REAL_BASE_MASK) | |
833 | { | |
834 | case REAL_BASE_2: | |
835 | base = 2; | |
836 | break; | |
837 | ||
838 | case REAL_BASE_8: | |
839 | base = 8; | |
840 | break; | |
841 | ||
842 | case REAL_BASE_16: | |
843 | base = 16; | |
844 | break; | |
845 | ||
846 | default: | |
847 | Asn1Error ("BDecAsnRealContent: ERROR - unsupported base for a binary real number.\n"); | |
848 | longjmp (env, -23); | |
849 | break; | |
850 | ||
851 | } | |
852 | ||
853 | tmpBase = base; | |
854 | tmpExp = exponent; | |
855 | ||
856 | *result = mantissa * pow ((double)base, (double)exponent); | |
857 | ||
858 | if (firstOctet & REAL_SIGN) | |
859 | *result = -*result; | |
860 | ||
861 | (*bytesDecoded) += len; | |
862 | } | |
863 | else /* decimal version */ | |
864 | { | |
865 | Asn1Error ("BDecAsnRealContent: ERROR - decimal REAL form is not currently supported\n"); | |
866 | longjmp (env, -24); | |
867 | } | |
868 | } | |
869 | ||
870 | } /* BDecAsnRealContent */ | |
871 | ||
872 | ||
873 | /* | |
874 | * Prints given REAL value to the given FILE * in ASN.1 Value Notation. | |
875 | * indent is ignored. | |
876 | */ | |
877 | void | |
878 | PrintAsnReal PARAMS ((f, v, indent), | |
879 | FILE *f _AND_ | |
880 | AsnReal *v _AND_ | |
881 | unsigned short int indent) | |
882 | { | |
883 | fprintf (f, "%.17E", *v); | |
884 | } |