XER can decode long values


git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@1185 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/skeletons/INTEGER.c b/skeletons/INTEGER.c
index 3643465..5a0ecce 100644
--- a/skeletons/INTEGER.c
+++ b/skeletons/INTEGER.c
@@ -297,6 +297,20 @@
 		INTEGER__compar_value2enum);
 }
 
+static int
+INTEGER_st_prealloc(INTEGER_t *st, int min_size) {
+	void *p = MALLOC(min_size + 1);
+	if(p) {
+		void *b = st->buf;
+		st->size = 0;
+		st->buf = p;
+		FREEMEM(b);
+		return 0;
+	} else {
+		return -1;
+	}
+}
+
 /*
  * Decode the chunk of XML text encoding INTEGER.
  */
@@ -310,11 +324,19 @@
 	const char *lstop = lstart + chunk_size;
 	enum {
 		ST_SKIPSPACE,
+		ST_SKIPSPHEX,
 		ST_WAITDIGITS,
 		ST_DIGITS,
+		ST_HEXDIGIT1,
+		ST_HEXDIGIT2,
+		ST_HEXCOLON,
 		ST_EXTRASTUFF
 	} state = ST_SKIPSPACE;
 
+	if(chunk_size)
+		ASN_DEBUG("INTEGER body %d 0x%2x..0x%2x",
+			chunk_size, *lstart, lstop[-1]);
+
 	/*
 	 * We may have received a tag here. It will be processed inline.
 	 * Use strtoul()-like code and serialize the result.
@@ -323,7 +345,19 @@
 		int lv = *lp;
 		switch(lv) {
 		case 0x09: case 0x0a: case 0x0d: case 0x20:
-			if(state == ST_SKIPSPACE) continue;
+			switch(state) {
+			case ST_SKIPSPACE:
+			case ST_SKIPSPHEX:
+				continue;
+			case ST_HEXCOLON:
+				if(xer_is_whitespace(lp, lstop - lp)) {
+					lp = lstop - 1;
+					continue;
+				}
+				break;
+			default:
+				break;
+			}
 			break;
 		case 0x2d:	/* '-' */
 			if(state == ST_SKIPSPACE) {
@@ -340,7 +374,24 @@
 			break;
 		case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
 		case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
-			if(state != ST_DIGITS) state = ST_DIGITS;
+			switch(state) {
+			case ST_DIGITS: break;
+			case ST_SKIPSPHEX:	/* Fall through */
+			case ST_HEXDIGIT1:
+				value = (lv - 0x30) << 4;
+				state = ST_HEXDIGIT2;
+				continue;
+			case ST_HEXDIGIT2:
+				value += (lv - 0x30);
+				state = ST_HEXCOLON;
+				st->buf[st->size++] = value;
+				continue;
+			case ST_HEXCOLON:
+				return XPBD_BROKEN_ENCODING;
+			default:
+				state = ST_DIGITS;
+				break;
+			}
 
 		    {
 			long new_value = value * 10;
@@ -381,22 +432,86 @@
 				ASN_DEBUG("Unknown identifier for INTEGER");
 			}
 			return XPBD_BROKEN_ENCODING;
+		case 0x3a:	/* ':' */
+			if(state == ST_HEXCOLON) {
+				/* This colon is expected */
+				state = ST_HEXDIGIT1;
+				continue;
+			} else if(state == ST_DIGITS) {
+				/* The colon here means that we have
+				 * decoded the first two hexadecimal
+				 * places as a decimal value.
+				 * Switch decoding mode. */
+				ASN_DEBUG("INTEGER re-evaluate as hex form");
+				if(INTEGER_st_prealloc(st, (chunk_size/3) + 1))
+					return XPBD_SYSTEM_FAILURE;
+				state = ST_SKIPSPHEX;
+				lp = lstart - 1;
+				continue;
+			} else {
+				ASN_DEBUG("state %d at %d", state, lp - lstart);
+				break;
+			}
+		/* [A-Fa-f] */
+		case 0x41:case 0x42:case 0x43:case 0x44:case 0x45:case 0x46:
+		case 0x61:case 0x62:case 0x63:case 0x64:case 0x65:case 0x66:
+			switch(state) {
+			case ST_SKIPSPHEX:
+			case ST_SKIPSPACE: /* Fall through */
+			case ST_HEXDIGIT1:
+				value = lv - ((lv < 0x61) ? 0x41 : 0x61);
+				value += 10;
+				value <<= 4;
+				state = ST_HEXDIGIT2;
+				continue;
+			case ST_HEXDIGIT2:
+				value += lv - ((lv < 0x61) ? 0x41 : 0x61);
+				value += 10;
+				st->buf[st->size++] = value;
+				state = ST_HEXCOLON;
+				continue;
+			case ST_DIGITS:
+				ASN_DEBUG("INTEGER re-evaluate as hex form");
+				if(INTEGER_st_prealloc(st, (chunk_size/3) + 1))
+					return XPBD_SYSTEM_FAILURE;
+				state = ST_SKIPSPHEX;
+				lp = lstart - 1;
+				continue;
+			default:
+				break;
+			}
+			break;
 		}
 
 		/* Found extra non-numeric stuff */
+		ASN_DEBUG("Found non-numeric 0x%2x at %d",
+			lv, lp - lstart);
 		state = ST_EXTRASTUFF;
 		break;
 	}
 
-	if(state != ST_DIGITS) {
+	switch(state) {
+	case ST_DIGITS:
+		/* Everything is cool */
+		break;
+	case ST_HEXCOLON:
+		st->buf[st->size] = 0;	/* Just in case termination */
+		return XPBD_BODY_CONSUMED;
+	case ST_HEXDIGIT1:
+	case ST_HEXDIGIT2:
+	case ST_SKIPSPHEX:
+		return XPBD_BROKEN_ENCODING;
+	default:
 		if(xer_is_whitespace(lp, lstop - lp)) {
 			if(state != ST_EXTRASTUFF)
 				return XPBD_NOT_BODY_IGNORE;
-			/* Fall through */
+			break;
 		} else {
-			ASN_DEBUG("No useful digits in output");
+			ASN_DEBUG("INTEGER: No useful digits (state %d)",
+				state);
 			return XPBD_BROKEN_ENCODING;	/* No digits */
 		}
+		break;
 	}
 
 	value *= sign;	/* Change sign, if needed */