Commit 62258e25 authored by Lev Walkin's avatar Lev Walkin

extensibility in SEQUENCE uper writer

parent 788490db
......@@ -212,6 +212,35 @@ xer_encoding_equal(char *obuf, size_t osize, char *nbuf, size_t nsize) {
return 1;
}
static void
compare_with_data_out(const char *fname, char *buf, int size) {
char outName[256];
char fbuf[1024];
size_t rd;
FILE *f;
sprintf(outName, "../data-126/%s", fname);
strcpy(outName + strlen(outName) - 3, ".out");
fprintf(stderr, "Comparing PER output with [%s]\n", outName);
f = fopen(outName, "r");
if(f) {
assert(f);
rd = fread(fbuf, 1, sizeof(fbuf), f);
assert(rd);
fclose(f);
assert(rd == size);
assert(memcmp(fbuf, buf, rd) == 0);
fprintf(stderr, "XER->PER recoding .in->.out match.\n");
} else if(getenv("REGENERATE")) {
f = fopen(outName, "w");
fwrite(buf, 1, size, f);
fclose(f);
}
}
static void
process_XER_data(const char *fname, char *fbuf, int size) {
PDU_t *st;
......@@ -220,8 +249,9 @@ process_XER_data(const char *fname, char *fbuf, int size) {
st = load_object_from(fname, fbuf, size, AS_XER);
if(!st) return;
/* Save and re-load as DER */
/* Save and re-load as PER */
save_object_as(st, AS_PER);
compare_with_data_out(fname, buf, buf_offset);
st = load_object_from("buffer", buf, buf_offset, AS_PER);
assert(st);
......
/*-
* Copyright (c) 2003, 2004, 2005, 2006 Lev Walkin <vlm@lionet.info>.
* Copyright (c) 2003, 2004, 2005, 2006, 2007 Lev Walkin <vlm@lionet.info>.
* All rights reserved.
* Redistribution and modifications are permitted subject to BSD license.
*/
......@@ -1162,12 +1162,87 @@ SEQUENCE_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
return rv;
}
/*
* #10.1, #10.2
*/
static int
uper_put_open_type(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
void *buf;
ssize_t size;
size = uper_encode_to_new_buffer(td, constraints, sptr, &buf);
if(size <= 0) return -1;
while(size) {
ssize_t maySave = uper_put_length(po, size);
if(maySave < 0) break;
if(per_put_many_bits(po, buf, maySave * 8)) break;
buf = (char *)buf + maySave;
size -= maySave;
}
if(size) {
FREEMEM(buf);
return -1;
}
return 0;
}
static int
SEQUENCE_handle_extensions(asn_TYPE_descriptor_t *td, void *sptr,
asn_per_outp_t *po1, asn_per_outp_t *po2) {
asn_SEQUENCE_specifics_t *specs
= (asn_SEQUENCE_specifics_t *)td->specifics;
int num = 0;
int edx;
if(specs->ext_before < 0)
return 0;
/* Find out which extensions are present */
for(edx = specs->ext_after + 1; edx < td->elements_count; edx++) {
asn_TYPE_member_t *elm = &td->elements[edx];
void *memb_ptr; /* Pointer to the member */
void **memb_ptr2; /* Pointer to that pointer */
int present;
if(!IN_EXTENSION_GROUP(specs, edx))
continue;
/* Fetch the pointer to this member */
if(elm->flags & ATF_POINTER) {
memb_ptr2 = (void **)((char *)sptr + elm->memb_offset);
present = (*memb_ptr2 != 0);
} else {
memb_ptr = (void *)((char *)sptr + elm->memb_offset);
memb_ptr2 = &memb_ptr;
present = 1;
}
ASN_DEBUG("checking ext %d is present => %d", edx, present);
num += present;
/* Encode as presence marker */
if(po1 && per_put_few_bits(po1, present, 1))
return -1;
/* Encode as open type field */
if(po2 && present && uper_put_open_type(elm->type,
elm->per_constraints, *memb_ptr2, po2))
return -1;
}
return num;
}
asn_enc_rval_t
SEQUENCE_encode_uper(asn_TYPE_descriptor_t *td,
asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
asn_SEQUENCE_specifics_t *specs
= (asn_SEQUENCE_specifics_t *)td->specifics;
asn_enc_rval_t er;
int n_extensions;
int edx;
int i;
......@@ -1179,8 +1254,18 @@ SEQUENCE_encode_uper(asn_TYPE_descriptor_t *td,
er.encoded = 0;
ASN_DEBUG("Encoding %s as SEQUENCE (UPER)", td->name);
if(specs->ext_before >= 0)
_ASN_ENCODE_FAILED; /* We don't encode extensions yet */
/*
* X.691#18.1 Whether structure is extensible
* and whether to encode extensions
*/
if(specs->ext_before >= 0) {
n_extensions = SEQUENCE_handle_extensions(td, sptr, 0, 0);
per_put_few_bits(po, n_extensions ? 1 : 0, 1);
} else {
n_extensions = 0; /* There are no extensions to encode */
}
/* Encode a presence bitmap */
for(i = 0; i < specs->roms_count; i++) {
......@@ -1216,10 +1301,11 @@ SEQUENCE_encode_uper(asn_TYPE_descriptor_t *td,
}
/*
* Get the sequence ROOT elements.
* Encode the sequence ROOT elements.
*/
ASN_DEBUG("ext_after = %d, ec = %d, eb = %d", specs->ext_after, td->elements_count, specs->ext_before);
for(edx = 0; edx < ((specs->ext_before < 0)
? td->elements_count : specs->ext_before - 1); edx++) {
? td->elements_count : specs->ext_after); edx++) {
asn_TYPE_member_t *elm = &td->elements[edx];
void *memb_ptr; /* Pointer to the member */
void **memb_ptr2; /* Pointer to that pointer */
......@@ -1244,12 +1330,32 @@ SEQUENCE_encode_uper(asn_TYPE_descriptor_t *td,
if(elm->default_value && elm->default_value(0, memb_ptr2) == 1)
continue;
ASN_DEBUG("encoding root %d", edx);
er = elm->type->uper_encoder(elm->type, elm->per_constraints,
*memb_ptr2, po);
if(er.encoded == -1)
return er;
}
/* No extensions to encode */
if(!n_extensions) _ASN_ENCODED_OK(er);
ASN_DEBUG("Length of %d bit-map", n_extensions);
/* #18.8. Write down the presence bit-map length. */
if(uper_put_nslength(po, n_extensions))
_ASN_ENCODE_FAILED;
ASN_DEBUG("Bit-map of %d elements", n_extensions);
/* #18.7. Encoding the extensions presence bit-map. */
/* TODO: act upon NOTE in #18.7 for canonical PER */
if(SEQUENCE_handle_extensions(td, sptr, po, 0) != n_extensions)
_ASN_ENCODE_FAILED;
ASN_DEBUG("Writing %d extensions", n_extensions);
/* #18.9. Encode extensions as open type fields. */
if(SEQUENCE_handle_extensions(td, sptr, 0, po) != n_extensions)
_ASN_ENCODE_FAILED;
_ASN_ENCODED_OK(er);
}
......@@ -2,41 +2,11 @@
#include <asn_internal.h>
#include <per_encoder.h>
/* Flush partially filled buffer */
static int _uper_encode_flush_outp(asn_per_outp_t *po);
static asn_enc_rval_t uper_encode_internal(asn_TYPE_descriptor_t *td, asn_per_constraints_t *, void *sptr, asn_app_consume_bytes_f *cb, void *app_key);
asn_enc_rval_t
uper_encode(asn_TYPE_descriptor_t *td, void *sptr, asn_app_consume_bytes_f *cb, void *app_key) {
asn_per_outp_t po;
asn_enc_rval_t er;
/*
* Invoke type-specific encoder.
*/
if(!td || !td->uper_encoder)
_ASN_ENCODE_FAILED; /* PER is not compiled in */
po.buffer = po.tmpspace;
po.nboff = 0;
po.nbits = 8 * sizeof(po.tmpspace);
po.outper = cb;
po.op_key = app_key;
po.flushed_bytes = 0;
er = td->uper_encoder(td, 0, sptr, &po);
if(er.encoded != -1) {
size_t bits_to_flush;
bits_to_flush = ((po.buffer - po.tmpspace) << 3) + po.nboff;
/* Set number of bits encoded to a firm value */
er.encoded = (po.flushed_bytes << 3) + bits_to_flush;
if(_uper_encode_flush_outp(&po))
_ASN_ENCODE_FAILED;
}
return er;
return uper_encode_internal(td, 0, sptr, cb, app_key);
}
/*
......@@ -63,20 +33,70 @@ asn_enc_rval_t
uper_encode_to_buffer(asn_TYPE_descriptor_t *td, void *sptr, void *buffer, size_t buffer_size) {
enc_to_buf_arg key;
/*
* Invoke type-specific encoder.
*/
if(!td || !td->uper_encoder)
_ASN_ENCODE_FAILED; /* PER is not compiled in */
key.buffer = buffer;
key.left = buffer_size;
ASN_DEBUG("Encoding \"%s\" using UNALIGNED PER", td->name);
if(td) ASN_DEBUG("Encoding \"%s\" using UNALIGNED PER", td->name);
return uper_encode(td, sptr, encode_to_buffer_cb, &key);
return uper_encode_internal(td, 0, sptr, encode_to_buffer_cb, &key);
}
typedef struct enc_dyn_arg {
void *buffer;
size_t length;
size_t allocated;
} enc_dyn_arg;
static int
encode_dyn_cb(const void *buffer, size_t size, void *key) {
enc_dyn_arg *arg = key;
if(arg->length + size >= arg->allocated) {
void *p;
arg->allocated = arg->allocated ? (arg->allocated << 2) : size;
p = REALLOC(arg->buffer, arg->allocated);
if(!p) {
FREEMEM(arg->buffer);
memset(arg, 0, sizeof(*arg));
return -1;
}
arg->buffer = p;
}
memcpy(((char *)arg->buffer) + arg->length, buffer, size);
arg->length += size;
return 0;
}
ssize_t
uper_encode_to_new_buffer(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, void **buffer_r) {
asn_enc_rval_t er;
enc_dyn_arg key;
memset(&key, 0, sizeof(key));
er = uper_encode_internal(td, constraints, sptr, encode_dyn_cb, &key);
switch(er.encoded) {
case -1:
FREEMEM(key.buffer);
return -1;
case 0:
FREEMEM(key.buffer);
key.buffer = MALLOC(1);
if(key.buffer) {
*(char *)key.buffer = '\0';
*buffer_r = key.buffer;
return 1;
} else {
return -1;
}
default:
*buffer_r = key.buffer;
return ((er.encoded + 7) >> 3);
}
}
/*
* Internally useful functions.
*/
/* Flush partially filled buffer */
static int
_uper_encode_flush_outp(asn_per_outp_t *po) {
uint8_t *buf;
......@@ -93,3 +113,38 @@ _uper_encode_flush_outp(asn_per_outp_t *po) {
return po->outper(po->tmpspace, buf - po->tmpspace, po->op_key);
}
static asn_enc_rval_t
uper_encode_internal(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, asn_app_consume_bytes_f *cb, void *app_key) {
asn_per_outp_t po;
asn_enc_rval_t er;
/*
* Invoke type-specific encoder.
*/
if(!td || !td->uper_encoder)
_ASN_ENCODE_FAILED; /* PER is not compiled in */
po.buffer = po.tmpspace;
po.nboff = 0;
po.nbits = 8 * sizeof(po.tmpspace);
po.outper = cb;
po.op_key = app_key;
po.flushed_bytes = 0;
er = td->uper_encoder(td, constraints, sptr, &po);
if(er.encoded != -1) {
size_t bits_to_flush;
bits_to_flush = ((po.buffer - po.tmpspace) << 3) + po.nboff;
/* Set number of bits encoded to a firm value */
er.encoded = (po.flushed_bytes << 3) + bits_to_flush;
if(_uper_encode_flush_outp(&po))
_ASN_ENCODE_FAILED;
}
return er;
}
......@@ -16,6 +16,9 @@ struct asn_TYPE_descriptor_s; /* Forward declaration */
/*
* Unaligned PER encoder of any ASN.1 type. May be invoked by the application.
* WARNING: This function returns the number of encoded bits in the .encoded
* field of the return value. Use the following formula to convert to bytes:
* bytes = ((.encoded + 7) / 8)
*/
asn_enc_rval_t uper_encode(struct asn_TYPE_descriptor_s *type_descriptor,
void *struct_ptr, /* Structure to be encoded */
......@@ -23,7 +26,11 @@ asn_enc_rval_t uper_encode(struct asn_TYPE_descriptor_s *type_descriptor,
void *app_key /* Arbitrary callback argument */
);
/* A variant of uper_encode() which encodes data into the existing buffer */
/*
* A variant of uper_encode() which encodes data into the existing buffer
* WARNING: This function returns the number of encoded bits in the .encoded
* field of the return value.
*/
asn_enc_rval_t uper_encode_to_buffer(
struct asn_TYPE_descriptor_s *type_descriptor,
void *struct_ptr, /* Structure to be encoded */
......@@ -31,6 +38,19 @@ asn_enc_rval_t uper_encode_to_buffer(
size_t buffer_size /* Initial buffer size (max) */
);
/*
* A variant of uper_encode_to_buffer() which allocates buffer itself.
* Returns the number of bytes in the buffer or -1 in case of failure.
* WARNING: This function produces a "Production of the complete encoding",
* with length of at least one octet. Contrast this to precise bit-preserving
* encoding of uper_encode() and uper_encode_to_buffer().
*/
ssize_t uper_encode_to_new_buffer(
struct asn_TYPE_descriptor_s *type_descriptor,
asn_per_constraints_t *constraints,
void *struct_ptr, /* Structure to be encoded */
void **buffer_r /* Buffer allocated and returned */
);
/*
* Type of the generic PER encoder function.
......
......@@ -318,3 +318,26 @@ uper_put_length(asn_per_outp_t *po, size_t length) {
? -1 : (ssize_t)(length << 14);
}
/*
* Put the normally small length "n" into the stream.
* This procedure used to encode length of extensions bit-maps
* for SET and SEQUENCE types.
*/
int
uper_put_nslength(asn_per_outp_t *po, size_t length) {
if(length <= 64) {
/* #10.9.3.4 */
if(length == 0) return -1;
return per_put_few_bits(po, length-1, 7) ? -1 : 0;
} else {
if(uper_put_length(po, length) != length) {
/* This might happen in case of >16K extensions */
return -1;
}
}
return 0;
}
......@@ -95,6 +95,12 @@ int per_put_many_bits(asn_per_outp_t *po, const uint8_t *src, int put_nbits);
*/
ssize_t uper_put_length(asn_per_outp_t *po, size_t whole_length);
/*
* Put the normally small length "n" to the Unaligned PER stream.
* Returns 0 or -1.
*/
int uper_put_nslength(asn_per_outp_t *po, size_t length);
/*
* Put the normally small non-negative whole number.
*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment