asn1c/skeletons/constr_SET_OF_oer.c

270 lines
7.2 KiB
C

/*
* Copyright (c) 2017 Lev Walkin <vlm@lionet.info>.
* All rights reserved.
* Redistribution and modifications are permitted subject to BSD license.
*/
#ifndef ASN_DISABLE_OER_SUPPORT
#include <asn_internal.h>
#include <constr_SET_OF.h>
#include <asn_SET_OF.h>
#include <errno.h>
/*
* This macro "eats" the part of the buffer which is definitely "consumed",
* i.e. was correctly converted into local representation or rightfully skipped.
*/
#undef ADVANCE
#define ADVANCE(num_bytes) \
do { \
size_t num = num_bytes; \
ptr = ((const char *)ptr) + num; \
size -= num; \
consumed_myself += num; \
} while(0)
/*
* Switch to the next phase of parsing.
*/
#undef NEXT_PHASE
#define NEXT_PHASE(ctx) \
do { \
ctx->phase++; \
ctx->step = 0; \
} while(0)
#undef SET_PHASE
#define SET_PHASE(ctx, value) \
do { \
ctx->phase = value; \
ctx->step = 0; \
} while(0)
/*
* Return a standardized complex structure.
*/
#undef RETURN
#define RETURN(_code) \
do { \
asn_dec_rval_t rval; \
rval.code = _code; \
rval.consumed = consumed_myself; \
return rval; \
} while(0)
/*
* The SEQUENCE OF and SET OF values utilize a "quantity field".
* It is is a pointless combination of #8.6 (length determinant, capable
* of encoding tiny and huge numbers in the shortest possible number of octets)
* and the variable sized integer. What could have been encoded by #8.6 alone
* is required to be encoded by #8.6 followed by that number of unsigned octets.
* This doesn't make too much sense. It seems that the original version of OER
* standard have been using the unconstrained unsigned integer as a quantity
* field, and this legacy have gone through ISO/ITU-T standardization process.
*/
static ssize_t
oer_fetch_quantity(const void *ptr, size_t size, size_t *qty_r) {
size_t len = 0;
size_t qty;
ssize_t len_len = oer_fetch_length(ptr, size, &len);
if(len_len <= 0) {
*qty_r = 0;
return len_len;
}
if((len_len + len) > size) {
*qty_r = 0;
return 0;
}
const uint8_t *b = (const uint8_t *)ptr + len_len;
const uint8_t *bend = b + len;
/* Skip the leading 0-bytes */
for(; b < bend && *b == 0; b++) {
}
if((bend - b) > (ssize_t)sizeof(size_t)) {
/* Length is not representable by the native size_t type */
*qty_r = 0;
return -1;
}
for(qty = 0; b < bend; b++) {
qty = (qty << 8) + *b;
}
if(qty > RSIZE_MAX) { /* A bit of C11 validation */
*qty_r = 0;
return -1;
}
*qty_r = qty;
assert((size_t)len_len + len == (size_t)(bend - (const uint8_t *)ptr));
return len_len + len;
}
asn_dec_rval_t
SET_OF_decode_oer(const asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
const asn_oer_constraints_t *constraints, void **struct_ptr,
const void *ptr, size_t size) {
asn_SET_OF_specifics_t *specs = (asn_SET_OF_specifics_t *)td->specifics;
asn_dec_rval_t rval = {RC_OK, 0};
void *st = *struct_ptr; /* Target structure */
asn_struct_ctx_t *ctx; /* Decoder context */
size_t consumed_myself = 0; /* Consumed bytes from ptr. */
(void)opt_codec_ctx;
(void)constraints;
/*
* Create the target structure if it is not present already.
*/
if(st == 0) {
st = *struct_ptr = CALLOC(1, specs->struct_size);
if(st == 0) {
RETURN(RC_FAIL);
}
}
/*
* Restore parsing context.
*/
ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset);
/*
* Start to parse where left previously.
*/
switch(ctx->phase) {
case 0: {
/*
* Fetch number of elements to decode.
*/
size_t length = 0;
size_t len_size = oer_fetch_quantity(ptr, size, &length);
switch(len_size) {
case 0:
RETURN(RC_WMORE);
case -1:
RETURN(RC_FAIL);
default:
ASN_DEBUG("ptr[] = %02x, advancing %zu, length=%zu", *(const uint8_t *)ptr, len_size, length);
ADVANCE(len_size);
ctx->left = length;
}
}
NEXT_PHASE(ctx);
/* FALL THROUGH */
case 1: {
/* Decode components of the extension root */
asn_TYPE_member_t *elm = td->elements;
asn_anonymous_set_ *list = _A_SET_FROM_VOID(st);
assert(td->elements_count == 1);
ASN_DEBUG("OER SET OF %s Decoding PHASE 1", td->name);
for(; ctx->left > 0; ctx->left--) {
asn_dec_rval_t rv = elm->type->op->oer_decoder(
opt_codec_ctx, elm->type, elm->oer_constraints, &ctx->ptr, ptr,
size);
ADVANCE(rv.consumed);
switch(rv.code) {
case RC_OK:
if(ASN_SET_ADD(list, ctx->ptr) != 0) {
RETURN(RC_FAIL);
} else {
ctx->ptr = 0;
break;
}
case RC_WMORE:
RETURN(RC_WMORE);
case RC_FAIL:
ASN_STRUCT_FREE(*elm->type, ctx->ptr);
ctx->ptr = 0;
SET_PHASE(ctx, 3);
RETURN(RC_FAIL);
}
}
/* Decoded decently. */
NEXT_PHASE(ctx);
}
/* Fall through */
case 2:
/* Ignore fully decoded */
assert(ctx->left == 0);
RETURN(RC_OK);
case 3:
/* Failed to decode. */
RETURN(RC_FAIL);
}
return rval;
}
static ssize_t
oer_put_quantity(size_t qty, asn_app_consume_bytes_f *cb, void *app_key) {
uint8_t buf[1 + sizeof(size_t)];
uint8_t *b = &buf[sizeof(size_t)]; /* Last addressable */
size_t encoded;
do {
*b-- = qty;
qty >>= 8;
} while(qty);
*b = sizeof(buf) - (b-buf) - 1;
encoded = sizeof(buf) - (b-buf);
if(cb(b, encoded, app_key) < 0)
return -1;
return encoded;
}
/*
* Encode as Canonical OER.
*/
asn_enc_rval_t
SET_OF_encode_oer(asn_TYPE_descriptor_t *td,
const asn_oer_constraints_t *constraints, void *sptr,
asn_app_consume_bytes_f *cb, void *app_key) {
size_t computed_size = 0;
ssize_t qty_len;
asn_TYPE_member_t *elm;
asn_anonymous_set_ *list;
int n;
(void)constraints;
if(!sptr) ASN__ENCODE_FAILED;
elm = td->elements;
list = _A_SET_FROM_VOID(sptr);
qty_len = oer_put_quantity(list->count, cb, app_key);
if(qty_len < 0) {
ASN__ENCODE_FAILED;
}
computed_size += qty_len;
for(n = 0; n < list->count; n++) {
void *memb_ptr = list->array[n];
asn_enc_rval_t er;
er = elm->type->op->oer_encoder(elm->type, elm->oer_constraints,
memb_ptr, cb, app_key);
if(er.encoded < 0) {
return er;
} else {
computed_size += er.encoded;
}
}
{
asn_enc_rval_t erval = {computed_size, 0, 0};
return erval;
}
}
#endif /* ASN_DISABLE_OER_SUPPORT */