extensibility in SEQUENCE uper writer
diff --git a/asn1c/tests/check-126.-gen-PER.c b/asn1c/tests/check-126.-gen-PER.c
index aa12f1b..5df2c78 100644
--- a/asn1c/tests/check-126.-gen-PER.c
+++ b/asn1c/tests/check-126.-gen-PER.c
@@ -213,6 +213,35 @@
 }
 
 static void
+compare_with_data_out(const char *fname, char *buf, int size) {
+	char outName[256];
+	char fbuf[1024];
+	size_t rd;
+	FILE *f;
+
+	sprintf(outName, "../data-126/%s", fname);
+	strcpy(outName + strlen(outName) - 3, ".out");
+
+	fprintf(stderr, "Comparing PER output with [%s]\n", outName);
+
+	f = fopen(outName, "r");
+	if(f) {
+		assert(f);
+		rd = fread(fbuf, 1, sizeof(fbuf), f);
+		assert(rd);
+		fclose(f);
+
+		assert(rd == size);
+		assert(memcmp(fbuf, buf, rd) == 0);
+		fprintf(stderr, "XER->PER recoding .in->.out match.\n");
+	} else if(getenv("REGENERATE")) {
+		f = fopen(outName, "w");
+		fwrite(buf, 1, size, f);
+		fclose(f);
+	}
+}
+
+static void
 process_XER_data(const char *fname, char *fbuf, int size) {
 	PDU_t *st;
 	int ret;
@@ -220,8 +249,9 @@
 	st = load_object_from(fname, fbuf, size, AS_XER);
 	if(!st) return;
 
-	/* Save and re-load as DER */
+	/* Save and re-load as PER */
 	save_object_as(st, AS_PER);
+	compare_with_data_out(fname, buf, buf_offset);
 	st = load_object_from("buffer", buf, buf_offset, AS_PER);
 	assert(st);
 
diff --git a/skeletons/constr_SEQUENCE.c b/skeletons/constr_SEQUENCE.c
index 2b0ac3d..a6d12fa 100644
--- a/skeletons/constr_SEQUENCE.c
+++ b/skeletons/constr_SEQUENCE.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003, 2004, 2005, 2006 Lev Walkin <vlm@lionet.info>.
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007 Lev Walkin <vlm@lionet.info>.
  * All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
@@ -1162,12 +1162,87 @@
 	return rv;
 }
 
+/*
+ * #10.1, #10.2
+ */
+static int
+uper_put_open_type(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
+	void *buf;
+	ssize_t size;
+
+	size = uper_encode_to_new_buffer(td, constraints, sptr, &buf);
+	if(size <= 0) return -1;
+
+	while(size) {
+		ssize_t maySave = uper_put_length(po, size);
+		if(maySave < 0) break;
+		if(per_put_many_bits(po, buf, maySave * 8)) break;
+		buf = (char *)buf + maySave;
+		size -= maySave;
+	}
+
+	if(size) {
+		FREEMEM(buf);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+SEQUENCE_handle_extensions(asn_TYPE_descriptor_t *td, void *sptr,
+		asn_per_outp_t *po1, asn_per_outp_t *po2) {
+	asn_SEQUENCE_specifics_t *specs
+		= (asn_SEQUENCE_specifics_t *)td->specifics;
+	int num = 0;
+	int edx;
+
+	if(specs->ext_before < 0)
+		return 0;
+
+	/* Find out which extensions are present */
+	for(edx = specs->ext_after + 1; edx < td->elements_count; edx++) {
+		asn_TYPE_member_t *elm = &td->elements[edx];
+		void *memb_ptr;		/* Pointer to the member */
+		void **memb_ptr2;	/* Pointer to that pointer */
+		int present;
+
+		if(!IN_EXTENSION_GROUP(specs, edx))
+			continue;
+
+		/* Fetch the pointer to this member */
+		if(elm->flags & ATF_POINTER) {
+			memb_ptr2 = (void **)((char *)sptr + elm->memb_offset);
+			present = (*memb_ptr2 != 0);
+		} else {
+			memb_ptr = (void *)((char *)sptr + elm->memb_offset);
+			memb_ptr2 = &memb_ptr;
+			present = 1;
+		}
+
+		ASN_DEBUG("checking ext %d is present => %d", edx, present);
+		num += present;
+
+		/* Encode as presence marker */
+		if(po1 && per_put_few_bits(po1, present, 1))
+			return -1;
+		/* Encode as open type field */
+		if(po2 && present && uper_put_open_type(elm->type,
+				elm->per_constraints, *memb_ptr2, po2))
+			return -1;
+
+	}
+
+	return num;
+}
+
 asn_enc_rval_t
 SEQUENCE_encode_uper(asn_TYPE_descriptor_t *td,
 	asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
 	asn_SEQUENCE_specifics_t *specs
 		= (asn_SEQUENCE_specifics_t *)td->specifics;
 	asn_enc_rval_t er;
+	int n_extensions;
 	int edx;
 	int i;
 
@@ -1179,8 +1254,18 @@
 	er.encoded = 0;
 
 	ASN_DEBUG("Encoding %s as SEQUENCE (UPER)", td->name);
-	if(specs->ext_before >= 0)
-		_ASN_ENCODE_FAILED;	/* We don't encode extensions yet */
+
+
+	/*
+	 * X.691#18.1 Whether structure is extensible
+	 * and whether to encode extensions
+	 */
+	if(specs->ext_before >= 0) {
+		n_extensions = SEQUENCE_handle_extensions(td, sptr, 0, 0);
+		per_put_few_bits(po, n_extensions ? 1 : 0, 1);
+	} else {
+		n_extensions = 0;	/* There are no extensions to encode */
+	}
 
 	/* Encode a presence bitmap */
 	for(i = 0; i < specs->roms_count; i++) {
@@ -1216,10 +1301,11 @@
 	}
 
 	/*
-	 * Get the sequence ROOT elements.
+	 * Encode the sequence ROOT elements.
 	 */
+	ASN_DEBUG("ext_after = %d, ec = %d, eb = %d", specs->ext_after, td->elements_count, specs->ext_before);
 	for(edx = 0; edx < ((specs->ext_before < 0)
-			? td->elements_count : specs->ext_before - 1); edx++) {
+			? td->elements_count : specs->ext_after); edx++) {
 		asn_TYPE_member_t *elm = &td->elements[edx];
 		void *memb_ptr;		/* Pointer to the member */
 		void **memb_ptr2;	/* Pointer to that pointer */
@@ -1244,12 +1330,32 @@
 		if(elm->default_value && elm->default_value(0, memb_ptr2) == 1)
 			continue;
 
+		ASN_DEBUG("encoding root %d", edx);
 		er = elm->type->uper_encoder(elm->type, elm->per_constraints,
 			*memb_ptr2, po);
 		if(er.encoded == -1)
 			return er;
 	}
 
+	/* No extensions to encode */
+	if(!n_extensions) _ASN_ENCODED_OK(er);
+
+	ASN_DEBUG("Length of %d bit-map", n_extensions);
+	/* #18.8. Write down the presence bit-map length. */
+	if(uper_put_nslength(po, n_extensions))
+		_ASN_ENCODE_FAILED;
+
+	ASN_DEBUG("Bit-map of %d elements", n_extensions);
+	/* #18.7. Encoding the extensions presence bit-map. */
+	/* TODO: act upon NOTE in #18.7 for canonical PER */
+	if(SEQUENCE_handle_extensions(td, sptr, po, 0) != n_extensions)
+		_ASN_ENCODE_FAILED;
+
+	ASN_DEBUG("Writing %d extensions", n_extensions);
+	/* #18.9. Encode extensions as open type fields. */
+	if(SEQUENCE_handle_extensions(td, sptr, 0, po) != n_extensions)
+		_ASN_ENCODE_FAILED;
+
 	_ASN_ENCODED_OK(er);
 }
 
diff --git a/skeletons/per_encoder.c b/skeletons/per_encoder.c
index 614dd23..960981a 100644
--- a/skeletons/per_encoder.c
+++ b/skeletons/per_encoder.c
@@ -2,41 +2,11 @@
 #include <asn_internal.h>
 #include <per_encoder.h>
 
-/* Flush partially filled buffer */
-static int _uper_encode_flush_outp(asn_per_outp_t *po);
+static asn_enc_rval_t uper_encode_internal(asn_TYPE_descriptor_t *td, asn_per_constraints_t *, void *sptr, asn_app_consume_bytes_f *cb, void *app_key);
 
 asn_enc_rval_t
 uper_encode(asn_TYPE_descriptor_t *td, void *sptr, asn_app_consume_bytes_f *cb, void *app_key) {
-	asn_per_outp_t po;
-	asn_enc_rval_t er;
-
-	/*
-	 * Invoke type-specific encoder.
-	 */
-	if(!td || !td->uper_encoder)
-		_ASN_ENCODE_FAILED;	/* PER is not compiled in */
-
-	po.buffer = po.tmpspace;
-	po.nboff = 0;
-	po.nbits = 8 * sizeof(po.tmpspace);
-	po.outper = cb;
-	po.op_key = app_key;
-	po.flushed_bytes = 0;
-
-	er = td->uper_encoder(td, 0, sptr, &po);
-	if(er.encoded != -1) {
-		size_t bits_to_flush;
-
-		bits_to_flush = ((po.buffer - po.tmpspace) << 3) + po.nboff;
-
-		/* Set number of bits encoded to a firm value */
-		er.encoded = (po.flushed_bytes << 3) + bits_to_flush;
-
-		if(_uper_encode_flush_outp(&po))
-			_ASN_ENCODE_FAILED;
-	}
-
-	return er;
+	return uper_encode_internal(td, 0, sptr, cb, app_key);
 }
 
 /*
@@ -63,20 +33,70 @@
 uper_encode_to_buffer(asn_TYPE_descriptor_t *td, void *sptr, void *buffer, size_t buffer_size) {
 	enc_to_buf_arg key;
 
-	/*
-	 * Invoke type-specific encoder.
-	 */
-	if(!td || !td->uper_encoder)
-		_ASN_ENCODE_FAILED;	/* PER is not compiled in */
-
 	key.buffer = buffer;
 	key.left = buffer_size;
 
-	ASN_DEBUG("Encoding \"%s\" using UNALIGNED PER", td->name);
+	if(td) ASN_DEBUG("Encoding \"%s\" using UNALIGNED PER", td->name);
 
-	return uper_encode(td, sptr, encode_to_buffer_cb, &key);
+	return uper_encode_internal(td, 0, sptr, encode_to_buffer_cb, &key);
 }
 
