+
+static const void *
+_get_member_ptr(const asn_TYPE_descriptor_t *td, const void *sptr,
+ asn_TYPE_member_t **elm_ptr, unsigned *present_out) {
+ const asn_CHOICE_specifics_t *specs =
+ (const asn_CHOICE_specifics_t *)td->specifics;
+ unsigned present;
+
+ if(!sptr) {
+ *elm_ptr = NULL;
+ *present_out = 0;
+ return NULL;
+ }
+
+ /*
+ * Figure out which CHOICE element is encoded.
+ */
+ present = _fetch_present_idx(sptr, specs->pres_offset, specs->pres_size);
+ *present_out = present;
+
+ /*
+ * The presence index is intentionally 1-based to avoid
+ * treating zeroed structure as a valid one.
+ */
+ if(present > 0 && present <= td->elements_count) {
+ asn_TYPE_member_t *const elm = &td->elements[present - 1];
+ const void *memb_ptr;
+
+ if(elm->flags & ATF_POINTER) {
+ memb_ptr =
+ *(const void *const *)((const char *)sptr + elm->memb_offset);
+ } else {
+ memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
+ }
+ *elm_ptr = elm;
+ return memb_ptr;
+ } else {
+ *elm_ptr = NULL;
+ return NULL;
+ }
+
+}
+
+int
+CHOICE_compare(const asn_TYPE_descriptor_t *td, const void *aptr, const void *bptr) {
+ asn_TYPE_member_t *aelm;
+ asn_TYPE_member_t *belm;
+ unsigned apresent = 0;
+ unsigned bpresent = 0;
+ const void *amember = _get_member_ptr(td, aptr, &aelm, &apresent);
+ const void *bmember = _get_member_ptr(td, bptr, &belm, &bpresent);
+
+ if(amember && bmember) {
+ if(apresent == bpresent) {
+ assert(aelm == belm);
+ return aelm->type->op->compare_struct(aelm->type, amember, bmember);
+ } else if(apresent < bpresent) {
+ return -1;
+ } else {
+ return 1;
+ }
+ } else if(!amember) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+/*
+ * Return the 1-based choice variant presence index.
+ * Returns 0 in case of error.
+ */
+unsigned
+CHOICE_variant_get_presence(const asn_TYPE_descriptor_t *td, const void *sptr) {
+ const asn_CHOICE_specifics_t *specs =
+ (const asn_CHOICE_specifics_t *)td->specifics;
+ return _fetch_present_idx(sptr, specs->pres_offset, specs->pres_size);
+}
+
+/*
+ * Sets or resets the 1-based choice variant presence index.
+ * In case a previous index is not zero, the currently selected structure
+ * member is freed and zeroed-out first.
+ * Returns 0 on success and -1 on error.
+ */
+int
+CHOICE_variant_set_presence(const asn_TYPE_descriptor_t *td, void *sptr,
+ unsigned present) {
+ const asn_CHOICE_specifics_t *specs =
+ (const asn_CHOICE_specifics_t *)td->specifics;
+ unsigned old_present;
+
+ if(!sptr) {
+ return -1;
+ }
+
+ if(present > td->elements_count)
+ return -1;
+
+ old_present =
+ _fetch_present_idx(sptr, specs->pres_offset, specs->pres_size);
+ if(present == old_present)
+ return 0;
+
+ if(old_present != 0) {
+ assert(old_present <= td->elements_count);
+ ASN_STRUCT_RESET(*td, sptr);
+ }
+
+ _set_present_idx(sptr, specs->pres_offset, specs->pres_size, present);
+
+ return 0;
+}
+
+
+asn_random_fill_result_t
+CHOICE_random_fill(const asn_TYPE_descriptor_t *td, void **sptr,
+ const asn_encoding_constraints_t *constr,
+ size_t max_length) {
+ const asn_CHOICE_specifics_t *specs =
+ (const asn_CHOICE_specifics_t *)td->specifics;
+ asn_random_fill_result_t res;
+ asn_random_fill_result_t result_failed = {ARFILL_FAILED, 0};
+ asn_random_fill_result_t result_skipped = {ARFILL_SKIPPED, 0};
+ const asn_TYPE_member_t *elm;
+ unsigned present;
+ void *memb_ptr; /* Pointer to the member */
+ void **memb_ptr2; /* Pointer to that pointer */
+ void *st = *sptr;
+
+ if(max_length == 0) return result_skipped;
+
+ (void)constr;
+
+ if(st == NULL) {
+ st = CALLOC(1, specs->struct_size);
+ if(st == NULL) {
+ return result_failed;
+ }
+ }
+
+ present = asn_random_between(1, td->elements_count);
+ elm = &td->elements[present - 1];
+
+ if(elm->flags & ATF_POINTER) {
+ /* Member is a pointer to another structure */
+ memb_ptr2 = (void **)((char *)st + elm->memb_offset);
+ } else {
+ memb_ptr = (char *)st + elm->memb_offset;
+ memb_ptr2 = &memb_ptr;
+ }
+
+ res = elm->type->op->random_fill(elm->type, memb_ptr2,
+ &elm->encoding_constraints, max_length);
+ _set_present_idx(st, specs->pres_offset, specs->pres_size, present);
+ if(res.code == ARFILL_OK) {
+ *sptr = st;
+ } else {
+ if(st == *sptr) {
+ ASN_STRUCT_RESET(*td, st);
+ } else {
+ ASN_STRUCT_FREE(*td, st);
+ }
+ }
+
+ return res;
+}
+
+
+asn_TYPE_operation_t asn_OP_CHOICE = {
+ CHOICE_free,
+ CHOICE_print,
+ CHOICE_compare,
+ CHOICE_decode_ber,
+ CHOICE_encode_der,
+ CHOICE_decode_xer,
+ CHOICE_encode_xer,
+#ifdef ASN_DISABLE_OER_SUPPORT
+ 0,
+ 0,
+#else
+ CHOICE_decode_oer,
+ CHOICE_encode_oer,
+#endif /* ASN_DISABLE_OER_SUPPORT */
+#ifdef ASN_DISABLE_PER_SUPPORT
+ 0,
+ 0,
+#else
+ CHOICE_decode_uper,
+ CHOICE_encode_uper,
+#endif /* ASN_DISABLE_PER_SUPPORT */
+ CHOICE_random_fill,
+ CHOICE_outmost_tag
+};