+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef ASN_DISABLE_OER_SUPPORT
+
+#include <asn_internal.h>
+#include <constr_SET_OF.h>
+#include <asn_SET_OF.h>
+#include <errno.h>
+
+/*
+ * This macro "eats" the part of the buffer which is definitely "consumed",
+ * i.e. was correctly converted into local representation or rightfully skipped.
+ */
+#undef ADVANCE
+#define ADVANCE(num_bytes) \
+ do { \
+ size_t num = num_bytes; \
+ ptr = ((const char *)ptr) + num; \
+ size -= num; \
+ consumed_myself += num; \
+ } while(0)
+
+/*
+ * Switch to the next phase of parsing.
+ */
+#undef NEXT_PHASE
+#define NEXT_PHASE(ctx) \
+ do { \
+ ctx->phase++; \
+ ctx->step = 0; \
+ } while(0)
+#undef SET_PHASE
+#define SET_PHASE(ctx, value) \
+ do { \
+ ctx->phase = value; \
+ ctx->step = 0; \
+ } while(0)
+
+/*
+ * Return a standardized complex structure.
+ */
+#undef RETURN
+#define RETURN(_code) \
+ do { \
+ asn_dec_rval_t rval; \
+ rval.code = _code; \
+ rval.consumed = consumed_myself; \
+ return rval; \
+ } while(0)
+
+/*
+ * The SEQUENCE OF and SET OF values utilize a "quantity field".
+ * It is is a pointless combination of #8.6 (length determinant, capable
+ * of encoding tiny and huge numbers in the shortest possible number of octets)
+ * and the variable sized integer. What could have been encoded by #8.6 alone
+ * is required to be encoded by #8.6 followed by that number of unsigned octets.
+ * This doesn't make too much sense. It seems that the original version of OER
+ * standard have been using the unconstrained unsigned integer as a quantity
+ * field, and this legacy have gone through ISO/ITU-T standardization process.
+ */
+static ssize_t
+oer_fetch_quantity(const void *ptr, size_t size, size_t *qty_r) {
+ const uint8_t *b;
+ const uint8_t *bend;
+ size_t len = 0;
+ size_t qty;
+
+ ssize_t len_len = oer_fetch_length(ptr, size, &len);
+ if(len_len <= 0) {
+ *qty_r = 0;
+ return len_len;
+ }
+
+ if((len_len + len) > size) {
+ *qty_r = 0;
+ return 0;
+ }
+
+ b = (const uint8_t *)ptr + len_len;
+ bend = b + len;
+
+ /* Skip the leading 0-bytes */
+ for(; b < bend && *b == 0; b++) {
+ }
+
+ if((bend - b) > (ssize_t)sizeof(size_t)) {
+ /* Length is not representable by the native size_t type */
+ *qty_r = 0;
+ return -1;
+ }
+
+ for(qty = 0; b < bend; b++) {
+ qty = (qty << 8) + *b;
+ }
+
+ if(qty > RSIZE_MAX) { /* A bit of C11 validation */
+ *qty_r = 0;
+ return -1;
+ }
+
+ *qty_r = qty;
+ assert((size_t)len_len + len == (size_t)(bend - (const uint8_t *)ptr));
+ return len_len + len;
+}
+
+asn_dec_rval_t
+SET_OF_decode_oer(const asn_codec_ctx_t *opt_codec_ctx,
+ const asn_TYPE_descriptor_t *td,
+ const asn_oer_constraints_t *constraints, void **struct_ptr,
+ const void *ptr, size_t size) {
+ const asn_SET_OF_specifics_t *specs = (const asn_SET_OF_specifics_t *)td->specifics;
+ asn_dec_rval_t rval = {RC_OK, 0};
+ void *st = *struct_ptr; /* Target structure */
+ asn_struct_ctx_t *ctx; /* Decoder context */
+ size_t consumed_myself = 0; /* Consumed bytes from ptr. */
+
+ (void)constraints;
+
+ if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx))
+ ASN__DECODE_FAILED;
+
+ /*
+ * Create the target structure if it is not present already.
+ */
+ if(st == 0) {
+ st = *struct_ptr = CALLOC(1, specs->struct_size);
+ if(st == 0) {
+ RETURN(RC_FAIL);
+ }
+ }
+
+ /*
+ * Restore parsing context.
+ */
+ ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset);
+
+ /*
+ * Start to parse where left previously.
+ */
+ switch(ctx->phase) {
+ case 0: {
+ /*
+ * Fetch number of elements to decode.
+ */
+ size_t length = 0;
+ size_t len_size = oer_fetch_quantity(ptr, size, &length);
+ switch(len_size) {
+ case 0:
+ RETURN(RC_WMORE);
+ case -1:
+ RETURN(RC_FAIL);
+ default:
+ ADVANCE(len_size);
+ ctx->left = length;
+ }
+ }
+ NEXT_PHASE(ctx);
+ /* FALL THROUGH */
+ case 1: {
+ /* Decode components of the extension root */
+ asn_TYPE_member_t *elm = td->elements;
+ asn_anonymous_set_ *list = _A_SET_FROM_VOID(st);
+ const void *base_ptr = ptr;
+ ber_tlv_len_t base_ctx_left = ctx->left;
+
+ assert(td->elements_count == 1);
+
+ ASN_DEBUG("OER SET OF %s Decoding PHASE 1", td->name);
+
+ for(; ctx->left > 0; ctx->left--) {
+ asn_dec_rval_t rv = elm->type->op->oer_decoder(
+ opt_codec_ctx, elm->type,
+ elm->encoding_constraints.oer_constraints, &ctx->ptr, ptr,
+ size);
+ ADVANCE(rv.consumed);
+ switch(rv.code) {
+ case RC_OK:
+ if(ASN_SET_ADD(list, ctx->ptr) != 0) {
+ RETURN(RC_FAIL);
+ } else {
+ ctx->ptr = 0;
+ /*
+ * This check is to avoid compression bomb with
+ * specs like SEQUENCE/SET OF NULL which don't
+ * consume data at all.
+ */
+ if(rv.consumed == 0 && base_ptr == ptr
+ && (base_ctx_left - ctx->left) > 200) {
+ ASN__DECODE_FAILED;
+ }
+ break;
+ }
+ case RC_WMORE:
+ RETURN(RC_WMORE);
+ case RC_FAIL:
+ ASN_STRUCT_FREE(*elm->type, ctx->ptr);
+ ctx->ptr = 0;
+ SET_PHASE(ctx, 3);
+ RETURN(RC_FAIL);
+ }
+ }
+ /* Decoded decently. */
+ NEXT_PHASE(ctx);
+ }
+ /* Fall through */
+ case 2:
+ /* Ignore fully decoded */
+ assert(ctx->left == 0);
+ RETURN(RC_OK);
+ case 3:
+ /* Failed to decode. */
+ RETURN(RC_FAIL);
+ }
+
+ return rval;
+}
+
+static ssize_t
+oer_put_quantity(size_t qty, asn_app_consume_bytes_f *cb, void *app_key) {
+ uint8_t buf[1 + sizeof(size_t)];
+ uint8_t *b = &buf[sizeof(size_t)]; /* Last addressable */
+ size_t encoded;
+
+ do {
+ *b-- = qty;
+ qty >>= 8;
+ } while(qty);
+
+ *b = sizeof(buf) - (b-buf) - 1;
+ encoded = sizeof(buf) - (b-buf);
+ if(cb(b, encoded, app_key) < 0)
+ return -1;
+ return encoded;
+}
+
+/*
+ * Encode as Canonical OER.
+ */
+asn_enc_rval_t
+SET_OF_encode_oer(const asn_TYPE_descriptor_t *td,
+ const asn_oer_constraints_t *constraints, const void *sptr,
+ asn_app_consume_bytes_f *cb, void *app_key) {
+ const asn_TYPE_member_t *elm;
+ const asn_anonymous_set_ *list;
+ size_t computed_size = 0;
+ ssize_t qty_len;
+ int n;
+
+ (void)constraints;
+
+ if(!sptr) ASN__ENCODE_FAILED;
+
+ elm = td->elements;
+ list = _A_CSET_FROM_VOID(sptr);
+
+ qty_len = oer_put_quantity(list->count, cb, app_key);
+ if(qty_len < 0) {
+ ASN__ENCODE_FAILED;
+ }
+ computed_size += qty_len;
+
+ for(n = 0; n < list->count; n++) {
+ void *memb_ptr = list->array[n];
+ asn_enc_rval_t er = {0,0,0};
+ er = elm->type->op->oer_encoder(
+ elm->type, elm->encoding_constraints.oer_constraints, memb_ptr, cb,
+ app_key);
+ if(er.encoded < 0) {
+ return er;
+ } else {
+ computed_size += er.encoded;
+ }
+ }
+
+ {
+ asn_enc_rval_t erval = {0,0,0};
+ erval.encoded = computed_size;
+ ASN__ENCODED_OK(erval);
+ }
+}
+
+#endif /* ASN_DISABLE_OER_SUPPORT */