more granular PER encoding

diff --git a/examples/sample.makefile.regen b/examples/sample.makefile.regen
index 95c1779..1b8f8c4 100755
--- a/examples/sample.makefile.regen
+++ b/examples/sample.makefile.regen
@@ -60,27 +60,39 @@
 	echo "	@if test -f ./sample-${ASN1PDU}-1.[db]er ; then \\"
 	echo "	for f in ./sample-${ASN1PDU}-*.[db]er; do \\"
 	echo '	echo "Recoding $$f into XER and back..."; \'
-	echo '	./${TARGET} -iber -oxer $$f > ./.tmp.1.$$ || exit 2; \'
-	echo '	./${TARGET} -ixer -oxer ./.tmp.1.$$ > ./.tmp.2.$$ || exit 2; \'
-	echo '	diff ./.tmp.1.$$ ./.tmp.2.$$ || exit 2; \'
-	echo '	rm -f ./.tmp.[12].$$; \'
+	echo '	./${TARGET} -iber -oxer -b 1 $$f > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	./${TARGET} -iber -oxer -b 17 $$f > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	./${TARGET} -iber -oxer -b 33 $$f > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	./${TARGET} -iber -oxer -b 980 $$f > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	./${TARGET} -iber -oxer $$f > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	./${TARGET} -ixer -oxer ./.tmp.1.$$$$ > ./.tmp.2.$$$$ || exit 2; \'
+	echo '	diff ./.tmp.1.$$$$ ./.tmp.2.$$$$ || exit 2; \'
+	echo '	rm -f ./.tmp.[12].$$$$; \'
 	echo '	done; fi'
 	echo "	@if test -f ./sample-${ASN1PDU}-1.xer ; then \\"
 	echo "	for f in ./sample-${ASN1PDU}-*.xer; do \\"
 	echo '	echo "Recoding $$f into DER and back..."; \'
-	echo '	./${TARGET} -ixer -oder $$f > ./.tmp.1.$$ || exit 2; \'
-	echo '	./${TARGET} -iber -oxer ./.tmp.1.$$ > ./.tmp.2.$$ || exit 2; \'
-	echo '	diff $$f ./.tmp.2.$$ || exit 2; \'
-	echo '	rm -f ./.tmp.[12].$$; \'
+	echo '	./${TARGET} -ixer -oder -b 1 $$f > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	./${TARGET} -ixer -oder -b 17 $$f > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	./${TARGET} -ixer -oder -b 33 $$f > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	./${TARGET} -ixer -oder -b 980 $$f > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	./${TARGET} -ixer -oder $$f > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	./${TARGET} -iber -oxer ./.tmp.1.$$$$ > ./.tmp.2.$$$$ || exit 2; \'
+	echo '	diff $$f ./.tmp.2.$$$$ || exit 2; \'
+	echo '	rm -f ./.tmp.[12].$$$$; \'
 	echo '	done; fi'
 	echo "	@if test -f ./sample-${ASN1PDU}-1.per ; then \\"
 	echo "	for f in ./sample-${ASN1PDU}-*.per; do \\"
 	echo '	echo "Recoding $$f into DER into XER and back..."; \'
-	echo '	./${TARGET} -iper -oder $$f > ./.tmp.1.$$ || exit 2; \'
-	echo '	./${TARGET} -ider -oxer ./.tmp.1.$$ > ./.tmp.2.$$ || exit 2; \'
-	echo '	./${TARGET} -ixer -oper ./.tmp.2.$$ > ./.tmp.1.$$ || exit 2; \'
-	echo '	diff $$f ./.tmp.1.$$ || exit 2; \'
-	echo '	rm -f ./.tmp.[12].$$; \'
+	echo '	./${TARGET} -iper -oder -b 1 $$f > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	./${TARGET} -iper -oder -b 17 $$f > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	./${TARGET} -iper -oder -b 33 $$f > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	./${TARGET} -iper -oder -b 980 $$f > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	./${TARGET} -iper -oder $$f > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	./${TARGET} -iber -oxer ./.tmp.1.$$$$ > ./.tmp.2.$$$$ || exit 2; \'
+	echo '	./${TARGET} -ixer -oper ./.tmp.2.$$$$ > ./.tmp.1.$$$$ || exit 2; \'
+	echo '	diff $$f ./.tmp.1.$$$$ || exit 2; \'
+	echo '	rm -f ./.tmp.[12].$$$$; \'
 	echo '	done; fi'
 	echo '	@echo ================'
 	echo '	@echo All tests passed'
diff --git a/examples/sample.source.MHEG5/Makefile b/examples/sample.source.MHEG5/Makefile
index 4a4f48f..db9e195 100644
--- a/examples/sample.source.MHEG5/Makefile
+++ b/examples/sample.source.MHEG5/Makefile
@@ -493,27 +493,27 @@
 	@if test -f ./sample-InterchangedObject-1.[db]er ; then \
 	for f in ./sample-InterchangedObject-*.[db]er; do \
 	echo "Recoding $$f into XER and back..."; \
-	./${TARGET} -iber -oxer $$f > ./.tmp.1.$$ || exit 2; \
-	./${TARGET} -ixer -oxer ./.tmp.1.$$ > ./.tmp.2.$$ || exit 2; \
-	diff ./.tmp.1.$$ ./.tmp.2.$$ || exit 2; \
-	rm -f ./.tmp.[12].$$; \
+	./${TARGET} -iber -oxer $$f > ./.tmp.1.$$$$ || exit 2; \
+	./${TARGET} -ixer -oxer ./.tmp.1.$$$$ > ./.tmp.2.$$$$ || exit 2; \
+	diff ./.tmp.1.$$$$ ./.tmp.2.$$$$ || exit 2; \
+	rm -f ./.tmp.[12].$$$$; \
 	done; fi
 	@if test -f ./sample-InterchangedObject-1.xer ; then \
 	for f in ./sample-InterchangedObject-*.xer; do \
 	echo "Recoding $$f into DER and back..."; \
-	./${TARGET} -ixer -oder $$f > ./.tmp.1.$$ || exit 2; \
-	./${TARGET} -iber -oxer ./.tmp.1.$$ > ./.tmp.2.$$ || exit 2; \
-	diff $$f ./.tmp.2.$$ || exit 2; \
-	rm -f ./.tmp.[12].$$; \
+	./${TARGET} -ixer -oder $$f > ./.tmp.1.$$$$ || exit 2; \
+	./${TARGET} -iber -oxer ./.tmp.1.$$$$ > ./.tmp.2.$$$$ || exit 2; \
+	diff $$f ./.tmp.2.$$$$ || exit 2; \
+	rm -f ./.tmp.[12].$$$$; \
 	done; fi
 	@if test -f ./sample-InterchangedObject-1.per ; then \
 	for f in ./sample-InterchangedObject-*.per; do \
 	echo "Recoding $$f into DER into XER and back..."; \
-	./${TARGET} -iper -oder $$f > ./.tmp.1.$$ || exit 2; \
-	./${TARGET} -ider -oxer ./.tmp.1.$$ > ./.tmp.2.$$ || exit 2; \
-	./${TARGET} -ixer -oper ./.tmp.2.$$ > ./.tmp.1.$$ || exit 2; \
-	diff $$f ./.tmp.1.$$ || exit 2; \
-	rm -f ./.tmp.[12].$$; \
+	./${TARGET} -iper -oder $$f > ./.tmp.1.$$$$ || exit 2; \
+	./${TARGET} -iber -oxer ./.tmp.1.$$$$ > ./.tmp.2.$$$$ || exit 2; \
+	./${TARGET} -ixer -oper ./.tmp.2.$$$$ > ./.tmp.1.$$$$ || exit 2; \
+	diff $$f ./.tmp.1.$$$$ || exit 2; \
+	rm -f ./.tmp.[12].$$$$; \
 	done; fi
 	@echo ================
 	@echo All tests passed
