universal get_arcs() for all interger type sizes

diff --git a/skeletons/OBJECT_IDENTIFIER.c b/skeletons/OBJECT_IDENTIFIER.c
index 0a27c8c..c417616 100644
--- a/skeletons/OBJECT_IDENTIFIER.c
+++ b/skeletons/OBJECT_IDENTIFIER.c
@@ -3,6 +3,7 @@
  * Redistribution and modifications are permitted subject to BSD license.
  */
 #include <OBJECT_IDENTIFIER.h>
+#include <limits.h>	/* for CHAR_BIT */
 #include <assert.h>
 #include <errno.h>
 
@@ -91,34 +92,100 @@
 }
 
 int
-OBJECT_IDENTIFIER_get_arc_l(uint8_t *arcbuf, int arclen, int add, unsigned long *rvalue) {
-	unsigned long accum;
-	uint8_t *arcend = arcbuf + arclen;
+OBJECT_IDENTIFIER_get_single_arc(uint8_t *arcbuf, unsigned int arclen, signed int add, void *rvbuf, unsigned int rvsize) {
+	unsigned LE = 1;	/* Little endian (x86) */
+	uint8_t *arcend = arcbuf + arclen;	/* End of arc */
+	void *rvstart = rvbuf;	/* Original start of the value buffer */
+	unsigned int cache = 0;	/* No more than 14 significant bits */
+	int inc;	/* Return value growth direction */
 
-	if((size_t)arclen * 7 > 8 * sizeof(accum)) {
-		if((size_t)arclen * 7 <= 8 * (sizeof(accum) + 1)) {
-			if((*arcbuf & ~0x8f)) {
+	rvsize *= CHAR_BIT;	/* bytes to bits */
+	arclen *= 7;		/* bytes to bits */
+
+	/*
+	 * The arc has the number of bits
+	 * cannot be represented using supplied return value type.
+	 */
+	if(arclen > rvsize) {
+		if(arclen > (rvsize + CHAR_BIT)) {
+			errno = ERANGE;	/* Overflow */
+			return -1;
+		} else {
+			/*
+			 * Even if the number of bits in the arc representation
+			 * is higher than the width of supplied * return value
+			 * type, there is still possible to fit it when there
+			 * are few unused high bits in the arc value
+			 * representaion.
+			 */
+			uint8_t mask = (0xff << (7-(arclen - rvsize))) & 0x7f;
+			if((*arcbuf & mask)) {
 				errno = ERANGE;	/* Overflow */
 				return -1;
 			}
-		} else {
+			/* Fool the routine computing unused bits */
+			arclen -= 7;
+			cache = *arcbuf & 0x7f;
+			arcbuf++;
+		}
+	}
+
+#ifndef	WORDS_BIGENDIAN
+	if(*(unsigned char *)&LE) {	/* Little endian (x86) */
+		/* "Convert" to big endian */
+		rvbuf += rvsize / CHAR_BIT;
+		inc = -1;	/* Descending */
+	} else {
+		inc = +1;	/* Ascending */
+	}
+#endif	/* !WORDS_BIGENDIAN */
+
+	{	/* Native big endian (Sparc, PPC) */
+		unsigned int bits;	/* typically no more than 3-4 bits */
+		/* Clear the high unused bits */
+		for(bits = rvsize - arclen;
+			bits > CHAR_BIT;
+				rvbuf += inc, bits -= CHAR_BIT)
+				*(unsigned char *)rvbuf = 0;
+		/* Fill the body of a value */
+		for(; arcbuf < arcend; arcbuf++) {
+			cache = (cache << 7) | (*arcbuf & 0x7f);
+			bits += 7;
+			if(bits >= CHAR_BIT) {
+				bits -= CHAR_BIT;
+				*(unsigned char *)rvbuf = (cache >> bits);
+				rvbuf += inc;
+			}
+		}
+		if(bits) {
+			*(unsigned char *)rvbuf = cache;
+			rvbuf += inc;
+		}
+	}
+
+	if(add) {
+		for(rvbuf -= inc; rvbuf != rvstart; rvbuf -= inc) {
+			int v = add + *(unsigned char *)rvbuf;
+			if(v & (-1 << CHAR_BIT)) {
+				*(unsigned char *)rvbuf
+					= v + (1 << CHAR_BIT);
+				add = -1;
+			} else {
+				*(unsigned char *)rvbuf = v;
+				break;
+			}
+		}
+		if(rvbuf == rvstart) {
+			/* No space to carry over */
 			errno = ERANGE;	/* Overflow */
 			return -1;
 		}
 	}
 
-	/* Gather all bits into the accumulator */
-	for(accum = 0; arcbuf < arcend; arcbuf++)
-		accum = (accum << 7) | (*arcbuf & ~0x80);
-
-	assert(accum >= (unsigned long)-add);
-	accum += add;	/* Actually, a negative value */
-
-	*rvalue = accum;
-
 	return 0;
 }
 
+
 int
 OBJECT_IDENTIFIER_print_arc(uint8_t *arcbuf, int arclen, int add,
 		asn_app_consume_bytes_f *cb, void *app_key) {
@@ -126,7 +193,8 @@
 	unsigned long accum;	/* Bits accumulator */
 	char *p;		/* Position in the scratch buffer */
 
-	if(OBJECT_IDENTIFIER_get_arc_l(arcbuf, arclen, add, &accum))
+	if(OBJECT_IDENTIFIER_get_single_arc(arcbuf, arclen, add,
+			&accum, sizeof(accum)))
 		return -1;
 
 	/* Fill the scratch buffer in reverse. */
@@ -194,10 +262,10 @@
 }
 
 int
-OBJECT_IDENTIFIER_get_arcs_l(OBJECT_IDENTIFIER_t *oid,
-	unsigned long *arcs, int arcs_slots) {
-	unsigned long arc_value;
-	int cur_arc = 0;
+OBJECT_IDENTIFIER_get_arcs(OBJECT_IDENTIFIER_t *oid, void *arcs,
+		unsigned int arc_type_size, unsigned int arc_slots) {
+	void *arcs_end = arcs + (arc_type_size * arc_slots);
+	int num_arcs = 0;
 	int startn = 0;
 	int add = 0;
 	int i;
@@ -212,56 +280,53 @@
 		if((b & 0x80))			/* Continuation expected */
 			continue;
 
-		if(startn == 0) {
+		if(num_arcs == 0) {
 			/*
 			 * First two arcs are encoded through the backdoor.
 			 */
-			if(i) {
-				add = -80;
-				if(cur_arc < arcs_slots) arcs[cur_arc] = 2;
-				cur_arc++;
-			} else if(b <= 39) {
-				add = 0;
-				if(cur_arc < arcs_slots) arcs[cur_arc] = 0;
-				cur_arc++;
-			} else if(b < 79) {
-				add = -40;
-				if(cur_arc < arcs_slots) arcs[cur_arc] = 1;
-				cur_arc++;
-			} else {
-				add = -80;
-				if(cur_arc < arcs_slots) arcs[cur_arc] = 2;
-				cur_arc++;
-			}
+			unsigned LE = 1;	/* Little endian */
+			int first_arc;
+			num_arcs++;
+			if(!arc_slots) { num_arcs++; continue; }
+
+			if(i) first_arc = 2;
+			else if(b <= 39) first_arc = 0;
+			else if(b < 79)	first_arc = 1;
+			else first_arc = 2;
+
+			add = -40 * first_arc;
+			memset(arcs, 0, arc_type_size);
+			*(unsigned char *)(arcs
+				+ ((*(char *)&LE)?0:(arc_type_size - 1)))
+					= first_arc;
+			arcs += arc_type_size;
 		}
 
-		/* Do not fill */
-		if(cur_arc >= arcs_slots) {
+		/* Decode, if has space */
+		if(arcs < arcs_end) {
+			if(OBJECT_IDENTIFIER_get_single_arc(&oid->buf[startn],
+				i - startn + 1, add,
+					arcs, arc_type_size))
+				return -1;
 			startn = i + 1;
-			continue;
+			arcs += arc_type_size;
+			add = 0;
 		}
-
-		if(OBJECT_IDENTIFIER_get_arc_l(&oid->buf[startn],
-				i - startn + 1,
-				add, &arc_value))
-			return -1;
-		arcs[cur_arc++] = arc_value;
-		startn = i + 1;
-		add = 0;
+		num_arcs++;
 	}
 
-	return cur_arc;
+	return num_arcs;
 }
 
 int
-OBJECT_IDENTIFIER_set_arcs_l(OBJECT_IDENTIFIER_t *oid, unsigned long *arcs, int arcs_slots) {
+OBJECT_IDENTIFIER_set_arcs_l(OBJECT_IDENTIFIER_t *oid, unsigned long *arcs, unsigned int arc_slots) {
 	uint8_t *buf;
 	uint8_t *bp;
 	unsigned long long first_value;
 	int size;
 	int i;
 
-	if(oid == NULL || arcs == NULL || arcs_slots < 2) {
+	if(oid == NULL || arcs == NULL || arc_slots < 2) {
 		errno = EINVAL;
 		return -1;
 	}
@@ -283,7 +348,7 @@
 	/*
 	 * Roughly estimate the maximum size necessary to encode these arcs.
 	 */
-	size = ((sizeof(arcs[0]) + 1) * 8 / 7) * arcs_slots;
+	size = ((sizeof(arcs[0]) + 1) * 8 / 7) * arc_slots;
 	bp = buf = MALLOC(size + 1);
 	if(!buf) {
 		/* ENOMEM */
@@ -321,7 +386,7 @@
 		}
 	}
 
-	for(i = 2; i < arcs_slots; i++) {
+	for(i = 2; i < arc_slots; i++) {
 		unsigned long value = arcs[i];
 		uint8_t tbuf[sizeof(value) * 2];  /* Conservatively sized */
 		uint8_t *tp = tbuf;
diff --git a/skeletons/OBJECT_IDENTIFIER.h b/skeletons/OBJECT_IDENTIFIER.h
index 3e71f31..77b07de 100644
--- a/skeletons/OBJECT_IDENTIFIER.h
+++ b/skeletons/OBJECT_IDENTIFIER.h
@@ -29,27 +29,29 @@
 
 /*
  * This function fills an (_arcs) array with OBJECT IDENTIFIER arcs
- * up to specified (_arcs_slots) elements.
+ * up to specified (_arc_slots) elements.
  * The function always returns the real number of arcs, even if there is no
- * sufficient (_arcs_slots) provided.
+ * sufficient (_arc_slots) provided.
  * 
  * EXAMPLE:
  * 	void print_arcs(OBJECT_IDENTIFIER_t *oid) {
  * 		unsigned long fixed_arcs[10];	// Try with fixed space first
  * 		unsigned long *arcs = fixed_arcs;
- * 		int arcs_slots = sizeof(fixed_arcs)/sizeof(fixed_arcs[0]); // 10
+ * 		int arc_type_size = sizeof(fixed_arcs[0]);	// sizeof(long)
+ * 		int arc_slots = sizeof(fixed_arcs)/sizeof(fixed_arcs[0]); // 10
  * 		int count;	// Real number of arcs.
  * 		int i;
  * 
- * 		count = OBJECT_IDENTIFIER_get_arcs_l(oid, arcs, arcs_slots);
+ * 		count = OBJECT_IDENTIFIER_get_arcs(oid, arcs,
+ * 			arc_type_size, arc_slots);
  * 		// If necessary, reallocate arcs array and try again.
- * 		if(count > arcs_slots) {
- * 			arcs_slots = count;
- * 			arcs = malloc(arcs_slots * sizeof(arcs[0]));
+ * 		if(count > arc_slots) {
+ * 			arc_slots = count;
+ * 			arcs = malloc(arc_type_size * arc_slots);
  * 			if(!arcs) return;
- * 			count = OBJECT_IDENTIFIER_get_arcs_l(oid,
- * 				arcs, arcs_slots);
- * 			assert(count == arcs_slots);
+ * 			count = OBJECT_IDENTIFIER_get_arcs(oid, arcs,
+ * 				arc_type_size, arc_slots);
+ * 			assert(count == arc_slots);
  * 		}
  * 
  * 		// Print the contents of the arcs array.
@@ -65,13 +67,8 @@
  * -1/ERANGE:	One or more arcs have value out of array cell type range.
  * >=0:		Number of arcs contained in the OBJECT IDENTIFIER
  */
-int OBJECT_IDENTIFIER_get_arcs_l(OBJECT_IDENTIFIER_t *_oid,
-	unsigned long *_arcs, int _arcs_slots);
-/*
-int OBJECT_IDENTIFIER_get_arcs_im(OBJECT_IDENTIFIER_t *_oid,
-	uintmax_t *_arcs, int _arcs_slots);
- */
-
+int OBJECT_IDENTIFIER_get_arcs(OBJECT_IDENTIFIER_t *_oid,
+	void *_arcs, unsigned int _arc_type_size, unsigned int _arc_slots);
 
 /*
  * This functions initializes the OBJECT IDENTIFIER object with
@@ -84,12 +81,14 @@
  * 0:		The object was initialized with new arcs.
  */
 int OBJECT_IDENTIFIER_set_arcs_l(OBJECT_IDENTIFIER_t *_oid,
-	unsigned long *arcs, int arcs_slots);
+	unsigned long *_arcs, unsigned int _arc_slots);
 
 /*
  * Internal functions.
  */
-int OBJECT_IDENTIFIER_get_arc_l(uint8_t *arcbuf, int arclen,
-	int add, unsigned long *value);
+int OBJECT_IDENTIFIER_get_single_arc(uint8_t *arcbuf, unsigned int arclen,
+	signed int add, void *value, unsigned int value_size);
+int OBJECT_IDENTIFIER_get_single_arc_l(uint8_t *arcbuf, unsigned int arclen,
+	signed int add, unsigned long *value);
 
 #endif	/* _OBJECT_IDENTIFIER_H_ */
diff --git a/skeletons/RELATIVE-OID.c b/skeletons/RELATIVE-OID.c
index 4fdad3d..37f557d 100644
--- a/skeletons/RELATIVE-OID.c
+++ b/skeletons/RELATIVE-OID.c
@@ -62,10 +62,10 @@
 
 
 int
-RELATIVE_OID_get_arcs_l(RELATIVE_OID_t *roid,
-	unsigned long *arcs, int arcs_slots) {
-	unsigned long arc_value;
-	int cur_arc = 0;
+RELATIVE_OID_get_arcs(RELATIVE_OID_t *roid,
+	void *arcs, unsigned int arc_type_size, unsigned int arc_slots) {
+	void *arcs_end = arcs + (arc_slots * arc_type_size);
+	int num_arcs = 0;
 	int startn = 0;
 	int i;
 
@@ -79,17 +79,20 @@
 		if((b & 0x80))			/* Continuation expected */
 			continue;
 
-		if(cur_arc < arcs_slots) {
-			if(OBJECT_IDENTIFIER_get_arc_l(&roid->buf[startn],
-					i - startn + 1, 0, &arc_value))
+		if(arcs < arcs_end) {
+			if(OBJECT_IDENTIFIER_get_single_arc(
+				&roid->buf[startn],
+					i - startn + 1, 0,
+					arcs, arc_type_size))
 				return -1;
-			arcs[cur_arc++] = arc_value;
+			arcs += arc_type_size;
+			num_arcs++;
 		}
 
 		startn = i + 1;
 	}
 
-	return cur_arc;
+	return num_arcs;
 }
 
 int
diff --git a/skeletons/RELATIVE-OID.h b/skeletons/RELATIVE-OID.h
index c0bc680..926aca3 100644
--- a/skeletons/RELATIVE-OID.h
+++ b/skeletons/RELATIVE-OID.h
@@ -23,8 +23,8 @@
 int RELATIVE_OID_set_arcs_l(RELATIVE_OID_t *_roid,
 	unsigned long *arcs, int arcs_slots);
 
-/* See OBJECT_IDENTIFIER_get_arcs_l() function in OBJECT_IDENTIFIER.h */
-int RELATIVE_OID_get_arcs_l(RELATIVE_OID_t *_roid,
-	unsigned long *arcs, int arcs_slots);
+/* See OBJECT_IDENTIFIER_get_arcs() function in OBJECT_IDENTIFIER.h */
+int RELATIVE_OID_get_arcs(RELATIVE_OID_t *_roid,
+	void *arcs, unsigned int arc_type_size, unsigned int arc_slots);
 
 #endif	/* _RELATIVE_OID_H_ */
diff --git a/skeletons/tests/check-OIDs.c b/skeletons/tests/check-OIDs.c
index a97a910..e7105f7 100644
--- a/skeletons/tests/check-OIDs.c
+++ b/skeletons/tests/check-OIDs.c
@@ -6,6 +6,7 @@
 #include "../ber_tlv_tag.c"
 #include "../der_encoder.c"
 #include "../constraints.c"
+#include <sys/time.h>
 
 static int
 _print(const void *buffer, size_t size, void *app_key) {
@@ -40,8 +41,8 @@
 	OBJECT_IDENTIFIER_print(&asn1_DEF_OBJECT_IDENTIFIER, oid, 0, _print, 0);
 	printf("\n");
 
-	alen = OBJECT_IDENTIFIER_get_arcs_l(oid,
-		arcs, sizeof(arcs)/sizeof(arcs[0]));
+	alen = OBJECT_IDENTIFIER_get_arcs(oid,
+		arcs, sizeof(arcs[0]), sizeof(arcs)/sizeof(arcs[0]));
 	assert(alen > 0);
 	assert(alen == ck_len);
 
@@ -83,8 +84,8 @@
 	RELATIVE_OID_print(&asn1_DEF_RELATIVE_OID, oid, 0, _print, 0);
 	printf("\n");
 
-	alen = RELATIVE_OID_get_arcs_l(oid,
-		arcs, sizeof(arcs)/sizeof(arcs[0]));
+	alen = RELATIVE_OID_get_arcs(oid,
+		arcs, sizeof(arcs[0]), sizeof(arcs)/sizeof(arcs[0]));
 	assert(alen > 0);
 	assert(alen == ck_len);
 
@@ -120,7 +121,8 @@
 	ret = RELATIVE_OID_set_arcs_l(&oid, (unsigned long *)arcs, acount);
 	assert(ret == 0);
 
-	alen = RELATIVE_OID_get_arcs_l(&oid, tmp_arcs, tmp_alen);
+	alen = RELATIVE_OID_get_arcs(&oid, tmp_arcs,
+		sizeof(tmp_arcs[0]), tmp_alen);
 	assert(alen >= 0);
 	assert(alen < tmp_alen);
 
@@ -154,7 +156,8 @@
 	ret = OBJECT_IDENTIFIER_set_arcs_l(&oid, (unsigned long *)arcs, acount);
 	assert(ret == 0);
 
-	alen = OBJECT_IDENTIFIER_get_arcs_l(&oid, tmp_arcs, tmp_alen);
+	alen = OBJECT_IDENTIFIER_get_arcs(&oid,
+		tmp_arcs, sizeof(tmp_arcs[0]), tmp_alen);
 	assert(alen >= 0);
 	assert(alen < tmp_alen);
 
@@ -166,6 +169,48 @@
 	printf(" }\n");
 }
 
+static int
+check_speed() {
+	uint8_t buf[] = { 0x80 | 7, 0x80 | 2, 0x80 | 3, 0x80 | 4, 13 };
+	int ret = 0;
+	int cycles = 100000000;
+	double a, b, c;
+	struct timeval tv;
+	unsigned long value;
+	int i;
+
+	ret = OBJECT_IDENTIFIER_get_single_arc(buf, sizeof(buf), 0,
+		&value, sizeof(value));
+	assert(ret == 0);
+	assert(value == 0x7040c20d);
+
+	gettimeofday(&tv, 0);
+	a = tv.tv_sec + tv.tv_usec / 1000000.0;
+	for(i = 0; i < cycles; i++) {
+		ret = OBJECT_IDENTIFIER_get_single_arc(buf, sizeof(buf), 0,
+			&value, sizeof(value));
+	}
+	assert(ret == 0);
+	assert(value == 0x7040c20d);
+	gettimeofday(&tv, 0);
+	b = tv.tv_sec + tv.tv_usec / 1000000.0;
+	for(i = 0; i < cycles; i++) {
+		ret = OBJECT_IDENTIFIER_get_single_arc(buf, sizeof(buf), 0,
+			&value, sizeof(value));
+	}
+	assert(ret == 0);
+	assert(value == 0x7040c20d);
+	gettimeofday(&tv, 0);
+	c = tv.tv_sec + tv.tv_usec / 1000000.0;
+
+	a = b - a;
+	b = c - b;
+	printf("Time for single_arc(): %f\n", a);
+	printf("Time for  get_arc_l(): %f\n", b);
+
+	return 0;
+}
+
 #define	CHECK_OID(n)	check_OID(buf ## n, sizeof(buf ## n),		\
 		buf ## n ## _check,					\
 		sizeof(buf ## n ## _check)/sizeof(buf ## n ## _check[0]))
@@ -179,13 +224,13 @@
 
 int
 main(int ac, char **av) {
-	/* {joint-iso-itu-t 100 3} */
+	/* {joint-iso-itu-t 230 3} */
 	uint8_t buf1[] = {
 		0x06,	/* OBJECT IDENTIFIER */
 		0x03,	/* Length */
-		0x81, 0x34, 0x03
+		0x82, 0x36, 0x03
 	};
-	int buf1_check[] = { 2, 100, 3 };
+	int buf1_check[] = { 2, 230, 3 };
 
 	/* {8571 3 2} */
 	uint8_t buf2[] = {
@@ -224,5 +269,7 @@
 	CHECK_REGEN_OID(12);
 	CHECK_REGEN_OID(13);
 
+	check_speed();
+
 	return 0;
 }