per support


git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@1127 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/skeletons/ANY.c b/skeletons/ANY.c
index a96aac0..612238b 100644
--- a/skeletons/ANY.c
+++ b/skeletons/ANY.c
@@ -21,7 +21,7 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_hex,
 	ANY_encode_xer,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	0, 0, 0, 0,
 	0,	/* No PER visible constraints */
diff --git a/skeletons/BIT_STRING.c b/skeletons/BIT_STRING.c
index 7434a2e..6469d4f 100644
--- a/skeletons/BIT_STRING.c
+++ b/skeletons/BIT_STRING.c
@@ -28,6 +28,7 @@
 	OCTET_STRING_decode_xer_binary,
 	BIT_STRING_encode_xer,
 	OCTET_STRING_decode_uper,	/* Unaligned PER decoder */
+	OCTET_STRING_encode_uper,	/* Unaligned PER encoder */
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_BIT_STRING_tags,
 	sizeof(asn_DEF_BIT_STRING_tags)
diff --git a/skeletons/BMPString.c b/skeletons/BMPString.c
index 10fd05e..1bfb174 100644
--- a/skeletons/BMPString.c
+++ b/skeletons/BMPString.c
@@ -23,7 +23,7 @@
 	OCTET_STRING_encode_der,
 	BMPString_decode_xer,		/* Convert from UTF-8 */
 	BMPString_encode_xer,		/* Convert to UTF-8 */
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_BMPString_tags,
 	sizeof(asn_DEF_BMPString_tags)
diff --git a/skeletons/BOOLEAN.c b/skeletons/BOOLEAN.c
index 84e5785..bb4697c 100644
--- a/skeletons/BOOLEAN.c
+++ b/skeletons/BOOLEAN.c
@@ -23,6 +23,7 @@
 	BOOLEAN_decode_xer,
 	BOOLEAN_encode_xer,
 	BOOLEAN_decode_uper,	/* Unaligned PER decoder */
+	BOOLEAN_encode_uper,	/* Unaligned PER encoder */
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_BOOLEAN_tags,
 	sizeof(asn_DEF_BOOLEAN_tags) / sizeof(asn_DEF_BOOLEAN_tags[0]),
@@ -266,3 +267,18 @@
 	return rv;
 }
 
+
+asn_enc_rval_t
+BOOLEAN_encode_uper(asn_TYPE_descriptor_t *td,
+	asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
+	const BOOLEAN_t *st = (const BOOLEAN_t *)sptr;
+	asn_enc_rval_t er;
+
+	(void)constraints;
+
+	if(!st) _ASN_ENCODE_FAILED;
+
+	per_put_few_bits(po, *st ? 1 : 0, 1);
+
+	_ASN_ENCODED_OK(er);
+}
diff --git a/skeletons/BOOLEAN.h b/skeletons/BOOLEAN.h
index 1cf1980..217d0f1 100644
--- a/skeletons/BOOLEAN.h
+++ b/skeletons/BOOLEAN.h
@@ -27,6 +27,7 @@
 xer_type_decoder_f BOOLEAN_decode_xer;
 xer_type_encoder_f BOOLEAN_encode_xer;
 per_type_decoder_f BOOLEAN_decode_uper;
+per_type_encoder_f BOOLEAN_encode_uper;
 
 #ifdef __cplusplus
 }
diff --git a/skeletons/ENUMERATED.c b/skeletons/ENUMERATED.c
index 6a8b9c6..90761a2 100644
--- a/skeletons/ENUMERATED.c
+++ b/skeletons/ENUMERATED.c
@@ -1,5 +1,6 @@
 /*-
- * Copyright (c) 2003, 2005 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2003, 2005, 2006 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 #include <asn_internal.h>
@@ -24,6 +25,7 @@
 	INTEGER_decode_xer,	/* This is temporary! */
 	INTEGER_encode_xer,
 	ENUMERATED_decode_uper,	/* Unaligned PER decoder */
+	ENUMERATED_encode_uper,	/* Unaligned PER encoder */
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_ENUMERATED_tags,
 	sizeof(asn_DEF_ENUMERATED_tags) / sizeof(asn_DEF_ENUMERATED_tags[0]),
@@ -54,3 +56,16 @@
 			rval.code = RC_FAIL;
 	return rval;
 }
+
+asn_enc_rval_t
+ENUMERATED_encode_uper(asn_TYPE_descriptor_t *td,
+	asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
+	ENUMERATED_t *st = (ENUMERATED_t *)sptr;
+	long value;
+
+	if(asn_INTEGER2long(st, &value))
+		_ASN_ENCODE_FAILED;
+
+	return NativeEnumerated_encode_uper(td, constraints, &value, po);
+}
+
diff --git a/skeletons/ENUMERATED.h b/skeletons/ENUMERATED.h
index e65a15a..542dcae 100644
--- a/skeletons/ENUMERATED.h
+++ b/skeletons/ENUMERATED.h
@@ -16,6 +16,7 @@
 extern asn_TYPE_descriptor_t asn_DEF_ENUMERATED;
 
 per_type_decoder_f ENUMERATED_decode_uper;
+per_type_encoder_f ENUMERATED_encode_uper;
 
 #ifdef __cplusplus
 }
diff --git a/skeletons/GeneralString.c b/skeletons/GeneralString.c
index c1a8823..55bb664 100644
--- a/skeletons/GeneralString.c
+++ b/skeletons/GeneralString.c
@@ -22,7 +22,7 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_hex,
 	OCTET_STRING_encode_xer,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_GeneralString_tags,
 	sizeof(asn_DEF_GeneralString_tags)
diff --git a/skeletons/GeneralizedTime.c b/skeletons/GeneralizedTime.c
index 6236d2a..6d7855e 100644
--- a/skeletons/GeneralizedTime.c
+++ b/skeletons/GeneralizedTime.c
@@ -152,7 +152,7 @@
 	GeneralizedTime_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	GeneralizedTime_encode_xer,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_GeneralizedTime_tags,
 	sizeof(asn_DEF_GeneralizedTime_tags)
diff --git a/skeletons/GraphicString.c b/skeletons/GraphicString.c
index 0c3ad47..135cd73 100644
--- a/skeletons/GraphicString.c
+++ b/skeletons/GraphicString.c
@@ -22,7 +22,7 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_hex,
 	OCTET_STRING_encode_xer,	/* Can't expect it to be ASCII/UTF8 */
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_GraphicString_tags,
 	sizeof(asn_DEF_GraphicString_tags)
diff --git a/skeletons/IA5String.c b/skeletons/IA5String.c
index abc9ff3..5c000b0 100644
--- a/skeletons/IA5String.c
+++ b/skeletons/IA5String.c
@@ -22,7 +22,7 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	OCTET_STRING_encode_xer_utf8,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_IA5String_tags,
 	sizeof(asn_DEF_IA5String_tags)
diff --git a/skeletons/INTEGER.c b/skeletons/INTEGER.c
index c43ac63..e9f61ac 100644
--- a/skeletons/INTEGER.c
+++ b/skeletons/INTEGER.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003, 2004, 2005 Lev Walkin <vlm@lionet.info>.
+ * Copyright (c) 2003, 2004, 2005, 2006 Lev Walkin <vlm@lionet.info>.
  * All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
@@ -25,6 +25,7 @@
 	INTEGER_decode_xer,
 	INTEGER_encode_xer,
 	INTEGER_decode_uper,	/* Unaligned PER decoder */
+	INTEGER_encode_uper,	/* Unaligned PER encoder */
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_INTEGER_tags,
 	sizeof(asn_DEF_INTEGER_tags) / sizeof(asn_DEF_INTEGER_tags[0]),
@@ -530,6 +531,78 @@
 	return rval;
 }
 
+asn_enc_rval_t
+INTEGER_encode_uper(asn_TYPE_descriptor_t *td,
+	asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
+	asn_enc_rval_t er;
+	INTEGER_t *st = (INTEGER_t *)sptr;
+	const uint8_t *buf;
+	const uint8_t *end;
+	asn_per_constraint_t *ct;
+	long value = 0;
+
+	if(!st || st->size == 0) _ASN_ENCODE_FAILED;
+
+	if(!constraints) constraints = td->per_constraints;
+	ct = constraints ? &constraints->value : 0;
+
+	er.encoded = 0;
+
+	if(ct) {
+		int inext = 0;
+		if(asn_INTEGER2long(st, &value))
+			_ASN_ENCODE_FAILED;
+		/* Check proper range */
+		if(ct->flags & APC_SEMI_CONSTRAINED) {
+			if(value < ct->lower_bound)
+				inext = 1;
+		} else if(ct->range_bits >= 0) {
+			if(value < ct->lower_bound
+			|| value > ct->upper_bound)
+				inext = 1;
+		}
+		ASN_DEBUG("Value %ld (%02x/%d) lb %ld ub %ld %s",
+			value, st->buf[0], st->size,
+			ct->lower_bound, ct->upper_bound,
+			inext ? "ext" : "fix");
+		if(ct->flags & APC_EXTENSIBLE) {
+			if(per_put_few_bits(po, inext, 1))
+				_ASN_ENCODE_FAILED;
+			if(inext) ct = 0;
+		} else if(inext) {
+			_ASN_ENCODE_FAILED;
+		}
+	}
+
+
+	/* X.691, #12.2.2 */
+	if(ct && ct->range_bits >= 0) {
+		/* #10.5.6 */
+		ASN_DEBUG("Encoding integer with range %d bits",
+			ct->range_bits);
+		if(per_put_few_bits(po, value - ct->lower_bound,
+				ct->range_bits))
+			_ASN_ENCODE_FAILED;
+		_ASN_ENCODED_OK(er);
+	}
+
+	if(ct && ct->lower_bound) {
+		ASN_DEBUG("Adjust lower bound to %ld", ct->lower_bound);
+		/* TODO: adjust lower bound */
+		_ASN_ENCODE_FAILED;
+	}
+
+	for(buf = st->buf, end = st->buf + st->size; buf < end;) {
+		ssize_t mayEncode = uper_put_length(po, end - buf);
+		if(mayEncode < 0)
+			_ASN_ENCODE_FAILED;
+		if(per_put_many_bits(po, buf, 8 * mayEncode))
+			_ASN_ENCODE_FAILED;
+	}
+
+	_ASN_ENCODED_OK(er);
+}
+
 int
 asn_INTEGER2long(const INTEGER_t *iptr, long *lptr) {
 	uint8_t *b, *end;
diff --git a/skeletons/INTEGER.h b/skeletons/INTEGER.h
index 410fbe9..62832b1 100644
--- a/skeletons/INTEGER.h
+++ b/skeletons/INTEGER.h
@@ -38,6 +38,7 @@
 xer_type_decoder_f INTEGER_decode_xer;
 xer_type_encoder_f INTEGER_encode_xer;
 per_type_decoder_f INTEGER_decode_uper;
+per_type_encoder_f INTEGER_encode_uper;
 
 /***********************************
  * Some handy conversion routines. *
diff --git a/skeletons/ISO646String.c b/skeletons/ISO646String.c
index 829af32..d164aa7 100644
--- a/skeletons/ISO646String.c
+++ b/skeletons/ISO646String.c
@@ -22,7 +22,7 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	OCTET_STRING_encode_xer_utf8,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_ISO646String_tags,
 	sizeof(asn_DEF_ISO646String_tags)
diff --git a/skeletons/NULL.c b/skeletons/NULL.c
index 504be95..6d3316f 100644
--- a/skeletons/NULL.c
+++ b/skeletons/NULL.c
@@ -24,6 +24,7 @@
 	NULL_decode_xer,
 	NULL_encode_xer,
 	NULL_decode_uper,	/* Unaligned PER decoder */
+	NULL_encode_uper,	/* Unaligned PER encoder */
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_NULL_tags,
 	sizeof(asn_DEF_NULL_tags) / sizeof(asn_DEF_NULL_tags[0]),
@@ -130,3 +131,17 @@
 	rv.consumed = 0;
 	return rv;
 }
+
+asn_enc_rval_t
+NULL_encode_uper(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints,
+		void *sptr, asn_per_outp_t *po) {
+	asn_enc_rval_t er;
+
+	(void)td;
+	(void)constraints;
+	(void)sptr;
+	(void)po;
+
+	er.encoded = 0;
+	_ASN_ENCODED_OK(er);
+}
diff --git a/skeletons/NULL.h b/skeletons/NULL.h
index 7126e1d..131e775 100644
--- a/skeletons/NULL.h
+++ b/skeletons/NULL.h
@@ -24,6 +24,7 @@
 xer_type_decoder_f NULL_decode_xer;
 xer_type_encoder_f NULL_encode_xer;
 per_type_decoder_f NULL_decode_uper;
+per_type_encoder_f NULL_encode_uper;
 
 #ifdef __cplusplus
 }
diff --git a/skeletons/NativeEnumerated.c b/skeletons/NativeEnumerated.c
index 937accd..fa16eb0 100644
--- a/skeletons/NativeEnumerated.c
+++ b/skeletons/NativeEnumerated.c
@@ -29,6 +29,7 @@
 	NativeInteger_decode_xer,
 	NativeEnumerated_encode_xer,
 	NativeEnumerated_decode_uper,
+	NativeEnumerated_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_NativeEnumerated_tags,
 	sizeof(asn_DEF_NativeEnumerated_tags) / sizeof(asn_DEF_NativeEnumerated_tags[0]),
@@ -70,8 +71,9 @@
 }
 
 asn_dec_rval_t
-NativeEnumerated_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
-	asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) {  
+NativeEnumerated_decode_uper(asn_codec_ctx_t *opt_codec_ctx,
+	asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints,
+	void **sptr, asn_per_data_t *pd) {
 	asn_INTEGER_specifics_t *specs = (asn_INTEGER_specifics_t *)td->specifics;
 	asn_dec_rval_t rval = { RC_OK, 0 };
 	long *native = (long *)*sptr;
@@ -123,3 +125,80 @@
 	return rval;
 }
 
+static int
+NativeEnumerated__compar_value2enum(const void *ap, const void *bp) {
+	const asn_INTEGER_enum_map_t *a = ap;
+	const asn_INTEGER_enum_map_t *b = bp;
+	if(a->nat_value == b->nat_value)
+		return 0;
+	if(a->nat_value < b->nat_value)
+		return -1;
+	return 1;
+}
+
+asn_enc_rval_t
+NativeEnumerated_encode_uper(asn_TYPE_descriptor_t *td,
+	asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
+	asn_INTEGER_specifics_t *specs = (asn_INTEGER_specifics_t *)td->specifics;
+	asn_enc_rval_t er;
+	long native, value;
+	asn_per_constraint_t *ct;
+	int inext = 0;
+	asn_INTEGER_enum_map_t key;
+	asn_INTEGER_enum_map_t *kf;
+
+	if(!sptr) _ASN_ENCODE_FAILED;
+	if(!specs) _ASN_ENCODE_FAILED;
+
+	if(constraints) ct = &constraints->value;
+	else if(td->per_constraints) ct = &td->per_constraints->value;
+	else _ASN_ENCODE_FAILED;	/* Mandatory! */
+
+	ASN_DEBUG("Encoding %s as NativeEnumerated", td->name);
+
+	er.encoded = 0;
+
+	native = *(long *)sptr;
+	if(native < 0) _ASN_ENCODE_FAILED;
+
+	key.nat_value = native;
+	kf = bsearch(&key, specs->value2enum, specs->map_count,
+		sizeof(key), NativeEnumerated__compar_value2enum);
+	if(!kf) {
+		ASN_DEBUG("No element corresponds to %ld", native);
+		_ASN_ENCODE_FAILED;
+	}
+	value = kf - specs->value2enum;
+
+	if(ct->range_bits >= 0) {
+		int cmpWith = specs->extension
+				? specs->extension - 1 : specs->map_count;
+		if(value >= cmpWith)
+			inext = 1;
+	}
+	if(ct->flags & APC_EXTENSIBLE) {
+		if(per_put_few_bits(po, inext, 0))
+			_ASN_ENCODE_FAILED;
+		ct = 0;
+	} else if(inext) {
+		_ASN_ENCODE_FAILED;
+	}
+
+	if(ct && ct->range_bits >= 0) {
+		if(per_put_few_bits(po, value, ct->range_bits))
+			_ASN_ENCODE_FAILED;
+		_ASN_ENCODED_OK(er);
+	}
+
+	if(!specs->extension)
+		_ASN_ENCODE_FAILED;
+
+	/*
+	 * X.691, #10.6: normally small non-negative whole number;
+	 */
+	if(uper_put_nsnnwn(po, value - (specs->extension - 1)))
+		_ASN_ENCODE_FAILED;
+
+	_ASN_ENCODED_OK(er);
+}
+
diff --git a/skeletons/NativeEnumerated.h b/skeletons/NativeEnumerated.h
index 5792bee..c59bb1b 100644
--- a/skeletons/NativeEnumerated.h
+++ b/skeletons/NativeEnumerated.h
@@ -1,5 +1,6 @@
 /*-
- * Copyright (c) 2004, 2005 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2004, 2005, 2006 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 /*
@@ -22,6 +23,7 @@
 
 xer_type_encoder_f NativeEnumerated_encode_xer;
 per_type_decoder_f NativeEnumerated_decode_uper;
+per_type_encoder_f NativeEnumerated_encode_uper;
 
 #ifdef __cplusplus
 }
diff --git a/skeletons/NativeInteger.c b/skeletons/NativeInteger.c
index 3d1d6b4..34599f6 100644
--- a/skeletons/NativeInteger.c
+++ b/skeletons/NativeInteger.c
@@ -1,5 +1,6 @@
 /*-
- * Copyright (c) 2004, 2005 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2004, 2005, 2006 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 /*
@@ -29,6 +30,7 @@
 	NativeInteger_decode_xer,
 	NativeInteger_encode_xer,
 	NativeInteger_decode_uper,	/* Unaligned PER decoder */
+	NativeInteger_encode_uper,	/* Unaligned PER encoder */
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_NativeInteger_tags,
 	sizeof(asn_DEF_NativeInteger_tags) / sizeof(asn_DEF_NativeInteger_tags[0]),
@@ -253,6 +255,27 @@
 	return rval;
 }
 
