XER support


git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@513 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/skeletons/xer_decoder.c b/skeletons/xer_decoder.c
new file mode 100644
index 0000000..39e830d
--- /dev/null
+++ b/skeletons/xer_decoder.c
@@ -0,0 +1,288 @@
+
+#include <asn_application.h>
+#include <asn_internal.h>
+#include <xer_support.h>		/* XER/XML parsing support */
+#include <assert.h>
+
+
+/*
+ * Decode the XER encoding of a given type.
+ */
+asn_dec_rval_t
+xer_decode(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
+		void **struct_ptr, void *buffer, size_t size) {
+	asn_codec_ctx_t s_codec_ctx;
+
+	/*
+	 * Satisfy the requirement that the codec context
+	 * must be allocated on the stack.
+	 */
+	if(opt_codec_ctx && opt_codec_ctx->max_stack_size) {
+		s_codec_ctx = *opt_codec_ctx;
+		opt_codec_ctx = &s_codec_ctx;
+	}
+
+	/*
+	 * Invoke type-specific decoder.
+	 */
+	return td->xer_decoder(opt_codec_ctx, td, struct_ptr, 0, buffer, size);
+}
+
+
+
+struct xer__cb_arg {
+	pxml_chunk_type_e	chunk_type;
+	size_t			chunk_size;
+	void			*chunk_buf;
+	int callback_not_invoked;
+};
+
+static int
+xer__token_cb(pxml_chunk_type_e type, void *_chunk_data, size_t _chunk_size, void *key) {
+	struct xer__cb_arg *arg = (struct xer__cb_arg *)key;
+	arg->chunk_type = type;
+	arg->chunk_size = _chunk_size;
+	arg->chunk_buf = _chunk_data;
+	arg->callback_not_invoked = 0;
+	return -1;	/* Terminate the XML parsing */
+}
+
+/*
+ * Fetch the next token from the XER/XML stream.
+ */
+ssize_t
+xer_next_token(int *stateContext, void *buffer, size_t size,
+		pxer_chunk_type_e *ch_type) {
+	struct xer__cb_arg arg;
+	ssize_t ret;
+
+	arg.callback_not_invoked = 1;
+	ret = pxml_parse(stateContext, buffer, size, xer__token_cb, &arg);
+	if(ret < 0) return -1;
+	if(arg.callback_not_invoked) {
+		assert(ret == 0);	/* No data was consumed */
+		return 0;		/* Try again with more data */
+	} else {
+		assert(arg.chunk_size);
+		assert(arg.chunk_buf == buffer);
+	}
+
+	/*
+	 * Translate the XML chunk types into more convenient ones.
+	 */
+	switch(arg.chunk_type) {
+	case PXML_TEXT:
+		*ch_type = PXER_TEXT;
+		break;
+	case PXML_TAG: return 0;	/* Want more */
+	case PXML_TAG_END:
+		*ch_type = PXER_TAG;
+		break;
+	case PXML_COMMENT:
+	case PXML_COMMENT_END:
+		*ch_type = PXER_COMMENT;
+		break;
+	}
+
+	return arg.chunk_size;
+}
+
+#define	CSLASH	0x2f	/* '/' */
+#define	LANGLE	0x3c	/* '<' */
+#define	RANGLE	0x3e	/* '>' */
+
+xer_check_tag_e
+xer_check_tag(const void *buf_ptr, int size, const char *need_tag) {
+	const char *buf = (const char *)buf_ptr;
+	const char *end;
+	xer_check_tag_e ct = XCT_OPENING;
+
+	if(size < 2 || buf[0] != LANGLE || buf[size-1] != RANGLE) {
+		return XCT_BROKEN;
+	}
+
+	/*
+	 * Determine the tag class.
+	 */
+	if(buf[1] == CSLASH) {
+		buf += 2;	/* advance past "</" */
+		size -= 3;	/* strip "</" and ">" */
+		ct = XCT_CLOSING;
+		if(size > 0 && buf[size-1] == CSLASH)
+			return XCT_BROKEN;	/* </abc/> */
+	} else {
+		buf++;		/* advance past "<" */
+		size -= 2;	/* strip "<" and ">" */
+		if(size > 0 && buf[size-1] == CSLASH) {
+			ct = XCT_BOTH;
+			size--;	/* One more, for "/" */
+		}
+	}
+
+	/*
+	 * Determine the tag name.
+	 */
+	for(end = buf + size; buf < end; buf++, need_tag++) {
+		int b = *buf, n = *need_tag;
+		if(b != n) {
+			if(n == 0) {
+				switch(b) {
+				case 0x09: case 0x0a: case 0x0c: case 0x0d:
+				case 0x20:
+					/* "<abc def/>": whitespace is normal */
+					return ct;
+				}
+			}
+			return XCT_UNEXPECTED;
+		}
+		if(b == 0)
+			return XCT_BROKEN;	/* Embedded 0 in buf?! */
+	}
+	if(*need_tag) return XCT_UNEXPECTED;
+
+	return ct;
+}
+
+
+#undef	ADVANCE
+#define	ADVANCE(num_bytes)	do {				\
+		size_t num = (num_bytes);			\
+		buf_ptr = ((char *)buf_ptr) + num;		\
+		size -= num;					\
+		consumed_myself += num;				\
+	} while(0)
+
+#undef	RETURN
+#define	RETURN(_code)	do {					\
+		rval.code = _code;				\
+		rval.consumed = consumed_myself;		\
+		return rval;					\
+	} while(0)
+
+#define	XER_GOT_BODY(chunk_buf, chunk_size)	do {		\
+		ssize_t converted_size = body_receiver		\
+			(struct_ptr, chunk_buf, chunk_size,	\
+				(size_t)chunk_size < size);	\
+		if(converted_size == -1) RETURN(RC_FAIL);	\
+		chunk_size = converted_size;			\
+	} while(0)
+#define	XER_GOT_EMPTY()	do {					\
+		ssize_t chunk_size = 0;				\
+		XER_GOT_BODY(0, chunk_size);			\
+	} while(0)
+
+/*
+ * Generalized function for decoding the primitive values.
+ */
+asn_dec_rval_t
+xer_decode_general(asn_codec_ctx_t *opt_codec_ctx,
+	asn_struct_ctx_t *ctx,	/* Type decoder context */
+	void *struct_ptr,	/* The structure must be already allocated */
+	const char *xml_tag,	/* Expected XML tag */
+	void *buf_ptr, size_t size,
+	int (*opt_unexpected_tag_decoder)
+		(void *struct_ptr, void *chunk_buf, size_t chunk_size),
+	ssize_t (*body_receiver)
+		(void *struct_ptr, void *chunk_buf, size_t chunk_size,
+			int have_more)
+	) {
+
+	asn_dec_rval_t rval;
+	ssize_t consumed_myself = 0;
+	pxer_chunk_type_e ch_type;	/* XER chunk type */
+	int xer_state;			/* XER low level parsing context */
+
+	(void)opt_codec_ctx;
+
+	/*
+	 * Phases of XER/XML processing:
+	 * Phase 0: Check that the opening tag matches our expectations.
+	 * Phase 1: Processing body and reacting on closing tag.
+	 */
+	if(ctx->phase > 1) RETURN(RC_FAIL);
+	for(xer_state = ctx->step;;) {
+		ssize_t ch_size;	/* Chunk size */
+		xer_check_tag_e tcv;	/* Tag check value */
+
+		/*
+		 * Get the next part of the XML stream.
+		 */
+		ch_size = xer_next_token(&xer_state, buf_ptr, size, &ch_type);
+		switch(ch_size) {
+		case -1: RETURN(RC_FAIL);
+		case 0:
+			ctx->step = xer_state;
+			RETURN(RC_WMORE);
+		default:
+			switch(ch_type) {
+			case PXER_COMMENT:		/* Got XML comment */
+				ADVANCE(ch_size);	/* Skip silently */
+				continue;
+			case PXER_TEXT:
+				if(ctx->phase == 0) {
+					/* Unexpected data */
+					/* TODO: ignore whitespace? */
+					RETURN(RC_FAIL);
+				}
+				XER_GOT_BODY(buf_ptr, ch_size);
+				ADVANCE(ch_size);
+				continue;
+			case PXER_TAG:
+				break;	/* Check the rest down there */
+			}
+		}
+
+		assert(ch_type == PXER_TAG && size);
+
+		tcv = xer_check_tag(buf_ptr, ch_size, xml_tag);
+		if(ctx->phase == 0) {
+			/*
+			 * Expecting the opening tag
+			 * for the type being processed.
+			 */
+			switch(tcv) {
+			case XCT_BOTH:
+				/* Finished decoding of an empty element */
+				XER_GOT_EMPTY();
+				ADVANCE(ch_size);
+				ctx->phase = 2;	/* Phase out */
+				RETURN(RC_OK);
+			case XCT_OPENING:
+				ADVANCE(ch_size);
+				ctx->phase = 1;	/* Processing body phase */
+				continue;
+			default:
+				break;		/* Unexpected tag */
+			}
+		} else {
+			/*
+			 * Waiting for the closing XML tag.
+			 */
+			switch(tcv) {
+			case XCT_CLOSING:
+				ADVANCE(ch_size);
+				ctx->phase = 2;	/* Phase out */
+				RETURN(RC_OK);
+			case XCT_UNEXPECTED:
+				/*
+				 * Certain tags in the body may be expected.
+				 */
+				if(opt_unexpected_tag_decoder
+				&& opt_unexpected_tag_decoder(struct_ptr,
+						buf_ptr, ch_size) == 0) {
+					/* Tag's processed fine */
+					ADVANCE(ch_size);
+					continue;
+				}
+				/* Fall through */
+			default:
+				break;
+			}
+			ASN_DEBUG("Unexpected XML tag");
+		}
+		break;	/* Dark and mysterious things have just happened */
+	}
+
+	RETURN(RC_FAIL);
+}
+