more PER support

diff --git a/libasn1compiler/asn1c_C.c b/libasn1compiler/asn1c_C.c
index 2acb5ea..f7a6c89 100644
--- a/libasn1compiler/asn1c_C.c
+++ b/libasn1compiler/asn1c_C.c
@@ -40,9 +40,8 @@
 static int expr_break_recursion(arg_t *arg, asn1p_expr_t *expr);
 static int expr_as_xmlvaluelist(arg_t *arg, asn1p_expr_t *expr);
 static int expr_elements_count(arg_t *arg, asn1p_expr_t *expr);
-static int emit_single_member_PER_constraint(arg_t *arg, asn1cnst_range_t *range, char *type);
-static int emit_single_member_PER_constraints(arg_t *arg, asn1p_expr_t *expr);
-static int emit_members_PER_constraints(arg_t *arg);
+static int emit_single_member_PER_constraint(arg_t *arg, asn1cnst_range_t *range, int juscountvalues, char *type);
+static int emit_member_PER_constraints(arg_t *arg, asn1p_expr_t *expr);
 static int emit_member_table(arg_t *arg, asn1p_expr_t *expr);
 static int emit_tag2member_map(arg_t *arg, tag2el_t *tag2el, int tag2el_count, const char *opt_modifier);
 static int emit_include_dependencies(arg_t *arg);
@@ -340,8 +339,6 @@
 	if(expr_elements_count(arg, expr)) {
 		int comp_mode = 0;	/* {root,ext=1,root,root,...} */
 
-		if(emit_members_PER_constraints(arg))
-			return -1;
 		OUT("static asn_TYPE_member_t asn_MBR_%s_%d[] = {\n",
 			MKID(expr), expr->_type_unique_index);
 
@@ -580,8 +577,6 @@
 	if(expr_elements_count(arg, expr)) {
 		int comp_mode = 0;	/* {root,ext=1,root,root,...} */
 
-		if(emit_members_PER_constraints(arg))
-			return -1;
 		OUT("static asn_TYPE_member_t asn_MBR_%s_%d[] = {\n",
 			MKID(expr), expr->_type_unique_index);
 	
@@ -782,8 +777,6 @@
 	/*
 	 * Print out the table according to which parsing is performed.
 	 */
-	if(emit_members_PER_constraints(arg))
-		return -1;
 	OUT("static asn_TYPE_member_t asn_MBR_%s_%d[] = {\n",
 		MKID(expr), expr->_type_unique_index);
 	INDENT(+1);
@@ -932,8 +925,6 @@
 	 */
 	if(expr_elements_count(arg, expr)) {
 
-		if(emit_members_PER_constraints(arg))
-			return -1;
 		OUT("static asn_TYPE_member_t asn_MBR_%s_%d[] = {\n",
 			MKID(expr), expr->_type_unique_index);
 
@@ -1157,16 +1148,6 @@
 		return 0;
 	}
 
-	REDIR(OT_STAT_DEFS);
-
-	/*
-	 * Print out asn_DEF_<type>_[all_]tags[] vectors.
-	 */
-	tv_mode = emit_tags_vectors(arg, expr, &tags_count, &all_tags_count);
-
-	emit_type_DEF(arg, expr, tv_mode, tags_count, all_tags_count,
-		0, etd_spec);
-
 	REDIR(OT_CODE);
 
 	/*
@@ -1182,6 +1163,7 @@
 		INDENT(+1);
 		OUT("\t\tasn_app_constraint_failed_f *ctfailcb, void *app_key) {");
 		OUT("\n");
+		DEBUG("expr constraint checking code for %s", p);
 		if(asn1c_emit_constraint_checking_code(arg) == 1) {
 			OUT("/* Replace with underlying type checker */\n");
 			OUT("td->check_constraints "
@@ -1195,6 +1177,20 @@
 		OUT("\n");
 	}
 
+	REDIR(OT_STAT_DEFS);
+
+	/*
+	 * Print out asn_DEF_<type>_[all_]tags[] vectors.
+	 */
+	tv_mode = emit_tags_vectors(arg, expr, &tags_count, &all_tags_count);
+	DEBUG("emit tag vectors for %s %d, %d, %d", expr->Identifier,
+		tv_mode, tags_count, all_tags_count);
+
+	emit_type_DEF(arg, expr, tv_mode, tags_count, all_tags_count,
+		0, etd_spec);
+
+	REDIR(OT_CODE);
+
 	/*
 	 * Emit suicidal functions.
 	 */
@@ -1588,7 +1584,7 @@
 		if(p)	*tag2el = p;
 		else	return -1;
 
-		DEBUG("Found tag for %s: %ld",
+		if(0) DEBUG("Found tag for %s: %ld",
 			arg->expr->Identifier,
 			(long)tag.tag_value);
 
@@ -1678,14 +1674,17 @@
 
 	/* Fetch a chain of tags */
 	tags_count = asn1f_fetch_tags(arg->asn, expr->module, expr, &tags, 0);
-	if(tags_count < 0)
+	if(tags_count < 0) {
+		DEBUG("fail to fetch tags for %s", expr->Identifier);
 		return -1;
+	}
 
 	/* Fetch a chain of tags */
 	all_tags_count = asn1f_fetch_tags(arg->asn, expr->module, expr,
 		&all_tags, AFT_FULL_COLLECT);
 	if(all_tags_count < 0) {
 		if(tags) free(tags);
+		DEBUG("fail to fetch tags chain for %s", expr->Identifier);
 		return -1;
 	}
 
@@ -1772,8 +1771,24 @@
 	return A1TC_INVALID;
 }
 
+static asn1c_integer_t
+PER_FROM_alphabet_characters(asn1cnst_range_t *range) {
+	asn1c_integer_t numchars = 0;
+	if(range->el_count) {
+		int i;
+		for(i = 0; i < range->el_count; i++)
+			numchars
+			+= PER_FROM_alphabet_characters(range->elements[i]);
+	} else {
+		assert(range->left.type == ARE_VALUE);
+		assert(range->right.type == ARE_VALUE);
+		numchars = 1 + (range->right.value - range->left.value);
+	}
+	return numchars;
+}
+
 static int
-emit_single_member_PER_constraint(arg_t *arg, asn1cnst_range_t *range, char *type) {
+emit_single_member_PER_constraint(arg_t *arg, asn1cnst_range_t *range, int alphabetsize, char *type) {
 	if(!range || range->incompatible || range->not_PER_visible) {
 		OUT("{ APC_UNCONSTRAINED,\t-1, -1,  0,  0 }");
 		return 0;
@@ -1790,6 +1805,11 @@
 			if(range->empty_constraint)
 				r = 0;
 
+			if(alphabetsize) {
+				/* X.691: 27.5.2 */
+				r = PER_FROM_alphabet_characters(range);
+			}
+
 			/* Compute real constraint */
 			for(rbits = 0; rbits < (8 * sizeof(r)); rbits++) {
 				if(r <= cover)
@@ -1823,6 +1843,7 @@
 						ebits = -1;
 				}
 			}
+
 			OUT("{ APC_CONSTRAINED%s,%s% d, % d, ",
 				range->extensible
 					? " | APC_EXTENSIBLE" : "",
@@ -1867,10 +1888,27 @@
 }
 
 static int
-emit_single_member_PER_constraints(arg_t *arg, asn1p_expr_t *expr) {
+emit_member_PER_constraints(arg_t *arg, asn1p_expr_t *expr) {
+	int save_target = arg->target->target;
 	asn1cnst_range_t *range;
 	asn1p_expr_type_e etype;
 
+	if((arg->flags & A1C_GEN_PER)
+	&& (expr->constraints
+		|| expr->expr_type == ASN_BASIC_ENUMERATED
+		|| expr->expr_type == ASN_CONSTR_CHOICE)
+	) {
+		/* Fall through */
+	} else {
+		return 0;
+	}
+
+	REDIR(OT_CTDEFS);
+
+	OUT("static asn_per_constraints_t "
+		"asn_PER_%s_constr_%d = {\n",
+		MKID(expr), expr->_type_unique_index);
+
 	etype = expr_get_type(arg, expr);
 
 	INDENT(+1);
@@ -1903,20 +1941,21 @@
 		tmprng.left.value = 0;
 		tmprng.right.type = ARE_VALUE;
 		tmprng.right.value = eidx < 0 ? 0 : eidx;
-		if(emit_single_member_PER_constraint(arg, &tmprng, 0))
+		if(emit_single_member_PER_constraint(arg, &tmprng, 0, 0))
 			return -1;
 	} else if(etype & ASN_STRING_KM_MASK) {
 		range = asn1constraint_compute_PER_range(etype,
 				expr->combined_constraints, ACT_CT_FROM,
 				0, 0, 0);
-		if(emit_single_member_PER_constraint(arg, range, 0))
+		DEBUG("Emitting FROM constraint for %s", expr->Identifier);
+		if(emit_single_member_PER_constraint(arg, range, 1, 0))
 			return -1;
 		asn1constraint_range_free(range);
 	} else {
 		range = asn1constraint_compute_PER_range(etype,
 				expr->combined_constraints, ACT_EL_RANGE,
 				0, 0, 0);
-		if(emit_single_member_PER_constraint(arg, range, 0))
+		if(emit_single_member_PER_constraint(arg, range, 0, 0))
 			return -1;
 		asn1constraint_range_free(range);
 	}
@@ -1924,36 +1963,55 @@
 
 	range = asn1constraint_compute_PER_range(etype,
 			expr->combined_constraints, ACT_CT_SIZE, 0, 0, 0);
-	if(emit_single_member_PER_constraint(arg, range, "SIZE"))
+	if(emit_single_member_PER_constraint(arg, range, 0, "SIZE"))
 		return -1;
 	asn1constraint_range_free(range);
-	OUT("\n");
+	OUT(",\n");
+
+	if((etype & ASN_STRING_KM_MASK) && (expr->_mark & TM_PERFROMCT)) {
+		int old_target = arg->target->target;
+		REDIR(OT_CODE);
+
+		OUT("static int asn_PER_MAP_%s_%d_v2c(unsigned int value) {\n",
+			MKID(expr), expr->_type_unique_index);
+		OUT("\tif(value >= sizeof(permitted_alphabet_table_%d)/"
+			"sizeof(permitted_alphabet_table_%d[0]))\n",
+			expr->_type_unique_index,
+			expr->_type_unique_index);
+		OUT("\t\treturn -1;\n");
+		OUT("\treturn permitted_alphabet_table_%d[value] - 1;\n",
+			expr->_type_unique_index);
+		OUT("}\n");
+
+		OUT("static int asn_PER_MAP_%s_%d_c2v(unsigned int code) {\n",
+			MKID(expr), expr->_type_unique_index);
+		OUT("\tif(code >= sizeof(permitted_alphabet_code2value_%d)/"
+			"sizeof(permitted_alphabet_code2value_%d[0]))\n",
+			expr->_type_unique_index,
+			expr->_type_unique_index);
+		OUT("\t\treturn -1;\n");
+		OUT("\treturn permitted_alphabet_code2value_%d[code];\n",
+			expr->_type_unique_index);
+		OUT("}\n");
+
+		REDIR(old_target);
+
+		OUT("asn_PER_MAP_%s_%d_v2c,\t/* Value to PER code map */\n",
+			MKID(expr), expr->_type_unique_index);
+		OUT("asn_PER_MAP_%s_%d_c2v\t/* PER code to value map */\n",
+			MKID(expr), expr->_type_unique_index);
+	} else if(etype & ASN_STRING_KM_MASK) {
+		DEBUG("No PER value map necessary for %s", MKID(expr));
+		OUT("0, 0\t/* No PER character map necessary */\n");
+	} else {
+		OUT("0, 0\t/* No PER value map */\n");
+	}
 
 	INDENT(-1);
 
-	return 0;
-}
+	OUT("};\n");
 
-static int
-emit_members_PER_constraints(arg_t *arg) {
-	asn1p_expr_t *expr = arg->expr;
-	asn1p_expr_t *v;
-
-	if(!(arg->flags & A1C_GEN_PER))
-		return 0;
-
-	TQ_FOR(v, &(expr->members), next) {
-		if(v->constraints
-		|| v->expr_type == ASN_BASIC_ENUMERATED
-		|| v->expr_type == ASN_CONSTR_CHOICE) {
-			OUT("static asn_per_constraints_t "
-				"asn_PER_memb_%s_constr_%d = {\n",
-				MKID(v), v->_type_unique_index);
-			if(emit_single_member_PER_constraints(arg, v))
-				return -1;
-			OUT("};\n");
-		}
-	}
+	REDIR(save_target);
 
 	return 0;
 }
@@ -2138,7 +2196,7 @@
 		if(expr->constraints
 		|| expr->expr_type == ASN_BASIC_ENUMERATED
 		|| expr->expr_type == ASN_CONSTR_CHOICE) {
-			OUT("&asn_PER_memb_%s_constr_%d,\n",
+			OUT("&asn_PER_%s_constr_%d,\n",
 				MKID(expr),
 				expr->_type_unique_index);
 		} else {
@@ -2182,6 +2240,7 @@
 	OUT("\t\tasn_app_constraint_failed_f *ctfailcb, void *app_key) {\n");
 	tmp_arg = *arg;
 	tmp_arg.expr = expr;
+	DEBUG("member constraint checking code for %s", p);
 	if(asn1c_emit_constraint_checking_code(&tmp_arg) == 1) {
 		OUT("return td->check_constraints"
 			"(td, sptr, ctfailcb, app_key);\n");
@@ -2190,6 +2249,9 @@
 	OUT("}\n");
 	OUT("\n");
 
+	if(emit_member_PER_constraints(arg, expr))
+		return -1;
+
 	REDIR(save_target);
 
 	return 0;
@@ -2206,17 +2268,8 @@
 
 	terminal = asn1f_find_terminal_type_ex(arg->asn, expr);
 
-	if((arg->flags & A1C_GEN_PER)
-	&& (expr->constraints
-		|| expr->expr_type == ASN_BASIC_ENUMERATED
-		|| expr->expr_type == ASN_CONSTR_CHOICE)
-	) {
-		OUT("static asn_per_constraints_t asn_PER_%s_constr_%d = {\n",
-			p, expr->_type_unique_index);
-		if(emit_single_member_PER_constraints(arg, expr))
-			return -1;
-		OUT("};\n");
-	}
+	if(emit_member_PER_constraints(arg, expr))
+		return -1;
 
 	if(HIDE_INNER_DEFS)
 		OUT("static /* Use -fall-defs-global to expose */\n");
diff --git a/libasn1compiler/asn1c_constraint.c b/libasn1compiler/asn1c_constraint.c
index da54f44..2fd8885 100644
--- a/libasn1compiler/asn1c_constraint.c
+++ b/libasn1compiler/asn1c_constraint.c
@@ -316,7 +316,8 @@
 		}
 		OUT("};\n");
 
-		if((arg->flags & A1C_GEN_PER)) {
+		if((arg->flags & A1C_GEN_PER)
+		&& (etype & ASN_STRING_KM_MASK)) {
 		    int c;
 		    OUT("static int permitted_alphabet_code2value_%d[%d] = {\n",
 			arg->expr->_type_unique_index, cardinal);
@@ -328,6 +329,8 @@
 		    }
 		    OUT("};\n");
 		    OUT("\n");
+		    DEBUG("code2value map gen for %s", arg->expr->Identifier);
+		    arg->expr->_mark |= TM_PERFROMCT;
 		}
 
 		OUT("\n");
diff --git a/libasn1compiler/asn1c_misc.c b/libasn1compiler/asn1c_misc.c
index c8adce6..543a165 100644
--- a/libasn1compiler/asn1c_misc.c
+++ b/libasn1compiler/asn1c_misc.c
@@ -158,7 +158,8 @@
 		while(top_parent->parent_expr)
 			top_parent = top_parent->parent_expr;
 
-	DEBUG("asn1c_type_name(%s: 0x%x)", expr->Identifier, expr->expr_type);
+	if(0) DEBUG("asn1c_type_name(%s: 0x%x)",
+		expr->Identifier, expr->expr_type);
 
 	switch(expr->expr_type) {
 	case A1TC_REFERENCE:
diff --git a/libasn1compiler/asn1c_out.h b/libasn1compiler/asn1c_out.h
index 3b1f055..0523cbc 100644
--- a/libasn1compiler/asn1c_out.h
+++ b/libasn1compiler/asn1c_out.h
@@ -22,6 +22,7 @@
 		OT_POST_INCLUDE,/* #include after type definition */
 		OT_CTABLES,	/* Constraint tables */
 		OT_CODE,	/* Some code */
+		OT_CTDEFS,	/* Constraint definitions */
 		OT_STAT_DEFS,	/* Static definitions */
 		OT_MAX
 	} target;
@@ -34,7 +35,7 @@
 } compiler_streams_t;
 
 static char *_compiler_stream2str[] __attribute__ ((unused))
-    = { "IGNORE", "INCLUDES", "DEPS", "FWD-DECLS", "TYPE-DECLS", "FUNC-DECLS", "POST-INCLUDE", "CTABLES", "CODE", "STAT-DEFS" };
+    = { "IGNORE", "INCLUDES", "DEPS", "FWD-DECLS", "TYPE-DECLS", "FUNC-DECLS", "POST-INCLUDE", "CTABLES", "CODE", "CTDEFS", "STAT-DEFS" };
 
 int asn1c_compiled_output(arg_t *arg, const char *fmt, ...);
 
diff --git a/libasn1compiler/asn1c_save.c b/libasn1compiler/asn1c_save.c
index 4236e95..1785d7d 100644
--- a/libasn1compiler/asn1c_save.c
+++ b/libasn1compiler/asn1c_save.c
@@ -273,10 +273,12 @@
 		fwrite(ot->buf, ot->len, 1, fp_c);
 	TQ_FOR(ot, &(cs->destination[OT_CODE].chunks), next)
 		fwrite(ot->buf, ot->len, 1, fp_c);
+	TQ_FOR(ot, &(cs->destination[OT_CTDEFS].chunks), next)
+		fwrite(ot->buf, ot->len, 1, fp_c);
 	TQ_FOR(ot, &(cs->destination[OT_STAT_DEFS].chunks), next)
 		fwrite(ot->buf, ot->len, 1, fp_c);
 
-	assert(OT_MAX == 10);	/* Protection from reckless changes */
+	assert(OT_MAX == 11);	/* Protection from reckless changes */
 
 	fclose(fp_c);
 	fclose(fp_h);