XER decoding of a REAL


git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@530 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/skeletons/REAL.c b/skeletons/REAL.c
index c61f723..ace2f14 100644
--- a/skeletons/REAL.c
+++ b/skeletons/REAL.c
@@ -31,7 +31,7 @@
 	asn_generic_no_constraint,
 	ber_decode_primitive,
 	der_encode_primitive,
-	0,				/* Not implemented yet */
+	REAL_decode_xer,
 	REAL_encode_xer,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_REAL_tags,
@@ -42,6 +42,23 @@
 	0	/* No specifics */
 };
 
+typedef enum specialRealValue {
+	SRV__NOT_A_NUMBER,
+	SRV__MINUS_INFINITY,
+	SRV__PLUS_INFINITY
+} specialRealValue_e;
+static struct specialRealValue_s {
+	char *string;
+	int length;
+	double dv;
+} specialRealValue[] = {
+#define	SRV_SET(foo, val)	{ foo, sizeof(foo) - 1, val }
+	SRV_SET("<NOT-A-NUMBER/>", 0.0),
+	SRV_SET("<MINUS-INFINITY/>", -1.0),
+	SRV_SET("<PLUS-INFINITY/>", 1.0),
+#undef	SRV_SET
+};
+
 ssize_t
 REAL__dump(double d, int canonical, asn_app_consume_bytes_f *cb, void *app_key) {
 	char local_buf[64];
@@ -55,16 +72,16 @@
 	 */
 	/* fpclassify(3) is not portable yet */
 	if(isnan(d)) {
-		buf = "<NOT-A-NUMBER/>";
-		buflen = 15;
+		buf = specialRealValue[SRV__NOT_A_NUMBER].string;
+		buflen = specialRealValue[SRV__NOT_A_NUMBER].length;
 		return (cb(buf, buflen, app_key) < 0) ? -1 : buflen;
 	} else if(!finite(d)) {
 		if(copysign(1.0, d) < 0.0) {
-			buf = "<MINUS-INFINITY/>";
-			buflen = 17;
+			buf = specialRealValue[SRV__MINUS_INFINITY].string;
+			buflen = specialRealValue[SRV__MINUS_INFINITY].length;
 		} else {
-			buf = "<PLUS-INFINITY/>";
-			buflen = 16;
+			buf = specialRealValue[SRV__PLUS_INFINITY].string;
+			buflen = specialRealValue[SRV__PLUS_INFINITY].length;
 		}
 		return (cb(buf, buflen, app_key) < 0) ? -1 : buflen;
 	} else if(ilogb(d) <= -INT_MAX) {
@@ -99,12 +116,13 @@
 	if(canonical) {
 		/*
 		 * Transform the "[-]d.dddE+-dd" output into "[-]d.dddE[-]d"
+		 * Check that snprintf() constructed the output correctly.
 		 */
 		char *dot, *E;
 		char *end = buf + buflen;
 		char *last_zero;
 
-		dot = (buf[0] == '-') ? (buf + 2) : (buf + 1);
+		dot = (buf[0] == 0x2d /* '-' */) ? (buf + 2) : (buf + 1);
 		if(*dot >= 0x30) {
 			errno = EINVAL;
 			return -1;	/* Not a dot, really */
@@ -116,7 +134,7 @@
 				char *expptr = ++E;
 				char *s = expptr;
 				int sign;
-				if(*expptr == '+') {
+				if(*expptr == 0x2b /* '+' */) {
 					/* Skip the "+" */
 					buflen -= 1;
 					sign = 0;
@@ -135,8 +153,12 @@
 				}
 				if(*last_zero == 0x30) {
 					*last_zero = 0x45;	/* E */
+					buflen -= s - (last_zero + 1);
 					s = last_zero + 1;
-					if(sign) *s++ = '-';
+					if(sign) {
+						*s++ = 0x2d /* '-' */;
+						buflen++;
+					}
 				}
 				for(; expptr <= end; s++, expptr++)
 					*s = *expptr;
@@ -221,6 +243,69 @@
 	return er;
 }
 
+
+/*
+ * Decode the chunk of XML text encoding REAL.
+ */
+static ssize_t
+REAL__xer_body_decode(REAL_t *st, void *chunk_buf, size_t chunk_size) {
+	double value;
+	char *xerdata = (char *)chunk_buf;
+	char *endptr = 0;
+	char *b;
+
+	if(!chunk_size) return -1;
+
+	/*
+	 * Decode an XMLSpecialRealValue: <MINUS-INFINITY>, etc.
+	 */
+	if(xerdata[0] == 0x3c /* '<' */) {
+		size_t i;
+		for(i = 0; i < sizeof(specialRealValue)
+				/ sizeof(specialRealValue[0]); i++) {
+			struct specialRealValue_s *srv = &specialRealValue[i];
+			if(srv->length != chunk_size
+			|| memcmp(srv->string, chunk_buf, chunk_size))
+				continue;
+
+			if(asn_double2REAL(st, srv->dv / real_zero))
+				return -1;
+
+			return chunk_size;
+		}
+		ASN_DEBUG("Unknown XMLSpecialRealValue");
+		return -1;
+	}
+
+	/*
+	 * Copy chunk into the nul-terminated string, and run strtod.
+	 */
+	b = MALLOC(chunk_size + 1);
+	if(!b) return -1;
+	memcpy(b, chunk_buf, chunk_size);
+	b[chunk_size] = 0;
+
+	value = strtod(b, &endptr);
+	free(b);
+	if(endptr == b) return -1;
+
+	if(asn_double2REAL(st, value))
+		return -1;
+
+	return endptr - b;
+}
+
+asn_dec_rval_t
+REAL_decode_xer(asn_codec_ctx_t *opt_codec_ctx,
+	asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname,
+		void *buf_ptr, size_t size) {
+
+	return xer_decode_primitive(opt_codec_ctx, td,
+		(ASN__PRIMITIVE_TYPE_t **)sptr, opt_mname,
+		buf_ptr, size, REAL__xer_body_decode);
+}
+
+
 int
 asn_REAL2double(const REAL_t *st, double *dbl_value) {
 	unsigned int octv;
@@ -344,6 +429,7 @@
 			m = scalbn(m, 8) + *ptr;
 	}
 
+	if(0)
 	ASN_DEBUG("m=%.10f, scF=%d, bF=%d, expval=%d, ldexp()=%f, scalbn()=%f",
 		m, scaleF, baseF, expval,
 		ldexp(m, expval * baseF + scaleF),
diff --git a/skeletons/REAL.h b/skeletons/REAL.h
index 86ae40f..114b7e5 100644
--- a/skeletons/REAL.h
+++ b/skeletons/REAL.h
@@ -13,6 +13,7 @@
 extern asn_TYPE_descriptor_t asn_DEF_REAL;
 
 asn_struct_print_f REAL_print;
+xer_type_decoder_f REAL_decode_xer;
 xer_type_encoder_f REAL_encode_xer;
 
 /***********************************
diff --git a/skeletons/tests/check-REAL.c b/skeletons/tests/check-REAL.c
index d0bf3fc..094017d 100644
--- a/skeletons/tests/check-REAL.c
+++ b/skeletons/tests/check-REAL.c
@@ -7,6 +7,7 @@
 #include <der_encoder.c>
 #include <xer_decoder.c>
 #include <xer_support.c>
+#include <xer_encoder.c>
 #include <constraints.c>
 
 static char reconstructed[2][512];
@@ -142,6 +143,64 @@
 	check_str_repr(val, sample, canonical_sample);
 }
 
+static void
+check_xer(int fuzzy, double orig_value) {
+	asn_enc_rval_t er;
+	asn_dec_rval_t rc;
+	REAL_t st;
+	REAL_t *newst0 = 0;
+	REAL_t *newst1 = 0;
+	double value0, value1;
+	int ret;
+
+	memset(&st, 0, sizeof(st));
+	ret = asn_double2REAL(&st, orig_value);
+	assert(ret == 0);
+
+	reconstr_lens[0] = 0;
+	reconstr_lens[1] = 0;
+	er = xer_encode(&asn_DEF_REAL, &st,
+		XER_F_BASIC, callback, 0);
+	assert(er.encoded == reconstr_lens[0]);
+	er = xer_encode(&asn_DEF_REAL, &st,
+		XER_F_CANONICAL, callback, (void *)1);
+	assert(er.encoded == reconstr_lens[1]);
+	reconstructed[0][reconstr_lens[0]] = 0;
+	reconstructed[1][reconstr_lens[1]] = 0;
+
+	printf("%f vs (%d)[%s] & (%d)%s",
+		orig_value,
+		reconstr_lens[1], reconstructed[1],
+		reconstr_lens[0], reconstructed[0]
+	);
+
+	rc = xer_decode(0, &asn_DEF_REAL, (void **)&newst0,
+		reconstructed[0], reconstr_lens[0]);
+	assert(rc.code == RC_OK);
+	assert(rc.consumed < reconstr_lens[0]);
+
+	rc = xer_decode(0, &asn_DEF_REAL, (void **)&newst1,
+		reconstructed[1], reconstr_lens[1]);
+	assert(rc.code == RC_OK);
+	assert(rc.consumed == reconstr_lens[1]);
+
+	ret = asn_REAL2double(newst0, &value0);
+	assert(ret == 0);
+	ret = asn_REAL2double(newst1, &value1);
+	assert(ret == 0);
+
+	assert(value0 == orig_value
+		|| (isnan(value0) && isnan(orig_value))
+		|| fuzzy);
+	assert(value1 == orig_value
+		|| (isnan(value1) && isnan(orig_value)));
+
+	assert(newst0->size == st.size || fuzzy);
+	assert(newst1->size == st.size);
+	assert(fuzzy || memcmp(newst0->buf, st.buf, st.size) == 0);
+	assert(memcmp(newst1->buf, st.buf, st.size) == 0);
+}
+
 int
 main() {
 	REAL_t rn;
@@ -190,5 +249,16 @@
 	check_buf(buf_mo1, sizeof(buf_mo1),	-3.14, "-3.14", "-3.14E0");
 	check_buf(buf_mo2, sizeof(buf_mo2),	3.14, "3.14", "3.14E0");
 
+
+	check_xer(0, zero/zero);	/* "<NOT-A-NUMBER/>" */
+	check_xer(0, 1.0/zero);		/* "<PLUS-INFINITY/>" */
+	check_xer(0, -1.0/zero);	/* "<MINUS-INFINITY/>" */
+	check_xer(0, 1.0);
+	check_xer(0, -1.0);
+	check_xer(0, 1.5);
+	check_xer(0, 123);
+	check_xer(1, 0.0000000000000000000001);
+	check_xer(1, -0.0000000000000000000001);
+
 	return 0;
 }