Logo Search packages:      
Sourcecode: biosig4c++ version File versions  Download package

xer_decoder.c

/*
 * Copyright (c) 2004, 2005 Lev Walkin <vlm@lionet.info>. All rights reserved.
 * Redistribution and modifications are permitted subject to BSD license.
 */
#include <asn_application.h>
#include <asn_internal.h>
#include <xer_support.h>            /* XER/XML parsing support */


/*
 * 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, const void *buffer, size_t size) {
      asn_codec_ctx_t s_codec_ctx;

      /*
       * Stack checker requires that the codec context
       * must be allocated on the stack.
       */
      if(opt_codec_ctx) {
            if(opt_codec_ctx->max_stack_size) {
                  s_codec_ctx = *opt_codec_ctx;
                  opt_codec_ctx = &s_codec_ctx;
            }
      } else {
            /* If context is not given, be security-conscious anyway */
            memset(&s_codec_ctx, 0, sizeof(s_codec_ctx));
            s_codec_ctx.max_stack_size = _ASN_DEFAULT_STACK_MAX;
            opt_codec_ctx = &s_codec_ctx;
      }

      /*
       * Invoke type-specific decoder.
       */
      return td->xer_decoder(opt_codec_ctx, td, struct_ptr, 0, buffer, size);
}



00042 struct xer__cb_arg {
      pxml_chunk_type_e chunk_type;
      size_t                  chunk_size;
      const void        *chunk_buf;
      int callback_not_invoked;
};

static int
xer__token_cb(pxml_chunk_type_e type, const 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, const void *buffer, size_t size, pxer_chunk_type_e *ch_type) {
      struct xer__cb_arg arg;
      int new_stateContext = *stateContext;
      ssize_t ret;

      arg.callback_not_invoked = 1;
      ret = pxml_parse(&new_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;
      }

      *stateContext = new_stateContext;
      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) {
            if(size >= 2)
            ASN_DEBUG("Broken XML tag: \"%c...%c\"", buf[0], buf[size - 1]);
            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 "/" */
            }
      }

      /* Sometimes we don't care about the tag */
      if(!need_tag || !*need_tag)
            return (xer_check_tag_e)(XCT__UNK__MASK | ct);

      /*
       * 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 (xer_check_tag_e)(XCT__UNK__MASK | ct);
            }
            if(b == 0)
                  return XCT_BROKEN;      /* Embedded 0 in buf?! */
      }
      if(*need_tag)
            return (xer_check_tag_e)(XCT__UNK__MASK | ct);

      return ct;
}


#undef      ADVANCE
#define     ADVANCE(num_bytes)      do {                    \
            size_t num = (num_bytes);                 \
            buf_ptr = ((const char *)buf_ptr) + num;  \
            size -= num;                              \
            consumed_myself += num;                   \
      } while(0)

#undef      RETURN
#define     RETURN(_code)     do {                          \
            rval.code = _code;                        \
            rval.consumed = consumed_myself;          \
            if(rval.code != RC_OK)                    \
                  ASN_DEBUG("Failed with %d", rval.code);   \
            return rval;                              \
      } while(0)

#define     XER_GOT_BODY(chunk_buf, chunk_size, size) do {  \
            ssize_t converted_size = body_receiver          \
                  (struct_key, chunk_buf, chunk_size, \
                        (size_t)chunk_size < size);   \
            if(converted_size == -1) RETURN(RC_FAIL); \
            if(converted_size == 0                    \
                  && size == (size_t)chunk_size)            \
                  RETURN(RC_WMORE);             \
            chunk_size = converted_size;              \
      } while(0)
