SIM-115: update simulator to use latest E2SM KPM version 3
[sim/e2-interface.git] / e2sim / asn1c / constr_SEQUENCE_ber.c
diff --git a/e2sim/asn1c/constr_SEQUENCE_ber.c b/e2sim/asn1c/constr_SEQUENCE_ber.c
new file mode 100644 (file)
index 0000000..e982172
--- /dev/null
@@ -0,0 +1,612 @@
+/*
+ * 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 <constr_SEQUENCE.h>
+#include <OPEN_TYPE.h>
+
+/*
+ * Number of bytes left for this structure.
+ * (ctx->left) indicates the number of bytes _transferred_ for the structure.
+ * (size) contains the number of bytes in the buffer passed.
+ */
+#define LEFT ((size<(size_t)ctx->left)?size:(size_t)ctx->left)
+
+/*
+ * If the subprocessor function returns with an indication that it wants
+ * more data, it may well be a fatal decoding problem, because the
+ * size is constrained by the <TLV>'s L, even if the buffer size allows
+ * reading more data.
+ * For example, consider the buffer containing the following TLVs:
+ * <T:5><L:1><V> <T:6>...
+ * The TLV length clearly indicates that one byte is expected in V, but
+ * if the V processor returns with "want more data" even if the buffer
+ * contains way more data than the V processor have seen.
+ */
+#define SIZE_VIOLATION (ctx->left >= 0 && (size_t)ctx->left <= size)
+
+/*
+ * 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;                      \
+        if(ctx->left >= 0)                \
+            ctx->left -= 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 PHASE_OUT
+#define PHASE_OUT(ctx)    \
+    do {                  \
+        ctx->phase = 10;  \
+    } while(0)
+
+/*
+ * Return a standardized complex structure.
+ */
+#undef RETURN
+#define RETURN(_code)                     \
+    do {                                  \
+        rval.code = _code;                \
+        rval.consumed = consumed_myself;  \
+        return rval;                      \
+    } while(0)
+
+/*
+ * Check whether we are inside the extensions group.
+ */
+#define IN_EXTENSION_GROUP(specs, memb_idx)                \
+    ((specs)->first_extension >= 0                         \
+     && (unsigned)(specs)->first_extension <= (memb_idx))
+
+/*
+ * Tags are canonically sorted in the tag2element map.
+ */
+static int
+_t2e_cmp(const void *ap, const void *bp) {
+    const asn_TYPE_tag2member_t *a = (const asn_TYPE_tag2member_t *)ap;
+    const asn_TYPE_tag2member_t *b = (const asn_TYPE_tag2member_t *)bp;
+
+    int a_class = BER_TAG_CLASS(a->el_tag);
+    int b_class = BER_TAG_CLASS(b->el_tag);
+
+    if(a_class == b_class) {
+        ber_tlv_tag_t a_value = BER_TAG_VALUE(a->el_tag);
+        ber_tlv_tag_t b_value = BER_TAG_VALUE(b->el_tag);
+
+        if(a_value == b_value) {
+            if(a->el_no > b->el_no)
+                return 1;
+            /*
+             * Important: we do not check
+             * for a->el_no <= b->el_no!
+             */
+            return 0;
+        } else if(a_value < b_value)
+            return -1;
+        else
+            return 1;
+    } else if(a_class < b_class) {
+        return -1;
+    } else {
+        return 1;
+    }
+}
+
+/*
+ * The decoder of the SEQUENCE type.
+ */
+asn_dec_rval_t
+SEQUENCE_decode_ber(const asn_codec_ctx_t *opt_codec_ctx,
+                    const asn_TYPE_descriptor_t *td, void **struct_ptr,
+                    const void *ptr, size_t size, int tag_mode) {
+    /*
+     * Bring closer parts of structure description.
+     */
+    const asn_SEQUENCE_specifics_t *specs = (const asn_SEQUENCE_specifics_t *)td->specifics;
+    const asn_TYPE_member_t *elements = td->elements;
+
+    /*
+     * Parts of the structure being constructed.
+     */
+    void *st = *struct_ptr;  /* Target structure. */
+    asn_struct_ctx_t *ctx;   /* Decoder context */
+
+    ber_tlv_tag_t tlv_tag;  /* T from TLV */
+    asn_dec_rval_t rval;    /* Return code from subparsers */
+
+    ssize_t consumed_myself = 0;  /* Consumed bytes from ptr */
+    size_t edx;  /* SEQUENCE element's index */
+
+    ASN_DEBUG("Decoding %s as SEQUENCE", td->name);
+
+    /*
+     * 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:
+        /*
+         * PHASE 0.
+         * Check that the set of tags associated with given structure
+         * perfectly fits our expectations.
+         */
+
+        rval = ber_check_tags(opt_codec_ctx, td, ctx, ptr, size,
+                              tag_mode, 1, &ctx->left, 0);
+        if(rval.code != RC_OK) {
+            ASN_DEBUG("%s tagging check failed: %d",
+                      td->name, rval.code);
+            return rval;
+        }
+
+        if(ctx->left >= 0)
+            ctx->left += rval.consumed;  /* ?Subtracted below! */
+        ADVANCE(rval.consumed);
+
+        NEXT_PHASE(ctx);
+
+        ASN_DEBUG("Structure consumes %ld bytes, buffer %ld",
+                  (long)ctx->left, (long)size);
+
+        /* Fall through */
+    case 1:
+        /*
+         * PHASE 1.
+         * From the place where we've left it previously,
+         * try to decode the next member from the list of
+         * this structure's elements.
+         * (ctx->step) stores the member being processed
+         * between invocations and the microphase {0,1} of parsing
+         * that member:
+         *     step = (<member_number> * 2 + <microphase>).
+         */
+        for(edx = ((size_t)ctx->step >> 1); edx < td->elements_count;
+                edx++, ctx->step = (ctx->step & ~1) + 2) {
+            void *memb_ptr;      /* Pointer to the member */
+            void **memb_ptr2;    /* Pointer to that pointer */
+            ssize_t tag_len;     /* Length of TLV's T */
+            size_t opt_edx_end;  /* Next non-optional element */
+            size_t n;
+            int use_bsearch;
+
+            if(ctx->step & 1)
+                goto microphase2;
+
+            /*
+             * MICROPHASE 1: Synchronize decoding.
+             */
+            ASN_DEBUG("In %s SEQUENCE left %d, edx=%" ASN_PRI_SIZE " flags=%d"
+                      " opt=%d ec=%d",
+                      td->name, (int)ctx->left, edx,
+                      elements[edx].flags, elements[edx].optional,
+                      td->elements_count);
+
+            if(ctx->left == 0 /* No more stuff is expected */
+               && (
+                      /* Explicit OPTIONAL specification reaches the end */
+                      (edx + elements[edx].optional == td->elements_count) ||
+                      /* All extensions are optional */
+                      IN_EXTENSION_GROUP(specs, edx))) {
+                ASN_DEBUG("End of SEQUENCE %s", td->name);
+                /*
+                 * Found the legitimate end of the structure.
+                 */
+                PHASE_OUT(ctx);
+                RETURN(RC_OK);
+            }
+
+            /*
+             * Fetch the T from TLV.
+             */
+            tag_len = ber_fetch_tag(ptr, LEFT, &tlv_tag);
+            ASN_DEBUG("Current tag in %s SEQUENCE for element %" ASN_PRI_SIZE " "
+                      "(%s) is %s encoded in %d bytes, of frame %ld",
+                      td->name, edx, elements[edx].name,
+                      ber_tlv_tag_string(tlv_tag), (int)tag_len, (long)LEFT);
+            switch(tag_len) {
+            case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
+                /* Fall through */
+            case -1: RETURN(RC_FAIL);
+            }
+
+            if(ctx->left < 0 && ((const uint8_t *)ptr)[0] == 0) {
+                if(LEFT < 2) {
+                    if(SIZE_VIOLATION) {
+                        RETURN(RC_FAIL);
+                    } else {
+                        RETURN(RC_WMORE);
+                    }
+                } else if(((const uint8_t *)ptr)[1] == 0) {
+                    ASN_DEBUG("edx = %" ASN_PRI_SIZE ", opt = %d, ec=%d", edx,
+                              elements[edx].optional, td->elements_count);
+                    if((edx + elements[edx].optional == td->elements_count)
+                       || IN_EXTENSION_GROUP(specs, edx)) {
+                        /*
+                         * Yeah, baby! Found the terminator
+                         * of the indefinite length structure.
+                         */
+                        /*
+                         * Proceed to the canonical
+                         * finalization function.
+                         * No advancing is necessary.
+                         */
+                        goto phase3;
+                    }
+                }
+            }
+
+            /*
+             * Find the next available type with this tag.
+             */
+            use_bsearch = 0;
+            opt_edx_end = edx + elements[edx].optional + 1;
+            if(opt_edx_end > td->elements_count)
+                opt_edx_end = td->elements_count;  /* Cap */
+            else if(opt_edx_end - edx > 8) {
+                /* Limit the scope of linear search... */
+                opt_edx_end = edx + 8;
+                use_bsearch = 1;
+                /* ... and resort to bsearch() */
+            }
+            for(n = edx; n < opt_edx_end; n++) {
+                if(BER_TAGS_EQUAL(tlv_tag, elements[n].tag)) {
+                    /*
+                     * Found element corresponding to the tag
+                     * being looked at.
+                     * Reposition over the right element.
+                     */
+                    edx = n;
+                    ctx->step = 1 + 2 * edx;  /* Remember! */
+                    goto microphase2;
+                } else if(elements[n].flags & ATF_ANY_TYPE) {
+                    /*
+                     * This is the ANY type, which may bear
+                     * any flag whatsoever.
+                     */
+                    edx = n;
+                    ctx->step = 1 + 2 * edx;  /* Remember! */
+                    goto microphase2;
+                } else if(elements[n].tag == (ber_tlv_tag_t)-1) {
+                    use_bsearch = 1;
+                    break;
+                }
+            }
+            if(use_bsearch) {
+                /*
+                 * Resort to a binary search over
+                 * sorted array of tags.
+                 */
+                const asn_TYPE_tag2member_t *t2m;
+                asn_TYPE_tag2member_t key = {0, 0, 0, 0};
+                key.el_tag = tlv_tag;
+                key.el_no = edx;
+                t2m = (const asn_TYPE_tag2member_t *)bsearch(&key,
+                       specs->tag2el, specs->tag2el_count,
+                       sizeof(specs->tag2el[0]), _t2e_cmp);
+                if(t2m) {
+                    const asn_TYPE_tag2member_t *best = 0;
+                    const asn_TYPE_tag2member_t *t2m_f, *t2m_l;
+                    size_t edx_max = edx + elements[edx].optional;
+                    /*
+                     * Rewind to the first element with that tag,
+                     * `cause bsearch() does not guarantee order.
+                     */
+                    t2m_f = t2m + t2m->toff_first;
+                    t2m_l = t2m + t2m->toff_last;
+                    for(t2m = t2m_f; t2m <= t2m_l; t2m++) {
+                        if(t2m->el_no > edx_max) break;
+                        if(t2m->el_no < edx) continue;
+                        best = t2m;
+                    }
+                    if(best) {
+                        edx = best->el_no;
+                        ctx->step = 1 + 2 * edx;
+                        goto microphase2;
+                    }
+                }
+                n = opt_edx_end;
+            }
+            if(n == opt_edx_end) {
+                /*
+                 * If tag is unknown, it may be either
+                 * an unknown (thus, incorrect) tag,
+                 * or an extension (...),
+                 * or an end of the indefinite-length structure.
+                 */
+                if(!IN_EXTENSION_GROUP(specs,
+                    edx + elements[edx].optional)) {
+                    ASN_DEBUG("Unexpected tag %s (at %" ASN_PRI_SIZE ")",
+                              ber_tlv_tag_string(tlv_tag), edx);
+                    ASN_DEBUG("Expected tag %s (%s)%s",
+                              ber_tlv_tag_string(elements[edx].tag),
+                              elements[edx].name,
+                              elements[edx].optional ?" or alternatives":"");
+                    RETURN(RC_FAIL);
+                } else {
+                    /* Skip this tag */
+                    ssize_t skip;
+                    edx += elements[edx].optional;
+
+                    ASN_DEBUG("Skipping unexpected %s (at %" ASN_PRI_SIZE ")",
+                              ber_tlv_tag_string(tlv_tag), edx);
+                    skip = ber_skip_length(opt_codec_ctx,
+                                           BER_TLV_CONSTRUCTED(ptr),
+                                           (const char *)ptr + tag_len,
+                                           LEFT - tag_len);
+                    ASN_DEBUG("Skip length %d in %s",
+                              (int)skip, td->name);
+                    switch(skip) {
+                    case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
+                        /* Fall through */
+                    case -1: RETURN(RC_FAIL);
+                    }
+
+                    ADVANCE(skip + tag_len);
+                    ctx->step -= 2;
+                    edx--;
+                    continue;  /* Try again with the next tag */
+                }
+            }
+
+            /*
+             * MICROPHASE 2: Invoke the member-specific decoder.
+             */
+            ctx->step |= 1;  /* Confirm entering next microphase */
+        microphase2:
+            ASN_DEBUG("Inside SEQUENCE %s MF2", td->name);
+
+            /*
+             * Compute the position of the member inside a structure,
+             * and also a type of containment (it may be contained
+             * as pointer or using inline inclusion).
+             */
+            if(elements[edx].flags & ATF_POINTER) {
+                /* Member is a pointer to another structure */
+                memb_ptr2 = (void **)((char *)st + elements[edx].memb_offset);
+            } else {
+                /*
+                 * A pointer to a pointer
+                 * holding the start of the structure
+                 */
+                memb_ptr = (char *)st + elements[edx].memb_offset;
+                memb_ptr2 = &memb_ptr;
+            }
+            /*
+             * Invoke the member fetch routine according to member's type
+             */
+            if(elements[edx].flags & ATF_OPEN_TYPE) {
+                rval = OPEN_TYPE_ber_get(opt_codec_ctx, td, st, &elements[edx], ptr, LEFT);
+            } else {
+                rval = elements[edx].type->op->ber_decoder(opt_codec_ctx,
+                                                           elements[edx].type,
+                                                           memb_ptr2, ptr, LEFT,
+                                                           elements[edx].tag_mode);
+            }
+            ASN_DEBUG("In %s SEQUENCE decoded %" ASN_PRI_SIZE " %s of %d "
+                      "in %d bytes rval.code %d, size=%d",
+                      td->name, edx, elements[edx].type->name,
+                      (int)LEFT, (int)rval.consumed, rval.code, (int)size);
+            switch(rval.code) {
+            case RC_OK:
+                break;
+            case RC_WMORE: /* More data expected */
+                if(!SIZE_VIOLATION) {
+                    ADVANCE(rval.consumed);
+                    RETURN(RC_WMORE);
+                }
+                ASN_DEBUG("Size violation (c->l=%ld <= s=%ld)",
+                          (long)ctx->left, (long)size);
+                /* Fall through */
+            case RC_FAIL: /* Fatal error */
+                RETURN(RC_FAIL);
+        } /* switch(rval) */
+
+        ADVANCE(rval.consumed);
+    }  /* for(all structure members) */
+
+    phase3:
+        ctx->phase = 3;
+        /* Fall through */
+    case 3:  /* 00 and other tags expected */
+    case 4:  /* only 00's expected */
+
+        ASN_DEBUG("SEQUENCE %s Leftover: %ld, size = %ld",
+                  td->name, (long)ctx->left, (long)size);
+
+        /*
+         * Skip everything until the end of the SEQUENCE.
+         */
+        while(ctx->left) {
+            ssize_t tl, ll;
+
+            tl = ber_fetch_tag(ptr, LEFT, &tlv_tag);
+            switch(tl) {
+            case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
+                /* Fall through */
+            case -1: RETURN(RC_FAIL);
+            }
+
+            /*
+             * If expected <0><0>...
+             */
+            if(ctx->left < 0 && ((const uint8_t *)ptr)[0] == 0) {
+                if(LEFT < 2) {
+                    if(SIZE_VIOLATION)
+                        RETURN(RC_FAIL);
+                    else
+                        RETURN(RC_WMORE);
+                } else if(((const uint8_t *)ptr)[1] == 0) {
+                    /*
+                     * Correctly finished with <0><0>.
+                     */
+                    ADVANCE(2);
+                    ctx->left++;
+                    ctx->phase = 4;
+                    continue;
+                }
+            }
+
+            if(!IN_EXTENSION_GROUP(specs, td->elements_count)
+            || ctx->phase == 4) {
+                ASN_DEBUG("Unexpected continuation "
+                          "of a non-extensible type "
+                          "%s (SEQUENCE): %s",
+                          td->name,
+                          ber_tlv_tag_string(tlv_tag));
+                RETURN(RC_FAIL);
+            }
+
+            ll = ber_skip_length(opt_codec_ctx,
+                                 BER_TLV_CONSTRUCTED(ptr),
+                                 (const char *)ptr + tl, LEFT - tl);
+            switch(ll) {
+            case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
+                /* Fall through */
+            case -1: RETURN(RC_FAIL);
+            }
+
+            ADVANCE(tl + ll);
+        }
+
+        PHASE_OUT(ctx);
+    }
+
+    RETURN(RC_OK);
+}
+
+
+/*
+ * The DER encoder of the SEQUENCE type.
+ */
+asn_enc_rval_t
+SEQUENCE_encode_der(const asn_TYPE_descriptor_t *td, const void *sptr,
+                    int tag_mode, ber_tlv_tag_t tag,
+                    asn_app_consume_bytes_f *cb, void *app_key) {
+    size_t computed_size = 0;
+    asn_enc_rval_t erval = {0,0,0};
+    ssize_t ret;
+    size_t edx;
+
+    ASN_DEBUG("%s %s as SEQUENCE",
+              cb?"Encoding":"Estimating", td->name);
+
+    /*
+     * Gather the length of the underlying members sequence.
+     */
+    for(edx = 0; edx < td->elements_count; edx++) {
+        asn_TYPE_member_t *elm = &td->elements[edx];
+
+        const void *memb_ptr;          /* Pointer to the member */
+        const void *const *memb_ptr2;  /* Pointer to that pointer */
+
+        if(elm->flags & ATF_POINTER) {
+            memb_ptr2 =
+                (const void *const *)((const char *)sptr + elm->memb_offset);
+            if(!*memb_ptr2) {
+                ASN_DEBUG("Element %s %" ASN_PRI_SIZE " not present",
+                          elm->name, edx);
+                if(elm->optional)
+                    continue;
+                /* Mandatory element is missing */
+                ASN__ENCODE_FAILED;
+            }
+        } else {
+            memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
+            memb_ptr2 = &memb_ptr;
+        }
+
+        /* Eliminate default values */
+        if(elm->default_value_cmp && elm->default_value_cmp(*memb_ptr2) == 0)
+            continue;
+
+        erval = elm->type->op->der_encoder(elm->type, *memb_ptr2,
+                                           elm->tag_mode, elm->tag,
+                                           0, 0);
+        if(erval.encoded == -1)
+            return erval;
+        computed_size += erval.encoded;
+        ASN_DEBUG("Member %" ASN_PRI_SIZE " %s estimated %ld bytes",
+                  edx, elm->name, (long)erval.encoded);
+    }
+
+    /*
+     * Encode the TLV for the sequence itself.
+     */
+    ret = der_write_tags(td, computed_size, tag_mode, 1, tag, cb, app_key);
+    ASN_DEBUG("Wrote tags: %ld (+%ld)", (long)ret, (long)computed_size);
+    if(ret == -1)
+        ASN__ENCODE_FAILED;
+    erval.encoded = computed_size + ret;
+
+    if(!cb) ASN__ENCODED_OK(erval);
+
+    /*
+     * Encode all members.
+     */
+    for(edx = 0; edx < td->elements_count; edx++) {
+        asn_TYPE_member_t *elm = &td->elements[edx];
+        asn_enc_rval_t tmperval = {0,0,0};
+        const void *memb_ptr;          /* Pointer to the member */
+        const void *const *memb_ptr2;  /* Pointer to that pointer */
+
+        if(elm->flags & ATF_POINTER) {
+            memb_ptr2 =
+                (const void *const *)((const char *)sptr + elm->memb_offset);
+            if(!*memb_ptr2) continue;
+        } else {
+            memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
+            memb_ptr2 = &memb_ptr;
+        }
+
+        /* Eliminate default values */
+        if(elm->default_value_cmp && elm->default_value_cmp(*memb_ptr2) == 0)
+            continue;
+
+        tmperval = elm->type->op->der_encoder(elm->type, *memb_ptr2,
+                                              elm->tag_mode, elm->tag, cb, app_key);
+        if(tmperval.encoded == -1)
+            return tmperval;
+        computed_size -= tmperval.encoded;
+        ASN_DEBUG("Member %" ASN_PRI_SIZE " %s of SEQUENCE %s encoded in %ld bytes",
+                  edx, elm->name, td->name, (long)tmperval.encoded);
+    }
+
+    if(computed_size != 0)
+        /*
+         * Encoded size is not equal to the computed size.
+         */
+        ASN__ENCODE_FAILED;
+
+    ASN__ENCODED_OK(erval);
+}