mirror of https://gerrit.osmocom.org/asn1c
abstract out sorting
This commit is contained in:
parent
b2219ac657
commit
005f8799b1
|
@ -271,42 +271,130 @@ SET_OF_decode_ber(const asn_codec_ctx_t *opt_codec_ctx,
|
||||||
struct _el_buffer {
|
struct _el_buffer {
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
size_t length;
|
size_t length;
|
||||||
size_t size;
|
size_t allocated_size;
|
||||||
|
unsigned bits_unused;
|
||||||
};
|
};
|
||||||
/* Append bytes to the above structure */
|
/* Append bytes to the above structure */
|
||||||
static int _el_addbytes(const void *buffer, size_t size, void *el_buf_ptr) {
|
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;
|
struct _el_buffer *el_buf = (struct _el_buffer *)el_buf_ptr;
|
||||||
|
|
||||||
if(el_buf->length + size > el_buf->size)
|
if(el_buf->length + size > el_buf->allocated_size) {
|
||||||
return -1;
|
size_t new_size;
|
||||||
|
void *p;
|
||||||
|
|
||||||
memcpy(el_buf->buf + el_buf->length, buffer, size);
|
new_size = el_buf->allocated_size ? 2 * el_buf->allocated_size : 16;
|
||||||
|
|
||||||
el_buf->length += size;
|
p = REALLOC(el_buf->buf, new_size);
|
||||||
return 0;
|
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 int _el_buf_cmp(const void *ap, const void *bp) {
|
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 *a = (const struct _el_buffer *)ap;
|
||||||
const struct _el_buffer *b = (const struct _el_buffer *)bp;
|
const struct _el_buffer *b = (const struct _el_buffer *)bp;
|
||||||
int ret;
|
size_t common_len;
|
||||||
size_t common_len;
|
int ret;
|
||||||
|
|
||||||
if(a->length < b->length)
|
if(a->length < b->length)
|
||||||
common_len = a->length;
|
common_len = a->length;
|
||||||
else
|
else
|
||||||
common_len = b->length;
|
common_len = b->length;
|
||||||
|
|
||||||
ret = memcmp(a->buf, b->buf, common_len);
|
ret = memcmp(a->buf, b->buf, common_len);
|
||||||
if(ret == 0) {
|
if(ret == 0) {
|
||||||
if(a->length < b->length)
|
if(a->length < b->length)
|
||||||
ret = -1;
|
ret = -1;
|
||||||
else if(a->length > b->length)
|
else if(a->length > b->length)
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
/* Ignore unused bits. */
|
||||||
|
if(a->length) {
|
||||||
|
assert((a->buf[a->length-1] & ~(0xff << a->bits_unused)) == 0);
|
||||||
|
assert((b->buf[b->length-1] & ~(0xff << b->bits_unused)) == 0);
|
||||||
|
} else {
|
||||||
|
assert(a->bits_unused == 0);
|
||||||
|
assert(b->bits_unused == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SET_OF__encode_method {
|
||||||
|
SOES_DER
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct _el_buffer *
|
||||||
|
SET_OF__encode_sorted(const asn_TYPE_member_t *elm,
|
||||||
|
const asn_anonymous_set_ *list,
|
||||||
|
enum SET_OF__encode_method method) {
|
||||||
|
static 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 *encoded_el = &encoded_els[edx];
|
||||||
|
asn_enc_rval_t erval;
|
||||||
|
|
||||||
|
if(!memb_ptr) break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encode the member into the prepared space.
|
||||||
|
*/
|
||||||
|
switch(method) {
|
||||||
|
case SOES_DER:
|
||||||
|
erval = elm->type->op->der_encoder(elm->type, memb_ptr, 0, elm->tag,
|
||||||
|
_el_addbytes, encoded_el);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(!"Unreachable");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(erval.encoded < 0) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The DER encoder of the SET OF type.
|
* The DER encoder of the SET OF type.
|
||||||
*/
|
*/
|
||||||
|
@ -315,134 +403,78 @@ SET_OF_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,
|
int tag_mode, ber_tlv_tag_t tag, asn_app_consume_bytes_f *cb,
|
||||||
void *app_key) {
|
void *app_key) {
|
||||||
const asn_TYPE_member_t *elm = td->elements;
|
const asn_TYPE_member_t *elm = td->elements;
|
||||||
const asn_TYPE_descriptor_t *elm_type = elm->type;
|
|
||||||
der_type_encoder_f *der_encoder = elm_type->op->der_encoder;
|
|
||||||
const asn_anonymous_set_ *list = _A_CSET_FROM_VOID(sptr);
|
const asn_anonymous_set_ *list = _A_CSET_FROM_VOID(sptr);
|
||||||
size_t computed_size = 0;
|
size_t computed_size = 0;
|
||||||
ssize_t encoding_size = 0;
|
ssize_t encoding_size = 0;
|
||||||
struct _el_buffer *encoded_els;
|
struct _el_buffer *encoded_els;
|
||||||
ssize_t eels_count = 0;
|
int edx;
|
||||||
size_t max_encoded_len = 1;
|
|
||||||
asn_enc_rval_t erval;
|
|
||||||
int ret;
|
|
||||||
int edx;
|
|
||||||
|
|
||||||
ASN_DEBUG("Estimating size for SET OF %s", td->name);
|
ASN_DEBUG("Estimating size for SET OF %s", td->name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gather the length of the underlying members sequence.
|
* Gather the length of the underlying members sequence.
|
||||||
*/
|
*/
|
||||||
for(edx = 0; edx < list->count; edx++) {
|
for(edx = 0; edx < list->count; edx++) {
|
||||||
void *memb_ptr = list->array[edx];
|
void *memb_ptr = list->array[edx];
|
||||||
if(!memb_ptr) continue;
|
asn_enc_rval_t erval;
|
||||||
erval = der_encoder(elm_type, memb_ptr, 0, elm->tag, 0, 0);
|
|
||||||
if(erval.encoded == -1)
|
|
||||||
return erval;
|
|
||||||
computed_size += erval.encoded;
|
|
||||||
|
|
||||||
/* Compute maximum encoding's size */
|
if(!memb_ptr) ASN__ENCODE_FAILED;
|
||||||
if(max_encoded_len < (size_t)erval.encoded)
|
|
||||||
max_encoded_len = erval.encoded;
|
erval =
|
||||||
|
elm->type->op->der_encoder(elm->type, memb_ptr, 0, elm->tag, 0, 0);
|
||||||
|
if(erval.encoded == -1) return erval;
|
||||||
|
computed_size += erval.encoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Encode the TLV for the sequence itself.
|
|
||||||
*/
|
|
||||||
encoding_size = der_write_tags(td, computed_size, tag_mode, 1, tag,
|
|
||||||
cb, app_key);
|
|
||||||
if(encoding_size == -1) {
|
|
||||||
erval.encoded = -1;
|
|
||||||
erval.failed_type = td;
|
|
||||||
erval.structure_ptr = sptr;
|
|
||||||
return erval;
|
|
||||||
}
|
|
||||||
computed_size += encoding_size;
|
|
||||||
|
|
||||||
if(!cb || list->count == 0) {
|
/*
|
||||||
erval.encoded = computed_size;
|
* Encode the TLV for the sequence itself.
|
||||||
ASN__ENCODED_OK(erval);
|
*/
|
||||||
}
|
encoding_size =
|
||||||
|
der_write_tags(td, computed_size, tag_mode, 1, tag, cb, app_key);
|
||||||
/*
|
if(encoding_size < 0) {
|
||||||
* DER mandates dynamic sorting of the SET OF elements
|
|
||||||
* according to their encodings. Build an array of the
|
|
||||||
* encoded elements.
|
|
||||||
*/
|
|
||||||
encoded_els =
|
|
||||||
(struct _el_buffer *)MALLOC(list->count * sizeof(encoded_els[0]));
|
|
||||||
if(encoded_els == NULL) {
|
|
||||||
ASN__ENCODE_FAILED;
|
ASN__ENCODE_FAILED;
|
||||||
}
|
}
|
||||||
|
computed_size += encoding_size;
|
||||||
|
|
||||||
|
if(!cb || list->count == 0) {
|
||||||
|
asn_enc_rval_t erval;
|
||||||
|
erval.encoded = computed_size;
|
||||||
|
ASN__ENCODED_OK(erval);
|
||||||
|
}
|
||||||
|
|
||||||
ASN_DEBUG("Encoding members of %s SET OF", td->name);
|
ASN_DEBUG("Encoding members of %s SET OF", td->name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Encode all members.
|
* DER mandates dynamic sorting of the SET OF elements
|
||||||
*/
|
* according to their encodings. Build an array of the
|
||||||
for(edx = 0; edx < list->count; edx++) {
|
* encoded elements.
|
||||||
const void *memb_ptr = list->array[edx];
|
*/
|
||||||
struct _el_buffer *encoded_el = &encoded_els[eels_count];
|
encoded_els = SET_OF__encode_sorted(elm, list, SOES_DER);
|
||||||
|
|
||||||
if(!memb_ptr) continue;
|
/*
|
||||||
|
* Report encoded elements to the application.
|
||||||
|
* Dispose of temporary sorted members table.
|
||||||
|
*/
|
||||||
|
for(edx = 0; edx < list->count; edx++) {
|
||||||
|
struct _el_buffer *encoded_el = &encoded_els[edx];
|
||||||
|
/* Report encoded chunks to the application */
|
||||||
|
if(cb(encoded_el->buf, encoded_el->length, app_key) < 0) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
encoding_size += encoded_el->length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
SET_OF__encode_sorted_free(encoded_els, list->count);
|
||||||
* Prepare space for encoding.
|
|
||||||
*/
|
|
||||||
encoded_el->buf = (uint8_t *)MALLOC(max_encoded_len);
|
|
||||||
if(encoded_el->buf) {
|
|
||||||
encoded_el->length = 0;
|
|
||||||
encoded_el->size = max_encoded_len;
|
|
||||||
} else {
|
|
||||||
for(edx--; edx >= 0; edx--)
|
|
||||||
FREEMEM(encoded_els[edx].buf);
|
|
||||||
FREEMEM(encoded_els);
|
|
||||||
ASN__ENCODE_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
if(edx == list->count) {
|
||||||
* Encode the member into the prepared space.
|
assert(computed_size == (size_t)encoding_size);
|
||||||
*/
|
asn_enc_rval_t erval;
|
||||||
erval = der_encoder(elm_type, memb_ptr, 0, elm->tag,
|
erval.encoded = computed_size;
|
||||||
_el_addbytes, encoded_el);
|
|
||||||
if(erval.encoded == -1) {
|
|
||||||
for(; edx >= 0; edx--)
|
|
||||||
FREEMEM(encoded_els[edx].buf);
|
|
||||||
FREEMEM(encoded_els);
|
|
||||||
return erval;
|
|
||||||
}
|
|
||||||
encoding_size += erval.encoded;
|
|
||||||
eels_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sort the encoded elements according to their encoding.
|
|
||||||
*/
|
|
||||||
qsort(encoded_els, eels_count, sizeof(encoded_els[0]), _el_buf_cmp);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Report encoded elements to the application.
|
|
||||||
* Dispose of temporary sorted members table.
|
|
||||||
*/
|
|
||||||
ret = 0;
|
|
||||||
for(edx = 0; edx < eels_count; edx++) {
|
|
||||||
struct _el_buffer *encoded_el = &encoded_els[edx];
|
|
||||||
/* Report encoded chunks to the application */
|
|
||||||
if(ret == 0
|
|
||||||
&& cb(encoded_el->buf, encoded_el->length, app_key) < 0)
|
|
||||||
ret = -1;
|
|
||||||
FREEMEM(encoded_el->buf);
|
|
||||||
}
|
|
||||||
FREEMEM(encoded_els);
|
|
||||||
|
|
||||||
if(ret || computed_size != (size_t)encoding_size) {
|
|
||||||
/*
|
|
||||||
* Standard callback failed, or
|
|
||||||
* encoded size is not equal to the computed size.
|
|
||||||
*/
|
|
||||||
ASN__ENCODE_FAILED;
|
|
||||||
} else {
|
|
||||||
erval.encoded = computed_size;
|
|
||||||
ASN__ENCODED_OK(erval);
|
ASN__ENCODED_OK(erval);
|
||||||
|
} else {
|
||||||
|
ASN__ENCODE_FAILED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue