Initial revision


git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@2 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/libasn1fix/asn1fix_bitstring.c b/libasn1fix/asn1fix_bitstring.c
new file mode 100644
index 0000000..0d3961d
--- /dev/null
+++ b/libasn1fix/asn1fix_bitstring.c
@@ -0,0 +1,230 @@
+#include "asn1fix_internal.h"
+
+int asn1f_fix_bit_string_value(arg_t *arg, asn1p_expr_t *ttype);
+static void asn1f_BS_remove_trailing_zero_bits(asn1p_value_t *value);
+static int asn1f_BS_unparsed_convert(arg_t *arg, asn1p_value_t *value, asn1p_expr_t *ttype);
+
+int
+asn1f_fix_bit_string(arg_t *arg) {
+	asn1p_expr_t *expr = arg->expr;
+	int r_value = 0;
+	int ret;
+
+	if(expr->meta_type == AMT_VALUE) {
+		asn1p_expr_t *ttype;
+
+		DEBUG("%s(%s) for line %d", __func__,
+			expr->Identifier, expr->_lineno);
+
+		ttype = asn1f_find_terminal_type(arg, expr, 0);
+		if(ttype && ttype->expr_type == ASN_BASIC_BIT_STRING) {
+			ret = asn1f_fix_bit_string_value(arg, ttype);
+			RET2RVAL(ret, r_value);
+		}
+	}
+
+	return r_value;
+}
+
+int
+asn1f_fix_bit_string_value(arg_t *arg, asn1p_expr_t *ttype) {
+	asn1p_expr_t *expr = arg->expr;
+	int r_value = 0;
+
+	DEBUG("%s(%s) for line %d", __func__,
+		expr->Identifier, expr->_lineno);
+
+	switch(expr->value->type) {
+	case ATV_UNPARSED:
+		/*
+		 * Most definitely we have something like
+		 * 	value BitStringType1 ::= { a, b, c }
+		 * which could not be parsed by the LALR parser, mostly
+		 * because it requires knowledge about BitStringType1
+		 * during the parsing. So, here's a little hack: we create
+		 * a buffer containing the full specification of a module,
+		 * which contains some pre-defined INTEGER type with the
+		 * opaque definition "{ a, b, c }" from the bit string.
+		 */
+		if(asn1f_BS_unparsed_convert(arg, expr->value, ttype)) {
+			r_value = -1;
+			break;
+		}
+		/* Fall through: remove trailing zero bits */
+	case ATV_BITVECTOR:
+		asn1f_BS_remove_trailing_zero_bits(expr->value);
+		break;
+	default:
+		break;
+	}
+
+	return r_value;
+}
+
+static void
+asn1f_BS_remove_trailing_zero_bits(asn1p_value_t *value) {
+	int lmfb = -1;	/* Last meaningful byte position */
+	int bits;	/* Number of bits in the BIT STRING value */
+	int b;
+
+	assert(value->type == ATV_BITVECTOR);
+
+	bits = value->value.binary_vector.size_in_bits;
+	/*
+	 * Figure out the rightmost meaningful byte.
+	 */
+	for(b = 0; b < ((bits + 7) >> 3); b++) {
+		uint8_t uc = value->value.binary_vector.bits[b];
+		if(uc && b > lmfb)
+			lmfb = b;
+	}
+	if(lmfb == -1) {
+		bits = 0;
+	} else {
+		uint8_t uc;
+		uc = value->value.binary_vector.bits[lmfb];
+		bits = (lmfb+1) * 8;
+		/*
+		 * Squeeze the bit string width until the rightmost
+		 * bit is set.
+		 */
+		for(; uc && (uc & 1) == 0; uc >>= 1)
+			bits--;
+		if(uc == 0) {
+			bits = lmfb * 8;
+		}
+	}
+	value->value.binary_vector.size_in_bits = bits;
+}
+
+static int
+asn1f_BS_unparsed_convert(arg_t *arg, asn1p_value_t *value, asn1p_expr_t *ttype) {
+	asn1p_t *asn;
+	asn1p_module_t *mod;
+	asn1p_expr_t *V;
+	asn1p_expr_t *bit;
+	asn1_integer_t aI;
+	uint8_t *bitbuf;
+	int bits;
+	int psize;
+	char *p;
+	int ret;
+	int r_value = 0;
+
+	assert(value->type == ATV_UNPARSED);
+
+	psize = value->value.string.size + 64;
+	p = malloc(psize);
+	if(p == NULL)
+		return -1;
+
+	ret = snprintf(p, psize,
+		"M DEFINITIONS ::=\nBEGIN\n"
+		"V ::= INTEGER %s\n"
+		"END\n",
+		value->value.string.buf
+	);
+	assert(ret < psize);
+	psize = ret;
+
+	asn = asn1p_parse_buffer(p, psize, A1P_NOFLAGS);
+	free(p);
+	if(asn == NULL) {
+		FATAL("Cannot parse BIT STRING value %s "
+			"defined as %s at line %d",
+			arg->expr->Identifier,
+			value->value.string.buf,
+			arg->expr->_lineno
+		);
+		return -1;
+	}
+
+	mod = TQ_FIRST(&(asn->modules));
+	assert(mod);
+	V = TQ_FIRST(&(mod->members));
+	assert(V);
+	assert(strcmp(V->Identifier, "V") == 0);
+	assert(TQ_FIRST(&(V->members)));
+
+	/*
+	 * Simple loop just to fetch the maximal bit position
+	 * out of the BIT STRING value defined as NamedBitList.
+	 */
+	aI = -1;
+	TQ_FOR(bit, &(V->members), next) {
+		asn1p_expr_t *bitdef;
+		bitdef = asn1f_lookup_child(ttype, bit->Identifier);
+		if(bitdef && bitdef->value
+		&& bitdef->value->type == ATV_INTEGER) {
+			if(bitdef->value->value.v_integer > aI)
+				aI = bitdef->value->value.v_integer;
+		}
+	}
+
+	if(aI > 1024 * 1024 * 8) {	/* One megabyte */
+		FATAL("Unsupportedly large BIT STRING value \"%s\" "
+			"defined at line %d "
+			"(larger than 1MByte)",
+			arg->expr->Identifier,
+			arg->expr->_lineno
+		);
+		asn1p_free(asn);
+		return -1;
+	}
+
+	bits = aI + 1;	/* Number of bits is more than a last bit position */
+	bitbuf = calloc(1, 1 + ((bits + 7) / 8));
+	if(bitbuf == NULL) {
+		asn1p_free(asn);
+		return -1;
+	}
+
+	TQ_FOR(bit, &(V->members), next) {
+		asn1p_expr_t *bitdef;
+		int set_bit_pos;
+
+		if(bit->value) {
+			WARNING("Identifier \"%s\" at line %d "
+				"must not have a value",
+				bit->Identifier, bit->_lineno);
+			RET2RVAL(1, r_value);
+		}
+		bitdef = asn1f_lookup_child(ttype, bit->Identifier);
+		if(bitdef == NULL) {
+			FATAL("Identifier \"%s\" at line %d is not defined "
+				"in the \"%s\" type definition at line %d",
+				bit->Identifier,
+				bit->_lineno,
+				ttype->Identifier,
+				ttype->_lineno
+			);
+			RET2RVAL(-1, r_value);
+			continue;
+		}
+		if(bitdef->value == NULL
+		|| bitdef->value->type != ATV_INTEGER) {
+			FATAL("Broken identifier "
+				"\"%s\" at line %d "
+				"referenced by \"%s\" at line %d",
+				bitdef->Identifier,
+				bitdef->_lineno,
+				arg->expr->Identifier,
+				arg->expr->_lineno
+			);
+			RET2RVAL(-1, r_value);
+			continue;
+		}
+
+		assert(bitdef->value->value.v_integer < bits);
+		set_bit_pos = bitdef->value->value.v_integer;
+		bitbuf[set_bit_pos>>3] |= 1 << (7-(set_bit_pos % 8));
+	}
+
+	asn1p_free(asn);
+	free(value->value.string.buf);
+	value->type = ATV_BITVECTOR;
+	value->value.binary_vector.bits = bitbuf;
+	value->value.binary_vector.size_in_bits = bits;
+
+	return r_value;
+}