blob: d395c848731d5519053b5f9697f2e3e2c50f5adb [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>
Lev Walkin41ba1f22004-09-14 12:46:35 +00007#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
Lev Walkin942fd082004-10-03 09:13:02 +000015static volatile double real_zero = 0.0;
Lev Walkinae1bce92004-09-27 22:18:34 +000016#ifndef NAN
Lev Walkinc51e7d62004-09-27 22:16:18 +000017#define NAN (real_zero/real_zero)
Lev Walkin40319a12004-09-14 13:40:42 +000018#endif
19
Lev Walkin41ba1f22004-09-14 12:46:35 +000020/*
21 * REAL basic type description.
22 */
Lev Walkin5e033762004-09-29 13:26:15 +000023static ber_tlv_tag_t asn_DEF_REAL_tags[] = {
Lev Walkin41ba1f22004-09-14 12:46:35 +000024 (ASN_TAG_CLASS_UNIVERSAL | (9 << 2))
25};
Lev Walkin5e033762004-09-29 13:26:15 +000026asn_TYPE_descriptor_t asn_DEF_REAL = {
Lev Walkin41ba1f22004-09-14 12:46:35 +000027 "REAL",
Lev Walkin8e8078a2004-09-26 13:10:40 +000028 ASN__PRIMITIVE_TYPE_free,
Lev Walkina9cc46e2004-09-22 16:06:28 +000029 REAL_print,
Lev Walkin41ba1f22004-09-14 12:46:35 +000030 asn_generic_no_constraint,
Lev Walkin8e8078a2004-09-26 13:10:40 +000031 ber_decode_primitive,
32 der_encode_primitive,
Lev Walkina9cc46e2004-09-22 16:06:28 +000033 0, /* Not implemented yet */
34 REAL_encode_xer,
Lev Walkin41ba1f22004-09-14 12:46:35 +000035 0, /* Use generic outmost tag fetcher */
Lev Walkin5e033762004-09-29 13:26:15 +000036 asn_DEF_REAL_tags,
37 sizeof(asn_DEF_REAL_tags) / sizeof(asn_DEF_REAL_tags[0]),
38 asn_DEF_REAL_tags, /* Same as above */
39 sizeof(asn_DEF_REAL_tags) / sizeof(asn_DEF_REAL_tags[0]),
Lev Walkin41ba1f22004-09-14 12:46:35 +000040 0, 0, /* No members */
41 0 /* No specifics */
42};
43
Lev Walkina9cc46e2004-09-22 16:06:28 +000044ssize_t
45REAL__dump(double d, int canonical, asn_app_consume_bytes_f *cb, void *app_key) {
Lev Walkin92b35d22004-09-27 20:52:18 +000046 char local_buf[64];
Lev Walkina9cc46e2004-09-22 16:06:28 +000047 char *buf = local_buf;
48 ssize_t buflen = sizeof(local_buf);
Lev Walkin92b35d22004-09-27 20:52:18 +000049 const char *fmt = canonical?"%.15E":"%.15f";
Lev Walkina9cc46e2004-09-22 16:06:28 +000050 ssize_t ret;
51
Lev Walkin8e8078a2004-09-26 13:10:40 +000052 /*
53 * Check whether it is a special value.
54 */
Lev Walkinc51e7d62004-09-27 22:16:18 +000055 /* fpclassify(3) is not portable yet */
56 if(isnan(d)) {
57 buf = "<NOT-A-NUMBER/>";
58 buflen = 15;
59 return (cb(buf, buflen, app_key) < 0) ? -1 : buflen;
Lev Walkina460ba32004-10-20 15:40:04 +000060 } else if(!finite(d)) {
Lev Walkinc51e7d62004-09-27 22:16:18 +000061 if(copysign(1.0, d) < 0.0) {
62 buf = "<MINUS-INFINITY/>";
63 buflen = 17;
64 } else {
65 buf = "<PLUS-INFINITY/>";
66 buflen = 16;
67 }
68 return (cb(buf, buflen, app_key) < 0) ? -1 : buflen;
69 } else if(ilogb(d) <= -INT_MAX) {
70 if(copysign(1.0, d) < 0.0) {
71 buf = "-0";
72 buflen = 2;
73 } else {
74 buf = "0";
75 buflen = 1;
Lev Walkin8e8078a2004-09-26 13:10:40 +000076 }
77 return (cb(buf, buflen, app_key) < 0) ? -1 : buflen;
78 }
79
80 /*
81 * Use the libc's double printing, hopefully they got it right.
82 */
Lev Walkina9cc46e2004-09-22 16:06:28 +000083 do {
84 ret = snprintf(buf, buflen, fmt, d);
85 if(ret < 0) {
86 buflen <<= 1;
87 } else if(ret >= buflen) {
88 buflen = ret + 1;
89 } else {
90 buflen = ret;
91 break;
92 }
93 if(buf != local_buf) free(buf);
Lev Walkin8e8078a2004-09-26 13:10:40 +000094 buf = (char *)MALLOC(buflen);
Lev Walkina9cc46e2004-09-22 16:06:28 +000095 if(!buf) return -1;
96 } while(1);
97
Lev Walkina9cc46e2004-09-22 16:06:28 +000098 if(canonical) {
Lev Walkin92b35d22004-09-27 20:52:18 +000099 /*
100 * Transform the "[-]d.dddE+-dd" output into "[-]d.dddE[-]d"
101 */
Lev Walkina9cc46e2004-09-22 16:06:28 +0000102 char *dot, *E;
103 char *end = buf + buflen;
Lev Walkin92b35d22004-09-27 20:52:18 +0000104 char *last_zero;
Lev Walkina9cc46e2004-09-22 16:06:28 +0000105
106 dot = (buf[0] == '-') ? (buf + 2) : (buf + 1);
107 if(*dot >= 0x30) {
108 errno = EINVAL;
109 return -1; /* Not a dot, really */
110 }
Lev Walkin92b35d22004-09-27 20:52:18 +0000111 *dot = 0x2e; /* Replace possible comma */
Lev Walkina9cc46e2004-09-22 16:06:28 +0000112
Lev Walkin92b35d22004-09-27 20:52:18 +0000113 for(last_zero = dot + 2, E = dot; dot < end; E++) {
114 if(*E == 0x45) {
115 char *expptr = ++E;
116 char *s = expptr;
117 int sign;
118 if(*expptr == '+') {
119 /* Skip the "+" */
Lev Walkina9cc46e2004-09-22 16:06:28 +0000120 buflen -= 1;
Lev Walkin92b35d22004-09-27 20:52:18 +0000121 sign = 0;
122 } else {
123 sign = 1;
Lev Walkina9cc46e2004-09-22 16:06:28 +0000124 s++;
125 }
Lev Walkin92b35d22004-09-27 20:52:18 +0000126 expptr++;
127 if(expptr > end) {
Lev Walkina9cc46e2004-09-22 16:06:28 +0000128 errno = EINVAL;
129 return -1;
130 }
Lev Walkin92b35d22004-09-27 20:52:18 +0000131 if(*expptr == 0x30) {
132 buflen--;
133 expptr++;
134 }
135 if(*last_zero == 0x30) {
136 *last_zero = 0x45; /* E */
137 s = last_zero + 1;
138 if(sign) *s++ = '-';
139 }
140 for(; expptr <= end; s++, expptr++)
141 *s = *expptr;
142 break;
143 } else if(*E == 0x30) {
144 if(*last_zero != 0x30)
145 last_zero = E;
Lev Walkina9cc46e2004-09-22 16:06:28 +0000146 }
147 }
148 if(E == end) {
149 errno = EINVAL;
150 return -1; /* No promised E */
151 }
Lev Walkin92b35d22004-09-27 20:52:18 +0000152 } else {
153 /*
154 * Remove trailing zeros.
155 */
156 char *end = buf + buflen;
157 char *last_zero = end;
158 char *z;
159 for(z = end - 1; z > buf; z--) {
160 switch(*z) {
161 case 0x030:
162 last_zero = z;
163 case 0x31: case 0x32: case 0x33: case 0x34:
164 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
165 continue;
166 default: /* Catch dot and other separators */
167 *z = 0x2e; /* Replace possible comma */
168 if(last_zero == z + 1) { /* leave x.0 */
169 last_zero++;
170 }
171 buflen = last_zero - buf;
172 *last_zero = '\0';
173 break;
174 }
175 break;
176 }
Lev Walkina9cc46e2004-09-22 16:06:28 +0000177 }
178
179 ret = cb(buf, buflen, app_key);
180 if(buf != local_buf) free(buf);
181 return (ret < 0) ? -1 : buflen;
182}
183
Lev Walkin41ba1f22004-09-14 12:46:35 +0000184int
Lev Walkin5e033762004-09-29 13:26:15 +0000185REAL_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
Lev Walkin41ba1f22004-09-14 12:46:35 +0000186 asn_app_consume_bytes_f *cb, void *app_key) {
187 const REAL_t *st = (const REAL_t *)sptr;
Lev Walkin8e8078a2004-09-26 13:10:40 +0000188 ssize_t ret;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000189 double d;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000190
191 (void)td; /* Unused argument */
192 (void)ilevel; /* Unused argument */
193
Lev Walkina9cc46e2004-09-22 16:06:28 +0000194 if(!st || !st->buf)
Lev Walkin8e8078a2004-09-26 13:10:40 +0000195 ret = cb("<absent>", 8, app_key);
Lev Walkin5e033762004-09-29 13:26:15 +0000196 else if(asn_REAL2double(st, &d))
Lev Walkin8e8078a2004-09-26 13:10:40 +0000197 ret = cb("<error>", 7, app_key);
198 else
199 ret = REAL__dump(d, 0, cb, app_key);
Lev Walkin41ba1f22004-09-14 12:46:35 +0000200
Lev Walkin8e8078a2004-09-26 13:10:40 +0000201 return (ret < 0) ? -1 : 0;
Lev Walkina9cc46e2004-09-22 16:06:28 +0000202}
Lev Walkin41ba1f22004-09-14 12:46:35 +0000203
Lev Walkina9cc46e2004-09-22 16:06:28 +0000204asn_enc_rval_t
Lev Walkin5e033762004-09-29 13:26:15 +0000205REAL_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
Lev Walkina9cc46e2004-09-22 16:06:28 +0000206 int ilevel, enum xer_encoder_flags_e flags,
207 asn_app_consume_bytes_f *cb, void *app_key) {
208 REAL_t *st = (REAL_t *)sptr;
209 asn_enc_rval_t er;
210 double d;
211
212 (void)ilevel;
213
Lev Walkin5e033762004-09-29 13:26:15 +0000214 if(!st || !st->buf || asn_REAL2double(st, &d))
Lev Walkina9cc46e2004-09-22 16:06:28 +0000215 _ASN_ENCODE_FAILED;
216
217 er.encoded = REAL__dump(d, flags & XER_F_CANONICAL, cb, app_key);
218 if(er.encoded < 0) _ASN_ENCODE_FAILED;
219
220 return er;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000221}
222
223int
Lev Walkin5e033762004-09-29 13:26:15 +0000224asn_REAL2double(const REAL_t *st, double *dbl_value) {
Lev Walkina9cc46e2004-09-22 16:06:28 +0000225 unsigned int octv;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000226
227 if(!st || !st->buf) {
228 errno = EINVAL;
229 return -1;
230 }
231
232 if(st->size == 0) {
233 *dbl_value = 0;
234 return 0;
235 }
236
237 octv = st->buf[0]; /* unsigned byte */
238
239 switch(octv & 0xC0) {
240 case 0x40: /* X.690: 8.5.8 */
241 /* "SpecialRealValue" */
242
243 /* Be liberal in what you accept...
244 if(st->size != 1) ...
245 */
246
247 switch(st->buf[0]) {
248 case 0x40: /* 01000000: PLUS-INFINITY */
Lev Walkinc51e7d62004-09-27 22:16:18 +0000249 *dbl_value = 1.0/real_zero;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000250 return 0;
251 case 0x41: /* 01000001: MINUS-INFINITY */
Lev Walkinc51e7d62004-09-27 22:16:18 +0000252 *dbl_value = -1.0/real_zero;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000253 return 0;
254 /*
255 * The following cases are defined by
256 * X.690 Amendment 1 (10/03)
257 */
258 case 0x42: /* 01000010: NOT-A-NUMBER */
259 *dbl_value = NAN;
260 return 0;
261 case 0x43: /* 01000011: minus zero */
Lev Walkin2a789d92004-09-27 21:36:59 +0000262 *dbl_value = -0.0;
Lev Walkin41ba1f22004-09-14 12:46:35 +0000263 return 0;
264 }
265
266 errno = EINVAL;
267 return -1;
268 case 0x00: { /* X.690: 8.5.6 */
269 /*
270 * Decimal. NR{1,2,3} format.
271 */
272 double d;
273
274 assert(st->buf[st->size - 1] == 0); /* Security, vashu mat' */
275
276 d = strtod((char *)st->buf, 0);
277 if(finite(d)) {
278 *dbl_value = d;
279 return 0;
280 } else {
281 errno = ERANGE;
282 return 0;
283 }
284 }
285 }
286
287 /*
288 * Binary representation.
289 */
290 {
291 double m;
292 int expval; /* exponent value */
293 unsigned int elen; /* exponent value length, in octets */
294 unsigned int scaleF;
295 unsigned int baseF;
296 uint8_t *ptr;
297 uint8_t *end;
298 int sign;
299
300 switch((octv & 0x30) >> 4) {
301 case 0x00: baseF = 1; break; /* base 2 */
302 case 0x01: baseF = 3; break; /* base 8 */
303 case 0x02: baseF = 4; break; /* base 16 */
304 default:
305 /* Reserved field, can't parse now. */
306 errno = EINVAL;
307 return -1;
308 }
309
310 sign = (octv & 0x40); /* bit 7 */
311 scaleF = (octv & 0x0C) >> 2; /* bits 4 to 3 */
312
Lev Walkina9cc46e2004-09-22 16:06:28 +0000313 if(st->size <= (int)(1 + (octv & 0x03))) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000314 errno = EINVAL;
315 return -1;
316 }
317
318 if((octv & 0x03) == 0x11) {
319 /* 8.5.6.4, case d) */
320 elen = st->buf[1]; /* unsigned binary number */
Lev Walkina9cc46e2004-09-22 16:06:28 +0000321 if(elen == 0 || st->size <= (int)(2 + elen)) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000322 errno = EINVAL;
323 return -1;
324 }
325 ptr = &st->buf[2];
326 } else {
327 elen = (octv & 0x03);
328 ptr = &st->buf[1];
329 }
330
331 /* Fetch the multibyte exponent */
332 expval = (int)(*(int8_t *)ptr);
333 end = ptr + elen + 1;
334 for(ptr++; ptr < end; ptr++)
335 expval = (expval * 256) + *ptr;
336
337 m = 0.0; /* Initial mantissa value */
338
339 /* Okay, the exponent is here. Now, what about mantissa? */
340 end = st->buf + st->size;
341 if(ptr < end) {
342 for(; ptr < end; ptr++)
343 m = scalbn(m, 8) + *ptr;
344 }
345
346 ASN_DEBUG("m=%.10f, scF=%d, bF=%d, expval=%d, ldexp()=%f, scalbn()=%f",
347 m, scaleF, baseF, expval,
348 ldexp(m, expval * baseF + scaleF),
349 scalbn(m, scaleF) * pow(pow(2, baseF), expval)
350 );
351
352 /*
353 * (S * N * 2^F) * B^E
354 * Essentially:
355 m = scalbn(m, scaleF) * pow(pow(2, base), expval);
356 */
357 m = ldexp(m, expval * baseF + scaleF);
358 if(finite(m)) {
359 *dbl_value = sign ? -m : m;
360 } else {
361 errno = ERANGE;
362 return -1;
363 }
364
365 } /* if(binary_format) */
366
367 return 0;
368}
369
370/*
371 * Assume IEEE 754 floating point: standard 64 bit double.
372 * [1 bit sign] [11 bits exponent] [52 bits mantissa]
373 */
374int
Lev Walkin5e033762004-09-29 13:26:15 +0000375asn_double2REAL(REAL_t *st, double dbl_value) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000376#ifdef WORDS_BIGENDIAN /* Known to be big-endian */
377 int littleEndian = 0;
378#else /* need to test: have no explicit information */
379 unsigned int LE = 1;
380 int littleEndian = *(unsigned char *)&LE;
381#endif
382 uint8_t buf[16]; /* More than enough for 8-byte dbl_value */
383 uint8_t dscr[sizeof(dbl_value)]; /* double value scratch pad */
384 /* Assertion guards: won't even compile, if unexpected double size */
385 char assertion_buffer1[9 - sizeof(dbl_value)] __attribute__((unused));
386 char assertion_buffer2[sizeof(dbl_value) - 7] __attribute__((unused));
387 uint8_t *ptr = buf;
388 uint8_t *mstop; /* Last byte of mantissa */
389 unsigned int mval; /* Value of the last byte of mantissa */
390 unsigned int bmsign; /* binary mask with sign */
391 unsigned int buflen;
392 unsigned int accum;
393 int expval;
394
395 if(!st) {
396 errno = EINVAL;
397 return -1;
398 }
399
Lev Walkin057fb732004-09-14 13:58:10 +0000400 /*
401 * ilogb(+-0) returns -INT_MAX or INT_MIN (platform-dependent)
402 * ilogb(+-inf) returns INT_MAX
Lev Walkin2a789d92004-09-27 21:36:59 +0000403 * ilogb(NaN) returns INT_MIN or INT_MAX (platform-dependent)
Lev Walkin057fb732004-09-14 13:58:10 +0000404 */
Lev Walkin41ba1f22004-09-14 12:46:35 +0000405 expval = ilogb(dbl_value);
Lev Walkin2a789d92004-09-27 21:36:59 +0000406 if(expval <= -INT_MAX /* Also catches +-0 and maybe isnan() */
407 || expval == INT_MAX /* catches isfin() and maybe isnan() */
Lev Walkin41ba1f22004-09-14 12:46:35 +0000408 ) {
409 if(!st->buf || st->size < 2) {
Lev Walkin8e8078a2004-09-26 13:10:40 +0000410 ptr = (uint8_t *)MALLOC(2);
Lev Walkin41ba1f22004-09-14 12:46:35 +0000411 if(!ptr) return -1;
412 st->buf = ptr;
413 }
414 /* fpclassify(3) is not portable yet */
Lev Walkin2a789d92004-09-27 21:36:59 +0000415 if(isnan(dbl_value)) {
416 st->buf[0] = 0x42; /* NaN */
417 st->buf[1] = 0;
418 st->size = 1;
Lev Walkina460ba32004-10-20 15:40:04 +0000419 } else if(!finite(dbl_value)) {
Lev Walkin7e03db92004-09-14 13:50:21 +0000420 if(copysign(1.0, dbl_value) < 0.0) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000421 st->buf[0] = 0x41; /* MINUS-INFINITY */
422 } else {
423 st->buf[0] = 0x40; /* PLUS-INFINITY */
424 }
425 st->buf[1] = 0;
426 st->size = 1;
Lev Walkinc51e7d62004-09-27 22:16:18 +0000427 } else {
428 if(copysign(1.0, dbl_value) < 0.0) {
429 st->buf[0] = 0x80 | 0x40;
430 st->buf[1] = 0;
431 st->size = 2;
432 } else {
433 /* no content octets: positive zero */
434 st->buf[0] = 0; /* JIC */
435 st->size = 0;
436 }
Lev Walkin41ba1f22004-09-14 12:46:35 +0000437 }
438 return 0;
439 }
440
441 if(littleEndian) {
442 uint8_t *s = ((uint8_t *)&dbl_value) + sizeof(dbl_value) - 2;
Lev Walkin057fb732004-09-14 13:58:10 +0000443 uint8_t *start = ((uint8_t *)&dbl_value);
Lev Walkin41ba1f22004-09-14 12:46:35 +0000444 uint8_t *d;
445
446 bmsign = 0x80 | ((s[1] >> 1) & 0x40); /* binary mask & - */
Lev Walkin057fb732004-09-14 13:58:10 +0000447 for(mstop = d = dscr; s >= start; d++, s--) {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000448 *d = *s;
449 if(*d) mstop = d;
450 }
451 } else {
452 uint8_t *s = ((uint8_t *)&dbl_value) + 1;
453 uint8_t *end = ((uint8_t *)&dbl_value) + sizeof(double);
454 uint8_t *d;
455
456 bmsign = 0x80 | ((s[-1] >> 1) & 0x40); /* binary mask & - */
457 for(mstop = d = dscr; s < end; d++, s++) {
458 *d = *s;
459 if(*d) mstop = d;
460 }
461 }
462
463 /* Remove parts of the exponent, leave mantissa and explicit 1. */
464 dscr[0] = 0x10 | (dscr[0] & 0x0f);
465
466 /* Adjust exponent in a very unobvious way */
467 expval -= 8 * ((mstop - dscr) + 1) - 4;
468
469 /* This loop ensures DER conformance by forcing mantissa odd: 11.3.1 */
470 mval = *mstop;
471 if(mval && !(mval & 1)) {
472 unsigned int shift_count = 1;
473 unsigned int ishift;
474 uint8_t *mptr;
475
476 /*
477 * Figure out what needs to be done to make mantissa odd.
478 */
479 if(!(mval & 0x0f)) /* Speed-up a little */
480 shift_count = 4;
481 while(((mval >> shift_count) & 1) == 0)
482 shift_count++;
483
484 ishift = 8 - shift_count;
485 accum = 0;
486
487 /* Go over the buffer, shifting it shift_count bits right. */
488 for(mptr = dscr; mptr <= mstop; mptr++) {
489 mval = *mptr;
490 *mptr = accum | (mval >> shift_count);
491 accum = mval << ishift;
492 }
493
494 /* Adjust mantissa appropriately. */
495 expval += shift_count;
496 }
497
498 if(expval < 0) {
499 if((expval >> 7) == -1) {
500 *ptr++ = bmsign | 0x00;
501 *ptr++ = expval;
502 } else if((expval >> 15) == -1) {
503 *ptr++ = bmsign | 0x01;
504 *ptr++ = expval >> 8;
505 *ptr++ = expval;
506 } else {
Lev Walkin41ba1f22004-09-14 12:46:35 +0000507 *ptr++ = bmsign | 0x02;
508 *ptr++ = expval >> 16;
509 *ptr++ = expval >> 8;
510 *ptr++ = expval;
511 }
512 } else if(expval <= 0x7f) {
513 *ptr++ = bmsign | 0x00;
514 *ptr++ = expval;
515 } else if(expval <= 0x7fff) {
516 *ptr++ = bmsign | 0x01;
517 *ptr++ = expval >> 8;
518 *ptr++ = expval;
519 } else {
520 assert(expval <= 0x7fffff);
521 *ptr++ = bmsign | 0x02;
522 *ptr++ = expval >> 16;
523 *ptr++ = expval >> 8;
524 *ptr++ = expval;
525 }
526
527 buflen = (mstop - dscr) + 1;
528 memcpy(ptr, dscr, buflen);
529 ptr += buflen;
530 buflen = ptr - buf;
531
532 (void *)ptr = MALLOC(buflen + 1);
533 if(!ptr) return -1;
534
535 memcpy(ptr, buf, buflen);
536 buf[buflen] = 0; /* JIC */
537
538 if(st->buf) FREEMEM(st->buf);
539 st->buf = ptr;
540 st->size = buflen;
541
542 return 0;
543}