constraint checking code


git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@149 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/libasn1compiler/Makefile.am b/libasn1compiler/Makefile.am
index ef96aef..bcfaf93 100644
--- a/libasn1compiler/Makefile.am
+++ b/libasn1compiler/Makefile.am
@@ -13,7 +13,9 @@
 	asn1c_lang.c asn1c_lang.h		\
 	asn1c_save.c asn1c_save.h		\
 	asn1c_C.c asn1c_C.h			\
+	asn1c_constraint.c asn1c_constraint.h	\
 	asn1c_compat.c asn1c_compat.h		\
+	asn1c_fdeps.c asn1c_fdeps.h		\
 	asn1c_internal.h
 
 TESTS = $(check_PROGRAMS)
diff --git a/libasn1compiler/Makefile.in b/libasn1compiler/Makefile.in
index e815393..b427be9 100644
--- a/libasn1compiler/Makefile.in
+++ b/libasn1compiler/Makefile.in
@@ -53,7 +53,7 @@
 libasn1compiler_la_LIBADD =
 am_libasn1compiler_la_OBJECTS = asn1compiler.lo asn1c_misc.lo \
 	asn1c_out.lo asn1c_lang.lo asn1c_save.lo asn1c_C.lo \
-	asn1c_compat.lo
+	asn1c_constraint.lo asn1c_compat.lo asn1c_fdeps.lo
 libasn1compiler_la_OBJECTS = $(am_libasn1compiler_la_OBJECTS)
 check_compiler_SOURCES = check_compiler.c
 check_compiler_OBJECTS = check_compiler.$(OBJEXT)
@@ -66,6 +66,8 @@
 am__depfiles_maybe = depfiles
 @AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/asn1c_C.Plo \
 @AMDEP_TRUE@	./$(DEPDIR)/asn1c_compat.Plo \
+@AMDEP_TRUE@	./$(DEPDIR)/asn1c_constraint.Plo \
+@AMDEP_TRUE@	./$(DEPDIR)/asn1c_fdeps.Plo \
 @AMDEP_TRUE@	./$(DEPDIR)/asn1c_lang.Plo \
 @AMDEP_TRUE@	./$(DEPDIR)/asn1c_misc.Plo \
 @AMDEP_TRUE@	./$(DEPDIR)/asn1c_out.Plo \
@@ -208,7 +210,9 @@
 	asn1c_lang.c asn1c_lang.h		\
 	asn1c_save.c asn1c_save.h		\
 	asn1c_C.c asn1c_C.h			\
+	asn1c_constraint.c asn1c_constraint.h	\
 	asn1c_compat.c asn1c_compat.h		\
+	asn1c_fdeps.c asn1c_fdeps.h		\
 	asn1c_internal.h
 
 TESTS = $(check_PROGRAMS)
@@ -279,6 +283,8 @@
 
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1c_C.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1c_compat.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1c_constraint.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1c_fdeps.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1c_lang.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1c_misc.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1c_out.Plo@am__quote@
diff --git a/libasn1compiler/asn1c_C.c b/libasn1compiler/asn1c_C.c
index 197ce1f..ac13873 100644
--- a/libasn1compiler/asn1c_C.c
+++ b/libasn1compiler/asn1c_C.c
@@ -4,7 +4,8 @@
  */
 #include "asn1c_internal.h"
 #include "asn1c_C.h"