+asn_enc_rval_t
+NativeInteger_encode_uper(asn_TYPE_descriptor_t *td,
+	asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
+	asn_enc_rval_t er;
+	long native;
+	INTEGER_t tmpint;
+
+	if(!sptr) _ASN_ENCODE_FAILED;
+
+	native = *(long *)sptr;
+
+	ASN_DEBUG("Encoding NativeInteger %s %ld (UPER)", td->name, native);
+
+	memset(&tmpint, 0, sizeof(tmpint));
+	if(asn_long2INTEGER(&tmpint, native))
+		_ASN_ENCODE_FAILED;
+	er = INTEGER_encode_uper(td, constraints, &tmpint, po);
+	ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_INTEGER, &tmpint);
+	return er;
+}
+
 /*
  * INTEGER specific human-readable output.
  */
diff --git a/skeletons/NativeInteger.h b/skeletons/NativeInteger.h
index e2ce22e..4e63a83 100644
--- a/skeletons/NativeInteger.h
+++ b/skeletons/NativeInteger.h
@@ -28,6 +28,7 @@
 xer_type_decoder_f NativeInteger_decode_xer;
 xer_type_encoder_f NativeInteger_encode_xer;
 per_type_decoder_f NativeInteger_decode_uper;
+per_type_encoder_f NativeInteger_encode_uper;
 
 #ifdef __cplusplus
 }
diff --git a/skeletons/NativeReal.c b/skeletons/NativeReal.c
index 1683f90..2b8ec16 100644
--- a/skeletons/NativeReal.c
+++ b/skeletons/NativeReal.c
@@ -29,7 +29,7 @@
 	NativeReal_encode_der,
 	NativeReal_decode_xer,
 	NativeReal_encode_xer,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_NativeReal_tags,
 	sizeof(asn_DEF_NativeReal_tags) / sizeof(asn_DEF_NativeReal_tags[0]),
diff --git a/skeletons/NumericString.c b/skeletons/NumericString.c
index f980dcd..cef64ea 100644
--- a/skeletons/NumericString.c
+++ b/skeletons/NumericString.c
@@ -22,7 +22,7 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	OCTET_STRING_encode_xer_utf8,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_NumericString_tags,
 	sizeof(asn_DEF_NumericString_tags)
diff --git a/skeletons/OBJECT_IDENTIFIER.c b/skeletons/OBJECT_IDENTIFIER.c
index 53d5353..b1666dc 100644
--- a/skeletons/OBJECT_IDENTIFIER.c
+++ b/skeletons/OBJECT_IDENTIFIER.c
@@ -23,7 +23,7 @@
 	der_encode_primitive,
 	OBJECT_IDENTIFIER_decode_xer,
 	OBJECT_IDENTIFIER_encode_xer,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_OBJECT_IDENTIFIER_tags,
 	sizeof(asn_DEF_OBJECT_IDENTIFIER_tags)
@@ -425,6 +425,7 @@
 	 * The following conditions must hold:
 	 * assert(arcval);
 	 * assert(arcval_size > 0);
+	 * assert(arcval_size <= 16);
 	 * assert(arcbuf);
 	 */
 #ifdef	WORDS_BIGENDIAN
@@ -437,12 +438,7 @@
 	unsigned int cache;
 	uint8_t *bp = arcbuf;
 	int bits;
