XER support
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);
+}
+