-#include <asn1fix_export.h>	/* exportable stuff from libasn1fix */
+#include "asn1c_constraint.h"
+#include <asn1fix_export.h>	/* Stuff exported by libasn1fix */
 
 typedef struct tag2el_s {
 	struct asn1p_type_tag_s el_tag;
@@ -25,17 +26,6 @@
 static int check_if_extensible(asn1p_expr_t *expr);
 static int emit_tags_vector(arg_t *arg, asn1p_expr_t *expr, int *tags_impl_skip, int choice_mode);
 static int emit_tag2member_map(arg_t *arg, tag2el_t *tag2el, int tag2el_count);
-static int emit_constraint_checking_code(arg_t *arg);
-static int emit_single_constraint_check(arg_t *arg, asn1p_constraint_t *ct, int mode);
-static int emit_alphabet_tables(arg_t *arg, asn1p_constraint_t *ct, int *table);
-static int emit_alphabet_check_cycle(arg_t *arg);
-static int check_constraint_type_presence(asn1p_constraint_t *ct, enum asn1p_constraint_type_e type);
-static asn1p_expr_type_e _find_terminal_type(arg_t *arg);
-static int emit_value_determination_code(arg_t *arg);
-static int emit_size_determination_code(arg_t *arg);
-static long compute_min_size(arg_t *arg);
-static long compute_max_size(arg_t *arg);
-static long compute_xxx_size(arg_t *arg, int _max);
 
 #define	C99_MODE	(arg->flags & A1C_NO_C99)
 #define	UNNAMED_UNIONS	(arg->flags & A1C_UNNAMED_UNIONS)
@@ -969,28 +959,28 @@
 	/*
 	 * Constraint checking.
 	 */
-	if(expr->constraints)	/* Emit tables with FROM() constraints */
-		emit_alphabet_tables(arg, expr->constraints, 0);
+	/* Emit FROM() tables and others */
+	asn1c_emit_constraint_tables(arg, 0);
+
 	p = MKID(expr->Identifier);
 	OUT("int\n");
 	OUT("%s_constraint(asn1_TYPE_descriptor_t *td, const void *sptr,\n", p);
 	INDENTED(
 	OUT("\t\tasn_app_consume_bytes_f *app_errlog, void *app_key) {\n");
 	OUT("\n");
-	if(expr->constraints) {
-
-		emit_constraint_checking_code(arg);
-
+	if(asn1c_emit_constraint_checking_code(arg) == 1) {
+		if(0) {
 		OUT("/* Check the constraints of the underlying type */\n");
 		OUT("return asn1_DEF_%s.check_constraints\n",
 			asn1c_type_name(arg, expr, TNF_SAFE));
 		OUT("\t(td, sptr, app_errlog, app_key);\n");
-	} else {
+		} else {
 		OUT("/* Make the underlying type checker permanent */\n");
 		OUT("td->check_constraints = asn1_DEF_%s.check_constraints;\n",
 			asn1c_type_name(arg, expr, TNF_SAFE));
 		OUT("return td->check_constraints\n");
 		OUT("\t(td, sptr, app_errlog, app_key);\n");
+		}
 	}
 	);
 	OUT("}\n");
@@ -1365,687 +1355,3 @@
 
 	return tags_count;
 }
-
-static int
-emit_constraint_checking_code(arg_t *arg) {
-	asn1p_expr_t *expr = arg->expr;
-	asn1p_expr_type_e etype;
-	int size_present, value_present;
-
-	if(expr->constraints == NULL)
-		return 0;	/* No constraints defined */
-
-	etype = _find_terminal_type(arg);
-
-	size_present = check_constraint_type_presence(expr->constraints,
-		ACT_CT_SIZE);
-	value_present = check_constraint_type_presence(expr->constraints,
-		ACT_EL_VALUE);
-
-	if(size_present || value_present) {
-		OUT("const %s_t *st = sptr;\n", MKID(arg->expr->Identifier));
-		if(size_present) {
-			OUT("size_t size;\n");
-			OUT("size_t min_size __attribute__ ((unused)) = %ld;\n",
-				compute_min_size(arg));
-			OUT("size_t max_size __attribute__ ((unused)) = %ld;\n",
-				compute_max_size(arg));
-		}
-		if(value_present)
-		switch(etype) {
-		case ASN_BASIC_INTEGER:
-		case ASN_BASIC_ENUMERATED:
-			OUT("long value;\n");
-			break;
-		case ASN_BASIC_BOOLEAN:
-			OUT("int value;\n");
-			break;
-		default:
-			break;
-		}
-		OUT("\n");
-	}
-
-	OUT("if(!sptr) {\n");
-	INDENT(+1);
-		OUT("_ASN_ERRLOG(app_errlog, app_key,\n");
-		OUT("\t\"%%s: value not given\", td->name);\n");
-		OUT("return -1;\n");
-	INDENT(-1);
-	OUT("}\n");
-	OUT("\n");
-
-	if(size_present)
-		emit_size_determination_code(arg);
-	if(value_present)
-		emit_value_determination_code(arg);
-
-	OUT("\n");
-	OUT("if(\n");
-	emit_single_constraint_check(arg, expr->constraints, 0);
-	OUT(") {\n");
-	INDENTED(OUT("/* Constraint check succeeded */\n"));
-	OUT("} else {\n");
-	INDENT(+1);
-		OUT("_ASN_ERRLOG(app_errlog, app_key,\n");
-		OUT("\t\"%%s: constraint failed\", td->name);\n");
-		OUT("return -1;\n");
-	INDENT(-1);
-	OUT("}\n");
-
-	return 0;
-}
-
-static int
-emit_single_constraint_check(arg_t *arg, asn1p_constraint_t *ct, int mode) {
-	char *s_v;
-	int el;
-
-	assert(arg && ct);
-
-	switch(ct->type) {
-	case ACT_INVALID:
-		assert(ct->type != ACT_INVALID);
-		OUT("-1 /* Invalid constraint at line %d */\n", ct->_lineno);
-		break;
-	case ACT_EL_VALUE:
-		OUT("(");
-		if(mode == ACT_CT_SIZE)	s_v = "size";
-		else			s_v = "value";
-		OUT("%s", s_v);
-		if(ct->value->type != ATV_TRUE)
-			OUT(" == ");
-		switch(ct->value->type) {
-		case ATV_INTEGER: OUT("%lld",
-			(long long)ct->value->value.v_integer); break;
-		case ATV_MIN:	OUT("min_%s", s_v);	break;
-		case ATV_MAX:	OUT("max_%s", s_v);	break;
-		case ATV_FALSE:	OUT("0");	break;
-		case ATV_TRUE:			break;
-		default:
-			break;
-		}
-		OUT(")\n");
-		break;
-	case ACT_EL_RANGE:
-	case ACT_EL_LLRANGE:
-	case ACT_EL_RLRANGE:
-	case ACT_EL_ULRANGE:
-		if(mode == ACT_CT_SIZE) {
-			s_v = "size";
-		} else {
-			s_v = "value";
-		}
-		OUT("((%s", s_v);
-		switch(ct->type) {
-		case ACT_EL_RANGE:
-		case ACT_EL_RLRANGE:
-			OUT(" >= ");	break;
-		case ACT_EL_LLRANGE:
-		case ACT_EL_ULRANGE:
-			OUT(" > ");	break;
-		default:	break;
-		}
-		switch(ct->range_start->type) {
-		case ATV_INTEGER: OUT("%lld",
-			(long long)ct->range_start->value.v_integer); break;
-		case ATV_MIN:	OUT("min_%s", s_v);	break;
-		case ATV_MAX:	OUT("max_%s", s_v);	break;
-		case ATV_FALSE:	OUT("0");	break;
-		case ATV_TRUE:			break;
-		default:
-			break;
-		}
-		OUT(") && (%s", s_v);
-		switch(ct->type) {
-		case ACT_EL_RANGE:
-		case ACT_EL_LLRANGE:
-			OUT(" <= ");	break;
-		case ACT_EL_RLRANGE:
-		case ACT_EL_ULRANGE:
-			OUT(" < ");	break;
-		default:	break;
-		}
-		switch(ct->range_stop->type) {
-		case ATV_INTEGER: OUT("%lld",
-			(long long)ct->range_stop->value.v_integer); break;
-		case ATV_MIN:	OUT("min_%s", s_v);	break;
-		case ATV_MAX:	OUT("max_%s", s_v);	break;
-		case ATV_FALSE:	OUT("0");	break;
-		case ATV_TRUE:			break;
-		default:
-			break;
-		}
-		OUT("))\n");
-		break;
-	case ACT_EL_EXT:
-		OUT("0 /* Extensible (...), but not defined herein */\n");
-		break;
-	case ACT_CT_SIZE:
-		if(mode) {
-			OUT("0 /* Invalid constraint at line %d */\n",
-				ct->_lineno);
-			return -1;
-		}
-		assert(ct->el_count == 1);
-		return emit_single_constraint_check(arg,
-			ct->elements[0], ACT_CT_SIZE);
-	case ACT_CT_FROM:
-		if(mode) {
-			OUT("0 /* Invalid constraint at line %d */\n",
-				ct->_lineno);
-			return -1;
-		}
-		OUT("check_alphabet_%x(sptr)\n", ct);
-		break;
-	case ACT_CT_WCOMP:
-	case ACT_CT_WCOMPS:
-		OUT("%d /* Unsupported constraint at line %d */\n",
-			ct->type, ct->_lineno);
-		return -1;
-		break;
-	case ACT_CA_SET:
-		OUT("(\n");
-		INDENT(+1);
-		for(el = 0; el < ct->el_count; el++) {
-			if(el) OUT("&& ");
-			emit_single_constraint_check(arg,
-				ct->elements[el], mode);
-		}
-		INDENT(-1);
-		OUT(")\n");
-		break;
-	case ACT_CA_CSV:
-		OUT("(\n");
-		INDENT(+1);
-		for(el = 0; el < ct->el_count; el++) {
-			if(el) OUT("|| ");
-			emit_single_constraint_check(arg,
-				ct->elements[el], mode);
-		}
-		INDENT(-1);
-		OUT(")\n");
-		break;
-	case ACT_CA_UNI:
-		OUT("(\n");
-		INDENT(+1);
-		for(el = 0; el < ct->el_count; el++) {
-			if(el) OUT("|| ");
-			emit_single_constraint_check(arg,
-				ct->elements[el], mode);
-		}
-		INDENT(-1);
-		OUT(")\n");
-		break;
-	case ACT_CA_INT:
-		OUT("(\n");
-		INDENT(+1);
-		for(el = 0; el < ct->el_count; el++) {
-			if(el) OUT("&& ");
-			emit_single_constraint_check(arg,
-				ct->elements[el], mode);
-		}
-		INDENT(-1);
-		OUT(")\n");
-		break;
-	case ACT_CA_CRC:
-		WARNING("Unsupported component relation constraint at line %d",
-			ct->_lineno);
-		OUT("%d /* Unsupported component relation constraint "
-			"at line %d */\n",
-			ct->type, ct->_lineno);
-		return -1;
-	case ACT_CA_EXC:
-		WARNING("Unsupported EXCEPT constraint at line %d",
-			ct->_lineno);
-		OUT("%d /* Unsupported EXCEPT constraint at line %d */\n",
-			ct->type, ct->_lineno);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int
-check_constraint_type_presence(asn1p_constraint_t *ct, enum asn1p_constraint_type_e type) {
-	int el;
-
-	if(ct == NULL) return 0;
-
-	if(ct->type == type) return 1;
-
-	if(type == ACT_EL_VALUE) {
-		if(ct->type >= ACT_CT_SIZE
-		&& ct->type <= ACT_CT_WCOMPS)
-			/* Values defined further
-			 * are not really value's values */
-			return 0;
-		if(ct->type > ACT_EL_VALUE && ct->type < ACT_CT_SIZE)
-			return 1;	/* Also values */
-	}
-
-	for(el = 0; el < ct->el_count; el++) {
-		if(check_constraint_type_presence(ct->elements[el], type))
-			return 1;
-	}
-
-	return 0;
-}
-
-static int
-emit_alphabet_tables(arg_t *arg, asn1p_constraint_t *ct, int *table) {
-	int ch = 0;
-	int ch_start = 0;
-	int ch_stop = 0;
-	int el = 0;
-
-	assert(arg && ct);
-
-	switch(ct->type) {
-	case ACT_INVALID:
-		break;
-	case ACT_EL_VALUE:
-		if(!table) break;
-
-		switch(ct->value->type) {
-		case ATV_INTEGER:
-			if(ct->value->value.v_integer < 0
-			|| ct->value->value.v_integer > 255) {
-				OUT("\n");
-				OUT("#error Value %lld out of range "
-				"for alphabet character at line %d\n",
-				(long long)ct->value->value.v_integer,
-					ct->_lineno);
-				break;
-			} else {
-				ch = ct->value->value.v_integer;
-				table[ch] = 1;
-			}
-			break;
-		case ATV_STRING:
-			for(ch = 0; ch < ct->value->value.string.size; ch++)
-				table[ct->value->value.string.buf[ch]] = 1;
-			break;
-		default:
-			OUT("\n");
-			WARNING("Invalid alphabet character specification "
-				"at line %d", ct->_lineno);
-			OUT("#error Invalid alphabet character specification "
-				"at line %d\n", ct->_lineno);
-			break;
-		}
-		break;
-	case ACT_EL_RANGE:
-	case ACT_EL_LLRANGE:
-	case ACT_EL_RLRANGE:
-	case ACT_EL_ULRANGE:
-		if(!table) break;
-
-		ch_start = 0;
-		ch_stop = 255;
-
-		switch(ct->range_start->type) {
-		case ATV_INTEGER:
-			ch_start = ct->range_start->value.v_integer; break;
-		case ATV_MIN:	ch_start = 0;	break;
-		case ATV_MAX:	ch_start = 255;	break;
-		case ATV_STRING:
-			if(ct->range_start->value.string.size == 1) {
-				ch_start = ct->range_start->value.string.buf[0];
-				break;
-			}
-			/* Fall through */
-		default:
-			OUT("\n");
-			FATAL("Invalid alphabet range constraint "
-				"at line %d\n", ct->_lineno);
-			OUT("#error Invalid alphabet range constraint "
-				"at line %d\n", ct->_lineno);
-			return -1;
-		}
-
-		switch(ct->range_stop->type) {
-		case ATV_INTEGER:
-			ch_stop = ct->range_stop->value.v_integer; break;
-		case ATV_MIN:	ch_stop = 0;	break;
-		case ATV_MAX:	ch_stop = 255;	break;
-		case ATV_STRING:
-			if(ct->range_stop->value.string.size == 1) {
-				ch_stop = ct->range_stop->value.string.buf[0];
-				break;
-			}
-			/* Fall through */
-		default:
-			OUT("\n");
-			FATAL("Invalid alphabet range constraint "
-				"at line %d\n", ct->_lineno);
-			OUT("#error Invalid alphabet range constraint "
-				"at line %d\n", ct->_lineno);
-			break;
-		}
-
-		switch(ct->type) {
-		case ACT_EL_RANGE:	break;
-		case ACT_EL_RLRANGE:	ch_stop--;	break;
-		case ACT_EL_LLRANGE:	ch_start++;	break;
-		case ACT_EL_ULRANGE:	ch_start++; ch_stop--; break;
-		default:	break;
-		}
-
-		if(ch_start > ch_stop) {
-			WARNING("Empty character range "
-			"alphabet constraint at line %d", ct->_lineno);
-			OUT_NOINDENT("#warning Empty character range "
-			"alphabet constraint at line %d\n", ct->_lineno);
-			break;
-		}
-
-		for(ch = ch_start; ch <= ch_stop; ch++) {
-			if(ch < 0 || ch > 255) continue;
-			table[ch] = 1;
-		}
-
-		break;
-	case ACT_EL_EXT:
-		break;
-	case ACT_CT_SIZE:
-		break;
-	case ACT_CT_FROM:
-	  if(table) {
-			OUT("#error Nested FROM in subtype constraints\n");
-			return -1;
-	  } else {
-			table = alloca(256 * sizeof(table[0]));
-			memset(table, 0, 256 * sizeof(table[0]));
-
-			for(el = 0; el < ct->el_count; el++) {
-				emit_alphabet_tables(arg, ct->elements[el],
-					table);
-			}
-			OUT("static int alphabet_table_%x[256] = {\n", ct);
-			for(ch = 0; ch < 256; ch++) {
-				OUT("%d,", table[ch]?1:0);
-				if(!((ch+1) % 16)) {
-				  if(ch) {
-					int c;
-					OUT("\t/* ");
-					for(c = ch - 16; c < ch; c++) {
-						if(table[c]) {
-							if(c > 0x20
-								&& c < 0x80)
-								OUT("%c", c);
-							else
-								OUT(".", c);
-						} else {
-							OUT(" ");
-						}
-					}
-					OUT(" */");
-				  }
-				  OUT("\n");
-				}
-			}
-			OUT("};\n");
-			OUT("static int check_alphabet_%x(const void *sptr) {\n", ct);
-			INDENT(+1);
-			OUT("int *table = alphabet_table_%x;\n", ct);
-			emit_alphabet_check_cycle(arg);
-			OUT("return 1;\n");
-			INDENT(-1);
-			OUT("};\n");
-	  }
-		break;
-	case ACT_CT_WCOMP:
-	case ACT_CT_WCOMPS:
-		break;
-	case ACT_CA_CRC:
-		break;
-	case ACT_CA_SET:
-	case ACT_CA_CSV:
-	case ACT_CA_UNI:
-		for(el = 0; el < ct->el_count; el++)
-			emit_alphabet_tables(arg, ct->elements[el], table);
-		break;
-	case ACT_CA_INT:
-		if(table) {
-			int table2[256];
-
-			assert(ct->el_count >= 1);
-			emit_alphabet_tables(arg, ct->elements[0], table);
-			for(el = 1; el < ct->el_count; el++) {
-				memset(table2, 0, sizeof(table2));
-				emit_alphabet_tables(arg,
-					ct->elements[el], table2);
-				/* Intersection */
-				for(ch = 0; ch < 256; ch++) {
-					if(table2[ch] == 0)
-						table[ch] = 0;
-				}
-			}
-		} else {
-			for(el = 0; el < ct->el_count; el++)
-				emit_alphabet_tables(arg, ct->elements[el], 0);
-		}
-
-		break;
-	case ACT_CA_EXC:
-		OUT("EXC\n");
-		if(table) {
-			int table2[256];
-
-			assert(ct->el_count >= 1);
-			emit_alphabet_tables(arg, ct->elements[0], table);
-			for(el = 1; el < ct->el_count; el++) {
-				memset(table2, 0, sizeof(table2));
-				emit_alphabet_tables(arg,
-					ct->elements[el], table2);
-				/* Exclusion */
-				for(ch = 0; ch < 256; ch++) {
-					if(table2[ch])
-						table[ch] = 0;
-				}
-			}
-		} else {
-			for(el = 0; el < ct->el_count; el++)
-				emit_alphabet_tables(arg, ct->elements[el], 0);
-		}
-		break;
-	}
-
-	return 0;
-}
-
-static int
-emit_alphabet_check_cycle(arg_t *arg) {
-	asn1p_expr_type_e etype;
-
-	etype = _find_terminal_type(arg);
-	if(!(etype & ASN_STRING_MASK)
-	&& !(etype == ASN_BASIC_OCTET_STRING)) {
-		OUT("#error Cannot apply FROM constraint to ASN.1 type %s\n",
-			ASN_EXPR_TYPE2STR(etype));
-		return -1;
-	}
-
-	OUT("/* The underlying type is %s */\n",
-		ASN_EXPR_TYPE2STR(etype));
-	OUT("const %s_t *st = sptr;\n", MKID(arg->expr->Identifier));
-
-	switch(etype) {
-	case ASN_STRING_UTF8String:
-		OUT("uint8_t *ch = st->buf;\n");
-		OUT("uint8_t *end = ch + st->size;\n");
-		OUT("\n");
-		OUT("for(; ch < end; ch++) {\n");
-		INDENT(+1);
-		OUT("if(*ch >= 0x80 || !table[*ch]) return 0;\n");
-		INDENT(-1);
-		OUT("}\n");
-		break;
-	case ASN_STRING_UniversalString:
-		OUT("uint32_t *ch = st->buf;\n");
-		OUT("uint32_t *end = ch + st->size;\n");
-		OUT("\n");
-		OUT("for(; ch < end; ch++) {\n");
-		INDENT(+1);
-		OUT("uint32_t wc = (((uint8_t *)ch)[0] << 24)\n");
-		OUT("\t\t| (((uint8_t *)ch)[1] << 16)\n");
-		OUT("\t\t| (((uint8_t *)ch)[2] << 8)\n");
-		OUT("\t\t|  ((uint8_t *)ch)[3]\n");
-		OUT("if(wc > 255 || !table[wc]) return 0;\n");
-		INDENT(-1);
-		OUT("}\n");
-		OUT("if(ch != end) return 0; /* (size%4)! */\n");
-		break;
-	case ASN_STRING_BMPString:
-		OUT("uint16_t *ch = st->buf;\n");
-		OUT("uint16_t *end = ch + st->size;\n");
-		OUT("\n");
-		OUT("for(; ch < end; ch++) {\n");
-		INDENT(+1);
-		OUT("uint16_t wc = (((uint8_t *)ch)[0] << 8)\n");
-		OUT("\t\t| ((uint8_t *)ch)[1];\n");
-		OUT("if(wc > 255 || !table[wc]) return 0;\n");
-		INDENT(-1);
-		OUT("}\n");
-		OUT("if(ch != end) return 0; /* (size%2)! */\n");
-		break;
-	case ASN_BASIC_OCTET_STRING:
-	default:
-		OUT("uint8_t *ch = st->buf;\n");
-		OUT("uint8_t *end = ch + st->size;\n");
-		OUT("\n");
-		OUT("for(; ch < end; ch++) {\n");
-		INDENT(+1);
-		OUT("if(!table[*ch]) return 0;\n");
-		INDENT(-1);
-		OUT("}\n");
-		break;
-	}
-
-	return 0;
-}
-
-static int
-emit_size_determination_code(arg_t *arg) {
-	asn1p_expr_type_e etype = _find_terminal_type(arg);
-
-	switch(etype) {
-	case ASN_BASIC_BIT_STRING:
-		OUT("if(st->size > 0) {\n");
-		OUT("\t/* Size in bits */\n");
-		OUT("\tsize = (st->size - 1) - (st->buf[0] & 0x7);\n");
-		OUT("} else {\n");
-		OUT("\tsize = 0;\n");
-		OUT("}\n");
-		break;
-	case ASN_STRING_UniversalString:
-		OUT("size = st->size >> 2;\t/* 4 byte per character */\n");
-		break;
-	case ASN_STRING_BMPString:
-		OUT("size = st->size >> 1;\t/* 2 byte per character */\n");
-		break;
-	case ASN_STRING_UTF8String:
-		OUT("size = UTF8String_length(st, td->name, app_errlog, app_key);\n");
-		OUT("if(size == (size_t)-1) return -1;\n");
-		break;
-	default:
-		if((etype & ASN_STRING_MASK)
-		|| etype == ASN_BASIC_OCTET_STRING) {
-			OUT("size = st->size;\n");
-			break;
-		} else {
-			const char *type_name = ASN_EXPR_TYPE2STR(etype);
-			if(!type_name) type_name = arg->expr->Identifier;
-			WARNING("SIZE constraint is not defined for %s",
-				type_name);
-			OUT_NOINDENT("#warning SIZE constraint "
-				"not defined for %s!\n", type_name);
-			OUT("size = st->size;\n");
-		}
-		return -1;
-	}
-
-	return 0;
-}
-
-static int
-emit_value_determination_code(arg_t *arg) {
-	asn1p_expr_type_e etype = _find_terminal_type(arg);
-
-	switch(etype) {
-	case ASN_BASIC_INTEGER:
-	case ASN_BASIC_ENUMERATED:
-		if(arg->flags & A1C_USE_NATIVE_INTEGERS) {
-			OUT("value = *(int *)st;\n");
-		} else {
-			OUT("if(asn1_INTEGER2long(st, &value)) {\n");
-			INDENT(+1);
-			OUT("_ASN_ERRLOG(app_errlog, app_key,\n");
-			OUT("\t\"%%s: value too large\", td->name);\n");
-			OUT("return -1;\n");
-			INDENT(-1);
-			OUT("}\n");
-		}
-		break;
-	case ASN_BASIC_BOOLEAN:
-		OUT("value = st->value;\n");
-		break;
-	default:
-		WARNING("Value cannot be determined "
-			"for constraint check for %s at line %d\n",
-			arg->expr->Identifier, arg->expr->_lineno);
-		OUT("#error Value cannot be determined for %s at %d\n",
-			arg->expr->Identifier, arg->expr->_lineno);
-		break;
-	}
-
-	return 0;
-}
-
-static long compute_min_size(arg_t *arg) { return compute_xxx_size(arg, 0); }
-static long compute_max_size(arg_t *arg) { return compute_xxx_size(arg, 1); }
-
-static long compute_xxx_size(arg_t *arg, int _max) {
-	asn1p_expr_type_e etype;
-	long basic_max = 0x7fffffff;
-	long basic_min = 0x80000000;
-	long svalue = 0;
-
-	etype = _find_terminal_type(arg);
-	switch(etype) {
-	case ASN_BASIC_BIT_STRING:
-		svalue = _max?basic_max/8:0;
-		break;
-	case ASN_STRING_UTF8String:
-		svalue = _max?basic_max/6:0;
-		break;
-	case ASN_STRING_UniversalString:
-		svalue = _max?basic_max/4:0;
-		break;
-	case ASN_STRING_BMPString:
-		svalue = _max?basic_max/2:0;
-		break;
-	case ASN_BASIC_OCTET_STRING:
-		svalue = _max?basic_max:0;
-		break;
-	default:
-		if((etype & ASN_STRING_MASK)) {
-			svalue = _max?basic_max:0;
-			break;
-		}
-		svalue = _max?basic_max:basic_min;
-		break;
-	}
-
-	return svalue;
-}
-
-static asn1p_expr_type_e
-_find_terminal_type(arg_t *arg) {
-	asn1p_expr_t *expr;
-	expr = asn1f_find_terminal_type_ex(arg->asn, arg->mod, arg->expr, NULL);
-	if(expr) return expr->expr_type;
-	return A1TC_INVALID;
-}
-
diff --git a/libasn1compiler/asn1c_constraint.c b/libasn1compiler/asn1c_constraint.c
new file mode 100644
index 0000000..dd54224
--- /dev/null
+++ b/libasn1compiler/asn1c_constraint.c
@@ -0,0 +1,482 @@
+#include "asn1c_internal.h"
+#include "asn1c_constraint.h"
+
+#include <asn1fix_crange.h>	/* constraint groker from libasn1fix */
+#include <asn1fix_export.h>	/* other exportable stuff from libasn1fix */
+
+static int emit_alphabet_check_loop(arg_t *arg, asn1cnst_range_t *range);
+static int emit_value_determination_code(arg_t *arg, asn1p_expr_type_e etype);
+static int emit_size_determination_code(arg_t *arg, asn1p_expr_type_e etype);
+static asn1p_expr_type_e _find_terminal_type(arg_t *arg);
+static int emit_range_comparison_code(arg_t *arg, asn1cnst_range_t *range, const char *varname, asn1_integer_t natural_start, asn1_integer_t natural_stop);
+
+#define	MKID(id)	asn1c_make_identifier(0, (id), 0)
+
+static int global_compile_mark;
+
+int
+asn1c_emit_constraint_checking_code(arg_t *arg) {
+	asn1cnst_range_t *r_size;
+	asn1cnst_range_t *r_value;
+	asn1p_expr_t *expr = arg->expr;
+	asn1p_expr_type_e etype;
+	asn1p_constraint_t *ct;
+	int got_something = 0;
+
+	ct = expr->combined_constraints;
+	if(ct == NULL)
+		return 1;	/* No additional constraints defined */
+
+	etype = _find_terminal_type(arg);
+
+	r_value=asn1constraint_compute_PER_range(etype, ct, ACT_EL_RANGE, 0, 0);
+	r_size = asn1constraint_compute_PER_range(etype, ct, ACT_CT_SIZE, 0, 0);
+	if(r_value) {
+		if(r_value->not_PER_visible
+		|| r_value->extensible
+		|| r_value->empty_constraint
+		|| (r_value->left.type == ARE_MIN
+			&& r_value->right.type == ARE_MAX)
+		|| (etype == ASN_BASIC_BOOLEAN
+			&& r_value->left.value == 0
+			&& r_value->right.value == 1)
+		) {
+			asn1constraint_range_free(r_value);
+			r_value = 0;
+		}
+	}
+	if(r_size) {
+		if(r_size->not_PER_visible
+		|| r_size->extensible
+		|| r_size->empty_constraint
+		|| (r_size->left.value == 0	/* or .type == MIN */
+			&& r_size->right.type == ARE_MAX)
+		) {
+			asn1constraint_range_free(r_size);
+			r_size = 0;
+		}
+	}
+
+	OUT("const %s_t *st = sptr;\n", MKID(arg->expr->Identifier));
+
+	if(r_size || r_value) {
+		if(r_size) {
+			OUT("size_t size;\n");
+		}
+		if(r_value)
+			switch(etype) {
+			case ASN_BASIC_INTEGER:
+			case ASN_BASIC_ENUMERATED:
+				OUT("long value;\n");
+				break;
+			case ASN_BASIC_BOOLEAN:
+				OUT("int value;\n");
+				break;
+			default:
+				break;
+		}
+	}
+
+	OUT("\n");
+
+	/*
+	 * Protection against null input.
+	 */
+	OUT("if(!sptr) {\n");
+		INDENT(+1);
+		OUT("_ASN_ERRLOG(app_errlog, app_key,\n");
+		OUT("\t\"%%s: value not given\", td->name);\n");
+		OUT("return -1;\n");
+		INDENT(-1);
+	OUT("}\n");
+	OUT("\n");
+
+	if(r_value)
+		emit_value_determination_code(arg, etype);
+	if(r_size)
+		emit_size_determination_code(arg, etype);
+
+	/*
+	 * Here is an if() {} else {} constaint checking code.
+	 */
+	OUT("\n");
+	OUT("if(");
+	INDENT(+1);
+		if(r_size) {
+			if(got_something++) { OUT("\n"); OUT(" && "); }
+			OUT("(");
+			emit_range_comparison_code(arg, r_size, "size", 0, -1);
+			OUT(")");
+		}
+		if(r_value) {
+			if(got_something++) { OUT("\n"); OUT(" && "); }
+			OUT("(");
+			if(etype == ASN_BASIC_BOOLEAN)
+				emit_range_comparison_code(arg, r_value,
+					"value", 0, 1);
+			else
+				emit_range_comparison_code(arg, r_value,
+					"value", -1, -1);
+			OUT(")");
+		}
+		if(ct->_compile_mark) {
+			if(got_something++) { OUT("\n"); OUT(" && "); }
+			OUT("check_permitted_alphabet_%d(sptr)",
+				ct->_compile_mark);
+		}
+		if(!got_something) {
+			OUT("1 /* No applicable constraints whatsoever */");
+		}
+	INDENT(-1);
+	OUT(") {\n");
+		INDENT(+1);
+		OUT("/* Constraint check succeeded */\n");
+		OUT("return 1;\n");
+		INDENT(-1);
+	OUT("} else {\n");
+		INDENT(+1);
+			OUT("_ASN_ERRLOG(app_errlog, app_key,\n");
+			OUT("\t\"%%s: constraint failed\", td->name);\n");
+			OUT("return -1;\n");
+		INDENT(-1);
+	OUT("}\n");
+
+	return 0;
+}
+
+int
+asn1c_emit_constraint_tables(arg_t *arg, asn1p_constraint_t *ct) {
+	asn1_integer_t range_start;
+	asn1_integer_t range_stop;
+	asn1p_expr_type_e etype;
+	asn1cnst_range_t *range;
+	int table[256];
+	int use_table;
+
+	if(!ct) ct = arg->expr->combined_constraints;
+	if(!ct) return 0;
+
+	etype = _find_terminal_type(arg);
+
+	range = asn1constraint_compute_PER_range(etype, ct, ACT_CT_FROM, 0, 0);
+	if(!range) return 0;
+
+	if(range->not_PER_visible
+	|| range->extensible
+	|| range->empty_constraint) {
+		asn1constraint_range_free(range);
+		return 0;
+	}
+
+	range_start = range->left.value;
+	range_stop = range->right.value;
+	assert(range->left.type == ARE_VALUE);
+	assert(range->right.type == ARE_VALUE);
+	assert(range_start <= range_stop);
+
+	range_start = 0;	/* Force old behavior */
+
+	/*
+	 * Check if we need a test table to check the alphabet.
+	 */
+	use_table = 1;
+	if((range_stop - range_start) > 255)
+		use_table = 0;
+	if(range->el_count == 0)
+		use_table = 0;
+
+	if(!ct->_compile_mark)
+		ct->_compile_mark = ++global_compile_mark;
+
+	if(use_table) {
+		int i, n = 0;
+		int untl;
+		memset(table, 0, sizeof(table));
+		for(i = -1; i < range->el_count; i++) {
+			asn1cnst_range_t *r;
+			asn1_integer_t v;
+			if(i == -1) {
+				if(range->el_count) continue;
+				r = range;
+			} else {
+				r = range->elements[i];
+			}
+			for(v = r->left.value; v <= r->right.value; v++) {
+				assert((v - range_start) >= 0);
+				assert((v - range_start) < 256);
+				table[v - range_start] = ++n;
+			}
+		}
+
+		OUT("static int permitted_alphabet_table_%d[256] = {\n",
+			ct->_compile_mark);
+		untl = (range_stop - range_start) + 1;
+		untl += (untl % 16)?16 - (untl % 16):0;
+		for(n = 0; n < untl; n++) {
+			OUT("%d,", table[n]?1:0);
+			if(!((n+1) % 16)) {
+				int c;
+				if(!n) {
+					OUT("\n");
+					continue;
+				}
+				OUT("\t/* ");
+				for(c = n - 15; c <= n; c++) {
+					if(table[c]) {
+						int a = c + range_start;
+						if(a > 0x20 && a < 0x80)
+							OUT("%c", a);
+						else
+							OUT(".");
+					} else {
+						OUT(" ");
+					}
+				}
+				OUT(" */");
+				OUT("\n");
+			}
+		}
+		OUT("};\n");
+		OUT("\n");
+	}
+
+	OUT("static int check_permitted_alphabet_%d(const void *sptr) {\n",
+			ct->_compile_mark);
+		INDENT(+1);
+		if(use_table) {
+			OUT("int *table = permitted_alphabet_table_%d;\n",
+				ct->_compile_mark);
+			emit_alphabet_check_loop(arg, 0);
+		} else {
+			emit_alphabet_check_loop(arg, range);
+		}
+		OUT("return 1;\n");
+		INDENT(-1);
+	OUT("}\n");
+	OUT("\n");
+
+	asn1constraint_range_free(range);
+
+	return 0;
+}
+
+static int
+emit_alphabet_check_loop(arg_t *arg, asn1cnst_range_t *range) {
+	asn1p_expr_type_e etype;
+	asn1_integer_t natural_stop;
+
+	etype = _find_terminal_type(arg);
+
+	OUT("/* The underlying type is %s */\n",
+		ASN_EXPR_TYPE2STR(etype));
+	OUT("const %s_t *st = sptr;\n", MKID(arg->expr->Identifier));
+
+	switch(etype) {
+	case ASN_STRING_UTF8String:
+		OUT("uint8_t *ch = st->buf;\n");
+		OUT("uint8_t *end = ch + st->size;\n");
+		OUT("\n");
+		OUT("for(; ch < end; ch++) {\n");
+			INDENT(+1);
+			OUT("uint8_t cv = *ch;\n");
+			if(!range) OUT("if(cv >= 0x80) return 0;\n");
+		natural_stop = 0xffffffffUL;
+		break;
+	case ASN_STRING_UniversalString:
+		OUT("uint32_t *ch = st->buf;\n");
+		OUT("uint32_t *end = ch + st->size;\n");
+		OUT("\n");
+		OUT("if(st->size % 4) return 0; /* (size%4)! */\n");
+		OUT("for(; ch < end; ch++) {\n");
+			INDENT(+1);
+			OUT("uint32_t cv = (((uint8_t *)ch)[0] << 24)\n");
+			OUT("\t\t| (((uint8_t *)ch)[1] << 16)\n");
+			OUT("\t\t| (((uint8_t *)ch)[2] << 8)\n");
+			OUT("\t\t|  ((uint8_t *)ch)[3]\n");
+			if(!range) OUT("if(cv > 255) return 0;\n");
+		natural_stop = 0xffffffffUL;
+		break;
+	case ASN_STRING_BMPString:
+		OUT("uint16_t *ch = st->buf;\n");
+		OUT("uint16_t *end = ch + st->size;\n");
+		OUT("\n");
+		OUT("if(st->size % 2) return 0; /* (size%2)! */\n");
+		OUT("for(; ch < end; ch++) {\n");
+			INDENT(+1);
+			OUT("uint16_t cv = (((uint8_t *)ch)[0] << 8)\n");
+			OUT("\t\t| ((uint8_t *)ch)[1];\n");
+			if(!range) OUT("if(cv > 255) return 0;\n");
+		natural_stop = 0xffff;
+		break;
+	case ASN_BASIC_OCTET_STRING:
+	default:
+		OUT("uint8_t *ch = st->buf;\n");
+		OUT("uint8_t *end = ch + st->size;\n");
+		OUT("\n");
+		OUT("for(; ch < end; ch++) {\n");
+			INDENT(+1);
+			OUT("uint8_t cv = *ch;\n");
+		natural_stop = 0xff;
+		break;
+	}
+
+	if(range) {
+		OUT("if(!(");
+		emit_range_comparison_code(arg, range, "cv", 0, natural_stop);
+		OUT(")) return 0;\n");
+	} else {
+		OUT("if(!table[cv]) return 0;\n");
+	}
+
+	INDENT(-1);
+	OUT("}\n");
+
+	return 0;
+}
+
+static int
+emit_range_comparison_code(arg_t *arg, asn1cnst_range_t *range, const char *varname, asn1_integer_t natural_start, asn1_integer_t natural_stop) {
+	int ignore_left;
+	int ignore_right;
+	int i;
+
+	for(i = -1; i < range->el_count; i++) {
+		asn1cnst_range_t *r;
+		if(i == -1) {
+			if(range->el_count) continue;
+			r = range;
+		} else {
+			if(i) OUT(" || ");
+			r = range->elements[i];
+		}
+
+		if(r != range) OUT("(");
+
+		ignore_left = (r->left.type == ARE_MIN)
+				|| (natural_start != -1
+					&& r->left.value <= natural_start);
+		ignore_right = (r->right.type == ARE_MAX)
+				|| (natural_stop != -1
+					&& r->right.value >= natural_stop);
+		if(ignore_left && ignore_right) {
+			OUT("1 /* Constraint matches natural range of %s */",
+				varname);
+			continue;
+		}
+
+		if(ignore_left) {
+			OUT("%s <= %lld", varname,
+				(long long)r->right.value);
+		} else if(ignore_right) {
+			OUT("%s >= %lld", varname,
+				(long long)r->left.value);
+		} else if(r->left.value == r->right.value) {
+			OUT("%s == %lld", varname,
+				(long long)r->right.value);
+		} else {
+			OUT("%s >= %lld && %s <= %lld",
+				varname,
+				(long long)r->left.value,
+				varname,
+				(long long)r->right.value);
+		}
+		if(r != range) OUT(")");
+	}
+
+	return 0;
+}
+
+static int
+emit_size_determination_code(arg_t *arg, asn1p_expr_type_e etype) {
+
+	switch(etype) {
+	case ASN_BASIC_BIT_STRING:
+		OUT("if(st->size > 0) {\n");
+		OUT("\t/* Size in bits */\n");
+		OUT("\tsize = (st->size - 1) - (st->buf[0] & 0x7);\n");
+		OUT("} else {\n");
+		OUT("\tsize = 0;\n");
+		OUT("}\n");
+		break;
+	case ASN_STRING_UniversalString:
+		OUT("size = st->size >> 2;\t/* 4 byte per character */\n");
+		break;
+	case ASN_STRING_BMPString:
+		OUT("size = st->size >> 1;\t/* 2 byte per character */\n");
+		break;
+	case ASN_STRING_UTF8String:
+		OUT("size = UTF8String_length(st, td->name, app_errlog, app_key);\n");
+		OUT("if(size == (size_t)-1) return -1;\n");
+		break;
+	case ASN_CONSTR_SET_OF:
+	case ASN_CONSTR_SEQUENCE_OF:
+		OUT("{ /* Determine the number of elements */\n");
+			INDENT(+1);
+			OUT("A_%s_OF(void) *list;\n",
+				etype==ASN_CONSTR_SET_OF?"SET":"SEQUENCE");
+			OUT("(void *)list = st;\n");
+			OUT("size = list->count;\n");
+			INDENT(-1);
+		OUT("}\n");
+		break;
+	default:
+		if((etype & ASN_STRING_MASK)
+		|| etype == ASN_BASIC_OCTET_STRING) {
+			OUT("size = st->size;\n");
+			break;
+		} else {
+			const char *type_name = ASN_EXPR_TYPE2STR(etype);
+			if(!type_name) type_name = arg->expr->Identifier;
+			WARNING("SizeConstraint is not defined for %s",
+				type_name);
+			OUT_NOINDENT("#warning SizeConstraint "
+				"is not defined for %s!\n", type_name);
+			OUT("size = st->size;\n");
+		}
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+emit_value_determination_code(arg_t *arg, asn1p_expr_type_e etype) {
+
+	switch(etype) {
+	case ASN_BASIC_INTEGER:
+	case ASN_BASIC_ENUMERATED:
+		if(arg->flags & A1C_USE_NATIVE_INTEGERS) {
+			OUT("value = *(int *)st;\n");
+		} else {
+			OUT("if(asn1_INTEGER2long(st, &value)) {\n");
+				INDENT(+1);
+				OUT("_ASN_ERRLOG(app_errlog, app_key,\n");
+				OUT("\t\"%%s: value too large\", td->name);\n");
+				OUT("return -1;\n");
+				INDENT(-1);
+			OUT("}\n");
+		}
+		break;
+	case ASN_BASIC_BOOLEAN:
+		OUT("value = (*(int *)st) ? 1 : 0;\n");
+		break;
+	default:
+		WARNING("Value cannot be determined "
+			"for constraint check for %s at line %d\n",
+			arg->expr->Identifier, arg->expr->_lineno);
+		OUT("#error Value cannot be determined for %s at %d\n",
+			arg->expr->Identifier, arg->expr->_lineno);
+		break;
+	}
+
+	return 0;
+}
+
+static asn1p_expr_type_e
+_find_terminal_type(arg_t *arg) {
+	asn1p_expr_t *expr;
+	expr = asn1f_find_terminal_type_ex(arg->asn, arg->mod, arg->expr, NULL);
+	if(expr) return expr->expr_type;
+	return A1TC_INVALID;
+}
+
diff --git a/libasn1compiler/asn1c_constraint.h b/libasn1compiler/asn1c_constraint.h
new file mode 100644
index 0000000..481c0b3
--- /dev/null
+++ b/libasn1compiler/asn1c_constraint.h
@@ -0,0 +1,7 @@
+#ifndef	_ASN1C_CONSTRAINT_H_
+#define	_ASN1C_CONSTRAINT_H_
+
+int asn1c_emit_constraint_tables(arg_t *arg, asn1p_constraint_t *ct);
+int asn1c_emit_constraint_checking_code(arg_t *arg);
+
+#endif	/* _ASN1C_CONSTRAINT_H_ */
diff --git a/libasn1compiler/asn1c_internal.h b/libasn1compiler/asn1c_internal.h
index 08a490f..061b936 100644
--- a/libasn1compiler/asn1c_internal.h
+++ b/libasn1compiler/asn1c_internal.h
@@ -11,7 +11,7 @@
 #include <unistd.h>		/* for unlink(2) */
 #include <fcntl.h>		/* for open(2) */
 #include <glob.h>		/* for glob(3) */
-#include <string.h>
+#include <string.h>		/* for strlen(3) and memset(3) */
 #include <ctype.h>		/* for isalnum(3) */
 #include <stdarg.h>
 #include <errno.h>