per encoding support

diff --git a/skeletons/INTEGER.c b/skeletons/INTEGER.c
index 5a0ecce..9c8b9ed 100644
--- a/skeletons/INTEGER.c
+++ b/skeletons/INTEGER.c
@@ -571,7 +571,7 @@
 
 	if(ct && ct->flags & APC_EXTENSIBLE) {
 		int inext = per_get_few_bits(pd, 1);
-		if(inext < 0) _ASN_DECODE_FAILED;
+		if(inext < 0) _ASN_DECODE_STARVED;
 		if(inext) ct = 0;
 	}
 
@@ -599,7 +599,7 @@
 		ASN_DEBUG("Integer with range %d bits", ct->range_bits);
 		if(ct->range_bits >= 0) {
 			long value = per_get_few_bits(pd, ct->range_bits);
-			if(value < 0) _ASN_DECODE_FAILED;
+			if(value < 0) _ASN_DECODE_STARVED;
 			ASN_DEBUG("Got value %ld + low %ld",
 				value, ct->lower_bound);
 			value += ct->lower_bound;
@@ -619,14 +619,14 @@
 
 		/* Get the PER length */
 		len = uper_get_length(pd, -1, &repeat);
-		if(len < 0) _ASN_DECODE_FAILED;
+		if(len < 0) _ASN_DECODE_STARVED;
 
 		p = REALLOC(st->buf, st->size + len + 1);
 		if(!p) _ASN_DECODE_FAILED;
 		st->buf = (uint8_t *)p;
 
 		ret = per_get_many_bits(pd, &st->buf[st->size], 0, 8 * len);
-		if(ret < 0) _ASN_DECODE_FAILED;
+		if(ret < 0) _ASN_DECODE_STARVED;
 		st->size += len;
 	} while(repeat);
 	st->buf[st->size] = 0;	/* JIC */
diff --git a/skeletons/NativeEnumerated.c b/skeletons/NativeEnumerated.c
index fa16eb0..e3af1ca 100644
--- a/skeletons/NativeEnumerated.c
+++ b/skeletons/NativeEnumerated.c
@@ -96,13 +96,13 @@
 
 	if(ct->flags & APC_EXTENSIBLE) {
 		int inext = per_get_few_bits(pd, 1);
-		if(inext < 0) _ASN_DECODE_FAILED;
+		if(inext < 0) _ASN_DECODE_STARVED;
 		if(inext) ct = 0;
 	}
 
 	if(ct && ct->range_bits >= 0) {
 		value = per_get_few_bits(pd, ct->range_bits);
-		if(value < 0) _ASN_DECODE_FAILED;
+		if(value < 0) _ASN_DECODE_STARVED;
 		if(value >= (specs->extension
 			? specs->extension - 1 : specs->map_count))
 			_ASN_DECODE_FAILED;
@@ -113,7 +113,7 @@
 		 * X.691, #10.6: normally small non-negative whole number;
 		 */
 		value = uper_get_nsnnwn(pd);
-		if(value < 0) _ASN_DECODE_FAILED;
+		if(value < 0) _ASN_DECODE_STARVED;
 		value += specs->extension - 1;
 		if(value >= specs->map_count)
 			_ASN_DECODE_FAILED;
diff --git a/skeletons/OCTET_STRING.c b/skeletons/OCTET_STRING.c
index 7b83b61..3bef560 100644
--- a/skeletons/OCTET_STRING.c
+++ b/skeletons/OCTET_STRING.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.
  */
@@ -1231,7 +1231,7 @@
 
 	if(ct->flags & APC_EXTENSIBLE) {
 		int inext = per_get_few_bits(pd, 1);
-		if(inext < 0) RETURN(RC_FAIL);
+		if(inext < 0) RETURN(RC_WMORE);
 		if(inext) ct = &asn_DEF_OCTET_STRING_constraint;
 		consumed_myself = 0;
 	}
@@ -1254,7 +1254,7 @@
 	if(ct->effective_bits == 0) {
 		int ret = per_get_many_bits(pd, st->buf, 0,
 					    unit_bits * ct->upper_bound);
-		if(ret < 0) RETURN(RC_FAIL);
+		if(ret < 0) RETURN(RC_WMORE);
 		consumed_myself += unit_bits * ct->upper_bound;
 		st->buf[st->size] = 0;
 		if(unit_bits == 1 && (ct->upper_bound & 0x7))
@@ -1271,11 +1271,12 @@
 
 		/* Get the PER length */
 		len_bits = uper_get_length(pd, ct->effective_bits, &repeat);
-		if(len_bits < 0) RETURN(RC_FAIL);
+		if(len_bits < 0) RETURN(RC_WMORE);
 		len_bits += ct->lower_bound;
 
-		ASN_DEBUG("Got per length eb %ld, len %ld",
-			(long)ct->effective_bits, (long)len_bits);
+		ASN_DEBUG("Got PER length eb %ld, len %ld, %s (%s)",
+			(long)ct->effective_bits, (long)len_bits,
+			repeat ? "repeat" : "once", td->name);
 		if(unit_bits == 1) {
 			len_bytes = (len_bits + 7) >> 3;
 			if(len_bits & 0x7)
@@ -1290,7 +1291,7 @@
 		st->buf = (uint8_t *)p;
 
 		ret = per_get_many_bits(pd, &st->buf[st->size], 0, len_bits);
-		if(ret < 0) RETURN(RC_FAIL);
+		if(ret < 0) RETURN(RC_WMORE);
 		st->size += len_bytes;
 	} while(repeat);
 	st->buf[st->size] = 0;	/* nul-terminate */
@@ -1327,8 +1328,8 @@
 		sizeinunits = sizeinunits * 8 - (st->bits_unused & 0x07);
 	}
 
-	ASN_DEBUG("Encoding %s into %d units",
-		td->name, sizeinunits);
+	ASN_DEBUG("Encoding %s into %d units of %d bits",
+		td->name, sizeinunits, unit_bits);
 
 	/* Figure out wheter size lies within PER visible consrtaint */
 
@@ -1361,7 +1362,7 @@
 		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);
+		ret = per_put_many_bits(po, st->buf, sizeinunits * unit_bits);
 		if(ret) _ASN_ENCODE_FAILED;
 		_ASN_ENCODED_OK(er);
 	}
@@ -1381,7 +1382,7 @@
 
 		ASN_DEBUG("Encoding %d of %d", maySave, sizeinunits);
 
-		ret = per_put_many_bits(po, buf, maySave);
+		ret = per_put_many_bits(po, buf, maySave * unit_bits);
 		if(ret) _ASN_ENCODE_FAILED;
 
 		if(unit_bits == 1)
diff --git a/skeletons/asn_codecs.h b/skeletons/asn_codecs.h
index bd6b946..1ee7377 100644
--- a/skeletons/asn_codecs.h
+++ b/skeletons/asn_codecs.h
@@ -93,6 +93,12 @@
 	tmp_error.consumed = 0;					\
 	return tmp_error;					\
 } while(0)
+#define	_ASN_DECODE_STARVED do {				\
+	asn_dec_rval_t tmp_error;				\
+	tmp_error.code = RC_WMORE;				\
+	tmp_error.consumed = 0;					\
+	return tmp_error;					\
+} while(0)
 
 #ifdef __cplusplus
 }