diff --git a/examples/sample.source.RRC/Makefile b/examples/sample.source.RRC/Makefile
index 862f2ca..f694fc4 100644
--- a/examples/sample.source.RRC/Makefile
+++ b/examples/sample.source.RRC/Makefile
@@ -4780,27 +4780,35 @@
 	@if test -f ./sample-DL-DCCH-Message-1.[db]er ; then \
 	for f in ./sample-DL-DCCH-Message-*.[db]er; do \
 	echo "Recoding $$f into XER and back..."; \
-	./${TARGET} -iber -oxer $$f > ./.tmp.1.$$ || exit 2; \
-	./${TARGET} -ixer -oxer ./.tmp.1.$$ > ./.tmp.2.$$ || exit 2; \
-	diff ./.tmp.1.$$ ./.tmp.2.$$ || exit 2; \
-	rm -f ./.tmp.[12].$$; \
+	./${TARGET} -iber -oxer -b 1 $$f > ./.tmp.1.$$$$ || exit 2; \
+	./${TARGET} -iber -oxer -b 17 $$f > ./.tmp.1.$$$$ || exit 2; \
+	./${TARGET} -iber -oxer -b 33 $$f > ./.tmp.1.$$$$ || exit 2; \
+	./${TARGET} -iber -oxer -b 980 $$f > ./.tmp.1.$$$$ || exit 2; \
+	./${TARGET} -iber -oxer $$f > ./.tmp.1.$$$$ || exit 2; \
+	./${TARGET} -ixer -oxer ./.tmp.1.$$$$ > ./.tmp.2.$$$$ || exit 2; \
+	diff ./.tmp.1.$$$$ ./.tmp.2.$$$$ || exit 2; \
+	rm -f ./.tmp.[12].$$$$; \
 	done; fi
 	@if test -f ./sample-DL-DCCH-Message-1.xer ; then \
 	for f in ./sample-DL-DCCH-Message-*.xer; do \
 	echo "Recoding $$f into DER and back..."; \
-	./${TARGET} -ixer -oder $$f > ./.tmp.1.$$ || exit 2; \
-	./${TARGET} -iber -oxer ./.tmp.1.$$ > ./.tmp.2.$$ || exit 2; \
-	diff $$f ./.tmp.2.$$ || exit 2; \
-	rm -f ./.tmp.[12].$$; \
+	./${TARGET} -ixer -oder -b 1 $$f > ./.tmp.1.$$$$ || exit 2; \
+	./${TARGET} -ixer -oder -b 17 $$f > ./.tmp.1.$$$$ || exit 2; \
+	./${TARGET} -ixer -oder -b 33 $$f > ./.tmp.1.$$$$ || exit 2; \
+	./${TARGET} -ixer -oder -b 980 $$f > ./.tmp.1.$$$$ || exit 2; \
+	./${TARGET} -ixer -oder $$f > ./.tmp.1.$$$$ || exit 2; \
+	./${TARGET} -iber -oxer ./.tmp.1.$$$$ > ./.tmp.2.$$$$ || exit 2; \
+	diff $$f ./.tmp.2.$$$$ || exit 2; \
+	rm -f ./.tmp.[12].$$$$; \
 	done; fi
 	@if test -f ./sample-DL-DCCH-Message-1.per ; then \
 	for f in ./sample-DL-DCCH-Message-*.per; do \
 	echo "Recoding $$f into DER into XER and back..."; \