-#ifdef	__GNUC__
-	uint8_t buffer[arcval_size];
-#else
-	uint8_t *buffer = alloca(arcval_size);
-	if(!buffer) { errno = ENOMEM; return -1; }
-#endif
+	uint8_t buffer[16];
 
 	if(isLittleEndian && !prepared_order) {
 		const uint8_t *a = (const unsigned char *)arcval + arcval_size - 1;
@@ -502,7 +498,9 @@
 	unsigned size;
 	unsigned i;
 
-	if(!oid || !arcs || arc_type_size < 1 || arc_slots < 2) {
+	if(!oid || !arcs || arc_type_size < 1
+	|| arc_type_size > 16
+	|| arc_slots < 2) {
 		errno = EINVAL;
 		return -1;
 	}
@@ -586,17 +584,8 @@
 	 */
 	{
 		uint8_t *tp;
-#ifdef	__GNUC__
-		uint8_t first_value[1 + arc_type_size];	/* of two arcs */
+		uint8_t first_value[1 + 16];	/* of two arcs */
 		uint8_t *fv = first_value;
-#else
-		uint8_t *first_value = alloca(1 + arc_type_size);
-		uint8_t *fv = first_value;
-		if(!first_value) {
-			errno = ENOMEM;
-			return -1;
-		}
-#endif
 
 		/*
 		 * Simulate first_value = arc0 * 40 + arc1;
diff --git a/skeletons/OCTET_STRING.c b/skeletons/OCTET_STRING.c
index c5a4ed3..f2dd1c6 100644
--- a/skeletons/OCTET_STRING.c
+++ b/skeletons/OCTET_STRING.c
@@ -33,6 +33,7 @@
 	OCTET_STRING_decode_xer_hex,
 	OCTET_STRING_encode_xer,
 	OCTET_STRING_decode_uper,	/* Unaligned PER decoder */
+	OCTET_STRING_encode_uper,	/* Unaligned PER encoder */
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_OCTET_STRING_tags,
 	sizeof(asn_DEF_OCTET_STRING_tags)
@@ -1297,6 +1298,103 @@
 	return rval;
 }
 
+asn_enc_rval_t
+OCTET_STRING_encode_uper(asn_TYPE_descriptor_t *td,
+        asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
+
+	asn_OCTET_STRING_specifics_t *specs = td->specifics
+		? (asn_OCTET_STRING_specifics_t *)td->specifics
+		: &asn_DEF_OCTET_STRING_specs;
+	asn_per_constraint_t *ct = constraints ? &constraints->size
+				: (td->per_constraints
+					? &td->per_constraints->size
+					: &asn_DEF_OCTET_STRING_constraint);
+	const BIT_STRING_t *st = (const BIT_STRING_t *)sptr;
+	int unit_bits = (specs->subvariant != 1) * 7 + 1;
+	asn_enc_rval_t er;
+	int ct_extensible = ct->flags & APC_EXTENSIBLE;
+	int inext = 0;		/* Lies not within extension root */
+	int sizeinunits = st->size;
+	const uint8_t *buf;
+	int ret;
+
+	if(!st || !st->buf)
+		_ASN_ENCODE_FAILED;
+
+	if(unit_bits == 1) {
+		ASN_DEBUG("BIT STRING of %d bytes, %d bits unused",
+				sizeinunits, st->bits_unused);
+		sizeinunits = sizeinunits * 8 - (st->bits_unused & 0x07);
+	}
+
+	ASN_DEBUG("Encoding %s into %d units",
+		td->name, sizeinunits);
+
+	/* Figure out wheter size lies within PER visible consrtaint */
+
+	if(ct->effective_bits >= 0) {
+		if(sizeinunits < ct->lower_bound
+		|| sizeinunits > ct->upper_bound) {
+			if(ct_extensible) {
+				ct = &asn_DEF_OCTET_STRING_constraint;
+				inext = 1;
+			} else
+				_ASN_ENCODE_FAILED;
+		}
+	} else {
+		inext = 0;
+	}
+
+	if(ct_extensible) {
+		/* Declare whether length is [not] within extension root */
+		if(per_put_few_bits(po, inext, 1))
+			_ASN_ENCODE_FAILED;
+	}
+
+	/* X.691, #16.5: zero-length encoding */
+	/* X.691, #16.6: short fixed length encoding (up to 2 octets) */
+	/* X.691, #16.7: long fixed length encoding (up to 64K octets) */
+	if(ct->effective_bits >= 0) {
+		ASN_DEBUG("Encoding %d bytes (%ld), length in %d bits",
+				st->size, sizeinunits - ct->lower_bound,
+				ct->effective_bits);
+		ret = per_put_few_bits(po, sizeinunits - ct->lower_bound,
+				ct->effective_bits);
+		if(ret) _ASN_ENCODE_FAILED;
+		ret = per_put_many_bits(po, st->buf, sizeinunits);
+		if(ret) _ASN_ENCODE_FAILED;
+		_ASN_ENCODED_OK(er);
+	}
+
+	ASN_DEBUG("Encoding %d bytes", st->size);
+
+	if(sizeinunits == 0) {
+		if(uper_put_length(po, 0))
+			_ASN_ENCODE_FAILED;
+		_ASN_ENCODED_OK(er);
+	}
+
+	buf = st->buf;
+	while(sizeinunits) {
+		ssize_t maySave = uper_put_length(po, sizeinunits);
+		if(maySave < 0) _ASN_ENCODE_FAILED;
+
+		ASN_DEBUG("Encoding %d of %d", maySave, sizeinunits);
+
+		ret = per_put_many_bits(po, buf, maySave);
+		if(ret) _ASN_ENCODE_FAILED;
+
+		if(unit_bits == 1)
+			buf += maySave >> 3;
+		else
+			buf += maySave;
+		sizeinunits -= maySave;
+		assert(!(maySave & 0x07) || !sizeinunits);
+	}
+
+	_ASN_ENCODED_OK(er);
+}
+
 int
 OCTET_STRING_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
 	asn_app_consume_bytes_f *cb, void *app_key) {
diff --git a/skeletons/OCTET_STRING.h b/skeletons/OCTET_STRING.h
index e85d05c..5150161 100644
--- a/skeletons/OCTET_STRING.h
+++ b/skeletons/OCTET_STRING.h
@@ -31,6 +31,7 @@
 xer_type_encoder_f OCTET_STRING_encode_xer;
 xer_type_encoder_f OCTET_STRING_encode_xer_utf8;
 per_type_decoder_f OCTET_STRING_decode_uper;
+per_type_encoder_f OCTET_STRING_encode_uper;
 
 /******************************
  * Handy conversion routines. *
diff --git a/skeletons/ObjectDescriptor.c b/skeletons/ObjectDescriptor.c
index 37f51e8..44cb5f6 100644
--- a/skeletons/ObjectDescriptor.c
+++ b/skeletons/ObjectDescriptor.c
@@ -22,7 +22,7 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	OCTET_STRING_encode_xer_utf8,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_ObjectDescriptor_tags,
 	sizeof(asn_DEF_ObjectDescriptor_tags)
diff --git a/skeletons/PrintableString.c b/skeletons/PrintableString.c
index 5533253..f588083 100644
--- a/skeletons/PrintableString.c
+++ b/skeletons/PrintableString.c
@@ -22,7 +22,7 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	OCTET_STRING_encode_xer_utf8,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_PrintableString_tags,
 	sizeof(asn_DEF_PrintableString_tags)
diff --git a/skeletons/REAL.c b/skeletons/REAL.c
index 689b05e..dfaedf7 100644
--- a/skeletons/REAL.c
+++ b/skeletons/REAL.c
@@ -42,7 +42,7 @@
 	der_encode_primitive,
 	REAL_decode_xer,
 	REAL_encode_xer,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_REAL_tags,
 	sizeof(asn_DEF_REAL_tags) / sizeof(asn_DEF_REAL_tags[0]),
diff --git a/skeletons/RELATIVE-OID.c b/skeletons/RELATIVE-OID.c
index 74511b7..0181434 100644
--- a/skeletons/RELATIVE-OID.c
+++ b/skeletons/RELATIVE-OID.c
@@ -25,7 +25,7 @@
 	der_encode_primitive,
 	RELATIVE_OID_decode_xer,
 	RELATIVE_OID_encode_xer,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_RELATIVE_OID_tags,
 	sizeof(asn_DEF_RELATIVE_OID_tags)
diff --git a/skeletons/T61String.c b/skeletons/T61String.c
index 4157a6b..25d887a 100644
--- a/skeletons/T61String.c
+++ b/skeletons/T61String.c
@@ -22,7 +22,7 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_hex,
 	OCTET_STRING_encode_xer,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_T61String_tags,
 	sizeof(asn_DEF_T61String_tags)
diff --git a/skeletons/TeletexString.c b/skeletons/TeletexString.c
index 11f5703..b96cb3b 100644
--- a/skeletons/TeletexString.c
+++ b/skeletons/TeletexString.c
@@ -22,7 +22,7 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_hex,
 	OCTET_STRING_encode_xer,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_TeletexString_tags,
 	sizeof(asn_DEF_TeletexString_tags)
diff --git a/skeletons/UTCTime.c b/skeletons/UTCTime.c
index 5d7ba84..3793a0e 100644
--- a/skeletons/UTCTime.c
+++ b/skeletons/UTCTime.c
@@ -28,7 +28,7 @@
 	OCTET_STRING_encode_der,    /* Implemented in terms of OCTET STRING */
 	OCTET_STRING_decode_xer_utf8,
 	UTCTime_encode_xer,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_UTCTime_tags,
 	sizeof(asn_DEF_UTCTime_tags)
diff --git a/skeletons/UTF8String.c b/skeletons/UTF8String.c
index 85f2ccd..3cd574d 100644
--- a/skeletons/UTF8String.c
+++ b/skeletons/UTF8String.c
@@ -22,7 +22,7 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	OCTET_STRING_encode_xer_utf8,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_UTF8String_tags,
 	sizeof(asn_DEF_UTF8String_tags)
diff --git a/skeletons/UniversalString.c b/skeletons/UniversalString.c
index 0fab915..a39e569 100644
--- a/skeletons/UniversalString.c
+++ b/skeletons/UniversalString.c
@@ -23,7 +23,7 @@
 	OCTET_STRING_encode_der,
 	UniversalString_decode_xer,	/* Convert from UTF-8 */
 	UniversalString_encode_xer,	/* Convert into UTF-8 */
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_UniversalString_tags,
 	sizeof(asn_DEF_UniversalString_tags)
diff --git a/skeletons/VideotexString.c b/skeletons/VideotexString.c
index c51c31a..5f5f33d 100644
--- a/skeletons/VideotexString.c
+++ b/skeletons/VideotexString.c
@@ -22,7 +22,7 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_hex,
 	OCTET_STRING_encode_xer,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_VideotexString_tags,
 	sizeof(asn_DEF_VideotexString_tags)
diff --git a/skeletons/VisibleString.c b/skeletons/VisibleString.c
index 7c70501..8796582 100644
--- a/skeletons/VisibleString.c
+++ b/skeletons/VisibleString.c
@@ -22,7 +22,7 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	OCTET_STRING_encode_xer_utf8,
-	0,
+	0, 0,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_VisibleString_tags,
 	sizeof(asn_DEF_VisibleString_tags)
diff --git a/skeletons/constr_CHOICE.c b/skeletons/constr_CHOICE.c
index 84a3b4e..224e56a 100644
--- a/skeletons/constr_CHOICE.c
+++ b/skeletons/constr_CHOICE.c
@@ -1,5 +1,5 @@
-/*-
- * Copyright (c) 2003, 2004, 2005 Lev Walkin <vlm@lionet.info>.
+/*
+ * Copyright (c) 2003, 2004, 2005, 2006 Lev Walkin <vlm@lionet.info>.
  * All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
@@ -363,6 +363,8 @@
 	size_t computed_size = 0;
 	int present;
 
+	if(!sptr) _ASN_ENCODE_FAILED;
+
 	ASN_DEBUG("%s %s as CHOICE",
 		cb?"Encoding":"Estimating", td->name);
 
@@ -892,6 +894,82 @@
 	return rv;
 }
    
+asn_enc_rval_t
+CHOICE_encode_uper(asn_TYPE_descriptor_t *td,
+	asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
+	asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics;
+	asn_TYPE_member_t *elm;	/* CHOICE's element */
+	asn_per_constraint_t *ct;
+	void *memb_ptr;
+	int present;
+
+	if(!sptr) _ASN_ENCODE_FAILED;
+
+	ASN_DEBUG("Encoding %s as CHOICE", td->name);
+
+	if(constraints) ct = &constraints->value;
+	else if(td->per_constraints) ct = &td->per_constraints->value;
+	else ct = 0;
+
+	present = _fetch_present_idx(sptr,
+		specs->pres_offset, specs->pres_size);
+
+	/*
+	 * If the structure was not initialized properly, it cannot be encoded:
+	 * can't deduce what to encode in the choice type.
+	 */
+	if(present <= 0 || present > td->elements_count)
+		_ASN_ENCODE_FAILED;
+	else
+		present--;
+
+	/* Adjust if canonical order is different from natural order */
+	if(specs->canonical_order)
+		present = specs->canonical_order[present];
+
+	ASN_DEBUG("Encoding %s CHOICE element %d", td->name, present);
+
+	if(ct && ct->range_bits >= 0) {
+		if(present < ct->lower_bound
+		|| present > ct->upper_bound) {
+			if(ct->flags & APC_EXTENSIBLE) {
+				if(per_put_few_bits(po, 1, 1))
+					_ASN_ENCODE_FAILED;
+			} else {
+				_ASN_ENCODE_FAILED;
+			}
+			ct = 0;
+		}
+	}
+	if(ct && ct->flags & APC_EXTENSIBLE)
+		if(per_put_few_bits(po, 0, 1))
+			_ASN_ENCODE_FAILED;
+
+	if(ct && ct->range_bits >= 0) {
+		if(per_put_few_bits(po, present, ct->range_bits))
+			_ASN_ENCODE_FAILED;
+	} else {
+		if(specs->ext_start == -1)
+			_ASN_ENCODE_FAILED;
+		if(uper_put_nsnnwn(po, present - specs->ext_start))
+			_ASN_ENCODE_FAILED;
+		ASN_DEBUG("NOT IMPLEMENTED YET");
+		_ASN_ENCODE_FAILED;
+	}
+
+	elm = &td->elements[present];
+	if(elm->flags & ATF_POINTER) {
+		/* Member is a pointer to another structure */
+		memb_ptr = *(void **)((char *)sptr + elm->memb_offset);
+		if(!memb_ptr) _ASN_ENCODE_FAILED;
+	} else {
+		memb_ptr = (char *)sptr + elm->memb_offset;
+	}
+
+	return elm->type->uper_encoder(elm->type, elm->per_constraints,
+			memb_ptr, po);
+}
+   
 
 int
 CHOICE_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
diff --git a/skeletons/constr_CHOICE.h b/skeletons/constr_CHOICE.h
index 6276d7e..83404e6 100644
--- a/skeletons/constr_CHOICE.h
+++ b/skeletons/constr_CHOICE.h
@@ -47,6 +47,7 @@
 xer_type_decoder_f CHOICE_decode_xer;
 xer_type_encoder_f CHOICE_encode_xer;
 per_type_decoder_f CHOICE_decode_uper;
+per_type_encoder_f CHOICE_encode_uper;
 asn_outmost_tag_f CHOICE_outmost_tag;
 
 #ifdef __cplusplus
diff --git a/skeletons/constr_SEQUENCE.c b/skeletons/constr_SEQUENCE.c
index d38afb5..5b24007 100644
--- a/skeletons/constr_SEQUENCE.c
+++ b/skeletons/constr_SEQUENCE.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003, 2004, 2005 Lev Walkin <vlm@lionet.info>.
+ * Copyright (c) 2003, 2004, 2005, 2006 Lev Walkin <vlm@lionet.info>.
  * All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
@@ -1097,7 +1097,7 @@
 				/* This element is not present */
 				if(elm->default_value) {
 					/* Fill-in DEFAULT */
-					if(elm->default_value(memb_ptr2)) {
+					if(elm->default_value(1, memb_ptr2)) {
 						FREEMEM(opres);
 						_ASN_DECODE_FAILED;
 					}
@@ -1137,14 +1137,15 @@
 
 			/* Fetch the pointer to this member */
 			if(elm->flags & ATF_POINTER) {
-				memb_ptr2 = (void **)((char *)st + elm->memb_offset);
+				memb_ptr2 = (void **)((char *)st
+						+ elm->memb_offset);
 			} else {
 				memb_ptr = (char *)st + elm->memb_offset;
 				memb_ptr2 = &memb_ptr;
 			}
 
 			/* Set default value */
-			if(elm->default_value(memb_ptr2)) {
+			if(elm->default_value(1, memb_ptr2)) {
 				FREEMEM(opres);
 				_ASN_DECODE_FAILED;
 			}
@@ -1157,3 +1158,92 @@
 	return rv;
 }
 
+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 edx;
+	int i;
+
+	(void)constraints;
+
+	if(!sptr)
+		_ASN_ENCODE_FAILED;
+
+	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 */
+
+	/* Encode a presence bitmap */
+	for(i = 0; i < specs->roms_count; i++) {
+		void *memb_ptr;		/* Pointer to the member */
+		void **memb_ptr2;	/* Pointer to that pointer */
+		edx = specs->oms[i];
+		asn_TYPE_member_t *elm = &td->elements[edx];
+		int present;
+
+		/* 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;
+		}
+
+		/* Eliminate default values */
+		if(present && elm->default_value
+		&& elm->default_value(0, memb_ptr2) == 1)
+			present = 0;
+
+		ASN_DEBUG("Element %s %s %s->%s is %s",
+			elm->flags & ATF_POINTER ? "ptr" : "inline",
+			elm->default_value ? "def" : "wtv",
+			td->name, elm->name, present ? "present" : "absent");
+		if(per_put_few_bits(po, present, 1))
+			_ASN_ENCODE_FAILED;
+	}
+
+	/*
+	 * Get the sequence ROOT elements.
+	 */
+	for(edx = 0; edx < ((specs->ext_before < 0)
+			? td->elements_count : specs->ext_before + 1); edx++) {
+		asn_TYPE_member_t *elm = &td->elements[edx];
+		void *memb_ptr;		/* Pointer to the member */
+		void **memb_ptr2;	/* Pointer to that pointer */
+
+		/* Fetch the pointer to this member */
+		if(elm->flags & ATF_POINTER) {
+			memb_ptr2 = (void **)((char *)sptr + elm->memb_offset);
+			if(!*memb_ptr2) {
+				ASN_DEBUG("Element %s %d not present",
+					elm->name, edx);
+				if(elm->optional)
+					continue;
+				/* Mandatory element is missing */
+				_ASN_ENCODE_FAILED;
+			}
+		} else {
+			memb_ptr = (void *)((char *)sptr + elm->memb_offset);
+			memb_ptr2 = &memb_ptr;
+		}
+
+		/* Eliminate default values */
+		if(elm->default_value && elm->default_value(0, memb_ptr2) == 1)
+			continue;
+
+		er = elm->type->uper_encoder(elm->type, elm->per_constraints,
+			*memb_ptr2, po);
+		if(er.encoded == -1)
+			return er;
+	}
+
+	_ASN_ENCODED_OK(er);
+}
+
diff --git a/skeletons/constr_SEQUENCE.h b/skeletons/constr_SEQUENCE.h
index b4ee47f..5f589d5 100644
--- a/skeletons/constr_SEQUENCE.h
+++ b/skeletons/constr_SEQUENCE.h
@@ -51,6 +51,7 @@
 xer_type_decoder_f SEQUENCE_decode_xer;
 xer_type_encoder_f SEQUENCE_encode_xer;
 per_type_decoder_f SEQUENCE_decode_uper;
+per_type_encoder_f SEQUENCE_encode_uper;
 
 #ifdef __cplusplus
 }
diff --git a/skeletons/constr_SEQUENCE_OF.c b/skeletons/constr_SEQUENCE_OF.c
index 3ddd5a3..aa10117 100644
--- a/skeletons/constr_SEQUENCE_OF.c
+++ b/skeletons/constr_SEQUENCE_OF.c
@@ -1,5 +1,6 @@
 /*-
- * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2003, 2004, 2006 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 #include <asn_internal.h>
@@ -139,3 +140,69 @@
 	_ASN_ENCODE_FAILED;
 }
 
+asn_enc_rval_t
+SEQUENCE_OF_encode_uper(asn_TYPE_descriptor_t *td,
+	asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
+	asn_anonymous_sequence_ *list;
+	asn_per_constraint_t *ct;
+	asn_enc_rval_t er;
+	asn_TYPE_member_t *elm = td->elements;
+	int seq;
+
+	if(!sptr) _ASN_ENCODE_FAILED;
+	list = _A_SEQUENCE_FROM_VOID(sptr);
+
+	er.encoded = 0;
+
+	ASN_DEBUG("Encoding %s as SEQUENCE OF (%d)", td->name, list->count);
+
+	if(constraints) ct = &constraints->size;
+	else if(td->per_constraints) ct = &td->per_constraints->size;
+	else ct = 0;
+
+	/* If extensible constraint, check if size is in root */
+	if(ct) {
+		int not_in_root = (list->count < ct->lower_bound
+				|| list->count > ct->upper_bound);
+		ASN_DEBUG("lb %ld ub %ld %s",
+			ct->lower_bound, ct->upper_bound,
+			ct->flags & APC_EXTENSIBLE ? "ext" : "fix");
+		if(ct->flags & APC_EXTENSIBLE) {
+			/* Declare whether size is in extension root */
+			if(per_put_few_bits(po, not_in_root, 1))
+				_ASN_ENCODE_FAILED;
+			if(not_in_root) ct = 0;
+		} else if(not_in_root && ct->effective_bits >= 0)
+			_ASN_ENCODE_FAILED;
+	}
+
+	if(ct && ct->effective_bits >= 0) {
+		/* X.691, #19.5: No length determinant */
+		if(per_put_few_bits(po, list->count - ct->lower_bound,
+				ct->effective_bits))
+			_ASN_ENCODE_FAILED;
+	}
+
+	for(seq = -1; seq < list->count;) {
+		ssize_t mayEncode;
+		if(seq < 0) seq = 0;
+		if(ct && ct->effective_bits >= 0) {
+			mayEncode = list->count;
+		} else {
+			mayEncode = uper_put_length(po, list->count - seq);
+			if(mayEncode < 0) _ASN_ENCODE_FAILED;
+		}
+
+		while(mayEncode--) {
+			void *memb_ptr = list->array[seq++];
+			if(!memb_ptr) _ASN_ENCODE_FAILED;
+			er = elm->type->uper_encoder(elm->type,
+				elm->per_constraints, memb_ptr, po);
+			if(er.encoded == -1)
+				_ASN_ENCODE_FAILED;
+		}
+	}
+
+	_ASN_ENCODED_OK(er);
+}
+
diff --git a/skeletons/constr_SEQUENCE_OF.h b/skeletons/constr_SEQUENCE_OF.h
index 32078cb..e2272f3 100644
--- a/skeletons/constr_SEQUENCE_OF.h
+++ b/skeletons/constr_SEQUENCE_OF.h
@@ -24,6 +24,7 @@
 #define	SEQUENCE_OF_decode_uper	SET_OF_decode_uper
 der_type_encoder_f SEQUENCE_OF_encode_der;
 xer_type_encoder_f SEQUENCE_OF_encode_xer;
