Support for canonical CANONICAL-XER ordering


git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@475 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/skeletons/constr_SET.c b/skeletons/constr_SET.c
index d9f93c3..a64c2ff 100644
--- a/skeletons/constr_SET.c
+++ b/skeletons/constr_SET.c
@@ -435,11 +435,11 @@
  */
 asn_enc_rval_t
 SET_encode_der(asn_TYPE_descriptor_t *td,
-	void *ptr, int tag_mode, ber_tlv_tag_t tag,
+	void *sptr, int tag_mode, ber_tlv_tag_t tag,
 	asn_app_consume_bytes_f *cb, void *app_key) {
 	asn_SET_specifics_t *specs = (asn_SET_specifics_t *)td->specifics;
 	size_t computed_size = 0;
-	asn_enc_rval_t my_erval;
+	asn_enc_rval_t er;
 	int t2m_build_own = (specs->tag2el_count != td->elements_count);
 	asn_TYPE_tag2member_t *t2m;
 	int t2m_count;
@@ -451,12 +451,7 @@
 	 */
 	if(t2m_build_own) {
 		(void *)t2m = alloca(td->elements_count * sizeof(t2m[0]));
-		if(!t2m) {	/* There are such platforms */
-			my_erval.encoded = -1;
-			my_erval.failed_type = td;
-			my_erval.structure_ptr = ptr;
-			return my_erval;
-		}
+		if(!t2m) _ASN_ENCODE_FAILED; /* There are such platforms */
 		t2m_count = 0;
 	} else {
 		/*
@@ -472,14 +467,14 @@
 	 */
 	for(edx = 0; edx < td->elements_count; edx++) {
 		asn_TYPE_member_t *elm = &td->elements[edx];
-		asn_enc_rval_t erval;
+		asn_enc_rval_t tmper;
 		void *memb_ptr;
 
 		/*
 		 * Compute the length of the encoding of this member.
 		 */
 		if(elm->flags & ATF_POINTER) {
-			memb_ptr = *(void **)((char *)ptr + elm->memb_offset);
+			memb_ptr = *(void **)((char *)sptr + elm->memb_offset);
 			if(!memb_ptr) {
 				if(t2m_build_own) {
 					t2m[t2m_count].el_no = edx;
@@ -489,14 +484,14 @@
 				continue;
 			}
 		} else {
-			memb_ptr = (void *)((char *)ptr + elm->memb_offset);
+			memb_ptr = (void *)((char *)sptr + elm->memb_offset);
 		}
-		erval = elm->type->der_encoder(elm->type, memb_ptr,
+		tmper = elm->type->der_encoder(elm->type, memb_ptr,
 			elm->tag_mode, elm->tag,
 			0, 0);
-		if(erval.encoded == -1)
-			return erval;
-		computed_size += erval.encoded;
+		if(tmper.encoded == -1)
+			return tmper;
+		computed_size += tmper.encoded;
 
 		/*
 		 * Remember the outmost tag of this member.
@@ -533,72 +528,74 @@
 	 * Encode the TLV for the sequence itself.
 	 */
 	ret = der_write_tags(td, computed_size, tag_mode, 1, tag, cb, app_key);
-	if(ret == -1) {
-		my_erval.encoded = -1;
-		my_erval.failed_type = td;
-		my_erval.structure_ptr = ptr;
-		return my_erval;
-	}
-	my_erval.encoded = computed_size + ret;
+	if(ret == -1) _ASN_ENCODE_FAILED;
+	er.encoded = computed_size + ret;
 
-	if(!cb) return my_erval;
+	if(!cb) return er;
 
 	/*
 	 * Encode all members.
 	 */
 	for(edx = 0; edx < td->elements_count; edx++) {
 		asn_TYPE_member_t *elm;
-		asn_enc_rval_t erval;
+		asn_enc_rval_t tmper;
 		void *memb_ptr;
 
 		/* Encode according to the tag order */
 		elm = &td->elements[t2m[edx].el_no];
 
 		if(elm->flags & ATF_POINTER) {
-			memb_ptr = *(void **)((char *)ptr + elm->memb_offset);
+			memb_ptr = *(void **)((char *)sptr + elm->memb_offset);
 			if(!memb_ptr) continue;
 		} else {
-			memb_ptr = (void *)((char *)ptr + elm->memb_offset);
+			memb_ptr = (void *)((char *)sptr + elm->memb_offset);
 		}
-		erval = elm->type->der_encoder(elm->type, memb_ptr,
+		tmper = elm->type->der_encoder(elm->type, memb_ptr,
 			elm->tag_mode, elm->tag,
 			cb, app_key);
-		if(erval.encoded == -1)
-			return erval;
-		computed_size -= erval.encoded;
+		if(tmper.encoded == -1)
+			return tmper;
+		computed_size -= tmper.encoded;
 	}
 
 	if(computed_size != 0) {
 		/*
 		 * Encoded size is not equal to the computed size.
 		 */
-		my_erval.encoded = -1;
-		my_erval.failed_type = td;
-		my_erval.structure_ptr = ptr;
+		_ASN_ENCODE_FAILED;
 	}
 
-	return my_erval;
+	return er;
 }
 
 asn_enc_rval_t
 SET_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
 	int ilevel, enum xer_encoder_flags_e flags,
 		asn_app_consume_bytes_f *cb, void *app_key) {
+	asn_SET_specifics_t *specs = (asn_SET_specifics_t *)td->specifics;
 	asn_enc_rval_t er;
 	int xcan = (flags & XER_F_CANONICAL);
+	asn_TYPE_tag2member_t *t2m = specs->tag2el_cxer;
+	int t2m_count = specs->tag2el_cxer_count;
 	int edx;
 
 	if(!sptr)
 		_ASN_ENCODE_FAILED;
 
+	assert(t2m_count == td->elements_count);
+
 	er.encoded = 0;
 
-	for(edx = 0; edx < td->elements_count; edx++) {
+	for(edx = 0; edx < t2m_count; edx++) {
 		asn_enc_rval_t tmper;
-		asn_TYPE_member_t *elm = &td->elements[edx];
+		asn_TYPE_member_t *elm;
 		void *memb_ptr;
-		const char *mname = elm->name;
-		unsigned int mlen = strlen(elm->name);
+		const char *mname;
+		unsigned int mlen;
+
+		elm = &td->elements[t2m[edx].el_no];
+		mname = elm->name;
+		mlen = strlen(elm->name);
 
 		if(elm->flags & ATF_POINTER) {
 			memb_ptr = *(void **)((char *)sptr + elm->memb_offset);
@@ -624,6 +621,8 @@
 	if(!xcan) _i_ASN_TEXT_INDENT(1, ilevel - 1);
 
 	return er;
+cb_failed:
+	_ASN_ENCODE_FAILED;
 }
 
 int
diff --git a/skeletons/constr_SET.h b/skeletons/constr_SET.h
index 8f9b265..41d6a7f 100644
--- a/skeletons/constr_SET.h
+++ b/skeletons/constr_SET.h
@@ -18,11 +18,20 @@
 
 	/*
 	 * Tags to members mapping table (sorted).
+	 * Sometimes suitable for DER encoding (untagged CHOICE is present);
+	 * if so, tag2el_count will be greater than td->elements_count.
 	 */
 	asn_TYPE_tag2member_t *tag2el;
 	int tag2el_count;
 
 	/*
+	 * Tags to members mapping table, second edition.
+	 * Suitable for CANONICAL-XER encoding.
+	 */
+	asn_TYPE_tag2member_t *tag2el_cxer;
+	int tag2el_cxer_count;
+
+	/*
 	 * Extensions-related stuff.
 	 */
 	int extensible;				/* Whether SET is extensible */
diff --git a/skeletons/constr_SET_OF.c b/skeletons/constr_SET_OF.c
index 00c4cb3..c298cc2 100644
--- a/skeletons/constr_SET_OF.c
+++ b/skeletons/constr_SET_OF.c
@@ -5,6 +5,7 @@
 #include <asn_internal.h>
 #include <constr_SET_OF.h>
 #include <asn_SET_OF.h>
+#include <assert.h>
 
 /*
  * Number of bytes left for this structure.
@@ -449,6 +450,43 @@
 	return erval;
 }
 
+typedef struct xer_tmp_enc_s {
+	void *buffer;
+	size_t offset;
+	size_t size;
+} xer_tmp_enc_t;
+static int
+SET_OF_encode_xer_callback(const void *buffer, size_t size, void *key) {
+	xer_tmp_enc_t *t = (xer_tmp_enc_t *)key;
+	if(t->offset + size >= t->size) {
+		size_t newsize = (t->size << 2) + size;
+		void *p = REALLOC(t->buffer, newsize);
+		if(!p) return -1;
+		t->buffer = p;
+		t->size = newsize;
+	}
+	memcpy((char *)t->buffer + t->offset, buffer, size);
+	t->offset += size;
+	return 0;
+}
+static int
+SET_OF_xer_order(const void *aptr, const void *bptr) {
+	const xer_tmp_enc_t *a = (const xer_tmp_enc_t *)aptr;
+	const xer_tmp_enc_t *b = (const xer_tmp_enc_t *)bptr;
+	size_t minlen = a->offset;
+	int ret;
+	if(b->offset < minlen) minlen = b->offset;
+	/* Well-formed UTF-8 has this nice lexicographical property... */
+	ret = memcmp(a->buffer, b->buffer, minlen);
+	if(ret != 0) return ret;
+	if(a->offset == b->offset)
+		return 0;
+	if(a->offset == minlen)
+		return -1;
+	return 1;
+}
+
+
 asn_enc_rval_t
 SET_OF_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
 	int ilevel, enum xer_encoder_flags_e flags,
@@ -461,19 +499,36 @@
 		? 0 : ((*element->name) ? element->name : element->type->name);
 	size_t mlen = mname ? strlen(mname) : 0;
 	int xcan = (flags & XER_F_CANONICAL);
+	xer_tmp_enc_t *encs = 0;
+	size_t encs_count = 0;
+	void *original_app_key = app_key;
+	asn_app_consume_bytes_f *original_cb = cb;
 	int i;
 
 	if(!sptr) _ASN_ENCODE_FAILED;
 
+	(void *)list = sptr;
+
+	if(xcan) {
+		encs = (xer_tmp_enc_t *)MALLOC(list->count * sizeof(encs[0]));
+		if(!encs) _ASN_ENCODE_FAILED;
+		cb = SET_OF_encode_xer_callback;
+	}
+
 	er.encoded = 0;
 
-	(void *)list = sptr;
 	for(i = 0; i < list->count; i++) {
 		asn_enc_rval_t tmper;
 
 		void *memb_ptr = list->array[i];
 		if(!memb_ptr) continue;
 
+		if(encs) {
+			memset(&encs[encs_count], 0, sizeof(encs[0]));
+			app_key = &encs[encs_count];
+			encs_count++;
+		}
+
 		if(mname) {
 			if(!xcan) _i_ASN_TEXT_INDENT(1, ilevel);
 			_ASN_CALLBACK3("<", 1, mname, mlen, ">", 1);
@@ -481,7 +536,11 @@
 
 		tmper = element->type->xer_encoder(element->type, memb_ptr,
 				ilevel + 1, flags, cb, app_key);
-		if(tmper.encoded == -1) return tmper;
+		if(tmper.encoded == -1) {
+			td = tmper.failed_type;
+			sptr = tmper.structure_ptr;
+			goto cb_failed;
+		}
 
 		if(mname) {
 			_ASN_CALLBACK3("</", 2, mname, mlen, ">", 1);
@@ -493,6 +552,37 @@
 
 	if(!xcan) _i_ASN_TEXT_INDENT(1, ilevel - 1);
 
+	if(encs) {
+		xer_tmp_enc_t *enc = encs;
+		xer_tmp_enc_t *end = encs + encs_count;
+		ssize_t control_size = 0;
+
+		cb = original_cb;
+		app_key = original_app_key;
+		qsort(encs, encs_count, sizeof(encs[0]), SET_OF_xer_order);
+
+		for(; enc < end; enc++) {
+			_ASN_CALLBACK(enc->buffer, enc->offset);
+			FREEMEM(enc->buffer);
+			enc->buffer = 0;
+			control_size += enc->offset;
+		}
+		assert(control_size == er.encoded);
+	}
+
+	goto cleanup;
+cb_failed:
+	er.encoded = -1;
+	er.failed_type = td;
+	er.structure_ptr = sptr;
+cleanup:
+	if(encs) {
+		while(encs_count-- > 0) {
+			if(encs[encs_count].buffer)
+				FREEMEM(encs[encs_count].buffer);
+		}
+		free(encs);
+	}
 	return er;
 }