abstract out sorting

This commit is contained in:
Lev Walkin 2017-10-23 23:15:51 -07:00
parent b2219ac657
commit 005f8799b1
1 changed files with 164 additions and 132 deletions

View File

@ -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;
}
}