]> git.stg.codes - stg.git/blobdiff - libs/smux/OCTET_STRING.c
Add subscriptions (to replace notifiers).
[stg.git] / libs / smux / OCTET_STRING.c
index 9e9694b5a5c8331b120498761977d9a03eede1c5..5420dedecab20670a8c6a6c36c2ef3ae94b1b52e 100644 (file)
 /*
  * OCTET STRING basic type description.
  */
-static ber_tlv_tag_t asn_DEF_OCTET_STRING_tags[] = {
+static const ber_tlv_tag_t asn_DEF_OCTET_STRING_tags[] = {
        (ASN_TAG_CLASS_UNIVERSAL | (4 << 2))
 };
-static asn_OCTET_STRING_specifics_t asn_DEF_OCTET_STRING_specs = {
+static const asn_OCTET_STRING_specifics_t asn_DEF_OCTET_STRING_specs = {
        sizeof(OCTET_STRING_t),
        offsetof(OCTET_STRING_t, _asn_ctx),
-       0
+       ASN_OSUBV_STR
 };
-static asn_per_constraint_t asn_DEF_OCTET_STRING_constraint = {
-       APC_SEMI_CONSTRAINED, -1, -1, 0, 0
+static const asn_per_constraints_t asn_DEF_OCTET_STRING_constraints = {
+       { APC_CONSTRAINED, 8, 8, 0, 255 },
+       { APC_SEMI_CONSTRAINED, -1, -1, 0, 0 },
+       0, 0
 };
 asn_TYPE_descriptor_t asn_DEF_OCTET_STRING = {
        "OCTET STRING",         /* Canonical name */
@@ -102,15 +104,6 @@ asn_TYPE_descriptor_t asn_DEF_OCTET_STRING = {
                st->size = _es;                                         \
        } while(0)
 
-/*
- * Internal variant of the OCTET STRING.
- */
-typedef enum OS_type {
-       _TT_GENERIC     = 0,    /* Just a random OCTET STRING */
-       _TT_BIT_STRING  = 1,    /* BIT STRING type, a special case */
-       _TT_ANY         = 2     /* ANY type, a special case too */
-} OS_type_e;
-
 /*
  * The main reason why ASN.1 is still alive is that too much time and effort
  * is necessary for learning it more or less adequately, thus creating a gut
@@ -185,11 +178,11 @@ OCTET_STRING_decode_ber(asn_codec_ctx_t *opt_codec_ctx,
        struct _stack *stck;            /* Expectations stack structure */
        struct _stack_el *sel = 0;      /* Stack element */
        int tlv_constr;
-       OS_type_e type_variant = (OS_type_e)specs->subvariant;
+       enum asn_OS_Subvariant type_variant = specs->subvariant;
 
        ASN_DEBUG("Decoding %s as %s (frame %ld)",
                td->name,
-               (type_variant == _TT_GENERIC) ?
+               (type_variant == ASN_OSUBV_STR) ?
                        "OCTET STRING" : "OS-SpecialCase",
                (long)size);
 
@@ -230,7 +223,7 @@ OCTET_STRING_decode_ber(asn_codec_ctx_t *opt_codec_ctx,
                         * Jump into stackless primitive decoding.
                         */
                        _CH_PHASE(ctx, 3);
-                       if(type_variant == _TT_ANY && tag_mode != 1)
+                       if(type_variant == ASN_OSUBV_ANY && tag_mode != 1)
                                APPEND(buf_ptr, rval.consumed);
                        ADVANCE(rval.consumed);
                        goto phase3;
@@ -309,7 +302,7 @@ OCTET_STRING_decode_ber(asn_codec_ctx_t *opt_codec_ctx,
 
                        ASN_DEBUG("Eat EOC; wn=%d--", sel->want_nulls);
 
-                       if(type_variant == _TT_ANY
+                       if(type_variant == ASN_OSUBV_ANY
                        && (tag_mode != 1 || sel->cont_level))
                                APPEND("\0\0", 2);
 
@@ -334,10 +327,10 @@ OCTET_STRING_decode_ber(asn_codec_ctx_t *opt_codec_ctx,
                 * depending on ASN.1 type being decoded.
                 */
                switch(type_variant) {
-               case _TT_BIT_STRING:
+               case ASN_OSUBV_BIT:
                        /* X.690: 8.6.4.1, NOTE 2 */
                        /* Fall through */
-               case _TT_GENERIC:
+               case ASN_OSUBV_STR:
                default:
                        if(sel) {
                                int level = sel->cont_level;
@@ -352,7 +345,7 @@ OCTET_STRING_decode_ber(asn_codec_ctx_t *opt_codec_ctx,
                                /* else, Fall through */
                        }
                        /* Fall through */
-               case _TT_ANY:
+               case ASN_OSUBV_ANY:
                        expected_tag = tlv_tag;
                        break;
                }
@@ -397,7 +390,7 @@ OCTET_STRING_decode_ber(asn_codec_ctx_t *opt_codec_ctx,
                } else {
                        sel->left = tlv_len;
                }
-               if(type_variant == _TT_ANY
+               if(type_variant == ASN_OSUBV_ANY
                && (tag_mode != 1 || sel->cont_level))
                        APPEND(buf_ptr, tlvl);
                sel->got += tlvl;
@@ -431,7 +424,7 @@ OCTET_STRING_decode_ber(asn_codec_ctx_t *opt_codec_ctx,
                len = ((ber_tlv_len_t)size < sel->left)
                                ? (ber_tlv_len_t)size : sel->left;
                if(len > 0) {
-                       if(type_variant == _TT_BIT_STRING
+                       if(type_variant == ASN_OSUBV_BIT
                        && sel->bits_chopped == 0) {
                                /* Put the unused-bits-octet away */
                                st->bits_unused = *(const uint8_t *)buf_ptr;
@@ -464,7 +457,7 @@ OCTET_STRING_decode_ber(asn_codec_ctx_t *opt_codec_ctx,
 
                if(size < (size_t)ctx->left) {
                        if(!size) RETURN(RC_WMORE);
-                       if(type_variant == _TT_BIT_STRING && !ctx->context) {
+                       if(type_variant == ASN_OSUBV_BIT && !ctx->context) {
                                st->bits_unused = *(const uint8_t *)buf_ptr;
                                ctx->left--;
                                ADVANCE(1);
@@ -475,7 +468,7 @@ OCTET_STRING_decode_ber(asn_codec_ctx_t *opt_codec_ctx,
                        ADVANCE(size);
                        RETURN(RC_WMORE);
                } else {
-                       if(type_variant == _TT_BIT_STRING
+                       if(type_variant == ASN_OSUBV_BIT
                        && !ctx->context && ctx->left) {
                                st->bits_unused = *(const uint8_t *)buf_ptr;
                                ctx->left--;
@@ -502,14 +495,14 @@ OCTET_STRING_decode_ber(asn_codec_ctx_t *opt_codec_ctx,
        /*
         * BIT STRING-specific processing.
         */
-       if(type_variant == _TT_BIT_STRING && st->size) {
+       if(type_variant == ASN_OSUBV_BIT && st->size) {
                /* Finalize BIT STRING: zero out unused bits. */
                st->buf[st->size-1] &= 0xff << st->bits_unused;
        }
 
        ASN_DEBUG("Took %ld bytes to encode %s: [%s]:%ld",
                (long)consumed_myself, td->name,
-               (type_variant == _TT_GENERIC) ? (char *)st->buf : "<data>",
+               (type_variant == ASN_OSUBV_STR) ? (char *)st->buf : "<data>",
                (long)st->size);
 
 
@@ -528,7 +521,7 @@ OCTET_STRING_encode_der(asn_TYPE_descriptor_t *td, void *sptr,
                                ? (asn_OCTET_STRING_specifics_t *)td->specifics
                                : &asn_DEF_OCTET_STRING_specs;
        BIT_STRING_t *st = (BIT_STRING_t *)sptr;
-       OS_type_e type_variant = (OS_type_e)specs->subvariant;
+       enum asn_OS_Subvariant type_variant = specs->subvariant;
        int fix_last_byte = 0;
 
        ASN_DEBUG("%s %s as OCTET STRING",
@@ -537,10 +530,11 @@ OCTET_STRING_encode_der(asn_TYPE_descriptor_t *td, void *sptr,
        /*
         * Write tags.
         */
-       if(type_variant != _TT_ANY || tag_mode == 1) {
+       if(type_variant != ASN_OSUBV_ANY || tag_mode == 1) {
                er.encoded = der_write_tags(td,
-                               (type_variant == _TT_BIT_STRING) + st->size,
-                       tag_mode, type_variant == _TT_ANY, tag, cb, app_key);
+                               (type_variant == ASN_OSUBV_BIT) + st->size,
+                       tag_mode, type_variant == ASN_OSUBV_ANY, tag,
+                       cb, app_key);
                if(er.encoded == -1) {
                        er.failed_type = td;
                        er.structure_ptr = sptr;
@@ -548,54 +542,55 @@ OCTET_STRING_encode_der(asn_TYPE_descriptor_t *td, void *sptr,
                }
        } else {
                /* Disallow: [<tag>] IMPLICIT ANY */
-               assert(type_variant != _TT_ANY || tag_mode != -1);
+               assert(type_variant != ASN_OSUBV_ANY || tag_mode != -1);
                er.encoded = 0;
        }
 
        if(!cb) {
-               er.encoded += (type_variant == _TT_BIT_STRING) + st->size;
-               _ASN_ENCODED_OK(er);
+               er.encoded += (type_variant == ASN_OSUBV_BIT) + st->size;
+               ASN__ENCODED_OK(er);
        }
 
        /*
         * Prepare to deal with the last octet of BIT STRING.
         */
-       if(type_variant == _TT_BIT_STRING) {
+       if(type_variant == ASN_OSUBV_BIT) {
                uint8_t b = st->bits_unused & 0x07;
                if(b && st->size) fix_last_byte = 1;
-               _ASN_CALLBACK(&b, 1);
+               ASN__CALLBACK(&b, 1);
                er.encoded++;
        }
 
        /* Invoke callback for the main part of the buffer */
-       _ASN_CALLBACK(st->buf, st->size - fix_last_byte);
+       ASN__CALLBACK(st->buf, st->size - fix_last_byte);
 
        /* The last octet should be stripped off the unused bits */
        if(fix_last_byte) {
                uint8_t b = st->buf[st->size-1] & (0xff << st->bits_unused);
-               _ASN_CALLBACK(&b, 1);
+               ASN__CALLBACK(&b, 1);
        }
 
        er.encoded += st->size;
-       _ASN_ENCODED_OK(er);
+       ASN__ENCODED_OK(er);
 cb_failed:
-       _ASN_ENCODE_FAILED;
+       ASN__ENCODE_FAILED;
 }
 
 asn_enc_rval_t
 OCTET_STRING_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
        int ilevel, enum xer_encoder_flags_e flags,
                asn_app_consume_bytes_f *cb, void *app_key) {
-       static const char *h2c = "0123456789ABCDEF";
+       const char * const h2c = "0123456789ABCDEF";
        const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr;
        asn_enc_rval_t er;
        char scratch[16 * 3 + 4];
        char *p = scratch;
        uint8_t *buf;
        uint8_t *end;
+       size_t i;
 
-       if(!st || !st->buf)
-               _ASN_ENCODE_FAILED;
+       if(!st || (!st->buf && st->size))
+               ASN__ENCODE_FAILED;
 
        er.encoded = 0;
 
@@ -608,7 +603,7 @@ OCTET_STRING_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
                char *scend = scratch + (sizeof(scratch) - 2);
                for(; buf < end; buf++) {
                        if(p >= scend) {
-                               _ASN_CALLBACK(scratch, p - scratch);
+                               ASN__CALLBACK(scratch, p - scratch);
                                er.encoded += p - scratch;
                                p = scratch;
                        }
@@ -616,16 +611,15 @@ OCTET_STRING_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
                        *p++ = h2c[*buf & 0x0F];
                }
 
-               _ASN_CALLBACK(scratch, p-scratch);      /* Dump the rest */
+               ASN__CALLBACK(scratch, p-scratch);      /* Dump the rest */
                er.encoded += p - scratch;
        } else {
-               size_t i;
                for(i = 0; buf < end; buf++, i++) {
                        if(!(i % 16) && (i || st->size > 16)) {
-                               _ASN_CALLBACK(scratch, p-scratch);
+                               ASN__CALLBACK(scratch, p-scratch);
                                er.encoded += (p-scratch);
                                p = scratch;
-                               _i_ASN_TEXT_INDENT(1, ilevel);
+                               ASN__TEXT_INDENT(1, ilevel);
                        }
                        *p++ = h2c[(*buf >> 4) & 0x0F];
                        *p++ = h2c[*buf & 0x0F];
@@ -633,20 +627,20 @@ OCTET_STRING_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
                }
                if(p - scratch) {
                        p--;    /* Remove the tail space */
-                       _ASN_CALLBACK(scratch, p-scratch); /* Dump the rest */
+                       ASN__CALLBACK(scratch, p-scratch); /* Dump the rest */
                        er.encoded += p - scratch;
                        if(st->size > 16)
-                               _i_ASN_TEXT_INDENT(1, ilevel-1);
+                               ASN__TEXT_INDENT(1, ilevel-1);
                }
        }
 
-       _ASN_ENCODED_OK(er);
+       ASN__ENCODED_OK(er);
 cb_failed:
-       _ASN_ENCODE_FAILED;
+       ASN__ENCODE_FAILED;
 }
 
-static struct OCTET_STRING__xer_escape_table_s {
-       char *string;
+static const struct OCTET_STRING__xer_escape_table_s {
+       const char *string;
        int size;
 } OCTET_STRING__xer_escape_table[] = {
 #define        OSXET(s)        { s, sizeof(s) - 1 }
@@ -708,7 +702,7 @@ OS__check_escaped_control_char(const void *buf, int size) {
         * nested table lookups).
         */
        for(i = 0; i < 32 /* Don't spend time on the bottom half */; i++) {
-               struct OCTET_STRING__xer_escape_table_s *el;
+               const struct OCTET_STRING__xer_escape_table_s *el;
                el = &OCTET_STRING__xer_escape_table[i];
                if(el->size == size && memcmp(buf, el->string, size) == 0)
                        return i;
@@ -751,8 +745,8 @@ OCTET_STRING_encode_xer_utf8(asn_TYPE_descriptor_t *td, void *sptr,
        (void)ilevel;   /* Unused argument */
        (void)flags;    /* Unused argument */
 
-       if(!st || !st->buf)
-               _ASN_ENCODE_FAILED;
+       if(!st || (!st->buf && st->size))
+               ASN__ENCODE_FAILED;
 
        buf = st->buf;
        end = buf + st->size;
@@ -769,7 +763,7 @@ OCTET_STRING_encode_xer_utf8(asn_TYPE_descriptor_t *td, void *sptr,
                        if(((buf - ss) && cb(ss, buf - ss, app_key) < 0)
                        || cb(OCTET_STRING__xer_escape_table[ch].string, s_len,
                                        app_key) < 0)
-                               _ASN_ENCODE_FAILED;
+                               ASN__ENCODE_FAILED;
                        encoded_len += (buf - ss) + s_len;
                        ss = buf + 1;
                }
@@ -777,10 +771,10 @@ OCTET_STRING_encode_xer_utf8(asn_TYPE_descriptor_t *td, void *sptr,
 
        encoded_len += (buf - ss);
        if((buf - ss) && cb(ss, buf - ss, app_key) < 0)
-               _ASN_ENCODE_FAILED;
+               ASN__ENCODE_FAILED;
 
        er.encoded = encoded_len;
-       _ASN_ENCODED_OK(er);
+       ASN__ENCODED_OK(er);
 }
 
 /*
@@ -1197,6 +1191,135 @@ OCTET_STRING_decode_xer_utf8(asn_codec_ctx_t *opt_codec_ctx,
                OCTET_STRING__convert_entrefs);
 }
 
+static int
+OCTET_STRING_per_get_characters(asn_per_data_t *po, uint8_t *buf,
+               size_t units, unsigned int bpc, unsigned int unit_bits,
+               long lb, long ub, asn_per_constraints_t *pc) {
+       uint8_t *end = buf + units * bpc;
+
+       ASN_DEBUG("Expanding %d characters into (%ld..%ld):%d",
+               (int)units, lb, ub, unit_bits);
+
+       /* X.691: 27.5.4 */
+       if((unsigned long)ub <= ((unsigned long)2 << (unit_bits - 1))) {
+               /* Decode without translation */
+               lb = 0;
+       } else if(pc && pc->code2value) {
+               if(unit_bits > 16)
+                       return 1;       /* FATAL: can't have constrained
+                                        * UniversalString with more than
+                                        * 16 million code points */
+               for(; buf < end; buf += bpc) {
+                       int value;
+                       int code = per_get_few_bits(po, unit_bits);
+                       if(code < 0) return -1; /* WMORE */
+                       value = pc->code2value(code);
+                       if(value < 0) {
+                               ASN_DEBUG("Code %d (0x%02x) is"
+                                       " not in map (%ld..%ld)",
+                                       code, code, lb, ub);
+                               return 1;       /* FATAL */
+                       }
+                       switch(bpc) {
+                       case 1: *buf = value; break;
+                       case 2: buf[0] = value >> 8; buf[1] = value; break;
+                       case 4: buf[0] = value >> 24; buf[1] = value >> 16;
+                               buf[2] = value >> 8; buf[3] = value; break;
+                       }
+               }
+               return 0;
+       }
+
+       /* Shortcut the no-op copying to the aligned structure */
+       if(lb == 0 && (unit_bits == 8 * bpc)) {
+               return per_get_many_bits(po, buf, 0, unit_bits * units);
+       }
+
+       for(; buf < end; buf += bpc) {
+               int code = per_get_few_bits(po, unit_bits);
+               int ch = code + lb;
+               if(code < 0) return -1; /* WMORE */
+               if(ch > ub) {
+                       ASN_DEBUG("Code %d is out of range (%ld..%ld)",
+                               ch, lb, ub);
+                       return 1;       /* FATAL */
+               }
+               switch(bpc) {
+               case 1: *buf = ch; break;
+               case 2: buf[0] = ch >> 8; buf[1] = ch; break;
+               case 4: buf[0] = ch >> 24; buf[1] = ch >> 16;
+                       buf[2] = ch >> 8; buf[3] = ch; break;
+               }
+       }
+
+       return 0;
+}
+
+static int
+OCTET_STRING_per_put_characters(asn_per_outp_t *po, const uint8_t *buf,
+               size_t units, unsigned int bpc, unsigned int unit_bits,
+               long lb, long ub, asn_per_constraints_t *pc) {
+       const uint8_t *end = buf + units * bpc;
+
+       ASN_DEBUG("Squeezing %d characters into (%ld..%ld):%d (%d bpc)",
+               (int)units, lb, ub, unit_bits, bpc);
+
+       /* X.691: 27.5.4 */
+       if((unsigned long)ub <= ((unsigned long)2 << (unit_bits - 1))) {
+               /* Encode as is */
+               lb = 0;
+       } else if(pc && pc->value2code) {
+               for(; buf < end; buf += bpc) {
+                       int code;
+                       uint32_t value;
+                       switch(bpc) {
+                       case 1: value = *(const uint8_t *)buf; break;
+                       case 2: value = (buf[0] << 8) | buf[1]; break;
+                       case 4: value = (buf[0] << 24) | (buf[1] << 16)
+                                       | (buf[2] << 8) | buf[3]; break;
+                       default: return -1;
+                       }
+                       code = pc->value2code(value);
+                       if(code < 0) {
+                               ASN_DEBUG("Character %d (0x%02x) is"
+                                       " not in map (%ld..%ld)",
+                                       *buf, *buf, lb, ub);
+                               return -1;
+                       }
+                       if(per_put_few_bits(po, code, unit_bits))
+                               return -1;
+               }
+       }
+
+       /* Shortcut the no-op copying to the aligned structure */
+       if(lb == 0 && (unit_bits == 8 * bpc)) {
+               return per_put_many_bits(po, buf, unit_bits * units);
+       }
+
+       for(ub -= lb; buf < end; buf += bpc) {
+               int ch;
+               uint32_t value;
+               switch(bpc) {
+               case 1: value = *(const uint8_t *)buf; break;
+               case 2: value = (buf[0] << 8) | buf[1]; break;
+               case 4: value = (buf[0] << 24) | (buf[1] << 16)
+                               | (buf[2] << 8) | buf[3]; break;
+               default: return -1;
+               }
+               ch = value - lb;
+               if(ch < 0 || ch > ub) {
+                       ASN_DEBUG("Character %d (0x%02x)"
+                       " is out of range (%ld..%ld)",
+                               *buf, *buf, lb, ub + lb);
+                       return -1;
+               }
+               if(per_put_few_bits(po, ch, unit_bits))
+                       return -1;
+       }
+
+       return 0;
+}
+
 asn_dec_rval_t
 OCTET_STRING_decode_uper(asn_codec_ctx_t *opt_codec_ctx,
        asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints,
@@ -1205,18 +1328,62 @@ OCTET_STRING_decode_uper(asn_codec_ctx_t *opt_codec_ctx,
        asn_OCTET_STRING_specifics_t *specs = td->specifics
                ? (asn_OCTET_STRING_specifics_t *)td->specifics
                : &asn_DEF_OCTET_STRING_specs;
-       asn_per_constraint_t *ct = constraints ? &constraints->size
-                               : (td->per_constraints
-                                       ? &td->per_constraints->size
-                                       : &asn_DEF_OCTET_STRING_constraint);
+       asn_per_constraints_t *pc = constraints ? constraints
+                               : td->per_constraints;
+       asn_per_constraint_t *cval;
+       asn_per_constraint_t *csiz;
        asn_dec_rval_t rval = { RC_OK, 0 };
        BIT_STRING_t *st = (BIT_STRING_t *)*sptr;
        ssize_t consumed_myself = 0;
        int repeat;
-       int unit_bits = (specs->subvariant != 1) * 7 + 1;
+       enum {
+               OS__BPC_BIT     = 0,
+               OS__BPC_CHAR    = 1,
+               OS__BPC_U16     = 2,
+               OS__BPC_U32     = 4
+       } bpc;  /* Bytes per character */
+       unsigned int unit_bits;
+       unsigned int canonical_unit_bits;
 
        (void)opt_codec_ctx;
 
+       if(pc) {
+               cval = &pc->value;
+               csiz = &pc->size;
+       } else {
+               cval = &asn_DEF_OCTET_STRING_constraints.value;
+               csiz = &asn_DEF_OCTET_STRING_constraints.size;
+       }
+
+       switch(specs->subvariant) {
+       default:
+       case ASN_OSUBV_ANY:
+               ASN_DEBUG("Unrecognized subvariant %d", specs->subvariant);
+               RETURN(RC_FAIL);
+       case ASN_OSUBV_BIT:
+               canonical_unit_bits = unit_bits = 1;
+               bpc = OS__BPC_BIT;
+               break;
+       case ASN_OSUBV_STR:
+               canonical_unit_bits = unit_bits = 8;
+               if(cval->flags & APC_CONSTRAINED)
+                       unit_bits = cval->range_bits;
+               bpc = OS__BPC_CHAR;
+               break;
+       case ASN_OSUBV_U16:
+               canonical_unit_bits = unit_bits = 16;
+               if(cval->flags & APC_CONSTRAINED)
+                       unit_bits = cval->range_bits;
+               bpc = OS__BPC_U16;
+               break;
+       case ASN_OSUBV_U32:
+               canonical_unit_bits = unit_bits = 32;
+               if(cval->flags & APC_CONSTRAINED)
+                       unit_bits = cval->range_bits;
+               bpc = OS__BPC_U32;
+               break;
+       }
+
        /*
         * Allocate the string.
         */
@@ -1225,24 +1392,26 @@ OCTET_STRING_decode_uper(asn_codec_ctx_t *opt_codec_ctx,
                if(!st) RETURN(RC_FAIL);
        }
 
-       ASN_DEBUG("PER Decoding %s %ld .. %ld bits %d",
-               ct->flags & APC_EXTENSIBLE ? "extensible" : "fixed",
-               ct->lower_bound, ct->upper_bound, ct->effective_bits);
+       ASN_DEBUG("PER Decoding %s size %ld .. %ld bits %d",
+               csiz->flags & APC_EXTENSIBLE ? "extensible" : "non-extensible",
+               csiz->lower_bound, csiz->upper_bound, csiz->effective_bits);
 
-       if(ct->flags & APC_EXTENSIBLE) {
+       if(csiz->flags & APC_EXTENSIBLE) {
                int inext = per_get_few_bits(pd, 1);
                if(inext < 0) RETURN(RC_WMORE);
-               if(inext) ct = &asn_DEF_OCTET_STRING_constraint;
-               consumed_myself = 0;
+               if(inext) {
+                       csiz = &asn_DEF_OCTET_STRING_constraints.size;
+                       cval = &asn_DEF_OCTET_STRING_constraints.value;
+                       unit_bits = canonical_unit_bits;
+               }
        }
 
-       if(ct->effective_bits >= 0
-       && (!st->buf || st->size < ct->upper_bound)) {
+       if(csiz->effective_bits >= 0) {
                FREEMEM(st->buf);
-               if(unit_bits == 1) {
-                       st->size = (ct->upper_bound + 7) >> 3;
+               if(bpc) {
+                       st->size = csiz->upper_bound * bpc;
                } else {
-                       st->size = ct->upper_bound;
+                       st->size = (csiz->upper_bound + 7) >> 3;
                }
                st->buf = (uint8_t *)MALLOC(st->size + 1);
                if(!st->buf) { st->size = 0; RETURN(RC_FAIL); }
@@ -1251,46 +1420,70 @@ OCTET_STRING_decode_uper(asn_codec_ctx_t *opt_codec_ctx,
        /* X.691, #16.5: zero-length encoding */
        /* X.691, #16.6: short fixed length encoding (up to 2 octets) */
        /* X.691, #16.7: long fixed length encoding (up to 64K octets) */
-       if(ct->effective_bits == 0) {
-               int ret = per_get_many_bits(pd, st->buf, 0,
-                                           unit_bits * ct->upper_bound);
+       if(csiz->effective_bits == 0) {
+               int ret;
+               if(bpc) {
+                       ASN_DEBUG("Encoding OCTET STRING size %ld",
+                               csiz->upper_bound);
+                       ret = OCTET_STRING_per_get_characters(pd, st->buf,
+                               csiz->upper_bound, bpc, unit_bits,
+                               cval->lower_bound, cval->upper_bound, pc);
+                       if(ret > 0) RETURN(RC_FAIL);
+               } else {
+                       ASN_DEBUG("Encoding BIT STRING size %ld",
+                               csiz->upper_bound);
+                       ret = per_get_many_bits(pd, st->buf, 0,
+                                           unit_bits * csiz->upper_bound);
+               }
                if(ret < 0) RETURN(RC_WMORE);
-               consumed_myself += unit_bits * ct->upper_bound;
+               consumed_myself += unit_bits * csiz->upper_bound;
                st->buf[st->size] = 0;
-               if(unit_bits == 1 && (ct->upper_bound & 0x7))
-                       st->bits_unused = 8 - (ct->upper_bound & 0x7);
+               if(bpc == 0) {
+                       int ubs = (csiz->upper_bound & 0x7);
+                       st->bits_unused = ubs ? 8 - ubs : 0;
+               }
                RETURN(RC_OK);
        }
 
        st->size = 0;
        do {
+               ssize_t raw_len;
                ssize_t len_bytes;
                ssize_t len_bits;
                void *p;
                int ret;
 
                /* Get the PER length */
-               len_bits = uper_get_length(pd, ct->effective_bits, &repeat);
-               if(len_bits < 0) RETURN(RC_WMORE);
-               len_bits += ct->lower_bound;
+               raw_len = uper_get_length(pd, csiz->effective_bits, &repeat);
+               if(raw_len < 0) RETURN(RC_WMORE);
+               raw_len += csiz->lower_bound;
 
                ASN_DEBUG("Got PER length eb %ld, len %ld, %s (%s)",
-                       (long)ct->effective_bits, (long)len_bits,
+                       (long)csiz->effective_bits, (long)raw_len,
                        repeat ? "repeat" : "once", td->name);
-               if(unit_bits == 1) {
+               if(bpc) {
+                       len_bytes = raw_len * bpc;
+                       len_bits = len_bytes * unit_bits;
+               } else {
+                       len_bits = raw_len;
                        len_bytes = (len_bits + 7) >> 3;
                        if(len_bits & 0x7)
                                st->bits_unused = 8 - (len_bits & 0x7);
                        /* len_bits be multiple of 16K if repeat is set */
-               } else {
-                       len_bytes = len_bits;
-                       len_bits = len_bytes << 3;
                }
                p = REALLOC(st->buf, st->size + len_bytes + 1);
                if(!p) RETURN(RC_FAIL);
                st->buf = (uint8_t *)p;
 
-               ret = per_get_many_bits(pd, &st->buf[st->size], 0, len_bits);
+               if(bpc) {
+                       ret = OCTET_STRING_per_get_characters(pd,
+                               &st->buf[st->size], raw_len, bpc, unit_bits,
+                               cval->lower_bound, cval->upper_bound, pc);
+                       if(ret > 0) RETURN(RC_FAIL);
+               } else {
+                       ret = per_get_many_bits(pd, &st->buf[st->size],
+                               0, len_bits);
+               }
                if(ret < 0) RETURN(RC_WMORE);
                st->size += len_bytes;
        } while(repeat);
@@ -1306,45 +1499,90 @@ OCTET_STRING_encode_uper(asn_TYPE_descriptor_t *td,
        asn_OCTET_STRING_specifics_t *specs = td->specifics
                ? (asn_OCTET_STRING_specifics_t *)td->specifics
                : &asn_DEF_OCTET_STRING_specs;
-       asn_per_constraint_t *ct = constraints ? &constraints->size
-                               : (td->per_constraints
-                                       ? &td->per_constraints->size
-                                       : &asn_DEF_OCTET_STRING_constraint);
+       asn_per_constraints_t *pc = constraints ? constraints
+                               : td->per_constraints;
+       asn_per_constraint_t *cval;
+       asn_per_constraint_t *csiz;
        const BIT_STRING_t *st = (const BIT_STRING_t *)sptr;
-       int unit_bits = (specs->subvariant != 1) * 7 + 1;
-       asn_enc_rval_t er;
-       int ct_extensible = ct->flags & APC_EXTENSIBLE;
+       asn_enc_rval_t er = { 0, 0, 0 };
        int inext = 0;          /* Lies not within extension root */
-       int sizeinunits = 0;
+       unsigned int unit_bits;
+       unsigned int canonical_unit_bits;
+       unsigned int sizeinunits;
        const uint8_t *buf;
        int ret;
-
-       if(!st || !st->buf)
-               _ASN_ENCODE_FAILED;
-       sizeinunits = st->size;
-
-       if(unit_bits == 1) {
+       enum {
+               OS__BPC_BIT     = 0,
+               OS__BPC_CHAR    = 1,
+               OS__BPC_U16     = 2,
+               OS__BPC_U32     = 4
+       } bpc;  /* Bytes per character */
+       int ct_extensible;
+
+       if(!st || (!st->buf && st->size))
+               ASN__ENCODE_FAILED;
+
+       if(pc) {
+               cval = &pc->value;
+               csiz = &pc->size;
+       } else {
+               cval = &asn_DEF_OCTET_STRING_constraints.value;
+               csiz = &asn_DEF_OCTET_STRING_constraints.size;
+       }
+       ct_extensible = csiz->flags & APC_EXTENSIBLE;
+
+       switch(specs->subvariant) {
+       default:
+       case ASN_OSUBV_ANY:
+               ASN__ENCODE_FAILED;
+       case ASN_OSUBV_BIT:
+               canonical_unit_bits = unit_bits = 1;
+               bpc = OS__BPC_BIT;
+               sizeinunits = st->size * 8 - (st->bits_unused & 0x07);
                ASN_DEBUG("BIT STRING of %d bytes, %d bits unused",
                                sizeinunits, st->bits_unused);
-               sizeinunits = sizeinunits * 8 - (st->bits_unused & 0x07);
+               break;
+       case ASN_OSUBV_STR:
+               canonical_unit_bits = unit_bits = 8;
+               if(cval->flags & APC_CONSTRAINED)
+                       unit_bits = cval->range_bits;
+               bpc = OS__BPC_CHAR;
+               sizeinunits = st->size;
+               break;
+       case ASN_OSUBV_U16:
+               canonical_unit_bits = unit_bits = 16;
+               if(cval->flags & APC_CONSTRAINED)
+                       unit_bits = cval->range_bits;
+               bpc = OS__BPC_U16;
+               sizeinunits = st->size / 2;
+               break;
+       case ASN_OSUBV_U32:
+               canonical_unit_bits = unit_bits = 32;
+               if(cval->flags & APC_CONSTRAINED)
+                       unit_bits = cval->range_bits;
+               bpc = OS__BPC_U32;
+               sizeinunits = st->size / 4;
+               break;
        }
 
        ASN_DEBUG("Encoding %s into %d units of %d bits"
-               " (%d..%d, effective %d)%s",
+               " (%ld..%ld, effective %d)%s",
                td->name, sizeinunits, unit_bits,
-               ct->lower_bound, ct->upper_bound,
-               ct->effective_bits, ct_extensible ? " EXT" : "");
+               csiz->lower_bound, csiz->upper_bound,
+               csiz->effective_bits, ct_extensible ? " EXT" : "");
 
-       /* Figure out wheter size lies within PER visible consrtaint */
+       /* Figure out whether size lies within PER visible constraint */
 
-       if(ct->effective_bits >= 0) {
-               if(sizeinunits < ct->lower_bound
-               || sizeinunits > ct->upper_bound) {
+       if(csiz->effective_bits >= 0) {
+               if((int)sizeinunits < csiz->lower_bound
+               || (int)sizeinunits > csiz->upper_bound) {
                        if(ct_extensible) {
-                               ct = &asn_DEF_OCTET_STRING_constraint;
+                               cval = &asn_DEF_OCTET_STRING_constraints.value;
+                               csiz = &asn_DEF_OCTET_STRING_constraints.size;
+                               unit_bits = canonical_unit_bits;
                                inext = 1;
                        } else
-                               _ASN_ENCODE_FAILED;
+                               ASN__ENCODE_FAILED;
                }
        } else {
                inext = 0;
@@ -1353,57 +1591,71 @@ OCTET_STRING_encode_uper(asn_TYPE_descriptor_t *td,
        if(ct_extensible) {
                /* Declare whether length is [not] within extension root */
                if(per_put_few_bits(po, inext, 1))
-                       _ASN_ENCODE_FAILED;
+                       ASN__ENCODE_FAILED;
        }
 
        /* X.691, #16.5: zero-length encoding */
        /* X.691, #16.6: short fixed length encoding (up to 2 octets) */
        /* X.691, #16.7: long fixed length encoding (up to 64K octets) */
-       if(ct->effective_bits >= 0) {
+       if(csiz->effective_bits >= 0) {
                ASN_DEBUG("Encoding %d bytes (%ld), length in %d bits",
-                               st->size, sizeinunits - ct->lower_bound,
-                               ct->effective_bits);
-               ret = per_put_few_bits(po, sizeinunits - ct->lower_bound,
-                               ct->effective_bits);
-               if(ret) _ASN_ENCODE_FAILED;
-               ret = per_put_many_bits(po, st->buf, sizeinunits * unit_bits);
-               if(ret) _ASN_ENCODE_FAILED;
-               _ASN_ENCODED_OK(er);
+                               st->size, sizeinunits - csiz->lower_bound,
+                               csiz->effective_bits);
+               ret = per_put_few_bits(po, sizeinunits - csiz->lower_bound,
+                               csiz->effective_bits);
+               if(ret) ASN__ENCODE_FAILED;
+               if(bpc) {
+                       ret = OCTET_STRING_per_put_characters(po, st->buf,
+                               sizeinunits, bpc, unit_bits,
+                               cval->lower_bound, cval->upper_bound, pc);
+               } else {
+                       ret = per_put_many_bits(po, st->buf,
+                               sizeinunits * unit_bits);
+               }
+               if(ret) ASN__ENCODE_FAILED;
+               ASN__ENCODED_OK(er);
        }
 
        ASN_DEBUG("Encoding %d bytes", st->size);
 
        if(sizeinunits == 0) {
                if(uper_put_length(po, 0))
-                       _ASN_ENCODE_FAILED;
-               _ASN_ENCODED_OK(er);
+                       ASN__ENCODE_FAILED;
+               ASN__ENCODED_OK(er);
        }
 
        buf = st->buf;
        while(sizeinunits) {
                ssize_t maySave = uper_put_length(po, sizeinunits);
-               if(maySave < 0) _ASN_ENCODE_FAILED;
+               if(maySave < 0) ASN__ENCODE_FAILED;
 
-               ASN_DEBUG("Encoding %d of %d", maySave, sizeinunits);
+               ASN_DEBUG("Encoding %ld of %ld",
+                       (long)maySave, (long)sizeinunits);
 
-               ret = per_put_many_bits(po, buf, maySave * unit_bits);
-               if(ret) _ASN_ENCODE_FAILED;
+               if(bpc) {
+                       ret = OCTET_STRING_per_put_characters(po, buf,
+                               maySave, bpc, unit_bits,
+                               cval->lower_bound, cval->upper_bound, pc);
+               } else {
+                       ret = per_put_many_bits(po, buf, maySave * unit_bits);
+               }
+               if(ret) ASN__ENCODE_FAILED;
 
-               if(unit_bits == 1)
-                       buf += maySave >> 3;
+               if(bpc)
+                       buf += maySave * bpc;
                else
-                       buf += maySave;
+                       buf += maySave >> 3;
                sizeinunits -= maySave;
                assert(!(maySave & 0x07) || !sizeinunits);
        }
 
-       _ASN_ENCODED_OK(er);
+       ASN__ENCODED_OK(er);
 }
 
 int
 OCTET_STRING_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
        asn_app_consume_bytes_f *cb, void *app_key) {
-       static const char *h2c = "0123456789ABCDEF";
+       const char * const h2c = "0123456789ABCDEF";
        const OCTET_STRING_t *st = (const OCTET_STRING_t *)sptr;
        char scratch[16 * 3 + 4];
        char *p = scratch;
@@ -1413,7 +1665,8 @@ OCTET_STRING_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
 
        (void)td;       /* Unused argument */
 
-       if(!st || !st->buf) return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
+       if(!st || (!st->buf && st->size))
+               return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
 
        /*
         * Dump the contents of the buffer in hexadecimal.
@@ -1449,7 +1702,7 @@ OCTET_STRING_print_utf8(asn_TYPE_descriptor_t *td, const void *sptr,
        (void)td;       /* Unused argument */
        (void)ilevel;   /* Unused argument */
 
-       if(st && st->buf) {
+       if(st && (st->buf || !st->size)) {
                return (cb(st->buf, st->size, app_key) < 0) ? -1 : 0;
        } else {
                return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
@@ -1459,20 +1712,23 @@ OCTET_STRING_print_utf8(asn_TYPE_descriptor_t *td, const void *sptr,
 void
 OCTET_STRING_free(asn_TYPE_descriptor_t *td, void *sptr, int contents_only) {
        OCTET_STRING_t *st = (OCTET_STRING_t *)sptr;
-       asn_OCTET_STRING_specifics_t *specs = td && td->specifics
-                               ? (asn_OCTET_STRING_specifics_t *)td->specifics
-                               : &asn_DEF_OCTET_STRING_specs;
-       asn_struct_ctx_t *ctx = (asn_struct_ctx_t *)
-                                       ((char *)st + specs->ctx_offset);
+       asn_OCTET_STRING_specifics_t *specs;
+       asn_struct_ctx_t *ctx;
        struct _stack *stck;
 
        if(!td || !st)
                return;
 
+       specs = td->specifics
+                   ? (asn_OCTET_STRING_specifics_t *)td->specifics
+                   : &asn_DEF_OCTET_STRING_specs;
+       ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset);
+
        ASN_DEBUG("Freeing %s as OCTET STRING", td->name);
 
        if(st->buf) {
                FREEMEM(st->buf);
+               st->buf = 0;
        }
 
        /*