diff options
-rw-r--r-- | byterun/compare.c | 251 |
1 files changed, 173 insertions, 78 deletions
diff --git a/byterun/compare.c b/byterun/compare.c index a75e37e1f..58f4fe361 100644 --- a/byterun/compare.c +++ b/byterun/compare.c @@ -12,100 +12,183 @@ /* $Id$ */ +#include <string.h> +#include <stdlib.h> #include "custom.h" #include "fail.h" #include "memory.h" #include "misc.h" #include "mlvalues.h" -/* Structural comparison on trees. - May loop on cyclic structures. */ +/* Structural comparison on trees. */ + +struct compare_item { value * v1, * v2; mlsize_t count; }; + +#define COMPARE_STACK_INIT_SIZE 256 +#define COMPARE_STACK_MAX_SIZE 1024*1024 + +static struct compare_item compare_stack_init[COMPARE_STACK_INIT_SIZE]; + +static struct compare_item * compare_stack = compare_stack_init; +static struct compare_item * compare_stack_limit = compare_stack_init + + COMPARE_STACK_INIT_SIZE; + +/* Free the compare stack if needed */ +static void compare_free_stack(void) +{ + if (compare_stack != compare_stack_init) { + stat_free(compare_stack); + /* Reinitialize the globals for next time around */ + compare_stack = compare_stack_init; + compare_stack_limit = compare_stack + COMPARE_STACK_INIT_SIZE; + } +} + +/* Same, then raise Stack_overflow */ +static void compare_stack_overflow(void) +{ + compare_free_stack(); +#if 0 + raise_stack_overflow(); +#else + raise_out_of_memory(); +#endif +} + +/* Grow the compare stack */ +static struct compare_item * compare_resize_stack(struct compare_item * sp) +{ + asize_t newsize = 2 * (compare_stack_limit - compare_stack); + asize_t sp_offset = sp - compare_stack; + struct compare_item * newstack; + + if (newsize >= COMPARE_STACK_MAX_SIZE) compare_stack_overflow(); + if (compare_stack == compare_stack_init) { + newstack = malloc(sizeof(struct compare_item) * newsize); + if (newstack == NULL) compare_stack_overflow(); + memcpy(newstack, compare_stack_init, + sizeof(struct compare_item) * COMPARE_STACK_INIT_SIZE); + } else { + newstack = + realloc(compare_stack, sizeof(struct compare_item) * newsize); + if (newstack == NULL) compare_stack_overflow(); + } + compare_stack = newstack; + compare_stack_limit = newstack + newsize; + return newstack + sp_offset; +} + +/* Structural comparison */ static long compare_val(value v1, value v2) { + struct compare_item * sp; tag_t t1, t2; - tailcall: - if (v1 == v2) return 0; - if (Is_long(v1)) { - if (Is_long(v2)) - return Long_val(v1) - Long_val(v2); - else - return -1; - } - if (Is_long(v2)) return 1; - /* If one of the objects is outside the heap (but is not an atom), - use address comparison. Since both addresses are 2-aligned, - shift lsb off to avoid overflow in subtraction. */ - if ((!Is_atom(v1) && !Is_young(v1) && !Is_in_heap(v1)) || - (!Is_atom(v2) && !Is_young(v2) && !Is_in_heap(v2))) + sp = compare_stack; + while (1) { + if (v1 == v2) goto next_item; + if (Is_long(v1)) { + if (Is_long(v2)) + return Long_val(v1) - Long_val(v2); + else + return -1; + } + if (Is_long(v2)) return 1; + /* If one of the objects is outside the heap (but is not an atom), + use address comparison. Since both addresses are 2-aligned, + shift lsb off to avoid overflow in subtraction. */ + if ((!Is_atom(v1) && !Is_young(v1) && !Is_in_heap(v1)) || + (!Is_atom(v2) && !Is_young(v2) && !Is_in_heap(v2))) return (v1 >> 1) - (v2 >> 1); - t1 = Tag_val(v1); - t2 = Tag_val(v2); - if (t1 != t2) return (long)t1 - (long)t2; - switch(t1) { - case String_tag: { - mlsize_t len1, len2, len; - unsigned char * p1, * p2; - len1 = string_length(v1); - len2 = string_length(v2); - for (len = (len1 <= len2 ? len1 : len2), - p1 = (unsigned char *) String_val(v1), - p2 = (unsigned char *) String_val(v2); - len > 0; - len--, p1++, p2++) - if (*p1 != *p2) return (long)*p1 - (long)*p2; - return len1 - len2; - } - case Double_tag: { - double d1 = Double_val(v1); - double d2 = Double_val(v2); - if (d1 < d2) return -1; else if (d1 > d2) return 1; else return 0; - } - case Double_array_tag: { - mlsize_t sz1 = Wosize_val(v1) / Double_wosize; - mlsize_t sz2 = Wosize_val(v2) / Double_wosize; - mlsize_t i; - if (sz1 != sz2) return sz1 - sz2; - for (i = 0; i < sz1; i++) { - double d1 = Double_field(v1, i); - double d2 = Double_field(v2, i); + t1 = Tag_val(v1); + t2 = Tag_val(v2); + if (t1 != t2) return (long)t1 - (long)t2; + switch(t1) { + case String_tag: { + mlsize_t len1, len2, len; + unsigned char * p1, * p2; + len1 = string_length(v1); + len2 = string_length(v2); + for (len = (len1 <= len2 ? len1 : len2), + p1 = (unsigned char *) String_val(v1), + p2 = (unsigned char *) String_val(v2); + len > 0; + len--, p1++, p2++) + if (*p1 != *p2) return (long)*p1 - (long)*p2; + if (len1 != len2) return len1 - len2; + break; + } + case Double_tag: { + double d1 = Double_val(v1); + double d2 = Double_val(v2); if (d1 < d2) return -1; else if (d1 > d2) return 1; + break; } - return 0; - } - case Abstract_tag: - invalid_argument("equal: abstract value"); - case Closure_tag: - case Infix_tag: - invalid_argument("equal: functional value"); - case Object_tag: - return (Oid_val(v1) - Oid_val(v2)); - case Custom_tag: - return Custom_ops_val(v1)->compare(v1, v2); - default: { - mlsize_t sz1 = Wosize_val(v1); - mlsize_t sz2 = Wosize_val(v2); - value * p1, * p2; - long res; - if (sz1 != sz2) return sz1 - sz2; - if (sz1 == 0) return 0; - for(p1 = Op_val(v1), p2 = Op_val(v2); - sz1 > 1; - sz1--, p1++, p2++) { - res = compare_val(*p1, *p2); + case Double_array_tag: { + mlsize_t sz1 = Wosize_val(v1) / Double_wosize; + mlsize_t sz2 = Wosize_val(v2) / Double_wosize; + mlsize_t i; + if (sz1 != sz2) return sz1 - sz2; + for (i = 0; i < sz1; i++) { + double d1 = Double_field(v1, i); + double d2 = Double_field(v2, i); + if (d1 < d2) return -1; else if (d1 > d2) return 1; + } + break; + } + case Abstract_tag: + compare_free_stack(); + invalid_argument("equal: abstract value"); + case Closure_tag: + case Infix_tag: + compare_free_stack(); + invalid_argument("equal: functional value"); + case Object_tag: { + long oid1 = Oid_val(v1); + long oid2 = Oid_val(v2); + if (oid1 != oid2) return oid1 - oid2; + break; + } + case Custom_tag: { + int res = Custom_ops_val(v1)->compare(v1, v2); if (res != 0) return res; + break; } - v1 = *p1; - v2 = *p2; - goto tailcall; - } + default: { + mlsize_t sz1 = Wosize_val(v1); + mlsize_t sz2 = Wosize_val(v2); + /* Compare sizes first for speed */ + if (sz1 != sz2) return sz1 - sz2; + if (sz1 == 0) break; + /* Remember that we still have to compare fields 1 ... sz - 1 */ + if (sz1 > 1) { + sp++; + if (sp >= compare_stack_limit) sp = compare_resize_stack(sp); + sp->v1 = &Field(v1, 1); + sp->v2 = &Field(v2, 1); + sp->count = sz1 - 1; + } + /* Continue comparison with first field */ + v1 = Field(v1, 0); + v2 = Field(v2, 0); + continue; + } + } + next_item: + /* Pop one more item to compare, if any */ + if (sp == compare_stack) return 0; /* we're done */ + v1 = *(sp->v1)++; + v2 = *(sp->v2)++; + if (--(sp->count) == 0) sp--; } } value compare(value v1, value v2) /* ML */ { long res = compare_val(v1, v2); + /* Free stack if needed */ + if (compare_stack != compare_stack_init) compare_free_stack(); if (res < 0) return Val_int(-1); else if (res > 0) @@ -116,30 +199,42 @@ value compare(value v1, value v2) /* ML */ value equal(value v1, value v2) /* ML */ { - return Val_int(compare_val(v1, v2) == 0); + long res = compare_val(v1, v2); + if (compare_stack != compare_stack_init) compare_free_stack(); + return Val_int(res == 0); } value notequal(value v1, value v2) /* ML */ { - return Val_int(compare_val(v1, v2) != 0); + long res = compare_val(v1, v2); + if (compare_stack != compare_stack_init) compare_free_stack(); + return Val_int(res != 0); } value lessthan(value v1, value v2) /* ML */ { - return Val_int(compare_val(v1, v2) < 0); + long res = compare_val(v1, v2); + if (compare_stack != compare_stack_init) compare_free_stack(); + return Val_int(res < 0); } value lessequal(value v1, value v2) /* ML */ { - return Val_int(compare_val(v1, v2) <= 0); + long res = compare_val(v1, v2); + if (compare_stack != compare_stack_init) compare_free_stack(); + return Val_int(res <= 0); } value greaterthan(value v1, value v2) /* ML */ { - return Val_int(compare_val(v1, v2) > 0); + long res = compare_val(v1, v2); + if (compare_stack != compare_stack_init) compare_free_stack(); + return Val_int(res > 0); } value greaterequal(value v1, value v2) /* ML */ { - return Val_int(compare_val(v1, v2) >= 0); + long res = compare_val(v1, v2); + if (compare_stack != compare_stack_init) compare_free_stack(); + return Val_int(res >= 0); } |