+typedef struct enc_dyn_arg {
+	void *buffer;
+	size_t length;
+	size_t allocated;
+} enc_dyn_arg;
+static int
+encode_dyn_cb(const void *buffer, size_t size, void *key) {
+	enc_dyn_arg *arg = key;
+	if(arg->length + size >= arg->allocated) {
+		void *p;
+		arg->allocated = arg->allocated ? (arg->allocated << 2) : size;
+		p = REALLOC(arg->buffer, arg->allocated);
+		if(!p) {
+			FREEMEM(arg->buffer);
+			memset(arg, 0, sizeof(*arg));
+			return -1;
+		}
+		arg->buffer = p;
+	}
+	memcpy(((char *)arg->buffer) + arg->length, buffer, size);
+	arg->length += size;
+	return 0;
+}
+ssize_t
+uper_encode_to_new_buffer(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, void **buffer_r) {
+	asn_enc_rval_t er;
+	enc_dyn_arg key;
+
+	memset(&key, 0, sizeof(key));
+
+	er = uper_encode_internal(td, constraints, sptr, encode_dyn_cb, &key);
+	switch(er.encoded) {
+	case -1:
+		FREEMEM(key.buffer);
+		return -1;
+	case 0:
+		FREEMEM(key.buffer);
+		key.buffer = MALLOC(1);
+		if(key.buffer) {
+			*(char *)key.buffer = '\0';
+			*buffer_r = key.buffer;
+			return 1;
+		} else {
+			return -1;
+		}
+	default:
+		*buffer_r = key.buffer;
+		return ((er.encoded + 7) >> 3);
+	}
+}
+
+/*
+ * Internally useful functions.
+ */
+
+/* Flush partially filled buffer */
 static int
 _uper_encode_flush_outp(asn_per_outp_t *po) {
 	uint8_t *buf;
@@ -93,3 +113,38 @@
 
 	return po->outper(po->tmpspace, buf - po->tmpspace, po->op_key);
 }
+
+static asn_enc_rval_t
+uper_encode_internal(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, asn_app_consume_bytes_f *cb, void *app_key) {
+	asn_per_outp_t po;
+	asn_enc_rval_t er;
+
+	/*
+	 * Invoke type-specific encoder.
+	 */
+	if(!td || !td->uper_encoder)
+		_ASN_ENCODE_FAILED;	/* PER is not compiled in */
+
+	po.buffer = po.tmpspace;
+	po.nboff = 0;
+	po.nbits = 8 * sizeof(po.tmpspace);
+	po.outper = cb;
+	po.op_key = app_key;
+	po.flushed_bytes = 0;
+
+	er = td->uper_encoder(td, constraints, sptr, &po);
+	if(er.encoded != -1) {
+		size_t bits_to_flush;
+
+		bits_to_flush = ((po.buffer - po.tmpspace) << 3) + po.nboff;
+
+		/* Set number of bits encoded to a firm value */
+		er.encoded = (po.flushed_bytes << 3) + bits_to_flush;
+
+		if(_uper_encode_flush_outp(&po))
+			_ASN_ENCODE_FAILED;
+	}
+
+	return er;
+}
+
diff --git a/skeletons/per_encoder.h b/skeletons/per_encoder.h
index 9ac130b..32de082 100644
--- a/skeletons/per_encoder.h
+++ b/skeletons/per_encoder.h
@@ -16,6 +16,9 @@
 
 /*
  * Unaligned PER encoder of any ASN.1 type. May be invoked by the application.
+ * WARNING: This function returns the number of encoded bits in the .encoded
+ * field of the return value. Use the following formula to convert to bytes:
+ * 	bytes = ((.encoded + 7) / 8)
  */
 asn_enc_rval_t uper_encode(struct asn_TYPE_descriptor_s *type_descriptor,
 	void *struct_ptr,	/* Structure to be encoded */
@@ -23,7 +26,11 @@
 	void *app_key		/* Arbitrary callback argument */
 );
 
-/* A variant of uper_encode() which encodes data into the existing buffer */
+/*
+ * A variant of uper_encode() which encodes data into the existing buffer
+ * WARNING: This function returns the number of encoded bits in the .encoded
+ * field of the return value.
+ */
 asn_enc_rval_t uper_encode_to_buffer(
 	struct asn_TYPE_descriptor_s *type_descriptor,
 	void *struct_ptr,	/* Structure to be encoded */
@@ -31,6 +38,19 @@
 	size_t buffer_size	/* Initial buffer size (max) */
 );
 
+/*
+ * A variant of uper_encode_to_buffer() which allocates buffer itself.
+ * Returns the number of bytes in the buffer or -1 in case of failure.
+ * WARNING: This function produces a "Production of the complete encoding",
+ * with length of at least one octet. Contrast this to precise bit-preserving
+ * encoding of uper_encode() and uper_encode_to_buffer().
+ */
+ssize_t uper_encode_to_new_buffer(
+	struct asn_TYPE_descriptor_s *type_descriptor,
+	asn_per_constraints_t *constraints,
+	void *struct_ptr,	/* Structure to be encoded */
+	void **buffer_r		/* Buffer allocated and returned */
+);
 
 /*
  * Type of the generic PER encoder function.
diff --git a/skeletons/per_support.c b/skeletons/per_support.c
index 0bb3d4b..73e2a8d 100644
--- a/skeletons/per_support.c
+++ b/skeletons/per_support.c
@@ -318,3 +318,26 @@
 			? -1 : (ssize_t)(length << 14);
 }
 
+
+/*
+ * Put the normally small length "n" into the stream.
+ * This procedure used to encode length of extensions bit-maps
+ * for SET and SEQUENCE types.
+ */
+int
+uper_put_nslength(asn_per_outp_t *po, size_t length) {
+
+	if(length <= 64) {
+		/* #10.9.3.4 */
+		if(length == 0) return -1;
+		return per_put_few_bits(po, length-1, 7) ? -1 : 0;
+	} else {
+		if(uper_put_length(po, length) != length) {
+			/* This might happen in case of >16K extensions */
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
diff --git a/skeletons/per_support.h b/skeletons/per_support.h
index 2708724..4e7e6bd 100644
--- a/skeletons/per_support.h
+++ b/skeletons/per_support.h
@@ -96,6 +96,12 @@
 ssize_t uper_put_length(asn_per_outp_t *po, size_t whole_length);
 
 /*
+ * Put the normally small length "n" to the Unaligned PER stream.
+ * Returns 0 or -1.
+ */
+int uper_put_nslength(asn_per_outp_t *po, size_t length);
+
+/*
  * Put the normally small non-negative whole number.
  */
 int uper_put_nsnnwn(asn_per_outp_t *po, int n);