#define     XER_GOT_EMPTY()   do {                          \
      if(body_receiver(struct_key, 0, 0, size > 0) == -1)   \
                  RETURN(RC_FAIL);              \
      } 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_key,
      const char *xml_tag,    /* Expected XML tag */
      const void *buf_ptr, size_t size,
      int (*opt_unexpected_tag_decoder)
            (void *struct_key, const void *chunk_buf, size_t chunk_size),
      ssize_t (*body_receiver)
            (void *struct_key, const void *chunk_buf, size_t chunk_size,
                  int have_more)
      ) {

      asn_dec_rval_t rval;
      ssize_t consumed_myself = 0;

      (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(;;) {
            pxer_chunk_type_e ch_type;    /* XER chunk type */
            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(&ctx->context, buf_ptr, size,
                  &ch_type);
            switch(ch_size) {
            case -1: RETURN(RC_FAIL);
            case 0:
                  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) {
                              /*
                               * We have to ignore whitespace here,
                               * but in order to be forward compatible
                               * with EXTENDED-XER (EMBED-VALUES, #25)
                               * any text is just ignored here.
                               */
                        } else {
                              XER_GOT_BODY(buf_ptr, ch_size, 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);
            /*
             * Phase 0:
             *    Expecting the opening tag
             *    for the type being processed.
             * Phase 1:
             *    Waiting for the closing XML tag.
             */
            switch(tcv) {
            case XCT_BOTH:
                  if(ctx->phase) break;
                  /* Finished decoding of an empty element */
                  XER_GOT_EMPTY();
                  ADVANCE(ch_size);
                  ctx->phase = 2;   /* Phase out */
                  RETURN(RC_OK);
            case XCT_OPENING:
                  if(ctx->phase) break;
                  ADVANCE(ch_size);
                  ctx->phase = 1;   /* Processing body phase */
                  continue;
            case XCT_CLOSING:
                  if(!ctx->phase) break;
                  ADVANCE(ch_size);
                  ctx->phase = 2;   /* Phase out */
                  RETURN(RC_OK);
            case XCT_UNKNOWN_BO:
                  /*
                   * Certain tags in the body may be expected.
                   */
                  if(opt_unexpected_tag_decoder
                  && opt_unexpected_tag_decoder(struct_key,
                              buf_ptr, ch_size) >= 0) {
                        /* Tag's processed fine */
                        ADVANCE(ch_size);
                        if(!ctx->phase) {
                              /* We are not expecting
                               * the closing tag anymore. */
                              ctx->phase = 2;   /* Phase out */
                              RETURN(RC_OK);
                        }
                        continue;
                  }
                  /* Fall through */
            default:
                  break;            /* Unexpected tag */
            }

            ASN_DEBUG("Unexpected XML tag (expected \"%s\")", xml_tag);
            break;      /* Dark and mysterious things have just happened */
      }

      RETURN(RC_FAIL);
}


int
xer_is_whitespace(const void *chunk_buf, size_t chunk_size) {
      const char *p = (const char *)chunk_buf;
      const char *pend = p + chunk_size;

      for(; p < pend; p++) {
            switch(*p) {
            /* X.693, #8.1.4
             * HORISONTAL TAB (9)
             * LINE FEED (10) 
             * CARRIAGE RETURN (13) 
             * SPACE (32)
             */
            case 0x09: case 0x0a: case 0x0d: case 0x20:
                  break;
            default:
                  return 0;
            }
      }
      return 1;       /* All whitespace */
}

/*
 * This is a vastly simplified, non-validating XML tree skipper.
 */
int
xer_skip_unknown(xer_check_tag_e tcv, ber_tlv_len_t *depth) {
      assert(*depth > 0);
      switch(tcv) {
      case XCT_BOTH:
      case XCT_UNKNOWN_BO:
            /* These negate each other. */
            return 0;
      case XCT_OPENING:
      case XCT_UNKNOWN_OP:
            ++(*depth);
            return 0;
      case XCT_CLOSING:
      case XCT_UNKNOWN_CL:
            if(--(*depth) == 0)
                  return (tcv == XCT_CLOSING) ? 2 : 1;
            return 0;
      default:
            return -1;
      }
}

Generated by  Doxygen 1.6.0   Back to index