extensibility in SEQUENCE uper writer
git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@1305 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/asn1c/tests/check-126.-gen-PER.c b/asn1c/tests/check-126.-gen-PER.c
index aa12f1b..5df2c78 100644
--- a/asn1c/tests/check-126.-gen-PER.c
+++ b/asn1c/tests/check-126.-gen-PER.c
@@ -213,6 +213,35 @@
}
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;
int ret;
@@ -220,8 +249,9 @@
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);
diff --git a/skeletons/constr_SEQUENCE.c b/skeletons/constr_SEQUENCE.c
index 2b0ac3d..a6d12fa 100644
--- a/skeletons/constr_SEQUENCE.c
+++ b/skeletons/constr_SEQUENCE.c
@@ -1,5 +1,5 @@
/*-
- * 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 @@
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 @@
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 @@
}
/*
- * 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 @@
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);
}
diff --git a/skeletons/per_encoder.c b/skeletons/per_encoder.c
index 614dd23..960981a 100644
--- a/skeletons/per_encoder.c
+++ b/skeletons/per_encoder.c
@@ -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 @@
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 @@
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;
+}
+
diff --git a/skeletons/per_encoder.h b/skeletons/per_encoder.h
index 9ac130b..32de082 100644
--- a/skeletons/per_encoder.h
+++ b/skeletons/per_encoder.h
@@ -16,6 +16,9 @@
/*
* 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 @@
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 @@
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.
diff --git a/skeletons/per_support.c b/skeletons/per_support.c
index 0bb3d4b..73e2a8d 100644
--- a/skeletons/per_support.c
+++ b/skeletons/per_support.c
@@ -318,3 +318,26 @@
? -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;
+}
+
diff --git a/skeletons/per_support.h b/skeletons/per_support.h
index 2708724..4e7e6bd 100644
--- a/skeletons/per_support.h
+++ b/skeletons/per_support.h
@@ -96,6 +96,12 @@
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.
*/
int uper_put_nsnnwn(asn_per_outp_t *po, int n);