summaryrefslogtreecommitdiff
path: root/src/lexical_numeric_compare.c
blob: 3905f3f8937a81982189e974a9f745548dd4b0ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/*
 * lexicographical-numeric compare
 */
#include <string.h>
#include <ctype.h>
#include <caml/mlvalues.h>
#include <caml/fail.h>

CAMLprim value caml_lex_numeric_compare(value str1, value str2) {
    const char* inconsistent = "internal indexing error";
    mlsize_t len, len1, len2;
    int pos, pos1, pos2;
    const char * s1, * s2;
    const char * p1, * p2;
    int n1, n2;
    int res;

    if (str1 == str2) return Val_int(0);
    len1 = caml_string_length(str1);
    len2 = caml_string_length(str2);
    len = len1 <= len2 ? len1 : len2;
    s1 = String_val(str1);
    s2 = String_val(str2);
    p1 = s1;
    p2 = s2;
    pos = 0;

    do {
        while ((pos < len) && (!isdigit(*s1) || !isdigit(*s2))) {
            s1++;
            s2++;
            pos++;
        }
        if (pos > 0) {
            res = memcmp(p1, p2, pos);
            if (res < 0) return Val_int(-1);
            if (res > 0) return Val_int(1);
            if (pos == len) {
                if (len1 < len2) return Val_int(-1);
                if (len2 < len1) return Val_int(1);
                return Val_int(0);
            }
        }
        p1 = s1;
        p2 = s2;
        len = len - pos;
        len1 = len1 - pos;
        len2 = len2 - pos;
        pos1 = pos2 = 0;
        n1 = n2 = 0;
        while ((pos1 < len1) && isdigit(*s1)) {
            n1 = n1 * 10 + *s1 - '0';
            s1++;
            pos1++;
        }
        while ((pos2 < len2) && isdigit(*s2)) {
            n2 = n2 * 10 + *s2 - '0';
            s2++;
            pos2++;
        }
        if (n1 < n2) return Val_int(-1);
        if (n2 < n1) return Val_int(1);
        if ((pos1 == len1) || (pos2 == len2)) {
            if (len1 < len2) return Val_int(-1);
            if (len2 < len1) return Val_int(1);
            return Val_int(0);

        }
        // if a sequence of 0's were encountered, it is possible that
        // pos1 != pos2; adjust
        pos = pos1 > pos2 ? pos2 : pos1;
        p1 = s1;
        p2 = s2;
        len = len - pos;
        len1 = len1 - pos;
        len2 = len2 - pos;
        pos = 0;
    } while (*s1 && *s2);
}