encode_cntype(asn1buf *buf, const void *val, size_t count,
              const struct cntype_info *c, taginfo *tag_out)
{
    krb5_error_code ret;

    switch (c->type) {
    case cntype_string: {
        const struct string_info *string = c->tinfo;
        assert(string->enc != NULL);
        ret = string->enc(buf, val, count);
        if (ret)
            return ret;
        tag_out->asn1class = UNIVERSAL;
        tag_out->construction = PRIMITIVE;
        tag_out->tagnum = string->tagval;
        break;
    }
    case cntype_der:
        return split_der(buf, val, count, tag_out);
    case cntype_seqof: {
        const struct atype_info *a = c->tinfo;
        const struct ptr_info *ptr = a->tinfo;
        assert(a->type == atype_ptr);
        val = LOADPTR(val, ptr);
        ret = encode_sequence_of(buf, count, val, ptr->basetype);
        if (ret)
            return ret;
        tag_out->asn1class = UNIVERSAL;
        tag_out->construction = CONSTRUCTED;
        tag_out->tagnum = ASN1_SEQUENCE;
        break;
    }
    case cntype_choice: {
        const struct choice_info *choice = c->tinfo;
        if (count >= choice->n_options)
            return ASN1_MISSING_FIELD;
        return encode_atype(buf, val, choice->options[count], tag_out);
    }

    default:
        assert(c->type > cntype_min);
        assert(c->type < cntype_max);
        abort();
    }

    return 0;
}
encode_cntype(asn1buf *buf, const void *val, size_t count,
              const struct cntype_info *c, taginfo *tag_out)
{
    krb5_error_code ret;

    switch (c->type) {
    case cntype_string: {
        const struct string_info *string = c->tinfo;
        assert(string->enc != NULL);
        ret = string->enc(buf, val, count);
        if (ret)
            return ret;
        tag_out->asn1class = UNIVERSAL;
        tag_out->construction = PRIMITIVE;
        tag_out->tagnum = string->tagval;
        break;
    }
    case cntype_der:
        return split_der(buf, val, count, tag_out);
    case cntype_seqof: {
        const struct atype_info *a = c->tinfo;
        const struct ptr_info *ptr = a->tinfo;
        assert(a->type == atype_ptr);
        val = LOADPTR(val, ptr);
        ret = encode_sequence_of(buf, count, val, ptr->basetype);
        if (ret)
            return ret;
        tag_out->asn1class = UNIVERSAL;
        tag_out->construction = CONSTRUCTED;
        tag_out->tagnum = ASN1_SEQUENCE;
        break;
    }
    case cntype_choice: {
        const struct choice_info *choice = c->tinfo;
        if (count >= choice->n_options)
            return ASN1_MISSING_FIELD;
        return encode_atype(buf, val, choice->options[count], tag_out);
    }

    default:
        assert(c->type > cntype_min);
        assert(c->type < cntype_max);
        abort();
    }

    return 0;
}