/*-
 * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
 * Redistribution and modifications are permitted subject to BSD license.
 */
#include <asn_internal.h>
#include <BIT_STRING.h>
#include <asn_internal.h>

/*
 * BIT STRING basic type description.
 */
static const ber_tlv_tag_t asn_DEF_BIT_STRING_tags[] = {
	(ASN_TAG_CLASS_UNIVERSAL | (3 << 2))
};
asn_OCTET_STRING_specifics_t asn_SPC_BIT_STRING_specs = {
	sizeof(BIT_STRING_t),
	offsetof(BIT_STRING_t, _asn_ctx),
	ASN_OSUBV_BIT
};
asn_TYPE_operation_t asn_OP_BIT_STRING = {
	OCTET_STRING_free,         /* Implemented in terms of OCTET STRING */
	BIT_STRING_print,
	BIT_STRING_compare,
	OCTET_STRING_decode_ber,   /* Implemented in terms of OCTET STRING */
	OCTET_STRING_encode_der,   /* Implemented in terms of OCTET STRING */
	OCTET_STRING_decode_xer_binary,
	BIT_STRING_encode_xer,
#ifdef	ASN_DISABLE_OER_SUPPORT
	0,
	0,
#else
	BIT_STRING_decode_oer,
	BIT_STRING_encode_oer,
#endif  /* ASN_DISABLE_OER_SUPPORT */
#ifdef	ASN_DISABLE_PER_SUPPORT
	0,
	0,
	0,
	0,
#else
	BIT_STRING_decode_uper,	/* Unaligned PER decoder */
	BIT_STRING_encode_uper,	/* Unaligned PER encoder */
	OCTET_STRING_decode_aper,	/* Aligned PER decoder */
	OCTET_STRING_encode_aper,	/* Aligned PER encoder */
#endif  /* ASN_DISABLE_PER_SUPPORT */
	BIT_STRING_random_fill,
	0	/* Use generic outmost tag fetcher */
};
asn_TYPE_descriptor_t asn_DEF_BIT_STRING = {
	"BIT STRING",
	"BIT_STRING",
	&asn_OP_BIT_STRING,
	asn_DEF_BIT_STRING_tags,
	sizeof(asn_DEF_BIT_STRING_tags)
	  / sizeof(asn_DEF_BIT_STRING_tags[0]),
	asn_DEF_BIT_STRING_tags,	/* Same as above */
	sizeof(asn_DEF_BIT_STRING_tags)
	  / sizeof(asn_DEF_BIT_STRING_tags[0]),
	{ 0, 0, BIT_STRING_constraint },
	0, 0,	/* No members */
	&asn_SPC_BIT_STRING_specs
};

/*
 * BIT STRING generic constraint.
 */
int
BIT_STRING_constraint(const asn_TYPE_descriptor_t *td, const void *sptr,
                      asn_app_constraint_failed_f *ctfailcb, void *app_key) {
    const BIT_STRING_t *st = (const BIT_STRING_t *)sptr;

	if(st && st->buf) {
		if((st->size == 0 && st->bits_unused)
		|| st->bits_unused < 0 || st->bits_unused > 7) {
			ASN__CTFAIL(app_key, td, sptr,
				"%s: invalid padding byte (%s:%d)",
				td->name, __FILE__, __LINE__);
			return -1;
		}
	} else {
		ASN__CTFAIL(app_key, td, sptr,
			"%s: value not given (%s:%d)",
			td->name, __FILE__, __LINE__);
		return -1;
	}

	return 0;
}

static const char *_bit_pattern[16] = {
	"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111",
	"1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"
};

asn_enc_rval_t
BIT_STRING_encode_xer(const asn_TYPE_descriptor_t *td, const void *sptr,
                      int ilevel, enum xer_encoder_flags_e flags,
                      asn_app_consume_bytes_f *cb, void *app_key) {
    asn_enc_rval_t er;
	char scratch[128];
	char *p = scratch;
	char *scend = scratch + (sizeof(scratch) - 10);
	const BIT_STRING_t *st = (const BIT_STRING_t *)sptr;
	int xcan = (flags & XER_F_CANONICAL);
	uint8_t *buf;
	uint8_t *end;

	if(!st || !st->buf)
		ASN__ENCODE_FAILED;

	er.encoded = 0;

	buf = st->buf;
	end = buf + st->size - 1;	/* Last byte is special */

	/*
	 * Binary dump
	 */
	for(; buf < end; buf++) {
		int v = *buf;
		int nline = xcan?0:(((buf - st->buf) % 8) == 0);
		if(p >= scend || nline) {
			ASN__CALLBACK(scratch, p - scratch);
			p = scratch;
			if(nline) ASN__TEXT_INDENT(1, ilevel);
		}
		memcpy(p + 0, _bit_pattern[v >> 4], 4);
		memcpy(p + 4, _bit_pattern[v & 0x0f], 4);
		p += 8;
	}

	if(!xcan && ((buf - st->buf) % 8) == 0)
		ASN__TEXT_INDENT(1, ilevel);
	ASN__CALLBACK(scratch, p - scratch);
	p = scratch;

	if(buf == end) {
		int v = *buf;
		int ubits = st->bits_unused;
		int i;
		for(i = 7; i >= ubits; i--)
			*p++ = (v & (1 << i)) ? 0x31 : 0x30;
		ASN__CALLBACK(scratch, p - scratch);
	}

	if(!xcan) ASN__TEXT_INDENT(1, ilevel - 1);

	ASN__ENCODED_OK(er);
cb_failed:
	ASN__ENCODE_FAILED;
}


/*
 * BIT STRING specific contents printer.
 */
int
BIT_STRING_print(const asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
                 asn_app_consume_bytes_f *cb, void *app_key) {
    const char * const h2c = "0123456789ABCDEF";
	char scratch[64];
	const BIT_STRING_t *st = (const BIT_STRING_t *)sptr;
	uint8_t *buf;
	uint8_t *end;
	char *p = scratch;

	(void)td;	/* Unused argument */

	if(!st || !st->buf)
		return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;

	ilevel++;
	buf = st->buf;
	end = buf + st->size;

	/*
	 * Hexadecimal dump.
	 */
	for(; buf < end; buf++) {
		if((buf - st->buf) % 16 == 0 && (st->size > 16)
				&& buf != st->buf) {
			_i_INDENT(1);
			/* Dump the string */
			if(cb(scratch, p - scratch, app_key) < 0) return -1;
			p = scratch;
		}
		*p++ = h2c[*buf >> 4];
		*p++ = h2c[*buf & 0x0F];
		*p++ = 0x20;
	}

	if(p > scratch) {
		p--;	/* Eat the tailing space */

		if((st->size > 16)) {
			_i_INDENT(1);
		}

		/* Dump the incomplete 16-bytes row */
		if(cb(scratch, p - scratch, app_key) < 0)
			return -1;
	}

    if(st->bits_unused) {
        int ret = snprintf(scratch, sizeof(scratch), " (%d bit%s unused)",
                           st->bits_unused, st->bits_unused == 1 ? "" : "s");
        assert(ret > 0 && ret < (ssize_t)sizeof(scratch));
        if(ret > 0 && ret < (ssize_t)sizeof(scratch)
           && cb(scratch, ret, app_key) < 0)
            return -1;
    }

	return 0;
}

/*
 * Non-destructively remove the trailing 0-bits from the given bit string.
 */
static const BIT_STRING_t *
BIT_STRING__compactify(const BIT_STRING_t *st, BIT_STRING_t *tmp) {
    const uint8_t *b;
    union {
        const uint8_t *c_buf;
        uint8_t *nc_buf;
    } unconst;

    if(st->size == 0) {
        assert(st->bits_unused == 0);
        return st;
    } else {
        for(b = &st->buf[st->size - 1]; b > st->buf && *b == 0; b--) {
            ;
        }
        /* b points to the last byte which may contain data */
        if(*b) {
            int unused = 7;
            uint8_t v = *b;
            v &= -(int8_t)v;
            if(v & 0x0F) unused -= 4;
            if(v & 0x33) unused -= 2;
            if(v & 0x55) unused -= 1;
            tmp->size = b-st->buf + 1;
            tmp->bits_unused = unused;
        } else {
            tmp->size = b-st->buf;
            tmp->bits_unused = 0;
        }

        assert(b >= st->buf);
    }

    unconst.c_buf = st->buf;
    tmp->buf = unconst.nc_buf;
    return tmp;
}

/*
 * Lexicographically compare the common prefix of both strings,
 * and if it is the same return -1 for the smallest string.
 */
int
BIT_STRING_compare(const asn_TYPE_descriptor_t *td, const void *aptr,
                   const void *bptr) {
    /*
     * Remove information about trailing bits, since
     * X.680 (08/2015) #22.7 "ensure that different semantics are not"
     * "associated with [values that differ only in] the trailing 0 bits."
     */
    BIT_STRING_t compact_a, compact_b;
    const BIT_STRING_t *a = BIT_STRING__compactify(aptr, &compact_a);
    const BIT_STRING_t *b = BIT_STRING__compactify(bptr, &compact_b);
    const asn_OCTET_STRING_specifics_t *specs = td->specifics;

    assert(specs && specs->subvariant == ASN_OSUBV_BIT);

    if(a && b) {
        size_t common_prefix_size = a->size <= b->size ? a->size : b->size;
        int ret = memcmp(a->buf, b->buf, common_prefix_size);
        if(ret == 0) {
            /* Figure out which string with equal prefixes is longer. */
            if(a->size < b->size) {
                return -1;
            } else if(a->size > b->size) {
                return 1;
            } else {
                /* Figure out how many unused bits */
                if(a->bits_unused > b->bits_unused) {
                    return -1;
                } else if(a->bits_unused < b->bits_unused) {
                    return 1;
                } else {
                    return 0;
                }
            }
        } else {
            return ret;
        }
    } else if(!a && !b) {
        return 0;
    } else if(!a) {
        return -1;
    } else {
        return 1;
    }
}

#ifndef  ASN_DISABLE_PER_SUPPORT

#undef  RETURN
#define RETURN(_code)                       \
    do {                                    \
        asn_dec_rval_t tmprval;             \
        tmprval.code = _code;               \
        tmprval.consumed = consumed_myself; \
        return tmprval;                     \
    } while(0)

static asn_per_constraint_t asn_DEF_BIT_STRING_constraint_size = {
    APC_SEMI_CONSTRAINED, -1, -1, 0, 0};

asn_dec_rval_t
BIT_STRING_decode_uper(const asn_codec_ctx_t *opt_codec_ctx,
                       const asn_TYPE_descriptor_t *td,
                       const asn_per_constraints_t *constraints, void **sptr,
                       asn_per_data_t *pd) {
    const asn_OCTET_STRING_specifics_t *specs = td->specifics
		? (const asn_OCTET_STRING_specifics_t *)td->specifics
		: &asn_SPC_BIT_STRING_specs;
    const asn_per_constraints_t *pc =
        constraints ? constraints : td->encoding_constraints.per_constraints;
	const 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;

	(void)opt_codec_ctx;

	if(pc) {
		csiz = &pc->size;
	} else {
		csiz = &asn_DEF_BIT_STRING_constraint_size;
	}

	if(specs->subvariant != ASN_OSUBV_BIT) {
		ASN_DEBUG("Subvariant %d is not BIT OSUBV_BIT", specs->subvariant);
		RETURN(RC_FAIL);
    }

	/*
	 * Allocate the string.
	 */
	if(!st) {
		st = (BIT_STRING_t *)(*sptr = CALLOC(1, specs->struct_size));
		if(!st) RETURN(RC_FAIL);
	}

	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(csiz->flags & APC_EXTENSIBLE) {
		int inext = per_get_few_bits(pd, 1);
		if(inext < 0) RETURN(RC_WMORE);
		if(inext) {
			csiz = &asn_DEF_BIT_STRING_constraint_size;
		}
	}

	if(csiz->effective_bits >= 0) {
		FREEMEM(st->buf);
        st->size = (csiz->upper_bound + 7) >> 3;
        st->buf = (uint8_t *)MALLOC(st->size + 1);
		if(!st->buf) { st->size = 0; RETURN(RC_FAIL); }
	}

	/* 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(csiz->effective_bits == 0) {
		int ret;
        ASN_DEBUG("Encoding BIT STRING size %ld", csiz->upper_bound);
        ret = per_get_many_bits(pd, st->buf, 0, csiz->upper_bound);
		if(ret < 0) RETURN(RC_WMORE);
		consumed_myself += csiz->upper_bound;
		st->buf[st->size] = 0;
        st->bits_unused = (8 - (csiz->upper_bound & 0x7)) & 0x7;
        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 */
		raw_len = uper_get_length(pd, csiz->effective_bits, csiz->lower_bound,
		                          &repeat);
		if(raw_len < 0) RETURN(RC_WMORE);
        if(raw_len == 0 && st->buf) break;

		ASN_DEBUG("Got PER length eb %ld, len %ld, %s (%s)",
			(long)csiz->effective_bits, (long)raw_len,
			repeat ? "repeat" : "once", td->name);
        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 */
        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(ret < 0) RETURN(RC_WMORE);
		st->size += len_bytes;
	} while(repeat);
	st->buf[st->size] = 0;	/* nul-terminate */

	return rval;
}

