From 6c52784de169a792156a4d1da1312097ff93d551 Mon Sep 17 00:00:00 2001 From: Lev Walkin Date: Sun, 9 Feb 2014 04:34:54 -0800 Subject: [PATCH] PER-encoding of integers wider than 32 bits --- ChangeLog | 1 + asn1c/tests/check-134.-gen-PER.c | 115 +++++++++++++++++++ skeletons/INTEGER.c | 65 ++++++----- skeletons/per_support.c | 58 +++++++++- skeletons/per_support.h | 9 +- tests/127-per-long-OK.asn1 | 2 + tests/134-per-long-OK.asn1 | 21 ++++ tests/134-per-long-OK.asn1.-Pgen-PER | 161 +++++++++++++++++++++++++++ 8 files changed, 394 insertions(+), 38 deletions(-) create mode 100644 asn1c/tests/check-134.-gen-PER.c create mode 100644 tests/134-per-long-OK.asn1 create mode 100644 tests/134-per-long-OK.asn1.-Pgen-PER diff --git a/ChangeLog b/ChangeLog index dff73bd5..7905dc62 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,7 @@ * Default constraint checking fix. Thanks to Bartosz Marcinkiewicz * Get rid of non-standard pointer arithmetics on void* pointer. + * PER-encoding of integers wider than 32 bits. 0.9.24: 2013-Mar-16 diff --git a/asn1c/tests/check-134.-gen-PER.c b/asn1c/tests/check-134.-gen-PER.c new file mode 100644 index 00000000..d129fe3b --- /dev/null +++ b/asn1c/tests/check-134.-gen-PER.c @@ -0,0 +1,115 @@ +/* + * Verify INTEGER values with greater than 32 bits range. + */ +#undef NDEBUG +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef _LP64 +int main() { + assert(sizeof(void *) < 8); + return; +} +#else /* 64-bit platform */ + +static unsigned long i2ul(const INTEGER_t *i) { + unsigned long l; + int ret = asn_INTEGER2ulong(i, &l); + assert(ret == 0); + return l; +} + +static void ul2i(INTEGER_t *i, unsigned long l) { + int ret = asn_ulong2INTEGER(i, l); + assert(ret == 0); +} + +static void +verify(int testNo, T_t *ti) { + asn_enc_rval_t er; + asn_dec_rval_t rv; + unsigned char buf[16]; + T_t *to = 0; + + fprintf(stderr, "%d IN: { %lu, %lu }\n", testNo, + i2ul(&ti->unsigned33), i2ul(&ti->unsigned42)); + + er = uper_encode_to_buffer(&asn_DEF_T, ti, buf, sizeof buf); + assert(er.encoded == 33 + 42); + + rv = uper_decode(0, &asn_DEF_T, (void *)&to, buf, sizeof buf, 0, 0); + assert(rv.code == RC_OK); + + fprintf(stderr, "%d ENC: %2x%2x%2x%2x %2x%2x%2x%2x\n", testNo, + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + fprintf(stderr, "%d OUT: { %lu, %lu } vs { %lu, %lu }\n", + testNo, + i2ul(&ti->unsigned33), i2ul(&ti->unsigned42), + i2ul(&to->unsigned33), i2ul(&to->unsigned42)); + assert(i2ul(&ti->unsigned33) == i2ul(&to->unsigned33)); + assert(i2ul(&ti->unsigned42) == i2ul(&to->unsigned42)); + + xer_fprint(stderr, &asn_DEF_T, ti); + xer_fprint(stderr, &asn_DEF_T, to); +} + +static void +NO_encode(int testNo, T_t *ti) { + asn_enc_rval_t er; + unsigned char buf[16]; + + fprintf(stderr, "%d IN: { %lu, %lu }\n", testNo, + i2ul(&ti->unsigned33), i2ul(&ti->unsigned42)); + + er = uper_encode_to_buffer(&asn_DEF_T, ti, buf, sizeof buf); + assert(er.encoded == -1); +} + +int main() { + T_t ti; + + memset(&ti, 0, sizeof(ti)); + ul2i(&ti.unsigned33, 0); + ul2i(&ti.unsigned42, 0); + verify(1, &ti); + + ul2i(&ti.unsigned33, 1); + ul2i(&ti.unsigned42, 1); + verify(2, &ti); + + ul2i(&ti.unsigned33, 5000000000); + ul2i(&ti.unsigned42, 3153600000000); + verify(3, &ti); + + ul2i(&ti.unsigned33, -1); + ul2i(&ti.unsigned42, 0); + NO_encode(4, &ti); + + ul2i(&ti.unsigned33, 0); + ul2i(&ti.unsigned42, -1); + NO_encode(5, &ti); + + ul2i(&ti.unsigned33, 5000000000 + 1); + ul2i(&ti.unsigned42, 0); + NO_encode(6, &ti); + + ul2i(&ti.unsigned33, 0); + ul2i(&ti.unsigned42, 3153600000000 + 1); + NO_encode(7, &ti); + + ul2i(&ti.unsigned33, 5000000000 - 1); + ul2i(&ti.unsigned42, 3153600000000 - 1); + verify(8, &ti); + + return 0; +} + +#endif /* 64-bit platform */ diff --git a/skeletons/INTEGER.c b/skeletons/INTEGER.c index 4dfe530a..9ce69604 100644 --- a/skeletons/INTEGER.c +++ b/skeletons/INTEGER.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003, 2004, 2005, 2006, 2007 Lev Walkin . + * Copyright (c) 2003-2014 Lev Walkin . * All rights reserved. * Redistribution and modifications are permitted subject to BSD license. */ @@ -595,30 +595,35 @@ INTEGER_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, } } - /* X.691, #12.2.2 */ + /* X.691-2008/11, #13.2.2, constrained whole number */ if(ct && ct->flags != APC_UNCONSTRAINED) { - /* #10.5.6 */ + /* #11.5.6 */ ASN_DEBUG("Integer with range %d bits", ct->range_bits); if(ct->range_bits >= 0) { - long value; - if(ct->range_bits == 32) { - long lhalf; - value = per_get_few_bits(pd, 16); - if(value < 0) _ASN_DECODE_STARVED; - lhalf = per_get_few_bits(pd, 16); - if(lhalf < 0) _ASN_DECODE_STARVED; - value = (value << 16) | lhalf; - } else { - value = per_get_few_bits(pd, ct->range_bits); - if(value < 0) _ASN_DECODE_STARVED; - } - ASN_DEBUG("Got value %ld + low %ld", - value, ct->lower_bound); - value += ct->lower_bound; - if((specs && specs->field_unsigned) - ? asn_ulong2INTEGER(st, value) - : asn_long2INTEGER(st, value)) + if((size_t)ct->range_bits > 8 * sizeof(unsigned long)) _ASN_DECODE_FAILED; + + if(specs && specs->field_unsigned) { + unsigned long uvalue; + if(uper_get_constrained_whole_number(pd, + &uvalue, ct->range_bits)) + _ASN_DECODE_STARVED; + ASN_DEBUG("Got value %lu + low %ld", + uvalue, ct->lower_bound); + uvalue += ct->lower_bound; + if(asn_ulong2INTEGER(st, uvalue)) + _ASN_DECODE_FAILED; + } else { + unsigned long svalue; + if(uper_get_constrained_whole_number(pd, + &svalue, ct->range_bits)) + _ASN_DECODE_STARVED; + ASN_DEBUG("Got value %ld + low %ld", + svalue, ct->lower_bound); + svalue += ct->lower_bound; + if(asn_long2INTEGER(st, svalue)) + _ASN_DECODE_FAILED; + } return rval; } } else { @@ -725,22 +730,14 @@ INTEGER_encode_uper(asn_TYPE_descriptor_t *td, } - /* X.691, #12.2.2 */ + /* X.691-11/2008, #13.2.2, test if constrained whole number */ if(ct && ct->range_bits >= 0) { - /* #10.5.6 */ + /* #11.5.6 -> #11.3 */ ASN_DEBUG("Encoding integer with range %d bits", ct->range_bits); - if(ct->range_bits == 32) { - /* TODO: extend to >32 bits */ - long v = value - ct->lower_bound; - if(per_put_few_bits(po, v >> 1, 31) - || per_put_few_bits(po, v, 1)) - _ASN_ENCODE_FAILED; - } else { - if(per_put_few_bits(po, value - ct->lower_bound, - ct->range_bits)) - _ASN_ENCODE_FAILED; - } + long v = value - ct->lower_bound; + if(uper_put_constrained_whole_number_s(po, v, ct->range_bits)) + _ASN_ENCODE_FAILED; _ASN_ENCODED_OK(er); } diff --git a/skeletons/per_support.c b/skeletons/per_support.c index 2481fffb..0d089f49 100644 --- a/skeletons/per_support.c +++ b/skeletons/per_support.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, 2007 Lev Walkin . + * Copyright (c) 2005-2014 Lev Walkin . * All rights reserved. * Redistribution and modifications are permitted subject to BSD license. */ @@ -238,8 +238,8 @@ uper_get_nsnnwn(asn_per_data_t *pd) { } /* - * Put the normally small non-negative whole number. - * X.691, #10.6 + * X.691-11/2008, #11.6 + * Encoding of a normally small non-negative whole number */ int uper_put_nsnnwn(asn_per_outp_t *po, int n) { @@ -264,6 +264,58 @@ uper_put_nsnnwn(asn_per_outp_t *po, int n) { } +/* X.691-2008/11, #11.5.6 -> #11.3 */ +int uper_get_constrained_whole_number(asn_per_data_t *pd, unsigned long *out_value, int nbits) { + unsigned long lhalf; /* Lower half of the number*/ + long half; + + if(nbits <= 31) { + half = per_get_few_bits(pd, nbits); + if(half < 0) return -1; + *out_value = half; + return 0; + } + + if((size_t)nbits > 8 * sizeof(*out_value)) + return -1; /* RANGE */ + + half = per_get_few_bits(pd, 31); + if(half < 0) return -1; + + if(uper_get_constrained_whole_number(pd, &lhalf, nbits - 31)) + return -1; + + *out_value = ((unsigned long)half << (nbits - 31)) | lhalf; + return 0; +} + + +/* X.691-2008/11, #11.5.6 -> #11.3 */ +int uper_put_constrained_whole_number_s(asn_per_outp_t *po, long v, int nbits) { + /* + * Assume signed number can be safely coerced into + * unsigned of the same range. + * The following testing code will likely be optimized out + * by compiler if it is true. + */ + unsigned long uvalue1 = ULONG_MAX; + long svalue = uvalue1; + unsigned long uvalue2 = svalue; + assert(uvalue1 == uvalue2); + return uper_put_constrained_whole_number_u(po, v, nbits); +} + +int uper_put_constrained_whole_number_u(asn_per_outp_t *po, unsigned long v, int nbits) { + if(nbits <= 31) { + return per_put_few_bits(po, v, nbits); + } else { + /* Put higher portion first, followed by lower 31-bit */ + if(uper_put_constrained_whole_number_u(po, v >> 31, nbits - 31)) + return -1; + return per_put_few_bits(po, v, 31); + } +} + /* * Put a small number of bits (<= 31). */ diff --git a/skeletons/per_support.h b/skeletons/per_support.h index 7cb1a0ca..10c84ed0 100644 --- a/skeletons/per_support.h +++ b/skeletons/per_support.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, 2007 Lev Walkin . + * Copyright (c) 2005-2014 Lev Walkin . * All rights reserved. * Redistribution and modifications are permitted subject to BSD license. */ @@ -81,6 +81,9 @@ ssize_t uper_get_nslength(asn_per_data_t *pd); */ ssize_t uper_get_nsnnwn(asn_per_data_t *pd); +/* X.691-2008/11, #11.5.6 */ +int uper_get_constrained_whole_number(asn_per_data_t *pd, unsigned long *v, int nbits); + /* Non-thread-safe debugging function, don't use it */ char *per_data_string(asn_per_data_t *pd); @@ -103,6 +106,10 @@ int per_put_few_bits(asn_per_outp_t *per_data, uint32_t bits, int obits); /* Output a large number of bits */ int per_put_many_bits(asn_per_outp_t *po, const uint8_t *src, int put_nbits); +/* X.691-2008/11, #11.5 */ +int uper_put_constrained_whole_number_s(asn_per_outp_t *po, long v, int nbits); +int uper_put_constrained_whole_number_u(asn_per_outp_t *po, unsigned long v, int nbits); + /* * Put the length "n" to the Unaligned PER stream. * This function returns the number of units which may be flushed diff --git a/tests/127-per-long-OK.asn1 b/tests/127-per-long-OK.asn1 index ad1c5008..0a7b6feb 100644 --- a/tests/127-per-long-OK.asn1 +++ b/tests/127-per-long-OK.asn1 @@ -1,5 +1,6 @@ -- OK: Everything is fine +-- Also see .134 for wider integer types. -- iso.org.dod.internet.private.enterprise (1.3.6.1.4.1) -- .spelio.software.asn1c.test (9363.1.5.1) @@ -12,6 +13,7 @@ ModulePERLong BEGIN T ::= SEQUENCE { + -- Should be supported on all 32-bit platforms and above. small32range INTEGER (-2000000000..2000000000), full32range INTEGER (-2147483648..2147483647), unsigned32 INTEGER (0..4294967295), diff --git a/tests/134-per-long-OK.asn1 b/tests/134-per-long-OK.asn1 new file mode 100644 index 00000000..02e6e66f --- /dev/null +++ b/tests/134-per-long-OK.asn1 @@ -0,0 +1,21 @@ + +-- OK: Everything is fine +-- Also see .127 for narrower integer types. + +-- iso.org.dod.internet.private.enterprise (1.3.6.1.4.1) +-- .spelio.software.asn1c.test (9363.1.5.1) +-- .134 + +ModulePERLong + { iso org(3) dod(6) internet (1) private(4) enterprise(1) + spelio(9363) software(1) asn1c(5) test(1) 134 } + DEFINITIONS AUTOMATIC TAGS ::= +BEGIN + + -- Supported only on 64-bit platforms. + T ::= SEQUENCE { + unsigned33 INTEGER (0..5000000000), -- range 33 bits + unsigned42 INTEGER (0..3153600000000) -- range 42 bits + } + +END diff --git a/tests/134-per-long-OK.asn1.-Pgen-PER b/tests/134-per-long-OK.asn1.-Pgen-PER new file mode 100644 index 00000000..f64c0998 --- /dev/null +++ b/tests/134-per-long-OK.asn1.-Pgen-PER @@ -0,0 +1,161 @@ + +/*** <<< INCLUDES [T] >>> ***/ + +#include +#include + +/*** <<< TYPE-DECLS [T] >>> ***/ + +typedef struct T { + INTEGER_t unsigned33; + INTEGER_t unsigned42; + + /* Context for parsing across buffer boundaries */ + asn_struct_ctx_t _asn_ctx; +} T_t; + +/*** <<< FUNC-DECLS [T] >>> ***/ + +extern asn_TYPE_descriptor_t asn_DEF_T; + +/*** <<< CODE [T] >>> ***/ + +static int +memb_unsigned33_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const INTEGER_t *st = (const INTEGER_t *)sptr; + long value; + + if(!sptr) { + _ASN_CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + if(asn_INTEGER2long(st, &value)) { + _ASN_CTFAIL(app_key, td, sptr, + "%s: value too large (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + if((value >= 0 && value <= 5000000000)) { + /* Constraint check succeeded */ + return 0; + } else { + _ASN_CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + +static int +memb_unsigned42_constraint_1(asn_TYPE_descriptor_t *td, const void *sptr, + asn_app_constraint_failed_f *ctfailcb, void *app_key) { + const INTEGER_t *st = (const INTEGER_t *)sptr; + long value; + + if(!sptr) { + _ASN_CTFAIL(app_key, td, sptr, + "%s: value not given (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + if(asn_INTEGER2long(st, &value)) { + _ASN_CTFAIL(app_key, td, sptr, + "%s: value too large (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } + + if((value >= 0 && value <= 3153600000000)) { + /* Constraint check succeeded */ + return 0; + } else { + _ASN_CTFAIL(app_key, td, sptr, + "%s: constraint failed (%s:%d)", + td->name, __FILE__, __LINE__); + return -1; + } +} + + +/*** <<< CTDEFS [T] >>> ***/ + +static asn_per_constraints_t asn_PER_memb_unsigned33_constr_2 GCC_NOTUSED = { + { APC_CONSTRAINED, 33, -1, 0, 5000000000 } /* (0..5000000000) */, + { APC_UNCONSTRAINED, -1, -1, 0, 0 }, + 0, 0 /* No PER value map */ +}; +static asn_per_constraints_t asn_PER_memb_unsigned42_constr_3 GCC_NOTUSED = { + { APC_CONSTRAINED, 42, -1, 0, 3153600000000 } /* (0..3153600000000) */, + { APC_UNCONSTRAINED, -1, -1, 0, 0 }, + 0, 0 /* No PER value map */ +}; + +/*** <<< STAT-DEFS [T] >>> ***/ + +static asn_TYPE_member_t asn_MBR_T_1[] = { + { ATF_NOFLAGS, 0, offsetof(struct T, unsigned33), + .tag = (ASN_TAG_CLASS_CONTEXT | (0 << 2)), + .tag_mode = -1, /* IMPLICIT tag at current level */ + .type = &asn_DEF_INTEGER, + .memb_constraints = memb_unsigned33_constraint_1, + .per_constraints = &asn_PER_memb_unsigned33_constr_2, + .default_value = 0, + .name = "unsigned33" + }, + { ATF_NOFLAGS, 0, offsetof(struct T, unsigned42), + .tag = (ASN_TAG_CLASS_CONTEXT | (1 << 2)), + .tag_mode = -1, /* IMPLICIT tag at current level */ + .type = &asn_DEF_INTEGER, + .memb_constraints = memb_unsigned42_constraint_1, + .per_constraints = &asn_PER_memb_unsigned42_constr_3, + .default_value = 0, + .name = "unsigned42" + }, +}; +static ber_tlv_tag_t asn_DEF_T_tags_1[] = { + (ASN_TAG_CLASS_UNIVERSAL | (16 << 2)) +}; +static asn_TYPE_tag2member_t asn_MAP_T_tag2el_1[] = { + { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* unsigned33 */ + { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 } /* unsigned42 */ +}; +static asn_SEQUENCE_specifics_t asn_SPC_T_specs_1 = { + sizeof(struct T), + offsetof(struct T, _asn_ctx), + asn_MAP_T_tag2el_1, + 2, /* Count of tags in the map */ + 0, 0, 0, /* Optional elements (not needed) */ + -1, /* Start extensions */ + -1 /* Stop extensions */ +}; +asn_TYPE_descriptor_t asn_DEF_T = { + "T", + "T", + SEQUENCE_free, + SEQUENCE_print, + SEQUENCE_constraint, + SEQUENCE_decode_ber, + SEQUENCE_encode_der, + SEQUENCE_decode_xer, + SEQUENCE_encode_xer, + SEQUENCE_decode_uper, + SEQUENCE_encode_uper, + 0, /* Use generic outmost tag fetcher */ + asn_DEF_T_tags_1, + sizeof(asn_DEF_T_tags_1) + /sizeof(asn_DEF_T_tags_1[0]), /* 1 */ + asn_DEF_T_tags_1, /* Same as above */ + sizeof(asn_DEF_T_tags_1) + /sizeof(asn_DEF_T_tags_1[0]), /* 1 */ + 0, /* No PER visible constraints */ + asn_MBR_T_1, + 2, /* Elements count */ + &asn_SPC_T_specs_1 /* Additional specs */ +}; +