diff --git a/src/core/struct.c b/src/core/struct.c index 29fd29dc..077e7536 100644 --- a/src/core/struct.c +++ b/src/core/struct.c @@ -53,10 +53,10 @@ const JanetKV *janet_struct_find(const JanetKV *st, Janet key) { int32_t index = janet_maphash(cap, janet_hash(key)); int32_t i; for (i = index; i < cap; i++) - if (janet_checktype(st[i].key, JANET_NIL) || janet_equals(st[i].key, key)) + if (janet_checktype(st[i].key, JANET_NIL) || janet_key_equals(st[i].key, key)) return st + i; for (i = 0; i < index; i++) - if (janet_checktype(st[i].key, JANET_NIL) || janet_equals(st[i].key, key)) + if (janet_checktype(st[i].key, JANET_NIL) || janet_key_equals(st[i].key, key)) return st + i; return NULL; } diff --git a/src/core/util.c b/src/core/util.c index c678703d..d31007f9 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -302,7 +302,7 @@ const JanetKV *janet_dict_find(const JanetKV *buckets, int32_t cap, Janet key) { } else if (NULL == first_bucket) { first_bucket = kv; } - } else if (janet_equals(kv->key, key)) { + } else if (janet_key_equals(kv->key, key)) { return buckets + i; } } @@ -315,7 +315,7 @@ const JanetKV *janet_dict_find(const JanetKV *buckets, int32_t cap, Janet key) { } else if (NULL == first_bucket) { first_bucket = kv; } - } else if (janet_equals(kv->key, key)) { + } else if (janet_key_equals(kv->key, key)) { return buckets + i; } } diff --git a/src/core/util.h b/src/core/util.h index af42c26f..0d95d062 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -69,6 +69,32 @@ uint32_t janet_hash_mix(uint32_t input, uint32_t more); #define janet_maphash(cap, hash) ((uint32_t)(hash) & (cap - 1)) +/* janet_bitwise_same is true when x and y are the same type and bits, + * guarded for the nanboxed implementation */ +#if defined(JANET_NANBOX_64) || defined(JANET_NANBOX_32) +#define janet_bitwise_same(x, y) (janet_u64(x) == janet_u64(y)) +#else +#define janet_bitwise_same(x, y) ((x).type == (y).type && janet_u64(x) == janet_u64(y)) +#endif + +/* Compare a stored key with a lookup key. Most types are equal only when bitwise identical, so + * janet_equals only runs for zero (since 0.0 and -0.0 are equal), strings, tuples, structs and abstract + * types. NaN is never stored as a key, so we skip it. */ +static inline int janet_key_equals(Janet stored, Janet key) { + if (janet_bitwise_same(stored, key)) return 1; + switch (janet_type(key)) { + case JANET_NUMBER: + return janet_unwrap_number(key) == 0 && janet_equals(stored, key); + case JANET_STRING: + case JANET_TUPLE: + case JANET_STRUCT: + case JANET_ABSTRACT: + return janet_equals(stored, key); + default: + return 0; + } +} + int janet_valid_utf8(const uint8_t *str, int32_t len); int janet_is_symbol_char(uint8_t c); diff --git a/src/core/value.c b/src/core/value.c index bd673362..fd7fc36a 100644 --- a/src/core/value.c +++ b/src/core/value.c @@ -246,27 +246,27 @@ static int janet_compare_abstract(JanetAbstract xx, JanetAbstract yy) { return xt->compare(xx, yy); } -int janet_equals(Janet x, Janet y) { +/* Compare two values of the same type that are not tuples or structs */ +static int janet_equals_leaf(Janet x, Janet y) { + switch (janet_type(x)) { + case JANET_NUMBER: + return janet_unwrap_number(x) == janet_unwrap_number(y); + case JANET_STRING: + return janet_string_equal(janet_unwrap_string(x), janet_unwrap_string(y)); + case JANET_ABSTRACT: + return !janet_compare_abstract(janet_unwrap_abstract(x), janet_unwrap_abstract(y)); + default: + return janet_bitwise_same(x, y); + } +} + +static int janet_equals_containers(Janet x, Janet y) { janet_vm.traversal = janet_vm.traversal_base; do { if (janet_type(x) != janet_type(y)) return 0; switch (janet_type(x)) { - case JANET_NIL: - break; - case JANET_BOOLEAN: - if (janet_unwrap_boolean(x) != janet_unwrap_boolean(y)) return 0; - break; - case JANET_NUMBER: - if (janet_unwrap_number(x) != janet_unwrap_number(y)) return 0; - break; - case JANET_STRING: - if (!janet_string_equal(janet_unwrap_string(x), janet_unwrap_string(y))) return 0; - break; - case JANET_ABSTRACT: - if (janet_compare_abstract(janet_unwrap_abstract(x), janet_unwrap_abstract(y))) return 0; - break; default: - if (janet_unwrap_pointer(x) != janet_unwrap_pointer(y)) return 0; + if (!janet_equals_leaf(x, y)) return 0; break; case JANET_TUPLE: { const Janet *t1 = janet_unwrap_tuple(x); @@ -278,7 +278,6 @@ int janet_equals(Janet x, Janet y) { push_traversal_node(janet_tuple_head(t1), janet_tuple_head(t2), 0); break; } - break; case JANET_STRUCT: { const JanetKV *s1 = janet_unwrap_struct(x); const JanetKV *s2 = janet_unwrap_struct(y); @@ -290,12 +289,17 @@ int janet_equals(Janet x, Janet y) { push_traversal_node(janet_struct_head(s1), janet_struct_head(s2), 0); break; } - break; } } while (!traversal_next(&x, &y)); return 1; } +int janet_equals(Janet x, Janet y) { + if (janet_type(x) != janet_type(y)) return 0; + if (janet_checktypes(x, JANET_TFLAG_TUPLE | JANET_TFLAG_STRUCT)) return janet_equals_containers(x, y); + return janet_equals_leaf(x, y); +} + static uint64_t murmur64(uint64_t h) { h ^= h >> 33; h *= 0xff51afd7ed558ccdUL;