+per_type_encoder_f SEQUENCE_OF_encode_uper;
 
 #ifdef __cplusplus
 }
diff --git a/skeletons/constr_SET_OF.h b/skeletons/constr_SET_OF.h
index 1420931..bcd0966 100644
--- a/skeletons/constr_SET_OF.h
+++ b/skeletons/constr_SET_OF.h
@@ -33,6 +33,7 @@
 xer_type_decoder_f SET_OF_decode_xer;
 xer_type_encoder_f SET_OF_encode_xer;
 per_type_decoder_f SET_OF_decode_uper;
+per_type_encoder_f SET_OF_encode_uper;
 
 #ifdef __cplusplus
 }
diff --git a/skeletons/constr_TYPE.h b/skeletons/constr_TYPE.h
index 70e7882..95507c8 100644
--- a/skeletons/constr_TYPE.h
+++ b/skeletons/constr_TYPE.h
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003, 2004, 2005 Lev Walkin <vlm@lionet.info>.
+ * Copyright (c) 2003, 2004, 2005, 2006 Lev Walkin <vlm@lionet.info>.
  * All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
@@ -40,6 +40,7 @@
 #include <xer_decoder.h>	/* Decoder of XER (XML, text) */
 #include <xer_encoder.h>	/* Encoder into XER (XML, text) */
 #include <per_decoder.h>	/* Packet Encoding Rules decoder */
