1 /***********************************************************************/
5 /* Xavier Leroy, projet Cristal, INRIA Rocquencourt */
7 /* Copyright 1996 Institut National de Recherche en Informatique et */
8 /* en Automatique. All rights reserved. This file is distributed */
9 /* under the terms of the GNU Library General Public License, with */
10 /* the special exception on linking described in file ../LICENSE. */
12 /***********************************************************************/
14 /* $Id: compare.c 8768 2008-01-11 16:13:18Z doligez $ */
24 /* Structural comparison on trees. */
26 struct compare_item { value * v1, * v2; mlsize_t count; };
28 #define COMPARE_STACK_INIT_SIZE 256
29 #define COMPARE_STACK_MAX_SIZE (1024*1024)
31 static struct compare_item compare_stack_init[COMPARE_STACK_INIT_SIZE];
33 static struct compare_item * compare_stack = compare_stack_init;
34 static struct compare_item * compare_stack_limit = compare_stack_init
35 + COMPARE_STACK_INIT_SIZE;
37 CAMLexport int caml_compare_unordered;
39 /* Free the compare stack if needed */
40 static void compare_free_stack(void)
42 if (compare_stack != compare_stack_init) {
44 /* Reinitialize the globals for next time around */
45 compare_stack = compare_stack_init;
46 compare_stack_limit = compare_stack + COMPARE_STACK_INIT_SIZE;
50 /* Same, then raise Out_of_memory */
51 static void compare_stack_overflow(void)
53 caml_gc_message (0x04, "Stack overflow in structural comparison\n", 0);
55 caml_raise_out_of_memory();
58 /* Grow the compare stack */
59 static struct compare_item * compare_resize_stack(struct compare_item * sp)
61 asize_t newsize = 2 * (compare_stack_limit - compare_stack);
62 asize_t sp_offset = sp - compare_stack;
63 struct compare_item * newstack;
65 if (newsize >= COMPARE_STACK_MAX_SIZE) compare_stack_overflow();
66 if (compare_stack == compare_stack_init) {
67 newstack = malloc(sizeof(struct compare_item) * newsize);
68 if (newstack == NULL) compare_stack_overflow();
69 memcpy(newstack, compare_stack_init,
70 sizeof(struct compare_item) * COMPARE_STACK_INIT_SIZE);
73 realloc(compare_stack, sizeof(struct compare_item) * newsize);
74 if (newstack == NULL) compare_stack_overflow();
76 compare_stack = newstack;
77 compare_stack_limit = newstack + newsize;
78 return newstack + sp_offset;
81 /* Structural comparison */
86 #define UNORDERED ((intnat)1 << (8 * sizeof(value) - 1))
88 /* The return value of compare_val is as follows:
89 > 0 v1 is greater than v2
91 < 0 and > UNORDERED v1 is less than v2
92 UNORDERED v1 and v2 cannot be compared */
94 static intnat compare_val(value v1, value v2, int total)
96 struct compare_item * sp;
101 if (v1 == v2 && total) goto next_item;
103 if (v1 == v2) goto next_item;
105 return Long_val(v1) - Long_val(v2);
106 /* Subtraction above cannot overflow and cannot result in UNORDERED */
107 if (Is_in_value_area(v2) &&
108 Tag_val(v2) == Forward_tag) {
109 v2 = Forward_val(v2);
112 return LESS; /* v1 long < v2 block */
115 if (Is_in_value_area(v1) &&
116 Tag_val(v1) == Forward_tag) {
117 v1 = Forward_val(v1);
120 return GREATER; /* v1 block > v2 long */
122 /* If one of the objects is outside the heap (but is not an atom),
123 use address comparison. Since both addresses are 2-aligned,
124 shift lsb off to avoid overflow in subtraction. */
125 if (! Is_in_value_area(v1) || ! Is_in_value_area(v2)) {
126 if (v1 == v2) goto next_item;
127 return (v1 >> 1) - (v2 >> 1);
128 /* Subtraction above cannot result in UNORDERED */
132 if (t1 == Forward_tag) { v1 = Forward_val (v1); continue; }
133 if (t2 == Forward_tag) { v2 = Forward_val (v2); continue; }
134 if (t1 != t2) return (intnat)t1 - (intnat)t2;
137 mlsize_t len1, len2, len;
138 unsigned char * p1, * p2;
140 len1 = caml_string_length(v1);
141 len2 = caml_string_length(v2);
142 for (len = (len1 <= len2 ? len1 : len2),
143 p1 = (unsigned char *) String_val(v1),
144 p2 = (unsigned char *) String_val(v2);
147 if (*p1 != *p2) return (intnat)*p1 - (intnat)*p2;
148 if (len1 != len2) return len1 - len2;
152 double d1 = Double_val(v1);
153 double d2 = Double_val(v2);
154 if (d1 < d2) return LESS;
155 if (d1 > d2) return GREATER;
157 if (! total) return UNORDERED;
158 /* One or both of d1 and d2 is NaN. Order according to the
159 convention NaN = NaN and NaN < f for all other floats f. */
160 if (d1 == d1) return GREATER; /* d1 is not NaN, d2 is NaN */
161 if (d2 == d2) return LESS; /* d2 is not NaN, d1 is NaN */
162 /* d1 and d2 are both NaN, thus equal: continue comparison */
166 case Double_array_tag: {
167 mlsize_t sz1 = Wosize_val(v1) / Double_wosize;
168 mlsize_t sz2 = Wosize_val(v2) / Double_wosize;
170 if (sz1 != sz2) return sz1 - sz2;
171 for (i = 0; i < sz1; i++) {
172 double d1 = Double_field(v1, i);
173 double d2 = Double_field(v2, i);
174 if (d1 < d2) return LESS;
175 if (d1 > d2) return GREATER;
177 if (! total) return UNORDERED;
178 /* See comment for Double_tag case */
179 if (d1 == d1) return GREATER;
180 if (d2 == d2) return LESS;
186 compare_free_stack();
187 caml_invalid_argument("equal: abstract value");
190 compare_free_stack();
191 caml_invalid_argument("equal: functional value");
193 intnat oid1 = Oid_val(v1);
194 intnat oid2 = Oid_val(v2);
195 if (oid1 != oid2) return oid1 - oid2;
200 int (*compare)(value v1, value v2) = Custom_ops_val(v1)->compare;
201 if (compare == NULL) {
202 compare_free_stack();
203 caml_invalid_argument("equal: abstract value");
205 caml_compare_unordered = 0;
206 res = Custom_ops_val(v1)->compare(v1, v2);
207 if (caml_compare_unordered && !total) return UNORDERED;
208 if (res != 0) return res;
212 mlsize_t sz1 = Wosize_val(v1);
213 mlsize_t sz2 = Wosize_val(v2);
214 /* Compare sizes first for speed */
215 if (sz1 != sz2) return sz1 - sz2;
217 /* Remember that we still have to compare fields 1 ... sz - 1 */
220 if (sp >= compare_stack_limit) sp = compare_resize_stack(sp);
221 sp->v1 = &Field(v1, 1);
222 sp->v2 = &Field(v2, 1);
225 /* Continue comparison with first field */
232 /* Pop one more item to compare, if any */
233 if (sp == compare_stack) return EQUAL; /* we're done */
236 if (--(sp->count) == 0) sp--;
240 CAMLprim value caml_compare(value v1, value v2)
242 intnat res = compare_val(v1, v2, 1);
243 /* Free stack if needed */
244 if (compare_stack != compare_stack_init) compare_free_stack();
246 return Val_int(LESS);
248 return Val_int(GREATER);
250 return Val_int(EQUAL);
253 CAMLprim value caml_equal(value v1, value v2)
255 intnat res = compare_val(v1, v2, 0);
256 if (compare_stack != compare_stack_init) compare_free_stack();
257 return Val_int(res == 0);
260 CAMLprim value caml_notequal(value v1, value v2)
262 intnat res = compare_val(v1, v2, 0);
263 if (compare_stack != compare_stack_init) compare_free_stack();
264 return Val_int(res != 0);
267 CAMLprim value caml_lessthan(value v1, value v2)
269 intnat res = compare_val(v1, v2, 0);
270 if (compare_stack != compare_stack_init) compare_free_stack();
271 return Val_int(res < 0 && res != UNORDERED);
274 CAMLprim value caml_lessequal(value v1, value v2)
276 intnat res = compare_val(v1, v2, 0);
277 if (compare_stack != compare_stack_init) compare_free_stack();
278 return Val_int(res <= 0 && res != UNORDERED);
281 CAMLprim value caml_greaterthan(value v1, value v2)
283 intnat res = compare_val(v1, v2, 0);
284 if (compare_stack != compare_stack_init) compare_free_stack();
285 return Val_int(res > 0);
288 CAMLprim value caml_greaterequal(value v1, value v2)
290 intnat res = compare_val(v1, v2, 0);
291 if (compare_stack != compare_stack_init) compare_free_stack();
292 return Val_int(res >= 0);