mirror of
				https://github.com/open5gs/open5gs.git
				synced 2025-11-04 05:53:18 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			372 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			372 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>.
 | 
						|
 * All rights reserved.
 | 
						|
 * Redistribution and modifications are permitted subject to BSD license.
 | 
						|
 */
 | 
						|
#include <asn_internal.h>
 | 
						|
#include <constr_SET_OF.h>
 | 
						|
 | 
						|
asn_TYPE_operation_t asn_OP_SET_OF = {
 | 
						|
    SET_OF_free,
 | 
						|
#if !defined(ASN_DISABLE_PRINT_SUPPORT)
 | 
						|
    SET_OF_print,
 | 
						|
#else
 | 
						|
    0,
 | 
						|
#endif  /* !defined(ASN_DISABLE_PRINT_SUPPORT) */
 | 
						|
    SET_OF_compare,
 | 
						|
#if !defined(ASN_DISABLE_BER_SUPPORT)
 | 
						|
    SET_OF_decode_ber,
 | 
						|
    SET_OF_encode_der,
 | 
						|
#else
 | 
						|
    0,
 | 
						|
    0,
 | 
						|
#endif  /* !defined(ASN_DISABLE_BER_SUPPORT) */
 | 
						|
#if !defined(ASN_DISABLE_XER_SUPPORT)
 | 
						|
    SET_OF_decode_xer,
 | 
						|
    SET_OF_encode_xer,
 | 
						|
#else
 | 
						|
    0,
 | 
						|
    0,
 | 
						|
#endif  /* !defined(ASN_DISABLE_XER_SUPPORT) */
 | 
						|
#if !defined(ASN_DISABLE_JER_SUPPORT)
 | 
						|
    SET_OF_encode_jer,
 | 
						|
#else
 | 
						|
    0,
 | 
						|
#endif  /* !defined(ASN_DISABLE_JER_SUPPORT) */
 | 
						|
#if !defined(ASN_DISABLE_OER_SUPPORT)
 | 
						|
    SET_OF_decode_oer,
 | 
						|
    SET_OF_encode_oer,
 | 
						|
#else
 | 
						|
    0,
 | 
						|
    0,
 | 
						|
#endif  /* !defined(ASN_DISABLE_OER_SUPPORT) */
 | 
						|
#if !defined(ASN_DISABLE_UPER_SUPPORT)
 | 
						|
    SET_OF_decode_uper,
 | 
						|
    SET_OF_encode_uper,
 | 
						|
#else
 | 
						|
    0,
 | 
						|
    0,
 | 
						|
#endif  /* !defined(ASN_DISABLE_UPER_SUPPORT) */
 | 
						|
#if !defined(ASN_DISABLE_APER_SUPPORT)
 | 
						|
    SET_OF_decode_aper,
 | 
						|
    SET_OF_encode_aper,
 | 
						|
#else
 | 
						|
    0,
 | 
						|
    0,
 | 
						|
#endif  /* !defined(ASN_DISABLE_APER_SUPPORT) */
 | 
						|
#if !defined(ASN_DISABLE_RFILL_SUPPORT)
 | 
						|
    SET_OF_random_fill,
 | 
						|
#else
 | 
						|
    0,
 | 
						|
#endif  /* !defined(ASN_DISABLE_RFILL_SUPPORT) */
 | 
						|
    0  /* Use generic outmost tag fetcher */
 | 
						|
};
 | 
						|
 | 
						|
/* Append bytes to the above structure */
 | 
						|
