/* Copyright (C) Boris Nikolaus, Germany, 1996-1997. All rights reserved. */
/* Copyright (C) Microsoft Corporation, 1997-1998. All rights reserved. */

// lonchanc: we seem to have a significant amount of memory leak
// while dealing with real number and unlimited integers.
// we definitely want to re-visit all the following routines carefully
// in the future.
// moreover, we need to make sure all the memory allocation and free
// are either using encoding and decoding memory manager or kernel one.
// need to make sure we do not mix them together.

#include "precomp.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* builtin intx values */
ASN1octet_t ASN1intx_0_[] = { 0 };
ASN1intx_t ASN1intx_0 = { 1, ASN1intx_0_ };
ASN1octet_t ASN1intx_1_[] = { 1 };
ASN1intx_t ASN1intx_1 = { 1, ASN1intx_1_ };
ASN1octet_t ASN1intx_2_[] = { 2 };
ASN1intx_t ASN1intx_2 = { 1, ASN1intx_2_ };
ASN1octet_t ASN1intx_16_[] = { 16 };
ASN1intx_t ASN1intx_16 = { 1, ASN1intx_16_ };
ASN1octet_t ASN1intx_256_[] = { 1, 0 };
ASN1intx_t ASN1intx_256 = { 2, ASN1intx_256_ };
ASN1octet_t ASN1intx_64K_[] = { 1, 0, 0 };
ASN1intx_t ASN1intx_64K = { 3, ASN1intx_64K_ };
ASN1octet_t ASN1intx_1G_[] = { 64, 0, 0, 0 };
ASN1intx_t ASN1intx_1G = { 4, ASN1intx_1G_ };

/* add two intx values */
void
ASN1intx_add(ASN1intx_t *dst, ASN1intx_t *arg1, ASN1intx_t *arg2)
{
    ASN1octet_t *v;
    int l;
    int s1, s2;
    int o1, o2;
    int i;
    int c;
    int w;

    /* get signs */
    s1 = arg1->value[0] > 0x7f ? 0xff : 0x00;
    s2 = arg2->value[0] > 0x7f ? 0xff : 0x00;

    /* result length will be <= l */
    l = arg1->length > arg2->length ? arg1->length + 1 : arg2->length + 1;

    /* offset into values */
    o1 = l - arg1->length;
    o2 = l - arg2->length;

    /* allocate result */
    v = (ASN1octet_t *)malloc(l);

    /* clear carry bit */
    c = 0;

    /* add octet by octet */
    for (i = l - 1; i >= 0; i--) {
        w = (i >= o1 ? arg1->value[i - o1] : s1) + (i >= o2 ? arg2->value[i - o2] : s2) + c;
        v[i] = (ASN1octet_t)w;
        c = w > 0xff;
    }

    /* octets which may shall dropped */
    w = v[0] > 0x7f ? 0xff : 0x00;

    /* count octets that shall be dropped */
    for (i = 0; i < l - 1; i++) {
        if (v[i] != w)
            break;
    }
    if ((v[i] ^ w) & 0x80)
        i--;

    /* allocate and copy result */
    dst->length = l - i;
    dst->value = (ASN1octet_t *)malloc(l - i);
    memcpy(dst->value, v + i, l - i);
    free(v);
}

/* substract two intx values */
void
ASN1intx_sub(ASN1intx_t *dst, ASN1intx_t *arg1, ASN1intx_t *arg2)
{
    ASN1octet_t *v;
    int l;
    int s1, s2;
    int o1, o2;
    int i;
    int c;
    int w;

    /* get signs */
    s1 = arg1->value[0] > 0x7f ? 0xff : 0x00;
    s2 = arg2->value[0] > 0x7f ? 0xff : 0x00;

    /* result length will be <= l */
    l = arg1->length > arg2->length ? arg1->length + 1 : arg2->length + 1;

    /* offset into values */
    o1 = l - arg1->length;
    o2 = l - arg2->length;

    /* allocate result */
    v = (ASN1octet_t *)malloc(l);

    /* clear borrow bit */
    c = 0;

    /* substract octet by octet */
    for (i = l - 1; i >= 0; i--) {
        w = (i >= o1 ? arg1->value[i - o1] : s1) - (i >= o2 ? arg2->value[i - o2] : s2) - c;
        v[i] = (ASN1octet_t)w;
        c = w < 0;
    }

    /* octets which may shall dropped */
    w = v[0] > 0x7f ? 0xff : 0x00;

    /* count octets that shall be dropped */
    for (i = 0; i < l - 1; i++) {
        if (v[i] != w)
            break;
    }
    if ((v[i] ^ w) & 0x80)
        i--;

// lonchanc: do we forget to free dst->value???
// in case that dst and arg1 are identical. for instance, 
// ASN1BEREncReal() calls ASN1intx_sub(&exponent, &exponent, &help);
    /* allocate and copy result */
    dst->length = l - i;
    dst->value = (ASN1octet_t *)malloc(l - i);
    memcpy(dst->value, v + i, l - i);
    free(v);
}