-	./${TARGET} -iper -oder $$f > ./.tmp.1.$$ || exit 2; \
-	./${TARGET} -ider -oxer ./.tmp.1.$$ > ./.tmp.2.$$ || exit 2; \
-	./${TARGET} -ixer -oper ./.tmp.2.$$ > ./.tmp.1.$$ || exit 2; \
-	diff $$f ./.tmp.1.$$ || exit 2; \
-	rm -f ./.tmp.[12].$$; \
+	./${TARGET} -iper -oder $$f > ./.tmp.1.$$$$ || exit 2; \
+	./${TARGET} -iber -oxer ./.tmp.1.$$$$ > ./.tmp.2.$$$$ || exit 2; \
+	./${TARGET} -ixer -oper ./.tmp.2.$$$$ > ./.tmp.1.$$$$ || exit 2; \
+	diff $$f ./.tmp.1.$$$$ || exit 2; \
+	rm -f ./.tmp.[12].$$$$; \
 	done; fi
 	@echo ================
 	@echo All tests passed
diff --git a/skeletons/converter-sample.c b/skeletons/converter-sample.c
index 79067a3..3ee0b3b 100644
--- a/skeletons/converter-sample.c
+++ b/skeletons/converter-sample.c
@@ -35,7 +35,7 @@
  * Open file and parse its contens.
  */
 static void *data_decode_from_file(asn_TYPE_descriptor_t *asnTypeOfPDU,
-	FILE *f, const char *filename, ssize_t suggested_bufsize);
+	FILE *file, const char *name, ssize_t suggested_bufsize, int first_pdu);
 static int write_out(const void *buffer, size_t size, void *key);
 static FILE *argument_to_file(char *av[], int idx);
 static char *argument_to_name(char *av[], int idx);
@@ -43,6 +43,7 @@
        int opt_debug;	/* -d */
 static int opt_check;	/* -c */
 static int opt_stack;	/* -s */
+static int opt_onepdu;	/* -1 */
 
 /* Input data format selector */
 static enum input_format {
@@ -107,21 +108,9 @@
 		fprintf(stderr, "-o<format>: '%s': improper format selector\n",
 			optarg);
 		exit(EX_UNAVAILABLE);
-	case 'p':
-#ifdef	ASN_PDU_COLLECTION
-		{
-			asn_TYPE_descriptor_t **pdu = asn_pdu_collection;
-			if(optarg[0] < 'A' || optarg[0] > 'Z') {
-				fprintf(stderr, "Available PDU types:\n");
-				for(; *pdu; pdu++) printf("%s\n", (*pdu)->name);
-				exit(0);
-			}
-			while(*pdu && strcmp((*pdu)->name, optarg)) pdu++;
-			if(*pdu) { pduType = *pdu; break; }
-		}
-#endif	/* ASN_PDU_COLLECTION */
-		fprintf(stderr, "-p %s: Unrecognized PDU\n", optarg);
-		exit(EX_UNAVAILABLE);
+	case '1':
+		opt_onepdu = 1;
+		break;
 	case 'b':
 		suggested_bufsize = atoi(optarg);
 		if(suggested_bufsize < 1
@@ -146,6 +135,21 @@
 			exit(EX_UNAVAILABLE);
 		}
 		break;
+	case 'p':
+#ifdef	ASN_PDU_COLLECTION
+		{
+			asn_TYPE_descriptor_t **pdu = asn_pdu_collection;
+			if(optarg[0] < 'A' || optarg[0] > 'Z') {
+				fprintf(stderr, "Available PDU types:\n");
+				for(; *pdu; pdu++) printf("%s\n", (*pdu)->name);
+				exit(0);
+			}
+			while(*pdu && strcmp((*pdu)->name, optarg)) pdu++;
+			if(*pdu) { pduType = *pdu; break; }
+		}
+#endif	/* ASN_PDU_COLLECTION */
+		fprintf(stderr, "-p %s: Unrecognized PDU\n", optarg);
+		exit(EX_UNAVAILABLE);
 	case 's':
 		opt_stack = atoi(optarg);
 		if(opt_stack < 0) {
@@ -208,20 +212,26 @@
 	   * Process all files in turn.
 	   */
 	  for(ac_i = 0; ac_i < ac; ac_i++) {
-		asn_enc_rval_t erv;
-		void *structure;	/* Decoded structure */
-		FILE *file = argument_to_file(av, ac_i);
-		char *name = argument_to_name(av, ac_i);
+	    asn_enc_rval_t erv;
+	    void *structure;	/* Decoded structure */
+	    FILE *file = argument_to_file(av, ac_i);
+	    char *name = argument_to_name(av, ac_i);
+	    int first_pdu;
 
+	    for(first_pdu = 1; first_pdu || !opt_onepdu; first_pdu = 0) {
 		/*
 		 * Decode the encoded structure from file.
 		 */
 		structure = data_decode_from_file(pduType,
-				file, name, suggested_bufsize);
-		if(file && file != stdin) fclose(file);
+				file, name, suggested_bufsize, first_pdu);
 		if(!structure) {
-			/* Error message is already printed */
-			exit(EX_DATAERR);
+			if(errno) {
+				/* Error message is already printed */
+				exit(EX_DATAERR);
+			} else {
+				/* EOF */
+				break;
+			}
 		}
 
 		/* Check ASN.1 constraints */
@@ -257,6 +267,7 @@
 					name);
 				exit(EX_UNAVAILABLE);
 			}
+			DEBUG("Encoded in %ld bytes of DER", (long)erv.encoded);
 			break;
 		case OUT_PER:
 			erv = uper_encode(pduType, structure, write_out, stdout);
@@ -265,10 +276,15 @@
 					name);
 				exit(EX_UNAVAILABLE);
 			}
+			DEBUG("Encoded in %ld bits of UPER", (long)erv.encoded);
 			break;
 		}
 
 		ASN_STRUCT_FREE(*pduType, structure);
+	    }
+
+	    if(file && file != stdin)
+		fclose(file);
 	  }
 	}
 
@@ -279,6 +295,7 @@
 	char  *data;		/* Pointer to the data bytes */
 	size_t offset;		/* Offset from the start */
 	size_t length;		/* Length of meaningful contents */
+	size_t unbit;		/* Unused bits in the last byte */
 	size_t allocated;	/* Allocated memory for data */
 	int    nreallocs;	/* Number of data reallocations */
 	off_t  bytes_shifted;	/* Number of bytes ever shifted */
@@ -332,17 +349,20 @@
 }
 
 static void *
-data_decode_from_file(asn_TYPE_descriptor_t *pduType, FILE *file, const char *filename, ssize_t suggested_bufsize) {
+data_decode_from_file(asn_TYPE_descriptor_t *pduType, FILE *file, const char *name, ssize_t suggested_bufsize, int on_first_pdu) {
 	static char *fbuf;
 	static ssize_t fbuf_size;
 	static asn_codec_ctx_t s_codec_ctx;
 	asn_codec_ctx_t *opt_codec_ctx = 0;
 	void *structure = 0;
 	asn_dec_rval_t rval;
+	size_t old_offset;	
+	size_t new_offset;
 	size_t rd;
 
 	if(!file) {
-		fprintf(stderr, "%s: %s\n", filename, strerror(errno));
+		fprintf(stderr, "%s: %s\n", name, strerror(errno));
+		errno = EINVAL;
 		return 0;
 	}
 
@@ -351,7 +371,7 @@
 		opt_codec_ctx = &s_codec_ctx;
 	}
 
-	DEBUG("Processing %s", filename);
+	DEBUG("Processing %s", name);
 
 	/* prepare the file buffer */
 	if(fbuf_size != suggested_bufsize) {
@@ -363,17 +383,23 @@
 		fbuf_size = suggested_bufsize;
 	}
 
-	DynamicBuffer.offset = 0;
-	DynamicBuffer.length = 0;
-	DynamicBuffer.allocated = 0;
-	DynamicBuffer.bytes_shifted = 0;
-	DynamicBuffer.nreallocs = 0;
+	if(on_first_pdu) {
+		DynamicBuffer.offset = 0;
+		DynamicBuffer.length = 0;
+		DynamicBuffer.unbit = 0;
+		DynamicBuffer.allocated = 0;
+		DynamicBuffer.bytes_shifted = 0;
+		DynamicBuffer.nreallocs = 0;
+	}
+
+	old_offset = DynamicBuffer.bytes_shifted + DynamicBuffer.offset;
 
 	/* Pretend immediate EOF */
 	rval.code = RC_WMORE;
 	rval.consumed = 0;
 
 	while((rd = fread(fbuf, 1, fbuf_size, file)) || !feof(file)) {
+		int    ecbits = 0;	/* Extra consumed bits in case of PER */
 		char  *i_bptr;
 		size_t i_size;
 
@@ -390,6 +416,8 @@
 			i_size = rd;
 		}
 
+		DEBUG("Decoding %ld bytes", (long)i_size);
+
 		switch(iform) {
 		case INP_BER:
 			rval = ber_decode(opt_codec_ctx, pduType,
@@ -401,7 +429,11 @@
 			break;
 		case INP_PER:
 			rval = uper_decode(opt_codec_ctx, pduType,
-				(void **)&structure, i_bptr, i_size);
+				(void **)&structure, i_bptr, i_size, 0);
+			ecbits = rval.consumed % 8;	/* Extra bits */
+			DEBUG("PER unused %d bits (consumed %ld, %d)",
+				ecbits, (long)rval.consumed);
+			rval.consumed /= 8; /* Convert to value in bytes! */
 			break;
 		}
 		DEBUG("decode(%ld) consumed %ld (%ld), code %d",
@@ -411,34 +443,37 @@
 
 		if(DynamicBuffer.allocated == 0) {
 			/*
-			 * Flush the remainder into the intermediate buffer.
+			 * Flush remainder into the intermediate buffer.
 			 */
 			if(rval.code != RC_FAIL && rval.consumed < rd) {
+				DEBUG("Saving %d bytes", rd - rval.consumed);
 				add_bytes_to_buffer(fbuf + rval.consumed,
 					     rd - rval.consumed);
 				rval.consumed = 0;
 			}
 		}
 
+		/*
+		 * Adjust position inside the source buffer.
+		 */
+		if(DynamicBuffer.allocated) {
+			DynamicBuffer.offset += rval.consumed;
+			DynamicBuffer.length -= rval.consumed;
+		} else {
+			DynamicBuffer.bytes_shifted += rval.consumed;
+		}
+
 		switch(rval.code) {
 		case RC_OK:
 			DEBUG("RC_OK, finishing up with %ld",
 				(long)rval.consumed);
 			return structure;
 		case RC_WMORE:
-			/*
-			 * Adjust position inside the source buffer.
-			 */
-			if(DynamicBuffer.allocated) {
-				DynamicBuffer.offset += rval.consumed;
-				DynamicBuffer.length -= rval.consumed;
-			}
 			DEBUG("RC_WMORE, continuing %ld with %ld..%ld..%ld",
 				(long)rval.consumed,
 				(long)DynamicBuffer.offset,
 				(long)DynamicBuffer.length,
 				(long)DynamicBuffer.allocated);
-			rval.consumed = 0;
 			continue;
 		case RC_FAIL:
 			break;
@@ -449,13 +484,24 @@
 	/* Clean up partially decoded structure */
 	ASN_STRUCT_FREE(*pduType, structure);
 
-	fprintf(stderr, "%s: "
-		"Decode failed past byte %ld: %s\n",
-		filename, (long)(DynamicBuffer.bytes_shifted
-			+ DynamicBuffer.offset + rval.consumed),
-		(rval.code == RC_WMORE)
-			? "Unexpected end of input"
-			: "Input processing error");
+	new_offset = DynamicBuffer.bytes_shifted + DynamicBuffer.offset;
+
+	/*
+	 * Print a message and return failure only if not EOF,
+	 * unless this is our first PDU (empty file).
+	 */
+	if(on_first_pdu || new_offset != old_offset) {
+		fprintf(stderr, "%s: "
+			"Decode failed past byte %ld: %s\n",
+			name, (long)new_offset,
+			(rval.code == RC_WMORE)
+				? "Unexpected end of input"
+				: "Input processing error");
+		errno = (rval.code == RC_WMORE) ? ENOMSG : EBADMSG;
+	} else {
+		/* Got EOF after a few successful PDU */	
+		errno = 0;
+	}
 
 	return 0;
 }
diff --git a/skeletons/per_decoder.c b/skeletons/per_decoder.c
index 506ad67..bc5f569 100644
--- a/skeletons/per_decoder.c
+++ b/skeletons/per_decoder.c
@@ -3,10 +3,13 @@
 #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) {
+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) {
 	asn_codec_ctx_t s_codec_ctx;
 	asn_per_data_t pd;
 
+	if(skip_bits < 0 || skip_bits > 7 || (skip_bits > 0 && !size))
+		_ASN_DECODE_FAILED;
+
 	/*
 	 * Stack checker requires that the codec context
 	 * must be allocated on the stack.
@@ -25,7 +28,7 @@
 
 	/* Fill in the position indicator */
 	pd.buffer = (const uint8_t *)buffer;
-	pd.nboff = 0;
+	pd.nboff = skip_bits;
 	pd.nbits = 8 * size; 	/* 8 is CHAR_BIT from <limits.h> */
 
 	/*
diff --git a/skeletons/per_decoder.h b/skeletons/per_decoder.h
index 27fe064..23cb7d7 100644
--- a/skeletons/per_decoder.h
+++ b/skeletons/per_decoder.h
@@ -21,7 +21,8 @@
 	struct asn_TYPE_descriptor_s *type_descriptor,	/* Type to decode */
 	void **struct_ptr,	/* Pointer to a target structure's pointer */
 	const void *buffer,	/* Data to be decoded */
-	size_t size		/* Size of data buffer */
+	size_t size,		/* Size of data buffer */
+	int skip_bits		/* Number of unused leading bits, 0..7 */
 	);
 
 
diff --git a/skeletons/per_encoder.c b/skeletons/per_encoder.c
index 6ba28e5..614dd23 100644
--- a/skeletons/per_encoder.c
+++ b/skeletons/per_encoder.c
@@ -21,10 +21,20 @@
 	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 && _uper_encode_flush_outp(&po))
-		_ASN_ENCODE_FAILED;
+	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;
 }
@@ -52,7 +62,6 @@
 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.
@@ -65,10 +74,7 @@
 
 	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;
+	return uper_encode(td, sptr, encode_to_buffer_cb, &key);
 }
 
 static int
diff --git a/skeletons/per_support.c b/skeletons/per_support.c
index 9217038..b0149f8 100644
--- a/skeletons/per_support.c
+++ b/skeletons/per_support.c
@@ -210,6 +210,7 @@
 			po->tmpspace[0] = po->buffer[0];
 		po->buffer = po->tmpspace;
 		po->nbits = 8 * sizeof(po->tmpspace);
+		po->flushed_bytes += complete_bytes;
 	}
 
 	/*
diff --git a/skeletons/per_support.h b/skeletons/per_support.h
index 7e5e594..420bb83 100644
--- a/skeletons/per_support.h
+++ b/skeletons/per_support.h
@@ -76,7 +76,8 @@
 	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;
+	void *op_key;		/* Key for (outper) data callback */
+	size_t flushed_bytes;	/* Bytes already flushed through (outper) */
 } asn_per_outp_t;
 
 /* Output a small number of bits (<= 31) */