static int _el_addbytes(const void *buffer, size_t size, void *el_buf_ptr) {
 | 
						|
    struct _el_buffer *el_buf = (struct _el_buffer *)el_buf_ptr;
 | 
						|
 | 
						|
    if(el_buf->length + size > el_buf->allocated_size) {
 | 
						|
        size_t new_size = el_buf->allocated_size ? el_buf->allocated_size : 8;
 | 
						|
        void *p;
 | 
						|
 | 
						|
        do {
 | 
						|
            new_size <<= 2;
 | 
						|
        } while(el_buf->length + size > new_size);
 | 
						|
 | 
						|
        p = REALLOC(el_buf->buf, new_size);
 | 
						|
        if(p) {
 | 
						|
            el_buf->buf = p;
 | 
						|
            el_buf->allocated_size = new_size;
 | 
						|
        } else {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    memcpy(el_buf->buf + el_buf->length, buffer, size);
 | 
						|
 | 
						|
    el_buf->length += size;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void assert_unused_bits(const struct _el_buffer* p) {
 | 
						|
    if(p->length) {
 | 
						|
        assert((p->buf[p->length-1] & ~(0xff << p->bits_unused)) == 0);
 | 
						|
    } else {
 | 
						|
        assert(p->bits_unused == 0);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int _el_buf_cmp(const void *ap, const void *bp) {
 | 
						|
    const struct _el_buffer *a = (const struct _el_buffer *)ap;
 | 
						|
    const struct _el_buffer *b = (const struct _el_buffer *)bp;
 | 
						|
    size_t common_len;
 | 
						|
    int ret = 0;
 | 
						|
 | 
						|
    if(a->length < b->length)
 | 
						|
        common_len = a->length;
 | 
						|
    else
 | 
						|
        common_len = b->length;
 | 
						|
 | 
						|
    if (a->buf && b->buf) {
 | 
						|
        ret = memcmp(a->buf, b->buf, common_len);
 | 
						|
    }
 | 
						|
    if(ret == 0) {
 | 
						|
        if(a->length < b->length)
 | 
						|
            ret = -1;
 | 
						|
        else if(a->length > b->length)
 | 
						|
            ret = 1;
 | 
						|
        /* Ignore unused bits. */
 | 
						|
        assert_unused_bits(a);
 | 
						|
        assert_unused_bits(b);
 | 
						|
    }
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
SET_OF__encode_sorted_free(struct _el_buffer *el_buf, size_t count) {
 | 
						|
    size_t i;
 | 
						|
 | 
						|
    for(i = 0; i < count; i++) {
 | 
						|
        FREEMEM(el_buf[i].buf);
 | 
						|
    }
 | 
						|
 | 
						|
    FREEMEM(el_buf);
 | 
						|
}
 | 
						|
 | 
						|
struct _el_buffer *
 | 
						|
SET_OF__encode_sorted(const asn_TYPE_member_t *elm,
 | 
						|
                      const asn_anonymous_set_ *list,
 | 
						|
                      enum SET_OF__encode_method method) {
 | 
						|
    struct _el_buffer *encoded_els;
 | 
						|
    int edx;
 | 
						|
 | 
						|
    encoded_els =
 | 
						|
        (struct _el_buffer *)CALLOC(list->count, sizeof(encoded_els[0]));
 | 
						|
    if(encoded_els == NULL) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Encode all members.
 | 
						|
	 */
 | 
						|
    for(edx = 0; edx < list->count; edx++) {
 | 
						|
        const void *memb_ptr = list->array[edx];
 | 
						|
        struct _el_buffer *encoding_el = &encoded_els[edx];
 | 
						|
        asn_enc_rval_t erval = {0,0,0};
 | 
						|
 | 
						|
        if(!memb_ptr) break;
 | 
						|
 | 
						|
        /*
 | 
						|
		 * Encode the member into the prepared space.
 | 
						|
		 */
 | 
						|
        switch(method) {
 | 
						|
#if !defined(ASN_DISABLE_BER_SUPPORT)
 | 
						|
        case SOES_DER:
 | 
						|
            erval = elm->type->op->der_encoder(elm->type, memb_ptr, 0, elm->tag,
 | 
						|
                                               _el_addbytes, encoding_el);
 | 
						|
            break;
 | 
						|
#endif  /* !defined(ASN_DISABLE_BER_SUPPORT) */
 | 
						|
#if !defined(ASN_DISABLE_UPER_SUPPORT)
 | 
						|
        case SOES_CUPER:
 | 
						|
            erval = uper_encode(elm->type,
 | 
						|
                                elm->encoding_constraints.per_constraints,
 | 
						|
                                memb_ptr, _el_addbytes, encoding_el);
 | 
						|
            if(erval.encoded != -1) {
 | 
						|
                size_t extra_bits = erval.encoded % 8;
 | 
						|
                assert(encoding_el->length == (size_t)(erval.encoded + 7) / 8);
 | 
						|
                encoding_el->bits_unused = (8 - extra_bits) & 0x7;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
#endif  /* !defined(ASN_DISABLE_UPER_SUPPORT) */
 | 
						|
#if !defined(ASN_DISABLE_APER_SUPPORT)
 | 
						|
        case SOES_CAPER:
 | 
						|
            erval = aper_encode(elm->type,
 | 
						|
                                elm->encoding_constraints.per_constraints,
 | 
						|
                                memb_ptr, _el_addbytes, encoding_el);
 | 
						|
            if(erval.encoded != -1) {
 | 
						|
                size_t extra_bits = erval.encoded % 8;
 | 
						|
                assert(encoding_el->length == (size_t)(erval.encoded + 7) / 8);
 | 
						|
                encoding_el->bits_unused = (8 - extra_bits) & 0x7;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
#endif  /* !defined(ASN_DISABLE_APER_SUPPORT) */
 | 
						|
 | 
						|
        default:
 | 
						|
            assert(!"Unreachable");
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        if(erval.encoded < 0) break;
 | 
						|
	}
 | 
						|
 | 
						|
    if(edx == list->count) {
 | 
						|
        /*
 | 
						|
         * Sort the encoded elements according to their encoding.
 | 
						|
         */
 | 
						|
        qsort(encoded_els, list->count, sizeof(encoded_els[0]), _el_buf_cmp);
 | 
						|
 | 
						|
        return encoded_els;
 | 
						|
    } else {
 | 
						|
        SET_OF__encode_sorted_free(encoded_els, edx);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
SET_OF_free(const asn_TYPE_descriptor_t *td, void *ptr,
 | 
						|
            enum asn_struct_free_method method) {
 | 
						|
    if(td && ptr) {
 | 
						|
		const asn_SET_OF_specifics_t *specs;
 | 
						|
		asn_TYPE_member_t *elm = td->elements;
 | 
						|
		asn_anonymous_set_ *list = _A_SET_FROM_VOID(ptr);
 | 
						|
		asn_struct_ctx_t *ctx;	/* Decoder context */
 | 
						|
		int i;
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Could not use set_of_empty() because of (*free)
 | 
						|
		 * incompatibility.
 | 
						|
		 */
 | 
						|
		for(i = 0; i < list->count; i++) {
 | 
						|
			void *memb_ptr = list->array[i];
 | 
						|
			if(memb_ptr)
 | 
						|
			ASN_STRUCT_FREE(*elm->type, memb_ptr);
 | 
						|
		}
 | 
						|
		list->count = 0;	/* No meaningful elements left */
 | 
						|
 | 
						|
		asn_set_empty(list);	/* Remove (list->array) */
 | 
						|
 | 
						|
		specs = (const asn_SET_OF_specifics_t *)td->specifics;
 | 
						|
		ctx = (asn_struct_ctx_t *)((char *)ptr + specs->ctx_offset);
 | 
						|
		if(ctx->ptr) {
 | 
						|
			ASN_STRUCT_FREE(*elm->type, ctx->ptr);
 | 
						|
			ctx->ptr = 0;
 | 
						|
		}
 | 
						|
 | 
						|
        switch(method) {
 | 
						|
        case ASFM_FREE_EVERYTHING:
 | 
						|
            FREEMEM(ptr);
 | 
						|
            break;
 | 
						|
        case ASFM_FREE_UNDERLYING:
 | 
						|
            break;
 | 
						|
        case ASFM_FREE_UNDERLYING_AND_RESET:
 | 
						|
            memset(ptr, 0, specs->struct_size);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
SET_OF_constraint(const asn_TYPE_descriptor_t *td, const void *sptr,
 | 
						|
                  asn_app_constraint_failed_f *ctfailcb, void *app_key) {
 | 
						|
    const asn_TYPE_member_t *elm = td->elements;
 | 
						|
	asn_constr_check_f *constr;
 | 
						|
	const asn_anonymous_set_ *list = _A_CSET_FROM_VOID(sptr);
 | 
						|
	int i;
 | 
						|
 | 
						|
	if(!sptr) {
 | 
						|
		ASN__CTFAIL(app_key, td, sptr,
 | 
						|
			"%s: value not given (%s:%d)",
 | 
						|
			td->name, __FILE__, __LINE__);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	constr = elm->encoding_constraints.general_constraints;
 | 
						|
	if(!constr) constr = elm->type->encoding_constraints.general_constraints;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Iterate over the members of an array.
 | 
						|
	 * Validate each in turn, until one fails.
 | 
						|
	 */
 | 
						|
	for(i = 0; i < list->count; i++) {
 | 
						|
		const void *memb_ptr = list->array[i];
 | 
						|
		int ret;
 | 
						|
 | 
						|
		if(!memb_ptr) continue;
 | 
						|
 | 
						|
		ret = constr(elm->type, memb_ptr, ctfailcb, app_key);
 | 
						|
		if(ret) return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
struct comparable_ptr {
 | 
						|
    const asn_TYPE_descriptor_t *td;
 | 
						|
    const void *sptr;
 | 
						|
};
 | 
						|
 | 
						|
static int
 | 
						|
SET_OF__compare_cb(const void *aptr, const void *bptr) {
 | 
						|
    const struct comparable_ptr *a = aptr;
 | 
						|
    const struct comparable_ptr *b = bptr;
 | 
						|
    assert(a->td == b->td);
 | 
						|
    return a->td->op->compare_struct(a->td, a->sptr, b->sptr);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
SET_OF_compare(const asn_TYPE_descriptor_t *td, const void *aptr,
 | 
						|
               const void *bptr) {
 | 
						|
    const asn_anonymous_set_ *a = _A_CSET_FROM_VOID(aptr);
 | 
						|
    const asn_anonymous_set_ *b = _A_CSET_FROM_VOID(bptr);
 | 
						|
 | 
						|
    if(a && b) {
 | 
						|
        struct comparable_ptr *asorted;
 | 
						|
        struct comparable_ptr *bsorted;
 | 
						|
        ssize_t common_length;
 | 
						|
        ssize_t idx;
 | 
						|
 | 
						|
        if(a->count == 0) {
 | 
						|
            if(b->count) return -1;
 | 
						|
            return 0;
 | 
						|
        } else if(b->count == 0) {
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
 | 
						|
        asorted = MALLOC(a->count * sizeof(asorted[0]));
 | 
						|
        bsorted = MALLOC(b->count * sizeof(bsorted[0]));
 | 
						|
        if(!asorted || !bsorted) {
 | 
						|
            FREEMEM(asorted);
 | 
						|
            FREEMEM(bsorted);
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
 | 
						|
        for(idx = 0; idx < a->count; idx++) {
 | 
						|
            asorted[idx].td = td->elements->type;
 | 
						|
            asorted[idx].sptr = a->array[idx];
 | 
						|
        }
 | 
						|
 | 
						|
        for(idx = 0; idx < b->count; idx++) {
 | 
						|
            bsorted[idx].td = td->elements->type;
 | 
						|
            bsorted[idx].sptr = b->array[idx];
 | 
						|
        }
 | 
						|
 | 
						|
        qsort(asorted, a->count, sizeof(asorted[0]), SET_OF__compare_cb);
 | 
						|
        qsort(bsorted, b->count, sizeof(bsorted[0]), SET_OF__compare_cb);
 | 
						|
 | 
						|
        common_length = (a->count < b->count ? a->count : b->count);
 | 
						|
        for(idx = 0; idx < common_length; idx++) {
 | 
						|
            int ret = td->elements->type->op->compare_struct(
 | 
						|
                td->elements->type, asorted[idx].sptr, bsorted[idx].sptr);
 | 
						|
            if(ret) {
 | 
						|
                FREEMEM(asorted);
 | 
						|
                FREEMEM(bsorted);
 | 
						|
                return ret;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        FREEMEM(asorted);
 | 
						|
        FREEMEM(bsorted);
 | 
						|
 | 
						|
        if(idx < b->count) /* more elements in b */
 | 
						|
            return -1;     /* a is shorter, so put it first */
 | 
						|
        if(idx < a->count) return 1;
 | 
						|
    } else if(!a) {
 | 
						|
        return -1;
 | 
						|
    } else if(!b) {
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 |