asn_enc_rval_t
BIT_STRING_encode_uper(const asn_TYPE_descriptor_t *td,
                       const asn_per_constraints_t *constraints,
                       const void *sptr, asn_per_outp_t *po) {
    const asn_OCTET_STRING_specifics_t *specs =
        td->specifics ? (const asn_OCTET_STRING_specifics_t *)td->specifics
                      : &asn_SPC_BIT_STRING_specs;
    const asn_per_constraints_t *pc =
        constraints ? constraints : td->encoding_constraints.per_constraints;
	const asn_per_constraint_t *csiz;
	const BIT_STRING_t *st = (const BIT_STRING_t *)sptr;
	BIT_STRING_t compact_bstr;  /* Do not modify this directly! */
	asn_enc_rval_t er = { 0, 0, 0 };
	int inext = 0;		/* Lies not within extension root */
	size_t size_in_bits;
	const uint8_t *buf;
	int ret;
	int ct_extensible;

	if(!st || (!st->buf && st->size))
		ASN__ENCODE_FAILED;

	if(specs->subvariant == ASN_OSUBV_BIT) {
        if((st->size == 0 && st->bits_unused) || (st->bits_unused & ~7))
            ASN__ENCODE_FAILED;
    } else {
		ASN__ENCODE_FAILED;
    }

	if(pc) {
        csiz = &pc->size;
    } else {
		csiz = &asn_DEF_BIT_STRING_constraint_size;
	}
	ct_extensible = csiz->flags & APC_EXTENSIBLE;

    /* Figure out the size without the trailing bits */
    st = BIT_STRING__compactify(st, &compact_bstr);
    size_in_bits = 8 * st->size - st->bits_unused;

    ASN_DEBUG(
        "Encoding %s into %" ASN_PRI_SIZE " bits"
        " (%ld..%ld, effective %d)%s",
        td->name, size_in_bits, csiz->lower_bound, csiz->upper_bound,
        csiz->effective_bits, ct_extensible ? " EXT" : "");

    /* Figure out whether size lies within PER visible constraint */

    if(csiz->effective_bits >= 0) {
        if((ssize_t)size_in_bits > csiz->upper_bound) {
            if(ct_extensible) {
                csiz = &asn_DEF_BIT_STRING_constraint_size;
                inext = 1;
            } else {
                ASN__ENCODE_FAILED;
            }
        }
    } else {
        inext = 0;
    }

    if(ct_extensible) {
		/* Declare whether length is [not] within extension root */
		if(per_put_few_bits(po, inext, 1))
			ASN__ENCODE_FAILED;
	}

    if(csiz->effective_bits >= 0 && !inext) {
        int add_trailer = (ssize_t)size_in_bits < csiz->lower_bound;
        ASN_DEBUG(
            "Encoding %" ASN_PRI_SIZE " bytes (%ld), length (in %d bits) trailer %d; actual "
            "value %" ASN_PRI_SSIZE "",
            st->size, size_in_bits - csiz->lower_bound, csiz->effective_bits,
            add_trailer,
            add_trailer ? 0 : (ssize_t)size_in_bits - csiz->lower_bound);
        ret = per_put_few_bits(
            po, add_trailer ? 0 : (ssize_t)size_in_bits - csiz->lower_bound,
            csiz->effective_bits);
        if(ret) ASN__ENCODE_FAILED;
        ret = per_put_many_bits(po, st->buf, size_in_bits);
        if(ret) ASN__ENCODE_FAILED;
        if(add_trailer) {
            static const uint8_t zeros[16];
            size_t trailing_zero_bits = csiz->lower_bound - size_in_bits;
            while(trailing_zero_bits > 0) {
                if(trailing_zero_bits > 8 * sizeof(zeros)) {
                    ret = per_put_many_bits(po, zeros, 8 * sizeof(zeros));
                    trailing_zero_bits -= 8 * sizeof(zeros);
                } else {
                    ret = per_put_many_bits(po, zeros, trailing_zero_bits);
                    trailing_zero_bits = 0;
                }
                if(ret) ASN__ENCODE_FAILED;
            }
        }
        ASN__ENCODED_OK(er);
    }

    ASN_DEBUG("Encoding %" ASN_PRI_SIZE " bytes", st->size);

    buf = st->buf;
    do {
        int need_eom = 0;
        ssize_t maySave = uper_put_length(po, size_in_bits, &need_eom);
        if(maySave < 0) ASN__ENCODE_FAILED;

        ASN_DEBUG("Encoding %" ASN_PRI_SSIZE " of %" ASN_PRI_SIZE "", maySave, size_in_bits);

        ret = per_put_many_bits(po, buf, maySave);
        if(ret) ASN__ENCODE_FAILED;

        buf += maySave >> 3;
        size_in_bits -= maySave;
        assert(!(maySave & 0x07) || !size_in_bits);
        if(need_eom && uper_put_length(po, 0, 0))
            ASN__ENCODE_FAILED; /* End of Message length */
    } while(size_in_bits);

    ASN__ENCODED_OK(er);
}