/* add one octet to an intx */
void
ASN1intx_addoctet(ASN1intx_t *dst, ASN1intx_t *arg1, ASN1octet_t arg2)
{
    ASN1octet_t *v;
    int l;
    int i;
    int c;
    int w;

    /* result length will be <= l */
    l = arg1->length + 1;

    /* allocate result */
    v = (ASN1octet_t *)malloc(l);

    /* add octet by octet */
    c = arg2;
    for (i = l - 2; i >= 0; i--) {
        w = arg1->value[i] + c;
        v[i + 1] = (ASN1octet_t)w;
        c = (w > 0xff);
    }
    v[0] = arg1->value[0] > 0x7f ? (ASN1octet_t)(0xff + c) : (ASN1octet_t)c;

    /* octets which may shall dropped */
    w = v[0] > 0x7f ? 0xff : 0x00;

    /* count octets that shall be dropped */
    for (i = 0; i < l - 1; i++) {
        if (v[i] != w)
            break;
    }
    if ((v[i] ^ w) & 0x80)
        i--;

    /* allocate and copy result */
    dst->length = l - i;
    dst->value = (ASN1octet_t *)malloc(l - i);
    memcpy(dst->value, v + i, l - i);
    free(v);
}

/* substract one octet to an intx */
void
ASN1intx_suboctet(ASN1intx_t *dst, ASN1intx_t *arg1, ASN1octet_t arg2)
{
    ASN1octet_t *v;
    int l;
    int i;
    int c;
    int w;

    /* result length will be <= l */
    l = arg1->length + 1;

    /* allocate result */
    v = (ASN1octet_t *)malloc(l);

    /* substract octet by octet */
    c = arg2;
    for (i = l - 2; i >= 0; i--) {
        w = arg1->value[i] - c;
        v[i + 1] = (ASN1octet_t)w;
        c = (w < 0);
    }
    v[0] = arg1->value[0] > 0x7f ? (ASN1octet_t)(0xff - c) : (ASN1octet_t)c;

    /* octets which may shall dropped */
    w = v[0] > 0x7f ? 0xff : 0x00;

    /* count octets that shall be dropped */
    for (i = 0; i < l - 1; i++) {
        if (v[i] != w)
            break;
    }
    if ((v[i] ^ w) & 0x80)
        i--;

    /* allocate and copy result */
    dst->length = l - i;
    dst->value = (ASN1octet_t *)malloc(l - i);
    memcpy(dst->value, v + i, l - i);
    free(v);
}

/* multiply intx by an octet */
void
ASN1intx_muloctet(ASN1intx_t *dst, ASN1intx_t *arg1, ASN1octet_t arg2)
{
    ASN1octet_t *v;
    int l;
    int c;
    int i;
    int w;

    /* result length will be <= l */
    l = arg1->length + 1;

    /* allocate result */
    v = (ASN1octet_t *)malloc(l);

    /* multiply octet by octet */
    c = 0;
    for (i = l - 2; i >= 0; i--) {
        w = arg1->value[i] * arg2 + c;
        v[i + 1] = (ASN1octet_t)w;
        c = w >> 8;
    }
    v[0] = (ASN1octet_t)(arg1->value[0] > 0x7f ? 0xff * arg2 + c : c);

    /* octets which may shall dropped */
    w = v[0] > 0x7f ? 0xff : 0x00;

    /* count octets that shall be dropped */
    for (i = 0; i < l - 1; i++) {
        if (v[i] != w)
            break;
    }
    if ((v[i] ^ w) & 0x80)
        i--;

    /* allocate and copy result */
    dst->length = l - i;
    dst->value = (ASN1octet_t *)malloc(l - i);
    memcpy(dst->value, v + i, l - i);
    free(v);
}

/* increment an intx */
void
ASN1intx_inc(ASN1intx_t *val)
{
    ASN1octet_t *v;
    int l;
    int i;
    int w;

    /* result length will be <= l */
    l = val->length + 1;

    /* allocate result */
    v = (ASN1octet_t *)malloc(l);

    /* copy value */
    memcpy(v + 1, val->value, l - 1);
    free(val->value);
    v[0] = v[1] > 0x7f ? 0xff : 0x00;

    /* increment value */
    for (i = l - 1; i >= 0; i--) {
        if (++v[i])
            break;
    }

    /* octets which may shall dropped */
    w = v[0] > 0x7f ? 0xff : 0x00;

    /* count octets that shall be dropped */
    for (i = 0; i < l - 1; i++) {
        if (v[i] != w)
            break;
    }
    if ((v[i] ^ w) & 0x80)
        i--;

    /* allocate and copy result */
    val->length = l - i;
    val->value = (ASN1octet_t *)malloc(l - i);
    memcpy(val->value, v + i, l - i);
    free(v);
}

/* decrement an intx */
void
ASN1intx_dec(ASN1intx_t *val)
{
    ASN1octet_t *v;
    int l;
    int i;
    int w;

    /* result length will be <= l */
    l = val->length + 1;

    /* allocate result */
    v = (ASN1octet_t *)malloc(l);

    /* copy value */
    memcpy(v + 1, val->value, l - 1);
    free(val->value);
    v[0] = v[1] > 0x7f ? 0xff : 0x00;

    /* decrement value */
    for (i = l - 1; i >= 0; i--) {
        if (v[i]--)
            break;
    }

    /* octets which may shall dropped */
    w = v[0] > 0x7f ? 0xff : 0x00;

    /* count octets that shall be dropped */
    for (i = 0; i < l - 1; i++) {
        if (v[i] != w)
            break;
    }
    if ((v[i] ^ w) & 0x80)
        i--;

    /* allocate and copy result */
    val->length = l - i;
    val->value = (ASN1octet_t *)malloc(l - i);
    memcpy(val->value, v + i, l - i);
    free(v);
}

/* negate an intx value */
void
ASN1intx_neg(ASN1intx_t *dst, ASN1intx_t *arg)
{
    ASN1uint32_t i;

    /* duplicate value */
    ASN1intx_dup(dst, arg);

    /* ones complement */
    for (i = 0; i < dst->length; i++)
        dst->value[i] = ~dst->value[i];
    
    /* and increment */
    ASN1intx_inc(dst);
}

/* returns floor(log2(arg - 1)) */
ASN1uint32_t
ASN1intx_log2(ASN1intx_t *arg)
{
    ASN1uint32_t i;
    ASN1intx_t v;
    ASN1uint32_t n;

    ASN1intx_dup(&v, arg);
    ASN1intx_dec(&v);
    if (v.value[0] > 0x7f) {
        ASN1intx_free(&v);
        return 0;
    }
    for (i = 0; i < v.length; i++) {
        if (v.value[i])
            break;
    }
    if (i >= v.length) {
        n = 0;
    } else if (v.value[i] > 0x7f) {
        n = 8 * (v.length - i - 1) + 8;
    } else if (v.value[i] > 0x3f) {
        n = 8 * (v.length - i - 1) + 7;
    } else if (v.value[i] > 0x1f) {
        n = 8 * (v.length - i - 1) + 6;
    } else if (v.value[i] > 0x0f) {
        n = 8 * (v.length - i - 1) + 5;
    } else if (v.value[i] > 0x07) {
        n = 8 * (v.length - i - 1) + 4;
    } else if (v.value[i] > 0x03) {
        n = 8 * (v.length - i - 1) + 3;
    } else if (v.value[i] > 0x01) {
        n = 8 * (v.length - i - 1) + 2;
    } else {
        n = 8 * (v.length - i - 1) + 1;
    }
    ASN1intx_free(&v);
    return n;
}

/* returns floor(log2(arg - 1)) */
ASN1uint32_t
ASN1uint32_log2(ASN1uint32_t arg)
{
    ASN1uint32_t i;

    arg--;
    for (i = 32; i != 0; i--) {
        if (arg & (1 << (i - 1)))
            break;
    }
    return i;
}

/* returns floor(log256(arg - 1)) */
ASN1uint32_t
ASN1intx_log256(ASN1intx_t *arg)
{
    ASN1uint32_t i;
    ASN1intx_t v;

    ASN1intx_dup(&v, arg);
    ASN1intx_dec(&v);
    if (v.value[0] > 0x7f) {
        ASN1intx_free(&v);
        return 0;
    }
    for (i = 0; i < v.length; i++) {
        if (v.value[i])
            break;
    }
    ASN1intx_free(&v);
    return v.length - i;
}

/* returns floor(log256(arg - 1)) */
ASN1uint32_t
ASN1uint32_log256(ASN1uint32_t arg)
{
    if (arg > 0x10000) {
        if (arg > 0x1000000)
            return 4;
        return 3;
    }
    if (arg > 0x100)
        return 2;
    if (arg > 1)
        return 1;
    return 0;
}

/* compare two intx values; return 0 iff equal */
ASN1int32_t
ASN1intx_cmp(ASN1intx_t *arg1, ASN1intx_t *arg2)
{
    int s1, s2;
    int o1, o2;
    int l;
    int i;
    int d;

    s1 = arg1->value[0] > 0x7f ? 0xff : 0x00;
    s2 = arg2->value[0] > 0x7f ? 0xff : 0x00;
    if (s1 != s2)
        return s1 == 0xff ? -1 : 1;
    l = arg1->length > arg2->length ? arg1->length : arg2->length;
    o1 = l - arg1->length;
    o2 = l - arg2->length;
    for (i = 0; i < l; i++) {
        d = (i >= o1 ? arg1->value[i - o1] : s1) - (i >= o2 ? arg2->value[i - o2] : s2);
        if (d)
            return d;
    }
    return 0;
}

/* create an intx value from an uint32 value */
void
ASN1intx_setuint32(ASN1intx_t *dst, ASN1uint32_t val)
{
    ASN1octet_t o[5], *v = o;
    int n = 5;
    v[0] = 0;
    v[1] = (ASN1octet_t)(val >> 24);
    v[2] = (ASN1octet_t)(val >> 16);
    v[3] = (ASN1octet_t)(val >> 8);
    v[4] = (ASN1octet_t)(val);
    while (n > 1 && !*v && v[1] <= 0x7f) {
        n--;
        v++;
    }
    dst->length = n;
    dst->value = (ASN1octet_t *)malloc(n);
    memcpy(dst->value, v, n);
}

/* create an intx value from an int32 value */
void
ASN1intx_setint32(ASN1intx_t *dst, ASN1int32_t val)
{
    ASN1octet_t o[5], *v = o;
    int n = 5;
    v[0] = (ASN1octet_t)(val < 0 ? 0xff : 0x00);
    v[1] = (ASN1octet_t)(val >> 24);
    v[2] = (ASN1octet_t)(val >> 16);
    v[3] = (ASN1octet_t)(val >> 8);
    v[4] = (ASN1octet_t)(val);
    while (n > 1 && ((!*v && v[1] <= 0x7f) || (*v == 0xff && v[1] > 0x7f))) {
        n--;
        v++;
    }
    dst->length = n;
    dst->value = (ASN1octet_t *)malloc(n);
    memcpy(dst->value, v, n);
}

/* copy constructor */
void
ASN1intx_dup(ASN1intx_t *dst, ASN1intx_t *val)
{
    dst->length = val->length;
    dst->value = (ASN1octet_t *)malloc(val->length);
    memcpy(dst->value, val->value, val->length);
}

/* free an intx value */
void
ASN1intx_free(ASN1intx_t *val)
{
    free(val->value);
}

