diff options
author | Lev Walkin <vlm@lionet.info> | 2014-02-09 04:34:54 -0800 |
---|---|---|
committer | Lev Walkin <vlm@lionet.info> | 2014-02-09 04:34:54 -0800 |
commit | 6c52784de169a792156a4d1da1312097ff93d551 (patch) | |
tree | 2abae447808b38c9130b22cdc2b9efdc0ba14882 /skeletons | |
parent | bfc76e8f1e3c96cb7ada29b13825019201945bfd (diff) |
PER-encoding of integers wider than 32 bits
Diffstat (limited to 'skeletons')
-rw-r--r-- | skeletons/INTEGER.c | 63 | ||||
-rw-r--r-- | skeletons/per_support.c | 58 | ||||
-rw-r--r-- | skeletons/per_support.h | 9 |
3 files changed, 93 insertions, 37 deletions
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 <vlm@lionet.info>. + * Copyright (c) 2003-2014 Lev Walkin <vlm@lionet.info>. * 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; + 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 { - value = per_get_few_bits(pd, ct->range_bits); - if(value < 0) _ASN_DECODE_STARVED; + 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; } - 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)) - _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 <vlm@lionet.info>. + * Copyright (c) 2005-2014 Lev Walkin <vlm@lionet.info>. * 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 <vlm@lionet.info>. + * Copyright (c) 2005-2014 Lev Walkin <vlm@lionet.info>. * 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 |