]> git.stg.codes - stg.git/blob - stglibs/smux.lib/INTEGER.c
Merge branch 'stg-2.409-radius'
[stg.git] / stglibs / smux.lib / INTEGER.c
1 /*-
2  * Copyright (c) 2003, 2004, 2005, 2006 Lev Walkin <vlm@lionet.info>.
3  * All rights reserved.
4  * Redistribution and modifications are permitted subject to BSD license.
5  */
6 #include <asn_internal.h>
7 #include <INTEGER.h>
8 #include <asn_codecs_prim.h>    /* Encoder and decoder of a primitive type */
9 #include <errno.h>
10
11 /*
12  * INTEGER basic type description.
13  */
14 static ber_tlv_tag_t asn_DEF_INTEGER_tags[] = {
15         (ASN_TAG_CLASS_UNIVERSAL | (2 << 2))
16 };
17 asn_TYPE_descriptor_t asn_DEF_INTEGER = {
18         "INTEGER",
19         "INTEGER",
20         ASN__PRIMITIVE_TYPE_free,
21         INTEGER_print,
22         asn_generic_no_constraint,
23         ber_decode_primitive,
24         INTEGER_encode_der,
25         INTEGER_decode_xer,
26         INTEGER_encode_xer,
27         INTEGER_decode_uper,    /* Unaligned PER decoder */
28         INTEGER_encode_uper,    /* Unaligned PER encoder */
29         0, /* Use generic outmost tag fetcher */
30         asn_DEF_INTEGER_tags,
31         sizeof(asn_DEF_INTEGER_tags) / sizeof(asn_DEF_INTEGER_tags[0]),
32         asn_DEF_INTEGER_tags,   /* Same as above */
33         sizeof(asn_DEF_INTEGER_tags) / sizeof(asn_DEF_INTEGER_tags[0]),
34         0,      /* No PER visible constraints */
35         0, 0,   /* No members */
36         0       /* No specifics */
37 };
38
39 /*
40  * Encode INTEGER type using DER.
41  */
42 asn_enc_rval_t
43 INTEGER_encode_der(asn_TYPE_descriptor_t *td, void *sptr,
44         int tag_mode, ber_tlv_tag_t tag,
45         asn_app_consume_bytes_f *cb, void *app_key) {
46         INTEGER_t *st = (INTEGER_t *)sptr;
47
48         ASN_DEBUG("%s %s as INTEGER (tm=%d)",
49                 cb?"Encoding":"Estimating", td->name, tag_mode);
50
51         /*
52          * Canonicalize integer in the buffer.
53          * (Remove too long sign extension, remove some first 0x00 bytes)
54          */
55         if(st->buf) {
56                 uint8_t *buf = st->buf;
57                 uint8_t *end1 = buf + st->size - 1;
58                 int shift;
59
60                 /* Compute the number of superfluous leading bytes */
61                 for(; buf < end1; buf++) {
62                         /*
63                          * If the contents octets of an integer value encoding
64                          * consist of more than one octet, then the bits of the
65                          * first octet and bit 8 of the second octet:
66                          * a) shall not all be ones; and
67                          * b) shall not all be zero.
68                          */
69                         switch(*buf) {
70                         case 0x00: if((buf[1] & 0x80) == 0)
71                                         continue;
72                                 break;
73                         case 0xff: if((buf[1] & 0x80))
74                                         continue;
75                                 break;
76                         }
77                         break;
78                 }
79
80                 /* Remove leading superfluous bytes from the integer */
81                 shift = buf - st->buf;
82                 if(shift) {
83                         uint8_t *nb = st->buf;
84                         uint8_t *end;
85
86                         st->size -= shift;      /* New size, minus bad bytes */
87                         end = nb + st->size;
88
89                         for(; nb < end; nb++, buf++)
90                                 *nb = *buf;
91                 }
92
93         } /* if(1) */
94
95         return der_encode_primitive(td, sptr, tag_mode, tag, cb, app_key);
96 }
97
98 static const asn_INTEGER_enum_map_t *INTEGER_map_enum2value(asn_INTEGER_specifics_t *specs, const char *lstart, const char *lstop);
99
100 /*
101  * INTEGER specific human-readable output.
102  */
103 static ssize_t
104 INTEGER__dump(asn_TYPE_descriptor_t *td, const INTEGER_t *st, asn_app_consume_bytes_f *cb, void *app_key, int plainOrXER) {
105         asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics;
106         char scratch[32];       /* Enough for 64-bit integer */
107         uint8_t *buf = st->buf;
108         uint8_t *buf_end = st->buf + st->size;
109         signed long accum;
110         ssize_t wrote = 0;
111         char *p;
112
113         /*
114          * Advance buf pointer until the start of the value's body.
115          * This will make us able to process large integers using simple case,
116          * when the actual value is small
117          * (0x0000000000abcdef would yield a fine 0x00abcdef)
118          */
119         /* Skip the insignificant leading bytes */
120         for(; buf < buf_end-1; buf++) {
121                 switch(*buf) {
122                 case 0x00: if((buf[1] & 0x80) == 0) continue; break;
123                 case 0xff: if((buf[1] & 0x80) != 0) continue; break;
124                 }
125                 break;
126         }
127
128         /* Simple case: the integer size is small */
129         if((size_t)(buf_end - buf) <= sizeof(accum)) {
130                 const asn_INTEGER_enum_map_t *el;
131                 size_t scrsize;
132                 int ret;
133                 char *scr;
134
135                 if(buf == buf_end) {
136                         accum = 0;
137                 } else {
138                         accum = (*buf & 0x80) ? -1 : 0;
139                         for(; buf < buf_end; buf++)
140                                 accum = (accum << 8) | *buf;
141                 }
142
143                 el = INTEGER_map_value2enum(specs, accum);
144                 if(el) {
145                         scrsize = el->enum_len + 32;
146                         scr = (char *)alloca(scrsize);
147                         if(plainOrXER == 0)
148                                 ret = snprintf(scr, scrsize,
149                                         "%ld (%s)", accum, el->enum_name);
150                         else
151                                 ret = snprintf(scr, scrsize,
152                                         "<%s/>", el->enum_name);
153                 } else if(plainOrXER && specs && specs->strict_enumeration) {
154                         ASN_DEBUG("ASN.1 forbids dealing with "
155                                 "unknown value of ENUMERATED type");
156                         errno = EPERM;
157                         return -1;
158                 } else {
159                         scrsize = sizeof(scratch);
160                         scr = scratch;
161                         ret = snprintf(scr, scrsize, "%ld", accum);
162                 }
163                 assert(ret > 0 && (size_t)ret < scrsize);
164                 return (cb(scr, ret, app_key) < 0) ? -1 : ret;
165         } else if(plainOrXER && specs && specs->strict_enumeration) {
166                 /*
167                  * Here and earlier, we cannot encode the ENUMERATED values
168                  * if there is no corresponding identifier.
169                  */
170                 ASN_DEBUG("ASN.1 forbids dealing with "
171                         "unknown value of ENUMERATED type");
172                 errno = EPERM;
173                 return -1;
174         }
175
176         /* Output in the long xx:yy:zz... format */
177         /* TODO: replace with generic algorithm (Knuth TAOCP Vol 2, 4.3.1) */
178         for(p = scratch; buf < buf_end; buf++) {
179                 static const char *h2c = "0123456789ABCDEF";
180                 if((p - scratch) >= (ssize_t)(sizeof(scratch) - 4)) {
181                         /* Flush buffer */
182                         if(cb(scratch, p - scratch, app_key) < 0)
183                                 return -1;
184                         wrote += p - scratch;
185                         p = scratch;
186                 }
187                 *p++ = h2c[*buf >> 4];
188                 *p++ = h2c[*buf & 0x0F];
189                 *p++ = 0x3a;    /* ":" */
190         }
191         if(p != scratch)
192                 p--;    /* Remove the last ":" */
193
194         wrote += p - scratch;
195         return (cb(scratch, p - scratch, app_key) < 0) ? -1 : wrote;
196 }
197
198 /*
199  * INTEGER specific human-readable output.
200  */
201 int
202 INTEGER_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
203         asn_app_consume_bytes_f *cb, void *app_key) {
204         const INTEGER_t *st = (const INTEGER_t *)sptr;
205         ssize_t ret;
206
207         (void)td;
208         (void)ilevel;
209
210         if(!st || !st->buf)
211                 ret = cb("<absent>", 8, app_key);
212         else
213                 ret = INTEGER__dump(td, st, cb, app_key, 0);
214
215         return (ret < 0) ? -1 : 0;
216 }
217
218 struct e2v_key {
219         const char *start;
220         const char *stop;
221         asn_INTEGER_enum_map_t *vemap;
222         unsigned int *evmap;
223 };
224 static int
225 INTEGER__compar_enum2value(const void *kp, const void *am) {
226         const struct e2v_key *key = (const struct e2v_key *)kp;
227         const asn_INTEGER_enum_map_t *el = (const asn_INTEGER_enum_map_t *)am;
228         const char *ptr, *end, *name;
229
230         /* Remap the element (sort by different criterion) */
231         el = key->vemap + key->evmap[el - key->vemap];
232
233         /* Compare strings */
234         for(ptr = key->start, end = key->stop, name = el->enum_name;
235                         ptr < end; ptr++, name++) {
236                 if(*ptr != *name)
237                         return *(const unsigned char *)ptr
238                                 - *(const unsigned char *)name;
239         }
240         return name[0] ? -1 : 0;
241 }
242
243 static const asn_INTEGER_enum_map_t *
244 INTEGER_map_enum2value(asn_INTEGER_specifics_t *specs, const char *lstart, const char *lstop) {
245         asn_INTEGER_enum_map_t *el_found;
246         int count = specs ? specs->map_count : 0;
247         struct e2v_key key;
248         const char *lp;
249
250         if(!count) return NULL;
251
252         /* Guaranteed: assert(lstart < lstop); */
253         /* Figure out the tag name */
254         for(lstart++, lp = lstart; lp < lstop; lp++) {
255                 switch(*lp) {
256                 case 9: case 10: case 11: case 12: case 13: case 32: /* WSP */
257                 case 0x2f: /* '/' */ case 0x3e: /* '>' */
258                         break;
259                 default:
260                         continue;
261                 }
262                 break;
263         }
264         if(lp == lstop) return NULL;    /* No tag found */
265         lstop = lp;
266
267         key.start = lstart;
268         key.stop = lstop;
269         key.vemap = specs->value2enum;
270         key.evmap = specs->enum2value;
271         el_found = (asn_INTEGER_enum_map_t *)bsearch(&key,
272                 specs->value2enum, count, sizeof(specs->value2enum[0]),
273                 INTEGER__compar_enum2value);
274         if(el_found) {
275                 /* Remap enum2value into value2enum */
276                 el_found = key.vemap + key.evmap[el_found - key.vemap];
277         }
278         return el_found;
279 }
280
281 static int
282 INTEGER__compar_value2enum(const void *kp, const void *am) {
283         long a = *(const long *)kp;
284         const asn_INTEGER_enum_map_t *el = (const asn_INTEGER_enum_map_t *)am;
285         long b = el->nat_value;
286         if(a < b) return -1;
287         else if(a == b) return 0;
288         else return 1;
289 }
290
291 const asn_INTEGER_enum_map_t *
292 INTEGER_map_value2enum(asn_INTEGER_specifics_t *specs, long value) {
293         int count = specs ? specs->map_count : 0;
294         if(!count) return 0;
295         return (asn_INTEGER_enum_map_t *)bsearch(&value, specs->value2enum,
296                 count, sizeof(specs->value2enum[0]),
297                 INTEGER__compar_value2enum);
298 }
299
300 static int
301 INTEGER_st_prealloc(INTEGER_t *st, int min_size) {
302         void *p = MALLOC(min_size + 1);
303         if(p) {
304                 void *b = st->buf;
305                 st->size = 0;
306                 st->buf = p;
307                 FREEMEM(b);
308                 return 0;
309         } else {
310                 return -1;
311         }
312 }
313
314 /*
315  * Decode the chunk of XML text encoding INTEGER.
316  */
317 static enum xer_pbd_rval
318 INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) {
319         INTEGER_t *st = (INTEGER_t *)sptr;
320         long sign = 1;
321         long value;
322         const char *lp;
323         const char *lstart = (const char *)chunk_buf;
324         const char *lstop = lstart + chunk_size;
325         enum {
326                 ST_SKIPSPACE,
327                 ST_SKIPSPHEX,
328                 ST_WAITDIGITS,
329                 ST_DIGITS,
330                 ST_HEXDIGIT1,
331                 ST_HEXDIGIT2,
332                 ST_HEXCOLON,
333                 ST_EXTRASTUFF
334         } state = ST_SKIPSPACE;
335
336         if(chunk_size)
337                 ASN_DEBUG("INTEGER body %d 0x%2x..0x%2x",
338                         chunk_size, *lstart, lstop[-1]);
339
340         /*
341          * We may have received a tag here. It will be processed inline.
342          * Use strtoul()-like code and serialize the result.
343          */
344         for(value = 0, lp = lstart; lp < lstop; lp++) {
345                 int lv = *lp;
346                 switch(lv) {
347                 case 0x09: case 0x0a: case 0x0d: case 0x20:
348                         switch(state) {
349                         case ST_SKIPSPACE:
350                         case ST_SKIPSPHEX:
351                                 continue;
352                         case ST_HEXCOLON:
353                                 if(xer_is_whitespace(lp, lstop - lp)) {
354                                         lp = lstop - 1;
355                                         continue;
356                                 }
357                                 break;
358                         default:
359                                 break;
360                         }
361                         break;
362                 case 0x2d:      /* '-' */
363                         if(state == ST_SKIPSPACE) {
364                                 sign = -1;
365                                 state = ST_WAITDIGITS;
366                                 continue;
367                         }
368                         break;
369                 case 0x2b:      /* '+' */
370                         if(state == ST_SKIPSPACE) {
371                                 state = ST_WAITDIGITS;
372                                 continue;
373                         }
374                         break;
375                 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
376                 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
377                         switch(state) {
378                         case ST_DIGITS: break;
379                         case ST_SKIPSPHEX:      /* Fall through */
380                         case ST_HEXDIGIT1:
381                                 value = (lv - 0x30) << 4;
382                                 state = ST_HEXDIGIT2;
383                                 continue;
384                         case ST_HEXDIGIT2:
385                                 value += (lv - 0x30);
386                                 state = ST_HEXCOLON;
387                                 st->buf[st->size++] = value;
388                                 continue;
389                         case ST_HEXCOLON:
390                                 return XPBD_BROKEN_ENCODING;
391                         default:
392                                 state = ST_DIGITS;
393                                 break;
394                         }
395
396                     {
397                         long new_value = value * 10;
398
399                         if(new_value / 10 != value)
400                                 /* Overflow */
401                                 return XPBD_DECODER_LIMIT;
402
403                         value = new_value + (lv - 0x30);
404                         /* Check for two's complement overflow */
405                         if(value < 0) {
406                                 /* Check whether it is a LONG_MIN */
407                                 if(sign == -1
408                                 && (unsigned long)value
409                                                 == ~((unsigned long)-1 >> 1)) {
410                                         sign = 1;
411                                 } else {
412                                         /* Overflow */
413                                         return XPBD_DECODER_LIMIT;
414                                 }
415                         }
416                     }
417                         continue;
418                 case 0x3c:      /* '<' */
419                         if(state == ST_SKIPSPACE) {
420                                 const asn_INTEGER_enum_map_t *el;
421                                 el = INTEGER_map_enum2value(
422                                         (asn_INTEGER_specifics_t *)
423                                         td->specifics, lstart, lstop);
424                                 if(el) {
425                                         ASN_DEBUG("Found \"%s\" => %ld",
426                                                 el->enum_name, el->nat_value);
427                                         state = ST_DIGITS;
428                                         value = el->nat_value;
429                                         lp = lstop - 1;
430                                         continue;
431                                 }
432                                 ASN_DEBUG("Unknown identifier for INTEGER");
433                         }
434                         return XPBD_BROKEN_ENCODING;
435                 case 0x3a:      /* ':' */
436                         if(state == ST_HEXCOLON) {
437                                 /* This colon is expected */
438                                 state = ST_HEXDIGIT1;
439                                 continue;
440                         } else if(state == ST_DIGITS) {
441                                 /* The colon here means that we have
442                                  * decoded the first two hexadecimal
443                                  * places as a decimal value.
444                                  * Switch decoding mode. */
445                                 ASN_DEBUG("INTEGER re-evaluate as hex form");
446                                 if(INTEGER_st_prealloc(st, (chunk_size/3) + 1))
447                                         return XPBD_SYSTEM_FAILURE;
448                                 state = ST_SKIPSPHEX;
449                                 lp = lstart - 1;
450                                 continue;
451                         } else {
452                                 ASN_DEBUG("state %d at %d", state, lp - lstart);
453                                 break;
454                         }
455                 /* [A-Fa-f] */
456                 case 0x41:case 0x42:case 0x43:case 0x44:case 0x45:case 0x46:
457                 case 0x61:case 0x62:case 0x63:case 0x64:case 0x65:case 0x66:
458                         switch(state) {
459                         case ST_SKIPSPHEX:
460                         case ST_SKIPSPACE: /* Fall through */
461                         case ST_HEXDIGIT1:
462                                 value = lv - ((lv < 0x61) ? 0x41 : 0x61);
463                                 value += 10;
464                                 value <<= 4;
465                                 state = ST_HEXDIGIT2;
466                                 continue;
467                         case ST_HEXDIGIT2:
468                                 value += lv - ((lv < 0x61) ? 0x41 : 0x61);
469                                 value += 10;
470                                 st->buf[st->size++] = value;
471                                 state = ST_HEXCOLON;
472                                 continue;
473                         case ST_DIGITS:
474                                 ASN_DEBUG("INTEGER re-evaluate as hex form");
475                                 if(INTEGER_st_prealloc(st, (chunk_size/3) + 1))
476                                         return XPBD_SYSTEM_FAILURE;
477                                 state = ST_SKIPSPHEX;
478                                 lp = lstart - 1;
479                                 continue;
480                         default:
481                                 break;
482                         }
483                         break;
484                 }
485
486                 /* Found extra non-numeric stuff */
487                 ASN_DEBUG("Found non-numeric 0x%2x at %d",
488                         lv, lp - lstart);
489                 state = ST_EXTRASTUFF;
490                 break;
491         }
492
493         switch(state) {
494         case ST_DIGITS:
495                 /* Everything is cool */
496                 break;
497         case ST_HEXCOLON:
498                 st->buf[st->size] = 0;  /* Just in case termination */
499                 return XPBD_BODY_CONSUMED;
500         case ST_HEXDIGIT1:
501         case ST_HEXDIGIT2:
502         case ST_SKIPSPHEX:
503                 return XPBD_BROKEN_ENCODING;
504         default:
505                 if(xer_is_whitespace(lp, lstop - lp)) {
506                         if(state != ST_EXTRASTUFF)
507                                 return XPBD_NOT_BODY_IGNORE;
508                         break;
509                 } else {
510                         ASN_DEBUG("INTEGER: No useful digits (state %d)",
511                                 state);
512                         return XPBD_BROKEN_ENCODING;    /* No digits */
513                 }
514                 break;
515         }
516
517         value *= sign;  /* Change sign, if needed */
518
519         if(asn_long2INTEGER(st, value))
520                 return XPBD_SYSTEM_FAILURE;
521
522         return XPBD_BODY_CONSUMED;
523 }
524
525 asn_dec_rval_t
526 INTEGER_decode_xer(asn_codec_ctx_t *opt_codec_ctx,
527         asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname,
528                 const void *buf_ptr, size_t size) {
529
530         return xer_decode_primitive(opt_codec_ctx, td,
531                 sptr, sizeof(INTEGER_t), opt_mname,
532                 buf_ptr, size, INTEGER__xer_body_decode);
533 }
534
535 asn_enc_rval_t
536 INTEGER_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
537         int ilevel, enum xer_encoder_flags_e flags,
538                 asn_app_consume_bytes_f *cb, void *app_key) {
539         const INTEGER_t *st = (const INTEGER_t *)sptr;
540         asn_enc_rval_t er;
541
542         (void)ilevel;
543         (void)flags;
544         
545         if(!st || !st->buf)
546                 _ASN_ENCODE_FAILED;
547
548         er.encoded = INTEGER__dump(td, st, cb, app_key, 1);
549         if(er.encoded < 0) _ASN_ENCODE_FAILED;
550
551         _ASN_ENCODED_OK(er);
552 }
553
554 asn_dec_rval_t
555 INTEGER_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
556         asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) {
557         asn_dec_rval_t rval = { RC_OK, 0 };
558         INTEGER_t *st = (INTEGER_t *)*sptr;
559         asn_per_constraint_t *ct;
560         int repeat;
561
562         (void)opt_codec_ctx;
563
564         if(!st) {
565                 st = (INTEGER_t *)(*sptr = CALLOC(1, sizeof(*st)));
566                 if(!st) _ASN_DECODE_FAILED;
567         }
568
569         if(!constraints) constraints = td->per_constraints;
570         ct = constraints ? &constraints->value : 0;
571
572         if(ct && ct->flags & APC_EXTENSIBLE) {
573                 int inext = per_get_few_bits(pd, 1);
574                 if(inext < 0) _ASN_DECODE_STARVED;
575                 if(inext) ct = 0;
576         }
577
578         FREEMEM(st->buf);
579         if(ct) {
580                 if(ct->flags & APC_SEMI_CONSTRAINED) {
581                         st->buf = (uint8_t *)CALLOC(1, 2);
582                         if(!st->buf) _ASN_DECODE_FAILED;
583                         st->size = 1;
584                 } else if(ct->flags & APC_CONSTRAINED && ct->range_bits >= 0) {
585                         size_t size = (ct->range_bits + 7) >> 3;
586                         st->buf = (uint8_t *)MALLOC(1 + size + 1);
587                         if(!st->buf) _ASN_DECODE_FAILED;
588                         st->size = size;
589                 } else {
590                         st->size = 0;
591                 }
592         } else {
593                 st->size = 0;
594         }
595
596         /* X.691, #12.2.2 */
597         if(ct && ct->flags != APC_UNCONSTRAINED) {
598                 /* #10.5.6 */
599                 ASN_DEBUG("Integer with range %d bits", ct->range_bits);
600                 if(ct->range_bits >= 0) {
601                         long value = per_get_few_bits(pd, ct->range_bits);
602                         if(value < 0) _ASN_DECODE_STARVED;
603                         ASN_DEBUG("Got value %ld + low %ld",
604                                 value, ct->lower_bound);
605                         value += ct->lower_bound;
606                         if(asn_long2INTEGER(st, value))
607                                 _ASN_DECODE_FAILED;
608                         return rval;
609                 }
610         } else {
611                 ASN_DEBUG("Decoding unconstrained integer %s", td->name);
612         }
613
614         /* X.691, #12.2.3, #12.2.4 */
615         do {
616                 ssize_t len;
617                 void *p;
618                 int ret;
619
620                 /* Get the PER length */
621                 len = uper_get_length(pd, -1, &repeat);
622                 if(len < 0) _ASN_DECODE_STARVED;
623
624                 p = REALLOC(st->buf, st->size + len + 1);
625                 if(!p) _ASN_DECODE_FAILED;
626                 st->buf = (uint8_t *)p;
627
628                 ret = per_get_many_bits(pd, &st->buf[st->size], 0, 8 * len);
629                 if(ret < 0) _ASN_DECODE_STARVED;
630                 st->size += len;
631         } while(repeat);
632         st->buf[st->size] = 0;  /* JIC */
633
634         /* #12.2.3 */
635         if(ct && ct->lower_bound) {
636                 /*
637                  * TODO: replace by in-place arithmetics.
638                  */
639                 long value;
640                 if(asn_INTEGER2long(st, &value))
641                         _ASN_DECODE_FAILED;
642                 if(asn_long2INTEGER(st, value + ct->lower_bound))
643                         _ASN_DECODE_FAILED;
644         }
645
646         return rval;
647 }
648
649 asn_enc_rval_t
650 INTEGER_encode_uper(asn_TYPE_descriptor_t *td,
651         asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
652         asn_enc_rval_t er;
653         INTEGER_t *st = (INTEGER_t *)sptr;
654         const uint8_t *buf;
655         const uint8_t *end;
656         asn_per_constraint_t *ct;
657         long value = 0;
658
659         if(!st || st->size == 0) _ASN_ENCODE_FAILED;
660
661         if(!constraints) constraints = td->per_constraints;
662         ct = constraints ? &constraints->value : 0;
663
664         er.encoded = 0;
665
666         if(ct) {
667                 int inext = 0;
668                 if(asn_INTEGER2long(st, &value))
669                         _ASN_ENCODE_FAILED;
670                 /* Check proper range */
671                 if(ct->flags & APC_SEMI_CONSTRAINED) {
672                         if(value < ct->lower_bound)
673                                 inext = 1;
674                 } else if(ct->range_bits >= 0) {
675                         if(value < ct->lower_bound
676                         || value > ct->upper_bound)
677                                 inext = 1;
678                 }
679                 ASN_DEBUG("Value %ld (%02x/%d) lb %ld ub %ld %s",
680                         value, st->buf[0], st->size,
681                         ct->lower_bound, ct->upper_bound,
682                         inext ? "ext" : "fix");
683                 if(ct->flags & APC_EXTENSIBLE) {
684                         if(per_put_few_bits(po, inext, 1))
685                                 _ASN_ENCODE_FAILED;
686                         if(inext) ct = 0;
687                 } else if(inext) {
688                         _ASN_ENCODE_FAILED;
689                 }
690         }
691
692
693         /* X.691, #12.2.2 */
694         if(ct && ct->range_bits >= 0) {
695                 /* #10.5.6 */
696                 ASN_DEBUG("Encoding integer with range %d bits",
697                         ct->range_bits);
698                 if(per_put_few_bits(po, value - ct->lower_bound,
699                                 ct->range_bits))
700                         _ASN_ENCODE_FAILED;
701                 _ASN_ENCODED_OK(er);
702         }
703
704         if(ct && ct->lower_bound) {
705                 ASN_DEBUG("Adjust lower bound to %ld", ct->lower_bound);
706                 /* TODO: adjust lower bound */
707                 _ASN_ENCODE_FAILED;
708         }
709
710         for(buf = st->buf, end = st->buf + st->size; buf < end;) {
711                 ssize_t mayEncode = uper_put_length(po, end - buf);
712                 if(mayEncode < 0)
713                         _ASN_ENCODE_FAILED;
714                 if(per_put_many_bits(po, buf, 8 * mayEncode))
715                         _ASN_ENCODE_FAILED;
716                 buf += mayEncode;
717         }
718
719         _ASN_ENCODED_OK(er);
720 }
721
722 int
723 asn_INTEGER2long(const INTEGER_t *iptr, long *lptr) {
724         uint8_t *b, *end;
725         size_t size;
726         long l;
727
728         /* Sanity checking */
729         if(!iptr || !iptr->buf || !lptr) {
730                 errno = EINVAL;
731                 return -1;
732         }
733
734         /* Cache the begin/end of the buffer */
735         b = iptr->buf;  /* Start of the INTEGER buffer */
736         size = iptr->size;
737         end = b + size; /* Where to stop */
738
739         if(size > sizeof(long)) {
740                 uint8_t *end1 = end - 1;
741                 /*
742                  * Slightly more advanced processing,
743                  * able to >sizeof(long) bytes,
744                  * when the actual value is small
745                  * (0x0000000000abcdef would yield a fine 0x00abcdef)
746                  */
747                 /* Skip out the insignificant leading bytes */
748                 for(; b < end1; b++) {
749                         switch(*b) {
750                         case 0x00: if((b[1] & 0x80) == 0) continue; break;
751                         case 0xff: if((b[1] & 0x80) != 0) continue; break;
752                         }
753                         break;
754                 }
755
756                 size = end - b;
757                 if(size > sizeof(long)) {
758                         /* Still cannot fit the long */
759                         errno = ERANGE;
760                         return -1;
761                 }
762         }
763
764         /* Shortcut processing of a corner case */
765         if(end == b) {
766                 *lptr = 0;
767                 return 0;
768         }
769
770         /* Perform the sign initialization */
771         /* Actually l = -(*b >> 7); gains nothing, yet unreadable! */
772         if((*b >> 7)) l = -1; else l = 0;
773
774         /* Conversion engine */
775         for(; b < end; b++)
776                 l = (l << 8) | *b;
777
778         *lptr = l;
779         return 0;
780 }
781
782 int
783 asn_long2INTEGER(INTEGER_t *st, long value) {
784         uint8_t *buf, *bp;
785         uint8_t *p;
786         uint8_t *pstart;
787         uint8_t *pend1;
788         int littleEndian = 1;   /* Run-time detection */
789         int add;
790
791         if(!st) {
792                 errno = EINVAL;
793                 return -1;
794         }
795
796         buf = (uint8_t *)MALLOC(sizeof(value));
797         if(!buf) return -1;
798
799         if(*(char *)&littleEndian) {
800                 pstart = (uint8_t *)&value + sizeof(value) - 1;
801                 pend1 = (uint8_t *)&value;
802                 add = -1;
803         } else {
804                 pstart = (uint8_t *)&value;
805                 pend1 = pstart + sizeof(value) - 1;
806                 add = 1;
807         }
808
809         /*
810          * If the contents octet consists of more than one octet,
811          * then bits of the first octet and bit 8 of the second octet:
812          * a) shall not all be ones; and
813          * b) shall not all be zero.
814          */
815         for(p = pstart; p != pend1; p += add) {
816                 switch(*p) {
817                 case 0x00: if((*(p+add) & 0x80) == 0)
818                                 continue;
819                         break;
820                 case 0xff: if((*(p+add) & 0x80))
821                                 continue;
822                         break;
823                 }
824                 break;
825         }
826         /* Copy the integer body */
827         for(pstart = p, bp = buf, pend1 += add; p != pend1; p += add)
828                 *bp++ = *p;
829
830         if(st->buf) FREEMEM(st->buf);
831         st->buf = buf;
832         st->size = bp - buf;
833
834         return 0;
835 }