diff --git a/skeletons/constr_CHOICE.c b/skeletons/constr_CHOICE.c
index a0a6d76..b8d6fa9 100644
--- a/skeletons/constr_CHOICE.c
+++ b/skeletons/constr_CHOICE.c
@@ -852,22 +852,22 @@
 
 	if(ct && ct->flags & APC_EXTENSIBLE) {
 		value = per_get_few_bits(pd, 1);
-		if(value < 0) _ASN_DECODE_FAILED;
+		if(value < 0) _ASN_DECODE_STARVED;
 		if(value) ct = 0;	/* Not restricted */
 	}
 
 	if(ct && ct->range_bits >= 0) {
 		value = per_get_few_bits(pd, ct->range_bits);
-		if(value < 0) _ASN_DECODE_FAILED;
-		if(value > ct->upper_bound)
-			_ASN_DECODE_FAILED;
+		if(value < 0) _ASN_DECODE_STARVED;
 		ASN_DEBUG("CHOICE %s got index %d in range %d",
 			td->name, value, ct->range_bits);
+		if(value > ct->upper_bound)
+			_ASN_DECODE_FAILED;
 	} else {
 		if(specs->ext_start == -1)
 			_ASN_DECODE_FAILED;
 		value = uper_get_nsnnwn(pd);
-		if(value < 0) _ASN_DECODE_FAILED;
+		if(value < 0) _ASN_DECODE_STARVED;
 		value += specs->ext_start;
 		if(value >= td->elements_count)
 			_ASN_DECODE_FAILED;
diff --git a/skeletons/constr_SEQUENCE.c b/skeletons/constr_SEQUENCE.c
index 9b7dcf3..b769434 100644
--- a/skeletons/constr_SEQUENCE.c
+++ b/skeletons/constr_SEQUENCE.c
@@ -1048,7 +1048,7 @@
 	/* Handle extensions */
 	if(specs->ext_before >= 0) {
 		extpresent = per_get_few_bits(pd, 1);
-		if(extpresent < 0) _ASN_DECODE_FAILED;
+		if(extpresent < 0) _ASN_DECODE_STARVED;
 	}
 
 	/* Prepare a place and read-in the presence bitmap */
@@ -1058,7 +1058,7 @@
 		/* Get the presence map */
 		if(per_get_many_bits(pd, opres, 0, specs->roms_count)) {
 			FREEMEM(opres);
-			_ASN_DECODE_FAILED;
+			_ASN_DECODE_STARVED;
 		}
 		opmd.buffer = opres;
 		opmd.nboff = 0;
diff --git a/skeletons/constr_SET_OF.c b/skeletons/constr_SET_OF.c
index 03c6fe2..09f27db 100644
--- a/skeletons/constr_SET_OF.c
+++ b/skeletons/constr_SET_OF.c
@@ -883,7 +883,7 @@
 
 	if(ct && ct->flags & APC_EXTENSIBLE) {
 		int value = per_get_few_bits(pd, 1);
-		if(value < 0) _ASN_DECODE_FAILED;
+		if(value < 0) _ASN_DECODE_STARVED;
 		if(value) ct = 0;	/* Not restricted! */
 	}
 
@@ -892,7 +892,7 @@
 		nelems = per_get_few_bits(pd, ct->effective_bits);
 		ASN_DEBUG("Preparing to fetch %ld+%ld elements from %s",
 			(long)nelems, ct->lower_bound, td->name);
-		if(nelems < 0)  _ASN_DECODE_FAILED;
+		if(nelems < 0)  _ASN_DECODE_STARVED;
 		nelems += ct->lower_bound;
 	} else {
 		nelems = -1;
@@ -905,7 +905,7 @@
 				ct ? ct->effective_bits : -1, &repeat);
 			ASN_DEBUG("Got to decode %d elements (eff %d)",
 				(int)nelems, (int)ct ? ct->effective_bits : -1);
-			if(nelems < 0) _ASN_DECODE_FAILED;
+			if(nelems < 0) _ASN_DECODE_STARVED;
 		}
 
 		for(i = 0; i < nelems; i++) {
@@ -921,12 +921,13 @@
 				ASN_DEBUG("Failed to add element into %s",
 					td->name);
 				/* Fall through */
+				rv.code == RC_FAIL;
 			} else {
 				ASN_DEBUG("Failed decoding %s of %s (SET OF)",
 					elm->type->name, td->name);
 			}
 			if(ptr) ASN_STRUCT_FREE(*elm->type, ptr);