#endif  /* ASN_DISABLE_PER_SUPPORT */

asn_random_fill_result_t
BIT_STRING_random_fill(const asn_TYPE_descriptor_t *td, void **sptr,
                       const asn_encoding_constraints_t *constraints,
                       size_t max_length) {
    const asn_OCTET_STRING_specifics_t *specs =
        td->specifics ? (const asn_OCTET_STRING_specifics_t *)td->specifics
                      : &asn_SPC_BIT_STRING_specs;
    asn_random_fill_result_t result_ok = {ARFILL_OK, 1};
    asn_random_fill_result_t result_failed = {ARFILL_FAILED, 0};
    asn_random_fill_result_t result_skipped = {ARFILL_SKIPPED, 0};
    static unsigned lengths[] = {0,     1,     2,     3,     4,     8,
                                 126,   127,   128,   16383, 16384, 16385,
                                 65534, 65535, 65536, 65537};
    uint8_t *buf;
    uint8_t *bend;
    uint8_t *b;
    size_t rnd_bits, rnd_len;
    BIT_STRING_t *st;

    if(max_length == 0) return result_skipped;

    switch(specs->subvariant) {
    case ASN_OSUBV_ANY:
        return result_failed;
    case ASN_OSUBV_BIT:
        break;
    default:
        break;
    }

    /* Figure out how far we should go */
    rnd_bits = lengths[asn_random_between(
        0, sizeof(lengths) / sizeof(lengths[0]) - 1)];
    if(!constraints || !constraints->per_constraints)
        constraints = &td->encoding_constraints;
    if(constraints->per_constraints) {
        const asn_per_constraint_t *pc = &constraints->per_constraints->size;
        if(pc->flags & APC_CONSTRAINED) {
            long suggested_upper_bound = pc->upper_bound < (ssize_t)max_length
                                             ? pc->upper_bound
                                             : (ssize_t)max_length;
            if(max_length < (size_t)pc->lower_bound) {
                return result_skipped;
            }
            if(pc->flags & APC_EXTENSIBLE) {
                switch(asn_random_between(0, 5)) {
                case 0:
                    if(pc->lower_bound > 0) {
                        rnd_bits = pc->lower_bound - 1;
                        break;
                    }
                    /* Fall through */
                case 1:
                    rnd_bits = pc->upper_bound + 1;
                    break;
                case 2:
                    /* Keep rnd_bits from the table */
                    if(rnd_bits < max_length) {
                        break;
                    }
                    /* Fall through */
                default:
                    rnd_bits = asn_random_between(pc->lower_bound,
                                                  suggested_upper_bound);
                }
            } else {
                rnd_bits =
                    asn_random_between(pc->lower_bound, suggested_upper_bound);
            }
        } else {
            rnd_bits = asn_random_between(0, max_length - 1);
        }
    } else if(rnd_bits >= max_length) {
        rnd_bits = asn_random_between(0, max_length - 1);
    }

    rnd_len = (rnd_bits + 7) / 8;
    buf = CALLOC(1, rnd_len + 1);
    if(!buf) return result_failed;

    bend = &buf[rnd_len];

    for(b = buf; b < bend; b++) {
        *(uint8_t *)b = asn_random_between(0, 255);
    }
    *b = 0; /* Zero-terminate just in case. */

    if(*sptr) {
        st = *sptr;
        FREEMEM(st->buf);
    } else {
        st = (BIT_STRING_t *)(*sptr = CALLOC(1, specs->struct_size));
        if(!st) {
            FREEMEM(buf);
            return result_failed;
        }
    }

    st->buf = buf;
    st->size = rnd_len;
    st->bits_unused = (8 - (rnd_bits & 0x7)) & 0x7;
    if(st->bits_unused) {
        assert(st->size > 0);
        st->buf[st->size-1] &= 0xff << st->bits_unused;
    }

    result_ok.length = st->size;
    return result_ok;
}
