--- /dev/null
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <INTEGER.h>
+
+asn_dec_rval_t
+INTEGER_decode_uper(const asn_codec_ctx_t *opt_codec_ctx,
+ const asn_TYPE_descriptor_t *td,
+ const asn_per_constraints_t *constraints, void **sptr,
+ asn_per_data_t *pd) {
+ const asn_INTEGER_specifics_t *specs =
+ (const asn_INTEGER_specifics_t *)td->specifics;
+ asn_dec_rval_t rval = { RC_OK, 0 };
+ INTEGER_t *st = (INTEGER_t *)*sptr;
+ const 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->encoding_constraints.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);
+ st->buf = 0;
+ st->size = 0;
+ 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;
+ }
+ }
+
+ /* X.691-2008/11, #13.2.2, constrained whole number */
+ if(ct && ct->flags != APC_UNCONSTRAINED) {
+ /* #11.5.6 */
+ ASN_DEBUG("Integer with range %d bits", ct->range_bits);
+ if(ct->range_bits >= 0) {
+ if((size_t)ct->range_bits > 8 * sizeof(uintmax_t))
+ ASN__DECODE_FAILED;
+
+ if(specs && specs->field_unsigned) {
+ uintmax_t uvalue = 0;
+ if(uper_get_constrained_whole_number(pd,
+ &uvalue, ct->range_bits))
+ ASN__DECODE_STARVED;
+ ASN_DEBUG("Got value %lu + low %ld",
+ uvalue, ct->lower_bound);
+ uvalue += ct->lower_bound;
+ if(asn_umax2INTEGER(st, uvalue))
+ ASN__DECODE_FAILED;
+ } else {
+ uintmax_t uvalue = 0;
+ intmax_t svalue;
+ if(uper_get_constrained_whole_number(pd,
+ &uvalue, ct->range_bits))
+ ASN__DECODE_STARVED;
+ ASN_DEBUG("Got value %lu + low %ld",
+ uvalue, ct->lower_bound);
+ if(per_imax_range_unrebase(uvalue, ct->lower_bound,
+ ct->upper_bound, &svalue)
+ || asn_imax2INTEGER(st, svalue)) {
+ 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 = 0;
+ void *p = NULL;
+ int ret = 0;
+
+ /* Get the PER length */
+ len = uper_get_length(pd, -1, 0, &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 arithmetic.
+ */
+ long value = 0;
+ if(asn_INTEGER2long(st, &value))
+ ASN__DECODE_FAILED;
+ if(asn_imax2INTEGER(st, value + ct->lower_bound))
+ ASN__DECODE_FAILED;
+ }
+
+ return rval;
+}
+
+asn_enc_rval_t
+INTEGER_encode_uper(const asn_TYPE_descriptor_t *td,
+ const asn_per_constraints_t *constraints, const void *sptr,
+ asn_per_outp_t *po) {
+ const asn_INTEGER_specifics_t *specs =
+ (const asn_INTEGER_specifics_t *)td->specifics;
+ asn_enc_rval_t er = {0,0,0};
+ const INTEGER_t *st = (const INTEGER_t *)sptr;
+ const uint8_t *buf;
+ const uint8_t *end;
+ const asn_per_constraint_t *ct;
+ union {
+ intmax_t s;
+ uintmax_t u;
+ } value;
+
+ if(!st || st->size == 0) ASN__ENCODE_FAILED;
+
+ if(!constraints) constraints = td->encoding_constraints.per_constraints;
+ ct = constraints ? &constraints->value : 0;
+
+ er.encoded = 0;
+
+ if(ct) {
+ int inext = 0;
+ if(specs && specs->field_unsigned) {
+ if(asn_INTEGER2umax(st, &value.u))
+ ASN__ENCODE_FAILED;
+ /* Check proper range */
+ if(ct->flags & APC_SEMI_CONSTRAINED) {
+ if(value.u < (uintmax_t)ct->lower_bound)
+ inext = 1;
+ } else if(ct->range_bits >= 0) {
+ if(value.u < (uintmax_t)ct->lower_bound
+ || value.u > (uintmax_t)ct->upper_bound)
+ inext = 1;
+ }
+ ASN_DEBUG("Value %lu (%02x/%" ASN_PRI_SIZE ") lb %lu ub %lu %s",
+ value.u, st->buf[0], st->size,
+ ct->lower_bound, ct->upper_bound,
+ inext ? "ext" : "fix");
+ } else {
+ if(asn_INTEGER2imax(st, &value.s))
+ ASN__ENCODE_FAILED;
+ /* Check proper range */
+ if(ct->flags & APC_SEMI_CONSTRAINED) {
+ if(value.s < ct->lower_bound)
+ inext = 1;
+ } else if(ct->range_bits >= 0) {
+ if(value.s < ct->lower_bound
+ || value.s > ct->upper_bound)
+ inext = 1;
+ }
+ ASN_DEBUG("Value %ld (%02x/%" ASN_PRI_SIZE ") lb %ld ub %ld %s",
+ value.s, 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-11/2008, #13.2.2, test if constrained whole number */
+ if(ct && ct->range_bits >= 0) {
+ uintmax_t v;
+ /* #11.5.6 -> #11.3 */
+ if(specs && specs->field_unsigned) {
+ if(((uintmax_t)ct->lower_bound > (uintmax_t)(ct->upper_bound)
+ || (value.u < (uintmax_t)ct->lower_bound))
+ || (value.u > (uintmax_t)ct->upper_bound)) {
+ ASN_DEBUG("Value %lu to-be-encoded is outside the bounds [%lu, %lu]!",
+ value.u, ct->lower_bound, ct->upper_bound);
+ ASN__ENCODE_FAILED;
+ }
+ v = value.u - (uintmax_t)ct->lower_bound;
+ } else {
+ if(per_imax_range_rebase(value.s, ct->lower_bound, ct->upper_bound, &v)) {
+ ASN__ENCODE_FAILED;
+ }
+ }
+ ASN_DEBUG("Encoding integer %lu with range %d bits",
+ v, ct->range_bits);
+ if(uper_put_constrained_whole_number_u(po, v, 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;) {
+ int need_eom = 0;
+ ssize_t mayEncode = uper_put_length(po, end - buf, &need_eom);
+ if(mayEncode < 0)
+ ASN__ENCODE_FAILED;
+ if(per_put_many_bits(po, buf, 8 * mayEncode))
+ ASN__ENCODE_FAILED;
+ buf += mayEncode;
+ if(need_eom && uper_put_length(po, 0, 0)) ASN__ENCODE_FAILED;
+ }
+
+ ASN__ENCODED_OK(er);
+}