Initial revision


git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@2 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/skeletons/OBJECT_IDENTIFIER.c b/skeletons/OBJECT_IDENTIFIER.c
new file mode 100644
index 0000000..bcbcdca
--- /dev/null
+++ b/skeletons/OBJECT_IDENTIFIER.c
@@ -0,0 +1,356 @@
+/*-
+ * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <OBJECT_IDENTIFIER.h>
+#include <assert.h>
+#include <errno.h>
+
+/*
+ * OBJECT IDENTIFIER basic type description.
+ */
+static ber_tlv_tag_t asn1_DEF_OBJECT_IDENTIFIER_tags[] = {
+	(ASN_TAG_CLASS_UNIVERSAL | (6 << 2))
+};
+asn1_TYPE_descriptor_t asn1_DEF_OBJECT_IDENTIFIER = {
+	"OBJECT IDENTIFIER",
+	OBJECT_IDENTIFIER_constraint,
+	INTEGER_decode_ber,	/* Implemented in terms of INTEGER type */
+	OBJECT_IDENTIFIER_encode_der,
+	OBJECT_IDENTIFIER_print,
+	INTEGER_free,
+	0, /* Use generic outmost tag fetcher */
+	asn1_DEF_OBJECT_IDENTIFIER_tags,
+	sizeof(asn1_DEF_OBJECT_IDENTIFIER_tags)
+	    / sizeof(asn1_DEF_OBJECT_IDENTIFIER_tags[0]),
+	1,	/* Single UNIVERSAL tag may be implicitly overriden */
+	0	/* Always in primitive form */
+};
+
+
+/*
+ * Encode OBJECT IDENTIFIER type using DER.
+ */
+der_enc_rval_t
+OBJECT_IDENTIFIER_encode_der(asn1_TYPE_descriptor_t *sd, void *ptr,
+	int tag_mode, ber_tlv_tag_t tag,
+	asn_app_consume_bytes_f *cb, void *app_key) {
+	der_enc_rval_t erval;
+	OBJECT_IDENTIFIER_t *st = ptr;
+
+	ASN_DEBUG("%s %s as OBJECT IDENTIFIER (tm=%d)",
+		cb?"Encoding":"Estimating", sd->name, tag_mode);
+
+	erval.encoded = der_write_tags(sd, st->size, tag_mode, tag,
+		cb, app_key);
+	ASN_DEBUG("OBJECT IDENTIFIER %s wrote tags %d",
+		sd->name, (int)erval.encoded);
+	if(erval.encoded == -1) {
+		erval.failed_type = sd;
+		erval.structure_ptr = ptr;
+		return erval;
+	}
+
+	if(cb && st->buf) {
+		ssize_t ret;
+
+		ret = cb(st->buf, st->size, app_key);
+		if(ret == -1) {
+			erval.encoded = -1;
+			erval.failed_type = sd;
+			erval.structure_ptr = ptr;
+			return erval;
+		}
+	} else {
+		assert(st->buf || st->size == 0);
+	}
+
+	erval.encoded += st->size;
+
+	return erval;
+}
+
+int
+OBJECT_IDENTIFIER_constraint(asn1_TYPE_descriptor_t *td, const void *sptr,
+		asn_app_consume_bytes_f *app_errlog, void *app_key) {
+	const OBJECT_IDENTIFIER_t *st = sptr;
+
+	if(st && st->buf) {
+		if(st->size < 1) {
+			_ASN_ERRLOG("%s: at least one numerical value expected",
+				td->name);
+			return -1;
+		}
+	} else {
+		_ASN_ERRLOG("%s: value not given", td->name);
+		return -1;
+	}
+
+	return 0;
+}
+
+int
+OBJECT_IDENTIFIER_get_arc_l(uint8_t *arcbuf, int arclen, int add, unsigned long *rvalue) {
+	unsigned long accum;
+	uint8_t *arcend = arcbuf + arclen;
+
+	if(arclen * 7 > 8 * sizeof(accum)) {
+		if(arclen * 7 <= 8 * (sizeof(accum) + 1)) {
+			if((*arcbuf & ~0x8f)) {
+				errno = ERANGE;	/* Overflow */
+				return -1;
+			}
+		} else {
+			errno = ERANGE;	/* Overflow */
+			return -1;
+		}
+	}
+
+	/* Gather all bits into the accumulator */
+	for(accum = 0; arcbuf < arcend; arcbuf++)
+		accum = (accum << 7) | (*arcbuf & ~0x80);
+
+	accum += add;	/* Actually, a negative value */
+	assert(accum >= 0);
+
+	*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) {
+	char scratch[64];	/* Conservative estimate */
+	unsigned long accum;	/* Bits accumulator */
+	char *p;		/* Position in the scratch buffer */
+
+	if(OBJECT_IDENTIFIER_get_arc_l(arcbuf, arclen, add, &accum))
+		return -1;
+
+	/* Fill the scratch buffer in reverse. */
+	p = scratch + sizeof(scratch);
+	for(; accum; accum /= 10)
+		*(--p) = (accum % 10) + 0x30;
+
+	return cb(p, sizeof(scratch) - (p - scratch), app_key);
+}
+
+int
+OBJECT_IDENTIFIER_print(asn1_TYPE_descriptor_t *td, const void *sptr,
+	int ilevel, asn_app_consume_bytes_f *cb, void *app_key) {
+	const OBJECT_IDENTIFIER_t *st = sptr;
+	int startn;
+	int add = 0;
+	int i;
+
+	if(!st || !st->buf)
+		return cb("<absent>", 8, app_key);
+
+	/* Dump preamble */
+	if(cb("{ ", 2, app_key))
+		return -1;
+
+	for(i = 0, startn = 0; i < st->size; i++) {
+		uint8_t b = st->buf[i];
+		if((b & 0x80))			/* Continuation expected */
+			continue;
+
+		if(startn == 0) {
+			/*
+			 * First two arcs are encoded through the backdoor.
+			 */
+			if(i) {
+				add = -80;
+				if(cb("2", 1, app_key)) return -1;
+			} else if(b <= 39) {
+				add = 0;
+				if(cb("0", 1, app_key)) return -1;
+			} else if(b < 79) {
+				add = -40;
+				if(cb("1", 1, app_key)) return -1;
+			} else {
+				add = -80;
+				if(cb("2", 1, app_key)) return -1;
+			}
+		}
+
+		if(cb(" ", 1, app_key))	/* Separate arcs */
+			return -1;
+
+		if(OBJECT_IDENTIFIER_print_arc(&st->buf[startn],
+				i - startn + 1, add,
+				cb, app_key))
+			return -1;
+		startn = i + 1;
+		add = 0;
+	}
+
+	return cb(" }", 2, app_key);
+}
+
+int
+OBJECT_IDENTIFIER_get_arcs_l(OBJECT_IDENTIFIER_t *oid,
+	unsigned long *arcs, int arcs_slots) {
+	unsigned long arc_value;
+	int cur_arc = 0;
+	int startn = 0;
+	int add = 0;
+	int i;
+
+	if(!oid || !oid->buf) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	for(i = 0; i < oid->size; i++) {
+		uint8_t b = oid->buf[i];
+		if((b & 0x80))			/* Continuation expected */
+			continue;
+
+		if(startn == 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++;
+			}
+		}
+
+		/* Do not fill */
+		if(cur_arc >= arcs_slots) {
+			startn = i + 1;
+			continue;
+		}
+
+		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;
+	}
+
+	return cur_arc;
+}
+
+int
+OBJECT_IDENTIFIER_set_arcs_l(OBJECT_IDENTIFIER_t *oid, unsigned long *arcs, int arcs_slots) {
+	uint8_t *buf;
+	uint8_t *bp;
+	unsigned long long first_value;
+	int size;
+	int i;
+
+	if(oid == NULL || arcs == NULL || arcs_slots < 2) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if(arcs[0] <= 1) {
+		if(arcs[1] >= 39) {
+			/* 8.19.4: At most 39 subsequent values (including 0) */
+			errno = ERANGE;
+			return -1;
+		}
+	} else if(arcs[0] > 2) {
+		/* 8.19.4: Only three values are allocated from the root node */
+		errno = ERANGE;
+		return -1;
+	}
+
+	first_value = arcs[0] * 40 + arcs[1];
+
+	/*
+	 * Roughly estimate the maximum size necessary to encode these arcs.
+	 */
+	size = ((sizeof(arcs[0]) + 1) * 8 / 7) * arcs_slots;
+	bp = buf = MALLOC(size + 1);
+	if(!buf) {
+		/* ENOMEM */
+		return -1;
+	}
+
+	/*
+	 * Encode the arcs and refine the encoding size.
+	 */
+	size = 0;
+
+	{
+		uint8_t tbuf[sizeof(first_value) * 2];
+		uint8_t *tp = tbuf;
+		int arc_len = 0;
+		int add = 0;
+
+		for(; first_value; first_value >>= 7) {
+			unsigned int b7 = first_value & 0x7f;
+			*tp++ = 0x80 | b7;
+			add++;
+			if(b7) { arc_len += add; add = 0; }
+		}
+
+		if(arc_len) {
+			tp = &tbuf[arc_len - 1];
+			/* The last octet does not have bit 8 set. */
+			*tbuf &= 0x7f;
+			for(; tp >= tbuf; tp--)
+				*bp++ = *tp;
+			size += arc_len;
+		} else {
+			*bp++ = 0;
+			size++;
+		}
+	}
+
+	for(i = 2; i < arcs_slots; i++) {
+		unsigned long value = arcs[i];
+		uint8_t tbuf[sizeof(value) * 2];  /* Conservatively sized */
+		uint8_t *tp = tbuf;
+		int arc_len = 0;
+		int add = 0;
+
+		for(; value; value >>= 7) {
+			unsigned int b7 = value & 0x7F;
+			*tp++ = 0x80 | b7;
+			add++;
+			if(b7) { arc_len += add; add = 0; }
+		}
+
+		if(arc_len) {
+			tp = &tbuf[arc_len - 1];
+			/* The last octet does not have bit 8 set. */
+			*tbuf &= 0x7f;
+			for(; tp >= tbuf; tp--)
+				*bp++ = *tp;
+			size += arc_len;
+		} else {
+			*bp++ = 0;
+			size++;
+		}
+	}
+
+	/*
+	 * Replace buffer.
+	 */
+	oid->size = size;
+	bp = oid->buf;
+	oid->buf = buf;
+	if(bp) FREEMEM(bp);
+
+	return 0;
+}