improved INTEGER printing


git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@55 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/skeletons/INTEGER.c b/skeletons/INTEGER.c
index d20379f..120b94f 100644
--- a/skeletons/INTEGER.c
+++ b/skeletons/INTEGER.c
@@ -186,8 +186,8 @@
 int
 INTEGER_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
 	asn_app_consume_bytes_f *cb, void *app_key) {
+	char scratch[32];	/* Enough for 64-bit integer */
 	const INTEGER_t *st = sptr;
-	char scratch[32];
 	uint8_t *buf = st->buf;
 	uint8_t *buf_end = st->buf + st->size;
 	signed long accum;
@@ -202,9 +202,24 @@
 	if(st->size == 0)
 		return cb("0", 1, app_key);
 
+	/*
+	 * Advance buf pointer until the start of the value's body.
+	 * This will make us able to process large integers using simple case,
+	 * when the actual value is small
+	 * (0x0000000000abcdef would yield a fine 0x00abcdef)
+	 */
+	/* Skip the insignificant leading bytes */
+	for(; buf < buf_end-1; buf++) {
+		switch(*buf) {
+		case 0x00: if((buf[1] & 0x80) == 0) continue; break;
+		case 0xff: if((buf[1] & 0x80) != 0) continue; break;
+		}
+		break;
+	}
+
 	/* Simple case: the integer size is small */
-	if((size_t)st->size < sizeof(accum) || (st->buf[0] & 0x80)) {
-		accum = (st->buf[0] & 0x80) ? -1 : 0;
+	if((size_t)(buf_end - buf) <= sizeof(accum)) {
+		accum = (*buf & 0x80) ? -1 : 0;
 		for(; buf < buf_end; buf++)
 			accum = (accum << 8) | *buf;
 		ret = snprintf(scratch, sizeof(scratch), "%ld", accum);
@@ -213,9 +228,10 @@
 	}
 
 	/* Output in the long xx:yy:zz... format */
+	/* TODO: replace with generic algorithm (Knuth TAOCP Vol 2, 4.3.1) */
 	for(p = scratch; buf < buf_end; buf++) {
 		static char h2c[16] = "0123456789ABCDEF";
-		if((p - scratch) >= (ssize_t)(sizeof(scratch) / 2)) {
+		if((p - scratch) >= (ssize_t)(sizeof(scratch) - 4)) {
 			/* Flush buffer */
 			if(cb(scratch, p - scratch, app_key))
 				return -1;
@@ -225,6 +241,8 @@
 		*p++ = h2c[*buf & 0x0F];
 		*p++ = ':';
 	}
+	if(p != scratch)
+		p--;	/* Remove the last ':' */
 
 	return cb(scratch, p - scratch, app_key);
 }
diff --git a/skeletons/NativeInteger.c b/skeletons/NativeInteger.c
index 8203695..8f13a59 100644
--- a/skeletons/NativeInteger.c
+++ b/skeletons/NativeInteger.c
@@ -166,7 +166,7 @@
 NativeInteger_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
 	asn_app_consume_bytes_f *cb, void *app_key) {
 	const int *Int = sptr;
-	char scratch[32];
+	char scratch[32];	/* Enough for 64-bit int */
 	int ret;
 
 	(void)td;	/* Unused argument */
diff --git a/skeletons/tests/check-INTEGER.c b/skeletons/tests/check-INTEGER.c
index 4053075..5aa400d 100644
--- a/skeletons/tests/check-INTEGER.c
+++ b/skeletons/tests/check-INTEGER.c
@@ -5,9 +5,22 @@
 #include "../der_encoder.c"
 #include "../constraints.c"
 
+static char *shared_scratch_start;
+
+static int _print2buf(const void *buf, size_t size, void *key) {
+	(void)key;
+	memcpy(shared_scratch_start, buf, size);
+	shared_scratch_start += size;
+	*shared_scratch_start = '\0';	/* 0-termination */
+	return 0;
+}
+
 static void
 check(uint8_t *buf, int size, long check_long, int check_ret) {
+	char scratch[128];
+	char verify[32];
 	INTEGER_t val;
+	uint8_t *buf_end = buf + size;
 	int ret;
 	long rlong = 123;
 
@@ -17,13 +30,35 @@
 	val.buf = buf;
 	val.size = size;
 
+	printf("Testing: [");
+	for(; buf < buf_end; buf++) {
+		if(buf != val.buf) printf(":");
+		printf("%02x", *buf);
+	}
+	printf("]: ");
 
 	ret = asn1_INTEGER2long(&val, &rlong);
-	printf("Testing (%ld, %d) vs (%ld, %d)\n",
+	printf(" (%ld, %d) vs (%ld, %d)\n",
 		rlong, ret, check_long, check_ret);
 	assert(ret == check_ret);
-	if(ret == -1) return;
 	assert(rlong == check_long);
+
+	shared_scratch_start = scratch;
+	ret = INTEGER_print(&asn1_DEF_INTEGER, &val, 0, _print2buf, scratch);
+	assert(shared_scratch_start < scratch + sizeof(scratch));
+	assert(ret == 0);
+	ret = snprintf(verify, sizeof(verify), "%ld", check_long);
+	assert(ret < sizeof(verify));
+	ret = strcmp(scratch, verify);
+	printf("         [%s] vs [%s]: %d%s\n",
+		scratch, verify, ret,
+		(check_ret == -1)?" (expected to fail)":""
+		);
+	if(check_ret == -1) {
+		assert(strcmp(scratch, verify));
+	} else {
+		assert(strcmp(scratch, verify) == 0);
+	}
 }
 
 int