+
+int
+asn_INTEGER2long(const INTEGER_t *iptr, long *l) {
+    intmax_t v;
+    if(asn_INTEGER2imax(iptr, &v) == 0) {
+        if(v < LONG_MIN || v > LONG_MAX) {
+            errno = ERANGE;
+            return -1;
+        }
+        *l = v;
+        return 0;
+    } else {
+        return -1;
+    }
+}
+
+int
+asn_INTEGER2ulong(const INTEGER_t *iptr, unsigned long *l) {
+    uintmax_t v;
+    if(asn_INTEGER2umax(iptr, &v) == 0) {
+        if(v > ULONG_MAX) {
+            errno = ERANGE;
+            return -1;
+        }
+        *l = v;
+        return 0;
+    } else {
+        return -1;
+    }
+}
+
+int
+asn_long2INTEGER(INTEGER_t *st, long value) {
+    return asn_imax2INTEGER(st, value);
+}
+
+int
+asn_ulong2INTEGER(INTEGER_t *st, unsigned long value) {
+    return asn_imax2INTEGER(st, value);
+}
+
+/*
+ * Parse the number in the given string until the given *end position,
+ * returning the position after the last parsed character back using the
+ * same (*end) pointer.
+ * WARNING: This behavior is different from the standard strtol/strtoimax(3).
+ */
+enum asn_strtox_result_e
+asn_strtoimax_lim(const char *str, const char **end, intmax_t *intp) {
+    int sign = 1;
+    intmax_t value;
+
+    const intmax_t asn1_intmax_max = ((~(uintmax_t)0) >> 1);
+    const intmax_t upper_boundary = asn1_intmax_max / 10;
+    intmax_t last_digit_max = asn1_intmax_max % 10;
+
+    if(str >= *end) return ASN_STRTOX_ERROR_INVAL;
+
+    switch(*str) {
+    case '-':
+        last_digit_max++;
+        sign = -1;
+        /* FALL THROUGH */
+    case '+':
+        str++;
+        if(str >= *end) {
+            *end = str;
+            return ASN_STRTOX_EXPECT_MORE;
+        }
+    }
+
+    for(value = 0; str < (*end); str++) {
+        if(*str >= 0x30 && *str <= 0x39) {
+            int d = *str - '0';
+            if(value < upper_boundary) {
+                value = value * 10 + d;
+            } else if(value == upper_boundary) {
+                if(d <= last_digit_max) {
+                    if(sign > 0) {
+                        value = value * 10 + d;
+                    } else {
+                        sign = 1;
+                        value = -value * 10 - d;
+                    }
+                    str += 1;
+                    if(str < *end) {
+                        // If digits continue, we're guaranteed out of range.
+                        *end = str;
+                        if(*str >= 0x30 && *str <= 0x39) {
+                            return ASN_STRTOX_ERROR_RANGE;
+                        } else {
+                            *intp = sign * value;
+                            return ASN_STRTOX_EXTRA_DATA;
+                        }
+                    }
+                    break;
+                } else {
+                    *end = str;
+                    return ASN_STRTOX_ERROR_RANGE;
+                }
+            } else {
+                *end = str;
+                return ASN_STRTOX_ERROR_RANGE;
+            }
+        } else {
+            *end = str;
+            *intp = sign * value;
+            return ASN_STRTOX_EXTRA_DATA;
+        }
+    }
+
+    *end = str;
+    *intp = sign * value;
+    return ASN_STRTOX_OK;
+}
+
+/*
+ * Parse the number in the given string until the given *end position,
+ * returning the position after the last parsed character back using the
+ * same (*end) pointer.
+ * WARNING: This behavior is different from the standard strtoul/strtoumax(3).
+ */
+enum asn_strtox_result_e
+asn_strtoumax_lim(const char *str, const char **end, uintmax_t *uintp) {
+    uintmax_t value;
+
+    const uintmax_t asn1_uintmax_max = ((~(uintmax_t)0));
+    const uintmax_t upper_boundary = asn1_uintmax_max / 10;
+    uintmax_t last_digit_max = asn1_uintmax_max % 10;
+
+    if(str >= *end) return ASN_STRTOX_ERROR_INVAL;
+
+    switch(*str) {
+    case '-':
+        return ASN_STRTOX_ERROR_INVAL;
+    case '+':
+        str++;
+        if(str >= *end) {
+            *end = str;
+            return ASN_STRTOX_EXPECT_MORE;
+        }
+    }
+
+    for(value = 0; str < (*end); str++) {
+        if(*str >= 0x30 && *str <= 0x39) {
+            unsigned int d = *str - '0';
+            if(value < upper_boundary) {
+                value = value * 10 + d;
+            } else if(value == upper_boundary) {
+                if(d <= last_digit_max) {
+                    value = value * 10 + d;
+                    str += 1;
+                    if(str < *end) {
+                        // If digits continue, we're guaranteed out of range.
+                        *end = str;
+                        if(*str >= 0x30 && *str <= 0x39) {
+                            return ASN_STRTOX_ERROR_RANGE;
+                        } else {
+                            *uintp = value;
+                            return ASN_STRTOX_EXTRA_DATA;
+                        }
+                    }
+                    break;
+                } else {
+                    *end = str;
+                    return ASN_STRTOX_ERROR_RANGE;
+                }
+            } else {
+                *end = str;
+                return ASN_STRTOX_ERROR_RANGE;
+            }
+        } else {
+            *end = str;
+            *uintp = value;
+            return ASN_STRTOX_EXTRA_DATA;
+        }
+    }
+
+    *end = str;
+    *uintp = value;
+    return ASN_STRTOX_OK;
+}
+
+enum asn_strtox_result_e
+asn_strtol_lim(const char *str, const char **end, long *lp) {
+    intmax_t value;
+    switch(asn_strtoimax_lim(str, end, &value)) {
+    case ASN_STRTOX_ERROR_RANGE:
+        return ASN_STRTOX_ERROR_RANGE;
+    case ASN_STRTOX_ERROR_INVAL:
+        return ASN_STRTOX_ERROR_INVAL;
+    case ASN_STRTOX_EXPECT_MORE:
+        return ASN_STRTOX_EXPECT_MORE;
+    case ASN_STRTOX_OK:
+        if(value >= LONG_MIN && value <= LONG_MAX) {
+            *lp = value;
+            return ASN_STRTOX_OK;
+        } else {
+            return ASN_STRTOX_ERROR_RANGE;
+        }
+    case ASN_STRTOX_EXTRA_DATA:
+        if(value >= LONG_MIN && value <= LONG_MAX) {
+            *lp = value;
+            return ASN_STRTOX_EXTRA_DATA;
+        } else {
+            return ASN_STRTOX_ERROR_RANGE;
+        }
+    }
+
+    assert(!"Unreachable");
+    return ASN_STRTOX_ERROR_INVAL;
+}
+
+enum asn_strtox_result_e
+asn_strtoul_lim(const char *str, const char **end, unsigned long *ulp) {
+    uintmax_t value;
+    switch(asn_strtoumax_lim(str, end, &value)) {
+    case ASN_STRTOX_ERROR_RANGE:
+        return ASN_STRTOX_ERROR_RANGE;
+    case ASN_STRTOX_ERROR_INVAL:
+        return ASN_STRTOX_ERROR_INVAL;
+    case ASN_STRTOX_EXPECT_MORE:
+        return ASN_STRTOX_EXPECT_MORE;
+    case ASN_STRTOX_OK:
+        if(value <= ULONG_MAX) {
+            *ulp = value;
+            return ASN_STRTOX_OK;
+        } else {
+            return ASN_STRTOX_ERROR_RANGE;
+        }
+    case ASN_STRTOX_EXTRA_DATA:
+        if(value <= ULONG_MAX) {
+            *ulp = value;
+            return ASN_STRTOX_EXTRA_DATA;
+        } else {
+            return ASN_STRTOX_ERROR_RANGE;
+        }
+    }
+
+    assert(!"Unreachable");
+    return ASN_STRTOX_ERROR_INVAL;
+}
+
+int
+INTEGER_compare(const asn_TYPE_descriptor_t *td, const void *aptr,
+                     const void *bptr) {
+    const INTEGER_t *a = aptr;
+    const INTEGER_t *b = bptr;
+
+    (void)td;
+
+    if(a && b) {
+        if(a->size && b->size) {
+            int sign_a = (a->buf[0] & 0x80) ? -1 : 1;
+            int sign_b = (b->buf[0] & 0x80) ? -1 : 1;
+
+            if(sign_a < sign_b) return -1;
+            if(sign_a > sign_b) return 1;
+
+            /* The shortest integer wins, unless comparing negatives */
+            if(a->size < b->size) {
+                return -1 * sign_a;
+            } else if(a->size > b->size) {
+                return 1 * sign_b;
+            }
+
+            return sign_a * memcmp(a->buf, b->buf, a->size);
+        } else if(a->size) {
+            int sign = (a->buf[0] & 0x80) ? -1 : 1;
+            return (1) * sign;
+        } else if(b->size) {
+            int sign = (a->buf[0] & 0x80) ? -1 : 1;
+            return (-1) * sign;
+        } else {
+            return 0;
+        }
+    } else if(!a && !b) {
+        return 0;
+    } else if(!a) {
+        return -1;
+    } else {
+        return 1;
+    }
+
+}
+
+asn_random_fill_result_t
+INTEGER_random_fill(const asn_TYPE_descriptor_t *td, void **sptr,
+                    const asn_encoding_constraints_t *constraints,
+                    size_t max_length) {
+    const asn_INTEGER_specifics_t *specs =
+        (const asn_INTEGER_specifics_t *)td->specifics;
+    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};
+    INTEGER_t *st = *sptr;
+    const asn_INTEGER_enum_map_t *emap;
+    size_t emap_len;
+    intmax_t value;
+    int find_inside_map;
+
+    if(max_length == 0) return result_skipped;
+
+    if(st == NULL) {
+        st = (INTEGER_t *)CALLOC(1, sizeof(*st));
+        if(st == NULL) {
+            return result_failed;
+        }
+    }
+
+    if(specs) {
+        emap = specs->value2enum;
+        emap_len = specs->map_count;
+        if(specs->strict_enumeration) {
+            find_inside_map = emap_len > 0;
+        } else {
+            find_inside_map = emap_len ? asn_random_between(0, 1) : 0;
+        }
+    } else {
+        emap = 0;
+        emap_len = 0;
+        find_inside_map = 0;
+    }
+
+    if(find_inside_map) {
+        assert(emap_len > 0);
+        value = emap[asn_random_between(0, emap_len - 1)].nat_value;
+    } else {
+        const asn_per_constraints_t *ct;
+
+        static const long variants[] = {
+            -65536, -65535, -65534, -32769, -32768, -32767, -16385, -16384,
+            -16383, -257,   -256,   -255,   -254,   -129,   -128,   -127,
+            -126,   -1,     0,      1,      126,    127,    128,    129,
+            254,    255,    256,    257,    16383,  16384,  16385,  32767,
+            32768,  32769,  65534,  65535,  65536,  65537};
+        if(specs && specs->field_unsigned) {
+            assert(variants[18] == 0);
+            value = variants[asn_random_between(
+                18, sizeof(variants) / sizeof(variants[0]) - 1)];
+        } else {
+            value = variants[asn_random_between(
+                0, sizeof(variants) / sizeof(variants[0]) - 1)];
+        }
+
+        if(!constraints) constraints = &td->encoding_constraints;
+        ct = constraints ? constraints->per_constraints : 0;
+        if(ct && (ct->value.flags & APC_CONSTRAINED)) {
+            if(value < ct->value.lower_bound || value > ct->value.upper_bound) {
+                value = asn_random_between(ct->value.lower_bound,
+                                           ct->value.upper_bound);
+            }
+        }
+    }
+
+    if(asn_imax2INTEGER(st, value)) {
+        if(st == *sptr) {
+            ASN_STRUCT_RESET(*td, st);
+        } else {
+            ASN_STRUCT_FREE(*td, st);
+        }
+        return result_failed;
+    } else {
+        *sptr = st;
+        result_ok.length = st->size;
+        return result_ok;
+    }
+}