]> git.stg.codes - stg.git/blobdiff - libs/smux/INTEGER.c
Port to CMake, get rid of os_int.h.
[stg.git] / libs / smux / INTEGER.c
diff --git a/libs/smux/INTEGER.c b/libs/smux/INTEGER.c
new file mode 100644 (file)
index 0000000..ba455f0
--- /dev/null
@@ -0,0 +1,835 @@
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <INTEGER.h>
+#include <asn_codecs_prim.h>   /* Encoder and decoder of a primitive type */
+#include <errno.h>
+
+/*
+ * INTEGER basic type description.
+ */
+static ber_tlv_tag_t asn_DEF_INTEGER_tags[] = {
+       (ASN_TAG_CLASS_UNIVERSAL | (2 << 2))
+};
+asn_TYPE_descriptor_t asn_DEF_INTEGER = {
+       "INTEGER",
+       "INTEGER",
+       ASN__PRIMITIVE_TYPE_free,
+       INTEGER_print,
+       asn_generic_no_constraint,
+       ber_decode_primitive,
+       INTEGER_encode_der,
+       INTEGER_decode_xer,
+       INTEGER_encode_xer,
+       INTEGER_decode_uper,    /* Unaligned PER decoder */
+       INTEGER_encode_uper,    /* Unaligned PER encoder */
+       0, /* Use generic outmost tag fetcher */
+       asn_DEF_INTEGER_tags,
+       sizeof(asn_DEF_INTEGER_tags) / sizeof(asn_DEF_INTEGER_tags[0]),
+       asn_DEF_INTEGER_tags,   /* Same as above */
+       sizeof(asn_DEF_INTEGER_tags) / sizeof(asn_DEF_INTEGER_tags[0]),
+       0,      /* No PER visible constraints */
+       0, 0,   /* No members */
+       0       /* No specifics */
+};
+
+/*
+ * Encode INTEGER type using DER.
+ */
+asn_enc_rval_t
+INTEGER_encode_der(asn_TYPE_descriptor_t *td, void *sptr,
+       int tag_mode, ber_tlv_tag_t tag,
+       asn_app_consume_bytes_f *cb, void *app_key) {
+       INTEGER_t *st = (INTEGER_t *)sptr;
+
+       ASN_DEBUG("%s %s as INTEGER (tm=%d)",
+               cb?"Encoding":"Estimating", td->name, tag_mode);
+
+       /*
+        * Canonicalize integer in the buffer.
+        * (Remove too long sign extension, remove some first 0x00 bytes)
+        */
+       if(st->buf) {
+               uint8_t *buf = st->buf;
+               uint8_t *end1 = buf + st->size - 1;
+               int shift;
+
+               /* Compute the number of superfluous leading bytes */
+               for(; buf < end1; buf++) {
+                       /*
+                        * If the contents octets of an integer value encoding
+                        * consist of more than one octet, then the bits of the
+                        * first octet and bit 8 of the second octet:
+                        * a) shall not all be ones; and
+                        * b) shall not all be zero.
+                        */
+                       switch(*buf) {
+                       case 0x00: if((buf[1] & 0x80) == 0)
+                                       continue;
+                               break;
+                       case 0xff: if((buf[1] & 0x80))
+                                       continue;
+                               break;
+                       }
+                       break;
+               }
+
+               /* Remove leading superfluous bytes from the integer */
+               shift = buf - st->buf;
+               if(shift) {
+                       uint8_t *nb = st->buf;
+                       uint8_t *end;
+
+                       st->size -= shift;      /* New size, minus bad bytes */
+                       end = nb + st->size;
+
+                       for(; nb < end; nb++, buf++)
+                               *nb = *buf;
+               }
+
+       } /* if(1) */
+
+       return der_encode_primitive(td, sptr, tag_mode, tag, cb, app_key);
+}
+
+static const asn_INTEGER_enum_map_t *INTEGER_map_enum2value(asn_INTEGER_specifics_t *specs, const char *lstart, const char *lstop);
+
+/*
+ * INTEGER specific human-readable output.
+ */
+static ssize_t
+INTEGER__dump(asn_TYPE_descriptor_t *td, const INTEGER_t *st, asn_app_consume_bytes_f *cb, void *app_key, int plainOrXER) {
+       asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics;
+       char scratch[32];       /* Enough for 64-bit integer */
+       uint8_t *buf = st->buf;
+       uint8_t *buf_end = st->buf + st->size;
+       signed long accum;
+       ssize_t wrote = 0;
+       char *p;
+
+       /*
+        * Advance buf pointer until the start of the value's body.
+        * This will make us able to process large integers using simple case,
+        * when the actual value is small
+        * (0x0000000000abcdef would yield a fine 0x00abcdef)
+        */
+       /* Skip the insignificant leading bytes */
+       for(; buf < buf_end-1; buf++) {
+               switch(*buf) {
+               case 0x00: if((buf[1] & 0x80) == 0) continue; break;
+               case 0xff: if((buf[1] & 0x80) != 0) continue; break;
+               }
+               break;
+       }
+
+       /* Simple case: the integer size is small */
+       if((size_t)(buf_end - buf) <= sizeof(accum)) {
+               const asn_INTEGER_enum_map_t *el;
+               size_t scrsize;
+               int ret;
+               char *scr;
+
+               if(buf == buf_end) {
+                       accum = 0;
+               } else {
+                       accum = (*buf & 0x80) ? -1 : 0;
+                       for(; buf < buf_end; buf++)
+                               accum = (accum << 8) | *buf;
+               }
+
+               el = INTEGER_map_value2enum(specs, accum);
+               if(el) {
+                       scrsize = el->enum_len + 32;
+                       scr = (char *)alloca(scrsize);
+                       if(plainOrXER == 0)
+                               ret = snprintf(scr, scrsize,
+                                       "%ld (%s)", accum, el->enum_name);
+                       else
+                               ret = snprintf(scr, scrsize,
+                                       "<%s/>", el->enum_name);
+               } else if(plainOrXER && specs && specs->strict_enumeration) {
+                       ASN_DEBUG("ASN.1 forbids dealing with "
+                               "unknown value of ENUMERATED type");
+                       errno = EPERM;
+                       return -1;
+               } else {
+                       scrsize = sizeof(scratch);
+                       scr = scratch;
+                       ret = snprintf(scr, scrsize, "%ld", accum);
+               }
+               assert(ret > 0 && (size_t)ret < scrsize);
+               return (cb(scr, ret, app_key) < 0) ? -1 : ret;
+       } else if(plainOrXER && specs && specs->strict_enumeration) {
+               /*
+                * Here and earlier, we cannot encode the ENUMERATED values
+                * if there is no corresponding identifier.
+                */
+               ASN_DEBUG("ASN.1 forbids dealing with "
+                       "unknown value of ENUMERATED type");
+               errno = EPERM;
+               return -1;
+       }
+
+       /* Output in the long xx:yy:zz... format */
+       /* TODO: replace with generic algorithm (Knuth TAOCP Vol 2, 4.3.1) */
+       for(p = scratch; buf < buf_end; buf++) {
+               static const char *h2c = "0123456789ABCDEF";
+               if((p - scratch) >= (ssize_t)(sizeof(scratch) - 4)) {
+                       /* Flush buffer */
+                       if(cb(scratch, p - scratch, app_key) < 0)
+                               return -1;
+                       wrote += p - scratch;
+                       p = scratch;
+               }
+               *p++ = h2c[*buf >> 4];
+               *p++ = h2c[*buf & 0x0F];
+               *p++ = 0x3a;    /* ":" */
+       }
+       if(p != scratch)
+               p--;    /* Remove the last ":" */
+
+       wrote += p - scratch;
+       return (cb(scratch, p - scratch, app_key) < 0) ? -1 : wrote;
+}
+
+/*
+ * INTEGER specific human-readable output.
+ */
+int
+INTEGER_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
+       asn_app_consume_bytes_f *cb, void *app_key) {
+       const INTEGER_t *st = (const INTEGER_t *)sptr;
+       ssize_t ret;
+
+       (void)td;
+       (void)ilevel;
+
+       if(!st || !st->buf)
+               ret = cb("<absent>", 8, app_key);
+       else
+               ret = INTEGER__dump(td, st, cb, app_key, 0);
+
+       return (ret < 0) ? -1 : 0;
+}
+
+struct e2v_key {
+       const char *start;
+       const char *stop;
+       asn_INTEGER_enum_map_t *vemap;
+       unsigned int *evmap;
+};
+static int
+INTEGER__compar_enum2value(const void *kp, const void *am) {
+       const struct e2v_key *key = (const struct e2v_key *)kp;
+       const asn_INTEGER_enum_map_t *el = (const asn_INTEGER_enum_map_t *)am;
+       const char *ptr, *end, *name;
+
+       /* Remap the element (sort by different criterion) */
+       el = key->vemap + key->evmap[el - key->vemap];
+
+       /* Compare strings */
+       for(ptr = key->start, end = key->stop, name = el->enum_name;
+                       ptr < end; ptr++, name++) {
+               if(*ptr != *name)
+                       return *(const unsigned char *)ptr
+                               - *(const unsigned char *)name;
+       }
+       return name[0] ? -1 : 0;
+}
+
+static const asn_INTEGER_enum_map_t *
+INTEGER_map_enum2value(asn_INTEGER_specifics_t *specs, const char *lstart, const char *lstop) {
+       asn_INTEGER_enum_map_t *el_found;
+       int count = specs ? specs->map_count : 0;
+       struct e2v_key key;
+       const char *lp;
+
+       if(!count) return NULL;
+
+       /* Guaranteed: assert(lstart < lstop); */
+       /* Figure out the tag name */
+       for(lstart++, lp = lstart; lp < lstop; lp++) {
+               switch(*lp) {
+               case 9: case 10: case 11: case 12: case 13: case 32: /* WSP */
+               case 0x2f: /* '/' */ case 0x3e: /* '>' */
+                       break;
+               default:
+                       continue;
+               }
+               break;
+       }
+       if(lp == lstop) return NULL;    /* No tag found */
+       lstop = lp;
+
+       key.start = lstart;
+       key.stop = lstop;
+       key.vemap = specs->value2enum;
+       key.evmap = specs->enum2value;
+       el_found = (asn_INTEGER_enum_map_t *)bsearch(&key,
+               specs->value2enum, count, sizeof(specs->value2enum[0]),
+               INTEGER__compar_enum2value);
+       if(el_found) {
+               /* Remap enum2value into value2enum */
+               el_found = key.vemap + key.evmap[el_found - key.vemap];
+       }
+       return el_found;
+}
+
+static int
+INTEGER__compar_value2enum(const void *kp, const void *am) {
+       long a = *(const long *)kp;
+       const asn_INTEGER_enum_map_t *el = (const asn_INTEGER_enum_map_t *)am;
+       long b = el->nat_value;
+       if(a < b) return -1;
+       else if(a == b) return 0;
+       else return 1;
+}
+
+const asn_INTEGER_enum_map_t *
+INTEGER_map_value2enum(asn_INTEGER_specifics_t *specs, long value) {
+       int count = specs ? specs->map_count : 0;
+       if(!count) return 0;
+       return (asn_INTEGER_enum_map_t *)bsearch(&value, specs->value2enum,
+               count, sizeof(specs->value2enum[0]),
+               INTEGER__compar_value2enum);
+}
+
+static int
+INTEGER_st_prealloc(INTEGER_t *st, int min_size) {
+       void *p = MALLOC(min_size + 1);
+       if(p) {
+               void *b = st->buf;
+               st->size = 0;
+               st->buf = p;
+               FREEMEM(b);
+               return 0;
+       } else {
+               return -1;
+       }
+}
+
+/*
+ * Decode the chunk of XML text encoding INTEGER.
+ */
+static enum xer_pbd_rval
+INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) {
+       INTEGER_t *st = (INTEGER_t *)sptr;
+       long sign = 1;
+       long value;
+       const char *lp;
+       const char *lstart = (const char *)chunk_buf;
+       const char *lstop = lstart + chunk_size;
+       enum {
+               ST_SKIPSPACE,
+               ST_SKIPSPHEX,
+               ST_WAITDIGITS,
+               ST_DIGITS,
+               ST_HEXDIGIT1,
+               ST_HEXDIGIT2,
+               ST_HEXCOLON,
+               ST_EXTRASTUFF
+       } state = ST_SKIPSPACE;
+
+       if(chunk_size)
+               ASN_DEBUG("INTEGER body %d 0x%2x..0x%2x",
+                       chunk_size, *lstart, lstop[-1]);
+
+       /*
+        * We may have received a tag here. It will be processed inline.
+        * Use strtoul()-like code and serialize the result.
+        */
+       for(value = 0, lp = lstart; lp < lstop; lp++) {
+               int lv = *lp;
+               switch(lv) {
+               case 0x09: case 0x0a: case 0x0d: case 0x20:
+                       switch(state) {
+                       case ST_SKIPSPACE:
+                       case ST_SKIPSPHEX:
+                               continue;
+                       case ST_HEXCOLON:
+                               if(xer_is_whitespace(lp, lstop - lp)) {
+                                       lp = lstop - 1;
+                                       continue;
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               case 0x2d:      /* '-' */
+                       if(state == ST_SKIPSPACE) {
+                               sign = -1;
+                               state = ST_WAITDIGITS;
+                               continue;
+                       }
+                       break;
+               case 0x2b:      /* '+' */
+                       if(state == ST_SKIPSPACE) {
+                               state = ST_WAITDIGITS;
+                               continue;
+                       }
+                       break;
+               case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
+               case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
+                       switch(state) {
+                       case ST_DIGITS: break;
+                       case ST_SKIPSPHEX:      /* Fall through */
+                       case ST_HEXDIGIT1:
+                               value = (lv - 0x30) << 4;
+                               state = ST_HEXDIGIT2;
+                               continue;
+                       case ST_HEXDIGIT2:
+                               value += (lv - 0x30);
+                               state = ST_HEXCOLON;
+                               st->buf[st->size++] = value;
+                               continue;
+                       case ST_HEXCOLON:
+                               return XPBD_BROKEN_ENCODING;
+                       default:
+                               state = ST_DIGITS;
+                               break;
+                       }
+
+                   {
+                       long new_value = value * 10;
+
+                       if(new_value / 10 != value)
+                               /* Overflow */
+                               return XPBD_DECODER_LIMIT;
+
+                       value = new_value + (lv - 0x30);
+                       /* Check for two's complement overflow */
+                       if(value < 0) {
+                               /* Check whether it is a LONG_MIN */
+                               if(sign == -1
+                               && (unsigned long)value
+                                               == ~((unsigned long)-1 >> 1)) {
+                                       sign = 1;
+                               } else {
+                                       /* Overflow */
+                                       return XPBD_DECODER_LIMIT;
+                               }
+                       }
+                   }
+                       continue;
+               case 0x3c:      /* '<' */
+                       if(state == ST_SKIPSPACE) {
+                               const asn_INTEGER_enum_map_t *el;
+                               el = INTEGER_map_enum2value(
+                                       (asn_INTEGER_specifics_t *)
+                                       td->specifics, lstart, lstop);
+                               if(el) {
+                                       ASN_DEBUG("Found \"%s\" => %ld",
+                                               el->enum_name, el->nat_value);
+                                       state = ST_DIGITS;
+                                       value = el->nat_value;
+                                       lp = lstop - 1;
+                                       continue;
+                               }
+                               ASN_DEBUG("Unknown identifier for INTEGER");
+                       }
+                       return XPBD_BROKEN_ENCODING;
+               case 0x3a:      /* ':' */
+                       if(state == ST_HEXCOLON) {
+                               /* This colon is expected */
+                               state = ST_HEXDIGIT1;
+                               continue;
+                       } else if(state == ST_DIGITS) {
+                               /* The colon here means that we have
+                                * decoded the first two hexadecimal
+                                * places as a decimal value.
+                                * Switch decoding mode. */
+                               ASN_DEBUG("INTEGER re-evaluate as hex form");
+                               if(INTEGER_st_prealloc(st, (chunk_size/3) + 1))
+                                       return XPBD_SYSTEM_FAILURE;
+                               state = ST_SKIPSPHEX;
+                               lp = lstart - 1;
+                               continue;
+                       } else {
+                               ASN_DEBUG("state %d at %d", state, lp - lstart);
+                               break;
+                       }
+               /* [A-Fa-f] */
+               case 0x41:case 0x42:case 0x43:case 0x44:case 0x45:case 0x46:
+               case 0x61:case 0x62:case 0x63:case 0x64:case 0x65:case 0x66:
+                       switch(state) {
+                       case ST_SKIPSPHEX:
+                       case ST_SKIPSPACE: /* Fall through */
+                       case ST_HEXDIGIT1:
+                               value = lv - ((lv < 0x61) ? 0x41 : 0x61);
+                               value += 10;
+                               value <<= 4;
+                               state = ST_HEXDIGIT2;
+                               continue;
+                       case ST_HEXDIGIT2:
+                               value += lv - ((lv < 0x61) ? 0x41 : 0x61);
+                               value += 10;
+                               st->buf[st->size++] = value;
+                               state = ST_HEXCOLON;
+                               continue;
+                       case ST_DIGITS:
+                               ASN_DEBUG("INTEGER re-evaluate as hex form");
+                               if(INTEGER_st_prealloc(st, (chunk_size/3) + 1))
+                                       return XPBD_SYSTEM_FAILURE;
+                               state = ST_SKIPSPHEX;
+                               lp = lstart - 1;
+                               continue;
+                       default:
+                               break;
+                       }
+                       break;
+               }
+
+               /* Found extra non-numeric stuff */
+               ASN_DEBUG("Found non-numeric 0x%2x at %d",
+                       lv, lp - lstart);
+               state = ST_EXTRASTUFF;
+               break;
+       }
+
+       switch(state) {
+       case ST_DIGITS:
+               /* Everything is cool */
+               break;
+       case ST_HEXCOLON:
+               st->buf[st->size] = 0;  /* Just in case termination */
+               return XPBD_BODY_CONSUMED;
+       case ST_HEXDIGIT1:
+       case ST_HEXDIGIT2:
+       case ST_SKIPSPHEX:
+               return XPBD_BROKEN_ENCODING;
+       default:
+               if(xer_is_whitespace(lp, lstop - lp)) {
+                       if(state != ST_EXTRASTUFF)
+                               return XPBD_NOT_BODY_IGNORE;
+                       break;
+               } else {
+                       ASN_DEBUG("INTEGER: No useful digits (state %d)",
+                               state);
+                       return XPBD_BROKEN_ENCODING;    /* No digits */
+               }
+               break;
+       }
+
+       value *= sign;  /* Change sign, if needed */
+
+       if(asn_long2INTEGER(st, value))
+               return XPBD_SYSTEM_FAILURE;
+
+       return XPBD_BODY_CONSUMED;
+}
+
+asn_dec_rval_t
+INTEGER_decode_xer(asn_codec_ctx_t *opt_codec_ctx,
+       asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname,
+               const void *buf_ptr, size_t size) {
+
+       return xer_decode_primitive(opt_codec_ctx, td,
+               sptr, sizeof(INTEGER_t), opt_mname,
+               buf_ptr, size, INTEGER__xer_body_decode);
+}
+
+asn_enc_rval_t
+INTEGER_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
+       int ilevel, enum xer_encoder_flags_e flags,
+               asn_app_consume_bytes_f *cb, void *app_key) {
+       const INTEGER_t *st = (const INTEGER_t *)sptr;
+       asn_enc_rval_t er;
+
+       (void)ilevel;
+       (void)flags;
+       
+       if(!st || !st->buf)
+               _ASN_ENCODE_FAILED;
+
+       er.encoded = INTEGER__dump(td, st, cb, app_key, 1);
+       if(er.encoded < 0) _ASN_ENCODE_FAILED;
+
+       _ASN_ENCODED_OK(er);
+}
+
+asn_dec_rval_t
+INTEGER_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) {
+       asn_dec_rval_t rval = { RC_OK, 0 };
+       INTEGER_t *st = (INTEGER_t *)*sptr;
+       asn_per_constraint_t *ct;
+       int repeat;
+
+       (void)opt_codec_ctx;
+
+       if(!st) {
+               st = (INTEGER_t *)(*sptr = CALLOC(1, sizeof(*st)));
+               if(!st) _ASN_DECODE_FAILED;
+       }
+
+       if(!constraints) constraints = td->per_constraints;
+       ct = constraints ? &constraints->value : 0;
+
+       if(ct && ct->flags & APC_EXTENSIBLE) {
+               int inext = per_get_few_bits(pd, 1);
+               if(inext < 0) _ASN_DECODE_STARVED;
+               if(inext) ct = 0;
+       }
+
+       FREEMEM(st->buf);
+       if(ct) {
+               if(ct->flags & APC_SEMI_CONSTRAINED) {
+                       st->buf = (uint8_t *)CALLOC(1, 2);
+                       if(!st->buf) _ASN_DECODE_FAILED;
+                       st->size = 1;
+               } else if(ct->flags & APC_CONSTRAINED && ct->range_bits >= 0) {
+                       size_t size = (ct->range_bits + 7) >> 3;
+                       st->buf = (uint8_t *)MALLOC(1 + size + 1);
+                       if(!st->buf) _ASN_DECODE_FAILED;
+                       st->size = size;
+               } else {
+                       st->size = 0;
+               }
+       } else {
+               st->size = 0;
+       }
+
+       /* X.691, #12.2.2 */
+       if(ct && ct->flags != APC_UNCONSTRAINED) {
+               /* #10.5.6 */
+               ASN_DEBUG("Integer with range %d bits", ct->range_bits);
+               if(ct->range_bits >= 0) {
+                       long value = per_get_few_bits(pd, ct->range_bits);
+                       if(value < 0) _ASN_DECODE_STARVED;
+                       ASN_DEBUG("Got value %ld + low %ld",
+                               value, ct->lower_bound);
+                       value += ct->lower_bound;
+                       if(asn_long2INTEGER(st, value))
+                               _ASN_DECODE_FAILED;
+                       return rval;
+               }
+       } else {
+               ASN_DEBUG("Decoding unconstrained integer %s", td->name);
+       }
+
+       /* X.691, #12.2.3, #12.2.4 */
+       do {
+               ssize_t len;
+               void *p;
+               int ret;
+
+               /* Get the PER length */
+               len = uper_get_length(pd, -1, &repeat);
+               if(len < 0) _ASN_DECODE_STARVED;
+
+               p = REALLOC(st->buf, st->size + len + 1);
+               if(!p) _ASN_DECODE_FAILED;
+               st->buf = (uint8_t *)p;
+
+               ret = per_get_many_bits(pd, &st->buf[st->size], 0, 8 * len);
+               if(ret < 0) _ASN_DECODE_STARVED;
+               st->size += len;
+       } while(repeat);
+       st->buf[st->size] = 0;  /* JIC */
+
+       /* #12.2.3 */
+       if(ct && ct->lower_bound) {
+               /*
+                * TODO: replace by in-place arithmetics.
+                */
+               long value;
+               if(asn_INTEGER2long(st, &value))
+                       _ASN_DECODE_FAILED;
+               if(asn_long2INTEGER(st, value + ct->lower_bound))
+                       _ASN_DECODE_FAILED;
+       }
+
+       return rval;
+}
+
+asn_enc_rval_t
+INTEGER_encode_uper(asn_TYPE_descriptor_t *td,
+       asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
+       asn_enc_rval_t er;
+       INTEGER_t *st = (INTEGER_t *)sptr;
+       const uint8_t *buf;
+       const uint8_t *end;
+       asn_per_constraint_t *ct;
+       long value = 0;
+
+       if(!st || st->size == 0) _ASN_ENCODE_FAILED;
+
+       if(!constraints) constraints = td->per_constraints;
+       ct = constraints ? &constraints->value : 0;
+
+       er.encoded = 0;
+
+       if(ct) {
+               int inext = 0;
+               if(asn_INTEGER2long(st, &value))
+                       _ASN_ENCODE_FAILED;
+               /* Check proper range */
+               if(ct->flags & APC_SEMI_CONSTRAINED) {
+                       if(value < ct->lower_bound)
+                               inext = 1;
+               } else if(ct->range_bits >= 0) {
+                       if(value < ct->lower_bound
+                       || value > ct->upper_bound)
+                               inext = 1;
+               }
+               ASN_DEBUG("Value %ld (%02x/%d) lb %ld ub %ld %s",
+                       value, st->buf[0], st->size,
+                       ct->lower_bound, ct->upper_bound,
+                       inext ? "ext" : "fix");
+               if(ct->flags & APC_EXTENSIBLE) {
+                       if(per_put_few_bits(po, inext, 1))
+                               _ASN_ENCODE_FAILED;
+                       if(inext) ct = 0;
+               } else if(inext) {
+                       _ASN_ENCODE_FAILED;
+               }
+       }
+
+
+       /* X.691, #12.2.2 */
+       if(ct && ct->range_bits >= 0) {
+               /* #10.5.6 */
+               ASN_DEBUG("Encoding integer with range %d bits",
+                       ct->range_bits);
+               if(per_put_few_bits(po, value - ct->lower_bound,
+                               ct->range_bits))
+                       _ASN_ENCODE_FAILED;
+               _ASN_ENCODED_OK(er);
+       }
+
+       if(ct && ct->lower_bound) {
+               ASN_DEBUG("Adjust lower bound to %ld", ct->lower_bound);
+               /* TODO: adjust lower bound */
+               _ASN_ENCODE_FAILED;
+       }
+
+       for(buf = st->buf, end = st->buf + st->size; buf < end;) {
+               ssize_t mayEncode = uper_put_length(po, end - buf);
+               if(mayEncode < 0)
+                       _ASN_ENCODE_FAILED;
+               if(per_put_many_bits(po, buf, 8 * mayEncode))
+                       _ASN_ENCODE_FAILED;
+               buf += mayEncode;
+       }
+
+       _ASN_ENCODED_OK(er);
+}
+
+int
+asn_INTEGER2long(const INTEGER_t *iptr, long *lptr) {
+       uint8_t *b, *end;
+       size_t size;
+       long l;
+
+       /* Sanity checking */
+       if(!iptr || !iptr->buf || !lptr) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       /* Cache the begin/end of the buffer */
+       b = iptr->buf;  /* Start of the INTEGER buffer */
+       size = iptr->size;
+       end = b + size; /* Where to stop */
+
+       if(size > sizeof(long)) {
+               uint8_t *end1 = end - 1;
+               /*
+                * Slightly more advanced processing,
+                * able to >sizeof(long) bytes,
+                * when the actual value is small
+                * (0x0000000000abcdef would yield a fine 0x00abcdef)
+                */
+               /* Skip out the insignificant leading bytes */
+               for(; b < end1; b++) {
+                       switch(*b) {
+                       case 0x00: if((b[1] & 0x80) == 0) continue; break;
+                       case 0xff: if((b[1] & 0x80) != 0) continue; break;
+                       }
+                       break;
+               }
+
+               size = end - b;
+               if(size > sizeof(long)) {
+                       /* Still cannot fit the long */
+                       errno = ERANGE;
+                       return -1;
+               }
+       }
+
+       /* Shortcut processing of a corner case */
+       if(end == b) {
+               *lptr = 0;
+               return 0;
+       }
+
+       /* Perform the sign initialization */
+       /* Actually l = -(*b >> 7); gains nothing, yet unreadable! */
+       if((*b >> 7)) l = -1; else l = 0;
+
+       /* Conversion engine */
+       for(; b < end; b++)
+               l = (l << 8) | *b;
+
+       *lptr = l;
+       return 0;
+}
+
+int
+asn_long2INTEGER(INTEGER_t *st, long value) {
+       uint8_t *buf, *bp;
+       uint8_t *p;
+       uint8_t *pstart;
+       uint8_t *pend1;
+       int littleEndian = 1;   /* Run-time detection */
+       int add;
+
+       if(!st) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       buf = (uint8_t *)MALLOC(sizeof(value));
+       if(!buf) return -1;
+
+       if(*(char *)&littleEndian) {
+               pstart = (uint8_t *)&value + sizeof(value) - 1;
+               pend1 = (uint8_t *)&value;
+               add = -1;
+       } else {
+               pstart = (uint8_t *)&value;
+               pend1 = pstart + sizeof(value) - 1;
+               add = 1;
+       }
+
+       /*
+        * If the contents octet consists of more than one octet,
+        * then bits of the first octet and bit 8 of the second octet:
+        * a) shall not all be ones; and
+        * b) shall not all be zero.
+        */
+       for(p = pstart; p != pend1; p += add) {
+               switch(*p) {
+               case 0x00: if((*(p+add) & 0x80) == 0)
+                               continue;
+                       break;
+               case 0xff: if((*(p+add) & 0x80))
+                               continue;
+                       break;
+               }
+               break;
+       }
+       /* Copy the integer body */
+       for(pstart = p, bp = buf, pend1 += add; p != pend1; p += add)
+               *bp++ = *p;
+
+       if(st->buf) FREEMEM(st->buf);
+       st->buf = buf;
+       st->size = bp - buf;
+
+       return 0;
+}