#ifdef HAS_SIXTYFOUR_BITS
/* convert an intx value to a uint64 value */
ASN1uint64_t
ASN1intx2uint64(ASN1intx_t *val)
{
    switch (val->length) {
    case 1:
        return (ASN1uint64_t)val->value[val->length - 1];
    case 2:
        return (ASN1uint64_t)(val->value[val->length - 1] |
            ((ASN1uint32_t)val->value[val->length - 2] << 8));
    case 3:
        return (ASN1uint64_t)(val->value[val->length - 1] |
            ((ASN1uint32_t)val->value[val->length - 2] << 8) |
            ((ASN1uint32_t)val->value[val->length - 3] << 16));
    case 4:
        return (ASN1uint64_t)(val->value[val->length - 1] |
            ((ASN1uint32_t)val->value[val->length - 2] << 8) |
            ((ASN1uint32_t)val->value[val->length - 3] << 16) |
            ((ASN1uint32_t)val->value[val->length - 4] << 24));
    case 5:
        return (ASN1uint64_t)(val->value[val->length - 1] |
            ((ASN1uint64_t)val->value[val->length - 2] << 8) |
            ((ASN1uint64_t)val->value[val->length - 3] << 16) |
            ((ASN1uint64_t)val->value[val->length - 4] << 24) |
            ((ASN1uint64_t)val->value[val->length - 5] << 32));
    case 6:
        return (ASN1uint64_t)(val->value[val->length - 1] |
            ((ASN1uint64_t)val->value[val->length - 2] << 8) |
            ((ASN1uint64_t)val->value[val->length - 3] << 16) |
            ((ASN1uint64_t)val->value[val->length - 4] << 24) |
            ((ASN1uint64_t)val->value[val->length - 5] << 32) |
            ((ASN1uint64_t)val->value[val->length - 6] << 40));
    case 7:
        return (ASN1uint64_t)(val->value[val->length - 1] |
            ((ASN1uint64_t)val->value[val->length - 2] << 8) |
            ((ASN1uint64_t)val->value[val->length - 3] << 16) |
            ((ASN1uint64_t)val->value[val->length - 4] << 24) |
            ((ASN1uint64_t)val->value[val->length - 5] << 32) |
            ((ASN1uint64_t)val->value[val->length - 6] << 40) |
            ((ASN1uint64_t)val->value[val->length - 7] << 48));
    default:
        return (ASN1uint64_t)(val->value[val->length - 1] |
            ((ASN1uint64_t)val->value[val->length - 2] << 8) |
            ((ASN1uint64_t)val->value[val->length - 3] << 16) |
            ((ASN1uint64_t)val->value[val->length - 4] << 24) |
            ((ASN1uint64_t)val->value[val->length - 5] << 32) |
            ((ASN1uint64_t)val->value[val->length - 6] << 40) |
            ((ASN1uint64_t)val->value[val->length - 7] << 48) |
            ((ASN1uint64_t)val->value[val->length - 8] << 56));
    }
}
#endif

/* check if intx value is a uint64 value */
int
ASN1intxisuint64(ASN1intx_t *val)
{
    if (val->value[0] > 0x7f)
        return 0;
    return ASN1intx_uoctets(val) <= 8;
}

