Initial version of asn1c
[com/asn1c.git] / libasn1compiler / asn1c_constraint.c
diff --git a/libasn1compiler/asn1c_constraint.c b/libasn1compiler/asn1c_constraint.c
new file mode 100644 (file)
index 0000000..0242321
--- /dev/null
@@ -0,0 +1,765 @@
+#include "asn1c_internal.h"
+#include "asn1c_constraint.h"
+#include "asn1c_misc.h"
+#include "asn1c_out.h"
+#include "asn1c_naming.h"
+
+#include <asn1fix_crange.h>    /* constraint groker from libasn1fix */
+#include <asn1fix_export.h>    /* other exportables from libasn1fix */
+
+static int asn1c_emit_constraint_tables(arg_t *arg, int got_size);
+static int emit_alphabet_check_loop(arg_t *arg, asn1cnst_range_t *range);
+static int emit_value_determination_code(arg_t *arg, asn1p_expr_type_e etype, asn1cnst_range_t *r_value);
+static int emit_size_determination_code(arg_t *arg, asn1p_expr_type_e etype);
+static asn1p_expr_type_e _find_terminal_type(arg_t *arg);
+static abuf *emit_range_comparison_code(asn1cnst_range_t *range,
+                                          const char *varname,
+                                          asn1c_integer_t natural_start,
+                                          asn1c_integer_t natural_stop);
+static int native_long_sign(arg_t *arg, asn1cnst_range_t *r);  /* -1, 0, 1 */
+
+static int
+ulong_optimization(arg_t *arg, asn1p_expr_type_e etype, asn1cnst_range_t *r_size,
+                                               asn1cnst_range_t *r_value)
+{
+       return (!r_size && r_value
+               && (etype == ASN_BASIC_INTEGER
+               || etype == ASN_BASIC_ENUMERATED)
+               && native_long_sign(arg, r_value) == 0);
+}
+
+int
+asn1c_emit_constraint_checking_code(arg_t *arg) {
+       asn1cnst_range_t *r_size;
+       asn1cnst_range_t *r_value;
+       asn1p_expr_t *expr = arg->expr;
+       asn1p_expr_type_e etype;
+       asn1p_constraint_t *ct;
+       int alphabet_table_compiled;
+       int produce_st = 0;
+       int ulong_optimize = 0;
+       int value_unsigned = 0;
+       int ret = 0;
+
+       ct = expr->combined_constraints;
+       if(ct == NULL)
+               return 1;       /* No additional constraints defined */
+
+       etype = _find_terminal_type(arg);
+
+       r_value=asn1constraint_compute_constraint_range(expr->Identifier, etype, ct, ACT_EL_RANGE,0,0,0);
+       r_size =asn1constraint_compute_constraint_range(expr->Identifier, etype, ct, ACT_CT_SIZE, 0,0,0);
+       if(r_value) {
+               if(r_value->incompatible
+               || r_value->empty_constraint
+               || (r_value->left.type == ARE_MIN
+                       && r_value->right.type == ARE_MAX)
+               || (etype == ASN_BASIC_BOOLEAN
+                       && r_value->left.value == 0
+                       && r_value->right.value == 1)
+               ) {
+                       asn1constraint_range_free(r_value);
+                       r_value = 0;
+               }
+       }
+       if(r_size) {
+               if(r_size->incompatible
+               || r_size->empty_constraint
+               || (r_size->left.value == 0     /* or .type == MIN */
+                       && r_size->right.type == ARE_MAX)
+               ) {
+                       asn1constraint_range_free(r_size);
+                       r_size = 0;
+               }
+       }
+
+       /*
+        * Do we really need an "*st = sptr" pointer?
+        */
+       switch(etype) {
+       case ASN_BASIC_INTEGER:
+       case ASN_BASIC_ENUMERATED:
+               if(asn1c_type_fits_long(arg, arg->expr) == FL_NOTFIT)
+                       produce_st = 1;
+               break;
+       case ASN_BASIC_REAL:
+        if((arg->flags & A1C_USE_WIDE_TYPES)
+           && asn1c_REAL_fits(arg, arg->expr) == RL_NOTFIT)
+            produce_st = 1;
+               break;
+       case ASN_BASIC_BIT_STRING:
+       case ASN_BASIC_OCTET_STRING:
+               produce_st = 1;
+               break;
+       default:
+               if(etype & ASN_STRING_MASK)
+                       produce_st = 1;
+               break;
+       }
+       if(produce_st) {
+               const char *tname = asn1c_type_name(arg, arg->expr, TNF_SAFE);
+               OUT("const %s_t *st = (const %s_t *)sptr;\n", tname, tname);
+       }
+
+       if(r_size || r_value) {
+               if(r_size) {
+                       OUT("size_t size;\n");
+               }
+               if(r_value)
+                       switch(etype) {
+                       case ASN_BASIC_INTEGER:
+                       case ASN_BASIC_ENUMERATED:
+                               if(native_long_sign(arg, r_value) >= 0) {
+                                       ulong_optimize = ulong_optimization(arg, etype, r_size, r_value);
+                                       if(!ulong_optimize) {
+                                               value_unsigned = 1;
+                                               OUT("unsigned long value;\n");
+                                       }
+                               } else {
+                                       OUT("long value;\n");
+                               }
+                               break;
+                       case ASN_BASIC_REAL:
+                               OUT("%s value;\n", c_name(arg).type.constrained_c_name);
+                               break;
+                       case ASN_BASIC_BOOLEAN:
+                               OUT("BOOLEAN_t value;\n");
+                               break;
+                       default:
+                               break;
+               }
+       }
+
+       OUT("\n");
+
+       /*
+        * Protection against null input.
+        */
+       OUT("if(!sptr) {\n");
+               INDENT(+1);
+               OUT("ASN__CTFAIL(app_key, td, sptr,\n");
+               OUT("\t\"%%s: value not given (%%s:%%d)\",\n");
+               OUT("\ttd->name, __FILE__, __LINE__);\n");
+               OUT("return -1;\n");
+               INDENT(-1);
+       OUT("}\n");
+       OUT("\n");
+
+       if((r_value) && (!ulong_optimize))
+               emit_value_determination_code(arg, etype, r_value);
+       if(r_size)
+               emit_size_determination_code(arg, etype);
+
+       INDENT(-1);
+       REDIR(OT_CTABLES);
+       /* Emit FROM() tables */
+       alphabet_table_compiled =
+               (asn1c_emit_constraint_tables(arg, r_size?1:0) == 1);
+       REDIR(OT_CODE);
+       INDENT(+1);
+
+       /*
+        * Optimization for unsigned longs.
+        */
+       if(ulong_optimize) {
+               OUT("\n");
+               OUT("/* Constraint check succeeded */\n");
+               OUT("return 0;\n");
+               goto end;
+       }
+
+       /*
+        * Here is an if() {} else {} consrtaint checking code.
+        */
+       int got_something = 0;
+    int value_unused = 0;
+       OUT("\n");
+       OUT("if(");
+       INDENT(+1);
+               if(r_size) {
+            abuf *ab = emit_range_comparison_code(r_size, "size", 0, -1);
+            if(ab->length)  {
+                OUT("(%s)", ab->buffer);
+                got_something++;
+            }
+            abuf_free(ab);
+               }
+               if(r_value) {
+                       if(got_something) { OUT("\n"); OUT(" && "); }
+            abuf *ab;
+            if(etype == ASN_BASIC_BOOLEAN)
+                ab = emit_range_comparison_code(r_value, "value", 0, 1);
+            else
+                ab = emit_range_comparison_code(r_value, "value",
+                                                value_unsigned ? 0 : -1, -1);
+            if(ab->length)  {
+                OUT("(%s)", ab->buffer);
+                got_something++;
+            } else {
+                value_unused = 1;
+            }
+            abuf_free(ab);
+               }
+               if(alphabet_table_compiled) {
+                       if(got_something) { OUT("\n"); OUT(" && "); }
+                       OUT("!check_permitted_alphabet_%d(%s)",
+                               arg->expr->_type_unique_index,
+                               produce_st ? "st" : "sptr");
+            got_something++;
+        }
+               if(!got_something) {
+                       OUT("1 /* No applicable constraints whatsoever */");
+                       OUT(") {\n");
+                       INDENT(-1);
+                       if(produce_st) {
+                               INDENTED(OUT("(void)st; /* Unused variable */\n"));
+                       }
+                       if(value_unused) {
+                               INDENTED(OUT("(void)value; /* Unused variable */\n"));
+                       }
+                       INDENTED(OUT("/* Nothing is here. See below */\n"));
+                       OUT("}\n");
+                       OUT("\n");
+                       ret = 1;
+                       goto end;
+               }
+       INDENT(-1);
+       OUT(") {\n");
+               INDENT(+1);
+               switch(etype) {
+               case ASN_CONSTR_SEQUENCE_OF:
+               case ASN_CONSTR_SET_OF:
+                       OUT("/* Perform validation of the inner elements */\n");
+                       OUT("return td->encoding_constraints.general_constraints(td, sptr, ctfailcb, app_key);\n");
+                       break;
+               default:
+                       OUT("/* Constraint check succeeded */\n");
+                       OUT("return 0;\n");
+               }
+               INDENT(-1);
+       OUT("} else {\n");
+               INDENT(+1);
+                       OUT("ASN__CTFAIL(app_key, td, sptr,\n");
+                       OUT("\t\"%%s: constraint failed (%%s:%%d)\",\n");
+                       OUT("\ttd->name, __FILE__, __LINE__);\n");
+                       OUT("return -1;\n");
+               INDENT(-1);
+       OUT("}\n");
+
+end:
+       if (r_value) asn1constraint_range_free(r_value);
+       if (r_size) asn1constraint_range_free(r_size);
+
+       return ret;
+}
+
+static int
+asn1c_emit_constraint_tables(arg_t *arg, int got_size) {
+       asn1c_integer_t range_start;
+       asn1c_integer_t range_stop;
+       asn1p_expr_type_e etype;
+       asn1cnst_range_t *range;
+       asn1p_constraint_t *ct;
+       int utf8_full_alphabet_check = 0;
+       int max_table_size = 256;
+       int table[256];
+       int use_table;
+
+       ct = arg->expr->combined_constraints;
+       if(!ct) return 0;
+
+       etype = _find_terminal_type(arg);
+
+       range = asn1constraint_compute_constraint_range(arg->expr->Identifier, etype, ct, ACT_CT_FROM, 0,0,0);
+       if(!range) return 0;
+
+       if(range->incompatible
+       || range->empty_constraint) {
+               asn1constraint_range_free(range);
+               return 0;
+       }
+
+       if(range->left.type == ARE_MIN
+       && range->right.type == ARE_MAX) {
+               /*
+                * The permitted alphabet constraint checker code guarantees
+                * that either both bounds (left/right) are present, or
+                * they're absent simultaneously. Thus, this assertion
+                * legitimately holds true.
+                */
+               assert(range->el_count == 0);
+               /* The full range is specified. Ignore it. */
+               asn1constraint_range_free(range);
+               return 0;
+       }
+
+       range_start = range->left.value;
+       range_stop = range->right.value;
+       assert(range->left.type == ARE_VALUE);
+       assert(range->right.type == ARE_VALUE);
+       assert(range_start <= range_stop);
+
+       range_start = 0;        /* Force old behavior */
+
+       /*
+        * Check if we need a test table to check the alphabet.
+        */
+       use_table = 1;
+       if(range->el_count == 0) {
+               /*
+                * It's better to have a short if() check
+                * than waste 1k of table space
+                */
+               use_table = 0;
+       }
+       if((range_stop - range_start) > 255)
+               use_table = 0;
+       if(etype == ASN_STRING_UTF8String) {
+               if(range_stop >= 0x80)
+                       use_table = 0;
+               else
+                       max_table_size = 128;
+       }
+
+       if(use_table) {
+               int cardinal = 0;
+               int i, n = 0;
+               int untl;
+               memset(table, 0, sizeof(table));
+               for(i = -1; i < range->el_count; i++) {
+                       asn1cnst_range_t *r;
+                       asn1c_integer_t v;
+                       if(i == -1) {
+                               if(range->el_count) continue;
+                               r = range;
+                       } else {
+                               r = range->elements[i];
+                       }
+                       for(v = r->left.value; v <= r->right.value; v++) {
+                               assert((v - range_start) >= 0);
+                               assert((v - range_start) < max_table_size);
+                               table[v - range_start] = ++n;
+                       }
+               }
+
+               untl = (range_stop - range_start) + 1;
+               untl += (untl % 16)?16 - (untl % 16):0;
+               OUT("static const int permitted_alphabet_table_%d[%d] = {\n",
+                       arg->expr->_type_unique_index, max_table_size);
+               for(n = 0; n < untl; n++) {
+                       cardinal += table[n] ? 1 : 0;
+                       OUT("%2d,", table[n]);
+                       if(!((n+1) % 16)) {
+                               int c;
+                               if(!n || (n-15) + range_start >= 0x80) {
+                                       OUT("\n");
+                                       continue;
+                               }
+                               OUT("\t/* ");
+                               for(c = n - 15; c <= n; c++) {
+                                       if(table[c]) {
+                                               int a = c + range_start;
+                                               if(a > 0x20 && a < 0x80)
+                                                       OUT("%c", a);
+                                               else
+                                                       OUT(".");
+                                       } else {
+                                               OUT(" ");
+                                       }
+                               }
+                               OUT(" */");
+                               OUT("\n");
+                       }
+               }
+               OUT("};\n");
+
+               if((arg->flags & A1C_GEN_PER)
+               && (etype & ASN_STRING_KM_MASK)) {
+                   int c;
+                   OUT("static const int permitted_alphabet_code2value_%d[%d] = {\n",
+                       arg->expr->_type_unique_index, cardinal);
+                   for(n = c = 0; c < max_table_size; c++) {
+                       if(table[c]) {
+                               OUT("%d,", c);
+                               if(!((++n) % 16)) OUT("\n");
+                       }
+                   }
+                   OUT("};\n");
+                   OUT("\n");
+                   DEBUG("code2value map gen for %s", arg->expr->Identifier);
+                   arg->expr->_mark |= TM_PERFROMCT;
+               }
+
+               OUT("\n");
+       } else if(etype == ASN_STRING_UTF8String) {
+               /*
+                * UTF8String type is a special case in many respects.
+                */
+               if(got_size) {
+                       /*
+                        * Size has been already determined.
+                        * The UTF8String length checker also checks
+                        * for the syntax validity, so we don't have
+                        * to repeat this process twice.
+                        */
+                       asn1constraint_range_free(range);
+                       return 0;
+               } else {
+                       utf8_full_alphabet_check = 1;
+               }
+       } else {
+               /*
+                * This permitted alphabet check will be
+                * expressed using conditional statements
+                * instead of table lookups. Table would be
+                * to large or otherwise inappropriate (too sparse?).
+                */
+       }
+
+       OUT("static int check_permitted_alphabet_%d(const void *sptr) {\n",
+                       arg->expr->_type_unique_index);
+       INDENT(+1);
+       if(utf8_full_alphabet_check) {
+               OUT("if(UTF8String_length((const UTF8String_t *)sptr) < 0)\n");
+               OUT("\treturn -1; /* Alphabet (sic!) test failed. */\n");
+               OUT("\n");
+       } else {
+               if(use_table) {
+                       OUT("const int *table = permitted_alphabet_table_%d;\n",
+                               arg->expr->_type_unique_index);
+                       emit_alphabet_check_loop(arg, 0);
+               } else {
+                       emit_alphabet_check_loop(arg, range);
+               }
+       }
+       OUT("return 0;\n");
+       INDENT(-1);
+       OUT("}\n");
+       OUT("\n");
+
+       asn1constraint_range_free(range);
+
+       return 1;
+}
+
+static int
+emit_alphabet_check_loop(arg_t *arg, asn1cnst_range_t *range) {
+       asn1c_integer_t natural_stop;
+       asn1p_expr_t *terminal;
+       const char *tname;
+
+       terminal = asn1f_find_terminal_type_ex(arg->asn, arg->ns, arg->expr);
+       if(terminal) {
+               OUT("/* The underlying type is %s */\n",
+                       ASN_EXPR_TYPE2STR(terminal->expr_type));
+       } else {
+               terminal = arg->expr;
+       }
+       tname = asn1c_type_name(arg, terminal, TNF_SAFE);
+       OUT("const %s_t *st = (const %s_t *)sptr;\n", tname, tname);
+
+       switch(terminal->expr_type) {
+       case ASN_STRING_UTF8String:
+               OUT("const uint8_t *ch = st->buf;\n");
+               OUT("const uint8_t *end = ch + st->size;\n");
+               OUT("\n");
+               OUT("for(; ch < end; ch++) {\n");
+                       INDENT(+1);
+                       OUT("uint8_t cv = *ch;\n");
+                       if(!range) OUT("if(cv >= 0x80) return -1;\n");
+               natural_stop = 0xffffffffUL;
+               break;
+       case ASN_STRING_UniversalString:
+               OUT("const uint8_t *ch = st->buf;\n");
+               OUT("const uint8_t *end = ch + st->size;\n");
+               OUT("\n");
+               OUT("if(st->size %% 4) return -1; /* (size%%4)! */\n");
+               OUT("for(; ch < end; ch += 4) {\n");
+                       INDENT(+1);
+                       OUT("uint32_t cv = (ch[0] << 24)\n");
+                       OUT("\t\t| (ch[1] << 16)\n");
+                       OUT("\t\t| (ch[2] << 8)\n");
+                       OUT("\t\t|  ch[3];\n");
+                       if(!range) OUT("if(cv > 255) return -1;\n");
+               natural_stop = 0xffffffffUL;
+               break;
+       case ASN_STRING_BMPString:
+               OUT("const uint8_t *ch = st->buf;\n");
+               OUT("const uint8_t *end = ch + st->size;\n");
+               OUT("\n");
+               OUT("if(st->size %% 2) return -1; /* (size%%2)! */\n");
+               OUT("for(; ch < end; ch += 2) {\n");
+                       INDENT(+1);
+                       OUT("uint16_t cv = (ch[0] << 8)\n");
+                       OUT("\t\t| ch[1];\n");
+                       if(!range) OUT("if(cv > 255) return -1;\n");
+               natural_stop = 0xffff;
+               break;
+       case ASN_BASIC_OCTET_STRING:
+       default:
+               OUT("const uint8_t *ch = st->buf;\n");
+               OUT("const uint8_t *end = ch + st->size;\n");
+               OUT("\n");
+               OUT("for(; ch < end; ch++) {\n");
+                       INDENT(+1);
+                       OUT("uint8_t cv = *ch;\n");
+               natural_stop = 0xff;
+               break;
+       }
+
+       if(range) {
+        abuf *ab = emit_range_comparison_code(range, "cv", 0, natural_stop);
+        if(ab->length) {
+            OUT("if(!(%s)) return -1;\n", ab->buffer);
+        } else {
+            OUT("(void)cv; /* Unused variable */\n");
+        }
+       } else {
+               OUT("if(!table[cv]) return -1;\n");
+       }
+
+       INDENT(-1);
+       OUT("}\n");
+
+       return 0;
+}
+
+static void
+abuf_oint(abuf *ab, asn1c_integer_t v) {
+    if(v == (-2147483647L - 1)) {
+        abuf_printf(ab, "(-2147483647L - 1)");
+    } else {
+        abuf_printf(ab, "%s", asn1p_itoa(v));
+    }
+}
+
+static abuf *
+emit_range_comparison_code(asn1cnst_range_t *range, const char *varname,
+                           asn1c_integer_t natural_start,
+                           asn1c_integer_t natural_stop) {
+    abuf *ab = abuf_new();
+
+    if(range->el_count == 0) {
+        int ignore_left =
+            (range->left.type == ARE_MIN)
+            || (natural_start != -1 && range->left.value <= natural_start);
+        int ignore_right =
+            (range->right.type == ARE_MAX)
+            || (natural_stop != -1 && range->right.value >= natural_stop);
+
+        if(ignore_left && ignore_right) {
+            /* Empty constraint comparison */
+        } else if(ignore_left) {
+            abuf_printf(ab, "%s <= ", varname);
+            abuf_oint(ab, range->right.value);
+        } else if(ignore_right) {
+            abuf_printf(ab, "%s >= ", varname);
+            abuf_oint(ab, range->left.value);
+        } else if(range->left.value == range->right.value) {
+            abuf_printf(ab, "%s == ", varname);
+            abuf_oint(ab, range->right.value);
+        } else {
+            abuf_printf(ab, "%s >= ", varname);
+            abuf_oint(ab, range->left.value);
+            abuf_printf(ab, " && ");
+            abuf_printf(ab, "%s <= ", varname);
+            abuf_oint(ab, range->right.value);
+        }
+    } else {
+        for(int i = 0; i < range->el_count; i++) {
+            asn1cnst_range_t *r = range->elements[i];
+
+            abuf *rec = emit_range_comparison_code(r, varname, natural_start,
+                                                   natural_stop);
+            if(rec->length) {
+                if(ab->length) {
+                    abuf_str(ab, " || ");
+                }
+                abuf_str(ab, "(");
+                abuf_buf(ab, rec);
+                abuf_str(ab, ")");
+            } else {
+                /* Ignore this part */
+            }
+            abuf_free(rec);
+        }
+    }
+
+    return ab;
+}
+
+static int
+emit_size_determination_code(arg_t *arg, asn1p_expr_type_e etype) {
+
+       switch(etype) {
+       case ASN_BASIC_BIT_STRING:
+               OUT("if(st->size > 0) {\n");
+               OUT("\t/* Size in bits */\n");
+               OUT("\tsize = 8 * st->size - (st->bits_unused & 0x07);\n");
+               OUT("} else {\n");
+               OUT("\tsize = 0;\n");
+               OUT("}\n");
+               break;
+       case ASN_STRING_UniversalString:
+               OUT("size = st->size >> 2;\t/* 4 byte per character */\n");
+               break;
+       case ASN_STRING_BMPString:
+               OUT("size = st->size >> 1;\t/* 2 byte per character */\n");
+               break;
+       case ASN_STRING_UTF8String:
+               OUT("size = UTF8String_length(st);\n");
+               OUT("if((ssize_t)size < 0) {\n");
+               OUT("\tASN__CTFAIL(app_key, td, sptr,\n");
+               OUT("\t\t\"%%s: UTF-8: broken encoding (%%s:%%d)\",\n");
+               OUT("\t\ttd->name, __FILE__, __LINE__);\n");
+               OUT("\treturn -1;\n");
+               OUT("}\n");
+               break;
+       case ASN_CONSTR_SET_OF:
+       case ASN_CONSTR_SEQUENCE_OF:
+               OUT("/* Determine the number of elements */\n");
+               OUT("size = _A_C%s_FROM_VOID(sptr)->count;\n",
+                       etype==ASN_CONSTR_SET_OF?"SET":"SEQUENCE");
+               break;
+       case ASN_BASIC_OCTET_STRING:
+               OUT("size = st->size;\n");
+               break;
+       default:
+               if(etype & ASN_STRING_MASK) {
+                       OUT("size = st->size;\n");
+                       break;
+               } else {
+                       const char *type_name = ASN_EXPR_TYPE2STR(etype);
+                       if(!type_name) type_name = arg->expr->Identifier;
+                       WARNING("SizeConstraint is not defined for %s",
+                               type_name);
+                       OUT_NOINDENT("#warning SizeConstraint "
+                               "is not defined for %s!\n", type_name);
+                       OUT("size = st->size;\n");
+               }
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+emit_value_determination_code(arg_t *arg, asn1p_expr_type_e etype, asn1cnst_range_t *r_value) {
+
+       switch(etype) {
+       case ASN_BASIC_INTEGER:
+       case ASN_BASIC_ENUMERATED:
+               if(asn1c_type_fits_long(arg, arg->expr) == FL_FITS_UNSIGN) {
+                       OUT("value = *(const unsigned long *)sptr;\n");
+               } else if(asn1c_type_fits_long(arg, arg->expr) != FL_NOTFIT) {
+                       OUT("value = *(const long *)sptr;\n");
+               } else {
+                       /*
+                        * In some cases we can explore our knowledge of
+                        * underlying INTEGER_t->buf format.
+                        */
+                       if(r_value->el_count == 0
+                       && (
+                               /* Speed-up common case: (0..MAX) */
+                               (r_value->left.type == ARE_VALUE
+                               && r_value->left.value == 0
+                               && r_value->right.type == ARE_MAX)
+                           ||
+                               /* Speed-up common case: (MIN..-1) */
+                               (r_value->left.type == ARE_MIN
+                               && r_value->right.type == ARE_VALUE
+                               && r_value->right.value == -1)
+                       )) {
+                               OUT("/* Check if the sign bit is present */\n");
+                               OUT("value = st->buf ? ((st->buf[0] & 0x80) ? -1 : 1) : 0;\n");
+                               break;
+                       }
+
+                       if(native_long_sign(arg, r_value) >= 0) {
+                               /* Special case for treating unsigned longs */
+                               OUT("if(asn_INTEGER2ulong(st, &value)) {\n");
+                               INDENT(+1);
+                               OUT("ASN__CTFAIL(app_key, td, sptr,\n");
+                               OUT("\t\"%%s: value too large (%%s:%%d)\",\n");
+                               OUT("\ttd->name, __FILE__, __LINE__);\n");
+                               OUT("return -1;\n");
+                               INDENT(-1);
+                               OUT("}\n");
+                       } else {
+                               OUT("if(asn_INTEGER2long(st, &value)) {\n");
+                               INDENT(+1);
+                               OUT("ASN__CTFAIL(app_key, td, sptr,\n");
+                               OUT("\t\"%%s: value too large (%%s:%%d)\",\n");
+                               OUT("\ttd->name, __FILE__, __LINE__);\n");
+                               OUT("return -1;\n");
+                               INDENT(-1);
+                               OUT("}\n");
+                       }
+               }
+               break;
+       case ASN_BASIC_REAL:
+               if(arg->flags & A1C_USE_WIDE_TYPES) {
+                       OUT("if(asn_REAL2double(st, &value)) {\n");
+                               INDENT(+1);
+                               OUT("ASN__CTFAIL(app_key, td, sptr,\n");
+                               OUT("\t\"%%s: value too large (%%s:%%d)\",\n");
+                               OUT("\ttd->name, __FILE__, __LINE__);\n");
+                               OUT("return -1;\n");
+                               INDENT(-1);
+                       OUT("}\n");
+               } else {
+                       OUT("value = *(const %s *)sptr;\n", c_name(arg).type.c_name);
+               }
+               break;
+       case ASN_BASIC_BOOLEAN:
+               OUT("value = (*(const long *)sptr) ? 1 : 0;\n");
+               break;
+       default:
+               WARNING("%s:%d: Value cannot be determined "
+                       "for constraint check for %s",
+                       arg->expr->module->source_file_name,
+                       arg->expr->_lineno,
+                       arg->expr->Identifier
+               );
+               OUT_NOINDENT(
+                       "#error %s:%d: Value of %s cannot be determined\n",
+                       arg->expr->module->source_file_name,
+                       arg->expr->_lineno,
+                       arg->expr->Identifier
+               );
+               break;
+       }
+
+       return 0;
+}
+
+static asn1p_expr_type_e
+_find_terminal_type(arg_t *arg) {
+       asn1p_expr_t *expr;
+       expr = asn1f_find_terminal_type_ex(arg->asn, arg->ns, arg->expr);
+       if(expr) return expr->expr_type;
+       return A1TC_INVALID;
+}
+
+static int
+native_long_sign(arg_t *arg, asn1cnst_range_t *r) {
+    if(!(arg->flags & A1C_USE_WIDE_TYPES) && r->left.type == ARE_VALUE
+       && r->left.value >= 0 && r->left.value <= 2147483647
+        && r->right.type == ARE_MAX) {
+        return 1;
+    }
+       if(r->left.type == ARE_VALUE
+       && r->left.value >= 0
+       && r->right.type == ARE_VALUE
+       && r->right.value > 2147483647
+       && r->right.value <= (asn1c_integer_t)(4294967295UL)) {
+               if(r->el_count == 0
+               && r->left.value == 0
+               && r->right.value == 4294967295UL)
+                       return 0;
+               else
+                       return 1;
+       } else {
+               return -1;
+       }
+}