+#include <per_encoder.h>	/* Packet Encoding Rules encoder */
 #include <constraints.h>	/* Subtype constraints support */
 
 /*
@@ -97,6 +98,7 @@
 	xer_type_decoder_f *xer_decoder;	/* Generic XER decoder */
 	xer_type_encoder_f *xer_encoder;	/* [Canonical] XER encoder */
 	per_type_decoder_f *uper_decoder;	/* Unaligned PER decoder */
+	per_type_encoder_f *uper_encoder;	/* Unaligned PER encoder */
 
 	/***********************************************************************
 	 * Internally useful members. Not to be used by applications directly. *
@@ -144,7 +146,7 @@
 	asn_TYPE_descriptor_t *type;	/* Member type descriptor */
 	asn_constr_check_f *memb_constraints;	/* Constraints validator */
 	asn_per_constraints_t *per_constraints;	/* PER compiled constraints */
-	int (*default_value)(void **sptr);	/* DEFAULT <value> */
+	int (*default_value)(int setval, void **sptr);	/* DEFAULT <value> */
 	char *name;			/* ASN.1 identifier of the element */
 } asn_TYPE_member_t;
 
diff --git a/skeletons/der_encoder.c b/skeletons/der_encoder.c
index 68dd24e..6c859e1 100644
--- a/skeletons/der_encoder.c
+++ b/skeletons/der_encoder.c
@@ -52,20 +52,19 @@
  */
 asn_enc_rval_t
 der_encode_to_buffer(asn_TYPE_descriptor_t *type_descriptor, void *struct_ptr,
-	void *buffer, size_t *buffer_size) {
+	void *buffer, size_t buffer_size) {
 	enc_to_buf_arg arg;
 	asn_enc_rval_t ec;
 
 	arg.buffer = buffer;
-	arg.left = *buffer_size;
+	arg.left = buffer_size;
 
 	ec = type_descriptor->der_encoder(type_descriptor,
 		struct_ptr,	/* Pointer to the destination structure */
 		0, 0, encode_to_buffer_cb, &arg);
 	if(ec.encoded != -1) {
-		assert(ec.encoded == (ssize_t)(*buffer_size - arg.left));
+		assert(ec.encoded == (ssize_t)(buffer_size - arg.left));
 		/* Return the encoded contents size */
-		*buffer_size = ec.encoded;
 	}
 	return ec;
 }
diff --git a/skeletons/der_encoder.h b/skeletons/der_encoder.h
index 9e7867d..4e2fb06 100644
--- a/skeletons/der_encoder.h
+++ b/skeletons/der_encoder.h
@@ -27,7 +27,7 @@
 		struct asn_TYPE_descriptor_s *type_descriptor,
 		void *struct_ptr,	/* Structure to be encoded */
 		void *buffer,		/* Pre-allocated buffer */
