diff --git a/src/libecmaobjects/ecma-globals.h b/src/libecmaobjects/ecma-globals.h index c96a35ec8..8ce82b2a3 100644 --- a/src/libecmaobjects/ecma-globals.h +++ b/src/libecmaobjects/ecma-globals.h @@ -740,6 +740,16 @@ typedef enum ECMA_MAGIC_STRING__COUNT /**< number of magic strings */ } ecma_magic_string_id_t; +/** + * ECMA string hash + */ +typedef uint16_t ecma_string_hash_t; + +/** + * Number of string's last characters to use for hash calculation + */ +#define ECMA_STRING_HASH_LAST_CHARS_COUNT (2) + /** * ECMA string-value descriptor */ @@ -755,8 +765,8 @@ typedef struct * in a stack variable (not in the heap) */ unsigned int is_stack_var : 1; - /** Padding */ - ecma_length_t padding; + /** Hash of the string (calculated from two last characters of the string) */ + ecma_string_hash_t hash; /** * Actual data or identifier of it's place in container (depending on 'container' field) diff --git a/src/libecmaobjects/ecma-helpers-string.c b/src/libecmaobjects/ecma-helpers-string.c index 455175afc..72d583259 100644 --- a/src/libecmaobjects/ecma-helpers-string.c +++ b/src/libecmaobjects/ecma-helpers-string.c @@ -305,8 +305,8 @@ ecma_init_ecma_string_from_lit_index (ecma_string_t *string_p, /**< descriptor t string_p->refs = 1; string_p->is_stack_var = is_stack_var; - string_p->container = ECMA_STRING_CONTAINER_LIT_TABLE; + string_p->hash = lit.data.lp.hash; string_p->u.lit_index = lit_index; } /* ecma_init_ecma_string_from_lit_index */ @@ -327,8 +327,9 @@ ecma_init_ecma_string_from_magic_string_id (ecma_string_t *string_p, /**< descri string_p->refs = 1; string_p->is_stack_var = is_stack_var; - string_p->container = ECMA_STRING_CONTAINER_MAGIC_STRING; + string_p->hash = ecma_chars_buffer_calc_hash_last_chars (ecma_get_magic_string_zt (magic_string_id), + ecma_magic_string_lengths [magic_string_id]); string_p->u.magic_string_id = magic_string_id; } /* ecma_init_ecma_string_from_magic_string_id */ @@ -363,6 +364,7 @@ ecma_new_ecma_string (const ecma_char_t *string_p) /**< zero-terminated string * string_desc_p->refs = 1; string_desc_p->is_stack_var = false; string_desc_p->container = ECMA_STRING_CONTAINER_HEAP_CHUNKS; + string_desc_p->hash = ecma_chars_buffer_calc_hash_last_chars (string_p, length); ecma_collection_header_t *collection_p = ecma_new_chars_collection (string_p, length); ECMA_SET_NON_NULL_POINTER (string_desc_p->u.collection_cp, collection_p); @@ -384,6 +386,34 @@ ecma_new_ecma_string_from_uint32 (uint32_t uint32_number) /**< UInt32-represente string_desc_p->container = ECMA_STRING_CONTAINER_UINT32_IN_DESC; string_desc_p->u.uint32_number = uint32_number; + uint32_t last_two_digits = uint32_number % 100; + uint32_t digit_pl = last_two_digits / 10; + uint32_t digit_l = last_two_digits % 10; + + FIXME (/* Use digit to char conversion routine */); + const ecma_char_t digits[10] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + const bool is_one_char_or_more = (uint32_number >= 10); + const ecma_char_t last_chars [ECMA_STRING_HASH_LAST_CHARS_COUNT] = + { + is_one_char_or_more ? digits [digit_pl] : digits[digit_l], + is_one_char_or_more ? digits [digit_l] : ECMA_CHAR_NULL + }; + + /* Only last two chars are really used for hash calculation */ + string_desc_p->hash = ecma_chars_buffer_calc_hash_last_chars (last_chars, + is_one_char_or_more ? 2 : 1); + +#ifndef JERRY_NDEBUG + ecma_char_t char_buf [ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32]; + ssize_t chars_copied = ecma_uint32_to_string (uint32_number, + char_buf, + ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32); + JERRY_ASSERT ((ecma_length_t) chars_copied == chars_copied); + + JERRY_ASSERT (string_desc_p->hash == ecma_chars_buffer_calc_hash_last_chars (char_buf, + ecma_zt_string_length (char_buf))); +#endif /* !JERRY_NDEBUG */ + return string_desc_p; } /* ecma_new_ecma_string_from_uint32 */ @@ -401,10 +431,16 @@ ecma_new_ecma_string_from_number (ecma_number_t num) /**< ecma-number */ return ecma_new_ecma_string_from_uint32 (uint32_num); } + ecma_char_t str_buf[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER + 1]; + ecma_length_t length = ecma_number_to_zt_string (num, + str_buf, + sizeof (str_buf)); + ecma_string_t* string_desc_p = ecma_alloc_string (); string_desc_p->refs = 1; string_desc_p->is_stack_var = false; string_desc_p->container = ECMA_STRING_CONTAINER_HEAP_NUMBER; + string_desc_p->hash = ecma_chars_buffer_calc_hash_last_chars (str_buf, length); ecma_number_t *num_p = ecma_alloc_number (); *num_p = num; @@ -479,7 +515,19 @@ ecma_concat_ecma_strings (ecma_string_t *string1_p, /**< first ecma-string */ JERRY_ASSERT (string1_p != NULL && string2_p != NULL); - int64_t length = (int64_t)ecma_string_get_length (string1_p) + (int64_t) ecma_string_get_length (string2_p); + uint32_t str1_len = (uint32_t) ecma_string_get_length (string1_p); + uint32_t str2_len = (uint32_t) ecma_string_get_length (string2_p); + + if (str1_len == 0) + { + return ecma_copy_or_ref_ecma_string (string2_p); + } + else if (str2_len == 0) + { + return ecma_copy_or_ref_ecma_string (string1_p); + } + + int64_t length = (int64_t) str1_len + (int64_t) str2_len; ecma_string_t* string_desc_p = ecma_alloc_string (); string_desc_p->refs = 1; @@ -497,6 +545,24 @@ ecma_concat_ecma_strings (ecma_string_t *string1_p, /**< first ecma-string */ ECMA_SET_NON_NULL_POINTER (string_desc_p->u.concatenation.string1_cp, string1_p); ECMA_SET_NON_NULL_POINTER (string_desc_p->u.concatenation.string2_cp, string2_p); + if (str2_len >= ECMA_STRING_HASH_LAST_CHARS_COUNT) + { + string_desc_p->hash = string2_p->hash; + } + else + { + JERRY_STATIC_ASSERT (ECMA_STRING_HASH_LAST_CHARS_COUNT == 2); + JERRY_ASSERT (str2_len == 1); + + ecma_char_t chars_buf[ECMA_STRING_HASH_LAST_CHARS_COUNT] = + { + ecma_string_get_char_at_pos (string1_p, str1_len - 1u), + ecma_string_get_char_at_pos (string2_p, 0) + }; + + string_desc_p->hash = ecma_chars_buffer_calc_hash_last_chars (chars_buf, ECMA_STRING_HASH_LAST_CHARS_COUNT); + } + return string_desc_p; } /* ecma_concat_ecma_strings */ @@ -982,9 +1048,9 @@ ecma_compare_ecma_strings (const ecma_string_t *string1_p, /* ecma-string */ { JERRY_ASSERT (string1_p != NULL && string2_p != NULL); - if (unlikely (string1_p == string2_p)) + if (string1_p->hash != string2_p->hash) { - return true; + return false; } if (string1_p->container == string2_p->container) @@ -1583,6 +1649,27 @@ ecma_string_try_hash (const ecma_string_t *string_p, /**< ecma-string to calcula return false; } /* ecma_string_try_hash */ +/** + * Calculate hash from last ECMA_STRING_HASH_LAST_CHARS_COUNT characters from the buffer. + * + * @return ecma-string's hash + */ +ecma_string_hash_t +ecma_chars_buffer_calc_hash_last_chars (const ecma_char_t *chars, /**< characters buffer */ + ecma_length_t length) /**< number of characters in the buffer */ +{ + JERRY_ASSERT (chars != NULL); + + ecma_char_t char1 = (length > 0) ? chars[length - 1] : ECMA_CHAR_NULL; + ecma_char_t char2 = (length > 1) ? chars[length - 2] : ECMA_CHAR_NULL; + + uint32_t t1 = (uint32_t) char1 + (uint32_t) char2; + uint32_t t2 = t1 * 0x24418b66; + uint32_t t3 = (t2 >> 16) ^ (t2 & 0xffffu); + + return (ecma_string_hash_t) t3; +} /* ecma_chars_buffer_calc_hash_last_chars */ + /** * @} * @} diff --git a/src/libecmaobjects/ecma-helpers.h b/src/libecmaobjects/ecma-helpers.h index c0876f90f..58f9a27a5 100644 --- a/src/libecmaobjects/ecma-helpers.h +++ b/src/libecmaobjects/ecma-helpers.h @@ -157,6 +157,7 @@ extern ecma_string_t* ecma_get_magic_string (ecma_magic_string_id_t id); extern bool ecma_is_string_magic (const ecma_string_t *string_p, ecma_magic_string_id_t *out_id_p); extern bool ecma_is_zt_string_magic (const ecma_char_t *zt_string_p, ecma_magic_string_id_t *out_id_p); extern bool ecma_string_try_hash (const ecma_string_t *string_p, uint32_t hash_length_bits, uint32_t *out_hash_p); +extern ecma_string_hash_t ecma_chars_buffer_calc_hash_last_chars (const ecma_char_t *chars, ecma_length_t length); /* ecma-helpers-number.c */ extern const ecma_number_t ecma_number_relative_eps; diff --git a/src/libintstructs/literal.c b/src/libintstructs/literal.c index f99070d58..97f3eb607 100644 --- a/src/libintstructs/literal.c +++ b/src/libintstructs/literal.c @@ -73,7 +73,8 @@ create_literal_from_zt (const ecma_char_t *s, ecma_length_t len) .data.lp = (lp_string) { .length = len, - .str = s + .str = s, + .hash = ecma_chars_buffer_calc_hash_last_chars (s, len) } }; } diff --git a/src/libintstructs/lp-string.h b/src/libintstructs/lp-string.h index d4486439e..b93c71b2a 100644 --- a/src/libintstructs/lp-string.h +++ b/src/libintstructs/lp-string.h @@ -23,6 +23,7 @@ typedef struct { const ecma_char_t *str; ecma_length_t length; + ecma_string_hash_t hash; } lp_string; diff --git a/src/libjsparser/lexer.c b/src/libjsparser/lexer.c index 735971c8e..b8a568ad9 100644 --- a/src/libjsparser/lexer.c +++ b/src/libjsparser/lexer.c @@ -165,6 +165,7 @@ adjust_string_ptrs (literal lit, size_t diff) .data.lp = (lp_string) { .length = lit.data.lp.length, + .hash = lit.data.lp.hash, .str = lit.data.lp.str + diff } };