Update to asn1c official repository svn trunk r1409
diff --git a/include/asn1c/INTEGER.h b/include/asn1c/INTEGER.h
index 62832b1..8411bfc 100644
--- a/include/asn1c/INTEGER.h
+++ b/include/asn1c/INTEGER.h
@@ -30,6 +30,8 @@
 	int map_count;				/* Elements in either map */
 	int extension;				/* This map is extensible */
 	int strict_enumeration;			/* Enumeration set is fixed */
+	int field_width;			/* Size of native integer */
+	int field_unsigned;			/* Signed=0, unsigned=1 */
 } asn_INTEGER_specifics_t;
 
 asn_struct_print_f INTEGER_print;
@@ -51,7 +53,9 @@
  * -1/ENOMEM: Memory allocation failed (in asn_long2INTEGER()).
  */
 int asn_INTEGER2long(const INTEGER_t *i, long *l);
+int asn_INTEGER2ulong(const INTEGER_t *i, unsigned long *l);
 int asn_long2INTEGER(INTEGER_t *i, long l);
+int asn_ulong2INTEGER(INTEGER_t *i, unsigned long l);
 
 /*
  * Convert the integer value into the corresponding enumeration map entry.
diff --git a/include/asn1c/Makefile.am b/include/asn1c/Makefile.am
index 81b654d..899ad78 100644
--- a/include/asn1c/Makefile.am
+++ b/include/asn1c/Makefile.am
@@ -1,3 +1,3 @@
 asn1cdir = $(includedir)/asn1c
 
-asn1c_HEADERS = ANY.h asn_application.h asn_codecs.h asn_codecs_prim.h asn_internal.h asn_SEQUENCE_OF.h asn_SET_OF.h asn_system.h ber_decoder.h ber_tlv_length.h ber_tlv_tag.h BIT_STRING.h BMPString.h BOOLEAN.h constraints.h constr_CHOICE.h constr_SEQUENCE.h constr_SEQUENCE_OF.h constr_SET.h constr_SET_OF.h constr_TYPE.h der_encoder.h ENUMERATED.h GeneralizedTime.h GeneralString.h GraphicString.h IA5String.h INTEGER.h ISO646String.h NativeEnumerated.h NativeInteger.h NativeReal.h NULL.h NumericString.h ObjectDescriptor.h OBJECT_IDENTIFIER.h OCTET_STRING.h per_decoder.h per_encoder.h per_support.h PrintableString.h REAL.h RELATIVE-OID.h T61String.h TeletexString.h UniversalString.h UTCTime.h UTF8String.h VideotexString.h VisibleString.h xer_decoder.h xer_encoder.h xer_support.h 
+asn1c_HEADERS = ANY.h asn_application.h asn_codecs.h asn_codecs_prim.h asn_internal.h asn_SEQUENCE_OF.h asn_SET_OF.h asn_system.h ber_decoder.h ber_tlv_length.h ber_tlv_tag.h BIT_STRING.h BMPString.h BOOLEAN.h constraints.h constr_CHOICE.h constr_SEQUENCE.h constr_SEQUENCE_OF.h constr_SET.h constr_SET_OF.h constr_TYPE.h der_encoder.h ENUMERATED.h GeneralizedTime.h GeneralString.h GraphicString.h IA5String.h INTEGER.h ISO646String.h NativeEnumerated.h NativeInteger.h NativeReal.h NULL.h NumericString.h ObjectDescriptor.h OBJECT_IDENTIFIER.h OCTET_STRING.h per_decoder.h per_encoder.h per_support.h PrintableString.h REAL.h RELATIVE-OID.h T61String.h TeletexString.h UniversalString.h UTCTime.h UTF8String.h VideotexString.h VisibleString.h xer_decoder.h xer_encoder.h xer_support.h per_opentype.h
diff --git a/include/asn1c/NativeReal.h b/include/asn1c/NativeReal.h
index 1f5266c..68a81d9 100644
--- a/include/asn1c/NativeReal.h
+++ b/include/asn1c/NativeReal.h
@@ -6,7 +6,7 @@
  * This type differs from the standard REAL in that it is modelled using
  * the fixed machine type (double), so it can hold only values of
  * limited precision. There is no explicit type (i.e., NativeReal_t).
- * Use of this type is normally enabled by -fnative-integers.
+ * Use of this type is normally enabled by -fnative-types.
  */
 #ifndef	ASN_TYPE_NativeReal_H
 #define	ASN_TYPE_NativeReal_H
@@ -25,6 +25,8 @@
 der_type_encoder_f NativeReal_encode_der;
 xer_type_decoder_f NativeReal_decode_xer;
 xer_type_encoder_f NativeReal_encode_xer;
+per_type_decoder_f NativeReal_decode_uper;
+per_type_encoder_f NativeReal_encode_uper;
 
 #ifdef __cplusplus
 }
diff --git a/include/asn1c/OCTET_STRING.h b/include/asn1c/OCTET_STRING.h
index 5150161..8df9a18 100644
--- a/include/asn1c/OCTET_STRING.h
+++ b/include/asn1c/OCTET_STRING.h
@@ -70,7 +70,13 @@
 	int struct_size;	/* Size of the structure */
 	int ctx_offset;		/* Offset of the asn_struct_ctx_t member */
 
-	int subvariant;		/* {0,1,2} for O-S, BIT STRING or ANY */
+	enum asn_OS_Subvariant {
+		ASN_OSUBV_ANY,	/* The open type (ANY) */
+		ASN_OSUBV_BIT,	/* BIT STRING */
+		ASN_OSUBV_STR,	/* String types, not {BMP,Universal}String  */
+		ASN_OSUBV_U16,	/* 16-bit character (BMPString) */
+		ASN_OSUBV_U32	/* 32-bit character (UniversalString) */
+	} subvariant;
 } asn_OCTET_STRING_specifics_t;
 
 #ifdef __cplusplus
diff --git a/include/asn1c/REAL.h b/include/asn1c/REAL.h
index 28ccf28..af3e84c 100644
--- a/include/asn1c/REAL.h
+++ b/include/asn1c/REAL.h
@@ -19,6 +19,8 @@
 asn_struct_print_f REAL_print;
 xer_type_decoder_f REAL_decode_xer;
 xer_type_encoder_f REAL_encode_xer;
+per_type_decoder_f REAL_decode_uper;
+per_type_encoder_f REAL_encode_uper;
 
 /***********************************
  * Some handy conversion routines. *
diff --git a/include/asn1c/asn_internal.h b/include/asn1c/asn_internal.h
index 67f055a..249d7ef 100644
--- a/include/asn1c/asn_internal.h
+++ b/include/asn1c/asn_internal.h
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003, 2004, 2005 Lev Walkin <vlm@lionet.info>.
+ * Copyright (c) 2003, 2004, 2005, 2007 Lev Walkin <vlm@lionet.info>.
  * All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
@@ -20,7 +20,7 @@
 #endif
 
 /* Environment version might be used to avoid running with the old library */
-#define	ASN1C_ENVIRONMENT_VERSION	920	/* Compile-time version */
+#define	ASN1C_ENVIRONMENT_VERSION	922	/* Compile-time version */
 int get_asn1c_environment_version(void);	/* Run-time version */
 
 #define	CALLOC(nmemb, size)	calloc(nmemb, size)
@@ -35,10 +35,17 @@
 #ifndef	ASN_DEBUG	/* If debugging code is not defined elsewhere... */
 #if	EMIT_ASN_DEBUG == 1	/* And it was asked to emit this code... */
 #ifdef	__GNUC__
-#define	ASN_DEBUG(fmt, args...)	do {		\
-		fprintf(stderr, fmt, ##args);	\
-		fprintf(stderr, " (%s:%d)\n",	\
-			__FILE__, __LINE__);	\
+#ifdef	ASN_THREAD_SAFE
+#define	asn_debug_indent	0
+#else	/* !ASN_THREAD_SAFE */
+int asn_debug_indent;
+#endif	/* ASN_THREAD_SAFE */
+#define	ASN_DEBUG(fmt, args...)	do {			\
+		int adi = asn_debug_indent;		\
+		while(adi--) fprintf(stderr, " ");	\
+		fprintf(stderr, fmt, ##args);		\
+		fprintf(stderr, " (%s:%d)\n",		\
+			__FILE__, __LINE__);		\
 	} while(0)
 #else	/* !__GNUC__ */
 void ASN_DEBUG_f(const char *fmt, ...);
@@ -70,6 +77,7 @@
 	int __nl = ((nl) != 0);						\
 	int __i;							\
 	if(__nl) _ASN_CALLBACK("\n", 1);				\
+	if(__level < 0) __level = 0;					\
 	for(__i = 0; __i < __level; __i++)				\
 		_ASN_CALLBACK("    ", 4);				\
 	er.encoded += __nl + 4 * __level;				\
diff --git a/include/asn1c/asn_system.h b/include/asn1c/asn_system.h
index d7ebdaa..0a9b092 100644
--- a/include/asn1c/asn_system.h
+++ b/include/asn1c/asn_system.h
@@ -1,5 +1,6 @@
 /*-
- * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2003, 2004, 2007 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 /*
@@ -16,20 +17,27 @@
 #include <stdlib.h>	/* For *alloc(3) */
 #include <string.h>	/* For memcpy(3) */
 #include <sys/types.h>	/* For size_t */
+#include <limits.h>	/* For LONG_MAX */
 #include <stdarg.h>	/* For va_start */
 #include <stddef.h>	/* for offsetof and ptrdiff_t */
 
 #ifdef	WIN32
 
 #include <malloc.h>
-#include <stdint.h>
 #define	 snprintf	_snprintf
 #define	 vsnprintf	_vsnprintf
 
+/* To avoid linking with ws2_32.lib, here's the definition of ntohl() */
+#define sys_ntohl(l)	((((l) << 24)  & 0xff000000)	\
+			| (((l) << 16) & 0xff0000)	\
+			| (((l) << 8)  & 0xff00)	\
+			| ((l) & 0xff))
+
 #ifdef _MSC_VER			/* MSVS.Net */
 #ifndef __cplusplus
 #define inline __inline
 #endif
+#ifndef	ASSUMESTDTYPES	/* Standard types have been defined elsewhere */
 #define	ssize_t		SSIZE_T
 typedef	char		int8_t;
 typedef	short		int16_t;
@@ -37,6 +45,7 @@
 typedef	unsigned char	uint8_t;
 typedef	unsigned short	uint16_t;
 typedef	unsigned int	uint32_t;
+#endif	/* ASSUMESTDTYPES */
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <float.h>
@@ -44,6 +53,8 @@
 #define finite _finite
 #define copysign _copysign
 #define	ilogb	_logb
+#else	/* !_MSC_VER */
+#include <stdint.h>
 #endif	/* _MSC_VER */
 
 #else	/* !WIN32 */
@@ -74,6 +85,9 @@
 #endif	/* defined(sun) */
 #endif
 
+#include <netinet/in.h> /* for ntohl() */
+#define	sys_ntohl(foo)	ntohl(foo)
+
 #endif	/* defined(__vxworks) */
 
 #endif	/* WIN32 */
@@ -82,11 +96,22 @@
 #ifndef	GCC_PRINTFLIKE
 #define	GCC_PRINTFLIKE(fmt,var)	__attribute__((format(printf,fmt,var)))
 #endif
+#ifndef	GCC_NOTUSED
+#define	GCC_NOTUSED		__attribute__((unused))
+#endif
 #else
 #ifndef	GCC_PRINTFLIKE
 #define	GCC_PRINTFLIKE(fmt,var)	/* nothing */
 #endif
+#ifndef	GCC_NOTUSED
+#define	GCC_NOTUSED
 #endif
+#endif
+
+/* Figure out if thread safety is requested */
+#if !defined(ASN_THREAD_SAFE) && (defined(THREAD_SAFE) || defined(_REENTRANT))
+#define	ASN_THREAD_SAFE
+#endif	/* Thread safety */
 
 #ifndef	offsetof	/* If not defined by <stddef.h> */
 #define	offsetof(s, m)	((ptrdiff_t)&(((s *)0)->m) - (ptrdiff_t)((s *)0))
diff --git a/include/asn1c/ber_decoder.h b/include/asn1c/ber_decoder.h
index 768133b..9fe2e89 100644
--- a/include/asn1c/ber_decoder.h
+++ b/include/asn1c/ber_decoder.h
@@ -17,6 +17,7 @@
 /*
  * The BER decoder of any type.
  * This function may be invoked directly from the application.
+ * The der_encode() function (der_encoder.h) is an opposite to ber_decode().
  */
 asn_dec_rval_t ber_decode(struct asn_codec_ctx_s *opt_codec_ctx,
 	struct asn_TYPE_descriptor_s *type_descriptor,
diff --git a/include/asn1c/der_encoder.h b/include/asn1c/der_encoder.h
index 4e2fb06..61431c6 100644
--- a/include/asn1c/der_encoder.h
+++ b/include/asn1c/der_encoder.h
@@ -15,6 +15,7 @@
 
 /*
  * The DER encoder of any type. May be invoked by the application.
+ * The ber_decode() function (ber_decoder.h) is an opposite of der_encode().
  */
 asn_enc_rval_t der_encode(struct asn_TYPE_descriptor_s *type_descriptor,
 		void *struct_ptr,	/* Structure to be encoded */
diff --git a/include/asn1c/per_decoder.h b/include/asn1c/per_decoder.h
index 26aaf59..8397a54 100644
--- a/include/asn1c/per_decoder.h
+++ b/include/asn1c/per_decoder.h
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2005 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2005, 2007 Lev Walkin <vlm@lionet.info>. All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 #ifndef	_PER_DECODER_H_
@@ -15,7 +15,19 @@
 struct asn_TYPE_descriptor_s;	/* Forward declaration */
 
 /*
+ * Unaligned PER decoder of a "complete encoding" as per X.691#10.1.
+ * On success, this call always returns (.consumed >= 1), as per X.691#10.1.3.
+ */
+asn_dec_rval_t uper_decode_complete(struct asn_codec_ctx_s *opt_codec_ctx,
+	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 */
+	);
+
+/*
  * Unaligned PER decoder of any ASN.1 type. May be invoked by the application.
+ * WARNING: This call returns the number of BITS read from the stream. Beware.
  */
 asn_dec_rval_t uper_decode(struct asn_codec_ctx_s *opt_codec_ctx,
 	struct asn_TYPE_descriptor_s *type_descriptor,	/* Type to decode */
diff --git a/include/asn1c/per_encoder.h b/include/asn1c/per_encoder.h
index 9ac130b..95a6506 100644
--- a/include/asn1c/per_encoder.h
+++ b/include/asn1c/per_encoder.h
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2006 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2006, 2007 Lev Walkin <vlm@lionet.info>. All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 #ifndef	_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-packing
+ * 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/include/asn1c/per_opentype.h b/include/asn1c/per_opentype.h
new file mode 100644
index 0000000..facfaa6
--- /dev/null
+++ b/include/asn1c/per_opentype.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2007 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	_PER_OPENTYPE_H_
+#define	_PER_OPENTYPE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+asn_dec_rval_t uper_open_type_get(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd);
+
+int uper_open_type_skip(asn_codec_ctx_t *opt_codec_ctx, asn_per_data_t *pd);
+
+int uper_open_type_put(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _PER_OPENTYPE_H_ */
diff --git a/include/asn1c/per_support.h b/include/asn1c/per_support.h
index 420bb83..7cb1a0c 100644
--- a/include/asn1c/per_support.h
+++ b/include/asn1c/per_support.h
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2005, 2006 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 #ifndef	_PER_SUPPORT_H_
@@ -29,15 +30,20 @@
 typedef struct asn_per_constraints_s {
 	asn_per_constraint_t value;
 	asn_per_constraint_t size;
+	int (*value2code)(unsigned int value);
+	int (*code2value)(unsigned int code);
 } asn_per_constraints_t;
 
 /*
  * 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 */
-        size_t  nboff;	/* Bit offset to the meaningful bit */
-        size_t  nbits;	/* Number of bits in the stream */
+  const uint8_t *buffer;  /* Pointer to the octet stream */
+         size_t  nboff;   /* Bit offset to the meaningful bit */
+         size_t  nbits;   /* Number of bits in the stream */
+         size_t  moved;   /* Number of bits moved through this bit stream */
+  int (*refill)(struct asn_per_data_s *);
+  void *refill_key;
 } asn_per_data_t;
 
 /*
@@ -47,6 +53,9 @@
  */
 int32_t per_get_few_bits(asn_per_data_t *per_data, int get_nbits);
 
+/* Undo the immediately preceeding "get_few_bits" operation */
+void per_get_undo(asn_per_data_t *per_data, int get_nbits);
+
 /*
  * 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
@@ -63,10 +72,18 @@
 			int *repeat);
 
 /*
+ * Get the normally small length "n".
+ */
+ssize_t uper_get_nslength(asn_per_data_t *pd);
+
+/*
  * Get the normally small non-negative whole number.
  */
 ssize_t uper_get_nsnnwn(asn_per_data_t *pd);
 
+/* Non-thread-safe debugging function, don't use it */
+char *per_data_string(asn_per_data_t *pd);
+
 /*
  * This structure supports forming PER output.
  */
@@ -94,6 +111,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);
diff --git a/src/ANY.c b/src/ANY.c
index 612238b..0ad60d0 100644
--- a/src/ANY.c
+++ b/src/ANY.c
@@ -9,7 +9,7 @@
 static asn_OCTET_STRING_specifics_t asn_DEF_ANY_specs = {
 	sizeof(ANY_t),
 	offsetof(ANY_t, _asn_ctx),
-	2	/* Special indicator that this is an ANY type */
+	ASN_OSUBV_ANY
 };
 asn_TYPE_descriptor_t asn_DEF_ANY = {
 	"ANY",
diff --git a/src/BIT_STRING.c b/src/BIT_STRING.c
index 6469d4f..9b98271 100644
--- a/src/BIT_STRING.c
+++ b/src/BIT_STRING.c
@@ -15,7 +15,7 @@
 static asn_OCTET_STRING_specifics_t asn_DEF_BIT_STRING_specs = {
 	sizeof(BIT_STRING_t),
 	offsetof(BIT_STRING_t, _asn_ctx),
-	1,	/* Special indicator that this is a BIT STRING type */
+	ASN_OSUBV_BIT
 };
 asn_TYPE_descriptor_t asn_DEF_BIT_STRING = {
 	"BIT STRING",
@@ -50,14 +50,15 @@
 	const BIT_STRING_t *st = (const BIT_STRING_t *)sptr;
 
 	if(st && st->buf) {
-		if(st->size == 1 && st->bits_unused) {
-			_ASN_CTFAIL(app_key, td,
+		if((st->size == 0 && st->bits_unused)
+		|| st->bits_unused < 0 || st->bits_unused > 7) {
+			_ASN_CTFAIL(app_key, td, sptr,
 				"%s: invalid padding byte (%s:%d)",
 				td->name, __FILE__, __LINE__);
 			return -1;
 		}
 	} else {
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: value not given (%s:%d)",
 			td->name, __FILE__, __LINE__);
 		return -1;
diff --git a/src/BMPString.c b/src/BMPString.c
index d6a9308..072bd07 100644
--- a/src/BMPString.c
+++ b/src/BMPString.c
@@ -13,6 +13,16 @@
 	(ASN_TAG_CLASS_UNIVERSAL | (30 << 2)),	/* [UNIVERSAL 30] IMPLICIT ...*/
 	(ASN_TAG_CLASS_UNIVERSAL | (4 << 2))	/* ... OCTET STRING */
 };
+static asn_OCTET_STRING_specifics_t asn_DEF_BMPString_specs = {
+	sizeof(BMPString_t),
+	offsetof(BMPString_t, _asn_ctx),
+	ASN_OSUBV_U16	/* 16-bits character */
+};
+static asn_per_constraints_t asn_DEF_BMPString_constraints = {
+	{ APC_CONSTRAINED, 16, 16, 0, 65535 },
+	{ APC_SEMI_CONSTRAINED, -1, -1, 0, 0 },
+	0, 0
+};
 asn_TYPE_descriptor_t asn_DEF_BMPString = {
 	"BMPString",
 	"BMPString",
@@ -23,7 +33,8 @@
 	OCTET_STRING_encode_der,
 	BMPString_decode_xer,		/* Convert from UTF-8 */
 	BMPString_encode_xer,		/* Convert to UTF-8 */
-	0, 0,
+	OCTET_STRING_decode_uper,
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_BMPString_tags,
 	sizeof(asn_DEF_BMPString_tags)
@@ -31,9 +42,9 @@
 	asn_DEF_BMPString_tags,
 	sizeof(asn_DEF_BMPString_tags)
 	  / sizeof(asn_DEF_BMPString_tags[0]),
-	0,	/* No PER visible constraints */
+	&asn_DEF_BMPString_constraints,
 	0, 0,	/* No members */
-	0	/* No specifics */
+	&asn_DEF_BMPString_specs
 };
 
 /*
@@ -131,7 +142,7 @@
 				rc.consumed = 0;
 				return rc;
 			} else {
-				dstwc[2 * wcs_len] = 0;
+				dstwc[wcs_len] = 0;	/* nul-terminate */
 				wcs = (uint32_t *)dstwc;
 			}
 		}
diff --git a/src/BOOLEAN.c b/src/BOOLEAN.c
index bb4697c..2c2bbcf 100644
--- a/src/BOOLEAN.c
+++ b/src/BOOLEAN.c
@@ -257,7 +257,7 @@
 	switch(per_get_few_bits(pd, 1)) {
 	case 1: *st = 1; break;
 	case 0: *st = 0; break;
-	case -1: default: _ASN_DECODE_FAILED;
+	case -1: default: _ASN_DECODE_STARVED;
 	}
 
 	ASN_DEBUG("%s decoded as %s", td->name, *st ? "TRUE" : "FALSE");
diff --git a/src/GeneralString.c b/src/GeneralString.c
index 55bb664..01b606b 100644
--- a/src/GeneralString.c
+++ b/src/GeneralString.c
@@ -22,7 +22,8 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_hex,
 	OCTET_STRING_encode_xer,
-	0, 0,
+	OCTET_STRING_decode_uper,    /* Implemented in terms of OCTET STRING */
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_GeneralString_tags,
 	sizeof(asn_DEF_GeneralString_tags)
diff --git a/src/GeneralizedTime.c b/src/GeneralizedTime.c
index f864d83..9d683ef 100644
--- a/src/GeneralizedTime.c
+++ b/src/GeneralizedTime.c
@@ -105,10 +105,13 @@
 	tzold = getenv("TZ");						\
 	if(tzold) {							\
 		size_t tzlen = strlen(tzold);				\
-		if(tzlen < sizeof(tzoldbuf))				\
+		if(tzlen < sizeof(tzoldbuf)) {				\
 			tzold = memcpy(tzoldbuf, tzold, tzlen + 1);	\
-		else							\
-			tzold = strdup(tzold);	/* Ignore error */	\
+		} else {						\
+			char *dupptr = tzold;				\
+			tzold = MALLOC(tzlen + 1);			\
+			if(tzold) memcpy(tzold, dupptr, tzlen + 1);	\
+		}							\
 		setenv("TZ", "UTC", 1);					\
 	}								\
 	tzset();							\
@@ -147,6 +150,11 @@
 	(ASN_TAG_CLASS_UNIVERSAL | (26 << 2)),  /* [UNIVERSAL 26] IMPLICIT ...*/
 	(ASN_TAG_CLASS_UNIVERSAL | (4 << 2))    /* ... OCTET STRING */
 };
+static asn_per_constraints_t asn_DEF_GeneralizedTime_constraints = {
+	{ APC_CONSTRAINED, 7, 7, 0x20, 0x7e },  /* Value */
+	{ APC_SEMI_CONSTRAINED, -1, -1, 0, 0 }, /* Size */
+	0, 0
+};
 asn_TYPE_descriptor_t asn_DEF_GeneralizedTime = {
 	"GeneralizedTime",
 	"GeneralizedTime",
@@ -157,7 +165,8 @@
 	GeneralizedTime_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	GeneralizedTime_encode_xer,
-	0, 0,
+	OCTET_STRING_decode_uper,
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_GeneralizedTime_tags,
 	sizeof(asn_DEF_GeneralizedTime_tags)
@@ -165,7 +174,7 @@
 	asn_DEF_GeneralizedTime_tags,
 	sizeof(asn_DEF_GeneralizedTime_tags)
 	  / sizeof(asn_DEF_GeneralizedTime_tags[0]),
-	0,	/* No PER visible constraints */
+	&asn_DEF_GeneralizedTime_constraints,
 	0, 0,	/* No members */
 	0	/* No specifics */
 };
@@ -184,7 +193,7 @@
 	errno = EPERM;			/* Just an unlikely error code */
 	tloc = asn_GT2time(st, 0, 0);
 	if(tloc == -1 && errno != EPERM) {
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: Invalid time format: %s (%s:%d)",
 			td->name, strerror(errno), __FILE__, __LINE__);
 		return -1;
@@ -657,7 +666,7 @@
 		int ret;
 		gmtoff %= 86400;
 		ret = snprintf(p, buf_size - size, "%+03ld%02ld",
-			gmtoff / 3600, labs(gmtoff % 3600));
+			gmtoff / 3600, labs(gmtoff % 3600) / 60);
 		if(ret != 5) {
 			FREEMEM(buf);
 			errno = EINVAL;
diff --git a/src/GraphicString.c b/src/GraphicString.c
index 135cd73..7d59d52 100644
--- a/src/GraphicString.c
+++ b/src/GraphicString.c
@@ -22,7 +22,8 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_hex,
 	OCTET_STRING_encode_xer,	/* Can't expect it to be ASCII/UTF8 */
-	0, 0,
+	OCTET_STRING_decode_uper,    /* Implemented in terms of OCTET STRING */
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_GraphicString_tags,
 	sizeof(asn_DEF_GraphicString_tags)
diff --git a/src/IA5String.c b/src/IA5String.c
index 5c000b0..02ecd3e 100644
--- a/src/IA5String.c
+++ b/src/IA5String.c
@@ -12,6 +12,11 @@
 	(ASN_TAG_CLASS_UNIVERSAL | (22 << 2)),	/* [UNIVERSAL 22] IMPLICIT ...*/
 	(ASN_TAG_CLASS_UNIVERSAL | (4 << 2))	/* ... OCTET STRING */
 };
+static asn_per_constraints_t asn_DEF_IA5String_constraints = {
+	{ APC_CONSTRAINED, 7, 7, 0, 0x7f },	/* Value */
+	{ APC_SEMI_CONSTRAINED, -1, -1, 0, 0 },	/* Size */
+	0, 0
+};
 asn_TYPE_descriptor_t asn_DEF_IA5String = {
 	"IA5String",
 	"IA5String",
@@ -22,7 +27,8 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	OCTET_STRING_encode_xer_utf8,
-	0, 0,
+	OCTET_STRING_decode_uper,
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_IA5String_tags,
 	sizeof(asn_DEF_IA5String_tags)
@@ -30,7 +36,7 @@
 	asn_DEF_IA5String_tags,
 	sizeof(asn_DEF_IA5String_tags)
 	  / sizeof(asn_DEF_IA5String_tags[0]),
-	0,	/* No PER visible constraints */
+	&asn_DEF_IA5String_constraints,
 	0, 0,	/* No members */
 	0	/* No specifics */
 };
@@ -49,7 +55,7 @@
 		 */
 		for(; buf < end; buf++) {
 			if(*buf > 0x7F) {
-				_ASN_CTFAIL(app_key, td,
+				_ASN_CTFAIL(app_key, td, sptr,
 					"%s: value byte %ld out of range: "
 					"%d > 127 (%s:%d)",
 					td->name,
@@ -60,7 +66,7 @@
 			}
 		}
 	} else {
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: value not given (%s:%d)",
 			td->name, __FILE__, __LINE__);
 		return -1;
diff --git a/src/INTEGER.c b/src/INTEGER.c
index 9c8b9ed..f016131 100644
--- a/src/INTEGER.c
+++ b/src/INTEGER.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.
  */
@@ -158,7 +158,9 @@
 		} else {
 			scrsize = sizeof(scratch);
 			scr = scratch;
-			ret = snprintf(scr, scrsize, "%ld", accum);
+			ret = snprintf(scr, scrsize,
+				(specs && specs->field_unsigned)
+				?"%lu":"%ld", accum);
 		}
 		assert(ret > 0 && (size_t)ret < scrsize);
 		return (cb(scr, ret, app_key) < 0) ? -1 : ret;
@@ -334,8 +336,8 @@
 	} state = ST_SKIPSPACE;
 
 	if(chunk_size)
-		ASN_DEBUG("INTEGER body %d 0x%2x..0x%2x",
-			chunk_size, *lstart, lstop[-1]);
+		ASN_DEBUG("INTEGER body %ld 0x%2x..0x%2x",
+			(long)chunk_size, *lstart, lstop[-1]);
 
 	/*
 	 * We may have received a tag here. It will be processed inline.
@@ -384,7 +386,7 @@
 			case ST_HEXDIGIT2:
 				value += (lv - 0x30);
 				state = ST_HEXCOLON;
-				st->buf[st->size++] = value;
+				st->buf[st->size++] = (uint8_t)value;
 				continue;
 			case ST_HEXCOLON:
 				return XPBD_BROKEN_ENCODING;
@@ -467,7 +469,7 @@
 			case ST_HEXDIGIT2:
 				value += lv - ((lv < 0x61) ? 0x41 : 0x61);
 				value += 10;
-				st->buf[st->size++] = value;
+				st->buf[st->size++] = (uint8_t)value;
 				state = ST_HEXCOLON;
 				continue;
 			case ST_DIGITS:
@@ -554,6 +556,7 @@
 asn_dec_rval_t
 INTEGER_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 };
 	INTEGER_t *st = (INTEGER_t *)*sptr;
 	asn_per_constraint_t *ct;
@@ -576,6 +579,8 @@
 	}
 
 	FREEMEM(st->buf);
+	st->buf = 0;
+	st->size = 0;
 	if(ct) {
 		if(ct->flags & APC_SEMI_CONSTRAINED) {
 			st->buf = (uint8_t *)CALLOC(1, 2);
@@ -586,11 +591,7 @@
 			st->buf = (uint8_t *)MALLOC(1 + size + 1);
 			if(!st->buf) _ASN_DECODE_FAILED;
 			st->size = size;
-		} else {
-			st->size = 0;
 		}
-	} else {
-		st->size = 0;
 	}
 
 	/* X.691, #12.2.2 */
@@ -598,12 +599,24 @@
 		/* #10.5.6 */
 		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_STARVED;
+			long value;
+			if(ct->range_bits == 32) {
+				long lhalf;
+				value = per_get_few_bits(pd, 16);
+				if(value < 0) _ASN_DECODE_STARVED;
+				lhalf = per_get_few_bits(pd, 16);
+				if(lhalf < 0) _ASN_DECODE_STARVED;
+				value = (value << 16) | lhalf;
+			} else {
+				value = per_get_few_bits(pd, ct->range_bits);
+				if(value < 0) _ASN_DECODE_STARVED;
+			}
 			ASN_DEBUG("Got value %ld + low %ld",
 				value, ct->lower_bound);
 			value += ct->lower_bound;
-			if(asn_long2INTEGER(st, value))
+			if((specs && specs->field_unsigned)
+				? asn_ulong2INTEGER(st, value)
+				: asn_long2INTEGER(st, value))
 				_ASN_DECODE_FAILED;
 			return rval;
 		}
@@ -649,6 +662,7 @@
 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_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics;
 	asn_enc_rval_t er;
 	INTEGER_t *st = (INTEGER_t *)sptr;
 	const uint8_t *buf;
@@ -665,21 +679,41 @@
 
 	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;
+		if(specs && specs->field_unsigned) {
+			unsigned long uval;
+			if(asn_INTEGER2ulong(st, &uval))
+				_ASN_ENCODE_FAILED;
+			/* Check proper range */
+			if(ct->flags & APC_SEMI_CONSTRAINED) {
+				if(uval < (unsigned long)ct->lower_bound)
+					inext = 1;
+			} else if(ct->range_bits >= 0) {
+				if(uval < (unsigned long)ct->lower_bound
+				|| uval > (unsigned long)ct->upper_bound)
+					inext = 1;
+			}
+			ASN_DEBUG("Value %lu (%02x/%d) lb %lu ub %lu %s",
+				uval, st->buf[0], st->size,
+				ct->lower_bound, ct->upper_bound,
+				inext ? "ext" : "fix");
+			value = uval;
+		} else {
+			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");
 		}
-		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;
@@ -695,9 +729,17 @@
 		/* #10.5.6 */
 		ASN_DEBUG("Encoding integer with range %d bits",
 			ct->range_bits);
-		if(per_put_few_bits(po, value - ct->lower_bound,
+		if(ct->range_bits == 32) {
+			/* TODO: extend to >32 bits */
+			long v = value - ct->lower_bound;
+			if(per_put_few_bits(po, v >> 1, 31)
+			|| per_put_few_bits(po, v, 1))
+				_ASN_ENCODE_FAILED;
+		} else {
+			if(per_put_few_bits(po, value - ct->lower_bound,
 				ct->range_bits))
-			_ASN_ENCODE_FAILED;
+				_ASN_ENCODE_FAILED;
+		}
 		_ASN_ENCODED_OK(er);
 	}
 
@@ -780,6 +822,63 @@
 }
 
 int
+asn_INTEGER2ulong(const INTEGER_t *iptr, unsigned long *lptr) {
+	uint8_t *b, *end;
+	unsigned long l;
+	size_t size;
+
+	if(!iptr || !iptr->buf || !lptr) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	b = iptr->buf;
+	size = iptr->size;
+	end = b + size;
+
+	/* If all extra leading bytes are zeroes, ignore them */
+	for(; size > sizeof(unsigned long); b++, size--) {
+		if(*b) {
+			/* Value won't fit unsigned long */
+			errno = ERANGE;
+			return -1;
+		}
+	}
+
+	/* Conversion engine */
+	for(l = 0; b < end; b++)
+		l = (l << 8) | *b;
+
+	*lptr = l;
+	return 0;
+}
+
+int
+asn_ulong2INTEGER(INTEGER_t *st, unsigned long value) {
+	uint8_t *buf;
+	uint8_t *end;
+	uint8_t *b;
+	int shr;
+
+	if(value <= LONG_MAX)
+		return asn_long2INTEGER(st, value);
+
+	buf = (uint8_t *)MALLOC(1 + sizeof(value));
+	if(!buf) return -1;
+
+	end = buf + (sizeof(value) + 1);
+	buf[0] = 0;
+	for(b = buf + 1, shr = (sizeof(long)-1)*8; b < end; shr -= 8, b++)
+		*b = (uint8_t)(value >> shr);
+
+	if(st->buf) FREEMEM(st->buf);
+	st->buf = buf;
+	st->size = 1 + sizeof(value);
+
+	return 0;
+}
+
+int
 asn_long2INTEGER(INTEGER_t *st, long value) {
 	uint8_t *buf, *bp;
 	uint8_t *p;
diff --git a/src/ISO646String.c b/src/ISO646String.c
index d164aa7..d6ded0e 100644
--- a/src/ISO646String.c
+++ b/src/ISO646String.c
@@ -12,6 +12,11 @@
 	(ASN_TAG_CLASS_UNIVERSAL | (26 << 2)),	/* [UNIVERSAL 26] IMPLICIT ...*/
 	(ASN_TAG_CLASS_UNIVERSAL | (4 << 2))	/* ... OCTET STRING */
 };
+static asn_per_constraints_t asn_DEF_ISO646String_constraints = {
+	{ APC_CONSTRAINED, 7, 7, 0x20, 0x7e },	/* Value */
+	{ APC_SEMI_CONSTRAINED, -1, -1, 0, 0 },	/* Size */
+	0, 0
+};
 asn_TYPE_descriptor_t asn_DEF_ISO646String = {
 	"ISO646String",
 	"ISO646String",
@@ -22,7 +27,8 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	OCTET_STRING_encode_xer_utf8,
-	0, 0,
+	OCTET_STRING_decode_uper,
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_ISO646String_tags,
 	sizeof(asn_DEF_ISO646String_tags)
@@ -30,8 +36,7 @@
 	asn_DEF_ISO646String_tags,
 	sizeof(asn_DEF_ISO646String_tags)
 	  / sizeof(asn_DEF_ISO646String_tags[0]),
-	0,	/* No PER visible constraints */
+	&asn_DEF_ISO646String_constraints,
 	0, 0,	/* No members */
 	0	/* No specifics */
 };
-
diff --git a/src/Makefile.am b/src/Makefile.am
index 4451f65..dcad645 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,5 +8,5 @@
 
 lib_LTLIBRARIES = libasn1c.la
 
-libasn1c_la_SOURCES = ANY.c              constraints.c         GeneralizedTime.c   NumericString.c      T61String.c asn_codecs_prim.c  constr_CHOICE.c       GeneralString.c     ObjectDescriptor.c   TeletexString.c asn_SEQUENCE_OF.c  constr_SEQUENCE.c     GraphicString.c     OBJECT_IDENTIFIER.c  UniversalString.c asn_SET_OF.c       constr_SEQUENCE_OF.c  IA5String.c         OCTET_STRING.c       UTCTime.c ber_decoder.c      constr_SET.c          INTEGER.c           per_decoder.c        UTF8String.c ber_tlv_length.c   constr_SET_OF.c       ISO646String.c      per_encoder.c        VideotexString.c ber_tlv_tag.c      constr_TYPE.c         NativeEnumerated.c  per_support.c        VisibleString.c BIT_STRING.c       NativeInteger.c     PrintableString.c    xer_decoder.c BMPString.c        der_encoder.c         NativeReal.c        REAL.c               xer_encoder.c BOOLEAN.c          ENUMERATED.c          NULL.c              RELATIVE-OID.c       xer_support.c 
+libasn1c_la_SOURCES = ANY.c              constraints.c         GeneralizedTime.c   NumericString.c      T61String.c asn_codecs_prim.c  constr_CHOICE.c       GeneralString.c     ObjectDescriptor.c   TeletexString.c asn_SEQUENCE_OF.c  constr_SEQUENCE.c     GraphicString.c     OBJECT_IDENTIFIER.c  UniversalString.c asn_SET_OF.c       constr_SEQUENCE_OF.c  IA5String.c         OCTET_STRING.c       UTCTime.c ber_decoder.c      constr_SET.c          INTEGER.c           per_decoder.c        UTF8String.c ber_tlv_length.c   constr_SET_OF.c       ISO646String.c      per_encoder.c        VideotexString.c ber_tlv_tag.c      constr_TYPE.c         NativeEnumerated.c  per_support.c        VisibleString.c BIT_STRING.c       NativeInteger.c     PrintableString.c    xer_decoder.c BMPString.c        der_encoder.c         NativeReal.c        REAL.c               xer_encoder.c BOOLEAN.c          ENUMERATED.c          NULL.c              RELATIVE-OID.c       xer_support.c	per_opentype.c
 
diff --git a/src/NativeEnumerated.c b/src/NativeEnumerated.c
index e3af1ca..1554220 100644
--- a/src/NativeEnumerated.c
+++ b/src/NativeEnumerated.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2004, 2007 Lev Walkin <vlm@lionet.info>. All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 /*
@@ -177,9 +177,9 @@
 			inext = 1;
 	}
 	if(ct->flags & APC_EXTENSIBLE) {
-		if(per_put_few_bits(po, inext, 0))
+		if(per_put_few_bits(po, inext, 1))
 			_ASN_ENCODE_FAILED;
-		ct = 0;
+		if(inext) ct = 0;
 	} else if(inext) {
 		_ASN_ENCODE_FAILED;
 	}
@@ -196,7 +196,10 @@
 	/*
 	 * X.691, #10.6: normally small non-negative whole number;
 	 */
-	if(uper_put_nsnnwn(po, value - (specs->extension - 1)))
+	ASN_DEBUG("value = %ld, ext = %d, inext = %d, res = %ld",
+		value, specs->extension, inext,
+		value - (inext ? (specs->extension - 1) : 0));
+	if(uper_put_nsnnwn(po, value - (inext ? (specs->extension - 1) : 0)))
 		_ASN_ENCODE_FAILED;
 
 	_ASN_ENCODED_OK(er);
diff --git a/src/NativeInteger.c b/src/NativeInteger.c
index 34599f6..abdb71a 100644
--- a/src/NativeInteger.c
+++ b/src/NativeInteger.c
@@ -48,6 +48,7 @@
 NativeInteger_decode_ber(asn_codec_ctx_t *opt_codec_ctx,
 	asn_TYPE_descriptor_t *td,
 	void **nint_ptr, const void *buf_ptr, size_t size, int tag_mode) {
+	asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics;
 	long *native = (long *)*nint_ptr;
 	asn_dec_rval_t rval;
 	ber_tlv_len_t length;
@@ -105,7 +106,9 @@
 		tmp.buf = (uint8_t *)unconst_buf.nonconstbuf;
 		tmp.size = length;
 
-		if(asn_INTEGER2long(&tmp, &l)) {
+		if((specs&&specs->field_unsigned)
+			? asn_INTEGER2ulong(&tmp, &l)
+			: asn_INTEGER2long(&tmp, &l)) {
 			rval.code = RC_FAIL;
 			rval.consumed = 0;
 			return rval;
@@ -145,7 +148,7 @@
 
 	/* Prepare a fake INTEGER */
 	for(p = buf + sizeof(buf) - 1; p >= buf; p--, native >>= 8)
-		*p = native;
+		*p = (uint8_t)native;
 
 	tmp.buf = buf;
 	tmp.size = sizeof(buf);
@@ -167,6 +170,7 @@
 NativeInteger_decode_xer(asn_codec_ctx_t *opt_codec_ctx,
 	asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname,
 		const void *buf_ptr, size_t size) {
+	asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics;
 	asn_dec_rval_t rval;
 	INTEGER_t st;
 	void *st_ptr = (void *)&st;
@@ -182,7 +186,9 @@
 		opt_mname, buf_ptr, size);
 	if(rval.code == RC_OK) {
 		long l;
-		if(asn_INTEGER2long(&st, &l)) {
+		if((specs&&specs->field_unsigned)
+			? asn_INTEGER2ulong(&st, &l)
+			: asn_INTEGER2long(&st, &l)) {
 			rval.code = RC_FAIL;
 			rval.consumed = 0;
 		} else {
@@ -205,6 +211,7 @@
 NativeInteger_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
 	int ilevel, enum xer_encoder_flags_e flags,
 		asn_app_consume_bytes_f *cb, void *app_key) {
+	asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics;
 	char scratch[32];	/* Enough for 64-bit int */
 	asn_enc_rval_t er;
 	const long *native = (const long *)sptr;
@@ -214,7 +221,9 @@
 
 	if(!native) _ASN_ENCODE_FAILED;
 
-	er.encoded = snprintf(scratch, sizeof(scratch), "%ld", *native);
+	er.encoded = snprintf(scratch, sizeof(scratch),
+			(specs && specs->field_unsigned)
+			? "%lu" : "%ld", *native);
 	if(er.encoded <= 0 || (size_t)er.encoded >= sizeof(scratch)
 		|| cb(scratch, er.encoded, app_key) < 0)
 		_ASN_ENCODE_FAILED;
@@ -227,6 +236,7 @@
 	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;
 	long *native = (long *)*sptr;
 	INTEGER_t tmpint;
@@ -244,7 +254,9 @@
 	rval = INTEGER_decode_uper(opt_codec_ctx, td, constraints,
 				   &tmpintptr, pd);
 	if(rval.code == RC_OK) {
-		if(asn_INTEGER2long(&tmpint, native))
+		if((specs&&specs->field_unsigned)
+			? asn_INTEGER2ulong(&tmpint, native)
+			: asn_INTEGER2long(&tmpint, native))
 			rval.code = RC_FAIL;
 		else
 			ASN_DEBUG("NativeInteger %s got value %ld",
@@ -258,6 +270,7 @@
 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_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics;
 	asn_enc_rval_t er;
 	long native;
 	INTEGER_t tmpint;
@@ -269,7 +282,9 @@
 	ASN_DEBUG("Encoding NativeInteger %s %ld (UPER)", td->name, native);
 
 	memset(&tmpint, 0, sizeof(tmpint));
-	if(asn_long2INTEGER(&tmpint, native))
+	if((specs&&specs->field_unsigned)
+		? asn_ulong2INTEGER(&tmpint, native)
+		: asn_long2INTEGER(&tmpint, native))
 		_ASN_ENCODE_FAILED;
 	er = INTEGER_encode_uper(td, constraints, &tmpint, po);
 	ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_INTEGER, &tmpint);
@@ -282,6 +297,7 @@
 int
 NativeInteger_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
 	asn_app_consume_bytes_f *cb, void *app_key) {
+	asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics;
 	const long *native = (const long *)sptr;
 	char scratch[32];	/* Enough for 64-bit int */
 	int ret;
@@ -290,7 +306,9 @@
 	(void)ilevel;	/* Unused argument */
 
 	if(native) {
-		ret = snprintf(scratch, sizeof(scratch), "%ld", *native);
+		ret = snprintf(scratch, sizeof(scratch),
+			(specs && specs->field_unsigned)
+			? "%lu" : "%ld", *native);
 		assert(ret > 0 && (size_t)ret < sizeof(scratch));
 		return (cb(scratch, ret, app_key) < 0) ? -1 : 0;
 	} else {
diff --git a/src/NativeReal.c b/src/NativeReal.c
index 2b8ec16..a1ff91e 100644
--- a/src/NativeReal.c
+++ b/src/NativeReal.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2004, 2006 Lev Walkin <vlm@lionet.info>. All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 /*
@@ -12,6 +12,7 @@
 #include <asn_internal.h>
 #include <NativeReal.h>
 #include <REAL.h>
+#include <OCTET_STRING.h>
 
 /*
  * NativeReal basic type description.
@@ -29,7 +30,8 @@
 	NativeReal_encode_der,
 	NativeReal_decode_xer,
 	NativeReal_encode_xer,
-	0, 0,
+	NativeReal_decode_uper,
+	NativeReal_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_NativeReal_tags,
 	sizeof(asn_DEF_NativeReal_tags) / sizeof(asn_DEF_NativeReal_tags[0]),
@@ -157,7 +159,74 @@
 	return erval;
 }
 
+/*
+ * Decode REAL type using PER.
+ */
+asn_dec_rval_t
+NativeReal_decode_uper(asn_codec_ctx_t *opt_codec_ctx,
+	asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints,
+		void **dbl_ptr, asn_per_data_t *pd) {
+	double *Dbl = (double *)*dbl_ptr;
+	asn_dec_rval_t rval;
+	REAL_t tmp;
+	void *ptmp = &tmp;
+	int ret;
 
+	(void)constraints;
+
+	/*
+	 * If the structure is not there, allocate it.
+	 */
+	if(Dbl == NULL) {
+		*dbl_ptr = CALLOC(1, sizeof(*Dbl));
+		Dbl = (double *)*dbl_ptr;
+		if(Dbl == NULL)
+			_ASN_DECODE_FAILED;
+	}
+
+	memset(&tmp, 0, sizeof(tmp));
+	rval = OCTET_STRING_decode_uper(opt_codec_ctx, td, NULL,
+			&ptmp, pd);
+	if(rval.code != RC_OK) {
+		ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_REAL, &tmp);
+		return rval;
+	}
+
+	ret = asn_REAL2double(&tmp, Dbl);
+	ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_REAL, &tmp);
+	if(ret) _ASN_DECODE_FAILED;
+
+	return rval;
+}
+
+/*
+ * Encode the NativeReal using the OCTET STRING PER encoder.
+ */
+asn_enc_rval_t
+NativeReal_encode_uper(asn_TYPE_descriptor_t *td,
+	asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
+	double Dbl = *(const double *)sptr;
+	asn_enc_rval_t erval;
+	REAL_t tmp;
+
+	(void)constraints;
+
+	/* Prepare a temporary clean structure */
+	memset(&tmp, 0, sizeof(tmp));
+
+	if(asn_double2REAL(&tmp, Dbl))
+		_ASN_ENCODE_FAILED;
+	
+	/* Encode a DER REAL */
+	erval = OCTET_STRING_encode_uper(td, NULL, &tmp, po);
+	if(erval.encoded == -1)
+		erval.structure_ptr = sptr;
+
+	/* Free possibly allocated members of the temporary structure */
+	ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_REAL, &tmp);
+
+	return erval;
+}
 
 /*
  * Decode the chunk of XML text encoding REAL.
diff --git a/src/NumericString.c b/src/NumericString.c
index cef64ea..50fe449 100644
--- a/src/NumericString.c
+++ b/src/NumericString.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2003, 2006 Lev Walkin <vlm@lionet.info>. All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 #include <asn_internal.h>
@@ -12,6 +12,31 @@
 	(ASN_TAG_CLASS_UNIVERSAL | (18 << 2)),	/* [UNIVERSAL 18] IMPLICIT ...*/
 	(ASN_TAG_CLASS_UNIVERSAL | (4 << 2))	/* ... OCTET STRING */
 };
+static int asn_DEF_NumericString_v2c(unsigned int value) {
+	switch(value) {
+	case 0x20: return 0;
+	case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
+	case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
+		return value - (0x30 - 1);
+	}
+	return -1;
+}
+static int asn_DEF_NumericString_c2v(unsigned int code) {
+	if(code > 0) {
+		if(code <= 10)
+			return code + (0x30 - 1);
+		else
+			return -1;
+	} else {
+		return 0x20;
+	}
+}
+static asn_per_constraints_t asn_DEF_NumericString_constraints = {
+	{ APC_CONSTRAINED, 4, 4, 0x20, 0x39 },	/* Value */
+	{ APC_SEMI_CONSTRAINED, -1, -1, 0, 0 },	/* Size */
+	asn_DEF_NumericString_v2c,
+	asn_DEF_NumericString_c2v
+};
 asn_TYPE_descriptor_t asn_DEF_NumericString = {
 	"NumericString",
 	"NumericString",
@@ -22,7 +47,8 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	OCTET_STRING_encode_xer_utf8,
-	0, 0,
+	OCTET_STRING_decode_uper,
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_NumericString_tags,
 	sizeof(asn_DEF_NumericString_tags)
@@ -30,7 +56,7 @@
 	asn_DEF_NumericString_tags,
 	sizeof(asn_DEF_NumericString_tags)
 	  / sizeof(asn_DEF_NumericString_tags[0]),
-	0,	/* No PER visible constraints */
+	&asn_DEF_NumericString_constraints,
 	0, 0,	/* No members */
 	0	/* No specifics */
 };
@@ -55,7 +81,7 @@
 			case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
 				continue;
 			}
-			_ASN_CTFAIL(app_key, td,
+			_ASN_CTFAIL(app_key, td, sptr,
 				"%s: value byte %ld (%d) "
 				"not in NumericString alphabet (%s:%d)",
 				td->name,
@@ -65,7 +91,7 @@
 			return -1;
 		}
 	} else {
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: value not given (%s:%d)",
 			td->name, __FILE__, __LINE__);
 		return -1;
diff --git a/src/OBJECT_IDENTIFIER.c b/src/OBJECT_IDENTIFIER.c
index b1666dc..0d7043e 100644
--- a/src/OBJECT_IDENTIFIER.c
+++ b/src/OBJECT_IDENTIFIER.c
@@ -4,6 +4,7 @@
  */
 #include <asn_internal.h>
 #include <OBJECT_IDENTIFIER.h>
+#include <OCTET_STRING.h>
 #include <limits.h>	/* for CHAR_BIT */
 #include <errno.h>
 
@@ -23,7 +24,8 @@
 	der_encode_primitive,
 	OBJECT_IDENTIFIER_decode_xer,
 	OBJECT_IDENTIFIER_encode_xer,
-	0, 0,
+	OCTET_STRING_decode_uper,
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_OBJECT_IDENTIFIER_tags,
 	sizeof(asn_DEF_OBJECT_IDENTIFIER_tags)
@@ -44,14 +46,14 @@
 
 	if(st && st->buf) {
 		if(st->size < 1) {
-			_ASN_CTFAIL(app_key, td,
+			_ASN_CTFAIL(app_key, td, sptr,
 				"%s: at least one numerical value "
 				"expected (%s:%d)",
 				td->name, __FILE__, __LINE__);
 			return -1;
 		}
 	} else {
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: value not given (%s:%d)",
 			td->name, __FILE__, __LINE__);
 		return -1;
@@ -63,7 +65,7 @@
 
 int
 OBJECT_IDENTIFIER_get_single_arc(uint8_t *arcbuf, unsigned int arclen, signed int add, void *rvbufp, unsigned int rvsize) {
-	unsigned LE __attribute__ ((unused)) = 1; /* Little endian (x86) */
+	unsigned LE GCC_NOTUSED = 1; /* Little endian (x86) */
 	uint8_t *arcend = arcbuf + arclen;	/* End of arc */
 	unsigned int cache = 0;	/* No more than 14 significant bits */
 	unsigned char *rvbuf = (unsigned char *)rvbufp;
diff --git a/src/OCTET_STRING.c b/src/OCTET_STRING.c
index 3a83bd9..584def8 100644
--- a/src/OCTET_STRING.c
+++ b/src/OCTET_STRING.c
@@ -17,10 +17,12 @@
 static asn_OCTET_STRING_specifics_t asn_DEF_OCTET_STRING_specs = {
 	sizeof(OCTET_STRING_t),
 	offsetof(OCTET_STRING_t, _asn_ctx),
-	0
+	ASN_OSUBV_STR
 };
-static asn_per_constraint_t asn_DEF_OCTET_STRING_constraint = {
-	APC_SEMI_CONSTRAINED, -1, -1, 0, 0
+static asn_per_constraints_t asn_DEF_OCTET_STRING_constraints = {
+	{ APC_CONSTRAINED, 8, 8, 0, 255 },
+	{ APC_SEMI_CONSTRAINED, -1, -1, 0, 0 },
+	0, 0
 };
 asn_TYPE_descriptor_t asn_DEF_OCTET_STRING = {
 	"OCTET STRING",		/* Canonical name */
@@ -103,15 +105,6 @@
 	} while(0)
 
 /*
- * Internal variant of the OCTET STRING.
- */
-typedef enum OS_type {
-	_TT_GENERIC	= 0,	/* Just a random OCTET STRING */
-	_TT_BIT_STRING	= 1,	/* BIT STRING type, a special case */
-	_TT_ANY		= 2	/* ANY type, a special case too */
-} OS_type_e;
-
-/*
  * The main reason why ASN.1 is still alive is that too much time and effort
  * is necessary for learning it more or less adequately, thus creating a gut
  * necessity to demonstrate that aquired skill everywhere afterwards.
@@ -185,11 +178,11 @@
 	struct _stack *stck;		/* Expectations stack structure */
 	struct _stack_el *sel = 0;	/* Stack element */
 	int tlv_constr;
-	OS_type_e type_variant = (OS_type_e)specs->subvariant;
+	enum asn_OS_Subvariant type_variant = specs->subvariant;
 
 	ASN_DEBUG("Decoding %s as %s (frame %ld)",
 		td->name,
-		(type_variant == _TT_GENERIC) ?
+		(type_variant == ASN_OSUBV_STR) ?
 			"OCTET STRING" : "OS-SpecialCase",
 		(long)size);
 
@@ -230,7 +223,7 @@
 			 * Jump into stackless primitive decoding.
 			 */
 			_CH_PHASE(ctx, 3);
-			if(type_variant == _TT_ANY && tag_mode != 1)
+			if(type_variant == ASN_OSUBV_ANY && tag_mode != 1)
 				APPEND(buf_ptr, rval.consumed);
 			ADVANCE(rval.consumed);
 			goto phase3;
@@ -309,7 +302,7 @@
 
 			ASN_DEBUG("Eat EOC; wn=%d--", sel->want_nulls);
 
-			if(type_variant == _TT_ANY
+			if(type_variant == ASN_OSUBV_ANY
 			&& (tag_mode != 1 || sel->cont_level))
 				APPEND("\0\0", 2);
 
@@ -334,10 +327,10 @@
 		 * depending on ASN.1 type being decoded.
 		 */
 		switch(type_variant) {
-		case _TT_BIT_STRING:
+		case ASN_OSUBV_BIT:
 			/* X.690: 8.6.4.1, NOTE 2 */
 			/* Fall through */
-		case _TT_GENERIC:
+		case ASN_OSUBV_STR:
 		default:
 			if(sel) {
 				int level = sel->cont_level;
@@ -352,7 +345,7 @@
 				/* else, Fall through */
 			}
 			/* Fall through */
-		case _TT_ANY:
+		case ASN_OSUBV_ANY:
 			expected_tag = tlv_tag;
 			break;
 		}
@@ -397,7 +390,7 @@
 		} else {
 			sel->left = tlv_len;
 		}
-		if(type_variant == _TT_ANY
+		if(type_variant == ASN_OSUBV_ANY
 		&& (tag_mode != 1 || sel->cont_level))
 			APPEND(buf_ptr, tlvl);
 		sel->got += tlvl;
@@ -431,7 +424,7 @@
 		len = ((ber_tlv_len_t)size < sel->left)
 				? (ber_tlv_len_t)size : sel->left;
 		if(len > 0) {
-			if(type_variant == _TT_BIT_STRING
+			if(type_variant == ASN_OSUBV_BIT
 			&& sel->bits_chopped == 0) {
 				/* Put the unused-bits-octet away */
 				st->bits_unused = *(const uint8_t *)buf_ptr;
@@ -464,7 +457,7 @@
 
 		if(size < (size_t)ctx->left) {
 			if(!size) RETURN(RC_WMORE);
-			if(type_variant == _TT_BIT_STRING && !ctx->context) {
+			if(type_variant == ASN_OSUBV_BIT && !ctx->context) {
 				st->bits_unused = *(const uint8_t *)buf_ptr;
 				ctx->left--;
 				ADVANCE(1);
@@ -475,7 +468,7 @@
 			ADVANCE(size);
 			RETURN(RC_WMORE);
 		} else {
-			if(type_variant == _TT_BIT_STRING
+			if(type_variant == ASN_OSUBV_BIT
 			&& !ctx->context && ctx->left) {
 				st->bits_unused = *(const uint8_t *)buf_ptr;
 				ctx->left--;
@@ -502,14 +495,14 @@
 	/*
 	 * BIT STRING-specific processing.
 	 */
-	if(type_variant == _TT_BIT_STRING && st->size) {
+	if(type_variant == ASN_OSUBV_BIT && st->size) {
 		/* Finalize BIT STRING: zero out unused bits. */
 		st->buf[st->size-1] &= 0xff << st->bits_unused;
 	}
 
 	ASN_DEBUG("Took %ld bytes to encode %s: [%s]:%ld",
 		(long)consumed_myself, td->name,
-		(type_variant == _TT_GENERIC) ? (char *)st->buf : "<data>",
+		(type_variant == ASN_OSUBV_STR) ? (char *)st->buf : "<data>",
 		(long)st->size);
 
 
@@ -528,7 +521,7 @@
 				? (asn_OCTET_STRING_specifics_t *)td->specifics
 				: &asn_DEF_OCTET_STRING_specs;
 	BIT_STRING_t *st = (BIT_STRING_t *)sptr;
-	OS_type_e type_variant = (OS_type_e)specs->subvariant;
+	enum asn_OS_Subvariant type_variant = specs->subvariant;
 	int fix_last_byte = 0;
 
 	ASN_DEBUG("%s %s as OCTET STRING",
@@ -537,10 +530,11 @@
 	/*
 	 * Write tags.
 	 */
-	if(type_variant != _TT_ANY || tag_mode == 1) {
+	if(type_variant != ASN_OSUBV_ANY || tag_mode == 1) {
 		er.encoded = der_write_tags(td,
-				(type_variant == _TT_BIT_STRING) + st->size,
-			tag_mode, type_variant == _TT_ANY, tag, cb, app_key);
+				(type_variant == ASN_OSUBV_BIT) + st->size,
+			tag_mode, type_variant == ASN_OSUBV_ANY, tag,
+			cb, app_key);
 		if(er.encoded == -1) {
 			er.failed_type = td;
 			er.structure_ptr = sptr;
@@ -548,19 +542,19 @@
 		}
 	} else {
 		/* Disallow: [<tag>] IMPLICIT ANY */
-		assert(type_variant != _TT_ANY || tag_mode != -1);
+		assert(type_variant != ASN_OSUBV_ANY || tag_mode != -1);
 		er.encoded = 0;
 	}
 
 	if(!cb) {
-		er.encoded += (type_variant == _TT_BIT_STRING) + st->size;
+		er.encoded += (type_variant == ASN_OSUBV_BIT) + st->size;
 		_ASN_ENCODED_OK(er);
 	}
 
 	/*
 	 * Prepare to deal with the last octet of BIT STRING.
 	 */
-	if(type_variant == _TT_BIT_STRING) {
+	if(type_variant == ASN_OSUBV_BIT) {
 		uint8_t b = st->bits_unused & 0x07;
 		if(b && st->size) fix_last_byte = 1;
 		_ASN_CALLBACK(&b, 1);
@@ -595,7 +589,7 @@
 	uint8_t *end;
 	size_t i;
 
-	if(!st || !st->buf)
+	if(!st || (!st->buf && st->size))
 		_ASN_ENCODE_FAILED;
 
 	er.encoded = 0;
@@ -751,7 +745,7 @@
 	(void)ilevel;	/* Unused argument */
 	(void)flags;	/* Unused argument */
 
-	if(!st || !st->buf)
+	if(!st || (!st->buf && st->size))
 		_ASN_ENCODE_FAILED;
 
 	buf = st->buf;
@@ -1197,6 +1191,135 @@
 		OCTET_STRING__convert_entrefs);
 }
 
+static int
+OCTET_STRING_per_get_characters(asn_per_data_t *po, uint8_t *buf,
+		size_t units, unsigned int bpc, unsigned int unit_bits,
+		long lb, long ub, asn_per_constraints_t *pc) {
+	uint8_t *end = buf + units * bpc;
+
+	ASN_DEBUG("Expanding %d characters into (%ld..%ld):%d",
+		(int)units, lb, ub, unit_bits);
+
+	/* X.691: 27.5.4 */
+	if((unsigned long)ub <= ((unsigned long)2 << (unit_bits - 1))) {
+		/* Decode without translation */
+		lb = 0;
+	} else if(pc && pc->code2value) {
+		if(unit_bits > 16)
+			return 1;	/* FATAL: can't have constrained
+					 * UniversalString with more than
+					 * 16 million code points */
+		for(; buf < end; buf += bpc) {
+			int value;
+			int code = per_get_few_bits(po, unit_bits);
+			if(code < 0) return -1;	/* WMORE */
+			value = pc->code2value(code);
+			if(value < 0) {
+				ASN_DEBUG("Code %d (0x%02x) is"
+					" not in map (%ld..%ld)",
+					code, code, lb, ub);
+				return 1;	/* FATAL */
+			}
+			switch(bpc) {
+			case 1: *buf = value; break;
+			case 2: buf[0] = value >> 8; buf[1] = value; break;
+			case 4: buf[0] = value >> 24; buf[1] = value >> 16;
+				buf[2] = value >> 8; buf[3] = value; break;
+			}
+		}
+		return 0;
+	}
+
+	/* Shortcut the no-op copying to the aligned structure */
+	if(lb == 0 && (unit_bits == 8 * bpc)) {
+		return per_get_many_bits(po, buf, 0, unit_bits * units);
+	}
+
+	for(; buf < end; buf += bpc) {
+		int code = per_get_few_bits(po, unit_bits);
+		int ch = code + lb;
+		if(code < 0) return -1;	/* WMORE */
+		if(ch > ub) {
+			ASN_DEBUG("Code %d is out of range (%ld..%ld)",
+				ch, lb, ub);
+			return 1;	/* FATAL */
+		}
+		switch(bpc) {
+		case 1: *buf = ch; break;
+		case 2: buf[0] = ch >> 8; buf[1] = ch; break;
+		case 4: buf[0] = ch >> 24; buf[1] = ch >> 16;
+			buf[2] = ch >> 8; buf[3] = ch; break;
+		}
+	}
+
+	return 0;
+}
+
+static int
+OCTET_STRING_per_put_characters(asn_per_outp_t *po, const uint8_t *buf,
+		size_t units, unsigned int bpc, unsigned int unit_bits,
+		long lb, long ub, asn_per_constraints_t *pc) {
+	const uint8_t *end = buf + units * bpc;
+
+	ASN_DEBUG("Squeezing %d characters into (%ld..%ld):%d (%d bpc)",
+		(int)units, lb, ub, unit_bits, bpc);
+
+	/* X.691: 27.5.4 */
+	if((unsigned long)ub <= ((unsigned long)2 << (unit_bits - 1))) {
+		/* Encode as is */
+		lb = 0;
+	} else if(pc && pc->value2code) {
+		for(; buf < end; buf += bpc) {
+			int code;
+			uint32_t value;
+			switch(bpc) {
+			case 1: value = *(const uint8_t *)buf; break;
+			case 2: value = (buf[0] << 8) | buf[1]; break;
+			case 4: value = (buf[0] << 24) | (buf[1] << 16)
+					| (buf[2] << 8) | buf[3]; break;
+			default: return -1;
+			}
+			code = pc->value2code(value);
+			if(code < 0) {
+				ASN_DEBUG("Character %d (0x%02x) is"
+					" not in map (%ld..%ld)",
+					*buf, *buf, lb, ub);
+				return -1;
+			}
+			if(per_put_few_bits(po, code, unit_bits))
+				return -1;
+		}
+	}
+
+	/* Shortcut the no-op copying to the aligned structure */
+	if(lb == 0 && (unit_bits == 8 * bpc)) {
+		return per_put_many_bits(po, buf, unit_bits * units);
+	}
+
+	for(ub -= lb; buf < end; buf += bpc) {
+		int ch;
+		uint32_t value;
+		switch(bpc) {
+		case 1: value = *(const uint8_t *)buf; break;
+		case 2: value = (buf[0] << 8) | buf[1]; break;
+		case 4: value = (buf[0] << 24) | (buf[1] << 16)
+				| (buf[2] << 8) | buf[3]; break;
+		default: return -1;
+		}
+		ch = value - lb;
+		if(ch < 0 || ch > ub) {
+			ASN_DEBUG("Character %d (0x%02x)"
+			" is out of range (%ld..%ld)",
+				*buf, *buf, lb, ub + lb);
+			return -1;
+		}
+		if(per_put_few_bits(po, ch, unit_bits))
+			return -1;
+	}
+
+	return 0;
+}
+
 asn_dec_rval_t
 OCTET_STRING_decode_uper(asn_codec_ctx_t *opt_codec_ctx,
 	asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints,
@@ -1205,18 +1328,62 @@
 	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);
+	asn_per_constraints_t *pc = constraints ? constraints
+				: td->per_constraints;
+	asn_per_constraint_t *cval;
+	asn_per_constraint_t *csiz;
 	asn_dec_rval_t rval = { RC_OK, 0 };
 	BIT_STRING_t *st = (BIT_STRING_t *)*sptr;
 	ssize_t consumed_myself = 0;
 	int repeat;
-	int unit_bits = (specs->subvariant != 1) * 7 + 1;
+	enum {
+		OS__BPC_BIT	= 0,
+		OS__BPC_CHAR	= 1,
+		OS__BPC_U16	= 2,
+		OS__BPC_U32	= 4
+	} bpc;	/* Bytes per character */
+	unsigned int unit_bits;
+	unsigned int canonical_unit_bits;
 
 	(void)opt_codec_ctx;
 
+	if(pc) {
+		cval = &pc->value;
+		csiz = &pc->size;
+	} else {
+		cval = &asn_DEF_OCTET_STRING_constraints.value;
+		csiz = &asn_DEF_OCTET_STRING_constraints.size;
+	}
+
+	switch(specs->subvariant) {
+	default:
+	case ASN_OSUBV_ANY:
+		ASN_DEBUG("Unrecognized subvariant %d", specs->subvariant);
+		RETURN(RC_FAIL);
+	case ASN_OSUBV_BIT:
+		canonical_unit_bits = unit_bits = 1;
+		bpc = OS__BPC_BIT;
+		break;
+	case ASN_OSUBV_STR:
+		canonical_unit_bits = unit_bits = 8;
+		if(cval->flags & APC_CONSTRAINED)
+			unit_bits = cval->range_bits;
+		bpc = OS__BPC_CHAR;
+		break;
+	case ASN_OSUBV_U16:
+		canonical_unit_bits = unit_bits = 16;
+		if(cval->flags & APC_CONSTRAINED)
+			unit_bits = cval->range_bits;
+		bpc = OS__BPC_U16;
+		break;
+	case ASN_OSUBV_U32:
+		canonical_unit_bits = unit_bits = 32;
+		if(cval->flags & APC_CONSTRAINED)
+			unit_bits = cval->range_bits;
+		bpc = OS__BPC_U32;
+		break;
+	}
+
 	/*
 	 * Allocate the string.
 	 */
@@ -1225,24 +1392,26 @@
 		if(!st) RETURN(RC_FAIL);
 	}
 
-	ASN_DEBUG("PER Decoding %s %ld .. %ld bits %d",
-		ct->flags & APC_EXTENSIBLE ? "extensible" : "fixed",
-		ct->lower_bound, ct->upper_bound, ct->effective_bits);
+	ASN_DEBUG("PER Decoding %s size %ld .. %ld bits %d",
+		csiz->flags & APC_EXTENSIBLE ? "extensible" : "non-extensible",
+		csiz->lower_bound, csiz->upper_bound, csiz->effective_bits);
 
-	if(ct->flags & APC_EXTENSIBLE) {
+	if(csiz->flags & APC_EXTENSIBLE) {
 		int inext = per_get_few_bits(pd, 1);
 		if(inext < 0) RETURN(RC_WMORE);
-		if(inext) ct = &asn_DEF_OCTET_STRING_constraint;
-		consumed_myself = 0;
+		if(inext) {
+			csiz = &asn_DEF_OCTET_STRING_constraints.size;
+			cval = &asn_DEF_OCTET_STRING_constraints.value;
+			unit_bits = canonical_unit_bits;
+		}
 	}
 
-	if(ct->effective_bits >= 0
-	&& (!st->buf || st->size < ct->upper_bound)) {
+	if(csiz->effective_bits >= 0) {
 		FREEMEM(st->buf);
-		if(unit_bits == 1) {
-			st->size = (ct->upper_bound + 7) >> 3;
+		if(bpc) {
+			st->size = csiz->upper_bound * bpc;
 		} else {
-			st->size = ct->upper_bound;
+			st->size = (csiz->upper_bound + 7) >> 3;
 		}
 		st->buf = (uint8_t *)MALLOC(st->size + 1);
 		if(!st->buf) { st->size = 0; RETURN(RC_FAIL); }
@@ -1251,46 +1420,70 @@
 	/* 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) {
-		int ret = per_get_many_bits(pd, st->buf, 0,
-					    unit_bits * ct->upper_bound);
+	if(csiz->effective_bits == 0) {
+		int ret;
+		if(bpc) {
+			ASN_DEBUG("Encoding OCTET STRING size %ld",
+				csiz->upper_bound);
+			ret = OCTET_STRING_per_get_characters(pd, st->buf,
+				csiz->upper_bound, bpc, unit_bits,
+				cval->lower_bound, cval->upper_bound, pc);
+			if(ret > 0) RETURN(RC_FAIL);
+		} else {
+			ASN_DEBUG("Encoding BIT STRING size %ld",
+				csiz->upper_bound);
+			ret = per_get_many_bits(pd, st->buf, 0,
+					    unit_bits * csiz->upper_bound);
+		}
 		if(ret < 0) RETURN(RC_WMORE);
-		consumed_myself += unit_bits * ct->upper_bound;
+		consumed_myself += unit_bits * csiz->upper_bound;
 		st->buf[st->size] = 0;
-		if(unit_bits == 1 && (ct->upper_bound & 0x7))
-			st->bits_unused = 8 - (ct->upper_bound & 0x7);
+		if(bpc == 0) {
+			int ubs = (csiz->upper_bound & 0x7);
+			st->bits_unused = ubs ? 8 - ubs : 0;
+		}
 		RETURN(RC_OK);
 	}
 
 	st->size = 0;
 	do {
+		ssize_t raw_len;
 		ssize_t len_bytes;
 		ssize_t len_bits;
 		void *p;
 		int ret;
 
 		/* Get the PER length */
-		len_bits = uper_get_length(pd, ct->effective_bits, &repeat);
-		if(len_bits < 0) RETURN(RC_WMORE);
-		len_bits += ct->lower_bound;
+		raw_len = uper_get_length(pd, csiz->effective_bits, &repeat);
+		if(raw_len < 0) RETURN(RC_WMORE);
+		raw_len += csiz->lower_bound;
 
 		ASN_DEBUG("Got PER length eb %ld, len %ld, %s (%s)",
-			(long)ct->effective_bits, (long)len_bits,
+			(long)csiz->effective_bits, (long)raw_len,
 			repeat ? "repeat" : "once", td->name);
-		if(unit_bits == 1) {
+		if(bpc) {
+			len_bytes = raw_len * bpc;
+			len_bits = len_bytes * unit_bits;
+		} else {
+			len_bits = raw_len;
 			len_bytes = (len_bits + 7) >> 3;
 			if(len_bits & 0x7)
 				st->bits_unused = 8 - (len_bits & 0x7);
 			/* len_bits be multiple of 16K if repeat is set */
-		} else {
-			len_bytes = len_bits;
-			len_bits = len_bytes << 3;
 		}
 		p = REALLOC(st->buf, st->size + len_bytes + 1);
 		if(!p) RETURN(RC_FAIL);
 		st->buf = (uint8_t *)p;
 
-		ret = per_get_many_bits(pd, &st->buf[st->size], 0, len_bits);
+		if(bpc) {
+			ret = OCTET_STRING_per_get_characters(pd,
+				&st->buf[st->size], raw_len, bpc, unit_bits,
+				cval->lower_bound, cval->upper_bound, pc);
+			if(ret > 0) RETURN(RC_FAIL);
+		} else {
+			ret = per_get_many_bits(pd, &st->buf[st->size],
+				0, len_bits);
+		}
 		if(ret < 0) RETURN(RC_WMORE);
 		st->size += len_bytes;
 	} while(repeat);
@@ -1306,41 +1499,87 @@
 	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);
+	asn_per_constraints_t *pc = constraints ? constraints
+				: td->per_constraints;
+	asn_per_constraint_t *cval;
+	asn_per_constraint_t *csiz;
 	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;
+	asn_enc_rval_t er = { 0, 0, 0 };
 	int inext = 0;		/* Lies not within extension root */
-	int sizeinunits = st->size;
+	unsigned int unit_bits;
+	unsigned int canonical_unit_bits;
+	unsigned int sizeinunits;
 	const uint8_t *buf;
 	int ret;
+	enum {
+		OS__BPC_BIT	= 0,
+		OS__BPC_CHAR	= 1,
+		OS__BPC_U16	= 2,
+		OS__BPC_U32	= 4
+	} bpc;	/* Bytes per character */
+	int ct_extensible;
 
-	if(!st || !st->buf)
+	if(!st || (!st->buf && st->size))
 		_ASN_ENCODE_FAILED;
 
-	if(unit_bits == 1) {
+	if(pc) {
+		cval = &pc->value;
+		csiz = &pc->size;
+	} else {
+		cval = &asn_DEF_OCTET_STRING_constraints.value;
+		csiz = &asn_DEF_OCTET_STRING_constraints.size;
+	}
+	ct_extensible = csiz->flags & APC_EXTENSIBLE;
+
+	switch(specs->subvariant) {
+	default:
+	case ASN_OSUBV_ANY:
+		_ASN_ENCODE_FAILED;
+	case ASN_OSUBV_BIT:
+		canonical_unit_bits = unit_bits = 1;
+		bpc = OS__BPC_BIT;
+		sizeinunits = st->size * 8 - (st->bits_unused & 0x07);
 		ASN_DEBUG("BIT STRING of %d bytes, %d bits unused",
 				sizeinunits, st->bits_unused);
-		sizeinunits = sizeinunits * 8 - (st->bits_unused & 0x07);
+		break;
+	case ASN_OSUBV_STR:
+		canonical_unit_bits = unit_bits = 8;
+		if(cval->flags & APC_CONSTRAINED)
+			unit_bits = cval->range_bits;
+		bpc = OS__BPC_CHAR;
+		sizeinunits = st->size;
+		break;
+	case ASN_OSUBV_U16:
+		canonical_unit_bits = unit_bits = 16;
+		if(cval->flags & APC_CONSTRAINED)
+			unit_bits = cval->range_bits;
+		bpc = OS__BPC_U16;
+		sizeinunits = st->size / 2;
+		break;
+	case ASN_OSUBV_U32:
+		canonical_unit_bits = unit_bits = 32;
+		if(cval->flags & APC_CONSTRAINED)
+			unit_bits = cval->range_bits;
+		bpc = OS__BPC_U32;
+		sizeinunits = st->size / 4;
+		break;
 	}
 
 	ASN_DEBUG("Encoding %s into %d units of %d bits"
-		" (%d..%d, effective %d)%s",
+		" (%ld..%ld, effective %d)%s",
 		td->name, sizeinunits, unit_bits,
-		ct->lower_bound, ct->upper_bound,
-		ct->effective_bits, ct_extensible ? " EXT" : "");
+		csiz->lower_bound, csiz->upper_bound,
+		csiz->effective_bits, ct_extensible ? " EXT" : "");
 
-	/* Figure out wheter size lies within PER visible consrtaint */
+	/* Figure out wheter size lies within PER visible constraint */
 
-	if(ct->effective_bits >= 0) {
-		if(sizeinunits < ct->lower_bound
-		|| sizeinunits > ct->upper_bound) {
+	if(csiz->effective_bits >= 0) {
+		if((int)sizeinunits < csiz->lower_bound
+		|| (int)sizeinunits > csiz->upper_bound) {
 			if(ct_extensible) {
-				ct = &asn_DEF_OCTET_STRING_constraint;
+				cval = &asn_DEF_OCTET_STRING_constraints.value;
+				csiz = &asn_DEF_OCTET_STRING_constraints.size;
+				unit_bits = canonical_unit_bits;
 				inext = 1;
 			} else
 				_ASN_ENCODE_FAILED;
@@ -1358,14 +1597,21 @@
 	/* 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) {
+	if(csiz->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);
+				st->size, sizeinunits - csiz->lower_bound,
+				csiz->effective_bits);
+		ret = per_put_few_bits(po, sizeinunits - csiz->lower_bound,
+				csiz->effective_bits);
 		if(ret) _ASN_ENCODE_FAILED;
-		ret = per_put_many_bits(po, st->buf, sizeinunits * unit_bits);
+		if(bpc) {
+			ret = OCTET_STRING_per_put_characters(po, st->buf,
+				sizeinunits, bpc, unit_bits,
+				cval->lower_bound, cval->upper_bound, pc);
+		} else {
+			ret = per_put_many_bits(po, st->buf,
+				sizeinunits * unit_bits);
+		}
 		if(ret) _ASN_ENCODE_FAILED;
 		_ASN_ENCODED_OK(er);
 	}
@@ -1383,15 +1629,22 @@
 		ssize_t maySave = uper_put_length(po, sizeinunits);
 		if(maySave < 0) _ASN_ENCODE_FAILED;
 
-		ASN_DEBUG("Encoding %d of %d", maySave, sizeinunits);
+		ASN_DEBUG("Encoding %ld of %ld",
+			(long)maySave, (long)sizeinunits);
 
-		ret = per_put_many_bits(po, buf, maySave * unit_bits);
+		if(bpc) {
+			ret = OCTET_STRING_per_put_characters(po, buf,
+				maySave, bpc, unit_bits,
+				cval->lower_bound, cval->upper_bound, pc);
+		} else {
+			ret = per_put_many_bits(po, buf, maySave * unit_bits);
+		}
 		if(ret) _ASN_ENCODE_FAILED;
 
-		if(unit_bits == 1)
-			buf += maySave >> 3;
+		if(bpc)
+			buf += maySave * bpc;
 		else
-			buf += maySave;
+			buf += maySave >> 3;
 		sizeinunits -= maySave;
 		assert(!(maySave & 0x07) || !sizeinunits);
 	}
@@ -1412,7 +1665,8 @@
 
 	(void)td;	/* Unused argument */
 
-	if(!st || !st->buf) return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
+	if(!st || (!st->buf && st->size))
+		return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
 
 	/*
 	 * Dump the contents of the buffer in hexadecimal.
@@ -1448,7 +1702,7 @@
 	(void)td;	/* Unused argument */
 	(void)ilevel;	/* Unused argument */
 
-	if(st && st->buf) {
+	if(st && (st->buf || !st->size)) {
 		return (cb(st->buf, st->size, app_key) < 0) ? -1 : 0;
 	} else {
 		return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
@@ -1472,6 +1726,7 @@
 
 	if(st->buf) {
 		FREEMEM(st->buf);
+		st->buf = 0;
 	}
 
 	/*
diff --git a/src/ObjectDescriptor.c b/src/ObjectDescriptor.c
index 44cb5f6..cd8e8a3 100644
--- a/src/ObjectDescriptor.c
+++ b/src/ObjectDescriptor.c
@@ -22,7 +22,8 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	OCTET_STRING_encode_xer_utf8,
-	0, 0,
+	OCTET_STRING_decode_uper,
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_ObjectDescriptor_tags,
 	sizeof(asn_DEF_ObjectDescriptor_tags)
diff --git a/src/PrintableString.c b/src/PrintableString.c
index f588083..c8ee3ae 100644
--- a/src/PrintableString.c
+++ b/src/PrintableString.c
@@ -1,17 +1,52 @@
 /*-
- * 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>
 #include <PrintableString.h>
 
 /*
+ * ASN.1:1984 (X.409)
+ */
+static int _PrintableString_alphabet[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/*                  */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/*                  */
+ 1, 0, 0, 0, 0, 0, 0, 2, 3, 4, 0, 5, 6, 7, 8, 9,	/* .      '() +,-./ */
+10,11,12,13,14,15,16,17,18,19,20, 0, 0,21, 0,22,	/* 0123456789:  = ? */
+ 0,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,	/*  ABCDEFGHIJKLMNO */
+38,39,40,41,42,43,44,45,46,47,48, 0, 0, 0, 0, 0,	/* PQRSTUVWXYZ      */
+ 0,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,	/*  abcdefghijklmno */
+64,65,66,67,68,69,70,71,72,73,74, 0, 0, 0, 0, 0,	/* pqrstuvwxyz      */
+};
+static int _PrintableString_code2value[74] = { 
+32,39,40,41,43,44,45,46,47,48,49,50,51,52,53,54,
+55,56,57,58,61,63,65,66,67,68,69,70,71,72,73,74,
+75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,
+97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,
+113,114,115,116,117,118,119,120,121,122};
+
+/*
  * PrintableString basic type description.
  */
 static ber_tlv_tag_t asn_DEF_PrintableString_tags[] = {
 	(ASN_TAG_CLASS_UNIVERSAL | (19 << 2)),	/* [UNIVERSAL 19] IMPLICIT ...*/
 	(ASN_TAG_CLASS_UNIVERSAL | (4 << 2))	/* ... OCTET STRING */
 };
+static int asn_DEF_PrintableString_v2c(unsigned int value) {
+	return _PrintableString_alphabet[value > 255 ? 0 : value] - 1;
+}
+static int asn_DEF_PrintableString_c2v(unsigned int code) {
+	if(code < 74)
+		return _PrintableString_code2value[code];
+	return -1;
+}
+static asn_per_constraints_t asn_DEF_PrintableString_constraints = {
+	{ APC_CONSTRAINED, 4, 4, 0x20, 0x39 },	/* Value */
+	{ APC_SEMI_CONSTRAINED, -1, -1, 0, 0 },	/* Size */
+	asn_DEF_PrintableString_v2c,
+	asn_DEF_PrintableString_c2v
+};
 asn_TYPE_descriptor_t asn_DEF_PrintableString = {
 	"PrintableString",
 	"PrintableString",
@@ -22,7 +57,8 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	OCTET_STRING_encode_xer_utf8,
-	0, 0,
+	OCTET_STRING_decode_uper,
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_PrintableString_tags,
 	sizeof(asn_DEF_PrintableString_tags)
@@ -30,34 +66,12 @@
 	asn_DEF_PrintableString_tags,
 	sizeof(asn_DEF_PrintableString_tags)
 	  / sizeof(asn_DEF_PrintableString_tags[0]),
-	0,	/* No PER visible constraints */
+	&asn_DEF_PrintableString_constraints,
 	0, 0,	/* No members */
 	0	/* No specifics */
 };
 
 
-/*
- * ASN.1:1984 (X.409)
- */
-static int _PrintableString_alphabet[256] = {
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 	/*               ' */
-0x41, 0x42, 0x00, 0x43, 0x44, 0x45, 0x46, 0x47, 	/* ( )   + , - . / */
-0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 	/* 0 1 2 3 4 5 6 7 */
-0x3d, 0x3e, 0x48, 0x00, 0x00, 0x49, 0x00, 0x4a, 	/* 8 9 :     =   ? */
-0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 	/*   A B C D E F G */
-0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 	/* H I J K L M N O */
-0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 	/* P Q R S T U V W */
-0x18, 0x19, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 	/* X Y Z           */
-0x00, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 	/*   a b c d e f g */
-0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 	/* h i j k l m n o */
-0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 	/* p q r s t u v w */
-0x32, 0x33, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 	/* x y z           */
-};
-
 int
 PrintableString_constraint(asn_TYPE_descriptor_t *td, const void *sptr,
 		asn_app_constraint_failed_f *ctfailcb, void *app_key) {
@@ -73,7 +87,7 @@
 		 */
 		for(; buf < end; buf++) {
 			if(!_PrintableString_alphabet[*buf]) {
-				_ASN_CTFAIL(app_key, td,
+				_ASN_CTFAIL(app_key, td, sptr,
 					"%s: value byte %ld (%d) "
 					"not in PrintableString alphabet "
 					"(%s:%d)",
@@ -85,7 +99,7 @@
 			}
 		}
 	} else {
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: value not given (%s:%d)",
 			td->name, __FILE__, __LINE__);
 		return -1;
diff --git a/src/REAL.c b/src/REAL.c
index 51098c0..5e93ac8 100644
--- a/src/REAL.c
+++ b/src/REAL.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2004, 2006 Lev Walkin <vlm@lionet.info>. All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 #if	defined(__alpha)
@@ -12,12 +12,13 @@
 #include <math.h>
 #include <errno.h>
 #include <REAL.h>
+#include <OCTET_STRING.h>
 
 #undef	INT_MAX
 #define	INT_MAX	((int)(((unsigned int)-1) >> 1))
 
 #if	!(defined(NAN) || defined(INFINITY))
-static volatile double real_zero __attribute__ ((unused)) = 0.0;
+static volatile double real_zero GCC_NOTUSED = 0.0;
 #endif
 #ifndef	NAN
 #define	NAN	(real_zero/real_zero)
@@ -42,7 +43,8 @@
 	der_encode_primitive,
 	REAL_decode_xer,
 	REAL_encode_xer,
-	0, 0,
+	REAL_decode_uper,
+	REAL_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_REAL_tags,
 	sizeof(asn_DEF_REAL_tags) / sizeof(asn_DEF_REAL_tags[0]),
@@ -341,6 +343,20 @@
 		buf_ptr, size, REAL__xer_body_decode);
 }
 
+asn_dec_rval_t
+REAL_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) {
+	(void)constraints;	/* No PER visible constraints */
+	return OCTET_STRING_decode_uper(opt_codec_ctx, td, 0, sptr, pd);
+}
+
+asn_enc_rval_t
+REAL_encode_uper(asn_TYPE_descriptor_t *td,
+	asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
+	(void)constraints;	/* No PER visible constraints */
+	return OCTET_STRING_encode_uper(td, 0, sptr, po);
+}
 
 int
 asn_REAL2double(const REAL_t *st, double *dbl_value) {
@@ -437,16 +453,16 @@
 		return -1;
 	}
 
-	if((octv & 0x03) == 0x11) {
-		/* 8.5.6.4, case d) */
+	elen = (octv & 0x03);	/* bits 2 to 1; 8.5.6.4 */
+	if(elen == 0x03) {	/* bits 2 to 1 = 11; 8.5.6.4, case d) */
 		elen = st->buf[1];	/* unsigned binary number */
 		if(elen == 0 || st->size <= (int)(2 + elen)) {
 			errno = EINVAL;
 			return -1;
 		}
+		/* FIXME: verify constraints of case d) */
 		ptr = &st->buf[2];
 	} else {
-		elen = (octv & 0x03);
 		ptr = &st->buf[1];
 	}
 
@@ -505,8 +521,8 @@
 	uint8_t buf[16];	/* More than enough for 8-byte dbl_value */
 	uint8_t dscr[sizeof(dbl_value)];	/* double value scratch pad */
 	/* Assertion guards: won't even compile, if unexpected double size */
-	char assertion_buffer1[9 - sizeof(dbl_value)] __attribute__((unused));
-	char assertion_buffer2[sizeof(dbl_value) - 7] __attribute__((unused));
+	char assertion_buffer1[9 - sizeof(dbl_value)] GCC_NOTUSED;
+	char assertion_buffer2[sizeof(dbl_value) - 7] GCC_NOTUSED;
 	uint8_t *ptr = buf;
 	uint8_t *mstop;		/* Last byte of mantissa */
 	unsigned int mval;	/* Value of the last byte of mantissa */
diff --git a/src/RELATIVE-OID.c b/src/RELATIVE-OID.c
index 0181434..983fc09 100644
--- a/src/RELATIVE-OID.c
+++ b/src/RELATIVE-OID.c
@@ -5,6 +5,7 @@
  */
 #include <asn_internal.h>
 #include <RELATIVE-OID.h>
+#include <OCTET_STRING.h>
 #include <asn_codecs_prim.h>	/* Encoder and decoder of a primitive type */
 #include <limits.h>	/* for CHAR_BIT */
 #include <errno.h>
@@ -25,7 +26,8 @@
 	der_encode_primitive,
 	RELATIVE_OID_decode_xer,
 	RELATIVE_OID_encode_xer,
-	0, 0,
+	OCTET_STRING_decode_uper,
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_RELATIVE_OID_tags,
 	sizeof(asn_DEF_RELATIVE_OID_tags)
diff --git a/src/T61String.c b/src/T61String.c
index 25d887a..98461bb 100644
--- a/src/T61String.c
+++ b/src/T61String.c
@@ -22,7 +22,8 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_hex,
 	OCTET_STRING_encode_xer,
-	0, 0,
+	OCTET_STRING_decode_uper,
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_T61String_tags,
 	sizeof(asn_DEF_T61String_tags)
diff --git a/src/TeletexString.c b/src/TeletexString.c
index b96cb3b..cc2acad 100644
--- a/src/TeletexString.c
+++ b/src/TeletexString.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2003, 2006 Lev Walkin <vlm@lionet.info>. All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 #include <asn_internal.h>
@@ -22,7 +22,8 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_hex,
 	OCTET_STRING_encode_xer,
-	0, 0,
+	OCTET_STRING_decode_uper,
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_TeletexString_tags,
 	sizeof(asn_DEF_TeletexString_tags)
@@ -35,3 +36,139 @@
 	0	/* No specifics */
 };
 
+/*
+ * Here is a formal attempt at creating a mapping from TeletexString
+ * (T61String) of the latest ASN.1 standard (X.680:2002) into the Unicode
+ * character set. -- Lev Walkin <vlm@lionet.info>
+ *
+ * The first thing to keep in mind is that TeletexString (T61String)
+ * is defined in ASN.1, and is not really a T.61 string.
+ * The T.61 standard is withdrawn by ITU-T and is no longer an authoritative
+ * reference. See http://www.itu.int/rec/T-REC-T.61
+ *
+ * The X.680 specifies TeletexString (T61String) as a combination of the
+ * character sets specified by the registration numbers listed in
+ * ISO International Register of Coded Character Sets to be used with
+ * Escape Sequences (ISO-2375):
+ * 6, 87, 102, 103, 106, 107, 126, 144, 150, 153, 156, 164, 165, 168,
+ * plus SPACE and DELETE characters.
+ * In addition to that, the X.680 Table 6 NOTE 2 allows using register entries
+ * 6 and 156 instead of 102 and 103.
+ *
+ * The ISO Register itself is available at http://www.itscj.ipsj.or.jp/ISO-IR/
+ *
+ * #6 is ASCII. http://www.itscj.ipsj.or.jp/ISO-IR/006.pdf
+ * 	Escapes into:
+ * 		G0: ESC 2/8 4/2 ("(B")
+ * 		G1: ESC 2/9 4/2 (")B")
+ * 	The range is [0x21 .. 0x7e]. Conversion into Unicode
+ *	is simple, because it has one-to-one correspondence.
+ * #87 is a "Japanese Graphic Character Set for Information Interchange".
+ * 	Is a multiple-byte set of 6877 characters.
+ * 	The character set is JIS X 0208-1983 (originally JIS C 6226-1983).
+ * 	Escapes into:
+ * 		G0: ESC 2/4 4/2 ("$B")
+ * 		G1: ESC 2/4 2/9 4/2 ("$)B")
+ * 		G2: ESC 2/4 2/10 4/2 ("$*B")
+ * 		G3: ESC 2/4 2/11 4/2 ("$+B")
+ * #102 is "Teletex Primary Set of Graphic Characters" and is almost ASCII.
+ * 	Escapes into:
+ * 		G0: ESC 2/8 7/5 ("(u")
+ * 		G1: ESC 2/9 7/5 (")u")
+ * 		G2: ESC 2/10 7/5 ("*u")
+ * 		G3: ESC 2/11 7/5 ("+u")
+ *	It is almost identical to ASCII, except for ASCII position for '$'
+ * 	(DOLLAR SIGN) is filled with '¤' (CURRENCY SIGN), which is U+00A4.
+ * 	Also, ASCII positions for '`', '\', '^', '{', '}', '~' are marked
+ * 	as "should not be used".
+ * #103 is a supplementary set of characters used in combination with #102.
+ * 	Escapes into:
+ * 		G0: ESC 2/8 7/6 ("(v")
+ * 		G1: ESC 2/9 7/6 (")v")
+ * 		G2: ESC 2/10 7/6 ("*v")
+ * 		G3: ESC 2/11 7/6 ("+v")
+ * 	Some characters in that character set are combining characters,
+ * 	which can only be restrictively used with certain basic Latin letters.
+ * 	It can be thought of as a subset of #156 with the exception of 4/12
+ * 	which is UNDERLINE in #103 and absent in #156.
+ * #106 is a primary set of control functions, used in combination with #107.
+ * 	Escapes into:
+ * 		C0: ESC 2/1 4/5 ("!E")
+ * 	This set is so short I can list it here:
+ * 		0x08	BS	BACKSPACE	-- same as Unicode
+ * 		0x0a	LF	LINE FEED	-- same as Unicode
+ * 		0x0c	FF	FORM FEED	-- same as Unicode
+ * 		0x0d	CR	CARRIAGE RETURN	-- same as Unicode
+ * 		0x0e	LS1	LOCKING SHIFT ONE
+ * 		0x0f	LS0	LOCKING SHIFT ZERO
+ * 		0x19	SS2	SINGLE SHIFT TWO
+ * 		0x1a	SUB	SUBSTITUTE CHARACTER
+ * 		0x1b	ESC	ESCAPE		-- same as Unicode
+ * 		0x1d	SS3	SINGLE SHIFT THREE
+ *	The LS1 and LS0 are two magical functions which, respectively, invoke
+ * 	the currently designated G1 or G0 set into positions 2/1 to 7/14
+ * 	The SS2 and SS3, respectively, invoke one character of the
+ * 	currently designated set G2 and G3.
+ * 	The SUB is wholly equivalent to U+001a (SUBSTITUTE)
+ * #107 is a supplementary set of control functions, used with #106.
+ * 	Escapes into:
+ * 		C1: ESC 2/2 4/8 ('"H')
+ * 	This set contains three special control codes:
+ * 		0x8b	PLD	PARTIAL LINE DOWN	-- similar to <SUB>
+ * 		0x8c	PLU	PARTIAL LINE UP		-- sumilar to <SUP>
+ * 		0x9b	CSI	CONTROL SEQUENCE INTRODUCER
+ * 	This set is so out of world we can probably safely ignore it.
+ * #126 is a "Right-hand Part of the Latin/Greek Alphabet".
+ * 	Comprises of 90 characters, including accented letters.
+ * 	Escapes into:
+ * 		G1: ESC 2/13 4/6 ("-F")
+ * 		G2: ESC 2/14 4/6 (".F")
+ * 		G3: ESC 2/15 4/6 ("/F")
+ * 	Note: This Registration is a subset of ISO-IR 227.
+ * #144 is a "Cyrillic part of the Latin/Cyrillic Alphabet".
+ * 	Comprises of 95 characters.
+ * 	Escapes into:
+ * 		G1: ESC 2/13 4/12 ("-L")
+ * 		G2: ESC 2/14 4/12 (".L")
+ * 		G3: ESC 2/15 4/12 ("/L")
+ * #150 is a "Greek Primary Set of Graphic Characters".
+ * 	Comprises of 94 characters.
+ * 	Escapes into:
+ * 		G0: ESC 2/8 2/1 4/0 ("(!@")
+ * 		G1: ESC 2/9 2/1 4/0 (")!@")
+ * 		G2: ESC 2/10 2/1 4/0 ("*!@")
+ * 		G3: ESC 2/11 2/1 4/0 ("+!@")
+ * #153 is a "Basic Cyrillic Character Set for 8-bit codes".
+ * 	Comprises of 68 characters.
+ * 	Escapes into:
+ * 		G1: ESC 2/13 4/15 ("-O")
+ * 		G2: ESC 2/14 4/15 (".O")
+ * 		G3: ESC 2/15 4/15 ("/O")
+ * #156 is a "Supplementary Set of ISO/IEC 6937:1992" for use with #6
+ * 	Comprises of 87 characters.
+ * 	Escapes into:
+ * 		G1: ESC 2/13 5/2 ("-R")
+ * 		G2: ESC 2/14 5/2 (".R")
+ * 		G3: ESC 2/15 5/2 ("/R")
+ * #164 is a "Hebrew Supplementary Set of Graphic Characters"
+ * 	Comprises of 27 characters.
+ * 	Escapes into:
+ * 		G1: ESC 2/13 5/3 ("-S")
+ * 		G2: ESC 2/14 5/3 (".S")
+ * 		G3: ESC 2/15 5/3 ("/S")
+ * #165 is a set of "Codes of the Chinese graphic character set"
+ * 	Is a multiple-byte set of 8446 characters.
+ * 	Escapes into:
+ * 		G0: ESC 2/4 2/8 4/5 ("$(E")
+ * 		G1: ESC 2/4 2/9 4/5 ("$)E")
+ * 		G2: ESC 2/4 2/10 4/5 ("$*E")
+ * 		G3: ESC 2/4 2/11 4/5 ("$+E")
+ * #168 is a "Japanese Graphic Character Set for Information Interchange"
+ * 	A multiple-byte set of 6879 characters updated from #87.
+ * 	Escapes into:
+ * 		G0: ESC 2/6 4/0 ESC 2/4 4/2 ("&@" "$B")
+ * 		G1: ESC 2/6 4/0 ESC 2/4 2/9 4/2 ("&@" "$)B")
+ * 		G2: ESC 2/6 4/0 ESC 2/4 2/10 4/2 ("&@" "$*B")
+ * 		G3: ESC 2/6 4/0 ESC 2/4 2/11 4/2 ("&@" "$+B")
+ */
+
diff --git a/src/UTCTime.c b/src/UTCTime.c
index 2a27718..0abe1db 100644
--- a/src/UTCTime.c
+++ b/src/UTCTime.c
@@ -23,6 +23,11 @@
 	(ASN_TAG_CLASS_UNIVERSAL | (26 << 2)),  /* [UNIVERSAL 26] IMPLICIT ...*/
 	(ASN_TAG_CLASS_UNIVERSAL | (4 << 2))    /* ... OCTET STRING */
 };
+static asn_per_constraints_t asn_DEF_UTCTime_constraints = {
+        { APC_CONSTRAINED, 7, 7, 0x20, 0x7e },  /* Value */
+        { APC_SEMI_CONSTRAINED, -1, -1, 0, 0 }, /* Size */
+        0, 0
+};
 asn_TYPE_descriptor_t asn_DEF_UTCTime = {
 	"UTCTime",
 	"UTCTime",
@@ -33,7 +38,8 @@
 	OCTET_STRING_encode_der,    /* Implemented in terms of OCTET STRING */
 	OCTET_STRING_decode_xer_utf8,
 	UTCTime_encode_xer,
-	0, 0,
+	OCTET_STRING_decode_uper,
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_UTCTime_tags,
 	sizeof(asn_DEF_UTCTime_tags)
@@ -41,7 +47,7 @@
 	asn_DEF_UTCTime_tags,
 	sizeof(asn_DEF_UTCTime_tags)
 	  / sizeof(asn_DEF_UTCTime_tags[0]),
-	0,	/* No PER visible constraints */
+	&asn_DEF_UTCTime_constraints,
 	0, 0,	/* No members */
 	0	/* No specifics */
 };
@@ -60,7 +66,7 @@
 	errno = EPERM;			/* Just an unlikely error code */
 	tloc = asn_UT2time(st, 0, 0);
 	if(tloc == -1 && errno != EPERM) {
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: Invalid time format: %s (%s:%d)",
 			td->name, strerror(errno), __FILE__, __LINE__);
 		return -1;
diff --git a/src/UTF8String.c b/src/UTF8String.c
index e3f7388..7e73d77 100644
--- a/src/UTF8String.c
+++ b/src/UTF8String.c
@@ -23,7 +23,8 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	OCTET_STRING_encode_xer_utf8,
-	0, 0,
+	OCTET_STRING_decode_uper,
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_UTF8String_tags,
 	sizeof(asn_DEF_UTF8String_tags)
@@ -72,26 +73,26 @@
 	ssize_t len = UTF8String_length((const UTF8String_t *)sptr);
 	switch(len) {
 	case U8E_EINVAL:
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: value not given", td->name);
 		break;
 	case U8E_TRUNC:
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: truncated UTF-8 sequence (%s:%d)",
 			td->name, __FILE__, __LINE__);
 		break;
 	case U8E_ILLSTART:
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: UTF-8 illegal start of encoding (%s:%d)",
 			td->name, __FILE__, __LINE__);
 		break;
 	case U8E_NOTCONT:
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: UTF-8 not continuation (%s:%d)",
 			td->name, __FILE__, __LINE__);
 		break;
 	case U8E_NOTMIN:
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: UTF-8 not minimal sequence (%s:%d)",
 			td->name, __FILE__, __LINE__);
 		break;
diff --git a/src/UniversalString.c b/src/UniversalString.c
index a39e569..7d16781 100644
--- a/src/UniversalString.c
+++ b/src/UniversalString.c
@@ -13,6 +13,16 @@
 	(ASN_TAG_CLASS_UNIVERSAL | (28 << 2)),	/* [UNIVERSAL 28] IMPLICIT ...*/
 	(ASN_TAG_CLASS_UNIVERSAL | (4 << 2))	/* ... OCTET STRING */
 };
+static asn_OCTET_STRING_specifics_t asn_DEF_UniversalString_specs = {
+	sizeof(UniversalString_t),
+	offsetof(UniversalString_t, _asn_ctx),
+	ASN_OSUBV_U32	/* 32-bits character */
+};
+static asn_per_constraints_t asn_DEF_UniversalString_constraints = {
+	{ APC_CONSTRAINED, 32, 32, 0, 2147483647 },
+	{ APC_SEMI_CONSTRAINED, -1, -1, 0, 0 },
+	0, 0
+};
 asn_TYPE_descriptor_t asn_DEF_UniversalString = {
 	"UniversalString",
 	"UniversalString",
@@ -23,7 +33,8 @@
 	OCTET_STRING_encode_der,
 	UniversalString_decode_xer,	/* Convert from UTF-8 */
 	UniversalString_encode_xer,	/* Convert into UTF-8 */
-	0, 0,
+	OCTET_STRING_decode_uper,
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_UniversalString_tags,
 	sizeof(asn_DEF_UniversalString_tags)
@@ -31,9 +42,9 @@
 	asn_DEF_UniversalString_tags,
 	sizeof(asn_DEF_UniversalString_tags)
 	  / sizeof(asn_DEF_UniversalString_tags[0]),
-	0,	/* No PER visible constraints */
+	&asn_DEF_UniversalString_constraints,
 	0, 0,	/* No members */
-	0	/* No specifics */
+	&asn_DEF_UniversalString_specs
 };
 
 
diff --git a/src/VideotexString.c b/src/VideotexString.c
index 5f5f33d..df7233e 100644
--- a/src/VideotexString.c
+++ b/src/VideotexString.c
@@ -22,7 +22,8 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_hex,
 	OCTET_STRING_encode_xer,
-	0, 0,
+	OCTET_STRING_decode_uper,    /* Implemented in terms of OCTET STRING */
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_VideotexString_tags,
 	sizeof(asn_DEF_VideotexString_tags)
diff --git a/src/VisibleString.c b/src/VisibleString.c
index 8796582..3487b6f 100644
--- a/src/VisibleString.c
+++ b/src/VisibleString.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2003, 2006 Lev Walkin <vlm@lionet.info>. All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 #include <asn_internal.h>
@@ -12,6 +12,11 @@
 	(ASN_TAG_CLASS_UNIVERSAL | (26 << 2)),	/* [UNIVERSAL 26] IMPLICIT ...*/
 	(ASN_TAG_CLASS_UNIVERSAL | (4 << 2))	/* ... OCTET STRING */
 };
+static asn_per_constraints_t asn_DEF_VisibleString_constraints = {
+	{ APC_CONSTRAINED, 7, 7, 0x20, 0x7e },	/* Value */
+	{ APC_SEMI_CONSTRAINED, -1, -1, 0, 0 },	/* Size */
+	0, 0
+};
 asn_TYPE_descriptor_t asn_DEF_VisibleString = {
 	"VisibleString",
 	"VisibleString",
@@ -22,7 +27,8 @@
 	OCTET_STRING_encode_der,
 	OCTET_STRING_decode_xer_utf8,
 	OCTET_STRING_encode_xer_utf8,
-	0, 0,
+	OCTET_STRING_decode_uper,
+	OCTET_STRING_encode_uper,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_VisibleString_tags,
 	sizeof(asn_DEF_VisibleString_tags)
@@ -30,7 +36,7 @@
 	asn_DEF_VisibleString_tags,
 	sizeof(asn_DEF_VisibleString_tags)
 	  / sizeof(asn_DEF_VisibleString_tags[0]),
-	0,	/* No PER visible constraints */
+	&asn_DEF_VisibleString_constraints,
 	0, 0,	/* No members */
 	0	/* No specifics */
 };
@@ -52,7 +58,7 @@
 		 */
 		for(; buf < end; buf++) {
 			if(*buf < 0x20 || *buf > 0x7e) {
-				_ASN_CTFAIL(app_key, td,
+				_ASN_CTFAIL(app_key, td, sptr,
 					"%s: value byte %ld (%d) "
 					"not in VisibleString alphabet (%s:%d)",
 					td->name,
@@ -63,7 +69,7 @@
 			}
 		}
 	} else {
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: value not given (%s:%d)",
 			td->name, __FILE__, __LINE__);
 		return -1;
diff --git a/src/constr_CHOICE.c b/src/constr_CHOICE.c
index b8d6fa9..a9eb719 100644
--- a/src/constr_CHOICE.c
+++ b/src/constr_CHOICE.c
@@ -1,10 +1,11 @@
 /*
- * 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.
  */
 #include <asn_internal.h>
 #include <constr_CHOICE.h>
+#include <per_opentype.h>
 
 /*
  * Number of bytes left for this structure.
@@ -482,7 +483,7 @@
 	int present;
 
 	if(!sptr) {
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: value not given (%s:%d)",
 			td->name, __FILE__, __LINE__);
 		return -1;
@@ -501,7 +502,7 @@
 			if(!memb_ptr) {
 				if(elm->optional)
 					return 0;
-				_ASN_CTFAIL(app_key, td,
+				_ASN_CTFAIL(app_key, td, sptr,
 					"%s: mandatory CHOICE element %s absent (%s:%d)",
 					td->name, elm->name, __FILE__, __LINE__);
 				return -1;
@@ -524,7 +525,7 @@
 			return ret;
 		}
 	} else {
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: no CHOICE element given (%s:%d)",
 			td->name, __FILE__, __LINE__);
 		return -1;
@@ -871,8 +872,6 @@
 		value += specs->ext_start;
 		if(value >= td->elements_count)
 			_ASN_DECODE_FAILED;
-		ASN_DEBUG("NOT IMPLEMENTED YET");
-		_ASN_DECODE_FAILED;
 	}
 
 	/* Adjust if canonical order is different from natural order */
@@ -892,11 +891,17 @@
 	}
 	ASN_DEBUG("Discovered CHOICE %s encodes %s", td->name, elm->name);
 
-	rv = elm->type->uper_decoder(opt_codec_ctx, elm->type,
+	if(ct && ct->range_bits >= 0) {
+		rv = elm->type->uper_decoder(opt_codec_ctx, elm->type,
 			elm->per_constraints, memb_ptr2, pd);
+	} else {
+		rv = uper_open_type_get(opt_codec_ctx, elm->type,
+			elm->per_constraints, memb_ptr2, pd);
+	}
+
 	if(rv.code != RC_OK)
-		ASN_DEBUG("Failed to decode %s in %s (CHOICE)",
-			elm->name, td->name);
+		ASN_DEBUG("Failed to decode %s in %s (CHOICE) %d",
+			elm->name, td->name, rv.code);
 	return rv;
 }
    
@@ -951,18 +956,6 @@
 		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 */
@@ -972,8 +965,24 @@
 		memb_ptr = (char *)sptr + elm->memb_offset;
 	}
 
-	return elm->type->uper_encoder(elm->type, elm->per_constraints,
+	if(ct && ct->range_bits >= 0) {
+		if(per_put_few_bits(po, present, ct->range_bits))
+			_ASN_ENCODE_FAILED;
+
+		return elm->type->uper_encoder(elm->type, elm->per_constraints,
 			memb_ptr, po);
+	} else {
+		asn_enc_rval_t rval;
+		if(specs->ext_start == -1)
+			_ASN_ENCODE_FAILED;
+		if(uper_put_nsnnwn(po, present - specs->ext_start))
+			_ASN_ENCODE_FAILED;
+		if(uper_open_type_put(elm->type, elm->per_constraints,
+			memb_ptr, po))
+			_ASN_ENCODE_FAILED;
+		rval.encoded = 0;
+		_ASN_ENCODED_OK(rval);
+	}
 }
    
 
diff --git a/src/constr_SEQUENCE.c b/src/constr_SEQUENCE.c
index b769434..db3c925 100644
--- a/src/constr_SEQUENCE.c
+++ b/src/constr_SEQUENCE.c
@@ -1,10 +1,11 @@
 /*-
- * 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.
  */
 #include <asn_internal.h>
 #include <constr_SEQUENCE.h>
+#include <per_opentype.h>
 
 /*
  * Number of bytes left for this structure.
@@ -346,7 +347,8 @@
 			 * or an extension (...),
 			 * or an end of the indefinite-length structure.
 			 */
-			if(!IN_EXTENSION_GROUP(specs, edx)) {
+			if(!IN_EXTENSION_GROUP(specs,
+				edx + elements[edx].optional)) {
 				ASN_DEBUG("Unexpected tag %s (at %d)",
 					ber_tlv_tag_string(tlv_tag), edx);
 				ASN_DEBUG("Expected tag %s (%s)%s",
@@ -358,7 +360,10 @@
 			} else {
 				/* Skip this tag */
 				ssize_t skip;
+				edx += elements[edx].optional;
 
+				ASN_DEBUG("Skipping unexpected %s (at %d)",
+					ber_tlv_tag_string(tlv_tag), edx);
 				skip = ber_skip_length(opt_codec_ctx,
 					BER_TLV_CONSTRUCTED(ptr),
 					(const char *)ptr + tag_len,
@@ -976,7 +981,7 @@
 	int edx;
 
 	if(!sptr) {
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: value not given (%s:%d)",
 			td->name, __FILE__, __LINE__);
 		return -1;
@@ -994,7 +999,7 @@
 			if(!memb_ptr) {
 				if(elm->optional)
 					continue;
-				_ASN_CTFAIL(app_key, td,
+				_ASN_CTFAIL(app_key, td, sptr,
 				"%s: mandatory element %s absent (%s:%d)",
 				td->name, elm->name, __FILE__, __LINE__);
 				return -1;
@@ -1027,7 +1032,7 @@
 	asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) {
 	asn_SEQUENCE_specifics_t *specs = (asn_SEQUENCE_specifics_t *)td->specifics;
 	void *st = *sptr;	/* Target structure. */
-	int extpresent = 0;	/* Extension additions are present */
+	int extpresent;		/* Extension additions are present */
 	uint8_t *opres;		/* Presence of optional root members */
 	asn_per_data_t opmd;
 	asn_dec_rval_t rv;
@@ -1049,9 +1054,12 @@
 	if(specs->ext_before >= 0) {
 		extpresent = per_get_few_bits(pd, 1);
 		if(extpresent < 0) _ASN_DECODE_STARVED;
+	} else {
+		extpresent = 0;
 	}
 
 	/* Prepare a place and read-in the presence bitmap */
+	memset(&opmd, 0, sizeof(opmd));
 	if(specs->roms_count) {
 		opres = (uint8_t *)MALLOC(((specs->roms_count + 7) >> 3) + 1);
 		if(!opres) _ASN_DECODE_FAILED;
@@ -1061,24 +1069,24 @@
 			_ASN_DECODE_STARVED;
 		}
 		opmd.buffer = opres;
-		opmd.nboff = 0;
 		opmd.nbits = specs->roms_count;
 		ASN_DEBUG("Read in presence bitmap for %s of %d bits (%x..)",
 			td->name, specs->roms_count, *opres);
 	} else {
 		opres = 0;
-		memset(&opmd, 0, sizeof opmd);
 	}
 
 	/*
 	 * Get the sequence ROOT elements.
 	 */
-	for(edx = 0; edx < ((specs->ext_before < 0)
-			? td->elements_count : specs->ext_before + 1); edx++) {
+	for(edx = 0; 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 */
 
+		if(IN_EXTENSION_GROUP(specs, edx))
+			continue;
+
 		/* Fetch the pointer to this member */
 		if(elm->flags & ATF_POINTER) {
 			memb_ptr2 = (void **)((char *)st + elm->memb_offset);
@@ -1101,6 +1109,7 @@
 						FREEMEM(opres);
 						_ASN_DECODE_FAILED;
 					}
+					ASN_DEBUG("Filled-in default");
 				}
 				/* The member is just not present */
 				continue;
@@ -1120,50 +1129,176 @@
 		}
 	}
 
+	/* Optionality map is not needed anymore */
+	FREEMEM(opres);
+
 	/*
 	 * Deal with extensions.
 	 */
 	if(extpresent) {
-		ASN_DEBUG("Extensibility for %s: NOT IMPLEMENTED", td->name);
-		_ASN_DECODE_FAILED;
-	} else {
-		for(edx = specs->roms_count; edx < specs->roms_count
-				+ specs->aoms_count; edx++) {
-			asn_TYPE_member_t *elm = &td->elements[edx];
-			void *memb_ptr;		/* Pointer to the member */
-			void **memb_ptr2;	/* Pointer to that pointer */
+		ssize_t bmlength;
+		uint8_t *epres;		/* Presence of extension members */
+		asn_per_data_t epmd;
 
-			if(!elm->default_value) continue;
+		bmlength = uper_get_nslength(pd);
+		if(bmlength < 0) _ASN_DECODE_STARVED;
 
-			/* Fetch the pointer to this member */
-			if(elm->flags & ATF_POINTER) {
-				memb_ptr2 = (void **)((char *)st
-						+ elm->memb_offset);
-			} else {
-				memb_ptr = (char *)st + elm->memb_offset;
-				memb_ptr2 = &memb_ptr;
+		ASN_DEBUG("Extensions %d present in %s", bmlength, td->name);
+
+		epres = (uint8_t *)MALLOC((bmlength + 15) >> 3);
+		if(!epres) _ASN_DECODE_STARVED;
+
+		/* Get the extensions map */
+		if(per_get_many_bits(pd, epres, 0, bmlength))
+			_ASN_DECODE_STARVED;
+
+		memset(&epmd, 0, sizeof(epmd));
+		epmd.buffer = epres;
+		epmd.nbits = bmlength;
+		ASN_DEBUG("Read in extensions bitmap for %s of %d bits (%x..)",
+			td->name, bmlength, *epres);
+
+	    /* Go over extensions and read them in */
+	    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)) {
+			ASN_DEBUG("%d is not extension", edx);
+			continue;
+		}
+
+		/* Fetch the pointer to this member */
+		if(elm->flags & ATF_POINTER) {
+			memb_ptr2 = (void **)((char *)st + elm->memb_offset);
+		} else {
+			memb_ptr = (void *)((char *)st + elm->memb_offset);
+			memb_ptr2 = &memb_ptr;
+		}
+
+		present = per_get_few_bits(&epmd, 1);
+		if(present <= 0) {
+			if(present < 0) break;	/* No more extensions */
+			continue;
+		}
+
+		ASN_DEBUG("Decoding member %s in %s %p", elm->name, td->name, *memb_ptr2);
+		rv = uper_open_type_get(opt_codec_ctx, elm->type,
+			elm->per_constraints, memb_ptr2, pd);
+		if(rv.code != RC_OK) {
+			FREEMEM(epres);
+			return rv;
+		}
+	    }
+
+		/* Skip over overflow extensions which aren't present
+		 * in this system's version of the protocol */
+		for(;;) {
+			ASN_DEBUG("Getting overflow extensions");
+			switch(per_get_few_bits(&epmd, 1)) {
+			case -1: break;
+			case 0: continue;
+			default:
+				if(uper_open_type_skip(opt_codec_ctx, pd)) {
+					FREEMEM(epres);
+					_ASN_DECODE_STARVED;
+				}
 			}
+			break;
+		}
 
-			/* Set default value */
-			if(elm->default_value(1, memb_ptr2)) {
-				FREEMEM(opres);
-				_ASN_DECODE_FAILED;
-			}
+		FREEMEM(epres);
+	}
+
+	/* Fill DEFAULT members in extensions */
+	for(edx = specs->roms_count; edx < specs->roms_count
+			+ specs->aoms_count; edx++) {
+		asn_TYPE_member_t *elm = &td->elements[edx];
+		void **memb_ptr2;	/* Pointer to member pointer */
+
+		if(!elm->default_value) continue;
+
+		/* Fetch the pointer to this member */
+		if(elm->flags & ATF_POINTER) {
+			memb_ptr2 = (void **)((char *)st
+					+ elm->memb_offset);
+			if(*memb_ptr2) continue;
+		} else {
+			continue;	/* Extensions are all optionals */
+		}
+
+		/* Set default value */
+		if(elm->default_value(1, memb_ptr2)) {
+			_ASN_DECODE_FAILED;
 		}
 	}
 
 	rv.consumed = 0;
 	rv.code = RC_OK;
-	FREEMEM(opres);
 	return rv;
 }
 
+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 exts_present = 0;
+	int exts_count = 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)) {
+			ASN_DEBUG("%s (@%d) is not extension", elm->type->name, 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 %s (@%d) present => %d",
+			elm->type->name, edx, present);
+		exts_count++;
+		exts_present += 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_open_type_put(elm->type,
+				elm->per_constraints, *memb_ptr2, po2))
+			return -1;
+
+	}
+
+	return exts_present ? exts_count : 0;
+}
+
 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;
 
@@ -1175,8 +1310,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++) {
@@ -1212,14 +1357,21 @@
 	}
 
 	/*
-	 * Get the sequence ROOT elements.
+	 * Encode the sequence ROOT elements.
 	 */
-	for(edx = 0; edx < ((specs->ext_before < 0)
-			? td->elements_count : specs->ext_before + 1); edx++) {
+	ASN_DEBUG("ext_after = %d, ec = %d, eb = %d", specs->ext_after, td->elements_count, specs->ext_before);
+	for(edx = 0; edx < ((specs->ext_after < 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 */
 
+		if(IN_EXTENSION_GROUP(specs, edx))
+			continue;
+
+		ASN_DEBUG("About to encode %s", elm->type->name);
+
 		/* Fetch the pointer to this member */
 		if(elm->flags & ATF_POINTER) {
 			memb_ptr2 = (void **)((char *)sptr + elm->memb_offset);
@@ -1240,12 +1392,32 @@
 		if(elm->default_value && elm->default_value(0, memb_ptr2) == 1)
 			continue;
 
+		ASN_DEBUG("Encoding %s->%s", td->name, elm->name);
 		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/src/constr_SET.c b/src/constr_SET.c
index 9b6012f..ecf5661 100644
--- a/src/constr_SET.c
+++ b/src/constr_SET.c
@@ -6,12 +6,6 @@
 #include <asn_internal.h>
 #include <constr_SET.h>
 
-#ifndef	WIN32
-#include <netinet/in.h>	/* for ntohl() */
-#else
-#include <winsock2.h>	/* for ntohl() */
-#endif
-
 /* Check that all the mandatory members are present */
 static int _SET_is_populated(asn_TYPE_descriptor_t *td, void *st);
 
@@ -411,7 +405,7 @@
 
 		midx = edx/(8 * sizeof(specs->_mandatory_elements[0]));
 		pres = ((unsigned int *)((char *)st+specs->pres_offset))[midx];
-		must = ntohl(specs->_mandatory_elements[midx]);
+		must = sys_ntohl(specs->_mandatory_elements[midx]);
 
 		if((pres & must) == must) {
 			/*
@@ -945,7 +939,7 @@
 	int edx;
 
 	if(!sptr) {
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: value not given (%s:%d)",
 			td->name, __FILE__, __LINE__);
 		return -1;
@@ -963,7 +957,7 @@
 			if(!memb_ptr) {
 				if(elm->optional)
 					continue;
-				_ASN_CTFAIL(app_key, td,
+				_ASN_CTFAIL(app_key, td, sptr,
 				"%s: mandatory element %s absent (%s:%d)",
 				td->name, elm->name, __FILE__, __LINE__);
 				return -1;
diff --git a/src/constr_SET_OF.c b/src/constr_SET_OF.c
index 09f27db..11eac57 100644
--- a/src/constr_SET_OF.c
+++ b/src/constr_SET_OF.c
@@ -227,6 +227,8 @@
 			}
 			/* Fall through */
 		case RC_FAIL: /* Fatal error */
+			ASN_STRUCT_FREE(*elm->type, ctx->ptr);
+			ctx->ptr = 0;
 			RETURN(RC_FAIL);
 		} /* switch(rval) */
 		
@@ -787,8 +789,10 @@
 void
 SET_OF_free(asn_TYPE_descriptor_t *td, void *ptr, int contents_only) {
 	if(td && ptr) {
+		asn_SET_OF_specifics_t *specs;
 		asn_TYPE_member_t *elm = td->elements;
 		asn_anonymous_set_ *list = _A_SET_FROM_VOID(ptr);
+		asn_struct_ctx_t *ctx;	/* Decoder context */
 		int i;
 
 		/*
@@ -804,6 +808,13 @@
 
 		asn_set_empty(list);	/* Remove (list->array) */
 
+		specs = (asn_SET_OF_specifics_t *)td->specifics;
+		ctx = (asn_struct_ctx_t *)((char *)ptr + specs->ctx_offset);
+		if(ctx->ptr) {
+			ASN_STRUCT_FREE(*elm->type, ctx->ptr);
+			ctx->ptr = 0;
+		}
+
 		if(!contents_only) {
 			FREEMEM(ptr);
 		}
@@ -819,7 +830,7 @@
 	int i;
 
 	if(!sptr) {
-		_ASN_CTFAIL(app_key, td,
+		_ASN_CTFAIL(app_key, td, sptr,
 			"%s: value not given (%s:%d)",
 			td->name, __FILE__, __LINE__);
 		return -1;
@@ -921,7 +932,7 @@
 				ASN_DEBUG("Failed to add element into %s",
 					td->name);
 				/* Fall through */
-				rv.code == RC_FAIL;
+				rv.code = RC_FAIL;
 			} else {
 				ASN_DEBUG("Failed decoding %s of %s (SET OF)",
 					elm->type->name, td->name);
diff --git a/src/per_decoder.c b/src/per_decoder.c
index 16dee36..2b3d2e2 100644
--- a/src/per_decoder.c
+++ b/src/per_decoder.c
@@ -2,6 +2,40 @@
 #include <asn_internal.h>
 #include <per_decoder.h>
 
+/*
+ * Decode a "Production of a complete encoding", X.691#10.1.
+ * The complete encoding contains at least one byte, and is an integral
+ * multiple of 8 bytes.
+ */
+asn_dec_rval_t
+uper_decode_complete(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, void **sptr, const void *buffer, size_t size) {
+	asn_dec_rval_t rval;
+
+	rval = uper_decode(opt_codec_ctx, td, sptr, buffer, size, 0, 0);
+	if(rval.consumed) {
+		/*
+		 * We've always given 8-aligned data,
+		 * so convert bits to integral bytes.
+		 */
+		rval.consumed += 7;
+		rval.consumed >>= 3;
+	} else if(rval.code == RC_OK) {
+		if(size) {
+			if(((uint8_t *)buffer)[0] == 0) {
+				rval.consumed = 1;	/* 1 byte */
+			} else {
+				ASN_DEBUG("Expecting single zeroed byte");
+				rval.code = RC_FAIL;
+			}
+		} else {
+			/* Must contain at least 8 bits. */
+			rval.code = RC_WMORE;
+		}
+	}
+
+	return rval;
+}
+
 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, int unused_bits) {
 	asn_codec_ctx_t s_codec_ctx;
@@ -30,6 +64,7 @@
 	}
 
 	/* Fill in the position indicator */
+	memset(&pd, 0, sizeof(pd));
 	pd.buffer = (const uint8_t *)buffer;
 	pd.nboff = skip_bits;
 	pd.nbits = 8 * size - unused_bits; /* 8 is CHAR_BIT from <limits.h> */
@@ -46,6 +81,9 @@
 		/* Return the number of consumed bits */
 		rval.consumed = ((pd.buffer - (const uint8_t *)buffer) << 3)
 					+ pd.nboff - skip_bits;
+		ASN_DEBUG("PER decoding consumed %d, counted %d",
+			rval.consumed, pd.moved);
+		assert(rval.consumed == pd.moved);
 	} else {
 		/* PER codec is not a restartable */
 		rval.consumed = 0;
diff --git a/src/per_encoder.c b/src/per_encoder.c
index 614dd23..f4bace0 100644
--- a/src/per_encoder.c
+++ b/src/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,71 @@
 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;
+		ASN_DEBUG("Complete encoded in %d bits", er.encoded);
+		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 +114,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/src/per_opentype.c b/src/per_opentype.c
new file mode 100644
index 0000000..c749c8c
--- /dev/null
+++ b/src/per_opentype.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2007 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <per_support.h>
+#include <constr_TYPE.h>
+#include <per_opentype.h>
+
+typedef struct uper_ugot_key {
+	asn_per_data_t oldpd;	/* Old per data source */
+	size_t unclaimed;
+	size_t ot_moved;	/* Number of bits moved by OT processing */
+	int repeat;
+} uper_ugot_key;
+
+static int uper_ugot_refill(asn_per_data_t *pd);
+static int per_skip_bits(asn_per_data_t *pd, int skip_nbits);
+static asn_dec_rval_t uper_sot_suck(asn_codec_ctx_t *, asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd);
+
+int asn_debug_indent;
+
+/*
+ * Encode an "open type field".
+ * #10.1, #10.2
+ */
+int
+uper_open_type_put(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
+	void *buf;
+	void *bptr;
+	ssize_t size;
+	size_t toGo;
+
+	ASN_DEBUG("Open type put %s ...", td->name);
+
+	size = uper_encode_to_new_buffer(td, constraints, sptr, &buf);
+	if(size <= 0) return -1;
+
+	for(bptr = buf, toGo = size; toGo;) {
+		ssize_t maySave = uper_put_length(po, toGo);
+		if(maySave < 0) break;
+		if(per_put_many_bits(po, bptr, maySave * 8)) break;
+		bptr = (char *)bptr + maySave;
+		toGo -= maySave;
+	}
+
+	FREEMEM(buf);
+	if(toGo) return -1;
+
+	ASN_DEBUG("Open type put %s of length %d + overhead (1byte?)",
+		td->name, size);
+
+	return 0;
+}
+
+static asn_dec_rval_t
+uper_open_type_get_simple(asn_codec_ctx_t *ctx, asn_TYPE_descriptor_t *td,
+	asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) {
+	asn_dec_rval_t rv;
+	ssize_t chunk_bytes;
+	int repeat;
+	uint8_t *buf = 0;
+	size_t bufLen = 0;
+	size_t bufSize = 0;
+	asn_per_data_t spd;
+	size_t padding;
+
+	_ASN_STACK_OVERFLOW_CHECK(ctx);
+
+	ASN_DEBUG("Getting open type %s...", td->name);
+
+	do {
+		chunk_bytes = uper_get_length(pd, -1, &repeat);
+		if(chunk_bytes < 0) {
+			FREEMEM(buf);
+			_ASN_DECODE_STARVED;
+		}
+		if(bufLen + chunk_bytes > bufSize) {
+			void *ptr;
+			bufSize = chunk_bytes + (bufSize << 2);
+			ptr = REALLOC(buf, bufSize);
+			if(!ptr) {
+				FREEMEM(buf);
+				_ASN_DECODE_FAILED;
+			}
+			buf = ptr;
+		}
+		if(per_get_many_bits(pd, buf + bufLen, 0, chunk_bytes << 3)) {
+			FREEMEM(buf);
+			_ASN_DECODE_STARVED;
+		}
+		bufLen += chunk_bytes;
+	} while(repeat);
+
+	ASN_DEBUG("Getting open type %s encoded in %d bytes", td->name,
+		bufLen);
+
+	memset(&spd, 0, sizeof(spd));
+	spd.buffer = buf;
+	spd.nbits = bufLen << 3;
+
+	asn_debug_indent += 4;
+	rv = td->uper_decoder(ctx, td, constraints, sptr, &spd);
+	asn_debug_indent -= 4;
+
+	if(rv.code == RC_OK) {
+		/* Check padding validity */
+		padding = spd.nbits - spd.nboff;
+		if(padding < 8 && per_get_few_bits(&spd, padding) == 0) {
+			/* Everything is cool */
+			FREEMEM(buf);
+			return rv;
+		}
+		FREEMEM(buf);
+		if(padding >= 8) {
+			ASN_DEBUG("Too large padding %d in open type", padding);
+			_ASN_DECODE_FAILED;
+		} else {
+			ASN_DEBUG("Non-zero padding");
+			_ASN_DECODE_FAILED;
+		}
+	} else {
+		FREEMEM(buf);
+		/* rv.code could be RC_WMORE, nonsense in this context */
+		rv.code = RC_FAIL; /* Noone would give us more */
+	}
+
+	return rv;
+}
+
+static asn_dec_rval_t GCC_NOTUSED
+uper_open_type_get_complex(asn_codec_ctx_t *ctx, asn_TYPE_descriptor_t *td,
+	asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) {
+	uper_ugot_key arg;
+	asn_dec_rval_t rv;
+	ssize_t padding;
+
+	_ASN_STACK_OVERFLOW_CHECK(ctx);
+
+	ASN_DEBUG("Getting open type %s from %s", td->name,
+		per_data_string(pd));
+	arg.oldpd = *pd;
+	arg.unclaimed = 0;
+	arg.ot_moved = 0;
+	arg.repeat = 1;
+	pd->refill = uper_ugot_refill;
+	pd->refill_key = &arg;
+	pd->nbits = pd->nboff;	/* 0 good bits at this point, will refill */
+	pd->moved = 0;	/* This now counts the open type size in bits */
+
+	asn_debug_indent += 4;
+	rv = td->uper_decoder(ctx, td, constraints, sptr, pd);
+	asn_debug_indent -= 4;
+
+#define	UPDRESTOREPD	do {						\
+	/* buffer and nboff are valid, preserve them. */		\
+	pd->nbits = arg.oldpd.nbits - (pd->moved - arg.ot_moved);	\
+	pd->moved = arg.oldpd.moved + (pd->moved - arg.ot_moved);	\
+	pd->refill = arg.oldpd.refill;					\
+	pd->refill_key = arg.oldpd.refill_key;				\
+  } while(0)
+
+	if(rv.code != RC_OK) {
+		UPDRESTOREPD;
+		return rv;
+	}
+
+	ASN_DEBUG("OpenType %s pd%s old%s unclaimed=%d, repeat=%d"
+		, td->name,
+		per_data_string(pd),
+		per_data_string(&arg.oldpd),
+		arg.unclaimed, arg.repeat);
+
+	padding = pd->moved % 8;
+	if(padding) {
+		int32_t pvalue;
+		if(padding > 7) {
+			ASN_DEBUG("Too large padding %d in open type",
+				padding);
+			rv.code = RC_FAIL;
+			UPDRESTOREPD;
+			return rv;
+		}
+		padding = 8 - padding;
+		ASN_DEBUG("Getting padding of %d bits", padding);
+		pvalue = per_get_few_bits(pd, padding);
+		switch(pvalue) {
+		case -1:
+			ASN_DEBUG("Padding skip failed");
+			UPDRESTOREPD;
+			_ASN_DECODE_STARVED;
+		case 0: break;
+		default:
+			ASN_DEBUG("Non-blank padding (%d bits 0x%02x)",
+				padding, (int)pvalue);
+			UPDRESTOREPD;
+			_ASN_DECODE_FAILED;
+		}
+	}
+	if(pd->nboff != pd->nbits) {
+		ASN_DEBUG("Open type %s overhead pd%s old%s", td->name,
+			per_data_string(pd), per_data_string(&arg.oldpd));
+		if(1) {
+			UPDRESTOREPD;
+			_ASN_DECODE_FAILED;
+		} else {
+			arg.unclaimed += pd->nbits - pd->nboff;
+		}
+	}
+
+	/* Adjust pd back so it points to original data */
+	UPDRESTOREPD;
+
+	/* Skip data not consumed by the decoder */
+	if(arg.unclaimed) ASN_DEBUG("Getting unclaimed %d", arg.unclaimed);
+	if(arg.unclaimed) {
+		switch(per_skip_bits(pd, arg.unclaimed)) {
+		case -1:
+			ASN_DEBUG("Claim of %d failed", arg.unclaimed);
+			_ASN_DECODE_STARVED;
+		case 0:
+			ASN_DEBUG("Got claim of %d", arg.unclaimed);
+			break;
+		default:
+			/* Padding must be blank */
+			ASN_DEBUG("Non-blank unconsumed padding");
+			_ASN_DECODE_FAILED;
+		}
+		arg.unclaimed = 0;
+	}
+
+	if(arg.repeat) {
+		ASN_DEBUG("Not consumed the whole thing");
+		rv.code = RC_FAIL;
+		return rv;
+	}
+
+	return rv;
+}
+
+
+asn_dec_rval_t
+uper_open_type_get(asn_codec_ctx_t *ctx, asn_TYPE_descriptor_t *td,
+	asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) {
+
+	return uper_open_type_get_simple(ctx, td, constraints,
+		sptr, pd);
+
+}
+
+int
+uper_open_type_skip(asn_codec_ctx_t *ctx, asn_per_data_t *pd) {
+	asn_TYPE_descriptor_t s_td;
+	asn_dec_rval_t rv;
+
+	s_td.name = "<unknown extension>";
+	s_td.uper_decoder = uper_sot_suck;
+
+	rv = uper_open_type_get(ctx, &s_td, 0, 0, pd);
+	if(rv.code != RC_OK)
+		return -1;
+	else
+		return 0;
+}
+
+/*
+ * Internal functions.
+ */
+
+static asn_dec_rval_t
+uper_sot_suck(asn_codec_ctx_t *ctx, asn_TYPE_descriptor_t *td,
+	asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) {
+	asn_dec_rval_t rv;
+
+	(void)ctx;
+	(void)td;
+	(void)constraints;
+	(void)sptr;
+
+	while(per_get_few_bits(pd, 24) >= 0);
+
+	rv.code = RC_OK;
+	rv.consumed = pd->moved;
+
+	return rv;
+}
+
+static int
+uper_ugot_refill(asn_per_data_t *pd) {
+	uper_ugot_key *arg = pd->refill_key;
+	ssize_t next_chunk_bytes, next_chunk_bits;
+	ssize_t avail;
+
+	asn_per_data_t *oldpd = &arg->oldpd;
+
+	ASN_DEBUG("REFILLING pd->moved=%d, oldpd->moved=%d",
+		pd->moved, oldpd->moved);
+
+	/* Advance our position to where pd is */
+	oldpd->buffer = pd->buffer;
+	oldpd->nboff  = pd->nboff;
+	oldpd->nbits -= pd->moved - arg->ot_moved;
+	oldpd->moved += pd->moved - arg->ot_moved;
+	arg->ot_moved = pd->moved;
+
+	if(arg->unclaimed) {
+		/* Refill the container */
+		if(per_get_few_bits(oldpd, 1))
+			return -1;
+		if(oldpd->nboff == 0) {
+			assert(0);
+			return -1;
+		}
+		pd->buffer = oldpd->buffer;
+		pd->nboff = oldpd->nboff - 1;
+		pd->nbits = oldpd->nbits;
+		ASN_DEBUG("UNCLAIMED <- return from (pd->moved=%d)", pd->moved);
+		return 0;
+	}
+
+	if(!arg->repeat) {
+		ASN_DEBUG("Want more but refill doesn't have it");
+		return -1;
+	}
+
+	next_chunk_bytes = uper_get_length(oldpd, -1, &arg->repeat);
+	ASN_DEBUG("Open type LENGTH %d bytes at off %d, repeat %d",
+		next_chunk_bytes, oldpd->moved, arg->repeat);
+	if(next_chunk_bytes < 0) return -1;
+	if(next_chunk_bytes == 0) {
+		pd->refill = 0;	/* No more refills, naturally */
+		assert(!arg->repeat);	/* Implementation guarantee */
+	}
+	next_chunk_bits = next_chunk_bytes << 3;
+	avail = oldpd->nbits - oldpd->nboff;
+	if(avail >= next_chunk_bits) {
+		pd->nbits = oldpd->nboff + next_chunk_bits;
+		arg->unclaimed = 0;
+		ASN_DEBUG("!+Parent frame %d bits, alloting %d [%d..%d] (%d)",
+			next_chunk_bits, oldpd->moved,
+			oldpd->nboff, oldpd->nbits,
+			oldpd->nbits - oldpd->nboff);
+	} else {
+		pd->nbits = oldpd->nbits;
+		arg->unclaimed = next_chunk_bits - avail;
+		ASN_DEBUG("!-Parent frame %d, require %d, will claim %d", avail, next_chunk_bits, arg->unclaimed);
+	}
+	pd->buffer = oldpd->buffer;
+	pd->nboff = oldpd->nboff;
+	ASN_DEBUG("Refilled pd%s old%s",
+		per_data_string(pd), per_data_string(oldpd));
+	return 0;
+}
+
+static int
+per_skip_bits(asn_per_data_t *pd, int skip_nbits) {
+	int hasNonZeroBits = 0;
+	while(skip_nbits > 0) {
+		int skip;
+		if(skip_nbits < skip)
+			skip = skip_nbits;
+		else
+			skip = 24;
+		skip_nbits -= skip;
+
+		switch(per_get_few_bits(pd, skip)) {
+		case -1: return -1;	/* Starving */
+		case 0: continue;	/* Skipped empty space */
+		default: hasNonZeroBits = 1; continue;
+		}
+	}
+	return hasNonZeroBits;
+}
diff --git a/src/per_support.c b/src/per_support.c
index c834419..e8299c7 100644
--- a/src/per_support.c
+++ b/src/per_support.c
@@ -1,25 +1,66 @@
 /*
- * Copyright (c) 2005, 2006 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 #include <asn_system.h>
 #include <asn_internal.h>
 #include <per_support.h>
 
+char *
+per_data_string(asn_per_data_t *pd) {
+	static char buf[2][32];
+	static int n;
+	n = (n+1) % 2;
+	snprintf(buf[n], sizeof(buf),
+		"{m=%d span %+d[%d..%d] (%d)}",
+		pd->moved,
+		(((int)pd->buffer) & 0xf),
+		pd->nboff, pd->nbits,
+		pd->nbits - pd->nboff);
+	return buf[n];
+}
+
+void
+per_get_undo(asn_per_data_t *pd, int nbits) {
+	if((ssize_t)pd->nboff < nbits) {
+		assert((ssize_t)pd->nboff < nbits);
+	} else {
+		pd->nboff -= nbits;
+		pd->moved -= nbits;
+	}
+}
+
 /*
  * Extract a small number of bits (<= 31) from the specified PER data pointer.
  */
 int32_t
 per_get_few_bits(asn_per_data_t *pd, int nbits) {
 	size_t off;	/* Next after last bit offset */
+	ssize_t nleft;	/* Number of bits left in this stream */
 	uint32_t accum;
 	const uint8_t *buf;
 
-	if(nbits < 0 || pd->nboff + nbits > pd->nbits)
+	if(nbits < 0)
 		return -1;
 
-	ASN_DEBUG("[PER get %d bits from %p+%d bits]",
-		nbits, pd->buffer, pd->nboff);
+	nleft = pd->nbits - pd->nboff;
+	if(nbits > nleft) {
+		int32_t tailv, vhead;
+		if(!pd->refill || nbits > 31) return -1;
+		/* Accumulate unused bytes before refill */
+		ASN_DEBUG("Obtain the rest %d bits (want %d)", nleft, nbits);
+		tailv = per_get_few_bits(pd, nleft);
+		if(tailv < 0) return -1;
+		/* Refill (replace pd contents with new data) */
+		if(pd->refill(pd))
+			return -1;
+		nbits -= nleft;
+		vhead = per_get_few_bits(pd, nbits);
+		/* Combine the rest of previous pd with the head of new one */
+		tailv = (tailv << nbits) | vhead;  /* Could == -1 */
+		return tailv;
+	}
 
 	/*
 	 * Normalize position indicator.
@@ -29,7 +70,9 @@
 		pd->nbits  -= (pd->nboff & ~0x07);
 		pd->nboff  &= 0x07;
 	}
-	off = (pd->nboff += nbits);
+	pd->moved += nbits;
+	pd->nboff += nbits;
+	off = pd->nboff;
 	buf = pd->buffer;
 
 	/*
@@ -47,15 +90,29 @@
 	else if(nbits <= 31) {
 		asn_per_data_t tpd = *pd;
 		/* Here are we with our 31-bits limit plus 1..7 bits offset. */
-		tpd.nboff -= nbits;
+		per_get_undo(&tpd, nbits);
+		/* The number of available bits in the stream allow
+		 * for the following operations to take place without
+		 * invoking the ->refill() function */
 		accum  = per_get_few_bits(&tpd, nbits - 24) << 24;
 		accum |= per_get_few_bits(&tpd, 24);
 	} else {
-		pd->nboff -= nbits;	/* Oops, revert back */
+		per_get_undo(pd, nbits);
 		return -1;
 	}
 
-	return (accum & (((uint32_t)1 << nbits) - 1));
+	accum &= (((uint32_t)1 << nbits) - 1);
+
+	ASN_DEBUG("  [PER got %2d<=%2d bits => span %d %+d[%d..%d]:%02x (%d) => 0x%x]",
+		nbits, nleft,
+		pd->moved,
+		(((int)pd->buffer) & 0xf),
+		pd->nboff, pd->nbits,
+		pd->buffer[0],
+		pd->nbits - pd->nboff,
+		(int)accum);
+
+	return accum;
 }
 
 /*
@@ -130,6 +187,30 @@
 }
 
 /*
+ * Get the normally small length "n".
+ * This procedure used to decode length of extensions bit-maps
+ * for SET and SEQUENCE types.
+ */
+ssize_t
+uper_get_nslength(asn_per_data_t *pd) {
+	ssize_t length;
+
+	ASN_DEBUG("Getting normally small length");
+
+	if(per_get_few_bits(pd, 1) == 0) {
+		length = per_get_few_bits(pd, 6) + 1;
+		if(length <= 0) return -1;
+		ASN_DEBUG("l=%d", length);
+		return length;
+	} else {
+		int repeat;
+		length = uper_get_length(pd, -1, &repeat);
+		if(length >= 0 && !repeat) return length;
+		return -1; /* Error, or do not support >16K extensions */
+	}
+}
+
+/*
  * Get the normally small non-negative whole number.
  * X.691, #10.6
  */
@@ -193,8 +274,8 @@
 
 	if(obits <= 0 || obits >= 32) return obits ? -1 : 0;
 
-	ASN_DEBUG("[PER put %d bits to %p+%d bits]",
-			obits, po->buffer, po->nboff);
+	ASN_DEBUG("[PER put %d bits %x to %p+%d bits]",
+			obits, (int)bits, po->buffer, po->nboff);
 
 	/*
 	 * Normalize position indicator.
@@ -210,7 +291,9 @@
 	 */
 	if(po->nboff + obits > po->nbits) {
 		int complete_bytes = (po->buffer - po->tmpspace);
-		if(po->outper(po->buffer, complete_bytes, po->op_key) < 0)
+		ASN_DEBUG("[PER output %d complete + %d]",
+			complete_bytes, po->flushed_bytes);
+		if(po->outper(po->tmpspace, complete_bytes, po->op_key) < 0)
 			return -1;
 		if(po->nboff)
 			po->tmpspace[0] = po->buffer[0];
@@ -229,7 +312,8 @@
 	/* 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,
+	ASN_DEBUG("[PER out %d %u/%x (t=%d,o=%d) %x&%x=%x]", obits,
+		(int)bits, (int)bits,
 		po->nboff - obits, off, buf[0], omsk&0xff, buf[0] & omsk);
 
 	if(off <= 8)	/* Completely within 1 byte */
@@ -258,7 +342,7 @@
 	}
 
 	ASN_DEBUG("[PER out %u/%x => %02x buf+%d]",
-		bits, bits, buf[0], po->buffer - po->tmpspace);
+		(int)bits, (int)bits, buf[0], po->buffer - po->tmpspace);
 
 	return 0;
 }
@@ -316,3 +400,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) != (ssize_t)length) {
+			/* This might happen in case of >16K extensions */
+			return -1;
+		}
+	}
+
+	return 0;
+}
+