-		size_t *buffer_size	/* Initial buffer size (max) */
+		size_t buffer_size	/* Initial buffer size (maximum) */
 	);
 
 /*
diff --git a/skeletons/file-dependencies b/skeletons/file-dependencies
index ca43363..972eea4 100644
--- a/skeletons/file-dependencies
+++ b/skeletons/file-dependencies
@@ -60,6 +60,7 @@
 xer_encoder.h xer_encoder.c	# XER encoding support
 per_support.h per_support.c	# PER parsing
 per_decoder.h per_decoder.c	# PER decoding support
+per_encoder.h per_encoder.c	# PER encoding support
 #asn-decoder-template.c		# Template for quick decoder creation
 
 CODEC-PER:			# THIS IS A SPECIAL SECTION
diff --git a/skeletons/per_encoder.c b/skeletons/per_encoder.c
new file mode 100644
index 0000000..6ba28e5
--- /dev/null
+++ b/skeletons/per_encoder.c
@@ -0,0 +1,89 @@
+#include <asn_application.h>
+#include <asn_internal.h>
+#include <per_encoder.h>
+
+/* Flush partially filled buffer */
+static int _uper_encode_flush_outp(asn_per_outp_t *po);
+
+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;
+
+	er = td->uper_encoder(td, 0, sptr, &po);
+	if(er.encoded != -1 && _uper_encode_flush_outp(&po))
+		_ASN_ENCODE_FAILED;
+
+	return er;
+}
+
+/*
+ * Argument type and callback necessary for uper_encode_to_buffer().
+ */
+typedef struct enc_to_buf_arg {
+	void *buffer;
+	size_t left;
+} enc_to_buf_arg;
+static int encode_to_buffer_cb(const void *buffer, size_t size, void *key) {
+	enc_to_buf_arg *arg = (enc_to_buf_arg *)key;
+
+	if(arg->left < size)
+		return -1;	/* Data exceeds the available buffer size */
+
+	memcpy(arg->buffer, buffer, size);
+	arg->buffer = ((char *)arg->buffer) + size;
+	arg->left -= size;
+
+	return 0;
+}
+
+asn_enc_rval_t
+uper_encode_to_buffer(asn_TYPE_descriptor_t *td, void *sptr, void *buffer, size_t buffer_size) {
+	enc_to_buf_arg key;
+	asn_enc_rval_t er;
+
+	/*
+	 * 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);
+
+	er = uper_encode(td, sptr, encode_to_buffer_cb, &key);
+	if(er.encoded != -1)
+		er.encoded = buffer_size - key.left;
+	return er;
+}
+
+static int
+_uper_encode_flush_outp(asn_per_outp_t *po) {
+	uint8_t *buf;
+
+	if(po->nboff == 0 && po->buffer == po->tmpspace)
+		return 0;
+
+	buf = po->buffer + (po->nboff >> 3);
+	/* Make sure we account for the last, partially filled */
+	if(po->nboff & 0x07) {
+		buf[0] &= 0xff << (8 - (po->nboff & 0x07));
+		buf++;
+	}
+
+	return po->outper(po->tmpspace, buf - po->tmpspace, po->op_key);
+}
diff --git a/skeletons/per_encoder.h b/skeletons/per_encoder.h
new file mode 100644
index 0000000..9ac130b
--- /dev/null
+++ b/skeletons/per_encoder.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2006 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_PER_ENCODER_H_
+#define	_PER_ENCODER_H_
+
+#include <asn_application.h>
+#include <per_support.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct asn_TYPE_descriptor_s;	/* Forward declaration */
+
+/*
+ * Unaligned PER encoder of any ASN.1 type. May be invoked by the application.
+ */
+asn_enc_rval_t uper_encode(struct asn_TYPE_descriptor_s *type_descriptor,
+	void *struct_ptr,	/* Structure to be encoded */
+	asn_app_consume_bytes_f *consume_bytes_cb,	/* Data collector */
+	void *app_key		/* Arbitrary callback argument */
+);
+
+/* A variant of uper_encode() which encodes data into the existing buffer */
+asn_enc_rval_t uper_encode_to_buffer(
+	struct asn_TYPE_descriptor_s *type_descriptor,
+	void *struct_ptr,	/* Structure to be encoded */
+	void *buffer,		/* Pre-allocated buffer */
+	size_t buffer_size	/* Initial buffer size (max) */
+);
+
+
+/*
+ * Type of the generic PER encoder function.
+ */
+typedef asn_enc_rval_t (per_type_encoder_f)(
+	struct asn_TYPE_descriptor_s *type_descriptor,
+	asn_per_constraints_t *constraints,
+	void *struct_ptr,
+	asn_per_outp_t *per_output
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _PER_ENCODER_H_ */
diff --git a/skeletons/per_support.c b/skeletons/per_support.c
index 63b39d0..0eb381c 100644
--- a/skeletons/per_support.c
+++ b/skeletons/per_support.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2005, 2006 Lev Walkin <vlm@lionet.info>. All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 #include <asn_system.h>
@@ -80,7 +80,7 @@
 		} else {
 			value = per_get_few_bits(pd, nbits);
 			if(value < 0) return -1;
-			if(nbits & 7) {	/* implies alright */
+			if(nbits & 7) {	/* implies left alignment */
 				value <<= 8 - (nbits & 7),
 				nbits += 8 - (nbits & 7);
 				if(nbits > 24)
@@ -150,3 +150,161 @@
 
 	return value;
 }
+
+/*
+ * Put the normally small non-negative whole number.
+ * X.691, #10.6
+ */
+int
+uper_put_nsnnwn(asn_per_outp_t *po, int n) {
+	int bytes;
+
+	if(n <= 63) {
+		if(n < 0) return -1;
+		return per_put_few_bits(po, n, 7);
+	}
+	if(n < 256)
+		bytes = 1;
+	else if(n < 65536)
+		bytes = 2;
+	else if(n < 256 * 65536)
+		bytes = 3;
+	else
+		return -1;	/* This is not a "normally small" value */
+	if(per_put_few_bits(po, bytes, 8))
+		return -1;
+
+	return per_put_few_bits(po, n, 8 * bytes);
+}
+
+
+/*
+ * Put a small number of bits (<= 31).
+ */
+int
+per_put_few_bits(asn_per_outp_t *po, uint32_t bits, int obits) {
+	size_t off;	/* Next after last bit offset */
+	size_t omsk;	/* Existing last byte meaningful bits mask */
+	uint8_t *buf;
+
+	if(obits <= 0 || obits >= 32) return obits ? -1 : 0;
+
+	/*
+	 * Normalize position indicator.
+	 */
+	if(po->nboff >= 8) {
+		po->buffer += (po->nboff >> 3);
+		po->nbits  -= (po->nboff & ~0x07);
+		po->nboff  &= 0x07;
+	}
+
+	/*
+	 * Flush whole-bytes output, if necessary.
+	 */
+	if(po->nboff + obits > po->nbits) {
+		int complete_bytes = (po->buffer - po->tmpspace);
+		if(po->outper(po->buffer, complete_bytes, po->op_key) < 0)
+			return -1;
+		if(po->nboff)
+			po->tmpspace[0] = po->buffer[0];
+		po->buffer = po->tmpspace;
+		po->nbits = 8 * sizeof(po->tmpspace);
+	}
+
+	/*
+	 * Now, due to sizeof(tmpspace), we are guaranteed large enough space.
+	 */
+	buf = po->buffer;
+	omsk = ~((1 << (8 - po->nboff)) - 1);
+	off = (po->nboff += obits);
+
+	/* Clear data of debris before meaningful bits */
+	bits &= (((uint32_t)1 << obits) - 1);
+
+	ASN_DEBUG("[PER out %d %u/%x (t=%d,o=%d) %x&%x=%x]", obits, bits, bits,
+		po->nboff - obits, off, buf[0], omsk&0xff, buf[0] & omsk);
+
+	if(off <= 8)	/* Completely within 1 byte */
+		bits <<= (8 - off),
+		buf[0] = (buf[0] & omsk) | bits;
+	else if(off <= 16)
+		bits <<= (16 - off),
+		buf[0] = (buf[0] & omsk) | (bits >> 8),
+		buf[1] = bits;
+	else if(off <= 24)
+		bits <<= (24 - off),
+		buf[0] = (buf[0] & omsk) | (bits >> 16),
+		buf[1] = bits >> 8,
+		buf[2] = bits;
+	else if(off <= 31)
+		bits <<= (32 - off),
+		buf[0] = (buf[0] & omsk) | (bits >> 24),
+		buf[1] = bits >> 16,
+		buf[2] = bits >> 8,
+		buf[3] = bits;
+	else {
+		ASN_DEBUG("->[PER out split %d]", obits);
+		per_put_few_bits(po, bits >> 8, 24);
+		per_put_few_bits(po, bits, obits - 24);
+		ASN_DEBUG("<-[PER out split %d]", obits);
+	}
+
+	ASN_DEBUG("[PER out %u/%x => %02x buf+%d]",
+		bits, bits, buf[0], po->buffer - po->tmpspace);
+
+	return 0;
+}
+
+
+/*
+ * Output a large number of bits.
+ */
+int
+per_put_many_bits(asn_per_outp_t *po, const uint8_t *src, int nbits) {
+
+	while(nbits) {
+		uint32_t value;
+
+		if(nbits >= 24) {
+			value = (src[0] << 16) | (src[1] << 8) | src[2];
+			src += 3;
+			nbits -= 24;
+			if(per_put_few_bits(po, value, 24))
+				return -1;
+		} else {
+			value = src[0];
+			if(nbits > 8)
+				value = (value << 8) | src[1];
+			if(nbits > 16)
+				value = (value << 8) | src[2];
+			if(nbits & 0x07)
+				value >>= (8 - (nbits & 0x07));
+			if(per_put_few_bits(po, value, nbits))
+				return -1;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Put the length "n" (or part of it) into the stream.
+ */
+ssize_t
+uper_put_length(asn_per_outp_t *po, size_t length) {
+
+	if(length <= 127)	/* #10.9.3.6 */
+		return per_put_few_bits(po, length, 8)
+			? -1 : (ssize_t)length;
+	else if(length < 16384)	/* #10.9.3.7 */
+		return per_put_few_bits(po, length|0x8000, 16)
+			? -1 : (ssize_t)length;
+
+	length >>= 14;
+	if(length > 4) length = 4;
+
+	return per_put_few_bits(po, 0xC0 | length, 8)
+			? -1 : (ssize_t)(length << 14);
+}
+
diff --git a/skeletons/per_support.h b/skeletons/per_support.h
index 44f0bb4..747abe2 100644
--- a/skeletons/per_support.h
+++ b/skeletons/per_support.h
@@ -12,7 +12,7 @@
 #endif
 
 /*
- * This structure describes a position inside a PER bit stream.
+ * This structure describes a position inside an incoming PER bit stream.
  */
 typedef struct asn_per_data_s {
  const uint8_t *buffer;	/* Pointer to the octet stream */
@@ -21,12 +21,27 @@
 } asn_per_data_t;
 
 /*
+ * This structure supports forming PER output.
+ */
+typedef struct asn_per_outp_s {
+	uint8_t *buffer;	/* Pointer into the (tmpspace) */
+	size_t nboff;		/* Bit offset to the meaningful bit */
+	size_t nbits;		/* Number of bits left in (tmpspace) */
+	uint8_t tmpspace[32];	/* Preliminary storage to hold data */
+	int (*outper)(const void *data, size_t size, void *op_key);
+	void *op_key;
+} asn_per_outp_t;
+
+/*
  * Extract a small number of bits (<= 31) from the specified PER data pointer.
  * This function returns -1 if the specified number of bits could not be
  * extracted due to EOD or other conditions.
  */
 int32_t per_get_few_bits(asn_per_data_t *per_data, int get_nbits);
 
+/* Output a small number of bits (<= 31) */
+int per_put_few_bits(asn_per_outp_t *per_data, uint32_t bits, int obits);
+
 /*
  * Extract a large number of bits from the specified PER data pointer.
  * This function returns -1 if the specified number of bits could not be
@@ -35,6 +50,9 @@
 int per_get_many_bits(asn_per_data_t *pd, uint8_t *dst, int right_align,
 			int get_nbits);
 
+/* Output a large number of bits */
+int per_put_many_bits(asn_per_outp_t *po, const uint8_t *src, int put_nbits);
+
 /*
  * Get the length "n" from the Unaligned PER stream.
  */
@@ -43,10 +61,22 @@
 			int *repeat);
 
 /*
+ * Put the length "n" to the Unaligned PER stream.
+ * This function returns the number of units which may be flushed
+ * in the next units saving iteration.
+ */
+ssize_t uper_put_length(asn_per_outp_t *po, size_t whole_length);
+
+/*
  * Get the normally small non-negative whole number.
  */
 ssize_t uper_get_nsnnwn(asn_per_data_t *pd);
 
+/*
+ * Put the normally small non-negative whole number.
+ */
+int uper_put_nsnnwn(asn_per_outp_t *po, int n);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/skeletons/tests/check-PER.c b/skeletons/tests/check-PER.c
index 5fcb1ae..6d355e8 100644
--- a/skeletons/tests/check-PER.c
+++ b/skeletons/tests/check-PER.c
@@ -1,8 +1,8 @@
 #include <per_support.c>
 #include <assert.h>
 
-int
-main() {
+static void
+check_per_decoding() {
 	uint8_t buf[] = { 0xB7, 0x19, 0x2F, 0xEE, 0xAD };
 	uint8_t tmpbuf[10];
 	int32_t z;
@@ -119,6 +119,102 @@
 	assert(tmpbuf[2] == buf[2]);
 	assert(tmpbuf[3] == buf[3]);
 	assert(tmpbuf[4] == buf[4]);
+}
 
+static int Ignore(const void *data, size_t size, void *op_key) {
+	return 0;
+}
+
+static void
+check_per_encoding() {
+	asn_per_outp_t po;
+	po.buffer = po.tmpspace;
+	po.nboff = 0;
+	po.nbits = 0;
+	po.outper = Ignore;
+	po.op_key = 0;
+	po.tmpspace[0] = 0xff;
+	int ret;
+
+	ret = per_put_few_bits(&po, 0, 0);
+	assert(ret == 0);
+	assert(po.nboff == 0);
+	assert(po.buffer == po.tmpspace);
+	assert(po.tmpspace[0] == 0xff);
+
+	ret = per_put_few_bits(&po, 0, 1);
+	assert(ret == 0);
+	assert(po.nboff == 1);
+	assert(po.nbits == 8 * sizeof(po.tmpspace));
+	assert(po.buffer == po.tmpspace);
+	assert(po.tmpspace[0] == 0x00);
+
+	ret = per_put_few_bits(&po, 1, 1);
+	assert(ret == 0);
+	assert(po.nboff == 2);
+	assert(po.nbits == 8 * sizeof(po.tmpspace));
+	assert(po.buffer == po.tmpspace);
+	assert(po.tmpspace[0] == 0x40);
+
+	ret = per_put_few_bits(&po, 1, 1);
+	assert(ret == 0);
+	assert(po.nboff == 3);
+	assert(po.nbits == 8 * sizeof(po.tmpspace));
+	assert(po.buffer == po.tmpspace);
+	assert(po.tmpspace[0] == 0x60);
+
+	ret = per_put_few_bits(&po, 15, 5);
+	assert(ret == 0);
+	assert(po.nboff == 8);
+	assert(po.nbits == 8 * sizeof(po.tmpspace));
+	assert(po.buffer == po.tmpspace);
+	assert(po.tmpspace[0] == 0x6F);
+
+	ret = per_put_few_bits(&po, 0xf0ff, 16);
+	assert(ret == 0);
+	assert(po.nboff == 16);
+	assert(po.nbits == 8 * sizeof(po.tmpspace) - 8);
+	assert(po.buffer == po.tmpspace + 1);
+	assert(po.tmpspace[0] == 0x6F);
+	assert(po.tmpspace[1] == 0xf0);
+	assert(po.tmpspace[2] == 0xff);
+
+	po.nboff--;
+
+	ret = per_put_few_bits(&po, 2, 1);
+	assert(ret == 0);
+	assert(po.nboff == 8);
+	assert(po.nbits == 8 * sizeof(po.tmpspace) - 16);
+	assert(po.buffer == po.tmpspace + 2);
+	assert(po.tmpspace[0] == 0x6F);
+	assert(po.tmpspace[1] == 0xf0);
+	assert(po.tmpspace[2] == 0xfe);
+
+	ret = per_put_few_bits(&po, 2, 32);
+	assert(ret == -1);
+
+	ret = per_put_few_bits(&po, 2, -1);
+	assert(ret == -1);
+
+	ret = per_put_few_bits(&po, -1, 31);
+	assert(ret == 0);
+	assert(po.nboff == 31);
+	assert(po.nbits == 8 * sizeof(po.tmpspace) - 24);
+	assert(po.buffer == po.tmpspace + 3);
+	assert(po.tmpspace[0] == 0x6F);
+	assert(po.tmpspace[1] == 0xf0);
+	assert(po.tmpspace[2] == 0xfe);
+	assert(po.tmpspace[3] == 0xff);
+	assert(po.tmpspace[4] == 0xff);
+	assert(po.tmpspace[5] == 0xff);
+	assert(po.tmpspace[6] == 0xfe);
+
+
+}
+
+int
+main() {
+	check_per_decoding();
+	check_per_encoding();
 	return 0;
 }