-			_ASN_DECODE_FAILED;
+			return rv;
 		}
 
 		nelems = -1;	/* Allow uper_get_length() */
diff --git a/skeletons/per_decoder.c b/skeletons/per_decoder.c
index f1b723f..16dee36 100644
--- a/skeletons/per_decoder.c
+++ b/skeletons/per_decoder.c
@@ -3,12 +3,14 @@
 #include <per_decoder.h>
 
 asn_dec_rval_t
-uper_decode(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, void **sptr, const void *buffer, size_t size, int skip_bits) {
+uper_decode(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, void **sptr, const void *buffer, size_t size, int skip_bits, int unused_bits) {
 	asn_codec_ctx_t s_codec_ctx;
 	asn_dec_rval_t rval;
 	asn_per_data_t pd;
 
-	if(skip_bits < 0 || skip_bits > 7 || (skip_bits > 0 && !size))
+	if(skip_bits < 0 || skip_bits > 7
+	|| unused_bits < 0 || unused_bits > 7
+	|| (unused_bits > 0 && !size))
 		_ASN_DECODE_FAILED;
 
 	/*
@@ -30,7 +32,9 @@
 	/* Fill in the position indicator */
 	pd.buffer = (const uint8_t *)buffer;
 	pd.nboff = skip_bits;
-	pd.nbits = 8 * size; 	/* 8 is CHAR_BIT from <limits.h> */
+	pd.nbits = 8 * size - unused_bits; /* 8 is CHAR_BIT from <limits.h> */
+	if(pd.nboff > pd.nbits)
+		_ASN_DECODE_FAILED;
 
 	/*
 	 * Invoke type-specific decoder.
@@ -38,12 +42,13 @@
 	if(!td->uper_decoder)
 		_ASN_DECODE_FAILED;	/* PER is not compiled in */
 	rval = td->uper_decoder(opt_codec_ctx, td, 0, sptr, &pd);
-	if(rval.code == RC_FAIL) {
-		rval.consumed = 0;
-	} else {
+	if(rval.code == RC_OK) {
 		/* Return the number of consumed bits */
 		rval.consumed = ((pd.buffer - (const uint8_t *)buffer) << 3)
 					+ pd.nboff - skip_bits;
+	} else {
+		/* PER codec is not a restartable */
+		rval.consumed = 0;
 	}
 	return rval;
 }
diff --git a/skeletons/per_decoder.h b/skeletons/per_decoder.h
index 23cb7d7..26aaf59 100644
--- a/skeletons/per_decoder.h
+++ b/skeletons/per_decoder.h
@@ -22,7 +22,8 @@
 	void **struct_ptr,	/* Pointer to a target structure's pointer */
 	const void *buffer,	/* Data to be decoded */
 	size_t size,		/* Size of data buffer */
-	int skip_bits		/* Number of unused leading bits, 0..7 */
+	int skip_bits,		/* Number of unused leading bits, 0..7 */
+	int unused_bits		/* Number of unused tailing bits, 0..7 */
 	);
 
 
diff --git a/skeletons/per_support.c b/skeletons/per_support.c
index b0149f8..c834419 100644
--- a/skeletons/per_support.c
+++ b/skeletons/per_support.c
@@ -18,6 +18,9 @@
 	if(nbits < 0 || pd->nboff + nbits > pd->nbits)
 		return -1;
 
+	ASN_DEBUG("[PER get %d bits from %p+%d bits]",
+		nbits, pd->buffer, pd->nboff);
+
 	/*
 	 * Normalize position indicator.
 	 */
@@ -190,6 +193,9 @@
 
 	if(obits <= 0 || obits >= 32) return obits ? -1 : 0;
 
+	ASN_DEBUG("[PER put %d bits to %p+%d bits]",
+			obits, po->buffer, po->nboff);
+
 	/*
 	 * Normalize position indicator.
 	 */