#ifdef HAS_SIXTYFOUR_BITS
/* convert an intx value to a int64 value */
ASN1int64_t
ASN1intx2int64(ASN1intx_t *val)
{
    switch (val->length) {
    case 1:
        return (ASN1int64_t)(ASN1int8_t)val->value[val->length - 1];
    case 2:
        return (ASN1int64_t)(ASN1int16_t)(val->value[val->length - 1] |
            ((ASN1uint32_t)val->value[val->length - 2] << 8));
    case 3:
        return (ASN1int64_t)(ASN1int32_t)(val->value[val->length - 1] |
            ((ASN1uint32_t)val->value[val->length - 2] << 8) |
            ((ASN1uint32_t)val->value[val->length - 3] << 16) |
            ((ASN1uint32_t)val->value[val->length - 3] > 0x7f ?
            0xffffffffff000000LL : 0));
    case 4:
        return (ASN1int64_t)(ASN1int32_t)(val->value[val->length - 1] |
            ((ASN1uint32_t)val->value[val->length - 2] << 8) |
            ((ASN1uint32_t)val->value[val->length - 3] << 16) |
            ((ASN1uint32_t)val->value[val->length - 4] << 24));
    case 5:
        return (ASN1int64_t)(val->value[val->length - 1] |
            ((ASN1uint64_t)val->value[val->length - 2] << 8) |
            ((ASN1uint64_t)val->value[val->length - 3] << 16) |
            ((ASN1uint64_t)val->value[val->length - 4] << 24) |
            ((ASN1uint64_t)val->value[val->length - 5] << 32) |
            ((ASN1uint64_t)val->value[val->length - 5] > 0x7f ?
            0xffffff0000000000LL : 0));
    case 6:
        return (ASN1int64_t)(val->value[val->length - 1] |
            ((ASN1uint64_t)val->value[val->length - 2] << 8) |
            ((ASN1uint64_t)val->value[val->length - 3] << 16) |
            ((ASN1uint64_t)val->value[val->length - 4] << 24) |
            ((ASN1uint64_t)val->value[val->length - 5] << 32) |
            ((ASN1uint64_t)val->value[val->length - 6] << 40) |
            ((ASN1uint64_t)val->value[val->length - 6] > 0x7f ?
            0xffff000000000000LL : 0));
    case 7:
        return (ASN1int64_t)((ASN1uint64_t)val->value[val->length - 1] |
            ((ASN1uint64_t)val->value[val->length - 2] << 8) |
            ((ASN1uint64_t)val->value[val->length - 3] << 16) |
            ((ASN1uint64_t)val->value[val->length - 4] << 24) |
            ((ASN1uint64_t)val->value[val->length - 5] << 32) |
            ((ASN1uint64_t)val->value[val->length - 6] << 40) |
            ((ASN1uint64_t)val->value[val->length - 7] << 48) |
            ((ASN1uint64_t)val->value[val->length - 7] > 0x7f ?
            0xff00000000000000LL : 0));
    default:
        return (ASN1int64_t)((ASN1uint64_t)val->value[val->length - 1] |
            ((ASN1uint64_t)val->value[val->length - 2] << 8) |
            ((ASN1uint64_t)val->value[val->length - 3] << 16) |
            ((ASN1uint64_t)val->value[val->length - 4] << 24) |
            ((ASN1uint64_t)val->value[val->length - 5] << 32) |
            ((ASN1uint64_t)val->value[val->length - 6] << 40) |
            ((ASN1uint64_t)val->value[val->length - 7] << 48) |
            ((ASN1uint64_t)val->value[val->length - 8] << 56));
    }
}
#endif

/* check if intx value is an int64 value */
int
ASN1intxisint64(ASN1intx_t *val)
{
    return ASN1intx_octets(val) <= 8;
}

/* convert intx value to uint32 value */
ASN1uint32_t
ASN1intx2uint32(ASN1intx_t *val)
{
    switch (val->length) {
    case 1:
        return (ASN1uint32_t)val->value[val->length - 1];
    case 2:
        return (ASN1uint32_t)(val->value[val->length - 1] |
            ((ASN1uint32_t)val->value[val->length - 2] << 8));
    case 3:
        return (ASN1uint32_t)(val->value[val->length - 1] |
            ((ASN1uint32_t)val->value[val->length - 2] << 8) |
            ((ASN1uint32_t)val->value[val->length - 3] << 16));
    default:
        return (ASN1uint32_t)(val->value[val->length - 1] |
            ((ASN1uint32_t)val->value[val->length - 2] << 8) |
            ((ASN1uint32_t)val->value[val->length - 3] << 16) |
            ((ASN1uint32_t)val->value[val->length - 4] << 24));
    }
}

/* check if intx value is an uint32 value */
int
ASN1intxisuint32(ASN1intx_t *val)
{
    if (val->value[0] > 0x7f)
        return 0;
    return ASN1intx_uoctets(val) <= 4;
}

/* convert intx value to int32 value */
ASN1int32_t
ASN1intx2int32(ASN1intx_t *val)
{
    switch (val->length) {
    case 1:
        return (ASN1int32_t)(ASN1int8_t)val->value[val->length - 1];
    case 2:
        return (ASN1int32_t)(ASN1int16_t)(val->value[val->length - 1] |
            ((ASN1uint32_t)val->value[val->length - 2] << 8));
    case 3:
        return (ASN1int32_t)(val->value[val->length - 1] |
            ((ASN1uint32_t)val->value[val->length - 2] << 8) |
            ((ASN1uint32_t)val->value[val->length - 3] << 16) |
            ((ASN1uint32_t)val->value[val->length - 3] > 0x7f ?
            0xff000000 : 0));
    default:
        return (ASN1int32_t)(val->value[val->length - 1] |
            ((ASN1uint32_t)val->value[val->length - 2] << 8) |
            ((ASN1uint32_t)val->value[val->length - 3] << 16) |
            ((ASN1uint32_t)val->value[val->length - 4] << 24));
    }
}

/* check if intx value is an int32 value */
int
ASN1intxisint32(ASN1intx_t *val)
{
    return ASN1intx_octets(val) <= 4;
}

/* convert intx value to uint16 value */
ASN1uint16_t
ASN1intx2uint16(ASN1intx_t *val)
{
    if (val->length == 1)
        return (ASN1uint16_t)val->value[val->length - 1];
    return (ASN1uint16_t)(val->value[val->length - 1] |
        ((ASN1uint32_t)val->value[val->length - 2] << 8));
}

/* check if intx value is an uint16 value */
int
ASN1intxisuint16(ASN1intx_t *val)
{
    if (val->value[0] > 0x7f)
        return 0;
    return ASN1intx_uoctets(val) <= 2;
}

/* convert intx value to int16 value */
ASN1int16_t
ASN1intx2int16(ASN1intx_t *val)
{
    if (val->length == 1)
        return (ASN1int16_t)(ASN1int8_t)val->value[val->length - 1];
    return (ASN1int16_t)(val->value[val->length - 1] |
        ((ASN1uint32_t)val->value[val->length - 2] << 8));
}

/* check if intx value is an int16 value */
int
ASN1intxisint16(ASN1intx_t *val)
{
    return ASN1intx_octets(val) <= 2;
}

/* convert intx value to uint8 value */
ASN1uint8_t
ASN1intx2uint8(ASN1intx_t *val)
{
    return (ASN1uint8_t)val->value[val->length - 1];
}

/* check if intx value is an uint8 value */
int
ASN1intxisuint8(ASN1intx_t *val)
{
    if (val->value[0] > 0x7f)
        return 0;
    return ASN1intx_uoctets(val) <= 1;
}

/* convert intx value to int8 value */
ASN1int8_t
ASN1intx2int8(ASN1intx_t *val)
{
    return (ASN1int8_t)val->value[val->length - 1];
}

/* check if intx value is an int8 value */
int
ASN1intxisint8(ASN1intx_t *val)
{
    return ASN1intx_octets(val) <= 1;
}

/* count octets for a signed encoding of an intx value */
ASN1uint32_t
ASN1intx_octets(ASN1intx_t *val)
{
    ASN1uint32_t i;
    ASN1uint32_t s;

    s = val->value[0] > 0x7f ? 0xff : 0x00;
    for (i = 0; i < val->length; i++) {
        if (val->value[i] != s)
            break;
    }
    if (i && ((val->value[i] ^ s) & 0x80))
        i--;
    return val->length - i;
}

/* count octets for unsigned encoding of an unsigned intx value */
ASN1uint32_t
ASN1intx_uoctets(ASN1intx_t *val)
{
    ASN1uint32_t i;

    for (i = 0; i < val->length; i++) {
        if (val->value[i])
            break;
    }
    return val->length - i;
}

/* count octets for signed encoding of an uint32 value */
ASN1uint32_t
ASN1uint32_octets(ASN1uint32_t val)
{
    if (val >= 0x8000) {
        if (val >= 0x800000) {
            if (val >= 0x80000000)
                return 5;
            return 4;
        }
        return 3;
    }
    if (val >= 0x80)
        return 2;
    return 1;
}

/* count octets for unsigned encoding of an uint32 value */
ASN1uint32_t
ASN1uint32_uoctets(ASN1uint32_t val)
{
    if (val >= 0x10000) {
        if (val >= 0x1000000)
            return 4;
        return 3;
    }
    if (val >= 0x100)
        return 2;
    return 1;
}

/* count octets for signed encoding of an int32 value */
ASN1uint32_t
ASN1int32_octets(ASN1int32_t val)
{
    if (val >= 0) {
        if (val >= 0x8000) {
            if (val >= 0x800000)
                return 4;
            return 3;
        }
        if (val >= 0x80)
            return 2;
        return 1;
    }
    if (val < -0x8000) {
        if (val < -0x800000)
            return 4;
        return 3;
    }
    if (val < -0x80)
        return 2;
    return 1;
}

/* convert an intx value into a double */
double
ASN1intx2double(ASN1intx_t *val)
{
    double ret;
    ASN1uint32_t i;

    if (val->value[0] > 0x7f)
        ret = (double)(val->value[0] - 0x100);
    else
        ret = (double)val->value[0];
    for (i = 1; i < val->length; i++) {
        ret = ret * 256.0 + (double)val->value[i];
    }
    return ret;
}