moved out common bit manipulation code
diff --git a/skeletons/Makefile.am b/skeletons/Makefile.am
index dedd0c6..1af93c2 100644
--- a/skeletons/Makefile.am
+++ b/skeletons/Makefile.am
@@ -59,6 +59,7 @@
 	asn_application.h asn_codecs.h			\
 	asn_codecs_prim.c asn_codecs_prim.h		\
 	asn_internal.h asn_system.h			\
+	asn_bit_data.c asn_bit_data.h			\
 	ber_decoder.c ber_decoder.h			\
 	ber_tlv_length.c ber_tlv_length.h		\
 	ber_tlv_tag.c ber_tlv_tag.h			\
diff --git a/skeletons/OPEN_TYPE_oer.c b/skeletons/OPEN_TYPE_oer.c
new file mode 100644
index 0000000..3de9574
--- /dev/null
+++ b/skeletons/OPEN_TYPE_oer.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <OPEN_TYPE.h>
+#include <constr_CHOICE.h>
+#include <oer_opentype.h>
+#include <errno.h>
+
+asn_dec_rval_t
+OPEN_TYPE_oer_get(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
+                  void *sptr, asn_TYPE_member_t *elm, const void *ptr,
+                  size_t size) {
+    asn_type_selector_result_t selected;
+    void *memb_ptr;   /* Pointer to the member */
+    void **memb_ptr2; /* Pointer to that pointer */
+    void *inner_value;
+    asn_dec_rval_t rv;
+    size_t ot_ret;
+
+    if(!(elm->flags & ATF_OPEN_TYPE) || !elm->type_selector) {
+        ASN__DECODE_FAILED;
+    }
+
+    selected = elm->type_selector(td, sptr);
+    if(!selected.presence_index) {
+        ASN__DECODE_FAILED;
+    }
+
+    /* Fetch the pointer to this member */
+    if(elm->flags & ATF_POINTER) {
+        memb_ptr2 = (void **)((char *)sptr + elm->memb_offset);
+    } else {
+        memb_ptr = (char *)sptr + elm->memb_offset;
+        memb_ptr2 = &memb_ptr;
+    }
+    if(*memb_ptr2 != NULL) {
+        /* Make sure we reset the structure first before encoding */
+        if(CHOICE_variant_set_presence(selected.type_descriptor, *memb_ptr2, 0)
+           != 0) {
+            ASN__DECODE_FAILED;
+        }
+    }
+
+    inner_value =
+        (char *)*memb_ptr2
+        + elm->type->elements[selected.presence_index - 1].memb_offset;
+
+    ot_ret = oer_open_type_get(opt_codec_ctx, selected.type_descriptor, NULL,
+                               &inner_value, ptr, size);
+    switch(ot_ret) {
+    default:
+        if(CHOICE_variant_set_presence(selected.type_descriptor, *memb_ptr2,
+                                       selected.presence_index)
+           == 0) {
+            rv.code = RC_OK;
+            rv.consumed = ot_ret;
+            return rv;
+        } else {
+            /* Oh, now a full-blown failure failure */
+        }
+        /* Fall through */
+    case -1:
+        rv.code = RC_FAIL;
+        rv.consumed = 0;
+        break;
+    case 0:
+        rv.code = RC_WMORE;
+        rv.consumed = 0;
+        break;
+    }
+
+    if(*memb_ptr2) {
+        asn_CHOICE_specifics_t *specs = selected.type_descriptor->specifics;
+        if(elm->flags & ATF_POINTER) {
+            ASN_STRUCT_FREE(*selected.type_descriptor, inner_value);
+            *memb_ptr2 = NULL;
+        } else {
+            ASN_STRUCT_FREE_CONTENTS_ONLY(*selected.type_descriptor,
+                                          inner_value);
+            memset(*memb_ptr2, 0, specs->struct_size);
+        }
+    }
+    return rv;
+}
diff --git a/skeletons/asn_bit_data.c b/skeletons/asn_bit_data.c
new file mode 100644
index 0000000..2852c2d
--- /dev/null
+++ b/skeletons/asn_bit_data.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2005-2017 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_system.h>
+#include <asn_internal.h>
+#include <asn_bit_data.h>
+
+char *
+asn_bit_data_string(asn_bit_data_t *pd) {
+	static char buf[2][32];
+	static int n;
+	n = (n+1) % 2;
+	snprintf(buf[n], sizeof(buf[n]),
+		"{m=%ld span %+ld[%d..%d] (%d)}",
+		(long)pd->moved,
+		(((long)pd->buffer) & 0xf),
+		(int)pd->nboff, (int)pd->nbits,
+		(int)(pd->nbits - pd->nboff));
+	return buf[n];
+}
+
+void
+asn_get_undo(asn_bit_data_t *pd, int nbits) {
+	if((ssize_t)pd->nboff < nbits) {
+		assert((ssize_t)pd->nboff < nbits);
+	} else {
+		pd->nboff -= nbits;
+		pd->moved -= nbits;
+	}
+}
+
+/*
+ * Extract a small number of bits (<= 31) from the specified PER data pointer.
+ */
+int32_t
+asn_get_few_bits(asn_bit_data_t *pd, int nbits) {
+	size_t off;	/* Next after last bit offset */
+	ssize_t nleft;	/* Number of bits left in this stream */
+	uint32_t accum;
+	const uint8_t *buf;
+
+	if(nbits < 0)
+		return -1;
+
+	nleft = pd->nbits - pd->nboff;
+	if(nbits > nleft) {
+		int32_t tailv, vhead;
+		if(!pd->refill || nbits > 31) return -1;
+		/* Accumulate unused bytes before refill */
+		ASN_DEBUG("Obtain the rest %d bits (want %d)",
+			(int)nleft, (int)nbits);
+		tailv = asn_get_few_bits(pd, nleft);
+		if(tailv < 0) return -1;
+		/* Refill (replace pd contents with new data) */
+		if(pd->refill(pd))
+			return -1;
+		nbits -= nleft;
+		vhead = asn_get_few_bits(pd, nbits);
+		/* Combine the rest of previous pd with the head of new one */
+		tailv = (tailv << nbits) | vhead;  /* Could == -1 */
+		return tailv;
+	}
+
+	/*
+	 * Normalize position indicator.
+	 */
+	if(pd->nboff >= 8) {
+		pd->buffer += (pd->nboff >> 3);
+		pd->nbits  -= (pd->nboff & ~0x07);
+		pd->nboff  &= 0x07;
+	}
+	pd->moved += nbits;
+	pd->nboff += nbits;
+	off = pd->nboff;
+	buf = pd->buffer;
+
+	/*
+	 * Extract specified number of bits.
+	 */
+	if(off <= 8)
+		accum = nbits ? (buf[0]) >> (8 - off) : 0;
+	else if(off <= 16)
+		accum = ((buf[0] << 8) + buf[1]) >> (16 - off);
+	else if(off <= 24)
+		accum = ((buf[0] << 16) + (buf[1] << 8) + buf[2]) >> (24 - off);
+	else if(off <= 31)
+		accum = ((buf[0] << 24) + (buf[1] << 16)
+			+ (buf[2] << 8) + (buf[3])) >> (32 - off);
+	else if(nbits <= 31) {
+		asn_bit_data_t tpd = *pd;
+		/* Here are we with our 31-bits limit plus 1..7 bits offset. */
+		asn_get_undo(&tpd, nbits);
+		/* The number of available bits in the stream allow
+		 * for the following operations to take place without
+		 * invoking the ->refill() function */
+		accum  = asn_get_few_bits(&tpd, nbits - 24) << 24;
+		accum |= asn_get_few_bits(&tpd, 24);
+	} else {
+		asn_get_undo(pd, nbits);
+		return -1;
+	}
+
+	accum &= (((uint32_t)1 << nbits) - 1);
+
+	ASN_DEBUG("  [PER got %2d<=%2d bits => span %d %+ld[%d..%d]:%02x (%d) => 0x%x]",
+		(int)nbits, (int)nleft,
+		(int)pd->moved,
+		(((long)pd->buffer) & 0xf),
+		(int)pd->nboff, (int)pd->nbits,
+		((pd->buffer != NULL)?pd->buffer[0]:0),
+		(int)(pd->nbits - pd->nboff),
+		(int)accum);
+
+	return accum;
+}
+
+/*
+ * Extract a large number of bits from the specified PER data pointer.
+ */
+int
+asn_get_many_bits(asn_bit_data_t *pd, uint8_t *dst, int alright, int nbits) {
+	int32_t value;
+
+	if(alright && (nbits & 7)) {
+		/* Perform right alignment of a first few bits */
+		value = asn_get_few_bits(pd, nbits & 0x07);
+		if(value < 0) return -1;
+		*dst++ = value;	/* value is already right-aligned */
+		nbits &= ~7;
+	}
+
+	while(nbits) {
+		if(nbits >= 24) {
+			value = asn_get_few_bits(pd, 24);
+			if(value < 0) return -1;
+			*(dst++) = value >> 16;
+			*(dst++) = value >> 8;
+			*(dst++) = value;
+			nbits -= 24;
+		} else {
+			value = asn_get_few_bits(pd, nbits);
+			if(value < 0) return -1;
+			if(nbits & 7) {	/* implies left alignment */
+				value <<= 8 - (nbits & 7),
+				nbits += 8 - (nbits & 7);
+				if(nbits > 24)
+					*dst++ = value >> 24;
+			}
+			if(nbits > 16)
+				*dst++ = value >> 16;
+			if(nbits > 8)
+				*dst++ = value >> 8;
+			*dst++ = value;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Put a small number of bits (<= 31).
+ */
+int
+asn_put_few_bits(asn_bit_outp_t *po, uint32_t bits, int obits) {
+	size_t off;	/* Next after last bit offset */
+	size_t omsk;	/* Existing last byte meaningful bits mask */
+	uint8_t *buf;
+
+	if(obits <= 0 || obits >= 32) return obits ? -1 : 0;
+
+	ASN_DEBUG("[PER put %d bits %x to %p+%d bits]",
+			obits, (int)bits, po->buffer, (int)po->nboff);
+
+	/*
+	 * Normalize position indicator.
+	 */
+	if(po->nboff >= 8) {
+		po->buffer += (po->nboff >> 3);
+		po->nbits  -= (po->nboff & ~0x07);
+		po->nboff  &= 0x07;
+	}
+
+	/*
+	 * Flush whole-bytes output, if necessary.
+	 */
+	if(po->nboff + obits > po->nbits) {
+		size_t complete_bytes;
+		if(!po->buffer) po->buffer = po->tmpspace;
+		complete_bytes = (po->buffer - po->tmpspace);
+		ASN_DEBUG("[PER output %ld complete + %ld]",
+			(long)complete_bytes, (long)po->flushed_bytes);
+		if(po->output(po->tmpspace, complete_bytes, po->op_key) < 0)
+			return -1;
+		if(po->nboff)
+			po->tmpspace[0] = po->buffer[0];
+		po->buffer = po->tmpspace;
+		po->nbits = 8 * sizeof(po->tmpspace);
+		po->flushed_bytes += complete_bytes;
+	}
+
+	/*
+	 * Now, due to sizeof(tmpspace), we are guaranteed large enough space.
+	 */
+	buf = po->buffer;
+	omsk = ~((1 << (8 - po->nboff)) - 1);
+	off = (po->nboff + obits);
+
+	/* Clear data of debris before meaningful bits */
+	bits &= (((uint32_t)1 << obits) - 1);
+
+	ASN_DEBUG("[PER out %d %u/%x (t=%d,o=%d) %x&%x=%x]", obits,
+		(int)bits, (int)bits,
+		(int)po->nboff, (int)off,
+		buf[0], (int)(omsk&0xff),
+		(int)(buf[0] & omsk));
+
+	if(off <= 8)	/* Completely within 1 byte */
+		po->nboff = off,
+		bits <<= (8 - off),
+		buf[0] = (buf[0] & omsk) | bits;
+	else if(off <= 16)
+		po->nboff = off,
+		bits <<= (16 - off),
+		buf[0] = (buf[0] & omsk) | (bits >> 8),
+		buf[1] = bits;
+	else if(off <= 24)
+		po->nboff = off,
+		bits <<= (24 - off),
+		buf[0] = (buf[0] & omsk) | (bits >> 16),
+		buf[1] = bits >> 8,
+		buf[2] = bits;
+	else if(off <= 31)
+		po->nboff = off,
+		bits <<= (32 - off),
+		buf[0] = (buf[0] & omsk) | (bits >> 24),
+		buf[1] = bits >> 16,
+		buf[2] = bits >> 8,
+		buf[3] = bits;
+	else {
+		if(asn_put_few_bits(po, bits >> (obits - 24), 24)) return -1;
+		if(asn_put_few_bits(po, bits, obits - 24)) return -1;
+	}
+
+	ASN_DEBUG("[PER out %u/%x => %02x buf+%ld]",
+		(int)bits, (int)bits, buf[0],
+		(long)(po->buffer - po->tmpspace));
+
+	return 0;
+}
+
+
+/*
+ * Output a large number of bits.
+ */
+int
+asn_put_many_bits(asn_bit_outp_t *po, const uint8_t *src, int nbits) {
+
+	while(nbits) {
+		uint32_t value;
+
+		if(nbits >= 24) {
+			value = (src[0] << 16) | (src[1] << 8) | src[2];
+			src += 3;
+			nbits -= 24;
+			if(asn_put_few_bits(po, value, 24))
+				return -1;
+		} else {
+			value = src[0];
+			if(nbits > 8)
+				value = (value << 8) | src[1];
+			if(nbits > 16)
+				value = (value << 8) | src[2];
+			if(nbits & 0x07)
+				value >>= (8 - (nbits & 0x07));
+			if(asn_put_few_bits(po, value, nbits))
+				return -1;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+
+int
+asn_put_aligned_flush(asn_bit_outp_t *po) {
+    uint32_t unused_bits = (0x7 & (8 - (po->nboff & 0x07)));
+    size_t complete_bytes =
+        (po->buffer ? po->buffer - po->tmpspace : 0) + ((po->nboff + 7) >> 3);
+
+    if(unused_bits) {
+        po->buffer[po->nboff >> 3] &= ~0 << unused_bits;
+    }
+
+    if(po->output(po->tmpspace, complete_bytes, po->op_key) < 0) {
+        return -1;
+    } else {
+        po->buffer = po->tmpspace;
+        po->nboff = 0;
+        po->nbits = 8 * sizeof(po->tmpspace);
+        po->flushed_bytes += complete_bytes;
+        return 0;
+    }
+}
+
diff --git a/skeletons/asn_bit_data.h b/skeletons/asn_bit_data.h
new file mode 100644
index 0000000..750522b
--- /dev/null
+++ b/skeletons/asn_bit_data.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2005-2017 Lev Walkin <vlm@lionet.info>.
+ * All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef	ASN_BIT_DATA
+#define	ASN_BIT_DATA
+
+#include <asn_system.h>		/* Platform-specific types */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This structure describes a position inside an incoming PER bit stream.
+ */
+typedef struct asn_bit_data_s {
+  const uint8_t *buffer;  /* Pointer to the octet stream */
+         size_t  nboff;   /* Bit offset to the meaningful bit */
+         size_t  nbits;   /* Number of bits in the stream */
+         size_t  moved;   /* Number of bits moved through this bit stream */
+  int (*refill)(struct asn_bit_data_s *);
+  void *refill_key;
+} asn_bit_data_t;
+
+/*
+ * Extract a small number of bits (<= 31) from the specified PER data pointer.
+ * This function returns -1 if the specified number of bits could not be
+ * extracted due to EOD or other conditions.
+ */
+int32_t asn_get_few_bits(asn_bit_data_t *, int get_nbits);
+
+/* Undo the immediately preceeding "get_few_bits" operation */
+void asn_get_undo(asn_bit_data_t *, int get_nbits);
+
+/*
+ * Extract a large number of bits from the specified PER data pointer.
+ * This function returns -1 if the specified number of bits could not be
+ * extracted due to EOD or other conditions.
+ */
+int asn_get_many_bits(asn_bit_data_t *, uint8_t *dst, int right_align,
+			int get_nbits);
+
+/* Non-thread-safe debugging function, don't use it */
+char *asn_bit_data_string(asn_bit_data_t *);
+
+/*
+ * This structure supports forming bit output.
+ */
+typedef struct asn_bit_outp_s {
+	uint8_t *buffer;	/* Pointer into the (tmpspace) */
+	size_t nboff;		/* Bit offset to the meaningful bit */
+	size_t nbits;		/* Number of bits left in (tmpspace) */
+	uint8_t tmpspace[32];	/* Preliminary storage to hold data */
+	int (*output)(const void *data, size_t size, void *op_key);
+	void *op_key;		/* Key for (output) data callback */
+	size_t flushed_bytes;	/* Bytes already flushed through (output) */
+} asn_bit_outp_t;
+
+/* Output a small number of bits (<= 31) */
+int asn_put_few_bits(asn_bit_outp_t *, uint32_t bits, int obits);
+
+/* Output a large number of bits */
+int asn_put_many_bits(asn_bit_outp_t *, const uint8_t *src, int put_nbits);
+
+/*
+ * Flush whole bytes (0 or more) through (outper) member.
+ * The least significant bits which are not used are guaranteed to be set to 0.
+ * Returns -1 if callback returns -1. Otherwise, 0.
+ */
+int asn_put_aligned_flush(asn_bit_outp_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* ASN_BIT_DATA */
diff --git a/skeletons/constr_SEQUENCE_oer.c b/skeletons/constr_SEQUENCE_oer.c
index 91e4ac3..fa3ca31 100644
--- a/skeletons/constr_SEQUENCE_oer.c
+++ b/skeletons/constr_SEQUENCE_oer.c
@@ -98,7 +98,7 @@
      */
     ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset);
     if(ctx->ptr == 0) {
-        ctx->ptr = CALLOC(1, sizeof(asn_per_data_t));
+        ctx->ptr = CALLOC(1, sizeof(asn_bit_data_t));
         if(!ctx->ptr) {
             RETURN(RC_FAIL);
         }
@@ -112,7 +112,7 @@
         /*
          * Fetch preamble.
          */
-        asn_per_data_t *preamble = ctx->ptr;
+        asn_bit_data_t *preamble = ctx->ptr;
         int has_extensions_bit = (specs->ext_before >= 0);
         size_t preamble_bits = (has_extensions_bit + specs->roms_count);
         size_t preamble_bytes = ((7 + preamble_bits) >> 3);
@@ -143,7 +143,7 @@
         /* FALL THROUGH */
     case 1: {
         /* Decode components of the extension root */
-        asn_per_data_t *preamble = ctx->ptr;
+        asn_bit_data_t *preamble = ctx->ptr;
         size_t edx;
 
         ASN_DEBUG("OER SEQUENCE %s Decoding PHASE 1", td->name);
@@ -165,7 +165,7 @@
             }
 
             if(elm->optional) {
-                int32_t present = per_get_few_bits(preamble, 1);
+                int32_t present = asn_get_few_bits(preamble, 1);
                 if(present < 0) {
                     ASN_DEBUG("Presence map ended prematurely: %d", present);
                     RETURN(RC_FAIL);
@@ -226,8 +226,8 @@
         /* FALL THROUGH */
     case 2: {
         /* Cleanup preamble. */
-        asn_per_data_t *preamble = ctx->ptr;
-        asn_per_data_t *extadds;
+        asn_bit_data_t *preamble = ctx->ptr;
+        asn_bit_data_t *extadds;
         int has_extensions_bit = (specs->ext_before >= 0);
         int extensions_present =
             has_extensions_bit && (((const uint8_t *)preamble->buffer)[0] & 0x80);
@@ -303,12 +303,12 @@
     case 3:
         ASN_DEBUG("OER SEQUENCE %s Decoding PHASE 3", td->name);
         for(; ctx->step < specs->ext_before - 1; ctx->step++) {
-            asn_per_data_t *extadds = ctx->ptr;
+            asn_bit_data_t *extadds = ctx->ptr;
             size_t edx = ctx->step;
             asn_TYPE_member_t *elm = &td->elements[edx];
             void **memb_ptr2 = element_ptrptr(st, elm);
 
-            switch(per_get_few_bits(extadds, 1)) {
+            switch(asn_get_few_bits(extadds, 1)) {
             case -1:
                 /*
                  * Not every one of our extensions is known to the remote side.
@@ -332,7 +332,7 @@
                     RETURN(RC_FAIL);
                 } else {
                     /* Roll back open type parsing */
-                    per_get_undo(extadds, 1);
+                    asn_get_undo(extadds, 1);
                     ASN_STRUCT_FREE(*elm->type, *memb_ptr2);
                     *memb_ptr2 = NULL;
                     RETURN(RC_WMORE);
@@ -350,8 +350,8 @@
         ASN_DEBUG("OER SEQUENCE %s Decoding PHASE 4", td->name);
         /* Read in the rest of Open Types while ignoring them */
         for(;;) {
-            asn_per_data_t *extadds = ctx->ptr;
-            switch(per_get_few_bits(extadds, 1)) {
+            asn_bit_data_t *extadds = ctx->ptr;
+            switch(asn_get_few_bits(extadds, 1)) {
             case 0:
                 continue;
             case 1: {
@@ -361,7 +361,7 @@
                 } else if(skipped < 0) {
                     RETURN(RC_FAIL);
                 } else {
-                    per_get_undo(extadds, 1);
+                    asn_get_undo(extadds, 1);
                     RETURN(RC_WMORE);
                 }
                 continue;
@@ -397,10 +397,10 @@
     (void)constraints;
 
     if(preamble_bits) {
-        asn_per_outp_t preamble;
+        asn_bit_outp_t preamble;
 
         memset(&preamble, 0, sizeof(preamble));
-        preamble.outper = cb;
+        preamble.output = cb;
         preamble.op_key = app_key;
 
         if(has_extensions_bit) {
@@ -412,7 +412,7 @@
                     break;
                 }
             }
-            ret = per_put_few_bits(&preamble, has_extensions, 1);
+            ret = asn_put_few_bits(&preamble, has_extensions, 1);
             assert(ret == 0);
             if(ret < 0) {
                 ASN__ENCODE_FAILED;
@@ -430,7 +430,7 @@
 
                 if(elm->optional) {
                     uint32_t has_component = (element_ptr(sptr, elm) != NULL);
-                    ret = per_put_few_bits(&preamble, has_component, 1);
+                    ret = asn_put_few_bits(&preamble, has_component, 1);
                     if(ret < 0) {
                         ASN__ENCODE_FAILED;
                     }
@@ -438,7 +438,7 @@
             }
         }
 
-        per_put_aligned_flush(&preamble);
+        asn_put_aligned_flush(&preamble);
         computed_size += preamble.flushed_bytes;
     }   /* if(preamble_bits) */
 
@@ -477,7 +477,7 @@
      # X.696 (08/2015) #16.4.
      */
     if(has_extensions) {
-        asn_per_outp_t extadds;
+        asn_bit_outp_t extadds;
 
         /* Special case allowing us to use exactly one byte for #8.6 */
         size_t aoms_length_bits = specs->aoms_count;
@@ -487,15 +487,15 @@
         assert(1 + aoms_length_bytes <= 127);
 
         memset(&extadds, 0, sizeof(extadds));
-        extadds.outper = cb;
+        extadds.output = cb;
         extadds.op_key = app_key;
 
         /* #8.6 length determinant */
-        ret = per_put_few_bits(&extadds, (1 + aoms_length_bytes), 8);
+        ret = asn_put_few_bits(&extadds, (1 + aoms_length_bytes), 8);
         if(ret < 0) ASN__ENCODE_FAILED;
 
         /* Number of unused bytes, #16.4.2 */
-        ret = per_put_few_bits(&extadds, unused_bits, 8);
+        ret = asn_put_few_bits(&extadds, unused_bits, 8);
         if(ret < 0) ASN__ENCODE_FAILED;
 
         /* Encode presence bitmap #16.4.3 */
@@ -503,11 +503,11 @@
             edx++) {
             asn_TYPE_member_t *elm = &td->elements[edx];
             void *memb_ptr = element_ptr(sptr, elm);
-            ret |= per_put_few_bits(&extadds, memb_ptr ? 1 : 0, 1);
+            ret |= asn_put_few_bits(&extadds, memb_ptr ? 1 : 0, 1);
         }
         if(ret < 0) ASN__ENCODE_FAILED;
 
-        per_put_aligned_flush(&extadds);
+        asn_put_aligned_flush(&extadds);
         computed_size += extadds.flushed_bytes;
 
         /* Now, encode extensions */
diff --git a/skeletons/file-dependencies b/skeletons/file-dependencies
index 10022f5..1a2c79b 100644
--- a/skeletons/file-dependencies
+++ b/skeletons/file-dependencies
@@ -46,6 +46,7 @@
 asn_system.h			# Platform-dependent types
 asn_codecs.h			# Return types of encoders and decoders
 asn_internal.h			# Internal stuff
+asn_bit_data.h asn_bit_data.c         # Bit streaming support
 OCTET_STRING.h OCTET_STRING.c	# This one is used too widely
 BIT_STRING.h BIT_STRING.c	# This one is necessary for the above one
 asn_codecs_prim.c asn_codecs_prim.h	# enc/decoders for primitive types
diff --git a/skeletons/per_encoder.c b/skeletons/per_encoder.c
index 5e3a77a..adee4e6 100644
--- a/skeletons/per_encoder.c
+++ b/skeletons/per_encoder.c
@@ -114,7 +114,7 @@
 		buf++;
 	}
 
-	return po->outper(po->tmpspace, buf - po->tmpspace, po->op_key);
+	return po->output(po->tmpspace, buf - po->tmpspace, po->op_key);
 }
 
 static asn_enc_rval_t
@@ -133,7 +133,7 @@
 	po.buffer = po.tmpspace;
 	po.nboff = 0;
 	po.nbits = 8 * sizeof(po.tmpspace);
-	po.outper = cb;
+	po.output = cb;
 	po.op_key = app_key;
 	po.flushed_bytes = 0;
 
diff --git a/skeletons/per_opentype.c b/skeletons/per_opentype.c
index 2ccaf39..8ed0933 100644
--- a/skeletons/per_opentype.c
+++ b/skeletons/per_opentype.c
@@ -144,7 +144,7 @@
 	ASN__STACK_OVERFLOW_CHECK(ctx);
 
 	ASN_DEBUG("Getting open type %s from %s", td->name,
-		per_data_string(pd));
+		asn_bit_data_string(pd));
 	arg.oldpd = *pd;
 	arg.unclaimed = 0;
 	arg.ot_moved = 0;
@@ -172,8 +172,8 @@
 	}
 
 	ASN_DEBUG("OpenType %s pd%s old%s unclaimed=%d, repeat=%d", td->name,
-		per_data_string(pd),
-		per_data_string(&arg.oldpd),
+		asn_bit_data_string(pd),
+		asn_bit_data_string(&arg.oldpd),
 		(int)arg.unclaimed, (int)arg.repeat);
 
 	padding = pd->moved % 8;
@@ -204,7 +204,7 @@
 	}
 	if(pd->nboff != pd->nbits) {
 		ASN_DEBUG("Open type %s overhead pd%s old%s", td->name,
-			per_data_string(pd), per_data_string(&arg.oldpd));
+			asn_bit_data_string(pd), asn_bit_data_string(&arg.oldpd));
 		if(1) {
 			UPDRESTOREPD;
 			ASN__DECODE_FAILED;
@@ -354,7 +354,7 @@
 	pd->buffer = oldpd->buffer;
 	pd->nboff = oldpd->nboff;
 	ASN_DEBUG("Refilled pd%s old%s",
-		per_data_string(pd), per_data_string(oldpd));
+		asn_bit_data_string(pd), asn_bit_data_string(oldpd));
 	return 0;
 }
 
diff --git a/skeletons/per_support.c b/skeletons/per_support.c
index 9153e58..f2c9013 100644
--- a/skeletons/per_support.c
+++ b/skeletons/per_support.c
@@ -7,159 +7,6 @@
 #include <asn_internal.h>
 #include <per_support.h>
 
-char *
-per_data_string(asn_per_data_t *pd) {
-	static char buf[2][32];
-	static int n;
-	n = (n+1) % 2;
-	snprintf(buf[n], sizeof(buf[n]),
-		"{m=%ld span %+ld[%d..%d] (%d)}",
-		(long)pd->moved,
-		(((long)pd->buffer) & 0xf),
-		(int)pd->nboff, (int)pd->nbits,
-		(int)(pd->nbits - pd->nboff));
-	return buf[n];
-}
-
-void
-per_get_undo(asn_per_data_t *pd, int nbits) {
-	if((ssize_t)pd->nboff < nbits) {
-		assert((ssize_t)pd->nboff < nbits);
-	} else {
-		pd->nboff -= nbits;
-		pd->moved -= nbits;
-	}
-}
-
-/*
- * Extract a small number of bits (<= 31) from the specified PER data pointer.
- */
-int32_t
-per_get_few_bits(asn_per_data_t *pd, int nbits) {
-	size_t off;	/* Next after last bit offset */
-	ssize_t nleft;	/* Number of bits left in this stream */
-	uint32_t accum;
-	const uint8_t *buf;
-
-	if(nbits < 0)
-		return -1;
-
-	nleft = pd->nbits - pd->nboff;
-	if(nbits > nleft) {
-		int32_t tailv, vhead;
-		if(!pd->refill || nbits > 31) return -1;
-		/* Accumulate unused bytes before refill */
-		ASN_DEBUG("Obtain the rest %d bits (want %d)",
-			(int)nleft, (int)nbits);
-		tailv = per_get_few_bits(pd, nleft);
-		if(tailv < 0) return -1;
-		/* Refill (replace pd contents with new data) */
-		if(pd->refill(pd))
-			return -1;
-		nbits -= nleft;
-		vhead = per_get_few_bits(pd, nbits);
-		/* Combine the rest of previous pd with the head of new one */
-		tailv = (tailv << nbits) | vhead;  /* Could == -1 */
-		return tailv;
-	}
-
-	/*
-	 * Normalize position indicator.
-	 */
-	if(pd->nboff >= 8) {
-		pd->buffer += (pd->nboff >> 3);
-		pd->nbits  -= (pd->nboff & ~0x07);
-		pd->nboff  &= 0x07;
-	}
-	pd->moved += nbits;
-	pd->nboff += nbits;
-	off = pd->nboff;
-	buf = pd->buffer;
-
-	/*
-	 * Extract specified number of bits.
-	 */
-	if(off <= 8)
-		accum = nbits ? (buf[0]) >> (8 - off) : 0;
-	else if(off <= 16)
-		accum = ((buf[0] << 8) + buf[1]) >> (16 - off);
-	else if(off <= 24)
-		accum = ((buf[0] << 16) + (buf[1] << 8) + buf[2]) >> (24 - off);
-	else if(off <= 31)
-		accum = ((buf[0] << 24) + (buf[1] << 16)
-			+ (buf[2] << 8) + (buf[3])) >> (32 - off);
-	else if(nbits <= 31) {
-		asn_per_data_t tpd = *pd;
-		/* Here are we with our 31-bits limit plus 1..7 bits offset. */
-		per_get_undo(&tpd, nbits);
-		/* The number of available bits in the stream allow
-		 * for the following operations to take place without
-		 * invoking the ->refill() function */
-		accum  = per_get_few_bits(&tpd, nbits - 24) << 24;
-		accum |= per_get_few_bits(&tpd, 24);
-	} else {
-		per_get_undo(pd, nbits);
-		return -1;
-	}
-
-	accum &= (((uint32_t)1 << nbits) - 1);
-
-	ASN_DEBUG("  [PER got %2d<=%2d bits => span %d %+ld[%d..%d]:%02x (%d) => 0x%x]",
-		(int)nbits, (int)nleft,
-		(int)pd->moved,
-		(((long)pd->buffer) & 0xf),
-		(int)pd->nboff, (int)pd->nbits,
-		((pd->buffer != NULL)?pd->buffer[0]:0),
-		(int)(pd->nbits - pd->nboff),
-		(int)accum);
-
-	return accum;
-}
-
-/*
- * Extract a large number of bits from the specified PER data pointer.
- */
-int
-per_get_many_bits(asn_per_data_t *pd, uint8_t *dst, int alright, int nbits) {
-	int32_t value;
-
-	if(alright && (nbits & 7)) {
-		/* Perform right alignment of a first few bits */
-		value = per_get_few_bits(pd, nbits & 0x07);
-		if(value < 0) return -1;
-		*dst++ = value;	/* value is already right-aligned */
-		nbits &= ~7;
-	}
-
-	while(nbits) {
-		if(nbits >= 24) {
-			value = per_get_few_bits(pd, 24);
-			if(value < 0) return -1;
-			*(dst++) = value >> 16;
-			*(dst++) = value >> 8;
-			*(dst++) = value;
-			nbits -= 24;
-		} else {
-			value = per_get_few_bits(pd, nbits);
-			if(value < 0) return -1;
-			if(nbits & 7) {	/* implies left alignment */
-				value <<= 8 - (nbits & 7),
-				nbits += 8 - (nbits & 7);
-				if(nbits > 24)
-					*dst++ = value >> 24;
-			}
-			if(nbits > 16)
-				*dst++ = value >> 16;
-			if(nbits > 8)
-				*dst++ = value >> 8;
-			*dst++ = value;
-			break;
-		}
-	}
-
-	return 0;
-}
-
 /*
  * X.691-201508 #10.9 General rules for encoding a length determinant.
  * Get the optionally constrained length "n" from the stream.
@@ -322,151 +169,6 @@
 	}
 }
 
-int
-per_put_aligned_flush(asn_per_outp_t *po) {
-    uint32_t unused_bits = (0x7 & (8 - (po->nboff & 0x07)));
-    size_t complete_bytes =
-        (po->buffer ? po->buffer - po->tmpspace : 0) + ((po->nboff + 7) >> 3);
-
-    if(unused_bits) {
-        po->buffer[po->nboff >> 3] &= ~0 << unused_bits;
-    }
-
-    if(po->outper(po->tmpspace, complete_bytes, po->op_key) < 0) {
-        return -1;
-    } else {
-        po->buffer = po->tmpspace;
-        po->nboff = 0;
-        po->nbits = 8 * sizeof(po->tmpspace);
-        po->flushed_bytes += complete_bytes;
-        return 0;
-    }
-}
-
-/*
- * Put a small number of bits (<= 31).
- */
-int
-per_put_few_bits(asn_per_outp_t *po, uint32_t bits, int obits) {
-	size_t off;	/* Next after last bit offset */
-	size_t omsk;	/* Existing last byte meaningful bits mask */
-	uint8_t *buf;
-
-	if(obits <= 0 || obits >= 32) return obits ? -1 : 0;
-
-	ASN_DEBUG("[PER put %d bits %x to %p+%d bits]",
-			obits, (int)bits, po->buffer, (int)po->nboff);
-
-	/*
-	 * Normalize position indicator.
-	 */
-	if(po->nboff >= 8) {
-		po->buffer += (po->nboff >> 3);
-		po->nbits  -= (po->nboff & ~0x07);
-		po->nboff  &= 0x07;
-	}
-
-	/*
-	 * Flush whole-bytes output, if necessary.
-	 */
-	if(po->nboff + obits > po->nbits) {
-		size_t complete_bytes;
-		if(!po->buffer) po->buffer = po->tmpspace;
-		complete_bytes = (po->buffer - po->tmpspace);
-		ASN_DEBUG("[PER output %ld complete + %ld]",
-			(long)complete_bytes, (long)po->flushed_bytes);
-		if(po->outper(po->tmpspace, complete_bytes, po->op_key) < 0)
-			return -1;
-		if(po->nboff)
-			po->tmpspace[0] = po->buffer[0];
-		po->buffer = po->tmpspace;
-		po->nbits = 8 * sizeof(po->tmpspace);
-		po->flushed_bytes += complete_bytes;
-	}
-
-	/*
-	 * Now, due to sizeof(tmpspace), we are guaranteed large enough space.
-	 */
-	buf = po->buffer;
-	omsk = ~((1 << (8 - po->nboff)) - 1);
-	off = (po->nboff + obits);
-
-	/* Clear data of debris before meaningful bits */
-	bits &= (((uint32_t)1 << obits) - 1);
-
-	ASN_DEBUG("[PER out %d %u/%x (t=%d,o=%d) %x&%x=%x]", obits,
-		(int)bits, (int)bits,
-		(int)po->nboff, (int)off,
-		buf[0], (int)(omsk&0xff),
-		(int)(buf[0] & omsk));
-
-	if(off <= 8)	/* Completely within 1 byte */
-		po->nboff = off,
-		bits <<= (8 - off),
-		buf[0] = (buf[0] & omsk) | bits;
-	else if(off <= 16)
-		po->nboff = off,
-		bits <<= (16 - off),
-		buf[0] = (buf[0] & omsk) | (bits >> 8),
-		buf[1] = bits;
-	else if(off <= 24)
-		po->nboff = off,
-		bits <<= (24 - off),
-		buf[0] = (buf[0] & omsk) | (bits >> 16),
-		buf[1] = bits >> 8,
-		buf[2] = bits;
-	else if(off <= 31)
-		po->nboff = off,
-		bits <<= (32 - off),
-		buf[0] = (buf[0] & omsk) | (bits >> 24),
-		buf[1] = bits >> 16,
-		buf[2] = bits >> 8,
-		buf[3] = bits;
-	else {
-		if(per_put_few_bits(po, bits >> (obits - 24), 24)) return -1;
-		if(per_put_few_bits(po, bits, obits - 24)) return -1;
-	}
-
-	ASN_DEBUG("[PER out %u/%x => %02x buf+%ld]",
-		(int)bits, (int)bits, buf[0],
-		(long)(po->buffer - po->tmpspace));
-
-	return 0;
-}
-
-
-/*
- * Output a large number of bits.
- */
-int
-per_put_many_bits(asn_per_outp_t *po, const uint8_t *src, int nbits) {
-
-	while(nbits) {
-		uint32_t value;
-
-		if(nbits >= 24) {
-			value = (src[0] << 16) | (src[1] << 8) | src[2];
-			src += 3;
-			nbits -= 24;
-			if(per_put_few_bits(po, value, 24))
-				return -1;
-		} else {
-			value = src[0];
-			if(nbits > 8)
-				value = (value << 8) | src[1];
-			if(nbits > 16)
-				value = (value << 8) | src[2];
-			if(nbits & 0x07)
-				value >>= (8 - (nbits & 0x07));
-			if(per_put_few_bits(po, value, nbits))
-				return -1;
-			break;
-		}
-	}
-
-	return 0;
-}
-
 /*
  * Put the length "n" (or part of it) into the stream.
  */
diff --git a/skeletons/per_support.h b/skeletons/per_support.h
index 4dcc9e6..9b564a2 100644
--- a/skeletons/per_support.h
+++ b/skeletons/per_support.h
@@ -7,6 +7,7 @@
 #define	_PER_SUPPORT_H_
 
 #include <asn_system.h>		/* Platform-specific types */
+#include <asn_bit_data.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -34,35 +35,12 @@
 	int (*code2value)(unsigned int code);
 } asn_per_constraints_t;
 
-/*
- * This structure describes a position inside an incoming PER bit stream.
- */
-typedef struct asn_per_data_s {
-  const uint8_t *buffer;  /* Pointer to the octet stream */
-         size_t  nboff;   /* Bit offset to the meaningful bit */
-         size_t  nbits;   /* Number of bits in the stream */
-         size_t  moved;   /* Number of bits moved through this bit stream */
-  int (*refill)(struct asn_per_data_s *);
-  void *refill_key;
-} asn_per_data_t;
-
-/*
- * Extract a small number of bits (<= 31) from the specified PER data pointer.
- * This function returns -1 if the specified number of bits could not be
- * extracted due to EOD or other conditions.
- */
-int32_t per_get_few_bits(asn_per_data_t *per_data, int get_nbits);
-
-/* Undo the immediately preceeding "get_few_bits" operation */
-void per_get_undo(asn_per_data_t *per_data, int get_nbits);
-
-/*
- * Extract a large number of bits from the specified PER data pointer.
- * This function returns -1 if the specified number of bits could not be
- * extracted due to EOD or other conditions.
- */
-int per_get_many_bits(asn_per_data_t *pd, uint8_t *dst, int right_align,
-			int get_nbits);
+/* Temporary compatibility layer. Will get removed. */
+typedef struct asn_bit_data_s asn_per_data_t;
+#define per_get_few_bits(data, bits)   asn_get_few_bits(data, bits)
+#define per_get_undo(data, bits)   asn_get_undo(data, bits)
+#define per_get_many_bits(data, dst, align, bits) \
+    asn_get_many_bits(data, dst, align, bits)
 
 /*
  * Get the length "n" from the Unaligned PER stream.
@@ -84,34 +62,12 @@
 /* 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);
 
-/*
- * This structure supports forming PER output.
- */
-typedef struct asn_per_outp_s {
-	uint8_t *buffer;	/* Pointer into the (tmpspace) */
-	size_t nboff;		/* Bit offset to the meaningful bit */
-	size_t nbits;		/* Number of bits left in (tmpspace) */
-	uint8_t tmpspace[32];	/* Preliminary storage to hold data */
-	int (*outper)(const void *data, size_t size, void *op_key);
-	void *op_key;		/* Key for (outper) data callback */
-	size_t flushed_bytes;	/* Bytes already flushed through (outper) */
-} asn_per_outp_t;
-
-/* Output a small number of bits (<= 31) */
-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);
-
-/*
- * Flush whole bytes (0 or more) through (outper) member.
- * The least significant bits which are not used are guaranteed to be set to 0.
- * Returns -1 if callback returns -1. Otherwise, 0.
- */
-int per_put_aligned_flush(asn_per_outp_t *po);
+/* Temporary compatibility layer. Will get removed. */
+typedef struct asn_bit_outp_s asn_per_outp_t;
+#define per_put_few_bits(out, bits, obits) asn_put_few_bits(out, bits, obits)
+#define per_put_many_bits(out, src, nbits) asn_put_many_bits(out, src, nbits)
+#define per_put_aligned_flush(out) asn_put_aligned_flush(out)
 
 /* X.691-2008/11, #11.5 */
 int uper_put_constrained_whole_number_s(asn_per_outp_t *po, long v, int nbits);