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 {
|
||||
uint8_t *buf;
|
||||
size_t length;
|
||||
size_t size;
|
||||
size_t allocated_size;
|
||||
unsigned bits_unused;
|
||||
};
|
||||
/* 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;
|
||||
struct _el_buffer *el_buf = (struct _el_buffer *)el_buf_ptr;
|
||||
|
||||
if(el_buf->length + size > el_buf->size)
|
||||
return -1;
|
||||
if(el_buf->length + size > el_buf->allocated_size) {
|
||||
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;
|
||||
return 0;
|
||||
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 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;
|
||||
int ret;
|
||||
size_t common_len;
|
||||
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;
|
||||
|
||||
if(a->length < b->length)
|
||||
common_len = a->length;
|
||||
else
|
||||
common_len = b->length;
|
||||
if(a->length < b->length)
|
||||
common_len = a->length;
|
||||
else
|
||||
common_len = b->length;
|
||||
|
||||
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;
|
||||
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. */
|
||||
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.
|
||||
*/
|
||||
|
@ -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,
|
||||
void *app_key) {
|
||||
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);
|
||||
size_t computed_size = 0;
|
||||
ssize_t encoding_size = 0;
|
||||
struct _el_buffer *encoded_els;
|
||||
ssize_t eels_count = 0;
|
||||
size_t max_encoded_len = 1;
|
||||
asn_enc_rval_t erval;
|
||||
int ret;
|
||||
int edx;
|
||||
ssize_t encoding_size = 0;
|
||||
struct _el_buffer *encoded_els;
|
||||
int edx;
|
||||
|
||||
ASN_DEBUG("Estimating size for SET OF %s", td->name);
|
||||
|
||||
/*
|
||||
* Gather the length of the underlying members sequence.
|
||||
*/
|
||||
for(edx = 0; edx < list->count; edx++) {
|
||||
void *memb_ptr = list->array[edx];
|
||||
if(!memb_ptr) continue;
|
||||
erval = der_encoder(elm_type, memb_ptr, 0, elm->tag, 0, 0);
|
||||
if(erval.encoded == -1)
|
||||
return erval;
|
||||
computed_size += erval.encoded;
|
||||
/*
|
||||
* Gather the length of the underlying members sequence.
|
||||
*/
|
||||
for(edx = 0; edx < list->count; edx++) {
|
||||
void *memb_ptr = list->array[edx];
|
||||
asn_enc_rval_t erval;
|
||||
|
||||
/* Compute maximum encoding's size */
|
||||
if(max_encoded_len < (size_t)erval.encoded)
|
||||
max_encoded_len = erval.encoded;
|
||||
if(!memb_ptr) ASN__ENCODE_FAILED;
|
||||
|
||||
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;
|
||||
ASN__ENCODED_OK(erval);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
/*
|
||||
* 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 < 0) {
|
||||
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);
|
||||
|
||||
/*
|
||||
* Encode all members.
|
||||
*/
|
||||
for(edx = 0; edx < list->count; edx++) {
|
||||
const void *memb_ptr = list->array[edx];
|
||||
struct _el_buffer *encoded_el = &encoded_els[eels_count];
|
||||
/*
|
||||
* DER mandates dynamic sorting of the SET OF elements
|
||||
* according to their encodings. Build an array of the
|
||||
* encoded elements.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
SET_OF__encode_sorted_free(encoded_els, list->count);
|
||||
|
||||
/*
|
||||
* Encode the member into the prepared space.
|
||||
*/
|
||||
erval = der_encoder(elm_type, memb_ptr, 0, elm->tag,
|
||||
_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;
|
||||
if(edx == list->count) {
|
||||
assert(computed_size == (size_t)encoding_size);
|
||||
asn_enc_rval_t erval;
|
||||
erval.encoded = computed_size;
|
||||
ASN__ENCODED_OK(erval);
|
||||
} else {
|
||||
ASN__ENCODE_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue